Merge "Add extra unit tests for base_url"
diff --git a/README.rst b/README.rst
index bc58cc0..281516b 100644
--- a/README.rst
+++ b/README.rst
@@ -4,6 +4,7 @@
 
 .. image:: http://governance.openstack.org/badges/tempest.svg
     :target: http://governance.openstack.org/reference/tags/index.html
+    :remote:
 
 .. Change things from this point on
 
diff --git a/bindep.txt b/bindep.txt
new file mode 100644
index 0000000..6a28348
--- /dev/null
+++ b/bindep.txt
@@ -0,0 +1,11 @@
+# This file contains runtime (non-python) dependencies
+# More info at: http://docs.openstack.org/infra/bindep/readme.html
+
+libffi-dev [platform:dpkg]
+libffi-devel [platform:rpm]
+gcc [platform:rpm]
+gcc [platform:dpkg]
+python-dev [platform:dpkg]
+python-devel [platform:rpm]
+openssl-devel [platform:rpm]
+libssl-dev [platform:dpkg]
diff --git a/releasenotes/notes/add-service-provider-client-cbba77d424a30dd3.yaml b/releasenotes/notes/add-service-provider-client-cbba77d424a30dd3.yaml
new file mode 100644
index 0000000..b504c78
--- /dev/null
+++ b/releasenotes/notes/add-service-provider-client-cbba77d424a30dd3.yaml
@@ -0,0 +1,4 @@
+---
+features:
+  - A Neutron Service Providers client is added to deal with resources
+    of the '/service-providers' route.
diff --git a/releasenotes/notes/remove-negative-test-generator-1653f4c0f86ccf75.yaml b/releasenotes/notes/remove-negative-test-generator-1653f4c0f86ccf75.yaml
new file mode 100644
index 0000000..a734d15
--- /dev/null
+++ b/releasenotes/notes/remove-negative-test-generator-1653f4c0f86ccf75.yaml
@@ -0,0 +1,4 @@
+---
+upgrade:
+  - The Negative Tests Generator has been removed (it was not used by any
+    Tempest tests).
diff --git a/tempest/api/compute/admin/test_hypervisor.py b/tempest/api/compute/admin/test_hypervisor.py
index 55134b1..1f043dc 100644
--- a/tempest/api/compute/admin/test_hypervisor.py
+++ b/tempest/api/compute/admin/test_hypervisor.py
@@ -87,7 +87,7 @@
         ironic_only = True
         hypers_without_ironic = []
         for hyper in hypers:
-            details = (self.client.show_hypervisor(hypers[0]['id'])
+            details = (self.client.show_hypervisor(hyper['id'])
                        ['hypervisor'])
             if details['hypervisor_type'] != 'ironic':
                 hypers_without_ironic.append(hyper)
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions.py b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
index fdf1e93..dcb2d2c 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
@@ -14,7 +14,6 @@
 #    under the License.
 
 from tempest.api.compute.floating_ips import base
-from tempest.common import waiters
 from tempest import config
 from tempest.lib.common.utils import test_utils
 from tempest.lib import exceptions as lib_exc
@@ -111,9 +110,7 @@
         # positive test:Association of an already associated floating IP
         # to specific server should change the association of the Floating IP
         # Create server so as to use for Multiple association
-        body = self.create_test_server()
-        waiters.wait_for_server_status(self.servers_client,
-                                       body['id'], 'ACTIVE')
+        body = self.create_test_server(wait_until='ACTIVE')
         self.new_server_id = body['id']
         self.addCleanup(self.servers_client.delete_server, self.new_server_id)
 
diff --git a/tempest/api/compute/images/test_images_negative.py b/tempest/api/compute/images/test_images_negative.py
index c48367f..549262e 100644
--- a/tempest/api/compute/images/test_images_negative.py
+++ b/tempest/api/compute/images/test_images_negative.py
@@ -46,7 +46,7 @@
         # An image should not be created if the server instance is removed
         server = self.create_test_server(wait_until='ACTIVE')
 
-        # Delete server before trying to create server
+        # Delete server before trying to create image
         self.servers_client.delete_server(server['id'])
         waiters.wait_for_server_termination(self.servers_client, server['id'])
         # Create a new image after server is deleted
diff --git a/tempest/api/compute/images/test_images_oneserver_negative.py b/tempest/api/compute/images/test_images_oneserver_negative.py
index 039283a..1c9b3f1 100644
--- a/tempest/api/compute/images/test_images_oneserver_negative.py
+++ b/tempest/api/compute/images/test_images_oneserver_negative.py
@@ -93,9 +93,9 @@
     @test.attr(type=['negative'])
     @test.idempotent_id('3d24d11f-5366-4536-bd28-cff32b748eca')
     def test_create_image_specify_metadata_over_limits(self):
-        # Return an error when creating image with meta data over 256 chars
+        # Return an error when creating image with meta data over 255 chars
         snapshot_name = data_utils.rand_name('test-snap')
-        meta = {'a' * 260: 'b' * 260}
+        meta = {'a' * 256: 'b' * 256}
         self.assertRaises(lib_exc.BadRequest, self.client.create_image,
                           self.server_id, name=snapshot_name, metadata=meta)
 
@@ -118,10 +118,10 @@
 
     @test.attr(type=['negative'])
     @test.idempotent_id('084f0cbc-500a-4963-8a4e-312905862581')
-    def test_create_image_specify_name_over_256_chars(self):
-        # Return an error if snapshot name over 256 characters is passed
+    def test_create_image_specify_name_over_character_limit(self):
+        # Return an error if snapshot name over 255 characters is passed
 
-        snapshot_name = data_utils.rand_name('a' * 260)
+        snapshot_name = ('a' * 256)
         self.assertRaises(lib_exc.BadRequest, self.client.create_image,
                           self.server_id, name=snapshot_name)
 
diff --git a/tempest/api/compute/security_groups/test_security_groups.py b/tempest/api/compute/security_groups/test_security_groups.py
index 755336f..4184afa 100644
--- a/tempest/api/compute/security_groups/test_security_groups.py
+++ b/tempest/api/compute/security_groups/test_security_groups.py
@@ -94,10 +94,8 @@
 
         # Create server and add the security group created
         # above to the server we just created
-        server = self.create_test_server()
+        server = self.create_test_server(wait_until='ACTIVE')
         server_id = server['id']
-        waiters.wait_for_server_status(self.servers_client, server_id,
-                                       'ACTIVE')
         self.servers_client.add_security_group(server_id, name=sg['name'])
 
         # Check that we are not able to delete the security
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index 8342a3e..a21ce94 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -46,7 +46,6 @@
     @classmethod
     def setup_clients(cls):
         super(AttachInterfacesTestJSON, cls).setup_clients()
-        cls.networks_client = cls.os.networks_client
         cls.subnets_client = cls.os.subnets_client
         cls.ports_client = cls.os.ports_client
 
diff --git a/tempest/api/compute/test_live_block_migration_negative.py b/tempest/api/compute/test_live_block_migration_negative.py
index bd2b185..f072b81 100644
--- a/tempest/api/compute/test_live_block_migration_negative.py
+++ b/tempest/api/compute/test_live_block_migration_negative.py
@@ -33,7 +33,6 @@
     @classmethod
     def setup_clients(cls):
         super(LiveBlockMigrationNegativeTestJSON, cls).setup_clients()
-        cls.admin_hosts_client = cls.os_adm.hosts_client
         cls.admin_servers_client = cls.os_adm.servers_client
 
     def _migrate_server_to(self, server_id, dest_host):
diff --git a/tempest/api/compute/test_quotas.py b/tempest/api/compute/test_quotas.py
index 122e7cc..b9e0c35 100644
--- a/tempest/api/compute/test_quotas.py
+++ b/tempest/api/compute/test_quotas.py
@@ -48,7 +48,8 @@
                                      'fixed_ips', 'key_pairs',
                                      'injected_file_path_bytes',
                                      'instances', 'security_group_rules',
-                                     'cores', 'security_groups'))
+                                     'cores', 'security_groups',
+                                     'server_group_members', 'server_groups'))
 
     @test.idempotent_id('f1ef0a97-dbbb-4cca-adc5-c9fbc4f76107')
     def test_get_quotas(self):
