There are lots of duplicated code in these two scripts, use config file of different host to decide test cases running in different host.
Signed-off-by: Yiqiao Pu <[email protected]> --- client/tests/kvm/host-version.cfg.sample | 32 +++- client/tests/kvm/tests/qmp_basic.py | 103 +++++--- client/tests/kvm/tests/qmp_basic_rhel6.py | 389 ----------------------------- 3 files changed, 94 insertions(+), 430 deletions(-) delete mode 100644 client/tests/kvm/tests/qmp_basic_rhel6.py diff --git a/client/tests/kvm/host-version.cfg.sample b/client/tests/kvm/host-version.cfg.sample index 2295a82..47c474a 100644 --- a/client/tests/kvm/host-version.cfg.sample +++ b/client/tests/kvm/host-version.cfg.sample @@ -13,17 +13,47 @@ variants: - RHEL_Server_5.7_host: - RHEL_Server_6.0_host: + qmp_basic: + check_details = no + expected_error = "MissingParameter" + data_dict = "name:device" + check_stop = no + check_screendump = no + check_device = no + - RHEL_Server_6.1_host: virtio_nic: vhost = "vhost=on" + qmp_basic: + check_details = no + expected_error = "MissingParameter" + data_dict = "name:device" + check_stop = no + check_screendump = no + check_device = no + - RHEL_Server_6.2_host: virtio_nic: vhost = "vhost=on" + qmp_basic: + expected_error = "MissingParameter" + data_dict = "name:device" + check_stop = no + check_screendump = no + check_device = no + - Fedora_13_host: + qmp_basic: + migrate_set_speed_type = int - Fedora_14_host: + qmp_basic: + migrate_set_speed_type = int - Fedora_15_host: + qmp_basic: + migrate_set_speed_type = int - Fedora_16_host: - + qmp_basic: + migrate_set_speed_type = int diff --git a/client/tests/kvm/tests/qmp_basic.py b/client/tests/kvm/tests/qmp_basic.py index a8c84ac..1c1a7f2 100644 --- a/client/tests/kvm/tests/qmp_basic.py +++ b/client/tests/kvm/tests/qmp_basic.py @@ -1,3 +1,4 @@ +import logging, re from autotest_lib.client.common_lib import error from autotest_lib.client.virt import kvm_monitor @@ -77,7 +78,7 @@ def run_qmp_basic(test, params, env): def check_key_is_int(qmp_dict, key): fail_no_key(qmp_dict, key) try: - value = int(qmp_dict[key]) + int(qmp_dict[key]) except Exception: raise error.TestFail("'%s' key is not of type int, it's '%s'" % (key, type(qmp_dict[key]))) @@ -111,6 +112,7 @@ def run_qmp_basic(test, params, env): @param classname: Expected error class name @param datadict: Expected error data dictionary """ + logging.debug("resp %s", str(resp)) check_key_is_dict(resp, "error") check_key_is_str(resp["error"], "class") if classname and resp["error"]["class"] != classname: @@ -130,9 +132,14 @@ def run_qmp_basic(test, params, env): { "qemu": { "major": json-int, "minor": json-int, "micro": json-int } "package": json-string } """ - check_key_is_dict(version, "qemu") - for key in [ "major", "minor", "micro" ]: - check_key_is_int(version["qemu"], key) + check_details = params.get("check_details", "yes") + if check_details == "yes": + check_key_is_dict(version, "qemu") + for key in [ "major", "minor", "micro" ]: + check_key_is_int(version["qemu"], key) + else: + check_key_is_str(version, "qemu") + check_key_is_str(version, "package") @@ -224,10 +231,15 @@ def run_qmp_basic(test, params, env): "arguments" and "id". Although expansion is supported, invalid key names must be detected. """ + expected_error = params.get("expected_error", + "QMPExtraInputObjectMember") + data_dict_str = params.get("data_dict", "member:foobar") + data_dict = {} + for i in re.split(";", data_dict_str.strip()): + item = re.split(":", i.strip()) + data_dict[item[0].strip()] = item[1].strip() resp = monitor.cmd_obj({ "execute": "eject", "foobar": True }) - check_error_resp(resp, "QMPExtraInputObjectMember", - { "member": "foobar" }) - + check_error_resp(resp, expected_error, data_dict) def test_bad_arguments_key_type(monitor): """ @@ -270,7 +282,8 @@ def run_qmp_basic(test, params, env): """ for cmd in [ "foo", [], True, 1 ]: resp = monitor.cmd_obj(cmd) - check_error_resp(resp, "QMPBadInputObject", { "expected":"object" }) + check_error_resp(resp, + "QMPBadInputObject", { "expected":"object" }) def test_good_input_obj(monitor): @@ -285,11 +298,11 @@ def run_qmp_basic(test, params, env): resp = monitor.cmd_obj({ "arguments": {}, "execute": "query-version" }) check_success_resp(resp) - id = "1234foo" - resp = monitor.cmd_obj({ "id": id, "execute": "query-version", + id_key = "1234foo" + resp = monitor.cmd_obj({ "id": id_key, "execute": "query-version", "arguments": {} }) check_success_resp(resp) - check_str_key(resp, "id", id) + check_str_key(resp, "id", id_key) # TODO: would be good to test simple argument usage, but we don't have # a read-only command that accepts arguments. @@ -319,37 +332,47 @@ def run_qmp_basic(test, params, env): command used doesn't matter much as QMP performs argument checking _before_ calling the command. """ - # stop doesn't take arguments - resp = monitor.cmd_qmp("stop", { "foo": 1 }) - check_error_resp(resp, "InvalidParameter", { "name": "foo" }) - - # required argument omitted - resp = monitor.cmd_qmp("screendump") - check_error_resp(resp, "MissingParameter", { "name": "filename" }) - - # 'bar' is not a valid argument - resp = monitor.cmd_qmp("screendump", { "filename": "outfile", - "bar": "bar" }) - check_error_resp(resp, "InvalidParameter", { "name": "bar"}) - + # qmp in RHEL6 is different from 0.13.*: + # 1. 'stop' command just return {} evenif stop have arguments. + # 2. there is no 'screendump' command. + # 3. argument isn't checked in 'device' command. + # so skip these tests in RHEL6. + check_stop = params.get("check_stop", "yes") + check_screendump = params.get("check_screendump", "yes") + check_device = params.get("check_device", "yes") + migrate_set_speed_type = params.get("migrate_set_speed_type", + "number") + if check_stop == "yes": + # stop doesn't take arguments + resp = monitor.cmd_qmp("stop", { "foo": 1 }) + check_error_resp(resp, "InvalidParameter", { "name": "foo" }) + if check_screendump == "yes": + # required argument omitted + resp = monitor.cmd_qmp("screendump") + check_error_resp(resp, "MissingParameter", { "name": "filename" }) + + # 'bar' is not a valid argument + resp = monitor.cmd_qmp("screendump", { "filename": "outfile", + "bar": "bar" }) + check_error_resp(resp, "InvalidParameter", { "name": "bar"}) + # filename argument must be a json-string + for arg in [ {}, [], 1, True ]: + resp = monitor.cmd_qmp("screendump", { "filename": arg }) + check_error_resp(resp, "InvalidParameterType", + { "name": "filename", "expected": "string" }) + if check_device == "yes": + # force argument must be a json-bool + for arg in [ {}, [], 1, "foo" ]: + resp = monitor.cmd_qmp("eject", + { "force": arg, "device": "foo" }) + check_error_resp(resp, "InvalidParameterType", + { "name": "force", "expected": "bool" }) # test optional argument: 'force' is omitted, but it's optional, so # the handler has to be called. Test this happens by checking an # error that is generated by the handler itself. resp = monitor.cmd_qmp("eject", { "device": "foobar" }) check_error_resp(resp, "DeviceNotFound") - # filename argument must be a json-string - for arg in [ {}, [], 1, True ]: - resp = monitor.cmd_qmp("screendump", { "filename": arg }) - check_error_resp(resp, "InvalidParameterType", - { "name": "filename", "expected": "string" }) - - # force argument must be a json-bool - for arg in [ {}, [], 1, "foo" ]: - resp = monitor.cmd_qmp("eject", { "force": arg, "device": "foo" }) - check_error_resp(resp, "InvalidParameterType", - { "name": "force", "expected": "bool" }) - # val argument must be a json-int for arg in [ {}, [], True, "foo" ]: resp = monitor.cmd_qmp("memsave", { "val": arg, "filename": "foo", @@ -361,16 +384,16 @@ def run_qmp_basic(test, params, env): for arg in [ {}, [], True, "foo" ]: resp = monitor.cmd_qmp("migrate_set_speed", { "value": arg }) check_error_resp(resp, "InvalidParameterType", - { "name": "value", "expected": "number" }) + { "name": "value", "expected": migrate_set_speed_type }) # qdev-type commands have their own argument checker, all QMP does # is to skip its checking and pass arguments through. Check this # works by providing invalid options to device_add and expecting # an error message from qdev - resp = monitor.cmd_qmp("device_add", { "driver": "e1000", - "foo": "bar" }) + resp = monitor.cmd_qmp("device_add", + { "driver": "e1000", "foo": "bar" }) check_error_resp(resp, "PropertyNotFound", - {"device": "e1000", "property": "foo"}) + {"device": "e1000", "property": "foo"}) def unknown_commands_suite(monitor): diff --git a/client/tests/kvm/tests/qmp_basic_rhel6.py b/client/tests/kvm/tests/qmp_basic_rhel6.py deleted file mode 100644 index b4e5ea6..0000000 --- a/client/tests/kvm/tests/qmp_basic_rhel6.py +++ /dev/null @@ -1,389 +0,0 @@ -import logging -from autotest_lib.client.common_lib import error -from autotest_lib.client.virt import kvm_monitor - - -def run_qmp_basic_rhel6(test, params, env): - """ - QMP Specification test-suite: this checks if the *basic* protocol conforms - to its specification, which is file QMP/qmp-spec.txt in QEMU's source tree. - - IMPORTANT NOTES: - - o Most tests depend heavily on QMP's error information (eg. classes), - this might have bad implications as the error interface is going to - change in QMP - - o Command testing is *not* covered in this suite. Each command has its - own specification and should be tested separately - - o We use the same terminology as used by the QMP specification, - specially with regard to JSON types (eg. a Python dict is called - a json-object) - - o This is divided in sub test-suites, please check the bottom of this - file to check the order in which they are run - - TODO: - - o Finding which test failed is not as easy as it should be - - o Are all those check_*() functions really needed? Wouldn't a - specialized class (eg. a Response class) do better? - """ - def fail_no_key(qmp_dict, key): - if not isinstance(qmp_dict, dict): - raise error.TestFail("qmp_dict is not a dict (it's '%s')" % - type(qmp_dict)) - if not key in qmp_dict: - raise error.TestFail("'%s' key doesn't exist in dict ('%s')" % - (key, str(qmp_dict))) - - - def check_dict_key(qmp_dict, key, keytype): - """ - Performs the following checks on a QMP dict key: - - 1. qmp_dict is a dict - 2. key exists in qmp_dict - 3. key is of type keytype - - If any of these checks fails, error.TestFail is raised. - """ - fail_no_key(qmp_dict, key) - if not isinstance(qmp_dict[key], keytype): - raise error.TestFail("'%s' key is not of type '%s', it's '%s'" % - (key, keytype, type(qmp_dict[key]))) - - - def check_key_is_dict(qmp_dict, key): - check_dict_key(qmp_dict, key, dict) - - - def check_key_is_list(qmp_dict, key): - check_dict_key(qmp_dict, key, list) - - - def check_key_is_str(qmp_dict, key): - check_dict_key(qmp_dict, key, unicode) - - - def check_str_key(qmp_dict, keyname, value=None): - check_dict_key(qmp_dict, keyname, unicode) - if value and value != qmp_dict[keyname]: - raise error.TestFail("'%s' key value '%s' should be '%s'" % - (keyname, str(qmp_dict[keyname]), str(value))) - - - def check_key_is_int(qmp_dict, key): - fail_no_key(qmp_dict, key) - try: - int(qmp_dict[key]) - except Exception: - raise error.TestFail("'%s' key is not of type int, it's '%s'" % - (key, type(qmp_dict[key]))) - - - def check_bool_key(qmp_dict, keyname, value=None): - check_dict_key(qmp_dict, keyname, bool) - if value and value != qmp_dict[keyname]: - raise error.TestFail("'%s' key value '%s' should be '%s'" % - (keyname, str(qmp_dict[keyname]), str(value))) - - - def check_success_resp(resp, empty=False): - """ - Check QMP OK response. - - @param resp: QMP response - @param empty: if True, response should not contain data to return - """ - check_key_is_dict(resp, "return") - if empty and len(resp["return"]) > 0: - raise error.TestFail("success response is not empty ('%s')" % - str(resp)) - - - def check_error_resp(resp, classname=None, datadict=None): - """ - Check QMP error response. - - @param resp: QMP response - @param classname: Expected error class name - @param datadict: Expected error data dictionary - """ - logging.debug("resp %s", str(resp)) - check_key_is_dict(resp, "error") - check_key_is_str(resp["error"], "class") - if classname and resp["error"]["class"] != classname: - raise error.TestFail("got error class '%s' expected '%s'" % - (resp["error"]["class"], classname)) - check_key_is_dict(resp["error"], "data") - if datadict and resp["error"]["data"] != datadict: - raise error.TestFail("got data dict '%s' expected '%s'" % - (resp["error"]["data"], datadict)) - - - def test_version(version): - """ - Check the QMP greeting message version key which, according to QMP's - documentation, should be: - - { "qemu": { "major": json-int, "minor": json-int, "micro": json-int } - "package": json-string } - """ - check_key_is_str(version, "qemu") - check_key_is_str(version, "package") - - - def test_greeting(greeting): - check_key_is_dict(greeting, "QMP") - check_key_is_dict(greeting["QMP"], "version") - check_key_is_list(greeting["QMP"], "capabilities") - - - def greeting_suite(monitor): - """ - Check the greeting message format, as described in the QMP - specfication section '2.2 Server Greeting'. - - { "QMP": { "version": json-object, "capabilities": json-array } } - """ - greeting = monitor.get_greeting() - test_greeting(greeting) - test_version(greeting["QMP"]["version"]) - - - def json_parsing_errors_suite(monitor): - """ - Check that QMP's parser is able to recover from parsing errors, please - check the JSON spec for more info on the JSON syntax (RFC 4627). - """ - # We're quite simple right now and the focus is on parsing errors that - # have already biten us in the past. - # - # TODO: The following test-cases are missing: - # - # - JSON numbers, strings and arrays - # - More invalid characters or malformed structures - # - Valid, but not obvious syntax, like zillion of spaces or - # strings with unicode chars (different suite maybe?) - bad_json = [] - - # A JSON value MUST be an object, array, number, string, true, false, - # or null - # - # NOTE: QMP seems to ignore a number of chars, like: | and ? - bad_json.append(":") - bad_json.append(",") - - # Malformed json-objects - # - # NOTE: sending only "}" seems to break QMP - # NOTE: Duplicate keys are accepted (should it?) - bad_json.append("{ \"execute\" }") - bad_json.append("{ \"execute\": \"query-version\", }") - bad_json.append("{ 1: \"query-version\" }") - bad_json.append("{ true: \"query-version\" }") - bad_json.append("{ []: \"query-version\" }") - bad_json.append("{ {}: \"query-version\" }") - - for cmd in bad_json: - resp = monitor.cmd_raw(cmd) - check_error_resp(resp, "JSONParsing") - - - def test_id_key(monitor): - """ - Check that QMP's "id" key is correctly handled. - """ - # The "id" key must be echoed back in error responses - id_key = "kvm-autotest" - resp = monitor.cmd_qmp("eject", { "foobar": True }, id=id_key) - check_error_resp(resp) - check_str_key(resp, "id", id_key) - - # The "id" key must be echoed back in success responses - resp = monitor.cmd_qmp("query-status", id=id_key) - check_success_resp(resp) - check_str_key(resp, "id", id_key) - - # The "id" key can be any json-object - for id_key in [ True, 1234, "string again!", [1, [], {}, True, "foo"], - { "key": {} } ]: - resp = monitor.cmd_qmp("query-status", id=id_key) - check_success_resp(resp) - if resp["id"] != id_key: - raise error.TestFail("expected id '%s' but got '%s'" % - (str(id_key), str(resp["id"]))) - - - def test_invalid_arg_key(monitor): - """ - Currently, the only supported keys in the input object are: "execute", - "arguments" and "id". Although expansion is supported, invalid key - names must be detected. - """ - resp = monitor.cmd_obj({ "execute": "eject", "foobar": True }) - expected_error = "MissingParameter" - data_dict = {"name": "device"} - check_error_resp(resp, expected_error, data_dict) - - - def test_bad_arguments_key_type(monitor): - """ - The "arguments" key must be an json-object. - - We use the eject command to perform the tests, but that's a random - choice, any command that accepts arguments will do, as the command - doesn't get called. - """ - for item in [ True, [], 1, "foo" ]: - resp = monitor.cmd_obj({ "execute": "eject", "arguments": item }) - check_error_resp(resp, "QMPBadInputObjectMember", - { "member": "arguments", "expected": "object" }) - - - def test_bad_execute_key_type(monitor): - """ - The "execute" key must be a json-string. - """ - for item in [ False, 1, {}, [] ]: - resp = monitor.cmd_obj({ "execute": item }) - check_error_resp(resp, "QMPBadInputObjectMember", - { "member": "execute", "expected": "string" }) - - - def test_no_execute_key(monitor): - """ - The "execute" key must exist, we also test for some stupid parsing - errors. - """ - for cmd in [ {}, { "execut": "qmp_capabilities" }, - { "executee": "qmp_capabilities" }, { "foo": "bar" }]: - resp = monitor.cmd_obj(cmd) - check_error_resp(resp) # XXX: check class and data dict? - - - def test_bad_input_obj_type(monitor): - """ - The input object must be... an json-object. - """ - for cmd in [ "foo", [], True, 1 ]: - resp = monitor.cmd_obj(cmd) - check_error_resp(resp, "QMPBadInputObject", { "expected":"object" }) - - - def test_good_input_obj(monitor): - """ - Basic success tests for issuing QMP commands. - """ - # NOTE: We don't use the cmd_qmp() method here because the command - # object is in a 'random' order - resp = monitor.cmd_obj({ "execute": "query-version" }) - check_success_resp(resp) - - resp = monitor.cmd_obj({ "arguments": {}, "execute": "query-version" }) - check_success_resp(resp) - - id_key = "1234foo" - resp = monitor.cmd_obj({ "id": id_key, "execute": "query-version", - "arguments": {} }) - check_success_resp(resp) - check_str_key(resp, "id", id_key) - - # TODO: would be good to test simple argument usage, but we don't have - # a read-only command that accepts arguments. - - - def input_object_suite(monitor): - """ - Check the input object format, as described in the QMP specfication - section '2.3 Issuing Commands'. - - { "execute": json-string, "arguments": json-object, "id": json-value } - """ - test_good_input_obj(monitor) - test_bad_input_obj_type(monitor) - test_no_execute_key(monitor) - test_bad_execute_key_type(monitor) - test_bad_arguments_key_type(monitor) - test_id_key(monitor) - test_invalid_arg_key(monitor) - - - def argument_checker_suite(monitor): - """ - Check that QMP's argument checker is detecting all possible errors. - - We use a number of different commands to perform the checks, but the - command used doesn't matter much as QMP performs argument checking - _before_ calling the command. - """ - # qmp in RHEL6 is different from 0.13.*: - # 1. 'stop' command just return {} evenif stop have arguments. - # 2. there is no 'screendump' command. - # 3. argument isn't checked in 'device' command. - # so skip these tests in RHEL6. - - # test optional argument: 'force' is omitted, but it's optional, so - # the handler has to be called. Test this happens by checking an - # error that is generated by the handler itself. - resp = monitor.cmd_qmp("eject", { "device": "foobar" }) - check_error_resp(resp, "DeviceNotFound") - - # val argument must be a json-int - for arg in [ {}, [], True, "foo" ]: - resp = monitor.cmd_qmp("memsave", { "val": arg, "filename": "foo", - "size": 10 }) - check_error_resp(resp, "InvalidParameterType", - { "name": "val", "expected": "int" }) - - # value argument must be a json-number - for arg in [ {}, [], True, "foo" ]: - resp = monitor.cmd_qmp("migrate_set_speed", { "value": arg }) - check_error_resp(resp, "InvalidParameterType", - { "name": "value", "expected": "number" }) - - # qdev-type commands have their own argument checker, all QMP does - # is to skip its checking and pass arguments through. Check this - # works by providing invalid options to device_add and expecting - # an error message from qdev - resp = monitor.cmd_qmp("device_add", {"driver": "e1000", - "foo": "bar" }) - check_error_resp(resp, "PropertyNotFound", - {"device": "e1000", "property": "foo"}) - - - def unknown_commands_suite(monitor): - """ - Check that QMP handles unknown commands correctly. - """ - # We also call a HMP-only command, to be sure it will fail as expected - for cmd in [ "bar", "query-", "query-foo", "q", "help" ]: - resp = monitor.cmd_qmp(cmd) - check_error_resp(resp, "CommandNotFound", { "name": cmd }) - - - vm = env.get_vm(params["main_vm"]) - vm.verify_alive() - - # Look for the first qmp monitor available, otherwise, fail the test - qmp_monitor = None - for m in vm.monitors: - if isinstance(m, kvm_monitor.QMPMonitor): - qmp_monitor = m - - if qmp_monitor is None: - raise error.TestError('Could not find a QMP monitor, aborting test') - - # Run all suites - greeting_suite(qmp_monitor) - input_object_suite(qmp_monitor) - argument_checker_suite(qmp_monitor) - unknown_commands_suite(qmp_monitor) - json_parsing_errors_suite(qmp_monitor) - - # check if QMP is still alive - if not qmp_monitor.is_responsive(): - raise error.TestFail('QMP monitor is not responsive after testing') -- 1.7.1 _______________________________________________ Autotest mailing list [email protected] http://test.kernel.org/cgi-bin/mailman/listinfo/autotest
