Merge "Don't log 'Not Found' ERROR during image cleanup"
diff --git a/tempest/api/compute/admin/test_hosts.py b/tempest/api/compute/admin/test_hosts.py
index 19d7973..bf09428 100644
--- a/tempest/api/compute/admin/test_hosts.py
+++ b/tempest/api/compute/admin/test_hosts.py
@@ -15,6 +15,7 @@
 #    under the License.
 
 from tempest.api.compute import base
+from tempest.common import tempest_fixtures as fixtures
 from tempest.common.utils.data_utils import rand_name
 from tempest import exceptions
 from tempest.test import attr
@@ -42,6 +43,7 @@
 
     @attr(type='gate')
     def test_list_hosts_with_zone(self):
+        self.useFixture(fixtures.LockFixture('availability_zone'))
         resp, hosts = self.client.list_hosts()
         host = hosts[0]
         zone_name = host['zone']
diff --git a/tempest/api/network/test_extensions.py b/tempest/api/network/test_extensions.py
new file mode 100644
index 0000000..1b27d1b
--- /dev/null
+++ b/tempest/api/network/test_extensions.py
@@ -0,0 +1,79 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 OpenStack, Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+
+from tempest.api.network import base
+from tempest.test import attr
+
+
+class ExtensionsTestJSON(base.BaseNetworkTest):
+    _interface = 'json'
+
+    """
+    Tests the following operations in the Neutron API using the REST client for
+    Neutron:
+
+        List all available extensions
+
+    v2.0 of the Neutron API is assumed. It is also assumed that the following
+    options are defined in the [network] section of etc/tempest.conf:
+
+    """
+
+    @classmethod
+    def setUpClass(cls):
+        super(ExtensionsTestJSON, cls).setUpClass()
+
+    @attr(type='smoke')
+    def test_list_show_extensions(self):
+        # List available extensions for the tenant
+        expected_alias = ['security-group', 'l3_agent_scheduler',
+                          'ext-gw-mode', 'binding', 'quotas',
+                          'agent', 'dhcp_agent_scheduler', 'provider',
+                          'router', 'extraroute', 'external-net',
+                          'allowed-address-pairs', 'extra_dhcp_opt']
+        actual_alias = list()
+        resp, extensions = self.client.list_extensions()
+        self.assertEqual('200', resp['status'])
+        list_extensions = extensions['extensions']
+        # Show and verify the details of the available extensions
+        for ext in list_extensions:
+            ext_name = ext['name']
+            ext_alias = ext['alias']
+            actual_alias.append(ext['alias'])
+            resp, ext_details = self.client.show_extension_details(ext_alias)
+            self.assertEqual('200', resp['status'])
+            ext_details = ext_details['extension']
+
+            self.assertIsNotNone(ext_details)
+            self.assertIn('updated', ext_details.keys())
+            self.assertIn('name', ext_details.keys())
+            self.assertIn('description', ext_details.keys())
+            self.assertIn('namespace', ext_details.keys())
+            self.assertIn('links', ext_details.keys())
+            self.assertIn('alias', ext_details.keys())
+            self.assertEqual(ext_details['name'], ext_name)
+            self.assertEqual(ext_details['alias'], ext_alias)
+            self.assertEqual(ext_details, ext)
+        # Verify if expected extensions are present in the actual list
+        # of extensions returned
+        for e in expected_alias:
+            self.assertIn(e, actual_alias)
+
+
+class ExtensionsTestXML(ExtensionsTestJSON):
+    _interface = 'xml'
diff --git a/tempest/api/volume/test_volumes_get.py b/tempest/api/volume/test_volumes_get.py
index 119e3e0..f2915f7 100644
--- a/tempest/api/volume/test_volumes_get.py
+++ b/tempest/api/volume/test_volumes_get.py
@@ -34,6 +34,15 @@
         self.assertEqual(202, resp.status)
         self.client.wait_for_resource_deletion(volume_id)
 
