diff options
Diffstat (limited to 'openstackclient')
23 files changed, 407 insertions, 49 deletions
diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py index b310f3ac..a2f85aff 100644 --- a/openstackclient/common/clientmanager.py +++ b/openstackclient/common/clientmanager.py @@ -49,7 +49,7 @@ class ClientManager(object): user_domain_id=None, user_domain_name=None, project_domain_id=None, project_domain_name=None, region_name=None, api_version=None, verify=True, - trust_id=None): + trust_id=None, timing=None): self._token = token self._url = url self._auth_url = auth_url @@ -67,6 +67,7 @@ class ClientManager(object): self._api_version = api_version self._trust_id = trust_id self._service_catalog = None + self.timing = timing # verify is the Requests-compatible form self._verify = verify @@ -116,7 +117,7 @@ def get_extension_modules(group): setattr( ClientManager, - ep.name, + module.API_NAME, ClientCache( getattr(sys.modules[ep.module_name], 'make_client', None) ), diff --git a/openstackclient/common/extension.py b/openstackclient/common/extension.py index a8b1a6b0..91ee228b 100644 --- a/openstackclient/common/extension.py +++ b/openstackclient/common/extension.py @@ -19,16 +19,14 @@ import logging from cliff import lister -from openstackclient.common import exceptions as exc from openstackclient.common import utils class ListExtension(lister.Lister): """List extension command""" - # TODO(mfisch): add support for volume and compute - # when the underlying APIs support it. Add support - # for network when it's added to openstackclient. + # TODO(mfisch): add support for volume and network + # when the underlying APIs support it. log = logging.getLogger(__name__ + '.ListExtension') @@ -44,6 +42,16 @@ class ListExtension(lister.Lister): action='store_true', default=False, help='List extensions for the Identity API') + parser.add_argument( + '--compute', + action='store_true', + default=False, + help='List extensions for the Compute API') + parser.add_argument( + '--volume', + action='store_true', + default=False, + help='List extensions for the Volume API') return parser def take_action(self, parsed_args): @@ -59,17 +67,33 @@ class ListExtension(lister.Lister): # by default we want to show everything, unless the # user specifies one or more of the APIs to show - # for now, only identity is supported - show_all = (not parsed_args.identity) + # for now, only identity and compute are supported. + show_all = (not parsed_args.identity and not parsed_args.compute + and not parsed_args.volume) if parsed_args.identity or show_all: identity_client = self.app.client_manager.identity try: data += identity_client.extensions.list() except Exception: - raise exc.CommandError( - "Extensions list not supported by" - " identity API") + message = "Extensions list not supported by Identity API" + self.log.warning(message) + + if parsed_args.compute or show_all: + compute_client = self.app.client_manager.compute + try: + data += compute_client.list_extensions.show_all() + except Exception: + message = "Extensions list not supported by Compute API" + self.log.warning(message) + + if parsed_args.volume or show_all: + volume_client = self.app.client_manager.volume + try: + data += volume_client.list_extensions.show_all() + except Exception: + message = "Extensions list not supported by Volume API" + self.log.warning(message) return (columns, (utils.get_item_properties( diff --git a/openstackclient/common/timing.py b/openstackclient/common/timing.py new file mode 100644 index 00000000..1c94682c --- /dev/null +++ b/openstackclient/common/timing.py @@ -0,0 +1,44 @@ +# 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. +# + +"""Timing Implementation""" + +import logging + +from cliff import lister + + +class Timing(lister.Lister): + """Show timing data""" + + log = logging.getLogger(__name__ + '.Timing') + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + + column_headers = ( + 'URL', + 'Seconds', + ) + + results = [] + total = 0.0 + for url, start, end in self.app.timing_data: + seconds = end - start + total += seconds + results.append((url, seconds)) + results.append(('Total', total)) + return ( + column_headers, + results, + ) diff --git a/openstackclient/common/utils.py b/openstackclient/common/utils.py index 0258f931..51c3ed4b 100644 --- a/openstackclient/common/utils.py +++ b/openstackclient/common/utils.py @@ -23,7 +23,6 @@ import sys import time from openstackclient.common import exceptions -from openstackclient.openstack.common import strutils def find_resource(manager, name_or_id): @@ -79,8 +78,7 @@ def format_dict(data): output = "" for s in data: - output = output + s + "='" + \ - strutils.safe_encode(six.text_type(data[s])) + "', " + output = output + s + "='" + six.text_type(data[s]) + "', " return output[:-2] diff --git a/openstackclient/compute/client.py b/openstackclient/compute/client.py index 3dacee88..dc50507e 100644 --- a/openstackclient/compute/client.py +++ b/openstackclient/compute/client.py @@ -15,6 +15,9 @@ import logging +from novaclient import extension +from novaclient.v1_1.contrib import list_extensions + from openstackclient.common import utils LOG = logging.getLogger(__name__) @@ -34,11 +37,12 @@ def make_client(instance): API_NAME, instance._api_version[API_NAME], API_VERSIONS) - LOG.debug('instantiating compute client: %s', compute_client) + LOG.debug('Instantiating compute client: %s', compute_client) # Set client http_log_debug to True if verbosity level is high enough http_log_debug = utils.get_effective_log_level() <= logging.DEBUG + extensions = [extension.Extension('list_extensions', list_extensions)] client = compute_client( username=instance._username, api_key=instance._password, @@ -49,12 +53,13 @@ def make_client(instance): region_name=instance._region_name, # FIXME(dhellmann): get endpoint_type from option? endpoint_type='publicURL', - # FIXME(dhellmann): add extension discovery - extensions=[], + extensions=extensions, service_type=API_NAME, # FIXME(dhellmann): what is service_name? service_name='', - http_log_debug=http_log_debug) + http_log_debug=http_log_debug, + timings=instance.timing, + ) # Populate the Nova client to skip another auth query to Identity if instance._url: diff --git a/openstackclient/identity/client.py b/openstackclient/identity/client.py index 32a2c23b..72e8bfae 100644 --- a/openstackclient/identity/client.py +++ b/openstackclient/identity/client.py @@ -36,8 +36,10 @@ def make_client(instance): API_NAME, instance._api_version[API_NAME], API_VERSIONS) + LOG.debug('Instantiating identity client: %s' % identity_client) + if instance._url: - LOG.debug('instantiating identity client: token flow') + LOG.debug('Using token auth') client = identity_client( endpoint=instance._url, token=instance._token, @@ -46,7 +48,7 @@ def make_client(instance): trust_id=instance._trust_id, ) else: - LOG.debug('instantiating identity client: password flow') + LOG.debug('Using password auth') client = identity_client( username=instance._username, password=instance._password, diff --git a/openstackclient/identity/common.py b/openstackclient/identity/common.py index 6aeaa3c3..48dc0c89 100644 --- a/openstackclient/identity/common.py +++ b/openstackclient/identity/common.py @@ -16,6 +16,7 @@ """Common identity code""" from keystoneclient import exceptions as identity_exc +from keystoneclient.v3 import domains from openstackclient.common import exceptions from openstackclient.common import utils @@ -36,3 +37,23 @@ def find_service(identity_client, name_type_or_id): msg = ("No service with a type, name or ID of '%s' exists." % name_type_or_id) raise exceptions.CommandError(msg) + + +def find_domain(identity_client, name_or_id): + """Find a domain. + + If the user does not have permssions to access the v3 domain API, + assume that domain given is the id rather than the name. This + method is used by the project list command, so errors access the + domain will be ignored and if the user has access to the project + API, everything will work fine. + + Closes bugs #1317478 and #1317485. + """ + try: + dom = utils.find_resource(identity_client.domains, name_or_id) + if dom is not None: + return dom + except identity_exc.Forbidden: + pass + return domains.Domain(None, {'id': name_or_id}) diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py index 00a98d19..fa935f0b 100644 --- a/openstackclient/identity/v3/project.py +++ b/openstackclient/identity/v3/project.py @@ -24,6 +24,7 @@ from cliff import show from openstackclient.common import parseractions from openstackclient.common import utils +from openstackclient.identity import common class CreateProject(show.ShowOne): @@ -73,10 +74,7 @@ class CreateProject(show.ShowOne): identity_client = self.app.client_manager.identity if parsed_args.domain: - domain = utils.find_resource( - identity_client.domains, - parsed_args.domain, - ).id + domain = common.find_domain(identity_client, parsed_args.domain).id else: domain = None @@ -156,10 +154,8 @@ class ListProject(lister.Lister): columns = ('ID', 'Name') kwargs = {} if parsed_args.domain: - kwargs['domain'] = utils.find_resource( - identity_client.domains, - parsed_args.domain, - ).id + domain = common.find_domain(identity_client, parsed_args.domain) + kwargs['domain'] = domain.id data = identity_client.projects.list(**kwargs) return (columns, (utils.get_item_properties( @@ -236,10 +232,8 @@ class SetProject(command.Command): if parsed_args.name: kwargs['name'] = parsed_args.name if parsed_args.domain: - kwargs['domain'] = utils.find_resource( - identity_client.domains, - parsed_args.domain, - ).id + domain = common.find_domain(identity_client, parsed_args.domain) + kwargs['domain'] = domain.id if parsed_args.description: kwargs['description'] = parsed_args.description if parsed_args.enable: diff --git a/openstackclient/image/client.py b/openstackclient/image/client.py index ba48a0e9..a23d349e 100644 --- a/openstackclient/image/client.py +++ b/openstackclient/image/client.py @@ -38,6 +38,7 @@ def make_client(instance): API_NAME, instance._api_version[API_NAME], API_VERSIONS) + LOG.debug('Instantiating image client: %s', image_client) if not instance._url: instance._url = instance.get_endpoint_for_service_type(API_NAME) diff --git a/openstackclient/network/client.py b/openstackclient/network/client.py index 3c87e135..d3102da1 100644 --- a/openstackclient/network/client.py +++ b/openstackclient/network/client.py @@ -18,11 +18,11 @@ from openstackclient.common import utils LOG = logging.getLogger(__name__) -DEFAULT_NETWORK_API_VERSION = '2.0' +DEFAULT_NETWORK_API_VERSION = '2' API_VERSION_OPTION = 'os_network_api_version' API_NAME = "network" API_VERSIONS = { - "2.0": "neutronclient.v2_0.client.Client", + "2": "neutronclient.v2_0.client.Client", } @@ -32,6 +32,8 @@ def make_client(instance): API_NAME, instance._api_version[API_NAME], API_VERSIONS) + LOG.debug('Instantiating network client: %s', network_client) + if not instance._url: instance._url = instance.get_endpoint_for_service_type("network") return network_client( diff --git a/openstackclient/network/common.py b/openstackclient/network/common.py index 5ba44f7b..bd6203bd 100644 --- a/openstackclient/network/common.py +++ b/openstackclient/network/common.py @@ -14,20 +14,22 @@ from openstackclient.common import exceptions -def find(client, resource, resources, name_or_id): +def find(client, resource, resources, name_or_id, name_attr='name'): """Find a network resource :param client: network client :param resource: name of the resource :param resources: plural name of resource :param name_or_id: name or id of resource user is looking for + :param name_attr: key to the name attribute for the resource For example: n = find(netclient, 'network', 'networks', 'matrix') """ list_method = getattr(client, "list_%s" % resources) # Search for by name - data = list_method(name=name_or_id, fields='id') + kwargs = {name_attr: name_or_id, 'fields': 'id'} + data = list_method(**kwargs) info = data[resources] if len(info) == 1: return info[0]['id'] diff --git a/openstackclient/network/v2_0/__init__.py b/openstackclient/network/v2/__init__.py index e69de29b..e69de29b 100644 --- a/openstackclient/network/v2_0/__init__.py +++ b/openstackclient/network/v2/__init__.py diff --git a/openstackclient/network/v2_0/network.py b/openstackclient/network/v2/network.py index c0c25e71..c0c25e71 100644 --- a/openstackclient/network/v2_0/network.py +++ b/openstackclient/network/v2/network.py diff --git a/openstackclient/object/client.py b/openstackclient/object/client.py index 273bea6e..4fe59794 100644 --- a/openstackclient/object/client.py +++ b/openstackclient/object/client.py @@ -35,11 +35,12 @@ def make_client(instance): API_NAME, instance._api_version[API_NAME], API_VERSIONS) + LOG.debug('Instantiating object client: %s' % object_client) + if instance._url: endpoint = instance._url else: endpoint = instance.get_endpoint_for_service_type(API_NAME) - LOG.debug('instantiating object client') client = object_client( endpoint=endpoint, token=instance._token, diff --git a/openstackclient/shell.py b/openstackclient/shell.py index 1d0c5771..28724343 100644 --- a/openstackclient/shell.py +++ b/openstackclient/shell.py @@ -32,6 +32,7 @@ from openstackclient.common import clientmanager from openstackclient.common import commandmanager from openstackclient.common import exceptions as exc from openstackclient.common import restapi +from openstackclient.common import timing from openstackclient.common import utils from openstackclient.identity import client as identity_client @@ -60,6 +61,7 @@ class OpenStackShell(app.App): CONSOLE_MESSAGE_FORMAT = '%(levelname)s: %(name)s %(message)s' log = logging.getLogger(__name__) + timing_data = [] def __init__(self): # Patch command.Command to add a default auth_required = True @@ -303,6 +305,12 @@ class OpenStackShell(app.App): metavar='<url>', default=env('OS_URL'), help='Defaults to env[OS_URL]') + parser.add_argument( + '--timing', + default=False, + action='store_true', + help="Print API call timing info", + ) parser.add_argument( '--os-identity-api-version', @@ -410,6 +418,7 @@ class OpenStackShell(app.App): password=self.options.os_password, region_name=self.options.os_region_name, verify=self.verify, + timing=self.options.timing, api_version=self.api_version, trust_id=self.options.os_trust_id, ) @@ -499,9 +508,33 @@ class OpenStackShell(app.App): def clean_up(self, cmd, result, err): self.log.debug('clean_up %s', cmd.__class__.__name__) + if err: self.log.debug('got an error: %s', err) + # Process collected timing data + if self.options.timing: + # Loop through extensions + for mod in self.ext_modules: + client = getattr(self.client_manager, mod.API_NAME) + if hasattr(client, 'get_timings'): + self.timing_data.extend(client.get_timings()) + + # Use the Timing pseudo-command to generate the output + tcmd = timing.Timing(self, self.options) + tparser = tcmd.get_parser('Timing') + + # If anything other than prettytable is specified, force csv + format = 'table' + # Check the formatter used in the actual command + if hasattr(cmd, 'formatter') \ + and cmd.formatter != cmd._formatter_plugins['table'].obj: + format = 'csv' + + sys.stdout.write('\n') + targs = tparser.parse_args(['-f', format]) + tcmd.run(targs) + def interact(self): # NOTE(dtroyer): Maintain the old behaviour for interactive use as # this path does not call prepare_to_run_command() diff --git a/openstackclient/tests/common/test_timing.py b/openstackclient/tests/common/test_timing.py new file mode 100644 index 00000000..aa910b91 --- /dev/null +++ b/openstackclient/tests/common/test_timing.py @@ -0,0 +1,87 @@ +# 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. +# + +"""Test Timing pseudo-command""" + +from openstackclient.common import timing +from openstackclient.tests import fakes +from openstackclient.tests import utils + + +timing_url = 'GET http://localhost:5000' +timing_start = 1404802774.872809 +timing_end = 1404802775.724802 + + +class FakeGenericClient(object): + + def __init__(self, **kwargs): + self.auth_token = kwargs['token'] + self.management_url = kwargs['endpoint'] + + +class TestTiming(utils.TestCommand): + + def setUp(self): + super(TestTiming, self).setUp() + + self.app.timing_data = [] + + self.app.client_manager.compute = FakeGenericClient( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + + self.app.client_manager.volume = FakeGenericClient( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + + # Get the command object to test + self.cmd = timing.Timing(self.app, None) + + def test_timing_list_no_data(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # DisplayCommandBase.take_action() returns two tuples + columns, data = self.cmd.take_action(parsed_args) + + collist = ('URL', 'Seconds') + self.assertEqual(collist, columns) + datalist = [ + ('Total', 0.0,) + ] + self.assertEqual(datalist, data) + + def test_timing_list(self): + self.app.timing_data = [ + (timing_url, timing_start, timing_end), + ] + + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # DisplayCommandBase.take_action() returns two tuples + columns, data = self.cmd.take_action(parsed_args) + + collist = ('URL', 'Seconds') + self.assertEqual(collist, columns) + timing_sec = timing_end - timing_start + datalist = [ + (timing_url, timing_sec), + ('Total', timing_sec) + ] + self.assertEqual(datalist, data) diff --git a/openstackclient/tests/identity/v3/test_project.py b/openstackclient/tests/identity/v3/test_project.py index e0420a1e..2e7bc54b 100644 --- a/openstackclient/tests/identity/v3/test_project.py +++ b/openstackclient/tests/identity/v3/test_project.py @@ -14,6 +14,7 @@ # import copy +import mock from openstackclient.identity.v3 import project from openstackclient.tests import fakes @@ -172,6 +173,45 @@ class TestProjectCreate(TestProject): ) self.assertEqual(data, datalist) + def test_project_create_domain_no_perms(self): + arglist = [ + '--domain', identity_fakes.domain_id, + identity_fakes.project_name, + ] + verifylist = [ + ('domain', identity_fakes.domain_id), + ('enable', False), + ('disable', False), + ('name', identity_fakes.project_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + mocker = mock.Mock() + mocker.return_value = None + + with mock.patch("openstackclient.common.utils.find_resource", mocker): + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': identity_fakes.project_name, + 'domain': identity_fakes.domain_id, + 'description': None, + 'enabled': True, + } + self.projects_mock.create.assert_called_with( + **kwargs + ) + collist = ('description', 'domain_id', 'enabled', 'id', 'name') + self.assertEqual(columns, collist) + datalist = ( + identity_fakes.project_description, + identity_fakes.domain_id, + True, + identity_fakes.project_id, + identity_fakes.project_name, + ) + self.assertEqual(data, datalist) + def test_project_create_enable(self): arglist = [ '--enable', @@ -411,6 +451,30 @@ class TestProjectList(TestProject): ), ) self.assertEqual(tuple(data), datalist) + def test_project_list_domain_no_perms(self): + arglist = [ + '--domain', identity_fakes.domain_id, + ] + verifylist = [ + ('domain', identity_fakes.domain_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + mocker = mock.Mock() + mocker.return_value = None + + with mock.patch("openstackclient.common.utils.find_resource", mocker): + columns, data = self.cmd.take_action(parsed_args) + + self.projects_mock.list.assert_called_with( + domain=identity_fakes.domain_id) + collist = ('ID', 'Name') + self.assertEqual(columns, collist) + datalist = (( + identity_fakes.project_id, + identity_fakes.project_name, + ), ) + self.assertEqual(tuple(data), datalist) + class TestProjectSet(TestProject): diff --git a/openstackclient/tests/network/test_common.py b/openstackclient/tests/network/test_common.py new file mode 100644 index 00000000..b30fdfcb --- /dev/null +++ b/openstackclient/tests/network/test_common.py @@ -0,0 +1,72 @@ +# 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. +# + +import mock + +from openstackclient.common import exceptions +from openstackclient.network import common +from openstackclient.tests import utils + +RESOURCE = 'resource' +RESOURCES = 'resources' +NAME = 'matrix' +ID = 'Fishburne' + + +class TestFind(utils.TestCase): + def setUp(self): + super(TestFind, self).setUp() + self.mock_client = mock.Mock() + self.list_resources = mock.Mock() + self.mock_client.list_resources = self.list_resources + self.matrix = {'id': ID} + + def test_name(self): + self.list_resources.return_value = {RESOURCES: [self.matrix]} + + result = common.find(self.mock_client, RESOURCE, RESOURCES, NAME) + + self.assertEqual(ID, result) + self.list_resources.assert_called_with(fields='id', name=NAME) + + def test_id(self): + self.list_resources.side_effect = [{RESOURCES: []}, + {RESOURCES: [self.matrix]}] + + result = common.find(self.mock_client, RESOURCE, RESOURCES, NAME) + + self.assertEqual(ID, result) + self.list_resources.assert_called_with(fields='id', id=NAME) + + def test_nameo(self): + self.list_resources.return_value = {RESOURCES: [self.matrix]} + + result = common.find(self.mock_client, RESOURCE, RESOURCES, NAME, + name_attr='nameo') + + self.assertEqual(ID, result) + self.list_resources.assert_called_with(fields='id', nameo=NAME) + + def test_dups(self): + dup = {'id': 'Larry'} + self.list_resources.return_value = {RESOURCES: [self.matrix, dup]} + + self.assertRaises(exceptions.CommandError, common.find, + self.mock_client, RESOURCE, RESOURCES, NAME) + + def test_nada(self): + self.list_resources.side_effect = [{RESOURCES: []}, + {RESOURCES: []}] + + self.assertRaises(exceptions.CommandError, common.find, + self.mock_client, RESOURCE, RESOURCES, NAME) diff --git a/openstackclient/tests/network/v2_0/__init__.py b/openstackclient/tests/network/v2/__init__.py index e69de29b..e69de29b 100644 --- a/openstackclient/tests/network/v2_0/__init__.py +++ b/openstackclient/tests/network/v2/__init__.py diff --git a/openstackclient/tests/network/v2_0/test_network.py b/openstackclient/tests/network/v2/test_network.py index ef7d24ee..08b61a0a 100644 --- a/openstackclient/tests/network/v2_0/test_network.py +++ b/openstackclient/tests/network/v2/test_network.py @@ -15,7 +15,7 @@ import copy import mock from openstackclient.common import exceptions -from openstackclient.network.v2_0 import network +from openstackclient.network.v2 import network from openstackclient.tests.network import common RESOURCE = 'network' @@ -49,7 +49,7 @@ class TestCreateNetwork(common.TestNetworkBase): cmd = network.CreateNetwork(self.app, self.namespace) parsed_args = self.check_parser(cmd, arglist, verifylist) - result = cmd.take_action(parsed_args) + result = list(cmd.take_action(parsed_args)) mocker.assert_called_with({ RESOURCE: { @@ -75,7 +75,7 @@ class TestCreateNetwork(common.TestNetworkBase): cmd = network.CreateNetwork(self.app, self.namespace) parsed_args = self.check_parser(cmd, arglist, verifylist) - result = cmd.take_action(parsed_args) + result = list(cmd.take_action(parsed_args)) mocker.assert_called_with({ RESOURCE: { @@ -102,7 +102,7 @@ class TestCreateNetwork(common.TestNetworkBase): cmd = network.CreateNetwork(self.app, self.namespace) parsed_args = self.check_parser(cmd, arglist, verifylist) - result = cmd.take_action(parsed_args) + result = list(cmd.take_action(parsed_args)) mocker.assert_called_with({ RESOURCE: { @@ -298,7 +298,7 @@ class TestShowNetwork(common.TestNetworkBase): cmd = network.ShowNetwork(self.app, self.namespace) parsed_args = self.check_parser(cmd, arglist, verifylist) - result = cmd.take_action(parsed_args) + result = list(cmd.take_action(parsed_args)) mocker.assert_called_with(FAKE_ID) self.assertEqual(FILTERED, result) @@ -313,7 +313,7 @@ class TestShowNetwork(common.TestNetworkBase): cmd = network.ShowNetwork(self.app, self.namespace) parsed_args = self.check_parser(cmd, arglist, verifylist) - result = cmd.take_action(parsed_args) + result = list(cmd.take_action(parsed_args)) mocker.assert_called_with(FAKE_ID) self.assertEqual(FILTERED, result) diff --git a/openstackclient/tests/test_shell.py b/openstackclient/tests/test_shell.py index dfb8021a..c180289e 100644 --- a/openstackclient/tests/test_shell.py +++ b/openstackclient/tests/test_shell.py @@ -39,13 +39,13 @@ DEFAULT_COMPUTE_API_VERSION = "2" DEFAULT_IDENTITY_API_VERSION = "2.0" DEFAULT_IMAGE_API_VERSION = "v2" DEFAULT_VOLUME_API_VERSION = "1" -DEFAULT_NETWORK_API_VERSION = "2.0" +DEFAULT_NETWORK_API_VERSION = "2" LIB_COMPUTE_API_VERSION = "2" LIB_IDENTITY_API_VERSION = "2.0" LIB_IMAGE_API_VERSION = "1" LIB_VOLUME_API_VERSION = "1" -LIB_NETWORK_API_VERSION = "2.0" +LIB_NETWORK_API_VERSION = "2" def make_shell(): diff --git a/openstackclient/tests/utils.py b/openstackclient/tests/utils.py index 3755fa26..38d47250 100644 --- a/openstackclient/tests/utils.py +++ b/openstackclient/tests/utils.py @@ -81,7 +81,10 @@ class TestCommand(TestCase): def check_parser(self, cmd, args, verify_args): cmd_parser = cmd.get_parser('check_parser') - parsed_args = cmd_parser.parse_args(args) + try: + parsed_args = cmd_parser.parse_args(args) + except SystemExit: + raise Exception("Argument parse failed") for av in verify_args: attr, value = av if attr: diff --git a/openstackclient/volume/client.py b/openstackclient/volume/client.py index 9b37b8f5..f71fbe8b 100644 --- a/openstackclient/volume/client.py +++ b/openstackclient/volume/client.py @@ -15,8 +15,11 @@ import logging +from cinderclient import extension +from cinderclient.v1.contrib import list_extensions from cinderclient.v1 import volume_snapshots from cinderclient.v1 import volumes + from openstackclient.common import utils # Monkey patch for v1 cinderclient @@ -40,12 +43,12 @@ def make_client(instance): instance._api_version[API_NAME], API_VERSIONS ) - - LOG.debug('instantiating volume client') + LOG.debug('Instantiating volume client: %s', volume_client) # Set client http_log_debug to True if verbosity level is high enough http_log_debug = utils.get_effective_log_level() <= logging.DEBUG + extensions = [extension.Extension('list_extensions', list_extensions)] client = volume_client( username=instance._username, api_key=instance._password, @@ -54,7 +57,8 @@ def make_client(instance): cacert=instance._cacert, insecure=instance._insecure, region_name=instance._region_name, - http_log_debug=http_log_debug + extensions=extensions, + http_log_debug=http_log_debug, ) # Populate the Cinder client to skip another auth query to Identity |
