[libvirt] [PATCH 0/9] Progress monitoring & cancellation of jobs

This series introduces 2 new APIs to allow long running jobs to be monitored and cancelled. This applies to virDomainMigrate, virDomainSave, virDomainRestore and virDomainCoreDump at least. The implementation is provided for QEMU only, any others that are able to implementation can do so as desired. Usefulness of these new APIs requires use of multiple threads on a single connection, or multiple connections, since the main API calls are still all fully blocking. Instead a parallel thread or process can monitor. This can be seen with virsh In termainl 1 $ virsh save demo demo.img In terminal 2 $ ./virsh domjobinfo demo Job type: Unbounded Time elapsed: 795 ms Data processed: 22446080 bytes Data remaining: 219119616 bytes Data total: 241565696 bytes Memory processed: 22446080 bytes Memory remaining: 219119616 bytes Memory total: 241565696 bytes $ ./virsh domjobinfo demo Job type: Unbounded Time elapsed: 1669 ms Data processed: 139784192 bytes Data remaining: 101781504 bytes Data total: 241565696 bytes Memory processed: 139784192 bytes Memory remaining: 101781504 bytes Memory total: 241565696 bytes $ ./virsh domjobabort demo Back in terminal 1 error: Failed to save domain demo to demo.img error: operation failed: Migration was cancelled by client

Introduce a new public API that provides a way to get progress info on currently running jobs on a virDomainpPtr. APIs that are initially within scope of this idea are virDomainMigrate virDomainMigrateToURI virDomainSave virDomainRestore virDomainCoreDump These all take a potentially long time and benefit from monitoring. The virDomainJobInfo struct allows for various pieces of information to be reported - Percentage completion - Time - Overall data - Guest memory data - Guest disk/file data * include/libvirt/libvirt.h.in: Add virDomainGetJobInfo * python/generator.py, python/libvirt-override-api.xml, python/libvirt-override.c: Override for virDomainGetJobInfo API * python/typewrappers.c, python/typewrappers.h: Introduce wrapper for unsigned long long type --- include/libvirt/libvirt.h.in | 48 +++++++++++++++++++++++++++++++++++++++ python/generator.py | 1 + python/libvirt-override-api.xml | 5 ++++ python/libvirt-override.c | 37 ++++++++++++++++++++++++++++++ python/typewrappers.c | 8 ++++++ python/typewrappers.h | 1 + 6 files changed, 100 insertions(+), 0 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index f192fb1..5058bc0 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1756,6 +1756,54 @@ int virConnectCompareCPU(virConnectPtr conn, unsigned int flags); +typedef enum { + VIR_DOMAIN_JOB_NONE = 0, /* No job is active */ + VIR_DOMAIN_JOB_BOUNDED = 1, /* Job with a finite completion time */ + VIR_DOMAIN_JOB_UNBOUNDED = 2, /* Job without a finite completion time */ +} virDomainJobType; + +typedef struct _virDomainJobInfo virDomainJobInfo; +typedef virDomainJobInfo *virDomainJobInfoPtr; +struct _virDomainJobInfo { + /* One of virDomainJobType */ + int type; + + /* Only for VIR_DOMAIN_JOB_BOUNDED */ + int percentComplete; + + /* Time is measured in seconds */ + unsigned long long timeElapsed; + unsigned long long timeRemaining; + + /* Data is measured in bytes unless otherwise specified + * and is measuring the job as a whole + * + * For VIR_DOMAIN_JOB_UNBOUNDED, dataTotal may be less + * than the final sum of dataProcessed + dataRemaining + * in the event that the hypervisor has to repeat some + * data eg due to dirtied pages during migration + * + * For VIR_DOMAIN_JOB_BOUNDED, dataTotal shall always + * equal sum of dataProcessed + dataRemaining + */ + unsigned long long dataTotal; + unsigned long long dataProcessed; + unsigned long long dataRemaining; + + /* As above, but only tracking guest memory progress */ + unsigned long long memTotal; + unsigned long long memProcessed; + unsigned long long memRemaining; + + /* As above, but only tracking guest disk file progress */ + unsigned long long fileTotal; + unsigned long long fileProcessed; + unsigned long long fileRemaining; +}; + +int virDomainGetJobInfo(virDomainPtr dom, + virDomainJobInfoPtr info); + #ifdef __cplusplus } #endif diff --git a/python/generator.py b/python/generator.py index 4182219..a0a3f58 100755 --- a/python/generator.py +++ b/python/generator.py @@ -271,6 +271,7 @@ skip_impl = ( 'virConnGetLastError', 'virGetLastError', 'virDomainGetInfo', + 'virDomainGetJobInfo', 'virNodeGetInfo', 'virDomainGetUUID', 'virDomainGetUUIDString', diff --git a/python/libvirt-override-api.xml b/python/libvirt-override-api.xml index 3d8f46c..efd21ef 100644 --- a/python/libvirt-override-api.xml +++ b/python/libvirt-override-api.xml @@ -48,6 +48,11 @@ <return type='int *' info='the list of information or None in case of error'/> <arg name='domain' type='virDomainPtr' info='a domain object'/> </function> + <function name='virDomainGetJobInfo' file='python'> + <info>Extract information about an active job being processed for a domain.</info> + <return type='int *' info='the list of information or None in case of error'/> + <arg name='domain' type='virDomainPtr' info='a domain object'/> + </function> <function name='virNodeGetInfo' file='python'> <info>Extract hardware information about the Node.</info> <return type='int *' info='the list of information or None in case of error'/> diff --git a/python/libvirt-override.c b/python/libvirt-override.c index d90a763..037dea1 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -2019,6 +2019,42 @@ libvirt_virConnectListDefinedInterfaces(PyObject *self ATTRIBUTE_UNUSED, return(py_retval); } +static PyObject * +libvirt_virDomainGetJobInfo(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { + PyObject *py_retval; + int c_retval; + virDomainPtr domain; + PyObject *pyobj_domain; + virDomainJobInfo info; + + if (!PyArg_ParseTuple(args, (char *)"O:virDomainGetJobInfo", &pyobj_domain)) + return(NULL); + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + + LIBVIRT_BEGIN_ALLOW_THREADS; + c_retval = virDomainGetJobInfo(domain, &info); + LIBVIRT_END_ALLOW_THREADS; + if (c_retval < 0) + return VIR_PY_NONE; + py_retval = PyList_New(13); + PyList_SetItem(py_retval, 0, libvirt_intWrap((int) info.type)); + PyList_SetItem(py_retval, 1, libvirt_intWrap(info.percentComplete)); + PyList_SetItem(py_retval, 2, libvirt_ulonglongWrap(info.timeElapsed)); + PyList_SetItem(py_retval, 3, libvirt_ulonglongWrap(info.timeRemaining)); + PyList_SetItem(py_retval, 4, libvirt_ulonglongWrap(info.dataTotal)); + PyList_SetItem(py_retval, 5, libvirt_ulonglongWrap(info.dataProcessed)); + PyList_SetItem(py_retval, 6, libvirt_ulonglongWrap(info.dataRemaining)); + PyList_SetItem(py_retval, 7, libvirt_ulonglongWrap(info.memTotal)); + PyList_SetItem(py_retval, 8, libvirt_ulonglongWrap(info.memProcessed)); + PyList_SetItem(py_retval, 9, libvirt_ulonglongWrap(info.memRemaining)); + PyList_SetItem(py_retval, 10, libvirt_ulonglongWrap(info.fileTotal)); + PyList_SetItem(py_retval, 11, libvirt_ulonglongWrap(info.fileProcessed)); + PyList_SetItem(py_retval, 12, libvirt_ulonglongWrap(info.fileRemaining)); + + return(py_retval); +} + + /******************************************* * Helper functions to avoid importing modules * for every callback @@ -2734,6 +2770,7 @@ static PyMethodDef libvirtMethods[] = { {(char *) "virSecretSetValue", libvirt_virSecretSetValue, METH_VARARGS, NULL}, {(char *) "virConnectListInterfaces", libvirt_virConnectListInterfaces, METH_VARARGS, NULL}, {(char *) "virConnectListDefinedInterfaces", libvirt_virConnectListDefinedInterfaces, METH_VARARGS, NULL}, + {(char *) "virDomainGetJobInfo", libvirt_virDomainGetJobInfo, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} }; diff --git a/python/typewrappers.c b/python/typewrappers.c index 9ba99de..b33822c 100644 --- a/python/typewrappers.c +++ b/python/typewrappers.c @@ -49,6 +49,14 @@ libvirt_longlongWrap(long long val) } PyObject * +libvirt_ulonglongWrap(unsigned long long val) +{ + PyObject *ret; + ret = PyLong_FromUnsignedLongLong(val); + return (ret); +} + +PyObject * libvirt_charPtrWrap(char *str) { PyObject *ret; diff --git a/python/typewrappers.h b/python/typewrappers.h index 61f7249..dadcdd4 100644 --- a/python/typewrappers.h +++ b/python/typewrappers.h @@ -138,6 +138,7 @@ PyObject * libvirt_intWrap(int val); PyObject * libvirt_longWrap(long val); PyObject * libvirt_ulongWrap(unsigned long val); PyObject * libvirt_longlongWrap(long long val); +PyObject * libvirt_ulonglongWrap(unsigned long long val); PyObject * libvirt_charPtrWrap(char *str); PyObject * libvirt_constcharPtrWrap(const char *str); PyObject * libvirt_charPtrConstWrap(const char *str); -- 1.6.6

