Merge "Implements 'microversions' api type - Part 2"
This commit is contained in:
commit
40a4070f28
|
@ -11,6 +11,7 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import functools
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import pkgutil
|
import pkgutil
|
||||||
|
@ -20,6 +21,7 @@ from oslo_utils import strutils
|
||||||
|
|
||||||
from novaclient import exceptions
|
from novaclient import exceptions
|
||||||
from novaclient.i18n import _, _LW
|
from novaclient.i18n import _, _LW
|
||||||
|
from novaclient import utils
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
if not LOG.handlers:
|
if not LOG.handlers:
|
||||||
|
@ -29,6 +31,7 @@ if not LOG.handlers:
|
||||||
# key is a deprecated version and value is an alternative version.
|
# key is a deprecated version and value is an alternative version.
|
||||||
DEPRECATED_VERSIONS = {"1.1": "2"}
|
DEPRECATED_VERSIONS = {"1.1": "2"}
|
||||||
|
|
||||||
|
_SUBSTITUTIONS = {}
|
||||||
|
|
||||||
_type_error_msg = _("'%(other)s' should be an instance of '%(cls)s'")
|
_type_error_msg = _("'%(other)s' should be an instance of '%(cls)s'")
|
||||||
|
|
||||||
|
@ -150,6 +153,31 @@ class APIVersion(object):
|
||||||
return "%s.%s" % (self.ver_major, self.ver_minor)
|
return "%s.%s" % (self.ver_major, self.ver_minor)
|
||||||
|
|
||||||
|
|
||||||
|
class VersionedMethod(object):
|
||||||
|
|
||||||
|
def __init__(self, name, start_version, end_version, func):
|
||||||
|
"""Versioning information for a single method
|
||||||
|
|
||||||
|
:param name: Name of the method
|
||||||
|
:param start_version: Minimum acceptable version
|
||||||
|
:param end_version: Maximum acceptable_version
|
||||||
|
:param func: Method to call
|
||||||
|
|
||||||
|
Minimum and maximums are inclusive
|
||||||
|
"""
|
||||||
|
self.name = name
|
||||||
|
self.start_version = start_version
|
||||||
|
self.end_version = end_version
|
||||||
|
self.func = func
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return ("Version Method %s: min: %s, max: %s"
|
||||||
|
% (self.name, self.start_version, self.end_version))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<VersionedMethod %s>" % self.name
|
||||||
|
|
||||||
|
|
||||||
def get_available_major_versions():
|
def get_available_major_versions():
|
||||||
# NOTE(andreykurilin): available clients version should not be
|
# NOTE(andreykurilin): available clients version should not be
|
||||||
# hardcoded, so let's discover them.
|
# hardcoded, so let's discover them.
|
||||||
|
@ -206,3 +234,44 @@ def update_headers(headers, api_version):
|
||||||
|
|
||||||
if not api_version.is_null() and api_version.ver_minor != 0:
|
if not api_version.is_null() and api_version.ver_minor != 0:
|
||||||
headers["X-OpenStack-Nova-API-Version"] = api_version.get_string()
|
headers["X-OpenStack-Nova-API-Version"] = api_version.get_string()
|
||||||
|
|
||||||
|
|
||||||
|
def add_substitution(versioned_method):
|
||||||
|
_SUBSTITUTIONS.setdefault(versioned_method.name, [])
|
||||||
|
_SUBSTITUTIONS[versioned_method.name].append(versioned_method)
|
||||||
|
|
||||||
|
|
||||||
|
def get_substitutions(func_name, api_version=None):
|
||||||
|
substitutions = _SUBSTITUTIONS.get(func_name, [])
|
||||||
|
if api_version and not api_version.is_null():
|
||||||
|
return [m for m in substitutions
|
||||||
|
if api_version.matches(m.start_version, m.end_version)]
|
||||||
|
return substitutions
|
||||||
|
|
||||||
|
|
||||||
|
def wraps(start_version, end_version=None):
|
||||||
|
start_version = APIVersion(start_version)
|
||||||
|
if end_version:
|
||||||
|
end_version = APIVersion(end_version)
|
||||||
|
else:
|
||||||
|
end_version = APIVersion("%s.latest" % start_version.ver_major)
|
||||||
|
|
||||||
|
def decor(func):
|
||||||
|
func.versioned = True
|
||||||
|
name = utils.get_function_name(func)
|
||||||
|
versioned_method = VersionedMethod(name, start_version,
|
||||||
|
end_version, func)
|
||||||
|
add_substitution(versioned_method)
|
||||||
|
|
||||||
|
@functools.wraps(func)
|
||||||
|
def substitution(obj, *args, **kwargs):
|
||||||
|
methods = get_substitutions(name, obj.api_version)
|
||||||
|
|
||||||
|
if not methods:
|
||||||
|
raise exceptions.VersionNotFoundForAPIMethod(
|
||||||
|
obj.api_version.get_string(), name)
|
||||||
|
else:
|
||||||
|
return max(methods, key=lambda f: f.start_version).func(
|
||||||
|
obj, *args, **kwargs)
|
||||||
|
return substitution
|
||||||
|
return decor
|
||||||
|
|
|
@ -61,6 +61,10 @@ class Manager(base.HookableMixin):
|
||||||
def client(self):
|
def client(self):
|
||||||
return self.api.client
|
return self.api.client
|
||||||
|
|
||||||
|
@property
|
||||||
|
def api_version(self):
|
||||||
|
return self.api.api_version
|
||||||
|
|
||||||
def _list(self, url, response_key, obj_class=None, body=None):
|
def _list(self, url, response_key, obj_class=None, body=None):
|
||||||
if body:
|
if body:
|
||||||
_resp, body = self.api.client.post(url, body=body)
|
_resp, body = self.api.client.post(url, body=body)
|
||||||
|
|
|
@ -82,6 +82,17 @@ class InstanceInErrorState(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class VersionNotFoundForAPIMethod(Exception):
|
||||||
|
msg_fmt = "API version '%(vers)s' is not supported on '%(method)s' method."
|
||||||
|
|
||||||
|
def __init__(self, version, method):
|
||||||
|
self.version = version
|
||||||
|
self.method = method
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.msg_fmt % {"vers": self.version, "method": self.method}
|
||||||
|
|
||||||
|
|
||||||
class ClientException(Exception):
|
class ClientException(Exception):
|
||||||
"""
|
"""
|
||||||
The base exception class for all exceptions this library raises.
|
The base exception class for all exceptions this library raises.
|
||||||
|
|
|
@ -426,7 +426,7 @@ class OpenStackComputeShell(object):
|
||||||
|
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def get_subcommand_parser(self, version):
|
def get_subcommand_parser(self, version, do_help=False):
|
||||||
parser = self.get_base_parser()
|
parser = self.get_base_parser()
|
||||||
|
|
||||||
self.subcommands = {}
|
self.subcommands = {}
|
||||||
|
@ -435,12 +435,11 @@ class OpenStackComputeShell(object):
|
||||||
actions_module = importutils.import_module(
|
actions_module = importutils.import_module(
|
||||||
"novaclient.v%s.shell" % version.ver_major)
|
"novaclient.v%s.shell" % version.ver_major)
|
||||||
|
|
||||||
# TODO(andreykurilin): discover actions based on microversions
|
self._find_actions(subparsers, actions_module, version, do_help)
|
||||||
self._find_actions(subparsers, actions_module)
|
self._find_actions(subparsers, self, version, do_help)
|
||||||
self._find_actions(subparsers, self)
|
|
||||||
|
|
||||||
for extension in self.extensions:
|
for extension in self.extensions:
|
||||||
self._find_actions(subparsers, extension.module)
|
self._find_actions(subparsers, extension.module, version, do_help)
|
||||||
|
|
||||||
self._add_bash_completion_subparser(subparsers)
|
self._add_bash_completion_subparser(subparsers)
|
||||||
|
|
||||||
|
@ -460,12 +459,28 @@ class OpenStackComputeShell(object):
|
||||||
self.subcommands['bash_completion'] = subparser
|
self.subcommands['bash_completion'] = subparser
|
||||||
subparser.set_defaults(func=self.do_bash_completion)
|
subparser.set_defaults(func=self.do_bash_completion)
|
||||||
|
|
||||||
def _find_actions(self, subparsers, actions_module):
|
def _find_actions(self, subparsers, actions_module, version, do_help):
|
||||||
|
msg = _(" (Supported by API versions '%(start)s' - '%(end)s')")
|
||||||
for attr in (a for a in dir(actions_module) if a.startswith('do_')):
|
for attr in (a for a in dir(actions_module) if a.startswith('do_')):
|
||||||
# I prefer to be hyphen-separated instead of underscores.
|
# I prefer to be hyphen-separated instead of underscores.
|
||||||
command = attr[3:].replace('_', '-')
|
command = attr[3:].replace('_', '-')
|
||||||
callback = getattr(actions_module, attr)
|
callback = getattr(actions_module, attr)
|
||||||
desc = callback.__doc__ or ''
|
desc = callback.__doc__ or ''
|
||||||
|
if hasattr(callback, "versioned"):
|
||||||
|
subs = api_versions.get_substitutions(
|
||||||
|
utils.get_function_name(callback))
|
||||||
|
if do_help:
|
||||||
|
desc += msg % {'start': subs[0].start_version.get_string(),
|
||||||
|
'end': subs[-1].end_version.get_string()}
|
||||||
|
else:
|
||||||
|
for versioned_method in subs:
|
||||||
|
if version.matches(versioned_method.start_version,
|
||||||
|
versioned_method.end_version):
|
||||||
|
callback = versioned_method.func
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
action_help = desc.strip()
|
action_help = desc.strip()
|
||||||
arguments = getattr(callback, 'arguments', [])
|
arguments = getattr(callback, 'arguments', [])
|
||||||
|
|
||||||
|
@ -482,7 +497,26 @@ class OpenStackComputeShell(object):
|
||||||
)
|
)
|
||||||
self.subcommands[command] = subparser
|
self.subcommands[command] = subparser
|
||||||
for (args, kwargs) in arguments:
|
for (args, kwargs) in arguments:
|
||||||
subparser.add_argument(*args, **kwargs)
|
start_version = kwargs.get("start_version", None)
|
||||||
|
if start_version:
|
||||||
|
start_version = api_versions.APIVersion(start_version)
|
||||||
|
end_version = kwargs.get("end_version", None)
|
||||||
|
if end_version:
|
||||||
|
end_version = api_versions.APIVersion(end_version)
|
||||||
|
else:
|
||||||
|
end_version = api_versions.APIVersion(
|
||||||
|
"%s.latest" % start_version.ver_major)
|
||||||
|
if do_help:
|
||||||
|
kwargs["help"] = kwargs.get("help", "") + (msg % {
|
||||||
|
"start": start_version.get_string(),
|
||||||
|
"end": end_version.get_string()})
|
||||||
|
else:
|
||||||
|
if not version.matches(start_version, end_version):
|
||||||
|
continue
|
||||||
|
kw = kwargs.copy()
|
||||||
|
kw.pop("start_version", None)
|
||||||
|
kw.pop("end_version", None)
|
||||||
|
subparser.add_argument(*args, **kw)
|
||||||
subparser.set_defaults(func=callback)
|
subparser.set_defaults(func=callback)
|
||||||
|
|
||||||
def setup_debugging(self, debug):
|
def setup_debugging(self, debug):
|
||||||
|
@ -534,7 +568,8 @@ class OpenStackComputeShell(object):
|
||||||
spot = argv.index('--endpoint_type')
|
spot = argv.index('--endpoint_type')
|
||||||
argv[spot] = '--endpoint-type'
|
argv[spot] = '--endpoint-type'
|
||||||
|
|
||||||
subcommand_parser = self.get_subcommand_parser(api_version)
|
subcommand_parser = self.get_subcommand_parser(
|
||||||
|
api_version, do_help=("help" in args))
|
||||||
self.parser = subcommand_parser
|
self.parser = subcommand_parser
|
||||||
|
|
||||||
if options.help or not argv:
|
if options.help or not argv:
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
# Copyright 2011 OpenStack Foundation
|
||||||
|
# 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 novaclient import api_versions
|
||||||
|
from novaclient.openstack.common import cliutils
|
||||||
|
|
||||||
|
|
||||||
|
@api_versions.wraps("2.10", "2.20")
|
||||||
|
def do_fake_action():
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
@api_versions.wraps("2.21", "2.30")
|
||||||
|
def do_fake_action():
|
||||||
|
return 2
|
||||||
|
|
||||||
|
|
||||||
|
@cliutils.arg(
|
||||||
|
'--foo',
|
||||||
|
start_version='2.1',
|
||||||
|
end_version='2.2')
|
||||||
|
@cliutils.arg(
|
||||||
|
'--bar',
|
||||||
|
start_version='2.3',
|
||||||
|
end_version='2.4')
|
||||||
|
def do_fake_action2():
|
||||||
|
return 3
|
|
@ -168,3 +168,82 @@ class GetAPIVersionTestCase(utils.TestCase):
|
||||||
self.assertEqual(mock_apiversion.return_value,
|
self.assertEqual(mock_apiversion.return_value,
|
||||||
api_versions.get_api_version(version))
|
api_versions.get_api_version(version))
|
||||||
mock_apiversion.assert_called_once_with(version)
|
mock_apiversion.assert_called_once_with(version)
|
||||||
|
|
||||||
|
|
||||||
|
class WrapsTestCase(utils.TestCase):
|
||||||
|
|
||||||
|
def _get_obj_with_vers(self, vers):
|
||||||
|
return mock.MagicMock(api_version=api_versions.APIVersion(vers))
|
||||||
|
|
||||||
|
def _side_effect_of_vers_method(self, *args, **kwargs):
|
||||||
|
m = mock.MagicMock(start_version=args[1], end_version=args[2])
|
||||||
|
m.name = args[0]
|
||||||
|
return m
|
||||||
|
|
||||||
|
@mock.patch("novaclient.utils.get_function_name")
|
||||||
|
@mock.patch("novaclient.api_versions.VersionedMethod")
|
||||||
|
def test_end_version_is_none(self, mock_versioned_method, mock_name):
|
||||||
|
func_name = "foo"
|
||||||
|
mock_name.return_value = func_name
|
||||||
|
mock_versioned_method.side_effect = self._side_effect_of_vers_method
|
||||||
|
|
||||||
|
@api_versions.wraps("2.2")
|
||||||
|
def foo(*args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
foo(self._get_obj_with_vers("2.4"))
|
||||||
|
|
||||||
|
mock_versioned_method.assert_called_once_with(
|
||||||
|
func_name, api_versions.APIVersion("2.2"),
|
||||||
|
api_versions.APIVersion("2.latest"), mock.ANY)
|
||||||
|
|
||||||
|
@mock.patch("novaclient.utils.get_function_name")
|
||||||
|
@mock.patch("novaclient.api_versions.VersionedMethod")
|
||||||
|
def test_start_and_end_version_are_presented(self, mock_versioned_method,
|
||||||
|
mock_name):
|
||||||
|
func_name = "foo"
|
||||||
|
mock_name.return_value = func_name
|
||||||
|
mock_versioned_method.side_effect = self._side_effect_of_vers_method
|
||||||
|
|
||||||
|
@api_versions.wraps("2.2", "2.6")
|
||||||
|
def foo(*args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
foo(self._get_obj_with_vers("2.4"))
|
||||||
|
|
||||||
|
mock_versioned_method.assert_called_once_with(
|
||||||
|
func_name, api_versions.APIVersion("2.2"),
|
||||||
|
api_versions.APIVersion("2.6"), mock.ANY)
|
||||||
|
|
||||||
|
@mock.patch("novaclient.utils.get_function_name")
|
||||||
|
@mock.patch("novaclient.api_versions.VersionedMethod")
|
||||||
|
def test_api_version_doesnt_match(self, mock_versioned_method, mock_name):
|
||||||
|
func_name = "foo"
|
||||||
|
mock_name.return_value = func_name
|
||||||
|
mock_versioned_method.side_effect = self._side_effect_of_vers_method
|
||||||
|
|
||||||
|
@api_versions.wraps("2.2", "2.6")
|
||||||
|
def foo(*args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.assertRaises(exceptions.VersionNotFoundForAPIMethod,
|
||||||
|
foo, self._get_obj_with_vers("2.1"))
|
||||||
|
|
||||||
|
mock_versioned_method.assert_called_once_with(
|
||||||
|
func_name, api_versions.APIVersion("2.2"),
|
||||||
|
api_versions.APIVersion("2.6"), mock.ANY)
|
||||||
|
|
||||||
|
def test_define_method_is_actually_called(self):
|
||||||
|
checker = mock.MagicMock()
|
||||||
|
|
||||||
|
@api_versions.wraps("2.2", "2.6")
|
||||||
|
def some_func(*args, **kwargs):
|
||||||
|
checker(*args, **kwargs)
|
||||||
|
|
||||||
|
obj = self._get_obj_with_vers("2.4")
|
||||||
|
some_args = ("arg_1", "arg_2")
|
||||||
|
some_kwargs = {"key1": "value1", "key2": "value2"}
|
||||||
|
|
||||||
|
some_func(obj, *some_args, **some_kwargs)
|
||||||
|
|
||||||
|
checker.assert_called_once_with(*((obj,) + some_args), **some_kwargs)
|
||||||
|
|
|
@ -23,9 +23,11 @@ import requests_mock
|
||||||
import six
|
import six
|
||||||
from testtools import matchers
|
from testtools import matchers
|
||||||
|
|
||||||
|
from novaclient import api_versions
|
||||||
import novaclient.client
|
import novaclient.client
|
||||||
from novaclient import exceptions
|
from novaclient import exceptions
|
||||||
import novaclient.shell
|
import novaclient.shell
|
||||||
|
from novaclient.tests.unit import fake_actions_module
|
||||||
from novaclient.tests.unit import utils
|
from novaclient.tests.unit import utils
|
||||||
|
|
||||||
FAKE_ENV = {'OS_USERNAME': 'username',
|
FAKE_ENV = {'OS_USERNAME': 'username',
|
||||||
|
@ -402,6 +404,113 @@ class ShellTest(utils.TestCase):
|
||||||
self.assertIsInstance(keyring_saver, novaclient.shell.SecretsHelper)
|
self.assertIsInstance(keyring_saver, novaclient.shell.SecretsHelper)
|
||||||
|
|
||||||
|
|
||||||
|
class TestLoadVersionedActions(utils.TestCase):
|
||||||
|
|
||||||
|
def test_load_versioned_actions(self):
|
||||||
|
parser = novaclient.shell.NovaClientArgumentParser()
|
||||||
|
subparsers = parser.add_subparsers(metavar='<subcommand>')
|
||||||
|
shell = novaclient.shell.OpenStackComputeShell()
|
||||||
|
shell.subcommands = {}
|
||||||
|
shell._find_actions(subparsers, fake_actions_module,
|
||||||
|
api_versions.APIVersion("2.15"), False)
|
||||||
|
self.assertIn('fake-action', shell.subcommands.keys())
|
||||||
|
self.assertEqual(
|
||||||
|
1, shell.subcommands['fake-action'].get_default('func')())
|
||||||
|
|
||||||
|
shell.subcommands = {}
|
||||||
|
shell._find_actions(subparsers, fake_actions_module,
|
||||||
|
api_versions.APIVersion("2.25"), False)
|
||||||
|
self.assertIn('fake-action', shell.subcommands.keys())
|
||||||
|
self.assertEqual(
|
||||||
|
2, shell.subcommands['fake-action'].get_default('func')())
|
||||||
|
|
||||||
|
self.assertIn('fake-action2', shell.subcommands.keys())
|
||||||
|
self.assertEqual(
|
||||||
|
3, shell.subcommands['fake-action2'].get_default('func')())
|
||||||
|
|
||||||
|
def test_load_versioned_actions_not_in_version_range(self):
|
||||||
|
parser = novaclient.shell.NovaClientArgumentParser()
|
||||||
|
subparsers = parser.add_subparsers(metavar='<subcommand>')
|
||||||
|
shell = novaclient.shell.OpenStackComputeShell()
|
||||||
|
shell.subcommands = {}
|
||||||
|
shell._find_actions(subparsers, fake_actions_module,
|
||||||
|
api_versions.APIVersion("2.10000"), False)
|
||||||
|
self.assertNotIn('fake-action', shell.subcommands.keys())
|
||||||
|
self.assertIn('fake-action2', shell.subcommands.keys())
|
||||||
|
|
||||||
|
def test_load_versioned_actions_with_help(self):
|
||||||
|
parser = novaclient.shell.NovaClientArgumentParser()
|
||||||
|
subparsers = parser.add_subparsers(metavar='<subcommand>')
|
||||||
|
shell = novaclient.shell.OpenStackComputeShell()
|
||||||
|
shell.subcommands = {}
|
||||||
|
shell._find_actions(subparsers, fake_actions_module,
|
||||||
|
api_versions.APIVersion("2.10000"), True)
|
||||||
|
self.assertIn('fake-action', shell.subcommands.keys())
|
||||||
|
expected_desc = ("(Supported by API versions '%(start)s' - "
|
||||||
|
"'%(end)s')") % {'start': '2.10', 'end': '2.30'}
|
||||||
|
self.assertIn(expected_desc,
|
||||||
|
shell.subcommands['fake-action'].description)
|
||||||
|
|
||||||
|
@mock.patch.object(novaclient.shell.NovaClientArgumentParser,
|
||||||
|
'add_argument')
|
||||||
|
def test_load_versioned_actions_with_args(self, mock_add_arg):
|
||||||
|
parser = novaclient.shell.NovaClientArgumentParser(add_help=False)
|
||||||
|
subparsers = parser.add_subparsers(metavar='<subcommand>')
|
||||||
|
shell = novaclient.shell.OpenStackComputeShell()
|
||||||
|
shell.subcommands = {}
|
||||||
|
shell._find_actions(subparsers, fake_actions_module,
|
||||||
|
api_versions.APIVersion("2.1"), False)
|
||||||
|
self.assertIn('fake-action2', shell.subcommands.keys())
|
||||||
|
mock_add_arg.assert_has_calls([
|
||||||
|
mock.call('-h', '--help', action='help', help='==SUPPRESS=='),
|
||||||
|
mock.call('--foo')])
|
||||||
|
|
||||||
|
@mock.patch.object(novaclient.shell.NovaClientArgumentParser,
|
||||||
|
'add_argument')
|
||||||
|
def test_load_versioned_actions_with_args2(self, mock_add_arg):
|
||||||
|
parser = novaclient.shell.NovaClientArgumentParser(add_help=False)
|
||||||
|
subparsers = parser.add_subparsers(metavar='<subcommand>')
|
||||||
|
shell = novaclient.shell.OpenStackComputeShell()
|
||||||
|
shell.subcommands = {}
|
||||||
|
shell._find_actions(subparsers, fake_actions_module,
|
||||||
|
api_versions.APIVersion("2.4"), False)
|
||||||
|
self.assertIn('fake-action2', shell.subcommands.keys())
|
||||||
|
mock_add_arg.assert_has_calls([
|
||||||
|
mock.call('-h', '--help', action='help', help='==SUPPRESS=='),
|
||||||
|
mock.call('--bar')])
|
||||||
|
|
||||||
|
@mock.patch.object(novaclient.shell.NovaClientArgumentParser,
|
||||||
|
'add_argument')
|
||||||
|
def test_load_versioned_actions_with_args_not_in_version_range(
|
||||||
|
self, mock_add_arg):
|
||||||
|
parser = novaclient.shell.NovaClientArgumentParser(add_help=False)
|
||||||
|
subparsers = parser.add_subparsers(metavar='<subcommand>')
|
||||||
|
shell = novaclient.shell.OpenStackComputeShell()
|
||||||
|
shell.subcommands = {}
|
||||||
|
shell._find_actions(subparsers, fake_actions_module,
|
||||||
|
api_versions.APIVersion("2.10000"), False)
|
||||||
|
self.assertIn('fake-action2', shell.subcommands.keys())
|
||||||
|
mock_add_arg.assert_has_calls([
|
||||||
|
mock.call('-h', '--help', action='help', help='==SUPPRESS==')])
|
||||||
|
|
||||||
|
@mock.patch.object(novaclient.shell.NovaClientArgumentParser,
|
||||||
|
'add_argument')
|
||||||
|
def test_load_versioned_actions_with_args_and_help(self, mock_add_arg):
|
||||||
|
parser = novaclient.shell.NovaClientArgumentParser(add_help=False)
|
||||||
|
subparsers = parser.add_subparsers(metavar='<subcommand>')
|
||||||
|
shell = novaclient.shell.OpenStackComputeShell()
|
||||||
|
shell.subcommands = {}
|
||||||
|
shell._find_actions(subparsers, fake_actions_module,
|
||||||
|
api_versions.APIVersion("2.4"), True)
|
||||||
|
mock_add_arg.assert_has_calls([
|
||||||
|
mock.call('-h', '--help', action='help', help='==SUPPRESS=='),
|
||||||
|
mock.call('-h', '--help', action='help', help='==SUPPRESS=='),
|
||||||
|
mock.call('--foo',
|
||||||
|
help=" (Supported by API versions '2.1' - '2.2')"),
|
||||||
|
mock.call('--bar',
|
||||||
|
help=" (Supported by API versions '2.3' - '2.4')")])
|
||||||
|
|
||||||
|
|
||||||
class ShellTestKeystoneV3(ShellTest):
|
class ShellTestKeystoneV3(ShellTest):
|
||||||
def make_env(self, exclude=None, fake_env=FAKE_ENV):
|
def make_env(self, exclude=None, fake_env=FAKE_ENV):
|
||||||
if 'OS_AUTH_URL' in fake_env:
|
if 'OS_AUTH_URL' in fake_env:
|
||||||
|
|
|
@ -2282,10 +2282,11 @@ class FakeHTTPClient(base_client.HTTPClient):
|
||||||
|
|
||||||
class FakeSessionClient(fakes.FakeClient, client.Client):
|
class FakeSessionClient(fakes.FakeClient, client.Client):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, api_version, *args, **kwargs):
|
||||||
client.Client.__init__(self, 'username', 'password',
|
client.Client.__init__(self, 'username', 'password',
|
||||||
'project_id', 'auth_url',
|
'project_id', 'auth_url',
|
||||||
extensions=kwargs.get('extensions'))
|
extensions=kwargs.get('extensions'),
|
||||||
|
api_version=api_version)
|
||||||
self.client = FakeSessionMockClient(**kwargs)
|
self.client = FakeSessionMockClient(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -374,3 +374,13 @@ def record_time(times, enabled, *args):
|
||||||
yield
|
yield
|
||||||
end = time.time()
|
end = time.time()
|
||||||
times.append((' '.join(args), start, end))
|
times.append((' '.join(args), start, end))
|
||||||
|
|
||||||
|
|
||||||
|
def get_function_name(func):
|
||||||
|
if six.PY2:
|
||||||
|
if hasattr(func, "im_class"):
|
||||||
|
return "%s.%s" % (func.im_class, func.__name__)
|
||||||
|
else:
|
||||||
|
return "%s.%s" % (func.__module__, func.__name__)
|
||||||
|
else:
|
||||||
|
return "%s.%s" % (func.__module__, func.__qualname__)
|
||||||
|
|
Loading…
Reference in New Issue