Skip to content

Commit c7d5823

Browse files
committed
compute: Improve 'server create --nic' option parsing
Simplify the parsing of this option by making use of a custom action. Change-Id: I670ff5109522d533ef4e62a79116e49a35c4e8fa Signed-off-by: Stephen Finucane <sfinucan@redhat.com>
1 parent d6c9b7f commit c7d5823

2 files changed

Lines changed: 185 additions & 140 deletions

File tree

openstackclient/compute/v2/server.py

Lines changed: 133 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -98,16 +98,6 @@ def _get_ip_address(addresses, address_type, ip_address_family):
9898
)
9999

100100

101-
def _prefix_checked_value(prefix):
102-
def func(value):
103-
if ',' in value or '=' in value:
104-
msg = _("Invalid argument %s, "
105-
"characters ',' and '=' are not allowed") % value
106-
raise argparse.ArgumentTypeError(msg)
107-
return prefix + value
108-
return func
109-
110-
111101
def _prep_server_detail(compute_client, image_client, server, refresh=True):
112102
"""Prepare the detailed server dict for printing
113103
@@ -611,6 +601,81 @@ def take_action(self, parsed_args):
611601
)
612602

613603

604+
# TODO(stephenfin): Replace with 'MultiKeyValueAction' when we no longer
605+
# support '--nic=auto' and '--nic=none'
606+
class NICAction(argparse.Action):
607+
608+
def __init__(
609+
self,
610+
option_strings,
611+
dest,
612+
nargs=None,
613+
const=None,
614+
default=None,
615+
type=None,
616+
choices=None,
617+
required=False,
618+
help=None,
619+
metavar=None,
620+
key=None,
621+
):
622+
self.key = key
623+
super().__init__(
624+
option_strings=option_strings, dest=dest, nargs=nargs, const=const,
625+
default=default, type=type, choices=choices, required=required,
626+
help=help, metavar=metavar,
627+
)
628+
629+
def __call__(self, parser, namespace, values, option_string=None):
630+
# Make sure we have an empty dict rather than None
631+
if getattr(namespace, self.dest, None) is None:
632+
setattr(namespace, self.dest, [])
633+
634+
# Handle the special auto/none cases
635+
if values in ('auto', 'none'):
636+
getattr(namespace, self.dest).append(values)
637+
return
638+
639+
if self.key:
640+
if ',' in values or '=' in values:
641+
msg = _(
642+
"Invalid argument %s; characters ',' and '=' are not "
643+
"allowed"
644+
)
645+
raise argparse.ArgumentTypeError(msg % values)
646+
647+
values = '='.join([self.key, values])
648+
649+
info = {
650+
'net-id': '',
651+
'port-id': '',
652+
'v4-fixed-ip': '',
653+
'v6-fixed-ip': '',
654+
}
655+
656+
for kv_str in values.split(','):
657+
k, sep, v = kv_str.partition("=")
658+
659+
if k not in info or not v:
660+
msg = _(
661+
"Invalid argument %s; argument must be of form "
662+
"'net-id=net-uuid,v4-fixed-ip=ip-addr,v6-fixed-ip=ip-addr,"
663+
"port-id=port-uuid'"
664+
)
665+
raise argparse.ArgumentTypeError(msg % values)
666+
667+
info[k] = v
668+
669+
if info['net-id'] and info['port-id']:
670+
msg = _(
671+
'Invalid argument %s; either network or port should be '
672+
'specified but not both'
673+
)
674+
raise argparse.ArgumentTypeError(msg % values)
675+
676+
getattr(namespace, self.dest).append(info)
677+
678+
614679
class CreateServer(command.ShowOne):
615680
_description = _("Create a new server")
616681

@@ -701,9 +766,10 @@ def get_parser(self, prog_name):
701766
parser.add_argument(
702767
'--network',
703768
metavar="<network>",
704-
action='append',
705-
dest='nic',
706-
type=_prefix_checked_value('net-id='),
769+
dest='nics',
770+
default=[],
771+
action=NICAction,
772+
key='net-id',
707773
# NOTE(RuiChen): Add '\n' to the end of line to improve formatting;
708774
# see cliff's _SmartHelpFormatter for more details.
709775
help=_(
@@ -719,9 +785,10 @@ def get_parser(self, prog_name):
719785
parser.add_argument(
720786
'--port',
721787
metavar="<port>",
722-
action='append',
723-
dest='nic',
724-
type=_prefix_checked_value('port-id='),
788+
dest='nics',
789+
default=[],
790+
action=NICAction,
791+
key='port-id',
725792
help=_(
726793
"Create a NIC on the server and connect it to port. "
727794
"Specify option multiple times to create multiple NICs. "
@@ -733,21 +800,28 @@ def get_parser(self, prog_name):
733800
)
734801
parser.add_argument(
735802
'--nic',
736-
metavar="<net-id=net-uuid,v4-fixed-ip=ip-addr,v6-fixed-ip=ip-addr,"
737-
"port-id=port-uuid,auto,none>",
738-
action='append',
803+
metavar="<net-id=net-uuid,port-id=port-uuid,v4-fixed-ip=ip-addr,"
804+
"v6-fixed-ip=ip-addr,auto,none>",
805+
action=NICAction,
806+
dest='nics',
807+
default=[],
739808
help=_(
740-
"Create a NIC on the server. "
741-
"Specify option multiple times to create multiple NICs. "
742-
"Either net-id or port-id must be provided, but not both. "
743-
"net-id: attach NIC to network with this UUID, "
744-
"port-id: attach NIC to port with this UUID, "
745-
"v4-fixed-ip: IPv4 fixed address for NIC (optional), "
746-
"v6-fixed-ip: IPv6 fixed address for NIC (optional), "
747-
"none: (v2.37+) no network is attached, "
809+
"Create a NIC on the server.\n"
810+
"NIC in the format:\n"
811+
"net-id=<net-uuid>: attach NIC to network with this UUID,\n"
812+
"port-id=<port-uuid>: attach NIC to port with this UUID,\n"
813+
"v4-fixed-ip=<ip-addr>: IPv4 fixed address for NIC (optional),"
814+
"\n"
815+
"v6-fixed-ip=<ip-addr>: IPv6 fixed address for NIC (optional),"
816+
"\n"
817+
"none: (v2.37+) no network is attached,\n"
748818
"auto: (v2.37+) the compute service will automatically "
749-
"allocate a network. Specifying a --nic of auto or none "
750-
"cannot be used with any other --nic value."
819+
"allocate a network.\n"
820+
"\n"
821+
"Specify option multiple times to create multiple NICs.\n"
822+
"Specifying a --nic of auto or none cannot be used with any "
823+
"other --nic value.\n"
824+
"Either net-id or port-id must be provided, but not both."
751825
),
752826
)
753827
parser.add_argument(
@@ -1103,84 +1177,55 @@ def _match_image(image_api, wanted_properties):
11031177
raise exceptions.CommandError(msg)
11041178
block_device_mapping_v2.append(mapping)
11051179

1106-
nics = []
1107-
auto_or_none = False
1108-
if parsed_args.nic is None:
1109-
parsed_args.nic = []
1110-
for nic_str in parsed_args.nic:
1111-
# Handle the special auto/none cases
1112-
if nic_str in ('auto', 'none'):
1113-
auto_or_none = True
1114-
nics.append(nic_str)
1115-
else:
1116-
nic_info = {
1117-
'net-id': '',
1118-
'v4-fixed-ip': '',
1119-
'v6-fixed-ip': '',
1120-
'port-id': '',
1121-
}
1122-
for kv_str in nic_str.split(","):
1123-
k, sep, v = kv_str.partition("=")
1124-
if k in nic_info and v:
1125-
nic_info[k] = v
1126-
else:
1127-
msg = _(
1128-
"Invalid nic argument '%s'. Nic arguments "
1129-
"must be of the form --nic <net-id=net-uuid"
1130-
",v4-fixed-ip=ip-addr,v6-fixed-ip=ip-addr,"
1131-
"port-id=port-uuid>."
1132-
)
1133-
raise exceptions.CommandError(msg % k)
1180+
nics = parsed_args.nics
11341181

1135-
if bool(nic_info["net-id"]) == bool(nic_info["port-id"]):
1136-
msg = _(
1137-
'Either network or port should be specified '
1138-
'but not both'
1139-
)
1140-
raise exceptions.CommandError(msg)
1182+
if 'auto' in nics or 'none' in nics:
1183+
if len(nics) > 1:
1184+
msg = _(
1185+
'Specifying a --nic of auto or none cannot '
1186+
'be used with any other --nic, --network '
1187+
'or --port value.'
1188+
)
1189+
raise exceptions.CommandError(msg)
11411190

1191+
nics = nics[0]
1192+
else:
1193+
for nic in nics:
11421194
if self.app.client_manager.is_network_endpoint_enabled():
11431195
network_client = self.app.client_manager.network
1144-
if nic_info["net-id"]:
1196+
1197+
if nic['net-id']:
11451198
net = network_client.find_network(
1146-
nic_info["net-id"], ignore_missing=False)
1147-
nic_info["net-id"] = net.id
1148-
if nic_info["port-id"]:
1199+
nic['net-id'], ignore_missing=False,
1200+
)
1201+
nic['net-id'] = net.id
1202+
1203+
if nic['port-id']:
11491204
port = network_client.find_port(
1150-
nic_info["port-id"], ignore_missing=False)
1151-
nic_info["port-id"] = port.id
1205+
nic['port-id'], ignore_missing=False,
1206+
)
1207+
nic['port-id'] = port.id
11521208
else:
1153-
if nic_info["net-id"]:
1154-
nic_info["net-id"] = compute_client.api.network_find(
1155-
nic_info["net-id"]
1209+
if nic['net-id']:
1210+
nic['net-id'] = compute_client.api.network_find(
1211+
nic['net-id'],
11561212
)['id']
1157-
if nic_info["port-id"]:
1213+
1214+
if nic['port-id']:
11581215
msg = _(
11591216
"Can't create server with port specified "
11601217
"since network endpoint not enabled"
11611218
)
11621219
raise exceptions.CommandError(msg)
11631220

1164-
nics.append(nic_info)
1165-
1166-
if nics:
1167-
if auto_or_none:
1168-
if len(nics) > 1:
1169-
msg = _(
1170-
'Specifying a --nic of auto or none cannot '
1171-
'be used with any other --nic, --network '
1172-
'or --port value.'
1173-
)
1174-
raise exceptions.CommandError(msg)
1175-
nics = nics[0]
1176-
else:
1221+
if not nics:
11771222
# Compute API version >= 2.37 requires a value, so default to
11781223
# 'auto' to maintain legacy behavior if a nic wasn't specified.
11791224
if compute_client.api_version >= api_versions.APIVersion('2.37'):
11801225
nics = 'auto'
11811226
else:
1182-
# Default to empty list if nothing was specified, let nova
1183-
# side to decide the default behavior.
1227+
# Default to empty list if nothing was specified and let nova
1228+
# decide the default behavior.
11841229
nics = []
11851230

11861231
# Check security group exist and convert ID to name

0 commit comments

Comments
 (0)