Merge "Rename image_client to images_client"
diff --git a/HACKING.rst b/HACKING.rst
index 9f7487d..0962f80 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -330,24 +330,25 @@
         # The created server should be in the detailed list of all servers
         ...
 
-Tempest includes a ``check_uuid.py`` tool that will test for the existence
-and uniqueness of idempotent_id metadata for every test. By default the
-tool runs against the Tempest package by calling::
+Tempest-lib includes a ``check-uuid`` tool that will test for the existence
+and uniqueness of idempotent_id metadata for every test. If you have
+tempest-lib installed you run the tool against Tempest by calling from the
+tempest repo::
 
-    python check_uuid.py
+    check-uuid
 
 It can be invoked against any test suite by passing a package name::
 
-    python check_uuid.py --package <package_name>
+    check-uuid --package <package_name>
 
 Tests without an ``idempotent_id`` can be automatically fixed by running
 the command with the ``--fix`` flag, which will modify the source package
 by inserting randomly generated uuids for every test that does not have
 one::
 
-    python check_uuid.py --fix
+    check-uuid --fix
 
-The ``check_uuid.py`` tool is used as part of the tempest gate job
+The ``check-uuid`` tool is used as part of the tempest gate job
 to ensure that all tests have an ``idempotent_id`` decorator.
 
 Branchless Tempest Considerations
diff --git a/tempest/api/identity/admin/v3/test_users_negative.py b/tempest/api/identity/admin/v3/test_users_negative.py
index ca2aaa4..d40a5b9 100644
--- a/tempest/api/identity/admin/v3/test_users_negative.py
+++ b/tempest/api/identity/admin/v3/test_users_negative.py
@@ -33,3 +33,14 @@
                           u_name, u_password,
                           email=u_email,
                           domain_id=data_utils.rand_uuid_hex())
+
+    @test.attr(type=['negative'])
+    @test.idempotent_id('b3c9fccc-4134-46f5-b600-1da6fb0a3b1f')
+    def test_authentication_for_disabled_user(self):
+        # Attempt to authenticate for disabled user should fail
+        self.data.setup_test_v3_user()
+        self.disable_user(self.data.test_user)
+        self.assertRaises(lib_exc.Unauthorized, self.token.auth,
+                          username=self.data.test_user,
+                          password=self.data.test_password,
+                          user_domain_id='default')
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index c56f4fb..0364f3a 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -170,6 +170,11 @@
         if len(role) > 0:
             return role[0]
 
+    @classmethod
+    def disable_user(cls, user_name):
+        user = cls.get_user_by_name(user_name)
+        cls.client.update_user(user['id'], user_name, enabled=False)
+
     def delete_domain(self, domain_id):
         # NOTE(mpavlase) It is necessary to disable the domain before deleting
         # otherwise it raises Forbidden exception
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index b798cb2..b4ea29b 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -105,7 +105,7 @@
             # Clean up metering labels
             for metering_label in cls.metering_labels:
                 cls._try_delete_resource(
-                    cls.admin_client.delete_metering_label,
+                    cls.admin_metering_labels_client.delete_metering_label,
                     metering_label['id'])
             # Clean up ports
             for port in cls.ports:
@@ -272,11 +272,12 @@
         cls.admin_subnets_client = cls.os_adm.subnets_client
         cls.admin_ports_client = cls.os_adm.ports_client
         cls.admin_floating_ips_client = cls.os_adm.floating_ips_client
+        cls.admin_metering_labels_client = cls.os_adm.metering_labels_client
 
     @classmethod
     def create_metering_label(cls, name, description):
         """Wrapper utility that returns a test metering label."""
