From: nshirokovskiy(a)virtuozzo.com <nshirokovskiy(a)virtuozzo.com>
This patch makes basic vz migration possible. For example by virsh:
virsh -c vz:///system migrate $NAME vz+ssh://$DST/system --p2p
Vz migration is implemented as p2p migration. The reason
is that vz sdk do all the job. The question may arise then
why don't implement it as a direct migration. The reason
is that we want to leverage rich libvirt authentication abilities
we lack in vz sdk. We can do it because vz sdk can use tokens to
factor out authentication from migration command.
Signed-off-by: Nikolay Shirokovskiy <nshirokovskiy(a)virtuozzo.com>
---
src/vz/vz_driver.c | 257 ++++++++++++++++++++++++++++++++++++++++++++++++++++
src/vz/vz_sdk.c | 33 +++++++
src/vz/vz_sdk.h | 2 +
3 files changed, 292 insertions(+), 0 deletions(-)
diff --git a/src/vz/vz_driver.c b/src/vz/vz_driver.c
index 8fa7957..36c64e9 100644
--- a/src/vz/vz_driver.c
+++ b/src/vz/vz_driver.c
@@ -146,6 +146,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;
@@ -1343,6 +1346,257 @@ vzDomainMemoryStats(virDomainPtr domain,
return ret;
}
+static char*
+vzFormatCookie(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 int
+vzParseCookie(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;
+}
+
+#define VZ_MIGRATION_FLAGS (VIR_MIGRATE_PEER2PEER)
+
+#define VZ_MIGRATION_PARAMETERS \
+ VIR_MIGRATE_PARAM_URI, VIR_TYPED_PARAM_STRING, \
+ NULL
+
+static char*
+vzCreateMigrationURI(void)
+{
+ char *hostname = NULL;
+ char *uri = NULL;
+
+ 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;
+ }
+
+ 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 ATTRIBUTE_UNUSED,
+ int cookieinlen ATTRIBUTE_UNUSED,
+ char **cookieout,
+ int *cookieoutlen,
+ char **uri_out,
+ unsigned int flags)
+{
+ vzConnPtr privconn = conn->privateData;
+ const char *miguri = 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)
+ goto cleanup;
+
+ /* This check is of par with direct managed imlementation */
+ if (miguri == NULL)
+ *uri_out = vzCreateMigrationURI();
+
+ if (!(*cookieout = vzFormatCookie(privconn->session_uuid)))
+ goto cleanup;
+ *cookieoutlen = strlen(*cookieout) + 1;
+ ret = 0;
+
+ cleanup:
+ if (ret != 0) {
+ VIR_FREE(*cookieout);
+ *cookieoutlen = 0;
+ VIR_FREE(*uri_out);
+ }
+
+ return ret;
+}
+
+static int
+vzConnectSupportsFeature(virConnectPtr conn ATTRIBUTE_UNUSED, int feature)
+{
+ switch (feature) {
+ case VIR_DRV_FEATURE_MIGRATION_PARAMS:
+ case VIR_DRV_FEATURE_MIGRATION_P2P:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+virURIPtr
+vzParseVzURI(const char *uri_str)
+{
+ virURIPtr uri = NULL;
+ int ret = -1;
+
+ if (!(uri = virURIParse(uri_str)))
+ goto cleanup;
+
+ if (uri->scheme == NULL || uri->server == NULL) {
+ virReportError(VIR_ERR_INVALID_ARG,
+ _("scheme and host are mandatory vz migration URI:
%s"),
+ uri_str);
+ 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"), uri_str);
+ goto cleanup;
+ }
+
+ if (STRNEQ(uri->scheme, "vzmigr")) {
+ virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED,
+ _("unsupported scheme %s in migration URI %s"),
+ uri->scheme, uri_str);
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ if (ret < 0) {
+ virURIFree(uri);
+ uri = NULL;
+ }
+
+ return uri;
+}
+
+static int
+vzDomainMigratePerform3Params(virDomainPtr domain,
+ const char *dconnuri,
+ 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 ret = -1;
+ virDomainObjPtr dom = NULL;
+ virConnectPtr dconn = NULL;
+ virURIPtr vzuri = NULL;
+ unsigned char session_uuid[VIR_UUID_BUFLEN];
+ vzConnPtr privconn = domain->conn->privateData;
+ char *cookie = NULL;
+ int cookielen = 0;
+ const char *miguri = NULL;
+
+ virCheckFlags(VZ_MIGRATION_FLAGS, -1);
+
+ if (virTypedParamsValidate(params, nparams, VZ_MIGRATION_PARAMETERS) < 0)
+ goto cleanup;
+
+ if (!(dom = vzDomObjFromDomain(domain)))
+ goto cleanup;
+
+ dconn = virConnectOpen(dconnuri);
+ if (dconn == NULL) {
+ virReportError(VIR_ERR_OPERATION_FAILED,
+ _("Failed to connect to remote libvirt URI %s: %s"),
+ dconnuri, virGetLastErrorMessage());
+ goto cleanup;
+ }
+
+ if (virDomainMigratePrepare3Params(dconn, params, nparams, NULL, 0,
+ &cookie, &cookielen,
+ (char**)&miguri, flags) < 0)
+ goto cleanup;
+
+ if (vzParseCookie(cookie, session_uuid) < 0)
+ goto cleanup;
+
+ if (miguri == NULL &&
+ virTypedParamsGetString(params, nparams,
+ VIR_MIGRATE_PARAM_URI, &miguri) < 0)
+ goto cleanup;
+
+ if (miguri == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("domainMigratePrepare3Params did not set miguri"));
+ }
+
+ if (!(vzuri = vzParseVzURI(miguri)))
+ goto cleanup;
+
+ if (prlsdkMigrate(dom, vzuri, session_uuid) < 0)
+ goto cleanup;
+
+ virDomainObjListRemove(privconn->domains, dom);
+ dom = NULL;
+
+ ret = 0;
+
+ cleanup:
+ if (dom)
+ virObjectUnlock(dom);
+ virObjectUnref(dconn);
+ virURIFree(vzuri);
+ VIR_FREE(cookie);
+
+ return ret;
+}
+
static virHypervisorDriver vzDriver = {
.name = "vz",
.connectOpen = vzConnectOpen, /* 0.10.0 */
@@ -1396,6 +1650,9 @@ static virHypervisorDriver vzDriver = {
.domainBlockStatsFlags = vzDomainBlockStatsFlags, /* 1.2.17 */
.domainInterfaceStats = vzDomainInterfaceStats, /* 1.2.17 */
.domainMemoryStats = vzDomainMemoryStats, /* 1.2.17 */
+ .connectSupportsFeature = vzConnectSupportsFeature, /* 1.2.20 */
+ .domainMigratePrepare3Params = vzDomainMigratePrepare3Params, /* 1.2.20 */
+ .domainMigratePerform3Params = vzDomainMigratePerform3Params, /* 1.2.20 */
};
static virConnectDriver vzConnectDriver = {
diff --git a/src/vz/vz_sdk.c b/src/vz/vz_sdk.c
index f7253de..783438d 100644
--- a/src/vz/vz_sdk.c
+++ b/src/vz/vz_sdk.c
@@ -4054,3 +4054,36 @@ 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, virURIPtr uri,
+ const unsigned char *session_uuid)
+{
+ int ret = -1;
+ vzDomObjPtr privdom = dom->privateData;
+ PRL_HANDLE job = PRL_INVALID_HANDLE;
+ char uuidstr[VIR_UUID_STRING_BUFLEN + 2];
+
+ 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:
+ return ret;
+}
diff --git a/src/vz/vz_sdk.h b/src/vz/vz_sdk.h
index ebe4591..d3f0caf 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, virURIPtr uri, const char unsigned *session_uuid);
--
1.7.1