diff --git a/tempest/api/identity/admin/v3/test_default_project_id.py b/tempest/api/identity/admin/v3/test_default_project_id.py
index 59ffc19..361ff31 100644
--- a/tempest/api/identity/admin/v3/test_default_project_id.py
+++ b/tempest/api/identity/admin/v3/test_default_project_id.py
@@ -36,7 +36,8 @@
     def test_default_project_id(self):
         # create a domain
         dom_name = data_utils.rand_name('dom')
-        domain_body = self.domains_client.create_domain(dom_name)['domain']
+        domain_body = self.domains_client.create_domain(
+            name=dom_name)['domain']
         dom_id = domain_body['id']
         self.addCleanup(self._delete_domain, dom_id)
 
diff --git a/tempest/api/identity/admin/v3/test_domains.py b/tempest/api/identity/admin/v3/test_domains.py
index cbf1439..e71341f 100644
--- a/tempest/api/identity/admin/v3/test_domains.py
+++ b/tempest/api/identity/admin/v3/test_domains.py
@@ -32,7 +32,7 @@
         cls.setup_domains = list()
         for i in range(3):
             domain = cls.domains_client.create_domain(
-                data_utils.rand_name('domain'),
+                name=data_utils.rand_name('domain'),
                 description=data_utils.rand_name('domain-desc'),
                 enabled=i < 2)['domain']
             cls.setup_domains.append(domain)
@@ -67,7 +67,7 @@
         # List domains filtering by name
         params = {'name': self.setup_domains[0]['name']}
         fetched_domains = self.domains_client.list_domains(
-            params=params)['domains']
+            **params)['domains']
         # Verify the filtered list is correct, domain names are unique
         # so exactly one domain should be found with the provided name
         self.assertEqual(1, len(fetched_domains))
@@ -79,7 +79,7 @@
         # List domains filtering by enabled domains
         params = {'enabled': True}
         fetched_domains = self.domains_client.list_domains(
-            params=params)['domains']
+            **params)['domains']
         # Verify the filtered list is correct
         self.assertIn(self.setup_domains[0], fetched_domains)
         self.assertIn(self.setup_domains[1], fetched_domains)
@@ -93,7 +93,7 @@
         d_name = data_utils.rand_name('domain')
         d_desc = data_utils.rand_name('domain-desc')
         domain = self.domains_client.create_domain(
-            d_name, description=d_desc)['domain']
+            name=d_name, description=d_desc)['domain']
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self._delete_domain, domain['id'])
         self.assertIn('id', domain)
@@ -138,7 +138,7 @@
         d_name = data_utils.rand_name('domain')
         d_desc = data_utils.rand_name('domain-desc')
         domain = self.domains_client.create_domain(
-            d_name, description=d_desc, enabled=False)['domain']
+            name=d_name, description=d_desc, enabled=False)['domain']
         self.addCleanup(self.domains_client.delete_domain, domain['id'])
         self.assertEqual(d_name, domain['name'])
         self.assertFalse(domain['enabled'])
@@ -148,11 +148,15 @@
     def test_create_domain_without_description(self):
         # Create domain only with name
         d_name = data_utils.rand_name('domain')
-        domain = self.domains_client.create_domain(d_name)['domain']
+        domain = self.domains_client.create_domain(name=d_name)['domain']
         self.addCleanup(self._delete_domain, domain['id'])
         self.assertIn('id', domain)
         expected_data = {'name': d_name, 'enabled': True}
-        self.assertIsNone(domain['description'])
+        # TODO(gmann): there is bug in keystone liberty version where
+        # description is not being returned if it is not being passed in
+        # request. Bug#1649245. Once bug is fixed then we can enable the below
+        # check.
+        # self.assertEqual('', domain['description'])
         self.assertDictContainsSubset(expected_data, domain)
 
 
diff --git a/tempest/api/identity/admin/v3/test_domains_negative.py b/tempest/api/identity/admin/v3/test_domains_negative.py
index 4330cab..100a121 100644
--- a/tempest/api/identity/admin/v3/test_domains_negative.py
+++ b/tempest/api/identity/admin/v3/test_domains_negative.py
@@ -28,7 +28,7 @@
         d_name = data_utils.rand_name('domain')
         d_desc = data_utils.rand_name('domain-desc')
         domain = self.domains_client.create_domain(
-            d_name,
+            name=d_name,
             description=d_desc)['domain']
         domain_id = domain['id']
 
@@ -51,7 +51,8 @@
         # Domain name length should not ne greater than 64 characters
         d_name = 'a' * 65
         self.assertRaises(lib_exc.BadRequest,
-                          self.domains_client.create_domain, d_name)
+                          self.domains_client.create_domain,
+                          name=d_name)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('43781c07-764f-4cf2-a405-953c1916f605')
@@ -64,9 +65,10 @@
     @test.idempotent_id('e6f9e4a2-4f36-4be8-bdbc-4e199ae29427')
     def test_domain_create_duplicate(self):
         domain_name = data_utils.rand_name('domain-dup')