+    def _is_true(self, val):
+        # NOTE(jdg): Temporary conversion method to get cinder patch
+        # merged.  Then we'll make this strict again and
+        #specifically check "true" or "false"
+        if val in ['true', 'True', True]:
+            return True
+        else:
+            return False
+
     def _volume_create_get_update_delete(self, **kwargs):
         # Create a volume, Get it's details and Delete the volume
         volume = {}
@@ -69,10 +78,14 @@
                          fetched_volume['metadata'],
                          'The fetched Volume is different '
                          'from the created Volume')
+
+        # NOTE(jdg): Revert back to strict true/false checking
+        # after fix for bug #1227837 merges
+        boot_flag = self._is_true(fetched_volume['bootable'])
         if 'imageRef' in kwargs:
-            self.assertEqual(fetched_volume['bootable'], True)
+            self.assertEqual(boot_flag, True)
         if 'imageRef' not in kwargs:
-            self.assertEqual(fetched_volume['bootable'], False)
+            self.assertEqual(boot_flag, False)
 
         # Update Volume
         new_v_name = rand_name('new-Volume')
@@ -92,10 +105,14 @@
         self.assertEqual(new_v_name, updated_volume['display_name'])
         self.assertEqual(new_desc, updated_volume['display_description'])
         self.assertEqual(metadata, updated_volume['metadata'])
+
+        # NOTE(jdg): Revert back to strict true/false checking
+        # after fix for bug #1227837 merges
+        boot_flag = self._is_true(updated_volume['bootable'])
         if 'imageRef' in kwargs:
-            self.assertEqual(updated_volume['bootable'], True)
+            self.assertEqual(boot_flag, True)
         if 'imageRef' not in kwargs:
-            self.assertEqual(updated_volume['bootable'], False)
+            self.assertEqual(boot_flag, False)
 
     @attr(type='gate')
     def test_volume_get_metadata_none(self):
diff --git a/tempest/api/volume/test_volumes_negative.py b/tempest/api/volume/test_volumes_negative.py
index 7eeb1ee..a48cea2 100644
--- a/tempest/api/volume/test_volumes_negative.py
+++ b/tempest/api/volume/test_volumes_negative.py
@@ -15,6 +15,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import uuid
+
 from tempest.api.volume import base
 from tempest.common.utils.data_utils import rand_name
 from tempest import exceptions
@@ -29,37 +31,24 @@
         super(VolumesNegativeTest, cls).setUpClass()
         cls.client = cls.volumes_client
 
+        # Create a test shared instance and volume for attach/detach tests
+        vol_name = rand_name('Volume-')
+
+        cls.volume = cls.create_volume(size=1, display_name=vol_name)
+        cls.client.wait_for_volume_status(cls.volume['id'], 'available')
+        cls.mountpoint = "/dev/vdc"
+
     @attr(type='gate')
     def test_volume_get_nonexistant_volume_id(self):
-        # Should not be able to get a non-existent volume
-        # Creating a non-existent volume id
-        volume_id_list = []
-        resp, volumes = self.client.list_volumes()
-        for i in range(len(volumes)):
-            volume_id_list.append(volumes[i]['id'])
-        while True:
-            non_exist_id = rand_name('999')
-            if non_exist_id not in volume_id_list:
-                break
-        # Trying to Get a non-existent volume
+        # Should not be able to get a non-existant volume
         self.assertRaises(exceptions.NotFound, self.client.get_volume,
-                          non_exist_id)
+                          str(uuid.uuid4()))
 
     @attr(type='gate')
     def test_volume_delete_nonexistant_volume_id(self):
-        # Should not be able to delete a non-existent Volume
-        # Creating non-existent volume id
-        volume_id_list = []
-        resp, volumes = self.client.list_volumes()
-        for i in range(len(volumes)):
-            volume_id_list.append(volumes[i]['id'])
-        while True:
-            non_exist_id = '12345678-abcd-4321-abcd-123456789098'
-            if non_exist_id not in volume_id_list:
-                break
-        # Try to delete a non-existent volume
+        # Should not be able to delete a non-existant Volume
         self.assertRaises(exceptions.NotFound, self.client.delete_volume,
-                          non_exist_id)
+                          str(uuid.uuid4()))
 
     @attr(type='gate')
     def test_create_volume_with_invalid_size(self):
