Add api-version to get server versions

Mitaka Cinder added an API to return Versions from the base
endpoint URL:
http://<url>:8776/
This patch exposes that API for /v3 endpoint microversions 3.0 and
above with the command:
cinder api-version

Implements: blueprint add-get-server-versions

Change-Id: Ieb1a56b28188ec17946fe5564b28c165833ffc24
This commit is contained in:
scottda 2016-04-08 15:35:41 -06:00
parent 72304df07a
commit 09b51a294e
7 changed files with 126 additions and 1 deletions

@ -335,6 +335,14 @@ class Manager(common_base.HookableMixin):
body = body or {}
return common_base.DictWithMeta(body, resp)
def _get_with_base_url(self, url, response_key=None):
resp, body = self.api.client.get_with_base_url(url)
if response_key:
return [self.resource_class(self, res, loaded=True)
for res in body[response_key] if res]
else:
return self.resource_class(self, body, loaded=True)
class ManagerWithFind(six.with_metaclass(abc.ABCMeta, Manager)):
"""

@ -151,6 +151,11 @@ class SessionClient(adapter.LegacyJsonAdapter):
def delete(self, url, **kwargs):
return self._cs_request(url, 'DELETE', **kwargs)
def _get_base_url(self):
endpoint = self.get_endpoint()
base_url = '/'.join(endpoint.split('/')[:3]) + '/'
return base_url
def get_volume_api_version_from_endpoint(self):
try:
version = get_volume_api_from_url(self.get_endpoint())
@ -176,6 +181,16 @@ class SessionClient(adapter.LegacyJsonAdapter):
raise AttributeError('There is no service catalog for this type of '
'auth plugin.')
def _cs_request_base_url(self, url, method, **kwargs):
base_url = self._get_base_url(**kwargs)
return self._cs_request(
base_url + url,
method,
**kwargs)
def get_with_base_url(self, url, **kwargs):
return self._cs_request_base_url(url, 'GET', **kwargs)
class HTTPClient(object):

@ -79,6 +79,13 @@ class ClientTest(utils.TestCase):
cinderclient.client.get_volume_api_from_url,
unknown_url)
@mock.patch('cinderclient.client.SessionClient.get_endpoint')
def test_get_base_url(self, mock_get_endpoint):
url = 'http://192.168.122.104:8776/v3/de50d1f33a38415fadfd3e1dea28f4d3'
mock_get_endpoint.return_value = url
cs = cinderclient.client.SessionClient(self, api_version='3.0')
self.assertEqual('http://192.168.122.104:8776/', cs._get_base_url())
@mock.patch.object(cinderclient.client, '_log_request_id')
@mock.patch.object(adapter.Adapter, 'request')
@mock.patch.object(exceptions, 'from_response')

@ -249,6 +249,65 @@ def _stub_extend(id, new_size):
return {'volume_id': '712f4980-5ac1-41e5-9383-390aa7c9f58b'}
def _stub_server_versions():
return [
{
"status": "SUPPORTED",
"updated": "2015-07-30T11:33:21Z",
"links": [
{
"href": "http://docs.openstack.org/",
"type": "text/html",
"rel": "describedby",
},
{
"href": "http://localhost:8776/v1/",
"rel": "self",
}
],
"min_version": "",
"version": "",
"id": "v1.0",
},
{
"status": "SUPPORTED",
"updated": "2015-09-30T11:33:21Z",
"links": [
{
"href": "http://docs.openstack.org/",
"type": "text/html",
"rel": "describedby",
},
{
"href": "http://localhost:8776/v2/",
"rel": "self",
}
],
"min_version": "",
"version": "",
"id": "v2.0",
},
{
"status": "CURRENT",
"updated": "2016-04-01T11:33:21Z",
"links": [
{
"href": "http://docs.openstack.org/",
"type": "text/html",
"rel": "describedby",
},
{
"href": "http://localhost:8776/v3/",
"rel": "self",
}
],
"min_version": "3.0",
"version": "3.1",
"id": "v3.0",
}
]
class FakeClient(fakes.FakeClient, client.Client):
def __init__(self, api_version=None, *args, **kwargs):
@ -264,7 +323,7 @@ class FakeClient(fakes.FakeClient, client.Client):
class FakeHTTPClient(base_client.HTTPClient):
def __init__(self, **kwargs):
def __init__(self, version_header=None, **kwargs):
self.username = 'username'
self.password = 'password'
self.auth_url = 'auth_url'
@ -272,6 +331,7 @@ class FakeHTTPClient(base_client.HTTPClient):
self.management_url = 'http://10.0.2.15:8776/v2/fake'
self.osapi_max_limit = 1000
self.marker = None
self.version_header = version_header
def _cs_request(self, url, method, **kwargs):
# Check that certain things are called correctly
@ -308,6 +368,8 @@ class FakeHTTPClient(base_client.HTTPClient):
status, headers, body = getattr(self, callback)(**kwargs)
# add fake request-id header
headers['x-openstack-request-id'] = REQUEST_ID
if self.version_header:
headers['OpenStack-API-version'] = version_header
r = utils.TestResponse({
"status_code": status,
"text": body,
@ -969,6 +1031,10 @@ class FakeHTTPClient(base_client.HTTPClient):
return (200, {},
{'transfer': _stub_transfer(transfer1, base_uri, tenant_id)})
def get_with_base_url(self, url, **kw):
server_versions = _stub_server_versions()
return (200, {'versions': server_versions})
#
# Services
#

@ -80,3 +80,8 @@ class ServicesTest(utils.TestCase):
self.assertIsInstance(s, services.Service)
self.assertEqual('disabled', s.status)
self._assert_request_id(s)
def test_api_version(self):
client = fakes.FakeClient(version_header='3.0')
svs = client.services.server_api_version()
[self.assertIsInstance(s, services.Service) for s in svs]

@ -16,6 +16,7 @@
"""
service interface
"""
from cinderclient import api_versions
from cinderclient import base
@ -77,3 +78,16 @@ class ServiceManager(base.ManagerWithFind):
"""Failover a replicated backend by hostname."""
body = {"host": host, "backend_id": backend_id}
return self._update("/os-services/failover_host", body)
@api_versions.wraps("3.0")
def server_api_version(self, url_append=""):
"""Returns the API Version supported by the server.
:param url_append: String to append to url to obtain specific version
:return: Returns response obj for a server that supports microversions.
Returns an empty list for Liberty and prior Cinder servers.
"""
try:
return self._get_with_base_url(url_append, response_key='versions')
except LookupError:
return []

@ -24,6 +24,7 @@ import time
import six
from cinderclient import api_versions
from cinderclient import base
from cinderclient import exceptions
from cinderclient import utils
@ -2680,3 +2681,12 @@ def do_thaw_host(cs, args):
def do_failover_host(cs, args):
"""Failover a replicating cinder-volume host."""
cs.services.failover_host(args.host, args.backend_id)
@utils.service_type('volumev3')
@api_versions.wraps("3.0")
def do_api_version(cs, args):
"""Display the API version information."""
columns = ['ID', 'Status', 'Version', 'Min_version']
response = cs.services.server_api_version()
utils.print_list(response, columns)