-        domain = self.domains_client.create_domain(domain_name)['domain']
+        domain = self.domains_client.create_domain(name=domain_name)['domain']
         domain_id = domain['id']
         self.addCleanup(self.delete_domain, domain_id)
         # Domain name should be unique
         self.assertRaises(
-            lib_exc.Conflict, self.domains_client.create_domain, domain_name)
+            lib_exc.Conflict, self.domains_client.create_domain,
+            name=domain_name)
diff --git a/tempest/api/identity/admin/v3/test_inherits.py b/tempest/api/identity/admin/v3/test_inherits.py
index c7e8411..33fce8d 100644
--- a/tempest/api/identity/admin/v3/test_inherits.py
+++ b/tempest/api/identity/admin/v3/test_inherits.py
@@ -31,7 +31,7 @@
         u_email = '%s@testmail.tm' % u_name
         u_password = data_utils.rand_name('pass-')
         cls.domain = cls.domains_client.create_domain(
-            data_utils.rand_name('domain-'),
+            name=data_utils.rand_name('domain-'),
             description=data_utils.rand_name('domain-desc-'))['domain']
         cls.project = cls.projects_client.create_project(
             data_utils.rand_name('project-'),
diff --git a/tempest/api/identity/admin/v3/test_roles.py b/tempest/api/identity/admin/v3/test_roles.py
index f5bf923..670cb2f 100644
--- a/tempest/api/identity/admin/v3/test_roles.py
+++ b/tempest/api/identity/admin/v3/test_roles.py
@@ -34,7 +34,7 @@
         u_email = '%s@testmail.tm' % u_name
         cls.u_password = data_utils.rand_password()
         cls.domain = cls.domains_client.create_domain(
-            data_utils.rand_name('domain'),
+            name=data_utils.rand_name('domain'),
             description=data_utils.rand_name('domain-desc'))['domain']
         cls.project = cls.projects_client.create_project(
             data_utils.rand_name('project'),
diff --git a/tempest/api/identity/v3/test_tokens.py b/tempest/api/identity/v3/test_tokens.py
index d5bed96..b410da6 100644
--- a/tempest/api/identity/v3/test_tokens.py
+++ b/tempest/api/identity/v3/test_tokens.py
@@ -34,6 +34,7 @@
         # it to be 'default'
         token_id, resp = self.non_admin_token.get_token(
             user_id=user_id,
+            username=username,
             user_domain_id=user_domain_id,
             password=password,
             auth_data=True)
@@ -49,9 +50,19 @@
         self.assertGreater(expires_at, now)
 
         subject_id = resp['user']['id']
-        self.assertEqual(subject_id, user_id)
+        if user_id:
+            self.assertEqual(subject_id, user_id)
+        else:
+            # Expect a user ID, but don't know what it will be.
+            self.assertGreaterEqual(len(subject_id), 0,
+                                    'Expected user ID in token.')
 
         subject_name = resp['user']['name']
-        self.assertEqual(subject_name, username)
+        if username:
+            self.assertEqual(subject_name, username)
+        else:
+            # Expect a user name, but don't know what it will be.
+            self.assertGreaterEqual(len(subject_name), 0,
+                                    'Expected user name in token.')
 
         self.assertEqual(resp['methods'][0], 'password')
diff --git a/tempest/api/image/v2/test_images_metadefs_schema.py b/tempest/api/image/v2/test_images_metadefs_schema.py
new file mode 100644
index 0000000..7edf1af
--- /dev/null
+++ b/tempest/api/image/v2/test_images_metadefs_schema.py
@@ -0,0 +1,81 @@
+# Copyright 2016 EasyStack.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.api.image import base
+from tempest import test
+
+
+class MetadataSchemaTest(base.BaseV2ImageTest):
+    """Test to get metadata schema"""
+
+    @test.idempotent_id('e9e44891-3cb8-3b40-a532-e0a39fea3dab')
+    def test_get_metadata_namespace_schema(self):
+        # Test to get namespace schema
+        body = self.schemas_client.show_schema("metadefs/namespace")
+        self.assertEqual("namespace", body['name'])
+
+    @test.idempotent_id('ffe44891-678b-3ba0-a3e2-e0a3967b3aeb')
+    def test_get_metadata_namespaces_schema(self):
+        # Test to get namespaces schema
+        body = self.schemas_client.show_schema("metadefs/namespaces")
+        self.assertEqual("namespaces", body['name'])
+
+    @test.idempotent_id('fde34891-678b-3b40-ae32-e0a3e67b6beb')
+    def test_get_metadata_resource_type_schema(self):
+        # Test to get resource_type schema
+        body = self.schemas_client.show_schema("metadefs/resource_type")
+        self.assertEqual("resource_type_association", body['name'])
+
+    @test.idempotent_id('dfe4a891-b38b-3bf0-a3b2-e03ee67b3a3a')
+    def test_get_metadata_resources_types_schema(self):
+        # Test to get resource_types schema
+        body = self.schemas_client.show_schema("metadefs/resource_types")
+        self.assertEqual("resource_type_associations", body['name'])
+
+    @test.idempotent_id('dff4a891-b38b-3bf0-a3b2-e03ee67b3a3b')
+    def test_get_metadata_object_schema(self):
+        # Test to get object schema
+        body = self.schemas_client.show_schema("metadefs/object")
+        self.assertEqual("object", body['name'])
+
+    @test.idempotent_id('dee4a891-b38b-3bf0-a3b2-e03ee67b3a3c')
+    def test_get_metadata_objects_schema(self):
+        # Test to get objects schema
+        body = self.schemas_client.show_schema("metadefs/objects")
+        self.assertEqual("objects", body['name'])
+
+    @test.idempotent_id('dae4a891-b38b-3bf0-a3b2-e03ee67b3a3d')
+    def test_get_metadata_property_schema(self):
+        # Test to get property schema
+        body = self.schemas_client.show_schema("metadefs/property")
+        self.assertEqual("property", body['name'])
+
+    @test.idempotent_id('dce4a891-b38b-3bf0-a3b2-e03ee67b3a3e')
+    def test_get_metadata_properties_schema(self):
+        # Test to get properties schema
+        body = self.schemas_client.show_schema("metadefs/properties")
+        self.assertEqual("properties", body['name'])
+
+    @test.idempotent_id('dde4a891-b38b-3bf0-a3b2-e03ee67b3a3e')
+    def test_get_metadata_tag_schema(self):
+        # Test to get tag schema
+        body = self.schemas_client.show_schema("metadefs/tag")
+        self.assertEqual("tag", body['name'])
+
+    @test.idempotent_id('cde4a891-b38b-3bf0-a3b2-e03ee67b3a3a')
+    def test_get_metadata_tags_schema(self):
+        # Test to get tags schema
+        body = self.schemas_client.show_schema("metadefs/tags")
+        self.assertEqual("tags", body['name'])
diff --git a/tempest/api/network/admin/test_negative_quotas.py b/tempest/api/network/admin/test_negative_quotas.py
index 7b037d5..c256b5b 100644
--- a/tempest/api/network/admin/test_negative_quotas.py
+++ b/tempest/api/network/admin/test_negative_quotas.py
@@ -38,11 +38,6 @@
             msg = "quotas extension not enabled."
             raise cls.skipException(msg)
 
-    @classmethod
-    def setup_clients(cls):
-        super(QuotasNegativeTest, cls).setup_clients()
-        cls.identity_admin_client = cls.os_adm.identity_client
-
     @test.idempotent_id('644f4e1b-1bf9-4af0-9fd8-eb56ac0f51cf')
     def test_network_quota_exceeding(self):
         # Set the network quota to two
diff --git a/tempest/api/network/admin/test_quotas.py b/tempest/api/network/admin/test_quotas.py
index 3a264ff..978fb8f 100644
--- a/tempest/api/network/admin/test_quotas.py
+++ b/tempest/api/network/admin/test_quotas.py
@@ -43,11 +43,6 @@
             msg = "quotas extension not enabled."
             raise cls.skipException(msg)
 
-    @classmethod
-    def setup_clients(cls):
-        super(QuotasTest, cls).setup_clients()
-        cls.identity_admin_client = cls.os_adm.identity_client
-
     def _check_quotas(self, new_quotas):
         # Add a project to conduct the test
         project = data_utils.rand_name('test_project_')
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index deefbeb..c2c42bb 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -81,6 +81,7 @@
         cls.security_group_rules_client = (
             cls.os.security_group_rules_client)
         cls.network_versions_client = cls.os.network_versions_client
+        cls.service_providers_client = cls.os.service_providers_client
 
     @classmethod
     def resource_setup(cls):
diff --git a/tempest/api/network/test_ports.py b/tempest/api/network/test_ports.py
index 15d289d..5b46088 100644
--- a/tempest/api/network/test_ports.py
+++ b/tempest/api/network/test_ports.py
@@ -360,11 +360,6 @@
 class PortsAdminExtendedAttrsTestJSON(base.BaseAdminNetworkTest):
 
     @classmethod
-    def setup_clients(cls):
-        super(PortsAdminExtendedAttrsTestJSON, cls).setup_clients()
-        cls.identity_client = cls.os_adm.identity_client
-
-    @classmethod
     def resource_setup(cls):
         super(PortsAdminExtendedAttrsTestJSON, cls).resource_setup()
         cls.network = cls.create_network()
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index e989b69..f2170ad 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -34,11 +34,6 @@
             raise cls.skipException(msg)
 
     @classmethod
-    def setup_clients(cls):
-        super(RoutersTest, cls).setup_clients()
-        cls.identity_admin_client = cls.os_adm.identity_client
-
-    @classmethod
     def resource_setup(cls):
         super(RoutersTest, cls).resource_setup()
         cls.tenant_cidr = (CONF.network.project_network_cidr
diff --git a/tempest/api/network/test_service_providers.py b/tempest/api/network/test_service_providers.py
new file mode 100644
index 0000000..09fb5fe
--- /dev/null
+++ b/tempest/api/network/test_service_providers.py
@@ -0,0 +1,23 @@
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.api.network import base
+from tempest import test
+
+
+class ServiceProvidersTest(base.BaseNetworkTest):
+
+    @test.idempotent_id('2cbbeea9-f010-40f6-8df5-4eaa0c918ea6')
+    def test_service_providers_list(self):
+        body = self.service_providers_client.list_service_providers()
+        self.assertIn('service_providers', body)
+        self.assertIsInstance(body['service_providers'], list)
diff --git a/tempest/api/network/test_service_type_management.py b/tempest/api/network/test_service_type_management.py
deleted file mode 100644
index f49f082..0000000
--- a/tempest/api/network/test_service_type_management.py
+++ /dev/null
@@ -1,31 +0,0 @@
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.api.network import base
-from tempest.lib import decorators
-from tempest import test
-
-
-class ServiceTypeManagementTestJSON(base.BaseNetworkTest):
-
-    @classmethod
-    def skip_checks(cls):
-        super(ServiceTypeManagementTestJSON, cls).skip_checks()
-        if not test.is_extension_enabled('service-type', 'network'):
-            msg = "Neutron Service Type Management not enabled."
-            raise cls.skipException(msg)
-
-    @decorators.skip_because(bug="1400370")
-    @test.idempotent_id('2cbbeea9-f010-40f6-8df5-4eaa0c918ea6')
-    def test_service_provider_list(self):
-        body = self.client.list_service_providers()
-        self.assertIsInstance(body['service_providers'], list)
diff --git a/tempest/api/object_storage/test_container_services_negative.py b/tempest/api/object_storage/test_container_services_negative.py
index e6c53ec..df91325 100644
--- a/tempest/api/object_storage/test_container_services_negative.py
+++ b/tempest/api/object_storage/test_container_services_negative.py
@@ -13,11 +13,16 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.api.object_storage import base
+from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib import exceptions
 from tempest import test
 
+CONF = config.CONF
+
 
 class ContainerNegativeTest(base.BaseObjectTest):
 
@@ -25,12 +30,16 @@
     def resource_setup(cls):
         super(ContainerNegativeTest, cls).resource_setup()
 
-        # use /info to get default constraints
-        _, body = cls.account_client.list_extensions()
-        cls.constraints = body['swift']
+        if CONF.object_storage_feature_enabled.discoverability:
+            # use /info to get default constraints
+            _, body = cls.account_client.list_extensions()
+            cls.constraints = body['swift']
 
     @test.attr(type=["negative"])
     @test.idempotent_id('30686921-4bed-4764-a038-40d741ed4e78')
+    @testtools.skipUnless(
+        CONF.object_storage_feature_enabled.discoverability,
+        'Discoverability function is disabled')
     def test_create_container_name_exceeds_max_length(self):
         # Attempts to create a container name that is longer than max
         max_length = self.constraints['max_container_name_length']
@@ -44,6 +53,9 @@
 
     @test.attr(type=["negative"])
     @test.idempotent_id('41e645bf-2e68-4f84-bf7b-c71aa5cd76ce')
+    @testtools.skipUnless(
+        CONF.object_storage_feature_enabled.discoverability,
+        'Discoverability function is disabled')
     def test_create_container_metadata_name_exceeds_max_length(self):
         # Attempts to create container with metadata name
         # that is longer than max.
@@ -58,6 +70,9 @@
 
     @test.attr(type=["negative"])
     @test.idempotent_id('81e36922-326b-4b7c-8155-3bbceecd7a82')
+    @testtools.skipUnless(
+        CONF.object_storage_feature_enabled.discoverability,
+        'Discoverability function is disabled')
     def test_create_container_metadata_value_exceeds_max_length(self):
         # Attempts to create container with metadata value
         # that is longer than max.
@@ -72,6 +87,9 @@
 
     @test.attr(type=["negative"])
     @test.idempotent_id('ac666539-d566-4f02-8ceb-58e968dfb732')
+    @testtools.skipUnless(
+        CONF.object_storage_feature_enabled.discoverability,
+        'Discoverability function is disabled')
     def test_create_container_metadata_exceeds_overall_metadata_count(self):
         # Attempts to create container with metadata that exceeds the
         # default count
diff --git a/tempest/api/orchestration/stacks/test_neutron_resources.py b/tempest/api/orchestration/stacks/test_neutron_resources.py
index bffcb64..6d27502 100644
--- a/tempest/api/orchestration/stacks/test_neutron_resources.py
+++ b/tempest/api/orchestration/stacks/test_neutron_resources.py
@@ -77,7 +77,7 @@
             cls.client.wait_for_stack_status(cls.stack_id, 'CREATE_COMPLETE')
             resources = (cls.client.list_resources(cls.stack_identifier)
                          ['resources'])
-        except exceptions.TimeoutException as e:
+        except exceptions.TimeoutException:
             if CONF.compute_feature_enabled.console_output:
                 # attempt to log the server console to help with debugging
                 # the cause of the server not signalling the waitcondition
@@ -89,7 +89,7 @@
                 output = cls.servers_client.get_console_output(
                     server_id)['output']
                 LOG.debug(output)
-            raise e
+            raise
 
         cls.test_resources = {}
         for resource in resources:
diff --git a/tempest/clients.py b/tempest/clients.py
index b3290ac..da42d32 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -127,6 +127,7 @@
             self.network.SecurityGroupRulesClient())
         self.security_groups_client = self.network.SecurityGroupsClient()
         self.network_versions_client = self.network.NetworkVersionsClient()
+        self.service_providers_client = self.network.ServiceProvidersClient()
 
     def _set_image_clients(self):
         if CONF.service_available.glance:
diff --git a/tempest/common/generator/__init__.py b/tempest/common/generator/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/common/generator/__init__.py
+++ /dev/null
diff --git a/tempest/common/generator/base_generator.py b/tempest/common/generator/base_generator.py
deleted file mode 100644
index 0647edb..0000000
--- a/tempest/common/generator/base_generator.py
+++ /dev/null
@@ -1,176 +0,0 @@
-# Copyright 2014 Deutsche Telekom AG
-# 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
-import functools
-
-import jsonschema
-import six
-
-
-def _check_for_expected_result(name, schema):
-    expected_result = None
-    if "results" in schema:
-        if name in schema["results"]:
-            expected_result = schema["results"][name]
-    return expected_result
-
-
-def generator_type(*args, **kwargs):
-    def wrapper(func):
-        func.types = args
-        for key in kwargs:
-            setattr(func, key, kwargs[key])
-        return func
-    return wrapper
-
-
-def simple_generator(fn):
-    """Decorator for simple generators that return one value"""
-    @functools.wraps(fn)
-    def wrapped(self, schema):
-        result = fn(self, schema)
-        if result is not None:
-            expected_result = _check_for_expected_result(fn.__name__, schema)
-            return (fn.__name__, result, expected_result)
-        return
-    return wrapped
-
-
-class BasicGeneratorSet(object):
-    _instance = None
-
-    schema = {
-        "type": "object",
-        "properties": {
-            "name": {"type": "string"},
-            "http-method": {
-                "enum": ["GET", "PUT", "HEAD",
-                         "POST", "PATCH", "DELETE", 'COPY']
-            },
-            "admin_client": {"type": "boolean"},
-            "url": {"type": "string"},
-            "default_result_code": {"type": "integer"},
-            "json-schema": {},
-            "resources": {
-                "type": "array",
-                "items": {
-                    "oneOf": [
-                        {"type": "string"},
-                        {
-                            "type": "object",
-                            "properties": {
-                                "name": {"type": "string"},
-                                "expected_result": {"type": "integer"}
-                            }
-                        }
-                    ]
-                }
-            },
-            "results": {
-                "type": "object",
-                "properties": {}
-            }
-        },
-        "required": ["name", "http-method", "url"],
-        "additionalProperties": False,
-    }
-
-    def __init__(self):
-        self.types_dict = {}
-        for m in dir(self):
-            if callable(getattr(self, m)) and not'__' in m:
-                method = getattr(self, m)
-                if hasattr(method, "types"):
-                    for type in method.types:
-                        if type not in self.types_dict:
-                            self.types_dict[type] = []
-                        self.types_dict[type].append(method)
-
-    def validate_schema(self, schema):
-        if "json-schema" in schema:
-            jsonschema.Draft4Validator.check_schema(schema['json-schema'])
-        jsonschema.validate(schema, self.schema)
-
-    def generate_scenarios(self, schema, path=None):
-        """Generate scenario (all possible test cases) out of the given schema
-
-        :param schema: a dict style schema (see ``BasicGeneratorSet.schema``)
-        :param path: the schema path if the given schema is a subschema
-        """
-        schema_type = schema['type']
-        scenarios = []
-
-        if schema_type == 'object':
-            properties = schema["properties"]
-            for attribute, definition in six.iteritems(properties):
-                current_path = copy.copy(path)
-                if path is not None:
-                    current_path.append(attribute)
-                else:
-                    current_path = [attribute]
-                scenarios.extend(
-                    self.generate_scenarios(definition, current_path))
-        elif isinstance(schema_type, list):
-            if "integer" in schema_type:
-                schema_type = "integer"
-            else:
-                raise Exception("non-integer list types not supported")
-        for generator in self.types_dict[schema_type]:
-            if hasattr(generator, "needed_property"):
-                prop = generator.needed_property
-                if (prop not in schema or
-                    schema[prop] is None or
-                    schema[prop] is False):
-                    continue
-
-            name = generator.__name__
-            if ("exclude_tests" in schema and
-               name in schema["exclude_tests"]):
-                continue
-            if path is not None:
-                name = "%s_%s" % ("_".join(path), name)
-            scenarios.append({
-                "_negtest_name": name,
-                "_negtest_generator": generator,
-                "_negtest_schema": schema,
-                "_negtest_path": path})
-        return scenarios
-
-    def generate_payload(self, test, schema):
-        """Generates one jsonschema out of the given test.
-
-        It's mandatory to use generate_scenarios before to register all needed
-        variables to the test.
-
-        :param test: A test object (scenario) with all _negtest variables on it
-        :param schema: schema for the test
-        """
-        generator = test._negtest_generator
-        ret = generator(test._negtest_schema)
-        path = copy.copy(test._negtest_path)
-        expected_result = None
-
-        if ret is not None:
-            generator_result = generator(test._negtest_schema)
-            invalid_snippet = generator_result[1]
-            expected_result = generator_result[2]
-            element = path.pop()
-            if len(path) > 0:
-                schema_snip = six.moves.reduce(dict.get, path, schema)
-                schema_snip[element] = invalid_snippet
-            else:
-                schema[element] = invalid_snippet
-        return expected_result
diff --git a/tempest/common/generator/negative_generator.py b/tempest/common/generator/negative_generator.py
deleted file mode 100644
index 67ace54..0000000
--- a/tempest/common/generator/negative_generator.py
+++ /dev/null
@@ -1,75 +0,0 @@
-# Copyright 2014 Deutsche Telekom AG
-# 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
-
-import tempest.common.generator.base_generator as base
-import tempest.common.generator.valid_generator as valid
-
-
-class NegativeTestGenerator(base.BasicGeneratorSet):
-    @base.generator_type("string")
-    @base.simple_generator
-    def gen_int(self, _):
-        return 4
-
-    @base.generator_type("integer")
-    @base.simple_generator
-    def gen_string(self, _):
-        return "XXXXXX"
-
-    @base.generator_type("integer", "string")
-    def gen_none(self, schema):
-        # Note(mkoderer): it's not using the decorator otherwise it'd be
-        # filtered
-        expected_result = base._check_for_expected_result('gen_none', schema)
-        return ('gen_none', None, expected_result)
-
-    @base.generator_type("string")
-    @base.simple_generator
-    def gen_str_min_length(self, schema):
-        min_length = schema.get("minLength", 0)
-        if min_length > 0:
-            return "x" * (min_length - 1)
-
-    @base.generator_type("string", needed_property="maxLength")
-    @base.simple_generator
-    def gen_str_max_length(self, schema):
-        max_length = schema.get("maxLength", -1)
-        return "x" * (max_length + 1)
-
-    @base.generator_type("integer", needed_property="minimum")
-    @base.simple_generator
-    def gen_int_min(self, schema):
-        minimum = schema["minimum"]
-        if "exclusiveMinimum" not in schema:
-            minimum -= 1
-        return minimum
-
-    @base.generator_type("integer", needed_property="maximum")
-    @base.simple_generator
-    def gen_int_max(self, schema):
-        maximum = schema["maximum"]
-        if "exclusiveMaximum" not in schema:
-            maximum += 1
-        return maximum
-
-    @base.generator_type("object", needed_property="additionalProperties")
-    @base.simple_generator
-    def gen_obj_add_attr(self, schema):
-        valid_schema = valid.ValidTestGenerator().generate_valid(schema)
-        new_valid = copy.deepcopy(valid_schema)
-        new_valid["$$$$$$$$$$"] = "xxx"
-        return new_valid
diff --git a/tempest/common/generator/valid_generator.py b/tempest/common/generator/valid_generator.py
deleted file mode 100644
index 3070489..0000000
--- a/tempest/common/generator/valid_generator.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# Copyright 2014 Deutsche Telekom AG
-# 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 six
-
-import tempest.common.generator.base_generator as base
-
-
-class ValidTestGenerator(base.BasicGeneratorSet):
-    @base.generator_type("string")
-    @base.simple_generator
-    def generate_valid_string(self, schema):
-        size = schema.get("minLength", 1)
-        # TODO(dkr mko): handle format and pattern
-        return "x" * size
-
-    @base.generator_type("integer")
-    @base.simple_generator
-    def generate_valid_integer(self, schema):
-        # TODO(dkr mko): handle multipleOf
-        if "minimum" in schema:
-            minimum = schema["minimum"]
-            if "exclusiveMinimum" not in schema:
-                return minimum
-            else:
-                return minimum + 1
-        if "maximum" in schema:
-            maximum = schema["maximum"]
-            if "exclusiveMaximum" not in schema:
-                return maximum
-            else:
-                return maximum - 1
-        return 0
-
-    @base.generator_type("object")
-    @base.simple_generator
-    def generate_valid_object(self, schema):
-        obj = {}
-        for k, v in six.iteritems(schema["properties"]):
-            obj[k] = self.generate_valid(v)
-        return obj
-
-    def generate(self, schema):
-        schema_type = schema["type"]
-        if isinstance(schema_type, list):
-            if "integer" in schema_type:
-                schema_type = "integer"
-            else:
-                raise Exception("non-integer list types not supported")
-        result = []
-        if schema_type not in self.types_dict:
-            raise TypeError("generator (%s) doesn't support type: %s"
-                            % (self.__class__.__name__, schema_type))
-        for generator in self.types_dict[schema_type]:
-            ret = generator(schema)
-            if ret is not None:
-                if isinstance(ret, list):
-                    result.extend(ret)
-                elif isinstance(ret, tuple):
-                    result.append(ret)
-                else:
-                    raise Exception("generator (%s) returns invalid result: %s"
-                                    % (generator, ret))
-        return result
-
-    def generate_valid(self, schema):
-        return self.generate(schema)[0][1]
diff --git a/tempest/config.py b/tempest/config.py
index 7550287..47eb427 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -1075,14 +1075,6 @@
                     "step in Node cleaning.")
 ]
 
