From 91734d2b87064879c3aab76de4f732e7f8d8f2b2 Mon Sep 17 00:00:00 2001 From: gongysh <gongysh@cn.ibm.com> Date: Tue, 11 Dec 2012 23:40:16 +0800 Subject: [PATCH] Allow known options defined after position arguments. We run the argument parser to split known options and unknown options. Make '-' work and have the same effect as '_' in both known and unknown option parts. Make metavar Uppercase. blueprint options-location Change-Id: Ic27b278484133c8b83e3b031a0810a76b050219f --- quantumclient/quantum/v2_0/__init__.py | 70 ++++++++++++++++------ quantumclient/quantum/v2_0/floatingip.py | 4 +- quantumclient/quantum/v2_0/network.py | 2 +- quantumclient/quantum/v2_0/port.py | 2 +- quantumclient/quantum/v2_0/router.py | 2 +- quantumclient/quantum/v2_0/subnet.py | 8 +-- quantumclient/shell.py | 40 +++++++++++++ quantumclient/tests/unit/test_casual_args.py | 2 +- quantumclient/tests/unit/test_cli20.py | 5 +- quantumclient/tests/unit/test_cli20_floatingips.py | 4 +- quantumclient/tests/unit/test_cli20_subnet.py | 61 +++++++++++++++++++ 11 files changed, 168 insertions(+), 32 deletions(-) diff --git a/quantumclient/quantum/v2_0/__init__.py b/quantumclient/quantum/v2_0/__init__.py index c73795b..5eae661 100644 --- a/quantumclient/quantum/v2_0/__init__.py +++ b/quantumclient/quantum/v2_0/__init__.py @@ -77,11 +77,17 @@ def add_show_list_common_argument(parser): action='store_true', help=argparse.SUPPRESS) parser.add_argument( - '-F', '--fields', + '--fields', + help=argparse.SUPPRESS, + action='append', + default=[]) + parser.add_argument( + '-F', '--field', + dest='fields', metavar='FIELD', help='specify the field(s) to be returned by server,' ' can be repeated', action='append', - default=[], ) + default=[]) def add_extra_argument(parser, name, _help): @@ -108,15 +114,16 @@ def parse_args_to_dict(values_specs): ''' # -- is a pseudo argument - if values_specs and values_specs[0] == '--': - del values_specs[0] + values_specs_copy = values_specs[:] + if values_specs_copy and values_specs_copy[0] == '--': + del values_specs_copy[0] _options = {} current_arg = None _values_specs = [] _value_number = 0 _list_flag = False current_item = None - for _item in values_specs: + for _item in values_specs_copy: if _item.startswith('--'): if current_arg is not None: if _value_number > 1 or _list_flag: @@ -166,22 +173,49 @@ def parse_args_to_dict(values_specs): current_arg.update({'nargs': '+'}) elif _value_number == 0: current_arg.update({'action': 'store_true'}) - _parser = argparse.ArgumentParser(add_help=False) - for opt, optspec in _options.iteritems(): - _parser.add_argument(opt, **optspec) - _args = _parser.parse_args(_values_specs) + _args = None + if _values_specs: + _parser = argparse.ArgumentParser(add_help=False) + for opt, optspec in _options.iteritems(): + _parser.add_argument(opt, **optspec) + _args = _parser.parse_args(_values_specs) result_dict = {} - for opt in _options.iterkeys(): - _opt = opt.split('--', 2)[1] - _value = getattr(_args, _opt.replace('-', '_')) - if _value is not None: - result_dict.update({_opt: _value}) + if _args: + for opt in _options.iterkeys(): + _opt = opt.split('--', 2)[1] + _opt = _opt.replace('-', '_') + _value = getattr(_args, _opt) + if _value is not None: + result_dict.update({_opt: _value}) return result_dict +def _merge_args(qCmd, parsed_args, _extra_values, value_specs): + """Merge arguments from _extra_values into parsed_args. + + If an argument value are provided in both and it is a list, + the values in _extra_values will be merged into parsed_args. + + @param parsed_args: the parsed args from known options + @param _extra_values: the other parsed arguments in unknown parts + @param values_specs: the unparsed unknown parts + """ + temp_values = _extra_values.copy() + for key, value in temp_values.iteritems(): + if hasattr(parsed_args, key): + arg_value = getattr(parsed_args, key) + if arg_value is not None and value is not None: + if isinstance(arg_value, list): + if value and isinstance(value, list): + if type(arg_value[0]) == type(value[0]): + arg_value.extend(value) + _extra_values.pop(key) + + class QuantumCommand(command.OpenStackCommand): api = 'network' log = logging.getLogger(__name__ + '.QuantumCommand') + values_specs = [] def get_client(self): return self.app.client_manager.quantum @@ -227,14 +261,12 @@ class CreateCommand(QuantumCommand, show.ShowOne): def get_parser(self, prog_name): parser = super(CreateCommand, self).get_parser(prog_name) parser.add_argument( - '--tenant-id', metavar='tenant-id', + '--tenant-id', metavar='TENANT_ID', help=_('the owner tenant ID'), ) parser.add_argument( '--tenant_id', help=argparse.SUPPRESS) self.add_known_arguments(parser) - add_extra_argument(parser, 'value_specs', - 'new values for the %s' % self.resource) return parser def add_known_arguments(self, parser): @@ -247,8 +279,10 @@ class CreateCommand(QuantumCommand, show.ShowOne): self.log.debug('get_data(%s)' % parsed_args) quantum_client = self.get_client() quantum_client.format = parsed_args.request_format + _extra_values = parse_args_to_dict(self.values_specs) + _merge_args(self, parsed_args, _extra_values, + self.values_specs) body = self.args2body(parsed_args) - _extra_values = parse_args_to_dict(parsed_args.value_specs) body[self.resource].update(_extra_values) obj_creator = getattr(quantum_client, "create_%s" % self.resource) diff --git a/quantumclient/quantum/v2_0/floatingip.py b/quantumclient/quantum/v2_0/floatingip.py index 5613af8..3198dfe 100644 --- a/quantumclient/quantum/v2_0/floatingip.py +++ b/quantumclient/quantum/v2_0/floatingip.py @@ -52,8 +52,8 @@ class CreateFloatingIP(CreateCommand): def add_known_arguments(self, parser): parser.add_argument( - 'floating_network_id', - help='Network to allocate floating IP from') + 'floating_network_id', metavar='FLOATING_NETWORK', + help='Network name or id to allocate floating IP from') parser.add_argument( '--port-id', help='ID of the port to be associated with the floatingip') diff --git a/quantumclient/quantum/v2_0/network.py b/quantumclient/quantum/v2_0/network.py index 19b9978..4d88ab3 100644 --- a/quantumclient/quantum/v2_0/network.py +++ b/quantumclient/quantum/v2_0/network.py @@ -84,7 +84,7 @@ class CreateNetwork(CreateCommand): default=argparse.SUPPRESS, help='Set the network as shared') parser.add_argument( - 'name', metavar='name', + 'name', metavar='NAME', help='Name of network to create') def args2body(self, parsed_args): diff --git a/quantumclient/quantum/v2_0/port.py b/quantumclient/quantum/v2_0/port.py index 7f39365..98661b5 100644 --- a/quantumclient/quantum/v2_0/port.py +++ b/quantumclient/quantum/v2_0/port.py @@ -112,7 +112,7 @@ class CreatePort(CreateCommand): action='append', help='desired IP for this port: ' 'subnet_id=<name_or_id>,ip_address=<ip>, ' - 'can be repeated') + '(This option can be repeated.)') parser.add_argument( '--fixed_ip', action='append', diff --git a/quantumclient/quantum/v2_0/router.py b/quantumclient/quantum/v2_0/router.py index db85b80..a0de0c8 100644 --- a/quantumclient/quantum/v2_0/router.py +++ b/quantumclient/quantum/v2_0/router.py @@ -68,7 +68,7 @@ class CreateRouter(CreateCommand): action='store_false', help=argparse.SUPPRESS) parser.add_argument( - 'name', metavar='name', + 'name', metavar='NAME', help='Name of router to create') def args2body(self, parsed_args): diff --git a/quantumclient/quantum/v2_0/subnet.py b/quantumclient/quantum/v2_0/subnet.py index 390f7bf..9339ff5 100644 --- a/quantumclient/quantum/v2_0/subnet.py +++ b/quantumclient/quantum/v2_0/subnet.py @@ -91,7 +91,7 @@ class CreateSubnet(CreateCommand): choices=[4, 6], help=argparse.SUPPRESS) parser.add_argument( - '--gateway', metavar='gateway', + '--gateway', metavar='GATEWAY_IP', help='gateway ip of this subnet') parser.add_argument( '--no-gateway', @@ -120,10 +120,10 @@ class CreateSubnet(CreateCommand): action='store_true', help='Disable DHCP for this subnet') parser.add_argument( - 'network_id', metavar='network', - help='Network id or name this subnet belongs to') + 'network_id', metavar='NETWORK', + help='network id or name this subnet belongs to') parser.add_argument( - 'cidr', metavar='cidr', + 'cidr', metavar='CIDR', help='cidr of subnet to create') def args2body(self, parsed_args): diff --git a/quantumclient/shell.py b/quantumclient/shell.py index 36f3e0c..a95bf09 100644 --- a/quantumclient/shell.py +++ b/quantumclient/shell.py @@ -349,6 +349,46 @@ class QuantumShell(App): result = self.run_subcommand(remainder) return result + def run_subcommand(self, argv): + subcommand = self.command_manager.find_command(argv) + cmd_factory, cmd_name, sub_argv = subcommand + cmd = cmd_factory(self, self.options) + err = None + result = 1 + try: + self.prepare_to_run_command(cmd) + full_name = (cmd_name + if self.interactive_mode + else ' '.join([self.NAME, cmd_name]) + ) + cmd_parser = cmd.get_parser(full_name) + known_args, values_specs = cmd_parser.parse_known_args(sub_argv) + cmd.values_specs = values_specs + result = cmd.run(known_args) + except Exception as err: + if self.options.debug: + self.log.exception(err) + else: + self.log.error(err) + try: + self.clean_up(cmd, result, err) + except Exception as err2: + if self.options.debug: + self.log.exception(err2) + else: + self.log.error('Could not clean up: %s', err2) + if self.options.debug: + raise + else: + try: + self.clean_up(cmd, result, None) + except Exception as err3: + if self.options.debug: + self.log.exception(err3) + else: + self.log.error('Could not clean up: %s', err3) + return result + def authenticate_user(self): """Make sure the user has provided all of the authentication info we need. diff --git a/quantumclient/tests/unit/test_casual_args.py b/quantumclient/tests/unit/test_casual_args.py index 4192ff8..4eb2b66 100644 --- a/quantumclient/tests/unit/test_casual_args.py +++ b/quantumclient/tests/unit/test_casual_args.py @@ -35,7 +35,7 @@ class CLITestArgs(unittest.TestCase): def test_bool_true(self): _specs = ['--my-bool', 'type=bool', 'true', '--arg1', 'value1'] _mydict = quantumV20.parse_args_to_dict(_specs) - self.assertTrue(_mydict['my-bool']) + self.assertTrue(_mydict['my_bool']) def test_bool_false(self): _specs = ['--my_bool', 'type=bool', 'false', '--arg1', 'value1'] diff --git a/quantumclient/tests/unit/test_cli20.py b/quantumclient/tests/unit/test_cli20.py index 950c23d..4c8f61c 100644 --- a/quantumclient/tests/unit/test_cli20.py +++ b/quantumclient/tests/unit/test_cli20.py @@ -168,8 +168,9 @@ class CLITestV20Base(unittest.TestCase): resstr)) self.mox.ReplayAll() cmd_parser = cmd.get_parser('create_' + resource) - parsed_args = cmd_parser.parse_args(args) - cmd.run(parsed_args) + known_args, values_specs = cmd_parser.parse_known_args(args) + cmd.values_specs = values_specs + cmd.run(known_args) self.mox.VerifyAll() self.mox.UnsetStubs() _str = self.fake_stdout.make_string() diff --git a/quantumclient/tests/unit/test_cli20_floatingips.py b/quantumclient/tests/unit/test_cli20_floatingips.py index e48d29e..7d0da04 100644 --- a/quantumclient/tests/unit/test_cli20_floatingips.py +++ b/quantumclient/tests/unit/test_cli20_floatingips.py @@ -57,7 +57,7 @@ class CLITestV20FloatingIps(CLITestV20Base): # Test dashed options args = [name, '--port-id', pid] - position_names = ['floating_network_id', 'port-id'] + position_names = ['floating_network_id', 'port_id'] _str = self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) @@ -76,7 +76,7 @@ class CLITestV20FloatingIps(CLITestV20Base): position_names, position_values) # Test dashed options args = [name, '--port-id', pid, '--fixed-ip-address', addr] - position_names = ['floating_network_id', 'port-id', 'fixed-ip-address'] + position_names = ['floating_network_id', 'port_id', 'fixed_ip_address'] _str = self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) diff --git a/quantumclient/tests/unit/test_cli20_subnet.py b/quantumclient/tests/unit/test_cli20_subnet.py index e07f6c7..ec4396f 100644 --- a/quantumclient/tests/unit/test_cli20_subnet.py +++ b/quantumclient/tests/unit/test_cli20_subnet.py @@ -239,6 +239,67 @@ class CLITestV20Subnet(CLITestV20Base): position_names, position_values, tenant_id='tenantid') + def test_create_subnet_merge_single_plurar(self): + resource = 'subnet' + cmd = CreateSubnet(MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + cidr = 'prefixvalue' + args = ['--tenant_id', 'tenantid', + '--allocation-pool', 'start=1.1.1.10,end=1.1.1.20', + netid, cidr, + '--allocation-pools', 'list=true', 'type=dict', + 'start=1.1.1.30,end=1.1.1.40'] + position_names = ['ip_version', 'allocation_pools', 'network_id', + 'cidr'] + pools = [{'start': '1.1.1.10', 'end': '1.1.1.20'}, + {'start': '1.1.1.30', 'end': '1.1.1.40'}] + position_values = [4, pools, netid, cidr] + _str = self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values, + tenant_id='tenantid') + + def test_create_subnet_merge_plurar(self): + resource = 'subnet' + cmd = CreateSubnet(MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + cidr = 'prefixvalue' + args = ['--tenant_id', 'tenantid', + netid, cidr, + '--allocation-pools', 'list=true', 'type=dict', + 'start=1.1.1.30,end=1.1.1.40'] + position_names = ['ip_version', 'allocation_pools', 'network_id', + 'cidr'] + pools = [{'start': '1.1.1.30', 'end': '1.1.1.40'}] + position_values = [4, pools, netid, cidr] + _str = self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values, + tenant_id='tenantid') + + def test_create_subnet_merge_single_single(self): + resource = 'subnet' + cmd = CreateSubnet(MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + cidr = 'prefixvalue' + args = ['--tenant_id', 'tenantid', + '--allocation-pool', 'start=1.1.1.10,end=1.1.1.20', + netid, cidr, + '--allocation-pool', + 'start=1.1.1.30,end=1.1.1.40'] + position_names = ['ip_version', 'allocation_pools', 'network_id', + 'cidr'] + pools = [{'start': '1.1.1.10', 'end': '1.1.1.20'}, + {'start': '1.1.1.30', 'end': '1.1.1.40'}] + position_values = [4, pools, netid, cidr] + _str = self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values, + tenant_id='tenantid') + def test_list_subnets_detail(self): """List subnets: -D.""" resources = "subnets"