static virDomainPtr openvzDomainCreateXML(virConnectPtr conn, const char *xml, unsigned int flags) { struct openvz_driver *driver = conn->privateData; virDomainDefPtr vmdef = NULL; virDomainObjPtr vm = NULL; virDomainPtr dom = NULL; const char *progstart[] = {VZCTL, "--quiet", "start", PROGRAM_SENTINAL, NULL}; virCheckFlags(0, NULL); openvzDriverLock(driver); if ((vmdef = virDomainDefParseString(driver->caps, xml, 1 << VIR_DOMAIN_VIRT_OPENVZ, VIR_DOMAIN_XML_INACTIVE)) == NULL) goto cleanup; vm = virDomainFindByName(&driver->domains, vmdef->name); if (vm) { openvzError(VIR_ERR_OPERATION_FAILED, _("Already an OPENVZ VM defined with the id '%s'"), vmdef->name); goto cleanup; } if (!(vm = virDomainAssignDef(driver->caps, &driver->domains, vmdef, false))) goto cleanup; vmdef = NULL; /* All OpenVZ domains seem to be persistent - this is a bit of a violation * of this libvirt API which is intended for transient domain creation */ vm->persistent = 1; if (openvzSetInitialConfig(vm->def) < 0) { VIR_ERROR(_("Error creating initial configuration")); goto cleanup; } if (openvzSetDefinedUUID(strtoI(vm->def->name), vm->def->uuid) < 0) { openvzError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not set UUID")); goto cleanup; } if (openvzDomainSetNetworkConfig(conn, vm->def) < 0) goto cleanup; openvzSetProgramSentinal(progstart, vm->def->name); if (virRun(progstart, NULL) < 0) { goto cleanup; } vm->pid = strtoI(vm->def->name); vm->def->id = vm->pid; virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_BOOTED); if (vm->def->maxvcpus > 0) { if (openvzDomainSetVcpusInternal(vm, vm->def->maxvcpus) < 0) { openvzError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not set number of virtual cpu")); goto cleanup; } } dom = virGetDomain(conn, vm->def->name, vm->def->uuid); if (dom) dom->id = vm->def->id; cleanup: virDomainDefFree(vmdef); if (vm) virDomainObjUnlock(vm); openvzDriverUnlock(driver); return dom; }
static virDomainPtr openvzDomainDefineXML(virConnectPtr conn, const char *xml) { struct openvz_driver *driver = conn->privateData; virDomainDefPtr vmdef = NULL; virDomainObjPtr vm = NULL; virDomainPtr dom = NULL; openvzDriverLock(driver); if ((vmdef = virDomainDefParseString(driver->caps, xml, 1 << VIR_DOMAIN_VIRT_OPENVZ, VIR_DOMAIN_XML_INACTIVE)) == NULL) goto cleanup; vm = virDomainFindByName(&driver->domains, vmdef->name); if (vm) { openvzError(VIR_ERR_OPERATION_FAILED, _("Already an OPENVZ VM active with the id '%s'"), vmdef->name); goto cleanup; } if (!(vm = virDomainAssignDef(driver->caps, &driver->domains, vmdef, false))) goto cleanup; vmdef = NULL; vm->persistent = 1; if (openvzSetInitialConfig(vm->def) < 0) { VIR_ERROR(_("Error creating initial configuration")); goto cleanup; } /* TODO: set quota */ if (openvzSetDefinedUUID(strtoI(vm->def->name), vm->def->uuid) < 0) { openvzError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not set UUID")); goto cleanup; } if (openvzDomainSetNetworkConfig(conn, vm->def) < 0) goto cleanup; if (vm->def->vcpus != vm->def->maxvcpus) { openvzError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("current vcpu count must equal maximum")); goto cleanup; } if (vm->def->maxvcpus > 0) { if (openvzDomainSetVcpusInternal(vm, vm->def->maxvcpus) < 0) { openvzError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not set number of virtual cpu")); goto cleanup; } } if (vm->def->mem.cur_balloon > 0) { if (openvzDomainSetMemoryInternal(vm, vm->def->mem.cur_balloon) < 0) { openvzError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not set memory size")); goto cleanup; } } dom = virGetDomain(conn, vm->def->name, vm->def->uuid); if (dom) dom->id = -1; cleanup: virDomainDefFree(vmdef); if (vm) virDomainObjUnlock(vm); openvzDriverUnlock(driver); return dom; }
static virDomainPtr vmwareDomainCreateXML(virConnectPtr conn, const char *xml, unsigned int flags ATTRIBUTE_UNUSED) { struct vmware_driver *driver = conn->privateData; virDomainDefPtr vmdef = NULL; virDomainObjPtr vm = NULL; virDomainPtr dom = NULL; char *vmx = NULL; char *vmxPath = NULL; vmwareDomainPtr pDomain = NULL; virVMXContext ctx; ctx.formatFileName = vmwareCopyVMXFileName; vmwareDriverLock(driver); if ((vmdef = virDomainDefParseString(driver->caps, xml, VIR_DOMAIN_XML_INACTIVE)) == NULL) goto cleanup; if (virDomainObjIsDuplicate(&driver->domains, vmdef, 1) < 0) goto cleanup; /* generate vmx file */ vmx = virVMXFormatConfig(&ctx, driver->caps, vmdef, 7); if (vmx == NULL) goto cleanup; if (vmwareVmxPath(vmdef, &vmxPath) < 0) goto cleanup; /* create vmx file */ if (virFileWriteStr(vmxPath, vmx, S_IRUSR|S_IWUSR) < 0) { vmwareError(VIR_ERR_INTERNAL_ERROR, _("Failed to write vmx file '%s'"), vmxPath); goto cleanup; } /* assign def */ if (!(vm = virDomainAssignDef(driver->caps, &driver->domains, vmdef, false))) goto cleanup; pDomain = vm->privateData; pDomain->vmxPath = strdup(vmxPath); vmwareDomainConfigDisplay(pDomain, vmdef); vmdef = NULL; if (vmwareStartVM(driver, vm) < 0) { virDomainRemoveInactive(&driver->domains, vm); vm = NULL; goto cleanup; } dom = virGetDomain(conn, vm->def->name, vm->def->uuid); if (dom) dom->id = vm->def->id; cleanup: virDomainDefFree(vmdef); VIR_FREE(vmx); VIR_FREE(vmxPath); if(vm) virDomainObjUnlock(vm); vmwareDriverUnlock(driver); return dom; }
int qemuMigrationWaitForCompletion(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 * 1000 * 1000ull }; struct timeval now; int rc; const char *job; switch (priv->jobActive) { case QEMU_JOB_MIGRATION_OUT: job = _("migration job"); break; case QEMU_JOB_SAVE: job = _("domain save job"); break; case QEMU_JOB_DUMP: job = _("domain core dump job"); break; default: job = _("job"); } if (!virDomainObjIsActive(vm)) { qemuReportError(VIR_ERR_INTERNAL_ERROR, _("%s: %s"), job, _("guest unexpectedly quit")); goto cleanup; } if (priv->jobSignals & QEMU_JOB_SIGNAL_CANCEL) { priv->jobSignals ^= QEMU_JOB_SIGNAL_CANCEL; VIR_DEBUG0("Cancelling job at client request"); qemuDomainObjEnterMonitorWithDriver(driver, vm); rc = qemuMonitorMigrateCancel(priv->mon); qemuDomainObjExitMonitorWithDriver(driver, vm); if (rc < 0) { VIR_WARN0("Unable to cancel job"); } } else if (priv->jobSignals & QEMU_JOB_SIGNAL_SUSPEND) { priv->jobSignals ^= QEMU_JOB_SIGNAL_SUSPEND; VIR_DEBUG0("Pausing domain for non-live migration"); if (qemuMigrationSetOffline(driver, vm) < 0) VIR_WARN0("Unable to pause domain"); } else if (priv->jobSignals & QEMU_JOB_SIGNAL_MIGRATE_DOWNTIME) { unsigned long long ms = priv->jobSignalsData.migrateDowntime; priv->jobSignals ^= QEMU_JOB_SIGNAL_MIGRATE_DOWNTIME; priv->jobSignalsData.migrateDowntime = 0; VIR_DEBUG("Setting migration downtime to %llums", ms); qemuDomainObjEnterMonitorWithDriver(driver, vm); rc = qemuMonitorSetMigrationDowntime(priv->mon, ms); qemuDomainObjExitMonitorWithDriver(driver, vm); if (rc < 0) VIR_WARN0("Unable to set migration downtime"); } else if (priv->jobSignals & QEMU_JOB_SIGNAL_MIGRATE_SPEED) { unsigned long bandwidth = priv->jobSignalsData.migrateBandwidth; priv->jobSignals ^= QEMU_JOB_SIGNAL_MIGRATE_SPEED; priv->jobSignalsData.migrateBandwidth = 0; VIR_DEBUG("Setting migration bandwidth to %luMbs", bandwidth); qemuDomainObjEnterMonitorWithDriver(driver, vm); rc = qemuMonitorSetMigrationSpeed(priv->mon, bandwidth); qemuDomainObjExitMonitorWithDriver(driver, vm); if (rc < 0) VIR_WARN0("Unable to set migration speed"); } /* Repeat check because the job signals might have caused * guest to die */ if (!virDomainObjIsActive(vm)) { qemuReportError(VIR_ERR_INTERNAL_ERROR, _("%s: %s"), job, _("guest unexpectedly quit")); goto cleanup; } 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 = timeval_to_ms(now) - priv->jobStart; switch (status) { case QEMU_MONITOR_MIGRATION_STATUS_INACTIVE: priv->jobInfo.type = VIR_DOMAIN_JOB_NONE; qemuReportError(VIR_ERR_OPERATION_FAILED, _("%s: %s"), job, _("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: %s"), job, _("unexpectedly failed")); break; case QEMU_MONITOR_MIGRATION_STATUS_CANCELLED: priv->jobInfo.type = VIR_DOMAIN_JOB_CANCELLED; qemuReportError(VIR_ERR_OPERATION_FAILED, _("%s: %s"), job, _("canceled by client")); break; } virDomainObjUnlock(vm); qemuDriverUnlock(driver); nanosleep(&ts, NULL); qemuDriverLock(driver); virDomainObjLock(vm); } cleanup: return ret; } /* Prepare is the first step, and it runs on the destination host. * * This version starts an empty VM listening on a localhost TCP port, and * sets up the corresponding virStream to handle the incoming data. */ int qemuMigrationPrepareTunnel(struct qemud_driver *driver, virConnectPtr dconn, virStreamPtr st, const char *dname, const char *dom_xml) { virDomainDefPtr def = NULL; virDomainObjPtr vm = NULL; virDomainEventPtr event = NULL; int ret = -1; int internalret; int dataFD[2] = { -1, -1 }; virBitmapPtr qemuCaps = NULL; qemuDomainObjPrivatePtr priv = NULL; struct timeval now; if (gettimeofday(&now, NULL) < 0) { virReportSystemError(errno, "%s", _("cannot get time of day")); return -1; } /* Parse the domain XML. */ if (!(def = virDomainDefParseString(driver->caps, dom_xml, VIR_DOMAIN_XML_INACTIVE))) goto cleanup; if (!qemuMigrationIsAllowed(def)) goto cleanup; /* Target domain name, maybe renamed. */ if (dname) { VIR_FREE(def->name); def->name = strdup(dname); if (def->name == NULL) goto cleanup; } if (virDomainObjIsDuplicate(&driver->domains, def, 1) < 0) goto cleanup; if (!(vm = virDomainAssignDef(driver->caps, &driver->domains, def, true))) { /* virDomainAssignDef already set the error */ goto cleanup; } def = NULL; priv = vm->privateData; if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) goto cleanup; priv->jobActive = QEMU_JOB_MIGRATION_OUT; /* Domain starts inactive, even if the domain XML had an id field. */ vm->def->id = -1; if (pipe(dataFD) < 0 || virSetCloseExec(dataFD[0]) < 0) { virReportSystemError(errno, "%s", _("cannot create pipe for tunnelled migration")); goto endjob; } /* check that this qemu version supports the interactive exec */ if (qemuCapsExtractVersionInfo(vm->def->emulator, vm->def->os.arch, NULL, &qemuCaps) < 0) { qemuReportError(VIR_ERR_INTERNAL_ERROR, _("Cannot determine QEMU argv syntax %s"), vm->def->emulator); goto endjob; } /* Start the QEMU daemon, with the same command-line arguments plus * -incoming stdin (which qemu_command might convert to exec:cat or fd:n) */ internalret = qemuProcessStart(dconn, driver, vm, "stdin", true, dataFD[1], NULL, VIR_VM_OP_MIGRATE_IN_START); if (internalret < 0) { qemuAuditDomainStart(vm, "migrated", false); /* Note that we don't set an error here because qemuProcessStart * should have already done that. */ if (!vm->persistent) { virDomainRemoveInactive(&driver->domains, vm); vm = NULL; } goto endjob; } if (virFDStreamOpen(st, dataFD[0]) < 0) { qemuAuditDomainStart(vm, "migrated", false); qemuProcessStop(driver, vm, 0); if (!vm->persistent) { if (qemuDomainObjEndJob(vm) > 0) virDomainRemoveInactive(&driver->domains, vm); vm = NULL; } virReportSystemError(errno, "%s", _("cannot pass pipe for tunnelled migration")); goto endjob; } qemuAuditDomainStart(vm, "migrated", true); event = virDomainEventNewFromObj(vm, VIR_DOMAIN_EVENT_STARTED, VIR_DOMAIN_EVENT_STARTED_MIGRATED); ret = 0; endjob: if (vm && qemuDomainObjEndJob(vm) == 0) vm = NULL; /* We set a fake job active which is held across * API calls until the finish() call. This prevents * any other APIs being invoked while incoming * migration is taking place */ if (vm && virDomainObjIsActive(vm)) { priv->jobActive = QEMU_JOB_MIGRATION_IN; priv->jobInfo.type = VIR_DOMAIN_JOB_UNBOUNDED; priv->jobStart = timeval_to_ms(now); } cleanup: qemuCapsFree(qemuCaps); virDomainDefFree(def); VIR_FORCE_CLOSE(dataFD[0]); VIR_FORCE_CLOSE(dataFD[1]); if (vm) virDomainObjUnlock(vm); if (event) qemuDomainEventQueue(driver, event); qemuDriverUnlock(driver); return ret; }
int qemuMigrationPrepareDirect(struct qemud_driver *driver, virConnectPtr dconn, const char *uri_in, char **uri_out, const char *dname, const char *dom_xml) { static int port = 0; virDomainDefPtr def = NULL; virDomainObjPtr vm = NULL; int this_port; char *hostname = NULL; char migrateFrom [64]; const char *p; virDomainEventPtr event = NULL; int ret = -1; int internalret; qemuDomainObjPrivatePtr priv = NULL; struct timeval now; if (gettimeofday(&now, NULL) < 0) { virReportSystemError(errno, "%s", _("cannot get time of day")); return -1; } /* The URI passed in may be NULL or a string "tcp://somehostname:port". * * If the URI passed in is NULL then we allocate a port number * from our pool of port numbers and return a URI of * "tcp://ourhostname:port". * * If the URI passed in is not NULL then we try to parse out the * port number and use that (note that the hostname is assumed * to be a correct hostname which refers to the target machine). */ if (uri_in == NULL) { this_port = QEMUD_MIGRATION_FIRST_PORT + port++; if (port == QEMUD_MIGRATION_NUM_PORTS) port = 0; /* Get hostname */ if ((hostname = virGetHostname(NULL)) == NULL) goto cleanup; if (STRPREFIX(hostname, "localhost")) { qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("hostname on destination resolved to localhost, but migration requires an FQDN")); goto cleanup; } /* XXX this really should have been a properly well-formed * URI, but we can't add in tcp:// now without breaking * compatability with old targets. We at least make the * new targets accept both syntaxes though. */ /* Caller frees */ internalret = virAsprintf(uri_out, "tcp:%s:%d", hostname, this_port); if (internalret < 0) { virReportOOMError(); goto cleanup; } } else { /* Check the URI starts with "tcp:". We will escape the * URI when passing it to the qemu monitor, so bad * characters in hostname part don't matter. */ if (!STRPREFIX (uri_in, "tcp:")) { qemuReportError (VIR_ERR_INVALID_ARG, "%s", _("only tcp URIs are supported for KVM/QEMU migrations")); goto cleanup; } /* Get the port number. */ p = strrchr (uri_in, ':'); if (p == strchr(uri_in, ':')) { /* Generate a port */ this_port = QEMUD_MIGRATION_FIRST_PORT + port++; if (port == QEMUD_MIGRATION_NUM_PORTS) port = 0; /* Caller frees */ if (virAsprintf(uri_out, "%s:%d", uri_in, this_port) < 0) { virReportOOMError(); goto cleanup; } } else { p++; /* definitely has a ':' in it, see above */ this_port = virParseNumber (&p); if (this_port == -1 || p-uri_in != strlen (uri_in)) { qemuReportError(VIR_ERR_INVALID_ARG, "%s", _("URI ended with incorrect ':port'")); goto cleanup; } } } if (*uri_out) VIR_DEBUG("Generated uri_out=%s", *uri_out); /* Parse the domain XML. */ if (!(def = virDomainDefParseString(driver->caps, dom_xml, VIR_DOMAIN_XML_INACTIVE))) goto cleanup; if (!qemuMigrationIsAllowed(def)) goto cleanup; /* Target domain name, maybe renamed. */ if (dname) { VIR_FREE(def->name); def->name = strdup(dname); if (def->name == NULL) goto cleanup; } if (virDomainObjIsDuplicate(&driver->domains, def, 1) < 0) goto cleanup; if (!(vm = virDomainAssignDef(driver->caps, &driver->domains, def, true))) { /* virDomainAssignDef already set the error */ goto cleanup; } def = NULL; priv = vm->privateData; if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) goto cleanup; priv->jobActive = QEMU_JOB_MIGRATION_OUT; /* Domain starts inactive, even if the domain XML had an id field. */ vm->def->id = -1; /* Start the QEMU daemon, with the same command-line arguments plus * -incoming tcp:0.0.0.0:port */ snprintf (migrateFrom, sizeof (migrateFrom), "tcp:0.0.0.0:%d", this_port); if (qemuProcessStart(dconn, driver, vm, migrateFrom, true, -1, NULL, VIR_VM_OP_MIGRATE_IN_START) < 0) { qemuAuditDomainStart(vm, "migrated", false); /* Note that we don't set an error here because qemuProcessStart * should have already done that. */ if (!vm->persistent) { if (qemuDomainObjEndJob(vm) > 0) virDomainRemoveInactive(&driver->domains, vm); vm = NULL; } goto endjob; } qemuAuditDomainStart(vm, "migrated", true); event = virDomainEventNewFromObj(vm, VIR_DOMAIN_EVENT_STARTED, VIR_DOMAIN_EVENT_STARTED_MIGRATED); ret = 0; endjob: if (vm && qemuDomainObjEndJob(vm) == 0) vm = NULL; /* We set a fake job active which is held across * API calls until the finish() call. This prevents * any other APIs being invoked while incoming * migration is taking place */ if (vm && virDomainObjIsActive(vm)) { priv->jobActive = QEMU_JOB_MIGRATION_IN; priv->jobInfo.type = VIR_DOMAIN_JOB_UNBOUNDED; priv->jobStart = timeval_to_ms(now); } cleanup: VIR_FREE(hostname); virDomainDefFree(def); if (ret != 0) VIR_FREE(*uri_out); if (vm) virDomainObjUnlock(vm); if (event) qemuDomainEventQueue(driver, event); return ret; }
static virDomainPtr vmwareDomainDefineXML(virConnectPtr conn, const char *xml) { struct vmware_driver *driver = conn->privateData; virDomainDefPtr vmdef = NULL; virDomainObjPtr vm = NULL; virDomainPtr dom = NULL; char *vmx = NULL; char *directoryName = NULL; char *fileName = NULL; char *vmxPath = NULL; vmwareDomainPtr pDomain = NULL; virVMXContext ctx; ctx.formatFileName = vmwareCopyVMXFileName; vmwareDriverLock(driver); if ((vmdef = virDomainDefParseString(driver->caps, xml, 1 << VIR_DOMAIN_VIRT_VMWARE, VIR_DOMAIN_XML_INACTIVE)) == NULL) goto cleanup; if (virDomainObjIsDuplicate(&driver->domains, vmdef, 1) < 0) goto cleanup; /* generate vmx file */ vmx = virVMXFormatConfig(&ctx, driver->caps, vmdef, 7); if (vmx == NULL) goto cleanup; if (vmwareVmxPath(vmdef, &vmxPath) < 0) goto cleanup; /* create vmx file */ if (virFileWriteStr(vmxPath, vmx, S_IRUSR|S_IWUSR) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Failed to write vmx file '%s'"), vmxPath); goto cleanup; } /* assign def */ if (!(vm = virDomainAssignDef(driver->caps, &driver->domains, vmdef, false))) goto cleanup; pDomain = vm->privateData; if ((pDomain->vmxPath = strdup(vmxPath)) == NULL) { virReportOOMError(); goto cleanup; } vmwareDomainConfigDisplay(pDomain, vmdef); vmdef = NULL; vm->persistent = 1; dom = virGetDomain(conn, vm->def->name, vm->def->uuid); if (dom) dom->id = -1; cleanup: virDomainDefFree(vmdef); VIR_FREE(vmx); VIR_FREE(directoryName); VIR_FREE(fileName); VIR_FREE(vmxPath); if (vm) virDomainObjUnlock(vm); vmwareDriverUnlock(driver); return dom; }