-negative_group = cfg.OptGroup(name='negative', title="Negative Test Options")
-
-NegativeGroup = [
-    cfg.StrOpt('test_generator',
-               default='tempest.common.' +
-               'generator.negative_generator.NegativeTestGenerator',
-               help="Test generator class for all negative tests"),
-]
 
 DefaultGroup = [
     cfg.StrOpt('resources_prefix',
@@ -1115,7 +1107,6 @@
     (debug_group, DebugGroup),
     (baremetal_group, BaremetalGroup),
     (input_scenario_group, InputScenarioGroup),
-    (negative_group, NegativeGroup),
     (None, DefaultGroup)
 ]
 
@@ -1179,7 +1170,6 @@
         self.debug = _CONF.debug
         self.baremetal = _CONF.baremetal
         self.input_scenario = _CONF['input-scenario']
-        self.negative = _CONF.negative
         logging.tempest_set_log_file('tempest.log')
 
     def __init__(self, parse_conf=True, config_path=None):
diff --git a/tempest/lib/cmd/check_uuid.py b/tempest/lib/cmd/check_uuid.py
index 1239ac5..88ce775 100755
--- a/tempest/lib/cmd/check_uuid.py
+++ b/tempest/lib/cmd/check_uuid.py
@@ -23,6 +23,7 @@
 import unittest
 import uuid
 
+from oslo_utils import uuidutils
 import six.moves.urllib.parse as urlparse
 
 DECORATOR_MODULE = 'test'
@@ -61,7 +62,7 @@
         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())
