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;
}
}
priv->jobActive = 1;
+ priv->jobStart = (now.tv_sec * 1000ull) + (now.tv_usec / 1000);
+ memset(&priv->jobInfo, 0, sizeof(priv->jobInfo));
return 0;
}
}
}
priv->jobActive = 1;
+ priv->jobStart = (now.tv_sec * 1000ull) + (now.tv_usec / 1000);
+ memset(&priv->jobInfo, 0, sizeof(priv->jobInfo));
virDomainObjUnlock(obj);
qemuDriverLock(driver);
qemuDomainObjPrivatePtr priv = obj->privateData;
priv->jobActive = 0;
+ priv->jobStart = 0;
+ memset(&priv->jobInfo, 0, sizeof(priv->jobInfo));
virCondSignal(&priv->jobCond);
return virDomainObjUnref(obj);
}
+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_UNBOUNDED) {
+ /* Poll every 50ms for progress & to allow cancellation */
+ struct timespec ts = { .tv_sec = 0, .tv_nsec = 50 * 1000ull };
+ struct timeval now;
+ int rc;
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ rc = qemuMonitorGetMigrationStatus(priv->mon,
+ &status,
+ &memProcessed,
+ &memRemaining,
+ &memTotal);
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+ if (rc < 0) {
+ priv->jobInfo.type = VIR_DOMAIN_JOB_FAILED;
+ goto cleanup;
+ }
+
+ if (gettimeofday(&now, NULL) < 0) {
+ priv->jobInfo.type = VIR_DOMAIN_JOB_FAILED;
+ virReportSystemError(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;
+ qemuReportError(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_COMPLETED;
+ ret = 0;
+ break;
+
+ case QEMU_MONITOR_MIGRATION_STATUS_ERROR:
+ priv->jobInfo.type = VIR_DOMAIN_JOB_FAILED;
+ qemuReportError(VIR_ERR_OPERATION_FAILED,
+ "%s", _("Migration unexpectedly failed"));
+ break;
+
+ case QEMU_MONITOR_MIGRATION_STATUS_CANCELLED:
+ priv->jobInfo.type = VIR_DOMAIN_JOB_CANCELLED;
+ qemuReportError(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
int ret = -1;
int rc;
virDomainEventPtr event = NULL;
+ qemuDomainObjPrivatePtr priv;
memset(&header, 0, sizeof(header));
memcpy(header.magic, QEMUD_SAVE_MAGIC, sizeof(header.magic));
_("no domain with matching uuid '%s'"), uuidstr);
goto cleanup;
}
+ priv = vm->privateData;
if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0)
goto cleanup;
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) {
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;
endjob:
if (vm &&
qemuDomainObjEndJob(vm) == 0)
- vm = NULL;
+ vm = NULL;
cleanup:
if (fd != -1)
"cat",
NULL,
};
+ qemuDomainObjPrivatePtr priv;
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, dom->uuid);
_("no domain with matching uuid '%s'"), uuidstr);
goto cleanup;
}
+ priv = vm->privateData;
if (qemuDomainObjBeginJob(vm) < 0)
goto cleanup;
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);
}
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 &&
{
int ret = -1;
xmlURIPtr uribits = NULL;
- int status;
- unsigned long long transferred, remaining, total;
qemuDomainObjPrivatePtr priv = vm->privateData;
/* Issue the migrate command. */
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) {
- qemuReportError(VIR_ERR_OPERATION_FAILED,
- "%s", _("migrate did not successfully complete"));
+ if (qemuDomainWaitForMigrationComplete(driver, vm) < 0)
goto cleanup;
- }
ret = 0;
virDomainEventPtr event = NULL;
int ret = -1;
int paused = 0;
+ qemuDomainObjPrivatePtr priv;
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, dom->uuid);
_("no domain with matching uuid '%s'"), uuidstr);
goto cleanup;
}
+ priv = vm->privateData;
if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0)
goto cleanup;
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) {
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) {
return ret;
}
+
static char *
qemuCPUBaseline(virConnectPtr conn ATTRIBUTE_UNUSED,
const char **xmlCPUs,
return cpu;
}
+
+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);
+ qemuReportError(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 {
+ 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",
qemuDomainIsPersistent,
qemuCPUCompare, /* cpuCompare */
qemuCPUBaseline, /* cpuBaseline */
- NULL, /* domainGetJobInfo */
+ qemuDomainGetJobInfo, /* domainGetJobInfo */
};
return strcasecmp(*sa, *sb);
}
+static double
+prettyCapacity(unsigned long long val,
+ const char **unit) {
+ if (val < 1024) {
+ *unit = "";
+ return (double)val;
+ } else if (val < (1024.0l * 1024.0l)) {
+ *unit = "KB";
+ return (((double)val / 1024.0l));
+ } else if (val < (1024.0l * 1024.0l * 1024.0l)) {
+ *unit = "MB";
+ return ((double)val / (1024.0l * 1024.0l));
+ } else if (val < (1024.0l * 1024.0l * 1024.0l * 1024.0l)) {
+ *unit = "GB";
+ return ((double)val / (1024.0l * 1024.0l * 1024.0l));
+ } else {
+ *unit = "TB";
+ return ((double)val / (1024.0l * 1024.0l * 1024.0l * 1024.0l));
+ }
+}
+
+
static virErrorPtr last_error;
/*
return ret;
}
+/*
+ * "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) {
+ const char *unit;
+ double val;
+
+ 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;
+ }
+
+ 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) {
+ val = prettyCapacity(info.dataProcessed, &unit);
+ vshPrint(ctl, "%-17s %-0.3lf %s\n", _("Data processed:"), val, unit);
+ val = prettyCapacity(info.dataRemaining, &unit);
+ vshPrint(ctl, "%-17s %-0.3lf %s\n", _("Data remaining:"), val, unit);
+ val = prettyCapacity(info.dataTotal, &unit);
+ vshPrint(ctl, "%-17s %-0.3lf %s\n", _("Data total:"), val, unit);
+ }
+ if (info.memTotal || info.memRemaining || info.memProcessed) {
+ val = prettyCapacity(info.memProcessed, &unit);
+ vshPrint(ctl, "%-17s %-0.3lf %s\n", _("Memory processed:"), val, unit);
+ val = prettyCapacity(info.memRemaining, &unit);
+ vshPrint(ctl, "%-17s %-0.3lf %s\n", _("Memory remaining:"), val, unit);
+ val = prettyCapacity(info.memTotal, &unit);
+ vshPrint(ctl, "%-17s %-0.3lf %s\n", _("Memory total:"), val, unit);
+ }
+ if (info.fileTotal || info.fileRemaining || info.fileProcessed) {
+ val = prettyCapacity(info.fileProcessed, &unit);
+ vshPrint(ctl, "%-17s %-0.3lf %s\n", _("File processed:"), val, unit);
+ val = prettyCapacity(info.fileRemaining, &unit);
+ vshPrint(ctl, "%-17s %-0.3lf %s\n", _("File remaining:"), val, unit);
+ val = prettyCapacity(info.fileTotal, &unit);
+ vshPrint(ctl, "%-17s %-0.3lf %s\n", _("File total:"), val, unit);
+ }
+ } else {
+ ret = FALSE;
+ }
+cleanup:
+ virDomainFree(dom);
+ return ret;
+}
+
/*
* "freecell" command
*/
}
-static double
-prettyCapacity(unsigned long long val,
- const char **unit) {
- if (val < 1024) {
- *unit = "";
- return (double)val;
- } else if (val < (1024.0l * 1024.0l)) {
- *unit = "KB";
- return (((double)val / 1024.0l));
- } else if (val < (1024.0l * 1024.0l * 1024.0l)) {
- *unit = "MB";
- return ((double)val / (1024.0l * 1024.0l));
- } else if (val < (1024.0l * 1024.0l * 1024.0l * 1024.0l)) {
- *unit = "GB";
- return ((double)val / (1024.0l * 1024.0l * 1024.0l));
- } else {
- *unit = "TB";
- return ((double)val / (1024.0l * 1024.0l * 1024.0l * 1024.0l));
- }
-}
-
/*
* "pool-info" command
*/
{"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},