提交 6e16575a 编写于 作者: C Chris Lalancette

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: NChris Lalancette <clalance@redhat.com>
上级 3bd4c7ba
^src/libvirt\.c$
^src/qemu/qemu_driver\.c$
^src/util/util\.c$
^src/xen/xend_internal\.c$
^daemon/libvirtd.c$
......
......@@ -55,6 +55,7 @@
#include "datatypes.h"
#include "memory.h"
#include "util.h"
#include "stream.h"
#define VIR_FROM_THIS VIR_FROM_REMOTE
#define REMOTE_DEBUG(fmt, ...) DEBUG(fmt, __VA_ARGS__)
......@@ -1492,6 +1493,49 @@ remoteDispatchDomainMigrateFinish2 (struct qemud_server *server ATTRIBUTE_UNUSED
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
remoteDispatchListDefinedDomains (struct qemud_server *server 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_undefine_args val_remote_secret_undefine_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_domain_migrate_prepare2_args *args,
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(
struct qemud_server *server,
struct qemud_client *client,
......
......@@ -742,3 +742,8 @@
.args_filter = (xdrproc_t) xdr_remote_secret_lookup_by_usage_args,
.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",
"virDomainMigratePrepare": "private function for migration",
"virDomainMigratePrepare2": "private function for migration",
"virDomainMigratePrepareTunnel": "private function for tunnelled migration",
"virDrvSupportsFeature": "private function for remote access",
"DllMain": "specific function for Win32",
}
......
......@@ -337,6 +337,7 @@ typedef virDomainInterfaceStatsStruct *virDomainInterfaceStatsPtr;
/* Domain migration flags. */
typedef enum {
VIR_MIGRATE_LIVE = 1, /* live migration */
VIR_MIGRATE_TUNNELLED = 2, /* tunnelled migration */
} virDomainMigrateFlags;
/* Domain migration. */
......
......@@ -347,6 +347,16 @@ typedef int
(*virDrvNodeDeviceReset)
(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:
*
......@@ -427,6 +437,7 @@ struct _virDriver {
virDrvNodeDeviceDettach nodeDeviceDettach;
virDrvNodeDeviceReAttach nodeDeviceReAttach;
virDrvNodeDeviceReset nodeDeviceReset;
virDrvDomainMigratePrepareTunnel domainMigratePrepareTunnel;
};
typedef int
......
......@@ -3274,6 +3274,7 @@ static virDriver esxDriver = {
NULL, /* nodeDeviceDettach */
NULL, /* nodeDeviceReAttach */
NULL, /* nodeDeviceReset */
NULL, /* domainMigratePrepareTunnel */
};
......
......@@ -3044,6 +3044,70 @@ virDomainMigrateVersion2 (virDomainPtr domain,
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:
* @domain: a domain object
......@@ -3058,6 +3122,8 @@ virDomainMigrateVersion2 (virDomainPtr domain,
*
* Flags may be one of more of the following:
* 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,
* then you may set the dname parameter to the new name (otherwise
......@@ -3116,31 +3182,47 @@ virDomainMigrate (virDomainPtr domain,
goto error;
}
/* Now checkout the destination */
if (!VIR_IS_CONNECT (dconn)) {
virLibConnError (domain->conn, VIR_ERR_INVALID_CONN, __FUNCTION__);
goto error;
if (flags & VIR_MIGRATE_TUNNELLED) {
/* tunnelled migration is more or less a completely different migration
* protocol. dconn has to be NULL, uri has to be set, and the flow
* of logic is completely different. Hence, here we split off from
* the main migration flow and use a separate function.
*/
if (dconn != NULL) {
virLibConnError(domain->conn, VIR_ERR_INVALID_ARG,
_("requested TUNNELLED migration, but non-NULL dconn"));
goto error;
}
ddomain = virDomainMigrateTunnelled(domain, flags, dname, uri, bandwidth);
}
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;
/* Now checkout the destination */
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)
......@@ -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:
* @conn: pointer to the hypervisor connection
......
......@@ -70,6 +70,12 @@ virDomainPtr virDomainMigrateFinish2 (virConnectPtr dconn,
const char *uri,
unsigned long flags,
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
......@@ -239,6 +239,7 @@ virDomainMigratePerform;
virDomainMigrateFinish;
virDomainMigratePrepare2;
virDomainMigrateFinish2;
virDomainMigratePrepareTunnel;
virRegisterDriver;
virRegisterInterfaceDriver;
virRegisterNetworkDriver;
......
......@@ -2147,6 +2147,7 @@ static virDriver lxcDriver = {
NULL, /* nodeDeviceDettach */
NULL, /* nodeDeviceReAttach */
NULL, /* nodeDeviceReset */
NULL, /* domainMigratePrepareTunnel */
};
static virStateDriver lxcStateDriver = {
......
......@@ -787,6 +787,7 @@ static virDriver oneDriver = {
NULL, /* nodeDeviceDettach; */
NULL, /* nodeDeviceReAttach; */
NULL, /* nodeDeviceReset; */
NULL, /* domainMigratePrepareTunnel */
};
static virStateDriver oneStateDriver = {
......
......@@ -1432,6 +1432,7 @@ static virDriver openvzDriver = {
NULL, /* nodeDeviceDettach */
NULL, /* nodeDeviceReAttach */
NULL, /* nodeDeviceReset */
NULL, /* domainMigratePrepareTunnel */
};
int openvzRegister(void) {
......
......@@ -1377,6 +1377,7 @@ virDriver phypDriver = {
NULL, /* nodeDeviceDettach */
NULL, /* nodeDeviceReAttach */
NULL, /* nodeDeviceReset */
NULL, /* domainMigratePrepareTunnel */
};
int
......
此差异已折叠。
......@@ -6698,7 +6698,6 @@ done:
}
#if 0
static struct private_stream_data *
remoteStreamOpen(virStreamPtr st,
int output ATTRIBUTE_UNUSED,
......@@ -7049,9 +7048,51 @@ static virStreamDriver remoteStreamDrv = {
.streamUpdateCallback = remoteStreamEventUpdateCallback,
.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 */
remoteNodeDeviceReAttach, /* nodeDeviceReAttach */
remoteNodeDeviceReset, /* nodeDeviceReset */
remoteDomainMigratePrepareTunnel, /* domainMigratePrepareTunnel */
};
static virNetworkDriver network_driver = {
......
......@@ -2697,6 +2697,23 @@ xdr_remote_secret_lookup_by_usage_ret (XDR *xdrs, remote_secret_lookup_by_usage_
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
xdr_remote_procedure (XDR *xdrs, remote_procedure *objp)
{
......
......@@ -1528,6 +1528,16 @@ struct remote_secret_lookup_by_usage_ret {
remote_nonnull_secret secret;
};
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_PROTOCOL_VERSION 1
......@@ -1679,6 +1689,7 @@ enum remote_procedure {
REMOTE_PROC_SECRET_GET_VALUE = 145,
REMOTE_PROC_SECRET_UNDEFINE = 146,
REMOTE_PROC_SECRET_LOOKUP_BY_USAGE = 147,
REMOTE_PROC_DOMAIN_MIGRATE_PREPARE_TUNNEL = 148,
};
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_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_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_message_type (XDR *, remote_message_type*);
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_lookup_by_usage_args ();
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_message_type ();
extern bool_t xdr_remote_message_status ();
......
......@@ -1355,6 +1355,14 @@ struct remote_secret_lookup_by_usage_ret {
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. -----*/
/* 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_GET_VALUE = 145,
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, /* nodeDeviceReAttach */
NULL, /* nodeDeviceReset */
NULL, /* domainMigratePrepareTunnel */
};
static virNetworkDriver testNetworkDriver = {
......
......@@ -1861,6 +1861,7 @@ static virDriver umlDriver = {
NULL, /* nodeDeviceDettach */
NULL, /* nodeDeviceReAttach */
NULL, /* nodeDeviceReset */
NULL, /* domainMigratePrepareTunnel */
};
......
......@@ -6467,7 +6467,7 @@ virDriver NAME(Driver) = {
NULL, /* nodeDeviceDettach */
NULL, /* nodeDeviceReAttach */
NULL, /* nodeDeviceReset */
NULL, /* domainMigratePrepareTunnel */
};
virNetworkDriver NAME(NetworkDriver) = {
......
......@@ -1722,6 +1722,7 @@ static virDriver xenUnifiedDriver = {
xenUnifiedNodeDeviceDettach, /* nodeDeviceDettach */
xenUnifiedNodeDeviceReAttach, /* nodeDeviceReAttach */
xenUnifiedNodeDeviceReset, /* nodeDeviceReset */
NULL, /* domainMigratePrepareTunnel */
};
/**
......
......@@ -2462,6 +2462,7 @@ static const vshCmdInfo info_migrate[] = {
static const vshCmdOptDef opts_migrate[] = {
{"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")},
{"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")},
......@@ -2499,12 +2500,31 @@ 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, "tunnelled"))
flags |= VIR_MIGRATE_TUNNELLED;
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. */
ddom = virDomainMigrate (dom, dconn, flags, dname, migrateuri, 0);
ddom = virDomainMigrate(dom, dconn, flags, dname, migrateuri, 0);
if (!ddom) goto done;
ret = TRUE;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册