+        patch_id = uuidutils.generate_uuid()
         if not patch.endswith('\n'):
             patch += '\n'
         self.patches[patch_id] = self._quote(patch)
diff --git a/tempest/lib/common/cred_client.py b/tempest/lib/common/cred_client.py
index ad968f1..3f10dee 100644
--- a/tempest/lib/common/cred_client.py
+++ b/tempest/lib/common/cred_client.py
@@ -140,7 +140,7 @@
             # Domain names must be unique, in any case a list is returned,
             # selecting the first (and only) element
             self.creds_domain = self.domains_client.list_domains(
-                params={'name': domain_name})['domains'][0]
+                name=domain_name)['domains'][0]
         except lib_exc.NotFound:
             # TODO(andrea) we could probably create the domain on the fly
             msg = "Requested domain %s could not be found" % domain_name
diff --git a/tempest/lib/common/utils/data_utils.py b/tempest/lib/common/utils/data_utils.py
index 4095c77..75c2e51 100644
--- a/tempest/lib/common/utils/data_utils.py
+++ b/tempest/lib/common/utils/data_utils.py
@@ -21,6 +21,7 @@
 
 from debtcollector import removals
 from oslo_utils import netutils
+from oslo_utils import uuidutils
 import six.moves
 
 
@@ -30,7 +31,7 @@
     :return: a random UUID (e.g. '1dc12c7d-60eb-4b61-a7a2-17cf210155b6')
     :rtype: string
     """
-    return str(uuid.uuid4())
+    return uuidutils.generate_uuid()
 
 
 def rand_uuid_hex():
diff --git a/tempest/lib/exceptions.py b/tempest/lib/exceptions.py
index a6c01bb..108ba70 100644
--- a/tempest/lib/exceptions.py
+++ b/tempest/lib/exceptions.py
@@ -71,54 +71,70 @@
     message = "The success code is different than the expected one"
 
 
-class NotFound(ClientRestClientException):
-    message = "Object not found"
+class BadRequest(ClientRestClientException):
+    status_code = 400
+    message = "Bad request"
 
 
 class Unauthorized(ClientRestClientException):
+    status_code = 401
     message = 'Unauthorized'
 
 
 class Forbidden(ClientRestClientException):
+    status_code = 403
     message = "Forbidden"
 
 
-class TimeoutException(OtherRestClientException):
-    message = "Request timed out"
-
-
-class BadRequest(ClientRestClientException):
-    message = "Bad request"
-
-
-class UnprocessableEntity(ClientRestClientException):
-    message = "Unprocessable entity"
-
-
-class RateLimitExceeded(ClientRestClientException):
-    message = "Rate limit exceeded"
-
-
-class OverLimit(ClientRestClientException):
-    message = "Request entity is too large"
-
-
-class ServerFault(ServerRestClientException):
-    message = "Got server fault"
-
-
-class NotImplemented(ServerRestClientException):
-    message = "Got NotImplemented error"
+class NotFound(ClientRestClientException):
+    status_code = 404
+    message = "Object not found"
 
 
 class Conflict(ClientRestClientException):
+    status_code = 409
     message = "An object with that identifier already exists"
 
 
 class Gone(ClientRestClientException):
+    status_code = 410
     message = "The requested resource is no longer available"
 
 
+class RateLimitExceeded(ClientRestClientException):
+    status_code = 413
+    message = "Rate limit exceeded"
+
+
+class OverLimit(ClientRestClientException):
+    status_code = 413
+    message = "Request entity is too large"
+
+
+class InvalidContentType(ClientRestClientException):
+    status_code = 415
+    message = "Invalid content type provided"
+
+
+class UnprocessableEntity(ClientRestClientException):
+    status_code = 422
+    message = "Unprocessable entity"
+
+
+class ServerFault(ServerRestClientException):
+    status_code = 500
+    message = "Got server fault"
+
+
+class NotImplemented(ServerRestClientException):
+    status_code = 501
+    message = "Got NotImplemented error"
+
+
+class TimeoutException(OtherRestClientException):
+    message = "Request timed out"
+
+
 class ResponseWithNonEmptyBody(OtherRestClientException):
     message = ("RFC Violation! Response with %(status)d HTTP Status Code "
                "MUST NOT have a body")
@@ -137,10 +153,6 @@
     message = "HTTP response header is invalid"
 
 
-class InvalidContentType(ClientRestClientException):
-    message = "Invalid content type provided"
-
-
 class UnexpectedContentType(OtherRestClientException):
     message = "Unexpected content type provided"
 
diff --git a/tempest/lib/services/network/__init__.py b/tempest/lib/services/network/__init__.py
index c466f07..19e5463 100644
--- a/tempest/lib/services/network/__init__.py
+++ b/tempest/lib/services/network/__init__.py
@@ -27,6 +27,8 @@
     SecurityGroupRulesClient
 from tempest.lib.services.network.security_groups_client import \
     SecurityGroupsClient
+from tempest.lib.services.network.service_providers_client import \
+    ServiceProvidersClient
 from tempest.lib.services.network.subnetpools_client import SubnetpoolsClient
 from tempest.lib.services.network.subnets_client import SubnetsClient
 from tempest.lib.services.network.versions_client import NetworkVersionsClient
@@ -35,4 +37,5 @@
            'MeteringLabelRulesClient', 'MeteringLabelsClient',
            'NetworksClient', 'PortsClient', 'QuotasClient', 'RoutersClient',
            'SecurityGroupRulesClient', 'SecurityGroupsClient',
-           'SubnetpoolsClient', 'SubnetsClient', 'NetworkVersionsClient']
+           'ServiceProvidersClient', 'SubnetpoolsClient', 'SubnetsClient',
+           'NetworkVersionsClient']
diff --git a/tempest/lib/services/network/service_providers_client.py b/tempest/lib/services/network/service_providers_client.py
new file mode 100644
index 0000000..0ee9bc3
--- /dev/null
+++ b/tempest/lib/services/network/service_providers_client.py
@@ -0,0 +1,21 @@
+#    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 base
+
+
+class ServiceProvidersClient(base.BaseNetworkClient):
+
+    def list_service_providers(self, **filters):
+        """Lists service providers."""
+        uri = '/service-providers'
+        return self.list_resources(uri, **filters)
diff --git a/tempest/scenario/test_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index 32f5d9f..1360b09 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -15,7 +15,6 @@
 from oslo_log import log
 import testtools
 
-from tempest import clients
 from tempest.common.utils import data_utils
 from tempest.common.utils import net_info
 from tempest import config
@@ -113,9 +112,9 @@
             access point
         """
 
