Merge "Add a bit more debugging to the client registry"
diff --git a/releasenotes/notes/add-list-version-to-identity-client-944cb7396088a575.yaml b/releasenotes/notes/add-list-version-to-identity-client-944cb7396088a575.yaml
new file mode 100644
index 0000000..dd66ff5
--- /dev/null
+++ b/releasenotes/notes/add-list-version-to-identity-client-944cb7396088a575.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Add versions_client module for identity service.
+    This new module provides list_versions() method which shows API versions
+    from Identity service.
diff --git a/requirements.txt b/requirements.txt
index 124da7a..6962e3e 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,7 +1,7 @@
 # The order of packages is significant, because pip processes them in the order
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
-pbr>=1.8 # Apache-2.0
+pbr>=2.0.0 # Apache-2.0
 cliff>=2.3.0 # Apache-2.0
 jsonschema!=2.5.0,<3.0.0,>=2.0.0 # MIT
 testtools>=1.4.0 # MIT
@@ -17,7 +17,7 @@
 fixtures>=3.0.0 # Apache-2.0/BSD
 PyYAML>=3.10.0 # MIT
 python-subunit>=0.0.18 # Apache-2.0/BSD
-stevedore>=1.17.1 # Apache-2.0
+stevedore>=1.20.0 # Apache-2.0
 PrettyTable<0.8,>=0.7.1 # BSD
 os-testr>=0.8.0 # Apache-2.0
 urllib3>=1.15.1 # MIT
diff --git a/setup.py b/setup.py
index 782bb21..566d844 100644
--- a/setup.py
+++ b/setup.py
@@ -25,5 +25,5 @@
     pass
 
 setuptools.setup(
-    setup_requires=['pbr>=1.8'],
+    setup_requires=['pbr>=2.0.0'],
     pbr=True)
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index 344779c..3c15d8c 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -178,6 +178,7 @@
         cls.non_admin_users_client = cls.os.users_v3_client
         cls.non_admin_token = cls.os.token_v3_client
         cls.non_admin_projects_client = cls.os.projects_client
+        cls.non_admin_versions_client = cls.os.identity_versions_v3_client
 
 
 class BaseIdentityV3AdminTest(BaseIdentityV3Test):
diff --git a/tempest/api/identity/v3/test_api_discovery.py b/tempest/api/identity/v3/test_api_discovery.py
index 2eed3c8..177a49d 100644
--- a/tempest/api/identity/v3/test_api_discovery.py
+++ b/tempest/api/identity/v3/test_api_discovery.py
@@ -21,6 +21,20 @@
 class TestApiDiscovery(base.BaseIdentityV3Test):
     """Tests for API discovery features."""
 
+    @decorators.idempotent_id('721f480f-35b6-46c7-846e-047e6acea0dc')
+    @test.attr(type='smoke')
+    def test_list_api_versions(self):
+        # NOTE: Actually this API doesn't depend on v3 API at all, because
+        # the API operation is "GET /" without v3's endpoint. The reason of
+        # this test path is just v3 API is CURRENT on Keystone side.
+        versions = self.non_admin_versions_client.list_versions()
+        expected_resources = ('id', 'links', 'media-types', 'status',
+                              'updated')
+
+        for version in versions['versions']["values"]:
+            for res in expected_resources:
+                self.assertIn(res, version)
+
     @test.attr(type='smoke')
     @decorators.idempotent_id('b9232f5e-d9e5-4d97-b96c-28d3db4de1bd')
     def test_api_version_resources(self):
diff --git a/tempest/clients.py b/tempest/clients.py
index cdd6925..9cb918a 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -225,6 +225,8 @@
         self.credentials_client = self.identity_v3.CredentialsClient(
             **params_v3)
         self.groups_client = self.identity_v3.GroupsClient(**params_v3)
+        self.identity_versions_v3_client = self.identity_v3.VersionsClient(
+            **params_v3)
 
         # Token clients do not use the catalog. They only need default_params.
         # They read auth_url, so they should only be set if the corresponding
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index f5bff20..3246d6a 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -23,6 +23,7 @@
 from oslo_log import log as logging
 from oslo_serialization import jsonutils as json
 import six
+from six.moves import urllib
 
 from tempest.lib.common import http
 from tempest.lib.common import jsonschema_validator
@@ -915,6 +916,16 @@
                     msg = ("HTTP response header is invalid (%s)" % ex)
                     raise exceptions.InvalidHTTPResponseHeader(msg)
 
