/* * Copyright (C) 2011-2014 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * */ #include #include "testutils.h" #include "testutilsqemu.h" #include "testutilsqemuschema.h" #include "qemumonitortestutils.h" #include "qemu/qemu_domain.h" #include "qemu/qemu_block.h" #include "qemu/qemu_monitor_json.h" #include "qemu/qemu_qapi.h" #include "virthread.h" #include "virerror.h" #include "virstring.h" #include "cpu/cpu.h" #include "qemu/qemu_monitor.h" #include "qemu/qemu_migration_params.h" #define LIBVIRT_QEMU_MIGRATION_PARAMSPRIV_H_ALLOW #include "qemu/qemu_migration_paramspriv.h" #define VIR_FROM_THIS VIR_FROM_NONE typedef struct _testQemuMonitorJSONSimpleFuncData testQemuMonitorJSONSimpleFuncData; typedef testQemuMonitorJSONSimpleFuncData *testQemuMonitorJSONSimpleFuncDataPtr; struct _testQemuMonitorJSONSimpleFuncData { const char *cmd; int (* func) (qemuMonitorPtr mon); virDomainXMLOptionPtr xmlopt; const char *reply; virHashTablePtr schema; bool allowDeprecated; bool allowRemoved; }; typedef struct _testGenericData testGenericData; struct _testGenericData { virDomainXMLOptionPtr xmlopt; virHashTablePtr schema; }; const char *queryBlockReply = "{" " \"return\": [" " {" " \"io-status\": \"ok\"," " \"device\": \"drive-virtio-disk0\"," " \"locked\": false," " \"removable\": false," " \"inserted\": {" " \"iops_rd\": 5," " \"iops_wr\": 6," " \"ro\": false," " \"backing_file_depth\": 0," " \"drv\": \"qcow2\"," " \"iops\": 4," " \"bps_wr\": 3," " \"encrypted\": false," " \"bps\": 1," " \"bps_rd\": 2," " \"bps_max\": 7," " \"iops_max\": 10," " \"bps_rd_max\": 8," " \"bps_wr_max\": 9," " \"iops_rd_max\": 11," " \"iops_wr_max\": 12," " \"iops_size\": 13," " \"group\": \"group14\"," " \"bps_max_length\": 15," " \"bps_rd_max_length\": 16," " \"bps_wr_max_length\": 17," " \"iops_max_length\": 18," " \"iops_rd_max_length\": 19," " \"iops_wr_max_length\": 20," " \"file\": \"/home/zippy/work/tmp/gentoo.qcow2\"," " \"encryption_key_missing\": false" " }," " \"type\": \"unknown\"" " }," " {" " \"io-status\": \"ok\"," " \"device\": \"drive-virtio-disk1\"," " \"locked\": false," " \"removable\": false," " \"inserted\": {" " \"iops_rd\": 0," " \"iops_wr\": 0," " \"ro\": false," " \"backing_file_depth\": 0," " \"drv\": \"raw\"," " \"iops\": 0," " \"bps_wr\": 0," " \"encrypted\": false," " \"bps\": 0," " \"bps_rd\": 0," " \"file\": \"/home/zippy/test.bin\"," " \"encryption_key_missing\": false" " }," " \"type\": \"unknown\"" " }," " {" " \"io-status\": \"ok\"," " \"device\": \"drive-ide0-1-0\"," " \"locked\": true," " \"removable\": true," " \"inserted\": {" " \"iops_rd\": 0," " \"iops_wr\": 0," " \"ro\": true," " \"backing_file_depth\": 0," " \"drv\": \"raw\"," " \"iops\": 0," " \"bps_wr\": 0," " \"encrypted\": false," " \"bps\": 0," " \"bps_rd\": 0," " \"file\": \"/home/zippy/tmp/install-amd64-minimal-20121210.iso\"," " \"encryption_key_missing\": false" " }," " \"tray_open\": false," " \"type\": \"unknown\"" " }," " {" " \"io-status\": \"ok\"," " \"device\": \"drive-ide0-1-1\"," " \"locked\": false," " \"removable\": true," " \"tray_open\": false," " \"type\": \"unknown\"" " }" " ]," " \"id\": \"libvirt-10\"" "}"; static int testQemuMonitorJSONGetStatus(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; bool running = false; virDomainPausedReason reason = 0; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; if (qemuMonitorTestAddItem(test, "query-status", "{ " " \"return\": { " " \"status\": \"running\", " " \"singlestep\": false, " " \"running\": true " " } " "}") < 0) return -1; if (qemuMonitorTestAddItem(test, "query-status", "{ " " \"return\": { " " \"singlestep\": false, " " \"running\": false " " } " "}") < 0) return -1; if (qemuMonitorTestAddItem(test, "query-status", "{ " " \"return\": { " " \"status\": \"inmigrate\", " " \"singlestep\": false, " " \"running\": false " " } " "}") < 0) return -1; if (qemuMonitorGetStatus(qemuMonitorTestGetMonitor(test), &running, &reason) < 0) return -1; if (!running) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "Running was not true"); return -1; } if (reason != VIR_DOMAIN_PAUSED_UNKNOWN) { virReportError(VIR_ERR_INTERNAL_ERROR, "Reason was unexpectedly set to %d", reason); return -1; } if (qemuMonitorGetStatus(qemuMonitorTestGetMonitor(test), &running, &reason) < 0) return -1; if (running) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "Running was not false"); return -1; } if (reason != VIR_DOMAIN_PAUSED_UNKNOWN) { virReportError(VIR_ERR_INTERNAL_ERROR, "Reason was unexpectedly set to %d", reason); return -1; } if (qemuMonitorGetStatus(qemuMonitorTestGetMonitor(test), &running, &reason) < 0) return -1; if (running) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "Running was not false"); return -1; } if (reason != VIR_DOMAIN_PAUSED_MIGRATION) { virReportError(VIR_ERR_INTERNAL_ERROR, "Reason was unexpectedly set to %d", reason); return -1; } return 0; } static int testQemuMonitorJSONGetVersion(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; int ret = -1; int major; int minor; int micro; char *package = NULL; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; if (qemuMonitorTestAddItem(test, "query-version", "{ " " \"return\":{ " " \"qemu\":{ " " \"major\":1, " " \"minor\":2, " " \"micro\":3 " " }," " \"package\":\"\"" " }" "}") < 0) goto cleanup; if (qemuMonitorTestAddItem(test, "query-version", "{ " " \"return\":{ " " \"qemu\":{ " " \"major\":0, " " \"minor\":11, " " \"micro\":6 " " }," " \"package\":\"2.283.el6\"" " }" "}") < 0) goto cleanup; if (qemuMonitorGetVersion(qemuMonitorTestGetMonitor(test), &major, &minor, µ, &package) < 0) goto cleanup; if (major != 1) { virReportError(VIR_ERR_INTERNAL_ERROR, "Major %d was not 1", major); goto cleanup; } if (minor != 2) { virReportError(VIR_ERR_INTERNAL_ERROR, "Minor %d was not 2", major); goto cleanup; } if (micro != 3) { virReportError(VIR_ERR_INTERNAL_ERROR, "Micro %d was not 3", major); goto cleanup; } if (STRNEQ(package, "")) { virReportError(VIR_ERR_INTERNAL_ERROR, "Package %s was not ''", package); goto cleanup; } VIR_FREE(package); if (qemuMonitorGetVersion(qemuMonitorTestGetMonitor(test), &major, &minor, µ, &package) < 0) goto cleanup; if (major != 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "Major %d was not 0", major); goto cleanup; } if (minor != 11) { virReportError(VIR_ERR_INTERNAL_ERROR, "Minor %d was not 11", major); goto cleanup; } if (micro != 6) { virReportError(VIR_ERR_INTERNAL_ERROR, "Micro %d was not 6", major); goto cleanup; } if (STRNEQ(package, "2.283.el6")) { virReportError(VIR_ERR_INTERNAL_ERROR, "Package %s was not '2.283.el6'", package); goto cleanup; } ret = 0; cleanup: VIR_FREE(package); return ret; } static int testQemuMonitorJSONGetMachines(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; int ret = -1; qemuMonitorMachineInfoPtr *info; int ninfo = 0; const char *null = NULL; size_t i; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; if (qemuMonitorTestAddItem(test, "query-machines", "{ " " \"return\": [ " " { " " \"name\": \"pc-1.0\" " " }, " " { " " \"name\": \"pc-1.1\" " " }, " " { " " \"name\": \"pc-1.2\", " " \"is-default\": true, " " \"alias\": \"pc\" " " } " " ]" "}") < 0) goto cleanup; if ((ninfo = qemuMonitorGetMachines(qemuMonitorTestGetMonitor(test), &info)) < 0) goto cleanup; if (ninfo != 3) { virReportError(VIR_ERR_INTERNAL_ERROR, "ninfo %d is not 3", ninfo); goto cleanup; } #define CHECK(i, wantname, wantisDefault, wantalias) \ do { \ if (STRNEQ(info[i]->name, (wantname))) { \ virReportError(VIR_ERR_INTERNAL_ERROR, \ "name %s is not %s", \ info[i]->name, (wantname)); \ goto cleanup; \ } \ if (info[i]->isDefault != (wantisDefault)) { \ virReportError(VIR_ERR_INTERNAL_ERROR, \ "isDefault %d is not %d", \ info[i]->isDefault, (wantisDefault)); \ goto cleanup; \ } \ if (STRNEQ_NULLABLE(info[i]->alias, (wantalias))) { \ virReportError(VIR_ERR_INTERNAL_ERROR, \ "alias %s is not %s", \ info[i]->alias, NULLSTR(wantalias)); \ goto cleanup; \ } \ } while (0) CHECK(0, "pc-1.0", false, null); CHECK(1, "pc-1.1", false, null); CHECK(2, "pc-1.2", true, "pc"); #undef CHECK ret = 0; cleanup: for (i = 0; i < ninfo; i++) qemuMonitorMachineInfoFree(info[i]); VIR_FREE(info); return ret; } static int testQemuMonitorJSONGetCPUDefinitions(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; g_autoptr(qemuMonitorCPUDefs) defs = NULL; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; if (qemuMonitorTestAddItem(test, "query-cpu-definitions", "{ " " \"return\": [ " " { " " \"name\": \"qemu64\" " " }, " " { " " \"name\": \"Opteron_G4\", " " \"unavailable-features\": [\"vme\"]" " }, " " { " " \"name\": \"Westmere\", " " \"unavailable-features\": []" " } " " ]" "}") < 0) return -1; if (qemuMonitorGetCPUDefinitions(qemuMonitorTestGetMonitor(test), &defs) < 0) return -1; if (defs->ncpus != 3) { virReportError(VIR_ERR_INTERNAL_ERROR, "ncpus %zu is not 3", defs->ncpus); return -1; } #define CHECK_FULL(i, wantname, Usable) \ do { \ if (STRNEQ(defs->cpus[i].name, (wantname))) { \ virReportError(VIR_ERR_INTERNAL_ERROR, \ "name %s is not %s", \ defs->cpus[i].name, (wantname)); \ return -1; \ } \ if (defs->cpus[i].usable != (Usable)) { \ virReportError(VIR_ERR_INTERNAL_ERROR, \ "%s: expecting usable flag %d, got %d", \ defs->cpus[i].name, Usable, defs->cpus[i].usable); \ return -1; \ } \ } while (0) #define CHECK(i, wantname) \ CHECK_FULL(i, wantname, VIR_DOMCAPS_CPU_USABLE_UNKNOWN) #define CHECK_USABLE(i, wantname, usable) \ CHECK_FULL(i, wantname, \ usable ? VIR_DOMCAPS_CPU_USABLE_YES : VIR_DOMCAPS_CPU_USABLE_NO) CHECK(0, "qemu64"); CHECK_USABLE(1, "Opteron_G4", false); CHECK_USABLE(2, "Westmere", true); #undef CHECK #undef CHECK_USABLE #undef CHECK_FULL return 0; } static int testQemuMonitorJSONGetCommands(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; int ret = -1; char **commands = NULL; int ncommands = 0; size_t i; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; if (qemuMonitorTestAddItem(test, "query-commands", "{ " " \"return\": [ " " { " " \"name\": \"system_wakeup\" " " }, " " { " " \"name\": \"cont\" " " }, " " { " " \"name\": \"quit\" " " } " " ]" "}") < 0) goto cleanup; if ((ncommands = qemuMonitorGetCommands(qemuMonitorTestGetMonitor(test), &commands)) < 0) goto cleanup; if (ncommands != 3) { virReportError(VIR_ERR_INTERNAL_ERROR, "ncommands %d is not 3", ncommands); goto cleanup; } #define CHECK(i, wantname) \ do { \ if (STRNEQ(commands[i], (wantname))) { \ virReportError(VIR_ERR_INTERNAL_ERROR, \ "name %s is not %s", \ commands[i], (wantname)); \ goto cleanup; \ } \ } while (0) CHECK(0, "system_wakeup"); CHECK(1, "cont"); CHECK(2, "quit"); #undef CHECK ret = 0; cleanup: for (i = 0; i < ncommands; i++) VIR_FREE(commands[i]); VIR_FREE(commands); return ret; } static int testQemuMonitorJSONGetTPMModels(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; int ret = -1; char **tpmmodels = NULL; int ntpmmodels = 0; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; if (qemuMonitorTestAddItem(test, "query-tpm-models", "{ " " \"return\": [ " " \"passthrough\"" " ]" "}") < 0) goto cleanup; if ((ntpmmodels = qemuMonitorGetTPMModels(qemuMonitorTestGetMonitor(test), &tpmmodels)) < 0) goto cleanup; if (ntpmmodels != 1) { virReportError(VIR_ERR_INTERNAL_ERROR, "ntpmmodels %d is not 1", ntpmmodels); goto cleanup; } #define CHECK(i, wantname) \ do { \ if (STRNEQ(tpmmodels[i], (wantname))) { \ virReportError(VIR_ERR_INTERNAL_ERROR, \ "name %s is not %s", \ tpmmodels[i], (wantname)); \ goto cleanup; \ } \ } while (0) CHECK(0, "passthrough"); #undef CHECK ret = 0; cleanup: g_strfreev(tpmmodels); return ret; } static int testQemuMonitorJSONGetCommandLineOptionParameters(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; int ret = -1; char **params = NULL; int nparams = 0; bool found = false; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; if (qemuMonitorTestAddItem(test, "query-command-line-options", "{ " " \"return\": [ " " {\"parameters\": [], \"option\": \"acpi\" }," " {\"parameters\": [" " {\"name\": \"romfile\", " " \"type\": \"string\"}, " " {\"name\": \"bootindex\", " " \"type\": \"number\"}], " " \"option\": \"option-rom\"}" " ]" "}") < 0) goto cleanup; /* present with params */ if ((nparams = qemuMonitorGetCommandLineOptionParameters(qemuMonitorTestGetMonitor(test), "option-rom", ¶ms, NULL)) < 0) goto cleanup; if (nparams != 2) { virReportError(VIR_ERR_INTERNAL_ERROR, "nparams was %d, expected 2", nparams); goto cleanup; } #define CHECK(i, wantname) \ do { \ if (STRNEQ(params[i], (wantname))) { \ virReportError(VIR_ERR_INTERNAL_ERROR, \ "name was %s, expected %s", \ params[i], (wantname)); \ goto cleanup; \ } \ } while (0) CHECK(0, "romfile"); CHECK(1, "bootindex"); #undef CHECK g_strfreev(params); params = NULL; /* present but empty */ if ((nparams = qemuMonitorGetCommandLineOptionParameters(qemuMonitorTestGetMonitor(test), "acpi", ¶ms, &found)) < 0) goto cleanup; if (nparams != 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "nparams was %d, expected 0", nparams); goto cleanup; } if (!found) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "found was false, expected true"); goto cleanup; } if (params && params[0]) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "unexpected array contents"); goto cleanup; } g_strfreev(params); params = NULL; /* no such option */ if ((nparams = qemuMonitorGetCommandLineOptionParameters(qemuMonitorTestGetMonitor(test), "foobar", ¶ms, &found)) < 0) goto cleanup; if (nparams != 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "nparams was %d, expected 0", nparams); goto cleanup; } if (found) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "found was true, expected false"); goto cleanup; } if (params && params[0]) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "unexpected array contents"); goto cleanup; } ret = 0; cleanup: g_strfreev(params); return ret; } struct qemuMonitorJSONTestAttachChardevData { qemuMonitorTestPtr test; virDomainChrSourceDefPtr chr; const char *expectPty; bool fail; }; static int testQemuMonitorJSONAttachChardev(const void *opaque) { const struct qemuMonitorJSONTestAttachChardevData *data = opaque; int rc; if ((rc = qemuMonitorAttachCharDev(qemuMonitorTestGetMonitor(data->test), "alias", data->chr)) < 0) goto cleanup; if (data->chr->type == VIR_DOMAIN_CHR_TYPE_PTY) { if (STRNEQ_NULLABLE(data->expectPty, data->chr->data.file.path)) { virReportError(VIR_ERR_INTERNAL_ERROR, "expected PTY path: %s got: %s", NULLSTR(data->expectPty), NULLSTR(data->chr->data.file.path)); rc = -1; } VIR_FREE(data->chr->data.file.path); } cleanup: if ((rc != 0) != data->fail) return -1; else return 0; } static int qemuMonitorJSONTestAttachOneChardev(virDomainXMLOptionPtr xmlopt, virHashTablePtr schema, const char *label, virDomainChrSourceDefPtr chr, const char *expectargs, const char *reply, const char *expectPty, bool fail) { struct qemuMonitorJSONTestAttachChardevData data = {0}; char *jsonreply = NULL; char *fulllabel = NULL; int ret = -1; if (!reply) reply = ""; jsonreply = g_strdup_printf("{\"return\": {%s}}", reply); fulllabel = g_strdup_printf("qemuMonitorJSONTestAttachChardev(%s)", label); data.chr = chr; data.fail = fail; data.expectPty = expectPty; if (!(data.test = qemuMonitorTestNewSchema(xmlopt, schema))) goto cleanup; qemuMonitorTestAllowUnusedCommands(data.test); if (qemuMonitorTestAddItemExpect(data.test, "chardev-add", expectargs, true, jsonreply) < 0) goto cleanup; if (virTestRun(fulllabel, &testQemuMonitorJSONAttachChardev, &data) < 0) goto cleanup; ret = 0; cleanup: qemuMonitorTestFree(data.test); VIR_FREE(jsonreply); VIR_FREE(fulllabel); return ret; } static int qemuMonitorJSONTestAttachChardev(virDomainXMLOptionPtr xmlopt, virHashTablePtr schema) { virDomainChrSourceDef chr; int ret = 0; #define CHECK(label, fail, expectargs) \ if (qemuMonitorJSONTestAttachOneChardev(xmlopt, schema, label, &chr, \ expectargs, NULL, NULL, fail) < 0) \ ret = -1 chr = (virDomainChrSourceDef) { .type = VIR_DOMAIN_CHR_TYPE_NULL }; CHECK("null", false, "{'id':'alias','backend':{'type':'null','data':{}}}"); chr = (virDomainChrSourceDef) { .type = VIR_DOMAIN_CHR_TYPE_VC }; CHECK("vc", false, "{'id':'alias','backend':{'type':'null','data':{}}}"); chr = (virDomainChrSourceDef) { .type = VIR_DOMAIN_CHR_TYPE_PTY }; if (qemuMonitorJSONTestAttachOneChardev(xmlopt, schema, "pty", &chr, "{'id':'alias'," "'backend':{'type':'pty'," "'data':{}}}", "\"pty\" : \"/dev/pts/0\"", "/dev/pts/0", false) < 0) ret = -1; chr = (virDomainChrSourceDef) { .type = VIR_DOMAIN_CHR_TYPE_PTY }; CHECK("pty missing path", true, "{'id':'alias','backend':{'type':'pty','data':{}}}"); memset(&chr, 0, sizeof(chr)); chr.type = VIR_DOMAIN_CHR_TYPE_FILE; chr.data.file.path = (char *) "/test/path"; CHECK("file", false, "{'id':'alias','backend':{'type':'file','data':{'out':'/test/path'}}}"); memset(&chr, 0, sizeof(chr)); chr.type = VIR_DOMAIN_CHR_TYPE_DEV; chr.data.file.path = (char *) "/test/path"; CHECK("device", false, "{'id':'alias','backend':{'type':'serial','data':{'device':'/test/path'}}}"); memset(&chr, 0, sizeof(chr)); chr.type = VIR_DOMAIN_CHR_TYPE_TCP; chr.data.tcp.host = (char *) "example.com"; chr.data.tcp.service = (char *) "1234"; CHECK("tcp", false, "{'id':'alias'," "'backend':{'type':'socket'," "'data':{'addr':{'type':'inet'," "'data':{'host':'example.com'," "'port':'1234'}}," "'telnet':false," "'server':false}}}"); memset(&chr, 0, sizeof(chr)); chr.type = VIR_DOMAIN_CHR_TYPE_UDP; chr.data.udp.connectHost = (char *) "example.com"; chr.data.udp.connectService = (char *) "1234"; CHECK("udp", false, "{'id':'alias'," "'backend':{'type':'udp'," "'data':{'remote':{'type':'inet'," "'data':{'host':'example.com'," "'port':'1234'}}}}}"); chr.data.udp.bindHost = (char *) "localhost"; chr.data.udp.bindService = (char *) "4321"; CHECK("udp", false, "{'id':'alias'," "'backend':{'type':'udp'," "'data':{'remote':{'type':'inet'," "'data':{'host':'example.com'," "'port':'1234'}}," "'local':{'type':'inet'," "'data':{'host':'localhost'," "'port':'4321'}}}}}"); chr.data.udp.bindHost = NULL; chr.data.udp.bindService = (char *) "4321"; CHECK("udp", false, "{'id':'alias'," "'backend':{'type':'udp'," "'data':{'remote':{'type':'inet'," "'data':{'host':'example.com'," "'port':'1234'}}," "'local':{'type':'inet'," "'data':{'host':''," "'port':'4321'}}}}}"); memset(&chr, 0, sizeof(chr)); chr.type = VIR_DOMAIN_CHR_TYPE_UNIX; chr.data.nix.path = (char *) "/path/to/socket"; CHECK("unix", false, "{'id':'alias'," "'backend':{'type':'socket'," "'data':{'addr':{'type':'unix'," "'data':{'path':'/path/to/socket'}}," "'server':false}}}"); chr = (virDomainChrSourceDef) { .type = VIR_DOMAIN_CHR_TYPE_SPICEVMC }; CHECK("spicevmc", false, "{'id':'alias','backend':{'type':'spicevmc','" "data':{'type':'vdagent'}}}"); chr = (virDomainChrSourceDef) { .type = VIR_DOMAIN_CHR_TYPE_PIPE }; CHECK("pipe", true, ""); chr = (virDomainChrSourceDef) { .type = VIR_DOMAIN_CHR_TYPE_STDIO }; CHECK("stdio", true, ""); #undef CHECK return ret; } static int testQemuMonitorJSONDetachChardev(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; if (qemuMonitorTestAddItem(test, "chardev-remove", "{\"return\": {}}") < 0) return -1; if (qemuMonitorDetachCharDev(qemuMonitorTestGetMonitor(test), "dummy_chrID") < 0) return -1; return 0; } /* * This test will request to return a list of paths for "/". It should be * a simple list of 1 real element that being the "machine". The following * is the execution and expected return: * * {"execute":"qom-list", "arguments": { "path": "/"}}" * {"return": [{"name": "machine", "type": "child"}, \ * {"name": "type", "type": "string"}]} */ static int testQemuMonitorJSONGetListPaths(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; int ret = -1; qemuMonitorJSONListPathPtr *paths; int npaths = 0; size_t i; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; if (qemuMonitorTestAddItem(test, "qom-list", "{ " " \"return\": [ " " {\"name\": \"machine\", " " \"type\": \"child\"}, " " {\"name\": \"type\", " " \"type\": \"string\"} " " ]" "}") < 0) goto cleanup; /* present with path */ if ((npaths = qemuMonitorJSONGetObjectListPaths( qemuMonitorTestGetMonitor(test), "/", &paths)) < 0) goto cleanup; if (npaths != 2) { virReportError(VIR_ERR_INTERNAL_ERROR, "npaths was %d, expected 1", npaths); goto cleanup; } #define CHECK(i, wantname, wanttype) \ do { \ if (STRNEQ(paths[i]->name, (wantname))) { \ virReportError(VIR_ERR_INTERNAL_ERROR, \ "name was %s, expected %s", \ paths[i]->name, (wantname)); \ goto cleanup; \ } \ if (STRNEQ_NULLABLE(paths[i]->type, (wanttype))) { \ virReportError(VIR_ERR_INTERNAL_ERROR, \ "type was %s, expected %s", \ NULLSTR(paths[i]->type), (wanttype)); \ goto cleanup; \ } \ } while (0) CHECK(0, "machine", "child"); #undef CHECK ret = 0; cleanup: for (i = 0; i < npaths; i++) qemuMonitorJSONListPathFree(paths[i]); VIR_FREE(paths); return ret; } /* * This test will use a path to /machine/i440fx which should exist in order * to ensure that the qom-get property fetch works properly. The following * is the execution and expected return: * * * { "execute": "qom-get","arguments": \ * { "path": "/machine/i440fx","property": "realized"}} * {"return": true} */ static int testQemuMonitorJSONGetObjectProperty(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; qemuMonitorJSONObjectProperty prop; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; if (qemuMonitorTestAddItem(test, "qom-get", "{ \"return\": true }") < 0) return -1; /* Present with path and property */ memset(&prop, 0, sizeof(qemuMonitorJSONObjectProperty)); prop.type = QEMU_MONITOR_OBJECT_PROPERTY_BOOLEAN; if (qemuMonitorJSONGetObjectProperty(qemuMonitorTestGetMonitor(test), "/machine/i440fx", "realized", &prop) < 0) return -1; if (!prop.val.b) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "expected true, but false returned"); return -1; } return 0; } /* * This test will use a path to /machine/i440fx which should exist in order * to ensure that the qom-set property set works properly. The test will * set a true property to true just as a proof of concept. Setting it to * false is not a good idea... */ static int testQemuMonitorJSONSetObjectProperty(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; qemuMonitorJSONObjectProperty prop; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; if (qemuMonitorTestAddItem(test, "qom-set", "{ \"return\": {} }") < 0) return -1; if (qemuMonitorTestAddItem(test, "qom-get", "{ \"return\": true }") < 0) return -1; /* Let's attempt the setting */ memset(&prop, 0, sizeof(qemuMonitorJSONObjectProperty)); prop.type = QEMU_MONITOR_OBJECT_PROPERTY_BOOLEAN; prop.val.b = true; if (qemuMonitorJSONSetObjectProperty(qemuMonitorTestGetMonitor(test), "/machine/i440fx", "realized", &prop) < 0) return -1; /* To make sure it worked, fetch the property - if this succeeds then * we didn't hose things */ memset(&prop, 0, sizeof(qemuMonitorJSONObjectProperty)); prop.type = QEMU_MONITOR_OBJECT_PROPERTY_BOOLEAN; if (qemuMonitorJSONGetObjectProperty(qemuMonitorTestGetMonitor(test), "/machine/i440fx", "realized", &prop) < 0) return -1; if (!prop.val.b) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "expected true, but false returned"); return -1; } return 0; } static int testQemuMonitorJSONGetDeviceAliases(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; int ret = -1; char **aliases = NULL; const char **alias; const char *expected[] = { "virtio-disk25", "video0", "serial0", "ide0-0-0", "usb", NULL }; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; if (qemuMonitorTestAddItem(test, "qom-list", "{\"return\": [" " {\"name\": \"virtio-disk25\"," " \"type\": \"child\"}," " {\"name\": \"video0\"," " \"type\": \"child\"}," " {\"name\": \"serial0\"," " \"type\": \"child\"}," " {\"name\": \"ide0-0-0\"," " \"type\": \"child\"}," " {\"name\": \"usb\"," " \"type\": \"child\"}," " {\"name\": \"type\", \"type\": \"string\"}" "]}") < 0) goto cleanup; if (qemuMonitorJSONGetDeviceAliases(qemuMonitorTestGetMonitor(test), &aliases) < 0) goto cleanup; if (!aliases) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "no aliases returned"); goto cleanup; } ret = 0; for (alias = (const char **) aliases; *alias; alias++) { if (!virStringListHasString(expected, *alias)) { fprintf(stderr, "got unexpected device alias '%s'\n", *alias); ret = -1; } } for (alias = expected; *alias; alias++) { if (!virStringListHasString((const char **) aliases, *alias)) { fprintf(stderr, "missing expected alias '%s'\n", *alias); ret = -1; } } cleanup: g_strfreev(aliases); return ret; } static int testQemuMonitorJSONCPU(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; bool running = false; virDomainPausedReason reason = 0; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; if (qemuMonitorTestAddItem(test, "stop", "{\"return\": {}}") < 0 || qemuMonitorTestAddItem(test, "query-status", "{\"return\": {" " \"status\": \"paused\"," " \"singlestep\": false," " \"running\": false}}") < 0 || qemuMonitorTestAddItem(test, "cont", "{\"return\": {}}") < 0 || qemuMonitorTestAddItem(test, "query-status", "{\"return\": {" " \"status\": \"running\"," " \"singlestep\": false," " \"running\": true}}") < 0) return -1; if (qemuMonitorJSONStopCPUs(qemuMonitorTestGetMonitor(test)) < 0) return -1; if (qemuMonitorGetStatus(qemuMonitorTestGetMonitor(test), &running, &reason) < 0) return -1; if (running) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "Running was not false"); return -1; } if (qemuMonitorJSONStartCPUs(qemuMonitorTestGetMonitor(test)) < 0) return -1; if (qemuMonitorGetStatus(qemuMonitorTestGetMonitor(test), &running, &reason) < 0) return -1; if (!running) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "Running was not true"); return -1; } return 0; } static int testQemuMonitorJSONSimpleFunc(const void *opaque) { testQemuMonitorJSONSimpleFuncDataPtr data = (testQemuMonitorJSONSimpleFuncDataPtr) opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; const char *reply = data->reply; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; if (!reply) reply = "{\"return\":{}}"; if (qemuMonitorTestAddItem(test, data->cmd, reply) < 0) return -1; if (data->func(qemuMonitorTestGetMonitor(test)) < 0) return -1; return 0; } #define GEN_TEST_FUNC(funcName, ...) \ static int \ testQemuMonitorJSON ## funcName(const void *opaque) \ { \ const testQemuMonitorJSONSimpleFuncData *data = opaque; \ virDomainXMLOptionPtr xmlopt = data->xmlopt; \ const char *reply = data->reply; \ g_autoptr(qemuMonitorTest) test = NULL; \ \ if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) \ return -1; \ \ if (data->allowDeprecated) \ qemuMonitorTestSkipDeprecatedValidation(test, data->allowRemoved); \ \ if (!reply) \ reply = "{\"return\":{}}"; \ \ if (qemuMonitorTestAddItem(test, data->cmd, reply) < 0) \ return -1; \ \ if (funcName(qemuMonitorTestGetMonitor(test), __VA_ARGS__) < 0) \ return -1; \ \ return 0; \ } GEN_TEST_FUNC(qemuMonitorJSONSetLink, "vnet0", VIR_DOMAIN_NET_INTERFACE_LINK_STATE_DOWN) GEN_TEST_FUNC(qemuMonitorJSONBlockResize, "vda", "asdf", 123456) GEN_TEST_FUNC(qemuMonitorJSONSetPassword, "spice", "secret_password", "disconnect") GEN_TEST_FUNC(qemuMonitorJSONExpirePassword, "spice", "123456") GEN_TEST_FUNC(qemuMonitorJSONSetBalloon, 1024) GEN_TEST_FUNC(qemuMonitorJSONSetCPU, 1, true) GEN_TEST_FUNC(qemuMonitorJSONEjectMedia, "hdc", true) GEN_TEST_FUNC(qemuMonitorJSONChangeMedia, "hdc", "/foo/bar", "formatstr") GEN_TEST_FUNC(qemuMonitorJSONSaveVirtualMemory, 0, 1024, "/foo/bar") GEN_TEST_FUNC(qemuMonitorJSONSavePhysicalMemory, 0, 1024, "/foo/bar") GEN_TEST_FUNC(qemuMonitorJSONSetMigrationSpeed, 1024) GEN_TEST_FUNC(qemuMonitorJSONSetMigrationDowntime, 1) GEN_TEST_FUNC(qemuMonitorJSONMigrate, QEMU_MONITOR_MIGRATE_BACKGROUND | QEMU_MONITOR_MIGRATE_NON_SHARED_DISK | QEMU_MONITOR_MIGRATE_NON_SHARED_INC, "tcp:localhost:12345") GEN_TEST_FUNC(qemuMonitorJSONDump, "dummy_protocol", "elf", true) GEN_TEST_FUNC(qemuMonitorJSONGraphicsRelocate, VIR_DOMAIN_GRAPHICS_TYPE_SPICE, "localhost", 12345, 12346, "certsubjectval") GEN_TEST_FUNC(qemuMonitorJSONRemoveNetdev, "net0") GEN_TEST_FUNC(qemuMonitorJSONDelDevice, "ide0") GEN_TEST_FUNC(qemuMonitorJSONAddDevice, "some_dummy_devicestr") GEN_TEST_FUNC(qemuMonitorJSONDriveMirror, "vdb", "/foo/bar", "formatstr", 1024, 1234, 31234, true, true) GEN_TEST_FUNC(qemuMonitorJSONBlockdevMirror, "jobname", true, "vdb", "targetnode", 1024, 1234, 31234, true) GEN_TEST_FUNC(qemuMonitorJSONBlockStream, "vdb", "jobname", true, "/foo/bar1", "backingnode", "backingfilename", 1024) GEN_TEST_FUNC(qemuMonitorJSONBlockCommit, "vdb", "jobname", true, "/foo/bar1", "topnode", "/foo/bar2", "basenode", "backingfilename", 1024) GEN_TEST_FUNC(qemuMonitorJSONDrivePivot, "vdb") GEN_TEST_FUNC(qemuMonitorJSONScreendump, "devicename", 1, "/foo/bar") GEN_TEST_FUNC(qemuMonitorJSONOpenGraphics, "spice", "spicefd", false) GEN_TEST_FUNC(qemuMonitorJSONNBDServerAdd, "vda", "export", true, "bitmap") GEN_TEST_FUNC(qemuMonitorJSONDetachCharDev, "serial1") GEN_TEST_FUNC(qemuMonitorJSONBlockdevTrayOpen, "foodev", true) GEN_TEST_FUNC(qemuMonitorJSONBlockdevTrayClose, "foodev") GEN_TEST_FUNC(qemuMonitorJSONBlockdevMediumRemove, "foodev") GEN_TEST_FUNC(qemuMonitorJSONBlockdevMediumInsert, "foodev", "newnode") GEN_TEST_FUNC(qemuMonitorJSONJobDismiss, "jobname") GEN_TEST_FUNC(qemuMonitorJSONJobCancel, "jobname", false) GEN_TEST_FUNC(qemuMonitorJSONJobComplete, "jobname") static int testQemuMonitorJSONqemuMonitorJSONNBDServerStart(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; virStorageNetHostDef server_tcp = { .name = (char *)"localhost", .port = 12345, .transport = VIR_STORAGE_NET_HOST_TRANS_TCP, }; virStorageNetHostDef server_unix = { .socket = (char *)"/tmp/sock", .transport = VIR_STORAGE_NET_HOST_TRANS_UNIX, }; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; if (qemuMonitorTestAddItem(test, "nbd-server-start", "{\"return\":{}}") < 0) return -1; if (qemuMonitorTestAddItem(test, "nbd-server-start", "{\"return\":{}}") < 0) return -1; if (qemuMonitorJSONNBDServerStart(qemuMonitorTestGetMonitor(test), &server_tcp, "test-alias") < 0) return -1; if (qemuMonitorJSONNBDServerStart(qemuMonitorTestGetMonitor(test), &server_unix, "test-alias") < 0) return -1; return 0; } static bool testQemuMonitorJSONqemuMonitorJSONQueryCPUsEqual(struct qemuMonitorQueryCpusEntry *a, struct qemuMonitorQueryCpusEntry *b) { if (a->tid != b->tid || STRNEQ_NULLABLE(a->qom_path, b->qom_path)) return false; return true; } static int testQEMUMonitorJSONqemuMonitorJSONQueryCPUsHelper(qemuMonitorTestPtr test, struct qemuMonitorQueryCpusEntry *expect, bool fast, size_t num) { struct qemuMonitorQueryCpusEntry *cpudata = NULL; size_t ncpudata = 0; size_t i; int ret = -1; if (qemuMonitorJSONQueryCPUs(qemuMonitorTestGetMonitor(test), &cpudata, &ncpudata, true, fast) < 0) goto cleanup; if (ncpudata != num) { virReportError(VIR_ERR_INTERNAL_ERROR, "Expecting ncpupids = %zu but got %zu", num, ncpudata); goto cleanup; } for (i = 0; i < ncpudata; i++) { if (!testQemuMonitorJSONqemuMonitorJSONQueryCPUsEqual(cpudata + i, expect + i)) { virReportError(VIR_ERR_INTERNAL_ERROR, "vcpu entry %zu does not match expected data", i); goto cleanup; } } ret = 0; cleanup: qemuMonitorQueryCpusFree(cpudata, ncpudata); return ret; } static int testQemuMonitorJSONqemuMonitorJSONQueryCPUs(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; struct qemuMonitorQueryCpusEntry expect_slow[] = { {0, 17622, (char *) "/machine/unattached/device[0]", true}, {1, 17624, (char *) "/machine/unattached/device[1]", true}, {2, 17626, (char *) "/machine/unattached/device[2]", true}, {3, 17628, NULL, true}, }; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; qemuMonitorTestSkipDeprecatedValidation(test, true); if (qemuMonitorTestAddItem(test, "query-cpus", "{" " \"return\": [" " {" " \"current\": true," " \"CPU\": 0," " \"qom_path\": \"/machine/unattached/device[0]\"," " \"pc\": -2130530478," " \"halted\": true," " \"thread_id\": 17622" " }," " {" " \"current\": false," " \"CPU\": 1," " \"qom_path\": \"/machine/unattached/device[1]\"," " \"pc\": -2130530478," " \"halted\": true," " \"thread_id\": 17624" " }," " {" " \"current\": false," " \"CPU\": 2," " \"qom_path\": \"/machine/unattached/device[2]\"," " \"pc\": -2130530478," " \"halted\": true," " \"thread_id\": 17626" " }," " {" " \"current\": false," " \"CPU\": 3," " \"pc\": -2130530478," " \"halted\": true," " \"thread_id\": 17628" " }" " ]," " \"id\": \"libvirt-7\"" "}") < 0) return -1; /* query-cpus */ if (testQEMUMonitorJSONqemuMonitorJSONQueryCPUsHelper(test, expect_slow, false, 4)) return -1; return 0; } static int testQemuMonitorJSONqemuMonitorJSONQueryCPUsFast(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; struct qemuMonitorQueryCpusEntry expect_fast[] = { {0, 17629, (char *) "/machine/unattached/device[0]", false}, {1, 17630, (char *) "/machine/unattached/device[1]", false}, }; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; if (qemuMonitorTestAddItem(test, "query-cpus-fast", "{" " \"return\": [" " {" " \"cpu-index\": 0," " \"qom-path\": \"/machine/unattached/device[0]\"," " \"thread-id\": 17629" " }," " {" " \"cpu-index\": 1," " \"qom-path\": \"/machine/unattached/device[1]\"," " \"thread-id\": 17630" " }" " ]," " \"id\": \"libvirt-8\"" "}") < 0) return -1; /* query-cpus-fast */ if (testQEMUMonitorJSONqemuMonitorJSONQueryCPUsHelper(test, expect_fast, true, 2)) return -1; return 0; } static int testQemuMonitorJSONqemuMonitorJSONGetBalloonInfo(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; unsigned long long currmem; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; if (qemuMonitorTestAddItem(test, "query-balloon", "{" " \"return\": {" " \"actual\": 18446744073709551615" " }," " \"id\": \"libvirt-9\"" "}") < 0) return -1; if (qemuMonitorJSONGetBalloonInfo(qemuMonitorTestGetMonitor(test), &currmem) < 0) return -1; if (currmem != (18446744073709551615ULL/1024)) { virReportError(VIR_ERR_INTERNAL_ERROR, "Unexpected currmem value: %llu", currmem); return -1; } return 0; } static int testQemuMonitorJSONqemuMonitorJSONGetVirtType(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; virDomainVirtType virtType; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; if (qemuMonitorTestAddItem(test, "query-kvm", "{" " \"return\": {" " \"enabled\": true," " \"present\": true" " }," " \"id\": \"libvirt-8\"" "}") < 0 || qemuMonitorTestAddItem(test, "query-kvm", "{" " \"return\": {" " \"enabled\": false," " \"present\": true" " }," " \"id\": \"libvirt-7\"" "}") < 0) return -1; if (qemuMonitorJSONGetVirtType(qemuMonitorTestGetMonitor(test), &virtType) < 0) return -1; if (virtType != VIR_DOMAIN_VIRT_KVM) { virReportError(VIR_ERR_INTERNAL_ERROR, "Unexpected virt type: %d, expecting %d", virtType, VIR_DOMAIN_VIRT_KVM); return -1; } if (qemuMonitorJSONGetVirtType(qemuMonitorTestGetMonitor(test), &virtType) < 0) return -1; if (virtType != VIR_DOMAIN_VIRT_QEMU) { virReportError(VIR_ERR_INTERNAL_ERROR, "Unexpected virt type: %d, expecting %d", virtType, VIR_DOMAIN_VIRT_QEMU); return -1; } return 0; } static void testQemuMonitorJSONGetBlockInfoPrint(const struct qemuDomainDiskInfo *d) { VIR_TEST_VERBOSE("removable: %d, tray: %d, tray_open: %d, empty: %d, " "io_status: %d, nodename: '%s'", d->removable, d->tray, d->tray_open, d->empty, d->io_status, NULLSTR(d->nodename)); } static int testHashEqualQemuDomainDiskInfo(const void *value1, const void *value2) { const struct qemuDomainDiskInfo *info1 = value1, *info2 = value2; int ret; if ((ret = memcmp(info1, info2, sizeof(*info1))) != 0) { testQemuMonitorJSONGetBlockInfoPrint(info1); testQemuMonitorJSONGetBlockInfoPrint(info2); } return ret; } static int testQemuMonitorJSONqemuMonitorJSONGetBlockInfo(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; int ret = -1; virHashTablePtr blockDevices = NULL, expectedBlockDevices = NULL; struct qemuDomainDiskInfo *info; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; if (!(blockDevices = virHashCreate(32, virHashValueFree)) || !(expectedBlockDevices = virHashCreate(32, virHashValueFree))) goto cleanup; info = g_new0(struct qemuDomainDiskInfo, 1); if (virHashAddEntry(expectedBlockDevices, "virtio-disk0", info) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "Unable to create expectedBlockDevices hash table"); goto cleanup; } info = g_new0(struct qemuDomainDiskInfo, 1); if (virHashAddEntry(expectedBlockDevices, "virtio-disk1", info) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "Unable to create expectedBlockDevices hash table"); goto cleanup; } info = g_new0(struct qemuDomainDiskInfo, 1); info->removable = true; info->tray = true; if (virHashAddEntry(expectedBlockDevices, "ide0-1-0", info) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "Unable to create expectedBlockDevices hash table"); goto cleanup; } info = g_new0(struct qemuDomainDiskInfo, 1); info->removable = true; info->tray = true; info->empty = true; if (virHashAddEntry(expectedBlockDevices, "ide0-1-1", info) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "Unable to create expectedBlockDevices hash table"); goto cleanup; } if (qemuMonitorTestAddItem(test, "query-block", queryBlockReply) < 0) goto cleanup; if (qemuMonitorJSONGetBlockInfo(qemuMonitorTestGetMonitor(test), blockDevices) < 0) goto cleanup; if (!virHashEqual(blockDevices, expectedBlockDevices, testHashEqualQemuDomainDiskInfo)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "Hashtable is different to the expected one"); goto cleanup; } ret = 0; cleanup: virHashFree(blockDevices); virHashFree(expectedBlockDevices); return ret; } static int testQemuMonitorJSONqemuMonitorJSONGetAllBlockStatsInfo(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; virHashTablePtr blockstats = NULL; qemuBlockStatsPtr stats; int ret = -1; g_autoptr(qemuMonitorTest) test = NULL; const char *reply = "{" " \"return\": [" " {" " \"device\": \"drive-virtio-disk0\"," " \"parent\": {" " \"stats\": {" " \"flush_total_time_ns\": 0," " \"wr_highest_offset\": 5256018944," " \"wr_total_time_ns\": 0," " \"wr_bytes\": 0," " \"rd_total_time_ns\": 0," " \"flush_operations\": 0," " \"wr_operations\": 0," " \"rd_bytes\": 0," " \"rd_operations\": 0" " }" " }," " \"stats\": {" " \"flush_total_time_ns\": 0," " \"wr_highest_offset\": 10406001664," " \"wr_total_time_ns\": 530699221," " \"wr_bytes\": 2845696," " \"rd_total_time_ns\": 640616474," " \"flush_operations\": 0," " \"wr_operations\": 174," " \"rd_bytes\": 28505088," " \"rd_operations\": 1279" " }" " }," " {" " \"device\": \"drive-virtio-disk1\"," " \"parent\": {" " \"stats\": {" " \"flush_total_time_ns\": 0," " \"wr_highest_offset\": 0," " \"wr_total_time_ns\": 0," " \"wr_bytes\": 0," " \"rd_total_time_ns\": 0," " \"flush_operations\": 0," " \"wr_operations\": 0," " \"rd_bytes\": 0," " \"rd_operations\": 0" " }" " }," " \"stats\": {" " \"flush_total_time_ns\": 0," " \"wr_highest_offset\": 0," " \"wr_total_time_ns\": 0," " \"wr_bytes\": 0," " \"rd_total_time_ns\": 8232156," " \"flush_operations\": 0," " \"wr_operations\": 0," " \"rd_bytes\": 348160," " \"rd_operations\": 85" " }" " }," " {" " \"device\": \"drive-ide0-1-0\"," " \"parent\": {" " \"stats\": {" " \"flush_total_time_ns\": 0," " \"wr_total_time_ns\": 0," " \"wr_bytes\": 0," " \"rd_total_time_ns\": 0," " \"flush_operations\": 0," " \"wr_operations\": 0," " \"rd_bytes\": 0," " \"rd_operations\": 0" " }" " }," " \"stats\": {" " \"flush_total_time_ns\": 0," " \"wr_highest_offset\": 0," " \"wr_total_time_ns\": 0," " \"wr_bytes\": 0," " \"rd_total_time_ns\": 1004952," " \"flush_operations\": 0," " \"wr_operations\": 0," " \"rd_bytes\": 49250," " \"rd_operations\": 16" " }" " }" " ]," " \"id\": \"libvirt-11\"" "}"; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; if (!(blockstats = virHashCreate(10, virHashValueFree))) goto cleanup; if (qemuMonitorTestAddItem(test, "query-blockstats", reply) < 0) goto cleanup; #define CHECK0FULL(var, value, varformat, valformat) \ if (stats->var != value) { \ virReportError(VIR_ERR_INTERNAL_ERROR, \ "Invalid " #var " value: " varformat \ ", expected " valformat, \ stats->var, value); \ goto cleanup; \ } #define CHECK0(var, value) CHECK0FULL(var, value, "%lld", "%d") #define CHECK(NAME, RD_REQ, RD_BYTES, RD_TOTAL_TIMES, WR_REQ, WR_BYTES, \ WR_TOTAL_TIMES, FLUSH_REQ, FLUSH_TOTAL_TIMES, \ WR_HIGHEST_OFFSET, WR_HIGHEST_OFFSET_VALID) \ if (!(stats = virHashLookup(blockstats, NAME))) { \ virReportError(VIR_ERR_INTERNAL_ERROR, \ "block stats for device '%s' is missing", NAME); \ goto cleanup; \ } \ CHECK0(rd_req, RD_REQ) \ CHECK0(rd_bytes, RD_BYTES) \ CHECK0(rd_total_times, RD_TOTAL_TIMES) \ CHECK0(wr_req, WR_REQ) \ CHECK0(wr_bytes, WR_BYTES) \ CHECK0(wr_total_times, WR_TOTAL_TIMES) \ CHECK0(flush_req, FLUSH_REQ) \ CHECK0(flush_total_times, FLUSH_TOTAL_TIMES) \ CHECK0FULL(wr_highest_offset, WR_HIGHEST_OFFSET, "%llu", "%llu") \ CHECK0FULL(wr_highest_offset_valid, WR_HIGHEST_OFFSET_VALID, "%d", "%d") if (qemuMonitorJSONGetAllBlockStatsInfo(qemuMonitorTestGetMonitor(test), blockstats, false) < 0) goto cleanup; if (!blockstats) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "qemuMonitorJSONGetAllBlockStatsInfo didn't return stats"); goto cleanup; } CHECK("virtio-disk0", 1279, 28505088, 640616474, 174, 2845696, 530699221, 0, 0, 5256018944ULL, true) CHECK("virtio-disk1", 85, 348160, 8232156, 0, 0, 0, 0, 0, 0ULL, true) CHECK("ide0-1-0", 16, 49250, 1004952, 0, 0, 0, 0, 0, 0ULL, false) ret = 0; #undef CHECK #undef CHECK0 #undef CHECK0FULL cleanup: virHashFree(blockstats); return ret; } static int testQemuMonitorJSONqemuMonitorJSONGetMigrationCacheSize(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; unsigned long long cacheSize; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; qemuMonitorTestSkipDeprecatedValidation(test, true); if (qemuMonitorTestAddItem(test, "query-migrate-cache-size", "{" " \"return\": 67108864," " \"id\": \"libvirt-12\"" "}") < 0) return -1; if (qemuMonitorJSONGetMigrationCacheSize(qemuMonitorTestGetMonitor(test), &cacheSize) < 0) return -1; if (cacheSize != 67108864) { virReportError(VIR_ERR_INTERNAL_ERROR, "Invalid cacheSize: %llu, expected 67108864", cacheSize); return -1; } return 0; } static int testQemuMonitorJSONqemuMonitorJSONGetMigrationStats(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; int ret = -1; qemuMonitorMigrationStats stats, expectedStats; char *error = NULL; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; memset(&expectedStats, 0, sizeof(expectedStats)); expectedStats.status = QEMU_MONITOR_MIGRATION_STATUS_ACTIVE; expectedStats.total_time = 47; expectedStats.ram_total = 1611038720; expectedStats.ram_remaining = 1605013504; expectedStats.ram_transferred = 3625548; if (qemuMonitorTestAddItem(test, "query-migrate", "{" " \"return\": {" " \"status\": \"active\"," " \"total-time\": 47," " \"ram\": {" " \"total\": 1611038720," " \"remaining\": 1605013504," " \"transferred\": 3625548" " }" " }," " \"id\": \"libvirt-13\"" "}") < 0 || qemuMonitorTestAddItem(test, "query-migrate", "{" " \"return\": {" " \"status\": \"failed\"," " \"error-desc\": \"It's broken\"" " }," " \"id\": \"libvirt-14\"" "}") < 0) goto cleanup; if (qemuMonitorJSONGetMigrationStats(qemuMonitorTestGetMonitor(test), &stats, &error) < 0) goto cleanup; if (memcmp(&stats, &expectedStats, sizeof(stats)) != 0 || error) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "Invalid migration statistics"); goto cleanup; } memset(&stats, 0, sizeof(stats)); if (qemuMonitorJSONGetMigrationStats(qemuMonitorTestGetMonitor(test), &stats, &error) < 0) goto cleanup; if (stats.status != QEMU_MONITOR_MIGRATION_STATUS_ERROR || STRNEQ_NULLABLE(error, "It's broken")) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "Invalid failed migration status"); goto cleanup; } ret = 0; cleanup: VIR_FREE(error); return ret; } static int testHashEqualChardevInfo(const void *value1, const void *value2) { const qemuMonitorChardevInfo *info1 = value1; const qemuMonitorChardevInfo *info2 = value2; if (info1->state != info2->state) goto error; if (STRNEQ_NULLABLE(info1->ptyPath, info2->ptyPath)) goto error; return 0; error: fprintf(stderr, "\n" "info1->state: %d info2->state: %d\n" "info1->ptyPath: %s info2->ptyPath: %s\n", info1->state, info2->state, info1->ptyPath, info2->ptyPath); return -1; } static int testQemuMonitorJSONqemuMonitorJSONGetChardevInfo(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; int ret = -1; virHashTablePtr info = NULL, expectedInfo = NULL; qemuMonitorChardevInfo info0 = { NULL, VIR_DOMAIN_CHR_DEVICE_STATE_DEFAULT }; qemuMonitorChardevInfo info1 = { (char *) "/dev/pts/21", VIR_DOMAIN_CHR_DEVICE_STATE_CONNECTED }; qemuMonitorChardevInfo info2 = { (char *) "/dev/pts/20", VIR_DOMAIN_CHR_DEVICE_STATE_DEFAULT }; qemuMonitorChardevInfo info3 = { NULL, VIR_DOMAIN_CHR_DEVICE_STATE_DISCONNECTED }; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; if (!(info = virHashCreate(32, qemuMonitorChardevInfoFree)) || !(expectedInfo = virHashCreate(32, NULL))) goto cleanup; if (virHashAddEntry(expectedInfo, "charserial1", &info1) < 0 || virHashAddEntry(expectedInfo, "charserial0", &info2) < 0 || virHashAddEntry(expectedInfo, "charmonitor", &info0) < 0 || virHashAddEntry(expectedInfo, "charserial2", &info3) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "Unable to create expectedInfo hash table"); goto cleanup; } if (qemuMonitorTestAddItem(test, "query-chardev", "{" " \"return\": [" " {" " \"filename\": \"pty:/dev/pts/21\"," " \"label\": \"charserial1\"," " \"frontend-open\": true" " }," " {" " \"filename\": \"pty:/dev/pts/20\"," " \"label\": \"charserial0\"" " }," " {" " \"filename\": \"unix:/var/lib/libvirt/qemu/gentoo.monitor,server\"," " \"label\": \"charmonitor\"" " }," " {" " \"filename\": \"unix:/path/to/socket,server\"," " \"label\": \"charserial2\"," " \"frontend-open\": false" " }" " ]," " \"id\": \"libvirt-15\"" "}") < 0) goto cleanup; if (qemuMonitorJSONGetChardevInfo(qemuMonitorTestGetMonitor(test), info) < 0) goto cleanup; if (!virHashEqual(info, expectedInfo, testHashEqualChardevInfo)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "Hashtable is different to the expected one"); goto cleanup; } ret = 0; cleanup: virHashFree(info); virHashFree(expectedInfo); return ret; } static int testValidateGetBlockIoThrottle(const virDomainBlockIoTuneInfo *info, const virDomainBlockIoTuneInfo *expectedInfo) { #define VALIDATE_IOTUNE(field) \ if (info->field != expectedInfo->field) { \ virReportError(VIR_ERR_INTERNAL_ERROR, \ "info->%s=%llu != expected=%llu", \ #field, info->field, expectedInfo->field); \ return -1; \ } \ if (info->field##_max != expectedInfo->field##_max) { \ virReportError(VIR_ERR_INTERNAL_ERROR, \ "info->%s_max=%llu != expected=%llu", \ #field, info->field##_max, expectedInfo->field##_max); \ return -1; \ } \ if (info->field##_max_length != expectedInfo->field##_max_length) { \ virReportError(VIR_ERR_INTERNAL_ERROR, \ "info->%s_max_length=%llu != expected=%llu", \ #field, info->field##_max_length, \ expectedInfo->field##_max_length); \ return -1; \ } VALIDATE_IOTUNE(total_bytes_sec); VALIDATE_IOTUNE(read_bytes_sec); VALIDATE_IOTUNE(write_bytes_sec); VALIDATE_IOTUNE(total_iops_sec); VALIDATE_IOTUNE(read_iops_sec); VALIDATE_IOTUNE(write_iops_sec); if (info->size_iops_sec != expectedInfo->size_iops_sec) { virReportError(VIR_ERR_INTERNAL_ERROR, "info->size_iops_sec=%llu != expected=%llu", info->size_iops_sec, expectedInfo->size_iops_sec); return -1; } if (STRNEQ(info->group_name, expectedInfo->group_name)) { virReportError(VIR_ERR_INTERNAL_ERROR, "info->group_name=%s != expected=%s", info->group_name, expectedInfo->group_name); return -1; } #undef VALIDATE_IOTUNE return 0; } static int testQemuMonitorJSONqemuMonitorJSONSetBlockIoThrottle(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; int ret = -1; virDomainBlockIoTuneInfo info, expectedInfo; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; expectedInfo = (virDomainBlockIoTuneInfo) {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, NULL, 15, 16, 17, 18, 19, 20}; expectedInfo.group_name = g_strdup("group14"); if (qemuMonitorTestAddItem(test, "query-block", queryBlockReply) < 0 || qemuMonitorTestAddItemParams(test, "block_set_io_throttle", "{\"return\":{}}", "device", "\"drive-virtio-disk1\"", "bps", "1", "bps_rd", "2", "bps_wr", "3", "iops", "4", "iops_rd", "5", "iops_wr", "6", "bps_max", "7", "bps_rd_max", "8", "bps_wr_max", "9", "iops_max", "10", "iops_rd_max", "11", "iops_wr_max", "12", "iops_size", "13", "group", "\"group14\"", "bps_max_length", "15", "bps_rd_max_length", "16", "bps_wr_max_length", "17", "iops_max_length", "18", "iops_rd_max_length", "19", "iops_wr_max_length", "20", NULL, NULL) < 0) goto cleanup; if (qemuMonitorJSONGetBlockIoThrottle(qemuMonitorTestGetMonitor(test), "drive-virtio-disk0", NULL, &info) < 0) goto cleanup; if (testValidateGetBlockIoThrottle(&info, &expectedInfo) < 0) goto cleanup; if (qemuMonitorJSONSetBlockIoThrottle(qemuMonitorTestGetMonitor(test), "drive-virtio-disk1", NULL, &info, true, true, true) < 0) goto cleanup; ret = 0; cleanup: VIR_FREE(info.group_name); VIR_FREE(expectedInfo.group_name); return ret; } static int testQemuMonitorJSONqemuMonitorJSONGetTargetArch(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; g_autofree char *arch = NULL; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; if (qemuMonitorTestAddItem(test, "query-target", "{" " \"return\": {" " \"arch\": \"x86_64\"" " }," " \"id\": \"libvirt-21\"" "}") < 0) return -1; if (!(arch = qemuMonitorJSONGetTargetArch(qemuMonitorTestGetMonitor(test)))) return -1; if (STRNEQ(arch, "x86_64")) { virReportError(VIR_ERR_INTERNAL_ERROR, "Unexpected architecture %s, expecting x86_64", arch); return -1; } return 0; } static int testQemuMonitorJSONqemuMonitorJSONGetMigrationCapabilities(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; int ret = -1; const char *cap; char **caps = NULL; virBitmapPtr bitmap = NULL; virJSONValuePtr json = NULL; const char *reply = "{" " \"return\": [" " {" " \"state\": false," " \"capability\": \"xbzrle\"" " }" " ]," " \"id\": \"libvirt-22\"" "}"; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; if (qemuMonitorTestAddItem(test, "query-migrate-capabilities", reply) < 0 || qemuMonitorTestAddItem(test, "migrate-set-capabilities", "{\"return\":{}}") < 0) goto cleanup; if (qemuMonitorGetMigrationCapabilities(qemuMonitorTestGetMonitor(test), &caps) < 0) goto cleanup; cap = qemuMigrationCapabilityTypeToString(QEMU_MIGRATION_CAP_XBZRLE); if (!virStringListHasString((const char **) caps, cap)) { virReportError(VIR_ERR_INTERNAL_ERROR, "Expected capability %s is missing", cap); goto cleanup; } bitmap = virBitmapNew(QEMU_MIGRATION_CAP_LAST); ignore_value(virBitmapSetBit(bitmap, QEMU_MIGRATION_CAP_XBZRLE)); if (!(json = qemuMigrationCapsToJSON(bitmap, bitmap))) goto cleanup; ret = qemuMonitorJSONSetMigrationCapabilities(qemuMonitorTestGetMonitor(test), json); json = NULL; cleanup: virJSONValueFree(json); g_strfreev(caps); virBitmapFree(bitmap); return ret; } static int testQemuMonitorJSONqemuMonitorJSONSendKey(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; unsigned int keycodes[] = {43, 26, 46, 32}; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; if (qemuMonitorTestAddItem(test, "send-key", "{\"return\": {}, \"id\": \"libvirt-16\"}") < 0) return -1; if (qemuMonitorJSONSendKey(qemuMonitorTestGetMonitor(test), 0, keycodes, G_N_ELEMENTS(keycodes)) < 0) return -1; return 0; } static int testQemuMonitorJSONqemuMonitorJSONSendKeyHoldtime(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; unsigned int keycodes[] = {43, 26, 46, 32}; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; if (qemuMonitorTestAddItemParams(test, "send-key", "{\"return\":{}}", "hold-time", "31337", "keys", "[{\"type\":\"number\",\"data\":43}," "{\"type\":\"number\",\"data\":26}," "{\"type\":\"number\",\"data\":46}," "{\"type\":\"number\",\"data\":32}]", NULL, NULL) < 0) return -1; if (qemuMonitorJSONSendKey(qemuMonitorTestGetMonitor(test), 31337, keycodes, G_N_ELEMENTS(keycodes)) < 0) return -1; return 0; } static int testQemuMonitorJSONqemuMonitorSupportsActiveCommit(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; const char *error1 = "{" " \"error\": {" " \"class\": \"DeviceNotFound\"," " \"desc\": \"Device 'bogus' not found\"" " }" "}"; const char *error2 = "{" " \"error\": {" " \"class\": \"GenericError\"," " \"desc\": \"Parameter 'top' is missing\"" " }" "}"; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; if (qemuMonitorTestAddItemParams(test, "block-commit", error1, "device", "\"bogus\"", NULL, NULL) < 0) return -1; if (!qemuMonitorSupportsActiveCommit(qemuMonitorTestGetMonitor(test))) return -1; if (qemuMonitorTestAddItemParams(test, "block-commit", error2, "device", "\"bogus\"", NULL, NULL) < 0) return -1; if (qemuMonitorSupportsActiveCommit(qemuMonitorTestGetMonitor(test))) return -1; return 0; } static int testQemuMonitorJSONqemuMonitorJSONGetDumpGuestMemoryCapability(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; int cap; const char *reply = "{" " \"return\": {" " \"formats\": [" " \"elf\"," " \"kdump-zlib\"," " \"kdump-lzo\"," " \"kdump-snappy\"" " ]" " }," " \"id\": \"libvirt-9\"" "}"; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; if (qemuMonitorTestAddItem(test, "query-dump-guest-memory-capability", reply) < 0) return -1; cap = qemuMonitorJSONGetDumpGuestMemoryCapability( qemuMonitorTestGetMonitor(test), "elf"); if (cap != 1) { virReportError(VIR_ERR_INTERNAL_ERROR, "Unexpected capability: %d, expecting 1", cap); return -1; } return 0; } struct testCPUData { const char *name; virDomainXMLOptionPtr xmlopt; virHashTablePtr schema; }; static int testQemuMonitorJSONGetCPUData(const void *opaque) { const struct testCPUData *data = opaque; virCPUDataPtr cpuData = NULL; char *jsonFile = NULL; char *dataFile = NULL; char *jsonStr = NULL; char *actual = NULL; int ret = -1; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(data->xmlopt, data->schema))) return -1; jsonFile = g_strdup_printf("%s/qemumonitorjsondata/qemumonitorjson-getcpu-%s.json", abs_srcdir, data->name); dataFile = g_strdup_printf("%s/qemumonitorjsondata/qemumonitorjson-getcpu-%s.data", abs_srcdir, data->name); if (virTestLoadFile(jsonFile, &jsonStr) < 0) goto cleanup; if (qemuMonitorTestAddItem(test, "qom-list", "{" " \"return\": [" " {" " \"name\": \"filtered-features\"," " \"type\": \"X86CPUFeatureWordInfo\"" " }," " {" " \"name\": \"feature-words\"," " \"type\": \"X86CPUFeatureWordInfo\"" " }" " ]," " \"id\": \"libvirt-19\"" "}") < 0) goto cleanup; if (qemuMonitorTestAddItem(test, "qom-get", jsonStr) < 0) goto cleanup; if (qemuMonitorJSONGetGuestCPUx86(qemuMonitorTestGetMonitor(test), &cpuData, NULL) < 0) goto cleanup; if (!(actual = virCPUDataFormat(cpuData))) goto cleanup; if (virTestCompareToFile(actual, dataFile) < 0) goto cleanup; ret = 0; cleanup: VIR_FREE(jsonFile); VIR_FREE(dataFile); VIR_FREE(jsonStr); VIR_FREE(actual); virCPUDataFree(cpuData); return ret; } static int testQemuMonitorJSONGetNonExistingCPUData(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; virCPUDataPtr cpuData = NULL; int rv, ret = -1; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; if (qemuMonitorTestAddItem(test, "qom-list", "{" " \"id\": \"libvirt-7\"," " \"error\": {" " \"class\": \"CommandNotFound\"," " \"desc\": \"The command qom-list has not been found\"" " }" "}") < 0) goto cleanup; rv = qemuMonitorJSONGetGuestCPUx86(qemuMonitorTestGetMonitor(test), &cpuData, NULL); if (rv != -2) { virReportError(VIR_ERR_INTERNAL_ERROR, "Unexpected return value %d, expecting -2", rv); goto cleanup; } if (cpuData) { virReportError(VIR_ERR_INTERNAL_ERROR, "Unexpected allocation of data = %p, expecting NULL", cpuData); goto cleanup; } ret = 0; cleanup: virCPUDataFree(cpuData); return ret; } static int testQemuMonitorJSONGetIOThreads(const void *opaque) { const testGenericData *data = opaque; virDomainXMLOptionPtr xmlopt = data->xmlopt; qemuMonitorIOThreadInfoPtr *info; int ninfo = 0; int ret = -1; size_t i; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) return -1; if (qemuMonitorTestAddItem(test, "query-iothreads", "{ " " \"return\": [ " " { " " \"id\": \"iothread1\", " " \"thread-id\": 30992 " " }, " " { " " \"id\": \"iothread2\", " " \"thread-id\": 30993 " " } " " ]" "}") < 0) goto cleanup; if ((ninfo = qemuMonitorGetIOThreads(qemuMonitorTestGetMonitor(test), &info)) < 0) goto cleanup; if (ninfo != 2) { virReportError(VIR_ERR_INTERNAL_ERROR, "ninfo %d is not 2", ninfo); goto cleanup; } #define CHECK(i, wantiothread_id, wantthread_id) \ do { \ if (info[i]->iothread_id != (wantiothread_id)) { \ virReportError(VIR_ERR_INTERNAL_ERROR, \ "iothread_id %u is not %u", \ info[i]->iothread_id, (wantiothread_id)); \ goto cleanup; \ } \ if (info[i]->thread_id != (wantthread_id)) { \ virReportError(VIR_ERR_INTERNAL_ERROR, \ "thread_id %d is not %d", \ info[i]->thread_id, (wantthread_id)); \ goto cleanup; \ } \ } while (0) CHECK(0, 1, 30992); CHECK(1, 2, 30993); #undef CHECK ret = 0; cleanup: for (i = 0; i < ninfo; i++) VIR_FREE(info[i]); VIR_FREE(info); return ret; } struct testCPUInfoData { const char *name; size_t maxvcpus; virDomainXMLOptionPtr xmlopt; bool fast; virHashTablePtr schema; }; static char * testQemuMonitorCPUInfoFormat(qemuMonitorCPUInfoPtr vcpus, size_t nvcpus) { g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; qemuMonitorCPUInfoPtr vcpu; size_t i; for (i = 0; i < nvcpus; i++) { vcpu = vcpus + i; virBufferAsprintf(&buf, "[vcpu libvirt-id='%zu']\n", i); virBufferAdjustIndent(&buf, 4); virBufferAsprintf(&buf, "online=%s\n", vcpu->online ? "yes" : "no"); virBufferAsprintf(&buf, "hotpluggable=%s\n", vcpu->hotpluggable ? "yes" : "no"); if (vcpu->tid) virBufferAsprintf(&buf, "thread-id='%llu'\n", (unsigned long long) vcpu->tid); if (vcpu->id != 0) virBufferAsprintf(&buf, "enable-id='%d'\n", vcpu->id); if (vcpu->qemu_id != -1) virBufferAsprintf(&buf, "query-cpus-id='%d'\n", vcpu->qemu_id); if (vcpu->type) virBufferAsprintf(&buf, "type='%s'\n", vcpu->type); if (vcpu->alias) virBufferAsprintf(&buf, "alias='%s'\n", vcpu->alias); if (vcpu->qom_path) virBufferAsprintf(&buf, "qom_path='%s'\n", vcpu->qom_path); if (vcpu->socket_id != -1 || vcpu->core_id != -1 || vcpu->thread_id != -1 || vcpu->vcpus != 0) { virBufferAddLit(&buf, "topology:"); if (vcpu->socket_id != -1) virBufferAsprintf(&buf, " socket='%d'", vcpu->socket_id); if (vcpu->die_id != -1) virBufferAsprintf(&buf, " die='%d'", vcpu->die_id); if (vcpu->core_id != -1) virBufferAsprintf(&buf, " core='%d'", vcpu->core_id); if (vcpu->thread_id != -1) virBufferAsprintf(&buf, " thread='%d'", vcpu->thread_id); if (vcpu->node_id != -1) virBufferAsprintf(&buf, " node='%d'", vcpu->node_id); if (vcpu->vcpus != 0) virBufferAsprintf(&buf, " vcpus='%u'", vcpu->vcpus); virBufferAddLit(&buf, "\n"); } if (vcpu->halted) virBufferAddLit(&buf, "halted\n"); virBufferAdjustIndent(&buf, -4); } return virBufferContentAndReset(&buf); } static int testQemuMonitorCPUInfo(const void *opaque) { const struct testCPUInfoData *data = opaque; virDomainObjPtr vm = NULL; char *queryCpusFile = NULL; char *queryHotpluggableFile = NULL; char *dataFile = NULL; char *queryCpusStr = NULL; char *queryHotpluggableStr = NULL; char *actual = NULL; const char *queryCpusFunction; qemuMonitorCPUInfoPtr vcpus = NULL; int rc; int ret = -1; g_autoptr(qemuMonitorTest) test = NULL; if (!(test = qemuMonitorTestNewSchema(data->xmlopt, data->schema))) return -1; queryCpusFile = g_strdup_printf("%s/qemumonitorjsondata/qemumonitorjson-cpuinfo-%s-cpus.json", abs_srcdir, data->name); queryHotpluggableFile = g_strdup_printf("%s/qemumonitorjsondata/qemumonitorjson-cpuinfo-%s-hotplug.json", abs_srcdir, data->name); dataFile = g_strdup_printf("%s/qemumonitorjsondata/qemumonitorjson-cpuinfo-%s.data", abs_srcdir, data->name); if (virTestLoadFile(queryCpusFile, &queryCpusStr) < 0) goto cleanup; if (virTestLoadFile(queryHotpluggableFile, &queryHotpluggableStr) < 0) goto cleanup; if (qemuMonitorTestAddItem(test, "query-hotpluggable-cpus", queryHotpluggableStr) < 0) goto cleanup; if (data->fast) { queryCpusFunction = "query-cpus-fast"; } else { queryCpusFunction = "query-cpus"; qemuMonitorTestSkipDeprecatedValidation(test, true); } if (qemuMonitorTestAddItem(test, queryCpusFunction, queryCpusStr) < 0) goto cleanup; vm = qemuMonitorTestGetDomainObj(test); if (!vm) goto cleanup; rc = qemuMonitorGetCPUInfo(qemuMonitorTestGetMonitor(test), &vcpus, data->maxvcpus, true, data->fast); if (rc < 0) goto cleanup; actual = testQemuMonitorCPUInfoFormat(vcpus, data->maxvcpus); if (virTestCompareToFile(actual, dataFile) < 0) goto cleanup; ret = 0; cleanup: VIR_FREE(queryCpusFile); VIR_FREE(queryHotpluggableFile); VIR_FREE(dataFile); VIR_FREE(queryCpusStr); VIR_FREE(queryHotpluggableStr); VIR_FREE(actual); qemuMonitorCPUInfoFree(vcpus, data->maxvcpus); return ret; } static int testBlockNodeNameDetectFormat(void *payload, const void *name, void *opaque) { qemuBlockNodeNameBackingChainDataPtr entry = payload; const char *diskalias = name; virBufferPtr buf = opaque; virBufferSetIndent(buf, 0); virBufferAdd(buf, diskalias, -1); virBufferAddLit(buf, "\n"); while (entry) { virBufferAsprintf(buf, "filename : '%s'\n", entry->qemufilename); virBufferAsprintf(buf, "format node : '%s'\n", NULLSTR(entry->nodeformat)); virBufferAsprintf(buf, "format drv : '%s'\n", NULLSTR(entry->drvformat)); virBufferAsprintf(buf, "storage node: '%s'\n", NULLSTR(entry->nodestorage)); virBufferAsprintf(buf, "storage drv : '%s'\n", NULLSTR(entry->drvstorage)); virBufferAdjustIndent(buf, 2); entry = entry->backing; } virBufferSetIndent(buf, 0); virBufferAddLit(buf, "\n"); return 0; } static int testBlockNodeNameDetect(const void *opaque) { const char *testname = opaque; const char *pathprefix = "qemumonitorjsondata/qemumonitorjson-nodename-"; char *resultFile = NULL; char *actual = NULL; virJSONValuePtr namedNodesJson = NULL; virJSONValuePtr blockstatsJson = NULL; virHashTablePtr nodedata = NULL; g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; int ret = -1; resultFile = g_strdup_printf("%s/%s%s.result", abs_srcdir, pathprefix, testname); if (!(namedNodesJson = virTestLoadFileJSON(pathprefix, testname, "-named-nodes.json", NULL))) goto cleanup; if (!(blockstatsJson = virTestLoadFileJSON(pathprefix, testname, "-blockstats.json", NULL))) goto cleanup; if (!(nodedata = qemuBlockNodeNameGetBackingChain(namedNodesJson, blockstatsJson))) goto cleanup; virHashForEach(nodedata, testBlockNodeNameDetectFormat, &buf); virBufferTrim(&buf, "\n"); actual = virBufferContentAndReset(&buf); if (virTestCompareToFile(actual, resultFile) < 0) goto cleanup; ret = 0; cleanup: VIR_FREE(resultFile); VIR_FREE(actual); virHashFree(nodedata); virJSONValueFree(namedNodesJson); virJSONValueFree(blockstatsJson); return ret; } struct testQAPISchemaData { virHashTablePtr schema; const char *name; const char *query; const char *json; bool success; int rc; bool replyobj; }; static int testQAPISchemaQuery(const void *opaque) { const struct testQAPISchemaData *data = opaque; virJSONValuePtr replyobj = NULL; int rc; rc = virQEMUQAPISchemaPathGet(data->query, data->schema, &replyobj); if (data->rc != rc || data->replyobj != !!replyobj) { VIR_TEST_VERBOSE("\n success: expected '%d' got '%d', replyobj: expected '%d' got '%d'", data->rc, rc, data->replyobj, !!replyobj); return -1; } return 0; } static int testQAPISchemaValidate(const void *opaque) { const struct testQAPISchemaData *data = opaque; g_auto(virBuffer) debug = VIR_BUFFER_INITIALIZER; virJSONValuePtr schemaroot; virJSONValuePtr json = NULL; int ret = -1; if (virQEMUQAPISchemaPathGet(data->query, data->schema, &schemaroot) < 0) goto cleanup; if (!(json = virJSONValueFromString(data->json))) goto cleanup; if ((testQEMUSchemaValidate(json, schemaroot, data->schema, false, &debug) == 0) != data->success) { if (!data->success) VIR_TEST_VERBOSE("\nschema validation should have failed"); } else { ret = 0; } if (virTestGetDebug() >= 3 || (ret < 0 && virTestGetVerbose())) { char *debugstr = virBufferContentAndReset(&debug); fprintf(stderr, "\n%s\n", debugstr); VIR_FREE(debugstr); } cleanup: virJSONValueFree(json); return ret; } /** * testQAPISchemaObjectDeviceAdd: * * Purpose of this test is to add a last-resort notification that 'object-add' * and 'device_add' are not covered by the QMP schema by surprise. Ideally QEMU * developers will notify us before they switch so we have time to adapt our * generators first. This didn't work out when netdev-add was converted. * * We validate that the QMP schema describes only the expected types and nothing * else assuming that no new field will be added until final conversion. */ static int testQAPISchemaObjectDeviceAdd(const void *opaque) { virHashTablePtr schema = (virHashTablePtr) opaque; virJSONValuePtr entry; if (virQEMUQAPISchemaPathGet("device_add/arg-type", schema, &entry) < 0) { fprintf(stderr, "schema for 'device_add' not found\n"); return -1; } if (testQEMUSchemaEntryMatchTemplate(entry, "str:driver", "str:bus", "str:id", NULL) < 0) { VIR_TEST_VERBOSE("device_add has unexpected members in schema"); return -1; } if (virQEMUQAPISchemaPathGet("object-add/arg-type", schema, &entry) < 0) { fprintf(stderr, "schema for 'objectadd' not found\n"); return -1; } if (testQEMUSchemaEntryMatchTemplate(entry, "str:qom-type", "str:id", "any:props", NULL) < 0) { VIR_TEST_VERBOSE("object-add has unexpected members in schema"); return -1; } return 0; } static void testQueryJobsPrintJob(virBufferPtr buf, qemuMonitorJobInfoPtr job) { virBufferAddLit(buf, "[job]\n"); virBufferAsprintf(buf, "id=%s\n", NULLSTR(job->id)); virBufferAsprintf(buf, "type=%s\n", NULLSTR(qemuMonitorJobTypeToString(job->type))); virBufferAsprintf(buf, "status=%s\n", NULLSTR(qemuMonitorJobStatusTypeToString(job->status))); virBufferAsprintf(buf, "error=%s\n", NULLSTR(job->error)); virBufferAddLit(buf, "\n"); } struct testQueryJobsData { const char *name; virDomainXMLOptionPtr xmlopt; }; static int testQueryJobs(const void *opaque) { const struct testQueryJobsData *data = opaque; qemuMonitorTestPtr test = qemuMonitorTestNewSimple(data->xmlopt); g_autofree char *filenameJSON = NULL; g_autofree char *fileJSON = NULL; g_autofree char *filenameResult = NULL; g_autofree char *actual = NULL; qemuMonitorJobInfoPtr *jobs = NULL; g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; size_t njobs = 0; size_t i; int ret = -1; if (!test) return -1; filenameJSON = g_strdup_printf(abs_srcdir "/qemumonitorjsondata/query-jobs-%s.json", data->name); filenameResult = g_strdup_printf(abs_srcdir "/qemumonitorjsondata/query-jobs-%s.result", data->name); if (virTestLoadFile(filenameJSON, &fileJSON) < 0) goto cleanup; if (qemuMonitorTestAddItem(test, "query-jobs", fileJSON) < 0) goto cleanup; if (qemuMonitorJSONGetJobInfo(qemuMonitorTestGetMonitor(test), &jobs, &njobs) < 0) goto cleanup; for (i = 0; i < njobs; i++) testQueryJobsPrintJob(&buf, jobs[i]); virBufferTrim(&buf, "\n"); actual = virBufferContentAndReset(&buf); if (virTestCompareToFile(actual, filenameResult) < 0) goto cleanup; ret = 0; cleanup: for (i = 0; i < njobs; i++) qemuMonitorJobInfoFree(jobs[i]); VIR_FREE(jobs); qemuMonitorTestFree(test); return ret; } static int testQemuMonitorJSONTransaction(const void *opaque) { const testGenericData *data = opaque; g_autoptr(qemuMonitorTest) test = NULL; g_autoptr(virJSONValue) actions = NULL; g_autoptr(virJSONValue) mergebitmaps = NULL; if (!(test = qemuMonitorTestNewSchema(data->xmlopt, data->schema))) return -1; actions = virJSONValueNewArray(); mergebitmaps = virJSONValueNewArray(); if (qemuMonitorTransactionBitmapMergeSourceAddBitmap(mergebitmaps, "node1", "bitmap1") < 0 || qemuMonitorTransactionBitmapMergeSourceAddBitmap(mergebitmaps, "node2", "bitmap2") < 0) return -1; if (qemuMonitorTransactionBitmapAdd(actions, "node1", "bitmap1", true, true, 1234) < 0 || qemuMonitorTransactionBitmapRemove(actions, "node2", "bitmap2") < 0 || qemuMonitorTransactionBitmapEnable(actions, "node3", "bitmap3") < 0 || qemuMonitorTransactionBitmapDisable(actions, "node4", "bitmap4") < 0 || qemuMonitorTransactionBitmapMerge(actions, "node5", "bitmap5", &mergebitmaps) < 0 || qemuMonitorTransactionSnapshotLegacy(actions, "dev6", "path", "qcow2", true) < 0 || qemuMonitorTransactionSnapshotBlockdev(actions, "node7", "overlay7") < 0 || qemuMonitorTransactionBackup(actions, "dev8", "job8", "target8", "bitmap8", QEMU_MONITOR_TRANSACTION_BACKUP_SYNC_MODE_NONE) < 0 || qemuMonitorTransactionBackup(actions, "dev9", "job9", "target9", "bitmap9", QEMU_MONITOR_TRANSACTION_BACKUP_SYNC_MODE_INCREMENTAL) < 0 || qemuMonitorTransactionBackup(actions, "devA", "jobA", "targetA", "bitmapA", QEMU_MONITOR_TRANSACTION_BACKUP_SYNC_MODE_FULL) < 0) return -1; if (qemuMonitorTestAddItem(test, "transaction", "{\"return\":{}}") < 0) return -1; if (qemuMonitorJSONTransaction(qemuMonitorTestGetMonitor(test), &actions) < 0) return -1; return 0; } static int testQemuMonitorJSONqemuMonitorJSONGetCPUModelComparison(const void *opaque) { const testGenericData *data = opaque; g_autoptr(qemuMonitorTest) test = NULL; g_autoptr(virCPUDef) cpu_a = virCPUDefNew(); g_autoptr(virCPUDef) cpu_b = virCPUDefNew(); g_autofree char *result = NULL; if (!(test = qemuMonitorTestNewSchema(data->xmlopt, data->schema))) return -1; if (qemuMonitorTestAddItem(test, "query-cpu-model-comparison", "{\"return\":{\"result\":\"test\"}}") < 0) return -1; cpu_a->model = g_strdup("cpu_a"); cpu_b->model = g_strdup("cpu_b"); if (qemuMonitorJSONGetCPUModelComparison(qemuMonitorTestGetMonitor(test), cpu_a, cpu_b, &result) < 0) return -1; if (!result || STRNEQ(result, "test")) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "Compare result not set"); return -1; } return 0; } static int testQemuMonitorJSONqemuMonitorJSONGetCPUModelBaseline(const void *opaque) { const testGenericData *data = opaque; g_autoptr(qemuMonitorTest) test = NULL; g_autoptr(virCPUDef) cpu_a = virCPUDefNew(); g_autoptr(virCPUDef) cpu_b = virCPUDefNew(); qemuMonitorCPUModelInfoPtr baseline = NULL; int ret = -1; if (!(test = qemuMonitorTestNewSchema(data->xmlopt, data->schema))) return -1; if (qemuMonitorTestAddItem(test, "query-cpu-model-baseline", "{ " " \"return\": { " " \"model\": { " " \"name\": \"cpu_c\", " " \"props\": { " " \"feat_a\": true, " " \"feat_b\": false " " } " " } " " } " "}") < 0) return -1; cpu_a->model = g_strdup("cpu_a"); cpu_b->model = g_strdup("cpu_b"); if (virCPUDefAddFeature(cpu_a, "feat_a", VIR_CPU_FEATURE_REQUIRE) < 0 || virCPUDefAddFeature(cpu_a, "feat_b", VIR_CPU_FEATURE_REQUIRE) < 0 || virCPUDefAddFeature(cpu_a, "feat_c", VIR_CPU_FEATURE_REQUIRE) < 0) goto cleanup; if (qemuMonitorJSONGetCPUModelBaseline(qemuMonitorTestGetMonitor(test), cpu_a, cpu_b, &baseline) < 0) goto cleanup; if (!baseline) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "Baseline missing result"); goto cleanup; } if (!baseline->name) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "Baseline missing model name"); goto cleanup; } if (baseline->nprops != 2) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "Baseline missing properties"); goto cleanup; } if (STRNEQ(baseline->props[0].name, "feat_a") || !baseline->props[0].value.boolean || STRNEQ(baseline->props[1].name, "feat_b") || baseline->props[1].value.boolean) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "Baseline property error"); goto cleanup; } ret = 0; cleanup: qemuMonitorCPUModelInfoFree(baseline); return ret; } static int mymain(void) { int ret = 0; virQEMUDriver driver; testQemuMonitorJSONSimpleFuncData simpleFunc; struct testQAPISchemaData qapiData; virJSONValuePtr metaschema = NULL; char *metaschemastr = NULL; if (qemuTestDriverInit(&driver) < 0) return EXIT_FAILURE; virEventRegisterDefaultImpl(); if (!(qapiData.schema = testQEMUSchemaLoadLatest("x86_64"))) { VIR_TEST_VERBOSE("failed to load qapi schema"); ret = -1; goto cleanup; } #define DO_TEST(name) \ do { \ testGenericData data = { driver.xmlopt, qapiData.schema }; \ if (virTestRun(# name, testQemuMonitorJSON ## name, &data) < 0) \ ret = -1; \ } while (0) #define DO_TEST_SIMPLE(CMD, FNC, ...) \ simpleFunc = (testQemuMonitorJSONSimpleFuncData) {.cmd = CMD, .func = FNC, \ .xmlopt = driver.xmlopt, \ .schema = qapiData.schema, \ __VA_ARGS__ }; \ if (virTestRun(# FNC, testQemuMonitorJSONSimpleFunc, &simpleFunc) < 0) \ ret = -1 #define DO_TEST_GEN_FULL(name, dpr, rmvd, ...) \ simpleFunc = (testQemuMonitorJSONSimpleFuncData) {.xmlopt = driver.xmlopt, \ .allowDeprecated = dpr, \ .allowRemoved = rmvd, \ .schema = qapiData.schema \ __VA_ARGS__ }; \ if (virTestRun(# name, testQemuMonitorJSON ## name, &simpleFunc) < 0) \ ret = -1 #define DO_TEST_GEN(name, ...) DO_TEST_GEN_FULL(name, false, false, __VA_ARGS__) #define DO_TEST_GEN_DEPRECATED(name, removed, ...) \ DO_TEST_GEN_FULL(name, true, removed, __VA_ARGS__) #define DO_TEST_CPU_DATA(name) \ do { \ struct testCPUData data = { name, driver.xmlopt, qapiData.schema }; \ const char *label = "GetCPUData(" name ")"; \ if (virTestRun(label, testQemuMonitorJSONGetCPUData, &data) < 0) \ ret = -1; \ } while (0) #define DO_TEST_CPU_INFO(name, maxvcpus) \ do { \ struct testCPUInfoData data = {name, maxvcpus, driver.xmlopt, false, \ qapiData.schema}; \ if (virTestRun("GetCPUInfo(" name ")", testQemuMonitorCPUInfo, \ &data) < 0) \ ret = -1; \ } while (0) #define DO_TEST_CPU_INFO_FAST(name, maxvcpus) \ do { \ struct testCPUInfoData data = {name, maxvcpus, driver.xmlopt, true, \ qapiData.schema }; \ if (virTestRun("GetCPUInfo(" name ")", testQemuMonitorCPUInfo, \ &data) < 0) \ ret = -1; \ } while (0) DO_TEST(GetStatus); DO_TEST(GetVersion); DO_TEST(GetMachines); DO_TEST(GetCPUDefinitions); DO_TEST(GetCommands); DO_TEST(GetTPMModels); DO_TEST(GetCommandLineOptionParameters); if (qemuMonitorJSONTestAttachChardev(driver.xmlopt, qapiData.schema) < 0) ret = -1; DO_TEST(DetachChardev); DO_TEST(GetListPaths); DO_TEST(GetObjectProperty); DO_TEST(SetObjectProperty); DO_TEST(GetDeviceAliases); DO_TEST(CPU); DO_TEST(GetNonExistingCPUData); DO_TEST(GetIOThreads); DO_TEST(Transaction); DO_TEST_SIMPLE("qmp_capabilities", qemuMonitorJSONSetCapabilities); DO_TEST_SIMPLE("system_powerdown", qemuMonitorJSONSystemPowerdown); DO_TEST_SIMPLE("system_reset", qemuMonitorJSONSystemReset); DO_TEST_SIMPLE("migrate_cancel", qemuMonitorJSONMigrateCancel); DO_TEST_SIMPLE("inject-nmi", qemuMonitorJSONInjectNMI); DO_TEST_SIMPLE("system_wakeup", qemuMonitorJSONSystemWakeup); DO_TEST_SIMPLE("nbd-server-stop", qemuMonitorJSONNBDServerStop); DO_TEST_SIMPLE("rtc-reset-reinjection", qemuMonitorJSONRTCResetReinjection); DO_TEST_GEN(qemuMonitorJSONSetLink); DO_TEST_GEN(qemuMonitorJSONBlockResize); DO_TEST_GEN(qemuMonitorJSONSetPassword); DO_TEST_GEN(qemuMonitorJSONExpirePassword); DO_TEST_GEN(qemuMonitorJSONSetBalloon); DO_TEST_GEN_DEPRECATED(qemuMonitorJSONSetCPU, true); DO_TEST_GEN(qemuMonitorJSONEjectMedia); DO_TEST_GEN_DEPRECATED(qemuMonitorJSONChangeMedia, true); DO_TEST_GEN(qemuMonitorJSONSaveVirtualMemory); DO_TEST_GEN(qemuMonitorJSONSavePhysicalMemory); DO_TEST_GEN_DEPRECATED(qemuMonitorJSONSetMigrationSpeed, true); DO_TEST_GEN_DEPRECATED(qemuMonitorJSONSetMigrationDowntime, true); DO_TEST_GEN(qemuMonitorJSONMigrate); DO_TEST_GEN(qemuMonitorJSONDump); DO_TEST_GEN(qemuMonitorJSONGraphicsRelocate); DO_TEST_GEN(qemuMonitorJSONRemoveNetdev); DO_TEST_GEN(qemuMonitorJSONDelDevice); DO_TEST_GEN(qemuMonitorJSONAddDevice); DO_TEST_GEN(qemuMonitorJSONDriveMirror); DO_TEST_GEN(qemuMonitorJSONBlockdevMirror); DO_TEST_GEN(qemuMonitorJSONBlockStream); DO_TEST_GEN(qemuMonitorJSONBlockCommit); DO_TEST_GEN(qemuMonitorJSONDrivePivot); DO_TEST_GEN(qemuMonitorJSONScreendump); DO_TEST_GEN(qemuMonitorJSONOpenGraphics); DO_TEST_GEN(qemuMonitorJSONNBDServerAdd); DO_TEST_GEN(qemuMonitorJSONDetachCharDev); DO_TEST_GEN(qemuMonitorJSONBlockdevTrayOpen); DO_TEST_GEN(qemuMonitorJSONBlockdevTrayClose); DO_TEST_GEN(qemuMonitorJSONBlockdevMediumRemove); DO_TEST_GEN(qemuMonitorJSONBlockdevMediumInsert); DO_TEST_GEN(qemuMonitorJSONJobDismiss); DO_TEST_GEN(qemuMonitorJSONJobCancel); DO_TEST_GEN(qemuMonitorJSONJobComplete); DO_TEST(qemuMonitorJSONGetBalloonInfo); DO_TEST(qemuMonitorJSONGetBlockInfo); DO_TEST(qemuMonitorJSONGetAllBlockStatsInfo); DO_TEST(qemuMonitorJSONGetMigrationCacheSize); DO_TEST(qemuMonitorJSONGetMigrationStats); DO_TEST(qemuMonitorJSONGetChardevInfo); DO_TEST(qemuMonitorJSONSetBlockIoThrottle); DO_TEST(qemuMonitorJSONGetTargetArch); DO_TEST(qemuMonitorJSONGetMigrationCapabilities); DO_TEST(qemuMonitorJSONQueryCPUs); DO_TEST(qemuMonitorJSONQueryCPUsFast); DO_TEST(qemuMonitorJSONGetVirtType); DO_TEST(qemuMonitorJSONSendKey); DO_TEST(qemuMonitorJSONGetDumpGuestMemoryCapability); DO_TEST(qemuMonitorJSONSendKeyHoldtime); DO_TEST(qemuMonitorSupportsActiveCommit); DO_TEST(qemuMonitorJSONNBDServerStart); DO_TEST_CPU_DATA("host"); DO_TEST_CPU_DATA("full"); DO_TEST_CPU_DATA("ecx"); DO_TEST_CPU_INFO("x86-basic-pluggable", 8); DO_TEST_CPU_INFO("x86-full", 11); DO_TEST_CPU_INFO("x86-node-full", 8); DO_TEST_CPU_INFO_FAST("x86-full-fast", 11); DO_TEST_CPU_INFO_FAST("x86-dies", 16); DO_TEST_CPU_INFO("ppc64-basic", 24); DO_TEST_CPU_INFO("ppc64-hotplug-1", 24); DO_TEST_CPU_INFO("ppc64-hotplug-2", 24); DO_TEST_CPU_INFO("ppc64-hotplug-4", 24); DO_TEST_CPU_INFO("ppc64-no-threads", 16); DO_TEST_CPU_INFO_FAST("s390-fast", 2); #define DO_TEST_BLOCK_NODE_DETECT(testname) \ do { \ if (virTestRun("node-name-detect(" testname ")", \ testBlockNodeNameDetect, testname) < 0) \ ret = -1; \ } while (0) DO_TEST_BLOCK_NODE_DETECT("basic"); DO_TEST_BLOCK_NODE_DETECT("same-backing"); DO_TEST_BLOCK_NODE_DETECT("relative"); DO_TEST_BLOCK_NODE_DETECT("gluster"); DO_TEST_BLOCK_NODE_DETECT("blockjob"); DO_TEST_BLOCK_NODE_DETECT("luks"); DO_TEST_BLOCK_NODE_DETECT("iscsi"); DO_TEST_BLOCK_NODE_DETECT("old"); DO_TEST_BLOCK_NODE_DETECT("empty"); #undef DO_TEST_BLOCK_NODE_DETECT #define DO_TEST_QAPI_QUERY(nme, qry, scc, rplobj) \ do { \ qapiData.name = nme; \ qapiData.query = qry; \ qapiData.rc = scc; \ qapiData.replyobj = rplobj; \ if (virTestRun("qapi schema query " nme, testQAPISchemaQuery, &qapiData) < 0)\ ret = -1; \ } while (0) DO_TEST_QAPI_QUERY("command", "blockdev-add", 1, true); DO_TEST_QAPI_QUERY("event", "RTC_CHANGE", 1, true); DO_TEST_QAPI_QUERY("object property", "screendump/arg-type/device", 1, true); DO_TEST_QAPI_QUERY("optional property", "block-commit/arg-type/*top", 1, true); DO_TEST_QAPI_QUERY("variant", "blockdev-add/arg-type/+file", 1, true); DO_TEST_QAPI_QUERY("variant property", "blockdev-add/arg-type/+file/filename", 1, true); DO_TEST_QAPI_QUERY("enum value", "query-status/ret-type/status/^debug", 1, false); DO_TEST_QAPI_QUERY("builtin type", "query-qmp-schema/ret-type/name/!string", 1, false); DO_TEST_QAPI_QUERY("alternate variant 1", "blockdev-add/arg-type/+qcow2/backing/!null", 1, false); DO_TEST_QAPI_QUERY("alternate variant 2", "blockdev-add/arg-type/+qcow2/backing/!string", 1, false); DO_TEST_QAPI_QUERY("alternate variant 3", "blockdev-add/arg-type/+qcow2/backing/+file/filename", 1, true); DO_TEST_QAPI_QUERY("nonexistent command", "nonexistent", 0, false); DO_TEST_QAPI_QUERY("nonexistent attr", "screendump/arg-type/nonexistent", 0, false); DO_TEST_QAPI_QUERY("nonexistent variant", "blockdev-add/arg-type/+nonexistent", 0, false); DO_TEST_QAPI_QUERY("nonexistent enum value", "query-status/ret-type/status/^nonexistentdebug", 0, false); DO_TEST_QAPI_QUERY("broken query for enum value", "query-status/ret-type/status/^debug/test", -1, false); DO_TEST_QAPI_QUERY("builtin type", "query-qmp-schema/ret-type/name/!number", 0, false); #undef DO_TEST_QAPI_QUERY #define DO_TEST_QAPI_VALIDATE(nme, rootquery, scc, jsonstr) \ do { \ qapiData.name = nme; \ qapiData.query = rootquery; \ qapiData.success = scc; \ qapiData.json = jsonstr; \ if (virTestRun("qapi schema validate " nme, testQAPISchemaValidate, &qapiData) < 0)\ ret = -1; \ } while (0) DO_TEST_QAPI_VALIDATE("string", "trace-event-get-state/arg-type", true, "{\"name\":\"test\"}"); DO_TEST_QAPI_VALIDATE("all attrs", "trace-event-get-state/arg-type", true, "{\"name\":\"test\", \"vcpu\":123}"); DO_TEST_QAPI_VALIDATE("attr type mismatch", "trace-event-get-state/arg-type", false, "{\"name\":123}"); DO_TEST_QAPI_VALIDATE("missing mandatory attr", "trace-event-get-state/arg-type", false, "{\"vcpu\":123}"); DO_TEST_QAPI_VALIDATE("attr name not present", "trace-event-get-state/arg-type", false, "{\"name\":\"test\", \"blah\":123}"); DO_TEST_QAPI_VALIDATE("variant", "blockdev-add/arg-type", true, "{\"driver\":\"file\", \"filename\":\"ble\"}"); DO_TEST_QAPI_VALIDATE("variant wrong", "blockdev-add/arg-type", false, "{\"driver\":\"filefilefilefile\", \"filename\":\"ble\"}"); DO_TEST_QAPI_VALIDATE("variant missing mandatory", "blockdev-add/arg-type", false, "{\"driver\":\"file\", \"pr-manager\":\"ble\"}"); DO_TEST_QAPI_VALIDATE("variant missing discriminator", "blockdev-add/arg-type", false, "{\"node-name\":\"dfgfdg\"}"); DO_TEST_QAPI_VALIDATE("alternate 1", "blockdev-add/arg-type", true, "{\"driver\":\"qcow2\"," "\"file\": { \"driver\":\"file\", \"filename\":\"ble\"}}"); DO_TEST_QAPI_VALIDATE("alternate 2", "blockdev-add/arg-type", true, "{\"driver\":\"qcow2\",\"file\": \"somepath\"}"); DO_TEST_QAPI_VALIDATE("alternate 2", "blockdev-add/arg-type", false, "{\"driver\":\"qcow2\",\"file\": 1234}"); if (!(metaschema = testQEMUSchemaGetLatest("x86_64")) || !(metaschemastr = virJSONValueToString(metaschema, false))) { VIR_TEST_VERBOSE("failed to load latest qapi schema"); ret = -1; goto cleanup; } DO_TEST_QAPI_VALIDATE("schema-meta", "query-qmp-schema/ret-type", true, metaschemastr); #undef DO_TEST_QAPI_VALIDATE if (virTestRun("validate that object-add and device_add don't have schema", testQAPISchemaObjectDeviceAdd, qapiData.schema) < 0) ret = -1; #define DO_TEST_QUERY_JOBS(name) \ do { \ struct testQueryJobsData data = { name, driver.xmlopt}; \ if (virTestRun("query-jobs-" name, testQueryJobs, &data) < 0) \ ret = -1; \ } while (0) DO_TEST_QUERY_JOBS("empty"); DO_TEST_QUERY_JOBS("create"); #undef DO_TEST_QUERY_JOBS virHashFree(qapiData.schema); if (!(qapiData.schema = testQEMUSchemaLoadLatest("s390x"))) { VIR_TEST_VERBOSE("failed to load qapi schema for s390x"); ret = -1; goto cleanup; } DO_TEST(qemuMonitorJSONGetCPUModelComparison); DO_TEST(qemuMonitorJSONGetCPUModelBaseline); cleanup: VIR_FREE(metaschemastr); virJSONValueFree(metaschema); virHashFree(qapiData.schema); qemuTestDriverFree(&driver); return (ret == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } VIR_TEST_MAIN_PRELOAD(mymain, VIR_TEST_MOCK("virdeterministichash"))