From: Matthew Oliver Date: Tue, 4 Feb 2020 02:29:48 +0000 (+1100) Subject: ceph_argparse: increment matchcnt on kwargs X-Git-Tag: v14.2.10~156^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=824590fcabc908194c55abde40dbf9d63efe0010;p=ceph.git ceph_argparse: increment matchcnt on kwargs Currently when you pass a param in on the ceph cli as a kwarg (--) the matchcnt isn't incremented in the validate method which is used to choose the right command signature. This means currently things fail like: ceph orchestrator rgw rm --realm_name=default --zone_name=default no valid command found; 1 closest matches: orchestrator rgw rm Error EINVAL: invalid command The '--realm_name' and '--zone_name' isn't counted to the matchcnt, so 'orchestrator rgw rm' isn't picked as the valid command. This patch simply corrects this by incrementing matchcnt on the kwarg validate path before calling shortcircuiting the loop. Fixes: https://tracker.ceph.com/issues/43803 Signed-off-by: Matthew Oliver (cherry picked from commit cb37c9ee609864a078edf38d98608bd8cc18cbd7) Conflicts: test: exclude helper method from nosetest discovery On nautilus the assertion helper was recognized by nosetest as a test even though it doens't start with test_ prefix. Explicitely decorate it with @nottest Signed-off-by: Shyukri Shyukriev --- diff --git a/src/pybind/ceph_argparse.py b/src/pybind/ceph_argparse.py index aedbcb02e20c..255e0f476c3e 100644 --- a/src/pybind/ceph_argparse.py +++ b/src/pybind/ceph_argparse.py @@ -1010,6 +1010,7 @@ def validate(args, signature, flags=0, partial=False): if kwarg_desc: validate_one(kwarg_v, kwarg_desc) + matchcnt += 1 store_arg(kwarg_desc, d) continue @@ -1111,7 +1112,7 @@ def validate_command(sigdict, args, verbose=False): Parse positional arguments into a parameter dict, according to the command descriptions. - Writes advice about nearly-matching commands ``sys.stderr`` if + Writes advice about nearly-matching commands ``sys.stderr`` if the arguments do not match any command. :param sigdict: A command description dictionary, as returned diff --git a/src/test/pybind/test_ceph_argparse.py b/src/test/pybind/test_ceph_argparse.py index 64321c7a70ce..854843779400 100755 --- a/src/test/pybind/test_ceph_argparse.py +++ b/src/test/pybind/test_ceph_argparse.py @@ -17,11 +17,16 @@ from nose.tools import eq_ as eq from nose.tools import * +from unittest import TestCase -from ceph_argparse import validate_command, parse_json_funcsigs +from ceph_argparse import validate_command, parse_json_funcsigs, validate, \ + parse_funcsig, ArgumentError, ArgumentTooFew, ArgumentMissing, \ + ArgumentNumber, ArgumentValid import os +import random import re +import string import sys import json try: @@ -1261,6 +1266,76 @@ class TestConfigKey(TestArgparse): def test_list(self): self.check_no_arg('config-key', 'list') + + +class TestValidate(TestCase): + + ARGS = 0 + KWARGS = 1 + KWARGS_EQ = 2 + MIXED = 3 + + def setUp(self): + self.prefix = ['some', 'random', 'cmd'] + self.args_dict = [ + {'name': 'variable_one', 'type': 'CephString'}, + {'name': 'variable_two', 'type': 'CephString'}, + {'name': 'variable_three', 'type': 'CephString'}, + {'name': 'variable_four', 'type': 'CephInt'}, + {'name': 'variable_five', 'type': 'CephString'}] + self.args = [] + for d in self.args_dict: + if d['type'] == 'CephInt': + val = "{}".format(random.randint(0, 100)) + elif d['type'] == 'CephString': + letters = string.ascii_letters + str_len = random.randint(5, 10) + val = ''.join(random.choice(letters) for _ in range(str_len)) + else: + self.skipTest() + + self.args.append((d['name'], val)) + + self.sig = parse_funcsig(self.prefix + self.args_dict) + + @nottest + def arg_kwarg_test(self, prefix, args, sig, arg_type=0): + """ + Runs validate in different arg/kargs ways. + + :param prefix: List of prefix commands (that can't be kwarged) + :param args: a list of kwarg, arg pairs: [(k1, v1), (k2, v2), ...] + :param sig: The sig to match + :param arg_type: how to build the args to send. As positional args (ARGS), + as long kwargs (KWARGS [--k v]), other style long kwargs + (KWARGS_EQ (--k=v]), and mixed (MIXED) where there will be + a random mix of the above. + :return: None, the method will assert. + """ + final_args = list(prefix) + for k, v in args: + a_type = arg_type + if a_type == self.MIXED: + a_type = random.choice((self.ARGS, + self.KWARGS, + self.KWARGS_EQ)) + if a_type == self.ARGS: + final_args.append(v) + elif a_type == self.KWARGS: + final_args.extend(["--{}".format(k), v]) + else: + final_args.append("--{}={}".format(k, v)) + + try: + validate(final_args, sig) + except (ArgumentError, ArgumentMissing, + ArgumentNumber, ArgumentTooFew, ArgumentValid) as ex: + self.fail("Validation failed: {}".format(str(ex))) + + def test_args_and_kwargs_validate(self): + for arg_type in (self.ARGS, self.KWARGS, self.KWARGS_EQ, self.MIXED): + self.arg_kwarg_test(self.prefix, self.args, self.sig, arg_type) + # Local Variables: # compile-command: "cd ../.. ; make -j4 && # PYTHONPATH=pybind nosetests --stop \