diff options
Diffstat (limited to 'openstackclient')
161 files changed, 3361 insertions, 4467 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..b56035e4 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'): @@ -115,7 +108,7 @@ 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) # grab tenant from project for v2.0 API compatibility if auth_plugin_name.startswith("v2"): if 'project_id' in auth_params: @@ -127,12 +120,12 @@ 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 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) + return (auth_plugin_loader, auth_params) def check_valid_auth_options(options, auth_plugin_name, required_scope=True): @@ -178,7 +171,8 @@ def check_valid_auth_options(options, auth_plugin_name, required_scope=True): 'auth.url')) 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 +182,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 +215,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..36dc5160 100644 --- a/openstackclient/api/auth_plugin.py +++ b/openstackclient/api/auth_plugin.py @@ -18,13 +18,15 @@ import logging from oslo_config import cfg from six.moves.urllib import parse as urlparse -from keystoneclient.auth.identity.generic import password as ksc_password -from keystoneclient.auth import token_endpoint +from keystoneauth1.loading._plugins import admin_token as token_endpoint +from keystoneauth1.loading._plugins.identity import generic as ksa_password + +from openstackclient.i18n import _ LOG = logging.getLogger(__name__) -class TokenEndpoint(token_endpoint.Token): +class TokenEndpoint(token_endpoint.AdminToken): """Auth plugin to handle traditional token/endpoint usage Implements the methods required to handle token authentication @@ -36,36 +38,31 @@ class TokenEndpoint(token_endpoint.Token): is for bootstrapping the Keystone database. """ - def __init__(self, url, token, **kwargs): + def load_from_options(self, url, token): """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'), + help=_('Specific service endpoint to use')), cfg.StrOpt('token', secret=True, - help='Authentication token to use'), + help=_('Authentication token to use')), ]) return options -class OSCGenericPassword(ksc_password.Password): +class OSCGenericPassword(ksa_password.Password): """Auth plugin hack to work around broken Keystone configurations The default Keystone configuration uses http://localhost:xxxx in 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..af161d1f 100644 --- a/openstackclient/common/availability_zone.py +++ b/openstackclient/common/availability_zone.py @@ -16,10 +16,10 @@ import copy 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 _ @@ -92,17 +92,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', diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py index 9c2b320c..04f624d0 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 @@ -269,7 +269,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 fee4559e..29c1534d 100644 --- a/openstackclient/common/command.py +++ b/openstackclient/common/command.py @@ -12,35 +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 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) - - -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..d6e2ab45 100644 --- a/openstackclient/common/configuration.py +++ b/openstackclient/common/configuration.py @@ -13,9 +13,10 @@ """Configuration action implementations""" +from osc_lib.command import command import six -from openstackclient.common import command +from openstackclient.i18n import _ REDACTED = "<redacted>" @@ -31,13 +32,13 @@ 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 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..327fc223 100644 --- a/openstackclient/common/extension.py +++ b/openstackclient/common/extension.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 _ class ListExtension(command.Lister): @@ -30,27 +32,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): 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 e177fbce..087d8ea3 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 @@ -142,7 +144,7 @@ class SetQuota(command.Command): if (compute_kwargs == {} and volume_kwargs == {} and network_kwargs == {}): - sys.stderr.write("No quotas updated") + sys.stderr.write("No quotas updated\n") return if parsed_args.project: @@ -187,7 +189,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 +197,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 82f09cec..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,8 +15,11 @@ 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__) @@ -82,9 +85,9 @@ def build_option_parser(parser): '--os-compute-api-version', metavar='<compute-api-version>', default=utils.env('OS_COMPUTE_API_VERSION'), - help='Compute API version, default=' + - DEFAULT_API_VERSION + - ' (Env: OS_COMPUTE_API_VERSION)') + help=_("Compute API version, default=%s " + "(Env: OS_COMPUTE_API_VERSION)") % DEFAULT_API_VERSION + ) return parser @@ -118,10 +121,9 @@ def check_api_version(check_version): novaclient.API_MIN_VERSION, novaclient.API_MAX_VERSION, ): - raise exceptions.CommandError( - "versions supported by client: %s - %s" % ( - novaclient.API_MIN_VERSION.get_string(), - novaclient.API_MAX_VERSION.get_string(), - ), - ) + msg = _("versions supported by client: %(min)s - %(max)s") % { + "min": novaclient.API_MIN_VERSION.get_string(), + "max": novaclient.API_MAX_VERSION.get_string(), + } + raise exceptions.CommandError(msg) return True diff --git a/openstackclient/compute/v2/agent.py b/openstackclient/compute/v2/agent.py index e358399d..ce6898c1 100644 --- a/openstackclient/compute/v2/agent.py +++ b/openstackclient/compute/v2/agent.py @@ -15,10 +15,11 @@ """Agent 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 utils from openstackclient.i18n import _ @@ -75,20 +76,35 @@ class CreateAgent(command.ShowOne): class DeleteAgent(command.Command): - """Delete compute agent command""" + """Delete compute agent(s)""" def get_parser(self, prog_name): parser = super(DeleteAgent, self).get_parser(prog_name) parser.add_argument( "id", metavar="<id>", - help=_("ID of agent to delete") + nargs='+', + help=_("ID of agent(s) to delete") ) return parser def take_action(self, parsed_args): compute_client = self.app.client_manager.compute - compute_client.agents.delete(parsed_args.id) + result = 0 + for id in parsed_args.id: + try: + 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}) + + if result > 0: + total = len(parsed_args.id) + msg = (_("%(result)s of %(total)s agents failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class ListAgent(command.Lister): diff --git a/openstackclient/compute/v2/aggregate.py b/openstackclient/compute/v2/aggregate.py index 752e0fdf..8000c93a 100644 --- a/openstackclient/compute/v2/aggregate.py +++ b/openstackclient/compute/v2/aggregate.py @@ -16,11 +16,11 @@ """Compute v2 Aggregate 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/compute/v2/console.py b/openstackclient/compute/v2/console.py index 1165862c..97d3f318 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 _ 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..ab2bc85d 100644 --- a/openstackclient/compute/v2/flavor.py +++ b/openstackclient/compute/v2/flavor.py @@ -15,12 +15,12 @@ """Flavor action implementations""" +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 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..764b22af 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,8 +15,9 @@ """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 _ 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 8a58e8f2..13c0a99c 100644 --- a/openstackclient/compute/v2/keypair.py +++ b/openstackclient/compute/v2/keypair.py @@ -17,12 +17,13 @@ import io 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 _ @@ -84,7 +85,7 @@ class DeleteKeypair(command.Command): parser.add_argument( 'name', metavar='<key>', - help=_("Public key to delete") + help=_("Public key to delete (name only)") ) return parser @@ -118,7 +119,7 @@ class ShowKeypair(command.ShowOne): parser.add_argument( 'name', metavar='<key>', - help=_("Public key to display") + help=_("Public key to display (name only)") ) parser.add_argument( '--public-key', diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 16384074..42736d66 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -19,19 +19,19 @@ import argparse import getpass import io 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 @@ -1390,7 +1390,7 @@ class ShowServer(command.ShowOne): if parsed_args.diagnostics: (resp, data) = server.diagnostics() if not resp.status_code == 200: - sys.stderr.write(_("Error retrieving diagnostics data")) + sys.stderr.write(_("Error retrieving diagnostics data\n")) return ({}, {}) else: data = _prep_server_detail(compute_client, server) 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 7baa6fe7..955b147e 100644 --- a/openstackclient/compute/v2/server_group.py +++ b/openstackclient/compute/v2/server_group.py @@ -15,9 +15,10 @@ """Compute v2 Server Group 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 exceptions +from osc_lib import utils + from openstackclient.i18n import _ @@ -129,15 +130,7 @@ class ListServerGroup(command.Lister): data = compute_client.server_groups.list(parsed_args.all_projects) if parsed_args.long: - column_headers = ( - 'ID', - 'Name', - 'Policies', - 'Members', - 'Project Id', - 'User Id', - ) - columns = ( + column_headers = columns = ( 'ID', 'Name', 'Policies', diff --git a/openstackclient/compute/v2/service.py b/openstackclient/compute/v2/service.py index 6093b8ce..d10af2ca 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,9 +15,12 @@ """Service action implementations""" -from openstackclient.common import command -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 _ +from openstackclient.i18n import _LE class DeleteService(command.Command): @@ -105,28 +108,36 @@ 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( "--enable", - dest="enabled", - default=True, action="store_true", - help=_("Enable a service (default)") + help=_("Enable service") ) enabled_group.add_argument( "--disable", - dest="enabled", - action="store_false", - help=_("Disable a service") + action="store_true", + help=_("Disable service") ) parser.add_argument( "--disable-reason", default=None, metavar="<reason>", - help=_("Reason for disabling the service (in quotas). Note that " - "when the service is enabled, this option is ignored.") + 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 @@ -134,16 +145,51 @@ class SetService(command.Command): compute_client = self.app.client_manager.compute cs = compute_client.services - if not parsed_args.enabled: - 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) - else: - if parsed_args.disable_reason: - msg = _("argument --disable-reason has been ignored") - self.log.info(msg) - - cs.enable(parsed_args.host, parsed_args.service) + if (parsed_args.enable or not parsed_args.disable) and \ + parsed_args.disable_reason: + msg = _("Cannot specify option --disable-reason without " + "--disable specified.") + raise exceptions.CommandError(msg) + + result = 0 + enabled = None + 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" + self.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" + self.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..cd767d2f 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,12 @@ 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_domain_id_if_requested(identity_client, domain_name_or_id): @@ -132,9 +131,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 +141,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 +151,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 +162,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 53a6fe34..62e6b1f9 100644 --- a/openstackclient/identity/v2_0/catalog.py +++ b/openstackclient/identity/v2_0/catalog.py @@ -13,10 +13,11 @@ """Identity v2 Service Catalog 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 utils from openstackclient.i18n import _ @@ -41,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( @@ -72,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 = service @@ -89,8 +92,8 @@ class ShowCatalog(command.ShowOne): break if not data: - self.app.log.error('service %s not found\n' % + self.app.log.error(_('service %s not found\n') % parsed_args.service) - return ([], []) + 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..78f65824 100644 --- a/openstackclient/identity/v2_0/ec2creds.py +++ b/openstackclient/identity/v2_0/ec2creds.py @@ -16,10 +16,10 @@ """Identity v2 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 _ diff --git a/openstackclient/identity/v2_0/endpoint.py b/openstackclient/identity/v2_0/endpoint.py index fcb82eb7..ee2bab6f 100644 --- a/openstackclient/identity/v2_0/endpoint.py +++ b/openstackclient/identity/v2_0/endpoint.py @@ -15,10 +15,10 @@ """Endpoint 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 @@ -31,7 +31,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', @@ -81,7 +81,8 @@ class DeleteEndpoint(command.Command): parser.add_argument( 'endpoint', metavar='<endpoint-id>', - help=_('Endpoint ID to delete')) + help=_('Endpoint to delete (ID only)'), + ) return parser def take_action(self, parsed_args): diff --git a/openstackclient/identity/v2_0/project.py b/openstackclient/identity/v2_0/project.py index d90162c9..8be482fe 100644 --- a/openstackclient/identity/v2_0/project.py +++ b/openstackclient/identity/v2_0/project.py @@ -15,13 +15,12 @@ """Identity v2 Project action implementations""" -import six - 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 _ @@ -88,7 +87,7 @@ class CreateProject(command.ShowOne): identity_client.tenants, parsed_args.name, ) - self.log.info('Returning existing project %s', project.name) + self.log.info(_('Returning existing project %s'), project.name) else: raise e @@ -231,7 +230,8 @@ class ShowProject(command.ShowOne): parser.add_argument( 'project', metavar='<project>', - help=_('Project to display (name or ID)')) + help=_('Project to display (name or ID)'), + ) return parser def take_action(self, parsed_args): @@ -295,9 +295,9 @@ class UnsetProject(command.Command): metavar='<key>', action='append', default=[], + required=True, help=_('Unset a project property ' '(repeat option to unset multiple properties)'), - required=True, ) return parser @@ -308,7 +308,7 @@ class UnsetProject(command.Command): parsed_args.project, ) if not parsed_args.property: - self.app.log.error("No changes requested\n") + self.app.log.error(_("No changes requested\n")) else: kwargs = project._info for key in parsed_args.property: diff --git a/openstackclient/identity/v2_0/role.py b/openstackclient/identity/v2_0/role.py index 1fcee15f..4c3fe6e2 100644 --- a/openstackclient/identity/v2_0/role.py +++ b/openstackclient/identity/v2_0/role.py @@ -15,13 +15,12 @@ """Identity v2 Role action implementations""" -import six - 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 _ @@ -95,7 +94,7 @@ class CreateRole(command.ShowOne): identity_client.roles, parsed_args.role_name, ) - self.log.info('Returning existing role %s', role.name) + self.log.info(_('Returning existing role %s'), role.name) else: raise e @@ -136,12 +135,12 @@ class ListRole(command.Lister): parser.add_argument( '--project', metavar='<project>', - help='Filter roles by <project> (name or ID)', + help=_('Filter roles by <project> (name or ID)'), ) parser.add_argument( '--user', metavar='<user>', - help='Filter roles by <user> (name or ID)', + help=_('Filter roles by <user> (name or ID)'), ) return parser @@ -231,18 +230,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..947f361c 100644 --- a/openstackclient/identity/v2_0/service.py +++ b/openstackclient/identity/v2_0/service.py @@ -16,11 +16,12 @@ """Service action implementations""" import argparse + +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 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 bc9bf837..7777bab8 100644 --- a/openstackclient/identity/v2_0/user.py +++ b/openstackclient/identity/v2_0/user.py @@ -15,12 +15,11 @@ """Identity v2.0 User action implementations""" -import six - 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 _ @@ -104,7 +103,7 @@ class CreateUser(command.ShowOne): identity_client.users, parsed_args.name, ) - self.log.info('Returning existing user %s', user.name) + self.log.info(_('Returning existing user %s'), user.name) else: raise e @@ -159,7 +158,8 @@ class ListUser(command.Lister): '--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): diff --git a/openstackclient/identity/v3/catalog.py b/openstackclient/identity/v3/catalog.py index 78d71f59..a6e141c9 100644 --- a/openstackclient/identity/v3/catalog.py +++ b/openstackclient/identity/v3/catalog.py @@ -13,10 +13,11 @@ """Identity v3 Service Catalog 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 utils from openstackclient.i18n import _ @@ -36,13 +37,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 +69,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 +87,8 @@ class ShowCatalog(command.ShowOne): break if not data: - self.app.log.error('service %s not found\n' % + self.app.log.error(_('service %s not found\n') % parsed_args.service) - return ([], []) + return ((), ()) return zip(*sorted(six.iteritems(data))) diff --git a/openstackclient/identity/v3/consumer.py b/openstackclient/identity/v3/consumer.py index 0da4103d..a4620bf9 100644 --- a/openstackclient/identity/v3/consumer.py +++ b/openstackclient/identity/v3/consumer.py @@ -15,11 +15,13 @@ """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 _ class CreateConsumer(command.ShowOne): @@ -30,7 +32,7 @@ class CreateConsumer(command.ShowOne): parser.add_argument( '--description', metavar='<description>', - help='New consumer description', + help=_('New consumer description'), ) return parser @@ -51,7 +53,7 @@ class DeleteConsumer(command.Command): parser.add_argument( 'consumer', metavar='<consumer>', - help='Consumer to delete', + help=_('Consumer to delete'), ) return parser @@ -83,12 +85,12 @@ class SetConsumer(command.Command): parser.add_argument( 'consumer', metavar='<consumer>', - help='Consumer to modify', + help=_('Consumer to modify'), ) parser.add_argument( '--description', metavar='<description>', - help='New consumer description', + help=_('New consumer description'), ) return parser @@ -101,7 +103,7 @@ class SetConsumer(command.Command): kwargs['description'] = parsed_args.description if not len(kwargs): - sys.stdout.write('Consumer not updated, no arguments present') + sys.stdout.write(_('Consumer not updated, no arguments present\n')) return consumer = identity_client.oauth1.consumers.update( @@ -116,7 +118,7 @@ class ShowConsumer(command.ShowOne): parser.add_argument( 'consumer', metavar='<consumer>', - help='Consumer to display', + help=_('Consumer to display'), ) return parser diff --git a/openstackclient/identity/v3/credential.py b/openstackclient/identity/v3/credential.py index b0d2cafd..eeeddfa5 100644 --- a/openstackclient/identity/v3/credential.py +++ b/openstackclient/identity/v3/credential.py @@ -15,10 +15,11 @@ """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 _ class CreateCredential(command.ShowOne): @@ -29,24 +30,25 @@ class CreateCredential(command.ShowOne): parser.add_argument( 'user', metavar='<user>', - help='Name or ID of user that owns the credential', + help=_('Name or ID of user that owns the credential'), ) parser.add_argument( '--type', default="cert", metavar='<type>', choices=['ec2', 'cert'], - help='New credential type', + help=_('New credential type'), ) parser.add_argument( 'data', metavar='<data>', - help='New credential data', + help=_('New credential data'), ) parser.add_argument( '--project', metavar='<project>', - help='Project name or ID which limits the scope of the credential', + help=_('Project name or ID which limits the ' + 'scope of the credential'), ) return parser @@ -77,7 +79,7 @@ class DeleteCredential(command.Command): parser.add_argument( 'credential', metavar='<credential-id>', - help='ID of credential to delete', + help=_('ID of credential to delete'), ) return parser @@ -108,31 +110,32 @@ class SetCredential(command.Command): parser.add_argument( 'credential', metavar='<credential-id>', - help='ID of credential to change', + help=_('ID of credential to change'), ) parser.add_argument( '--user', metavar='<user>', required=True, - help='Name or ID of user that owns the credential', + help=_('Name or ID of user that owns the credential'), ) parser.add_argument( '--type', metavar='<type>', choices=['ec2', 'cert'], required=True, - help='New credential type', + help=_('New credential type'), ) parser.add_argument( '--data', metavar='<data>', required=True, - help='New credential data', + help=_('New credential data'), ) parser.add_argument( '--project', metavar='<project>', - help='Project name or ID which limits the scope of the credential', + help=_('Project name or ID which limits the ' + 'scope of the credential'), ) return parser @@ -163,7 +166,7 @@ class ShowCredential(command.ShowOne): parser.add_argument( 'credential', metavar='<credential-id>', - help='ID of credential to display', + help=_('ID of credential to display'), ) return parser diff --git a/openstackclient/identity/v3/domain.py b/openstackclient/identity/v3/domain.py index 7fcab4f1..8eb7bc91 100644 --- a/openstackclient/identity/v3/domain.py +++ b/openstackclient/identity/v3/domain.py @@ -15,13 +15,13 @@ """Identity v3 Domain action implementations""" -import six 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 _ @@ -33,23 +33,23 @@ class CreateDomain(command.ShowOne): parser.add_argument( 'name', metavar='<domain-name>', - help='New domain name', + help=_('New domain name'), ) parser.add_argument( '--description', metavar='<description>', - help='New domain description', + help=_('New domain description'), ) enable_group = parser.add_mutually_exclusive_group() enable_group.add_argument( '--enable', action='store_true', - help='Enable domain (default)', + help=_('Enable domain (default)'), ) enable_group.add_argument( '--disable', action='store_true', - help='Disable domain', + help=_('Disable domain'), ) parser.add_argument( '--or-show', @@ -75,7 +75,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) + self.log.info(_('Returning existing domain %s'), domain.name) else: raise e @@ -91,7 +91,7 @@ class DeleteDomain(command.Command): parser.add_argument( 'domain', metavar='<domain>', - help='Domain to delete (name or ID)', + help=_('Domain to delete (name or ID)'), ) return parser @@ -123,28 +123,28 @@ class SetDomain(command.Command): parser.add_argument( 'domain', metavar='<domain>', - help='Domain to modify (name or ID)', + help=_('Domain to modify (name or ID)'), ) parser.add_argument( '--name', metavar='<name>', - help='New domain name', + help=_('New domain name'), ) parser.add_argument( '--description', metavar='<description>', - help='New domain description', + help=_('New domain description'), ) enable_group = parser.add_mutually_exclusive_group() enable_group.add_argument( '--enable', action='store_true', - help='Enable domain', + help=_('Enable domain'), ) enable_group.add_argument( '--disable', action='store_true', - help='Disable domain', + help=_('Disable domain'), ) return parser @@ -164,7 +164,7 @@ class SetDomain(command.Command): kwargs['enabled'] = False if not kwargs: - sys.stdout.write("Domain not updated, no arguments present") + sys.stdout.write(_("Domain not updated, no arguments present\n")) return identity_client.domains.update(domain.id, **kwargs) @@ -177,7 +177,7 @@ class ShowDomain(command.ShowOne): parser.add_argument( 'domain', metavar='<domain>', - help='Domain to display (name or ID)', + help=_('Domain to display (name or ID)'), ) return parser diff --git a/openstackclient/identity/v3/ec2creds.py b/openstackclient/identity/v3/ec2creds.py index a12b2d3b..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 @@ -56,18 +56,14 @@ class CreateEC2Creds(command.ShowOne): parser.add_argument( '--project', metavar='<project>', - help=_( - 'Create credentials in project ' - '(name or ID; default: current authenticated project)' - ), + help=_('Create credentials in project ' + '(name or ID; default: current authenticated project)'), ) parser.add_argument( '--user', metavar='<user>', - help=_( - 'Create credentials for user ' - '(name or ID; default: current authenticated user)' - ), + help=_('Create credentials for user ' + '(name or ID; default: current authenticated user)'), ) common.add_user_domain_option_to_parser(parser) common.add_project_domain_option_to_parser(parser) diff --git a/openstackclient/identity/v3/endpoint.py b/openstackclient/identity/v3/endpoint.py index b03b78ce..2f1cc9f3 100644 --- a/openstackclient/identity/v3/endpoint.py +++ b/openstackclient/identity/v3/endpoint.py @@ -15,11 +15,13 @@ """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 @@ -38,23 +40,23 @@ 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', metavar='<interface>', choices=['admin', 'public', 'internal'], - help='New endpoint interface type (admin, public or internal)', + help=_('New endpoint interface type (admin, public or internal)'), ) parser.add_argument( 'url', metavar='<url>', - help='New endpoint URL', + help=_('New endpoint URL'), ) parser.add_argument( '--region', metavar='<region-id>', - help='New endpoint region ID', + help=_('New endpoint region ID'), ) enable_group = parser.add_mutually_exclusive_group() enable_group.add_argument( @@ -62,13 +64,13 @@ class CreateEndpoint(command.ShowOne): dest='enabled', action='store_true', default=True, - help='Enable endpoint (default)', + help=_('Enable endpoint (default)'), ) enable_group.add_argument( '--disable', dest='enabled', action='store_false', - help='Disable endpoint', + help=_('Disable endpoint'), ) return parser @@ -100,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 @@ -119,18 +121,18 @@ 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', metavar='<interface>', choices=['admin', 'public', 'internal'], - help='Filter by interface type (admin, public or internal)', + help=_('Filter by interface type (admin, public or internal)'), ) parser.add_argument( '--region', metavar='<region-id>', - help='Filter by region ID', + help=_('Filter by region ID'), ) return parser @@ -167,41 +169,41 @@ 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', metavar='<region-id>', - help='New endpoint region ID', + help=_('New endpoint region ID'), ) parser.add_argument( '--interface', metavar='<interface>', choices=['admin', 'public', 'internal'], - help='New endpoint interface type (admin, public or internal)', + help=_('New endpoint interface type (admin, public or internal)'), ) parser.add_argument( '--url', metavar='<url>', - help='New endpoint URL', + help=_('New endpoint URL'), ) parser.add_argument( '--service', metavar='<service>', - help='New endpoint service (name or ID)', + help=_('New endpoint service (name or ID)'), ) enable_group = parser.add_mutually_exclusive_group() enable_group.add_argument( '--enable', dest='enabled', action='store_true', - help='Enable endpoint', + help=_('Enable endpoint'), ) enable_group.add_argument( '--disable', dest='disabled', action='store_true', - help='Disable endpoint', + help=_('Disable endpoint'), ) return parser @@ -213,7 +215,7 @@ class SetEndpoint(command.Command): if (not parsed_args.interface and not parsed_args.url and not parsed_args.service and not parsed_args.region and not parsed_args.enabled and not parsed_args.disabled): - sys.stdout.write("Endpoint not updated, no arguments present") + sys.stdout.write(_("Endpoint not updated, no arguments present\n")) return service_id = None @@ -244,8 +246,8 @@ class ShowEndpoint(command.ShowOne): parser.add_argument( 'endpoint', metavar='<endpoint>', - help='Endpoint to display (endpoint ID, service ID,' - ' service name, service type)', + help=_('Endpoint to display (endpoint ID, service ID,' + ' service name, service type)'), ) return parser diff --git a/openstackclient/identity/v3/federation_protocol.py b/openstackclient/identity/v3/federation_protocol.py index 27c837c5..377ddcce 100644 --- a/openstackclient/identity/v3/federation_protocol.py +++ b/openstackclient/identity/v3/federation_protocol.py @@ -14,10 +14,11 @@ """Identity v3 Protocols actions 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 _ class CreateProtocol(command.ShowOne): @@ -28,19 +29,22 @@ class CreateProtocol(command.ShowOne): parser.add_argument( 'federation_protocol', metavar='<name>', - help='New federation protocol name (must be unique per identity ' - ' provider)') + help=_('New federation protocol name (must be unique ' + 'per identity provider)'), + ) parser.add_argument( '--identity-provider', metavar='<identity-provider>', required=True, - help='Identity provider that will support the new federation ' - ' protocol (name or ID) (required)') + help=_('Identity provider that will support the new federation ' + ' protocol (name or ID) (required)'), + ) parser.add_argument( '--mapping', metavar='<mapping>', required=True, - help='Mapping that is to be used (name or ID) (required)') + help=_('Mapping that is to be used (name or ID) (required)'), + ) return parser @@ -69,13 +73,15 @@ class DeleteProtocol(command.Command): parser.add_argument( 'federation_protocol', metavar='<federation-protocol>', - help='Federation protocol to delete (name or ID)') + help=_('Federation protocol to delete (name or ID)'), + ) parser.add_argument( '--identity-provider', metavar='<identity-provider>', required=True, - help='Identity provider that supports <federation-protocol> ' - '(name or ID) (required)') + help=_('Identity provider that supports <federation-protocol> ' + '(name or ID) (required)'), + ) return parser @@ -94,7 +100,8 @@ class ListProtocols(command.Lister): '--identity-provider', metavar='<identity-provider>', required=True, - help='Identity provider to list (name or ID) (required)') + help=_('Identity provider to list (name or ID) (required)'), + ) return parser @@ -118,24 +125,27 @@ class SetProtocol(command.Command): parser.add_argument( 'federation_protocol', metavar='<name>', - help='Federation protocol to modify (name or ID)') + help=_('Federation protocol to modify (name or ID)'), + ) parser.add_argument( '--identity-provider', metavar='<identity-provider>', required=True, - help='Identity provider that supports <federation-protocol> ' - '(name or ID) (required)') + help=_('Identity provider that supports <federation-protocol> ' + '(name or ID) (required)'), + ) parser.add_argument( '--mapping', metavar='<mapping>', - help='Mapping that is to be used (name or ID)') + help=_('Mapping that is to be used (name or ID)'), + ) return parser def take_action(self, parsed_args): identity_client = self.app.client_manager.identity if not parsed_args.mapping: - self.app.log.error("No changes requested") + self.app.log.error(_("No changes requested")) return protocol = identity_client.federation.protocols.update( @@ -159,13 +169,15 @@ class ShowProtocol(command.ShowOne): parser.add_argument( 'federation_protocol', metavar='<federation-protocol>', - help='Federation protocol to display (name or ID)') + help=_('Federation protocol to display (name or ID)'), + ) parser.add_argument( '--identity-provider', metavar='<identity-provider>', required=True, - help=('Identity provider that supports <federation-protocol> ' - '(name or ID) (required)')) + help=_('Identity provider that supports <federation-protocol> ' + '(name or ID) (required)'), + ) return parser def take_action(self, parsed_args): diff --git a/openstackclient/identity/v3/group.py b/openstackclient/identity/v3/group.py index 3c243531..fb0a25b9 100644 --- a/openstackclient/identity/v3/group.py +++ b/openstackclient/identity/v3/group.py @@ -15,13 +15,13 @@ """Group action implementations""" -import six 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 @@ -34,12 +34,12 @@ class AddUserToGroup(command.Command): parser.add_argument( 'group', metavar='<group>', - help='Group to contain <user> (name or ID)', + help=_('Group to contain <user> (name or ID)'), ) parser.add_argument( 'user', metavar='<user>', - help='User to add to <group> (name or ID)', + help=_('User to add to <group> (name or ID)'), ) common.add_group_domain_option_to_parser(parser) common.add_user_domain_option_to_parser(parser) @@ -58,11 +58,17 @@ class AddUserToGroup(command.Command): try: identity_client.users.add_to_group(user_id, group_id) except Exception: - sys.stderr.write("%s not added to group %s\n" % - (parsed_args.user, parsed_args.group)) + msg = _("%(user)s not added to group %(group)s\n") % { + 'user': parsed_args.user, + 'group': parsed_args.group, + } + sys.stderr.write(msg) else: - sys.stdout.write("%s added to group %s\n" % - (parsed_args.user, parsed_args.group)) + msg = _("%(user)s added to group %(group)s\n") % { + 'user': parsed_args.user, + 'group': parsed_args.group, + } + sys.stdout.write(msg) class CheckUserInGroup(command.Command): @@ -73,12 +79,12 @@ class CheckUserInGroup(command.Command): parser.add_argument( 'group', metavar='<group>', - help='Group to check (name or ID)', + help=_('Group to check (name or ID)'), ) parser.add_argument( 'user', metavar='<user>', - help='User to check (name or ID)', + help=_('User to check (name or ID)'), ) common.add_group_domain_option_to_parser(parser) common.add_user_domain_option_to_parser(parser) @@ -97,11 +103,17 @@ class CheckUserInGroup(command.Command): try: identity_client.users.check_in_group(user_id, group_id) except Exception: - sys.stderr.write("%s not in group %s\n" % - (parsed_args.user, parsed_args.group)) + msg = _("%(user)s not in group %(group)s\n") % { + 'user': parsed_args.user, + 'group': parsed_args.group, + } + sys.stderr.write(msg) else: - sys.stdout.write("%s in group %s\n" % - (parsed_args.user, parsed_args.group)) + msg = _("%(user)s in group %(group)s\n") % { + 'user': parsed_args.user, + 'group': parsed_args.group, + } + sys.stdout.write(msg) class CreateGroup(command.ShowOne): @@ -112,17 +124,17 @@ class CreateGroup(command.ShowOne): parser.add_argument( 'name', metavar='<group-name>', - help='New group name', + help=_('New group name'), ) parser.add_argument( '--domain', metavar='<domain>', - help='Domain to contain new group (name or ID)', + help=_('Domain to contain new group (name or ID)'), ) parser.add_argument( '--description', metavar='<description>', - help='New group description', + help=_('New group description'), ) parser.add_argument( '--or-show', @@ -149,7 +161,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) + self.log.info(_('Returning existing group %s'), group.name) else: raise e @@ -166,11 +178,12 @@ class DeleteGroup(command.Command): 'groups', metavar='<group>', nargs="+", - help='Group(s) to delete (name or ID)') + help=_('Group(s) to delete (name or ID)'), + ) parser.add_argument( '--domain', metavar='<domain>', - help='Domain containing group(s) (name or ID)', + help=_('Domain containing group(s) (name or ID)'), ) return parser @@ -192,19 +205,19 @@ class ListGroup(command.Lister): parser.add_argument( '--domain', metavar='<domain>', - help='Filter group list by <domain> (name or ID)', + help=_('Filter group list by <domain> (name or ID)'), ) parser.add_argument( '--user', metavar='<user>', - help='Filter group list by <user> (name or ID)', + help=_('Filter group list by <user> (name or ID)'), ) common.add_user_domain_option_to_parser(parser) parser.add_argument( '--long', action='store_true', default=False, - help='List additional fields in output', + help=_('List additional fields in output'), ) return parser @@ -252,12 +265,12 @@ class RemoveUserFromGroup(command.Command): parser.add_argument( 'group', metavar='<group>', - help='Group containing <user> (name or ID)', + help=_('Group containing <user> (name or ID)'), ) parser.add_argument( 'user', metavar='<user>', - help='User to remove from <group> (name or ID)', + help=_('User to remove from <group> (name or ID)'), ) common.add_group_domain_option_to_parser(parser) common.add_user_domain_option_to_parser(parser) @@ -276,11 +289,17 @@ class RemoveUserFromGroup(command.Command): try: identity_client.users.remove_from_group(user_id, group_id) except Exception: - sys.stderr.write("%s not removed from group %s\n" % - (parsed_args.user, parsed_args.group)) + msg = _("%(user)s not removed from group %(group)s\n") % { + 'user': parsed_args.user, + 'group': parsed_args.group, + } + sys.stderr.write(msg) else: - sys.stdout.write("%s removed from group %s\n" % - (parsed_args.user, parsed_args.group)) + msg = _("%(user)s removed from group %(group)s\n") % { + 'user': parsed_args.user, + 'group': parsed_args.group, + } + sys.stdout.write(msg) class SetGroup(command.Command): @@ -291,20 +310,23 @@ class SetGroup(command.Command): parser.add_argument( 'group', metavar='<group>', - help='Group to modify (name or ID)') + help=_('Group to modify (name or ID)'), + ) parser.add_argument( '--domain', metavar='<domain>', - help='Domain containing <group> (name or ID)', + help=_('Domain containing <group> (name or ID)'), ) parser.add_argument( '--name', metavar='<name>', - help='New group name') + help=_('New group name'), + ) parser.add_argument( '--description', metavar='<description>', - help='New group description') + help=_('New group description'), + ) return parser def take_action(self, parsed_args): @@ -318,7 +340,7 @@ class SetGroup(command.Command): kwargs['description'] = parsed_args.description if not len(kwargs): - sys.stderr.write("Group not updated, no arguments present") + sys.stderr.write("Group not updated, no arguments present\n") return identity_client.groups.update(group.id, **kwargs) @@ -331,12 +353,12 @@ class ShowGroup(command.ShowOne): parser.add_argument( 'group', metavar='<group>', - help='Group to display (name or ID)', + help=_('Group to display (name or ID)'), ) parser.add_argument( '--domain', metavar='<domain>', - help='Domain containing <group> (name or ID)', + help=_('Domain containing <group> (name or ID)'), ) return parser diff --git a/openstackclient/identity/v3/identity_provider.py b/openstackclient/identity/v3/identity_provider.py index 39f440f4..0b530a26 100644 --- a/openstackclient/identity/v3/identity_provider.py +++ b/openstackclient/identity/v3/identity_provider.py @@ -13,10 +13,11 @@ """Identity v3 IdentityProvider 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 _ class CreateIdentityProvider(command.ShowOne): @@ -27,26 +28,26 @@ class CreateIdentityProvider(command.ShowOne): parser.add_argument( 'identity_provider_id', metavar='<name>', - help='New identity provider name (must be unique)' + help=_('New identity provider name (must be unique)'), ) identity_remote_id_provider = parser.add_mutually_exclusive_group() identity_remote_id_provider.add_argument( '--remote-id', metavar='<remote-id>', action='append', - help='Remote IDs to associate with the Identity Provider ' - '(repeat option to provide multiple values)' + help=_('Remote IDs to associate with the Identity Provider ' + '(repeat option to provide multiple values)'), ) identity_remote_id_provider.add_argument( '--remote-id-file', metavar='<file-name>', - help='Name of a file that contains many remote IDs to associate ' - 'with the identity provider, one per line' + help=_('Name of a file that contains many remote IDs to associate ' + 'with the identity provider, one per line'), ) parser.add_argument( '--description', metavar='<description>', - help='New identity provider description', + help=_('New identity provider description'), ) enable_identity_provider = parser.add_mutually_exclusive_group() enable_identity_provider.add_argument( @@ -54,13 +55,13 @@ class CreateIdentityProvider(command.ShowOne): dest='enabled', action='store_true', default=True, - help='Enable identity provider (default)', + help=_('Enable identity provider (default)'), ) enable_identity_provider.add_argument( '--disable', dest='enabled', action='store_false', - help='Disable the identity provider', + help=_('Disable the identity provider'), ) return parser @@ -94,7 +95,7 @@ class DeleteIdentityProvider(command.Command): parser.add_argument( 'identity_provider', metavar='<identity-provider>', - help='Identity provider to delete', + help=_('Identity provider to delete'), ) return parser @@ -126,37 +127,37 @@ class SetIdentityProvider(command.Command): parser.add_argument( 'identity_provider', metavar='<identity-provider>', - help='Identity provider to modify', + help=_('Identity provider to modify'), ) parser.add_argument( '--description', metavar='<description>', - help='Set identity provider description', + help=_('Set identity provider description'), ) identity_remote_id_provider = parser.add_mutually_exclusive_group() identity_remote_id_provider.add_argument( '--remote-id', metavar='<remote-id>', action='append', - help='Remote IDs to associate with the Identity Provider ' - '(repeat option to provide multiple values)' + help=_('Remote IDs to associate with the Identity Provider ' + '(repeat option to provide multiple values)'), ) identity_remote_id_provider.add_argument( '--remote-id-file', metavar='<file-name>', - help='Name of a file that contains many remote IDs to associate ' - 'with the identity provider, one per line' + help=_('Name of a file that contains many remote IDs to associate ' + 'with the identity provider, one per line'), ) enable_identity_provider = parser.add_mutually_exclusive_group() enable_identity_provider.add_argument( '--enable', action='store_true', - help='Enable the identity provider', + help=_('Enable the identity provider'), ) enable_identity_provider.add_argument( '--disable', action='store_true', - help='Disable the identity provider', + help=_('Disable the identity provider'), ) return parser @@ -168,7 +169,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') + self.log.error(_('No changes requested')) return (None, None) # Always set remote_ids if either is passed in @@ -206,7 +207,7 @@ class ShowIdentityProvider(command.ShowOne): parser.add_argument( 'identity_provider', metavar='<identity-provider>', - help='Identity provider to display', + help=_('Identity provider to display'), ) return parser diff --git a/openstackclient/identity/v3/mapping.py b/openstackclient/identity/v3/mapping.py index 3cdc8afc..5937474e 100644 --- a/openstackclient/identity/v3/mapping.py +++ b/openstackclient/identity/v3/mapping.py @@ -17,11 +17,12 @@ import json +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 _ class _RulesReader(object): @@ -69,9 +70,9 @@ class _RulesReader(object): try: rules = json.loads(blob) except ValueError as e: - raise exceptions.CommandError( - 'An error occurred when reading ' - 'rules from file %s: %s' % (path, e)) + msg = _("An error occurred when reading rules from file " + "%(path)s: %(error)s") % {"path": path, "error": e} + raise exceptions.CommandError(msg) else: return rules @@ -84,12 +85,12 @@ class CreateMapping(command.ShowOne, _RulesReader): parser.add_argument( 'mapping', metavar='<name>', - help='New mapping name (must be unique)', + help=_('New mapping name (must be unique)'), ) parser.add_argument( '--rules', metavar='<filename>', required=True, - help='Filename that contains a set of mapping rules (required)', + help=_('Filename that contains a set of mapping rules (required)'), ) return parser @@ -113,7 +114,7 @@ class DeleteMapping(command.Command): parser.add_argument( 'mapping', metavar='<mapping>', - help='Mapping to delete', + help=_('Mapping to delete'), ) return parser @@ -145,12 +146,12 @@ class SetMapping(command.Command, _RulesReader): parser.add_argument( 'mapping', metavar='<name>', - help='Mapping to modify', + help=_('Mapping to modify'), ) parser.add_argument( '--rules', metavar='<filename>', - help='Filename that contains a new set of mapping rules', + help=_('Filename that contains a new set of mapping rules'), ) return parser @@ -158,7 +159,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") + self.app.log.error(_("No changes requested")) return rules = self._read_rules(parsed_args.rules) @@ -179,7 +180,7 @@ class ShowMapping(command.ShowOne): parser.add_argument( 'mapping', metavar='<mapping>', - help='Mapping to display', + help=_('Mapping to display'), ) return parser diff --git a/openstackclient/identity/v3/policy.py b/openstackclient/identity/v3/policy.py index 3c2d1a7c..68fb2738 100644 --- a/openstackclient/identity/v3/policy.py +++ b/openstackclient/identity/v3/policy.py @@ -15,11 +15,13 @@ """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 _ class CreatePolicy(command.ShowOne): @@ -31,13 +33,13 @@ class CreatePolicy(command.ShowOne): '--type', metavar='<type>', default="application/json", - help='New MIME type of the policy rules file ' - '(defaults to application/json)', + help=_('New MIME type of the policy rules file ' + '(defaults to application/json)'), ) parser.add_argument( 'rules', metavar='<filename>', - help='New serialized policy rules file', + help=_('New serialized policy rules file'), ) return parser @@ -62,7 +64,7 @@ class DeletePolicy(command.Command): parser.add_argument( 'policy', metavar='<policy>', - help='Policy to delete', + help=_('Policy to delete'), ) return parser @@ -80,7 +82,7 @@ class ListPolicy(command.Lister): '--long', action='store_true', default=False, - help='List additional fields in output', + help=_('List additional fields in output'), ) return parser @@ -107,17 +109,17 @@ class SetPolicy(command.Command): parser.add_argument( 'policy', metavar='<policy>', - help='Policy to modify', + help=_('Policy to modify'), ) parser.add_argument( '--type', metavar='<type>', - help='New MIME type of the policy rules file', + help=_('New MIME type of the policy rules file'), ) parser.add_argument( '--rules', metavar='<filename>', - help='New serialized policy rules file', + help=_('New serialized policy rules file'), ) return parser @@ -135,7 +137,7 @@ class SetPolicy(command.Command): kwargs['type'] = parsed_args.type if not kwargs: - sys.stdout.write('Policy not updated, no arguments present \n') + sys.stdout.write(_('Policy not updated, no arguments present\n')) return identity_client.policies.update(parsed_args.policy, **kwargs) @@ -148,7 +150,7 @@ class ShowPolicy(command.ShowOne): parser.add_argument( 'policy', metavar='<policy>', - help='Policy to display', + help=_('Policy to display'), ) return parser diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py index 4990b1b9..6145dcf1 100644 --- a/openstackclient/identity/v3/project.py +++ b/openstackclient/identity/v3/project.py @@ -15,13 +15,12 @@ """Project action implementations""" -import six - 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 @@ -34,40 +33,40 @@ class CreateProject(command.ShowOne): parser.add_argument( 'name', metavar='<project-name>', - help='New project name', + help=_('New project name'), ) parser.add_argument( '--domain', metavar='<domain>', - help='Domain owning the project (name or ID)', + help=_('Domain owning the project (name or ID)'), ) parser.add_argument( '--parent', metavar='<project>', - help='Parent of the project (name or ID)', + help=_('Parent of the project (name or ID)'), ) parser.add_argument( '--description', metavar='<description>', - help='Project description', + help=_('Project description'), ) enable_group = parser.add_mutually_exclusive_group() enable_group.add_argument( '--enable', action='store_true', - help='Enable project', + help=_('Enable project'), ) enable_group.add_argument( '--disable', action='store_true', - help='Disable project', + help=_('Disable project'), ) parser.add_argument( '--property', metavar='<key=value>', action=parseractions.KeyValueAction, - help='Add a property to <name> ' - '(repeat option to set multiple properties)', + help=_('Add a property to <name> ' + '(repeat option to set multiple properties)'), ) parser.add_argument( '--or-show', @@ -112,7 +111,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) + self.log.info(_('Returning existing project %s'), project.name) else: raise e @@ -129,12 +128,12 @@ class DeleteProject(command.Command): 'projects', metavar='<project>', nargs="+", - help='Project(s) to delete (name or ID)', + help=_('Project(s) to delete (name or ID)'), ) parser.add_argument( '--domain', metavar='<domain>', - help='Domain owning <project> (name or ID)', + help=_('Domain owning <project> (name or ID)'), ) return parser @@ -163,18 +162,18 @@ class ListProject(command.Lister): parser.add_argument( '--domain', metavar='<domain>', - help='Filter projects by <domain> (name or ID)', + help=_('Filter projects by <domain> (name or ID)'), ) parser.add_argument( '--user', metavar='<user>', - help='Filter projects by <user> (name or ID)', + help=_('Filter projects by <user> (name or ID)'), ) parser.add_argument( '--long', action='store_true', default=False, - help='List additional fields in output', + help=_('List additional fields in output'), ) return parser @@ -219,40 +218,40 @@ class SetProject(command.Command): parser.add_argument( 'project', metavar='<project>', - help='Project to modify (name or ID)', + help=_('Project to modify (name or ID)'), ) parser.add_argument( '--name', metavar='<name>', - help='Set project name', + help=_('Set project name'), ) parser.add_argument( '--domain', metavar='<domain>', - help='Domain owning <project> (name or ID)', + help=_('Domain owning <project> (name or ID)'), ) parser.add_argument( '--description', metavar='<description>', - help='Set project description', + help=_('Set project description'), ) enable_group = parser.add_mutually_exclusive_group() enable_group.add_argument( '--enable', action='store_true', - help='Enable project', + help=_('Enable project'), ) enable_group.add_argument( '--disable', action='store_true', - help='Disable project', + help=_('Disable project'), ) parser.add_argument( '--property', metavar='<key=value>', action=parseractions.KeyValueAction, - help='Set a property on <project> ' - '(repeat option to set multiple properties)', + help=_('Set a property on <project> ' + '(repeat option to set multiple properties)'), ) return parser @@ -293,24 +292,24 @@ class ShowProject(command.ShowOne): parser.add_argument( 'project', metavar='<project>', - help='Project to display (name or ID)', + help=_('Project to display (name or ID)'), ) parser.add_argument( '--domain', metavar='<domain>', - help='Domain owning <project> (name or ID)', + help=_('Domain owning <project> (name or ID)'), ) parser.add_argument( '--parents', action='store_true', default=False, - help='Show the project\'s parents as a list', + help=_('Show the project\'s parents as a list'), ) parser.add_argument( '--children', action='store_true', default=False, - help='Show project\'s subtree (children) as a list', + help=_('Show project\'s subtree (children) as a list'), ) return parser 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 f93c9d80..1bb27585 100644 --- a/openstackclient/identity/v3/role.py +++ b/openstackclient/identity/v3/role.py @@ -15,13 +15,13 @@ """Identity v3 Role action implementations""" -import six 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 @@ -31,23 +31,23 @@ def _add_identity_and_resource_options_to_parser(parser): domain_or_project.add_argument( '--domain', metavar='<domain>', - help='Include <domain> (name or ID)', + help=_('Include <domain> (name or ID)'), ) domain_or_project.add_argument( '--project', metavar='<project>', - help='Include <project> (name or ID)', + help=_('Include <project> (name or ID)'), ) user_or_group = parser.add_mutually_exclusive_group() user_or_group.add_argument( '--user', metavar='<user>', - help='Include <user> (name or ID)', + help=_('Include <user> (name or ID)'), ) user_or_group.add_argument( '--group', metavar='<group>', - help='Include <group> (name or ID)', + help=_('Include <group> (name or ID)'), ) common.add_group_domain_option_to_parser(parser) common.add_project_domain_option_to_parser(parser) @@ -112,7 +112,7 @@ class AddRole(command.Command): parser.add_argument( 'role', metavar='<role>', - help='Role to add to <user> (name or ID)', + help=_('Role to add to <user> (name or ID)'), ) _add_identity_and_resource_options_to_parser(parser) return parser @@ -131,9 +131,9 @@ class AddRole(command.Command): kwargs = _process_identity_and_resource_options( parsed_args, self.app.client_manager.identity) if not kwargs: - sys.stderr.write("Role not added, incorrect set of arguments " - "provided. See openstack --help for more " - "details\n") + sys.stderr.write(_("Role not added, incorrect set of arguments " + "provided. See openstack --help for more " + "details\n")) return identity_client.roles.grant(role.id, **kwargs) @@ -147,7 +147,7 @@ class CreateRole(command.ShowOne): parser.add_argument( 'name', metavar='<role-name>', - help='New role name', + help=_('New role name'), ) parser.add_argument( '--or-show', @@ -165,7 +165,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) + self.log.info(_('Returning existing role %s'), role.name) else: raise e @@ -182,7 +182,7 @@ class DeleteRole(command.Command): 'roles', metavar='<role>', nargs="+", - help='Role(s) to delete (name or ID)', + help=_('Role(s) to delete (name or ID)'), ) return parser @@ -285,9 +285,9 @@ class ListRole(command.Lister): group_role.group = group.name group_role.project = project.name else: - sys.stderr.write("Error: If a user or group is specified, either " - "--domain or --project must also be specified to " - "list role grants.\n") + sys.stderr.write(_("Error: If a user or group is specified, " + "either --domain or --project must also be " + "specified to list role grants.\n")) return ([], []) return (columns, @@ -305,7 +305,7 @@ class RemoveRole(command.Command): parser.add_argument( 'role', metavar='<role>', - help='Role to remove (name or ID)', + help=_('Role to remove (name or ID)'), ) _add_identity_and_resource_options_to_parser(parser) return parser @@ -315,9 +315,8 @@ class RemoveRole(command.Command): if (not parsed_args.user and not parsed_args.domain and not parsed_args.group and not parsed_args.project): - sys.stderr.write("Incorrect set of arguments " - "provided. See openstack --help for more " - "details\n") + sys.stderr.write(_("Incorrect set of arguments provided. " + "See openstack --help for more details\n")) return role = utils.find_resource( identity_client.roles, @@ -327,8 +326,9 @@ class RemoveRole(command.Command): kwargs = _process_identity_and_resource_options( parsed_args, self.app.client_manager.identity) if not kwargs: - sys.stderr.write("Role not removed, incorrect set of arguments \ - provided. See openstack --help for more details\n") + sys.stderr.write(_("Role not removed, incorrect set of arguments " + "provided. See openstack --help for more " + "details\n")) return identity_client.roles.revoke(role.id, **kwargs) @@ -341,12 +341,12 @@ class SetRole(command.Command): parser.add_argument( 'role', metavar='<role>', - help='Role to modify (name or ID)', + help=_('Role to modify (name or ID)'), ) parser.add_argument( '--name', metavar='<name>', - help='Set role name', + help=_('Set role name'), ) return parser @@ -354,9 +354,8 @@ class SetRole(command.Command): identity_client = self.app.client_manager.identity if not parsed_args.name: - sys.stderr.write("Incorrect set of arguments " - "provided. See openstack --help for more " - "details\n") + sys.stderr.write(_("Incorrect set of arguments provided. " + "See openstack --help for more details\n")) return role = utils.find_resource( identity_client.roles, @@ -374,7 +373,7 @@ class ShowRole(command.ShowOne): parser.add_argument( 'role', metavar='<role>', - help='Role to display (name or ID)', + help=_('Role to display (name or ID)'), ) return parser diff --git a/openstackclient/identity/v3/role_assignment.py b/openstackclient/identity/v3/role_assignment.py index a1418a82..39e2336d 100644 --- a/openstackclient/identity/v3/role_assignment.py +++ b/openstackclient/identity/v3/role_assignment.py @@ -13,8 +13,10 @@ """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 @@ -27,41 +29,41 @@ class ListRoleAssignment(command.Lister): '--effective', action="store_true", default=False, - help='Returns only effective role assignments', + help=_('Returns only effective role assignments'), ) parser.add_argument( '--role', metavar='<role>', - help='Role to filter (name or ID)', + help=_('Role to filter (name or ID)'), ) parser.add_argument( '--names', action="store_true", - help='Display names instead of IDs', + help=_('Display names instead of IDs'), ) user_or_group = parser.add_mutually_exclusive_group() user_or_group.add_argument( '--user', metavar='<user>', - help='User to filter (name or ID)', + help=_('User to filter (name or ID)'), ) common.add_user_domain_option_to_parser(parser) user_or_group.add_argument( '--group', metavar='<group>', - help='Group to filter (name or ID)', + help=_('Group to filter (name or ID)'), ) common.add_group_domain_option_to_parser(parser) domain_or_project = parser.add_mutually_exclusive_group() domain_or_project.add_argument( '--domain', metavar='<domain>', - help='Domain to filter (name or ID)', + help=_('Domain to filter (name or ID)'), ) domain_or_project.add_argument( '--project', metavar='<project>', - help='Project to filter (name or ID)', + help=_('Project to filter (name or ID)'), ) common.add_project_domain_option_to_parser(parser) common.add_inherited_option_to_parser(parser) diff --git a/openstackclient/identity/v3/service.py b/openstackclient/identity/v3/service.py index f43ada56..7beadc9d 100644 --- a/openstackclient/identity/v3/service.py +++ b/openstackclient/identity/v3/service.py @@ -15,11 +15,13 @@ """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 @@ -31,28 +33,28 @@ class CreateService(command.ShowOne): parser.add_argument( 'type', metavar='<type>', - help='New service type (compute, image, identity, volume, etc)', + help=_('New service type (compute, image, identity, volume, etc)'), ) parser.add_argument( '--name', metavar='<name>', - help='New service name', + help=_('New service name'), ) parser.add_argument( '--description', metavar='<description>', - help='New service description', + help=_('New service description'), ) enable_group = parser.add_mutually_exclusive_group() enable_group.add_argument( '--enable', action='store_true', - help='Enable service (default)', + help=_('Enable service (default)'), ) enable_group.add_argument( '--disable', action='store_true', - help='Disable service', + help=_('Disable service'), ) return parser @@ -82,7 +84,7 @@ class DeleteService(command.Command): parser.add_argument( 'service', metavar='<service>', - help='Service to delete (type, name or ID)', + help=_('Service to delete (type, name or ID)'), ) return parser @@ -103,7 +105,7 @@ class ListService(command.Lister): '--long', action='store_true', default=False, - help='List additional fields in output', + help=_('List additional fields in output'), ) return parser @@ -128,33 +130,33 @@ class SetService(command.Command): parser.add_argument( 'service', metavar='<service>', - help='Service to update (type, name or ID)', + help=_('Service to update (type, name or ID)'), ) parser.add_argument( '--type', metavar='<type>', - help='New service type (compute, image, identity, volume, etc)', + help=_('New service type (compute, image, identity, volume, etc)'), ) parser.add_argument( '--name', metavar='<service-name>', - help='New service name', + help=_('New service name'), ) parser.add_argument( '--description', metavar='<description>', - help='New service description', + help=_('New service description'), ) enable_group = parser.add_mutually_exclusive_group() enable_group.add_argument( '--enable', action='store_true', - help='Enable service', + help=_('Enable service'), ) enable_group.add_argument( '--disable', action='store_true', - help='Disable service', + help=_('Disable service'), ) return parser @@ -166,9 +168,8 @@ class SetService(command.Command): and not parsed_args.description and not parsed_args.enable and not parsed_args.disable): - sys.stderr.write("Incorrect set of arguments " - "provided. See openstack --help for more " - "details\n") + sys.stderr.write(_("Incorrect set of arguments provided. " + "See openstack --help for more details\n")) return service = common.find_service(identity_client, parsed_args.service) @@ -198,7 +199,7 @@ class ShowService(command.ShowOne): parser.add_argument( 'service', metavar='<service>', - help='Service to display (type, name or ID)', + help=_('Service to display (type, name or ID)'), ) return parser diff --git a/openstackclient/identity/v3/service_provider.py b/openstackclient/identity/v3/service_provider.py index 8b433b4d..0beea813 100644 --- a/openstackclient/identity/v3/service_provider.py +++ b/openstackclient/identity/v3/service_provider.py @@ -13,11 +13,13 @@ """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 _ class CreateServiceProvider(command.ShowOne): @@ -28,26 +30,26 @@ class CreateServiceProvider(command.ShowOne): parser.add_argument( 'service_provider_id', metavar='<name>', - help='New service provider name (must be unique)' + help=_('New service provider name (must be unique)'), ) parser.add_argument( '--auth-url', metavar='<auth-url>', required=True, - help='Authentication URL of remote federated service provider ' - '(required)', + help=_('Authentication URL of remote federated service provider ' + '(required)'), ) parser.add_argument( '--description', metavar='<description>', - help='New service provider description', + help=_('New service provider description'), ) parser.add_argument( '--service-provider-url', metavar='<sp-url>', required=True, - help='A service URL where SAML assertions are being sent ' - '(required)', + help=_('A service URL where SAML assertions are being sent ' + '(required)'), ) enable_service_provider = parser.add_mutually_exclusive_group() @@ -56,13 +58,13 @@ class CreateServiceProvider(command.ShowOne): dest='enabled', action='store_true', default=True, - help='Enable the service provider (default)', + help=_('Enable the service provider (default)'), ) enable_service_provider.add_argument( '--disable', dest='enabled', action='store_false', - help='Disable the service provider', + help=_('Disable the service provider'), ) return parser @@ -88,7 +90,7 @@ class DeleteServiceProvider(command.Command): parser.add_argument( 'service_provider', metavar='<service-provider>', - help='Service provider to delete', + help=_('Service provider to delete'), ) return parser @@ -121,34 +123,35 @@ class SetServiceProvider(command.Command): parser.add_argument( 'service_provider', metavar='<service-provider>', - help='Service provider to modify', + help=_('Service provider to modify'), ) parser.add_argument( '--auth-url', metavar='<auth-url>', - help='New Authentication URL of remote federated service provider', + help=_('New Authentication URL of remote ' + 'federated service provider'), ) parser.add_argument( '--description', metavar='<description>', - help='New service provider description', + help=_('New service provider description'), ) parser.add_argument( '--service-provider-url', metavar='<sp-url>', - help='New service provider URL, where SAML assertions are sent', + help=_('New service provider URL, where SAML assertions are sent'), ) enable_service_provider = parser.add_mutually_exclusive_group() enable_service_provider.add_argument( '--enable', action='store_true', - help='Enable the service provider', + help=_('Enable the service provider'), ) enable_service_provider.add_argument( '--disable', action='store_true', - help='Disable the service provider', + help=_('Disable the service provider'), ) return parser @@ -164,8 +167,8 @@ class SetServiceProvider(command.Command): if not any((enabled is not None, parsed_args.description, parsed_args.service_provider_url, parsed_args.auth_url)): - sys.stdout.write("Service Provider not updated, no arguments " - "present") + sys.stdout.write(_("Service Provider not updated, no arguments " + "present\n")) return (None, None) service_provider = federation_client.service_providers.update( @@ -184,7 +187,7 @@ class ShowServiceProvider(command.ShowOne): parser.add_argument( 'service_provider', metavar='<service-provider>', - help='Service provider to display', + help=_('Service provider to display'), ) return parser diff --git a/openstackclient/identity/v3/token.py b/openstackclient/identity/v3/token.py index bdc5e95f..ecf09693 100644 --- a/openstackclient/identity/v3/token.py +++ b/openstackclient/identity/v3/token.py @@ -15,11 +15,12 @@ """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 @@ -31,17 +32,17 @@ class AuthorizeRequestToken(command.ShowOne): parser.add_argument( '--request-key', metavar='<request-key>', - help='Request token to authorize (ID only) (required)', - required=True + required=True, + help=_('Request token to authorize (ID only) (required)'), ) parser.add_argument( '--role', metavar='<role>', action='append', default=[], - help='Roles to authorize (name or ID) ' - '(repeat option to set multiple values, required)', - required=True + required=True, + help=_('Roles to authorize (name or ID) ' + '(repeat option to set multiple values, required)'), ) return parser @@ -72,31 +73,31 @@ class CreateAccessToken(command.ShowOne): parser.add_argument( '--consumer-key', metavar='<consumer-key>', - help='Consumer key (required)', + help=_('Consumer key (required)'), required=True ) parser.add_argument( '--consumer-secret', metavar='<consumer-secret>', - help='Consumer secret (required)', + help=_('Consumer secret (required)'), required=True ) parser.add_argument( '--request-key', metavar='<request-key>', - help='Request token to exchange for access token (required)', + help=_('Request token to exchange for access token (required)'), required=True ) parser.add_argument( '--request-secret', metavar='<request-secret>', - help='Secret associated with <request-key> (required)', + help=_('Secret associated with <request-key> (required)'), required=True ) parser.add_argument( '--verifier', metavar='<verifier>', - help='Verifier associated with <request-key> (required)', + help=_('Verifier associated with <request-key> (required)'), required=True ) return parser @@ -118,26 +119,26 @@ class CreateRequestToken(command.ShowOne): parser.add_argument( '--consumer-key', metavar='<consumer-key>', - help='Consumer key (required)', + help=_('Consumer key (required)'), required=True ) parser.add_argument( '--consumer-secret', metavar='<consumer-secret>', - help='Consumer secret (required)', + help=_('Consumer secret (required)'), required=True ) parser.add_argument( '--project', metavar='<project>', - help='Project that consumer wants to access (name or ID)' - ' (required)', + help=_('Project that consumer wants to access (name or ID)' + ' (required)'), required=True ) parser.add_argument( '--domain', metavar='<domain>', - help='Domain owning <project> (name or ID)', + help=_('Domain owning <project> (name or ID)'), ) return parser @@ -173,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))) + _("Only an authorized user may issue a new 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): @@ -190,7 +201,7 @@ class RevokeToken(command.Command): parser.add_argument( 'token', metavar='<token>', - help='Token to be deleted', + help=_('Token to be deleted'), ) return parser diff --git a/openstackclient/identity/v3/trust.py b/openstackclient/identity/v3/trust.py index b6f5c6b4..bbc86adb 100644 --- a/openstackclient/identity/v3/trust.py +++ b/openstackclient/identity/v3/trust.py @@ -14,10 +14,12 @@ """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 @@ -29,26 +31,26 @@ class CreateTrust(command.ShowOne): parser.add_argument( 'trustor', metavar='<trustor-user>', - help='User that is delegating authorization (name or ID)', + help=_('User that is delegating authorization (name or ID)'), ) parser.add_argument( 'trustee', metavar='<trustee-user>', - help='User that is assuming authorization (name or ID)', + help=_('User that is assuming authorization (name or ID)'), ) parser.add_argument( '--project', metavar='<project>', required=True, - help='Project being delegated (name or ID) (required)', + help=_('Project being delegated (name or ID) (required)'), ) parser.add_argument( '--role', metavar='<role>', action='append', default=[], - help='Roles to authorize (name or ID) ' - '(repeat option to set multiple values, required)', + help=_('Roles to authorize (name or ID) ' + '(repeat option to set multiple values, required)'), required=True ) parser.add_argument( @@ -56,25 +58,25 @@ class CreateTrust(command.ShowOne): dest='impersonate', action='store_true', default=False, - help='Tokens generated from the trust will represent <trustor>' - ' (defaults to False)', + help=_('Tokens generated from the trust will represent <trustor>' + ' (defaults to False)'), ) parser.add_argument( '--expiration', metavar='<expiration>', - help='Sets an expiration date for the trust' - ' (format of YYYY-mm-ddTHH:MM:SS)', + help=_('Sets an expiration date for the trust' + ' (format of YYYY-mm-ddTHH:MM:SS)'), ) common.add_project_domain_option_to_parser(parser) parser.add_argument( '--trustor-domain', metavar='<trustor-domain>', - help='Domain that contains <trustor> (name or ID)', + help=_('Domain that contains <trustor> (name or ID)'), ) parser.add_argument( '--trustee-domain', metavar='<trustee-domain>', - help='Domain that contains <trustee> (name or ID)', + help=_('Domain that contains <trustee> (name or ID)'), ) return parser @@ -136,8 +138,8 @@ class DeleteTrust(command.Command): parser.add_argument( 'trust', metavar='<trust>', - help='Trust(s) to delete', nargs="+", + help=_('Trust(s) to delete'), ) return parser @@ -170,7 +172,7 @@ class ShowTrust(command.ShowOne): parser.add_argument( 'trust', metavar='<trust>', - help='Trust to display', + help=_('Trust to display'), ) return parser diff --git a/openstackclient/identity/v3/unscoped_saml.py b/openstackclient/identity/v3/unscoped_saml.py index 8e2616a6..f116174b 100644 --- a/openstackclient/identity/v3/unscoped_saml.py +++ b/openstackclient/identity/v3/unscoped_saml.py @@ -17,9 +17,11 @@ 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 _ UNSCOPED_AUTH_PLUGINS = ['v3unscopedsaml', 'v3unscopedadfs', 'v3oidc'] @@ -33,10 +35,10 @@ def auth_with_unscoped_saml(func): if auth_plugin_name in UNSCOPED_AUTH_PLUGINS: return func(self, parsed_args) else: - msg = ('This command requires the use of an unscoped SAML ' - 'authentication plugin. Please use argument ' - '--os-auth-type with one of the following ' - 'plugins: ' + ', '.join(UNSCOPED_AUTH_PLUGINS)) + msg = (_('This command requires the use of an unscoped SAML ' + 'authentication plugin. Please use argument ' + '--os-auth-type with one of the following ' + 'plugins: %s') % ', '.join(UNSCOPED_AUTH_PLUGINS)) raise exceptions.CommandError(msg) return _decorated diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index 8bc4183b..39125e2c 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -16,13 +16,13 @@ """Identity v3 User action implementations""" import copy -import six 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 @@ -35,50 +35,50 @@ class CreateUser(command.ShowOne): parser.add_argument( 'name', metavar='<name>', - help='New user name', + help=_('New user name'), ) parser.add_argument( '--domain', metavar='<domain>', - help='Default domain (name or ID)', + help=_('Default domain (name or ID)'), ) parser.add_argument( '--project', metavar='<project>', - help='Default project (name or ID)', + help=_('Default project (name or ID)'), ) common.add_project_domain_option_to_parser(parser) parser.add_argument( '--password', metavar='<password>', - help='Set user password', + help=_('Set user password'), ) parser.add_argument( '--password-prompt', dest="password_prompt", action="store_true", - help='Prompt interactively for password', + help=_('Prompt interactively for password'), ) parser.add_argument( '--email', metavar='<email-address>', - help='Set user email address', + help=_('Set user email address'), ) parser.add_argument( '--description', metavar='<description>', - help='User description', + help=_('User description'), ) enable_group = parser.add_mutually_exclusive_group() enable_group.add_argument( '--enable', action='store_true', - help='Enable user (default)', + help=_('Enable user (default)'), ) enable_group.add_argument( '--disable', action='store_true', - help='Disable user', + help=_('Disable user'), ) parser.add_argument( '--or-show', @@ -122,7 +122,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) + self.log.info(_('Returning existing user %s'), user.name) else: raise e @@ -139,12 +139,12 @@ class DeleteUser(command.Command): 'users', metavar='<user>', nargs="+", - help='User(s) to delete (name or ID)', + help=_('User(s) to delete (name or ID)'), ) parser.add_argument( '--domain', metavar='<domain>', - help='Domain owning <user> (name or ID)', + help=_('Domain owning <user> (name or ID)'), ) return parser @@ -173,24 +173,24 @@ class ListUser(command.Lister): parser.add_argument( '--domain', metavar='<domain>', - help='Filter users by <domain> (name or ID)', + help=_('Filter users by <domain> (name or ID)'), ) project_or_group = parser.add_mutually_exclusive_group() project_or_group.add_argument( '--group', metavar='<group>', - help='Filter users by <group> membership (name or ID)', + help=_('Filter users by <group> membership (name or ID)'), ) project_or_group.add_argument( '--project', metavar='<project>', - help='Filter users by <project> (name or ID)', + help=_('Filter users by <project> (name or ID)'), ) parser.add_argument( '--long', action='store_true', default=False, - help='List additional fields in output', + help=_('List additional fields in output'), ) return parser @@ -273,50 +273,50 @@ class SetUser(command.Command): parser.add_argument( 'user', metavar='<user>', - help='User to change (name or ID)', + help=_('User to change (name or ID)'), ) parser.add_argument( '--name', metavar='<name>', - help='Set user name', + help=_('Set user name'), ) parser.add_argument( '--project', metavar='<project>', - help='Set default project (name or ID)', + help=_('Set default project (name or ID)'), ) common.add_project_domain_option_to_parser(parser) parser.add_argument( '--password', metavar='<password>', - help='Set user password', + help=_('Set user password'), ) parser.add_argument( '--password-prompt', dest="password_prompt", action="store_true", - help='Prompt interactively for password', + help=_('Prompt interactively for password'), ) parser.add_argument( '--email', metavar='<email-address>', - help='Set user email address', + help=_('Set user email address'), ) parser.add_argument( '--description', metavar='<description>', - help='Set user description', + help=_('Set user description'), ) enable_group = parser.add_mutually_exclusive_group() enable_group.add_argument( '--enable', action='store_true', - help='Enable user (default)', + help=_('Enable user (default)'), ) enable_group.add_argument( '--disable', action='store_true', - help='Disable user', + help=_('Disable user'), ) return parser @@ -334,9 +334,8 @@ class SetUser(command.Command): and not parsed_args.description and not parsed_args.enable and not parsed_args.disable): - sys.stderr.write("Incorrect set of arguments " - "provided. See openstack --help for more " - "details\n") + sys.stderr.write(_("Incorrect set of arguments provided. " + "See openstack --help for more details\n")) return user = utils.find_resource( @@ -376,12 +375,12 @@ class SetPasswordUser(command.Command): parser.add_argument( '--password', metavar='<new-password>', - help='New user password' + help=_('New user password'), ) parser.add_argument( '--original-password', metavar='<original-password>', - help='Original user password' + help=_('Original user password'), ) return parser @@ -429,12 +428,12 @@ class ShowUser(command.ShowOne): parser.add_argument( 'user', metavar='<user>', - help='User to display (name or ID)', + help=_('User to display (name or ID)'), ) parser.add_argument( '--domain', metavar='<domain>', - help='Domain owning <user> (name or ID)', + help=_('Domain owning <user> (name or ID)'), ) return parser 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..1644809d 100644 --- a/openstackclient/image/v1/image.py +++ b/openstackclient/image/v1/image.py @@ -18,7 +18,6 @@ import argparse import io import os -import six import sys if os.name == "nt": @@ -27,10 +26,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 _ diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index a81f092c..d20fdba6 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -16,15 +16,15 @@ """Image V2 Action Implementations""" import argparse -import six 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 @@ -372,13 +372,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 + self.app.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): @@ -933,7 +947,7 @@ class UnsetImage(command.Command): 'propret': propret, 'proptotal': proptotal}) raise exceptions.CommandError(msg) elif tagret > 0: - msg = (_("Failed to unset %(target)s of %(tagtotal)s tags.") + msg = (_("Failed to unset %(tagret)s of %(tagtotal)s tags.") % {'tagret': tagret, 'tagtotal': tagtotal}) raise exceptions.CommandError(msg) elif propret > 0: diff --git a/openstackclient/locale/de/LC_MESSAGES/openstackclient.po b/openstackclient/locale/de/LC_MESSAGES/openstackclient.po index f7445b1c..06f1c8d6 100644 --- a/openstackclient/locale/de/LC_MESSAGES/openstackclient.po +++ b/openstackclient/locale/de/LC_MESSAGES/openstackclient.po @@ -8,13 +8,13 @@ # Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata msgid "" msgstr "" -"Project-Id-Version: python-openstackclient 2.2.1.dev235\n" +"Project-Id-Version: python-openstackclient 2.5.1.dev51\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" -"POT-Creation-Date: 2016-04-19 05:02+0000\n" +"POT-Creation-Date: 2016-06-03 19:37+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2015-09-19 07:10+0000\n" +"PO-Revision-Date: 2016-06-02 01:43+0000\n" "Last-Translator: Andreas Jaeger <jaegerandi@gmail.com>\n" "Language: de\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" @@ -22,69 +22,6 @@ msgstr "" "X-Generator: Zanata 3.7.3\n" "Language-Team: German\n" -msgid "" -"\n" -"Complete\n" -msgstr "" -"\n" -"Fertig\n" - -msgid "" -"\n" -"Error creating server" -msgstr "" -"\n" -"Fehler beim Erstellen des Servers" - -msgid "" -"\n" -"Error creating server snapshot" -msgstr "" -"\n" -"Fehler beim Erstellen der Server-Schattenkopie" - -msgid "" -"\n" -"Error deleting server" -msgstr "" -"\n" -"Fehler beim Löschen des Servers" - -msgid "" -"\n" -"Error migrating server" -msgstr "" -"\n" -"Fehler beim Migrieren des Servers" - -msgid "" -"\n" -"Error rebooting server\n" -msgstr "" -"\n" -"Fehler beim Neustarten des Servers\n" - -msgid "" -"\n" -"Error rebuilding server" -msgstr "" -"\n" -"Fehler bei der Wiederherstellung des Servers" - -msgid "" -"\n" -"Error resizing server" -msgstr "" -"\n" -"Fehler bei der Größenänderung des Servers" - -msgid "" -"\n" -"Reboot complete\n" -msgstr "" -"\n" -"Neustart abgeschlossen\n" - msgid "Add a property to <name> (repeat option to set multiple properties)" msgstr "" "Fügen Sie eine Eigenschaft zu <Name> hinzu (wiederholen Sie die Option, um " @@ -93,6 +30,9 @@ msgstr "" msgid "Allow disk over-commit on the destination host" msgstr "Festplattenüberladung auf dem Zielhost erlauben" +msgid "Availability zone name" +msgstr "Name der Verfügbarkeitszone" + msgid "Complete\n" msgstr "Fertig\n" @@ -138,6 +78,9 @@ msgstr "Anmeldedaten für Benutzer löschen (name or ID)" msgid "Destination port (ssh -p option)" msgstr "Zielport (ssh -p Option)" +msgid "Disable a service" +msgstr "Dienst deaktivieren" + msgid "Disable project" msgstr "Projekt deaktivieren" @@ -162,13 +105,19 @@ msgstr "Benutzer aktivieren (Standardeinstellung)" msgid "Endpoint ID to delete" msgstr "Zu löschende Endpunktkennung" -msgid "Endpoint ID to display" -msgstr "Anzuzeigende Endpunktkennung" +msgid "Error creating server\n" +msgstr "Fehler beim Erstellen des Servers\n" + +msgid "Error creating server snapshot\n" +msgstr "Fehler beim Erstellen der Server-Schattenkopie\n" #, python-format msgid "Error creating server: %s" msgstr "Fehler beim Erstellen des Servers: %s" +msgid "Error deleting server\n" +msgstr "Fehler beim Löschen des Servers\n" + #, python-format msgid "Error deleting server: %s" msgstr "Fehler beim Löschen des Servers: %s" @@ -188,6 +137,9 @@ msgstr "Benutzer nach <project> filtern (Name oder Kennung)" msgid "Filter users by project (name or ID)" msgstr "Benutzer nach Projekt filtern (Name oder Kennung)" +msgid "Floating IP address" +msgstr "Bewegliche IP-Adresse" + msgid "Hints for the scheduler (optional extension)" msgstr "Hinweise für den Planer (optionale Erweiterung)" @@ -209,6 +161,9 @@ msgstr "Zusätzliche Felder in der Ausgabe auflisten" msgid "Login name (ssh -l option)" msgstr "Anmeldename (ssh -l Option)" +msgid "MD5 hash" +msgstr "MD5-Hashwert" + msgid "" "Map block devices; map is <id>:<type>:<size(GB)>:<delete_on_terminate> " "(optional extension)" @@ -442,27 +397,27 @@ msgstr "" "OS_DOMAIN_NAME oder auth.domain_name." msgid "" -"Set a service AUTH_URL, with --os-auth-url, OS_AUTH_URL or auth.auth_url\n" +"Set a service AUTH_URL, with --os-auth-url, OS_AUTH_URL or auth.auth_url" msgstr "" "Legen Sie eine Dienst-AUTH_URL mit --os-auth-url, OS_AUTH_URL oder auth." -"auth_url fest\n" +"auth_url fest" -msgid "Set a service URL, with --os-url, OS_URL or auth.url\n" -msgstr "Legen Sie eine Dienst-URL mit --os-url, OS_URL oder auth.url fest\n" +msgid "Set a service URL, with --os-url, OS_URL or auth.url" +msgstr "Legen Sie eine Dienst-URL mit --os-url, OS_URL oder auth.url fest" -msgid "Set a token with --os-token, OS_TOKEN or auth.token\n" -msgstr "Legen Sie einen Token mit --os-token, OS_TOKEN oder auth.token fest\n" +msgid "Set a token with --os-token, OS_TOKEN or auth.token" +msgstr "Legen Sie einen Token mit --os-token, OS_TOKEN oder auth.token fest" -msgid "Set a username with --os-username, OS_USERNAME, or auth.username\n" +msgid "Set a username with --os-username, OS_USERNAME, or auth.username" msgstr "" "Legen Sie einen Benutzernamen mit --os-username, OS_USERNAME oder auth." -"username fest\n" +"username fest" msgid "" -"Set an authentication URL, with --os-auth-url, OS_AUTH_URL or auth.auth_url\n" +"Set an authentication URL, with --os-auth-url, OS_AUTH_URL or auth.auth_url" msgstr "" "Legen Sie eine Authentifizierungs-URL mit --os-auth-url, OS_AUTH_URL oder " -"auth.auth_url fest\n" +"auth.auth_url fest" msgid "Set default project (name or ID)" msgstr "Standardprojekt festlegen (Name oder Kennung)" @@ -504,6 +459,9 @@ msgstr "" msgid "Token to be deleted" msgstr "Zu löschender Token" +msgid "URL" +msgstr "URL" + msgid "Use only IPv4 addresses" msgstr "Nur IPv4-Adressen verwenden" @@ -543,6 +501,9 @@ msgstr "Aufzulistender Benutzer (Name oder Kennung)" msgid "User(s) to delete (name or ID)" msgstr "Zu löschende(r) Benutzer (Name oder Kennung)" +msgid "Version" +msgstr "Version" + msgid "Volume to add (name or ID)" msgstr "Zu hinzufügender Datenträger (Name oder Kennung)" diff --git a/openstackclient/locale/openstackclient.pot b/openstackclient/locale/openstackclient.pot deleted file mode 100644 index e197fa7b..00000000 --- a/openstackclient/locale/openstackclient.pot +++ /dev/null @@ -1,1410 +0,0 @@ -# Translations template for python-openstackclient. -# Copyright (C) 2016 ORGANIZATION -# This file is distributed under the same license as the -# python-openstackclient project. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2016. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: python-openstackclient 2.2.1.dev235\n" -"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" -"POT-Creation-Date: 2016-04-19 06:14+0000\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" -"Language-Team: LANGUAGE <LL@li.org>\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.2.0\n" - -#: openstackclient/api/auth.py:148 -msgid "Set a username with --os-username, OS_USERNAME, or auth.username\n" -msgstr "" - -#: openstackclient/api/auth.py:151 -msgid "" -"Set an authentication URL, with --os-auth-url, OS_AUTH_URL or " -"auth.auth_url\n" -msgstr "" - -#: openstackclient/api/auth.py:160 -msgid "" -"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" -msgstr "" - -#: openstackclient/api/auth.py:166 openstackclient/api/auth.py:172 -msgid "Set a token with --os-token, OS_TOKEN or auth.token\n" -msgstr "" - -#: openstackclient/api/auth.py:168 -msgid "Set a service AUTH_URL, with --os-auth-url, OS_AUTH_URL or auth.auth_url\n" -msgstr "" - -#: openstackclient/api/auth.py:174 -msgid "Set a service URL, with --os-url, OS_URL or auth.url\n" -msgstr "" - -#: openstackclient/common/availability_zone.py:110 -#: openstackclient/compute/v2/server.py:757 -#: openstackclient/identity/v2_0/endpoint.py:101 -#: openstackclient/identity/v2_0/project.py:133 -#: openstackclient/identity/v2_0/service.py:115 -#: openstackclient/identity/v2_0/user.py:162 -#: openstackclient/network/v2/router.py:230 -#: openstackclient/network/v2/subnet.py:299 -#: openstackclient/network/v2/subnet_pool.py:165 -msgid "List additional fields in output" -msgstr "" - -#: openstackclient/common/parseractions.py:99 -#, python-format -msgid "" -"Invalid keys %(invalid_keys)s specified.\n" -"Valid keys are: %(valid_keys)s." -msgstr "" - -#: openstackclient/common/parseractions.py:109 -#, python-format -msgid "" -"Missing required keys %(missing_keys)s.\n" -"Required keys are: %(required_keys)s." -msgstr "" - -#: openstackclient/compute/v2/server.py:195 -#: openstackclient/compute/v2/server.py:227 -#: openstackclient/compute/v2/server.py:598 -#: openstackclient/compute/v2/server.py:937 -#: openstackclient/compute/v2/server.py:1039 -#: openstackclient/compute/v2/server.py:1091 -#: openstackclient/compute/v2/server.py:1177 -#: openstackclient/compute/v2/server.py:1213 -#: openstackclient/compute/v2/server.py:1236 -#: openstackclient/compute/v2/server.py:1343 -#: openstackclient/compute/v2/server.py:1421 -#: openstackclient/compute/v2/server.py:1455 -#: openstackclient/compute/v2/server.py:1712 -#: openstackclient/compute/v2/server.py:1733 -msgid "Server (name or ID)" -msgstr "" - -#: openstackclient/compute/v2/server.py:200 -msgid "Security group to add (name or ID)" -msgstr "" - -#: openstackclient/compute/v2/server.py:232 -msgid "Volume to add (name or ID)" -msgstr "" - -#: openstackclient/compute/v2/server.py:237 -msgid "Server internal device name for volume" -msgstr "" - -#: openstackclient/compute/v2/server.py:269 -#: openstackclient/compute/v2/server.py:1348 -msgid "New server name" -msgstr "" - -#: openstackclient/compute/v2/server.py:277 -msgid "Create server from this image (name or ID)" -msgstr "" - -#: openstackclient/compute/v2/server.py:282 -msgid "Create server from this volume (name or ID)" -msgstr "" - -#: openstackclient/compute/v2/server.py:288 -msgid "Create server with this flavor (name or ID)" -msgstr "" - -#: openstackclient/compute/v2/server.py:295 -msgid "" -"Security group to assign to this server (name or ID) (repeat option to " -"set multiple groups)" -msgstr "" - -#: openstackclient/compute/v2/server.py:301 -msgid "Keypair to inject into this server (optional extension)" -msgstr "" - -#: openstackclient/compute/v2/server.py:307 -msgid "Set a property on this server (repeat option to set multiple values)" -msgstr "" - -#: openstackclient/compute/v2/server.py:315 -msgid "" -"File to inject into image before boot (repeat option to set multiple " -"files)" -msgstr "" - -#: openstackclient/compute/v2/server.py:321 -msgid "User data file to serve from the metadata server" -msgstr "" - -#: openstackclient/compute/v2/server.py:326 -msgid "Select an availability zone for the server" -msgstr "" - -#: openstackclient/compute/v2/server.py:333 -msgid "" -"Map block devices; map is <id>:<type>:<size(GB)>:<delete_on_terminate> " -"(optional extension)" -msgstr "" - -#: openstackclient/compute/v2/server.py:343 -msgid "" -"Create a NIC on the server. Specify option multiple times to create " -"multiple NICs. Either net-id or port-id must be provided, but not both. " -"net-id: attach NIC to network with this UUID, port-id: attach NIC to port" -" with this UUID, v4-fixed-ip: IPv4 fixed address for NIC (optional), v6" -"-fixed-ip: IPv6 fixed address for NIC (optional)." -msgstr "" - -#: openstackclient/compute/v2/server.py:356 -msgid "Hints for the scheduler (optional extension)" -msgstr "" - -#: openstackclient/compute/v2/server.py:362 -msgid "" -"Use specified volume as the config drive, or 'True' to use an ephemeral " -"drive" -msgstr "" - -#: openstackclient/compute/v2/server.py:370 -msgid "Minimum number of servers to launch (default=1)" -msgstr "" - -#: openstackclient/compute/v2/server.py:377 -msgid "Maximum number of servers to launch (default=1)" -msgstr "" - -#: openstackclient/compute/v2/server.py:382 -msgid "Wait for build to complete" -msgstr "" - -#: openstackclient/compute/v2/server.py:421 -msgid "min instances should be <= max instances" -msgstr "" - -#: openstackclient/compute/v2/server.py:424 -msgid "min instances should be > 0" -msgstr "" - -#: openstackclient/compute/v2/server.py:427 -msgid "max instances should be > 0" -msgstr "" - -#: openstackclient/compute/v2/server.py:456 -msgid "Volume name or ID must be specified if --block-device-mapping is specified" -msgstr "" - -#: openstackclient/compute/v2/server.py:468 -msgid "either net-id or port-id should be specified but not both" -msgstr "" - -#: openstackclient/compute/v2/server.py:488 -msgid "can't create server with port specified since network endpoint not enabled" -msgstr "" - -#: openstackclient/compute/v2/server.py:553 -#, python-format -msgid "Error creating server: %s" -msgstr "" - -#: openstackclient/compute/v2/server.py:555 -msgid "" -"\n" -"Error creating server" -msgstr "" - -#: openstackclient/compute/v2/server.py:577 -msgid "Server(s) to create dump file (name or ID)" -msgstr "" - -#: openstackclient/compute/v2/server.py:603 -msgid "Name of new image (default is server name)" -msgstr "" - -#: openstackclient/compute/v2/server.py:608 -msgid "Wait for image create to complete" -msgstr "" - -#: openstackclient/compute/v2/server.py:637 -#, python-format -msgid "Error creating snapshot of server: %s" -msgstr "" - -#: openstackclient/compute/v2/server.py:639 -msgid "" -"\n" -"Error creating server snapshot" -msgstr "" - -#: openstackclient/compute/v2/server.py:656 -msgid "Server(s) to delete (name or ID)" -msgstr "" - -#: openstackclient/compute/v2/server.py:661 -msgid "Wait for delete to complete" -msgstr "" - -#: openstackclient/compute/v2/server.py:679 -#, python-format -msgid "Error deleting server: %s" -msgstr "" - -#: openstackclient/compute/v2/server.py:681 -msgid "" -"\n" -"Error deleting server" -msgstr "" - -#: openstackclient/compute/v2/server.py:693 -msgid "Only return instances that match the reservation" -msgstr "" - -#: openstackclient/compute/v2/server.py:698 -msgid "Regular expression to match IP addresses" -msgstr "" - -#: openstackclient/compute/v2/server.py:703 -msgid "Regular expression to match IPv6 addresses" -msgstr "" - -#: openstackclient/compute/v2/server.py:708 -msgid "Regular expression to match names" -msgstr "" - -#: openstackclient/compute/v2/server.py:713 -msgid "Regular expression to match instance name (admin only)" -msgstr "" - -#: openstackclient/compute/v2/server.py:719 -msgid "Search by server status" -msgstr "" - -#: openstackclient/compute/v2/server.py:724 -msgid "Search by flavor (name or ID)" -msgstr "" - -#: openstackclient/compute/v2/server.py:729 -msgid "Search by image (name or ID)" -msgstr "" - -#: openstackclient/compute/v2/server.py:734 -msgid "Search by hostname" -msgstr "" - -#: openstackclient/compute/v2/server.py:740 -msgid "Include all projects (admin only)" -msgstr "" - -#: openstackclient/compute/v2/server.py:750 -msgid "Search by user (admin only) (name or ID)" -msgstr "" - -#: openstackclient/compute/v2/server.py:905 -msgid "Server(s) to lock (name or ID)" -msgstr "" - -#: openstackclient/compute/v2/server.py:942 -msgid "Target hostname" -msgstr "" - -#: openstackclient/compute/v2/server.py:950 -msgid "Perform a shared live migration (default)" -msgstr "" - -#: openstackclient/compute/v2/server.py:956 -msgid "Perform a block live migration" -msgstr "" - -#: openstackclient/compute/v2/server.py:963 -msgid "Allow disk over-commit on the destination host" -msgstr "" - -#: openstackclient/compute/v2/server.py:970 -msgid "Do not over-commit disk on the destination host (default)" -msgstr "" - -#: openstackclient/compute/v2/server.py:976 -#: openstackclient/compute/v2/server.py:1256 -msgid "Wait for resize to complete" -msgstr "" - -#: openstackclient/compute/v2/server.py:1003 -#: openstackclient/compute/v2/server.py:1280 -msgid "Complete\n" -msgstr "" - -#: openstackclient/compute/v2/server.py:1005 -msgid "" -"\n" -"Error migrating server" -msgstr "" - -#: openstackclient/compute/v2/server.py:1018 -msgid "Server(s) to pause (name or ID)" -msgstr "" - -#: openstackclient/compute/v2/server.py:1048 -msgid "Perform a hard reboot" -msgstr "" - -#: openstackclient/compute/v2/server.py:1056 -msgid "Perform a soft reboot" -msgstr "" - -#: openstackclient/compute/v2/server.py:1061 -msgid "Wait for reboot to complete" -msgstr "" - -#: openstackclient/compute/v2/server.py:1077 -msgid "" -"\n" -"Reboot complete\n" -msgstr "" - -#: openstackclient/compute/v2/server.py:1079 -msgid "" -"\n" -"Error rebooting server\n" -msgstr "" - -#: openstackclient/compute/v2/server.py:1096 -msgid "" -"Recreate server from the specified image (name or ID). Defaults to the " -"currently used one." -msgstr "" - -#: openstackclient/compute/v2/server.py:1107 -msgid "Wait for rebuild to complete" -msgstr "" - -#: openstackclient/compute/v2/server.py:1128 -msgid "" -"\n" -"Complete\n" -msgstr "" - -#: openstackclient/compute/v2/server.py:1130 -msgid "" -"\n" -"Error rebuilding server" -msgstr "" - -#: openstackclient/compute/v2/server.py:1145 -msgid "Name or ID of server to use" -msgstr "" - -#: openstackclient/compute/v2/server.py:1150 -msgid "Name or ID of security group to remove from server" -msgstr "" - -#: openstackclient/compute/v2/server.py:1182 -msgid "Volume to remove (name or ID)" -msgstr "" - -#: openstackclient/compute/v2/server.py:1241 -msgid "Resize server to specified flavor" -msgstr "" - -#: openstackclient/compute/v2/server.py:1246 -msgid "Confirm server resize is complete" -msgstr "" - -#: openstackclient/compute/v2/server.py:1251 -msgid "Restore server state before resize" -msgstr "" - -#: openstackclient/compute/v2/server.py:1282 -msgid "" -"\n" -"Error resizing server" -msgstr "" - -#: openstackclient/compute/v2/server.py:1299 -msgid "Server(s) to restore (name or ID)" -msgstr "" - -#: openstackclient/compute/v2/server.py:1321 -msgid "Server(s) to resume (name or ID)" -msgstr "" - -#: openstackclient/compute/v2/server.py:1353 -msgid "Set new root password (interactive only)" -msgstr "" - -#: openstackclient/compute/v2/server.py:1359 -msgid "" -"Property to add/change for this server (repeat option to set multiple " -"properties)" -msgstr "" - -#: openstackclient/compute/v2/server.py:1382 -msgid "New password: " -msgstr "" - -#: openstackclient/compute/v2/server.py:1383 -msgid "Retype new password: " -msgstr "" - -#: openstackclient/compute/v2/server.py:1387 -msgid "Passwords do not match, password unchanged" -msgstr "" - -#: openstackclient/compute/v2/server.py:1400 -msgid "Server(s) to shelve (name or ID)" -msgstr "" - -#: openstackclient/compute/v2/server.py:1427 -msgid "Display server diagnostics information" -msgstr "" - -#: openstackclient/compute/v2/server.py:1439 -msgid "Error retrieving diagnostics data" -msgstr "" - -#: openstackclient/compute/v2/server.py:1460 -msgid "Login name (ssh -l option)" -msgstr "" - -#: openstackclient/compute/v2/server.py:1472 -msgid "Destination port (ssh -p option)" -msgstr "" - -#: openstackclient/compute/v2/server.py:1484 -msgid "Private key file (ssh -i option)" -msgstr "" - -#: openstackclient/compute/v2/server.py:1495 -msgid "Options in ssh_config(5) format (ssh -o option)" -msgstr "" - -#: openstackclient/compute/v2/server.py:1509 -msgid "Use only IPv4 addresses" -msgstr "" - -#: openstackclient/compute/v2/server.py:1516 -msgid "Use only IPv6 addresses" -msgstr "" - -#: openstackclient/compute/v2/server.py:1525 -msgid "Use public IP address" -msgstr "" - -#: openstackclient/compute/v2/server.py:1533 -msgid "Use private IP address" -msgstr "" - -#: openstackclient/compute/v2/server.py:1540 -msgid "Use other IP address (public, private, etc)" -msgstr "" - -#: openstackclient/compute/v2/server.py:1600 -msgid "Server(s) to start (name or ID)" -msgstr "" - -#: openstackclient/compute/v2/server.py:1622 -msgid "Server(s) to stop (name or ID)" -msgstr "" - -#: openstackclient/compute/v2/server.py:1644 -msgid "Server(s) to suspend (name or ID)" -msgstr "" - -#: openstackclient/compute/v2/server.py:1667 -msgid "Server(s) to unlock (name or ID)" -msgstr "" - -#: openstackclient/compute/v2/server.py:1690 -msgid "Server(s) to unpause (name or ID)" -msgstr "" - -#: openstackclient/compute/v2/server.py:1740 -msgid "" -"Property key to remove from server (repeat option to remove multiple " -"values)" -msgstr "" - -#: openstackclient/compute/v2/server.py:1768 -msgid "Server(s) to unshelve (name or ID)" -msgstr "" - -#: openstackclient/compute/v2/service.py:138 -msgid "argument --disable-reason has been ignored" -msgstr "" - -#: openstackclient/identity/v2_0/catalog.py:67 -#: openstackclient/identity/v3/catalog.py:64 -msgid "Service to display (type or name)" -msgstr "" - -#: openstackclient/identity/v2_0/ec2creds.py:34 -#: openstackclient/identity/v3/ec2creds.py:59 -msgid "" -"Create credentials in project (name or ID; default: current authenticated" -" project)" -msgstr "" - -#: openstackclient/identity/v2_0/ec2creds.py:42 -#: openstackclient/identity/v3/ec2creds.py:67 -msgid "" -"Create credentials for user (name or ID; default: current authenticated " -"user)" -msgstr "" - -#: openstackclient/identity/v2_0/ec2creds.py:90 -#: openstackclient/identity/v2_0/ec2creds.py:157 -#: openstackclient/identity/v3/ec2creds.py:120 -#: openstackclient/identity/v3/ec2creds.py:172 -msgid "Credentials access key" -msgstr "" - -#: openstackclient/identity/v2_0/ec2creds.py:95 -#: openstackclient/identity/v3/ec2creds.py:125 -msgid "Delete credentials for user (name or ID)" -msgstr "" - -#: openstackclient/identity/v2_0/ec2creds.py:122 -#: openstackclient/identity/v3/ec2creds.py:144 -msgid "Filter list by user (name or ID)" -msgstr "" - -#: openstackclient/identity/v2_0/ec2creds.py:162 -#: openstackclient/identity/v3/ec2creds.py:177 -msgid "Show credentials for user (name or ID)" -msgstr "" - -#: openstackclient/identity/v2_0/endpoint.py:34 -msgid "New endpoint service (name or ID)" -msgstr "" - -#: openstackclient/identity/v2_0/endpoint.py:40 -msgid "New endpoint public URL (required)" -msgstr "" - -#: openstackclient/identity/v2_0/endpoint.py:45 -msgid "New endpoint admin URL" -msgstr "" - -#: openstackclient/identity/v2_0/endpoint.py:50 -msgid "New endpoint internal URL" -msgstr "" - -#: openstackclient/identity/v2_0/endpoint.py:55 -msgid "New endpoint region ID" -msgstr "" - -#: openstackclient/identity/v2_0/endpoint.py:84 -msgid "Endpoint ID to delete" -msgstr "" - -#: openstackclient/identity/v2_0/endpoint.py:133 -msgid "Endpoint ID to display" -msgstr "" - -#: openstackclient/identity/v2_0/project.py:36 -msgid "New project name" -msgstr "" - -#: openstackclient/identity/v2_0/project.py:41 -msgid "Project description" -msgstr "" - -#: openstackclient/identity/v2_0/project.py:47 -msgid "Enable project (default)" -msgstr "" - -#: openstackclient/identity/v2_0/project.py:52 -#: openstackclient/identity/v2_0/project.py:179 -msgid "Disable project" -msgstr "" - -#: openstackclient/identity/v2_0/project.py:58 -msgid "Add a property to <name> (repeat option to set multiple properties)" -msgstr "" - -#: openstackclient/identity/v2_0/project.py:64 -#: openstackclient/identity/v3/project.py:75 -msgid "Return existing project" -msgstr "" - -#: openstackclient/identity/v2_0/project.py:109 -msgid "Project(s) to delete (name or ID)" -msgstr "" - -#: openstackclient/identity/v2_0/project.py:158 -#: openstackclient/identity/v2_0/project.py:291 -msgid "Project to modify (name or ID)" -msgstr "" - -#: openstackclient/identity/v2_0/project.py:163 -msgid "Set project name" -msgstr "" - -#: openstackclient/identity/v2_0/project.py:168 -msgid "Set project description" -msgstr "" - -#: openstackclient/identity/v2_0/project.py:174 -msgid "Enable project" -msgstr "" - -#: openstackclient/identity/v2_0/project.py:185 -msgid "Set a project property (repeat option to set multiple properties)" -msgstr "" - -#: openstackclient/identity/v2_0/project.py:234 -msgid "Project to display (name or ID)" -msgstr "" - -#: openstackclient/identity/v2_0/project.py:298 -msgid "Unset a project property (repeat option to unset multiple properties)" -msgstr "" - -#: openstackclient/identity/v2_0/role.py:36 -msgid "Role to add to <project>:<user> (name or ID)" -msgstr "" - -#: openstackclient/identity/v2_0/role.py:42 -#: openstackclient/identity/v2_0/role.py:288 -msgid "Include <project> (name or ID)" -msgstr "" - -#: openstackclient/identity/v2_0/role.py:48 -#: openstackclient/identity/v2_0/role.py:294 -msgid "Include <user> (name or ID)" -msgstr "" - -#: openstackclient/identity/v2_0/role.py:79 -msgid "New role name" -msgstr "" - -#: openstackclient/identity/v2_0/role.py:84 -#: openstackclient/identity/v3/role.py:155 -msgid "Return existing role" -msgstr "" - -#: openstackclient/identity/v2_0/role.py:116 -msgid "Role(s) to delete (name or ID)" -msgstr "" - -#: openstackclient/identity/v2_0/role.py:178 -#: openstackclient/identity/v2_0/role.py:238 -msgid "Project must be specified" -msgstr "" - -#: openstackclient/identity/v2_0/role.py:192 -#: openstackclient/identity/v2_0/role.py:244 -msgid "User must be specified" -msgstr "" - -#: openstackclient/identity/v2_0/role.py:218 -msgid "User to list (name or ID)" -msgstr "" - -#: openstackclient/identity/v2_0/role.py:223 -msgid "Filter users by <project> (name or ID)" -msgstr "" - -#: openstackclient/identity/v2_0/role.py:282 -msgid "Role to remove (name or ID)" -msgstr "" - -#: openstackclient/identity/v2_0/role.py:320 -msgid "Role to display (name or ID)" -msgstr "" - -#: openstackclient/identity/v2_0/service.py:36 -msgid "New service type (compute, image, identity, volume, etc)" -msgstr "" - -#: openstackclient/identity/v2_0/service.py:47 -msgid "New service name" -msgstr "" - -#: openstackclient/identity/v2_0/service.py:52 -msgid "New service description" -msgstr "" - -#: openstackclient/identity/v2_0/service.py:71 -msgid "" -"The argument --type is deprecated, use service create --name <service-" -"name> type instead." -msgstr "" - -#: openstackclient/identity/v2_0/service.py:96 -msgid "Service to delete (name or ID)" -msgstr "" - -#: openstackclient/identity/v2_0/service.py:140 -msgid "Service to display (type, name or ID)" -msgstr "" - -#: openstackclient/identity/v2_0/service.py:146 -msgid "Show service catalog information" -msgstr "" - -#: openstackclient/identity/v2_0/service.py:163 -#, python-format -msgid "No service catalog with a type, name or ID of '%s' exists." -msgstr "" - -#: openstackclient/identity/v2_0/token.py:50 -msgid "Token to be deleted" -msgstr "" - -#: openstackclient/identity/v2_0/user.py:35 -msgid "New user name" -msgstr "" - -#: openstackclient/identity/v2_0/user.py:40 -msgid "Default project (name or ID)" -msgstr "" - -#: openstackclient/identity/v2_0/user.py:45 -#: openstackclient/identity/v2_0/user.py:258 -msgid "Set user password" -msgstr "" - -#: openstackclient/identity/v2_0/user.py:51 -#: openstackclient/identity/v2_0/user.py:264 -msgid "Prompt interactively for password" -msgstr "" - -#: openstackclient/identity/v2_0/user.py:56 -#: openstackclient/identity/v2_0/user.py:269 -msgid "Set user email address" -msgstr "" - -#: openstackclient/identity/v2_0/user.py:62 -#: openstackclient/identity/v2_0/user.py:275 -msgid "Enable user (default)" -msgstr "" - -#: openstackclient/identity/v2_0/user.py:67 -#: openstackclient/identity/v2_0/user.py:280 -msgid "Disable user" -msgstr "" - -#: openstackclient/identity/v2_0/user.py:72 -#: openstackclient/identity/v3/user.py:86 -msgid "Return existing user" -msgstr "" - -#: openstackclient/identity/v2_0/user.py:133 -msgid "User(s) to delete (name or ID)" -msgstr "" - -#: openstackclient/identity/v2_0/user.py:156 -msgid "Filter users by project (name or ID)" -msgstr "" - -#: openstackclient/identity/v2_0/user.py:243 -msgid "User to change (name or ID)" -msgstr "" - -#: openstackclient/identity/v2_0/user.py:248 -msgid "Set user name" -msgstr "" - -#: openstackclient/identity/v2_0/user.py:253 -msgid "Set default project (name or ID)" -msgstr "" - -#: openstackclient/identity/v2_0/user.py:342 -msgid "User to display (name or ID)" -msgstr "" - -#: openstackclient/identity/v3/domain.py:57 -msgid "Return existing domain" -msgstr "" - -#: openstackclient/identity/v3/group.py:130 -msgid "Return existing group" -msgstr "" - -#: openstackclient/identity/v3/region.py:33 -msgid "New region ID" -msgstr "" - -#: openstackclient/identity/v3/region.py:38 -msgid "Parent region ID" -msgstr "" - -#: openstackclient/identity/v3/region.py:43 -#: openstackclient/identity/v3/region.py:128 -msgid "New region description" -msgstr "" - -#: openstackclient/identity/v3/region.py:70 -msgid "Region ID to delete" -msgstr "" - -#: openstackclient/identity/v3/region.py:88 -msgid "Filter by parent region ID" -msgstr "" - -#: openstackclient/identity/v3/region.py:118 -msgid "Region to modify" -msgstr "" - -#: openstackclient/identity/v3/region.py:123 -msgid "New parent region ID" -msgstr "" - -#: openstackclient/identity/v3/region.py:154 -msgid "Region to display" -msgstr "" - -#: openstackclient/image/v1/image.py:191 openstackclient/image/v1/image.py:609 -#: openstackclient/image/v2/image.py:283 openstackclient/image/v2/image.py:794 -msgid "The --owner option is deprecated, please use --project instead." -msgstr "" - -#: openstackclient/network/v2/floating_ip.py:67 -msgid "Network to allocate floating IP from (name or ID)" -msgstr "" - -#: openstackclient/network/v2/floating_ip.py:75 -msgid "Subnet on which you want to create the floating IP (name or ID)" -msgstr "" - -#: openstackclient/network/v2/floating_ip.py:81 -msgid "Port to be associated with the floating IP (name or ID)" -msgstr "" - -#: openstackclient/network/v2/floating_ip.py:88 -msgid "Floating IP address" -msgstr "" - -#: openstackclient/network/v2/floating_ip.py:94 -msgid "Fixed IP address mapped to the floating IP" -msgstr "" - -#: openstackclient/network/v2/floating_ip.py:119 -msgid "Floating IP to delete (IP address or ID)" -msgstr "" - -#: openstackclient/network/v2/floating_ip.py:193 -msgid "Floating IP to display (IP address or ID)" -msgstr "" - -#: openstackclient/network/v2/network.py:114 -msgid "New network name" -msgstr "" - -#: openstackclient/network/v2/network.py:121 -#: openstackclient/network/v2/network.py:385 -msgid "Share the network between projects" -msgstr "" - -#: openstackclient/network/v2/network.py:126 -#: openstackclient/network/v2/network.py:390 -msgid "Do not share the network between projects" -msgstr "" - -#: openstackclient/network/v2/network.py:136 -msgid "Enable network (default)" -msgstr "" - -#: openstackclient/network/v2/network.py:141 -#: openstackclient/network/v2/network.py:378 -msgid "Disable network" -msgstr "" - -#: openstackclient/network/v2/network.py:146 -#: openstackclient/network/v2/port.py:246 -#: openstackclient/network/v2/router.py:174 -#: openstackclient/network/v2/security_group.py:116 -#: openstackclient/network/v2/security_group_rule.py:127 -#: openstackclient/network/v2/subnet.py:183 -#: openstackclient/network/v2/subnet_pool.py:114 -msgid "Owner's project (name or ID)" -msgstr "" - -#: openstackclient/network/v2/network.py:154 -msgid "" -"Availability Zone in which to create this network (Network Availability " -"Zone extension required, repeat option to set multiple availability " -"zones)" -msgstr "" - -#: openstackclient/network/v2/network.py:162 -#: openstackclient/network/v2/network.py:396 -msgid "Set this network as an external network (external-net extension required)" -msgstr "" - -#: openstackclient/network/v2/network.py:168 -msgid "Set this network as an internal network (default)" -msgstr "" - -#: openstackclient/network/v2/network.py:174 -msgid "Specify if this network should be used as the default external network" -msgstr "" - -#: openstackclient/network/v2/network.py:180 -msgid "Do not use the network as the default external network. (default)" -msgstr "" - -#: openstackclient/network/v2/network.py:188 -msgid "" -"The physical mechanism by which the virtual network is implemented. The " -"supported options are: flat, gre, local, vlan, vxlan" -msgstr "" - -#: openstackclient/network/v2/network.py:196 -msgid "Name of the physical network over which the virtual network is implemented" -msgstr "" - -#: openstackclient/network/v2/network.py:203 -msgid "VLAN ID for VLAN networks or Tunnel ID for GRE/VXLAN networks" -msgstr "" - -#: openstackclient/network/v2/network.py:212 -msgid "IPv4 subnet for fixed IPs (in CIDR notation)" -msgstr "" - -#: openstackclient/network/v2/network.py:361 -msgid "Network to modify (name or ID)" -msgstr "" - -#: openstackclient/network/v2/network.py:366 -msgid "Set network name" -msgstr "" - -#: openstackclient/network/v2/network.py:373 -msgid "Enable network" -msgstr "" - -#: openstackclient/network/v2/network.py:402 -msgid "Set this network as an internal network" -msgstr "" - -#: openstackclient/network/v2/network.py:408 -msgid "Set the network as the default external network" -msgstr "" - -#: openstackclient/network/v2/network.py:413 -msgid "Do not use the network as the default external network" -msgstr "" - -#: openstackclient/network/v2/network.py:436 -msgid "Network to display (name or ID)" -msgstr "" - -#: openstackclient/network/v2/port.py:73 -msgid "The --device-id option is deprecated, please use --device instead." -msgstr "" - -#: openstackclient/network/v2/port.py:79 -msgid "The --host-id option is deprecated, please use --host instead." -msgstr "" - -#: openstackclient/network/v2/port.py:161 -msgid "Port device ID" -msgstr "" - -#: openstackclient/network/v2/port.py:171 -msgid "Device owner of this port" -msgstr "" - -#: openstackclient/network/v2/port.py:178 -msgid "" -"VNIC type for this port (direct | direct-physical | macvtap | normal | " -"baremetal, default: normal)" -msgstr "" - -#: openstackclient/network/v2/port.py:187 -msgid "Allocate port on host <host-id> (ID only)" -msgstr "" - -#: openstackclient/network/v2/port.py:206 -msgid "Network this port belongs to (name or ID)" -msgstr "" - -#: openstackclient/network/v2/port.py:214 -#: openstackclient/network/v2/port.py:365 -msgid "" -"Desired IP and/or subnet (name or ID) for this port: subnet=<subnet>,ip-" -"address=<ip-address> (repeat option to set multiple fixed IP addresses)" -msgstr "" - -#: openstackclient/network/v2/port.py:222 -#: openstackclient/network/v2/port.py:379 -msgid "" -"Custom data to be passed as binding:profile: <key>=<value> (repeat option" -" to set multiple binding:profile data)" -msgstr "" - -#: openstackclient/network/v2/port.py:231 -msgid "Enable port (default)" -msgstr "" - -#: openstackclient/network/v2/port.py:236 -#: openstackclient/network/v2/port.py:352 -msgid "Disable port" -msgstr "" - -#: openstackclient/network/v2/port.py:241 -msgid "MAC address of this port" -msgstr "" - -#: openstackclient/network/v2/port.py:280 -msgid "Port(s) to delete (name or ID)" -msgstr "" - -#: openstackclient/network/v2/port.py:301 -msgid "List only ports attached to this router (name or ID)" -msgstr "" - -#: openstackclient/network/v2/port.py:347 -msgid "Enable port" -msgstr "" - -#: openstackclient/network/v2/port.py:357 -msgid "Set port name" -msgstr "" - -#: openstackclient/network/v2/port.py:372 -msgid "Clear existing information of fixed IP addresses" -msgstr "" - -#: openstackclient/network/v2/port.py:386 -msgid "Clear existing information of binding:profile" -msgstr "" - -#: openstackclient/network/v2/port.py:391 -msgid "Port to modify (name or ID)" -msgstr "" - -#: openstackclient/network/v2/port.py:429 -msgid "Port to display (name or ID)" -msgstr "" - -#: openstackclient/network/v2/router.py:99 -msgid "Router to which port will be added (name or ID)" -msgstr "" - -#: openstackclient/network/v2/router.py:104 -msgid "Port to be added (name or ID)" -msgstr "" - -#: openstackclient/network/v2/router.py:123 -msgid "Router to which subnet will be added (name or ID)" -msgstr "" - -#: openstackclient/network/v2/router.py:128 -msgid "Subnet to be added (name or ID)" -msgstr "" - -#: openstackclient/network/v2/router.py:150 -msgid "New router name" -msgstr "" - -#: openstackclient/network/v2/router.py:157 -msgid "Enable router (default)" -msgstr "" - -#: openstackclient/network/v2/router.py:162 -#: openstackclient/network/v2/router.py:351 -msgid "Disable router" -msgstr "" - -#: openstackclient/network/v2/router.py:169 -msgid "Create a distributed router" -msgstr "" - -#: openstackclient/network/v2/router.py:182 -msgid "" -"Availability Zone in which to create this router (Router Availability " -"Zone extension required, repeat option to set multiple availability " -"zones)" -msgstr "" - -#: openstackclient/network/v2/router.py:210 -msgid "Router(s) to delete (name or ID)" -msgstr "" - -#: openstackclient/network/v2/router.py:283 -msgid "Router from which port will be removed (name or ID)" -msgstr "" - -#: openstackclient/network/v2/router.py:288 -msgid "Port to be removed (name or ID)" -msgstr "" - -#: openstackclient/network/v2/router.py:307 -msgid "Router from which the subnet will be removed (name or ID)" -msgstr "" - -#: openstackclient/network/v2/router.py:312 -msgid "Subnet to be removed (name or ID)" -msgstr "" - -#: openstackclient/network/v2/router.py:334 -msgid "Router to modify (name or ID)" -msgstr "" - -#: openstackclient/network/v2/router.py:339 -msgid "Set router name" -msgstr "" - -#: openstackclient/network/v2/router.py:346 -msgid "Enable router" -msgstr "" - -#: openstackclient/network/v2/router.py:357 -msgid "Set router to distributed mode (disabled router only)" -msgstr "" - -#: openstackclient/network/v2/router.py:362 -msgid "Set router to centralized mode (disabled router only)" -msgstr "" - -#: openstackclient/network/v2/router.py:372 -msgid "" -"Routes associated with the router destination: destination subnet (in " -"CIDR notation) gateway: nexthop IP address (repeat option to set multiple" -" routes)" -msgstr "" - -#: openstackclient/network/v2/router.py:380 -msgid "Clear routes associated with the router" -msgstr "" - -#: openstackclient/network/v2/router.py:412 -msgid "Router to display (name or ID)" -msgstr "" - -#: openstackclient/network/v2/security_group.py:103 -#: openstackclient/network/v2/security_group.py:249 -msgid "New security group name" -msgstr "" - -#: openstackclient/network/v2/security_group.py:108 -msgid "Security group description" -msgstr "" - -#: openstackclient/network/v2/security_group.py:173 -msgid "Security group to delete (name or ID)" -msgstr "" - -#: openstackclient/network/v2/security_group.py:208 -msgid "Display information from all projects (admin only)" -msgstr "" - -#: openstackclient/network/v2/security_group.py:244 -msgid "Security group to modify (name or ID)" -msgstr "" - -#: openstackclient/network/v2/security_group.py:254 -msgid "New security group description" -msgstr "" - -#: openstackclient/network/v2/security_group.py:299 -msgid "Security group to display (name or ID)" -msgstr "" - -#: openstackclient/network/v2/security_group_rule.py:82 -msgid "IP protocol (icmp, tcp, udp; default: tcp)" -msgstr "" - -#: openstackclient/network/v2/security_group_rule.py:88 -msgid "" -"Source IP address block (may use CIDR notation; default for IPv4 rule: " -"0.0.0.0/0)" -msgstr "" - -#: openstackclient/network/v2/security_group_rule.py:94 -msgid "Source security group (name or ID)" -msgstr "" - -#: openstackclient/network/v2/security_group_rule.py:101 -msgid "" -"Destination port, may be a single port or port range: 137:139 (only " -"required for IP protocols tcp and udp)" -msgstr "" - -#: openstackclient/network/v2/security_group_rule.py:111 -msgid "Rule applies to incoming network traffic (default)" -msgstr "" - -#: openstackclient/network/v2/security_group_rule.py:116 -msgid "Rule applies to outgoing network traffic" -msgstr "" - -#: openstackclient/network/v2/security_group_rule.py:122 -msgid "Ethertype of network traffic (IPv4, IPv6; default: IPv4)" -msgstr "" - -#: openstackclient/network/v2/security_group_rule.py:221 -msgid "Security group rule to delete (ID only)" -msgstr "" - -#: openstackclient/network/v2/security_group_rule.py:241 -msgid "List all rules in this security group (name or ID)" -msgstr "" - -#: openstackclient/network/v2/security_group_rule.py:336 -msgid "Security group rule to display (ID only)" -msgstr "" - -#: openstackclient/network/v2/subnet.py:54 -msgid "" -"Allocation pool IP addresses for this subnet e.g.: " -"start=192.168.199.2,end=192.168.199.254 (repeat option to add multiple IP" -" addresses)" -msgstr "" - -#: openstackclient/network/v2/subnet.py:63 -msgid "DNS server for this subnet (repeat option to set multiple DNS servers)" -msgstr "" - -#: openstackclient/network/v2/subnet.py:72 -msgid "" -"Additional route for this subnet e.g.: " -"destination=10.10.0.0/16,gateway=192.168.71.254 destination: destination " -"subnet (in CIDR notation) gateway: nexthop IP address (repeat option to " -"add multiple routes)" -msgstr "" - -#: openstackclient/network/v2/subnet.py:178 -msgid "New subnet name" -msgstr "" - -#: openstackclient/network/v2/subnet.py:190 -msgid "Subnet pool from which this subnet will obtain a CIDR (Name or ID)" -msgstr "" - -#: openstackclient/network/v2/subnet.py:196 -msgid "Use default subnet pool for --ip-version" -msgstr "" - -#: openstackclient/network/v2/subnet.py:201 -msgid "Prefix length for subnet allocation from subnet pool" -msgstr "" - -#: openstackclient/network/v2/subnet.py:206 -msgid "" -"Subnet range in CIDR notation (required if --subnet-pool is not " -"specified, optional otherwise)" -msgstr "" - -#: openstackclient/network/v2/subnet.py:215 -msgid "Enable DHCP (default)" -msgstr "" - -#: openstackclient/network/v2/subnet.py:220 -#: openstackclient/network/v2/subnet.py:348 -msgid "Disable DHCP" -msgstr "" - -#: openstackclient/network/v2/subnet.py:226 -msgid "" -"Specify a gateway for the subnet. The three options are: <ip-address>: " -"Specific IP address to use as the gateway, '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')" -msgstr "" - -#: openstackclient/network/v2/subnet.py:238 -msgid "" -"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" -msgstr "" - -#: openstackclient/network/v2/subnet.py:245 -msgid "" -"IPv6 RA (Router Advertisement) mode, valid modes: [dhcpv6-stateful, " -"dhcpv6-stateless, slaac]" -msgstr "" - -#: openstackclient/network/v2/subnet.py:251 -msgid "IPv6 address mode, valid modes: [dhcpv6-stateful, dhcpv6-stateless, slaac]" -msgstr "" - -#: openstackclient/network/v2/subnet.py:258 -msgid "Network this subnet belongs to (name or ID)" -msgstr "" - -#: openstackclient/network/v2/subnet.py:280 -msgid "Subnet to delete (name or ID)" -msgstr "" - -#: openstackclient/network/v2/subnet.py:331 -msgid "Subnet to modify (name or ID)" -msgstr "" - -#: openstackclient/network/v2/subnet.py:336 -msgid "Updated name of the subnet" -msgstr "" - -#: openstackclient/network/v2/subnet.py:343 -msgid "Enable DHCP" -msgstr "" - -#: openstackclient/network/v2/subnet.py:353 -msgid "" -"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" -msgstr "" - -#: openstackclient/network/v2/subnet.py:387 -msgid "Subnet to display (name or ID)" -msgstr "" - -#: openstackclient/network/v2/subnet_pool.py:77 -msgid "" -"Set subnet pool prefixes (in CIDR notation) (repeat option to set " -"multiple prefixes)" -msgstr "" - -#: openstackclient/network/v2/subnet_pool.py:84 -msgid "Set subnet pool default prefix length" -msgstr "" - -#: openstackclient/network/v2/subnet_pool.py:90 -msgid "Set subnet pool minimum prefix length" -msgstr "" - -#: openstackclient/network/v2/subnet_pool.py:96 -msgid "Set subnet pool maximum prefix length" -msgstr "" - -#: openstackclient/network/v2/subnet_pool.py:108 -msgid "Name of the new subnet pool" -msgstr "" - -#: openstackclient/network/v2/subnet_pool.py:120 -#: openstackclient/network/v2/subnet_pool.py:226 -msgid "" -"Set address scope associated with the subnet pool (name or ID), prefixes " -"must be unique across address scopes" -msgstr "" - -#: openstackclient/network/v2/subnet_pool.py:146 -msgid "Subnet pool to delete (name or ID)" -msgstr "" - -#: openstackclient/network/v2/subnet_pool.py:214 -msgid "Subnet pool to modify (name or ID)" -msgstr "" - -#: openstackclient/network/v2/subnet_pool.py:219 -msgid "Set subnet pool name" -msgstr "" - -#: openstackclient/network/v2/subnet_pool.py:233 -msgid "Remove address scope associated with the subnet pool" -msgstr "" - -#: openstackclient/network/v2/subnet_pool.py:262 -msgid "Subnet pool to display (name or ID)" -msgstr "" - diff --git a/openstackclient/locale/zh_TW/LC_MESSAGES/openstackclient.po b/openstackclient/locale/zh_TW/LC_MESSAGES/openstackclient.po index a121acab..d9bbf6fe 100644 --- a/openstackclient/locale/zh_TW/LC_MESSAGES/openstackclient.po +++ b/openstackclient/locale/zh_TW/LC_MESSAGES/openstackclient.po @@ -7,9 +7,9 @@ # Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata msgid "" msgstr "" -"Project-Id-Version: python-openstackclient 2.2.1.dev235\n" +"Project-Id-Version: python-openstackclient 2.5.1.dev51\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" -"POT-Creation-Date: 2016-04-19 05:02+0000\n" +"POT-Creation-Date: 2016-06-03 19:37+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -21,62 +21,6 @@ msgstr "" "X-Generator: Zanata 3.7.3\n" "Language-Team: Chinese (Taiwan)\n" -msgid "" -"\n" -"Complete\n" -msgstr "" -"\n" -"完成\n" - -msgid "" -"\n" -"Error creating server" -msgstr "" -"\n" -"新增雲實例時出錯" - -msgid "" -"\n" -"Error creating server snapshot" -msgstr "" -"\n" -"新增雲實例即時存檔時出錯" - -msgid "" -"\n" -"Error migrating server" -msgstr "" -"\n" -"轉移雲實例出錯" - -msgid "" -"\n" -"Error rebooting server\n" -msgstr "" -"\n" -"重開雲實例出錯\n" - -msgid "" -"\n" -"Error rebuilding server" -msgstr "" -"\n" -"重建雲實例出錯" - -msgid "" -"\n" -"Error resizing server" -msgstr "" -"\n" -"調整雲實例容量時出錯" - -msgid "" -"\n" -"Reboot complete\n" -msgstr "" -"\n" -"重開機完成\n" - msgid "Add a property to <name> (repeat option to set multiple properties)" msgstr "加入屬性到 <name>(重復這選項來設定多個屬性)" @@ -122,9 +66,6 @@ msgstr "啟用用戶(預設)" msgid "Endpoint ID to delete" msgstr "要刪除的端點識別號" -msgid "Endpoint ID to display" -msgstr "要顯示的端點識別號" - #, python-format msgid "Error creating server: %s" msgstr "新增雲實例時出錯:%s" 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..16cc7379 100644 --- a/openstackclient/network/common.py +++ b/openstackclient/network/common.py @@ -12,10 +12,11 @@ # import abc + +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 _ diff --git a/openstackclient/network/v2/address_scope.py b/openstackclient/network/v2/address_scope.py index dbc6865f..bc1a96c3 100644 --- a/openstackclient/network/v2/address_scope.py +++ b/openstackclient/network/v2/address_scope.py @@ -13,9 +13,10 @@ """Address scope 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 exceptions +from osc_lib import utils + from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -94,7 +95,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): @@ -200,9 +201,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 +225,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 new file mode 100644 index 00000000..1d7b2aed --- /dev/null +++ b/openstackclient/network/v2/ip_availability.py @@ -0,0 +1,112 @@ +# 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. +# + +"""IP Availability Info implementations""" + +from osc_lib.command import command +from osc_lib import utils + +from openstackclient.i18n import _ +from openstackclient.identity import common as identity_common + + +_formatters = { + 'subnet_ip_availability': utils.format_list_of_dicts, +} + + +def _get_columns(item): + columns = list(item.keys()) + if 'tenant_id' in columns: + columns.remove('tenant_id') + columns.append('project_id') + return tuple(sorted(columns)) + + +class ListIPAvailability(command.Lister): + """List IP availability for network""" + + def get_parser(self, prog_name): + parser = super(ListIPAvailability, self).get_parser(prog_name) + 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 (default is 4)"), + ) + parser.add_argument( + '--project', + metavar='<project>', + help=_("List IP availability of given project (name or ID)"), + ) + identity_common.add_project_domain_option_to_parser(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + + columns = ( + 'network_id', + 'network_name', + 'total_ips', + 'used_ips', + ) + column_headers = ( + 'Network ID', + 'Network Name', + 'Total IPs', + 'Used IPs', + ) + + filters = {} + if parsed_args.ip_version: + filters['ip_version'] = parsed_args.ip_version + + if parsed_args.project: + identity_client = self.app.client_manager.identity + project_id = identity_common.find_project( + identity_client, + parsed_args.project, + parsed_args.project_domain, + ).id + filters['tenant_id'] = project_id + data = client.network_ip_availabilities(**filters) + return (column_headers, + (utils.get_item_properties( + s, columns, + ) for s in data)) + + +class ShowIPAvailability(command.ShowOne): + """Show network IP availability details""" + + def get_parser(self, prog_name): + parser = super(ShowIPAvailability, self).get_parser(prog_name) + parser.add_argument( + 'network', + metavar="<network>", + help=_("Show IP availability for a specific network (name or ID)"), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + obj = client.find_network_ip_availability(parsed_args.network, + ignore_missing=False) + columns = _get_columns(obj) + data = utils.get_item_properties(obj, columns, formatters=_formatters) + return (columns, data) diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py index bf01e2ec..41735500 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 d8a91fd2..bedf15f7 100644 --- a/openstackclient/network/v2/network_segment.py +++ b/openstackclient/network/v2/network_segment.py @@ -15,9 +15,9 @@ # TODO(rtheis): Add description and name properties when support is available. -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 _ @@ -25,7 +25,7 @@ class ListNetworkSegment(command.Lister): """List network segments (Caution: This is a beta command and subject to change. - Use global option --enable-beta-commands to enable + Use global option --os-beta-command to enable this command) """ @@ -46,11 +46,7 @@ class ListNetworkSegment(command.Lister): return parser def take_action(self, parsed_args): - if not self.app.options.enable_beta_commands: - msg = _('Caution: This is a beta command and subject to ' - 'change. Use global option --enable-beta-commands ' - 'to enable this command.') - raise exceptions.CommandError(msg) + self.validate_os_beta_command_enabled() network_client = self.app.client_manager.network @@ -94,7 +90,7 @@ class ShowNetworkSegment(command.ShowOne): """Display network segment details (Caution: This is a beta command and subject to change. - Use global option --enable-beta-commands to enable + Use global option --os-beta-command to enable this command) """ @@ -108,11 +104,7 @@ class ShowNetworkSegment(command.ShowOne): return parser def take_action(self, parsed_args): - if not self.app.options.enable_beta_commands: - msg = _('Caution: This is a beta command and subject to ' - 'change. Use global option --enable-beta-commands ' - 'to enable this command.') - raise exceptions.CommandError(msg) + self.validate_os_beta_command_enabled() client = self.app.client_manager.network obj = client.find_segment( diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index ca02281f..1c5db706 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -16,10 +16,11 @@ import argparse 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 @@ -266,7 +267,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 +285,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 + self.app.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): @@ -413,9 +427,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 +447,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..92ecf84d 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -17,10 +17,10 @@ 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 utils + from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -204,7 +204,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): @@ -426,10 +426,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 +446,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..18863223 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): diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py index e7e1be99..ceb1cb14 100644 --- a/openstackclient/network/v2/subnet.py +++ b/openstackclient/network/v2/subnet.py @@ -12,12 +12,14 @@ # """Subnet action implementations""" + import copy -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 @@ -373,9 +375,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..79f98fd9 100644 --- a/openstackclient/network/v2/subnet_pool.py +++ b/openstackclient/network/v2/subnet_pool.py @@ -13,10 +13,10 @@ """Subnet pool action implementations""" -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 utils + from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -286,9 +286,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 index c783d013..98e037bc 100644 --- a/openstackclient/releasenotes/notes/bug-1543222-6f8579344ff5c958.yaml +++ b/openstackclient/releasenotes/notes/bug-1543222-6f8579344ff5c958.yaml @@ -3,4 +3,3 @@ 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 9968d73f..12a63af2 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( - '--enable-beta-commands', + '--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 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..520cce13 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() diff --git a/openstackclient/tests/common/test_command.py b/openstackclient/tests/common/test_command.py index 7467d9eb..658bc895 100644 --- a/openstackclient/tests/common/test_command.py +++ b/openstackclient/tests/common/test_command.py @@ -14,7 +14,10 @@ import mock +from osc_lib import exceptions + from openstackclient.common import command +from openstackclient.tests import fakes as test_fakes from openstackclient.tests import utils as test_utils @@ -31,3 +34,16 @@ class TestCommand(test_utils.TestCase): self.assertTrue(hasattr(cmd, 'log')) self.assertEqual('openstackclient.tests.common.test_command.' 'FakeCommand', cmd.log.name) + + def test_validate_os_beta_command_enabled(self): + cmd = FakeCommand(mock.Mock(), mock.Mock()) + cmd.app = mock.Mock() + cmd.app.options = test_fakes.FakeOptions() + + # No exception is raised when enabled. + cmd.app.options.os_beta_command = True + cmd.validate_os_beta_command_enabled() + + cmd.app.options.os_beta_command = False + self.assertRaises(exceptions.CommandError, + cmd.validate_os_beta_command_enabled) 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..60abb8ef 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 @@ -259,6 +239,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 +452,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.""" diff --git a/openstackclient/tests/compute/v2/test_agent.py b/openstackclient/tests/compute/v2/test_agent.py index bdff8c5e..b9040ba9 100644 --- a/openstackclient/tests/compute/v2/test_agent.py +++ b/openstackclient/tests/compute/v2/test_agent.py @@ -13,6 +13,11 @@ # under the License. # +import mock + +from mock import call +from osc_lib import exceptions + from openstackclient.compute.v2 import agent from openstackclient.tests.compute.v2 import fakes as compute_fakes @@ -89,26 +94,73 @@ class TestAgentCreate(TestAgent): class TestAgentDelete(TestAgent): + fake_agents = compute_fakes.FakeAgent.create_agents(count=2) + def setUp(self): super(TestAgentDelete, self).setUp() - self.agents_mock.get.return_value = self.fake_agent + self.agents_mock.get.return_value = self.fake_agents self.cmd = agent.DeleteAgent(self.app, None) - def test_one_agent_delete(self): + def test_delete_one_agent(self): arglist = [ - 'test' + self.fake_agents[0].agent_id + ] + + verifylist = [ + ('id', [self.fake_agents[0].agent_id]), ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.agents_mock.delete.assert_called_with( + self.fake_agents[0].agent_id) + self.assertIsNone(result) + + def test_delete_multiple_agents(self): + arglist = [] + for n in self.fake_agents: + arglist.append(n.agent_id) verifylist = [ - ('id', 'test'), + ('id', arglist), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.agents_mock.delete.assert_called_with(parsed_args.id) + + calls = [] + for n in self.fake_agents: + calls.append(call(n.agent_id)) + self.agents_mock.delete.assert_has_calls(calls) self.assertIsNone(result) + def test_delete_multiple_agents_exception(self): + arglist = [ + self.fake_agents[0].agent_id, + self.fake_agents[1].agent_id, + 'x-y-z', + ] + verifylist = [ + ('id', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + ret_delete = [ + None, + None, + exceptions.NotFound('404') + ] + self.agents_mock.delete = mock.Mock(side_effect=ret_delete) + + self.assertRaises(exceptions.CommandError, self.cmd.take_action, + parsed_args) + calls = [ + call(self.fake_agents[0].agent_id), + call(self.fake_agents[1].agent_id), + ] + self.agents_mock.delete.assert_has_calls(calls) + class TestAgentList(TestAgent): diff --git a/openstackclient/tests/compute/v2/test_flavor.py b/openstackclient/tests/compute/v2/test_flavor.py index 9d424890..4365a540 100644 --- a/openstackclient/tests/compute/v2/test_flavor.py +++ b/openstackclient/tests/compute/v2/test_flavor.py @@ -15,8 +15,9 @@ import copy -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 flavor from openstackclient.tests.compute.v2 import fakes as compute_fakes from openstackclient.tests import fakes @@ -468,6 +469,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,26 +484,27 @@ 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 = [ - '--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_set_no_flavor(self): arglist = [ @@ -510,12 +513,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) @@ -542,7 +541,6 @@ 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) @@ -646,6 +644,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): @@ -662,24 +662,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 = [ @@ -688,12 +690,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) @@ -708,9 +706,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): @@ -720,7 +716,6 @@ 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) 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_server.py b/openstackclient/tests/compute/v2/test_server.py index 01aa9fc1..0f155601 100644 --- a/openstackclient/tests/compute/v2/test_server.py +++ b/openstackclient/tests/compute/v2/test_server.py @@ -12,11 +12,13 @@ # License for the specific language governing permissions and limitations # under the License. # +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 @@ -1129,6 +1131,53 @@ class TestServerSet(TestServer): self.check_parser, self.cmd, arglist, verifylist) + def test_server_set_with_name(self): + arglist = [ + '--name', 'foo_name', + 'foo_vm', + ] + verifylist = [ + ('name', 'foo_name'), + ('server', 'foo_vm'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.fake_servers[0].update.assert_called_once_with(name='foo_name') + self.assertIsNone(result) + + def test_server_set_with_property(self): + arglist = [ + '--property', 'key1=value1', + '--property', 'key2=value2', + 'foo_vm', + ] + verifylist = [ + ('property', {'key1': 'value1', 'key2': 'value2'}), + ('server', 'foo_vm'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.servers_mock.set_meta.assert_called_once_with( + self.fake_servers[0], parsed_args.property) + self.assertIsNone(result) + + @mock.patch.object(getpass, 'getpass', + return_value=mock.sentinel.fake_pass) + def test_server_set_with_root_password(self, mock_getpass): + arglist = [ + '--root-password', + 'foo_vm', + ] + verifylist = [ + ('root_password', True), + ('server', 'foo_vm'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.fake_servers[0].change_password.assert_called_once_with( + mock.sentinel.fake_pass) + self.assertIsNone(result) + class TestServerShelve(TestServer): @@ -1345,6 +1394,45 @@ class TestServerUnpause(TestServer): self.run_method_with_servers('unpause', 3) +class TestServerUnset(TestServer): + + def setUp(self): + super(TestServerUnset, self).setUp() + + self.fake_server = self.setup_servers_mock(1)[0] + + # Get the command object to test + self.cmd = server.UnsetServer(self.app, None) + + def test_server_unset_no_option(self): + arglist = [ + 'foo_vm', + ] + verifylist = [ + ('server', 'foo_vm'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.assertNotCalled(self.servers_mock.delete_meta) + self.assertIsNone(result) + + def test_server_unset_with_property(self): + arglist = [ + '--property', 'key1', + '--property', 'key2', + 'foo_vm', + ] + verifylist = [ + ('property', ['key1', 'key2']), + ('server', 'foo_vm'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.servers_mock.delete_meta.assert_called_once_with( + self.fake_server, ['key1', 'key2']) + self.assertIsNone(result) + + class TestServerUnshelve(TestServer): def setUp(self): @@ -1460,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_service.py b/openstackclient/tests/compute/v2/test_service.py index 7a5a840f..b360c9dc 100644 --- a/openstackclient/tests/compute/v2/test_service.py +++ b/openstackclient/tests/compute/v2/test_service.py @@ -15,6 +15,8 @@ import mock +from osc_lib import exceptions + from openstackclient.compute.v2 import service from openstackclient.tests.compute.v2 import fakes as compute_fakes @@ -128,6 +130,23 @@ class TestServiceSet(TestService): self.cmd = service.SetService(self.app, None) + def test_set_nothing(self): + arglist = [ + self.service.host, + self.service.binary, + ] + verifylist = [ + ('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_not_called() + self.service_mock.disable.assert_not_called() + self.service_mock.disable_log_reason.assert_not_called() + self.assertIsNone(result) + def test_service_set_enable(self): arglist = [ '--enable', @@ -135,7 +154,7 @@ class TestServiceSet(TestService): self.service.binary, ] verifylist = [ - ('enabled', True), + ('enable', True), ('host', self.service.host), ('service', self.service.binary), ] @@ -156,7 +175,7 @@ class TestServiceSet(TestService): self.service.binary, ] verifylist = [ - ('enabled', False), + ('disable', True), ('host', self.service.host), ('service', self.service.binary), ] @@ -179,7 +198,7 @@ class TestServiceSet(TestService): self.service.binary, ] verifylist = [ - ('enabled', False), + ('disable', True), ('disable_reason', reason), ('host', self.service.host), ('service', self.service.binary), @@ -203,24 +222,17 @@ class TestServiceSet(TestService): self.service.binary, ] verifylist = [ - ('enabled', True), ('disable_reason', reason), ('host', self.service.host), ('service', self.service.binary), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - with mock.patch.object(self.cmd.log, 'info') as mock_log: - result = self.cmd.take_action(parsed_args) - - msg = "argument --disable-reason has been ignored" - mock_log.assert_called_once_with(msg) - - self.service_mock.enable.assert_called_with( - self.service.host, - self.service.binary - ) - self.assertIsNone(result) + 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' @@ -231,21 +243,99 @@ class TestServiceSet(TestService): self.service.binary, ] verifylist = [ - ('enabled', True), + ('enable', True), ('disable_reason', reason), ('host', self.service.host), ('service', self.service.binary), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) + 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) - with mock.patch.object(self.cmd.log, 'info') as mock_log: - result = self.cmd.take_action(parsed_args) - - msg = "argument --disable-reason has been ignored" - mock_log.assert_called_once_with(msg) + 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) - self.service_mock.enable.assert_called_with( + def test_service_set_enable_and_state_down(self): + arglist = [ + '--enable', + '--down', self.service.host, - self.service.binary - ) + 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.cmd.log, 'error') as mock_log: + with mock.patch.object(self.service_mock, 'enable', + side_effect=Exception()): + self.assertRaises(exceptions.CommandError, + self.cmd.take_action, parsed_args) + mock_log.assert_called_once_with( + "Failed to set service status to %s", "enabled") + 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 7fb1daa9..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): @@ -99,7 +114,7 @@ class FakeApp(object): class FakeOptions(object): def __init__(self, **kwargs): - self.enable_beta_commands = False + self.os_beta_command = False class FakeClient(object): diff --git a/openstackclient/tests/identity/v2_0/fakes.py b/openstackclient/tests/identity/v2_0/fakes.py index b37bd9da..c613ad82 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 @@ -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,39 @@ 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 diff --git a/openstackclient/tests/identity/v2_0/test_catalog.py b/openstackclient/tests/identity/v2_0/test_catalog.py index d9ae6a80..2fdbafbe 100644 --- a/openstackclient/tests/identity/v2_0/test_catalog.py +++ b/openstackclient/tests/identity/v2_0/test_catalog.py @@ -14,6 +14,7 @@ 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 @@ -49,7 +50,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, ] @@ -74,6 +75,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.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) @@ -82,7 +90,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 = (( @@ -117,9 +124,12 @@ class TestCatalogList(TestCatalog): }, ], } - self.sc_mock.service_catalog.get_data.return_value = [ - fake_service, - ] + auth_ref = identity_fakes.fake_auth_ref( + identity_fakes.TOKEN, + fake_service=fake_service, + ) + self.ar_mock = mock.PropertyMock(return_value=auth_ref) + type(self.app.client_manager).auth_ref = self.ar_mock arglist = [] verifylist = [] @@ -129,7 +139,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 +160,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.fake_service, + ) + self.ar_mock = mock.PropertyMock(return_value=auth_ref) + type(self.app.client_manager).auth_ref = self.ar_mock + arglist = [ 'compute', ] @@ -163,7 +179,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/v2_0/test_role.py b/openstackclient/tests/identity/v2_0/test_role.py index 3c4b79a4..7241c7bd 100644 --- a/openstackclient/tests/identity/v2_0/test_role.py +++ b/openstackclient/tests/identity/v2_0/test_role.py @@ -17,8 +17,8 @@ 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 @@ -26,6 +26,13 @@ from openstackclient.tests.identity.v2_0 import fakes as identity_fakes class TestRole(identity_fakes.TestIdentityv2): + fake_service = copy.deepcopy(identity_fakes.SERVICE) + fake_service['endpoints'] = [ + { + 'publicURL': identity_fakes.ENDPOINT['publicurl'], + }, + ] + def setUp(self): super(TestRole, self).setUp() @@ -41,6 +48,13 @@ 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): @@ -320,7 +334,14 @@ class TestUserRoleList(TestRole): # 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 +353,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) @@ -361,7 +378,14 @@ class TestUserRoleList(TestRole): ), ) self.assertEqual(datalist, tuple(data)) - def test_user_role_list_project(self): + 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 = fakes.FakeResource( None, copy.deepcopy(identity_fakes.PROJECT_2), @@ -375,18 +399,26 @@ class TestUserRoleList(TestRole): ] 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( + identity_fakes.user_id, + identity_fakes.PROJECT_2['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 = (( + identity_fakes.role_id, + identity_fakes.role_name, + identity_fakes.PROJECT_2['name'], + identity_fakes.user_name, + ), ) + self.assertEqual(datalist, tuple(data)) + def test_user_role_list_project_scoped_token(self): self.projects_mock.get.return_value = fakes.FakeResource( None, copy.deepcopy(identity_fakes.PROJECT_2), diff --git a/openstackclient/tests/identity/v2_0/test_token.py b/openstackclient/tests/identity/v2_0/test_token.py index 613139dd..96f08e87 100644 --- a/openstackclient/tests/identity/v2_0/test_token.py +++ b/openstackclient/tests/identity/v2_0/test_token.py @@ -24,10 +24,9 @@ class TestToken(identity_fakes.TestIdentityv2): 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 +34,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,12 +52,10 @@ 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, @@ -61,8 +63,11 @@ class TestTokenIssue(TestToken): 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,12 +76,14 @@ 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, ) diff --git a/openstackclient/tests/identity/v2_0/test_user.py b/openstackclient/tests/identity/v2_0/test_user.py index 921e215d..caf38a6f 100644 --- a/openstackclient/tests/identity/v2_0/test_user.py +++ b/openstackclient/tests/identity/v2_0/test_user.py @@ -152,7 +152,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 @@ -638,7 +638,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) # UserManager.update_password(user, password) diff --git a/openstackclient/tests/identity/v3/fakes.py b/openstackclient/tests/identity/v3/fakes.py index 1422166a..cd1b4bd7 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): 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_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..8fcada6e 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( 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..85522e57 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): diff --git a/openstackclient/tests/image/v1/test_image.py b/openstackclient/tests/image/v1/test_image.py index 018e1199..99c0b0ee 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)], [], 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..96e8734f 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 diff --git a/openstackclient/tests/network/v2/fakes.py b/openstackclient/tests/network/v2/fakes.py index 587fdc1a..6b09e297 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): @@ -196,6 +180,83 @@ class FakeAvailabilityZone(object): return availability_zones +class FakeIPAvailability(object): + """Fake one or more network ip availabilities.""" + + @staticmethod + def create_one_ip_availability(): + """Create a fake list with ip availability stats of a network. + + :return: + A FakeResource object with network_name, network_id, etc. + """ + + # Set default attributes. + network_ip_availability = { + 'network_id': 'network-id-' + uuid.uuid4().hex, + 'network_name': 'network-name-' + uuid.uuid4().hex, + 'tenant_id': '', + 'subnet_ip_availability': [], + 'total_ips': 254, + 'used_ips': 6, + } + + network_ip_availability = fakes.FakeResource( + info=copy.deepcopy(network_ip_availability), + loaded=True) + return network_ip_availability + + @staticmethod + def create_ip_availability(count=2): + """Create fake list of ip availability stats of multiple networks. + + :param int count: + The number of networks to fake + :return: + A list of FakeResource objects faking network ip availability stats + """ + network_ip_availabilities = [] + for i in range(0, count): + network_ip_availability = \ + FakeIPAvailability.create_one_ip_availability() + network_ip_availabilities.append(network_ip_availability) + + 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.""" @@ -550,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.""" @@ -609,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.""" 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 new file mode 100644 index 00000000..c6ec2b0b --- /dev/null +++ b/openstackclient/tests/network/v2/test_ip_availability.py @@ -0,0 +1,184 @@ +# 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 copy +import mock + +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 +from openstackclient.tests.network.v2 import fakes as network_fakes +from openstackclient.tests import utils as tests_utils + + +class TestIPAvailability(network_fakes.TestNetworkV2): + + def setUp(self): + super(TestIPAvailability, self).setUp() + + # Get a shortcut to the network client + self.network = self.app.client_manager.network + + # Set identity client v3. And get a shortcut to Identity client. + identity_client = identity_fakes.FakeIdentityv3Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + self.app.client_manager.identity = identity_client + self.identity = self.app.client_manager.identity + + # Get a shortcut to the ProjectManager Mock + self.projects_mock = self.identity.projects + self.projects_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.PROJECT), + loaded=True, + ) + + +class TestListIPAvailability(TestIPAvailability): + + _ip_availability = \ + network_fakes.FakeIPAvailability.create_ip_availability(count=3) + columns = ( + 'Network ID', + 'Network Name', + 'Total IPs', + 'Used IPs', + ) + data = [] + for net in _ip_availability: + data.append(( + net.network_id, + net.network_name, + net.total_ips, + net.used_ips, + )) + + def setUp(self): + super(TestListIPAvailability, self).setUp() + + self.cmd = ip_availability.ListIPAvailability( + self.app, self.namespace) + self.network.network_ip_availabilities = mock.Mock( + return_value=self._ip_availability) + + def test_list_no_options(self): + arglist = [] + verifylist = [] + + 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( + **filters) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_list_ip_version(self): + arglist = [ + '--ip-version', str(4), + ] + verifylist = [ + ('ip_version', 4) + ] + + 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( + **filters) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_list_project(self): + arglist = [ + '--project', identity_fakes.project_name + ] + verifylist = [ + ('project', identity_fakes.project_name) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + filters = {'tenant_id': identity_fakes.project_id, + 'ip_version': 4} + + self.network.network_ip_availabilities.assert_called_once_with( + **filters) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + +class TestShowIPAvailability(TestIPAvailability): + + _ip_availability = \ + network_fakes.FakeIPAvailability.create_one_ip_availability() + + columns = ( + 'network_id', + 'network_name', + 'project_id', + 'subnet_ip_availability', + 'total_ips', + 'used_ips', + ) + data = ( + _ip_availability.network_id, + _ip_availability.network_name, + _ip_availability.tenant_id, + common_utils.format_list( + _ip_availability.subnet_ip_availability), + _ip_availability.total_ips, + _ip_availability.used_ips, + ) + + def setUp(self): + super(TestShowIPAvailability, self).setUp() + + self.network.find_network_ip_availability = mock.Mock( + return_value=self._ip_availability) + + # Get the command object to test + self.cmd = ip_availability.ShowIPAvailability( + self.app, self.namespace) + + def test_show_no_option(self): + arglist = [] + verifylist = [] + + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_show_all_options(self): + arglist = [ + self._ip_availability.network_name, + ] + verifylist = [ + ('network', self._ip_availability.network_name) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.network.find_network_ip_availability.assert_called_once_with( + self._ip_availability.network_name, + ignore_missing=False) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) 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 2822581c..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 @@ -25,7 +26,7 @@ class TestNetworkSegment(network_fakes.TestNetworkV2): super(TestNetworkSegment, self).setUp() # Enable beta commands. - self.app.options.enable_beta_commands = True + self.app.options.os_beta_command = True # Get a shortcut to the network client self.network = self.app.client_manager.network @@ -89,7 +90,7 @@ class TestListNetworkSegment(TestNetworkSegment): self.assertEqual(self.data, list(data)) def test_list_no_beta_commands(self): - self.app.options.enable_beta_commands = False + self.app.options.os_beta_command = False parsed_args = self.check_parser(self.cmd, [], []) self.assertRaises(exceptions.CommandError, self.cmd.take_action, parsed_args) @@ -174,7 +175,7 @@ class TestShowNetworkSegment(TestNetworkSegment): verifylist = [ ('network_segment', self._network_segment.id), ] - self.app.options.enable_beta_commands = False + self.app.options.os_beta_command = False parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises(exceptions.CommandError, self.cmd.take_action, parsed_args) diff --git a/openstackclient/tests/network/v2/test_port.py b/openstackclient/tests/network/v2/test_port.py index f2aa26cf..779dca05 100644 --- a/openstackclient/tests/network/v2/test_port.py +++ b/openstackclient/tests/network/v2/test_port.py @@ -13,7 +13,10 @@ 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 @@ -174,30 +177,82 @@ class TestCreatePort(TestPort): 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): @@ -283,6 +338,7 @@ class TestSetPort(TestPort): ] verifylist = [ ('fixed_ip', [{'ip-address': '10.0.0.11'}]), + ('port', self._port.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -304,6 +360,7 @@ class TestSetPort(TestPort): ] verifylist = [ ('fixed_ip', [{'ip-address': '10.0.0.12'}]), + ('port', _testport.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) @@ -325,6 +382,7 @@ class TestSetPort(TestPort): ('disable', True), ('no_binding_profile', True), ('no_fixed_ip', True), + ('port', self._port.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -352,7 +410,8 @@ class TestSetPort(TestPort): ('vnic_type', 'macvtap'), ('binding_profile', {'foo': 'bar'}), ('host', 'binding-host-id-xxxx'), - ('name', 'newName') + ('name', 'newName'), + ('port', self._port.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -368,6 +427,21 @@ 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) + class TestShowPort(TestPort): diff --git a/openstackclient/tests/network/v2/test_router.py b/openstackclient/tests/network/v2/test_router.py index 99b41d2d..16296139 100644 --- a/openstackclient/tests/network/v2/test_router.py +++ b/openstackclient/tests/network/v2/test_router.py @@ -13,8 +13,8 @@ import mock -from openstackclient.common import exceptions -from openstackclient.common import utils as osc_utils +from osc_lib 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 @@ -568,12 +568,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..de7e1821 100644 --- a/openstackclient/tests/network/v2/test_subnet.py +++ b/openstackclient/tests/network/v2/test_subnet.py @@ -14,8 +14,8 @@ import copy import mock -from openstackclient.common import exceptions -from openstackclient.common import utils +from osc_lib 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 @@ -549,8 +549,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( diff --git a/openstackclient/tests/network/v2/test_subnet_pool.py b/openstackclient/tests/network/v2/test_subnet_pool.py index de12c9e9..10ef76d8 100644 --- a/openstackclient/tests/network/v2/test_subnet_pool.py +++ b/openstackclient/tests/network/v2/test_subnet_pool.py @@ -15,8 +15,8 @@ import argparse import copy import mock -from openstackclient.common import exceptions -from openstackclient.common import utils +from osc_lib 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 @@ -443,10 +443,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/fakes.py b/openstackclient/tests/volume/v1/fakes.py index d6c46439..6c349866 100644 --- a/openstackclient/tests/volume/v1/fakes.py +++ b/openstackclient/tests/volume/v1/fakes.py @@ -129,6 +129,57 @@ QOS_WITH_ASSOCIATIONS = { } +class FakeTransferClient(object): + + def __init__(self, **kwargs): + + self.transfers = mock.Mock() + self.transfers.resource_class = fakes.FakeResource(None, {}) + + +class TestTransfer(utils.TestCommand): + + def setUp(self): + super(TestTransfer, self).setUp() + + self.app.client_manager.volume = FakeTransferClient( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN + ) + + +class FakeTransfer(object): + """Fake one or more Transfer.""" + + @staticmethod + def create_one_transfer(attrs=None): + """Create a fake transfer. + + :param Dictionary attrs: + A dictionary with all attributes of Transfer Request + :retrun: + A FakeResource object with volume_id, name, id. + """ + # Set default attribute + transfer_info = { + 'volume_id': 'ce26708d-a7f8-4b4b-9861-4a80256615a7', + 'name': 'fake_transfer_name', + 'id': '731a7f53-aa92-4fbd-9de3-6f7d729c926b' + } + + # Overwrite default attributes if there are some attributes set + attrs = attrs or {} + + transfer_info.update(attrs) + + transfer = fakes.FakeResource( + None, + transfer_info, + loaded=True) + + return transfer + + class FakeServiceClient(object): def __init__(self, **kwargs): @@ -171,8 +222,8 @@ class FakeService(object): } # Overwrite default attributes if there are some attributes set - if attrs is None: - attrs = {} + attrs = attrs or {} + service_info.update(attrs) service = fakes.FakeResource( 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_transfer_request.py b/openstackclient/tests/volume/v1/test_transfer_request.py new file mode 100644 index 00000000..94e02d62 --- /dev/null +++ b/openstackclient/tests/volume/v1/test_transfer_request.py @@ -0,0 +1,114 @@ +# +# 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. +# + + +from openstackclient.tests.volume.v1 import fakes as transfer_fakes +from openstackclient.volume.v1 import volume_transfer_request + + +class TestTransfer(transfer_fakes.TestTransfer): + + def setUp(self): + super(TestTransfer, self).setUp() + + # Get a shortcut to the TransferManager Mock + self.transfer_mock = self.app.client_manager.volume.transfers + self.transfer_mock.reset_mock() + + +class TestTransferList(TestTransfer): + + # The Transfers to be listed + volume_transfers = transfer_fakes.FakeTransfer.create_one_transfer() + + def setUp(self): + super(TestTransferList, self).setUp() + + self.transfer_mock.list.return_value = [self.volume_transfers] + + # Get the command object to test + self.cmd = volume_transfer_request.ListTransferRequests(self.app, None) + + def test_transfer_list_without_argument(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) + + expected_columns = [ + 'ID', + 'Volume', + 'Name', + ] + + # confirming if all expected columns are present in the result. + self.assertEqual(expected_columns, columns) + + datalist = (( + self.volume_transfers.id, + self.volume_transfers.volume_id, + self.volume_transfers.name, + ), ) + + # confirming if all expected values are present in the result. + self.assertEqual(datalist, tuple(data)) + + # checking if proper call was made to list volume_transfers + self.transfer_mock.list.assert_called_with( + detailed=True, + search_opts={'all_tenants': 0} + ) + + def test_transfer_list_with_argument(self): + arglist = [ + "--all-projects" + ] + verifylist = [ + ("all_projects", True) + ] + + 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) + + expected_columns = [ + 'ID', + 'Volume', + 'Name', + ] + + # confirming if all expected columns are present in the result. + self.assertEqual(expected_columns, columns) + + datalist = (( + self.volume_transfers.id, + self.volume_transfers.volume_id, + self.volume_transfers.name, + ), ) + + # confirming if all expected values are present in the result. + self.assertEqual(datalist, tuple(data)) + + # checking if proper call was made to list volume_transfers + self.transfer_mock.list.assert_called_with( + detailed=True, + search_opts={'all_tenants': 1} + ) diff --git a/openstackclient/tests/volume/v1/test_volume.py b/openstackclient/tests/volume/v1/test_volume.py index e0fd1c08..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 @@ -578,9 +579,6 @@ class TestVolumeSet(TestVolume): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - - self.assertEqual("No changes requested\n", - self.app.log.messages.get('error')) self.assertIsNone(result) def test_volume_set_name(self): @@ -659,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, @@ -675,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', @@ -697,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 158ab0ab..1cbbf68a 100644 --- a/openstackclient/tests/volume/v2/fakes.py +++ b/openstackclient/tests/volume/v2/fakes.py @@ -17,219 +17,63 @@ 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): + + def __init__(self, **kwargs): + + self.transfers = mock.Mock() + self.transfers.resource_class = fakes.FakeResource(None, {}) + + +class TestTransfer(utils.TestCommand): + + def setUp(self): + super(TestTransfer, self).setUp() + + self.app.client_manager.volume = FakeTransferClient( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN + ) + + +class FakeTransfer(object): + """Fake one or more Transfer.""" + + @staticmethod + def create_one_transfer(attrs=None): + """Create a fake transfer. + + :param Dictionary attrs: + A dictionary with all attributes of Transfer Request + :retrun: + A FakeResource object with volume_id, name, id. + """ + # Set default attribute + transfer_info = { + 'volume_id': 'ce26708d-a7f8-4b4b-9861-4a80256615a7', + 'name': 'fake_transfer_name', + 'id': '731a7f53-aa92-4fbd-9de3-6f7d729c926b' + } + + # Overwrite default attributes if there are some attributes set + attrs = attrs or {} + + transfer_info.update(attrs) + + transfer = fakes.FakeResource( + None, + transfer_info, + loaded=True) + + return transfer class FakeServiceClient(object): @@ -274,8 +118,8 @@ class FakeService(object): } # Overwrite default attributes if there are some attributes set - if attrs is None: - attrs = {} + attrs = attrs or {} + service_info.update(attrs) service = fakes.FakeResource( @@ -328,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() @@ -592,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_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_transfer_request.py b/openstackclient/tests/volume/v2/test_transfer_request.py new file mode 100644 index 00000000..945833c9 --- /dev/null +++ b/openstackclient/tests/volume/v2/test_transfer_request.py @@ -0,0 +1,114 @@ +# +# 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. +# + + +from openstackclient.tests.volume.v2 import fakes as transfer_fakes +from openstackclient.volume.v2 import volume_transfer_request + + +class TestTransfer(transfer_fakes.TestTransfer): + + def setUp(self): + super(TestTransfer, self).setUp() + + # Get a shortcut to the TransferManager Mock + self.transfer_mock = self.app.client_manager.volume.transfers + self.transfer_mock.reset_mock() + + +class TestTransferList(TestTransfer): + + # The Transfers to be listed + volume_transfers = transfer_fakes.FakeTransfer.create_one_transfer() + + def setUp(self): + super(TestTransferList, self).setUp() + + self.transfer_mock.list.return_value = [self.volume_transfers] + + # Get the command object to test + self.cmd = volume_transfer_request.ListTransferRequests(self.app, None) + + def test_transfer_list_without_argument(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) + + expected_columns = [ + 'ID', + 'Volume', + 'Name', + ] + + # confirming if all expected columns are present in the result. + self.assertEqual(expected_columns, columns) + + datalist = (( + self.volume_transfers.id, + self.volume_transfers.volume_id, + self.volume_transfers.name, + ), ) + + # confirming if all expected values are present in the result. + self.assertEqual(datalist, tuple(data)) + + # checking if proper call was made to list volume_transfers + self.transfer_mock.list.assert_called_with( + detailed=True, + search_opts={'all_tenants': 0} + ) + + def test_transfer_list_with_argument(self): + arglist = [ + "--all-projects" + ] + verifylist = [ + ("all_projects", True) + ] + + 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) + + expected_columns = [ + 'ID', + 'Volume', + 'Name', + ] + + # confirming if all expected columns are present in the result. + self.assertEqual(expected_columns, columns) + + datalist = (( + self.volume_transfers.id, + self.volume_transfers.volume_id, + self.volume_transfers.name, + ), ) + + # confirming if all expected values are present in the result. + self.assertEqual(datalist, tuple(data)) + + # checking if proper call was made to list volume_transfers + self.transfer_mock.list.assert_called_with( + detailed=True, + search_opts={'all_tenants': 1} + ) diff --git a/openstackclient/tests/volume/v2/test_type.py b/openstackclient/tests/volume/v2/test_type.py index 872b4ae9..7eea1c3c 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 @@ -397,17 +398,19 @@ class TestTypeUnset(TestType): def test_type_unset(self): arglist = [ '--property', 'property', + '--property', 'multi_property', self.volume_type.id, ] verifylist = [ - ('property', 'property'), + ('property', ['property', 'multi_property']), ('volume_type', self.volume_type.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.volume_type.unset_keys.assert_called_once_with('property') + self.volume_type.unset_keys.assert_called_once_with( + ['property', 'multi_property']) self.assertIsNone(result) def test_type_unset_project_access(self): 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..212c74ef 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 _ diff --git a/openstackclient/volume/v1/qos_specs.py b/openstackclient/volume/v1/qos_specs.py index b9eb8752..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 _ @@ -202,8 +202,6 @@ class SetQos(command.Command): if parsed_args.property: volume_client.qos_specs.set_keys(qos_spec.id, parsed_args.property) - else: - self.app.log.error(_("No changes requested\n")) class ShowQos(command.ShowOne): @@ -263,5 +261,3 @@ class UnsetQos(command.Command): if parsed_args.property: volume_client.qos_specs.unset_keys(qos_spec.id, parsed_args.property) - else: - self.app.log.error(_("No changes requested\n")) 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 bf5bf264..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 _ @@ -208,10 +209,6 @@ class SetSnapshot(command.Command): if parsed_args.description: kwargs['display_description'] = parsed_args.description - if not kwargs and not parsed_args.property: - self.app.log.error(_("No changes requested\n")) - return - snapshot.update(**kwargs) @@ -270,5 +267,3 @@ class UnsetSnapshot(command.Command): snapshot.id, parsed_args.property, ) - else: - self.app.log.error(_("No changes requested\n")) diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py index 11e42f83..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) @@ -363,9 +367,6 @@ class SetVolume(command.Command): if kwargs: volume_client.volumes.update(volume.id, **kwargs) - if not kwargs and not parsed_args.property and not parsed_args.size: - self.app.log.error(_("No changes requested\n")) - class ShowVolume(command.ShowOne): """Show volume details""" @@ -428,5 +429,3 @@ class UnsetVolume(command.Command): volume.id, parsed_args.property, ) - else: - self.app.log.error(_("No changes requested\n")) diff --git a/openstackclient/volume/v1/volume_transfer_request.py b/openstackclient/volume/v1/volume_transfer_request.py new file mode 100644 index 00000000..5d8ff683 --- /dev/null +++ b/openstackclient/volume/v1/volume_transfer_request.py @@ -0,0 +1,51 @@ +# +# 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. +# + +"""Volume v2 transfer action implementations""" + +from osc_lib.command import command +from osc_lib import utils + +from openstackclient.i18n import _ + + +class ListTransferRequests(command.Lister): + """Lists all volume transfer requests.""" + + def get_parser(self, prog_name): + parser = super(ListTransferRequests, self).get_parser(prog_name) + parser.add_argument( + '--all-projects', + dest='all_projects', + action="store_true", + default=False, + help=_('Shows detail for all projects. Admin only. ' + '(defaults to False)') + ) + return parser + + def take_action(self, parsed_args): + columns = ['ID', 'Volume ID', 'Name'] + column_headers = ['ID', 'Volume', 'Name'] + + volume_client = self.app.client_manager.volume + + volume_transfer_result = volume_client.transfers.list( + detailed=True, + search_opts={'all_tenants': parsed_args.all_projects} + ) + + return (column_headers, ( + utils.get_item_properties(s, columns) + for s in volume_transfer_result)) diff --git a/openstackclient/volume/v1/volume_type.py b/openstackclient/volume/v1/volume_type.py index 73927022..289966e5 100644 --- a/openstackclient/volume/v1/volume_type.py +++ b/openstackclient/volume/v1/volume_type.py @@ -15,11 +15,11 @@ """Volume v1 Type 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 _ @@ -181,5 +181,3 @@ class UnsetVolumeType(command.Command): if parsed_args.property: volume_type.unset_keys(parsed_args.property) - else: - self.app.log.error(_("No changes requested\n")) diff --git a/openstackclient/volume/v2/backup.py b/openstackclient/volume/v2/backup.py index e6fbe78d..6a44e30c 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 _ diff --git a/openstackclient/volume/v2/qos_specs.py b/openstackclient/volume/v2/qos_specs.py index 3cea3303..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 _ @@ -202,8 +202,6 @@ class SetQos(command.Command): if parsed_args.property: volume_client.qos_specs.set_keys(qos_spec.id, parsed_args.property) - else: - self.app.log.error(_("No changes requested\n")) class ShowQos(command.ShowOne): @@ -263,5 +261,3 @@ class UnsetQos(command.Command): if parsed_args.property: volume_client.qos_specs.unset_keys(qos_spec.id, parsed_args.property) - else: - self.app.log.error(_("No changes requested\n")) 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 2886aaef..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 _ @@ -205,11 +205,6 @@ class SetSnapshot(command.Command): if parsed_args.description: kwargs['description'] = parsed_args.description - if (not kwargs and not parsed_args.property and not - parsed_args.state): - self.app.log.error(_("No changes requested\n")) - return - if parsed_args.property: volume_client.volume_snapshots.set_metadata(snapshot.id, parsed_args.property) @@ -271,5 +266,3 @@ class UnsetSnapshot(command.Command): snapshot.id, parsed_args.property, ) - else: - self.app.log.error(_("No changes requested\n")) diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index 0e07aa78..e54395fa 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) @@ -385,10 +388,6 @@ class SetVolume(command.Command): if kwargs: volume_client.volumes.update(volume.id, **kwargs) - if (not kwargs and not parsed_args.property - and not parsed_args.image_property and not parsed_args.size): - self.app.log.error(_("No changes requested\n")) - class ShowVolume(command.ShowOne): """Display volume details""" @@ -460,4 +459,4 @@ class UnsetVolume(command.Command): 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")) + LOG.error(_("No changes requested")) diff --git a/openstackclient/volume/v2/volume_transfer_request.py b/openstackclient/volume/v2/volume_transfer_request.py new file mode 100644 index 00000000..5d8ff683 --- /dev/null +++ b/openstackclient/volume/v2/volume_transfer_request.py @@ -0,0 +1,51 @@ +# +# 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. +# + +"""Volume v2 transfer action implementations""" + +from osc_lib.command import command +from osc_lib import utils + +from openstackclient.i18n import _ + + +class ListTransferRequests(command.Lister): + """Lists all volume transfer requests.""" + + def get_parser(self, prog_name): + parser = super(ListTransferRequests, self).get_parser(prog_name) + parser.add_argument( + '--all-projects', + dest='all_projects', + action="store_true", + default=False, + help=_('Shows detail for all projects. Admin only. ' + '(defaults to False)') + ) + return parser + + def take_action(self, parsed_args): + columns = ['ID', 'Volume ID', 'Name'] + column_headers = ['ID', 'Volume', 'Name'] + + volume_client = self.app.client_manager.volume + + volume_transfer_result = volume_client.transfers.list( + detailed=True, + search_opts={'all_tenants': parsed_args.all_projects} + ) + + return (column_headers, ( + utils.get_item_properties(s, columns) + for s in volume_transfer_result)) diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py index 96f13c83..5e9faa1d 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""" @@ -176,13 +181,6 @@ class SetVolumeType(command.Command): volume_type = utils.find_resource( volume_client.volume_types, parsed_args.volume_type) - if (not parsed_args.name - and not parsed_args.description - and not parsed_args.property - and not parsed_args.project): - self.app.log.error(_("No changes requested\n")) - return - result = 0 kwargs = {} if parsed_args.name: @@ -197,16 +195,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: ") + - str(e)) + LOG.error(_("Failed to set volume type property: %s"), e) result += 1 if parsed_args.project: @@ -220,13 +217,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): @@ -263,6 +260,7 @@ class UnsetVolumeType(command.Command): parser.add_argument( '--property', metavar='<key>', + action='append', help=_('Remove a property from this volume type ' '(repeat option to remove multiple properties)'), ) @@ -285,18 +283,12 @@ class UnsetVolumeType(command.Command): parsed_args.volume_type, ) - if (not parsed_args.property - and not parsed_args.project): - self.app.log.error(_("No changes requested\n")) - return - result = 0 if parsed_args.property: 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: @@ -310,8 +302,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: |
