summaryrefslogtreecommitdiff
path: root/openstackclient
diff options
context:
space:
mode:
Diffstat (limited to 'openstackclient')
-rw-r--r--openstackclient/common/clientmanager.py5
-rw-r--r--openstackclient/common/extension.py42
-rw-r--r--openstackclient/common/timing.py44
-rw-r--r--openstackclient/common/utils.py4
-rw-r--r--openstackclient/compute/client.py13
-rw-r--r--openstackclient/identity/client.py6
-rw-r--r--openstackclient/identity/common.py21
-rw-r--r--openstackclient/identity/v3/project.py18
-rw-r--r--openstackclient/image/client.py1
-rw-r--r--openstackclient/network/client.py6
-rw-r--r--openstackclient/network/common.py6
-rw-r--r--openstackclient/network/v2/__init__.py (renamed from openstackclient/network/v2_0/__init__.py)0
-rw-r--r--openstackclient/network/v2/network.py (renamed from openstackclient/network/v2_0/network.py)0
-rw-r--r--openstackclient/object/client.py3
-rw-r--r--openstackclient/shell.py33
-rw-r--r--openstackclient/tests/common/test_timing.py87
-rw-r--r--openstackclient/tests/identity/v3/test_project.py64
-rw-r--r--openstackclient/tests/network/test_common.py72
-rw-r--r--openstackclient/tests/network/v2/__init__.py (renamed from openstackclient/tests/network/v2_0/__init__.py)0
-rw-r--r--openstackclient/tests/network/v2/test_network.py (renamed from openstackclient/tests/network/v2_0/test_network.py)12
-rw-r--r--openstackclient/tests/test_shell.py4
-rw-r--r--openstackclient/tests/utils.py5
-rw-r--r--openstackclient/volume/client.py10
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