diff options
Diffstat (limited to 'openstackclient')
47 files changed, 1127 insertions, 287 deletions
diff --git a/openstackclient/api/object_store_v1.py b/openstackclient/api/object_store_v1.py index c8514a57..8092abd0 100644 --- a/openstackclient/api/object_store_v1.py +++ b/openstackclient/api/object_store_v1.py @@ -87,7 +87,7 @@ class APIv1(api.BaseAPI): def container_list( self, - all_data=False, + full_listing=False, limit=None, marker=None, end_marker=None, @@ -96,7 +96,7 @@ class APIv1(api.BaseAPI): ): """Get containers in an account - :param boolean all_data: + :param boolean full_listing: if True, return a full listing, else returns a max of 10000 listings :param integer limit: @@ -113,7 +113,7 @@ class APIv1(api.BaseAPI): params['format'] = 'json' - if all_data: + if full_listing: data = listing = self.container_list( limit=limit, marker=marker, @@ -299,7 +299,7 @@ class APIv1(api.BaseAPI): def object_list( self, container=None, - all_data=False, + full_listing=False, limit=None, marker=None, end_marker=None, @@ -311,7 +311,7 @@ class APIv1(api.BaseAPI): :param string container: container name to get a listing for - :param boolean all_data: + :param boolean full_listing: if True, return a full listing, else returns a max of 10000 listings :param integer limit: @@ -328,11 +328,11 @@ class APIv1(api.BaseAPI): headers will be a dict and all header names will be lowercase. """ - if container is None or object is None: + if container is None: return None params['format'] = 'json' - if all_data: + if full_listing: data = listing = self.object_list( container=container, limit=limit, diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 8be78049..93e9f966 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -1921,9 +1921,12 @@ class RebuildServer(command.ShowOne): compute_client.servers, parsed_args.server) # If parsed_args.image is not set, default to the currently used one. - image_id = parsed_args.image or server.to_dict().get( - 'image', {}).get('id') - image = image_client.get_image(image_id) + if parsed_args.image: + image = image_client.find_image( + parsed_args.image, ignore_missing=False) + else: + image_id = server.to_dict().get('image', {}).get('id') + image = image_client.get_image(image_id) kwargs = {} if parsed_args.property: diff --git a/openstackclient/identity/common.py b/openstackclient/identity/common.py index 7be2a17b..e70d87d2 100644 --- a/openstackclient/identity/common.py +++ b/openstackclient/identity/common.py @@ -213,6 +213,15 @@ def _find_identity_resource(identity_client_manager, name_or_id, return resource_type(None, {'id': name_or_id, 'name': name_or_id}) +def get_immutable_options(parsed_args): + options = {} + if parsed_args.immutable: + options['immutable'] = True + if parsed_args.no_immutable: + options['immutable'] = False + return options + + def add_user_domain_option_to_parser(parser): parser.add_argument( '--user-domain', @@ -261,3 +270,18 @@ def add_inherited_option_to_parser(parser): help=_('Specifies if the role grant is inheritable to the sub ' 'projects'), ) + + +def add_resource_option_to_parser(parser): + enable_group = parser.add_mutually_exclusive_group() + enable_group.add_argument( + '--immutable', + action='store_true', + help=_('Make resource immutable. An immutable project may not ' + 'be deleted or modified except to remove the immutable flag'), + ) + enable_group.add_argument( + '--no-immutable', + action='store_true', + help=_('Make resource mutable (default)'), + ) diff --git a/openstackclient/identity/v3/domain.py b/openstackclient/identity/v3/domain.py index dbcc97f6..e33fce05 100644 --- a/openstackclient/identity/v3/domain.py +++ b/openstackclient/identity/v3/domain.py @@ -60,6 +60,7 @@ class CreateDomain(command.ShowOne): action='store_true', help=_('Return existing domain'), ) + common.add_resource_option_to_parser(parser) return parser def take_action(self, parsed_args): @@ -69,10 +70,13 @@ class CreateDomain(command.ShowOne): if parsed_args.disable: enabled = False + options = common.get_immutable_options(parsed_args) + try: domain = identity_client.domains.create( name=parsed_args.name, description=parsed_args.description, + options=options, enabled=enabled, ) except ks_exc.Conflict: @@ -163,6 +167,7 @@ class SetDomain(command.Command): action='store_true', help=_('Disable domain'), ) + common.add_resource_option_to_parser(parser) return parser def take_action(self, parsed_args): @@ -180,6 +185,10 @@ class SetDomain(command.Command): if parsed_args.disable: kwargs['enabled'] = False + options = common.get_immutable_options(parsed_args) + if options: + kwargs['options'] = options + identity_client.domains.update(domain.id, **kwargs) diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py index 9ecc70ef..5e8ce829 100644 --- a/openstackclient/identity/v3/project.py +++ b/openstackclient/identity/v3/project.py @@ -78,6 +78,7 @@ class CreateProject(command.ShowOne): action='store_true', help=_('Return existing project'), ) + common.add_resource_option_to_parser(parser) tag.add_tag_option_to_parser_for_create(parser, _('project')) return parser @@ -99,9 +100,20 @@ class CreateProject(command.ShowOne): enabled = True if parsed_args.disable: enabled = False + + options = common.get_immutable_options(parsed_args) + kwargs = {} if parsed_args.property: kwargs = parsed_args.property.copy() + if 'is_domain' in kwargs.keys(): + if kwargs['is_domain'].lower() == "true": + kwargs['is_domain'] = True + elif kwargs['is_domain'].lower() == "false": + kwargs['is_domain'] = False + elif kwargs['is_domain'].lower() == "none": + kwargs['is_domain'] = None + kwargs['tags'] = list(set(parsed_args.tags)) try: @@ -111,6 +123,7 @@ class CreateProject(command.ShowOne): parent=parent, description=parsed_args.description, enabled=enabled, + options=options, **kwargs ) except ks_exc.Conflict: @@ -317,6 +330,7 @@ class SetProject(command.Command): help=_('Set a property on <project> ' '(repeat option to set multiple properties)'), ) + common.add_resource_option_to_parser(parser) tag.add_tag_option_to_parser_for_set(parser, _('project')) return parser @@ -336,6 +350,9 @@ class SetProject(command.Command): kwargs['enabled'] = True if parsed_args.disable: kwargs['enabled'] = False + options = common.get_immutable_options(parsed_args) + if options: + kwargs['options'] = options if parsed_args.property: kwargs.update(parsed_args.property) tag.update_tags_in_args(parsed_args, project, kwargs) diff --git a/openstackclient/identity/v3/role.py b/openstackclient/identity/v3/role.py index 36f3f938..980ebf11 100644 --- a/openstackclient/identity/v3/role.py +++ b/openstackclient/identity/v3/role.py @@ -191,6 +191,7 @@ class CreateRole(command.ShowOne): action='store_true', help=_('Return existing role'), ) + common.add_resource_option_to_parser(parser) return parser def take_action(self, parsed_args): @@ -201,10 +202,12 @@ class CreateRole(command.ShowOne): domain_id = common.find_domain(identity_client, parsed_args.domain).id + options = common.get_immutable_options(parsed_args) + try: role = identity_client.roles.create( name=parsed_args.name, domain=domain_id, - description=parsed_args.description) + description=parsed_args.description, options=options) except ks_exc.Conflict: if parsed_args.or_show: @@ -366,6 +369,7 @@ class SetRole(command.Command): metavar='<name>', help=_('Set role name'), ) + common.add_resource_option_to_parser(parser) return parser def take_action(self, parsed_args): @@ -376,12 +380,14 @@ class SetRole(command.Command): domain_id = common.find_domain(identity_client, parsed_args.domain).id + options = common.get_immutable_options(parsed_args) role = utils.find_resource(identity_client.roles, parsed_args.role, domain_id=domain_id) identity_client.roles.update(role.id, name=parsed_args.name, - description=parsed_args.description) + description=parsed_args.description, + options=options) class ShowRole(command.ShowOne): diff --git a/openstackclient/network/sdk_utils.py b/openstackclient/network/sdk_utils.py index af9c74f9..cff30713 100644 --- a/openstackclient/network/sdk_utils.py +++ b/openstackclient/network/sdk_utils.py @@ -10,6 +10,8 @@ # License for the specific language governing permissions and limitations # under the License. +import munch + def get_osc_show_columns_for_sdk_resource( sdk_resource, @@ -38,6 +40,9 @@ def get_osc_show_columns_for_sdk_resource( # Build the OSC column names to display for the SDK resource. attr_map = {} display_columns = list(resource_dict.keys()) + for col_name in display_columns: + if isinstance(resource_dict[col_name], munch.Munch): + display_columns.remove(col_name) invisible_columns = [] if invisible_columns is None else invisible_columns for col_name in invisible_columns: if col_name in display_columns: diff --git a/openstackclient/network/v2/address_scope.py b/openstackclient/network/v2/address_scope.py index 7efbb631..71c1a9af 100644 --- a/openstackclient/network/v2/address_scope.py +++ b/openstackclient/network/v2/address_scope.py @@ -15,7 +15,6 @@ import logging -from osc_lib.cli import format_columns from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils @@ -28,11 +27,6 @@ from openstackclient.network import sdk_utils LOG = logging.getLogger(__name__) -_formatters = { - 'location': format_columns.DictColumn, -} - - def _get_columns(item): column_map = { 'is_shared': 'shared', @@ -106,7 +100,7 @@ class CreateAddressScope(command.ShowOne): attrs = _get_attrs(self.app.client_manager, parsed_args) obj = client.create_address_scope(**attrs) display_columns, columns = _get_columns(obj) - data = utils.get_item_properties(obj, columns, formatters=_formatters) + data = utils.get_item_properties(obj, columns, formatters={}) return (display_columns, data) @@ -295,6 +289,6 @@ class ShowAddressScope(command.ShowOne): parsed_args.address_scope, ignore_missing=False) display_columns, columns = _get_columns(obj) - data = utils.get_item_properties(obj, columns, formatters=_formatters) + data = utils.get_item_properties(obj, columns, formatters={}) return (display_columns, data) diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py index f3e3e5c4..a2765cd1 100644 --- a/openstackclient/network/v2/floating_ip.py +++ b/openstackclient/network/v2/floating_ip.py @@ -13,7 +13,6 @@ """IP Floating action implementations""" -from osc_lib.cli import format_columns from osc_lib.command import command from osc_lib import utils from osc_lib.utils import tags as _tag @@ -25,7 +24,6 @@ from openstackclient.network import sdk_utils _formatters = { - 'location': format_columns.DictColumn, 'port_details': utils.format_dict, } diff --git a/openstackclient/network/v2/floating_ip_port_forwarding.py b/openstackclient/network/v2/floating_ip_port_forwarding.py index f94bcc06..06b3df8b 100644 --- a/openstackclient/network/v2/floating_ip_port_forwarding.py +++ b/openstackclient/network/v2/floating_ip_port_forwarding.py @@ -75,6 +75,12 @@ class CreateFloatingIPPortForwarding(command.ShowOne): required=True, help=_("The protocol used in the floating IP " "port forwarding, for instance: TCP, UDP") + ), + parser.add_argument( + '--description', + metavar='<description>', + help=_("A text to describe/contextualize the use of the " + "port forwarding configuration") ) parser.add_argument( 'floating_ip', @@ -113,6 +119,9 @@ class CreateFloatingIPPortForwarding(command.ShowOne): attrs['internal_ip_address'] = parsed_args.internal_ip_address attrs['protocol'] = parsed_args.protocol + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + obj = client.create_floating_ip_port_forwarding( floating_ip.id, **attrs @@ -212,6 +221,7 @@ class ListFloatingIPPortForwarding(command.Lister): 'internal_port', 'external_port', 'protocol', + 'description', ) headers = ( 'ID', @@ -220,6 +230,7 @@ class ListFloatingIPPortForwarding(command.Lister): 'Internal Port', 'External Port', 'Protocol', + 'Description', ) query = {} @@ -296,6 +307,12 @@ class SetFloatingIPPortForwarding(command.Command): metavar='<protocol>', choices=['tcp', 'udp'], help=_("The IP protocol used in the floating IP port forwarding") + ), + parser.add_argument( + '--description', + metavar='<description>', + help=_("A text to describe/contextualize the use of " + "the port forwarding configuration") ) return parser @@ -332,6 +349,9 @@ class SetFloatingIPPortForwarding(command.Command): if parsed_args.protocol: attrs['protocol'] = parsed_args.protocol + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + client.update_floating_ip_port_forwarding( floating_ip.id, parsed_args.port_forwarding_id, **attrs) diff --git a/openstackclient/network/v2/ip_availability.py b/openstackclient/network/v2/ip_availability.py index c026baa0..ddc88e55 100644 --- a/openstackclient/network/v2/ip_availability.py +++ b/openstackclient/network/v2/ip_availability.py @@ -21,9 +21,7 @@ from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import sdk_utils - _formatters = { - 'location': format_columns.DictColumn, 'subnet_ip_availability': format_columns.ListDictColumn, } diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py index 3f579b6d..7a12d523 100644 --- a/openstackclient/network/v2/network.py +++ b/openstackclient/network/v2/network.py @@ -16,7 +16,6 @@ from cliff import columns as cliff_columns from osc_lib.cli import format_columns from osc_lib.command import command -from osc_lib import exceptions from osc_lib import utils from osc_lib.utils import tags as _tag @@ -41,7 +40,6 @@ _formatters = { 'subnet_ids': format_columns.ListColumn, 'admin_state_up': AdminStateColumn, 'is_admin_state_up': AdminStateColumn, - 'location': format_columns.DictColumn, 'router:external': RouterExternalColumn, 'is_router_external': RouterExternalColumn, 'availability_zones': format_columns.ListColumn, @@ -126,9 +124,6 @@ def _get_attrs_network(client_manager, parsed_args): attrs['is_default'] = False if parsed_args.default: attrs['is_default'] = True - if attrs.get('is_default') and not attrs.get('router:external'): - msg = _("Cannot set default for internal network") - raise exceptions.CommandError(msg) # Update Provider network options if parsed_args.provider_network_type: attrs['provider:network_type'] = parsed_args.provider_network_type @@ -706,8 +701,7 @@ class SetNetwork(command.Command): default_router_grp.add_argument( '--default', action='store_true', - help=_("Set the network as the default external network " - "(cannot be used with internal network).") + help=_("Set the network as the default external network") ) default_router_grp.add_argument( '--no-default', diff --git a/openstackclient/network/v2/network_agent.py b/openstackclient/network/v2/network_agent.py index a6ed3629..16784854 100644 --- a/openstackclient/network/v2/network_agent.py +++ b/openstackclient/network/v2/network_agent.py @@ -43,7 +43,6 @@ _formatters = { 'alive': AliveColumn, 'admin_state_up': AdminStateColumn, 'is_admin_state_up': AdminStateColumn, - 'location': format_columns.DictColumn, 'configurations': format_columns.DictColumn, } diff --git a/openstackclient/network/v2/network_auto_allocated_topology.py b/openstackclient/network/v2/network_auto_allocated_topology.py index f6070a02..36f39200 100644 --- a/openstackclient/network/v2/network_auto_allocated_topology.py +++ b/openstackclient/network/v2/network_auto_allocated_topology.py @@ -15,7 +15,6 @@ import logging -from osc_lib.cli import format_columns from osc_lib.command import command from osc_lib import utils @@ -26,11 +25,6 @@ from openstackclient.network import sdk_utils LOG = logging.getLogger(__name__) -_formatters = { - 'location': format_columns.DictColumn, -} - - def _get_columns(item): column_map = { 'tenant_id': 'project_id', @@ -99,17 +93,16 @@ class CreateAutoAllocatedTopology(command.ShowOne): obj = client.validate_auto_allocated_topology(parsed_args.project) columns = _format_check_resource_columns() - data = utils.get_item_properties( - _format_check_resource(obj), - columns, - formatters=_formatters, - ) + data = utils.get_item_properties(_format_check_resource(obj), + columns, + formatters={}) + return (columns, data) def get_topology(self, client, parsed_args): obj = client.get_auto_allocated_topology(parsed_args.project) display_columns, columns = _get_columns(obj) - data = utils.get_item_properties(obj, columns, formatters=_formatters) + data = utils.get_item_properties(obj, columns, formatters={}) return (display_columns, data) def take_action(self, parsed_args): diff --git a/openstackclient/network/v2/network_flavor.py b/openstackclient/network/v2/network_flavor.py index 355d04c1..c9d368bf 100644 --- a/openstackclient/network/v2/network_flavor.py +++ b/openstackclient/network/v2/network_flavor.py @@ -15,7 +15,6 @@ import logging -from osc_lib.cli import format_columns from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils @@ -28,11 +27,6 @@ from openstackclient.network import sdk_utils LOG = logging.getLogger(__name__) -_formatters = { - 'location': format_columns.DictColumn, -} - - def _get_columns(item): column_map = { 'is_enabled': 'enabled', @@ -142,7 +136,7 @@ class CreateNetworkFlavor(command.ShowOne): attrs = _get_attrs(self.app.client_manager, parsed_args) obj = client.create_flavor(**attrs) display_columns, columns = _get_columns(obj) - data = utils.get_item_properties(obj, columns, formatters=_formatters) + data = utils.get_item_properties(obj, columns, formatters={}) return (display_columns, data) @@ -306,5 +300,5 @@ class ShowNetworkFlavor(command.ShowOne): client = self.app.client_manager.network obj = client.find_flavor(parsed_args.flavor, ignore_missing=False) display_columns, columns = _get_columns(obj) - data = utils.get_item_properties(obj, columns, formatters=_formatters) + data = utils.get_item_properties(obj, columns) return display_columns, data diff --git a/openstackclient/network/v2/network_flavor_profile.py b/openstackclient/network/v2/network_flavor_profile.py index 492fd432..6cf0c412 100644 --- a/openstackclient/network/v2/network_flavor_profile.py +++ b/openstackclient/network/v2/network_flavor_profile.py @@ -13,7 +13,6 @@ import logging -from osc_lib.cli import format_columns from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils @@ -26,11 +25,6 @@ from openstackclient.network import sdk_utils LOG = logging.getLogger(__name__) -_formatters = { - 'location': format_columns.DictColumn, -} - - def _get_columns(item): column_map = { 'is_enabled': 'enabled', @@ -116,7 +110,7 @@ class CreateNetworkFlavorProfile(command.ShowOne): obj = client.create_service_profile(**attrs) display_columns, columns = _get_columns(obj) - data = utils.get_item_properties(obj, columns, formatters=_formatters) + data = utils.get_item_properties(obj, columns, formatters={}) return (display_columns, data) @@ -252,5 +246,5 @@ class ShowNetworkFlavorProfile(command.ShowOne): obj = client.find_service_profile(parsed_args.flavor_profile, ignore_missing=False) display_columns, columns = _get_columns(obj) - data = utils.get_item_properties(obj, columns, formatters=_formatters) + data = utils.get_item_properties(obj, columns) return (display_columns, data) diff --git a/openstackclient/network/v2/network_meter.py b/openstackclient/network/v2/network_meter.py index cde7a304..df0e1da1 100644 --- a/openstackclient/network/v2/network_meter.py +++ b/openstackclient/network/v2/network_meter.py @@ -15,7 +15,6 @@ import logging -from osc_lib.cli import format_columns from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils @@ -27,11 +26,6 @@ from openstackclient.network import sdk_utils LOG = logging.getLogger(__name__) -_formatters = { - 'location': format_columns.DictColumn, -} - - def _get_columns(item): column_map = { 'is_shared': 'shared', @@ -108,7 +102,7 @@ class CreateMeter(command.ShowOne): attrs = _get_attrs(self.app.client_manager, parsed_args) obj = client.create_metering_label(**attrs) display_columns, columns = _get_columns(obj) - data = utils.get_item_properties(obj, columns, formatters=_formatters) + data = utils.get_item_properties(obj, columns, formatters={}) return (display_columns, data) @@ -192,5 +186,5 @@ class ShowMeter(command.ShowOne): obj = client.find_metering_label(parsed_args.meter, ignore_missing=False) display_columns, columns = _get_columns(obj) - data = utils.get_item_properties(obj, columns, formatters=_formatters) + data = utils.get_item_properties(obj, columns) return display_columns, data diff --git a/openstackclient/network/v2/network_meter_rule.py b/openstackclient/network/v2/network_meter_rule.py index 5f31255a..49ff9e1b 100644 --- a/openstackclient/network/v2/network_meter_rule.py +++ b/openstackclient/network/v2/network_meter_rule.py @@ -15,7 +15,6 @@ import logging -from osc_lib.cli import format_columns from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils @@ -27,11 +26,6 @@ from openstackclient.network import sdk_utils LOG = logging.getLogger(__name__) -_formatters = { - 'location': format_columns.DictColumn, -} - - def _get_columns(item): column_map = { 'tenant_id': 'project_id', @@ -122,7 +116,7 @@ class CreateMeterRule(command.ShowOne): attrs = _get_attrs(self.app.client_manager, parsed_args) obj = client.create_metering_label_rule(**attrs) display_columns, columns = _get_columns(obj) - data = utils.get_item_properties(obj, columns, formatters=_formatters) + data = utils.get_item_properties(obj, columns, formatters={}) return (display_columns, data) @@ -205,5 +199,5 @@ class ShowMeterRule(command.ShowOne): obj = client.find_metering_label_rule(parsed_args.meter_rule_id, ignore_missing=False) display_columns, columns = _get_columns(obj) - data = utils.get_item_properties(obj, columns, formatters=_formatters) + data = utils.get_item_properties(obj, columns) return display_columns, data diff --git a/openstackclient/network/v2/network_qos_policy.py b/openstackclient/network/v2/network_qos_policy.py index 1622de4a..fd5ff937 100644 --- a/openstackclient/network/v2/network_qos_policy.py +++ b/openstackclient/network/v2/network_qos_policy.py @@ -15,7 +15,6 @@ import logging -from osc_lib.cli import format_columns from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils @@ -28,11 +27,6 @@ from openstackclient.network import sdk_utils LOG = logging.getLogger(__name__) -_formatters = { - 'location': format_columns.DictColumn, -} - - def _get_columns(item): column_map = { 'is_shared': 'shared', @@ -125,7 +119,7 @@ class CreateNetworkQosPolicy(command.ShowOne): attrs = _get_attrs(self.app.client_manager, parsed_args) obj = client.create_qos_policy(**attrs) display_columns, columns = _get_columns(obj) - data = utils.get_item_properties(obj, columns, formatters=_formatters) + data = utils.get_item_properties(obj, columns, formatters={}) return (display_columns, data) @@ -285,5 +279,5 @@ class ShowNetworkQosPolicy(command.ShowOne): obj = client.find_qos_policy(parsed_args.policy, ignore_missing=False) display_columns, columns = _get_columns(obj) - data = utils.get_item_properties(obj, columns, formatters=_formatters) + data = utils.get_item_properties(obj, columns) return (display_columns, data) diff --git a/openstackclient/network/v2/network_qos_rule.py b/openstackclient/network/v2/network_qos_rule.py index d74beda7..28c5600a 100644 --- a/openstackclient/network/v2/network_qos_rule.py +++ b/openstackclient/network/v2/network_qos_rule.py @@ -15,7 +15,6 @@ import itertools -from osc_lib.cli import format_columns from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils @@ -47,11 +46,6 @@ ACTION_SET = 'update' ACTION_SHOW = 'get' -_formatters = { - 'location': format_columns.DictColumn, -} - - def _get_columns(item): column_map = { 'tenant_id': 'project_id', @@ -214,7 +208,7 @@ class CreateNetworkQosRule(command.ShowOne): msg = (_('Failed to create Network QoS rule: %(e)s') % {'e': e}) raise exceptions.CommandError(msg) display_columns, columns = _get_columns(obj) - data = utils.get_item_properties(obj, columns, formatters=_formatters) + data = utils.get_item_properties(obj, columns) return display_columns, data @@ -364,5 +358,5 @@ class ShowNetworkQosRule(command.ShowOne): {'rule': rule_id, 'e': e}) raise exceptions.CommandError(msg) display_columns, columns = _get_columns(obj) - data = utils.get_item_properties(obj, columns, formatters=_formatters) + data = utils.get_item_properties(obj, columns) return display_columns, data diff --git a/openstackclient/network/v2/network_qos_rule_type.py b/openstackclient/network/v2/network_qos_rule_type.py index e842944c..7b92c8ad 100644 --- a/openstackclient/network/v2/network_qos_rule_type.py +++ b/openstackclient/network/v2/network_qos_rule_type.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -from osc_lib.cli import format_columns from osc_lib.command import command from osc_lib import utils @@ -21,11 +20,6 @@ from openstackclient.i18n import _ from openstackclient.network import sdk_utils -_formatters = { - 'location': format_columns.DictColumn, -} - - def _get_columns(item): column_map = { "type": "rule_type_name", @@ -71,5 +65,5 @@ class ShowNetworkQosRuleType(command.ShowOne): client = self.app.client_manager.network obj = client.get_qos_rule_type(parsed_args.rule_type) display_columns, columns = _get_columns(obj) - data = utils.get_item_properties(obj, columns, formatters=_formatters) + data = utils.get_item_properties(obj, columns) return display_columns, data diff --git a/openstackclient/network/v2/network_rbac.py b/openstackclient/network/v2/network_rbac.py index 1781193f..b88ef019 100644 --- a/openstackclient/network/v2/network_rbac.py +++ b/openstackclient/network/v2/network_rbac.py @@ -15,7 +15,6 @@ import logging -from osc_lib.cli import format_columns from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils @@ -28,11 +27,6 @@ from openstackclient.network import sdk_utils LOG = logging.getLogger(__name__) -_formatters = { - 'location': format_columns.DictColumn, -} - - def _get_columns(item): column_map = { 'target_tenant': 'target_project_id', @@ -58,6 +52,15 @@ def _get_attrs(client_manager, parsed_args): object_id = network_client.find_security_group( parsed_args.rbac_object, ignore_missing=False).id + if parsed_args.type == 'address_scope': + object_id = network_client.find_address_scope( + parsed_args.rbac_object, + ignore_missing=False).id + if parsed_args.type == 'subnetpool': + object_id = network_client.find_subnet_pool( + parsed_args.rbac_object, + ignore_missing=False).id + attrs['object_id'] = object_id identity_client = client_manager.identity @@ -97,9 +100,11 @@ class CreateNetworkRBAC(command.ShowOne): '--type', metavar="<type>", required=True, - choices=['security_group', 'qos_policy', 'network'], + choices=['address_scope', 'security_group', 'subnetpool', + 'qos_policy', 'network'], help=_('Type of the object that RBAC policy ' - 'affects ("security_group", "qos_policy" or "network")') + 'affects ("address_scope", "security_group", "subnetpool",' + ' "qos_policy" or "network")') ) parser.add_argument( '--action', @@ -142,7 +147,7 @@ class CreateNetworkRBAC(command.ShowOne): attrs = _get_attrs(self.app.client_manager, parsed_args) obj = client.create_rbac_policy(**attrs) display_columns, columns = _get_columns(obj) - data = utils.get_item_properties(obj, columns, formatters=_formatters) + data = utils.get_item_properties(obj, columns) return display_columns, data @@ -188,10 +193,11 @@ class ListNetworkRBAC(command.Lister): parser.add_argument( '--type', metavar='<type>', - choices=['security_group', 'qos_policy', 'network'], + choices=['address_scope', 'security_group', 'subnetpool', + 'qos_policy', 'network'], help=_('List network RBAC policies according to ' - 'given object type ("security_group", "qos_policy" ' - 'or "network")') + 'given object type ("address_scope", "security_group", ' + '"subnetpool", "qos_policy" or "network")') ) parser.add_argument( '--action', @@ -299,5 +305,5 @@ class ShowNetworkRBAC(command.ShowOne): obj = client.find_rbac_policy(parsed_args.rbac_policy, ignore_missing=False) display_columns, columns = _get_columns(obj) - data = utils.get_item_properties(obj, columns, formatters=_formatters) + data = utils.get_item_properties(obj, columns) return display_columns, data diff --git a/openstackclient/network/v2/network_segment.py b/openstackclient/network/v2/network_segment.py index 5899dc69..c1a672e2 100644 --- a/openstackclient/network/v2/network_segment.py +++ b/openstackclient/network/v2/network_segment.py @@ -15,7 +15,6 @@ import logging -from osc_lib.cli import format_columns from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils @@ -27,11 +26,6 @@ from openstackclient.network import sdk_utils LOG = logging.getLogger(__name__) -_formatters = { - 'location': format_columns.DictColumn, -} - - def _get_columns(item): return sdk_utils.get_osc_show_columns_for_sdk_resource(item, {}) @@ -96,7 +90,7 @@ class CreateNetworkSegment(command.ShowOne): attrs['segmentation_id'] = parsed_args.segment obj = client.create_segment(**attrs) display_columns, columns = _get_columns(obj) - data = utils.get_item_properties(obj, columns, formatters=_formatters) + data = utils.get_item_properties(obj, columns) return (display_columns, data) @@ -248,5 +242,5 @@ class ShowNetworkSegment(command.ShowOne): ignore_missing=False ) display_columns, columns = _get_columns(obj) - data = utils.get_item_properties(obj, columns, formatters=_formatters) + data = utils.get_item_properties(obj, columns) return (display_columns, data) diff --git a/openstackclient/network/v2/network_segment_range.py b/openstackclient/network/v2/network_segment_range.py index b38c72c2..6229995a 100644 --- a/openstackclient/network/v2/network_segment_range.py +++ b/openstackclient/network/v2/network_segment_range.py @@ -19,7 +19,6 @@ import itertools import logging -from osc_lib.cli import format_columns from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils @@ -31,10 +30,6 @@ from openstackclient.network import sdk_utils LOG = logging.getLogger(__name__) -_formatters = { - 'location': format_columns.DictColumn, -} - def _get_columns(item): return sdk_utils.get_osc_show_columns_for_sdk_resource(item, {}) @@ -216,7 +211,7 @@ class CreateNetworkSegmentRange(command.ShowOne): attrs['physical_network'] = parsed_args.physical_network obj = network_client.create_network_segment_range(**attrs) display_columns, columns = _get_columns(obj) - data = utils.get_item_properties(obj, columns, formatters=_formatters) + data = utils.get_item_properties(obj, columns) data = _update_additional_fields_from_props(columns, props=data) return (display_columns, data) @@ -455,6 +450,6 @@ class ShowNetworkSegmentRange(command.ShowOne): ignore_missing=False ) display_columns, columns = _get_columns(obj) - data = utils.get_item_properties(obj, columns, formatters=_formatters) + data = utils.get_item_properties(obj, columns) data = _update_additional_fields_from_props(columns, props=data) return (display_columns, data) diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index 4011f5f0..a21324ae 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -51,7 +51,6 @@ _formatters = { 'dns_assignment': format_columns.ListDictColumn, 'extra_dhcp_opts': format_columns.ListDictColumn, 'fixed_ips': format_columns.ListDictColumn, - 'location': format_columns.DictColumn, 'security_group_ids': format_columns.ListColumn, 'tags': format_columns.ListColumn, } diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index 464dbbec..e3e8accd 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -61,7 +61,6 @@ _formatters = { 'external_gateway_info': RouterInfoColumn, 'availability_zones': format_columns.ListColumn, 'availability_zone_hints': format_columns.ListColumn, - 'location': format_columns.DictColumn, 'routes': RoutesColumn, 'tags': format_columns.ListColumn, } @@ -168,6 +167,93 @@ class AddSubnetToRouter(command.Command): subnet_id=subnet.id) +class AddExtraRoutesToRouter(command.ShowOne): + _description = _("Add extra static routes to a router's routing table.") + + def get_parser(self, prog_name): + parser = super(AddExtraRoutesToRouter, self).get_parser(prog_name) + parser.add_argument( + 'router', + metavar='<router>', + help=_("Router to which extra static routes " + "will be added (name or ID).") + ) + parser.add_argument( + '--route', + metavar='destination=<subnet>,gateway=<ip-address>', + action=parseractions.MultiKeyValueAction, + dest='routes', + default=[], + required_keys=['destination', 'gateway'], + help=_("Add extra static route to the router. " + "destination: destination subnet (in CIDR notation), " + "gateway: nexthop IP address. " + "Repeat option to add multiple routes. " + "Trying to add a route that's already present " + "(exactly, including destination and nexthop) " + "in the routing table is allowed and is considered " + "a successful operation.") + ) + return parser + + def take_action(self, parsed_args): + if parsed_args.routes is not None: + for route in parsed_args.routes: + route['nexthop'] = route.pop('gateway') + client = self.app.client_manager.network + router_obj = client.add_extra_routes_to_router( + client.find_router(parsed_args.router, ignore_missing=False), + body={'router': {'routes': parsed_args.routes}}) + display_columns, columns = _get_columns(router_obj) + data = utils.get_item_properties( + router_obj, columns, formatters=_formatters) + return (display_columns, data) + + +class RemoveExtraRoutesFromRouter(command.ShowOne): + _description = _( + "Remove extra static routes from a router's routing table.") + + def get_parser(self, prog_name): + parser = super(RemoveExtraRoutesFromRouter, self).get_parser(prog_name) + parser.add_argument( + 'router', + metavar='<router>', + help=_("Router from which extra static routes " + "will be removed (name or ID).") + ) + parser.add_argument( + '--route', + metavar='destination=<subnet>,gateway=<ip-address>', + action=parseractions.MultiKeyValueAction, + dest='routes', + default=[], + required_keys=['destination', 'gateway'], + help=_("Remove extra static route from the router. " + "destination: destination subnet (in CIDR notation), " + "gateway: nexthop IP address. " + "Repeat option to remove multiple routes. " + "Trying to remove a route that's already missing " + "(fully, including destination and nexthop) " + "from the routing table is allowed and is considered " + "a successful operation.") + ) + return parser + + def take_action(self, parsed_args): + if parsed_args.routes is not None: + for route in parsed_args.routes: + route['nexthop'] = route.pop('gateway') + client = self.app.client_manager.network + router_obj = client.remove_extra_routes_from_router( + client.find_router(parsed_args.router, ignore_missing=False), + body={'router': {'routes': parsed_args.routes}}) + display_columns, columns = _get_columns(router_obj) + data = utils.get_item_properties( + router_obj, columns, formatters=_formatters) + return (display_columns, data) + + # TODO(yanxing'an): Use the SDK resource mapped attribute names once the # OSC minimum requirements include SDK 1.0. class CreateRouter(command.ShowOne): @@ -540,17 +626,21 @@ class SetRouter(command.Command): dest='routes', default=None, required_keys=['destination', 'gateway'], - help=_("Routes associated with the router " + help=_("Add routes to the router " "destination: destination subnet (in CIDR notation) " "gateway: nexthop IP address " - "(repeat option to set multiple routes)") + "(repeat option to add multiple routes). " + "This is deprecated in favor of 'router add/remove route' " + "since it is prone to race conditions between concurrent " + "clients when not used together with --no-route to " + "overwrite the current value of 'routes'.") ) parser.add_argument( '--no-route', action='store_true', help=_("Clear routes associated with the router. " "Specify both --route and --no-route to overwrite " - "current value of route.") + "current value of routes.") ) routes_ha = parser.add_mutually_exclusive_group() routes_ha.add_argument( diff --git a/openstackclient/network/v2/security_group.py b/openstackclient/network/v2/security_group.py index 2033af14..0732c23e 100644 --- a/openstackclient/network/v2/security_group.py +++ b/openstackclient/network/v2/security_group.py @@ -16,7 +16,6 @@ import argparse from cliff import columns as cliff_columns -from osc_lib.cli import format_columns from osc_lib.command import command from osc_lib import utils from osc_lib.utils import tags as _tag @@ -77,13 +76,11 @@ class ComputeSecurityGroupRulesColumn(cliff_columns.FormattableColumn): _formatters_network = { - 'location': format_columns.DictColumn, 'security_group_rules': NetworkSecurityGroupRulesColumn, } _formatters_compute = { - 'location': format_columns.DictColumn, 'rules': ComputeSecurityGroupRulesColumn, } @@ -120,6 +117,19 @@ class CreateSecurityGroup(common.NetworkAndComputeShowOne): metavar='<project>', help=self.enhance_help_neutron(_("Owner's project (name or ID)")) ) + stateful_group = parser.add_mutually_exclusive_group() + stateful_group.add_argument( + "--stateful", + action='store_true', + default=None, + help=_("Security group is stateful (Default)") + ) + stateful_group.add_argument( + "--stateless", + action='store_true', + default=None, + help=_("Security group is stateless") + ) identity_common.add_project_domain_option_to_parser( parser, enhance_help=self.enhance_help_neutron) _tag.add_tag_option_to_parser_for_create( @@ -138,6 +148,10 @@ class CreateSecurityGroup(common.NetworkAndComputeShowOne): attrs = {} attrs['name'] = parsed_args.name attrs['description'] = self._get_description(parsed_args) + if parsed_args.stateful: + attrs['stateful'] = True + if parsed_args.stateless: + attrs['stateful'] = False if parsed_args.project is not None: identity_client = self.app.client_manager.identity project_id = identity_common.find_project( @@ -315,6 +329,19 @@ class SetSecurityGroup(common.NetworkAndComputeCommand): metavar="<description>", help=_("New security group description") ) + stateful_group = parser.add_mutually_exclusive_group() + stateful_group.add_argument( + "--stateful", + action='store_true', + default=None, + help=_("Security group is stateful (Default)") + ) + stateful_group.add_argument( + "--stateless", + action='store_true', + default=None, + help=_("Security group is stateless") + ) return parser def update_parser_network(self, parser): @@ -331,6 +358,10 @@ class SetSecurityGroup(common.NetworkAndComputeCommand): attrs['name'] = parsed_args.name if parsed_args.description is not None: attrs['description'] = parsed_args.description + if parsed_args.stateful: + attrs['stateful'] = True + if parsed_args.stateless: + attrs['stateful'] = False # NOTE(rtheis): Previous behavior did not raise a CommandError # if there were no updates. Maintain this behavior and issue # the update. diff --git a/openstackclient/network/v2/security_group_rule.py b/openstackclient/network/v2/security_group_rule.py index f48478ea..1fbd97ab 100644 --- a/openstackclient/network/v2/security_group_rule.py +++ b/openstackclient/network/v2/security_group_rule.py @@ -16,7 +16,6 @@ import argparse import logging -from osc_lib.cli import format_columns from osc_lib.cli import parseractions from osc_lib import exceptions from osc_lib import utils @@ -31,11 +30,6 @@ from openstackclient.network import utils as network_utils LOG = logging.getLogger(__name__) -_formatters = { - 'location': format_columns.DictColumn, -} - - def _format_security_group_rule_show(obj): data = network_utils.transform_compute_security_group_rule(obj) return zip(*sorted(data.items())) @@ -353,7 +347,7 @@ class CreateSecurityGroupRule(common.NetworkAndComputeShowOne): # Create and show the security group rule. obj = client.create_security_group_rule(**attrs) display_columns, columns = _get_columns(obj) - data = utils.get_item_properties(obj, columns, formatters=_formatters) + data = utils.get_item_properties(obj, columns) return (display_columns, data) def take_action_compute(self, client, parsed_args): @@ -620,7 +614,7 @@ class ShowSecurityGroupRule(common.NetworkAndComputeShowOne): if not obj['remote_ip_prefix']: obj['remote_ip_prefix'] = _format_remote_ip_prefix(obj) display_columns, columns = _get_columns(obj) - data = utils.get_item_properties(obj, columns, formatters=_formatters) + data = utils.get_item_properties(obj, columns) return (display_columns, data) def take_action_compute(self, client, parsed_args): diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py index f6844065..f87f7abe 100644 --- a/openstackclient/network/v2/subnet.py +++ b/openstackclient/network/v2/subnet.py @@ -61,7 +61,6 @@ _formatters = { 'allocation_pools': AllocationPoolsColumn, 'dns_nameservers': format_columns.ListColumn, 'host_routes': HostRoutesColumn, - 'location': format_columns.DictColumn, 'service_types': format_columns.ListColumn, 'tags': format_columns.ListColumn, } diff --git a/openstackclient/network/v2/subnet_pool.py b/openstackclient/network/v2/subnet_pool.py index 2750574a..56cf6152 100644 --- a/openstackclient/network/v2/subnet_pool.py +++ b/openstackclient/network/v2/subnet_pool.py @@ -42,7 +42,6 @@ def _get_columns(item): _formatters = { - 'location': format_columns.DictColumn, 'prefixes': format_columns.ListColumn, 'tags': format_columns.ListColumn, } diff --git a/openstackclient/tests/functional/identity/v3/test_project.py b/openstackclient/tests/functional/identity/v3/test_project.py index 96d41c3a..27cf4481 100644 --- a/openstackclient/tests/functional/identity/v3/test_project.py +++ b/openstackclient/tests/functional/identity/v3/test_project.py @@ -79,7 +79,6 @@ class ProjectTests(common.IdentityTests): '--disable ' '--property k0=v0 ' '%(name)s' % {'new_name': new_project_name, - 'domain': self.domain_name, 'name': project_name}) self.assertEqual(0, len(raw_output)) # check project details diff --git a/openstackclient/tests/functional/network/v2/test_router.py b/openstackclient/tests/functional/network/v2/test_router.py index 05aad7a0..0769dca6 100644 --- a/openstackclient/tests/functional/network/v2/test_router.py +++ b/openstackclient/tests/functional/network/v2/test_router.py @@ -261,3 +261,46 @@ class RouterTests(common.NetworkTagTests): new_name )) self.assertIsNone(cmd_output["external_gateway_info"]) + + def test_router_add_remove_route(self): + network_name = uuid.uuid4().hex + subnet_name = uuid.uuid4().hex + router_name = uuid.uuid4().hex + + self.openstack('network create %s' % network_name) + self.addCleanup(self.openstack, 'network delete %s' % network_name) + + self.openstack( + 'subnet create %s ' + '--network %s --subnet-range 10.0.0.0/24' % ( + subnet_name, network_name)) + + self.openstack('router create %s' % router_name) + self.addCleanup(self.openstack, 'router delete %s' % router_name) + + self.openstack('router add subnet %s %s' % (router_name, subnet_name)) + self.addCleanup(self.openstack, 'router remove subnet %s %s' % ( + router_name, subnet_name)) + + out1 = json.loads(self.openstack( + 'router add route -f json %s ' + '--route destination=10.0.10.0/24,gateway=10.0.0.10' % + router_name)), + self.assertEqual(1, len(out1[0]['routes'])) + + self.addCleanup( + self.openstack, 'router set %s --no-route' % router_name) + + out2 = json.loads(self.openstack( + 'router add route -f json %s ' + '--route destination=10.0.10.0/24,gateway=10.0.0.10 ' + '--route destination=10.0.11.0/24,gateway=10.0.0.11' % + router_name)), + self.assertEqual(2, len(out2[0]['routes'])) + + out3 = json.loads(self.openstack( + 'router remove route -f json %s ' + '--route destination=10.0.11.0/24,gateway=10.0.0.11 ' + '--route destination=10.0.12.0/24,gateway=10.0.0.12' % + router_name)), + self.assertEqual(1, len(out3[0]['routes'])) diff --git a/openstackclient/tests/functional/network/v2/test_security_group.py b/openstackclient/tests/functional/network/v2/test_security_group.py index 8ae24b72..d46f8db7 100644 --- a/openstackclient/tests/functional/network/v2/test_security_group.py +++ b/openstackclient/tests/functional/network/v2/test_security_group.py @@ -42,7 +42,7 @@ class SecurityGroupTests(common.NetworkTests): def test_security_group_set(self): other_name = uuid.uuid4().hex raw_output = self.openstack( - 'security group set --description NSA --name ' + + 'security group set --description NSA --stateless --name ' + other_name + ' ' + self.NAME ) self.assertEqual('', raw_output) @@ -50,8 +50,10 @@ class SecurityGroupTests(common.NetworkTests): cmd_output = json.loads(self.openstack( 'security group show -f json ' + other_name)) self.assertEqual('NSA', cmd_output['description']) + self.assertFalse(cmd_output['stateful']) def test_security_group_show(self): cmd_output = json.loads(self.openstack( 'security group show -f json ' + self.NAME)) self.assertEqual(self.NAME, cmd_output['name']) + self.assertTrue(cmd_output['stateful']) diff --git a/openstackclient/tests/unit/api/test_object_store_v1.py b/openstackclient/tests/unit/api/test_object_store_v1.py index 96c68d5a..b9e0740c 100644 --- a/openstackclient/tests/unit/api/test_object_store_v1.py +++ b/openstackclient/tests/unit/api/test_object_store_v1.py @@ -30,8 +30,10 @@ FAKE_CONTAINER = 'rainbarrel' FAKE_OBJECT = 'spigot' LIST_CONTAINER_RESP = [ - 'qaz', - 'fred', + {"name": "qaz", "count": 0, "bytes": 0, + "last_modified": "2020-05-16T05:52:07.377550"}, + {"name": "fred", "count": 0, "bytes": 0, + "last_modified": "2020-05-16T05:55:07.377550"}, ] LIST_OBJECT_RESP = [ @@ -117,34 +119,32 @@ class TestContainer(TestObjectAPIv1): ) self.assertEqual(LIST_CONTAINER_RESP, ret) -# def test_container_list_full_listing(self): -# sess = self.app.client_manager.session -# -# def side_effect(*args, **kwargs): -# rv = sess.get().json.return_value -# sess.get().json.return_value = [] -# sess.get().json.side_effect = None -# return rv -# -# resp = [{'name': 'is-name'}] -# sess.get().json.return_value = resp -# sess.get().json.side_effect = side_effect -# -# data = lib_container.list_containers( -# self.app.client_manager.session, -# fake_url, -# full_listing=True, -# ) -# -# # Check expected values -# sess.get.assert_called_with( -# fake_url, -# params={ -# 'format': 'json', -# 'marker': 'is-name', -# } -# ) -# self.assertEqual(resp, data) + def test_container_list_full_listing(self): + self.requests_mock.register_uri( + 'GET', + FAKE_URL + '?limit=1&format=json', + json=[LIST_CONTAINER_RESP[0]], + status_code=200, + ) + self.requests_mock.register_uri( + 'GET', + FAKE_URL + + '?marker=%s&limit=1&format=json' % LIST_CONTAINER_RESP[0]['name'], + json=[LIST_CONTAINER_RESP[1]], + status_code=200, + ) + self.requests_mock.register_uri( + 'GET', + FAKE_URL + + '?marker=%s&limit=1&format=json' % LIST_CONTAINER_RESP[1]['name'], + json=[], + status_code=200, + ) + ret = self.api.container_list( + limit=1, + full_listing=True, + ) + self.assertEqual(LIST_CONTAINER_RESP, ret) def test_container_show(self): headers = { diff --git a/openstackclient/tests/unit/common/test_parseractions.py b/openstackclient/tests/unit/common/test_parseractions.py index d015da43..736cd0b6 100644 --- a/openstackclient/tests/unit/common/test_parseractions.py +++ b/openstackclient/tests/unit/common/test_parseractions.py @@ -92,7 +92,7 @@ class TestMultiKeyValueAction(utils.TestCase): {'req1': 'aaa', 'req2': 'bbb'}, {'req1': '', 'req2': ''}, ] - self.assertItemsEqual(expect, actual) + self.assertCountEqual(expect, actual) def test_empty_required_optional(self): self.parser.add_argument( @@ -116,7 +116,7 @@ class TestMultiKeyValueAction(utils.TestCase): {'req1': 'aaa', 'req2': 'bbb'}, {'req1': '', 'req2': ''}, ] - self.assertItemsEqual(expect, actual) + self.assertCountEqual(expect, actual) def test_error_values_with_comma(self): self.assertRaises( diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 8ec8217d..7e4c71c5 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -3577,6 +3577,41 @@ class TestServerRebuild(TestServer): self.cmd = server.RebuildServer(self.app, None) + def test_rebuild_with_image_name(self): + image_name = 'my-custom-image' + user_image = image_fakes.FakeImage.create_one_image( + attrs={'name': image_name}) + self.find_image_mock.return_value = user_image + + attrs = { + 'image': { + 'id': user_image.id + }, + 'networks': {}, + 'adminPass': 'passw0rd', + } + new_server = compute_fakes.FakeServer.create_one_server(attrs=attrs) + self.server.rebuild.return_value = new_server + + arglist = [ + self.server.id, + '--image', image_name + ] + verifylist = [ + ('server', self.server.id), + ('image', image_name) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # Get the command object to test. + self.cmd.take_action(parsed_args) + + self.servers_mock.get.assert_called_with(self.server.id) + self.find_image_mock.assert_called_with( + image_name, ignore_missing=False) + self.get_image_mock.assert_called_with(user_image.id) + self.server.rebuild.assert_called_with(user_image, None) + def test_rebuild_with_current_image(self): arglist = [ self.server.id, @@ -3590,6 +3625,7 @@ class TestServerRebuild(TestServer): self.cmd.take_action(parsed_args) self.servers_mock.get.assert_called_with(self.server.id) + self.find_image_mock.assert_not_called() self.get_image_mock.assert_called_with(self.image.id) self.server.rebuild.assert_called_with(self.image, None) diff --git a/openstackclient/tests/unit/identity/v3/test_access_rule.py b/openstackclient/tests/unit/identity/v3/test_access_rule.py index f8b6093a..904fe323 100644 --- a/openstackclient/tests/unit/identity/v3/test_access_rule.py +++ b/openstackclient/tests/unit/identity/v3/test_access_rule.py @@ -14,8 +14,8 @@ # import copy +from unittest import mock -import mock from osc_lib import exceptions from osc_lib import utils diff --git a/openstackclient/tests/unit/identity/v3/test_domain.py b/openstackclient/tests/unit/identity/v3/test_domain.py index 014986e5..46f389e8 100644 --- a/openstackclient/tests/unit/identity/v3/test_domain.py +++ b/openstackclient/tests/unit/identity/v3/test_domain.py @@ -68,6 +68,7 @@ class TestDomainCreate(TestDomain): kwargs = { 'name': self.domain.name, 'description': None, + 'options': {}, 'enabled': True, } self.domains_mock.create.assert_called_with( @@ -97,6 +98,7 @@ class TestDomainCreate(TestDomain): kwargs = { 'name': self.domain.name, 'description': 'new desc', + 'options': {}, 'enabled': True, } self.domains_mock.create.assert_called_with( @@ -126,6 +128,7 @@ class TestDomainCreate(TestDomain): kwargs = { 'name': self.domain.name, 'description': None, + 'options': {}, 'enabled': True, } self.domains_mock.create.assert_called_with( @@ -155,6 +158,7 @@ class TestDomainCreate(TestDomain): kwargs = { 'name': self.domain.name, 'description': None, + 'options': {}, 'enabled': False, } self.domains_mock.create.assert_called_with( @@ -164,6 +168,66 @@ class TestDomainCreate(TestDomain): self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) + def test_domain_create_with_immutable(self): + arglist = [ + '--immutable', + self.domain.name, + ] + verifylist = [ + ('immutable', True), + ('name', self.domain.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.domain.name, + 'description': None, + 'options': {'immutable': True}, + 'enabled': True, + } + self.domains_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_domain_create_with_no_immutable(self): + arglist = [ + '--no-immutable', + self.domain.name, + ] + verifylist = [ + ('no_immutable', True), + ('name', self.domain.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.domain.name, + 'description': None, + 'options': {'immutable': False}, + 'enabled': True, + } + self.domains_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + class TestDomainDelete(TestDomain): @@ -354,6 +418,52 @@ class TestDomainSet(TestDomain): ) self.assertIsNone(result) + def test_domain_set_immutable_option(self): + arglist = [ + '--immutable', + self.domain.id, + ] + verifylist = [ + ('immutable', True), + ('domain', self.domain.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'options': {'immutable': True}, + } + self.domains_mock.update.assert_called_with( + self.domain.id, + **kwargs + ) + self.assertIsNone(result) + + def test_domain_set_no_immutable_option(self): + arglist = [ + '--no-immutable', + self.domain.id, + ] + verifylist = [ + ('no_immutable', True), + ('domain', self.domain.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'options': {'immutable': False}, + } + self.domains_mock.update.assert_called_with( + self.domain.id, + **kwargs + ) + self.assertIsNone(result) + class TestDomainShow(TestDomain): diff --git a/openstackclient/tests/unit/identity/v3/test_project.py b/openstackclient/tests/unit/identity/v3/test_project.py index 466bea18..dfd0805b 100644 --- a/openstackclient/tests/unit/identity/v3/test_project.py +++ b/openstackclient/tests/unit/identity/v3/test_project.py @@ -98,7 +98,8 @@ class TestProjectCreate(TestProject): 'description': None, 'enabled': True, 'parent': None, - 'tags': [] + 'tags': [], + 'options': {}, } # ProjectManager.create(name=, domain=, description=, # enabled=, **kwargs) @@ -156,7 +157,8 @@ class TestProjectCreate(TestProject): 'description': 'new desc', 'enabled': True, 'parent': None, - 'tags': [] + 'tags': [], + 'options': {}, } # ProjectManager.create(name=, domain=, description=, # enabled=, **kwargs) @@ -194,7 +196,8 @@ class TestProjectCreate(TestProject): 'description': None, 'enabled': True, 'parent': None, - 'tags': [] + 'tags': [], + 'options': {}, } # ProjectManager.create(name=, domain=, description=, # enabled=, **kwargs) @@ -232,7 +235,8 @@ class TestProjectCreate(TestProject): 'description': None, 'enabled': True, 'parent': None, - 'tags': [] + 'tags': [], + 'options': {}, } self.projects_mock.create.assert_called_with( **kwargs @@ -266,7 +270,8 @@ class TestProjectCreate(TestProject): 'description': None, 'enabled': True, 'parent': None, - 'tags': [] + 'tags': [], + 'options': {}, } # ProjectManager.create(name=, domain=, description=, # enabled=, **kwargs) @@ -302,7 +307,8 @@ class TestProjectCreate(TestProject): 'description': None, 'enabled': False, 'parent': None, - 'tags': [] + 'tags': [], + 'options': {}, } # ProjectManager.create(name=, domain=, # description=, enabled=, **kwargs) @@ -339,7 +345,8 @@ class TestProjectCreate(TestProject): 'parent': None, 'fee': 'fi', 'fo': 'fum', - 'tags': [] + 'tags': [], + 'options': {}, } # ProjectManager.create(name=, domain=, description=, # enabled=, **kwargs) @@ -350,6 +357,126 @@ class TestProjectCreate(TestProject): self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) + def test_project_create_is_domain_false_property(self): + arglist = [ + '--property', 'is_domain=false', + self.project.name, + ] + verifylist = [ + ('parent', None), + ('enable', False), + ('disable', False), + ('name', self.project.name), + ('tags', []), + ('property', {'is_domain': 'false'}), + ('name', self.project.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.project.name, + 'domain': None, + 'description': None, + 'enabled': True, + 'parent': None, + 'is_domain': False, + 'tags': [], + 'options': {}, + } + self.projects_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_project_create_is_domain_true_property(self): + arglist = [ + '--property', 'is_domain=true', + self.project.name, + ] + verifylist = [ + ('parent', None), + ('enable', False), + ('disable', False), + ('name', self.project.name), + ('tags', []), + ('property', {'is_domain': 'true'}), + ('name', self.project.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.project.name, + 'domain': None, + 'description': None, + 'enabled': True, + 'parent': None, + 'is_domain': True, + 'tags': [], + 'options': {}, + } + self.projects_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_project_create_is_domain_none_property(self): + arglist = [ + '--property', 'is_domain=none', + self.project.name, + ] + verifylist = [ + ('parent', None), + ('enable', False), + ('disable', False), + ('name', self.project.name), + ('tags', []), + ('property', {'is_domain': 'none'}), + ('name', self.project.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.project.name, + 'domain': None, + 'description': None, + 'enabled': True, + 'parent': None, + 'is_domain': None, + 'tags': [], + 'options': {}, + } + self.projects_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + def test_project_create_parent(self): self.parent = identity_fakes.FakeProject.create_one_project() self.project = identity_fakes.FakeProject.create_one_project( @@ -380,7 +507,8 @@ class TestProjectCreate(TestProject): 'parent': self.parent.id, 'description': None, 'enabled': True, - 'tags': [] + 'tags': [], + 'options': {}, } self.projects_mock.create.assert_called_with( @@ -465,8 +593,89 @@ class TestProjectCreate(TestProject): 'description': None, 'enabled': True, 'parent': None, - 'tags': ['foo'] + 'tags': ['foo'], + 'options': {}, + } + self.projects_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_project_create_with_immutable_option(self): + arglist = [ + '--immutable', + self.project.name, + ] + verifylist = [ + ('immutable', True), + ('description', None), + ('enable', False), + ('disable', False), + ('name', self.project.name), + ('parent', None), + ('tags', []) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.project.name, + 'domain': None, + 'description': None, + 'enabled': True, + 'parent': None, + 'tags': [], + 'options': {'immutable': True}, } + # ProjectManager.create(name=, domain=, description=, + # enabled=, **kwargs) + self.projects_mock.create.assert_called_with( + **kwargs + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_project_create_with_no_immutable_option(self): + arglist = [ + '--no-immutable', + self.project.name, + ] + verifylist = [ + ('no_immutable', True), + ('description', None), + ('enable', False), + ('disable', False), + ('name', self.project.name), + ('parent', None), + ('tags', []) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': self.project.name, + 'domain': None, + 'description': None, + 'enabled': True, + 'parent': None, + 'tags': [], + 'options': {'immutable': False}, + } + # ProjectManager.create(name=, domain=, description=, + # enabled=, **kwargs) self.projects_mock.create.assert_called_with( **kwargs ) @@ -927,6 +1136,60 @@ class TestProjectSet(TestProject): ) self.assertIsNone(result) + def test_project_set_with_immutable_option(self): + arglist = [ + '--domain', self.project.domain_id, + '--immutable', + self.project.name, + ] + verifylist = [ + ('domain', self.project.domain_id), + ('immutable', True), + ('enable', False), + ('disable', False), + ('project', self.project.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'options': {'immutable': True}, + } + self.projects_mock.update.assert_called_with( + self.project.id, + **kwargs + ) + self.assertIsNone(result) + + def test_project_set_with_no_immutable_option(self): + arglist = [ + '--domain', self.project.domain_id, + '--no-immutable', + self.project.name, + ] + verifylist = [ + ('domain', self.project.domain_id), + ('no_immutable', True), + ('enable', False), + ('disable', False), + ('project', self.project.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'options': {'immutable': False}, + } + self.projects_mock.update.assert_called_with( + self.project.id, + **kwargs + ) + self.assertIsNone(result) + class TestProjectShow(TestProject): diff --git a/openstackclient/tests/unit/identity/v3/test_role.py b/openstackclient/tests/unit/identity/v3/test_role.py index 4278ab1c..544da7c1 100644 --- a/openstackclient/tests/unit/identity/v3/test_role.py +++ b/openstackclient/tests/unit/identity/v3/test_role.py @@ -333,6 +333,7 @@ class TestRoleCreate(TestRole): 'domain': None, 'name': identity_fakes.role_name, 'description': None, + 'options': {}, } # RoleManager.create(name=, domain=) @@ -377,6 +378,7 @@ class TestRoleCreate(TestRole): 'domain': identity_fakes.domain_id, 'name': identity_fakes.ROLE_2['name'], 'description': None, + 'options': {}, } # RoleManager.create(name=, domain=) @@ -420,6 +422,97 @@ class TestRoleCreate(TestRole): 'description': identity_fakes.role_description, 'name': identity_fakes.ROLE_2['name'], 'domain': None, + 'options': {}, + } + + # RoleManager.create(name=, domain=) + self.roles_mock.create.assert_called_with( + **kwargs + ) + + collist = ('domain', 'id', 'name') + self.assertEqual(collist, columns) + datalist = ( + 'd1', + identity_fakes.ROLE_2['id'], + identity_fakes.ROLE_2['name'], + ) + self.assertEqual(datalist, data) + + def test_role_create_with_immutable_option(self): + + self.roles_mock.create.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.ROLE_2), + loaded=True, + ) + arglist = [ + '--immutable', + identity_fakes.ROLE_2['name'], + ] + verifylist = [ + ('immutable', True), + ('name', identity_fakes.ROLE_2['name']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + + 'options': {'immutable': True}, + 'description': None, + 'name': identity_fakes.ROLE_2['name'], + 'domain': None, + } + + # RoleManager.create(name=, domain=) + self.roles_mock.create.assert_called_with( + **kwargs + ) + + collist = ('domain', 'id', 'name') + self.assertEqual(collist, columns) + datalist = ( + 'd1', + identity_fakes.ROLE_2['id'], + identity_fakes.ROLE_2['name'], + ) + self.assertEqual(datalist, data) + + def test_role_create_with_no_immutable_option(self): + + self.roles_mock.create.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.ROLE_2), + loaded=True, + ) + arglist = [ + '--no-immutable', + identity_fakes.ROLE_2['name'], + ] + verifylist = [ + ('no_immutable', True), + ('name', identity_fakes.ROLE_2['name']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns a two-part tuple with a tuple of column names and a tuple of + # data to be shown. + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + + 'options': {'immutable': False}, + 'description': None, + 'name': identity_fakes.ROLE_2['name'], + 'domain': None, } # RoleManager.create(name=, domain=) @@ -871,6 +964,7 @@ class TestRoleSet(TestRole): kwargs = { 'name': 'over', 'description': None, + 'options': {}, } # RoleManager.update(role, name=) self.roles_mock.update.assert_called_with( @@ -903,6 +997,7 @@ class TestRoleSet(TestRole): kwargs = { 'name': 'over', 'description': None, + 'options': {}, } # RoleManager.update(role, name=) self.roles_mock.update.assert_called_with( @@ -935,6 +1030,73 @@ class TestRoleSet(TestRole): kwargs = { 'name': 'over', 'description': identity_fakes.role_description, + 'options': {}, + } + # RoleManager.update(role, name=) + self.roles_mock.update.assert_called_with( + identity_fakes.ROLE_2['id'], + **kwargs + ) + self.assertIsNone(result) + + def test_role_set_with_immutable(self): + self.roles_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.ROLE_2), + loaded=True, + ) + arglist = [ + '--name', 'over', + '--immutable', + identity_fakes.ROLE_2['name'], + ] + verifylist = [ + ('name', 'over'), + ('immutable', True), + ('role', identity_fakes.ROLE_2['name']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': 'over', + 'description': None, + 'options': {'immutable': True}, + } + # RoleManager.update(role, name=) + self.roles_mock.update.assert_called_with( + identity_fakes.ROLE_2['id'], + **kwargs + ) + self.assertIsNone(result) + + def test_role_set_with_no_immutable(self): + self.roles_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes.ROLE_2), + loaded=True, + ) + arglist = [ + '--name', 'over', + '--no-immutable', + identity_fakes.ROLE_2['name'], + ] + verifylist = [ + ('name', 'over'), + ('no_immutable', True), + ('role', identity_fakes.ROLE_2['name']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'name': 'over', + 'description': None, + 'options': {'immutable': False}, } # RoleManager.update(role, name=) self.roles_mock.update.assert_called_with( diff --git a/openstackclient/tests/unit/integ/cli/test_shell.py b/openstackclient/tests/unit/integ/cli/test_shell.py index 0c98a129..5788b473 100644 --- a/openstackclient/tests/unit/integ/cli/test_shell.py +++ b/openstackclient/tests/unit/integ/cli/test_shell.py @@ -20,16 +20,6 @@ from openstackclient import shell from openstackclient.tests.unit.integ import base as test_base from openstackclient.tests.unit import test_shell -# NOTE(dtroyer): Attempt the import to detect if the SDK installed is new -# enough to contain the os_client_config code. If so, use -# that path for mocks. -CONFIG_MOCK_BASE = "openstack.config.loader" -try: - from openstack.config import defaults # noqa -except ImportError: - # Fall back to os-client-config - CONFIG_MOCK_BASE = "os_client_config.config" - class TestIntegShellCliNoAuth(test_base.TestInteg): @@ -455,8 +445,8 @@ class TestIntegShellCliPrecedenceOCC(test_base.TestInteg): temp_dir = self.useFixture(fixtures.TempDir()) return temp_dir.join(filename) - @mock.patch(CONFIG_MOCK_BASE + ".OpenStackConfig._load_vendor_file") - @mock.patch(CONFIG_MOCK_BASE + ".OpenStackConfig._load_config_file") + @mock.patch("openstack.config.loader.OpenStackConfig._load_vendor_file") + @mock.patch("openstack.config.loader.OpenStackConfig._load_config_file") def test_shell_args_precedence_1(self, config_mock, vendor_mock): """Precedence run 1 @@ -473,7 +463,6 @@ class TestIntegShellCliPrecedenceOCC(test_base.TestInteg): return ('file.yaml', copy.deepcopy(test_shell.PUBLIC_1)) vendor_mock.side_effect = vendor_mock_return - print("CONFIG_MOCK_BASE=%s" % CONFIG_MOCK_BASE) _shell = shell.OpenStackShell() _shell.run( "--os-password qaz extension list".split(), @@ -527,8 +516,8 @@ class TestIntegShellCliPrecedenceOCC(test_base.TestInteg): # +env, +cli, +occ # see test_shell_args_precedence_2() - @mock.patch(CONFIG_MOCK_BASE + ".OpenStackConfig._load_vendor_file") - @mock.patch(CONFIG_MOCK_BASE + ".OpenStackConfig._load_config_file") + @mock.patch("openstack.config.loader.OpenStackConfig._load_vendor_file") + @mock.patch("openstack.config.loader.OpenStackConfig._load_config_file") def test_shell_args_precedence_2(self, config_mock, vendor_mock): """Precedence run 2 @@ -545,7 +534,6 @@ class TestIntegShellCliPrecedenceOCC(test_base.TestInteg): return ('file.yaml', copy.deepcopy(test_shell.PUBLIC_1)) vendor_mock.side_effect = vendor_mock_return - print("CONFIG_MOCK_BASE=%s" % CONFIG_MOCK_BASE) _shell = shell.OpenStackShell() _shell.run( "--os-username zarquon --os-password qaz " diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py index a553f501..cef0a11c 100644 --- a/openstackclient/tests/unit/network/v2/fakes.py +++ b/openstackclient/tests/unit/network/v2/fakes.py @@ -1227,6 +1227,7 @@ class FakeSecurityGroup(object): 'id': 'security-group-id-' + uuid.uuid4().hex, 'name': 'security-group-name-' + uuid.uuid4().hex, 'description': 'security-group-description-' + uuid.uuid4().hex, + 'stateful': True, 'project_id': 'project-id-' + uuid.uuid4().hex, 'security_group_rules': [], 'tags': [] @@ -1832,7 +1833,7 @@ class FakeFloatingIPPortForwarding(object): """ attrs = attrs or {} floatingip_id = ( - attrs.get('floatingip_id') or'floating-ip-id-' + uuid.uuid4().hex + attrs.get('floatingip_id') or 'floating-ip-id-' + uuid.uuid4().hex ) # Set default attributes. port_forwarding_attrs = { @@ -1843,6 +1844,7 @@ class FakeFloatingIPPortForwarding(object): 'internal_port': randint(1, 65535), 'external_port': randint(1, 65535), 'protocol': 'tcp', + 'description': 'some description', } # Overwrite default attributes. diff --git a/openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py b/openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py index ea6cdd26..1028c18a 100644 --- a/openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py +++ b/openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py @@ -62,6 +62,7 @@ class TestCreateFloatingIPPortForwarding(TestFloatingIPPortForwarding): self.app, self.namespace) self.columns = ( + 'description', 'external_port', 'floatingip_id', 'id', @@ -73,6 +74,7 @@ class TestCreateFloatingIPPortForwarding(TestFloatingIPPortForwarding): ) self.data = ( + self.new_port_forwarding.description, self.new_port_forwarding.external_port, self.new_port_forwarding.floatingip_id, self.new_port_forwarding.id, @@ -102,6 +104,8 @@ class TestCreateFloatingIPPortForwarding(TestFloatingIPPortForwarding): self.new_port_forwarding.floatingip_id, '--internal-ip-address', self.new_port_forwarding.internal_ip_address, + '--description', + self.new_port_forwarding.description, ] verifylist = [ ('port', self.new_port_forwarding.internal_port_id), @@ -111,6 +115,7 @@ class TestCreateFloatingIPPortForwarding(TestFloatingIPPortForwarding): ('floating_ip', self.new_port_forwarding.floatingip_id), ('internal_ip_address', self.new_port_forwarding. internal_ip_address), + ('description', self.new_port_forwarding.description), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) @@ -126,6 +131,7 @@ class TestCreateFloatingIPPortForwarding(TestFloatingIPPortForwarding): 'internal_port_id': self.new_port_forwarding. internal_port_id, 'protocol': self.new_port_forwarding.protocol, + 'description': self.new_port_forwarding.description, }) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) @@ -251,7 +257,8 @@ class TestListFloatingIPPortForwarding(TestFloatingIPPortForwarding): 'Internal IP Address', 'Internal Port', 'External Port', - 'Protocol' + 'Protocol', + 'Description', ) def setUp(self): @@ -273,6 +280,7 @@ class TestListFloatingIPPortForwarding(TestFloatingIPPortForwarding): port_forwarding.internal_port, port_forwarding.external_port, port_forwarding.protocol, + port_forwarding.description, )) self.network.floating_ip_port_forwardings = mock.Mock( return_value=self.port_forwardings @@ -393,6 +401,7 @@ class TestSetFloatingIPPortForwarding(TestFloatingIPPortForwarding): '--internal-protocol-port', '100', '--external-protocol-port', '200', '--protocol', 'tcp', + '--description', 'some description', self._port_forwarding.floatingip_id, self._port_forwarding.id, ] @@ -402,6 +411,7 @@ class TestSetFloatingIPPortForwarding(TestFloatingIPPortForwarding): ('internal_protocol_port', 100), ('external_protocol_port', 200), ('protocol', 'tcp'), + ('description', 'some description'), ('floating_ip', self._port_forwarding.floatingip_id), ('port_forwarding_id', self._port_forwarding.id), ] @@ -415,6 +425,7 @@ class TestSetFloatingIPPortForwarding(TestFloatingIPPortForwarding): 'internal_port': 100, 'external_port': 200, 'protocol': 'tcp', + 'description': 'some description', } self.network.update_floating_ip_port_forwarding.assert_called_with( self._port_forwarding.floatingip_id, @@ -428,6 +439,7 @@ class TestShowFloatingIPPortForwarding(TestFloatingIPPortForwarding): # The port forwarding to show. columns = ( + 'description', 'external_port', 'floatingip_id', 'id', @@ -450,6 +462,7 @@ class TestShowFloatingIPPortForwarding(TestFloatingIPPortForwarding): ) ) self.data = ( + self._port_forwarding.description, self._port_forwarding.external_port, self._port_forwarding.floatingip_id, self._port_forwarding.id, diff --git a/openstackclient/tests/unit/network/v2/test_network.py b/openstackclient/tests/unit/network/v2/test_network.py index 45d6008b..5f8eed67 100644 --- a/openstackclient/tests/unit/network/v2/test_network.py +++ b/openstackclient/tests/unit/network/v2/test_network.py @@ -278,24 +278,6 @@ class TestCreateNetworkIdentityV3(TestNetwork): def test_create_with_no_tag(self): self._test_create_with_tag(add_tags=False) - def test_create_default_internal(self): - arglist = [ - self._network.name, - "--default", - ] - verifylist = [ - ('name', self._network.name), - ('enable', True), - ('share', None), - ('project', None), - ('external', False), - ('default', True), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.assertRaises(exceptions.CommandError, self.cmd.take_action, - parsed_args) - class TestCreateNetworkIdentityV2(TestNetwork): @@ -1043,21 +1025,6 @@ class TestSetNetwork(TestNetwork): def test_set_with_no_tag(self): self._test_set_tags(with_tags=False) - def test_set_default_internal(self): - arglist = [ - self._network.name, - '--internal', - '--default', - ] - verifylist = [ - ('internal', True), - ('default', True), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.assertRaises(exceptions.CommandError, self.cmd.take_action, - parsed_args) - class TestShowNetwork(TestNetwork): diff --git a/openstackclient/tests/unit/network/v2/test_network_rbac.py b/openstackclient/tests/unit/network/v2/test_network_rbac.py index 078188ce..d7c71ea7 100644 --- a/openstackclient/tests/unit/network/v2/test_network_rbac.py +++ b/openstackclient/tests/unit/network/v2/test_network_rbac.py @@ -14,6 +14,7 @@ from unittest import mock from unittest.mock import call +import ddt from osc_lib import exceptions from openstackclient.network.v2 import network_rbac @@ -33,11 +34,14 @@ class TestNetworkRBAC(network_fakes.TestNetworkV2): self.projects_mock = self.app.client_manager.identity.projects +@ddt.ddt class TestCreateNetworkRBAC(TestNetworkRBAC): network_object = network_fakes.FakeNetwork.create_one_network() qos_object = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() sg_object = network_fakes.FakeNetworkSecGroup.create_one_security_group() + as_object = network_fakes.FakeAddressScope.create_one_address_scope() + snp_object = network_fakes.FakeSubnetPool.create_one_subnet_pool() project = identity_fakes_v3.FakeProject.create_one_project() rbac_policy = network_fakes.FakeNetworkRBAC.create_one_network_rbac( attrs={'tenant_id': project.id, @@ -77,6 +81,10 @@ class TestCreateNetworkRBAC(TestNetworkRBAC): return_value=self.qos_object) self.network.find_security_group = mock.Mock( return_value=self.sg_object) + self.network.find_address_scope = mock.Mock( + return_value=self.as_object) + self.network.find_subnet_pool = mock.Mock( + return_value=self.snp_object) self.projects_mock.get.return_value = self.project def test_network_rbac_create_no_type(self): @@ -224,57 +232,29 @@ class TestCreateNetworkRBAC(TestNetworkRBAC): self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) - def test_network_rbac_create_qos_object(self): - self.rbac_policy.object_type = 'qos_policy' - self.rbac_policy.object_id = self.qos_object.id - arglist = [ - '--type', 'qos_policy', - '--action', self.rbac_policy.action, - '--target-project', self.rbac_policy.target_tenant, - self.qos_object.name, - ] - verifylist = [ - ('type', 'qos_policy'), - ('action', self.rbac_policy.action), - ('target_project', self.rbac_policy.target_tenant), - ('rbac_object', self.qos_object.name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # DisplayCommandBase.take_action() returns two tuples - columns, data = self.cmd.take_action(parsed_args) - - self.network.create_rbac_policy.assert_called_with(**{ - 'object_id': self.qos_object.id, - 'object_type': 'qos_policy', - 'action': self.rbac_policy.action, - 'target_tenant': self.rbac_policy.target_tenant, - }) - self.data = [ - self.rbac_policy.action, - self.rbac_policy.id, - self.qos_object.id, - 'qos_policy', - self.rbac_policy.tenant_id, - self.rbac_policy.target_tenant, - ] - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, list(data)) + @ddt.data( + ('qos_policy', "qos_object"), + ('security_group', "sg_object"), + ('subnetpool', "snp_object"), + ('address_scope', "as_object") + ) + @ddt.unpack + def test_network_rbac_create_object(self, obj_type, obj_fake_attr): + obj_fake = getattr(self, obj_fake_attr) - def test_network_rbac_create_security_group_object(self): - self.rbac_policy.object_type = 'security_group' - self.rbac_policy.object_id = self.sg_object.id + self.rbac_policy.object_type = obj_type + self.rbac_policy.object_id = obj_fake.id arglist = [ - '--type', 'security_group', + '--type', obj_type, '--action', self.rbac_policy.action, '--target-project', self.rbac_policy.target_tenant, - self.sg_object.name, + obj_fake.name, ] verifylist = [ - ('type', 'security_group'), + ('type', obj_type), ('action', self.rbac_policy.action), ('target_project', self.rbac_policy.target_tenant), - ('rbac_object', self.sg_object.name), + ('rbac_object', obj_fake.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -282,16 +262,16 @@ class TestCreateNetworkRBAC(TestNetworkRBAC): columns, data = self.cmd.take_action(parsed_args) self.network.create_rbac_policy.assert_called_with(**{ - 'object_id': self.sg_object.id, - 'object_type': 'security_group', + 'object_id': obj_fake.id, + 'object_type': obj_type, 'action': self.rbac_policy.action, 'target_tenant': self.rbac_policy.target_tenant, }) self.data = [ self.rbac_policy.action, self.rbac_policy.id, - self.sg_object.id, - 'security_group', + obj_fake.id, + obj_type, self.rbac_policy.tenant_id, self.rbac_policy.target_tenant, ] diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py index 38861b0a..09b4957c 100644 --- a/openstackclient/tests/unit/network/v2/test_router.py +++ b/openstackclient/tests/unit/network/v2/test_router.py @@ -776,6 +776,146 @@ class TestRemoveSubnetFromRouter(TestRouter): self.assertIsNone(result) +class TestAddExtraRoutesToRouter(TestRouter): + + _router = network_fakes.FakeRouter.create_one_router() + + def setUp(self): + super(TestAddExtraRoutesToRouter, self).setUp() + self.network.add_extra_routes_to_router = mock.Mock( + return_value=self._router) + self.cmd = router.AddExtraRoutesToRouter(self.app, self.namespace) + self.network.find_router = mock.Mock(return_value=self._router) + + def test_add_no_extra_route(self): + arglist = [ + self._router.id, + ] + verifylist = [ + ('router', self._router.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.network.add_extra_routes_to_router.assert_called_with( + self._router, body={'router': {'routes': []}}) + self.assertEqual(2, len(result)) + + def test_add_one_extra_route(self): + arglist = [ + self._router.id, + '--route', 'destination=dst1,gateway=gw1', + ] + verifylist = [ + ('router', self._router.id), + ('routes', [{'destination': 'dst1', 'gateway': 'gw1'}]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.network.add_extra_routes_to_router.assert_called_with( + self._router, body={'router': {'routes': [ + {'destination': 'dst1', 'nexthop': 'gw1'}, + ]}}) + self.assertEqual(2, len(result)) + + def test_add_multiple_extra_routes(self): + arglist = [ + self._router.id, + '--route', 'destination=dst1,gateway=gw1', + '--route', 'destination=dst2,gateway=gw2', + ] + verifylist = [ + ('router', self._router.id), + ('routes', [ + {'destination': 'dst1', 'gateway': 'gw1'}, + {'destination': 'dst2', 'gateway': 'gw2'}, + ]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.network.add_extra_routes_to_router.assert_called_with( + self._router, body={'router': {'routes': [ + {'destination': 'dst1', 'nexthop': 'gw1'}, + {'destination': 'dst2', 'nexthop': 'gw2'}, + ]}}) + self.assertEqual(2, len(result)) + + +class TestRemoveExtraRoutesFromRouter(TestRouter): + + _router = network_fakes.FakeRouter.create_one_router() + + def setUp(self): + super(TestRemoveExtraRoutesFromRouter, self).setUp() + self.network.remove_extra_routes_from_router = mock.Mock( + return_value=self._router) + self.cmd = router.RemoveExtraRoutesFromRouter(self.app, self.namespace) + self.network.find_router = mock.Mock(return_value=self._router) + + def test_remove_no_extra_route(self): + arglist = [ + self._router.id, + ] + verifylist = [ + ('router', self._router.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.network.remove_extra_routes_from_router.assert_called_with( + self._router, body={'router': {'routes': []}}) + self.assertEqual(2, len(result)) + + def test_remove_one_extra_route(self): + arglist = [ + self._router.id, + '--route', 'destination=dst1,gateway=gw1', + ] + verifylist = [ + ('router', self._router.id), + ('routes', [{'destination': 'dst1', 'gateway': 'gw1'}]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.network.remove_extra_routes_from_router.assert_called_with( + self._router, body={'router': {'routes': [ + {'destination': 'dst1', 'nexthop': 'gw1'}, + ]}}) + self.assertEqual(2, len(result)) + + def test_remove_multiple_extra_routes(self): + arglist = [ + self._router.id, + '--route', 'destination=dst1,gateway=gw1', + '--route', 'destination=dst2,gateway=gw2', + ] + verifylist = [ + ('router', self._router.id), + ('routes', [ + {'destination': 'dst1', 'gateway': 'gw1'}, + {'destination': 'dst2', 'gateway': 'gw2'}, + ]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.network.remove_extra_routes_from_router.assert_called_with( + self._router, body={'router': {'routes': [ + {'destination': 'dst1', 'nexthop': 'gw1'}, + {'destination': 'dst2', 'nexthop': 'gw2'}, + ]}}) + self.assertEqual(2, len(result)) + + class TestSetRouter(TestRouter): # The router to set. diff --git a/openstackclient/tests/unit/network/v2/test_security_group_network.py b/openstackclient/tests/unit/network/v2/test_security_group_network.py index 67908fa8..7c1d7fb6 100644 --- a/openstackclient/tests/unit/network/v2/test_security_group_network.py +++ b/openstackclient/tests/unit/network/v2/test_security_group_network.py @@ -49,6 +49,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork): 'name', 'project_id', 'rules', + 'stateful', 'tags', ) @@ -58,6 +59,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork): _security_group.name, _security_group.project_id, security_group.NetworkSecurityGroupRulesColumn([]), + _security_group.stateful, _security_group.tags, ) @@ -101,6 +103,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork): '--description', self._security_group.description, '--project', self.project.name, '--project-domain', self.domain.name, + '--stateful', self._security_group.name, ] verifylist = [ @@ -108,6 +111,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork): ('name', self._security_group.name), ('project', self.project.name), ('project_domain', self.domain.name), + ('stateful', self._security_group.stateful), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -115,6 +119,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork): self.network.create_security_group.assert_called_once_with(**{ 'description': self._security_group.description, + 'stateful': self._security_group.stateful, 'name': self._security_group.name, 'tenant_id': self.project.id, }) @@ -421,11 +426,13 @@ class TestSetSecurityGroupNetwork(TestSecurityGroupNetwork): arglist = [ '--name', new_name, '--description', new_description, + '--stateful', self._security_group.name, ] verifylist = [ ('description', new_description), ('group', self._security_group.name), + ('stateful', self._security_group.stateful), ('name', new_name), ] @@ -435,6 +442,7 @@ class TestSetSecurityGroupNetwork(TestSecurityGroupNetwork): attrs = { 'description': new_description, 'name': new_name, + 'stateful': True, } self.network.update_security_group.assert_called_once_with( self._security_group, @@ -489,6 +497,7 @@ class TestShowSecurityGroupNetwork(TestSecurityGroupNetwork): 'name', 'project_id', 'rules', + 'stateful', 'tags', ) @@ -499,6 +508,7 @@ class TestShowSecurityGroupNetwork(TestSecurityGroupNetwork): _security_group.project_id, security_group.NetworkSecurityGroupRulesColumn( [_security_group_rule._info]), + _security_group.stateful, _security_group.tags, ) |
