Merge "Make compute keypairs_client use rest_client"
diff --git a/releasenotes/notes/10.0-supported-openstack-releases-b88db468695348f6.yaml b/releasenotes/notes/10.0-supported-openstack-releases-b88db468695348f6.yaml
new file mode 100644
index 0000000..217c2f6
--- /dev/null
+++ b/releasenotes/notes/10.0-supported-openstack-releases-b88db468695348f6.yaml
@@ -0,0 +1,13 @@
+---
+other:
+    - OpenStack Releases Supported at this time are the same as in the
+      previous release 9,
+        **Kilo** and
+        **Liberty**.
+
+
+      The release under current development as of this tag is Mitaka,
+      meaning that every Tempest commit is also tested against master during
+      the Mitaka cycle. However, this does not necessarily mean that using
+      Tempest as of this tag will work against a Mitaka (or future releases)
+      cloud.
diff --git a/releasenotes/notes/Tempest-library-interface-0eb680b810139a50.yaml b/releasenotes/notes/Tempest-library-interface-0eb680b810139a50.yaml
new file mode 100644
index 0000000..0ed3130
--- /dev/null
+++ b/releasenotes/notes/Tempest-library-interface-0eb680b810139a50.yaml
@@ -0,0 +1,11 @@
+---
+prelude: |
+    This release includes the addition of the stable library interface for
+    tempest. This behaves just as tempest-lib did prior to this, but instead
+    it lives directly in the tempest project. For more information refer to
+    the `library docs`_.
+
+    .. _library docs: http://docs.openstack.org/developer/tempest/library.html#library
+
+features:
+  - Tempest library interface
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/common/dynamic_creds.py b/tempest/common/dynamic_creds.py
index a51f389..0a01bd4 100644
--- a/tempest/common/dynamic_creds.py
+++ b/tempest/common/dynamic_creds.py
@@ -184,12 +184,19 @@
                 router = self._create_router(router_name, tenant_id)
                 self._add_router_interface(router['id'], subnet['id'])
         except Exception:
-            if router:
-                self._clear_isolated_router(router['id'], router['name'])
-            if subnet:
-                self._clear_isolated_subnet(subnet['id'], subnet['name'])
-            if network:
-                self._clear_isolated_network(network['id'], network['name'])
+            try:
+                if router:
+                    self._clear_isolated_router(router['id'], router['name'])
+                if subnet:
+                    self._clear_isolated_subnet(subnet['id'], subnet['name'])
+                if network:
+                    self._clear_isolated_network(network['id'],
+                                                 network['name'])
+            except Exception as cleanup_exception:
+                msg = "There was an exception trying to setup network " \
+                      "resources for tenant %s, and this error happened " \
+                      "trying to clean them up: %s"
+                LOG.warning(msg % (tenant_id, cleanup_exception))
             raise
         return network, subnet, router
 
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/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/')