Merge "Add Extensions client unit tests"
diff --git a/releasenotes/notes/add-identity-v3-clients-for-os-ep-filter-api-endpoint-groups-3518a90bbb731d0f.yaml b/releasenotes/notes/add-identity-v3-clients-for-os-ep-filter-api-endpoint-groups-3518a90bbb731d0f.yaml
new file mode 100644
index 0000000..1dc33aa
--- /dev/null
+++ b/releasenotes/notes/add-identity-v3-clients-for-os-ep-filter-api-endpoint-groups-3518a90bbb731d0f.yaml
@@ -0,0 +1,7 @@
+---
+features:
+  - |
+    Defines the identity v3 OS-EP-FILTER EndPoint Groups API client.
+    This client manages Create, Get, Update, Check, List, and Delete
+    of EndPoint Group.
+
diff --git a/releasenotes/notes/identity-token-client-8aaef74b1d61090a.yaml b/releasenotes/notes/identity-token-client-8aaef74b1d61090a.yaml
new file mode 100644
index 0000000..d94de3e
--- /dev/null
+++ b/releasenotes/notes/identity-token-client-8aaef74b1d61090a.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Add additional API endpoints to the identity v2 client token API:
+    -  list_endpoints_for_token
+    -  check_token_existence
diff --git a/tempest/api/identity/admin/v2/test_tokens.py b/tempest/api/identity/admin/v2/test_tokens.py
index b4c9389..6b30d23 100644
--- a/tempest/api/identity/admin/v2/test_tokens.py
+++ b/tempest/api/identity/admin/v2/test_tokens.py
@@ -14,14 +14,18 @@
 #    under the License.
 
 from tempest.api.identity import base
+from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
+
+CONF = config.CONF
 
 
 class TokensTestJSON(base.BaseIdentityV2AdminTest):
 
     @decorators.idempotent_id('453ad4d5-e486-4b2f-be72-cffc8149e586')
-    def test_create_get_delete_token(self):
+    def test_create_check_get_delete_token(self):
         # get a token by username and password
         user_name = data_utils.rand_name(name='user')
         user_password = data_utils.rand_password()
@@ -40,6 +44,7 @@
                          tenant['name'])
         # Perform GET Token
         token_id = body['token']['id']
+        self.client.check_token_existence(token_id)
         token_details = self.client.show_token(token_id)['access']
         self.assertEqual(token_id, token_details['token']['id'])
         self.assertEqual(user['id'], token_details['user']['id'])
@@ -48,6 +53,9 @@
                          token_details['token']['tenant']['name'])
         # then delete the token
         self.client.delete_token(token_id)
+        self.assertRaises(lib_exc.NotFound,
+                          self.client.check_token_existence,
+                          token_id)
 
     @decorators.idempotent_id('25ba82ee-8a32-4ceb-8f50-8b8c71e8765e')
     def test_rescope_token(self):
@@ -101,3 +109,25 @@
         # Use the unscoped token to get a token scoped to tenant2
         body = self.token_client.auth_token(token_id,
                                             tenant=tenant2_name)
