>From ae4ec398d9cc229d4d2ea8cb537d30572d58a61b Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Wed, 21 Oct 2009 13:26:38 +0100 Subject: [PATCH] Support QEMU watchdog device. This adds simple support for configuring a guest with a QEMU/KVM virtual hardware watchdog device. --- docs/formatdomain.html.in | 75 +++++++++++ docs/schemas/domain.rng | 24 ++++ src/conf/domain_conf.c | 136 ++++++++++++++++++++- src/conf/domain_conf.h | 30 +++++ src/libvirt_private.syms | 4 + src/qemu/qemu_conf.c | 40 ++++++ tests/qemuargv2xmltest.c | 1 + tests/qemuxml2argvdata/qemuxml2argv-watchdog.args | 1 + tests/qemuxml2argvdata/qemuxml2argv-watchdog.xml | 23 ++++ 9 files changed, 333 insertions(+), 1 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-watchdog.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-watchdog.xml diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 829adef..53cd960 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -1028,6 +1028,81 @@ qemu-kvm -net nic,model=? /dev/null +

Watchdog device

+ +

+ A virtual hardware watchdog device can be added to the guest via + the watchdog element. + Since 0.7.3, QEMU and KVM only +

+ +

+ The watchdog device requires an additional driver and management + daemon in the guest. Just enabling the watchdog in the libvirt + configuration does not do anything useful on its own. +

+ +

+ Currently libvirt does not support notification when the + watchdog fires. This feature is planned for a future version of + libvirt. +

+ +
+      ...
+      <watchdog model='i6300esb'/>
+      ...
+ +
+      ...
+      <watchdog model='i6300esb' action='poweroff'/>
+      ...
+ +
+
model
+
+

+ The required model attribute specifies what real + watchdog device is emulated. Valid values are specific to the + underlying hypervisor. +

+

+ QEMU and KVM support: +

+
    +
  • 'i6300esb' — the recommended device, + emulating a PCI Intel 6300ESB
  • +
  • 'ib700' — emulating an ISA iBase IB700
  • +
+
+
action
+
+

+ The optional action attribute describes what + action to take when the watchdog expires. Valid values are + specific to the underlying hypervisor. +

+

+ QEMU and KVM support: +

+
    +
  • 'reset' — default, forcefully reset the guest
  • +
  • 'shutdown' — gracefully shutdown the guest + (not recommended)
  • +
  • 'poweroff' — forcefully power off the guest
  • +
  • 'pause' — pause the guest
  • +
  • 'none' — do nothing
  • +
+

+ Note that the 'shutdown' action requires that the guest + is responsive to ACPI signals. In the sort of situations + where the watchdog has expired, guests are usually unable + to respond to ACPI signals. Therefore using 'shutdown' + is not recommended. +

+
+
+

Example configs

diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index 1935f15..bb2db1d 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -1013,6 +1013,27 @@ + + + + + i6300esb + ib700 + + + + + + reset + shutdown + poweroff + pause + none + + + + + @@ -1139,6 +1160,9 @@ + + + diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index f50a8ef..7d558e8 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -84,7 +84,8 @@ VIR_ENUM_IMPL(virDomainDevice, VIR_DOMAIN_DEVICE_LAST, "input", "sound", "video", - "hostdev") + "hostdev", + "watchdog") VIR_ENUM_IMPL(virDomainDisk, VIR_DOMAIN_DISK_TYPE_LAST, "block", @@ -144,6 +145,17 @@ VIR_ENUM_IMPL(virDomainSoundModel, VIR_DOMAIN_SOUND_MODEL_LAST, "pcspk", "ac97") +VIR_ENUM_IMPL(virDomainWatchdogModel, VIR_DOMAIN_WATCHDOG_MODEL_LAST, + "i6300esb", + "ib700") + +VIR_ENUM_IMPL(virDomainWatchdogAction, VIR_DOMAIN_WATCHDOG_ACTION_LAST, + "reset", + "shutdown", + "poweroff", + "pause", + "none") + VIR_ENUM_IMPL(virDomainVideo, VIR_DOMAIN_VIDEO_TYPE_LAST, "vga", "cirrus", @@ -387,6 +399,14 @@ void virDomainSoundDefFree(virDomainSoundDefPtr def) VIR_FREE(def); } +void virDomainWatchdogDefFree(virDomainWatchdogDefPtr def) +{ + if (!def) + return; + + VIR_FREE(def); +} + void virDomainVideoDefFree(virDomainVideoDefPtr def) { if (!def) @@ -429,6 +449,9 @@ void virDomainDeviceDefFree(virDomainDeviceDefPtr def) case VIR_DOMAIN_DEVICE_HOSTDEV: virDomainHostdevDefFree(def->data.hostdev); break; + case VIR_DOMAIN_DEVICE_WATCHDOG: + virDomainWatchdogDefFree(def->data.watchdog); + break; } VIR_FREE(def); @@ -508,6 +531,8 @@ void virDomainDefFree(virDomainDefPtr def) VIR_FREE(def->emulator); VIR_FREE(def->description); + virDomainWatchdogDefFree(def->watchdog); + virSecurityLabelDefFree(def); VIR_FREE(def); @@ -1739,6 +1764,58 @@ error: } +static virDomainWatchdogDefPtr +virDomainWatchdogDefParseXML(virConnectPtr conn, + const xmlNodePtr node, + int flags ATTRIBUTE_UNUSED) { + + char *model = NULL; + char *action = NULL; + virDomainWatchdogDefPtr def; + + if (VIR_ALLOC (def) < 0) { + virReportOOMError (conn); + return NULL; + } + + model = virXMLPropString (node, "model"); + if (model == NULL) { + virDomainReportError (conn, VIR_ERR_INTERNAL_ERROR, + _("watchdog must contain model name")); + goto error; + } + def->model = virDomainWatchdogModelTypeFromString (model); + if (def->model < 0) { + virDomainReportError (conn, VIR_ERR_INTERNAL_ERROR, + _("unknown watchdog model '%s'"), model); + goto error; + } + + action = virXMLPropString (node, "action"); + if (action == NULL) + def->action = VIR_DOMAIN_WATCHDOG_ACTION_RESET; + else { + def->action = virDomainWatchdogActionTypeFromString (action); + if (def->action < 0) { + virDomainReportError (conn, VIR_ERR_INTERNAL_ERROR, + _("unknown watchdog action '%s'"), action); + goto error; + } + } + +cleanup: + VIR_FREE (action); + VIR_FREE (model); + + return def; + +error: + virDomainWatchdogDefFree (def); + def = NULL; + goto cleanup; +} + + int virDomainVideoDefaultRAM(virDomainDefPtr def, int type) @@ -2365,6 +2442,11 @@ virDomainDeviceDefPtr virDomainDeviceDefParse(virConnectPtr conn, dev->type = VIR_DOMAIN_DEVICE_SOUND; if (!(dev->data.sound = virDomainSoundDefParseXML(conn, node, flags))) goto error; + } else if (xmlStrEqual(node->name, BAD_CAST "watchdog")) { + dev->type = VIR_DOMAIN_DEVICE_WATCHDOG; + if (!(dev->data.watchdog = virDomainWatchdogDefParseXML(conn, node, + flags))) + goto error; } else if (xmlStrEqual(node->name, BAD_CAST "video")) { dev->type = VIR_DOMAIN_DEVICE_VIDEO; if (!(dev->data.video = virDomainVideoDefParseXML(conn, node, def, flags))) @@ -3039,6 +3121,28 @@ static virDomainDefPtr virDomainDefParseXML(virConnectPtr conn, } VIR_FREE(nodes); + /* analysis of the watchdog devices */ + def->watchdog = NULL; + if ((n = virXPathNodeSet(conn, "./devices/watchdog", ctxt, &nodes)) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot extract watchdog devices")); + goto error; + } + if (n > 1) { + virDomainReportError (conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("only a single watchdog device is supported")); + goto error; + } + if (n > 0) { + virDomainWatchdogDefPtr watchdog = + virDomainWatchdogDefParseXML (conn, nodes[0], flags); + if (!watchdog) + goto error; + + def->watchdog = watchdog; + VIR_FREE(nodes); + } + /* analysis of security label */ if (virSecurityLabelDefParseXML(conn, def, ctxt, flags) == -1) goto error; @@ -3946,6 +4050,33 @@ virDomainSoundDefFormat(virConnectPtr conn, } +static int +virDomainWatchdogDefFormat(virConnectPtr conn, + virBufferPtr buf, + virDomainWatchdogDefPtr def) +{ + const char *model = virDomainWatchdogModelTypeToString (def->model); + const char *action = virDomainWatchdogActionTypeToString (def->action); + + if (!model) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected watchdog model %d"), def->model); + return -1; + } + + if (!action) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected watchdog action %d"), def->action); + return -1; + } + + virBufferVSprintf(buf, " \n", + model, action); + + return 0; +} + + static void virDomainVideoAccelDefFormat(virBufferPtr buf, virDomainVideoAccelDefPtr def) @@ -4392,6 +4523,9 @@ char *virDomainDefFormat(virConnectPtr conn, if (virDomainHostdevDefFormat(conn, &buf, def->hostdevs[n], flags) < 0) goto cleanup; + if (def->watchdog) + virDomainWatchdogDefFormat (conn, &buf, def->watchdog); + virBufferAddLit(&buf, " \n"); if (def->seclabel.model) { diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 4b3646e..ff1b0cf 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -298,6 +298,30 @@ struct _virDomainSoundDef { int model; }; +enum virDomainWatchdogModel { + VIR_DOMAIN_WATCHDOG_MODEL_I6300ESB, + VIR_DOMAIN_WATCHDOG_MODEL_IB700, + + VIR_DOMAIN_WATCHDOG_MODEL_LAST +}; + +enum virDomainWatchdogAction { + VIR_DOMAIN_WATCHDOG_ACTION_RESET, + VIR_DOMAIN_WATCHDOG_ACTION_SHUTDOWN, + VIR_DOMAIN_WATCHDOG_ACTION_POWEROFF, + VIR_DOMAIN_WATCHDOG_ACTION_PAUSE, + VIR_DOMAIN_WATCHDOG_ACTION_NONE, + + VIR_DOMAIN_WATCHDOG_ACTION_LAST +}; + +typedef struct _virDomainWatchdogDef virDomainWatchdogDef; +typedef virDomainWatchdogDef *virDomainWatchdogDefPtr; +struct _virDomainWatchdogDef { + int model; + int action; +}; + enum virDomainVideoType { VIR_DOMAIN_VIDEO_TYPE_VGA, @@ -438,6 +462,7 @@ enum virDomainDeviceType { VIR_DOMAIN_DEVICE_SOUND, VIR_DOMAIN_DEVICE_VIDEO, VIR_DOMAIN_DEVICE_HOSTDEV, + VIR_DOMAIN_DEVICE_WATCHDOG, VIR_DOMAIN_DEVICE_LAST, }; @@ -454,6 +479,7 @@ struct _virDomainDeviceDef { virDomainSoundDefPtr sound; virDomainVideoDefPtr video; virDomainHostdevDefPtr hostdev; + virDomainWatchdogDefPtr watchdog; } data; }; @@ -586,6 +612,7 @@ struct _virDomainDef { /* Only 1 */ virDomainChrDefPtr console; virSecurityLabelDef seclabel; + virDomainWatchdogDefPtr watchdog; }; /* Guest VM runtime state */ @@ -639,6 +666,7 @@ void virDomainFSDefFree(virDomainFSDefPtr def); void virDomainNetDefFree(virDomainNetDefPtr def); void virDomainChrDefFree(virDomainChrDefPtr def); void virDomainSoundDefFree(virDomainSoundDefPtr def); +void virDomainWatchdogDefFree(virDomainWatchdogDefPtr def); void virDomainVideoDefFree(virDomainVideoDefPtr def); void virDomainHostdevDefFree(virDomainHostdevDefPtr def); void virDomainDeviceDefFree(virDomainDeviceDefPtr def); @@ -769,6 +797,8 @@ VIR_ENUM_DECL(virDomainFS) VIR_ENUM_DECL(virDomainNet) VIR_ENUM_DECL(virDomainChr) VIR_ENUM_DECL(virDomainSoundModel) +VIR_ENUM_DECL(virDomainWatchdogModel) +VIR_ENUM_DECL(virDomainWatchdogAction) VIR_ENUM_DECL(virDomainVideo) VIR_ENUM_DECL(virDomainHostdevMode) VIR_ENUM_DECL(virDomainHostdevSubsys) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 45d1069..98ea7f8 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -132,6 +132,10 @@ virDomainSaveStatus; virDomainSoundDefFree; virDomainSoundModelTypeFromString; virDomainSoundModelTypeToString; +virDomainWatchdogModelTypeFromString; +virDomainWatchdogModelTypeToString; +virDomainWatchdogActionTypeFromString; +virDomainWatchdogActionTypeToString; virDomainVideoDefFree; virDomainVideoTypeToString; virDomainVideoTypeFromString; diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index b881f1e..158e9a3 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -2233,6 +2233,28 @@ int qemudBuildCommandLine(virConnectPtr conn, ADD_ARG(modstr); } + /* Add watchdog hardware */ + if (def->watchdog) { + virDomainWatchdogDefPtr watchdog = def->watchdog; + const char *model = virDomainWatchdogModelTypeToString(watchdog->model); + if (!model) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("invalid watchdog model")); + goto error; + } + ADD_ARG_LIT("-watchdog"); + ADD_ARG_LIT(model); + + const char *action = virDomainWatchdogActionTypeToString(watchdog->action); + if (!action) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("invalid watchdog action")); + goto error; + } + ADD_ARG_LIT("-watchdog-action"); + ADD_ARG_LIT(action); + } + /* Add host passthrough hardware */ for (i = 0 ; i < def->nhostdevs ; i++) { int ret; @@ -3482,6 +3504,24 @@ virDomainDefPtr qemuParseCommandLine(virConnectPtr conn, start = tmp ? tmp + 1 : NULL; } + } else if (STREQ(arg, "-watchdog")) { + WANT_VALUE(); + int model = virDomainWatchdogModelTypeFromString (val); + + if (model != -1) { + virDomainWatchdogDefPtr wd; + if (VIR_ALLOC(wd) < 0) + goto no_memory; + wd->model = model; + wd->action = VIR_DOMAIN_WATCHDOG_ACTION_RESET; + def->watchdog = wd; + } + } else if (STREQ(arg, "-watchdog-action") && def->watchdog) { + WANT_VALUE(); + int action = virDomainWatchdogActionTypeFromString (val); + + if (action != -1) + def->watchdog->action = action; } else if (STREQ(arg, "-bootloader")) { WANT_VALUE(); def->os.bootloader = strdup(val); diff --git a/tests/qemuargv2xmltest.c b/tests/qemuargv2xmltest.c index 65d4d14..1b16aa9 100644 --- a/tests/qemuargv2xmltest.c +++ b/tests/qemuargv2xmltest.c @@ -212,6 +212,7 @@ mymain(int argc, char **argv) DO_TEST("parallel-tcp", 0); DO_TEST("console-compat", 0); DO_TEST("sound", 0); + DO_TEST("watchdog", 0); DO_TEST("hostdev-usb-product", 0); DO_TEST("hostdev-usb-address", 0); diff --git a/tests/qemuxml2argvdata/qemuxml2argv-watchdog.args b/tests/qemuxml2argvdata/qemuxml2argv-watchdog.args new file mode 100644 index 0000000..40a656b --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-watchdog.args @@ -0,0 +1 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S -M pc -m 214 -smp 1 -nographic -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -net none -serial none -parallel none -usb -watchdog ib700 -watchdog-action poweroff diff --git a/tests/qemuxml2argvdata/qemuxml2argv-watchdog.xml b/tests/qemuxml2argvdata/qemuxml2argv-watchdog.xml new file mode 100644 index 0000000..9b2ffdf --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-watchdog.xml @@ -0,0 +1,23 @@ + + QEMUGuest1 + c7a5fdbd-edaf-9455-926a-d65c16db1809 + 219200 + 219200 + 1 + + hvm + + + + destroy + restart + destroy + + /usr/bin/qemu + + + + + + + -- 1.6.5.rc2