summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--openstack-common.conf2
-rw-r--r--openstackclient/common/clientmanager.py14
-rw-r--r--openstackclient/common/utils.py15
-rw-r--r--openstackclient/compute/v2/security_group.py12
-rw-r--r--openstackclient/compute/v2/server.py2
-rw-r--r--openstackclient/identity/client.py31
-rw-r--r--openstackclient/identity/v3/endpoint.py28
-rw-r--r--openstackclient/object/v1/container.py8
-rw-r--r--openstackclient/object/v1/lib/container.py49
-rw-r--r--openstackclient/object/v1/lib/object.py46
-rw-r--r--openstackclient/object/v1/object.py8
-rw-r--r--openstackclient/openstack/common/gettextutils.py67
-rw-r--r--openstackclient/openstack/common/importutils.py73
-rw-r--r--openstackclient/openstack/common/strutils.py72
-rw-r--r--openstackclient/shell.py63
-rw-r--r--openstackclient/tests/common/test_clientmanager.py104
-rw-r--r--openstackclient/tests/fakes.py17
-rw-r--r--openstackclient/tests/identity/v3/test_endpoint.py96
-rw-r--r--openstackclient/tests/object/v1/lib/test_container.py72
-rw-r--r--openstackclient/tests/object/v1/lib/test_object.py103
-rw-r--r--openstackclient/tests/object/v1/test_container.py16
-rw-r--r--openstackclient/tests/object/v1/test_object.py18
-rw-r--r--setup.cfg3
23 files changed, 641 insertions, 278 deletions
diff --git a/openstack-common.conf b/openstack-common.conf
index 91d6387b..ac923902 100644
--- a/openstack-common.conf
+++ b/openstack-common.conf
@@ -1,7 +1,9 @@
[DEFAULT]
# The list of modules to copy from openstack-common
+module=gettextutils
module=install_venv_common
+module=importutils
module=strutils
# The base module to hold the copy of openstack.common
diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py
index a2f85aff..4dcec8e0 100644
--- a/openstackclient/common/clientmanager.py
+++ b/openstackclient/common/clientmanager.py
@@ -19,6 +19,7 @@ import logging
import pkg_resources
import sys
+from openstackclient.common import restapi
from openstackclient.identity import client as identity_client
@@ -77,7 +78,18 @@ class ClientManager(object):
self._insecure = not verify
else:
self._cacert = verify
- self._insecure = True
+ self._insecure = False
+
+ self.session = restapi.RESTApi(
+ verify=verify,
+ debug=True,
+ )
+
+ # Get logging from root logger
+ root_logger = logging.getLogger('')
+ LOG.setLevel(root_logger.getEffectiveLevel())
+ restapi_logger = logging.getLogger('restapi')
+ restapi_logger.setLevel(root_logger.getEffectiveLevel())
self.auth_ref = None
diff --git a/openstackclient/common/utils.py b/openstackclient/common/utils.py
index eb7f1b0e..227b0438 100644
--- a/openstackclient/common/utils.py
+++ b/openstackclient/common/utils.py
@@ -19,10 +19,10 @@ import getpass
import logging
import os
import six
-import sys
import time
from openstackclient.common import exceptions
+from openstackclient.openstack.common import importutils
def find_resource(manager, name_or_id):
@@ -157,17 +157,6 @@ def env(*vars, **kwargs):
return kwargs.get('default', '')
-def import_class(import_str):
- """Returns a class from a string including module and class
-
- :param import_str: a string representation of the class name
- :rtype: the requested class
- """
- mod_str, _sep, class_str = import_str.rpartition('.')
- __import__(mod_str)
- return getattr(sys.modules[mod_str], class_str)
-
-
def get_client_class(api_name, version, version_map):
"""Returns the client class for the requested API version
@@ -183,7 +172,7 @@ def get_client_class(api_name, version, version_map):
(api_name, version, ', '.join(version_map.keys())))
raise exceptions.UnsupportedVersion(msg)
- return import_class(client_path)
+ return importutils.import_class(client_path)
def wait_for_status(status_f,
diff --git a/openstackclient/compute/v2/security_group.py b/openstackclient/compute/v2/security_group.py
index 0ba55c98..cd330857 100644
--- a/openstackclient/compute/v2/security_group.py
+++ b/openstackclient/compute/v2/security_group.py
@@ -23,6 +23,7 @@ from cliff import command
from cliff import lister
from cliff import show
+from keystoneclient.openstack.common.apiclient import exceptions as ksc_exc
from novaclient.v1_1 import security_group_rules
from openstackclient.common import parseractions
from openstackclient.common import utils
@@ -150,10 +151,15 @@ class ListSecurityGroup(lister.Lister):
search = {'all_tenants': parsed_args.all_projects}
data = compute_client.security_groups.list(search_opts=search)
- projects = self.app.client_manager.identity.projects.list()
project_hash = {}
- for project in projects:
- project_hash[project.id] = project
+ try:
+ projects = self.app.client_manager.identity.projects.list()
+ except ksc_exc.Forbidden:
+ # This fails when the user is not an admin, just move along
+ pass
+ else:
+ for project in projects:
+ project_hash[project.id] = project
return (column_headers,
(utils.get_item_properties(
diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py
index cb12a6ff..ec7f212d 100644
--- a/openstackclient/compute/v2/server.py
+++ b/openstackclient/compute/v2/server.py
@@ -175,7 +175,7 @@ class AddServerSecurityGroup(command.Command):
parsed_args.group,
)
- server.add_security_group(security_group)
+ server.add_security_group(security_group.name)
return
diff --git a/openstackclient/identity/client.py b/openstackclient/identity/client.py
index 7f5390c8..820d08cb 100644
--- a/openstackclient/identity/client.py
+++ b/openstackclient/identity/client.py
@@ -66,10 +66,41 @@ def make_client(instance):
insecure=instance._insecure,
trust_id=instance._trust_id,
)
+
+ # TODO(dtroyer): the identity v2 role commands use this yet, fix that
+ # so we can remove it
instance.auth_ref = client.auth_ref
+
+ # NOTE(dtroyer): this is hanging around until restapi is replace by
+ # ksc session
+ instance.session.set_auth(
+ client.auth_ref.auth_token,
+ )
+
return client
+def build_option_parser(parser):
+ """Hook to add global options"""
+ parser.add_argument(
+ '--os-identity-api-version',
+ metavar='<identity-api-version>',
+ default=utils.env(
+ 'OS_IDENTITY_API_VERSION',
+ default=DEFAULT_IDENTITY_API_VERSION),
+ help='Identity API version, default=' +
+ DEFAULT_IDENTITY_API_VERSION +
+ ' (Env: OS_IDENTITY_API_VERSION)')
+ parser.add_argument(
+ '--os-trust-id',
+ metavar='<trust-id>',
+ default=utils.env('OS_TRUST_ID'),
+ help='Trust ID to use when authenticating. '
+ 'This can only be used with Keystone v3 API '
+ '(Env: OS_TRUST_ID)')
+ return parser
+
+
class IdentityClientv2_0(identity_client_v2_0.Client):
"""Tweak the earlier client class to deal with some changes"""
def __getattr__(self, name):
diff --git a/openstackclient/identity/v3/endpoint.py b/openstackclient/identity/v3/endpoint.py
index 5ab5dac4..39798b2d 100644
--- a/openstackclient/identity/v3/endpoint.py
+++ b/openstackclient/identity/v3/endpoint.py
@@ -114,12 +114,38 @@ class ListEndpoint(lister.Lister):
log = logging.getLogger(__name__ + '.ListEndpoint')
+ def get_parser(self, prog_name):
+ parser = super(ListEndpoint, self).get_parser(prog_name)
+ parser.add_argument(
+ '--service',
+ metavar='<service>',
+ help='Filter by a specific service')
+ parser.add_argument(
+ '--interface',
+ metavar='<interface>',
+ choices=['admin', 'public', 'internal'],
+ help='Filter by a specific interface, must be admin, public or'
+ ' internal')
+ parser.add_argument(
+ '--region',
+ metavar='<region>',
+ help='Filter by a specific region')
+ return parser
+
def take_action(self, parsed_args):
self.log.debug('take_action(%s)', parsed_args)
identity_client = self.app.client_manager.identity
columns = ('ID', 'Region', 'Service Name', 'Service Type',
'Enabled', 'Interface', 'URL')
- data = identity_client.endpoints.list()
+ kwargs = {}
+ if parsed_args.service:
+ service = common.find_service(identity_client, parsed_args.service)
+ kwargs['service'] = service.id
+ if parsed_args.interface:
+ kwargs['interface'] = parsed_args.interface
+ if parsed_args.region:
+ kwargs['region'] = parsed_args.region
+ data = identity_client.endpoints.list(**kwargs)
for ep in data:
service = common.find_service(identity_client, ep.service_id)
diff --git a/openstackclient/object/v1/container.py b/openstackclient/object/v1/container.py
index ae4013fc..1ca07f3a 100644
--- a/openstackclient/object/v1/container.py
+++ b/openstackclient/object/v1/container.py
@@ -45,7 +45,7 @@ class CreateContainer(show.ShowOne):
self.log.debug('take_action(%s)', parsed_args)
data = lib_container.create_container(
- self.app.restapi,
+ self.app.client_manager.session,
self.app.client_manager.object_store.endpoint,
parsed_args.container,
)
@@ -71,7 +71,7 @@ class DeleteContainer(command.Command):
self.log.debug('take_action(%s)', parsed_args)
lib_container.delete_container(
- self.app.restapi,
+ self.app.client_manager.session,
self.app.client_manager.object_store.endpoint,
parsed_args.container,
)
@@ -140,7 +140,7 @@ class ListContainer(lister.Lister):
kwargs['full_listing'] = True
data = lib_container.list_containers(
- self.app.restapi,
+ self.app.client_manager.session,
self.app.client_manager.object_store.endpoint,
**kwargs
)
@@ -170,7 +170,7 @@ class ShowContainer(show.ShowOne):
self.log.debug('take_action(%s)', parsed_args)
data = lib_container.show_container(
- self.app.restapi,
+ self.app.client_manager.session,
self.app.client_manager.object_store.endpoint,
parsed_args.container,
)
diff --git a/openstackclient/object/v1/lib/container.py b/openstackclient/object/v1/lib/container.py
index bd509555..65a9fe4d 100644
--- a/openstackclient/object/v1/lib/container.py
+++ b/openstackclient/object/v1/lib/container.py
@@ -23,46 +23,46 @@ except ImportError:
def create_container(
- api,
+ session,
url,
container,
):
"""Create a container
- :param api: a restapi object
+ :param session: a restapi object
:param url: endpoint
:param container: name of container to create
:returns: dict of returned headers
"""
- response = api.put("%s/%s" % (url, container))
+ response = session.put("%s/%s" % (url, container))
url_parts = urlparse(url)
data = {
'account': url_parts.path.split('/')[-1],
'container': container,
+ 'x-trans-id': response.headers.get('x-trans-id', None),
}
- data['x-trans-id'] = response.headers.get('x-trans-id', None)
return data
def delete_container(
- api,
+ session,
url,
container,
):
"""Delete a container
- :param api: a restapi object
+ :param session: a restapi object
:param url: endpoint
:param container: name of container to delete
"""
- api.delete("%s/%s" % (url, container))
+ session.delete("%s/%s" % (url, container))
def list_containers(
- api,
+ session,
url,
marker=None,
limit=None,
@@ -72,7 +72,7 @@ def list_containers(
):
"""Get containers in an account
- :param api: a restapi object
+ :param session: a restapi object
:param url: endpoint
:param marker: marker query
:param limit: limit query
@@ -85,7 +85,7 @@ def list_containers(
if full_listing:
data = listing = list_containers(
- api,
+ session,
url,
marker,
limit,
@@ -95,7 +95,7 @@ def list_containers(
while listing:
marker = listing[-1]['name']
listing = list_containers(
- api,
+ session,
url,
marker,
limit,
@@ -117,34 +117,35 @@ def list_containers(
params['end_marker'] = end_marker
if prefix:
params['prefix'] = prefix
- return api.list(url, params=params)
+ return session.get(url, params=params).json()
def show_container(
- api,
+ session,
url,
container,
):
"""Get container details
- :param api: a restapi object
+ :param session: a restapi object
:param url: endpoint
:param container: name of container to show
:returns: dict of returned headers
"""
- response = api.head("%s/%s" % (url, container))
- url_parts = urlparse(url)
+ response = session.head("%s/%s" % (url, container))
data = {
- 'account': url_parts.path.split('/')[-1],
+ 'account': response.headers.get('x-container-meta-owner', None),
'container': container,
+ 'object_count': response.headers.get(
+ 'x-container-object-count',
+ None,
+ ),
+ 'bytes_used': response.headers.get('x-container-bytes-used', None),
+ 'read_acl': response.headers.get('x-container-read', None),
+ 'write_acl': response.headers.get('x-container-write', None),
+ 'sync_to': response.headers.get('x-container-sync-to', None),
+ 'sync_key': response.headers.get('x-container-sync-key', None),
}
- data['object_count'] = response.headers.get(
- 'x-container-object-count', None)
- data['bytes_used'] = response.headers.get('x-container-bytes-used', None)
- data['read_acl'] = response.headers.get('x-container-read', None)
- data['write_acl'] = response.headers.get('x-container-write', None)
- data['sync_to'] = response.headers.get('x-container-sync-to', None)
- data['sync_key'] = response.headers.get('x-container-sync-key', None)
return data
diff --git a/openstackclient/object/v1/lib/object.py b/openstackclient/object/v1/lib/object.py
index 2473caa3..0ded0dad 100644
--- a/openstackclient/object/v1/lib/object.py
+++ b/openstackclient/object/v1/lib/object.py
@@ -25,14 +25,14 @@ except ImportError:
def create_object(
- api,
+ session,
url,
container,
object,
):
"""Create an object, upload it to a container
- :param api: a restapi object
+ :param session: a restapi object
:param url: endpoint
:param container: name of container to store object
:param object: local path to object
@@ -40,38 +40,38 @@ def create_object(
"""
full_url = "%s/%s/%s" % (url, container, object)
- response = api.put(full_url, data=open(object))
+ response = session.put(full_url, data=open(object))
url_parts = urlparse(url)
data = {
'account': url_parts.path.split('/')[-1],
'container': container,
'object': object,
+ 'x-trans-id': response.headers.get('X-Trans-Id', None),
+ 'etag': response.headers.get('Etag', None),
}
- data['x-trans-id'] = response.headers.get('X-Trans-Id', None)
- data['etag'] = response.headers.get('Etag', None)
return data
def delete_object(
- api,
+ session,
url,
container,
object,
):
"""Delete an object stored in a container
- :param api: a restapi object
+ :param session: a restapi object
:param url: endpoint
:param container: name of container that stores object
:param container: name of object to delete
"""
- api.delete("%s/%s/%s" % (url, container, object))
+ session.delete("%s/%s/%s" % (url, container, object))
def list_objects(
- api,
+ session,
url,
container,
marker=None,
@@ -84,7 +84,7 @@ def list_objects(
):
"""Get objects in a container
- :param api: a restapi object
+ :param session: a restapi object
:param url: endpoint
:param container: container name to get a listing for
:param marker: marker query
@@ -101,7 +101,7 @@ def list_objects(
if full_listing:
data = listing = list_objects(
- api,
+ session,
url,
container,
marker,
@@ -117,7 +117,7 @@ def list_objects(
else:
marker = listing[-1]['name']
listing = list_objects(
- api,
+ session,
url,
container,
marker,
@@ -131,7 +131,6 @@ def list_objects(
data.extend(listing)
return data
- object_url = url
params = {
'format': 'json',
}
@@ -147,32 +146,31 @@ def list_objects(
params['prefix'] = prefix
if path:
params['path'] = path
- url = "%s/%s" % (object_url, container)
- return api.list(url, params=params)
+ requrl = "%s/%s" % (url, container)
+ return session.get(requrl, params=params).json()
def show_object(
- api,
+ session,
url,
container,
obj,
):
"""Get object details
- :param api: a restapi object
+ :param session: a restapi object
:param url: endpoint
:param container: container name to get a listing for
:returns: dict of object properties
"""
- response = api.head("%s/%s/%s" % (url, container, obj))
- url_parts = urlparse(url)
+ response = session.head("%s/%s/%s" % (url, container, obj))
data = {
- 'account': url_parts.path.split('/')[-1],
+ 'account': response.headers.get('x-container-meta-owner', None),
'container': container,
'object': obj,
+ 'content-type': response.headers.get('content-type', None),
}
- data['content-type'] = response.headers.get('content-type', None)
if 'content-length' in response.headers:
data['content-length'] = response.headers.get('content-length', None)
if 'last-modified' in response.headers:
@@ -184,10 +182,10 @@ def show_object(
'x-object-manifest', None)
for key, value in six.iteritems(response.headers):
if key.startswith('x-object-meta-'):
- data[key[len('x-object-meta-'):].title()] = value
+ data[key[len('x-object-meta-'):].lower()] = value
elif key not in (
'content-type', 'content-length', 'last-modified',
- 'etag', 'date', 'x-object-manifest'):
- data[key.title()] = value
+ 'etag', 'date', 'x-object-manifest', 'x-container-meta-owner'):
+ data[key.lower()] = value
return data
diff --git a/openstackclient/object/v1/object.py b/openstackclient/object/v1/object.py
index 4a99a8f1..812ad6e1 100644
--- a/openstackclient/object/v1/object.py
+++ b/openstackclient/object/v1/object.py
@@ -50,7 +50,7 @@ class CreateObject(show.ShowOne):
self.log.debug('take_action(%s)', parsed_args)
data = lib_object.create_object(
- self.app.restapi,
+ self.app.client_manager.session,
self.app.client_manager.object_store.endpoint,
parsed_args.container,
parsed_args.object,
@@ -82,7 +82,7 @@ class DeleteObject(command.Command):
self.log.debug('take_action(%s)', parsed_args)
lib_object.delete_object(
- self.app.restapi,
+ self.app.client_manager.session,
self.app.client_manager.object_store.endpoint,
parsed_args.container,
parsed_args.object,
@@ -170,7 +170,7 @@ class ListObject(lister.Lister):
kwargs['full_listing'] = True
data = lib_object.list_objects(
- self.app.restapi,
+ self.app.client_manager.session,
self.app.client_manager.object_store.endpoint,
parsed_args.container,
**kwargs
@@ -206,7 +206,7 @@ class ShowObject(show.ShowOne):
self.log.debug('take_action(%s)', parsed_args)
data = lib_object.show_object(
- self.app.restapi,
+ self.app.client_manager.session,
self.app.client_manager.object_store.endpoint,
parsed_args.container,
parsed_args.object,
diff --git a/openstackclient/openstack/common/gettextutils.py b/openstackclient/openstack/common/gettextutils.py
index 6f573a7f..0c82634b 100644
--- a/openstackclient/openstack/common/gettextutils.py
+++ b/openstackclient/openstack/common/gettextutils.py
@@ -23,7 +23,6 @@ Usual usage in an openstack.common module:
"""
import copy
-import functools
import gettext
import locale
from logging import handlers
@@ -42,7 +41,7 @@ class TranslatorFactory(object):
"""Create translator functions
"""
- def __init__(self, domain, lazy=False, localedir=None):
+ def __init__(self, domain, localedir=None):
"""Establish a set of translation functions for the domain.
:param domain: Name of translation domain,
@@ -55,7 +54,6 @@ class TranslatorFactory(object):
:type localedir: str
"""
self.domain = domain
- self.lazy = lazy
if localedir is None:
localedir = os.environ.get(domain.upper() + '_LOCALEDIR')
self.localedir = localedir
@@ -75,16 +73,19 @@ class TranslatorFactory(object):
"""
if domain is None:
domain = self.domain
- if self.lazy:
- return functools.partial(Message, domain=domain)
- t = gettext.translation(
- domain,
- localedir=self.localedir,
- fallback=True,
- )
- if six.PY3:
- return t.gettext
- return t.ugettext
+ t = gettext.translation(domain,
+ localedir=self.localedir,
+ fallback=True)
+ # Use the appropriate method of the translation object based
+ # on the python version.
+ m = t.gettext if six.PY3 else t.ugettext
+
+ def f(msg):
+ """oslo.i18n.gettextutils translation function."""
+ if USE_LAZY:
+ return Message(msg, domain=domain)
+ return m(msg)
+ return f
@property
def primary(self):
@@ -147,19 +148,11 @@ def enable_lazy():
your project is importing _ directly instead of using the
gettextutils.install() way of importing the _ function.
"""
- # FIXME(dhellmann): This function will be removed in oslo.i18n,
- # because the TranslatorFactory makes it superfluous.
- global _, _LI, _LW, _LE, _LC, USE_LAZY
- tf = TranslatorFactory('openstackclient', lazy=True)
- _ = tf.primary
- _LI = tf.log_info
- _LW = tf.log_warning
- _LE = tf.log_error
- _LC = tf.log_critical
+ global USE_LAZY
USE_LAZY = True
-def install(domain, lazy=False):
+def install(domain):
"""Install a _() function using the given translation domain.
Given a translation domain, install a _() function using gettext's
@@ -170,26 +163,14 @@ def install(domain, lazy=False):
a translation-domain-specific environment variable (e.g.
NOVA_LOCALEDIR).
+ Note that to enable lazy translation, enable_lazy must be
+ called.
+
:param domain: the translation domain
- :param lazy: indicates whether or not to install the lazy _() function.
- The lazy _() introduces a way to do deferred translation
- of messages by installing a _ that builds Message objects,
- instead of strings, which can then be lazily translated into
- any available locale.
"""
- if lazy:
- from six import moves
- tf = TranslatorFactory(domain, lazy=True)
- moves.builtins.__dict__['_'] = tf.primary
- else:
- localedir = '%s_LOCALEDIR' % domain.upper()
- if six.PY3:
- gettext.install(domain,
- localedir=os.environ.get(localedir))
- else:
- gettext.install(domain,
- localedir=os.environ.get(localedir),
- unicode=True)
+ from six import moves
+ tf = TranslatorFactory(domain)
+ moves.builtins.__dict__['_'] = tf.primary
class Message(six.text_type):
@@ -373,8 +354,8 @@ def get_available_languages(domain):
'zh_Hant_HK': 'zh_HK',
'zh_Hant': 'zh_TW',
'fil': 'tl_PH'}
- for (locale, alias) in six.iteritems(aliases):
- if locale in language_list and alias not in language_list:
+ for (locale_, alias) in six.iteritems(aliases):
+ if locale_ in language_list and alias not in language_list:
language_list.append(alias)
_AVAILABLE_LANGUAGES[domain] = language_list
diff --git a/openstackclient/openstack/common/importutils.py b/openstackclient/openstack/common/importutils.py
new file mode 100644
index 00000000..69e8d8f1
--- /dev/null
+++ b/openstackclient/openstack/common/importutils.py
@@ -0,0 +1,73 @@
+# 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.
+
+"""
+Import related utilities and helper functions.
+"""
+
+import sys
+import traceback
+
+
+def import_class(import_str):
+ """Returns a class from a string including module and class."""
+ mod_str, _sep, class_str = import_str.rpartition('.')
+ __import__(mod_str)
+ try:
+ return getattr(sys.modules[mod_str], class_str)
+ except AttributeError:
+ raise ImportError('Class %s cannot be found (%s)' %
+ (class_str,
+ traceback.format_exception(*sys.exc_info())))
+
+
+def import_object(import_str, *args, **kwargs):
+ """Import a class and return an instance of it."""
+ return import_class(import_str)(*args, **kwargs)
+
+
+def import_object_ns(name_space, import_str, *args, **kwargs):
+ """Tries to import object from default namespace.
+
+ Imports a class and return an instance of it, first by trying
+ to find the class in a default namespace, then failing back to
+ a full path if not found in the default namespace.
+ """
+ import_value = "%s.%s" % (name_space, import_str)
+ try:
+ return import_class(import_value)(*args, **kwargs)
+ except ImportError:
+ return import_class(import_str)(*args, **kwargs)
+
+
+def import_module(import_str):
+ """Import a module."""
+ __import__(import_str)
+ return sys.modules[import_str]
+
+
+def import_versioned_module(version, submodule=None):
+ module = 'openstackclient.v%s' % version
+ if submodule:
+ module = '.'.join((module, submodule))
+ return import_module(module)
+
+
+def try_import(import_str, default=None):
+ """Try to import a module and if it fails return default."""
+ try:
+ return import_module(import_str)
+ except ImportError:
+ return default
diff --git a/openstackclient/openstack/common/strutils.py b/openstackclient/openstack/common/strutils.py
index 9d70264f..ad3cb44c 100644
--- a/openstackclient/openstack/common/strutils.py
+++ b/openstackclient/openstack/common/strutils.py
@@ -50,6 +50,39 @@ SLUGIFY_STRIP_RE = re.compile(r"[^\w\s-]")
SLUGIFY_HYPHENATE_RE = re.compile(r"[-\s]+")
+# NOTE(flaper87): The following globals are used by `mask_password`
+_SANITIZE_KEYS = ['adminPass', 'admin_pass', 'password', 'admin_password']
+
+# NOTE(ldbragst): Let's build a list of regex objects using the list of
+# _SANITIZE_KEYS we already have. This way, we only have to add the new key
+# to the list of _SANITIZE_KEYS and we can generate regular expressions
+# for XML and JSON automatically.
+_SANITIZE_PATTERNS_2 = []
+_SANITIZE_PATTERNS_1 = []
+
+# NOTE(amrith): Some regular expressions have only one parameter, some
+# have two parameters. Use different lists of patterns here.
+_FORMAT_PATTERNS_1 = [r'(%(key)s\s*[=]\s*)[^\s^\'^\"]+']
+_FORMAT_PATTERNS_2 = [r'(%(key)s\s*[=]\s*[\"\']).*?([\"\'])',
+ r'(%(key)s\s+[\"\']).*?([\"\'])',
+ r'([-]{2}%(key)s\s+)[^\'^\"^=^\s]+([\s]*)',
+ r'(<%(key)s>).*?(</%(key)s>)',
+ r'([\"\']%(key)s[\"\']\s*:\s*[\"\']).*?([\"\'])',
+ r'([\'"].*?%(key)s[\'"]\s*:\s*u?[\'"]).*?([\'"])',
+ r'([\'"].*?%(key)s[\'"]\s*,\s*\'--?[A-z]+\'\s*,\s*u?'
+ '[\'"]).*?([\'"])',
+ r'(%(key)s\s*--?[A-z]+\s*)\S+(\s*)']
+
+for key in _SANITIZE_KEYS:
+ for pattern in _FORMAT_PATTERNS_2:
+ reg_ex = re.compile(pattern % {'key': key}, re.DOTALL)
+ _SANITIZE_PATTERNS_2.append(reg_ex)
+
+ for pattern in _FORMAT_PATTERNS_1:
+ reg_ex = re.compile(pattern % {'key': key}, re.DOTALL)
+ _SANITIZE_PATTERNS_1.append(reg_ex)
+
+
def int_from_bool_as_string(subject):
"""Interpret a string as a boolean and return either 1 or 0.
@@ -237,3 +270,42 @@ def to_slug(value, incoming=None, errors="strict"):
"ascii", "ignore").decode("ascii")
value = SLUGIFY_STRIP_RE.sub("", value).strip().lower()
return SLUGIFY_HYPHENATE_RE.sub("-", value)
+
+
+def mask_password(message, secret="***"):
+ """Replace password with 'secret' in message.
+
+ :param message: The string which includes security information.
+ :param secret: value with which to replace passwords.
+ :returns: The unicode value of message with the password fields masked.
+
+ For example:
+
+ >>> mask_password("'adminPass' : 'aaaaa'")
+ "'adminPass' : '***'"
+ >>> mask_password("'admin_pass' : 'aaaaa'")
+ "'admin_pass' : '***'"
+ >>> mask_password('"password" : "aaaaa"')
+ '"password" : "***"'
+ >>> mask_password("'original_password' : 'aaaaa'")
+ "'original_password' : '***'"
+ >>> mask_password("u'original_password' : u'aaaaa'")
+ "u'original_password' : u'***'"
+ """
+ message = six.text_type(message)
+
+ # NOTE(ldbragst): Check to see if anything in message contains any key
+ # specified in _SANITIZE_KEYS, if not then just return the message since
+ # we don't have to mask any passwords.
+ if not any(key in message for key in _SANITIZE_KEYS):
+ return message
+
+ substitute = r'\g<1>' + secret + r'\g<2>'
+ for pattern in _SANITIZE_PATTERNS_2:
+ message = re.sub(pattern, substitute, message)
+
+ substitute = r'\g<1>' + secret
+ for pattern in _SANITIZE_PATTERNS_1:
+ message = re.sub(pattern, substitute, message)
+
+ return message
diff --git a/openstackclient/shell.py b/openstackclient/shell.py
index 28724343..24804343 100644
--- a/openstackclient/shell.py
+++ b/openstackclient/shell.py
@@ -31,10 +31,8 @@ import openstackclient
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
KEYRING_SERVICE = 'openstack'
@@ -76,6 +74,8 @@ class OpenStackShell(app.App):
version=openstackclient.__version__,
command_manager=commandmanager.CommandManager('openstack.cli'))
+ self.api_version = {}
+
# Until we have command line arguments parsed, dump any stack traces
self.dump_stack_trace = True
@@ -86,10 +86,14 @@ class OpenStackShell(app.App):
# Assume TLS host certificate verification is enabled
self.verify = True
- # Get list of extension modules
+ # Get list of base modules
self.ext_modules = clientmanager.get_extension_modules(
- 'openstack.cli.extension',
+ 'openstack.cli.base',
)
+ # Append list of extension modules
+ self.ext_modules.extend(clientmanager.get_extension_modules(
+ 'openstack.cli.extension',
+ ))
# Loop through extensions to get parser additions
for mod in self.ext_modules:
@@ -312,23 +316,6 @@ class OpenStackShell(app.App):
help="Print API call timing info",
)
- parser.add_argument(
- '--os-identity-api-version',
- metavar='<identity-api-version>',
- default=env(
- 'OS_IDENTITY_API_VERSION',
- default=identity_client.DEFAULT_IDENTITY_API_VERSION),
- help='Identity API version, default=' +
- identity_client.DEFAULT_IDENTITY_API_VERSION +
- ' (Env: OS_IDENTITY_API_VERSION)')
- parser.add_argument(
- '--os-trust-id',
- metavar='<trust-id>',
- default=utils.env('OS_TRUST_ID'),
- help='Trust ID to use when authenticating. '
- 'This can only be used with Keystone v3 API '
- '(Env: OS_TRUST_ID)')
-
return parser
def authenticate_user(self):
@@ -437,24 +424,19 @@ class OpenStackShell(app.App):
# Save default domain
self.default_domain = self.options.os_default_domain
- # Stash selected API versions for later
- self.api_version = {
- 'identity': self.options.os_identity_api_version,
- }
# Loop through extensions to get API versions
for mod in self.ext_modules:
- ver = getattr(self.options, mod.API_VERSION_OPTION, None)
- if ver:
- self.api_version[mod.API_NAME] = ver
- self.log.debug('%(name)s API version %(version)s',
- {'name': mod.API_NAME, 'version': ver})
-
- # Add the API version-specific commands
- for api in self.api_version.keys():
- version = '.v' + self.api_version[api].replace('.', '_')
- cmd_group = 'openstack.' + api.replace('-', '_') + version
- self.log.debug('command group %s', cmd_group)
- self.command_manager.add_command_group(cmd_group)
+ version_opt = getattr(self.options, mod.API_VERSION_OPTION, None)
+ if version_opt:
+ api = mod.API_NAME
+ self.api_version[api] = version_opt
+ version = '.v' + version_opt.replace('.', '_')
+ cmd_group = 'openstack.' + api.replace('-', '_') + version
+ self.command_manager.add_command_group(cmd_group)
+ self.log.debug(
+ '%(name)s API version %(version)s, cmd group %(group)s',
+ {'name': api, 'version': version_opt, 'group': cmd_group}
+ )
# Commands that span multiple APIs
self.command_manager.add_command_group(
@@ -484,10 +466,6 @@ class OpenStackShell(app.App):
self.verify = self.options.os_cacert
else:
self.verify = not self.options.insecure
- self.restapi = restapi.RESTApi(
- verify=self.verify,
- debug=self.options.debug,
- )
def prepare_to_run_command(self, cmd):
"""Set up auth and API versions"""
@@ -498,12 +476,10 @@ class OpenStackShell(app.App):
if cmd.best_effort:
try:
self.authenticate_user()
- self.restapi.set_auth(self.client_manager.identity.auth_token)
except Exception:
pass
else:
self.authenticate_user()
- self.restapi.set_auth(self.client_manager.identity.auth_token)
return
def clean_up(self, cmd, result, err):
@@ -539,7 +515,6 @@ class OpenStackShell(app.App):
# NOTE(dtroyer): Maintain the old behaviour for interactive use as
# this path does not call prepare_to_run_command()
self.authenticate_user()
- self.restapi.set_auth(self.client_manager.identity.auth_token)
super(OpenStackShell, self).interact()
diff --git a/openstackclient/tests/common/test_clientmanager.py b/openstackclient/tests/common/test_clientmanager.py
index 6aee711d..5a25fa2c 100644
--- a/openstackclient/tests/common/test_clientmanager.py
+++ b/openstackclient/tests/common/test_clientmanager.py
@@ -14,11 +14,26 @@
#
from openstackclient.common import clientmanager
+from openstackclient.common import restapi
from openstackclient.tests import utils
+AUTH_REF = {'a': 1}
AUTH_TOKEN = "foobar"
AUTH_URL = "http://0.0.0.0"
+USERNAME = "itchy"
+PASSWORD = "scratchy"
+SERVICE_CATALOG = {'sc': '123'}
+
+
+def FakeMakeClient(instance):
+ return FakeClient()
+
+
+class FakeClient(object):
+ auth_ref = AUTH_REF
+ auth_token = AUTH_TOKEN
+ service_catalog = SERVICE_CATALOG
class Container(object):
@@ -28,31 +43,96 @@ class Container(object):
pass
+class TestClientCache(utils.TestCase):
+
+ def test_singleton(self):
+ # NOTE(dtroyer): Verify that the ClientCache descriptor only invokes
+ # the factory one time and always returns the same value after that.
+ c = Container()
+ self.assertEqual(c.attr, c.attr)
+
+
class TestClientManager(utils.TestCase):
def setUp(self):
super(TestClientManager, self).setUp()
- api_version = {"identity": "2.0"}
+ clientmanager.ClientManager.identity = \
+ clientmanager.ClientCache(FakeMakeClient)
- self.client_manager = clientmanager.ClientManager(
+ def test_client_manager_token(self):
+
+ client_manager = clientmanager.ClientManager(
token=AUTH_TOKEN,
url=AUTH_URL,
+ verify=True,
+ )
+
+ self.assertEqual(
+ AUTH_TOKEN,
+ client_manager._token,
+ )
+ self.assertEqual(
+ AUTH_URL,
+ client_manager._url,
+ )
+ self.assertIsInstance(
+ client_manager.session,
+ restapi.RESTApi,
+ )
+ self.assertFalse(client_manager._insecure)
+ self.assertTrue(client_manager._verify)
+
+ def test_client_manager_password(self):
+
+ client_manager = clientmanager.ClientManager(
auth_url=AUTH_URL,
- api_version=api_version,
+ username=USERNAME,
+ password=PASSWORD,
+ verify=False,
)
- def test_singleton(self):
- # NOTE(dtroyer): Verify that the ClientCache descriptor only invokes
- # the factory one time and always returns the same value after that.
- c = Container()
- self.assertEqual(c.attr, c.attr)
+ self.assertEqual(
+ AUTH_URL,
+ client_manager._auth_url,
+ )
+ self.assertEqual(
+ USERNAME,
+ client_manager._username,
+ )
+ self.assertEqual(
+ PASSWORD,
+ client_manager._password,
+ )
+ self.assertIsInstance(
+ client_manager.session,
+ restapi.RESTApi,
+ )
+ self.assertTrue(client_manager._insecure)
+ self.assertFalse(client_manager._verify)
- def test_make_client_identity_default(self):
+ # These need to stick around until the old-style clients are gone
+ self.assertEqual(
+ AUTH_REF,
+ client_manager.auth_ref,
+ )
self.assertEqual(
- self.client_manager.identity.auth_token,
AUTH_TOKEN,
+ client_manager._token,
)
self.assertEqual(
- self.client_manager.identity.management_url,
- AUTH_URL,
+ SERVICE_CATALOG,
+ client_manager._service_catalog,
)
+
+ def test_client_manager_password_verify_ca(self):
+
+ client_manager = clientmanager.ClientManager(
+ auth_url=AUTH_URL,
+ username=USERNAME,
+ password=PASSWORD,
+ verify='cafile',
+ )
+
+ self.assertFalse(client_manager._insecure)
+ self.assertTrue(client_manager._verify)
+ self.assertEqual('cafile', client_manager._cacert)
diff --git a/openstackclient/tests/fakes.py b/openstackclient/tests/fakes.py
index fb27ef94..263640ee 100644
--- a/openstackclient/tests/fakes.py
+++ b/openstackclient/tests/fakes.py
@@ -13,9 +13,12 @@
# under the License.
#
+import json
import six
import sys
+import requests
+
AUTH_TOKEN = "foobar"
AUTH_URL = "http://0.0.0.0"
@@ -42,7 +45,6 @@ class FakeApp(object):
self.stdin = sys.stdin
self.stdout = _stdout or sys.stdout
self.stderr = sys.stderr
- self.restapi = None
class FakeClientManager(object):
@@ -53,6 +55,7 @@ class FakeClientManager(object):
self.object = None
self.volume = None
self.network = None
+ self.session = None
self.auth_ref = None
@@ -78,3 +81,15 @@ class FakeResource(object):
k != 'manager')
info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys)
return "<%s %s>" % (self.__class__.__name__, info)
+
+
+class FakeResponse(requests.Response):
+ def __init__(self, headers={}, status_code=200, data=None, encoding=None):
+ super(FakeResponse, self).__init__()
+
+ self.status_code = status_code
+
+ self.headers.update(headers)
+ self._content = json.dumps(data)
+ if not isinstance(self._content, six.binary_type):
+ self._content = self._content.encode()
diff --git a/openstackclient/tests/identity/v3/test_endpoint.py b/openstackclient/tests/identity/v3/test_endpoint.py
index b90ba719..ea05326e 100644
--- a/openstackclient/tests/identity/v3/test_endpoint.py
+++ b/openstackclient/tests/identity/v3/test_endpoint.py
@@ -317,6 +317,102 @@ class TestEndpointList(TestEndpoint):
),)
self.assertEqual(datalist, tuple(data))
+ def test_endpoint_list_service(self):
+ arglist = [
+ '--service', identity_fakes.service_name,
+ ]
+ verifylist = [
+ ('service', identity_fakes.service_name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'service': identity_fakes.service_id,
+ }
+ self.endpoints_mock.list.assert_called_with(**kwargs)
+
+ collist = ('ID', 'Region', 'Service Name', 'Service Type',
+ 'Enabled', 'Interface', 'URL')
+ self.assertEqual(collist, columns)
+ datalist = ((
+ identity_fakes.endpoint_id,
+ identity_fakes.endpoint_region,
+ identity_fakes.service_name,
+ identity_fakes.service_type,
+ True,
+ identity_fakes.endpoint_interface,
+ identity_fakes.endpoint_url,
+ ),)
+ self.assertEqual(datalist, tuple(data))
+
+ def test_endpoint_list_interface(self):
+ arglist = [
+ '--interface', identity_fakes.endpoint_interface,
+ ]
+ verifylist = [
+ ('interface', identity_fakes.endpoint_interface),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'interface': identity_fakes.endpoint_interface,
+ }
+ self.endpoints_mock.list.assert_called_with(**kwargs)
+
+ collist = ('ID', 'Region', 'Service Name', 'Service Type',
+ 'Enabled', 'Interface', 'URL')
+ self.assertEqual(collist, columns)
+ datalist = ((
+ identity_fakes.endpoint_id,
+ identity_fakes.endpoint_region,
+ identity_fakes.service_name,
+ identity_fakes.service_type,
+ True,
+ identity_fakes.endpoint_interface,
+ identity_fakes.endpoint_url,
+ ),)
+ self.assertEqual(datalist, tuple(data))
+
+ def test_endpoint_list_region(self):
+ arglist = [
+ '--region', identity_fakes.endpoint_region,
+ ]
+ verifylist = [
+ ('region', identity_fakes.endpoint_region),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'region': identity_fakes.endpoint_region,
+ }
+ self.endpoints_mock.list.assert_called_with(**kwargs)
+
+ collist = ('ID', 'Region', 'Service Name', 'Service Type',
+ 'Enabled', 'Interface', 'URL')
+ self.assertEqual(collist, columns)
+ datalist = ((
+ identity_fakes.endpoint_id,
+ identity_fakes.endpoint_region,
+ identity_fakes.service_name,
+ identity_fakes.service_type,
+ True,
+ identity_fakes.endpoint_interface,
+ identity_fakes.endpoint_url,
+ ),)
+ self.assertEqual(datalist, tuple(data))
+
class TestEndpointSet(TestEndpoint):
diff --git a/openstackclient/tests/object/v1/lib/test_container.py b/openstackclient/tests/object/v1/lib/test_container.py
index f7355592..ce70b835 100644
--- a/openstackclient/tests/object/v1/lib/test_container.py
+++ b/openstackclient/tests/object/v1/lib/test_container.py
@@ -18,7 +18,7 @@
import mock
from openstackclient.object.v1.lib import container as lib_container
-from openstackclient.tests.common import test_restapi as restapi
+from openstackclient.tests import fakes
from openstackclient.tests.object.v1 import fakes as object_fakes
@@ -39,156 +39,158 @@ class TestContainer(object_fakes.TestObjectv1):
def setUp(self):
super(TestContainer, self).setUp()
- self.app.restapi = mock.MagicMock()
+ self.app.client_manager.session = mock.MagicMock()
class TestContainerList(TestContainer):
def test_container_list_no_options(self):
resp = [{'name': 'is-name'}]
- self.app.restapi.list.return_value = resp
+ self.app.client_manager.session.get().json.return_value = resp
data = lib_container.list_containers(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
)
# Check expected values
- self.app.restapi.list.assert_called_with(
+ self.app.client_manager.session.get.assert_called_with(
fake_url,
params={
'format': 'json',
}
)
- self.assertEqual(data, resp)
+ self.assertEqual(resp, data)
def test_container_list_marker(self):
resp = [{'name': 'is-name'}]
- self.app.restapi.list.return_value = resp
+ self.app.client_manager.session.get().json.return_value = resp
data = lib_container.list_containers(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
marker='next',
)
# Check expected values
- self.app.restapi.list.assert_called_with(
+ self.app.client_manager.session.get.assert_called_with(
fake_url,
params={
'format': 'json',
'marker': 'next',
}
)
- self.assertEqual(data, resp)
+ self.assertEqual(resp, data)
def test_container_list_limit(self):
resp = [{'name': 'is-name'}]
- self.app.restapi.list.return_value = resp
+ self.app.client_manager.session.get().json.return_value = resp
data = lib_container.list_containers(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
limit=5,
)
# Check expected values
- self.app.restapi.list.assert_called_with(
+ self.app.client_manager.session.get.assert_called_with(
fake_url,
params={
'format': 'json',
'limit': 5,
}
)
- self.assertEqual(data, resp)
+ self.assertEqual(resp, data)
def test_container_list_end_marker(self):
resp = [{'name': 'is-name'}]
- self.app.restapi.list.return_value = resp
+ self.app.client_manager.session.get().json.return_value = resp
data = lib_container.list_containers(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
end_marker='last',
)
# Check expected values
- self.app.restapi.list.assert_called_with(
+ self.app.client_manager.session.get.assert_called_with(
fake_url,
params={
'format': 'json',
'end_marker': 'last',
}
)
- self.assertEqual(data, resp)
+ self.assertEqual(resp, data)
def test_container_list_prefix(self):
resp = [{'name': 'is-name'}]
- self.app.restapi.list.return_value = resp
+ self.app.client_manager.session.get().json.return_value = resp
data = lib_container.list_containers(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
prefix='foo/',
)
# Check expected values
- self.app.restapi.list.assert_called_with(
+ self.app.client_manager.session.get.assert_called_with(
fake_url,
params={
'format': 'json',
'prefix': 'foo/',
}
)
- self.assertEqual(data, resp)
+ self.assertEqual(resp, data)
def test_container_list_full_listing(self):
+ sess = self.app.client_manager.session
def side_effect(*args, **kwargs):
- rv = self.app.restapi.list.return_value
- self.app.restapi.list.return_value = []
- self.app.restapi.list.side_effect = None
+ rv = sess.get().json.return_value
+ sess.get().json.return_value = []
+ sess.get().json.side_effect = None
return rv
resp = [{'name': 'is-name'}]
- self.app.restapi.list.return_value = resp
- self.app.restapi.list.side_effect = side_effect
+ sess.get().json.return_value = resp
+ sess.get().json.side_effect = side_effect
data = lib_container.list_containers(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
full_listing=True,
)
# Check expected values
- self.app.restapi.list.assert_called_with(
+ sess.get.assert_called_with(
fake_url,
params={
'format': 'json',
'marker': 'is-name',
}
)
- self.assertEqual(data, resp)
+ self.assertEqual(resp, data)
class TestContainerShow(TestContainer):
def test_container_show_no_options(self):
resp = {
+ 'X-Container-Meta-Owner': fake_account,
'x-container-object-count': 1,
'x-container-bytes-used': 577,
}
- self.app.restapi.head.return_value = \
- restapi.FakeResponse(headers=resp)
+ self.app.client_manager.session.head.return_value = \
+ fakes.FakeResponse(headers=resp)
data = lib_container.show_container(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
'is-name',
)
# Check expected values
- self.app.restapi.head.assert_called_with(
+ self.app.client_manager.session.head.assert_called_with(
fake_url + '/is-name',
)
@@ -202,4 +204,4 @@ class TestContainerShow(TestContainer):
'sync_to': None,
'sync_key': None,
}
- self.assertEqual(data, data_expected)
+ self.assertEqual(data_expected, data)
diff --git a/openstackclient/tests/object/v1/lib/test_object.py b/openstackclient/tests/object/v1/lib/test_object.py
index 064efb53..f96732b4 100644
--- a/openstackclient/tests/object/v1/lib/test_object.py
+++ b/openstackclient/tests/object/v1/lib/test_object.py
@@ -18,7 +18,7 @@
import mock
from openstackclient.object.v1.lib import object as lib_object
-from openstackclient.tests.common import test_restapi as restapi
+from openstackclient.tests import fakes
from openstackclient.tests.object.v1 import fakes as object_fakes
@@ -40,99 +40,99 @@ class TestObject(object_fakes.TestObjectv1):
def setUp(self):
super(TestObject, self).setUp()
- self.app.restapi = mock.MagicMock()
+ self.app.client_manager.session = mock.MagicMock()
class TestObjectListObjects(TestObject):
def test_list_objects_no_options(self):
resp = [{'name': 'is-name'}]
- self.app.restapi.list.return_value = resp
+ self.app.client_manager.session.get().json.return_value = resp
data = lib_object.list_objects(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
fake_container,
)
# Check expected values
- self.app.restapi.list.assert_called_with(
+ self.app.client_manager.session.get.assert_called_with(
fake_url + '/' + fake_container,
params={
'format': 'json',
}
)
- self.assertEqual(data, resp)
+ self.assertEqual(resp, data)
def test_list_objects_marker(self):
resp = [{'name': 'is-name'}]
- self.app.restapi.list.return_value = resp
+ self.app.client_manager.session.get().json.return_value = resp
data = lib_object.list_objects(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
fake_container,
marker='next',
)
# Check expected values
- self.app.restapi.list.assert_called_with(
+ self.app.client_manager.session.get.assert_called_with(
fake_url + '/' + fake_container,
params={
'format': 'json',
'marker': 'next',
}
)
- self.assertEqual(data, resp)
+ self.assertEqual(resp, data)
def test_list_objects_limit(self):
resp = [{'name': 'is-name'}]
- self.app.restapi.list.return_value = resp
+ self.app.client_manager.session.get().json.return_value = resp
data = lib_object.list_objects(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
fake_container,
limit=5,
)
# Check expected values
- self.app.restapi.list.assert_called_with(
+ self.app.client_manager.session.get.assert_called_with(
fake_url + '/' + fake_container,
params={
'format': 'json',
'limit': 5,
}
)
- self.assertEqual(data, resp)
+ self.assertEqual(resp, data)
def test_list_objects_end_marker(self):
resp = [{'name': 'is-name'}]
- self.app.restapi.list.return_value = resp
+ self.app.client_manager.session.get().json.return_value = resp
data = lib_object.list_objects(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
fake_container,
end_marker='last',
)
# Check expected values
- self.app.restapi.list.assert_called_with(
+ self.app.client_manager.session.get.assert_called_with(
fake_url + '/' + fake_container,
params={
'format': 'json',
'end_marker': 'last',
}
)
- self.assertEqual(data, resp)
+ self.assertEqual(resp, data)
def test_list_objects_delimiter(self):
resp = [{'name': 'is-name'}]
- self.app.restapi.list.return_value = resp
+ self.app.client_manager.session.get().json.return_value = resp
data = lib_object.list_objects(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
fake_container,
delimiter='|',
@@ -142,85 +142,86 @@ class TestObjectListObjects(TestObject):
# NOTE(dtroyer): requests handles the URL encoding and we're
# mocking that so use the otherwise-not-legal
# pipe '|' char in the response.
- self.app.restapi.list.assert_called_with(
+ self.app.client_manager.session.get.assert_called_with(
fake_url + '/' + fake_container,
params={
'format': 'json',
'delimiter': '|',
}
)
- self.assertEqual(data, resp)
+ self.assertEqual(resp, data)
def test_list_objects_prefix(self):
resp = [{'name': 'is-name'}]
- self.app.restapi.list.return_value = resp
+ self.app.client_manager.session.get().json.return_value = resp
data = lib_object.list_objects(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
fake_container,
prefix='foo/',
)
# Check expected values
- self.app.restapi.list.assert_called_with(
+ self.app.client_manager.session.get.assert_called_with(
fake_url + '/' + fake_container,
params={
'format': 'json',
'prefix': 'foo/',
}
)
- self.assertEqual(data, resp)
+ self.assertEqual(resp, data)
def test_list_objects_path(self):
resp = [{'name': 'is-name'}]
- self.app.restapi.list.return_value = resp
+ self.app.client_manager.session.get().json.return_value = resp
data = lib_object.list_objects(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
fake_container,
path='next',
)
# Check expected values
- self.app.restapi.list.assert_called_with(
+ self.app.client_manager.session.get.assert_called_with(
fake_url + '/' + fake_container,
params={
'format': 'json',
'path': 'next',
}
)
- self.assertEqual(data, resp)
+ self.assertEqual(resp, data)
def test_list_objects_full_listing(self):
+ sess = self.app.client_manager.session
def side_effect(*args, **kwargs):
- rv = self.app.restapi.list.return_value
- self.app.restapi.list.return_value = []
- self.app.restapi.list.side_effect = None
+ rv = sess.get().json.return_value
+ sess.get().json.return_value = []
+ sess.get().json.side_effect = None
return rv
resp = [{'name': 'is-name'}]
- self.app.restapi.list.return_value = resp
- self.app.restapi.list.side_effect = side_effect
+ sess.get().json.return_value = resp
+ sess.get().json.side_effect = side_effect
data = lib_object.list_objects(
- self.app.restapi,
+ sess,
fake_url,
fake_container,
full_listing=True,
)
# Check expected values
- self.app.restapi.list.assert_called_with(
+ sess.get.assert_called_with(
fake_url + '/' + fake_container,
params={
'format': 'json',
'marker': 'is-name',
}
)
- self.assertEqual(data, resp)
+ self.assertEqual(resp, data)
class TestObjectShowObjects(TestObject):
@@ -228,19 +229,20 @@ class TestObjectShowObjects(TestObject):
def test_object_show_no_options(self):
resp = {
'content-type': 'text/alpha',
+ 'x-container-meta-owner': fake_account,
}
- self.app.restapi.head.return_value = \
- restapi.FakeResponse(headers=resp)
+ self.app.client_manager.session.head.return_value = \
+ fakes.FakeResponse(headers=resp)
data = lib_object.show_object(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
fake_container,
fake_object,
)
# Check expected values
- self.app.restapi.head.assert_called_with(
+ self.app.client_manager.session.head.assert_called_with(
fake_url + '/%s/%s' % (fake_container, fake_object),
)
@@ -250,7 +252,7 @@ class TestObjectShowObjects(TestObject):
'object': fake_object,
'content-type': 'text/alpha',
}
- self.assertEqual(data, data_expected)
+ self.assertEqual(data_expected, data)
def test_object_show_all_options(self):
resp = {
@@ -258,22 +260,23 @@ class TestObjectShowObjects(TestObject):
'content-length': 577,
'last-modified': '20130101',
'etag': 'qaz',
+ 'x-container-meta-owner': fake_account,
'x-object-manifest': None,
'x-object-meta-wife': 'Wilma',
'x-tra-header': 'yabba-dabba-do',
}
- self.app.restapi.head.return_value = \
- restapi.FakeResponse(headers=resp)
+ self.app.client_manager.session.head.return_value = \
+ fakes.FakeResponse(headers=resp)
data = lib_object.show_object(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
fake_container,
fake_object,
)
# Check expected values
- self.app.restapi.head.assert_called_with(
+ self.app.client_manager.session.head.assert_called_with(
fake_url + '/%s/%s' % (fake_container, fake_object),
)
@@ -286,7 +289,7 @@ class TestObjectShowObjects(TestObject):
'last-modified': '20130101',
'etag': 'qaz',
'x-object-manifest': None,
- 'Wife': 'Wilma',
- 'X-Tra-Header': 'yabba-dabba-do',
+ 'wife': 'Wilma',
+ 'x-tra-header': 'yabba-dabba-do',
}
- self.assertEqual(data, data_expected)
+ self.assertEqual(data_expected, data)
diff --git a/openstackclient/tests/object/v1/test_container.py b/openstackclient/tests/object/v1/test_container.py
index 4afb1006..b72c79d6 100644
--- a/openstackclient/tests/object/v1/test_container.py
+++ b/openstackclient/tests/object/v1/test_container.py
@@ -77,7 +77,7 @@ class TestContainerList(TestObject):
kwargs = {
}
c_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
**kwargs
)
@@ -113,7 +113,7 @@ class TestContainerList(TestObject):
'prefix': 'bit',
}
c_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
**kwargs
)
@@ -148,7 +148,7 @@ class TestContainerList(TestObject):
'marker': object_fakes.container_name,
}
c_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
**kwargs
)
@@ -183,7 +183,7 @@ class TestContainerList(TestObject):
'end_marker': object_fakes.container_name_3,
}
c_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
**kwargs
)
@@ -218,7 +218,7 @@ class TestContainerList(TestObject):
'limit': 2,
}
c_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
**kwargs
)
@@ -252,7 +252,7 @@ class TestContainerList(TestObject):
kwargs = {
}
c_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
**kwargs
)
@@ -296,7 +296,7 @@ class TestContainerList(TestObject):
'full_listing': True,
}
c_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
**kwargs
)
@@ -341,7 +341,7 @@ class TestContainerShow(TestObject):
}
# lib.container.show_container(api, url, container)
c_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name,
**kwargs
diff --git a/openstackclient/tests/object/v1/test_object.py b/openstackclient/tests/object/v1/test_object.py
index bea0d270..26d07b2c 100644
--- a/openstackclient/tests/object/v1/test_object.py
+++ b/openstackclient/tests/object/v1/test_object.py
@@ -71,7 +71,7 @@ class TestObjectList(TestObject):
columns, data = self.cmd.take_action(parsed_args)
o_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name,
)
@@ -107,7 +107,7 @@ class TestObjectList(TestObject):
'prefix': 'floppy',
}
o_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name_2,
**kwargs
@@ -143,7 +143,7 @@ class TestObjectList(TestObject):
'delimiter': '=',
}
o_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name_2,
**kwargs
@@ -179,7 +179,7 @@ class TestObjectList(TestObject):
'marker': object_fakes.object_name_2,
}
o_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name_2,
**kwargs
@@ -215,7 +215,7 @@ class TestObjectList(TestObject):
'end_marker': object_fakes.object_name_2,
}
o_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name_2,
**kwargs
@@ -251,7 +251,7 @@ class TestObjectList(TestObject):
'limit': 2,
}
o_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name_2,
**kwargs
@@ -287,7 +287,7 @@ class TestObjectList(TestObject):
kwargs = {
}
o_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name,
**kwargs
@@ -337,7 +337,7 @@ class TestObjectList(TestObject):
'full_listing': True,
}
o_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name,
**kwargs
@@ -384,7 +384,7 @@ class TestObjectShow(TestObject):
}
# lib.container.show_container(api, url, container)
c_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name,
object_fakes.object_name_1,
diff --git a/setup.cfg b/setup.cfg
index b1b80a06..17bf62b2 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -30,8 +30,9 @@ console_scripts =
openstack.cli =
module_list = openstackclient.common.module:ListModule
-openstack.cli.extension =
+openstack.cli.base =
compute = openstackclient.compute.client
+ identity = openstackclient.identity.client
image = openstackclient.image.client
network = openstackclient.network.client
object_store = openstackclient.object.client