+
+    @decorators.idempotent_id('ca3ea6f7-ed08-4a61-adbd-96906456ad31')
+    def test_list_endpoints_for_token(self):
+        # get a token for the user
+        creds = self.os_primary.credentials
+        username = creds.username
+        password = creds.password
+        tenant_name = creds.tenant_name
+        token = self.token_client.auth(username,
+                                       password,
+                                       tenant_name)['token']
+        endpoints = self.client.list_endpoints_for_token(
+            token['id'])['endpoints']
+        self.assertIsInstance(endpoints, list)
+        # Store list of service names
+        service_names = [e['name'] for e in endpoints]
+        # Get the list of available services.
+        available_services = [s[0] for s in list(
+            CONF.service_available.items()) if s[1] is True]
+        # Verify that all available services are present.
+        for service in available_services:
+            self.assertIn(service, service_names)
diff --git a/tempest/api/identity/admin/v2/test_tokens_negative.py b/tempest/api/identity/admin/v2/test_tokens_negative.py
new file mode 100644
index 0000000..eb3e365
--- /dev/null
+++ b/tempest/api/identity/admin/v2/test_tokens_negative.py
@@ -0,0 +1,38 @@
+# Copyright 2017 AT&T 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.
+
+from tempest.api.identity import base
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
+
+
+class TokensAdminTestNegative(base.BaseIdentityV2AdminTest):
+
+    credentials = ['primary', 'admin', 'alt']
+
+    @decorators.attr(type=['negative'])
+    @decorators.idempotent_id('a0a0a600-4292-4364-99c5-922c834fdf05')
+    def test_check_token_existence_negative(self):
+        creds = self.os_primary.credentials
+        creds_alt = self.os_alt.credentials
+        username = creds.username
+        password = creds.password
+        tenant_name = creds.tenant_name
+        alt_tenant_name = creds_alt.tenant_name
+        body = self.token_client.auth(username, password, tenant_name)
+        self.assertRaises(lib_exc.Unauthorized,
+                          self.client.check_token_existence,
+                          body['token']['id'],
+                          belongsTo=alt_tenant_name)
diff --git a/tempest/api/identity/admin/v3/test_endpoint_groups.py b/tempest/api/identity/admin/v3/test_endpoint_groups.py
new file mode 100644
index 0000000..5cd456c
--- /dev/null
+++ b/tempest/api/identity/admin/v3/test_endpoint_groups.py
@@ -0,0 +1,157 @@
+# Copyright 2017 AT&T 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.
+
+from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+
+
+class EndPointGroupsTest(base.BaseIdentityV3AdminTest):
+
+    @classmethod
+    def setup_clients(cls):
+        super(EndPointGroupsTest, cls).setup_clients()
+        cls.client = cls.endpoint_groups_client
+
+    @classmethod
+    def resource_setup(cls):
+        super(EndPointGroupsTest, cls).resource_setup()
+        cls.service_ids = list()
+        cls.endpoint_groups = list()
+
+        # Create endpoint group so as to use it for LIST test
+        service_id = cls._create_service()
+
+        name = data_utils.rand_name('service_group')
+        description = data_utils.rand_name('description')
+        filters = {'service_id': service_id}
+
+        endpoint_group = cls.client.create_endpoint_group(
+            name=name,
+            description=description,
+            filters=filters)['endpoint_group']
+
+        cls.endpoint_groups.append(endpoint_group)
+
+    @classmethod
+    def resource_cleanup(cls):
+        for e in cls.endpoint_groups:
+            cls.client.delete_endpoint_group(e['id'])
+        for s in cls.service_ids:
+            cls.services_client.delete_service(s)
+        super(EndPointGroupsTest, cls).resource_cleanup()
+
+    @classmethod
+    def _create_service(self):
+        s_name = data_utils.rand_name('service')
+        s_type = data_utils.rand_name('type')
+        s_description = data_utils.rand_name('description')
+        service_data = (
+            self.services_client.create_service(name=s_name,
+                                                type=s_type,
+                                                description=s_description))
+
+        service_id = service_data['service']['id']
+        self.service_ids.append(service_id)
+        return service_id
+
+    @decorators.idempotent_id('7c69e7a1-f865-402d-a2ea-44493017315a')
+    def test_create_list_show_check_delete_endpoint_group(self):
+        service_id = self._create_service()
+        name = data_utils.rand_name('service_group')
+        description = data_utils.rand_name('description')
+        filters = {'service_id': service_id}
+
+        endpoint_group = self.client.create_endpoint_group(
+            name=name,
+            description=description,
+            filters=filters)['endpoint_group']
+
+        self.endpoint_groups.append(endpoint_group)
+
+        # Asserting created endpoint group response body
+        self.assertIn('id', endpoint_group)
+        self.assertEqual(name, endpoint_group['name'])
+        self.assertEqual(description, endpoint_group['description'])
+
+        # Checking if endpoint groups are present in the list of endpoints
+        # Note that there are two endpoint groups in the list, one created
+        # in the resource setup, one created in this test case.
+        fetched_endpoints = \
+            self.client.list_endpoint_groups()['endpoint_groups']
+
+        missing_endpoints = \
+            [e for e in self.endpoint_groups if e not in fetched_endpoints]
+
+        # Asserting LIST endpoints
+        self.assertEmpty(missing_endpoints,
+                         "Failed to find endpoint %s in fetched list" %
+                         ', '.join(str(e) for e in missing_endpoints))
+
+        # Show endpoint group
+        fetched_endpoint = self.client.show_endpoint_group(
+            endpoint_group['id'])['endpoint_group']
+
+        # Asserting if the attributes of endpoint group are the same
+        self.assertEqual(service_id,
+                         fetched_endpoint['filters']['service_id'])
+        for attr in ('id', 'name', 'description'):
+            self.assertEqual(endpoint_group[attr], fetched_endpoint[attr])
+
+        # Check endpoint group
+        self.client.check_endpoint_group(endpoint_group['id'])
+
+        # Deleting the endpoint group created in this method
+        self.client.delete_endpoint_group(endpoint_group['id'])
+        self.endpoint_groups.remove(endpoint_group)
+
+        # Checking whether endpoint group is deleted successfully
+        fetched_endpoints = \
+            self.client.list_endpoint_groups()['endpoint_groups']
+        fetched_endpoint_ids = [e['id'] for e in fetched_endpoints]
+        self.assertNotIn(endpoint_group['id'], fetched_endpoint_ids)
+
+    @decorators.idempotent_id('51c8fc38-fa84-4e76-b5b6-6fc37770fb26')
+    def test_update_endpoint_group(self):
+        # Creating an endpoint group so as to check update endpoint group
+        # with new values
+        service1_id = self._create_service()
+        name = data_utils.rand_name('service_group')
+        description = data_utils.rand_name('description')
+        filters = {'service_id': service1_id}
+
+        endpoint_group = self.client.create_endpoint_group(
+            name=name,
+            description=description,
+            filters=filters)['endpoint_group']
+        self.endpoint_groups.append(endpoint_group)
+
+        # Creating new attr values to update endpoint group
+        service2_id = self._create_service()
+        name2 = data_utils.rand_name('service_group2')
+        description2 = data_utils.rand_name('description2')
+        filters = {'service_id': service2_id}
+
+        # Updating endpoint group with new attr values
+        updated_endpoint_group = self.client.update_endpoint_group(
+            endpoint_group['id'],
+            name=name2,
+            description=description2,
+            filters=filters)['endpoint_group']
+
+        self.assertEqual(name2, updated_endpoint_group['name'])
+        self.assertEqual(description2, updated_endpoint_group['description'])
+        self.assertEqual(service2_id,
+                         updated_endpoint_group['filters']['service_id'])
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index 785485b..3bc6ce1 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -225,6 +225,7 @@
         cls.oauth_consumers_client = cls.os_admin.oauth_consumers_client
         cls.domain_config_client = cls.os_admin.domain_config_client
         cls.endpoint_filter_client = cls.os_admin.endpoint_filter_client
+        cls.endpoint_groups_client = cls.os_admin.endpoint_groups_client
 
         if CONF.identity.admin_domain_scope:
             # NOTE(andreaf) When keystone policy requires it, the identity
diff --git a/tempest/api/identity/v3/test_tokens.py b/tempest/api/identity/v3/test_tokens.py
index 042c821..4c72d82 100644
--- a/tempest/api/identity/v3/test_tokens.py
+++ b/tempest/api/identity/v3/test_tokens.py
@@ -15,12 +15,39 @@
 
 from oslo_utils import timeutils
 import six
+
 from tempest.api.identity import base
 from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
 
 
 class TokensV3Test(base.BaseIdentityV3Test):
 
+    @decorators.idempotent_id('a9512ac3-3909-48a4-b395-11f438e16260')
+    def test_validate_token(self):
+        creds = self.os_primary.credentials
+        user_id = creds.user_id
+        username = creds.username
+        password = creds.password
+        user_domain_id = creds.user_domain_id
+        # GET and validate token
+        subject_token, token_body = self.non_admin_token.get_token(
+            user_id=user_id,
+            username=username,
+            user_domain_id=user_domain_id,
+            password=password,
+            auth_data=True)
+        authenticated_token = self.non_admin_client.show_token(
+            subject_token)['token']
+        # sanity checking to make sure they are indeed the same token
+        self.assertEqual(authenticated_token, token_body)
+        # test to see if token has been properly authenticated
+        self.assertEqual(authenticated_token['user']['id'], user_id)
+        self.assertEqual(authenticated_token['user']['name'], username)
+        self.non_admin_client.delete_token(subject_token)
+        self.assertRaises(
+            lib_exc.NotFound, self.non_admin_client.show_token, subject_token)
+
     @decorators.idempotent_id('6f8e4436-fc96-4282-8122-e41df57197a9')
     def test_create_token(self):
 
