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(a)redhat.com>
---
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