mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-07-06 18:05:45 +00:00
Tunnelled migration.
Implementation of tunnelled migration, using a Unix Domain Socket on the qemu backend. Note that this requires very new versions of qemu (0.10.7 at least) in order to get the appropriate bugfixes. Signed-off-by: Chris Lalancette <clalance@redhat.com>
This commit is contained in:
parent
3bd4c7ba16
commit
6e16575a37
@ -1,4 +1,5 @@
|
|||||||
^src/libvirt\.c$
|
^src/libvirt\.c$
|
||||||
|
^src/qemu/qemu_driver\.c$
|
||||||
^src/util/util\.c$
|
^src/util/util\.c$
|
||||||
^src/xen/xend_internal\.c$
|
^src/xen/xend_internal\.c$
|
||||||
^daemon/libvirtd.c$
|
^daemon/libvirtd.c$
|
||||||
|
@ -55,6 +55,7 @@
|
|||||||
#include "datatypes.h"
|
#include "datatypes.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "stream.h"
|
||||||
|
|
||||||
#define VIR_FROM_THIS VIR_FROM_REMOTE
|
#define VIR_FROM_THIS VIR_FROM_REMOTE
|
||||||
#define REMOTE_DEBUG(fmt, ...) DEBUG(fmt, __VA_ARGS__)
|
#define REMOTE_DEBUG(fmt, ...) DEBUG(fmt, __VA_ARGS__)
|
||||||
@ -1492,6 +1493,49 @@ remoteDispatchDomainMigrateFinish2 (struct qemud_server *server ATTRIBUTE_UNUSED
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
remoteDispatchDomainMigratePrepareTunnel(struct qemud_server *server ATTRIBUTE_UNUSED,
|
||||||
|
struct qemud_client *client,
|
||||||
|
virConnectPtr conn,
|
||||||
|
remote_message_header *hdr,
|
||||||
|
remote_error *rerr,
|
||||||
|
remote_domain_migrate_prepare_tunnel_args *args,
|
||||||
|
void *ret ATTRIBUTE_UNUSED)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
char *uri_in;
|
||||||
|
char *dname;
|
||||||
|
struct qemud_client_stream *stream;
|
||||||
|
CHECK_CONN (client);
|
||||||
|
|
||||||
|
uri_in = args->uri_in == NULL ? NULL : *args->uri_in;
|
||||||
|
dname = args->dname == NULL ? NULL : *args->dname;
|
||||||
|
|
||||||
|
stream = remoteCreateClientStream(conn, hdr);
|
||||||
|
if (!stream) {
|
||||||
|
remoteDispatchOOMError(rerr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = virDomainMigratePrepareTunnel(conn, stream->st, uri_in,
|
||||||
|
args->flags, dname, args->resource,
|
||||||
|
args->dom_xml);
|
||||||
|
if (r == -1) {
|
||||||
|
remoteFreeClientStream(client, stream);
|
||||||
|
remoteDispatchConnError(rerr, conn);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remoteAddClientStream(client, stream, 0) < 0) {
|
||||||
|
remoteDispatchConnError(rerr, conn);
|
||||||
|
virStreamAbort(stream->st);
|
||||||
|
remoteFreeClientStream(client, stream);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
remoteDispatchListDefinedDomains (struct qemud_server *server ATTRIBUTE_UNUSED,
|
remoteDispatchListDefinedDomains (struct qemud_server *server ATTRIBUTE_UNUSED,
|
||||||
struct qemud_client *client ATTRIBUTE_UNUSED,
|
struct qemud_client *client ATTRIBUTE_UNUSED,
|
||||||
|
@ -125,3 +125,4 @@
|
|||||||
remote_secret_get_value_args val_remote_secret_get_value_args;
|
remote_secret_get_value_args val_remote_secret_get_value_args;
|
||||||
remote_secret_undefine_args val_remote_secret_undefine_args;
|
remote_secret_undefine_args val_remote_secret_undefine_args;
|
||||||
remote_secret_lookup_by_usage_args val_remote_secret_lookup_by_usage_args;
|
remote_secret_lookup_by_usage_args val_remote_secret_lookup_by_usage_args;
|
||||||
|
remote_domain_migrate_prepare_tunnel_args val_remote_domain_migrate_prepare_tunnel_args;
|
||||||
|
@ -298,6 +298,14 @@ static int remoteDispatchDomainMigratePrepare2(
|
|||||||
remote_error *err,
|
remote_error *err,
|
||||||
remote_domain_migrate_prepare2_args *args,
|
remote_domain_migrate_prepare2_args *args,
|
||||||
remote_domain_migrate_prepare2_ret *ret);
|
remote_domain_migrate_prepare2_ret *ret);
|
||||||
|
static int remoteDispatchDomainMigratePrepareTunnel(
|
||||||
|
struct qemud_server *server,
|
||||||
|
struct qemud_client *client,
|
||||||
|
virConnectPtr conn,
|
||||||
|
remote_message_header *hdr,
|
||||||
|
remote_error *err,
|
||||||
|
remote_domain_migrate_prepare_tunnel_args *args,
|
||||||
|
void *ret);
|
||||||
static int remoteDispatchDomainPinVcpu(
|
static int remoteDispatchDomainPinVcpu(
|
||||||
struct qemud_server *server,
|
struct qemud_server *server,
|
||||||
struct qemud_client *client,
|
struct qemud_client *client,
|
||||||
|
@ -742,3 +742,8 @@
|
|||||||
.args_filter = (xdrproc_t) xdr_remote_secret_lookup_by_usage_args,
|
.args_filter = (xdrproc_t) xdr_remote_secret_lookup_by_usage_args,
|
||||||
.ret_filter = (xdrproc_t) xdr_remote_secret_lookup_by_usage_ret,
|
.ret_filter = (xdrproc_t) xdr_remote_secret_lookup_by_usage_ret,
|
||||||
},
|
},
|
||||||
|
{ /* DomainMigratePrepareTunnel => 148 */
|
||||||
|
.fn = (dispatch_fn) remoteDispatchDomainMigratePrepareTunnel,
|
||||||
|
.args_filter = (xdrproc_t) xdr_remote_domain_migrate_prepare_tunnel_args,
|
||||||
|
.ret_filter = (xdrproc_t) xdr_void,
|
||||||
|
},
|
||||||
|
@ -38,6 +38,7 @@ ignored_functions = {
|
|||||||
"virDomainMigratePerform": "private function for migration",
|
"virDomainMigratePerform": "private function for migration",
|
||||||
"virDomainMigratePrepare": "private function for migration",
|
"virDomainMigratePrepare": "private function for migration",
|
||||||
"virDomainMigratePrepare2": "private function for migration",
|
"virDomainMigratePrepare2": "private function for migration",
|
||||||
|
"virDomainMigratePrepareTunnel": "private function for tunnelled migration",
|
||||||
"virDrvSupportsFeature": "private function for remote access",
|
"virDrvSupportsFeature": "private function for remote access",
|
||||||
"DllMain": "specific function for Win32",
|
"DllMain": "specific function for Win32",
|
||||||
}
|
}
|
||||||
|
@ -337,6 +337,7 @@ typedef virDomainInterfaceStatsStruct *virDomainInterfaceStatsPtr;
|
|||||||
/* Domain migration flags. */
|
/* Domain migration flags. */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
VIR_MIGRATE_LIVE = 1, /* live migration */
|
VIR_MIGRATE_LIVE = 1, /* live migration */
|
||||||
|
VIR_MIGRATE_TUNNELLED = 2, /* tunnelled migration */
|
||||||
} virDomainMigrateFlags;
|
} virDomainMigrateFlags;
|
||||||
|
|
||||||
/* Domain migration. */
|
/* Domain migration. */
|
||||||
|
11
src/driver.h
11
src/driver.h
@ -347,6 +347,16 @@ typedef int
|
|||||||
(*virDrvNodeDeviceReset)
|
(*virDrvNodeDeviceReset)
|
||||||
(virNodeDevicePtr dev);
|
(virNodeDevicePtr dev);
|
||||||
|
|
||||||
|
typedef int
|
||||||
|
(*virDrvDomainMigratePrepareTunnel)
|
||||||
|
(virConnectPtr conn,
|
||||||
|
virStreamPtr st,
|
||||||
|
const char *uri_in,
|
||||||
|
unsigned long flags,
|
||||||
|
const char *dname,
|
||||||
|
unsigned long resource,
|
||||||
|
const char *dom_xml);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* _virDriver:
|
* _virDriver:
|
||||||
*
|
*
|
||||||
@ -427,6 +437,7 @@ struct _virDriver {
|
|||||||
virDrvNodeDeviceDettach nodeDeviceDettach;
|
virDrvNodeDeviceDettach nodeDeviceDettach;
|
||||||
virDrvNodeDeviceReAttach nodeDeviceReAttach;
|
virDrvNodeDeviceReAttach nodeDeviceReAttach;
|
||||||
virDrvNodeDeviceReset nodeDeviceReset;
|
virDrvNodeDeviceReset nodeDeviceReset;
|
||||||
|
virDrvDomainMigratePrepareTunnel domainMigratePrepareTunnel;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef int
|
typedef int
|
||||||
|
@ -3274,6 +3274,7 @@ static virDriver esxDriver = {
|
|||||||
NULL, /* nodeDeviceDettach */
|
NULL, /* nodeDeviceDettach */
|
||||||
NULL, /* nodeDeviceReAttach */
|
NULL, /* nodeDeviceReAttach */
|
||||||
NULL, /* nodeDeviceReset */
|
NULL, /* nodeDeviceReset */
|
||||||
|
NULL, /* domainMigratePrepareTunnel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
181
src/libvirt.c
181
src/libvirt.c
@ -3044,6 +3044,70 @@ virDomainMigrateVersion2 (virDomainPtr domain,
|
|||||||
return ddomain;
|
return ddomain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tunnelled migration has the following flow:
|
||||||
|
*
|
||||||
|
* virDomainMigrate(src, uri)
|
||||||
|
* - virDomainMigratePerform(src, uri)
|
||||||
|
* - dst = virConnectOpen(uri)
|
||||||
|
* - virDomainMigratePrepareTunnel(dst)
|
||||||
|
* - while (1)
|
||||||
|
* - virStreamSend(dst, data)
|
||||||
|
* - virDomainMigrateFinish(dst)
|
||||||
|
* - virConnectClose(dst)
|
||||||
|
*/
|
||||||
|
static virDomainPtr
|
||||||
|
virDomainMigrateTunnelled(virDomainPtr domain,
|
||||||
|
unsigned long flags,
|
||||||
|
const char *dname,
|
||||||
|
const char *uri,
|
||||||
|
unsigned long bandwidth)
|
||||||
|
{
|
||||||
|
virConnectPtr dconn;
|
||||||
|
virDomainPtr ddomain = NULL;
|
||||||
|
|
||||||
|
if (uri == NULL) {
|
||||||
|
/* if you are doing a secure migration, you *must* also pass a uri */
|
||||||
|
virLibConnError(domain->conn, VIR_ERR_INVALID_ARG,
|
||||||
|
_("requested TUNNELLED migration, but no URI passed"));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (domain->conn->flags & VIR_CONNECT_RO) {
|
||||||
|
virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FIXME: do we even need this check? In theory, V1 of the protocol
|
||||||
|
* should be able to do tunnelled migration as well
|
||||||
|
*/
|
||||||
|
if (!VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
|
||||||
|
VIR_DRV_FEATURE_MIGRATION_V2)) {
|
||||||
|
virLibConnError(domain->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Perform the migration. The driver isn't supposed to return
|
||||||
|
* until the migration is complete.
|
||||||
|
*/
|
||||||
|
if (domain->conn->driver->domainMigratePerform
|
||||||
|
(domain, NULL, 0, uri, flags, dname, bandwidth) == -1)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
dconn = virConnectOpen(uri);
|
||||||
|
if (dconn == NULL)
|
||||||
|
/* FIXME: this is pretty crappy; as far as we know, the migration has
|
||||||
|
* now succeeded, but we can't connect back to the other side
|
||||||
|
*/
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
ddomain = virDomainLookupByName(dconn, dname ? dname : domain->name);
|
||||||
|
|
||||||
|
virConnectClose(dconn);
|
||||||
|
|
||||||
|
return ddomain;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* virDomainMigrate:
|
* virDomainMigrate:
|
||||||
* @domain: a domain object
|
* @domain: a domain object
|
||||||
@ -3058,6 +3122,8 @@ virDomainMigrateVersion2 (virDomainPtr domain,
|
|||||||
*
|
*
|
||||||
* Flags may be one of more of the following:
|
* Flags may be one of more of the following:
|
||||||
* VIR_MIGRATE_LIVE Attempt a live migration.
|
* VIR_MIGRATE_LIVE Attempt a live migration.
|
||||||
|
* VIR_MIGRATE_TUNNELLED Attempt to do a migration tunnelled through the
|
||||||
|
* libvirt RPC mechanism
|
||||||
*
|
*
|
||||||
* If a hypervisor supports renaming domains during migration,
|
* If a hypervisor supports renaming domains during migration,
|
||||||
* then you may set the dname parameter to the new name (otherwise
|
* then you may set the dname parameter to the new name (otherwise
|
||||||
@ -3116,31 +3182,47 @@ virDomainMigrate (virDomainPtr domain,
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now checkout the destination */
|
if (flags & VIR_MIGRATE_TUNNELLED) {
|
||||||
if (!VIR_IS_CONNECT (dconn)) {
|
/* tunnelled migration is more or less a completely different migration
|
||||||
virLibConnError (domain->conn, VIR_ERR_INVALID_CONN, __FUNCTION__);
|
* protocol. dconn has to be NULL, uri has to be set, and the flow
|
||||||
goto error;
|
* of logic is completely different. Hence, here we split off from
|
||||||
}
|
* the main migration flow and use a separate function.
|
||||||
if (dconn->flags & VIR_CONNECT_RO) {
|
*/
|
||||||
/* NB, deliberately report error against source object, not dest */
|
if (dconn != NULL) {
|
||||||
virLibDomainError (domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__);
|
virLibConnError(domain->conn, VIR_ERR_INVALID_ARG,
|
||||||
goto error;
|
_("requested TUNNELLED migration, but non-NULL dconn"));
|
||||||
}
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
/* Check that migration is supported by both drivers. */
|
ddomain = virDomainMigrateTunnelled(domain, flags, dname, uri, bandwidth);
|
||||||
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 {
|
else {
|
||||||
virLibConnError (domain->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
|
/* Now checkout the destination */
|
||||||
goto error;
|
if (!VIR_IS_CONNECT(dconn)) {
|
||||||
|
virLibConnError(domain->conn, VIR_ERR_INVALID_CONN, __FUNCTION__);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if (dconn->flags & VIR_CONNECT_RO) {
|
||||||
|
/* NB, deliberately report error against source object, not dest */
|
||||||
|
virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__);
|
||||||
|
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)
|
if (ddomain == NULL)
|
||||||
@ -3398,6 +3480,59 @@ error:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Not for public use. This function is part of the internal
|
||||||
|
* implementation of migration in the remote case.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
virDomainMigratePrepareTunnel(virConnectPtr conn,
|
||||||
|
virStreamPtr st,
|
||||||
|
const char *uri_in,
|
||||||
|
unsigned long flags,
|
||||||
|
const char *dname,
|
||||||
|
unsigned long bandwidth,
|
||||||
|
const char *dom_xml)
|
||||||
|
|
||||||
|
{
|
||||||
|
VIR_DEBUG("conn=%p, stream=%p, uri_in=%s, flags=%lu, dname=%s, "
|
||||||
|
"bandwidth=%lu, dom_xml=%s", conn, st, uri_in, flags,
|
||||||
|
NULLSTR(dname), bandwidth, dom_xml);
|
||||||
|
|
||||||
|
virResetLastError();
|
||||||
|
|
||||||
|
if (!VIR_IS_CONNECT(conn)) {
|
||||||
|
virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn->flags & VIR_CONNECT_RO) {
|
||||||
|
virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn != st->conn) {
|
||||||
|
virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn->driver->domainMigratePrepareTunnel) {
|
||||||
|
int rv = conn->driver->domainMigratePrepareTunnel(conn, st, uri_in,
|
||||||
|
flags, dname,
|
||||||
|
bandwidth, dom_xml);
|
||||||
|
if (rv < 0)
|
||||||
|
goto error;
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
virLibConnError(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
|
||||||
|
|
||||||
|
error:
|
||||||
|
/* Copy to connection error object for back compatability */
|
||||||
|
virSetConnError(conn);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* virNodeGetInfo:
|
* virNodeGetInfo:
|
||||||
* @conn: pointer to the hypervisor connection
|
* @conn: pointer to the hypervisor connection
|
||||||
|
@ -70,6 +70,12 @@ virDomainPtr virDomainMigrateFinish2 (virConnectPtr dconn,
|
|||||||
const char *uri,
|
const char *uri,
|
||||||
unsigned long flags,
|
unsigned long flags,
|
||||||
int retcode);
|
int retcode);
|
||||||
|
int virDomainMigratePrepareTunnel(virConnectPtr conn,
|
||||||
|
virStreamPtr st,
|
||||||
|
const char *uri_in,
|
||||||
|
unsigned long flags,
|
||||||
|
const char *dname,
|
||||||
|
unsigned long resource,
|
||||||
|
const char *dom_xml);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -239,6 +239,7 @@ virDomainMigratePerform;
|
|||||||
virDomainMigrateFinish;
|
virDomainMigrateFinish;
|
||||||
virDomainMigratePrepare2;
|
virDomainMigratePrepare2;
|
||||||
virDomainMigrateFinish2;
|
virDomainMigrateFinish2;
|
||||||
|
virDomainMigratePrepareTunnel;
|
||||||
virRegisterDriver;
|
virRegisterDriver;
|
||||||
virRegisterInterfaceDriver;
|
virRegisterInterfaceDriver;
|
||||||
virRegisterNetworkDriver;
|
virRegisterNetworkDriver;
|
||||||
|
@ -2147,6 +2147,7 @@ static virDriver lxcDriver = {
|
|||||||
NULL, /* nodeDeviceDettach */
|
NULL, /* nodeDeviceDettach */
|
||||||
NULL, /* nodeDeviceReAttach */
|
NULL, /* nodeDeviceReAttach */
|
||||||
NULL, /* nodeDeviceReset */
|
NULL, /* nodeDeviceReset */
|
||||||
|
NULL, /* domainMigratePrepareTunnel */
|
||||||
};
|
};
|
||||||
|
|
||||||
static virStateDriver lxcStateDriver = {
|
static virStateDriver lxcStateDriver = {
|
||||||
|
@ -787,6 +787,7 @@ static virDriver oneDriver = {
|
|||||||
NULL, /* nodeDeviceDettach; */
|
NULL, /* nodeDeviceDettach; */
|
||||||
NULL, /* nodeDeviceReAttach; */
|
NULL, /* nodeDeviceReAttach; */
|
||||||
NULL, /* nodeDeviceReset; */
|
NULL, /* nodeDeviceReset; */
|
||||||
|
NULL, /* domainMigratePrepareTunnel */
|
||||||
};
|
};
|
||||||
|
|
||||||
static virStateDriver oneStateDriver = {
|
static virStateDriver oneStateDriver = {
|
||||||
|
@ -1432,6 +1432,7 @@ static virDriver openvzDriver = {
|
|||||||
NULL, /* nodeDeviceDettach */
|
NULL, /* nodeDeviceDettach */
|
||||||
NULL, /* nodeDeviceReAttach */
|
NULL, /* nodeDeviceReAttach */
|
||||||
NULL, /* nodeDeviceReset */
|
NULL, /* nodeDeviceReset */
|
||||||
|
NULL, /* domainMigratePrepareTunnel */
|
||||||
};
|
};
|
||||||
|
|
||||||
int openvzRegister(void) {
|
int openvzRegister(void) {
|
||||||
|
@ -1377,6 +1377,7 @@ virDriver phypDriver = {
|
|||||||
NULL, /* nodeDeviceDettach */
|
NULL, /* nodeDeviceDettach */
|
||||||
NULL, /* nodeDeviceReAttach */
|
NULL, /* nodeDeviceReAttach */
|
||||||
NULL, /* nodeDeviceReset */
|
NULL, /* nodeDeviceReset */
|
||||||
|
NULL, /* domainMigratePrepareTunnel */
|
||||||
};
|
};
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -71,6 +71,7 @@
|
|||||||
#include "hostusb.h"
|
#include "hostusb.h"
|
||||||
#include "security/security_driver.h"
|
#include "security/security_driver.h"
|
||||||
#include "cgroup.h"
|
#include "cgroup.h"
|
||||||
|
#include "libvirt_internal.h"
|
||||||
|
|
||||||
|
|
||||||
#define VIR_FROM_THIS VIR_FROM_QEMU
|
#define VIR_FROM_THIS VIR_FROM_QEMU
|
||||||
@ -5796,6 +5797,432 @@ static void qemuDomainEventQueue(struct qemud_driver *driver,
|
|||||||
|
|
||||||
/* Migration support. */
|
/* Migration support. */
|
||||||
|
|
||||||
|
/* Tunnelled migration stream support */
|
||||||
|
struct qemuStreamMigFile {
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
int watch;
|
||||||
|
unsigned int cbRemoved;
|
||||||
|
unsigned int dispatching;
|
||||||
|
virStreamEventCallback cb;
|
||||||
|
void *opaque;
|
||||||
|
virFreeCallback ff;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int qemuStreamMigRemoveCallback(virStreamPtr stream)
|
||||||
|
{
|
||||||
|
struct qemud_driver *driver = stream->conn->privateData;
|
||||||
|
struct qemuStreamMigFile *qemust = stream->privateData;
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
|
if (!qemust) {
|
||||||
|
qemudReportError(stream->conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"%s", _("stream is not open"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
qemuDriverLock(driver);
|
||||||
|
if (qemust->watch == 0) {
|
||||||
|
qemudReportError(stream->conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"%s", _("stream does not have a callback registered"));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
virEventRemoveHandle(qemust->watch);
|
||||||
|
if (qemust->dispatching)
|
||||||
|
qemust->cbRemoved = 1;
|
||||||
|
else if (qemust->ff)
|
||||||
|
(qemust->ff)(qemust->opaque);
|
||||||
|
|
||||||
|
qemust->watch = 0;
|
||||||
|
qemust->ff = NULL;
|
||||||
|
qemust->cb = NULL;
|
||||||
|
qemust->opaque = NULL;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
qemuDriverUnlock(driver);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int qemuStreamMigUpdateCallback(virStreamPtr stream, int events)
|
||||||
|
{
|
||||||
|
struct qemud_driver *driver = stream->conn->privateData;
|
||||||
|
struct qemuStreamMigFile *qemust = stream->privateData;
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
|
if (!qemust) {
|
||||||
|
qemudReportError(stream->conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"%s", _("stream is not open"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
qemuDriverLock(driver);
|
||||||
|
if (qemust->watch == 0) {
|
||||||
|
qemudReportError(stream->conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"%s", _("stream does not have a callback registered"));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
virEventUpdateHandle(qemust->watch, events);
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
qemuDriverUnlock(driver);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qemuStreamMigEvent(int watch ATTRIBUTE_UNUSED,
|
||||||
|
int fd ATTRIBUTE_UNUSED,
|
||||||
|
int events,
|
||||||
|
void *opaque)
|
||||||
|
{
|
||||||
|
virStreamPtr stream = opaque;
|
||||||
|
struct qemud_driver *driver = stream->conn->privateData;
|
||||||
|
struct qemuStreamMigFile *qemust = stream->privateData;
|
||||||
|
virStreamEventCallback cb;
|
||||||
|
void *cbopaque;
|
||||||
|
virFreeCallback ff;
|
||||||
|
|
||||||
|
qemuDriverLock(driver);
|
||||||
|
if (!qemust || !qemust->cb) {
|
||||||
|
qemuDriverUnlock(driver);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cb = qemust->cb;
|
||||||
|
cbopaque = qemust->opaque;
|
||||||
|
ff = qemust->ff;
|
||||||
|
qemust->dispatching = 1;
|
||||||
|
qemuDriverUnlock(driver);
|
||||||
|
|
||||||
|
cb(stream, events, cbopaque);
|
||||||
|
|
||||||
|
qemuDriverLock(driver);
|
||||||
|
qemust->dispatching = 0;
|
||||||
|
if (qemust->cbRemoved && ff)
|
||||||
|
(ff)(cbopaque);
|
||||||
|
qemuDriverUnlock(driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
qemuStreamMigAddCallback(virStreamPtr st,
|
||||||
|
int events,
|
||||||
|
virStreamEventCallback cb,
|
||||||
|
void *opaque,
|
||||||
|
virFreeCallback ff)
|
||||||
|
{
|
||||||
|
struct qemud_driver *driver = st->conn->privateData;
|
||||||
|
struct qemuStreamMigFile *qemust = st->privateData;
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
|
if (!qemust) {
|
||||||
|
qemudReportError(st->conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"%s", _("stream is not open"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
qemuDriverLock(driver);
|
||||||
|
if (qemust->watch != 0) {
|
||||||
|
qemudReportError(st->conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"%s", _("stream already has a callback registered"));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((qemust->watch = virEventAddHandle(qemust->fd,
|
||||||
|
events,
|
||||||
|
qemuStreamMigEvent,
|
||||||
|
st,
|
||||||
|
NULL)) < 0) {
|
||||||
|
qemudReportError(st->conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"%s", _("cannot register file watch on stream"));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
qemust->cbRemoved = 0;
|
||||||
|
qemust->cb = cb;
|
||||||
|
qemust->opaque = opaque;
|
||||||
|
qemust->ff = ff;
|
||||||
|
virStreamRef(st);
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
qemuDriverUnlock(driver);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qemuStreamMigFree(struct qemuStreamMigFile *qemust)
|
||||||
|
{
|
||||||
|
if (qemust->fd != -1)
|
||||||
|
close(qemust->fd);
|
||||||
|
VIR_FREE(qemust);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct qemuStreamMigFile *qemuStreamMigOpen(virStreamPtr st,
|
||||||
|
const char *unixfile)
|
||||||
|
{
|
||||||
|
struct qemuStreamMigFile *qemust = NULL;
|
||||||
|
struct sockaddr_un sa_qemu;
|
||||||
|
int i = 0;
|
||||||
|
int timeout = 3;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (VIR_ALLOC(qemust) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
qemust->fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (qemust->fd < 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
memset(&sa_qemu, 0, sizeof(sa_qemu));
|
||||||
|
sa_qemu.sun_family = AF_UNIX;
|
||||||
|
if (virStrcpy(sa_qemu.sun_path, unixfile, sizeof(sa_qemu.sun_path)) == NULL)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
do {
|
||||||
|
ret = connect(qemust->fd, (struct sockaddr *)&sa_qemu, sizeof(sa_qemu));
|
||||||
|
if (ret == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (errno == ENOENT || errno == ECONNREFUSED) {
|
||||||
|
/* ENOENT : Socket may not have shown up yet
|
||||||
|
* ECONNREFUSED : Leftover socket hasn't been removed yet */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
goto cleanup;
|
||||||
|
} while ((++i <= timeout*5) && (usleep(.2 * 1000000) <= 0));
|
||||||
|
|
||||||
|
if ((st->flags & VIR_STREAM_NONBLOCK) && virSetNonBlock(qemust->fd) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
return qemust;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
qemuStreamMigFree(qemust);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
qemuStreamMigClose(virStreamPtr st)
|
||||||
|
{
|
||||||
|
struct qemud_driver *driver = st->conn->privateData;
|
||||||
|
struct qemuStreamMigFile *qemust = st->privateData;
|
||||||
|
|
||||||
|
if (!qemust)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
qemuDriverLock(driver);
|
||||||
|
|
||||||
|
qemuStreamMigFree(qemust);
|
||||||
|
|
||||||
|
st->privateData = NULL;
|
||||||
|
|
||||||
|
qemuDriverUnlock(driver);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int qemuStreamMigWrite(virStreamPtr st, const char *bytes, size_t nbytes)
|
||||||
|
{
|
||||||
|
struct qemud_driver *driver = st->conn->privateData;
|
||||||
|
struct qemuStreamMigFile *qemust = st->privateData;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!qemust) {
|
||||||
|
qemudReportError(st->conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"%s", _("stream is not open"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
qemuDriverLock(driver);
|
||||||
|
|
||||||
|
retry:
|
||||||
|
ret = write(qemust->fd, bytes, nbytes);
|
||||||
|
if (ret < 0) {
|
||||||
|
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||||
|
ret = -2;
|
||||||
|
} else if (errno == EINTR) {
|
||||||
|
goto retry;
|
||||||
|
} else {
|
||||||
|
ret = -1;
|
||||||
|
virReportSystemError(st->conn, errno, "%s",
|
||||||
|
_("cannot write to stream"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qemuDriverUnlock(driver);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static virStreamDriver qemuStreamMigDrv = {
|
||||||
|
.streamSend = qemuStreamMigWrite,
|
||||||
|
.streamFinish = qemuStreamMigClose,
|
||||||
|
.streamAbort = qemuStreamMigClose,
|
||||||
|
.streamAddCallback = qemuStreamMigAddCallback,
|
||||||
|
.streamUpdateCallback = qemuStreamMigUpdateCallback,
|
||||||
|
.streamRemoveCallback = qemuStreamMigRemoveCallback
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Prepare is the first step, and it runs on the destination host.
|
||||||
|
*
|
||||||
|
* This version starts an empty VM listening on a localhost TCP port, and
|
||||||
|
* sets up the corresponding virStream to handle the incoming data.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
qemudDomainMigratePrepareTunnel(virConnectPtr dconn,
|
||||||
|
virStreamPtr st,
|
||||||
|
const char *uri_in,
|
||||||
|
unsigned long flags,
|
||||||
|
const char *dname,
|
||||||
|
unsigned long resource ATTRIBUTE_UNUSED,
|
||||||
|
const char *dom_xml)
|
||||||
|
{
|
||||||
|
struct qemud_driver *driver = dconn->privateData;
|
||||||
|
virDomainDefPtr def = NULL;
|
||||||
|
virDomainObjPtr vm = NULL;
|
||||||
|
char *migrateFrom;
|
||||||
|
virDomainEventPtr event = NULL;
|
||||||
|
int ret = -1;
|
||||||
|
int internalret;
|
||||||
|
char *unixfile = NULL;
|
||||||
|
unsigned int qemuCmdFlags;
|
||||||
|
struct qemuStreamMigFile *qemust = NULL;
|
||||||
|
|
||||||
|
qemuDriverLock(driver);
|
||||||
|
if (!dom_xml) {
|
||||||
|
qemudReportError(dconn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"%s", _("no domain XML passed"));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if (!uri_in) {
|
||||||
|
qemudReportError(dconn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"%s", _("no URI passed"));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if (!(flags & VIR_MIGRATE_TUNNELLED)) {
|
||||||
|
qemudReportError(dconn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"%s", _("PrepareTunnel called but no TUNNELLED flag set"));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if (st == NULL) {
|
||||||
|
qemudReportError(dconn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"%s", _("tunnelled migration requested but NULL stream passed"));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse the domain XML. */
|
||||||
|
if (!(def = virDomainDefParseString(dconn, driver->caps, dom_xml,
|
||||||
|
VIR_DOMAIN_XML_INACTIVE))) {
|
||||||
|
qemudReportError(dconn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
|
||||||
|
"%s", _("failed to parse XML"));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Target domain name, maybe renamed. */
|
||||||
|
dname = dname ? dname : def->name;
|
||||||
|
|
||||||
|
/* Ensure the name and UUID don't already exist in an active VM */
|
||||||
|
vm = virDomainFindByUUID(&driver->domains, def->uuid);
|
||||||
|
|
||||||
|
if (!vm) vm = virDomainFindByName(&driver->domains, dname);
|
||||||
|
if (vm) {
|
||||||
|
if (virDomainIsActive(vm)) {
|
||||||
|
qemudReportError(dconn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
|
||||||
|
_("domain with the same name or UUID already exists as '%s'"),
|
||||||
|
vm->def->name);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
virDomainObjUnlock(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(vm = virDomainAssignDef(dconn,
|
||||||
|
&driver->domains,
|
||||||
|
def))) {
|
||||||
|
qemudReportError(dconn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
|
||||||
|
"%s", _("failed to assign new VM"));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
def = NULL;
|
||||||
|
|
||||||
|
/* Domain starts inactive, even if the domain XML had an id field. */
|
||||||
|
vm->def->id = -1;
|
||||||
|
|
||||||
|
if (virAsprintf(&unixfile, "%s/qemu.tunnelmigrate.dest.%s",
|
||||||
|
driver->stateDir, vm->def->name) < 0) {
|
||||||
|
virReportOOMError (dconn);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
unlink(unixfile);
|
||||||
|
|
||||||
|
/* check that this qemu version supports the interactive exec */
|
||||||
|
if (qemudExtractVersionInfo(vm->def->emulator, NULL, &qemuCmdFlags) < 0) {
|
||||||
|
qemudReportError(dconn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
_("Cannot determine QEMU argv syntax %s"),
|
||||||
|
vm->def->emulator);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if (qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_UNIX)
|
||||||
|
internalret = virAsprintf(&migrateFrom, "unix:%s", unixfile);
|
||||||
|
else if (qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC)
|
||||||
|
internalret = virAsprintf(&migrateFrom, "exec:nc -U -l %s", unixfile);
|
||||||
|
else {
|
||||||
|
qemudReportError(dconn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
|
||||||
|
"%s", _("Destination qemu is too old to support tunnelled migration"));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if (internalret < 0) {
|
||||||
|
virReportOOMError(dconn);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
/* Start the QEMU daemon, with the same command-line arguments plus
|
||||||
|
* -incoming unix:/path/to/file or exec:nc -U /path/to/file
|
||||||
|
*/
|
||||||
|
internalret = qemudStartVMDaemon(dconn, driver, vm, migrateFrom, -1);
|
||||||
|
VIR_FREE(migrateFrom);
|
||||||
|
if (internalret < 0) {
|
||||||
|
/* Note that we don't set an error here because qemudStartVMDaemon
|
||||||
|
* should have already done that.
|
||||||
|
*/
|
||||||
|
if (!vm->persistent) {
|
||||||
|
virDomainRemoveInactive(&driver->domains, vm);
|
||||||
|
vm = NULL;
|
||||||
|
}
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
qemust = qemuStreamMigOpen(st, unixfile);
|
||||||
|
if (qemust == NULL) {
|
||||||
|
qemudShutdownVMDaemon(NULL, driver, vm);
|
||||||
|
virReportSystemError(dconn, errno,
|
||||||
|
_("cannot open unix socket '%s' for tunnelled migration"),
|
||||||
|
unixfile);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
st->driver = &qemuStreamMigDrv;
|
||||||
|
st->privateData = qemust;
|
||||||
|
|
||||||
|
event = virDomainEventNewFromObj(vm,
|
||||||
|
VIR_DOMAIN_EVENT_STARTED,
|
||||||
|
VIR_DOMAIN_EVENT_STARTED_MIGRATED);
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
virDomainDefFree(def);
|
||||||
|
unlink(unixfile);
|
||||||
|
VIR_FREE(unixfile);
|
||||||
|
if (vm)
|
||||||
|
virDomainObjUnlock(vm);
|
||||||
|
if (event)
|
||||||
|
qemuDomainEventQueue(driver, event);
|
||||||
|
qemuDriverUnlock(driver);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* Prepare is the first step, and it runs on the destination host.
|
/* Prepare is the first step, and it runs on the destination host.
|
||||||
*
|
*
|
||||||
* This starts an empty VM listening on a TCP port.
|
* This starts an empty VM listening on a TCP port.
|
||||||
@ -5806,7 +6233,7 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn,
|
|||||||
int *cookielen ATTRIBUTE_UNUSED,
|
int *cookielen ATTRIBUTE_UNUSED,
|
||||||
const char *uri_in,
|
const char *uri_in,
|
||||||
char **uri_out,
|
char **uri_out,
|
||||||
unsigned long flags ATTRIBUTE_UNUSED,
|
unsigned long flags,
|
||||||
const char *dname,
|
const char *dname,
|
||||||
unsigned long resource ATTRIBUTE_UNUSED,
|
unsigned long resource ATTRIBUTE_UNUSED,
|
||||||
const char *dom_xml)
|
const char *dom_xml)
|
||||||
@ -5826,6 +6253,15 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn,
|
|||||||
*uri_out = NULL;
|
*uri_out = NULL;
|
||||||
|
|
||||||
qemuDriverLock(driver);
|
qemuDriverLock(driver);
|
||||||
|
if (flags & VIR_MIGRATE_TUNNELLED) {
|
||||||
|
/* this is a logical error; we never should have gotten here with
|
||||||
|
* VIR_MIGRATE_TUNNELLED set
|
||||||
|
*/
|
||||||
|
qemudReportError(dconn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"%s", _("Tunnelled migration requested but invalid RPC method called"));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
if (!dom_xml) {
|
if (!dom_xml) {
|
||||||
qemudReportError (dconn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
qemudReportError (dconn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
"%s", _("no domain XML passed"));
|
"%s", _("no domain XML passed"));
|
||||||
@ -5967,6 +6403,209 @@ cleanup:
|
|||||||
qemuDomainEventQueue(driver, event);
|
qemuDomainEventQueue(driver, event);
|
||||||
qemuDriverUnlock(driver);
|
qemuDriverUnlock(driver);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static int doTunnelMigrate(virDomainPtr dom,
|
||||||
|
virDomainObjPtr vm,
|
||||||
|
const char *uri,
|
||||||
|
unsigned long flags,
|
||||||
|
const char *dname,
|
||||||
|
unsigned long resource)
|
||||||
|
{
|
||||||
|
struct qemud_driver *driver = dom->conn->privateData;
|
||||||
|
int client_sock, qemu_sock;
|
||||||
|
struct sockaddr_un sa_qemu, sa_client;
|
||||||
|
socklen_t addrlen;
|
||||||
|
virConnectPtr dconn;
|
||||||
|
virDomainPtr ddomain;
|
||||||
|
int retval = -1;
|
||||||
|
ssize_t bytes;
|
||||||
|
char buffer[65536];
|
||||||
|
virStreamPtr st;
|
||||||
|
char *dom_xml = NULL;
|
||||||
|
char *unixfile = NULL;
|
||||||
|
int internalret;
|
||||||
|
unsigned int qemuCmdFlags;
|
||||||
|
int status;
|
||||||
|
unsigned long long transferred, remaining, total;
|
||||||
|
|
||||||
|
/* the order of operations is important here; we make sure the
|
||||||
|
* destination side is completely setup before we touch the source
|
||||||
|
*/
|
||||||
|
|
||||||
|
dconn = virConnectOpen(uri);
|
||||||
|
if (dconn == NULL) {
|
||||||
|
qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
|
||||||
|
_("Failed to connect to remote libvirt URI %s"), uri);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!VIR_DRV_SUPPORTS_FEATURE(dconn->driver, dconn,
|
||||||
|
VIR_DRV_FEATURE_MIGRATION_V2)) {
|
||||||
|
qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, "%s",
|
||||||
|
_("Destination libvirt does not support required migration protocol 2"));
|
||||||
|
goto close_dconn;
|
||||||
|
}
|
||||||
|
|
||||||
|
st = virStreamNew(dconn, 0);
|
||||||
|
if (st == NULL)
|
||||||
|
/* virStreamNew only fails on OOM, and it reports the error itself */
|
||||||
|
goto close_dconn;
|
||||||
|
|
||||||
|
dom_xml = virDomainDefFormat(dom->conn, vm->def, VIR_DOMAIN_XML_SECURE);
|
||||||
|
if (!dom_xml) {
|
||||||
|
qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
|
||||||
|
"%s", _("failed to get domain xml"));
|
||||||
|
goto close_stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
internalret = dconn->driver->domainMigratePrepareTunnel(dconn, st, uri,
|
||||||
|
flags, dname,
|
||||||
|
resource, dom_xml);
|
||||||
|
VIR_FREE(dom_xml);
|
||||||
|
if (internalret < 0)
|
||||||
|
/* domainMigratePrepareTunnel sets the error for us */
|
||||||
|
goto close_stream;
|
||||||
|
|
||||||
|
if (virAsprintf(&unixfile, "%s/qemu.tunnelmigrate.src.%s",
|
||||||
|
driver->stateDir, vm->def->name) < 0) {
|
||||||
|
virReportOOMError(dom->conn);
|
||||||
|
goto finish_migrate;
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (qemu_sock < 0) {
|
||||||
|
virReportSystemError(dom->conn, errno, "%s",
|
||||||
|
_("cannot open tunnelled migration socket"));
|
||||||
|
goto free_unix_path;
|
||||||
|
}
|
||||||
|
memset(&sa_qemu, 0, sizeof(sa_qemu));
|
||||||
|
sa_qemu.sun_family = AF_UNIX;
|
||||||
|
if (virStrcpy(sa_qemu.sun_path, unixfile,
|
||||||
|
sizeof(sa_qemu.sun_path)) == NULL) {
|
||||||
|
qemudReportError(dom->conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
_("Unix socket '%s' too big for destination"),
|
||||||
|
unixfile);
|
||||||
|
goto close_qemu_sock;
|
||||||
|
}
|
||||||
|
unlink(unixfile);
|
||||||
|
if (bind(qemu_sock, (struct sockaddr *)&sa_qemu, sizeof(sa_qemu)) < 0) {
|
||||||
|
virReportSystemError(dom->conn, errno,
|
||||||
|
_("Cannot bind to unix socket '%s' for tunnelled migration"),
|
||||||
|
unixfile);
|
||||||
|
goto close_qemu_sock;
|
||||||
|
}
|
||||||
|
if (listen(qemu_sock, 1) < 0) {
|
||||||
|
virReportSystemError(dom->conn, errno,
|
||||||
|
_("Cannot listen on unix socket '%s' for tunnelled migration"),
|
||||||
|
unixfile);
|
||||||
|
goto close_qemu_sock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check that this qemu version supports the unix migration */
|
||||||
|
if (qemudExtractVersionInfo(vm->def->emulator, NULL, &qemuCmdFlags) < 0) {
|
||||||
|
qemudReportError(dom->conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
_("Cannot extract Qemu version from '%s'"),
|
||||||
|
vm->def->emulator);
|
||||||
|
goto close_qemu_sock;
|
||||||
|
}
|
||||||
|
if (qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_UNIX)
|
||||||
|
internalret = qemuMonitorMigrateToUnix(vm, 1, unixfile);
|
||||||
|
else if (qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC) {
|
||||||
|
const char *args[] = { "nc", "-U", unixfile, NULL };
|
||||||
|
internalret = qemuMonitorMigrateToCommand(vm, 1, args, "/dev/null");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qemudReportError(dom->conn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
|
||||||
|
"%s", _("Source qemu is too old to support tunnelled migration"));
|
||||||
|
goto close_qemu_sock;
|
||||||
|
}
|
||||||
|
if (internalret < 0) {
|
||||||
|
qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
|
||||||
|
"%s", _("tunnelled migration monitor command failed"));
|
||||||
|
goto close_qemu_sock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* it is also possible that the migrate didn't fail initially, but
|
||||||
|
* rather failed later on. Check the output of "info migrate"
|
||||||
|
*/
|
||||||
|
if (qemuMonitorGetMigrationStatus(vm, &status,
|
||||||
|
&transferred,
|
||||||
|
&remaining,
|
||||||
|
&total) < 0) {
|
||||||
|
goto qemu_cancel_migration;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status == QEMU_MONITOR_MIGRATION_STATUS_ERROR) {
|
||||||
|
qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
|
||||||
|
"%s",_("migrate failed"));
|
||||||
|
goto qemu_cancel_migration;
|
||||||
|
}
|
||||||
|
|
||||||
|
addrlen = sizeof(sa_client);
|
||||||
|
while ((client_sock = accept(qemu_sock, (struct sockaddr *)&sa_client, &addrlen)) < 0) {
|
||||||
|
if (errno == EAGAIN || errno == EINTR)
|
||||||
|
continue;
|
||||||
|
virReportSystemError(dom->conn, errno, "%s",
|
||||||
|
_("tunnelled migration failed to accept from qemu"));
|
||||||
|
goto qemu_cancel_migration;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
bytes = saferead(client_sock, buffer, sizeof(buffer));
|
||||||
|
if (bytes < 0) {
|
||||||
|
virReportSystemError(dconn, errno, "%s",
|
||||||
|
_("tunnelled migration failed to read from qemu"));
|
||||||
|
goto close_client_sock;
|
||||||
|
}
|
||||||
|
else if (bytes == 0)
|
||||||
|
/* EOF; get out of here */
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (virStreamSend(st, buffer, bytes) < 0) {
|
||||||
|
qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
|
||||||
|
_("Failed to write migration data to remote libvirtd"));
|
||||||
|
virStreamAbort(st);
|
||||||
|
goto close_client_sock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (virStreamFinish(st) < 0)
|
||||||
|
/* virStreamFinish set the error for us */
|
||||||
|
goto close_client_sock;
|
||||||
|
|
||||||
|
retval = 0;
|
||||||
|
|
||||||
|
close_client_sock:
|
||||||
|
close(client_sock);
|
||||||
|
|
||||||
|
qemu_cancel_migration:
|
||||||
|
if (retval != 0)
|
||||||
|
qemuMonitorMigrateCancel(vm);
|
||||||
|
|
||||||
|
close_qemu_sock:
|
||||||
|
close(qemu_sock);
|
||||||
|
|
||||||
|
free_unix_path:
|
||||||
|
unlink(unixfile);
|
||||||
|
VIR_FREE(unixfile);
|
||||||
|
|
||||||
|
finish_migrate:
|
||||||
|
dname = dname ? dname : dom->name;
|
||||||
|
ddomain = dconn->driver->domainMigrateFinish2
|
||||||
|
(dconn, dname, NULL, 0, uri, flags, retval);
|
||||||
|
if (ddomain)
|
||||||
|
virUnrefDomain(ddomain);
|
||||||
|
|
||||||
|
close_stream:
|
||||||
|
/* don't call virStreamFree(), because that resets any pending errors */
|
||||||
|
virUnrefStream(st);
|
||||||
|
|
||||||
|
close_dconn:
|
||||||
|
/* don't call virConnectClose(), because that resets any pending errors */
|
||||||
|
virUnrefConnect(dconn);
|
||||||
|
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Perform is the second step, and it runs on the source host. */
|
/* Perform is the second step, and it runs on the source host. */
|
||||||
@ -6022,42 +6661,49 @@ qemudDomainMigratePerform (virDomainPtr dom,
|
|||||||
qemuMonitorSetMigrationSpeed(vm, resource) < 0)
|
qemuMonitorSetMigrationSpeed(vm, resource) < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
/* Issue the migrate command. */
|
if (!(flags & VIR_MIGRATE_TUNNELLED)) {
|
||||||
if (STRPREFIX(uri, "tcp:") && !STRPREFIX(uri, "tcp://")) {
|
/* Issue the migrate command. */
|
||||||
/* HACK: source host generates bogus URIs, so fix them up */
|
if (STRPREFIX(uri, "tcp:") && !STRPREFIX(uri, "tcp://")) {
|
||||||
char *tmpuri;
|
/* HACK: source host generates bogus URIs, so fix them up */
|
||||||
if (virAsprintf(&tmpuri, "tcp://%s", uri + strlen("tcp:")) < 0) {
|
char *tmpuri;
|
||||||
virReportOOMError(dom->conn);
|
if (virAsprintf(&tmpuri, "tcp://%s", uri + strlen("tcp:")) < 0) {
|
||||||
|
virReportOOMError(dom->conn);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
uribits = xmlParseURI(tmpuri);
|
||||||
|
VIR_FREE(tmpuri);
|
||||||
|
} else {
|
||||||
|
uribits = xmlParseURI(uri);
|
||||||
|
}
|
||||||
|
if (!uribits) {
|
||||||
|
qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
_("cannot parse URI %s"), uri);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
uribits = xmlParseURI(tmpuri);
|
|
||||||
VIR_FREE(tmpuri);
|
|
||||||
} else {
|
|
||||||
uribits = xmlParseURI(uri);
|
|
||||||
}
|
|
||||||
if (!uribits) {
|
|
||||||
qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
||||||
_("cannot parse URI %s"), uri);
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qemuMonitorMigrateToHost(vm, 0, uribits->server, uribits->port) < 0)
|
if (qemuMonitorMigrateToHost(vm, 0, uribits->server, uribits->port) < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
/* it is also possible that the migrate didn't fail initially, but
|
/* it is also possible that the migrate didn't fail initially, but
|
||||||
* rather failed later on. Check the output of "info migrate"
|
* rather failed later on. Check the output of "info migrate"
|
||||||
*/
|
*/
|
||||||
if (qemuMonitorGetMigrationStatus(vm, &status,
|
if (qemuMonitorGetMigrationStatus(vm, &status,
|
||||||
&transferred,
|
&transferred,
|
||||||
&remaining,
|
&remaining,
|
||||||
&total) < 0) {
|
&total) < 0) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status != QEMU_MONITOR_MIGRATION_STATUS_COMPLETED) {
|
||||||
|
qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
|
||||||
|
"%s", _("migrate did not successfully complete"));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
if (status != QEMU_MONITOR_MIGRATION_STATUS_COMPLETED) {
|
if (doTunnelMigrate(dom, vm, uri, flags, dname, resource) < 0)
|
||||||
qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
|
/* doTunnelMigrate already set the error, so just get out */
|
||||||
"%s", _("migrate did not successfully complete"));
|
goto cleanup;
|
||||||
goto cleanup;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clean up the source domain. */
|
/* Clean up the source domain. */
|
||||||
@ -6357,6 +7003,7 @@ static virDriver qemuDriver = {
|
|||||||
qemudNodeDeviceDettach, /* nodeDeviceDettach */
|
qemudNodeDeviceDettach, /* nodeDeviceDettach */
|
||||||
qemudNodeDeviceReAttach, /* nodeDeviceReAttach */
|
qemudNodeDeviceReAttach, /* nodeDeviceReAttach */
|
||||||
qemudNodeDeviceReset, /* nodeDeviceReset */
|
qemudNodeDeviceReset, /* nodeDeviceReset */
|
||||||
|
qemudDomainMigratePrepareTunnel, /* domainMigratePrepareTunnel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -6698,7 +6698,6 @@ done:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
static struct private_stream_data *
|
static struct private_stream_data *
|
||||||
remoteStreamOpen(virStreamPtr st,
|
remoteStreamOpen(virStreamPtr st,
|
||||||
int output ATTRIBUTE_UNUSED,
|
int output ATTRIBUTE_UNUSED,
|
||||||
@ -7049,9 +7048,51 @@ static virStreamDriver remoteStreamDrv = {
|
|||||||
.streamUpdateCallback = remoteStreamEventUpdateCallback,
|
.streamUpdateCallback = remoteStreamEventUpdateCallback,
|
||||||
.streamRemoveCallback = remoteStreamEventRemoveCallback,
|
.streamRemoveCallback = remoteStreamEventRemoveCallback,
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
remoteDomainMigratePrepareTunnel(virConnectPtr conn,
|
||||||
|
virStreamPtr st,
|
||||||
|
const char *uri_in,
|
||||||
|
unsigned long flags,
|
||||||
|
const char *dname,
|
||||||
|
unsigned long resource,
|
||||||
|
const char *dom_xml)
|
||||||
|
{
|
||||||
|
struct private_data *priv = conn->privateData;
|
||||||
|
struct private_stream_data *privst = NULL;
|
||||||
|
int rv = -1;
|
||||||
|
remote_domain_migrate_prepare_tunnel_args args;
|
||||||
|
|
||||||
|
remoteDriverLock(priv);
|
||||||
|
|
||||||
|
if (!(privst = remoteStreamOpen(st, 1, REMOTE_PROC_DOMAIN_MIGRATE_PREPARE_TUNNEL, priv->counter)))
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
st->driver = &remoteStreamDrv;
|
||||||
|
st->privateData = privst;
|
||||||
|
|
||||||
|
args.uri_in = uri_in == NULL ? NULL : (char **) &uri_in;
|
||||||
|
args.flags = flags;
|
||||||
|
args.dname = dname == NULL ? NULL : (char **) &dname;
|
||||||
|
args.resource = resource;
|
||||||
|
args.dom_xml = (char *) dom_xml;
|
||||||
|
|
||||||
|
if (call(conn, priv, 0, REMOTE_PROC_DOMAIN_MIGRATE_PREPARE_TUNNEL,
|
||||||
|
(xdrproc_t) xdr_remote_domain_migrate_prepare_tunnel_args, (char *) &args,
|
||||||
|
(xdrproc_t) xdr_void, NULL) == -1) {
|
||||||
|
remoteStreamRelease(st);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = 0;
|
||||||
|
|
||||||
|
done:
|
||||||
|
remoteDriverUnlock(priv);
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
/*----------------------------------------------------------------------*/
|
/*----------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
@ -8410,6 +8451,7 @@ static virDriver remote_driver = {
|
|||||||
remoteNodeDeviceDettach, /* nodeDeviceDettach */
|
remoteNodeDeviceDettach, /* nodeDeviceDettach */
|
||||||
remoteNodeDeviceReAttach, /* nodeDeviceReAttach */
|
remoteNodeDeviceReAttach, /* nodeDeviceReAttach */
|
||||||
remoteNodeDeviceReset, /* nodeDeviceReset */
|
remoteNodeDeviceReset, /* nodeDeviceReset */
|
||||||
|
remoteDomainMigratePrepareTunnel, /* domainMigratePrepareTunnel */
|
||||||
};
|
};
|
||||||
|
|
||||||
static virNetworkDriver network_driver = {
|
static virNetworkDriver network_driver = {
|
||||||
|
@ -2697,6 +2697,23 @@ xdr_remote_secret_lookup_by_usage_ret (XDR *xdrs, remote_secret_lookup_by_usage_
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool_t
|
||||||
|
xdr_remote_domain_migrate_prepare_tunnel_args (XDR *xdrs, remote_domain_migrate_prepare_tunnel_args *objp)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!xdr_remote_string (xdrs, &objp->uri_in))
|
||||||
|
return FALSE;
|
||||||
|
if (!xdr_uint64_t (xdrs, &objp->flags))
|
||||||
|
return FALSE;
|
||||||
|
if (!xdr_remote_string (xdrs, &objp->dname))
|
||||||
|
return FALSE;
|
||||||
|
if (!xdr_uint64_t (xdrs, &objp->resource))
|
||||||
|
return FALSE;
|
||||||
|
if (!xdr_remote_nonnull_string (xdrs, &objp->dom_xml))
|
||||||
|
return FALSE;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
bool_t
|
bool_t
|
||||||
xdr_remote_procedure (XDR *xdrs, remote_procedure *objp)
|
xdr_remote_procedure (XDR *xdrs, remote_procedure *objp)
|
||||||
{
|
{
|
||||||
|
@ -1528,6 +1528,16 @@ struct remote_secret_lookup_by_usage_ret {
|
|||||||
remote_nonnull_secret secret;
|
remote_nonnull_secret secret;
|
||||||
};
|
};
|
||||||
typedef struct remote_secret_lookup_by_usage_ret remote_secret_lookup_by_usage_ret;
|
typedef struct remote_secret_lookup_by_usage_ret remote_secret_lookup_by_usage_ret;
|
||||||
|
|
||||||
|
struct remote_domain_migrate_prepare_tunnel_args {
|
||||||
|
remote_string uri_in;
|
||||||
|
uint64_t flags;
|
||||||
|
remote_string dname;
|
||||||
|
uint64_t resource;
|
||||||
|
remote_nonnull_string dom_xml;
|
||||||
|
};
|
||||||
|
typedef struct remote_domain_migrate_prepare_tunnel_args remote_domain_migrate_prepare_tunnel_args;
|
||||||
|
|
||||||
#define REMOTE_PROGRAM 0x20008086
|
#define REMOTE_PROGRAM 0x20008086
|
||||||
#define REMOTE_PROTOCOL_VERSION 1
|
#define REMOTE_PROTOCOL_VERSION 1
|
||||||
|
|
||||||
@ -1679,6 +1689,7 @@ enum remote_procedure {
|
|||||||
REMOTE_PROC_SECRET_GET_VALUE = 145,
|
REMOTE_PROC_SECRET_GET_VALUE = 145,
|
||||||
REMOTE_PROC_SECRET_UNDEFINE = 146,
|
REMOTE_PROC_SECRET_UNDEFINE = 146,
|
||||||
REMOTE_PROC_SECRET_LOOKUP_BY_USAGE = 147,
|
REMOTE_PROC_SECRET_LOOKUP_BY_USAGE = 147,
|
||||||
|
REMOTE_PROC_DOMAIN_MIGRATE_PREPARE_TUNNEL = 148,
|
||||||
};
|
};
|
||||||
typedef enum remote_procedure remote_procedure;
|
typedef enum remote_procedure remote_procedure;
|
||||||
|
|
||||||
@ -1959,6 +1970,7 @@ extern bool_t xdr_remote_secret_get_value_ret (XDR *, remote_secret_get_value_r
|
|||||||
extern bool_t xdr_remote_secret_undefine_args (XDR *, remote_secret_undefine_args*);
|
extern bool_t xdr_remote_secret_undefine_args (XDR *, remote_secret_undefine_args*);
|
||||||
extern bool_t xdr_remote_secret_lookup_by_usage_args (XDR *, remote_secret_lookup_by_usage_args*);
|
extern bool_t xdr_remote_secret_lookup_by_usage_args (XDR *, remote_secret_lookup_by_usage_args*);
|
||||||
extern bool_t xdr_remote_secret_lookup_by_usage_ret (XDR *, remote_secret_lookup_by_usage_ret*);
|
extern bool_t xdr_remote_secret_lookup_by_usage_ret (XDR *, remote_secret_lookup_by_usage_ret*);
|
||||||
|
extern bool_t xdr_remote_domain_migrate_prepare_tunnel_args (XDR *, remote_domain_migrate_prepare_tunnel_args*);
|
||||||
extern bool_t xdr_remote_procedure (XDR *, remote_procedure*);
|
extern bool_t xdr_remote_procedure (XDR *, remote_procedure*);
|
||||||
extern bool_t xdr_remote_message_type (XDR *, remote_message_type*);
|
extern bool_t xdr_remote_message_type (XDR *, remote_message_type*);
|
||||||
extern bool_t xdr_remote_message_status (XDR *, remote_message_status*);
|
extern bool_t xdr_remote_message_status (XDR *, remote_message_status*);
|
||||||
@ -2213,6 +2225,7 @@ extern bool_t xdr_remote_secret_get_value_ret ();
|
|||||||
extern bool_t xdr_remote_secret_undefine_args ();
|
extern bool_t xdr_remote_secret_undefine_args ();
|
||||||
extern bool_t xdr_remote_secret_lookup_by_usage_args ();
|
extern bool_t xdr_remote_secret_lookup_by_usage_args ();
|
||||||
extern bool_t xdr_remote_secret_lookup_by_usage_ret ();
|
extern bool_t xdr_remote_secret_lookup_by_usage_ret ();
|
||||||
|
extern bool_t xdr_remote_domain_migrate_prepare_tunnel_args ();
|
||||||
extern bool_t xdr_remote_procedure ();
|
extern bool_t xdr_remote_procedure ();
|
||||||
extern bool_t xdr_remote_message_type ();
|
extern bool_t xdr_remote_message_type ();
|
||||||
extern bool_t xdr_remote_message_status ();
|
extern bool_t xdr_remote_message_status ();
|
||||||
|
@ -1355,6 +1355,14 @@ struct remote_secret_lookup_by_usage_ret {
|
|||||||
remote_nonnull_secret secret;
|
remote_nonnull_secret secret;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct remote_domain_migrate_prepare_tunnel_args {
|
||||||
|
remote_string uri_in;
|
||||||
|
unsigned hyper flags;
|
||||||
|
remote_string dname;
|
||||||
|
unsigned hyper resource;
|
||||||
|
remote_nonnull_string dom_xml;
|
||||||
|
};
|
||||||
|
|
||||||
/*----- Protocol. -----*/
|
/*----- Protocol. -----*/
|
||||||
|
|
||||||
/* Define the program number, protocol version and procedure numbers here. */
|
/* Define the program number, protocol version and procedure numbers here. */
|
||||||
@ -1523,7 +1531,9 @@ enum remote_procedure {
|
|||||||
REMOTE_PROC_SECRET_SET_VALUE = 144,
|
REMOTE_PROC_SECRET_SET_VALUE = 144,
|
||||||
REMOTE_PROC_SECRET_GET_VALUE = 145,
|
REMOTE_PROC_SECRET_GET_VALUE = 145,
|
||||||
REMOTE_PROC_SECRET_UNDEFINE = 146,
|
REMOTE_PROC_SECRET_UNDEFINE = 146,
|
||||||
REMOTE_PROC_SECRET_LOOKUP_BY_USAGE = 147
|
REMOTE_PROC_SECRET_LOOKUP_BY_USAGE = 147,
|
||||||
|
|
||||||
|
REMOTE_PROC_DOMAIN_MIGRATE_PREPARE_TUNNEL = 148
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -4267,6 +4267,7 @@ static virDriver testDriver = {
|
|||||||
NULL, /* nodeDeviceDettach */
|
NULL, /* nodeDeviceDettach */
|
||||||
NULL, /* nodeDeviceReAttach */
|
NULL, /* nodeDeviceReAttach */
|
||||||
NULL, /* nodeDeviceReset */
|
NULL, /* nodeDeviceReset */
|
||||||
|
NULL, /* domainMigratePrepareTunnel */
|
||||||
};
|
};
|
||||||
|
|
||||||
static virNetworkDriver testNetworkDriver = {
|
static virNetworkDriver testNetworkDriver = {
|
||||||
|
@ -1861,6 +1861,7 @@ static virDriver umlDriver = {
|
|||||||
NULL, /* nodeDeviceDettach */
|
NULL, /* nodeDeviceDettach */
|
||||||
NULL, /* nodeDeviceReAttach */
|
NULL, /* nodeDeviceReAttach */
|
||||||
NULL, /* nodeDeviceReset */
|
NULL, /* nodeDeviceReset */
|
||||||
|
NULL, /* domainMigratePrepareTunnel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -6467,7 +6467,7 @@ virDriver NAME(Driver) = {
|
|||||||
NULL, /* nodeDeviceDettach */
|
NULL, /* nodeDeviceDettach */
|
||||||
NULL, /* nodeDeviceReAttach */
|
NULL, /* nodeDeviceReAttach */
|
||||||
NULL, /* nodeDeviceReset */
|
NULL, /* nodeDeviceReset */
|
||||||
|
NULL, /* domainMigratePrepareTunnel */
|
||||||
};
|
};
|
||||||
|
|
||||||
virNetworkDriver NAME(NetworkDriver) = {
|
virNetworkDriver NAME(NetworkDriver) = {
|
||||||
|
@ -1722,6 +1722,7 @@ static virDriver xenUnifiedDriver = {
|
|||||||
xenUnifiedNodeDeviceDettach, /* nodeDeviceDettach */
|
xenUnifiedNodeDeviceDettach, /* nodeDeviceDettach */
|
||||||
xenUnifiedNodeDeviceReAttach, /* nodeDeviceReAttach */
|
xenUnifiedNodeDeviceReAttach, /* nodeDeviceReAttach */
|
||||||
xenUnifiedNodeDeviceReset, /* nodeDeviceReset */
|
xenUnifiedNodeDeviceReset, /* nodeDeviceReset */
|
||||||
|
NULL, /* domainMigratePrepareTunnel */
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2462,6 +2462,7 @@ static const vshCmdInfo info_migrate[] = {
|
|||||||
|
|
||||||
static const vshCmdOptDef opts_migrate[] = {
|
static const vshCmdOptDef opts_migrate[] = {
|
||||||
{"live", VSH_OT_BOOL, 0, gettext_noop("live migration")},
|
{"live", VSH_OT_BOOL, 0, gettext_noop("live migration")},
|
||||||
|
{"tunnelled", VSH_OT_BOOL, 0, gettext_noop("tunnelled migration")},
|
||||||
{"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("domain name, id or uuid")},
|
{"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")},
|
{"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")},
|
{"migrateuri", VSH_OT_DATA, 0, gettext_noop("migration URI, usually can be omitted")},
|
||||||
@ -2499,12 +2500,31 @@ cmdMigrate (vshControl *ctl, const vshCmd *cmd)
|
|||||||
if (vshCommandOptBool (cmd, "live"))
|
if (vshCommandOptBool (cmd, "live"))
|
||||||
flags |= VIR_MIGRATE_LIVE;
|
flags |= VIR_MIGRATE_LIVE;
|
||||||
|
|
||||||
/* Temporarily connect to the destination host. */
|
if (vshCommandOptBool (cmd, "tunnelled"))
|
||||||
dconn = virConnectOpenAuth (desturi, virConnectAuthPtrDefault, 0);
|
flags |= VIR_MIGRATE_TUNNELLED;
|
||||||
if (!dconn) goto done;
|
|
||||||
|
if (!(flags & VIR_MIGRATE_TUNNELLED)) {
|
||||||
|
/* For regular live migration, temporarily connect to the destination
|
||||||
|
* host. For tunnelled migration, that will be done by the remote
|
||||||
|
* libvirtd.
|
||||||
|
*/
|
||||||
|
dconn = virConnectOpenAuth(desturi, virConnectAuthPtrDefault, 0);
|
||||||
|
if (!dconn) goto done;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* when doing tunnelled migration, use migrateuri if it's available,
|
||||||
|
* but if not, fall back to desturi. This allows both of these
|
||||||
|
* to work:
|
||||||
|
*
|
||||||
|
* virsh migrate guest qemu+tls://dest/system
|
||||||
|
* virsh migrate guest qemu+tls://dest/system qemu+tls://dest-alt/system
|
||||||
|
*/
|
||||||
|
if (migrateuri == NULL)
|
||||||
|
migrateuri = desturi;
|
||||||
|
}
|
||||||
|
|
||||||
/* Migrate. */
|
/* Migrate. */
|
||||||
ddom = virDomainMigrate (dom, dconn, flags, dname, migrateuri, 0);
|
ddom = virDomainMigrate(dom, dconn, flags, dname, migrateuri, 0);
|
||||||
if (!ddom) goto done;
|
if (!ddom) goto done;
|
||||||
|
|
||||||
ret = TRUE;
|
ret = TRUE;
|
||||||
|
Loading…
Reference in New Issue
Block a user