diff options
author | Eric Harney <eharney@redhat.com> | 2019-06-10 13:07:37 -0400 |
---|---|---|
committer | Eric Harney <eharney@redhat.com> | 2019-08-28 11:57:02 -0400 |
commit | d1b044b82a9d632e5769b377abe4f7f653cb94d4 (patch) | |
tree | 7437a4bd9586a04f91bf3ba174b0358d573c7b62 | |
parent | a9d9d347788f6a604d3dc7bbd43393efb73d8553 (diff) | |
download | python-cinderclient-d1b044b82a9d632e5769b377abe4f7f653cb94d4.tar.gz |
Autonegotiate API version for shell
If OS_VOLUME_API_VERSION is not set, use the highest
supported by both the client and the server.
If OS_VOLUME_API_VERSION exceeds that supported by the server,
use the highest supported by both the client and the server.
A warning message is printed for the user indicating that this
happened.
(This is similar to the behavior of the manila CLI, and is
mostly code from manilaclient tweaked to work in cinderclient.)
Change-Id: Ie1403eca2a191f62169e60c0cde1622575327387
-rw-r--r-- | cinderclient/api_versions.py | 3 | ||||
-rw-r--r-- | cinderclient/shell.py | 105 | ||||
-rw-r--r-- | cinderclient/tests/unit/v3/test_shell.py | 16 | ||||
-rw-r--r-- | doc/source/user/shell.rst | 8 | ||||
-rw-r--r-- | releasenotes/notes/cli-api-ver-negotiation-9f8fd8b77ae299fd.yaml | 12 |
5 files changed, 132 insertions, 12 deletions
diff --git a/cinderclient/api_versions.py b/cinderclient/api_versions.py index 5fa6340..ab8b20e 100644 --- a/cinderclient/api_versions.py +++ b/cinderclient/api_versions.py @@ -160,6 +160,9 @@ class APIVersion(object): return "%s.%s" % (self.ver_major, "latest") return "%s.%s" % (self.ver_major, self.ver_minor) + def get_major_version(self): + return "%s" % self.ver_major + class VersionedMethod(object): diff --git a/cinderclient/shell.py b/cinderclient/shell.py index ecc5862..75e42e9 100644 --- a/cinderclient/shell.py +++ b/cinderclient/shell.py @@ -516,6 +516,21 @@ class OpenStackCinderShell(object): else: return argv + @staticmethod + def _validate_input_api_version(options): + if not options.os_volume_api_version: + api_version = api_versions.APIVersion(api_versions.MAX_VERSION) + else: + api_version = api_versions.get_api_version( + options.os_volume_api_version) + return api_version + + @staticmethod + def downgrade_warning(requested, discovered): + logger.warning("API version %s requested, " % requested.get_string()) + logger.warning("downgrading to %s based on server support." % + discovered.get_string()) + def main(self, argv): # Parse args once to find version and debug settings parser = self.get_base_parser() @@ -527,14 +542,7 @@ class OpenStackCinderShell(object): do_help = ('help' in argv) or ( '--help' in argv) or ('-h' in argv) or not argv - if not options.os_volume_api_version: - use_version = DEFAULT_MAJOR_OS_VOLUME_API_VERSION - if do_help: - use_version = api_versions.MAX_VERSION - api_version = api_versions.get_api_version(use_version) - else: - api_version = api_versions.get_api_version( - options.os_volume_api_version) + api_version = self._validate_input_api_version(options) # build available subcommands based on version major_version_string = "%s" % api_version.ver_major @@ -670,9 +678,7 @@ class OpenStackCinderShell(object): insecure = self.options.insecure - self.cs = client.Client( - api_version, os_username, - os_password, os_project_name, os_auth_url, + client_args = dict( region_name=os_region_name, tenant_id=os_project_id, endpoint_type=endpoint_type, @@ -689,6 +695,11 @@ class OpenStackCinderShell(object): session=auth_session, logger=self.ks_logger if auth_session else self.client_logger) + self.cs = client.Client( + api_version, os_username, + os_password, os_project_name, os_auth_url, + **client_args) + try: if not utils.isunauthenticated(args.func): self.cs.authenticate() @@ -718,6 +729,28 @@ class OpenStackCinderShell(object): "to the default API version: %s", endpoint_api_version) + API_MAX_VERSION = api_versions.APIVersion(api_versions.MAX_VERSION) + if endpoint_api_version[0] == '3': + disc_client = client.Client(API_MAX_VERSION, + os_username, + os_password, + os_project_name, + os_auth_url, + **client_args) + self.cs, discovered_version = self._discover_client( + disc_client, + api_version, + args.os_endpoint_type, + args.service_type, + os_username, + os_password, + os_project_name, + os_auth_url, + client_args) + + if discovered_version < api_version: + self.downgrade_warning(api_version, discovered_version) + profile = osprofiler_profiler and options.profile if profile: osprofiler_profiler.init(options.profile) @@ -731,6 +764,56 @@ class OpenStackCinderShell(object): print("To display trace use next command:\n" "osprofiler trace show --html %s " % trace_id) + def _discover_client(self, + current_client, + os_api_version, + os_endpoint_type, + os_service_type, + os_username, + os_password, + os_project_name, + os_auth_url, + client_args): + + if (os_api_version.get_major_version() in + api_versions.DEPRECATED_VERSIONS): + discovered_version = api_versions.DEPRECATED_VERSION + os_service_type = 'volume' + else: + discovered_version = api_versions.discover_version( + current_client, + os_api_version) + + if not os_endpoint_type: + os_endpoint_type = DEFAULT_CINDER_ENDPOINT_TYPE + + if not os_service_type: + os_service_type = self._discover_service_type(discovered_version) + + API_MAX_VERSION = api_versions.APIVersion(api_versions.MAX_VERSION) + + if (discovered_version != API_MAX_VERSION or + os_service_type != 'volume' or + os_endpoint_type != DEFAULT_CINDER_ENDPOINT_TYPE): + client_args['service_type'] = os_service_type + client_args['endpoint_type'] = os_endpoint_type + + return (client.Client(discovered_version, + os_username, + os_password, + os_project_name, + os_auth_url, + **client_args), + discovered_version) + else: + return current_client, discovered_version + + def _discover_service_type(self, discovered_version): + SERVICE_TYPES = {'1': 'volume', '2': 'volumev2', '3': 'volumev3'} + major_version = discovered_version.get_major_version() + service_type = SERVICE_TYPES[major_version] + return service_type + def _run_extension_hooks(self, hook_type, *args, **kwargs): """Runs hooks for all registered extensions.""" for extension in self.extensions: diff --git a/cinderclient/tests/unit/v3/test_shell.py b/cinderclient/tests/unit/v3/test_shell.py index b145d8e..6e7a33d 100644 --- a/cinderclient/tests/unit/v3/test_shell.py +++ b/cinderclient/tests/unit/v3/test_shell.py @@ -46,6 +46,7 @@ import six from six.moves.urllib import parse import cinderclient +from cinderclient import api_versions from cinderclient import base from cinderclient import client from cinderclient import exceptions @@ -91,7 +92,12 @@ class ShellTest(utils.TestCase): self.cs = mock.Mock() def run_command(self, cmd): - self.shell.main(cmd.split()) + # Ensure the version negotiation indicates that + # all versions are supported + with mock.patch('cinderclient.api_versions._get_server_version_range', + return_value=(api_versions.APIVersion('3.0'), + api_versions.APIVersion('3.99'))): + self.shell.main(cmd.split()) def assert_called(self, method, url, body=None, partial_body=None, **kwargs): @@ -284,6 +290,14 @@ class ShellTest(utils.TestCase): mock_print.assert_called_once_with(mock.ANY, key_list, exclude_unavailable=True, sortby_index=0) + @mock.patch("cinderclient.shell.OpenStackCinderShell.downgrade_warning") + def test_list_version_downgrade(self, mock_warning): + self.run_command('--os-volume-api-version 3.998 list') + mock_warning.assert_called_once_with( + api_versions.APIVersion('3.998'), + api_versions.APIVersion(api_versions.MAX_VERSION) + ) + def test_list_availability_zone(self): self.run_command('availability-zone-list') self.assert_called('GET', '/os-availability-zone') diff --git a/doc/source/user/shell.rst b/doc/source/user/shell.rst index 0d03b01..50d8feb 100644 --- a/doc/source/user/shell.rst +++ b/doc/source/user/shell.rst @@ -40,6 +40,14 @@ For example, in Bash you'd use:: export OS_AUTH_URL=http://auth.example.com:5000/v3 export OS_VOLUME_API_VERSION=3 +If OS_VOLUME_API_VERSION is not set, the highest version +supported by the server will be used. + +If OS_VOLUME_API_VERSION exceeds the highest version +supported by the server, the highest version supported by +both the client and server will be used. A warning +message is printed when this occurs. + From there, all shell commands take the form:: cinder <command> [arguments...] diff --git a/releasenotes/notes/cli-api-ver-negotiation-9f8fd8b77ae299fd.yaml b/releasenotes/notes/cli-api-ver-negotiation-9f8fd8b77ae299fd.yaml new file mode 100644 index 0000000..4501850 --- /dev/null +++ b/releasenotes/notes/cli-api-ver-negotiation-9f8fd8b77ae299fd.yaml @@ -0,0 +1,12 @@ +--- +features: + - | + Automatic version negotiation for the cinderclient CLI. + If an API version is not specified, the CLI will use the newest + supported by the client and the server. + If an API version newer than the server supports is requested, + the CLI will fall back to the newest version supported by the server + and issue a warning message. + This does not affect cinderclient library usage. + + |