Merge "Add json schema for disable service"
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index ec430b7..dd2f56b 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -76,8 +76,9 @@
 To enable and use tenant isolation you only need to configure 2 things:
 
  #. A set of admin credentials with permissions to create users and
-    tenants/projects. This is specified in the identity section with the
-    admin_username, admin_tenant_name, and admin_password options
+    tenants/projects. This is specified in the auth section with the
+    admin_username, admin_tenant_name, admin_domain_name, and admin_password
+    options
  #. To enable tenant_isolation in the auth section with the
     allow_tenant_isolation option.
 
@@ -126,6 +127,9 @@
 
 Non-locking test accounts (aka credentials config options)
 """"""""""""""""""""""""""""""""""""""""""""""""""""""""""
+**Starting in the Liberty release this mechanism was deprecated and will be
+removed in a future release**
+
 When Tempest was refactored to allow for locking test accounts, the original
 non-tenant isolated case was converted to internally work similarly to the
 accounts.yaml file. This mechanism was then called the non-locking test accounts
diff --git a/tempest/api/compute/admin/test_simple_tenant_usage.py b/tempest/api/compute/admin/test_simple_tenant_usage.py
index 7333acb..e5c17ca 100644
--- a/tempest/api/compute/admin/test_simple_tenant_usage.py
+++ b/tempest/api/compute/admin/test_simple_tenant_usage.py
@@ -14,10 +14,14 @@
 #    under the License.
 
 import datetime
-import time
 
 from tempest.api.compute import base
 from tempest import test
+from tempest_lib import exceptions as e
+
+# Time that waits for until returning valid response
+# TODO(takmatsu): Ideally this value would come from configuration.
+VALID_WAIT = 30
 
 
 class TenantUsagesTestJSON(base.BaseV2ComputeAdminTest):
@@ -35,7 +39,6 @@
 
         # Create a server in the demo tenant
         cls.create_test_server(wait_until='ACTIVE')
-        time.sleep(2)
 
         now = datetime.datetime.now()
         cls.start = cls._parse_strtime(now - datetime.timedelta(days=1))
@@ -46,17 +49,32 @@
         # Returns formatted datetime
         return at.strftime('%Y-%m-%dT%H:%M:%S.%f')
 
+    def call_until_valid(self, func, duration, *args, **kwargs):
+        # Call until get valid response for "duration"
+        # because tenant usage doesn't become available immediately
+        # after create VM.
+        def is_valid():
+            try:
+                self.resp = func(*args, **kwargs)
+                return True
+            except e.InvalidHTTPResponseBody:
+                return False
+        test.call_until_true(is_valid, duration, 1)
+        return self.resp
+
     @test.idempotent_id('062c8ae9-9912-4249-8b51-e38d664e926e')
     def test_list_usage_all_tenants(self):
         # Get usage for all tenants
-        tenant_usage = self.adm_client.list_tenant_usages(
+        tenant_usage = self.call_until_valid(
+            self.adm_client.list_tenant_usages, VALID_WAIT,
             start=self.start, end=self.end, detailed="1")['tenant_usages'][0]
         self.assertEqual(len(tenant_usage), 8)
 
     @test.idempotent_id('94135049-a4c5-4934-ad39-08fa7da4f22e')
     def test_get_usage_tenant(self):
         # Get usage for a specific tenant
-        tenant_usage = self.adm_client.show_tenant_usage(
+        tenant_usage = self.call_until_valid(
+            self.adm_client.show_tenant_usage, VALID_WAIT,
             self.tenant_id, start=self.start, end=self.end)['tenant_usage']
 
         self.assertEqual(len(tenant_usage), 8)
@@ -64,7 +82,7 @@
     @test.idempotent_id('9d00a412-b40e-4fd9-8eba-97b496316116')
     def test_get_usage_tenant_with_non_admin_user(self):
         # Get usage for a specific tenant with non admin user
-        tenant_usage = self.client.show_tenant_usage(
+        tenant_usage = self.call_until_valid(
+            self.client.show_tenant_usage, VALID_WAIT,
             self.tenant_id, start=self.start, end=self.end)['tenant_usage']
-
         self.assertEqual(len(tenant_usage), 8)
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index b0fdbac..ec2192f 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -246,8 +246,8 @@
             name = data_utils.rand_name(cls.__name__ + "-Server-Group")
         if policy is None:
             policy = ['affinity']
-        body = (cls.server_groups_client.create_server_group(name, policy)
-                ['server_group'])
+        body = cls.server_groups_client.create_server_group(
+            name=name, policies=policy)['server_group']
         cls.server_groups.append(body['id'])
         return body
 
diff --git a/tempest/api/image/v2/test_images_metadefs_namespaces.py b/tempest/api/image/v2/test_images_metadefs_namespaces.py
new file mode 100644
index 0000000..21247b1
--- /dev/null
+++ b/tempest/api/image/v2/test_images_metadefs_namespaces.py
@@ -0,0 +1,71 @@
+# Copyright 2015 Red Hat, Inc.
+# 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.image import base
+from tempest.common.utils import data_utils
+from tempest import test
+from tempest_lib import exceptions as lib_exc
+
+
+class MetadataNamespacesTest(base.BaseV2ImageTest):
+    """
+    Here we will test the Metadata definition Namespaces basic functionality.
+    """
+    @test.idempotent_id('319b765e-7f3d-4b3d-8b37-3ca3876ee768')
+    def test_basic_metadata_definition_namespaces(self):
+        # get the available resource types and use one resource_type
+        body = self.client.list_resource_types()
+        resource_name = body['resource_types'][0]['name']
+        name = [{'name': resource_name}]
+        namespace_name = data_utils.rand_name('namespace')
+        # create the metadef namespaces
+        body = self.client.create_namespaces(namespace=namespace_name,
+                                             visibility='public',
+                                             description='Tempest',
+                                             display_name=namespace_name,
+                                             resource_type_associations=name,
+                                             protected=True)
+        self.addCleanup(self._cleanup_namespace, namespace_name)
+        # get namespaces details
+        body = self.client.show_namespaces(namespace_name)
+        self.assertEqual(namespace_name, body['namespace'])
+        self.assertEqual('public', body['visibility'])
+        # unable to delete protected namespace
+        self.assertRaises(lib_exc.Forbidden, self.client.delete_namespaces,
+                          namespace_name)
+        # update the visibility to private and protected to False
+        body = self.client.update_namespaces(namespace=namespace_name,
+                                             description='Tempest',
+                                             visibility='private',
+                                             display_name=namespace_name,
+                                             protected=False)
+        self.assertEqual('private', body['visibility'])
+        self.assertEqual(False, body['protected'])
+        # now able to delete the non-protected namespace
+        self.client.delete_namespaces(namespace_name)
+
+    def _cleanup_namespace(self, namespace_name):
+        # this is used to cleanup the resources
+        try:
+            body = self.client.show_namespaces(namespace_name)
+            self.assertEqual(namespace_name, body['namespace'])
+            body = self.client.update_namespaces(namespace=namespace_name,
+                                                 description='Tempest',
+                                                 visibility='private',
+                                                 display_name=namespace_name,
+                                                 protected=False)
+            self.client.delete_namespaces(namespace_name)
+        except lib_exc.NotFound:
+            pass
diff --git a/tempest/api/network/test_subnetpools_extensions.py b/tempest/api/network/test_subnetpools_extensions.py
new file mode 100644
index 0000000..09478ca
--- /dev/null
+++ b/tempest/api/network/test_subnetpools_extensions.py
@@ -0,0 +1,78 @@
+# Copyright 2015 GlobalLogic.  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.common.utils import data_utils
+from tempest import config
+from tempest import test
+from tempest_lib import exceptions as lib_exc
+
+CONF = config.CONF
+
+
+class SubnetPoolsTestJSON(base.BaseNetworkTest):
+    """
+    Tests the following operations in the subnetpools API using the REST client
+    for Neutron:
+
+        Create a subnet pool.
+        Update a subnet pool.
+        Delete a subnet pool.
+        Lists subnet pool.
+        Show subnet pool details.
+
+    v2.0 of the Neutron API is assumed. It is assumed that subnetpools
+    options mentioned in the [network-feature-enabled] section and
+    default_network option mentioned in the [network] section of
+    etc/tempest.conf:
+
+    """
+
+    @classmethod
+    def skip_checks(cls):
+        super(SubnetPoolsTestJSON, cls).skip_checks()
+        if not test.is_extension_enabled('subnetpools', 'network'):
+            msg = "subnet pools extension not enabled."
+            raise cls.skipException(msg)
+
+    @test.attr(type='smoke')
+    @test.idempotent_id('62595970-ab1c-4b7f-8fcc-fddfe55e9811')
+    def test_create_list_show_update_delete_subnetpools(self):
+        subnetpool_name = data_utils.rand_name('subnetpools')
+        # create subnet pool
+        prefix = CONF.network.default_network
+        body = self.client.create_subnetpools(name=subnetpool_name,
+                                              prefixes=prefix)
+        subnetpool_id = body["subnetpool"]["id"]
+        self.addCleanup(self._cleanup_subnetpools, subnetpool_id)
+        self.assertEqual(subnetpool_name, body["subnetpool"]["name"])
+        # get detail about subnet pool
+        body = self.client.show_subnetpools(subnetpool_id)
+        self.assertEqual(subnetpool_name, body["subnetpool"]["name"])
+        # update the subnet pool
+        subnetpool_name = data_utils.rand_name('subnetpools_update')
+        body = self.client.update_subnetpools(subnetpool_id,
+                                              name=subnetpool_name)
+        self.assertEqual(subnetpool_name, body["subnetpool"]["name"])
+        # delete subnet pool
+        body = self.client.delete_subnetpools(subnetpool_id)
+        self.assertRaises(lib_exc.NotFound, self.client.show_subnetpools,
+                          subnetpool_id)
+
+    def _cleanup_subnetpools(self, subnetpool_id):
+        # this is used to cleanup the resources
+        try:
+            self.client.delete_subnetpools(subnetpool_id)
+        except lib_exc.NotFound:
+            pass
diff --git a/tempest/common/cred_provider.py b/tempest/common/cred_provider.py
index 783a5fc..5e80f50 100644
--- a/tempest/common/cred_provider.py
+++ b/tempest/common/cred_provider.py
@@ -26,7 +26,7 @@
 
 # Type of credentials available from configuration
 CREDENTIAL_TYPES = {
-    'identity_admin': ('identity', 'admin'),
+    'identity_admin': ('auth', 'admin'),
     'user': ('identity', None),
     'alt_user': ('identity', 'alt')
 }
diff --git a/tempest/config.py b/tempest/config.py
index eb49fed..85db089 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -82,6 +82,26 @@
                      "creates. However in some neutron configurations, like "
                      "with VLAN provider networks, this doesn't work. So if "
                      "set to False the isolated networks will not be created"),
+    cfg.StrOpt('admin_username',
+               help="Username for an administrative user. This is needed for "
+                    "authenticating requests made by tenant isolation to "
+                    "create users and projects",
+               deprecated_group='identity'),
+    cfg.StrOpt('admin_tenant_name',
+               help="Tenant name to use for an  administrative user. This is "
+                    "needed for authenticating requests made by tenant "
+                    "isolation to create users and projects",
+               deprecated_group='identity'),
+    cfg.StrOpt('admin_password',
+               help="Password to use for an  administrative user. This is "
+                    "needed for authenticating requests made by tenant "
+                    "isolation to create users and projects",
+               secret=True,
+               deprecated_group='identity'),
+    cfg.StrOpt('admin_domain_name',
+               help="Admin domain name for authentication (Keystone V3)."
+                    "The same domain applies to user and project",
+               deprecated_group='identity'),
 ]
 
 identity_group = cfg.OptGroup(name='identity',
@@ -133,42 +153,38 @@
                help="The endpoint type to use for OpenStack Identity "
                     "(Keystone) API v3"),
     cfg.StrOpt('username',
-               help="Username to use for Nova API requests."),
+               help="Username to use for Nova API requests.",
+               deprecated_for_removal=True),
     cfg.StrOpt('tenant_name',
-               help="Tenant name to use for Nova API requests."),
+               help="Tenant name to use for Nova API requests.",
+               deprecated_for_removal=True),
     cfg.StrOpt('admin_role',
                default='admin',
                help="Role required to administrate keystone."),
     cfg.StrOpt('password',
                help="API key to use when authenticating.",
-               secret=True),
+               secret=True,
+               deprecated_for_removal=True),
     cfg.StrOpt('domain_name',
                help="Domain name for authentication (Keystone V3)."
-                    "The same domain applies to user and project"),
+                    "The same domain applies to user and project",
+               deprecated_for_removal=True),
     cfg.StrOpt('alt_username',
                help="Username of alternate user to use for Nova API "
-                    "requests."),
+                    "requests.",
+               deprecated_for_removal=True),
     cfg.StrOpt('alt_tenant_name',
                help="Alternate user's Tenant name to use for Nova API "
-                    "requests."),
+                    "requests.",
+               deprecated_for_removal=True),
     cfg.StrOpt('alt_password',
                help="API key to use when authenticating as alternate user.",
-               secret=True),
+               secret=True,
+               deprecated_for_removal=True),
     cfg.StrOpt('alt_domain_name',
                help="Alternate domain name for authentication (Keystone V3)."
-                    "The same domain applies to user and project"),
-    cfg.StrOpt('admin_username',
-               help="Administrative Username to use for "
-                    "Keystone API requests."),
-    cfg.StrOpt('admin_tenant_name',
-               help="Administrative Tenant name to use for Keystone API "
-                    "requests."),
-    cfg.StrOpt('admin_password',
-               help="API key to use when authenticating as admin.",
-               secret=True),
-    cfg.StrOpt('admin_domain_name',
-               help="Admin domain name for authentication (Keystone V3)."
-                    "The same domain applies to user and project"),
+                    "The same domain applies to user and project",
+               deprecated_for_removal=True),
     cfg.StrOpt('default_domain_id',
                default='default',
                help="ID of the default domain"),
@@ -544,6 +560,10 @@
                     " with pre-configured ports."
                     " Supported ports are:"
                     " ['normal','direct','macvtap']"),
+    cfg.ListOpt('default_network',
+                default=["1.0.0.0/16", "2.0.0.0/16"],
+                help="List of ip pools"
+                     " for subnetpools creation"),
 ]
 
 network_feature_group = cfg.OptGroup(name='network-feature-enabled',
diff --git a/tempest/services/compute/json/server_groups_client.py b/tempest/services/compute/json/server_groups_client.py
index 33501fb..62258d3 100644
--- a/tempest/services/compute/json/server_groups_client.py
+++ b/tempest/services/compute/json/server_groups_client.py
@@ -22,18 +22,13 @@
 
 class ServerGroupsClient(service_client.ServiceClient):
 
-    def create_server_group(self, name, policies):
+    def create_server_group(self, **kwargs):
         """
         Create the server group
         name : Name of the server-group
         policies : List of the policies - affinity/anti-affinity)
         """
-        post_body = {
-            'name': name,
-            'policies': policies,
-        }
-
-        post_body = json.dumps({'server_group': post_body})
+        post_body = json.dumps({'server_group': kwargs})
         resp, body = self.post('os-server-groups', post_body)
 
         body = json.loads(body)
diff --git a/tempest/services/image/v2/json/image_client.py b/tempest/services/image/v2/json/image_client.py
index 6cad746..eea179d 100644
--- a/tempest/services/image/v2/json/image_client.py
+++ b/tempest/services/image/v2/json/image_client.py
@@ -212,3 +212,63 @@
         self.expected_success(200, resp.status)
         body = json.loads(body)
         return service_client.ResponseBody(resp, body)
+
+    def list_resource_types(self):
+        url = '/v2/metadefs/resource_types'
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def create_namespaces(self, namespace, **kwargs):
+        params = {
+            "namespace": namespace,
+        }
+
+        for option in kwargs:
+            value = kwargs.get(option)
+            if isinstance(value, dict) or isinstance(value, tuple):
+                params.update(value)
+            else:
+                params[option] = value
+
+        data = json.dumps(params)
+        self._validate_schema(data)
+
+        resp, body = self.post('/v2/metadefs/namespaces', data)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def show_namespaces(self, namespace):
+        url = '/v2/metadefs/namespaces/%s' % namespace
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def update_namespaces(self, namespace, visibility, **kwargs):
+        params = {
+            "namespace": namespace,
+            "visibility": visibility
+        }
+        for option in kwargs:
+            value = kwargs.get(option)
+            if isinstance(value, dict) or isinstance(value, tuple):
+                params.update(value)
+            else:
+                params[option] = value
+
+        data = json.dumps(params)
+        self._validate_schema(data)
+        url = '/v2/metadefs/namespaces/%s' % namespace
+        resp, body = self.put(url, body=data)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def delete_namespaces(self, namespace):
+        url = '/v2/metadefs/namespaces/%s' % namespace
+        resp, _ = self.delete(url)
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp)
diff --git a/tempest/services/network/json/network_client.py b/tempest/services/network/json/network_client.py
index 10bd23e..d766aa5 100644
--- a/tempest/services/network/json/network_client.py
+++ b/tempest/services/network/json/network_client.py
@@ -423,3 +423,25 @@
         post_body = {'network_id': network_id}
         uri = '/agents/%s/dhcp-networks' % agent_id
         return self.create_resource(uri, post_body)
+
+    def list_subnetpools(self, **filters):
+        uri = '/subnetpools'
+        return self.list_resources(uri, **filters)
+
+    def create_subnetpools(self, **kwargs):
+        uri = '/subnetpools'
+        post_data = {'subnetpool': kwargs}
+        return self.create_resource(uri, post_data)
+
+    def show_subnetpools(self, subnetpool_id, **fields):
+        uri = '/subnetpools/%s' % subnetpool_id
+        return self.show_resource(uri, **fields)
+
+    def update_subnetpools(self, subnetpool_id, **kwargs):
+        uri = '/subnetpools/%s' % subnetpool_id
+        post_data = {'subnetpool': kwargs}
+        return self.update_resource(uri, post_data)
+
+    def delete_subnetpools(self, subnetpool_id):
+        uri = '/subnetpools/%s' % subnetpool_id
+        return self.delete_resource(uri)
diff --git a/tempest/tests/common/test_admin_available.py b/tempest/tests/common/test_admin_available.py
index 5c69c5e..9f47ccc 100644
--- a/tempest/tests/common/test_admin_available.py
+++ b/tempest/tests/common/test_admin_available.py
@@ -64,9 +64,9 @@
             else:
                 (u, t, p) = (None, None, None)
 
-            cfg.CONF.set_default('admin_username', u, group='identity')
-            cfg.CONF.set_default('admin_tenant_name', t, group='identity')
-            cfg.CONF.set_default('admin_password', p, group='identity')
+            cfg.CONF.set_default('admin_username', u, group='auth')
+            cfg.CONF.set_default('admin_tenant_name', t, group='auth')
+            cfg.CONF.set_default('admin_password', p, group='auth')
 
         expected = admin_creds is not None or tenant_isolation
         observed = credentials.is_admin_available()
diff --git a/tempest/tests/common/test_cred_provider.py b/tempest/tests/common/test_cred_provider.py
index 1bc7147..d404660 100644
--- a/tempest/tests/common/test_cred_provider.py
+++ b/tempest/tests/common/test_cred_provider.py
@@ -123,5 +123,9 @@
         cfg.CONF.set_default('auth_version', 'v3', group='identity')
         # Identity group items
         for prefix in ['', 'alt_', 'admin_']:
+            if prefix == 'admin_':
+                group = 'auth'
+            else:
+                group = 'identity'
             cfg.CONF.set_default(prefix + 'domain_name', 'fake_domain_name',
-                                 group='identity')
+                                 group=group)
diff --git a/tempest/tests/fake_config.py b/tempest/tests/fake_config.py
index 4898c9c..ca8bc3e 100644
--- a/tempest/tests/fake_config.py
+++ b/tempest/tests/fake_config.py
@@ -48,9 +48,13 @@
         for config_option in ['username', 'password', 'tenant_name']:
             # Identity group items
             for prefix in ['', 'alt_', 'admin_']:
+                if prefix == 'admin_':
+                    group = 'auth'
+                else:
+                    group = 'identity'
                 self.conf.set_default(prefix + config_option,
                                       'fake_' + config_option,
-                                      group='identity')
+                                      group=group)
 
 
 class FakePrivate(config.TempestConfigPrivate):
diff --git a/tempest/tests/services/compute/test_images_client.py b/tempest/tests/services/compute/test_images_client.py
new file mode 100644
index 0000000..1d532b7
--- /dev/null
+++ b/tempest/tests/services/compute/test_images_client.py
@@ -0,0 +1,240 @@
+# Copyright 2015 NEC Corporation.  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.
+
+import copy
+
+from oslotest import mockpatch
+from tempest_lib import exceptions as lib_exc
+
+from tempest.services.compute.json import images_client
+from tempest.tests import fake_auth_provider
+from tempest.tests.services.compute import base
+
+
+class TestImagesClient(base.BaseComputeServiceTest):
+    # Data Dictionaries used for testing #
+    FAKE_IMAGE_METADATA = {
+        "list":
+            {"metadata": {
+             "auto_disk_config": "True",
+             "Label": "Changed"
+             }},
+        "set_item":
+            {"meta": {
+             "auto_disk_config": "True"
+             }},
+        "show_item":
+            {"meta": {
+             "kernel_id": "nokernel",
+             }},
+        "update":
+            {"metadata": {
+             "kernel_id": "False",
+             "Label": "UpdatedImage"
+             }},
+        "set":
+            {"metadata": {
+             "Label": "Changed",
+             "auto_disk_config": "True"
+             }},
+        "delete_item": {}
+        }
+
+    FAKE_IMAGE_DATA = {
+        "list":
+            {"images": [
+             {"id": "70a599e0-31e7-49b7-b260-868f441e862b",
+              "links": [
+                    {"href": "http://openstack.example.com/v2/openstack" +
+                             "/images/70a599e0-31e7-49b7-b260-868f441e862b",
+                     "rel": "self"
+                     }
+              ],
+              "name": "fakeimage7"
+              }]},
+        "show": {"image": {
+            "created": "2011-01-01T01:02:03Z",
+            "id": "70a599e0-31e7-49b7-b260-868f441e862b",
+            "links": [
+                {
+                    "href": "http://openstack.example.com/v2/openstack" +
+                            "/images/70a599e0-31e7-49b7-b260-868f441e862b",
+                    "rel": "self"
+                },
+            ],
+            "metadata": {
+                "architecture": "x86_64",
+                "auto_disk_config": "True",
+                "kernel_id": "nokernel",
+                "ramdisk_id": "nokernel"
+            },
+            "minDisk": 0,
+            "minRam": 0,
+            "name": "fakeimage7",
+            "progress": 100,
+            "status": "ACTIVE",
+            "updated": "2011-01-01T01:02:03Z"}},
+        "delete": {}
+        }
+    func2mock = {
+        'get': 'tempest.common.service_client.ServiceClient.get',
+        'post': 'tempest.common.service_client.ServiceClient.post',
+        'put': 'tempest.common.service_client.ServiceClient.put',
+        'delete': 'tempest.common.service_client.ServiceClient.delete'}
+    # Variable definition
+    FAKE_IMAGE_ID = FAKE_IMAGE_DATA['show']['image']['id']
+    FAKE_CREATE_INFO = {'location': 'None'}
+    FAKE_METADATA = FAKE_IMAGE_METADATA['show_item']['meta']
+
+    def setUp(self):
+        super(TestImagesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = images_client.ImagesClient(fake_auth,
+                                                 "compute", "regionOne")
+
+    def _test_image_operation(self, operation="delete", bytes_body=False):
+        response_code = 200
+        mock_operation = self.func2mock['get']
+        expected_op = self.FAKE_IMAGE_DATA[operation]
+        params = {"image_id": self.FAKE_IMAGE_ID}
+        if operation == 'list':
+            function = self.client.list_images
+        elif operation == 'show':
+            function = self.client.show_image
+        else:
+            function = self.client.delete_image
+            mock_operation = self.func2mock['delete']
+            response_code = 204
+
+        self.check_service_client_function(
+            function, mock_operation, expected_op,
+            bytes_body, response_code, **params)
+
+    def _test_image_metadata(self, operation="set_item", bytes_body=False):
+        response_code = 200
+        expected_op = self.FAKE_IMAGE_METADATA[operation]
+        if operation == 'list':
+            function = self.client.list_image_metadata
+            mock_operation = self.func2mock['get']
+            params = {"image_id": self.FAKE_IMAGE_ID}
+
+        elif operation == 'set':
+            function = self.client.set_image_metadata
+            mock_operation = self.func2mock['put']
+            params = {"image_id": "_dummy_data",
+                      "meta": self.FAKE_METADATA}
+
+        elif operation == 'update':
+            function = self.client.update_image_metadata
+            mock_operation = self.func2mock['post']
+            params = {"image_id": self.FAKE_IMAGE_ID,
+                      "meta": self.FAKE_METADATA}
+
+        elif operation == 'show_item':
+            mock_operation = self.func2mock['get']
+            function = self.client.show_image_metadata_item
+            params = {"image_id": self.FAKE_IMAGE_ID,
+                      "key": "123"}
+
+        elif operation == 'delete_item':
+            function = self.client.delete_image_metadata_item
+            mock_operation = self.func2mock['delete']
+            response_code = 204
+            params = {"image_id": self.FAKE_IMAGE_ID,
+                      "key": "123"}
+
+        else:
+            function = self.client.set_image_metadata_item
+            mock_operation = self.func2mock['put']
+            params = {"image_id": self.FAKE_IMAGE_ID,
+                      "key": "123",
+                      "meta": self.FAKE_METADATA}
+
+        self.check_service_client_function(
+            function, mock_operation, expected_op,
+            bytes_body, response_code, **params)
+
+    def _test_resource_deleted(self, bytes_body=False):
+        params = {"id": self.FAKE_IMAGE_ID}
+        expected_op = self.FAKE_IMAGE_DATA['show']['image']
+        self.useFixture(mockpatch.Patch('tempest.services.compute.json'
+                        '.images_client.ImagesClient.show_image',
+                                        side_effect=lib_exc.NotFound))
+        self.assertEqual(True, self.client.is_resource_deleted(**params))
+        tempdata = copy.deepcopy(self.FAKE_IMAGE_DATA['show'])
+        tempdata['image']['id'] = None
+        self.useFixture(mockpatch.Patch('tempest.services.compute.json'
+                        '.images_client.ImagesClient.show_image',
+                                        return_value=expected_op))
+        self.assertEqual(False, self.client.is_resource_deleted(**params))
+
+    def test_list_images_with_str_body(self):
+        self._test_image_operation('list')
+
+    def test_list_images_with_bytes_body(self):
+        self._test_image_operation('list', True)
+
+    def test_show_image_with_str_body(self):
+        self._test_image_operation('show')
+
+    def test_show_image_with_bytes_body(self):
+        self._test_image_operation('show', True)
+
+    def test_delete_image_with_str_body(self):
+        self._test_image_operation('delete')
+
+    def test_delete_image_with_bytes_body(self):
+        self._test_image_operation('delete', True)
+
+    def test_list_image_metadata_with_str_body(self):
+        self._test_image_metadata('list')
+
+    def test_list_image_metadata_with_bytes_body(self):
+        self._test_image_metadata('list', True)
+
+    def test_set_image_metadata_with_str_body(self):
+        self._test_image_metadata('set')
+
+    def test_set_image_metadata_with_bytes_body(self):
+        self._test_image_metadata('set', True)
+
+    def test_update_image_metadata_with_str_body(self):
+        self._test_image_metadata('update')
+
+    def test_update_image_metadata_with_bytes_body(self):
+        self._test_image_metadata('update', True)
+
+    def test_set_image_metadata_item_with_str_body(self):
+        self._test_image_metadata()
+
+    def test_set_image_metadata_item_with_bytes_body(self):
+        self._test_image_metadata(bytes_body=True)
+
+    def test_show_image_metadata_item_with_str_body(self):
+        self._test_image_metadata('show_item')
+
+    def test_show_image_metadata_item_with_bytes_body(self):
+        self._test_image_metadata('show_item', True)
+
+    def test_delete_image_metadata_item_with_str_body(self):
+        self._test_image_metadata('delete_item')
+
+    def test_delete_image_metadata_item_with_bytes_body(self):
+        self._test_image_metadata('delete_item', True)
+
+    def test_resource_delete_with_str_body(self):
+        self._test_resource_deleted()
+
+    def test_resource_delete_with_bytes_body(self):
+        self._test_resource_deleted(True)