Introduces several new public API options for migration - VIR_MIGRATE_PEER2PEER: With this flag the client only invokes the virDomainMigratePerform method, expecting the source host driver to do whatever is required to complete the entire migration process. - VIR_MIGRATE_TUNNELLED: With this flag the actual data for migration will be tunnelled over the libvirtd RPC channel. This requires that VIR_MIGRATE_PEER2PEER is also set. - virDomainMigrateToURI: This is variant of the existing virDomainMigrate method which can be used when the VIR_MIGRATE_PEER2PEER flag is set. The benefit of this method is that no virConnectPtr object is required for the destination host, only a destination URI. The URI for VIR_MIGRATE_TUNNELLED must be a valid libvirt URI. For non-tunnelled migration a hypervisor specific migration URI is used. * include/libvirt/libvirt.h, include/libvirt/libvirt.h.in: Add VIR_MIGRATE_PEER2PEER, VIR_MIGRATE_TUNNELLED and virDomainMigrateToURI. * src/driver.h: Remove feature flags * src/libvirt_internal.h: Add feature flags, and include new VIR_FEATURE_MIGRATE_P2P indicating support for the new VIR_MIGRATE_PEER2PEER mode. * src/libvirt.c: Implement support for VIR_MIGRATE_PEER2PEER and virDomainMigrateToURI APIs. * src/virsh.c: Add --p2p and --tunnelled args and use the new virDomainMigrateToURI method where possible. * src/xen_unified.c: Advertise support for P2P migration * src/xend_internal.c: Accept VIR_MIGRATE_PEER2PEER flag. * src/libvirt_public.syms: Export virDomainMigrateToURI method --- include/libvirt/libvirt.h | 8 ++- include/libvirt/libvirt.h.in | 8 ++- src/driver.h | 19 ---- src/internal.h | 2 + src/libvirt.c | 224 +++++++++++++++++++++++++++++++++++++----- src/libvirt_internal.h | 30 ++++++ src/libvirt_public.syms | 1 + src/virsh.c | 42 ++++++-- src/xen_unified.c | 7 +- src/xend_internal.c | 6 + 10 files changed, 286 insertions(+), 61 deletions(-) diff --git a/include/libvirt/libvirt.h b/include/libvirt/libvirt.h index 10ec04a..1745396 100644 --- a/include/libvirt/libvirt.h +++ b/include/libvirt/libvirt.h @@ -336,7 +336,9 @@ typedef virDomainInterfaceStatsStruct *virDomainInterfaceStatsPtr; /* Domain migration flags. */ typedef enum { - VIR_MIGRATE_LIVE = 1, /* live migration */ + VIR_MIGRATE_LIVE = (1 << 0), /* live migration */ + VIR_MIGRATE_PEER2PEER = (1 << 1), /* direct source -> dest host control channel */ + VIR_MIGRATE_TUNNELLED = (1 << 2), /* tunnel migration data over libvirtd connection */ } virDomainMigrateFlags; /* Domain migration. */ @@ -344,6 +346,10 @@ virDomainPtr virDomainMigrate (virDomainPtr domain, virConnectPtr dconn, unsigned long flags, const char *dname, const char *uri, unsigned long bandwidth); +int virDomainMigrateToURI (virDomainPtr domain, const char *duri, + unsigned long flags, const char *dname, + unsigned long bandwidth); + /** * VIR_NODEINFO_MAXCPUS: * @nodeinfo: virNodeInfo instance diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 012d30e..5e1a500 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -336,7 +336,9 @@ typedef virDomainInterfaceStatsStruct *virDomainInterfaceStatsPtr; /* Domain migration flags. */ typedef enum { - VIR_MIGRATE_LIVE = 1, /* live migration */ + VIR_MIGRATE_LIVE = (1 << 0), /* live migration */ + VIR_MIGRATE_PEER2PEER = (1 << 1), /* direct source -> dest host control channel */ + VIR_MIGRATE_TUNNELLED = (1 << 2), /* tunnel migration data over libvirtd connection */ } virDomainMigrateFlags; /* Domain migration. */ @@ -344,6 +346,10 @@ virDomainPtr virDomainMigrate (virDomainPtr domain, virConnectPtr dconn, unsigned long flags, const char *dname, const char *uri, unsigned long bandwidth); +int virDomainMigrateToURI (virDomainPtr domain, const char *duri, + unsigned long flags, const char *dname, + unsigned long bandwidth); + /** * VIR_NODEINFO_MAXCPUS: * @nodeinfo: virNodeInfo instance diff --git a/src/driver.h b/src/driver.h index 6a3dcc2..893e98b 100644 --- a/src/driver.h +++ b/src/driver.h @@ -44,25 +44,6 @@ typedef enum { VIR_DRV_OPEN_ERROR = -2, } virDrvOpenStatus; -/* Feature detection. This is a libvirt-private interface for determining - * what features are supported by the driver. - * - * The remote driver passes features through to the real driver at the - * remote end unmodified, except if you query a VIR_DRV_FEATURE_REMOTE* - * feature. - */ - /* Driver supports V1-style virDomainMigrate, ie. domainMigratePrepare/ - * domainMigratePerform/domainMigrateFinish. - */ -#define VIR_DRV_FEATURE_MIGRATION_V1 1 - - /* Driver is not local. */ -#define VIR_DRV_FEATURE_REMOTE 2 - - /* Driver supports V2-style virDomainMigrate, ie. domainMigratePrepare2/ - * domainMigratePerform/domainMigrateFinish2. - */ -#define VIR_DRV_FEATURE_MIGRATION_V2 3 /* Internal feature-detection macro. Don't call drv->supports_feature * directly, because it may be NULL, use this macro instead. diff --git a/src/internal.h b/src/internal.h index 8fa579c..bd1cfe6 100644 --- a/src/internal.h +++ b/src/internal.h @@ -24,6 +24,8 @@ #include "libvirt/libvirt.h" #include "libvirt/virterror.h" +#include "libvirt_internal.h" + /* On architectures which lack these limits, define them (ie. Cygwin). * Note that the libvirt code should be robust enough to handle the * case where actual value is longer than these limits (eg. by setting diff --git a/src/libvirt.c b/src/libvirt.c index 124e5db..481afec 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -35,7 +35,6 @@ #include "virterror_internal.h" #include "logging.h" #include "datatypes.h" -#include "libvirt_internal.h" #include "driver.h" #include "uuid.h" @@ -3059,6 +3058,38 @@ virDomainMigrateVersion2 (virDomainPtr domain, return ddomain; } + + /* + * This is sort of a migration v3 + * + * This performs a peer-2-peer migration where source host + * does all the communication with the destination host. + */ +static int +virDomainMigrateP2P (virDomainPtr domain, + unsigned long flags, + const char *dname, + const char *uri, + unsigned long bandwidth) +{ + if (!domain->conn->driver->domainMigratePerform) { + virLibConnError (domain->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; + } + + /* Perform the migration. The driver isn't supposed to return + * until the migration is complete. + */ + return domain->conn->driver->domainMigratePerform(domain, + NULL, /* cookie */ + 0, /* cookielen */ + uri, + flags, + dname, + bandwidth); +} + + /** * virDomainMigrate: * @domain: a domain object @@ -3072,22 +3103,34 @@ virDomainMigrateVersion2 (virDomainPtr domain, * host given by dconn (a connection to the destination host). * * Flags may be one of more of the following: - * VIR_MIGRATE_LIVE Attempt a live migration. + * VIR_MIGRATE_LIVE Do not pause the VM during migration + * VIR_MIGRATE_PEER2PEER Direct connection between source & destination hosts + * VIR_MIGRATE_TUNNELLED Tunnel migration data over the libvirt RPC channel + * + * VIR_MIGRATE_TUNNELLED requires that VIR_MIGRATE_PEER2PEER be set. + * Applications using the VIR_MIGRATE_PEER2PEER flag will probably + * prefer to invoke virDomainMigrateToURI, avoiding the need to + * open connection to the destination host themselves. * * If a hypervisor supports renaming domains during migration, * then you may set the dname parameter to the new name (otherwise * it keeps the same name). If this is not supported by the * hypervisor, dname must be NULL or else you will get an error. * - * Since typically the two hypervisors connect directly to each - * other in order to perform the migration, you may need to specify - * a path from the source to the destination. This is the purpose - * of the uri parameter. If uri is NULL, then libvirt will try to - * find the best method. Uri may specify the hostname or IP address - * of the destination host as seen from the source. Or uri may be - * a URI giving transport, hostname, user, port, etc. in the usual - * form. Refer to driver documentation for the particular URIs - * supported. + * If the VIR_MIGRATE_TUNNELLED flag is set, the uri parameter + * must be a valid libvirt connection URI, by which the source + * libvirt driver can connect to the destination libvirt. If + * omitted, the dconn connection object will be queried for its + * current URI. + * + * If the VIR_MIGRATE_TUNNELED flag is NOT set, the URI parameter + * takes a hypervisor specific format. The hypervisor capabilities + * XML includes details of the support URI schemes. If omitted + * the dconn will be asked for a default URI. + * + * In either case it is typically only neccessary to specify a + * URI if the destination host has multiple interfaces and a + * specific interface is required to transmit migration data. * * The maximum bandwidth (in Mbps) that will be used to do migration * can be specified with the bandwidth parameter. If set to 0, @@ -3142,24 +3185,50 @@ virDomainMigrate (virDomainPtr domain, goto error; } - /* Check that migration is supported by both drivers. */ - if (VIR_DRV_SUPPORTS_FEATURE (domain->conn->driver, domain->conn, - VIR_DRV_FEATURE_MIGRATION_V1) && - VIR_DRV_SUPPORTS_FEATURE (dconn->driver, dconn, - VIR_DRV_FEATURE_MIGRATION_V1)) - ddomain = virDomainMigrateVersion1 (domain, dconn, flags, dname, uri, bandwidth); - else if (VIR_DRV_SUPPORTS_FEATURE (domain->conn->driver, domain->conn, - VIR_DRV_FEATURE_MIGRATION_V2) && - VIR_DRV_SUPPORTS_FEATURE (dconn->driver, dconn, - VIR_DRV_FEATURE_MIGRATION_V2)) - ddomain = virDomainMigrateVersion2 (domain, dconn, flags, dname, uri, bandwidth); - else { - virLibConnError (domain->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); - goto error; + if (flags & VIR_MIGRATE_PEER2PEER) { + if (VIR_DRV_SUPPORTS_FEATURE (domain->conn->driver, domain->conn, + VIR_DRV_FEATURE_MIGRATION_P2P)) { + char *duri = NULL; + if (!uri) + duri = virConnectGetURI(dconn); + + if (virDomainMigrateP2P (domain, flags, dname, uri ? uri : duri, bandwidth) < 0) { + VIR_FREE(duri); + goto error; + } + VIR_FREE(duri); + + ddomain = virDomainLookupByName (dconn, dname ? dname : domain->name); + } else { + virLibConnError (domain->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + } else { + if (flags & VIR_MIGRATE_TUNNELLED) { + virLibConnError(domain->conn, VIR_ERR_OPERATION_INVALID, + _("cannot perform tunnelled migration without using peer2peer flag")); + goto error; + } + + /* Check that migration is supported by both drivers. */ + if (VIR_DRV_SUPPORTS_FEATURE (domain->conn->driver, domain->conn, + VIR_DRV_FEATURE_MIGRATION_V1) && + VIR_DRV_SUPPORTS_FEATURE (dconn->driver, dconn, + VIR_DRV_FEATURE_MIGRATION_V1)) + ddomain = virDomainMigrateVersion1 (domain, dconn, flags, dname, uri, bandwidth); + else if (VIR_DRV_SUPPORTS_FEATURE (domain->conn->driver, domain->conn, + VIR_DRV_FEATURE_MIGRATION_V2) && + VIR_DRV_SUPPORTS_FEATURE (dconn->driver, dconn, + VIR_DRV_FEATURE_MIGRATION_V2)) + ddomain = virDomainMigrateVersion2 (domain, dconn, flags, dname, uri, bandwidth); + else { + virLibConnError (domain->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } } - if (ddomain == NULL) - goto error; + if (ddomain == NULL) + goto error; return ddomain; @@ -3169,6 +3238,107 @@ error: return NULL; } + +/** + * virDomainMigrateToURI: + * @domain: a domain object + * @duri: libvirt connection URI for destination host + * @flags: flags + * @dname: (optional) rename domain to this at destination + * @bandwidth: (optional) specify migration bandwidth limit in Mbps + * + * Migrate the domain object from its current host to the destination + * host given by duri. The duri is a libvirt connection URI that the + * source host will use to talk to the destination. + * + * The difference from the regular virDomainMigrate method is + * that the calling application does not need a direct connection + * to the destination host. The source libvirt driver makes a + * direct peer-to-peer connection to the destination libvirtd + * without the client appliction being involved. + * + * The VIR_MIGRATE_PEER2PEER flag is mandatory for this method. + * If an application wishes to run without this flag, then it + * may use the alternative virDomainMigrate method which requires + * an virConnectPtr object for the destination host + * + * The following additional flags may also be set + * VIR_MIGRATE_LIVE Do not pause the VM during migration + * VIR_MIGRATE_TUNNELLED Tunnel migration data over the libvirt RPC channel + * + * If a hypervisor supports renaming domains during migration, + * then you may set the dname parameter to the new name (otherwise + * it keeps the same name). If this is not supported by the + * hypervisor, dname must be NULL or else you will get an error. + * + * The maximum bandwidth (in Mbps) that will be used to do migration + * can be specified with the bandwidth parameter. If set to 0, + * libvirt will choose a suitable default. Some hypervisors do + * not support this feature and will return an error if bandwidth + * is not 0. + * + * To see which features are supported by the current hypervisor, + * see virConnectGetCapabilities, /capabilities/host/migration_features. + * + * There are many limitations on migration imposed by the underlying + * technology - for example it may not be possible to migrate between + * different processors even with the same architecture, or between + * different types of hypervisor. + * + * Returns 0 if the migration succeeded, -1 upon error. + */ +int +virDomainMigrateToURI (virDomainPtr domain, + const char *duri, + unsigned long flags, + const char *dname, + unsigned long bandwidth) +{ + DEBUG("domain=%p, duri=%p, flags=%lu, dname=%s, bandwidth=%lu", + domain, NULLSTR(duri), flags, NULLSTR(dname), bandwidth); + + virResetLastError(); + + /* First checkout the source */ + if (!VIR_IS_CONNECTED_DOMAIN (domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return -1; + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (duri == NULL) { + virLibConnError (domain->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + /* XXX perhaps we should just automatically set this flag instead ? */ + if (flags & VIR_MIGRATE_PEER2PEER) { + if (VIR_DRV_SUPPORTS_FEATURE (domain->conn->driver, domain->conn, + VIR_DRV_FEATURE_MIGRATION_P2P)) { + if (virDomainMigrateP2P (domain, flags, dname, duri, bandwidth) < 0) + goto error; + } else { + virLibConnError (domain->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + } else { + virLibConnError (domain->conn, VIR_ERR_OPERATION_INVALID, + _("cannot migrate to a destination URI without peer2peer flag")); + goto error; + } + + return 0; + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); + return -1; +} + + /* * Not for public use. This function is part of the internal * implementation of migration in the remote case. diff --git a/src/libvirt_internal.h b/src/libvirt_internal.h index 5913798..5f1a7fe 100644 --- a/src/libvirt_internal.h +++ b/src/libvirt_internal.h @@ -17,6 +17,7 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * + * NB This file is (secret) ABI sensitive. Append only */ #ifndef __LIBVIRT_H_ @@ -31,6 +32,35 @@ int virStateReload(void); int virStateActive(void); #endif +/* Feature detection. This is a libvirt-private interface for determining + * what features are supported by the driver. + * + * The remote driver passes features through to the real driver at the + * remote end unmodified, except if you query a VIR_DRV_FEATURE_REMOTE* + * feature. + * + */ +enum { + /* Driver supports V1-style virDomainMigrate, ie. domainMigratePrepare/ + * domainMigratePerform/domainMigrateFinish. + */ + VIR_DRV_FEATURE_MIGRATION_V1 = 1, + + /* Driver is not local. */ + VIR_DRV_FEATURE_REMOTE = 2, + + /* Driver supports V2-style virDomainMigrate, ie. domainMigratePrepare2/ + * domainMigratePerform/domainMigrateFinish2. + */ + VIR_DRV_FEATURE_MIGRATION_V2 = 3, + + /* Driver supports peer-2-peer virDomainMigrate ie soruce host + * do all the prepare/perform/finish steps directly + */ + VIR_DRV_FEATURE_MIGRATION_P2P = 4, +}; + + int virDrvSupportsFeature (virConnectPtr conn, int feature); int virDomainMigratePrepare (virConnectPtr dconn, diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 888ea26..757e54c 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -323,6 +323,7 @@ LIBVIRT_0.7.1 { virStreamFinish; virStreamAbort; virStreamFree; + virDomainMigrateToURI; } LIBVIRT_0.7.0; # .... define new API here using predicted next version number .... diff --git a/src/virsh.c b/src/virsh.c index 4825f1c..cabbd3d 100644 --- a/src/virsh.c +++ b/src/virsh.c @@ -2463,6 +2463,8 @@ static const vshCmdInfo info_migrate[] = { static const vshCmdOptDef opts_migrate[] = { {"live", VSH_OT_BOOL, 0, gettext_noop("live migration")}, + {"p2p", VSH_OT_BOOL, 0, gettext_noop("peer-2-peer migration")}, + {"tunnelled", VSH_OT_BOOL, 0, gettext_noop("tunnelled migration")}, {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("domain name, id or uuid")}, {"desturi", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("connection URI of the destination host")}, {"migrateuri", VSH_OT_DATA, 0, gettext_noop("migration URI, usually can be omitted")}, @@ -2478,8 +2480,6 @@ cmdMigrate (vshControl *ctl, const vshCmd *cmd) const char *migrateuri; const char *dname; int flags = 0, found, ret = FALSE; - virConnectPtr dconn = NULL; - virDomainPtr ddom = NULL; if (!vshConnectionUsability (ctl, ctl->conn, TRUE)) return FALSE; @@ -2500,20 +2500,40 @@ cmdMigrate (vshControl *ctl, const vshCmd *cmd) if (vshCommandOptBool (cmd, "live")) flags |= VIR_MIGRATE_LIVE; - /* Temporarily connect to the destination host. */ - dconn = virConnectOpenAuth (desturi, virConnectAuthPtrDefault, 0); - if (!dconn) goto done; + if (vshCommandOptBool (cmd, "p2p")) + flags |= VIR_MIGRATE_PEER2PEER; - /* Migrate. */ - ddom = virDomainMigrate (dom, dconn, flags, dname, migrateuri, 0); - if (!ddom) goto done; + if (vshCommandOptBool (cmd, "tunnelled")) + flags |= VIR_MIGRATE_TUNNELLED; - ret = TRUE; + if ((flags & VIR_MIGRATE_PEER2PEER)) { + /* For peer2peer migration we only expect one URI, a libvirt URI */ + + if (migrateuri != NULL) { + vshError(ctl, FALSE, "%s", _("migrate: Unexpected migrateuri for peer2peer migration")); + goto done; + } + + if (virDomainMigrateToURI (dom, desturi, flags, dname, 0) == 0) + ret = TRUE; + } else { + /* For regular live migration, connect to the destination host directly. */ + virConnectPtr dconn = NULL; + virDomainPtr ddom = NULL; + + dconn = virConnectOpenAuth (desturi, virConnectAuthPtrDefault, 0); + if (!dconn) goto done; + + ddom = virDomainMigrate (dom, dconn, flags, dname, migrateuri, 0); + if (ddom) { + virDomainFree(ddom); + ret = TRUE; + } + virConnectClose (dconn); + } done: if (dom) virDomainFree (dom); - if (ddom) virDomainFree (ddom); - if (dconn) virConnectClose (dconn); return ret; } diff --git a/src/xen_unified.c b/src/xen_unified.c index dfa9ca5..954b187 100644 --- a/src/xen_unified.c +++ b/src/xen_unified.c @@ -455,8 +455,11 @@ static int xenUnifiedSupportsFeature (virConnectPtr conn ATTRIBUTE_UNUSED, int feature) { switch (feature) { - case VIR_DRV_FEATURE_MIGRATION_V1: return 1; - default: return 0; + case VIR_DRV_FEATURE_MIGRATION_V1: + case VIR_DRV_FEATURE_MIGRATION_P2P: + return 1; + default: + return 0; } } diff --git a/src/xend_internal.c b/src/xend_internal.c index 7f55116..da5c039 100644 --- a/src/xend_internal.c +++ b/src/xend_internal.c @@ -4400,6 +4400,12 @@ xenDaemonDomainMigratePerform (virDomainPtr domain, strcpy (live, "1"); flags &= ~VIR_MIGRATE_LIVE; } + /* Trivially support this in Xen, since XenD on dest is always + * ready to accept incoming migration */ + if ((flags & VIR_MIGRATE_PEER2PEER)) { + flags &= ~VIR_MIGRATE_PEER2PEER; + } + /* XXX we could easily do tunnelled migration too if we want to */ if (flags != 0) { virXendError (conn, VIR_ERR_NO_SUPPORT, "%s", _("xenDaemonDomainMigrate: unsupported flag")); -- 1.6.2.5 -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list