[libvirt] [PATCH v6 REBASE 3/5] vz: implement managed migration

Nikolay Shirokovskiy nshirokovskiy at virtuozzo.com
Fri Apr 22 07:20:14 UTC 2016


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 at virtuozzo.com>
---
 src/vz/vz_driver.c | 444 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/vz/vz_sdk.c    |  42 +++++
 src/vz/vz_sdk.h    |   6 +
 3 files changed, 492 insertions(+)

diff --git a/src/vz/vz_driver.c b/src/vz/vz_driver.c
index 1497b72..46f2487 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,441 @@ 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 = NULL;
+    vzMigrationCookiePtr mig = NULL;
+
+    if (VIR_ALLOC(mig) < 0)
+        return NULL;
+
+    if (!cookiein)
+        return mig;
+
+    if (cookiein[cookieinlen - 1] != '\0') {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Migration cookie was not NULL terminated"));
+        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"));
+        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"));
+        goto error;
+    }
+
+    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);
+    VIR_FREE(tmp);
+    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 (!(mig = vzEatCookie(NULL, 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
+     * managmed 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 +2086,12 @@ static virHypervisorDriver vzHypervisorDriver = {
     .connectUnregisterCloseCallback = vzConnectUnregisterCloseCallback, /* 1.3.2 */
     .domainSetMemoryFlags = vzDomainSetMemoryFlags, /* 1.3.4 */
     .domainSetMemory = vzDomainSetMemory, /* 1.3.4 */
+    .connectSupportsFeature = vzConnectSupportsFeature, /* 1.3.4 */
+    .domainMigrateBegin3Params = vzDomainMigrateBegin3Params, /* 1.3.4 */
+    .domainMigratePrepare3Params = vzDomainMigratePrepare3Params, /* 1.3.4 */
+    .domainMigratePerform3Params = vzDomainMigratePerform3Params, /* 1.3.4 */
+    .domainMigrateFinish3Params = vzDomainMigrateFinish3Params, /* 1.3.4 */
+    .domainMigrateConfirm3Params = vzDomainMigrateConfirm3Params, /* 1.3.4 */
 };
 
 static virConnectDriver vzConnectDriver = {
diff --git a/src/vz/vz_sdk.c b/src/vz/vz_sdk.c
index 5fee10c..f6a1152 100644
--- a/src/vz/vz_sdk.c
+++ b/src/vz/vz_sdk.c
@@ -4289,3 +4289,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




More information about the libvir-list mailing list