[libvirt] [PATCH] libxl: support ACPI shutdown flag
by Jim Fehlig
Add support for VIR_DOMAIN_SHUTDOWN_ACPI_POWER_BTN flag in
libxlDomainShutdownFlags(). Inspired by similar functionality
in the Xen xl client.
Signed-off-by: Jim Fehlig <jfehlig(a)suse.com>
---
I considered invoking libxl_send_trigger() immediately when
VIR_DOMAIN_SHUTDOWN_ACPI_POWER_BTN flag is specified, but in the
end decided to only honor the flag if a "normal" shutdown request
failed. This behavior is similar to xl and conforms to the
virDomainShutdownFlags() docs
"If @flags is set to zero, then the hypervisor will choose the method
of shutdown it considers best. To have greater control pass one or
more of the virDomainShutdownFlagValues. The order in which the
hypervisor tries each shutdown method is undefined, and a hypervisor
is not required to support all methods."
I'm certainly receptive to only invoking libxl_send_trigger() when
VIR_DOMAIN_SHUTDOWN_ACPI_POWER_BTN is specified if folks think that
is a better approach.
src/libxl/libxl_driver.c | 26 ++++++++++++++++++++++----
1 file changed, 22 insertions(+), 4 deletions(-)
diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c
index b3f8df6..2b30c08 100644
--- a/src/libxl/libxl_driver.c
+++ b/src/libxl/libxl_driver.c
@@ -868,7 +868,7 @@ libxlDomainShutdownFlags(virDomainPtr dom, unsigned int flags)
int ret = -1;
libxlDomainObjPrivatePtr priv;
- virCheckFlags(0, -1);
+ virCheckFlags(VIR_DOMAIN_SHUTDOWN_ACPI_POWER_BTN, -1);
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
@@ -883,17 +883,35 @@ libxlDomainShutdownFlags(virDomainPtr dom, unsigned int flags)
}
priv = vm->privateData;
- if (libxl_domain_shutdown(priv->ctx, vm->def->id) != 0) {
+ ret = libxl_domain_shutdown(priv->ctx, vm->def->id);
+ if (ret == ERROR_NOPARAVIRT) {
+ if (flags & VIR_DOMAIN_SHUTDOWN_ACPI_POWER_BTN) {
+ VIR_DEBUG("PV control interface not available, "
+ "sending ACPI power button event.");
+ ret = libxl_send_trigger(priv->ctx, vm->def->id,
+ LIBXL_TRIGGER_POWER, 0);
+ } else {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("PV control interface not available, "
+ "preventing external graceful shutdown. "
+ "Consider using an ACPI power event."));
+ ret = -1;
+ goto cleanup;
+ }
+ }
+
+ if (ret != 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to shutdown domain '%d' with libxenlight"),
vm->def->id);
+ ret = -1;
goto cleanup;
}
- /* vm is marked shutoff (or removed from domains list if not persistent)
+ /* ret == 0 == successful shutdown request
+ * vm is marked shutoff (or removed from domains list if not persistent)
* in shutdown event handler.
*/
- ret = 0;
cleanup:
if (vm)
--
1.8.1.4
10 years, 7 months
[libvirt] [PATCH V4] libxl: add migration support
by Jim Fehlig
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(a)suse.com>
---
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(a)suse.com>
+ * Chunyan Liu <cyliu(a)suse.com>
+ */
+
+#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(a)suse.com>
+ */
+
+#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
10 years, 7 months
[libvirt] [PATCH v2] docs: update docs for setting the QEMU BIOS path
by Chen Hanxiao
commit ddf2dfa1f79af0405df5ca10583764a497c7a0db
provide a way to determine which bios files to use.
But we need to update related docs.
disccussed at:
http://www.redhat.com/archives/libvir-list/2012-June/msg01286.html
Signed-off-by: Chen Hanxiao <chenhanxiao(a)cn.fujitsu.com>
---
v2: updated description by Jim's comments.
docs/formatdomain.html.in | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index b9bbcc9..e4bd87e 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -126,9 +126,9 @@
these. <span class="since">Since 0.0.1</span></dd>
<dt><code>loader</code></dt>
<dd>The optional <code>loader</code> tag refers to a firmware blob
- used to assist the domain creation process. At this time, it is
- only needed by Xen fully virtualized
- domains. <span class="since">Since 0.1.0</span></dd>
+ used to assist the domain creation process. It is used by Xen
+ fully virtualized domains as well as setting the QEMU BIOS file
+ path for QEMU/KVM domains. <span class="since">Since 0.9.12</span></dd>
<dt><code>boot</code></dt>
<dd>The <code>dev</code> attribute takes one of the values "fd", "hd",
"cdrom" or "network" and is used to specify the next boot device
--
1.9.0
10 years, 7 months
[libvirt] [PATCH] qemu: don't call virFileExists() for network type disks
by Martin Kletzander
Based on suggestion from Eric [1], because it might not get cleaned up
before the release, so to avoid potential errors.
[1] https://www.redhat.com/archives/libvir-list/2014-April/msg00929.html
Signed-off-by: Martin Kletzander <mkletzan(a)redhat.com>
---
src/qemu/qemu_domain.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index ab19738..f412f49 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -2270,6 +2270,7 @@ qemuDomainCheckDiskPresence(virQEMUDriverPtr driver,
disk = vm->def->disks[i - 1];
const char *path = virDomainDiskGetSource(disk);
enum virStorageFileFormat format = virDomainDiskGetFormat(disk);
+ enum virStorageType type = virDomainDiskGetType(disk);
if (!path)
continue;
@@ -2277,7 +2278,8 @@ qemuDomainCheckDiskPresence(virQEMUDriverPtr driver,
/* There is no need to check the backing chain for disks
* without backing support, the fact that the file exists is
* more than enough */
- if (format >= VIR_STORAGE_FILE_NONE &&
+ if (type != VIR_STORAGE_TYPE_NETWORK &&
+ format >= VIR_STORAGE_FILE_NONE &&
format < VIR_STORAGE_FILE_BACKING &&
virFileExists(path))
continue;
--
1.9.2
10 years, 7 months
[libvirt] [PATCH] docs: update docs for setting the QEMU BIOS file path
by Chen Hanxiao
commit ddf2dfa1f79af0405df5ca10583764a497c7a0db
provides a way to determine which bios files to use.
So we need to update related docs.
disccussed at:
http://www.redhat.com/archives/libvir-list/2012-June/msg01286.html
Signed-off-by: Chen Hanxiao <chenhanxiao(a)cn.fujitsu.com>
---
docs/formatdomain.html.in | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index e851f85..93f14fa 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -128,7 +128,13 @@
<dd>The optional <code>loader</code> tag refers to a firmware blob
used to assist the domain creation process. At this time, it is
only needed by Xen fully virtualized
- domains. <span class="since">Since 0.1.0</span></dd>
+ domains. <span class="since">Since 0.1.0</span>
+ <p>
+ It could also be used to set the QEMU BIOS file path.
+ Then guest will be started with that BIOS file.
+ <span class="since">Since 0.9.12</span>
+ </p>
+ </dd>
<dt><code>boot</code></dt>
<dd>The <code>dev</code> attribute takes one of the values "fd", "hd",
"cdrom" or "network" and is used to specify the next boot device
--
1.9.0
10 years, 7 months
[libvirt] [PATCH] qemu: properly quit migration with abort_on_error
by Martin Kletzander
When EIO comes to qemu while it's replying to
qemuMigrationUpdateJobStatus(), qemu blocks, the migration of RAM can
complete in the meantime, and when qemu unblocks, it sends us
BLOCK_IO_ERROR plus migrations "status": "complete". Even though we
act upon the BLOCK_IO_ERROR by setting the proper state of the domain,
the call still waits for the proper reply on monitor for query_migrate
and after it gets it, it checks that migration is completed and the
migration is finished. This is what abort_on_error flag was meant for
(we can migrate with these errors, but this flag must inhibit such
behaviour). Changing the order of the steps guarantees the flag works
properly.
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1045833
Signed-off-by: Martin Kletzander <mkletzan(a)redhat.com>
---
src/qemu/qemu_migration.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index 593d2d3..3d005a0 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -1812,15 +1812,15 @@ qemuMigrationWaitForCompletion(virQEMUDriverPtr driver, virDomainObjPtr vm,
/* Poll every 50ms for progress & to allow cancellation */
struct timespec ts = { .tv_sec = 0, .tv_nsec = 50 * 1000 * 1000ull };
+ if (qemuMigrationUpdateJobStatus(driver, vm, job, asyncJob) < 0)
+ goto cleanup;
+
/* cancel migration if disk I/O error is emitted while migrating */
if (abort_on_error &&
virDomainObjGetState(vm, &pauseReason) == VIR_DOMAIN_PAUSED &&
pauseReason == VIR_DOMAIN_PAUSED_IOERROR)
goto cancel;
- if (qemuMigrationUpdateJobStatus(driver, vm, job, asyncJob) < 0)
- goto cleanup;
-
if (dconn && virConnectIsAlive(dconn) <= 0) {
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
_("Lost connection to destination host"));
--
1.9.2
10 years, 7 months
[libvirt] [PATCH] storage_backend_rbd: Correct argument order to rbd_create3
by Steven McDonald
The stripe_unit and stripe_count arguments are passed to rbd_create3 in
the wrong order, resulting in a stripe size of 1 byte with 4194304
stripes on newly created RBD volumes.
https://bugzilla.redhat.com/show_bug.cgi?id=1092208
Signed-off-by: Steven McDonald <steven.mcdonald(a)anchor.net.au>
---
src/storage/storage_backend_rbd.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/storage/storage_backend_rbd.c b/src/storage/storage_backend_rbd.c
index 029d2f0..5d4ef79 100644
--- a/src/storage/storage_backend_rbd.c
+++ b/src/storage/storage_backend_rbd.c
@@ -492,7 +492,7 @@ static int virStorageBackendRBDCreateImage(rados_ioctx_t io,
uint64_t stripe_unit = 4194304;
if (rbd_create3(io, name, capacity, features, &order,
- stripe_count, stripe_unit) < 0) {
+ stripe_unit, stripe_count) < 0) {
#else
if (rbd_create(io, name, capacity, &order) < 0) {
#endif
--
2.0.0.rc0
10 years, 7 months
[libvirt] [PATCHv2 0/7] saner readdir usage
by Eric Blake
This extends the work started by Nataneal to cover the entire
tree. Basically, readdir() is awkward to use correctly: to
properly check for (unlikely) errors, one must pre-set errno.
Forcing everything to go through a wrapper ensures that all
our uses will be consistent; this also fixes several bugs
in some of the call sites.
Eric Blake (5):
conf: use virDirRead API
util: use virDirRead API
drivers: use virDirRead API
storage: use virDirRead API
enforce sane readdir usage
Natanael Copa (2):
util: introduce virDirRead wrapper for readdir
nodeinfo: use virDirRead API
cfg.mk | 6 +++
src/conf/domain_conf.c | 5 ++-
src/conf/network_conf.c | 11 +++---
src/conf/nwfilter_conf.c | 6 +--
src/conf/storage_conf.c | 6 +--
src/libvirt_private.syms | 1 +
src/nodeinfo.c | 31 +++++----------
src/openvz/openvz_conf.c | 9 +----
src/parallels/parallels_storage.c | 11 ++++--
src/qemu/qemu_driver.c | 5 ++-
src/qemu/qemu_hostdev.c | 7 ++--
src/secret/secret_driver.c | 6 +--
src/storage/storage_backend.c | 8 ++--
src/storage/storage_backend_fs.c | 6 ++-
src/storage/storage_backend_iscsi.c | 5 ++-
src/storage/storage_backend_scsi.c | 10 +++--
src/util/vircgroup.c | 28 ++++++++------
src/util/virfile.c | 76 +++++++++++++++++++++++--------------
src/util/virfile.h | 4 ++
src/util/virnetdevtap.c | 10 +----
src/util/virpci.c | 20 +++++-----
src/util/virscsi.c | 9 +++--
src/util/virusb.c | 7 +++-
src/util/virutil.c | 4 +-
src/xen/xen_inotify.c | 7 +++-
src/xen/xm_internal.c | 3 +-
26 files changed, 170 insertions(+), 131 deletions(-)
--
1.9.0
10 years, 7 months
[libvirt] [PATCH] xen: ensure /usr/sbin/xend exists before checking status
by Jim Fehlig
With xend on the way out, installations may not even have
/usr/sbin/xend, which results in the following error when the
drivers are probed
2014-04-28 18:21:19.271+0000: 22129: error : virCommandWait:2426 :
internal error: Child process (/usr/sbin/xend status) unexpected exit
status 127: libvirt: error : cannot execute binary /usr/sbin/xend:
No such file or directory
Check for existence of /usr/sbin/xend before trying to run it with
the 'status' option.
Signed-off-by: Jim Fehlig <jfehlig(a)suse.com>
---
src/libxl/libxl_driver.c | 19 ++++++++++++-------
src/xen/xen_driver.c | 13 ++++++++-----
2 files changed, 20 insertions(+), 12 deletions(-)
diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c
index e5ed0f2..8ee61c7 100644
--- a/src/libxl/libxl_driver.c
+++ b/src/libxl/libxl_driver.c
@@ -223,7 +223,6 @@ static bool
libxlDriverShouldLoad(bool privileged)
{
bool ret = false;
- virCommandPtr cmd;
int status;
char *output = NULL;
@@ -236,7 +235,7 @@ libxlDriverShouldLoad(bool privileged)
if (!virFileExists(HYPERVISOR_CAPABILITIES)) {
VIR_INFO("Disabling driver as " HYPERVISOR_CAPABILITIES
" does not exist");
- return false;
+ return ret;
}
/*
* Don't load if not running on a Xen control domain (dom0). It is not
@@ -256,14 +255,20 @@ libxlDriverShouldLoad(bool privileged)
}
/* Don't load if legacy xen toolstack (xend) is in use */
- cmd = virCommandNewArgList("/usr/sbin/xend", "status", NULL);
- if (virCommandRun(cmd, &status) == 0 && status == 0) {
- VIR_INFO("Legacy xen tool stack seems to be in use, disabling "
- "libxenlight driver.");
+ if (virFileExists("/usr/sbin/xend")) {
+ virCommandPtr cmd;
+
+ cmd = virCommandNewArgList("/usr/sbin/xend", "status", NULL);
+ if (virCommandRun(cmd, &status) == 0 && status == 0) {
+ VIR_INFO("Legacy xen tool stack seems to be in use, disabling "
+ "libxenlight driver.");
+ } else {
+ ret = true;
+ }
+ virCommandFree(cmd);
} else {
ret = true;
}
- virCommandFree(cmd);
return ret;
}
diff --git a/src/xen/xen_driver.c b/src/xen/xen_driver.c
index 2ffb06b..bd51909 100644
--- a/src/xen/xen_driver.c
+++ b/src/xen/xen_driver.c
@@ -315,13 +315,16 @@ xenUnifiedProbe(void)
static bool
xenUnifiedXendProbe(void)
{
- virCommandPtr cmd;
bool ret = false;
- cmd = virCommandNewArgList("/usr/sbin/xend", "status", NULL);
- if (virCommandRun(cmd, NULL) == 0)
- ret = true;
- virCommandFree(cmd);
+ if (virFileExists("/usr/sbin/xend")) {
+ virCommandPtr cmd;
+
+ cmd = virCommandNewArgList("/usr/sbin/xend", "status", NULL);
+ if (virCommandRun(cmd, NULL) == 0)
+ ret = true;
+ virCommandFree(cmd);
+ }
return ret;
}
--
1.8.1.4
10 years, 7 months
[libvirt] [PATCH] tests: remove hostdevmgr directory on cleanup
by Martin Kletzander
Signed-off-by: Martin Kletzander <mkletzan(a)redhat.com>
---
tests/virhostdevtest.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/tests/virhostdevtest.c b/tests/virhostdevtest.c
index 089014e..de4cdde 100644
--- a/tests/virhostdevtest.c
+++ b/tests/virhostdevtest.c
@@ -65,6 +65,9 @@ myCleanup(void)
}
if (mgr) {
+ if (mgr->stateDir && !getenv("LIBVIRT_SKIP_CLEANUP"))
+ virFileDeleteTree(mgr->stateDir);
+
virObjectUnref(mgr->activePCIHostdevs);
virObjectUnref(mgr->inactivePCIHostdevs);
virObjectUnref(mgr->activeUSBHostdevs);
--
1.9.2
10 years, 7 months