-        def __init__(self, credentials):
-            self.manager = clients.Manager(credentials)
+        def __init__(self, clients):
             # Credentials from manager are filled with both names and IDs
+            self.manager = clients
             self.creds = self.manager.credentials
             self.network = None
             self.subnet = None
@@ -152,11 +151,6 @@
         # Create no network resources for these tests.
         cls.set_network_resources()
         super(TestSecurityGroupsBasicOps, cls).setup_credentials()
-        # TODO(mnewby) Consider looking up entities as needed instead
-        # of storing them as collections on the class.
-
-        # Credentials from the manager are filled with both IDs and Names
-        cls.alt_creds = cls.alt_manager.credentials
 
     @classmethod
     def resource_setup(cls):
@@ -171,9 +165,8 @@
 
         cls.floating_ips = {}
         cls.tenants = {}
-        creds = cls.manager.credentials
-        cls.primary_tenant = cls.TenantProperties(creds)
-        cls.alt_tenant = cls.TenantProperties(cls.alt_creds)
+        cls.primary_tenant = cls.TenantProperties(cls.os)
+        cls.alt_tenant = cls.TenantProperties(cls.os_alt)
         for tenant in [cls.primary_tenant, cls.alt_tenant]:
             cls.tenants[tenant.creds.tenant_id] = tenant
 
