summaryrefslogtreecommitdiff
path: root/openstackclient
diff options
context:
space:
mode:
Diffstat (limited to 'openstackclient')
-rw-r--r--openstackclient/api/api.py23
-rw-r--r--openstackclient/api/auth.py145
-rw-r--r--openstackclient/api/auth_plugin.py102
-rw-r--r--openstackclient/api/object_store_v1.py3
-rw-r--r--openstackclient/common/availability_zone.py33
-rw-r--r--openstackclient/common/clientmanager.py103
-rw-r--r--openstackclient/common/command.py46
-rw-r--r--openstackclient/common/configuration.py17
-rw-r--r--openstackclient/common/exceptions.py108
-rw-r--r--openstackclient/common/extension.py42
-rw-r--r--openstackclient/common/limits.py23
-rw-r--r--openstackclient/common/logs.py188
-rw-r--r--openstackclient/common/module.py10
-rw-r--r--openstackclient/common/parseractions.py153
-rw-r--r--openstackclient/common/quota.py27
-rw-r--r--openstackclient/common/timing.py32
-rw-r--r--openstackclient/common/utils.py413
-rw-r--r--openstackclient/compute/client.py8
-rw-r--r--openstackclient/compute/v2/agent.py24
-rw-r--r--openstackclient/compute/v2/aggregate.py40
-rw-r--r--openstackclient/compute/v2/console.py21
-rw-r--r--openstackclient/compute/v2/fixedip.py4
-rw-r--r--openstackclient/compute/v2/flavor.py85
-rw-r--r--openstackclient/compute/v2/floatingip.py4
-rw-r--r--openstackclient/compute/v2/floatingippool.py4
-rw-r--r--openstackclient/compute/v2/host.py33
-rw-r--r--openstackclient/compute/v2/hypervisor.py7
-rw-r--r--openstackclient/compute/v2/hypervisor_stats.py2
-rw-r--r--openstackclient/compute/v2/keypair.py36
-rw-r--r--openstackclient/compute/v2/server.py120
-rw-r--r--openstackclient/compute/v2/server_backup.py6
-rw-r--r--openstackclient/compute/v2/server_group.py14
-rw-r--r--openstackclient/compute/v2/server_image.py113
-rw-r--r--openstackclient/compute/v2/service.py115
-rw-r--r--openstackclient/compute/v2/usage.py4
-rw-r--r--openstackclient/identity/client.py10
-rw-r--r--openstackclient/identity/common.py71
-rw-r--r--openstackclient/identity/v2_0/catalog.py41
-rw-r--r--openstackclient/identity/v2_0/ec2creds.py31
-rw-r--r--openstackclient/identity/v2_0/endpoint.py36
-rw-r--r--openstackclient/identity/v2_0/project.py35
-rw-r--r--openstackclient/identity/v2_0/role.py35
-rw-r--r--openstackclient/identity/v2_0/service.py41
-rw-r--r--openstackclient/identity/v2_0/token.py21
-rw-r--r--openstackclient/identity/v2_0/user.py23
-rw-r--r--openstackclient/identity/v3/catalog.py41
-rw-r--r--openstackclient/identity/v3/consumer.py7
-rw-r--r--openstackclient/identity/v3/credential.py4
-rw-r--r--openstackclient/identity/v3/domain.py19
-rw-r--r--openstackclient/identity/v3/ec2creds.py4
-rw-r--r--openstackclient/identity/v3/endpoint.py15
-rw-r--r--openstackclient/identity/v3/federation_protocol.py11
-rw-r--r--openstackclient/identity/v3/group.py12
-rw-r--r--openstackclient/identity/v3/identity_provider.py11
-rw-r--r--openstackclient/identity/v3/mapping.py12
-rw-r--r--openstackclient/identity/v3/policy.py7
-rw-r--r--openstackclient/identity/v3/project.py21
-rw-r--r--openstackclient/identity/v3/region.py4
-rw-r--r--openstackclient/identity/v3/role.py12
-rw-r--r--openstackclient/identity/v3/role_assignment.py5
-rw-r--r--openstackclient/identity/v3/service.py9
-rw-r--r--openstackclient/identity/v3/service_provider.py7
-rw-r--r--openstackclient/identity/v3/token.py26
-rw-r--r--openstackclient/identity/v3/trust.py5
-rw-r--r--openstackclient/identity/v3/unscoped_saml.py7
-rw-r--r--openstackclient/identity/v3/user.py20
-rw-r--r--openstackclient/image/client.py3
-rw-r--r--openstackclient/image/v1/image.py34
-rw-r--r--openstackclient/image/v2/image.py76
-rw-r--r--openstackclient/network/client.py3
-rw-r--r--openstackclient/network/common.py23
-rw-r--r--openstackclient/network/v2/address_scope.py25
-rw-r--r--openstackclient/network/v2/floating_ip.py21
-rw-r--r--openstackclient/network/v2/ip_availability.py13
-rw-r--r--openstackclient/network/v2/network.py18
-rw-r--r--openstackclient/network/v2/network_segment.py5
-rw-r--r--openstackclient/network/v2/port.py72
-rw-r--r--openstackclient/network/v2/router.py37
-rw-r--r--openstackclient/network/v2/security_group.py21
-rw-r--r--openstackclient/network/v2/security_group_rule.py29
-rw-r--r--openstackclient/network/v2/subnet.py59
-rw-r--r--openstackclient/network/v2/subnet_pool.py41
-rw-r--r--openstackclient/object/client.py3
-rw-r--r--openstackclient/object/v1/account.py7
-rw-r--r--openstackclient/object/v1/container.py7
-rw-r--r--openstackclient/object/v1/object.py7
-rw-r--r--openstackclient/releasenotes/notes/bug-1543222-6f8579344ff5c958.yaml6
-rw-r--r--openstackclient/shell.py57
-rw-r--r--openstackclient/tests/api/test_api.py3
-rw-r--r--openstackclient/tests/common/test_clientmanager.py35
-rw-r--r--openstackclient/tests/common/test_command.py3
-rw-r--r--openstackclient/tests/common/test_configuration.py17
-rw-r--r--openstackclient/tests/common/test_extension.py156
-rw-r--r--openstackclient/tests/common/test_logs.py15
-rw-r--r--openstackclient/tests/common/test_parseractions.py20
-rw-r--r--openstackclient/tests/common/test_timing.py94
-rw-r--r--openstackclient/tests/common/test_utils.py400
-rw-r--r--openstackclient/tests/compute/v2/fakes.py139
-rw-r--r--openstackclient/tests/compute/v2/test_agent.py13
-rw-r--r--openstackclient/tests/compute/v2/test_aggregate.py65
-rw-r--r--openstackclient/tests/compute/v2/test_console.py149
-rw-r--r--openstackclient/tests/compute/v2/test_flavor.py152
-rw-r--r--openstackclient/tests/compute/v2/test_host.py14
-rw-r--r--openstackclient/tests/compute/v2/test_hypervisor.py3
-rw-r--r--openstackclient/tests/compute/v2/test_keypair.py62
-rw-r--r--openstackclient/tests/compute/v2/test_server.py153
-rw-r--r--openstackclient/tests/compute/v2/test_server_backup.py5
-rw-r--r--openstackclient/tests/compute/v2/test_server_group.py5
-rw-r--r--openstackclient/tests/compute/v2/test_server_image.py228
-rw-r--r--openstackclient/tests/compute/v2/test_service.py198
-rw-r--r--openstackclient/tests/fakes.py15
-rw-r--r--openstackclient/tests/identity/v2_0/fakes.py388
-rw-r--r--openstackclient/tests/identity/v2_0/test_catalog.py62
-rw-r--r--openstackclient/tests/identity/v2_0/test_endpoint.py158
-rw-r--r--openstackclient/tests/identity/v2_0/test_project.py264
-rw-r--r--openstackclient/tests/identity/v2_0/test_role.py297
-rw-r--r--openstackclient/tests/identity/v2_0/test_service.py121
-rw-r--r--openstackclient/tests/identity/v2_0/test_token.py44
-rw-r--r--openstackclient/tests/identity/v2_0/test_user.py295
-rw-r--r--openstackclient/tests/identity/v3/fakes.py37
-rw-r--r--openstackclient/tests/identity/v3/test_catalog.py19
-rw-r--r--openstackclient/tests/identity/v3/test_domain.py10
-rw-r--r--openstackclient/tests/identity/v3/test_mappings.py3
-rw-r--r--openstackclient/tests/identity/v3/test_project.py45
-rw-r--r--openstackclient/tests/identity/v3/test_token.py49
-rw-r--r--openstackclient/tests/identity/v3/test_unscoped_saml.py3
-rw-r--r--openstackclient/tests/identity/v3/test_user.py17
-rw-r--r--openstackclient/tests/image/v1/test_image.py9
-rw-r--r--openstackclient/tests/image/v2/fakes.py5
-rw-r--r--openstackclient/tests/image/v2/test_image.py66
-rw-r--r--openstackclient/tests/network/v2/fakes.py139
-rw-r--r--openstackclient/tests/network/v2/test_address_scope.py11
-rw-r--r--openstackclient/tests/network/v2/test_floating_ip.py131
-rw-r--r--openstackclient/tests/network/v2/test_ip_availability.py12
-rw-r--r--openstackclient/tests/network/v2/test_network.py15
-rw-r--r--openstackclient/tests/network/v2/test_network_segment.py3
-rw-r--r--openstackclient/tests/network/v2/test_port.py181
-rw-r--r--openstackclient/tests/network/v2/test_router.py86
-rw-r--r--openstackclient/tests/network/v2/test_security_group.py139
-rw-r--r--openstackclient/tests/network/v2/test_security_group_rule.py130
-rw-r--r--openstackclient/tests/network/v2/test_subnet.py167
-rw-r--r--openstackclient/tests/network/v2/test_subnet_pool.py79
-rw-r--r--openstackclient/tests/test_shell.py2
-rw-r--r--openstackclient/tests/volume/test_find_resource.py4
-rw-r--r--openstackclient/tests/volume/v1/test_qos_specs.py3
-rw-r--r--openstackclient/tests/volume/v1/test_volume.py19
-rw-r--r--openstackclient/tests/volume/v2/fakes.py249
-rw-r--r--openstackclient/tests/volume/v2/test_backup.py8
-rw-r--r--openstackclient/tests/volume/v2/test_qos_specs.py2
-rw-r--r--openstackclient/tests/volume/v2/test_snapshot.py3
-rw-r--r--openstackclient/tests/volume/v2/test_type.py7
-rw-r--r--openstackclient/tests/volume/v2/test_volume.py3
-rw-r--r--openstackclient/volume/client.py5
-rw-r--r--openstackclient/volume/v1/backup.py11
-rw-r--r--openstackclient/volume/v1/qos_specs.py6
-rw-r--r--openstackclient/volume/v1/service.py5
-rw-r--r--openstackclient/volume/v1/snapshot.py9
-rw-r--r--openstackclient/volume/v1/volume.py20
-rw-r--r--openstackclient/volume/v1/volume_transfer_request.py4
-rw-r--r--openstackclient/volume/v1/volume_type.py41
-rw-r--r--openstackclient/volume/v2/backup.py15
-rw-r--r--openstackclient/volume/v2/qos_specs.py6
-rw-r--r--openstackclient/volume/v2/service.py5
-rw-r--r--openstackclient/volume/v2/snapshot.py6
-rw-r--r--openstackclient/volume/v2/volume.py22
-rw-r--r--openstackclient/volume/v2/volume_transfer_request.py4
-rw-r--r--openstackclient/volume/v2/volume_type.py62
167 files changed, 4873 insertions, 3855 deletions
diff --git a/openstackclient/api/api.py b/openstackclient/api/api.py
index bd2abd88..04d88f31 100644
--- a/openstackclient/api/api.py
+++ b/openstackclient/api/api.py
@@ -17,8 +17,9 @@ import simplejson as json
from keystoneauth1 import exceptions as ks_exceptions
from keystoneauth1 import session as ks_session
+from osc_lib import exceptions
-from openstackclient.common import exceptions
+from openstackclient.i18n import _
class KeystoneSession(object):
@@ -255,9 +256,11 @@ class BaseAPI(KeystoneSession):
if len(data) == 1:
return data[0]
if len(data) > 1:
- msg = "Multiple %s exist with %s='%s'"
+ msg = _("Multiple %(resource)s exist with %(attr)s='%(value)s'")
raise exceptions.CommandError(
- msg % (resource, attr, value),
+ msg % {'resource': resource,
+ 'attr': attr,
+ 'value': value}
)
# Search by id
@@ -265,8 +268,12 @@ class BaseAPI(KeystoneSession):
data = getlist(kwargs)
if len(data) == 1:
return data[0]
- msg = "No %s with a %s or ID of '%s' found"
- raise exceptions.CommandError(msg % (resource, attr, value))
+ msg = _("No %(resource)s with a %(attr)s or ID of '%(value)s' found")
+ raise exceptions.CommandError(
+ msg % {'resource': resource,
+ 'attr': attr,
+ 'value': value}
+ )
def find_bulk(
self,
@@ -314,10 +321,10 @@ class BaseAPI(KeystoneSession):
bulk_list = self.find_bulk(path, **kwargs)
num_bulk = len(bulk_list)
if num_bulk == 0:
- msg = "none found"
+ msg = _("none found")
raise exceptions.NotFound(msg)
elif num_bulk > 1:
- msg = "many found"
+ msg = _("many found")
raise RuntimeError(msg)
return bulk_list[0]
@@ -344,7 +351,7 @@ class BaseAPI(KeystoneSession):
try:
ret = self.find_one("/%s/detail" % (path), **kwargs)
except ks_exceptions.NotFound:
- msg = "%s not found" % value
+ msg = _("%s not found") % value
raise exceptions.NotFound(msg)
return ret
diff --git a/openstackclient/api/auth.py b/openstackclient/api/auth.py
index c74e8005..0c82fe9b 100644
--- a/openstackclient/api/auth.py
+++ b/openstackclient/api/auth.py
@@ -16,15 +16,12 @@
import argparse
import logging
-import stevedore
+from keystoneauth1.loading import base
+from osc_lib import exceptions as exc
+from osc_lib import utils
-from keystoneclient.auth import base
-
-from openstackclient.common import exceptions as exc
-from openstackclient.common import utils
from openstackclient.i18n import _
-
LOG = logging.getLogger(__name__)
# Initialize the list of Authentication plugins early in order
@@ -37,15 +34,10 @@ OPTIONS_LIST = {}
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,
- )
+ PLUGIN_LIST = base.get_available_plugin_names()
return PLUGIN_LIST
@@ -55,8 +47,9 @@ def get_options_list():
global OPTIONS_LIST
if not OPTIONS_LIST:
- for plugin in get_plugin_list():
- for o in plugin.plugin.get_options():
+ for plugin_name in get_plugin_list():
+ plugin_options = base.get_plugin_options(plugin_name)
+ for o in plugin_options:
os_name = o.dest.lower().replace('_', '-')
os_env_name = 'OS_' + os_name.upper().replace('-', '_')
OPTIONS_LIST.setdefault(
@@ -66,7 +59,7 @@ def get_options_list():
# 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,
+ plugin_name,
o.help,
)
return OPTIONS_LIST
@@ -83,7 +76,7 @@ def select_auth_plugin(options):
if options.auth.get('url') and options.auth.get('token'):
# service token authentication
auth_plugin_name = 'token_endpoint'
- elif options.auth_type in [plugin.name for plugin in PLUGIN_LIST]:
+ elif options.auth_type in PLUGIN_LIST:
# A direct plugin name was given, use it
auth_plugin_name = options.auth_type
elif options.auth.get('username'):
@@ -93,7 +86,7 @@ def select_auth_plugin(options):
auth_plugin_name = 'v2password'
else:
# let keystoneclient figure it out itself
- auth_plugin_name = 'osc_password'
+ auth_plugin_name = 'password'
elif options.auth.get('token'):
if options.identity_api_version == '3':
auth_plugin_name = 'v3token'
@@ -105,17 +98,19 @@ def select_auth_plugin(options):
else:
# The ultimate default is similar to the original behaviour,
# but this time with version discovery
- auth_plugin_name = 'osc_password'
+ auth_plugin_name = 'password'
LOG.debug("Auth plugin %s selected", auth_plugin_name)
return auth_plugin_name
def build_auth_params(auth_plugin_name, cmd_options):
- 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)
+ auth_plugin_loader = base.get_plugin_loader(auth_plugin_name)
+ auth_params = {opt.dest: opt.default
+ for opt in base.get_plugin_options(auth_plugin_name)}
+ auth_params.update(dict(cmd_options.auth))
# grab tenant from project for v2.0 API compatibility
if auth_plugin_name.startswith("v2"):
if 'project_id' in auth_params:
@@ -127,58 +122,59 @@ def build_auth_params(auth_plugin_name, cmd_options):
else:
LOG.debug('no auth_type')
# delay the plugin choice, grab every option
- auth_plugin_class = None
+ auth_plugin_loader = None
+ auth_params = dict(cmd_options.auth)
plugin_options = set([o.replace('-', '_') for o in get_options_list()])
for option in plugin_options:
LOG.debug('fetching option %s', option)
auth_params[option] = getattr(cmd_options.auth, option, None)
- return (auth_plugin_class, auth_params)
-
-
-def check_valid_auth_options(options, auth_plugin_name, required_scope=True):
- """Perform basic option checking, provide helpful error messages.
-
- :param required_scope: indicate whether a scoped token is required
-
- """
-
+ return (auth_plugin_loader, auth_params)
+
+
+def check_valid_authorization_options(options, auth_plugin_name):
+ """Validate authorization options, and provide helpful error messages."""
+ if (options.auth.get('project_id') and not
+ options.auth.get('domain_id') and not
+ options.auth.get('domain_name') and not
+ options.auth.get('project_name') and not
+ options.auth.get('tenant_id') and not
+ options.auth.get('tenant_name')):
+ raise exc.CommandError(_(
+ 'Missing parameter(s): '
+ 'Set either a project or a domain scope, but not both. Set a '
+ 'project scope with --os-project-name, OS_PROJECT_NAME, or '
+ 'auth.project_name. Alternatively, set a domain scope with '
+ '--os-domain-name, OS_DOMAIN_NAME or auth.domain_name.'))
+
+
+def check_valid_authentication_options(options, auth_plugin_name):
+ """Validate authentication options, and provide helpful error messages."""
+
+ # Get all the options defined within the plugin.
+ plugin_opts = base.get_plugin_options(auth_plugin_name)
+ plugin_opts = {opt.dest: opt for opt in plugin_opts}
+
+ # NOTE(aloga): this is an horrible hack. We need a way to specify the
+ # required options in the plugins. Using the "required" argument for
+ # the oslo_config.cfg.Opt does not work, as it is not possible to load the
+ # plugin if the option is not defined, so the error will simply be:
+ # "NoMatchingPlugin: The plugin foobar could not be found"
msgs = []
- if auth_plugin_name.endswith('password'):
- if not options.auth.get('username'):
- msgs.append(_('Set a username with --os-username, OS_USERNAME,'
- ' or auth.username'))
- if not options.auth.get('auth_url'):
- msgs.append(_('Set an authentication URL, with --os-auth-url,'
- ' OS_AUTH_URL or auth.auth_url'))
- if (required_scope and not
- options.auth.get('project_id') and not
- options.auth.get('domain_id') and not
- options.auth.get('domain_name') and not
- options.auth.get('project_name') and not
- options.auth.get('tenant_id') and not
- options.auth.get('tenant_name')):
- msgs.append(_('Set a scope, such as a project or domain, set a '
- 'project scope with --os-project-name, '
- 'OS_PROJECT_NAME or auth.project_name, set a domain '
- 'scope with --os-domain-name, OS_DOMAIN_NAME or '
- 'auth.domain_name'))
- elif auth_plugin_name.endswith('token'):
- if not options.auth.get('token'):
- msgs.append(_('Set a token with --os-token, OS_TOKEN or '
- 'auth.token'))
- if not options.auth.get('auth_url'):
- msgs.append(_('Set a service AUTH_URL, with --os-auth-url, '
- 'OS_AUTH_URL or auth.auth_url'))
- elif auth_plugin_name == 'token_endpoint':
- if not options.auth.get('token'):
- msgs.append(_('Set a token with --os-token, OS_TOKEN or '
- 'auth.token'))
- if not options.auth.get('url'):
- msgs.append(_('Set a service URL, with --os-url, OS_URL or '
- 'auth.url'))
-
+ if 'password' in plugin_opts and not options.auth.get('username'):
+ msgs.append(_('Set a username with --os-username, OS_USERNAME,'
+ ' or auth.username'))
+ if 'auth_url' in plugin_opts and not options.auth.get('auth_url'):
+ msgs.append(_('Set a service AUTH_URL, with --os-auth-url, '
+ 'OS_AUTH_URL or auth.auth_url'))
+ if 'url' in plugin_opts and not options.auth.get('url'):
+ msgs.append(_('Set a service URL, with --os-url, '
+ 'OS_URL or auth.url'))
+ if 'token' in plugin_opts and not options.auth.get('token'):
+ msgs.append(_('Set a token with --os-token, '
+ 'OS_TOKEN or auth.token'))
if msgs:
- raise exc.CommandError('Missing parameter(s): \n%s' % '\n'.join(msgs))
+ raise exc.CommandError(
+ _('Missing parameter(s): \n%s') % '\n'.join(msgs))
def build_auth_plugins_option_parser(parser):
@@ -188,16 +184,15 @@ def build_auth_plugins_option_parser(parser):
authentication plugin.
"""
- available_plugins = [plugin.name for plugin in get_plugin_list()]
+ available_plugins = list(get_plugin_list())
parser.add_argument(
'--os-auth-type',
metavar='<auth-type>',
dest='auth_type',
default=utils.env('OS_AUTH_TYPE'),
- help='Select an authentication type. Available types: ' +
- ', '.join(available_plugins) +
- '. Default: selected based on --os-username/--os-token' +
- ' (Env: OS_AUTH_TYPE)',
+ help=_('Select an authentication type. Available types: %s.'
+ ' Default: selected based on --os-username/--os-token'
+ ' (Env: OS_AUTH_TYPE)') % ', '.join(available_plugins),
choices=available_plugins
)
# Maintain compatibility with old tenant env vars
@@ -222,10 +217,10 @@ def build_auth_plugins_option_parser(parser):
OPTIONS_LIST[o]['env'],
utils.env(OPTIONS_LIST[o]['env']),
),
- help='%s\n(Env: %s)' % (
- OPTIONS_LIST[o]['help'],
- OPTIONS_LIST[o]['env'],
- ),
+ help=_('%(help)s\n(Env: %(env)s)') % {
+ 'help': OPTIONS_LIST[o]['help'],
+ 'env': OPTIONS_LIST[o]['env'],
+ },
)
# add tenant-related options for compatibility
# this is deprecated but still used in some tempest tests...
diff --git a/openstackclient/api/auth_plugin.py b/openstackclient/api/auth_plugin.py
index cff0b75d..dc47a688 100644
--- a/openstackclient/api/auth_plugin.py
+++ b/openstackclient/api/auth_plugin.py
@@ -15,93 +15,51 @@
import logging
-from oslo_config import cfg
-from six.moves.urllib import parse as urlparse
+from keystoneauth1 import loading
+from keystoneauth1 import token_endpoint
-from keystoneclient.auth.identity.generic import password as ksc_password
-from keystoneclient.auth import token_endpoint
+from openstackclient.i18n import _
LOG = logging.getLogger(__name__)
-class TokenEndpoint(token_endpoint.Token):
+class TokenEndpoint(loading.BaseLoader):
"""Auth plugin to handle traditional token/endpoint usage
- Implements the methods required to handle token authentication
- with a user-specified token and service endpoint; no Identity calls
- are made for re-scoping, service catalog lookups or the like.
-
- The purpose of this plugin is to get rid of the special-case paths
- in the code to handle this authentication format. Its primary use
- is for bootstrapping the Keystone database.
+ Keystoneauth contains a Token plugin class that now correctly
+ handles the token/endpoint auth compatible with OSC. However,
+ the AdminToken loader deprecates the 'url' argument, which breaks
+ OSC compatibility, so make one that works.
"""
- def __init__(self, url, token, **kwargs):
+ @property
+ def plugin_class(self):
+ return token_endpoint.Token
+
+ def load_from_options(self, url, token, **kwargs):
"""A plugin for static authentication with an existing token
:param string url: Service endpoint
:param string token: Existing token
"""
- super(TokenEndpoint, self).__init__(endpoint=url,
- token=token)
- def get_auth_ref(self, session, **kwargs):
- # Stub this method for compatibility
- return None
+ return super(TokenEndpoint, self).load_from_options(
+ endpoint=url,
+ token=token,
+ )
- @classmethod
def get_options(self):
- options = super(TokenEndpoint, self).get_options()
-
- options.extend([
- # Maintain name 'url' for compatibility
- cfg.StrOpt('url',
- help='Specific service endpoint to use'),
- cfg.StrOpt('token',
- secret=True,
- help='Authentication token to use'),
- ])
-
+ """Return the legacy options"""
+
+ options = [
+ loading.Opt(
+ 'url',
+ help=_('Specific service endpoint to use'),
+ ),
+ loading.Opt(
+ 'token',
+ secret=True,
+ help=_('Authentication token to use'),
+ ),
+ ]
return options
-
-
-class OSCGenericPassword(ksc_password.Password):
- """Auth plugin hack to work around broken Keystone configurations
-
- The default Keystone configuration uses http://localhost:xxxx in
- admin_endpoint and public_endpoint and are returned in the links.href
- attribute by the version routes. Deployments that do not set these
- are unusable with newer keystoneclient version discovery.
-
- """
-
- def create_plugin(self, session, version, url, raw_status=None):
- """Handle default Keystone endpoint configuration
-
- Build the actual API endpoint from the scheme, host and port of the
- original auth URL and the rest from the returned version URL.
- """
-
- ver_u = urlparse.urlparse(url)
-
- # Only hack this if it is the default setting
- if ver_u.netloc.startswith('localhost'):
- auth_u = urlparse.urlparse(self.auth_url)
- # from original auth_url: scheme, netloc
- # from api_url: path, query (basically, the rest)
- url = urlparse.urlunparse((
- auth_u.scheme,
- auth_u.netloc,
- ver_u.path,
- ver_u.params,
- ver_u.query,
- ver_u.fragment,
- ))
- LOG.debug('Version URL updated: %s', url)
-
- return super(OSCGenericPassword, self).create_plugin(
- session=session,
- version=version,
- url=url,
- raw_status=raw_status,
- )
diff --git a/openstackclient/api/object_store_v1.py b/openstackclient/api/object_store_v1.py
index 632e8b19..ae49922d 100644
--- a/openstackclient/api/object_store_v1.py
+++ b/openstackclient/api/object_store_v1.py
@@ -20,8 +20,9 @@ import os
import six
from six.moves import urllib
+from osc_lib import utils
+
from openstackclient.api import api
-from openstackclient.common import utils
class APIv1(api.BaseAPI):
diff --git a/openstackclient/common/availability_zone.py b/openstackclient/common/availability_zone.py
index 3b0270ad..89d77d15 100644
--- a/openstackclient/common/availability_zone.py
+++ b/openstackclient/common/availability_zone.py
@@ -14,15 +14,19 @@
"""Availability Zone action implementations"""
import copy
+import logging
from novaclient import exceptions as nova_exceptions
+from osc_lib.command import command
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import utils
from openstackclient.i18n import _
+LOG = logging.getLogger(__name__)
+
+
def _xform_common_availability_zone(az, zone_info):
if hasattr(az, 'zoneState'):
zone_info['zone_status'] = ('available' if az.zoneState['available']
@@ -92,17 +96,20 @@ class ListAvailabilityZone(command.Lister):
'--compute',
action='store_true',
default=False,
- help='List compute availability zones')
+ help=_('List compute availability zones'),
+ )
parser.add_argument(
'--network',
action='store_true',
default=False,
- help='List network availability zones')
+ help=_('List network availability zones'),
+ )
parser.add_argument(
'--volume',
action='store_true',
default=False,
- help='List volume availability zones')
+ help=_('List volume availability zones'),
+ )
parser.add_argument(
'--long',
action='store_true',
@@ -133,11 +140,11 @@ class ListAvailabilityZone(command.Lister):
try:
data = volume_client.availability_zones.list()
except Exception as e:
- self.log.debug('Volume availability zone exception: ' + str(e))
+ LOG.debug('Volume availability zone exception: %s', e)
if parsed_args.volume:
- message = "Availability zones list not supported by " \
- "Block Storage API"
- self.log.warning(message)
+ message = _("Availability zones list not supported by "
+ "Block Storage API")
+ LOG.warning(message)
result = []
for zone in data:
@@ -151,11 +158,11 @@ class ListAvailabilityZone(command.Lister):
network_client.find_extension('Availability Zone',
ignore_missing=False)
except Exception as e:
- self.log.debug('Network availability zone exception: ' + str(e))
+ LOG.debug('Network availability zone exception: ', e)
if parsed_args.network:
- message = "Availability zones list not supported by " \
- "Network API"
- self.log.warning(message)
+ message = _("Availability zones list not supported by "
+ "Network API")
+ LOG.warning(message)
return []
result = []
diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py
index 9c2b320c..3c35b529 100644
--- a/openstackclient/common/clientmanager.py
+++ b/openstackclient/common/clientmanager.py
@@ -20,12 +20,12 @@ import logging
import pkg_resources
import sys
+from osc_lib import exceptions
from oslo_utils import strutils
import requests
import six
from openstackclient.api import auth
-from openstackclient.common import exceptions
from openstackclient.common import session as osc_session
from openstackclient.identity import client as identity_client
@@ -140,10 +140,49 @@ class ClientManager(object):
# prior to dereferrencing auth_ref.
self._auth_setup_completed = False
- def setup_auth(self, required_scope=True):
- """Set up authentication
+ def _set_default_scope_options(self):
+ # 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(hieulq): If USER_DOMAIN_NAME, USER_DOMAIN_ID, PROJECT_DOMAIN_ID
+ # or PROJECT_DOMAIN_NAME is present and API_VERSION is 2.0, then
+ # ignore all domain related configs.
+ if (self._api_version.get('identity') == '2.0' and
+ self.auth_plugin_name.endswith('password')):
+ domain_props = ['project_domain_name', 'project_domain_id',
+ 'user_domain_name', 'user_domain_id']
+ for prop in domain_props:
+ if self._auth_params.pop(prop, None) is not None:
+ LOG.warning("Ignoring domain related configs " +
+ prop + " because identity API version is 2.0")
+ return
+
+ # NOTE(aloga): The scope parameters below only apply to v3 and v3
+ # related auth plugins, so we stop the parameter checking if v2 is
+ # being used.
+ if (self._api_version.get('identity') != '3' or
+ self.auth_plugin_name.startswith('v2')):
+ return
+
+ # 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 ('project_domain_id' in self._auth_params and
+ not self._auth_params.get('project_domain_id') and
+ not self._auth_params.get('project_domain_name')):
+ self._auth_params['project_domain_id'] = default_domain
+
+ # NOTE(stevemar): If USER_DOMAIN_ID or USER_DOMAIN_NAME is present,
+ # then do not change the behaviour. Otherwise, set the
+ # USER_DOMAIN_ID to 'OS_DEFAULT_DOMAIN' for better usability.
+ if ('user_domain_id' in self._auth_params and
+ not self._auth_params.get('user_domain_id') and
+ not self._auth_params.get('user_domain_name')):
+ self._auth_params['user_domain_id'] = default_domain
- :param required_scope: indicate whether a scoped token is required
+ def setup_auth(self):
+ """Set up authentication
This is deferred until authentication is actually attempted because
it gets in the way of things that do not require auth.
@@ -157,9 +196,8 @@ class ClientManager(object):
self.auth_plugin_name = auth.select_auth_plugin(self._cli_options)
# Basic option checking to avoid unhelpful error messages
- auth.check_valid_auth_options(self._cli_options,
- self.auth_plugin_name,
- required_scope=required_scope)
+ auth.check_valid_authentication_options(self._cli_options,
+ self.auth_plugin_name)
# Horrible hack alert...must handle prompt for null password if
# password auth is requested.
@@ -172,40 +210,7 @@ class ClientManager(object):
self._cli_options,
)
- # 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
- self.auth_plugin_name.endswith('password') and
- not self._auth_params.get('project_domain_id') and
- not self.auth_plugin_name.startswith('v2') and
- not self._auth_params.get('project_domain_name')):
- self._auth_params['project_domain_id'] = default_domain
-
- # NOTE(stevemar): If USER_DOMAIN_ID or USER_DOMAIN_NAME is present,
- # then do not change the behaviour. Otherwise, set the USER_DOMAIN_ID
- # 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_plugin_name.startswith('v2') and
- not self._auth_params.get('user_domain_id') and
- not self._auth_params.get('user_domain_name')):
- self._auth_params['user_domain_id'] = default_domain
-
- # NOTE(hieulq): If USER_DOMAIN_NAME, USER_DOMAIN_ID, PROJECT_DOMAIN_ID
- # or PROJECT_DOMAIN_NAME is present and API_VERSION is 2.0, then
- # ignore all domain related configs.
- if (self._api_version.get('identity') == '2.0' and
- self.auth_plugin_name.endswith('password')):
- domain_props = ['project_domain_name', 'project_domain_id',
- 'user_domain_name', 'user_domain_id']
- for prop in domain_props:
- if self._auth_params.pop(prop, None) is not None:
- LOG.warning("Ignoring domain related configs " +
- prop + " because identity API version is 2.0")
+ self._set_default_scope_options()
# For compatibility until all clients can be updated
if 'project_name' in self._auth_params:
@@ -229,6 +234,20 @@ class ClientManager(object):
self._auth_setup_completed = True
+ def validate_scope(self):
+ if self._auth_ref.project_id is not None:
+ # We already have a project scope.
+ return
+ if self._auth_ref.domain_id is not None:
+ # We already have a domain scope.
+ return
+
+ # We do not have a scoped token (and the user's default project scope
+ # was not implied), so the client needs to be explicitly configured
+ # with a scope.
+ auth.check_valid_authorization_options(self._cli_options,
+ self.auth_plugin_name)
+
@property
def auth_ref(self):
"""Dereference will trigger an auth if it hasn't already"""
@@ -269,7 +288,7 @@ class ClientManager(object):
endpoint = self.auth_ref.service_catalog.url_for(
service_type=service_type,
region_name=region_name,
- endpoint_type=interface,
+ interface=interface,
)
else:
# Get the passed endpoint directly from the auth plugin
diff --git a/openstackclient/common/command.py b/openstackclient/common/command.py
index 144a0db1..29c1534d 100644
--- a/openstackclient/common/command.py
+++ b/openstackclient/common/command.py
@@ -12,45 +12,15 @@
# License for the specific language governing permissions and limitations
# under the License.
-import abc
-import logging
+# NOTE(dtroyer): This file is deprecated in Jun 2016, remove after 4.x release
+# or Jun 2017.
-from cliff import command
-from cliff import lister
-from cliff import show
-import six
+import sys
-from openstackclient.common import exceptions
-from openstackclient.i18n import _
+from osc_lib.command.command import * # noqa
-class CommandMeta(abc.ABCMeta):
-
- def __new__(mcs, name, bases, cls_dict):
- if 'log' not in cls_dict:
- cls_dict['log'] = logging.getLogger(
- cls_dict['__module__'] + '.' + name)
- return super(CommandMeta, mcs).__new__(mcs, name, bases, cls_dict)
-
-
-@six.add_metaclass(CommandMeta)
-class Command(command.Command):
-
- def run(self, parsed_args):
- self.log.debug('run(%s)', parsed_args)
- return super(Command, self).run(parsed_args)
-
- def validate_os_beta_command_enabled(self):
- if not self.app.options.os_beta_command:
- msg = _('Caution: This is a beta command and subject to '
- 'change. Use global option --os-beta-command '
- 'to enable this command.')
- raise exceptions.CommandError(msg)
-
-
-class Lister(Command, lister.Lister):
- pass
-
-
-class ShowOne(Command, show.ShowOne):
- pass
+sys.stderr.write(
+ "WARNING: %s is deprecated and will be removed after Jun 2017. "
+ "Please use osc_lib.command.command\n" % __name__
+)
diff --git a/openstackclient/common/configuration.py b/openstackclient/common/configuration.py
index a70e4d14..016e9191 100644
--- a/openstackclient/common/configuration.py
+++ b/openstackclient/common/configuration.py
@@ -13,9 +13,11 @@
"""Configuration action implementations"""
+from keystoneauth1.loading import base
+from osc_lib.command import command
import six
-from openstackclient.common import command
+from openstackclient.i18n import _
REDACTED = "<redacted>"
@@ -31,24 +33,25 @@ class ShowConfiguration(command.ShowOne):
dest="mask",
action="store_true",
default=True,
- help="Attempt to mask passwords (default)",
+ help=_("Attempt to mask passwords (default)"),
)
mask_group.add_argument(
"--unmask",
dest="mask",
action="store_false",
- help="Show password in clear text",
+ help=_("Show password in clear text"),
)
return parser
def take_action(self, parsed_args):
+ auth_plg_name = self.app.client_manager.auth_plugin_name
+ secret_opts = [o.dest for o in base.get_plugin_options(auth_plg_name)
+ if o.secret]
+
info = self.app.client_manager.get_configuration()
for key, value in six.iteritems(info.pop('auth', {})):
- if parsed_args.mask:
- if 'password' in key.lower():
- value = REDACTED
- if 'token' in key.lower():
+ if parsed_args.mask and key.lower() in secret_opts:
value = REDACTED
info['auth.' + key] = value
return zip(*sorted(six.iteritems(info)))
diff --git a/openstackclient/common/exceptions.py b/openstackclient/common/exceptions.py
index bdc33ddb..7124074c 100644
--- a/openstackclient/common/exceptions.py
+++ b/openstackclient/common/exceptions.py
@@ -1,5 +1,3 @@
-# Copyright 2012-2013 OpenStack, LLC.
-#
# 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
@@ -13,105 +11,15 @@
# under the License.
#
-"""Exception definitions."""
-
-
-class CommandError(Exception):
- pass
-
-
-class AuthorizationFailure(Exception):
- pass
-
-
-class PluginAttributeError(Exception):
- """A plugin threw an AttributeError while being lazily loaded."""
- # This *must not* inherit from AttributeError;
- # that would defeat the whole purpose.
- pass
-
-
-class NoTokenLookupException(Exception):
- """This does not support looking up endpoints from an existing token."""
- pass
-
-
-class EndpointNotFound(Exception):
- """Could not find Service or Region in Service Catalog."""
- pass
-
-
-class UnsupportedVersion(Exception):
- """The user is trying to use an unsupported version of the API"""
- pass
-
-
-class ClientException(Exception):
- """The base exception class for all exceptions this library raises."""
+# NOTE(dtroyer): This file is deprecated in Jun 2016, remove after 4.x release
+# or Jun 2017.
- def __init__(self, code, message=None, details=None):
- self.code = code
- self.message = message or self.__class__.message
- self.details = details
+import sys
- def __str__(self):
- return "%s (HTTP %s)" % (self.message, self.code)
+from osc_lib.exceptions import * # noqa
-class BadRequest(ClientException):
- """HTTP 400 - Bad request: you sent some malformed data."""
- http_status = 400
- message = "Bad request"
-
-
-class Unauthorized(ClientException):
- """HTTP 401 - Unauthorized: bad credentials."""
- http_status = 401
- message = "Unauthorized"
-
-
-class Forbidden(ClientException):
- """HTTP 403 - Forbidden: not authorized to access to this resource."""
- http_status = 403
- message = "Forbidden"
-
-
-class NotFound(ClientException):
- """HTTP 404 - Not found"""
- http_status = 404
- message = "Not found"
-
-
-class Conflict(ClientException):
- """HTTP 409 - Conflict"""
- http_status = 409
- message = "Conflict"
-
-
-class OverLimit(ClientException):
- """HTTP 413 - Over limit: reached the API limits for this time period."""
- http_status = 413
- message = "Over limit"
-
-
-# NotImplemented is a python keyword.
-class HTTPNotImplemented(ClientException):
- """HTTP 501 - Not Implemented: server does not support this operation."""
- http_status = 501
- message = "Not Implemented"
-
-
-# In Python 2.4 Exception is old-style and thus doesn't have a __subclasses__()
-# so we can do this:
-# _code_map = dict((c.http_status, c)
-# for c in ClientException.__subclasses__())
-#
-# Instead, we have to hardcode it:
-_code_map = dict((c.http_status, c) for c in [
- BadRequest,
- Unauthorized,
- Forbidden,
- NotFound,
- OverLimit,
- HTTPNotImplemented
-])
+sys.stderr.write(
+ "WARNING: %s is deprecated and will be removed after Jun 2017. "
+ "Please use osc_lib.exceptions\n" % __name__
+)
diff --git a/openstackclient/common/extension.py b/openstackclient/common/extension.py
index 8b556b4c..de480016 100644
--- a/openstackclient/common/extension.py
+++ b/openstackclient/common/extension.py
@@ -16,9 +16,15 @@
"""Extension action implementations"""
import itertools
+import logging
-from openstackclient.common import command
-from openstackclient.common import utils
+from osc_lib.command import command
+from osc_lib import utils
+
+from openstackclient.i18n import _
+
+
+LOG = logging.getLogger(__name__)
class ListExtension(command.Lister):
@@ -30,27 +36,32 @@ class ListExtension(command.Lister):
'--compute',
action='store_true',
default=False,
- help='List extensions for the Compute API')
+ help=_('List extensions for the Compute API'),
+ )
parser.add_argument(
'--identity',
action='store_true',
default=False,
- help='List extensions for the Identity API')
+ help=_('List extensions for the Identity API'),
+ )
parser.add_argument(
'--network',
action='store_true',
default=False,
- help='List extensions for the Network API')
+ help=_('List extensions for the Network API'),
+ )
parser.add_argument(
'--volume',
action='store_true',
default=False,
- help='List extensions for the Block Storage API')
+ help=_('List extensions for the Block Storage API'),
+ )
parser.add_argument(
'--long',
action='store_true',
default=False,
- help='List additional fields in output')
+ help=_('List additional fields in output'),
+ )
return parser
def take_action(self, parsed_args):
@@ -73,24 +84,25 @@ class ListExtension(command.Lister):
try:
data += identity_client.extensions.list()
except Exception:
- message = "Extensions list not supported by Identity API"
- self.log.warning(message)
+ message = _("Extensions list not supported by Identity API")
+ LOG.warning(message)
if parsed_args.compute or show_all:
compute_client = self.app.client_manager.compute
try:
data += compute_client.list_extensions.show_all()
except Exception:
- message = "Extensions list not supported by Compute API"
- self.log.warning(message)
+ message = _("Extensions list not supported by Compute API")
+ LOG.warning(message)
if parsed_args.volume or show_all:
volume_client = self.app.client_manager.volume
try:
data += volume_client.list_extensions.show_all()
except Exception:
- message = "Extensions list not supported by Block Storage API"
- self.log.warning(message)
+ message = _("Extensions list not supported by "
+ "Block Storage API")
+ LOG.warning(message)
# Resource classes for the above
extension_tuples = (
@@ -118,7 +130,7 @@ class ListExtension(command.Lister):
dict_tuples
)
except Exception:
- message = "Extensions list not supported by Network API"
- self.log.warning(message)
+ message = _("Extensions list not supported by Network API")
+ LOG.warning(message)
return (columns, extension_tuples)
diff --git a/openstackclient/common/limits.py b/openstackclient/common/limits.py
index 1f87abf3..f7aa82f6 100644
--- a/openstackclient/common/limits.py
+++ b/openstackclient/common/limits.py
@@ -17,8 +17,10 @@
import itertools
-from openstackclient.common import command
-from openstackclient.common import utils
+from osc_lib.command import command
+from osc_lib import utils
+
+from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
@@ -33,30 +35,33 @@ class ShowLimits(command.Lister):
dest="is_absolute",
action="store_true",
default=False,
- help="Show absolute limits")
+ help=_("Show absolute limits"),
+ )
type_group.add_argument(
"--rate",
dest="is_rate",
action="store_true",
default=False,
- help="Show rate limits")
+ help=_("Show rate limits"),
+ )
parser.add_argument(
"--reserved",
dest="is_reserved",
action="store_true",
default=False,
- help="Include reservations count [only valid with --absolute]")
+ 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]',
+ help=_('Show limits for a specific project (name or ID)'
+ ' [only valid with --absolute]'),
)
parser.add_argument(
'--domain',
metavar='<domain>',
- help='Domain the project belongs to (name or ID)'
- ' [only valid with --absolute]',
+ help=_('Domain the project belongs to (name or ID)'
+ ' [only valid with --absolute]'),
)
return parser
diff --git a/openstackclient/common/logs.py b/openstackclient/common/logs.py
index 221c5997..8aa97d5b 100644
--- a/openstackclient/common/logs.py
+++ b/openstackclient/common/logs.py
@@ -11,188 +11,16 @@
# under the License.
#
-"""Application logging"""
+# NOTE(dtroyer): This file is deprecated in Jun 2016, remove after 4.x release
+# or Jun 2017.
-import logging
import sys
-import warnings
+from osc_lib.logs import * # noqa
+from osc_lib.logs import _FileFormatter # noqa
-def get_loggers():
- loggers = {}
- for logkey in logging.Logger.manager.loggerDict.keys():
- loggers[logkey] = logging.getLevelName(logging.getLogger(logkey).level)
- return loggers
-
-def log_level_from_options(options):
- # if --debug, --quiet or --verbose is not specified,
- # the default logging level is warning
- log_level = logging.WARNING
- if options.verbose_level == 0:
- # --quiet
- log_level = logging.ERROR
- elif options.verbose_level == 2:
- # One --verbose
- log_level = logging.INFO
- elif options.verbose_level >= 3:
- # Two or more --verbose
- log_level = logging.DEBUG
- return log_level
-
-
-def log_level_from_string(level_string):
- log_level = {
- 'critical': logging.CRITICAL,
- 'error': logging.ERROR,
- 'warning': logging.WARNING,
- 'info': logging.INFO,
- 'debug': logging.DEBUG,
- }.get(level_string, logging.WARNING)
- return log_level
-
-
-def log_level_from_config(config):
- # Check the command line option
- verbose_level = config.get('verbose_level')
- if config.get('debug', False):
- verbose_level = 3
- if verbose_level == 0:
- verbose_level = 'error'
- elif verbose_level == 1:
- # If a command line option has not been specified, check the
- # configuration file
- verbose_level = config.get('log_level', 'warning')
- elif verbose_level == 2:
- verbose_level = 'info'
- else:
- verbose_level = 'debug'
- return log_level_from_string(verbose_level)
-
-
-def set_warning_filter(log_level):
- if log_level == logging.ERROR:
- warnings.simplefilter("ignore")
- elif log_level == logging.WARNING:
- warnings.simplefilter("ignore")
- elif log_level == logging.INFO:
- warnings.simplefilter("once")
-
-
-class _FileFormatter(logging.Formatter):
- """Customize the logging format for logging handler"""
- _LOG_MESSAGE_BEGIN = (
- '%(asctime)s.%(msecs)03d %(process)d %(levelname)s %(name)s ')
- _LOG_MESSAGE_CONTEXT = '[%(cloud)s %(username)s %(project)s] '
- _LOG_MESSAGE_END = '%(message)s'
- _LOG_DATE_FORMAT = '%Y-%m-%d %H:%M:%S'
-
- def __init__(self, options=None, config=None, **kwargs):
- context = {}
- if options:
- context = {
- 'cloud': getattr(options, 'cloud', ''),
- 'project': getattr(options, 'os_project_name', ''),
- 'username': getattr(options, 'username', ''),
- }
- elif config:
- context = {
- 'cloud': config.config.get('cloud', ''),
- 'project': config.auth.get('project_name', ''),
- 'username': config.auth.get('username', ''),
- }
- if context:
- self.fmt = (self._LOG_MESSAGE_BEGIN +
- (self._LOG_MESSAGE_CONTEXT % context) +
- self._LOG_MESSAGE_END)
- else:
- self.fmt = self._LOG_MESSAGE_BEGIN + self._LOG_MESSAGE_END
- logging.Formatter.__init__(self, self.fmt, self._LOG_DATE_FORMAT)
-
-
-class LogConfigurator(object):
-
- _CONSOLE_MESSAGE_FORMAT = '%(message)s'
-
- def __init__(self, options):
- self.root_logger = logging.getLogger('')
- self.root_logger.setLevel(logging.DEBUG)
-
- # Force verbose_level 3 on --debug
- self.dump_trace = False
- if options.debug:
- options.verbose_level = 3
- self.dump_trace = True
-
- # Always send higher-level messages to the console via stderr
- self.console_logger = logging.StreamHandler(sys.stderr)
- log_level = log_level_from_options(options)
- self.console_logger.setLevel(log_level)
- formatter = logging.Formatter(self._CONSOLE_MESSAGE_FORMAT)
- self.console_logger.setFormatter(formatter)
- self.root_logger.addHandler(self.console_logger)
-
- # Set the warning filter now
- set_warning_filter(log_level)
-
- # Set up logging to a file
- self.file_logger = None
- log_file = options.log_file
- if log_file:
- self.file_logger = logging.FileHandler(filename=log_file)
- self.file_logger.setFormatter(_FileFormatter(options=options))
- self.file_logger.setLevel(log_level)
- self.root_logger.addHandler(self.file_logger)
-
- # Requests logs some stuff at INFO that we don't want
- # unless we have DEBUG
- requests_log = logging.getLogger("requests")
-
- # Other modules we don't want DEBUG output for
- cliff_log = logging.getLogger('cliff')
- stevedore_log = logging.getLogger('stevedore')
- iso8601_log = logging.getLogger("iso8601")
-
- if options.debug:
- # --debug forces traceback
- requests_log.setLevel(logging.DEBUG)
- else:
- requests_log.setLevel(logging.ERROR)
-
- cliff_log.setLevel(logging.ERROR)
- stevedore_log.setLevel(logging.ERROR)
- iso8601_log.setLevel(logging.ERROR)
-
- def configure(self, cloud_config):
- log_level = log_level_from_config(cloud_config.config)
- set_warning_filter(log_level)
- self.dump_trace = cloud_config.config.get('debug', self.dump_trace)
- self.console_logger.setLevel(log_level)
-
- log_file = cloud_config.config.get('log_file')
- if log_file:
- if not self.file_logger:
- self.file_logger = logging.FileHandler(filename=log_file)
- formatter = _FileFormatter(cloud_config=cloud_config)
- self.file_logger.setFormatter(formatter)
- self.file_logger.setFormatter(_FileFormatter(config=cloud_config))
- self.file_logger.setLevel(log_level)
- self.root_logger.addHandler(self.file_logger)
-
- logconfig = cloud_config.config.get('logging')
- if logconfig:
- highest_level = logging.NOTSET
- for k in logconfig.keys():
- level = log_level_from_string(logconfig[k])
- logging.getLogger(k).setLevel(level)
- if (highest_level < level):
- highest_level = level
- self.console_logger.setLevel(highest_level)
- if self.file_logger:
- self.file_logger.setLevel(highest_level)
- # loggers that are not set will use the handler level, so we
- # need to set the global level for all the loggers
- for logkey in logging.Logger.manager.loggerDict.keys():
- logger = logging.getLogger(logkey)
- if logger.level == logging.NOTSET:
- logger.setLevel(log_level)
+sys.stderr.write(
+ "WARNING: %s is deprecated and will be removed after Jun 2017. "
+ "Please use osc_lib.logs\n" % __name__
+)
diff --git a/openstackclient/common/module.py b/openstackclient/common/module.py
index 30c67c68..7c5fcd55 100644
--- a/openstackclient/common/module.py
+++ b/openstackclient/common/module.py
@@ -15,11 +15,13 @@
"""Module action implementation"""
-import six
import sys
-from openstackclient.common import command
-from openstackclient.common import utils
+from osc_lib.command import command
+from osc_lib import utils
+import six
+
+from openstackclient.i18n import _
class ListCommand(command.Lister):
@@ -61,7 +63,7 @@ class ListModule(command.ShowOne):
'--all',
action='store_true',
default=False,
- help='Show all modules that have version information',
+ help=_('Show all modules that have version information'),
)
return parser
diff --git a/openstackclient/common/parseractions.py b/openstackclient/common/parseractions.py
index 77798f90..fa5148ec 100644
--- a/openstackclient/common/parseractions.py
+++ b/openstackclient/common/parseractions.py
@@ -1,5 +1,3 @@
-# Copyright 2013 OpenStack Foundation
-#
# 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
@@ -13,150 +11,15 @@
# under the License.
#
-"""argparse Custom Actions"""
-
-import argparse
-
-from openstackclient.i18n import _
-
-
-class KeyValueAction(argparse.Action):
- """A custom action to parse arguments as key=value pairs
-
- Ensures that ``dest`` is a dict
- """
-
- def __call__(self, parser, namespace, values, option_string=None):
- # Make sure we have an empty dict rather than None
- if getattr(namespace, self.dest, None) is None:
- setattr(namespace, self.dest, {})
-
- # Add value if an assignment else remove it
- if '=' in values:
- getattr(namespace, self.dest, {}).update([values.split('=', 1)])
- else:
- getattr(namespace, self.dest, {}).pop(values, None)
-
-
-class MultiKeyValueAction(argparse.Action):
- """A custom action to parse arguments as key1=value1,key2=value2 pairs
-
- Ensure that ``dest`` is a list. The list will finally contain multiple
- dicts, with key=value pairs in them.
-
- NOTE: The arguments string should be a comma separated key-value pairs.
- And comma(',') and equal('=') may not be used in the key or value.
- """
-
- def __init__(self, option_strings, dest, nargs=None,
- required_keys=None, optional_keys=None, **kwargs):
- """Initialize the action object, and parse customized options
-
- Required keys and optional keys can be specified when initializing
- the action to enable the key validation. If none of them specified,
- the key validation will be skipped.
-
- :param required_keys: a list of required keys
- :param optional_keys: a list of optional keys
- """
- if nargs:
- raise ValueError("Parameter 'nargs' is not allowed, but got %s"
- % nargs)
-
- super(MultiKeyValueAction, self).__init__(option_strings,
- dest, **kwargs)
-
- # required_keys: A list of keys that is required. None by default.
- if required_keys and not isinstance(required_keys, list):
- raise TypeError("'required_keys' must be a list")
- self.required_keys = set(required_keys or [])
-
- # optional_keys: A list of keys that is optional. None by default.
- if optional_keys and not isinstance(optional_keys, list):
- raise TypeError("'optional_keys' must be a list")
- self.optional_keys = set(optional_keys or [])
-
- def __call__(self, parser, namespace, values, metavar=None):
- # Make sure we have an empty list rather than None
- if getattr(namespace, self.dest, None) is None:
- setattr(namespace, self.dest, [])
-
- params = {}
- for kv in values.split(','):
- # Add value if an assignment else raise ArgumentTypeError
- if '=' in kv:
- params.update([kv.split('=', 1)])
- else:
- msg = ("Expected key=value pairs separated by comma, "
- "but got: %s" % (str(kv)))
- raise argparse.ArgumentTypeError(msg)
-
- # Check key validation
- valid_keys = self.required_keys | self.optional_keys
- if valid_keys:
- invalid_keys = [k for k in params if k not in valid_keys]
- if invalid_keys:
- msg = _("Invalid keys %(invalid_keys)s specified.\n"
- "Valid keys are: %(valid_keys)s.")
- raise argparse.ArgumentTypeError(
- msg % {'invalid_keys': ', '.join(invalid_keys),
- 'valid_keys': ', '.join(valid_keys)}
- )
-
- if self.required_keys:
- missing_keys = [k for k in self.required_keys if k not in params]
- if missing_keys:
- msg = _("Missing required keys %(missing_keys)s.\n"
- "Required keys are: %(required_keys)s.")
- raise argparse.ArgumentTypeError(
- msg % {'missing_keys': ', '.join(missing_keys),
- 'required_keys': ', '.join(self.required_keys)}
- )
-
- # Update the dest dict
- getattr(namespace, self.dest, []).append(params)
-
-
-class RangeAction(argparse.Action):
- """A custom action to parse a single value or a range of values
-
- Parses single integer values or a range of integer values delimited
- by a colon and returns a tuple of integers:
- '4' sets ``dest`` to (4, 4)
- '6:9' sets ``dest`` to (6, 9)
- """
-
- def __call__(self, parser, namespace, values, option_string=None):
- range = values.split(':')
- if len(range) == 0:
- # Nothing passed, return a zero default
- setattr(namespace, self.dest, (0, 0))
- elif len(range) == 1:
- # Only a single value is present
- setattr(namespace, self.dest, (int(range[0]), int(range[0])))
- elif len(range) == 2:
- # Range of two values
- if int(range[0]) <= int(range[1]):
- setattr(namespace, self.dest, (int(range[0]), int(range[1])))
- else:
- msg = "Invalid range, %s is not less than %s" % \
- (range[0], range[1])
- raise argparse.ArgumentError(self, msg)
- else:
- # Too many values
- msg = "Invalid range, too many values"
- raise argparse.ArgumentError(self, msg)
+# NOTE(dtroyer): This file is deprecated in Jun 2016, remove after 4.x release
+# or Jun 2017.
+import sys
-class NonNegativeAction(argparse.Action):
- """A custom action to check whether the value is non-negative or not
+from osc_lib.cli.parseractions import * # noqa
- Ensures the value is >= 0.
- """
- def __call__(self, parser, namespace, values, option_string=None):
- if int(values) >= 0:
- setattr(namespace, self.dest, values)
- else:
- msg = "%s expected a non-negative integer" % (str(option_string))
- raise argparse.ArgumentTypeError(msg)
+sys.stderr.write(
+ "WARNING: %s is deprecated and will be removed after Jun 2017. "
+ "Please use osc_lib.cli.parseractions\n" % __name__
+)
diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py
index f85d550b..69415f0d 100644
--- a/openstackclient/common/quota.py
+++ b/openstackclient/common/quota.py
@@ -16,11 +16,13 @@
"""Quota action implementations"""
import itertools
-import six
import sys
-from openstackclient.common import command
-from openstackclient.common import utils
+from osc_lib.command import command
+from osc_lib import utils
+import six
+
+from openstackclient.i18n import _
# List the quota items, map the internal argument name to the option
@@ -84,14 +86,14 @@ class SetQuota(command.Command):
parser.add_argument(
'project',
metavar='<project/class>',
- help='Set quotas for this project or class (name/ID)',
+ help=_('Set quotas for this project or class (name/ID)'),
)
parser.add_argument(
'--class',
dest='quota_class',
action='store_true',
default=False,
- help='Set quotas for <class>',
+ help=_('Set quotas for <class>'),
)
for k, v in self._build_options_list():
parser.add_argument(
@@ -99,12 +101,12 @@ class SetQuota(command.Command):
metavar='<%s>' % v,
dest=k,
type=int,
- help='New value for the %s quota' % v,
+ help=_('New value for the %s quota') % v,
)
parser.add_argument(
'--volume-type',
metavar='<volume-type>',
- help='Set quotas for a specific <volume-type>',
+ help=_('Set quotas for a specific <volume-type>'),
)
return parser
@@ -140,11 +142,6 @@ class SetQuota(command.Command):
if value is not None:
compute_kwargs[k] = value
- if (compute_kwargs == {} and volume_kwargs == {}
- and network_kwargs == {}):
- sys.stderr.write("No quotas updated\n")
- return
-
if parsed_args.project:
project = utils.find_resource(
identity_client.projects,
@@ -187,7 +184,7 @@ class ShowQuota(command.ShowOne):
'project',
metavar='<project/class>',
nargs='?',
- help='Show quotas for this project or class (name or ID)',
+ help=_('Show quotas for this project or class (name or ID)'),
)
type_group = parser.add_mutually_exclusive_group()
type_group.add_argument(
@@ -195,14 +192,14 @@ class ShowQuota(command.ShowOne):
dest='quota_class',
action='store_true',
default=False,
- help='Show quotas for <class>',
+ help=_('Show quotas for <class>'),
)
type_group.add_argument(
'--default',
dest='default',
action='store_true',
default=False,
- help='Show default quotas for <project>'
+ help=_('Show default quotas for <project>')
)
return parser
diff --git a/openstackclient/common/timing.py b/openstackclient/common/timing.py
index 71c2fec7..facbec35 100644
--- a/openstackclient/common/timing.py
+++ b/openstackclient/common/timing.py
@@ -11,31 +11,15 @@
# under the License.
#
-"""Timing Implementation"""
+# NOTE(dtroyer): This file is deprecated in Jun 2016, remove after 4.x release
+# or Jun 2017.
-from openstackclient.common import command
+import sys
+from osc_lib.command.timing import * # noqa
-class Timing(command.Lister):
- """Show timing data"""
- def take_action(self, parsed_args):
- column_headers = (
- 'URL',
- 'Seconds',
- )
-
- results = []
- total = 0.0
- 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,
- results,
- )
+sys.stderr.write(
+ "WARNING: %s is deprecated and will be removed after Jun 2017. "
+ "Please use osc_lib.command.timing\n" % __name__
+)
diff --git a/openstackclient/common/utils.py b/openstackclient/common/utils.py
index daa65c25..73cd3dc9 100644
--- a/openstackclient/common/utils.py
+++ b/openstackclient/common/utils.py
@@ -1,5 +1,3 @@
-# Copyright 2012-2013 OpenStack Foundation
-#
# 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
@@ -13,410 +11,15 @@
# under the License.
#
-"""Common client utilities"""
-
-import getpass
-import logging
-import os
-import six
-import time
-
-from oslo_utils import importutils
-
-from openstackclient.common import exceptions
-
-
-def find_resource(manager, name_or_id, **kwargs):
- """Helper for the _find_* methods.
-
- :param manager: A client manager class
- :param name_or_id: The resource we are trying to find
- :param kwargs: To be used in calling .find()
- :rtype: The found resource
-
- This method will attempt to find a resource in a variety of ways.
- Primarily .get() methods will be called with `name_or_id` as an integer
- value, and tried again as a string value.
-
- If both fail, then a .find() is attempted, which is essentially calling
- a .list() function with a 'name' query parameter that is set to
- `name_or_id`.
-
- Lastly, if any kwargs are passed in, they will be treated as additional
- query parameters. This is particularly handy in the case of finding
- resources in a domain.
-
- """
-
- # Try to get entity as integer id
- try:
- if isinstance(name_or_id, int) or name_or_id.isdigit():
- return manager.get(int(name_or_id), **kwargs)
- # FIXME(dtroyer): The exception to catch here is dependent on which
- # client library the manager passed in belongs to.
- # Eventually this should be pulled from a common set
- # of client exceptions.
- except Exception as ex:
- if type(ex).__name__ == 'NotFound':
- pass
- else:
- raise
-
- # Try directly using the passed value
- try:
- return manager.get(name_or_id, **kwargs)
- except Exception:
- pass
-
- if len(kwargs) == 0:
- kwargs = {}
-
- try:
- # Prepare the kwargs for calling find
- if 'NAME_ATTR' in manager.resource_class.__dict__:
- # novaclient does this for oddball resources
- kwargs[manager.resource_class.NAME_ATTR] = name_or_id
- else:
- kwargs['name'] = name_or_id
- except Exception:
- pass
-
- # finally try to find entity by name
- try:
- return manager.find(**kwargs)
- # FIXME(dtroyer): The exception to catch here is dependent on which
- # client library the manager passed in belongs to.
- # Eventually this should be pulled from a common set
- # of client exceptions.
- except Exception as ex:
- if type(ex).__name__ == 'NotFound':
- msg = "No %s with a name or ID of '%s' exists." % \
- (manager.resource_class.__name__.lower(), name_or_id)
- raise exceptions.CommandError(msg)
- if type(ex).__name__ == 'NoUniqueMatch':
- msg = "More than one %s exists with the name '%s'." % \
- (manager.resource_class.__name__.lower(), name_or_id)
- raise exceptions.CommandError(msg)
- else:
- pass
-
- for resource in manager.list():
- # short circuit and return the first match
- if (resource.get('id') == name_or_id or
- resource.get('name') == name_or_id):
- return resource
- else:
- # we found no match, report back this error:
- msg = "Could not find resource %s" % name_or_id
- raise exceptions.CommandError(msg)
-
-
-def format_dict(data):
- """Return a formatted string of key value pairs
-
- :param data: a dict
- :rtype: a string formatted to key='value'
- """
-
- output = ""
- for s in sorted(data):
- output = output + s + "='" + six.text_type(data[s]) + "', "
- return output[:-2]
-
-
-def format_list(data, separator=', '):
- """Return a formatted strings
-
- :param data: a list of strings
- :param separator: the separator to use between strings (default: ', ')
- :rtype: a string formatted based on separator
- """
-
- return separator.join(sorted(data))
-
-
-def format_list_of_dicts(data):
- """Return a formatted string of key value pairs for each dict
-
- :param data: a list of dicts
- :rtype: a string formatted to key='value' with dicts separated by new line
- """
-
- return '\n'.join(format_dict(i) for i in data)
-
-
-def get_field(item, field):
- try:
- if isinstance(item, dict):
- return item[field]
- else:
- return getattr(item, field)
- except Exception:
- msg = "Resource doesn't have field %s" % field
- raise exceptions.CommandError(msg)
-
-
-def get_item_properties(item, fields, mixed_case_fields=None, formatters=None):
- """Return a tuple containing the item properties.
-
- :param item: a single item resource (e.g. Server, Project, etc)
- :param fields: tuple of strings with the desired field names
- :param mixed_case_fields: tuple of field names to preserve case
- :param formatters: dictionary mapping field names to callables
- to format the values
- """
- if mixed_case_fields is None:
- mixed_case_fields = []
- if formatters is None:
- formatters = {}
-
- row = []
-
- for field in fields:
- if field in mixed_case_fields:
- field_name = field.replace(' ', '_')
- else:
- field_name = field.lower().replace(' ', '_')
- data = getattr(item, field_name, '')
- if field in formatters:
- row.append(formatters[field](data))
- else:
- row.append(data)
- return tuple(row)
-
-
-def get_dict_properties(item, fields, mixed_case_fields=None, formatters=None):
- """Return a tuple containing the item properties.
-
- :param item: a single dict resource
- :param fields: tuple of strings with the desired field names
- :param mixed_case_fields: tuple of field names to preserve case
- :param formatters: dictionary mapping field names to callables
- to format the values
- """
- if mixed_case_fields is None:
- mixed_case_fields = []
- if formatters is None:
- formatters = {}
-
- row = []
-
- for field in fields:
- if field in mixed_case_fields:
- field_name = field.replace(' ', '_')
- else:
- field_name = field.lower().replace(' ', '_')
- data = item[field_name] if field_name in item else ''
- if field in formatters:
- row.append(formatters[field](data))
- else:
- row.append(data)
- return tuple(row)
-
-
-def sort_items(items, sort_str):
- """Sort items based on sort keys and sort directions given by sort_str.
-
- :param items: a list or generator object of items
- :param sort_str: a string defining the sort rules, the format is
- '<key1>:[direction1],<key2>:[direction2]...', direction can be 'asc'
- for ascending or 'desc' for descending, if direction is not given,
- it's ascending by default
- :return: sorted items
- """
- if not sort_str:
- return items
- # items may be a generator object, transform it to a list
- items = list(items)
- sort_keys = sort_str.strip().split(',')
- for sort_key in reversed(sort_keys):
- reverse = False
- if ':' in sort_key:
- sort_key, direction = sort_key.split(':', 1)
- if not sort_key:
- msg = "empty string is not a valid sort key"
- raise exceptions.CommandError(msg)
- if direction not in ['asc', 'desc']:
- if not direction:
- direction = "empty string"
- msg = ("%s is not a valid sort direction for sort key %s, "
- "use asc or desc instead" % (direction, sort_key))
- raise exceptions.CommandError(msg)
- if direction == 'desc':
- reverse = True
- items.sort(key=lambda item: get_field(item, sort_key),
- reverse=reverse)
- return items
-
-
-def env(*vars, **kwargs):
- """Search for the first defined of possibly many env vars
-
- Returns the first environment variable defined in vars, or
- returns the default defined in kwargs.
- """
- for v in vars:
- value = os.environ.get(v, None)
- if value:
- return value
- return kwargs.get('default', '')
-
-
-def get_client_class(api_name, version, version_map):
- """Returns the client class for the requested API version
-
- :param api_name: the name of the API, e.g. 'compute', 'image', etc
- :param version: the requested API version
- :param version_map: a dict of client classes keyed by version
- :rtype: a client class for the requested API version
- """
- try:
- client_path = version_map[str(version)]
- except (KeyError, ValueError):
- msg = "Invalid %s client version '%s'. must be one of: %s" % (
- (api_name, version, ', '.join(list(version_map.keys()))))
- raise exceptions.UnsupportedVersion(msg)
-
- return importutils.import_class(client_path)
-
-
-def wait_for_status(status_f,
- res_id,
- status_field='status',
- success_status=['active'],
- error_status=['error'],
- sleep_time=5,
- callback=None):
- """Wait for status change on a resource during a long-running operation
-
- :param status_f: a status function that takes a single id argument
- :param res_id: the resource id to watch
- :param status_field: the status attribute in the returned resource object
- :param success_status: a list of status strings for successful completion
- :param error_status: a list of status strings for error
- :param sleep_time: wait this long (seconds)
- :param callback: called per sleep cycle, useful to display progress
- :rtype: True on success
- """
- while True:
- res = status_f(res_id)
- status = getattr(res, status_field, '').lower()
- if status in success_status:
- retval = True
- break
- elif status in error_status:
- retval = False
- break
- if callback:
- progress = getattr(res, 'progress', None) or 0
- callback(progress)
- time.sleep(sleep_time)
- return retval
-
-
-def wait_for_delete(manager,
- res_id,
- status_field='status',
- error_status=['error'],
- exception_name=['NotFound'],
- sleep_time=5,
- timeout=300,
- callback=None):
- """Wait for resource deletion
-
- :param manager: the manager from which we can get the resource
- :param res_id: the resource id to watch
- :param status_field: the status attribute in the returned resource object,
- this is used to check for error states while the resource is being
- deleted
- :param error_status: a list of status strings for error
- :param exception_name: a list of exception strings for deleted case
- :param sleep_time: wait this long between checks (seconds)
- :param timeout: check until this long (seconds)
- :param callback: called per sleep cycle, useful to display progress; this
- function is passed a progress value during each iteration of the wait
- loop
- :rtype: True on success, False if the resource has gone to error state or
- the timeout has been reached
- """
- total_time = 0
- while total_time < timeout:
- try:
- # might not be a bad idea to re-use find_resource here if it was
- # a bit more friendly in the exceptions it raised so we could just
- # handle a NotFound exception here without parsing the message
- res = manager.get(res_id)
- except Exception as ex:
- if type(ex).__name__ in exception_name:
- return True
- raise
-
- status = getattr(res, status_field, '').lower()
- if status in error_status:
- return False
-
- if callback:
- progress = getattr(res, 'progress', None) or 0
- callback(progress)
- time.sleep(sleep_time)
- total_time += sleep_time
-
- # if we got this far we've timed out
- return False
-
-
-def get_effective_log_level():
- """Returns the lowest logging level considered by logging handlers
-
- Retrieve and return the smallest log level set among the root
- logger's handlers (in case of multiple handlers).
- """
- root_log = logging.getLogger()
- min_log_lvl = logging.CRITICAL
- for handler in root_log.handlers:
- min_log_lvl = min(min_log_lvl, handler.level)
- return min_log_lvl
-
-
-def get_password(stdin, prompt=None, confirm=True):
- message = prompt or "User Password:"
- if hasattr(stdin, 'isatty') and stdin.isatty():
- try:
- while True:
- first_pass = getpass.getpass(message)
- if not confirm:
- return first_pass
- second_pass = getpass.getpass("Repeat " + message)
- if first_pass == second_pass:
- return first_pass
- print("The passwords entered were not the same")
- except EOFError: # Ctl-D
- raise exceptions.CommandError("Error reading password.")
- raise exceptions.CommandError("There was a request to be prompted for a"
- " password and a terminal was not detected.")
-
-
-def read_blob_file_contents(blob_file):
- try:
- with open(blob_file) as file:
- blob = file.read().strip()
- return blob
- except IOError:
- msg = "Error occurred trying to read from file %s"
- raise exceptions.CommandError(msg % blob_file)
+# NOTE(dtroyer): This file is deprecated in Jun 2016, remove after 4.x release
+# or Jun 2017.
+import sys
-def build_kwargs_dict(arg_name, value):
- """Return a dictionary containing `arg_name` if `value` is set."""
- kwargs = {}
- if value:
- kwargs[arg_name] = value
- return kwargs
+from osc_lib.utils import * # noqa
-def is_ascii(string):
- try:
- string.decode('ascii')
- return True
- except UnicodeDecodeError:
- return False
+sys.stderr.write(
+ "WARNING: %s is deprecated and will be removed after Jun 2017. "
+ "Please use osc_lib.utils\n" % __name__
+)
diff --git a/openstackclient/compute/client.py b/openstackclient/compute/client.py
index 8e6eedcf..1583676a 100644
--- a/openstackclient/compute/client.py
+++ b/openstackclient/compute/client.py
@@ -1,4 +1,4 @@
-# Copyright 2012-2013 OpenStack, LLC.
+# Copyright 2012-2013 OpenStack Foundation
#
# 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
@@ -15,10 +15,12 @@
import logging
-from openstackclient.common import exceptions
-from openstackclient.common import utils
+from osc_lib import exceptions
+from osc_lib import utils
+
from openstackclient.i18n import _
+
LOG = logging.getLogger(__name__)
DEFAULT_API_VERSION = '2'
diff --git a/openstackclient/compute/v2/agent.py b/openstackclient/compute/v2/agent.py
index 064fe5a6..4d923955 100644
--- a/openstackclient/compute/v2/agent.py
+++ b/openstackclient/compute/v2/agent.py
@@ -15,16 +15,21 @@
"""Agent action implementations"""
+import logging
+
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import exceptions
-from openstackclient.common import utils
from openstackclient.i18n import _
+LOG = logging.getLogger(__name__)
+
+
class CreateAgent(command.ShowOne):
- """Create compute agent command"""
+ """Create compute agent"""
def get_parser(self, prog_name):
parser = super(CreateAgent, self).get_parser(prog_name)
@@ -96,19 +101,18 @@ class DeleteAgent(command.Command):
compute_client.agents.delete(id)
except Exception as e:
result += 1
- self.app.log.error(_("Failed to delete agent with "
- "ID '%(id)s': %(e)s")
- % {'id': id, 'e': e})
+ LOG.error(_("Failed to delete agent with ID '%(id)s': %(e)s"),
+ {'id': id, 'e': e})
if result > 0:
total = len(parsed_args.id)
msg = (_("%(result)s of %(total)s agents failed "
- "to delete.") % {'result': result, 'total': total})
+ "to delete.") % {'result': result, 'total': total})
raise exceptions.CommandError(msg)
class ListAgent(command.Lister):
- """List compute agent command"""
+ """List compute agents"""
def get_parser(self, prog_name):
parser = super(ListAgent, self).get_parser(prog_name)
@@ -138,7 +142,7 @@ class ListAgent(command.Lister):
class SetAgent(command.Command):
- """Set compute agent command"""
+ """Set compute agent properties"""
def get_parser(self, prog_name):
parser = super(SetAgent, self).get_parser(prog_name)
diff --git a/openstackclient/compute/v2/aggregate.py b/openstackclient/compute/v2/aggregate.py
index 752e0fdf..2e2838c5 100644
--- a/openstackclient/compute/v2/aggregate.py
+++ b/openstackclient/compute/v2/aggregate.py
@@ -16,14 +16,20 @@
"""Compute v2 Aggregate action implementations"""
+import logging
+
+from osc_lib.cli import parseractions
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import parseractions
-from openstackclient.common import utils
from openstackclient.i18n import _
+LOG = logging.getLogger(__name__)
+
+
class AddAggregateHost(command.ShowOne):
"""Add host to aggregate"""
@@ -99,25 +105,37 @@ class CreateAggregate(command.ShowOne):
class DeleteAggregate(command.Command):
- """Delete an existing aggregate"""
+ """Delete existing aggregate(s)"""
def get_parser(self, prog_name):
parser = super(DeleteAggregate, self).get_parser(prog_name)
parser.add_argument(
'aggregate',
metavar='<aggregate>',
- help=_("Aggregate to delete (name or ID)")
+ nargs='+',
+ help=_("Aggregate(s) to delete (name or ID)")
)
return parser
def take_action(self, parsed_args):
-
compute_client = self.app.client_manager.compute
- data = utils.find_resource(
- compute_client.aggregates,
- parsed_args.aggregate,
- )
- compute_client.aggregates.delete(data.id)
+ result = 0
+ for a in parsed_args.aggregate:
+ try:
+ data = utils.find_resource(
+ compute_client.aggregates, a)
+ compute_client.aggregates.delete(data.id)
+ except Exception as e:
+ result += 1
+ LOG.error(_("Failed to delete aggregate with name or "
+ "ID '%(aggregate)s': %(e)s")
+ % {'aggregate': a, 'e': e})
+
+ if result > 0:
+ total = len(parsed_args.aggregate)
+ msg = (_("%(result)s of %(total)s aggregates failed "
+ "to delete.") % {'result': result, 'total': total})
+ raise exceptions.CommandError(msg)
class ListAggregate(command.Lister):
diff --git a/openstackclient/compute/v2/console.py b/openstackclient/compute/v2/console.py
index 1165862c..4179fa5c 100644
--- a/openstackclient/compute/v2/console.py
+++ b/openstackclient/compute/v2/console.py
@@ -15,12 +15,13 @@
"""Compute v2 Console action implementations"""
-import six
import sys
-from openstackclient.common import command
-from openstackclient.common import parseractions
-from openstackclient.common import utils
+from osc_lib.cli import parseractions
+from osc_lib.command import command
+from osc_lib import utils
+import six
+
from openstackclient.i18n import _
@@ -92,7 +93,7 @@ class ShowConsoleURL(command.ShowOne):
'--spice',
dest='url_type',
action='store_const',
- const='spice',
+ const='spice-html5',
help=_("Show SPICE console URL")
)
return parser
@@ -104,14 +105,20 @@ class ShowConsoleURL(command.ShowOne):
parsed_args.server,
)
+ data = None
if parsed_args.url_type in ['novnc', 'xvpvnc']:
data = server.get_vnc_console(parsed_args.url_type)
- if parsed_args.url_type in ['spice']:
+ if parsed_args.url_type in ['spice-html5']:
data = server.get_spice_console(parsed_args.url_type)
if not data:
return ({}, {})
info = {}
- info.update(data['console'])
+ # NOTE(Rui Chen): Return 'remote_console' in compute microversion API
+ # 2.6 and later, return 'console' in compute
+ # microversion API from 2.0 to 2.5, do compatibility
+ # handle for different microversion API.
+ console_data = data.get('remote_console', data.get('console'))
+ info.update(console_data)
return zip(*sorted(six.iteritems(info)))
diff --git a/openstackclient/compute/v2/fixedip.py b/openstackclient/compute/v2/fixedip.py
index daac97d1..8bd72ca3 100644
--- a/openstackclient/compute/v2/fixedip.py
+++ b/openstackclient/compute/v2/fixedip.py
@@ -15,8 +15,8 @@
"""Fixed IP action implementations"""
-from openstackclient.common import command
-from openstackclient.common import utils
+from osc_lib.command import command
+from osc_lib import utils
class AddFixedIP(command.Command):
diff --git a/openstackclient/compute/v2/flavor.py b/openstackclient/compute/v2/flavor.py
index 87909a18..01d7da75 100644
--- a/openstackclient/compute/v2/flavor.py
+++ b/openstackclient/compute/v2/flavor.py
@@ -15,16 +15,21 @@
"""Flavor action implementations"""
+import logging
+
+from osc_lib.cli import parseractions
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import exceptions
-from openstackclient.common import parseractions
-from openstackclient.common import utils
from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
+LOG = logging.getLogger(__name__)
+
+
def _find_flavor(compute_client, flavor):
try:
return compute_client.flavors.get(flavor)
@@ -116,10 +121,22 @@ class CreateFlavor(command.ShowOne):
action="store_false",
help=_("Flavor is not available to other projects")
)
+ parser.add_argument(
+ '--project',
+ metavar='<project>',
+ help=_("Allow <project> to access private flavor (name or ID) "
+ "(Must be used with --private option)"),
+ )
+ identity_common.add_project_domain_option_to_parser(parser)
return parser
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
+ identity_client = self.app.client_manager.identity
+
+ if parsed_args.project and parsed_args.public:
+ msg = _("--project is only allowed with --private")
+ raise exceptions.CommandError(msg)
args = (
parsed_args.name,
@@ -136,25 +153,54 @@ class CreateFlavor(command.ShowOne):
flavor = compute_client.flavors.create(*args)._info.copy()
flavor.pop("links")
+ if parsed_args.project:
+ try:
+ project_id = identity_common.find_project(
+ identity_client,
+ parsed_args.project,
+ parsed_args.project_domain,
+ ).id
+ compute_client.flavor_access.add_tenant_access(
+ parsed_args.id, project_id)
+ except Exception as e:
+ msg = _("Failed to add project %(project)s access to "
+ "flavor: %(e)s")
+ LOG.error(msg % {'project': parsed_args.project, 'e': e})
+
return zip(*sorted(six.iteritems(flavor)))
class DeleteFlavor(command.Command):
- """Delete flavor"""
+ """Delete flavor(s)"""
def get_parser(self, prog_name):
parser = super(DeleteFlavor, self).get_parser(prog_name)
parser.add_argument(
"flavor",
metavar="<flavor>",
- help=_("Flavor to delete (name or ID)")
+ nargs='+',
+ help=_("Flavor(s) to delete (name or ID)")
)
return parser
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
- flavor = _find_flavor(compute_client, parsed_args.flavor)
- compute_client.flavors.delete(flavor.id)
+ result = 0
+ for f in parsed_args.flavor:
+ try:
+ flavor = _find_flavor(compute_client, f)
+ compute_client.flavors.delete(flavor.id)
+ except Exception as e:
+ result += 1
+ LOG.error(_("Failed to delete flavor with name or "
+ "ID '%(flavor)s': %(e)s")
+ % {'flavor': f, 'e': e})
+
+ if result > 0:
+ total = len(parsed_args.flavor)
+ msg = (_("%(result)s of %(total)s flavors failed "
+ "to delete.") % {'result': result, 'total': total})
+ raise exceptions.CommandError(msg)
class ListFlavor(command.Lister):
@@ -274,16 +320,12 @@ class SetFlavor(command.Command):
flavor = _find_flavor(compute_client, parsed_args.flavor)
- if not parsed_args.property and not parsed_args.project:
- raise exceptions.CommandError(_("Nothing specified to be set."))
-
result = 0
if parsed_args.property:
try:
flavor.set_keys(parsed_args.property)
except Exception as e:
- self.app.log.error(
- _("Failed to set flavor property: %s") % str(e))
+ LOG.error(_("Failed to set flavor property: %s"), e)
result += 1
if parsed_args.project:
@@ -300,13 +342,12 @@ class SetFlavor(command.Command):
compute_client.flavor_access.add_tenant_access(
flavor.id, project_id)
except Exception as e:
- self.app.log.error(_("Failed to set flavor access to"
- " project: %s") % str(e))
+ LOG.error(_("Failed to set flavor access to project: %s"), e)
result += 1
if result > 0:
raise exceptions.CommandError(_("Command Failed: One or more of"
- " the operations failed"))
+ " the operations failed"))
class ShowFlavor(command.ShowOne):
@@ -365,16 +406,12 @@ class UnsetFlavor(command.Command):
flavor = _find_flavor(compute_client, parsed_args.flavor)
- if not parsed_args.property and not parsed_args.project:
- raise exceptions.CommandError(_("Nothing specified to be unset."))
-
result = 0
if parsed_args.property:
try:
flavor.unset_keys(parsed_args.property)
except Exception as e:
- self.app.log.error(
- _("Failed to unset flavor property: %s") % str(e))
+ LOG.error(_("Failed to unset flavor property: %s"), e)
result += 1
if parsed_args.project:
@@ -391,10 +428,10 @@ class UnsetFlavor(command.Command):
compute_client.flavor_access.remove_tenant_access(
flavor.id, project_id)
except Exception as e:
- self.app.log.error(_("Failed to remove flavor access from"
- " project: %s") % str(e))
+ LOG.error(_("Failed to remove flavor access from project: %s"),
+ e)
result += 1
if result > 0:
raise exceptions.CommandError(_("Command Failed: One or more of"
- " the operations failed"))
+ " the operations failed"))
diff --git a/openstackclient/compute/v2/floatingip.py b/openstackclient/compute/v2/floatingip.py
index fac4d2e3..98079fbc 100644
--- a/openstackclient/compute/v2/floatingip.py
+++ b/openstackclient/compute/v2/floatingip.py
@@ -15,8 +15,8 @@
"""Floating IP action implementations"""
-from openstackclient.common import command
-from openstackclient.common import utils
+from osc_lib.command import command
+from osc_lib import utils
class AddFloatingIP(command.Command):
diff --git a/openstackclient/compute/v2/floatingippool.py b/openstackclient/compute/v2/floatingippool.py
index 997e0324..0d46e32b 100644
--- a/openstackclient/compute/v2/floatingippool.py
+++ b/openstackclient/compute/v2/floatingippool.py
@@ -15,8 +15,8 @@
"""Floating IP Pool action implementations"""
-from openstackclient.common import command
-from openstackclient.common import utils
+from osc_lib.command import command
+from osc_lib import utils
class ListFloatingIPPool(command.Lister):
diff --git a/openstackclient/compute/v2/host.py b/openstackclient/compute/v2/host.py
index 73e2cdf9..4785377e 100644
--- a/openstackclient/compute/v2/host.py
+++ b/openstackclient/compute/v2/host.py
@@ -1,4 +1,4 @@
-# Copyright 2013 OpenStack, LLC.
+# Copyright 2012-2013 OpenStack Foundation
#
# 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
@@ -15,13 +15,14 @@
"""Host action implementations"""
-from openstackclient.common import command
-from openstackclient.common import utils
+from osc_lib.command import command
+from osc_lib import utils
+
from openstackclient.i18n import _
class ListHost(command.Lister):
- """List host command"""
+ """List hosts"""
def get_parser(self, prog_name):
parser = super(ListHost, self).get_parser(prog_name)
@@ -53,7 +54,7 @@ class SetHost(command.Command):
parser.add_argument(
"host",
metavar="<host>",
- help=_("The host to modify (name or ID)")
+ help=_("Host to modify (name only)")
)
status = parser.add_mutually_exclusive_group()
status.add_argument(
@@ -83,28 +84,30 @@ class SetHost(command.Command):
kwargs = {}
if parsed_args.enable:
- kwargs['status'] = True
+ kwargs['status'] = 'enable'
if parsed_args.disable:
- kwargs['status'] = False
+ kwargs['status'] = 'disable'
if parsed_args.enable_maintenance:
- kwargs['maintenance_mode'] = True
+ kwargs['maintenance_mode'] = 'enable'
if parsed_args.disable_maintenance:
- kwargs['maintenance_mode'] = False
+ kwargs['maintenance_mode'] = 'disable'
compute_client = self.app.client_manager.compute
- foundhost = utils.find_resource(
- compute_client.hosts,
- parsed_args.host
- )
+
+ # More than one hosts will be returned by using find_resource()
+ # so that the return value cannot be used in host update() method.
+ # find_resource() is just used for checking existence of host and
+ # keeping the exception message consistent with other commands.
+ utils.find_resource(compute_client.hosts, parsed_args.host)
compute_client.hosts.update(
- foundhost.id,
+ parsed_args.host,
kwargs
)
class ShowHost(command.Lister):
- """Show host command"""
+ """Display host details"""
def get_parser(self, prog_name):
parser = super(ShowHost, self).get_parser(prog_name)
diff --git a/openstackclient/compute/v2/hypervisor.py b/openstackclient/compute/v2/hypervisor.py
index 333a7dea..00625050 100644
--- a/openstackclient/compute/v2/hypervisor.py
+++ b/openstackclient/compute/v2/hypervisor.py
@@ -1,4 +1,4 @@
-# Copyright 2013 OpenStack, LLC.
+# Copyright 2012-2013 OpenStack Foundation
#
# 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
@@ -16,10 +16,11 @@
"""Hypervisor action implementations"""
import re
+
+from osc_lib.command import command
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import utils
from openstackclient.i18n import _
diff --git a/openstackclient/compute/v2/hypervisor_stats.py b/openstackclient/compute/v2/hypervisor_stats.py
index 290f5418..a70f0ce1 100644
--- a/openstackclient/compute/v2/hypervisor_stats.py
+++ b/openstackclient/compute/v2/hypervisor_stats.py
@@ -16,7 +16,7 @@
import six
-from openstackclient.common import command
+from osc_lib.command import command
class ShowHypervisorStats(command.ShowOne):
diff --git a/openstackclient/compute/v2/keypair.py b/openstackclient/compute/v2/keypair.py
index 8af209fe..3725a3a8 100644
--- a/openstackclient/compute/v2/keypair.py
+++ b/openstackclient/compute/v2/keypair.py
@@ -16,16 +16,21 @@
"""Keypair action implementations"""
import io
+import logging
import os
-import six
import sys
-from openstackclient.common import command
-from openstackclient.common import exceptions
-from openstackclient.common import utils
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
+import six
+
from openstackclient.i18n import _
+LOG = logging.getLogger(__name__)
+
+
class CreateKeypair(command.ShowOne):
"""Create new public key"""
@@ -77,20 +82,37 @@ class CreateKeypair(command.ShowOne):
class DeleteKeypair(command.Command):
- """Delete public key"""
+ """Delete public key(s)"""
def get_parser(self, prog_name):
parser = super(DeleteKeypair, self).get_parser(prog_name)
parser.add_argument(
'name',
metavar='<key>',
- help=_("Public key to delete (name only)")
+ nargs='+',
+ help=_("Public key(s) to delete (name only)")
)
return parser
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
- compute_client.keypairs.delete(parsed_args.name)
+ result = 0
+ for n in parsed_args.name:
+ try:
+ data = utils.find_resource(
+ compute_client.keypairs, n)
+ compute_client.keypairs.delete(data.name)
+ except Exception as e:
+ result += 1
+ LOG.error(_("Failed to delete public key with name "
+ "'%(name)s': %(e)s")
+ % {'name': n, 'e': e})
+
+ if result > 0:
+ total = len(parsed_args.name)
+ msg = (_("%(result)s of %(total)s public keys failed "
+ "to delete.") % {'result': result, 'total': total})
+ raise exceptions.CommandError(msg)
class ListKeypair(command.Lister):
diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py
index 5688b55f..7e4b0dc1 100644
--- a/openstackclient/compute/v2/server.py
+++ b/openstackclient/compute/v2/server.py
@@ -18,24 +18,28 @@
import argparse
import getpass
import io
+import logging
import os
-import six
import sys
-from openstackclient.common import command
+from osc_lib.cli import parseractions
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
+import six
try:
from novaclient.v2 import servers
except ImportError:
from novaclient.v1_1 import servers
-from openstackclient.common import exceptions
-from openstackclient.common import parseractions
-from openstackclient.common import utils
from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
+LOG = logging.getLogger(__name__)
+
+
def _format_servers_list_networks(networks):
"""Return a formatted string of a server's networks
@@ -164,23 +168,6 @@ def _prep_server_detail(compute_client, server):
return info
-def _prep_image_detail(image_client, image_id):
- """Prepare the detailed image dict for printing
-
- :param image_client: an image client instance
- :param image_id: id of image created
- :rtype: a dict of image details
- """
-
- info = utils.find_resource(
- image_client.images,
- image_id,
- )
- # Glance client V2 doesn't have _info attribute
- # The following condition deals with it.
- return getattr(info, "_info", info)
-
-
def _show_progress(progress):
if progress:
sys.stdout.write('\rProgress: %s' % progress)
@@ -538,8 +525,8 @@ class CreateServer(command.ShowOne):
scheduler_hints=hints,
config_drive=config_drive)
- self.log.debug('boot_args: %s', boot_args)
- self.log.debug('boot_kwargs: %s', boot_kwargs)
+ LOG.debug('boot_args: %s', boot_args)
+ LOG.debug('boot_kwargs: %s', boot_kwargs)
# Wrap the call to catch exceptions in order to close files
try:
@@ -560,8 +547,8 @@ class CreateServer(command.ShowOne):
):
sys.stdout.write('\n')
else:
- self.log.error(_('Error creating server: %s'),
- parsed_args.server_name)
+ LOG.error(_('Error creating server: %s'),
+ parsed_args.server_name)
sys.stdout.write(_('Error creating server\n'))
raise SystemExit
@@ -597,63 +584,6 @@ class CreateServerDump(command.Command):
).trigger_crash_dump()
-class CreateServerImage(command.ShowOne):
- """Create a new disk image from a running server"""
-
- def get_parser(self, prog_name):
- parser = super(CreateServerImage, self).get_parser(prog_name)
- parser.add_argument(
- 'server',
- metavar='<server>',
- help=_('Server (name or ID)'),
- )
- parser.add_argument(
- '--name',
- metavar='<image-name>',
- help=_('Name of new image (default is server name)'),
- )
- parser.add_argument(
- '--wait',
- action='store_true',
- help=_('Wait for image create to complete'),
- )
- return parser
-
- def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
- image_client = self.app.client_manager.image
- server = utils.find_resource(
- compute_client.servers,
- parsed_args.server,
- )
- if parsed_args.name:
- name = parsed_args.name
- else:
- name = server.name
-
- image_id = compute_client.servers.create_image(
- server,
- name,
- )
-
- if parsed_args.wait:
- if utils.wait_for_status(
- image_client.images.get,
- image_id,
- callback=_show_progress,
- ):
- sys.stdout.write('\n')
- else:
- self.log.error(_('Error creating snapshot of server: %s'),
- parsed_args.server)
- sys.stdout.write(_('Error creating server snapshot\n'))
- raise SystemExit
-
- image = _prep_image_detail(image_client, image_id)
-
- return zip(*sorted(six.iteritems(image)))
-
-
class DeleteServer(command.Command):
"""Delete server(s)"""
@@ -686,8 +616,8 @@ class DeleteServer(command.Command):
):
sys.stdout.write('\n')
else:
- self.log.error(_('Error deleting server: %s'),
- server_obj.id)
+ LOG.error(_('Error deleting server: %s'),
+ server_obj.id)
sys.stdout.write(_('Error deleting server\n'))
raise SystemExit
@@ -836,7 +766,7 @@ class ListServer(command.Lister):
'all_tenants': parsed_args.all_projects,
'user_id': user_id,
}
- self.log.debug('search options: %s', search_opts)
+ LOG.debug('search options: %s', search_opts)
if parsed_args.long:
columns = (
@@ -1013,8 +943,8 @@ class MigrateServer(command.Command):
):
sys.stdout.write(_('Complete\n'))
else:
- self.log.error(_('Error migrating server: %s'),
- server.id)
+ LOG.error(_('Error migrating server: %s'),
+ server.id)
sys.stdout.write(_('Error migrating server\n'))
raise SystemExit
@@ -1089,8 +1019,8 @@ class RebootServer(command.Command):
):
sys.stdout.write(_('Complete\n'))
else:
- self.log.error(_('Error rebooting server: %s'),
- server.id)
+ LOG.error(_('Error rebooting server: %s'),
+ server.id)
sys.stdout.write(_('Error rebooting server\n'))
raise SystemExit
@@ -1142,8 +1072,8 @@ class RebuildServer(command.ShowOne):
):
sys.stdout.write(_('Complete\n'))
else:
- self.log.error(_('Error rebuilding server: %s'),
- server.id)
+ LOG.error(_('Error rebuilding server: %s'),
+ server.id)
sys.stdout.write(_('Error rebuilding server\n'))
raise SystemExit
@@ -1296,8 +1226,8 @@ class ResizeServer(command.Command):
):
sys.stdout.write(_('Complete\n'))
else:
- self.log.error(_('Error resizing server: %s'),
- server.id)
+ LOG.error(_('Error resizing server: %s'),
+ server.id)
sys.stdout.write(_('Error resizing server\n'))
raise SystemExit
elif parsed_args.confirm:
@@ -1612,7 +1542,7 @@ class SshServer(command.Command):
ip_address = _get_ip_address(server.addresses,
parsed_args.address_type,
ip_address_family)
- self.log.debug("ssh command: %s", (cmd % (login, ip_address)))
+ LOG.debug("ssh command: %s", (cmd % (login, ip_address)))
os.system(cmd % (login, ip_address))
diff --git a/openstackclient/compute/v2/server_backup.py b/openstackclient/compute/v2/server_backup.py
index 24d71015..c0e2e5ee 100644
--- a/openstackclient/compute/v2/server_backup.py
+++ b/openstackclient/compute/v2/server_backup.py
@@ -17,12 +17,12 @@
import sys
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
from oslo_utils import importutils
import six
-from openstackclient.common import command
-from openstackclient.common import exceptions
-from openstackclient.common import utils
from openstackclient.i18n import _
diff --git a/openstackclient/compute/v2/server_group.py b/openstackclient/compute/v2/server_group.py
index 2e275b71..d51b1ec2 100644
--- a/openstackclient/compute/v2/server_group.py
+++ b/openstackclient/compute/v2/server_group.py
@@ -15,12 +15,18 @@
"""Compute v2 Server Group action implementations"""
-from openstackclient.common import command
-from openstackclient.common import exceptions
-from openstackclient.common import utils
+import logging
+
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
+
from openstackclient.i18n import _
+LOG = logging.getLogger(__name__)
+
+
_formatters = {
'policies': utils.format_list,
'members': utils.format_list,
@@ -94,7 +100,7 @@ class DeleteServerGroup(command.Command):
# Catch all exceptions in order to avoid to block the next deleting
except Exception as e:
result += 1
- self.app.log.error(e)
+ LOG.error(e)
if result > 0:
total = len(parsed_args.server_group)
diff --git a/openstackclient/compute/v2/server_image.py b/openstackclient/compute/v2/server_image.py
new file mode 100644
index 00000000..285c7fd2
--- /dev/null
+++ b/openstackclient/compute/v2/server_image.py
@@ -0,0 +1,113 @@
+# Copyright 2012-2013 OpenStack Foundation
+#
+# 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.
+#
+
+"""Compute v2 Server action implementations"""
+
+import logging
+import sys
+
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
+from oslo_utils import importutils
+import six
+
+from openstackclient.i18n import _
+
+
+LOG = logging.getLogger(__name__)
+
+
+def _show_progress(progress):
+ if progress:
+ sys.stdout.write('\rProgress: %s' % progress)
+ sys.stdout.flush()
+
+
+class CreateServerImage(command.ShowOne):
+ """Create a new server disk image from an existing server"""
+
+ IMAGE_API_VERSIONS = {
+ "1": "openstackclient.image.v1.image",
+ "2": "openstackclient.image.v2.image",
+ }
+
+ def get_parser(self, prog_name):
+ parser = super(CreateServerImage, self).get_parser(prog_name)
+ parser.add_argument(
+ 'server',
+ metavar='<server>',
+ help=_('Server to create image (name or ID)'),
+ )
+ parser.add_argument(
+ '--name',
+ metavar='<image-name>',
+ help=_('Name of new disk image (default: server name)'),
+ )
+ parser.add_argument(
+ '--wait',
+ action='store_true',
+ help=_('Wait for operation to complete'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ compute_client = self.app.client_manager.compute
+
+ server = utils.find_resource(
+ compute_client.servers,
+ parsed_args.server,
+ )
+ if parsed_args.name:
+ image_name = parsed_args.name
+ else:
+ image_name = server.name
+
+ image_id = compute_client.servers.create_image(
+ server.id,
+ image_name,
+ )
+
+ image_client = self.app.client_manager.image
+ image = utils.find_resource(
+ image_client.images,
+ image_id,
+ )
+
+ if parsed_args.wait:
+ if utils.wait_for_status(
+ image_client.images.get,
+ image_id,
+ callback=_show_progress,
+ ):
+ sys.stdout.write('\n')
+ else:
+ LOG.error(_('Error creating server image: %s'),
+ parsed_args.server)
+ raise exceptions.CommandError
+
+ if self.app.client_manager._api_version['image'] == '1':
+ info = {}
+ info.update(image._info)
+ info['properties'] = utils.format_dict(info.get('properties', {}))
+ else:
+ # Get the right image module to format the output
+ image_module = importutils.import_module(
+ self.IMAGE_API_VERSIONS[
+ self.app.client_manager._api_version['image']
+ ]
+ )
+ info = image_module._format_image(image)
+ return zip(*sorted(six.iteritems(info)))
diff --git a/openstackclient/compute/v2/service.py b/openstackclient/compute/v2/service.py
index e7966a8a..53624f55 100644
--- a/openstackclient/compute/v2/service.py
+++ b/openstackclient/compute/v2/service.py
@@ -1,4 +1,4 @@
-# Copyright 2013 OpenStack, LLC.
+# Copyright 2012-2013 OpenStack Foundation
#
# 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
@@ -15,32 +15,53 @@
"""Service action implementations"""
-from openstackclient.common import command
-from openstackclient.common import exceptions
-from openstackclient.common import utils
+import logging
+
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
+
from openstackclient.i18n import _
+from openstackclient.i18n import _LE
+
+
+LOG = logging.getLogger(__name__)
class DeleteService(command.Command):
- """Delete service command"""
+ """Delete compute service(s)"""
def get_parser(self, prog_name):
parser = super(DeleteService, self).get_parser(prog_name)
parser.add_argument(
"service",
metavar="<service>",
- help=_("Compute service to delete (ID only)")
+ nargs='+',
+ help=_("Compute service(s) to delete (ID only)")
)
return parser
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
-
- compute_client.services.delete(parsed_args.service)
+ result = 0
+ for s in parsed_args.service:
+ try:
+ compute_client.services.delete(s)
+ except Exception as e:
+ result += 1
+ LOG.error(_("Failed to delete compute service with "
+ "ID '%(service)s': %(e)s")
+ % {'service': s, 'e': e})
+
+ if result > 0:
+ total = len(parsed_args.service)
+ msg = (_("%(result)s of %(total)s compute services failed "
+ "to delete.") % {'result': result, 'total': total})
+ raise exceptions.CommandError(msg)
class ListService(command.Lister):
- """List service command"""
+ """List compute services"""
def get_parser(self, prog_name):
parser = super(ListService, self).get_parser(prog_name)
@@ -66,7 +87,7 @@ class ListService(command.Lister):
compute_client = self.app.client_manager.compute
if parsed_args.long:
columns = (
- "Id",
+ "ID",
"Binary",
"Host",
"Zone",
@@ -77,7 +98,7 @@ class ListService(command.Lister):
)
else:
columns = (
- "Id",
+ "ID",
"Binary",
"Host",
"Zone",
@@ -94,7 +115,7 @@ class ListService(command.Lister):
class SetService(command.Command):
- """Set service command"""
+ """Set compute service properties"""
def get_parser(self, prog_name):
parser = super(SetService, self).get_parser(prog_name)
@@ -106,7 +127,7 @@ class SetService(command.Command):
parser.add_argument(
"service",
metavar="<service>",
- help=_("Name of service")
+ help=_("Name of service (Binary name)")
)
enabled_group = parser.add_mutually_exclusive_group()
enabled_group.add_argument(
@@ -126,6 +147,17 @@ class SetService(command.Command):
help=_("Reason for disabling the service (in quotas). "
"Should be used with --disable option.")
)
+ up_down_group = parser.add_mutually_exclusive_group()
+ up_down_group.add_argument(
+ '--up',
+ action='store_true',
+ help=_('Force up service'),
+ )
+ up_down_group.add_argument(
+ '--down',
+ action='store_true',
+ help=_('Force down service'),
+ )
return parser
def take_action(self, parsed_args):
@@ -138,20 +170,45 @@ class SetService(command.Command):
"--disable specified.")
raise exceptions.CommandError(msg)
+ result = 0
enabled = None
- if parsed_args.enable:
- enabled = True
- if parsed_args.disable:
- enabled = False
-
- if enabled is None:
- return
- elif enabled:
- cs.enable(parsed_args.host, parsed_args.service)
- else:
- if parsed_args.disable_reason:
- cs.disable_log_reason(parsed_args.host,
- parsed_args.service,
- parsed_args.disable_reason)
- else:
- cs.disable(parsed_args.host, parsed_args.service)
+ try:
+ if parsed_args.enable:
+ enabled = True
+ if parsed_args.disable:
+ enabled = False
+
+ if enabled is not None:
+ if enabled:
+ cs.enable(parsed_args.host, parsed_args.service)
+ else:
+ if parsed_args.disable_reason:
+ cs.disable_log_reason(parsed_args.host,
+ parsed_args.service,
+ parsed_args.disable_reason)
+ else:
+ cs.disable(parsed_args.host, parsed_args.service)
+ except Exception:
+ status = "enabled" if enabled else "disabled"
+ LOG.error(_LE("Failed to set service status to %s"), status)
+ result += 1
+
+ force_down = None
+ try:
+ if parsed_args.down:
+ force_down = True
+ if parsed_args.up:
+ force_down = False
+ if force_down is not None:
+ cs.force_down(parsed_args.host, parsed_args.service,
+ force_down=force_down)
+ except Exception:
+ state = "down" if force_down else "up"
+ LOG.error(_LE("Failed to set service state to %s"), state)
+ result += 1
+
+ if result > 0:
+ msg = _("Compute service %(service)s of host %(host)s failed to "
+ "set.") % {"service": parsed_args.service,
+ "host": parsed_args.host}
+ raise exceptions.CommandError(msg)
diff --git a/openstackclient/compute/v2/usage.py b/openstackclient/compute/v2/usage.py
index b83bef13..2f35b01b 100644
--- a/openstackclient/compute/v2/usage.py
+++ b/openstackclient/compute/v2/usage.py
@@ -18,10 +18,10 @@
import datetime
import sys
+from osc_lib.command import command
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import utils
from openstackclient.i18n import _
diff --git a/openstackclient/identity/client.py b/openstackclient/identity/client.py
index c166e66a..be7b643f 100644
--- a/openstackclient/identity/client.py
+++ b/openstackclient/identity/client.py
@@ -16,8 +16,10 @@
import logging
from keystoneclient.v2_0 import client as identity_client_v2
+from osc_lib import utils
+
from openstackclient.api import auth
-from openstackclient.common import utils
+from openstackclient.i18n import _
LOG = logging.getLogger(__name__)
@@ -64,9 +66,9 @@ def build_option_parser(parser):
'--os-identity-api-version',
metavar='<identity-api-version>',
default=utils.env('OS_IDENTITY_API_VERSION'),
- help='Identity API version, default=' +
- DEFAULT_API_VERSION +
- ' (Env: OS_IDENTITY_API_VERSION)')
+ help=_('Identity API version, default=%s '
+ '(Env: OS_IDENTITY_API_VERSION)') % DEFAULT_API_VERSION,
+ )
return auth.build_auth_plugins_option_parser(parser)
diff --git a/openstackclient/identity/common.py b/openstackclient/identity/common.py
index 2afa41fb..379f4114 100644
--- a/openstackclient/identity/common.py
+++ b/openstackclient/identity/common.py
@@ -20,9 +20,10 @@ from keystoneclient.v3 import domains
from keystoneclient.v3 import groups
from keystoneclient.v3 import projects
from keystoneclient.v3 import users
+from osc_lib import exceptions
+from osc_lib import utils
-from openstackclient.common import exceptions
-from openstackclient.common import utils
+from openstackclient.i18n import _
def find_service(identity_client, name_type_or_id):
@@ -38,14 +39,45 @@ def find_service(identity_client, name_type_or_id):
# FIXME(dtroyer): This exception should eventually come from
# common client exceptions
except identity_exc.NotFound:
- msg = ("No service with a type, name or ID of '%s' exists."
- % name_type_or_id)
- raise exceptions.CommandError(msg)
+ msg = _("No service with a type, name or ID of '%s' exists.")
+ raise exceptions.CommandError(msg % name_type_or_id)
except identity_exc.NoUniqueMatch:
- msg = ("Multiple service matches found for '%s', "
- "use an ID to be more specific."
- % name_type_or_id)
- raise exceptions.CommandError(msg)
+ msg = _("Multiple service matches found for '%s', "
+ "use an ID to be more specific.")
+ raise exceptions.CommandError(msg % name_type_or_id)
+
+
+def _get_token_resource(client, resource, parsed_name):
+ """Peek into the user's auth token to get resource IDs
+
+ Look into a user's token to try and find the ID of a domain, project or
+ user, when given the name. Typically non-admin users will interact with
+ the CLI using names. However, by default, keystone does not allow look up
+ by name since it would involve listing all entities. Instead opt to use
+ the correct ID (from the token) instead.
+ :param client: An identity client
+ :param resource: A resource to look at in the token, this may be `domain`,
+ `project_domain`, `user_domain`, `project`, or `user`.
+ :param parsed_name: This is input from parsed_args that the user is hoping
+ to find in the token.
+
+ :returns: The ID of the resource from the token, or the original value from
+ parsed_args if it does not match.
+ """
+
+ try:
+ token = client.auth.client.get_token()
+ token_data = client.tokens.get_token_data(token)
+ token_dict = token_data['token']
+
+ # NOTE(stevemar): If domain is passed, just look at the project domain.
+ if resource == 'domain':
+ token_dict = token_dict['project']
+ obj = token_dict[resource]
+ return obj['id'] if obj['name'] == parsed_name else parsed_name
+ # diaper defense in case parsing the token fails
+ except Exception: # noqa
+ return parsed_name
def _get_domain_id_if_requested(identity_client, domain_name_or_id):
@@ -132,9 +164,9 @@ def add_user_domain_option_to_parser(parser):
parser.add_argument(
'--user-domain',
metavar='<user-domain>',
- help=('Domain the user belongs to (name or ID). '
- 'This can be used in case collisions between user names '
- 'exist.')
+ help=_('Domain the user belongs to (name or ID). '
+ 'This can be used in case collisions between user names '
+ 'exist.'),
)
@@ -142,9 +174,9 @@ def add_group_domain_option_to_parser(parser):
parser.add_argument(
'--group-domain',
metavar='<group-domain>',
- help=('Domain the group belongs to (name or ID). '
- 'This can be used in case collisions between group names '
- 'exist.')
+ help=_('Domain the group belongs to (name or ID). '
+ 'This can be used in case collisions between group names '
+ 'exist.'),
)
@@ -152,9 +184,9 @@ def add_project_domain_option_to_parser(parser):
parser.add_argument(
'--project-domain',
metavar='<project-domain>',
- help=('Domain the project belongs to (name or ID). '
- 'This can be used in case collisions between project names '
- 'exist.')
+ help=_('Domain the project belongs to (name or ID). '
+ 'This can be used in case collisions between project names '
+ 'exist.'),
)
@@ -163,5 +195,6 @@ def add_inherited_option_to_parser(parser):
'--inherited',
action='store_true',
default=False,
- help=('Specifies if the role grant is inheritable to the sub projects')
+ help=_('Specifies if the role grant is inheritable to the sub '
+ 'projects'),
)
diff --git a/openstackclient/identity/v2_0/catalog.py b/openstackclient/identity/v2_0/catalog.py
index c8f48cb6..7a15cf3a 100644
--- a/openstackclient/identity/v2_0/catalog.py
+++ b/openstackclient/identity/v2_0/catalog.py
@@ -13,13 +13,19 @@
"""Identity v2 Service Catalog action implementations"""
+import logging
+
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import utils
from openstackclient.i18n import _
+LOG = logging.getLogger(__name__)
+
+
def _format_endpoints(eps=None):
if not eps:
return ""
@@ -41,13 +47,14 @@ class ListCatalog(command.Lister):
def take_action(self, parsed_args):
- # This is ugly because if auth hasn't happened yet we need
- # to trigger it here.
- sc = self.app.client_manager.session.auth.get_auth_ref(
- self.app.client_manager.session,
- ).service_catalog
+ # Trigger auth if it has not happened yet
+ auth_ref = self.app.client_manager.auth_ref
+ if not auth_ref:
+ raise exceptions.AuthorizationFailure(
+ "Only an authorized user may issue a new token."
+ )
- data = sc.get_data()
+ data = auth_ref.service_catalog.catalog
columns = ('Name', 'Type', 'Endpoints')
return (columns,
(utils.get_dict_properties(
@@ -72,14 +79,15 @@ class ShowCatalog(command.ShowOne):
def take_action(self, parsed_args):
- # This is ugly because if auth hasn't happened yet we need
- # to trigger it here.
- sc = self.app.client_manager.session.auth.get_auth_ref(
- self.app.client_manager.session,
- ).service_catalog
+ # Trigger auth if it has not happened yet
+ auth_ref = self.app.client_manager.auth_ref
+ if not auth_ref:
+ raise exceptions.AuthorizationFailure(
+ "Only an authorized user may issue a new token."
+ )
data = None
- for service in sc.get_data():
+ for service in auth_ref.service_catalog.catalog:
if (service.get('name') == parsed_args.service or
service.get('type') == parsed_args.service):
data = service
@@ -89,8 +97,7 @@ class ShowCatalog(command.ShowOne):
break
if not data:
- self.app.log.error(_('service %s not found\n') %
- parsed_args.service)
- return ([], [])
+ LOG.error(_('service %s not found\n'), parsed_args.service)
+ return ((), ())
return zip(*sorted(six.iteritems(data)))
diff --git a/openstackclient/identity/v2_0/ec2creds.py b/openstackclient/identity/v2_0/ec2creds.py
index dfd67591..058b5772 100644
--- a/openstackclient/identity/v2_0/ec2creds.py
+++ b/openstackclient/identity/v2_0/ec2creds.py
@@ -16,13 +16,19 @@
"""Identity v2 EC2 Credentials action implementations"""
+import logging
+
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import utils
from openstackclient.i18n import _
+LOG = logging.getLogger(__name__)
+
+
class CreateEC2Creds(command.ShowOne):
"""Create EC2 credentials"""
@@ -85,9 +91,10 @@ class DeleteEC2Creds(command.Command):
def get_parser(self, prog_name):
parser = super(DeleteEC2Creds, self).get_parser(prog_name)
parser.add_argument(
- 'access_key',
+ 'access_keys',
metavar='<access-key>',
- help=_('Credentials access key'),
+ nargs='+',
+ help=_('Credentials access keys'),
)
parser.add_argument(
'--user',
@@ -108,7 +115,21 @@ class DeleteEC2Creds(command.Command):
# Get the user from the current auth
user = self.app.client_manager.auth_ref.user_id
- identity_client.ec2.delete(user, parsed_args.access_key)
+ result = 0
+ for access_key in parsed_args.access_keys:
+ try:
+ identity_client.ec2.delete(user, access_key)
+ except Exception as e:
+ result += 1
+ LOG.error(_("Failed to delete EC2 keys with "
+ "access key '%(access_key)s': %(e)s")
+ % {'access_key': access_key, 'e': e})
+
+ if result > 0:
+ total = len(parsed_args.access_keys)
+ msg = (_("%(result)s of %(total)s EC2 keys failed "
+ "to delete.") % {'result': result, 'total': total})
+ raise exceptions.CommandError(msg)
class ListEC2Creds(command.Lister):
diff --git a/openstackclient/identity/v2_0/endpoint.py b/openstackclient/identity/v2_0/endpoint.py
index 09ea738f..5a3b3186 100644
--- a/openstackclient/identity/v2_0/endpoint.py
+++ b/openstackclient/identity/v2_0/endpoint.py
@@ -15,14 +15,20 @@
"""Endpoint action implementations"""
+import logging
+
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import utils
from openstackclient.i18n import _
from openstackclient.identity import common
+LOG = logging.getLogger(__name__)
+
+
class CreateEndpoint(command.ShowOne):
"""Create new endpoint"""
@@ -31,7 +37,7 @@ class CreateEndpoint(command.ShowOne):
parser.add_argument(
'service',
metavar='<service>',
- help=_('New endpoint service (name or ID)'),
+ help=_('Service to be associated with new endpoint (name or ID)'),
)
parser.add_argument(
'--publicurl',
@@ -74,20 +80,36 @@ class CreateEndpoint(command.ShowOne):
class DeleteEndpoint(command.Command):
- """Delete endpoint"""
+ """Delete endpoint(s)"""
def get_parser(self, prog_name):
parser = super(DeleteEndpoint, self).get_parser(prog_name)
parser.add_argument(
- 'endpoint',
+ 'endpoints',
metavar='<endpoint-id>',
- help=_('Endpoint ID to delete'),
+ nargs='+',
+ help=_('Endpoint(s) to delete (ID only)'),
)
return parser
def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
- identity_client.endpoints.delete(parsed_args.endpoint)
+
+ result = 0
+ for endpoint in parsed_args.endpoints:
+ try:
+ identity_client.endpoints.delete(endpoint)
+ except Exception as e:
+ result += 1
+ LOG.error(_("Failed to delete endpoint with "
+ "ID '%(endpoint)s': %(e)s")
+ % {'endpoint': endpoint, 'e': e})
+
+ if result > 0:
+ total = len(parsed_args.endpoints)
+ msg = (_("%(result)s of %(total)s endpoints failed "
+ "to delete.") % {'result': result, 'total': total})
+ raise exceptions.CommandError(msg)
class ListEndpoint(command.Lister):
diff --git a/openstackclient/identity/v2_0/project.py b/openstackclient/identity/v2_0/project.py
index 80f88d73..6c5db13c 100644
--- a/openstackclient/identity/v2_0/project.py
+++ b/openstackclient/identity/v2_0/project.py
@@ -15,16 +15,20 @@
"""Identity v2 Project action implementations"""
-import six
+import logging
from keystoneauth1 import exceptions as ks_exc
+from osc_lib.cli import parseractions
+from osc_lib.command import command
+from osc_lib import utils
+import six
-from openstackclient.common import command
-from openstackclient.common import parseractions
-from openstackclient.common import utils
from openstackclient.i18n import _
+LOG = logging.getLogger(__name__)
+
+
class CreateProject(command.ShowOne):
"""Create new project"""
@@ -88,7 +92,7 @@ class CreateProject(command.ShowOne):
identity_client.tenants,
parsed_args.name,
)
- self.log.info(_('Returning existing project %s'), project.name)
+ LOG.info(_('Returning existing project %s'), project.name)
else:
raise e
@@ -190,13 +194,6 @@ class SetProject(command.Command):
def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
- if (not parsed_args.name
- and not parsed_args.description
- and not parsed_args.enable
- and not parsed_args.property
- and not parsed_args.disable):
- return
-
project = utils.find_resource(
identity_client.tenants,
parsed_args.project,
@@ -296,7 +293,6 @@ class UnsetProject(command.Command):
metavar='<key>',
action='append',
default=[],
- required=True,
help=_('Unset a project property '
'(repeat option to unset multiple properties)'),
)
@@ -308,11 +304,8 @@ class UnsetProject(command.Command):
identity_client.tenants,
parsed_args.project,
)
- if not parsed_args.property:
- self.app.log.error(_("No changes requested\n"))
- else:
- kwargs = project._info
- for key in parsed_args.property:
- if key in kwargs:
- kwargs[key] = None
- identity_client.tenants.update(project.id, **kwargs)
+ kwargs = project._info
+ for key in parsed_args.property:
+ if key in kwargs:
+ kwargs[key] = None
+ identity_client.tenants.update(project.id, **kwargs)
diff --git a/openstackclient/identity/v2_0/role.py b/openstackclient/identity/v2_0/role.py
index 6b014d86..6d06230c 100644
--- a/openstackclient/identity/v2_0/role.py
+++ b/openstackclient/identity/v2_0/role.py
@@ -15,16 +15,20 @@
"""Identity v2 Role action implementations"""
-import six
+import logging
from keystoneauth1 import exceptions as ks_exc
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
+import six
-from openstackclient.common import command
-from openstackclient.common import exceptions
-from openstackclient.common import utils
from openstackclient.i18n import _
+LOG = logging.getLogger(__name__)
+
+
class AddRole(command.ShowOne):
"""Add role to project:user"""
@@ -95,7 +99,7 @@ class CreateRole(command.ShowOne):
identity_client.roles,
parsed_args.role_name,
)
- self.log.info(_('Returning existing role %s'), role.name)
+ LOG.info(_('Returning existing role %s'), role.name)
else:
raise e
@@ -231,18 +235,19 @@ class ListUserRole(command.Lister):
# Project and user are required, if not included in command args
# default to the values used for authentication. For token-flow
# authentication they must be included on the command line.
+ if (not parsed_args.project and
+ self.app.client_manager.auth_ref.project_id):
+ parsed_args.project = auth_ref.project_id
if not parsed_args.project:
- if self.app.client_manager.auth_ref:
- parsed_args.project = auth_ref.project_id
- else:
- msg = _("Project must be specified")
- raise exceptions.CommandError(msg)
+ msg = _("Project must be specified")
+ raise exceptions.CommandError(msg)
+
+ if (not parsed_args.user and
+ self.app.client_manager.auth_ref.user_id):
+ parsed_args.user = auth_ref.user_id
if not parsed_args.user:
- if self.app.client_manager.auth_ref:
- parsed_args.user = auth_ref.user_id
- else:
- msg = _("User must be specified")
- raise exceptions.CommandError(msg)
+ msg = _("User must be specified")
+ raise exceptions.CommandError(msg)
project = utils.find_resource(
identity_client.tenants,
diff --git a/openstackclient/identity/v2_0/service.py b/openstackclient/identity/v2_0/service.py
index 7fe66d91..e318643c 100644
--- a/openstackclient/identity/v2_0/service.py
+++ b/openstackclient/identity/v2_0/service.py
@@ -16,15 +16,20 @@
"""Service action implementations"""
import argparse
+import logging
+
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import exceptions
-from openstackclient.common import utils
from openstackclient.i18n import _
from openstackclient.identity import common
+LOG = logging.getLogger(__name__)
+
+
class CreateService(command.ShowOne):
"""Create new service"""
@@ -68,8 +73,8 @@ class CreateService(command.ShowOne):
# display deprecation message.
elif type:
name = type_or_name
- self.log.warning(_('The argument --type is deprecated, use service'
- ' create --name <service-name> type instead.'))
+ LOG.warning(_('The argument --type is deprecated, use service'
+ ' create --name <service-name> type instead.'))
# If --name option is present the positional is handled as <type>.
# Making --type optional is new, but back-compatible
elif name:
@@ -86,21 +91,37 @@ class CreateService(command.ShowOne):
class DeleteService(command.Command):
- """Delete service"""
+ """Delete service(s)"""
def get_parser(self, prog_name):
parser = super(DeleteService, self).get_parser(prog_name)
parser.add_argument(
- 'service',
+ 'services',
metavar='<service>',
- help=_('Service to delete (name or ID)'),
+ nargs='+',
+ help=_('Service(s) to delete (type, name or ID)'),
)
return parser
def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
- service = common.find_service(identity_client, parsed_args.service)
- identity_client.services.delete(service.id)
+
+ result = 0
+ for service in parsed_args.services:
+ try:
+ service = common.find_service(identity_client, service)
+ identity_client.services.delete(service.id)
+ except Exception as e:
+ result += 1
+ LOG.error(_("Failed to delete service with "
+ "name or ID '%(service)s': %(e)s")
+ % {'service': service, 'e': e})
+
+ if result > 0:
+ total = len(parsed_args.services)
+ msg = (_("%(result)s of %(total)s services failed "
+ "to delete.") % {'result': result, 'total': total})
+ raise exceptions.CommandError(msg)
class ListService(command.Lister):
diff --git a/openstackclient/identity/v2_0/token.py b/openstackclient/identity/v2_0/token.py
index f435d7ce..56f920f3 100644
--- a/openstackclient/identity/v2_0/token.py
+++ b/openstackclient/identity/v2_0/token.py
@@ -15,9 +15,10 @@
"""Identity v2 Token action implementations"""
+from osc_lib.command import command
+from osc_lib import exceptions
import six
-from openstackclient.common import command
from openstackclient.i18n import _
@@ -32,11 +33,21 @@ class IssueToken(command.ShowOne):
return parser
def take_action(self, parsed_args):
+ auth_ref = self.app.client_manager.auth_ref
+ if not auth_ref:
+ raise exceptions.AuthorizationFailure(
+ "Only an authorized user may issue a new token.")
- token = self.app.client_manager.auth_ref.service_catalog.get_token()
- if 'tenant_id' in token:
- token['project_id'] = token.pop('tenant_id')
- return zip(*sorted(six.iteritems(token)))
+ data = {}
+ if auth_ref.auth_token:
+ data['id'] = auth_ref.auth_token
+ if auth_ref.expires:
+ data['expires'] = auth_ref.expires
+ if auth_ref.project_id:
+ data['project_id'] = auth_ref.project_id
+ if auth_ref.user_id:
+ data['user_id'] = auth_ref.user_id
+ return zip(*sorted(six.iteritems(data)))
class RevokeToken(command.Command):
diff --git a/openstackclient/identity/v2_0/user.py b/openstackclient/identity/v2_0/user.py
index f8f5df29..0f327830 100644
--- a/openstackclient/identity/v2_0/user.py
+++ b/openstackclient/identity/v2_0/user.py
@@ -15,15 +15,19 @@
"""Identity v2.0 User action implementations"""
-import six
+import logging
from keystoneauth1 import exceptions as ks_exc
+from osc_lib.command import command
+from osc_lib import utils
+import six
-from openstackclient.common import command
-from openstackclient.common import utils
from openstackclient.i18n import _
+LOG = logging.getLogger(__name__)
+
+
class CreateUser(command.ShowOne):
"""Create new user"""
@@ -104,7 +108,7 @@ class CreateUser(command.ShowOne):
identity_client.users,
parsed_args.name,
)
- self.log.info(_('Returning existing user %s'), user.name)
+ LOG.info(_('Returning existing user %s'), user.name)
else:
raise e
@@ -241,7 +245,7 @@ class SetUser(command.Command):
parser.add_argument(
'user',
metavar='<user>',
- help=_('User to change (name or ID)'),
+ help=_('User to modify (name or ID)'),
)
parser.add_argument(
'--name',
@@ -288,15 +292,6 @@ class SetUser(command.Command):
if parsed_args.password_prompt:
parsed_args.password = utils.get_password(self.app.stdin)
- if (not parsed_args.name
- and not parsed_args.name
- and not parsed_args.password
- and not parsed_args.email
- and not parsed_args.project
- and not parsed_args.enable
- and not parsed_args.disable):
- return
-
user = utils.find_resource(
identity_client.users,
parsed_args.user,
diff --git a/openstackclient/identity/v3/catalog.py b/openstackclient/identity/v3/catalog.py
index 4c794692..a62d0a93 100644
--- a/openstackclient/identity/v3/catalog.py
+++ b/openstackclient/identity/v3/catalog.py
@@ -13,13 +13,19 @@
"""Identity v3 Service Catalog action implementations"""
+import logging
+
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import utils
from openstackclient.i18n import _
+LOG = logging.getLogger(__name__)
+
+
def _format_endpoints(eps=None):
if not eps:
return ""
@@ -36,13 +42,14 @@ class ListCatalog(command.Lister):
def take_action(self, parsed_args):
- # This is ugly because if auth hasn't happened yet we need
- # to trigger it here.
- sc = self.app.client_manager.session.auth.get_auth_ref(
- self.app.client_manager.session,
- ).service_catalog
+ # Trigger auth if it has not happened yet
+ auth_ref = self.app.client_manager.auth_ref
+ if not auth_ref:
+ raise exceptions.AuthorizationFailure(
+ "Only an authorized user may issue a new token."
+ )
- data = sc.get_data()
+ data = auth_ref.service_catalog.catalog
columns = ('Name', 'Type', 'Endpoints')
return (columns,
(utils.get_dict_properties(
@@ -67,14 +74,15 @@ class ShowCatalog(command.ShowOne):
def take_action(self, parsed_args):
- # This is ugly because if auth hasn't happened yet we need
- # to trigger it here.
- sc = self.app.client_manager.session.auth.get_auth_ref(
- self.app.client_manager.session,
- ).service_catalog
+ # Trigger auth if it has not happened yet
+ auth_ref = self.app.client_manager.auth_ref
+ if not auth_ref:
+ raise exceptions.AuthorizationFailure(
+ "Only an authorized user may issue a new token."
+ )
data = None
- for service in sc.get_data():
+ for service in auth_ref.service_catalog.catalog:
if (service.get('name') == parsed_args.service or
service.get('type') == parsed_args.service):
data = dict(service)
@@ -84,8 +92,7 @@ class ShowCatalog(command.ShowOne):
break
if not data:
- self.app.log.error(_('service %s not found\n') %
- parsed_args.service)
- return ([], [])
+ LOG.error(_('service %s not found\n'), parsed_args.service)
+ return ((), ())
return zip(*sorted(six.iteritems(data)))
diff --git a/openstackclient/identity/v3/consumer.py b/openstackclient/identity/v3/consumer.py
index a062b743..a4620bf9 100644
--- a/openstackclient/identity/v3/consumer.py
+++ b/openstackclient/identity/v3/consumer.py
@@ -15,11 +15,12 @@
"""Identity v3 Consumer action implementations"""
-import six
import sys
-from openstackclient.common import command
-from openstackclient.common import utils
+from osc_lib.command import command
+from osc_lib import utils
+import six
+
from openstackclient.i18n import _
diff --git a/openstackclient/identity/v3/credential.py b/openstackclient/identity/v3/credential.py
index 99013478..eeeddfa5 100644
--- a/openstackclient/identity/v3/credential.py
+++ b/openstackclient/identity/v3/credential.py
@@ -15,10 +15,10 @@
"""Identity v3 Credential action implementations"""
+from osc_lib.command import command
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import utils
from openstackclient.i18n import _
diff --git a/openstackclient/identity/v3/domain.py b/openstackclient/identity/v3/domain.py
index c345028f..8ba76c41 100644
--- a/openstackclient/identity/v3/domain.py
+++ b/openstackclient/identity/v3/domain.py
@@ -15,14 +15,19 @@
"""Identity v3 Domain action implementations"""
-import six
+import logging
import sys
from keystoneauth1 import exceptions as ks_exc
+from osc_lib.command import command
+from osc_lib import utils
+import six
-from openstackclient.common import command
-from openstackclient.common import utils
from openstackclient.i18n import _
+from openstackclient.identity import common
+
+
+LOG = logging.getLogger(__name__)
class CreateDomain(command.ShowOne):
@@ -75,7 +80,7 @@ class CreateDomain(command.ShowOne):
if parsed_args.or_show:
domain = utils.find_resource(identity_client.domains,
parsed_args.name)
- self.log.info(_('Returning existing domain %s'), domain.name)
+ LOG.info(_('Returning existing domain %s'), domain.name)
else:
raise e
@@ -183,8 +188,12 @@ class ShowDomain(command.ShowOne):
def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
+
+ domain_str = common._get_token_resource(identity_client, 'domain',
+ parsed_args.domain)
+
domain = utils.find_resource(identity_client.domains,
- parsed_args.domain)
+ domain_str)
domain._info.pop('links')
return zip(*sorted(six.iteritems(domain._info)))
diff --git a/openstackclient/identity/v3/ec2creds.py b/openstackclient/identity/v3/ec2creds.py
index 859ec2a7..835fe96f 100644
--- a/openstackclient/identity/v3/ec2creds.py
+++ b/openstackclient/identity/v3/ec2creds.py
@@ -12,10 +12,10 @@
"""Identity v3 EC2 Credentials action implementations"""
+from osc_lib.command import command
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import utils
from openstackclient.i18n import _
from openstackclient.identity import common
diff --git a/openstackclient/identity/v3/endpoint.py b/openstackclient/identity/v3/endpoint.py
index 39022d27..2f1cc9f3 100644
--- a/openstackclient/identity/v3/endpoint.py
+++ b/openstackclient/identity/v3/endpoint.py
@@ -15,11 +15,12 @@
"""Identity v3 Endpoint action implementations"""
-import six
import sys
-from openstackclient.common import command
-from openstackclient.common import utils
+from osc_lib.command import command
+from osc_lib import utils
+import six
+
from openstackclient.i18n import _
from openstackclient.identity import common
@@ -39,7 +40,7 @@ class CreateEndpoint(command.ShowOne):
parser.add_argument(
'service',
metavar='<service>',
- help=_('New endpoint service (name or ID)'),
+ help=_('Service to be associated with new endpoint (name or ID)'),
)
parser.add_argument(
'interface',
@@ -101,7 +102,7 @@ class DeleteEndpoint(command.Command):
parser.add_argument(
'endpoint',
metavar='<endpoint-id>',
- help=_('Endpoint ID to delete'),
+ help=_('Endpoint to delete (ID only)'),
)
return parser
@@ -120,7 +121,7 @@ class ListEndpoint(command.Lister):
parser.add_argument(
'--service',
metavar='<service>',
- help=_('Filter by service'),
+ help=_('Filter by service (name or ID)'),
)
parser.add_argument(
'--interface',
@@ -168,7 +169,7 @@ class SetEndpoint(command.Command):
parser.add_argument(
'endpoint',
metavar='<endpoint-id>',
- help=_('Endpoint ID to modify'),
+ help=_('Endpoint to modify (ID only)'),
)
parser.add_argument(
'--region',
diff --git a/openstackclient/identity/v3/federation_protocol.py b/openstackclient/identity/v3/federation_protocol.py
index c0f4bc93..09480245 100644
--- a/openstackclient/identity/v3/federation_protocol.py
+++ b/openstackclient/identity/v3/federation_protocol.py
@@ -14,13 +14,18 @@
"""Identity v3 Protocols actions implementations"""
+import logging
+
+from osc_lib.command import command
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import utils
from openstackclient.i18n import _
+LOG = logging.getLogger(__name__)
+
+
class CreateProtocol(command.ShowOne):
"""Create new federation protocol"""
@@ -145,7 +150,7 @@ class SetProtocol(command.Command):
identity_client = self.app.client_manager.identity
if not parsed_args.mapping:
- self.app.log.error(_("No changes requested"))
+ LOG.error(_("No changes requested"))
return
protocol = identity_client.federation.protocols.update(
diff --git a/openstackclient/identity/v3/group.py b/openstackclient/identity/v3/group.py
index fdb94da6..8351fe64 100644
--- a/openstackclient/identity/v3/group.py
+++ b/openstackclient/identity/v3/group.py
@@ -15,17 +15,21 @@
"""Group action implementations"""
-import six
+import logging
import sys
from keystoneauth1 import exceptions as ks_exc
+from osc_lib.command import command
+from osc_lib import utils
+import six
-from openstackclient.common import command
-from openstackclient.common import utils
from openstackclient.i18n import _
from openstackclient.identity import common
+LOG = logging.getLogger(__name__)
+
+
class AddUserToGroup(command.Command):
"""Add user to group"""
@@ -161,7 +165,7 @@ class CreateGroup(command.ShowOne):
group = utils.find_resource(identity_client.groups,
parsed_args.name,
domain_id=domain)
- self.log.info(_('Returning existing group %s'), group.name)
+ LOG.info(_('Returning existing group %s'), group.name)
else:
raise e
diff --git a/openstackclient/identity/v3/identity_provider.py b/openstackclient/identity/v3/identity_provider.py
index 3749aa35..5c638f9b 100644
--- a/openstackclient/identity/v3/identity_provider.py
+++ b/openstackclient/identity/v3/identity_provider.py
@@ -13,13 +13,18 @@
"""Identity v3 IdentityProvider action implementations"""
+import logging
+
+from osc_lib.command import command
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import utils
from openstackclient.i18n import _
+LOG = logging.getLogger(__name__)
+
+
class CreateIdentityProvider(command.ShowOne):
"""Create new identity provider"""
@@ -169,7 +174,7 @@ class SetIdentityProvider(command.Command):
not parsed_args.remote_id and
not parsed_args.remote_id_file and
not parsed_args.description):
- self.log.error(_('No changes requested'))
+ LOG.error(_('No changes requested'))
return (None, None)
# Always set remote_ids if either is passed in
diff --git a/openstackclient/identity/v3/mapping.py b/openstackclient/identity/v3/mapping.py
index c45796e4..74ead228 100644
--- a/openstackclient/identity/v3/mapping.py
+++ b/openstackclient/identity/v3/mapping.py
@@ -16,15 +16,19 @@
"""Identity v3 federation mapping action implementations"""
import json
+import logging
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import exceptions
-from openstackclient.common import utils
from openstackclient.i18n import _
+LOG = logging.getLogger(__name__)
+
+
class _RulesReader(object):
"""Helper class capable of reading rules from files"""
@@ -159,7 +163,7 @@ class SetMapping(command.Command, _RulesReader):
identity_client = self.app.client_manager.identity
if not parsed_args.rules:
- self.app.log.error(_("No changes requested"))
+ LOG.error(_("No changes requested"))
return
rules = self._read_rules(parsed_args.rules)
diff --git a/openstackclient/identity/v3/policy.py b/openstackclient/identity/v3/policy.py
index 74a783b0..68fb2738 100644
--- a/openstackclient/identity/v3/policy.py
+++ b/openstackclient/identity/v3/policy.py
@@ -15,11 +15,12 @@
"""Identity v3 Policy action implementations"""
-import six
import sys
-from openstackclient.common import command
-from openstackclient.common import utils
+from osc_lib.command import command
+from osc_lib import utils
+import six
+
from openstackclient.i18n import _
diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py
index acf639f2..56c1d41a 100644
--- a/openstackclient/identity/v3/project.py
+++ b/openstackclient/identity/v3/project.py
@@ -15,17 +15,21 @@
"""Project action implementations"""
-import six
+import logging
from keystoneauth1 import exceptions as ks_exc
+from osc_lib.cli import parseractions
+from osc_lib.command import command
+from osc_lib import utils
+import six
-from openstackclient.common import command
-from openstackclient.common import parseractions
-from openstackclient.common import utils
from openstackclient.i18n import _
from openstackclient.identity import common
+LOG = logging.getLogger(__name__)
+
+
class CreateProject(command.ShowOne):
"""Create new project"""
@@ -112,7 +116,7 @@ class CreateProject(command.ShowOne):
project = utils.find_resource(identity_client.projects,
parsed_args.name,
domain_id=domain)
- self.log.info(_('Returning existing project %s'), project.name)
+ LOG.info(_('Returning existing project %s'), project.name)
else:
raise e
@@ -317,18 +321,21 @@ class ShowProject(command.ShowOne):
def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
+ project_str = common._get_token_resource(identity_client, 'project',
+ parsed_args.project)
+
if parsed_args.domain:
domain = common.find_domain(identity_client, parsed_args.domain)
project = utils.find_resource(
identity_client.projects,
- parsed_args.project,
+ project_str,
domain_id=domain.id,
parents_as_list=parsed_args.parents,
subtree_as_list=parsed_args.children)
else:
project = utils.find_resource(
identity_client.projects,
- parsed_args.project,
+ project_str,
parents_as_list=parsed_args.parents,
subtree_as_list=parsed_args.children)
diff --git a/openstackclient/identity/v3/region.py b/openstackclient/identity/v3/region.py
index 053e4b31..b5e46a9a 100644
--- a/openstackclient/identity/v3/region.py
+++ b/openstackclient/identity/v3/region.py
@@ -13,10 +13,10 @@
"""Identity v3 Region action implementations"""
+from osc_lib.command import command
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import utils
from openstackclient.i18n import _
diff --git a/openstackclient/identity/v3/role.py b/openstackclient/identity/v3/role.py
index e7078f44..965ca3f5 100644
--- a/openstackclient/identity/v3/role.py
+++ b/openstackclient/identity/v3/role.py
@@ -15,17 +15,21 @@
"""Identity v3 Role action implementations"""
-import six
+import logging
import sys
from keystoneauth1 import exceptions as ks_exc
+from osc_lib.command import command
+from osc_lib import utils
+import six
-from openstackclient.common import command
-from openstackclient.common import utils
from openstackclient.i18n import _
from openstackclient.identity import common
+LOG = logging.getLogger(__name__)
+
+
def _add_identity_and_resource_options_to_parser(parser):
domain_or_project = parser.add_mutually_exclusive_group()
domain_or_project.add_argument(
@@ -165,7 +169,7 @@ class CreateRole(command.ShowOne):
if parsed_args.or_show:
role = utils.find_resource(identity_client.roles,
parsed_args.name)
- self.log.info(_('Returning existing role %s'), role.name)
+ LOG.info(_('Returning existing role %s'), role.name)
else:
raise e
diff --git a/openstackclient/identity/v3/role_assignment.py b/openstackclient/identity/v3/role_assignment.py
index 521075fe..39e2336d 100644
--- a/openstackclient/identity/v3/role_assignment.py
+++ b/openstackclient/identity/v3/role_assignment.py
@@ -13,8 +13,9 @@
"""Identity v3 Assignment action implementations """
-from openstackclient.common import command
-from openstackclient.common import utils
+from osc_lib.command import command
+from osc_lib import utils
+
from openstackclient.i18n import _
from openstackclient.identity import common
diff --git a/openstackclient/identity/v3/service.py b/openstackclient/identity/v3/service.py
index 35507a63..195b2701 100644
--- a/openstackclient/identity/v3/service.py
+++ b/openstackclient/identity/v3/service.py
@@ -15,11 +15,12 @@
"""Identity v3 Service action implementations"""
-import six
import sys
-from openstackclient.common import command
-from openstackclient.common import utils
+from osc_lib.command import command
+from osc_lib import utils
+import six
+
from openstackclient.i18n import _
from openstackclient.identity import common
@@ -129,7 +130,7 @@ class SetService(command.Command):
parser.add_argument(
'service',
metavar='<service>',
- help=_('Service to update (type, name or ID)'),
+ help=_('Service to modify (type, name or ID)'),
)
parser.add_argument(
'--type',
diff --git a/openstackclient/identity/v3/service_provider.py b/openstackclient/identity/v3/service_provider.py
index f1e9f35d..0beea813 100644
--- a/openstackclient/identity/v3/service_provider.py
+++ b/openstackclient/identity/v3/service_provider.py
@@ -13,11 +13,12 @@
"""Service Provider action implementations"""
-import six
import sys
-from openstackclient.common import command
-from openstackclient.common import utils
+from osc_lib.command import command
+from osc_lib import utils
+import six
+
from openstackclient.i18n import _
diff --git a/openstackclient/identity/v3/token.py b/openstackclient/identity/v3/token.py
index 56a7497c..ecf09693 100644
--- a/openstackclient/identity/v3/token.py
+++ b/openstackclient/identity/v3/token.py
@@ -15,11 +15,11 @@
"""Identity v3 Token action implementations"""
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import exceptions
-from openstackclient.common import utils
from openstackclient.i18n import _
from openstackclient.identity import common
@@ -174,13 +174,23 @@ class IssueToken(command.ShowOne):
return parser
def take_action(self, parsed_args):
- if not self.app.client_manager.auth_ref:
+ auth_ref = self.app.client_manager.auth_ref
+ if not auth_ref:
raise exceptions.AuthorizationFailure(
_("Only an authorized user may issue a new token."))
- token = self.app.client_manager.auth_ref.service_catalog.get_token()
- if 'tenant_id' in token:
- token['project_id'] = token.pop('tenant_id')
- return zip(*sorted(six.iteritems(token)))
+
+ data = {}
+ if auth_ref.auth_token:
+ data['id'] = auth_ref.auth_token
+ if auth_ref.expires:
+ data['expires'] = auth_ref.expires
+ if auth_ref.project_id:
+ data['project_id'] = auth_ref.project_id
+ if auth_ref.user_id:
+ data['user_id'] = auth_ref.user_id
+ if auth_ref.domain_id:
+ data['domain_id'] = auth_ref.domain_id
+ return zip(*sorted(six.iteritems(data)))
class RevokeToken(command.Command):
diff --git a/openstackclient/identity/v3/trust.py b/openstackclient/identity/v3/trust.py
index 336b703c..bbc86adb 100644
--- a/openstackclient/identity/v3/trust.py
+++ b/openstackclient/identity/v3/trust.py
@@ -14,10 +14,11 @@
"""Identity v3 Trust action implementations"""
import datetime
+
+from osc_lib.command import command
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import utils
from openstackclient.i18n import _
from openstackclient.identity import common
diff --git a/openstackclient/identity/v3/unscoped_saml.py b/openstackclient/identity/v3/unscoped_saml.py
index 5cb8e486..f116174b 100644
--- a/openstackclient/identity/v3/unscoped_saml.py
+++ b/openstackclient/identity/v3/unscoped_saml.py
@@ -17,9 +17,10 @@ The first step of federated auth is to fetch an unscoped token. From there,
the user can list domains and projects they are allowed to access, and request
a scoped token."""
-from openstackclient.common import command
-from openstackclient.common import exceptions
-from openstackclient.common import utils
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
+
from openstackclient.i18n import _
diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py
index a71b4b78..dd5af06a 100644
--- a/openstackclient/identity/v3/user.py
+++ b/openstackclient/identity/v3/user.py
@@ -16,17 +16,21 @@
"""Identity v3 User action implementations"""
import copy
-import six
+import logging
import sys
from keystoneauth1 import exceptions as ks_exc
+from osc_lib.command import command
+from osc_lib import utils
+import six
-from openstackclient.common import command
-from openstackclient.common import utils
from openstackclient.i18n import _
from openstackclient.identity import common
+LOG = logging.getLogger(__name__)
+
+
class CreateUser(command.ShowOne):
"""Create new user"""
@@ -122,7 +126,7 @@ class CreateUser(command.ShowOne):
user = utils.find_resource(identity_client.users,
parsed_args.name,
domain_id=domain_id)
- self.log.info(_('Returning existing user %s'), user.name)
+ LOG.info(_('Returning existing user %s'), user.name)
else:
raise e
@@ -273,7 +277,7 @@ class SetUser(command.Command):
parser.add_argument(
'user',
metavar='<user>',
- help=_('User to change (name or ID)'),
+ help=_('User to modify (name or ID)'),
)
parser.add_argument(
'--name',
@@ -440,14 +444,16 @@ class ShowUser(command.ShowOne):
def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
+ user_str = common._get_token_resource(identity_client, 'user',
+ parsed_args.user)
if parsed_args.domain:
domain = common.find_domain(identity_client, parsed_args.domain)
user = utils.find_resource(identity_client.users,
- parsed_args.user,
+ user_str,
domain_id=domain.id)
else:
user = utils.find_resource(identity_client.users,
- parsed_args.user)
+ user_str)
user._info.pop('links')
return zip(*sorted(six.iteritems(user._info)))
diff --git a/openstackclient/image/client.py b/openstackclient/image/client.py
index 9c45a63f..0ef694f4 100644
--- a/openstackclient/image/client.py
+++ b/openstackclient/image/client.py
@@ -15,7 +15,8 @@
import logging
-from openstackclient.common import utils
+from osc_lib import utils
+
from openstackclient.i18n import _
diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py
index e2109fca..e7b60e5a 100644
--- a/openstackclient/image/v1/image.py
+++ b/openstackclient/image/v1/image.py
@@ -17,8 +17,8 @@
import argparse
import io
+import logging
import os
-import six
import sys
if os.name == "nt":
@@ -27,10 +27,12 @@ else:
msvcrt = None
from glanceclient.common import utils as gc_utils
+from osc_lib.cli import parseractions
+from osc_lib.command import command
+from osc_lib import utils
+import six
+
from openstackclient.api import utils as api_utils
-from openstackclient.common import command
-from openstackclient.common import parseractions
-from openstackclient.common import utils
from openstackclient.i18n import _
@@ -38,6 +40,9 @@ DEFAULT_CONTAINER_FORMAT = 'bare'
DEFAULT_DISK_FORMAT = 'raw'
+LOG = logging.getLogger(__name__)
+
+
def _format_visibility(data):
"""Return a formatted visibility string
@@ -188,10 +193,8 @@ class CreateImage(command.ShowOne):
image_client = self.app.client_manager.image
if getattr(parsed_args, 'owner', None) is not None:
- self.log.warning(_(
- 'The --owner option is deprecated, '
- 'please use --project instead.'
- ))
+ LOG.warning(_('The --owner option is deprecated, '
+ 'please use --project instead.'))
# Build an attribute dict from the parsed args, only include
# attributes that were actually set on the command line
@@ -607,10 +610,8 @@ class SetImage(command.Command):
image_client = self.app.client_manager.image
if getattr(parsed_args, 'owner', None) is not None:
- self.log.warning(_(
- 'The --owner option is deprecated, '
- 'please use --project instead.'
- ))
+ LOG.warning(_('The --owner option is deprecated, '
+ 'please use --project instead.'))
kwargs = {}
copy_attrs = ('name', 'owner', 'min_disk', 'min_ram', 'properties',
@@ -683,18 +684,13 @@ class SetImage(command.Command):
# will do a chunked transfer
kwargs["data"] = sys.stdin
else:
- self.log.warning(_('Use --stdin to enable read '
- 'image data from standard '
- 'input'))
+ LOG.warning(_('Use --stdin to enable read image '
+ 'data from standard input'))
if image.properties and parsed_args.properties:
image.properties.update(kwargs['properties'])
kwargs['properties'] = image.properties
- if not kwargs:
- self.log.warning('no arguments specified')
- return
-
image = image_client.images.update(image.id, **kwargs)
finally:
# Clean up open files - make sure data isn't a string
diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py
index fa1de424..309b1b6b 100644
--- a/openstackclient/image/v2/image.py
+++ b/openstackclient/image/v2/image.py
@@ -16,15 +16,16 @@
"""Image V2 Action Implementations"""
import argparse
-import six
+import logging
from glanceclient.common import utils as gc_utils
+from osc_lib.cli import parseractions
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
+import six
from openstackclient.api import utils as api_utils
-from openstackclient.common import command
-from openstackclient.common import exceptions
-from openstackclient.common import parseractions
-from openstackclient.common import utils
from openstackclient.i18n import _
from openstackclient.identity import common
@@ -33,6 +34,9 @@ DEFAULT_CONTAINER_FORMAT = 'bare'
DEFAULT_DISK_FORMAT = 'raw'
+LOG = logging.getLogger(__name__)
+
+
def _format_image(image):
"""Format an image to make it more consistent with OSC operations. """
@@ -280,10 +284,8 @@ class CreateImage(command.ShowOne):
project_arg = parsed_args.project
if parsed_args.owner:
project_arg = parsed_args.owner
- self.log.warning(_(
- 'The --owner option is deprecated, '
- 'please use --project instead.'
- ))
+ LOG.warning(_('The --owner option is deprecated, '
+ 'please use --project instead.'))
if project_arg:
kwargs['owner'] = common.find_project(
identity_client,
@@ -301,7 +303,7 @@ class CreateImage(command.ShowOne):
"the same time"))
if fp is None and parsed_args.file:
- self.log.warning(_("Failed to get an image file."))
+ LOG.warning(_("Failed to get an image file."))
return {}, {}
if parsed_args.owner:
@@ -372,13 +374,27 @@ class DeleteImage(command.Command):
return parser
def take_action(self, parsed_args):
+
+ del_result = 0
image_client = self.app.client_manager.image
for image in parsed_args.images:
- image_obj = utils.find_resource(
- image_client.images,
- image,
- )
- image_client.images.delete(image_obj.id)
+ try:
+ image_obj = utils.find_resource(
+ image_client.images,
+ image,
+ )
+ image_client.images.delete(image_obj.id)
+ except Exception as e:
+ del_result += 1
+ LOG.error(_("Failed to delete image with name or "
+ "ID '%(image)s': %(e)s"),
+ {'image': image, 'e': e})
+
+ total = len(parsed_args.images)
+ if (del_result > 0):
+ msg = (_("Failed to delete %(dresult)s of %(total)s images.")
+ % {'dresult': del_result, 'total': total})
+ raise exceptions.CommandError(msg)
class ListImage(command.Lister):
@@ -792,10 +808,8 @@ class SetImage(command.Command):
project_arg = parsed_args.project
if parsed_args.owner:
project_arg = parsed_args.owner
- self.log.warning(_(
- 'The --owner option is deprecated, '
- 'please use --project instead.'
- ))
+ LOG.warning(_('The --owner option is deprecated, '
+ 'please use --project instead.'))
if project_arg:
kwargs['owner'] = common.find_project(
identity_client,
@@ -803,11 +817,6 @@ class SetImage(command.Command):
parsed_args.project_domain,
).id
- # Checks if anything that requires getting the image
- if not (kwargs or parsed_args.deactivate or parsed_args.activate):
- msg = _("No arguments specified")
- raise exceptions.CommandError(msg)
-
image = utils.find_resource(
image_client.images, parsed_args.image)
@@ -819,10 +828,6 @@ class SetImage(command.Command):
image_client.images.reactivate(image.id)
activation_status = "activated"
- # Check if need to do the actual update
- if not kwargs:
- return {}, {}
-
if parsed_args.tags:
# Tags should be extended, but duplicates removed
kwargs['tags'] = list(set(image.tags).union(set(parsed_args.tags)))
@@ -831,7 +836,8 @@ class SetImage(command.Command):
image = image_client.images.update(image.id, **kwargs)
except Exception as e:
if activation_status is not None:
- print("Image %s was %s." % (image.id, activation_status))
+ LOG.info(_("Image %(id)s was %(status)s."),
+ {'id': image.id, 'status': activation_status})
raise e
@@ -895,10 +901,6 @@ class UnsetImage(command.Command):
parsed_args.image,
)
- if not (parsed_args.tags or parsed_args.properties):
- msg = _("No arguments specified")
- raise exceptions.CommandError(msg)
-
kwargs = {}
tagret = 0
propret = 0
@@ -907,8 +909,8 @@ class UnsetImage(command.Command):
try:
image_client.image_tags.delete(image.id, k)
except Exception:
- self.log.error(_("tag unset failed,"
- " '%s' is a nonexistent tag ") % k)
+ LOG.error(_("tag unset failed, '%s' is a "
+ "nonexistent tag "), k)
tagret += 1
if parsed_args.properties:
@@ -916,8 +918,8 @@ class UnsetImage(command.Command):
try:
assert(k in image.keys())
except AssertionError:
- self.log.error(_("property unset failed,"
- " '%s' is a nonexistent property ") % k)
+ LOG.error(_("property unset failed, '%s' is a "
+ "nonexistent property "), k)
propret += 1
image_client.images.update(
image.id,
diff --git a/openstackclient/network/client.py b/openstackclient/network/client.py
index d711f4fc..d12987dd 100644
--- a/openstackclient/network/client.py
+++ b/openstackclient/network/client.py
@@ -15,8 +15,8 @@ import logging
from openstack import connection
from openstack import profile
+from osc_lib import utils
-from openstackclient.common import utils
from openstackclient.i18n import _
@@ -36,6 +36,7 @@ def make_client(instance):
prof = profile.Profile()
prof.set_region(API_NAME, instance._region_name)
prof.set_version(API_NAME, instance._api_version[API_NAME])
+ prof.set_interface(API_NAME, instance._interface)
conn = connection.Connection(authenticator=instance.session.auth,
verify=instance.session.verify,
cert=instance.session.cert,
diff --git a/openstackclient/network/common.py b/openstackclient/network/common.py
index 4028ac0d..f62840fc 100644
--- a/openstackclient/network/common.py
+++ b/openstackclient/network/common.py
@@ -12,13 +12,18 @@
#
import abc
+import logging
+
+from osc_lib.command import command
+from osc_lib import exceptions
import six
-from openstackclient.common import command
-from openstackclient.common import exceptions
from openstackclient.i18n import _
+LOG = logging.getLogger(__name__)
+
+
@six.add_metaclass(abc.ABCMeta)
class NetworkAndComputeCommand(command.Command):
"""Network and Compute Command
@@ -38,10 +43,10 @@ class NetworkAndComputeCommand(command.Command):
parsed_args)
def get_parser(self, prog_name):
- self.log.debug('get_parser(%s)', prog_name)
+ LOG.debug('get_parser(%s)', prog_name)
parser = super(NetworkAndComputeCommand, self).get_parser(prog_name)
parser = self.update_parser_common(parser)
- self.log.debug('common parser: %s', parser)
+ LOG.debug('common parser: %s', parser)
if self.app.client_manager.is_network_endpoint_enabled():
return self.update_parser_network(parser)
else:
@@ -101,7 +106,7 @@ class NetworkAndComputeDelete(NetworkAndComputeCommand):
"name_or_id": r,
"e": e,
}
- self.app.log.error(msg)
+ LOG.error(msg)
ret += 1
if ret:
@@ -133,10 +138,10 @@ class NetworkAndComputeLister(command.Lister):
parsed_args)
def get_parser(self, prog_name):
- self.log.debug('get_parser(%s)', prog_name)
+ LOG.debug('get_parser(%s)', prog_name)
parser = super(NetworkAndComputeLister, self).get_parser(prog_name)
parser = self.update_parser_common(parser)
- self.log.debug('common parser: %s', parser)
+ LOG.debug('common parser: %s', parser)
if self.app.client_manager.is_network_endpoint_enabled():
return self.update_parser_network(parser)
else:
@@ -184,10 +189,10 @@ class NetworkAndComputeShowOne(command.ShowOne):
parsed_args)
def get_parser(self, prog_name):
- self.log.debug('get_parser(%s)', prog_name)
+ LOG.debug('get_parser(%s)', prog_name)
parser = super(NetworkAndComputeShowOne, self).get_parser(prog_name)
parser = self.update_parser_common(parser)
- self.log.debug('common parser: %s', parser)
+ LOG.debug('common parser: %s', parser)
if self.app.client_manager.is_network_endpoint_enabled():
return self.update_parser_network(parser)
else:
diff --git a/openstackclient/network/v2/address_scope.py b/openstackclient/network/v2/address_scope.py
index dbc6865f..6cd13f8c 100644
--- a/openstackclient/network/v2/address_scope.py
+++ b/openstackclient/network/v2/address_scope.py
@@ -13,13 +13,19 @@
"""Address scope action implementations"""
-from openstackclient.common import command
-from openstackclient.common import exceptions
-from openstackclient.common import utils
+import logging
+
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
+
from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
+LOG = logging.getLogger(__name__)
+
+
def _get_columns(item):
columns = list(item.keys())
if 'tenant_id' in columns:
@@ -94,7 +100,7 @@ class CreateAddressScope(command.ShowOne):
columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns, formatters={})
- return columns, data
+ return (columns, data)
class DeleteAddressScope(command.Command):
@@ -121,9 +127,9 @@ class DeleteAddressScope(command.Command):
client.delete_address_scope(obj)
except Exception as e:
result += 1
- self.app.log.error(_("Failed to delete address scope with "
- "name or ID '%(scope)s': %(e)s")
- % {'scope': scope, 'e': e})
+ LOG.error(_("Failed to delete address scope with "
+ "name or ID '%(scope)s': %(e)s"),
+ {'scope': scope, 'e': e})
if result > 0:
total = len(parsed_args.address_scope)
@@ -200,9 +206,6 @@ class SetAddressScope(command.Command):
attrs['shared'] = True
if parsed_args.no_share:
attrs['shared'] = False
- if attrs == {}:
- msg = _("Nothing specified to be set.")
- raise exceptions.CommandError(msg)
client.update_address_scope(obj, **attrs)
@@ -227,4 +230,4 @@ class ShowAddressScope(command.ShowOne):
columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns, formatters={})
- return columns, data
+ return (columns, data)
diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py
index 21f86599..8fbf049e 100644
--- a/openstackclient/network/v2/floating_ip.py
+++ b/openstackclient/network/v2/floating_ip.py
@@ -13,7 +13,8 @@
"""IP Floating action implementations"""
-from openstackclient.common import utils
+from osc_lib import utils
+
from openstackclient.i18n import _
from openstackclient.network import common
@@ -109,26 +110,28 @@ class CreateFloatingIP(common.NetworkAndComputeShowOne):
return (columns, data)
-class DeleteFloatingIP(common.NetworkAndComputeCommand):
- """Delete floating IP"""
+class DeleteFloatingIP(common.NetworkAndComputeDelete):
+ """Delete floating IP(s)"""
+
+ # Used by base class to find resources in parsed_args.
+ resource = 'floating_ip'
+ r = None
def update_parser_common(self, parser):
parser.add_argument(
'floating_ip',
metavar="<floating-ip>",
- help=_("Floating IP to delete (IP address or ID)")
+ nargs="+",
+ help=_("Floating IP(s) to delete (IP address or ID)")
)
return parser
def take_action_network(self, client, parsed_args):
- obj = client.find_ip(parsed_args.floating_ip)
+ obj = client.find_ip(self.r, ignore_missing=False)
client.delete_ip(obj)
def take_action_compute(self, client, parsed_args):
- obj = utils.find_resource(
- client.floating_ips,
- parsed_args.floating_ip,
- )
+ obj = utils.find_resource(client.floating_ips, self.r)
client.floating_ips.delete(obj.id)
diff --git a/openstackclient/network/v2/ip_availability.py b/openstackclient/network/v2/ip_availability.py
index cc240338..1d7b2aed 100644
--- a/openstackclient/network/v2/ip_availability.py
+++ b/openstackclient/network/v2/ip_availability.py
@@ -13,8 +13,9 @@
"""IP Availability Info implementations"""
-from openstackclient.common import command
-from openstackclient.common import utils
+from osc_lib.command import command
+from osc_lib import utils
+
from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
@@ -40,15 +41,17 @@ class ListIPAvailability(command.Lister):
parser.add_argument(
'--ip-version',
type=int,
+ default=4,
choices=[4, 6],
metavar='<ip-version>',
dest='ip_version',
- help=_("List IP availability of given IP version networks"),
+ help=_("List IP availability of given IP version "
+ "networks (default is 4)"),
)
parser.add_argument(
'--project',
metavar='<project>',
- help=_("List IP availability of given project"),
+ help=_("List IP availability of given project (name or ID)"),
)
identity_common.add_project_domain_option_to_parser(parser)
return parser
@@ -106,4 +109,4 @@ class ShowIPAvailability(command.ShowOne):
ignore_missing=False)
columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns, formatters=_formatters)
- return columns, data
+ return (columns, data)
diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py
index bf01e2ec..31dfc798 100644
--- a/openstackclient/network/v2/network.py
+++ b/openstackclient/network/v2/network.py
@@ -13,9 +13,9 @@
"""Network action implementations"""
-from openstackclient.common import command
-from openstackclient.common import exceptions
-from openstackclient.common import utils
+from osc_lib.command import command
+from osc_lib import utils
+
from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
from openstackclient.network import common
@@ -104,11 +104,11 @@ def _add_additional_network_options(parser):
parser.add_argument(
'--provider-network-type',
metavar='<provider-network-type>',
- choices=['flat', 'gre', 'local',
+ choices=['flat', 'geneve', 'gre', 'local',
'vlan', 'vxlan'],
help=_("The physical mechanism by which the virtual network "
"is implemented. The supported options are: "
- "flat, gre, local, vlan, vxlan"))
+ "flat, geneve, gre, local, vlan, vxlan."))
parser.add_argument(
'--provider-physical-network',
metavar='<provider-physical-network>',
@@ -119,8 +119,8 @@ def _add_additional_network_options(parser):
'--provider-segment',
metavar='<provider-segment>',
dest='segmentation_id',
- help=_("VLAN ID for VLAN networks or Tunnel ID for GRE/VXLAN "
- "networks"))
+ help=_("VLAN ID for VLAN networks or Tunnel ID for "
+ "GENEVE/GRE/VXLAN networks"))
vlan_transparent_grp = parser.add_mutually_exclusive_group()
vlan_transparent_grp.add_argument(
@@ -434,10 +434,6 @@ class SetNetwork(command.Command):
obj = client.find_network(parsed_args.network, ignore_missing=False)
attrs = _get_attrs(self.app.client_manager, parsed_args)
- if attrs == {}:
- msg = _("Nothing specified to be set")
- raise exceptions.CommandError(msg)
-
client.update_network(obj, **attrs)
diff --git a/openstackclient/network/v2/network_segment.py b/openstackclient/network/v2/network_segment.py
index 818ffc02..bedf15f7 100644
--- a/openstackclient/network/v2/network_segment.py
+++ b/openstackclient/network/v2/network_segment.py
@@ -15,8 +15,9 @@
# TODO(rtheis): Add description and name properties when support is available.
-from openstackclient.common import command
-from openstackclient.common import utils
+from osc_lib.command import command
+from osc_lib import utils
+
from openstackclient.i18n import _
diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py
index ca02281f..5d1431b5 100644
--- a/openstackclient/network/v2/port.py
+++ b/openstackclient/network/v2/port.py
@@ -14,12 +14,14 @@
"""Port action implementations"""
import argparse
+import json
import logging
-from openstackclient.common import command
-from openstackclient.common import exceptions
-from openstackclient.common import parseractions
-from openstackclient.common import utils
+from osc_lib.cli import parseractions
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
+
from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
@@ -62,6 +64,32 @@ def _get_columns(item):
return tuple(sorted(columns))
+class JSONKeyValueAction(argparse.Action):
+ """A custom action to parse arguments as JSON or key=value pairs
+
+ Ensures that ``dest`` is a dict
+ """
+
+ def __call__(self, parser, namespace, values, option_string=None):
+
+ # Make sure we have an empty dict rather than None
+ if getattr(namespace, self.dest, None) is None:
+ setattr(namespace, self.dest, {})
+
+ # Try to load JSON first before falling back to <key>=<value>.
+ current_dest = getattr(namespace, self.dest)
+ try:
+ current_dest.update(json.loads(values))
+ except ValueError as e:
+ if '=' in values:
+ current_dest.update([values.split('=', 1)])
+ else:
+ msg = _("Expected '<key>=<value>' or JSON data for option "
+ "%(option)s, but encountered JSON parsing error: "
+ "%(error)s") % {"option": option_string, "error": e}
+ raise argparse.ArgumentTypeError(msg)
+
+
def _get_attrs(client_manager, parsed_args):
attrs = {}
@@ -218,9 +246,9 @@ class CreatePort(command.ShowOne):
parser.add_argument(
'--binding-profile',
metavar='<binding-profile>',
- action=parseractions.KeyValueAction,
- help=_("Custom data to be passed as binding:profile: "
- "<key>=<value> "
+ action=JSONKeyValueAction,
+ help=_("Custom data to be passed as binding:profile. Data may "
+ "be passed as <key>=<value> or JSON. "
"(repeat option to set multiple binding:profile data)")
)
admin_group = parser.add_mutually_exclusive_group()
@@ -266,7 +294,7 @@ class CreatePort(command.ShowOne):
columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns, formatters=_formatters)
- return columns, data
+ return (columns, data)
class DeletePort(command.Command):
@@ -284,10 +312,23 @@ class DeletePort(command.Command):
def take_action(self, parsed_args):
client = self.app.client_manager.network
+ result = 0
for port in parsed_args.port:
- res = client.find_port(port)
- client.delete_port(res)
+ try:
+ obj = client.find_port(port, ignore_missing=False)
+ client.delete_port(obj)
+ except Exception as e:
+ result += 1
+ LOG.error(_("Failed to delete port with "
+ "name or ID '%(port)s': %(e)s"),
+ {'port': port, 'e': e})
+
+ if result > 0:
+ total = len(parsed_args.port)
+ msg = (_("%(result)s of %(total)s ports failed "
+ "to delete.") % {'result': result, 'total': total})
+ raise exceptions.CommandError(msg)
class ListPort(command.Lister):
@@ -376,9 +417,9 @@ class SetPort(command.Command):
binding_profile.add_argument(
'--binding-profile',
metavar='<binding-profile>',
- action=parseractions.KeyValueAction,
- help=_("Custom data to be passed as binding:profile: "
- "<key>=<value> "
+ action=JSONKeyValueAction,
+ help=_("Custom data to be passed as binding:profile. Data may "
+ "be passed as <key>=<value> or JSON. "
"(repeat option to set multiple binding:profile data)")
)
binding_profile.add_argument(
@@ -413,9 +454,6 @@ class SetPort(command.Command):
elif parsed_args.no_fixed_ip:
attrs['fixed_ips'] = []
- if attrs == {}:
- msg = _("Nothing specified to be set")
- raise exceptions.CommandError(msg)
client.update_port(obj, **attrs)
@@ -436,4 +474,4 @@ class ShowPort(command.ShowOne):
obj = client.find_port(parsed_args.port, ignore_missing=False)
columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns, formatters=_formatters)
- return columns, data
+ return (columns, data)
diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py
index a2f0df1d..2f41838f 100644
--- a/openstackclient/network/v2/router.py
+++ b/openstackclient/network/v2/router.py
@@ -17,10 +17,11 @@ import argparse
import json
import logging
-from openstackclient.common import command
-from openstackclient.common import exceptions
-from openstackclient.common import parseractions
-from openstackclient.common import utils
+from osc_lib.cli import parseractions
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
+
from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
@@ -204,7 +205,7 @@ class CreateRouter(command.ShowOne):
columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns, formatters=_formatters)
- return columns, data
+ return (columns, data)
class DeleteRouter(command.Command):
@@ -222,9 +223,23 @@ class DeleteRouter(command.Command):
def take_action(self, parsed_args):
client = self.app.client_manager.network
+ result = 0
+
for router in parsed_args.router:
- obj = client.find_router(router)
- client.delete_router(obj)
+ try:
+ obj = client.find_router(router, ignore_missing=False)
+ client.delete_router(obj)
+ except Exception as e:
+ result += 1
+ LOG.error(_("Failed to delete router with "
+ "name or ID '%(router)s': %(e)s")
+ % {'router': router, 'e': e})
+
+ if result > 0:
+ total = len(parsed_args.router)
+ msg = (_("%(result)s of %(total)s routers failed "
+ "to delete.") % {'result': result, 'total': total})
+ raise exceptions.CommandError(msg)
class ListRouter(command.Lister):
@@ -294,7 +309,7 @@ class RemovePortFromRouter(command.Command):
parser.add_argument(
'port',
metavar='<port>',
- help=_("Port to be removed (name or ID)")
+ help=_("Port to be removed and deleted (name or ID)")
)
return parser
@@ -426,10 +441,6 @@ class SetRouter(command.Command):
route['nexthop'] = route.pop('gateway')
attrs['routes'] = obj.routes + parsed_args.routes
- if attrs == {}:
- msg = _("Nothing specified to be set")
- raise exceptions.CommandError(msg)
-
client.update_router(obj, **attrs)
@@ -450,4 +461,4 @@ class ShowRouter(command.ShowOne):
obj = client.find_router(parsed_args.router, ignore_missing=False)
columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns, formatters=_formatters)
- return columns, data
+ return (columns, data)
diff --git a/openstackclient/network/v2/security_group.py b/openstackclient/network/v2/security_group.py
index 1ef2754e..f832f721 100644
--- a/openstackclient/network/v2/security_group.py
+++ b/openstackclient/network/v2/security_group.py
@@ -14,9 +14,10 @@
"""Security Group action implementations"""
import argparse
+
+from osc_lib import utils
import six
-from openstackclient.common import utils
from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
from openstackclient.network import common
@@ -163,26 +164,28 @@ class CreateSecurityGroup(common.NetworkAndComputeShowOne):
return (display_columns, data)
-class DeleteSecurityGroup(common.NetworkAndComputeCommand):
- """Delete a security group"""
+class DeleteSecurityGroup(common.NetworkAndComputeDelete):
+ """Delete security group(s)"""
+
+ # Used by base class to find resources in parsed_args.
+ resource = 'group'
+ r = None
def update_parser_common(self, parser):
parser.add_argument(
'group',
metavar='<group>',
- help=_("Security group to delete (name or ID)")
+ nargs="+",
+ help=_("Security group(s) to delete (name or ID)"),
)
return parser
def take_action_network(self, client, parsed_args):
- obj = client.find_security_group(parsed_args.group)
+ obj = client.find_security_group(self.r, ignore_missing=False)
client.delete_security_group(obj)
def take_action_compute(self, client, parsed_args):
- data = utils.find_resource(
- client.security_groups,
- parsed_args.group,
- )
+ data = utils.find_resource(client.security_groups, self.r)
client.security_groups.delete(data.id)
diff --git a/openstackclient/network/v2/security_group_rule.py b/openstackclient/network/v2/security_group_rule.py
index 5abe9b9d..e3be44ec 100644
--- a/openstackclient/network/v2/security_group_rule.py
+++ b/openstackclient/network/v2/security_group_rule.py
@@ -14,16 +14,17 @@
"""Security Group Rule action implementations"""
import argparse
-import six
try:
from novaclient.v2 import security_group_rules as compute_secgroup_rules
except ImportError:
from novaclient.v1_1 import security_group_rules as compute_secgroup_rules
-from openstackclient.common import exceptions
-from openstackclient.common import parseractions
-from openstackclient.common import utils
+from osc_lib.cli import parseractions
+from osc_lib import exceptions
+from osc_lib import utils
+import six
+
from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
from openstackclient.network import common
@@ -332,23 +333,29 @@ class CreateSecurityGroupRule(common.NetworkAndComputeShowOne):
return _format_security_group_rule_show(obj._info)
-class DeleteSecurityGroupRule(common.NetworkAndComputeCommand):
- """Delete a security group rule"""
+class DeleteSecurityGroupRule(common.NetworkAndComputeDelete):
+ """Delete security group rule(s)"""
+
+ # Used by base class to find resources in parsed_args.
+ resource = 'rule'
+ r = None
def update_parser_common(self, parser):
parser.add_argument(
'rule',
metavar='<rule>',
- help=_("Security group rule to delete (ID only)")
+ nargs="+",
+ help=_("Security group rule(s) to delete (ID only)")
)
return parser
def take_action_network(self, client, parsed_args):
- obj = client.find_security_group_rule(parsed_args.rule)
+ obj = client.find_security_group_rule(
+ self.r, ignore_missing=False)
client.delete_security_group_rule(obj)
def take_action_compute(self, client, parsed_args):
- client.security_group_rules.delete(parsed_args.rule)
+ client.security_group_rules.delete(self.r)
class ListSecurityGroupRule(common.NetworkAndComputeLister):
@@ -519,8 +526,8 @@ class ShowSecurityGroupRule(common.NetworkAndComputeShowOne):
break
if obj is None:
- msg = _("Could not find security group rule with ID ") + \
- parsed_args.rule
+ msg = _("Could not find security group rule "
+ "with ID '%s'") % parsed_args.rule
raise exceptions.CommandError(msg)
# NOTE(rtheis): Format security group rule
diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py
index e7e1be99..b076d82e 100644
--- a/openstackclient/network/v2/subnet.py
+++ b/openstackclient/network/v2/subnet.py
@@ -12,16 +12,22 @@
#
"""Subnet action implementations"""
+
import copy
+import logging
+
+from osc_lib.cli import parseractions
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
-from openstackclient.common import command
-from openstackclient.common import exceptions
-from openstackclient.common import parseractions
-from openstackclient.common import utils
from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
+LOG = logging.getLogger(__name__)
+
+
def _format_allocation_pools(data):
pool_formatted = ['%s-%s' % (pool.get('start', ''), pool.get('end', ''))
for pool in data]
@@ -136,6 +142,9 @@ def _get_attrs(client_manager, parsed_args, is_create=True):
attrs['ipv6_ra_mode'] = parsed_args.ipv6_ra_mode
if parsed_args.ipv6_address_mode is not None:
attrs['ipv6_address_mode'] = parsed_args.ipv6_address_mode
+ if 'network_segment' in parsed_args:
+ attrs['segment_id'] = client.find_segment(
+ parsed_args.network_segment, ignore_missing=False).id
if 'gateway' in parsed_args and parsed_args.gateway is not None:
gateway = parsed_args.gateway.lower()
@@ -226,7 +235,7 @@ class CreateSubnet(command.ShowOne):
"'auto': Gateway address should automatically be chosen "
"from within the subnet itself, 'none': This subnet will "
"not use a gateway, e.g.: --gateway 192.168.9.1, "
- "--gateway auto, --gateway none (default is 'auto')")
+ "--gateway auto, --gateway none (default is 'auto').")
)
parser.add_argument(
'--ip-version',
@@ -235,7 +244,7 @@ class CreateSubnet(command.ShowOne):
choices=[4, 6],
help=_("IP version (default is 4). Note that when subnet pool is "
"specified, IP version is determined from the subnet pool "
- "and this option is ignored")
+ "and this option is ignored.")
)
parser.add_argument(
'--ipv6-ra-mode',
@@ -249,6 +258,13 @@ class CreateSubnet(command.ShowOne):
help=_("IPv6 address mode, "
"valid modes: [dhcpv6-stateful, dhcpv6-stateless, slaac]")
)
+ if self.app.options.os_beta_command:
+ parser.add_argument(
+ '--network-segment',
+ metavar='<network-segment>',
+ help=_("Network segment to associate with this subnet "
+ "(ID only)")
+ )
parser.add_argument(
'--network',
required=True,
@@ -268,21 +284,37 @@ class CreateSubnet(command.ShowOne):
class DeleteSubnet(command.Command):
- """Delete subnet"""
+ """Delete subnet(s)"""
def get_parser(self, prog_name):
parser = super(DeleteSubnet, self).get_parser(prog_name)
parser.add_argument(
'subnet',
metavar="<subnet>",
- help=_("Subnet to delete (name or ID)")
+ nargs='+',
+ help=_("Subnet(s) to delete (name or ID)")
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.network
- client.delete_subnet(
- client.find_subnet(parsed_args.subnet))
+ result = 0
+
+ for subnet in parsed_args.subnet:
+ try:
+ obj = client.find_subnet(subnet, ignore_missing=False)
+ client.delete_subnet(obj)
+ except Exception as e:
+ result += 1
+ LOG.error(_("Failed to delete subnet with "
+ "name or ID '%(subnet)s': %(e)s")
+ % {'subnet': subnet, 'e': e})
+
+ if result > 0:
+ total = len(parsed_args.subnet)
+ msg = (_("%(result)s of %(total)s subnets failed "
+ "to delete.") % {'result': result, 'total': total})
+ raise exceptions.CommandError(msg)
class ListSubnet(command.Lister):
@@ -302,7 +334,7 @@ class ListSubnet(command.Lister):
choices=[4, 6],
metavar='<ip-version>',
dest='ip_version',
- help=_("List only subnets of given IP version in output"
+ help=_("List only subnets of given IP version in output."
"Allowed values for IP version are 4 and 6."),
)
return parser
@@ -363,7 +395,7 @@ class SetSubnet(command.Command):
help=_("Specify a gateway for the subnet. The options are: "
"<ip-address>: Specific IP address to use as the gateway, "
"'none': This subnet will not use a gateway, "
- "e.g.: --gateway 192.168.9.1, --gateway none")
+ "e.g.: --gateway 192.168.9.1, --gateway none.")
)
_get_common_parse_arguments(parser)
return parser
@@ -373,9 +405,6 @@ class SetSubnet(command.Command):
obj = client.find_subnet(parsed_args.subnet, ignore_missing=False)
attrs = _get_attrs(self.app.client_manager, parsed_args,
is_create=False)
- if not attrs:
- msg = "Nothing specified to be set"
- raise exceptions.CommandError(msg)
if 'dns_nameservers' in attrs:
attrs['dns_nameservers'] += obj.dns_nameservers
if 'host_routes' in attrs:
diff --git a/openstackclient/network/v2/subnet_pool.py b/openstackclient/network/v2/subnet_pool.py
index a1a94426..55dfed83 100644
--- a/openstackclient/network/v2/subnet_pool.py
+++ b/openstackclient/network/v2/subnet_pool.py
@@ -13,14 +13,20 @@
"""Subnet pool action implementations"""
-from openstackclient.common import command
-from openstackclient.common import exceptions
-from openstackclient.common import parseractions
-from openstackclient.common import utils
+import logging
+
+from osc_lib.cli import parseractions
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
+
from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
+LOG = logging.getLogger(__name__)
+
+
def _get_columns(item):
columns = list(item.keys())
if 'tenant_id' in columns:
@@ -176,21 +182,37 @@ class CreateSubnetPool(command.ShowOne):
class DeleteSubnetPool(command.Command):
- """Delete subnet pool"""
+ """Delete subnet pool(s)"""
def get_parser(self, prog_name):
parser = super(DeleteSubnetPool, self).get_parser(prog_name)
parser.add_argument(
'subnet_pool',
metavar='<subnet-pool>',
- help=_("Subnet pool to delete (name or ID)")
+ nargs='+',
+ help=_("Subnet pool(s) to delete (name or ID)")
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.network
- obj = client.find_subnet_pool(parsed_args.subnet_pool)
- client.delete_subnet_pool(obj)
+ result = 0
+
+ for pool in parsed_args.subnet_pool:
+ try:
+ obj = client.find_subnet_pool(pool, ignore_missing=False)
+ client.delete_subnet_pool(obj)
+ except Exception as e:
+ result += 1
+ LOG.error(_("Failed to delete subnet pool with "
+ "name or ID '%(pool)s': %(e)s")
+ % {'pool': pool, 'e': e})
+
+ if result > 0:
+ total = len(parsed_args.subnet_pool)
+ msg = (_("%(result)s of %(total)s subnet pools failed "
+ "to delete.") % {'result': result, 'total': total})
+ raise exceptions.CommandError(msg)
class ListSubnetPool(command.Lister):
@@ -286,9 +308,6 @@ class SetSubnetPool(command.Command):
ignore_missing=False)
attrs = _get_attrs(self.app.client_manager, parsed_args)
- if attrs == {}:
- msg = _("Nothing specified to be set")
- raise exceptions.CommandError(msg)
# Existing prefixes must be a subset of the new prefixes.
if 'prefixes' in attrs:
diff --git a/openstackclient/object/client.py b/openstackclient/object/client.py
index 3af6f8a0..cb3ddc28 100644
--- a/openstackclient/object/client.py
+++ b/openstackclient/object/client.py
@@ -15,8 +15,9 @@
"""Object client"""
+from osc_lib import utils
+
from openstackclient.api import object_store_v1
-from openstackclient.common import utils
DEFAULT_API_VERSION = '1'
API_VERSION_OPTION = 'os_object_api_version'
diff --git a/openstackclient/object/v1/account.py b/openstackclient/object/v1/account.py
index 543ce4f3..801fe450 100644
--- a/openstackclient/object/v1/account.py
+++ b/openstackclient/object/v1/account.py
@@ -13,12 +13,11 @@
"""Account v1 action implementations"""
+from osc_lib.cli import parseractions
+from osc_lib.command import command
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import parseractions
-from openstackclient.common import utils
-
class SetAccount(command.Command):
"""Set account properties"""
diff --git a/openstackclient/object/v1/container.py b/openstackclient/object/v1/container.py
index 80b84238..2b021ec2 100644
--- a/openstackclient/object/v1/container.py
+++ b/openstackclient/object/v1/container.py
@@ -16,12 +16,11 @@
"""Container v1 action implementations"""
+from osc_lib.cli import parseractions
+from osc_lib.command import command
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import parseractions
-from openstackclient.common import utils
-
class CreateContainer(command.Lister):
"""Create new container"""
diff --git a/openstackclient/object/v1/object.py b/openstackclient/object/v1/object.py
index f9a55e9c..39dba3d5 100644
--- a/openstackclient/object/v1/object.py
+++ b/openstackclient/object/v1/object.py
@@ -16,12 +16,11 @@
"""Object v1 action implementations"""
+from osc_lib.cli import parseractions
+from osc_lib.command import command
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import parseractions
-from openstackclient.common import utils
-
class CreateObject(command.Lister):
"""Upload object to container"""
diff --git a/openstackclient/releasenotes/notes/bug-1543222-6f8579344ff5c958.yaml b/openstackclient/releasenotes/notes/bug-1543222-6f8579344ff5c958.yaml
deleted file mode 100644
index c783d013..00000000
--- a/openstackclient/releasenotes/notes/bug-1543222-6f8579344ff5c958.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-fixes:
- - Keystone V3 `user password set` is a self-service operation. It should
- not required a scoped token as it is not considered a `scoped operation`.
- [Bug `1543222 <https://bugs.launchpad.net/bugs/1543222>`_]
-
diff --git a/openstackclient/shell.py b/openstackclient/shell.py
index 9179ad01..49a06040 100644
--- a/openstackclient/shell.py
+++ b/openstackclient/shell.py
@@ -26,16 +26,17 @@ from cliff import app
from cliff import command
from cliff import complete
from cliff import help
+from osc_lib.command import timing
+from osc_lib import exceptions as exc
+from osc_lib import logs
+from osc_lib import utils
from oslo_utils import importutils
from oslo_utils import strutils
import openstackclient
from openstackclient.common import clientmanager
from openstackclient.common import commandmanager
-from openstackclient.common import exceptions as exc
-from openstackclient.common import logs
-from openstackclient.common import timing
-from openstackclient.common import utils
+from openstackclient.i18n import _
from os_client_config import config as cloud_config
@@ -63,9 +64,8 @@ def prompt_for_password(prompt=None):
pass
# No password because we did't have a tty or nothing was entered
if not pw:
- raise exc.CommandError(
- "No password entered, or found via --os-password or OS_PASSWORD",
- )
+ raise exc.CommandError(_("No password entered, or found via"
+ " --os-password or OS_PASSWORD"),)
return pw
@@ -185,7 +185,7 @@ class OpenStackShell(app.App):
metavar='<cloud-config-name>',
dest='cloud',
default=utils.env('OS_CLOUD'),
- help='Cloud name in clouds.yaml (Env: OS_CLOUD)',
+ help=_('Cloud name in clouds.yaml (Env: OS_CLOUD)'),
)
# Global arguments
parser.add_argument(
@@ -193,37 +193,41 @@ class OpenStackShell(app.App):
metavar='<auth-region-name>',
dest='region_name',
default=utils.env('OS_REGION_NAME'),
- help='Authentication region name (Env: OS_REGION_NAME)')
+ help=_('Authentication region name (Env: OS_REGION_NAME)'),
+ )
parser.add_argument(
'--os-cacert',
metavar='<ca-bundle-file>',
dest='cacert',
default=utils.env('OS_CACERT'),
- help='CA certificate bundle file (Env: OS_CACERT)')
+ help=_('CA certificate bundle file (Env: OS_CACERT)'),
+ )
parser.add_argument(
'--os-cert',
metavar='<certificate-file>',
dest='cert',
default=utils.env('OS_CERT'),
- help='Client certificate bundle file (Env: OS_CERT)')
+ help=_('Client certificate bundle file (Env: OS_CERT)'),
+ )
parser.add_argument(
'--os-key',
metavar='<key-file>',
dest='key',
default=utils.env('OS_KEY'),
- help='Client certificate key file (Env: OS_KEY)')
+ help=_('Client certificate key file (Env: OS_KEY)'),
+ )
verify_group = parser.add_mutually_exclusive_group()
verify_group.add_argument(
'--verify',
action='store_true',
default=None,
- help='Verify server certificate (default)',
+ help=_('Verify server certificate (default)'),
)
verify_group.add_argument(
'--insecure',
action='store_true',
default=None,
- help='Disable server certificate verification',
+ help=_('Disable server certificate verification'),
)
parser.add_argument(
'--os-default-domain',
@@ -232,28 +236,29 @@ class OpenStackShell(app.App):
default=utils.env(
'OS_DEFAULT_DOMAIN',
default=DEFAULT_DOMAIN),
- help='Default domain ID, default=' +
- DEFAULT_DOMAIN +
- ' (Env: OS_DEFAULT_DOMAIN)')
+ help=_('Default domain ID, default=%s. '
+ '(Env: OS_DEFAULT_DOMAIN)') % DEFAULT_DOMAIN,
+ )
parser.add_argument(
'--os-interface',
metavar='<interface>',
dest='interface',
choices=['admin', 'public', 'internal'],
default=utils.env('OS_INTERFACE'),
- help='Select an interface type.'
- ' Valid interface types: [admin, public, internal].'
- ' (Env: OS_INTERFACE)')
+ help=_('Select an interface type.'
+ ' Valid interface types: [admin, public, internal].'
+ ' (Env: OS_INTERFACE)'),
+ )
parser.add_argument(
'--timing',
default=False,
action='store_true',
- help="Print API call timing info",
+ help=_("Print API call timing info"),
)
parser.add_argument(
'--os-beta-command',
action='store_true',
- help="Enable beta commands which are subject to change",
+ help=_("Enable beta commands which are subject to change"),
)
# osprofiler HMAC key argument
@@ -262,7 +267,7 @@ class OpenStackShell(app.App):
'--os-profile',
metavar='hmac-key',
dest='profile',
- help='HMAC key for encrypting profiling context data',
+ help=_('HMAC key for encrypting profiling context data'),
)
# NOTE(dtroyer): This global option should have been named
# --os-profile as --profile interferes with at
@@ -438,12 +443,12 @@ class OpenStackShell(app.App):
cmd.__class__.__name__,
)
if cmd.auth_required:
- if hasattr(cmd, 'required_scope'):
+ self.client_manager.setup_auth()
+ if hasattr(cmd, 'required_scope') and cmd.required_scope:
# let the command decide whether we need a scoped token
- self.client_manager.setup_auth(cmd.required_scope)
+ self.client_manager.validate_scope()
# Trigger the Identity client to initialize
self.client_manager.auth_ref
- return
def clean_up(self, cmd, result, err):
self.log.debug('clean_up %s: %s', cmd.__class__.__name__, err or '')
diff --git a/openstackclient/tests/api/test_api.py b/openstackclient/tests/api/test_api.py
index 81197967..b444d9f1 100644
--- a/openstackclient/tests/api/test_api.py
+++ b/openstackclient/tests/api/test_api.py
@@ -13,8 +13,9 @@
"""Base API Library Tests"""
+from osc_lib import exceptions
+
from openstackclient.api import api
-from openstackclient.common import exceptions
from openstackclient.tests.api import fakes as api_fakes
diff --git a/openstackclient/tests/common/test_clientmanager.py b/openstackclient/tests/common/test_clientmanager.py
index fa6c3fcc..0a9965e0 100644
--- a/openstackclient/tests/common/test_clientmanager.py
+++ b/openstackclient/tests/common/test_clientmanager.py
@@ -15,21 +15,20 @@
import json as jsonutils
import mock
-from requests_mock.contrib import fixture
-from keystoneclient.auth.identity import v2 as auth_v2
-from keystoneclient import service_catalog
+from keystoneauth1.access import service_catalog
+from keystoneauth1.identity import v2 as auth_v2
+from keystoneauth1 import token_endpoint
+from osc_lib import exceptions as exc
+from requests_mock.contrib import fixture
from openstackclient.api import auth
-from openstackclient.api import auth_plugin
from openstackclient.common import clientmanager
-from openstackclient.common import exceptions as exc
from openstackclient.tests import fakes
from openstackclient.tests import utils
API_VERSION = {"identity": "2.0"}
-
AUTH_REF = {'version': 'v2.0'}
AUTH_REF.update(fakes.TEST_RESPONSE_DICT['access'])
SERVICE_CATALOG = service_catalog.ServiceCatalogV2(AUTH_REF)
@@ -126,7 +125,7 @@ class TestClientManager(utils.TestCase):
)
self.assertIsInstance(
client_manager.auth,
- auth_plugin.TokenEndpoint,
+ token_endpoint.Token,
)
self.assertFalse(client_manager._insecure)
self.assertTrue(client_manager._verify)
@@ -205,11 +204,14 @@ class TestClientManager(utils.TestCase):
)
self.assertTrue(client_manager._insecure)
self.assertFalse(client_manager._verify)
-
# These need to stick around until the old-style clients are gone
self.assertEqual(
- AUTH_REF,
- client_manager.auth_ref,
+ AUTH_REF.pop('version'),
+ client_manager.auth_ref.version,
+ )
+ self.assertEqual(
+ fakes.to_unicode_dict(AUTH_REF),
+ client_manager.auth_ref._data['access'],
)
self.assertEqual(
dir(SERVICE_CATALOG),
@@ -296,9 +298,10 @@ class TestClientManager(utils.TestCase):
def _select_auth_plugin(self, auth_params, api_version, auth_plugin_name):
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,
+ api_version={"identity": api_version},
verify=True
)
client_manager.setup_auth()
@@ -353,8 +356,8 @@ class TestClientManager(utils.TestCase):
client_manager.setup_auth,
)
- @mock.patch('openstackclient.api.auth.check_valid_auth_options')
- def test_client_manager_auth_setup_once(self, check_auth_options_func):
+ @mock.patch('openstackclient.api.auth.check_valid_authentication_options')
+ def test_client_manager_auth_setup_once(self, check_authn_options_func):
client_manager = clientmanager.ClientManager(
cli_options=FakeOptions(
auth=dict(
@@ -369,11 +372,11 @@ class TestClientManager(utils.TestCase):
)
self.assertFalse(client_manager._auth_setup_completed)
client_manager.setup_auth()
- self.assertTrue(check_auth_options_func.called)
+ self.assertTrue(check_authn_options_func.called)
self.assertTrue(client_manager._auth_setup_completed)
# now make sure we don't do auth setup the second time around
# by checking whether check_valid_auth_options() gets called again
- check_auth_options_func.reset_mock()
+ check_authn_options_func.reset_mock()
client_manager.auth_ref
- check_auth_options_func.assert_not_called()
+ check_authn_options_func.assert_not_called()
diff --git a/openstackclient/tests/common/test_command.py b/openstackclient/tests/common/test_command.py
index 722a4c06..658bc895 100644
--- a/openstackclient/tests/common/test_command.py
+++ b/openstackclient/tests/common/test_command.py
@@ -14,8 +14,9 @@
import mock
+from osc_lib import exceptions
+
from openstackclient.common import command
-from openstackclient.common import exceptions
from openstackclient.tests import fakes as test_fakes
from openstackclient.tests import utils as test_utils
diff --git a/openstackclient/tests/common/test_configuration.py b/openstackclient/tests/common/test_configuration.py
index e81550ed..915e5bd3 100644
--- a/openstackclient/tests/common/test_configuration.py
+++ b/openstackclient/tests/common/test_configuration.py
@@ -11,6 +11,8 @@
# under the License.
#
+import mock
+
from openstackclient.common import configuration
from openstackclient.tests import fakes
from openstackclient.tests import utils
@@ -33,7 +35,12 @@ class TestConfiguration(utils.TestCommand):
fakes.REGION_NAME,
)
- def test_show(self):
+ opts = [mock.Mock(secret=True, dest="password"),
+ mock.Mock(secret=True, dest="token")]
+
+ @mock.patch("keystoneauth1.loading.base.get_plugin_options",
+ return_value=opts)
+ def test_show(self, m_get_plugin_opts):
arglist = []
verifylist = [('mask', True)]
cmd = configuration.ShowConfiguration(self.app, None)
@@ -44,7 +51,9 @@ class TestConfiguration(utils.TestCommand):
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data)
- def test_show_unmask(self):
+ @mock.patch("keystoneauth1.loading.base.get_plugin_options",
+ return_value=opts)
+ def test_show_unmask(self, m_get_plugin_opts):
arglist = ['--unmask']
verifylist = [('mask', False)]
cmd = configuration.ShowConfiguration(self.app, None)
@@ -62,7 +71,9 @@ class TestConfiguration(utils.TestCommand):
)
self.assertEqual(datalist, data)
- def test_show_mask(self):
+ @mock.patch("keystoneauth1.loading.base.get_plugin_options",
+ return_value=opts)
+ def test_show_mask(self, m_get_plugin_opts):
arglist = ['--mask']
verifylist = [('mask', True)]
cmd = configuration.ShowConfiguration(self.app, None)
diff --git a/openstackclient/tests/common/test_extension.py b/openstackclient/tests/common/test_extension.py
index 0736a3e5..17a3b492 100644
--- a/openstackclient/tests/common/test_extension.py
+++ b/openstackclient/tests/common/test_extension.py
@@ -11,7 +11,6 @@
# under the License.
#
-import copy
import mock
from openstackclient.common import extension
@@ -29,26 +28,38 @@ class TestExtension(utils.TestCommand):
def setUp(self):
super(TestExtension, self).setUp()
- self.app.client_manager.identity = identity_fakes.FakeIdentityv2Client(
+ identity_client = identity_fakes.FakeIdentityv2Client(
endpoint=fakes.AUTH_URL,
token=fakes.AUTH_TOKEN,
)
- self.identity_extensions_mock = (
- self.app.client_manager.identity.extensions)
+ self.app.client_manager.identity = identity_client
+ self.identity_extensions_mock = identity_client.extensions
self.identity_extensions_mock.reset_mock()
- self.app.client_manager.compute = compute_fakes.FakeComputev2Client(
+ compute_client = compute_fakes.FakeComputev2Client(
endpoint=fakes.AUTH_URL,
token=fakes.AUTH_TOKEN,
)
+ self.app.client_manager.compute = compute_client
+ compute_client.list_extensions = mock.Mock()
+ self.compute_extensions_mock = compute_client.list_extensions
+ self.compute_extensions_mock.reset_mock()
- self.app.client_manager.volume = volume_fakes.FakeVolumeClient(
+ volume_client = volume_fakes.FakeVolumeClient(
endpoint=fakes.AUTH_URL,
token=fakes.AUTH_TOKEN,
)
+ self.app.client_manager.volume = volume_client
+ volume_client.list_extensions = mock.Mock()
+ self.volume_extensions_mock = volume_client.list_extensions
+ self.volume_extensions_mock.reset_mock()
- network_client = network_fakes.FakeNetworkV2Client()
+ network_client = network_fakes.FakeNetworkV2Client(
+ endpoint=fakes.AUTH_URL,
+ token=fakes.AUTH_TOKEN,
+ )
self.app.client_manager.network = network_client
+ network_client.extensions = mock.Mock()
self.network_extensions_mock = network_client.extensions
self.network_extensions_mock.reset_mock()
@@ -59,38 +70,21 @@ class TestExtensionList(TestExtension):
long_columns = ('Name', 'Namespace', 'Description', 'Alias', 'Updated',
'Links')
+ volume_extension = volume_fakes.FakeExtension.create_one_extension()
+ identity_extension = identity_fakes.FakeExtension.create_one_extension()
+ compute_extension = compute_fakes.FakeExtension.create_one_extension()
+ network_extension = network_fakes.FakeExtension.create_one_extension()
+
def setUp(self):
super(TestExtensionList, self).setUp()
self.identity_extensions_mock.list.return_value = [
- fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.EXTENSION),
- loaded=True,
- ),
- ]
-
- self.app.client_manager.compute.list_extensions = mock.Mock()
- self.compute_extensions_mock = (
- self.app.client_manager.compute.list_extensions)
+ self.identity_extension]
self.compute_extensions_mock.show_all.return_value = [
- fakes.FakeResource(
- None,
- copy.deepcopy(compute_fakes.EXTENSION),
- loaded=True,
- ),
- ]
-
- self.app.client_manager.volume.list_extensions = mock.Mock()
- self.volume_extensions_mock = (
- self.app.client_manager.volume.list_extensions)
+ self.compute_extension]
self.volume_extensions_mock.show_all.return_value = [
- fakes.FakeResource(
- None,
- copy.deepcopy(volume_fakes.EXTENSION),
- loaded=True,
- ),
- ]
+ self.volume_extension]
+ self.network_extensions_mock.return_value = [self.network_extension]
# Get the command object to test
self.cmd = extension.ListExtension(self.app, None)
@@ -115,24 +109,24 @@ class TestExtensionList(TestExtension):
verifylist = []
datalist = (
(
- identity_fakes.extension_name,
- identity_fakes.extension_alias,
- identity_fakes.extension_description,
+ self.identity_extension.name,
+ self.identity_extension.alias,
+ self.identity_extension.description,
),
(
- compute_fakes.extension_name,
- compute_fakes.extension_alias,
- compute_fakes.extension_description,
+ self.compute_extension.name,
+ self.compute_extension.alias,
+ self.compute_extension.description,
),
(
- volume_fakes.extension_name,
- volume_fakes.extension_alias,
- volume_fakes.extension_description,
+ self.volume_extension.name,
+ self.volume_extension.alias,
+ self.volume_extension.description,
),
(
- network_fakes.extension_name,
- network_fakes.extension_alias,
- network_fakes.extension_description,
+ self.network_extension.name,
+ self.network_extension.alias,
+ self.network_extension.description,
),
)
self._test_extension_list_helper(arglist, verifylist, datalist)
@@ -150,36 +144,36 @@ class TestExtensionList(TestExtension):
]
datalist = (
(
- identity_fakes.extension_name,
- identity_fakes.extension_namespace,
- identity_fakes.extension_description,
- identity_fakes.extension_alias,
- identity_fakes.extension_updated,
- identity_fakes.extension_links,
+ self.identity_extension.name,
+ self.identity_extension.namespace,
+ self.identity_extension.description,
+ self.identity_extension.alias,
+ self.identity_extension.updated,
+ self.identity_extension.links,
),
(
- compute_fakes.extension_name,
- compute_fakes.extension_namespace,
- compute_fakes.extension_description,
- compute_fakes.extension_alias,
- compute_fakes.extension_updated,
- compute_fakes.extension_links,
+ self.compute_extension.name,
+ self.compute_extension.namespace,
+ self.compute_extension.description,
+ self.compute_extension.alias,
+ self.compute_extension.updated,
+ self.compute_extension.links,
),
(
- volume_fakes.extension_name,
- volume_fakes.extension_namespace,
- volume_fakes.extension_description,
- volume_fakes.extension_alias,
- volume_fakes.extension_updated,
- volume_fakes.extension_links,
+ self.volume_extension.name,
+ self.volume_extension.namespace,
+ self.volume_extension.description,
+ self.volume_extension.alias,
+ self.volume_extension.updated,
+ self.volume_extension.links,
),
(
- network_fakes.extension_name,
- network_fakes.extension_namespace,
- network_fakes.extension_description,
- network_fakes.extension_alias,
- network_fakes.extension_updated,
- network_fakes.extension_links,
+ self.network_extension.name,
+ self.network_extension.namespace,
+ self.network_extension.description,
+ self.network_extension.alias,
+ self.network_extension.updated,
+ self.network_extension.links,
),
)
self._test_extension_list_helper(arglist, verifylist, datalist, True)
@@ -196,9 +190,9 @@ class TestExtensionList(TestExtension):
('identity', True),
]
datalist = ((
- identity_fakes.extension_name,
- identity_fakes.extension_alias,
- identity_fakes.extension_description,
+ self.identity_extension.name,
+ self.identity_extension.alias,
+ self.identity_extension.description,
), )
self._test_extension_list_helper(arglist, verifylist, datalist)
self.identity_extensions_mock.list.assert_called_with()
@@ -212,9 +206,9 @@ class TestExtensionList(TestExtension):
]
datalist = (
(
- network_fakes.extension_name,
- network_fakes.extension_alias,
- network_fakes.extension_description,
+ self.network_extension.name,
+ self.network_extension.alias,
+ self.network_extension.description,
),
)
self._test_extension_list_helper(arglist, verifylist, datalist)
@@ -228,9 +222,9 @@ class TestExtensionList(TestExtension):
('compute', True),
]
datalist = ((
- compute_fakes.extension_name,
- compute_fakes.extension_alias,
- compute_fakes.extension_description,
+ self.compute_extension.name,
+ self.compute_extension.alias,
+ self.compute_extension.description,
), )
self._test_extension_list_helper(arglist, verifylist, datalist)
self.compute_extensions_mock.show_all.assert_called_with()
@@ -243,9 +237,9 @@ class TestExtensionList(TestExtension):
('volume', True),
]
datalist = ((
- volume_fakes.extension_name,
- volume_fakes.extension_alias,
- volume_fakes.extension_description,
+ self.volume_extension.name,
+ self.volume_extension.alias,
+ self.volume_extension.description,
), )
self._test_extension_list_helper(arglist, verifylist, datalist)
self.volume_extensions_mock.show_all.assert_called_with()
diff --git a/openstackclient/tests/common/test_logs.py b/openstackclient/tests/common/test_logs.py
index 0386cdfd..5091510c 100644
--- a/openstackclient/tests/common/test_logs.py
+++ b/openstackclient/tests/common/test_logs.py
@@ -11,6 +11,9 @@
# under the License.
#
+# NOTE(dtroyer): This file is deprecated in Jun 2016, remove after 4.x release
+# or Jun 2017.
+
import logging
import mock
@@ -121,7 +124,7 @@ class TestLogConfigurator(utils.TestCase):
@mock.patch('logging.StreamHandler')
@mock.patch('logging.getLogger')
- @mock.patch('openstackclient.common.logs.set_warning_filter')
+ @mock.patch('osc_lib.logs.set_warning_filter')
def test_init(self, warning_filter, getLogger, handle):
getLogger.side_effect = self.loggers
console_logger = mock.Mock()
@@ -142,7 +145,7 @@ class TestLogConfigurator(utils.TestCase):
self.assertFalse(configurator.dump_trace)
@mock.patch('logging.getLogger')
- @mock.patch('openstackclient.common.logs.set_warning_filter')
+ @mock.patch('osc_lib.logs.set_warning_filter')
def test_init_no_debug(self, warning_filter, getLogger):
getLogger.side_effect = self.loggers
self.options.debug = True
@@ -155,8 +158,8 @@ class TestLogConfigurator(utils.TestCase):
@mock.patch('logging.FileHandler')
@mock.patch('logging.getLogger')
- @mock.patch('openstackclient.common.logs.set_warning_filter')
- @mock.patch('openstackclient.common.logs._FileFormatter')
+ @mock.patch('osc_lib.logs.set_warning_filter')
+ @mock.patch('osc_lib.logs._FileFormatter')
def test_init_log_file(self, formatter, warning_filter, getLogger, handle):
getLogger.side_effect = self.loggers
self.options.log_file = '/tmp/log_file'
@@ -176,8 +179,8 @@ class TestLogConfigurator(utils.TestCase):
@mock.patch('logging.FileHandler')
@mock.patch('logging.getLogger')
- @mock.patch('openstackclient.common.logs.set_warning_filter')
- @mock.patch('openstackclient.common.logs._FileFormatter')
+ @mock.patch('osc_lib.logs.set_warning_filter')
+ @mock.patch('osc_lib.logs._FileFormatter')
def test_configure(self, formatter, warning_filter, getLogger, handle):
getLogger.side_effect = self.loggers
configurator = logs.LogConfigurator(self.options)
diff --git a/openstackclient/tests/common/test_parseractions.py b/openstackclient/tests/common/test_parseractions.py
index 5c5ca3d3..60d4a8cf 100644
--- a/openstackclient/tests/common/test_parseractions.py
+++ b/openstackclient/tests/common/test_parseractions.py
@@ -13,6 +13,9 @@
# under the License.
#
+# NOTE(dtroyer): This file is deprecated in Jun 2016, remove after 4.x release
+# or Jun 2017.
+
import argparse
from openstackclient.common import parseractions
@@ -49,16 +52,13 @@ class TestKeyValueAction(utils.TestCase):
self.assertDictEqual(expect, actual)
def test_error_values(self):
- results = self.parser.parse_args([
- '--property', 'red',
- '--property', 'green=100%',
- '--property', 'blue',
- ])
-
- actual = getattr(results, 'property', {})
- # There should be no red or blue
- expect = {'green': '100%', 'format': '#rgb'}
- self.assertDictEqual(expect, actual)
+ self.assertRaises(
+ argparse.ArgumentTypeError,
+ self.parser.parse_args,
+ [
+ '--property', 'red',
+ ]
+ )
class TestMultiKeyValueAction(utils.TestCase):
diff --git a/openstackclient/tests/common/test_timing.py b/openstackclient/tests/common/test_timing.py
deleted file mode 100644
index e33bb7ae..00000000
--- a/openstackclient/tests/common/test_timing.py
+++ /dev/null
@@ -1,94 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-
-"""Test Timing pseudo-command"""
-
-import datetime
-
-from openstackclient.common import timing
-from openstackclient.tests import fakes
-from openstackclient.tests import utils
-
-
-timing_url = 'GET http://localhost:5000'
-timing_elapsed = 0.872809
-
-
-class FakeGenericClient(object):
-
- def __init__(self, **kwargs):
- self.auth_token = kwargs['token']
- self.management_url = kwargs['endpoint']
-
-
-class TestTiming(utils.TestCommand):
-
- columns = (
- 'URL',
- 'Seconds',
- )
-
- def setUp(self):
- super(TestTiming, self).setUp()
-
- self.app.timing_data = []
-
- self.app.client_manager.compute = FakeGenericClient(
- endpoint=fakes.AUTH_URL,
- token=fakes.AUTH_TOKEN,
- )
-
- self.app.client_manager.volume = FakeGenericClient(
- endpoint=fakes.AUTH_URL,
- token=fakes.AUTH_TOKEN,
- )
-
- # Get the command object to test
- self.cmd = timing.Timing(self.app, None)
-
- def test_timing_list_no_data(self):
- arglist = []
- verifylist = []
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- # In base command class Lister in cliff, abstract method take_action()
- # returns a tuple containing the column names and an iterable
- # containing the data to be listed.
- columns, data = self.cmd.take_action(parsed_args)
-
- self.assertEqual(self.columns, columns)
- datalist = [
- ('Total', 0.0,)
- ]
- self.assertEqual(datalist, data)
-
- def test_timing_list(self):
- self.app.timing_data = [(
- timing_url,
- datetime.timedelta(microseconds=timing_elapsed * 1000000),
- )]
-
- arglist = []
- verifylist = []
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- # In base command class Lister in cliff, abstract method take_action()
- # returns a tuple containing the column names and an iterable
- # containing the data to be listed.
- columns, data = self.cmd.take_action(parsed_args)
- self.assertEqual(self.columns, columns)
- datalist = [
- (timing_url, timing_elapsed),
- ('Total', timing_elapsed),
- ]
- self.assertEqual(datalist, data)
diff --git a/openstackclient/tests/common/test_utils.py b/openstackclient/tests/common/test_utils.py
deleted file mode 100644
index 2248d043..00000000
--- a/openstackclient/tests/common/test_utils.py
+++ /dev/null
@@ -1,400 +0,0 @@
-# Copyright 2012-2013 OpenStack, LLC.
-#
-# 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 time
-import uuid
-
-import mock
-
-from openstackclient.common import exceptions
-from openstackclient.common import utils
-from openstackclient.tests import fakes
-from openstackclient.tests import utils as test_utils
-
-PASSWORD = "Pa$$w0rd"
-WASSPORD = "Wa$$p0rd"
-DROWSSAP = "dr0w$$aP"
-
-
-class FakeOddballResource(fakes.FakeResource):
-
- def get(self, attr):
- """get() is needed for utils.find_resource()"""
- if attr == 'id':
- return self.id
- elif attr == 'name':
- return self.name
- else:
- return None
-
-
-class TestUtils(test_utils.TestCase):
-
- def test_get_password_good(self):
- with mock.patch("getpass.getpass", return_value=PASSWORD):
- mock_stdin = mock.Mock()
- mock_stdin.isatty = mock.Mock()
- mock_stdin.isatty.return_value = True
- self.assertEqual(PASSWORD, utils.get_password(mock_stdin))
-
- def test_get_password_bad_once(self):
- answers = [PASSWORD, WASSPORD, DROWSSAP, DROWSSAP]
- with mock.patch("getpass.getpass", side_effect=answers):
- mock_stdin = mock.Mock()
- mock_stdin.isatty = mock.Mock()
- mock_stdin.isatty.return_value = True
- self.assertEqual(DROWSSAP, utils.get_password(mock_stdin))
-
- def test_get_password_no_tty(self):
- mock_stdin = mock.Mock()
- mock_stdin.isatty = mock.Mock()
- mock_stdin.isatty.return_value = False
- self.assertRaises(exceptions.CommandError,
- utils.get_password,
- mock_stdin)
-
- def test_get_password_cntrl_d(self):
- with mock.patch("getpass.getpass", side_effect=EOFError()):
- mock_stdin = mock.Mock()
- mock_stdin.isatty = mock.Mock()
- mock_stdin.isatty.return_value = True
- self.assertRaises(exceptions.CommandError,
- utils.get_password,
- mock_stdin)
-
- def get_test_items(self):
- item1 = {'a': 1, 'b': 2}
- item2 = {'a': 1, 'b': 3}
- item3 = {'a': 2, 'b': 2}
- item4 = {'a': 2, 'b': 1}
- return [item1, item2, item3, item4]
-
- def test_sort_items_with_one_key(self):
- items = self.get_test_items()
- sort_str = 'b'
- expect_items = [items[3], items[0], items[2], items[1]]
- self.assertEqual(expect_items, utils.sort_items(items, sort_str))
-
- def test_sort_items_with_multiple_keys(self):
- items = self.get_test_items()
- sort_str = 'a,b'
- expect_items = [items[0], items[1], items[3], items[2]]
- self.assertEqual(expect_items, utils.sort_items(items, sort_str))
-
- def test_sort_items_all_with_direction(self):
- items = self.get_test_items()
- sort_str = 'a:desc,b:desc'
- expect_items = [items[2], items[3], items[1], items[0]]
- self.assertEqual(expect_items, utils.sort_items(items, sort_str))
-
- def test_sort_items_some_with_direction(self):
- items = self.get_test_items()
- sort_str = 'a,b:desc'
- expect_items = [items[1], items[0], items[2], items[3]]
- self.assertEqual(expect_items, utils.sort_items(items, sort_str))
-
- def test_sort_items_with_object(self):
- item1 = mock.Mock(a=1, b=2)
- item2 = mock.Mock(a=1, b=3)
- item3 = mock.Mock(a=2, b=2)
- item4 = mock.Mock(a=2, b=1)
- items = [item1, item2, item3, item4]
- sort_str = 'b,a'
- expect_items = [item4, item1, item3, item2]
- self.assertEqual(expect_items, utils.sort_items(items, sort_str))
-
- def test_sort_items_with_empty_key(self):
- items = self.get_test_items()
- sort_srt = ''
- self.assertEqual(items, utils.sort_items(items, sort_srt))
- sort_srt = None
- self.assertEqual(items, utils.sort_items(items, sort_srt))
-
- def test_sort_items_with_invalid_key(self):
- items = self.get_test_items()
- sort_str = 'c'
- self.assertRaises(exceptions.CommandError,
- utils.sort_items,
- items, sort_str)
-
- def test_sort_items_with_invalid_direction(self):
- items = self.get_test_items()
- sort_str = 'a:bad_dir'
- self.assertRaises(exceptions.CommandError,
- utils.sort_items,
- items, sort_str)
-
- @mock.patch.object(time, 'sleep')
- def test_wait_for_status_ok(self, mock_sleep):
- # Tests the normal flow that the resource is status=active
- resource = mock.MagicMock(status='ACTIVE')
- status_f = mock.Mock(return_value=resource)
- res_id = str(uuid.uuid4())
- self.assertTrue(utils.wait_for_status(status_f, res_id,))
- mock_sleep.assert_not_called()
-
- @mock.patch.object(time, 'sleep')
- def test_wait_for_status_ok__with_overrides(self, mock_sleep):
- # Tests the normal flow that the resource is status=complete
- resource = mock.MagicMock(my_status='COMPLETE')
- status_f = mock.Mock(return_value=resource)
- res_id = str(uuid.uuid4())
- self.assertTrue(utils.wait_for_status(status_f, res_id,
- status_field='my_status',
- success_status=['complete']))
- mock_sleep.assert_not_called()
-
- @mock.patch.object(time, 'sleep')
- def test_wait_for_status_error(self, mock_sleep):
- # Tests that we fail if the resource is status=error
- resource = mock.MagicMock(status='ERROR')
- status_f = mock.Mock(return_value=resource)
- res_id = str(uuid.uuid4())
- self.assertFalse(utils.wait_for_status(status_f, res_id))
- mock_sleep.assert_not_called()
-
- @mock.patch.object(time, 'sleep')
- def test_wait_for_status_error_with_overrides(self, mock_sleep):
- # Tests that we fail if the resource is my_status=failed
- resource = mock.MagicMock(my_status='FAILED')
- status_f = mock.Mock(return_value=resource)
- res_id = str(uuid.uuid4())
- self.assertFalse(utils.wait_for_status(status_f, res_id,
- status_field='my_status',
- error_status=['failed']))
- mock_sleep.assert_not_called()
-
- @mock.patch.object(time, 'sleep')
- def test_wait_for_delete_ok(self, mock_sleep):
- # Tests the normal flow that the resource is deleted with a 404 coming
- # back on the 2nd iteration of the wait loop.
- resource = mock.MagicMock(status='ACTIVE', progress=None)
- mock_get = mock.Mock(side_effect=[resource,
- exceptions.NotFound(404)])
- manager = mock.MagicMock(get=mock_get)
- res_id = str(uuid.uuid4())
- callback = mock.Mock()
- self.assertTrue(utils.wait_for_delete(manager, res_id,
- callback=callback))
- mock_sleep.assert_called_once_with(5)
- callback.assert_called_once_with(0)
-
- @mock.patch.object(time, 'sleep')
- def test_wait_for_delete_timeout(self, mock_sleep):
- # Tests that we fail if the resource is not deleted before the timeout.
- resource = mock.MagicMock(status='ACTIVE')
- mock_get = mock.Mock(return_value=resource)
- manager = mock.MagicMock(get=mock_get)
- res_id = str(uuid.uuid4())
- self.assertFalse(utils.wait_for_delete(manager, res_id, sleep_time=1,
- timeout=1))
- mock_sleep.assert_called_once_with(1)
-
- @mock.patch.object(time, 'sleep')
- def test_wait_for_delete_error(self, mock_sleep):
- # Tests that we fail if the resource goes to error state while waiting.
- resource = mock.MagicMock(status='ERROR')
- mock_get = mock.Mock(return_value=resource)
- manager = mock.MagicMock(get=mock_get)
- res_id = str(uuid.uuid4())
- self.assertFalse(utils.wait_for_delete(manager, res_id))
- mock_sleep.assert_not_called()
-
- @mock.patch.object(time, 'sleep')
- def test_wait_for_delete_error_with_overrides(self, mock_sleep):
- # Tests that we fail if the resource is my_status=failed
- resource = mock.MagicMock(my_status='FAILED')
- mock_get = mock.Mock(return_value=resource)
- manager = mock.MagicMock(get=mock_get)
- res_id = str(uuid.uuid4())
- self.assertFalse(utils.wait_for_delete(manager, res_id,
- status_field='my_status',
- error_status=['failed']))
- mock_sleep.assert_not_called()
-
- @mock.patch.object(time, 'sleep')
- def test_wait_for_delete_error_with_overrides_exception(self, mock_sleep):
- # Tests that we succeed if the resource is specific exception
- mock_get = mock.Mock(side_effect=Exception)
- manager = mock.MagicMock(get=mock_get)
- res_id = str(uuid.uuid4())
- self.assertTrue(utils.wait_for_delete(manager, res_id,
- exception_name=['Exception']))
- mock_sleep.assert_not_called()
-
- def test_build_kwargs_dict_value_set(self):
- self.assertEqual({'arg_bla': 'bla'},
- utils.build_kwargs_dict('arg_bla', 'bla'))
-
- def test_build_kwargs_dict_value_None(self):
- self.assertEqual({}, utils.build_kwargs_dict('arg_bla', None))
-
- def test_build_kwargs_dict_value_empty_str(self):
- self.assertEqual({}, utils.build_kwargs_dict('arg_bla', ''))
-
-
-class NoUniqueMatch(Exception):
- pass
-
-
-class TestFindResource(test_utils.TestCase):
-
- def setUp(self):
- super(TestFindResource, self).setUp()
- self.name = 'legos'
- self.expected = mock.Mock()
- self.manager = mock.Mock()
- self.manager.resource_class = mock.Mock()
- self.manager.resource_class.__name__ = 'lego'
-
- def test_find_resource_get_int(self):
- self.manager.get = mock.Mock(return_value=self.expected)
- result = utils.find_resource(self.manager, 1)
- self.assertEqual(self.expected, result)
- self.manager.get.assert_called_with(1)
-
- def test_find_resource_get_int_string(self):
- self.manager.get = mock.Mock(return_value=self.expected)
- result = utils.find_resource(self.manager, "2")
- self.assertEqual(self.expected, result)
- self.manager.get.assert_called_with(2)
-
- def test_find_resource_get_uuid(self):
- uuid = '9a0dc2a0-ad0d-11e3-a5e2-0800200c9a66'
- self.manager.get = mock.Mock(return_value=self.expected)
- result = utils.find_resource(self.manager, uuid)
- self.assertEqual(self.expected, result)
- self.manager.get.assert_called_with(uuid)
-
- def test_find_resource_get_whatever(self):
- self.manager.get = mock.Mock(return_value=self.expected)
- result = utils.find_resource(self.manager, 'whatever')
- self.assertEqual(self.expected, result)
- self.manager.get.assert_called_with('whatever')
-
- def test_find_resource_find(self):
- self.manager.get = mock.Mock(side_effect=Exception('Boom!'))
- self.manager.find = mock.Mock(return_value=self.expected)
- result = utils.find_resource(self.manager, self.name)
- self.assertEqual(self.expected, result)
- self.manager.get.assert_called_with(self.name)
- self.manager.find.assert_called_with(name=self.name)
-
- def test_find_resource_find_not_found(self):
- self.manager.get = mock.Mock(side_effect=Exception('Boom!'))
- self.manager.find = mock.Mock(
- side_effect=exceptions.NotFound(404, "2")
- )
- result = self.assertRaises(exceptions.CommandError,
- utils.find_resource,
- self.manager,
- self.name)
- self.assertEqual("No lego with a name or ID of 'legos' exists.",
- str(result))
- self.manager.get.assert_called_with(self.name)
- self.manager.find.assert_called_with(name=self.name)
-
- def test_find_resource_list_forbidden(self):
- self.manager.get = mock.Mock(side_effect=Exception('Boom!'))
- self.manager.find = mock.Mock(side_effect=Exception('Boom!'))
- self.manager.list = mock.Mock(
- side_effect=exceptions.Forbidden(403)
- )
- self.assertRaises(exceptions.Forbidden,
- utils.find_resource,
- self.manager,
- self.name)
- self.manager.list.assert_called_with()
-
- def test_find_resource_find_no_unique(self):
- self.manager.get = mock.Mock(side_effect=Exception('Boom!'))
- self.manager.find = mock.Mock(side_effect=NoUniqueMatch())
- result = self.assertRaises(exceptions.CommandError,
- utils.find_resource,
- self.manager,
- self.name)
- self.assertEqual("More than one lego exists with the name 'legos'.",
- str(result))
- self.manager.get.assert_called_with(self.name)
- self.manager.find.assert_called_with(name=self.name)
-
- def test_find_resource_silly_resource(self):
- # We need a resource with no resource_class for this test, start fresh
- self.manager = mock.Mock()
- self.manager.get = mock.Mock(side_effect=Exception('Boom!'))
- self.manager.find = mock.Mock(
- side_effect=AttributeError(
- "'Controller' object has no attribute 'find'",
- )
- )
- silly_resource = FakeOddballResource(
- None,
- {'id': '12345', 'name': self.name},
- loaded=True,
- )
- self.manager.list = mock.Mock(
- return_value=[silly_resource, ],
- )
- result = utils.find_resource(self.manager, self.name)
- self.assertEqual(silly_resource, result)
- self.manager.get.assert_called_with(self.name)
- self.manager.find.assert_called_with(name=self.name)
-
- def test_find_resource_silly_resource_not_found(self):
- # We need a resource with no resource_class for this test, start fresh
- self.manager = mock.Mock()
- self.manager.get = mock.Mock(side_effect=Exception('Boom!'))
- self.manager.find = mock.Mock(
- side_effect=AttributeError(
- "'Controller' object has no attribute 'find'",
- )
- )
- self.manager.list = mock.Mock(return_value=[])
- result = self.assertRaises(exceptions.CommandError,
- utils.find_resource,
- self.manager,
- self.name)
- self.assertEqual("Could not find resource legos",
- str(result))
- self.manager.get.assert_called_with(self.name)
- self.manager.find.assert_called_with(name=self.name)
-
- def test_format_dict(self):
- expected = "a='b', c='d', e='f'"
- self.assertEqual(expected,
- utils.format_dict({'a': 'b', 'c': 'd', 'e': 'f'}))
- self.assertEqual(expected,
- utils.format_dict({'e': 'f', 'c': 'd', 'a': 'b'}))
-
- def test_format_list(self):
- expected = 'a, b, c'
- self.assertEqual(expected, utils.format_list(['a', 'b', 'c']))
- self.assertEqual(expected, utils.format_list(['c', 'b', 'a']))
-
- def test_format_list_of_dicts(self):
- expected = "a='b', c='d'\ne='f'"
- sorted_data = [{'a': 'b', 'c': 'd'}, {'e': 'f'}]
- unsorted_data = [{'c': 'd', 'a': 'b'}, {'e': 'f'}]
- self.assertEqual(expected, utils.format_list_of_dicts(sorted_data))
- self.assertEqual(expected, utils.format_list_of_dicts(unsorted_data))
- self.assertEqual('', utils.format_list_of_dicts([]))
- self.assertEqual('', utils.format_list_of_dicts([{}]))
-
- def test_format_list_separator(self):
- expected = 'a\nb\nc'
- actual_pre_sorted = utils.format_list(['a', 'b', 'c'], separator='\n')
- actual_unsorted = utils.format_list(['c', 'b', 'a'], separator='\n')
- self.assertEqual(expected, actual_pre_sorted)
- self.assertEqual(expected, actual_unsorted)
diff --git a/openstackclient/tests/compute/v2/fakes.py b/openstackclient/tests/compute/v2/fakes.py
index c9e2025d..882d8480 100644
--- a/openstackclient/tests/compute/v2/fakes.py
+++ b/openstackclient/tests/compute/v2/fakes.py
@@ -24,26 +24,6 @@ from openstackclient.tests.network.v2 import fakes as network_fakes
from openstackclient.tests import utils
from openstackclient.tests.volume.v2 import fakes as volume_fakes
-
-extension_name = 'Multinic'
-extension_namespace = 'http://docs.openstack.org/compute/ext/'\
- 'multinic/api/v1.1'
-extension_description = 'Multiple network support'
-extension_updated = '2014-01-07T12:00:0-00:00'
-extension_alias = 'NMN'
-extension_links = '[{"href":'\
- '"https://github.com/openstack/compute-api", "type":'\
- ' "text/html", "rel": "describedby"}]'
-
-EXTENSION = {
- 'name': extension_name,
- 'namespace': extension_namespace,
- 'description': extension_description,
- 'updated': extension_updated,
- 'alias': extension_alias,
- 'links': extension_links,
-}
-
floating_ip_num = 100
fix_ip_num = 100
injected_file_num = 100
@@ -107,6 +87,42 @@ class FakeAggregate(object):
loaded=True)
return aggregate
+ @staticmethod
+ def create_aggregates(attrs=None, count=2):
+ """Create multiple fake aggregates.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :param int count:
+ The number of aggregates to fake
+ :return:
+ A list of FakeResource objects faking the aggregates
+ """
+ aggregates = []
+ for i in range(0, count):
+ aggregates.append(FakeAggregate.create_one_aggregate(attrs))
+
+ return aggregates
+
+ @staticmethod
+ def get_aggregates(aggregates=None, count=2):
+ """Get an iterable MagicMock object with a list of faked aggregates.
+
+ If aggregates list is provided, then initialize the Mock object
+ with the list. Otherwise create one.
+
+ :param List aggregates:
+ A list of FakeResource objects faking aggregates
+ :param int count:
+ The number of aggregates to fake
+ :return:
+ An iterable Mock object with side_effect set to a list of faked
+ aggregates
+ """
+ if aggregates is None:
+ aggregates = FakeAggregate.create_aggregates(count)
+ return mock.MagicMock(side_effect=aggregates)
+
class FakeComputev2Client(object):
@@ -259,6 +275,42 @@ class FakeAgent(object):
return agents
+class FakeExtension(object):
+ """Fake one or more extension."""
+
+ @staticmethod
+ def create_one_extension(attrs=None):
+ """Create a fake extension.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ A FakeResource object with name, namespace, etc.
+ """
+ attrs = attrs or {}
+
+ # Set default attributes.
+ extension_info = {
+ 'name': 'name-' + uuid.uuid4().hex,
+ 'namespace': (
+ 'http://docs.openstack.org/compute/ext/multinic/api/v1.1'),
+ 'description': 'description-' + uuid.uuid4().hex,
+ 'updated': '2014-01-07T12:00:0-00:00',
+ 'alias': 'NMN',
+ 'links': ('[{"href":'
+ '"https://github.com/openstack/compute-api", "type":'
+ ' "text/html", "rel": "describedby"}]')
+ }
+
+ # Overwrite default attributes.
+ extension_info.update(attrs)
+
+ extension = fakes.FakeResource(
+ info=copy.deepcopy(extension_info),
+ loaded=True)
+ return extension
+
+
class FakeHypervisor(object):
"""Fake one or more hypervisor."""
@@ -436,6 +488,25 @@ class FakeSecurityGroup(object):
return security_groups
+ @staticmethod
+ def get_security_groups(security_groups=None, count=2):
+ """Get an iterable MagicMock object with a list of faked security groups.
+
+ If security groups list is provided, then initialize the Mock object
+ with the list. Otherwise create one.
+
+ :param List security groups:
+ A list of FakeResource objects faking security groups
+ :param int count:
+ The number of security groups to fake
+ :return:
+ An iterable Mock object with side_effect set to a list of faked
+ security groups
+ """
+ if security_groups is None:
+ security_groups = FakeSecurityGroup.create_security_groups(count)
+ return mock.MagicMock(side_effect=security_groups)
+
class FakeSecurityGroupRule(object):
"""Fake one or more security group rules."""
@@ -577,15 +648,19 @@ class FakeService(object):
:param Dictionary attrs:
A dictionary with all attributes
:return:
- A FakeResource object, with id, name, ram, vcpus, properties
+ A FakeResource object, with id, host, binary
"""
attrs = attrs or {}
# Set default attributes.
service_info = {
+ 'id': 'id-' + uuid.uuid4().hex,
'host': 'host-' + uuid.uuid4().hex,
'binary': 'binary-' + uuid.uuid4().hex,
'status': 'enabled',
+ 'zone': 'zone-' + uuid.uuid4().hex,
+ 'state': 'state-' + uuid.uuid4().hex,
+ 'updated_at': 'time-' + uuid.uuid4().hex,
'disabled_reason': 'earthquake',
}
@@ -697,7 +772,7 @@ class FakeFlavor(object):
flavors
"""
if flavors is None:
- flavors = FakeServer.create_flavors(count)
+ flavors = FakeFlavor.create_flavors(count)
return mock.MagicMock(side_effect=flavors)
@@ -751,6 +826,25 @@ class FakeKeypair(object):
return keypairs
+ @staticmethod
+ def get_keypairs(keypairs=None, count=2):
+ """Get an iterable MagicMock object with a list of faked keypairs.
+
+ If keypairs list is provided, then initialize the Mock object with the
+ list. Otherwise create one.
+
+ :param List keypairs:
+ A list of FakeResource objects faking keypairs
+ :param int count:
+ The number of keypairs to fake
+ :return:
+ An iterable Mock object with side_effect set to a list of faked
+ keypairs
+ """
+ if keypairs is None:
+ keypairs = FakeKeypair.create_keypairs(count)
+ return mock.MagicMock(side_effect=keypairs)
+
class FakeAvailabilityZone(object):
"""Fake one or more compute availability zones (AZs)."""
@@ -987,7 +1081,6 @@ class FakeHost(object):
# Set default attributes.
host_info = {
- "id": 1,
"service_id": 1,
"host": "host1",
"uuid": 'host-id-' + uuid.uuid4().hex,
diff --git a/openstackclient/tests/compute/v2/test_agent.py b/openstackclient/tests/compute/v2/test_agent.py
index d3d1ff29..da329728 100644
--- a/openstackclient/tests/compute/v2/test_agent.py
+++ b/openstackclient/tests/compute/v2/test_agent.py
@@ -16,9 +16,11 @@
import mock
from mock import call
-from openstackclient.common import exceptions
+from osc_lib import exceptions
+
from openstackclient.compute.v2 import agent
from openstackclient.tests.compute.v2 import fakes as compute_fakes
+from openstackclient.tests import utils as tests_utils
class TestAgent(compute_fakes.TestComputev2):
@@ -160,6 +162,15 @@ class TestAgentDelete(TestAgent):
]
self.agents_mock.delete.assert_has_calls(calls)
+ def test_agent_delete_no_input(self):
+ arglist = []
+ verifylist = None
+ self.assertRaises(tests_utils.ParserException,
+ self.check_parser,
+ self.cmd,
+ arglist,
+ verifylist)
+
class TestAgentList(TestAgent):
diff --git a/openstackclient/tests/compute/v2/test_aggregate.py b/openstackclient/tests/compute/v2/test_aggregate.py
index 58dd7755..3ebca35f 100644
--- a/openstackclient/tests/compute/v2/test_aggregate.py
+++ b/openstackclient/tests/compute/v2/test_aggregate.py
@@ -13,6 +13,12 @@
# under the License.
#
+import mock
+from mock import call
+
+from osc_lib import exceptions
+from osc_lib import utils
+
from openstackclient.compute.v2 import aggregate
from openstackclient.tests.compute.v2 import fakes as compute_fakes
from openstackclient.tests import utils as tests_utils
@@ -135,25 +141,74 @@ class TestAggregateCreate(TestAggregate):
class TestAggregateDelete(TestAggregate):
+ fake_ags = compute_fakes.FakeAggregate.create_aggregates(count=2)
+
def setUp(self):
super(TestAggregateDelete, self).setUp()
- self.aggregate_mock.get.return_value = self.fake_ag
+ self.aggregate_mock.get = (
+ compute_fakes.FakeAggregate.get_aggregates(self.fake_ags))
self.cmd = aggregate.DeleteAggregate(self.app, None)
def test_aggregate_delete(self):
arglist = [
- 'ag1',
+ self.fake_ags[0].id
]
verifylist = [
- ('aggregate', 'ag1'),
+ ('aggregate', [self.fake_ags[0].id]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
- self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate)
- self.aggregate_mock.delete.assert_called_once_with(self.fake_ag.id)
+ self.aggregate_mock.get.assert_called_once_with(self.fake_ags[0].id)
+ self.aggregate_mock.delete.assert_called_once_with(self.fake_ags[0].id)
self.assertIsNone(result)
+ def test_delete_multiple_aggregates(self):
+ arglist = []
+ for a in self.fake_ags:
+ arglist.append(a.id)
+ verifylist = [
+ ('aggregate', arglist),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ calls = []
+ for a in self.fake_ags:
+ calls.append(call(a.id))
+ self.aggregate_mock.delete.assert_has_calls(calls)
+ self.assertIsNone(result)
+
+ def test_delete_multiple_agggregates_with_exception(self):
+ arglist = [
+ self.fake_ags[0].id,
+ 'unexist_aggregate',
+ ]
+ verifylist = [
+ ('aggregate', arglist),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ find_mock_result = [self.fake_ags[0], exceptions.CommandError]
+ with mock.patch.object(utils, 'find_resource',
+ side_effect=find_mock_result) as find_mock:
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('1 of 2 aggregates failed to delete.',
+ str(e))
+
+ find_mock.assert_any_call(self.aggregate_mock, self.fake_ags[0].id)
+ find_mock.assert_any_call(self.aggregate_mock, 'unexist_aggregate')
+
+ self.assertEqual(2, find_mock.call_count)
+ self.aggregate_mock.delete.assert_called_once_with(
+ self.fake_ags[0].id
+ )
+
class TestAggregateList(TestAggregate):
diff --git a/openstackclient/tests/compute/v2/test_console.py b/openstackclient/tests/compute/v2/test_console.py
new file mode 100644
index 00000000..6be08126
--- /dev/null
+++ b/openstackclient/tests/compute/v2/test_console.py
@@ -0,0 +1,149 @@
+# Copyright 2016 Huawei, Inc. 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 mock
+
+from openstackclient.compute.v2 import console
+from openstackclient.tests.compute.v2 import fakes as compute_fakes
+
+
+class TestConsole(compute_fakes.TestComputev2):
+
+ def setUp(self):
+ super(TestConsole, self).setUp()
+ self.servers_mock = self.app.client_manager.compute.servers
+ self.servers_mock.reset_mock()
+
+
+class TestConsoleUrlShow(TestConsole):
+
+ def setUp(self):
+ super(TestConsoleUrlShow, self).setUp()
+ fake_console_data = {'remote_console': {'url': 'http://localhost',
+ 'protocol': 'fake_protocol',
+ 'type': 'fake_type'}}
+ methods = {
+ 'get_vnc_console': fake_console_data,
+ 'get_spice_console': fake_console_data,
+ 'get_serial_console': fake_console_data,
+ 'get_rdp_console': fake_console_data,
+ 'get_mks_console': fake_console_data,
+ }
+ self.fake_server = compute_fakes.FakeServer.create_one_server(
+ methods=methods)
+ self.servers_mock.get.return_value = self.fake_server
+
+ self.columns = (
+ 'protocol',
+ 'type',
+ 'url',
+ )
+ self.data = (
+ fake_console_data['remote_console']['protocol'],
+ fake_console_data['remote_console']['type'],
+ fake_console_data['remote_console']['url']
+ )
+
+ self.cmd = console.ShowConsoleURL(self.app, None)
+
+ def test_console_url_show_by_default(self):
+ arglist = [
+ 'foo_vm',
+ ]
+ verifylist = [
+ ('url_type', 'novnc'),
+ ('server', 'foo_vm'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+ self.fake_server.get_vnc_console.assert_called_once_with('novnc')
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ def test_console_url_show_with_novnc(self):
+ arglist = [
+ '--novnc',
+ 'foo_vm',
+ ]
+ verifylist = [
+ ('url_type', 'novnc'),
+ ('server', 'foo_vm'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+ self.fake_server.get_vnc_console.assert_called_once_with('novnc')
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ def test_console_url_show_with_xvpvnc(self):
+ arglist = [
+ '--xvpvnc',
+ 'foo_vm',
+ ]
+ verifylist = [
+ ('url_type', 'xvpvnc'),
+ ('server', 'foo_vm'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+ self.fake_server.get_vnc_console.assert_called_once_with('xvpvnc')
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ def test_console_url_show_with_spice(self):
+ arglist = [
+ '--spice',
+ 'foo_vm',
+ ]
+ verifylist = [
+ ('url_type', 'spice-html5'),
+ ('server', 'foo_vm'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+ self.fake_server.get_spice_console.assert_called_once_with(
+ 'spice-html5')
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ def test_console_url_show_compatible(self):
+ methods = {
+ 'get_vnc_console': {'console': {'url': 'http://localhost',
+ 'type': 'fake_type'}},
+ }
+ old_fake_server = compute_fakes.FakeServer.create_one_server(
+ methods=methods)
+ old_columns = (
+ 'type',
+ 'url',
+ )
+ old_data = (
+ methods['get_vnc_console']['console']['type'],
+ methods['get_vnc_console']['console']['url']
+ )
+ arglist = [
+ 'foo_vm',
+ ]
+ verifylist = [
+ ('url_type', 'novnc'),
+ ('server', 'foo_vm'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ with mock.patch.object(self.servers_mock, 'get',
+ return_value=old_fake_server):
+ columns, data = self.cmd.take_action(parsed_args)
+ old_fake_server.get_vnc_console.assert_called_once_with('novnc')
+ self.assertEqual(old_columns, columns)
+ self.assertEqual(old_data, data)
diff --git a/openstackclient/tests/compute/v2/test_flavor.py b/openstackclient/tests/compute/v2/test_flavor.py
index 434d5f92..da76b6d7 100644
--- a/openstackclient/tests/compute/v2/test_flavor.py
+++ b/openstackclient/tests/compute/v2/test_flavor.py
@@ -14,9 +14,12 @@
#
import copy
+import mock
+from mock import call
+
+from osc_lib import exceptions
+from osc_lib import utils
-from openstackclient.common import exceptions
-from openstackclient.common import utils
from openstackclient.compute.v2 import flavor
from openstackclient.tests.compute.v2 import fakes as compute_fakes
from openstackclient.tests import fakes
@@ -74,6 +77,12 @@ class TestFlavorCreate(TestFlavor):
def setUp(self):
super(TestFlavorCreate, self).setUp()
+ # Return a project
+ self.projects_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.PROJECT),
+ loaded=True,
+ )
self.flavors_mock.create.return_value = self.flavor
self.cmd = flavor.CreateFlavor(self.app, None)
@@ -160,6 +169,7 @@ class TestFlavorCreate(TestFlavor):
'--vcpus', str(self.flavor.vcpus),
'--rxtx-factor', str(self.flavor.rxtx_factor),
'--private',
+ '--project', identity_fakes.project_id,
]
verifylist = [
('name', self.flavor.name),
@@ -171,6 +181,7 @@ class TestFlavorCreate(TestFlavor):
('vcpus', self.flavor.vcpus),
('rxtx_factor', self.flavor.rxtx_factor),
('public', False),
+ ('project', identity_fakes.project_id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -187,10 +198,28 @@ class TestFlavorCreate(TestFlavor):
)
columns, data = self.cmd.take_action(parsed_args)
self.flavors_mock.create.assert_called_once_with(*args)
-
+ self.flavor_access_mock.add_tenant_access.assert_called_with(
+ self.flavor.id,
+ identity_fakes.project_id,
+ )
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
+ def test_public_flavor_create_with_project(self):
+ arglist = [
+ '--project', identity_fakes.project_id,
+ self.flavor.name,
+ ]
+ verifylist = [
+ ('project', identity_fakes.project_id),
+ ('name', self.flavor.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.assertRaises(exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+
def test_flavor_create_no_options(self):
arglist = []
verifylist = None
@@ -203,47 +232,73 @@ class TestFlavorCreate(TestFlavor):
class TestFlavorDelete(TestFlavor):
- flavor = compute_fakes.FakeFlavor.create_one_flavor()
+ flavors = compute_fakes.FakeFlavor.create_flavors(count=2)
def setUp(self):
super(TestFlavorDelete, self).setUp()
- self.flavors_mock.get.return_value = self.flavor
+ self.flavors_mock.get = (
+ compute_fakes.FakeFlavor.get_flavors(self.flavors))
self.flavors_mock.delete.return_value = None
self.cmd = flavor.DeleteFlavor(self.app, None)
def test_flavor_delete(self):
arglist = [
- self.flavor.id
+ self.flavors[0].id
]
verifylist = [
- ('flavor', self.flavor.id),
+ ('flavor', [self.flavors[0].id]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
- self.flavors_mock.delete.assert_called_with(self.flavor.id)
+ self.flavors_mock.delete.assert_called_with(self.flavors[0].id)
self.assertIsNone(result)
- def test_flavor_delete_with_unexist_flavor(self):
- self.flavors_mock.get.side_effect = exceptions.NotFound(None)
- self.flavors_mock.find.side_effect = exceptions.NotFound(None)
+ def test_delete_multiple_flavors(self):
+ arglist = []
+ for f in self.flavors:
+ arglist.append(f.id)
+ verifylist = [
+ ('flavor', arglist),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ calls = []
+ for f in self.flavors:
+ calls.append(call(f.id))
+ self.flavors_mock.delete.assert_has_calls(calls)
+ self.assertIsNone(result)
+ def test_multi_flavors_delete_with_exception(self):
arglist = [
- 'unexist_flavor'
+ self.flavors[0].id,
+ 'unexist_flavor',
]
verifylist = [
- ('flavor', 'unexist_flavor'),
+ ('flavor', [self.flavors[0].id, 'unexist_flavor'])
]
-
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.assertRaises(
- exceptions.CommandError,
- self.cmd.take_action,
- parsed_args)
+ find_mock_result = [self.flavors[0], exceptions.CommandError]
+ self.flavors_mock.get = (
+ mock.MagicMock(side_effect=find_mock_result)
+ )
+ self.flavors_mock.find.side_effect = exceptions.NotFound(None)
+
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('1 of 2 flavors failed to delete.', str(e))
+
+ self.flavors_mock.get.assert_any_call(self.flavors[0].id)
+ self.flavors_mock.get.assert_any_call('unexist_flavor')
+ self.flavors_mock.delete.assert_called_once_with(self.flavors[0].id)
class TestFlavorList(TestFlavor):
@@ -468,6 +523,7 @@ class TestFlavorSet(TestFlavor):
result = self.cmd.take_action(parsed_args)
self.flavors_mock.find.assert_called_with(name=parsed_args.flavor,
is_public=None)
+ self.flavor.set_keys.assert_called_with({'FOO': '"B A R"'})
self.assertIsNone(result)
def test_flavor_set_project(self):
@@ -482,12 +538,15 @@ class TestFlavorSet(TestFlavor):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
- self.assertIsNone(result)
+ self.flavors_mock.find.assert_called_with(name=parsed_args.flavor,
+ is_public=None)
self.flavor_access_mock.add_tenant_access.assert_called_with(
self.flavor.id,
identity_fakes.project_id,
)
+ self.flavor.set_keys.assert_not_called()
+ self.assertIsNone(result)
def test_flavor_set_no_project(self):
arglist = [
@@ -495,7 +554,7 @@ class TestFlavorSet(TestFlavor):
self.flavor.id,
]
verifylist = [
- ('project', ''),
+ ('project', None),
('flavor', self.flavor.id),
]
self.assertRaises(tests_utils.ParserException, self.check_parser,
@@ -508,12 +567,8 @@ class TestFlavorSet(TestFlavor):
verifylist = [
('project', identity_fakes.project_id),
]
-
- self.assertRaises(tests_utils.ParserException,
- self.check_parser,
- self.cmd,
- arglist,
- verifylist)
+ self.assertRaises(tests_utils.ParserException, self.check_parser,
+ self.cmd, arglist, verifylist)
def test_flavor_set_with_unexist_flavor(self):
self.flavors_mock.get.side_effect = exceptions.NotFound(None)
@@ -540,10 +595,13 @@ class TestFlavorSet(TestFlavor):
verifylist = [
('flavor', self.flavor.id),
]
-
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.assertRaises(exceptions.CommandError, self.cmd.take_action,
- parsed_args)
+ result = self.cmd.take_action(parsed_args)
+
+ self.flavors_mock.find.assert_called_with(name=parsed_args.flavor,
+ is_public=None)
+ self.flavor_access_mock.add_tenant_access.assert_not_called()
+ self.assertIsNone(result)
class TestFlavorShow(TestFlavor):
@@ -644,6 +702,8 @@ class TestFlavorUnset(TestFlavor):
result = self.cmd.take_action(parsed_args)
self.flavors_mock.find.assert_called_with(name=parsed_args.flavor,
is_public=None)
+ self.flavor.unset_keys.assert_called_with(['property'])
+ self.flavor_access_mock.remove_tenant_access.assert_not_called()
self.assertIsNone(result)
def test_flavor_unset_project(self):
@@ -660,24 +720,26 @@ class TestFlavorUnset(TestFlavor):
result = self.cmd.take_action(parsed_args)
self.assertIsNone(result)
+ self.flavors_mock.find.assert_called_with(name=parsed_args.flavor,
+ is_public=None)
self.flavor_access_mock.remove_tenant_access.assert_called_with(
self.flavor.id,
identity_fakes.project_id,
)
+ self.flavor.unset_keys.assert_not_called()
+ self.assertIsNone(result)
def test_flavor_unset_no_project(self):
arglist = [
- '--project', '',
+ '--project',
self.flavor.id,
]
verifylist = [
- ('project', ''),
+ ('project', None),
('flavor', self.flavor.id),
]
-
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.assertRaises(exceptions.CommandError, self.cmd.take_action,
- parsed_args)
+ self.assertRaises(tests_utils.ParserException, self.check_parser,
+ self.cmd, arglist, verifylist)
def test_flavor_unset_no_flavor(self):
arglist = [
@@ -686,12 +748,8 @@ class TestFlavorUnset(TestFlavor):
verifylist = [
('project', identity_fakes.project_id),
]
-
- self.assertRaises(tests_utils.ParserException,
- self.check_parser,
- self.cmd,
- arglist,
- verifylist)
+ self.assertRaises(tests_utils.ParserException, self.check_parser,
+ self.cmd, arglist, verifylist)
def test_flavor_unset_with_unexist_flavor(self):
self.flavors_mock.get.side_effect = exceptions.NotFound(None)
@@ -706,9 +764,7 @@ class TestFlavorUnset(TestFlavor):
('flavor', 'unexist_flavor'),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- self.assertRaises(exceptions.CommandError,
- self.cmd.take_action,
+ self.assertRaises(exceptions.CommandError, self.cmd.take_action,
parsed_args)
def test_flavor_unset_nothing(self):
@@ -718,7 +774,9 @@ class TestFlavorUnset(TestFlavor):
verifylist = [
('flavor', self.flavor.id),
]
-
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.assertRaises(exceptions.CommandError, self.cmd.take_action,
- parsed_args)
+
+ result = self.cmd.take_action(parsed_args)
+ self.assertIsNone(result)
+
+ self.flavor_access_mock.remove_tenant_access.assert_not_called()
diff --git a/openstackclient/tests/compute/v2/test_host.py b/openstackclient/tests/compute/v2/test_host.py
index de537577..63ae1f6d 100644
--- a/openstackclient/tests/compute/v2/test_host.py
+++ b/openstackclient/tests/compute/v2/test_host.py
@@ -40,10 +40,10 @@ class TestHostSet(TestHost):
def test_host_set_no_option(self):
arglist = [
- str(self.host.id)
+ self.host.host
]
verifylist = [
- ('host', str(self.host.id))
+ ('host', self.host.host)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -52,18 +52,18 @@ class TestHostSet(TestHost):
self.assertIsNone(result)
body = {}
- self.host_mock.update.assert_called_with(self.host.id, body)
+ self.host_mock.update.assert_called_with(self.host.host, body)
def test_host_set(self):
arglist = [
'--enable',
'--disable-maintenance',
- str(self.host.id)
+ self.host.host
]
verifylist = [
('enable', True),
('enable_maintenance', False),
- ('host', str(self.host.id))
+ ('host', self.host.host)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -71,5 +71,5 @@ class TestHostSet(TestHost):
result = self.cmd.take_action(parsed_args)
self.assertIsNone(result)
- body = {'status': True, 'maintenance_mode': False}
- self.host_mock.update.assert_called_with(self.host.id, body)
+ body = {'status': 'enable', 'maintenance_mode': 'disable'}
+ self.host_mock.update.assert_called_with(self.host.host, body)
diff --git a/openstackclient/tests/compute/v2/test_hypervisor.py b/openstackclient/tests/compute/v2/test_hypervisor.py
index 8d717ba7..ee0f40ed 100644
--- a/openstackclient/tests/compute/v2/test_hypervisor.py
+++ b/openstackclient/tests/compute/v2/test_hypervisor.py
@@ -15,7 +15,8 @@
import copy
-from openstackclient.common import exceptions
+from osc_lib import exceptions
+
from openstackclient.compute.v2 import hypervisor
from openstackclient.tests.compute.v2 import fakes as compute_fakes
from openstackclient.tests import fakes
diff --git a/openstackclient/tests/compute/v2/test_keypair.py b/openstackclient/tests/compute/v2/test_keypair.py
index a50a5323..25949e31 100644
--- a/openstackclient/tests/compute/v2/test_keypair.py
+++ b/openstackclient/tests/compute/v2/test_keypair.py
@@ -14,6 +14,10 @@
#
import mock
+from mock import call
+
+from osc_lib import exceptions
+from osc_lib import utils
from openstackclient.compute.v2 import keypair
from openstackclient.tests.compute.v2 import fakes as compute_fakes
@@ -114,22 +118,23 @@ class TestKeypairCreate(TestKeypair):
class TestKeypairDelete(TestKeypair):
- keypair = compute_fakes.FakeKeypair.create_one_keypair()
+ keypairs = compute_fakes.FakeKeypair.create_keypairs(count=2)
def setUp(self):
super(TestKeypairDelete, self).setUp()
- self.keypairs_mock.get.return_value = self.keypair
+ self.keypairs_mock.get = compute_fakes.FakeKeypair.get_keypairs(
+ self.keypairs)
self.keypairs_mock.delete.return_value = None
self.cmd = keypair.DeleteKeypair(self.app, None)
def test_keypair_delete(self):
arglist = [
- self.keypair.name
+ self.keypairs[0].name
]
verifylist = [
- ('name', self.keypair.name),
+ ('name', [self.keypairs[0].name]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -137,7 +142,54 @@ class TestKeypairDelete(TestKeypair):
ret = self.cmd.take_action(parsed_args)
self.assertIsNone(ret)
- self.keypairs_mock.delete.assert_called_with(self.keypair.name)
+ self.keypairs_mock.delete.assert_called_with(self.keypairs[0].name)
+
+ def test_delete_multiple_keypairs(self):
+ arglist = []
+ for k in self.keypairs:
+ arglist.append(k.name)
+ verifylist = [
+ ('name', arglist),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ calls = []
+ for k in self.keypairs:
+ calls.append(call(k.name))
+ self.keypairs_mock.delete.assert_has_calls(calls)
+ self.assertIsNone(result)
+
+ def test_delete_multiple_keypairs_with_exception(self):
+ arglist = [
+ self.keypairs[0].name,
+ 'unexist_keypair',
+ ]
+ verifylist = [
+ ('name', arglist),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ find_mock_result = [self.keypairs[0], exceptions.CommandError]
+ with mock.patch.object(utils, 'find_resource',
+ side_effect=find_mock_result) as find_mock:
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('1 of 2 public keys failed to delete.',
+ str(e))
+
+ find_mock.assert_any_call(
+ self.keypairs_mock, self.keypairs[0].name)
+ find_mock.assert_any_call(self.keypairs_mock, 'unexist_keypair')
+
+ self.assertEqual(2, find_mock.call_count)
+ self.keypairs_mock.delete.assert_called_once_with(
+ self.keypairs[0].name
+ )
class TestKeypairList(TestKeypair):
diff --git a/openstackclient/tests/compute/v2/test_server.py b/openstackclient/tests/compute/v2/test_server.py
index d642e6dd..0f155601 100644
--- a/openstackclient/tests/compute/v2/test_server.py
+++ b/openstackclient/tests/compute/v2/test_server.py
@@ -14,10 +14,11 @@
#
import getpass
import mock
-
from mock import call
-from openstackclient.common import exceptions
-from openstackclient.common import utils as common_utils
+
+from osc_lib import exceptions
+from osc_lib import utils as common_utils
+
from openstackclient.compute.v2 import server
from openstackclient.tests.compute.v2 import fakes as compute_fakes
from openstackclient.tests.image.v2 import fakes as image_fakes
@@ -510,150 +511,6 @@ class TestServerDumpCreate(TestServer):
self.run_method_with_servers('trigger_crash_dump', 3)
-class TestServerImageCreate(TestServer):
-
- columns = (
- 'id',
- 'name',
- 'owner',
- 'protected',
- 'tags',
- 'visibility',
- )
-
- def datalist(self):
- datalist = (
- self.image.id,
- self.image.name,
- self.image.owner,
- self.image.protected,
- self.image.tags,
- self.image.visibility,
- )
- return datalist
-
- def setUp(self):
- super(TestServerImageCreate, self).setUp()
-
- self.server = compute_fakes.FakeServer.create_one_server()
-
- # This is the return value for utils.find_resource()
- self.servers_mock.get.return_value = self.server
-
- self.image = image_fakes.FakeImage.create_one_image()
- self.images_mock.get.return_value = self.image
- self.servers_mock.create_image.return_value = self.image.id
-
- # Get the command object to test
- self.cmd = server.CreateServerImage(self.app, None)
-
- def test_server_image_create_no_options(self):
- arglist = [
- self.server.id,
- ]
- verifylist = [
- ('server', self.server.id),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- # In base command class ShowOne in cliff, abstract method take_action()
- # returns a two-part tuple with a tuple of column names and a tuple of
- # data to be shown.
- columns, data = self.cmd.take_action(parsed_args)
-
- # ServerManager.create_image(server, image_name, metadata=)
- self.servers_mock.create_image.assert_called_with(
- self.servers_mock.get.return_value,
- self.server.name,
- )
-
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist(), data)
-
- def test_server_image_create_name(self):
- arglist = [
- '--name', 'img-nam',
- self.server.id,
- ]
- verifylist = [
- ('name', 'img-nam'),
- ('server', self.server.id),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- # In base command class ShowOne in cliff, abstract method take_action()
- # returns a two-part tuple with a tuple of column names and a tuple of
- # data to be shown.
- columns, data = self.cmd.take_action(parsed_args)
-
- # ServerManager.create_image(server, image_name, metadata=)
- self.servers_mock.create_image.assert_called_with(
- self.servers_mock.get.return_value,
- 'img-nam',
- )
-
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist(), data)
-
- @mock.patch.object(common_utils, 'wait_for_status', return_value=False)
- def test_server_create_image_with_wait_fails(self, mock_wait_for_status):
- arglist = [
- '--wait',
- self.server.id,
- ]
- verifylist = [
- ('wait', True),
- ('server', self.server.id),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- self.assertRaises(SystemExit, self.cmd.take_action, parsed_args)
-
- mock_wait_for_status.assert_called_once_with(
- self.images_mock.get,
- self.image.id,
- callback=server._show_progress
- )
-
- # ServerManager.create_image(server, image_name, metadata=)
- self.servers_mock.create_image.assert_called_with(
- self.servers_mock.get.return_value,
- self.server.name,
- )
-
- @mock.patch.object(common_utils, 'wait_for_status', return_value=True)
- def test_server_create_image_with_wait_ok(self, mock_wait_for_status):
- arglist = [
- '--wait',
- self.server.id,
- ]
- verifylist = [
- ('wait', True),
- ('server', self.server.id),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- # In base command class ShowOne in cliff, abstract method take_action()
- # returns a two-part tuple with a tuple of column names and a tuple of
- # data to be shown.
- columns, data = self.cmd.take_action(parsed_args)
-
- # ServerManager.create_image(server, image_name, metadata=)
- self.servers_mock.create_image.assert_called_with(
- self.servers_mock.get.return_value,
- self.server.name,
- )
-
- mock_wait_for_status.assert_called_once_with(
- self.images_mock.get,
- self.image.id,
- callback=server._show_progress
- )
-
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist(), data)
-
-
class TestServerList(TestServer):
# Columns to be listed up.
@@ -1691,7 +1548,7 @@ class TestServerGeneral(TestServer):
(data_1, data_2, networks_format))
self.assertIn(networks_format, (data_1, data_2), msg)
- @mock.patch('openstackclient.common.utils.find_resource')
+ @mock.patch('osc_lib.utils.find_resource')
def test_prep_server_detail(self, find_resource):
# Setup mock method return value. utils.find_resource() will be called
# three times in _prep_server_detail():
diff --git a/openstackclient/tests/compute/v2/test_server_backup.py b/openstackclient/tests/compute/v2/test_server_backup.py
index b35f9f52..b6802ff0 100644
--- a/openstackclient/tests/compute/v2/test_server_backup.py
+++ b/openstackclient/tests/compute/v2/test_server_backup.py
@@ -13,8 +13,9 @@
import mock
-from openstackclient.common import exceptions
-from openstackclient.common import utils as common_utils
+from osc_lib import exceptions
+from osc_lib import utils as common_utils
+
from openstackclient.compute.v2 import server_backup
from openstackclient.tests.compute.v2 import fakes as compute_fakes
from openstackclient.tests.image.v2 import fakes as image_fakes
diff --git a/openstackclient/tests/compute/v2/test_server_group.py b/openstackclient/tests/compute/v2/test_server_group.py
index 70ff23f9..bd5f8471 100644
--- a/openstackclient/tests/compute/v2/test_server_group.py
+++ b/openstackclient/tests/compute/v2/test_server_group.py
@@ -15,8 +15,9 @@
import mock
-from openstackclient.common import exceptions
-from openstackclient.common import utils
+from osc_lib import exceptions
+from osc_lib import utils
+
from openstackclient.compute.v2 import server_group
from openstackclient.tests.compute.v2 import fakes as compute_fakes
from openstackclient.tests import utils as tests_utils
diff --git a/openstackclient/tests/compute/v2/test_server_image.py b/openstackclient/tests/compute/v2/test_server_image.py
new file mode 100644
index 00000000..8a8bd9bc
--- /dev/null
+++ b/openstackclient/tests/compute/v2/test_server_image.py
@@ -0,0 +1,228 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+import mock
+
+from osc_lib import exceptions
+from osc_lib import utils as common_utils
+
+from openstackclient.compute.v2 import server_image
+from openstackclient.tests.compute.v2 import fakes as compute_fakes
+from openstackclient.tests.image.v2 import fakes as image_fakes
+
+
+class TestServerImage(compute_fakes.TestComputev2):
+
+ def setUp(self):
+ super(TestServerImage, self).setUp()
+
+ # Get a shortcut to the compute client ServerManager Mock
+ self.servers_mock = self.app.client_manager.compute.servers
+ self.servers_mock.reset_mock()
+
+ # Get a shortcut to the image client ImageManager Mock
+ self.images_mock = self.app.client_manager.image.images
+ self.images_mock.reset_mock()
+
+ # Set object attributes to be tested. Could be overwriten in subclass.
+ self.attrs = {}
+
+ # Set object methods to be tested. Could be overwriten in subclass.
+ self.methods = {}
+
+ def setup_servers_mock(self, count):
+ servers = compute_fakes.FakeServer.create_servers(
+ attrs=self.attrs,
+ methods=self.methods,
+ count=count,
+ )
+
+ # This is the return value for utils.find_resource()
+ self.servers_mock.get = compute_fakes.FakeServer.get_servers(
+ servers,
+ 0,
+ )
+ return servers
+
+
+class TestServerImageCreate(TestServerImage):
+
+ def image_columns(self, image):
+ columnlist = tuple(sorted(image.keys()))
+ return columnlist
+
+ def image_data(self, image):
+ datalist = (
+ image['id'],
+ image['name'],
+ image['owner'],
+ image['protected'],
+ 'active',
+ common_utils.format_list(image.get('tags')),
+ image['visibility'],
+ )
+ return datalist
+
+ def setUp(self):
+ super(TestServerImageCreate, self).setUp()
+
+ # Get the command object to test
+ self.cmd = server_image.CreateServerImage(self.app, None)
+
+ self.methods = {
+ 'create_image': None,
+ }
+
+ def setup_images_mock(self, count, servers=None):
+ if servers:
+ images = image_fakes.FakeImage.create_images(
+ attrs={
+ 'name': servers[0].name,
+ 'status': 'active',
+ },
+ count=count,
+ )
+ else:
+ images = image_fakes.FakeImage.create_images(
+ attrs={
+ 'status': 'active',
+ },
+ count=count,
+ )
+
+ self.images_mock.get = mock.MagicMock(side_effect=images)
+ self.servers_mock.create_image = mock.MagicMock(
+ return_value=images[0].id,
+ )
+ return images
+
+ def test_server_image_create_defaults(self):
+ servers = self.setup_servers_mock(count=1)
+ images = self.setup_images_mock(count=1, servers=servers)
+
+ arglist = [
+ servers[0].id,
+ ]
+ verifylist = [
+ ('server', servers[0].id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # ServerManager.create_image(server, image_name, metadata=)
+ self.servers_mock.create_image.assert_called_with(
+ servers[0].id,
+ servers[0].name,
+ )
+
+ self.assertEqual(self.image_columns(images[0]), columns)
+ self.assertEqual(self.image_data(images[0]), data)
+
+ def test_server_image_create_options(self):
+ servers = self.setup_servers_mock(count=1)
+ images = self.setup_images_mock(count=1, servers=servers)
+
+ arglist = [
+ '--name', 'img-nam',
+ servers[0].id,
+ ]
+ verifylist = [
+ ('name', 'img-nam'),
+ ('server', servers[0].id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # ServerManager.create_image(server, image_name, metadata=)
+ self.servers_mock.create_image.assert_called_with(
+ servers[0].id,
+ 'img-nam',
+ )
+
+ self.assertEqual(self.image_columns(images[0]), columns)
+ self.assertEqual(self.image_data(images[0]), data)
+
+ @mock.patch.object(common_utils, 'wait_for_status', return_value=False)
+ def test_server_create_image_wait_fail(self, mock_wait_for_status):
+ servers = self.setup_servers_mock(count=1)
+ images = self.setup_images_mock(count=1, servers=servers)
+
+ arglist = [
+ '--wait',
+ servers[0].id,
+ ]
+ verifylist = [
+ ('wait', True),
+ ('server', servers[0].id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args,
+ )
+
+ # ServerManager.create_image(server, image_name, metadata=)
+ self.servers_mock.create_image.assert_called_with(
+ servers[0].id,
+ servers[0].name,
+ )
+
+ mock_wait_for_status.assert_called_once_with(
+ self.images_mock.get,
+ images[0].id,
+ callback=mock.ANY
+ )
+
+ @mock.patch.object(common_utils, 'wait_for_status', return_value=True)
+ def test_server_create_image_wait_ok(self, mock_wait_for_status):
+ servers = self.setup_servers_mock(count=1)
+ images = self.setup_images_mock(count=1, servers=servers)
+
+ arglist = [
+ '--wait',
+ servers[0].id,
+ ]
+ verifylist = [
+ ('wait', True),
+ ('server', servers[0].id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # ServerManager.create_image(server, image_name, metadata=)
+ self.servers_mock.create_image.assert_called_with(
+ servers[0].id,
+ servers[0].name,
+ )
+
+ mock_wait_for_status.assert_called_once_with(
+ self.images_mock.get,
+ images[0].id,
+ callback=mock.ANY
+ )
+
+ self.assertEqual(self.image_columns(images[0]), columns)
+ self.assertEqual(self.image_data(images[0]), data)
diff --git a/openstackclient/tests/compute/v2/test_service.py b/openstackclient/tests/compute/v2/test_service.py
index cf534978..1599f466 100644
--- a/openstackclient/tests/compute/v2/test_service.py
+++ b/openstackclient/tests/compute/v2/test_service.py
@@ -13,7 +13,11 @@
# under the License.
#
-from openstackclient.common import exceptions
+import mock
+from mock import call
+
+from osc_lib import exceptions
+
from openstackclient.compute.v2 import service
from openstackclient.tests.compute.v2 import fakes as compute_fakes
@@ -30,40 +34,106 @@ class TestService(compute_fakes.TestComputev2):
class TestServiceDelete(TestService):
+ services = compute_fakes.FakeService.create_services(count=2)
+
def setUp(self):
super(TestServiceDelete, self).setUp()
- self.service = compute_fakes.FakeService.create_one_service()
-
self.service_mock.delete.return_value = None
# Get the command object to test
self.cmd = service.DeleteService(self.app, None)
- def test_service_delete_no_options(self):
+ def test_service_delete(self):
arglist = [
- self.service.binary,
+ self.services[0].binary,
]
verifylist = [
- ('service', self.service.binary),
+ ('service', [self.services[0].binary]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.service_mock.delete.assert_called_with(
- self.service.binary,
+ self.services[0].binary,
)
self.assertIsNone(result)
+ def test_multi_services_delete(self):
+ arglist = []
+ for s in self.services:
+ arglist.append(s.binary)
+ verifylist = [
+ ('service', arglist),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ calls = []
+ for s in self.services:
+ calls.append(call(s.binary))
+ self.service_mock.delete.assert_has_calls(calls)
+ self.assertIsNone(result)
+
+ def test_multi_services_delete_with_exception(self):
+ arglist = [
+ self.services[0].binary,
+ 'unexist_service',
+ ]
+ verifylist = [
+ ('service', arglist)
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ delete_mock_result = [None, exceptions.CommandError]
+ self.service_mock.delete = (
+ mock.MagicMock(side_effect=delete_mock_result)
+ )
+
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual(
+ '1 of 2 compute services failed to delete.', str(e))
+
+ self.service_mock.delete.assert_any_call(self.services[0].binary)
+ self.service_mock.delete.assert_any_call('unexist_service')
+
class TestServiceList(TestService):
+ service = compute_fakes.FakeService.create_one_service()
+
+ columns = (
+ 'ID',
+ 'Binary',
+ 'Host',
+ 'Zone',
+ 'Status',
+ 'State',
+ 'Updated At',
+ )
+ columns_long = columns + (
+ 'Disabled Reason',
+ )
+
+ data = [(
+ service.id,
+ service.binary,
+ service.host,
+ service.zone,
+ service.status,
+ service.state,
+ service.updated_at,
+ )]
+ data_long = [data[0] + (service.disabled_reason, )]
+
def setUp(self):
super(TestServiceList, self).setUp()
- self.service = compute_fakes.FakeService.create_one_service()
-
self.service_mock.list.return_value = [self.service]
# Get the command object to test
@@ -90,8 +160,8 @@ class TestServiceList(TestService):
self.service.binary,
)
- self.assertNotIn("Disabled Reason", columns)
- self.assertNotIn(self.service.disabled_reason, list(data)[0])
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
def test_service_list_with_long_option(self):
arglist = [
@@ -111,8 +181,13 @@ class TestServiceList(TestService):
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
- self.assertIn("Disabled Reason", columns)
- self.assertIn(self.service.disabled_reason, list(data)[0])
+ self.service_mock.list.assert_called_with(
+ self.service.host,
+ self.service.binary,
+ )
+
+ self.assertEqual(self.columns_long, columns)
+ self.assertEqual(self.data_long, list(data))
class TestServiceSet(TestService):
@@ -224,8 +299,12 @@ class TestServiceSet(TestService):
('service', self.service.binary),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.assertRaises(exceptions.CommandError, self.cmd.take_action,
- parsed_args)
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail("CommandError should be raised.")
+ except exceptions.CommandError as e:
+ self.assertEqual("Cannot specify option --disable-reason without "
+ "--disable specified.", str(e))
def test_service_set_enable_with_disable_reason(self):
reason = 'earthquake'
@@ -242,5 +321,90 @@ class TestServiceSet(TestService):
('service', self.service.binary),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.assertRaises(exceptions.CommandError, self.cmd.take_action,
- parsed_args)
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail("CommandError should be raised.")
+ except exceptions.CommandError as e:
+ self.assertEqual("Cannot specify option --disable-reason without "
+ "--disable specified.", str(e))
+
+ def test_service_set_state_up(self):
+ arglist = [
+ '--up',
+ self.service.host,
+ self.service.binary,
+ ]
+ verifylist = [
+ ('up', True),
+ ('host', self.service.host),
+ ('service', self.service.binary),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+ self.service_mock.force_down.assert_called_once_with(
+ self.service.host, self.service.binary, force_down=False)
+ self.assertNotCalled(self.service_mock.enable)
+ self.assertNotCalled(self.service_mock.disable)
+ self.assertIsNone(result)
+
+ def test_service_set_state_down(self):
+ arglist = [
+ '--down',
+ self.service.host,
+ self.service.binary,
+ ]
+ verifylist = [
+ ('down', True),
+ ('host', self.service.host),
+ ('service', self.service.binary),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+ self.service_mock.force_down.assert_called_once_with(
+ self.service.host, self.service.binary, force_down=True)
+ self.assertNotCalled(self.service_mock.enable)
+ self.assertNotCalled(self.service_mock.disable)
+ self.assertIsNone(result)
+
+ def test_service_set_enable_and_state_down(self):
+ arglist = [
+ '--enable',
+ '--down',
+ self.service.host,
+ self.service.binary,
+ ]
+ verifylist = [
+ ('enable', True),
+ ('down', True),
+ ('host', self.service.host),
+ ('service', self.service.binary),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+ self.service_mock.enable.assert_called_once_with(
+ self.service.host, self.service.binary)
+ self.service_mock.force_down.assert_called_once_with(
+ self.service.host, self.service.binary, force_down=True)
+ self.assertIsNone(result)
+
+ def test_service_set_enable_and_state_down_with_exception(self):
+ arglist = [
+ '--enable',
+ '--down',
+ self.service.host,
+ self.service.binary,
+ ]
+ verifylist = [
+ ('enable', True),
+ ('down', True),
+ ('host', self.service.host),
+ ('service', self.service.binary),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ with mock.patch.object(self.service_mock, 'enable',
+ side_effect=Exception()):
+ self.assertRaises(exceptions.CommandError,
+ self.cmd.take_action, parsed_args)
+ self.service_mock.force_down.assert_called_once_with(
+ self.service.host, self.service.binary, force_down=True)
diff --git a/openstackclient/tests/fakes.py b/openstackclient/tests/fakes.py
index ad4705a4..d0cab019 100644
--- a/openstackclient/tests/fakes.py
+++ b/openstackclient/tests/fakes.py
@@ -50,6 +50,21 @@ TEST_RESPONSE_DICT_V3.set_project_scope()
TEST_VERSIONS = fixture.DiscoveryList(href=AUTH_URL)
+def to_unicode_dict(catalog_dict):
+ """Converts dict to unicode dict
+
+ """
+ if isinstance(catalog_dict, dict):
+ return {to_unicode_dict(key): to_unicode_dict(value)
+ for key, value in catalog_dict.items()}
+ elif isinstance(catalog_dict, list):
+ return [to_unicode_dict(element) for element in catalog_dict]
+ elif isinstance(catalog_dict, str):
+ return catalog_dict + u""
+ else:
+ return catalog_dict
+
+
class FakeStdout(object):
def __init__(self):
diff --git a/openstackclient/tests/identity/v2_0/fakes.py b/openstackclient/tests/identity/v2_0/fakes.py
index b37bd9da..662d56b6 100644
--- a/openstackclient/tests/identity/v2_0/fakes.py
+++ b/openstackclient/tests/identity/v2_0/fakes.py
@@ -13,7 +13,12 @@
# under the License.
#
+import copy
import mock
+import uuid
+
+from keystoneauth1 import access
+from keystoneauth1 import fixture
from openstackclient.tests import fakes
from openstackclient.tests import utils
@@ -71,19 +76,19 @@ USER = {
}
token_expires = '2014-01-01T00:00:00Z'
-token_id = 'tttttttt-tttt-tttt-tttt-tttttttttttt'
+token_id = 'token-id-' + uuid.uuid4().hex
TOKEN = {
'expires': token_expires,
'id': token_id,
- 'tenant_id': project_id,
- 'user_id': user_id,
+ 'tenant_id': 'project-id',
+ 'user_id': 'user-id',
}
UNSCOPED_TOKEN = {
'expires': token_expires,
'id': token_id,
- 'user_id': user_id,
+ 'user_id': 'user-id',
}
endpoint_name = service_name
@@ -106,25 +111,42 @@ ENDPOINT = {
'service_id': endpoint_service_id,
}
-extension_name = 'OpenStack Keystone User CRUD'
-extension_namespace = 'http://docs.openstack.org/identity/'\
- 'api/ext/OS-KSCRUD/v1.0'
-extension_description = 'OpenStack extensions to Keystone v2.0 API'\
- ' enabling User Operations.'
-extension_updated = '2013-07-07T12:00:0-00:00'
-extension_alias = 'OS-KSCRUD'
-extension_links = '[{"href":'\
- '"https://github.com/openstack/identity-api", "type":'\
- ' "text/html", "rel": "describedby"}]'
-
-EXTENSION = {
- 'name': extension_name,
- 'namespace': extension_namespace,
- 'description': extension_description,
- 'updated': extension_updated,
- 'alias': extension_alias,
- 'links': extension_links,
-}
+
+def fake_auth_ref(fake_token, fake_service=None):
+ """Create an auth_ref using keystoneauth's fixtures"""
+ token_copy = copy.deepcopy(fake_token)
+ token_copy['token_id'] = token_copy.pop('id')
+ token = fixture.V2Token(**token_copy)
+ # An auth_ref is actually an access info object
+ auth_ref = access.create(body=token)
+
+ # Create a service catalog
+ if fake_service:
+ service = token.add_service(
+ fake_service.type,
+ fake_service.name,
+ )
+ # TODO(dtroyer): Add an 'id' element to KSA's _Service fixure
+ service['id'] = fake_service.id
+ for e in fake_service.endpoints:
+ # KSA's _Service fixture copies publicURL to internalURL and
+ # adminURL if they do not exist. Soooo helpful...
+ internal = e.get('internalURL', None)
+ admin = e.get('adminURL', None)
+ region = e.get('region_id') or e.get('region', '<none>')
+ endpoint = service.add_endpoint(
+ public=e['publicURL'],
+ internal=internal,
+ admin=admin,
+ region=region,
+ )
+ # ...so undo that helpfulness
+ if not internal:
+ endpoint['internalURL'] = None
+ if not admin:
+ endpoint['adminURL'] = None
+
+ return auth_ref
class FakeIdentityv2Client(object):
@@ -166,3 +188,323 @@ class TestIdentityv2(utils.TestCommand):
endpoint=fakes.AUTH_URL,
token=fakes.AUTH_TOKEN,
)
+
+
+class FakeExtension(object):
+ """Fake one or more extension."""
+
+ @staticmethod
+ def create_one_extension(attrs=None):
+ """Create a fake extension.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ A FakeResource object with name, namespace, etc.
+ """
+ attrs = attrs or {}
+
+ # Set default attributes.
+ extension_info = {
+ 'name': 'name-' + uuid.uuid4().hex,
+ 'namespace': ('http://docs.openstack.org/identity/'
+ 'api/ext/OS-KSCRUD/v1.0'),
+ 'description': 'description-' + uuid.uuid4().hex,
+ 'updated': '2013-07-07T12:00:0-00:00',
+ 'alias': 'OS-KSCRUD',
+ 'links': ('[{"href":'
+ '"https://github.com/openstack/identity-api", "type":'
+ ' "text/html", "rel": "describedby"}]')
+ }
+
+ # Overwrite default attributes.
+ extension_info.update(attrs)
+
+ extension = fakes.FakeResource(
+ info=copy.deepcopy(extension_info),
+ loaded=True)
+ return extension
+
+
+class FakeCatalog(object):
+ """Fake one or more catalog."""
+
+ @staticmethod
+ def create_catalog(attrs=None):
+ """Create a fake catalog.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ A FakeResource object with id, name, type and so on.
+ """
+ attrs = attrs or {}
+
+ # Set default attributes.
+ catalog_info = {
+ 'id': 'service-id-' + uuid.uuid4().hex,
+ 'type': 'compute',
+ 'name': 'supernova',
+ 'endpoints': [
+ {
+ 'region': 'one',
+ 'publicURL': 'https://public.one.example.com',
+ 'internalURL': 'https://internal.one.example.com',
+ 'adminURL': 'https://admin.one.example.com',
+ },
+ {
+ 'region': 'two',
+ 'publicURL': 'https://public.two.example.com',
+ 'internalURL': 'https://internal.two.example.com',
+ 'adminURL': 'https://admin.two.example.com',
+ },
+ {
+ 'region': None,
+ 'publicURL': 'https://public.none.example.com',
+ 'internalURL': 'https://internal.none.example.com',
+ 'adminURL': 'https://admin.none.example.com',
+ },
+ ],
+ }
+ # Overwrite default attributes.
+ catalog_info.update(attrs)
+
+ catalog = fakes.FakeResource(
+ info=copy.deepcopy(catalog_info),
+ loaded=True)
+
+ return catalog
+
+
+class FakeProject(object):
+ """Fake one or more project."""
+
+ @staticmethod
+ def create_one_project(attrs=None):
+ """Create a fake project.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ A FakeResource object, with id, name, and so on
+ """
+
+ attrs = attrs or {}
+
+ # set default attributes.
+ project_info = {
+ 'id': 'project-id-' + uuid.uuid4().hex,
+ 'name': 'project-name-' + uuid.uuid4().hex,
+ 'description': 'project_description',
+ 'enabled': True,
+ }
+ project_info.update(attrs)
+
+ project = fakes.FakeResource(info=copy.deepcopy(project_info),
+ loaded=True)
+ return project
+
+ @staticmethod
+ def create_projects(attrs=None, count=2):
+ """Create multiple fake projects.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :param int count:
+ The number of projects to fake
+ :return:
+ A list of FakeResource objects faking the projects
+ """
+ projects = []
+ for i in range(0, count):
+ projects.append(FakeProject.create_one_project(attrs))
+
+ return projects
+
+
+class FakeEndpoint(object):
+ """Fake one or more endpoint."""
+
+ @staticmethod
+ def create_one_endpoint(attrs=None):
+ """Create a fake agent.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ A FakeResource object, with id, name, region, and so on
+ """
+
+ attrs = attrs or {}
+
+ # set default attributes.
+ endpoint_info = {
+ 'service_name': 'service-name-' + uuid.uuid4().hex,
+ 'adminurl': 'http://endpoint_adminurl',
+ 'region': 'endpoint_region',
+ 'internalurl': 'http://endpoint_internalurl',
+ 'service_type': 'service_type',
+ 'id': 'endpoint-id-' + uuid.uuid4().hex,
+ 'publicurl': 'http://endpoint_publicurl',
+ 'service_id': 'service-name-' + uuid.uuid4().hex,
+
+ }
+ endpoint_info.update(attrs)
+
+ endpoint = fakes.FakeResource(info=copy.deepcopy(endpoint_info),
+ loaded=True)
+ return endpoint
+
+ @staticmethod
+ def create_endpoints(attrs=None, count=2):
+ """Create multiple fake endpoints.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :param int count:
+ The number of endpoints to fake
+ :return:
+ A list of FakeResource objects faking the endpoints
+ """
+ endpoints = []
+ for i in range(0, count):
+ endpoints.append(FakeEndpoint.create_one_endpoint(attrs))
+
+ return endpoints
+
+
+class FakeService(object):
+ """Fake one or more service."""
+
+ @staticmethod
+ def create_one_service(attrs=None):
+ """Create a fake service.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ A FakeResource object, with id, name, type, and so on
+ """
+
+ attrs = attrs or {}
+
+ # set default attributes.
+ service_info = {
+ 'id': 'service-id-' + uuid.uuid4().hex,
+ 'name': 'service-name-' + uuid.uuid4().hex,
+ 'description': 'service_description',
+ 'type': 'service_type',
+
+ }
+ service_info.update(attrs)
+
+ service = fakes.FakeResource(info=copy.deepcopy(service_info),
+ loaded=True)
+ return service
+
+ @staticmethod
+ def create_services(attrs=None, count=2):
+ """Create multiple fake services.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :param int count:
+ The number of services to fake
+ :return:
+ A list of FakeResource objects faking the services
+ """
+ services = []
+ for i in range(0, count):
+ services.append(FakeService.create_one_service(attrs))
+
+ return services
+
+
+class FakeRole(object):
+ """Fake one or more role."""
+
+ @staticmethod
+ def create_one_role(attrs=None):
+ """Create a fake role.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ A FakeResource object, with id, name, and so on
+ """
+
+ attrs = attrs or {}
+
+ # set default attributes.
+ role_info = {
+ 'id': 'role-id' + uuid.uuid4().hex,
+ 'name': 'role-name' + uuid.uuid4().hex,
+ }
+ role_info.update(attrs)
+
+ role = fakes.FakeResource(info=copy.deepcopy(role_info),
+ loaded=True)
+ return role
+
+ @staticmethod
+ def create_roles(attrs=None, count=2):
+ """Create multiple fake roles.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :param int count:
+ The number of roles to fake
+ :return:
+ A list of FakeResource objects faking the roles
+ """
+ roles = []
+ for i in range(0, count):
+ roles.append(FakeRole.create_one_role(attrs))
+
+ return roles
+
+
+class FakeUser(object):
+ """Fake one or more user."""
+
+ @staticmethod
+ def create_one_user(attrs=None):
+ """Create a fake user.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ A FakeResource object, with id, name, and so on
+ """
+ attrs = attrs or {}
+
+ # set default attributes.
+ user_info = {
+ 'id': 'user-id-' + uuid.uuid4().hex,
+ 'name': 'user-name-' + uuid.uuid4().hex,
+ 'tenantId': 'project-id-' + uuid.uuid4().hex,
+ 'email': 'admin@openstack.org',
+ 'enabled': True,
+ }
+ user_info.update(attrs)
+
+ user = fakes.FakeResource(info=copy.deepcopy(user_info),
+ loaded=True)
+ return user
+
+ @staticmethod
+ def create_users(attrs=None, count=2):
+ """Create multiple fake users.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :param int count:
+ The number of users to fake
+ :return:
+ A list of FakeResource objects faking the users
+ """
+ users = []
+ for i in range(0, count):
+ users.append(FakeUser.create_one_user(attrs))
+
+ return users
diff --git a/openstackclient/tests/identity/v2_0/test_catalog.py b/openstackclient/tests/identity/v2_0/test_catalog.py
index d9ae6a80..487d8f31 100644
--- a/openstackclient/tests/identity/v2_0/test_catalog.py
+++ b/openstackclient/tests/identity/v2_0/test_catalog.py
@@ -14,43 +14,20 @@
import mock
from openstackclient.identity.v2_0 import catalog
+from openstackclient.tests.identity.v2_0 import fakes as identity_fakes
from openstackclient.tests import utils
class TestCatalog(utils.TestCommand):
- fake_service = {
- 'id': 'qwertyuiop',
- 'type': 'compute',
- 'name': 'supernova',
- 'endpoints': [
- {
- 'region': 'one',
- 'publicURL': 'https://public.one.example.com',
- 'internalURL': 'https://internal.one.example.com',
- 'adminURL': 'https://admin.one.example.com',
- },
- {
- 'region': 'two',
- 'publicURL': 'https://public.two.example.com',
- 'internalURL': 'https://internal.two.example.com',
- 'adminURL': 'https://admin.two.example.com',
- },
- {
- 'region': None,
- 'publicURL': 'https://public.none.example.com',
- 'internalURL': 'https://internal.none.example.com',
- 'adminURL': 'https://admin.none.example.com',
- },
- ],
- }
+ service_catalog = identity_fakes.FakeCatalog.create_catalog()
def setUp(self):
super(TestCatalog, self).setUp()
self.sc_mock = mock.MagicMock()
- self.sc_mock.service_catalog.get_data.return_value = [
- self.fake_service,
+ self.sc_mock.service_catalog.catalog.return_value = [
+ self.service_catalog,
]
self.auth_mock = mock.MagicMock()
@@ -74,6 +51,13 @@ class TestCatalogList(TestCatalog):
self.cmd = catalog.ListCatalog(self.app, None)
def test_catalog_list(self):
+ auth_ref = identity_fakes.fake_auth_ref(
+ identity_fakes.TOKEN,
+ fake_service=self.service_catalog,
+ )
+ self.ar_mock = mock.PropertyMock(return_value=auth_ref)
+ type(self.app.client_manager).auth_ref = self.ar_mock
+
arglist = []
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -82,7 +66,6 @@ class TestCatalogList(TestCatalog):
# returns a tuple containing the column names and an iterable
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
- self.sc_mock.service_catalog.get_data.assert_called_with()
self.assertEqual(self.columns, columns)
datalist = ((
@@ -101,7 +84,7 @@ class TestCatalogList(TestCatalog):
self.assertEqual(datalist, tuple(data))
def test_catalog_list_with_endpoint_url(self):
- fake_service = {
+ attr = {
'id': 'qwertyuiop',
'type': 'compute',
'name': 'supernova',
@@ -117,9 +100,13 @@ class TestCatalogList(TestCatalog):
},
],
}
- self.sc_mock.service_catalog.get_data.return_value = [
- fake_service,
- ]
+ service_catalog = identity_fakes.FakeCatalog.create_catalog(attr)
+ auth_ref = identity_fakes.fake_auth_ref(
+ identity_fakes.TOKEN,
+ fake_service=service_catalog,
+ )
+ self.ar_mock = mock.PropertyMock(return_value=auth_ref)
+ type(self.app.client_manager).auth_ref = self.ar_mock
arglist = []
verifylist = []
@@ -129,7 +116,6 @@ class TestCatalogList(TestCatalog):
# returns a tuple containing the column names and an iterable
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
- self.sc_mock.service_catalog.get_data.assert_called_with()
self.assertEqual(self.columns, columns)
datalist = ((
@@ -151,6 +137,13 @@ class TestCatalogShow(TestCatalog):
self.cmd = catalog.ShowCatalog(self.app, None)
def test_catalog_show(self):
+ auth_ref = identity_fakes.fake_auth_ref(
+ identity_fakes.UNSCOPED_TOKEN,
+ fake_service=self.service_catalog,
+ )
+ self.ar_mock = mock.PropertyMock(return_value=auth_ref)
+ type(self.app.client_manager).auth_ref = self.ar_mock
+
arglist = [
'compute',
]
@@ -163,7 +156,6 @@ class TestCatalogShow(TestCatalog):
# returns a two-part tuple with a tuple of column names and a tuple of
# data to be shown.
columns, data = self.cmd.take_action(parsed_args)
- self.sc_mock.service_catalog.get_data.assert_called_with()
collist = ('endpoints', 'id', 'name', 'type')
self.assertEqual(collist, columns)
@@ -177,7 +169,7 @@ class TestCatalogShow(TestCatalog):
'<none>\n publicURL: https://public.none.example.com\n '
'internalURL: https://internal.none.example.com\n '
'adminURL: https://admin.none.example.com\n',
- 'qwertyuiop',
+ self.service_catalog.id,
'supernova',
'compute',
)
diff --git a/openstackclient/tests/identity/v2_0/test_endpoint.py b/openstackclient/tests/identity/v2_0/test_endpoint.py
index 45ece45a..b2b6d0f1 100644
--- a/openstackclient/tests/identity/v2_0/test_endpoint.py
+++ b/openstackclient/tests/identity/v2_0/test_endpoint.py
@@ -11,15 +11,19 @@
# under the License.
#
-import copy
-
from openstackclient.identity.v2_0 import endpoint
-from openstackclient.tests import fakes
from openstackclient.tests.identity.v2_0 import fakes as identity_fakes
class TestEndpoint(identity_fakes.TestIdentityv2):
+ fake_service = identity_fakes.FakeService.create_one_service()
+ attr = {
+ 'service_name': fake_service.name,
+ 'service_id': fake_service.id,
+ }
+ fake_endpoint = identity_fakes.FakeEndpoint.create_one_endpoint(attr)
+
def setUp(self):
super(TestEndpoint, self).setUp()
@@ -37,35 +41,27 @@ class TestEndpointCreate(TestEndpoint):
def setUp(self):
super(TestEndpointCreate, self).setUp()
- self.endpoints_mock.create.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.ENDPOINT),
- loaded=True,
- )
+ self.endpoints_mock.create.return_value = self.fake_endpoint
- self.services_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.SERVICE),
- loaded=True,
- )
+ self.services_mock.get.return_value = self.fake_service
# Get the command object to test
self.cmd = endpoint.CreateEndpoint(self.app, None)
def test_endpoint_create(self):
arglist = [
- '--publicurl', identity_fakes.endpoint_publicurl,
- '--internalurl', identity_fakes.endpoint_internalurl,
- '--adminurl', identity_fakes.endpoint_adminurl,
- '--region', identity_fakes.endpoint_region,
- identity_fakes.endpoint_name,
+ '--publicurl', self.fake_endpoint.publicurl,
+ '--internalurl', self.fake_endpoint.internalurl,
+ '--adminurl', self.fake_endpoint.adminurl,
+ '--region', self.fake_endpoint.region,
+ self.fake_service.id,
]
verifylist = [
- ('adminurl', identity_fakes.endpoint_adminurl),
- ('internalurl', identity_fakes.endpoint_internalurl),
- ('publicurl', identity_fakes.endpoint_publicurl),
- ('region', identity_fakes.endpoint_region),
- ('service', identity_fakes.service_name),
+ ('adminurl', self.fake_endpoint.adminurl),
+ ('internalurl', self.fake_endpoint.internalurl),
+ ('publicurl', self.fake_endpoint.publicurl),
+ ('region', self.fake_endpoint.region),
+ ('service', self.fake_service.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -77,25 +73,25 @@ class TestEndpointCreate(TestEndpoint):
# EndpointManager.create(region, service_id, publicurl, adminurl,
# internalurl)
self.endpoints_mock.create.assert_called_with(
- identity_fakes.endpoint_region,
- identity_fakes.service_id,
- identity_fakes.endpoint_publicurl,
- identity_fakes.endpoint_adminurl,
- identity_fakes.endpoint_internalurl,
+ self.fake_endpoint.region,
+ self.fake_service.id,
+ self.fake_endpoint.publicurl,
+ self.fake_endpoint.adminurl,
+ self.fake_endpoint.internalurl,
)
collist = ('adminurl', 'id', 'internalurl', 'publicurl',
'region', 'service_id', 'service_name', 'service_type')
self.assertEqual(collist, columns)
datalist = (
- identity_fakes.endpoint_adminurl,
- identity_fakes.endpoint_id,
- identity_fakes.endpoint_internalurl,
- identity_fakes.endpoint_publicurl,
- identity_fakes.endpoint_region,
- identity_fakes.service_id,
- identity_fakes.service_name,
- identity_fakes.service_type,
+ self.fake_endpoint.adminurl,
+ self.fake_endpoint.id,
+ self.fake_endpoint.internalurl,
+ self.fake_endpoint.publicurl,
+ self.fake_endpoint.region,
+ self.fake_endpoint.service_id,
+ self.fake_endpoint.service_name,
+ self.fake_endpoint.service_type,
)
self.assertEqual(datalist, data)
@@ -106,17 +102,9 @@ class TestEndpointDelete(TestEndpoint):
def setUp(self):
super(TestEndpointDelete, self).setUp()
- self.endpoints_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.ENDPOINT),
- loaded=True,
- )
+ self.endpoints_mock.get.return_value = self.fake_endpoint
- self.services_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.SERVICE),
- loaded=True,
- )
+ self.services_mock.get.return_value = self.fake_service
self.endpoints_mock.delete.return_value = None
@@ -125,17 +113,17 @@ class TestEndpointDelete(TestEndpoint):
def test_endpoint_delete_no_options(self):
arglist = [
- identity_fakes.endpoint_id,
+ self.fake_endpoint.id,
]
verifylist = [
- ('endpoint', identity_fakes.endpoint_id),
+ ('endpoints', [self.fake_endpoint.id]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.endpoints_mock.delete.assert_called_with(
- identity_fakes.endpoint_id,
+ self.fake_endpoint.id,
)
self.assertIsNone(result)
@@ -145,19 +133,9 @@ class TestEndpointList(TestEndpoint):
def setUp(self):
super(TestEndpointList, self).setUp()
- self.endpoints_mock.list.return_value = [
- fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.ENDPOINT),
- loaded=True,
- ),
- ]
+ self.endpoints_mock.list.return_value = [self.fake_endpoint]
- self.services_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.SERVICE),
- loaded=True,
- )
+ self.services_mock.get.return_value = self.fake_service
# Get the command object to test
self.cmd = endpoint.ListEndpoint(self.app, None)
@@ -177,10 +155,10 @@ class TestEndpointList(TestEndpoint):
collist = ('ID', 'Region', 'Service Name', 'Service Type')
self.assertEqual(collist, columns)
datalist = ((
- identity_fakes.endpoint_id,
- identity_fakes.endpoint_region,
- identity_fakes.service_name,
- identity_fakes.service_type,
+ self.fake_endpoint.id,
+ self.fake_endpoint.region,
+ self.fake_endpoint.service_name,
+ self.fake_endpoint.service_type,
), )
self.assertEqual(datalist, tuple(data))
@@ -204,13 +182,13 @@ class TestEndpointList(TestEndpoint):
'PublicURL', 'AdminURL', 'InternalURL')
self.assertEqual(collist, columns)
datalist = ((
- identity_fakes.endpoint_id,
- identity_fakes.endpoint_region,
- identity_fakes.service_name,
- identity_fakes.service_type,
- identity_fakes.endpoint_publicurl,
- identity_fakes.endpoint_adminurl,
- identity_fakes.endpoint_internalurl,
+ self.fake_endpoint.id,
+ self.fake_endpoint.region,
+ self.fake_endpoint.service_name,
+ self.fake_endpoint.service_type,
+ self.fake_endpoint.publicurl,
+ self.fake_endpoint.adminurl,
+ self.fake_endpoint.internalurl,
), )
self.assertEqual(datalist, tuple(data))
@@ -220,29 +198,19 @@ class TestEndpointShow(TestEndpoint):
def setUp(self):
super(TestEndpointShow, self).setUp()
- self.endpoints_mock.list.return_value = [
- fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.ENDPOINT),
- loaded=True,
- ),
- ]
+ self.endpoints_mock.list.return_value = [self.fake_endpoint]
- self.services_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.SERVICE),
- loaded=True,
- )
+ self.services_mock.get.return_value = self.fake_service
# Get the command object to test
self.cmd = endpoint.ShowEndpoint(self.app, None)
def test_endpoint_show(self):
arglist = [
- identity_fakes.endpoint_name,
+ self.fake_endpoint.id,
]
verifylist = [
- ('endpoint_or_service', identity_fakes.endpoint_name),
+ ('endpoint_or_service', self.fake_endpoint.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -255,20 +223,20 @@ class TestEndpointShow(TestEndpoint):
self.endpoints_mock.list.assert_called_with()
# ServiceManager.get(name)
self.services_mock.get.assert_called_with(
- identity_fakes.service_name,
+ self.fake_endpoint.service_id,
)
collist = ('adminurl', 'id', 'internalurl', 'publicurl',
'region', 'service_id', 'service_name', 'service_type')
self.assertEqual(collist, columns)
datalist = (
- identity_fakes.endpoint_adminurl,
- identity_fakes.endpoint_id,
- identity_fakes.endpoint_internalurl,
- identity_fakes.endpoint_publicurl,
- identity_fakes.endpoint_region,
- identity_fakes.service_id,
- identity_fakes.service_name,
- identity_fakes.service_type,
+ self.fake_endpoint.adminurl,
+ self.fake_endpoint.id,
+ self.fake_endpoint.internalurl,
+ self.fake_endpoint.publicurl,
+ self.fake_endpoint.region,
+ self.fake_endpoint.service_id,
+ self.fake_endpoint.service_name,
+ self.fake_endpoint.service_type,
)
self.assertEqual(datalist, data)
diff --git a/openstackclient/tests/identity/v2_0/test_project.py b/openstackclient/tests/identity/v2_0/test_project.py
index 38684aaf..96731c0c 100644
--- a/openstackclient/tests/identity/v2_0/test_project.py
+++ b/openstackclient/tests/identity/v2_0/test_project.py
@@ -13,26 +13,16 @@
# under the License.
#
-import copy
-
from keystoneauth1 import exceptions as ks_exc
+from osc_lib import exceptions
from openstackclient.identity.v2_0 import project
-from openstackclient.tests import fakes
from openstackclient.tests.identity.v2_0 import fakes as identity_fakes
class TestProject(identity_fakes.TestIdentityv2):
- def setUp(self):
- super(TestProject, self).setUp()
-
- # Get a shortcut to the TenantManager Mock
- self.projects_mock = self.app.client_manager.identity.tenants
- self.projects_mock.reset_mock()
-
-
-class TestProjectCreate(TestProject):
+ fake_project = identity_fakes.FakeProject.create_one_project()
columns = (
'description',
@@ -41,32 +31,38 @@ class TestProjectCreate(TestProject):
'name',
)
datalist = (
- identity_fakes.project_description,
+ fake_project.description,
True,
- identity_fakes.project_id,
- identity_fakes.project_name,
+ fake_project.id,
+ fake_project.name,
)
def setUp(self):
+ super(TestProject, self).setUp()
+
+ # Get a shortcut to the TenantManager Mock
+ self.projects_mock = self.app.client_manager.identity.tenants
+ self.projects_mock.reset_mock()
+
+
+class TestProjectCreate(TestProject):
+
+ def setUp(self):
super(TestProjectCreate, self).setUp()
- self.projects_mock.create.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.PROJECT),
- loaded=True,
- )
+ self.projects_mock.create.return_value = self.fake_project
# Get the command object to test
self.cmd = project.CreateProject(self.app, None)
def test_project_create_no_options(self):
arglist = [
- identity_fakes.project_name,
+ self.fake_project.name,
]
verifylist = [
('enable', False),
('disable', False),
- ('name', identity_fakes.project_name),
+ ('name', self.fake_project.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -81,7 +77,7 @@ class TestProjectCreate(TestProject):
'enabled': True,
}
self.projects_mock.create.assert_called_with(
- identity_fakes.project_name,
+ self.fake_project.name,
**kwargs
)
self.assertEqual(self.columns, columns)
@@ -90,11 +86,11 @@ class TestProjectCreate(TestProject):
def test_project_create_description(self):
arglist = [
'--description', 'new desc',
- identity_fakes.project_name,
+ self.fake_project.name,
]
verifylist = [
('description', 'new desc'),
- ('name', identity_fakes.project_name),
+ ('name', self.fake_project.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -109,7 +105,7 @@ class TestProjectCreate(TestProject):
'enabled': True,
}
self.projects_mock.create.assert_called_with(
- identity_fakes.project_name,
+ self.fake_project.name,
**kwargs
)
@@ -119,12 +115,12 @@ class TestProjectCreate(TestProject):
def test_project_create_enable(self):
arglist = [
'--enable',
- identity_fakes.project_name,
+ self.fake_project.name,
]
verifylist = [
('enable', True),
('disable', False),
- ('name', identity_fakes.project_name),
+ ('name', self.fake_project.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -139,7 +135,7 @@ class TestProjectCreate(TestProject):
'enabled': True,
}
self.projects_mock.create.assert_called_with(
- identity_fakes.project_name,
+ self.fake_project.name,
**kwargs
)
@@ -149,12 +145,12 @@ class TestProjectCreate(TestProject):
def test_project_create_disable(self):
arglist = [
'--disable',
- identity_fakes.project_name,
+ self.fake_project.name,
]
verifylist = [
('enable', False),
('disable', True),
- ('name', identity_fakes.project_name),
+ ('name', self.fake_project.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -169,7 +165,7 @@ class TestProjectCreate(TestProject):
'enabled': False,
}
self.projects_mock.create.assert_called_with(
- identity_fakes.project_name,
+ self.fake_project.name,
**kwargs
)
@@ -180,11 +176,11 @@ class TestProjectCreate(TestProject):
arglist = [
'--property', 'fee=fi',
'--property', 'fo=fum',
- identity_fakes.project_name,
+ self.fake_project.name,
]
verifylist = [
('property', {'fee': 'fi', 'fo': 'fum'}),
- ('name', identity_fakes.project_name),
+ ('name', self.fake_project.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -201,7 +197,7 @@ class TestProjectCreate(TestProject):
'fo': 'fum',
}
self.projects_mock.create.assert_called_with(
- identity_fakes.project_name,
+ self.fake_project.name,
**kwargs
)
@@ -215,19 +211,15 @@ class TestProjectCreate(TestProject):
# need to make this throw an exception...
self.projects_mock.create.side_effect = _raise_conflict
- self.projects_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.PROJECT),
- loaded=True,
- )
+ self.projects_mock.get.return_value = self.fake_project
arglist = [
'--or-show',
- identity_fakes.project_name,
+ self.fake_project.name,
]
verifylist = [
- ('name', identity_fakes.project_name),
('or_show', True),
+ ('name', self.fake_project.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -237,7 +229,7 @@ class TestProjectCreate(TestProject):
columns, data = self.cmd.take_action(parsed_args)
# ProjectManager.create(name, description, enabled)
- self.projects_mock.get.assert_called_with(identity_fakes.project_name)
+ self.projects_mock.get.assert_called_with(self.fake_project.name)
# Set expected values
kwargs = {
@@ -245,7 +237,7 @@ class TestProjectCreate(TestProject):
'enabled': True,
}
self.projects_mock.create.assert_called_with(
- identity_fakes.project_name,
+ self.fake_project.name,
**kwargs
)
@@ -255,11 +247,11 @@ class TestProjectCreate(TestProject):
def test_project_create_or_show_not_exists(self):
arglist = [
'--or-show',
- identity_fakes.project_name,
+ self.fake_project.name,
]
verifylist = [
- ('name', identity_fakes.project_name),
('or_show', True),
+ ('name', self.fake_project.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -274,7 +266,7 @@ class TestProjectCreate(TestProject):
'enabled': True,
}
self.projects_mock.create.assert_called_with(
- identity_fakes.project_name,
+ self.fake_project.name,
**kwargs
)
@@ -288,11 +280,7 @@ class TestProjectDelete(TestProject):
super(TestProjectDelete, self).setUp()
# This is the return value for utils.find_resource()
- self.projects_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.PROJECT),
- loaded=True,
- )
+ self.projects_mock.get.return_value = self.fake_project
self.projects_mock.delete.return_value = None
# Get the command object to test
@@ -300,17 +288,17 @@ class TestProjectDelete(TestProject):
def test_project_delete_no_options(self):
arglist = [
- identity_fakes.project_id,
+ self.fake_project.id,
]
verifylist = [
- ('projects', [identity_fakes.project_id]),
+ ('projects', [self.fake_project.id]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.projects_mock.delete.assert_called_with(
- identity_fakes.project_id,
+ self.fake_project.id,
)
self.assertIsNone(result)
@@ -320,13 +308,7 @@ class TestProjectList(TestProject):
def setUp(self):
super(TestProjectList, self).setUp()
- self.projects_mock.list.return_value = [
- fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.PROJECT),
- loaded=True,
- ),
- ]
+ self.projects_mock.list.return_value = [self.fake_project]
# Get the command object to test
self.cmd = project.ListProject(self.app, None)
@@ -345,8 +327,8 @@ class TestProjectList(TestProject):
collist = ('ID', 'Name')
self.assertEqual(collist, columns)
datalist = ((
- identity_fakes.project_id,
- identity_fakes.project_name,
+ self.fake_project.id,
+ self.fake_project.name,
), )
self.assertEqual(datalist, tuple(data))
@@ -368,9 +350,9 @@ class TestProjectList(TestProject):
collist = ('ID', 'Name', 'Description', 'Enabled')
self.assertEqual(collist, columns)
datalist = ((
- identity_fakes.project_id,
- identity_fakes.project_name,
- identity_fakes.project_description,
+ self.fake_project.id,
+ self.fake_project.name,
+ self.fake_project.description,
True,
), )
self.assertEqual(datalist, tuple(data))
@@ -381,26 +363,18 @@ class TestProjectSet(TestProject):
def setUp(self):
super(TestProjectSet, self).setUp()
- self.projects_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.PROJECT),
- loaded=True,
- )
- self.projects_mock.update.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.PROJECT),
- loaded=True,
- )
+ self.projects_mock.get.return_value = self.fake_project
+ self.projects_mock.update.return_value = self.fake_project
# Get the command object to test
self.cmd = project.SetProject(self.app, None)
def test_project_set_no_options(self):
arglist = [
- identity_fakes.project_name,
+ self.fake_project.name,
]
verifylist = [
- ('project', identity_fakes.project_name),
+ ('project', self.fake_project.name),
('enable', False),
('disable', False),
]
@@ -410,16 +384,36 @@ class TestProjectSet(TestProject):
self.assertIsNone(result)
+ def test_project_set_unexist_project(self):
+ arglist = [
+ "unexist-project",
+ ]
+ verifylist = [
+ ('project', "unexist-project"),
+ ('name', None),
+ ('description', None),
+ ('enable', False),
+ ('disable', False),
+ ('property', None),
+ ]
+ self.projects_mock.get.side_effect = exceptions.NotFound(None)
+ self.projects_mock.find.side_effect = exceptions.NotFound(None)
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.assertRaises(
+ exceptions.CommandError, self.cmd.take_action, parsed_args)
+
def test_project_set_name(self):
arglist = [
- '--name', 'qwerty',
- identity_fakes.project_name,
+ '--name', self.fake_project.name,
+ self.fake_project.name,
]
verifylist = [
- ('name', 'qwerty'),
+ ('name', self.fake_project.name),
('enable', False),
('disable', False),
- ('project', identity_fakes.project_name),
+ ('project', self.fake_project.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -427,26 +421,26 @@ class TestProjectSet(TestProject):
# Set expected values
kwargs = {
- 'description': identity_fakes.project_description,
+ 'description': self.fake_project.description,
'enabled': True,
- 'tenant_name': 'qwerty',
+ 'tenant_name': self.fake_project.name,
}
self.projects_mock.update.assert_called_with(
- identity_fakes.project_id,
+ self.fake_project.id,
**kwargs
)
self.assertIsNone(result)
def test_project_set_description(self):
arglist = [
- '--description', 'new desc',
- identity_fakes.project_name,
+ '--description', self.fake_project.description,
+ self.fake_project.name,
]
verifylist = [
- ('description', 'new desc'),
+ ('description', self.fake_project.description),
('enable', False),
('disable', False),
- ('project', identity_fakes.project_name),
+ ('project', self.fake_project.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -454,12 +448,12 @@ class TestProjectSet(TestProject):
# Set expected values
kwargs = {
- 'description': 'new desc',
+ 'description': self.fake_project.description,
'enabled': True,
- 'tenant_name': identity_fakes.project_name,
+ 'tenant_name': self.fake_project.name,
}
self.projects_mock.update.assert_called_with(
- identity_fakes.project_id,
+ self.fake_project.id,
**kwargs
)
self.assertIsNone(result)
@@ -467,12 +461,12 @@ class TestProjectSet(TestProject):
def test_project_set_enable(self):
arglist = [
'--enable',
- identity_fakes.project_name,
+ self.fake_project.name,
]
verifylist = [
('enable', True),
('disable', False),
- ('project', identity_fakes.project_name),
+ ('project', self.fake_project.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -480,12 +474,12 @@ class TestProjectSet(TestProject):
# Set expected values
kwargs = {
- 'description': identity_fakes.project_description,
+ 'description': self.fake_project.description,
'enabled': True,
- 'tenant_name': identity_fakes.project_name,
+ 'tenant_name': self.fake_project.name,
}
self.projects_mock.update.assert_called_with(
- identity_fakes.project_id,
+ self.fake_project.id,
**kwargs
)
self.assertIsNone(result)
@@ -493,12 +487,12 @@ class TestProjectSet(TestProject):
def test_project_set_disable(self):
arglist = [
'--disable',
- identity_fakes.project_name,
+ self.fake_project.name,
]
verifylist = [
('enable', False),
('disable', True),
- ('project', identity_fakes.project_name),
+ ('project', self.fake_project.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -506,12 +500,12 @@ class TestProjectSet(TestProject):
# Set expected values
kwargs = {
- 'description': identity_fakes.project_description,
+ 'description': self.fake_project.description,
'enabled': False,
- 'tenant_name': identity_fakes.project_name,
+ 'tenant_name': self.fake_project.name,
}
self.projects_mock.update.assert_called_with(
- identity_fakes.project_id,
+ self.fake_project.id,
**kwargs
)
self.assertIsNone(result)
@@ -520,11 +514,11 @@ class TestProjectSet(TestProject):
arglist = [
'--property', 'fee=fi',
'--property', 'fo=fum',
- identity_fakes.project_name,
+ self.fake_project.name,
]
verifylist = [
('property', {'fee': 'fi', 'fo': 'fum'}),
- ('project', identity_fakes.project_name),
+ ('project', self.fake_project.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -532,14 +526,14 @@ class TestProjectSet(TestProject):
# Set expected values
kwargs = {
- 'description': identity_fakes.project_description,
+ 'description': self.fake_project.description,
'enabled': True,
- 'tenant_name': identity_fakes.project_name,
+ 'tenant_name': self.fake_project.name,
'fee': 'fi',
'fo': 'fum',
}
self.projects_mock.update.assert_called_with(
- identity_fakes.project_id,
+ self.fake_project.id,
**kwargs
)
self.assertIsNone(result)
@@ -547,24 +541,22 @@ class TestProjectSet(TestProject):
class TestProjectShow(TestProject):
+ fake_proj_show = identity_fakes.FakeProject.create_one_project()
+
def setUp(self):
super(TestProjectShow, self).setUp()
- self.projects_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.PROJECT),
- loaded=True,
- )
+ self.projects_mock.get.return_value = self.fake_proj_show
# Get the command object to test
self.cmd = project.ShowProject(self.app, None)
def test_project_show(self):
arglist = [
- identity_fakes.project_id,
+ self.fake_proj_show.id,
]
verifylist = [
- ('project', identity_fakes.project_id),
+ ('project', self.fake_proj_show.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -573,16 +565,16 @@ class TestProjectShow(TestProject):
# data to be shown.
columns, data = self.cmd.take_action(parsed_args)
self.projects_mock.get.assert_called_with(
- identity_fakes.project_id,
+ self.fake_proj_show.id,
)
collist = ('description', 'enabled', 'id', 'name', 'properties')
self.assertEqual(collist, columns)
datalist = (
- identity_fakes.project_description,
+ self.fake_proj_show.description,
True,
- identity_fakes.project_id,
- identity_fakes.project_name,
+ self.fake_proj_show.id,
+ self.fake_proj_show.name,
'',
)
self.assertEqual(datalist, data)
@@ -590,25 +582,35 @@ class TestProjectShow(TestProject):
class TestProjectUnset(TestProject):
+ attr = {'fee': 'fi', 'fo': 'fum'}
+ fake_proj = identity_fakes.FakeProject.create_one_project(attr)
+
def setUp(self):
super(TestProjectUnset, self).setUp()
- project_dict = {'fee': 'fi', 'fo': 'fum'}
- project_dict.update(identity_fakes.PROJECT)
- self.projects_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(project_dict),
- loaded=True,
- )
+ self.projects_mock.get.return_value = self.fake_proj
# Get the command object to test
self.cmd = project.UnsetProject(self.app, None)
+ def test_project_unset_no_options(self):
+ arglist = [
+ self.fake_proj.name,
+ ]
+ verifylist = [
+ ('project', self.fake_proj.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.assertIsNone(result)
+
def test_project_unset_key(self):
arglist = [
'--property', 'fee',
'--property', 'fo',
- identity_fakes.project_name,
+ self.fake_proj.name,
]
verifylist = [
('property', ['fee', 'fo']),
@@ -618,16 +620,16 @@ class TestProjectUnset(TestProject):
result = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
- 'description': identity_fakes.project_description,
+ 'description': self.fake_proj.description,
'enabled': True,
'fee': None,
'fo': None,
- 'id': identity_fakes.project_id,
- 'name': identity_fakes.project_name,
+ 'id': self.fake_proj.id,
+ 'name': self.fake_proj.name,
}
self.projects_mock.update.assert_called_with(
- identity_fakes.project_id,
+ self.fake_proj.id,
**kwargs
)
self.assertIsNone(result)
diff --git a/openstackclient/tests/identity/v2_0/test_role.py b/openstackclient/tests/identity/v2_0/test_role.py
index 3c4b79a4..3d379356 100644
--- a/openstackclient/tests/identity/v2_0/test_role.py
+++ b/openstackclient/tests/identity/v2_0/test_role.py
@@ -13,19 +13,32 @@
# under the License.
#
-import copy
import mock
from keystoneauth1 import exceptions as ks_exc
+from osc_lib import exceptions
-from openstackclient.common import exceptions
from openstackclient.identity.v2_0 import role
-from openstackclient.tests import fakes
from openstackclient.tests.identity.v2_0 import fakes as identity_fakes
class TestRole(identity_fakes.TestIdentityv2):
+ attr = {}
+ attr['endpoints'] = [
+ {
+ 'publicURL': identity_fakes.ENDPOINT['publicurl'],
+ },
+ ]
+ fake_service = identity_fakes.FakeService.create_one_service(attr)
+ fake_role = identity_fakes.FakeRole.create_one_role()
+ fake_project = identity_fakes.FakeProject.create_one_project()
+ attr = {}
+ attr = {
+ 'tenantId': fake_project.id,
+ }
+ fake_user = identity_fakes.FakeUser.create_one_user(attr)
+
def setUp(self):
super(TestRole, self).setUp()
@@ -41,48 +54,39 @@ class TestRole(identity_fakes.TestIdentityv2):
self.roles_mock = self.app.client_manager.identity.roles
self.roles_mock.reset_mock()
+ auth_ref = identity_fakes.fake_auth_ref(
+ identity_fakes.TOKEN,
+ fake_service=self.fake_service,
+ )
+ self.ar_mock = mock.PropertyMock(return_value=auth_ref)
+ type(self.app.client_manager).auth_ref = self.ar_mock
+
class TestRoleAdd(TestRole):
def setUp(self):
super(TestRoleAdd, self).setUp()
- self.projects_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.PROJECT),
- loaded=True,
- )
+ self.projects_mock.get.return_value = self.fake_project
- self.users_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.USER),
- loaded=True,
- )
+ self.users_mock.get.return_value = self.fake_user
- self.roles_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.ROLE),
- loaded=True,
- )
- self.roles_mock.add_user_role.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.ROLE),
- loaded=True,
- )
+ self.roles_mock.get.return_value = self.fake_role
+ self.roles_mock.add_user_role.return_value = self.fake_role
# Get the command object to test
self.cmd = role.AddRole(self.app, None)
def test_role_add(self):
arglist = [
- '--project', identity_fakes.project_name,
- '--user', identity_fakes.user_name,
- identity_fakes.role_name,
+ '--project', self.fake_project.name,
+ '--user', self.fake_user.name,
+ self.fake_role.name,
]
verifylist = [
- ('project', identity_fakes.project_name),
- ('user', identity_fakes.user_name),
- ('role', identity_fakes.role_name),
+ ('project', self.fake_project.name),
+ ('user', self.fake_user.name),
+ ('role', self.fake_role.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -93,49 +97,46 @@ class TestRoleAdd(TestRole):
# RoleManager.add_user_role(user, role, tenant=None)
self.roles_mock.add_user_role.assert_called_with(
- identity_fakes.user_id,
- identity_fakes.role_id,
- identity_fakes.project_id,
+ self.fake_user.id,
+ self.fake_role.id,
+ self.fake_project.id,
)
collist = ('id', 'name')
self.assertEqual(collist, columns)
datalist = (
- identity_fakes.role_id,
- identity_fakes.role_name,
+ self.fake_role.id,
+ self.fake_role.name,
)
self.assertEqual(datalist, data)
class TestRoleCreate(TestRole):
+ fake_role_c = identity_fakes.FakeRole.create_one_role()
columns = (
'id',
'name'
)
datalist = (
- identity_fakes.role_id,
- identity_fakes.role_name,
+ fake_role_c.id,
+ fake_role_c.name,
)
def setUp(self):
super(TestRoleCreate, self).setUp()
- self.roles_mock.create.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.ROLE),
- loaded=True,
- )
+ self.roles_mock.create.return_value = self.fake_role_c
# Get the command object to test
self.cmd = role.CreateRole(self.app, None)
def test_role_create_no_options(self):
arglist = [
- identity_fakes.role_name,
+ self.fake_role_c.name,
]
verifylist = [
- ('role_name', identity_fakes.role_name),
+ ('role_name', self.fake_role_c.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -146,7 +147,7 @@ class TestRoleCreate(TestRole):
# RoleManager.create(name)
self.roles_mock.create.assert_called_with(
- identity_fakes.role_name,
+ self.fake_role_c.name,
)
self.assertEqual(self.columns, columns)
@@ -159,18 +160,14 @@ class TestRoleCreate(TestRole):
# need to make this throw an exception...
self.roles_mock.create.side_effect = _raise_conflict
- self.roles_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.ROLE),
- loaded=True,
- )
+ self.roles_mock.get.return_value = self.fake_role_c
arglist = [
'--or-show',
- identity_fakes.role_name,
+ self.fake_role_c.name,
]
verifylist = [
- ('role_name', identity_fakes.role_name),
+ ('role_name', self.fake_role_c.name),
('or_show', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -181,11 +178,11 @@ class TestRoleCreate(TestRole):
columns, data = self.cmd.take_action(parsed_args)
# RoleManager.get(name, description, enabled)
- self.roles_mock.get.assert_called_with(identity_fakes.role_name)
+ self.roles_mock.get.assert_called_with(self.fake_role_c.name)
# RoleManager.create(name)
self.roles_mock.create.assert_called_with(
- identity_fakes.role_name,
+ self.fake_role_c.name,
)
self.assertEqual(self.columns, columns)
@@ -194,10 +191,10 @@ class TestRoleCreate(TestRole):
def test_role_create_or_show_not_exists(self):
arglist = [
'--or-show',
- identity_fakes.role_name,
+ self.fake_role_c.name,
]
verifylist = [
- ('role_name', identity_fakes.role_name),
+ ('role_name', self.fake_role_c.name),
('or_show', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -209,7 +206,7 @@ class TestRoleCreate(TestRole):
# RoleManager.create(name)
self.roles_mock.create.assert_called_with(
- identity_fakes.role_name,
+ self.fake_role_c.name,
)
self.assertEqual(self.columns, columns)
@@ -221,11 +218,7 @@ class TestRoleDelete(TestRole):
def setUp(self):
super(TestRoleDelete, self).setUp()
- self.roles_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.ROLE),
- loaded=True,
- )
+ self.roles_mock.get.return_value = self.fake_role
self.roles_mock.delete.return_value = None
# Get the command object to test
@@ -233,17 +226,17 @@ class TestRoleDelete(TestRole):
def test_role_delete_no_options(self):
arglist = [
- identity_fakes.role_name,
+ self.fake_role.name,
]
verifylist = [
- ('roles', [identity_fakes.role_name]),
+ ('roles', [self.fake_role.name]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.roles_mock.delete.assert_called_with(
- identity_fakes.role_id,
+ self.fake_role.id,
)
self.assertIsNone(result)
@@ -253,13 +246,7 @@ class TestRoleList(TestRole):
def setUp(self):
super(TestRoleList, self).setUp()
- self.roles_mock.list.return_value = [
- fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.ROLE),
- loaded=True,
- ),
- ]
+ self.roles_mock.list.return_value = [self.fake_role]
# Get the command object to test
self.cmd = role.ListRole(self.app, None)
@@ -279,8 +266,8 @@ class TestRoleList(TestRole):
collist = ('ID', 'Name')
self.assertEqual(collist, columns)
datalist = ((
- identity_fakes.role_id,
- identity_fakes.role_name,
+ self.fake_role.id,
+ self.fake_role.name,
), )
self.assertEqual(datalist, tuple(data))
@@ -297,30 +284,23 @@ class TestUserRoleList(TestRole):
def setUp(self):
super(TestUserRoleList, self).setUp()
- self.projects_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.PROJECT),
- loaded=True,
- )
+ self.projects_mock.get.return_value = self.fake_project
- self.users_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.USER),
- loaded=True,
- )
+ self.users_mock.get.return_value = self.fake_user
- self.roles_mock.roles_for_user.return_value = [
- fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.ROLE),
- loaded=True,
- ),
- ]
+ self.roles_mock.roles_for_user.return_value = [self.fake_role]
# Get the command object to test
self.cmd = role.ListUserRole(self.app, None)
- def test_user_role_list_no_options(self):
+ def test_user_role_list_no_options_unscoped_token(self):
+ auth_ref = identity_fakes.fake_auth_ref(
+ identity_fakes.UNSCOPED_TOKEN,
+ fake_service=self.fake_service,
+ )
+ self.ar_mock = mock.PropertyMock(return_value=auth_ref)
+ type(self.app.client_manager).auth_ref = self.ar_mock
+
arglist = []
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -332,11 +312,7 @@ class TestUserRoleList(TestRole):
parsed_args,
)
- def test_user_role_list_no_options_def_creds(self):
- auth_ref = self.app.client_manager.auth_ref = mock.MagicMock()
- auth_ref.project_id.return_value = identity_fakes.project_id
- auth_ref.user_id.return_value = identity_fakes.user_id
-
+ def test_user_role_list_no_options_scoped_token(self):
arglist = []
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -347,56 +323,63 @@ class TestUserRoleList(TestRole):
columns, data = self.cmd.take_action(parsed_args)
self.roles_mock.roles_for_user.assert_called_with(
- identity_fakes.user_id,
- identity_fakes.project_id,
+ self.fake_user.id,
+ self.fake_project.id,
)
collist = ('ID', 'Name', 'Project', 'User')
self.assertEqual(collist, columns)
datalist = ((
- identity_fakes.role_id,
- identity_fakes.role_name,
- identity_fakes.project_name,
- identity_fakes.user_name,
+ self.fake_role.id,
+ self.fake_role.name,
+ self.fake_project.name,
+ self.fake_user.name,
), )
self.assertEqual(datalist, tuple(data))
- def test_user_role_list_project(self):
- self.projects_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.PROJECT_2),
- loaded=True,
+ def test_user_role_list_project_unscoped_token(self):
+ auth_ref = identity_fakes.fake_auth_ref(
+ identity_fakes.UNSCOPED_TOKEN,
+ fake_service=self.fake_service,
)
+ self.ar_mock = mock.PropertyMock(return_value=auth_ref)
+ type(self.app.client_manager).auth_ref = self.ar_mock
+
+ self.projects_mock.get.return_value = self.fake_project
arglist = [
- '--project', identity_fakes.PROJECT_2['name'],
+ '--project', self.fake_project.name,
]
verifylist = [
- ('project', identity_fakes.PROJECT_2['name']),
+ ('project', self.fake_project.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- # This argument combination should raise a CommandError
- self.assertRaises(
- exceptions.CommandError,
- self.cmd.take_action,
- parsed_args,
+ # In base command class Lister in cliff, abstract method take_action()
+ # returns a tuple containing the column names and an iterable
+ # containing the data to be listed.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.roles_mock.roles_for_user.assert_called_with(
+ self.fake_user.id,
+ self.fake_project.id,
)
- def test_user_role_list_project_def_creds(self):
- auth_ref = self.app.client_manager.auth_ref = mock.MagicMock()
- auth_ref.project_id.return_value = identity_fakes.project_id
- auth_ref.user_id.return_value = identity_fakes.user_id
+ self.assertEqual(columns, columns)
+ datalist = ((
+ self.fake_role.id,
+ self.fake_role.name,
+ self.fake_project.name,
+ self.fake_user.name,
+ ), )
+ self.assertEqual(datalist, tuple(data))
- self.projects_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.PROJECT_2),
- loaded=True,
- )
+ def test_user_role_list_project_scoped_token(self):
+ self.projects_mock.get.return_value = self.fake_project
arglist = [
- '--project', identity_fakes.PROJECT_2['name'],
+ '--project', self.fake_project.name,
]
verifylist = [
- ('project', identity_fakes.PROJECT_2['name']),
+ ('project', self.fake_project.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -406,16 +389,16 @@ class TestUserRoleList(TestRole):
columns, data = self.cmd.take_action(parsed_args)
self.roles_mock.roles_for_user.assert_called_with(
- identity_fakes.user_id,
- identity_fakes.PROJECT_2['id'],
+ self.fake_user.id,
+ self.fake_project.id,
)
self.assertEqual(columns, columns)
datalist = ((
- identity_fakes.role_id,
- identity_fakes.role_name,
- identity_fakes.PROJECT_2['name'],
- identity_fakes.user_name,
+ self.fake_role.id,
+ self.fake_role.name,
+ self.fake_project.name,
+ self.fake_user.name,
), )
self.assertEqual(datalist, tuple(data))
@@ -425,23 +408,11 @@ class TestRoleRemove(TestRole):
def setUp(self):
super(TestRoleRemove, self).setUp()
- self.projects_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.PROJECT),
- loaded=True,
- )
+ self.projects_mock.get.return_value = self.fake_project
- self.users_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.USER),
- loaded=True,
- )
+ self.users_mock.get.return_value = self.fake_user
- self.roles_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.ROLE),
- loaded=True,
- )
+ self.roles_mock.get.return_value = self.fake_role
self.roles_mock.remove_user_role.return_value = None
# Get the command object to test
@@ -449,14 +420,14 @@ class TestRoleRemove(TestRole):
def test_role_remove(self):
arglist = [
- '--project', identity_fakes.project_name,
- '--user', identity_fakes.user_name,
- identity_fakes.role_name,
+ '--project', self.fake_project.name,
+ '--user', self.fake_user.name,
+ self.fake_role.name,
]
verifylist = [
- ('role', identity_fakes.role_name),
- ('project', identity_fakes.project_name),
- ('user', identity_fakes.user_name),
+ ('role', self.fake_role.name),
+ ('project', self.fake_project.name),
+ ('user', self.fake_user.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -464,9 +435,9 @@ class TestRoleRemove(TestRole):
# RoleManager.remove_user_role(user, role, tenant=None)
self.roles_mock.remove_user_role.assert_called_with(
- identity_fakes.user_id,
- identity_fakes.role_id,
- identity_fakes.project_id,
+ self.fake_user.id,
+ self.fake_role.id,
+ self.fake_project.id,
)
self.assertIsNone(result)
@@ -476,21 +447,17 @@ class TestRoleShow(TestRole):
def setUp(self):
super(TestRoleShow, self).setUp()
- self.roles_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.ROLE),
- loaded=True,
- )
+ self.roles_mock.get.return_value = self.fake_role
# Get the command object to test
self.cmd = role.ShowRole(self.app, None)
def test_service_show(self):
arglist = [
- identity_fakes.role_name,
+ self.fake_role.name,
]
verifylist = [
- ('role', identity_fakes.role_name),
+ ('role', self.fake_role.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -501,13 +468,13 @@ class TestRoleShow(TestRole):
# RoleManager.get(role)
self.roles_mock.get.assert_called_with(
- identity_fakes.role_name,
+ self.fake_role.name,
)
collist = ('id', 'name')
self.assertEqual(collist, columns)
datalist = (
- identity_fakes.role_id,
- identity_fakes.role_name,
+ self.fake_role.id,
+ self.fake_role.name,
)
self.assertEqual(datalist, data)
diff --git a/openstackclient/tests/identity/v2_0/test_service.py b/openstackclient/tests/identity/v2_0/test_service.py
index dc0fbcd1..318fa83d 100644
--- a/openstackclient/tests/identity/v2_0/test_service.py
+++ b/openstackclient/tests/identity/v2_0/test_service.py
@@ -13,14 +13,12 @@
# under the License.
#
-import copy
-
from openstackclient.identity.v2_0 import service
-from openstackclient.tests import fakes
from openstackclient.tests.identity.v2_0 import fakes as identity_fakes
class TestService(identity_fakes.TestIdentityv2):
+ fake_service = identity_fakes.FakeService.create_one_service()
def setUp(self):
super(TestService, self).setUp()
@@ -32,6 +30,7 @@ class TestService(identity_fakes.TestIdentityv2):
class TestServiceCreate(TestService):
+ fake_service_c = identity_fakes.FakeService.create_one_service()
columns = (
'description',
'id',
@@ -39,30 +38,26 @@ class TestServiceCreate(TestService):
'type',
)
datalist = (
- identity_fakes.service_description,
- identity_fakes.service_id,
- identity_fakes.service_name,
- identity_fakes.service_type,
+ fake_service_c.description,
+ fake_service_c.id,
+ fake_service_c.name,
+ fake_service_c.type,
)
def setUp(self):
super(TestServiceCreate, self).setUp()
- self.services_mock.create.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.SERVICE),
- loaded=True,
- )
+ self.services_mock.create.return_value = self.fake_service_c
# Get the command object to test
self.cmd = service.CreateService(self.app, None)
def test_service_create_with_type_positional(self):
arglist = [
- identity_fakes.service_type,
+ self.fake_service_c.type,
]
verifylist = [
- ('type_or_name', identity_fakes.service_type),
+ ('type_or_name', self.fake_service_c.type),
('type', None),
('description', None),
('name', None),
@@ -77,7 +72,7 @@ class TestServiceCreate(TestService):
# ServiceManager.create(name, service_type, description)
self.services_mock.create.assert_called_with(
None,
- identity_fakes.service_type,
+ self.fake_service_c.type,
None,
)
@@ -86,12 +81,12 @@ class TestServiceCreate(TestService):
def test_service_create_with_type_option(self):
arglist = [
- '--type', identity_fakes.service_type,
- identity_fakes.service_name,
+ '--type', self.fake_service_c.type,
+ self.fake_service_c.name,
]
verifylist = [
- ('type_or_name', identity_fakes.service_name),
- ('type', identity_fakes.service_type),
+ ('type_or_name', self.fake_service_c.name),
+ ('type', self.fake_service_c.type),
('description', None),
('name', None),
]
@@ -104,8 +99,8 @@ class TestServiceCreate(TestService):
# ServiceManager.create(name, service_type, description)
self.services_mock.create.assert_called_with(
- identity_fakes.service_name,
- identity_fakes.service_type,
+ self.fake_service_c.name,
+ self.fake_service_c.type,
None,
)
@@ -114,14 +109,14 @@ class TestServiceCreate(TestService):
def test_service_create_with_name_option(self):
arglist = [
- '--name', identity_fakes.service_name,
- identity_fakes.service_type,
+ '--name', self.fake_service_c.name,
+ self.fake_service_c.type,
]
verifylist = [
- ('type_or_name', identity_fakes.service_type),
+ ('type_or_name', self.fake_service_c.type),
('type', None),
('description', None),
- ('name', identity_fakes.service_name),
+ ('name', self.fake_service_c.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -132,8 +127,8 @@ class TestServiceCreate(TestService):
# ServiceManager.create(name, service_type, description)
self.services_mock.create.assert_called_with(
- identity_fakes.service_name,
- identity_fakes.service_type,
+ self.fake_service_c.name,
+ self.fake_service_c.type,
None,
)
@@ -142,15 +137,15 @@ class TestServiceCreate(TestService):
def test_service_create_description(self):
arglist = [
- '--name', identity_fakes.service_name,
- '--description', identity_fakes.service_description,
- identity_fakes.service_type,
+ '--name', self.fake_service_c.name,
+ '--description', self.fake_service_c.description,
+ self.fake_service_c.type,
]
verifylist = [
- ('type_or_name', identity_fakes.service_type),
+ ('type_or_name', self.fake_service_c.type),
('type', None),
- ('description', identity_fakes.service_description),
- ('name', identity_fakes.service_name),
+ ('description', self.fake_service_c.description),
+ ('name', self.fake_service_c.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -161,9 +156,9 @@ class TestServiceCreate(TestService):
# ServiceManager.create(name, service_type, description)
self.services_mock.create.assert_called_with(
- identity_fakes.service_name,
- identity_fakes.service_type,
- identity_fakes.service_description,
+ self.fake_service_c.name,
+ self.fake_service_c.type,
+ self.fake_service_c.description,
)
self.assertEqual(self.columns, columns)
@@ -175,11 +170,7 @@ class TestServiceDelete(TestService):
def setUp(self):
super(TestServiceDelete, self).setUp()
- self.services_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.SERVICE),
- loaded=True,
- )
+ self.services_mock.get.return_value = self.fake_service
self.services_mock.delete.return_value = None
# Get the command object to test
@@ -187,17 +178,17 @@ class TestServiceDelete(TestService):
def test_service_delete_no_options(self):
arglist = [
- identity_fakes.service_name,
+ self.fake_service.name,
]
verifylist = [
- ('service', identity_fakes.service_name),
+ ('services', [self.fake_service.name]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.services_mock.delete.assert_called_with(
- identity_fakes.service_id,
+ self.fake_service.id,
)
self.assertIsNone(result)
@@ -207,13 +198,7 @@ class TestServiceList(TestService):
def setUp(self):
super(TestServiceList, self).setUp()
- self.services_mock.list.return_value = [
- fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.SERVICE),
- loaded=True,
- ),
- ]
+ self.services_mock.list.return_value = [self.fake_service]
# Get the command object to test
self.cmd = service.ListService(self.app, None)
@@ -233,9 +218,9 @@ class TestServiceList(TestService):
collist = ('ID', 'Name', 'Type')
self.assertEqual(collist, columns)
datalist = ((
- identity_fakes.service_id,
- identity_fakes.service_name,
- identity_fakes.service_type,
+ self.fake_service.id,
+ self.fake_service.name,
+ self.fake_service.type,
), )
self.assertEqual(datalist, tuple(data))
@@ -258,10 +243,10 @@ class TestServiceList(TestService):
collist = ('ID', 'Name', 'Type', 'Description')
self.assertEqual(collist, columns)
datalist = ((
- identity_fakes.service_id,
- identity_fakes.service_name,
- identity_fakes.service_type,
- identity_fakes.service_description,
+ self.fake_service.id,
+ self.fake_service.name,
+ self.fake_service.type,
+ self.fake_service.description,
), )
self.assertEqual(datalist, tuple(data))
@@ -271,21 +256,17 @@ class TestServiceShow(TestService):
def setUp(self):
super(TestServiceShow, self).setUp()
- self.services_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.SERVICE),
- loaded=True,
- )
+ self.services_mock.get.return_value = self.fake_service
# Get the command object to test
self.cmd = service.ShowService(self.app, None)
def test_service_show(self):
arglist = [
- identity_fakes.service_name,
+ self.fake_service.name,
]
verifylist = [
- ('service', identity_fakes.service_name),
+ ('service', self.fake_service.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -296,15 +277,15 @@ class TestServiceShow(TestService):
# ServiceManager.get(id)
self.services_mock.get.assert_called_with(
- identity_fakes.service_name,
+ self.fake_service.name,
)
collist = ('description', 'id', 'name', 'type')
self.assertEqual(collist, columns)
datalist = (
- identity_fakes.service_description,
- identity_fakes.service_id,
- identity_fakes.service_name,
- identity_fakes.service_type,
+ self.fake_service.description,
+ self.fake_service.id,
+ self.fake_service.name,
+ self.fake_service.type,
)
self.assertEqual(datalist, data)
diff --git a/openstackclient/tests/identity/v2_0/test_token.py b/openstackclient/tests/identity/v2_0/test_token.py
index 613139dd..bb776707 100644
--- a/openstackclient/tests/identity/v2_0/test_token.py
+++ b/openstackclient/tests/identity/v2_0/test_token.py
@@ -21,13 +21,15 @@ from openstackclient.tests.identity.v2_0 import fakes as identity_fakes
class TestToken(identity_fakes.TestIdentityv2):
+ fake_user = identity_fakes.FakeUser.create_one_user()
+ fake_project = identity_fakes.FakeProject.create_one_project()
+
def setUp(self):
super(TestToken, self).setUp()
- # Get a shortcut to the Service Catalog Mock
- self.sc_mock = mock.Mock()
- self.app.client_manager.auth_ref = mock.Mock()
- self.app.client_manager.auth_ref.service_catalog = self.sc_mock
+ # Get a shortcut to the Auth Ref Mock
+ self.ar_mock = mock.PropertyMock()
+ type(self.app.client_manager).auth_ref = self.ar_mock
class TestTokenIssue(TestToken):
@@ -35,10 +37,15 @@ class TestTokenIssue(TestToken):
def setUp(self):
super(TestTokenIssue, self).setUp()
- self.sc_mock.get_token.return_value = identity_fakes.TOKEN
self.cmd = token.IssueToken(self.app, None)
def test_token_issue(self):
+ auth_ref = identity_fakes.fake_auth_ref(
+ identity_fakes.TOKEN,
+ )
+ self.ar_mock = mock.PropertyMock(return_value=auth_ref)
+ type(self.app.client_manager).auth_ref = self.ar_mock
+
arglist = []
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -48,21 +55,22 @@ class TestTokenIssue(TestToken):
# data to be shown.
columns, data = self.cmd.take_action(parsed_args)
- self.sc_mock.get_token.assert_called_with()
-
collist = ('expires', 'id', 'project_id', 'user_id')
self.assertEqual(collist, columns)
datalist = (
- identity_fakes.token_expires,
+ auth_ref.expires,
identity_fakes.token_id,
- identity_fakes.project_id,
- identity_fakes.user_id,
+ 'project-id',
+ 'user-id',
)
self.assertEqual(datalist, data)
def test_token_issue_with_unscoped_token(self):
- # make sure we return an unscoped token
- self.sc_mock.get_token.return_value = identity_fakes.UNSCOPED_TOKEN
+ auth_ref = identity_fakes.fake_auth_ref(
+ identity_fakes.UNSCOPED_TOKEN,
+ )
+ self.ar_mock = mock.PropertyMock(return_value=auth_ref)
+ type(self.app.client_manager).auth_ref = self.ar_mock
arglist = []
verifylist = []
@@ -71,14 +79,16 @@ class TestTokenIssue(TestToken):
# DisplayCommandBase.take_action() returns two tuples
columns, data = self.cmd.take_action(parsed_args)
- self.sc_mock.get_token.assert_called_with()
-
- collist = ('expires', 'id', 'user_id')
+ collist = (
+ 'expires',
+ 'id',
+ 'user_id',
+ )
self.assertEqual(collist, columns)
datalist = (
- identity_fakes.token_expires,
+ auth_ref.expires,
identity_fakes.token_id,
- identity_fakes.user_id,
+ 'user-id',
)
self.assertEqual(datalist, data)
diff --git a/openstackclient/tests/identity/v2_0/test_user.py b/openstackclient/tests/identity/v2_0/test_user.py
index 921e215d..ba871247 100644
--- a/openstackclient/tests/identity/v2_0/test_user.py
+++ b/openstackclient/tests/identity/v2_0/test_user.py
@@ -13,18 +13,23 @@
# under the License.
#
-import copy
import mock
from keystoneauth1 import exceptions as ks_exc
+from osc_lib import exceptions
from openstackclient.identity.v2_0 import user
-from openstackclient.tests import fakes
from openstackclient.tests.identity.v2_0 import fakes as identity_fakes
class TestUser(identity_fakes.TestIdentityv2):
+ fake_project = identity_fakes.FakeProject.create_one_project()
+ attr = {
+ 'tenantId': fake_project.id,
+ }
+ fake_user = identity_fakes.FakeUser.create_one_user(attr)
+
def setUp(self):
super(TestUser, self).setUp()
@@ -39,6 +44,12 @@ class TestUser(identity_fakes.TestIdentityv2):
class TestUserCreate(TestUser):
+ fake_project_c = identity_fakes.FakeProject.create_one_project()
+ attr = {
+ 'tenantId': fake_project_c.id,
+ }
+ fake_user_c = identity_fakes.FakeUser.create_one_user(attr)
+
columns = (
'email',
'enabled',
@@ -47,39 +58,31 @@ class TestUserCreate(TestUser):
'project_id',
)
datalist = (
- identity_fakes.user_email,
+ fake_user_c.email,
True,
- identity_fakes.user_id,
- identity_fakes.user_name,
- identity_fakes.project_id,
+ fake_user_c.id,
+ fake_user_c.name,
+ fake_project_c.id,
)
def setUp(self):
super(TestUserCreate, self).setUp()
- self.projects_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.PROJECT),
- loaded=True,
- )
+ self.projects_mock.get.return_value = self.fake_project_c
- self.users_mock.create.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.USER),
- loaded=True,
- )
+ self.users_mock.create.return_value = self.fake_user_c
# Get the command object to test
self.cmd = user.CreateUser(self.app, None)
def test_user_create_no_options(self):
arglist = [
- identity_fakes.user_name,
+ self.fake_user_c.name,
]
verifylist = [
('enable', False),
('disable', False),
- ('name', identity_fakes.user_name),
+ ('name', self.fake_user_c.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -95,7 +98,7 @@ class TestUserCreate(TestUser):
}
# UserManager.create(name, password, email, tenant_id=, enabled=)
self.users_mock.create.assert_called_with(
- identity_fakes.user_name,
+ self.fake_user_c.name,
None,
None,
**kwargs
@@ -107,10 +110,10 @@ class TestUserCreate(TestUser):
def test_user_create_password(self):
arglist = [
'--password', 'secret',
- identity_fakes.user_name,
+ self.fake_user_c.name,
]
verifylist = [
- ('name', identity_fakes.user_name),
+ ('name', self.fake_user_c.name),
('password_prompt', False),
('password', 'secret')
]
@@ -128,7 +131,7 @@ class TestUserCreate(TestUser):
}
# UserManager.create(name, password, email, tenant_id=, enabled=)
self.users_mock.create.assert_called_with(
- identity_fakes.user_name,
+ self.fake_user_c.name,
'secret',
None,
**kwargs
@@ -139,10 +142,10 @@ class TestUserCreate(TestUser):
def test_user_create_password_prompt(self):
arglist = [
'--password-prompt',
- identity_fakes.user_name,
+ self.fake_user_c.name,
]
verifylist = [
- ('name', identity_fakes.user_name),
+ ('name', self.fake_user_c.name),
('password_prompt', True)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -152,7 +155,7 @@ class TestUserCreate(TestUser):
# data to be shown.
mocker = mock.Mock()
mocker.return_value = 'abc123'
- with mock.patch("openstackclient.common.utils.get_password", mocker):
+ with mock.patch("osc_lib.utils.get_password", mocker):
columns, data = self.cmd.take_action(parsed_args)
# Set expected values
@@ -162,7 +165,7 @@ class TestUserCreate(TestUser):
}
# UserManager.create(name, password, email, tenant_id=, enabled=)
self.users_mock.create.assert_called_with(
- identity_fakes.user_name,
+ self.fake_user_c.name,
'abc123',
None,
**kwargs
@@ -174,10 +177,10 @@ class TestUserCreate(TestUser):
def test_user_create_email(self):
arglist = [
'--email', 'barney@example.com',
- identity_fakes.user_name,
+ self.fake_user_c.name,
]
verifylist = [
- ('name', identity_fakes.user_name),
+ ('name', self.fake_user_c.name),
('email', 'barney@example.com'),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -194,7 +197,7 @@ class TestUserCreate(TestUser):
}
# UserManager.create(name, password, email, tenant_id=, enabled=)
self.users_mock.create.assert_called_with(
- identity_fakes.user_name,
+ self.fake_user_c.name,
None,
'barney@example.com',
**kwargs
@@ -205,27 +208,22 @@ class TestUserCreate(TestUser):
def test_user_create_project(self):
# Return the new project
- self.projects_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.PROJECT_2),
- loaded=True,
- )
+ self.projects_mock.get.return_value = self.fake_project_c
+
# Set up to return an updated user
- USER_2 = copy.deepcopy(identity_fakes.USER)
- USER_2['tenantId'] = identity_fakes.PROJECT_2['id']
- self.users_mock.create.return_value = fakes.FakeResource(
- None,
- USER_2,
- loaded=True,
- )
+ attr = {
+ 'tenantId': self.fake_project_c.id,
+ }
+ user_2 = identity_fakes.FakeUser.create_one_user(attr)
+ self.users_mock.create.return_value = user_2
arglist = [
- '--project', identity_fakes.PROJECT_2['name'],
- identity_fakes.user_name,
+ '--project', self.fake_project_c.name,
+ user_2.name,
]
verifylist = [
- ('name', identity_fakes.user_name),
- ('project', identity_fakes.PROJECT_2['name']),
+ ('name', user_2.name),
+ ('project', self.fake_project_c.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -237,11 +235,11 @@ class TestUserCreate(TestUser):
# Set expected values
kwargs = {
'enabled': True,
- 'tenant_id': identity_fakes.PROJECT_2['id'],
+ 'tenant_id': self.fake_project_c.id,
}
# UserManager.create(name, password, email, tenant_id=, enabled=)
self.users_mock.create.assert_called_with(
- identity_fakes.user_name,
+ user_2.name,
None,
None,
**kwargs
@@ -249,21 +247,21 @@ class TestUserCreate(TestUser):
self.assertEqual(self.columns, columns)
datalist = (
- identity_fakes.user_email,
+ user_2.email,
True,
- identity_fakes.user_id,
- identity_fakes.user_name,
- identity_fakes.PROJECT_2['id'],
+ user_2.id,
+ user_2.name,
+ self.fake_project_c.id,
)
self.assertEqual(datalist, data)
def test_user_create_enable(self):
arglist = [
'--enable',
- identity_fakes.user_name,
+ self.fake_user_c.name,
]
verifylist = [
- ('name', identity_fakes.user_name),
+ ('name', self.fake_user_c.name),
('enable', True),
('disable', False),
]
@@ -281,7 +279,7 @@ class TestUserCreate(TestUser):
}
# UserManager.create(name, password, email, tenant_id=, enabled=)
self.users_mock.create.assert_called_with(
- identity_fakes.user_name,
+ self.fake_user_c.name,
None,
None,
**kwargs
@@ -293,10 +291,10 @@ class TestUserCreate(TestUser):
def test_user_create_disable(self):
arglist = [
'--disable',
- identity_fakes.user_name,
+ self.fake_user_c.name,
]
verifylist = [
- ('name', identity_fakes.user_name),
+ ('name', self.fake_user_c.name),
('enable', False),
('disable', True),
]
@@ -314,7 +312,7 @@ class TestUserCreate(TestUser):
}
# UserManager.create(name, password, email, tenant_id=, enabled=)
self.users_mock.create.assert_called_with(
- identity_fakes.user_name,
+ self.fake_user_c.name,
None,
None,
**kwargs
@@ -330,18 +328,14 @@ class TestUserCreate(TestUser):
# need to make this throw an exception...
self.users_mock.create.side_effect = _raise_conflict
- self.users_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.USER),
- loaded=True,
- )
+ self.users_mock.get.return_value = self.fake_user_c
arglist = [
'--or-show',
- identity_fakes.user_name,
+ self.fake_user_c.name,
]
verifylist = [
- ('name', identity_fakes.user_name),
+ ('name', self.fake_user_c.name),
('or_show', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -352,7 +346,7 @@ class TestUserCreate(TestUser):
columns, data = self.cmd.take_action(parsed_args)
# UserManager.create(name, password, email, tenant_id=, enabled=)
- self.users_mock.get.assert_called_with(identity_fakes.user_name)
+ self.users_mock.get.assert_called_with(self.fake_user_c.name)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data)
@@ -360,10 +354,10 @@ class TestUserCreate(TestUser):
def test_user_create_or_show_not_exists(self):
arglist = [
'--or-show',
- identity_fakes.user_name,
+ self.fake_user_c.name,
]
verifylist = [
- ('name', identity_fakes.user_name),
+ ('name', self.fake_user_c.name),
('or_show', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -380,7 +374,7 @@ class TestUserCreate(TestUser):
}
# UserManager.create(name, password, email, tenant_id=, enabled=)
self.users_mock.create.assert_called_with(
- identity_fakes.user_name,
+ self.fake_user_c.name,
None,
None,
**kwargs
@@ -395,11 +389,7 @@ class TestUserDelete(TestUser):
super(TestUserDelete, self).setUp()
# This is the return value for utils.find_resource()
- self.users_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.USER),
- loaded=True,
- )
+ self.users_mock.get.return_value = self.fake_user
self.users_mock.delete.return_value = None
# Get the command object to test
@@ -407,57 +397,47 @@ class TestUserDelete(TestUser):
def test_user_delete_no_options(self):
arglist = [
- identity_fakes.user_id,
+ self.fake_user.id,
]
verifylist = [
- ('users', [identity_fakes.user_id]),
+ ('users', [self.fake_user.id]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.users_mock.delete.assert_called_with(
- identity_fakes.user_id,
+ self.fake_user.id,
)
self.assertIsNone(result)
class TestUserList(TestUser):
+ fake_project_l = identity_fakes.FakeProject.create_one_project()
+ attr = {
+ 'tenantId': fake_project_l.id,
+ }
+ fake_user_l = identity_fakes.FakeUser.create_one_user(attr)
+
columns = (
'ID',
'Name',
)
datalist = (
(
- identity_fakes.user_id,
- identity_fakes.user_name,
+ fake_user_l.id,
+ fake_user_l.name,
),
)
def setUp(self):
super(TestUserList, self).setUp()
- self.projects_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.PROJECT_2),
- loaded=True,
- )
- self.projects_mock.list.return_value = [
- fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.PROJECT),
- loaded=True,
- ),
- ]
+ self.projects_mock.get.return_value = self.fake_project_l
+ self.projects_mock.list.return_value = [self.fake_project_l]
- self.users_mock.list.return_value = [
- fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.USER),
- loaded=True,
- ),
- ]
+ self.users_mock.list.return_value = [self.fake_user_l]
# Get the command object to test
self.cmd = user.ListUser(self.app, None)
@@ -479,13 +459,13 @@ class TestUserList(TestUser):
def test_user_list_project(self):
arglist = [
- '--project', identity_fakes.project_id,
+ '--project', self.fake_project_l.id,
]
verifylist = [
- ('project', identity_fakes.project_id),
+ ('project', self.fake_project_l.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- project_id = identity_fakes.PROJECT_2['id']
+ project_id = self.fake_project_l.id
# In base command class Lister in cliff, abstract method take_action()
# returns a tuple containing the column names and an iterable
@@ -516,10 +496,10 @@ class TestUserList(TestUser):
collist = ('ID', 'Name', 'Project', 'Email', 'Enabled')
self.assertEqual(collist, columns)
datalist = ((
- identity_fakes.user_id,
- identity_fakes.user_name,
- identity_fakes.project_name,
- identity_fakes.user_email,
+ self.fake_user_l.id,
+ self.fake_user_l.name,
+ self.fake_project_l.name,
+ self.fake_user_l.email,
True,
), )
self.assertEqual(datalist, tuple(data))
@@ -530,23 +510,15 @@ class TestUserSet(TestUser):
def setUp(self):
super(TestUserSet, self).setUp()
- self.projects_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.PROJECT),
- loaded=True,
- )
- self.users_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.USER),
- loaded=True,
- )
+ self.projects_mock.get.return_value = self.fake_project
+ self.users_mock.get.return_value = self.fake_user
# Get the command object to test
self.cmd = user.SetUser(self.app, None)
def test_user_set_no_options(self):
arglist = [
- identity_fakes.user_name,
+ self.fake_user.name,
]
verifylist = [
('name', None),
@@ -555,7 +527,7 @@ class TestUserSet(TestUser):
('project', None),
('enable', False),
('disable', False),
- ('user', identity_fakes.user_name),
+ ('user', self.fake_user.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -563,10 +535,31 @@ class TestUserSet(TestUser):
self.assertIsNone(result)
+ def test_user_set_unexist_user(self):
+ arglist = [
+ "unexist-user",
+ ]
+ verifylist = [
+ ('name', None),
+ ('password', None),
+ ('email', None),
+ ('project', None),
+ ('enable', False),
+ ('disable', False),
+ ('user', "unexist-user"),
+ ]
+ self.users_mock.get.side_effect = exceptions.NotFound(None)
+ self.users_mock.find.side_effect = exceptions.NotFound(None)
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.assertRaises(
+ exceptions.CommandError, self.cmd.take_action, parsed_args)
+
def test_user_set_name(self):
arglist = [
'--name', 'qwerty',
- identity_fakes.user_name,
+ self.fake_user.name,
]
verifylist = [
('name', 'qwerty'),
@@ -575,7 +568,7 @@ class TestUserSet(TestUser):
('project', None),
('enable', False),
('disable', False),
- ('user', identity_fakes.user_name),
+ ('user', self.fake_user.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -588,7 +581,7 @@ class TestUserSet(TestUser):
}
# UserManager.update(user, **kwargs)
self.users_mock.update.assert_called_with(
- identity_fakes.user_id,
+ self.fake_user.id,
**kwargs
)
self.assertIsNone(result)
@@ -596,7 +589,7 @@ class TestUserSet(TestUser):
def test_user_set_password(self):
arglist = [
'--password', 'secret',
- identity_fakes.user_name,
+ self.fake_user.name,
]
verifylist = [
('name', None),
@@ -606,7 +599,7 @@ class TestUserSet(TestUser):
('project', None),
('enable', False),
('disable', False),
- ('user', identity_fakes.user_name),
+ ('user', self.fake_user.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -614,7 +607,7 @@ class TestUserSet(TestUser):
# UserManager.update_password(user, password)
self.users_mock.update_password.assert_called_with(
- identity_fakes.user_id,
+ self.fake_user.id,
'secret',
)
self.assertIsNone(result)
@@ -622,7 +615,7 @@ class TestUserSet(TestUser):
def test_user_set_password_prompt(self):
arglist = [
'--password-prompt',
- identity_fakes.user_name,
+ self.fake_user.name,
]
verifylist = [
('name', None),
@@ -632,18 +625,18 @@ class TestUserSet(TestUser):
('project', None),
('enable', False),
('disable', False),
- ('user', identity_fakes.user_name),
+ ('user', self.fake_user.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
mocker = mock.Mock()
mocker.return_value = 'abc123'
- with mock.patch("openstackclient.common.utils.get_password", mocker):
+ with mock.patch("osc_lib.utils.get_password", mocker):
result = self.cmd.take_action(parsed_args)
# UserManager.update_password(user, password)
self.users_mock.update_password.assert_called_with(
- identity_fakes.user_id,
+ self.fake_user.id,
'abc123',
)
self.assertIsNone(result)
@@ -651,7 +644,7 @@ class TestUserSet(TestUser):
def test_user_set_email(self):
arglist = [
'--email', 'barney@example.com',
- identity_fakes.user_name,
+ self.fake_user.name,
]
verifylist = [
('name', None),
@@ -660,7 +653,7 @@ class TestUserSet(TestUser):
('project', None),
('enable', False),
('disable', False),
- ('user', identity_fakes.user_name),
+ ('user', self.fake_user.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -673,24 +666,24 @@ class TestUserSet(TestUser):
}
# UserManager.update(user, **kwargs)
self.users_mock.update.assert_called_with(
- identity_fakes.user_id,
+ self.fake_user.id,
**kwargs
)
self.assertIsNone(result)
def test_user_set_project(self):
arglist = [
- '--project', identity_fakes.project_id,
- identity_fakes.user_name,
+ '--project', self.fake_project.id,
+ self.fake_user.name,
]
verifylist = [
('name', None),
('password', None),
('email', None),
- ('project', identity_fakes.project_id),
+ ('project', self.fake_project.id),
('enable', False),
('disable', False),
- ('user', identity_fakes.user_name),
+ ('user', self.fake_user.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -698,15 +691,15 @@ class TestUserSet(TestUser):
# UserManager.update_tenant(user, tenant)
self.users_mock.update_tenant.assert_called_with(
- identity_fakes.user_id,
- identity_fakes.project_id,
+ self.fake_user.id,
+ self.fake_project.id,
)
self.assertIsNone(result)
def test_user_set_enable(self):
arglist = [
'--enable',
- identity_fakes.user_name,
+ self.fake_user.name,
]
verifylist = [
('name', None),
@@ -715,7 +708,7 @@ class TestUserSet(TestUser):
('project', None),
('enable', True),
('disable', False),
- ('user', identity_fakes.user_name),
+ ('user', self.fake_user.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -727,7 +720,7 @@ class TestUserSet(TestUser):
}
# UserManager.update(user, **kwargs)
self.users_mock.update.assert_called_with(
- identity_fakes.user_id,
+ self.fake_user.id,
**kwargs
)
self.assertIsNone(result)
@@ -735,7 +728,7 @@ class TestUserSet(TestUser):
def test_user_set_disable(self):
arglist = [
'--disable',
- identity_fakes.user_name,
+ self.fake_user.name,
]
verifylist = [
('name', None),
@@ -744,7 +737,7 @@ class TestUserSet(TestUser):
('project', None),
('enable', False),
('disable', True),
- ('user', identity_fakes.user_name),
+ ('user', self.fake_user.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -756,7 +749,7 @@ class TestUserSet(TestUser):
}
# UserManager.update(user, **kwargs)
self.users_mock.update.assert_called_with(
- identity_fakes.user_id,
+ self.fake_user.id,
**kwargs
)
self.assertIsNone(result)
@@ -767,21 +760,17 @@ class TestUserShow(TestUser):
def setUp(self):
super(TestUserShow, self).setUp()
- self.users_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.USER),
- loaded=True,
- )
+ self.users_mock.get.return_value = self.fake_user
# Get the command object to test
self.cmd = user.ShowUser(self.app, None)
def test_user_show(self):
arglist = [
- identity_fakes.user_id,
+ self.fake_user.id,
]
verifylist = [
- ('user', identity_fakes.user_id),
+ ('user', self.fake_user.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -790,15 +779,15 @@ class TestUserShow(TestUser):
# data to be shown.
columns, data = self.cmd.take_action(parsed_args)
- self.users_mock.get.assert_called_with(identity_fakes.user_id)
+ self.users_mock.get.assert_called_with(self.fake_user.id)
collist = ('email', 'enabled', 'id', 'name', 'project_id')
self.assertEqual(collist, columns)
datalist = (
- identity_fakes.user_email,
+ self.fake_user.email,
True,
- identity_fakes.user_id,
- identity_fakes.user_name,
- identity_fakes.project_id,
+ self.fake_user.id,
+ self.fake_user.name,
+ self.fake_project.id,
)
self.assertEqual(datalist, data)
diff --git a/openstackclient/tests/identity/v3/fakes.py b/openstackclient/tests/identity/v3/fakes.py
index 1422166a..dd918616 100644
--- a/openstackclient/tests/identity/v3/fakes.py
+++ b/openstackclient/tests/identity/v3/fakes.py
@@ -13,8 +13,12 @@
# under the License.
#
+import copy
import mock
+from keystoneauth1 import access
+from keystoneauth1 import fixture
+
from openstackclient.tests import fakes
from openstackclient.tests import utils
@@ -419,6 +423,36 @@ OAUTH_VERIFIER = {
}
+def fake_auth_ref(fake_token, fake_service=None):
+ """Create an auth_ref using keystoneauth's fixtures"""
+ token_copy = copy.deepcopy(fake_token)
+ token_id = token_copy.pop('id')
+ token = fixture.V3Token(**token_copy)
+ # An auth_ref is actually an access info object
+ auth_ref = access.create(
+ body=token,
+ auth_token=token_id,
+ )
+
+ # Create a service catalog
+ if fake_service:
+ service = token.add_service(
+ fake_service['type'],
+ fake_service['name'],
+ )
+ # TODO(dtroyer): Add an 'id' element to KSA's _Service fixure
+ service['id'] = fake_service['id']
+ for e in fake_service['endpoints']:
+ region = e.get('region_id') or e.get('region', '<none>')
+ service.add_endpoint(
+ e['interface'],
+ e['url'],
+ region=region,
+ )
+
+ return auth_ref
+
+
class FakeAuth(object):
def __init__(self, auth_method_class=None):
@@ -468,6 +502,9 @@ class FakeIdentityv3Client(object):
self.role_assignments.resource_class = fakes.FakeResource(None, {})
self.auth_token = kwargs['token']
self.management_url = kwargs['endpoint']
+ self.auth = FakeAuth()
+ self.auth.client = mock.Mock()
+ self.auth.client.resource_class = fakes.FakeResource(None, {})
class FakeFederationManager(object):
diff --git a/openstackclient/tests/identity/v3/test_catalog.py b/openstackclient/tests/identity/v3/test_catalog.py
index 1b8fa085..e3c5ed3d 100644
--- a/openstackclient/tests/identity/v3/test_catalog.py
+++ b/openstackclient/tests/identity/v3/test_catalog.py
@@ -14,6 +14,7 @@
import mock
from openstackclient.identity.v3 import catalog
+from openstackclient.tests.identity.v3 import fakes as identity_fakes
from openstackclient.tests import utils
@@ -50,7 +51,7 @@ class TestCatalog(utils.TestCommand):
super(TestCatalog, self).setUp()
self.sc_mock = mock.MagicMock()
- self.sc_mock.service_catalog.get_data.return_value = [
+ self.sc_mock.service_catalog.catalog.return_value = [
self.fake_service,
]
@@ -69,6 +70,13 @@ class TestCatalogList(TestCatalog):
self.cmd = catalog.ListCatalog(self.app, None)
def test_catalog_list(self):
+ auth_ref = identity_fakes.fake_auth_ref(
+ identity_fakes.TOKEN_WITH_PROJECT_ID,
+ fake_service=self.fake_service,
+ )
+ self.ar_mock = mock.PropertyMock(return_value=auth_ref)
+ type(self.app.client_manager).auth_ref = self.ar_mock
+
arglist = []
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -77,7 +85,6 @@ class TestCatalogList(TestCatalog):
# returns a tuple containing the column names and an iterable
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
- self.sc_mock.service_catalog.get_data.assert_called_with()
collist = ('Name', 'Type', 'Endpoints')
self.assertEqual(collist, columns)
@@ -101,6 +108,13 @@ class TestCatalogShow(TestCatalog):
self.cmd = catalog.ShowCatalog(self.app, None)
def test_catalog_show(self):
+ auth_ref = identity_fakes.fake_auth_ref(
+ identity_fakes.TOKEN_WITH_PROJECT_ID,
+ fake_service=self.fake_service,
+ )
+ self.ar_mock = mock.PropertyMock(return_value=auth_ref)
+ type(self.app.client_manager).auth_ref = self.ar_mock
+
arglist = [
'compute',
]
@@ -113,7 +127,6 @@ class TestCatalogShow(TestCatalog):
# returns a two-part tuple with a tuple of column names and a tuple of
# data to be shown.
columns, data = self.cmd.take_action(parsed_args)
- self.sc_mock.service_catalog.get_data.assert_called_with()
collist = ('endpoints', 'id', 'name', 'type')
self.assertEqual(collist, columns)
diff --git a/openstackclient/tests/identity/v3/test_domain.py b/openstackclient/tests/identity/v3/test_domain.py
index f3777f12..e06e0681 100644
--- a/openstackclient/tests/identity/v3/test_domain.py
+++ b/openstackclient/tests/identity/v3/test_domain.py
@@ -389,6 +389,16 @@ class TestDomainShow(TestDomain):
('domain', identity_fakes.domain_id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.app.client_manager.identity.tokens.get_token_data.return_value = \
+ {'token':
+ {'project':
+ {'domain':
+ {'id': 'd1',
+ 'name': 'd1'
+ }
+ }
+ }
+ }
# In base command class ShowOne in cliff, abstract method take_action()
# returns a two-part tuple with a tuple of column names and a tuple of
diff --git a/openstackclient/tests/identity/v3/test_mappings.py b/openstackclient/tests/identity/v3/test_mappings.py
index b9e3b1d5..af7b135d 100644
--- a/openstackclient/tests/identity/v3/test_mappings.py
+++ b/openstackclient/tests/identity/v3/test_mappings.py
@@ -16,7 +16,8 @@ import copy
import mock
-from openstackclient.common import exceptions
+from osc_lib import exceptions
+
from openstackclient.identity.v3 import mapping
from openstackclient.tests import fakes
from openstackclient.tests.identity.v3 import fakes as identity_fakes
diff --git a/openstackclient/tests/identity/v3/test_project.py b/openstackclient/tests/identity/v3/test_project.py
index 1e9d1c8b..93bf18af 100644
--- a/openstackclient/tests/identity/v3/test_project.py
+++ b/openstackclient/tests/identity/v3/test_project.py
@@ -16,7 +16,8 @@
import copy
import mock
-from openstackclient.common import exceptions
+from osc_lib import exceptions
+
from openstackclient.identity.v3 import project
from openstackclient.tests import fakes
from openstackclient.tests.identity.v3 import fakes as identity_fakes
@@ -201,7 +202,7 @@ class TestProjectCreate(TestProject):
mocker = mock.Mock()
mocker.return_value = None
- with mock.patch("openstackclient.common.utils.find_resource", mocker):
+ with mock.patch("osc_lib.utils.find_resource", mocker):
columns, data = self.cmd.take_action(parsed_args)
# Set expected values
@@ -548,7 +549,7 @@ class TestProjectList(TestProject):
mocker = mock.Mock()
mocker.return_value = None
- with mock.patch("openstackclient.common.utils.find_resource", mocker):
+ with mock.patch("osc_lib.utils.find_resource", mocker):
columns, data = self.cmd.take_action(parsed_args)
self.projects_mock.list.assert_called_with(
@@ -748,6 +749,7 @@ class TestProjectShow(TestProject):
self.cmd = project.ShowProject(self.app, None)
def test_project_show(self):
+
arglist = [
identity_fakes.project_id,
]
@@ -756,6 +758,16 @@ class TestProjectShow(TestProject):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.app.client_manager.identity.tokens.get_token_data.return_value = \
+ {'token':
+ {'project':
+ {'domain': {},
+ 'name': parsed_args.project,
+ 'id': parsed_args.project
+ }
+ }
+ }
+
# In base command class ShowOne in cliff, abstract method take_action()
# returns a two-part tuple with a tuple of column names and a tuple of
# data to be shown.
@@ -796,6 +808,15 @@ class TestProjectShow(TestProject):
('children', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.app.client_manager.identity.tokens.get_token_data.return_value = \
+ {'token':
+ {'project':
+ {'domain': {},
+ 'name': parsed_args.project,
+ 'id': parsed_args.project
+ }
+ }
+ }
columns, data = self.cmd.take_action(parsed_args)
self.projects_mock.get.assert_called_with(
@@ -844,6 +865,15 @@ class TestProjectShow(TestProject):
('children', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.app.client_manager.identity.tokens.get_token_data.return_value = \
+ {'token':
+ {'project':
+ {'domain': {},
+ 'name': parsed_args.project,
+ 'id': parsed_args.project
+ }
+ }
+ }
columns, data = self.cmd.take_action(parsed_args)
self.projects_mock.get.assert_called_with(
@@ -894,6 +924,15 @@ class TestProjectShow(TestProject):
('children', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.app.client_manager.identity.tokens.get_token_data.return_value = \
+ {'token':
+ {'project':
+ {'domain': {},
+ 'name': parsed_args.project,
+ 'id': parsed_args.project
+ }
+ }
+ }
columns, data = self.cmd.take_action(parsed_args)
self.projects_mock.get.assert_called_with(
diff --git a/openstackclient/tests/identity/v3/test_token.py b/openstackclient/tests/identity/v3/test_token.py
index b68bc242..9728c6e1 100644
--- a/openstackclient/tests/identity/v3/test_token.py
+++ b/openstackclient/tests/identity/v3/test_token.py
@@ -24,10 +24,9 @@ class TestToken(identity_fakes.TestIdentityv3):
def setUp(self):
super(TestToken, self).setUp()
- # Get a shortcut to the Service Catalog Mock
- self.sc_mock = mock.Mock()
- self.app.client_manager.auth_ref = mock.Mock()
- self.app.client_manager.auth_ref.service_catalog = self.sc_mock
+ # Get a shortcut to the Auth Ref Mock
+ self.ar_mock = mock.PropertyMock()
+ type(self.app.client_manager).auth_ref = self.ar_mock
class TestTokenIssue(TestToken):
@@ -38,23 +37,25 @@ class TestTokenIssue(TestToken):
self.cmd = token.IssueToken(self.app, None)
def test_token_issue_with_project_id(self):
+ auth_ref = identity_fakes.fake_auth_ref(
+ identity_fakes.TOKEN_WITH_PROJECT_ID,
+ )
+ self.ar_mock = mock.PropertyMock(return_value=auth_ref)
+ type(self.app.client_manager).auth_ref = self.ar_mock
+
arglist = []
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.sc_mock.get_token.return_value = \
- identity_fakes.TOKEN_WITH_PROJECT_ID
# In base command class ShowOne in cliff, abstract method take_action()
# returns a two-part tuple with a tuple of column names and a tuple of
# data to be shown.
columns, data = self.cmd.take_action(parsed_args)
- self.sc_mock.get_token.assert_called_with()
-
collist = ('expires', 'id', 'project_id', 'user_id')
self.assertEqual(collist, columns)
datalist = (
- identity_fakes.token_expires,
+ auth_ref.expires,
identity_fakes.token_id,
identity_fakes.project_id,
identity_fakes.user_id,
@@ -62,45 +63,53 @@ class TestTokenIssue(TestToken):
self.assertEqual(datalist, data)
def test_token_issue_with_domain_id(self):
+ auth_ref = identity_fakes.fake_auth_ref(
+ identity_fakes.TOKEN_WITH_DOMAIN_ID,
+ )
+ self.ar_mock = mock.PropertyMock(return_value=auth_ref)
+ type(self.app.client_manager).auth_ref = self.ar_mock
+
arglist = []
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.sc_mock.get_token.return_value = \
- identity_fakes.TOKEN_WITH_DOMAIN_ID
# In base command class ShowOne in cliff, abstract method take_action()
# returns a two-part tuple with a tuple of column names and a tuple of
# data to be shown.
columns, data = self.cmd.take_action(parsed_args)
- self.sc_mock.get_token.assert_called_with()
-
collist = ('domain_id', 'expires', 'id', 'user_id')
self.assertEqual(collist, columns)
datalist = (
identity_fakes.domain_id,
- identity_fakes.token_expires,
+ auth_ref.expires,
identity_fakes.token_id,
identity_fakes.user_id,
)
self.assertEqual(datalist, data)
def test_token_issue_with_unscoped(self):
+ auth_ref = identity_fakes.fake_auth_ref(
+ identity_fakes.UNSCOPED_TOKEN,
+ )
+ self.ar_mock = mock.PropertyMock(return_value=auth_ref)
+ type(self.app.client_manager).auth_ref = self.ar_mock
+
arglist = []
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.sc_mock.get_token.return_value = \
- identity_fakes.UNSCOPED_TOKEN
# DisplayCommandBase.take_action() returns two tuples
columns, data = self.cmd.take_action(parsed_args)
- self.sc_mock.get_token.assert_called_with()
-
- collist = ('expires', 'id', 'user_id')
+ collist = (
+ 'expires',
+ 'id',
+ 'user_id',
+ )
self.assertEqual(collist, columns)
datalist = (
- identity_fakes.token_expires,
+ auth_ref.expires,
identity_fakes.token_id,
identity_fakes.user_id,
)
diff --git a/openstackclient/tests/identity/v3/test_unscoped_saml.py b/openstackclient/tests/identity/v3/test_unscoped_saml.py
index d12cb454..62623902 100644
--- a/openstackclient/tests/identity/v3/test_unscoped_saml.py
+++ b/openstackclient/tests/identity/v3/test_unscoped_saml.py
@@ -12,7 +12,8 @@
import copy
-from openstackclient.common import exceptions
+from osc_lib import exceptions
+
from openstackclient.identity.v3 import unscoped_saml
from openstackclient.tests import fakes
from openstackclient.tests.identity.v3 import fakes as identity_fakes
diff --git a/openstackclient/tests/identity/v3/test_user.py b/openstackclient/tests/identity/v3/test_user.py
index 5dafa772..c4fb1521 100644
--- a/openstackclient/tests/identity/v3/test_user.py
+++ b/openstackclient/tests/identity/v3/test_user.py
@@ -185,7 +185,7 @@ class TestUserCreate(TestUser):
# data to be shown.
mocker = mock.Mock()
mocker.return_value = 'abc123'
- with mock.patch("openstackclient.common.utils.get_password", mocker):
+ with mock.patch("osc_lib.utils.get_password", mocker):
columns, data = self.cmd.take_action(parsed_args)
# Set expected values
@@ -841,7 +841,7 @@ class TestUserSet(TestUser):
mocker = mock.Mock()
mocker.return_value = 'abc123'
- with mock.patch("openstackclient.common.utils.get_password", mocker):
+ with mock.patch("osc_lib.utils.get_password", mocker):
result = self.cmd.take_action(parsed_args)
# Set expected values
@@ -1023,7 +1023,7 @@ class TestUserSetPassword(TestUser):
@contextlib.contextmanager
def _mock_get_password(*passwords):
mocker = mock.Mock(side_effect=passwords)
- with mock.patch("openstackclient.common.utils.get_password", mocker):
+ with mock.patch("osc_lib.utils.get_password", mocker):
yield
def test_user_password_change(self):
@@ -1094,6 +1094,17 @@ class TestUserShow(TestUser):
# Get the command object to test
self.cmd = user.ShowUser(self.app, None)
+ self.app.client_manager.identity.auth.client.get_user_id.\
+ return_value = 'bbbbbbb-aaaa-aaaa-aaaa-bbbbbbbaaaa'
+ self.app.client_manager.identity.tokens.get_token_data.return_value = \
+ {'token':
+ {'user':
+ {'domain': {},
+ 'id': 'bbbbbbb-aaaa-aaaa-aaaa-bbbbbbbaaaa',
+ 'name': 'bbbbbbb-aaaa-aaaa-aaaa-bbbbbbbaaaa'
+ }
+ }
+ }
def test_user_show(self):
arglist = [
diff --git a/openstackclient/tests/image/v1/test_image.py b/openstackclient/tests/image/v1/test_image.py
index 018e1199..14aa331f 100644
--- a/openstackclient/tests/image/v1/test_image.py
+++ b/openstackclient/tests/image/v1/test_image.py
@@ -16,7 +16,8 @@
import copy
import mock
-from openstackclient.common import exceptions
+from osc_lib import exceptions
+
from openstackclient.image.v1 import image
from openstackclient.tests import fakes
from openstackclient.tests.image.v1 import fakes as image_fakes
@@ -416,7 +417,7 @@ class TestImageList(TestImage):
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, tuple(data))
- @mock.patch('openstackclient.common.utils.sort_items')
+ @mock.patch('osc_lib.utils.sort_items')
def test_image_list_sort_option(self, si_mock):
si_mock.side_effect = [
[copy.deepcopy(image_fakes.IMAGE)], [],
@@ -474,8 +475,8 @@ class TestImageSet(TestImage):
result = self.cmd.take_action(parsed_args)
- # Verify update() was not called, if it was show the args
- self.assertEqual(self.images_mock.update.call_args_list, [])
+ self.images_mock.update.assert_called_with(image_fakes.image_id,
+ **{})
self.assertIsNone(result)
def test_image_set_options(self):
diff --git a/openstackclient/tests/image/v2/fakes.py b/openstackclient/tests/image/v2/fakes.py
index 24aaec51..8e22fbb2 100644
--- a/openstackclient/tests/image/v2/fakes.py
+++ b/openstackclient/tests/image/v2/fakes.py
@@ -19,13 +19,12 @@ import random
import uuid
from glanceclient.v2 import schemas
+from osc_lib import utils as common_utils
import warlock
-from openstackclient.common import utils as common_utils
from openstackclient.tests import fakes
-from openstackclient.tests import utils
-
from openstackclient.tests.identity.v3 import fakes as identity_fakes
+from openstackclient.tests import utils
image_id = '0f41529e-7c12-4de8-be2d-181abb825b3c'
image_name = 'graven'
diff --git a/openstackclient/tests/image/v2/test_image.py b/openstackclient/tests/image/v2/test_image.py
index ca20d83d..592def21 100644
--- a/openstackclient/tests/image/v2/test_image.py
+++ b/openstackclient/tests/image/v2/test_image.py
@@ -19,8 +19,9 @@ import mock
import warlock
from glanceclient.v2 import schemas
-from openstackclient.common import exceptions
-from openstackclient.common import utils as common_utils
+from osc_lib import exceptions
+from osc_lib import utils as common_utils
+
from openstackclient.image.v2 import image
from openstackclient.tests import fakes
from openstackclient.tests.identity.v3 import fakes as identity_fakes
@@ -473,6 +474,37 @@ class TestImageDelete(TestImage):
self.images_mock.delete.assert_has_calls(calls)
self.assertIsNone(result)
+ def test_image_delete_multi_images_exception(self):
+
+ images = image_fakes.FakeImage.create_images(count=2)
+ arglist = [
+ images[0].id,
+ images[1].id,
+ 'x-y-x',
+ ]
+ verifylist = [
+ ('images', arglist)
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # Fake exception in utils.find_resource()
+ # In image v2, we use utils.find_resource() to find a network.
+ # It calls get() several times, but find() only one time. So we
+ # choose to fake get() always raise exception, then pass through.
+ # And fake find() to find the real network or not.
+ ret_find = [
+ images[0],
+ images[1],
+ exceptions.NotFound('404'),
+ ]
+
+ self.images_mock.get = Exception()
+ self.images_mock.find.side_effect = ret_find
+ self.assertRaises(exceptions.CommandError, self.cmd.take_action,
+ parsed_args)
+ calls = [mock.call(i.id) for i in images]
+ self.images_mock.delete.assert_has_calls(calls)
+
class TestImageList(TestImage):
@@ -660,7 +692,7 @@ class TestImageList(TestImage):
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, tuple(data))
- @mock.patch('openstackclient.common.utils.sort_items')
+ @mock.patch('osc_lib.utils.sort_items')
def test_image_list_sort_option(self, si_mock):
si_mock.return_value = [copy.deepcopy(self._image)]
@@ -697,7 +729,7 @@ class TestImageList(TestImage):
self.assertEqual(self.columns, columns)
self.assertEqual(len(self.datalist), len(tuple(data)))
- @mock.patch('openstackclient.common.utils.find_resource')
+ @mock.patch('osc_lib.utils.find_resource')
def test_image_list_marker_option(self, fr_mock):
# tangchen: Since image_fakes.IMAGE is a dict, it cannot offer a .id
# operation. Will fix this by using FakeImage class instead
@@ -810,6 +842,19 @@ class TestImageSet(TestImage):
# Get the command object to test
self.cmd = image.SetImage(self.app, None)
+ def test_image_set_no_options(self):
+ arglist = [
+ image_fakes.image_id,
+ ]
+ verifylist = [
+ ('image', image_fakes.image_id)
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.assertIsNone(result)
+
def test_image_set_options(self):
arglist = [
'--name', 'new-name',
@@ -1210,6 +1255,19 @@ class TestImageUnset(TestImage):
# Get the command object to test
self.cmd = image.UnsetImage(self.app, None)
+ def test_image_unset_no_options(self):
+ arglist = [
+ image_fakes.image_id,
+ ]
+ verifylist = [
+ ('image', image_fakes.image_id)
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.assertIsNone(result)
+
def test_image_unset_tag_option(self):
arglist = [
diff --git a/openstackclient/tests/network/v2/fakes.py b/openstackclient/tests/network/v2/fakes.py
index ccbe395b..50d9899c 100644
--- a/openstackclient/tests/network/v2/fakes.py
+++ b/openstackclient/tests/network/v2/fakes.py
@@ -19,12 +19,6 @@ import uuid
from openstackclient.tests import fakes
from openstackclient.tests import utils
-extension_name = 'Matrix'
-extension_namespace = 'http://docs.openstack.org/network/'
-extension_description = 'Simulated reality'
-extension_updated = '2013-07-09T12:00:0-00:00'
-extension_alias = 'Dystopian'
-extension_links = '[{"href":''"https://github.com/os/network", "type"}]'
QUOTA = {
"subnet": 10,
@@ -42,21 +36,11 @@ QUOTA = {
}
-def create_extension():
- extension = mock.Mock()
- extension.name = extension_name
- extension.namespace = extension_namespace
- extension.description = extension_description
- extension.updated = extension_updated
- extension.alias = extension_alias
- extension.links = extension_links
- return extension
-
-
class FakeNetworkV2Client(object):
def __init__(self, **kwargs):
- self.extensions = mock.Mock(return_value=[create_extension()])
+ self.extensions = mock.Mock()
+ self.extensions.resource_class = fakes.FakeResource(None, {})
class TestNetworkV2(utils.TestCommand):
@@ -240,6 +224,39 @@ class FakeIPAvailability(object):
return network_ip_availabilities
+class FakeExtension(object):
+ """Fake one or more extension."""
+
+ @staticmethod
+ def create_one_extension(attrs=None):
+ """Create a fake extension.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ A FakeResource object with name, namespace, etc.
+ """
+ attrs = attrs or {}
+
+ # Set default attributes.
+ extension_info = {
+ 'name': 'name-' + uuid.uuid4().hex,
+ 'namespace': 'http://docs.openstack.org/network/',
+ 'description': 'description-' + uuid.uuid4().hex,
+ 'updated': '2013-07-09T12:00:0-00:00',
+ 'alias': 'Dystopian',
+ 'links': '[{"href":''"https://github.com/os/network", "type"}]',
+ }
+
+ # Overwrite default attributes.
+ extension_info.update(attrs)
+
+ extension = fakes.FakeResource(
+ info=copy.deepcopy(extension_info),
+ loaded=True)
+ return extension
+
+
class FakeNetwork(object):
"""Fake one or more networks."""
@@ -335,7 +352,7 @@ class FakeNetworkSegment(object):
# Set default attributes.
network_segment_attrs = {
- 'id': 'segment-id-' + uuid.uuid4().hex,
+ 'id': 'network-segment-id-' + uuid.uuid4().hex,
'network_id': 'network-id-' + uuid.uuid4().hex,
'network_type': 'vlan',
'physical_network': 'physical-network-name-' + uuid.uuid4().hex,
@@ -594,6 +611,25 @@ class FakeSecurityGroup(object):
return security_groups
+ @staticmethod
+ def get_security_groups(security_groups=None, count=2):
+ """Get an iterable MagicMock object with a list of faked security groups.
+
+ If security groups list is provided, then initialize the Mock object
+ with the list. Otherwise create one.
+
+ :param List security groups:
+ A list of FakeResource objects faking security groups
+ :param int count:
+ The number of security groups to fake
+ :return:
+ An iterable Mock object with side_effect set to a list of faked
+ security groups
+ """
+ if security_groups is None:
+ security_groups = FakeSecurityGroup.create_security_groups(count)
+ return mock.MagicMock(side_effect=security_groups)
+
class FakeSecurityGroupRule(object):
"""Fake one or more security group rules."""
@@ -653,6 +689,26 @@ class FakeSecurityGroupRule(object):
return security_group_rules
+ @staticmethod
+ def get_security_group_rules(security_group_rules=None, count=2):
+ """Get an iterable MagicMock object with a list of faked security group rules.
+
+ If security group rules list is provided, then initialize the Mock
+ object with the list. Otherwise create one.
+
+ :param List security group rules:
+ A list of FakeResource objects faking security group rules
+ :param int count:
+ The number of security group rules to fake
+ :return:
+ An iterable Mock object with side_effect set to a list of faked
+ security group rules
+ """
+ if security_group_rules is None:
+ security_group_rules = (
+ FakeSecurityGroupRule.create_security_group_rules(count))
+ return mock.MagicMock(side_effect=security_group_rules)
+
class FakeSubnet(object):
"""Fake one or more subnets."""
@@ -682,9 +738,10 @@ class FakeSubnet(object):
'host_routes': [],
'ip_version': 4,
'gateway_ip': '10.10.10.1',
- 'ipv6_address_mode': 'None',
- 'ipv6_ra_mode': 'None',
- 'subnetpool_id': 'None',
+ 'ipv6_address_mode': None,
+ 'ipv6_ra_mode': None,
+ 'segment_id': None,
+ 'subnetpool_id': None,
}
# Overwrite default attributes.
@@ -715,6 +772,25 @@ class FakeSubnet(object):
return subnets
+ @staticmethod
+ def get_subnets(subnets=None, count=2):
+ """Get an iterable MagicMock object with a list of faked subnets.
+
+ If subnets list is provided, then initialize the Mock object
+ with the list. Otherwise create one.
+
+ :param List subnets:
+ A list of FakeResource objects faking subnets
+ :param int count:
+ The number of subnets to fake
+ :return:
+ An iterable Mock object with side_effect set to a list of faked
+ subnets
+ """
+ if subnets is None:
+ subnets = FakeSubnet.create_subnets(count)
+ return mock.MagicMock(side_effect=subnets)
+
class FakeFloatingIP(object):
"""Fake one or more floating ip."""
@@ -854,3 +930,22 @@ class FakeSubnetPool(object):
)
return subnet_pools
+
+ @staticmethod
+ def get_subnet_pools(subnet_pools=None, count=2):
+ """Get an iterable MagicMock object with a list of faked subnet pools.
+
+ If subnet_pools list is provided, then initialize the Mock object
+ with the list. Otherwise create one.
+
+ :param List subnet pools:
+ A list of FakeResource objects faking subnet pools
+ :param int count:
+ The number of subnet pools to fake
+ :return:
+ An iterable Mock object with side_effect set to a list of faked
+ subnet pools
+ """
+ if subnet_pools is None:
+ subnet_pools = FakeSubnetPool.create_subnet_pools(count)
+ return mock.MagicMock(side_effect=subnet_pools)
diff --git a/openstackclient/tests/network/v2/test_address_scope.py b/openstackclient/tests/network/v2/test_address_scope.py
index b4f4fa88..722371f9 100644
--- a/openstackclient/tests/network/v2/test_address_scope.py
+++ b/openstackclient/tests/network/v2/test_address_scope.py
@@ -15,7 +15,8 @@ import copy
import mock
from mock import call
-from openstackclient.common import exceptions
+from osc_lib import exceptions
+
from openstackclient.network.v2 import address_scope
from openstackclient.tests import fakes
from openstackclient.tests.identity.v3 import fakes as identity_fakes_v3
@@ -313,8 +314,12 @@ class TestSetAddressScope(TestAddressScope):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.assertRaises(exceptions.CommandError, self.cmd.take_action,
- parsed_args)
+ result = self.cmd.take_action(parsed_args)
+
+ attrs = {}
+ self.network.update_address_scope.assert_called_with(
+ self._address_scope, **attrs)
+ self.assertIsNone(result)
def test_set_name_and_share(self):
arglist = [
diff --git a/openstackclient/tests/network/v2/test_floating_ip.py b/openstackclient/tests/network/v2/test_floating_ip.py
index f9ccfe1c..5cd5279a 100644
--- a/openstackclient/tests/network/v2/test_floating_ip.py
+++ b/openstackclient/tests/network/v2/test_floating_ip.py
@@ -12,6 +12,9 @@
#
import mock
+from mock import call
+
+from osc_lib import exceptions
from openstackclient.network.v2 import floating_ip
from openstackclient.tests.compute.v2 import fakes as compute_fakes
@@ -140,33 +143,84 @@ class TestCreateFloatingIPNetwork(TestFloatingIPNetwork):
class TestDeleteFloatingIPNetwork(TestFloatingIPNetwork):
- # The floating ip to be deleted.
- floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip()
+ # The floating ips to be deleted.
+ floating_ips = network_fakes.FakeFloatingIP.create_floating_ips(count=2)
def setUp(self):
super(TestDeleteFloatingIPNetwork, self).setUp()
self.network.delete_ip = mock.Mock(return_value=None)
- self.network.find_ip = mock.Mock(return_value=self.floating_ip)
+ self.network.find_ip = (
+ network_fakes.FakeFloatingIP.get_floating_ips(self.floating_ips))
# Get the command object to test
self.cmd = floating_ip.DeleteFloatingIP(self.app, self.namespace)
def test_floating_ip_delete(self):
arglist = [
- self.floating_ip.id,
+ self.floating_ips[0].id,
]
verifylist = [
- ('floating_ip', self.floating_ip.id),
+ ('floating_ip', [self.floating_ips[0].id]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
- self.network.find_ip.assert_called_once_with(self.floating_ip.id)
- self.network.delete_ip.assert_called_once_with(self.floating_ip)
+ self.network.find_ip.assert_called_once_with(
+ self.floating_ips[0].id, ignore_missing=False)
+ self.network.delete_ip.assert_called_once_with(self.floating_ips[0])
self.assertIsNone(result)
+ def test_multi_floating_ips_delete(self):
+ arglist = []
+ verifylist = []
+
+ for f in self.floating_ips:
+ arglist.append(f.id)
+ verifylist = [
+ ('floating_ip', arglist),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ calls = []
+ for f in self.floating_ips:
+ calls.append(call(f))
+ self.network.delete_ip.assert_has_calls(calls)
+ self.assertIsNone(result)
+
+ def test_multi_floating_ips_delete_with_exception(self):
+ arglist = [
+ self.floating_ips[0].id,
+ 'unexist_floating_ip',
+ ]
+ verifylist = [
+ ('floating_ip',
+ [self.floating_ips[0].id, 'unexist_floating_ip']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ find_mock_result = [self.floating_ips[0], exceptions.CommandError]
+ self.network.find_ip = (
+ mock.MagicMock(side_effect=find_mock_result)
+ )
+
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('1 of 2 floating_ip failed to delete.', str(e))
+
+ self.network.find_ip.assert_any_call(
+ self.floating_ips[0].id, ignore_missing=False)
+ self.network.find_ip.assert_any_call(
+ 'unexist_floating_ip', ignore_missing=False)
+ self.network.delete_ip.assert_called_once_with(
+ self.floating_ips[0]
+ )
+
class TestListFloatingIPNetwork(TestFloatingIPNetwork):
@@ -335,8 +389,8 @@ class TestCreateFloatingIPCompute(TestFloatingIPCompute):
class TestDeleteFloatingIPCompute(TestFloatingIPCompute):
- # The floating ip to be deleted.
- floating_ip = compute_fakes.FakeFloatingIP.create_one_floating_ip()
+ # The floating ips to be deleted.
+ floating_ips = compute_fakes.FakeFloatingIP.create_floating_ips(count=2)
def setUp(self):
super(TestDeleteFloatingIPCompute, self).setUp()
@@ -346,27 +400,78 @@ class TestDeleteFloatingIPCompute(TestFloatingIPCompute):
self.compute.floating_ips.delete.return_value = None
# Return value of utils.find_resource()
- self.compute.floating_ips.get.return_value = self.floating_ip
+ self.compute.floating_ips.get = (
+ compute_fakes.FakeFloatingIP.get_floating_ips(self.floating_ips))
# Get the command object to test
self.cmd = floating_ip.DeleteFloatingIP(self.app, None)
def test_floating_ip_delete(self):
arglist = [
- self.floating_ip.id,
+ self.floating_ips[0].id,
]
verifylist = [
- ('floating_ip', self.floating_ip.id),
+ ('floating_ip', [self.floating_ips[0].id]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.compute.floating_ips.delete.assert_called_once_with(
- self.floating_ip.id
+ self.floating_ips[0].id
)
self.assertIsNone(result)
+ def test_multi_floating_ips_delete(self):
+ arglist = []
+ verifylist = []
+
+ for f in self.floating_ips:
+ arglist.append(f.id)
+ verifylist = [
+ ('floating_ip', arglist),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ calls = []
+ for f in self.floating_ips:
+ calls.append(call(f.id))
+ self.compute.floating_ips.delete.assert_has_calls(calls)
+ self.assertIsNone(result)
+
+ def test_multi_floating_ips_delete_with_exception(self):
+ arglist = [
+ self.floating_ips[0].id,
+ 'unexist_floating_ip',
+ ]
+ verifylist = [
+ ('floating_ip',
+ [self.floating_ips[0].id, 'unexist_floating_ip']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ find_mock_result = [self.floating_ips[0], exceptions.CommandError]
+ self.compute.floating_ips.get = (
+ mock.MagicMock(side_effect=find_mock_result)
+ )
+ self.compute.floating_ips.find.side_effect = exceptions.NotFound(None)
+
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('1 of 2 floating_ip failed to delete.', str(e))
+
+ self.compute.floating_ips.get.assert_any_call(
+ self.floating_ips[0].id)
+ self.compute.floating_ips.get.assert_any_call(
+ 'unexist_floating_ip')
+ self.compute.floating_ips.delete.assert_called_once_with(
+ self.floating_ips[0].id
+ )
+
class TestListFloatingIPCompute(TestFloatingIPCompute):
diff --git a/openstackclient/tests/network/v2/test_ip_availability.py b/openstackclient/tests/network/v2/test_ip_availability.py
index 04979e77..c6ec2b0b 100644
--- a/openstackclient/tests/network/v2/test_ip_availability.py
+++ b/openstackclient/tests/network/v2/test_ip_availability.py
@@ -14,7 +14,8 @@
import copy
import mock
-from openstackclient.common import utils as osc_utils
+from osc_lib import utils as common_utils
+
from openstackclient.network.v2 import ip_availability
from openstackclient.tests import fakes
from openstackclient.tests.identity.v3 import fakes as identity_fakes
@@ -81,8 +82,10 @@ class TestListIPAvailability(TestIPAvailability):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
+ filters = {'ip_version': 4}
- self.network.network_ip_availabilities.assert_called_once_with()
+ self.network.network_ip_availabilities.assert_called_once_with(
+ **filters)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
@@ -115,7 +118,8 @@ class TestListIPAvailability(TestIPAvailability):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- filters = {'tenant_id': identity_fakes.project_id}
+ filters = {'tenant_id': identity_fakes.project_id,
+ 'ip_version': 4}
self.network.network_ip_availabilities.assert_called_once_with(
**filters)
@@ -140,7 +144,7 @@ class TestShowIPAvailability(TestIPAvailability):
_ip_availability.network_id,
_ip_availability.network_name,
_ip_availability.tenant_id,
- osc_utils.format_list(
+ common_utils.format_list(
_ip_availability.subnet_ip_availability),
_ip_availability.total_ips,
_ip_availability.used_ips,
diff --git a/openstackclient/tests/network/v2/test_network.py b/openstackclient/tests/network/v2/test_network.py
index ba810f16..8fc9dadf 100644
--- a/openstackclient/tests/network/v2/test_network.py
+++ b/openstackclient/tests/network/v2/test_network.py
@@ -13,10 +13,11 @@
import copy
import mock
-
from mock import call
-from openstackclient.common import exceptions
-from openstackclient.common import utils
+
+from osc_lib import exceptions
+from osc_lib import utils
+
from openstackclient.network.v2 import network
from openstackclient.tests.compute.v2 import fakes as compute_fakes
from openstackclient.tests import fakes
@@ -609,8 +610,12 @@ class TestSetNetwork(TestNetwork):
verifylist = [('network', self._network.name), ]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.assertRaises(exceptions.CommandError, self.cmd.take_action,
- parsed_args)
+ result = self.cmd.take_action(parsed_args)
+
+ attrs = {}
+ self.network.update_network.assert_called_once_with(
+ self._network, **attrs)
+ self.assertIsNone(result)
class TestShowNetwork(TestNetwork):
diff --git a/openstackclient/tests/network/v2/test_network_segment.py b/openstackclient/tests/network/v2/test_network_segment.py
index 0a99eced..a635d845 100644
--- a/openstackclient/tests/network/v2/test_network_segment.py
+++ b/openstackclient/tests/network/v2/test_network_segment.py
@@ -13,7 +13,8 @@
import mock
-from openstackclient.common import exceptions
+from osc_lib import exceptions
+
from openstackclient.network.v2 import network_segment
from openstackclient.tests.network.v2 import fakes as network_fakes
from openstackclient.tests import utils as tests_utils
diff --git a/openstackclient/tests/network/v2/test_port.py b/openstackclient/tests/network/v2/test_port.py
index c3f175bf..a998585e 100644
--- a/openstackclient/tests/network/v2/test_port.py
+++ b/openstackclient/tests/network/v2/test_port.py
@@ -11,9 +11,13 @@
# under the License.
#
+import argparse
import mock
-from openstackclient.common import utils
+from mock import call
+from osc_lib import exceptions
+from osc_lib import utils
+
from openstackclient.network.v2 import port
from openstackclient.tests.network.v2 import fakes as network_fakes
from openstackclient.tests import utils as tests_utils
@@ -171,33 +175,137 @@ class TestCreatePort(TestPort):
self.assertEqual(ref_columns, columns)
self.assertEqual(ref_data, data)
+ def test_create_invalid_json_binding_profile(self):
+ arglist = [
+ '--network', self._port.network_id,
+ '--binding-profile', '{"parent_name":"fake_parent"',
+ 'test-port',
+ ]
+ self.assertRaises(argparse.ArgumentTypeError,
+ self.check_parser,
+ self.cmd,
+ arglist,
+ None)
+
+ def test_create_invalid_key_value_binding_profile(self):
+ arglist = [
+ '--network', self._port.network_id,
+ '--binding-profile', 'key',
+ 'test-port',
+ ]
+ self.assertRaises(argparse.ArgumentTypeError,
+ self.check_parser,
+ self.cmd,
+ arglist,
+ None)
+
+ def test_create_json_binding_profile(self):
+ arglist = [
+ '--network', self._port.network_id,
+ '--binding-profile', '{"parent_name":"fake_parent"}',
+ '--binding-profile', '{"tag":42}',
+ 'test-port',
+ ]
+ verifylist = [
+ ('network', self._port.network_id,),
+ ('enable', True),
+ ('binding_profile', {'parent_name': 'fake_parent', 'tag': 42}),
+ ('name', 'test-port'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = (self.cmd.take_action(parsed_args))
+
+ self.network.create_port.assert_called_once_with(**{
+ 'admin_state_up': True,
+ 'network_id': self._port.network_id,
+ 'binding:profile': {'parent_name': 'fake_parent', 'tag': 42},
+ 'name': 'test-port',
+ })
+
+ ref_columns, ref_data = self._get_common_cols_data(self._port)
+ self.assertEqual(ref_columns, columns)
+ self.assertEqual(ref_data, data)
+
class TestDeletePort(TestPort):
- # The port to delete.
- _port = network_fakes.FakePort.create_one_port()
+ # Ports to delete.
+ _ports = network_fakes.FakePort.create_ports(count=2)
def setUp(self):
super(TestDeletePort, self).setUp()
self.network.delete_port = mock.Mock(return_value=None)
- self.network.find_port = mock.Mock(return_value=self._port)
+ self.network.find_port = network_fakes.FakePort.get_ports(
+ ports=self._ports)
# Get the command object to test
self.cmd = port.DeletePort(self.app, self.namespace)
- def test_delete(self):
+ def test_port_delete(self):
arglist = [
- self._port.name,
+ self._ports[0].name,
]
verifylist = [
- ('port', [self._port.name]),
+ ('port', [self._ports[0].name]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
- self.network.delete_port.assert_called_once_with(self._port)
+ self.network.find_port.assert_called_once_with(
+ self._ports[0].name, ignore_missing=False)
+ self.network.delete_port.assert_called_once_with(self._ports[0])
+ self.assertIsNone(result)
+
+ def test_multi_ports_delete(self):
+ arglist = []
+ verifylist = []
+
+ for p in self._ports:
+ arglist.append(p.name)
+ verifylist = [
+ ('port', arglist),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ calls = []
+ for p in self._ports:
+ calls.append(call(p))
+ self.network.delete_port.assert_has_calls(calls)
self.assertIsNone(result)
+ def test_multi_ports_delete_with_exception(self):
+ arglist = [
+ self._ports[0].name,
+ 'unexist_port',
+ ]
+ verifylist = [
+ ('port',
+ [self._ports[0].name, 'unexist_port']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ find_mock_result = [self._ports[0], exceptions.CommandError]
+ self.network.find_port = (
+ mock.MagicMock(side_effect=find_mock_result)
+ )
+
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('1 of 2 ports failed to delete.', str(e))
+
+ self.network.find_port.assert_any_call(
+ self._ports[0].name, ignore_missing=False)
+ self.network.find_port.assert_any_call(
+ 'unexist_port', ignore_missing=False)
+ self.network.delete_port.assert_called_once_with(
+ self._ports[0]
+ )
+
class TestListPort(TestPort):
@@ -372,6 +480,63 @@ class TestSetPort(TestPort):
self.network.update_port.assert_called_once_with(self._port, **attrs)
self.assertIsNone(result)
+ def test_set_nothing(self):
+ arglist = [
+ self._port.name,
+ ]
+ verifylist = [
+ ('port', self._port.name),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ attrs = {}
+ self.network.update_port.assert_called_once_with(self._port, **attrs)
+ self.assertIsNone(result)
+
+ def test_set_invalid_json_binding_profile(self):
+ arglist = [
+ '--binding-profile', '{"parent_name"}',
+ 'test-port',
+ ]
+ self.assertRaises(argparse.ArgumentTypeError,
+ self.check_parser,
+ self.cmd,
+ arglist,
+ None)
+
+ def test_set_invalid_key_value_binding_profile(self):
+ arglist = [
+ '--binding-profile', 'key',
+ 'test-port',
+ ]
+ self.assertRaises(argparse.ArgumentTypeError,
+ self.check_parser,
+ self.cmd,
+ arglist,
+ None)
+
+ def test_set_mixed_binding_profile(self):
+ arglist = [
+ '--binding-profile', 'foo=bar',
+ '--binding-profile', '{"foo2": "bar2"}',
+ self._port.name,
+ ]
+ verifylist = [
+ ('binding_profile', {'foo': 'bar', 'foo2': 'bar2'}),
+ ('port', self._port.name),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ attrs = {
+ 'binding:profile': {'foo': 'bar', 'foo2': 'bar2'},
+ }
+ self.network.update_port.assert_called_once_with(self._port, **attrs)
+ self.assertIsNone(result)
+
class TestShowPort(TestPort):
diff --git a/openstackclient/tests/network/v2/test_router.py b/openstackclient/tests/network/v2/test_router.py
index 99b41d2d..e3da253a 100644
--- a/openstackclient/tests/network/v2/test_router.py
+++ b/openstackclient/tests/network/v2/test_router.py
@@ -12,9 +12,11 @@
#
import mock
+from mock import call
+
+from osc_lib import exceptions
+from osc_lib import utils as osc_utils
-from openstackclient.common import exceptions
-from openstackclient.common import utils as osc_utils
from openstackclient.network.v2 import router
from openstackclient.tests.network.v2 import fakes as network_fakes
from openstackclient.tests import utils as tests_utils
@@ -202,32 +204,82 @@ class TestCreateRouter(TestRouter):
class TestDeleteRouter(TestRouter):
- # The router to delete.
- _router = network_fakes.FakeRouter.create_one_router()
+ # The routers to delete.
+ _routers = network_fakes.FakeRouter.create_routers(count=2)
def setUp(self):
super(TestDeleteRouter, self).setUp()
self.network.delete_router = mock.Mock(return_value=None)
- self.network.find_router = mock.Mock(return_value=self._router)
+ self.network.find_router = (
+ network_fakes.FakeRouter.get_routers(self._routers))
# Get the command object to test
self.cmd = router.DeleteRouter(self.app, self.namespace)
- def test_delete(self):
+ def test_router_delete(self):
arglist = [
- self._router.name,
+ self._routers[0].name,
]
verifylist = [
- ('router', [self._router.name]),
+ ('router', [self._routers[0].name]),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ self.network.delete_router.assert_called_once_with(self._routers[0])
+ self.assertIsNone(result)
+
+ def test_multi_routers_delete(self):
+ arglist = []
+ verifylist = []
+
+ for r in self._routers:
+ arglist.append(r.name)
+ verifylist = [
+ ('router', arglist),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
- self.network.delete_router.assert_called_once_with(self._router)
+
+ calls = []
+ for r in self._routers:
+ calls.append(call(r))
+ self.network.delete_router.assert_has_calls(calls)
self.assertIsNone(result)
+ def test_multi_routers_delete_with_exception(self):
+ arglist = [
+ self._routers[0].name,
+ 'unexist_router',
+ ]
+ verifylist = [
+ ('router',
+ [self._routers[0].name, 'unexist_router']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ find_mock_result = [self._routers[0], exceptions.CommandError]
+ self.network.find_router = (
+ mock.MagicMock(side_effect=find_mock_result)
+ )
+
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('1 of 2 routers failed to delete.', str(e))
+
+ self.network.find_router.assert_any_call(
+ self._routers[0].name, ignore_missing=False)
+ self.network.find_router.assert_any_call(
+ 'unexist_router', ignore_missing=False)
+ self.network.delete_router.assert_called_once_with(
+ self._routers[0]
+ )
+
class TestListRouter(TestRouter):
@@ -568,12 +620,20 @@ class TestSetRouter(TestRouter):
self.cmd, arglist, verifylist)
def test_set_nothing(self):
- arglist = [self._router.name, ]
- verifylist = [('router', self._router.name), ]
+ arglist = [
+ self._router.name,
+ ]
+ verifylist = [
+ ('router', self._router.name),
+ ]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.assertRaises(exceptions.CommandError, self.cmd.take_action,
- parsed_args)
+ result = self.cmd.take_action(parsed_args)
+
+ attrs = {}
+ self.network.update_router.assert_called_once_with(
+ self._router, **attrs)
+ self.assertIsNone(result)
class TestShowRouter(TestRouter):
diff --git a/openstackclient/tests/network/v2/test_security_group.py b/openstackclient/tests/network/v2/test_security_group.py
index 213367a4..b0c14985 100644
--- a/openstackclient/tests/network/v2/test_security_group.py
+++ b/openstackclient/tests/network/v2/test_security_group.py
@@ -13,6 +13,9 @@
import copy
import mock
+from mock import call
+
+from osc_lib import exceptions
from openstackclient.network.v2 import security_group
from openstackclient.tests.compute.v2 import fakes as compute_fakes
@@ -227,42 +230,93 @@ class TestCreateSecurityGroupCompute(TestSecurityGroupCompute):
class TestDeleteSecurityGroupNetwork(TestSecurityGroupNetwork):
- # The security group to be deleted.
- _security_group = \
- network_fakes.FakeSecurityGroup.create_one_security_group()
+ # The security groups to be deleted.
+ _security_groups = \
+ network_fakes.FakeSecurityGroup.create_security_groups()
def setUp(self):
super(TestDeleteSecurityGroupNetwork, self).setUp()
self.network.delete_security_group = mock.Mock(return_value=None)
- self.network.find_security_group = mock.Mock(
- return_value=self._security_group)
+ self.network.find_security_group = (
+ network_fakes.FakeSecurityGroup.get_security_groups(
+ self._security_groups)
+ )
# Get the command object to test
self.cmd = security_group.DeleteSecurityGroup(self.app, self.namespace)
def test_security_group_delete(self):
arglist = [
- self._security_group.name,
+ self._security_groups[0].name,
]
verifylist = [
- ('group', self._security_group.name),
+ ('group', [self._security_groups[0].name]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.network.delete_security_group.assert_called_once_with(
- self._security_group)
+ self._security_groups[0])
+ self.assertIsNone(result)
+
+ def test_multi_security_groups_delete(self):
+ arglist = []
+ verifylist = []
+
+ for s in self._security_groups:
+ arglist.append(s.name)
+ verifylist = [
+ ('group', arglist),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ calls = []
+ for s in self._security_groups:
+ calls.append(call(s))
+ self.network.delete_security_group.assert_has_calls(calls)
self.assertIsNone(result)
+ def test_multi_security_groups_delete_with_exception(self):
+ arglist = [
+ self._security_groups[0].name,
+ 'unexist_security_group',
+ ]
+ verifylist = [
+ ('group',
+ [self._security_groups[0].name, 'unexist_security_group']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ find_mock_result = [self._security_groups[0], exceptions.CommandError]
+ self.network.find_security_group = (
+ mock.MagicMock(side_effect=find_mock_result)
+ )
+
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('1 of 2 group failed to delete.', str(e))
+
+ self.network.find_security_group.assert_any_call(
+ self._security_groups[0].name, ignore_missing=False)
+ self.network.find_security_group.assert_any_call(
+ 'unexist_security_group', ignore_missing=False)
+ self.network.delete_security_group.assert_called_once_with(
+ self._security_groups[0]
+ )
+
class TestDeleteSecurityGroupCompute(TestSecurityGroupCompute):
- # The security group to be deleted.
- _security_group = \
- compute_fakes.FakeSecurityGroup.create_one_security_group()
+ # The security groups to be deleted.
+ _security_groups = \
+ compute_fakes.FakeSecurityGroup.create_security_groups()
def setUp(self):
super(TestDeleteSecurityGroupCompute, self).setUp()
@@ -271,27 +325,80 @@ class TestDeleteSecurityGroupCompute(TestSecurityGroupCompute):
self.compute.security_groups.delete = mock.Mock(return_value=None)
- self.compute.security_groups.get = mock.Mock(
- return_value=self._security_group)
+ self.compute.security_groups.get = (
+ compute_fakes.FakeSecurityGroup.get_security_groups(
+ self._security_groups)
+ )
# Get the command object to test
self.cmd = security_group.DeleteSecurityGroup(self.app, None)
def test_security_group_delete(self):
arglist = [
- self._security_group.name,
+ self._security_groups[0].id,
]
verifylist = [
- ('group', self._security_group.name),
+ ('group', [self._security_groups[0].id]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.compute.security_groups.delete.assert_called_once_with(
- self._security_group.id)
+ self._security_groups[0].id)
+ self.assertIsNone(result)
+
+ def test_multi_security_groups_delete(self):
+ arglist = []
+ verifylist = []
+
+ for s in self._security_groups:
+ arglist.append(s.id)
+ verifylist = [
+ ('group', arglist),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ calls = []
+ for s in self._security_groups:
+ calls.append(call(s.id))
+ self.compute.security_groups.delete.assert_has_calls(calls)
self.assertIsNone(result)
+ def test_multi_security_groups_delete_with_exception(self):
+ arglist = [
+ self._security_groups[0].id,
+ 'unexist_security_group',
+ ]
+ verifylist = [
+ ('group',
+ [self._security_groups[0].id, 'unexist_security_group']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ find_mock_result = [self._security_groups[0], exceptions.CommandError]
+ self.compute.security_groups.get = (
+ mock.MagicMock(side_effect=find_mock_result)
+ )
+ self.compute.security_groups.find.side_effect = (
+ exceptions.NotFound(None))
+
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('1 of 2 group failed to delete.', str(e))
+
+ self.compute.security_groups.get.assert_any_call(
+ self._security_groups[0].id)
+ self.compute.security_groups.get.assert_any_call(
+ 'unexist_security_group')
+ self.compute.security_groups.delete.assert_called_once_with(
+ self._security_groups[0].id
+ )
+
class TestListSecurityGroupNetwork(TestSecurityGroupNetwork):
diff --git a/openstackclient/tests/network/v2/test_security_group_rule.py b/openstackclient/tests/network/v2/test_security_group_rule.py
index 2a64b884..b2862679 100644
--- a/openstackclient/tests/network/v2/test_security_group_rule.py
+++ b/openstackclient/tests/network/v2/test_security_group_rule.py
@@ -13,8 +13,10 @@
import copy
import mock
+from mock import call
+
+from osc_lib import exceptions
-from openstackclient.common import exceptions
from openstackclient.network import utils as network_utils
from openstackclient.network.v2 import security_group_rule
from openstackclient.tests.compute.v2 import fakes as compute_fakes
@@ -667,17 +669,20 @@ class TestCreateSecurityGroupRuleCompute(TestSecurityGroupRuleCompute):
class TestDeleteSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
- # The security group rule to be deleted.
- _security_group_rule = \
- network_fakes.FakeSecurityGroupRule.create_one_security_group_rule()
+ # The security group rules to be deleted.
+ _security_group_rules = \
+ network_fakes.FakeSecurityGroupRule.create_security_group_rules(
+ count=2)
def setUp(self):
super(TestDeleteSecurityGroupRuleNetwork, self).setUp()
self.network.delete_security_group_rule = mock.Mock(return_value=None)
- self.network.find_security_group_rule = mock.Mock(
- return_value=self._security_group_rule)
+ self.network.find_security_group_rule = (
+ network_fakes.FakeSecurityGroupRule.get_security_group_rules(
+ self._security_group_rules)
+ )
# Get the command object to test
self.cmd = security_group_rule.DeleteSecurityGroupRule(
@@ -685,25 +690,76 @@ class TestDeleteSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
def test_security_group_rule_delete(self):
arglist = [
- self._security_group_rule.id,
+ self._security_group_rules[0].id,
]
verifylist = [
- ('rule', self._security_group_rule.id),
+ ('rule', [self._security_group_rules[0].id]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.network.delete_security_group_rule.assert_called_once_with(
- self._security_group_rule)
+ self._security_group_rules[0])
self.assertIsNone(result)
+ def test_multi_security_group_rules_delete(self):
+ arglist = []
+ verifylist = []
+
+ for s in self._security_group_rules:
+ arglist.append(s.id)
+ verifylist = [
+ ('rule', arglist),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ calls = []
+ for s in self._security_group_rules:
+ calls.append(call(s))
+ self.network.delete_security_group_rule.assert_has_calls(calls)
+ self.assertIsNone(result)
+
+ def test_multi_security_group_rules_delete_with_exception(self):
+ arglist = [
+ self._security_group_rules[0].id,
+ 'unexist_rule',
+ ]
+ verifylist = [
+ ('rule',
+ [self._security_group_rules[0].id, 'unexist_rule']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ find_mock_result = [
+ self._security_group_rules[0], exceptions.CommandError]
+ self.network.find_security_group_rule = (
+ mock.MagicMock(side_effect=find_mock_result)
+ )
+
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('1 of 2 rule failed to delete.', str(e))
+
+ self.network.find_security_group_rule.assert_any_call(
+ self._security_group_rules[0].id, ignore_missing=False)
+ self.network.find_security_group_rule.assert_any_call(
+ 'unexist_rule', ignore_missing=False)
+ self.network.delete_security_group_rule.assert_called_once_with(
+ self._security_group_rules[0]
+ )
+
class TestDeleteSecurityGroupRuleCompute(TestSecurityGroupRuleCompute):
# The security group rule to be deleted.
- _security_group_rule = \
- compute_fakes.FakeSecurityGroupRule.create_one_security_group_rule()
+ _security_group_rules = \
+ compute_fakes.FakeSecurityGroupRule.create_security_group_rules(
+ count=2)
def setUp(self):
super(TestDeleteSecurityGroupRuleCompute, self).setUp()
@@ -715,19 +771,65 @@ class TestDeleteSecurityGroupRuleCompute(TestSecurityGroupRuleCompute):
def test_security_group_rule_delete(self):
arglist = [
- self._security_group_rule.id,
+ self._security_group_rules[0].id,
]
verifylist = [
- ('rule', self._security_group_rule.id),
+ ('rule', [self._security_group_rules[0].id]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.compute.security_group_rules.delete.assert_called_once_with(
- self._security_group_rule.id)
+ self._security_group_rules[0].id)
+ self.assertIsNone(result)
+
+ def test_multi_security_group_rules_delete(self):
+ arglist = []
+ verifylist = []
+
+ for s in self._security_group_rules:
+ arglist.append(s.id)
+ verifylist = [
+ ('rule', arglist),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ calls = []
+ for s in self._security_group_rules:
+ calls.append(call(s.id))
+ self.compute.security_group_rules.delete.assert_has_calls(calls)
self.assertIsNone(result)
+ def test_multi_security_group_rules_delete_with_exception(self):
+ arglist = [
+ self._security_group_rules[0].id,
+ 'unexist_rule',
+ ]
+ verifylist = [
+ ('rule',
+ [self._security_group_rules[0].id, 'unexist_rule']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ find_mock_result = [None, exceptions.CommandError]
+ self.compute.security_group_rules.delete = (
+ mock.MagicMock(side_effect=find_mock_result)
+ )
+
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('1 of 2 rule failed to delete.', str(e))
+
+ self.compute.security_group_rules.delete.assert_any_call(
+ self._security_group_rules[0].id)
+ self.compute.security_group_rules.delete.assert_any_call(
+ 'unexist_rule')
+
class TestListSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
diff --git a/openstackclient/tests/network/v2/test_subnet.py b/openstackclient/tests/network/v2/test_subnet.py
index 22c288f9..99b558c0 100644
--- a/openstackclient/tests/network/v2/test_subnet.py
+++ b/openstackclient/tests/network/v2/test_subnet.py
@@ -13,9 +13,11 @@
import copy
import mock
+from mock import call
+
+from osc_lib import exceptions
+from osc_lib import utils
-from openstackclient.common import exceptions
-from openstackclient.common import utils
from openstackclient.network.v2 import subnet as subnet_v2
from openstackclient.tests import fakes
from openstackclient.tests.identity.v3 import fakes as identity_fakes_v3
@@ -89,6 +91,14 @@ class TestCreateSubnet(TestSubnet):
}
)
+ # The network segment to be returned from find_segment
+ _network_segment = \
+ network_fakes.FakeNetworkSegment.create_one_network_segment(
+ attrs={
+ 'network_id': _subnet.network_id,
+ }
+ )
+
columns = (
'allocation_pools',
'cidr',
@@ -103,6 +113,7 @@ class TestCreateSubnet(TestSubnet):
'name',
'network_id',
'project_id',
+ 'segment_id',
'subnetpool_id',
)
@@ -120,6 +131,7 @@ class TestCreateSubnet(TestSubnet):
_subnet.name,
_subnet.network_id,
_subnet.project_id,
+ _subnet.segment_id,
_subnet.subnetpool_id,
)
@@ -137,6 +149,7 @@ class TestCreateSubnet(TestSubnet):
_subnet_from_pool.name,
_subnet_from_pool.network_id,
_subnet_from_pool.project_id,
+ _subnet_from_pool.segment_id,
_subnet_from_pool.subnetpool_id,
)
@@ -154,6 +167,7 @@ class TestCreateSubnet(TestSubnet):
_subnet_ipv6.name,
_subnet_ipv6.network_id,
_subnet_ipv6.project_id,
+ _subnet_ipv6.segment_id,
_subnet_ipv6.subnetpool_id,
)
@@ -187,6 +201,15 @@ class TestCreateSubnet(TestSubnet):
loaded=True,
)
+ # Mock SDK calls for all tests.
+ self.network.find_network = mock.Mock(return_value=self._network)
+ self.network.find_segment = mock.Mock(
+ return_value=self._network_segment
+ )
+ self.network.find_subnet_pool = mock.Mock(
+ return_value=self._subnet_pool
+ )
+
def test_create_no_options(self):
arglist = []
verifylist = []
@@ -197,11 +220,9 @@ class TestCreateSubnet(TestSubnet):
self.check_parser, self.cmd, arglist, verifylist)
def test_create_default_options(self):
- # Mock create_subnet and find_network sdk calls to return the
- # values we want for this test
+ # Mock SDK calls for this test.
self.network.create_subnet = mock.Mock(return_value=self._subnet)
self._network.id = self._subnet.network_id
- self.network.find_network = mock.Mock(return_value=self._network)
arglist = [
"--subnet-range", self._subnet.cidr,
@@ -231,14 +252,10 @@ class TestCreateSubnet(TestSubnet):
self.assertEqual(self.data, data)
def test_create_from_subnet_pool_options(self):
- # Mock create_subnet, find_subnet_pool, and find_network sdk calls
- # to return the values we want for this test
+ # Mock SDK calls for this test.
self.network.create_subnet = \
mock.Mock(return_value=self._subnet_from_pool)
self._network.id = self._subnet_from_pool.network_id
- self.network.find_network = mock.Mock(return_value=self._network)
- self.network.find_subnet_pool = \
- mock.Mock(return_value=self._subnet_pool)
arglist = [
self._subnet_from_pool.name,
@@ -291,11 +308,9 @@ class TestCreateSubnet(TestSubnet):
self.assertEqual(self.data_subnet_pool, data)
def test_create_options_subnet_range_ipv6(self):
- # Mock create_subnet and find_network sdk calls to return the
- # values we want for this test
+ # Mock SDK calls for this test.
self.network.create_subnet = mock.Mock(return_value=self._subnet_ipv6)
self._network.id = self._subnet_ipv6.network_id
- self.network.find_network = mock.Mock(return_value=self._network)
arglist = [
self._subnet_ipv6.name,
@@ -358,35 +373,138 @@ class TestCreateSubnet(TestSubnet):
self.assertEqual(self.columns, columns)
self.assertEqual(self.data_ipv6, data)
+ def test_create_no_beta_command_options(self):
+ arglist = [
+ "--subnet-range", self._subnet.cidr,
+ "--network-segment", self._network_segment.id,
+ "--network", self._subnet.network_id,
+ self._subnet.name,
+ ]
+ verifylist = [
+ ('name', self._subnet.name),
+ ('subnet_range', self._subnet.cidr),
+ ('network-segment', self._network_segment.id),
+ ('network', self._subnet.network_id),
+ ]
+ self.app.options.os_beta_command = False
+ self.assertRaises(tests_utils.ParserException,
+ self.check_parser, self.cmd, arglist, verifylist)
+
+ def test_create_with_network_segment(self):
+ # Mock SDK calls for this test.
+ self.network.create_subnet = mock.Mock(return_value=self._subnet)
+ self._network.id = self._subnet.network_id
+
+ arglist = [
+ "--subnet-range", self._subnet.cidr,
+ "--network-segment", self._network_segment.id,
+ "--network", self._subnet.network_id,
+ self._subnet.name,
+ ]
+ verifylist = [
+ ('name', self._subnet.name),
+ ('subnet_range', self._subnet.cidr),
+ ('network_segment', self._network_segment.id),
+ ('network', self._subnet.network_id),
+ ('ip_version', self._subnet.ip_version),
+ ('gateway', 'auto'),
+
+ ]
+
+ self.app.options.os_beta_command = True
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.create_subnet.assert_called_once_with(**{
+ 'cidr': self._subnet.cidr,
+ 'enable_dhcp': self._subnet.enable_dhcp,
+ 'ip_version': self._subnet.ip_version,
+ 'name': self._subnet.name,
+ 'network_id': self._subnet.network_id,
+ 'segment_id': self._network_segment.id,
+ })
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
class TestDeleteSubnet(TestSubnet):
- # The subnet to delete.
- _subnet = network_fakes.FakeSubnet.create_one_subnet()
+ # The subnets to delete.
+ _subnets = network_fakes.FakeSubnet.create_subnets(count=2)
def setUp(self):
super(TestDeleteSubnet, self).setUp()
self.network.delete_subnet = mock.Mock(return_value=None)
- self.network.find_subnet = mock.Mock(return_value=self._subnet)
+ self.network.find_subnet = (
+ network_fakes.FakeSubnet.get_subnets(self._subnets))
# Get the command object to test
self.cmd = subnet_v2.DeleteSubnet(self.app, self.namespace)
- def test_delete(self):
+ def test_subnet_delete(self):
arglist = [
- self._subnet.name,
+ self._subnets[0].name,
]
verifylist = [
- ('subnet', self._subnet.name),
+ ('subnet', [self._subnets[0].name]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
- self.network.delete_subnet.assert_called_once_with(self._subnet)
+ self.network.delete_subnet.assert_called_once_with(self._subnets[0])
self.assertIsNone(result)
+ def test_multi_subnets_delete(self):
+ arglist = []
+ verifylist = []
+
+ for s in self._subnets:
+ arglist.append(s.name)
+ verifylist = [
+ ('subnet', arglist),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ calls = []
+ for s in self._subnets:
+ calls.append(call(s))
+ self.network.delete_subnet.assert_has_calls(calls)
+ self.assertIsNone(result)
+
+ def test_multi_subnets_delete_with_exception(self):
+ arglist = [
+ self._subnets[0].name,
+ 'unexist_subnet',
+ ]
+ verifylist = [
+ ('subnet',
+ [self._subnets[0].name, 'unexist_subnet']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ find_mock_result = [self._subnets[0], exceptions.CommandError]
+ self.network.find_subnet = (
+ mock.MagicMock(side_effect=find_mock_result)
+ )
+
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('1 of 2 subnets failed to delete.', str(e))
+
+ self.network.find_subnet.assert_any_call(
+ self._subnets[0].name, ignore_missing=False)
+ self.network.find_subnet.assert_any_call(
+ 'unexist_subnet', ignore_missing=False)
+ self.network.delete_subnet.assert_called_once_with(
+ self._subnets[0]
+ )
+
class TestListSubnet(TestSubnet):
# The subnets going to be listed up.
@@ -549,8 +667,11 @@ class TestSetSubnet(TestSubnet):
verifylist = [('subnet', self._subnet.name)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.assertRaises(exceptions.CommandError, self.cmd.take_action,
- parsed_args)
+ result = self.cmd.take_action(parsed_args)
+
+ attrs = {}
+ self.network.update_subnet.assert_called_with(self._subnet, **attrs)
+ self.assertIsNone(result)
def test_append_options(self):
_testsubnet = network_fakes.FakeSubnet.create_one_subnet(
@@ -591,6 +712,7 @@ class TestShowSubnet(TestSubnet):
'name',
'network_id',
'project_id',
+ 'segment_id',
'subnetpool_id',
)
@@ -608,6 +730,7 @@ class TestShowSubnet(TestSubnet):
_subnet.name,
_subnet.network_id,
_subnet.tenant_id,
+ _subnet.segment_id,
_subnet.subnetpool_id,
)
diff --git a/openstackclient/tests/network/v2/test_subnet_pool.py b/openstackclient/tests/network/v2/test_subnet_pool.py
index de12c9e9..7a96b30f 100644
--- a/openstackclient/tests/network/v2/test_subnet_pool.py
+++ b/openstackclient/tests/network/v2/test_subnet_pool.py
@@ -14,9 +14,11 @@
import argparse
import copy
import mock
+from mock import call
+
+from osc_lib import exceptions
+from osc_lib import utils
-from openstackclient.common import exceptions
-from openstackclient.common import utils
from openstackclient.network.v2 import subnet_pool
from openstackclient.tests import fakes
from openstackclient.tests.identity.v3 import fakes as identity_fakes_v3
@@ -263,36 +265,85 @@ class TestCreateSubnetPool(TestSubnetPool):
class TestDeleteSubnetPool(TestSubnetPool):
- # The subnet pool to delete.
- _subnet_pool = network_fakes.FakeSubnetPool.create_one_subnet_pool()
+ # The subnet pools to delete.
+ _subnet_pools = network_fakes.FakeSubnetPool.create_subnet_pools(count=2)
def setUp(self):
super(TestDeleteSubnetPool, self).setUp()
self.network.delete_subnet_pool = mock.Mock(return_value=None)
- self.network.find_subnet_pool = mock.Mock(
- return_value=self._subnet_pool
+ self.network.find_subnet_pool = (
+ network_fakes.FakeSubnetPool.get_subnet_pools(self._subnet_pools)
)
# Get the command object to test
self.cmd = subnet_pool.DeleteSubnetPool(self.app, self.namespace)
- def test_delete(self):
+ def test_subnet_pool_delete(self):
arglist = [
- self._subnet_pool.name,
+ self._subnet_pools[0].name,
]
verifylist = [
- ('subnet_pool', self._subnet_pool.name),
+ ('subnet_pool', [self._subnet_pools[0].name]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.network.delete_subnet_pool.assert_called_once_with(
- self._subnet_pool)
+ self._subnet_pools[0])
+ self.assertIsNone(result)
+
+ def test_multi_subnet_pools_delete(self):
+ arglist = []
+ verifylist = []
+
+ for s in self._subnet_pools:
+ arglist.append(s.name)
+ verifylist = [
+ ('subnet_pool', arglist),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ calls = []
+ for s in self._subnet_pools:
+ calls.append(call(s))
+ self.network.delete_subnet_pool.assert_has_calls(calls)
self.assertIsNone(result)
+ def test_multi_subnet_pools_delete_with_exception(self):
+ arglist = [
+ self._subnet_pools[0].name,
+ 'unexist_subnet_pool',
+ ]
+ verifylist = [
+ ('subnet_pool',
+ [self._subnet_pools[0].name, 'unexist_subnet_pool']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ find_mock_result = [self._subnet_pools[0], exceptions.CommandError]
+ self.network.find_subnet_pool = (
+ mock.MagicMock(side_effect=find_mock_result)
+ )
+
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('1 of 2 subnet pools failed to delete.', str(e))
+
+ self.network.find_subnet_pool.assert_any_call(
+ self._subnet_pools[0].name, ignore_missing=False)
+ self.network.find_subnet_pool.assert_any_call(
+ 'unexist_subnet_pool', ignore_missing=False)
+ self.network.delete_subnet_pool.assert_called_once_with(
+ self._subnet_pools[0]
+ )
+
class TestListSubnetPool(TestSubnetPool):
# The subnet pools going to be listed up.
@@ -443,10 +494,14 @@ class TestSetSubnetPool(TestSubnetPool):
def test_set_nothing(self):
arglist = [self._subnet_pool.name, ]
verifylist = [('subnet_pool', self._subnet_pool.name), ]
+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
- self.assertRaises(exceptions.CommandError, self.cmd.take_action,
- parsed_args)
+ attrs = {}
+ self.network.update_subnet_pool.assert_called_once_with(
+ self._subnet_pool, **attrs)
+ self.assertIsNone(result)
def test_set_len_negative(self):
arglist = [
diff --git a/openstackclient/tests/test_shell.py b/openstackclient/tests/test_shell.py
index 90454fc2..7d0bbd12 100644
--- a/openstackclient/tests/test_shell.py
+++ b/openstackclient/tests/test_shell.py
@@ -1,4 +1,4 @@
-# Copyright 2012-2013 OpenStack, LLC.
+# Copyright 2012-2013 OpenStack Foundation
#
# 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
diff --git a/openstackclient/tests/volume/test_find_resource.py b/openstackclient/tests/volume/test_find_resource.py
index 00cc46a6..227d6ca7 100644
--- a/openstackclient/tests/volume/test_find_resource.py
+++ b/openstackclient/tests/volume/test_find_resource.py
@@ -17,9 +17,9 @@ import mock
from cinderclient.v1 import volume_snapshots
from cinderclient.v1 import volumes
+from osc_lib import exceptions
+from osc_lib import utils
-from openstackclient.common import exceptions
-from openstackclient.common import utils
from openstackclient.tests import utils as test_utils
from openstackclient.volume import client # noqa
diff --git a/openstackclient/tests/volume/v1/test_qos_specs.py b/openstackclient/tests/volume/v1/test_qos_specs.py
index 4943f5df..392017c6 100644
--- a/openstackclient/tests/volume/v1/test_qos_specs.py
+++ b/openstackclient/tests/volume/v1/test_qos_specs.py
@@ -15,7 +15,8 @@
import copy
-from openstackclient.common import utils
+from osc_lib import utils
+
from openstackclient.tests import fakes
from openstackclient.tests.volume.v1 import fakes as volume_fakes
from openstackclient.volume.v1 import qos_specs
diff --git a/openstackclient/tests/volume/v1/test_volume.py b/openstackclient/tests/volume/v1/test_volume.py
index e4f51bb5..380bc632 100644
--- a/openstackclient/tests/volume/v1/test_volume.py
+++ b/openstackclient/tests/volume/v1/test_volume.py
@@ -14,6 +14,7 @@
#
import copy
+import mock
from openstackclient.tests import fakes
from openstackclient.tests.identity.v2_0 import fakes as identity_fakes
@@ -656,7 +657,8 @@ class TestVolumeSet(TestVolume):
)
self.assertIsNone(result)
- def test_volume_set_size_smaller(self):
+ @mock.patch.object(volume.LOG, 'error')
+ def test_volume_set_size_smaller(self, mock_log_error):
arglist = [
'--size', '100',
volume_fakes.volume_name,
@@ -672,12 +674,13 @@ class TestVolumeSet(TestVolume):
result = self.cmd.take_action(parsed_args)
- self.assertEqual("New size must be greater than %s GB" %
- volume_fakes.volume_size,
- self.app.log.messages.get('error'))
+ mock_log_error.assert_called_with("New size must be greater "
+ "than %s GB",
+ volume_fakes.volume_size)
self.assertIsNone(result)
- def test_volume_set_size_not_available(self):
+ @mock.patch.object(volume.LOG, 'error')
+ def test_volume_set_size_not_available(self, mock_log_error):
self.volumes_mock.get.return_value.status = 'error'
arglist = [
'--size', '130',
@@ -694,9 +697,9 @@ class TestVolumeSet(TestVolume):
result = self.cmd.take_action(parsed_args)
- self.assertEqual("Volume is in %s state, it must be available before "
- "size can be extended" % 'error',
- self.app.log.messages.get('error'))
+ mock_log_error.assert_called_with("Volume is in %s state, it must be "
+ "available before size can be "
+ "extended", 'error')
self.assertIsNone(result)
def test_volume_set_property(self):
diff --git a/openstackclient/tests/volume/v2/fakes.py b/openstackclient/tests/volume/v2/fakes.py
index e61fe8aa..1cbbf68a 100644
--- a/openstackclient/tests/volume/v2/fakes.py
+++ b/openstackclient/tests/volume/v2/fakes.py
@@ -17,220 +17,13 @@ import mock
import random
import uuid
-from openstackclient.common import utils as common_utils
+from osc_lib import utils as common_utils
+
from openstackclient.tests import fakes
from openstackclient.tests.identity.v3 import fakes as identity_fakes
from openstackclient.tests.image.v2 import fakes as image_fakes
from openstackclient.tests import utils
-volume_attachment_server = {
- 'device': '/dev/ice',
- 'server_id': '1233',
-}
-
-volume_id = "ce26708d-a7f8-4b4b-9861-4a80256615a6"
-volume_name = "fake_volume"
-volume_description = "fake description"
-volume_status = "available"
-volume_size = 20
-volume_type = "fake_lvmdriver-1"
-volume_metadata = {
- 'Alpha': 'a',
- 'Beta': 'b',
- 'Gamma': 'g',
-}
-volume_metadata_str = "Alpha='a', Beta='b', Gamma='g'"
-volume_snapshot_id = 1
-volume_availability_zone = "nova"
-volume_attachments = [volume_attachment_server]
-
-VOLUME = {
- "id": volume_id,
- "name": volume_name,
- "description": volume_description,
- "status": volume_status,
- "size": volume_size,
- "volume_type": volume_type,
- "metadata": volume_metadata,
- "snapshot_id": volume_snapshot_id,
- "availability_zone": volume_availability_zone,
- "attachments": volume_attachments
-}
-
-VOLUME_columns = (
- "attachments",
- "availability_zone",
- "description",
- "id",
- "name",
- "properties",
- "size",
- "snapshot_id",
- "status",
- "type"
-)
-
-VOLUME_data = (
- volume_attachments,
- volume_availability_zone,
- volume_description,
- volume_id,
- volume_name,
- common_utils.format_dict(volume_metadata),
- volume_size,
- volume_snapshot_id,
- volume_status,
- volume_type
-)
-
-
-snapshot_id = "cb2d364e-4d1c-451a-8c68-b5bbcb340fb2"
-snapshot_name = "fake_snapshot"
-snapshot_description = "fake description"
-snapshot_size = 10
-snapshot_metadata = {
- "foo": "bar"
-}
-snapshot_volume_id = "bdbae8dc-e6ca-43c0-8076-951cc1b093a4"
-
-SNAPSHOT = {
- "id": snapshot_id,
- "name": snapshot_name,
- "description": snapshot_description,
- "size": snapshot_size,
- "status": "available",
- "metadata": snapshot_metadata,
- "created_at": "2015-06-03T18:49:19.000000",
- "volume_id": volume_name
-}
-EXPECTED_SNAPSHOT = copy.deepcopy(SNAPSHOT)
-EXPECTED_SNAPSHOT.pop("metadata")
-EXPECTED_SNAPSHOT['properties'] = "foo='bar'"
-SNAPSHOT_columns = tuple(sorted(EXPECTED_SNAPSHOT))
-SNAPSHOT_data = tuple((EXPECTED_SNAPSHOT[x]
- for x in sorted(EXPECTED_SNAPSHOT)))
-
-
-type_id = "5520dc9e-6f9b-4378-a719-729911c0f407"
-type_description = "fake description"
-type_name = "fake-lvmdriver-1"
-type_extra_specs = {
- "foo": "bar"
-}
-
-TYPE = {
- 'id': type_id,
- 'name': type_name,
- 'description': type_description,
- 'extra_specs': type_extra_specs
-}
-
-TYPE_columns = tuple(sorted(TYPE))
-TYPE_data = tuple((TYPE[x] for x in sorted(TYPE)))
-
-formatted_type_properties = "foo='bar'"
-TYPE_FORMATTED = {
- 'id': type_id,
- 'name': type_name,
- 'description': type_description,
- 'properties': formatted_type_properties
-}
-TYPE_FORMATTED_columns = tuple(sorted(TYPE_FORMATTED))
-TYPE_FORMATTED_data = tuple((TYPE_FORMATTED[x] for x in
- sorted(TYPE_FORMATTED)))
-
-backup_id = "3c409fe6-4d03-4a06-aeab-18bdcdf3c8f4"
-backup_volume_id = "bdbae8dc-e6ca-43c0-8076-951cc1b093a4"
-backup_name = "fake_backup"
-backup_description = "fake description"
-backup_object_count = None
-backup_container = None
-backup_size = 10
-backup_status = "error"
-
-BACKUP = {
- "id": backup_id,
- "name": backup_name,
- "volume_id": backup_volume_id,
- "description": backup_description,
- "object_count": backup_object_count,
- "container": backup_container,
- "size": backup_size,
- "status": backup_status,
- "availability_zone": volume_availability_zone,
-}
-
-BACKUP_columns = tuple(sorted(BACKUP))
-BACKUP_data = tuple((BACKUP[x] for x in sorted(BACKUP)))
-
-qos_id = '6f2be1de-997b-4230-b76c-a3633b59e8fb'
-qos_consumer = 'front-end'
-qos_default_consumer = 'both'
-qos_name = "fake-qos-specs"
-qos_specs = {
- 'foo': 'bar',
- 'iops': '9001'
-}
-qos_association = {
- 'association_type': 'volume_type',
- 'name': type_name,
- 'id': type_id
-}
-
-QOS = {
- 'id': qos_id,
- 'consumer': qos_consumer,
- 'name': qos_name
-}
-
-QOS_DEFAULT_CONSUMER = {
- 'id': qos_id,
- 'consumer': qos_default_consumer,
- 'name': qos_name
-}
-
-QOS_WITH_SPECS = {
- 'id': qos_id,
- 'consumer': qos_consumer,
- 'name': qos_name,
- 'specs': qos_specs
-}
-
-QOS_WITH_ASSOCIATIONS = {
- 'id': qos_id,
- 'consumer': qos_consumer,
- 'name': qos_name,
- 'specs': qos_specs,
- 'associations': [qos_association]
-}
-
-image_id = 'im1'
-image_name = 'graven'
-IMAGE = {
- 'id': image_id,
- 'name': image_name
-}
-
-extension_name = 'SchedulerHints'
-extension_namespace = 'http://docs.openstack.org/'\
- 'block-service/ext/scheduler-hints/api/v2'
-extension_description = 'Pass arbitrary key/value'\
- 'pairs to the scheduler.'
-extension_updated = '2013-04-18T00:00:00+00:00'
-extension_alias = 'OS-SCH-HNT'
-extension_links = '[{"href":'\
- '"https://github.com/openstack/block-api", "type":'\
- ' "text/html", "rel": "describedby"}]'
-
-EXTENSION = {
- 'name': extension_name,
- 'namespace': extension_namespace,
- 'description': extension_description,
- 'updated': extension_updated,
- 'alias': extension_alias,
- 'links': extension_links,
-}
-
class FakeTransferClient(object):
@@ -379,6 +172,8 @@ class FakeVolumeClient(object):
def __init__(self, **kwargs):
self.volumes = mock.Mock()
self.volumes.resource_class = fakes.FakeResource(None, {})
+ self.extensions = mock.Mock()
+ self.extensions.resource_class = fakes.FakeResource(None, {})
self.volume_snapshots = mock.Mock()
self.volume_snapshots.resource_class = fakes.FakeResource(None, {})
self.backups = mock.Mock()
@@ -643,6 +438,42 @@ class FakeBackup(object):
return backups
+class FakeExtension(object):
+ """Fake one or more extension."""
+
+ @staticmethod
+ def create_one_extension(attrs=None):
+ """Create a fake extension.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ A FakeResource object with name, namespace, etc.
+ """
+ attrs = attrs or {}
+
+ # Set default attributes.
+ extension_info = {
+ 'name': 'name-' + uuid.uuid4().hex,
+ 'namespace': ('http://docs.openstack.org/'
+ 'block-service/ext/scheduler-hints/api/v2'),
+ 'description': 'description-' + uuid.uuid4().hex,
+ 'updated': '2013-04-18T00:00:00+00:00',
+ 'alias': 'OS-SCH-HNT',
+ 'links': ('[{"href":'
+ '"https://github.com/openstack/block-api", "type":'
+ ' "text/html", "rel": "describedby"}]'),
+ }
+
+ # Overwrite default attributes.
+ extension_info.update(attrs)
+
+ extension = fakes.FakeResource(
+ info=copy.deepcopy(extension_info),
+ loaded=True)
+ return extension
+
+
class FakeQos(object):
"""Fake one or more Qos specification."""
diff --git a/openstackclient/tests/volume/v2/test_backup.py b/openstackclient/tests/volume/v2/test_backup.py
index 8a151a91..ba0f1c18 100644
--- a/openstackclient/tests/volume/v2/test_backup.py
+++ b/openstackclient/tests/volume/v2/test_backup.py
@@ -72,12 +72,14 @@ class TestBackupCreate(TestBackup):
"--name", self.new_backup.name,
"--description", self.new_backup.description,
"--container", self.new_backup.container,
+ "--force",
self.new_backup.volume_id,
]
verifylist = [
("name", self.new_backup.name),
("description", self.new_backup.description),
("container", self.new_backup.container),
+ ("force", True),
("volume", self.new_backup.volume_id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -88,7 +90,8 @@ class TestBackupCreate(TestBackup):
self.new_backup.volume_id,
container=self.new_backup.container,
name=self.new_backup.name,
- description=self.new_backup.description
+ description=self.new_backup.description,
+ force=True,
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
@@ -112,7 +115,8 @@ class TestBackupCreate(TestBackup):
self.new_backup.volume_id,
container=self.new_backup.container,
name=None,
- description=self.new_backup.description
+ description=self.new_backup.description,
+ force=False,
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
diff --git a/openstackclient/tests/volume/v2/test_qos_specs.py b/openstackclient/tests/volume/v2/test_qos_specs.py
index 741f4e70..11047535 100644
--- a/openstackclient/tests/volume/v2/test_qos_specs.py
+++ b/openstackclient/tests/volume/v2/test_qos_specs.py
@@ -13,9 +13,9 @@
# under the License.
#
-from openstackclient.common import utils
from openstackclient.tests.volume.v2 import fakes as volume_fakes
from openstackclient.volume.v2 import qos_specs
+from osc_lib import utils
class TestQos(volume_fakes.TestVolume):
diff --git a/openstackclient/tests/volume/v2/test_snapshot.py b/openstackclient/tests/volume/v2/test_snapshot.py
index fe6fbb52..ef199cbc 100644
--- a/openstackclient/tests/volume/v2/test_snapshot.py
+++ b/openstackclient/tests/volume/v2/test_snapshot.py
@@ -12,7 +12,8 @@
# under the License.
#
-from openstackclient.common import utils
+from osc_lib import utils
+
from openstackclient.tests.volume.v2 import fakes as volume_fakes
from openstackclient.volume.v2 import snapshot
diff --git a/openstackclient/tests/volume/v2/test_type.py b/openstackclient/tests/volume/v2/test_type.py
index 10c38612..174f33f2 100644
--- a/openstackclient/tests/volume/v2/test_type.py
+++ b/openstackclient/tests/volume/v2/test_type.py
@@ -14,7 +14,8 @@
import copy
-from openstackclient.common import utils
+from osc_lib import utils
+
from openstackclient.tests import fakes
from openstackclient.tests.identity.v3 import fakes as identity_fakes
from openstackclient.tests import utils as tests_utils
@@ -127,13 +128,13 @@ class TestTypeDelete(TestType):
self.volume_type.id
]
verifylist = [
- ("volume_type", self.volume_type.id)
+ ("volume_types", [self.volume_type.id])
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
- self.types_mock.delete.assert_called_with(self.volume_type.id)
+ self.types_mock.delete.assert_called_with(self.volume_type)
self.assertIsNone(result)
diff --git a/openstackclient/tests/volume/v2/test_volume.py b/openstackclient/tests/volume/v2/test_volume.py
index fb48d8ac..68158df0 100644
--- a/openstackclient/tests/volume/v2/test_volume.py
+++ b/openstackclient/tests/volume/v2/test_volume.py
@@ -16,7 +16,8 @@ import copy
from mock import call
-from openstackclient.common import utils
+from osc_lib import utils
+
from openstackclient.tests import fakes
from openstackclient.tests.identity.v3 import fakes as identity_fakes
from openstackclient.tests.image.v2 import fakes as image_fakes
diff --git a/openstackclient/volume/client.py b/openstackclient/volume/client.py
index a60f4b0e..ea0c441c 100644
--- a/openstackclient/volume/client.py
+++ b/openstackclient/volume/client.py
@@ -1,4 +1,4 @@
-# Copyright 2012-2013 OpenStack, LLC.
+# Copyright 2012-2013 OpenStack Foundation
#
# 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
@@ -15,7 +15,8 @@
import logging
-from openstackclient.common import utils
+from osc_lib import utils
+
from openstackclient.i18n import _
LOG = logging.getLogger(__name__)
diff --git a/openstackclient/volume/v1/backup.py b/openstackclient/volume/v1/backup.py
index 607b5211..5f34a2c5 100644
--- a/openstackclient/volume/v1/backup.py
+++ b/openstackclient/volume/v1/backup.py
@@ -16,10 +16,11 @@
"""Volume v1 Backup action implementations"""
import copy
+
+from osc_lib.command import command
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import utils
from openstackclient.i18n import _
@@ -75,7 +76,7 @@ class DeleteBackup(command.Command):
'backups',
metavar='<backup>',
nargs="+",
- help=_('Backup(s) to delete (ID only)'),
+ help=_('Backup(s) to delete (name or ID)'),
)
return parser
@@ -149,7 +150,7 @@ class RestoreBackup(command.Command):
parser.add_argument(
'backup',
metavar='<backup>',
- help=_('Backup to restore (ID only)')
+ help=_('Backup to restore (name or ID)')
)
parser.add_argument(
'volume',
@@ -176,7 +177,7 @@ class ShowBackup(command.ShowOne):
parser.add_argument(
'backup',
metavar='<backup>',
- help=_('Backup to display (ID only)')
+ help=_('Backup to display (name or ID)')
)
return parser
diff --git a/openstackclient/volume/v1/qos_specs.py b/openstackclient/volume/v1/qos_specs.py
index c49477a0..56a96256 100644
--- a/openstackclient/volume/v1/qos_specs.py
+++ b/openstackclient/volume/v1/qos_specs.py
@@ -15,11 +15,11 @@
"""Volume v1 QoS action implementations"""
+from osc_lib.cli import parseractions
+from osc_lib.command import command
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import parseractions
-from openstackclient.common import utils
from openstackclient.i18n import _
diff --git a/openstackclient/volume/v1/service.py b/openstackclient/volume/v1/service.py
index 023dda98..2df38573 100644
--- a/openstackclient/volume/v1/service.py
+++ b/openstackclient/volume/v1/service.py
@@ -14,8 +14,9 @@
"""Service action implementations"""
-from openstackclient.common import command
-from openstackclient.common import utils
+from osc_lib.command import command
+from osc_lib import utils
+
from openstackclient.i18n import _
diff --git a/openstackclient/volume/v1/snapshot.py b/openstackclient/volume/v1/snapshot.py
index 5132d71e..bb3a1fc3 100644
--- a/openstackclient/volume/v1/snapshot.py
+++ b/openstackclient/volume/v1/snapshot.py
@@ -1,4 +1,4 @@
-# Copyright 2012-2013 OpenStack, LLC.
+# Copyright 2012-2013 OpenStack Foundation
#
# 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
@@ -16,11 +16,12 @@
"""Volume v1 Snapshot action implementations"""
import copy
+
+from osc_lib.cli import parseractions
+from osc_lib.command import command
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import parseractions
-from openstackclient.common import utils
from openstackclient.i18n import _
diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py
index ffec1803..e11aa1a7 100644
--- a/openstackclient/volume/v1/volume.py
+++ b/openstackclient/volume/v1/volume.py
@@ -16,14 +16,19 @@
"""Volume v1 Volume action implementations"""
import argparse
+import logging
+
+from osc_lib.cli import parseractions
+from osc_lib.command import command
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import parseractions
-from openstackclient.common import utils
from openstackclient.i18n import _
+LOG = logging.getLogger(__name__)
+
+
class CreateVolume(command.ShowOne):
"""Create new volume"""
@@ -342,13 +347,12 @@ class SetVolume(command.Command):
if parsed_args.size:
if volume.status != 'available':
- self.app.log.error(_("Volume is in %s state, it must be "
- "available before size can be extended") %
- volume.status)
+ LOG.error(_("Volume is in %s state, it must be available "
+ "before size can be extended"), volume.status)
return
if parsed_args.size <= volume.size:
- self.app.log.error(_("New size must be greater than %s GB") %
- volume.size)
+ LOG.error(_("New size must be greater than %s GB"),
+ volume.size)
return
volume_client.volumes.extend(volume.id, parsed_args.size)
diff --git a/openstackclient/volume/v1/volume_transfer_request.py b/openstackclient/volume/v1/volume_transfer_request.py
index 98689e7b..5d8ff683 100644
--- a/openstackclient/volume/v1/volume_transfer_request.py
+++ b/openstackclient/volume/v1/volume_transfer_request.py
@@ -14,9 +14,9 @@
"""Volume v2 transfer action implementations"""
+from osc_lib.command import command
+from osc_lib import utils
-from openstackclient.common import command
-from openstackclient.common import utils
from openstackclient.i18n import _
diff --git a/openstackclient/volume/v1/volume_type.py b/openstackclient/volume/v1/volume_type.py
index 4e9b1920..3fe4fa05 100644
--- a/openstackclient/volume/v1/volume_type.py
+++ b/openstackclient/volume/v1/volume_type.py
@@ -15,14 +15,20 @@
"""Volume v1 Type action implementations"""
+import logging
+
+from osc_lib.cli import parseractions
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import parseractions
-from openstackclient.common import utils
from openstackclient.i18n import _
+LOG = logging.getLogger(__name__)
+
+
class CreateVolumeType(command.ShowOne):
"""Create new volume type"""
@@ -56,22 +62,39 @@ class CreateVolumeType(command.ShowOne):
class DeleteVolumeType(command.Command):
- """Delete volume type"""
+ """Delete volume type(s)"""
def get_parser(self, prog_name):
parser = super(DeleteVolumeType, self).get_parser(prog_name)
parser.add_argument(
- 'volume_type',
+ 'volume_types',
metavar='<volume-type>',
- help=_('Volume type to delete (name or ID)'),
+ nargs='+',
+ help=_('Volume type(s) to delete (name or ID)'),
)
return parser
def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
- volume_type_id = utils.find_resource(
- volume_client.volume_types, parsed_args.volume_type).id
- volume_client.volume_types.delete(volume_type_id)
+ result = 0
+
+ for volume_type in parsed_args.volume_types:
+ try:
+ vol_type = utils.find_resource(volume_client.volume_types,
+ volume_type)
+
+ volume_client.volume_types.delete(vol_type)
+ except Exception as e:
+ result += 1
+ LOG.error(_("Failed to delete volume type with "
+ "name or ID '%(volume_type)s': %(e)s")
+ % {'volume_type': volume_type, 'e': e})
+
+ if result > 0:
+ total = len(parsed_args.volume_types)
+ msg = (_("%(result)s of %(total)s volume types failed "
+ "to delete.") % {'result': result, 'total': total})
+ raise exceptions.CommandError(msg)
class ListVolumeType(command.Lister):
diff --git a/openstackclient/volume/v2/backup.py b/openstackclient/volume/v2/backup.py
index e6fbe78d..519913a9 100644
--- a/openstackclient/volume/v2/backup.py
+++ b/openstackclient/volume/v2/backup.py
@@ -16,10 +16,10 @@
import copy
+from osc_lib.command import command
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import utils
from openstackclient.i18n import _
@@ -48,6 +48,12 @@ class CreateBackup(command.ShowOne):
metavar="<container>",
help=_("Optional backup container name")
)
+ parser.add_argument(
+ '--force',
+ action='store_true',
+ default=False,
+ help=_("Allow to back up an in-use volume")
+ )
return parser
def take_action(self, parsed_args):
@@ -58,7 +64,8 @@ class CreateBackup(command.ShowOne):
volume_id,
container=parsed_args.container,
name=parsed_args.name,
- description=parsed_args.description
+ description=parsed_args.description,
+ force=parsed_args.force,
)
backup._info.pop("links", None)
return zip(*sorted(six.iteritems(backup._info)))
@@ -147,7 +154,7 @@ class RestoreBackup(command.ShowOne):
parser.add_argument(
"backup",
metavar="<backup>",
- help=_("Backup to restore (ID only)")
+ help=_("Backup to restore (name or ID)")
)
parser.add_argument(
"volume",
diff --git a/openstackclient/volume/v2/qos_specs.py b/openstackclient/volume/v2/qos_specs.py
index 90e11c77..7ec272b3 100644
--- a/openstackclient/volume/v2/qos_specs.py
+++ b/openstackclient/volume/v2/qos_specs.py
@@ -15,11 +15,11 @@
"""Volume v2 QoS action implementations"""
+from osc_lib.cli import parseractions
+from osc_lib.command import command
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import parseractions
-from openstackclient.common import utils
from openstackclient.i18n import _
diff --git a/openstackclient/volume/v2/service.py b/openstackclient/volume/v2/service.py
index 023dda98..2df38573 100644
--- a/openstackclient/volume/v2/service.py
+++ b/openstackclient/volume/v2/service.py
@@ -14,8 +14,9 @@
"""Service action implementations"""
-from openstackclient.common import command
-from openstackclient.common import utils
+from osc_lib.command import command
+from osc_lib import utils
+
from openstackclient.i18n import _
diff --git a/openstackclient/volume/v2/snapshot.py b/openstackclient/volume/v2/snapshot.py
index d9017080..439904e7 100644
--- a/openstackclient/volume/v2/snapshot.py
+++ b/openstackclient/volume/v2/snapshot.py
@@ -16,11 +16,11 @@
import copy
+from osc_lib.cli import parseractions
+from osc_lib.command import command
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import parseractions
-from openstackclient.common import utils
from openstackclient.i18n import _
diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py
index 18473da3..be2388fb 100644
--- a/openstackclient/volume/v2/volume.py
+++ b/openstackclient/volume/v2/volume.py
@@ -15,16 +15,20 @@
"""Volume V2 Volume action implementations"""
import copy
+import logging
+from osc_lib.cli import parseractions
+from osc_lib.command import command
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import parseractions
-from openstackclient.common import utils
from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
+LOG = logging.getLogger(__name__)
+
+
class CreateVolume(command.ShowOne):
"""Create new volume"""
@@ -361,13 +365,12 @@ class SetVolume(command.Command):
if parsed_args.size:
if volume.status != 'available':
- self.app.log.error(_("Volume is in %s state, it must be "
- "available before size can be extended") %
- volume.status)
+ LOG.error(_("Volume is in %s state, it must be available "
+ "before size can be extended"), volume.status)
return
if parsed_args.size <= volume.size:
- self.app.log.error(_("New size must be greater than %s GB") %
- volume.size)
+ LOG.error(_("New size must be greater than %s GB"),
+ volume.size)
return
volume_client.volumes.extend(volume.id, parsed_args.size)
@@ -454,6 +457,3 @@ class UnsetVolume(command.Command):
if parsed_args.image_property:
volume_client.volumes.delete_image_metadata(
volume.id, parsed_args.image_property)
-
- if (not parsed_args.image_property and not parsed_args.property):
- self.app.log.error(_("No changes requested\n"))
diff --git a/openstackclient/volume/v2/volume_transfer_request.py b/openstackclient/volume/v2/volume_transfer_request.py
index 98689e7b..5d8ff683 100644
--- a/openstackclient/volume/v2/volume_transfer_request.py
+++ b/openstackclient/volume/v2/volume_transfer_request.py
@@ -14,9 +14,9 @@
"""Volume v2 transfer action implementations"""
+from osc_lib.command import command
+from osc_lib import utils
-from openstackclient.common import command
-from openstackclient.common import utils
from openstackclient.i18n import _
diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py
index 9aed17bc..87f4c547 100644
--- a/openstackclient/volume/v2/volume_type.py
+++ b/openstackclient/volume/v2/volume_type.py
@@ -14,16 +14,21 @@
"""Volume v2 Type action implementations"""
+import logging
+
+from osc_lib.cli import parseractions
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
import six
-from openstackclient.common import command
-from openstackclient.common import exceptions
-from openstackclient.common import parseractions
-from openstackclient.common import utils
from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
+LOG = logging.getLogger(__name__)
+
+
class CreateVolumeType(command.ShowOne):
"""Create new volume type"""
@@ -87,22 +92,39 @@ class CreateVolumeType(command.ShowOne):
class DeleteVolumeType(command.Command):
- """Delete volume type"""
+ """Delete volume type(s)"""
def get_parser(self, prog_name):
parser = super(DeleteVolumeType, self).get_parser(prog_name)
parser.add_argument(
- "volume_type",
+ "volume_types",
metavar="<volume-type>",
- help=_("Volume type to delete (name or ID)")
+ nargs="+",
+ help=_("Volume type(s) to delete (name or ID)")
)
return parser
def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
- volume_type = utils.find_resource(
- volume_client.volume_types, parsed_args.volume_type)
- volume_client.volume_types.delete(volume_type.id)
+ result = 0
+
+ for volume_type in parsed_args.volume_types:
+ try:
+ vol_type = utils.find_resource(volume_client.volume_types,
+ volume_type)
+
+ volume_client.volume_types.delete(vol_type)
+ except Exception as e:
+ result += 1
+ LOG.error(_("Failed to delete volume type with "
+ "name or ID '%(volume_type)s': %(e)s")
+ % {'volume_type': volume_type, 'e': e})
+
+ if result > 0:
+ total = len(parsed_args.volume_types)
+ msg = (_("%(result)s of %(total)s volume types failed "
+ "to delete.") % {'result': result, 'total': total})
+ raise exceptions.CommandError(msg)
class ListVolumeType(command.Lister):
@@ -190,16 +212,15 @@ class SetVolumeType(command.Command):
**kwargs
)
except Exception as e:
- self.app.log.error(_("Failed to update volume type name or"
- " description: %s") % str(e))
+ LOG.error(_("Failed to update volume type name or"
+ " description: %s"), e)
result += 1
if parsed_args.property:
try:
volume_type.set_keys(parsed_args.property)
except Exception as e:
- self.app.log.error(_("Failed to set volume type"
- " property: %s") % str(e))
+ LOG.error(_("Failed to set volume type property: %s"), e)
result += 1
if parsed_args.project:
@@ -213,13 +234,13 @@ class SetVolumeType(command.Command):
volume_client.volume_type_access.add_project_access(
volume_type.id, project_info.id)
except Exception as e:
- self.app.log.error(_("Failed to set volume type access to"
- " project: %s") % str(e))
+ LOG.error(_("Failed to set volume type access to "
+ "project: %s"), e)
result += 1
if result > 0:
raise exceptions.CommandError(_("Command Failed: One or more of"
- " the operations failed"))
+ " the operations failed"))
class ShowVolumeType(command.ShowOne):
@@ -284,8 +305,7 @@ class UnsetVolumeType(command.Command):
try:
volume_type.unset_keys(parsed_args.property)
except Exception as e:
- self.app.log.error(_("Failed to unset volume type property: %s"
- ) % str(e))
+ LOG.error(_("Failed to unset volume type property: %s"), e)
result += 1
if parsed_args.project:
@@ -299,8 +319,8 @@ class UnsetVolumeType(command.Command):
volume_client.volume_type_access.remove_project_access(
volume_type.id, project_info.id)
except Exception as e:
- self.app.log.error(_("Failed to remove volume type access from"
- " project: %s") % str(e))
+ LOG.error(_("Failed to remove volume type access from "
+ "project: %s"), e)
result += 1
if result > 0: