Merge "Adds to verify_cinder_api_versions api_v3 check"
diff --git a/tempest/api/compute/images/test_images.py b/tempest/api/compute/images/test_images.py
index 150e8af..3754637 100644
--- a/tempest/api/compute/images/test_images.py
+++ b/tempest/api/compute/images/test_images.py
@@ -49,6 +49,9 @@
                                               name=snapshot_name,
                                               wait_until='SAVING')
         self.client.delete_image(image['id'])
+        msg = ('The image with ID {image_id} failed to be deleted'
+               .format(image_id=image['id']))
+        self.assertTrue(self.client.is_resource_deleted(image['id']), msg)
 
     @test.idempotent_id('aaacd1d0-55a2-4ce8-818a-b5439df8adc9')
     def test_create_image_from_stopped_server(self):
diff --git a/tempest/api/volume/v2/test_volumes_list.py b/tempest/api/volume/v2/test_volumes_list.py
index 1fdcb49..c501ffc 100644
--- a/tempest/api/volume/v2/test_volumes_list.py
+++ b/tempest/api/volume/v2/test_volumes_list.py
@@ -42,22 +42,13 @@
         super(VolumesV2ListTestJSON, cls).resource_setup()
 
         # Create 3 test volumes
-        cls.volume_list = []
         cls.volume_id_list = []
         cls.metadata = {'Type': 'work'}
         for i in range(3):
             volume = cls.create_volume(metadata=cls.metadata)
             volume = cls.client.show_volume(volume['id'])['volume']
-            cls.volume_list.append(volume)
             cls.volume_id_list.append(volume['id'])
 
-    @classmethod
-    def resource_cleanup(cls):
-        # Delete the created volumes
-        for volid in cls.volume_id_list:
-            cls.delete_volume(cls.client, volid)
-        super(VolumesV2ListTestJSON, cls).resource_cleanup()
-
     @test.idempotent_id('2a7064eb-b9c3-429b-b888-33928fc5edd3')
     def test_volume_list_details_with_multiple_params(self):
         # List volumes detail using combined condition