-        body = cls.admin_client.create_metering_label(
+        body = cls.admin_metering_labels_client.create_metering_label(
             description=description,
             name=data_utils.rand_name("metering-label"))
         metering_label = body['metering_label']
diff --git a/tempest/api/network/test_metering_extensions.py b/tempest/api/network/test_metering_extensions.py
index a213f92..c4021b4 100644
--- a/tempest/api/network/test_metering_extensions.py
+++ b/tempest/api/network/test_metering_extensions.py
@@ -51,9 +51,11 @@
 
     def _delete_metering_label(self, metering_label_id):
         # Deletes a label and verifies if it is deleted or not
-        self.admin_client.delete_metering_label(metering_label_id)
+        self.admin_metering_labels_client.delete_metering_label(
+            metering_label_id)
         # Asserting that the label is not found in list after deletion
-        labels = self.admin_client.list_metering_labels(id=metering_label_id)
+        labels = self.admin_metering_labels_client.list_metering_labels(
+            id=metering_label_id)
         self.assertEqual(len(labels['metering_labels']), 0)
 
     def _delete_metering_label_rule(self, metering_label_rule_id):
@@ -68,7 +70,7 @@
     @test.idempotent_id('e2fb2f8c-45bf-429a-9f17-171c70444612')
     def test_list_metering_labels(self):
         # Verify label filtering
-        body = self.admin_client.list_metering_labels(id=33)
+        body = self.admin_metering_labels_client.list_metering_labels(id=33)
         metering_labels = body['metering_labels']
         self.assertEqual(0, len(metering_labels))
 
@@ -77,21 +79,22 @@
         # Creates a label
         name = data_utils.rand_name('metering-label-')
         description = "label created by tempest"
-        body = self.admin_client.create_metering_label(name=name,
-                                                       description=description)
+        body = self.admin_metering_labels_client.create_metering_label(
+            name=name, description=description)
         metering_label = body['metering_label']
         self.addCleanup(self._delete_metering_label,
                         metering_label['id'])
         # Assert whether created labels are found in labels list or fail
         # if created labels are not found in labels list
-        labels = (self.admin_client.list_metering_labels(
+        labels = (self.admin_metering_labels_client.list_metering_labels(
                   id=metering_label['id']))
         self.assertEqual(len(labels['metering_labels']), 1)
 
     @test.idempotent_id('30abb445-0eea-472e-bd02-8649f54a5968')
     def test_show_metering_label(self):
         # Verifies the details of a label
-        body = self.admin_client.show_metering_label(self.metering_label['id'])
+        body = self.admin_metering_labels_client.show_metering_label(
+            self.metering_label['id'])
         metering_label = body['metering_label']
         self.assertEqual(self.metering_label['id'], metering_label['id'])
         self.assertEqual(self.metering_label['tenant_id'],
diff --git a/tempest/api_schema/response/compute/v2_1/agents.py b/tempest/api_schema/response/compute/v2_1/agents.py
deleted file mode 100644
index da38198..0000000
--- a/tempest/api_schema/response/compute/v2_1/agents.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# Copyright 2014 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.
-
-common_agent_info = {
-    'type': 'object',
-    'properties': {
-        'agent_id': {'type': ['integer', 'string']},
-        'hypervisor': {'type': 'string'},
-        'os': {'type': 'string'},
-        'architecture': {'type': 'string'},
-        'version': {'type': 'string'},
-        'url': {'type': 'string', 'format': 'uri'},
-        'md5hash': {'type': 'string'}
-    },
-    'additionalProperties': False,
-    'required': ['agent_id', 'hypervisor', 'os', 'architecture',
-                 'version', 'url', 'md5hash']
-}
-
-list_agents = {
-    'status_code': [200],
-    'response_body': {
-        'type': 'object',
-        'properties': {
-            'agents': {
-                'type': 'array',
-                'items': common_agent_info
-            }
-        },
-        'additionalProperties': False,
-        'required': ['agents']
-    }
-}
-
-create_agent = {
-    'status_code': [200],
-    'response_body': {
-        'type': 'object',
-        'properties': {
-            'agent': common_agent_info
-        },
-        'additionalProperties': False,
-        'required': ['agent']
-    }
-}
-
-delete_agent = {
-    'status_code': [200]
-}
diff --git a/tempest/clients.py b/tempest/clients.py
index 257fa49..d16b47b 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -108,6 +108,8 @@
 from tempest.services.messaging.json.messaging_client import \
     MessagingClient
 from tempest.services.network.json.floating_ips_client import FloatingIPsClient
+from tempest.services.network.json.metering_labels_client import \
+    MeteringLabelsClient
 from tempest.services.network.json.network_client import NetworkClient
 from tempest.services.network.json.networks_client import NetworksClient
 from tempest.services.network.json.ports_client import PortsClient
@@ -231,6 +233,14 @@
             build_interval=CONF.network.build_interval,
             build_timeout=CONF.network.build_timeout,
             **self.default_params)
+        self.metering_labels_client = MeteringLabelsClient(
+            self.auth_provider,
+            CONF.network.catalog_type,
+            CONF.network.region or CONF.identity.region,
+            endpoint_type=CONF.network.endpoint_type,
+            build_interval=CONF.network.build_interval,
+            build_timeout=CONF.network.build_timeout,
+            **self.default_params)
         self.messaging_client = MessagingClient(
             self.auth_provider,
             CONF.messaging.catalog_type,
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index ba6bf6c..f2dd7af 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -387,6 +387,7 @@
         self.subnets_client = manager.subnets_client
         self.ports_client = manager.ports_client
         self.floating_ips_client = manager.floating_ips_client
+        self.metering_labels_client = manager.metering_labels_client
 
     def _filter_by_conf_networks(self, item_list):
         if not item_list or not all(('network_id' in i for i in item_list)):
@@ -600,7 +601,7 @@
 class NetworkMeteringLabelService(NetworkService):
 
     def list(self):
-        client = self.client
+        client = self.metering_labels_client
         labels = client.list_metering_labels()
         labels = labels['metering_labels']
         labels = self._filter_by_tenant_id(labels)
@@ -608,7 +609,7 @@
         return labels
 
     def delete(self):
-        client = self.client
+        client = self.metering_labels_client
         labels = self.list()
         for label in labels:
             try:
diff --git a/tempest/common/glance_http.py b/tempest/common/glance_http.py
index e5431a0..800e977 100644
--- a/tempest/common/glance_http.py
+++ b/tempest/common/glance_http.py
@@ -24,12 +24,10 @@
 
 import OpenSSL
 from oslo_log import log as logging
-from oslo_serialization import jsonutils as json
 import six
 from six import moves
 from six.moves import http_client as httplib
 from six.moves.urllib import parse as urlparse
-from tempest_lib import exceptions as lib_exc
 
 from tempest import exceptions as exc
 
@@ -51,19 +49,20 @@
         self.endpoint_port = endpoint_parts.port
         self.endpoint_path = endpoint_parts.path
 
-        self.connection_class = self.get_connection_class(self.endpoint_scheme)
-        self.connection_kwargs = self.get_connection_kwargs(
+        self.connection_class = self._get_connection_class(
+            self.endpoint_scheme)
+        self.connection_kwargs = self._get_connection_kwargs(
             self.endpoint_scheme, **kwargs)
 
     @staticmethod
-    def get_connection_class(scheme):
+    def _get_connection_class(scheme):
         if scheme == 'https':
             return VerifiedHTTPSConnection
         else:
             return httplib.HTTPConnection
 
     @staticmethod
-    def get_connection_kwargs(scheme, **kwargs):
+    def _get_connection_kwargs(scheme, **kwargs):
         _kwargs = {'timeout': float(kwargs.get('timeout', 600))}
 
         if scheme == 'https':
@@ -75,7 +74,7 @@
 
         return _kwargs
 
-    def get_connection(self):
+    def _get_connection(self):
         _class = self.connection_class
         try:
             return _class(self.endpoint_hostname, self.endpoint_port,
@@ -95,7 +94,7 @@
 
         self._log_request(method, url, kwargs['headers'])
 
-        conn = self.get_connection()
+        conn = self._get_connection()
 
         try:
             url_parts = urlparse.urlparse(url)
@@ -159,30 +158,6 @@
                 self.LOG.debug("Large body (%d) md5 summary: %s", length,
                                hashlib.md5(str_body).hexdigest())
 
-    def json_request(self, method, url, **kwargs):
-        kwargs.setdefault('headers', {})
-        kwargs['headers'].setdefault('Content-Type', 'application/json')
-        if kwargs['headers']['Content-Type'] != 'application/json':
-            msg = "Only application/json content-type is supported."
-            raise lib_exc.InvalidContentType(msg)
-
-        if 'body' in kwargs:
-            kwargs['body'] = json.dumps(kwargs['body'])
-
-        resp, body_iter = self._http_request(url, method, **kwargs)
-
-        if 'application/json' in resp.getheader('content-type', ''):
-            body = ''.join([chunk for chunk in body_iter])
-            try:
-                body = json.loads(body)
-            except ValueError:
-                LOG.error('Could not decode response body as JSON')
-        else:
-            msg = "Only json/application content-type is supported."
-            raise lib_exc.InvalidContentType(msg)
-
-        return resp, body
-
     def raw_request(self, method, url, **kwargs):
         kwargs.setdefault('headers', {})
         kwargs['headers'].setdefault('Content-Type',
diff --git a/tempest/services/network/json/metering_labels_client.py b/tempest/services/network/json/metering_labels_client.py
new file mode 100644
index 0000000..2e5cdae
--- /dev/null
+++ b/tempest/services/network/json/metering_labels_client.py
@@ -0,0 +1,33 @@
+#    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.services.network.json import base
+
+
+class MeteringLabelsClient(base.BaseNetworkClient):
+
+    def create_metering_label(self, **kwargs):
+        uri = '/metering/metering-labels'
+        post_data = {'metering_label': kwargs}
+        return self.create_resource(uri, post_data)
+
+    def show_metering_label(self, metering_label_id, **fields):
+        uri = '/metering/metering-labels/%s' % metering_label_id
+        return self.show_resource(uri, **fields)
+
+    def delete_metering_label(self, metering_label_id):
+        uri = '/metering/metering-labels/%s' % metering_label_id
+        return self.delete_resource(uri)
+
+    def list_metering_labels(self, **filters):
+        uri = '/metering/metering-labels'
+        return self.list_resources(uri, **filters)
diff --git a/tempest/services/network/json/network_client.py b/tempest/services/network/json/network_client.py
index 5a4229c..b525143 100644
--- a/tempest/services/network/json/network_client.py
+++ b/tempest/services/network/json/network_client.py
@@ -35,23 +35,6 @@
     quotas
     """
 
-    def create_metering_label(self, **kwargs):
-        uri = '/metering/metering-labels'
-        post_data = {'metering_label': kwargs}
-        return self.create_resource(uri, post_data)
-
-    def show_metering_label(self, metering_label_id, **fields):
-        uri = '/metering/metering-labels/%s' % metering_label_id
-        return self.show_resource(uri, **fields)
-
-    def delete_metering_label(self, metering_label_id):
-        uri = '/metering/metering-labels/%s' % metering_label_id
-        return self.delete_resource(uri)
-
-    def list_metering_labels(self, **filters):
-        uri = '/metering/metering-labels'
-        return self.list_resources(uri, **filters)
-
     def create_metering_label_rule(self, **kwargs):
         uri = '/metering/metering-label-rules'
         post_data = {'metering_label_rule': kwargs}
diff --git a/tempest/services/volume/base/admin/base_volume_quotas_client.py b/tempest/services/volume/base/admin/base_volume_quotas_client.py
index d7909ff..ad8ba03 100644
--- a/tempest/services/volume/base/admin/base_volume_quotas_client.py
+++ b/tempest/services/volume/base/admin/base_volume_quotas_client.py
@@ -50,21 +50,14 @@
         body = self.show_quota_set(tenant_id, params={'usage': True})
         return body
 
-    def update_quota_set(self, tenant_id, gigabytes=None, volumes=None,
-                         snapshots=None):
-        post_body = {}
+    def update_quota_set(self, tenant_id, **kwargs):
+        """Updates quota set
 
-        if gigabytes is not None:
-            post_body['gigabytes'] = gigabytes
-
-        if volumes is not None:
-            post_body['volumes'] = volumes
-
-        if snapshots is not None:
-            post_body['snapshots'] = snapshots
-
-        post_body = jsonutils.dumps({'quota_set': post_body})
-        resp, body = self.put('os-quota-sets/%s' % tenant_id, post_body)
+        Available params: see http://developer.openstack.org/
+                              api-ref-blockstorage-v2.html#updateQuotas-v2
+        """
+        put_body = jsonutils.dumps({'quota_set': kwargs})
+        resp, body = self.put('os-quota-sets/%s' % tenant_id, put_body)
         self.expected_success(200, resp.status)
         body = jsonutils.loads(body)
         return service_client.ResponseBody(resp, body)
diff --git a/tempest/test.py b/tempest/test.py
index 435d10a..30eb93d 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -27,6 +27,7 @@
 from oslo_serialization import jsonutils as json
 from oslo_utils import importutils
 import six
+from tempest_lib import decorators
 import testscenarios
 import testtools
 
@@ -43,6 +44,8 @@
 
 CONF = config.CONF
 
+idempotent_id = decorators.idempotent_id
+
 
 def attr(**kwargs):
     """A decorator which applies the testtools attr decorator
@@ -62,23 +65,6 @@
     return decorator
 
 
-def idempotent_id(id):
-    """Stub for metadata decorator"""
-    if not isinstance(id, six.string_types):
-        raise TypeError('Test idempotent_id must be string not %s'
-                        '' % type(id).__name__)
-    uuid.UUID(id)
-
-    def decorator(f):
-        f = testtools.testcase.attr('id-%s' % id)(f)
-        if f.__doc__:
-            f.__doc__ = 'Test idempotent id: %s\n%s' % (id, f.__doc__)
-        else:
-            f.__doc__ = 'Test idempotent id: %s' % id
-        return f
-    return decorator
-
-
 def get_service_list():
     service_list = {
         'compute': CONF.service_available.nova,
diff --git a/tempest/tests/test_glance_http.py b/tempest/tests/test_glance_http.py
index 105caec..ed886da 100644
--- a/tempest/tests/test_glance_http.py
+++ b/tempest/tests/test_glance_http.py
@@ -13,14 +13,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import socket
-
 import mock
-from oslo_serialization import jsonutils as json
 from oslotest import mockpatch
 import six
 from six.moves import http_client as httplib
-from tempest_lib import exceptions as lib_exc
 
 from tempest.common import glance_http
 from tempest import exceptions
@@ -56,60 +52,6 @@
                         'getresponse', return_value=resp))
         return resp
 
-    def test_json_request_without_content_type_header_in_response(self):
-        self._set_response_fixture({}, 200, 'fake_response_body')
-        self.assertRaises(lib_exc.InvalidContentType,
-                          self.client.json_request, 'GET', '/images')
-
-    def test_json_request_with_xml_content_type_header_in_request(self):
-        self.assertRaises(lib_exc.InvalidContentType,
-                          self.client.json_request, 'GET', '/images',
-                          headers={'Content-Type': 'application/xml'})
-
-    def test_json_request_with_xml_content_type_header_in_response(self):
-        self._set_response_fixture({'content-type': 'application/xml'},
-                                   200, 'fake_response_body')
-        self.assertRaises(lib_exc.InvalidContentType,
-                          self.client.json_request, 'GET', '/images')
-
-    def test_json_request_with_json_content_type_header_only_in_resp(self):
-        self._set_response_fixture({'content-type': 'application/json'},
-                                   200, 'fake_response_body')
-        resp, body = self.client.json_request('GET', '/images')
-        self.assertEqual(200, resp.status)
-        self.assertEqual('fake_response_body', body)
-
-    def test_json_request_with_json_content_type_header_in_req_and_resp(self):
-        self._set_response_fixture({'content-type': 'application/json'},
-                                   200, 'fake_response_body')
-        resp, body = self.client.json_request('GET', '/images', headers={
-            'Content-Type': 'application/json'})
-        self.assertEqual(200, resp.status)
-        self.assertEqual('fake_response_body', body)
-
-    def test_json_request_fails_to_json_loads(self):
-        self._set_response_fixture({'content-type': 'application/json'},
-                                   200, 'fake_response_body')
-        self.useFixture(mockpatch.PatchObject(json, 'loads',
-                        side_effect=ValueError()))
-        resp, body = self.client.json_request('GET', '/images')
-        self.assertEqual(200, resp.status)
-        self.assertEqual(body, 'fake_response_body')
-
-    def test_json_request_socket_timeout(self):
-        self.useFixture(mockpatch.PatchObject(httplib.HTTPConnection,
-                                              'request',
-                                              side_effect=socket.timeout()))
-        self.assertRaises(exceptions.TimeoutException,
-                          self.client.json_request, 'GET', '/images')
-
-    def test_json_request_endpoint_not_found(self):
-        self.useFixture(mockpatch.PatchObject(httplib.HTTPConnection,
-                                              'request',
-                                              side_effect=socket.gaierror()))
-        self.assertRaises(exceptions.EndpointNotFound,
-                          self.client.json_request, 'GET', '/images')
-
     def test_raw_request(self):
         self._set_response_fixture({}, 200, 'fake_response_body')
         resp, body = self.client.raw_request('GET', '/images')
@@ -141,22 +83,22 @@
         self.assertEqual(call_count - 1, req_body.tell())
 
     def test_get_connection_class_for_https(self):
-        conn_class = self.client.get_connection_class('https')
+        conn_class = self.client._get_connection_class('https')
         self.assertEqual(glance_http.VerifiedHTTPSConnection, conn_class)
 
     def test_get_connection_class_for_http(self):
-        conn_class = (self.client.get_connection_class('http'))
+        conn_class = (self.client._get_connection_class('http'))
         self.assertEqual(httplib.HTTPConnection, conn_class)
 
     def test_get_connection_http(self):
-        self.assertTrue(isinstance(self.client.get_connection(),
+        self.assertTrue(isinstance(self.client._get_connection(),
                                    httplib.HTTPConnection))
 
     def test_get_connection_https(self):
         endpoint = 'https://fake_url.com'
         self.fake_auth.base_url = mock.MagicMock(return_value=endpoint)
         self.client = glance_http.HTTPClient(self.fake_auth, {})
-        self.assertTrue(isinstance(self.client.get_connection(),
+        self.assertTrue(isinstance(self.client._get_connection(),
                                    glance_http.VerifiedHTTPSConnection))
 
     def test_get_connection_url_not_fount(self):
@@ -164,22 +106,22 @@
                                               side_effect=httplib.InvalidURL()
                                               ))
         self.assertRaises(exceptions.EndpointNotFound,
-                          self.client.get_connection)
+                          self.client._get_connection)
 
     def test_get_connection_kwargs_default_for_http(self):
-        kwargs = self.client.get_connection_kwargs('http')
+        kwargs = self.client._get_connection_kwargs('http')
         self.assertEqual(600, kwargs['timeout'])
         self.assertEqual(1, len(kwargs.keys()))
 
     def test_get_connection_kwargs_set_timeout_for_http(self):
-        kwargs = self.client.get_connection_kwargs('http', timeout=10,
-                                                   ca_certs='foo')
+        kwargs = self.client._get_connection_kwargs('http', timeout=10,
+                                                    ca_certs='foo')
         self.assertEqual(10, kwargs['timeout'])
         # nothing more than timeout is evaluated for http connections
         self.assertEqual(1, len(kwargs.keys()))
 
     def test_get_connection_kwargs_default_for_https(self):
-        kwargs = self.client.get_connection_kwargs('https')
+        kwargs = self.client._get_connection_kwargs('https')
         self.assertEqual(600, kwargs['timeout'])
         self.assertEqual(None, kwargs['ca_certs'])
         self.assertEqual(None, kwargs['cert_file'])
@@ -189,12 +131,12 @@
         self.assertEqual(6, len(kwargs.keys()))
 
     def test_get_connection_kwargs_set_params_for_https(self):
-        kwargs = self.client.get_connection_kwargs('https', timeout=10,
-                                                   ca_certs='foo',
-                                                   cert_file='/foo/bar.cert',
-                                                   key_file='/foo/key.pem',
-                                                   insecure=True,
-                                                   ssl_compression=False)
+        kwargs = self.client._get_connection_kwargs('https', timeout=10,
+                                                    ca_certs='foo',
+                                                    cert_file='/foo/bar.cert',
+                                                    key_file='/foo/key.pem',
+                                                    insecure=True,
+                                                    ssl_compression=False)
         self.assertEqual(10, kwargs['timeout'])
         self.assertEqual('foo', kwargs['ca_certs'])
         self.assertEqual('/foo/bar.cert', kwargs['cert_file'])
diff --git a/tools/check_uuid.py b/tools/check_uuid.py
deleted file mode 100755
index a71ad39..0000000
--- a/tools/check_uuid.py
+++ /dev/null
@@ -1,358 +0,0 @@
-#!/usr/bin/env python
-
-# Copyright 2014 Mirantis, Inc.
-#
-# 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 argparse
-import ast
-import importlib
-import inspect
-import os
-import sys
-import unittest
-import urllib
-import uuid
-
-DECORATOR_MODULE = 'test'
-DECORATOR_NAME = 'idempotent_id'
-DECORATOR_IMPORT = 'tempest.%s' % DECORATOR_MODULE
-IMPORT_LINE = 'from tempest import %s' % DECORATOR_MODULE
-DECORATOR_TEMPLATE = "@%s.%s('%%s')" % (DECORATOR_MODULE,
-                                        DECORATOR_NAME)
-UNIT_TESTS_EXCLUDE = 'tempest.tests'
-
-
-class SourcePatcher(object):
-
-    """"Lazy patcher for python source files"""
-
-    def __init__(self):
-        self.source_files = None
-        self.patches = None
-        self.clear()
-
-    def clear(self):
-        """Clear inner state"""
-        self.source_files = {}
-        self.patches = {}
-
-    @staticmethod
-    def _quote(s):
-        return urllib.quote(s)
-
-    @staticmethod
-    def _unquote(s):
-        return urllib.unquote(s)
-
-    def add_patch(self, filename, patch, line_no):
-        """Add lazy patch"""
-        if filename not in self.source_files:
-            with open(filename) as f:
-                self.source_files[filename] = self._quote(f.read())
-        patch_id = str(uuid.uuid4())
-        if not patch.endswith('\n'):
-            patch += '\n'
-        self.patches[patch_id] = self._quote(patch)
-        lines = self.source_files[filename].split(self._quote('\n'))
-        lines[line_no - 1] = ''.join(('{%s:s}' % patch_id, lines[line_no - 1]))
-        self.source_files[filename] = self._quote('\n').join(lines)
-
-    def _save_changes(self, filename, source):
-        print('%s fixed' % filename)
-        with open(filename, 'w') as f:
-            f.write(source)
-
-    def apply_patches(self):
-        """Apply all patches"""
-        for filename in self.source_files:
-            patched_source = self._unquote(
-                self.source_files[filename].format(**self.patches)
-            )
-            self._save_changes(filename, patched_source)
-        self.clear()
-
-
-class TestChecker(object):
-
-    def __init__(self, package):
-        self.package = package
-        self.base_path = os.path.abspath(os.path.dirname(package.__file__))
-
-    def _path_to_package(self, path):
-        relative_path = path[len(self.base_path) + 1:]
-        if relative_path:
-            return '.'.join((self.package.__name__,) +
-                            tuple(relative_path.split('/')))
-        else:
-            return self.package.__name__
-
-    def _modules_search(self):
-        """Recursive search for python modules in base package"""
-        modules = []
-        for root, dirs, files in os.walk(self.base_path):
-            if not os.path.exists(os.path.join(root, '__init__.py')):
-                continue
-            root_package = self._path_to_package(root)
-            for item in files:
-                if item.endswith('.py'):
-                    module_name = '.'.join((root_package,
-                                           os.path.splitext(item)[0]))
-                    if not module_name.startswith(UNIT_TESTS_EXCLUDE):
-                        modules.append(module_name)
-        return modules
-
-    @staticmethod
-    def _get_idempotent_id(test_node):
-        # Return key-value dict with all metadata from @test.idempotent_id
-        # decorators for test method
-        idempotent_id = None
-        for decorator in test_node.decorator_list:
-            if (hasattr(decorator, 'func') and
-                hasattr(decorator.func, 'attr') and
-                decorator.func.attr == DECORATOR_NAME and
-                hasattr(decorator.func, 'value') and
-                decorator.func.value.id == DECORATOR_MODULE):
-                for arg in decorator.args:
-                    idempotent_id = ast.literal_eval(arg)
-        return idempotent_id
-
-    @staticmethod
-    def _is_decorator(line):
-        return line.strip().startswith('@')
-
-    @staticmethod
-    def _is_def(line):
-        return line.strip().startswith('def ')
-
-    def _add_uuid_to_test(self, patcher, test_node, source_path):
-        with open(source_path) as src:
-            src_lines = src.read().split('\n')
-        lineno = test_node.lineno
-        insert_position = lineno
-        while True:
-            if (self._is_def(src_lines[lineno - 1]) or
-                    (self._is_decorator(src_lines[lineno - 1]) and
-                        (DECORATOR_TEMPLATE.split('(')[0] <=
-                            src_lines[lineno - 1].strip().split('(')[0]))):
-                insert_position = lineno
-                break
-            lineno += 1
-        patcher.add_patch(
-            source_path,
-            ' ' * test_node.col_offset + DECORATOR_TEMPLATE % uuid.uuid4(),
-            insert_position
-        )
-
-    @staticmethod
-    def _is_test_case(module, node):
-        if (node.__class__ is ast.ClassDef and
-                hasattr(module, node.name) and
-                inspect.isclass(getattr(module, node.name))):
-            return issubclass(getattr(module, node.name), unittest.TestCase)
-
-    @staticmethod
-    def _is_test_method(node):
-        return (node.__class__ is ast.FunctionDef
-                and node.name.startswith('test_'))
-
-    @staticmethod
-    def _next_node(body, node):
-        if body.index(node) < len(body):
-            return body[body.index(node) + 1]
-
-    @staticmethod
-    def _import_name(node):
-        if type(node) == ast.Import:
-            return node.names[0].name
-        elif type(node) == ast.ImportFrom:
-            return '%s.%s' % (node.module, node.names[0].name)
-
-    def _add_import_for_test_uuid(self, patcher, src_parsed, source_path):
-        with open(source_path) as f:
-            src_lines = f.read().split('\n')
-        line_no = 0
-        tempest_imports = [node for node in src_parsed.body
-                           if self._import_name(node) and
-                           'tempest.' in self._import_name(node)]
-        if not tempest_imports:
-            import_snippet = '\n'.join(('', IMPORT_LINE, ''))
-        else:
-            for node in tempest_imports:
-                if self._import_name(node) < DECORATOR_IMPORT:
-                    continue
-                else:
-                    line_no = node.lineno
-                    import_snippet = IMPORT_LINE
-                    break
-            else:
-                line_no = tempest_imports[-1].lineno
-                while True:
-                    if (not src_lines[line_no - 1] or
-                            getattr(self._next_node(src_parsed.body,
-                                                    tempest_imports[-1]),
-                                    'lineno') == line_no or
-                            line_no == len(src_lines)):
-                        break
-                    line_no += 1
-                import_snippet = '\n'.join((IMPORT_LINE, ''))
-        patcher.add_patch(source_path, import_snippet, line_no)
-
-    def get_tests(self):
-        """Get test methods with sources from base package with metadata"""
-        tests = {}
-        for module_name in self._modules_search():
-            tests[module_name] = {}
-            module = importlib.import_module(module_name)
-            source_path = '.'.join(
-                (os.path.splitext(module.__file__)[0], 'py')
-            )
-            with open(source_path, 'r') as f:
-                source = f.read()
-            tests[module_name]['source_path'] = source_path
-            tests[module_name]['tests'] = {}
-            source_parsed = ast.parse(source)
-            tests[module_name]['ast'] = source_parsed
-            tests[module_name]['import_valid'] = (
-                hasattr(module, DECORATOR_MODULE) and
-                inspect.ismodule(getattr(module, DECORATOR_MODULE))
-            )
-            test_cases = (node for node in source_parsed.body
-                          if self._is_test_case(module, node))
-            for node in test_cases:
-                for subnode in filter(self._is_test_method, node.body):
-                        test_name = '%s.%s' % (node.name, subnode.name)
-                        tests[module_name]['tests'][test_name] = subnode
-        return tests
-
-    @staticmethod
-    def _filter_tests(function, tests):
-        """Filter tests with condition 'function(test_node) == True'"""
-        result = {}
-        for module_name in tests:
-            for test_name in tests[module_name]['tests']:
-                if function(module_name, test_name, tests):
-                    if module_name not in result:
-                        result[module_name] = {
-                            'ast': tests[module_name]['ast'],
-                            'source_path': tests[module_name]['source_path'],
-                            'import_valid': tests[module_name]['import_valid'],
-                            'tests': {}
-                        }
-                    result[module_name]['tests'][test_name] = \
-                        tests[module_name]['tests'][test_name]
-        return result
-
-    def find_untagged(self, tests):
-        """Filter all tests without uuid in metadata"""
-        def check_uuid_in_meta(module_name, test_name, tests):
-            idempotent_id = self._get_idempotent_id(
-                tests[module_name]['tests'][test_name])
-            return not idempotent_id
-        return self._filter_tests(check_uuid_in_meta, tests)
-
-    def report_collisions(self, tests):
-        """Reports collisions if there are any.
-
-        Returns true if collisions exist.
-        """
-        uuids = {}
-
-        def report(module_name, test_name, tests):
-            test_uuid = self._get_idempotent_id(
-                tests[module_name]['tests'][test_name])
-            if not test_uuid:
-                return
-            if test_uuid in uuids:
-                error_str = "%s:%s\n uuid %s collision: %s<->%s\n%s:%s" % (
-                    tests[module_name]['source_path'],
-                    tests[module_name]['tests'][test_name].lineno,
-                    test_uuid,
-                    test_name,
-                    uuids[test_uuid]['test_name'],
-                    uuids[test_uuid]['source_path'],
-                    uuids[test_uuid]['test_node'].lineno,
-                )
-                print(error_str)
-                print("cannot automatically resolve the collision, please "
-                      "manually remove the duplicate value on the new test.")
-                return True
-            else:
-                uuids[test_uuid] = {
-                    'module': module_name,
-                    'test_name': test_name,
-                    'test_node': tests[module_name]['tests'][test_name],
-                    'source_path': tests[module_name]['source_path']
-                }
-        return bool(self._filter_tests(report, tests))
-
-    def report_untagged(self, tests):
-        """Reports untagged tests if there are any.
-
-        Returns true if untagged tests exist.
-        """
-        def report(module_name, test_name, tests):
-            error_str = "%s:%s\nmissing @test.idempotent_id('...')\n%s\n" % (
-                tests[module_name]['source_path'],
-                tests[module_name]['tests'][test_name].lineno,
-                test_name
-            )
-            print(error_str)
-            return True
-        return bool(self._filter_tests(report, tests))
-
-    def fix_tests(self, tests):
-        """Add uuids to all tests specified in tests and fix it"""
-        patcher = SourcePatcher()
-        for module_name in tests:
-            add_import_once = True
-            for test_name in tests[module_name]['tests']:
-                if not tests[module_name]['import_valid'] and add_import_once:
-                    self._add_import_for_test_uuid(
-                        patcher,
-                        tests[module_name]['ast'],
-                        tests[module_name]['source_path']
-                    )
-                    add_import_once = False
-                self._add_uuid_to_test(
-                    patcher, tests[module_name]['tests'][test_name],
-                    tests[module_name]['source_path'])
-        patcher.apply_patches()
-
-
-def run():
-    parser = argparse.ArgumentParser()
-    parser.add_argument('--package', action='store', dest='package',
-                        default='tempest', type=str,
-                        help='Package with tests')
-    parser.add_argument('--fix', action='store_true', dest='fix_tests',
-                        help='Attempt to fix tests without UUIDs')
-    args = parser.parse_args()
-    sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
-    pkg = importlib.import_module(args.package)
-    checker = TestChecker(pkg)
-    errors = False
-    tests = checker.get_tests()
-    untagged = checker.find_untagged(tests)
-    errors = checker.report_collisions(tests) or errors
-    if args.fix_tests and untagged:
-        checker.fix_tests(untagged)
-    else:
-        errors = checker.report_untagged(untagged) or errors
-    if errors:
-        sys.exit("@test.idempotent_id existence and uniqueness checks failed\n"
-                 "Run 'tox -v -euuidgen' to automatically fix tests with\n"
-                 "missing @test.idempotent_id decorators.")
-
-if __name__ == '__main__':
-    run()
diff --git a/tox.ini b/tox.ini
index ecd4f24..41eece1 100644
--- a/tox.ini
+++ b/tox.ini
@@ -114,11 +114,11 @@
 [testenv:pep8]
 commands =
    flake8 {posargs}
-   python tools/check_uuid.py
+   check-uuid
 
 [testenv:uuidgen]
 commands =
-   python tools/check_uuid.py --fix
+   check-uuid --fix
 
 [hacking]
 local-check-factory = tempest.hacking.checks.factory