diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 081b698f7f86a44bc19ac1c1ea889df074c46599..d03c2359051f707c20c3befe6681356f3d06b32b 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -84,9 +84,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); @@ -3945,6 +3949,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, @@ -8838,6 +8853,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); + qemuReportError(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 { + qemuReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("no job is active on the domain")); + goto cleanup; + } + } else { + qemuReportError(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", @@ -8918,7 +8976,7 @@ static virDriver qemuDriver = { qemuCPUCompare, /* cpuCompare */ qemuCPUBaseline, /* cpuBaseline */ qemuDomainGetJobInfo, /* domainGetJobInfo */ - NULL, /* domainFinishJob */ + qemuDomainAbortJob, /* domainAbortJob */ }; diff --git a/tools/virsh.c b/tools/virsh.c index b36fcefaf8ba63092214ff104881a54538befcba..a6a637d1ba056b0ba211678ec7b0f6d29a6426a3 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -1906,6 +1906,42 @@ cleanup: return ret; } +/* + * "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 */ @@ -7621,6 +7657,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},