diff options
Diffstat (limited to 'openstackclient')
42 files changed, 833 insertions, 394 deletions
diff --git a/openstackclient/api/object_store_v1.py b/openstackclient/api/object_store_v1.py index ae49922d..faa55118 100644 --- a/openstackclient/api/object_store_v1.py +++ b/openstackclient/api/object_store_v1.py @@ -17,11 +17,10 @@ import io import logging import os +from osc_lib import utils import six from six.moves import urllib -from osc_lib import utils - from openstackclient.api import api diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py index 9e4b9c93..2105a497 100644 --- a/openstackclient/common/clientmanager.py +++ b/openstackclient/common/clientmanager.py @@ -19,8 +19,6 @@ import logging import pkg_resources import sys -from keystoneauth1.loading import base -from osc_lib.api import auth from osc_lib import clientmanager @@ -31,77 +29,6 @@ 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 ClientManager(clientmanager.ClientManager): """Manages access to API clients, including authentication diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py index 3c12c366..5d53171c 100644 --- a/openstackclient/common/quota.py +++ b/openstackclient/common/quota.py @@ -38,6 +38,8 @@ COMPUTE_QUOTAS = { 'key_pairs': 'key-pairs', 'metadata_items': 'properties', 'ram': 'ram', + 'server_groups': 'server-groups', + 'server_group_members': 'server-group-members', } VOLUME_QUOTAS = { diff --git a/openstackclient/compute/v2/hypervisor_stats.py b/openstackclient/compute/v2/hypervisor_stats.py index a70f0ce1..c6fd2992 100644 --- a/openstackclient/compute/v2/hypervisor_stats.py +++ b/openstackclient/compute/v2/hypervisor_stats.py @@ -14,9 +14,8 @@ """Hypervisor Stats action implementations""" -import six - from osc_lib.command import command +import six class ShowHypervisorStats(command.ShowOne): diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 16c86bd9..3e6903b7 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -260,7 +260,7 @@ class AddServerSecurityGroup(command.Command): parsed_args.group, ) - server.add_security_group(security_group.name) + server.add_security_group(security_group.id) class AddServerVolume(command.Command): diff --git a/openstackclient/identity/client.py b/openstackclient/identity/client.py index d932b970..0c609313 100644 --- a/openstackclient/identity/client.py +++ b/openstackclient/identity/client.py @@ -21,6 +21,7 @@ from osc_lib import utils from openstackclient.i18n import _ + LOG = logging.getLogger(__name__) DEFAULT_API_VERSION = '3' diff --git a/openstackclient/identity/common.py b/openstackclient/identity/common.py index 379f4114..1e40f396 100644 --- a/openstackclient/identity/common.py +++ b/openstackclient/identity/common.py @@ -30,21 +30,32 @@ def find_service(identity_client, name_type_or_id): """Find a service by id, name or type.""" try: - # search for the usual ID or name - return utils.find_resource(identity_client.services, name_type_or_id) - except exceptions.CommandError: - try: - # search for service type - return identity_client.services.find(type=name_type_or_id) - # FIXME(dtroyer): This exception should eventually come from - # common client exceptions - except identity_exc.NotFound: - msg = _("No service with a type, name or ID of '%s' exists.") - raise exceptions.CommandError(msg % name_type_or_id) - except identity_exc.NoUniqueMatch: - msg = _("Multiple service matches found for '%s', " - "use an ID to be more specific.") - raise exceptions.CommandError(msg % name_type_or_id) + # search for service id + return identity_client.services.get(name_type_or_id) + except identity_exc.NotFound: + # ignore NotFound exception, raise others + pass + + try: + # search for service name + return identity_client.services.find(name=name_type_or_id) + except identity_exc.NotFound: + pass + except identity_exc.NoUniqueMatch: + msg = _("Multiple service matches found for '%s', " + "use an ID to be more specific.") + raise exceptions.CommandError(msg % name_type_or_id) + + try: + # search for service type + return identity_client.services.find(type=name_type_or_id) + except identity_exc.NotFound: + msg = _("No service with a type, name or ID of '%s' exists.") + raise exceptions.CommandError(msg % name_type_or_id) + except identity_exc.NoUniqueMatch: + msg = _("Multiple service matches found for '%s', " + "use an ID to be more specific.") + raise exceptions.CommandError(msg % name_type_or_id) def _get_token_resource(client, resource, parsed_name): diff --git a/openstackclient/network/v2/subnet_pool.py b/openstackclient/network/v2/subnet_pool.py index ed2bb0ef..d3fab8ac 100644 --- a/openstackclient/network/v2/subnet_pool.py +++ b/openstackclient/network/v2/subnet_pool.py @@ -13,7 +13,6 @@ """Subnet pool action implementations""" import copy - import logging from osc_lib.cli import parseractions diff --git a/openstackclient/object/v1/container.py b/openstackclient/object/v1/container.py index 2b021ec2..f00cc150 100644 --- a/openstackclient/object/v1/container.py +++ b/openstackclient/object/v1/container.py @@ -15,7 +15,6 @@ """Container v1 action implementations""" - from osc_lib.cli import parseractions from osc_lib.command import command from osc_lib import utils diff --git a/openstackclient/tests/api/fakes.py b/openstackclient/tests/api/fakes.py index e285a61c..8d1d88ff 100644 --- a/openstackclient/tests/api/fakes.py +++ b/openstackclient/tests/api/fakes.py @@ -13,9 +13,9 @@ """API Test Fakes""" +from keystoneauth1 import session from requests_mock.contrib import fixture -from keystoneauth1 import session from openstackclient.tests import utils diff --git a/openstackclient/tests/api/test_image_v1.py b/openstackclient/tests/api/test_image_v1.py index f3479756..f8ad6692 100644 --- a/openstackclient/tests/api/test_image_v1.py +++ b/openstackclient/tests/api/test_image_v1.py @@ -13,9 +13,9 @@ """Image v1 API Library Tests""" +from keystoneauth1 import session from requests_mock.contrib import fixture -from keystoneauth1 import session from openstackclient.api import image_v1 from openstackclient.tests import utils diff --git a/openstackclient/tests/api/test_image_v2.py b/openstackclient/tests/api/test_image_v2.py index 77063997..28b0d580 100644 --- a/openstackclient/tests/api/test_image_v2.py +++ b/openstackclient/tests/api/test_image_v2.py @@ -13,9 +13,9 @@ """Image v2 API Library Tests""" +from keystoneauth1 import session from requests_mock.contrib import fixture -from keystoneauth1 import session from openstackclient.api import image_v2 from openstackclient.tests import utils diff --git a/openstackclient/tests/api/test_object_store_v1.py b/openstackclient/tests/api/test_object_store_v1.py index 8cc3a927..e6ac203d 100644 --- a/openstackclient/tests/api/test_object_store_v1.py +++ b/openstackclient/tests/api/test_object_store_v1.py @@ -15,9 +15,9 @@ import mock +from keystoneauth1 import session from requests_mock.contrib import fixture -from keystoneauth1 import session from openstackclient.api import object_store_v1 as object_store from openstackclient.tests import utils diff --git a/openstackclient/tests/common/test_availability_zone.py b/openstackclient/tests/common/test_availability_zone.py index feecaf55..014ab8bc 100644 --- a/openstackclient/tests/common/test_availability_zone.py +++ b/openstackclient/tests/common/test_availability_zone.py @@ -12,6 +12,7 @@ # import mock + import six from openstackclient.common import availability_zone diff --git a/openstackclient/tests/common/test_extension.py b/openstackclient/tests/common/test_extension.py index 17a3b492..10023aa6 100644 --- a/openstackclient/tests/common/test_extension.py +++ b/openstackclient/tests/common/test_extension.py @@ -14,12 +14,11 @@ import mock from openstackclient.common import extension -from openstackclient.tests import fakes -from openstackclient.tests import utils - from openstackclient.tests.compute.v2 import fakes as compute_fakes +from openstackclient.tests import fakes from openstackclient.tests.identity.v2_0 import fakes as identity_fakes from openstackclient.tests.network.v2 import fakes as network_fakes +from openstackclient.tests import utils from openstackclient.tests.volume.v2 import fakes as volume_fakes diff --git a/openstackclient/tests/common/test_parseractions.py b/openstackclient/tests/common/test_parseractions.py index 60d4a8cf..3038701f 100644 --- a/openstackclient/tests/common/test_parseractions.py +++ b/openstackclient/tests/common/test_parseractions.py @@ -49,7 +49,7 @@ class TestKeyValueAction(utils.TestCase): actual = getattr(results, 'property', {}) # All should pass through unmolested expect = {'red': '', 'green': '100%', 'blue': '50%', 'format': '#rgb'} - self.assertDictEqual(expect, actual) + self.assertEqual(expect, actual) def test_error_values(self): self.assertRaises( diff --git a/openstackclient/tests/common/test_quota.py b/openstackclient/tests/common/test_quota.py index c9ec5599..16fa35f6 100644 --- a/openstackclient/tests/common/test_quota.py +++ b/openstackclient/tests/common/test_quota.py @@ -11,7 +11,6 @@ # under the License. import copy - import mock from openstackclient.common import quota @@ -116,6 +115,8 @@ class TestQuotaSet(TestQuota): '--properties', str(compute_fakes.property_num), '--secgroup-rules', str(compute_fakes.secgroup_rule_num), '--secgroups', str(compute_fakes.secgroup_num), + '--server-groups', str(compute_fakes.servgroup_num), + '--server-group-members', str(compute_fakes.servgroup_members_num), identity_fakes.project_name, ] verifylist = [ @@ -132,6 +133,8 @@ class TestQuotaSet(TestQuota): ('metadata_items', compute_fakes.property_num), ('security_group_rules', compute_fakes.secgroup_rule_num), ('security_groups', compute_fakes.secgroup_num), + ('server_groups', compute_fakes.servgroup_num), + ('server_group_members', compute_fakes.servgroup_members_num), ('project', identity_fakes.project_name), ] @@ -154,6 +157,8 @@ class TestQuotaSet(TestQuota): 'metadata_items': compute_fakes.property_num, 'security_group_rules': compute_fakes.secgroup_rule_num, 'security_groups': compute_fakes.secgroup_num, + 'server_groups': compute_fakes.servgroup_num, + 'server_group_members': compute_fakes.servgroup_members_num, } self.quotas_mock.update.assert_called_with( diff --git a/openstackclient/tests/compute/v2/fakes.py b/openstackclient/tests/compute/v2/fakes.py index b4243a22..85c11c94 100644 --- a/openstackclient/tests/compute/v2/fakes.py +++ b/openstackclient/tests/compute/v2/fakes.py @@ -36,6 +36,8 @@ instance_num = 10 property_num = 128 secgroup_rule_num = 20 secgroup_num = 10 +servgroup_num = 10 +servgroup_members_num = 10 project_name = 'project_test' QUOTA = { 'project': project_name, @@ -51,6 +53,8 @@ QUOTA = { 'properties': property_num, 'secgroup_rules': secgroup_rule_num, 'secgroups': secgroup_num, + 'server-groups': servgroup_num, + 'server-group-members': servgroup_members_num } QUOTA_columns = tuple(sorted(QUOTA)) diff --git a/openstackclient/tests/compute/v2/test_agent.py b/openstackclient/tests/compute/v2/test_agent.py index 7695ee41..07265bb0 100644 --- a/openstackclient/tests/compute/v2/test_agent.py +++ b/openstackclient/tests/compute/v2/test_agent.py @@ -14,8 +14,8 @@ # import mock - from mock import call + from osc_lib import exceptions from openstackclient.compute.v2 import agent diff --git a/openstackclient/tests/compute/v2/test_flavor.py b/openstackclient/tests/compute/v2/test_flavor.py index 40cd17c1..d326ea8a 100644 --- a/openstackclient/tests/compute/v2/test_flavor.py +++ b/openstackclient/tests/compute/v2/test_flavor.py @@ -13,7 +13,6 @@ # under the License. # -import copy import mock from mock import call @@ -22,7 +21,6 @@ from osc_lib import utils from openstackclient.compute.v2 import flavor from openstackclient.tests.compute.v2 import fakes as compute_fakes -from openstackclient.tests import fakes from openstackclient.tests.identity.v3 import fakes as identity_fakes from openstackclient.tests import utils as tests_utils @@ -48,7 +46,7 @@ class TestFlavorCreate(TestFlavor): flavor = compute_fakes.FakeFlavor.create_one_flavor( attrs={'links': 'flavor-links'}) - + project = identity_fakes.FakeProject.create_one_project() columns = ( 'OS-FLV-DISABLED:disabled', 'OS-FLV-EXT-DATA:ephemeral', @@ -80,11 +78,7 @@ class TestFlavorCreate(TestFlavor): super(TestFlavorCreate, self).setUp() # Return a project - self.projects_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.PROJECT), - loaded=True, - ) + self.projects_mock.get.return_value = self.project self.flavors_mock.create.return_value = self.flavor self.cmd = flavor.CreateFlavor(self.app, None) @@ -174,7 +168,7 @@ class TestFlavorCreate(TestFlavor): '--vcpus', str(self.flavor.vcpus), '--rxtx-factor', str(self.flavor.rxtx_factor), '--private', - '--project', identity_fakes.project_id, + '--project', self.project.id, '--property', 'key1=value1', '--property', 'key2=value2', self.flavor.name, @@ -188,7 +182,7 @@ class TestFlavorCreate(TestFlavor): ('vcpus', self.flavor.vcpus), ('rxtx_factor', self.flavor.rxtx_factor), ('public', False), - ('project', identity_fakes.project_id), + ('project', self.project.id), ('property', {'key1': 'value1', 'key2': 'value2'}), ('name', self.flavor.name), ] @@ -209,7 +203,7 @@ class TestFlavorCreate(TestFlavor): self.flavors_mock.create.assert_called_once_with(*args) self.flavor_access_mock.add_tenant_access.assert_called_with( self.flavor.id, - identity_fakes.project_id, + self.project.id, ) self.flavor.set_keys.assert_called_with( {'key1': 'value1', 'key2': 'value2'}) @@ -219,11 +213,11 @@ class TestFlavorCreate(TestFlavor): def test_public_flavor_create_with_project(self): arglist = [ - '--project', identity_fakes.project_id, + '--project', self.project.id, self.flavor.name, ] verifylist = [ - ('project', identity_fakes.project_id), + ('project', self.project.id), ('name', self.flavor.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -507,6 +501,7 @@ class TestFlavorSet(TestFlavor): # Return value of self.flavors_mock.find(). flavor = compute_fakes.FakeFlavor.create_one_flavor( attrs={'os-flavor-access:is_public': False}) + project = identity_fakes.FakeProject.create_one_project() def setUp(self): super(TestFlavorSet, self).setUp() @@ -514,11 +509,7 @@ class TestFlavorSet(TestFlavor): self.flavors_mock.find.return_value = self.flavor self.flavors_mock.get.side_effect = exceptions.NotFound(None) # Return a project - self.projects_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.PROJECT), - loaded=True, - ) + self.projects_mock.get.return_value = self.project self.cmd = flavor.SetFlavor(self.app, None) def test_flavor_set_property(self): @@ -540,11 +531,11 @@ class TestFlavorSet(TestFlavor): def test_flavor_set_project(self): arglist = [ - '--project', identity_fakes.project_id, + '--project', self.project.id, self.flavor.id, ] verifylist = [ - ('project', identity_fakes.project_id), + ('project', self.project.id), ('flavor', self.flavor.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -555,7 +546,7 @@ class TestFlavorSet(TestFlavor): is_public=None) self.flavor_access_mock.add_tenant_access.assert_called_with( self.flavor.id, - identity_fakes.project_id, + self.project.id, ) self.flavor.set_keys.assert_not_called() self.assertIsNone(result) @@ -574,10 +565,10 @@ class TestFlavorSet(TestFlavor): def test_flavor_set_no_flavor(self): arglist = [ - '--project', identity_fakes.project_id, + '--project', self.project.id, ] verifylist = [ - ('project', identity_fakes.project_id), + ('project', self.project.id), ] self.assertRaises(tests_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) @@ -587,11 +578,11 @@ class TestFlavorSet(TestFlavor): self.flavors_mock.find.side_effect = exceptions.NotFound(None) arglist = [ - '--project', identity_fakes.project_id, + '--project', self.project.id, 'unexist_flavor', ] verifylist = [ - ('project', identity_fakes.project_id), + ('project', self.project.id), ('flavor', 'unexist_flavor'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -729,6 +720,7 @@ class TestFlavorUnset(TestFlavor): # Return value of self.flavors_mock.find(). flavor = compute_fakes.FakeFlavor.create_one_flavor( attrs={'os-flavor-access:is_public': False}) + project = identity_fakes.FakeProject.create_one_project() def setUp(self): super(TestFlavorUnset, self).setUp() @@ -736,11 +728,7 @@ class TestFlavorUnset(TestFlavor): self.flavors_mock.find.return_value = self.flavor self.flavors_mock.get.side_effect = exceptions.NotFound(None) # Return a project - self.projects_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.PROJECT), - loaded=True, - ) + self.projects_mock.get.return_value = self.project self.cmd = flavor.UnsetFlavor(self.app, None) def test_flavor_unset_property(self): @@ -763,11 +751,11 @@ class TestFlavorUnset(TestFlavor): def test_flavor_unset_project(self): arglist = [ - '--project', identity_fakes.project_id, + '--project', self.project.id, self.flavor.id, ] verifylist = [ - ('project', identity_fakes.project_id), + ('project', self.project.id), ('flavor', self.flavor.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -779,7 +767,7 @@ class TestFlavorUnset(TestFlavor): is_public=None) self.flavor_access_mock.remove_tenant_access.assert_called_with( self.flavor.id, - identity_fakes.project_id, + self.project.id, ) self.flavor.unset_keys.assert_not_called() self.assertIsNone(result) @@ -798,10 +786,10 @@ class TestFlavorUnset(TestFlavor): def test_flavor_unset_no_flavor(self): arglist = [ - '--project', identity_fakes.project_id, + '--project', self.project.id, ] verifylist = [ - ('project', identity_fakes.project_id), + ('project', self.project.id), ] self.assertRaises(tests_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) @@ -811,11 +799,11 @@ class TestFlavorUnset(TestFlavor): self.flavors_mock.find.side_effect = exceptions.NotFound(None) arglist = [ - '--project', identity_fakes.project_id, + '--project', self.project.id, 'unexist_flavor', ] verifylist = [ - ('project', identity_fakes.project_id), + ('project', self.project.id), ('flavor', 'unexist_flavor'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/openstackclient/tests/compute/v2/test_server.py b/openstackclient/tests/compute/v2/test_server.py index 9c89c6af..a98398ee 100644 --- a/openstackclient/tests/compute/v2/test_server.py +++ b/openstackclient/tests/compute/v2/test_server.py @@ -168,6 +168,54 @@ class TestServerAddFloatingIP(TestServer): self.assertIsNone(result) +class TestServerAddSecurityGroup(TestServer): + + def setUp(self): + super(TestServerAddSecurityGroup, 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 = { + 'add_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.AddServerSecurityGroup(self.app, None) + + def test_server_add_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.add_security_group.assert_called_with( + self.security_group.id, + ) + self.assertIsNone(result) + + class TestServerCreate(TestServer): columns = ( @@ -1793,4 +1841,4 @@ class TestServerGeneral(TestServer): server_detail.pop('networks') # Check the results. - self.assertDictEqual(info, server_detail) + self.assertEqual(info, server_detail) diff --git a/openstackclient/tests/fakes.py b/openstackclient/tests/fakes.py index d0cab019..786cd6d4 100644 --- a/openstackclient/tests/fakes.py +++ b/openstackclient/tests/fakes.py @@ -15,11 +15,11 @@ import json import mock -import six import sys from keystoneauth1 import fixture import requests +import six AUTH_TOKEN = "foobar" diff --git a/openstackclient/tests/identity/v2_0/test_endpoint.py b/openstackclient/tests/identity/v2_0/test_endpoint.py index b2b6d0f1..26ec654d 100644 --- a/openstackclient/tests/identity/v2_0/test_endpoint.py +++ b/openstackclient/tests/identity/v2_0/test_endpoint.py @@ -103,9 +103,6 @@ class TestEndpointDelete(TestEndpoint): super(TestEndpointDelete, self).setUp() self.endpoints_mock.get.return_value = self.fake_endpoint - - self.services_mock.get.return_value = self.fake_service - self.endpoints_mock.delete.return_value = None # Get the command object to test diff --git a/openstackclient/tests/identity/v2_0/test_service.py b/openstackclient/tests/identity/v2_0/test_service.py index 318fa83d..7efd2a60 100644 --- a/openstackclient/tests/identity/v2_0/test_service.py +++ b/openstackclient/tests/identity/v2_0/test_service.py @@ -13,6 +13,9 @@ # under the License. # +from keystoneclient import exceptions as identity_exc +from osc_lib import exceptions + from openstackclient.identity.v2_0 import service from openstackclient.tests.identity.v2_0 import fakes as identity_fakes @@ -170,7 +173,8 @@ class TestServiceDelete(TestService): def setUp(self): super(TestServiceDelete, self).setUp() - self.services_mock.get.return_value = self.fake_service + self.services_mock.get.side_effect = identity_exc.NotFound(None) + self.services_mock.find.return_value = self.fake_service self.services_mock.delete.return_value = None # Get the command object to test @@ -253,20 +257,23 @@ class TestServiceList(TestService): class TestServiceShow(TestService): + fake_service_s = identity_fakes.FakeService.create_one_service() + def setUp(self): super(TestServiceShow, self).setUp() - self.services_mock.get.return_value = self.fake_service + self.services_mock.get.side_effect = identity_exc.NotFound(None) + self.services_mock.find.return_value = self.fake_service_s # Get the command object to test self.cmd = service.ShowService(self.app, None) def test_service_show(self): arglist = [ - self.fake_service.name, + self.fake_service_s.name, ] verifylist = [ - ('service', self.fake_service.name), + ('service', self.fake_service_s.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -275,17 +282,35 @@ class TestServiceShow(TestService): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - # ServiceManager.get(id) - self.services_mock.get.assert_called_with( - self.fake_service.name, + # ServiceManager.find(id) + self.services_mock.find.assert_called_with( + name=self.fake_service_s.name, ) collist = ('description', 'id', 'name', 'type') self.assertEqual(collist, columns) datalist = ( - self.fake_service.description, - self.fake_service.id, - self.fake_service.name, - self.fake_service.type, + self.fake_service_s.description, + self.fake_service_s.id, + self.fake_service_s.name, + self.fake_service_s.type, ) self.assertEqual(datalist, data) + + def test_service_show_nounique(self): + self.services_mock.find.side_effect = identity_exc.NoUniqueMatch(None) + arglist = [ + 'nounique_service', + ] + verifylist = [ + ('service', 'nounique_service'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual( + "Multiple service matches found for 'nounique_service'," + " use an ID to be more specific.", str(e)) diff --git a/openstackclient/tests/identity/v3/fakes.py b/openstackclient/tests/identity/v3/fakes.py index df532df4..bad15b6c 100644 --- a/openstackclient/tests/identity/v3/fakes.py +++ b/openstackclient/tests/identity/v3/fakes.py @@ -198,8 +198,6 @@ SERVICE_WITHOUT_NAME = { 'links': base_url + 'services/' + service_id, } -credential_id = 'c-123' - endpoint_id = 'e-123' endpoint_url = 'http://127.0.0.1:35357' endpoint_region = 'RegionOne' @@ -639,3 +637,104 @@ class FakeDomain(object): domain = fakes.FakeResource(info=copy.deepcopy(domain_info), loaded=True) return domain + + +class FakeCredential(object): + """Fake one or more credential.""" + + @staticmethod + def create_one_credential(attrs=None): + """Create a fake credential. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, type, and so on + """ + + attrs = attrs or {} + + # set default attributes. + credential_info = { + 'id': 'credential-id-' + uuid.uuid4().hex, + 'type': 'cert', + 'user_id': 'user-id-' + uuid.uuid4().hex, + 'blob': 'credential-data-' + uuid.uuid4().hex, + 'project_id': 'project-id-' + uuid.uuid4().hex, + 'links': 'links-' + uuid.uuid4().hex, + } + credential_info.update(attrs) + + credential = fakes.FakeResource( + info=copy.deepcopy(credential_info), loaded=True) + return credential + + @staticmethod + def create_credentials(attrs=None, count=2): + """Create multiple fake credentials. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of credentials to fake + :return: + A list of FakeResource objects faking the credentials + """ + credentials = [] + for i in range(0, count): + credential = FakeCredential.create_one_credential(attrs) + credentials.append(credential) + + return credentials + + @staticmethod + def get_credentials(credentials=None, count=2): + """Get an iterable MagicMock object with a list of faked credentials. + + If credentials list is provided, then initialize the Mock object with + the list. Otherwise create one. + + :param List credentials: + A list of FakeResource objects faking credentials + :param Integer count: + The number of credentials to be faked + :return + An iterable Mock object with side_effect set to a list of faked + credentials + """ + if credentials is None: + credentials = FakeCredential.create_credentials(count) + + return mock.MagicMock(side_effect=credentials) + + +class FakeUser(object): + """Fake one or more user.""" + + @staticmethod + def create_one_user(attrs=None): + """Create a fake user. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, name, and so on + """ + + attrs = attrs or {} + + # set default attributes. + user_info = { + 'id': 'user-id-' + uuid.uuid4().hex, + 'name': 'user-name-' + uuid.uuid4().hex, + 'default_project_id': 'project-' + uuid.uuid4().hex, + 'email': 'user-email-' + uuid.uuid4().hex, + 'enabled': True, + 'domain_id': 'domain-id' + uuid.uuid4().hex, + 'links': 'links-' + uuid.uuid4().hex, + } + user_info.update(attrs) + + user = fakes.FakeResource(info=copy.deepcopy(user_info), + loaded=True) + return user diff --git a/openstackclient/tests/identity/v3/test_credential.py b/openstackclient/tests/identity/v3/test_credential.py index d8866124..b272087d 100644 --- a/openstackclient/tests/identity/v3/test_credential.py +++ b/openstackclient/tests/identity/v3/test_credential.py @@ -10,7 +10,10 @@ # License for the specific language governing permissions and limitations # under the License. -import json +import mock +from mock import call + +from osc_lib import exceptions from openstackclient.identity.v3 import credential from openstackclient.tests.identity.v3 import fakes as identity_fakes @@ -18,16 +21,6 @@ from openstackclient.tests import utils class TestCredential(identity_fakes.TestIdentityv3): - data = { - "access": "abc123", - "secret": "hidden-message", - "trust_id": None - } - - def __init__(self, *args): - super(TestCredential, self).__init__(*args) - - self.json_data = json.dumps(self.data) def setUp(self): super(TestCredential, self).setUp() @@ -45,15 +38,221 @@ class TestCredential(identity_fakes.TestIdentityv3): self.projects_mock.reset_mock() +class TestCredentialCreate(TestCredential): + + user = identity_fakes.FakeUser.create_one_user() + project = identity_fakes.FakeProject.create_one_project() + columns = ( + 'blob', + 'id', + 'project_id', + 'type', + 'user_id', + ) + + def setUp(self): + super(TestCredentialCreate, self).setUp() + + self.credential = identity_fakes.FakeCredential.create_one_credential( + attrs={'user_id': self.user.id, 'project_id': self.project.id}) + self.credentials_mock.create.return_value = self.credential + self.users_mock.get.return_value = self.user + self.projects_mock.get.return_value = self.project + self.data = ( + self.credential.blob, + self.credential.id, + self.credential.project_id, + self.credential.type, + self.credential.user_id, + ) + + self.cmd = credential.CreateCredential(self.app, None) + + def test_credential_create_no_options(self): + arglist = [ + self.credential.user_id, + self.credential.blob, + ] + verifylist = [ + ('user', self.credential.user_id), + ('data', self.credential.blob), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + kwargs = { + 'user': self.credential.user_id, + 'type': self.credential.type, + 'blob': self.credential.blob, + 'project': None, + } + self.credentials_mock.create.assert_called_once_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_credential_create_with_options(self): + arglist = [ + self.credential.user_id, + self.credential.blob, + '--type', self.credential.type, + '--project', self.credential.project_id, + ] + verifylist = [ + ('user', self.credential.user_id), + ('data', self.credential.blob), + ('type', self.credential.type), + ('project', self.credential.project_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + kwargs = { + 'user': self.credential.user_id, + 'type': self.credential.type, + 'blob': self.credential.blob, + 'project': self.credential.project_id, + } + self.credentials_mock.create.assert_called_once_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_credential_create_with_invalid_type(self): + arglist = [ + self.credential.user_id, + self.credential.blob, + '--type', 'invalid_type', + ] + verifylist = [ + ('user', self.credential.user_id), + ('data', self.credential.blob), + ('type', 'invalid_type'), + ] + self.assertRaises(utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + +class TestCredentialDelete(TestCredential): + + credentials = identity_fakes.FakeCredential.create_credentials(count=2) + + def setUp(self): + super(TestCredentialDelete, self).setUp() + + self.credentials_mock.delete.return_value = None + + # Get the command object to test + self.cmd = credential.DeleteCredential(self.app, None) + + def test_credential_delete(self): + arglist = [ + self.credentials[0].id, + ] + verifylist = [ + ('credential', [self.credentials[0].id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.credentials_mock.delete.assert_called_with( + self.credentials[0].id, + ) + self.assertIsNone(result) + + def test_credential_multi_delete(self): + arglist = [] + for c in self.credentials: + arglist.append(c.id) + verifylist = [ + ('credential', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + calls = [] + for c in self.credentials: + calls.append(call(c.id)) + self.credentials_mock.delete.assert_has_calls(calls) + self.assertIsNone(result) + + def test_credential_multi_delete_with_exception(self): + arglist = [ + self.credentials[0].id, + 'unexist_credential', + ] + verifylist = [ + ('credential', [self.credentials[0].id, 'unexist_credential']) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + delete_mock_result = [None, exceptions.CommandError] + self.credentials_mock.delete = ( + mock.MagicMock(side_effect=delete_mock_result) + ) + + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 credential failed to delete.', str(e)) + + self.credentials_mock.delete.assert_any_call(self.credentials[0].id) + self.credentials_mock.delete.assert_any_call('unexist_credential') + + +class TestCredentialList(TestCredential): + + credential = identity_fakes.FakeCredential.create_one_credential() + + columns = ('ID', 'Type', 'User ID', 'Data', 'Project ID') + data = (( + credential.id, + credential.type, + credential.user_id, + credential.blob, + credential.project_id, + ), ) + + def setUp(self): + super(TestCredentialList, self).setUp() + + self.credentials_mock.list.return_value = [self.credential] + + # Get the command object to test + self.cmd = credential.ListCredential(self.app, None) + + def test_domain_list_no_options(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.credentials_mock.list.assert_called_with() + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, tuple(data)) + + class TestCredentialSet(TestCredential): + credential = identity_fakes.FakeCredential.create_one_credential() + def setUp(self): super(TestCredentialSet, self).setUp() self.cmd = credential.SetCredential(self.app, None) def test_credential_set_no_options(self): arglist = [ - identity_fakes.credential_id, + self.credential.id, ] self.assertRaises(utils.ParserException, @@ -62,8 +261,8 @@ class TestCredentialSet(TestCredential): def test_credential_set_missing_user(self): arglist = [ '--type', 'ec2', - '--data', self.json_data, - identity_fakes.credential_id, + '--data', self.credential.blob, + self.credential.id, ] self.assertRaises(utils.ParserException, @@ -71,9 +270,9 @@ class TestCredentialSet(TestCredential): def test_credential_set_missing_type(self): arglist = [ - '--user', identity_fakes.user_name, - '--data', self.json_data, - identity_fakes.credential_id, + '--user', self.credential.user_id, + '--data', self.credential.blob, + self.credential.id, ] self.assertRaises(utils.ParserException, @@ -81,9 +280,9 @@ class TestCredentialSet(TestCredential): def test_credential_set_missing_data(self): arglist = [ - '--user', identity_fakes.user_name, + '--user', self.credential.user_id, '--type', 'ec2', - identity_fakes.credential_id, + self.credential.id, ] self.assertRaises(utils.ParserException, @@ -91,10 +290,10 @@ class TestCredentialSet(TestCredential): def test_credential_set_valid(self): arglist = [ - '--user', identity_fakes.user_name, + '--user', self.credential.user_id, '--type', 'ec2', - '--data', self.json_data, - identity_fakes.credential_id, + '--data', self.credential.blob, + self.credential.id, ] parsed_args = self.check_parser(self.cmd, arglist, []) @@ -104,14 +303,55 @@ class TestCredentialSet(TestCredential): def test_credential_set_valid_with_project(self): arglist = [ - '--user', identity_fakes.user_name, + '--user', self.credential.user_id, '--type', 'ec2', - '--data', self.json_data, - '--project', identity_fakes.project_name, - identity_fakes.credential_id, + '--data', self.credential.blob, + '--project', self.credential.project_id, + self.credential.id, ] parsed_args = self.check_parser(self.cmd, arglist, []) result = self.cmd.take_action(parsed_args) self.assertIsNone(result) + + +class TestCredentialShow(TestCredential): + + columns = ( + 'blob', + 'id', + 'project_id', + 'type', + 'user_id', + ) + + def setUp(self): + super(TestCredentialShow, self).setUp() + + self.credential = identity_fakes.FakeCredential.create_one_credential() + self.credentials_mock.get.return_value = self.credential + self.data = ( + self.credential.blob, + self.credential.id, + self.credential.project_id, + self.credential.type, + self.credential.user_id, + ) + + self.cmd = credential.ShowCredential(self.app, None) + + def test_credential_show(self): + arglist = [ + self.credential.id, + ] + verifylist = [ + ('credential', self.credential.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.credentials_mock.get.assert_called_once_with(self.credential.id) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) diff --git a/openstackclient/tests/identity/v3/test_identity_provider.py b/openstackclient/tests/identity/v3/test_identity_provider.py index 1ec61052..b5d784ef 100644 --- a/openstackclient/tests/identity/v3/test_identity_provider.py +++ b/openstackclient/tests/identity/v3/test_identity_provider.py @@ -13,7 +13,6 @@ # under the License. import copy - import mock from openstackclient.identity.v3 import identity_provider diff --git a/openstackclient/tests/identity/v3/test_mappings.py b/openstackclient/tests/identity/v3/test_mappings.py index 6aa1a6e5..09a383eb 100644 --- a/openstackclient/tests/identity/v3/test_mappings.py +++ b/openstackclient/tests/identity/v3/test_mappings.py @@ -13,7 +13,6 @@ # under the License. import copy - import mock from osc_lib import exceptions diff --git a/openstackclient/tests/identity/v3/test_service.py b/openstackclient/tests/identity/v3/test_service.py index a1f85adc..65d8ebd7 100644 --- a/openstackclient/tests/identity/v3/test_service.py +++ b/openstackclient/tests/identity/v3/test_service.py @@ -15,6 +15,9 @@ import copy +from keystoneclient import exceptions as identity_exc +from osc_lib import exceptions + from openstackclient.identity.v3 import service from openstackclient.tests import fakes from openstackclient.tests.identity.v3 import fakes as identity_fakes @@ -185,7 +188,8 @@ class TestServiceDelete(TestService): def setUp(self): super(TestServiceDelete, self).setUp() - self.services_mock.get.return_value = fakes.FakeResource( + self.services_mock.get.side_effect = identity_exc.NotFound(None) + self.services_mock.find.return_value = fakes.FakeResource( None, copy.deepcopy(identity_fakes.SERVICE), loaded=True, @@ -282,7 +286,8 @@ class TestServiceSet(TestService): def setUp(self): super(TestServiceSet, self).setUp() - self.services_mock.get.return_value = fakes.FakeResource( + self.services_mock.get.side_effect = identity_exc.NotFound(None) + self.services_mock.find.return_value = fakes.FakeResource( None, copy.deepcopy(identity_fakes.SERVICE), loaded=True, @@ -460,7 +465,8 @@ class TestServiceShow(TestService): def setUp(self): super(TestServiceShow, self).setUp() - self.services_mock.get.return_value = fakes.FakeResource( + self.services_mock.get.side_effect = identity_exc.NotFound(None) + self.services_mock.find.return_value = fakes.FakeResource( None, copy.deepcopy(identity_fakes.SERVICE), loaded=True, @@ -484,8 +490,8 @@ class TestServiceShow(TestService): columns, data = self.cmd.take_action(parsed_args) # ServiceManager.get(id) - self.services_mock.get.assert_called_with( - identity_fakes.service_name, + self.services_mock.find.assert_called_with( + name=identity_fakes.service_name ) collist = ('description', 'enabled', 'id', 'name', 'type') @@ -498,3 +504,21 @@ class TestServiceShow(TestService): identity_fakes.service_type, ) self.assertEqual(datalist, data) + + def test_service_show_nounique(self): + self.services_mock.find.side_effect = identity_exc.NoUniqueMatch(None) + arglist = [ + 'nounique_service', + ] + verifylist = [ + ('service', 'nounique_service'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual( + "Multiple service matches found for 'nounique_service'," + " use an ID to be more specific.", str(e)) diff --git a/openstackclient/tests/identity/v3/test_user.py b/openstackclient/tests/identity/v3/test_user.py index c4fb1521..7b99a4cb 100644 --- a/openstackclient/tests/identity/v3/test_user.py +++ b/openstackclient/tests/identity/v3/test_user.py @@ -15,7 +15,6 @@ import contextlib import copy - import mock from openstackclient.identity.v3 import user diff --git a/openstackclient/tests/image/v2/test_image.py b/openstackclient/tests/image/v2/test_image.py index c6b83bc5..2b116b4e 100644 --- a/openstackclient/tests/image/v2/test_image.py +++ b/openstackclient/tests/image/v2/test_image.py @@ -16,14 +16,12 @@ import copy import mock -import warlock - from glanceclient.v2 import schemas from osc_lib import exceptions from osc_lib import utils as common_utils +import warlock from openstackclient.image.v2 import image -from openstackclient.tests import fakes from openstackclient.tests.identity.v3 import fakes as identity_fakes from openstackclient.tests.image.v2 import fakes as image_fakes @@ -58,23 +56,18 @@ class TestImage(image_fakes.TestImagev2): class TestImageCreate(TestImage): + project = identity_fakes.FakeProject.create_one_project() + domain = identity_fakes.FakeDomain.create_one_domain() + def setUp(self): super(TestImageCreate, self).setUp() self.new_image = image_fakes.FakeImage.create_one_image() self.images_mock.create.return_value = self.new_image - self.project_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.PROJECT), - loaded=True, - ) + self.project_mock.get.return_value = self.project - self.domain_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.DOMAIN), - loaded=True, - ) + self.domain_mock.get.return_value = self.domain # This is the return value for utils.find_resource() self.images_mock.get.return_value = copy.deepcopy( @@ -145,7 +138,7 @@ class TestImageCreate(TestImage): ('--private' if self.new_image.visibility == 'private' else '--public'), '--project', self.new_image.owner, - '--project-domain', identity_fakes.domain_id, + '--project-domain', self.domain.id, self.new_image.name, ] verifylist = [ @@ -158,7 +151,7 @@ class TestImageCreate(TestImage): ('public', self.new_image.visibility == 'public'), ('private', self.new_image.visibility == 'private'), ('project', self.new_image.owner), - ('project_domain', identity_fakes.domain_id), + ('project_domain', self.domain.id), ('name', self.new_image.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -175,7 +168,7 @@ class TestImageCreate(TestImage): disk_format='fs', min_disk=10, min_ram=4, - owner=identity_fakes.project_id, + owner=self.project.id, protected=self.new_image.protected, visibility=self.new_image.visibility, ) @@ -346,10 +339,12 @@ class TestImageCreate(TestImage): class TestAddProjectToImage(TestImage): + project = identity_fakes.FakeProject.create_one_project() + domain = identity_fakes.FakeDomain.create_one_domain() _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} + 'member_id': project.id} ) columns = ( @@ -360,8 +355,8 @@ class TestAddProjectToImage(TestImage): datalist = ( _image.id, - identity_fakes.project_id, - new_member.status + new_member.member_id, + new_member.status, ) def setUp(self): @@ -372,27 +367,19 @@ class TestAddProjectToImage(TestImage): # Update the image_id in the MEMBER dict self.image_members_mock.create.return_value = self.new_member - self.project_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.PROJECT), - loaded=True, - ) - self.domain_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.DOMAIN), - loaded=True, - ) + self.project_mock.get.return_value = self.project + self.domain_mock.get.return_value = self.domain # Get the command object to test self.cmd = image.AddProjectToImage(self.app, None) def test_add_project_to_image_no_option(self): arglist = [ self._image.id, - identity_fakes.project_id, + self.project.id, ] verifylist = [ ('image', self._image.id), - ('project', identity_fakes.project_id), + ('project', self.project.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -402,7 +389,7 @@ class TestAddProjectToImage(TestImage): columns, data = self.cmd.take_action(parsed_args) self.image_members_mock.create.assert_called_with( self._image.id, - identity_fakes.project_id + self.project.id ) self.assertEqual(self.columns, columns) @@ -411,13 +398,13 @@ class TestAddProjectToImage(TestImage): def test_add_project_to_image_with_option(self): arglist = [ self._image.id, - identity_fakes.project_id, - '--project-domain', identity_fakes.domain_id, + self.project.id, + '--project-domain', self.domain.id, ] verifylist = [ ('image', self._image.id), - ('project', identity_fakes.project_id), - ('project_domain', identity_fakes.domain_id), + ('project', self.project.id), + ('project_domain', self.domain.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -427,7 +414,7 @@ class TestAddProjectToImage(TestImage): columns, data = self.cmd.take_action(parsed_args) self.image_members_mock.create.assert_called_with( self._image.id, - identity_fakes.project_id + self.project.id ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) @@ -755,6 +742,9 @@ class TestImageList(TestImage): class TestRemoveProjectImage(TestImage): + project = identity_fakes.FakeProject.create_one_project() + domain = identity_fakes.FakeDomain.create_one_domain() + def setUp(self): super(TestRemoveProjectImage, self).setUp() @@ -762,16 +752,8 @@ class TestRemoveProjectImage(TestImage): # This is the return value for utils.find_resource() self.images_mock.get.return_value = self._image - self.project_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.PROJECT), - loaded=True, - ) - self.domain_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.DOMAIN), - loaded=True, - ) + self.project_mock.get.return_value = self.project + self.domain_mock.get.return_value = self.domain self.image_members_mock.delete.return_value = None # Get the command object to test self.cmd = image.RemoveProjectImage(self.app, None) @@ -779,11 +761,11 @@ class TestRemoveProjectImage(TestImage): def test_remove_project_image_no_options(self): arglist = [ self._image.id, - identity_fakes.project_id, + self.project.id, ] verifylist = [ ('image', self._image.id), - ('project', identity_fakes.project_id), + ('project', self.project.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -791,20 +773,20 @@ class TestRemoveProjectImage(TestImage): self.image_members_mock.delete.assert_called_with( self._image.id, - identity_fakes.project_id, + self.project.id, ) self.assertIsNone(result) def test_remove_project_image_with_options(self): arglist = [ self._image.id, - identity_fakes.project_id, - '--project-domain', identity_fakes.domain_id, + self.project.id, + '--project-domain', self.domain.id, ] verifylist = [ ('image', self._image.id), - ('project', identity_fakes.project_id), - ('project_domain', identity_fakes.domain_id), + ('project', self.project.id), + ('project_domain', self.domain.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -812,13 +794,16 @@ class TestRemoveProjectImage(TestImage): self.image_members_mock.delete.assert_called_with( self._image.id, - identity_fakes.project_id, + self.project.id, ) self.assertIsNone(result) class TestImageSet(TestImage): + project = identity_fakes.FakeProject.create_one_project() + domain = identity_fakes.FakeDomain.create_one_domain() + def setUp(self): super(TestImageSet, self).setUp() # Set up the schema @@ -827,17 +812,9 @@ class TestImageSet(TestImage): schemas.SchemaBasedModel, ) - self.project_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.PROJECT), - loaded=True, - ) + self.project_mock.get.return_value = self.project - self.domain_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.DOMAIN), - loaded=True, - ) + self.domain_mock.get.return_value = self.domain self.images_mock.get.return_value = self.model(**image_fakes.IMAGE) self.images_mock.update.return_value = self.model(**image_fakes.IMAGE) @@ -864,8 +841,8 @@ class TestImageSet(TestImage): '--min-ram', '4', '--container-format', 'ovf', '--disk-format', 'vmdk', - '--project', identity_fakes.project_name, - '--project-domain', identity_fakes.domain_id, + '--project', self.project.name, + '--project-domain', self.domain.id, image_fakes.image_id, ] verifylist = [ @@ -874,8 +851,8 @@ class TestImageSet(TestImage): ('min_ram', 4), ('container_format', 'ovf'), ('disk_format', 'vmdk'), - ('project', identity_fakes.project_name), - ('project_domain', identity_fakes.domain_id), + ('project', self.project.name), + ('project_domain', self.domain.id), ('image', image_fakes.image_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -884,7 +861,7 @@ class TestImageSet(TestImage): kwargs = { 'name': 'new-name', - 'owner': identity_fakes.project_id, + 'owner': self.project.id, 'min_disk': 2, 'min_ram': 4, 'container_format': 'ovf', diff --git a/openstackclient/tests/network/v2/test_address_scope.py b/openstackclient/tests/network/v2/test_address_scope.py index 502516f9..342cf49b 100644 --- a/openstackclient/tests/network/v2/test_address_scope.py +++ b/openstackclient/tests/network/v2/test_address_scope.py @@ -12,8 +12,8 @@ # import mock - from mock import call + from osc_lib import exceptions from openstackclient.network.v2 import address_scope diff --git a/openstackclient/tests/object/v1/fakes.py b/openstackclient/tests/object/v1/fakes.py index b9e86db7..6c367111 100644 --- a/openstackclient/tests/object/v1/fakes.py +++ b/openstackclient/tests/object/v1/fakes.py @@ -14,6 +14,7 @@ # from keystoneauth1 import session + from openstackclient.api import object_store_v1 as object_store from openstackclient.tests import utils diff --git a/openstackclient/tests/utils.py b/openstackclient/tests/utils.py index 8dead718..f3ab5ea6 100644 --- a/openstackclient/tests/utils.py +++ b/openstackclient/tests/utils.py @@ -14,9 +14,8 @@ # under the License. # -import os - import fixtures +import os import testtools from openstackclient.tests import fakes diff --git a/openstackclient/tests/volume/v1/test_volume.py b/openstackclient/tests/volume/v1/test_volume.py index 380bc632..e41c2a72 100644 --- a/openstackclient/tests/volume/v1/test_volume.py +++ b/openstackclient/tests/volume/v1/test_volume.py @@ -50,6 +50,9 @@ class TestVolume(volume_fakes.TestVolumev1): class TestVolumeCreate(TestVolume): + project = identity_fakes.FakeProject.create_one_project() + user = identity_fakes.FakeUser.create_one_user() + columns = ( 'attach_status', 'availability_zone', @@ -168,28 +171,20 @@ class TestVolumeCreate(TestVolume): def test_volume_create_user_project_id(self): # Return a project - self.projects_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.PROJECT), - loaded=True, - ) + self.projects_mock.get.return_value = self.project # Return a user - self.users_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.USER), - loaded=True, - ) + self.users_mock.get.return_value = self.user arglist = [ '--size', str(volume_fakes.volume_size), - '--project', identity_fakes.project_id, - '--user', identity_fakes.user_id, + '--project', self.project.id, + '--user', self.user.id, volume_fakes.volume_name, ] verifylist = [ ('size', volume_fakes.volume_size), - ('project', identity_fakes.project_id), - ('user', identity_fakes.user_id), + ('project', self.project.id), + ('user', self.user.id), ('name', volume_fakes.volume_name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -211,8 +206,8 @@ class TestVolumeCreate(TestVolume): volume_fakes.volume_name, None, None, - identity_fakes.user_id, - identity_fakes.project_id, + self.user.id, + self.project.id, None, None, None, @@ -223,28 +218,20 @@ class TestVolumeCreate(TestVolume): def test_volume_create_user_project_name(self): # Return a project - self.projects_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.PROJECT), - loaded=True, - ) + self.projects_mock.get.return_value = self.project # Return a user - self.users_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.USER), - loaded=True, - ) + self.users_mock.get.return_value = self.user arglist = [ '--size', str(volume_fakes.volume_size), - '--project', identity_fakes.project_name, - '--user', identity_fakes.user_name, + '--project', self.project.name, + '--user', self.user.name, volume_fakes.volume_name, ] verifylist = [ ('size', volume_fakes.volume_size), - ('project', identity_fakes.project_name), - ('user', identity_fakes.user_name), + ('project', self.project.name), + ('user', self.user.name), ('name', volume_fakes.volume_name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -266,8 +253,8 @@ class TestVolumeCreate(TestVolume): volume_fakes.volume_name, None, None, - identity_fakes.user_id, - identity_fakes.project_id, + self.user.id, + self.project.id, None, None, None, diff --git a/openstackclient/tests/volume/v2/test_snapshot.py b/openstackclient/tests/volume/v2/test_snapshot.py index 04e0285e..3eb740ba 100644 --- a/openstackclient/tests/volume/v2/test_snapshot.py +++ b/openstackclient/tests/volume/v2/test_snapshot.py @@ -12,6 +12,7 @@ # under the License. # +import argparse import mock from mock import call @@ -260,16 +261,33 @@ class TestSnapshotList(TestSnapshot): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) + + self.snapshots_mock.list.assert_called_once_with( + limit=None, marker=None, search_opts={'all_tenants': False}) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) def test_snapshot_list_with_options(self): - arglist = ["--long"] - verifylist = [("long", True), ('all_projects', False)] + arglist = [ + "--long", + "--limit", "2", + "--marker", self.snapshots[0].id, + ] + verifylist = [ + ("long", True), + ("limit", 2), + ("marker", self.snapshots[0].id), + ('all_projects', False), + ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) + self.snapshots_mock.list.assert_called_once_with( + limit=2, + marker=self.snapshots[0].id, + search_opts={'all_tenants': False} + ) self.assertEqual(self.columns_long, columns) self.assertEqual(self.data_long, list(data)) @@ -285,9 +303,21 @@ class TestSnapshotList(TestSnapshot): columns, data = self.cmd.take_action(parsed_args) + self.snapshots_mock.list.assert_called_once_with( + limit=None, marker=None, search_opts={'all_tenants': True}) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) + def test_snapshot_list_negative_limit(self): + arglist = [ + "--limit", "-2", + ] + verifylist = [ + ("limit", -2), + ] + self.assertRaises(argparse.ArgumentTypeError, self.check_parser, + self.cmd, arglist, verifylist) + class TestSnapshotSet(TestSnapshot): diff --git a/openstackclient/tests/volume/v2/test_type.py b/openstackclient/tests/volume/v2/test_type.py index e148bba4..b0316aef 100644 --- a/openstackclient/tests/volume/v2/test_type.py +++ b/openstackclient/tests/volume/v2/test_type.py @@ -12,13 +12,11 @@ # under the License. # -import copy import mock from osc_lib import exceptions from osc_lib import utils -from openstackclient.tests import fakes from openstackclient.tests.identity.v3 import fakes as identity_fakes from openstackclient.tests import utils as tests_utils from openstackclient.tests.volume.v2 import fakes as volume_fakes @@ -199,26 +197,54 @@ class TestTypeList(TestType): def test_type_list_without_options(self): arglist = [] verifylist = [ - ("long", False) + ("long", False), + ("private", False), + ("public", False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) + self.types_mock.list.assert_called_once_with(is_public=None) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) def test_type_list_with_options(self): - arglist = ["--long"] - verifylist = [("long", True)] + arglist = [ + "--long", + "--public", + ] + verifylist = [ + ("long", True), + ("private", False), + ("public", True), + ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) + self.types_mock.list.assert_called_once_with(is_public=True) self.assertEqual(self.columns_long, columns) self.assertEqual(self.data_long, list(data)) + def test_type_list_with_private_option(self): + arglist = [ + "--private", + ] + verifylist = [ + ("long", False), + ("private", True), + ("public", False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.types_mock.list.assert_called_once_with(is_public=False) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + class TestTypeSet(TestType): + project = identity_fakes.FakeProject.create_one_project() volume_type = volume_fakes.FakeType.create_one_type( methods={'set_keys': None}) @@ -228,11 +254,7 @@ class TestTypeSet(TestType): self.types_mock.get.return_value = self.volume_type # Return a project - 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 the command object to test self.cmd = volume_type.SetVolumeType(self.app, None) @@ -339,11 +361,11 @@ class TestTypeSet(TestType): def test_type_set_project_access(self): arglist = [ - '--project', identity_fakes.project_id, + '--project', self.project.id, self.volume_type.id, ] verifylist = [ - ('project', identity_fakes.project_id), + ('project', self.project.id), ('volume_type', self.volume_type.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -353,7 +375,7 @@ class TestTypeSet(TestType): self.types_access_mock.add_project_access.assert_called_with( self.volume_type.id, - identity_fakes.project_id, + self.project.id, ) @@ -469,6 +491,7 @@ class TestTypeShow(TestType): class TestTypeUnset(TestType): + project = identity_fakes.FakeProject.create_one_project() volume_type = volume_fakes.FakeType.create_one_type( methods={'unset_keys': None}) @@ -478,11 +501,7 @@ class TestTypeUnset(TestType): self.types_mock.get.return_value = self.volume_type # Return a project - 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 the command object to test self.cmd = volume_type.UnsetVolumeType(self.app, None) @@ -507,11 +526,11 @@ class TestTypeUnset(TestType): def test_type_unset_project_access(self): arglist = [ - '--project', identity_fakes.project_id, + '--project', self.project.id, self.volume_type.id, ] verifylist = [ - ('project', identity_fakes.project_id), + ('project', self.project.id), ('volume_type', self.volume_type.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -521,7 +540,7 @@ class TestTypeUnset(TestType): self.types_access_mock.remove_project_access.assert_called_with( self.volume_type.id, - identity_fakes.project_id, + self.project.id, ) def test_type_unset_not_called_without_project_argument(self): diff --git a/openstackclient/tests/volume/v2/test_volume.py b/openstackclient/tests/volume/v2/test_volume.py index db65c3bd..6f552ad6 100644 --- a/openstackclient/tests/volume/v2/test_volume.py +++ b/openstackclient/tests/volume/v2/test_volume.py @@ -12,14 +12,12 @@ # under the License. # -import copy import mock from mock import call from osc_lib import exceptions from osc_lib import utils -from openstackclient.tests import fakes from openstackclient.tests.identity.v3 import fakes as identity_fakes from openstackclient.tests.image.v2 import fakes as image_fakes from openstackclient.tests.volume.v2 import fakes as volume_fakes @@ -57,6 +55,9 @@ class TestVolume(volume_fakes.TestVolume): class TestVolumeCreate(TestVolume): + project = identity_fakes.FakeProject.create_one_project() + user = identity_fakes.FakeUser.create_one_user() + columns = ( 'attachments', 'availability_zone', @@ -168,28 +169,20 @@ class TestVolumeCreate(TestVolume): def test_volume_create_user_project_id(self): # Return a project - self.projects_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.PROJECT), - loaded=True, - ) + self.projects_mock.get.return_value = self.project # Return a user - self.users_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.USER), - loaded=True, - ) + self.users_mock.get.return_value = self.user arglist = [ '--size', str(self.new_volume.size), - '--project', identity_fakes.project_id, - '--user', identity_fakes.user_id, + '--project', self.project.id, + '--user', self.user.id, self.new_volume.name, ] verifylist = [ ('size', self.new_volume.size), - ('project', identity_fakes.project_id), - ('user', identity_fakes.user_id), + ('project', self.project.id), + ('user', self.user.id), ('name', self.new_volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -205,8 +198,8 @@ class TestVolumeCreate(TestVolume): name=self.new_volume.name, description=None, volume_type=None, - user_id=identity_fakes.user_id, - project_id=identity_fakes.project_id, + user_id=self.user.id, + project_id=self.project.id, availability_zone=None, metadata=None, imageRef=None, @@ -218,28 +211,20 @@ class TestVolumeCreate(TestVolume): def test_volume_create_user_project_name(self): # Return a project - self.projects_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.PROJECT), - loaded=True, - ) + self.projects_mock.get.return_value = self.project # Return a user - self.users_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.USER), - loaded=True, - ) + self.users_mock.get.return_value = self.user arglist = [ '--size', str(self.new_volume.size), - '--project', identity_fakes.project_name, - '--user', identity_fakes.user_name, + '--project', self.project.name, + '--user', self.user.name, self.new_volume.name, ] verifylist = [ ('size', self.new_volume.size), - ('project', identity_fakes.project_name), - ('user', identity_fakes.user_name), + ('project', self.project.name), + ('user', self.user.name), ('name', self.new_volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -255,8 +240,8 @@ class TestVolumeCreate(TestVolume): name=self.new_volume.name, description=None, volume_type=None, - user_id=identity_fakes.user_id, - project_id=identity_fakes.project_id, + user_id=self.user.id, + project_id=self.project.id, availability_zone=None, metadata=None, imageRef=None, @@ -435,13 +420,16 @@ class TestVolumeDelete(TestVolume): volumes[0].id ] verifylist = [ - ("volumes", [volumes[0].id]) + ("force", False), + ("purge", False), + ("volumes", [volumes[0].id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.volumes_mock.delete.assert_called_with(volumes[0].id) + self.volumes_mock.delete.assert_called_once_with( + volumes[0].id, cascade=False) self.assertIsNone(result) def test_volume_delete_multi_volumes(self): @@ -449,13 +437,15 @@ class TestVolumeDelete(TestVolume): arglist = [v.id for v in volumes] verifylist = [ + ('force', False), + ('purge', False), ('volumes', arglist), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - calls = [call(v.id) for v in volumes] + calls = [call(v.id, cascade=False) for v in volumes] self.volumes_mock.delete.assert_has_calls(calls) self.assertIsNone(result) @@ -467,6 +457,8 @@ class TestVolumeDelete(TestVolume): 'unexist_volume', ] verifylist = [ + ('force', False), + ('purge', False), ('volumes', arglist), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -486,12 +478,53 @@ class TestVolumeDelete(TestVolume): self.assertEqual(2, find_mock.call_count) self.volumes_mock.delete.assert_called_once_with( - volumes[0].id - ) + volumes[0].id, cascade=False) + + def test_volume_delete_with_purge(self): + volumes = self.setup_volumes_mock(count=1) + + arglist = [ + '--purge', + volumes[0].id, + ] + verifylist = [ + ('force', False), + ('purge', True), + ('volumes', [volumes[0].id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.volumes_mock.delete.assert_called_once_with( + volumes[0].id, cascade=True) + self.assertIsNone(result) + + def test_volume_delete_with_force(self): + volumes = self.setup_volumes_mock(count=1) + + arglist = [ + '--force', + volumes[0].id, + ] + verifylist = [ + ('force', True), + ('purge', False), + ('volumes', [volumes[0].id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.volumes_mock.force_delete.assert_called_once_with(volumes[0].id) + self.assertIsNone(result) class TestVolumeList(TestVolume): + project = identity_fakes.FakeProject.create_one_project() + user = identity_fakes.FakeUser.create_one_user() + columns = [ 'ID', 'Display Name', @@ -506,21 +539,9 @@ class TestVolumeList(TestVolume): self.mock_volume = volume_fakes.FakeVolume.create_one_volume() self.volumes_mock.list.return_value = [self.mock_volume] - self.users_mock.get.return_value = [ - fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.USER), - loaded=True, - ), - ] + self.users_mock.get.return_value = self.user - 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 the command object to test self.cmd = volume.ListVolume(self.app, None) @@ -553,10 +574,10 @@ class TestVolumeList(TestVolume): def test_volume_list_project(self): arglist = [ - '--project', identity_fakes.project_name, + '--project', self.project.name, ] verifylist = [ - ('project', identity_fakes.project_name), + ('project', self.project.name), ('long', False), ('all_projects', False), ('status', None), @@ -581,12 +602,12 @@ class TestVolumeList(TestVolume): def test_volume_list_project_domain(self): arglist = [ - '--project', identity_fakes.project_name, - '--project-domain', identity_fakes.domain_name, + '--project', self.project.name, + '--project-domain', self.project.domain_id, ] verifylist = [ - ('project', identity_fakes.project_name), - ('project_domain', identity_fakes.domain_name), + ('project', self.project.name), + ('project_domain', self.project.domain_id), ('long', False), ('all_projects', False), ('status', None), @@ -611,10 +632,10 @@ class TestVolumeList(TestVolume): def test_volume_list_user(self): arglist = [ - '--user', identity_fakes.user_name, + '--user', self.user.name, ] verifylist = [ - ('user', identity_fakes.user_name), + ('user', self.user.name), ('long', False), ('all_projects', False), ('status', None), @@ -638,12 +659,12 @@ class TestVolumeList(TestVolume): def test_volume_list_user_domain(self): arglist = [ - '--user', identity_fakes.user_name, - '--user-domain', identity_fakes.domain_name, + '--user', self.user.name, + '--user-domain', self.user.domain_id, ] verifylist = [ - ('user', identity_fakes.user_name), - ('user_domain', identity_fakes.domain_name), + ('user', self.user.name), + ('user_domain', self.user.domain_id), ('long', False), ('all_projects', False), ('status', None), diff --git a/openstackclient/volume/client.py b/openstackclient/volume/client.py index ea0c441c..11f20673 100644 --- a/openstackclient/volume/client.py +++ b/openstackclient/volume/client.py @@ -19,6 +19,7 @@ from osc_lib import utils from openstackclient.i18n import _ + LOG = logging.getLogger(__name__) DEFAULT_API_VERSION = '2' diff --git a/openstackclient/volume/v2/snapshot.py b/openstackclient/volume/v2/snapshot.py index ba692074..8304a5eb 100644 --- a/openstackclient/volume/v2/snapshot.py +++ b/openstackclient/volume/v2/snapshot.py @@ -134,6 +134,18 @@ class ListSnapshot(command.Lister): default=False, help=_('List additional fields in output'), ) + parser.add_argument( + '--marker', + metavar='<marker>', + help=_('The last snapshot ID of the previous page'), + ) + parser.add_argument( + '--limit', + type=int, + action=parseractions.NonNegativeAction, + metavar='<limit>', + help=_('Maximum number of snapshots to display'), + ) return parser def take_action(self, parsed_args): @@ -174,7 +186,10 @@ class ListSnapshot(command.Lister): } data = self.app.client_manager.volume.volume_snapshots.list( - search_opts=search_opts) + search_opts=search_opts, + marker=parsed_args.marker, + limit=parsed_args.limit, + ) return (column_headers, (utils.get_item_properties( s, columns, diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index 85f267ef..6f055922 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -166,13 +166,19 @@ class DeleteVolume(command.Command): nargs="+", help=_("Volume(s) to delete (name or ID)") ) - parser.add_argument( + group = parser.add_mutually_exclusive_group() + group.add_argument( "--force", action="store_true", - default=False, help=_("Attempt forced removal of volume(s), regardless of state " "(defaults to False)") ) + group.add_argument( + "--purge", + action="store_true", + help=_("Remove any snapshots along with volume(s) " + "(defaults to False)") + ) return parser def take_action(self, parsed_args): @@ -186,12 +192,13 @@ class DeleteVolume(command.Command): if parsed_args.force: volume_client.volumes.force_delete(volume_obj.id) else: - volume_client.volumes.delete(volume_obj.id) + volume_client.volumes.delete(volume_obj.id, + cascade=parsed_args.purge) except Exception as e: result += 1 LOG.error(_("Failed to delete volume with " - "name or ID '%(volume)s': %(e)s") - % {'volume': i, 'e': e}) + "name or ID '%(volume)s': %(e)s"), + {'volume': i, 'e': e}) if result > 0: total = len(parsed_args.volumes) diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py index 62d619d0..e42fffe0 100644 --- a/openstackclient/volume/v2/volume_type.py +++ b/openstackclient/volume/v2/volume_type.py @@ -160,6 +160,17 @@ class ListVolumeType(command.Lister): action='store_true', default=False, help=_('List additional fields in output')) + public_group = parser.add_mutually_exclusive_group() + public_group.add_argument( + "--public", + action="store_true", + help=_("List only public types") + ) + public_group.add_argument( + "--private", + action="store_true", + help=_("List only private types (admin only)") + ) return parser def take_action(self, parsed_args): @@ -169,7 +180,14 @@ class ListVolumeType(command.Lister): else: columns = ['ID', 'Name'] column_headers = columns - data = self.app.client_manager.volume.volume_types.list() + + is_public = None + if parsed_args.public: + is_public = True + if parsed_args.private: + is_public = False + data = self.app.client_manager.volume.volume_types.list( + is_public=is_public) return (column_headers, (utils.get_item_properties( s, columns, |