diff --git a/tempest/clients.py b/tempest/clients.py
index a941301..8ad6e92 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -204,6 +204,8 @@
             **params_v3)
         self.endpoint_filter_client = \
             self.identity_v3.EndPointsFilterClient(**params_v3)
+        self.endpoint_groups_client = self.identity_v3.EndPointGroupsClient(
+            **params_v3)
 
         # Token clients do not use the catalog. They only need default_params.
         # They read auth_url, so they should only be set if the corresponding
diff --git a/tempest/lib/common/utils/linux/remote_client.py b/tempest/lib/common/utils/linux/remote_client.py
index 64d6be2..aef2ff3 100644
--- a/tempest/lib/common/utils/linux/remote_client.py
+++ b/tempest/lib/common/utils/linux/remote_client.py
@@ -28,29 +28,37 @@
     def wrapper(self, *args, **kwargs):
         try:
             return function(self, *args, **kwargs)
-        except tempest.lib.exceptions.SSHTimeout:
-            try:
-                original_exception = sys.exc_info()
-                caller = test_utils.find_test_caller() or "not found"
-                if self.server:
-                    msg = 'Caller: %s. Timeout trying to ssh to server %s'
-                    LOG.debug(msg, caller, self.server)
-                    if self.console_output_enabled and self.servers_client:
-                        try:
-                            msg = 'Console log for server %s: %s'
-                            console_log = (
-                                self.servers_client.get_console_output(
-                                    self.server['id'])['output'])
-                            LOG.debug(msg, self.server['id'], console_log)
-                        except Exception:
-                            msg = 'Could not get console_log for server %s'
-                            LOG.debug(msg, self.server['id'])
-                # re-raise the original ssh timeout exception
-                six.reraise(*original_exception)
-            finally:
-                # Delete the traceback to avoid circular references
-                _, _, trace = original_exception
-                del trace
+        except Exception as e:
+            caller = test_utils.find_test_caller() or "not found"
+            if not isinstance(e, tempest.lib.exceptions.SSHTimeout):
+                message = ('Initializing SSH connection to %(ip)s failed. '
+                           'Error: %(error)s' % {'ip': self.ip_address,
+                                                 'error': e})
+                message = '(%s) %s' % (caller, message)
+                LOG.error(message)
+                raise
+            else:
+                try:
+                    original_exception = sys.exc_info()
+                    if self.server:
+                        msg = 'Caller: %s. Timeout trying to ssh to server %s'
+                        LOG.debug(msg, caller, self.server)
+                        if self.console_output_enabled and self.servers_client:
+                            try:
+                                msg = 'Console log for server %s: %s'
+                                console_log = (
+                                    self.servers_client.get_console_output(
+                                        self.server['id'])['output'])
+                                LOG.debug(msg, self.server['id'], console_log)
+                            except Exception:
+                                msg = 'Could not get console_log for server %s'
+                                LOG.debug(msg, self.server['id'])
+                    # re-raise the original ssh timeout exception
+                    six.reraise(*original_exception)
+                finally:
+                    # Delete the traceback to avoid circular references
+                    _, _, trace = original_exception
+                    del trace
     return wrapper
 
 
@@ -78,6 +86,7 @@
         """
         self.server = server
         self.servers_client = servers_client
+        self.ip_address = ip_address
         self.console_output_enabled = console_output_enabled
         self.ssh_shell_prologue = ssh_shell_prologue
         self.ping_count = ping_count
diff --git a/tempest/lib/services/identity/v2/identity_client.py b/tempest/lib/services/identity/v2/identity_client.py
index 6caff0e..c610d65 100644
--- a/tempest/lib/services/identity/v2/identity_client.py
+++ b/tempest/lib/services/identity/v2/identity_client.py
@@ -11,6 +11,7 @@
 #    under the License.
 
 from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
@@ -45,3 +46,24 @@
         self.expected_success(200, resp.status)
         body = json.loads(body)
         return rest_client.ResponseBody(resp, body)
+
+    def list_endpoints_for_token(self, token_id):
+        """List endpoints for a token """
+        resp, body = self.get("tokens/%s/endpoints" % token_id)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def check_token_existence(self, token_id, **params):
+        """Validates a token and confirms that it belongs to a tenant.
+
+        For a full list of available parameters, please refer to the
+        official API reference:
+        https://developer.openstack.org/api-ref/identity/v2-admin/#validate-token
+        """
+        url = "tokens/%s" % token_id
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.head(url)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/__init__.py b/tempest/lib/services/identity/v3/__init__.py
index 6f498d9..ce607ff 100644
--- a/tempest/lib/services/identity/v3/__init__.py
+++ b/tempest/lib/services/identity/v3/__init__.py
@@ -19,6 +19,8 @@
 from tempest.lib.services.identity.v3.domains_client import DomainsClient
 from tempest.lib.services.identity.v3.endpoint_filter_client import \
     EndPointsFilterClient
+from tempest.lib.services.identity.v3.endpoint_groups_client import \
+    EndPointGroupsClient
 from tempest.lib.services.identity.v3.endpoints_client import EndPointsClient
 from tempest.lib.services.identity.v3.groups_client import GroupsClient
 from tempest.lib.services.identity.v3.identity_client import IdentityClient
@@ -39,8 +41,9 @@
 from tempest.lib.services.identity.v3.versions_client import VersionsClient
 
 __all__ = ['CredentialsClient', 'DomainsClient', 'DomainConfigurationClient',
-           'EndPointsClient', 'EndPointsFilterClient', 'GroupsClient',
-           'IdentityClient', 'InheritedRolesClient', 'OAUTHConsumerClient',
-           'PoliciesClient', 'ProjectsClient', 'RegionsClient',
-           'RoleAssignmentsClient', 'RolesClient', 'ServicesClient',
-           'V3TokenClient', 'TrustsClient', 'UsersClient', 'VersionsClient']
+           'EndPointGroupsClient', 'EndPointsClient', 'EndPointsFilterClient',
+           'GroupsClient', 'IdentityClient', 'InheritedRolesClient',
+           'OAUTHConsumerClient', 'PoliciesClient', 'ProjectsClient',
+           'RegionsClient', 'RoleAssignmentsClient', 'RolesClient',
+           'ServicesClient', 'V3TokenClient', 'TrustsClient', 'UsersClient',
+           'VersionsClient']
diff --git a/tempest/lib/services/identity/v3/endpoint_groups_client.py b/tempest/lib/services/identity/v3/endpoint_groups_client.py
new file mode 100644
index 0000000..723aeaa
--- /dev/null
+++ b/tempest/lib/services/identity/v3/endpoint_groups_client.py
@@ -0,0 +1,78 @@
+# Copyright 2017 AT&T 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.

+

+from oslo_serialization import jsonutils as json

+

+from tempest.lib.common import rest_client

+

+

+class EndPointGroupsClient(rest_client.RestClient):

+    api_version = "v3"

+

+    def create_endpoint_group(self, **kwargs):

+        """Create endpoint group.

