[libvirt] [PATCH 0/6] [RFC] Add disk streaming support to libvirt

( Those on Cc: sorry for any duplicated messages. git was configured to use the wrong smtp relay ) I've been working with Anthony Liguori and Stefan Hajnoczi to enable data streaming to copy-on-read disk images in qemu. This work is working its way through review and I expect it to be upstream soon as part of the support for the new QED disk image format. Disk streaming is extremely useful when provisioning domains from a central repository of template images. Currently the domain must be provisioned by either: 1) copying the template image to local storage before the VM can be started or, 2) creating a qcow2 image that backs to a base image in the remote repository. Option 1 can introduce a significant delay when provisioning large disks. Option 2 introduces a permanent dependency on a remote service, increased network load to satisfy disk reads. Device streaming provides the "instant-on" benefits of option 2 without introducing a permanent dependency to the image repository. Once the VM is started, the contents of the disk can be streamed to the local image in parallel. Once streaming is finished, the domain has a complete and coherent copy of the image and no longer depends on the central image repository. Qemu will support two streaming modes: full device and single sector. Full device streaming is the easiest to use because one command will cause the whole device to be streamed as fast as possible. Single sector mode can be used if one wants to throttle streaming to reduce I/O pressure. In this mode, a management tool issues individual commands to stream single sectors. To enable this support in libvirt, I propose the following API... virDomainStreamDisk() will start or stop a full device stream or stream a single sector of a device. The behavior is controlled by setting virDomainStreamDiskFlags. When either starting or stopping a full device stream, the return value is either 0 or -1 to indicate whether the operation succeeded. For a single sector stream, a device offset is returned (or -1 on failure). This value can be used to continue streaming with a subsequent call to virDomainStreamDisk(). virDomainStreamDiskInfo() returns information about active full device streams (the device alias, current streaming position, and total size). While streaming can be used to enable live block migration, this API addresses the direct streaming use case. If we can agree that the above use case is compelling on its own, I would prefer to work on the block migration workflow immediately after this series. In that case, we just need to be careful not to paint ourselves into a corner with respect to migration, but specifics about that feature can be discussed later. Does this seem reasonable? Adam Litke (6): Add new API virDomainStreamDisk[Info] to header and drivers virDomainStreamDisk: Add public symbols to libvirt API Implement disk streaming in the qemu driver Add disk streaming support to the remote driver Add new disk streaming commands to virsh python: Add python bindings for virDomainStreamDisk[Info] daemon/remote.c | 96 +++++++++++++++++++++ daemon/remote_dispatch_args.h | 2 + daemon/remote_dispatch_prototypes.h | 16 ++++ daemon/remote_dispatch_ret.h | 2 + daemon/remote_dispatch_table.h | 10 ++ include/libvirt/libvirt.h.in | 34 ++++++++ python/generator.py | 1 + python/libvirt-override-api.xml | 5 + python/libvirt-override.c | 46 ++++++++++ src/driver.h | 11 +++ src/esx/esx_driver.c | 2 + src/libvirt.c | 121 +++++++++++++++++++++++++++ src/libvirt_public.syms | 6 ++ src/lxc/lxc_driver.c | 2 + src/opennebula/one_driver.c | 2 + src/openvz/openvz_driver.c | 2 + src/phyp/phyp_driver.c | 2 + src/qemu/qemu_driver.c | 75 +++++++++++++++++ src/qemu/qemu_monitor.c | 41 +++++++++ src/qemu/qemu_monitor.h | 6 ++ src/qemu/qemu_monitor_json.c | 104 +++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 7 ++ src/qemu/qemu_monitor_text.c | 156 ++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_text.h | 8 ++ src/remote/remote_driver.c | 85 +++++++++++++++++++ src/remote/remote_protocol.c | 63 ++++++++++++++ src/remote/remote_protocol.h | 51 +++++++++++ src/remote/remote_protocol.x | 37 ++++++++- src/test/test_driver.c | 2 + src/uml/uml_driver.c | 2 + src/vbox/vbox_tmpl.c | 2 + src/xen/xen_driver.c | 2 + tools/virsh.c | 157 +++++++++++++++++++++++++++++++++++ 33 files changed, 1157 insertions(+), 1 deletions(-) -- 1.7.3.2.164.g6f10c

