Merge "Fix ambiguous method name"
diff --git a/tempest/api/volume/admin/test_volume_services.py b/tempest/api/volume/admin/test_volume_services.py
index 47130de..755365d 100644
--- a/tempest/api/volume/admin/test_volume_services.py
+++ b/tempest/api/volume/admin/test_volume_services.py
@@ -12,9 +12,6 @@
 #    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 import decorators
-
 from tempest.api.volume import base
 from tempest import config
 from tempest import test
@@ -23,6 +20,12 @@
 CONF = config.CONF
 
 
+def _get_host(host):
+    if CONF.volume_feature_enabled.volume_services:
+        host = host.split('@')[0]
+    return host
+
+
 class VolumesServicesV2TestJSON(base.BaseVolumeAdminTest):
     """Tests Volume Services API.
 
@@ -34,7 +37,10 @@
         super(VolumesServicesV2TestJSON, cls).resource_setup()
         cls.services = (cls.admin_volume_services_client.list_services()
                         ['services'])
-        cls.host_name = cls.services[0]['host']
+        # NOTE: Cinder service-list API returns the list contains
+        # "<host name>@<driver name>" like "nova-compute01@lvmdriver-1".
+        # So here picks <host name> up as a host.
+        cls.host_name = _get_host(cls.services[0]['host'])
         cls.binary_name = cls.services[0]['binary']
 
     @test.idempotent_id('e0218299-0a59-4f43-8b2b-f1c035b3d26d')
@@ -51,16 +57,10 @@
         for service in services:
             self.assertEqual(self.binary_name, service['binary'])
 
-    @decorators.skip_because(bug="1530144")
     @test.idempotent_id('178710e4-7596-4e08-9333-745cb8bc4f8d')
     def test_get_service_by_host_name(self):
-        def get_host(host):
-            if CONF.volume_feature_enabled.volume_services:
-                host = host.split('@')[0]
-            return host
-
         services_on_host = [service for service in self.services if
-                            get_host(service['host']) == self.host_name]
+                            _get_host(service['host']) == self.host_name]
 
         services = (self.admin_volume_services_client.list_services(
             host=self.host_name)['services'])
@@ -80,7 +80,7 @@
             host=self.host_name, binary=self.binary_name))['services']
 
         self.assertEqual(1, len(services))
-        self.assertEqual(self.host_name, services[0]['host'])
+        self.assertEqual(self.host_name, _get_host(services[0]['host']))
         self.assertEqual(self.binary_name, services[0]['binary'])
 
 
diff --git a/tempest/clients.py b/tempest/clients.py
index 60baca6..e88a016 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -122,7 +122,8 @@
     EndPointClient as EndPointV3Client
 from tempest.services.identity.v3.json.groups_client import \
     GroupsClient as GroupsV3Client
-from tempest.services.identity.v3.json.identity_client import IdentityV3Client
+from tempest.services.identity.v3.json.identity_client import \
+    IdentityClient as IdentityV3Client
 from tempest.services.identity.v3.json.policies_client import \
     PoliciesClient as PoliciesV3Client
 from tempest.services.identity.v3.json.projects_client import ProjectsClient
@@ -133,7 +134,8 @@
 from tempest.services.identity.v3.json.services_client import \
     ServicesClient as IdentityServicesV3Client
 from tempest.services.identity.v3.json.trusts_client import TrustsClient
-from tempest.services.identity.v3.json.users_clients import UsersV3Client
+from tempest.services.identity.v3.json.users_clients import \
+    UsersClient as UsersV3Client
 from tempest.services.image.v1.json.images_client import ImagesClient
 from tempest.services.image.v2.json.images_client import ImagesClientV2
 from tempest.services.network.json.network_client import NetworkClient
diff --git a/tempest/cmd/javelin.py b/tempest/cmd/javelin.py
index e474d86..95be89e 100755
--- a/tempest/cmd/javelin.py
+++ b/tempest/cmd/javelin.py
@@ -756,7 +756,7 @@
     # we cannot assume they all have the same signature so we need to discard
     # the unused response first value it two values are being returned.
     body = get_resources()
-    if type(body) == tuple:
+    if isinstance(body, tuple):
         body = body[1]
     if isinstance(body, dict):
         body = body[resource]
diff --git a/tempest/lib/auth.py b/tempest/lib/auth.py
index 806acb5..0586346 100644
--- a/tempest/lib/auth.py
+++ b/tempest/lib/auth.py
@@ -323,10 +323,10 @@
 
         parts = urlparse.urlparse(_base_url)
         if filters.get('api_version', None) is not None:
-            path = "/" + filters['api_version']
-            noversion_path = "/".join(parts.path.split("/")[2:])
-            if noversion_path != "":
-                path += "/" + noversion_path
+            path = re.sub(r'(^|/)+v\d+(?:\.\d+)?',
+                          '/' + filters['api_version'],
+                          parts.path,
+                          count=1)
             _base_url = _base_url.replace(parts.path, path)
         if filters.get('skip_path', None) is not None and parts.path != '':
             _base_url = _base_url.replace(parts.path, "/")
@@ -445,10 +445,10 @@
 
         parts = urlparse.urlparse(_base_url)
         if filters.get('api_version', None) is not None:
-            path = "/" + filters['api_version']
-            noversion_path = "/".join(parts.path.split("/")[2:])
-            if noversion_path != "":
-                path += "/" + noversion_path
+            path = re.sub(r'(^|/)+v\d+(?:\.\d+)?',
+                          '/' + filters['api_version'],
+                          parts.path,
+                          count=1)
             _base_url = _base_url.replace(parts.path, path)
         if filters.get('skip_path', None) is not None:
             _base_url = _base_url.replace(parts.path, "/")
diff --git a/tempest/lib/cmd/check_uuid.py b/tempest/lib/cmd/check_uuid.py
index 3adeecd..be3aa49 100755
--- a/tempest/lib/cmd/check_uuid.py
+++ b/tempest/lib/cmd/check_uuid.py
@@ -173,9 +173,9 @@
 
     @staticmethod
     def _import_name(node):
-        if type(node) == ast.Import:
+        if isinstance(node, ast.Import):
             return node.names[0].name
-        elif type(node) == ast.ImportFrom:
+        elif isinstance(node, ast.ImportFrom):
             return '%s.%s' % (node.module, node.names[0].name)
 
     def _add_import_for_test_uuid(self, patcher, src_parsed, source_path):
diff --git a/tempest/lib/services/compute/versions_client.py b/tempest/lib/services/compute/versions_client.py
index 5898f93..ed82c74 100644
--- a/tempest/lib/services/compute/versions_client.py
+++ b/tempest/lib/services/compute/versions_client.py
@@ -12,6 +12,8 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
+import re
+
 from oslo_serialization import jsonutils as json
 from six.moves import urllib
 
@@ -22,14 +24,17 @@
 class VersionsClient(rest_client.RestClient):
 
     def _get_base_version_url(self):
-        # NOTE: The URL which is gotten from keystone's catalog contains
-        # API version and project-id like "v2/{project-id}", but we need
-        # to access the URL which doesn't contain them for getting API
-        # versions. For that, here should use raw_request() instead of
-        # get().
+        # NOTE: The URL which is got from keystone's catalog contains
+        # API version and project-id like "/app-name/v2/{project-id}" or
+        # "/v2/{project-id}", but we need to access the URL which doesn't
+        # contain API version for getting API versions. For that, here
+        # should use raw_request() instead of get().
         endpoint = self.base_url
-        url = urllib.parse.urlparse(endpoint)
-        return '%s://%s/' % (url.scheme, url.netloc)
+        url = urllib.parse.urlsplit(endpoint)
+        new_path = re.split(r'(^|/)+v\d+(\.\d+)?', url.path)[0]
+        url = list(url)
+        url[2] = new_path + '/'
+        return urllib.parse.urlunsplit(url)
 
     def list_versions(self):
         version_url = self._get_base_version_url()
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 4ce57db..71bb50e 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -10,6 +10,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from oslo_log import log as logging
+
 from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
@@ -17,6 +19,7 @@
 from tempest import test
 
 CONF = config.CONF
+LOG = logging.getLogger(__name__)
 
 
 class TestVolumeBootPattern(manager.ScenarioTest):
@@ -32,6 +35,11 @@
      * Boot an additional instance from the new snapshot based volume
      * Check written content in the instance booted from snapshot
     """