The internal glue layer for the new pubic API * src/driver.h: Define internal driver API contract * src/libvirt.c, src/libvirt_public.syms: Wire up public API to internal driver API * src/esx/esx_driver.c, src/lxc/lxc_driver.c, src/opennebula/one_driver.c, src/openvz/openvz_driver.c, src/phyp/phyp_driver.c, src/qemu/qemu_driver.c, src/remote/remote_driver.c, src/test/test_driver.c, src/uml/uml_driver.c, src/vbox/vbox_tmpl.c, src/xen/xen_driver.c: Stub new entry point --- src/driver.h | 5 ++++ src/esx/esx_driver.c | 1 + src/libvirt.c | 51 +++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 5 ++++ src/lxc/lxc_driver.c | 1 + src/opennebula/one_driver.c | 1 + src/openvz/openvz_driver.c | 1 + src/phyp/phyp_driver.c | 1 + src/qemu/qemu_driver.c | 1 + src/remote/remote_driver.c | 1 + src/test/test_driver.c | 1 + src/uml/uml_driver.c | 1 + src/vbox/vbox_tmpl.c | 1 + src/xen/xen_driver.c | 1 + 14 files changed, 72 insertions(+), 0 deletions(-) diff --git a/src/driver.h b/src/driver.h index c7e4fbf..8f8592a 100644 --- a/src/driver.h +++ b/src/driver.h @@ -360,6 +360,10 @@ typedef int const char *cpu, unsigned int flags); +typedef int + (*virDrvDomainGetJobInfo)(virDomainPtr domain, + virDomainJobInfoPtr info); + /** * _virDriver: * @@ -448,6 +452,7 @@ struct _virDriver { virDrvDomainIsActive domainIsActive; virDrvDomainIsPersistent domainIsPersistent; virDrvCPUCompare cpuCompare; + virDrvDomainGetJobInfo domainGetJobInfo; }; typedef int diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c index fc254c6..e7312a7 100644 --- a/src/esx/esx_driver.c +++ b/src/esx/esx_driver.c @@ -3401,6 +3401,7 @@ static virDriver esxDriver = { esxDomainIsActive, /* domainIsActive */ esxDomainIsPersistent, /* domainIsPersistent */ NULL, /* cpuCompare */ + NULL, /* domainGetJobInfo */ }; diff --git a/src/libvirt.c b/src/libvirt.c index 8a9ee20..a5dedf7 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -10980,3 +10980,54 @@ error: virDispatchError(conn); return VIR_CPU_COMPARE_ERROR; } + + + +/** + * virDomainGetJobInfo: + * @domain: a domain object + * @info: pointer to a virDomainJobInfo structure allocated by the user + * + * Extract information about progress of a background job on a domain. + * Will return an error if the domain is not active. + * + * Returns 0 in case of success and -1 in case of failure. + */ +int +virDomainGetJobInfo(virDomainPtr domain, virDomainJobInfoPtr info) +{ + virConnectPtr conn; + DEBUG("domain=%p, info=%p", domain, info); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return (-1); + } + if (info == NULL) { + virLibDomainError(domain, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + memset(info, 0, sizeof(virDomainJobInfo)); + + conn = domain->conn; + + if (conn->driver->domainGetJobInfo) { + int ret; + ret = conn->driver->domainGetJobInfo (domain, info); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(domain->conn); + return -1; +} + + diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 0521158..7afdc05 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -349,4 +349,9 @@ LIBVIRT_0.7.5 { virDomainMemoryStats; } LIBVIRT_0.7.3; +LIBVIRT_0.7.7 { + global: + virDomainGetJobInfo; +} LIBVIRT_0.7.5; + # .... define new API here using predicted next version number .... diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index d78f7f7..983cab7 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -2456,6 +2456,7 @@ static virDriver lxcDriver = { lxcDomainIsActive, lxcDomainIsPersistent, NULL, /* cpuCompare */ + NULL, /* domainGetJobInfo */ }; static virStateDriver lxcStateDriver = { diff --git a/src/opennebula/one_driver.c b/src/opennebula/one_driver.c index ad7faca..2c1dd52 100644 --- a/src/opennebula/one_driver.c +++ b/src/opennebula/one_driver.c @@ -783,6 +783,7 @@ static virDriver oneDriver = { NULL, /* domainIsActive */ NULL, /* domainIsPersistent */ NULL, /* cpuCompare */ + NULL, /* domainGetJobInfo */ }; static virStateDriver oneStateDriver = { diff --git a/src/openvz/openvz_driver.c b/src/openvz/openvz_driver.c index 196fd8c..a7bf72d 100644 --- a/src/openvz/openvz_driver.c +++ b/src/openvz/openvz_driver.c @@ -1535,6 +1535,7 @@ static virDriver openvzDriver = { openvzDomainIsActive, openvzDomainIsPersistent, NULL, /* cpuCompare */ + NULL, /* domainGetJobInfo */ }; int openvzRegister(void) { diff --git a/src/phyp/phyp_driver.c b/src/phyp/phyp_driver.c index bd5cfc7..f723f03 100644 --- a/src/phyp/phyp_driver.c +++ b/src/phyp/phyp_driver.c @@ -1651,6 +1651,7 @@ virDriver phypDriver = { NULL, /* domainIsActive */ NULL, /* domainIsPersistent */ NULL, /* cpuCompare */ + NULL, /* domainGetJobInfo */ }; int diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 1e796ef..1e3a8b7 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -8606,6 +8606,7 @@ static virDriver qemuDriver = { qemuDomainIsActive, qemuDomainIsPersistent, qemuCPUCompare, /* cpuCompare */ + NULL, /* domainGetJobInfo */ }; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index d6f5fce..692c83c 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -8923,6 +8923,7 @@ static virDriver remote_driver = { remoteDomainIsActive, /* domainIsActive */ remoteDomainIsPersistent, /* domainIsPersistent */ remoteCPUCompare, /* cpuCompare */ + NULL, /* domainGetJobInfo */ }; static virNetworkDriver network_driver = { diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 2122a1b..d23fb68 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -5238,6 +5238,7 @@ static virDriver testDriver = { testDomainIsActive, /* domainIsActive */ testDomainIsPersistent, /* domainIsPersistent */ NULL, /* cpuCompare */ + NULL, /* domainGetJobInfo */ }; static virNetworkDriver testNetworkDriver = { diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c index 31cea5c..de42e26 100644 --- a/src/uml/uml_driver.c +++ b/src/uml/uml_driver.c @@ -1924,6 +1924,7 @@ static virDriver umlDriver = { umlDomainIsActive, umlDomainIsPersistent, NULL, /* cpuCompare */ + NULL, /* domainGetJobInfo */ }; diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index e40c848..5abb9f2 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -7035,6 +7035,7 @@ virDriver NAME(Driver) = { vboxDomainIsActive, vboxDomainIsPersistent, NULL, /* cpuCompare */ + NULL, /* domainGetJobInfo */ }; virNetworkDriver NAME(NetworkDriver) = { diff --git a/src/xen/xen_driver.c b/src/xen/xen_driver.c index 72f56ae..71fc9e9 100644 --- a/src/xen/xen_driver.c +++ b/src/xen/xen_driver.c @@ -1864,6 +1864,7 @@ static virDriver xenUnifiedDriver = { xenUnifiedDomainIsActive, xenUnifiedDomainisPersistent, NULL, /* cpuCompare */ + NULL, /* domainGetJobInfo */ }; /** -- 1.6.6

* src/remote/remote_protocol.x: Define wire protocol format for virDomainGetJobInfo API * src/remote/remote_driver.c, daemon/remote.c: Implement client and server marshalling code for virDomainGetJobInfo() * 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: Rebuild files from src/remote/remote_protocol.x --- daemon/remote.c | 44 +++++++++++++++++++++++++++++++++++ daemon/remote_dispatch_args.h | 1 + daemon/remote_dispatch_prototypes.h | 8 ++++++ daemon/remote_dispatch_ret.h | 1 + daemon/remote_dispatch_table.h | 5 ++++ src/remote/remote_driver.c | 43 +++++++++++++++++++++++++++++++++- src/remote/remote_protocol.c | 42 +++++++++++++++++++++++++++++++++ src/remote/remote_protocol.h | 27 +++++++++++++++++++++ src/remote/remote_protocol.x | 27 ++++++++++++++++++++- 9 files changed, 196 insertions(+), 2 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 299f971..e434591 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -5312,6 +5312,50 @@ remoteDispatchCpuCompare(struct qemud_server *server ATTRIBUTE_UNUSED, } +static int +remoteDispatchDomainGetJobInfo (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_get_job_info_args *args, + remote_domain_get_job_info_ret *ret) +{ + virDomainPtr dom; + virDomainJobInfo info; + + dom = get_nonnull_domain (conn, args->dom); + if (dom == NULL) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + if (virDomainGetJobInfo (dom, &info) == -1) { + virDomainFree(dom); + remoteDispatchConnError(rerr, conn); + return -1; + } + + ret->type = info.type; + ret->percentComplete = info.percentComplete; + ret->timeElapsed = info.timeElapsed; + ret->timeRemaining = info.timeRemaining; + ret->dataTotal = info.dataTotal; + ret->dataProcessed = info.dataProcessed; + ret->dataRemaining = info.dataRemaining; + ret->memTotal = info.memTotal; + ret->memProcessed = info.memProcessed; + ret->memRemaining = info.memRemaining; + ret->fileTotal = info.fileTotal; + ret->fileProcessed = info.fileProcessed; + ret->fileRemaining = info.fileRemaining; + + virDomainFree(dom); + + return 0; +} + + /*----- Helpers. -----*/ /* get_nonnull_domain and get_nonnull_network turn an on-wire diff --git a/daemon/remote_dispatch_args.h b/daemon/remote_dispatch_args.h index c1801d0..b66af1c 100644 --- a/daemon/remote_dispatch_args.h +++ b/daemon/remote_dispatch_args.h @@ -135,3 +135,4 @@ remote_interface_is_active_args val_remote_interface_is_active_args; remote_cpu_compare_args val_remote_cpu_compare_args; remote_domain_memory_stats_args val_remote_domain_memory_stats_args; + remote_domain_get_job_info_args val_remote_domain_get_job_info_args; diff --git a/daemon/remote_dispatch_prototypes.h b/daemon/remote_dispatch_prototypes.h index 1a0f8d8..b520f7d 100644 --- a/daemon/remote_dispatch_prototypes.h +++ b/daemon/remote_dispatch_prototypes.h @@ -170,6 +170,14 @@ static int remoteDispatchDomainGetInfo( remote_error *err, remote_domain_get_info_args *args, remote_domain_get_info_ret *ret); +static int remoteDispatchDomainGetJobInfo( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_message_header *hdr, + remote_error *err, + remote_domain_get_job_info_args *args, + remote_domain_get_job_info_ret *ret); static int remoteDispatchDomainGetMaxMemory( struct qemud_server *server, struct qemud_client *client, diff --git a/daemon/remote_dispatch_ret.h b/daemon/remote_dispatch_ret.h index d7811de..02a1734 100644 --- a/daemon/remote_dispatch_ret.h +++ b/daemon/remote_dispatch_ret.h @@ -117,3 +117,4 @@ remote_get_lib_version_ret val_remote_get_lib_version_ret; remote_cpu_compare_ret val_remote_cpu_compare_ret; remote_domain_memory_stats_ret val_remote_domain_memory_stats_ret; + remote_domain_get_job_info_ret val_remote_domain_get_job_info_ret; diff --git a/daemon/remote_dispatch_table.h b/daemon/remote_dispatch_table.h index 09ebeee..18acba8 100644 --- a/daemon/remote_dispatch_table.h +++ b/daemon/remote_dispatch_table.h @@ -802,3 +802,8 @@ .args_filter = (xdrproc_t) xdr_remote_domain_memory_stats_args, .ret_filter = (xdrproc_t) xdr_remote_domain_memory_stats_ret, }, +{ /* DomainGetJobInfo => 160 */ + .fn = (dispatch_fn) remoteDispatchDomainGetJobInfo, + .args_filter = (xdrproc_t) xdr_remote_domain_get_job_info_args, + .ret_filter = (xdrproc_t) xdr_remote_domain_get_job_info_ret, +}, diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 692c83c..f45bcae 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -7535,6 +7535,47 @@ done: return rv; } + +static int +remoteDomainGetJobInfo (virDomainPtr domain, virDomainJobInfoPtr info) +{ + int rv = -1; + remote_domain_get_job_info_args args; + remote_domain_get_job_info_ret ret; + struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); + + make_nonnull_domain (&args.dom, domain); + + memset (&ret, 0, sizeof ret); + if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_JOB_INFO, + (xdrproc_t) xdr_remote_domain_get_job_info_args, (char *) &args, + (xdrproc_t) xdr_remote_domain_get_job_info_ret, (char *) &ret) == -1) + goto done; + + info->type = ret.type; + info->percentComplete = ret.percentComplete; + info->timeElapsed = ret.timeElapsed; + info->timeRemaining = ret.timeRemaining; + info->dataTotal = ret.dataTotal; + info->dataProcessed = ret.dataProcessed; + info->dataRemaining = ret.dataRemaining; + info->memTotal = ret.memTotal; + info->memProcessed = ret.memProcessed; + info->memRemaining = ret.memRemaining; + info->fileTotal = ret.fileTotal; + info->fileProcessed = ret.fileProcessed; + info->fileRemaining = ret.fileRemaining; + + rv = 0; + +done: + remoteDriverUnlock(priv); + return rv; +} + + /*----------------------------------------------------------------------*/ @@ -8923,7 +8964,7 @@ static virDriver remote_driver = { remoteDomainIsActive, /* domainIsActive */ remoteDomainIsPersistent, /* domainIsPersistent */ remoteCPUCompare, /* cpuCompare */ - NULL, /* domainGetJobInfo */ + remoteDomainGetJobInfo, /* domainGetJobInfo */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.c b/src/remote/remote_protocol.c index 834eb6b..662170d 100644 --- a/src/remote/remote_protocol.c +++ b/src/remote/remote_protocol.c @@ -2912,6 +2912,48 @@ xdr_remote_cpu_compare_ret (XDR *xdrs, remote_cpu_compare_ret *objp) } bool_t +xdr_remote_domain_get_job_info_args (XDR *xdrs, remote_domain_get_job_info_args *objp) +{ + + if (!xdr_remote_nonnull_domain (xdrs, &objp->dom)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_get_job_info_ret (XDR *xdrs, remote_domain_get_job_info_ret *objp) +{ + + if (!xdr_int (xdrs, &objp->type)) + return FALSE; + if (!xdr_int (xdrs, &objp->percentComplete)) + return FALSE; + if (!xdr_uint64_t (xdrs, &objp->timeElapsed)) + return FALSE; + if (!xdr_uint64_t (xdrs, &objp->timeRemaining)) + return FALSE; + if (!xdr_uint64_t (xdrs, &objp->dataTotal)) + return FALSE; + if (!xdr_uint64_t (xdrs, &objp->dataProcessed)) + return FALSE; + if (!xdr_uint64_t (xdrs, &objp->dataRemaining)) + return FALSE; + if (!xdr_uint64_t (xdrs, &objp->memTotal)) + return FALSE; + if (!xdr_uint64_t (xdrs, &objp->memProcessed)) + return FALSE; + if (!xdr_uint64_t (xdrs, &objp->memRemaining)) + return FALSE; + if (!xdr_uint64_t (xdrs, &objp->fileTotal)) + return FALSE; + if (!xdr_uint64_t (xdrs, &objp->fileProcessed)) + return FALSE; + if (!xdr_uint64_t (xdrs, &objp->fileRemaining)) + return FALSE; + return TRUE; +} + +bool_t xdr_remote_procedure (XDR *xdrs, remote_procedure *objp) { diff --git a/src/remote/remote_protocol.h b/src/remote/remote_protocol.h index 3f3b520..47ff223 100644 --- a/src/remote/remote_protocol.h +++ b/src/remote/remote_protocol.h @@ -1649,6 +1649,28 @@ struct remote_cpu_compare_ret { int result; }; typedef struct remote_cpu_compare_ret remote_cpu_compare_ret; + +struct remote_domain_get_job_info_args { + remote_nonnull_domain dom; +}; +typedef struct remote_domain_get_job_info_args remote_domain_get_job_info_args; + +struct remote_domain_get_job_info_ret { + int type; + int percentComplete; + uint64_t timeElapsed; + uint64_t timeRemaining; + uint64_t dataTotal; + uint64_t dataProcessed; + uint64_t dataRemaining; + uint64_t memTotal; + uint64_t memProcessed; + uint64_t memRemaining; + uint64_t fileTotal; + uint64_t fileProcessed; + uint64_t fileRemaining; +}; +typedef struct remote_domain_get_job_info_ret remote_domain_get_job_info_ret; #define REMOTE_PROGRAM 0x20008086 #define REMOTE_PROTOCOL_VERSION 1 @@ -1812,6 +1834,7 @@ enum remote_procedure { REMOTE_PROC_GET_LIB_VERSION = 157, REMOTE_PROC_CPU_COMPARE = 158, REMOTE_PROC_DOMAIN_MEMORY_STATS = 159, + REMOTE_PROC_DOMAIN_GET_JOB_INFO = 160, }; typedef enum remote_procedure remote_procedure; @@ -2114,6 +2137,8 @@ extern bool_t xdr_remote_interface_is_active_args (XDR *, remote_interface_is_a extern bool_t xdr_remote_interface_is_active_ret (XDR *, remote_interface_is_active_ret*); extern bool_t xdr_remote_cpu_compare_args (XDR *, remote_cpu_compare_args*); extern bool_t xdr_remote_cpu_compare_ret (XDR *, remote_cpu_compare_ret*); +extern bool_t xdr_remote_domain_get_job_info_args (XDR *, remote_domain_get_job_info_args*); +extern bool_t xdr_remote_domain_get_job_info_ret (XDR *, remote_domain_get_job_info_ret*); extern bool_t xdr_remote_procedure (XDR *, remote_procedure*); extern bool_t xdr_remote_message_type (XDR *, remote_message_type*); extern bool_t xdr_remote_message_status (XDR *, remote_message_status*); @@ -2390,6 +2415,8 @@ extern bool_t xdr_remote_interface_is_active_args (); extern bool_t xdr_remote_interface_is_active_ret (); extern bool_t xdr_remote_cpu_compare_args (); extern bool_t xdr_remote_cpu_compare_ret (); +extern bool_t xdr_remote_domain_get_job_info_args (); +extern bool_t xdr_remote_domain_get_job_info_ret (); extern bool_t xdr_remote_procedure (); extern bool_t xdr_remote_message_type (); extern bool_t xdr_remote_message_status (); diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index bed3940..9b37bb4 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -1461,6 +1461,30 @@ struct remote_cpu_compare_ret { }; +struct remote_domain_get_job_info_args { + remote_nonnull_domain dom; +}; + +struct remote_domain_get_job_info_ret { + int type; + int percentComplete; + + unsigned hyper timeElapsed; + unsigned hyper timeRemaining; + + unsigned hyper dataTotal; + unsigned hyper dataProcessed; + unsigned hyper dataRemaining; + + unsigned hyper memTotal; + unsigned hyper memProcessed; + unsigned hyper memRemaining; + + unsigned hyper fileTotal; + unsigned hyper fileProcessed; + unsigned hyper fileRemaining; +}; + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -1641,7 +1665,8 @@ enum remote_procedure { REMOTE_PROC_INTERFACE_IS_ACTIVE = 156, REMOTE_PROC_GET_LIB_VERSION = 157, REMOTE_PROC_CPU_COMPARE = 158, - REMOTE_PROC_DOMAIN_MEMORY_STATS = 159 + REMOTE_PROC_DOMAIN_MEMORY_STATS = 159, + REMOTE_PROC_DOMAIN_GET_JOB_INFO = 160 /* * Notice how the entries are grouped in sets of 10 ? -- 1.6.6

Introduce support for virDomainGetJobInfo in the QEMU driver. This allows for monitoring of any API that uses the 'info migrate' monitor command. ie virDomainMigrate, virDomainSave and virDomainCoreDump Unfortunately QEMU does not provide a way to monitor incoming migration so we can't wire up virDomainRestore yet. The virsh tool gets a new command 'domjobinfo' to query status * src/qemu/qemu_driver.c: Record virDomainJobInfo and start time in qemuDomainObjPrivatePtr objects. Add generic shared handler for calling 'info migrate' with all migration based APIs. * src/qemu/qemu_monitor_text.c: Fix parsing of 'info migration' reply * tools/virsh.c: add new 'domjobinfo' command to query progress --- src/qemu/qemu_driver.c | 205 +++++++++++++++++++++++++++++++++++------- src/qemu/qemu_monitor_text.c | 7 +- tools/virsh.c | 75 +++++++++++++++ 3 files changed, 252 insertions(+), 35 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 1e3a8b7..540558f 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -86,6 +86,8 @@ struct _qemuDomainObjPrivate { int jobActive; /* Non-zero if a job is active. Only 1 job is allowed at any time * A job includes *all* monitor commands, even those just querying * information, not merely actions */ + virDomainJobInfo jobInfo; + unsigned long long jobStart; qemuMonitorPtr mon; virDomainChrDefPtr monConfig; @@ -329,6 +331,8 @@ static int qemuDomainObjBeginJob(virDomainObjPtr obj) } } priv->jobActive = 1; + priv->jobStart = (now.tv_sec * 1000ull) + (now.tv_usec / 1000); + memset(&priv->jobInfo, 0, sizeof(priv->jobInfo)); return 0; } @@ -373,6 +377,8 @@ static int qemuDomainObjBeginJobWithDriver(struct qemud_driver *driver, } } priv->jobActive = 1; + priv->jobStart = (now.tv_sec * 1000ull) + (now.tv_usec / 1000); + memset(&priv->jobInfo, 0, sizeof(priv->jobInfo)); virDomainObjUnlock(obj); qemuDriverLock(driver); @@ -395,6 +401,8 @@ static int ATTRIBUTE_RETURN_CHECK qemuDomainObjEndJob(virDomainObjPtr obj) qemuDomainObjPrivatePtr priv = obj->privateData; priv->jobActive = 0; + priv->jobStart = 0; + memset(&priv->jobInfo, 0, sizeof(priv->jobInfo)); virCondSignal(&priv->jobCond); return virDomainObjUnref(obj); @@ -3800,6 +3808,93 @@ cleanup: } +static int +qemuDomainWaitForMigrationComplete(struct qemud_driver *driver, virDomainObjPtr vm) +{ + int ret = -1; + int status; + unsigned long long memProcessed; + unsigned long long memRemaining; + unsigned long long memTotal; + qemuDomainObjPrivatePtr priv = vm->privateData; + + priv->jobInfo.type = VIR_DOMAIN_JOB_UNBOUNDED; + + while (priv->jobInfo.type != VIR_DOMAIN_JOB_NONE) { + /* Poll every 1/2 second for progress & to allow cancellation */ + struct timespec ts = { .tv_sec = 0, .tv_nsec = 500 * 1000ull }; + struct timeval now; + int rc; + + qemuDomainObjEnterMonitorWithDriver(driver, vm); + rc = qemuMonitorGetMigrationStatus(priv->mon, + &status, + &memProcessed, + &memRemaining, + &memTotal); + qemuDomainObjExitMonitorWithDriver(driver, vm); + + if (rc < 0) + goto cleanup; + + if (gettimeofday(&now, NULL) < 0) { + virReportSystemError(NULL, errno, "%s", + _("cannot get time of day")); + goto cleanup; + } + priv->jobInfo.timeElapsed = + ((now.tv_sec * 1000ull) + (now.tv_usec / 1000)) - + priv->jobStart; + + switch (status) { + case QEMU_MONITOR_MIGRATION_STATUS_INACTIVE: + priv->jobInfo.type = VIR_DOMAIN_JOB_NONE; + qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED, + "%s", _("Migration is not active")); + break; + + case QEMU_MONITOR_MIGRATION_STATUS_ACTIVE: + priv->jobInfo.dataTotal = memTotal; + priv->jobInfo.dataRemaining = memRemaining; + priv->jobInfo.dataProcessed = memProcessed; + + priv->jobInfo.memTotal = memTotal; + priv->jobInfo.memRemaining = memRemaining; + priv->jobInfo.memProcessed = memProcessed; + break; + + case QEMU_MONITOR_MIGRATION_STATUS_COMPLETED: + priv->jobInfo.type = VIR_DOMAIN_JOB_NONE; + ret = 0; + break; + + case QEMU_MONITOR_MIGRATION_STATUS_ERROR: + priv->jobInfo.type = VIR_DOMAIN_JOB_NONE; + qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED, + "%s", _("Migration unexpectedly failed")); + break; + + case QEMU_MONITOR_MIGRATION_STATUS_CANCELLED: + priv->jobInfo.type = VIR_DOMAIN_JOB_NONE; + qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED, + "%s", _("Migration was cancelled by client")); + break; + } + + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + + nanosleep(&ts, NULL); + + qemuDriverLock(driver); + virDomainObjLock(vm); + } + +cleanup: + return ret; +} + + #define QEMUD_SAVE_MAGIC "LibvirtQemudSave" #define QEMUD_SAVE_VERSION 2 @@ -3848,6 +3943,7 @@ static int qemudDomainSave(virDomainPtr dom, int ret = -1; int rc; virDomainEventPtr event = NULL; + qemuDomainObjPrivatePtr priv; memset(&header, 0, sizeof(header)); memcpy(header.magic, QEMUD_SAVE_MAGIC, sizeof(header.magic)); @@ -3876,6 +3972,7 @@ static int qemudDomainSave(virDomainPtr dom, _("no domain with matching uuid '%s'"), uuidstr); goto cleanup; } + priv = vm->privateData; if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) goto cleanup; @@ -3886,9 +3983,11 @@ static int qemudDomainSave(virDomainPtr dom, goto endjob; } + memset(&priv->jobInfo, 0, sizeof(priv->jobInfo)); + priv->jobInfo.type = VIR_DOMAIN_JOB_UNBOUNDED; + /* Pause */ if (vm->state == VIR_DOMAIN_RUNNING) { - qemuDomainObjPrivatePtr priv = vm->privateData; header.was_running = 1; qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuMonitorStopCPUs(priv->mon) < 0) { @@ -3942,26 +4041,29 @@ static int qemudDomainSave(virDomainPtr dom, if (header.compressed == QEMUD_SAVE_FORMAT_RAW) { const char *args[] = { "cat", NULL }; - qemuDomainObjPrivatePtr priv = vm->privateData; qemuDomainObjEnterMonitorWithDriver(driver, vm); - rc = qemuMonitorMigrateToCommand(priv->mon, 0, args, path); + rc = qemuMonitorMigrateToCommand(priv->mon, 1, args, path); qemuDomainObjExitMonitorWithDriver(driver, vm); } else { const char *prog = qemudSaveCompressionTypeToString(header.compressed); - qemuDomainObjPrivatePtr priv = vm->privateData; const char *args[] = { prog, "-c", NULL }; qemuDomainObjEnterMonitorWithDriver(driver, vm); - rc = qemuMonitorMigrateToCommand(priv->mon, 0, args, path); + rc = qemuMonitorMigrateToCommand(priv->mon, 1, args, path); qemuDomainObjExitMonitorWithDriver(driver, vm); } if (rc < 0) goto endjob; + rc = qemuDomainWaitForMigrationComplete(driver, vm); + + if (rc < 0) + goto endjob; + if (driver->securityDriver && driver->securityDriver->domainRestoreSavedStateLabel && driver->securityDriver->domainRestoreSavedStateLabel(dom->conn, vm, path) == -1) @@ -3984,7 +4086,7 @@ static int qemudDomainSave(virDomainPtr dom, endjob: if (vm && qemuDomainObjEndJob(vm) == 0) - vm = NULL; + vm = NULL; cleanup: if (fd != -1) @@ -4013,6 +4115,7 @@ static int qemudDomainCoreDump(virDomainPtr dom, "cat", NULL, }; + qemuDomainObjPrivatePtr priv; qemuDriverLock(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); @@ -4025,6 +4128,7 @@ static int qemudDomainCoreDump(virDomainPtr dom, _("no domain with matching uuid '%s'"), uuidstr); goto cleanup; } + priv = vm->privateData; if (qemuDomainObjBeginJob(vm) < 0) goto cleanup; @@ -4058,8 +4162,6 @@ static int qemudDomainCoreDump(virDomainPtr dom, independent of whether the stop command is issued. */ resume = (vm->state == VIR_DOMAIN_RUNNING); - qemuDomainObjPrivatePtr priv = vm->privateData; - /* Pause domain for non-live dump */ if (!(flags & VIR_DUMP_LIVE) && vm->state == VIR_DOMAIN_RUNNING) { qemuDomainObjEnterMonitor(vm); @@ -4072,9 +4174,18 @@ static int qemudDomainCoreDump(virDomainPtr dom, } qemuDomainObjEnterMonitor(vm); - ret = qemuMonitorMigrateToCommand(priv->mon, 0, args, path); + ret = qemuMonitorMigrateToCommand(priv->mon, 1, args, path); qemuDomainObjExitMonitor(vm); - paused |= (ret == 0); + + if (ret < 0) + goto endjob; + + ret = qemuDomainWaitForMigrationComplete(driver, vm); + + if (ret < 0) + goto endjob; + + paused = 1; if (driver->securityDriver && driver->securityDriver->domainRestoreSavedStateLabel && @@ -7763,8 +7874,6 @@ static int doNativeMigrate(virDomainPtr dom, { int ret = -1; xmlURIPtr uribits = NULL; - int status; - unsigned long long transferred, remaining, total; qemuDomainObjPrivatePtr priv = vm->privateData; /* Issue the migrate command. */ @@ -7793,29 +7902,14 @@ static int doNativeMigrate(virDomainPtr dom, goto cleanup; } - if (qemuMonitorMigrateToHost(priv->mon, 0, uribits->server, uribits->port) < 0) { - qemuDomainObjExitMonitorWithDriver(driver, vm); - goto cleanup; - } - - /* it is also possible that the migrate didn't fail initially, but - * rather failed later on. Check the output of "info migrate" - */ - if (qemuMonitorGetMigrationStatus(priv->mon, - &status, - &transferred, - &remaining, - &total) < 0) { + if (qemuMonitorMigrateToHost(priv->mon, 1, uribits->server, uribits->port) < 0) { qemuDomainObjExitMonitorWithDriver(driver, vm); goto cleanup; } qemuDomainObjExitMonitorWithDriver(driver, vm); - if (status != QEMU_MONITOR_MIGRATION_STATUS_COMPLETED) { - qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, - "%s", _("migrate did not successfully complete")); + if (qemuDomainWaitForMigrationComplete(driver, vm) < 0) goto cleanup; - } ret = 0; @@ -8167,6 +8261,7 @@ qemudDomainMigratePerform (virDomainPtr dom, virDomainEventPtr event = NULL; int ret = -1; int paused = 0; + qemuDomainObjPrivatePtr priv; qemuDriverLock(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); @@ -8177,6 +8272,7 @@ qemudDomainMigratePerform (virDomainPtr dom, _("no domain with matching uuid '%s'"), uuidstr); goto cleanup; } + priv = vm->privateData; if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) goto cleanup; @@ -8187,8 +8283,10 @@ qemudDomainMigratePerform (virDomainPtr dom, goto endjob; } + memset(&priv->jobInfo, 0, sizeof(priv->jobInfo)); + priv->jobInfo.type = VIR_DOMAIN_JOB_UNBOUNDED; + if (!(flags & VIR_MIGRATE_LIVE) && vm->state == VIR_DOMAIN_RUNNING) { - qemuDomainObjPrivatePtr priv = vm->privateData; /* Pause domain for non-live migration */ qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuMonitorStopCPUs(priv->mon) < 0) { @@ -8233,7 +8331,6 @@ qemudDomainMigratePerform (virDomainPtr dom, endjob: if (paused) { - qemuDomainObjPrivatePtr priv = vm->privateData; /* we got here through some sort of failure; start the domain again */ qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuMonitorStartCPUs(priv->mon, dom->conn) < 0) { @@ -8530,6 +8627,50 @@ qemuCPUCompare(virConnectPtr conn, return ret; } + +static int qemuDomainGetJobInfo(virDomainPtr dom, + virDomainJobInfoPtr info) { + struct qemud_driver *driver = dom->conn->privateData; + virDomainObjPtr vm; + int ret = -1; + qemuDomainObjPrivatePtr priv; + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + qemuDriverUnlock(driver); + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(dom->uuid, uuidstr); + qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + priv = vm->privateData; + + if (virDomainObjIsActive(vm)) { + if (priv->jobActive) { + memcpy(info, &priv->jobInfo, sizeof(*info)); + } else { + memset(info, 0, sizeof(*info)); + info->type = VIR_DOMAIN_JOB_NONE; + } + } else { + qemudReportError(dom->conn, NULL, NULL, VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto cleanup; + } + + ret = 0; + +cleanup: + if (vm) + virDomainObjUnlock(vm); + return ret; +} + + + static virDriver qemuDriver = { VIR_DRV_QEMU, "QEMU", @@ -8606,7 +8747,7 @@ static virDriver qemuDriver = { qemuDomainIsActive, qemuDomainIsPersistent, qemuCPUCompare, /* cpuCompare */ - NULL, /* domainGetJobInfo */ + qemuDomainGetJobInfo, /* domainGetJobInfo */ }; diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index 42ce363..ac90d85 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -997,18 +997,19 @@ int qemuMonitorTextGetMigrationStatus(qemuMonitorPtr mon, goto done; tmp += strlen(MIGRATION_TRANSFER_PREFIX); - if (virStrToLong_ull(tmp, NULL, 10, transferred) < 0) { + if (virStrToLong_ull(tmp, &end, 10, transferred) < 0 || !end) { qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, _("cannot parse migration data transferred statistic %s"), tmp); goto cleanup; } *transferred *= 1024; + tmp = end; if (!(tmp = strstr(tmp, MIGRATION_REMAINING_PREFIX))) goto done; tmp += strlen(MIGRATION_REMAINING_PREFIX); - if (virStrToLong_ull(tmp, NULL, 10, remaining) < 0) { + if (virStrToLong_ull(tmp, &end, 10, remaining) < 0 || !end) { qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, _("cannot parse migration data remaining statistic %s"), tmp); goto cleanup; @@ -1019,7 +1020,7 @@ int qemuMonitorTextGetMigrationStatus(qemuMonitorPtr mon, goto done; tmp += strlen(MIGRATION_TOTAL_PREFIX); - if (virStrToLong_ull(tmp, NULL, 10, total) < 0) { + if (virStrToLong_ull(tmp, &end, 10, total) < 0 || !end) { qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, _("cannot parse migration data total statistic %s"), tmp); goto cleanup; diff --git a/tools/virsh.c b/tools/virsh.c index 01d2038..bb8190c 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -1791,6 +1791,80 @@ cmdDominfo(vshControl *ctl, const vshCmd *cmd) } /* + * "domjobinfo" command + */ +static const vshCmdInfo info_domjobinfo[] = { + {"help", gettext_noop("domain job information")}, + {"desc", gettext_noop("Returns information about jobs running on a domain.")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_domjobinfo[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("domain name, id or uuid")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdDomjobinfo(vshControl *ctl, const vshCmd *cmd) +{ + virDomainJobInfo info; + virDomainPtr dom; + int ret = TRUE, autostart; + unsigned int id; + char *str, uuid[VIR_UUID_STRING_BUFLEN]; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + return FALSE; + + if (virDomainGetJobInfo(dom, &info) == 0) { + vshPrint(ctl, "%-17s ", _("Job type:")); + switch (info.type) { + case VIR_DOMAIN_JOB_BOUNDED: + vshPrint(ctl, "%-12s\n", _("Bounded")); + break; + + case VIR_DOMAIN_JOB_UNBOUNDED: + vshPrint(ctl, "%-12s\n", _("Unbounded")); + break; + + case VIR_DOMAIN_JOB_NONE: + default: + vshPrint(ctl, "%-12s\n", _("None")); + goto cleanup; + } + + if (info.type == VIR_DOMAIN_JOB_BOUNDED) + vshPrint(ctl, "%-17s %-12d%%\n", _("Completion:"), info.percentComplete); + vshPrint(ctl, "%-17s %-12llu ms\n", _("Time elapsed:"), info.timeElapsed); + if (info.type == VIR_DOMAIN_JOB_BOUNDED) + vshPrint(ctl, "%-17s %-12llu ms\n", _("Time remaining:"), info.timeRemaining); + if (info.dataTotal || info.dataRemaining || info.dataProcessed) { + vshPrint(ctl, "%-17s %-12llu bytes\n", _("Data processed:"), info.dataProcessed); + vshPrint(ctl, "%-17s %-12llu bytes\n", _("Data remaining:"), info.dataRemaining); + vshPrint(ctl, "%-17s %-12llu bytes\n", _("Data total:"), info.dataTotal); + } + if (info.memTotal || info.memRemaining || info.memProcessed) { + vshPrint(ctl, "%-17s %-12llu bytes\n", _("Memory processed:"), info.memProcessed); + vshPrint(ctl, "%-17s %-12llu bytes\n", _("Memory remaining:"), info.memRemaining); + vshPrint(ctl, "%-17s %-12llu bytes\n", _("Memory total:"), info.memTotal); + } + if (info.fileTotal || info.fileRemaining || info.fileProcessed) { + vshPrint(ctl, "%-17s %-12llu bytes\n", _("File processed:"), info.fileProcessed); + vshPrint(ctl, "%-17s %-12llu bytes\n", _("File remaining:"), info.fileRemaining); + vshPrint(ctl, "%-17s %-12llu bytes\n", _("File total:"), info.fileTotal); + } + } else { + ret = FALSE; + } +cleanup: + virDomainFree(dom); + return ret; +} + +/* * "freecell" command */ static const vshCmdInfo info_freecell[] = { @@ -7340,6 +7414,7 @@ static const vshCmdDef commands[] = { {"domid", cmdDomid, opts_domid, info_domid}, {"domuuid", cmdDomuuid, opts_domuuid, info_domuuid}, {"dominfo", cmdDominfo, opts_dominfo, info_dominfo}, + {"domjobinfo", cmdDomjobinfo, opts_domjobinfo, info_domjobinfo}, {"domname", cmdDomname, opts_domname, info_domname}, {"domstate", cmdDomstate, opts_domstate, info_domstate}, {"domblkstat", cmdDomblkstat, opts_domblkstat, info_domblkstat}, -- 1.6.6

The new virDomainAbortJob() method provides a way for a second thread to abort an ongoing job run by another thread. This extends to any API with which the virDomainGetJobInfo() API is intended to work. Cancellation is not guarenteed, rather best effort on part of the hypervisor and not required to be implmented. * include/libvirt/libvirt.h.in: Define virDomainAbortJob() --- include/libvirt/libvirt.h.in | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 5058bc0..0ffcf11 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1804,6 +1804,8 @@ struct _virDomainJobInfo { int virDomainGetJobInfo(virDomainPtr dom, virDomainJobInfoPtr info); +int virDomainAbortJob(virDomainPtr dom); + #ifdef __cplusplus } #endif -- 1.6.6

This provides the internal glue for the driver API * src/driver.h: Internal API contract * src/libvirt.c, src/libvirt_public.syms: Connect public API to driver API * src/esx/esx_driver.c, src/lxc/lxc_driver.c, src/opennebula/one_driver.c, src/openvz/openvz_driver.c, src/phyp/phyp_driver.c, src/qemu/qemu_driver.c, src/remote/remote_driver.c, src/test/test_driver.c src/uml/uml_driver.c, src/vbox/vbox_tmpl.c, src/xen/xen_driver.c: Stub out entry points --- src/driver.h | 4 +++ src/esx/esx_driver.c | 1 + src/libvirt.c | 45 +++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 1 + src/lxc/lxc_driver.c | 1 + src/opennebula/one_driver.c | 1 + src/openvz/openvz_driver.c | 1 + src/phyp/phyp_driver.c | 1 + src/qemu/qemu_driver.c | 1 + src/remote/remote_driver.c | 1 + src/test/test_driver.c | 1 + src/uml/uml_driver.c | 1 + src/vbox/vbox_tmpl.c | 1 + src/xen/xen_driver.c | 1 + 14 files changed, 61 insertions(+), 0 deletions(-) diff --git a/src/driver.h b/src/driver.h index 8f8592a..390b87e 100644 --- a/src/driver.h +++ b/src/driver.h @@ -364,6 +364,9 @@ typedef int (*virDrvDomainGetJobInfo)(virDomainPtr domain, virDomainJobInfoPtr info); +typedef int + (*virDrvDomainAbortJob)(virDomainPtr domain); + /** * _virDriver: * @@ -453,6 +456,7 @@ struct _virDriver { virDrvDomainIsPersistent domainIsPersistent; virDrvCPUCompare cpuCompare; virDrvDomainGetJobInfo domainGetJobInfo; + virDrvDomainAbortJob domainAbortJob; }; typedef int diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c index e7312a7..b375527 100644 --- a/src/esx/esx_driver.c +++ b/src/esx/esx_driver.c @@ -3402,6 +3402,7 @@ static virDriver esxDriver = { esxDomainIsPersistent, /* domainIsPersistent */ NULL, /* cpuCompare */ NULL, /* domainGetJobInfo */ + NULL, /* domainAbortJob */ }; diff --git a/src/libvirt.c b/src/libvirt.c index a5dedf7..b7c4622 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -11031,3 +11031,48 @@ error: } +/** + * virDomainAbortJob: + * @domain: a domain object + * + * Requests that the current background job be aborted at the + * soonest opportunity. This will block until the job has + * either completed, or aborted. + * + * Returns 0 in case of success and -1 in case of failure. + */ +int +virDomainAbortJob(virDomainPtr domain) +{ + virConnectPtr conn; + + DEBUG("domain=%p", domain); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return (-1); + } + + conn = domain->conn; + if (conn->flags & VIR_CONNECT_RO) { + virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (conn->driver->domainAbortJob) { + int ret; + ret = conn->driver->domainAbortJob(domain); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(conn); + return -1; +} diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 7afdc05..c40ded1 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -352,6 +352,7 @@ LIBVIRT_0.7.5 { LIBVIRT_0.7.7 { global: virDomainGetJobInfo; + virDomainAbortJob; } LIBVIRT_0.7.5; # .... define new API here using predicted next version number .... diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 983cab7..b299549 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -2457,6 +2457,7 @@ static virDriver lxcDriver = { lxcDomainIsPersistent, NULL, /* cpuCompare */ NULL, /* domainGetJobInfo */ + NULL, /* domainAbortJob */ }; static virStateDriver lxcStateDriver = { diff --git a/src/opennebula/one_driver.c b/src/opennebula/one_driver.c index 2c1dd52..9def5f3 100644 --- a/src/opennebula/one_driver.c +++ b/src/opennebula/one_driver.c @@ -784,6 +784,7 @@ static virDriver oneDriver = { NULL, /* domainIsPersistent */ NULL, /* cpuCompare */ NULL, /* domainGetJobInfo */ + NULL, /* domainAbortJob */ }; static virStateDriver oneStateDriver = { diff --git a/src/openvz/openvz_driver.c b/src/openvz/openvz_driver.c index a7bf72d..88229af 100644 --- a/src/openvz/openvz_driver.c +++ b/src/openvz/openvz_driver.c @@ -1536,6 +1536,7 @@ static virDriver openvzDriver = { openvzDomainIsPersistent, NULL, /* cpuCompare */ NULL, /* domainGetJobInfo */ + NULL, /* domainAbortJob */ }; int openvzRegister(void) { diff --git a/src/phyp/phyp_driver.c b/src/phyp/phyp_driver.c index f723f03..05f7bf2 100644 --- a/src/phyp/phyp_driver.c +++ b/src/phyp/phyp_driver.c @@ -1652,6 +1652,7 @@ virDriver phypDriver = { NULL, /* domainIsPersistent */ NULL, /* cpuCompare */ NULL, /* domainGetJobInfo */ + NULL, /* domainAbortJob */ }; int diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 540558f..171670a 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -8748,6 +8748,7 @@ static virDriver qemuDriver = { qemuDomainIsPersistent, qemuCPUCompare, /* cpuCompare */ qemuDomainGetJobInfo, /* domainGetJobInfo */ + NULL, /* domainFinishJob */ }; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index f45bcae..dd0bf9a 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -8965,6 +8965,7 @@ static virDriver remote_driver = { remoteDomainIsPersistent, /* domainIsPersistent */ remoteCPUCompare, /* cpuCompare */ remoteDomainGetJobInfo, /* domainGetJobInfo */ + NULL, /* domainFinishJob */ }; static virNetworkDriver network_driver = { diff --git a/src/test/test_driver.c b/src/test/test_driver.c index d23fb68..361c061 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -5239,6 +5239,7 @@ static virDriver testDriver = { testDomainIsPersistent, /* domainIsPersistent */ NULL, /* cpuCompare */ NULL, /* domainGetJobInfo */ + NULL, /* domainAbortJob */ }; static virNetworkDriver testNetworkDriver = { diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c index de42e26..be7f912 100644 --- a/src/uml/uml_driver.c +++ b/src/uml/uml_driver.c @@ -1925,6 +1925,7 @@ static virDriver umlDriver = { umlDomainIsPersistent, NULL, /* cpuCompare */ NULL, /* domainGetJobInfo */ + NULL, /* domainAbortJob */ }; diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index 5abb9f2..88c5d99 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -7036,6 +7036,7 @@ virDriver NAME(Driver) = { vboxDomainIsPersistent, NULL, /* cpuCompare */ NULL, /* domainGetJobInfo */ + NULL, /* domainAbortJob */ }; virNetworkDriver NAME(NetworkDriver) = { diff --git a/src/xen/xen_driver.c b/src/xen/xen_driver.c index 71fc9e9..975d34a 100644 --- a/src/xen/xen_driver.c +++ b/src/xen/xen_driver.c @@ -1865,6 +1865,7 @@ static virDriver xenUnifiedDriver = { xenUnifiedDomainisPersistent, NULL, /* cpuCompare */ NULL, /* domainGetJobInfo */ + NULL, /* domainAbortJob */ }; /** -- 1.6.6

This defines the wire protocol for the new API * src/remote/remote_protocol.x: Wire protocol definition * src/remote/remote_driver.c,daemon/remote.c: Client and server side implementation * daemon/remote_dispatch_args.h, daemon/remote_dispatch_prototypes.h, daemon/remote_dispatch_table.h, src/remote/remote_protocol.c, src/remote/remote_protocol.h: Re-generate from remote_protocol.x --- daemon/remote.c | 29 +++++++++++++++++++++++++++++ daemon/remote_dispatch_args.h | 1 + daemon/remote_dispatch_prototypes.h | 8 ++++++++ daemon/remote_dispatch_table.h | 5 +++++ src/remote/remote_driver.c | 26 +++++++++++++++++++++++++- src/remote/remote_protocol.c | 9 +++++++++ src/remote/remote_protocol.h | 8 ++++++++ src/remote/remote_protocol.x | 9 ++++++++- 8 files changed, 93 insertions(+), 2 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index e434591..3269bde 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -5356,6 +5356,35 @@ remoteDispatchDomainGetJobInfo (struct qemud_server *server ATTRIBUTE_UNUSED, } +static int +remoteDispatchDomainAbortJob (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_abort_job_args *args, + void *ret ATTRIBUTE_UNUSED) +{ + virDomainPtr dom; + + dom = get_nonnull_domain (conn, args->dom); + if (dom == NULL) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + if (virDomainAbortJob (dom) == -1) { + virDomainFree(dom); + remoteDispatchConnError(rerr, conn); + return -1; + } + + virDomainFree(dom); + + return 0; +} + + /*----- Helpers. -----*/ /* get_nonnull_domain and get_nonnull_network turn an on-wire diff --git a/daemon/remote_dispatch_args.h b/daemon/remote_dispatch_args.h index b66af1c..9cbe258 100644 --- a/daemon/remote_dispatch_args.h +++ b/daemon/remote_dispatch_args.h @@ -136,3 +136,4 @@ remote_cpu_compare_args val_remote_cpu_compare_args; remote_domain_memory_stats_args val_remote_domain_memory_stats_args; remote_domain_get_job_info_args val_remote_domain_get_job_info_args; + remote_domain_abort_job_args val_remote_domain_abort_job_args; diff --git a/daemon/remote_dispatch_prototypes.h b/daemon/remote_dispatch_prototypes.h index b520f7d..e34d756 100644 --- a/daemon/remote_dispatch_prototypes.h +++ b/daemon/remote_dispatch_prototypes.h @@ -58,6 +58,14 @@ static int remoteDispatchCpuCompare( remote_error *err, remote_cpu_compare_args *args, remote_cpu_compare_ret *ret); +static int remoteDispatchDomainAbortJob( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_message_header *hdr, + remote_error *err, + remote_domain_abort_job_args *args, + void *ret); static int remoteDispatchDomainAttachDevice( struct qemud_server *server, struct qemud_client *client, diff --git a/daemon/remote_dispatch_table.h b/daemon/remote_dispatch_table.h index 18acba8..de3a355 100644 --- a/daemon/remote_dispatch_table.h +++ b/daemon/remote_dispatch_table.h @@ -807,3 +807,8 @@ .args_filter = (xdrproc_t) xdr_remote_domain_get_job_info_args, .ret_filter = (xdrproc_t) xdr_remote_domain_get_job_info_ret, }, +{ /* DomainAbortJob => 161 */ + .fn = (dispatch_fn) remoteDispatchDomainAbortJob, + .args_filter = (xdrproc_t) xdr_remote_domain_abort_job_args, + .ret_filter = (xdrproc_t) xdr_void, +}, diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index dd0bf9a..b777b31 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -7576,6 +7576,30 @@ done: } +static int +remoteDomainAbortJob (virDomainPtr domain) +{ + int rv = -1; + remote_domain_abort_job_args args; + struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); + + make_nonnull_domain (&args.dom, domain); + + if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_ABORT_JOB, + (xdrproc_t) xdr_remote_domain_abort_job_args, (char *) &args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + goto done; + + rv = 0; + +done: + remoteDriverUnlock(priv); + return rv; +} + + /*----------------------------------------------------------------------*/ @@ -8965,7 +8989,7 @@ static virDriver remote_driver = { remoteDomainIsPersistent, /* domainIsPersistent */ remoteCPUCompare, /* cpuCompare */ remoteDomainGetJobInfo, /* domainGetJobInfo */ - NULL, /* domainFinishJob */ + remoteDomainAbortJob, /* domainFinishJob */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.c b/src/remote/remote_protocol.c index 662170d..c81bb46 100644 --- a/src/remote/remote_protocol.c +++ b/src/remote/remote_protocol.c @@ -2954,6 +2954,15 @@ xdr_remote_domain_get_job_info_ret (XDR *xdrs, remote_domain_get_job_info_ret *o } bool_t +xdr_remote_domain_abort_job_args (XDR *xdrs, remote_domain_abort_job_args *objp) +{ + + if (!xdr_remote_nonnull_domain (xdrs, &objp->dom)) + return FALSE; + return TRUE; +} + +bool_t xdr_remote_procedure (XDR *xdrs, remote_procedure *objp) { diff --git a/src/remote/remote_protocol.h b/src/remote/remote_protocol.h index 47ff223..e014621 100644 --- a/src/remote/remote_protocol.h +++ b/src/remote/remote_protocol.h @@ -1671,6 +1671,11 @@ struct remote_domain_get_job_info_ret { uint64_t fileRemaining; }; typedef struct remote_domain_get_job_info_ret remote_domain_get_job_info_ret; + +struct remote_domain_abort_job_args { + remote_nonnull_domain dom; +}; +typedef struct remote_domain_abort_job_args remote_domain_abort_job_args; #define REMOTE_PROGRAM 0x20008086 #define REMOTE_PROTOCOL_VERSION 1 @@ -1835,6 +1840,7 @@ enum remote_procedure { REMOTE_PROC_CPU_COMPARE = 158, REMOTE_PROC_DOMAIN_MEMORY_STATS = 159, REMOTE_PROC_DOMAIN_GET_JOB_INFO = 160, + REMOTE_PROC_DOMAIN_ABORT_JOB = 161, }; typedef enum remote_procedure remote_procedure; @@ -2139,6 +2145,7 @@ extern bool_t xdr_remote_cpu_compare_args (XDR *, remote_cpu_compare_args*); extern bool_t xdr_remote_cpu_compare_ret (XDR *, remote_cpu_compare_ret*); extern bool_t xdr_remote_domain_get_job_info_args (XDR *, remote_domain_get_job_info_args*); extern bool_t xdr_remote_domain_get_job_info_ret (XDR *, remote_domain_get_job_info_ret*); +extern bool_t xdr_remote_domain_abort_job_args (XDR *, remote_domain_abort_job_args*); extern bool_t xdr_remote_procedure (XDR *, remote_procedure*); extern bool_t xdr_remote_message_type (XDR *, remote_message_type*); extern bool_t xdr_remote_message_status (XDR *, remote_message_status*); @@ -2417,6 +2424,7 @@ extern bool_t xdr_remote_cpu_compare_args (); extern bool_t xdr_remote_cpu_compare_ret (); extern bool_t xdr_remote_domain_get_job_info_args (); extern bool_t xdr_remote_domain_get_job_info_ret (); +extern bool_t xdr_remote_domain_abort_job_args (); extern bool_t xdr_remote_procedure (); extern bool_t xdr_remote_message_type (); extern bool_t xdr_remote_message_status (); diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 9b37bb4..0753852 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -1485,6 +1485,11 @@ struct remote_domain_get_job_info_ret { unsigned hyper fileRemaining; }; + +struct remote_domain_abort_job_args { + remote_nonnull_domain dom; +}; + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -1666,7 +1671,9 @@ enum remote_procedure { REMOTE_PROC_GET_LIB_VERSION = 157, REMOTE_PROC_CPU_COMPARE = 158, REMOTE_PROC_DOMAIN_MEMORY_STATS = 159, - REMOTE_PROC_DOMAIN_GET_JOB_INFO = 160 + REMOTE_PROC_DOMAIN_GET_JOB_INFO = 160, + + REMOTE_PROC_DOMAIN_ABORT_JOB = 161 /* * Notice how the entries are grouped in sets of 10 ? -- 1.6.6

This supports cancellation of jobs for the QEMU driver against the virDomainMigrate, virDomainSave and virDomainCoreDump APIs. It is not yet supported for the virDomainRestore API, although it is desirable. * src/qemu/qemu_driver.c: Issue 'migrate_cancel' command if virDomainAbortJob is issued during a migration operation * tools/virsh.c: Add a domjobabort command --- src/qemu/qemu_driver.c | 66 +++++++++++++++++++++++++++++++++++++++++++++--- tools/virsh.c | 37 +++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 4 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 171670a..09412f9 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -83,9 +83,10 @@ typedef struct _qemuDomainObjPrivate qemuDomainObjPrivate; typedef qemuDomainObjPrivate *qemuDomainObjPrivatePtr; struct _qemuDomainObjPrivate { virCond jobCond; /* Use in conjunction with main virDomainObjPtr lock */ - int jobActive; /* Non-zero if a job is active. Only 1 job is allowed at any time - * A job includes *all* monitor commands, even those just querying - * information, not merely actions */ + unsigned int jobActive : 1; /* Non-zero if a job is active. Only 1 job is allowed at any time + * A job includes *all* monitor commands, even those just querying + * information, not merely actions */ + unsigned int jobCancel : 1; /* Non-zero if a cancel request from client has arrived */ virDomainJobInfo jobInfo; unsigned long long jobStart; @@ -331,6 +332,7 @@ static int qemuDomainObjBeginJob(virDomainObjPtr obj) } } priv->jobActive = 1; + priv->jobCancel = 0; priv->jobStart = (now.tv_sec * 1000ull) + (now.tv_usec / 1000); memset(&priv->jobInfo, 0, sizeof(priv->jobInfo)); @@ -377,6 +379,7 @@ static int qemuDomainObjBeginJobWithDriver(struct qemud_driver *driver, } } priv->jobActive = 1; + priv->jobCancel = 0; priv->jobStart = (now.tv_sec * 1000ull) + (now.tv_usec / 1000); memset(&priv->jobInfo, 0, sizeof(priv->jobInfo)); @@ -401,6 +404,7 @@ static int ATTRIBUTE_RETURN_CHECK qemuDomainObjEndJob(virDomainObjPtr obj) qemuDomainObjPrivatePtr priv = obj->privateData; priv->jobActive = 0; + priv->jobCancel = 0; priv->jobStart = 0; memset(&priv->jobInfo, 0, sizeof(priv->jobInfo)); virCondSignal(&priv->jobCond); @@ -3826,6 +3830,17 @@ qemuDomainWaitForMigrationComplete(struct qemud_driver *driver, virDomainObjPtr struct timeval now; int rc; + if (priv->jobCancel) { + priv->jobCancel = 0; + VIR_DEBUG0("Cancelling migration at client request"); + qemuDomainObjEnterMonitorWithDriver(driver, vm); + rc = qemuMonitorMigrateCancel(priv->mon); + qemuDomainObjExitMonitorWithDriver(driver, vm); + if (rc < 0) { + VIR_WARN0("Unable to cancel migration"); + } + } + qemuDomainObjEnterMonitorWithDriver(driver, vm); rc = qemuMonitorGetMigrationStatus(priv->mon, &status, @@ -8670,6 +8685,49 @@ cleanup: } +static int qemuDomainAbortJob(virDomainPtr dom) { + struct qemud_driver *driver = dom->conn->privateData; + virDomainObjPtr vm; + int ret = -1; + qemuDomainObjPrivatePtr priv; + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + qemuDriverUnlock(driver); + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(dom->uuid, uuidstr); + qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + priv = vm->privateData; + + if (virDomainObjIsActive(vm)) { + if (priv->jobActive) { + VIR_DEBUG("Requesting cancellation of job on vm %s", vm->def->name); + priv->jobCancel = 1; + } else { + qemudReportError(dom->conn, NULL, NULL, VIR_ERR_OPERATION_INVALID, + "%s", _("no job is active on the domain")); + goto cleanup; + } + } else { + qemudReportError(dom->conn, NULL, NULL, VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto cleanup; + } + + ret = 0; + +cleanup: + if (vm) + virDomainObjUnlock(vm); + return ret; +} + + static virDriver qemuDriver = { VIR_DRV_QEMU, @@ -8748,7 +8806,7 @@ static virDriver qemuDriver = { qemuDomainIsPersistent, qemuCPUCompare, /* cpuCompare */ qemuDomainGetJobInfo, /* domainGetJobInfo */ - NULL, /* domainFinishJob */ + qemuDomainAbortJob, /* domainAbortJob */ }; diff --git a/tools/virsh.c b/tools/virsh.c index bb8190c..22503cd 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -1865,6 +1865,42 @@ cleanup: } /* + * "domjobabort" command + */ +static const vshCmdInfo info_domjobabort[] = { + {"help", gettext_noop("abort active domain job")}, + {"desc", gettext_noop("Aborts the currently running domain job")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_domjobabort[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("domain name, id or uuid")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdDomjobabort(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom; + int ret = TRUE; + unsigned int id; + char *str, uuid[VIR_UUID_STRING_BUFLEN]; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + return FALSE; + + if (virDomainAbortJob(dom) < 0) + ret = FALSE; + +cleanup: + virDomainFree(dom); + return ret; +} + +/* * "freecell" command */ static const vshCmdInfo info_freecell[] = { @@ -7415,6 +7451,7 @@ static const vshCmdDef commands[] = { {"domuuid", cmdDomuuid, opts_domuuid, info_domuuid}, {"dominfo", cmdDominfo, opts_dominfo, info_dominfo}, {"domjobinfo", cmdDomjobinfo, opts_domjobinfo, info_domjobinfo}, + {"domjobabort", cmdDomjobabort, opts_domjobabort, info_domjobabort}, {"domname", cmdDomname, opts_domname, info_domname}, {"domstate", cmdDomstate, opts_domstate, info_domstate}, {"domblkstat", cmdDomblkstat, opts_domblkstat, info_domblkstat}, -- 1.6.6

When a VM save attempt failed, the VM would be left in a paused state. It is neccessary to resume CPU execution upon failure if it was running originally * src/qemu/qemu_driver.c: Resume CPUs upon save failure --- src/qemu/qemu_driver.c | 10 ++++++++++ 1 files changed, 10 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 09412f9..f298eac 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4099,6 +4099,16 @@ static int qemudDomainSave(virDomainPtr dom, } endjob: + if (ret != 0 && header.was_running) { + qemuDomainObjEnterMonitorWithDriver(driver, vm); + rc = qemuMonitorStartCPUs(priv->mon, dom->conn); + qemuDomainObjExitMonitorWithDriver(driver, vm); + if (rc < 0) + VIR_WARN0("Unable to resume guest CPUs after save failure"); + else + vm->state = VIR_DOMAIN_RUNNING; + } + if (vm && qemuDomainObjEndJob(vm) == 0) vm = NULL; -- 1.6.6
participants (1)
-
Daniel P. Berrange