diff --git a/tempest/services/identity/v3/json/domains_client.py b/tempest/services/identity/v3/json/domains_client.py
index fe929a5..43cb62c 100644
--- a/tempest/services/identity/v3/json/domains_client.py
+++ b/tempest/services/identity/v3/json/domains_client.py
@@ -21,29 +21,36 @@
 class DomainsClient(rest_client.RestClient):
     api_version = "v3"
 
-    def create_domain(self, name, **kwargs):
-        """Creates a domain."""
-        description = kwargs.get('description', None)
-        en = kwargs.get('enabled', True)
-        post_body = {
-            'description': description,
-            'enabled': en,
-            'name': name
-        }
-        post_body = json.dumps({'domain': post_body})
+    def create_domain(self, **kwargs):
+        """Creates a domain.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#create-domain
+        """
+        post_body = json.dumps({'domain': kwargs})
         resp, body = self.post('domains', post_body)
         self.expected_success(201, resp.status)
         body = json.loads(body)
         return rest_client.ResponseBody(resp, body)
 
     def delete_domain(self, domain_id):
-        """Deletes a domain."""
+        """Deletes a domain.
+
+        For APi details, please refer to the official API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#delete-domain
+        """
         resp, body = self.delete('domains/%s' % domain_id)
         self.expected_success(204, resp.status)
         return rest_client.ResponseBody(resp, body)
 
-    def list_domains(self, params=None):
-        """List Domains."""
+    def list_domains(self, **params):
+        """List Domains.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#list-domains
+        """
         url = 'domains'
         if params:
             url += '?%s' % urllib.urlencode(params)
@@ -53,24 +60,24 @@
         return rest_client.ResponseBody(resp, body)
 
     def update_domain(self, domain_id, **kwargs):
-        """Updates a domain."""
-        body = self.show_domain(domain_id)['domain']
-        description = kwargs.get('description', body['description'])
-        en = kwargs.get('enabled', body['enabled'])
-        name = kwargs.get('name', body['name'])
-        post_body = {
-            'description': description,
-            'enabled': en,
-            'name': name
-        }
-        post_body = json.dumps({'domain': post_body})
+        """Updates a domain.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#update-domain
+        """
+        post_body = json.dumps({'domain': kwargs})
         resp, body = self.patch('domains/%s' % domain_id, post_body)
         self.expected_success(200, resp.status)
         body = json.loads(body)
         return rest_client.ResponseBody(resp, body)
 
     def show_domain(self, domain_id):
-        """Get Domain details."""
+        """Get Domain details.
+
+        For API details, please refer to the official API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#show-domain-details
+        """
         resp, body = self.get('domains/%s' % domain_id)
         self.expected_success(200, resp.status)
         body = json.loads(body)
diff --git a/tempest/test.py b/tempest/test.py
index 93fbed3..451b876 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -504,8 +504,12 @@
             else:
                 raise lib_exc.InvalidCredentials(
                     "Invalid credentials type %s" % credential_type)
-        return cls.client_manager(credentials=creds.credentials,
-                                  service=cls._service)
+        manager = cls.client_manager(credentials=creds.credentials,
+                                     service=cls._service)
+        # NOTE(andreaf) Ensure credentials have user and project id fields.
+        # It may not be the case when using pre-provisioned credentials.
+        manager.auth_provider.set_auth()
+        return manager
 
     @classmethod
     def clear_credentials(cls):
diff --git a/tempest/tests/lib/services/network/test_service_providers_client.py b/tempest/tests/lib/services/network/test_service_providers_client.py
new file mode 100644
index 0000000..ae11ef0
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_service_providers_client.py
@@ -0,0 +1,37 @@
+#    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 service_providers_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestServiceProvidersClient(base.BaseServiceTest):
+    def setUp(self):
+        super(TestServiceProvidersClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = service_providers_client.ServiceProvidersClient(
+            fake_auth, 'network', 'regionOne')
+
+    def _test_list_service_providers(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_service_providers,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {"service_providers": []},
+            bytes_body)
+
+    def test_list_service_providers_with_str_body(self):
+        self._test_list_service_providers()
+
+    def test_list_service_providers_with_bytes_body(self):
+        self._test_list_service_providers(bytes_body=True)
diff --git a/tempest/tests/negative/test_negative_generators.py b/tempest/tests/negative/test_negative_generators.py
deleted file mode 100644
index 7e1ee2c..0000000
--- a/tempest/tests/negative/test_negative_generators.py
+++ /dev/null
@@ -1,150 +0,0 @@
-# Copyright 2014 Deutsche Telekom AG
-# 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
-
-import jsonschema
-import mock
-import six
-
-from tempest.common.generator import base_generator
-from tempest.common.generator import negative_generator
-from tempest.common.generator import valid_generator
-from tempest.tests import base
-
-
-class TestNegativeBasicGenerator(base.TestCase):
-    valid_desc = {
-        "name": "list-flavors-with-detail",
-        "http-method": "GET",
-        "url": "flavors/detail",
-        "json-schema": {
-            "type": "object",
-            "properties": {
-                "minRam": {"type": "integer"},
-                "minDisk": {"type": "integer"}
-            }
-        },
-        "resources": ["flavor", "volume", "image"]
-    }
-
-    minimal_desc = {
-        "name": "list-flavors-with-detail",
-        "http-method": "GET",
-        "url": "flavors/detail",
-    }
-
-    add_prop_desc = {
-        "name": "list-flavors-with-detail",
-        "http-method": "GET",
-        "url": "flavors/detail",
-        "unknown_field": [12]
-    }
-
-    invalid_json_schema_desc = {
-        "name": "list-flavors-with-detail",
-        "http-method": "GET",
-        "url": "flavors/detail",
-        "json-schema": {"type": "NotExistingType"}
-    }
-
-    def setUp(self):
-        super(TestNegativeBasicGenerator, self).setUp()
-        self.generator = base_generator.BasicGeneratorSet()
-
-    def _assert_valid_jsonschema_call(self, jsonschema_mock, desc):
-        self.assertEqual(jsonschema_mock.call_count, 1)
-        jsonschema_mock.assert_called_with(desc, self.generator.schema)
-
-    @mock.patch('jsonschema.validate', wraps=jsonschema.validate)
-    def test_validate_schema_with_valid_input(self, jsonschema_mock):
-        self.generator.validate_schema(self.valid_desc)
-        self._assert_valid_jsonschema_call(jsonschema_mock, self.valid_desc)
-
-    @mock.patch('jsonschema.validate', wraps=jsonschema.validate)
-    def test_validate_schema_with_minimal_input(self, jsonschema_mock):
-        self.generator.validate_schema(self.minimal_desc)
-        self._assert_valid_jsonschema_call(jsonschema_mock, self.minimal_desc)
-
-    def test_validate_schema_with_invalid_input(self):
-        self.assertRaises(jsonschema.ValidationError,
-                          self.generator.validate_schema, self.add_prop_desc)
-        self.assertRaises(jsonschema.SchemaError,
-                          self.generator.validate_schema,
-                          self.invalid_json_schema_desc)
-
-
-class BaseNegativeGenerator(object):
-    types = ['string', 'integer', 'object']
-
-    fake_input_obj = {"type": "object",
-                      "properties": {"minRam": {"type": "integer"},
-                                     "diskName": {"type": "string"},
-                                     "maxRam": {"type": "integer", }
-                                     }
-                      }
-
-    unknown_type_schema = {
-        "type": "not_defined"
-    }
-
-    class fake_test_class(object):
-        def __init__(self, scenario):
-            for k, v in six.iteritems(scenario):
-                setattr(self, k, v)
-
-    def _validate_result(self, valid_schema, invalid_schema):
-        for k, v in six.iteritems(valid_schema):
-            self.assertIn(k, invalid_schema)
-
-    def test_generator_mandatory_functions(self):
-        for data_type in self.types:
-            self.assertIn(data_type, self.generator.types_dict)
-
-    def test_generate_with_unknown_type(self):
-        self.assertRaises(TypeError, self.generator.generate_payload,
-                          self.unknown_type_schema)
-
-
-class TestNegativeValidGenerator(base.TestCase, BaseNegativeGenerator):
-    def setUp(self):
-        super(TestNegativeValidGenerator, self).setUp()
-        self.generator = valid_generator.ValidTestGenerator()
-
-    def test_generate_valid(self):
-        result = self.generator.generate_valid(self.fake_input_obj)
-        self.assertIn("minRam", result)
-        self.assertIsInstance(result["minRam"], int)
-        self.assertIn("diskName", result)
-        self.assertIsInstance(result["diskName"], str)
-
-
-class TestNegativeNegativeGenerator(base.TestCase, BaseNegativeGenerator):
-    def setUp(self):
-        super(TestNegativeNegativeGenerator, self).setUp()
-        self.generator = negative_generator.NegativeTestGenerator()
-
-    def test_generate_obj(self):
-        schema = self.fake_input_obj
-        scenarios = self.generator.generate_scenarios(schema)
-        for scenario in scenarios:
-            test = self.fake_test_class(scenario)
-            valid_schema = \
-                valid_generator.ValidTestGenerator().generate_valid(schema)
-            schema_under_test = copy.copy(valid_schema)
-            expected_result = \
-                self.generator.generate_payload(test, schema_under_test)
-            self.assertIsNone(expected_result)
-            self._validate_result(valid_schema, schema_under_test)
diff --git a/tox.ini b/tox.ini
index 82dba92..7a36e84 100644
--- a/tox.ini
+++ b/tox.ini
@@ -143,10 +143,10 @@
 # E125 is a won't fix until https://github.com/jcrocholl/pep8/issues/126 is resolved.  For further detail see https://review.openstack.org/#/c/36788/
 # E123 skipped because it is ignored by default in the default pep8
 # E129 skipped because it is too limiting when combined with other rules
-# Skipped because of new hacking 0.9: H405
 ignore = E125,E123,E129
 show-source = True
 exclude = .git,.venv,.tox,dist,doc,*egg
+enable-extensions = H106,H203
 
 [testenv:releasenotes]
 commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
@@ -161,3 +161,12 @@
 commands=
     pip-extra-reqs -d --ignore-file=tempest/tests/* tempest
     pip-missing-reqs -d --ignore-file=tempest/tests/* tempest
+
+
+[testenv:bindep]
+# Do not install any requirements. We want this to be fast and work even if
+# system dependencies are missing, since it's used to tell you what system
+# dependencies are missing! This also means that bindep must be installed
+# separately, outside of the requirements files.
+deps = bindep
+commands = bindep test