Set up the types for the disk streaming functions and insert it into the virDriver structure definition. Because of static initializers, update every driver and set the new field to NULL. * include/libvirt/libvirt.h.in: new API * src/driver.h src/*/*_driver.c src/vbox/vbox_tmpl.c: add the new entry to the driver structure * python/generator.py: fix compiler errors, the actual python bindings are implemented later Signed-off-by: Adam Litke <agl@us.ibm.com> --- include/libvirt/libvirt.h.in | 34 ++++++++++++++++++++++++++++++++++ python/generator.py | 3 +++ src/driver.h | 11 +++++++++++ src/esx/esx_driver.c | 2 ++ src/lxc/lxc_driver.c | 2 ++ src/opennebula/one_driver.c | 2 ++ src/openvz/openvz_driver.c | 2 ++ src/phyp/phyp_driver.c | 2 ++ src/qemu/qemu_driver.c | 2 ++ src/remote/remote_driver.c | 2 ++ src/test/test_driver.c | 2 ++ src/uml/uml_driver.c | 2 ++ src/vbox/vbox_tmpl.c | 2 ++ src/xen/xen_driver.c | 2 ++ 14 files changed, 70 insertions(+), 0 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 81db3a2..b46e0cb 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1046,6 +1046,40 @@ int virDomainUpdateDeviceFlags(virDomainPtr domain, const char *xml, unsigned int flags); /* + * Disk Streaming + */ +typedef enum { + VIR_STREAM_DISK_ONE = 1, /* Stream a single disk unit */ + VIR_STREAM_DISK_START = 2, /* Stream the entire disk */ + VIR_STREAM_DISK_STOP = 4, /* Stop streaming a disk */ +} virDomainStreamDiskFlags; + +#define VIR_STREAM_PATH_BUFLEN 1024 +#define VIR_STREAM_DISK_MAX_STREAMS 10 + +typedef struct _virStreamDiskState virStreamDiskState; +struct _virStreamDiskState { + char path[VIR_STREAM_PATH_BUFLEN]; + /* + * The unit of measure for size and offset is unspecified. These fields + * are meant to indicate the progress of a continuous streaming operation. + */ + unsigned long long offset; /* Current offset of active streaming */ + unsigned long long size; /* Disk size */ +}; +typedef virStreamDiskState *virStreamDiskStatePtr; + +unsigned long long virDomainStreamDisk(virDomainPtr dom, + const char *path, + unsigned long long offset, + unsigned int flags); + +int virDomainStreamDiskInfo(virDomainPtr dom, + virStreamDiskStatePtr states, + unsigned int nr_states, + unsigned int flags); + +/* * NUMA support */ diff --git a/python/generator.py b/python/generator.py index 68009b9..2477b59 100755 --- a/python/generator.py +++ b/python/generator.py @@ -161,6 +161,8 @@ def enum(type, name, value): functions_failed = [] functions_skipped = [ "virConnectListDomains", + "virDomainStreamDisk", + "virDomainStreamDiskInfo", ] skipped_modules = { @@ -175,6 +177,7 @@ skipped_types = { 'virConnectDomainEventIOErrorCallback': "No function types in python", 'virConnectDomainEventGraphicsCallback': "No function types in python", 'virEventAddHandleFunc': "No function types in python", + 'virStreamDiskStatePtr': "Not implemented yet", } ####################################################################### diff --git a/src/driver.h b/src/driver.h index 79a96c1..3c83cf5 100644 --- a/src/driver.h +++ b/src/driver.h @@ -476,6 +476,15 @@ typedef int (*virDrvDomainSnapshotDelete)(virDomainSnapshotPtr snapshot, unsigned int flags); +typedef unsigned long long + (*virDrvDomainStreamDisk)(virDomainPtr dom, const char *path, + unsigned long long offset, unsigned int flags); + +typedef int + (*virDrvDomainStreamDiskInfo)(virDomainPtr dom, + virStreamDiskStatePtr states, + unsigned int nr_states, unsigned int flags); + typedef int (*virDrvQemuDomainMonitorCommand)(virDomainPtr domain, const char *cmd, char **result, unsigned int flags); @@ -598,6 +607,8 @@ struct _virDriver { virDrvQemuDomainMonitorCommand qemuDomainMonitorCommand; virDrvDomainSetMemoryParameters domainSetMemoryParameters; virDrvDomainGetMemoryParameters domainGetMemoryParameters; + virDrvDomainStreamDisk domainStreamDisk; + virDrvDomainStreamDiskInfo domainStreamDiskInfo; }; typedef int diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c index b3e1284..39c6e43 100644 --- a/src/esx/esx_driver.c +++ b/src/esx/esx_driver.c @@ -4253,6 +4253,8 @@ static virDriver esxDriver = { NULL, /* qemuDomainMonitorCommand */ NULL, /* domainSetMemoryParameters */ NULL, /* domainGetMemoryParameters */ + NULL, /* domainStreamDisk */ + NULL, /* domainStreamDiskInfo */ }; diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index d39b60e..685316d 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -2844,6 +2844,8 @@ static virDriver lxcDriver = { NULL, /* qemuDomainMonitorCommand */ lxcDomainSetMemoryParameters, /* domainSetMemoryParameters */ lxcDomainGetMemoryParameters, /* domainGetMemoryParameters */ + NULL, /* domainStreamDisk */ + NULL, /* domainStreamDiskInfo */ }; static virStateDriver lxcStateDriver = { diff --git a/src/opennebula/one_driver.c b/src/opennebula/one_driver.c index 199fca3..1525815 100644 --- a/src/opennebula/one_driver.c +++ b/src/opennebula/one_driver.c @@ -822,6 +822,8 @@ static virDriver oneDriver = { NULL, /* qemuDomainMonitorCommand */ NULL, /* domainSetMemoryParameters */ NULL, /* domainGetMemoryParameters */ + NULL, /* domainStreamDisk */ + NULL, /* domainStreamDiskInfo */ }; static virStateDriver oneStateDriver = { diff --git a/src/openvz/openvz_driver.c b/src/openvz/openvz_driver.c index b7c2754..752f764 100644 --- a/src/openvz/openvz_driver.c +++ b/src/openvz/openvz_driver.c @@ -1690,6 +1690,8 @@ static virDriver openvzDriver = { NULL, /* qemuDomainMonitorCommand */ NULL, /* domainSetMemoryParameters */ NULL, /* domainGetMemoryParameters */ + NULL, /* domainStreamDisk */ + NULL, /* domainStreamDiskInfo */ }; int openvzRegister(void) { diff --git a/src/phyp/phyp_driver.c b/src/phyp/phyp_driver.c index 3d0ed11..5cf8ef0 100644 --- a/src/phyp/phyp_driver.c +++ b/src/phyp/phyp_driver.c @@ -4036,6 +4036,8 @@ static virDriver phypDriver = { NULL, /* qemuMonitorCommand */ NULL, /* domainSetMemoryParameters */ NULL, /* domainGetMemoryParameters */ + NULL, /* domainStreamDisk */ + NULL, /* domainStreamDiskInfo */ }; static virStorageDriver phypStorageDriver = { diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 1eea3a9..dbde9e7 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -13298,6 +13298,8 @@ static virDriver qemuDriver = { qemuDomainMonitorCommand, /* qemuDomainMonitorCommand */ qemuDomainSetMemoryParameters, /* domainSetMemoryParameters */ qemuDomainGetMemoryParameters, /* domainGetMemoryParameters */ + NULL, /* domainStreamDisk */ + NULL, /* domainStreamDiskInfo */ }; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index c8d9a4d..237e03b 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -10540,6 +10540,8 @@ static virDriver remote_driver = { remoteQemuDomainMonitorCommand, /* qemuDomainMonitorCommand */ remoteDomainSetMemoryParameters, /* domainSetMemoryParameters */ remoteDomainGetMemoryParameters, /* domainGetMemoryParameters */ + NULL, /* domainStreamDisk */ + NULL, /* domainStreamDiskInfo */ }; static virNetworkDriver network_driver = { diff --git a/src/test/test_driver.c b/src/test/test_driver.c index a9d3d89..01c2956 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -5447,6 +5447,8 @@ static virDriver testDriver = { NULL, /* qemuDomainMonitorCommand */ NULL, /* domainSetMemoryParameters */ NULL, /* domainGetMemoryParameters */ + NULL, /* domainStreamDisk */ + NULL, /* domainStreamDiskInfo */ }; static virNetworkDriver testNetworkDriver = { diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c index 5161012..37fe937 100644 --- a/src/uml/uml_driver.c +++ b/src/uml/uml_driver.c @@ -2200,6 +2200,8 @@ static virDriver umlDriver = { NULL, /* qemuDomainMonitorCommand */ NULL, /* domainSetMemoryParamters */ NULL, /* domainGetMemoryParamters */ + NULL, /* domainStreamDisk */ + NULL, /* domainStreamDiskInfo */ }; static int diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index ddbca97..b317a11 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -8464,6 +8464,8 @@ virDriver NAME(Driver) = { NULL, /* qemuDomainMonitorCommand */ NULL, /* domainSetMemoryParameters */ NULL, /* domainGetMemoryParameters */ + NULL, /* domainStreamDisk */ + NULL, /* domainStreamDiskInfo */ }; virNetworkDriver NAME(NetworkDriver) = { diff --git a/src/xen/xen_driver.c b/src/xen/xen_driver.c index 66e8518..08550a7 100644 --- a/src/xen/xen_driver.c +++ b/src/xen/xen_driver.c @@ -2087,6 +2087,8 @@ static virDriver xenUnifiedDriver = { NULL, /* qemuDomainMonitorCommand */ NULL, /* domainSetMemoryParameters */ NULL, /* domainGetMemoryParameters */ + NULL, /* domainStreamDisk */ + NULL, /* domainStreamDiskInfo */ }; /** -- 1.7.3.2.164.g6f10c

