The newest version of migration protocol - version 3 with parameters is implemented.
Supported flags is VIR_MIGRATE_PAUSED only. Supported parameters are
VIR_MIGRATE_PARAM_URI and VIR_MIGRATE_PARAM_DEST_NAME. VIR_MIGRATE_PARAM_DEST_XML
is in VZ_MIGRATION_PARAMETERS for technical onyl reasons.
Signed-off-by: Nikolay Shirokovskiy <nshirokovskiy(a)virtuozzo.com>
---
src/vz/vz_driver.c | 443 +++++++++++++++++++++++++++++++++++++++++++++++++++++
src/vz/vz_sdk.c | 42 +++++
src/vz/vz_sdk.h | 6 +
3 files changed, 491 insertions(+)
diff --git a/src/vz/vz_driver.c b/src/vz/vz_driver.c
index 1497b72..d1b8558 100644
--- a/src/vz/vz_driver.c
+++ b/src/vz/vz_driver.c
@@ -139,6 +139,9 @@ vzBuildCapabilities(void)
caps->host.cpu = cpu;
+ if (virCapabilitiesAddHostMigrateTransport(caps, "vzmigr") < 0)
+ goto error;
+
if (!(data = cpuNodeData(cpu->arch))
|| cpuDecode(cpu, data, NULL, 0, NULL) < 0) {
goto cleanup;
@@ -1581,6 +1584,440 @@ static int vzDomainSetMemory(virDomainPtr domain, unsigned long
memory)
return vzDomainSetMemoryFlagsImpl(domain, memory, 0, false);
}
+typedef struct _vzMigrationCookie vzMigrationCookie;
+typedef vzMigrationCookie *vzMigrationCookiePtr;
+struct _vzMigrationCookie {
+ unsigned char session_uuid[VIR_UUID_BUFLEN];
+ unsigned char uuid[VIR_UUID_BUFLEN];
+ char *name;
+};
+
+static void
+vzMigrationCookieFree(vzMigrationCookiePtr mig)
+{
+ if (!mig)
+ return;
+ VIR_FREE(mig->name);
+ VIR_FREE(mig);
+}
+
+static int
+vzBakeCookie(vzMigrationCookiePtr mig, char **cookieout, int *cookieoutlen)
+{
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+ if (!cookieout || !cookieoutlen) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Migration cookie parameters are not provided."));
+ return -1;
+ }
+
+ *cookieout = NULL;
+ *cookieoutlen = 0;
+
+ virBufferAddLit(&buf, "<vz-migration>\n");
+ virBufferAdjustIndent(&buf, 2);
+ virUUIDFormat(mig->session_uuid, uuidstr);
+ virBufferAsprintf(&buf,
"<session-uuid>%s</session-uuid>\n", uuidstr);
+ virUUIDFormat(mig->uuid, uuidstr);
+ virBufferAsprintf(&buf, "<uuid>%s</uuid>\n", uuidstr);
+ virBufferAsprintf(&buf, "<name>%s</name>\n",
mig->name);
+ virBufferAdjustIndent(&buf, -2);
+ virBufferAddLit(&buf, "</vz-migration>\n");
+
+ if (virBufferCheckError(&buf) < 0)
+ return -1;
+
+ *cookieout = virBufferContentAndReset(&buf);
+ *cookieoutlen = strlen(*cookieout) + 1;
+
+ return 0;
+}
+
+static vzMigrationCookiePtr
+vzEatCookie(const char *cookiein, int cookieinlen)
+{
+ xmlDocPtr doc = NULL;
+ xmlXPathContextPtr ctx = NULL;
+ char *tmp;
+ vzMigrationCookiePtr mig = NULL;
+
+ if (VIR_ALLOC(mig) < 0)
+ return NULL;
+
+ if (!cookiein || cookieinlen <= 0 || cookiein[cookieinlen - 1] != '\0') {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Invalid migration cookie"));
+ goto error;
+ }
+
+ if (!(doc = virXMLParseStringCtxt(cookiein,
+ _("(_migration_cookie)"), &ctx)))
+ goto error;
+
+ if (!(tmp = virXPathString("string(./session-uuid[1])", ctx))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("missing session-uuid element in migration data"));
+ goto error;
+ }
+ if (virUUIDParse(tmp, mig->session_uuid) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("malformed session-uuid element in migration data"));
+ VIR_FREE(tmp);
+ goto error;
+ }
+ VIR_FREE(tmp);
+
+ if (!(tmp = virXPathString("string(./uuid[1])", ctx))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("missing uuid element in migration data"));
+ goto error;
+ }
+ if (virUUIDParse(tmp, mig->uuid) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("malformed uuid element in migration data"));
+ VIR_FREE(tmp);
+ goto error;
+ }
+ VIR_FREE(tmp);
+
+ if (!(mig->name = virXPathString("string(./name[1])", ctx))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("missing name element in migration data"));
+ goto error;
+ }
+
+ cleanup:
+ xmlXPathFreeContext(ctx);
+ xmlFreeDoc(doc);
+ return mig;
+
+ error:
+ vzMigrationCookieFree(mig);
+ mig = NULL;
+ goto cleanup;
+}
+
+#define VZ_MIGRATION_FLAGS VIR_MIGRATE_PAUSED
+
+#define VZ_MIGRATION_PARAMETERS \
+ VIR_MIGRATE_PARAM_DEST_XML, VIR_TYPED_PARAM_STRING, \
+ VIR_MIGRATE_PARAM_URI, VIR_TYPED_PARAM_STRING, \
+ VIR_MIGRATE_PARAM_DEST_NAME, VIR_TYPED_PARAM_STRING, \
+ NULL
+
+static char *
+vzDomainMigrateBegin3Params(virDomainPtr domain,
+ virTypedParameterPtr params,
+ int nparams,
+ char **cookieout,
+ int *cookieoutlen,
+ unsigned int flags)
+{
+ char *xml = NULL;
+ virDomainObjPtr dom = NULL;
+ vzConnPtr privconn = domain->conn->privateData;
+ vzMigrationCookiePtr mig = NULL;
+
+ virCheckFlags(VZ_MIGRATION_FLAGS, NULL);
+
+ if (virTypedParamsValidate(params, nparams, VZ_MIGRATION_PARAMETERS) < 0)
+ goto cleanup;
+
+ /* we can't do this check via VZ_MIGRATION_PARAMETERS as on preparation
+ * step domain xml will be passed via this parameter and it is a common
+ * style to use single allowed parameter list definition in all steps */
+ if (virTypedParamsGet(params, nparams, VIR_MIGRATE_PARAM_DEST_XML)) {
+ virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
+ _("Changing destination XML is not supported"));
+ goto cleanup;
+ }
+
+ if (!(dom = vzDomObjFromDomain(domain)))
+ goto cleanup;
+
+ if (VIR_ALLOC(mig) < 0)
+ goto cleanup;
+
+ if (VIR_STRDUP(mig->name, dom->def->name) < 0)
+ goto cleanup;
+
+ memcpy(mig->uuid, dom->def->uuid, VIR_UUID_BUFLEN);
+
+ if (vzBakeCookie(mig, cookieout, cookieoutlen) < 0)
+ goto cleanup;
+
+ xml = virDomainDefFormat(dom->def, privconn->driver->caps,
+ VIR_DOMAIN_XML_MIGRATABLE);
+
+ cleanup:
+
+ vzMigrationCookieFree(mig);
+ if (dom)
+ virObjectUnlock(dom);
+ return xml;
+}
+
+static char*
+vzMigrationCreateURI(void)
+{
+ char *hostname = NULL;
+ char *uri = NULL;
+
+ if (!(hostname = virGetHostname()))
+ goto cleanup;
+
+ if (STRPREFIX(hostname, "localhost")) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("hostname on destination resolved to localhost,"
+ " but migration requires an FQDN"));
+ goto cleanup;
+ }
+
+ if (virAsprintf(&uri, "vzmigr://%s", hostname) < 0)
+ goto cleanup;
+
+ cleanup:
+ VIR_FREE(hostname);
+ return uri;
+}
+
+static int
+vzDomainMigratePrepare3Params(virConnectPtr conn,
+ virTypedParameterPtr params,
+ int nparams,
+ const char *cookiein,
+ int cookieinlen,
+ char **cookieout,
+ int *cookieoutlen,
+ char **uri_out,
+ unsigned int flags)
+{
+ vzConnPtr privconn = conn->privateData;
+ const char *miguri = NULL;
+ const char *dname = NULL;
+ virDomainObjPtr dom = NULL;
+ vzMigrationCookiePtr mig = NULL;
+ int ret = -1;
+
+ virCheckFlags(VZ_MIGRATION_FLAGS, -1);
+
+ if (virTypedParamsValidate(params, nparams, VZ_MIGRATION_PARAMETERS) < 0)
+ goto cleanup;
+
+ if (virTypedParamsGetString(params, nparams,
+ VIR_MIGRATE_PARAM_URI, &miguri) < 0 ||
+ virTypedParamsGetString(params, nparams,
+ VIR_MIGRATE_PARAM_DEST_NAME, &dname) < 0)
+ goto cleanup;
+
+ /* We must set uri_out if miguri is not set. This is direct
+ * managed migration requirement */
+ if (!miguri && !(*uri_out = vzMigrationCreateURI()))
+ goto cleanup;
+
+ if (!(mig = vzEatCookie(cookiein, cookieinlen)))
+ goto cleanup;
+
+ memcpy(mig->session_uuid, privconn->driver->session_uuid, VIR_UUID_BUFLEN);
+
+ if (vzBakeCookie(mig, cookieout, cookieoutlen) < 0)
+ goto cleanup;
+
+ if (!(dom = vzNewDomain(privconn->driver,
+ dname ? dname : mig->name, mig->uuid)))
+ goto cleanup;
+
+ ret = 0;
+
+ cleanup:
+ vzMigrationCookieFree(mig);
+ if (dom)
+ virObjectUnlock(dom);
+ return ret;
+}
+
+static int
+vzConnectSupportsFeature(virConnectPtr conn ATTRIBUTE_UNUSED, int feature)
+{
+ switch (feature) {
+ case VIR_DRV_FEATURE_MIGRATION_PARAMS:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static virURIPtr
+vzParseVzURI(const char *uri_str)
+{
+ virURIPtr uri = NULL;
+
+ if (!(uri = virURIParse(uri_str)))
+ goto error;
+
+ if (!uri->scheme || !uri->server) {
+ virReportError(VIR_ERR_INVALID_ARG,
+ _("scheme and host are mandatory vz migration URI:
%s"),
+ uri_str);
+ goto error;
+ }
+
+ if (uri->user || uri->path || uri->query || uri->fragment) {
+ virReportError(VIR_ERR_INVALID_ARG,
+ _("only scheme, host and port are supported in "
+ "vz migration URI: %s"), uri_str);
+ goto error;
+ }
+
+ if (STRNEQ(uri->scheme, "vzmigr")) {
+ virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED,
+ _("unsupported scheme %s in migration URI %s"),
+ uri->scheme, uri_str);
+ goto error;
+ }
+
+ return uri;
+
+ error:
+ virURIFree(uri);
+ return NULL;
+}
+
+static int
+vzDomainMigratePerform3Params(virDomainPtr domain,
+ const char *dconnuri ATTRIBUTE_UNUSED,
+ virTypedParameterPtr params,
+ int nparams,
+ const char *cookiein,
+ int cookieinlen,
+ char **cookieout ATTRIBUTE_UNUSED,
+ int *cookieoutlen ATTRIBUTE_UNUSED,
+ unsigned int flags)
+{
+ int ret = -1;
+ virDomainObjPtr dom = NULL;
+ virURIPtr vzuri = NULL;
+ vzConnPtr privconn = domain->conn->privateData;
+ const char *miguri = NULL;
+ const char *dname = NULL;
+ vzMigrationCookiePtr mig = NULL;
+
+ virCheckFlags(VZ_MIGRATION_FLAGS, -1);
+
+ if (virTypedParamsValidate(params, nparams, VZ_MIGRATION_PARAMETERS) < 0)
+ goto cleanup;
+
+ if (virTypedParamsGetString(params, nparams,
+ VIR_MIGRATE_PARAM_URI, &miguri) < 0 ||
+ virTypedParamsGetString(params, nparams,
+ VIR_MIGRATE_PARAM_DEST_NAME, &dname) < 0)
+ goto cleanup;
+
+ if (!miguri) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("migrate uri is not set"));
+ goto cleanup;
+ }
+
+ if (!(mig = vzEatCookie(cookiein, cookieinlen)))
+ goto cleanup;
+
+ if (!(dom = vzDomObjFromDomain(domain)))
+ goto cleanup;
+
+ if (!(vzuri = vzParseVzURI(miguri)))
+ goto cleanup;
+
+ if (prlsdkMigrate(dom, vzuri, mig->session_uuid, dname, flags) < 0)
+ goto cleanup;
+
+ virDomainObjListRemove(privconn->driver->domains, dom);
+ dom = NULL;
+
+ ret = 0;
+
+ cleanup:
+ if (dom)
+ virObjectUnlock(dom);
+ virURIFree(vzuri);
+ vzMigrationCookieFree(mig);
+
+ return ret;
+}
+
+static virDomainPtr
+vzDomainMigrateFinish3Params(virConnectPtr dconn,
+ virTypedParameterPtr params,
+ int nparams,
+ const char *cookiein ATTRIBUTE_UNUSED,
+ int cookieinlen ATTRIBUTE_UNUSED,
+ char **cookieout ATTRIBUTE_UNUSED,
+ int *cookieoutlen ATTRIBUTE_UNUSED,
+ unsigned int flags,
+ int cancelled)
+{
+ virDomainObjPtr dom = NULL;
+ virDomainPtr domain = NULL;
+ vzConnPtr privconn = dconn->privateData;
+ vzDriverPtr driver = privconn->driver;
+ const char *name = NULL;
+
+ virCheckFlags(VZ_MIGRATION_FLAGS, NULL);
+
+ if (virTypedParamsValidate(params, nparams, VZ_MIGRATION_PARAMETERS) < 0)
+ goto cleanup;
+
+ if (virTypedParamsGetString(params, nparams,
+ VIR_MIGRATE_PARAM_DEST_NAME, &name) < 0)
+ goto cleanup;
+
+ if (!(dom = virDomainObjListFindByName(driver->domains, name))) {
+ virReportError(VIR_ERR_NO_DOMAIN,
+ _("no domain with matching name '%s'"), name);
+ goto cleanup;
+ }
+
+ if (cancelled) {
+ virDomainObjListRemove(driver->domains, dom);
+ dom = NULL;
+ goto cleanup;
+ }
+
+ if (prlsdkLoadDomain(driver, dom))
+ goto cleanup;
+
+ domain = virGetDomain(dconn, dom->def->name, dom->def->uuid);
+ if (domain)
+ domain->id = dom->def->id;
+
+ cleanup:
+ /* In this situation we have to restore domain on source. But the migration
+ * is already finished. */
+ if (!cancelled && !domain)
+ VIR_WARN("Can't provide domain '%s' after successfull
migration.", name);
+ virDomainObjEndAPI(&dom);
+ return domain;
+}
+
+static int
+vzDomainMigrateConfirm3Params(virDomainPtr domain ATTRIBUTE_UNUSED,
+ virTypedParameterPtr params,
+ int nparams,
+ const char *cookiein ATTRIBUTE_UNUSED,
+ int cookieinlen ATTRIBUTE_UNUSED,
+ unsigned int flags,
+ int cancelled ATTRIBUTE_UNUSED)
+{
+ virCheckFlags(VZ_MIGRATION_FLAGS, -1);
+
+ if (virTypedParamsValidate(params, nparams, VZ_MIGRATION_PARAMETERS) < 0)
+ return -1;
+
+ return 0;
+}
+
static virHypervisorDriver vzHypervisorDriver = {
.name = "vz",
.connectOpen = vzConnectOpen, /* 0.10.0 */
@@ -1648,6 +2085,12 @@ static virHypervisorDriver vzHypervisorDriver = {
.connectUnregisterCloseCallback = vzConnectUnregisterCloseCallback, /* 1.3.2 */
.domainSetMemoryFlags = vzDomainSetMemoryFlags, /* 1.3.4 */
.domainSetMemory = vzDomainSetMemory, /* 1.3.4 */
+ .connectSupportsFeature = vzConnectSupportsFeature, /* 1.3.5 */
+ .domainMigrateBegin3Params = vzDomainMigrateBegin3Params, /* 1.3.5 */
+ .domainMigratePrepare3Params = vzDomainMigratePrepare3Params, /* 1.3.5 */
+ .domainMigratePerform3Params = vzDomainMigratePerform3Params, /* 1.3.5 */
+ .domainMigrateFinish3Params = vzDomainMigrateFinish3Params, /* 1.3.5 */
+ .domainMigrateConfirm3Params = vzDomainMigrateConfirm3Params, /* 1.3.5 */
};
static virConnectDriver vzConnectDriver = {
diff --git a/src/vz/vz_sdk.c b/src/vz/vz_sdk.c
index fbcf4d9..559f149 100644
--- a/src/vz/vz_sdk.c
+++ b/src/vz/vz_sdk.c
@@ -4296,3 +4296,45 @@ int prlsdkSetMemsize(virDomainObjPtr dom, unsigned int memsize)
error:
return -1;
}
+
+/* high security is default choice for 2 reasons:
+ * 1. as this is the highest set security we can't get
+ * reject from server with high security settings
+ * 2. this is on par with security level of driver
+ * connection to dispatcher
+ */
+
+#define PRLSDK_MIGRATION_FLAGS (PSL_HIGH_SECURITY)
+
+int prlsdkMigrate(virDomainObjPtr dom, virURIPtr uri,
+ const unsigned char *session_uuid,
+ const char *dname,
+ unsigned int flags)
+{
+ int ret = -1;
+ vzDomObjPtr privdom = dom->privateData;
+ PRL_HANDLE job = PRL_INVALID_HANDLE;
+ char uuidstr[VIR_UUID_STRING_BUFLEN + 2];
+ PRL_UINT32 vzflags = PRLSDK_MIGRATION_FLAGS;
+
+ if (flags & VIR_MIGRATE_PAUSED)
+ vzflags |= PVMT_DONT_RESUME_VM;
+
+ prlsdkUUIDFormat(session_uuid, uuidstr);
+ job = PrlVm_MigrateWithRenameEx(privdom->sdkdom, uri->server,
+ uri->port, uuidstr,
+ dname == NULL ? "" : dname,
+ "",
+ vzflags,
+ 0,
+ PRL_TRUE
+ );
+
+ if (PRL_FAILED(waitJob(job)))
+ goto cleanup;
+
+ ret = 0;
+
+ cleanup:
+ return ret;
+}
diff --git a/src/vz/vz_sdk.h b/src/vz/vz_sdk.h
index e562f98..3d27d12 100644
--- a/src/vz/vz_sdk.h
+++ b/src/vz/vz_sdk.h
@@ -82,3 +82,9 @@ void
prlsdkDomObjFreePrivate(void *p);
/* memsize is in MiB */
int prlsdkSetMemsize(virDomainObjPtr dom, unsigned int memsize);
+int
+prlsdkMigrate(virDomainObjPtr dom,
+ virURIPtr uri,
+ const char unsigned *session_uuid,
+ const char *dname,
+ unsigned int flags);
--
1.8.3.1