Merge "Use base.delete_server in ServersTestJSON"
diff --git a/tempest/api/compute/volumes/test_attach_volume_negative.py b/tempest/api/compute/volumes/test_attach_volume_negative.py
index b7fa0fe..1f18bfe 100644
--- a/tempest/api/compute/volumes/test_attach_volume_negative.py
+++ b/tempest/api/compute/volumes/test_attach_volume_negative.py
@@ -29,6 +29,7 @@
             skip_msg = ("%s skipped as Cinder is not available" % cls.__name__)
             raise cls.skipException(skip_msg)
 
+    @test.related_bug('1630783', status_code=500)
     @test.idempotent_id('a313b5cd-fbd0-49cc-94de-870e99f763c7')
     def test_delete_attached_volume(self):
         server = self.create_test_server(wait_until='ACTIVE')
diff --git a/tempest/api/object_storage/base.py b/tempest/api/object_storage/base.py
index 52b0a9c..eb313d2 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -50,6 +50,7 @@
         cls.object_client = cls.os.object_client
         cls.container_client = cls.os.container_client
         cls.account_client = cls.os.account_client
+        cls.capabilities_client = cls.os.capabilities_client
 
     @classmethod
     def resource_setup(cls):
@@ -65,7 +66,7 @@
         cls.policies = None
 
         if CONF.object_storage_feature_enabled.discoverability:
-            _, body = cls.account_client.list_extensions()
+            _, body = cls.capabilities_client.list_capabilities()
 
             if 'swift' in body and 'policies' in body['swift']:
                 cls.policies = body['swift']['policies']
diff --git a/tempest/api/object_storage/test_account_services.py b/tempest/api/object_storage/test_account_services.py
index 4209f1a..59129e5 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -135,7 +135,7 @@
         not CONF.object_storage_feature_enabled.discoverability,
         'Discoverability function is disabled')
     def test_list_extensions(self):
-        resp, extensions = self.account_client.list_extensions()
+        resp, extensions = self.capabilities_client.list_capabilities()
 
         self.assertThat(resp, custom_matchers.AreAllWellFormatted())
 
diff --git a/tempest/api/object_storage/test_container_services_negative.py b/tempest/api/object_storage/test_container_services_negative.py
index df91325..f63c518 100644
--- a/tempest/api/object_storage/test_container_services_negative.py
+++ b/tempest/api/object_storage/test_container_services_negative.py
@@ -32,7 +32,7 @@
 
         if CONF.object_storage_feature_enabled.discoverability:
             # use /info to get default constraints
-            _, body = cls.account_client.list_extensions()
+            _, body = cls.capabilities_client.list_capabilities()
             cls.constraints = body['swift']
 
     @test.attr(type=["negative"])
diff --git a/tempest/clients.py b/tempest/clients.py
index 78a93d1..4a30f6f 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -315,6 +315,8 @@
 
         self.account_client = object_storage.AccountClient(self.auth_provider,
                                                            **params)
+        self.capabilities_client = object_storage.CapabilitiesClient(
+            self.auth_provider, **params)
         self.container_client = object_storage.ContainerClient(
             self.auth_provider, **params)
         self.object_client = object_storage.ObjectClient(self.auth_provider,
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index 381f3df..0a1881c 100644
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -169,7 +169,7 @@
         'nova': os.extensions_client,
         'cinder': os.volumes_extension_client,
         'neutron': os.network_extensions_client,
-        'swift': os.account_client,
+        'swift': os.capabilities_client,
     }
     # NOTE (e0ne): Use Cinder API v2 by default because v1 is deprecated
     if CONF.volume_feature_enabled.api_v2:
@@ -201,7 +201,7 @@
     if service != 'swift':
         resp = extensions_client.list_extensions()
     else:
-        __, resp = extensions_client.list_extensions()
+        __, resp = extensions_client.list_capabilities()
     # For Nova, Cinder and Neutron we use the alias name rather than the
     # 'name' field because the alias is considered to be the canonical
     # name.
diff --git a/tempest/services/object_storage/__init__.py b/tempest/services/object_storage/__init__.py
index 96fe4a3..d1a61d6 100644
--- a/tempest/services/object_storage/__init__.py
+++ b/tempest/services/object_storage/__init__.py
@@ -13,7 +13,10 @@
 # the License.
 
 from tempest.services.object_storage.account_client import AccountClient
+from tempest.services.object_storage.capabilities_client import \
+    CapabilitiesClient
 from tempest.services.object_storage.container_client import ContainerClient
 from tempest.services.object_storage.object_client import ObjectClient
 
