[libvirt] [PATCH 1/6] vz: add migration backbone code

From: Nikolay Shirokovskiy <nshirokovskiy@virtuozzo.com> This patch makes basic vz migration possible. For example by virsh: virsh -c vz:///system migrate $NAME vz+ssh://$DST/system Vz migration is implemented thru interface for managed migrations for drivers although it looks like a candadate for direct migration as all work is done by vz sdk. The reason is that vz sdk lacks rich remote authentication capabilities of libvirt and if we choose to implement direct migration we have to reimplement auth means of libvirt. This brings the requirement that destination side should have running libvirt daemon. This is not the problem as vz is moving in the direction of tight integration with libvirt. Another issue of this choice is that if the managment migration fails on 'finish' step driver is supposed to resume on source. This is not compatible with vz sdk migration but this can be overcome without loosing a constistency, see comments in code. Technically we have a libvirt connection to destination in managed migration scheme and we use this connection to obtain a session_uuid (which acts as authZ token) for vz migration. This uuid is passed from destination through cookie on 'prepare' step. A few words on vz migration uri. I'd probably use just 'hostname:port' uris as we don't have different migration schemes in vz but scheme part is mandatory, so 'tcp' is used. Looks like good name. Signed-off-by: Nikolay Shirokovskiy <nshirokovskiy@virtuozzo.com> --- src/vz/vz_driver.c | 250 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/vz/vz_sdk.c | 79 ++++++++++++++-- src/vz/vz_sdk.h | 2 + src/vz/vz_utils.h | 1 + 4 files changed, 322 insertions(+), 10 deletions(-) diff --git a/src/vz/vz_driver.c b/src/vz/vz_driver.c index 9f0c52f..e003646 100644 --- a/src/vz/vz_driver.c +++ b/src/vz/vz_driver.c @@ -1343,6 +1343,250 @@ vzDomainMemoryStats(virDomainPtr domain, return ret; } +static int +vzConnectSupportsFeature(virConnectPtr conn ATTRIBUTE_UNUSED, int feature) +{ + switch (feature) { + case VIR_DRV_FEATURE_MIGRATION_PARAMS: + return 1; + default: + return 0; + } +} + +#define VZ_MIGRATION_PARAMETERS NULL + +static char * +vzDomainMigrateBegin3Params(virDomainPtr domain, + virTypedParameterPtr params, + int nparams, + char **cookieout ATTRIBUTE_UNUSED, + int *cookieoutlen ATTRIBUTE_UNUSED, + unsigned int fflags ATTRIBUTE_UNUSED) +{ + virDomainObjPtr dom = NULL; + char *xml = NULL; + + if (virTypedParamsValidate(params, nparams, VZ_MIGRATION_PARAMETERS) < 0) + goto cleanup; + + if (!(dom = vzDomObjFromDomain(domain))) + goto cleanup; + + xml = virDomainDefFormat(dom->def, VIR_DOMAIN_DEF_FORMAT_SECURE); + + cleanup: + if (dom) + virObjectUnlock(dom); + + return xml; +} + +/* return 'hostname' */ +static char * +vzCreateMigrateUri(void) +{ + char *hostname = NULL; + char *out = NULL; + virURI uri = {}; + + if ((hostname = virGetHostname()) == NULL) + 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; + } + + /* to set const string to non-const */ + if (VIR_STRDUP(uri.scheme, "tcp") < 0) + goto cleanup; + uri.server = hostname; + out = virURIFormat(&uri); + + cleanup: + VIR_FREE(hostname); + VIR_FREE(uri.scheme); + return out; +} + +static int +vzDomainMigratePrepare3Params(virConnectPtr dconn, + virTypedParameterPtr params ATTRIBUTE_UNUSED, + int nparams ATTRIBUTE_UNUSED, + const char *cookiein ATTRIBUTE_UNUSED, + int cookieinlen ATTRIBUTE_UNUSED, + char **cookieout, + int *cookieoutlen, + char **uri_out, + unsigned int fflags ATTRIBUTE_UNUSED) +{ + vzConnPtr privconn = dconn->privateData; + int ret = -1; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + *cookieout = NULL; + *uri_out = NULL; + + virUUIDFormat(privconn->session_uuid, uuidstr); + if (VIR_STRDUP(*cookieout, uuidstr) < 0) + goto cleanup; + *cookieoutlen = strlen(*cookieout) + 1; + + if (!(*uri_out = vzCreateMigrateUri())) + goto cleanup; + + ret = 0; + + cleanup: + if (ret != 0) { + VIR_FREE(*cookieout); + VIR_FREE(*uri_out); + *cookieoutlen = 0; + } + + return ret; +} + +static int +vzDomainMigratePerform3Params(virDomainPtr domain, + const char *dconnuri ATTRIBUTE_UNUSED, + virTypedParameterPtr params, + int nparams, + const char *cookiein, + int cookieinlen ATTRIBUTE_UNUSED, + char **cookieout, + int *cookieoutlen, + unsigned int fflags ATTRIBUTE_UNUSED) +{ + int ret = -1; + virDomainObjPtr dom = NULL; + const char *uri = NULL; + unsigned char session_uuid[VIR_UUID_BUFLEN]; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + *cookieout = NULL; + + if (virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_URI, + &uri) < 0) + goto cleanup; + + if (!(dom = vzDomObjFromDomain(domain))) + goto cleanup; + + if (!uri) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("migration URI should be provided")); + goto cleanup; + } + + if (virUUIDParse(cookiein, session_uuid) < 0) + goto cleanup; + + if (prlsdkMigrate(dom, uri, session_uuid) < 0) + goto cleanup; + + virUUIDFormat(domain->uuid, uuidstr); + if (VIR_STRDUP(*cookieout, uuidstr) < 0) + goto cleanup; + *cookieoutlen = strlen(*cookieout) + 1; + + ret = 0; + + cleanup: + if (dom) + virObjectUnlock(dom); + if (ret != 0) { + VIR_FREE(*cookieout); + *cookieoutlen = 0; + } + + return ret; +} + +/* if we return NULL from this function we are supposed + to cleanup destination side, but we can't do it + because 'perform' step is finished and migration is actually + completed by dispatcher. Unfortunately in OOM situation we + have to return NULL. As a result high level migration + function return NULL which is supposed to be + treated as migration error while migration is + actually successful. This should not be a problem + as we are in consistent state. For example later + attempts to list source and destination domains + will reveal actual situation. */ + +static virDomainPtr +vzDomainMigrateFinish3Params(virConnectPtr dconn, + virTypedParameterPtr params ATTRIBUTE_UNUSED, + int nparams ATTRIBUTE_UNUSED, + const char *cookiein, + int cookieinlen ATTRIBUTE_UNUSED, + char **cookieout ATTRIBUTE_UNUSED, + int *cookieoutlen ATTRIBUTE_UNUSED, + unsigned int fflags ATTRIBUTE_UNUSED, + int cancelled) +{ + vzConnPtr privconn = dconn->privateData; + virDomainObjPtr dom = NULL; + virDomainPtr domain = NULL; + unsigned char domain_uuid[VIR_UUID_BUFLEN]; + + /* we have nothing to cleanup, whole job is done by PCS dispatcher */ + if (cancelled) + return NULL; + + if (virUUIDParse(cookiein, domain_uuid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not parse UUID from string '%s'"), + cookiein); + goto cleanup; + } + + if (!(dom = prlsdkAddDomain(privconn, domain_uuid))) + goto cleanup; + + domain = virGetDomain(dconn, dom->def->name, dom->def->uuid); + if (domain) + domain->id = dom->def->id; + + cleanup: + if (!domain) + VIR_WARN("Can't provide domain with uuid '%s' after successfull migration.", cookiein); + virDomainObjEndAPI(&dom); + return domain; +} + +/* This is executed only if 'perform' step is successfull that + is migration is completed by PCS dispatcher. Thus we should + ignore 'canceled' parameter and always kill source domain. */ +static int +vzDomainMigrateConfirm3Params(virDomainPtr domain, + virTypedParameterPtr params ATTRIBUTE_UNUSED, + int nparams ATTRIBUTE_UNUSED, + const char *cookiein ATTRIBUTE_UNUSED, + int cookieinlen ATTRIBUTE_UNUSED, + unsigned int fflags ATTRIBUTE_UNUSED, + int cancelled ATTRIBUTE_UNUSED) +{ + vzConnPtr privconn = domain->conn->privateData; + virDomainObjPtr dom = NULL; + + if (!(dom = vzDomObjFromDomain(domain))) + goto cleanup; + + virDomainObjListRemove(privconn->domains, dom); + + cleanup: + if (dom) + virObjectUnlock(dom); + + return 0; +} + static virHypervisorDriver vzDriver = { .name = "vz", .connectOpen = vzConnectOpen, /* 0.10.0 */ @@ -1396,6 +1640,12 @@ static virHypervisorDriver vzDriver = { .domainBlockStatsFlags = vzDomainBlockStatsFlags, /* 1.2.17 */ .domainInterfaceStats = vzDomainInterfaceStats, /* 1.2.17 */ .domainMemoryStats = vzDomainMemoryStats, /* 1.2.17 */ + .connectSupportsFeature = vzConnectSupportsFeature, /* 1.2.18 */ + .domainMigrateBegin3Params = vzDomainMigrateBegin3Params, /* 1.2.18 */ + .domainMigratePrepare3Params = vzDomainMigratePrepare3Params, /* 1.2.18 */ + .domainMigratePerform3Params = vzDomainMigratePerform3Params, /* 1.2.18 */ + .domainMigrateFinish3Params = vzDomainMigrateFinish3Params, /* 1.2.18 */ + .domainMigrateConfirm3Params = vzDomainMigrateConfirm3Params, /* 1.2.18 */ }; static virConnectDriver vzConnectDriver = { diff --git a/src/vz/vz_sdk.c b/src/vz/vz_sdk.c index d1bc312..908bfc1 100644 --- a/src/vz/vz_sdk.c +++ b/src/vz/vz_sdk.c @@ -37,6 +37,9 @@ #define VIR_FROM_THIS VIR_FROM_PARALLELS #define JOB_INFINIT_WAIT_TIMEOUT UINT_MAX +static int +prlsdkUUIDParse(const char *uuidstr, unsigned char *uuid); + VIR_LOG_INIT("parallels.sdk"); /* @@ -228,24 +231,40 @@ prlsdkDeinit(void) int prlsdkConnect(vzConnPtr privconn) { - PRL_RESULT ret; + int ret = -1; + PRL_RESULT pret; PRL_HANDLE job = PRL_INVALID_HANDLE; + PRL_HANDLE result = PRL_INVALID_HANDLE; + PRL_HANDLE response = PRL_INVALID_HANDLE; + char session_uuid[VIR_UUID_STRING_BUFLEN + 2]; + PRL_UINT32 buflen = ARRAY_CARDINALITY(session_uuid); - ret = PrlSrv_Create(&privconn->server); - if (PRL_FAILED(ret)) { - logPrlError(ret); - return -1; - } + pret = PrlSrv_Create(&privconn->server); + prlsdkCheckRetGoto(pret, cleanup); job = PrlSrv_LoginLocalEx(privconn->server, NULL, 0, PSL_HIGH_SECURITY, PACF_NON_INTERACTIVE_MODE); + if (PRL_FAILED(getJobResult(job, &result))) + goto cleanup; + + pret = PrlResult_GetParam(result, &response); + prlsdkCheckRetGoto(pret, cleanup); + + pret = PrlLoginResponse_GetSessionUuid(response, session_uuid, &buflen); + prlsdkCheckRetGoto(pret, cleanup); + + if (prlsdkUUIDParse(session_uuid, privconn->session_uuid) < 0) + goto cleanup; + + ret = 0; - if (waitJob(job)) { + cleanup: + if (ret < 0) PrlHandle_Free(privconn->server); - return -1; - } + PrlHandle_Free(result); + PrlHandle_Free(response); - return 0; + return ret; } void @@ -4035,3 +4054,43 @@ prlsdkGetMemoryStats(virDomainObjPtr dom, return ret; } + +/* 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, const char* uri_str, + const unsigned char *session_uuid) +{ + int ret = -1; + vzDomObjPtr privdom = dom->privateData; + virURIPtr uri = NULL; + PRL_HANDLE job = PRL_INVALID_HANDLE; + char uuidstr[VIR_UUID_STRING_BUFLEN + 2]; + + uri = virURIParse(uri_str); + /* no special error logs as uri should be checked on prepare step */ + if (uri == NULL) + goto cleanup; + + prlsdkUUIDFormat(session_uuid, uuidstr); + job = PrlVm_MigrateEx(privdom->sdkdom, uri->server, uri->port, uuidstr, + "", /* use default dir for migrated instance bundle */ + PRLSDK_MIGRATION_FLAGS, + 0, /* reserved flags */ + PRL_TRUE /* don't ask for confirmations */ + ); + + if (PRL_FAILED(waitJob(job))) + goto cleanup; + + ret = 0; + + cleanup: + virURIFree(uri); + return ret; +} diff --git a/src/vz/vz_sdk.h b/src/vz/vz_sdk.h index ebe4591..1a90eca 100644 --- a/src/vz/vz_sdk.h +++ b/src/vz/vz_sdk.h @@ -76,3 +76,5 @@ int prlsdkGetVcpuStats(virDomainObjPtr dom, int idx, unsigned long long *time); int prlsdkGetMemoryStats(virDomainObjPtr dom, virDomainMemoryStatPtr stats, unsigned int nr_stats); +int +prlsdkMigrate(virDomainObjPtr dom, const char* uri_str, const char unsigned *session_uuid); diff --git a/src/vz/vz_utils.h b/src/vz/vz_utils.h index db09647..a779b03 100644 --- a/src/vz/vz_utils.h +++ b/src/vz/vz_utils.h @@ -62,6 +62,7 @@ struct _vzConn { virDomainObjListPtr domains; PRL_HANDLE server; + unsigned char session_uuid[VIR_UUID_BUFLEN]; virStoragePoolObjList pools; virNetworkObjListPtr networks; virCapsPtr caps; -- 1.7.1