diff --git a/tempest/clients.py b/tempest/clients.py
index 406f9d5..e14f6f8 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -97,19 +97,16 @@
         This uses `config.service_client_config` for all services to collect
         most configuration items needed to init the clients.
         """
-        # NOTE(andreaf) Configuration items will be passed in future patches
-        # into ClientFactory objects, but for now we update all the
-        # _set_*_client methods to consume them so we can verify that the
-        # configuration collected is correct
+        # NOTE(andreaf) Once all service clients in Tempest are migrated
+        # to tempest.lib, their configuration will be picked up from the
+        # registry, and this method will become redundant.
 
         configuration = {}
 
-        # Setup the parameters for all Tempest services.
+        # Setup the parameters for all Tempest services which are not in lib.
         # NOTE(andreaf) Since client.py is an internal module of Tempest,
         # it doesn't have to consider plugin configuration.
-        all_tempest_modules = (set(clients.tempest_modules()) |
-                               clients._tempest_internal_modules())
-        for service in all_tempest_modules:
+        for service in clients._tempest_internal_modules():
             try:
                 # NOTE(andreaf) Use the unversioned service name to fetch
                 # the configuration since configuration is not versioned.
diff --git a/tempest/config.py b/tempest/config.py
index 84edede..3fd20ab 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -26,6 +26,7 @@
 import testtools
 
 from tempest.lib import exceptions
+from tempest.lib.services import clients
 from tempest.test_discover import plugins
 
 
@@ -1288,6 +1289,15 @@
             lockutils.set_defaults(lock_dir)
             self._config = TempestConfigPrivate(config_path=self._path)
 
+            # Pushing tempest internal service client configuration to the
+            # service clients register. Doing this in the config module ensures
+            # that the configuration is available by the time we register the
+            # service clients.
+            # NOTE(andreaf) This has to be done at the time the first
+            # attribute is accessed, to ensure all plugins have been already
+            # loaded, options registered, and _config is set.
+            _register_tempest_service_clients()
+
         return getattr(self._config, attr)
 
     def set_config_path(self, path):
@@ -1447,3 +1457,29 @@
     # Set service
     _parameters['service'] = getattr(options, 'catalog_type')
     return _parameters
+
+
+def _register_tempest_service_clients():
+    # Register tempest own service clients using the same mechanism used
+    # for external plugins.
+    # The configuration data is pushed to the registry so that automatic
+    # configuration of tempest own service clients is possible both for
+    # tempest as well as for the plugins.
+    service_clients = clients.tempest_modules()
+    registry = clients.ClientsRegistry()
+    all_clients = []
+    for service_client in service_clients:
+        module = service_clients[service_client]
+        configs = service_client.split('.')[0]
+        service_client_data = dict(
+            name=service_client.replace('.', '_'),
+            service_version=service_client,
+            module_path=module.__name__,
+            client_names=module.__all__,
+            **service_client_config(configs)
+        )
+        all_clients.append(service_client_data)
+    # NOTE(andreaf) Internal service clients do not actually belong
+    # to a plugin, so using '__tempest__' to indicate a virtual plugin
+    # which holds internal service clients.
+    registry.register_service_client('__tempest__', all_clients)
diff --git a/tempest/lib/services/clients.py b/tempest/lib/services/clients.py
index e782321..adf666b 100644
--- a/tempest/lib/services/clients.py
+++ b/tempest/lib/services/clients.py
@@ -18,7 +18,6 @@
 import importlib
 import inspect
 import logging
-import six
 
 from tempest.lib import auth
 from tempest.lib.common.utils import misc
@@ -90,11 +89,14 @@
                                                 plug_service_versions))
                 raise exceptions.PluginRegistrationException(
                     name=plugin_name, detailed_error=detailed_error)
-            if not plug_service_versions.isdisjoint(_tempest_modules):
+            # NOTE(andreaf) Once all tempest clients are stable, the following
+            # if will have to be removed.
+            if not plug_service_versions.isdisjoint(
+                    _tempest_internal_modules()):
                 detailed_error = (
                     'Plugin %s is trying to register a service %s already '
                     'claimed by a Tempest one' % (plugin_name,
-                                                  _tempest_modules &
+                                                  _tempest_internal_modules() &
                                                   plug_service_versions))
                 raise exceptions.PluginRegistrationException(
                     name=plugin_name, detailed_error=detailed_error)
@@ -351,15 +353,7 @@
             raise exceptions.UnknownServiceClient(
                 services=list(client_parameters.keys()))
 
-        # Register service clients owned by tempest
-        for service, module in six.iteritems(tempest_modules()):
-            attribute = service.replace('.', '_')
-            configs = service.split('.')[0]
-            self.register_service_client_module(
-                attribute, service, module.__name__,
-                module.__all__, **self.parameters[configs])
-
-        # Register service clients from plugins
+        # Register service clients from the registry (__tempest__ and plugins)
         clients_registry = ClientsRegistry()
         plugin_service_clients = clients_registry.get_service_clients()
         for plugin in plugin_service_clients:
@@ -432,6 +426,8 @@
 
     @property
     def registered_services(self):
+        # NOTE(andreaf) Once all tempest modules are stable this needs to
+        # be updated to remove _tempest_internal_modules
         return self._registered_services | _tempest_internal_modules()
 
     def _setup_parameters(self, parameters):
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 952c0c2..6da0570 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -127,6 +127,21 @@
             wait_dict['client'] = waiter_client
         self.cleanup_waits.append(wait_dict)
 
+    def _create_port(self, network_id, client=None, namestart='port-quotatest',
+                     **kwargs):
+        if not client:
+            client = self.ports_client
+        name = data_utils.rand_name(namestart)
+        result = client.create_port(
+            name=name,
+            network_id=network_id,
+            **kwargs)
+        self.assertIsNotNone(result, 'Unable to allocate port')
+        port = result['port']
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        client.delete_port, port['id'])
+        return port
+
     def _wait_for_cleanups(self):
         # To handle async delete actions, a list of waits is added
         # which will be iterated over as the last step of clearing the
@@ -209,18 +224,18 @@
                 networks = []
 
             # If there are no networks passed to us we look up
-            # for the project's private networks and create a port
-            # if there is only one private network. The same behaviour
-            # as we would expect when passing the call to the clients
-            # with no networks
+            # for the project's private networks and create a port.
+            # The same behaviour as we would expect when passing
+            # the call to the clients with no networks
             if not networks:
                 networks = clients.networks_client.list_networks(
-                    filters={'router:external': False})
-                self.assertEqual(1, len(networks),
-                                 "There is more than one"
-                                 " network for the tenant")
+                    **{'router:external': False, 'fields': 'id'})['networks']
+
+            # It's net['uuid'] if networks come from kwargs
+            # and net['id'] if they come from
+            # clients.networks_client.list_networks
             for net in networks:
-                net_id = net['uuid']
+                net_id = net.get('uuid', net['id'])
                 if 'port' not in net:
                     port = self._create_port(network_id=net_id,
                                              client=clients.ports_client,
@@ -813,21 +828,6 @@
 
         return subnet
 
-    def _create_port(self, network_id, client=None, namestart='port-quotatest',
-                     **kwargs):
-        if not client:
-            client = self.ports_client
-        name = data_utils.rand_name(namestart)
-        result = client.create_port(
-            name=name,
-            network_id=network_id,
-            **kwargs)
-        self.assertIsNotNone(result, 'Unable to allocate port')
-        port = result['port']
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        client.delete_port, port['id'])
-        return port
-
     def _get_server_port_id_and_ip4(self, server, ip_addr=None):
         ports = self._list_ports(device_id=server['id'], fixed_ip=ip_addr)
         # A port can have more then one IP address in some cases.