diff options
Diffstat (limited to 'openstackclient')
19 files changed, 633 insertions, 87 deletions
diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py index eb79cd2f..1f239b67 100644 --- a/openstackclient/image/v1/image.py +++ b/openstackclient/image/v1/image.py @@ -39,7 +39,7 @@ from openstackclient.i18n import _ DEFAULT_CONTAINER_FORMAT = 'bare' DEFAULT_DISK_FORMAT = 'raw' DISK_CHOICES = ["ami", "ari", "aki", "vhd", "vmdk", "raw", "qcow2", "vhdx", - "vdi", "iso"] + "vdi", "iso", "ploop"] LOG = logging.getLogger(__name__) diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index 1d167605..55eb7eb1 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -33,7 +33,7 @@ from openstackclient.identity import common DEFAULT_CONTAINER_FORMAT = 'bare' DEFAULT_DISK_FORMAT = 'raw' DISK_CHOICES = ["ami", "ari", "aki", "vhd", "vmdk", "raw", "qcow2", "vhdx", - "vdi", "iso"] + "vdi", "iso", "ploop"] LOG = logging.getLogger(__name__) diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py index 1c06c462..26cd02c6 100644 --- a/openstackclient/network/v2/network.py +++ b/openstackclient/network/v2/network.py @@ -19,6 +19,7 @@ from osc_lib import utils from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common +from openstackclient.network import sdk_utils def _format_admin_state(item): @@ -31,13 +32,33 @@ def _format_router_external(item): _formatters = { 'subnets': utils.format_list, + 'subnet_ids': utils.format_list, 'admin_state_up': _format_admin_state, + 'is_admin_state_up': _format_admin_state, 'router:external': _format_router_external, + 'is_router_external': _format_router_external, 'availability_zones': utils.format_list, 'availability_zone_hints': utils.format_list, } +def _get_network_columns(item): + column_map = { + 'subnet_ids': 'subnets', + 'is_admin_state_up': 'admin_state_up', + 'is_router_external': 'router:external', + 'is_port_security_enabled': 'port_security_enabled', + 'provider_network_type': 'provider:network_type', + 'provider_physical_network': 'provider:physical_network', + 'provider_segmentation_id': 'provider:segmentation_id', + 'is_shared': 'shared', + 'ipv4_address_scope_id': 'ipv4_address_scope', + 'ipv6_address_scope_id': 'ipv6_address_scope', + 'tenant_id': 'project_id', + } + return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) + + def _get_columns(item): columns = list(item.keys()) if 'tenant_id' in columns: @@ -162,6 +183,8 @@ def _get_attrs_compute(client_manager, parsed_args): return attrs +# TODO(sindhu): Use the SDK resource mapped attribute names once the +# OSC minimum requirements include SDK 1.0. class CreateNetwork(common.NetworkAndComputeShowOne): _description = _("Create new network") @@ -275,9 +298,9 @@ class CreateNetwork(common.NetworkAndComputeShowOne): def take_action_network(self, client, parsed_args): attrs = _get_attrs(self.app.client_manager, parsed_args) obj = client.create_network(**attrs) - columns = _get_columns(obj) + display_columns, columns = _get_network_columns(obj) data = utils.get_item_properties(obj, columns, formatters=_formatters) - return (columns, data) + return (display_columns, data) def take_action_compute(self, client, parsed_args): attrs = _get_attrs_compute(self.app.client_manager, parsed_args) @@ -313,6 +336,8 @@ class DeleteNetwork(common.NetworkAndComputeDelete): client.networks.delete(network.id) +# TODO(sindhu): Use the SDK resource mapped attribute names once the +# OSC minimum requirements include SDK 1.0. class ListNetwork(common.NetworkAndComputeLister): _description = _("List networks") @@ -405,12 +430,12 @@ class ListNetwork(common.NetworkAndComputeLister): 'id', 'name', 'status', - 'tenant_id', - 'admin_state_up', - 'shared', - 'subnets', + 'project_id', + 'is_admin_state_up', + 'is_shared', + 'subnet_ids', 'provider_network_type', - 'router:external', + 'is_router_external', 'availability_zones', ) column_headers = ( @@ -429,7 +454,7 @@ class ListNetwork(common.NetworkAndComputeLister): columns = ( 'id', 'name', - 'subnets' + 'subnet_ids' ) column_headers = ( 'ID', @@ -441,16 +466,20 @@ class ListNetwork(common.NetworkAndComputeLister): if parsed_args.external: args['router:external'] = True + args['is_router_external'] = True elif parsed_args.internal: args['router:external'] = False + args['is_router_external'] = False if parsed_args.name is not None: args['name'] = parsed_args.name if parsed_args.enable: args['admin_state_up'] = True + args['is_admin_state_up'] = True elif parsed_args.disable: args['admin_state_up'] = False + args['is_admin_state_up'] = False if parsed_args.project: project = identity_common.find_project( @@ -459,21 +488,27 @@ class ListNetwork(common.NetworkAndComputeLister): parsed_args.project_domain, ) args['tenant_id'] = project.id + args['project_id'] = project.id if parsed_args.share: args['shared'] = True + args['is_shared'] = True elif parsed_args.no_share: args['shared'] = False + args['is_shared'] = False if parsed_args.status: args['status'] = parsed_args.status if parsed_args.provider_network_type: args['provider:network_type'] = parsed_args.provider_network_type + args['provider_network_type'] = parsed_args.provider_network_type if parsed_args.physical_network: args['provider:physical_network'] = parsed_args.physical_network + args['provider_physical_network'] = parsed_args.physical_network if parsed_args.segmentation_id: args['provider:segmentation_id'] = parsed_args.segmentation_id + args['provider_segmentation_id'] = parsed_args.segmentation_id data = client.networks(**args) @@ -504,6 +539,8 @@ class ListNetwork(common.NetworkAndComputeLister): ) for s in data)) +# TODO(sindhu): Use the SDK resource mapped attribute names once the +# OSC minimum requirements include SDK 1.0. class SetNetwork(command.Command): _description = _("Set network properties") @@ -619,9 +656,9 @@ class ShowNetwork(common.NetworkAndComputeShowOne): def take_action_network(self, client, parsed_args): obj = client.find_network(parsed_args.network, ignore_missing=False) - columns = _get_columns(obj) + display_columns, columns = _get_network_columns(obj) data = utils.get_item_properties(obj, columns, formatters=_formatters) - return (columns, data) + return (display_columns, data) def take_action_compute(self, client, parsed_args): obj = utils.find_resource( diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index bce3e2d3..4525da18 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -25,6 +25,7 @@ from osc_lib import utils from openstackclient.i18n import _ from openstackclient.identity import common as identity_common +from openstackclient.network import sdk_utils LOG = logging.getLogger(__name__) @@ -36,34 +37,33 @@ def _format_admin_state(state): _formatters = { 'admin_state_up': _format_admin_state, + 'is_admin_state_up': _format_admin_state, 'allowed_address_pairs': utils.format_list_of_dicts, 'binding_profile': utils.format_dict, 'binding_vif_details': utils.format_dict, + 'binding:profile': utils.format_dict, + 'binding:vif_details': utils.format_dict, 'dns_assignment': utils.format_list_of_dicts, 'extra_dhcp_opts': utils.format_list_of_dicts, 'fixed_ips': utils.format_list_of_dicts, + 'security_group_ids': utils.format_list, 'security_groups': utils.format_list, } def _get_columns(item): - columns = list(item.keys()) - if 'tenant_id' in columns: - columns.remove('tenant_id') - if 'project_id' not in columns: - columns.append('project_id') - binding_columns = [ - 'binding:host_id', - 'binding:profile', - 'binding:vif_details', - 'binding:vif_type', - 'binding:vnic_type', - ] - for binding_column in binding_columns: - if binding_column in columns: - columns.remove(binding_column) - columns.append(binding_column.replace('binding:', 'binding_', 1)) - return tuple(sorted(columns)) + column_map = { + 'binding:host_id': 'binding_host_id', + 'binding:profile': 'binding_profile', + 'binding:vif_details': 'binding_vif_details', + 'binding:vif_type': 'binding_vif_type', + 'binding:vnic_type': 'binding_vnic_type', + 'is_admin_state_up': 'admin_state_up', + 'is_port_security_enabled': 'port_security_enabled', + 'security_group_ids': 'security_groups', + 'tenant_id': 'project_id', + } + return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) class JSONKeyValueAction(argparse.Action): @@ -244,6 +244,8 @@ def _add_updatable_args(parser): ) +# TODO(abhiraut): Use the SDK resource mapped attribute names once the +# OSC minimum requirements include SDK 1.0. class CreatePort(command.ShowOne): _description = _("Create a new port") @@ -349,10 +351,10 @@ class CreatePort(command.ShowOne): attrs['security_groups'] = [] obj = client.create_port(**attrs) - columns = _get_columns(obj) + display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns, formatters=_formatters) - return (columns, data) + return (display_columns, data) class DeletePort(command.Command): @@ -389,6 +391,8 @@ class DeletePort(command.Command): raise exceptions.CommandError(msg) +# TODO(abhiraut): Use only the SDK resource mapped attribute names once the +# OSC minimum requirements include SDK 1.0. class ListPort(command.Lister): _description = _("List ports") @@ -451,7 +455,7 @@ class ListPort(command.Lister): filters = {} if parsed_args.long: - columns += ('security_groups', 'device_owner',) + columns += ('security_group_ids', 'device_owner',) column_headers += ('Security Groups', 'Device Owner',) if parsed_args.device_owner is not None: filters['device_owner'] = parsed_args.device_owner @@ -479,6 +483,8 @@ class ListPort(command.Lister): ) for s in data)) +# TODO(abhiraut): Use the SDK resource mapped attribute names once the +# OSC minimum requirements include SDK 1.0. class SetPort(command.Command): _description = _("Set port properties") @@ -621,11 +627,13 @@ class ShowPort(command.ShowOne): def take_action(self, parsed_args): client = self.app.client_manager.network obj = client.find_port(parsed_args.port, ignore_missing=False) - columns = _get_columns(obj) + display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns, formatters=_formatters) - return (columns, data) + return (display_columns, data) +# TODO(abhiraut): Use the SDK resource mapped attribute names once the +# OSC minimum requirements include SDK 1.0. class UnsetPort(command.Command): _description = _("Unset port properties") @@ -666,7 +674,7 @@ class UnsetPort(command.Command): def take_action(self, parsed_args): client = self.app.client_manager.network obj = client.find_port(parsed_args.port, ignore_missing=False) - # SDK ignores update() if it recieves a modified obj and attrs + # SDK ignores update() if it receives a modified obj and attrs # To handle the same tmp_obj is created in all take_action of # Unset* classes tmp_fixed_ips = copy.deepcopy(obj.fixed_ips) diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index cdd634d0..61a005e6 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -52,6 +52,7 @@ def _format_routes(routes): _formatters = { 'admin_state_up': _format_admin_state, + 'is_admin_state_up': _format_admin_state, 'external_gateway_info': _format_external_gateway_info, 'availability_zones': utils.format_list, 'availability_zone_hints': utils.format_list, @@ -62,6 +63,9 @@ _formatters = { def _get_columns(item): column_map = { 'tenant_id': 'project_id', + 'is_ha': 'ha', + 'is_distributed': 'distributed', + 'is_admin_state_up': 'admin_state_up', } return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) @@ -150,6 +154,8 @@ class AddSubnetToRouter(command.Command): subnet_id=subnet.id) +# TODO(yanxing'an): Use the SDK resource mapped attribute names once the +# OSC minimum requirements include SDK 1.0. class CreateRouter(command.ShowOne): _description = _("Create a new router") @@ -255,6 +261,8 @@ class DeleteRouter(command.Command): raise exceptions.CommandError(msg) +# TODO(yanxing'an): Use the SDK resource mapped attribute names once the +# OSC minimum requirements include SDK 1.0. class ListRouter(command.Lister): _description = _("List routers") @@ -297,10 +305,10 @@ class ListRouter(command.Lister): 'id', 'name', 'status', - 'admin_state_up', - 'distributed', - 'ha', - 'tenant_id', + 'is_admin_state_up', + 'is_distributed', + 'is_ha', + 'project_id', ) column_headers = ( 'ID', @@ -319,8 +327,10 @@ class ListRouter(command.Lister): if parsed_args.enable: args['admin_state_up'] = True + args['is_admin_state_up'] = True elif parsed_args.disable: args['admin_state_up'] = False + args['is_admin_state_up'] = False if parsed_args.project: project_id = identity_common.find_project( @@ -329,6 +339,7 @@ class ListRouter(command.Lister): parsed_args.project_domain, ).id args['tenant_id'] = project_id + args['project_id'] = project_id if parsed_args.long: columns = columns + ( 'routes', @@ -407,6 +418,8 @@ class RemoveSubnetFromRouter(command.Command): subnet_id=subnet.id) +# TODO(yanxing'an): Use the SDK resource mapped attribute names once the +# OSC minimum requirements include SDK 1.0. class SetRouter(command.Command): _description = _("Set router properties") diff --git a/openstackclient/tests/functional/volume/v2/test_volume_type.py b/openstackclient/tests/functional/volume/v2/test_volume_type.py index d8bd3a96..b4df5b2d 100644 --- a/openstackclient/tests/functional/volume/v2/test_volume_type.py +++ b/openstackclient/tests/functional/volume/v2/test_volume_type.py @@ -42,6 +42,11 @@ class VolumeTypeTests(common.BaseVolumeTests): raw_output = self.openstack('volume type list' + opts) self.assertIn(self.NAME, raw_output) + def test_volume_type_list_default(self): + opts = self.get_opts(self.HEADERS) + raw_output = self.openstack('volume type list --default' + opts) + self.assertEqual("lvmdriver-1\n", raw_output) + def test_volume_type_show(self): opts = self.get_opts(self.FIELDS) raw_output = self.openstack('volume type show ' + self.NAME + opts) diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py index 97d07076..84f145fb 100644 --- a/openstackclient/tests/unit/network/v2/fakes.py +++ b/openstackclient/tests/unit/network/v2/fakes.py @@ -297,15 +297,17 @@ class FakeNetwork(object): 'admin_state_up': True, 'shared': False, 'subnets': ['a', 'b'], - 'provider_network_type': 'vlan', - 'provider_physical_network': 'physnet1', - 'provider_segmentation_id': "400", + 'provider:network_type': 'vlan', + 'provider:physical_network': 'physnet1', + 'provider:segmentation_id': "400", 'router:external': True, 'availability_zones': [], 'availability_zone_hints': [], 'is_default': False, 'port_security_enabled': True, 'qos_policy_id': 'qos-policy-id-' + uuid.uuid4().hex, + 'ipv4_address_scope': 'ipv4' + uuid.uuid4().hex, + 'ipv6_address_scope': 'ipv6' + uuid.uuid4().hex, } # Overwrite default attributes. @@ -317,8 +319,21 @@ class FakeNetwork(object): # Set attributes with special mapping in OpenStack SDK. network.project_id = network_attrs['tenant_id'] network.is_router_external = network_attrs['router:external'] + network.is_admin_state_up = network_attrs['admin_state_up'] network.is_port_security_enabled = \ network_attrs['port_security_enabled'] + network.subnet_ids = network_attrs['subnets'] + network.is_shared = network_attrs['shared'] + network.provider_network_type = \ + network_attrs['provider:network_type'] + network.provider_physical_network = \ + network_attrs['provider:physical_network'] + network.provider_segmentation_id = \ + network_attrs['provider:segmentation_id'] + network.ipv4_address_scope_id = \ + network_attrs['ipv4_address_scope'] + network.ipv6_address_scope_id = \ + network_attrs['ipv6_address_scope'] return network @@ -461,12 +476,15 @@ class FakePort(object): loaded=True) # Set attributes with special mappings in OpenStack SDK. - port.project_id = port_attrs['tenant_id'] port.binding_host_id = port_attrs['binding:host_id'] port.binding_profile = port_attrs['binding:profile'] port.binding_vif_details = port_attrs['binding:vif_details'] port.binding_vif_type = port_attrs['binding:vif_type'] port.binding_vnic_type = port_attrs['binding:vnic_type'] + port.is_admin_state_up = port_attrs['admin_state_up'] + port.is_port_security_enabled = port_attrs['port_security_enabled'] + port.project_id = port_attrs['tenant_id'] + port.security_group_ids = port_attrs['security_groups'] return port @@ -834,6 +852,9 @@ class FakeRouter(object): # Set attributes with special mapping in OpenStack SDK. router.project_id = router_attrs['tenant_id'] + router.is_admin_state_up = router_attrs['admin_state_up'] + router.is_distributed = router_attrs['distributed'] + router.is_ha = router_attrs['ha'] return router diff --git a/openstackclient/tests/unit/network/v2/test_network.py b/openstackclient/tests/unit/network/v2/test_network.py index 96b1b102..c5283443 100644 --- a/openstackclient/tests/unit/network/v2/test_network.py +++ b/openstackclient/tests/unit/network/v2/test_network.py @@ -62,13 +62,15 @@ class TestCreateNetworkIdentityV3(TestNetwork): 'availability_zones', 'description', 'id', + 'ipv4_address_scope', + 'ipv6_address_scope', 'is_default', 'name', 'port_security_enabled', 'project_id', - 'provider_network_type', - 'provider_physical_network', - 'provider_segmentation_id', + 'provider:network_type', + 'provider:physical_network', + 'provider:segmentation_id', 'qos_policy_id', 'router:external', 'shared', @@ -82,6 +84,8 @@ class TestCreateNetworkIdentityV3(TestNetwork): utils.format_list(_network.availability_zones), _network.description, _network.id, + _network.ipv4_address_scope_id, + _network.ipv6_address_scope_id, _network.is_default, _network.name, _network.is_port_security_enabled, @@ -236,13 +240,15 @@ class TestCreateNetworkIdentityV2(TestNetwork): 'availability_zones', 'description', 'id', + 'ipv4_address_scope', + 'ipv6_address_scope', 'is_default', 'name', 'port_security_enabled', 'project_id', - 'provider_network_type', - 'provider_physical_network', - 'provider_segmentation_id', + 'provider:network_type', + 'provider:physical_network', + 'provider:segmentation_id', 'qos_policy_id', 'router:external', 'shared', @@ -256,6 +262,8 @@ class TestCreateNetworkIdentityV2(TestNetwork): utils.format_list(_network.availability_zones), _network.description, _network.id, + _network.ipv4_address_scope_id, + _network.ipv6_address_scope_id, _network.is_default, _network.name, _network.is_port_security_enabled, @@ -512,7 +520,7 @@ class TestListNetwork(TestNetwork): columns, data = self.cmd.take_action(parsed_args) self.network.networks.assert_called_once_with( - **{'router:external': True} + **{'router:external': True, 'is_router_external': True} ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -529,7 +537,7 @@ class TestListNetwork(TestNetwork): columns, data = self.cmd.take_action(parsed_args) self.network.networks.assert_called_once_with( - **{'router:external': False} + **{'router:external': False, 'is_router_external': False} ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -585,7 +593,7 @@ class TestListNetwork(TestNetwork): columns, data = self.cmd.take_action(parsed_args) self.network.networks.assert_called_once_with( - **{'admin_state_up': True} + **{'admin_state_up': True, 'is_admin_state_up': True} ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -603,7 +611,7 @@ class TestListNetwork(TestNetwork): columns, data = self.cmd.take_action(parsed_args) self.network.networks.assert_called_once_with( - **{'admin_state_up': False} + **{'admin_state_up': False, 'is_admin_state_up': False} ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -621,7 +629,7 @@ class TestListNetwork(TestNetwork): columns, data = self.cmd.take_action(parsed_args) self.network.networks.assert_called_once_with( - **{'tenant_id': project.id} + **{'tenant_id': project.id, 'project_id': project.id} ) self.assertEqual(self.columns, columns) @@ -640,7 +648,7 @@ class TestListNetwork(TestNetwork): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - filters = {'tenant_id': project.id} + filters = {'tenant_id': project.id, 'project_id': project.id} self.network.networks.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) @@ -658,7 +666,7 @@ class TestListNetwork(TestNetwork): columns, data = self.cmd.take_action(parsed_args) self.network.networks.assert_called_once_with( - **{'shared': True} + **{'shared': True, 'is_shared': True} ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -675,7 +683,7 @@ class TestListNetwork(TestNetwork): columns, data = self.cmd.take_action(parsed_args) self.network.networks.assert_called_once_with( - **{'shared': False} + **{'shared': False, 'is_shared': False} ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -711,7 +719,8 @@ class TestListNetwork(TestNetwork): columns, data = self.cmd.take_action(parsed_args) self.network.networks.assert_called_once_with( - **{'provider:network_type': network_type} + **{'provider:network_type': network_type, + 'provider_network_type': network_type} ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -728,7 +737,8 @@ class TestListNetwork(TestNetwork): columns, data = self.cmd.take_action(parsed_args) self.network.networks.assert_called_once_with( - **{'provider:physical_network': physical_network} + **{'provider:physical_network': physical_network, + 'provider_physical_network': physical_network} ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -745,7 +755,8 @@ class TestListNetwork(TestNetwork): columns, data = self.cmd.take_action(parsed_args) self.network.networks.assert_called_once_with( - **{'provider:segmentation_id': segmentation_id} + **{'provider:segmentation_id': segmentation_id, + 'provider_segmentation_id': segmentation_id} ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -878,13 +889,15 @@ class TestShowNetwork(TestNetwork): 'availability_zones', 'description', 'id', + 'ipv4_address_scope', + 'ipv6_address_scope', 'is_default', 'name', 'port_security_enabled', 'project_id', - 'provider_network_type', - 'provider_physical_network', - 'provider_segmentation_id', + 'provider:network_type', + 'provider:physical_network', + 'provider:segmentation_id', 'qos_policy_id', 'router:external', 'shared', @@ -898,6 +911,8 @@ class TestShowNetwork(TestNetwork): utils.format_list(_network.availability_zones), _network.description, _network.id, + _network.ipv4_address_scope_id, + _network.ipv6_address_scope_id, _network.is_default, _network.name, _network.is_port_security_enabled, diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py index aeb9884a..255e8116 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -324,7 +324,7 @@ class TestCreatePort(TestPort): self.assertEqual(ref_columns, columns) self.assertEqual(ref_data, data) - def test_create_with_no_secuirty_groups(self): + def test_create_with_no_security_groups(self): arglist = [ '--network', self._port.network_id, '--no-security-group', diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py index b0409447..8c1d5804 100644 --- a/openstackclient/tests/unit/network/v2/test_router.py +++ b/openstackclient/tests/unit/network/v2/test_router.py @@ -459,7 +459,7 @@ class TestListRouter(TestRouter): columns, data = self.cmd.take_action(parsed_args) self.network.routers.assert_called_once_with( - **{'admin_state_up': True} + **{'admin_state_up': True, 'is_admin_state_up': True} ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -476,7 +476,7 @@ class TestListRouter(TestRouter): columns, data = self.cmd.take_action(parsed_args) self.network.routers.assert_called_once_with( - **{'admin_state_up': False} + **{'admin_state_up': False, 'is_admin_state_up': False} ) self.assertEqual(self.columns, columns) @@ -494,7 +494,7 @@ class TestListRouter(TestRouter): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - filters = {'tenant_id': project.id} + filters = {'tenant_id': project.id, 'project_id': project.id} self.network.routers.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) @@ -514,7 +514,7 @@ class TestListRouter(TestRouter): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - filters = {'tenant_id': project.id} + filters = {'tenant_id': project.id, 'project_id': project.id} self.network.routers.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) diff --git a/openstackclient/tests/unit/test_shell.py b/openstackclient/tests/unit/test_shell.py index 3d91da9b..b9fac684 100644 --- a/openstackclient/tests/unit/test_shell.py +++ b/openstackclient/tests/unit/test_shell.py @@ -422,7 +422,7 @@ class TestShellArgV(TestShell): Use the argv supplied by the test runner so we get actual Python runtime behaviour; we only need to check the type of argv[0] - which will alwyas be present. + which will always be present. """ with mock.patch( diff --git a/openstackclient/tests/unit/volume/v1/test_volume.py b/openstackclient/tests/unit/volume/v1/test_volume.py index 7a44dea8..6c6d9a1d 100644 --- a/openstackclient/tests/unit/volume/v1/test_volume.py +++ b/openstackclient/tests/unit/volume/v1/test_volume.py @@ -431,6 +431,142 @@ class TestVolumeCreate(TestVolume): self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) + def test_volume_create_with_bootable_and_readonly(self): + arglist = [ + '--bootable', + '--read-only', + '--size', str(self.new_volume.size), + self.new_volume.display_name, + ] + verifylist = [ + ('bootable', True), + ('non_bootable', False), + ('read_only', True), + ('read_write', False), + ('size', self.new_volume.size), + ('name', self.new_volume.display_name), + ] + + parsed_args = self.check_parser( + self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.volumes_mock.create.assert_called_with( + self.new_volume.size, + None, + None, + self.new_volume.display_name, + None, + None, + None, + None, + None, + None, + None, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + self.volumes_mock.set_bootable.assert_called_with( + self.new_volume.id, True) + self.volumes_mock.update_readonly_flag.assert_called_with( + self.new_volume.id, True) + + def test_volume_create_with_nonbootable_and_readwrite(self): + arglist = [ + '--non-bootable', + '--read-write', + '--size', str(self.new_volume.size), + self.new_volume.display_name, + ] + verifylist = [ + ('bootable', False), + ('non_bootable', True), + ('read_only', False), + ('read_write', True), + ('size', self.new_volume.size), + ('name', self.new_volume.display_name), + ] + + parsed_args = self.check_parser( + self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.volumes_mock.create.assert_called_with( + self.new_volume.size, + None, + None, + self.new_volume.display_name, + None, + None, + None, + None, + None, + None, + None, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + self.volumes_mock.set_bootable.assert_called_with( + self.new_volume.id, False) + self.volumes_mock.update_readonly_flag.assert_called_with( + self.new_volume.id, False) + + @mock.patch.object(volume.LOG, 'error') + def test_volume_create_with_bootable_and_readonly_fail( + self, mock_error): + + self.volumes_mock.set_bootable.side_effect = ( + exceptions.CommandError()) + + self.volumes_mock.update_readonly_flag.side_effect = ( + exceptions.CommandError()) + + arglist = [ + '--bootable', + '--read-only', + '--size', str(self.new_volume.size), + self.new_volume.display_name, + ] + verifylist = [ + ('bootable', True), + ('non_bootable', False), + ('read_only', True), + ('read_write', False), + ('size', self.new_volume.size), + ('name', self.new_volume.display_name), + ] + + parsed_args = self.check_parser( + self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.volumes_mock.create.assert_called_with( + self.new_volume.size, + None, + None, + self.new_volume.display_name, + None, + None, + None, + None, + None, + None, + None, + ) + + self.assertEqual(2, mock_error.call_count) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + self.volumes_mock.set_bootable.assert_called_with( + self.new_volume.id, True) + self.volumes_mock.update_readonly_flag.assert_called_with( + self.new_volume.id, True) + def test_volume_create_without_size(self): arglist = [ self.new_volume.display_name, diff --git a/openstackclient/tests/unit/volume/v2/test_snapshot.py b/openstackclient/tests/unit/volume/v2/test_snapshot.py index cedf21a9..8ce356ae 100644 --- a/openstackclient/tests/unit/volume/v2/test_snapshot.py +++ b/openstackclient/tests/unit/volume/v2/test_snapshot.py @@ -67,6 +67,7 @@ class TestSnapshotCreate(TestSnapshot): self.volumes_mock.get.return_value = self.volume self.snapshots_mock.create.return_value = self.new_snapshot + self.snapshots_mock.manage.return_value = self.new_snapshot # Get the command object to test self.cmd = volume_snapshot.CreateVolumeSnapshot(self.app, None) @@ -152,6 +153,33 @@ class TestSnapshotCreate(TestSnapshot): self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) + def test_snapshot_create_without_remote_source(self): + arglist = [ + '--remote-source', 'source-name=test_source_name', + '--remote-source', 'source-id=test_source_id', + '--volume', self.new_snapshot.volume_id, + ] + ref_dict = {'source-name': 'test_source_name', + 'source-id': 'test_source_id'} + verifylist = [ + ('remote_source', ref_dict), + ('volume', self.new_snapshot.volume_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.snapshots_mock.manage.assert_called_with( + volume_id=self.new_snapshot.volume_id, + ref=ref_dict, + name=None, + description=None, + metadata=None, + ) + self.snapshots_mock.create.assert_not_called() + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + class TestSnapshotDelete(TestSnapshot): diff --git a/openstackclient/tests/unit/volume/v2/test_type.py b/openstackclient/tests/unit/volume/v2/test_type.py index 325872d7..0d556e13 100644 --- a/openstackclient/tests/unit/volume/v2/test_type.py +++ b/openstackclient/tests/unit/volume/v2/test_type.py @@ -172,7 +172,11 @@ class TestTypeList(TestType): "Description", "Properties" ] - + data_with_default_type = [( + volume_types[0].id, + volume_types[0].name, + True + )] data = [] for t in volume_types: data.append(( @@ -194,6 +198,7 @@ class TestTypeList(TestType): super(TestTypeList, self).setUp() self.types_mock.list.return_value = self.volume_types + self.types_mock.default.return_value = self.volume_types[0] # get the command to test self.cmd = volume_type.ListVolumeType(self.app, None) @@ -203,6 +208,7 @@ class TestTypeList(TestType): ("long", False), ("private", False), ("public", False), + ("default", False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -220,6 +226,7 @@ class TestTypeList(TestType): ("long", True), ("private", False), ("public", True), + ("default", False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -236,6 +243,7 @@ class TestTypeList(TestType): ("long", False), ("private", True), ("public", False), + ("default", False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -244,6 +252,23 @@ class TestTypeList(TestType): self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) + def test_type_list_with_default_option(self): + arglist = [ + "--default", + ] + verifylist = [ + ("long", False), + ("private", False), + ("public", False), + ("default", True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.types_mock.default.assert_called_once_with() + self.assertEqual(self.columns, columns) + self.assertEqual(self.data_with_default_type, list(data)) + class TestTypeSet(TestType): diff --git a/openstackclient/tests/unit/volume/v2/test_volume.py b/openstackclient/tests/unit/volume/v2/test_volume.py index 41728342..1529c2e2 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume.py +++ b/openstackclient/tests/unit/volume/v2/test_volume.py @@ -450,6 +450,154 @@ class TestVolumeCreate(TestVolume): self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) + def test_volume_create_with_bootable_and_readonly(self): + arglist = [ + '--bootable', + '--read-only', + '--size', str(self.new_volume.size), + self.new_volume.name, + ] + verifylist = [ + ('bootable', True), + ('non_bootable', False), + ('read_only', True), + ('read_write', False), + ('size', self.new_volume.size), + ('name', self.new_volume.name), + ] + + parsed_args = self.check_parser( + self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.volumes_mock.create.assert_called_with( + size=self.new_volume.size, + snapshot_id=None, + name=self.new_volume.name, + description=None, + volume_type=None, + user_id=None, + project_id=None, + availability_zone=None, + metadata=None, + imageRef=None, + source_volid=None, + consistencygroup_id=None, + source_replica=None, + multiattach=False, + scheduler_hints=None, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + self.volumes_mock.set_bootable.assert_called_with( + self.new_volume.id, True) + self.volumes_mock.update_readonly_flag.assert_called_with( + self.new_volume.id, True) + + def test_volume_create_with_nonbootable_and_readwrite(self): + arglist = [ + '--non-bootable', + '--read-write', + '--size', str(self.new_volume.size), + self.new_volume.name, + ] + verifylist = [ + ('bootable', False), + ('non_bootable', True), + ('read_only', False), + ('read_write', True), + ('size', self.new_volume.size), + ('name', self.new_volume.name), + ] + + parsed_args = self.check_parser( + self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.volumes_mock.create.assert_called_with( + size=self.new_volume.size, + snapshot_id=None, + name=self.new_volume.name, + description=None, + volume_type=None, + user_id=None, + project_id=None, + availability_zone=None, + metadata=None, + imageRef=None, + source_volid=None, + consistencygroup_id=None, + source_replica=None, + multiattach=False, + scheduler_hints=None, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + self.volumes_mock.set_bootable.assert_called_with( + self.new_volume.id, False) + self.volumes_mock.update_readonly_flag.assert_called_with( + self.new_volume.id, False) + + @mock.patch.object(volume.LOG, 'error') + def test_volume_create_with_bootable_and_readonly_fail( + self, mock_error): + + self.volumes_mock.set_bootable.side_effect = ( + exceptions.CommandError()) + + self.volumes_mock.update_readonly_flag.side_effect = ( + exceptions.CommandError()) + + arglist = [ + '--bootable', + '--read-only', + '--size', str(self.new_volume.size), + self.new_volume.name, + ] + verifylist = [ + ('bootable', True), + ('non_bootable', False), + ('read_only', True), + ('read_write', False), + ('size', self.new_volume.size), + ('name', self.new_volume.name), + ] + + parsed_args = self.check_parser( + self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.volumes_mock.create.assert_called_with( + size=self.new_volume.size, + snapshot_id=None, + name=self.new_volume.name, + description=None, + volume_type=None, + user_id=None, + project_id=None, + availability_zone=None, + metadata=None, + imageRef=None, + source_volid=None, + consistencygroup_id=None, + source_replica=None, + multiattach=False, + scheduler_hints=None, + ) + + self.assertEqual(2, mock_error.call_count) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + self.volumes_mock.set_bootable.assert_called_with( + self.new_volume.id, True) + self.volumes_mock.update_readonly_flag.assert_called_with( + self.new_volume.id, True) + def test_volume_create_with_source_replicated(self): self.volumes_mock.get.return_value = self.new_volume arglist = [ diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py index 0087bad4..739484df 100644 --- a/openstackclient/volume/v1/volume.py +++ b/openstackclient/volume/v1/volume.py @@ -114,6 +114,28 @@ class CreateVolume(command.ShowOne): help=_('Set a property on this volume ' '(repeat option to set multiple properties)'), ) + bootable_group = parser.add_mutually_exclusive_group() + bootable_group.add_argument( + "--bootable", + action="store_true", + help=_("Mark volume as bootable") + ) + bootable_group.add_argument( + "--non-bootable", + action="store_true", + help=_("Mark volume as non-bootable (default)") + ) + readonly_group = parser.add_mutually_exclusive_group() + readonly_group.add_argument( + "--read-only", + action="store_true", + help=_("Set volume to read-only access mode") + ) + readonly_group.add_argument( + "--read-write", + action="store_true", + help=_("Set volume to read-write access mode (default)") + ) return parser @@ -166,6 +188,22 @@ class CreateVolume(command.ShowOne): parsed_args.property, image, ) + + if parsed_args.bootable or parsed_args.non_bootable: + try: + volume_client.volumes.set_bootable( + volume.id, parsed_args.bootable) + except Exception as e: + LOG.error(_("Failed to set volume bootable property: %s"), e) + if parsed_args.read_only or parsed_args.read_write: + try: + volume_client.volumes.update_readonly_flag( + volume.id, + parsed_args.read_only) + except Exception as e: + LOG.error(_("Failed to set volume read-only access " + "mode flag: %s"), e) + # Map 'metadata' column to 'properties' volume._info.update( { diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index 80abfb55..301bf5e4 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -132,6 +132,28 @@ class CreateVolume(command.ShowOne): help=_("Allow volume to be attached more than once " "(default to False)") ) + bootable_group = parser.add_mutually_exclusive_group() + bootable_group.add_argument( + "--bootable", + action="store_true", + help=_("Mark volume as bootable") + ) + bootable_group.add_argument( + "--non-bootable", + action="store_true", + help=_("Mark volume as non-bootable (default)") + ) + readonly_group = parser.add_mutually_exclusive_group() + readonly_group.add_argument( + "--read-only", + action="store_true", + help=_("Set volume to read-only access mode") + ) + readonly_group.add_argument( + "--read-write", + action="store_true", + help=_("Set volume to read-write access mode (default)") + ) return parser def take_action(self, parsed_args): @@ -199,6 +221,22 @@ class CreateVolume(command.ShowOne): multiattach=parsed_args.multi_attach, scheduler_hints=parsed_args.hint, ) + + if parsed_args.bootable or parsed_args.non_bootable: + try: + volume_client.volumes.set_bootable( + volume.id, parsed_args.bootable) + except Exception as e: + LOG.error(_("Failed to set volume bootable property: %s"), e) + if parsed_args.read_only or parsed_args.read_write: + try: + volume_client.volumes.update_readonly_flag( + volume.id, + parsed_args.read_only) + except Exception as e: + LOG.error(_("Failed to set volume read-only access " + "mode flag: %s"), e) + # Remove key links from being displayed volume._info.update( { diff --git a/openstackclient/volume/v2/volume_snapshot.py b/openstackclient/volume/v2/volume_snapshot.py index d95a49a4..34b8fb82 100644 --- a/openstackclient/volume/v2/volume_snapshot.py +++ b/openstackclient/volume/v2/volume_snapshot.py @@ -65,6 +65,15 @@ class CreateVolumeSnapshot(command.ShowOne): help=_("Set a property to this snapshot " "(repeat option to set multiple properties)"), ) + parser.add_argument( + "--remote-source", + metavar="<key=value>", + action=parseractions.KeyValueAction, + help=_("The attribute(s) of the exsiting remote volume snapshot " + "(admin required) (repeat option to specify multiple " + "attributes) e.g.: '--remote-source source-name=test_name " + "--remote-source source-id=test_id'"), + ) return parser def take_action(self, parsed_args): @@ -74,13 +83,29 @@ class CreateVolumeSnapshot(command.ShowOne): volume = parsed_args.snapshot_name volume_id = utils.find_resource( volume_client.volumes, volume).id - snapshot = volume_client.volume_snapshots.create( - volume_id, - force=parsed_args.force, - name=parsed_args.snapshot_name, - description=parsed_args.description, - metadata=parsed_args.property, - ) + if parsed_args.remote_source: + # Create a new snapshot from an existing remote snapshot source + if parsed_args.force: + msg = (_("'--force' option will not work when you create " + "new volume snapshot from an existing remote " + "volume snapshot")) + LOG.warning(msg) + snapshot = volume_client.volume_snapshots.manage( + volume_id=volume_id, + ref=parsed_args.remote_source, + name=parsed_args.snapshot_name, + description=parsed_args.description, + metadata=parsed_args.property, + ) + else: + # create a new snapshot from scratch + snapshot = volume_client.volume_snapshots.create( + volume_id, + force=parsed_args.force, + name=parsed_args.snapshot_name, + description=parsed_args.description, + metadata=parsed_args.property, + ) snapshot._info.update( {'properties': utils.format_dict(snapshot._info.pop('metadata'))} ) diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py index 42ebb53e..46466783 100644 --- a/openstackclient/volume/v2/volume_type.py +++ b/openstackclient/volume/v2/volume_type.py @@ -160,9 +160,16 @@ class ListVolumeType(command.Lister): '--long', action='store_true', default=False, - help=_('List additional fields in output')) + help=_('List additional fields in output') + ) public_group = parser.add_mutually_exclusive_group() public_group.add_argument( + "--default", + action='store_true', + default=False, + help=_('List the default volume type') + ) + public_group.add_argument( "--public", action="store_true", help=_("List only public types") @@ -175,6 +182,7 @@ class ListVolumeType(command.Lister): return parser def take_action(self, parsed_args): + volume_client = self.app.client_manager.volume if parsed_args.long: columns = ['ID', 'Name', 'Is Public', 'Description', 'Extra Specs'] column_headers = [ @@ -182,14 +190,16 @@ class ListVolumeType(command.Lister): else: columns = ['ID', 'Name', 'Is Public'] column_headers = columns - - 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) + if parsed_args.default: + data = [volume_client.volume_types.default()] + else: + is_public = None + if parsed_args.public: + is_public = True + if parsed_args.private: + is_public = False + data = volume_client.volume_types.list( + is_public=is_public) return (column_headers, (utils.get_item_properties( s, columns, @@ -240,7 +250,6 @@ class SetVolumeType(command.Command): volume_type = utils.find_resource( volume_client.volume_types, parsed_args.volume_type) - result = 0 kwargs = {} if parsed_args.name: |