+
+    # Boot from volume scenario is quite slow, and needs extra
+    # breathing room to get through deletes in the time allotted.
+    TIMEOUT_SCALING_FACTOR = 2
+
     @classmethod
     def skip_checks(cls):
         super(TestVolumeBootPattern, cls).skip_checks()
@@ -101,42 +109,53 @@
     @test.attr(type='smoke')
     @test.services('compute', 'volume', 'image')
     def test_volume_boot_pattern(self):
+        LOG.info("Creating keypair and security group")
         keypair = self.create_keypair()
         security_group = self._create_security_group()
 
         # create an instance from volume
+        LOG.info("Booting instance 1 from volume")
         volume_origin = self._create_volume_from_image()
         instance_1st = self._boot_instance_from_volume(volume_origin['id'],
                                                        keypair, security_group)
+        LOG.info("Booted first instance: %s" % instance_1st)
 
         # write content to volume on instance
+        LOG.info("Setting timestamp in instance %s" % instance_1st)
         ip_instance_1st = self.get_server_ip(instance_1st)
         timestamp = self.create_timestamp(ip_instance_1st,
                                           private_key=keypair['private_key'])
 
         # delete instance
+        LOG.info("Deleting first instance: %s" % instance_1st)
         self._delete_server(instance_1st)
 
         # create a 2nd instance from volume
         instance_2nd = self._boot_instance_from_volume(volume_origin['id'],
                                                        keypair, security_group)