* src/libvirt.c: implement the main entry points * src/libvirt_public.syms: add them to the exported symbols Signed-off-by: Adam Litke <agl@us.ibm.com> --- src/libvirt.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 6 ++ 2 files changed, 127 insertions(+), 0 deletions(-) diff --git a/src/libvirt.c b/src/libvirt.c index aebd3bc..4244d23 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -5897,6 +5897,127 @@ error: } /** + * virDomainStreamDisk: + * @dom: pointer to the domain object + * @path: path to the block device + * @offset: when streaming a single disk unit, the offset of the unit to stream + * @flags: flags to control disk streaming behavior + * + * Returns: Next offset or 0 on success, -1 on failure. + */ +unsigned long long +virDomainStreamDisk(virDomainPtr dom, + const char *path, + unsigned long long offset, + unsigned int flags) +{ + virConnectPtr conn; + unsigned long long ret = -1; + + DEBUG("domain=%p, path=%p, offset=%llu, flags=%u", + dom, path, offset, flags); + + if (path == NULL) { + virLibDomainError (dom, VIR_ERR_INVALID_ARG, + _("path must not be NULL")); + goto error; + } + + if (flags == VIR_STREAM_DISK_START || flags == VIR_STREAM_DISK_STOP) { + if (offset != 0) { + virLibDomainError (dom, VIR_ERR_INVALID_ARG, + _("offset must be 0 for continuous streaming")); + goto error; + } + } else if (flags != VIR_STREAM_DISK_ONE) { + virLibDomainError (dom, VIR_ERR_INVALID_ARG, + _("Invalid value for flags")); + goto error; + } + + virResetLastError(); + if (!VIR_IS_CONNECTED_DOMAIN (dom)) { + virLibDomainError (NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return -1; + } + + conn = dom->conn; + if (conn->driver->domainStreamDisk) { + ret = conn->driver->domainStreamDisk (dom, path, offset, flags); + if (ret == -1) + goto error; + return ret; + } + + virLibDomainError (dom, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(dom->conn); + return -1; +} + +/** + * virDomainStreamDiskInfo: + * @dom: pointer to the domain object + * @states: An array of virStreamDiskState structures to store stream info + * @nr_states: The maximimum number of stream states to report + * @flags: future flags, use 0 for now + * + * Returns: The number of streams reported or -1 on failure. + */ +int +virDomainStreamDiskInfo(virDomainPtr dom, + virStreamDiskStatePtr states, + unsigned int nr_states, + unsigned int flags) +{ + virConnectPtr conn; + int ret = -1; + + DEBUG("dom=%p, states=%p, nr_states=%u, flags=%u", + dom, states, nr_states, flags); + + if (states == NULL) { + virLibDomainError (dom, VIR_ERR_INVALID_ARG, + _("states must not be NULL")); + goto error; + } + + if (nr_states == 0) { + virLibDomainError (dom, VIR_ERR_INVALID_ARG, + _("nr_states must be positive")); + goto error; + } + + if (flags != 0) { + virLibDomainError (dom, VIR_ERR_INVALID_ARG, + _("flags must be 0")); + goto error; + } + + virResetLastError(); + if (!VIR_IS_CONNECTED_DOMAIN (dom)) { + virLibDomainError (NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return -1; + } + + conn = dom->conn; + if (conn->driver->domainStreamDiskInfo) { + ret = conn->driver->domainStreamDiskInfo (dom, states, nr_states, + flags); + if (ret == -1) + goto error; + return ret; + } + + virLibDomainError (dom, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(dom->conn); + return -1; +} + +/** * virNodeGetCellsFreeMemory: * @conn: pointer to the hypervisor connection * @freeMems: pointer to the array of unsigned long long diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index a8091b1..a25596c 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -413,4 +413,10 @@ LIBVIRT_0.8.5 { virDomainSetVcpusFlags; } LIBVIRT_0.8.2; +LIBVIRT_0.8.6 { + global: + virDomainStreamDisk; + virDomainStreamDiskInfo; +} LIBVIRT_0.8.5; + # .... define new API here using predicted next version number .... -- 1.7.3.2.164.g6f10c

Add support for: starting/stopping full device streaming, streaming a single sector, and getting the status of streaming. These operations are done by using the 'stream' and 'info stream' qemu monitor commands. * src/qemu/qemu_driver.c src/qemu/qemu_monitor_text.[ch]: implement disk streaming by using the stream and info stream text monitor commands * src/qemu/qemu_monitor_json.[ch]: implement commands using the qmp monitor Signed-off-by: Adam Litke <agl@us.ibm.com> --- src/qemu/qemu_driver.c | 77 ++++++++++++++++++++- src/qemu/qemu_monitor.c | 41 +++++++++++ src/qemu/qemu_monitor.h | 6 ++ src/qemu/qemu_monitor_json.c | 104 ++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 7 ++ src/qemu/qemu_monitor_text.c | 156 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_text.h | 8 ++ 7 files changed, 397 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index dbde9e7..d7c049a 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -13143,6 +13143,79 @@ cleanup: return ret; } +static unsigned long long +qemudDomainStreamDisk (virDomainPtr dom, const char *path, + unsigned long long offset, unsigned int flags) +{ + struct qemud_driver *driver = dom->conn->privateData; + virDomainObjPtr vm; + unsigned long long ret = -1; + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + qemuDriverUnlock(driver); + + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(dom->uuid, uuidstr); + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + if (virDomainObjIsActive(vm)) { + qemuDomainObjPrivatePtr priv = vm->privateData; + qemuDomainObjEnterMonitor(vm); + ret = qemuMonitorStreamDisk(priv->mon, path, offset, flags); + qemuDomainObjExitMonitor(vm); + } else { + qemuReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + } + +cleanup: + if (vm) + virDomainObjUnlock(vm); + return ret; +} + +static int +qemudDomainStreamDiskInfo (virDomainPtr dom, virStreamDiskStatePtr states, + unsigned int nr_states, + unsigned int flags ATTRIBUTE_UNUSED) +{ + struct qemud_driver *driver = dom->conn->privateData; + virDomainObjPtr vm; + unsigned int ret = -1; + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + qemuDriverUnlock(driver); + + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(dom->uuid, uuidstr); + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + if (virDomainObjIsActive(vm)) { + qemuDomainObjPrivatePtr priv = vm->privateData; + qemuDomainObjEnterMonitor(vm); + ret = qemuMonitorStreamDiskInfo(priv->mon, states, nr_states); + qemuDomainObjExitMonitor(vm); + } else { + qemuReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + } + +cleanup: + if (vm) + virDomainObjUnlock(vm); + return ret; +} + static int qemuDomainMonitorCommand(virDomainPtr domain, const char *cmd, char **result, unsigned int flags) { @@ -13298,8 +13371,8 @@ static virDriver qemuDriver = { qemuDomainMonitorCommand, /* qemuDomainMonitorCommand */ qemuDomainSetMemoryParameters, /* domainSetMemoryParameters */ qemuDomainGetMemoryParameters, /* domainGetMemoryParameters */ - NULL, /* domainStreamDisk */ - NULL, /* domainStreamDiskInfo */ + qemudDomainStreamDisk, /* domainStreamDisk */ + qemudDomainStreamDiskInfo, /* domainStreamDiskInfo */ }; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 2366fdb..9169e23 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -1917,6 +1917,47 @@ int qemuMonitorDeleteSnapshot(qemuMonitorPtr mon, const char *name) return ret; } +unsigned long long +qemuMonitorStreamDisk(qemuMonitorPtr mon, const char *path, + unsigned long long offset, unsigned int flags) +{ + unsigned long long ret; + + DEBUG("mon=%p, path=%p, offset=%llu, flags=%u", mon, path, offset, flags); + + if (!mon) { + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("monitor must not be NULL")); + return -1; + } + + if (mon->json) + ret = qemuMonitorJSONStreamDisk(mon, path, offset, flags); + else + ret = qemuMonitorTextStreamDisk(mon, path, offset, flags); + return ret; +} + +int qemuMonitorStreamDiskInfo(qemuMonitorPtr mon, virStreamDiskStatePtr states, + unsigned int nr_states) +{ + int ret; + + DEBUG("mon=%p, states=%p, nr_states=%u", mon, states, nr_states); + + if (!mon) { + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("monitor must not be NULL")); + return -1; + } + + if (mon->json) + ret = qemuMonitorJSONStreamDiskInfo(mon, states, nr_states); + else + ret = qemuMonitorTextStreamDiskInfo(mon, states, nr_states); + return ret; +} + int qemuMonitorArbitraryCommand(qemuMonitorPtr mon, const char *cmd, char **reply) { int ret; diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 7d09145..719de76 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -389,6 +389,12 @@ int qemuMonitorCreateSnapshot(qemuMonitorPtr mon, const char *name); int qemuMonitorLoadSnapshot(qemuMonitorPtr mon, const char *name); int qemuMonitorDeleteSnapshot(qemuMonitorPtr mon, const char *name); +unsigned long long +qemuMonitorStreamDisk(qemuMonitorPtr mon, const char *path, + unsigned long long offset, unsigned int flags); +int qemuMonitorStreamDiskInfo(qemuMonitorPtr mon, virStreamDiskStatePtr states, + unsigned int nr_states); + int qemuMonitorArbitraryCommand(qemuMonitorPtr mon, const char *cmd, char **reply); /** diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index d2c6f0a..15e0c5b 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -2342,6 +2342,110 @@ int qemuMonitorJSONDeleteSnapshot(qemuMonitorPtr mon, const char *name) return ret; } +static int qemuMonitorJSONExtractStreamState(virJSONValuePtr reply, + virStreamDiskStatePtr state) +{ + virJSONValuePtr data; + int ret = -1; + const char *path; + unsigned long long offset, size; + + if (!(data = virJSONValueObjectGet(reply, "return"))) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("stream reply was missing return data")); + goto cleanup; + } + + if ((path = virJSONValueObjectGetString(data, "device"))) { + if (!virJSONValueObjectGetNumberUlong(data, "offset", &offset)) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("stream reply was missing offset")); + goto cleanup; + } + if (!virJSONValueObjectGetNumberUlong(data, "size", &size)) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("stream reply was missing size")); + goto cleanup; + } + + memcpy(state->path, path, strlen(path)); + state->offset = offset; + state->size = size; + ret = 1; + } else { + /* No currently active streams */ + ret = 0; + } + +cleanup: + return ret; +} + +unsigned long long +qemuMonitorJSONStreamDisk(qemuMonitorPtr mon, const char *path, + unsigned long long offset, unsigned int flags) +{ + virJSONValuePtr cmd = NULL; + virJSONValuePtr reply = NULL; + struct _virStreamDiskState state; + int rc; + unsigned long long ret = -1; + + if (flags == VIR_STREAM_DISK_START) + cmd = qemuMonitorJSONMakeCommand("stream", "b:all", "true", + "s:device", path, NULL); + else if (flags == VIR_STREAM_DISK_STOP) + cmd = qemuMonitorJSONMakeCommand("stream", "b:stop", "true", + "s:device", path, NULL); + else if (flags == VIR_STREAM_DISK_ONE) + cmd = qemuMonitorJSONMakeCommand("stream", "s:device", path, + "i:offset", offset, NULL); + else + qemuReportError(VIR_ERR_INTERNAL_ERROR, "Invalid argument for flags: " + "%u", flags); + + if (!cmd) + return -1; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + goto cleanup; + rc = qemuMonitorJSONExtractStreamState(reply, &state); + if (rc == 0 && (flags == VIR_STREAM_DISK_START || + flags == VIR_STREAM_DISK_STOP)) + ret = 0; + if (rc == 1 && flags == VIR_STREAM_DISK_ONE) + ret = state.offset; + +cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + +int qemuMonitorJSONStreamDiskInfo(qemuMonitorPtr mon, + virStreamDiskStatePtr states, + unsigned int nr_states) +{ + virJSONValuePtr cmd = NULL; + virJSONValuePtr reply = NULL; + int ret = -1; + + /* Qemu only supports one stream at a time */ + nr_states = 1; + + cmd = qemuMonitorJSONMakeCommand("query-stream", NULL); + if (!cmd) + return -1; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + goto cleanup; + ret = qemuMonitorJSONExtractStreamState(reply, states); +cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon, const char *cmd_str, char **reply_str) diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 94806c1..f4db2b4 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -196,6 +196,13 @@ int qemuMonitorJSONCreateSnapshot(qemuMonitorPtr mon, const char *name); int qemuMonitorJSONLoadSnapshot(qemuMonitorPtr mon, const char *name); int qemuMonitorJSONDeleteSnapshot(qemuMonitorPtr mon, const char *name); +unsigned long long +qemuMonitorJSONStreamDisk(qemuMonitorPtr mon, const char *path, + unsigned long long offset, unsigned int flags); +int qemuMonitorJSONStreamDiskInfo(qemuMonitorPtr mon, + virStreamDiskStatePtr states, + unsigned int nr_states); + int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon, const char *cmd_str, char **reply_str); diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index d7e128c..115b220 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -2569,6 +2569,162 @@ cleanup: return ret; } +static int qemuMonitorParseStreamInfo(char *text, + virStreamDiskStatePtr state) +{ + char *p; + unsigned long long data; + unsigned int device_len; + + memset(state->path, 0, VIR_STREAM_PATH_BUFLEN); + state->offset = 0; + state->size = 0; + + if (strstr(text, "Device '") && strstr(text, "' not found")) { + qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", _("Device not found")); + return -1; + } + + if (strstr(text, "expects a sector size less than device length")) { + qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Offset parameter is greater than the device size")); + return -1; + } + + if (strstr(text, "Device '") && strstr(text, "' is in use")) { + qemuReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("Another streaming operation is in progress")); + return -1; + } + + if (strstr(text, "No active stream") || STREQ(text, "")) + return 0; + + if ((text = STRSKIP(text, "Streaming device ")) == NULL) + return -1; + + /* Parse the device path */ + p = strstr(text, ": Completed "); + if (!p) + return -1; + + device_len = (unsigned int)(p - text); + if (device_len >= VIR_STREAM_PATH_BUFLEN) { + qemuReportError(VIR_ERR_OPERATION_FAILED, "%s", + "Device name is too long"); + return -1; + } + + if (sprintf((char *)&state->path, "%.*s", device_len, text) < 0) { + qemuReportError(VIR_ERR_OPERATION_FAILED, "%s", + "Unable to store device name"); + return -1; + } + text = p + 12; /* Skip over ": Completed " */ + + /* Parse the current sector offset */ + if (virStrToLong_ull (text, &p, 10, &data)) + return -1; + state->offset = (size_t) data; + text = p; + + /* Parse the total number of sectors */ + if (!STRPREFIX(text, " of ")) + return -1; + text += 4; + if (virStrToLong_ull (text, &p, 10, &data)) + return -1; + state->size = (size_t) data; + text = p; + + /* Verify the ending */ + if (!STRPREFIX(text, " sectors")) + return -1; + + return 1; +} + +unsigned long long +qemuMonitorTextStreamDisk(qemuMonitorPtr mon, const char *path, + unsigned long long offset, unsigned int flags) +{ + char *cmd; + char *reply = NULL; + int rc; + unsigned long long ret = -1; + virStreamDiskState state; + + if (flags == VIR_STREAM_DISK_START) + rc = virAsprintf(&cmd, "stream -a %s", path); + else if (flags == VIR_STREAM_DISK_STOP) + rc = virAsprintf(&cmd, "stream -s %s", path); + else if (flags == VIR_STREAM_DISK_ONE) + rc = virAsprintf(&cmd, "stream %s %llu", path, offset); + else { + qemuReportError(VIR_ERR_OPERATION_INVALID, "%s%u", + _("invalid value for flags: "), flags); + return -1; + } + + if (rc < 0) { + virReportOOMError(); + return -1; + } + + if (qemuMonitorCommand(mon, cmd, &reply)) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("failed to perform stream command '%s'"), + cmd); + goto cleanup; + } + + rc = qemuMonitorParseStreamInfo(reply, &state); + if (rc == 0 && (flags == VIR_STREAM_DISK_START || + flags == VIR_STREAM_DISK_STOP)) + ret = 0; /* A successful full disk start or stop produces no output */ + if (rc == 1 && flags == VIR_STREAM_DISK_ONE) + ret = state.offset; + +cleanup: + VIR_FREE(cmd); + VIR_FREE(reply); + return ret; +} + +int qemuMonitorTextStreamDiskInfo(qemuMonitorPtr mon, + virStreamDiskStatePtr states, + unsigned int nr_states) +{ + char *cmd; + char *reply = NULL; + int ret = -1; + + /* Qemu only supports one stream at a time */ + nr_states = 1; + + if (virAsprintf(&cmd, "info stream") < 0) { + virReportOOMError(); + return -1; + } + + if (qemuMonitorCommand(mon, cmd, &reply)) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("failed to perform stream command '%s'"), + cmd); + goto cleanup; + } + + ret = qemuMonitorParseStreamInfo(reply, states); + if (ret == -1) + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("Failed to parse monitor output: '%s'"), reply); + +cleanup: + VIR_FREE(cmd); + VIR_FREE(reply); + return ret; +} + int qemuMonitorTextArbitraryCommand(qemuMonitorPtr mon, const char *cmd, char **reply) { diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h index c017509..4d4aaa3 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -194,6 +194,14 @@ int qemuMonitorTextCreateSnapshot(qemuMonitorPtr mon, const char *name); int qemuMonitorTextLoadSnapshot(qemuMonitorPtr mon, const char *name); int qemuMonitorTextDeleteSnapshot(qemuMonitorPtr mon, const char *name); +unsigned long long +qemuMonitorTextStreamDisk(qemuMonitorPtr mon, const char *path, + unsigned long long offset, unsigned int flags); +int qemuMonitorTextStreamDiskInfo(qemuMonitorPtr mon, + virStreamDiskStatePtr states, + unsigned int nr_states); + + int qemuMonitorTextArbitraryCommand(qemuMonitorPtr mon, const char *cmd, char **reply); -- 1.7.3.2.164.g6f10c

* src/remote/remote_protocol.x: provide defines for the new entry points * src/remote/remote_driver.c daemon/remote.c: implement the client and server side * daemon/remote_dispatch_args.h daemon/remote_dispatch_prototypes.h daemon/remote_dispatch_ret.h daemon/remote_dispatch_table.h src/remote/remote_protocol.c src/remote/remote_protocol.h: generated stubs Signed-off-by: Adam Litke <agl@us.ibm.com> --- daemon/remote.c | 96 +++++++++++++++++++++++++++++++++++ daemon/remote_dispatch_args.h | 2 + daemon/remote_dispatch_prototypes.h | 16 ++++++ daemon/remote_dispatch_ret.h | 2 + daemon/remote_dispatch_table.h | 10 ++++ src/remote/remote_driver.c | 87 +++++++++++++++++++++++++++++++- src/remote/remote_protocol.c | 63 +++++++++++++++++++++++ src/remote/remote_protocol.h | 51 ++++++++++++++++++ src/remote/remote_protocol.x | 37 +++++++++++++- 9 files changed, 361 insertions(+), 3 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 886d53d..69709b3 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -6591,6 +6591,102 @@ cleanup: return rc; } +static int +remoteDispatchDomainStreamDisk (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *rerr, + remote_domain_stream_disk_args *args, + remote_domain_stream_disk_ret *ret) +{ + virDomainPtr dom; + const char *path; + unsigned long long offset; + unsigned int flags; + int rc; + + dom = get_nonnull_domain (conn, args->dom); + if (dom == NULL) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + path = args->path; + offset = args->offset; + flags = args->flags; + + rc = virDomainStreamDisk(dom, path, offset, flags); + if (rc == (unsigned long long) -1) { + remoteDispatchConnError(rerr, conn); + return -1; + } + ret->offset = rc; + return 0; +} + +static int +remoteDispatchDomainStreamDiskInfo (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *rerr, + remote_domain_stream_disk_info_args *args, + remote_domain_stream_disk_info_ret *ret) +{ + virDomainPtr dom; + struct _virStreamDiskState *states; + unsigned int nr_states, flags, i; + int nr_returned; + + if (args->nr_results > REMOTE_DOMAIN_STREAM_DISK_STATES_MAX) { + remoteDispatchFormatError (rerr, "%s", + _("nr_results > REMOTE_DOMAIN_STREAM_DISK_STATES_MAX")); + return -1; + } + + dom = get_nonnull_domain (conn, args->dom); + if (dom == NULL) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + nr_states = args->nr_results; + flags = args->flags; + + /* Allocate array of stats structs for making dispatch call */ + if (VIR_ALLOC_N(states, nr_states) < 0) { + virDomainFree (dom); + remoteDispatchOOMError(rerr); + return -1; + } + + nr_returned = virDomainStreamDiskInfo (dom, states, nr_states, flags); + virDomainFree (dom); + if (nr_returned < 0) { + VIR_FREE(states); + remoteDispatchConnError(rerr, conn); + return -1; + } + + /* Allocate return buffer */ + if (VIR_ALLOC_N(ret->states.states_val, nr_returned) < 0) { + VIR_FREE(states); + remoteDispatchOOMError(rerr); + return -1; + } + + /* Copy the stats into the return structure */ + ret->states.states_len = nr_returned; + for (i = 0; i < nr_returned; i++) { + ret->states.states_val[i].path.path_val = strdup(states[i].path); + ret->states.states_val[i].path.path_len = strlen(states[i].path); + ret->states.states_val[i].offset = states[i].offset; + ret->states.states_val[i].size = states[i].size; + } + VIR_FREE(states); + return 0; +} static int remoteDispatchDomainEventsRegisterAny (struct qemud_server *server ATTRIBUTE_UNUSED, diff --git a/daemon/remote_dispatch_args.h b/daemon/remote_dispatch_args.h index 9583e9c..91cbd52 100644 --- a/daemon/remote_dispatch_args.h +++ b/daemon/remote_dispatch_args.h @@ -169,3 +169,5 @@ remote_domain_get_memory_parameters_args val_remote_domain_get_memory_parameters_args; remote_domain_set_vcpus_flags_args val_remote_domain_set_vcpus_flags_args; remote_domain_get_vcpus_flags_args val_remote_domain_get_vcpus_flags_args; + remote_domain_stream_disk_args val_remote_domain_stream_disk_args; + remote_domain_stream_disk_info_args val_remote_domain_stream_disk_info_args; diff --git a/daemon/remote_dispatch_prototypes.h b/daemon/remote_dispatch_prototypes.h index 6b35851..491bc96 100644 --- a/daemon/remote_dispatch_prototypes.h +++ b/daemon/remote_dispatch_prototypes.h @@ -634,6 +634,22 @@ static int remoteDispatchDomainSnapshotNum( remote_error *err, remote_domain_snapshot_num_args *args, remote_domain_snapshot_num_ret *ret); +static int remoteDispatchDomainStreamDisk( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_message_header *hdr, + remote_error *err, + remote_domain_stream_disk_args *args, + remote_domain_stream_disk_ret *ret); +static int remoteDispatchDomainStreamDiskInfo( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_message_header *hdr, + remote_error *err, + remote_domain_stream_disk_info_args *args, + remote_domain_stream_disk_info_ret *ret); static int remoteDispatchDomainSuspend( struct qemud_server *server, struct qemud_client *client, diff --git a/daemon/remote_dispatch_ret.h b/daemon/remote_dispatch_ret.h index 3723b00..84d8ba4 100644 --- a/daemon/remote_dispatch_ret.h +++ b/daemon/remote_dispatch_ret.h @@ -137,3 +137,5 @@ remote_domain_create_with_flags_ret val_remote_domain_create_with_flags_ret; remote_domain_get_memory_parameters_ret val_remote_domain_get_memory_parameters_ret; remote_domain_get_vcpus_flags_ret val_remote_domain_get_vcpus_flags_ret; + remote_domain_stream_disk_ret val_remote_domain_stream_disk_ret; + remote_domain_stream_disk_info_ret val_remote_domain_stream_disk_info_ret; diff --git a/daemon/remote_dispatch_table.h b/daemon/remote_dispatch_table.h index dd2adc7..fac32d3 100644 --- a/daemon/remote_dispatch_table.h +++ b/daemon/remote_dispatch_table.h @@ -1007,3 +1007,13 @@ .args_filter = (xdrproc_t) xdr_remote_domain_get_vcpus_flags_args, .ret_filter = (xdrproc_t) xdr_remote_domain_get_vcpus_flags_ret, }, +{ /* DomainStreamDisk => 201 */ + .fn = (dispatch_fn) remoteDispatchDomainStreamDisk, + .args_filter = (xdrproc_t) xdr_remote_domain_stream_disk_args, + .ret_filter = (xdrproc_t) xdr_remote_domain_stream_disk_ret, +}, +{ /* DomainStreamDiskInfo => 202 */ + .fn = (dispatch_fn) remoteDispatchDomainStreamDiskInfo, + .args_filter = (xdrproc_t) xdr_remote_domain_stream_disk_info_args, + .ret_filter = (xdrproc_t) xdr_remote_domain_stream_disk_info_ret, +}, diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 237e03b..47705e7 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -8948,6 +8948,89 @@ done: return rv; } +static unsigned long long +remoteDomainStreamDisk(virDomainPtr domain, const char *path, + unsigned long long offset, unsigned int flags) +{ + unsigned long long rv = (unsigned long long) -1; + + remote_domain_stream_disk_args args; + remote_domain_stream_disk_ret ret; + struct private_data *priv = domain->conn->privateData; + + args.offset = offset; + args.flags = flags; + args.path = strdup(path); + if (args.path == NULL) { + virReportOOMError(); + return -1; + } + + remoteDriverLock(priv); + make_nonnull_domain (&args.dom, domain); + memset (&ret, 0, sizeof ret); + + if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_STREAM_DISK, + (xdrproc_t) xdr_remote_domain_stream_disk_args, + (char *) &args, + (xdrproc_t) xdr_remote_domain_stream_disk_ret, + (char *) &ret) == -1) + goto done; + + rv = ret.offset; + xdr_free((xdrproc_t) xdr_remote_domain_stream_disk_ret, (char *) &ret); + +done: + remoteDriverUnlock(priv); + VIR_FREE(args.path); + return rv; +} + +static int remoteDomainStreamDiskInfo(virDomainPtr domain, + virStreamDiskStatePtr states, + unsigned int nr_states, + unsigned int flags) +{ + int rv = -1; + remote_domain_stream_disk_info_args args; + remote_domain_stream_disk_info_ret ret; + struct private_data *priv = domain->conn->privateData; + unsigned int i; + + remoteDriverLock(priv); + + make_nonnull_domain (&args.dom, domain); + if (nr_states > REMOTE_DOMAIN_STREAM_DISK_STATES_MAX) { + remoteError(VIR_ERR_RPC, + _("too many disk stream stats requested: %d > %d"), + nr_states, REMOTE_DOMAIN_STREAM_DISK_STATES_MAX); + goto done; + } + args.nr_results = nr_states; + args.flags = flags; + memset (&ret, 0, sizeof ret); + + if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_STREAM_DISK_INFO, + (xdrproc_t) xdr_remote_domain_stream_disk_info_args, + (char *) &args, + (xdrproc_t) xdr_remote_domain_stream_disk_info_ret, + (char *) &ret) == -1) + goto done; + + for (i = 0; i < ret.states.states_len; i++) { + strncpy (states[i].path, ret.states.states_val[i].path.path_val, + VIR_STREAM_PATH_BUFLEN); + states[i].offset = ret.states.states_val[i].offset; + states[i].size = ret.states.states_val[i].size; + } + rv = ret.states.states_len; + xdr_free((xdrproc_t) xdr_remote_domain_stream_disk_info_ret, (char *) &ret); + +done: + remoteDriverUnlock(priv); + return rv; +} + static int remoteDomainEventRegisterAny(virConnectPtr conn, virDomainPtr dom, int eventID, @@ -10540,8 +10623,8 @@ static virDriver remote_driver = { remoteQemuDomainMonitorCommand, /* qemuDomainMonitorCommand */ remoteDomainSetMemoryParameters, /* domainSetMemoryParameters */ remoteDomainGetMemoryParameters, /* domainGetMemoryParameters */ - NULL, /* domainStreamDisk */ - NULL, /* domainStreamDiskInfo */ + remoteDomainStreamDisk, /* domainStreamDisk */ + remoteDomainStreamDiskInfo, /* domainStreamDiskInfo */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.c b/src/remote/remote_protocol.c index 38ea050..0d072cd 100644 --- a/src/remote/remote_protocol.c +++ b/src/remote/remote_protocol.c @@ -1574,6 +1574,69 @@ xdr_remote_domain_set_autostart_args (XDR *xdrs, remote_domain_set_autostart_arg } bool_t +xdr_remote_domain_stream_disk_args (XDR *xdrs, remote_domain_stream_disk_args *objp) +{ + + if (!xdr_remote_nonnull_domain (xdrs, &objp->dom)) + return FALSE; + if (!xdr_remote_nonnull_string (xdrs, &objp->path)) + return FALSE; + if (!xdr_uint64_t (xdrs, &objp->offset)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->flags)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_stream_disk_ret (XDR *xdrs, remote_domain_stream_disk_ret *objp) +{ + + if (!xdr_uint64_t (xdrs, &objp->offset)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_stream_disk_info_args (XDR *xdrs, remote_domain_stream_disk_info_args *objp) +{ + + if (!xdr_remote_nonnull_domain (xdrs, &objp->dom)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->nr_results)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->flags)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_stream_disk_state (XDR *xdrs, remote_stream_disk_state *objp) +{ + char **objp_cpp0 = (char **) (void *) &objp->path.path_val; + + if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->path.path_len, VIR_STREAM_PATH_BUFLEN, + sizeof (char), (xdrproc_t) xdr_char)) + return FALSE; + if (!xdr_uint64_t (xdrs, &objp->offset)) + return FALSE; + if (!xdr_uint64_t (xdrs, &objp->size)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_stream_disk_info_ret (XDR *xdrs, remote_domain_stream_disk_info_ret *objp) +{ + char **objp_cpp0 = (char **) (void *) &objp->states.states_val; + + if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->states.states_len, REMOTE_DOMAIN_STREAM_DISK_STATES_MAX, + sizeof (remote_stream_disk_state), (xdrproc_t) xdr_remote_stream_disk_state)) + return FALSE; + return TRUE; +} + +bool_t xdr_remote_num_of_networks_ret (XDR *xdrs, remote_num_of_networks_ret *objp) { diff --git a/src/remote/remote_protocol.h b/src/remote/remote_protocol.h index d75e76c..91aa6cf 100644 --- a/src/remote/remote_protocol.h +++ b/src/remote/remote_protocol.h @@ -67,6 +67,7 @@ typedef remote_nonnull_string *remote_string; #define REMOTE_SECRET_VALUE_MAX 65536 #define REMOTE_SECRET_UUID_LIST_MAX 16384 #define REMOTE_CPU_BASELINE_MAX 256 +#define REMOTE_DOMAIN_STREAM_DISK_STATES_MAX VIR_STREAM_DISK_MAX_STREAMS typedef char remote_uuid[VIR_UUID_BUFLEN]; @@ -882,6 +883,44 @@ struct remote_domain_set_autostart_args { }; typedef struct remote_domain_set_autostart_args remote_domain_set_autostart_args; +struct remote_domain_stream_disk_args { + remote_nonnull_domain dom; + remote_nonnull_string path; + uint64_t offset; + u_int flags; +}; +typedef struct remote_domain_stream_disk_args remote_domain_stream_disk_args; + +struct remote_domain_stream_disk_ret { + uint64_t offset; +}; +typedef struct remote_domain_stream_disk_ret remote_domain_stream_disk_ret; + +struct remote_domain_stream_disk_info_args { + remote_nonnull_domain dom; + u_int nr_results; + u_int flags; +}; +typedef struct remote_domain_stream_disk_info_args remote_domain_stream_disk_info_args; + +struct remote_stream_disk_state { + struct { + u_int path_len; + char *path_val; + } path; + uint64_t offset; + uint64_t size; +}; +typedef struct remote_stream_disk_state remote_stream_disk_state; + +struct remote_domain_stream_disk_info_ret { + struct { + u_int states_len; + remote_stream_disk_state *states_val; + } states; +}; +typedef struct remote_domain_stream_disk_info_ret remote_domain_stream_disk_info_ret; + struct remote_num_of_networks_ret { int num; }; @@ -2301,6 +2340,8 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_GET_MEMORY_PARAMETERS = 198, REMOTE_PROC_DOMAIN_SET_VCPUS_FLAGS = 199, REMOTE_PROC_DOMAIN_GET_VCPUS_FLAGS = 200, + REMOTE_PROC_DOMAIN_STREAM_DISK = 201, + REMOTE_PROC_DOMAIN_STREAM_DISK_INFO = 202, }; typedef enum remote_procedure remote_procedure; @@ -2461,6 +2502,11 @@ extern bool_t xdr_remote_domain_update_device_flags_args (XDR *, remote_domain_ extern bool_t xdr_remote_domain_get_autostart_args (XDR *, remote_domain_get_autostart_args*); extern bool_t xdr_remote_domain_get_autostart_ret (XDR *, remote_domain_get_autostart_ret*); extern bool_t xdr_remote_domain_set_autostart_args (XDR *, remote_domain_set_autostart_args*); +extern bool_t xdr_remote_domain_stream_disk_args (XDR *, remote_domain_stream_disk_args*); +extern bool_t xdr_remote_domain_stream_disk_ret (XDR *, remote_domain_stream_disk_ret*); +extern bool_t xdr_remote_domain_stream_disk_info_args (XDR *, remote_domain_stream_disk_info_args*); +extern bool_t xdr_remote_stream_disk_state (XDR *, remote_stream_disk_state*); +extern bool_t xdr_remote_domain_stream_disk_info_ret (XDR *, remote_domain_stream_disk_info_ret*); extern bool_t xdr_remote_num_of_networks_ret (XDR *, remote_num_of_networks_ret*); extern bool_t xdr_remote_list_networks_args (XDR *, remote_list_networks_args*); extern bool_t xdr_remote_list_networks_ret (XDR *, remote_list_networks_ret*); @@ -2804,6 +2850,11 @@ extern bool_t xdr_remote_domain_update_device_flags_args (); extern bool_t xdr_remote_domain_get_autostart_args (); extern bool_t xdr_remote_domain_get_autostart_ret (); extern bool_t xdr_remote_domain_set_autostart_args (); +extern bool_t xdr_remote_domain_stream_disk_args (); +extern bool_t xdr_remote_domain_stream_disk_ret (); +extern bool_t xdr_remote_domain_stream_disk_info_args (); +extern bool_t xdr_remote_stream_disk_state (); +extern bool_t xdr_remote_domain_stream_disk_info_ret (); extern bool_t xdr_remote_num_of_networks_ret (); extern bool_t xdr_remote_list_networks_args (); extern bool_t xdr_remote_list_networks_ret (); diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index d57e6d0..e6de63e 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -188,6 +188,11 @@ const REMOTE_SECRET_UUID_LIST_MAX = 16384; */ const REMOTE_CPU_BASELINE_MAX = 256; +/* + * Maximum number of active disk streams that can be reported + */ +const REMOTE_DOMAIN_STREAM_DISK_STATES_MAX = VIR_STREAM_DISK_MAX_STREAMS; + /* UUID. VIR_UUID_BUFLEN definition comes from libvirt.h */ typedef opaque remote_uuid[VIR_UUID_BUFLEN]; @@ -863,6 +868,33 @@ struct remote_domain_set_autostart_args { int autostart; }; +struct remote_domain_stream_disk_args { + remote_nonnull_domain dom; + remote_nonnull_string path; + unsigned hyper offset; + unsigned int flags; +}; + +struct remote_domain_stream_disk_ret { + unsigned hyper offset; +}; + +struct remote_domain_stream_disk_info_args { + remote_nonnull_domain dom; + unsigned int nr_results; + unsigned int flags; +}; + +struct remote_stream_disk_state { + char path<VIR_STREAM_PATH_BUFLEN>; + unsigned hyper offset; + unsigned hyper size; +}; + +struct remote_domain_stream_disk_info_ret { + remote_stream_disk_state states<REMOTE_DOMAIN_STREAM_DISK_STATES_MAX>; +}; + /* Network calls: */ struct remote_num_of_networks_ret { @@ -2079,7 +2111,10 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_SET_MEMORY_PARAMETERS = 197, REMOTE_PROC_DOMAIN_GET_MEMORY_PARAMETERS = 198, REMOTE_PROC_DOMAIN_SET_VCPUS_FLAGS = 199, - REMOTE_PROC_DOMAIN_GET_VCPUS_FLAGS = 200 + REMOTE_PROC_DOMAIN_GET_VCPUS_FLAGS = 200, + + REMOTE_PROC_DOMAIN_STREAM_DISK = 201, + REMOTE_PROC_DOMAIN_STREAM_DISK_INFO = 202 /* * Notice how the entries are grouped in sets of 10 ? -- 1.7.3.2.164.g6f10c

Define two new virsh commands: one to control disk streaming and one to print the status of active disk streams. * tools/virsh.c: implement the new commands Signed-off-by: Adam Litke <agl@us.ibm.com> --- tools/virsh.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 157 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index ab83976..17957ac 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -229,6 +229,8 @@ static char *vshCommandOptString(const vshCmd *cmd, const char *name, int *found); static long long vshCommandOptLongLong(const vshCmd *cmd, const char *name, int *found); +static unsigned long long vshCommandOptULL(const vshCmd *cmd, const char *name, + int *found); static int vshCommandOptBool(const vshCmd *cmd, const char *name); static char *vshCommandOptArgv(const vshCmd *cmd, int count); @@ -3490,6 +3492,141 @@ done: } /* + * "domstreamdisk" command + */ +static const vshCmdInfo info_domstreamdisk[] = { + {"help", gettext_noop("Stream data to a disk")}, + {"desc", gettext_noop("Stream data to a disk connected to a running domain")}, + { NULL, NULL }, +}; + +static const vshCmdOptDef opts_domstreamdisk[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("domain name, id or uuid")}, + {"start", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("Start streaming a disk") }, + {"stop", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("Stop streaming a disk") }, + {"incremental", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("Perform an incremental stream") }, + {"path", VSH_OT_DATA, VSH_OFLAG_REQ, N_("block device")}, + {"offset", VSH_OT_DATA, VSH_OFLAG_NONE, N_("Device offset for incremental stream")}, + { NULL, 0, 0, NULL }, +}; + +static int +cmdDomStreamDisk(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom; + char *name; + const char *path; + int found; + unsigned long long offset, next; + unsigned int flags, start, stop, incr; + + if (!vshConnectionUsability(ctl, ctl->conn)) + return FALSE; + + flags = start = stop = incr = 0; + if (vshCommandOptBool(cmd, "start")) { + start = 1; + flags = VIR_STREAM_DISK_START; + } + if (vshCommandOptBool(cmd, "stop")) { + stop = 1; + flags = VIR_STREAM_DISK_STOP; + } + if (vshCommandOptBool(cmd, "incremental")) { + incr = 1; + flags = VIR_STREAM_DISK_ONE; + } + if (start + stop + incr != 1) { + vshError(ctl, _("Exactly one mode: --start, --stop, --incremental, " + "is required")); + return FALSE; + } + + if (!(dom = vshCommandOptDomain(ctl, cmd, &name))) + return FALSE; + + path = vshCommandOptString(cmd, "path", NULL); + + if (flags == VIR_STREAM_DISK_ONE) { + offset = vshCommandOptULL(cmd, "offset", &found); + if (!found) { + vshError(ctl, _("An offset is required for incremental streaming")); + virDomainFree(dom); + return FALSE; + } + } else { + offset = 0; + } + + next = virDomainStreamDisk(dom, path, offset, flags); + if (next == (unsigned long long) -1) { + vshError(ctl, _("Stream operation failed for the device" + "'%s' connected to the domain '%s'"), path, name); + virDomainFree(dom); + return FALSE; + } + + if (flags == VIR_STREAM_DISK_START) + vshPrint (ctl, "Stream successfully started\n"); + else if (flags == VIR_STREAM_DISK_STOP) + vshPrint (ctl, "Stream successfully stopped\n"); + else + vshPrint (ctl, "Strem successful. Continue at offset %llu\n", next); + + virDomainFree(dom); + return TRUE; +} + +/* + * "domstreamdiskinfo" command + */ +static const vshCmdInfo info_domstreamdiskinfo[] = { + {"help", gettext_noop("Get disk streaming status for a domain")}, + {"desc", gettext_noop("Get disk streaming status for a running domain")}, + { NULL, NULL }, +}; + +static const vshCmdOptDef opts_domstreamdiskinfo[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("domain name, id or uuid")}, + { NULL, 0, 0, NULL }, +}; + +static int +cmdDomStreamDiskInfo(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom; + char *name; + struct _virStreamDiskState streams[VIR_STREAM_DISK_MAX_STREAMS]; + int nr_streams, i; + + if (!vshConnectionUsability(ctl, ctl->conn)) + return FALSE; + + if (!(dom = vshCommandOptDomain(ctl, cmd, &name))) + return FALSE; + + nr_streams = virDomainStreamDiskInfo(dom, streams, + VIR_STREAM_DISK_MAX_STREAMS, 0); + if (nr_streams < 0) { + vshError(ctl, _("Failed to get disk stream information for domain %s"), + name); + virDomainFree(dom); + return FALSE; + } + + vshPrint (ctl, "%-30s %-10s %-10s\n", _("Device"), _("Offset"), + _("Size")); + vshPrint (ctl, "----------------------------------------------------\n"); + for (i = 0; i < nr_streams; i++) { + vshPrint (ctl, "%-30s %-10llu %-10llu\n", streams[i].path, + streams[i].offset, streams[i].size); + } + + virDomainFree(dom); + return TRUE; +} + +/* * "net-autostart" command */ static const vshCmdInfo info_network_autostart[] = { @@ -9854,6 +9991,8 @@ static const vshCmdDef commands[] = { {"maxvcpus", cmdMaxvcpus, opts_maxvcpus, info_maxvcpus}, {"migrate", cmdMigrate, opts_migrate, info_migrate}, {"migrate-setmaxdowntime", cmdMigrateSetMaxDowntime, opts_migrate_setmaxdowntime, info_migrate_setmaxdowntime}, + {"domstreamdisk", cmdDomStreamDisk, opts_domstreamdisk, info_domstreamdisk}, + {"domstreamdiskinfo", cmdDomStreamDiskInfo, opts_domstreamdiskinfo, info_domstreamdiskinfo}, {"net-autostart", cmdNetworkAutostart, opts_network_autostart, info_network_autostart}, {"net-create", cmdNetworkCreate, opts_network_create, info_network_create}, @@ -10288,6 +10427,24 @@ vshCommandOptLongLong(const vshCmd *cmd, const char *name, int *found) } /* + * Returns option as unsigned long long + */ +static unsigned long long +vshCommandOptULL(const vshCmd *cmd, const char *name, int *found) +{ + vshCmdOpt *arg = vshCommandOpt(cmd, name); + int num_found = FALSE; + unsigned long long res = 0; + char *end_p = NULL; + + if ((arg != NULL) && (arg->data != NULL)) + num_found = !virStrToLong_ull(arg->data, &end_p, 10, &res); + if (found) + *found = num_found; + return res; +} + +/* * Returns TRUE/FALSE if the option exists */ static int -- 1.7.3.2.164.g6f10c

Enable virDomainStreamDiskInfo in the python API. dom.StreamDiskInfo() will return a list containing a dictionary for each active stream. Each dictionary contains items to report: the disk alias, the current stream offset, and the total disk size. virDomainStreamDisk() works with the automatic wrappers. * python/generator.py: reenable bindings for this entry point * python/libvirt-override-api.xml python/libvirt-override.c: the generator can't handle this new function, add the new binding, and the XML description Signed-off-by: Adam Litke <agl@us.ibm.com> --- python/generator.py | 4 +-- python/libvirt-override-api.xml | 5 ++++ python/libvirt-override.c | 46 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/python/generator.py b/python/generator.py index 2477b59..d523afd 100755 --- a/python/generator.py +++ b/python/generator.py @@ -161,8 +161,6 @@ def enum(type, name, value): functions_failed = [] functions_skipped = [ "virConnectListDomains", - "virDomainStreamDisk", - "virDomainStreamDiskInfo", ] skipped_modules = { @@ -177,7 +175,6 @@ skipped_types = { 'virConnectDomainEventIOErrorCallback': "No function types in python", 'virConnectDomainEventGraphicsCallback': "No function types in python", 'virEventAddHandleFunc': "No function types in python", - 'virStreamDiskStatePtr': "Not implemented yet", } ####################################################################### @@ -337,6 +334,7 @@ skip_impl = ( 'virNodeDeviceListCaps', 'virConnectBaselineCPU', 'virDomainRevertToSnapshot', + 'virDomainStreamDiskInfo', ) diff --git a/python/libvirt-override-api.xml b/python/libvirt-override-api.xml index f209608..207a874 100644 --- a/python/libvirt-override-api.xml +++ b/python/libvirt-override-api.xml @@ -296,5 +296,10 @@ <arg name='flags' type='unsigned int' info='flags, curently unused'/> <return type='int' info="0 on success, -1 on error"/> </function> + <function name='virDomainStreamDiskInfo' file='python'> + <info>collect information about active disk streams</info> + <arg name='domain' type='virDomainPtr' info='pointer to the domain'/> + <return type='virDomainStreamDiskInfo *' info='A list containing one dictionary of statistics for each active stream' /> + </function> </symbols> </api> diff --git a/python/libvirt-override.c b/python/libvirt-override.c index 4a03d72..4b91279 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -3491,6 +3491,51 @@ libvirt_virConnectDomainEventDeregisterAny(ATTRIBUTE_UNUSED PyObject * self, return (py_retval); } +static PyObject * +libvirt_virDomainStreamDiskInfo(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) { + virDomainPtr domain; + PyObject *pyobj_domain; + unsigned int nr_streams, i; + struct _virStreamDiskState streams[VIR_STREAM_DISK_MAX_STREAMS]; + PyObject *ret; + + if (!PyArg_ParseTuple(args, (char *)"O:virDomainStreamDiskInfo", + &pyobj_domain)) + return(NULL); + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + + nr_streams = virDomainStreamDiskInfo(domain, streams, + VIR_STREAM_DISK_MAX_STREAMS, 0); + if (nr_streams == -1) + return VIR_PY_NONE; + + if ((ret = PyList_New(nr_streams)) == NULL) + return VIR_PY_NONE; + + for (i = 0; i < nr_streams; i++) { + PyObject *dict = PyDict_New(); + if (dict == NULL) + goto error; + PyDict_SetItem(dict, libvirt_constcharPtrWrap("path"), + libvirt_constcharPtrWrap(streams[i].path)); + PyDict_SetItem(dict, libvirt_constcharPtrWrap("offset"), + libvirt_ulonglongWrap(streams[i].offset)); + PyDict_SetItem(dict, libvirt_constcharPtrWrap("size"), + libvirt_ulonglongWrap(streams[i].size)); + PyList_SetItem(ret, i, dict); + } + return ret; + +error: + for (i = 0; i < PyList_Size(ret); i++) { + PyObject *item = PyList_GET_ITEM(ret, i); + Py_XDECREF(item); + } + Py_DECREF(ret); + return VIR_PY_NONE; +} + /************************************************************************ * * @@ -3566,6 +3611,7 @@ static PyMethodDef libvirtMethods[] = { {(char *) "virDomainGetJobInfo", libvirt_virDomainGetJobInfo, METH_VARARGS, NULL}, {(char *) "virDomainSnapshotListNames", libvirt_virDomainSnapshotListNames, METH_VARARGS, NULL}, {(char *) "virDomainRevertToSnapshot", libvirt_virDomainRevertToSnapshot, METH_VARARGS, NULL}, + {(char *) "virDomainStreamDiskInfo", libvirt_virDomainStreamDiskInfo, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} }; -- 1.7.3.2.164.g6f10c
participants (1)
-
Adam Litke