-__all__ = ['AccountClient', 'ContainerClient', 'ObjectClient']
+__all__ = ['AccountClient', 'CapabilitiesClient', 'ContainerClient',
+           'ObjectClient']
diff --git a/tempest/services/object_storage/account_client.py b/tempest/services/object_storage/account_client.py
index 6012a92..9932b4a 100644
--- a/tempest/services/object_storage/account_client.py
+++ b/tempest/services/object_storage/account_client.py
@@ -144,13 +144,3 @@
             body = body.strip().splitlines()
         self.expected_success([200, 204], resp.status)
         return resp, body
-
-    def list_extensions(self):
-        self.skip_path()
-        try:
-            resp, body = self.get('info')
-        finally:
-            self.reset_path()
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return resp, body
diff --git a/tempest/services/object_storage/capabilities_client.py b/tempest/services/object_storage/capabilities_client.py
new file mode 100644
index 0000000..0fe437f
--- /dev/null
+++ b/tempest/services/object_storage/capabilities_client.py
@@ -0,0 +1,31 @@
+# Copyright 2012 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 oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class CapabilitiesClient(rest_client.RestClient):
+
+    def list_capabilities(self):
+        self.skip_path()
+        try:
+            resp, body = self.get('info')
+        finally:
+            self.reset_path()
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return resp, body
diff --git a/tempest/test.py b/tempest/test.py
index 057e278..51f0a6a 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -141,6 +141,28 @@
     return False
 
 
+def related_bug(bug, status_code=None):
+    """A decorator useful to know solutions from launchpad bug reports
+
+    @param bug: The launchpad bug number causing the test
+    @param status_code: The status code related to the bug report
+    """
+    def decorator(f):
+        @functools.wraps(f)
+        def wrapper(self, *func_args, **func_kwargs):
+            try:
+                return f(self, *func_args, **func_kwargs)
+            except Exception as exc:
+                exc_status_code = getattr(exc, 'status_code', None)
+                if status_code is None or status_code == exc_status_code:
+                    LOG.error('Hints: This test was made for the bug %s. '
+                              'The failure could be related to '
+                              'https://launchpad.net/bugs/%s' % (bug, bug))
+                raise exc
+        return wrapper
+    return decorator
+
+
 def is_scheduler_filter_enabled(filter_name):
     """Check the list of enabled compute scheduler filters from config. """
 
diff --git a/tempest/tests/cmd/test_verify_tempest_config.py b/tempest/tests/cmd/test_verify_tempest_config.py
index 00b4542..1af0d95 100644
--- a/tempest/tests/cmd/test_verify_tempest_config.py
+++ b/tempest/tests/cmd/test_verify_tempest_config.py
@@ -401,7 +401,7 @@
                            'not_fake': 'metadata',
                            'swift': 'metadata'})
         fake_os = mock.MagicMock()
-        fake_os.account_client.list_extensions = fake_list_extensions
+        fake_os.capabilities_client.list_capabilities = fake_list_extensions
         self.useFixture(mockpatch.PatchObject(
             verify_tempest_config, 'get_enabled_extensions',
             return_value=(['fake1', 'fake2', 'fake3'])))
@@ -423,7 +423,7 @@
                            'not_fake': 'metadata',
                            'swift': 'metadata'})
         fake_os = mock.MagicMock()
-        fake_os.account_client.list_extensions = fake_list_extensions
+        fake_os.capabilities_client.list_capabilities = fake_list_extensions
         self.useFixture(mockpatch.PatchObject(
             verify_tempest_config, 'get_enabled_extensions',
             return_value=(['all'])))
