Re: [libvirt] [PATCH] qemu: Allow domain reboot after core dump
yes, the patch can work, when I execute virsh dump --reboot domian file and the type of file is: QEMU's suspend to disk image ??: [libvirt] [PATCH] qemu: Allow domain reboot after core dump ??: Tue, 20 Sep 2011 13:34:27 +0200 ???:Michal Privoznik mpriv...@redhat.com ???:libvir-list@redhat.com This patch introduces possibility to reboot domain after core dump finishes. The new flag VIR_DUMP_REBOOT was added to virDomainCoreDumpFlags. The new functionality is accessible via virsh too: virsh dump --rebootdomain --- include/libvirt/libvirt.h.in |1 + src/qemu/qemu_driver.c |8 +++- tools/virsh.c|3 +++ 3 files changed, 11 insertions(+), 1 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 39155a6..8c41f5a 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -748,6 +748,7 @@ typedef enum { VIR_DUMP_CRASH= (1 0), /* crash after dump */ VIR_DUMP_LIVE = (1 1), /* live dump */ VIR_DUMP_BYPASS_CACHE = (1 2), /* avoid file system cache pollution */ +VIR_DUMP_REBOOT = (1 3), /* reboot domain after dump finishes */ } virDomainCoreDumpFlags; /* Domain migration flags. */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index e2f428f..22576a8 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -3104,7 +3104,8 @@ static int qemudDomainCoreDump(virDomainPtr dom, int ret = -1; virDomainEventPtr event = NULL; -virCheckFlags(VIR_DUMP_LIVE | VIR_DUMP_CRASH | VIR_DUMP_BYPASS_CACHE, -1); +virCheckFlags(VIR_DUMP_LIVE | VIR_DUMP_CRASH | + VIR_DUMP_BYPASS_CACHE | VIR_DUMP_REBOOT, -1); qemuDriverLock(driver); vm = virDomainFindByUUID(driver-domains, dom-uuid); @@ -3189,6 +3190,11 @@ cleanup: if (event) qemuDomainEventQueue(driver, event); qemuDriverUnlock(driver); + +if ((ret == 0) (flags VIR_DUMP_REBOOT)) { +qemuDomainReboot(dom, 0); +} + return ret; } diff --git a/tools/virsh.c b/tools/virsh.c index d575425..74f6a79 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -2840,6 +2840,7 @@ static const vshCmdOptDef opts_dump[] = { {crash, VSH_OT_BOOL, 0, N_(crash the domain after core dump)}, {bypass-cache, VSH_OT_BOOL, 0, N_(avoid file system cache when saving)}, +{reboot, VSH_OT_BOOL, 0, N_(reboot the domain after core dump)}, {domain, VSH_OT_DATA, VSH_OFLAG_REQ, N_(domain name, id or uuid)}, {file, VSH_OT_DATA, VSH_OFLAG_REQ, N_(where to dump the core)}, {NULL, 0, 0, NULL} @@ -2869,6 +2870,8 @@ cmdDump(vshControl *ctl, const vshCmd *cmd) flags |= VIR_DUMP_CRASH; if (vshCommandOptBool(cmd, bypass-cache)) flags |= VIR_DUMP_BYPASS_CACHE; +if (vshCommandOptBool(cmd, reboot)) +flags |= VIR_DUMP_REBOOT; if (virDomainCoreDump(dom, to, flags) 0) { vshError(ctl, _(Failed to core dump domain %s to %s), name, to); -- 1.7.3.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH v3 2/2] add interface for blkio.weight_device
This patch adds a parameter --weight-device to virsh command blkiotune for setting/getting blkio.weight_device. --- daemon/remote.c |5 + include/libvirt/libvirt.h.in |9 ++ src/conf/domain_conf.c | 114 - src/conf/domain_conf.h | 16 src/libvirt_private.syms |1 + src/qemu/qemu_cgroup.c | 22 + src/qemu/qemu_driver.c | 191 +- src/util/cgroup.c| 33 +++ src/util/cgroup.h|3 + tools/virsh.c| 52 ++-- tools/virsh.pod |5 +- 11 files changed, 437 insertions(+), 14 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 82ee13b..bee1b94 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1566,6 +1566,7 @@ remoteDispatchDomainGetBlkioParameters(virNetServerPtr server ATTRIBUTE_UNUSED, int nparams = args-nparams; unsigned int flags; int rv = -1; +int i; struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client); @@ -1610,6 +1611,10 @@ success: cleanup: if (rv 0) virNetMessageSaveError(rerr); +for (i = 0; i nparams; i++) { +if (params[i].type == VIR_TYPED_PARAM_STRING) +VIR_FREE(params[i].value.s); +} VIR_FREE(params); if (dom) virDomainFree(dom); diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 448a0e7..a1f2c98 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1139,6 +1139,15 @@ char * virDomainGetSchedulerType(virDomainPtr domain, #define VIR_DOMAIN_BLKIO_WEIGHT weight +/** + * VIR_DOMAIN_BLKIO_WEIGHT_DEVICE: + * + * Macro for the blkio tunable weight_device: it represents the + * per device weight. + */ + +#define VIR_DOMAIN_BLKIO_WEIGHT_DEVICE weight_device + /* Set Blkio tunables for the domain*/ int virDomainSetBlkioParameters(virDomainPtr domain, virTypedParameterPtr params, diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 7463d7c..75b17cd 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -566,6 +566,82 @@ VIR_ENUM_IMPL(virDomainNumatuneMemMode, VIR_DOMAIN_NUMATUNE_MEM_LAST, #define VIR_DOMAIN_XML_WRITE_FLAGS VIR_DOMAIN_XML_SECURE #define VIR_DOMAIN_XML_READ_FLAGS VIR_DOMAIN_XML_INACTIVE +/** + * virBlkioWeightDeviceToStr: + * + * This function returns a string representing device weights that is + * suitable for writing to /cgroup/blkio/blkio.weight_device, given + * a list of weight devices. + */ +int virBlkioWeightDeviceToStr(virBlkioWeightDevicePtr weightdevices, + int ndevices, + char **result) +{ +int i; +virBuffer buf = VIR_BUFFER_INITIALIZER; + +for (i = 0; i ndevices; i++) { +virBufferAsprintf(buf, %d:%d %d\n, + weightdevices[i].major, + weightdevices[i].minor, + weightdevices[i].weight); +} + +*result = virBufferContentAndReset(buf); +return 0; +} + +/** + * virDomainBlkioWeightDeviceParseXML + * + * this function parses a XML node: + * + * device + * path/fully/qulaified/device/path/path + * weightweight/weight + * /device + * + * and fills a virBlkioWeightDevice struct. + */ +static int virDomainBlkioWeightDeviceParseXML(xmlNodePtr root, + virBlkioWeightDevicePtr dw) +{ +char *c; +struct stat s; +xmlNodePtr node; + +if (!dw) +return -1; + +node = root-children; +while (node) { +if (node-type == XML_ELEMENT_NODE) { +if (xmlStrEqual(node-name, BAD_CAST path)) { +c = (char *)xmlNodeGetContent(node); +if (!c) +return -1; +if (stat(c, s) == -1) +return -1; +if ((s.st_mode S_IFMT) == S_IFBLK) { +dw-major = major(s.st_rdev); +dw-minor = minor(s.st_rdev); +} else +return -1; +dw-path = (char *)xmlNodeGetContent(node); +} else if (xmlStrEqual(node-name, BAD_CAST weight)) { +c = (char *)xmlNodeGetContent(node); +dw-weight = atoi(c); +VIR_FREE(c); +} +} +node = node-next; +} + +return 0; +} + + + static void virDomainObjListDataFree(void *payload, const void *name ATTRIBUTE_UNUSED) { @@ -1230,6 +1306,8 @@ void virDomainDefFree(virDomainDefPtr def) VIR_FREE(def-emulator); VIR_FREE(def-description); +VIR_FREE(def-blkio.weight_devices); + virDomainWatchdogDefFree(def-watchdog); virDomainMemballoonDefFree(def-memballoon); @@ -6464,6 +6542,20 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps,
[libvirt] [PATCH v3 1/2] Add VIR_TYPED_PARAM_STRING
This makes string can be transported between client and server. For compatibility, o new server should not send strings to old client if it doesn't see the flag VIR_DOMAIN_TYPED_STRING_OKAY. o new client that wants to be able to send/receive strings should always set the flag VIR_DOMAIN_TYPED_STRING_OKAY; if it is rejected by a old server that doesn't understand VIR_DOMAIN_TYPED_STRING_OKAY, then the client should have a second try with out flag VIR_DOMAIN_TYPED_STRING_OKAY to cope with an old server. Ideas of compatibility are coming from Eric, thanks. --- daemon/remote.c | 17 + include/libvirt/libvirt.h.in |5 - src/remote/remote_driver.c | 17 + src/remote/remote_protocol.x |2 ++ src/remote_protocol-structs |2 ++ 5 files changed, 42 insertions(+), 1 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 245d41c..82ee13b 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -672,6 +672,15 @@ remoteSerializeTypedParameters(virTypedParameterPtr params, case VIR_TYPED_PARAM_BOOLEAN: val[i].value.remote_typed_param_value_u.b = params[i].value.b; break; +case VIR_TYPED_PARAM_STRING: +if (params[i].value.s) { +val[i].value.remote_typed_param_value_u.s = strdup(params[i].value.s); +if (val[i].value.remote_typed_param_value_u.s == NULL) { +virReportOOMError(); +goto cleanup; +} +} +break; default: virNetError(VIR_ERR_RPC, _(unknown parameter type: %d), params[i].type); @@ -750,6 +759,14 @@ remoteDeserializeTypedParameters(remote_typed_param *args_params_val, params[i].value.b = args_params_val[i].value.remote_typed_param_value_u.b; break; +case VIR_TYPED_PARAM_STRING: +params[i].value.s = +strdup(args_params_val[i].value.remote_typed_param_value_u.s); +if (params[i].value.s == NULL) { +virReportOOMError(); +goto cleanup; +} +break; default: virNetError(VIR_ERR_INTERNAL_ERROR, _(unknown parameter type: %d), params[i].type); diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 39155a6..448a0e7 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -205,6 +205,7 @@ typedef enum { VIR_DOMAIN_AFFECT_CURRENT = 0, /* Affect current domain state. */ VIR_DOMAIN_AFFECT_LIVE= 1 0, /* Affect running domain state. */ VIR_DOMAIN_AFFECT_CONFIG = 1 1, /* Affect persistent domain state. */ +VIR_DOMAIN_TYPED_STRING_OKAY = 1 2, } virDomainModificationImpact; /** @@ -489,7 +490,8 @@ typedef enum { VIR_TYPED_PARAM_LLONG = 3, /* long long case */ VIR_TYPED_PARAM_ULLONG = 4, /* unsigned long long case */ VIR_TYPED_PARAM_DOUBLE = 5, /* double case */ -VIR_TYPED_PARAM_BOOLEAN = 6 /* boolean(character) case */ +VIR_TYPED_PARAM_BOOLEAN = 6, /* boolean(character) case */ +VIR_TYPED_PARAM_STRING = 7 /* string case */ } virTypedParameterType; /** @@ -520,6 +522,7 @@ struct _virTypedParameter { unsigned long long int ul; /* type is ULLONG */ double d; /* type is DOUBLE */ char b; /* type is BOOLEAN */ +char *s;/* type is STRING */ } value; /* parameter value */ }; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 1217d94..85eaeea 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -1276,6 +1276,15 @@ remoteSerializeTypedParameters(virTypedParameterPtr params, case VIR_TYPED_PARAM_BOOLEAN: val[i].value.remote_typed_param_value_u.b = params[i].value.b; break; +case VIR_TYPED_PARAM_STRING: +if (params[i].value.s) { +val[i].value.remote_typed_param_value_u.s = strdup(params[i].value.s); +if (val[i].value.remote_typed_param_value_u.s == NULL) { +virReportOOMError(); +goto cleanup; +} +} +break; default: remoteError(VIR_ERR_RPC, _(unknown parameter type: %d), params[i].type); @@ -1347,6 +1356,14 @@ remoteDeserializeTypedParameters(remote_typed_param *ret_params_val, params[i].value.b = ret_params_val[i].value.remote_typed_param_value_u.b; break; +case VIR_TYPED_PARAM_STRING: +params[i].value.s = +strdup(ret_params_val[i].value.remote_typed_param_value_u.s); +if (params[i].value.s == NULL) { +virReportOOMError(); +goto
[libvirt] [PATCH v3 0/2] add blkio.weight_device support
This series adds support for blkio.weight_device. changes from v2: - fix the compatibility issue of TYPED_STRING with old clients and old servers - modify XML format for weight_device as danp suggested Hu Tao (2): Add VIR_TYPED_PARAM_STRING add interface for blkio.weight_device daemon/remote.c | 22 + include/libvirt/libvirt.h.in | 14 +++- src/conf/domain_conf.c | 114 - src/conf/domain_conf.h | 16 src/libvirt_private.syms |1 + src/qemu/qemu_cgroup.c | 22 + src/qemu/qemu_driver.c | 191 +- src/remote/remote_driver.c | 17 src/remote/remote_protocol.x |2 + src/remote_protocol-structs |2 + src/util/cgroup.c| 33 +++ src/util/cgroup.h|3 + tools/virsh.c| 52 ++-- tools/virsh.pod |5 +- 14 files changed, 479 insertions(+), 15 deletions(-) -- 1.7.3.1 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [RFC PATCH] Prevent defining a domain has disk used by other domain
$subject + If the disk is shared and readonly. If the disk is not shared or readonly, the later started domain will relabel the disk, thus the first domain will lose the permission to write to the disk and be corrupt. -- I'm not sure if it's the design to allow multiple domains use same disk not shared and readonly. So this patch just gives a demo of the implementation (it skips the checking of nbd disk, and only changes qemu driver), to see whether if the pricinple is right or not. --- src/conf/domain_conf.c | 34 ++ src/conf/domain_conf.h |2 ++ src/libvirt_private.syms |1 + src/qemu/qemu_driver.c | 24 4 files changed, 61 insertions(+), 0 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 7463d7c..b64450b 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -655,6 +655,40 @@ virDomainObjPtr virDomainFindByName(const virDomainObjListPtr doms, return obj; } +static int +virDomainObjListSearchDiskPath(const void *payload, + const void *name ATTRIBUTE_UNUSED, + const void *data) +{ +virDomainObjPtr obj = (virDomainObjPtr)payload; +int i; +int want = 0; +const char *path = NULL; + +path = (const char *)data; + +virDomainObjLock(obj); +for (i = 0; obj-def-ndisks; i++) { +if (STREQ(obj-def-disks[i]-src, path)) { +want = 1; +break; +} +} +virDomainObjUnlock(obj); + +return want; +} + +virDomainObjPtr +virDomainFindByDiskPath(const virDomainObjListPtr doms, +const char *path) +{ +virDomainObjPtr obj = NULL; +obj = virHashSearch(doms-objs, virDomainObjListSearchDiskPath, path); +if (obj) +virDomainObjLock(obj); +return obj; +} bool virDomainObjTaint(virDomainObjPtr obj, enum virDomainTaintFlags taint) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 371f270..d7d42dd 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1549,6 +1549,8 @@ virDomainObjPtr virDomainFindByUUID(const virDomainObjListPtr doms, const unsigned char *uuid); virDomainObjPtr virDomainFindByName(const virDomainObjListPtr doms, const char *name); +virDomainObjPtr virDomainFindByDiskPath(const virDomainObjListPtr doms, +const char *path); bool virDomainObjTaint(virDomainObjPtr obj, enum virDomainTaintFlags taint); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 8235ea1..c33cb2d 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -302,6 +302,7 @@ virDomainFSTypeToString; virDomainFindByID; virDomainFindByName; virDomainFindByUUID; +virDomainFindByDiskPath; virDomainGetRootFilesystem; virDomainGraphicsAuthConnectedTypeFromString; virDomainGraphicsAuthConnectedTypeToString; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0d0bea2..a448a0b 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4796,6 +4796,7 @@ static virDomainPtr qemudDomainDefine(virConnectPtr conn, const char *xml) { virDomainPtr dom = NULL; virDomainEventPtr event = NULL; int dupVM; +int i; qemuDriverLock(driver); if (!(def = virDomainDefParseString(driver-caps, xml, @@ -4809,6 +4810,29 @@ static virDomainPtr qemudDomainDefine(virConnectPtr conn, const char *xml) { if ((dupVM = virDomainObjIsDuplicate(driver-domains, def, 0)) 0) goto cleanup; +/* Make sure no disk is used by other domain, if it's not + * either shareable or readonly. + */ +for (i = 0; i def-ndisks; i++) { +/* XXX: Do we also need to check if same hostport is used by + * other domain for disk of nbd type? + */ +if (def-disks[i]-type == VIR_DOMAIN_DISK_TYPE_NETWORK +def-disks[i]-protocol == VIR_DOMAIN_DISK_PROTOCOL_NBD) +continue; + +if (!def-disks[i]-shared +!def-disks[i]-readonly +(vm = virDomainFindByDiskPath(driver-domains, + def-disks[i]-src))) { +qemuReportError(VIR_ERR_INVALID_ARG, +_(disk '%s' already used by domain '%s'), +def-disks[i]-src, +vm-def-name); +goto cleanup; +} +} + if (qemudCanonicalizeMachine(driver, def) 0) goto cleanup; -- 1.7.6 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [RFC PATCH] Prevent defining a domain has disk used by other domain
On Fri, Sep 23, 2011 at 03:59:04PM +0800, Osier Yang wrote: $subject + If the disk is shared and readonly. If the disk is not shared or readonly, the later started domain will relabel the disk, thus the first domain will lose the permission to write to the disk and be corrupt. -- I'm not sure if it's the design to allow multiple domains use same disk not shared and readonly. So this patch just gives a demo of the implementation (it skips the checking of nbd disk, and only changes qemu driver), to see whether if the pricinple is right or not. --- src/conf/domain_conf.c | 34 ++ src/conf/domain_conf.h |2 ++ src/libvirt_private.syms |1 + src/qemu/qemu_driver.c | 24 4 files changed, 61 insertions(+), 0 deletions(-) NACK, defining two guests with the same disk is not a bug. Only *starting* two guests with the same disk is a problem, and that is what the lock manager code is protecting against. Regards, Daniel -- |: http://berrange.com -o-http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :| -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [RFC PATCH] Prevent defining a domain has disk used by other domain
于 2011年09月23日 15:59, Osier Yang 写道: $subject + If the disk is shared and readonly. s/disk is/disk is not/ If the disk is not shared or readonly, the later started domain will relabel the disk, thus the first domain will lose the permission to write to the disk and be corrupt. -- I'm not sure if it's the design to allow multiple domains use same disk not shared and readonly. So this patch just gives a demo of the implementation (it skips the checking of nbd disk, and only changes qemu driver), Hmm, preventing the relabeling in security driver instead might be the more proper way? (If the disk source is used by other *running* domain, then quit relabeling and exit with error). However, this won't prevent one using same disk source for multiple domains if security_driver is disabled. And if security_driver is disabled, there will be no permission problem, all the domains can write to the same disk source, thus it might cause inconsistency between the domains or corrupt. to see whether if the pricinple is right or not. --- src/conf/domain_conf.c | 34 ++ src/conf/domain_conf.h |2 ++ src/libvirt_private.syms |1 + src/qemu/qemu_driver.c | 24 4 files changed, 61 insertions(+), 0 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 7463d7c..b64450b 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -655,6 +655,40 @@ virDomainObjPtr virDomainFindByName(const virDomainObjListPtr doms, return obj; } +static int +virDomainObjListSearchDiskPath(const void *payload, + const void *name ATTRIBUTE_UNUSED, + const void *data) +{ +virDomainObjPtr obj = (virDomainObjPtr)payload; +int i; +int want = 0; +const char *path = NULL; + +path = (const char *)data; + +virDomainObjLock(obj); +for (i = 0; obj-def-ndisks; i++) { +if (STREQ(obj-def-disks[i]-src, path)) { +want = 1; +break; +} +} +virDomainObjUnlock(obj); + +return want; +} + +virDomainObjPtr +virDomainFindByDiskPath(const virDomainObjListPtr doms, +const char *path) +{ +virDomainObjPtr obj = NULL; +obj = virHashSearch(doms-objs, virDomainObjListSearchDiskPath, path); +if (obj) +virDomainObjLock(obj); +return obj; +} bool virDomainObjTaint(virDomainObjPtr obj, enum virDomainTaintFlags taint) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 371f270..d7d42dd 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1549,6 +1549,8 @@ virDomainObjPtr virDomainFindByUUID(const virDomainObjListPtr doms, const unsigned char *uuid); virDomainObjPtr virDomainFindByName(const virDomainObjListPtr doms, const char *name); +virDomainObjPtr virDomainFindByDiskPath(const virDomainObjListPtr doms, +const char *path); bool virDomainObjTaint(virDomainObjPtr obj, enum virDomainTaintFlags taint); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 8235ea1..c33cb2d 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -302,6 +302,7 @@ virDomainFSTypeToString; virDomainFindByID; virDomainFindByName; virDomainFindByUUID; +virDomainFindByDiskPath; virDomainGetRootFilesystem; virDomainGraphicsAuthConnectedTypeFromString; virDomainGraphicsAuthConnectedTypeToString; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0d0bea2..a448a0b 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4796,6 +4796,7 @@ static virDomainPtr qemudDomainDefine(virConnectPtr conn, const char *xml) { virDomainPtr dom = NULL; virDomainEventPtr event = NULL; int dupVM; +int i; qemuDriverLock(driver); if (!(def = virDomainDefParseString(driver-caps, xml, @@ -4809,6 +4810,29 @@ static virDomainPtr qemudDomainDefine(virConnectPtr conn, const char *xml) { if ((dupVM = virDomainObjIsDuplicate(driver-domains, def, 0)) 0) goto cleanup; +/* Make sure no disk is used by other domain, if it's not + * either shareable or readonly. + */ +for (i = 0; i def-ndisks; i++) { +/* XXX: Do we also need to check if same hostport is used by + * other domain for disk of nbd type? + */ +if (def-disks[i]-type == VIR_DOMAIN_DISK_TYPE_NETWORK +def-disks[i]-protocol == VIR_DOMAIN_DISK_PROTOCOL_NBD) +continue; + +if (!def-disks[i]-shared +!def-disks[i]-readonly +(vm = virDomainFindByDiskPath(driver-domains, + def-disks[i]-src)) ) { +
[libvirt] [PATCH v2 12/12] qemu: Cancel p2p migration when connection breaks
If a connection to destination host is lost during peer-to-peer migration (because keepalive protocol timed out), we won't be able to finish the migration and it doesn't make sense to wait for qemu to transmit all data. This patch automatically cancels such migration without waiting for virDomainAbortJob to be called. --- src/qemu/qemu_migration.c | 39 +-- 1 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 301d8ba..fb3b411 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -901,7 +901,8 @@ qemuMigrationUpdateJobStatus(struct qemud_driver *driver, static int qemuMigrationWaitForCompletion(struct qemud_driver *driver, virDomainObjPtr vm, - enum qemuDomainAsyncJob asyncJob) + enum qemuDomainAsyncJob asyncJob, + virConnectPtr dconn) { qemuDomainObjPrivatePtr priv = vm-privateData; const char *job; @@ -929,6 +930,12 @@ qemuMigrationWaitForCompletion(struct qemud_driver *driver, virDomainObjPtr vm, if (qemuMigrationUpdateJobStatus(driver, vm, job, asyncJob) 0) goto cleanup; +if (dconn virConnectIsAlive(dconn) = 0) { +qemuReportError(VIR_ERR_OPERATION_FAILED, %s, +_(Lost connection to destination host)); +goto cleanup; +} + virDomainObjUnlock(vm); qemuDriverUnlock(driver); @@ -1491,7 +1498,8 @@ qemuMigrationRun(struct qemud_driver *driver, int *cookieoutlen, unsigned long flags, unsigned long resource, - qemuMigrationSpecPtr spec) + qemuMigrationSpecPtr spec, + virConnectPtr dconn) { int ret = -1; unsigned int migrate_flags = QEMU_MONITOR_MIGRATE_BACKGROUND; @@ -1610,7 +1618,8 @@ qemuMigrationRun(struct qemud_driver *driver, goto cancel; if (qemuMigrationWaitForCompletion(driver, vm, - QEMU_ASYNC_JOB_MIGRATION_OUT) 0) + QEMU_ASYNC_JOB_MIGRATION_OUT, + dconn) 0) goto cleanup; /* When migration completed, QEMU will have paused the @@ -1667,7 +1676,8 @@ static int doNativeMigrate(struct qemud_driver *driver, char **cookieout, int *cookieoutlen, unsigned long flags, - unsigned long resource) + unsigned long resource, + virConnectPtr dconn) { qemuDomainObjPrivatePtr priv = vm-privateData; xmlURIPtr uribits = NULL; @@ -1725,7 +1735,7 @@ static int doNativeMigrate(struct qemud_driver *driver, } ret = qemuMigrationRun(driver, vm, cookiein, cookieinlen, cookieout, - cookieoutlen, flags, resource, spec); + cookieoutlen, flags, resource, spec, dconn); cleanup: if (spec.destType == MIGRATION_DEST_FD) @@ -1746,7 +1756,8 @@ static int doTunnelMigrate(struct qemud_driver *driver, char **cookieout, int *cookieoutlen, unsigned long flags, - unsigned long resource) + unsigned long resource, + virConnectPtr dconn) { qemuDomainObjPrivatePtr priv = vm-privateData; virNetSocketPtr sock = NULL; @@ -1809,7 +1820,7 @@ static int doTunnelMigrate(struct qemud_driver *driver, } ret = qemuMigrationRun(driver, vm, cookiein, cookieinlen, cookieout, - cookieoutlen, flags, resource, spec); + cookieoutlen, flags, resource, spec, dconn); cleanup: if (spec.destType == MIGRATION_DEST_FD) { @@ -1912,12 +1923,12 @@ static int doPeer2PeerMigrate2(struct qemud_driver *driver, if (flags VIR_MIGRATE_TUNNELLED) ret = doTunnelMigrate(driver, vm, st, NULL, 0, NULL, NULL, - flags, resource); + flags, resource, dconn); else ret = doNativeMigrate(driver, vm, uri_out, cookie, cookielen, NULL, NULL, /* No out cookie with v2 migration */ - flags, resource); + flags, resource, dconn); /* Perform failed. Make sure Finish doesn't overwrite the error */ if (ret 0) @@ -2058,12 +2069,12 @@ static int doPeer2PeerMigrate3(struct qemud_driver *driver, ret = doTunnelMigrate(driver, vm, st, cookiein, cookieinlen, cookieout, cookieoutlen, -
[libvirt] [PATCH v2 02/12] Implement common keepalive handling
These APIs are used by both client and server RPC layer to handle processing of keepalive messages. --- po/POTFILES.in |1 + src/Makefile.am|3 +- src/rpc/virkeepalive.c | 464 src/rpc/virkeepalive.h | 58 ++ 4 files changed, 525 insertions(+), 1 deletions(-) create mode 100644 src/rpc/virkeepalive.c create mode 100644 src/rpc/virkeepalive.h diff --git a/po/POTFILES.in b/po/POTFILES.in index 5ce35ae..71254dd 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -72,6 +72,7 @@ src/qemu/qemu_monitor_text.c src/qemu/qemu_process.c src/remote/remote_client_bodies.h src/remote/remote_driver.c +src/rpc/virkeepalive.c src/rpc/virnetclient.c src/rpc/virnetclientprogram.c src/rpc/virnetclientstream.c diff --git a/src/Makefile.am b/src/Makefile.am index ff890e1..d983d28 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1326,7 +1326,8 @@ libvirt_net_rpc_la_SOURCES = \ rpc/virnetprotocol.h rpc/virnetprotocol.c \ rpc/virnetsocket.h rpc/virnetsocket.c \ rpc/virnettlscontext.h rpc/virnettlscontext.c \ - rpc/virkeepaliveprotocol.h rpc/virkeepaliveprotocol.c + rpc/virkeepaliveprotocol.h rpc/virkeepaliveprotocol.c \ + rpc/virkeepalive.h rpc/virkeepalive.c if HAVE_SASL libvirt_net_rpc_la_SOURCES += \ rpc/virnetsaslcontext.h rpc/virnetsaslcontext.c diff --git a/src/rpc/virkeepalive.c b/src/rpc/virkeepalive.c new file mode 100644 index 000..5536b61 --- /dev/null +++ b/src/rpc/virkeepalive.c @@ -0,0 +1,464 @@ +/* + * virkeepalive.c: keepalive handling + * + * Copyright (C) 2011 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Jiri Denemark jdene...@redhat.com + */ + +#include config.h + +#include memory.h +#include threads.h +#include virfile.h +#include logging.h +#include util.h +#include virterror_internal.h +#include virnetsocket.h +#include virkeepaliveprotocol.h +#include virkeepalive.h + +#define VIR_FROM_THIS VIR_FROM_RPC +#define virNetError(code, ...)\ +virReportErrorHelper(VIR_FROM_THIS, code, __FILE__, \ + __FUNCTION__, __LINE__, __VA_ARGS__) + +struct _virKeepAlive { +int refs; +virMutex lock; + +int interval; +unsigned int count; +unsigned int countToDeath; +time_t lastPacketReceived; +bool advertised; +int timer; + +virNetMessagePtr response; +int responseTimer; + +virKeepAliveSendFunc sendCB; +virKeepAliveDeadFunc deadCB; +virKeepAliveFreeFunc freeCB; +void *client; +}; + + +static void +virKeepAliveLock(virKeepAlivePtr ka) +{ +virMutexLock(ka-lock); +} + +static void +virKeepAliveUnlock(virKeepAlivePtr ka) +{ +virMutexUnlock(ka-lock); +} + + +static virNetMessagePtr +virKeepAliveMessage(int proc) +{ +virNetMessagePtr msg; + +if (!(msg = virNetMessageNew(false))) +return NULL; + +msg-header.prog = KEEPALIVE_PROGRAM; +msg-header.vers = KEEPALIVE_VERSION; +msg-header.type = VIR_NET_MESSAGE; +msg-header.proc = proc; + +if (virNetMessageEncodeHeader(msg) 0 || +virNetMessageEncodePayloadEmpty(msg) 0) { +virNetMessageFree(msg); +return NULL; +} + +return msg; +} + + +static int +virKeepAliveSend(virKeepAlivePtr ka, virNetMessagePtr msg) +{ +int ret; +const char *proc; +void *client = ka-client; +virKeepAliveSendFunc sendCB = ka-sendCB; + +switch (msg-header.proc) { +case KEEPALIVE_PROC_ADVERTISE: +proc = advertisement; +break; +case KEEPALIVE_PROC_PING: +proc = request; +break; +case KEEPALIVE_PROC_PONG: +proc = response; +break; +} + +VIR_DEBUG(Sending keepalive %s to client %p, proc, ka-client); + +ka-refs++; +virKeepAliveUnlock(ka); + +if ((ret = sendCB(client, msg)) 0) { +VIR_WARN(Failed to send keepalive %s to client %p, proc, client); +virNetMessageFree(msg); +} + +virKeepAliveLock(ka); +ka-refs--; + +return ret; +} + + +int +virKeepAliveAdvertise(virKeepAlivePtr ka) +{ +virNetMessagePtr msg; +int ret = -1; + +virKeepAliveLock(ka); + +VIR_DEBUG(Advertising keepalive support to client %p,
[libvirt] [PATCH v2 01/12] Define keepalive protocol
The keepalive program has three procedures: ADVERTISE, PING, and PONG. All are used only in asynchronous messages and the sender doesn't wait for any reply. However, the party which receives PING messages is supposed to react by sending PONG message the other party, but no explicit binding between PING and PONG messages is made. ADVERTISE is sent by a client to indicate it supports keepalive protocol. Server is not allowed to send any keepalive message until it sees ADVERTISE. --- .gitignore |1 + src/Makefile.am| 12 +--- src/rpc/virkeepaliveprotocol.x |8 3 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 src/rpc/virkeepaliveprotocol.x diff --git a/.gitignore b/.gitignore index 41fa50f..3859fab 100644 --- a/.gitignore +++ b/.gitignore @@ -65,6 +65,7 @@ /src/locking/qemu-sanlock.conf /src/remote/*_client_bodies.h /src/remote/*_protocol.[ch] +/src/rpc/virkeepaliveprotocol.[ch] /src/rpc/virnetprotocol.[ch] /src/util/virkeymaps.h /tests/*.log diff --git a/src/Makefile.am b/src/Makefile.am index 738ee91..ff890e1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -266,7 +266,8 @@ PDWTAGS = \ PROTOCOL_STRUCTS = \ $(srcdir)/remote_protocol-structs \ $(srcdir)/qemu_protocol-structs \ - $(srcdir)/virnetprotocol-structs + $(srcdir)/virnetprotocol-structs \ + $(srcdir)/virkeepaliveprotocol-structs if WITH_REMOTE # The .o file that pdwtags parses is created as a side effect of running # libtool; but from make's perspective we depend on the .lo file. @@ -274,6 +275,7 @@ $(srcdir)/%_protocol-structs: libvirt_driver_remote_la-%_protocol.lo $(PDWTAGS) $(srcdir)/virnetprotocol-structs: libvirt_net_rpc_la-virnetprotocol.lo $(PDWTAGS) +$(srcdir)/virkeepaliveprotocol-structs: libvirt_net_rpc_la-virkeepaliveprotocol.lo else !WITH_REMOTE # These generated files must live in git, because they cannot be re-generated # when configured --without-remote. @@ -1307,12 +1309,15 @@ noinst_LTLIBRARIES += \ EXTRA_DIST += \ rpc/virnetprotocol.x \ + rpc/virkeepaliveprotocol.x \ rpc/gendispatch.pl \ rpc/genprotocol.pl VIR_NET_RPC_GENERATED = \ $(srcdir)/rpc/virnetprotocol.h \ - $(srcdir)/rpc/virnetprotocol.c + $(srcdir)/rpc/virnetprotocol.c \ + $(srcdir)/rpc/virkeepaliveprotocol.h \ + $(srcdir)/rpc/virkeepaliveprotocol.c BUILT_SOURCES += $(VIR_NET_RPC_GENERATED) @@ -1320,7 +1325,8 @@ libvirt_net_rpc_la_SOURCES = \ rpc/virnetmessage.h rpc/virnetmessage.c \ rpc/virnetprotocol.h rpc/virnetprotocol.c \ rpc/virnetsocket.h rpc/virnetsocket.c \ - rpc/virnettlscontext.h rpc/virnettlscontext.c + rpc/virnettlscontext.h rpc/virnettlscontext.c \ + rpc/virkeepaliveprotocol.h rpc/virkeepaliveprotocol.c if HAVE_SASL libvirt_net_rpc_la_SOURCES += \ rpc/virnetsaslcontext.h rpc/virnetsaslcontext.c diff --git a/src/rpc/virkeepaliveprotocol.x b/src/rpc/virkeepaliveprotocol.x new file mode 100644 index 000..326ea78 --- /dev/null +++ b/src/rpc/virkeepaliveprotocol.x @@ -0,0 +1,8 @@ +const KEEPALIVE_PROGRAM = 0x6b656570; +const KEEPALIVE_VERSION = 1; + +enum keepalive_procedure { +KEEPALIVE_PROC_ADVERTISE = 1, +KEEPALIVE_PROC_PING = 2, +KEEPALIVE_PROC_PONG = 3 +}; -- 1.7.6.1 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH v2 06/12] Add support for async close of client RPC socket
--- src/rpc/virnetclient.c | 76 ++-- 1 files changed, 67 insertions(+), 9 deletions(-) diff --git a/src/rpc/virnetclient.c b/src/rpc/virnetclient.c index 7ea9a27..45b0edb 100644 --- a/src/rpc/virnetclient.c +++ b/src/rpc/virnetclient.c @@ -92,9 +92,13 @@ struct _virNetClient { size_t nstreams; virNetClientStreamPtr *streams; + +bool wantClose; }; +void virNetClientRequestClose(virNetClientPtr client); + static int virNetClientSendInternal(virNetClientPtr client, virNetMessagePtr msg, bool expectReply, @@ -297,12 +301,14 @@ void virNetClientFree(virNetClientPtr client) } -void virNetClientClose(virNetClientPtr client) +static void +virNetClientCloseLocked(virNetClientPtr client) { -if (!client) +VIR_DEBUG(client=%p, sock=%p, client, client-sock); + +if (!client-sock) return; -virNetClientLock(client); virNetSocketRemoveIOCallback(client-sock); virNetSocketFree(client-sock); client-sock = NULL; @@ -312,6 +318,41 @@ void virNetClientClose(virNetClientPtr client) virNetSASLSessionFree(client-sasl); client-sasl = NULL; #endif +client-wantClose = false; +} + +void virNetClientClose(virNetClientPtr client) +{ +if (!client) +return; + +virNetClientLock(client); +virNetClientCloseLocked(client); +virNetClientUnlock(client); +} + +void +virNetClientRequestClose(virNetClientPtr client) +{ +VIR_DEBUG(client=%p, client); + +virNetClientLock(client); + +/* If there is a thread polling for data on the socket, set wantClose flag + * and wake the thread up or just immediately close the socket when no-one + * is polling on it. + */ +if (client-waitDispatch) { +char ignore = 1; +int len = sizeof(ignore); + +client-wantClose = true; +if (safewrite(client-wakeupSendFD, ignore, len) != len) +VIR_ERROR(_(failed to wake up polling thread)); +} else { +virNetClientCloseLocked(client); +} + virNetClientUnlock(client); } @@ -856,11 +897,12 @@ static int virNetClientIOEventLoop(virNetClientPtr client, int timeout = -1; bool discardNonBlocking; -/* If we have existing SASL decoded data we - * don't want to sleep in the poll(), just - * check if any other FDs are also ready +/* If we have existing SASL decoded data we don't want to sleep in + * the poll(), just check if any other FDs are also ready. + * If the connection is going to be closed, we don't want to sleep in + * poll() either. */ -if (virNetSocketHasCachedData(client-sock)) +if (virNetSocketHasCachedData(client-sock) || client-wantClose) timeout = 0; fds[0].events = fds[0].revents = 0; @@ -922,6 +964,11 @@ static int virNetClientIOEventLoop(virNetClientPtr client, if (virNetSocketHasCachedData(client-sock)) fds[0].revents |= POLLIN; +/* If wantClose flag is set, pretend there was an error on the socket + */ +if (client-wantClose) +fds[0].revents = POLLERR; + if (fds[1].revents) { VIR_DEBUG(Woken up from poll by other thread); if (saferead(client-wakeupReadFD, ignore, sizeof(ignore)) != sizeof(ignore)) { @@ -1041,6 +1088,8 @@ error: if (client-waitDispatch) { VIR_DEBUG(Passing the buck to %p, client-waitDispatch); virCondSignal(client-waitDispatch-cond); +} else if (client-wantClose) { +virNetClientCloseLocked(client); } return -1; } @@ -1182,7 +1231,8 @@ static int virNetClientIO(virNetClientPtr client, virResetLastError(); rv = virNetClientIOEventLoop(client, thiscall); -virNetSocketUpdateIOCallback(client-sock, VIR_EVENT_HANDLE_READABLE); +if (client-sock) +virNetSocketUpdateIOCallback(client-sock, VIR_EVENT_HANDLE_READABLE); if (rv == 0 virGetLastError()) @@ -1206,7 +1256,7 @@ void virNetClientIncomingEvent(virNetSocketPtr sock, goto done; /* This should be impossible, but it doesn't hurt to check */ -if (client-waitDispatch) +if (client-waitDispatch || client-wantClose) goto done; VIR_DEBUG(Event fired %p %d, sock, events); @@ -1251,6 +1301,12 @@ virNetClientSendInternal(virNetClientPtr client, virNetClientLock(client); +if (!client-sock || client-wantClose) { +virNetError(VIR_ERR_INTERNAL_ERROR, %s, +_(Client socket is closed)); +goto unlock; +} + /* We don't need call-cond for non-blocking calls since there's no * thread to be woken up anyway */ @@ -1290,6 +1346,8 @@ cleanup: } else { VIR_FREE(call); } + +unlock: virNetClientUnlock(client); return ret; } -- 1.7.6.1 -- libvir-list mailing list
[libvirt] [PATCH v2 04/12] Implement keepalive protocol in libvirt daemon
--- daemon/libvirtd.aug |4 + daemon/libvirtd.c| 11 daemon/libvirtd.conf | 15 + daemon/remote.c | 38 + src/remote/remote_protocol.x |2 +- src/rpc/virnetserver.c | 10 +++ src/rpc/virnetserver.h |2 + src/rpc/virnetserverclient.c | 126 +++--- src/rpc/virnetserverclient.h |6 ++ 9 files changed, 204 insertions(+), 10 deletions(-) diff --git a/daemon/libvirtd.aug b/daemon/libvirtd.aug index ce00db5..6cd3f28 100644 --- a/daemon/libvirtd.aug +++ b/daemon/libvirtd.aug @@ -66,6 +66,9 @@ module Libvirtd = let auditing_entry = int_entry audit_level | bool_entry audit_logging + let keepalive_entry = int_entry keepalive_interval + | int_entry keepalive_count + (* Each enty in the config is one of the following three ... *) let entry = network_entry | sock_acl_entry @@ -75,6 +78,7 @@ module Libvirtd = | processing_entry | logging_entry | auditing_entry + | keepalive_entry let comment = [ label #comment . del /#[ \t]*/ # . store /([^ \t\n][^\n]*)?/ . del /\n/ \n ] let empty = [ label #empty . eol ] diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index d1bc3dd..4ad2b2e 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -146,6 +146,9 @@ struct daemonConfig { int audit_level; int audit_logging; + +int keepalive_interval; +unsigned int keepalive_count; }; enum { @@ -899,6 +902,9 @@ daemonConfigNew(bool privileged ATTRIBUTE_UNUSED) data-audit_level = 1; data-audit_logging = 0; +data-keepalive_interval = 5; +data-keepalive_count = 5; + localhost = virGetHostname(NULL); if (localhost == NULL) { /* we couldn't resolve the hostname; assume that we are @@ -1062,6 +1068,9 @@ daemonConfigLoad(struct daemonConfig *data, GET_CONF_STR (conf, filename, log_outputs); GET_CONF_INT (conf, filename, log_buffer_size); +GET_CONF_INT (conf, filename, keepalive_interval); +GET_CONF_INT (conf, filename, keepalive_count); + virConfFree (conf); return 0; @@ -1452,6 +1461,8 @@ int main(int argc, char **argv) { config-max_workers, config-prio_workers, config-max_clients, +config-keepalive_interval, +config-keepalive_count, config-mdns_adv ? config-mdns_name : NULL, use_polkit_dbus, remoteClientInitHook))) { diff --git a/daemon/libvirtd.conf b/daemon/libvirtd.conf index da3983e..4f6ab9f 100644 --- a/daemon/libvirtd.conf +++ b/daemon/libvirtd.conf @@ -366,3 +366,18 @@ # it with the output of the 'uuidgen' command and then # uncomment this entry #host_uuid = ---- + +### +# Keepalive protocol: +# This allows libvirtd to detect broken client connections or even +# dead client. A keepalive message is sent to a client after +# keepalive_interval seconds of inactivity to check if the client is +# still responding; keepalive_count is a maximum number of keepalive +# messages that are allowed to be sent to the client without getting +# any response before the connection is considered broken. In other +# words, the connection is automatically closed approximately after +# keepalive_interval * (keepalive_count + 1) seconds since the last +# message received from the client. +# +#keepalive_interval = 5 +#keepalive_count = 5 diff --git a/daemon/remote.c b/daemon/remote.c index 245d41c..946bb7e 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3112,6 +3112,44 @@ cleanup: } +static int +remoteDispatchSupportsFeature(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_supports_feature_args *args, + remote_supports_feature_ret *ret) +{ +int rv = -1; +int supported; +struct daemonClientPrivate *priv = +virNetServerClientGetPrivateData(client); + +if (!priv-conn) { +virNetError(VIR_ERR_INTERNAL_ERROR, %s, _(connection not open)); +goto cleanup; +} + +if (args-feature == VIR_DRV_FEATURE_PROGRAM_KEEPALIVE) { +supported = 1; +goto done; +} + +if ((supported = virDrvSupportsFeature(priv-conn, args-feature)) 0) +goto cleanup; + +done: +ret-supported = supported; +rv = 0; + +cleanup: +if (rv 0) +virNetMessageSaveError(rerr); +return rv; +} + + + /*- Helpers. -*/
[libvirt] [PATCH v2 00/12] Implement keepalive protocol for libvirt RPC
This patchset can also be found at https://gitorious.org/~jirka/libvirt/jirka-staging/commits/keepalive This allows us to detect broken connections between server and client without waiting for TCP timeout and dead deamon/client. By default a connection is considered broken after about 30 seconds of no messages received from remote party. After that period, the connection is automatically closed. The main reason for implementing this is that peer-to-peer migration can now be canceled when a connection between source and target breaks. Although this will really work only after qemu fixes migrate_cancel command so that it doesn't block when outgoing TCP buffers are full. Version 2 adds virConnectIsAlive API and uses it to detect that a connection was closed as a result of keepalive timeout. The only patch that was changed in v2 is Add keepalive support into domain-events examples. All other patches are either new or without any change from v1. Jiri Denemark (12): Define keepalive protocol Implement common keepalive handling Introduce two public APIs for keepalive protocol Implement keepalive protocol in libvirt daemon Add support for non-blocking calls in client RPC Add support for async close of client RPC socket Implement keepalive protocol in remote driver Introduce virConnectIsAlive API Implement virConnectIsAlive in all drivers Add keepalive support into domain-events examples qemu: Add support for keepalive messages during p2p migration qemu: Cancel p2p migration when connection breaks .gitignore |1 + daemon/libvirtd.aug|4 + daemon/libvirtd.c | 11 + daemon/libvirtd.conf | 15 + daemon/remote.c| 38 ++ examples/domain-events/events-c/event-test.c | 13 +- examples/domain-events/events-python/event-test.py |5 +- include/libvirt/libvirt.h.in |6 + po/POTFILES.in |1 + src/Makefile.am| 13 +- src/driver.h | 12 + src/esx/esx_driver.c | 18 + src/hyperv/hyperv_driver.c | 18 + src/libvirt.c | 143 ++ src/libvirt_internal.h | 10 +- src/libvirt_public.syms|7 + src/libxl/libxl_driver.c |8 + src/lxc/lxc_driver.c |7 + src/openvz/openvz_driver.c |7 + src/phyp/phyp_driver.c | 18 + src/qemu/libvirtd_qemu.aug |2 + src/qemu/qemu.conf | 16 + src/qemu/qemu_conf.c | 11 + src/qemu/qemu_conf.h |3 + src/qemu/qemu_driver.c |6 + src/qemu/qemu_migration.c | 49 ++- src/qemu/test_libvirtd_qemu.aug|6 + src/remote/remote_driver.c | 48 ++ src/remote/remote_protocol.x |2 +- src/rpc/virkeepalive.c | 464 src/rpc/virkeepalive.h | 58 +++ src/rpc/virkeepaliveprotocol.x |8 + src/rpc/virnetclient.c | 335 -- src/rpc/virnetclient.h |6 + src/rpc/virnetserver.c | 10 + src/rpc/virnetserver.h |2 + src/rpc/virnetserverclient.c | 126 +- src/rpc/virnetserverclient.h |6 + src/test/test_driver.c |6 + src/uml/uml_driver.c |7 + src/vbox/vbox_tmpl.c |6 + src/vmware/vmware_driver.c |7 + src/xen/xen_driver.c |8 + src/xenapi/xenapi_driver.c | 12 + 44 files changed, 1483 insertions(+), 76 deletions(-) create mode 100644 src/rpc/virkeepalive.c create mode 100644 src/rpc/virkeepalive.h create mode 100644 src/rpc/virkeepaliveprotocol.x -- 1.7.6.1 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH v2 09/12] Implement virConnectIsAlive in all drivers
--- src/esx/esx_driver.c | 18 ++ src/hyperv/hyperv_driver.c | 18 ++ src/libxl/libxl_driver.c |8 src/lxc/lxc_driver.c |7 +++ src/openvz/openvz_driver.c |7 +++ src/phyp/phyp_driver.c | 18 ++ src/qemu/qemu_driver.c |6 ++ src/remote/remote_driver.c | 18 ++ src/rpc/virnetclient.c | 14 ++ src/rpc/virnetclient.h |1 + src/test/test_driver.c |6 ++ src/uml/uml_driver.c |7 +++ src/vbox/vbox_tmpl.c |6 ++ src/vmware/vmware_driver.c |7 +++ src/xen/xen_driver.c |8 src/xenapi/xenapi_driver.c | 12 16 files changed, 161 insertions(+), 0 deletions(-) diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c index f1102ea..c71f4f8 100644 --- a/src/esx/esx_driver.c +++ b/src/esx/esx_driver.c @@ -4145,6 +4145,23 @@ esxIsSecure(virConnectPtr conn) static int +esxIsAlive(virConnectPtr conn) +{ +esxPrivate *priv = conn-privateData; + +/* XXX we should be able to do something better than this is simple, safe, + * and good enough for now. In worst case, the function will return true + * even though the connection is not alive. + */ +if (priv-host) +return 1; +else +return 0; +} + + + +static int esxDomainIsActive(virDomainPtr domain) { int result = -1; @@ -4813,6 +4830,7 @@ static virDriver esxDriver = { .domainSnapshotCurrent = esxDomainSnapshotCurrent, /* 0.8.0 */ .domainRevertToSnapshot = esxDomainRevertToSnapshot, /* 0.8.0 */ .domainSnapshotDelete = esxDomainSnapshotDelete, /* 0.8.0 */ +.isAlive = esxIsAlive, /* 0.9.7 */ }; diff --git a/src/hyperv/hyperv_driver.c b/src/hyperv/hyperv_driver.c index b022fee..bf8575c 100644 --- a/src/hyperv/hyperv_driver.c +++ b/src/hyperv/hyperv_driver.c @@ -1100,6 +1100,23 @@ hypervIsSecure(virConnectPtr conn) static int +hypervIsAlive(virConnectPtr conn) +{ +hypervPrivate *priv = conn-privateData; + +/* XXX we should be able to do something better than this is simple, safe, + * and good enough for now. In worst case, the function will return true + * even though the connection is not alive. + */ +if (priv-client) +return 1; +else +return 0; +} + + + +static int hypervDomainIsActive(virDomainPtr domain) { int result = -1; @@ -1257,6 +1274,7 @@ static virDriver hypervDriver = { .domainManagedSave = hypervDomainManagedSave, /* 0.9.5 */ .domainHasManagedSaveImage = hypervDomainHasManagedSaveImage, /* 0.9.5 */ .domainManagedSaveRemove = hypervDomainManagedSaveRemove, /* 0.9.5 */ +.isAlive = hypervIsAlive, /* 0.9.7 */ }; diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c index d324632..42503d6 100644 --- a/src/libxl/libxl_driver.c +++ b/src/libxl/libxl_driver.c @@ -3875,6 +3875,13 @@ libxlDomainEventDeregisterAny(virConnectPtr conn, int callbackID) } +static int +libxlIsAlive(virConnectPtr conn ATTRIBUTE_UNUSED) +{ +return 1; +} + + static virDriver libxlDriver = { .no = VIR_DRV_LIBXL, .name = xenlight, @@ -3948,6 +3955,7 @@ static virDriver libxlDriver = { .domainIsUpdated = libxlDomainIsUpdated, /* 0.9.0 */ .domainEventRegisterAny = libxlDomainEventRegisterAny, /* 0.9.0 */ .domainEventDeregisterAny = libxlDomainEventDeregisterAny, /* 0.9.0 */ +.isAlive = libxlIsAlive, /* 0.9.7 */ }; static virStateDriver libxlStateDriver = { diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 6cf7203..28a7f9c 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -185,6 +185,12 @@ static int lxcIsEncrypted(virConnectPtr conn ATTRIBUTE_UNUSED) } +static int lxcIsAlive(virConnectPtr conn ATTRIBUTE_UNUSED) +{ +return 1; +} + + static char *lxcGetCapabilities(virConnectPtr conn) { lxc_driver_t *driver = conn-privateData; char *xml; @@ -2986,6 +2992,7 @@ static virDriver lxcDriver = { .domainEventRegisterAny = lxcDomainEventRegisterAny, /* 0.8.0 */ .domainEventDeregisterAny = lxcDomainEventDeregisterAny, /* 0.8.0 */ .domainOpenConsole = lxcDomainOpenConsole, /* 0.8.6 */ +.isAlive = lxcIsAlive, /* 0.9.7 */ }; static virStateDriver lxcStateDriver = { diff --git a/src/openvz/openvz_driver.c b/src/openvz/openvz_driver.c index 69ff444..8a4e6cf 100644 --- a/src/openvz/openvz_driver.c +++ b/src/openvz/openvz_driver.c @@ -1426,6 +1426,12 @@ static int openvzIsSecure(virConnectPtr conn ATTRIBUTE_UNUSED) { return 1; } +static int +openvzIsAlive(virConnectPtr conn ATTRIBUTE_UNUSED) +{ +return 1; +} + static char *openvzGetCapabilities(virConnectPtr conn) { struct openvz_driver *driver = conn-privateData; char *ret; @@ -1714,6 +1720,7 @@ static virDriver openvzDriver = { .domainIsActive = openvzDomainIsActive, /* 0.7.3 */ .domainIsPersistent = openvzDomainIsPersistent,
[libvirt] [PATCH v2 03/12] Introduce two public APIs for keepalive protocol
This introduces virConnectAllowKeepAlive and virConnectStartKeepAlive public APIs which can be used by a client connecting to remote server to indicate support for keepalive protocol. Both APIs are handled directly by remote driver and not transmitted over the wire to the server. --- include/libvirt/libvirt.h.in |5 ++ src/driver.h |9 src/libvirt.c| 107 ++ src/libvirt_internal.h | 10 +++- src/libvirt_public.syms |6 ++ 5 files changed, 135 insertions(+), 2 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 39155a6..6f61cc0 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -3210,6 +3210,11 @@ typedef struct _virTypedParameter virMemoryParameter; */ typedef virMemoryParameter *virMemoryParameterPtr; +int virConnectAllowKeepAlive(virConnectPtr conn); +int virConnectStartKeepAlive(virConnectPtr conn, + int interval, + unsigned int count); + #ifdef __cplusplus } #endif diff --git a/src/driver.h b/src/driver.h index 3792003..cd17d83 100644 --- a/src/driver.h +++ b/src/driver.h @@ -718,6 +718,13 @@ typedef int (*virDrvDomainBlockPull)(virDomainPtr dom, const char *path, unsigned long bandwidth, unsigned int flags); +typedef int +(*virDrvAllowKeepAlive)(virConnectPtr conn); + +typedef int +(*virDrvStartKeepAlive)(virConnectPtr conn, +int interval, +unsigned int count); /** * _virDriver: @@ -872,6 +879,8 @@ struct _virDriver { virDrvDomainGetBlockJobInfo domainGetBlockJobInfo; virDrvDomainBlockJobSetSpeed domainBlockJobSetSpeed; virDrvDomainBlockPull domainBlockPull; +virDrvAllowKeepAlive allowKeepAlive; +virDrvStartKeepAlive startKeepAlive; }; typedef int diff --git a/src/libvirt.c b/src/libvirt.c index 8f94b11..138f367 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -16590,3 +16590,110 @@ error: virDispatchError(dom-conn); return -1; } + +/** + * virConnectAllowKeepAlive: + * @conn: pointer to a hypervisor connection + * + * Tell remote party we support keepalive messages so the it can use them and + * we will respond to them. To actually start sending keepalive messages to a + * client needs to call virConnectStartKeepAlive(). + * + * Note: client has to implement and run event loop to be able to respond to + * asynchronous keepalive messages. If a client doesn't run event loop but + * still calls this API, every connection made may be automatically closed by + * remote party after a certain period of inactivity. + * + * Returns -1 on error, 0 on success, 1 when remote party doesn't support + * keepalive messages. + */ +int virConnectAllowKeepAlive(virConnectPtr conn) +{ +int ret = -1; + +VIR_DEBUG(conn=%p, conn); + +virResetLastError(); + +if (!VIR_IS_CONNECT(conn)) { +virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); +virDispatchError(NULL); +return -1; +} + +if (!VIR_DRV_SUPPORTS_FEATURE(conn-driver, conn, + VIR_DRV_FEATURE_PROGRAM_KEEPALIVE)) { +VIR_DEBUG(Remote party doesn't support keepalive messages); +return 1; +} + +if (conn-driver-allowKeepAlive) { +ret = conn-driver-allowKeepAlive(conn); +if (ret 0) +goto error; +return ret; +} + +virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: +virDispatchError(conn); +return -1; +} + +/** + * virConnectStartKeepAlive: + * @conn: pointer to a hypervisor connection + * @interval: number of seconds of inactivity before a keepalive message is sent + * @count: number of messages that can be sent in a row + * + * Start sending keepalive messages after interval second of inactivity and + * consider the connection to be broken when no response is received after + * count keepalive messages sent in a row. In other words, sending count + 1 + * keepalive message results in closing the connection. + * + * This API may be called only after calling virConnectAllowKeepAlive and + * checking it returned 0, which ensures remote party supports keepalive + * protocol. Failure to do so will be detected and reported as an error. + * + * Note: client has to implement and run event loop to be able to use keepalive + * messages. Failture to do so may result in connections being closed + * unexpectedly. + * + * Returns 0 on success, -1 on error. + */ +int virConnectStartKeepAlive(virConnectPtr conn, + int interval, + unsigned int count) +{ +int ret = -1; + +VIR_DEBUG(conn=%p, interval=%d, count=%u, conn, interval, count); + +virResetLastError(); + +if (!VIR_IS_CONNECT(conn)) { +virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); +
[libvirt] [PATCH v2 05/12] Add support for non-blocking calls in client RPC
When a client wants to send a keepalive message it needs to do so in a non-blocking way to avoid blocking its event loop. This patch adds dontBlock flag which says that the call should be processed without blocking. Such calls do not have a thread waiting for the result associated with them. This means, that sending such call fails if no thread is dispatching and writing to the socket would block. In case there is a thread waiting for its (normal) call to finish, sending non-blocking call just pushes it into the queue and lets the dispatching thread send it. The thread which has the buck tries to send all non-blocking calls in the queue in a best effort way---if sending them would block or there's an error on the socket, non-blocking calls are simply removed from the queue and discarded. --- src/rpc/virnetclient.c | 149 1 files changed, 113 insertions(+), 36 deletions(-) diff --git a/src/rpc/virnetclient.c b/src/rpc/virnetclient.c index 055361d..7ea9a27 100644 --- a/src/rpc/virnetclient.c +++ b/src/rpc/virnetclient.c @@ -55,6 +55,7 @@ struct _virNetClientCall { virNetMessagePtr msg; bool expectReply; +bool dontBlock; virCond cond; @@ -94,6 +95,11 @@ struct _virNetClient { }; +static int virNetClientSendInternal(virNetClientPtr client, +virNetMessagePtr msg, +bool expectReply, +bool dontBlock); + static void virNetClientLock(virNetClientPtr client) { virMutexLock(client-lock); @@ -848,6 +854,7 @@ static int virNetClientIOEventLoop(virNetClientPtr client, char ignore; sigset_t oldmask, blockedsigs; int timeout = -1; +bool discardNonBlocking; /* If we have existing SASL decoded data we * don't want to sleep in the poll(), just @@ -865,6 +872,11 @@ static int virNetClientIOEventLoop(virNetClientPtr client, fds[0].events |= POLLIN; if (tmp-mode == VIR_NET_CLIENT_MODE_WAIT_TX) fds[0].events |= POLLOUT; +/* We don't want to sleep in poll if any of the calls is + * non-blocking + */ +if (tmp-dontBlock) +timeout = 0; tmp = tmp-next; } @@ -937,35 +949,63 @@ static int virNetClientIOEventLoop(virNetClientPtr client, goto error; } -/* Iterate through waiting threads and if - * any are complete then tell 'em to wakeup +/* Iterate through waiting calls and + * - remove all completed nonblocking calls + * - remove all nonblocking calls in case poll() would block + * - remove all nonblocking calls if we got error from poll() + * - wake up threads waiting for calls that have been completed */ +discardNonBlocking = ret == 0 || + (fds[0].revents POLLHUP) || + (fds[0].revents POLLERR); tmp = client-waitDispatch; prev = NULL; while (tmp) { +virNetClientCallPtr next = tmp-next; + if (tmp != thiscall -tmp-mode == VIR_NET_CLIENT_MODE_COMPLETE) { +(tmp-mode == VIR_NET_CLIENT_MODE_COMPLETE || + (discardNonBlocking tmp-dontBlock))) { /* Take them out of the list */ if (prev) prev-next = tmp-next; else client-waitDispatch = tmp-next; -/* And wake them up - * ...they won't actually wakeup until - * we release our mutex a short while - * later... - */ -VIR_DEBUG(Waking up sleep %p %p, tmp, client-waitDispatch); -virCondSignal(tmp-cond); +if (tmp-dontBlock) { +/* tmp is a non-blocking call, no-one is waiting for it so + * we just free it here + */ +if (tmp-mode != VIR_NET_CLIENT_MODE_COMPLETE) { +VIR_DEBUG(Can't finish nonblocking call %p without + blocking or error, tmp); +} +virNetMessageFree(tmp-msg); +VIR_FREE(tmp); +} else { +/* And wake them up + * ...they won't actually wakeup until + * we release our mutex a short while + * later... + */ +VIR_DEBUG(Waking up sleep %p %p, + tmp, client-waitDispatch); +virCondSignal(tmp-cond); +} } else { prev = tmp; } -tmp = tmp-next; +tmp = next; } /*
[libvirt] [PATCH v2 11/12] qemu: Add support for keepalive messages during p2p migration
--- src/qemu/libvirtd_qemu.aug |2 ++ src/qemu/qemu.conf | 16 src/qemu/qemu_conf.c| 11 +++ src/qemu/qemu_conf.h|3 +++ src/qemu/qemu_migration.c | 10 ++ src/qemu/test_libvirtd_qemu.aug |6 ++ 6 files changed, 48 insertions(+), 0 deletions(-) diff --git a/src/qemu/libvirtd_qemu.aug b/src/qemu/libvirtd_qemu.aug index 6c145c7..ad34e42 100644 --- a/src/qemu/libvirtd_qemu.aug +++ b/src/qemu/libvirtd_qemu.aug @@ -52,6 +52,8 @@ module Libvirtd_qemu = | int_entry max_processes | str_entry lock_manager | int_entry max_queued + | int_entry keepalive_interval + | int_entry keepalive_count (* Each enty in the config is one of the following three ... *) let entry = vnc_entry diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index 4da5d5a..e623bc3 100644 --- a/src/qemu/qemu.conf +++ b/src/qemu/qemu.conf @@ -316,3 +316,19 @@ # Note, that job lock is per domain. # # max_queued = 0 + +### +# Keepalive protocol: +# This allows qemu driver to detect broken connections to remote +# libvirtd during peer-to-peer migration. A keepalive message is +# sent to the deamon after keepalive_interval seconds of inactivity +# to check if the deamon is still responding; keepalive_count is a +# maximum number of keepalive messages that are allowed to be sent +# to the deamon without getting any response before the connection +# is considered broken. In other words, the connection is +# automatically closed approximately after +# keepalive_interval * (keepalive_count + 1) seconds since the last +# message received from the deamon. +# +#keepalive_interval = 5 +#keepalive_count = 5 diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index d1bf075..19f24b1 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -118,6 +118,9 @@ int qemudLoadDriverConfig(struct qemud_driver *driver, virLockManagerPluginNew(nop, NULL, 0))) return -1; +driver-keepAliveInterval = 5; +driver-keepAliveCount = 5; + /* Just check the file is readable before opening it, otherwise * libvirt emits an error. */ @@ -462,6 +465,14 @@ int qemudLoadDriverConfig(struct qemud_driver *driver, CHECK_TYPE(max_queued, VIR_CONF_LONG); if (p) driver-max_queued = p-l; +p = virConfGetValue(conf, keepalive_interval); +CHECK_TYPE(keepalive_interval, VIR_CONF_LONG); +if (p) driver-keepAliveInterval = p-l; + +p = virConfGetValue(conf, keepalive_count); +CHECK_TYPE(keepalive_count, VIR_CONF_LONG); +if (p) driver-keepAliveCount = p-l; + virConfFree (conf); return 0; } diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index e8b92a4..70c3fda 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -138,6 +138,9 @@ struct qemud_driver { * of guests which will be automatically killed * when the virConnectPtr is closed*/ virHashTablePtr autodestroy; + +int keepAliveInterval; +unsigned int keepAliveCount; }; typedef struct _qemuDomainCmdlineDef qemuDomainCmdlineDef; diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 0a5a13d..301d8ba 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -2173,6 +2173,7 @@ static int doPeer2PeerMigrate(struct qemud_driver *driver, virConnectPtr dconn = NULL; bool p2p; virErrorPtr orig_err = NULL; +int rc; VIR_DEBUG(driver=%p, sconn=%p, vm=%p, xmlin=%s, dconnuri=%s, uri=%s, flags=%lx, dname=%s, resource=%lu, @@ -2193,6 +2194,15 @@ static int doPeer2PeerMigrate(struct qemud_driver *driver, } qemuDomainObjEnterRemoteWithDriver(driver, vm); +if ((rc = virConnectAllowKeepAlive(dconn)) == 0) { +rc = virConnectStartKeepAlive(dconn, driver-keepAliveInterval, + driver-keepAliveCount); +} +qemuDomainObjExitRemoteWithDriver(driver, vm); +if (rc 0) +goto cleanup; + +qemuDomainObjEnterRemoteWithDriver(driver, vm); p2p = VIR_DRV_SUPPORTS_FEATURE(dconn-driver, dconn, VIR_DRV_FEATURE_MIGRATION_P2P); /* v3proto reflects whether the caller used Perform3, but with diff --git a/src/qemu/test_libvirtd_qemu.aug b/src/qemu/test_libvirtd_qemu.aug index b1f9114..f7476ae 100644 --- a/src/qemu/test_libvirtd_qemu.aug +++ b/src/qemu/test_libvirtd_qemu.aug @@ -115,6 +115,9 @@ vnc_auto_unix_socket = 1 max_processes = 12345 lock_manager = \fcntl\ + +keepalive_interval = 1 +keepalive_count = 42 test Libvirtd_qemu.lns get conf = @@ -240,3 +243,6 @@ lock_manager = \fcntl\ { max_processes = 12345 } { #empty } { lock_manager = fcntl } +{ #empty } +{ keepalive_interval = 1 } +{ keepalive_count = 42 } -- 1.7.6.1 -- libvir-list mailing list
[libvirt] [PATCH v2 07/12] Implement keepalive protocol in remote driver
--- src/remote/remote_driver.c | 30 + src/rpc/virnetclient.c | 102 ++- src/rpc/virnetclient.h |5 ++ 3 files changed, 134 insertions(+), 3 deletions(-) diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 1217d94..e218c40 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -4082,6 +4082,34 @@ done: } +static int +remoteAllowKeepAlive(virConnectPtr conn) +{ +struct private_data *priv = conn-privateData; +int ret = -1; + +remoteDriverLock(priv); +ret = virNetClientKeepAliveAdvertise(priv-client); +remoteDriverUnlock(priv); + +return ret; +} + + +static int +remoteStartKeepAlive(virConnectPtr conn, int interval, unsigned int count) +{ +struct private_data *priv = conn-privateData; +int ret = -1; + +remoteDriverLock(priv); +ret = virNetClientKeepAliveStart(priv-client, interval, count); +remoteDriverUnlock(priv); + +return ret; +} + + #include remote_client_bodies.h #include qemu_client_bodies.h @@ -4430,6 +4458,8 @@ static virDriver remote_driver = { .domainGetBlockJobInfo = remoteDomainGetBlockJobInfo, /* 0.9.4 */ .domainBlockJobSetSpeed = remoteDomainBlockJobSetSpeed, /* 0.9.4 */ .domainBlockPull = remoteDomainBlockPull, /* 0.9.4 */ +.allowKeepAlive = remoteAllowKeepAlive, /* 0.9.7 */ +.startKeepAlive = remoteStartKeepAlive, /* 0.9.7 */ }; static virNetworkDriver network_driver = { diff --git a/src/rpc/virnetclient.c b/src/rpc/virnetclient.c index 45b0edb..dee5059 100644 --- a/src/rpc/virnetclient.c +++ b/src/rpc/virnetclient.c @@ -29,6 +29,7 @@ #include virnetclient.h #include virnetsocket.h +#include virkeepalive.h #include memory.h #include threads.h #include virfile.h @@ -93,11 +94,12 @@ struct _virNetClient { size_t nstreams; virNetClientStreamPtr *streams; +virKeepAlivePtr keepalive; bool wantClose; }; -void virNetClientRequestClose(virNetClientPtr client); +static void virNetClientRequestClose(virNetClientPtr client); static int virNetClientSendInternal(virNetClientPtr client, virNetMessagePtr msg, @@ -127,11 +129,71 @@ static void virNetClientEventFree(void *opaque) virNetClientFree(client); } +int +virNetClientKeepAliveAdvertise(virNetClientPtr client) +{ +virKeepAlivePtr ka; +int ret = -1; + +virNetClientLock(client); +if ((ka = client-keepalive)) +virKeepAliveRef(ka); +virNetClientUnlock(client); + +if (ka) { +ret = virKeepAliveAdvertise(ka); +virKeepAliveFree(ka); +} else { +virNetError(VIR_ERR_INTERNAL_ERROR, %s, +_(the caller doesn't support keepalive protocol; + perhaps it's missing event loop implementation)); +} + +return ret; +} + +int +virNetClientKeepAliveStart(virNetClientPtr client, + int interval, + unsigned int count) +{ +int ret = -1; + +virNetClientLock(client); + +if (!client-keepalive) { +virNetError(VIR_ERR_INTERNAL_ERROR, %s, +_(the caller doesn't support keepalive protocol; + perhaps it's missing event loop implementation)); +goto cleanup; +} + +ret = virKeepAliveStart(client-keepalive, interval, count); + +cleanup: +virNetClientUnlock(client); +return ret; +} + +static void +virNetClientKeepAliveDeadCB(void *opaque) +{ +virNetClientRequestClose(opaque); +} + +static int +virNetClientKeepAliveSendCB(void *opaque, +virNetMessagePtr msg) +{ +return virNetClientSendInternal(opaque, msg, false, true); +} + static virNetClientPtr virNetClientNew(virNetSocketPtr sock, const char *hostname) { virNetClientPtr client = NULL; int wakeupFD[2] = { -1, -1 }; +virKeepAlivePtr ka = NULL; if (pipe2(wakeupFD, O_CLOEXEC) 0) { virReportSystemError(errno, %s, @@ -164,9 +226,21 @@ static virNetClientPtr virNetClientNew(virNetSocketPtr sock, client, virNetClientEventFree) 0) { client-refs--; -VIR_DEBUG(Failed to add event watch, disabling events); +VIR_DEBUG(Failed to add event watch, disabling events and support for + keepalive messages); +} else { +/* Keepalive protocol consists of async messages so it can only be used + * if the client supports them */ +if (!(ka = virKeepAliveNew(-1, 0, client, + virNetClientKeepAliveSendCB, + virNetClientKeepAliveDeadCB, + virNetClientEventFree))) +goto error; +/* keepalive object has a reference to client */ +client-refs++; } +client-keepalive = ka;
[libvirt] [PATCH v2 08/12] Introduce virConnectIsAlive API
This API can be used to check if the socket associated with virConnectPtr is still open or it was closed (probably because keepalive protocol timed out). If there the connection is local (i.e., no socket is associated with the connection, it is trivially always alive. --- include/libvirt/libvirt.h.in |1 + src/driver.h |3 +++ src/libvirt.c| 36 src/libvirt_public.syms |1 + 4 files changed, 41 insertions(+), 0 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 6f61cc0..3767582 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2546,6 +2546,7 @@ int virInterfaceIsActive(virInterfacePtr iface); int virConnectIsEncrypted(virConnectPtr conn); int virConnectIsSecure(virConnectPtr conn); +int virConnectIsAlive(virConnectPtr conn); /* * CPU specification API diff --git a/src/driver.h b/src/driver.h index cd17d83..8c01690 100644 --- a/src/driver.h +++ b/src/driver.h @@ -506,6 +506,8 @@ typedef int typedef int (*virDrvConnectIsSecure)(virConnectPtr conn); typedef int +(*virDrvConnectIsAlive)(virConnectPtr conn); +typedef int (*virDrvDomainIsActive)(virDomainPtr dom); typedef int (*virDrvDomainIsPersistent)(virDomainPtr dom); @@ -881,6 +883,7 @@ struct _virDriver { virDrvDomainBlockPull domainBlockPull; virDrvAllowKeepAlive allowKeepAlive; virDrvStartKeepAlive startKeepAlive; +virDrvConnectIsAlive isAlive; }; typedef int diff --git a/src/libvirt.c b/src/libvirt.c index 138f367..19ac4b5 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -16697,3 +16697,39 @@ error: virDispatchError(conn); return -1; } + +/** + * virConnectIsAlive: + * @conn: pointer to the connection object + * + * Determine if the connection to the hypervisor is still alive + * + * A connection will be classed as alive if it is either local, or running + * over a channel (TCP or UNIX socket) which is not closed. + * + * Returns 1 if alive, 0 if dead, -1 on error + */ +int virConnectIsAlive(virConnectPtr conn) +{ +VIR_DEBUG(conn=%p, conn); + +virResetLastError(); + +if (!VIR_IS_CONNECT(conn)) { +virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); +virDispatchError(NULL); +return -1; +} +if (conn-driver-isAlive) { +int ret; +ret = conn-driver-isAlive(conn); +if (ret 0) +goto error; +return ret; +} + +virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: +virDispatchError(conn); +return -1; +} diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index f7441d7..e60f66d 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -492,6 +492,7 @@ LIBVIRT_0.9.5 { LIBVIRT_0.9.7 { global: virConnectAllowKeepAlive; +virConnectIsAlive; virConnectStartKeepAlive; } LIBVIRT_0.9.5; -- 1.7.6.1 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH v2 10/12] Add keepalive support into domain-events examples
--- Notes: Version 2: - automatically exit when a connection is closed because of keepalive timeout examples/domain-events/events-c/event-test.c | 13 - examples/domain-events/events-python/event-test.py |5 - 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/examples/domain-events/events-c/event-test.c b/examples/domain-events/events-c/event-test.c index 6a3ed26..e310eb4 100644 --- a/examples/domain-events/events-c/event-test.c +++ b/examples/domain-events/events-c/event-test.c @@ -390,7 +390,18 @@ int main(int argc, char **argv) (callback5ret != -1) (callback6ret != -1) (callback7ret != -1)) { -while (run) { +int rc; + +if ((rc = virConnectAllowKeepAlive(dconn)) == 0) +rc = virConnectStartKeepAlive(dconn, 5, 3); +if (rc 0) { +virErrorPtr err = virGetLastError(); +fprintf(stderr, Failed to start keepalive protocol: %s\n, +err err-message ? err-message : Unknown error); +run = 0; +} + +while (run virConnectIsAlive(dconn) == 1) { if (virEventRunDefaultImpl() 0) { virErrorPtr err = virGetLastError(); fprintf(stderr, Failed to run event loop: %s\n, diff --git a/examples/domain-events/events-python/event-test.py b/examples/domain-events/events-python/event-test.py index 76fda2b..bc854b8 100644 --- a/examples/domain-events/events-python/event-test.py +++ b/examples/domain-events/events-python/event-test.py @@ -518,11 +518,14 @@ def main(): vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_WATCHDOG, myDomainEventWatchdogCallback, None) vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_GRAPHICS, myDomainEventGraphicsCallback, None) +vc.allowKeepAlive() +vc.startKeepAlive(5, 3) + # The rest of your app would go here normally, but for sake # of demo we'll just go to sleep. The other option is to # run the event loop in your main thread if your app is # totally event based. -while 1: +while vc.isAlive() == 1: time.sleep(1) -- 1.7.6.1 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH] qemu: Allow domain reboot after core dump
yes, the patch can work, when I execute virsh dump --reboot domain file and the type of file is: QEMU's suspend to disk image but the guest does not reboot. The guest does nothing. but I try virsh shutdown domain, the guest Ubuntu OS pop up a dialog box with four options: Shut Down, Restart, Suspend and HIbernate. ??: [libvirt] [PATCH] qemu: Allow domain reboot after core dump ??: Tue, 20 Sep 2011 13:34:27 +0200 ???:Michal Privoznik mpriv...@redhat.com ???:libvir-list@redhat.com This patch introduces possibility to reboot domain after core dump finishes. The new flag VIR_DUMP_REBOOT was added to virDomainCoreDumpFlags. The new functionality is accessible via virsh too: virsh dump --rebootdomain --- include/libvirt/libvirt.h.in |1 + src/qemu/qemu_driver.c |8 +++- tools/virsh.c|3 +++ 3 files changed, 11 insertions(+), 1 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 39155a6..8c41f5a 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -748,6 +748,7 @@ typedef enum { VIR_DUMP_CRASH= (1 0), /* crash after dump */ VIR_DUMP_LIVE = (1 1), /* live dump */ VIR_DUMP_BYPASS_CACHE = (1 2), /* avoid file system cache pollution */ +VIR_DUMP_REBOOT = (1 3), /* reboot domain after dump finishes */ } virDomainCoreDumpFlags; /* Domain migration flags. */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index e2f428f..22576a8 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -3104,7 +3104,8 @@ static int qemudDomainCoreDump(virDomainPtr dom, int ret = -1; virDomainEventPtr event = NULL; -virCheckFlags(VIR_DUMP_LIVE | VIR_DUMP_CRASH | VIR_DUMP_BYPASS_CACHE, -1); +virCheckFlags(VIR_DUMP_LIVE | VIR_DUMP_CRASH | + VIR_DUMP_BYPASS_CACHE | VIR_DUMP_REBOOT, -1); qemuDriverLock(driver); vm = virDomainFindByUUID(driver-domains, dom-uuid); @@ -3189,6 +3190,11 @@ cleanup: if (event) qemuDomainEventQueue(driver, event); qemuDriverUnlock(driver); + +if ((ret == 0) (flags VIR_DUMP_REBOOT)) { +qemuDomainReboot(dom, 0); +} + return ret; } diff --git a/tools/virsh.c b/tools/virsh.c index d575425..74f6a79 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -2840,6 +2840,7 @@ static const vshCmdOptDef opts_dump[] = { {crash, VSH_OT_BOOL, 0, N_(crash the domain after core dump)}, {bypass-cache, VSH_OT_BOOL, 0, N_(avoid file system cache when saving)}, +{reboot, VSH_OT_BOOL, 0, N_(reboot the domain after core dump)}, {domain, VSH_OT_DATA, VSH_OFLAG_REQ, N_(domain name, id or uuid)}, {file, VSH_OT_DATA, VSH_OFLAG_REQ, N_(where to dump the core)}, {NULL, 0, 0, NULL} @@ -2869,6 +2870,8 @@ cmdDump(vshControl *ctl, const vshCmd *cmd) flags |= VIR_DUMP_CRASH; if (vshCommandOptBool(cmd, bypass-cache)) flags |= VIR_DUMP_BYPASS_CACHE; +if (vshCommandOptBool(cmd, reboot)) +flags |= VIR_DUMP_REBOOT; if (virDomainCoreDump(dom, to, flags) 0) { vshError(ctl, _(Failed to core dump domain %s to %s), name, to); -- 1.7.3.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH] storage: Do not use comma as seperator for lvs output
于 2011年09月22日 17:08, Osier Yang 写道: 于 2011年09月22日 16:41, Eli 写道: hi Osier: 于 2011年09月22日 15:08, Osier Yang 写道: 于 2011年09月22日 14:31, Eli 写道: hi Osier : 于 2011年09月21日 17:07, Osier Yang 写道: * src/storage/storage_backend_logical.c: If a logical vol is created with multiple stripes. (e.g. --stripes 3), the device field of lvs output will have multiple fileds which are seperated by comma. It means the RE we write in the codes will not work well anymore. E.g. (lvs output for a stripped vol, uses # as seperator here): test_stripes##fSLSZH-zAS2-yAIb-n4mV-Al9u-HA3V-oo9K1B#\ /dev/sdc1(10240),/dev/sdd1(0)#42949672960#4194304 The RE we uses: const char *regexes[] = { ^\\s*(\\S+),(\\S*),(\\S+),(\\S+)\\((\\S+)\\),(\\S+),([0-9]+),?\\s*$ }; This patch changes the seperator into # to fix the problem. Related RHBZ: https://bugzilla.redhat.com/show_bug.cgi?id=727474 --- src/storage/storage_backend_logical.c |7 --- 1 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/storage/storage_backend_logical.c b/src/storage/storage_backend_logical.c index 4f42047..45f77ad 100644 --- a/src/storage/storage_backend_logical.c +++ b/src/storage/storage_backend_logical.c @@ -187,19 +187,20 @@ virStorageBackendLogicalFindLVs(virStoragePoolObjPtr pool, * * NB can be multiple rows per volume if they have many extents * - * NB lvs from some distros (e.g. SLES10 SP2) outputs trailing , on each line + * NB lvs from some distros (e.g. SLES10 SP2) outputs trailing + * @separator on each line * * NB Encrypted logical volumes can print ':' in their name, so it is *not a suitable separator (rhbz 470693). */ const char *regexes[] = { - ^\\s*(\\S+),(\\S*),(\\S+),(\\S+)\\((\\S+)\\),(\\S+),([0-9]+),?\\s*$ + ^\\s*(\\S+)#(\\S*)#(\\S+)#(\\S+)\\((\\S+)\\)#(\\S+)#([0-9]+)#?\\s*$ }; int vars[] = { 7 }; const char *prog[] = { -LVS, --separator, ,, --noheadings, --units, b, +LVS, --separator, #, --noheadings, --units, b, --unbuffered, --nosuffix, --options, lv_name,origin,uuid,devices,seg_size,vg_extent_size, pool-def-source.name, NULL I reproduced the bug : [root@localhost bin]# ./virsh -d 5 pool-create-as vg_ssd logical --target /dev/vg_ssd error: Failed to create pool vg_ssd error: cannot open volume '/dev/vg_ssd/test_stripes,': No such file or directory and then I tested this patch , it seems work well. [root@localhost bin]# ./virsh -d 5 pool-create-as vg_ssd logical --target /dev/vg_ssd Pool vg_ssd created [root@localhost bin]# ./virsh pool-info vg_ssd Name: vg_ssd UUID: c45cc84e-7879-cc15-ee78-2d2dda6b531d State: running Persistent: no Autostart: no Capacity: 200.00 MB Allocation: 152.00 MB Available: 48.00 MB Thanks for the testing, Eli, Could you also check what's the vol XML? I want to confirm if the extents in sourcedevice/device/sourcedisplays well, though it looks to me there is no path element defined for extents in the storage vol schema yet. 1. my vol XML is like this: virsh # vol-dumpxml test_stripes --pool vg_ssd volume nametest_stripes/name key1pc6Gf-1hn2-WGnw-ASKt-uX6w-xUed-qERq50/key *source device path='/dev/sdb(0),/dev/sdc' * do you mean the xml should like this : device path='/dev/sdb(0)' extent start='0' end='xxx'/ /device device path='/dev/sdc' extent start='xxx' end='yyy'/ /device This is expected, and is what's Daniel Berrange worried about. *extent start='0' end='159383552'/ /device /source* Thanks Osier -- best regards eli -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH v3] qemu: Allow domain reboot after core dump
This patch introduces possibility to reboot domain after core dump finishes. The new flag VIR_DUMP_REBOOT was added to virDomainCoreDumpFlags. The new functionality is accessible via virsh too: virsh dump --reboot domain --- diff to v2: -issue reset command instead of qemuDomainReboot include/libvirt/libvirt.h.in |1 + src/qemu/qemu_driver.c | 54 +++-- tools/virsh.c|3 ++ 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 39155a6..8c41f5a 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -748,6 +748,7 @@ typedef enum { VIR_DUMP_CRASH= (1 0), /* crash after dump */ VIR_DUMP_LIVE = (1 1), /* live dump */ VIR_DUMP_BYPASS_CACHE = (1 2), /* avoid file system cache pollution */ +VIR_DUMP_REBOOT = (1 3), /* reboot domain after dump finishes */ } virDomainCoreDumpFlags; /* Domain migration flags. */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0d0bea2..3e05e14 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2860,6 +2860,47 @@ getCompressionType(struct qemud_driver *driver) return compress; } +static int +doSystemReset(struct qemud_driver *driver, + virDomainObjPtr *vm) +{ +int ret = -1; +qemuDomainObjPrivatePtr priv; + +/* Okay, this should never happen, but doesn't hurt to check. */ +if (!driver || !vm || !*vm) { +qemuReportError(VIR_ERR_INTERNAL_ERROR, %s, +_(invalid argument supplied)); +goto cleanup; +} + +priv = (*vm)-privateData; + +if (qemuDomainObjBeginJob(driver, *vm, QEMU_JOB_MODIFY) 0) +goto cleanup; + +if (!virDomainObjIsActive(*vm)) { +qemuReportError(VIR_ERR_INTERNAL_ERROR, %s, +_(guest unexpectedly quit)); +goto endjob; +} + +qemuDomainObjEnterMonitorWithDriver(driver, *vm); +if (qemuMonitorSystemReset(priv-mon) 0) { +qemuDomainObjExitMonitorWithDriver(driver, *vm); +goto endjob; +} +qemuDomainObjExitMonitorWithDriver(driver, *vm); + +ret = 0; + +endjob: +if (qemuDomainObjEndJob(driver, *vm) == 0) +*vm = NULL; +cleanup: +return ret; +} + static int qemudDomainCoreDump(virDomainPtr dom, const char *path, unsigned int flags) @@ -2870,7 +2911,8 @@ static int qemudDomainCoreDump(virDomainPtr dom, int ret = -1; virDomainEventPtr event = NULL; -virCheckFlags(VIR_DUMP_LIVE | VIR_DUMP_CRASH | VIR_DUMP_BYPASS_CACHE, -1); +virCheckFlags(VIR_DUMP_LIVE | VIR_DUMP_CRASH | + VIR_DUMP_BYPASS_CACHE | VIR_DUMP_REBOOT, -1); qemuDriverLock(driver); vm = virDomainFindByUUID(driver-domains, dom-uuid); @@ -2949,11 +2991,17 @@ endjob: } cleanup: -if (vm) -virDomainObjUnlock(vm); if (event) qemuDomainEventQueue(driver, event); + +if ((ret == 0) (flags VIR_DUMP_REBOOT) vm) { +ret = doSystemReset(driver, vm); +} + +if (vm) +virDomainObjUnlock(vm); qemuDriverUnlock(driver); + return ret; } diff --git a/tools/virsh.c b/tools/virsh.c index e5ea9d7..bdff33c 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -2899,6 +2899,7 @@ static const vshCmdOptDef opts_dump[] = { {crash, VSH_OT_BOOL, 0, N_(crash the domain after core dump)}, {bypass-cache, VSH_OT_BOOL, 0, N_(avoid file system cache when saving)}, +{reboot, VSH_OT_BOOL, 0, N_(reboot the domain after core dump)}, {domain, VSH_OT_DATA, VSH_OFLAG_REQ, N_(domain name, id or uuid)}, {file, VSH_OT_DATA, VSH_OFLAG_REQ, N_(where to dump the core)}, {NULL, 0, 0, NULL} @@ -2928,6 +2929,8 @@ cmdDump(vshControl *ctl, const vshCmd *cmd) flags |= VIR_DUMP_CRASH; if (vshCommandOptBool(cmd, bypass-cache)) flags |= VIR_DUMP_BYPASS_CACHE; +if (vshCommandOptBool(cmd, reboot)) +flags |= VIR_DUMP_REBOOT; if (virDomainCoreDump(dom, to, flags) 0) { vshError(ctl, _(Failed to core dump domain %s to %s), name, to); -- 1.7.3.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH] selinux: Correctly report warning if virt_use_nfs not set
On 22.09.2011 18:56, Laine Stump wrote: On 09/22/2011 05:48 AM, Michal Privoznik wrote: Previous patch c9b37fee tried to deal with virt_use_nfs. But setfilecon() returns EOPNOTSUPP on NFS so we need to move the warning to else branch. I have a vague memory of the error code of something like this changing from some other error on an older version of RHEL to EOPNOTSUPP on newer version. It may have been for something else, but may be worth checking out to make sure this patch gives the desired results with, e.g. RHEL5 and RHEL6.0 as well as 6.2 and Fedora. Thanks. Pushed with this squashed in: diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index 9a9a305..19ca801 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -419,7 +419,7 @@ SELinuxSetFilecon(const char *path, char *tcon) * The user hopefully set one of the necessary SELinux * virt_use_{nfs,usb,pci} boolean tunables to allow it... */ -if (setfilecon_errno != EOPNOTSUPP) { +if (setfilecon_errno != EOPNOTSUPP setfilecon_errno != ENOTSUP) { virReportSystemError(setfilecon_errno, _(unable to set security context '%s' on '%s'), tcon, path); -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] 1 of 85 tests failed
hello send you result. /usr/bin/make check-TESTSmake[5]: Entering directory `/root/libvirt/libvirt-0.8.8/gnulib/tests'PASS: test-alignofPASS: test-alloca-optPASS: test-areadlinkPASS: test-arpa_inetPASS: test-base64PASS: test-binary-io.shPASS: test-c-ctypePASS: test-canonicalize-lgplPASS: test-chownPASS: test-count-one-bitsPASS: test-md5PASS: test-environPASS: test-errnoPASS: test-fcntl-hPASS: test-fseeko.shPASS: test-fseeko2.shPASS: test-funcPASS: test-getaddrinfoPASS: test-getdelimPASS: test-getgroupsPASS: test-gethostnamePASS: test-getlinePASS: test-gettimeofdayPASS: test-ignore-valuePASS: test-inet_ntopPASS: test-inet_ptonStarting test_lock ... OKStarting test_rwlock ... OKStarting test_recursive_lock ... OKStarting test_once ... OKPASS: test-lockPASS: test-lseek.shPASS: test-lstatPASS: test-mallocaPASS: test-memchrPASS: test-netdbPASS: test-netinet_intest-open.h:60: assertion failed/bin/bash: line 5: 9235 Aborted EXEEXT='' srcdir='.' abs_aux_dir='/root/libvirt/libvirt-0.8.8/build-aux' MAKE='/usr/bin/make' ${dir}$tstFAIL: test-openPASS: test-perror.shPASS: test-pipePASS: test-poll-hUnconnected socket test... passedConnected sockets test... passedGeneral socket test with fork... passedPipe test... passedPASS: test-pollPASS: test-random_rPASS: test-rawmemchrPASS: test-readlinkPASS: test-realloc-gnuPASS: test-schedUnconnected socket test... passedConnected sockets test... passedGeneral socket test with fork... passedPipe test... passedPASS: test-selectPASS: test-select-in.shPASS: test-select-out.shPASS: test-sigactionPASS: test-signalPASS: test-sigpipe.shPASS: test-sleepPASS: test-snprintfPASS: test-socketsPASS: test-statPASS: test-stat-timePASS: test-stdboolPASS: test-stddefPASS: test-stdintPASS: test-stdioPASS: test-stdlibPASS: test-strchrnulPASS: test-strerrorPASS: test-strerror_rPASS: test-stringPASS: test-stringsPASS: test-strnlenPASS: test-symlinkPASS: test-sys_ioctlPASS: test-sys_selectPASS: test-sys_socketPASS: test-sys_statPASS: test-sys_timePASS: test-sys_utsnamePASS: test-sys_waitPASS: test-termiosPASS: test-timePASS: test-unamePASS: test-unistdPASS: test-unsetenvPASS: test-usleepPASS: test-vasnprintfPASS: test-vasprintftest-vc-list-files-git.sh: skipped test: git not found in PATHSKIP: test-vc-list-files-git.shtest-vc-list-files-cvs.sh: skipped test: cvs not found in PATHSKIP: test-vc-list-files-cvs.shPASS: test-verifyPASS: test-verify.shPASS: test-wcharPASS: test-xalloc-die.sh===1 of 85 tests failed(2 tests were not run)Please report to libvir-list@redhat.com===make[5]: *** [check-TESTS] Error 1make[5]: Leaving directory `/root/libvirt/libvirt-0.8.8/gnulib/tests'make[4]: *** [check-am] Error 2make[4]: Leaving directory `/root/libvirt/libvirt-0.8.8/gnulib/tests'make[3]: *** [check-recursive] Error 1make[3]: Leaving directory `/root/libvirt/libvirt-0.8.8/gnulib/tests'make[2]: *** [check] Error 2make[2]: Leaving directory `/root/libvirt/libvirt-0.8.8/gnulib/tests'make[1]: *** [check-recursive] Error 1make[1]: Leaving directory `/root/libvirt/libvirt-0.8.8'make: *** [debian/stamp-makefile-check] Error 2dpkg-buildpackage: error: debian/rules build gave error exit status 2root@ubuntu:~/libvirt/libvirt-0.8.8# /bin/bash: line 5: 9235 Aborted EXEEXT='' srcdir='.' abs_aux_dir='/root/libvirt/libvirt-0.8.8/build-aux' MAKE='/usr/bin/make' ${dir}$tst-bash: /bin/bash:: No such file or directoryroot@ubuntu:~/libvirt/libvirt-0.8.8# test-open.h:60: assertion failedtest-open.h:60:: command not foundroot@ubuntu:~/libvirt/libvirt-0.8.8# -- Tavs bezmaksas pasts Inbox.lv -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] 1 of 85 tests failed
On 09/22/2011 11:54 PM, vi...@inbox.lv wrote: hello send you result. /usr/bin/make check-TESTS make[5]: Entering directory `/root/libvirt/libvirt-0.8.8/gnulib/tests' Thanks for the report. 0.8.8 is rather old. Would you mind repeating your test on 0.9.6? test-open.h:60: assertion failed /bin/bash: line 5: 9235 Aborted EXEEXT='' srcdir='.' abs_aux_dir='/root/libvirt/libvirt-0.8.8/build-aux' MAKE='/usr/bin/make' ${dir}$tst FAIL: test-open Also, what platform was this on? That particular line of the test appears to be in a portion of the file testing that /dev/null returns EOF on read attempts; if that's not working, your system likely has much bigger problems. I doubt that this is an actual bug in open(), so much as an issue in your setup. -- Eric Blake ebl...@redhat.com+1-801-349-2682 Libvirt virtualization library http://libvirt.org -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCHv3] Add unsafe cache mode support for disk driver
On 09/22/2011 01:33 PM, Oskari Saarenmaa wrote: QEMU 0.13 introduced cache=unsafe for -drive, this patch exposes it in the libvirt layer. * Introduced a new QEMU capability flag ($prefix_CACHE_UNSAFE), as even if $prefix_CACHE_V2 is set, we can't known if unsafe s/known/know/ is supported. * Improved the reliability of qemu cache type detection. --- Updated patch based on Eric Blake's comments and rebased it to c4111bd0 +++ b/docs/formatdomain.html.in @@ -996,10 +996,15 @@ li The optionalcodecache/code attribute controls the cache mechanism, possible values are default, none, -writethrough, writeback, and directsync. directsync -is like writethrough, but it bypasses the host page -cache. -span class=sinceSince 0.6.0/span +writethrough, writeback, directsync (like +writethrough, but it bypasses the host page cache) and +unsafe (host may cache all disk io and sync requests from s/io and/io, and/ @@ -912,12 +914,16 @@ qemuCapsComputeCmdFlags(const char *help, else if (strstr(help, -domid)) qemuCapsSet(flags, QEMU_CAPS_DOMID); if (strstr(help, -drive)) { +const char *cache = strstr(help, cache=); + qemuCapsSet(flags, QEMU_CAPS_DRIVE); -if (strstr(help, cache=) -!strstr(help, cache=on|off)) { -qemuCapsSet(flags, QEMU_CAPS_DRIVE_CACHE_V2); -if (strstr(help, directsync)) +if (cache (p = strchr(cache, ']'))) { +if (memmem(cache, p - cache, on|off, sizeof(on|off) - 1) == NULL) +qemuCapsSet(flags, QEMU_CAPS_DRIVE_CACHE_V2); +if (memmem(cache, p - cache, directsync, sizeof(directsync) - 1)) qemuCapsSet(flags, QEMU_CAPS_DRIVE_CACHE_DIRECTSYNC); +if (memmem(cache, p - cache, unsafe, sizeof(unsafe) - 1)) +qemuCapsSet(flags, QEMU_CAPS_DRIVE_CACHE_UNSAFE); Nice! And indeed, unsafe but not directsync is present in qemu 0.14.0, so this does need another capability bit. ACK and pushed. -- Eric Blake ebl...@redhat.com+1-801-349-2682 Libvirt virtualization library http://libvirt.org -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH 1/2] Modify generic ethernet interface so it will work when sVirt is enabled with qemu
On 09/20/2011 05:17 PM, Tyler Coumbes wrote: Add a generic utility library for working with the TUN driver (/dev/net/tun). Thanks for posting these patches. Next time, can you convince your mailer to send the series as a single thread (that is, make both 1/2 and 2/2 have In-Reply-To set to point to 0/2)? 'git send-email' makes this relatively easy. Also, sending patches with a diffstat makes it easier to see at a glance how much the patch involves. --- diff --git a/src/Makefile.am b/src/Makefile.am index 738ee91..ddd1b77 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -88,7 +88,8 @@ UTIL_SOURCES = \ util/xml.c util/xml.h \ util/virterror.c util/virterror_internal.h \ util/virkeycode.c util/virkeycode.h \ - util/virkeymaps.h + util/virkeymaps.h \ + util/tunctl.c util/tunctl.h Being a new file, it would probably be better to go with a name starting with 'vir', as in virtunctl (that is, this file represents libvirt's wrappers around TUN, and not a generic TUN manipulation library). EXTRA_DIST += $(srcdir)/util/virkeymaps.h $(srcdir)/util/keymaps.csv \ $(srcdir)/util/virkeycode-mapgen.py @@ -1182,6 +1183,8 @@ if WITH_NETWORK USED_SYM_FILES += libvirt_network.syms endif +USED_SYM_FILES += libvirt_tunctl.syms If [vir]tunctl is compiled unconditionally, then we don't need a new .syms file - just stick the new symbols in libvirt_private.syms. On the other hand, since TUN tends to be a Linux-specific concept, I think you need to compile this code conditionally, at which point maybe the existing libvirt_linux.syms is a better fit. + EXTRA_DIST += \ libvirt_public.syms \ libvirt_private.syms\ And if all else fails and you really do need to create a new .syms file, then it needs to be listed in EXTRA_DIST. diff --git a/src/libvirt_tunctl.syms b/src/libvirt_tunctl.syms new file mode 100644 index 000..d1e00bb --- /dev/null +++ b/src/libvirt_tunctl.syms @@ -0,0 +1,5 @@ +#tunctl.h +createTap; +delTap; +tapSetInterfaceUp; +tapSetInterfaceMac; This naming isn't namespace clean. It might be better to go with: virTunCreateTap virTunDeleteTap virTunSetInterfaceUp virTunSetInterfaceMac Oh, and if all of the functions are named tap instead of tun, then maybe naming the file virtap instead of virtun would make more sense. Which is it, tap or tun? diff --git a/src/util/tunctl.c b/src/util/tunctl.c new file mode 100644 index 000..e758e6d --- /dev/null +++ b/src/util/tunctl.c @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2007, 2009, 2011 Red Hat, Inc. It's okay to list earlier years if your file was copying substantial layout from another file with those years, but if the file really is from scratch, it may be okay to list just 2011. What file were you copying from, + * + * Authors: + */ Feel free to list yourself, if you'd like. This line looks awkward with no author listing. + +# includeconfig.h +# include tunctl.h +# include virfile.h + +# includestdlib.h +# includestdio.h +# includestring.h +# includeunistd.h +# includefcntl.h +# includeerrno.h +# includearpa/inet.h +# includesys/types.h +# includesys/socket.h +# includesys/ioctl.h +# includepaths.h +# includesys/wait.h + +# includelinux/param.h /* HZ */ Yep, definitely Linux-specific, so the Makefile.am will need to be written so that this file is only conditionally compiled - basically, your new file should be in the same conditionals as the existing src/util/bridge.c. +# ifdef IFF_VNET_HDR +static int tapProbeVnetHdr(int tapfd) +{ +# if defined(IFF_VNET_HDR) defined(TUNGETFEATURES) defined(TUNGETIFF) This looks very similar to brProbeVnetHdr in src/util/bridge.c. If you are going to refactor code into a new file for larger reuse later, then it may be better to do the full code motion in a single commit, rather than to add the new function now and delete the original code later. That's not a hard-and-fast rule, but whichever way you go, it would be nice to mention in the commit message that you are adding the new code here in order to refactor common code out of bridge.c later. It took me a while to realize that you were reusing code rather than writing it from scratch. -- Eric Blake ebl...@redhat.com+1-801-349-2682 Libvirt virtualization library http://libvirt.org -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH] qemu: Check for ejected media during startup and migration
On 16.09.2011 17:38, Eric Blake wrote: On 09/14/2011 01:52 AM, Michal Privoznik wrote: On 14.09.2011 01:05, Eric Blake wrote: On 09/13/2011 05:03 PM, Eric Blake wrote: On 09/13/2011 10:14 AM, Michal Privoznik wrote: If the daemon is restarted so we reconnect to monitor, cdrom media can be ejected. In that case we don't want to show it in domain xml, or require it on migration destination. To check for disk status use 'info block' monitor command. Yuck - this is still polling-based (we have to ask qemu every time we want to dumpxml). Whatever happened to the proposal of having interrupt-based cdrom events, where qemu sends an event any time the virtual tray changes state (whether by monitor command or guest-initiated), and libvirt monitors those events to update its internal state at that time, so that libvirt's internal state is always accurate? For reference: https://www.redhat.com/archives/libvir-list/2011-August/msg00487.html My patch does not touch dumpxml. It access monitor only on process reconnect and in early migration phase. In both cases we access monitor anyway. That is, you are polling the status at any location where you would be doing an internal dumpxml. So while this is not exposing things to the user (and thus less polling), it is still doing polling. I'm also worried about virDomainSave and virDomainSnapshotCreateXML needing to be hooked into doing these sorts of polling, to work correctly. I'd feel much better revisiting this issue post-0.9.5 - it's too invasive to fix right now. But I agree that events would be nicer, but I don't think they are gonna be implemented in near future. However, even if they would have been implemented now, we need to check the tray during process reconnect. You have a point there - but that is a one-time poll, rather than on every operation that needs to preserve xml state. Because if the libvirtd was down and during this time guest ejected cdrom and qemu sent event, we would not catch it - because we are not running. So in order to keep things consistent, we must check tray during startup. Migration is the same. Even if qemu will sent events at one time, we still need to check if we are talking to older qemu. This should be capability-based - we should know whether the qemu we are talking to supports nothing (0.14), polling (is it in qemu yet? or still just pending), or events (even further down the road), and make appropriate decisions on how much to probe according to the qemu we are talking to. So I think the conclusion is: we need both, extended 'info block' (to do one-time poll on startup) and events (any later on). And I agree with that. However, qemu implements only the first part - extended 'info block' command so far. As soon as it will implement events too, we can adapt to them = make the check in migration early phase dependent on qemu capabilities. But I think the current patch is the best we can do right now. Michal -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH] storage: Do not break the whole vol lookup process in the middle
On 09/21/2011 04:36 AM, Osier Yang wrote: * src/storage/storage_driver.c: As virStorageVolLookupByPath lookups all the pool objs of the drivers, breaking when failing on getting the stable path of the pool will just breaks the lookup process, it can cause the API fails even if the vol exists indeed. It won't get any benifit. This patch is to fix it. s/benifit/benefit/ --- src/storage/storage_driver.c | 14 ++ 1 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index c05b74e..8a3b5be 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -1247,14 +1247,13 @@ storageVolumeLookupByPath(virConnectPtr conn, stable_path = virStorageBackendStablePath(driver-pools.objs[i], cleanpath); -/* - * virStorageBackendStablePath already does - * virStorageReportError if it fails; we just need to keep - * propagating the return code - */ if (stable_path == NULL) { -virStoragePoolObjUnlock(driver-pools.objs[i]); -goto cleanup; +/* Don't break the whole lookup process if fails on s/if fails/if it fails/ + * getting the stabe path for some of the pool. s/stabe/stable/ s/pool./pools./ + */ +VIR_WARN(Failed to get stable path for pool '%s', + driver-pools.objs[i]-def-name); +continue; You still need to call virStoragePoolObjUnlock prior to continue, otherwise you have a resource leak. } vol = virStorageVolDefFindByPath(driver-pools.objs[i], @@ -1274,7 +1273,6 @@ storageVolumeLookupByPath(virConnectPtr conn, virStorageReportError(VIR_ERR_NO_STORAGE_VOL, %s, _(no storage vol with matching path)); -cleanup: VIR_FREE(cleanpath); storageDriverUnlock(driver); return ret; I like the idea, but it would be worth seeing a v2. -- Eric Blake ebl...@redhat.com+1-801-349-2682 Libvirt virtualization library http://libvirt.org -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH v3] qemu: Allow domain reboot after core dump
On 09/23/2011 03:47 AM, Michal Privoznik wrote: This patch introduces possibility to reboot domain after core dump finishes. The new flag VIR_DUMP_REBOOT was added to virDomainCoreDumpFlags. The new functionality is accessible via virsh too: virsh dump --rebootdomain --- diff to v2: -issue reset command instead of qemuDomainReboot include/libvirt/libvirt.h.in |1 + src/qemu/qemu_driver.c | 54 +++-- tools/virsh.c|3 ++ 3 files changed, 55 insertions(+), 3 deletions(-) Missing documentation of the new flag in src/libvirt.c and tools/virsh.pod. Also, is the new flag compatible with VIR_DUMP_CRASH or VIR_DUMP_LIVE? For example, we already declare _CRASH and _LIVE as mutually exclusive, and off-hand, it seems like REBOOT is exclusive with both of these as well. Also, I'd split this patch into two pieces - one for documenting the new API (include/, src/libvirt.c, tools/), and the other for the qemu implementation of the new API (since most of my technical concerns are over the qemu implementation). Independent of your patch, I also just realized that virDomainSave[Flags], virDomainRestore[Flags], virDomainSaveImageGetXMLDesc, virDomainSaveImageDefineXML, and virDomainCoreDump all have the same design issue: they are not very nice for remote management. Each of these functions convert a relative path name into absolute name on the client side, before passing the string to the remote side, even though the actual file to be used is on the remote side. This is not always guaranteed to work, and also leaves things stuck with the file being on the remote side (no way to dump the core across the network back to the client, like there is with virDomainScreenshot). At some point, we may want to introduce new API that performs these types of operations on a stream, where the client can then manage the stream. And/or we may want to introduce a way to specify the file to manipulate as a virStorageVolPtr, or even a string relative to a virStoragePoolPtr, for more precise control of where the file ends up without having to first absolutize the file argument in the client. +++ b/src/qemu/qemu_driver.c @@ -2860,6 +2860,47 @@ getCompressionType(struct qemud_driver *driver) return compress; } +static int +doSystemReset(struct qemud_driver *driver, + virDomainObjPtr *vm) +{ +int ret = -1; +qemuDomainObjPrivatePtr priv; + +/* Okay, this should never happen, but doesn't hurt to check. */ +if (!driver || !vm || !*vm) { +qemuReportError(VIR_ERR_INTERNAL_ERROR, %s, +_(invalid argument supplied)); +goto cleanup; +} + +priv = (*vm)-privateData; + +if (qemuDomainObjBeginJob(driver, *vm, QEMU_JOB_MODIFY) 0) +goto cleanup; + +if (!virDomainObjIsActive(*vm)) { +qemuReportError(VIR_ERR_INTERNAL_ERROR, %s, +_(guest unexpectedly quit)); +goto endjob; +} Just wondering if this is a case where it would actually make sense to start up a new qemu process, instead of just report that the guest unexpectedly quit. + +qemuDomainObjEnterMonitorWithDriver(driver, *vm); +if (qemuMonitorSystemReset(priv-mon) 0) { +qemuDomainObjExitMonitorWithDriver(driver, *vm); This is the key point of this new function. But I can't help but wonder... +goto endjob; +} +qemuDomainObjExitMonitorWithDriver(driver, *vm); + +ret = 0; + +endjob: +if (qemuDomainObjEndJob(driver, *vm) == 0) +*vm = NULL; +cleanup: +return ret; +} + static int qemudDomainCoreDump(virDomainPtr dom, const char *path, unsigned int flags) @@ -2870,7 +2911,8 @@ static int qemudDomainCoreDump(virDomainPtr dom, int ret = -1; virDomainEventPtr event = NULL; -virCheckFlags(VIR_DUMP_LIVE | VIR_DUMP_CRASH | VIR_DUMP_BYPASS_CACHE, -1); +virCheckFlags(VIR_DUMP_LIVE | VIR_DUMP_CRASH | + VIR_DUMP_BYPASS_CACHE | VIR_DUMP_REBOOT, -1); qemuDriverLock(driver); vm = virDomainFindByUUID(driver-domains, dom-uuid); @@ -2949,11 +2991,17 @@ endjob: } cleanup: -if (vm) -virDomainObjUnlock(vm); if (event) qemuDomainEventQueue(driver, event); + +if ((ret == 0) (flags VIR_DUMP_REBOOT) vm) { +ret = doSystemReset(driver,vm); This ends up starting a second job. Wouldn't it be better to reuse the first job, by checking for VIR_DUMP_REBOOT in the endjob label alongside the check for 'resume paused', and make the call to qemuMonitorSystemReset instead of qemuProcessStartCPUs at that point, so that the cleanup label remains untouched? -- Eric Blake ebl...@redhat.com+1-801-349-2682 Libvirt virtualization library http://libvirt.org -- libvir-list mailing list libvir-list@redhat.com
Re: [libvirt] [PATCHv2] qemu: add ability to set PCI device rombar on or off
On 09/22/2011 10:05 AM, Laine Stump wrote: (This addresses Eric's comments in his review of V1. BTW, I'm a bit surprised nobody has commented about the choice of name/placement of the new attribute - speak now or forever hold your peace :-)) I was okay with the xml naming - but I agree that if anyone else has a better suggestion, now is the time to speak up :) This patch was made in response to: https://bugzilla.redhat.com/show_bug.cgi?id=738095 In short, qemu's default for the rombar setting (which makes the firmware ROM of a PCI device visible/not on the guest) was previously 0 (not visible), but they recently changed the default to 1 (visible). Unfortunately, there are some PCI devices that fail in the guest when rombar is 1, so the setting must be exposed in libvirt to prevent a regression in behavior (it will still require explicitly settingrom bar='off'/ in the guest XML). rombar is forced on/off by adding: rom bar='on|off'/ inside ahostdev element that defines a PCI device. It is currently ignored for all other types of devices. +++ b/docs/formatdomain.html.in @@ -1371,6 +1371,7 @@ lt;address bus='0x06' slot='0x02' function='0x0'/gt; lt;/sourcegt; lt;boot order='1'/gt; +lt;rom bar='0'/gt; s/0/off/ lt;/hostdevgt; lt;/devicesgt; .../pre @@ -1402,6 +1403,18 @@ used together with general boot elements in a href=#elementsOSBIOSBIOS bootloader/a section. span class=sinceSince 0.8.8/span/dd +dtcoderom/code/dt +ddThecoderom/code element is used to change how a PCI +device's ROM is presented to the guest. Thecodebar/code +attribute can be set to on or off, and determines whether +or not the device's ROM will be visible in the guest's memory +map. (In PCI documentation, the rombar setting controls the +presence of the Base Address Register for the ROM). If no rom +bar is specified, the qemu default will be used (older +versions of qemu used a default of off, while newer qemus +have a default of on).span class=sinceSince +0.9.6/span looks much nicer, now that my v1 comments are incorporated, but still one nit: s/0.9.6/0.9.7/ @@ -10387,6 +10407,18 @@ virDomainHostdevDefFormat(virBufferPtr buf, if (virDomainDeviceInfoFormat(buf,def-info, flags) 0) return -1; +if (def-rombar) { +const char *rombar += virDomainPciRombarModeTypeToString(def-rombar); +if (!rombar) { +virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _(unexpected rom bar value %d), + def-rombar); +return -1; +} +virBufferAsprintf(buf, rom bar='%s'/\n, rombar); Aargh - Thunderbird strikes me again. When will they EVER fix their horrible whitespace munging bug? This will conflict with my snapshotdomain reindentation patch series - so whoever applies first gets the easier side of the bargain :) +} + virBufferAddLit(buf, /hostdev\n); return 0; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 371f270..262c970 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -936,6 +936,14 @@ enum virDomainHostdevSubsysType { VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST }; +enum virDomainPciRombarMode { +VIR_DOMAIN_PCI_ROMBAR_DEFAULT = 0, +VIR_DOMAIN_PCI_ROMBAR_ON, +VIR_DOMAIN_PCI_ROMBAR_OFF, + +VIR_DOMAIN_PCI_ROMBAR_LAST +}; + typedef struct _virDomainHostdevDef virDomainHostdevDef; typedef virDomainHostdevDef *virDomainHostdevDefPtr; struct _virDomainHostdevDef { @@ -964,6 +972,7 @@ struct _virDomainHostdevDef { } source; int bootIndex; virDomainDeviceInfo info; /* Guest address */ +int rombar; /* rombar on/off/unspecified */ Your comment could go out of date if the enum grows. Lately, I've been listing fields like this as: int rombar; /* enum virDomainPciRombarMode */ which stays correct even if we later add new values to the enum. +++ b/src/qemu/qemu_capabilities.h @@ -110,6 +110,7 @@ enum qemuCapsFlags { QEMU_CAPS_PCI_OHCI = 71, /* -device pci-ohci */ QEMU_CAPS_USB_REDIR = 72, /* -device usb-redir */ QEMU_CAPS_USB_HUB = 73, /* -device usb-hub */ +QEMU_CAPS_PCI_ROMBAR= 74, /* -device rombar=0|1 */ Needs an obvious rebase for NO_SHUTDOWN and DRIVE_CACHE_UNSAFE. ACK to the idea. I think my findings are small enough that I'm okay if you push with nits fixed rather than posting a v3 patch, although you may still want to wait for any other comments on the proposed xml spelling. -- Eric Blake ebl...@redhat.com+1-801-349-2682 Libvirt virtualization library http://libvirt.org -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH v3 1/2] Add VIR_TYPED_PARAM_STRING
On 09/23/2011 12:40 AM, Hu Tao wrote: This makes string can be transported between client and server. For compatibility, o new server should not send strings to old client if it doesn't see the flag VIR_DOMAIN_TYPED_STRING_OKAY. o new client that wants to be able to send/receive strings should always set the flag VIR_DOMAIN_TYPED_STRING_OKAY; if it is rejected by a old server that doesn't understand VIR_DOMAIN_TYPED_STRING_OKAY, then the client should have a second try with out flag VIR_DOMAIN_TYPED_STRING_OKAY to cope with an old server. These points should probably be documented in libvirt.h.in, as well. I'm also thinking that libvirt.c should do some argument validation - for every API that uses a virTypedParameter array, it should validate that no VIR_TYPED_PARAM_STRING occurs if the flag is not present. +++ b/daemon/remote.c @@ -672,6 +672,15 @@ remoteSerializeTypedParameters(virTypedParameterPtr params, case VIR_TYPED_PARAM_BOOLEAN: val[i].value.remote_typed_param_value_u.b = params[i].value.b; break; +case VIR_TYPED_PARAM_STRING: +if (params[i].value.s) { +val[i].value.remote_typed_param_value_u.s = strdup(params[i].value.s); +if (val[i].value.remote_typed_param_value_u.s == NULL) { +virReportOOMError(); +goto cleanup; +} +} +break; default: Memory leak. The cleanup: label must free any successfully allocated remote_typed_param_value_u.s contents prior to the failure point. virNetError(VIR_ERR_RPC, _(unknown parameter type: %d), params[i].type); @@ -750,6 +759,14 @@ remoteDeserializeTypedParameters(remote_typed_param *args_params_val, params[i].value.b = args_params_val[i].value.remote_typed_param_value_u.b; break; +case VIR_TYPED_PARAM_STRING: +params[i].value.s = +strdup(args_params_val[i].value.remote_typed_param_value_u.s); +if (params[i].value.s == NULL) { +virReportOOMError(); +goto cleanup; +} +break; default: virNetError(VIR_ERR_INTERNAL_ERROR, _(unknown parameter type: %d), params[i].type); Memory leak. The cleanup: label must now iterate over params, and free any successfully allocated params[i].value.s allocated prior to a failure. diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 39155a6..448a0e7 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -205,6 +205,7 @@ typedef enum { VIR_DOMAIN_AFFECT_CURRENT = 0, /* Affect current domain state. */ VIR_DOMAIN_AFFECT_LIVE= 1 0, /* Affect running domain state. */ VIR_DOMAIN_AFFECT_CONFIG = 1 1, /* Affect persistent domain state. */ +VIR_DOMAIN_TYPED_STRING_OKAY = 1 2, } virDomainModificationImpact; I'm not sure if this is the best place to stick this enum value; it might fit better if it is listed (and documented!) closer to the rest of virTypedParameterType stuff. But I agree with setting it to the value of 12, since all current clients of virTypedParameterType seem to be using VIR_DOMAIN_AFFECT_* and nothing further. /** @@ -489,7 +490,8 @@ typedef enum { VIR_TYPED_PARAM_LLONG = 3, /* long long case */ VIR_TYPED_PARAM_ULLONG = 4, /* unsigned long long case */ VIR_TYPED_PARAM_DOUBLE = 5, /* double case */ -VIR_TYPED_PARAM_BOOLEAN = 6 /* boolean(character) case */ +VIR_TYPED_PARAM_BOOLEAN = 6, /* boolean(character) case */ +VIR_TYPED_PARAM_STRING = 7 /* string case */ Put a trailing comma after 7, so that the next addition (if any) doesn't have to touch two lines like this addition did. } virTypedParameterType; /** @@ -520,6 +522,7 @@ struct _virTypedParameter { unsigned long long int ul; /* type is ULLONG */ double d; /* type is DOUBLE */ char b; /* type is BOOLEAN */ +char *s;/* type is STRING */ Here, I'd use: char *s; /* type is STRING, see also VIR_DOMAIN_TYPED_STRING_OKAY */ } value; /* parameter value */ }; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 1217d94..85eaeea 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -1276,6 +1276,15 @@ remoteSerializeTypedParameters(virTypedParameterPtr params, case VIR_TYPED_PARAM_BOOLEAN: val[i].value.remote_typed_param_value_u.b = params[i].value.b; break; +case VIR_TYPED_PARAM_STRING: +if (params[i].value.s) { +val[i].value.remote_typed_param_value_u.s = strdup(params[i].value.s); +if
Re: [libvirt] [PATCH v3 2/2] add interface for blkio.weight_device
On 09/23/2011 12:40 AM, Hu Tao wrote: This patch adds a parameter --weight-device to virsh command blkiotune for setting/getting blkio.weight_device. --- daemon/remote.c |5 + include/libvirt/libvirt.h.in |9 ++ src/conf/domain_conf.c | 114 - src/conf/domain_conf.h | 16 src/libvirt_private.syms |1 + src/qemu/qemu_cgroup.c | 22 + src/qemu/qemu_driver.c | 191 +- src/util/cgroup.c| 33 +++ src/util/cgroup.h|3 + tools/virsh.c| 52 ++-- tools/virsh.pod |5 +- 11 files changed, 437 insertions(+), 14 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 82ee13b..bee1b94 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1566,6 +1566,7 @@ remoteDispatchDomainGetBlkioParameters(virNetServerPtr server ATTRIBUTE_UNUSED, int nparams = args-nparams; unsigned int flags; int rv = -1; +int i; struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client); @@ -1610,6 +1611,10 @@ success: cleanup: if (rv 0) virNetMessageSaveError(rerr); +for (i = 0; i nparams; i++) { +if (params[i].type == VIR_TYPED_PARAM_STRING) +VIR_FREE(params[i].value.s); +} VIR_FREE(params); This for loop seems like something that will be done frequently enough that it might be worth factoring it into a util file for managing virTypedParameters. Something like: virTypedParameterFree(params). if (dom) virDomainFree(dom); diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 448a0e7..a1f2c98 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1139,6 +1139,15 @@ char * virDomainGetSchedulerType(virDomainPtr domain, #define VIR_DOMAIN_BLKIO_WEIGHT weight +/** + * VIR_DOMAIN_BLKIO_WEIGHT_DEVICE: + * + * Macro for the blkio tunable weight_device: it represents the + * per device weight. + */ Also mention that this name refers to a VIR_TYPED_PARAMETER_STRING. +/** + * virDomainBlkioWeightDeviceParseXML + * + * this function parses a XML node: + * + *device + *path/fully/qulaified/device/path/path s/qulaified/qualified/ + *weightweight/weight + */device + * + * and fills a virBlkioWeightDevice struct. + */ +static int virDomainBlkioWeightDeviceParseXML(xmlNodePtr root, + virBlkioWeightDevicePtr dw) +{ +char *c; +struct stat s; +xmlNodePtr node; + +if (!dw) +return -1; + +node = root-children; +while (node) { +if (node-type == XML_ELEMENT_NODE) { +if (xmlStrEqual(node-name, BAD_CAST path)) { +c = (char *)xmlNodeGetContent(node); +if (!c) +return -1; +if (stat(c,s) == -1) +return -1; This stat() ties the parse to the existence of the node. But that seems wrong - it should be possible to define a domain with XML that refers to a node that does not yet exist, provided that the node later exists at the time we try to start the domain. +if ((s.st_mode S_IFMT) == S_IFBLK) { +dw-major = major(s.st_rdev); +dw-minor = minor(s.st_rdev); Also, breaking the parse into major/minor this early makes the result unmigratable, since we can't guarantee major/minor stability across hosts. I think you have to store the name, not the major/minor, here in the domain_conf representation of the device weight, and only convert to major/minor in the hypervisor that is actually enforcing the limits. +} else +return -1; +dw-path = (char *)xmlNodeGetContent(node); +} else if (xmlStrEqual(node-name, BAD_CAST weight)) { +c = (char *)xmlNodeGetContent(node); +dw-weight = atoi(c); atoi is unsafe. It cannot detect errors. You should use virStrToLong_i (or similar) instead. @@ -10499,10 +10591,26 @@ virDomainDefFormatInternal(virDomainDefPtr def, def-mem.cur_balloon); /* add blkiotune only if there are any */ -if (def-blkio.weight) { +if (def-blkio.weight || def-blkio.weight_devices) { virBufferAsprintf(buf, blkiotune\n); -virBufferAsprintf(buf, weight%u/weight\n, - def-blkio.weight); + +if (def-blkio.weight) +virBufferAsprintf(buf, weight%u/weight\n, (Stupid Thunderbird for munging whitespace). This will conflict with my patch series for indenting domainsnapshot. Shouldn't be too hard to figure out, but it boils down to who gets acked first. +++ b/src/qemu/qemu_driver.c @@ -44,6 +44,7 @@ #includesys/ioctl.h #includesys/un.h #includebyteswap.h +#includectype.h This
Re: [libvirt] [RFC PATCH] Prevent defining a domain has disk used by other domain
On 09/23/2011 02:17 AM, Osier Yang wrote: Hmm, preventing the relabeling in security driver instead might be the more proper way? (If the disk source is used by other *running* domain, then quit relabeling and exit with error). No, prevent the relabeling in the lock manager. If one domain is running and the lock manager is running, that should be sufficient to prevent any other domain from starting with the same disk, even before we get to the labeling point. However, this won't prevent one using same disk source for multiple domains if security_driver is disabled. And if security_driver is disabled, there will be no permission problem, all the domains can write to the same disk source, thus it might cause inconsistency between the domains or corrupt. to see whether if the pricinple is right or not. The principle here is whether the lock manager is running. Only if you can still reproduce the problem with a lock manager (whether sanlock or fcntl) do we have a bug to fix. -- Eric Blake ebl...@redhat.com+1-801-349-2682 Libvirt virtualization library http://libvirt.org -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [libvirt-virshcmdref 08/10] update documentation for command migrate-getspeed
On 09/14/2011 01:28 AM, Hu Tao wrote: --- common.sh |2 +- source/migrate-getspeed.xml | 55 +++ 2 files changed, 56 insertions(+), 1 deletions(-) create mode 100644 source/migrate-getspeed.xml diff --git a/common.sh b/common.sh index a84c832..6454fe9 100755 --- a/common.sh +++ b/common.sh @@ -5,7 +5,7 @@ DOMAIN_COMMANDS=attach-device attach-disk attach-interface autostart domname domuuid domxml-from-native domxml-to-native dump dumpxml echo edit freecell hostname inject-nmi managedsave managedsave-remove maxvcpus memtune migrate migrate-setmaxdowntime migrate-setspeed - reboot restore resume + migrate-getspeed reboot restore resume Not quite alphabetical, so I fixed that. ACK, and I've pushed 6-8. Just 9 and 10 left to review. -- Eric Blake ebl...@redhat.com+1-801-349-2682 Libvirt virtualization library http://libvirt.org -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [libvirt-virshcmdref 10/10] update documentation for command vcpucount
On 09/14/2011 01:29 AM, Hu Tao wrote: --- source/vcpucount.xml | 67 - 1 files changed, 65 insertions(+), 2 deletions(-) ACK and pushed 9 and 10. Sorry it took me so long to get through this series. -- Eric Blake ebl...@redhat.com+1-801-349-2682 Libvirt virtualization library http://libvirt.org -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH v3] qemu: Allow domain reboot after core dump
On Fri, Sep 23, 2011 at 11:47:24AM +0200, Michal Privoznik wrote: This patch introduces possibility to reboot domain after core dump finishes. The new flag VIR_DUMP_REBOOT was added to virDomainCoreDumpFlags. The new functionality is accessible via virsh too: virsh dump --reboot domain --- diff to v2: -issue reset command instead of qemuDomainReboot We should name the option --reset rather than --reboot then. include/libvirt/libvirt.h.in |1 + src/qemu/qemu_driver.c | 54 +++-- tools/virsh.c|3 ++ 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 39155a6..8c41f5a 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -748,6 +748,7 @@ typedef enum { VIR_DUMP_CRASH= (1 0), /* crash after dump */ VIR_DUMP_LIVE = (1 1), /* live dump */ VIR_DUMP_BYPASS_CACHE = (1 2), /* avoid file system cache pollution */ +VIR_DUMP_REBOOT = (1 3), /* reboot domain after dump finishes */ } virDomainCoreDumpFlags; /* Domain migration flags. */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0d0bea2..3e05e14 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2860,6 +2860,47 @@ getCompressionType(struct qemud_driver *driver) return compress; } +static int +doSystemReset(struct qemud_driver *driver, + virDomainObjPtr *vm) +{ +int ret = -1; +qemuDomainObjPrivatePtr priv; + +/* Okay, this should never happen, but doesn't hurt to check. */ +if (!driver || !vm || !*vm) { +qemuReportError(VIR_ERR_INTERNAL_ERROR, %s, +_(invalid argument supplied)); +goto cleanup; +} + +priv = (*vm)-privateData; + +if (qemuDomainObjBeginJob(driver, *vm, QEMU_JOB_MODIFY) 0) +goto cleanup; + +if (!virDomainObjIsActive(*vm)) { +qemuReportError(VIR_ERR_INTERNAL_ERROR, %s, +_(guest unexpectedly quit)); +goto endjob; +} + +qemuDomainObjEnterMonitorWithDriver(driver, *vm); +if (qemuMonitorSystemReset(priv-mon) 0) { +qemuDomainObjExitMonitorWithDriver(driver, *vm); +goto endjob; +} +qemuDomainObjExitMonitorWithDriver(driver, *vm); + +ret = 0; + +endjob: +if (qemuDomainObjEndJob(driver, *vm) == 0) +*vm = NULL; +cleanup: +return ret; +} + static int qemudDomainCoreDump(virDomainPtr dom, const char *path, unsigned int flags) @@ -2870,7 +2911,8 @@ static int qemudDomainCoreDump(virDomainPtr dom, int ret = -1; virDomainEventPtr event = NULL; -virCheckFlags(VIR_DUMP_LIVE | VIR_DUMP_CRASH | VIR_DUMP_BYPASS_CACHE, -1); +virCheckFlags(VIR_DUMP_LIVE | VIR_DUMP_CRASH | + VIR_DUMP_BYPASS_CACHE | VIR_DUMP_REBOOT, -1); qemuDriverLock(driver); vm = virDomainFindByUUID(driver-domains, dom-uuid); @@ -2949,11 +2991,17 @@ endjob: } cleanup: -if (vm) -virDomainObjUnlock(vm); if (event) qemuDomainEventQueue(driver, event); + +if ((ret == 0) (flags VIR_DUMP_REBOOT) vm) { +ret = doSystemReset(driver, vm); +} + +if (vm) +virDomainObjUnlock(vm); qemuDriverUnlock(driver); + return ret; } diff --git a/tools/virsh.c b/tools/virsh.c index e5ea9d7..bdff33c 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -2899,6 +2899,7 @@ static const vshCmdOptDef opts_dump[] = { {crash, VSH_OT_BOOL, 0, N_(crash the domain after core dump)}, {bypass-cache, VSH_OT_BOOL, 0, N_(avoid file system cache when saving)}, +{reboot, VSH_OT_BOOL, 0, N_(reboot the domain after core dump)}, {domain, VSH_OT_DATA, VSH_OFLAG_REQ, N_(domain name, id or uuid)}, {file, VSH_OT_DATA, VSH_OFLAG_REQ, N_(where to dump the core)}, {NULL, 0, 0, NULL} @@ -2928,6 +2929,8 @@ cmdDump(vshControl *ctl, const vshCmd *cmd) flags |= VIR_DUMP_CRASH; if (vshCommandOptBool(cmd, bypass-cache)) flags |= VIR_DUMP_BYPASS_CACHE; +if (vshCommandOptBool(cmd, reboot)) +flags |= VIR_DUMP_REBOOT; if (virDomainCoreDump(dom, to, flags) 0) { vshError(ctl, _(Failed to core dump domain %s to %s), name, to); -- 1.7.3.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH] nodedev: document virsh nodedev-* commands
This section of the man page was completely missing; I stumbled on it when I had no clue that I had to use nodedev-reattach after I was done playing with hostdev device passthrough to one of my guests. * tools/virsh.pod (NODEDEV COMMANDS): New section. --- I also need to write something useful for http://libvirt.org/formatnode.html, but that's a patch for another day. Please double-check this for technical accuracy. tools/virsh.pod | 315 ++- 1 files changed, 241 insertions(+), 74 deletions(-) diff --git a/tools/virsh.pod b/tools/virsh.pod index 43ed1ea..e9f415a 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -1163,84 +1163,251 @@ is not available the processes will provide an exit code of 1. =back -=head1 DEVICE COMMANDS +=head1 NODEDEV COMMANDS + +The following commands manipulate host devices that are intended to be +passed through to guest domains via hostdev elements in a domain's +devices section. A node device key is generally specified by the bus +name followed by its address, using underscores between all components, +such as pci__00_02_1, usb_1_5_3, or net_eth1_00_27_13_6a_fe_00. +The Bnodedev-list gives the full list of host devices that are known +to libvirt, although this includes devices that cannot be assigned to +a guest (for example, attempting to detach the PCI device that controls +the host's hard disk controller where the guest's disk images live could +cause the host system to lock up or reboot). + +For more information on node device definition see: +Lhttp://libvirt.org/formatnode.html. + +Passthrough devices cannot be simultaneously used by the host and its +guest domains. Attempts to use a passthrough hostdev for a guest may +have the ability to behave as if Bnodedev-dettach had been called, +although making this call explicitly is safe. Once a guest no longer +needs a passthrough device, reversing the process so that the host can +again use the device requires the explicit use of Bnodedev-reattach. -The following commands manipulate devices associated to domains. -The domain-id can be specified as a short integer, a name or a full UUID. -To better understand the values allowed as options for the command -reading the documentation at Lhttp://libvirt.org/formatdomain.html on the -format of the device sections to get the most accurate set of accepted values. +=over 4 + +=item Bnodedev-create IFILE + +Create a device on the host node that can then be assigned to virtual +machines. Normally, libvirt is able to automatically determine which +host nodes are available for use, but this allows registration of +host hardware that libvirt did not automatically detect. Ifile +contains xml for a top-level device description of a node device. + +=item Bnodedev-destroy Inodedev + +Destroy (stop) a device on the host. Note that this makes libvirt +quit managing a host device, and may even make that device unusable +by the rest of the physical host until a reboot. + +=item Bnodedev-dettach Inodedev + +Detach Inodedev from the host, so that it can safely be used by +guests via hostdev passthrough. This is reversed with +Bnodedev-reattach. + +=item Bnodedev-dumpxml Inodedev + +Dump a device XML representation for the given node device, including +such information as the device name, which bus owns the device, the +vendor and product id, and any capabilities of the device usable by +libvirt (such as whether device reset is supported). + +=item Bnodedev-list Icap I--tree + +List all of the devices available on the node that are known by libvirt. +If Icap is used, the list is filtered to show only the nodes that +include the given capability. If I--tree is used, the output is +formatted in a tree representing parents of each node. + +=item Bnodedev-reattach Inodedev + +Declare that Inodedev is no longer in use by any guests, and that +the host can resume normal use of the device. While libvirt can +sometimes perform an implicit Bnodedev-dettach when creating a +guest, it currently requires an explicit Bnodedev-reattach after +the last guest use of the device before the host regains full control. + +=item Bnodedev-reset Inodedev + +Trigger a device reset for Inodedev, useful prior to transferring +a node device between guest passthrough or the host. Libvirt will +often do this action implicitly when required, but this command +allows an explicit reset when needed. + +=back + +=head1 SNAPSHOT COMMMANDS + +The following commands manipulate domain snapshots. Snapshots take the +disk, memory, and device state of a domain at a point-of-time, and save it +for future use. They have many uses, from saving a clean copy of an OS +image to saving a domain's state before a potentially destructive operation. +Snapshots are identified with a unique name. See +Lhttp://libvirt.org/formatsnapshot.html for documentation of the XML format +used to represent properties of snapshots. =over 4 -=item Battach-device Idomain-id IFILE -