+

+        For a full list of available parameters, please refer to the

+        official API reference:

+        https://developer.openstack.org/api-ref/identity/v3-ext/#create-endpoint-group

+        """

+        post_body = json.dumps({'endpoint_group': kwargs})

+        resp, body = self.post('OS-EP-FILTER/endpoint_groups', post_body)

+        self.expected_success(201, resp.status)

+        body = json.loads(body)

+        return rest_client.ResponseBody(resp, body)

+

+    def update_endpoint_group(self, endpoint_group_id, **kwargs):

+        """Update endpoint group.

+

+        For a full list of available parameters, please refer to the

+        official API reference:

+        https://developer.openstack.org/api-ref/identity/v3-ext/#update-endpoint-group

+        """

+        post_body = json.dumps({'endpoint_group': kwargs})

+        resp, body = self.patch(

+            'OS-EP-FILTER/endpoint_groups/%s' % endpoint_group_id, post_body)

+        self.expected_success(200, resp.status)

+        body = json.loads(body)

+        return rest_client.ResponseBody(resp, body)

+

+    def delete_endpoint_group(self, endpoint_group_id):

+        """Delete endpoint group."""

+        resp_header, resp_body = self.delete(

+            'OS-EP-FILTER/endpoint_groups/%s' % endpoint_group_id)

+        self.expected_success(204, resp_header.status)

+        return rest_client.ResponseBody(resp_header, resp_body)

+

+    def show_endpoint_group(self, endpoint_group_id):

+        """Get endpoint group."""

+        resp_header, resp_body = self.get(

+            'OS-EP-FILTER/endpoint_groups/%s' % endpoint_group_id)

+        self.expected_success(200, resp_header.status)

+        resp_body = json.loads(resp_body)

+        return rest_client.ResponseBody(resp_header, resp_body)

+

+    def check_endpoint_group(self, endpoint_group_id):

+        """Check endpoint group."""

+        resp_header, resp_body = self.head(

+            'OS-EP-FILTER/endpoint_groups/%s' % endpoint_group_id)

+        self.expected_success(200, resp_header.status)

+        return rest_client.ResponseBody(resp_header, resp_body)

+

+    def list_endpoint_groups(self):

+        """Get endpoint groups."""

+        resp_header, resp_body = self.get('OS-EP-FILTER/endpoint_groups')

+        self.expected_success(200, resp_header.status)

+        resp_body = json.loads(resp_body)

+        return rest_client.ResponseBody(resp_header, resp_body)

diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index f25ab1d..38e03c7 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -313,13 +313,15 @@
 
         return secgroup
 
-    def get_remote_client(self, ip_address, username=None, private_key=None):
+    def get_remote_client(self, ip_address, username=None, private_key=None,
+                          server=None):
         """Get a SSH client to a remote server
 
         @param ip_address the server floating or fixed IP address to use
                           for ssh validation
         @param username name of the Linux account on the remote server
         @param private_key the SSH private key to use
