summaryrefslogtreecommitdiff
path: root/openstackclient
diff options
context:
space:
mode:
Diffstat (limited to 'openstackclient')
-rw-r--r--openstackclient/api/auth.py166
-rw-r--r--openstackclient/common/clientmanager.py24
-rw-r--r--openstackclient/common/limits.py29
-rw-r--r--openstackclient/common/quota.py12
-rw-r--r--openstackclient/common/session.py50
-rw-r--r--openstackclient/common/timing.py10
-rw-r--r--openstackclient/compute/client.py20
-rw-r--r--openstackclient/compute/v2/server.py11
-rw-r--r--openstackclient/identity/common.py59
-rw-r--r--openstackclient/identity/v3/group.py7
-rw-r--r--openstackclient/identity/v3/project.py3
-rw-r--r--openstackclient/identity/v3/role.py81
-rw-r--r--openstackclient/identity/v3/role_assignment.py17
-rw-r--r--openstackclient/identity/v3/trust.py9
-rw-r--r--openstackclient/identity/v3/user.py9
-rw-r--r--openstackclient/image/v1/image.py2
-rw-r--r--openstackclient/shell.py115
-rw-r--r--openstackclient/tests/common/test_clientmanager.py87
-rw-r--r--openstackclient/tests/common/test_timing.py17
-rw-r--r--openstackclient/tests/test_shell.py427
-rw-r--r--openstackclient/tests/volume/test_find_resource.py7
-rw-r--r--openstackclient/volume/client.py20
22 files changed, 702 insertions, 480 deletions
diff --git a/openstackclient/api/auth.py b/openstackclient/api/auth.py
index ba51bee1..9fb26e71 100644
--- a/openstackclient/api/auth.py
+++ b/openstackclient/api/auth.py
@@ -27,29 +27,49 @@ from openstackclient.i18n import _
LOG = logging.getLogger(__name__)
-
# Initialize the list of Authentication plugins early in order
# to get the command-line options
-PLUGIN_LIST = stevedore.ExtensionManager(
- base.PLUGIN_NAMESPACE,
- invoke_on_load=False,
- propagate_map_exceptions=True,
-)
+PLUGIN_LIST = None
-# Get the command line options so the help action has them available
+# List of plugin command line options
OPTIONS_LIST = {}
-for plugin in PLUGIN_LIST:
- for o in plugin.plugin.get_options():
- os_name = o.dest.lower().replace('_', '-')
- os_env_name = 'OS_' + os_name.upper().replace('-', '_')
- OPTIONS_LIST.setdefault(os_name, {'env': os_env_name, 'help': ''})
- # TODO(mhu) simplistic approach, would be better to only add
- # help texts if they vary from one auth plugin to another
- # also the text rendering is ugly in the CLI ...
- OPTIONS_LIST[os_name]['help'] += 'With %s: %s\n' % (
- plugin.name,
- o.help,
+
+
+def get_plugin_list():
+ """Gather plugin list and cache it"""
+
+ global PLUGIN_LIST
+
+ if PLUGIN_LIST is None:
+ PLUGIN_LIST = stevedore.ExtensionManager(
+ base.PLUGIN_NAMESPACE,
+ invoke_on_load=False,
+ propagate_map_exceptions=True,
)
+ return PLUGIN_LIST
+
+
+def get_options_list():
+ """Gather plugin options so the help action has them available"""
+
+ global OPTIONS_LIST
+
+ if not OPTIONS_LIST:
+ for plugin in get_plugin_list():
+ for o in plugin.plugin.get_options():
+ os_name = o.dest.lower().replace('_', '-')
+ os_env_name = 'OS_' + os_name.upper().replace('-', '_')
+ OPTIONS_LIST.setdefault(
+ os_name, {'env': os_env_name, 'help': ''},
+ )
+ # TODO(mhu) simplistic approach, would be better to only add
+ # help texts if they vary from one auth plugin to another
+ # also the text rendering is ugly in the CLI ...
+ OPTIONS_LIST[os_name]['help'] += 'With %s: %s\n' % (
+ plugin.name,
+ o.help,
+ )
+ return OPTIONS_LIST
def select_auth_plugin(options):
@@ -57,25 +77,27 @@ def select_auth_plugin(options):
auth_plugin_name = None
- if options.os_auth_type in [plugin.name for plugin in PLUGIN_LIST]:
- # A direct plugin name was given, use it
- return options.os_auth_type
-
- if options.os_url and options.os_token:
+ # Do the token/url check first as this must override the default
+ # 'password' set by os-client-config
+ # Also, url and token are not copied into o-c-c's auth dict (yet?)
+ if options.auth.get('url', None) and options.auth.get('token', None):
# service token authentication
auth_plugin_name = 'token_endpoint'
- elif options.os_username:
- if options.os_identity_api_version == '3':
+ elif options.auth_type in [plugin.name for plugin in PLUGIN_LIST]:
+ # A direct plugin name was given, use it
+ auth_plugin_name = options.auth_type
+ elif options.auth.get('username', None):
+ if options.identity_api_version == '3':
auth_plugin_name = 'v3password'
- elif options.os_identity_api_version == '2.0':
+ elif options.identity_api_version.startswith('2'):
auth_plugin_name = 'v2password'
else:
# let keystoneclient figure it out itself
auth_plugin_name = 'osc_password'
- elif options.os_token:
- if options.os_identity_api_version == '3':
+ elif options.auth.get('token', None):
+ if options.identity_api_version == '3':
auth_plugin_name = 'v3token'
- elif options.os_identity_api_version == '2.0':
+ elif options.identity_api_version.startswith('2'):
auth_plugin_name = 'v2token'
else:
# let keystoneclient figure it out itself
@@ -89,35 +111,27 @@ def select_auth_plugin(options):
def build_auth_params(auth_plugin_name, cmd_options):
- auth_params = {}
+
+ auth_params = dict(cmd_options.auth)
if auth_plugin_name:
LOG.debug('auth_type: %s', auth_plugin_name)
auth_plugin_class = base.get_plugin_class(auth_plugin_name)
- plugin_options = auth_plugin_class.get_options()
- for option in plugin_options:
- option_name = 'os_' + option.dest
- LOG.debug('fetching option %s' % option_name)
- auth_params[option.dest] = getattr(cmd_options, option_name, None)
# grab tenant from project for v2.0 API compatibility
if auth_plugin_name.startswith("v2"):
- auth_params['tenant_id'] = getattr(
- cmd_options,
- 'os_project_id',
- None,
- )
- auth_params['tenant_name'] = getattr(
- cmd_options,
- 'os_project_name',
- None,
- )
+ if 'project_id' in auth_params:
+ auth_params['tenant_id'] = auth_params['project_id']
+ del auth_params['project_id']
+ if 'project_name' in auth_params:
+ auth_params['tenant_name'] = auth_params['project_name']
+ del auth_params['project_name']
else:
LOG.debug('no auth_type')
# delay the plugin choice, grab every option
- plugin_options = set([o.replace('-', '_') for o in OPTIONS_LIST])
+ auth_plugin_class = None
+ plugin_options = set([o.replace('-', '_') for o in get_options_list()])
for option in plugin_options:
- option_name = 'os_' + option
- LOG.debug('fetching option %s' % option_name)
- auth_params[option] = getattr(cmd_options, option_name, None)
+ LOG.debug('fetching option %s' % option)
+ auth_params[option] = getattr(cmd_options.auth, option, None)
return (auth_plugin_class, auth_params)
@@ -126,15 +140,29 @@ def check_valid_auth_options(options, auth_plugin_name):
msg = ''
if auth_plugin_name.endswith('password'):
- if not options.os_username:
- msg += _('Set a username with --os-username or OS_USERNAME\n')
- if not options.os_auth_url:
- msg += _('Set an authentication URL, with --os-auth-url or'
- ' OS_AUTH_URL\n')
- if (not options.os_project_id and not options.os_domain_id and not
- options.os_domain_name and not options.os_project_name):
+ if not options.auth.get('username', None):
+ msg += _('Set a username with --os-username, OS_USERNAME,'
+ ' or auth.username\n')
+ if not options.auth.get('auth_url', None):
+ msg += _('Set an authentication URL, with --os-auth-url,'
+ ' OS_AUTH_URL or auth.auth_url\n')
+ if (not options.auth.get('project_id', None) and not
+ options.auth.get('domain_id', None) and not
+ options.auth.get('domain_name', None) and not
+ options.auth.get('project_name', None)):
msg += _('Set a scope, such as a project or domain, with '
- '--os-project-name or OS_PROJECT_NAME')
+ '--os-project-name, OS_PROJECT_NAME or auth.project_name')
+ elif auth_plugin_name.endswith('token'):
+ if not options.auth.get('token', None):
+ msg += _('Set a token with --os-token, OS_TOKEN or auth.token\n')
+ if not options.auth.get('auth_url', None):
+ msg += _('Set a service AUTH_URL, with --os-auth-url, '
+ 'OS_AUTH_URL or auth.auth_url\n')
+ elif auth_plugin_name == 'token_endpoint':
+ if not options.auth.get('token', None):
+ msg += _('Set a token with --os-token, OS_TOKEN or auth.token\n')
+ if not options.auth.get('url', None):
+ msg += _('Set a service URL, with --os-url, OS_URL or auth.url\n')
if msg:
raise exc.CommandError('Missing parameter(s): \n%s' % msg)
@@ -147,10 +175,11 @@ def build_auth_plugins_option_parser(parser):
authentication plugin.
"""
- available_plugins = [plugin.name for plugin in PLUGIN_LIST]
+ available_plugins = [plugin.name for plugin in get_plugin_list()]
parser.add_argument(
'--os-auth-type',
metavar='<auth-type>',
+ dest='auth_type',
default=utils.env('OS_AUTH_TYPE'),
help='Select an auhentication type. Available types: ' +
', '.join(available_plugins) +
@@ -158,7 +187,7 @@ def build_auth_plugins_option_parser(parser):
' (Env: OS_AUTH_TYPE)',
choices=available_plugins
)
- # make sure we catch old v2.0 env values
+ # Maintain compatibility with old tenant env vars
envs = {
'OS_PROJECT_NAME': utils.env(
'OS_PROJECT_NAME',
@@ -169,16 +198,21 @@ def build_auth_plugins_option_parser(parser):
default=utils.env('OS_TENANT_ID')
),
}
- for o in OPTIONS_LIST:
- # remove allusion to tenants from v2.0 API
+ for o in get_options_list():
+ # Remove tenant options from KSC plugins and replace them below
if 'tenant' not in o:
parser.add_argument(
'--os-' + o,
metavar='<auth-%s>' % o,
- default=envs.get(OPTIONS_LIST[o]['env'],
- utils.env(OPTIONS_LIST[o]['env'])),
- help='%s\n(Env: %s)' % (OPTIONS_LIST[o]['help'],
- OPTIONS_LIST[o]['env']),
+ dest=o.replace('-', '_'),
+ default=envs.get(
+ OPTIONS_LIST[o]['env'],
+ utils.env(OPTIONS_LIST[o]['env']),
+ ),
+ help='%s\n(Env: %s)' % (
+ OPTIONS_LIST[o]['help'],
+ OPTIONS_LIST[o]['env'],
+ ),
)
# add tenant-related options for compatibility
# this is deprecated but still used in some tempest tests...
@@ -186,14 +220,12 @@ def build_auth_plugins_option_parser(parser):
'--os-tenant-name',
metavar='<auth-tenant-name>',
dest='os_project_name',
- default=utils.env('OS_TENANT_NAME'),
help=argparse.SUPPRESS,
)
parser.add_argument(
'--os-tenant-id',
metavar='<auth-tenant-id>',
dest='os_project_id',
- default=utils.env('OS_TENANT_ID'),
help=argparse.SUPPRESS,
)
return parser
diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py
index 10f38c25..ca5ece0d 100644
--- a/openstackclient/common/clientmanager.py
+++ b/openstackclient/common/clientmanager.py
@@ -19,10 +19,10 @@ import logging
import pkg_resources
import sys
-from keystoneclient import session
import requests
from openstackclient.api import auth
+from openstackclient.common import session as osc_session
from openstackclient.identity import client as identity_client
@@ -58,7 +58,7 @@ class ClientManager(object):
def __init__(
self,
- cli_options,
+ cli_options=None,
api_version=None,
verify=True,
pw_func=None,
@@ -82,8 +82,8 @@ class ClientManager(object):
self._cli_options = cli_options
self._api_version = api_version
self._pw_callback = pw_func
- self._url = self._cli_options.os_url
- self._region_name = self._cli_options.os_region_name
+ self._url = self._cli_options.auth.get('url', None)
+ self._region_name = self._cli_options.region_name
self.timing = self._cli_options.timing
@@ -121,7 +121,7 @@ class ClientManager(object):
# Horrible hack alert...must handle prompt for null password if
# password auth is requested.
if (self.auth_plugin_name.endswith('password') and
- not self._cli_options.os_password):
+ not self._cli_options.auth.get('password', None)):
self._cli_options.os_password = self._pw_callback()
(auth_plugin, self._auth_params) = auth.build_auth_params(
@@ -129,13 +129,15 @@ class ClientManager(object):
self._cli_options,
)
- default_domain = self._cli_options.os_default_domain
+ # TODO(mordred): This is a usability improvement that's broadly useful
+ # We should port it back up into os-client-config.
+ default_domain = self._cli_options.default_domain
# NOTE(stevemar): If PROJECT_DOMAIN_ID or PROJECT_DOMAIN_NAME is
# present, then do not change the behaviour. Otherwise, set the
# PROJECT_DOMAIN_ID to 'OS_DEFAULT_DOMAIN' for better usability.
if (self._api_version.get('identity') == '3' and
- not self._auth_params.get('project_domain_id') and
- not self._auth_params.get('project_domain_name')):
+ not self._auth_params.get('project_domain_id', None) and
+ not self._auth_params.get('project_domain_name', None)):
self._auth_params['project_domain_id'] = default_domain
# NOTE(stevemar): If USER_DOMAIN_ID or USER_DOMAIN_NAME is present,
@@ -143,8 +145,8 @@ class ClientManager(object):
# to 'OS_DEFAULT_DOMAIN' for better usability.
if (self._api_version.get('identity') == '3' and
self.auth_plugin_name.endswith('password') and
- not self._auth_params.get('user_domain_id') and
- not self._auth_params.get('user_domain_name')):
+ not self._auth_params.get('user_domain_id', None) and
+ not self._auth_params.get('user_domain_name', None)):
self._auth_params['user_domain_id'] = default_domain
# For compatibility until all clients can be updated
@@ -157,7 +159,7 @@ class ClientManager(object):
self.auth = auth_plugin.load_from_options(**self._auth_params)
# needed by SAML authentication
request_session = requests.session()
- self.session = session.Session(
+ self.session = osc_session.TimingSession(
auth=self.auth,
session=request_session,
verify=self._verify,
diff --git a/openstackclient/common/limits.py b/openstackclient/common/limits.py
index 9c9458ab..4abcf169 100644
--- a/openstackclient/common/limits.py
+++ b/openstackclient/common/limits.py
@@ -21,6 +21,7 @@ import logging
from cliff import lister
from openstackclient.common import utils
+from openstackclient.identity import common as identity_common
class ShowLimits(lister.Lister):
@@ -49,6 +50,18 @@ class ShowLimits(lister.Lister):
action="store_true",
default=False,
help="Include reservations count [only valid with --absolute]")
+ parser.add_argument(
+ '--project',
+ metavar='<project>',
+ help='Show limits for a specific project (name or ID)'
+ ' [only valid with --absolute]',
+ )
+ parser.add_argument(
+ '--domain',
+ metavar='<domain>',
+ help='Domain that owns --project (name or ID)'
+ ' [only valid with --absolute]',
+ )
return parser
def take_action(self, parsed_args):
@@ -57,7 +70,21 @@ class ShowLimits(lister.Lister):
compute_client = self.app.client_manager.compute
volume_client = self.app.client_manager.volume
- compute_limits = compute_client.limits.get(parsed_args.is_reserved)
+ project_id = None
+ if parsed_args.project is not None:
+ identity_client = self.app.client_manager.identity
+ if parsed_args.domain is not None:
+ domain = identity_common.find_domain(identity_client,
+ parsed_args.domain)
+ project_id = utils.find_resource(identity_client.projects,
+ parsed_args.project,
+ domain_id=domain.id).id
+ else:
+ project_id = utils.find_resource(identity_client.projects,
+ parsed_args.project).id
+
+ compute_limits = compute_client.limits.get(parsed_args.is_reserved,
+ tenant_id=project_id)
volume_limits = volume_client.limits.get()
if parsed_args.is_absolute:
diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py
index dde4a9ac..ea1dc38f 100644
--- a/openstackclient/common/quota.py
+++ b/openstackclient/common/quota.py
@@ -82,6 +82,11 @@ class SetQuota(command.Command):
type=int,
help='New value for the %s quota' % v,
)
+ parser.add_argument(
+ '--volume-type',
+ metavar='<volume-type>',
+ help='Set quotas for a specific <volume-type>',
+ )
return parser
def take_action(self, parsed_args):
@@ -97,8 +102,11 @@ class SetQuota(command.Command):
volume_kwargs = {}
for k, v in VOLUME_QUOTAS.items():
- if v in parsed_args:
- volume_kwargs[k] = getattr(parsed_args, v, None)
+ value = getattr(parsed_args, v, None)
+ if value is not None:
+ if parsed_args.volume_type:
+ k = k + '_%s' % parsed_args.volume_type
+ volume_kwargs[k] = value
if compute_kwargs == {} and volume_kwargs == {}:
sys.stderr.write("No quotas updated")
diff --git a/openstackclient/common/session.py b/openstackclient/common/session.py
new file mode 100644
index 00000000..dda1c417
--- /dev/null
+++ b/openstackclient/common/session.py
@@ -0,0 +1,50 @@
+# 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.
+#
+
+"""Subclass of keystoneclient.session"""
+
+from keystoneclient import session
+
+
+class TimingSession(session.Session):
+ """A Session that supports collection of timing data per Method URL"""
+
+ def __init__(
+ self,
+ **kwargs
+ ):
+ """Pass through all arguments except timing"""
+ super(TimingSession, self).__init__(**kwargs)
+
+ # times is a list of tuples: ("method url", elapsed_time)
+ self.times = []
+
+ def get_timings(self):
+ return self.times
+
+ def reset_timings(self):
+ self.times = []
+
+ def request(self, url, method, **kwargs):
+ """Wrap the usual request() method with the timers"""
+ resp = super(TimingSession, self).request(url, method, **kwargs)
+ for h in resp.history:
+ self.times.append((
+ "%s %s" % (h.request.method, h.request.url),
+ h.elapsed,
+ ))
+ self.times.append((
+ "%s %s" % (resp.request.method, resp.request.url),
+ resp.elapsed,
+ ))
+ return resp
diff --git a/openstackclient/common/timing.py b/openstackclient/common/timing.py
index 1c94682c..d13c86e7 100644
--- a/openstackclient/common/timing.py
+++ b/openstackclient/common/timing.py
@@ -33,10 +33,12 @@ class Timing(lister.Lister):
results = []
total = 0.0
- for url, start, end in self.app.timing_data:
- seconds = end - start
- total += seconds
- results.append((url, seconds))
+ for url, td in self.app.timing_data:
+ # NOTE(dtroyer): Take the long way here because total_seconds()
+ # was added in py27.
+ sec = (td.microseconds + (td.seconds + td.days*86400) * 1e6) / 1e6
+ total += sec
+ results.append((url, sec))
results.append(('Total', total))
return (
column_headers,
diff --git a/openstackclient/compute/client.py b/openstackclient/compute/client.py
index 7ca08a4f..93a7b715 100644
--- a/openstackclient/compute/client.py
+++ b/openstackclient/compute/client.py
@@ -15,14 +15,6 @@
import logging
-from novaclient import client as nova_client
-from novaclient import extension
-
-try:
- from novaclient.v2.contrib import list_extensions
-except ImportError:
- from novaclient.v1_1.contrib import list_extensions
-
from openstackclient.common import utils
LOG = logging.getLogger(__name__)
@@ -30,10 +22,22 @@ LOG = logging.getLogger(__name__)
DEFAULT_COMPUTE_API_VERSION = '2'
API_VERSION_OPTION = 'os_compute_api_version'
API_NAME = 'compute'
+API_VERSIONS = {
+ "2": "novaclient.client",
+}
def make_client(instance):
"""Returns a compute service client."""
+
+ # Defer client imports until we actually need them
+ from novaclient import client as nova_client
+ from novaclient import extension
+ try:
+ from novaclient.v2.contrib import list_extensions
+ except ImportError:
+ from novaclient.v1_1.contrib import list_extensions
+
compute_client = nova_client.get_client_class(
instance._api_version[API_NAME],
)
diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py
index 49ef18b2..e4e96ee7 100644
--- a/openstackclient/compute/v2/server.py
+++ b/openstackclient/compute/v2/server.py
@@ -275,10 +275,17 @@ class CreateServer(show.ShowOne):
)
parser.add_argument(
'--nic',
- metavar='<nic-config-string>',
+ metavar="<net-id=net-uuid,v4-fixed-ip=ip-addr,v6-fixed-ip=ip-addr,"
+ "port-id=port-uuid>",
action='append',
default=[],
- help=_('Specify NIC configuration (optional extension)'),
+ help=_("Create a NIC on the server. "
+ "Specify option multiple times to create multiple NICs. "
+ "Either net-id or port-id must be provided, but not both. "
+ "net-id: attach NIC to network with this UUID, "
+ "port-id: attach NIC to port with this UUID, "
+ "v4-fixed-ip: IPv4 fixed address for NIC (optional), "
+ "v6-fixed-ip: IPv6 fixed address for NIC (optional)."),
)
parser.add_argument(
'--hint',
diff --git a/openstackclient/identity/common.py b/openstackclient/identity/common.py
index 253729bd..a1b46cb4 100644
--- a/openstackclient/identity/common.py
+++ b/openstackclient/identity/common.py
@@ -17,6 +17,9 @@
from keystoneclient import exceptions as identity_exc
from keystoneclient.v3 import domains
+from keystoneclient.v3 import groups
+from keystoneclient.v3 import projects
+from keystoneclient.v3 import users
from openstackclient.common import exceptions
from openstackclient.common import utils
@@ -56,4 +59,58 @@ def find_domain(identity_client, name_or_id):
return dom
except identity_exc.Forbidden:
pass
- return domains.Domain(None, {'id': name_or_id})
+ return domains.Domain(None, {'id': name_or_id, 'name': name_or_id})
+
+
+def find_group(identity_client, name_or_id):
+ """Find a group.
+
+ If the user does not have permissions to to perform a list groups call,
+ e.g., if the user is a project admin, assume that the group given is the
+ id rather than the name. This method is used by the role add command to
+ allow a role to be assigned to a group by a project admin who does not
+ have permission to list groups.
+ """
+ try:
+ group = utils.find_resource(identity_client.groups, name_or_id)
+ if group is not None:
+ return group
+ except identity_exc.Forbidden:
+ pass
+ return groups.Group(None, {'id': name_or_id, 'name': name_or_id})
+
+
+def find_project(identity_client, name_or_id):
+ """Find a project.
+
+ If the user does not have permissions to to perform a list projects
+ call, e.g., if the user is a project admin, assume that the project
+ given is the id rather than the name. This method is used by the role
+ add command to allow a role to be assigned to a user by a project admin
+ who does not have permission to list projects.
+ """
+ try:
+ project = utils.find_resource(identity_client.projects, name_or_id)
+ if project is not None:
+ return project
+ except identity_exc.Forbidden:
+ pass
+ return projects.Project(None, {'id': name_or_id, 'name': name_or_id})
+
+
+def find_user(identity_client, name_or_id):
+ """Find a user.
+
+ If the user does not have permissions to to perform a list users call,
+ e.g., if the user is a project admin, assume that the user given is the
+ id rather than the name. This method is used by the role add command to
+ allow a role to be assigned to a user by a project admin who does not
+ have permission to list users.
+ """
+ try:
+ user = utils.find_resource(identity_client.users, name_or_id)
+ if user is not None:
+ return user
+ except identity_exc.Forbidden:
+ pass
+ return users.User(None, {'id': name_or_id, 'name': name_or_id})
diff --git a/openstackclient/identity/v3/group.py b/openstackclient/identity/v3/group.py
index a2afecb9..91acf3e5 100644
--- a/openstackclient/identity/v3/group.py
+++ b/openstackclient/identity/v3/group.py
@@ -137,11 +137,11 @@ class CreateGroup(show.ShowOne):
def take_action(self, parsed_args):
self.log.debug('take_action(%s)', parsed_args)
identity_client = self.app.client_manager.identity
+
+ domain = None
if parsed_args.domain:
domain = common.find_domain(identity_client,
parsed_args.domain).id
- else:
- domain = None
try:
group = identity_client.groups.create(
@@ -228,11 +228,10 @@ class ListGroup(lister.Lister):
self.log.debug('take_action(%s)', parsed_args)
identity_client = self.app.client_manager.identity
+ domain = None
if parsed_args.domain:
domain = common.find_domain(identity_client,
parsed_args.domain).id
- else:
- domain = None
if parsed_args.user:
user = utils.find_resource(
diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py
index 1c93ad5d..c5560f5e 100644
--- a/openstackclient/identity/v3/project.py
+++ b/openstackclient/identity/v3/project.py
@@ -80,11 +80,10 @@ class CreateProject(show.ShowOne):
self.log.debug('take_action(%s)', parsed_args)
identity_client = self.app.client_manager.identity
+ domain = None
if parsed_args.domain:
domain = common.find_domain(identity_client,
parsed_args.domain).id
- else:
- domain = None
enabled = True
if parsed_args.disable:
diff --git a/openstackclient/identity/v3/role.py b/openstackclient/identity/v3/role.py
index 03760709..3dd998ba 100644
--- a/openstackclient/identity/v3/role.py
+++ b/openstackclient/identity/v3/role.py
@@ -26,6 +26,7 @@ from keystoneclient import exceptions as ksc_exc
from openstackclient.common import utils
from openstackclient.i18n import _ # noqa
+from openstackclient.identity import common
class AddRole(command.Command):
@@ -78,12 +79,12 @@ class AddRole(command.Command):
)
if parsed_args.user and parsed_args.domain:
- user = utils.find_resource(
- identity_client.users,
+ user = common.find_user(
+ identity_client,
parsed_args.user,
)
- domain = utils.find_resource(
- identity_client.domains,
+ domain = common.find_domain(
+ identity_client,
parsed_args.domain,
)
identity_client.roles.grant(
@@ -92,12 +93,12 @@ class AddRole(command.Command):
domain=domain.id,
)
elif parsed_args.user and parsed_args.project:
- user = utils.find_resource(
- identity_client.users,
+ user = common.find_user(
+ identity_client,
parsed_args.user,
)
- project = utils.find_resource(
- identity_client.projects,
+ project = common.find_project(
+ identity_client,
parsed_args.project,
)
identity_client.roles.grant(
@@ -106,12 +107,12 @@ class AddRole(command.Command):
project=project.id,
)
elif parsed_args.group and parsed_args.domain:
- group = utils.find_resource(
- identity_client.groups,
+ group = common.find_group(
+ identity_client,
parsed_args.group,
)
- domain = utils.find_resource(
- identity_client.domains,
+ domain = common.find_domain(
+ identity_client,
parsed_args.domain,
)
identity_client.roles.grant(
@@ -120,12 +121,12 @@ class AddRole(command.Command):
domain=domain.id,
)
elif parsed_args.group and parsed_args.project:
- group = utils.find_resource(
- identity_client.groups,
+ group = common.find_group(
+ identity_client,
parsed_args.group,
)
- project = utils.find_resource(
- identity_client.projects,
+ project = common.find_project(
+ identity_client,
parsed_args.project,
)
identity_client.roles.grant(
@@ -240,24 +241,24 @@ class ListRole(lister.Lister):
identity_client = self.app.client_manager.identity
if parsed_args.user:
- user = utils.find_resource(
- identity_client.users,
+ user = common.find_user(
+ identity_client,
parsed_args.user,
)
elif parsed_args.group:
- group = utils.find_resource(
- identity_client.groups,
+ group = common.find_group(
+ identity_client,
parsed_args.group,
)
if parsed_args.domain:
- domain = utils.find_resource(
- identity_client.domains,
+ domain = common.find_domain(
+ identity_client,
parsed_args.domain,
)
elif parsed_args.project:
- project = utils.find_resource(
- identity_client.projects,
+ project = common.find_project(
+ identity_client,
parsed_args.project,
)
@@ -370,12 +371,12 @@ class RemoveRole(command.Command):
)
if parsed_args.user and parsed_args.domain:
- user = utils.find_resource(
- identity_client.users,
+ user = common.find_user(
+ identity_client,
parsed_args.user,
)
- domain = utils.find_resource(
- identity_client.domains,
+ domain = common.find_domain(
+ identity_client,
parsed_args.domain,
)
identity_client.roles.revoke(
@@ -384,12 +385,12 @@ class RemoveRole(command.Command):
domain=domain.id,
)
elif parsed_args.user and parsed_args.project:
- user = utils.find_resource(
- identity_client.users,
+ user = common.find_user(
+ identity_client,
parsed_args.user,
)
- project = utils.find_resource(
- identity_client.projects,
+ project = common.find_project(
+ identity_client,
parsed_args.project,
)
identity_client.roles.revoke(
@@ -398,12 +399,12 @@ class RemoveRole(command.Command):
project=project.id,
)
elif parsed_args.group and parsed_args.domain:
- group = utils.find_resource(
- identity_client.groups,
+ group = common.find_group(
+ identity_client,
parsed_args.group,
)
- domain = utils.find_resource(
- identity_client.domains,
+ domain = common.find_domain(
+ identity_client,
parsed_args.domain,
)
identity_client.roles.revoke(
@@ -412,12 +413,12 @@ class RemoveRole(command.Command):
domain=domain.id,
)
elif parsed_args.group and parsed_args.project:
- group = utils.find_resource(
- identity_client.groups,
+ group = common.find_group(
+ identity_client,
parsed_args.group,
)
- project = utils.find_resource(
- identity_client.projects,
+ project = common.find_project(
+ identity_client,
parsed_args.project,
)
identity_client.roles.revoke(
diff --git a/openstackclient/identity/v3/role_assignment.py b/openstackclient/identity/v3/role_assignment.py
index f053b608..24e3a7f7 100644
--- a/openstackclient/identity/v3/role_assignment.py
+++ b/openstackclient/identity/v3/role_assignment.py
@@ -18,6 +18,7 @@ import logging
from cliff import lister
from openstackclient.common import utils
+from openstackclient.identity import common
class ListRoleAssignment(lister.Lister):
@@ -80,29 +81,29 @@ class ListRoleAssignment(lister.Lister):
user = None
if parsed_args.user:
- user = utils.find_resource(
- identity_client.users,
+ user = common.find_user(
+ identity_client,
parsed_args.user,
)
domain = None
if parsed_args.domain:
- domain = utils.find_resource(
- identity_client.domains,
+ domain = common.find_domain(
+ identity_client,
parsed_args.domain,
)
project = None
if parsed_args.project:
- project = utils.find_resource(
- identity_client.projects,
+ project = common.find_project(
+ identity_client,
parsed_args.project,
)
group = None
if parsed_args.group:
- group = utils.find_resource(
- identity_client.groups,
+ group = common.find_group(
+ identity_client,
parsed_args.group,
)
diff --git a/openstackclient/identity/v3/trust.py b/openstackclient/identity/v3/trust.py
index e67b02e7..ab6673d2 100644
--- a/openstackclient/identity/v3/trust.py
+++ b/openstackclient/identity/v3/trust.py
@@ -92,23 +92,20 @@ class CreateTrust(show.ShowOne):
self.log.debug('take_action(%s)' % parsed_args)
identity_client = self.app.client_manager.identity
+ project_domain = None
if parsed_args.project_domain:
project_domain = common.find_domain(identity_client,
parsed_args.project_domain).id
- else:
- project_domain = None
+ trustor_domain = None
if parsed_args.trustor_domain:
trustor_domain = common.find_domain(identity_client,
parsed_args.trustor_domain).id
- else:
- trustor_domain = None
+ trustee_domain = None
if parsed_args.trustee_domain:
trustee_domain = common.find_domain(identity_client,
parsed_args.trustee_domain).id
- else:
- trustee_domain = None
# NOTE(stevemar): Find the two users, project and roles that
# are necessary for making a trust usable, the API dictates that
diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py
index 0a154f64..c1a0a43c 100644
--- a/openstackclient/identity/v3/user.py
+++ b/openstackclient/identity/v3/user.py
@@ -94,19 +94,17 @@ class CreateUser(show.ShowOne):
self.log.debug('take_action(%s)', parsed_args)
identity_client = self.app.client_manager.identity
+ project_id = None
if parsed_args.project:
project_id = utils.find_resource(
identity_client.projects,
parsed_args.project,
).id
- else:
- project_id = None
+ domain_id = None
if parsed_args.domain:
domain_id = common.find_domain(identity_client,
parsed_args.domain).id
- else:
- domain_id = None
enabled = True
if parsed_args.disable:
@@ -211,11 +209,10 @@ class ListUser(lister.Lister):
self.log.debug('take_action(%s)', parsed_args)
identity_client = self.app.client_manager.identity
+ domain = None
if parsed_args.domain:
domain = common.find_domain(identity_client,
parsed_args.domain).id
- else:
- domain = None
if parsed_args.group:
group = utils.find_resource(
diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py
index 7f5f5af9..4c07d360 100644
--- a/openstackclient/image/v1/image.py
+++ b/openstackclient/image/v1/image.py
@@ -190,7 +190,7 @@ class CreateImage(show.ShowOne):
kwargs = {}
copy_attrs = ('name', 'id', 'store', 'container_format',
'disk_format', 'owner', 'size', 'min_disk', 'min_ram',
- 'localtion', 'copy_from', 'volume', 'force',
+ 'location', 'copy_from', 'volume', 'force',
'checksum', 'properties')
for attr in copy_attrs:
if attr in parsed_args:
diff --git a/openstackclient/shell.py b/openstackclient/shell.py
index 3cfd7312..00f4a3c9 100644
--- a/openstackclient/shell.py
+++ b/openstackclient/shell.py
@@ -1,4 +1,5 @@
# Copyright 2012-2013 OpenStack Foundation
+# Copyright 2015 Dean Troyer
#
# 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
@@ -19,6 +20,7 @@ import getpass
import logging
import sys
import traceback
+import warnings
from cliff import app
from cliff import command
@@ -31,6 +33,8 @@ from openstackclient.common import exceptions as exc
from openstackclient.common import timing
from openstackclient.common import utils
+from os_client_config import config as cloud_config
+
DEFAULT_DOMAIN = 'default'
@@ -76,51 +80,19 @@ class OpenStackShell(app.App):
super(OpenStackShell, self).__init__(
description=__doc__.strip(),
version=openstackclient.__version__,
- command_manager=commandmanager.CommandManager('openstack.cli'))
+ command_manager=commandmanager.CommandManager('openstack.cli'),
+ deferred_help=True)
self.api_version = {}
# Until we have command line arguments parsed, dump any stack traces
self.dump_stack_trace = True
- # This is instantiated in initialize_app() only when using
- # password flow auth
- self.auth_client = None
-
# Assume TLS host certificate verification is enabled
self.verify = True
self.client_manager = None
- # NOTE(dtroyer): This hack changes the help action that Cliff
- # automatically adds to the parser so we can defer
- # its execution until after the api-versioned commands
- # have been loaded. There doesn't seem to be a
- # way to edit/remove anything from an existing parser.
-
- # Replace the cliff-added help.HelpAction to defer its execution
- self.DeferredHelpAction = None
- for a in self.parser._actions:
- if type(a) == help.HelpAction:
- # Found it, save and replace it
- self.DeferredHelpAction = a
-
- # These steps are argparse-implementation-dependent
- self.parser._actions.remove(a)
- if self.parser._option_string_actions['-h']:
- del self.parser._option_string_actions['-h']
- if self.parser._option_string_actions['--help']:
- del self.parser._option_string_actions['--help']
-
- # Make a new help option to just set a flag
- self.parser.add_argument(
- '-h', '--help',
- action='store_true',
- dest='deferred_help',
- default=False,
- help="Show this help message and exit",
- )
-
def configure_logging(self):
"""Configure logging for the app
@@ -139,12 +111,15 @@ class OpenStackShell(app.App):
if self.options.verbose_level == 0:
# --quiet
root_logger.setLevel(logging.ERROR)
+ warnings.simplefilter("ignore")
elif self.options.verbose_level == 1:
# This is the default case, no --debug, --verbose or --quiet
root_logger.setLevel(logging.WARNING)
+ warnings.simplefilter("ignore")
elif self.options.verbose_level == 2:
# One --verbose
root_logger.setLevel(logging.INFO)
+ warnings.simplefilter("once")
elif self.options.verbose_level >= 3:
# Two or more --verbose
root_logger.setLevel(logging.DEBUG)
@@ -188,10 +163,19 @@ class OpenStackShell(app.App):
description,
version)
+ # service token auth argument
+ parser.add_argument(
+ '--os-cloud',
+ metavar='<cloud-config-name>',
+ dest='cloud',
+ default=utils.env('OS_CLOUD'),
+ help='Cloud name in clouds.yaml (Env: OS_CLOUD)',
+ )
# Global arguments
parser.add_argument(
'--os-region-name',
metavar='<auth-region-name>',
+ dest='region_name',
default=utils.env('OS_REGION_NAME'),
help='Authentication region name (Env: OS_REGION_NAME)')
parser.add_argument(
@@ -236,8 +220,43 @@ class OpenStackShell(app.App):
* authenticate against Identity if requested
"""
+ # Parent __init__ parses argv into self.options
super(OpenStackShell, self).initialize_app(argv)
+ # Resolve the verify/insecure exclusive pair here as cloud_config
+ # doesn't know about verify
+ self.options.insecure = (
+ self.options.insecure and not self.options.verify
+ )
+
+ # Set the default plugin to token_endpoint if rl and token are given
+ if (self.options.url and self.options.token):
+ # Use service token authentication
+ cloud_config.set_default('auth_type', 'token_endpoint')
+ else:
+ cloud_config.set_default('auth_type', 'osc_password')
+ self.log.debug("options: %s", self.options)
+
+ # Do configuration file handling
+ cc = cloud_config.OpenStackConfig()
+ self.log.debug("defaults: %s", cc.defaults)
+
+ self.cloud = cc.get_one_cloud(
+ cloud=self.options.cloud,
+ argparse=self.options,
+ )
+ self.log.debug("cloud cfg: %s", self.cloud.config)
+
+ # Set up client TLS
+ cacert = self.cloud.cacert
+ if cacert:
+ self.verify = cacert
+ else:
+ self.verify = not getattr(self.cloud.config, 'insecure', False)
+
+ # Neutralize verify option
+ self.options.verify = None
+
# Save default domain
self.default_domain = self.options.os_default_domain
@@ -247,6 +266,11 @@ class OpenStackShell(app.App):
if version_opt:
api = mod.API_NAME
self.api_version[api] = version_opt
+ if version_opt not in mod.API_VERSIONS:
+ self.log.warning(
+ "The %s version <%s> is not in supported versions <%s>"
+ % (api, version_opt,
+ ', '.join(mod.API_VERSIONS.keys())))
# Command groups deal only with major versions
version = '.v' + version_opt.replace('.', '_').split('_')[0]
cmd_group = 'openstack.' + api.replace('-', '_') + version
@@ -276,17 +300,10 @@ class OpenStackShell(app.App):
# set up additional clients to stuff in to client_manager??
# Handle deferred help and exit
- if self.options.deferred_help:
- self.DeferredHelpAction(self.parser, self.parser, None, None)
-
- # Set up common client session
- if self.options.os_cacert:
- self.verify = self.options.os_cacert
- else:
- self.verify = not self.options.insecure
+ self.print_help_if_requested()
self.client_manager = clientmanager.ClientManager(
- cli_options=self.options,
+ cli_options=self.cloud,
verify=self.verify,
api_version=self.api_version,
pw_func=prompt_for_password,
@@ -304,7 +321,8 @@ class OpenStackShell(app.App):
try:
# Trigger the Identity client to initialize
self.client_manager.auth_ref
- except Exception:
+ except Exception as e:
+ self.log.warning("Possible error authenticating: " + str(e))
pass
return
@@ -316,11 +334,10 @@ class OpenStackShell(app.App):
# 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())
+ # Get session data
+ self.timing_data.extend(
+ self.client_manager.session.get_timings(),
+ )
# Use the Timing pseudo-command to generate the output
tcmd = timing.Timing(self, self.options)
diff --git a/openstackclient/tests/common/test_clientmanager.py b/openstackclient/tests/common/test_clientmanager.py
index 3648bf57..26cf4967 100644
--- a/openstackclient/tests/common/test_clientmanager.py
+++ b/openstackclient/tests/common/test_clientmanager.py
@@ -34,6 +34,10 @@ AUTH_REF.update(fakes.TEST_RESPONSE_DICT['access'])
SERVICE_CATALOG = service_catalog.ServiceCatalogV2(AUTH_REF)
+# This is deferred in api.auth but we need it here...
+auth.get_options_list()
+
+
class Container(object):
attr = clientmanager.ClientCache(lambda x: object())
@@ -44,13 +48,14 @@ class Container(object):
class FakeOptions(object):
def __init__(self, **kwargs):
for option in auth.OPTIONS_LIST:
- setattr(self, 'os_' + option.replace('-', '_'), None)
- self.os_auth_type = None
- self.os_identity_api_version = '2.0'
+ setattr(self, option.replace('-', '_'), None)
+ self.auth_type = None
+ self.identity_api_version = '2.0'
self.timing = None
- self.os_region_name = None
- self.os_url = None
- self.os_default_domain = 'default'
+ self.region_name = None
+ self.url = None
+ self.auth = {}
+ self.default_domain = 'default'
self.__dict__.update(kwargs)
@@ -82,9 +87,11 @@ class TestClientManager(utils.TestCase):
client_manager = clientmanager.ClientManager(
cli_options=FakeOptions(
- os_token=fakes.AUTH_TOKEN,
- os_url=fakes.AUTH_URL,
- os_auth_type='token_endpoint',
+ auth_type='token_endpoint',
+ auth=dict(
+ token=fakes.AUTH_TOKEN,
+ url=fakes.AUTH_URL,
+ ),
),
api_version=API_VERSION,
verify=True
@@ -110,9 +117,11 @@ class TestClientManager(utils.TestCase):
client_manager = clientmanager.ClientManager(
cli_options=FakeOptions(
- os_token=fakes.AUTH_TOKEN,
- os_auth_url=fakes.AUTH_URL,
- os_auth_type='v2token',
+ auth=dict(
+ token=fakes.AUTH_TOKEN,
+ auth_url=fakes.AUTH_URL,
+ ),
+ auth_type='v2token',
),
api_version=API_VERSION,
verify=True
@@ -134,10 +143,12 @@ class TestClientManager(utils.TestCase):
client_manager = clientmanager.ClientManager(
cli_options=FakeOptions(
- os_auth_url=fakes.AUTH_URL,
- os_username=fakes.USERNAME,
- os_password=fakes.PASSWORD,
- os_project_name=fakes.PROJECT_NAME,
+ auth=dict(
+ auth_url=fakes.AUTH_URL,
+ username=fakes.USERNAME,
+ password=fakes.PASSWORD,
+ project_name=fakes.PROJECT_NAME,
+ ),
),
api_version=API_VERSION,
verify=False,
@@ -194,11 +205,13 @@ class TestClientManager(utils.TestCase):
client_manager = clientmanager.ClientManager(
cli_options=FakeOptions(
- os_auth_url=fakes.AUTH_URL,
- os_username=fakes.USERNAME,
- os_password=fakes.PASSWORD,
- os_project_name=fakes.PROJECT_NAME,
- os_auth_type='v2password',
+ auth=dict(
+ auth_url=fakes.AUTH_URL,
+ username=fakes.USERNAME,
+ password=fakes.PASSWORD,
+ project_name=fakes.PROJECT_NAME,
+ ),
+ auth_type='v2password',
),
api_version=API_VERSION,
verify='cafile',
@@ -210,8 +223,8 @@ class TestClientManager(utils.TestCase):
self.assertEqual('cafile', client_manager._cacert)
def _select_auth_plugin(self, auth_params, api_version, auth_plugin_name):
- auth_params['os_auth_type'] = auth_plugin_name
- auth_params['os_identity_api_version'] = api_version
+ auth_params['auth_type'] = auth_plugin_name
+ auth_params['identity_api_version'] = api_version
client_manager = clientmanager.ClientManager(
cli_options=FakeOptions(**auth_params),
api_version=API_VERSION,
@@ -226,19 +239,33 @@ class TestClientManager(utils.TestCase):
def test_client_manager_select_auth_plugin(self):
# test token auth
- params = dict(os_token=fakes.AUTH_TOKEN,
- os_auth_url=fakes.AUTH_URL)
+ params = dict(
+ auth=dict(
+ auth_url=fakes.AUTH_URL,
+ token=fakes.AUTH_TOKEN,
+ ),
+ )
self._select_auth_plugin(params, '2.0', 'v2token')
self._select_auth_plugin(params, '3', 'v3token')
self._select_auth_plugin(params, 'XXX', 'token')
# test token/endpoint auth
- params = dict(os_token=fakes.AUTH_TOKEN, os_url='test')
+ params = dict(
+ auth_plugin='token_endpoint',
+ auth=dict(
+ url='test',
+ token=fakes.AUTH_TOKEN,
+ ),
+ )
self._select_auth_plugin(params, 'XXX', 'token_endpoint')
# test password auth
- params = dict(os_auth_url=fakes.AUTH_URL,
- os_username=fakes.USERNAME,
- os_password=fakes.PASSWORD,
- os_project_name=fakes.PROJECT_NAME)
+ params = dict(
+ auth=dict(
+ auth_url=fakes.AUTH_URL,
+ username=fakes.USERNAME,
+ password=fakes.PASSWORD,
+ project_name=fakes.PROJECT_NAME,
+ ),
+ )
self._select_auth_plugin(params, '2.0', 'v2password')
self._select_auth_plugin(params, '3', 'v3password')
self._select_auth_plugin(params, 'XXX', 'password')
diff --git a/openstackclient/tests/common/test_timing.py b/openstackclient/tests/common/test_timing.py
index aa910b91..a7f93b55 100644
--- a/openstackclient/tests/common/test_timing.py
+++ b/openstackclient/tests/common/test_timing.py
@@ -13,14 +13,15 @@
"""Test Timing pseudo-command"""
+import datetime
+
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
+timing_elapsed = 0.872809
class FakeGenericClient(object):
@@ -66,9 +67,10 @@ class TestTiming(utils.TestCommand):
self.assertEqual(datalist, data)
def test_timing_list(self):
- self.app.timing_data = [
- (timing_url, timing_start, timing_end),
- ]
+ self.app.timing_data = [(
+ timing_url,
+ datetime.timedelta(microseconds=timing_elapsed*1000000),
+ )]
arglist = []
verifylist = []
@@ -79,9 +81,8 @@ class TestTiming(utils.TestCommand):
collist = ('URL', 'Seconds')
self.assertEqual(collist, columns)
- timing_sec = timing_end - timing_start
datalist = [
- (timing_url, timing_sec),
- ('Total', timing_sec)
+ (timing_url, timing_elapsed),
+ ('Total', timing_elapsed),
]
self.assertEqual(datalist, data)
diff --git a/openstackclient/tests/test_shell.py b/openstackclient/tests/test_shell.py
index f1043072..a43be954 100644
--- a/openstackclient/tests/test_shell.py
+++ b/openstackclient/tests/test_shell.py
@@ -82,34 +82,62 @@ class TestShell(utils.TestCase):
fake_execute(_shell, _cmd)
self.app.assert_called_with(["list", "project"])
- self.assertEqual(default_args["auth_url"],
- _shell.options.os_auth_url)
- self.assertEqual(default_args["project_id"],
- _shell.options.os_project_id)
- self.assertEqual(default_args["project_name"],
- _shell.options.os_project_name)
- self.assertEqual(default_args["domain_id"],
- _shell.options.os_domain_id)
- self.assertEqual(default_args["domain_name"],
- _shell.options.os_domain_name)
- self.assertEqual(default_args["user_domain_id"],
- _shell.options.os_user_domain_id)
- self.assertEqual(default_args["user_domain_name"],
- _shell.options.os_user_domain_name)
- self.assertEqual(default_args["project_domain_id"],
- _shell.options.os_project_domain_id)
- self.assertEqual(default_args["project_domain_name"],
- _shell.options.os_project_domain_name)
- self.assertEqual(default_args["username"],
- _shell.options.os_username)
- self.assertEqual(default_args["password"],
- _shell.options.os_password)
- self.assertEqual(default_args["region_name"],
- _shell.options.os_region_name)
- self.assertEqual(default_args["trust_id"],
- _shell.options.os_trust_id)
- self.assertEqual(default_args['auth_type'],
- _shell.options.os_auth_type)
+ self.assertEqual(
+ default_args.get("auth_url", ''),
+ _shell.options.auth_url,
+ )
+ self.assertEqual(
+ default_args.get("project_id", ''),
+ _shell.options.project_id,
+ )
+ self.assertEqual(
+ default_args.get("project_name", ''),
+ _shell.options.project_name,
+ )
+ self.assertEqual(
+ default_args.get("domain_id", ''),
+ _shell.options.domain_id,
+ )
+ self.assertEqual(
+ default_args.get("domain_name", ''),
+ _shell.options.domain_name,
+ )
+ self.assertEqual(
+ default_args.get("user_domain_id", ''),
+ _shell.options.user_domain_id,
+ )
+ self.assertEqual(
+ default_args.get("user_domain_name", ''),
+ _shell.options.user_domain_name,
+ )
+ self.assertEqual(
+ default_args.get("project_domain_id", ''),
+ _shell.options.project_domain_id,
+ )
+ self.assertEqual(
+ default_args.get("project_domain_name", ''),
+ _shell.options.project_domain_name,
+ )
+ self.assertEqual(
+ default_args.get("username", ''),
+ _shell.options.username,
+ )
+ self.assertEqual(
+ default_args.get("password", ''),
+ _shell.options.password,
+ )
+ self.assertEqual(
+ default_args.get("region_name", ''),
+ _shell.options.region_name,
+ )
+ self.assertEqual(
+ default_args.get("trust_id", ''),
+ _shell.options.trust_id,
+ )
+ self.assertEqual(
+ default_args.get('auth_type', ''),
+ _shell.options.auth_type,
+ )
def _assert_token_auth(self, cmd_options, default_args):
with mock.patch("openstackclient.shell.OpenStackShell.initialize_app",
@@ -118,9 +146,34 @@ class TestShell(utils.TestCase):
fake_execute(_shell, _cmd)
self.app.assert_called_with(["list", "role"])
- self.assertEqual(default_args["os_token"], _shell.options.os_token)
- self.assertEqual(default_args["os_auth_url"],
- _shell.options.os_auth_url)
+ self.assertEqual(
+ default_args.get("token", ''),
+ _shell.options.token,
+ "token"
+ )
+ self.assertEqual(
+ default_args.get("auth_url", ''),
+ _shell.options.auth_url,
+ "auth_url"
+ )
+
+ def _assert_token_endpoint_auth(self, cmd_options, default_args):
+ with mock.patch("openstackclient.shell.OpenStackShell.initialize_app",
+ self.app):
+ _shell, _cmd = make_shell(), cmd_options + " list role"
+ fake_execute(_shell, _cmd)
+
+ self.app.assert_called_with(["list", "role"])
+ self.assertEqual(
+ default_args.get("token", ''),
+ _shell.options.token,
+ "token",
+ )
+ self.assertEqual(
+ default_args.get("url", ''),
+ _shell.options.url,
+ "url",
+ )
def _assert_cli(self, cmd_options, default_args):
with mock.patch("openstackclient.shell.OpenStackShell.initialize_app",
@@ -178,301 +231,233 @@ class TestShellPasswordAuth(TestShell):
flag = "--os-auth-url " + DEFAULT_AUTH_URL
kwargs = {
"auth_url": DEFAULT_AUTH_URL,
- "project_id": "",
- "project_name": "",
- "user_domain_id": "",
- "domain_id": "",
- "domain_name": "",
- "user_domain_name": "",
- "project_domain_id": "",
- "project_domain_name": "",
- "username": "",
- "password": "",
- "region_name": "",
- "trust_id": "",
- "auth_type": "",
}
self._assert_password_auth(flag, kwargs)
def test_only_project_id_flow(self):
flag = "--os-project-id " + DEFAULT_PROJECT_ID
kwargs = {
- "auth_url": "",
"project_id": DEFAULT_PROJECT_ID,
- "project_name": "",
- "domain_id": "",
- "domain_name": "",
- "user_domain_id": "",
- "user_domain_name": "",
- "project_domain_id": "",
- "project_domain_name": "",
- "username": "",
- "password": "",
- "region_name": "",
- "trust_id": "",
- "auth_type": "",
}
self._assert_password_auth(flag, kwargs)
def test_only_project_name_flow(self):
flag = "--os-project-name " + DEFAULT_PROJECT_NAME
kwargs = {
- "auth_url": "",
- "project_id": "",
"project_name": DEFAULT_PROJECT_NAME,
- "domain_id": "",
- "domain_name": "",
- "user_domain_id": "",
- "user_domain_name": "",
- "project_domain_id": "",
- "project_domain_name": "",
- "username": "",
- "password": "",
- "region_name": "",
- "trust_id": "",
- "auth_type": "",
}
self._assert_password_auth(flag, kwargs)
def test_only_domain_id_flow(self):
flag = "--os-domain-id " + DEFAULT_DOMAIN_ID
kwargs = {
- "auth_url": "",
- "project_id": "",
- "project_name": "",
"domain_id": DEFAULT_DOMAIN_ID,
- "domain_name": "",
- "user_domain_id": "",
- "user_domain_name": "",
- "project_domain_id": "",
- "project_domain_name": "",
- "username": "",
- "password": "",
- "region_name": "",
- "trust_id": "",
- "auth_type": "",
}
self._assert_password_auth(flag, kwargs)
def test_only_domain_name_flow(self):
flag = "--os-domain-name " + DEFAULT_DOMAIN_NAME
kwargs = {
- "auth_url": "",
- "project_id": "",
- "project_name": "",
- "domain_id": "",
"domain_name": DEFAULT_DOMAIN_NAME,
- "user_domain_id": "",
- "user_domain_name": "",
- "project_domain_id": "",
- "project_domain_name": "",
- "username": "",
- "password": "",
- "region_name": "",
- "trust_id": "",
- "auth_type": "",
}
self._assert_password_auth(flag, kwargs)
def test_only_user_domain_id_flow(self):
flag = "--os-user-domain-id " + DEFAULT_USER_DOMAIN_ID
kwargs = {
- "auth_url": "",
- "project_id": "",
- "project_name": "",
- "domain_id": "",
- "domain_name": "",
"user_domain_id": DEFAULT_USER_DOMAIN_ID,
- "user_domain_name": "",
- "project_domain_id": "",
- "project_domain_name": "",
- "username": "",
- "password": "",
- "region_name": "",
- "trust_id": "",
- "auth_type": "",
}
self._assert_password_auth(flag, kwargs)
def test_only_user_domain_name_flow(self):
flag = "--os-user-domain-name " + DEFAULT_USER_DOMAIN_NAME
kwargs = {
- "auth_url": "",
- "project_id": "",
- "project_name": "",
- "domain_id": "",
- "domain_name": "",
- "user_domain_id": "",
"user_domain_name": DEFAULT_USER_DOMAIN_NAME,
- "project_domain_id": "",
- "project_domain_name": "",
- "username": "",
- "password": "",
- "region_name": "",
- "trust_id": "",
- "auth_type": "",
}
self._assert_password_auth(flag, kwargs)
def test_only_project_domain_id_flow(self):
flag = "--os-project-domain-id " + DEFAULT_PROJECT_DOMAIN_ID
kwargs = {
- "auth_url": "",
- "project_id": "",
- "project_name": "",
- "domain_id": "",
- "domain_name": "",
- "user_domain_id": "",
- "user_domain_name": "",
"project_domain_id": DEFAULT_PROJECT_DOMAIN_ID,
- "project_domain_name": "",
- "username": "",
- "password": "",
- "region_name": "",
- "trust_id": "",
- "auth_type": "",
}
self._assert_password_auth(flag, kwargs)
def test_only_project_domain_name_flow(self):
flag = "--os-project-domain-name " + DEFAULT_PROJECT_DOMAIN_NAME
kwargs = {
- "auth_url": "",
- "project_id": "",
- "project_name": "",
- "domain_id": "",
- "domain_name": "",
- "user_domain_id": "",
- "user_domain_name": "",
- "project_domain_id": "",
"project_domain_name": DEFAULT_PROJECT_DOMAIN_NAME,
- "username": "",
- "password": "",
- "region_name": "",
- "trust_id": "",
- "auth_type": "",
}
self._assert_password_auth(flag, kwargs)
def test_only_username_flow(self):
flag = "--os-username " + DEFAULT_USERNAME
kwargs = {
- "auth_url": "",
- "project_id": "",
- "project_name": "",
- "domain_id": "",
- "domain_name": "",
- "user_domain_id": "",
- "user_domain_name": "",
- "project_domain_id": "",
- "project_domain_name": "",
"username": DEFAULT_USERNAME,
- "password": "",
- "region_name": "",
- "trust_id": "",
- "auth_type": "",
}
self._assert_password_auth(flag, kwargs)
def test_only_password_flow(self):
flag = "--os-password " + DEFAULT_PASSWORD
kwargs = {
- "auth_url": "",
- "project_id": "",
- "project_name": "",
- "domain_id": "",
- "domain_name": "",
- "user_domain_id": "",
- "user_domain_name": "",
- "project_domain_id": "",
- "project_domain_name": "",
- "username": "",
"password": DEFAULT_PASSWORD,
- "region_name": "",
- "trust_id": "",
- "auth_type": "",
}
self._assert_password_auth(flag, kwargs)
def test_only_region_name_flow(self):
flag = "--os-region-name " + DEFAULT_REGION_NAME
kwargs = {
- "auth_url": "",
- "project_id": "",
- "project_name": "",
- "domain_id": "",
- "domain_name": "",
- "user_domain_id": "",
- "user_domain_name": "",
- "project_domain_id": "",
- "project_domain_name": "",
- "username": "",
- "password": "",
"region_name": DEFAULT_REGION_NAME,
- "trust_id": "",
- "auth_type": "",
}
self._assert_password_auth(flag, kwargs)
def test_only_trust_id_flow(self):
flag = "--os-trust-id " + "1234"
kwargs = {
- "auth_url": "",
- "project_id": "",
- "project_name": "",
- "domain_id": "",
- "domain_name": "",
- "user_domain_id": "",
- "user_domain_name": "",
- "project_domain_id": "",
- "project_domain_name": "",
- "username": "",
- "password": "",
- "region_name": "",
"trust_id": "1234",
- "auth_type": "",
}
self._assert_password_auth(flag, kwargs)
def test_only_auth_type_flow(self):
flag = "--os-auth-type " + "v2password"
kwargs = {
- "auth_url": "",
- "project_id": "",
- "project_name": "",
- "domain_id": "",
- "domain_name": "",
- "user_domain_id": "",
- "user_domain_name": "",
- "project_domain_id": "",
- "project_domain_name": "",
- "username": "",
- "password": "",
- "region_name": "",
- "trust_id": "",
"auth_type": DEFAULT_AUTH_PLUGIN
}
self._assert_password_auth(flag, kwargs)
class TestShellTokenAuth(TestShell):
+ def test_only_token(self):
+ flag = "--os-token " + DEFAULT_TOKEN
+ kwargs = {
+ "token": DEFAULT_TOKEN,
+ "auth_url": '',
+ }
+ self._assert_token_auth(flag, kwargs)
+
+ def test_only_auth_url(self):
+ flag = "--os-auth-url " + DEFAULT_AUTH_URL
+ kwargs = {
+ "token": '',
+ "auth_url": DEFAULT_AUTH_URL,
+ }
+ self._assert_token_auth(flag, kwargs)
+
+ def test_empty_auth(self):
+ os.environ = {}
+ flag = ""
+ kwargs = {}
+ self._assert_token_auth(flag, kwargs)
+
+
+class TestShellTokenAuthEnv(TestShell):
def setUp(self):
- super(TestShellTokenAuth, self).setUp()
+ super(TestShellTokenAuthEnv, self).setUp()
env = {
"OS_TOKEN": DEFAULT_TOKEN,
- "OS_AUTH_URL": DEFAULT_SERVICE_URL,
+ "OS_AUTH_URL": DEFAULT_AUTH_URL,
}
self.orig_env, os.environ = os.environ, env.copy()
def tearDown(self):
- super(TestShellTokenAuth, self).tearDown()
+ super(TestShellTokenAuthEnv, self).tearDown()
os.environ = self.orig_env
- def test_default_auth(self):
+ def test_env(self):
flag = ""
kwargs = {
- "os_token": DEFAULT_TOKEN,
- "os_auth_url": DEFAULT_SERVICE_URL
+ "token": DEFAULT_TOKEN,
+ "auth_url": DEFAULT_AUTH_URL,
+ }
+ self._assert_token_auth(flag, kwargs)
+
+ def test_only_token(self):
+ flag = "--os-token xyzpdq"
+ kwargs = {
+ "token": "xyzpdq",
+ "auth_url": DEFAULT_AUTH_URL,
+ }
+ self._assert_token_auth(flag, kwargs)
+
+ def test_only_auth_url(self):
+ flag = "--os-auth-url http://cloud.local:555"
+ kwargs = {
+ "token": DEFAULT_TOKEN,
+ "auth_url": "http://cloud.local:555",
+ }
+ self._assert_token_auth(flag, kwargs)
+
+ def test_empty_auth(self):
+ os.environ = {}
+ flag = ""
+ kwargs = {
+ "token": '',
+ "auth_url": '',
+ }
+ self._assert_token_auth(flag, kwargs)
+
+
+class TestShellTokenEndpointAuth(TestShell):
+ def test_only_token(self):
+ flag = "--os-token " + DEFAULT_TOKEN
+ kwargs = {
+ "token": DEFAULT_TOKEN,
+ "url": '',
+ }
+ self._assert_token_endpoint_auth(flag, kwargs)
+
+ def test_only_url(self):
+ flag = "--os-url " + DEFAULT_SERVICE_URL
+ kwargs = {
+ "token": '',
+ "url": DEFAULT_SERVICE_URL,
+ }
+ self._assert_token_endpoint_auth(flag, kwargs)
+
+ def test_empty_auth(self):
+ os.environ = {}
+ flag = ""
+ kwargs = {
+ "token": '',
+ "auth_url": '',
+ }
+ self._assert_token_endpoint_auth(flag, kwargs)
+
+
+class TestShellTokenEndpointAuthEnv(TestShell):
+ def setUp(self):
+ super(TestShellTokenEndpointAuthEnv, self).setUp()
+ env = {
+ "OS_TOKEN": DEFAULT_TOKEN,
+ "OS_URL": DEFAULT_SERVICE_URL,
+ }
+ self.orig_env, os.environ = os.environ, env.copy()
+
+ def tearDown(self):
+ super(TestShellTokenEndpointAuthEnv, self).tearDown()
+ os.environ = self.orig_env
+
+ def test_env(self):
+ flag = ""
+ kwargs = {
+ "token": DEFAULT_TOKEN,
+ "url": DEFAULT_SERVICE_URL,
+ }
+ self._assert_token_auth(flag, kwargs)
+
+ def test_only_token(self):
+ flag = "--os-token xyzpdq"
+ kwargs = {
+ "token": "xyzpdq",
+ "url": DEFAULT_SERVICE_URL,
+ }
+ self._assert_token_auth(flag, kwargs)
+
+ def test_only_url(self):
+ flag = "--os-url http://cloud.local:555"
+ kwargs = {
+ "token": DEFAULT_TOKEN,
+ "url": "http://cloud.local:555",
}
self._assert_token_auth(flag, kwargs)
@@ -480,8 +465,8 @@ class TestShellTokenAuth(TestShell):
os.environ = {}
flag = ""
kwargs = {
- "os_token": "",
- "os_auth_url": ""
+ "token": '',
+ "url": '',
}
self._assert_token_auth(flag, kwargs)
diff --git a/openstackclient/tests/volume/test_find_resource.py b/openstackclient/tests/volume/test_find_resource.py
index 56081966..00cc46a6 100644
--- a/openstackclient/tests/volume/test_find_resource.py
+++ b/openstackclient/tests/volume/test_find_resource.py
@@ -24,6 +24,13 @@ from openstackclient.tests import utils as test_utils
from openstackclient.volume import client # noqa
+# Monkey patch for v1 cinderclient
+# NOTE(dtroyer): Do here because openstackclient.volume.client
+# doesn't do it until the client object is created now.
+volumes.Volume.NAME_ATTR = 'display_name'
+volume_snapshots.Snapshot.NAME_ATTR = 'display_name'
+
+
ID = '1after909'
NAME = 'PhilSpector'
diff --git a/openstackclient/volume/client.py b/openstackclient/volume/client.py
index 21072aeb..a7b64def 100644
--- a/openstackclient/volume/client.py
+++ b/openstackclient/volume/client.py
@@ -15,17 +15,8 @@
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
-volumes.Volume.NAME_ATTR = 'display_name'
-volume_snapshots.Snapshot.NAME_ATTR = 'display_name'
-
LOG = logging.getLogger(__name__)
DEFAULT_VOLUME_API_VERSION = '1'
@@ -38,6 +29,17 @@ API_VERSIONS = {
def make_client(instance):
"""Returns a volume service client."""
+
+ # Defer client imports until we actually need them
+ from cinderclient import extension
+ from cinderclient.v1.contrib import list_extensions
+ from cinderclient.v1 import volume_snapshots
+ from cinderclient.v1 import volumes
+
+ # Monkey patch for v1 cinderclient
+ volumes.Volume.NAME_ATTR = 'display_name'
+ volume_snapshots.Snapshot.NAME_ATTR = 'display_name'
+
volume_client = utils.get_client_class(
API_NAME,
instance._api_version[API_NAME],