This is already possible with qemu, and actually already happening with q35 machines and a specified watchdog since q35 already includes a watchdog we do not include in the XML. In order to express such posibility multiple watchdogs need to be supported. Signed-off-by: Martin Kletzander <mkletzan@xxxxxxxxxx> --- src/conf/domain_conf.c | 82 +++++++++++++------ src/conf/domain_conf.h | 8 +- src/conf/schemas/domaincommon.rng | 4 +- src/libvirt_private.syms | 1 + src/qemu/qemu_alias.c | 26 ++++-- src/qemu/qemu_alias.h | 5 +- src/qemu/qemu_command.c | 24 ++++-- src/qemu/qemu_domain_address.c | 8 +- src/qemu/qemu_driver.c | 14 ++-- src/qemu/qemu_hotplug.c | 61 ++++++-------- src/qemu/qemu_process.c | 3 +- src/qemu/qemu_validate.c | 25 ++++++ .../watchdog-q35-multiple.x86_64-latest.args | 39 +++++++++ .../watchdog-q35-multiple.xml | 25 ++++++ tests/qemuxml2argvtest.c | 1 + .../watchdog-q35-multiple.x86_64-latest.xml | 50 +++++++++++ tests/qemuxml2xmltest.c | 1 + 17 files changed, 287 insertions(+), 90 deletions(-) create mode 100644 tests/qemuxml2argvdata/watchdog-q35-multiple.x86_64-latest.args create mode 100644 tests/qemuxml2argvdata/watchdog-q35-multiple.xml create mode 100644 tests/qemuxml2xmloutdata/watchdog-q35-multiple.x86_64-latest.xml diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 733399e6da0d..7c61da1d765b 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3875,7 +3875,9 @@ void virDomainDefFree(virDomainDef *def) def->blkio.ndevices); g_free(def->blkio.devices); - virDomainWatchdogDefFree(def->watchdog); + for (i = 0; i < def->nwatchdogs; i++) + virDomainWatchdogDefFree(def->watchdogs[i]); + g_free(def->watchdogs); virDomainMemballoonDefFree(def->memballoon); virDomainNVRAMDefFree(def->nvram); @@ -4647,10 +4649,10 @@ virDomainDeviceInfoIterateFlags(virDomainDef *def, if ((rc = cb(def, &device, &def->fss[i]->info, opaque)) != 0) return rc; } - if (def->watchdog) { - device.type = VIR_DOMAIN_DEVICE_WATCHDOG; - device.data.watchdog = def->watchdog; - if ((rc = cb(def, &device, &def->watchdog->info, opaque)) != 0) + device.type = VIR_DOMAIN_DEVICE_WATCHDOG; + for (i = 0; i < def->nwatchdogs; i++) { + device.data.watchdog = def->watchdogs[i]; + if ((rc = cb(def, &device, &def->watchdogs[i]->info, opaque)) != 0) return rc; } if (def->memballoon) { @@ -18809,24 +18811,21 @@ virDomainDefParseXML(xmlXPathContextPtr ctxt, VIR_FREE(nodes); /* analysis of the watchdog devices */ - def->watchdog = NULL; - if ((n = virXPathNodeSet("./devices/watchdog", ctxt, &nodes)) < 0) + n = virXPathNodeSet("./devices/watchdog", ctxt, &nodes); + if (n < 0) return NULL; - if (n > 1) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("only a single watchdog device is supported")); - return NULL; - } - if (n > 0) { + if (n) + def->watchdogs = g_new0(virDomainWatchdogDef *, n); + for (i = 0; i < n; i++) { virDomainWatchdogDef *watchdog; - watchdog = virDomainWatchdogDefParseXML(xmlopt, nodes[0], ctxt, flags); + watchdog = virDomainWatchdogDefParseXML(xmlopt, nodes[i], ctxt, flags); if (!watchdog) return NULL; - def->watchdog = watchdog; - VIR_FREE(nodes); + def->watchdogs[def->nwatchdogs++] = watchdog; } + VIR_FREE(nodes); /* analysis of the memballoon devices */ def->memballoon = NULL; @@ -21255,18 +21254,19 @@ virDomainDefCheckABIStabilityFlags(virDomainDef *src, dst->redirfilter)) goto error; - if ((!src->watchdog && dst->watchdog) || - (src->watchdog && !dst->watchdog)) { + + if (src->nwatchdogs != dst->nwatchdogs) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Target domain watchdog count %d " - "does not match source %d"), - dst->watchdog ? 1 : 0, src->watchdog ? 1 : 0); + _("Target domain watchdog device count %zu " + "does not match source %zu"), + dst->nwatchdogs, src->nwatchdogs); goto error; } - if (src->watchdog && - !virDomainWatchdogDefCheckABIStability(src->watchdog, dst->watchdog)) - goto error; + for (i = 0; i < src->nwatchdogs; i++) { + if (!virDomainWatchdogDefCheckABIStability(src->watchdogs[i], dst->watchdogs[i])) + goto error; + } if ((!src->memballoon && dst->memballoon) || (src->memballoon && !dst->memballoon)) { @@ -27517,8 +27517,8 @@ virDomainDefFormatInternalSetRootName(virDomainDef *def, return -1; } - if (def->watchdog) - virDomainWatchdogDefFormat(buf, def->watchdog, flags); + for (n = 0; n < def->nwatchdogs; n++) + virDomainWatchdogDefFormat(buf, def->watchdogs[n], flags); if (def->memballoon) virDomainMemballoonDefFormat(buf, def->memballoon, flags); @@ -30578,3 +30578,33 @@ virDomainDefHasSpiceGraphics(const virDomainDef *def) return false; } + + +ssize_t +virDomainWatchdogDefFind(const virDomainDef *def, + const virDomainWatchdogDef *watchdog) +{ + size_t i; + + for (i = 0; i < def->nwatchdogs; i++) { + const virDomainWatchdogDef *tmp = def->watchdogs[i]; + + if (tmp->model != watchdog->model) + continue; + + if (tmp->action != watchdog->action) + continue; + + if (watchdog->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && + !virDomainDeviceInfoAddressIsEqual(&watchdog->info, &tmp->info)) + continue; + + if (watchdog->info.alias && + STRNEQ_NULLABLE(watchdog->info.alias, tmp->info.alias)) + continue; + + return i; + } + + return -1; +} diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 3e4985a67da2..da785076151d 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -3062,12 +3062,14 @@ struct _virDomainDef { size_t nsysinfo; virSysinfoDef **sysinfo; + size_t nwatchdogs; + virDomainWatchdogDef **watchdogs; + /* At maximum 2 TPMs on the domain if a TPM Proxy is present. */ size_t ntpms; virDomainTPMDef **tpms; /* Only 1 */ - virDomainWatchdogDef *watchdog; virDomainMemballoonDef *memballoon; virDomainNVRAMDef *nvram; virCPUDef *cpu; @@ -3526,6 +3528,10 @@ virDomainSoundDef *virDomainSoundDefRemove(virDomainDef *def, size_t idx); void virDomainAudioDefFree(virDomainAudioDef *def); void virDomainMemballoonDefFree(virDomainMemballoonDef *def); void virDomainNVRAMDefFree(virDomainNVRAMDef *def); +ssize_t +virDomainWatchdogDefFind(const virDomainDef *def, + const virDomainWatchdogDef *watchdog) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT; void virDomainWatchdogDefFree(virDomainWatchdogDef *def); virDomainVideoDef *virDomainVideoDefNew(virDomainXMLOption *xmlopt); void virDomainVideoDefFree(virDomainVideoDef *def); diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index 6cb0a20e1e10..a5d0505d9b9b 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -6428,9 +6428,9 @@ <ref name="memorydev"/> </choice> </zeroOrMore> - <optional> + <zeroOrMore> <ref name="watchdog"/> - </optional> + </zeroOrMore> <optional> <ref name="memballoon"/> </optional> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 576ec8f95f17..2c5489b2b86e 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -692,6 +692,7 @@ virDomainVsockDefFree; virDomainVsockDefNew; virDomainWatchdogActionTypeFromString; virDomainWatchdogActionTypeToString; +virDomainWatchdogDefFind; virDomainWatchdogDefFree; virDomainWatchdogModelTypeFromString; virDomainWatchdogModelTypeToString; diff --git a/src/qemu/qemu_alias.c b/src/qemu/qemu_alias.c index ef8e87ab58a2..809a6f03ed46 100644 --- a/src/qemu/qemu_alias.c +++ b/src/qemu/qemu_alias.c @@ -560,12 +560,26 @@ qemuAssignDeviceShmemAlias(virDomainDef *def, void -qemuAssignDeviceWatchdogAlias(virDomainWatchdogDef *watchdog) +qemuAssignDeviceWatchdogAlias(virDomainDef *def, + virDomainWatchdogDef *watchdog, + int idx) { - /* Currently, there's just one watchdog per domain */ + ssize_t i = 0; + + if (watchdog->info.alias) + return; + + if (idx == -1) { + for (i = 0; i < def->nwatchdogs; i++) { + idx = MAX(idx, + qemuDomainDeviceAliasIndex(&def->watchdogs[i]->info, + "watchdog")); + } + + idx++; + } - if (!watchdog->info.alias) - watchdog->info.alias = g_strdup("watchdog0"); + watchdog->info.alias = g_strdup_printf("watchdog%d", idx); } @@ -671,8 +685,8 @@ qemuAssignDeviceAliases(virDomainDef *def) for (i = 0; i < def->nsmartcards; i++) { qemuAssignDeviceSmartcardAlias(def->smartcards[i], i); } - if (def->watchdog) { - qemuAssignDeviceWatchdogAlias(def->watchdog); + for (i = 0; i < def->nwatchdogs; i++) { + qemuAssignDeviceWatchdogAlias(def, def->watchdogs[i], i); } if (def->memballoon && def->memballoon->model != VIR_DOMAIN_MEMBALLOON_MODEL_NONE) { diff --git a/src/qemu/qemu_alias.h b/src/qemu/qemu_alias.h index 6433ae4cecaf..d7f34489f570 100644 --- a/src/qemu/qemu_alias.h +++ b/src/qemu/qemu_alias.h @@ -62,7 +62,10 @@ void qemuAssignDeviceShmemAlias(virDomainDef *def, virDomainShmemDef *shmem, int idx); -void qemuAssignDeviceWatchdogAlias(virDomainWatchdogDef *watchdog); +void +qemuAssignDeviceWatchdogAlias(virDomainDef *def, + virDomainWatchdogDef *watchdog, + int idx); void qemuAssignDeviceInputAlias(virDomainDef *def, virDomainInputDef *input, diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index b96f2d33c158..6e28f8b15e1c 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -4044,22 +4044,28 @@ qemuBuildWatchdogCommandLine(virCommand *cmd, const virDomainDef *def, virQEMUCaps *qemuCaps) { - virDomainWatchdogDef *watchdog = def->watchdog; - g_autoptr(virJSONValue) props = NULL; + virDomainWatchdogDef *watchdog = NULL; const char *action; int actualAction; + ssize_t i = 0; - if (!def->watchdog) + if (def->nwatchdogs == 0) return 0; - if (qemuCommandAddExtDevice(cmd, &def->watchdog->info, def, qemuCaps) < 0) - return -1; + for (i = 0; i < def->nwatchdogs; i++) { + g_autoptr(virJSONValue) props = NULL; - if (!(props = qemuBuildWatchdogDevProps(def, watchdog))) - return -1; + watchdog = def->watchdogs[i]; - if (qemuBuildDeviceCommandlineFromJSON(cmd, props, def, qemuCaps)) - return -1; + if (qemuCommandAddExtDevice(cmd, &watchdog->info, def, qemuCaps) < 0) + return -1; + + if (!(props = qemuBuildWatchdogDevProps(def, watchdog))) + return -1; + + if (qemuBuildDeviceCommandlineFromJSON(cmd, props, def, qemuCaps)) + return -1; + } /* qemu doesn't have a 'dump' action; we tell qemu to 'pause', then libvirt listens for the watchdog event, and we perform the dump diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c index b8d1969fbebd..db4e91501d3f 100644 --- a/src/qemu/qemu_domain_address.c +++ b/src/qemu/qemu_domain_address.c @@ -2326,10 +2326,10 @@ qemuDomainAssignDevicePCISlots(virDomainDef *def, } /* A watchdog - check if it is a PCI device */ - if (def->watchdog && - def->watchdog->model == VIR_DOMAIN_WATCHDOG_MODEL_I6300ESB && - virDeviceInfoPCIAddressIsWanted(&def->watchdog->info)) { - if (qemuDomainPCIAddressReserveNextAddr(addrs, &def->watchdog->info) < 0) + for (i = 0; i < def->nwatchdogs; i++) { + if (def->watchdogs[i]->model == VIR_DOMAIN_WATCHDOG_MODEL_I6300ESB && + virDeviceInfoPCIAddressIsWanted(&def->watchdogs[i]->info) && + qemuDomainPCIAddressReserveNextAddr(addrs, &def->watchdogs[i]->info) < 0) return -1; } diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index d6879175fece..dce3bef5b85b 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7258,12 +7258,13 @@ qemuDomainAttachDeviceConfig(virDomainDef *vmdef, break; case VIR_DOMAIN_DEVICE_WATCHDOG: - if (vmdef->watchdog) { + if (virDomainWatchdogDefFind(vmdef, dev->data.watchdog) >= 0) { virReportError(VIR_ERR_OPERATION_INVALID, "%s", - _("domain already has a watchdog")); + _("device is already in the domain configuration")); return -1; } - vmdef->watchdog = g_steal_pointer(&dev->data.watchdog); + + VIR_APPEND_ELEMENT(vmdef->watchdogs, vmdef->nwatchdogs, dev->data.watchdog); break; case VIR_DOMAIN_DEVICE_INPUT: @@ -7457,12 +7458,13 @@ qemuDomainDetachDeviceConfig(virDomainDef *vmdef, case VIR_DOMAIN_DEVICE_WATCHDOG: - if (!vmdef->watchdog) { + idx = virDomainWatchdogDefFind(vmdef, dev->data.watchdog); + if (idx < 0) { virReportError(VIR_ERR_DEVICE_MISSING, "%s", - _("domain has no watchdog")); + _("no matching watchdog was found")); return -1; } - g_clear_pointer(&vmdef->watchdog, virDomainWatchdogDefFree); + VIR_DELETE_ELEMENT(vmdef->watchdogs, idx, vmdef->nwatchdogs); break; case VIR_DOMAIN_DEVICE_INPUT: diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 1fa3cc3ea9b1..b9832ea7815a 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -2928,15 +2928,9 @@ qemuDomainAttachWatchdog(virDomainObj *vm, virDomainDeviceDef dev = { VIR_DOMAIN_DEVICE_WATCHDOG, { .watchdog = watchdog } }; g_autoptr(virJSONValue) props = NULL; bool releaseAddress = false; - int rv; + int rv = 0; - if (vm->def->watchdog) { - virReportError(VIR_ERR_OPERATION_INVALID, "%s", - _("domain already has a watchdog")); - return -1; - } - - qemuAssignDeviceWatchdogAlias(watchdog); + qemuAssignDeviceWatchdogAlias(vm->def, watchdog, -1); if (watchdog->model != VIR_DOMAIN_WATCHDOG_MODEL_I6300ESB) { virReportError(VIR_ERR_OPERATION_UNSUPPORTED, @@ -2954,10 +2948,13 @@ qemuDomainAttachWatchdog(virDomainObj *vm, qemuDomainObjEnterMonitor(vm); - /* QEMU doesn't have a 'dump' action; we tell qemu to 'pause', then - libvirt listens for the watchdog event, and we perform the dump - ourselves. so convert 'dump' to 'pause' for the qemu cli */ - if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_SET_ACTION)) { + if (vm->def->nwatchdogs) { + /* Domain already has a watchdog and all must have the same action. */ + rv = 0; + } else if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_SET_ACTION)) { + /* QEMU doesn't have a 'dump' action; we tell qemu to 'pause', then + libvirt listens for the watchdog event, and we perform the dump + ourselves. so convert 'dump' to 'pause' for the qemu cli */ qemuMonitorActionWatchdog watchdogaction = QEMU_MONITOR_ACTION_WATCHDOG_KEEP; switch (watchdog->action) { @@ -3015,7 +3012,7 @@ qemuDomainAttachWatchdog(virDomainObj *vm, goto cleanup; releaseAddress = false; - vm->def->watchdog = watchdog; + VIR_APPEND_ELEMENT_COPY(vm->def->watchdogs, vm->def->nwatchdogs, watchdog); ret = 0; cleanup: @@ -4848,11 +4845,20 @@ static int qemuDomainRemoveWatchdog(virDomainObj *vm, virDomainWatchdogDef *watchdog) { + size_t i = 0; + VIR_DEBUG("Removing watchdog %s from domain %p %s", watchdog->info.alias, vm, vm->def->name); + for (i = 0; i < vm->def->nwatchdogs; i++) { + if (vm->def->watchdogs[i] == watchdog) + break; + } + qemuDomainReleaseDeviceAddress(vm, &watchdog->info); - g_clear_pointer(&vm->def->watchdog, virDomainWatchdogDefFree); + virDomainWatchdogDefFree(watchdog); + VIR_DELETE_ELEMENT(vm->def->watchdogs, i, vm->def->nwatchdogs); + return 0; } @@ -5603,33 +5609,20 @@ qemuDomainDetachPrepWatchdog(virDomainObj *vm, virDomainWatchdogDef *match, virDomainWatchdogDef **detach) { - virDomainWatchdogDef *watchdog; - - *detach = watchdog = vm->def->watchdog; + ssize_t idx = virDomainWatchdogDefFind(vm->def, match); - if (!watchdog) { - virReportError(VIR_ERR_DEVICE_MISSING, "%s", - _("watchdog device not present in domain configuration")); + if (idx < 0) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("no matching watchdog was found")); return -1; } - /* While domains can have up to one watchdog, the one supplied by the user - * doesn't necessarily match the one domain has. Refuse to detach in such - * case. */ - if (!(watchdog->model == match->model && - watchdog->action == match->action && - virDomainDeviceInfoAddressIsEqual(&match->info, &watchdog->info))) { - virReportError(VIR_ERR_DEVICE_MISSING, - _("model '%s' watchdog device not present " - "in domain configuration"), - virDomainWatchdogModelTypeToString(watchdog->model)); - return -1; - } + *detach = vm->def->watchdogs[idx]; - if (watchdog->model != VIR_DOMAIN_WATCHDOG_MODEL_I6300ESB) { + if ((*detach)->model != VIR_DOMAIN_WATCHDOG_MODEL_I6300ESB) { virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("hot unplug of watchdog of model %s is not supported"), - virDomainWatchdogModelTypeToString(watchdog->model)); + virDomainWatchdogModelTypeToString((*detach)->model)); return -1; } diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index ee9f0784d3a3..218231f3596c 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -804,7 +804,8 @@ qemuProcessHandleWatchdog(qemuMonitor *mon G_GNUC_UNUSED, qemuDomainSaveStatus(vm); } - if (vm->def->watchdog->action == VIR_DOMAIN_WATCHDOG_ACTION_DUMP) { + if (vm->def->nwatchdogs && + vm->def->watchdogs[0]->action == VIR_DOMAIN_WATCHDOG_ACTION_DUMP) { qemuProcessEventSubmit(vm, QEMU_PROCESS_EVENT_WATCHDOG, VIR_DOMAIN_WATCHDOG_ACTION_DUMP, 0, NULL); } diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index 6e04b22da43f..cb002dee0eb9 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -1188,6 +1188,28 @@ qemuValidateDomainDefTPMs(const virDomainDef *def) } +static int +qemuValidateDomainDefWatchdogs(const virDomainDef *def) +{ + ssize_t i = 0; + + for (i = 0; i < def->nwatchdogs; i++) { + /* We could theoretically support different watchdogs having dump and + * pause, but let's be honest, we support multiple watchdogs only + * because we need to be able to add a second, implicit one, not because + * it is a brilliant idea to have multiple watchdogs. */ + if (def->watchdogs[i]->action != def->watchdogs[0]->action) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("watchdogs with different actions are not supported " + "with this QEMU binary")); + return -1; + } + } + + return 0; +} + + int qemuValidateLifecycleAction(virDomainLifecycleAction onPoweroff, virDomainLifecycleAction onReboot, @@ -1388,6 +1410,9 @@ qemuValidateDomainDef(const virDomainDef *def, if (qemuValidateDomainDefTPMs(def) < 0) return -1; + if (qemuValidateDomainDefWatchdogs(def) < 0) + return -1; + if (def->sec) { switch ((virDomainLaunchSecurity) def->sec->sectype) { case VIR_DOMAIN_LAUNCH_SECURITY_SEV: diff --git a/tests/qemuxml2argvdata/watchdog-q35-multiple.x86_64-latest.args b/tests/qemuxml2argvdata/watchdog-q35-multiple.x86_64-latest.args new file mode 100644 index 000000000000..eccf5a2e119f --- /dev/null +++ b/tests/qemuxml2argvdata/watchdog-q35-multiple.x86_64-latest.args @@ -0,0 +1,39 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/tmp/lib/domain--1-QEMUGuest1 \ +USER=test \ +LOGNAME=test \ +XDG_DATA_HOME=/tmp/lib/domain--1-QEMUGuest1/.local/share \ +XDG_CACHE_HOME=/tmp/lib/domain--1-QEMUGuest1/.cache \ +XDG_CONFIG_HOME=/tmp/lib/domain--1-QEMUGuest1/.config \ +/usr/bin/qemu-system-x86_64 \ +-name guest=QEMUGuest1,debug-threads=on \ +-S \ +-object '{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/tmp/lib/domain--1-QEMUGuest1/master-key.aes"}' \ +-machine q35,usb=off,dump-guest-core=off,memory-backend=pc.ram \ +-accel tcg \ +-cpu qemu64 \ +-m 214 \ +-object '{"qom-type":"memory-backend-ram","id":"pc.ram","size":224395264}' \ +-overcommit mem-lock=off \ +-smp 1,sockets=1,cores=1,threads=1 \ +-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \ +-display none \ +-no-user-config \ +-nodefaults \ +-chardev socket,id=charmonitor,fd=1729,server=on,wait=off \ +-mon chardev=charmonitor,id=monitor,mode=control \ +-rtc base=utc \ +-no-shutdown \ +-no-acpi \ +-boot strict=on \ +-device '{"driver":"pcie-root-port","port":8,"chassis":1,"id":"pci.1","bus":"pcie.0","multifunction":true,"addr":"0x1"}' \ +-device '{"driver":"pcie-pci-bridge","id":"pci.2","bus":"pci.1","addr":"0x0"}' \ +-device '{"driver":"pcie-root-port","port":9,"chassis":3,"id":"pci.3","bus":"pcie.0","addr":"0x1.0x1"}' \ +-device '{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.2","addr":"0x1"}' \ +-audiodev '{"id":"audio1","driver":"none"}' \ +-device '{"driver":"ib700","id":"watchdog0"}' \ +-device '{"driver":"i6300esb","id":"watchdog1","bus":"pci.2","addr":"0x2"}' \ +-watchdog-action poweroff \ +-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \ +-msg timestamp=on diff --git a/tests/qemuxml2argvdata/watchdog-q35-multiple.xml b/tests/qemuxml2argvdata/watchdog-q35-multiple.xml new file mode 100644 index 000000000000..af0cb169f0d9 --- /dev/null +++ b/tests/qemuxml2argvdata/watchdog-q35-multiple.xml @@ -0,0 +1,25 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='x86_64' machine='q35'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-x86_64</emulator> + <controller type='pci' index='0' model='pcie-root'/> + <controller type='usb' index='0'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <watchdog model='ib700' action='poweroff'/> + <watchdog model='i6300esb' action='poweroff'/> + <memballoon model='none'/> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 8c52feb83c3c..9faa6f7791b1 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -1756,6 +1756,7 @@ mymain(void) DO_TEST_CAPS_LATEST("watchdog-device"); DO_TEST_CAPS_LATEST("watchdog-dump"); DO_TEST_CAPS_LATEST("watchdog-injectnmi"); + DO_TEST_CAPS_LATEST("watchdog-q35-multiple"); DO_TEST_CAPS_ARCH_LATEST("watchdog-diag288", "s390x"); DO_TEST_NOCAPS("balloon-device"); DO_TEST("balloon-device-deflate", diff --git a/tests/qemuxml2xmloutdata/watchdog-q35-multiple.x86_64-latest.xml b/tests/qemuxml2xmloutdata/watchdog-q35-multiple.x86_64-latest.xml new file mode 100644 index 000000000000..e507576085f7 --- /dev/null +++ b/tests/qemuxml2xmloutdata/watchdog-q35-multiple.x86_64-latest.xml @@ -0,0 +1,50 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='x86_64' machine='q35'>hvm</type> + <boot dev='hd'/> + </os> + <cpu mode='custom' match='exact' check='none'> + <model fallback='forbid'>qemu64</model> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-x86_64</emulator> + <controller type='pci' index='0' model='pcie-root'/> + <controller type='usb' index='0' model='piix3-uhci'> + <address type='pci' domain='0x0000' bus='0x02' slot='0x01' function='0x0'/> + </controller> + <controller type='sata' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/> + </controller> + <controller type='pci' index='1' model='pcie-root-port'> + <model name='pcie-root-port'/> + <target chassis='1' port='0x8'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0' multifunction='on'/> + </controller> + <controller type='pci' index='2' model='pcie-to-pci-bridge'> + <model name='pcie-pci-bridge'/> + <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/> + </controller> + <controller type='pci' index='3' model='pcie-root-port'> + <model name='pcie-root-port'/> + <target chassis='3' port='0x9'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> + </controller> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <audio id='1' type='none'/> + <watchdog model='ib700' action='poweroff'/> + <watchdog model='i6300esb' action='poweroff'> + <address type='pci' domain='0x0000' bus='0x02' slot='0x02' function='0x0'/> + </watchdog> + <memballoon model='none'/> + </devices> +</domain> diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 72f724bfcec7..59ec90467e71 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -489,6 +489,7 @@ mymain(void) QEMU_CAPS_HDA_DUPLEX, QEMU_CAPS_HDA_OUTPUT); DO_TEST_NOCAPS("watchdog"); + DO_TEST_CAPS_LATEST("watchdog-q35-multiple"); DO_TEST("net-bandwidth", QEMU_CAPS_DEVICE_VGA, QEMU_CAPS_VNC); DO_TEST("net-bandwidth2", QEMU_CAPS_DEVICE_VGA, QEMU_CAPS_VNC); DO_TEST_NOCAPS("net-mtu"); -- 2.39.1