diff --git a/tempest/tests/lib/services/image/v2/test_namespace_properties_client.py b/tempest/tests/lib/services/image/v2/test_namespace_properties_client.py
new file mode 100644
index 0000000..1d56db6
--- /dev/null
+++ b/tempest/tests/lib/services/image/v2/test_namespace_properties_client.py
@@ -0,0 +1,191 @@
+# Copyright 2016 EasyStack.  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.lib.services.image.v2 import namespace_properties_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestNamespacePropertiesClient(base.BaseServiceTest):
+    FAKE_CREATE_SHOW_NAMESPACE_PROPERTY = {
+        "description": "property",
+        "enum": ["xen", "qemu", "kvm", "lxc", "uml", "vmware", "hyperv"],
+        "name": "OS::Glance::Image",
+        "title": "Hypervisor Type",
+        "type": "string"
+    }
+
+    FAKE_LIST_NAMESPACE_PROPERTY = {
+        "properties": {
+            "hw_disk_bus": {
+                "description": "property.",
+                "enum": ["scsi", "virtio", "uml", "xen", "ide", "usb"],
+                "title": "Disk Bus",
+                "type": "string"
+            },
+            "hw_machine_type": {
+                "description": "desc.",
+                "title": "Machine Type",
+                "type": "string"
+            },
+            "hw_qemu_guest_agent": {
+                "description": "desc.",
+                "enum": [
+                    "yes",
+                    "no"
+                ],
+                "title": "QEMU Guest Agent",
+                "type": "string"
+            },
+            "hw_rng_model": {
+                "default": "virtio",
+                "description": "desc",
+                "title": "Random Number Generator Device",
+                "type": "string"
+            },
+            "hw_scsi_model": {
+                "default": "virtio-scsi",
+                "description": "desc.",
+                "title": "SCSI Model",
+                "type": "string"
+            },
+            "hw_video_model": {
+                "description": "The video image driver used.",
+                "enum": [
+                    "vga",
+                    "cirrus",
+                    "vmvga",
+                    "xen",
+                    "qxl"
+                ],
+                "title": "Video Model",
+                "type": "string"
+            },
+            "hw_video_ram": {
+                "description": "desc.",
+                "title": "Max Video Ram",
+                "type": "integer"
+            },
+            "hw_vif_model": {
+                "description": "desc.",
+                "enum": ["e1000",
+                         "ne2k_pci",
+                         "pcnet",
+                         "rtl8139",
+                         "virtio",
+                         "e1000",
+                         "e1000e",
+                         "VirtualE1000",
+                         "VirtualE1000e",
+                         "VirtualPCNet32",
+                         "VirtualSriovEthernetCard",
+                         "VirtualVmxnet",
+                         "netfront",
+                         "ne2k_pci"
+                         ],
+                "title": "Virtual Network Interface",
+                "type": "string"
+            },
+            "os_command_line": {
+                "description": "desc.",
+                "title": "Kernel Command Line",
+                "type": "string"
+            }
+        }
+    }
+
+    FAKE_UPDATE_NAMESPACE_PROPERTY = {
+        "description": "property",
+        "enum": ["xen", "qemu", "kvm", "lxc", "uml", "vmware", "hyperv"],
+        "name": "OS::Glance::Image",
+        "title": "update Hypervisor Type",
+        "type": "string"
+    }
+
+    def setUp(self):
+        super(TestNamespacePropertiesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = namespace_properties_client.NamespacePropertiesClient(
+            fake_auth, 'image', 'regionOne')
+
+    def _test_create_namespace_property(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_namespace_property,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_SHOW_NAMESPACE_PROPERTY,
+            bytes_body, status=201,
+            namespace="OS::Compute::Hypervisor",
+            title="Hypervisor Type", name="OS::Glance::Image",
+            type="string",
+            enum=["xen", "qemu", "kvm", "lxc", "uml", "vmware", "hyperv"])
+
+    def _test_list_namespace_property(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_namespace_properties,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_NAMESPACE_PROPERTY,
+            bytes_body,
+            namespace="OS::Compute::Hypervisor")
+
+    def _test_show_namespace_property(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_namespace_properties,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_CREATE_SHOW_NAMESPACE_PROPERTY,
+            bytes_body,
+            namespace="OS::Compute::Hypervisor",
+            property_name="OS::Glance::Image")
+
+    def _test_update_namespace_property(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_namespace_properties,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_UPDATE_NAMESPACE_PROPERTY,
+            bytes_body,
+            namespace="OS::Compute::Hypervisor",
+            property_name="OS::Glance::Image",
+            title="update Hypervisor Type", type="string",
+            enum=["xen", "qemu", "kvm", "lxc", "uml", "vmware", "hyperv"],
+            name="OS::Glance::Image")
+
+    def test_create_namespace_property_with_str_body(self):
+        self._test_create_namespace_property()
+
+    def test_create_namespace_property_with_bytes_body(self):
+        self._test_create_namespace_property(bytes_body=True)
+
+    def test_list_namespace_property_with_str_body(self):
+        self._test_list_namespace_property()
+
+    def test_list_namespace_property_with_bytes_body(self):
+        self._test_list_namespace_property(bytes_body=True)
+
+    def test_show_namespace_property_with_str_body(self):
+        self._test_show_namespace_property()
+
+    def test_show_namespace_property_with_bytes_body(self):
+        self._test_show_namespace_property(bytes_body=True)
+
+    def test_update_namespace_property_with_str_body(self):
+        self._test_update_namespace_property()
+
+    def test_update_namespace_property_with_bytes_body(self):
+        self._test_update_namespace_property(bytes_body=True)
+
+    def test_delete_namespace(self):
+        self.check_service_client_function(
+            self.client.delete_namespace_property,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {}, namespace="OS::Compute::Hypervisor",
+            property_name="OS::Glance::Image", status=204)