[libvirt] [RFC]How to improve KVM VM resource assignment and per-vm process/thread scheduling.
by Huangpeng (Peter)
Hi, ALL
Currently kvm hypervisor have lots of features depend on linux standard apis,
like vcpupin/mempin/processpin etc. But in the real production environment,
we need an automated resource assign and/or scheduling, is there any plan to
implement it?
resource assignment requirements like:
cpu eligible by the VMs
In case it is eligible, whether it is in use
if it is in use, whether it is dedicated to one VM, or can be shared by many VMs
In case of Shared CPU
need to configure oversubscription ratio
used ratio info
So does memory, I/O device assignment requirements.
per-vm process/thread scheduling requirements like:
On hypervisor side, VMs use vhost-net, virtio-scsi devices have qemu io-thread, vhost-net
thread, ovs thread, hNIC interrupt context(hirq/softirq), you shoud place there threads on the same
numa node to gain best performance, another important thing, you should balance these
threads' cpuload on all the numa cores to avoid unbalance between vcpu usable resources.
Thanks.
Peter Huang
10 years, 7 months
[libvirt] [PATCH] virsh: Move 'cpu-baseline' to host command group
by Li Yang
As manual said, 'cpu-baseline' isn't specific to a domain,
it should not belong to domain command group, and it's
used for host usually, so move it to host command group.
Signed-off-by: Li Yang <liyang.fnst(a)cn.fujitsu.com>
---
tools/virsh-domain.c | 115 -------------------------------------------------
tools/virsh-host.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 116 insertions(+), 115 deletions(-)
diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c
index 73414f8..16a8854 100644
--- a/tools/virsh-domain.c
+++ b/tools/virsh-domain.c
@@ -6223,115 +6223,6 @@ cmdCPUCompare(vshControl *ctl, const vshCmd *cmd)
return ret;
}
-/*
- * "cpu-baseline" command
- */
-static const vshCmdInfo info_cpu_baseline[] = {
- {.name = "help",
- .data = N_("compute baseline CPU")
- },
- {.name = "desc",
- .data = N_("Compute baseline CPU for a set of given CPUs.")
- },
- {.name = NULL}
-};
-
-static const vshCmdOptDef opts_cpu_baseline[] = {
- {.name = "file",
- .type = VSH_OT_DATA,
- .flags = VSH_OFLAG_REQ,
- .help = N_("file containing XML CPU descriptions")
- },
- {.name = "features",
- .type = VSH_OT_BOOL,
- .help = N_("Show features that are part of the CPU model type")
- },
- {.name = NULL}
-};
-
-static bool
-cmdCPUBaseline(vshControl *ctl, const vshCmd *cmd)
-{
- const char *from = NULL;
- bool ret = false;
- char *buffer;
- char *result = NULL;
- char **list = NULL;
- unsigned int flags = 0;
- int count = 0;
-
- xmlDocPtr xml = NULL;
- xmlNodePtr *node_list = NULL;
- xmlXPathContextPtr ctxt = NULL;
- virBuffer buf = VIR_BUFFER_INITIALIZER;
- size_t i;
-
- if (vshCommandOptBool(cmd, "features"))
- flags |= VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES;
-
- if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
- return false;
-
- if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0)
- return false;
-
- /* add a separate container around the xml */
- virBufferStrcat(&buf, "<container>", buffer, "</container>", NULL);
- if (virBufferError(&buf))
- goto no_memory;
-
- VIR_FREE(buffer);
- buffer = virBufferContentAndReset(&buf);
-
-
- if (!(xml = virXMLParseStringCtxt(buffer, from, &ctxt)))
- goto cleanup;
-
- if ((count = virXPathNodeSet("//cpu[not(ancestor::cpus)]",
- ctxt, &node_list)) == -1)
- goto cleanup;
-
- if (count == 0) {
- vshError(ctl, _("No host CPU specified in '%s'"), from);
- goto cleanup;
- }
-
- list = vshCalloc(ctl, count, sizeof(const char *));
-
- for (i = 0; i < count; i++) {
- if (!(list[i] = virXMLNodeToString(xml, node_list[i]))) {
- vshSaveLibvirtError();
- goto cleanup;
- }
- }
-
- result = virConnectBaselineCPU(ctl->conn,
- (const char **)list, count, flags);
-
- if (result) {
- vshPrint(ctl, "%s", result);
- ret = true;
- }
-
- cleanup:
- xmlXPathFreeContext(ctxt);
- xmlFreeDoc(xml);
- VIR_FREE(result);
- if (list != NULL && count > 0) {
- for (i = 0; i < count; i++)
- VIR_FREE(list[i]);
- }
- VIR_FREE(list);
- VIR_FREE(buffer);
- VIR_FREE(node_list);
-
- return ret;
-
- no_memory:
- vshError(ctl, "%s", _("Out of memory"));
- ret = false;
- goto cleanup;
-}
/*
* "cpu-stats" command
@@ -11472,12 +11363,6 @@ const vshCmdDef domManagementCmds[] = {
.flags = 0
},
#endif
- {.name = "cpu-baseline",
- .handler = cmdCPUBaseline,
- .opts = opts_cpu_baseline,
- .info = info_cpu_baseline,
- .flags = 0
- },
{.name = "cpu-compare",
.handler = cmdCPUCompare,
.opts = opts_cpu_compare,
diff --git a/tools/virsh-host.c b/tools/virsh-host.c
index cac6086..8273654 100644
--- a/tools/virsh-host.c
+++ b/tools/virsh-host.c
@@ -38,6 +38,7 @@
#include "virxml.h"
#include "virtypedparam.h"
#include "virstring.h"
+#include "virfile.h"
/*
* "capabilities" command
@@ -939,6 +940,115 @@ cmdNodeMemoryTune(vshControl *ctl, const vshCmd *cmd)
goto cleanup;
}
+/*
+ * "cpu-baseline" command
+ */
+static const vshCmdInfo info_cpu_baseline[] = {
+ {.name = "help",
+ .data = N_("compute baseline CPU")
+ },
+ {.name = "desc",
+ .data = N_("Compute baseline CPU for a set of given CPUs.")
+ },
+ {.name = NULL}
+};
+
+static const vshCmdOptDef opts_cpu_baseline[] = {
+ {.name = "file",
+ .type = VSH_OT_DATA,
+ .flags = VSH_OFLAG_REQ,
+ .help = N_("file containing XML CPU descriptions")
+ },
+ {.name = "features",
+ .type = VSH_OT_BOOL,
+ .help = N_("Show features that are part of the CPU model type")
+ },
+ {.name = NULL}
+};
+
+static bool
+cmdCPUBaseline(vshControl *ctl, const vshCmd *cmd)
+{
+ const char *from = NULL;
+ bool ret = false;
+ char *buffer;
+ char *result = NULL;
+ char **list = NULL;
+ unsigned int flags = 0;
+ int count = 0;
+
+ xmlDocPtr xml = NULL;
+ xmlNodePtr *node_list = NULL;
+ xmlXPathContextPtr ctxt = NULL;
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+ size_t i;
+
+ if (vshCommandOptBool(cmd, "features"))
+ flags |= VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES;
+
+ if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
+ return false;
+
+ if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0)
+ return false;
+
+ /* add a separate container around the xml */
+ virBufferStrcat(&buf, "<container>", buffer, "</container>", NULL);
+ if (virBufferError(&buf))
+ goto no_memory;
+
+ VIR_FREE(buffer);
+ buffer = virBufferContentAndReset(&buf);
+
+
+ if (!(xml = virXMLParseStringCtxt(buffer, from, &ctxt)))
+ goto cleanup;
+
+ if ((count = virXPathNodeSet("//cpu[not(ancestor::cpus)]",
+ ctxt, &node_list)) == -1)
+ goto cleanup;
+
+ if (count == 0) {
+ vshError(ctl, _("No host CPU specified in '%s'"), from);
+ goto cleanup;
+ }
+
+ list = vshCalloc(ctl, count, sizeof(const char *));
+
+ for (i = 0; i < count; i++) {
+ if (!(list[i] = virXMLNodeToString(xml, node_list[i]))) {
+ vshSaveLibvirtError();
+ goto cleanup;
+ }
+ }
+
+ result = virConnectBaselineCPU(ctl->conn,
+ (const char **)list, count, flags);
+
+ if (result) {
+ vshPrint(ctl, "%s", result);
+ ret = true;
+ }
+
+ cleanup:
+ xmlXPathFreeContext(ctxt);
+ xmlFreeDoc(xml);
+ VIR_FREE(result);
+ if (list != NULL && count > 0) {
+ for (i = 0; i < count; i++)
+ VIR_FREE(list[i]);
+ }
+ VIR_FREE(list);
+ VIR_FREE(buffer);
+ VIR_FREE(node_list);
+
+ return ret;
+
+ no_memory:
+ vshError(ctl, "%s", _("Out of memory"));
+ ret = false;
+ goto cleanup;
+}
const vshCmdDef hostAndHypervisorCmds[] = {
{.name = "capabilities",
.handler = cmdCapabilities,
@@ -952,6 +1062,12 @@ const vshCmdDef hostAndHypervisorCmds[] = {
.info = info_cpu_models,
.flags = 0
},
+ {.name = "cpu-baseline",
+ .handler = cmdCPUBaseline,
+ .opts = opts_cpu_baseline,
+ .info = info_cpu_baseline,
+ .flags = 0
+ },
{.name = "freecell",
.handler = cmdFreecell,
.opts = opts_freecell,
--
1.7.1
10 years, 7 months
[libvirt] [PATCH] 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 of patch to add migration support to the libxl driver. V2 is here
https://www.redhat.com/archives/libvir-list/2014-March/msg00880.html
Patches 1-12 in the original series have been pushed, leaving only
this patch to complete the migration support.
This version adds a simple, extensible messaging protocol to coordinate
transfer of migration data between libxl hosts. Michal Privoznik
noted the even simpler protocol in V2 was not extensible, which in the
end was a good point IMO so I've tried to address that with V3.
Comments welcome. Thanks!
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 | 221 +++++++++++
src/libxl/libxl_migration.c | 920 ++++++++++++++++++++++++++++++++++++++++++++
src/libxl/libxl_migration.h | 78 ++++
7 files changed, 1229 insertions(+), 1 deletion(-)
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 122b853..5618631 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 21d56fc..f0dd4ae 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -702,7 +702,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 a6ae8a1..acf68b8 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;
@@ -4120,6 +4129,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;
@@ -4298,6 +4308,212 @@ 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)
+ 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,
@@ -4388,6 +4604,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..4b74ef4
--- /dev/null
+++ b/src/libxl/libxl_migration.c
@@ -0,0 +1,920 @@
+/*
+ * 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
+
+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=negotiatedversion
+ * - src->dst: state="sending-binary-data", version=negotiatedversion
+ * _ src->dst: binary migration data
+ * - dst->src: state="received-binary-data", version=negotiatedversion
+ * - src->dst: state="done", version=negotiatedversion
+ *
+ */
+
+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
+libxlMigrationMsgIsState(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 int
+libxlMigrationMsgSetState(virJSONValuePtr message, const char *state)
+{
+ virJSONValuePtr body;
+
+ if (!(body = virJSONValueObjectGet(message, "libvirt-libxl-mig-msg")))
+ return -1;
+
+ virJSONValueObjectRemoveKey(body, "state", NULL);
+ virJSONValueObjectAppendString(body, "state", state);
+
+ return 0;
+}
+
+
+static int
+libxlMigrationMsgSend(int fd, virJSONValuePtr message, int status)
+{
+ int ret = -1;
+ virJSONValuePtr body;
+ int len;
+ char *msgstr;
+
+ if (!(body = virJSONValueObjectGet(message, "libvirt-libxl-mig-msg")))
+ return -1;
+
+ virJSONValueObjectRemoveKey(body, "status", NULL);
+ virJSONValueObjectAppendNumberInt(body, "status", status);
+
+ if (!(msgstr = virJSONValueToString(message, false)))
+ 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[512];
+ int ret;
+ virJSONValuePtr msg = NULL;
+ virJSONValuePtr body;
+ int status;
+ size_t i;
+
+ for (i = 0; i < sizeof(buf);) {
+ ret = saferead(fd, &(buf[i]), 1);
+ if (ret == -1 && errno != EAGAIN)
+ goto error;
+ if (ret == 1) {
+ if (buf[i] == '\0')
+ break;
+ i++;
+ }
+ }
+
+ VIR_DEBUG("Received migration message %s", buf);
+ if (!(msg = virJSONValueFromString(buf)))
+ return NULL;
+
+ if (!(body = virJSONValueObjectGet(msg, "libvirt-libxl-mig-msg")))
+ goto error;
+
+ if (virJSONValueObjectGetNumberInt(body, "status", &status) < 0)
+ goto error;
+
+ if (status != 0)
+ goto error;
+
+ return msg;
+
+ error:
+ virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("Failed to receive migration message"));
+ virJSONValueFree(msg);
+ return NULL;
+}
+
+static int
+libxlMigrationSrcNegotiate(int fd)
+{
+ int ret = -1;
+ virJSONValuePtr msg;
+ virJSONValuePtr body;
+ int ver;
+
+ if (!(msg = libxlMigrationMsgReceive(fd)))
+ return -1;
+
+ if (!libxlMigrationMsgIsState(msg, "negotiate-version"))
+ goto cleanup;
+ /*
+ * ACK by returning the message, with version set to
+ * min(dest ver, src ver)
+ */
+ if (!(body = virJSONValueObjectGet(msg, "libvirt-libxl-mig-msg")))
+ goto cleanup;
+
+ if (virJSONValueObjectGetNumberInt(body, "version", &ver) < 0)
+ goto cleanup;
+ if (ver > LIBXL_MIGRATION_PROTO_VERSION) {
+ virJSONValueObjectRemoveKey(body, "version", NULL);
+ virJSONValueObjectAppendNumberInt(body,
+ "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 (!libxlMigrationMsgIsState(msg, "send-binary-data"))
+ goto cleanup;
+
+ /* ACK by returning the message with state sending */
+ libxlMigrationMsgSetState(msg, "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;
+
+ libxlMigrationMsgSetState(msg, "send-binary-data");
+ if (libxlMigrationMsgSend(fd, msg, 0) < 0)
+ return -1;
+
+ /* Receive ACK, state sending */
+ if (!(tmp = libxlMigrationMsgReceive(fd)))
+ return -1;
+ if (!libxlMigrationMsgIsState(tmp, "sending-binary-data"))
+ 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 (!libxlMigrationMsgIsState(msg, "received-binary-data"))
+ goto cleanup;
+
+ /* ACK by returning the message with state done */
+ libxlMigrationMsgSetState(msg, "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;
+
+ libxlMigrationMsgSetState(msg, "received-binary-data");
+ if (libxlMigrationMsgSend(fd, msg, 0) < 0)
+ return -1;
+
+ /* Receive ACK, state done */
+ if (!(tmp = libxlMigrationMsgReceive(fd)))
+ return -1;
+ if (!libxlMigrationMsgIsState(tmp, "done"))
+ 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)
+{
+ libxlMigrationMsgSetState(msg, "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;
+
+ if (libxl_domain_suspend(priv->ctx, vm->def->id,
+ sockfd, xl_flags, NULL) != 0) {
+ libxlMigrationSrcFailed(sockfd, ret);
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Failed to save domain '%d' with libxenlight"),
+ vm->def->id);
+ 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:
+ 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] KVM Connection issue
by Vikas Kokare
We are using Libvirt Java API version 0.5.0 to connect RHEL 5 KVM, and
fetch KVM environment attributes. While doing it, we are seeing the
following exception:
*this function is not supported by the connection driver:
qemuGetSchedulerType Stack trace:
org.libvirt.ErrorHandler.processError(Unknown Source)
org.libvirt.Connect.processError(Unknown Source)
org.libvirt.Domain.processError(Unknown Source)
org.libvirt.Domain.getSchedulerType(Unknown Source)*
The machine that is running the Java code to connect to the KVM is also
RHEL 5. Is this related to certain libvirt version mismatch or something
not supported?
10 years, 7 months
[libvirt] [PATCH 1/1] Enable QEMU_CAPS_PCI_MULTIBUS capability for QEMU2.0 forward.
by Li Zhang
From: Li Zhang <zhlcindy(a)linux.vnet.ibm.com>
For QEMU2.0 forward version, it supports PCI multiBUS.
Currently, libvirt still disables it which causes an error
"Bus 'pci' not found".
Signed-off-by: Li Zhang <zhlcindy(a)linux.vnet.ibm.com>
---
src/qemu/qemu_capabilities.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
index 2c8ec10..b49398f 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -3019,6 +3019,9 @@ virQEMUCapsInitQMPMonitor(virQEMUCapsPtr qemuCaps,
if (qemuCaps->version >= 1006000)
virQEMUCapsSet(qemuCaps, QEMU_CAPS_DEVICE_VIDEO_PRIMARY);
+ if (qemuCaps->version >= 2000000)
+ virQEMUCapsSet(qemuCaps, QEMU_CAPS_PCI_MULTIBUS);
+
if (virQEMUCapsProbeQMPCommands(qemuCaps, mon) < 0)
goto cleanup;
if (virQEMUCapsProbeQMPEvents(qemuCaps, mon) < 0)
--
1.8.2.1
10 years, 7 months
[libvirt] [PATCH] docs: document nmdm type console
by Roman Bogorodskiy
* Add nmdm type device to domain format documnetation
* Add a section about nmdm console usage to the bhyve driver
documentation
---
docs/drvbhyve.html.in | 32 ++++++++++++++++++++++++++++++++
docs/formatdomain.html.in | 29 +++++++++++++++++++++++++++++
2 files changed, 61 insertions(+)
diff --git a/docs/drvbhyve.html.in b/docs/drvbhyve.html.in
index 60eee85..0b0e296 100644
--- a/docs/drvbhyve.html.in
+++ b/docs/drvbhyve.html.in
@@ -79,5 +79,37 @@ in libvirt bhyve driver is yet to be implemented).
</domain>
</pre>
+
+<h2><a name="usage">Guest usage / management</a></h2>
+
+<h3><a name="console">Connecting to a guest console</a></h3>
+
+<p>
+Guest console connection is supported through the <code>nmdm</code> device. It could be enabled by adding
+the following to the domain XML (<span class="since">Since 1.2.4</span>):
+</p>
+
+<pre>
+ ...
+ <devices>
+ <serial type="nmdm">
+ <source master="/dev/nmdm0A" slave="/dev/nmdm0B"/>
+ </serial>
+ </devices>
+ ...</pre>
+
+<p>
+Then <code>virsh console</code> command can be used to connect to the text console
+of a guest.</p>
+
+<p><b>NB:</b> Some versions of bhyve have a bug that prevents guests from booting
+until the console is opened by a client. This bug was fixed in FreeBSD
+<a href="http://svnweb.freebsd.org/changeset/base/262884">r262884</a>. If
+an older version is used, one either have to open a console manually with <code>virsh console</code>
+to let a guest boot or start a guest using:</p>
+
+<pre>start --console domname</pre>
+
+
</body>
</html>
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index e851f85..a937e42 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -4721,6 +4721,35 @@ qemu-kvm -net nic,model=? /dev/null
</devices>
...</pre>
+ <h6><a name="elementsNmdm">Nmdm device</a></h6>
+
+ <p>
+ The nmdm device driver, available on FreeBSD, provides two
+ tty devices connected together by a virual null modem cable.
+ <span class="since">Since 1.2.4</span>
+ </p>
+
+<pre>
+ ...
+ <devices>
+ <serial type="nmdm">
+ <source master="/dev/nmdm0A" slave="/dev/nmdm0B"/>
+ </serial>
+ </devices>
+ ...</pre>
+
+ <p>
+ The <code>source</code> element has these attributes:
+ </p>
+
+ <dl>
+ <dt><code>master</code></dt>
+ <dd>Master device of the pair, that is passed to the hypervisor.</dd>
+
+ <dt><code>slave</code></dt>
+ <dd>Slave device of the pair, that is passed to the clients for connection
+ to the guest console.</dd>
+ </dl>
<h4><a name="elementsSound">Sound devices</a></h4>
--
1.9.0
10 years, 7 months
[libvirt] [PATCH v3] qemu: don't check for backing chains for formats w/o snapshot support
by Martin Kletzander
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1019926
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=868673
Signed-off-by: Martin Kletzander <mkletzan(a)redhat.com>
---
Notes:
v3:
- rebased on current master and this time it doesn't break
startupPolicy for RAW disks
v2:
- smaller and cleaner variant of v1 [1] after discussion with Eric
[1] https://www.redhat.com/archives/libvir-list/2014-April/msg00716.html
src/qemu/qemu_domain.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index bb9cb6b..ab19738 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -2268,8 +2268,18 @@ qemuDomainCheckDiskPresence(virQEMUDriverPtr driver,
VIR_DEBUG("Checking for disk presence");
for (i = vm->def->ndisks; i > 0; i--) {
disk = vm->def->disks[i - 1];
+ const char *path = virDomainDiskGetSource(disk);
+ enum virStorageFileFormat format = virDomainDiskGetFormat(disk);
- if (!virDomainDiskGetSource(disk))
+ if (!path)
+ continue;
+
+ /* 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 &&
+ format < VIR_STORAGE_FILE_BACKING &&
+ virFileExists(path))
continue;
if (qemuDomainDetermineDiskChain(driver, vm, disk, false) >= 0 &&
--
1.9.2
10 years, 7 months
[libvirt] [PATCHv2 0/6] Utility functions for storing uninstalled location
by Nehal J Wani
When libvirtd is run from a build directory without being installed, it
should not depend on files from a libvirt package installed in the
system. Currently, APIs defined in src/ don't know whether libvirtd
is being run from the build dir or the installed dir. The following
additions provide the functionality to do so:
virSetUninstalledDir
virGetUninstalledDir
Example usage (utility = lxc|iohelper):
char *path_tmp = virGetUninstalledDir();
if (path_tmp)
/* do stuff with ("%s/../../src/libvirt_<utility>", path_tmp) */
else
/* do stuff with (LIBEXECDIR "/libvirt_<utility>") */
v1:
Refer: https://www.redhat.com/archives/libvir-list/2014-March/msg01427.html
Nehal J Wani (6):
Add utility functions for storing uninstalled location
Use virGetUninstalledDir() in src/util/virfile.c
Use virGetUninstalledDir() in src/lxc/lxc_conf.c
Use virGetUninstalledDir() in src/storage/storage_backend_disk.c
Use virGetUninstalledDir() in src/fdstream.c
Remove obsolete function virFDStreamSetIOHelper()
10 years, 7 months
[libvirt] [PATCH 0/4] Publish disk backing chains in domain XML
by Jiri Denemark
This series depends on Peter's "Get rid of virStorageFileMetadata" sent
about 10 hours ago.
Jiri Denemark (4):
conf: Output disk backing store details in domain XML
conf: Format and parse backing chains in domain XML
tests: More output options for xml2xml tests
tests: Test backing store XML formatting and parsing
docs/formatdomain.html.in | 59 ++++++++++
docs/schemas/domaincommon.rng | 48 +++++++-
src/conf/domain_conf.c | 128 +++++++++++++++++++++
tests/domainschemadata/backing-chains.xml | 94 +++++++++++++++
.../qemuxml2argv-disk-backing-chains.xml | 94 +++++++++++++++
.../qemuxml2argvdata/qemuxml2argv-disk-mirror.xml | 4 +
.../qemuxml2argv-seclabel-static-labelskip.xml | 1 +
.../qemuxml2xmlout-disk-backing-chains-active.xml | 96 ++++++++++++++++
...qemuxml2xmlout-disk-backing-chains-inactive.xml | 59 ++++++++++
tests/qemuxml2xmltest.c | 46 ++++++--
tests/sexpr2xmldata/sexpr2xml-boot-grub.xml | 1 +
tests/sexpr2xmldata/sexpr2xml-bridge-ipaddr.xml | 1 +
tests/sexpr2xmldata/sexpr2xml-curmem.xml | 1 +
.../sexpr2xml-disk-block-shareable.xml | 1 +
tests/sexpr2xmldata/sexpr2xml-disk-block.xml | 1 +
.../sexpr2xml-disk-drv-blktap-qcow.xml | 1 +
.../sexpr2xml-disk-drv-blktap-raw.xml | 1 +
.../sexpr2xml-disk-drv-blktap2-raw.xml | 1 +
tests/sexpr2xmldata/sexpr2xml-disk-file.xml | 1 +
tests/sexpr2xmldata/sexpr2xml-fv-autoport.xml | 2 +
tests/sexpr2xmldata/sexpr2xml-fv-empty-kernel.xml | 2 +
tests/sexpr2xmldata/sexpr2xml-fv-force-hpet.xml | 2 +
tests/sexpr2xmldata/sexpr2xml-fv-force-nohpet.xml | 2 +
tests/sexpr2xmldata/sexpr2xml-fv-kernel.xml | 1 +
tests/sexpr2xmldata/sexpr2xml-fv-legacy-vfb.xml | 1 +
tests/sexpr2xmldata/sexpr2xml-fv-localtime.xml | 2 +
tests/sexpr2xmldata/sexpr2xml-fv-net-ioemu.xml | 2 +
tests/sexpr2xmldata/sexpr2xml-fv-net-netfront.xml | 2 +
tests/sexpr2xmldata/sexpr2xml-fv-parallel-tcp.xml | 2 +
.../sexpr2xml-fv-serial-dev-2-ports.xml | 2 +
.../sexpr2xml-fv-serial-dev-2nd-port.xml | 2 +
tests/sexpr2xmldata/sexpr2xml-fv-serial-file.xml | 2 +
tests/sexpr2xmldata/sexpr2xml-fv-serial-null.xml | 2 +
tests/sexpr2xmldata/sexpr2xml-fv-serial-pipe.xml | 2 +
tests/sexpr2xmldata/sexpr2xml-fv-serial-pty.xml | 2 +
tests/sexpr2xmldata/sexpr2xml-fv-serial-stdio.xml | 2 +
.../sexpr2xml-fv-serial-tcp-telnet.xml | 2 +
tests/sexpr2xmldata/sexpr2xml-fv-serial-tcp.xml | 2 +
tests/sexpr2xmldata/sexpr2xml-fv-serial-udp.xml | 2 +
tests/sexpr2xmldata/sexpr2xml-fv-serial-unix.xml | 2 +
tests/sexpr2xmldata/sexpr2xml-fv-sound-all.xml | 2 +
tests/sexpr2xmldata/sexpr2xml-fv-sound.xml | 2 +
tests/sexpr2xmldata/sexpr2xml-fv-usbmouse.xml | 2 +
tests/sexpr2xmldata/sexpr2xml-fv-usbtablet.xml | 2 +
tests/sexpr2xmldata/sexpr2xml-fv-utc.xml | 2 +
tests/sexpr2xmldata/sexpr2xml-fv-v2.xml | 2 +
tests/sexpr2xmldata/sexpr2xml-fv.xml | 2 +
tests/sexpr2xmldata/sexpr2xml-net-bridged.xml | 1 +
tests/sexpr2xmldata/sexpr2xml-net-e1000.xml | 1 +
tests/sexpr2xmldata/sexpr2xml-net-routed.xml | 1 +
tests/sexpr2xmldata/sexpr2xml-no-source-cdrom.xml | 2 +
tests/sexpr2xmldata/sexpr2xml-pci-devs.xml | 1 +
.../sexpr2xml-pv-bootloader-cmdline.xml | 1 +
tests/sexpr2xmldata/sexpr2xml-pv-bootloader.xml | 1 +
tests/sexpr2xmldata/sexpr2xml-pv-localtime.xml | 1 +
tests/sexpr2xmldata/sexpr2xml-pv-vcpus.xml | 1 +
.../sexpr2xml-pv-vfb-new-vncdisplay.xml | 1 +
tests/sexpr2xmldata/sexpr2xml-pv-vfb-new.xml | 1 +
tests/sexpr2xmldata/sexpr2xml-pv-vfb-orig.xml | 1 +
.../sexpr2xmldata/sexpr2xml-pv-vfb-type-crash.xml | 1 +
tests/sexpr2xmldata/sexpr2xml-pv.xml | 1 +
61 files changed, 691 insertions(+), 16 deletions(-)
create mode 100644 tests/domainschemadata/backing-chains.xml
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-disk-backing-chains.xml
create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-disk-backing-chains-active.xml
create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-disk-backing-chains-inactive.xml
--
1.9.2
10 years, 7 months
[libvirt] [PATCH v2] qemu: don't check for backing chains for formats w/o snapshot support
by Martin Kletzander
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1019926
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=868673
Signed-off-by: Martin Kletzander <mkletzan(a)redhat.com>
---
Notes:
Smaller and cleaner variant of v1 [1] after discussion with Eric
[1] https://www.redhat.com/archives/libvir-list/2014-April/msg00716.html
src/qemu/qemu_domain.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 45ed872..5687075 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -2268,11 +2268,14 @@ qemuDomainCheckDiskPresence(virQEMUDriverPtr driver,
VIR_DEBUG("Checking for disk presence");
for (i = vm->def->ndisks; i > 0; i--) {
disk = vm->def->disks[i - 1];
+ enum virStorageFileFormat format = virDomainDiskGetFormat(disk);
if (!virDomainDiskGetSource(disk))
continue;
- if (qemuDomainDetermineDiskChain(driver, vm, disk, false) >= 0 &&
+ if ((format < VIR_STORAGE_FILE_NONE ||
+ format >= VIR_STORAGE_FILE_BACKING) &&
+ qemuDomainDetermineDiskChain(driver, vm, disk, false) >= 0 &&
qemuDiskChainCheckBroken(disk) >= 0)
continue;
--
1.9.2
10 years, 7 months