[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH V4] libxl: add migration support
This patch adds initial migration support to the libxl driver, using the VIR_DRV_FEATURE_MIGRATION_PARAMS family of migration functions. Signed-off-by: Jim Fehlig <jfehlig@xxxxxxxx> --- V3 here https://www.redhat.com/archives/libvir-list/2014-April/msg00968.html In V4: - Code cleanup - Improved error handling Although this patch has been floating around for a long time, not sure if it is 1.2.4 material since it brings a new feature to the libxl driver. po/POTFILES.in | 1 + src/Makefile.am | 3 +- src/libxl/libxl_conf.h | 6 + src/libxl/libxl_domain.h | 1 + src/libxl/libxl_driver.c | 224 ++++++++++ src/libxl/libxl_migration.c | 978 ++++++++++++++++++++++++++++++++++++++++++++ src/libxl/libxl_migration.h | 78 ++++ 7 files changed, 1290 insertions(+), 1 deletion(-) diff --git a/po/POTFILES.in b/po/POTFILES.in index e35eb82..a72dc1e 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -73,6 +73,7 @@ src/lxc/lxc_process.c src/libxl/libxl_domain.c src/libxl/libxl_driver.c src/libxl/libxl_conf.c +src/libxl/libxl_migration.c src/network/bridge_driver.c src/network/bridge_driver_linux.c src/node_device/node_device_driver.c diff --git a/src/Makefile.am b/src/Makefile.am index e9dc9e0..0dbda7f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -706,7 +706,8 @@ XENAPI_DRIVER_SOURCES = \ LIBXL_DRIVER_SOURCES = \ libxl/libxl_conf.c libxl/libxl_conf.h \ libxl/libxl_domain.c libxl/libxl_domain.h \ - libxl/libxl_driver.c libxl/libxl_driver.h + libxl/libxl_driver.c libxl/libxl_driver.h \ + libxl/libxl_migration.c libxl/libxl_migration.h UML_DRIVER_SOURCES = \ uml/uml_conf.c uml/uml_conf.h \ diff --git a/src/libxl/libxl_conf.h b/src/libxl/libxl_conf.h index 24e1720..b798567 100644 --- a/src/libxl/libxl_conf.h +++ b/src/libxl/libxl_conf.h @@ -43,6 +43,9 @@ # define LIBXL_VNC_PORT_MIN 5900 # define LIBXL_VNC_PORT_MAX 65535 +# define LIBXL_MIGRATION_PORT_MIN 49152 +# define LIBXL_MIGRATION_PORT_MAX 49216 + # define LIBXL_CONFIG_DIR SYSCONFDIR "/libvirt/libxl" # define LIBXL_AUTOSTART_DIR LIBXL_CONFIG_DIR "/autostart" # define LIBXL_STATE_DIR LOCALSTATEDIR "/run/libvirt/libxl" @@ -115,6 +118,9 @@ struct _libxlDriverPrivate { /* Immutable pointer, self-locking APIs */ virPortAllocatorPtr reservedVNCPorts; + /* Immutable pointer, self-locking APIs */ + virPortAllocatorPtr migrationPorts; + /* Immutable pointer, lockless APIs*/ virSysinfoDefPtr hostsysinfo; }; diff --git a/src/libxl/libxl_domain.h b/src/libxl/libxl_domain.h index 979ce2a..9d48049 100644 --- a/src/libxl/libxl_domain.h +++ b/src/libxl/libxl_domain.h @@ -69,6 +69,7 @@ struct _libxlDomainObjPrivate { virChrdevsPtr devs; libxl_evgen_domain_death *deathW; libxlDriverPrivatePtr driver; + unsigned short migrationPort; struct libxlDomainJobObj job; }; diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c index e5ed0f2..2359f71 100644 --- a/src/libxl/libxl_driver.c +++ b/src/libxl/libxl_driver.c @@ -45,6 +45,7 @@ #include "libxl_domain.h" #include "libxl_driver.h" #include "libxl_conf.h" +#include "libxl_migration.h" #include "xen_xm.h" #include "xen_sxpr.h" #include "virtypedparam.h" @@ -209,6 +210,7 @@ libxlStateCleanup(void) virObjectUnref(libxl_driver->xmlopt); virObjectUnref(libxl_driver->domains); virObjectUnref(libxl_driver->reservedVNCPorts); + virObjectUnref(libxl_driver->migrationPorts); virObjectEventStateFree(libxl_driver->domainEventState); virSysinfoDefFree(libxl_driver->hostsysinfo); @@ -296,6 +298,13 @@ libxlStateInitialize(bool privileged, LIBXL_VNC_PORT_MAX))) goto error; + /* Allocate bitmap for migration port reservation */ + if (!(libxl_driver->migrationPorts = + virPortAllocatorNew(_("migration"), + LIBXL_MIGRATION_PORT_MIN, + LIBXL_MIGRATION_PORT_MAX))) + goto error; + if (!(libxl_driver->domains = virDomainObjListNew())) goto error; @@ -4126,6 +4135,7 @@ libxlConnectSupportsFeature(virConnectPtr conn, int feature) switch (feature) { case VIR_DRV_FEATURE_TYPED_PARAM_STRING: + case VIR_DRV_FEATURE_MIGRATION_PARAMS: return 1; default: return 0; @@ -4304,6 +4314,215 @@ libxlNodeDeviceReset(virNodeDevicePtr dev) return ret; } +static char * +libxlDomainMigrateBegin3Params(virDomainPtr domain, + virTypedParameterPtr params, + int nparams, + char **cookieout ATTRIBUTE_UNUSED, + int *cookieoutlen ATTRIBUTE_UNUSED, + unsigned int flags) +{ + const char *xmlin = NULL; + virDomainObjPtr vm = NULL; + + virCheckFlags(LIBXL_MIGRATION_FLAGS, NULL); + if (virTypedParamsValidate(params, nparams, LIBXL_MIGRATION_PARAMETERS) < 0) + return NULL; + + if (virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_DEST_XML, + &xmlin) < 0) + return NULL; + + if (!(vm = libxlDomObjFromDomain(domain))) + return NULL; + + if (virDomainMigrateBegin3ParamsEnsureACL(domain->conn, vm->def) < 0) { + virObjectUnlock(vm); + return NULL; + } + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + virObjectUnlock(vm); + return NULL; + } + + return libxlDomainMigrationBegin(domain->conn, vm, xmlin); +} + +static int +libxlDomainMigratePrepare3Params(virConnectPtr dconn, + virTypedParameterPtr params, + int nparams, + const char *cookiein ATTRIBUTE_UNUSED, + int cookieinlen ATTRIBUTE_UNUSED, + char **cookieout ATTRIBUTE_UNUSED, + int *cookieoutlen ATTRIBUTE_UNUSED, + char **uri_out, + unsigned int flags) +{ + libxlDriverPrivatePtr driver = dconn->privateData; + virDomainDefPtr def = NULL; + const char *dom_xml = NULL; + const char *dname = NULL; + const char *uri_in = NULL; + + virCheckFlags(LIBXL_MIGRATION_FLAGS, -1); + if (virTypedParamsValidate(params, nparams, LIBXL_MIGRATION_PARAMETERS) < 0) + goto error; + + if (virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_DEST_XML, + &dom_xml) < 0 || + virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_DEST_NAME, + &dname) < 0 || + virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_URI, + &uri_in) < 0) + + goto error; + + if (!(def = libxlDomainMigrationPrepareDef(driver, dom_xml, dname))) + goto error; + + if (virDomainMigratePrepare3ParamsEnsureACL(dconn, def) < 0) + goto error; + + if (libxlDomainMigrationPrepare(dconn, def, uri_in, uri_out) < 0) + goto error; + + return 0; + + error: + virDomainDefFree(def); + return -1; +} + +static int +libxlDomainMigratePerform3Params(virDomainPtr dom, + 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) +{ + libxlDriverPrivatePtr driver = dom->conn->privateData; + virDomainObjPtr vm = NULL; + const char *dom_xml = NULL; + const char *dname = NULL; + const char *uri = NULL; + int ret = -1; + + virCheckFlags(LIBXL_MIGRATION_FLAGS, -1); + if (virTypedParamsValidate(params, nparams, LIBXL_MIGRATION_PARAMETERS) < 0) + goto cleanup; + + if (virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_DEST_XML, + &dom_xml) < 0 || + virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_DEST_NAME, + &dname) < 0 || + virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_URI, + &uri) < 0) + + goto cleanup; + + if (!(vm = libxlDomObjFromDomain(dom))) + goto cleanup; + + if (virDomainMigratePerform3ParamsEnsureACL(dom->conn, vm->def) < 0) + goto cleanup; + + if (libxlDomainMigrationPerform(driver, vm, dom_xml, dconnuri, + uri, dname, flags) < 0) { + /* Job terminated and vm unlocked if MigrationPerform failed */ + vm = NULL; + goto cleanup; + } + + ret = 0; + + cleanup: + if (vm) + virObjectUnlock(vm); + return ret; +} + +static virDomainPtr +libxlDomainMigrateFinish3Params(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) +{ + libxlDriverPrivatePtr driver = dconn->privateData; + virDomainObjPtr vm = NULL; + const char *dname = NULL; + + virCheckFlags(LIBXL_MIGRATION_FLAGS, NULL); + if (virTypedParamsValidate(params, nparams, LIBXL_MIGRATION_PARAMETERS) < 0) + return NULL; + + if (virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_DEST_NAME, + &dname) < 0) + return NULL; + + if (!dname || + !(vm = virDomainObjListFindByName(driver->domains, dname))) { + virReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching name '%s'"), + NULLSTR(dname)); + return NULL; + } + + if (virDomainMigrateFinish3ParamsEnsureACL(dconn, vm->def) < 0) { + virObjectUnlock(vm); + return NULL; + } + + return libxlDomainMigrationFinish(dconn, vm, flags, cancelled); +} + +static int +libxlDomainMigrateConfirm3Params(virDomainPtr domain, + virTypedParameterPtr params, + int nparams, + const char *cookiein ATTRIBUTE_UNUSED, + int cookieinlen ATTRIBUTE_UNUSED, + unsigned int flags, + int cancelled) +{ + libxlDriverPrivatePtr driver = domain->conn->privateData; + virDomainObjPtr vm = NULL; + + virCheckFlags(LIBXL_MIGRATION_FLAGS, -1); + if (virTypedParamsValidate(params, nparams, LIBXL_MIGRATION_PARAMETERS) < 0) + return -1; + + if (!(vm = libxlDomObjFromDomain(domain))) + return -1; + + if (virDomainMigrateConfirm3ParamsEnsureACL(domain->conn, vm->def) < 0) { + virObjectUnlock(vm); + return -1; + } + + return libxlDomainMigrationConfirm(driver, vm, flags, cancelled); +} + static virDriver libxlDriver = { .no = VIR_DRV_LIBXL, @@ -4394,6 +4613,11 @@ static virDriver libxlDriver = { .nodeDeviceDetachFlags = libxlNodeDeviceDetachFlags, /* 1.2.3 */ .nodeDeviceReAttach = libxlNodeDeviceReAttach, /* 1.2.3 */ .nodeDeviceReset = libxlNodeDeviceReset, /* 1.2.3 */ + .domainMigrateBegin3Params = libxlDomainMigrateBegin3Params, /* 1.2.3 */ + .domainMigratePrepare3Params = libxlDomainMigratePrepare3Params, /* 1.2.3 */ + .domainMigratePerform3Params = libxlDomainMigratePerform3Params, /* 1.2.3 */ + .domainMigrateFinish3Params = libxlDomainMigrateFinish3Params, /* 1.2.3 */ + .domainMigrateConfirm3Params = libxlDomainMigrateConfirm3Params, /* 1.2.3 */ }; static virStateDriver libxlStateDriver = { diff --git a/src/libxl/libxl_migration.c b/src/libxl/libxl_migration.c new file mode 100644 index 0000000..7776934 --- /dev/null +++ b/src/libxl/libxl_migration.c @@ -0,0 +1,978 @@ +/* + * libxl_migration.c: methods for handling migration with libxenlight + * + * Copyright (C) 2014 SUSE LINUX Products GmbH, Nuernberg, Germany. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Authors: + * Jim Fehlig <jfehlig@xxxxxxxx> + * Chunyan Liu <cyliu@xxxxxxxx> + */ + +#include <config.h> + +#include "internal.h" +#include "virlog.h" +#include "virerror.h" +#include "virconf.h" +#include "datatypes.h" +#include "virfile.h" +#include "viralloc.h" +#include "viruuid.h" +#include "vircommand.h" +#include "virstring.h" +#include "rpc/virnetsocket.h" +#include "libxl_domain.h" +#include "libxl_driver.h" +#include "libxl_conf.h" +#include "libxl_migration.h" + +#define VIR_FROM_THIS VIR_FROM_LIBXL + +VIR_LOG_INIT("libxl.libxl_migration"); + +#define LIBXL_MIGRATION_PROTO_VERSION 1 +#define LIBXL_MIGRATION_MSG_MAX_SIZE 512 + +typedef struct _libxlMigrateReceiveArgs { + virConnectPtr conn; + virDomainObjPtr vm; + + /* for freeing listen sockets */ + virNetSocketPtr *socks; + size_t nsocks; +} libxlMigrateReceiveArgs; + +/* + * For future extensibility, a simple messaging protocol used to send migration + * data between libxl hosts. The message is encapsulated in json and currently + * includes the following entries: + * + * {"libvirt-libxl-mig-msg" : + * {"version" : $ver}, + * {"state" : $state}, + * {"status" : $status} + * } + * + * Possible $state values are "negotiate-version", "send-binary-data", + * "sending-binary-data", "received-binary-data", "done", and "error". + * + * Migration between source and destination libxl hosts is performed with the + * following message exchange: + * + * - src->dst: connect + * - dst->src: state="negotiate-version", version=LIBXL_MIGRATION_PROTO_VERSION + * - src->dst: state-"negotiate-version", + * version=min(dst ver, LIBXL_MIGRATION_PROTO_VERSION) + * - dst->src: state="send-binary-data", version=negotiated_version + * - src->dst: state="sending-binary-data", version=negotiated_version + * _ src->dst: binary migration data + * - dst->src: state="received-binary-data", version=negotiated_version + * - src->dst: state="done", version=negotiated_version + * + */ + +static virJSONValuePtr +libxlMigrationMsgNew(const char *state) +{ + virJSONValuePtr msg; + virJSONValuePtr body; + + if (!(msg = virJSONValueNewObject())) + return NULL; + + if (!(body = virJSONValueNewObject())) { + virJSONValueFree(msg); + return NULL; + } + + virJSONValueObjectAppendNumberInt(body, "version", + LIBXL_MIGRATION_PROTO_VERSION); + virJSONValueObjectAppendString(body, "state", state); + virJSONValueObjectAppendNumberInt(body, "status", 0); + + virJSONValueObjectAppend(msg, "libvirt-libxl-mig-msg", body); + + return msg; +} + +static bool +libxlMigrationMsgCheckState(virJSONValuePtr message, const char *state) +{ + virJSONValuePtr body; + const char *msg_state; + + if (!(body = virJSONValueObjectGet(message, "libvirt-libxl-mig-msg"))) + return false; + + msg_state = virJSONValueObjectGetString(body, "state"); + if (!msg_state || STRNEQ(msg_state, state)) + return false; + + return true; +} + +static void +libxlMigrationMsgSetStringField(virJSONValuePtr message, + const char *field, + const char *val) +{ + virJSONValuePtr body; + + if ((body = virJSONValueObjectGet(message, "libvirt-libxl-mig-msg"))) { + virJSONValueObjectRemoveKey(body, field, NULL); + virJSONValueObjectAppendString(body, field, val); + } +} + +static void +libxlMigrationMsgSetIntField(virJSONValuePtr message, + const char *field, + int val) +{ + virJSONValuePtr body; + + if ((body = virJSONValueObjectGet(message, "libvirt-libxl-mig-msg"))) { + virJSONValueObjectRemoveKey(body, field, NULL); + virJSONValueObjectAppendNumberInt(body, field, val); + } +} + +static int +libxlMigrationMsgGetIntField(virJSONValuePtr message, + const char *field, + int *val) +{ + virJSONValuePtr body; + + if (!(body = virJSONValueObjectGet(message, "libvirt-libxl-mig-msg"))) + return -1; + + if (virJSONValueObjectGetNumberInt(body, field, val) < 0) + return -1; + + return 0; +} + + +/* + * Send a migration message with 'status' field set to @status. + */ +static int +libxlMigrationMsgSend(int fd, virJSONValuePtr message, int status) +{ + int ret = -1; + size_t len; + char *msgstr; + + libxlMigrationMsgSetIntField(message, "status", status); + if (!(msgstr = virJSONValueToString(message, false))) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("Malformed migration message")); + return -1; + } + + VIR_DEBUG("Sending migration message %s", msgstr); + len = strlen(msgstr) + 1; + if (safewrite(fd, msgstr, len) != len) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("Failed to send migration message")); + goto cleanup; + } + + ret = 0; + + cleanup: + VIR_FREE(msgstr); + return ret; +} + +static virJSONValuePtr +libxlMigrationMsgReceive(int fd) +{ + char buf[LIBXL_MIGRATION_MSG_MAX_SIZE]; + int ret; + virJSONValuePtr msg = NULL; + size_t i; + + /* + * Message is small and NULL-terminated. Read a byte at a time + * until encountering the null-termination. + */ + for (i = 0; i < LIBXL_MIGRATION_MSG_MAX_SIZE;) { + ret = saferead(fd, &(buf[i]), 1); + if (ret == 1) + if (buf[i++] == '\0') + break; + + /* bail out if eof or failure != EAGAIN */ + if ((ret == 0) || (ret == -1 && errno != EAGAIN)) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("Failed to receive migration message")); + return NULL; + } + } + + if (i == LIBXL_MIGRATION_MSG_MAX_SIZE) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("Migration message exceeded max size")); + return NULL; + } + + VIR_DEBUG("Received migration message %s", buf); + if (!(msg = virJSONValueFromString(buf))) + goto error; + + /* Verify a libvirt-libxl-mig-msg was received */ + if (!(virJSONValueObjectGet(msg, "libvirt-libxl-mig-msg"))) + goto error; + + return msg; + + error: + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("Malformed migration message")); + virJSONValueFree(msg); + return NULL; +} + +static int +libxlMigrationSrcNegotiate(int fd) +{ + int ret = -1; + virJSONValuePtr msg; + int dstver; + + if (!(msg = libxlMigrationMsgReceive(fd))) + return -1; + + if (!libxlMigrationMsgCheckState(msg, "negotiate-version")) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("Failed to negotiate libxl " + "migration protocol version")); + goto cleanup; + } + + /* + * ACK by returning the message, with version set to + * min(dest ver, src ver) + */ + if (libxlMigrationMsgGetIntField(msg, "version", &dstver) < 0) + goto cleanup; + + if (dstver > LIBXL_MIGRATION_PROTO_VERSION) + libxlMigrationMsgSetIntField(msg, + "version", + LIBXL_MIGRATION_PROTO_VERSION); + + if (libxlMigrationMsgSend(fd, msg, 0) < 0) + goto cleanup; + + ret = 0; + + cleanup: + virJSONValueFree(msg); + return ret; +} + +static int +libxlMigrationDstNegotiate(int fd, virJSONValuePtr *message) +{ + virJSONValuePtr msg; + + if (!(msg = libxlMigrationMsgNew("negotiate-version"))) + return -1; + + if (libxlMigrationMsgSend(fd, msg, 0) < 0) { + virJSONValueFree(msg); + return -1; + } + + /* + * Receive ACK. src set version=min(dst ver, highest src ver), + * which is the negotiated version. Use this message with + * negotiated version throughout the transaction. + */ + virJSONValueFree(msg); + if (!(msg = libxlMigrationMsgReceive(fd))) + return -1; + + *message = msg; + return 0; +} + +static int +libxlMigrationSrcReady(int fd) +{ + int ret = -1; + virJSONValuePtr msg; + + if (!(msg = libxlMigrationMsgReceive(fd))) + return -1; + + if (!libxlMigrationMsgCheckState(msg, "send-binary-data")) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("Failed to receive 'send-binary-data' message " + "from destination libxl migration host")); + goto cleanup; + } + + /* ACK by returning the message with state sending */ + libxlMigrationMsgSetStringField(msg, "state", "sending-binary-data"); + if (libxlMigrationMsgSend(fd, msg, 0) < 0) + goto cleanup; + + ret = 0; + + cleanup: + virJSONValueFree(msg); + return ret; +} + +static int +libxlMigrationDstReady(int fd, virJSONValuePtr msg) +{ + int ret = -1; + virJSONValuePtr tmp = NULL; + + libxlMigrationMsgSetStringField(msg, "state", "send-binary-data"); + if (libxlMigrationMsgSend(fd, msg, 0) < 0) + return -1; + + /* Receive ACK, state sending */ + if (!(tmp = libxlMigrationMsgReceive(fd))) + return -1; + if (!libxlMigrationMsgCheckState(tmp, "sending-binary-data")) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("Failed to receive 'sending-binary-data' message " + "from source libxl migration host")); + goto cleanup; + } + + ret = 0; + + cleanup: + virJSONValueFree(tmp); + return ret; +} + +static int +libxlMigrationSrcDone(int fd) +{ + int ret = -1; + virJSONValuePtr msg; + + if (!(msg = libxlMigrationMsgReceive(fd))) + return -1; + + if (!libxlMigrationMsgCheckState(msg, "received-binary-data")) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("Failed to receive 'received-binary-data' message " + "from destination libxl migration host")); + goto cleanup; + } + + /* ACK by returning the message with state done */ + libxlMigrationMsgSetStringField(msg, "state", "done"); + if (libxlMigrationMsgSend(fd, msg, 0) < 0) + goto cleanup; + + ret = 0; + + cleanup: + virJSONValueFree(msg); + return ret; +} + +static int +libxlMigrationDestDone(int fd, virJSONValuePtr msg) +{ + int ret = -1; + virJSONValuePtr tmp; + + libxlMigrationMsgSetStringField(msg, "state", "received-binary-data"); + if (libxlMigrationMsgSend(fd, msg, 0) < 0) + return -1; + + /* Receive ACK, state done */ + if (!(tmp = libxlMigrationMsgReceive(fd))) + return -1; + if (!libxlMigrationMsgCheckState(tmp, "done")) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("Failed to receive 'done' message " + "from source libxl migration host")); + goto cleanup; + } + + ret = 0; + + cleanup: + virJSONValueFree(tmp); + return ret; +} + +static void +libxlMigrationSrcFailed(int fd, int status) +{ + virJSONValuePtr msg; + + if (!(msg = libxlMigrationMsgNew("error"))) + return; + libxlMigrationMsgSend(fd, msg, status); + virJSONValueFree(msg); +} + +static void +libxlMigrationDstFailed(int fd, virJSONValuePtr msg, int status) +{ + libxlMigrationMsgSetStringField(msg, "state", "error"); + libxlMigrationMsgSend(fd, msg, status); +} + +static void +libxlDoMigrateReceive(virNetSocketPtr sock, + int events ATTRIBUTE_UNUSED, + void *opaque) +{ + libxlMigrateReceiveArgs *data = opaque; + virConnectPtr conn = data->conn; + virDomainObjPtr vm = data->vm; + libxlDomainObjPrivatePtr priv = vm->privateData; + virNetSocketPtr *socks = data->socks; + size_t nsocks = data->nsocks; + libxlDriverPrivatePtr driver = conn->privateData; + virNetSocketPtr client_sock; + virJSONValuePtr mig_msg = NULL; + int recvfd; + size_t i; + int ret; + + virNetSocketAccept(sock, &client_sock); + if (client_sock == NULL) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Fail to accept migration connection")); + goto cleanup; + } + VIR_DEBUG("Accepted migration connection\n"); + recvfd = virNetSocketDupFD(client_sock, true); + virObjectUnref(client_sock); + + if (libxlMigrationDstNegotiate(recvfd, &mig_msg) < 0) + goto cleanup; + + if (libxlMigrationDstReady(recvfd, mig_msg) < 0) + goto cleanup; + + virObjectLock(vm); + ret = libxlDomainStart(driver, vm, false, recvfd); + virObjectUnlock(vm); + + if (ret < 0) { + libxlMigrationDstFailed(recvfd, mig_msg, ret); + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Failed to restore domain with libxenlight")); + if (!vm->persistent) + virDomainObjListRemove(driver->domains, vm); + goto cleanup; + } + + if (libxlMigrationDestDone(recvfd, mig_msg) < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("Failed to notify sender that migration completed")); + goto cleanup_dom; + } + + /* Remove all listen socks from event handler, and close them. */ + for (i = 0; i < nsocks; i++) { + virNetSocketUpdateIOCallback(socks[i], 0); + virNetSocketRemoveIOCallback(socks[i]); + virNetSocketClose(socks[i]); + virObjectUnref(socks[i]); + } + VIR_FREE(socks); + goto cleanup; + + cleanup_dom: + libxl_domain_destroy(priv->ctx, vm->def->id, NULL); + vm->def->id = -1; + if (!vm->persistent) + virDomainObjListRemove(driver->domains, vm); + + cleanup: + VIR_FORCE_CLOSE(recvfd); + virJSONValueFree(mig_msg); + VIR_FREE(opaque); + return; +} + +static int +libxlDoMigrateSend(libxlDriverPrivatePtr driver, + virDomainObjPtr vm, + unsigned long flags, + int sockfd) +{ + libxlDomainObjPrivatePtr priv; + libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver); + virObjectEventPtr event = NULL; + int xl_flags = 0; + int ret = -1; + + if (flags & VIR_MIGRATE_LIVE) + xl_flags = LIBXL_SUSPEND_LIVE; + + priv = vm->privateData; + + if (libxlMigrationSrcNegotiate(sockfd) < 0) + goto cleanup; + + if (libxlMigrationSrcReady(sockfd) < 0) + goto cleanup; + + ret = libxl_domain_suspend(priv->ctx, vm->def->id, sockfd, + xl_flags, NULL); + if (ret != 0) { + libxlMigrationSrcFailed(sockfd, ret); + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Failed to save domain '%d' with libxenlight"), + vm->def->id); + ret = -1; + goto cleanup; + } + + if (libxlMigrationSrcDone(sockfd) < 0) { + if (libxl_domain_resume(priv->ctx, vm->def->id, 0, 0) != 0) { + VIR_DEBUG("Failed to resume domain '%d' with libxenlight", + vm->def->id); + virDomainObjSetState(vm, VIR_DOMAIN_PAUSED, + VIR_DOMAIN_PAUSED_MIGRATION); + event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_SUSPENDED, + VIR_DOMAIN_EVENT_SUSPENDED_MIGRATED); + if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0) + goto cleanup; + } + goto cleanup; + } + + ret = 0; + + cleanup: + if (event) + libxlDomainEventQueue(driver, event); + virObjectUnref(cfg); + return ret; +} + +static bool +libxlDomainMigrationIsAllowed(virDomainDefPtr def) +{ + /* Migration is not allowed if definition contains any hostdevs */ + if (def->nhostdevs > 0) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("domain has assigned host devices")); + return false; + } + + return true; +} + +char * +libxlDomainMigrationBegin(virConnectPtr conn, + virDomainObjPtr vm, + const char *xmlin) +{ + libxlDriverPrivatePtr driver = conn->privateData; + libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver); + virDomainDefPtr tmpdef = NULL; + virDomainDefPtr def; + char *xml = NULL; + + if (libxlDomainObjBeginJob(driver, vm, LIBXL_JOB_MODIFY) < 0) + goto cleanup; + + if (xmlin) { + if (!(tmpdef = virDomainDefParseString(xmlin, cfg->caps, + driver->xmlopt, + 1 << VIR_DOMAIN_VIRT_XEN, + VIR_DOMAIN_XML_INACTIVE))) + goto endjob; + + def = tmpdef; + } else { + def = vm->def; + } + + if (!libxlDomainMigrationIsAllowed(def)) + goto endjob; + + xml = virDomainDefFormat(def, VIR_DOMAIN_XML_SECURE); + + cleanup: + if (vm) + virObjectUnlock(vm); + + virDomainDefFree(tmpdef); + virObjectUnref(cfg); + return xml; + + endjob: + if (!libxlDomainObjEndJob(driver, vm)) + vm = NULL; + goto cleanup; +} + +virDomainDefPtr +libxlDomainMigrationPrepareDef(libxlDriverPrivatePtr driver, + const char *dom_xml, + const char *dname) +{ + libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver); + virDomainDefPtr def; + char *name = NULL; + + if (!dom_xml) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("no domain XML passed")); + return NULL; + } + + if (!(def = virDomainDefParseString(dom_xml, cfg->caps, driver->xmlopt, + 1 << VIR_DOMAIN_VIRT_XEN, + VIR_DOMAIN_XML_INACTIVE))) + goto cleanup; + + if (dname) { + name = def->name; + if (VIR_STRDUP(def->name, dname) < 0) { + virDomainDefFree(def); + def = NULL; + } + } + + cleanup: + virObjectUnref(cfg); + VIR_FREE(name); + return def; +} + +int +libxlDomainMigrationPrepare(virConnectPtr dconn, + virDomainDefPtr def, + const char *uri_in, + char **uri_out) +{ + libxlDriverPrivatePtr driver = dconn->privateData; + virDomainObjPtr vm = NULL; + char *hostname = NULL; + unsigned short port; + char portstr[100]; + virURIPtr uri = NULL; + virNetSocketPtr *socks = NULL; + size_t nsocks = 0; + int nsocks_listen = 0; + libxlMigrateReceiveArgs *args; + size_t i; + int ret = -1; + + if (!(vm = virDomainObjListAdd(driver->domains, def, + driver->xmlopt, + VIR_DOMAIN_OBJ_LIST_ADD_LIVE | + VIR_DOMAIN_OBJ_LIST_ADD_CHECK_LIVE, + NULL))) + goto cleanup; + + /* Create socket connection to receive migration data */ + if (!uri_in) { + 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 (virPortAllocatorAcquire(driver->migrationPorts, &port) < 0) + goto cleanup; + + if (virAsprintf(uri_out, "tcp://%s:%d", hostname, port) < 0) + goto cleanup; + } else { + if (!(STRPREFIX(uri_in, "tcp://"))) { + /* not full URI, add prefix tcp:// */ + char *tmp; + if (virAsprintf(&tmp, "tcp://%s", uri_in) < 0) + goto cleanup; + uri = virURIParse(tmp); + VIR_FREE(tmp); + } else { + uri = virURIParse(uri_in); + } + + if (uri == NULL) { + virReportError(VIR_ERR_INVALID_ARG, + _("unable to parse URI: %s"), + uri_in); + goto cleanup; + } + + if (uri->server == NULL) { + virReportError(VIR_ERR_INVALID_ARG, + _("missing host in migration URI: %s"), + uri_in); + goto cleanup; + } else { + hostname = uri->server; + } + + if (uri->port == 0) { + if (virPortAllocatorAcquire(driver->migrationPorts, &port) < 0) + goto cleanup; + + } else { + port = uri->port; + } + + if (virAsprintf(uri_out, "tcp://%s:%d", hostname, port) < 0) + goto cleanup; + } + + snprintf(portstr, sizeof(portstr), "%d", port); + + if (virNetSocketNewListenTCP(hostname, portstr, &socks, &nsocks) < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("Fail to create socket for incoming migration")); + goto cleanup; + } + + if (VIR_ALLOC(args) < 0) + goto cleanup; + + args->conn = dconn; + args->vm = vm; + args->socks = socks; + args->nsocks = nsocks; + + for (i = 0; i < nsocks; i++) { + if (virNetSocketSetBlocking(socks[i], true) < 0) + continue; + + if (virNetSocketListen(socks[i], 1) < 0) + continue; + + if (virNetSocketAddIOCallback(socks[i], + 0, + libxlDoMigrateReceive, + args, + NULL) < 0) { + continue; + } + + virNetSocketUpdateIOCallback(socks[i], VIR_EVENT_HANDLE_READABLE); + nsocks_listen++; + } + + if (!nsocks_listen) + goto cleanup; + + ret = 0; + goto done; + + cleanup: + for (i = 0; i < nsocks; i++) { + virNetSocketClose(socks[i]); + virObjectUnref(socks[i]); + } + VIR_FREE(socks); + + done: + virURIFree(uri); + if (vm) + virObjectUnlock(vm); + return ret; +} + +int +libxlDomainMigrationPerform(libxlDriverPrivatePtr driver, + virDomainObjPtr vm, + const char *dom_xml ATTRIBUTE_UNUSED, + const char *dconnuri ATTRIBUTE_UNUSED, + const char *uri_str, + const char *dname ATTRIBUTE_UNUSED, + unsigned int flags) +{ + char *hostname = NULL; + unsigned short port = 0; + char portstr[100]; + virURIPtr uri = NULL; + virNetSocketPtr sock; + int sockfd = -1; + int saved_errno = EINVAL; + int ret = -1; + + /* parse dst host:port from uri */ + uri = virURIParse(uri_str); + if (uri == NULL || uri->server == NULL || uri->port == 0) + goto cleanup; + + hostname = uri->server; + port = uri->port; + snprintf(portstr, sizeof(portstr), "%d", port); + + /* socket connect to dst host:port */ + if (virNetSocketNewConnectTCP(hostname, portstr, &sock) < 0) { + virReportSystemError(saved_errno, + _("unable to connect to '%s:%s'"), + hostname, portstr); + goto cleanup; + } + + if (virNetSocketSetBlocking(sock, true) < 0) { + virObjectUnref(sock); + goto cleanup; + } + + sockfd = virNetSocketDupFD(sock, true); + virObjectUnref(sock); + + /* suspend vm and send saved data to dst through socket fd */ + virObjectUnlock(vm); + ret = libxlDoMigrateSend(driver, vm, flags, sockfd); + virObjectLock(vm); + + cleanup: + /* If failure, terminate the job started in MigrationBegin */ + if (ret == -1) { + if (libxlDomainObjEndJob(driver, vm)) + virObjectUnlock(vm); + } + VIR_FORCE_CLOSE(sockfd); + virURIFree(uri); + return ret; +} + +virDomainPtr +libxlDomainMigrationFinish(virConnectPtr dconn, + virDomainObjPtr vm, + unsigned int flags, + int cancelled) +{ + libxlDriverPrivatePtr driver = dconn->privateData; + libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver); + libxlDomainObjPrivatePtr priv = vm->privateData; + virObjectEventPtr event = NULL; + virDomainPtr dom = NULL; + + virPortAllocatorRelease(driver->migrationPorts, priv->migrationPort); + priv->migrationPort = 0; + + if (cancelled) + goto cleanup; + + if (!(flags & VIR_MIGRATE_PAUSED)) { + if (libxl_domain_unpause(priv->ctx, vm->def->id) != 0) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("Failed to unpause domain")); + goto cleanup; + } + + virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, + VIR_DOMAIN_RUNNING_MIGRATED); + event = virDomainEventLifecycleNewFromObj(vm, + VIR_DOMAIN_EVENT_RESUMED, + VIR_DOMAIN_EVENT_RESUMED_MIGRATED); + } else { + virDomainObjSetState(vm, VIR_DOMAIN_PAUSED, VIR_DOMAIN_PAUSED_USER); + event = virDomainEventLifecycleNewFromObj(vm, + VIR_DOMAIN_EVENT_SUSPENDED, + VIR_DOMAIN_EVENT_SUSPENDED_PAUSED); + } + + if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0) + goto cleanup; + + dom = virGetDomain(dconn, vm->def->name, vm->def->uuid); + + if (dom == NULL) { + libxl_domain_destroy(priv->ctx, vm->def->id, NULL); + libxlDomainCleanup(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED); + event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_STOPPED, + VIR_DOMAIN_EVENT_STOPPED_FAILED); + libxlDomainEventQueue(driver, event); + } + + cleanup: + if (event) + libxlDomainEventQueue(driver, event); + if (vm) + virObjectUnlock(vm); + virObjectUnref(cfg); + return dom; +} + +int +libxlDomainMigrationConfirm(libxlDriverPrivatePtr driver, + virDomainObjPtr vm, + unsigned int flags, + int cancelled) +{ + libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver); + libxlDomainObjPrivatePtr priv = vm->privateData; + virObjectEventPtr event = NULL; + int ret = -1; + + if (cancelled) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("migration failed, attempting to resume on source host")); + if (libxl_domain_resume(priv->ctx, vm->def->id, 1, 0) == 0) { + ret = 0; + } else { + VIR_DEBUG("Unable to resume domain '%s' after failed migration", + vm->def->name); + virDomainObjSetState(vm, VIR_DOMAIN_PAUSED, + VIR_DOMAIN_PAUSED_MIGRATION); + event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_SUSPENDED, + VIR_DOMAIN_EVENT_SUSPENDED_MIGRATED); + ignore_value(virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm)); + } + goto cleanup; + } + + libxl_domain_destroy(priv->ctx, vm->def->id, NULL); + libxlDomainCleanup(driver, vm, VIR_DOMAIN_SHUTOFF_MIGRATED); + event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_STOPPED, + VIR_DOMAIN_EVENT_STOPPED_MIGRATED); + + VIR_DEBUG("Domain '%s' successfully migrated", vm->def->name); + + if (flags & VIR_MIGRATE_UNDEFINE_SOURCE) + virDomainDeleteConfig(cfg->configDir, cfg->autostartDir, vm); + + if (!vm->persistent || (flags & VIR_MIGRATE_UNDEFINE_SOURCE)) + virDomainObjListRemove(driver->domains, vm); + + ret = 0; + + cleanup: + if (!libxlDomainObjEndJob(driver, vm)) + vm = NULL; + if (event) + libxlDomainEventQueue(driver, event); + if (vm) + virObjectUnlock(vm); + virObjectUnref(cfg); + return ret; +} diff --git a/src/libxl/libxl_migration.h b/src/libxl/libxl_migration.h new file mode 100644 index 0000000..63d8bdc --- /dev/null +++ b/src/libxl/libxl_migration.h @@ -0,0 +1,78 @@ +/* + * libxl_migration.h: methods for handling migration with libxenlight + * + * Copyright (c) 2014 SUSE LINUX Products GmbH, Nuernberg, Germany. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Authors: + * Jim Fehlig <jfehlig@xxxxxxxx> + */ + +#ifndef LIBXL_MIGRATION_H +# define LIBXL_MIGRATION_H + +# include "libxl_conf.h" + +# define LIBXL_MIGRATION_FLAGS \ + (VIR_MIGRATE_LIVE | \ + VIR_MIGRATE_UNDEFINE_SOURCE | \ + VIR_MIGRATE_PAUSED) + +/* All supported migration parameters and their types. */ +# define LIBXL_MIGRATION_PARAMETERS \ + VIR_MIGRATE_PARAM_URI, VIR_TYPED_PARAM_STRING, \ + VIR_MIGRATE_PARAM_DEST_NAME, VIR_TYPED_PARAM_STRING, \ + VIR_MIGRATE_PARAM_DEST_XML, VIR_TYPED_PARAM_STRING, \ + NULL + +char * +libxlDomainMigrationBegin(virConnectPtr conn, + virDomainObjPtr vm, + const char *xmlin); + +virDomainDefPtr +libxlDomainMigrationPrepareDef(libxlDriverPrivatePtr driver, + const char *dom_xml, + const char *dname); + +int +libxlDomainMigrationPrepare(virConnectPtr dconn, + virDomainDefPtr def, + const char *uri_in, + char **uri_out); + +int +libxlDomainMigrationPerform(libxlDriverPrivatePtr driver, + virDomainObjPtr vm, + const char *dom_xml, + const char *dconnuri, + const char *uri_str, + const char *dname, + unsigned int flags); + +virDomainPtr +libxlDomainMigrationFinish(virConnectPtr dconn, + virDomainObjPtr vm, + unsigned int flags, + int cancelled); + +int +libxlDomainMigrationConfirm(libxlDriverPrivatePtr driver, + virDomainObjPtr vm, + unsigned int flags, + int cancelled); + +#endif /* LIBXL_DRIVER_H */ -- 1.8.1.4 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |