diff --git a/novaclient/tests/functional/v2/legacy/test_readonly_nova.py b/novaclient/tests/functional/v2/legacy/test_readonly_nova.py index 6b783a408..bf1ebe4c0 100644 --- a/novaclient/tests/functional/v2/legacy/test_readonly_nova.py +++ b/novaclient/tests/functional/v2/legacy/test_readonly_nova.py @@ -100,7 +100,7 @@ class SimpleReadOnlyNovaClientTest(base.ClientTestBase): self.nova('migration-list', flags='--debug') def test_version_list(self): - self.nova('version-list') + self.nova('version-list', flags='--debug') def test_quota_defaults(self): self.nova('quota-defaults') diff --git a/novaclient/tests/unit/v2/test_versions.py b/novaclient/tests/unit/v2/test_versions.py index 6b20cd0b3..63d63c1a3 100644 --- a/novaclient/tests/unit/v2/test_versions.py +++ b/novaclient/tests/unit/v2/test_versions.py @@ -30,7 +30,7 @@ class VersionsTest(utils.TestCase): def test_list_services(self): vl = self.cs.versions.list() self.assert_request_id(vl, fakes.FAKE_REQUEST_ID_LIST) - self.cs.assert_called('GET', 'http://nova-api:8774/') + self.cs.assert_called('GET', 'http://nova-api:8774') def test_get_current(self): self.cs.callback = [] @@ -75,3 +75,28 @@ class VersionsTest(utils.TestCase): # check that the full request works as expected cs_2.assert_called('GET', expected_endpoint) + + def test_list_versions(self): + fapi = mock.Mock() + version_mgr = versions.VersionManager(fapi) + version_mgr._list = mock.Mock() + data = [ + ("https://example.com:777/v2", "https://example.com:777"), + ("https://example.com/v2", "https://example.com"), + ("http://example.com/compute/v2", "http://example.com/compute"), + ("https://example.com/v2/prrrooojeect-uuid", + "https://example.com"), + ("https://example.com:777/v2.1", "https://example.com:777"), + ("https://example.com/v2.1", "https://example.com"), + ("http://example.com/compute/v2.1", "http://example.com/compute"), + ("https://example.com/v2.1/prrrooojeect-uuid", + "https://example.com"), + ("http://example.com/compute", "http://example.com/compute"), + ("http://compute.example.com", "http://compute.example.com"), + ] + + for endpoint, expected in data: + version_mgr._list.reset_mock() + fapi.client.get_endpoint.return_value = endpoint + version_mgr.list() + version_mgr._list.assert_called_once_with(expected, "versions") diff --git a/novaclient/v2/versions.py b/novaclient/v2/versions.py index 48cc52cd6..dd157d9f2 100644 --- a/novaclient/v2/versions.py +++ b/novaclient/v2/versions.py @@ -78,11 +78,25 @@ class VersionManager(base.ManagerWithFind): def list(self): """List all versions.""" - # NOTE: "list versions" API needs to be accessed without base - # URI (like "v2/{project-id}"), so here should be a scheme("http", - # etc.) and a hostname. endpoint = self.api.client.get_endpoint() url = urllib.parse.urlparse(endpoint) - version_url = '%s://%s/' % (url.scheme, url.netloc) + # NOTE(andreykurilin): endpoint URL has at least 3 formats: + # 1. the classic (legacy) endpoint: + # http://{host}:{optional_port}/v{2 or 2.1}/{project-id} + # 2. starting from microversion 2.18 project-id is not included: + # http://{host}:{optional_port}/v{2 or 2.1} + # 3. under wsgi: + # http://{host}:{optional_port}/compute/v{2 or 2.1} + if (url.path.endswith("v2") or "/v2/" in url.path or + url.path.endswith("v2.1") or "/v2.1/" in url.path): + # this way should handle all 3 possible formats + path = url.path[:url.path.rfind("/v2")] + version_url = '%s://%s%s' % (url.scheme, url.netloc, path) + else: + # NOTE(andreykurilin): probably, it is one of the next cases: + # * https://compute.example.com/ + # * https://example.com/compute + # leave as is without cropping. + version_url = endpoint return self._list(version_url, "versions")