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

Maxim Nestratov mnestratov at virtuozzo.com
Thu May 19 10:38:03 UTC 2016


18.05.2016 11:11, Nikolay Shirokovskiy пишет:

> 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 | 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;

Just realized that since virDomainObjListAdd returns a valid and 
existing domain if its name and uuid is the same this call can cause 
some problems.
For instance, if a user specified the same source and destination uri, 
the migration will fail and Finish code will remove the domain from list.
Thus what about the following chunk instead existing?:

     virObjectLock(privconn->driver);
     dom = virDomainObjListFindByUUID(privconn->driver->domains, mig->uuid);
     if (dom) {
         char uuidstr[VIR_UUID_STRING_BUFLEN];
         virUUIDFormat(mig->uuid, uuidstr);
         virReportError(VIR_ERR_INTERNAL_ERROR,
                        _("A domain with uuid '%s' already exists"),
                        uuidstr);
         goto unlock;
     }

     if (!(dom = vzNewDomain(privconn->driver,
                             dname ? dname : mig->name, mig->uuid)))
         goto unlock;

     ret = 0;

  unlock:
     virObjectUnlock(privconn->driver);

> +
> +    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);




More information about the libvir-list mailing list