diff --git a/ChangeLog b/ChangeLog index 558e3e4055..2ec5e6d72d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +Tue Aug 21 10:21:00 BST 2007 Richard W.M. Jones + + * src/libvirt.c (virDomainMigrate): Added virDomainMigrate + API call. + * src/xend_internal.c, src/xen_unified.c: Support for migration + of Xen domains. + * src/xen_internal.c: Xen capabilities indicates level of + support for migration. + * qemud/remote.c, qemud/remote_protocol.x, src/remote_internal.c: + Support for migration between remote hypervisors. + * src/virsh.c: Added 'virsh migrate' command. + * docs/libvir.html, docs/hvsupport.html: Updated hvsupport + documentation. + Tue Aug 21 09:56:00 BST 2007 Richard W.M. Jones * qemud/remote.c, qemud/remote_protocol.x, src/driver.h, diff --git a/docs/hvsupport.html b/docs/hvsupport.html index f6b22b6b3a..fe7f30faf3 100644 --- a/docs/hvsupport.html +++ b/docs/hvsupport.html @@ -243,6 +243,12 @@ updated on 2007-08-20. ≥ 0.2.0 ≥ 0.2.0 ≥ 0.3.0 + virDomainMigrate + 0.3.2 + ≥ 0.3.2 + x + x + 0.3.2 virDomainPinVcpu 0.1.4 ≥ 0.1.4 diff --git a/docs/libvir.html b/docs/libvir.html index c7e947ba1a..0e7539d8db 100644 --- a/docs/libvir.html +++ b/docs/libvir.html @@ -2996,6 +2996,14 @@ updated on 2007-08-20. ≥ 0.2.0 ≥ 0.3.0 + + virDomainMigrate + 0.3.2 + ≥ 0.3.2 + x + x + 0.3.2 + virDomainPinVcpu 0.1.4 diff --git a/include/libvirt/libvirt.h b/include/libvirt/libvirt.h index 6125795797..0ef4e27080 100644 --- a/include/libvirt/libvirt.h +++ b/include/libvirt/libvirt.h @@ -197,6 +197,16 @@ int virDomainSetSchedulerParameters (virDomainPtr domain, virSchedParameterPtr params, int nparams); +/* Domain migration flags. */ +typedef enum { + VIR_MIGRATE_LIVE = 1, /* live migration */ +} virDomainMigrateFlags; + +/* Domain migration. */ +virDomainPtr virDomainMigrate (virDomainPtr domain, virConnectPtr dconn, + unsigned long flags, const char *dname, + const char *uri, unsigned long bandwidth); + /** * VIR_NODEINFO_MAXCPUS: * @nodeinfo: virNodeInfo instance diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 8d31288093..255d11e2d0 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -197,6 +197,16 @@ int virDomainSetSchedulerParameters (virDomainPtr domain, virSchedParameterPtr params, int nparams); +/* Domain migration flags. */ +typedef enum { + VIR_MIGRATE_LIVE = 1, /* live migration */ +} virDomainMigrateFlags; + +/* Domain migration. */ +virDomainPtr virDomainMigrate (virDomainPtr domain, virConnectPtr dconn, + unsigned long flags, const char *dname, + const char *uri, unsigned long bandwidth); + /** * VIR_NODEINFO_MAXCPUS: * @nodeinfo: virNodeInfo instance diff --git a/qemud/remote.c b/qemud/remote.c index 62c353a53e..90ee1529d7 100644 --- a/qemud/remote.c +++ b/qemud/remote.c @@ -1058,6 +1058,91 @@ remoteDispatchDomainGetVcpus (struct qemud_client *client, return 0; } +static int +remoteDispatchDomainMigratePrepare (struct qemud_client *client, + remote_message_header *req, + remote_domain_migrate_prepare_args *args, + remote_domain_migrate_prepare_ret *ret) +{ + int r; + char *cookie = NULL; + int cookielen = 0; + char *uri_in; + char **uri_out; + char *dname; + CHECK_CONN (client); + + uri_in = args->uri_in == NULL ? NULL : *args->uri_in; + dname = args->dname == NULL ? NULL : *args->dname; + + /* Wacky world of XDR ... */ + uri_out = calloc (1, sizeof (char *)); + + r = __virDomainMigratePrepare (client->conn, &cookie, &cookielen, + uri_in, uri_out, + args->flags, dname, args->resource); + if (r == -1) return -1; + + /* remoteDispatchClientRequest will free cookie, uri_out and + * the string if there is one. + */ + ret->cookie.cookie_len = cookielen; + ret->cookie.cookie_val = cookie; + ret->uri_out = *uri_out == NULL ? NULL : uri_out; + + return 0; +} + +static int +remoteDispatchDomainMigratePerform (struct qemud_client *client, + remote_message_header *req, + remote_domain_migrate_perform_args *args, + void *ret ATTRIBUTE_UNUSED) +{ + int r; + virDomainPtr dom; + char *dname; + CHECK_CONN (client); + + dom = get_nonnull_domain (client->conn, args->dom); + if (dom == NULL) { + remoteDispatchError (client, req, "domain not found"); + return -2; + } + + dname = args->dname == NULL ? NULL : *args->dname; + + r = __virDomainMigratePerform (dom, + args->cookie.cookie_val, + args->cookie.cookie_len, + args->uri, + args->flags, dname, args->resource); + if (r == -1) return -1; + + return 0; +} + +static int +remoteDispatchDomainMigrateFinish (struct qemud_client *client, + remote_message_header *req, + remote_domain_migrate_finish_args *args, + remote_domain_migrate_finish_ret *ret) +{ + virDomainPtr ddom; + CHECK_CONN (client); + + ddom = __virDomainMigrateFinish (client->conn, args->dname, + args->cookie.cookie_val, + args->cookie.cookie_len, + args->uri, + args->flags); + if (ddom == NULL) return -1; + + make_nonnull_domain (&ret->ddom, ddom); + + return 0; +} + static int remoteDispatchListDefinedDomains (struct qemud_client *client, remote_message_header *req, diff --git a/qemud/remote_dispatch_localvars.h b/qemud/remote_dispatch_localvars.h index 3f2fcbcd95..07795d7c9e 100644 --- a/qemud/remote_dispatch_localvars.h +++ b/qemud/remote_dispatch_localvars.h @@ -12,6 +12,8 @@ remote_domain_save_args lv_remote_domain_save_args; remote_domain_shutdown_args lv_remote_domain_shutdown_args; remote_list_defined_domains_args lv_remote_list_defined_domains_args; remote_list_defined_domains_ret lv_remote_list_defined_domains_ret; +remote_domain_migrate_prepare_args lv_remote_domain_migrate_prepare_args; +remote_domain_migrate_prepare_ret lv_remote_domain_migrate_prepare_ret; remote_get_capabilities_ret lv_remote_get_capabilities_ret; remote_domain_set_max_memory_args lv_remote_domain_set_max_memory_args; remote_domain_undefine_args lv_remote_domain_undefine_args; @@ -79,6 +81,8 @@ remote_network_get_bridge_name_ret lv_remote_network_get_bridge_name_ret; remote_domain_destroy_args lv_remote_domain_destroy_args; remote_domain_define_xml_args lv_remote_domain_define_xml_args; remote_domain_define_xml_ret lv_remote_domain_define_xml_ret; +remote_domain_migrate_finish_args lv_remote_domain_migrate_finish_args; +remote_domain_migrate_finish_ret lv_remote_domain_migrate_finish_ret; remote_domain_get_vcpus_args lv_remote_domain_get_vcpus_args; remote_domain_get_vcpus_ret lv_remote_domain_get_vcpus_ret; remote_domain_get_scheduler_parameters_args lv_remote_domain_get_scheduler_parameters_args; @@ -88,3 +92,4 @@ remote_domain_dump_xml_ret lv_remote_domain_dump_xml_ret; remote_get_max_vcpus_args lv_remote_get_max_vcpus_args; remote_get_max_vcpus_ret lv_remote_get_max_vcpus_ret; remote_node_get_info_ret lv_remote_node_get_info_ret; +remote_domain_migrate_perform_args lv_remote_domain_migrate_perform_args; diff --git a/qemud/remote_dispatch_proc_switch.h b/qemud/remote_dispatch_proc_switch.h index 63511ec730..e58d8e97fd 100644 --- a/qemud/remote_dispatch_proc_switch.h +++ b/qemud/remote_dispatch_proc_switch.h @@ -161,6 +161,30 @@ case REMOTE_PROC_DOMAIN_LOOKUP_BY_UUID: ret = (char *) &lv_remote_domain_lookup_by_uuid_ret; memset (&lv_remote_domain_lookup_by_uuid_ret, 0, sizeof lv_remote_domain_lookup_by_uuid_ret); break; +case REMOTE_PROC_DOMAIN_MIGRATE_FINISH: + fn = (dispatch_fn) remoteDispatchDomainMigrateFinish; + args_filter = (xdrproc_t) xdr_remote_domain_migrate_finish_args; + args = (char *) &lv_remote_domain_migrate_finish_args; + memset (&lv_remote_domain_migrate_finish_args, 0, sizeof lv_remote_domain_migrate_finish_args); + ret_filter = (xdrproc_t) xdr_remote_domain_migrate_finish_ret; + ret = (char *) &lv_remote_domain_migrate_finish_ret; + memset (&lv_remote_domain_migrate_finish_ret, 0, sizeof lv_remote_domain_migrate_finish_ret); + break; +case REMOTE_PROC_DOMAIN_MIGRATE_PERFORM: + fn = (dispatch_fn) remoteDispatchDomainMigratePerform; + args_filter = (xdrproc_t) xdr_remote_domain_migrate_perform_args; + args = (char *) &lv_remote_domain_migrate_perform_args; + memset (&lv_remote_domain_migrate_perform_args, 0, sizeof lv_remote_domain_migrate_perform_args); + break; +case REMOTE_PROC_DOMAIN_MIGRATE_PREPARE: + fn = (dispatch_fn) remoteDispatchDomainMigratePrepare; + args_filter = (xdrproc_t) xdr_remote_domain_migrate_prepare_args; + args = (char *) &lv_remote_domain_migrate_prepare_args; + memset (&lv_remote_domain_migrate_prepare_args, 0, sizeof lv_remote_domain_migrate_prepare_args); + ret_filter = (xdrproc_t) xdr_remote_domain_migrate_prepare_ret; + ret = (char *) &lv_remote_domain_migrate_prepare_ret; + memset (&lv_remote_domain_migrate_prepare_ret, 0, sizeof lv_remote_domain_migrate_prepare_ret); + break; case REMOTE_PROC_DOMAIN_PIN_VCPU: fn = (dispatch_fn) remoteDispatchDomainPinVcpu; args_filter = (xdrproc_t) xdr_remote_domain_pin_vcpu_args; diff --git a/qemud/remote_dispatch_prototypes.h b/qemud/remote_dispatch_prototypes.h index 3fae468244..9ad9fb9b32 100644 --- a/qemud/remote_dispatch_prototypes.h +++ b/qemud/remote_dispatch_prototypes.h @@ -22,6 +22,9 @@ static int remoteDispatchDomainGetVcpus (struct qemud_client *client, remote_mes static int remoteDispatchDomainLookupById (struct qemud_client *client, remote_message_header *req, remote_domain_lookup_by_id_args *args, remote_domain_lookup_by_id_ret *ret); static int remoteDispatchDomainLookupByName (struct qemud_client *client, remote_message_header *req, remote_domain_lookup_by_name_args *args, remote_domain_lookup_by_name_ret *ret); static int remoteDispatchDomainLookupByUuid (struct qemud_client *client, remote_message_header *req, remote_domain_lookup_by_uuid_args *args, remote_domain_lookup_by_uuid_ret *ret); +static int remoteDispatchDomainMigrateFinish (struct qemud_client *client, remote_message_header *req, remote_domain_migrate_finish_args *args, remote_domain_migrate_finish_ret *ret); +static int remoteDispatchDomainMigratePerform (struct qemud_client *client, remote_message_header *req, remote_domain_migrate_perform_args *args, void *ret); +static int remoteDispatchDomainMigratePrepare (struct qemud_client *client, remote_message_header *req, remote_domain_migrate_prepare_args *args, remote_domain_migrate_prepare_ret *ret); static int remoteDispatchDomainPinVcpu (struct qemud_client *client, remote_message_header *req, remote_domain_pin_vcpu_args *args, void *ret); static int remoteDispatchDomainReboot (struct qemud_client *client, remote_message_header *req, remote_domain_reboot_args *args, void *ret); static int remoteDispatchDomainRestore (struct qemud_client *client, remote_message_header *req, remote_domain_restore_args *args, void *ret); diff --git a/qemud/remote_protocol.c b/qemud/remote_protocol.c index 309c35682f..e5dfb1c2cb 100644 --- a/qemud/remote_protocol.c +++ b/qemud/remote_protocol.c @@ -677,6 +677,78 @@ xdr_remote_domain_dump_xml_ret (XDR *xdrs, remote_domain_dump_xml_ret *objp) return TRUE; } +bool_t +xdr_remote_domain_migrate_prepare_args (XDR *xdrs, remote_domain_migrate_prepare_args *objp) +{ + + if (!xdr_remote_string (xdrs, &objp->uri_in)) + return FALSE; + if (!xdr_u_quad_t (xdrs, &objp->flags)) + return FALSE; + if (!xdr_remote_string (xdrs, &objp->dname)) + return FALSE; + if (!xdr_u_quad_t (xdrs, &objp->resource)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_migrate_prepare_ret (XDR *xdrs, remote_domain_migrate_prepare_ret *objp) +{ + char **objp_cpp0 = (char **) (void *) &objp->cookie.cookie_val; + + if (!xdr_bytes (xdrs, objp_cpp0, (u_int *) &objp->cookie.cookie_len, REMOTE_MIGRATE_COOKIE_MAX)) + return FALSE; + if (!xdr_remote_string (xdrs, &objp->uri_out)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_migrate_perform_args (XDR *xdrs, remote_domain_migrate_perform_args *objp) +{ + char **objp_cpp0 = (char **) (void *) &objp->cookie.cookie_val; + + if (!xdr_remote_nonnull_domain (xdrs, &objp->dom)) + return FALSE; + if (!xdr_bytes (xdrs, objp_cpp0, (u_int *) &objp->cookie.cookie_len, REMOTE_MIGRATE_COOKIE_MAX)) + return FALSE; + if (!xdr_remote_nonnull_string (xdrs, &objp->uri)) + return FALSE; + if (!xdr_u_quad_t (xdrs, &objp->flags)) + return FALSE; + if (!xdr_remote_string (xdrs, &objp->dname)) + return FALSE; + if (!xdr_u_quad_t (xdrs, &objp->resource)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_migrate_finish_args (XDR *xdrs, remote_domain_migrate_finish_args *objp) +{ + char **objp_cpp0 = (char **) (void *) &objp->cookie.cookie_val; + + if (!xdr_remote_nonnull_string (xdrs, &objp->dname)) + return FALSE; + if (!xdr_bytes (xdrs, objp_cpp0, (u_int *) &objp->cookie.cookie_len, REMOTE_MIGRATE_COOKIE_MAX)) + return FALSE; + if (!xdr_remote_nonnull_string (xdrs, &objp->uri)) + return FALSE; + if (!xdr_u_quad_t (xdrs, &objp->flags)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_migrate_finish_ret (XDR *xdrs, remote_domain_migrate_finish_ret *objp) +{ + + if (!xdr_remote_nonnull_domain (xdrs, &objp->ddom)) + return FALSE; + return TRUE; +} + bool_t xdr_remote_list_defined_domains_args (XDR *xdrs, remote_list_defined_domains_args *objp) { diff --git a/qemud/remote_protocol.h b/qemud/remote_protocol.h index 4944296f14..5a0310b561 100644 --- a/qemud/remote_protocol.h +++ b/qemud/remote_protocol.h @@ -25,6 +25,7 @@ typedef remote_nonnull_string *remote_string; #define REMOTE_CPUMAP_MAX 256 #define REMOTE_VCPUINFO_MAX 2048 #define REMOTE_CPUMAPS_MAX 16384 +#define REMOTE_MIGRATE_COOKIE_MAX 256 #define REMOTE_NETWORK_NAME_LIST_MAX 256 #define REMOTE_DOMAIN_SCHEDULER_PARAMETERS_MAX 16 @@ -341,6 +342,52 @@ struct remote_domain_dump_xml_ret { }; typedef struct remote_domain_dump_xml_ret remote_domain_dump_xml_ret; +struct remote_domain_migrate_prepare_args { + remote_string uri_in; + u_quad_t flags; + remote_string dname; + u_quad_t resource; +}; +typedef struct remote_domain_migrate_prepare_args remote_domain_migrate_prepare_args; + +struct remote_domain_migrate_prepare_ret { + struct { + u_int cookie_len; + char *cookie_val; + } cookie; + remote_string uri_out; +}; +typedef struct remote_domain_migrate_prepare_ret remote_domain_migrate_prepare_ret; + +struct remote_domain_migrate_perform_args { + remote_nonnull_domain dom; + struct { + u_int cookie_len; + char *cookie_val; + } cookie; + remote_nonnull_string uri; + u_quad_t flags; + remote_string dname; + u_quad_t resource; +}; +typedef struct remote_domain_migrate_perform_args remote_domain_migrate_perform_args; + +struct remote_domain_migrate_finish_args { + remote_nonnull_string dname; + struct { + u_int cookie_len; + char *cookie_val; + } cookie; + remote_nonnull_string uri; + u_quad_t flags; +}; +typedef struct remote_domain_migrate_finish_args remote_domain_migrate_finish_args; + +struct remote_domain_migrate_finish_ret { + remote_nonnull_domain ddom; +}; +typedef struct remote_domain_migrate_finish_ret remote_domain_migrate_finish_ret; + struct remote_list_defined_domains_args { int maxnames; }; @@ -643,6 +690,9 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_SET_SCHEDULER_PARAMETERS = 58, REMOTE_PROC_GET_HOSTNAME = 59, REMOTE_PROC_SUPPORTS_FEATURE = 60, + REMOTE_PROC_DOMAIN_MIGRATE_PREPARE = 61, + REMOTE_PROC_DOMAIN_MIGRATE_PERFORM = 62, + REMOTE_PROC_DOMAIN_MIGRATE_FINISH = 63, }; typedef enum remote_procedure remote_procedure; @@ -727,6 +777,11 @@ extern bool_t xdr_remote_domain_restore_args (XDR *, remote_domain_restore_args extern bool_t xdr_remote_domain_core_dump_args (XDR *, remote_domain_core_dump_args*); extern bool_t xdr_remote_domain_dump_xml_args (XDR *, remote_domain_dump_xml_args*); extern bool_t xdr_remote_domain_dump_xml_ret (XDR *, remote_domain_dump_xml_ret*); +extern bool_t xdr_remote_domain_migrate_prepare_args (XDR *, remote_domain_migrate_prepare_args*); +extern bool_t xdr_remote_domain_migrate_prepare_ret (XDR *, remote_domain_migrate_prepare_ret*); +extern bool_t xdr_remote_domain_migrate_perform_args (XDR *, remote_domain_migrate_perform_args*); +extern bool_t xdr_remote_domain_migrate_finish_args (XDR *, remote_domain_migrate_finish_args*); +extern bool_t xdr_remote_domain_migrate_finish_ret (XDR *, remote_domain_migrate_finish_ret*); extern bool_t xdr_remote_list_defined_domains_args (XDR *, remote_list_defined_domains_args*); extern bool_t xdr_remote_list_defined_domains_ret (XDR *, remote_list_defined_domains_ret*); extern bool_t xdr_remote_num_of_defined_domains_ret (XDR *, remote_num_of_defined_domains_ret*); @@ -830,6 +885,11 @@ extern bool_t xdr_remote_domain_restore_args (); extern bool_t xdr_remote_domain_core_dump_args (); extern bool_t xdr_remote_domain_dump_xml_args (); extern bool_t xdr_remote_domain_dump_xml_ret (); +extern bool_t xdr_remote_domain_migrate_prepare_args (); +extern bool_t xdr_remote_domain_migrate_prepare_ret (); +extern bool_t xdr_remote_domain_migrate_perform_args (); +extern bool_t xdr_remote_domain_migrate_finish_args (); +extern bool_t xdr_remote_domain_migrate_finish_ret (); extern bool_t xdr_remote_list_defined_domains_args (); extern bool_t xdr_remote_list_defined_domains_ret (); extern bool_t xdr_remote_num_of_defined_domains_ret (); diff --git a/qemud/remote_protocol.x b/qemud/remote_protocol.x index b7ca853c29..c9012ad8f3 100644 --- a/qemud/remote_protocol.x +++ b/qemud/remote_protocol.x @@ -72,6 +72,9 @@ const REMOTE_VCPUINFO_MAX = 2048; /* Upper limit on cpumaps (bytes) passed to virDomainGetVcpus. */ const REMOTE_CPUMAPS_MAX = 16384; +/* Upper limit on migrate cookie. */ +const REMOTE_MIGRATE_COOKIE_MAX = 256; + /* Upper limit on lists of network names. */ const REMOTE_NETWORK_NAME_LIST_MAX = 256; @@ -265,7 +268,6 @@ struct remote_domain_lookup_by_id_args { }; struct remote_domain_lookup_by_id_ret { - /* XXX "Not found" semantic is ill-defined. */ remote_nonnull_domain dom; }; @@ -274,7 +276,6 @@ struct remote_domain_lookup_by_uuid_args { }; struct remote_domain_lookup_by_uuid_ret { - /* XXX "Not found" semantic is ill-defined. */ remote_nonnull_domain dom; }; @@ -283,7 +284,6 @@ struct remote_domain_lookup_by_name_args { }; struct remote_domain_lookup_by_name_ret { - /* XXX "Not found" semantic is ill-defined. */ remote_nonnull_domain dom; }; @@ -370,6 +370,38 @@ struct remote_domain_dump_xml_ret { remote_nonnull_string xml; }; +struct remote_domain_migrate_prepare_args { + remote_string uri_in; + unsigned hyper flags; + remote_string dname; + unsigned hyper resource; +}; + +struct remote_domain_migrate_prepare_ret { + opaque cookie; + remote_string uri_out; +}; + +struct remote_domain_migrate_perform_args { + remote_nonnull_domain dom; + opaque cookie; + remote_nonnull_string uri; + unsigned hyper flags; + remote_string dname; + unsigned hyper resource; +}; + +struct remote_domain_migrate_finish_args { + remote_nonnull_string dname; + opaque cookie; + remote_nonnull_string uri; + unsigned hyper flags; +}; + +struct remote_domain_migrate_finish_ret { + remote_nonnull_domain ddom; +}; + struct remote_list_defined_domains_args { int maxnames; }; @@ -482,7 +514,6 @@ struct remote_network_lookup_by_uuid_args { }; struct remote_network_lookup_by_uuid_ret { - /* XXX "Not found" semantic is ill-defined. */ remote_nonnull_network net; }; @@ -491,7 +522,6 @@ struct remote_network_lookup_by_name_args { }; struct remote_network_lookup_by_name_ret { - /* XXX "Not found" semantic is ill-defined. */ remote_nonnull_network net; }; @@ -619,7 +649,10 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_GET_SCHEDULER_PARAMETERS = 57, REMOTE_PROC_DOMAIN_SET_SCHEDULER_PARAMETERS = 58, REMOTE_PROC_GET_HOSTNAME = 59, - REMOTE_PROC_SUPPORTS_FEATURE = 60 + REMOTE_PROC_SUPPORTS_FEATURE = 60, + REMOTE_PROC_DOMAIN_MIGRATE_PREPARE = 61, + REMOTE_PROC_DOMAIN_MIGRATE_PERFORM = 62, + REMOTE_PROC_DOMAIN_MIGRATE_FINISH = 63 }; /* Custom RPC structure. */ diff --git a/src/Makefile.am b/src/Makefile.am index 0a941697a2..2dd735450d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -13,7 +13,7 @@ INCLUDES = -I$(top_builddir)/include \ $(WARN_CFLAGS) \ $(LIBVIRT_FEATURES) DEPS = libvirt.la -LDADDS = @STATIC_BINARIES@ libvirt.la +LDADDS = @STATIC_BINARIES@ $(WARN_CFLAGS) libvirt.la VIRSH_LIBS = @VIRSH_LIBS@ EXTRA_DIST = libvirt_sym.version diff --git a/src/driver.h b/src/driver.h index 60a75586b5..d73a940c7a 100644 --- a/src/driver.h +++ b/src/driver.h @@ -211,6 +211,36 @@ typedef int virSchedParameterPtr params, int nparams); +typedef int + (*virDrvDomainMigratePrepare) + (virConnectPtr dconn, + char **cookie, + int *cookielen, + const char *uri_in, + char **uri_out, + unsigned long flags, + const char *dname, + unsigned long resource); + +typedef int + (*virDrvDomainMigratePerform) + (virDomainPtr domain, + const char *cookie, + int cookielen, + const char *uri, + unsigned long flags, + const char *dname, + unsigned long resource); + +typedef virDomainPtr + (*virDrvDomainMigrateFinish) + (virConnectPtr dconn, + const char *dname, + const char *cookie, + int cookielen, + const char *uri, + unsigned long flags); + typedef struct _virDriver virDriver; typedef virDriver *virDriverPtr; @@ -276,6 +306,9 @@ struct _virDriver { virDrvDomainGetSchedulerType domainGetSchedulerType; virDrvDomainGetSchedulerParameters domainGetSchedulerParameters; virDrvDomainSetSchedulerParameters domainSetSchedulerParameters; + virDrvDomainMigratePrepare domainMigratePrepare; + virDrvDomainMigratePerform domainMigratePerform; + virDrvDomainMigrateFinish domainMigrateFinish; }; typedef int diff --git a/src/internal.h b/src/internal.h index 54bca2a43f..2181afe3de 100644 --- a/src/internal.h +++ b/src/internal.h @@ -231,6 +231,10 @@ int __virStateActive(void); int __virDrvSupportsFeature (virConnectPtr conn, int feature); +int __virDomainMigratePrepare (virConnectPtr dconn, char **cookie, int *cookielen, const char *uri_in, char **uri_out, unsigned long flags, const char *dname, unsigned long bandwidth); +int __virDomainMigratePerform (virDomainPtr domain, const char *cookie, int cookielen, const char *uri, unsigned long flags, const char *dname, unsigned long bandwidth); +virDomainPtr __virDomainMigrateFinish (virConnectPtr dconn, const char *dname, const char *cookie, int cookielen, const char *uri, unsigned long flags); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/libvirt.c b/src/libvirt.c index 57fbbf9ac2..fa81ea868c 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -1673,6 +1674,224 @@ virDomainGetXMLDesc(virDomainPtr domain, int flags) return NULL; } +/** + * virDomainMigrate: + * @domain: a domain object + * @dconn: destination host (a connection object) + * @flags: flags + * @dname: (optional) rename domain to this at destination + * @uri: (optional) dest hostname/URI as seen from the source host + * @bandwidth: (optional) specify migration bandwidth limit in Mbps + * + * Migrate the domain object from its current host to the destination + * 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. + * + * 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. + * + * 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 the new domain object if the migration was successful, + * or NULL in case of error. Note that the new domain object + * exists in the scope of the destination connection (dconn). + */ +virDomainPtr +virDomainMigrate (virDomainPtr domain, + virConnectPtr dconn, + unsigned long flags, + const char *dname, + const char *uri, + unsigned long bandwidth) +{ + virConnectPtr conn; + virDomainPtr ddomain = NULL; + char *uri_out = NULL; + char *cookie = NULL; + int cookielen = 0, ret; + DEBUG("domain=%p, dconn=%p, flags=%lu, dname=%s, uri=%s, bandwidth=%lu", + domain, dconn, flags, dname, uri, bandwidth); + + if (!VIR_IS_DOMAIN (domain)) { + virLibDomainError(domain, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return NULL; + } + conn = domain->conn; /* Source connection. */ + if (!VIR_IS_CONNECT (dconn)) { + virLibConnError (conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return NULL; + } + + /* Check that migration is supported by both drivers. */ + if (!VIR_DRV_SUPPORTS_FEATURE (conn->driver, conn, + VIR_DRV_FEATURE_MIGRATION_V1) || + !VIR_DRV_SUPPORTS_FEATURE (dconn->driver, dconn, + VIR_DRV_FEATURE_MIGRATION_V1)) { + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; + } + + /* Prepare the migration. + * + * The destination host may return a cookie, or leave cookie as + * NULL. + * + * The destination host MUST set uri_out if uri_in is NULL. + * + * If uri_in is non-NULL, then the destination host may modify + * the URI by setting uri_out. If it does not wish to modify + * the URI, it should leave uri_out as NULL. + */ + ret = dconn->driver->domainMigratePrepare + (dconn, &cookie, &cookielen, uri, &uri_out, flags, dname, bandwidth); + if (ret == -1) goto done; + if (uri == NULL && uri_out == NULL) { + virLibConnError (conn, VIR_ERR_INTERNAL_ERROR, + "domainMigratePrepare did not set uri"); + goto done; + } + if (uri_out) uri = uri_out; /* Did domainMigratePrepare change URI? */ + + assert (uri != NULL); + + /* Perform the migration. The driver isn't supposed to return + * until the migration is complete. + */ + ret = conn->driver->domainMigratePerform + (domain, cookie, cookielen, uri, flags, dname, bandwidth); + + if (ret == -1) goto done; + + /* Get the destination domain and return it or error. + * 'domain' no longer actually exists at this point (or so we hope), but + * we still use the object in memory in order to get the name. + */ + dname = dname ? dname : domain->name; + if (dconn->driver->domainMigrateFinish) + ddomain = dconn->driver->domainMigrateFinish + (dconn, dname, cookie, cookielen, uri, flags); + else + ddomain = virDomainLookupByName (dconn, dname); + + done: + if (uri_out) free (uri_out); + if (cookie) free (cookie); + return ddomain; +} + +/* Not for public use. This function is part of the internal + * implementation of migration in the remote case. + */ +int +__virDomainMigratePrepare (virConnectPtr dconn, + char **cookie, + int *cookielen, + const char *uri_in, + char **uri_out, + unsigned long flags, + const char *dname, + unsigned long bandwidth) +{ + DEBUG("dconn=%p, cookie=%p, cookielen=%p, uri_in=%s, uri_out=%p, flags=%lu, dname=%s, bandwidth=%lu", dconn, cookie, cookielen, uri_in, uri_out, flags, dname, bandwidth); + + if (!VIR_IS_CONNECT (dconn)) { + virLibConnError (dconn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return -1; + } + + if (dconn->driver->domainMigratePrepare) + return dconn->driver->domainMigratePrepare (dconn, cookie, cookielen, + uri_in, uri_out, + flags, dname, bandwidth); + + virLibConnError (dconn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +/* Not for public use. This function is part of the internal + * implementation of migration in the remote case. + */ +int +__virDomainMigratePerform (virDomainPtr domain, + const char *cookie, + int cookielen, + const char *uri, + unsigned long flags, + const char *dname, + unsigned long bandwidth) +{ + virConnectPtr conn; + DEBUG("domain=%p, cookie=%p, cookielen=%d, uri=%s, flags=%lu, dname=%s, bandwidth=%lu", domain, cookie, cookielen, uri, flags, dname, bandwidth); + + if (!VIR_IS_DOMAIN (domain)) { + virLibDomainError (domain, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return -1; + } + conn = domain->conn; + + if (conn->driver->domainMigratePerform) + return conn->driver->domainMigratePerform (domain, cookie, cookielen, + uri, + flags, dname, bandwidth); + + virLibDomainError (domain, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +/* Not for public use. This function is part of the internal + * implementation of migration in the remote case. + */ +virDomainPtr +__virDomainMigrateFinish (virConnectPtr dconn, + const char *dname, + const char *cookie, + int cookielen, + const char *uri, + unsigned long flags) +{ + DEBUG("dconn=%p, dname=%s, cookie=%p, cookielen=%d, uri=%s, flags=%lu", dconn, dname, cookie, cookielen, uri, flags); + + if (!VIR_IS_CONNECT (dconn)) { + virLibConnError (dconn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return NULL; + } + + if (dconn->driver->domainMigrateFinish) + return dconn->driver->domainMigrateFinish (dconn, dname, + cookie, cookielen, + uri, flags); + + virLibConnError (dconn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; +} + + /** * virNodeGetInfo: * @conn: pointer to the hypervisor connection diff --git a/src/libvirt_sym.version b/src/libvirt_sym.version index b9cd55efdb..b218ec0837 100644 --- a/src/libvirt_sym.version +++ b/src/libvirt_sym.version @@ -69,6 +69,8 @@ virDomainAttachDevice; virDomainDetachDevice; + virDomainMigrate; + virNetworkGetConnect; virConnectNumOfNetworks; virConnectListNetworks; @@ -118,5 +120,9 @@ __virDrvSupportsFeature; + __virDomainMigratePrepare; + __virDomainMigratePerform; + __virDomainMigrateFinish; + local: *; }; diff --git a/src/qemu_driver.c b/src/qemu_driver.c index 7da3ba4855..e00d449df4 100644 --- a/src/qemu_driver.c +++ b/src/qemu_driver.c @@ -2662,6 +2662,9 @@ static virDriver qemuDriver = { NULL, /* domainGetSchedulerType */ NULL, /* domainGetSchedulerParameters */ NULL, /* domainSetSchedulerParameters */ + NULL, /* domainMigratePrepare */ + NULL, /* domainMigratePerform */ + NULL, /* domainMigrateFinish */ }; static virNetworkDriver qemuNetworkDriver = { diff --git a/src/remote_internal.c b/src/remote_internal.c index 69b944f8cf..d952374a5a 100644 --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -1856,6 +1856,97 @@ remoteDomainDumpXML (virDomainPtr domain, int flags) return ret.xml; } +static int +remoteDomainMigratePrepare (virConnectPtr dconn, + char **cookie, int *cookielen, + const char *uri_in, char **uri_out, + unsigned long flags, const char *dname, + unsigned long resource) +{ + remote_domain_migrate_prepare_args args; + remote_domain_migrate_prepare_ret ret; + GET_PRIVATE (dconn, -1); + + args.uri_in = uri_in == NULL ? NULL : (char **) &uri_in; + args.flags = flags; + args.dname = dname == NULL ? NULL : (char **) &dname; + args.resource = resource; + + memset (&ret, 0, sizeof ret); + if (call (dconn, priv, 0, REMOTE_PROC_DOMAIN_MIGRATE_PREPARE, + (xdrproc_t) xdr_remote_domain_migrate_prepare_args, (char *) &args, + (xdrproc_t) xdr_remote_domain_migrate_prepare_ret, (char *) &ret) == -1) + return -1; + + if (ret.cookie.cookie_len > 0) { + *cookie = ret.cookie.cookie_val; /* Caller frees. */ + *cookielen = ret.cookie.cookie_len; + } + if (ret.uri_out) + *uri_out = *ret.uri_out; /* Caller frees. */ + + return 0; +} + +static int +remoteDomainMigratePerform (virDomainPtr domain, + const char *cookie, + int cookielen, + const char *uri, + unsigned long flags, + const char *dname, + unsigned long resource) +{ + remote_domain_migrate_perform_args args; + GET_PRIVATE (domain->conn, -1); + + make_nonnull_domain (&args.dom, domain); + args.cookie.cookie_len = cookielen; + args.cookie.cookie_val = (char *) cookie; + args.uri = (char *) uri; + args.flags = flags; + args.dname = dname == NULL ? NULL : (char **) &dname; + args.resource = resource; + + if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_MIGRATE_PERFORM, + (xdrproc_t) xdr_remote_domain_migrate_perform_args, (char *) &args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + return -1; + + return 0; +} + +static virDomainPtr +remoteDomainMigrateFinish (virConnectPtr dconn, + const char *dname, + const char *cookie, + int cookielen, + const char *uri, + unsigned long flags) +{ + virDomainPtr ddom; + remote_domain_migrate_finish_args args; + remote_domain_migrate_finish_ret ret; + GET_PRIVATE (dconn, NULL); + + args.dname = (char *) dname; + args.cookie.cookie_len = cookielen; + args.cookie.cookie_val = (char *) cookie; + args.uri = (char *) uri; + args.flags = flags; + + memset (&ret, 0, sizeof ret); + if (call (dconn, priv, 0, REMOTE_PROC_DOMAIN_MIGRATE_FINISH, + (xdrproc_t) xdr_remote_domain_migrate_finish_args, (char *) &args, + (xdrproc_t) xdr_remote_domain_migrate_finish_ret, (char *) &ret) == -1) + return NULL; + + ddom = get_nonnull_domain (dconn, ret.ddom); + xdr_free ((xdrproc_t) &xdr_remote_domain_migrate_finish_ret, (char *) &ret); + + return ddom; +} + static int remoteListDefinedDomains (virConnectPtr conn, char **const names, int maxnames) { @@ -2963,6 +3054,9 @@ static virDriver driver = { .domainGetSchedulerType = remoteDomainGetSchedulerType, .domainGetSchedulerParameters = remoteDomainGetSchedulerParameters, .domainSetSchedulerParameters = remoteDomainSetSchedulerParameters, + .domainMigratePrepare = remoteDomainMigratePrepare, + .domainMigratePerform = remoteDomainMigratePerform, + .domainMigrateFinish = remoteDomainMigrateFinish, }; static virNetworkDriver network_driver = { diff --git a/src/test.c b/src/test.c index 4300d2af9e..8252241069 100644 --- a/src/test.c +++ b/src/test.c @@ -1964,6 +1964,9 @@ static virDriver testDriver = { testDomainGetSchedulerType, /* domainGetSchedulerType */ testDomainGetSchedulerParams, /* domainGetSchedulerParameters */ testDomainSetSchedulerParams, /* domainSetSchedulerParameters */ + NULL, /* domainMigratePrepare */ + NULL, /* domainMigratePerform */ + NULL, /* domainMigrateFinish */ }; static virNetworkDriver testNetworkDriver = { diff --git a/src/virsh.c b/src/virsh.c index 69dc559dfd..dc68a719ff 100644 --- a/src/virsh.c +++ b/src/virsh.c @@ -2022,6 +2022,69 @@ cmdDomuuid(vshControl * ctl, vshCmd * cmd) return TRUE; } +/* + * "migrate" command + */ +static vshCmdInfo info_migrate[] = { + {"syntax", "migrate [--live] []"}, + {"help", gettext_noop("migrate domain to another host")}, + {"desc", gettext_noop("Migrate domain to another host. Add --live for live migration.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_migrate[] = { + {"live", VSH_OT_BOOL, 0, gettext_noop("live 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")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdMigrate (vshControl *ctl, vshCmd *cmd) +{ + virDomainPtr dom = NULL; + const char *desturi; + const char *migrateuri; + int flags = 0, found, ret = FALSE; + virConnectPtr dconn = NULL; + virDomainPtr ddom = NULL; + + if (!vshConnectionUsability (ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(dom = vshCommandOptDomain (ctl, cmd, "domain", NULL))) + return FALSE; + + desturi = vshCommandOptString (cmd, "desturi", &found); + if (!found) { + vshError (ctl, FALSE, _("migrate: Missing desturi")); + goto done; + } + + migrateuri = vshCommandOptString (cmd, "migrateuri", &found); + if (!found) migrateuri = NULL; + + if (vshCommandOptBool (cmd, "live")) + flags |= VIR_MIGRATE_LIVE; + + /* Temporarily connect to the destination host. */ + dconn = virConnectOpen (desturi); + if (!dconn) goto done; + + /* Migrate. */ + ddom = virDomainMigrate (dom, dconn, flags, NULL, migrateuri, 0); + if (!ddom) goto done; + + ret = TRUE; + + done: + if (dom) virDomainFree (dom); + if (ddom) virDomainFree (ddom); + if (dconn) virConnectClose (dconn); + return ret; +} + /* * "net-autostart" command */ @@ -3469,6 +3532,7 @@ static vshCmdDef commands[] = { {"dumpxml", cmdDumpXML, opts_dumpxml, info_dumpxml}, {"hostname", cmdHostname, NULL, info_hostname}, {"list", cmdList, opts_list, info_list}, + {"migrate", cmdMigrate, opts_migrate, info_migrate}, {"net-autostart", cmdNetworkAutostart, opts_network_autostart, info_network_autostart}, {"net-create", cmdNetworkCreate, opts_network_create, info_network_create}, {"net-define", cmdNetworkDefine, opts_network_define, info_network_define}, diff --git a/src/xen_internal.c b/src/xen_internal.c index 638e340d45..de7021b55d 100644 --- a/src/xen_internal.c +++ b/src/xen_internal.c @@ -2216,6 +2216,12 @@ xenHypervisorMakeCapabilitiesXML(virConnectPtr conn ATTRIBUTE_UNUSED, "\ \n\ \n\ + \n\ + \n\ + \n\ + xenmigr\n\ + \n\ + \n\ \n", -1); if (r == -1) goto vir_buffer_failed; diff --git a/src/xen_unified.c b/src/xen_unified.c index 5d9bb9418e..fafc24ca58 100644 --- a/src/xen_unified.c +++ b/src/xen_unified.c @@ -214,6 +214,16 @@ xenUnifiedType (virConnectPtr conn) return NULL; } +/* Which features are supported by this driver? */ +static int +xenUnifiedSupportsFeature (virConnectPtr conn ATTRIBUTE_UNUSED, int feature) +{ + switch (feature) { + case VIR_DRV_FEATURE_MIGRATION_V1: return 1; + default: return 0; + } +} + static int xenUnifiedVersion (virConnectPtr conn, unsigned long *hvVer) { @@ -805,6 +815,57 @@ xenUnifiedDomainDumpXML (virDomainPtr dom, int flags) return NULL; } +static int +xenUnifiedDomainMigratePrepare (virConnectPtr dconn, + char **cookie, + int *cookielen, + const char *uri_in, + char **uri_out, + unsigned long flags, + const char *dname, + unsigned long resource) +{ + GET_PRIVATE(dconn); + + if (priv->opened[XEN_UNIFIED_XEND_OFFSET]) + return xenDaemonDomainMigratePrepare (dconn, cookie, cookielen, + uri_in, uri_out, + flags, dname, resource); + + xenUnifiedError (dconn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +static int +xenUnifiedDomainMigratePerform (virDomainPtr dom, + const char *cookie, + int cookielen, + const char *uri, + unsigned long flags, + const char *dname, + unsigned long resource) +{ + GET_PRIVATE(dom->conn); + + if (priv->opened[XEN_UNIFIED_XEND_OFFSET]) + return xenDaemonDomainMigratePerform (dom, cookie, cookielen, uri, + flags, dname, resource); + + xenUnifiedError (dom->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +static virDomainPtr +xenUnifiedDomainMigrateFinish (virConnectPtr dconn, + const char *dname, + const char *cookie ATTRIBUTE_UNUSED, + int cookielen ATTRIBUTE_UNUSED, + const char *uri ATTRIBUTE_UNUSED, + unsigned long flags ATTRIBUTE_UNUSED) +{ + return xenUnifiedDomainLookupByName (dconn, dname); +} + static int xenUnifiedListDefinedDomains (virConnectPtr conn, char **const names, int maxnames) @@ -975,6 +1036,7 @@ static virDriver xenUnifiedDriver = { .ver = VERSION, .open = xenUnifiedOpen, .close = xenUnifiedClose, + .supports_feature = xenUnifiedSupportsFeature, .type = xenUnifiedType, .version = xenUnifiedVersion, .getHostname = xenUnifiedGetHostname, @@ -1016,6 +1078,9 @@ static virDriver xenUnifiedDriver = { .domainGetSchedulerType = xenUnifiedDomainGetSchedulerType, .domainGetSchedulerParameters = xenUnifiedDomainGetSchedulerParameters, .domainSetSchedulerParameters = xenUnifiedDomainSetSchedulerParameters, + .domainMigratePrepare = xenUnifiedDomainMigratePrepare, + .domainMigratePerform = xenUnifiedDomainMigratePerform, + .domainMigrateFinish = xenUnifiedDomainMigrateFinish, }; /** diff --git a/src/xend_internal.c b/src/xend_internal.c index dfeab67c09..5a1b09f231 100644 --- a/src/xend_internal.c +++ b/src/xend_internal.c @@ -11,6 +11,8 @@ */ #ifdef WITH_XEN +#include "config.h" + #include #include #include @@ -3148,6 +3150,168 @@ xenDaemonDetachDevice(virDomainPtr domain, char *xml) } +int +xenDaemonDomainMigratePrepare (virConnectPtr dconn, + char **cookie ATTRIBUTE_UNUSED, + int *cookielen ATTRIBUTE_UNUSED, + const char *uri_in, + char **uri_out, + unsigned long flags ATTRIBUTE_UNUSED, + const char *dname ATTRIBUTE_UNUSED, + unsigned long resource ATTRIBUTE_UNUSED) +{ + int r; + char hostname [HOST_NAME_MAX+1]; + + /* If uri_in is NULL, get the current hostname as a best guess + * of how the source host should connect to us. Note that caller + * deallocates this string. + */ + if (uri_in == NULL) { + r = gethostname (hostname, HOST_NAME_MAX+1); + if (r == -1) { + virXendError (dconn, VIR_ERR_SYSTEM_ERROR, strerror (errno)); + return -1; + } + *uri_out = strdup (hostname); + if (*uri_out == NULL) { + virXendError (dconn, VIR_ERR_SYSTEM_ERROR, strerror (errno)); + return -1; + } + } + + return 0; +} + +int +xenDaemonDomainMigratePerform (virDomainPtr domain, + const char *cookie ATTRIBUTE_UNUSED, + int cookielen ATTRIBUTE_UNUSED, + const char *uri, + unsigned long flags, + const char *dname, + unsigned long bandwidth) +{ + /* Upper layers have already checked domain. */ + virConnectPtr conn = domain->conn; + /* NB: Passing port=0 to xend means it ignores + * the port. However this is somewhat specific to + * the internals of the xend Python code. (XXX). + */ + char port[16] = "0"; + char live[2] = "0"; + int ret; + char *p, *hostname = NULL; + + /* Xen doesn't support renaming domains during migration. */ + if (dname) { + virXendError (conn, VIR_ERR_NO_SUPPORT, + "xenDaemonDomainMigrate: Xen does not support renaming domains during migration"); + return -1; + } + + /* Xen (at least up to 3.1.0) takes a resource parameter but + * ignores it. + */ + if (bandwidth) { + virXendError (conn, VIR_ERR_NO_SUPPORT, + "xenDaemonDomainMigrate: Xen does not support bandwidth limits during migration"); + return -1; + } + + /* Check the flags. */ + if ((flags & VIR_MIGRATE_LIVE)) { + strcpy (live, "1"); + flags &= ~VIR_MIGRATE_LIVE; + } + if (flags != 0) { + virXendError (conn, VIR_ERR_NO_SUPPORT, + "xenDaemonDomainMigrate: unsupported flag"); + return -1; + } + + /* Set hostname and port. + * + * URI is non-NULL (guaranteed by caller). We expect either + * "hostname", "hostname:port" or "xenmigr://hostname[:port]/". + */ + if (strstr (uri, "//")) { /* Full URI. */ + xmlURIPtr uriptr = xmlParseURI (uri); + if (!uriptr) { + virXendError (conn, VIR_ERR_INVALID_ARG, + "xenDaemonDomainMigrate: invalid URI"); + return -1; + } + if (uriptr->scheme && STRCASENEQ (uriptr->scheme, "xenmigr")) { + virXendError (conn, VIR_ERR_INVALID_ARG, + "xenDaemonDomainMigrate: only xenmigr:// migrations are supported by Xen"); + xmlFreeURI (uriptr); + return -1; + } + if (!uriptr->server) { + virXendError (conn, VIR_ERR_INVALID_ARG, + "xenDaemonDomainMigrate: a hostname must be specified in the URI"); + xmlFreeURI (uriptr); + return -1; + } + hostname = strdup (uriptr->server); + if (!hostname) { + virXendError (conn, VIR_ERR_NO_MEMORY, "strdup"); + xmlFreeURI (uriptr); + return -1; + } + if (uriptr->port) + snprintf (port, sizeof port, "%d", uriptr->port); + xmlFreeURI (uriptr); + } + else if ((p = strrchr (uri, ':')) != NULL) { /* "hostname:port" */ + int port_nr, n; + + if (sscanf (p+1, "%d", &port_nr) != 1) { + virXendError (conn, VIR_ERR_INVALID_ARG, + "xenDaemonDomainMigrate: invalid port number"); + return -1; + } + snprintf (port, sizeof port, "%d", port_nr); + + /* Get the hostname. */ + n = p - uri; /* n = Length of hostname in bytes. */ + hostname = strdup (uri); + if (!hostname) { + virXendError (conn, VIR_ERR_NO_MEMORY, "strdup"); + return -1; + } + hostname[n] = '\0'; + } + else { /* "hostname" (or IP address) */ + hostname = strdup (uri); + if (!hostname) { + virXendError (conn, VIR_ERR_NO_MEMORY, "strdup"); + return -1; + } + } + +#ifdef ENABLE_DEBUG + fprintf (stderr, "hostname = %s, port = %s\n", hostname, port); +#endif + + /* Make the call. */ + ret = xend_op (domain->conn, domain->name, + "op", "migrate", + "destination", hostname, + "live", live, + "port", port, + "resource", "0", /* required, xend ignores it */ + NULL); + free (hostname); + +#ifdef ENABLE_DEBUG + fprintf (stderr, "migration done\n"); +#endif + + return ret; +} + virDomainPtr xenDaemonDomainDefineXML(virConnectPtr conn, const char *xmlDesc) { int ret; char *sexpr; diff --git a/src/xend_internal.h b/src/xend_internal.h index 3facac1bb6..68833b0d75 100644 --- a/src/xend_internal.h +++ b/src/xend_internal.h @@ -219,6 +219,8 @@ int xenDaemonInit (void); virDomainPtr xenDaemonLookupByID(virConnectPtr conn, int id); virDomainPtr xenDaemonLookupByUUID(virConnectPtr conn, const unsigned char *uuid); virDomainPtr xenDaemonLookupByName(virConnectPtr conn, const char *domname); +int xenDaemonDomainMigratePrepare (virConnectPtr dconn, char **cookie, int *cookielen, const char *uri_in, char **uri_out, unsigned long flags, const char *dname, unsigned long resource); +int xenDaemonDomainMigratePerform (virDomainPtr domain, const char *cookie, int cookielen, const char *uri, unsigned long flags, const char *dname, unsigned long resource); #ifdef __cplusplus }