From: Nikolay Shirokovskiy <nshirokovskiy@virtuozzo.com> This way we can easily keep backward compatibility in the future. Use 2 distinct cookies format: 1 - between phases 'prepare' and 'perform' 2 - between phases 'perform' and 'finish' I see no reason to use unified format like in qemu yet. Signed-off-by: Nikolay Shirokovskiy <nshirokovskiy@virtuozzo.com> --- src/vz/vz_driver.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++------ src/vz/vz_sdk.c | 3 + 2 files changed, 102 insertions(+), 12 deletions(-) diff --git a/src/vz/vz_driver.c b/src/vz/vz_driver.c index e003646..d5cbdc6 100644 --- a/src/vz/vz_driver.c +++ b/src/vz/vz_driver.c @@ -1412,6 +1412,101 @@ vzCreateMigrateUri(void) return out; } +static char* +vzFormatCookie1(const unsigned char *session_uuid) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virBuffer buf = VIR_BUFFER_INITIALIZER; + + virBufferAddLit(&buf, "<vz-migration1>\n"); + virUUIDFormat(session_uuid, uuidstr); + virBufferAsprintf(&buf, "<session_uuid>%s</session_uuid>\n", uuidstr); + virBufferAddLit(&buf, "</vz-migration1>\n"); + + if (virBufferCheckError(&buf) < 0) + return NULL; + + return virBufferContentAndReset(&buf); +} + +static char* +vzFormatCookie2(const unsigned char *domain_uuid) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virBuffer buf = VIR_BUFFER_INITIALIZER; + + virBufferAddLit(&buf, "<vz-migration2>\n"); + virUUIDFormat(domain_uuid, uuidstr); + virBufferAsprintf(&buf, "<domain_uuid>%s</domain_uuid>\n", uuidstr); + virBufferAddLit(&buf, "</vz-migration2>\n"); + + if (virBufferCheckError(&buf) < 0) + return NULL; + + return virBufferContentAndReset(&buf); +} + +static int +vzParseCookie1(const char *xml, unsigned char *session_uuid) +{ + xmlDocPtr doc = NULL; + xmlXPathContextPtr ctx = NULL; + char *tmp = NULL; + int ret = -1; + + if (!(doc = virXMLParseStringCtxt(xml, _("(_migration_cookie)"), &ctx))) + goto cleanup; + + if (!(tmp = virXPathString("string(./session_uuid[1])", ctx))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("missing session_uuid element in migration data")); + goto cleanup; + } + if (virUUIDParse(tmp, session_uuid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("malformed session_uuid element in migration data")); + goto cleanup; + } + ret = 0; + + cleanup: + xmlXPathFreeContext(ctx); + xmlFreeDoc(doc); + VIR_FREE(tmp); + + return ret; +} + +static int +vzParseCookie2(const char *xml, unsigned char *domain_uuid) +{ + xmlDocPtr doc = NULL; + xmlXPathContextPtr ctx = NULL; + char *tmp = NULL; + int ret = -1; + if (!(doc = virXMLParseStringCtxt(xml, _("(_migration_cookie)"), &ctx))) + goto cleanup; + + if (!(tmp = virXPathString("string(./domain_uuid[1])", ctx))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("missing domain_uuid element in migration data")); + goto cleanup; + } + if (virUUIDParse(tmp, domain_uuid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("malformed domain_uuid element in migration data")); + goto cleanup; + } + ret = 0; + + cleanup: + xmlXPathFreeContext(ctx); + xmlFreeDoc(doc); + VIR_FREE(tmp); + + return ret; +} + static int vzDomainMigratePrepare3Params(virConnectPtr dconn, virTypedParameterPtr params ATTRIBUTE_UNUSED, @@ -1425,13 +1520,11 @@ vzDomainMigratePrepare3Params(virConnectPtr dconn, { vzConnPtr privconn = dconn->privateData; int ret = -1; - char uuidstr[VIR_UUID_STRING_BUFLEN]; *cookieout = NULL; *uri_out = NULL; - virUUIDFormat(privconn->session_uuid, uuidstr); - if (VIR_STRDUP(*cookieout, uuidstr) < 0) + if (!(*cookieout = vzFormatCookie1(privconn->session_uuid))) goto cleanup; *cookieoutlen = strlen(*cookieout) + 1; @@ -1465,7 +1558,6 @@ vzDomainMigratePerform3Params(virDomainPtr domain, virDomainObjPtr dom = NULL; const char *uri = NULL; unsigned char session_uuid[VIR_UUID_BUFLEN]; - char uuidstr[VIR_UUID_STRING_BUFLEN]; *cookieout = NULL; @@ -1483,14 +1575,13 @@ vzDomainMigratePerform3Params(virDomainPtr domain, goto cleanup; } - if (virUUIDParse(cookiein, session_uuid) < 0) + if (vzParseCookie1(cookiein, session_uuid) < 0) goto cleanup; if (prlsdkMigrate(dom, uri, session_uuid) < 0) goto cleanup; - virUUIDFormat(domain->uuid, uuidstr); - if (VIR_STRDUP(*cookieout, uuidstr) < 0) + if (!(*cookieout = vzFormatCookie2(dom->def->uuid))) goto cleanup; *cookieoutlen = strlen(*cookieout) + 1; @@ -1539,12 +1630,8 @@ vzDomainMigrateFinish3Params(virConnectPtr dconn, if (cancelled) return NULL; - if (virUUIDParse(cookiein, domain_uuid) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Could not parse UUID from string '%s'"), - cookiein); + if (vzParseCookie2(cookiein, domain_uuid) < 0) goto cleanup; - } if (!(dom = prlsdkAddDomain(privconn, domain_uuid))) goto cleanup; diff --git a/src/vz/vz_sdk.c b/src/vz/vz_sdk.c index 908bfc1..a329c68 100644 --- a/src/vz/vz_sdk.c +++ b/src/vz/vz_sdk.c @@ -42,6 +42,9 @@ prlsdkUUIDParse(const char *uuidstr, unsigned char *uuid); VIR_LOG_INIT("parallels.sdk"); +static int +prlsdkUUIDParse(const char *uuidstr, unsigned char *uuid); + /* * Log error description */ -- 1.7.1

From: Nikolay Shirokovskiy <nshirokovskiy@virtuozzo.com> --- src/vz/vz_driver.c | 12 +++++++++--- src/vz/vz_sdk.c | 5 +++-- src/vz/vz_sdk.h | 5 ++++- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/vz/vz_driver.c b/src/vz/vz_driver.c index d5cbdc6..a42597c 100644 --- a/src/vz/vz_driver.c +++ b/src/vz/vz_driver.c @@ -1354,7 +1354,9 @@ vzConnectSupportsFeature(virConnectPtr conn ATTRIBUTE_UNUSED, int feature) } } -#define VZ_MIGRATION_PARAMETERS NULL +#define VZ_MIGRATION_PARAMETERS \ + VIR_MIGRATE_PARAM_DEST_NAME, VIR_TYPED_PARAM_STRING, \ + NULL static char * vzDomainMigrateBegin3Params(virDomainPtr domain, @@ -1558,12 +1560,16 @@ vzDomainMigratePerform3Params(virDomainPtr domain, virDomainObjPtr dom = NULL; const char *uri = NULL; unsigned char session_uuid[VIR_UUID_BUFLEN]; + const char *dname = NULL; *cookieout = NULL; if (virTypedParamsGetString(params, nparams, VIR_MIGRATE_PARAM_URI, - &uri) < 0) + &uri) < 0 || + virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_DEST_NAME, + &dname) < 0) goto cleanup; if (!(dom = vzDomObjFromDomain(domain))) @@ -1578,7 +1584,7 @@ vzDomainMigratePerform3Params(virDomainPtr domain, if (vzParseCookie1(cookiein, session_uuid) < 0) goto cleanup; - if (prlsdkMigrate(dom, uri, session_uuid) < 0) + if (prlsdkMigrate(dom, uri, session_uuid, dname) < 0) goto cleanup; if (!(*cookieout = vzFormatCookie2(dom->def->uuid))) diff --git a/src/vz/vz_sdk.c b/src/vz/vz_sdk.c index a329c68..f1fa6da 100644 --- a/src/vz/vz_sdk.c +++ b/src/vz/vz_sdk.c @@ -4067,7 +4067,7 @@ prlsdkGetMemoryStats(virDomainObjPtr dom, #define PRLSDK_MIGRATION_FLAGS (PSL_HIGH_SECURITY) int prlsdkMigrate(virDomainObjPtr dom, const char* uri_str, - const unsigned char *session_uuid) + const unsigned char *session_uuid, const char *dname) { int ret = -1; vzDomObjPtr privdom = dom->privateData; @@ -4081,7 +4081,8 @@ int prlsdkMigrate(virDomainObjPtr dom, const char* uri_str, goto cleanup; prlsdkUUIDFormat(session_uuid, uuidstr); - job = PrlVm_MigrateEx(privdom->sdkdom, uri->server, uri->port, uuidstr, + job = PrlVm_MigrateWithRenameEx(privdom->sdkdom, uri->server, uri->port, uuidstr, + dname == NULL ? "" : dname, "", /* use default dir for migrated instance bundle */ PRLSDK_MIGRATION_FLAGS, 0, /* reserved flags */ diff --git a/src/vz/vz_sdk.h b/src/vz/vz_sdk.h index 1a90eca..971f913 100644 --- a/src/vz/vz_sdk.h +++ b/src/vz/vz_sdk.h @@ -77,4 +77,7 @@ prlsdkGetVcpuStats(virDomainObjPtr dom, int idx, unsigned long long *time); int prlsdkGetMemoryStats(virDomainObjPtr dom, virDomainMemoryStatPtr stats, unsigned int nr_stats); int -prlsdkMigrate(virDomainObjPtr dom, const char* uri_str, const char unsigned *session_uuid); +prlsdkMigrate(virDomainObjPtr dom, + const char* uri_str, + const unsigned char *session_uuid, + const char* dname); -- 1.7.1

From: Nikolay Shirokovskiy <nshirokovskiy@virtuozzo.com> Signed-off-by: Nikolay Shirokovskiy <nshirokovskiy@virtuozzo.com> --- src/vz/vz_driver.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 51 insertions(+), 1 deletions(-) diff --git a/src/vz/vz_driver.c b/src/vz/vz_driver.c index a42597c..9fefac1 100644 --- a/src/vz/vz_driver.c +++ b/src/vz/vz_driver.c @@ -1356,6 +1356,7 @@ vzConnectSupportsFeature(virConnectPtr conn ATTRIBUTE_UNUSED, int feature) #define VZ_MIGRATION_PARAMETERS \ VIR_MIGRATE_PARAM_DEST_NAME, VIR_TYPED_PARAM_STRING, \ + VIR_MIGRATE_PARAM_URI, VIR_TYPED_PARAM_STRING, \ NULL static char * @@ -1509,6 +1510,45 @@ vzParseCookie2(const char *xml, unsigned char *domain_uuid) return ret; } +/* return copy of 'in' and check it is correct */ +static char * +vzAdaptInUri(const char *in) +{ + virURIPtr uri = NULL; + char *out = NULL; + + uri = virURIParse(in); + + if (uri->scheme == NULL || uri->server == NULL) { + virReportError(VIR_ERR_INVALID_ARG, + _("scheme and host are mandatory vz migration URI: %s"), + in); + goto cleanup; + } + + if (uri->user != NULL || uri->path != NULL || + uri->query != NULL || uri->fragment != NULL) { + virReportError(VIR_ERR_INVALID_ARG, + _("only scheme, host and port are supported in " + "vz migration URI: %s"), in); + goto cleanup; + } + + if (STRNEQ(uri->scheme, "tcp")) { + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, + _("unsupported scheme %s in migration URI %s"), + uri->scheme, in); + goto cleanup; + } + + if (VIR_STRDUP(out, in) < 0) + goto cleanup; + + cleanup: + virURIFree(uri); + return out; +} + static int vzDomainMigratePrepare3Params(virConnectPtr dconn, virTypedParameterPtr params ATTRIBUTE_UNUSED, @@ -1522,6 +1562,11 @@ vzDomainMigratePrepare3Params(virConnectPtr dconn, { vzConnPtr privconn = dconn->privateData; int ret = -1; + const char *uri = NULL; + + if (virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_URI, &uri) < 0) + goto cleanup; *cookieout = NULL; *uri_out = NULL; @@ -1530,7 +1575,12 @@ vzDomainMigratePrepare3Params(virConnectPtr dconn, goto cleanup; *cookieoutlen = strlen(*cookieout) + 1; - if (!(*uri_out = vzCreateMigrateUri())) + if (uri == NULL) + *uri_out = vzCreateMigrateUri(); + else + *uri_out = vzAdaptInUri(uri); + + if (*uri_out == NULL) goto cleanup; ret = 0; -- 1.7.1

From: Nikolay Shirokovskiy <nshirokovskiy@virtuozzo.com> Migration API has a lot of options. This patch intention is to provide support for those options that can be trivially supported and give estimation for other options support in this commit message. I. Supported. 1. VIR_MIGRATE_COMPRESSED. Means 'use compression when migration domain memory'. It is supported but quite uncommon way: vz migration demands that this option should be set. This is due to vz is hardcoded to moving VMs memory using compression. So anyone who wants to migrate vz domain should set this option thus declaring it knows it uses compression. Why bother? May be just support this option and ignore if it is not set or don't support at all as we can't change behaviour in this aspect. Well I believe that this option is, first, inherent to hypervisor implementation as we have a task of moving domain memory to different place and, second, we have a tradeoff here between cpu and network resources and some managment should choose the stratery via this option. If we choose ignoring or unsupporting implementation than this option has a too vague meaning. Let's go into more detail. First if we ignore situation where option is not set than we put user into fallacy that vz hypervisor don't use compression and thus have lower cpu usage. Second approach is to not support the option. The main reason not to follow this way is that 'not supported and not set' is indistinguishable from 'supported and not set' and thus again fool the user. 2. VIR_MIGRATE_LIVE. Means 'reduce domain downtime by suspending it as lately as possible' which technically means 'migrate as much domain memory as possible before suspending'. Supported in same manner as VIR_MIGRATE_COMPRESSED as both vz VMs and CTs are always migrated via live scheme. One may be fooled by vz sdk flags of migration api: PVMT_HOT_MIGRATION(aka live) and PVMT_WARM_MIGRATION(aka normal). Current implementation ignore these flags and always use live migration. 3. VIR_MIGRATE_PERSIST_DEST, VIR_MIGRATE_UNDEFINE_SOURCE. This two comes together. Vz domain are alwasy persistent so we have to support demand option VIR_MIGRATE_PERSIST_DEST is set and VIR_MIGRATE_UNDEFINE_SOURCE is not (and this is done just by unsupporting it). 4. VIR_MIGRATE_PAUSED. Means 'don't resume domain on destination'. This is trivially supported as we have a corresponding option in vz migration. All that said the minimal command to migrate vz domain looks next: migrate $DOMAIN $DESTINATION --live --persistent --compressed. Not good. Say if you want to just migrate a domain without further details you will get error messages until you add these options to command line. I think there is a lack of notion 'default' behaviour in all these aspects. If we have it we could just issue: migrate $DOMAIN $DESTINATION For vz this would give default compression for example, for qemu - default no-compression. Then we could have flags --compressed and -no-compressed and for vz the latter would give unsupported error. II. Unsupported. 1. VIR_MIGRATE_UNSAFE. Vz disks are always have 'cache=none' set (this is not reflected in current version of vz driver and will be fixed soon). So we need not to support this option. 2. VIR_MIGRATE_CHANGE_PROTECTION. Unsupported as we have no appopriate support from vz sdk. Although we have locks they are advisory and cant help us. 3. VIR_MIGRATE_TUNNELLED. Means 'use libvirtd to libvirtd connection to pass hypervisor migration traffic'. Unsupported as not among vz hypervisor usecases. Moreover this feature only has meaning for peer2peer migration that is not implemented in this patch set. 4. Direct migration. Which is exposed via *toURI* interface with VIR_MIGRATE_PEER2PEER flag unset. Means 'migrate without using libvirtd on the other side'. To support it we should add authN means to vz driver as mentioned in 'backbone patch' which looks ugly. 5. VIR_MIGRATE_ABORT_ON_ERROR, VIR_MIGRATE_AUTO_CONVERGE, VIR_MIGRATE_RDMA_PIN_ALL, VIR_MIGRATE_NON_SHARED_INC, VIR_MIGRATE_PARAM_DEST_XML, VIR_MIGRATE_PARAM_BANDWIDTH, VIR_MIGRATE_PARAM_GRAPHICS_URI, VIR_MIGRATE_PARAM_LISTEN_ADDRESS, VIR_MIGRATE_PARAM_MIGRATE_DISKS. Without further discussion. They are just not usecases of vz hypevisor. III. Unimplemented. 1. VIR_MIGRATE_OFFLINE. Means 'migrate only XML definition of a domain'. Actually the same vz sdk call supports offline migration but nevertheless we don't get it for free for vz domain because in case of offline migration only steps 'begin' and 'prepare' are performed while we can't issue vz migration command ealier than 'perform' step as we need authN cookie. So we need extra work to be done which goes to different patchset. 2. VIR_MIGRATE_PEER2PEER. Means 'whole migration managment should be done by the daemon of the source side'. QEMU does this but at the cost of heavily(at my estimate) duplicate client side migration managment code. We can do these too for vz or even better - refactor and than support. So it goes to different patchset. IV. Undecided and thus unsupported. 6. VIR_MIGRATE_NON_SHARED_DISK. The meaning of this option is not clear to me. Look, if qemu domain has a non-shared disk than it will refuse to migrate. But after you specify this option it will refuse too. You need to create image file for the disk on the destination side. Only after that you can migrate. Unexpectedly existence of this file is enough to migrate without option too. In this case you will get a domain on the destination with disk image unrelated to source one and this is in case of live migration! Looks like a bug. Ok, imagine this is fixed so that migration of non-shared disk is only possible with actual coping disk to destination. What we get from this option? We get that you have to specify this option if you want to migrate a domain with non-shared disk like some forcing option. May be it is a good approach but it is incompatible with vz. Vz don't demand any user awareness of migration of non-shared disks. And this case incompatibility can not be easily resolved as for 'compressed' option as this option depends on classifying of shared/non-shared for disks which is done inside vz. vz: implement misc migration options Signed-off-by: Nikolay Shirokovskiy <nshirokovskiy@virtuozzo.com> --- src/vz/vz_driver.c | 42 +++++++++++++++++++++++++++++++++++++++--- src/vz/vz_sdk.c | 8 ++++++-- src/vz/vz_sdk.h | 3 ++- 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/vz/vz_driver.c b/src/vz/vz_driver.c index 9fefac1..5a0b3f8 100644 --- a/src/vz/vz_driver.c +++ b/src/vz/vz_driver.c @@ -1359,17 +1359,53 @@ vzConnectSupportsFeature(virConnectPtr conn ATTRIBUTE_UNUSED, int feature) VIR_MIGRATE_PARAM_URI, VIR_TYPED_PARAM_STRING, \ NULL +#define VZ_MIGRATION_FLAGS \ + (VIR_MIGRATE_LIVE | \ + VIR_MIGRATE_COMPRESSED | \ + VIR_MIGRATE_PERSIST_DEST | \ + VIR_MIGRATE_PAUSED) + +int vzCheckFlags(int flags) +{ + if (!(flags & VIR_MIGRATE_LIVE)) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("flags VIR_MIGRATE_LIVE must be set" + " for vz migration")); + return -1; + } + + if (!(flags & VIR_MIGRATE_COMPRESSED)) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("flags VIR_MIGRATE_COMPRESSED must be set" + " for vz migration")); + return -1; + } + + if (!(flags & VIR_MIGRATE_PERSIST_DEST)) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("flags VIR_MIGRATE_PERSIST_DEST must be set" + " for vz migration")); + return -1; + } + + virCheckFlags(VZ_MIGRATION_FLAGS, -1); + return 0; +} + static char * vzDomainMigrateBegin3Params(virDomainPtr domain, virTypedParameterPtr params, int nparams, char **cookieout ATTRIBUTE_UNUSED, int *cookieoutlen ATTRIBUTE_UNUSED, - unsigned int fflags ATTRIBUTE_UNUSED) + unsigned int flags) { virDomainObjPtr dom = NULL; char *xml = NULL; + if (vzCheckFlags(flags) < 0) + goto cleanup; + if (virTypedParamsValidate(params, nparams, VZ_MIGRATION_PARAMETERS) < 0) goto cleanup; @@ -1604,7 +1640,7 @@ vzDomainMigratePerform3Params(virDomainPtr domain, int cookieinlen ATTRIBUTE_UNUSED, char **cookieout, int *cookieoutlen, - unsigned int fflags ATTRIBUTE_UNUSED) + unsigned int fflags) { int ret = -1; virDomainObjPtr dom = NULL; @@ -1634,7 +1670,7 @@ vzDomainMigratePerform3Params(virDomainPtr domain, if (vzParseCookie1(cookiein, session_uuid) < 0) goto cleanup; - if (prlsdkMigrate(dom, uri, session_uuid, dname) < 0) + if (prlsdkMigrate(dom, uri, session_uuid, dname, fflags) < 0) goto cleanup; if (!(*cookieout = vzFormatCookie2(dom->def->uuid))) diff --git a/src/vz/vz_sdk.c b/src/vz/vz_sdk.c index f1fa6da..7646796 100644 --- a/src/vz/vz_sdk.c +++ b/src/vz/vz_sdk.c @@ -4067,24 +4067,28 @@ prlsdkGetMemoryStats(virDomainObjPtr dom, #define PRLSDK_MIGRATION_FLAGS (PSL_HIGH_SECURITY) int prlsdkMigrate(virDomainObjPtr dom, const char* uri_str, - const unsigned char *session_uuid, const char *dname) + const unsigned char *session_uuid, const char *dname, unsigned int flags) { int ret = -1; vzDomObjPtr privdom = dom->privateData; virURIPtr uri = NULL; PRL_HANDLE job = PRL_INVALID_HANDLE; char uuidstr[VIR_UUID_STRING_BUFLEN + 2]; + PRL_UINT32 vzflags = PRLSDK_MIGRATION_FLAGS; uri = virURIParse(uri_str); /* no special error logs as uri should be checked on prepare step */ if (uri == NULL) goto cleanup; + 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, "", /* use default dir for migrated instance bundle */ - PRLSDK_MIGRATION_FLAGS, + vzflags, 0, /* reserved flags */ PRL_TRUE /* don't ask for confirmations */ ); diff --git a/src/vz/vz_sdk.h b/src/vz/vz_sdk.h index 971f913..02bd7f5 100644 --- a/src/vz/vz_sdk.h +++ b/src/vz/vz_sdk.h @@ -80,4 +80,5 @@ int prlsdkMigrate(virDomainObjPtr dom, const char* uri_str, const unsigned char *session_uuid, - const char* dname); + const char* dname, + unsigned int flags); -- 1.7.1

From: Nikolay Shirokovskiy <nshirokovskiy@virtuozzo.com> vz puts uuids into curly braces. Simply introduce new contstant to reflect this and get rid of magic +2 in code. Signed-off-by: Nikolay Shirokovskiy <nshirokovskiy@virtuozzo.com> --- src/vz/vz_sdk.c | 12 ++++++------ src/vz/vz_utils.h | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/vz/vz_sdk.c b/src/vz/vz_sdk.c index 7646796..187fcec 100644 --- a/src/vz/vz_sdk.c +++ b/src/vz/vz_sdk.c @@ -239,7 +239,7 @@ prlsdkConnect(vzConnPtr privconn) PRL_HANDLE job = PRL_INVALID_HANDLE; PRL_HANDLE result = PRL_INVALID_HANDLE; PRL_HANDLE response = PRL_INVALID_HANDLE; - char session_uuid[VIR_UUID_STRING_BUFLEN + 2]; + char session_uuid[VZ_UUID_STRING_BUFLEN]; PRL_UINT32 buflen = ARRAY_CARDINALITY(session_uuid); pret = PrlSrv_Create(&privconn->server); @@ -319,7 +319,7 @@ prlsdkUUIDFormat(const unsigned char *uuid, char *uuidstr) static PRL_HANDLE prlsdkSdkDomainLookupByUUID(vzConnPtr privconn, const unsigned char *uuid) { - char uuidstr[VIR_UUID_STRING_BUFLEN + 2]; + char uuidstr[VZ_UUID_STRING_BUFLEN]; PRL_HANDLE sdkdom = PRL_INVALID_HANDLE; prlsdkUUIDFormat(uuid, uuidstr); @@ -368,7 +368,7 @@ prlsdkGetDomainIds(PRL_HANDLE sdkdom, char **name, unsigned char *uuid) { - char uuidstr[VIR_UUID_STRING_BUFLEN + 2]; + char uuidstr[VZ_UUID_STRING_BUFLEN]; PRL_UINT32 len; PRL_RESULT pret; @@ -1725,7 +1725,7 @@ prlsdkEventsHandler(PRL_HANDLE prlEvent, PRL_VOID_PTR opaque) vzConnPtr privconn = opaque; PRL_RESULT pret = PRL_ERR_FAILURE; PRL_HANDLE_TYPE handleType; - char uuidstr[VIR_UUID_STRING_BUFLEN + 2]; + char uuidstr[VZ_UUID_STRING_BUFLEN]; unsigned char uuid[VIR_UUID_BUFLEN]; PRL_UINT32 bufsize = ARRAY_CARDINALITY(uuidstr); PRL_EVENT_TYPE prlEventType; @@ -3483,7 +3483,7 @@ prlsdkDoApplyConfig(virConnectPtr conn, { PRL_RESULT pret; size_t i; - char uuidstr[VIR_UUID_STRING_BUFLEN + 2]; + char uuidstr[VZ_UUID_STRING_BUFLEN]; bool needBoot = true; char *mask = NULL; @@ -4073,7 +4073,7 @@ int prlsdkMigrate(virDomainObjPtr dom, const char* uri_str, vzDomObjPtr privdom = dom->privateData; virURIPtr uri = NULL; PRL_HANDLE job = PRL_INVALID_HANDLE; - char uuidstr[VIR_UUID_STRING_BUFLEN + 2]; + char uuidstr[VZ_UUID_STRING_BUFLEN]; PRL_UINT32 vzflags = PRLSDK_MIGRATION_FLAGS; uri = virURIParse(uri_str); diff --git a/src/vz/vz_utils.h b/src/vz/vz_utils.h index a779b03..98a8f77 100644 --- a/src/vz/vz_utils.h +++ b/src/vz/vz_utils.h @@ -55,6 +55,8 @@ # define PARALLELS_REQUIRED_BRIDGED_NETWORK "Bridged" # define PARALLELS_BRIDGED_NETWORK_TYPE "bridged" +# define VZ_UUID_STRING_BUFLEN (VIR_UUID_STRING_BUFLEN + 2) + struct _vzConn { virMutex lock; -- 1.7.1

Please wait the next version of the patchset. I was not very comfortable with this patch as it is a kind of hack. Eventually vz migration is direct migration in terms of libvirt and forcing it to follow 5-staged way of managed migration for getting an authZ token from destination is not good. Better introduce vz specific driver function to get the token and use direct scheme. On 13.07.2015 15:28, nshirokovskiy@virtuozzo.com wrote:
From: Nikolay Shirokovskiy <nshirokovskiy@virtuozzo.com>
This patch makes basic vz migration possible. For example by virsh: virsh -c vz:///system migrate $NAME vz+ssh://$DST/system
Vz migration is implemented thru interface for managed migrations for drivers although it looks like a candadate for direct migration as all work is done by vz sdk. The reason is that vz sdk lacks rich remote authentication capabilities of libvirt and if we choose to implement direct migration we have to reimplement auth means of libvirt. This brings the requirement that destination side should have running libvirt daemon. This is not the problem as vz is moving in the direction of tight integration with libvirt.
Another issue of this choice is that if the managment migration fails on 'finish' step driver is supposed to resume on source. This is not compatible with vz sdk migration but this can be overcome without loosing a constistency, see comments in code.
Technically we have a libvirt connection to destination in managed migration scheme and we use this connection to obtain a session_uuid (which acts as authZ token) for vz migration. This uuid is passed from destination through cookie on 'prepare' step.
A few words on vz migration uri. I'd probably use just 'hostname:port' uris as we don't have different migration schemes in vz but scheme part is mandatory, so 'tcp' is used. Looks like good name.
Signed-off-by: Nikolay Shirokovskiy <nshirokovskiy@virtuozzo.com> --- src/vz/vz_driver.c | 250 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/vz/vz_sdk.c | 79 ++++++++++++++-- src/vz/vz_sdk.h | 2 + src/vz/vz_utils.h | 1 + 4 files changed, 322 insertions(+), 10 deletions(-)
diff --git a/src/vz/vz_driver.c b/src/vz/vz_driver.c index 9f0c52f..e003646 100644 --- a/src/vz/vz_driver.c +++ b/src/vz/vz_driver.c @@ -1343,6 +1343,250 @@ vzDomainMemoryStats(virDomainPtr domain, return ret; }
+static int +vzConnectSupportsFeature(virConnectPtr conn ATTRIBUTE_UNUSED, int feature) +{ + switch (feature) { + case VIR_DRV_FEATURE_MIGRATION_PARAMS: + return 1; + default: + return 0; + } +} + +#define VZ_MIGRATION_PARAMETERS NULL + +static char * +vzDomainMigrateBegin3Params(virDomainPtr domain, + virTypedParameterPtr params, + int nparams, + char **cookieout ATTRIBUTE_UNUSED, + int *cookieoutlen ATTRIBUTE_UNUSED, + unsigned int fflags ATTRIBUTE_UNUSED) +{ + virDomainObjPtr dom = NULL; + char *xml = NULL; + + if (virTypedParamsValidate(params, nparams, VZ_MIGRATION_PARAMETERS) < 0) + goto cleanup; + + if (!(dom = vzDomObjFromDomain(domain))) + goto cleanup; + + xml = virDomainDefFormat(dom->def, VIR_DOMAIN_DEF_FORMAT_SECURE); + + cleanup: + if (dom) + virObjectUnlock(dom); + + return xml; +} + +/* return 'hostname' */ +static char * +vzCreateMigrateUri(void) +{ + char *hostname = NULL; + char *out = NULL; + virURI uri = {}; + + if ((hostname = virGetHostname()) == NULL) + 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; + } + + /* to set const string to non-const */ + if (VIR_STRDUP(uri.scheme, "tcp") < 0) + goto cleanup; + uri.server = hostname; + out = virURIFormat(&uri); + + cleanup: + VIR_FREE(hostname); + VIR_FREE(uri.scheme); + return out; +} + +static int +vzDomainMigratePrepare3Params(virConnectPtr dconn, + virTypedParameterPtr params ATTRIBUTE_UNUSED, + int nparams ATTRIBUTE_UNUSED, + const char *cookiein ATTRIBUTE_UNUSED, + int cookieinlen ATTRIBUTE_UNUSED, + char **cookieout, + int *cookieoutlen, + char **uri_out, + unsigned int fflags ATTRIBUTE_UNUSED) +{ + vzConnPtr privconn = dconn->privateData; + int ret = -1; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + *cookieout = NULL; + *uri_out = NULL; + + virUUIDFormat(privconn->session_uuid, uuidstr); + if (VIR_STRDUP(*cookieout, uuidstr) < 0) + goto cleanup; + *cookieoutlen = strlen(*cookieout) + 1; + + if (!(*uri_out = vzCreateMigrateUri())) + goto cleanup; + + ret = 0; + + cleanup: + if (ret != 0) { + VIR_FREE(*cookieout); + VIR_FREE(*uri_out); + *cookieoutlen = 0; + } + + return ret; +} + +static int +vzDomainMigratePerform3Params(virDomainPtr domain, + const char *dconnuri ATTRIBUTE_UNUSED, + virTypedParameterPtr params, + int nparams, + const char *cookiein, + int cookieinlen ATTRIBUTE_UNUSED, + char **cookieout, + int *cookieoutlen, + unsigned int fflags ATTRIBUTE_UNUSED) +{ + int ret = -1; + virDomainObjPtr dom = NULL; + const char *uri = NULL; + unsigned char session_uuid[VIR_UUID_BUFLEN]; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + *cookieout = NULL; + + if (virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_URI, + &uri) < 0) + goto cleanup; + + if (!(dom = vzDomObjFromDomain(domain))) + goto cleanup; + + if (!uri) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("migration URI should be provided")); + goto cleanup; + } + + if (virUUIDParse(cookiein, session_uuid) < 0) + goto cleanup; + + if (prlsdkMigrate(dom, uri, session_uuid) < 0) + goto cleanup; + + virUUIDFormat(domain->uuid, uuidstr); + if (VIR_STRDUP(*cookieout, uuidstr) < 0) + goto cleanup; + *cookieoutlen = strlen(*cookieout) + 1; + + ret = 0; + + cleanup: + if (dom) + virObjectUnlock(dom); + if (ret != 0) { + VIR_FREE(*cookieout); + *cookieoutlen = 0; + } + + return ret; +} + +/* if we return NULL from this function we are supposed + to cleanup destination side, but we can't do it + because 'perform' step is finished and migration is actually + completed by dispatcher. Unfortunately in OOM situation we + have to return NULL. As a result high level migration + function return NULL which is supposed to be + treated as migration error while migration is + actually successful. This should not be a problem + as we are in consistent state. For example later + attempts to list source and destination domains + will reveal actual situation. */ + +static virDomainPtr +vzDomainMigrateFinish3Params(virConnectPtr dconn, + virTypedParameterPtr params ATTRIBUTE_UNUSED, + int nparams ATTRIBUTE_UNUSED, + const char *cookiein, + int cookieinlen ATTRIBUTE_UNUSED, + char **cookieout ATTRIBUTE_UNUSED, + int *cookieoutlen ATTRIBUTE_UNUSED, + unsigned int fflags ATTRIBUTE_UNUSED, + int cancelled) +{ + vzConnPtr privconn = dconn->privateData; + virDomainObjPtr dom = NULL; + virDomainPtr domain = NULL; + unsigned char domain_uuid[VIR_UUID_BUFLEN]; + + /* we have nothing to cleanup, whole job is done by PCS dispatcher */ + if (cancelled) + return NULL; + + if (virUUIDParse(cookiein, domain_uuid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not parse UUID from string '%s'"), + cookiein); + goto cleanup; + } + + if (!(dom = prlsdkAddDomain(privconn, domain_uuid))) + goto cleanup; + + domain = virGetDomain(dconn, dom->def->name, dom->def->uuid); + if (domain) + domain->id = dom->def->id; + + cleanup: + if (!domain) + VIR_WARN("Can't provide domain with uuid '%s' after successfull migration.", cookiein); + virDomainObjEndAPI(&dom); + return domain; +} + +/* This is executed only if 'perform' step is successfull that + is migration is completed by PCS dispatcher. Thus we should + ignore 'canceled' parameter and always kill source domain. */ +static int +vzDomainMigrateConfirm3Params(virDomainPtr domain, + virTypedParameterPtr params ATTRIBUTE_UNUSED, + int nparams ATTRIBUTE_UNUSED, + const char *cookiein ATTRIBUTE_UNUSED, + int cookieinlen ATTRIBUTE_UNUSED, + unsigned int fflags ATTRIBUTE_UNUSED, + int cancelled ATTRIBUTE_UNUSED) +{ + vzConnPtr privconn = domain->conn->privateData; + virDomainObjPtr dom = NULL; + + if (!(dom = vzDomObjFromDomain(domain))) + goto cleanup; + + virDomainObjListRemove(privconn->domains, dom); + + cleanup: + if (dom) + virObjectUnlock(dom); + + return 0; +} + static virHypervisorDriver vzDriver = { .name = "vz", .connectOpen = vzConnectOpen, /* 0.10.0 */ @@ -1396,6 +1640,12 @@ static virHypervisorDriver vzDriver = { .domainBlockStatsFlags = vzDomainBlockStatsFlags, /* 1.2.17 */ .domainInterfaceStats = vzDomainInterfaceStats, /* 1.2.17 */ .domainMemoryStats = vzDomainMemoryStats, /* 1.2.17 */ + .connectSupportsFeature = vzConnectSupportsFeature, /* 1.2.18 */ + .domainMigrateBegin3Params = vzDomainMigrateBegin3Params, /* 1.2.18 */ + .domainMigratePrepare3Params = vzDomainMigratePrepare3Params, /* 1.2.18 */ + .domainMigratePerform3Params = vzDomainMigratePerform3Params, /* 1.2.18 */ + .domainMigrateFinish3Params = vzDomainMigrateFinish3Params, /* 1.2.18 */ + .domainMigrateConfirm3Params = vzDomainMigrateConfirm3Params, /* 1.2.18 */ };
static virConnectDriver vzConnectDriver = { diff --git a/src/vz/vz_sdk.c b/src/vz/vz_sdk.c index d1bc312..908bfc1 100644 --- a/src/vz/vz_sdk.c +++ b/src/vz/vz_sdk.c @@ -37,6 +37,9 @@ #define VIR_FROM_THIS VIR_FROM_PARALLELS #define JOB_INFINIT_WAIT_TIMEOUT UINT_MAX
+static int +prlsdkUUIDParse(const char *uuidstr, unsigned char *uuid); + VIR_LOG_INIT("parallels.sdk");
/* @@ -228,24 +231,40 @@ prlsdkDeinit(void) int prlsdkConnect(vzConnPtr privconn) { - PRL_RESULT ret; + int ret = -1; + PRL_RESULT pret; PRL_HANDLE job = PRL_INVALID_HANDLE; + PRL_HANDLE result = PRL_INVALID_HANDLE; + PRL_HANDLE response = PRL_INVALID_HANDLE; + char session_uuid[VIR_UUID_STRING_BUFLEN + 2]; + PRL_UINT32 buflen = ARRAY_CARDINALITY(session_uuid);
- ret = PrlSrv_Create(&privconn->server); - if (PRL_FAILED(ret)) { - logPrlError(ret); - return -1; - } + pret = PrlSrv_Create(&privconn->server); + prlsdkCheckRetGoto(pret, cleanup);
job = PrlSrv_LoginLocalEx(privconn->server, NULL, 0, PSL_HIGH_SECURITY, PACF_NON_INTERACTIVE_MODE); + if (PRL_FAILED(getJobResult(job, &result))) + goto cleanup; + + pret = PrlResult_GetParam(result, &response); + prlsdkCheckRetGoto(pret, cleanup); + + pret = PrlLoginResponse_GetSessionUuid(response, session_uuid, &buflen); + prlsdkCheckRetGoto(pret, cleanup); + + if (prlsdkUUIDParse(session_uuid, privconn->session_uuid) < 0) + goto cleanup; + + ret = 0;
- if (waitJob(job)) { + cleanup: + if (ret < 0) PrlHandle_Free(privconn->server); - return -1; - } + PrlHandle_Free(result); + PrlHandle_Free(response);
- return 0; + return ret; }
void @@ -4035,3 +4054,43 @@ prlsdkGetMemoryStats(virDomainObjPtr dom,
return ret; } + +/* 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, const char* uri_str, + const unsigned char *session_uuid) +{ + int ret = -1; + vzDomObjPtr privdom = dom->privateData; + virURIPtr uri = NULL; + PRL_HANDLE job = PRL_INVALID_HANDLE; + char uuidstr[VIR_UUID_STRING_BUFLEN + 2]; + + uri = virURIParse(uri_str); + /* no special error logs as uri should be checked on prepare step */ + if (uri == NULL) + goto cleanup; + + prlsdkUUIDFormat(session_uuid, uuidstr); + job = PrlVm_MigrateEx(privdom->sdkdom, uri->server, uri->port, uuidstr, + "", /* use default dir for migrated instance bundle */ + PRLSDK_MIGRATION_FLAGS, + 0, /* reserved flags */ + PRL_TRUE /* don't ask for confirmations */ + ); + + if (PRL_FAILED(waitJob(job))) + goto cleanup; + + ret = 0; + + cleanup: + virURIFree(uri); + return ret; +} diff --git a/src/vz/vz_sdk.h b/src/vz/vz_sdk.h index ebe4591..1a90eca 100644 --- a/src/vz/vz_sdk.h +++ b/src/vz/vz_sdk.h @@ -76,3 +76,5 @@ int prlsdkGetVcpuStats(virDomainObjPtr dom, int idx, unsigned long long *time); int prlsdkGetMemoryStats(virDomainObjPtr dom, virDomainMemoryStatPtr stats, unsigned int nr_stats); +int +prlsdkMigrate(virDomainObjPtr dom, const char* uri_str, const char unsigned *session_uuid); diff --git a/src/vz/vz_utils.h b/src/vz/vz_utils.h index db09647..a779b03 100644 --- a/src/vz/vz_utils.h +++ b/src/vz/vz_utils.h @@ -62,6 +62,7 @@ struct _vzConn { virDomainObjListPtr domains;
PRL_HANDLE server; + unsigned char session_uuid[VIR_UUID_BUFLEN]; virStoragePoolObjList pools; virNetworkObjListPtr networks; virCapsPtr caps;
participants (2)
-
Nikolay Shirokovskiy
-
nshirokovskiy@virtuozzo.com