@@ -109,6 +98,26 @@
         # Should not be able to delete volume when empty ID is passed
         self.assertRaises(exceptions.NotFound, self.client.delete_volume, '')
 
+    @attr(type=['negative', 'gate'])
+    def test_attach_volumes_with_nonexistent_volume_id(self):
+        srv_name = rand_name('Instance-')
+        resp, server = self.servers_client.create_server(srv_name,
+                                                         self.image_ref,
+                                                         self.flavor_ref)
+        self.addCleanup(self.servers_client.delete_server, server['id'])
+        self.servers_client.wait_for_server_status(server['id'], 'ACTIVE')
+        self.assertRaises(exceptions.NotFound,
+                          self.client.attach_volume,
+                          str(uuid.uuid4()),
+                          server['id'],
+                          self.mountpoint)
+
+    @attr(type=['negative', 'gate'])
+    def test_detach_volumes_with_invalid_volume_id(self):
+        self.assertRaises(exceptions.NotFound,
+                          self.client.detach_volume,
+                          'xxx')
+
 
 class VolumesNegativeTestXML(VolumesNegativeTest):
     _interface = 'xml'
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index f71ea46..44788f2 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -374,8 +374,10 @@
                                                          **ruleset)
             self.set_resource(sg_rule.id, sg_rule)
 
-    def create_server(self, client, name=None, image=None, flavor=None,
+    def create_server(self, client=None, name=None, image=None, flavor=None,
                       create_kwargs={}):
+        if client is None:
+            client = self.compute_client
         if name is None:
             name = rand_name('scenario-server-')
         if image is None:
@@ -481,7 +483,9 @@
             cls.config.identity.password,
             cls.config.identity.tenant_name).tenant_id
 
-    def _create_security_group(self, client, namestart='secgroup-smoke-'):
+    def _create_security_group(self, client=None, namestart='secgroup-smoke-'):
+        if client is None:
+            client = self.compute_client
         # Create security group
         sg_name = rand_name(namestart)
         sg_desc = sg_name + " description"
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index ce4d1bd..752ff6f 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -87,8 +87,7 @@
 
     def nova_boot(self):
         create_kwargs = {'key_name': self.keypair.name}
-        self.server = self.create_server(self.compute_client,
-                                         image=self.image,
+        self.server = self.create_server(image=self.image,
                                          create_kwargs=create_kwargs)
 
     def nova_list(self):
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 2aa5de3..7f7ba91 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -168,8 +168,7 @@
             name=rand_name('keypair-smoke-'))
 
     def _create_security_groups(self):
-        self.security_groups[self.tenant_id] = self._create_security_group(
-            self.compute_client)
+        self.security_groups[self.tenant_id] = self._create_security_group()
 
     def _create_networks(self):
         network = self._create_network(self.tenant_id)
@@ -214,8 +213,7 @@
             'key_name': keypair_name,
             'security_groups': security_groups,
         }
-        server = self.create_server(self.compute_client, name=name,
-                                    create_kwargs=create_kwargs)
+        server = self.create_server(name=name, create_kwargs=create_kwargs)
         return server
 
     def _create_servers(self):
diff --git a/tempest/scenario/test_server_advanced_ops.py b/tempest/scenario/test_server_advanced_ops.py
index 26bc95e..853b1ba 100644
--- a/tempest/scenario/test_server_advanced_ops.py
+++ b/tempest/scenario/test_server_advanced_ops.py
@@ -48,7 +48,7 @@
     @services('compute')
     def test_resize_server_confirm(self):
         # We create an instance for use in this test