+    def _get_base_version_url(self):
+        # TODO(oomichi): This method can be used for auth's replace_version().
+        # So it is nice to have common logic for the maintenance.
+        endpoint = self.base_url
+        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)
+
 
 class ResponseBody(dict):
     """Class that wraps an http response and dict body into a single value.
diff --git a/tempest/lib/services/compute/versions_client.py b/tempest/lib/services/compute/versions_client.py
index b2052c3..75984ec 100644
--- a/tempest/lib/services/compute/versions_client.py
+++ b/tempest/lib/services/compute/versions_client.py
@@ -12,10 +12,7 @@
 # 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
 
 from tempest.lib.api_schema.response.compute.v2_1 import versions as schema
 from tempest.lib.common import rest_client
@@ -24,19 +21,6 @@
 
 class VersionsClient(base_compute_client.BaseComputeClient):
 
-    def _get_base_version_url(self):
-        # 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.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()
         resp, body = self.raw_request(version_url, 'GET')
diff --git a/tempest/lib/services/identity/v3/__init__.py b/tempest/lib/services/identity/v3/__init__.py
index 8058d51..88801e7 100644
--- a/tempest/lib/services/identity/v3/__init__.py
+++ b/tempest/lib/services/identity/v3/__init__.py
@@ -30,9 +30,10 @@
 from tempest.lib.services.identity.v3.token_client import V3TokenClient
 from tempest.lib.services.identity.v3.trusts_client import TrustsClient
 from tempest.lib.services.identity.v3.users_client import UsersClient
+from tempest.lib.services.identity.v3.versions_client import VersionsClient
 
 __all__ = ['CredentialsClient', 'DomainsClient', 'EndPointsClient',
            'GroupsClient', 'IdentityClient', 'InheritedRolesClient',
            'PoliciesClient', 'ProjectsClient', 'RegionsClient',
            'RoleAssignmentsClient', 'RolesClient', 'ServicesClient',
-           'V3TokenClient', 'TrustsClient', 'UsersClient', ]
+           'V3TokenClient', 'TrustsClient', 'UsersClient', 'VersionsClient']
diff --git a/tempest/lib/services/identity/v3/versions_client.py b/tempest/lib/services/identity/v3/versions_client.py
new file mode 100644
index 0000000..441ee0d
--- /dev/null
+++ b/tempest/lib/services/identity/v3/versions_client.py
@@ -0,0 +1,38 @@
+# Copyright 2017 NEC Corporation.  All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import time
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class VersionsClient(rest_client.RestClient):
+    api_version = "v3"
+
+    def list_versions(self):
+        """List API versions"""
+        version_url = self._get_base_version_url()
+
+        start = time.time()
+        resp, body = self.raw_request(version_url, 'GET')
+        end = time.time()
+        self._log_request('GET', version_url, resp, secs=(end - start),
+                          resp_body=body)
+        self._error_checker(resp, body)
+
+        self.expected_success(300, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/tests/lib/services/identity/v3/test_versions_client.py b/tempest/tests/lib/services/identity/v3/test_versions_client.py
new file mode 100644
index 0000000..3bfaf1e
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_versions_client.py
@@ -0,0 +1,70 @@
+# Copyright 2017 NEC Corporation.  All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.lib.services.identity.v3 import versions_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestIdentityClient(base.BaseServiceTest):
+
+    FAKE_VERSIONS_INFO = {
+        "versions": {
+            "values": [
+                {"status": "stable", "updated": "2017-02-22T00:00:00Z",
+                 "media-types": [
+                     {"base": "application/json", "type":
+                      "application/vnd.openstack.identity-v3+json"}
+                 ],
+                 "id": "v3.8",
+                 "links": [
+                     {"href": "https://15.184.67.226/identity_admin/v3/",
+                      "rel": "self"}
+                 ]},
+                {"status": "deprecated", "updated": "2016-08-04T00:00:00Z",
+                 "media-types": [
+                     {"base": "application/json",
+                      "type": "application/vnd.openstack.identity-v2.0+json"}
+                 ],
+                 "id": "v2.0",
+                 "links": [
+                     {"href": "https://15.184.67.226/identity_admin/v2.0/",
+                      "rel": "self"},
+                     {"href": "https://docs.openstack.org/",
+                      "type": "text/html", "rel": "describedby"}
+                 ]}
+            ]
+        }
+    }
+
+    def setUp(self):
+        super(TestIdentityClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = versions_client.VersionsClient(fake_auth,
+                                                     'identity',
+                                                     'regionOne')
+
+    def _test_list_versions(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_versions,
+            'tempest.lib.common.rest_client.RestClient.raw_request',
+            self.FAKE_VERSIONS_INFO,
+            bytes_body,
+            300)
+
+    def test_list_versions_with_str_body(self):
+        self._test_list_versions()
+
+    def test_list_versions_with_bytes_body(self):
+        self._test_list_versions(bytes_body=True)
diff --git a/test-requirements.txt b/test-requirements.txt
index 936d5aa..13950bd 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,7 +1,7 @@
 # The order of packages is significant, because pip processes them in the order
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
-hacking>=0.12.0,!=0.13.0,<0.14  # Apache-2.0
+hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
 # needed for doc build
 sphinx>=1.5.1 # BSD
 oslosphinx>=4.7.0 # Apache-2.0