summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Harney <eharney@redhat.com>2019-06-10 13:07:37 -0400
committerEric Harney <eharney@redhat.com>2019-08-28 11:57:02 -0400
commitd1b044b82a9d632e5769b377abe4f7f653cb94d4 (patch)
tree7437a4bd9586a04f91bf3ba174b0358d573c7b62
parenta9d9d347788f6a604d3dc7bbd43393efb73d8553 (diff)
downloadpython-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.py3
-rw-r--r--cinderclient/shell.py105
-rw-r--r--cinderclient/tests/unit/v3/test_shell.py16
-rw-r--r--doc/source/user/shell.rst8
-rw-r--r--releasenotes/notes/cli-api-ver-negotiation-9f8fd8b77ae299fd.yaml12
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.
+
+