+        @param server: server dict, used for debugging purposes
         @return a RemoteClient object
         """
 
@@ -334,22 +336,10 @@
         else:
             password = CONF.validation.image_ssh_password
             private_key = None
-        linux_client = remote_client.RemoteClient(ip_address, username,
-                                                  pkey=private_key,
-                                                  password=password)
-        try:
-            linux_client.validate_authentication()
-        except Exception as e:
-            message = ('Initializing SSH connection to %(ip)s failed. '
-                       'Error: %(error)s' % {'ip': ip_address,
-                                             'error': e})
-            caller = test_utils.find_test_caller()
-            if caller:
-                message = '(%s) %s' % (caller, message)
-            LOG.exception(message)
-            self._log_console_output()
-            raise
-
+        linux_client = remote_client.RemoteClient(
+            ip_address, username, pkey=private_key, password=password,
+            server=server, servers_client=self.servers_client)
+        linux_client.validate_authentication()
         return linux_client
 
     def _image_create(self, name, fmt, path,
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index eae1056..26a834b 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -141,14 +141,16 @@
 
         # check that we can SSH to the server before reboot
         self.linux_client = self.get_remote_client(
-            floating_ip['ip'], private_key=keypair['private_key'])
+            floating_ip['ip'], private_key=keypair['private_key'],
+            server=server)
 
         self.nova_reboot(server)
 
         # check that we can SSH to the server after reboot
         # (both connections are part of the scenario)
         self.linux_client = self.get_remote_client(
-            floating_ip['ip'], private_key=keypair['private_key'])
+            floating_ip['ip'], private_key=keypair['private_key'],
+            server=server)
 
         self.check_disks()
 
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 4efeffd..48ddac6 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -240,7 +240,7 @@
         ip_address = old_floating_ip['floating_ip_address']
         private_key = self._get_server_key(server)
         ssh_client = self.get_remote_client(
-            ip_address, private_key=private_key)
+            ip_address, private_key=private_key, server=server)
         old_nic_list = self._get_server_nics(ssh_client)
         # get a port from a list of one item
         port_list = self.os_admin.ports_client.list_ports(
@@ -348,7 +348,8 @@
         ip_address = floating_ip['floating_ip_address']
         private_key = self._get_server_key(self.floating_ip_tuple.server)
         ssh_source = self.get_remote_client(
-            ip_address, private_key=private_key)
+            ip_address, private_key=private_key,
+            server=self.floating_ip_tuple.server)
 
         for remote_ip in address_list:
             self.check_remote_connectivity(ssh_source, remote_ip,
@@ -575,7 +576,7 @@
         ip_address = floating_ip['floating_ip_address']
         private_key = self._get_server_key(server)
         ssh_client = self.get_remote_client(
-            ip_address, private_key=private_key)
+            ip_address, private_key=private_key, server=server)
 
         dns_servers = [initial_dns_server]
         servers = ssh_client.get_dns_servers()
@@ -641,7 +642,8 @@
 
         private_key = self._get_server_key(server2)
         ssh_client = self.get_remote_client(server2_fip['floating_ip_address'],
-                                            private_key=private_key)
+                                            private_key=private_key,
+                                            server=server2)
 
         self.check_public_network_connectivity(
             should_connect=True, msg="before updating "
@@ -830,7 +832,8 @@
         spoof_port = new_ports[0]
         private_key = self._get_server_key(server)
         ssh_client = self.get_remote_client(fip['floating_ip_address'],
-                                            private_key=private_key)
+                                            private_key=private_key,
+                                            server=server)
         spoof_nic = ssh_client.get_nic_name_by_mac(spoof_port["mac_address"])
         peer = self._create_server(self.new_net)
         peer_address = peer['addresses'][self.new_net['name']][0]['addr']
diff --git a/tempest/scenario/test_network_v6.py b/tempest/scenario/test_network_v6.py
index 6d9addd..bf26c2e 100644
--- a/tempest/scenario/test_network_v6.py
+++ b/tempest/scenario/test_network_v6.py
@@ -131,7 +131,7 @@
         ips = self.define_server_ips(srv=srv)
         ssh = self.get_remote_client(
             ip_address=fip['floating_ip_address'],
-            username=username)
+            username=username, server=srv)
         return ssh, ips, srv["id"]
 
     def turn_nic6_on(self, ssh, sid, network_id):
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index 77563b3..0c441ab 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -62,7 +62,8 @@
             self.ssh_client = self.get_remote_client(
                 ip_address=self.fip,
                 username=self.ssh_user,
-                private_key=keypair['private_key'])
+                private_key=keypair['private_key'],
+                server=self.instance)
 
     def verify_metadata(self):
         if self.run_ssh and CONF.compute_feature_enabled.metadata_service:
diff --git a/tempest/tests/lib/services/identity/v2/test_identity_client.py b/tempest/tests/lib/services/identity/v2/test_identity_client.py
index 96d50d7..303d1f7 100644
--- a/tempest/tests/lib/services/identity/v2/test_identity_client.py
+++ b/tempest/tests/lib/services/identity/v2/test_identity_client.py
@@ -26,6 +26,33 @@
         }
     }
 
+    FAKE_ENDPOINTS_FOR_TOKEN = {
+        "endpoints_links": [],
+        "endpoints": [
+            {
+                "name": "nova",
+                "adminURL": "https://nova.region-one.internal.com/" +
+                            "v2/be1319401cfa4a0aa590b97cc7b64d8d",
+                "region": "RegionOne",
+                "internalURL": "https://nova.region-one.internal.com/" +
+                               "v2/be1319401cfa4a0aa590b97cc7b64d8d",
+                "type": "compute",
+                "id": "11b41ee1b00841128b7333d4bf1a6140",
+                "publicURL": "https://nova.region-one.public.com/v2/" +
+                             "be1319401cfa4a0aa590b97cc7b64d8d"
+            },
+            {
+                "name": "neutron",
+                "adminURL": "https://neutron.region-one.internal.com/",
+                "region": "RegionOne",
+                "internalURL": "https://neutron.region-one.internal.com/",
+                "type": "network",
+                "id": "cdbfa3c416d741a9b5c968f2dc628acb",
+                "publicURL": "https://neutron.region-one.public.com/"
+            }
+        ]
+    }
+
     FAKE_API_INFO = {
         "name": "API_info",
         "type": "API",
@@ -148,6 +175,22 @@
             bytes_body,
             token_id="cbc36478b0bd8e67e89")
 
+    def _test_list_endpoints_for_token(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_endpoints_for_token,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_ENDPOINTS_FOR_TOKEN,
+            bytes_body,
+            token_id="cbc36478b0bd8e67e89")
+
+    def _test_check_token_existence(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.check_token_existence,
+            'tempest.lib.common.rest_client.RestClient.head',
+            {},
+            bytes_body,
+            token_id="cbc36478b0bd8e67e89")
+
     def test_show_api_description_with_str_body(self):
         self._test_show_api_description()
 
@@ -166,6 +209,18 @@
     def test_show_token_with_bytes_body(self):
         self._test_show_token(bytes_body=True)
 
+    def test_list_endpoints_for_token_with_str_body(self):
+        self._test_list_endpoints_for_token()
+
+    def test_list_endpoints_for_token_with_bytes_body(self):
+        self._test_list_endpoints_for_token(bytes_body=True)
+
+    def test_check_token_existence_with_bytes_body(self):
+        self._test_check_token_existence(bytes_body=True)
+
+    def test_check_token_existence_with_str_body(self):
+        self._test_check_token_existence()
+
     def test_delete_token(self):
         self.check_service_client_function(
             self.client.delete_token,
diff --git a/tempest/tests/lib/services/identity/v3/test_endpoint_groups_client.py b/tempest/tests/lib/services/identity/v3/test_endpoint_groups_client.py
new file mode 100644
index 0000000..8b034e6
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_endpoint_groups_client.py
@@ -0,0 +1,162 @@
+# Copyright 2017 AT&T 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.

+

+from tempest.lib.services.identity.v3 import endpoint_groups_client

+from tempest.tests.lib import fake_auth_provider

+from tempest.tests.lib.services import base

+

+

+class TestEndPointGroupsClient(base.BaseServiceTest):

+    FAKE_CREATE_ENDPOINT_GROUP = {

+        "endpoint_group": {

+            "id": 1,

+            "name": "FAKE_ENDPOINT_GROUP",

+            "description": "FAKE SERVICE ENDPOINT GROUP",

+            "filters": {

+                "service_id": 1

+            }

+        }

+    }

+

+    FAKE_ENDPOINT_GROUP_INFO = {

+        "endpoint_group": {

+            "id": 1,

+            "name": "FAKE_ENDPOINT_GROUP",

+            "description": "FAKE SERVICE ENDPOINT GROUP",

+            "links": {

+                "self": "http://example.com/identity/v3/OS-EP-FILTER/" +

+                        "endpoint_groups/1"

+            },

+            "filters": {

+                "service_id": 1

+            }

+        }

+    }

+

+    FAKE_LIST_ENDPOINT_GROUPS = {

+        "endpoint_groups": [

+            {

+                "id": 1,

+                "name": "SERVICE_GROUP1",

+                "description": "FAKE SERVICE ENDPOINT GROUP",

+                "links": {

+                    "self": "http://example.com/identity/v3/OS-EP-FILTER/" +

+                            "endpoint_groups/1"

+                },

+                "filters": {

+                    "service_id": 1

+                }

+            },

+            {

+                "id": 2,

+                "name": "SERVICE_GROUP2",

+                "description": "FAKE SERVICE ENDPOINT GROUP",

+                "links": {

+                    "self": "http://example.com/identity/v3/OS-EP-FILTER/" +

+                            "endpoint_groups/2"

+                },

+                "filters": {

+                    "service_id": 2

+                }

+            }

+        ]

+    }

+

+    def setUp(self):

+        super(TestEndPointGroupsClient, self).setUp()

+        fake_auth = fake_auth_provider.FakeAuthProvider()

+        self.client = endpoint_groups_client.EndPointGroupsClient(

+            fake_auth, 'identity', 'regionOne')

+

+    def _test_create_endpoint_group(self, bytes_body=False):

+        self.check_service_client_function(

+            self.client.create_endpoint_group,

+            'tempest.lib.common.rest_client.RestClient.post',

+            self.FAKE_CREATE_ENDPOINT_GROUP,

+            bytes_body,

+            status=201,

+            name="FAKE_ENDPOINT_GROUP",

+            filters={'service_id': "1"})

+

+    def _test_show_endpoint_group(self, bytes_body=False):

+        self.check_service_client_function(

+            self.client.show_endpoint_group,

+            'tempest.lib.common.rest_client.RestClient.get',

+            self.FAKE_ENDPOINT_GROUP_INFO,

+            bytes_body,

+            endpoint_group_id="1")

+

+    def _test_check_endpoint_group(self, bytes_body=False):

+        self.check_service_client_function(

+            self.client.check_endpoint_group,

+            'tempest.lib.common.rest_client.RestClient.head',

+            {},

+            bytes_body,

+            status=200,

+            endpoint_group_id="1")

+

+    def _test_update_endpoint_group(self, bytes_body=False):

+        self.check_service_client_function(

+            self.client.update_endpoint_group,

+            'tempest.lib.common.rest_client.RestClient.patch',

+            self.FAKE_ENDPOINT_GROUP_INFO,

+            bytes_body,

+            endpoint_group_id="1",

+            name="NewName")

+

+    def _test_list_endpoint_groups(self, bytes_body=False):

+        self.check_service_client_function(

+            self.client.list_endpoint_groups,

+            'tempest.lib.common.rest_client.RestClient.get',

+            self.FAKE_LIST_ENDPOINT_GROUPS,

+            bytes_body)

+

+    def test_create_endpoint_group_with_str_body(self):

+        self._test_create_endpoint_group()

+

+    def test_create_endpoint_group_with_bytes_body(self):

+        self._test_create_endpoint_group(bytes_body=True)

+

+    def test_show_endpoint_group_with_str_body(self):

+        self._test_show_endpoint_group()

+

+    def test_show_endpoint_group_with_bytes_body(self):

+        self._test_show_endpoint_group(bytes_body=True)

+

+    def test_check_endpoint_group_with_str_body(self):

+        self._test_check_endpoint_group()

+

+    def test_check_endpoint_group_with_bytes_body(self):

+        self._test_check_endpoint_group(bytes_body=True)

+

+    def test_list_endpoint_groups_with_str_body(self):

+        self._test_list_endpoint_groups()

+

+    def test_list_endpoint_groups_with_bytes_body(self):

+        self._test_list_endpoint_groups(bytes_body=True)

+

+    def test_update_endpoint_group_with_str_body(self):

+        self._test_update_endpoint_group()

+

+    def test_update_endpoint_group_with_bytes_body(self):

+        self._test_update_endpoint_group(bytes_body=True)

+

+    def test_delete_endpoint_group(self):

+        self.check_service_client_function(

+            self.client.delete_endpoint_group,

+            'tempest.lib.common.rest_client.RestClient.delete',

+            {},

+            endpoint_group_id="1",

+            status=204)

diff --git a/tempest/tests/lib/services/network/test_floating_ips_client.py b/tempest/tests/lib/services/network/test_floating_ips_client.py
new file mode 100644
index 0000000..c5b1845
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_floating_ips_client.py
@@ -0,0 +1,145 @@
+# Copyright 2017 AT&T 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 tempest.lib.services.network import floating_ips_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestFloatingIPsClient(base.BaseServiceTest):
+
+    FAKE_FLOATING_IPS = {
+        "floatingips": [
+            {
+                "router_id": "d23abc8d-2991-4a55-ba98-2aaea84cc72f",
+                "description": "for test",
+                "created_at": "2016-12-21T10:55:50Z",
+                "updated_at": "2016-12-21T10:55:53Z",
+                "revision_number": 1,
+                "project_id": "4969c491a3c74ee4af974e6d800c62de",
+                "tenant_id": "4969c491a3c74ee4af974e6d800c62de",
+                "floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57",
+                "fixed_ip_address": "10.0.0.3",
+                "floating_ip_address": "172.24.4.228",
+                "port_id": "ce705c24-c1ef-408a-bda3-7bbd946164ab",
+                "id": "2f245a7b-796b-4f26-9cf9-9e82d248fda7",
+                "status": "ACTIVE"
+            },
+            {
+                "router_id": None,
+                "description": "for test",
+                "created_at": "2016-12-21T11:55:50Z",
+                "updated_at": "2016-12-21T11:55:53Z",
+                "revision_number": 2,
+                "project_id": "4969c491a3c74ee4af974e6d800c62de",
+                "tenant_id": "4969c491a3c74ee4af974e6d800c62de",
+                "floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57",
+                "fixed_ip_address": None,
+                "floating_ip_address": "172.24.4.227",
+                "port_id": None,
+                "id": "61cea855-49cb-4846-997d-801b70c71bdd",
+                "status": "DOWN"
+            }
+        ]
+    }
+
+    FAKE_FLOATING_IP_ID = "2f245a7b-796b-4f26-9cf9-9e82d248fda7"
+
+    def setUp(self):
+        super(TestFloatingIPsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.floating_ips_client = floating_ips_client.FloatingIPsClient(
+            fake_auth, "compute", "regionOne")
+
+    def _test_list_floatingips(self, bytes_body=False):
+        self.check_service_client_function(
+            self.floating_ips_client.list_floatingips,
+            "tempest.lib.common.rest_client.RestClient.get",
+            self.FAKE_FLOATING_IPS,
+            bytes_body,
+            200)
+
+    def _test_create_floatingip(self, bytes_body=False):
+        self.check_service_client_function(
+            self.floating_ips_client.create_floatingip,
+            "tempest.lib.common.rest_client.RestClient.post",
+            {"floatingip": self.FAKE_FLOATING_IPS["floatingips"][1]},
+            bytes_body,
+            201,
+            floating_network_id="172.24.4.228")
+
+    def _test_show_floatingip(self, bytes_body=False):
+        self.check_service_client_function(
+            self.floating_ips_client.show_floatingip,
+            "tempest.lib.common.rest_client.RestClient.get",
+            {"floatingip": self.FAKE_FLOATING_IPS["floatingips"][0]},
+            bytes_body,
+            200,
+            floatingip_id=self.FAKE_FLOATING_IP_ID)
+
+    def _test_update_floatingip(self, bytes_body=False):
+        update_kwargs = {
+            "port_id": "fc861431-0e6c-4842-a0ed-e2363f9bc3a8"
+        }
+
+        resp_body = {
+            "floatingip": copy.deepcopy(
+                self.FAKE_FLOATING_IPS["floatingips"][0]
+            )
+        }
+        resp_body["floatingip"].update(update_kwargs)
+
+        self.check_service_client_function(
+            self.floating_ips_client.update_floatingip,
+            "tempest.lib.common.rest_client.RestClient.put",
+            resp_body,
+            bytes_body,
+            200,
+            floatingip_id=self.FAKE_FLOATING_IP_ID,
+            **update_kwargs)
+
+    def test_list_floatingips_with_str_body(self):
+        self._test_list_floatingips()
+
+    def test_list_floatingips_with_bytes_body(self):
+        self._test_list_floatingips(bytes_body=True)
+
+    def test_create_floatingip_with_str_body(self):
+        self._test_create_floatingip()
+
+    def test_create_floatingip_with_bytes_body(self):
+        self._test_create_floatingip(bytes_body=True)
+
+    def test_show_floatingips_with_str_body(self):
+        self._test_show_floatingip()
+
+    def test_show_floatingips_with_bytes_body(self):
+        self._test_show_floatingip(bytes_body=True)
+
+    def test_update_floatingip_with_str_body(self):
+        self._test_update_floatingip()
+
+    def test_update_floatingip_with_bytes_body(self):
+        self._test_update_floatingip(bytes_body=True)
+
+    def test_delete_floatingip(self):
+        self.check_service_client_function(
+            self.floating_ips_client.delete_floatingip,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            status=204,
+            floatingip_id=self.FAKE_FLOATING_IP_ID)
diff --git a/tempest/tests/lib/services/network/test_metering_label_rules_client.py b/tempest/tests/lib/services/network/test_metering_label_rules_client.py
new file mode 100644
index 0000000..047c34f
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_metering_label_rules_client.py
@@ -0,0 +1,110 @@
+# Copyright 2017 AT&T 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.
+
+from tempest.lib.services.network import metering_label_rules_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestMeteringLabelRulesClient(base.BaseServiceTest):
+
+    FAKE_METERING_LABEL_RULES = {
+        "metering_label_rules": [
+            {
+                "remote_ip_prefix": "20.0.0.0/24",
+                "direction": "ingress",
+                "metering_label_id": "e131d186-b02d-4c0b-83d5-0c0725c4f812",
+                "id": "9536641a-7d14-4dc5-afaf-93a973ce0eb8",
+                "excluded": False
+            },
+            {
+                "remote_ip_prefix": "10.0.0.0/24",
+                "direction": "ingress",
+                "metering_label_id": "e131d186-b02d-4c0b-83d5-0c0725c4f812",
+                "id": "ffc6fd15-40de-4e7d-b617-34d3f7a93aec",
+                "excluded": False
+            }
+        ]
+    }
+
+    FAKE_METERING_LABEL_RULE = {
+        "remote_ip_prefix": "20.0.0.0/24",
+        "direction": "ingress",
+        "metering_label_id": "e131d186-b02d-4c0b-83d5-0c0725c4f812"
+    }
+
+    FAKE_METERING_LABEL_ID = "e131d186-b02d-4c0b-83d5-0c0725c4f812"
+    FAKE_METERING_LABEL_RULE_ID = "9536641a-7d14-4dc5-afaf-93a973ce0eb8"
+
+    def setUp(self):
+        super(TestMeteringLabelRulesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.metering_label_rules_client = \
+            metering_label_rules_client.MeteringLabelRulesClient(
+                fake_auth, "network", "regionOne")
+
+    def _test_list_metering_label_rules(self, bytes_body=False):
+        self.check_service_client_function(
+            self.metering_label_rules_client.list_metering_label_rules,
+            "tempest.lib.common.rest_client.RestClient.get",
+            self.FAKE_METERING_LABEL_RULES,
+            bytes_body,
+            200)
+
+    def _test_create_metering_label_rule(self, bytes_body=False):
+        self.check_service_client_function(
+            self.metering_label_rules_client.create_metering_label_rule,
+            "tempest.lib.common.rest_client.RestClient.post",
+            {"metering_label_rule": self.FAKE_METERING_LABEL_RULES[
+                "metering_label_rules"][0]},
+            bytes_body,
+            201,
+            **self.FAKE_METERING_LABEL_RULE)
+
+    def _test_show_metering_label_rule(self, bytes_body=False):
+        self.check_service_client_function(
+            self.metering_label_rules_client.show_metering_label_rule,
+            "tempest.lib.common.rest_client.RestClient.get",
+            {"metering_label_rule": self.FAKE_METERING_LABEL_RULES[
+                "metering_label_rules"][0]},
+            bytes_body,
+            200,
+            metering_label_rule_id=self.FAKE_METERING_LABEL_RULE_ID)
+
+    def test_delete_metering_label_rule(self):
+        self.check_service_client_function(
+            self.metering_label_rules_client.delete_metering_label_rule,
+            "tempest.lib.common.rest_client.RestClient.delete",
+            {},
+            status=204,
+            metering_label_rule_id=self.FAKE_METERING_LABEL_RULE_ID)
+
+    def test_list_metering_label_rules_with_str_body(self):
+        self._test_list_metering_label_rules()
+
+    def test_list_metering_label_rules_with_bytes_body(self):
+        self._test_list_metering_label_rules(bytes_body=True)
+
+    def test_create_metering_label_rule_with_str_body(self):
+        self._test_create_metering_label_rule()
+
+    def test_create_metering_label_rule_with_bytes_body(self):
+        self._test_create_metering_label_rule(bytes_body=True)
+
+    def test_show_metering_label_rule_with_str_body(self):
+        self._test_show_metering_label_rule()
+
+    def test_show_metering_label_rule_with_bytes_body(self):
+        self._test_show_metering_label_rule(bytes_body=True)
diff --git a/tempest/tests/lib/services/network/test_metering_labels_client.py b/tempest/tests/lib/services/network/test_metering_labels_client.py
new file mode 100644
index 0000000..a048326
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_metering_labels_client.py
@@ -0,0 +1,107 @@
+# Copyright 2017 AT&T 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.
+
+from tempest.lib.services.network import metering_labels_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestMeteringLabelsClient(base.BaseServiceTest):
+
+    FAKE_METERING_LABELS = {
+        "metering_labels": [
+            {
+                "project_id": "45345b0ee1ea477fac0f541b2cb79cd4",
+                "tenant_id": "45345b0ee1ea477fac0f541b2cb79cd4",
+                "description": "label1 description",
+                "name": "label1",
+                "id": "a6700594-5b7a-4105-8bfe-723b346ce866",
+                "shared": False
+            },
+            {
+                "project_id": "45345b0ee1ea477fac0f541b2cb79cd4",
+                "tenant_id": "45345b0ee1ea477fac0f541b2cb79cd4",
+                "description": "label2 description",
+                "name": "label2",
+                "id": "e131d186-b02d-4c0b-83d5-0c0725c4f812",
+                "shared": False
+            }
+        ]
+    }
+
+    FAKE_METERING_LABEL_ID = "a6700594-5b7a-4105-8bfe-723b346ce866"
+
+    def setUp(self):
+        super(TestMeteringLabelsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.metering_labels_client = \
+            metering_labels_client.MeteringLabelsClient(
+                fake_auth, "network", "regionOne")
+
+    def _test_list_metering_labels(self, bytes_body=False):
+        self.check_service_client_function(
+            self.metering_labels_client.list_metering_labels,
+            "tempest.lib.common.rest_client.RestClient.get",
+            self.FAKE_METERING_LABELS,
+            bytes_body,
+            200)
+
+    def _test_create_metering_label(self, bytes_body=False):
+        self.check_service_client_function(
+            self.metering_labels_client.create_metering_label,
+            "tempest.lib.common.rest_client.RestClient.post",
+            {"metering_label": self.FAKE_METERING_LABELS[
+                "metering_labels"][1]},
+            bytes_body,
+            201,
+            name="label1",
+            description="label1 description",
+            shared=False)
+
+    def _test_show_metering_label(self, bytes_body=False):
+        self.check_service_client_function(
+            self.metering_labels_client.show_metering_label,
+            "tempest.lib.common.rest_client.RestClient.get",
+            {"metering_label": self.FAKE_METERING_LABELS[
+                "metering_labels"][0]},
+            bytes_body,
+            200,
+            metering_label_id=self.FAKE_METERING_LABEL_ID)
+
+    def test_delete_metering_label(self):
+        self.check_service_client_function(
+            self.metering_labels_client.delete_metering_label,
+            "tempest.lib.common.rest_client.RestClient.delete",
+            {},
+            status=204,
+            metering_label_id=self.FAKE_METERING_LABEL_ID)
+
+    def test_list_metering_labels_with_str_body(self):
+        self._test_list_metering_labels()
+
+    def test_list_metering_labels_with_bytes_body(self):
+        self._test_list_metering_labels(bytes_body=True)
+
+    def test_create_metering_label_with_str_body(self):
+        self._test_create_metering_label()
+
+    def test_create_metering_label_with_bytes_body(self):
+        self._test_create_metering_label(bytes_body=True)
+
+    def test_show_metering_label_with_str_body(self):
+        self._test_show_metering_label()
+
+    def test_show_metering_label_with_bytes_body(self):
+        self._test_show_metering_label(bytes_body=True)
diff --git a/tempest/tests/lib/services/volume/v2/test_capabilities_client.py b/tempest/tests/lib/services/volume/v2/test_capabilities_client.py
new file mode 100644
index 0000000..3d3f1e1
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v2/test_capabilities_client.py
@@ -0,0 +1,77 @@
+# Copyright 2017 AT&T 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.
+
+from tempest.lib.services.volume.v2 import capabilities_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestCapabilitiesClient(base.BaseServiceTest):
+
+    FAKE_BACKEND_CAPABILITIES = {
+        "namespace": "OS::Storage::Capabilities::fake",
+        "vendor_name": "OpenStack",
+        "volume_backend_name": "lvmdriver-1",
+        "pool_name": "pool",
+        "driver_version": "2.0.0",
+        "storage_protocol": "iSCSI",
+        "display_name": "Capabilities of Cinder LVM driver",
+        "description": (
+            "These are volume type options provided by Cinder LVM driver."),
+        "visibility": "public",
+        "replication_targets": [],
+        "properties": {
+            "compression": {
+                "title": "Compression",
+                "description": "Enables compression.",
+                "type": "boolean"
+            },
+            "qos": {
+                "title": "QoS",
+                "description": "Enables QoS.",
+                "type": "boolean"
+            },
+            "replication": {
+                "title": "Replication",
+                "description": "Enables replication.",
+                "type": "boolean"
+            },
+            "thin_provisioning": {
+                "title": "Thin Provisioning",
+                "description": "Sets thin provisioning.",
+                "type": "boolean"
+            }
+        }
+    }
+
+    def setUp(self):
+        super(TestCapabilitiesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = capabilities_client.CapabilitiesClient(
+            fake_auth, 'volume', 'regionOne')
+
+    def _test_show_backend_capabilities(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_backend_capabilities,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_BACKEND_CAPABILITIES,
+            bytes_body,
+            host='lvmdriver-1')
+
+    def test_show_backend_capabilities_with_str_body(self):
+        self._test_show_backend_capabilities()
+
+    def test_show_backend_capabilities_with_bytes_body(self):
+        self._test_show_backend_capabilities(bytes_body=True)