diff --git a/lower-constraints.txt b/lower-constraints.txt index 09aabede1f..861749b738 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -38,7 +38,7 @@ msgpack-python==0.4.0 munch==2.1.0 netaddr==0.7.18 netifaces==0.10.4 -openstacksdk==0.53.0 +openstacksdk==0.56.0 os-client-config==2.1.0 os-service-types==1.7.0 osc-lib==2.3.0 diff --git a/openstackclient/network/common.py b/openstackclient/network/common.py index 47ffbe77a6..b1902a6c9c 100644 --- a/openstackclient/network/common.py +++ b/openstackclient/network/common.py @@ -16,10 +16,12 @@ import contextlib import logging import openstack.exceptions +from osc_lib.cli import parseractions from osc_lib.command import command from osc_lib import exceptions from openstackclient.i18n import _ +from openstackclient.network import utils LOG = logging.getLogger(__name__) @@ -75,7 +77,6 @@ class NetDetectionMixin(metaclass=abc.ABCMeta): """ # Have we set it up yet for this command? if not hasattr(self, '_net_type'): - # import pdb; pdb.set_trace() try: if self.app.client_manager.is_network_endpoint_enabled(): net_type = _NET_TYPE_NEUTRON @@ -255,3 +256,74 @@ class NetworkAndComputeShowOne(NetDetectionMixin, command.ShowOne, if exc.details: msg += ", " + str(exc.details) raise exceptions.CommandError(msg) + + +class NeutronCommandWithExtraArgs(command.Command): + """Create and Update commands with additional extra properties. + + Extra properties can be passed to the command and are then send to the + Neutron as given to the command. + """ + + # dict of allowed types + _allowed_types_dict = { + 'bool': utils.str2bool, + 'dict': utils.str2dict, + 'list': utils.str2list, + 'int': int, + 'str': str, + } + + def _get_property_converter(self, _property): + if 'type' not in _property: + converter = str + else: + converter = self._allowed_types_dict.get(_property['type']) + + if not converter: + raise exceptions.CommandError( + _("Type {property_type} of property {name} " + "is not supported").format( + property_type=_property['type'], + name=_property['name'])) + return converter + + def _parse_extra_properties(self, extra_properties): + result = {} + if extra_properties: + for _property in extra_properties: + converter = self._get_property_converter(_property) + result[_property['name']] = converter(_property['value']) + return result + + def get_parser(self, prog_name): + parser = super(NeutronCommandWithExtraArgs, self).get_parser(prog_name) + parser.add_argument( + '--extra-property', + metavar='type=,name=,' + 'value=', + dest='extra_properties', + action=parseractions.MultiKeyValueAction, + required_keys=['name', 'value'], + optional_keys=['type'], + help=_("Additional parameters can be passed using this property. " + "Default type of the extra property is string ('str'), but " + "other types can be used as well. Available types are: " + "'dict', 'list', 'str', 'bool', 'int'. " + "In case of 'list' type, 'value' can be " + "semicolon-separated list of values. " + "For 'dict' value is semicolon-separated list of the " + "key:value pairs.") + ) + return parser + + +class NeutronUnsetCommandWithExtraArgs(NeutronCommandWithExtraArgs): + + def _parse_extra_properties(self, extra_properties): + result = {} + if extra_properties: + for _property in extra_properties: + result[_property['name']] = None + + return result diff --git a/openstackclient/network/utils.py b/openstackclient/network/utils.py index 287f027163..4d4d18e470 100644 --- a/openstackclient/network/utils.py +++ b/openstackclient/network/utils.py @@ -11,6 +11,10 @@ # under the License. # +from osc_lib import exceptions + +from openstackclient.i18n import _ + # Transform compute security group rule for display. def transform_compute_security_group_rule(sg_rule): @@ -39,3 +43,41 @@ def transform_compute_security_group_rule(sg_rule): else: info['remote_security_group'] = '' return info + + +def str2bool(strbool): + if strbool is None: + return None + return strbool.lower() == 'true' + + +def str2list(strlist): + result = [] + if strlist: + result = strlist.split(';') + return result + + +def str2dict(strdict): + """Convert key1:value1;key2:value2;... string into dictionary. + + :param strdict: string in the form of key1:value1;key2:value2 + """ + result = {} + if not strdict: + return result + i = 0 + kvlist = [] + for kv in strdict.split(';'): + if ':' in kv: + kvlist.append(kv) + i += 1 + elif i == 0: + msg = _("missing value for key '%s'") + raise exceptions.CommandError(msg % kv) + else: + kvlist[i - 1] = "%s;%s" % (kvlist[i - 1], kv) + for kv in kvlist: + key, sep, value = kv.partition(':') + result[key] = value + return result diff --git a/openstackclient/network/v2/address_group.py b/openstackclient/network/v2/address_group.py index c5b2f12606..fc83470053 100644 --- a/openstackclient/network/v2/address_group.py +++ b/openstackclient/network/v2/address_group.py @@ -22,6 +22,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 @@ -57,7 +58,7 @@ def _get_attrs(client_manager, parsed_args): return attrs -class CreateAddressGroup(command.ShowOne): +class CreateAddressGroup(command.ShowOne, common.NeutronCommandWithExtraArgs): _description = _("Create a new Address Group") def get_parser(self, prog_name): @@ -93,6 +94,9 @@ class CreateAddressGroup(command.ShowOne): client = self.app.client_manager.network attrs = _get_attrs(self.app.client_manager, parsed_args) + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) + obj = client.create_address_group(**attrs) display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns, formatters={}) @@ -191,7 +195,7 @@ class ListAddressGroup(command.Lister): ) for s in data)) -class SetAddressGroup(command.Command): +class SetAddressGroup(common.NeutronCommandWithExtraArgs): _description = _("Set address group properties") def get_parser(self, prog_name): @@ -231,6 +235,9 @@ class SetAddressGroup(command.Command): attrs['name'] = parsed_args.name if parsed_args.description is not None: attrs['description'] = parsed_args.description + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) + if attrs: client.update_address_group(obj, **attrs) if parsed_args.address: diff --git a/openstackclient/network/v2/address_scope.py b/openstackclient/network/v2/address_scope.py index 71c1a9afb7..cd27678ee9 100644 --- a/openstackclient/network/v2/address_scope.py +++ b/openstackclient/network/v2/address_scope.py @@ -21,6 +21,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 @@ -57,7 +58,7 @@ def _get_attrs(client_manager, parsed_args): # TODO(rtheis): Use the SDK resource mapped attribute names once the # OSC minimum requirements include SDK 1.0. -class CreateAddressScope(command.ShowOne): +class CreateAddressScope(command.ShowOne, common.NeutronCommandWithExtraArgs): _description = _("Create a new Address Scope") def get_parser(self, prog_name): @@ -98,6 +99,8 @@ class CreateAddressScope(command.ShowOne): def take_action(self, parsed_args): client = self.app.client_manager.network attrs = _get_attrs(self.app.client_manager, parsed_args) + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) obj = client.create_address_scope(**attrs) display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns, formatters={}) @@ -226,7 +229,7 @@ class ListAddressScope(command.Lister): # TODO(rtheis): Use the SDK resource mapped attribute names once the # OSC minimum requirements include SDK 1.0. -class SetAddressScope(command.Command): +class SetAddressScope(common.NeutronCommandWithExtraArgs): _description = _("Set address scope properties") def get_parser(self, prog_name): @@ -267,6 +270,8 @@ class SetAddressScope(command.Command): attrs['shared'] = True if parsed_args.no_share: attrs['shared'] = False + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) client.update_address_scope(obj, **attrs) diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py index a2765cd1ef..25b2a1baf6 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.command import command from osc_lib import utils from osc_lib.utils import tags as _tag @@ -94,7 +93,8 @@ def _get_attrs(client_manager, parsed_args): return attrs -class CreateFloatingIP(common.NetworkAndComputeShowOne): +class CreateFloatingIP(common.NetworkAndComputeShowOne, + common.NeutronCommandWithExtraArgs): _description = _("Create floating IP") def update_parser_common(self, parser): @@ -175,6 +175,8 @@ class CreateFloatingIP(common.NetworkAndComputeShowOne): def take_action_network(self, client, parsed_args): attrs = _get_attrs(self.app.client_manager, parsed_args) + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) with common.check_missing_extension_if_error( self.app.client_manager.network, attrs): obj = client.create_ip(**attrs) @@ -390,7 +392,7 @@ class ListFloatingIP(common.NetworkAndComputeLister): ) for s in data)) -class SetFloatingIP(command.Command): +class SetFloatingIP(common.NeutronCommandWithExtraArgs): _description = _("Set floating IP Properties") def get_parser(self, prog_name): @@ -456,6 +458,9 @@ class SetFloatingIP(command.Command): if 'no_qos_policy' in parsed_args and parsed_args.no_qos_policy: attrs['qos_policy_id'] = None + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) + if attrs: client.update_ip(obj, **attrs) @@ -490,7 +495,7 @@ class ShowFloatingIP(common.NetworkAndComputeShowOne): return (columns, data) -class UnsetFloatingIP(command.Command): +class UnsetFloatingIP(common.NeutronCommandWithExtraArgs): _description = _("Unset floating IP Properties") def get_parser(self, prog_name): @@ -526,6 +531,8 @@ class UnsetFloatingIP(command.Command): attrs['port_id'] = None if parsed_args.qos_policy: attrs['qos_policy_id'] = None + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) if attrs: client.update_ip(obj, **attrs) diff --git a/openstackclient/network/v2/floating_ip_port_forwarding.py b/openstackclient/network/v2/floating_ip_port_forwarding.py index 06b3df8bcd..71b0b7da63 100644 --- a/openstackclient/network/v2/floating_ip_port_forwarding.py +++ b/openstackclient/network/v2/floating_ip_port_forwarding.py @@ -19,6 +19,7 @@ from osc_lib import exceptions from osc_lib import utils from openstackclient.i18n import _ +from openstackclient.network import common from openstackclient.network import sdk_utils @@ -32,7 +33,8 @@ def _get_columns(item): return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) -class CreateFloatingIPPortForwarding(command.ShowOne): +class CreateFloatingIPPortForwarding(command.ShowOne, + common.NeutronCommandWithExtraArgs): _description = _("Create floating IP port forwarding") def get_parser(self, prog_name): @@ -122,6 +124,9 @@ class CreateFloatingIPPortForwarding(command.ShowOne): if parsed_args.description is not None: attrs['description'] = parsed_args.description + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) + obj = client.create_floating_ip_port_forwarding( floating_ip.id, **attrs @@ -258,7 +263,7 @@ class ListFloatingIPPortForwarding(command.Lister): ) for s in data)) -class SetFloatingIPPortForwarding(command.Command): +class SetFloatingIPPortForwarding(common.NeutronCommandWithExtraArgs): _description = _("Set floating IP Port Forwarding Properties") def get_parser(self, prog_name): @@ -352,6 +357,9 @@ class SetFloatingIPPortForwarding(command.Command): if parsed_args.description is not None: attrs['description'] = parsed_args.description + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) + client.update_floating_ip_port_forwarding( floating_ip.id, parsed_args.port_forwarding_id, **attrs) diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py index 7a12d523e2..b8eb9f014b 100644 --- a/openstackclient/network/v2/network.py +++ b/openstackclient/network/v2/network.py @@ -15,7 +15,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 utils from osc_lib.utils import tags as _tag @@ -189,7 +188,8 @@ def _add_additional_network_options(parser): # TODO(sindhu): Use the SDK resource mapped attribute names once the # OSC minimum requirements include SDK 1.0. -class CreateNetwork(common.NetworkAndComputeShowOne): +class CreateNetwork(common.NetworkAndComputeShowOne, + common.NeutronCommandWithExtraArgs): _description = _("Create new network") def update_parser_common(self, parser): @@ -334,6 +334,8 @@ class CreateNetwork(common.NetworkAndComputeShowOne): attrs['vlan_transparent'] = True if parsed_args.no_transparent_vlan: attrs['vlan_transparent'] = False + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) with common.check_missing_extension_if_error( self.app.client_manager.network, attrs): obj = client.create_network(**attrs) @@ -623,7 +625,7 @@ class ListNetwork(common.NetworkAndComputeLister): # TODO(sindhu): Use the SDK resource mapped attribute names once the # OSC minimum requirements include SDK 1.0. -class SetNetwork(command.Command): +class SetNetwork(common.NeutronCommandWithExtraArgs): _description = _("Set network properties") def get_parser(self, prog_name): @@ -728,6 +730,8 @@ class SetNetwork(command.Command): obj = client.find_network(parsed_args.network, ignore_missing=False) attrs = _get_attrs_network(self.app.client_manager, parsed_args) + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) if attrs: with common.check_missing_extension_if_error( self.app.client_manager.network, attrs): @@ -761,7 +765,7 @@ class ShowNetwork(common.NetworkAndComputeShowOne): return (display_columns, data) -class UnsetNetwork(command.Command): +class UnsetNetwork(common.NeutronUnsetCommandWithExtraArgs): _description = _("Unset network properties") def get_parser(self, prog_name): @@ -778,8 +782,9 @@ class UnsetNetwork(command.Command): client = self.app.client_manager.network obj = client.find_network(parsed_args.network, ignore_missing=False) - # NOTE: As of now, UnsetNetwork has no attributes which need - # to be updated by update_network(). + attrs = self._parse_extra_properties(parsed_args.extra_properties) + if attrs: + client.update_network(obj, **attrs) # tags is a subresource and it needs to be updated separately. _tag.update_tags_for_unset(client, obj, parsed_args) diff --git a/openstackclient/network/v2/network_flavor.py b/openstackclient/network/v2/network_flavor.py index c9d368bfc1..9e758ae29c 100644 --- a/openstackclient/network/v2/network_flavor.py +++ b/openstackclient/network/v2/network_flavor.py @@ -21,6 +21,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 @@ -88,7 +89,7 @@ class AddNetworkFlavorToProfile(command.Command): # TODO(dasanind): Use the SDK resource mapped attribute names once the # OSC minimum requirements include SDK 1.0. -class CreateNetworkFlavor(command.ShowOne): +class CreateNetworkFlavor(command.ShowOne, common.NeutronCommandWithExtraArgs): _description = _("Create new network flavor") def get_parser(self, prog_name): @@ -134,6 +135,8 @@ class CreateNetworkFlavor(command.ShowOne): def take_action(self, parsed_args): client = self.app.client_manager.network attrs = _get_attrs(self.app.client_manager, parsed_args) + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) obj = client.create_flavor(**attrs) display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns, formatters={}) @@ -234,7 +237,7 @@ class RemoveNetworkFlavorFromProfile(command.Command): # TODO(dasanind): Use only the SDK resource mapped attribute names once the # OSC minimum requirements include SDK 1.0. -class SetNetworkFlavor(command.Command): +class SetNetworkFlavor(common.NeutronCommandWithExtraArgs): _description = _("Set network flavor properties") def get_parser(self, prog_name): @@ -281,6 +284,8 @@ class SetNetworkFlavor(command.Command): attrs['enabled'] = True if parsed_args.disable: attrs['enabled'] = False + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) client.update_flavor(obj, **attrs) diff --git a/openstackclient/network/v2/network_flavor_profile.py b/openstackclient/network/v2/network_flavor_profile.py index 6cf0c4124d..0212e0d9ba 100644 --- a/openstackclient/network/v2/network_flavor_profile.py +++ b/openstackclient/network/v2/network_flavor_profile.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 @@ -60,7 +61,8 @@ def _get_attrs(client_manager, parsed_args): # TODO(ndahiwade): Use the SDK resource mapped attribute names once the # OSC minimum requirements include SDK 1.0. -class CreateNetworkFlavorProfile(command.ShowOne): +class CreateNetworkFlavorProfile(command.ShowOne, + common.NeutronCommandWithExtraArgs): _description = _("Create new network flavor profile") def get_parser(self, prog_name): @@ -103,6 +105,8 @@ class CreateNetworkFlavorProfile(command.ShowOne): def take_action(self, parsed_args): client = self.app.client_manager.network attrs = _get_attrs(self.app.client_manager, parsed_args) + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) if parsed_args.driver is None and parsed_args.metainfo is None: msg = _("Either --driver or --metainfo or both are required") @@ -180,7 +184,7 @@ class ListNetworkFlavorProfile(command.Lister): # TODO(ndahiwade): Use the SDK resource mapped attribute names once the # OSC minimum requirements include SDK 1.0. -class SetNetworkFlavorProfile(command.Command): +class SetNetworkFlavorProfile(common.NeutronCommandWithExtraArgs): _description = _("Set network flavor profile properties") def get_parser(self, prog_name): @@ -225,6 +229,8 @@ class SetNetworkFlavorProfile(command.Command): obj = client.find_service_profile(parsed_args.flavor_profile, ignore_missing=False) attrs = _get_attrs(self.app.client_manager, parsed_args) + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) client.update_service_profile(obj, **attrs) diff --git a/openstackclient/network/v2/network_meter.py b/openstackclient/network/v2/network_meter.py index df0e1da119..f8f188a806 100644 --- a/openstackclient/network/v2/network_meter.py +++ b/openstackclient/network/v2/network_meter.py @@ -21,6 +21,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 LOG = logging.getLogger(__name__) @@ -59,7 +60,7 @@ def _get_attrs(client_manager, parsed_args): # TODO(ankur-gupta-f): Use the SDK resource mapped attribute names once the # OSC minimum requirements include SDK 1.0. -class CreateMeter(command.ShowOne): +class CreateMeter(command.ShowOne, common.NeutronCommandWithExtraArgs): _description = _("Create network meter") def get_parser(self, prog_name): @@ -100,6 +101,8 @@ class CreateMeter(command.ShowOne): def take_action(self, parsed_args): client = self.app.client_manager.network attrs = _get_attrs(self.app.client_manager, parsed_args) + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) obj = client.create_metering_label(**attrs) display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns, formatters={}) diff --git a/openstackclient/network/v2/network_meter_rule.py b/openstackclient/network/v2/network_meter_rule.py index 1cf0395f44..06362fa14c 100644 --- a/openstackclient/network/v2/network_meter_rule.py +++ b/openstackclient/network/v2/network_meter_rule.py @@ -21,6 +21,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 LOG = logging.getLogger(__name__) @@ -64,7 +65,7 @@ def _get_attrs(client_manager, parsed_args): return attrs -class CreateMeterRule(command.ShowOne): +class CreateMeterRule(command.ShowOne, common.NeutronCommandWithExtraArgs): _description = _("Create a new meter rule") def get_parser(self, prog_name): @@ -130,6 +131,8 @@ class CreateMeterRule(command.ShowOne): ignore_missing=False) parsed_args.meter = _meter.id attrs = _get_attrs(self.app.client_manager, parsed_args) + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) obj = client.create_metering_label_rule(**attrs) display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns, formatters={}) diff --git a/openstackclient/network/v2/network_qos_policy.py b/openstackclient/network/v2/network_qos_policy.py index fd5ff93771..7300a5c0ac 100644 --- a/openstackclient/network/v2/network_qos_policy.py +++ b/openstackclient/network/v2/network_qos_policy.py @@ -21,6 +21,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 @@ -67,7 +68,8 @@ def _get_attrs(client_manager, parsed_args): # TODO(abhiraut): Use the SDK resource mapped attribute names once the # OSC minimum requirements include SDK 1.0. -class CreateNetworkQosPolicy(command.ShowOne): +class CreateNetworkQosPolicy(command.ShowOne, + common.NeutronCommandWithExtraArgs): _description = _("Create a QoS policy") def get_parser(self, prog_name): @@ -117,6 +119,8 @@ class CreateNetworkQosPolicy(command.ShowOne): def take_action(self, parsed_args): client = self.app.client_manager.network attrs = _get_attrs(self.app.client_manager, parsed_args) + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) obj = client.create_qos_policy(**attrs) display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns, formatters={}) @@ -209,7 +213,7 @@ class ListNetworkQosPolicy(command.Lister): # TODO(abhiraut): Use the SDK resource mapped attribute names once the # OSC minimum requirements include SDK 1.0. -class SetNetworkQosPolicy(command.Command): +class SetNetworkQosPolicy(common.NeutronCommandWithExtraArgs): _description = _("Set QoS policy properties") def get_parser(self, prog_name): @@ -259,6 +263,8 @@ class SetNetworkQosPolicy(command.Command): parsed_args.policy, ignore_missing=False) attrs = _get_attrs(self.app.client_manager, parsed_args) + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) client.update_qos_policy(obj, **attrs) diff --git a/openstackclient/network/v2/network_qos_rule.py b/openstackclient/network/v2/network_qos_rule.py index 2e4b385d2e..f30a5aeb1f 100644 --- a/openstackclient/network/v2/network_qos_rule.py +++ b/openstackclient/network/v2/network_qos_rule.py @@ -20,6 +20,7 @@ from osc_lib import exceptions from osc_lib import utils from openstackclient.i18n import _ +from openstackclient.network import common from openstackclient.network import sdk_utils @@ -171,7 +172,8 @@ def _add_rule_arguments(parser): ) -class CreateNetworkQosRule(command.ShowOne): +class CreateNetworkQosRule(command.ShowOne, + common.NeutronCommandWithExtraArgs): _description = _("Create new Network QoS rule") def get_parser(self, prog_name): @@ -198,6 +200,8 @@ class CreateNetworkQosRule(command.ShowOne): def take_action(self, parsed_args): network_client = self.app.client_manager.network attrs = _get_attrs(network_client, parsed_args, is_create=True) + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) try: obj = _rule_action_call( network_client, ACTION_CREATE, parsed_args.type)( @@ -285,7 +289,7 @@ class ListNetworkQosRule(command.Lister): (_get_item_properties(s, columns) for s in data)) -class SetNetworkQosRule(command.Command): +class SetNetworkQosRule(common.NeutronCommandWithExtraArgs): _description = _("Set Network QoS rule properties") def get_parser(self, prog_name): @@ -312,6 +316,8 @@ class SetNetworkQosRule(command.Command): if not rule_type: raise Exception('Rule not found') attrs = _get_attrs(network_client, parsed_args) + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) qos_id = attrs.pop('qos_policy_id') qos_rule = _rule_action_call(network_client, ACTION_FIND, rule_type)(attrs.pop('id'), qos_id) diff --git a/openstackclient/network/v2/network_rbac.py b/openstackclient/network/v2/network_rbac.py index 4984e89d56..692a43857d 100644 --- a/openstackclient/network/v2/network_rbac.py +++ b/openstackclient/network/v2/network_rbac.py @@ -21,6 +21,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 @@ -90,7 +91,7 @@ def _get_attrs(client_manager, parsed_args): # TODO(abhiraut): Use the SDK resource mapped attribute names once the # OSC minimum requirements include SDK 1.0. -class CreateNetworkRBAC(command.ShowOne): +class CreateNetworkRBAC(command.ShowOne, common.NeutronCommandWithExtraArgs): _description = _("Create network RBAC policy") def get_parser(self, prog_name): @@ -150,6 +151,8 @@ class CreateNetworkRBAC(command.ShowOne): def take_action(self, parsed_args): client = self.app.client_manager.network attrs = _get_attrs(self.app.client_manager, parsed_args) + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) obj = client.create_rbac_policy(**attrs) display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns) @@ -253,7 +256,7 @@ class ListNetworkRBAC(command.Lister): # TODO(abhiraut): Use the SDK resource mapped attribute names once the # OSC minimum requirements include SDK 1.0. -class SetNetworkRBAC(command.Command): +class SetNetworkRBAC(common.NeutronCommandWithExtraArgs): _description = _("Set network RBAC policy properties") def get_parser(self, prog_name): @@ -291,6 +294,8 @@ class SetNetworkRBAC(command.Command): parsed_args.target_project_domain, ).id attrs['target_tenant'] = project_id + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) client.update_rbac_policy(obj, **attrs) diff --git a/openstackclient/network/v2/network_segment.py b/openstackclient/network/v2/network_segment.py index c1a672e2d5..14a8edabe5 100644 --- a/openstackclient/network/v2/network_segment.py +++ b/openstackclient/network/v2/network_segment.py @@ -20,6 +20,7 @@ from osc_lib import exceptions from osc_lib import utils from openstackclient.i18n import _ +from openstackclient.network import common from openstackclient.network import sdk_utils @@ -30,7 +31,8 @@ def _get_columns(item): return sdk_utils.get_osc_show_columns_for_sdk_resource(item, {}) -class CreateNetworkSegment(command.ShowOne): +class CreateNetworkSegment(command.ShowOne, + common.NeutronCommandWithExtraArgs): _description = _("Create new network segment") def get_parser(self, prog_name): @@ -88,6 +90,8 @@ class CreateNetworkSegment(command.ShowOne): attrs['physical_network'] = parsed_args.physical_network if parsed_args.segment is not None: attrs['segmentation_id'] = parsed_args.segment + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) obj = client.create_segment(**attrs) display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns) @@ -189,7 +193,7 @@ class ListNetworkSegment(command.Lister): ) for s in data)) -class SetNetworkSegment(command.Command): +class SetNetworkSegment(common.NeutronCommandWithExtraArgs): _description = _("Set network segment properties") def get_parser(self, prog_name): @@ -220,6 +224,8 @@ class SetNetworkSegment(command.Command): attrs['description'] = parsed_args.description if parsed_args.name is not None: attrs['name'] = parsed_args.name + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) client.update_segment(obj, **attrs) diff --git a/openstackclient/network/v2/network_segment_range.py b/openstackclient/network/v2/network_segment_range.py index 6229995aab..ee414407ee 100644 --- a/openstackclient/network/v2/network_segment_range.py +++ b/openstackclient/network/v2/network_segment_range.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 common from openstackclient.network import sdk_utils @@ -87,7 +88,8 @@ def _update_additional_fields_from_props(columns, props): return props -class CreateNetworkSegmentRange(command.ShowOne): +class CreateNetworkSegmentRange(command.ShowOne, + common.NeutronCommandWithExtraArgs): _description = _("Create new network segment range") def get_parser(self, prog_name): @@ -209,6 +211,10 @@ class CreateNetworkSegmentRange(command.ShowOne): if parsed_args.physical_network: attrs['physical_network'] = parsed_args.physical_network + + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) + obj = network_client.create_network_segment_range(**attrs) display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns) @@ -365,7 +371,7 @@ class ListNetworkSegmentRange(command.Lister): return headers, display_props -class SetNetworkSegmentRange(command.Command): +class SetNetworkSegmentRange(common.NeutronCommandWithExtraArgs): _description = _("Set network segment range properties") def get_parser(self, prog_name): @@ -419,6 +425,8 @@ class SetNetworkSegmentRange(command.Command): attrs['minimum'] = parsed_args.minimum if parsed_args.maximum: attrs['maximum'] = parsed_args.maximum + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) network_client.update_network_segment_range(obj, **attrs) diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index 4feffc1df4..ecb2382a85 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -326,7 +326,7 @@ def _convert_extra_dhcp_options(parsed_args): return dhcp_options -class CreatePort(command.ShowOne): +class CreatePort(command.ShowOne, common.NeutronCommandWithExtraArgs): _description = _("Create a new port") def get_parser(self, prog_name): @@ -501,6 +501,9 @@ class CreatePort(command.ShowOne): if parsed_args.tags: attrs['tags'] = list(set(parsed_args.tags)) + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) + with common.check_missing_extension_if_error( self.app.client_manager.network, attrs): obj = client.create_port(**attrs) @@ -697,7 +700,7 @@ class ListPort(command.Lister): # TODO(abhiraut): Use the SDK resource mapped attribute names once the # OSC minimum requirements include SDK 1.0. -class SetPort(command.Command): +class SetPort(common.NeutronCommandWithExtraArgs): _description = _("Set port properties") def get_parser(self, prog_name): @@ -871,6 +874,9 @@ class SetPort(command.Command): if parsed_args.data_plane_status: attrs['data_plane_status'] = parsed_args.data_plane_status + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) + if attrs: with common.check_missing_extension_if_error( self.app.client_manager.network, attrs): @@ -902,7 +908,7 @@ class ShowPort(command.ShowOne): # TODO(abhiraut): Use the SDK resource mapped attribute names once the # OSC minimum requirements include SDK 1.0. -class UnsetPort(command.Command): +class UnsetPort(common.NeutronUnsetCommandWithExtraArgs): _description = _("Unset port properties") def get_parser(self, prog_name): @@ -1023,6 +1029,9 @@ class UnsetPort(command.Command): if parsed_args.numa_policy: attrs['numa_affinity_policy'] = None + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) + if attrs: client.update_port(obj, **attrs) diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index e3e8accd21..d15300a0b7 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -27,6 +27,7 @@ from osc_lib.utils import tags as _tag from openstackclient.i18n import _ from openstackclient.identity import common as identity_common +from openstackclient.network import common from openstackclient.network import sdk_utils @@ -256,7 +257,7 @@ class RemoveExtraRoutesFromRouter(command.ShowOne): # TODO(yanxing'an): Use the SDK resource mapped attribute names once the # OSC minimum requirements include SDK 1.0. -class CreateRouter(command.ShowOne): +class CreateRouter(command.ShowOne, common.NeutronCommandWithExtraArgs): _description = _("Create a new router") def get_parser(self, prog_name): @@ -332,6 +333,9 @@ class CreateRouter(command.ShowOne): attrs['ha'] = True if parsed_args.no_ha: attrs['ha'] = False + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) + obj = client.create_router(**attrs) # tags cannot be set when created, so tags need to be set later. _tag.update_tags_for_set(client, obj, parsed_args) @@ -576,7 +580,7 @@ class RemoveSubnetFromRouter(command.Command): # TODO(yanxing'an): Use the SDK resource mapped attribute names once the # OSC minimum requirements include SDK 1.0. -class SetRouter(command.Command): +class SetRouter(common.NeutronCommandWithExtraArgs): _description = _("Set router properties") def get_parser(self, prog_name): @@ -767,6 +771,10 @@ class SetRouter(command.Command): if 'no_qos_policy' in parsed_args and parsed_args.no_qos_policy: attrs['external_gateway_info']['qos_policy_id'] = None + + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) + if attrs: client.update_router(obj, **attrs) # tags is a subresource and it needs to be updated separately. @@ -809,7 +817,7 @@ class ShowRouter(command.ShowOne): return (display_columns, data) -class UnsetRouter(command.Command): +class UnsetRouter(common.NeutronUnsetCommandWithExtraArgs): _description = _("Unset router properties") def get_parser(self, prog_name): @@ -875,6 +883,10 @@ class UnsetRouter(command.Command): if parsed_args.external_gateway: attrs['external_gateway_info'] = {} + + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) + if attrs: client.update_router(obj, **attrs) # tags is a subresource and it needs to be updated separately. diff --git a/openstackclient/network/v2/security_group.py b/openstackclient/network/v2/security_group.py index 0732c23edc..49dc14e403 100644 --- a/openstackclient/network/v2/security_group.py +++ b/openstackclient/network/v2/security_group.py @@ -95,7 +95,8 @@ def _get_columns(item): # TODO(abhiraut): Use the SDK resource mapped attribute names once the # OSC minimum requirements include SDK 1.0. -class CreateSecurityGroup(common.NetworkAndComputeShowOne): +class CreateSecurityGroup(common.NetworkAndComputeShowOne, + common.NeutronCommandWithExtraArgs): _description = _("Create a new security group") def update_parser_common(self, parser): @@ -160,6 +161,8 @@ class CreateSecurityGroup(common.NetworkAndComputeShowOne): parsed_args.project_domain, ).id attrs['tenant_id'] = project_id + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) # Create the security group and display the results. obj = client.create_security_group(**attrs) @@ -310,7 +313,8 @@ class ListSecurityGroup(common.NetworkAndComputeLister): ) for s in data)) -class SetSecurityGroup(common.NetworkAndComputeCommand): +class SetSecurityGroup(common.NetworkAndComputeCommand, + common.NeutronCommandWithExtraArgs): _description = _("Set security group properties") def update_parser_common(self, parser): @@ -362,6 +366,8 @@ class SetSecurityGroup(common.NetworkAndComputeCommand): attrs['stateful'] = True if parsed_args.stateless: attrs['stateful'] = False + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) # 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 17241ed256..e273ded3d6 100644 --- a/openstackclient/network/v2/security_group_rule.py +++ b/openstackclient/network/v2/security_group_rule.py @@ -104,7 +104,8 @@ def _is_icmp_protocol(protocol): # TODO(abhiraut): Use the SDK resource mapped attribute names once the # OSC minimum requirements include SDK 1.0. -class CreateSecurityGroupRule(common.NetworkAndComputeShowOne): +class CreateSecurityGroupRule(common.NetworkAndComputeShowOne, + common.NeutronCommandWithExtraArgs): _description = _("Create a new security group rule") def update_parser_common(self, parser): @@ -355,6 +356,9 @@ class CreateSecurityGroupRule(common.NetworkAndComputeShowOne): ).id attrs['tenant_id'] = project_id + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) + # Create and show the security group rule. obj = client.create_security_group_rule(**attrs) display_columns, columns = _get_columns(obj) diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py index eccdd4e460..09fd7c7c39 100644 --- a/openstackclient/network/v2/subnet.py +++ b/openstackclient/network/v2/subnet.py @@ -26,6 +26,7 @@ from osc_lib.utils import tags as _tag from openstackclient.i18n import _ from openstackclient.identity import common as identity_common +from openstackclient.network import common from openstackclient.network import sdk_utils @@ -250,7 +251,7 @@ def _get_attrs(client_manager, parsed_args, is_create=True): # TODO(abhiraut): Use the SDK resource mapped attribute names once the # OSC minimum requirements include SDK 1.0. -class CreateSubnet(command.ShowOne): +class CreateSubnet(command.ShowOne, common.NeutronCommandWithExtraArgs): _description = _("Create a subnet") def get_parser(self, prog_name): @@ -373,6 +374,8 @@ class CreateSubnet(command.ShowOne): def take_action(self, parsed_args): client = self.app.client_manager.network attrs = _get_attrs(self.app.client_manager, parsed_args) + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) obj = client.create_subnet(**attrs) # tags cannot be set when created, so tags need to be set later. _tag.update_tags_for_set(client, obj, parsed_args) @@ -545,7 +548,7 @@ class ListSubnet(command.Lister): # TODO(abhiraut): Use the SDK resource mapped attribute names once the # OSC minimum requirements include SDK 1.0. -class SetSubnet(command.Command): +class SetSubnet(common.NeutronCommandWithExtraArgs): _description = _("Set subnet properties") def get_parser(self, prog_name): @@ -630,6 +633,8 @@ class SetSubnet(command.Command): attrs['allocation_pools'] = [] if 'service_types' in attrs: attrs['service_types'] += obj.service_types + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) if attrs: client.update_subnet(obj, **attrs) # tags is a subresource and it needs to be updated separately. @@ -657,7 +662,7 @@ class ShowSubnet(command.ShowOne): return (display_columns, data) -class UnsetSubnet(command.Command): +class UnsetSubnet(common.NeutronUnsetCommandWithExtraArgs): _description = _("Unset subnet properties") def get_parser(self, prog_name): @@ -744,6 +749,9 @@ class UnsetSubnet(command.Command): _update_arguments(attrs['service_types'], parsed_args.service_types, 'service-type') + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) + if attrs: client.update_subnet(obj, **attrs) diff --git a/openstackclient/network/v2/subnet_pool.py b/openstackclient/network/v2/subnet_pool.py index 56cf61526c..bdf7aba805 100644 --- a/openstackclient/network/v2/subnet_pool.py +++ b/openstackclient/network/v2/subnet_pool.py @@ -24,6 +24,7 @@ from osc_lib.utils import tags as _tag from openstackclient.i18n import _ from openstackclient.identity import common as identity_common +from openstackclient.network import common from openstackclient.network import sdk_utils @@ -146,7 +147,7 @@ def _add_default_options(parser): # TODO(rtheis): Use the SDK resource mapped attribute names once the # OSC minimum requirements include SDK 1.0. -class CreateSubnetPool(command.ShowOne): +class CreateSubnetPool(command.ShowOne, common.NeutronCommandWithExtraArgs): _description = _("Create subnet pool") def get_parser(self, prog_name): @@ -203,6 +204,8 @@ class CreateSubnetPool(command.ShowOne): # NeutronServer expects prefixes to be a List if "prefixes" not in attrs: attrs['prefixes'] = [] + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) obj = client.create_subnet_pool(**attrs) # tags cannot be set when created, so tags need to be set later. _tag.update_tags_for_set(client, obj, parsed_args) @@ -351,7 +354,7 @@ class ListSubnetPool(command.Lister): # TODO(rtheis): Use the SDK resource mapped attribute names once the # OSC minimum requirements include SDK 1.0. -class SetSubnetPool(command.Command): +class SetSubnetPool(common.NeutronCommandWithExtraArgs): _description = _("Set subnet pool properties") def get_parser(self, prog_name): @@ -408,6 +411,9 @@ class SetSubnetPool(command.Command): if 'prefixes' in attrs: attrs['prefixes'].extend(obj.prefixes) + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) + if attrs: client.update_subnet_pool(obj, **attrs) # tags is a subresource and it needs to be updated separately. diff --git a/openstackclient/tests/unit/network/test_common.py b/openstackclient/tests/unit/network/test_common.py index cde321aa23..4dde1b2bea 100644 --- a/openstackclient/tests/unit/network/test_common.py +++ b/openstackclient/tests/unit/network/test_common.py @@ -102,6 +102,27 @@ class FakeNetworkAndComputeShowOne(common.NetworkAndComputeShowOne): return client.compute_action(parsed_args) +class FakeCreateNeutronCommandWithExtraArgs( + common.NeutronCommandWithExtraArgs): + + def get_parser(self, prog_name): + parser = super(FakeCreateNeutronCommandWithExtraArgs, + self).get_parser(prog_name) + parser.add_argument( + '--known-attribute', + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = {} + if 'known_attribute' in parsed_args: + attrs['known_attribute'] = parsed_args.known_attribute + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties)) + client.test_create_action(**attrs) + + class TestNetworkAndCompute(utils.TestCommand): def setUp(self): @@ -187,3 +208,121 @@ class TestNetworkAndComputeShowOne(TestNetworkAndCompute): m_action.side_effect = openstack.exceptions.HttpException("bar") self.assertRaisesRegex(exceptions.CommandError, "bar", self.cmd.take_action, mock.Mock()) + + +class TestNeutronCommandWithExtraArgs(utils.TestCommand): + + def setUp(self): + super(TestNeutronCommandWithExtraArgs, self).setUp() + + self.namespace = argparse.Namespace() + + self.app.client_manager.network = mock.Mock() + self.network = self.app.client_manager.network + self.network.test_create_action = mock.Mock() + + # Subclasses can override the command object to test. + self.cmd = FakeCreateNeutronCommandWithExtraArgs( + self.app, self.namespace) + + def test_create_extra_attributes_default_type(self): + arglist = [ + '--known-attribute', 'known-value', + '--extra-property', 'name=extra_name,value=extra_value' + ] + verifylist = [ + ('known_attribute', 'known-value'), + ('extra_properties', [{'name': 'extra_name', + 'value': 'extra_value'}]) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + self.network.test_create_action.assert_called_with( + known_attribute='known-value', extra_name='extra_value') + + def test_create_extra_attributes_string(self): + arglist = [ + '--known-attribute', 'known-value', + '--extra-property', 'type=str,name=extra_name,value=extra_value' + ] + verifylist = [ + ('known_attribute', 'known-value'), + ('extra_properties', [{'name': 'extra_name', + 'type': 'str', + 'value': 'extra_value'}]) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + self.network.test_create_action.assert_called_with( + known_attribute='known-value', extra_name='extra_value') + + def test_create_extra_attributes_bool(self): + arglist = [ + '--known-attribute', 'known-value', + '--extra-property', 'type=bool,name=extra_name,value=TrUe' + ] + verifylist = [ + ('known_attribute', 'known-value'), + ('extra_properties', [{'name': 'extra_name', + 'type': 'bool', + 'value': 'TrUe'}]) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + self.network.test_create_action.assert_called_with( + known_attribute='known-value', extra_name=True) + + def test_create_extra_attributes_int(self): + arglist = [ + '--known-attribute', 'known-value', + '--extra-property', 'type=int,name=extra_name,value=8' + ] + verifylist = [ + ('known_attribute', 'known-value'), + ('extra_properties', [{'name': 'extra_name', + 'type': 'int', + 'value': '8'}]) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + self.network.test_create_action.assert_called_with( + known_attribute='known-value', extra_name=8) + + def test_create_extra_attributes_list(self): + arglist = [ + '--known-attribute', 'known-value', + '--extra-property', 'type=list,name=extra_name,value=v_1;v_2' + ] + verifylist = [ + ('known_attribute', 'known-value'), + ('extra_properties', [{'name': 'extra_name', + 'type': 'list', + 'value': 'v_1;v_2'}]) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + self.network.test_create_action.assert_called_with( + known_attribute='known-value', extra_name=['v_1', 'v_2']) + + def test_create_extra_attributes_dict(self): + arglist = [ + '--known-attribute', 'known-value', + '--extra-property', 'type=dict,name=extra_name,value=n1:v1;n2:v2' + ] + verifylist = [ + ('known_attribute', 'known-value'), + ('extra_properties', [{'name': 'extra_name', + 'type': 'dict', + 'value': 'n1:v1;n2:v2'}]) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + self.network.test_create_action.assert_called_with( + known_attribute='known-value', + extra_name={'n1': 'v1', 'n2': 'v2'}) diff --git a/openstackclient/tests/unit/network/test_utils.py b/openstackclient/tests/unit/network/test_utils.py new file mode 100644 index 0000000000..6252d7f766 --- /dev/null +++ b/openstackclient/tests/unit/network/test_utils.py @@ -0,0 +1,59 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from osc_lib import exceptions + +from openstackclient.network import utils +from openstackclient.tests.unit import utils as tests_utils + + +class TestUtils(tests_utils.TestCase): + + def test_str2bool(self): + self.assertTrue(utils.str2bool("true")) + self.assertTrue(utils.str2bool("True")) + self.assertTrue(utils.str2bool("TRUE")) + self.assertTrue(utils.str2bool("TrUe")) + + self.assertFalse(utils.str2bool("false")) + self.assertFalse(utils.str2bool("False")) + self.assertFalse(utils.str2bool("FALSE")) + self.assertFalse(utils.str2bool("FaLsE")) + self.assertFalse(utils.str2bool("Something else")) + self.assertFalse(utils.str2bool("")) + + self.assertIsNone(utils.str2bool(None)) + + def test_str2list(self): + self.assertEqual( + ['a', 'b', 'c'], utils.str2list("a;b;c")) + self.assertEqual( + ['abc'], utils.str2list("abc")) + + self.assertEqual([], utils.str2list("")) + self.assertEqual([], utils.str2list(None)) + + def test_str2dict(self): + self.assertEqual( + {'a': 'aaa', 'b': '2'}, + utils.str2dict('a:aaa;b:2')) + self.assertEqual( + {'a': 'aaa;b;c', 'd': 'ddd'}, + utils.str2dict('a:aaa;b;c;d:ddd')) + + self.assertEqual({}, utils.str2dict("")) + self.assertEqual({}, utils.str2dict(None)) + + self.assertRaises( + exceptions.CommandError, + utils.str2dict, "aaa;b:2") diff --git a/requirements.txt b/requirements.txt index d3a17e9a48..0ac991da20 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ pbr!=2.1.0,>=2.0.0 # Apache-2.0 cliff>=3.5.0 # Apache-2.0 iso8601>=0.1.11 # MIT -openstacksdk>=0.53.0 # Apache-2.0 +openstacksdk>=0.56.0 # Apache-2.0 osc-lib>=2.3.0 # Apache-2.0 oslo.i18n>=3.15.3 # Apache-2.0 oslo.utils>=3.33.0 # Apache-2.0