diff options
Diffstat (limited to 'openstackclient')
56 files changed, 1075 insertions, 364 deletions
diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py index 13c6b92c..ec005dc0 100644 --- a/openstackclient/common/clientmanager.py +++ b/openstackclient/common/clientmanager.py @@ -20,6 +20,7 @@ import logging import pkg_resources import sys +from keystoneauth1.loading import base from osc_lib.api import auth from osc_lib import exceptions from oslo_utils import strutils @@ -37,6 +38,77 @@ PLUGIN_MODULES = [] USER_AGENT = 'python-openstackclient' +# NOTE(dtroyer): Bringing back select_auth_plugin() and build_auth_params() +# temporarily because osc-lib 0.3.0 removed it a wee bit early +def select_auth_plugin(options): + """Pick an auth plugin based on --os-auth-type or other options""" + + auth_plugin_name = None + + # Do the token/url check first as this must override the default + # 'password' set by os-client-config + # Also, url and token are not copied into o-c-c's auth dict (yet?) + if options.auth.get('url') and options.auth.get('token'): + # service token authentication + auth_plugin_name = 'token_endpoint' + elif options.auth_type in auth.PLUGIN_LIST: + # A direct plugin name was given, use it + auth_plugin_name = options.auth_type + elif options.auth.get('username'): + if options.identity_api_version == '3': + auth_plugin_name = 'v3password' + elif options.identity_api_version.startswith('2'): + auth_plugin_name = 'v2password' + else: + # let keystoneauth figure it out itself + auth_plugin_name = 'password' + elif options.auth.get('token'): + if options.identity_api_version == '3': + auth_plugin_name = 'v3token' + elif options.identity_api_version.startswith('2'): + auth_plugin_name = 'v2token' + else: + # let keystoneauth figure it out itself + auth_plugin_name = 'token' + else: + # The ultimate default is similar to the original behaviour, + # but this time with version discovery + auth_plugin_name = 'password' + LOG.debug("Auth plugin %s selected", auth_plugin_name) + return auth_plugin_name + + +def build_auth_params(auth_plugin_name, cmd_options): + if auth_plugin_name: + LOG.debug('auth_type: %s', auth_plugin_name) + auth_plugin_loader = base.get_plugin_loader(auth_plugin_name) + auth_params = { + opt.dest: opt.default + for opt in base.get_plugin_options(auth_plugin_name) + } + auth_params.update(dict(cmd_options.auth)) + # grab tenant from project for v2.0 API compatibility + if auth_plugin_name.startswith("v2"): + if 'project_id' in auth_params: + auth_params['tenant_id'] = auth_params['project_id'] + del auth_params['project_id'] + if 'project_name' in auth_params: + auth_params['tenant_name'] = auth_params['project_name'] + del auth_params['project_name'] + else: + LOG.debug('no auth_type') + # delay the plugin choice, grab every option + auth_plugin_loader = None + auth_params = dict(cmd_options.auth) + plugin_options = set( + [o.replace('-', '_') for o in auth.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_loader, auth_params) + + class ClientCache(object): """Descriptor class for caching created client handles.""" @@ -193,7 +265,7 @@ class ClientManager(object): # If no auth type is named by the user, select one based on # the supplied options - self.auth_plugin_name = auth.select_auth_plugin(self._cli_options) + self.auth_plugin_name = select_auth_plugin(self._cli_options) # Basic option checking to avoid unhelpful error messages auth.check_valid_authentication_options(self._cli_options, @@ -205,7 +277,7 @@ class ClientManager(object): not self._cli_options.auth.get('password')): self._cli_options.auth['password'] = self._pw_callback() - (auth_plugin, self._auth_params) = auth.build_auth_params( + (auth_plugin, self._auth_params) = build_auth_params( self.auth_plugin_name, self._cli_options, ) diff --git a/openstackclient/compute/v2/agent.py b/openstackclient/compute/v2/agent.py index 4d923955..76c1b3b7 100644 --- a/openstackclient/compute/v2/agent.py +++ b/openstackclient/compute/v2/agent.py @@ -152,28 +152,48 @@ class SetAgent(command.Command): help=_("ID of the agent") ) parser.add_argument( - "version", + "--agent-version", + dest="version", metavar="<version>", help=_("Version of the agent") ) parser.add_argument( - "url", + "--url", metavar="<url>", - help=_("URL") + help=_("URL of the agent") ) parser.add_argument( - "md5hash", + "--md5hash", metavar="<md5hash>", - help=_("MD5 hash") + help=_("MD5 hash of the agent") ) return parser def take_action(self, parsed_args): compute_client = self.app.client_manager.compute + data = compute_client.agents.list(hypervisor=None) + agent = {} + + for s in data: + if s.agent_id == int(parsed_args.id): + agent['version'] = s.version + agent['url'] = s.url + agent['md5hash'] = s.md5hash + if agent == {}: + msg = _("No agent with a ID of '%(id)s' exists.") + raise exceptions.CommandError(msg % parsed_args.id) + + if parsed_args.version: + agent['version'] = parsed_args.version + if parsed_args.url: + agent['url'] = parsed_args.url + if parsed_args.md5hash: + agent['md5hash'] = parsed_args.md5hash + args = ( parsed_args.id, - parsed_args.version, - parsed_args.url, - parsed_args.md5hash + agent['version'], + agent['url'], + agent['md5hash'], ) compute_client.agents.update(*args) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 7d04ffc3..a317c11d 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -1222,7 +1222,7 @@ class RemoveServerSecurityGroup(command.Command): parsed_args.group, ) - server.remove_security_group(security_group) + server.remove_security_group(security_group.id) class RemoveServerVolume(command.Command): diff --git a/openstackclient/identity/v3/consumer.py b/openstackclient/identity/v3/consumer.py index a4620bf9..b41a37ca 100644 --- a/openstackclient/identity/v3/consumer.py +++ b/openstackclient/identity/v3/consumer.py @@ -15,15 +15,19 @@ """Identity v3 Consumer action implementations""" -import sys +import logging from osc_lib.command import command +from osc_lib import exceptions from osc_lib import utils import six from openstackclient.i18n import _ +LOG = logging.getLogger(__name__) + + class CreateConsumer(command.ShowOne): """Create new consumer""" @@ -46,22 +50,37 @@ class CreateConsumer(command.ShowOne): class DeleteConsumer(command.Command): - """Delete consumer""" + """Delete consumer(s)""" def get_parser(self, prog_name): parser = super(DeleteConsumer, self).get_parser(prog_name) parser.add_argument( 'consumer', metavar='<consumer>', - help=_('Consumer to delete'), + nargs='+', + help=_('Consumer(s) to delete'), ) return parser def take_action(self, parsed_args): identity_client = self.app.client_manager.identity - consumer = utils.find_resource( - identity_client.oauth1.consumers, parsed_args.consumer) - identity_client.oauth1.consumers.delete(consumer.id) + result = 0 + for i in parsed_args.consumer: + try: + consumer = utils.find_resource( + identity_client.oauth1.consumers, i) + identity_client.oauth1.consumers.delete(consumer.id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete consumer with name or " + "ID '%(consumer)s': %(e)s") + % {'consumer': i, 'e': e}) + + if result > 0: + total = len(parsed_args.consumer) + msg = (_("%(result)s of %(total)s consumers failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class ListConsumer(command.Lister): @@ -102,10 +121,6 @@ class SetConsumer(command.Command): if parsed_args.description: kwargs['description'] = parsed_args.description - if not len(kwargs): - sys.stdout.write(_('Consumer not updated, no arguments present\n')) - return - consumer = identity_client.oauth1.consumers.update( consumer.id, **kwargs) diff --git a/openstackclient/identity/v3/credential.py b/openstackclient/identity/v3/credential.py index eeeddfa5..0ef94cf4 100644 --- a/openstackclient/identity/v3/credential.py +++ b/openstackclient/identity/v3/credential.py @@ -15,22 +15,28 @@ """Identity v3 Credential action implementations""" +import logging + from osc_lib.command import command +from osc_lib import exceptions from osc_lib import utils import six from openstackclient.i18n import _ +LOG = logging.getLogger(__name__) + + class CreateCredential(command.ShowOne): - """Create credential command""" + """Create new credential""" def get_parser(self, prog_name): parser = super(CreateCredential, self).get_parser(prog_name) parser.add_argument( 'user', metavar='<user>', - help=_('Name or ID of user that owns the credential'), + help=_('user that owns the credential (name or ID)'), ) parser.add_argument( '--type', @@ -47,8 +53,8 @@ class CreateCredential(command.ShowOne): parser.add_argument( '--project', metavar='<project>', - help=_('Project name or ID which limits the ' - 'scope of the credential'), + help=_('Project which limits the scope of ' + 'the credential (name or ID)'), ) return parser @@ -72,24 +78,39 @@ class CreateCredential(command.ShowOne): class DeleteCredential(command.Command): - """Delete credential command""" + """Delete credential(s)""" def get_parser(self, prog_name): parser = super(DeleteCredential, self).get_parser(prog_name) parser.add_argument( 'credential', metavar='<credential-id>', - help=_('ID of credential to delete'), + nargs='+', + help=_('ID of credential(s) to delete'), ) return parser def take_action(self, parsed_args): identity_client = self.app.client_manager.identity - identity_client.credentials.delete(parsed_args.credential) + result = 0 + for i in parsed_args.credential: + try: + identity_client.credentials.delete(i) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete credentials with " + "ID '%(credential)s': %(e)s") + % {'credential': i, 'e': e}) + + if result > 0: + total = len(parsed_args.credential) + msg = (_("%(result)s of %(total)s credential failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class ListCredential(command.Lister): - """List credential command""" + """List credentials""" def take_action(self, parsed_args): columns = ('ID', 'Type', 'User ID', 'Blob', 'Project ID') @@ -103,7 +124,7 @@ class ListCredential(command.Lister): class SetCredential(command.Command): - """Set credential command""" + """Set credential properties""" def get_parser(self, prog_name): parser = super(SetCredential, self).get_parser(prog_name) @@ -116,7 +137,7 @@ class SetCredential(command.Command): '--user', metavar='<user>', required=True, - help=_('Name or ID of user that owns the credential'), + help=_('User that owns the credential (name or ID)'), ) parser.add_argument( '--type', @@ -134,8 +155,8 @@ class SetCredential(command.Command): parser.add_argument( '--project', metavar='<project>', - help=_('Project name or ID which limits the ' - 'scope of the credential'), + help=_('Project which limits the scope of ' + 'the credential (name or ID)'), ) return parser @@ -159,7 +180,7 @@ class SetCredential(command.Command): class ShowCredential(command.ShowOne): - """Show credential command""" + """Display credential details""" def get_parser(self, prog_name): parser = super(ShowCredential, self).get_parser(prog_name) diff --git a/openstackclient/identity/v3/domain.py b/openstackclient/identity/v3/domain.py index 0546ac52..76e47d32 100755 --- a/openstackclient/identity/v3/domain.py +++ b/openstackclient/identity/v3/domain.py @@ -16,10 +16,10 @@ """Identity v3 Domain action implementations""" import logging -import sys 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 @@ -89,22 +89,36 @@ class CreateDomain(command.ShowOne): class DeleteDomain(command.Command): - """Delete domain""" + """Delete domain(s)""" def get_parser(self, prog_name): parser = super(DeleteDomain, self).get_parser(prog_name) parser.add_argument( 'domain', metavar='<domain>', - help=_('Domain to delete (name or ID)'), + nargs='+', + help=_('Domain(s) to delete (name or ID)'), ) return parser def take_action(self, parsed_args): identity_client = self.app.client_manager.identity - domain = utils.find_resource(identity_client.domains, - parsed_args.domain) - identity_client.domains.delete(domain.id) + result = 0 + for i in parsed_args.domain: + try: + domain = utils.find_resource(identity_client.domains, i) + identity_client.domains.delete(domain.id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete domain with name or " + "ID '%(domain)s': %(e)s") + % {'domain': i, 'e': e}) + + if result > 0: + total = len(parsed_args.domain) + msg = (_("%(result)s of %(total)s domains failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class ListDomain(command.Lister): @@ -168,9 +182,6 @@ class SetDomain(command.Command): if parsed_args.disable: kwargs['enabled'] = False - if not kwargs: - sys.stdout.write(_("Domain not updated, no arguments present\n")) - return identity_client.domains.update(domain.id, **kwargs) diff --git a/openstackclient/identity/v3/ec2creds.py b/openstackclient/identity/v3/ec2creds.py index 835fe96f..7ad01719 100644 --- a/openstackclient/identity/v3/ec2creds.py +++ b/openstackclient/identity/v3/ec2creds.py @@ -12,7 +12,10 @@ """Identity v3 EC2 Credentials action implementations""" +import logging + from osc_lib.command import command +from osc_lib import exceptions from osc_lib import utils import six @@ -20,6 +23,9 @@ from openstackclient.i18n import _ from openstackclient.identity import common +LOG = logging.getLogger(__name__) + + def _determine_ec2_user(parsed_args, client_manager): """Determine a user several different ways. @@ -113,7 +119,8 @@ class DeleteEC2Creds(command.Command): parser.add_argument( 'access_key', metavar='<access-key>', - help=_('Credentials access key'), + nargs='+', + help=_('Credentials access key(s)'), ) parser.add_argument( '--user', @@ -126,7 +133,21 @@ class DeleteEC2Creds(command.Command): def take_action(self, parsed_args): client_manager = self.app.client_manager user = _determine_ec2_user(parsed_args, client_manager) - client_manager.identity.ec2.delete(user, parsed_args.access_key) + result = 0 + for i in parsed_args.access_key: + try: + client_manager.identity.ec2.delete(user, i) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete EC2 credentials with " + "access key '%(access_key)s': %(e)s") + % {'access_key': i, 'e': e}) + + if result > 0: + total = len(parsed_args.access_key) + msg = (_("%(result)s of %(total)s EC2 keys failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class ListEC2Creds(command.Lister): diff --git a/openstackclient/identity/v3/endpoint.py b/openstackclient/identity/v3/endpoint.py index 2f1cc9f3..73b37a43 100644 --- a/openstackclient/identity/v3/endpoint.py +++ b/openstackclient/identity/v3/endpoint.py @@ -15,9 +15,10 @@ """Identity v3 Endpoint action implementations""" -import sys +import logging from osc_lib.command import command +from osc_lib import exceptions from osc_lib import utils import six @@ -25,6 +26,9 @@ from openstackclient.i18n import _ from openstackclient.identity import common +LOG = logging.getLogger(__name__) + + def get_service_name(service): if hasattr(service, 'name'): return service.name @@ -95,22 +99,37 @@ class CreateEndpoint(command.ShowOne): class DeleteEndpoint(command.Command): - """Delete endpoint""" + """Delete endpoint(s)""" def get_parser(self, prog_name): parser = super(DeleteEndpoint, self).get_parser(prog_name) parser.add_argument( 'endpoint', metavar='<endpoint-id>', - help=_('Endpoint to delete (ID only)'), + nargs='+', + help=_('Endpoint(s) to delete (ID only)'), ) return parser def take_action(self, parsed_args): identity_client = self.app.client_manager.identity - endpoint_id = utils.find_resource(identity_client.endpoints, - parsed_args.endpoint).id - identity_client.endpoints.delete(endpoint_id) + result = 0 + for i in parsed_args.endpoint: + try: + endpoint_id = utils.find_resource( + identity_client.endpoints, i).id + identity_client.endpoints.delete(endpoint_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete endpoint with " + "ID '%(endpoint)s': %(e)s") + % {'endpoint': i, 'e': e}) + + if result > 0: + total = len(parsed_args.endpoint) + msg = (_("%(result)s of %(total)s endpoints failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class ListEndpoint(command.Lister): @@ -212,12 +231,6 @@ class SetEndpoint(command.Command): endpoint = utils.find_resource(identity_client.endpoints, parsed_args.endpoint) - 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\n")) - return - service_id = None if parsed_args.service: service = common.find_service(identity_client, parsed_args.service) diff --git a/openstackclient/identity/v3/federation_protocol.py b/openstackclient/identity/v3/federation_protocol.py index 09480245..3fde9027 100644 --- a/openstackclient/identity/v3/federation_protocol.py +++ b/openstackclient/identity/v3/federation_protocol.py @@ -17,6 +17,7 @@ import logging from osc_lib.command import command +from osc_lib import exceptions from osc_lib import utils import six @@ -71,14 +72,15 @@ class CreateProtocol(command.ShowOne): class DeleteProtocol(command.Command): - """Delete federation protocol""" + """Delete federation protocol(s)""" def get_parser(self, prog_name): parser = super(DeleteProtocol, self).get_parser(prog_name) parser.add_argument( 'federation_protocol', metavar='<federation-protocol>', - help=_('Federation protocol to delete (name or ID)'), + nargs='+', + help=_('Federation protocol(s) to delete (name or ID)'), ) parser.add_argument( '--identity-provider', @@ -92,8 +94,22 @@ class DeleteProtocol(command.Command): def take_action(self, parsed_args): identity_client = self.app.client_manager.identity - identity_client.federation.protocols.delete( - parsed_args.identity_provider, parsed_args.federation_protocol) + result = 0 + for i in parsed_args.federation_protocol: + try: + identity_client.federation.protocols.delete( + parsed_args.identity_provider, i) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete federation protocol " + "with name or ID '%(protocol)s': %(e)s") + % {'protocol': i, 'e': e}) + + if result > 0: + total = len(parsed_args.federation_protocol) + msg = (_("%(result)s of %(total)s federation protocols failed" + " to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class ListProtocols(command.Lister): @@ -149,10 +165,6 @@ class SetProtocol(command.Command): def take_action(self, parsed_args): identity_client = self.app.client_manager.identity - if not parsed_args.mapping: - LOG.error(_("No changes requested")) - return - protocol = identity_client.federation.protocols.update( parsed_args.identity_provider, parsed_args.federation_protocol, parsed_args.mapping) diff --git a/openstackclient/identity/v3/group.py b/openstackclient/identity/v3/group.py index c79a64e7..f780810a 100755 --- a/openstackclient/identity/v3/group.py +++ b/openstackclient/identity/v3/group.py @@ -343,9 +343,6 @@ class SetGroup(command.Command): if parsed_args.description: kwargs['description'] = parsed_args.description - if not len(kwargs): - sys.stderr.write("Group not updated, no arguments present\n") - return identity_client.groups.update(group.id, **kwargs) diff --git a/openstackclient/identity/v3/identity_provider.py b/openstackclient/identity/v3/identity_provider.py index 5c638f9b..0453e888 100644 --- a/openstackclient/identity/v3/identity_provider.py +++ b/openstackclient/identity/v3/identity_provider.py @@ -16,6 +16,7 @@ import logging from osc_lib.command import command +from osc_lib import exceptions from osc_lib import utils import six @@ -93,21 +94,35 @@ class CreateIdentityProvider(command.ShowOne): class DeleteIdentityProvider(command.Command): - """Delete identity provider""" + """Delete identity provider(s)""" def get_parser(self, prog_name): parser = super(DeleteIdentityProvider, self).get_parser(prog_name) parser.add_argument( 'identity_provider', metavar='<identity-provider>', - help=_('Identity provider to delete'), + nargs='+', + help=_('Identity provider(s) to delete'), ) return parser def take_action(self, parsed_args): identity_client = self.app.client_manager.identity - identity_client.federation.identity_providers.delete( - parsed_args.identity_provider) + result = 0 + for i in parsed_args.identity_provider: + try: + identity_client.federation.identity_providers.delete(i) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete identity providers with " + "name or ID '%(provider)s': %(e)s") + % {'provider': i, 'e': e}) + + if result > 0: + total = len(parsed_args.identity_provider) + msg = (_("%(result)s of %(total)s identity providers failed" + " to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class ListIdentityProvider(command.Lister): @@ -169,14 +184,6 @@ class SetIdentityProvider(command.Command): def take_action(self, parsed_args): federation_client = self.app.client_manager.identity.federation - # Basic argument checking - if (not parsed_args.enable and not parsed_args.disable and - not parsed_args.remote_id and - not parsed_args.remote_id_file and - not parsed_args.description): - LOG.error(_('No changes requested')) - return (None, None) - # Always set remote_ids if either is passed in if parsed_args.remote_id_file: file_content = utils.read_blob_file_contents( diff --git a/openstackclient/identity/v3/mapping.py b/openstackclient/identity/v3/mapping.py index 74ead228..09181a0b 100644 --- a/openstackclient/identity/v3/mapping.py +++ b/openstackclient/identity/v3/mapping.py @@ -111,21 +111,35 @@ class CreateMapping(command.ShowOne, _RulesReader): class DeleteMapping(command.Command): - """Delete mapping""" + """Delete mapping(s)""" def get_parser(self, prog_name): parser = super(DeleteMapping, self).get_parser(prog_name) parser.add_argument( 'mapping', metavar='<mapping>', - help=_('Mapping to delete'), + nargs='+', + help=_('Mapping(s) to delete'), ) return parser def take_action(self, parsed_args): identity_client = self.app.client_manager.identity - - identity_client.federation.mappings.delete(parsed_args.mapping) + result = 0 + for i in parsed_args.mapping: + try: + identity_client.federation.mappings.delete(i) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete mapping with name or " + "ID '%(mapping)s': %(e)s") + % {'mapping': i, 'e': e}) + + if result > 0: + total = len(parsed_args.mapping) + msg = (_("%(result)s of %(total)s mappings failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class ListMapping(command.Lister): @@ -162,10 +176,6 @@ class SetMapping(command.Command, _RulesReader): def take_action(self, parsed_args): identity_client = self.app.client_manager.identity - if not parsed_args.rules: - LOG.error(_("No changes requested")) - return - rules = self._read_rules(parsed_args.rules) mapping = identity_client.federation.mappings.update( diff --git a/openstackclient/identity/v3/policy.py b/openstackclient/identity/v3/policy.py index 68fb2738..596eae01 100644 --- a/openstackclient/identity/v3/policy.py +++ b/openstackclient/identity/v3/policy.py @@ -15,15 +15,19 @@ """Identity v3 Policy action implementations""" -import sys +import logging from osc_lib.command import command +from osc_lib import exceptions from osc_lib import utils import six from openstackclient.i18n import _ +LOG = logging.getLogger(__name__) + + class CreatePolicy(command.ShowOne): """Create new policy""" @@ -57,20 +61,35 @@ class CreatePolicy(command.ShowOne): class DeletePolicy(command.Command): - """Delete policy""" + """Delete policy(s)""" def get_parser(self, prog_name): parser = super(DeletePolicy, self).get_parser(prog_name) parser.add_argument( 'policy', metavar='<policy>', - help=_('Policy to delete'), + nargs='+', + help=_('Policy(s) to delete'), ) return parser def take_action(self, parsed_args): identity_client = self.app.client_manager.identity - identity_client.policies.delete(parsed_args.policy) + result = 0 + for i in parsed_args.policy: + try: + identity_client.policies.delete(i) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete policy with name or " + "ID '%(policy)s': %(e)s") + % {'policy': i, 'e': e}) + + if result > 0: + total = len(parsed_args.policy) + msg = (_("%(result)s of %(total)s policys failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class ListPolicy(command.Lister): @@ -136,9 +155,6 @@ class SetPolicy(command.Command): if parsed_args.type: kwargs['type'] = parsed_args.type - if not kwargs: - sys.stdout.write(_('Policy not updated, no arguments present\n')) - return identity_client.policies.update(parsed_args.policy, **kwargs) diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py index 53987017..56c4fbc8 100755 --- a/openstackclient/identity/v3/project.py +++ b/openstackclient/identity/v3/project.py @@ -263,13 +263,6 @@ class SetProject(command.Command): def take_action(self, parsed_args): identity_client = self.app.client_manager.identity - if (not parsed_args.name - and not parsed_args.domain - and not parsed_args.description - and not parsed_args.enable - and not parsed_args.property - and not parsed_args.disable): - return project = common.find_project(identity_client, parsed_args.project, parsed_args.domain) diff --git a/openstackclient/identity/v3/region.py b/openstackclient/identity/v3/region.py index b5e46a9a..b7c51f93 100644 --- a/openstackclient/identity/v3/region.py +++ b/openstackclient/identity/v3/region.py @@ -13,13 +13,19 @@ """Identity v3 Region action implementations""" +import logging + from osc_lib.command import command +from osc_lib import exceptions from osc_lib import utils import six from openstackclient.i18n import _ +LOG = logging.getLogger(__name__) + + class CreateRegion(command.ShowOne): """Create new region""" @@ -60,21 +66,35 @@ class CreateRegion(command.ShowOne): class DeleteRegion(command.Command): - """Delete region""" + """Delete region(s)""" def get_parser(self, prog_name): parser = super(DeleteRegion, self).get_parser(prog_name) parser.add_argument( 'region', metavar='<region-id>', - help=_('Region ID to delete'), + nargs='+', + help=_('Region ID(s) to delete'), ) return parser def take_action(self, parsed_args): identity_client = self.app.client_manager.identity - - identity_client.regions.delete(parsed_args.region) + result = 0 + for i in parsed_args.region: + try: + identity_client.regions.delete(i) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete region with " + "ID '%(region)s': %(e)s") + % {'region': i, 'e': e}) + + if result > 0: + total = len(parsed_args.region) + msg = (_("%(result)s of %(total)s regions failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class ListRegion(command.Lister): @@ -132,8 +152,6 @@ class SetRegion(command.Command): def take_action(self, parsed_args): identity_client = self.app.client_manager.identity - if not parsed_args.parent_region and not parsed_args.description: - return kwargs = {} if parsed_args.description: kwargs['description'] = parsed_args.description diff --git a/openstackclient/identity/v3/role.py b/openstackclient/identity/v3/role.py index d3c530a5..27380179 100755 --- a/openstackclient/identity/v3/role.py +++ b/openstackclient/identity/v3/role.py @@ -357,10 +357,6 @@ class SetRole(command.Command): def take_action(self, parsed_args): 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")) - return role = utils.find_resource( identity_client.roles, parsed_args.role, diff --git a/openstackclient/identity/v3/service.py b/openstackclient/identity/v3/service.py index 195b2701..97e64dc6 100644 --- a/openstackclient/identity/v3/service.py +++ b/openstackclient/identity/v3/service.py @@ -15,9 +15,10 @@ """Identity v3 Service action implementations""" -import sys +import logging from osc_lib.command import command +from osc_lib import exceptions from osc_lib import utils import six @@ -25,6 +26,9 @@ from openstackclient.i18n import _ from openstackclient.identity import common +LOG = logging.getLogger(__name__) + + class CreateService(command.ShowOne): """Create new service""" @@ -77,23 +81,36 @@ class CreateService(command.ShowOne): class DeleteService(command.Command): - """Delete service""" + """Delete service(s)""" def get_parser(self, prog_name): parser = super(DeleteService, self).get_parser(prog_name) parser.add_argument( 'service', metavar='<service>', - help=_('Service to delete (type, name or ID)'), + nargs='+', + help=_('Service(s) to delete (type, name or ID)'), ) return parser def take_action(self, parsed_args): identity_client = self.app.client_manager.identity - - service = common.find_service(identity_client, parsed_args.service) - - identity_client.services.delete(service.id) + result = 0 + for i in parsed_args.service: + try: + service = common.find_service(identity_client, i) + identity_client.services.delete(service.id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete consumer with type, " + "name or ID '%(service)s': %(e)s") + % {'service': i, 'e': e}) + + if result > 0: + total = len(parsed_args.service) + msg = (_("%(result)s of %(total)s services failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class ListService(command.Lister): @@ -163,14 +180,6 @@ class SetService(command.Command): def take_action(self, parsed_args): identity_client = self.app.client_manager.identity - if (not parsed_args.name - and not parsed_args.type - 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")) - return service = common.find_service(identity_client, parsed_args.service) kwargs = {} diff --git a/openstackclient/identity/v3/service_provider.py b/openstackclient/identity/v3/service_provider.py index 0beea813..440eba40 100644 --- a/openstackclient/identity/v3/service_provider.py +++ b/openstackclient/identity/v3/service_provider.py @@ -13,15 +13,19 @@ """Service Provider action implementations""" -import sys +import logging from osc_lib.command import command +from osc_lib import exceptions from osc_lib import utils import six from openstackclient.i18n import _ +LOG = logging.getLogger(__name__) + + class CreateServiceProvider(command.ShowOne): """Create new service provider""" @@ -83,21 +87,35 @@ class CreateServiceProvider(command.ShowOne): class DeleteServiceProvider(command.Command): - """Delete service provider""" + """Delete service provider(s)""" def get_parser(self, prog_name): parser = super(DeleteServiceProvider, self).get_parser(prog_name) parser.add_argument( 'service_provider', metavar='<service-provider>', - help=_('Service provider to delete'), + nargs='+', + help=_('Service provider(s) to delete'), ) return parser def take_action(self, parsed_args): service_client = self.app.client_manager.identity - service_client.federation.service_providers.delete( - parsed_args.service_provider) + result = 0 + for i in parsed_args.service_provider: + try: + service_client.federation.service_providers.delete(i) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete service provider with " + "name or ID '%(provider)s': %(e)s") + % {'provider': i, 'e': e}) + + if result > 0: + total = len(parsed_args.service_provider) + msg = (_("%(result)s of %(total)s service providers failed" + " to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class ListServiceProvider(command.Lister): @@ -164,13 +182,6 @@ class SetServiceProvider(command.Command): elif parsed_args.disable is True: enabled = False - 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\n")) - return (None, None) - service_provider = federation_client.service_providers.update( parsed_args.service_provider, enabled=enabled, description=parsed_args.description, diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index 3e189ac1..dc47ef8d 100755 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -17,7 +17,6 @@ import copy import logging -import sys from keystoneauth1 import exceptions as ks_exc from osc_lib.command import command @@ -330,18 +329,6 @@ class SetUser(command.Command): if parsed_args.password_prompt: parsed_args.password = utils.get_password(self.app.stdin) - if (not parsed_args.name - and not parsed_args.name - and not parsed_args.password - and not parsed_args.email - and not parsed_args.project - and not parsed_args.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")) - return - user = utils.find_resource( identity_client.users, parsed_args.user, diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py index e7b60e5a..5f669c64 100644 --- a/openstackclient/image/v1/image.py +++ b/openstackclient/image/v1/image.py @@ -378,6 +378,7 @@ class ListImage(command.Lister): 'Disk Format', 'Container Format', 'Size', + 'Checksum', 'Status', 'is_public', 'protected', @@ -390,6 +391,7 @@ class ListImage(command.Lister): 'Disk Format', 'Container Format', 'Size', + 'Checksum', 'Status', 'Visibility', 'Protected', diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index 64092fea..0712e09c 100755 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -490,6 +490,7 @@ class ListImage(command.Lister): 'Disk Format', 'Container Format', 'Size', + 'Checksum', 'Status', 'visibility', 'protected', @@ -502,6 +503,7 @@ class ListImage(command.Lister): 'Disk Format', 'Container Format', 'Size', + 'Checksum', 'Status', 'Visibility', 'Protected', diff --git a/openstackclient/network/common.py b/openstackclient/network/common.py index f62840fc..2b1a5656 100644 --- a/openstackclient/network/common.py +++ b/openstackclient/network/common.py @@ -111,7 +111,7 @@ class NetworkAndComputeDelete(NetworkAndComputeCommand): if ret: total = len(resources) - msg = _("%(num)s of %(total)s %(resource)s failed to delete.") % { + msg = _("%(num)s of %(total)s %(resource)ss failed to delete.") % { "num": ret, "total": total, "resource": self.resource, diff --git a/openstackclient/tests/compute/v2/test_agent.py b/openstackclient/tests/compute/v2/test_agent.py index da329728..7695ee41 100644 --- a/openstackclient/tests/compute/v2/test_agent.py +++ b/openstackclient/tests/compute/v2/test_agent.py @@ -25,7 +25,9 @@ from openstackclient.tests import utils as tests_utils class TestAgent(compute_fakes.TestComputev2): - fake_agent = compute_fakes.FakeAgent.create_one_agent() + attr = {} + attr['agent_id'] = 1 + fake_agent = compute_fakes.FakeAgent.create_one_agent(attr) columns = ( 'agent_id', @@ -238,21 +240,34 @@ class TestAgentSet(TestAgent): super(TestAgentSet, self).setUp() self.agents_mock.update.return_value = self.fake_agent + self.agents_mock.list.return_value = [self.fake_agent] self.cmd = agent.SetAgent(self.app, None) - def test_agent_set(self): + def test_agent_set_nothing(self): arglist = [ - 'id', - 'new-version', - 'new-url', - 'new-md5hash', + '1', + ] + verifylist = [ + ('id', '1'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.agents_mock.update.assert_called_with(parsed_args.id, + self.fake_agent.version, + self.fake_agent.url, + self.fake_agent.md5hash) + self.assertIsNone(result) + + def test_agent_set_version(self): + arglist = [ + '1', + '--agent-version', 'new-version', ] verifylist = [ - ('id', 'id'), + ('id', '1'), ('version', 'new-version'), - ('url', 'new-url'), - ('md5hash', 'new-md5hash'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -260,6 +275,46 @@ class TestAgentSet(TestAgent): self.agents_mock.update.assert_called_with(parsed_args.id, parsed_args.version, + self.fake_agent.url, + self.fake_agent.md5hash) + self.assertIsNone(result) + + def test_agent_set_url(self): + arglist = [ + '1', + '--url', 'new-url', + ] + + verifylist = [ + ('id', '1'), + ('url', 'new-url'), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.agents_mock.update.assert_called_with(parsed_args.id, + self.fake_agent.version, parsed_args.url, + self.fake_agent.md5hash) + self.assertIsNone(result) + + def test_agent_set_md5hash(self): + arglist = [ + '1', + '--md5hash', 'new-md5hash', + ] + + verifylist = [ + ('id', '1'), + ('md5hash', 'new-md5hash'), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.agents_mock.update.assert_called_with(parsed_args.id, + self.fake_agent.version, + self.fake_agent.url, parsed_args.md5hash) self.assertIsNone(result) diff --git a/openstackclient/tests/compute/v2/test_server.py b/openstackclient/tests/compute/v2/test_server.py index 24f92e46..1c5a5fe4 100644 --- a/openstackclient/tests/compute/v2/test_server.py +++ b/openstackclient/tests/compute/v2/test_server.py @@ -43,6 +43,11 @@ class TestServer(compute_fakes.TestComputev2): self.flavors_mock = self.app.client_manager.compute.flavors self.flavors_mock.reset_mock() + # Get a shortcut to the compute client SecurityGroupManager Mock + self.security_groups_mock = \ + self.app.client_manager.compute.security_groups + self.security_groups_mock.reset_mock() + # Get a shortcut to the image client ImageManager Mock self.images_mock = self.app.client_manager.image.images self.images_mock.reset_mock() @@ -981,6 +986,54 @@ class TestServerRemoveFloatingIP(TestServer): self.assertIsNone(result) +class TestServerRemoveSecurityGroup(TestServer): + + def setUp(self): + super(TestServerRemoveSecurityGroup, self).setUp() + + self.security_group = \ + compute_fakes.FakeSecurityGroup.create_one_security_group() + # This is the return value for utils.find_resource() for security group + self.security_groups_mock.get.return_value = self.security_group + + attrs = { + 'security_groups': [{'name': self.security_group.id}] + } + methods = { + 'remove_security_group': None, + } + + self.server = compute_fakes.FakeServer.create_one_server( + attrs=attrs, + methods=methods + ) + # This is the return value for utils.find_resource() for server + self.servers_mock.get.return_value = self.server + + # Get the command object to test + self.cmd = server.RemoveServerSecurityGroup(self.app, None) + + def test_server_remove_security_group(self): + arglist = [ + self.server.id, + self.security_group.id + ] + verifylist = [ + ('server', self.server.id), + ('group', self.security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.security_groups_mock.get.assert_called_with( + self.security_group.id, + ) + self.servers_mock.get.assert_called_with(self.server.id) + self.server.remove_security_group.assert_called_with( + self.security_group.id, + ) + self.assertIsNone(result) + + class TestServerResize(TestServer): def setUp(self): diff --git a/openstackclient/tests/identity/v3/test_consumer.py b/openstackclient/tests/identity/v3/test_consumer.py index 4a8cf087..d90c7347 100644 --- a/openstackclient/tests/identity/v3/test_consumer.py +++ b/openstackclient/tests/identity/v3/test_consumer.py @@ -83,7 +83,7 @@ class TestConsumerDelete(TestOAuth1): identity_fakes.consumer_id, ] verifylist = [ - ('consumer', identity_fakes.consumer_id), + ('consumer', [identity_fakes.consumer_id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/openstackclient/tests/identity/v3/test_domain.py b/openstackclient/tests/identity/v3/test_domain.py index 9229ddd0..5e094021 100644 --- a/openstackclient/tests/identity/v3/test_domain.py +++ b/openstackclient/tests/identity/v3/test_domain.py @@ -182,7 +182,7 @@ class TestDomainDelete(TestDomain): self.domain.id, ] verifylist = [ - ('domain', self.domain.id), + ('domain', [self.domain.id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -253,7 +253,11 @@ class TestDomainSet(TestDomain): result = self.cmd.take_action(parsed_args) - self.assertNotCalled(self.domains_mock.update) + kwargs = {} + self.domains_mock.update.assert_called_with( + self.domain.id, + **kwargs + ) self.assertIsNone(result) def test_domain_set_name(self): diff --git a/openstackclient/tests/identity/v3/test_endpoint.py b/openstackclient/tests/identity/v3/test_endpoint.py index d953459c..04276319 100644 --- a/openstackclient/tests/identity/v3/test_endpoint.py +++ b/openstackclient/tests/identity/v3/test_endpoint.py @@ -273,7 +273,7 @@ class TestEndpointDelete(TestEndpoint): identity_fakes.endpoint_id, ] verifylist = [ - ('endpoint', identity_fakes.endpoint_id), + ('endpoint', [identity_fakes.endpoint_id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -485,7 +485,17 @@ class TestEndpointSet(TestEndpoint): result = self.cmd.take_action(parsed_args) - self.assertNotCalled(self.endpoints_mock.update) + kwargs = { + 'enabled': None, + 'interface': None, + 'region': None, + 'service': None, + 'url': None, + } + self.endpoints_mock.update.assert_called_with( + identity_fakes.endpoint_id, + **kwargs + ) self.assertIsNone(result) def test_endpoint_set_interface(self): diff --git a/openstackclient/tests/identity/v3/test_identity_provider.py b/openstackclient/tests/identity/v3/test_identity_provider.py index 161a1a1b..1ec61052 100644 --- a/openstackclient/tests/identity/v3/test_identity_provider.py +++ b/openstackclient/tests/identity/v3/test_identity_provider.py @@ -255,7 +255,7 @@ class TestIdentityProviderDelete(TestIdentityProvider): identity_fakes.idp_id, ] verifylist = [ - ('identity_provider', identity_fakes.idp_id), + ('identity_provider', [identity_fakes.idp_id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -585,8 +585,8 @@ class TestIdentityProviderSet(TestIdentityProvider): # expect take_action() to return (None, None) as # neither --enable nor --disable was specified - self.assertIsNone(columns) - self.assertIsNone(data) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) class TestIdentityProviderShow(TestIdentityProvider): diff --git a/openstackclient/tests/identity/v3/test_mappings.py b/openstackclient/tests/identity/v3/test_mappings.py index af7b135d..6aa1a6e5 100644 --- a/openstackclient/tests/identity/v3/test_mappings.py +++ b/openstackclient/tests/identity/v3/test_mappings.py @@ -91,7 +91,7 @@ class TestMappingDelete(TestMapping): identity_fakes.mapping_id ] verifylist = [ - ('mapping', identity_fakes.mapping_id) + ('mapping', [identity_fakes.mapping_id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/openstackclient/tests/identity/v3/test_protocol.py b/openstackclient/tests/identity/v3/test_protocol.py index 238b0ff8..f718b27b 100644 --- a/openstackclient/tests/identity/v3/test_protocol.py +++ b/openstackclient/tests/identity/v3/test_protocol.py @@ -88,7 +88,7 @@ class TestProtocolDelete(TestProtocol): identity_fakes.protocol_id ] verifylist = [ - ('federation_protocol', identity_fakes.protocol_id), + ('federation_protocol', [identity_fakes.protocol_id]), ('identity_provider', identity_fakes.idp_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/openstackclient/tests/identity/v3/test_region.py b/openstackclient/tests/identity/v3/test_region.py index 02dec568..41ee5ce9 100644 --- a/openstackclient/tests/identity/v3/test_region.py +++ b/openstackclient/tests/identity/v3/test_region.py @@ -153,7 +153,7 @@ class TestRegionDelete(TestRegion): identity_fakes.region_id, ] verifylist = [ - ('region', identity_fakes.region_id), + ('region', [identity_fakes.region_id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -253,7 +253,11 @@ class TestRegionSet(TestRegion): result = self.cmd.take_action(parsed_args) - self.assertNotCalled(self.regions_mock.update) + kwargs = {} + self.regions_mock.update.assert_called_with( + identity_fakes.region_id, + **kwargs + ) self.assertIsNone(result) def test_region_set_description(self): diff --git a/openstackclient/tests/identity/v3/test_service.py b/openstackclient/tests/identity/v3/test_service.py index 1e70383f..a1f85adc 100644 --- a/openstackclient/tests/identity/v3/test_service.py +++ b/openstackclient/tests/identity/v3/test_service.py @@ -200,7 +200,7 @@ class TestServiceDelete(TestService): identity_fakes.service_name, ] verifylist = [ - ('service', identity_fakes.service_name), + ('service', [identity_fakes.service_name]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/openstackclient/tests/identity/v3/test_service_provider.py b/openstackclient/tests/identity/v3/test_service_provider.py index 1d82cd15..f5270d83 100644 --- a/openstackclient/tests/identity/v3/test_service_provider.py +++ b/openstackclient/tests/identity/v3/test_service_provider.py @@ -185,7 +185,7 @@ class TestServiceProviderDelete(TestServiceProvider): service_fakes.sp_id, ] verifylist = [ - ('service_provider', service_fakes.sp_id), + ('service_provider', [service_fakes.sp_id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -377,8 +377,15 @@ class TestServiceProviderSet(TestServiceProvider): # expect take_action() to return (None, None) as none of --disabled, # --enabled, --description, --service-provider-url, --auth_url option # was set. - self.assertIsNone(columns) - self.assertIsNone(data) + self.assertEqual(self.columns, columns) + datalist = ( + service_fakes.sp_auth_url, + service_fakes.sp_description, + True, + service_fakes.sp_id, + service_fakes.service_provider_url + ) + self.assertEqual(datalist, data) class TestServiceProviderShow(TestServiceProvider): diff --git a/openstackclient/tests/image/v1/test_image.py b/openstackclient/tests/image/v1/test_image.py index 14aa331f..cf08d138 100644 --- a/openstackclient/tests/image/v1/test_image.py +++ b/openstackclient/tests/image/v1/test_image.py @@ -363,6 +363,7 @@ class TestImageList(TestImage): 'Disk Format', 'Container Format', 'Size', + 'Checksum', 'Status', 'Visibility', 'Protected', @@ -378,6 +379,7 @@ class TestImageList(TestImage): '', '', '', + '', 'public', False, image_fakes.image_owner, diff --git a/openstackclient/tests/image/v2/fakes.py b/openstackclient/tests/image/v2/fakes.py index c2a8d721..d450dec1 100644 --- a/openstackclient/tests/image/v2/fakes.py +++ b/openstackclient/tests/image/v2/fakes.py @@ -49,13 +49,6 @@ IMAGE_SHOW = copy.copy(IMAGE) IMAGE_SHOW['tags'] = '' IMAGE_SHOW_data = tuple((IMAGE_SHOW[x] for x in sorted(IMAGE_SHOW))) -member_status = 'pending' -MEMBER = { - 'member_id': identity_fakes.project_id, - 'image_id': image_id, - 'status': member_status, -} - # Just enough v2 schema to do some testing IMAGE_schema = { "additionalProperties": { @@ -288,3 +281,29 @@ class FakeImage(object): else: data_list.append(getattr(image, x)) return tuple(data_list) + + @staticmethod + def create_one_image_member(attrs=None): + """Create a fake image member. + + :param Dictionary attrs: + A dictionary with all attrbutes of image member + :return: + A FakeResource object with member_id, image_id and so on + """ + attrs = attrs or {} + + # Set default attribute + image_member_info = { + 'member_id': 'member-id-' + uuid.uuid4().hex, + 'image_id': 'image-id-' + uuid.uuid4().hex, + 'status': 'pending', + } + + # Overwrite default attributes if there are some attributes set + image_member_info.update(attrs) + + image_member = fakes.FakeModel( + copy.deepcopy(image_member_info)) + + return image_member diff --git a/openstackclient/tests/image/v2/test_image.py b/openstackclient/tests/image/v2/test_image.py index 592def21..c6b83bc5 100644 --- a/openstackclient/tests/image/v2/test_image.py +++ b/openstackclient/tests/image/v2/test_image.py @@ -347,6 +347,10 @@ class TestImageCreate(TestImage): class TestAddProjectToImage(TestImage): _image = image_fakes.FakeImage.create_one_image() + new_member = image_fakes.FakeImage.create_one_image_member( + attrs={'image_id': _image.id, + 'member_id': identity_fakes.project_id} + ) columns = ( 'image_id', @@ -357,7 +361,7 @@ class TestAddProjectToImage(TestImage): datalist = ( _image.id, identity_fakes.project_id, - image_fakes.member_status + new_member.status ) def setUp(self): @@ -367,11 +371,7 @@ class TestAddProjectToImage(TestImage): self.images_mock.get.return_value = self._image # Update the image_id in the MEMBER dict - self.new_member = copy.deepcopy(image_fakes.MEMBER) - self.new_member['image_id'] = self._image.id - self.image_members_mock.create.return_value = fakes.FakeModel( - self.new_member, - ) + self.image_members_mock.create.return_value = self.new_member self.project_mock.get.return_value = fakes.FakeResource( None, copy.deepcopy(identity_fakes.PROJECT), @@ -643,6 +643,7 @@ class TestImageList(TestImage): 'Disk Format', 'Container Format', 'Size', + 'Checksum', 'Status', 'Visibility', 'Protected', @@ -658,6 +659,7 @@ class TestImageList(TestImage): '', '', '', + '', self._image.visibility, self._image.protected, self._image.owner, diff --git a/openstackclient/tests/network/v2/test_address_scope.py b/openstackclient/tests/network/v2/test_address_scope.py index 722371f9..16e74f46 100644 --- a/openstackclient/tests/network/v2/test_address_scope.py +++ b/openstackclient/tests/network/v2/test_address_scope.py @@ -11,7 +11,6 @@ # under the License. # -import copy import mock from mock import call @@ -35,11 +34,13 @@ class TestAddressScope(network_fakes.TestNetworkV2): class TestCreateAddressScope(TestAddressScope): + project = identity_fakes_v3.FakeProject.create_one_project() + domain = identity_fakes_v3.FakeDomain.create_one_domain() # The new address scope created. new_address_scope = ( network_fakes.FakeAddressScope.create_one_address_scope( attrs={ - 'tenant_id': identity_fakes_v3.project_id, + 'tenant_id': project.id, } )) columns = ( @@ -75,19 +76,11 @@ class TestCreateAddressScope(TestAddressScope): # 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_v3.PROJECT), - loaded=True, - ) + self.projects_mock.get.return_value = self.project # Get a shortcut to the DomainManager Mock self.domains_mock = self.identity.domains - self.domains_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes_v3.DOMAIN), - loaded=True, - ) + self.domains_mock.get.return_value = self.domain def test_create_no_options(self): arglist = [] @@ -121,15 +114,15 @@ class TestCreateAddressScope(TestAddressScope): arglist = [ '--ip-version', str(self.new_address_scope.ip_version), '--share', - '--project', identity_fakes_v3.project_name, - '--project-domain', identity_fakes_v3.domain_name, + '--project', self.project.name, + '--project-domain', self.domain.name, self.new_address_scope.name, ] verifylist = [ ('ip_version', self.new_address_scope.ip_version), ('share', True), - ('project', identity_fakes_v3.project_name), - ('project_domain', identity_fakes_v3.domain_name), + ('project', self.project.name), + ('project_domain', self.domain.name), ('name', self.new_address_scope.name), ] @@ -139,7 +132,7 @@ class TestCreateAddressScope(TestAddressScope): self.network.create_address_scope.assert_called_once_with(**{ 'ip_version': self.new_address_scope.ip_version, 'shared': True, - 'tenant_id': identity_fakes_v3.project_id, + 'tenant_id': self.project.id, 'name': self.new_address_scope.name, }) self.assertEqual(self.columns, columns) diff --git a/openstackclient/tests/network/v2/test_floating_ip.py b/openstackclient/tests/network/v2/test_floating_ip.py index 5cd5279a..234fe446 100644 --- a/openstackclient/tests/network/v2/test_floating_ip.py +++ b/openstackclient/tests/network/v2/test_floating_ip.py @@ -211,7 +211,7 @@ class TestDeleteFloatingIPNetwork(TestFloatingIPNetwork): 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.assertEqual('1 of 2 floating_ips failed to delete.', str(e)) self.network.find_ip.assert_any_call( self.floating_ips[0].id, ignore_missing=False) @@ -462,7 +462,7 @@ class TestDeleteFloatingIPCompute(TestFloatingIPCompute): 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.assertEqual('1 of 2 floating_ips failed to delete.', str(e)) self.compute.floating_ips.get.assert_any_call( self.floating_ips[0].id) diff --git a/openstackclient/tests/network/v2/test_ip_availability.py b/openstackclient/tests/network/v2/test_ip_availability.py index c6ec2b0b..21d44d07 100644 --- a/openstackclient/tests/network/v2/test_ip_availability.py +++ b/openstackclient/tests/network/v2/test_ip_availability.py @@ -11,7 +11,6 @@ # under the License. # -import copy import mock from osc_lib import utils as common_utils @@ -41,11 +40,8 @@ class TestIPAvailability(network_fakes.TestNetworkV2): # 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, - ) + self.project = identity_fakes.FakeProject.create_one_project() + self.projects_mock.get.return_value = self.project class TestListIPAvailability(TestIPAvailability): @@ -109,16 +105,16 @@ class TestListIPAvailability(TestIPAvailability): def test_list_project(self): arglist = [ - '--project', identity_fakes.project_name + '--project', self.project.name ] verifylist = [ - ('project', identity_fakes.project_name) + ('project', self.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, + filters = {'tenant_id': self.project.id, 'ip_version': 4} self.network.network_ip_availabilities.assert_called_once_with( diff --git a/openstackclient/tests/network/v2/test_network.py b/openstackclient/tests/network/v2/test_network.py index 8fc9dadf..4322bf9a 100644 --- a/openstackclient/tests/network/v2/test_network.py +++ b/openstackclient/tests/network/v2/test_network.py @@ -11,7 +11,6 @@ # under the License. # -import copy import mock from mock import call @@ -40,10 +39,12 @@ class TestNetwork(network_fakes.TestNetworkV2): class TestCreateNetworkIdentityV3(TestNetwork): + project = identity_fakes_v3.FakeProject.create_one_project() + domain = identity_fakes_v3.FakeDomain.create_one_domain() # The new network created. _network = network_fakes.FakeNetwork.create_one_network( attrs={ - 'tenant_id': identity_fakes_v3.project_id, + 'tenant_id': project.id, 'availability_zone_hints': ["nova"], } ) @@ -96,19 +97,11 @@ class TestCreateNetworkIdentityV3(TestNetwork): # 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_v3.PROJECT), - loaded=True, - ) + self.projects_mock.get.return_value = self.project # Get a shortcut to the DomainManager Mock self.domains_mock = self.identity.domains - self.domains_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes_v3.DOMAIN), - loaded=True, - ) + self.domains_mock.get.return_value = self.domain def test_create_no_options(self): arglist = [] @@ -143,8 +136,8 @@ class TestCreateNetworkIdentityV3(TestNetwork): arglist = [ "--disable", "--share", - "--project", identity_fakes_v3.project_name, - "--project-domain", identity_fakes_v3.domain_name, + "--project", self.project.name, + "--project-domain", self.domain.name, "--availability-zone-hint", "nova", "--external", "--default", "--provider-network-type", "vlan", @@ -156,8 +149,8 @@ class TestCreateNetworkIdentityV3(TestNetwork): verifylist = [ ('disable', True), ('share', True), - ('project', identity_fakes_v3.project_name), - ('project_domain', identity_fakes_v3.domain_name), + ('project', self.project.name), + ('project_domain', self.domain.name), ('availability_zone_hints', ["nova"]), ('external', True), ('default', True), @@ -176,7 +169,7 @@ class TestCreateNetworkIdentityV3(TestNetwork): 'availability_zone_hints': ["nova"], 'name': self._network.name, 'shared': True, - 'tenant_id': identity_fakes_v3.project_id, + 'tenant_id': self.project.id, 'is_default': True, 'router:external': True, 'provider:network_type': 'vlan', @@ -214,9 +207,10 @@ class TestCreateNetworkIdentityV3(TestNetwork): class TestCreateNetworkIdentityV2(TestNetwork): + project = identity_fakes_v2.FakeProject.create_one_project() # The new network created. _network = network_fakes.FakeNetwork.create_one_network( - attrs={'tenant_id': identity_fakes_v2.project_id} + attrs={'tenant_id': project.id} ) columns = ( @@ -267,24 +261,20 @@ class TestCreateNetworkIdentityV2(TestNetwork): # Get a shortcut to the ProjectManager Mock self.projects_mock = self.identity.tenants - self.projects_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes_v2.PROJECT), - loaded=True, - ) + self.projects_mock.get.return_value = self.project # There is no DomainManager Mock in fake identity v2. def test_create_with_project_identityv2(self): arglist = [ - "--project", identity_fakes_v2.project_name, + "--project", self.project.name, self._network.name, ] verifylist = [ ('enable', True), ('share', None), ('name', self._network.name), - ('project', identity_fakes_v2.project_name), + ('project', self.project.name), ('external', False), ] @@ -294,22 +284,22 @@ class TestCreateNetworkIdentityV2(TestNetwork): self.network.create_network.assert_called_once_with(**{ 'admin_state_up': True, 'name': self._network.name, - 'tenant_id': identity_fakes_v2.project_id, + 'tenant_id': self.project.id, }) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) def test_create_with_domain_identityv2(self): arglist = [ - "--project", identity_fakes_v3.project_name, - "--project-domain", identity_fakes_v3.domain_name, + "--project", self.project.name, + "--project-domain", "domain-name", self._network.name, ] verifylist = [ ('enable', True), ('share', None), - ('project', identity_fakes_v3.project_name), - ('project_domain', identity_fakes_v3.domain_name), + ('project', self.project.name), + ('project_domain', "domain-name"), ('name', self._network.name), ('external', False), ] diff --git a/openstackclient/tests/network/v2/test_security_group.py b/openstackclient/tests/network/v2/test_security_group.py index b0c14985..cea64897 100644 --- a/openstackclient/tests/network/v2/test_security_group.py +++ b/openstackclient/tests/network/v2/test_security_group.py @@ -11,7 +11,6 @@ # under the License. # -import copy import mock from mock import call @@ -45,6 +44,8 @@ class TestSecurityGroupCompute(compute_fakes.TestComputev2): class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork): + project = identity_fakes.FakeProject.create_one_project() + domain = identity_fakes.FakeDomain.create_one_domain() # The security group to be created. _security_group = \ network_fakes.FakeSecurityGroup.create_one_security_group() @@ -81,19 +82,11 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork): # 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, - ) + self.projects_mock.get.return_value = self.project # Get a shortcut to the DomainManager Mock self.domains_mock = self.identity.domains - self.domains_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.DOMAIN), - loaded=True, - ) + self.domains_mock.get.return_value = self.domain # Get the command object to test self.cmd = security_group.CreateSecurityGroup(self.app, self.namespace) @@ -123,15 +116,15 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork): def test_create_all_options(self): arglist = [ '--description', self._security_group.description, - '--project', identity_fakes.project_name, - '--project-domain', identity_fakes.domain_name, + '--project', self.project.name, + '--project-domain', self.domain.name, self._security_group.name, ] verifylist = [ ('description', self._security_group.description), ('name', self._security_group.name), - ('project', identity_fakes.project_name), - ('project_domain', identity_fakes.domain_name), + ('project', self.project.name), + ('project_domain', self.domain.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -140,7 +133,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork): self.network.create_security_group.assert_called_once_with(**{ 'description': self._security_group.description, 'name': self._security_group.name, - 'tenant_id': identity_fakes.project_id, + 'tenant_id': self.project.id, }) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) @@ -148,6 +141,8 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork): class TestCreateSecurityGroupCompute(TestSecurityGroupCompute): + project = identity_fakes.FakeProject.create_one_project() + domain = identity_fakes.FakeDomain.create_one_domain() # The security group to be shown. _security_group = \ compute_fakes.FakeSecurityGroup.create_one_security_group() @@ -184,8 +179,8 @@ class TestCreateSecurityGroupCompute(TestSecurityGroupCompute): def test_create_network_options(self): arglist = [ - '--project', identity_fakes.project_name, - '--project-domain', identity_fakes.domain_name, + '--project', self.project.name, + '--project-domain', self.domain.name, self._security_group.name, ] self.assertRaises(tests_utils.ParserException, @@ -301,7 +296,7 @@ class TestDeleteSecurityGroupNetwork(TestSecurityGroupNetwork): 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.assertEqual('1 of 2 groups failed to delete.', str(e)) self.network.find_security_group.assert_any_call( self._security_groups[0].name, ignore_missing=False) @@ -389,7 +384,7 @@ class TestDeleteSecurityGroupCompute(TestSecurityGroupCompute): 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.assertEqual('1 of 2 groups failed to delete.', str(e)) self.compute.security_groups.get.assert_any_call( self._security_groups[0].id) diff --git a/openstackclient/tests/network/v2/test_security_group_rule.py b/openstackclient/tests/network/v2/test_security_group_rule.py index b2862679..34d35629 100644 --- a/openstackclient/tests/network/v2/test_security_group_rule.py +++ b/openstackclient/tests/network/v2/test_security_group_rule.py @@ -46,6 +46,8 @@ class TestSecurityGroupRuleCompute(compute_fakes.TestComputev2): class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): + project = identity_fakes.FakeProject.create_one_project() + domain = identity_fakes.FakeDomain.create_one_domain() # The security group rule to be created. _security_group_rule = None @@ -103,19 +105,11 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): # 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, - ) + self.projects_mock.get.return_value = self.project # Get a shortcut to the DomainManager Mock self.domains_mock = self.identity.domains - self.domains_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.DOMAIN), - loaded=True, - ) + self.domains_mock.get.return_value = self.domain # Get the command object to test self.cmd = security_group_rule.CreateSecurityGroupRule( @@ -306,8 +300,8 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): '--dst-port', str(self._security_group_rule.port_range_min), '--egress', '--ethertype', self._security_group_rule.ethertype, - '--project', identity_fakes.project_name, - '--project-domain', identity_fakes.domain_name, + '--project', self.project.name, + '--project-domain', self.domain.name, '--protocol', self._security_group_rule.protocol, self._security_group.id, ] @@ -316,8 +310,8 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): self._security_group_rule.port_range_max)), ('egress', True), ('ethertype', self._security_group_rule.ethertype), - ('project', identity_fakes.project_name), - ('project_domain', identity_fakes.domain_name), + ('project', self.project.name), + ('project_domain', self.domain.name), ('protocol', self._security_group_rule.protocol), ('group', self._security_group.id), ] @@ -332,7 +326,7 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): 'port_range_min': self._security_group_rule.port_range_min, 'protocol': self._security_group_rule.protocol, 'security_group_id': self._security_group.id, - 'tenant_id': identity_fakes.project_id, + 'tenant_id': self.project.id, }) self.assertEqual(self.expected_columns, columns) self.assertEqual(self.expected_data, data) @@ -470,6 +464,8 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): class TestCreateSecurityGroupRuleCompute(TestSecurityGroupRuleCompute): + project = identity_fakes.FakeProject.create_one_project() + domain = identity_fakes.FakeDomain.create_one_domain() # The security group rule to be created. _security_group_rule = None @@ -534,8 +530,8 @@ class TestCreateSecurityGroupRuleCompute(TestSecurityGroupRuleCompute): '--ethertype', 'IPv4', '--icmp-type', '3', '--icmp-code', '11', - '--project', identity_fakes.project_name, - '--project-domain', identity_fakes.domain_name, + '--project', self.project.name, + '--project-domain', self.domain.name, self._security_group.id, ] self.assertRaises(tests_utils.ParserException, @@ -743,7 +739,7 @@ class TestDeleteSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): 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.assertEqual('1 of 2 rules failed to delete.', str(e)) self.network.find_security_group_rule.assert_any_call( self._security_group_rules[0].id, ignore_missing=False) @@ -823,7 +819,7 @@ class TestDeleteSecurityGroupRuleCompute(TestSecurityGroupRuleCompute): 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.assertEqual('1 of 2 rules failed to delete.', str(e)) self.compute.security_group_rules.delete.assert_any_call( self._security_group_rules[0].id) diff --git a/openstackclient/tests/network/v2/test_subnet.py b/openstackclient/tests/network/v2/test_subnet.py index 82813d6c..46af44fb 100644 --- a/openstackclient/tests/network/v2/test_subnet.py +++ b/openstackclient/tests/network/v2/test_subnet.py @@ -11,7 +11,6 @@ # under the License. # -import copy import mock from mock import call @@ -36,10 +35,12 @@ class TestSubnet(network_fakes.TestNetworkV2): class TestCreateSubnet(TestSubnet): + project = identity_fakes_v3.FakeProject.create_one_project() + domain = identity_fakes_v3.FakeDomain.create_one_domain() # An IPv4 subnet to be created with mostly default values _subnet = network_fakes.FakeSubnet.create_one_subnet( attrs={ - 'tenant_id': identity_fakes_v3.project_id, + 'tenant_id': project.id, } ) @@ -49,7 +50,7 @@ class TestCreateSubnet(TestSubnet): # An IPv4 subnet to be created using a specific subnet pool _subnet_from_pool = network_fakes.FakeSubnet.create_one_subnet( attrs={ - 'tenant_id': identity_fakes_v3.project_id, + 'tenant_id': project.id, 'subnetpool_id': _subnet_pool.id, 'dns_nameservers': ['8.8.8.8', '8.8.4.4'], @@ -63,7 +64,7 @@ class TestCreateSubnet(TestSubnet): # An IPv6 subnet to be created with most options specified _subnet_ipv6 = network_fakes.FakeSubnet.create_one_subnet( attrs={ - 'tenant_id': identity_fakes_v3.project_id, + 'tenant_id': project.id, 'cidr': 'fe80:0:0:a00a::/64', 'enable_dhcp': True, 'dns_nameservers': ['fe80:27ff:a00a:f00f::ffff', @@ -187,19 +188,11 @@ class TestCreateSubnet(TestSubnet): # 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_v3.PROJECT), - loaded=True, - ) + self.projects_mock.get.return_value = self.project # Get a shortcut to the DomainManager Mock self.domains_mock = self.identity.domains - self.domains_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes_v3.DOMAIN), - loaded=True, - ) + self.domains_mock.get.return_value = self.domain # Mock SDK calls for all tests. self.network.find_network = mock.Mock(return_value=self._network) diff --git a/openstackclient/tests/network/v2/test_subnet_pool.py b/openstackclient/tests/network/v2/test_subnet_pool.py index 41b6170f..8269af0b 100644 --- a/openstackclient/tests/network/v2/test_subnet_pool.py +++ b/openstackclient/tests/network/v2/test_subnet_pool.py @@ -12,7 +12,6 @@ # import argparse -import copy import mock from mock import call @@ -37,6 +36,8 @@ class TestSubnetPool(network_fakes.TestNetworkV2): class TestCreateSubnetPool(TestSubnetPool): + project = identity_fakes_v3.FakeProject.create_one_project() + domain = identity_fakes_v3.FakeDomain.create_one_domain() # The new subnet pool to create. _subnet_pool = network_fakes.FakeSubnetPool.create_one_subnet_pool() @@ -93,19 +94,11 @@ class TestCreateSubnetPool(TestSubnetPool): # 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_v3.PROJECT), - loaded=True, - ) + self.projects_mock.get.return_value = self.project # Get a shortcut to the DomainManager Mock self.domains_mock = self.identity.domains - self.domains_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes_v3.DOMAIN), - loaded=True, - ) + self.domains_mock.get.return_value = self.domain def test_create_no_options(self): arglist = [] @@ -191,14 +184,14 @@ class TestCreateSubnetPool(TestSubnetPool): def test_create_project_domain(self): arglist = [ '--pool-prefix', '10.0.10.0/24', - "--project", identity_fakes_v3.project_name, - "--project-domain", identity_fakes_v3.domain_name, + "--project", self.project.name, + "--project-domain", self.domain.name, self._subnet_pool.name, ] verifylist = [ ('prefixes', ['10.0.10.0/24']), - ('project', identity_fakes_v3.project_name), - ('project_domain', identity_fakes_v3.domain_name), + ('project', self.project.name), + ('project_domain', self.domain.name), ('name', self._subnet_pool.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -207,7 +200,7 @@ class TestCreateSubnetPool(TestSubnetPool): self.network.create_subnet_pool.assert_called_once_with(**{ 'prefixes': ['10.0.10.0/24'], - 'tenant_id': identity_fakes_v3.project_id, + 'tenant_id': self.project.id, 'name': self._subnet_pool.name, }) self.assertEqual(self.columns, columns) diff --git a/openstackclient/tests/volume/v2/fakes.py b/openstackclient/tests/volume/v2/fakes.py index 8a628ac9..74e30a41 100644 --- a/openstackclient/tests/volume/v2/fakes.py +++ b/openstackclient/tests/volume/v2/fakes.py @@ -418,6 +418,26 @@ class FakeBackup(object): return backups + @staticmethod + def get_backups(backups=None, count=2): + """Get an iterable MagicMock object with a list of faked backups. + + If backups list is provided, then initialize the Mock object with the + list. Otherwise create one. + + :param List volumes: + A list of FakeResource objects faking backups + :param Integer count: + The number of backups to be faked + :return + An iterable Mock object with side_effect set to a list of faked + backups + """ + if backups is None: + backups = FakeBackup.create_backups(count) + + return mock.MagicMock(side_effect=backups) + class FakeExtension(object): """Fake one or more extension.""" @@ -529,6 +549,26 @@ class FakeQos(object): return qoses + @staticmethod + def get_qoses(qoses=None, count=2): + """Get an iterable MagicMock object with a list of faked qoses. + + If qoses list is provided, then initialize the Mock object with the + list. Otherwise create one. + + :param List volumes: + A list of FakeResource objects faking qoses + :param Integer count: + The number of qoses to be faked + :return + An iterable Mock object with side_effect set to a list of faked + qoses + """ + if qoses is None: + qoses = FakeQos.create_qoses(count) + + return mock.MagicMock(side_effect=qoses) + class FakeSnapshot(object): """Fake one or more snapshot.""" @@ -582,6 +622,26 @@ class FakeSnapshot(object): return snapshots + @staticmethod + def get_snapshots(snapshots=None, count=2): + """Get an iterable MagicMock object with a list of faked snapshots. + + If snapshots list is provided, then initialize the Mock object with the + list. Otherwise create one. + + :param List volumes: + A list of FakeResource objects faking snapshots + :param Integer count: + The number of snapshots to be faked + :return + An iterable Mock object with side_effect set to a list of faked + snapshots + """ + if snapshots is None: + snapshots = FakeSnapshot.create_snapshots(count) + + return mock.MagicMock(side_effect=snapshots) + class FakeType(object): """Fake one or more type.""" diff --git a/openstackclient/tests/volume/v2/test_backup.py b/openstackclient/tests/volume/v2/test_backup.py index 31bfa64c..3c2b3948 100644 --- a/openstackclient/tests/volume/v2/test_backup.py +++ b/openstackclient/tests/volume/v2/test_backup.py @@ -12,6 +12,12 @@ # under the License. # +import mock +from mock import call + +from osc_lib import exceptions +from osc_lib import utils + from openstackclient.tests.volume.v2 import fakes as volume_fakes from openstackclient.volume.v2 import backup @@ -138,12 +144,13 @@ class TestBackupCreate(TestBackup): class TestBackupDelete(TestBackup): - backup = volume_fakes.FakeBackup.create_one_backup() + backups = volume_fakes.FakeBackup.create_backups(count=2) def setUp(self): super(TestBackupDelete, self).setUp() - self.backups_mock.get.return_value = self.backup + self.backups_mock.get = ( + volume_fakes.FakeBackup.get_backups(self.backups)) self.backups_mock.delete.return_value = None # Get the command object to mock @@ -151,34 +158,81 @@ class TestBackupDelete(TestBackup): def test_backup_delete(self): arglist = [ - self.backup.id + self.backups[0].id ] verifylist = [ - ("backups", [self.backup.id]) + ("backups", [self.backups[0].id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.backups_mock.delete.assert_called_with(self.backup.id, False) + self.backups_mock.delete.assert_called_with( + self.backups[0].id, False) self.assertIsNone(result) def test_backup_delete_with_force(self): arglist = [ '--force', - self.backup.id, + self.backups[0].id, ] verifylist = [ ('force', True), - ("backups", [self.backup.id]) + ("backups", [self.backups[0].id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.backups_mock.delete.assert_called_with(self.backup.id, True) + self.backups_mock.delete.assert_called_with(self.backups[0].id, True) self.assertIsNone(result) + def test_delete_multiple_backups(self): + arglist = [] + for b in self.backups: + arglist.append(b.id) + verifylist = [ + ('backups', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + calls = [] + for b in self.backups: + calls.append(call(b.id, False)) + self.backups_mock.delete.assert_has_calls(calls) + self.assertIsNone(result) + + def test_delete_multiple_backups_with_exception(self): + arglist = [ + self.backups[0].id, + 'unexist_backup', + ] + verifylist = [ + ('backups', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + find_mock_result = [self.backups[0], exceptions.CommandError] + with mock.patch.object(utils, 'find_resource', + side_effect=find_mock_result) as find_mock: + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 backups failed to delete.', + str(e)) + + find_mock.assert_any_call(self.backups_mock, self.backups[0].id) + find_mock.assert_any_call(self.backups_mock, 'unexist_backup') + + self.assertEqual(2, find_mock.call_count) + self.backups_mock.delete.assert_called_once_with( + self.backups[0].id, False + ) + class TestBackupList(TestBackup): diff --git a/openstackclient/tests/volume/v2/test_qos_specs.py b/openstackclient/tests/volume/v2/test_qos_specs.py index 92ffca74..56b8ae03 100644 --- a/openstackclient/tests/volume/v2/test_qos_specs.py +++ b/openstackclient/tests/volume/v2/test_qos_specs.py @@ -13,9 +13,14 @@ # under the License. # +import mock +from mock import call + +from osc_lib import exceptions +from osc_lib 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): @@ -155,45 +160,94 @@ class TestQosCreate(TestQos): class TestQosDelete(TestQos): - qos_spec = volume_fakes.FakeQos.create_one_qos() + qos_specs = volume_fakes.FakeQos.create_qoses(count=2) def setUp(self): super(TestQosDelete, self).setUp() - self.qos_mock.get.return_value = self.qos_spec + self.qos_mock.get = ( + volume_fakes.FakeQos.get_qoses(self.qos_specs)) # Get the command object to test self.cmd = qos_specs.DeleteQos(self.app, None) def test_qos_delete(self): arglist = [ - self.qos_spec.id + self.qos_specs[0].id ] verifylist = [ - ('qos_specs', [self.qos_spec.id]) + ('qos_specs', [self.qos_specs[0].id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.qos_mock.delete.assert_called_with(self.qos_spec.id, False) + self.qos_mock.delete.assert_called_with( + self.qos_specs[0].id, False) self.assertIsNone(result) def test_qos_delete_with_force(self): arglist = [ '--force', - self.qos_spec.id + self.qos_specs[0].id ] verifylist = [ ('force', True), - ('qos_specs', [self.qos_spec.id]) + ('qos_specs', [self.qos_specs[0].id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.qos_mock.delete.assert_called_with(self.qos_spec.id, True) + self.qos_mock.delete.assert_called_with( + self.qos_specs[0].id, True) + self.assertIsNone(result) + + def test_delete_multiple_qoses(self): + arglist = [] + for q in self.qos_specs: + arglist.append(q.id) + verifylist = [ + ('qos_specs', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + calls = [] + for q in self.qos_specs: + calls.append(call(q.id, False)) + self.qos_mock.delete.assert_has_calls(calls) self.assertIsNone(result) + def test_delete_multiple_qoses_with_exception(self): + arglist = [ + self.qos_specs[0].id, + 'unexist_qos', + ] + verifylist = [ + ('qos_specs', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + find_mock_result = [self.qos_specs[0], exceptions.CommandError] + with mock.patch.object(utils, 'find_resource', + side_effect=find_mock_result) as find_mock: + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual( + '1 of 2 QoS specifications failed to delete.', str(e)) + + find_mock.assert_any_call(self.qos_mock, self.qos_specs[0].id) + find_mock.assert_any_call(self.qos_mock, 'unexist_qos') + + self.assertEqual(2, find_mock.call_count) + self.qos_mock.delete.assert_called_once_with( + self.qos_specs[0].id, False + ) + class TestQosDisassociate(TestQos): diff --git a/openstackclient/tests/volume/v2/test_snapshot.py b/openstackclient/tests/volume/v2/test_snapshot.py index 2e9bcc82..04e0285e 100644 --- a/openstackclient/tests/volume/v2/test_snapshot.py +++ b/openstackclient/tests/volume/v2/test_snapshot.py @@ -12,6 +12,10 @@ # under the License. # +import mock +from mock import call + +from osc_lib import exceptions from osc_lib import utils from openstackclient.tests.volume.v2 import fakes as volume_fakes @@ -123,12 +127,13 @@ class TestSnapshotCreate(TestSnapshot): class TestSnapshotDelete(TestSnapshot): - snapshot = volume_fakes.FakeSnapshot.create_one_snapshot() + snapshots = volume_fakes.FakeSnapshot.create_snapshots(count=2) def setUp(self): super(TestSnapshotDelete, self).setUp() - self.snapshots_mock.get.return_value = self.snapshot + self.snapshots_mock.get = ( + volume_fakes.FakeSnapshot.get_snapshots(self.snapshots)) self.snapshots_mock.delete.return_value = None # Get the command object to mock @@ -136,18 +141,66 @@ class TestSnapshotDelete(TestSnapshot): def test_snapshot_delete(self): arglist = [ - self.snapshot.id + self.snapshots[0].id ] verifylist = [ - ("snapshots", [self.snapshot.id]) + ("snapshots", [self.snapshots[0].id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.snapshots_mock.delete.assert_called_with(self.snapshot.id) + self.snapshots_mock.delete.assert_called_with( + self.snapshots[0].id) self.assertIsNone(result) + def test_delete_multiple_snapshots(self): + arglist = [] + for s in self.snapshots: + arglist.append(s.id) + verifylist = [ + ('snapshots', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + calls = [] + for s in self.snapshots: + calls.append(call(s.id)) + self.snapshots_mock.delete.assert_has_calls(calls) + self.assertIsNone(result) + + def test_delete_multiple_snapshots_with_exception(self): + arglist = [ + self.snapshots[0].id, + 'unexist_snapshot', + ] + verifylist = [ + ('snapshots', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + find_mock_result = [self.snapshots[0], exceptions.CommandError] + with mock.patch.object(utils, 'find_resource', + side_effect=find_mock_result) as find_mock: + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 snapshots failed to delete.', + str(e)) + + find_mock.assert_any_call( + self.snapshots_mock, self.snapshots[0].id) + find_mock.assert_any_call(self.snapshots_mock, 'unexist_snapshot') + + self.assertEqual(2, find_mock.call_count) + self.snapshots_mock.delete.assert_called_once_with( + self.snapshots[0].id + ) + class TestSnapshotList(TestSnapshot): diff --git a/openstackclient/tests/volume/v2/test_type.py b/openstackclient/tests/volume/v2/test_type.py index 174f33f2..a7db2e49 100644 --- a/openstackclient/tests/volume/v2/test_type.py +++ b/openstackclient/tests/volume/v2/test_type.py @@ -14,6 +14,7 @@ import copy +from osc_lib import exceptions from osc_lib import utils from openstackclient.tests import fakes @@ -41,6 +42,7 @@ class TestType(volume_fakes.TestVolume): class TestTypeCreate(TestType): + project = identity_fakes.FakeProject.create_one_project() columns = ( 'description', 'id', @@ -58,6 +60,7 @@ class TestTypeCreate(TestType): ) self.types_mock.create.return_value = self.new_volume_type + self.projects_mock.get.return_value = self.project # Get the command object to test self.cmd = volume_type.CreateVolumeType(self.app, None) @@ -89,12 +92,14 @@ class TestTypeCreate(TestType): arglist = [ "--description", self.new_volume_type.description, "--private", + "--project", self.project.id, self.new_volume_type.name, ] verifylist = [ ("description", self.new_volume_type.description), ("public", False), ("private", True), + ("project", self.project.id), ("name", self.new_volume_type.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -109,6 +114,21 @@ class TestTypeCreate(TestType): self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) + def test_public_type_create_with_project(self): + arglist = [ + '--project', self.project.id, + self.new_volume_type.name, + ] + verifylist = [ + ('project', self.project.id), + ('name', self.new_volume_type.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises(exceptions.CommandError, + self.cmd.take_action, + parsed_args) + class TestTypeDelete(TestType): diff --git a/openstackclient/tests/volume/v2/test_volume.py b/openstackclient/tests/volume/v2/test_volume.py index 68158df0..db65c3bd 100644 --- a/openstackclient/tests/volume/v2/test_volume.py +++ b/openstackclient/tests/volume/v2/test_volume.py @@ -13,9 +13,10 @@ # import copy - +import mock from mock import call +from osc_lib import exceptions from osc_lib import utils from openstackclient.tests import fakes @@ -458,6 +459,36 @@ class TestVolumeDelete(TestVolume): self.volumes_mock.delete.assert_has_calls(calls) self.assertIsNone(result) + def test_volume_delete_multi_volumes_with_exception(self): + volumes = self.setup_volumes_mock(count=2) + + arglist = [ + volumes[0].id, + 'unexist_volume', + ] + verifylist = [ + ('volumes', arglist), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + find_mock_result = [volumes[0], exceptions.CommandError] + with mock.patch.object(utils, 'find_resource', + side_effect=find_mock_result) as find_mock: + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 volumes failed to delete.', + str(e)) + + find_mock.assert_any_call(self.volumes_mock, volumes[0].id) + find_mock.assert_any_call(self.volumes_mock, 'unexist_volume') + + self.assertEqual(2, find_mock.call_count) + self.volumes_mock.delete.assert_called_once_with( + volumes[0].id + ) + class TestVolumeList(TestVolume): diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py index e11aa1a7..820673bb 100644 --- a/openstackclient/volume/v1/volume.py +++ b/openstackclient/volume/v1/volume.py @@ -175,7 +175,6 @@ class DeleteVolume(command.Command): ) parser.add_argument( '--force', - dest='force', action='store_true', default=False, help=_('Attempt forced removal of volume(s), regardless of state ' diff --git a/openstackclient/volume/v2/backup.py b/openstackclient/volume/v2/backup.py index 49226bcc..3d27c121 100644 --- a/openstackclient/volume/v2/backup.py +++ b/openstackclient/volume/v2/backup.py @@ -15,14 +15,19 @@ """Volume v2 Backup action implementations""" import copy +import logging from osc_lib.command import command +from osc_lib import exceptions from osc_lib import utils import six from openstackclient.i18n import _ +LOG = logging.getLogger(__name__) + + class CreateBackup(command.ShowOne): """Create new backup""" @@ -109,10 +114,24 @@ class DeleteBackup(command.Command): def take_action(self, parsed_args): volume_client = self.app.client_manager.volume - for backup in parsed_args.backups: - backup_id = utils.find_resource( - volume_client.backups, backup).id - volume_client.backups.delete(backup_id, parsed_args.force) + result = 0 + + for i in parsed_args.backups: + try: + backup_id = utils.find_resource( + volume_client.backups, i).id + volume_client.backups.delete(backup_id, parsed_args.force) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete backup with " + "name or ID '%(backup)s': %(e)s") + % {'backup': i, 'e': e}) + + if result > 0: + total = len(parsed_args.backups) + msg = (_("%(result)s of %(total)s backups failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class ListBackup(command.Lister): diff --git a/openstackclient/volume/v2/qos_specs.py b/openstackclient/volume/v2/qos_specs.py index 5ed1225b..9797f1a6 100644 --- a/openstackclient/volume/v2/qos_specs.py +++ b/openstackclient/volume/v2/qos_specs.py @@ -15,14 +15,20 @@ """Volume v2 QoS 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.i18n import _ +LOG = logging.getLogger(__name__) + + class AssociateQos(command.Command): """Associate a QoS specification to a volume type""" @@ -113,9 +119,23 @@ class DeleteQos(command.Command): def take_action(self, parsed_args): volume_client = self.app.client_manager.volume - for qos in parsed_args.qos_specs: - qos_spec = utils.find_resource(volume_client.qos_specs, qos) - volume_client.qos_specs.delete(qos_spec.id, parsed_args.force) + result = 0 + + for i in parsed_args.qos_specs: + try: + qos_spec = utils.find_resource(volume_client.qos_specs, i) + volume_client.qos_specs.delete(qos_spec.id, parsed_args.force) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete QoS specification with " + "name or ID '%(qos)s': %(e)s") + % {'qos': i, 'e': e}) + + if result > 0: + total = len(parsed_args.qos_specs) + msg = (_("%(result)s of %(total)s QoS specifications failed" + " to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class DisassociateQos(command.Command): diff --git a/openstackclient/volume/v2/snapshot.py b/openstackclient/volume/v2/snapshot.py index 5e6949d4..ba692074 100644 --- a/openstackclient/volume/v2/snapshot.py +++ b/openstackclient/volume/v2/snapshot.py @@ -15,15 +15,20 @@ """Volume v2 snapshot action implementations""" import copy +import logging from osc_lib.cli import parseractions from osc_lib.command import command +from osc_lib import exceptions from osc_lib import utils import six from openstackclient.i18n import _ +LOG = logging.getLogger(__name__) + + class CreateSnapshot(command.ShowOne): """Create new snapshot""" @@ -92,10 +97,24 @@ class DeleteSnapshot(command.Command): def take_action(self, parsed_args): volume_client = self.app.client_manager.volume - for snapshot in parsed_args.snapshots: - snapshot_id = utils.find_resource( - volume_client.volume_snapshots, snapshot).id - volume_client.volume_snapshots.delete(snapshot_id) + result = 0 + + for i in parsed_args.snapshots: + try: + snapshot_id = utils.find_resource( + volume_client.volume_snapshots, i).id + volume_client.volume_snapshots.delete(snapshot_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete snapshot with " + "name or ID '%(snapshot)s': %(e)s") + % {'snapshot': i, 'e': e}) + + if result > 0: + total = len(parsed_args.snapshots) + msg = (_("%(result)s of %(total)s snapshots failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class ListSnapshot(command.Lister): diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index be2388fb..85f267ef 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -19,6 +19,7 @@ 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 @@ -167,7 +168,6 @@ class DeleteVolume(command.Command): ) parser.add_argument( "--force", - dest="force", action="store_true", default=False, help=_("Attempt forced removal of volume(s), regardless of state " @@ -177,13 +177,27 @@ class DeleteVolume(command.Command): def take_action(self, parsed_args): volume_client = self.app.client_manager.volume - for volume in parsed_args.volumes: - volume_obj = utils.find_resource( - volume_client.volumes, volume) - if parsed_args.force: - volume_client.volumes.force_delete(volume_obj.id) - else: - volume_client.volumes.delete(volume_obj.id) + result = 0 + + for i in parsed_args.volumes: + try: + volume_obj = utils.find_resource( + volume_client.volumes, i) + if parsed_args.force: + volume_client.volumes.force_delete(volume_obj.id) + else: + volume_client.volumes.delete(volume_obj.id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete volume with " + "name or ID '%(volume)s': %(e)s") + % {'volume': i, 'e': e}) + + if result > 0: + total = len(parsed_args.volumes) + msg = (_("%(result)s of %(total)s volumes failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class ListVolume(command.Lister): diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py index 87f4c547..ac11785c 100644 --- a/openstackclient/volume/v2/volume_type.py +++ b/openstackclient/volume/v2/volume_type.py @@ -47,14 +47,12 @@ class CreateVolumeType(command.ShowOne): public_group = parser.add_mutually_exclusive_group() public_group.add_argument( "--public", - dest="public", action="store_true", default=False, help=_("Volume type is accessible to the public"), ) public_group.add_argument( "--private", - dest="private", action="store_true", default=False, help=_("Volume type is not accessible to the public"), @@ -66,12 +64,23 @@ class CreateVolumeType(command.ShowOne): help=_('Set a property on this volume type ' '(repeat option to set multiple properties)'), ) + parser.add_argument( + '--project', + metavar='<project>', + help=_("Allow <project> to access private type (name or ID) " + "(Must be used with --private option)"), + ) + identity_common.add_project_domain_option_to_parser(parser) return parser def take_action(self, parsed_args): - + identity_client = self.app.client_manager.identity volume_client = self.app.client_manager.volume + if parsed_args.project and not parsed_args.private: + msg = _("--project is only allowed with --private") + raise exceptions.CommandError(msg) + kwargs = {} if parsed_args.public: kwargs['is_public'] = True @@ -84,6 +93,20 @@ class CreateVolumeType(command.ShowOne): **kwargs ) volume_type._info.pop('extra_specs') + + if parsed_args.project: + try: + project_id = identity_common.find_project( + identity_client, + parsed_args.project, + parsed_args.project_domain, + ).id + volume_client.volume_type_access.add_project_access( + volume_type.id, project_id) + except Exception as e: + msg = _("Failed to add project %(project)s access to " + "type: %(e)s") + LOG.error(msg % {'project': parsed_args.project, 'e': e}) if parsed_args.property: result = volume_type.set_keys(parsed_args.property) volume_type._info.update({'properties': utils.format_dict(result)}) |