+        LOG.info("Booted second instance %s" % instance_2nd)
 
         # check the content of written file
+        LOG.info("Getting timestamp in instance %s" % instance_2nd)
         ip_instance_2nd = self.get_server_ip(instance_2nd)
         timestamp2 = self.get_timestamp(ip_instance_2nd,
                                         private_key=keypair['private_key'])
         self.assertEqual(timestamp, timestamp2)
 
         # snapshot a volume
+        LOG.info("Creating snapshot from volume: %s" % volume_origin['id'])
         snapshot = self._create_snapshot_from_volume(volume_origin['id'])
 
         # create a 3rd instance from snapshot
+        LOG.info("Creating third instance from snapshot: %s" % snapshot['id'])
         volume = self._create_volume_from_snapshot(snapshot['id'])
         server_from_snapshot = (
             self._boot_instance_from_volume(volume['id'],
                                             keypair, security_group))
 
         # check the content of written file
+        LOG.info("Logging into third instance to get timestamp: %s" %
+                 server_from_snapshot)
         server_from_snapshot_ip = self.get_server_ip(server_from_snapshot)
         timestamp3 = self.get_timestamp(server_from_snapshot_ip,
                                         private_key=keypair['private_key'])
diff --git a/tempest/services/baremetal/base.py b/tempest/services/baremetal/base.py
index d8cb99d..6e24801 100644
--- a/tempest/services/baremetal/base.py
+++ b/tempest/services/baremetal/base.py
@@ -16,7 +16,7 @@
 import six
 from six.moves.urllib import parse as urllib
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
 def handle_errors(f):
@@ -39,7 +39,7 @@
     return wrapper
 
 
-class BaremetalClient(service_client.ServiceClient):
+class BaremetalClient(rest_client.RestClient):
     """Base Tempest REST client for Ironic API."""
 
     uri_prefix = ''
diff --git a/tempest/services/compute/json/keypairs_client.py b/tempest/services/compute/json/keypairs_client.py
index ec9b1e0..045f03c 100644
--- a/tempest/services/compute/json/keypairs_client.py
+++ b/tempest/services/compute/json/keypairs_client.py
@@ -17,7 +17,7 @@
 
 from tempest.api_schema.response.compute.v2_1 import keypairs as schemav21
 from tempest.api_schema.response.compute.v2_2 import keypairs as schemav22
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 from tempest.services.compute.json import base_compute_client
 
 
@@ -31,14 +31,14 @@
         body = json.loads(body)
         schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.list_keypairs, resp, body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def show_keypair(self, keypair_name):
         resp, body = self.get("os-keypairs/%s" % keypair_name)
         body = json.loads(body)
         schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.get_keypair, resp, body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def create_keypair(self, **kwargs):
         post_body = json.dumps({'keypair': kwargs})
@@ -46,10 +46,10 @@
         body = json.loads(body)
         schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.create_keypair, resp, body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def delete_keypair(self, keypair_name):
         resp, body = self.delete("os-keypairs/%s" % keypair_name)
         schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.delete_keypair, resp, body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/data_processing/v1_1/data_processing_client.py b/tempest/services/data_processing/v1_1/data_processing_client.py
index 5aa2622..c74672f 100644
--- a/tempest/services/data_processing/v1_1/data_processing_client.py
+++ b/tempest/services/data_processing/v1_1/data_processing_client.py
@@ -14,10 +14,10 @@
 
 from oslo_serialization import jsonutils as json
 
-from tempest.common import service_client
+from tempest.lib.common import rest_client
 
 
-class DataProcessingClient(service_client.ServiceClient):
+class DataProcessingClient(rest_client.RestClient):
 
     def _request_and_check_resp(self, request_func, uri, resp_status):
         """Make a request and check response status code.
@@ -26,7 +26,7 @@
         """
         resp, body = request_func(uri)
         self.expected_success(resp_status, resp.status)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def _request_and_check_resp_data(self, request_func, uri, resp_status):
         """Make a request and check response status code.
@@ -47,7 +47,7 @@
         resp, body = request_func(uri, headers=headers, *args, **kwargs)
         self.expected_success(resp_status, resp.status)
         body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return rest_client.ResponseBody(resp, body)
 
     def list_node_group_templates(self):
         """List all node group templates for a user."""
diff --git a/tempest/services/identity/v3/json/identity_client.py b/tempest/services/identity/v3/json/identity_client.py
index fed33c5..28c3cfd 100644
--- a/tempest/services/identity/v3/json/identity_client.py
+++ b/tempest/services/identity/v3/json/identity_client.py
@@ -18,7 +18,7 @@
 from tempest.common import service_client
 
 
-class IdentityV3Client(service_client.ServiceClient):
+class IdentityClient(service_client.ServiceClient):
     api_version = "v3"
 
     def show_api_description(self):
diff --git a/tempest/services/identity/v3/json/users_clients.py b/tempest/services/identity/v3/json/users_clients.py
index 85c2e79..481fdf0 100644
--- a/tempest/services/identity/v3/json/users_clients.py
+++ b/tempest/services/identity/v3/json/users_clients.py
@@ -18,7 +18,7 @@
 from tempest.common import service_client
 
 
-class UsersV3Client(service_client.ServiceClient):
+class UsersClient(service_client.ServiceClient):
     api_version = "v3"
 
     def create_user(self, user_name, password=None, project_id=None,
diff --git a/tempest/test.py b/tempest/test.py
index 35a1944..fe3c770 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -249,6 +249,9 @@
     # Client manager class to use in this test case.
     client_manager = clients.Manager
 
+    # A way to adjust slow test classes
+    TIMEOUT_SCALING_FACTOR = 1
+
     @classmethod
     def setUpClass(cls):
         # It should never be overridden by descendants
@@ -419,7 +422,7 @@
         at_exit_set.add(self.__class__)
         test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0)
         try:
-            test_timeout = int(test_timeout)
+            test_timeout = int(test_timeout) * self.TIMEOUT_SCALING_FACTOR
         except ValueError:
             test_timeout = 0
         if test_timeout > 0:
diff --git a/tempest/tests/common/test_service_clients.py b/tempest/tests/common/test_service_clients.py
index 7e9ff9d..0c58e71 100644
--- a/tempest/tests/common/test_service_clients.py
+++ b/tempest/tests/common/test_service_clients.py
@@ -16,8 +16,6 @@
 import random
 import six
 
-from tempest.services.baremetal.v1.json import baremetal_client
-from tempest.services.data_processing.v1_1 import data_processing_client
 from tempest.services.database.json import flavors_client as db_flavor_client
 from tempest.services.database.json import versions_client as db_version_client
 from tempest.services.identity.v2.json import identity_client as \
@@ -81,8 +79,6 @@
     @mock.patch('tempest.lib.common.rest_client.RestClient.__init__')
     def test_service_client_creations_with_specified_args(self, mock_init):
         test_clients = [
-            baremetal_client.BaremetalClient,
-            data_processing_client.DataProcessingClient,
             db_flavor_client.DatabaseFlavorsClient,
             db_version_client.DatabaseVersionsClient,
             network_client.NetworkClient,
@@ -115,7 +111,7 @@
             identity_v2_identity_client.IdentityClient,
             credentials_client.CredentialsClient,
             endpoints_client.EndPointClient,
-            identity_v3_identity_client.IdentityV3Client,
+            identity_v3_identity_client.IdentityClient,
             policies_client.PoliciesClient,
             regions_client.RegionsClient,
             services_client.ServicesClient,
diff --git a/tempest/tests/lib/fake_auth_provider.py b/tempest/tests/lib/fake_auth_provider.py
index 7f00fb8..8095453 100644
--- a/tempest/tests/lib/fake_auth_provider.py
+++ b/tempest/tests/lib/fake_auth_provider.py
@@ -16,15 +16,16 @@
 
 class FakeAuthProvider(object):
 
-    def __init__(self, creds_dict=None):
+    def __init__(self, creds_dict=None, fake_base_url=None):
         creds_dict = creds_dict or {}
         self.credentials = FakeCredentials(creds_dict)
+        self.fake_base_url = fake_base_url
 
     def auth_request(self, method, url, headers=None, body=None, filters=None):
         return url, headers, body
 
     def base_url(self, filters, auth_data=None):
-        return "https://example.com"
+        return self.fake_base_url or "https://example.com"
 
 
 class FakeCredentials(object):
diff --git a/tempest/tests/lib/services/compute/test_versions_client.py b/tempest/tests/lib/services/compute/test_versions_client.py
index 5ac2f2d..fc6c1d2 100644
--- a/tempest/tests/lib/services/compute/test_versions_client.py
+++ b/tempest/tests/lib/services/compute/test_versions_client.py
@@ -94,3 +94,42 @@
 
     def test_get_version_by_url_with_bytes_body(self):
         self._test_get_version_by_url(bytes_body=True)
+
+    def _test_get_base_version_url(self, url, expected_base_url):
+        auth = fake_auth_provider.FakeAuthProvider(fake_base_url=url)
+        client = versions_client.VersionsClient(auth, 'compute', 'regionOne')
+        self.assertEqual(expected_base_url, client._get_base_version_url())
+
+    def test_get_base_version_url(self):
+        self._test_get_base_version_url('https://bar.org/v2/123',
+                                        'https://bar.org/')
+        self._test_get_base_version_url('https://bar.org/v2.1/123',
+                                        'https://bar.org/')
+        self._test_get_base_version_url('https://bar.org/v2.15/123',
+                                        'https://bar.org/')
+        self._test_get_base_version_url('https://bar.org/v22.2/123',
+                                        'https://bar.org/')
+        self._test_get_base_version_url('https://bar.org/v22/123',
+                                        'https://bar.org/')
+
+    def test_get_base_version_url_app_name(self):
+        self._test_get_base_version_url('https://bar.org/compute/v2/123',
+                                        'https://bar.org/compute/')
+        self._test_get_base_version_url('https://bar.org/compute/v2.1/123',
+                                        'https://bar.org/compute/')
+        self._test_get_base_version_url('https://bar.org/compute/v2.15/123',
+                                        'https://bar.org/compute/')
+        self._test_get_base_version_url('https://bar.org/compute/v22.2/123',
+                                        'https://bar.org/compute/')
+        self._test_get_base_version_url('https://bar.org/compute/v22/123',
+                                        'https://bar.org/compute/')
+
+    def test_get_base_version_url_double_slash(self):
+        self._test_get_base_version_url('https://bar.org//v2/123',
+                                        'https://bar.org/')
+        self._test_get_base_version_url('https://bar.org//v2.1/123',
+                                        'https://bar.org/')
+        self._test_get_base_version_url('https://bar.org/compute//v2/123',
+                                        'https://bar.org/compute/')
+        self._test_get_base_version_url('https://bar.org/compute//v2.1/123',
+                                        'https://bar.org/compute/')