-        instance = self.create_server(self.compute_client)
+        instance = self.create_server()
         instance_id = instance.id
         resize_flavor = self.config.compute.flavor_ref_alt
         LOG.debug("Resizing instance %s from flavor %s to flavor %s",
@@ -66,7 +66,7 @@
     @services('compute')
     def test_server_sequence_suspend_resume(self):
         # We create an instance for use in this test
-        instance = self.create_server(self.compute_client)
+        instance = self.create_server()
         instance_id = instance.id
         LOG.debug("Suspending instance %s. Current status: %s",
                   instance_id, instance.status)
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index 03e6e41..c32d49d 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -56,8 +56,7 @@
         create_kwargs = {
             'key_name': self.keypair.id
         }
-        instance = self.create_server(self.compute_client,
-                                      create_kwargs=create_kwargs)
+        instance = self.create_server(create_kwargs=create_kwargs)
         self.set_resource('instance', instance)
 
     def pause_server(self):
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
index 8c2cc76..ba347e0 100644
--- a/tempest/scenario/test_snapshot_pattern.py
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -34,8 +34,7 @@
         create_kwargs = {
             'key_name': self.keypair.name
         }
-        return self.create_server(self.compute_client, image=image_id,
-                                  create_kwargs=create_kwargs)
+        return self.create_server(image=image_id, create_kwargs=create_kwargs)
 
     def _add_keypair(self):
         self.keypair = self.create_keypair()
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index ab464e3..4f49d65 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -58,8 +58,7 @@
         create_kwargs = {
             'key_name': self.keypair.name
         }
-        return self.create_server(self.compute_client, image=image_id,
-                                  create_kwargs=create_kwargs)
+        return self.create_server(image=image_id, create_kwargs=create_kwargs)
 
     def _add_keypair(self):
         self.keypair = self.create_keypair()
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 3572166..d12cd56 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -49,8 +49,7 @@
             'block_device_mapping': bd_map,
             'key_name': keypair.name
         }
-        return self.create_server(self.compute_client,
-                                  create_kwargs=create_kwargs)
+        return self.create_server(create_kwargs=create_kwargs)
 
     def _create_snapshot_from_volume(self, vol_id):
         volume_snapshots = self.volume_client.volume_snapshots
diff --git a/tempest/services/network/json/network_client.py b/tempest/services/network/json/network_client.py
index 369dd81..c2d701d 100644
--- a/tempest/services/network/json/network_client.py
+++ b/tempest/services/network/json/network_client.py
@@ -598,3 +598,15 @@
                                                      health_monitor_id)
         resp, body = self.delete(uri, headers=self.headers)
         return resp, body
+
+    def list_extensions(self):
+        uri = '%s/extensions' % (self.uri_prefix)
+        resp, body = self.get(uri, self.headers)
+        body = json.loads(body)
+        return resp, body
+
+    def show_extension_details(self, ext_alias):
+        uri = '%s/extensions/%s' % (self.uri_prefix, ext_alias)
+        resp, body = self.get(uri, headers=self.headers)
+        body = json.loads(body)
+        return resp, body
diff --git a/tempest/services/network/xml/network_client.py b/tempest/services/network/xml/network_client.py
index a9b5512..1523ed0 100755
--- a/tempest/services/network/xml/network_client.py
+++ b/tempest/services/network/xml/network_client.py
@@ -428,6 +428,19 @@
                                                      health_monitor_id)
         return self.delete(uri, self.headers)
 
+    def list_extensions(self):
+        url = '%s/extensions' % (self.uri_prefix)
+        resp, body = self.get(url, self.headers)
+        extensions = self._parse_array(etree.fromstring(body))
+        extensions = {"extensions": extensions}
+        return resp, extensions
+
+    def show_extension_details(self, ext_alias):
+        uri = '%s/extensions/%s' % (self.uri_prefix, str(ext_alias))
+        resp, body = self.get(uri, self.headers)
+        body = _root_tag_fetcher_and_xml_to_json_parse(body)
+        return resp, body
+
 
 def _root_tag_fetcher_and_xml_to_json_parse(xml_returned_body):
     body = ET.fromstring(xml_returned_body)