[libvirt] [PATCH 00/21] Make migration APIs more extensible

Current migration APIs support a fixed set of optional parameters and whenever there is a need for a new parameter, a whole bunch of new internal and external APIs have to be introduced. This patch set introduces new extensible APIs that use virTypedParameters for passing optional parameters to a migration protocol. Thus making it possible to introduce new parameters without the need for new APIs. The first 7 patches provide some basic infrastructure needed by the following patches. Patches 8 to 19 add support for the new extensible APIs and patches 20 and 21 make use of the new APIs to introduce additional migration parameter. Have fun. Jiri Denemark (21): 1 Rename virTypedParameterArrayValidate as virTypedParamsValidate 2 util: Emit proper error code in virTypedParamsValidate 3 Introduce virTypedParamsCheck internal API 4 Introduce virTypedParamsReplaceString internal API 5 Introduce VIR_TYPED_PARAMS_DEBUG macro for dumping typed params 6 Log input type parameters in API entry points 7 Introduce virTypedParamsCopy internal API 8 Introduce migration parameters 9 New internal migration APIs with extensible parameters 10 Implement extensible migration APIs in remote driver 11 Implement extensible migration APIs in qemu driver 12 qemu: Move internals of Begin phase to qemu_migration.c 13 qemu: Move internals of Prepare phase to qemu_migration.c 14 qemu: Move internals of Confirm phase to qemu_migration.c 15 Adapt virDomainMigrateVersion3 for extensible migration APIs 16 Adapt virDomainMigratePeer2Peer for extensible migration APIs 17 Extensible migration APIs 18 python: Add bindings for extensible migration APIs 19 virsh: Use extensible migration APIs 20 Introduce VIR_MIGRATE_PARAM_GRAPHICS_URI parameter 21 qemu: Implement support for VIR_MIGRATE_PARAM_GRAPHICS_URI daemon/remote.c | 331 ++++++++++++- docs/apibuild.py | 11 +- docs/hvsupport.pl | 7 + include/libvirt/libvirt.h.in | 87 ++++ python/generator.py | 2 + python/libvirt-override-api.xml | 18 + python/libvirt-override.c | 218 +++++++++ src/driver.h | 67 +++ src/esx/esx_driver.c | 24 +- src/libvirt.c | 1022 ++++++++++++++++++++++++++++++++++----- src/libvirt_internal.h | 59 +++ src/libvirt_private.syms | 14 +- src/libvirt_public.syms | 6 + src/libxl/libxl_driver.c | 12 +- src/lxc/lxc_driver.c | 40 +- src/nodeinfo.c | 16 +- src/openvz/openvz_driver.c | 16 +- src/qemu/qemu_driver.c | 633 ++++++++++++------------ src/qemu/qemu_migration.c | 730 ++++++++++++++++++++-------- src/qemu/qemu_migration.h | 45 +- src/remote/remote_driver.c | 398 +++++++++++++++ src/remote/remote_protocol.x | 96 +++- src/remote_protocol-structs | 107 ++++ src/test/test_driver.c | 8 +- src/util/virtypedparam.c | 163 ++++++- src/util/virtypedparam.h | 36 +- src/xen/xen_hypervisor.c | 12 +- tools/virsh-domain.c | 88 +++- tools/virsh.pod | 18 +- 29 files changed, 3548 insertions(+), 736 deletions(-) -- 1.8.2.1

--- docs/apibuild.py | 2 +- src/esx/esx_driver.c | 24 ++++----- src/libvirt_private.syms | 2 +- src/libxl/libxl_driver.c | 12 ++--- src/lxc/lxc_driver.c | 40 +++++++-------- src/nodeinfo.c | 16 +++--- src/openvz/openvz_driver.c | 16 +++--- src/qemu/qemu_driver.c | 120 ++++++++++++++++++++++----------------------- src/test/test_driver.c | 8 +-- src/util/virtypedparam.c | 2 +- src/util/virtypedparam.h | 4 +- src/xen/xen_hypervisor.c | 12 ++--- 12 files changed, 129 insertions(+), 129 deletions(-) diff --git a/docs/apibuild.py b/docs/apibuild.py index d17b593..686a234 100755 --- a/docs/apibuild.py +++ b/docs/apibuild.py @@ -64,7 +64,7 @@ ignored_functions = { "virDomainMigrateConfirm3": "private function for migration", "virDomainMigratePrepareTunnel3": "private function for tunnelled migration", "DllMain": "specific function for Win32", - "virTypedParameterArrayValidate": "internal function in virtypedparam.c", + "virTypedParamsValidate": "internal function in virtypedparam.c", "virTypedParameterAssign": "internal function in virtypedparam.c", "virTypedParameterAssignFromStr": "internal function in virtypedparam.c", } diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c index d5e9c2c..a5df810 100644 --- a/src/esx/esx_driver.c +++ b/src/esx/esx_driver.c @@ -3710,14 +3710,14 @@ esxDomainSetSchedulerParametersFlags(virDomainPtr domain, int i; virCheckFlags(0, -1); - if (virTypedParameterArrayValidate(params, nparams, - VIR_DOMAIN_SCHEDULER_RESERVATION, - VIR_TYPED_PARAM_LLONG, - VIR_DOMAIN_SCHEDULER_LIMIT, - VIR_TYPED_PARAM_LLONG, - VIR_DOMAIN_SCHEDULER_SHARES, - VIR_TYPED_PARAM_INT, - NULL) < 0) + if (virTypedParamsValidate(params, nparams, + VIR_DOMAIN_SCHEDULER_RESERVATION, + VIR_TYPED_PARAM_LLONG, + VIR_DOMAIN_SCHEDULER_LIMIT, + VIR_TYPED_PARAM_LLONG, + VIR_DOMAIN_SCHEDULER_SHARES, + VIR_TYPED_PARAM_INT, + NULL) < 0) return -1; if (esxVI_EnsureSession(priv->primary) < 0) { @@ -4874,10 +4874,10 @@ esxDomainSetMemoryParameters(virDomainPtr domain, virTypedParameterPtr params, int i; virCheckFlags(0, -1); - if (virTypedParameterArrayValidate(params, nparams, - VIR_DOMAIN_MEMORY_MIN_GUARANTEE, - VIR_TYPED_PARAM_ULLONG, - NULL) < 0) + if (virTypedParamsValidate(params, nparams, + VIR_DOMAIN_MEMORY_MIN_GUARANTEE, + VIR_TYPED_PARAM_ULLONG, + NULL) < 0) return -1; if (esxVI_EnsureSession(priv->primary) < 0) { diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index b449293..f61fe55 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1897,9 +1897,9 @@ virTPMCreateCancelPath; # util/virtypedparam.h -virTypedParameterArrayValidate; virTypedParameterAssign; virTypedParameterAssignFromStr; +virTypedParamsValidate; # util/viruri.h diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c index 9a7cb20..481a8b1 100644 --- a/src/libxl/libxl_driver.c +++ b/src/libxl/libxl_driver.c @@ -4172,12 +4172,12 @@ libxlDomainSetSchedulerParametersFlags(virDomainPtr dom, int ret = -1; virCheckFlags(0, -1); - if (virTypedParameterArrayValidate(params, nparams, - VIR_DOMAIN_SCHEDULER_WEIGHT, - VIR_TYPED_PARAM_UINT, - VIR_DOMAIN_SCHEDULER_CAP, - VIR_TYPED_PARAM_UINT, - NULL) < 0) + if (virTypedParamsValidate(params, nparams, + VIR_DOMAIN_SCHEDULER_WEIGHT, + VIR_TYPED_PARAM_UINT, + VIR_DOMAIN_SCHEDULER_CAP, + VIR_TYPED_PARAM_UINT, + NULL) < 0) return -1; libxlDriverLock(driver); diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 3d6baf5..218715b 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -752,14 +752,14 @@ lxcDomainSetMemoryParameters(virDomainPtr dom, virLXCDomainObjPrivatePtr priv; virCheckFlags(0, -1); - if (virTypedParameterArrayValidate(params, nparams, - VIR_DOMAIN_MEMORY_HARD_LIMIT, - VIR_TYPED_PARAM_ULLONG, - VIR_DOMAIN_MEMORY_SOFT_LIMIT, - VIR_TYPED_PARAM_ULLONG, - VIR_DOMAIN_MEMORY_SWAP_HARD_LIMIT, - VIR_TYPED_PARAM_ULLONG, - NULL) < 0) + if (virTypedParamsValidate(params, nparams, + VIR_DOMAIN_MEMORY_HARD_LIMIT, + VIR_TYPED_PARAM_ULLONG, + VIR_DOMAIN_MEMORY_SOFT_LIMIT, + VIR_TYPED_PARAM_ULLONG, + VIR_DOMAIN_MEMORY_SWAP_HARD_LIMIT, + VIR_TYPED_PARAM_ULLONG, + NULL) < 0) return -1; lxcDriverLock(driver); @@ -1732,14 +1732,14 @@ lxcDomainSetSchedulerParametersFlags(virDomainPtr dom, virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | VIR_DOMAIN_AFFECT_CONFIG, -1); - if (virTypedParameterArrayValidate(params, nparams, - VIR_DOMAIN_SCHEDULER_CPU_SHARES, - VIR_TYPED_PARAM_ULLONG, - VIR_DOMAIN_SCHEDULER_VCPU_PERIOD, - VIR_TYPED_PARAM_ULLONG, - VIR_DOMAIN_SCHEDULER_VCPU_QUOTA, - VIR_TYPED_PARAM_LLONG, - NULL) < 0) + if (virTypedParamsValidate(params, nparams, + VIR_DOMAIN_SCHEDULER_CPU_SHARES, + VIR_TYPED_PARAM_ULLONG, + VIR_DOMAIN_SCHEDULER_VCPU_PERIOD, + VIR_TYPED_PARAM_ULLONG, + VIR_DOMAIN_SCHEDULER_VCPU_QUOTA, + VIR_TYPED_PARAM_LLONG, + NULL) < 0) return -1; lxcDriverLock(driver); @@ -1979,10 +1979,10 @@ lxcDomainSetBlkioParameters(virDomainPtr dom, virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | VIR_DOMAIN_AFFECT_CONFIG, -1); - if (virTypedParameterArrayValidate(params, nparams, - VIR_DOMAIN_BLKIO_WEIGHT, - VIR_TYPED_PARAM_UINT, - NULL) < 0) + if (virTypedParamsValidate(params, nparams, + VIR_DOMAIN_BLKIO_WEIGHT, + VIR_TYPED_PARAM_UINT, + NULL) < 0) return -1; lxcDriverLock(driver); diff --git a/src/nodeinfo.c b/src/nodeinfo.c index d8375ab..a50f892 100644 --- a/src/nodeinfo.c +++ b/src/nodeinfo.c @@ -1196,14 +1196,14 @@ nodeSetMemoryParameters(virTypedParameterPtr params ATTRIBUTE_UNUSED, int i; int rc; - if (virTypedParameterArrayValidate(params, nparams, - VIR_NODE_MEMORY_SHARED_PAGES_TO_SCAN, - VIR_TYPED_PARAM_UINT, - VIR_NODE_MEMORY_SHARED_SLEEP_MILLISECS, - VIR_TYPED_PARAM_UINT, - VIR_NODE_MEMORY_SHARED_MERGE_ACROSS_NODES, - VIR_TYPED_PARAM_UINT, - NULL) < 0) + if (virTypedParamsValidate(params, nparams, + VIR_NODE_MEMORY_SHARED_PAGES_TO_SCAN, + VIR_TYPED_PARAM_UINT, + VIR_NODE_MEMORY_SHARED_SLEEP_MILLISECS, + VIR_TYPED_PARAM_UINT, + VIR_NODE_MEMORY_SHARED_MERGE_ACROSS_NODES, + VIR_TYPED_PARAM_UINT, + NULL) < 0) return -1; if (!nodeMemoryParametersIsAllSupported(params, nparams)) diff --git a/src/openvz/openvz_driver.c b/src/openvz/openvz_driver.c index d8abe9c..d04e3ba 100644 --- a/src/openvz/openvz_driver.c +++ b/src/openvz/openvz_driver.c @@ -1871,14 +1871,14 @@ openvzDomainSetMemoryParameters(virDomainPtr domain, goto cleanup; virCheckFlags(0, -1); - if (virTypedParameterArrayValidate(params, nparams, - VIR_DOMAIN_MEMORY_HARD_LIMIT, - VIR_TYPED_PARAM_ULLONG, - VIR_DOMAIN_MEMORY_SOFT_LIMIT, - VIR_TYPED_PARAM_ULLONG, - VIR_DOMAIN_MEMORY_MIN_GUARANTEE, - VIR_TYPED_PARAM_ULLONG, - NULL) < 0) + if (virTypedParamsValidate(params, nparams, + VIR_DOMAIN_MEMORY_HARD_LIMIT, + VIR_TYPED_PARAM_ULLONG, + VIR_DOMAIN_MEMORY_SOFT_LIMIT, + VIR_TYPED_PARAM_ULLONG, + VIR_DOMAIN_MEMORY_MIN_GUARANTEE, + VIR_TYPED_PARAM_ULLONG, + NULL) < 0) return -1; for (i = 0; i < nparams; i++) { diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 9b738e0..a682e36 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7231,12 +7231,12 @@ qemuDomainSetBlkioParameters(virDomainPtr dom, virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | VIR_DOMAIN_AFFECT_CONFIG, -1); - if (virTypedParameterArrayValidate(params, nparams, - VIR_DOMAIN_BLKIO_WEIGHT, - VIR_TYPED_PARAM_UINT, - VIR_DOMAIN_BLKIO_DEVICE_WEIGHT, - VIR_TYPED_PARAM_STRING, - NULL) < 0) + if (virTypedParamsValidate(params, nparams, + VIR_DOMAIN_BLKIO_WEIGHT, + VIR_TYPED_PARAM_UINT, + VIR_DOMAIN_BLKIO_DEVICE_WEIGHT, + VIR_TYPED_PARAM_STRING, + NULL) < 0) return -1; if (!(vm = qemuDomObjFromDomain(dom))) @@ -7558,14 +7558,14 @@ qemuDomainSetMemoryParameters(virDomainPtr dom, virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | VIR_DOMAIN_AFFECT_CONFIG, -1); - if (virTypedParameterArrayValidate(params, nparams, - VIR_DOMAIN_MEMORY_HARD_LIMIT, - VIR_TYPED_PARAM_ULLONG, - VIR_DOMAIN_MEMORY_SOFT_LIMIT, - VIR_TYPED_PARAM_ULLONG, - VIR_DOMAIN_MEMORY_SWAP_HARD_LIMIT, - VIR_TYPED_PARAM_ULLONG, - NULL) < 0) + if (virTypedParamsValidate(params, nparams, + VIR_DOMAIN_MEMORY_HARD_LIMIT, + VIR_TYPED_PARAM_ULLONG, + VIR_DOMAIN_MEMORY_SOFT_LIMIT, + VIR_TYPED_PARAM_ULLONG, + VIR_DOMAIN_MEMORY_SWAP_HARD_LIMIT, + VIR_TYPED_PARAM_ULLONG, + NULL) < 0) return -1; @@ -7836,12 +7836,12 @@ qemuDomainSetNumaParameters(virDomainPtr dom, virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | VIR_DOMAIN_AFFECT_CONFIG, -1); - if (virTypedParameterArrayValidate(params, nparams, - VIR_DOMAIN_NUMA_MODE, - VIR_TYPED_PARAM_INT, - VIR_DOMAIN_NUMA_NODESET, - VIR_TYPED_PARAM_STRING, - NULL) < 0) + if (virTypedParamsValidate(params, nparams, + VIR_DOMAIN_NUMA_MODE, + VIR_TYPED_PARAM_INT, + VIR_DOMAIN_NUMA_NODESET, + VIR_TYPED_PARAM_STRING, + NULL) < 0) return -1; if (!(vm = qemuDomObjFromDomain(dom))) @@ -8170,18 +8170,18 @@ qemuDomainSetSchedulerParametersFlags(virDomainPtr dom, virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | VIR_DOMAIN_AFFECT_CONFIG, -1); - if (virTypedParameterArrayValidate(params, nparams, - VIR_DOMAIN_SCHEDULER_CPU_SHARES, - VIR_TYPED_PARAM_ULLONG, - VIR_DOMAIN_SCHEDULER_VCPU_PERIOD, - VIR_TYPED_PARAM_ULLONG, - VIR_DOMAIN_SCHEDULER_VCPU_QUOTA, - VIR_TYPED_PARAM_LLONG, - VIR_DOMAIN_SCHEDULER_EMULATOR_PERIOD, - VIR_TYPED_PARAM_ULLONG, - VIR_DOMAIN_SCHEDULER_EMULATOR_QUOTA, - VIR_TYPED_PARAM_LLONG, - NULL) < 0) + if (virTypedParamsValidate(params, nparams, + VIR_DOMAIN_SCHEDULER_CPU_SHARES, + VIR_TYPED_PARAM_ULLONG, + VIR_DOMAIN_SCHEDULER_VCPU_PERIOD, + VIR_TYPED_PARAM_ULLONG, + VIR_DOMAIN_SCHEDULER_VCPU_QUOTA, + VIR_TYPED_PARAM_LLONG, + VIR_DOMAIN_SCHEDULER_EMULATOR_PERIOD, + VIR_TYPED_PARAM_ULLONG, + VIR_DOMAIN_SCHEDULER_EMULATOR_QUOTA, + VIR_TYPED_PARAM_LLONG, + NULL) < 0) return -1; if (!(vm = qemuDomObjFromDomain(dom))) @@ -8964,20 +8964,20 @@ qemuDomainSetInterfaceParameters(virDomainPtr dom, virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | VIR_DOMAIN_AFFECT_CONFIG, -1); - if (virTypedParameterArrayValidate(params, nparams, - VIR_DOMAIN_BANDWIDTH_IN_AVERAGE, - VIR_TYPED_PARAM_UINT, - VIR_DOMAIN_BANDWIDTH_IN_PEAK, - VIR_TYPED_PARAM_UINT, - VIR_DOMAIN_BANDWIDTH_IN_BURST, - VIR_TYPED_PARAM_UINT, - VIR_DOMAIN_BANDWIDTH_OUT_AVERAGE, - VIR_TYPED_PARAM_UINT, - VIR_DOMAIN_BANDWIDTH_OUT_PEAK, - VIR_TYPED_PARAM_UINT, - VIR_DOMAIN_BANDWIDTH_OUT_BURST, - VIR_TYPED_PARAM_UINT, - NULL) < 0) + if (virTypedParamsValidate(params, nparams, + VIR_DOMAIN_BANDWIDTH_IN_AVERAGE, + VIR_TYPED_PARAM_UINT, + VIR_DOMAIN_BANDWIDTH_IN_PEAK, + VIR_TYPED_PARAM_UINT, + VIR_DOMAIN_BANDWIDTH_IN_BURST, + VIR_TYPED_PARAM_UINT, + VIR_DOMAIN_BANDWIDTH_OUT_AVERAGE, + VIR_TYPED_PARAM_UINT, + VIR_DOMAIN_BANDWIDTH_OUT_PEAK, + VIR_TYPED_PARAM_UINT, + VIR_DOMAIN_BANDWIDTH_OUT_BURST, + VIR_TYPED_PARAM_UINT, + NULL) < 0) return -1; if (!(vm = qemuDomObjFromDomain(dom))) @@ -14050,20 +14050,20 @@ qemuDomainSetBlockIoTune(virDomainPtr dom, virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | VIR_DOMAIN_AFFECT_CONFIG, -1); - if (virTypedParameterArrayValidate(params, nparams, - VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_BYTES_SEC, - VIR_TYPED_PARAM_ULLONG, - VIR_DOMAIN_BLOCK_IOTUNE_READ_BYTES_SEC, - VIR_TYPED_PARAM_ULLONG, - VIR_DOMAIN_BLOCK_IOTUNE_WRITE_BYTES_SEC, - VIR_TYPED_PARAM_ULLONG, - VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_IOPS_SEC, - VIR_TYPED_PARAM_ULLONG, - VIR_DOMAIN_BLOCK_IOTUNE_READ_IOPS_SEC, - VIR_TYPED_PARAM_ULLONG, - VIR_DOMAIN_BLOCK_IOTUNE_WRITE_IOPS_SEC, - VIR_TYPED_PARAM_ULLONG, - NULL) < 0) + if (virTypedParamsValidate(params, nparams, + VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_BYTES_SEC, + VIR_TYPED_PARAM_ULLONG, + VIR_DOMAIN_BLOCK_IOTUNE_READ_BYTES_SEC, + VIR_TYPED_PARAM_ULLONG, + VIR_DOMAIN_BLOCK_IOTUNE_WRITE_BYTES_SEC, + VIR_TYPED_PARAM_ULLONG, + VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_IOPS_SEC, + VIR_TYPED_PARAM_ULLONG, + VIR_DOMAIN_BLOCK_IOTUNE_READ_IOPS_SEC, + VIR_TYPED_PARAM_ULLONG, + VIR_DOMAIN_BLOCK_IOTUNE_WRITE_IOPS_SEC, + VIR_TYPED_PARAM_ULLONG, + NULL) < 0) return -1; memset(&info, 0, sizeof(info)); diff --git a/src/test/test_driver.c b/src/test/test_driver.c index f1cdd92..30c2194 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -2784,10 +2784,10 @@ testDomainSetSchedulerParametersFlags(virDomainPtr domain, int ret = -1, i; virCheckFlags(0, -1); - if (virTypedParameterArrayValidate(params, nparams, - VIR_DOMAIN_SCHEDULER_WEIGHT, - VIR_TYPED_PARAM_UINT, - NULL) < 0) + if (virTypedParamsValidate(params, nparams, + VIR_DOMAIN_SCHEDULER_WEIGHT, + VIR_TYPED_PARAM_UINT, + NULL) < 0) return -1; testDriverLock(privconn); diff --git a/src/util/virtypedparam.c b/src/util/virtypedparam.c index bf374a6..8b2211f 100644 --- a/src/util/virtypedparam.c +++ b/src/util/virtypedparam.c @@ -48,7 +48,7 @@ VIR_ENUM_IMPL(virTypedParameter, VIR_TYPED_PARAM_LAST, * parameters. Return 0 on success, -1 on failure with error message * already issued. */ int -virTypedParameterArrayValidate(virTypedParameterPtr params, int nparams, ...) +virTypedParamsValidate(virTypedParameterPtr params, int nparams, ...) { va_list ap; int ret = -1; diff --git a/src/util/virtypedparam.h b/src/util/virtypedparam.h index c777a55..8f6bd1b 100644 --- a/src/util/virtypedparam.h +++ b/src/util/virtypedparam.h @@ -25,8 +25,8 @@ # include "internal.h" -int virTypedParameterArrayValidate(virTypedParameterPtr params, int nparams, - /* const char *name, int type ... */ ...) +int virTypedParamsValidate(virTypedParameterPtr params, int nparams, + /* const char *name, int type ... */ ...) ATTRIBUTE_SENTINEL ATTRIBUTE_RETURN_CHECK; int virTypedParameterAssign(virTypedParameterPtr param, const char *name, diff --git a/src/xen/xen_hypervisor.c b/src/xen/xen_hypervisor.c index cfc8139..9d9c3ee 100644 --- a/src/xen/xen_hypervisor.c +++ b/src/xen/xen_hypervisor.c @@ -1277,12 +1277,12 @@ xenHypervisorSetSchedulerParameters(virConnectPtr conn, return 0; } - if (virTypedParameterArrayValidate(params, nparams, - VIR_DOMAIN_SCHEDULER_WEIGHT, - VIR_TYPED_PARAM_UINT, - VIR_DOMAIN_SCHEDULER_CAP, - VIR_TYPED_PARAM_UINT, - NULL) < 0) + if (virTypedParamsValidate(params, nparams, + VIR_DOMAIN_SCHEDULER_WEIGHT, + VIR_TYPED_PARAM_UINT, + VIR_DOMAIN_SCHEDULER_CAP, + VIR_TYPED_PARAM_UINT, + NULL) < 0) return -1; /* -- 1.8.2.1

When unsupported parameter is passed to virTypedParamsValidate, VIR_ERR_ARGUMENT_UNSUPPORTED should be returned rather than VIR_ERR_INVALID_ARG, which is more appropriate for supported parameters used incorrectly. --- src/util/virtypedparam.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/virtypedparam.c b/src/util/virtypedparam.c index 8b2211f..eef9e30 100644 --- a/src/util/virtypedparam.c +++ b/src/util/virtypedparam.c @@ -87,7 +87,7 @@ virTypedParamsValidate(virTypedParameterPtr params, int nparams, ...) name = va_arg(ap, const char *); } if (!name) { - virReportError(VIR_ERR_INVALID_ARG, + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, _("parameter '%s' not supported"), params[i].field); goto cleanup; -- 1.8.2.1

This API is useful for checking whether only a specific subset of supported typed parameters were passed. --- docs/apibuild.py | 1 + src/libvirt_private.syms | 1 + src/util/virtypedparam.c | 26 ++++++++++++++++++++++++++ src/util/virtypedparam.h | 5 +++++ 4 files changed, 33 insertions(+) diff --git a/docs/apibuild.py b/docs/apibuild.py index 686a234..c816197 100755 --- a/docs/apibuild.py +++ b/docs/apibuild.py @@ -67,6 +67,7 @@ ignored_functions = { "virTypedParamsValidate": "internal function in virtypedparam.c", "virTypedParameterAssign": "internal function in virtypedparam.c", "virTypedParameterAssignFromStr": "internal function in virtypedparam.c", + "virTypedParamsCheck": "internal function in virtypedparam.c", } ignored_macros = { diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index f61fe55..ae9f356 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1899,6 +1899,7 @@ virTPMCreateCancelPath; # util/virtypedparam.h virTypedParameterAssign; virTypedParameterAssignFromStr; +virTypedParamsCheck; virTypedParamsValidate; diff --git a/src/util/virtypedparam.c b/src/util/virtypedparam.c index eef9e30..825148b 100644 --- a/src/util/virtypedparam.c +++ b/src/util/virtypedparam.c @@ -109,6 +109,32 @@ cleanup: } +/* Check if params contains only specified parameter names. Return true if + * only specified names are present in params, false if params contains any + * unspecified parameter name. */ +bool +virTypedParamsCheck(virTypedParameterPtr params, + int nparams, + const char **names, + int nnames) +{ + int i, j; + + for (i = 0; i < nparams; i++) { + bool found = false; + for (j = 0; j < nnames; j++) { + if (STREQ(params[i].field, names[j])) { + found = true; + break; + } + } + if (!found) + return false; + } + + return true; +} + /* Assign name, type, and the appropriately typed arg to param; in the * case of a string, the caller is assumed to have malloc'd a string, * or can pass NULL to have this function malloc an empty string. diff --git a/src/util/virtypedparam.h b/src/util/virtypedparam.h index 8f6bd1b..b0f8522 100644 --- a/src/util/virtypedparam.h +++ b/src/util/virtypedparam.h @@ -29,6 +29,11 @@ int virTypedParamsValidate(virTypedParameterPtr params, int nparams, /* const char *name, int type ... */ ...) ATTRIBUTE_SENTINEL ATTRIBUTE_RETURN_CHECK; +bool virTypedParamsCheck(virTypedParameterPtr params, + int nparams, + const char **names, + int nnames); + int virTypedParameterAssign(virTypedParameterPtr param, const char *name, int type, /* TYPE arg */ ...) ATTRIBUTE_RETURN_CHECK; -- 1.8.2.1

--- src/libvirt_private.syms | 1 + src/util/virtypedparam.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++ src/util/virtypedparam.h | 5 +++++ 3 files changed, 64 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index ae9f356..4e01073 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1900,6 +1900,7 @@ virTPMCreateCancelPath; virTypedParameterAssign; virTypedParameterAssignFromStr; virTypedParamsCheck; +virTypedParamsReplaceString; virTypedParamsValidate; diff --git a/src/util/virtypedparam.c b/src/util/virtypedparam.c index 825148b..760bb05 100644 --- a/src/util/virtypedparam.c +++ b/src/util/virtypedparam.c @@ -285,6 +285,64 @@ cleanup: } +/** + * virTypedParamsReplaceString: + * @params: pointer to the array of typed parameters + * @nparams: number of parameters in the @params array + * @name: name of the parameter to set + * @value: the value to store into the parameter + * + * Sets new value @value to parameter called @name with char * type. If the + * parameter does not exist yet in @params, it is automatically created and + * @naprams is incremented by one. Otherwise current value of the parameter + * is freed on success. The function creates its own copy of @value string, + * which needs to be freed using virTypedParamsFree or virTypedParamsClear. + * + * Returns 0 on success, -1 on error. + */ +int +virTypedParamsReplaceString(virTypedParameterPtr *params, + int *nparams, + const char *name, + const char *value) +{ + char *str = NULL; + char *old = NULL; + size_t n = *nparams; + virTypedParameterPtr param; + + virResetLastError(); + + param = virTypedParamsGet(*params, n, name); + if (param) { + old = param->value.s; + } else if (VIR_EXPAND_N(*params, n, 1) < 0) { + virReportOOMError(); + goto error; + } else { + param = *params + n - 1; + } + + if (VIR_STRDUP(str, value) < 0) + goto error; + + if (virTypedParameterAssign(param, name, + VIR_TYPED_PARAM_STRING, str) < 0) { + param->value.s = old; + VIR_FREE(str); + goto error; + } + VIR_FREE(old); + + *nparams = n; + return 0; + +error: + virDispatchError(NULL); + return -1; +} + + /* The following APIs are public and their signature may never change. */ /** diff --git a/src/util/virtypedparam.h b/src/util/virtypedparam.h index b0f8522..6eb61c4 100644 --- a/src/util/virtypedparam.h +++ b/src/util/virtypedparam.h @@ -44,4 +44,9 @@ int virTypedParameterAssignFromStr(virTypedParameterPtr param, const char *val) ATTRIBUTE_RETURN_CHECK; +int virTypedParamsReplaceString(virTypedParameterPtr *params, + int *nparams, + const char *name, + const char *value); + #endif /* __VIR_TYPED_PARAM_H */ -- 1.8.2.1

On 18.06.2013 16:05, Jiri Denemark wrote:
--- src/libvirt_private.syms | 1 + src/util/virtypedparam.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++ src/util/virtypedparam.h | 5 +++++ 3 files changed, 64 insertions(+)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index ae9f356..4e01073 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1900,6 +1900,7 @@ virTPMCreateCancelPath; virTypedParameterAssign; virTypedParameterAssignFromStr; virTypedParamsCheck; +virTypedParamsReplaceString; virTypedParamsValidate;
diff --git a/src/util/virtypedparam.c b/src/util/virtypedparam.c index 825148b..760bb05 100644 --- a/src/util/virtypedparam.c +++ b/src/util/virtypedparam.c @@ -285,6 +285,64 @@ cleanup: }
+/** + * virTypedParamsReplaceString: + * @params: pointer to the array of typed parameters + * @nparams: number of parameters in the @params array + * @name: name of the parameter to set + * @value: the value to store into the parameter + * + * Sets new value @value to parameter called @name with char * type. If the + * parameter does not exist yet in @params, it is automatically created and + * @naprams is incremented by one. Otherwise current value of the parameter + * is freed on success. The function creates its own copy of @value string, + * which needs to be freed using virTypedParamsFree or virTypedParamsClear. + * + * Returns 0 on success, -1 on error. + */ +int +virTypedParamsReplaceString(virTypedParameterPtr *params, + int *nparams, + const char *name, + const char *value) +{ + char *str = NULL; + char *old = NULL; + size_t n = *nparams; + virTypedParameterPtr param; + + virResetLastError(); + + param = virTypedParamsGet(*params, n, name);
You need to check if the param is a type of VIR_TYPED_PARAM_STRING.
+ if (param) { + old = param->value.s; + } else if (VIR_EXPAND_N(*params, n, 1) < 0) { + virReportOOMError(); + goto error; + } else { + param = *params + n - 1; + }
Please no. I'd rather see this as: if (param) { old = param->value.s; } else { if (VIR_EXPAND_N(*params, n, 1) < 0) { virReportOOMError(); goto error; } param = *params + n - 1; }
+ + if (VIR_STRDUP(str, value) < 0) + goto error; + + if (virTypedParameterAssign(param, name, + VIR_TYPED_PARAM_STRING, str) < 0) { + param->value.s = old; + VIR_FREE(str); + goto error; + } + VIR_FREE(old); + + *nparams = n; + return 0; + +error: + virDispatchError(NULL); + return -1; +} + + /* The following APIs are public and their signature may never change. */
/** diff --git a/src/util/virtypedparam.h b/src/util/virtypedparam.h index b0f8522..6eb61c4 100644 --- a/src/util/virtypedparam.h +++ b/src/util/virtypedparam.h @@ -44,4 +44,9 @@ int virTypedParameterAssignFromStr(virTypedParameterPtr param, const char *val) ATTRIBUTE_RETURN_CHECK;
+int virTypedParamsReplaceString(virTypedParameterPtr *params, + int *nparams, + const char *name, + const char *value); + #endif /* __VIR_TYPED_PARAM_H */

All APIs that take typed parameters are only using params address in their entry point debug messages. With the new VIR_TYPED_PARAMS_DEBUG macro, all functions can easily log all individual typed parameters passed to them. --- docs/apibuild.py | 1 + src/libvirt_private.syms | 3 +++ src/util/virtypedparam.c | 41 ++++++++++++++++++++++++++++++++++++++++- src/util/virtypedparam.h | 18 ++++++++++++++++++ 4 files changed, 62 insertions(+), 1 deletion(-) diff --git a/docs/apibuild.py b/docs/apibuild.py index c816197..e0996bf 100755 --- a/docs/apibuild.py +++ b/docs/apibuild.py @@ -67,6 +67,7 @@ ignored_functions = { "virTypedParamsValidate": "internal function in virtypedparam.c", "virTypedParameterAssign": "internal function in virtypedparam.c", "virTypedParameterAssignFromStr": "internal function in virtypedparam.c", + "virTypedParameterToString": "internal function in virtypedparam.c", "virTypedParamsCheck": "internal function in virtypedparam.c", } diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 4e01073..0e0c3bc 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1899,6 +1899,9 @@ virTPMCreateCancelPath; # util/virtypedparam.h virTypedParameterAssign; virTypedParameterAssignFromStr; +virTypedParameterToString; +virTypedParameterTypeFromString; +virTypedParameterTypeToString; virTypedParamsCheck; virTypedParamsReplaceString; virTypedParamsValidate; diff --git a/src/util/virtypedparam.c b/src/util/virtypedparam.c index 760bb05..8b18a5a 100644 --- a/src/util/virtypedparam.c +++ b/src/util/virtypedparam.c @@ -31,7 +31,6 @@ #define VIR_FROM_THIS VIR_FROM_NONE -VIR_ENUM_DECL(virTypedParameter) VIR_ENUM_IMPL(virTypedParameter, VIR_TYPED_PARAM_LAST, "unknown", "int", @@ -135,6 +134,46 @@ virTypedParamsCheck(virTypedParameterPtr params, return true; } +char * +virTypedParameterToString(virTypedParameterPtr param) +{ + char *value; + int ret = -1; + + switch (param->type) { + case VIR_TYPED_PARAM_INT: + ret = virAsprintf(&value, "%d", param->value.i); + break; + case VIR_TYPED_PARAM_UINT: + ret = virAsprintf(&value, "%u", param->value.ui); + break; + case VIR_TYPED_PARAM_LLONG: + ret = virAsprintf(&value, "%lld", param->value.l); + break; + case VIR_TYPED_PARAM_ULLONG: + ret = virAsprintf(&value, "%llu", param->value.ul); + break; + case VIR_TYPED_PARAM_DOUBLE: + ret = virAsprintf(&value, "%g", param->value.d); + break; + case VIR_TYPED_PARAM_BOOLEAN: + ret = virAsprintf(&value, "%d", param->value.b); + break; + case VIR_TYPED_PARAM_STRING: + ret = VIR_STRDUP(value, param->value.s); + break; + default: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected type %d for field %s"), + param->type, param->field); + } + + if (ret < 0) + return NULL; + else + return value; +} + /* Assign name, type, and the appropriately typed arg to param; in the * case of a string, the caller is assumed to have malloc'd a string, * or can pass NULL to have this function malloc an empty string. diff --git a/src/util/virtypedparam.h b/src/util/virtypedparam.h index 6eb61c4..ca062c0 100644 --- a/src/util/virtypedparam.h +++ b/src/util/virtypedparam.h @@ -24,6 +24,7 @@ # define __VIR_TYPED_PARAM_H_ # include "internal.h" +# include "virutil.h" int virTypedParamsValidate(virTypedParameterPtr params, int nparams, /* const char *name, int type ... */ ...) @@ -49,4 +50,21 @@ int virTypedParamsReplaceString(virTypedParameterPtr *params, const char *name, const char *value); +char *virTypedParameterToString(virTypedParameterPtr param); + +VIR_ENUM_DECL(virTypedParameter) + +# define VIR_TYPED_PARAMS_DEBUG(params, nparams) \ + do { \ + int _i; \ + for (_i = 0; _i < (nparams); _i++) { \ + char *_value = virTypedParameterToString((params) + _i); \ + VIR_DEBUG("params[\"%s\"]=(%s)%s", \ + (params)[_i].field, \ + virTypedParameterTypeToString((params)[_i].type), \ + NULLSTR(_value)); \ + VIR_FREE(_value); \ + } \ + } while (0) + #endif /* __VIR_TYPED_PARAM_H */ -- 1.8.2.1

On 18.06.2013 16:05, Jiri Denemark wrote:
All APIs that take typed parameters are only using params address in their entry point debug messages. With the new VIR_TYPED_PARAMS_DEBUG macro, all functions can easily log all individual typed parameters passed to them. --- docs/apibuild.py | 1 + src/libvirt_private.syms | 3 +++ src/util/virtypedparam.c | 41 ++++++++++++++++++++++++++++++++++++++++- src/util/virtypedparam.h | 18 ++++++++++++++++++ 4 files changed, 62 insertions(+), 1 deletion(-)
diff --git a/docs/apibuild.py b/docs/apibuild.py index c816197..e0996bf 100755 --- a/docs/apibuild.py +++ b/docs/apibuild.py @@ -67,6 +67,7 @@ ignored_functions = { "virTypedParamsValidate": "internal function in virtypedparam.c", "virTypedParameterAssign": "internal function in virtypedparam.c", "virTypedParameterAssignFromStr": "internal function in virtypedparam.c", + "virTypedParameterToString": "internal function in virtypedparam.c", "virTypedParamsCheck": "internal function in virtypedparam.c", }
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 4e01073..0e0c3bc 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1899,6 +1899,9 @@ virTPMCreateCancelPath; # util/virtypedparam.h virTypedParameterAssign; virTypedParameterAssignFromStr; +virTypedParameterToString; +virTypedParameterTypeFromString; +virTypedParameterTypeToString; virTypedParamsCheck; virTypedParamsReplaceString; virTypedParamsValidate; diff --git a/src/util/virtypedparam.c b/src/util/virtypedparam.c index 760bb05..8b18a5a 100644 --- a/src/util/virtypedparam.c +++ b/src/util/virtypedparam.c @@ -31,7 +31,6 @@
#define VIR_FROM_THIS VIR_FROM_NONE
-VIR_ENUM_DECL(virTypedParameter) VIR_ENUM_IMPL(virTypedParameter, VIR_TYPED_PARAM_LAST, "unknown", "int", @@ -135,6 +134,46 @@ virTypedParamsCheck(virTypedParameterPtr params, return true; }
+char * +virTypedParameterToString(virTypedParameterPtr param) +{ + char *value; + int ret = -1; + + switch (param->type) { + case VIR_TYPED_PARAM_INT: + ret = virAsprintf(&value, "%d", param->value.i); + break; + case VIR_TYPED_PARAM_UINT: + ret = virAsprintf(&value, "%u", param->value.ui); + break; + case VIR_TYPED_PARAM_LLONG: + ret = virAsprintf(&value, "%lld", param->value.l); + break; + case VIR_TYPED_PARAM_ULLONG: + ret = virAsprintf(&value, "%llu", param->value.ul); + break; + case VIR_TYPED_PARAM_DOUBLE: + ret = virAsprintf(&value, "%g", param->value.d); + break; + case VIR_TYPED_PARAM_BOOLEAN: + ret = virAsprintf(&value, "%d", param->value.b);
virAsprintf() doesn't report OOM error (yet) ...
+ break; + case VIR_TYPED_PARAM_STRING: + ret = VIR_STRDUP(value, param->value.s);
... while VIR_STRDUP does.
+ break; + default: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected type %d for field %s"), + param->type, param->field); + } + + if (ret < 0) + return NULL;
But I guess it doesn't matter, does it (esp. when each call is followed by virResetLastError();)
+ else + return value; +} + /* Assign name, type, and the appropriately typed arg to param; in the * case of a string, the caller is assumed to have malloc'd a string, * or can pass NULL to have this function malloc an empty string. diff --git a/src/util/virtypedparam.h b/src/util/virtypedparam.h index 6eb61c4..ca062c0 100644 --- a/src/util/virtypedparam.h +++ b/src/util/virtypedparam.h @@ -24,6 +24,7 @@ # define __VIR_TYPED_PARAM_H_
# include "internal.h" +# include "virutil.h"
int virTypedParamsValidate(virTypedParameterPtr params, int nparams, /* const char *name, int type ... */ ...) @@ -49,4 +50,21 @@ int virTypedParamsReplaceString(virTypedParameterPtr *params, const char *name, const char *value);
+char *virTypedParameterToString(virTypedParameterPtr param); + +VIR_ENUM_DECL(virTypedParameter) + +# define VIR_TYPED_PARAMS_DEBUG(params, nparams) \ + do { \
I'd rather check (params) != NULL here as it might avoid SIGSEGV in the next patch if user calls i.e. virDomainSetSchedulerParametersFlags(dom, NULL, 10, 0); But that's not a show stopper as users might pass invalid pointer anyway.
+ int _i; \ + for (_i = 0; _i < (nparams); _i++) { \ + char *_value = virTypedParameterToString((params) + _i); \ + VIR_DEBUG("params[\"%s\"]=(%s)%s", \ + (params)[_i].field, \ + virTypedParameterTypeToString((params)[_i].type), \ + NULLSTR(_value)); \ + VIR_FREE(_value); \ + } \ + } while (0) + #endif /* __VIR_TYPED_PARAM_H */

--- src/libvirt.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/libvirt.c b/src/libvirt.c index db120b7..a6efaf8 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -65,6 +65,7 @@ #include "virthread.h" #include "virstring.h" #include "virutil.h" +#include "virtypedparam.h" #ifdef WITH_TEST # include "test/test_driver.h" @@ -3839,6 +3840,7 @@ virDomainSetMemoryParameters(virDomainPtr domain, VIR_DOMAIN_DEBUG(domain, "params=%p, nparams=%d, flags=%x", params, nparams, flags); + VIR_TYPED_PARAMS_DEBUG(params, nparams); virResetLastError(); @@ -3981,6 +3983,7 @@ virDomainSetNumaParameters(virDomainPtr domain, VIR_DOMAIN_DEBUG(domain, "params=%p, nparams=%d, flags=%x", params, nparams, flags); + VIR_TYPED_PARAMS_DEBUG(params, nparams); virResetLastError(); @@ -4108,6 +4111,7 @@ virDomainSetBlkioParameters(virDomainPtr domain, VIR_DOMAIN_DEBUG(domain, "params=%p, nparams=%d, flags=%x", params, nparams, flags); + VIR_TYPED_PARAMS_DEBUG(params, nparams); virResetLastError(); @@ -6988,6 +6992,7 @@ virNodeSetMemoryParameters(virConnectPtr conn, { VIR_DEBUG("conn=%p, params=%p, nparams=%d, flags=%x", conn, params, nparams, flags); + VIR_TYPED_PARAMS_DEBUG(params, nparams); virResetLastError(); @@ -7228,6 +7233,7 @@ virDomainSetSchedulerParameters(virDomainPtr domain, virConnectPtr conn; VIR_DOMAIN_DEBUG(domain, "params=%p, nparams=%d", params, nparams); + VIR_TYPED_PARAMS_DEBUG(params, nparams); virResetLastError(); @@ -7292,6 +7298,7 @@ virDomainSetSchedulerParametersFlags(virDomainPtr domain, VIR_DOMAIN_DEBUG(domain, "params=%p, nparams=%d, flags=%x", params, nparams, flags); + VIR_TYPED_PARAMS_DEBUG(params, nparams); virResetLastError(); @@ -7574,6 +7581,7 @@ virDomainSetInterfaceParameters(virDomainPtr domain, VIR_DOMAIN_DEBUG(domain, "device=%s, params=%p, nparams=%d, flags=%x", device, params, nparams, flags); + VIR_TYPED_PARAMS_DEBUG(params, nparams); virResetLastError(); @@ -20415,6 +20423,7 @@ int virDomainSetBlockIoTune(virDomainPtr dom, VIR_DOMAIN_DEBUG(dom, "disk=%s, params=%p, nparams=%d, flags=%x", disk, params, nparams, flags); + VIR_TYPED_PARAMS_DEBUG(params, nparams); virResetLastError(); -- 1.8.2.1

--- docs/apibuild.py | 1 + src/libvirt_private.syms | 1 + src/util/virtypedparam.c | 34 ++++++++++++++++++++++++++++++++++ src/util/virtypedparam.h | 4 ++++ 4 files changed, 40 insertions(+) diff --git a/docs/apibuild.py b/docs/apibuild.py index e0996bf..aca2370 100755 --- a/docs/apibuild.py +++ b/docs/apibuild.py @@ -69,6 +69,7 @@ ignored_functions = { "virTypedParameterAssignFromStr": "internal function in virtypedparam.c", "virTypedParameterToString": "internal function in virtypedparam.c", "virTypedParamsCheck": "internal function in virtypedparam.c", + "virTypedParamsCopy": "internal function in virtypedparam.c", } ignored_macros = { diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 0e0c3bc..fa1041c 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1903,6 +1903,7 @@ virTypedParameterToString; virTypedParameterTypeFromString; virTypedParameterTypeToString; virTypedParamsCheck; +virTypedParamsCopy; virTypedParamsReplaceString; virTypedParamsValidate; diff --git a/src/util/virtypedparam.c b/src/util/virtypedparam.c index 8b18a5a..cf0fde5 100644 --- a/src/util/virtypedparam.c +++ b/src/util/virtypedparam.c @@ -382,6 +382,40 @@ error: } +int +virTypedParamsCopy(virTypedParameterPtr *dst, + virTypedParameterPtr src, + int nparams) +{ + int i; + + *dst = NULL; + if (!src || nparams <= 0) + return 0; + + if (VIR_ALLOC_N(*dst, nparams) < 0) { + virReportOOMError(); + return -1; + } + + for (i = 0; i < nparams; i++) { + ignore_value(virStrcpyStatic((*dst)[i].field, src[i].field)); + (*dst)[i].type = src[i].type; + if (src[i].type == VIR_TYPED_PARAM_STRING) { + if (VIR_STRDUP((*dst)[i].value.s, src[i].value.s) < 0) { + virTypedParamsFree(*dst, i - 1); + *dst = NULL; + return -1; + } + } else { + (*dst)[i].value = src[i].value; + } + } + + return 0; +} + + /* The following APIs are public and their signature may never change. */ /** diff --git a/src/util/virtypedparam.h b/src/util/virtypedparam.h index ca062c0..728cae3 100644 --- a/src/util/virtypedparam.h +++ b/src/util/virtypedparam.h @@ -50,6 +50,10 @@ int virTypedParamsReplaceString(virTypedParameterPtr *params, const char *name, const char *value); +int virTypedParamsCopy(virTypedParameterPtr *dst, + virTypedParameterPtr src, + int nparams); + char *virTypedParameterToString(virTypedParameterPtr param); VIR_ENUM_DECL(virTypedParameter) -- 1.8.2.1

To be used by new migration APIs with extensible set of parameters. --- include/libvirt/libvirt.h.in | 56 ++++++++++++++++++++++++++++++++++++++++++++ src/libvirt_internal.h | 5 ++++ 2 files changed, 61 insertions(+) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index acf3218..35bffea 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1191,6 +1191,62 @@ typedef enum { VIR_MIGRATE_ABORT_ON_ERROR = (1 << 12), /* abort migration on I/O errors happened during migration */ } virDomainMigrateFlags; + +/** + * VIR_MIGRATE_PARAM_URI: + * + * virDomainMigrate* params field: URI to use for initiating domain migration + * as VIR_TYPED_PARAM_STRING. It takes a hypervisor specific format. The + * uri_transports element of the hypervisor capabilities XML includes details + * of the supported URI schemes. When omitted libvirt will auto-generate + * suitable default URI. It is typically only necessary to specify this URI if + * the destination host has multiple interfaces and a specific interface is + * required to transmit migration data. + * + * This filed may not be used when VIR_MIGRATE_TUNNELLED flag is set. + */ +#define VIR_MIGRATE_PARAM_URI "migrate_uri" + +/** + * VIR_MIGRATE_PARAM_DEST_NAME: + * + * virDomainMigrate* params field: the name to be used for the domain on the + * destination host as VIR_TYPED_PARAM_STRING. Omitting this parameter keeps + * the domain name the same. This field is only allowed to be used with + * hypervisors that support domain renaming during migration. + */ +#define VIR_MIGRATE_PARAM_DEST_NAME "destination_name" + +/** + * VIR_MIGRATE_PARAM_DEST_XML: + * + * virDomainMigrate* params field: the new configuration to be used for the + * domain on the destination host as VIR_TYPED_PARAM_STRING. The configuration + * must include an identical set of virtual devices, to ensure a stable guest + * ABI across migration. Only parameters related to host side configuration + * can be changed in the XML. Hypervisors which support this will validate + * this and refuse to allow migration if the provided XML would cause a change + * in the guest ABI. This field cannot be used to rename the domain during + * migration (use VIR_MIGRATE_PARAM_DEST_NAME field for that purpose). Domain + * name in the destination XML must match the original domain name. + * + * Omitting this parameter keeps the original domain configuration. Using this + * field with hypervisors that do not support changing domain configuration + * during migration will result in a failure. + */ +#define VIR_MIGRATE_PARAM_DEST_XML "destination_xml" + +/** + * VIR_MIGRATE_PARAM_BANDWIDTH: + * + * virDomainMigrate* params field: the maximum bandwidth (in MiB/s) that will + * be used for migration as VIR_TYPED_PARAM_ULLONG. If set to 0 or omitted, + * libvirt will choose a suitable default. Some hypervisors do not support this + * feature and will return an error if this field is used and is not 0. + */ +#define VIR_MIGRATE_PARAM_BANDWIDTH "bandwidth" + + /* Domain migration. */ virDomainPtr virDomainMigrate (virDomainPtr domain, virConnectPtr dconn, unsigned long flags, const char *dname, diff --git a/src/libvirt_internal.h b/src/libvirt_internal.h index 29f2043..434d795 100644 --- a/src/libvirt_internal.h +++ b/src/libvirt_internal.h @@ -110,6 +110,11 @@ enum { * Support for offline migration. */ VIR_DRV_FEATURE_MIGRATION_OFFLINE = 12, + + /* + * Support for migration parameters. + */ + VIR_DRV_FEATURE_MIGRATION_PARAMS = 13, }; -- 1.8.2.1

On 18.06.2013 16:05, Jiri Denemark wrote:
To be used by new migration APIs with extensible set of parameters. --- include/libvirt/libvirt.h.in | 56 ++++++++++++++++++++++++++++++++++++++++++++ src/libvirt_internal.h | 5 ++++ 2 files changed, 61 insertions(+)
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index acf3218..35bffea 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1191,6 +1191,62 @@ typedef enum { VIR_MIGRATE_ABORT_ON_ERROR = (1 << 12), /* abort migration on I/O errors happened during migration */ } virDomainMigrateFlags;
+ +/** + * VIR_MIGRATE_PARAM_URI: + * + * virDomainMigrate* params field: URI to use for initiating domain migration + * as VIR_TYPED_PARAM_STRING. It takes a hypervisor specific format. The + * uri_transports element of the hypervisor capabilities XML includes details + * of the supported URI schemes. When omitted libvirt will auto-generate + * suitable default URI. It is typically only necessary to specify this URI if + * the destination host has multiple interfaces and a specific interface is + * required to transmit migration data. + * + * This filed may not be used when VIR_MIGRATE_TUNNELLED flag is set. + */ +#define VIR_MIGRATE_PARAM_URI "migrate_uri" + +/** + * VIR_MIGRATE_PARAM_DEST_NAME: + * + * virDomainMigrate* params field: the name to be used for the domain on the + * destination host as VIR_TYPED_PARAM_STRING. Omitting this parameter keeps + * the domain name the same. This field is only allowed to be used with + * hypervisors that support domain renaming during migration. + */ +#define VIR_MIGRATE_PARAM_DEST_NAME "destination_name" + +/** + * VIR_MIGRATE_PARAM_DEST_XML: + * + * virDomainMigrate* params field: the new configuration to be used for the + * domain on the destination host as VIR_TYPED_PARAM_STRING. The configuration + * must include an identical set of virtual devices, to ensure a stable guest + * ABI across migration. Only parameters related to host side configuration + * can be changed in the XML. Hypervisors which support this will validate
... Hypervisors which support this field will validate it and refuse to allow migration ...
+ * this and refuse to allow migration if the provided XML would cause a change + * in the guest ABI. This field cannot be used to rename the domain during + * migration (use VIR_MIGRATE_PARAM_DEST_NAME field for that purpose). Domain + * name in the destination XML must match the original domain name. + * + * Omitting this parameter keeps the original domain configuration. Using this + * field with hypervisors that do not support changing domain configuration + * during migration will result in a failure. + */ +#define VIR_MIGRATE_PARAM_DEST_XML "destination_xml" + +/** + * VIR_MIGRATE_PARAM_BANDWIDTH: + * + * virDomainMigrate* params field: the maximum bandwidth (in MiB/s) that will + * be used for migration as VIR_TYPED_PARAM_ULLONG. If set to 0 or omitted, + * libvirt will choose a suitable default. Some hypervisors do not support this + * feature and will return an error if this field is used and is not 0. + */ +#define VIR_MIGRATE_PARAM_BANDWIDTH "bandwidth" + + /* Domain migration. */ virDomainPtr virDomainMigrate (virDomainPtr domain, virConnectPtr dconn, unsigned long flags, const char *dname, diff --git a/src/libvirt_internal.h b/src/libvirt_internal.h index 29f2043..434d795 100644 --- a/src/libvirt_internal.h +++ b/src/libvirt_internal.h @@ -110,6 +110,11 @@ enum { * Support for offline migration. */ VIR_DRV_FEATURE_MIGRATION_OFFLINE = 12, + + /* + * Support for migration parameters. + */ + VIR_DRV_FEATURE_MIGRATION_PARAMS = 13,
Isn't it a bit too soon for this chunk? I think it fits into 11/21 better.
};

This patch implements extensible variants of all internal migration APIs used for v3 migration. --- daemon/remote.c | 331 ++++++++++++++++++++++++++++++++++++++++++- docs/apibuild.py | 6 + docs/hvsupport.pl | 7 + src/driver.h | 67 +++++++++ src/libvirt.c | 324 ++++++++++++++++++++++++++++++++++++++++++ src/libvirt_internal.h | 54 +++++++ src/libvirt_private.syms | 6 + src/remote/remote_protocol.x | 96 ++++++++++++- src/remote_protocol-structs | 107 ++++++++++++++ 9 files changed, 996 insertions(+), 2 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 47267c2..ba11379 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -885,7 +885,7 @@ remoteDeserializeTypedParameters(remote_typed_param *args_params_val, virTypedParameterPtr params = NULL; /* Check the length of the returned list carefully. */ - if (args_params_len > limit) { + if (limit && args_params_len > limit) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large")); goto cleanup; } @@ -4677,6 +4677,335 @@ cleanup: return rv; } +static int +remoteDispatchDomainMigrateBegin3Params( + virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_domain_migrate_begin3_params_args *args, + remote_domain_migrate_begin3_params_ret *ret) +{ + char *xml = NULL; + virDomainPtr dom = NULL; + virTypedParameterPtr params = NULL; + int nparams = 0; + char *cookieout = NULL; + int cookieoutlen = 0; + int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); + + if (!priv->conn) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) + goto cleanup; + + if (!(params = remoteDeserializeTypedParameters(args->params.params_val, + args->params.params_len, + 0, &nparams))) + goto cleanup; + + if (!(xml = virDomainMigrateBegin3Params(dom, params, nparams, + &cookieout, &cookieoutlen, + args->flags))) + goto cleanup; + + ret->cookie_out.cookie_out_len = cookieoutlen; + ret->cookie_out.cookie_out_val = cookieout; + ret->xml = xml; + + rv = 0; + +cleanup: + virTypedParamsFree(params, nparams); + if (rv < 0) + virNetMessageSaveError(rerr); + if (dom) + virDomainFree(dom); + return rv; +} + +static int +remoteDispatchDomainMigratePrepare3Params( + virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_domain_migrate_prepare3_params_args *args, + remote_domain_migrate_prepare3_params_ret *ret) +{ + virTypedParameterPtr params = NULL; + int nparams = 0; + char *cookieout = NULL; + int cookieoutlen = 0; + char **uri_out; + int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); + + if (!priv->conn) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + if (!(params = remoteDeserializeTypedParameters(args->params.params_val, + args->params.params_len, + 0, &nparams))) + goto cleanup; + + /* Wacky world of XDR ... */ + if (VIR_ALLOC(uri_out) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (virDomainMigratePrepare3Params(priv->conn, params, nparams, + args->cookie_in.cookie_in_val, + args->cookie_in.cookie_in_len, + &cookieout, &cookieoutlen, + uri_out, args->flags) < 0) + goto cleanup; + + ret->cookie_out.cookie_out_len = cookieoutlen; + ret->cookie_out.cookie_out_val = cookieout; + ret->uri_out = !*uri_out ? NULL : uri_out; + + rv = 0; + +cleanup: + virTypedParamsFree(params, nparams); + if (rv < 0) { + virNetMessageSaveError(rerr); + VIR_FREE(uri_out); + } + return rv; +} + +static int +remoteDispatchDomainMigratePrepareTunnel3Params( + virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_domain_migrate_prepare_tunnel3_params_args *args, + remote_domain_migrate_prepare_tunnel3_params_ret *ret) +{ + virTypedParameterPtr params = NULL; + int nparams = 0; + char *cookieout = NULL; + int cookieoutlen = 0; + int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); + virStreamPtr st = NULL; + daemonClientStreamPtr stream = NULL; + + if (!priv->conn) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + if (!(params = remoteDeserializeTypedParameters(args->params.params_val, + args->params.params_len, + 0, &nparams))) + goto cleanup; + + if (!(st = virStreamNew(priv->conn, VIR_STREAM_NONBLOCK)) || + !(stream = daemonCreateClientStream(client, st, remoteProgram, + &msg->header))) + goto cleanup; + + if (virDomainMigratePrepareTunnel3Params(priv->conn, st, params, nparams, + args->cookie_in.cookie_in_val, + args->cookie_in.cookie_in_len, + &cookieout, &cookieoutlen, + args->flags) < 0) + goto cleanup; + + if (daemonAddClientStream(client, stream, false) < 0) + goto cleanup; + + ret->cookie_out.cookie_out_val = cookieout; + ret->cookie_out.cookie_out_len = cookieoutlen; + rv = 0; + +cleanup: + virTypedParamsFree(params, nparams); + if (rv < 0) { + virNetMessageSaveError(rerr); + VIR_FREE(cookieout); + if (stream) { + virStreamAbort(st); + daemonFreeClientStream(client, stream); + } else { + virStreamFree(st); + } + } + return rv; +} + + +static int +remoteDispatchDomainMigratePerform3Params( + virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_domain_migrate_perform3_params_args *args, + remote_domain_migrate_perform3_params_ret *ret) +{ + virTypedParameterPtr params = NULL; + int nparams = 0; + virDomainPtr dom = NULL; + char *cookieout = NULL; + int cookieoutlen = 0; + char *dconnuri; + int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); + + if (!priv->conn) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) + goto cleanup; + + if (!(params = remoteDeserializeTypedParameters(args->params.params_val, + args->params.params_len, + 0, &nparams))) + goto cleanup; + + dconnuri = args->dconnuri == NULL ? NULL : *args->dconnuri; + + if (virDomainMigratePerform3Params(dom, dconnuri, params, nparams, + args->cookie_in.cookie_in_val, + args->cookie_in.cookie_in_len, + &cookieout, &cookieoutlen, + args->flags) < 0) + goto cleanup; + + ret->cookie_out.cookie_out_len = cookieoutlen; + ret->cookie_out.cookie_out_val = cookieout; + + rv = 0; + +cleanup: + virTypedParamsFree(params, nparams); + if (rv < 0) + virNetMessageSaveError(rerr); + if (dom) + virDomainFree(dom); + return rv; +} + + +static int +remoteDispatchDomainMigrateFinish3Params( + virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_domain_migrate_finish3_params_args *args, + remote_domain_migrate_finish3_params_ret *ret) +{ + virTypedParameterPtr params = NULL; + int nparams = 0; + virDomainPtr dom = NULL; + char *cookieout = NULL; + int cookieoutlen = 0; + int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); + + if (!priv->conn) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + if (!(params = remoteDeserializeTypedParameters(args->params.params_val, + args->params.params_len, + 0, &nparams))) + goto cleanup; + + dom = virDomainMigrateFinish3Params(priv->conn, params, nparams, + args->cookie_in.cookie_in_val, + args->cookie_in.cookie_in_len, + &cookieout, &cookieoutlen, + args->flags, args->cancelled); + if (!dom) + goto cleanup; + + make_nonnull_domain(&ret->dom, dom); + + ret->cookie_out.cookie_out_len = cookieoutlen; + ret->cookie_out.cookie_out_val = cookieout; + + rv = 0; + +cleanup: + virTypedParamsFree(params, nparams); + if (rv < 0) { + virNetMessageSaveError(rerr); + VIR_FREE(cookieout); + } + if (dom) + virDomainFree(dom); + return rv; +} + + +static int +remoteDispatchDomainMigrateConfirm3Params( + virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_domain_migrate_confirm3_params_args *args) +{ + virTypedParameterPtr params = NULL; + int nparams = 0; + virDomainPtr dom = NULL; + int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); + + if (!priv->conn) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) + goto cleanup; + + if (!(params = remoteDeserializeTypedParameters(args->params.params_val, + args->params.params_len, + 0, &nparams))) + goto cleanup; + + if (virDomainMigrateConfirm3Params(dom, params, nparams, + args->cookie_in.cookie_in_val, + args->cookie_in.cookie_in_len, + args->flags, args->cancelled) < 0) + goto cleanup; + + rv = 0; + +cleanup: + virTypedParamsFree(params, nparams); + if (rv < 0) + virNetMessageSaveError(rerr); + if (dom) + virDomainFree(dom); + return rv; +} + + /*----- Helpers. -----*/ /* get_nonnull_domain and get_nonnull_network turn an on-wire diff --git a/docs/apibuild.py b/docs/apibuild.py index aca2370..e65c559 100755 --- a/docs/apibuild.py +++ b/docs/apibuild.py @@ -70,6 +70,12 @@ ignored_functions = { "virTypedParameterToString": "internal function in virtypedparam.c", "virTypedParamsCheck": "internal function in virtypedparam.c", "virTypedParamsCopy": "internal function in virtypedparam.c", + "virDomainMigrateBegin3Params": "private function for migration", + "virDomainMigrateFinish3Params": "private function for migration", + "virDomainMigratePerform3Params": "private function for migration", + "virDomainMigratePrepare3Params": "private function for migration", + "virDomainMigrateConfirm3Params": "private function for migration", + "virDomainMigratePrepareTunnel3Params": "private function for tunnelled migration", } ignored_macros = { diff --git a/docs/hvsupport.pl b/docs/hvsupport.pl index e31441e..c774ada 100755 --- a/docs/hvsupport.pl +++ b/docs/hvsupport.pl @@ -169,6 +169,13 @@ $apis{virDomainMigratePerform3} = "0.9.2"; $apis{virDomainMigrateFinish3} = "0.9.2"; $apis{virDomainMigrateConfirm3} = "0.9.2"; +$apis{virDomainMigrateBegin3Params} = "1.0.7"; +$apis{virDomainMigratePrepare3Params} = "1.0.7"; +$apis{virDomainMigratePrepareTunnel3Params} = "1.0.7"; +$apis{virDomainMigratePerform3Params} = "1.0.7"; +$apis{virDomainMigrateFinish3Params} = "1.0.7"; +$apis{virDomainMigrateConfirm3Params} = "1.0.7"; + # Now we want to get the mapping between public APIs diff --git a/src/driver.h b/src/driver.h index ec5fc53..31851cb 100644 --- a/src/driver.h +++ b/src/driver.h @@ -1045,6 +1045,67 @@ typedef int int **fdlist, unsigned int flags); +typedef char * +(*virDrvDomainMigrateBegin3Params)(virDomainPtr domain, + virTypedParameterPtr params, + int nparams, + char **cookieout, + int *cookieoutlen, + unsigned int flags); + +typedef int +(*virDrvDomainMigratePrepare3Params)(virConnectPtr dconn, + virTypedParameterPtr params, + int nparams, + const char *cookiein, + int cookieinlen, + char **cookieout, + int *cookieoutlen, + char **uri_out, + unsigned int flags); + +typedef int +(*virDrvDomainMigratePrepareTunnel3Params)(virConnectPtr dconn, + virStreamPtr st, + virTypedParameterPtr params, + int nparams, + const char *cookiein, + int cookieinlen, + char **cookieout, + int *cookieoutlen, + unsigned int flags); + +typedef int +(*virDrvDomainMigratePerform3Params)(virDomainPtr dom, + const char *dconnuri, + virTypedParameterPtr params, + int nparams, + const char *cookiein, + int cookieinlen, + char **cookieout, + int *cookieoutlen, + unsigned int flags); + +typedef virDomainPtr +(*virDrvDomainMigrateFinish3Params)(virConnectPtr dconn, + virTypedParameterPtr params, + int nparams, + const char *cookiein, + int cookieinlen, + char **cookieout, + int *cookieoutlen, + unsigned int flags, + int cancelled); + +typedef int +(*virDrvDomainMigrateConfirm3Params)(virDomainPtr domain, + virTypedParameterPtr params, + int nparams, + const char *cookiein, + int cookieinlen, + unsigned int flags, + int cancelled); + typedef struct _virDriver virDriver; typedef virDriver *virDriverPtr; @@ -1246,6 +1307,12 @@ struct _virDriver { virDrvDomainFSTrim domainFSTrim; virDrvDomainSendProcessSignal domainSendProcessSignal; virDrvDomainLxcOpenNamespace domainLxcOpenNamespace; + virDrvDomainMigrateBegin3Params domainMigrateBegin3Params; + virDrvDomainMigratePrepare3Params domainMigratePrepare3Params; + virDrvDomainMigratePrepareTunnel3Params domainMigratePrepareTunnel3Params; + virDrvDomainMigratePerform3Params domainMigratePerform3Params; + virDrvDomainMigrateFinish3Params domainMigrateFinish3Params; + virDrvDomainMigrateConfirm3Params domainMigrateConfirm3Params; }; diff --git a/src/libvirt.c b/src/libvirt.c index a6efaf8..d6ded5e 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -6542,6 +6542,330 @@ error: } +/* + * Not for public use. This function is part of the internal + * implementation of migration in the remote case. + */ +char * +virDomainMigrateBegin3Params(virDomainPtr domain, + virTypedParameterPtr params, + int nparams, + char **cookieout, + int *cookieoutlen, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(domain, "params=%p, nparams=%d, " + "cookieout=%p, cookieoutlen=%p, flags=%x", + params, nparams, cookieout, cookieoutlen, flags); + VIR_TYPED_PARAMS_DEBUG(params, nparams); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return NULL; + } + conn = domain->conn; + + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (conn->driver->domainMigrateBegin3Params) { + char *xml; + xml = conn->driver->domainMigrateBegin3Params(domain, params, nparams, + cookieout, cookieoutlen, + flags); + VIR_DEBUG("xml %s", NULLSTR(xml)); + if (!xml) + goto error; + return xml; + } + + virLibDomainError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(domain->conn); + return NULL; +} + + +/* + * Not for public use. This function is part of the internal + * implementation of migration in the remote case. + */ +int +virDomainMigratePrepare3Params(virConnectPtr dconn, + virTypedParameterPtr params, + int nparams, + const char *cookiein, + int cookieinlen, + char **cookieout, + int *cookieoutlen, + char **uri_out, + unsigned int flags) +{ + VIR_DEBUG("dconn=%p, params=%p, nparams=%d, cookiein=%p, cookieinlen=%d, " + "cookieout=%p, cookieoutlen=%p, uri_out=%p, flags=%x", + dconn, params, nparams, cookiein, cookieinlen, + cookieout, cookieoutlen, uri_out, flags); + VIR_TYPED_PARAMS_DEBUG(params, nparams); + + virResetLastError(); + + if (!VIR_IS_CONNECT(dconn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + if (dconn->flags & VIR_CONNECT_RO) { + virLibConnError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (dconn->driver->domainMigratePrepare3Params) { + int ret; + ret = dconn->driver->domainMigratePrepare3Params(dconn, params, nparams, + cookiein, cookieinlen, + cookieout, cookieoutlen, + uri_out, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(dconn); + return -1; +} + + +/* + * Not for public use. This function is part of the internal + * implementation of migration in the remote case. + */ +int +virDomainMigratePrepareTunnel3Params(virConnectPtr conn, + virStreamPtr st, + virTypedParameterPtr params, + int nparams, + const char *cookiein, + int cookieinlen, + char **cookieout, + int *cookieoutlen, + unsigned int flags) + +{ + VIR_DEBUG("conn=%p, stream=%p, params=%p, nparams=%d, cookiein=%p, " + "cookieinlen=%d, cookieout=%p, cookieoutlen=%p, flags=%x", + conn, st, params, nparams, cookiein, cookieinlen, + cookieout, cookieoutlen, flags); + VIR_TYPED_PARAMS_DEBUG(params, nparams); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + if (conn->flags & VIR_CONNECT_RO) { + virLibConnError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (conn != st->conn) { + virReportInvalidArg(conn, + _("conn in %s must match stream connection"), + __FUNCTION__); + goto error; + } + + if (conn->driver->domainMigratePrepareTunnel3Params) { + int rv; + rv = conn->driver->domainMigratePrepareTunnel3Params( + conn, st, params, nparams, cookiein, cookieinlen, + cookieout, cookieoutlen, flags); + if (rv < 0) + goto error; + return rv; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(conn); + return -1; +} + + +/* + * Not for public use. This function is part of the internal + * implementation of migration in the remote case. + */ +int +virDomainMigratePerform3Params(virDomainPtr domain, + const char *dconnuri, + virTypedParameterPtr params, + int nparams, + const char *cookiein, + int cookieinlen, + char **cookieout, + int *cookieoutlen, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(domain, "dconnuri=%s, params=%p, nparams=%d, cookiein=%p, " + "cookieinlen=%d, cookieout=%p, cookieoutlen=%p, flags=%x", + NULLSTR(dconnuri), params, nparams, cookiein, + cookieinlen, cookieout, cookieoutlen, flags); + VIR_TYPED_PARAMS_DEBUG(params, nparams); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + conn = domain->conn; + + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (conn->driver->domainMigratePerform3Params) { + int ret; + ret = conn->driver->domainMigratePerform3Params( + domain, dconnuri, params, nparams, cookiein, cookieinlen, + cookieout, cookieoutlen, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibDomainError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(domain->conn); + return -1; +} + + +/* + * Not for public use. This function is part of the internal + * implementation of migration in the remote case. + */ +virDomainPtr +virDomainMigrateFinish3Params(virConnectPtr dconn, + virTypedParameterPtr params, + int nparams, + const char *cookiein, + int cookieinlen, + char **cookieout, + int *cookieoutlen, + unsigned int flags, + int cancelled) +{ + VIR_DEBUG("dconn=%p, params=%p, nparams=%d, cookiein=%p, cookieinlen=%d, " + "cookieout=%p, cookieoutlen=%p, flags=%x, cancelled=%d", + dconn, params, nparams, cookiein, cookieinlen, cookieout, + cookieoutlen, flags, cancelled); + VIR_TYPED_PARAMS_DEBUG(params, nparams); + + virResetLastError(); + + if (!VIR_IS_CONNECT(dconn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return NULL; + } + + if (dconn->flags & VIR_CONNECT_RO) { + virLibConnError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (dconn->driver->domainMigrateFinish3Params) { + virDomainPtr ret; + ret = dconn->driver->domainMigrateFinish3Params( + dconn, params, nparams, cookiein, cookieinlen, + cookieout, cookieoutlen, flags, cancelled); + if (!ret) + goto error; + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(dconn); + return NULL; +} + + +/* + * Not for public use. This function is part of the internal + * implementation of migration in the remote case. + */ +int +virDomainMigrateConfirm3Params(virDomainPtr domain, + virTypedParameterPtr params, + int nparams, + const char *cookiein, + int cookieinlen, + unsigned int flags, + int cancelled) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(domain, "params=%p, nparams=%d, cookiein=%p, " + "cookieinlen=%d, flags=%x, cancelled=%d", + params, nparams, cookiein, cookieinlen, flags, cancelled); + VIR_TYPED_PARAMS_DEBUG(params, nparams); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + conn = domain->conn; + + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (conn->driver->domainMigrateConfirm3Params) { + int ret; + ret = conn->driver->domainMigrateConfirm3Params( + domain, params, nparams, + cookiein, cookieinlen, flags, cancelled); + if (ret < 0) + goto error; + return ret; + } + + virLibDomainError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(domain->conn); + return -1; +} + + /** * virNodeGetInfo: * @conn: pointer to the hypervisor connection diff --git a/src/libvirt_internal.h b/src/libvirt_internal.h index 434d795..115d8d1 100644 --- a/src/libvirt_internal.h +++ b/src/libvirt_internal.h @@ -226,4 +226,58 @@ int virDomainMigrateConfirm3(virDomainPtr domain, unsigned long flags, int restart); /* Restart the src VM */ +char *virDomainMigrateBegin3Params(virDomainPtr domain, + virTypedParameterPtr params, + int nparams, + char **cookieout, + int *cookieoutlen, + unsigned int flags); + +int virDomainMigratePrepare3Params(virConnectPtr dconn, + virTypedParameterPtr params, + int nparams, + const char *cookiein, + int cookieinlen, + char **cookieout, + int *cookieoutlen, + char **uri_out, + unsigned int flags); + +int virDomainMigratePrepareTunnel3Params(virConnectPtr conn, + virStreamPtr st, + virTypedParameterPtr params, + int nparams, + const char *cookiein, + int cookieinlen, + char **cookieout, + int *cookieoutlen, + unsigned int flags); + +int virDomainMigratePerform3Params(virDomainPtr domain, + const char *dconnuri, + virTypedParameterPtr params, + int nparams, + const char *cookiein, + int cookieinlen, + char **cookieout, + int *cookieoutlen, + unsigned int flags); + +virDomainPtr virDomainMigrateFinish3Params(virConnectPtr dconn, + virTypedParameterPtr params, + int nparams, + const char *cookiein, + int cookieinlen, + char **cookieout, + int *cookieoutlen, + unsigned int flags, + int cancelled); + +int virDomainMigrateConfirm3Params(virDomainPtr domain, + virTypedParameterPtr params, + int nparams, + const char *cookiein, + int cookieinlen, + unsigned int flags, + int cancelled); #endif diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index fa1041c..bb5f91e 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -709,17 +709,23 @@ virFDStreamSetIOHelper; # libvirt_internal.h virConnectSupportsFeature; virDomainMigrateBegin3; +virDomainMigrateBegin3Params; virDomainMigrateConfirm3; +virDomainMigrateConfirm3Params; virDomainMigrateFinish; virDomainMigrateFinish2; virDomainMigrateFinish3; +virDomainMigrateFinish3Params; virDomainMigratePerform; virDomainMigratePerform3; +virDomainMigratePerform3Params; virDomainMigratePrepare; virDomainMigratePrepare2; virDomainMigratePrepare3; +virDomainMigratePrepare3Params; virDomainMigratePrepareTunnel; virDomainMigratePrepareTunnel3; +virDomainMigratePrepareTunnel3Params; virRegisterDriver; virRegisterInterfaceDriver; virRegisterNetworkDriver; diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 9723377..b506f99 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -2744,6 +2744,70 @@ struct remote_domain_fstrim_args { unsigned int flags; }; +struct remote_domain_migrate_begin3_params_args { + remote_nonnull_domain dom; + remote_typed_param params<>; + unsigned int flags; +}; + +struct remote_domain_migrate_begin3_params_ret { + opaque cookie_out<REMOTE_MIGRATE_COOKIE_MAX>; + remote_nonnull_string xml; +}; + +struct remote_domain_migrate_prepare3_params_args { + remote_typed_param params<>; + opaque cookie_in<REMOTE_MIGRATE_COOKIE_MAX>; + unsigned int flags; +}; + +struct remote_domain_migrate_prepare3_params_ret { + opaque cookie_out<REMOTE_MIGRATE_COOKIE_MAX>; + remote_string uri_out; +}; + +struct remote_domain_migrate_prepare_tunnel3_params_args { + remote_typed_param params<>; + opaque cookie_in<REMOTE_MIGRATE_COOKIE_MAX>; + unsigned int flags; +}; + +struct remote_domain_migrate_prepare_tunnel3_params_ret { + opaque cookie_out<REMOTE_MIGRATE_COOKIE_MAX>; +}; + +struct remote_domain_migrate_perform3_params_args { + remote_nonnull_domain dom; + remote_string dconnuri; + remote_typed_param params<>; + opaque cookie_in<REMOTE_MIGRATE_COOKIE_MAX>; + unsigned int flags; +}; + +struct remote_domain_migrate_perform3_params_ret { + opaque cookie_out<REMOTE_MIGRATE_COOKIE_MAX>; +}; + +struct remote_domain_migrate_finish3_params_args { + remote_typed_param params<>; + opaque cookie_in<REMOTE_MIGRATE_COOKIE_MAX>; + unsigned int flags; + int cancelled; +}; + +struct remote_domain_migrate_finish3_params_ret { + remote_nonnull_domain dom; + opaque cookie_out<REMOTE_MIGRATE_COOKIE_MAX>; +}; + +struct remote_domain_migrate_confirm3_params_args { + remote_nonnull_domain dom; + remote_typed_param params<>; + opaque cookie_in<REMOTE_MIGRATE_COOKIE_MAX>; + unsigned int flags; + int cancelled; +}; + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -4434,6 +4498,36 @@ enum remote_procedure { /** * @generate: server */ - REMOTE_PROC_NODE_DEVICE_DETACH_FLAGS = 301 + REMOTE_PROC_NODE_DEVICE_DETACH_FLAGS = 301, + + /** + * @generate: none + */ + REMOTE_PROC_DOMAIN_MIGRATE_BEGIN3_PARAMS = 302, + + /** + * @generate: none + */ + REMOTE_PROC_DOMAIN_MIGRATE_PREPARE3_PARAMS = 303, + + /** + * @generate: none + */ + REMOTE_PROC_DOMAIN_MIGRATE_PREPARE_TUNNEL3_PARAMS = 304, + + /** + * @generate: none + */ + REMOTE_PROC_DOMAIN_MIGRATE_PERFORM3_PARAMS = 305, + + /** + * @generate: none + */ + REMOTE_PROC_DOMAIN_MIGRATE_FINISH3_PARAMS = 306, + + /** + * @generate: none + */ + REMOTE_PROC_DOMAIN_MIGRATE_CONFIRM3_PARAMS = 307 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index ea38ea2..e38d24a 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2192,6 +2192,107 @@ struct remote_domain_fstrim_args { uint64_t minimum; u_int flags; }; +struct remote_domain_migrate_begin3_params_args { + remote_nonnull_domain dom; + struct { + u_int params_len; + remote_typed_param * params_val; + } params; + u_int flags; +}; +struct remote_domain_migrate_begin3_params_ret { + struct { + u_int cookie_out_len; + char * cookie_out_val; + } cookie_out; + remote_nonnull_string xml; +}; +struct remote_domain_migrate_prepare3_params_args { + struct { + u_int params_len; + remote_typed_param * params_val; + } params; + struct { + u_int cookie_in_len; + char * cookie_in_val; + } cookie_in; + u_int flags; +}; +struct remote_domain_migrate_prepare3_params_ret { + struct { + u_int cookie_out_len; + char * cookie_out_val; + } cookie_out; + remote_string uri_out; +}; +struct remote_domain_migrate_prepare_tunnel3_params_args { + struct { + u_int params_len; + remote_typed_param * params_val; + } params; + struct { + u_int cookie_in_len; + char * cookie_in_val; + } cookie_in; + u_int flags; +}; +struct remote_domain_migrate_prepare_tunnel3_params_ret { + struct { + u_int cookie_out_len; + char * cookie_out_val; + } cookie_out; +}; +struct remote_domain_migrate_perform3_params_args { + remote_nonnull_domain dom; + remote_string dconnuri; + struct { + u_int params_len; + remote_typed_param * params_val; + } params; + struct { + u_int cookie_in_len; + char * cookie_in_val; + } cookie_in; + u_int flags; +}; +struct remote_domain_migrate_perform3_params_ret { + struct { + u_int cookie_out_len; + char * cookie_out_val; + } cookie_out; +}; +struct remote_domain_migrate_finish3_params_args { + struct { + u_int params_len; + remote_typed_param * params_val; + } params; + struct { + u_int cookie_in_len; + char * cookie_in_val; + } cookie_in; + u_int flags; + int cancelled; +}; +struct remote_domain_migrate_finish3_params_ret { + remote_nonnull_domain dom; + struct { + u_int cookie_out_len; + char * cookie_out_val; + } cookie_out; +}; +struct remote_domain_migrate_confirm3_params_args { + remote_nonnull_domain dom; + struct { + u_int params_len; + remote_typed_param * params_val; + } params; + struct { + u_int cookie_in_len; + char * cookie_in_val; + } cookie_in; + u_int flags; + int cancelled; +}; enum remote_procedure { REMOTE_PROC_CONNECT_OPEN = 1, REMOTE_PROC_CONNECT_CLOSE = 2, @@ -2494,4 +2595,10 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_MIGRATE_GET_COMPRESSION_CACHE = 299, REMOTE_PROC_DOMAIN_MIGRATE_SET_COMPRESSION_CACHE = 300, REMOTE_PROC_NODE_DEVICE_DETACH_FLAGS = 301, + REMOTE_PROC_DOMAIN_MIGRATE_BEGIN3_PARAMS = 302, + REMOTE_PROC_DOMAIN_MIGRATE_PREPARE3_PARAMS = 303, + REMOTE_PROC_DOMAIN_MIGRATE_PREPARE_TUNNEL3_PARAMS = 304, + REMOTE_PROC_DOMAIN_MIGRATE_PERFORM3_PARAMS = 305, + REMOTE_PROC_DOMAIN_MIGRATE_FINISH3_PARAMS = 306, + REMOTE_PROC_DOMAIN_MIGRATE_CONFIRM3_PARAMS = 307, }; -- 1.8.2.1

On 18.06.2013 16:05, Jiri Denemark wrote:
This patch implements extensible variants of all internal migration APIs used for v3 migration. --- daemon/remote.c | 331 ++++++++++++++++++++++++++++++++++++++++++- docs/apibuild.py | 6 + docs/hvsupport.pl | 7 + src/driver.h | 67 +++++++++ src/libvirt.c | 324 ++++++++++++++++++++++++++++++++++++++++++ src/libvirt_internal.h | 54 +++++++ src/libvirt_private.syms | 6 + src/remote/remote_protocol.x | 96 ++++++++++++- src/remote_protocol-structs | 107 ++++++++++++++ 9 files changed, 996 insertions(+), 2 deletions(-)
+static int +remoteDispatchDomainMigratePrepareTunnel3Params( + virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED,
Drop ATTRIBUTE_UNUSED as @msg is clearly used ...
+ virNetMessageErrorPtr rerr, + remote_domain_migrate_prepare_tunnel3_params_args *args, + remote_domain_migrate_prepare_tunnel3_params_ret *ret) +{ + virTypedParameterPtr params = NULL; + int nparams = 0; + char *cookieout = NULL; + int cookieoutlen = 0; + int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); + virStreamPtr st = NULL; + daemonClientStreamPtr stream = NULL; + + if (!priv->conn) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + if (!(params = remoteDeserializeTypedParameters(args->params.params_val, + args->params.params_len, + 0, &nparams))) + goto cleanup; + + if (!(st = virStreamNew(priv->conn, VIR_STREAM_NONBLOCK)) || + !(stream = daemonCreateClientStream(client, st, remoteProgram, + &msg->header)))
... here.
+ goto cleanup; + + if (virDomainMigratePrepareTunnel3Params(priv->conn, st, params, nparams, + args->cookie_in.cookie_in_val, + args->cookie_in.cookie_in_len, + &cookieout, &cookieoutlen, + args->flags) < 0) + goto cleanup; + + if (daemonAddClientStream(client, stream, false) < 0) + goto cleanup; + + ret->cookie_out.cookie_out_val = cookieout; + ret->cookie_out.cookie_out_len = cookieoutlen; + rv = 0; + +cleanup: + virTypedParamsFree(params, nparams); + if (rv < 0) { + virNetMessageSaveError(rerr); + VIR_FREE(cookieout); + if (stream) { + virStreamAbort(st); + daemonFreeClientStream(client, stream); + } else { + virStreamFree(st); + } + } + return rv; +} + +

--- src/remote/remote_driver.c | 398 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 398 insertions(+) diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 87c61f4..4b3a514 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -6042,6 +6042,398 @@ done: } +static char * +remoteDomainMigrateBegin3Params(virDomainPtr domain, + virTypedParameterPtr params, + int nparams, + char **cookieout, + int *cookieoutlen, + unsigned int flags) +{ + char *rv = NULL; + remote_domain_migrate_begin3_params_args args; + remote_domain_migrate_begin3_params_ret ret; + struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); + + memset(&args, 0, sizeof(args)); + memset(&ret, 0, sizeof(ret)); + + make_nonnull_domain(&args.dom, domain); + args.flags = flags; + + if (remoteSerializeTypedParameters(params, nparams, + &args.params.params_val, + &args.params.params_len) < 0) { + xdr_free((xdrproc_t) xdr_remote_domain_migrate_begin3_params_args, + (char *) &args); + goto cleanup; + } + + if (call(domain->conn, priv, 0, REMOTE_PROC_DOMAIN_MIGRATE_BEGIN3_PARAMS, + (xdrproc_t) xdr_remote_domain_migrate_begin3_params_args, + (char *) &args, + (xdrproc_t) xdr_remote_domain_migrate_begin3_params_ret, + (char *) &ret) == -1) + goto cleanup; + + if (ret.cookie_out.cookie_out_len > 0) { + if (!cookieout || !cookieoutlen) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("caller ignores cookieout or cookieoutlen")); + goto error; + } + *cookieout = ret.cookie_out.cookie_out_val; /* Caller frees. */ + *cookieoutlen = ret.cookie_out.cookie_out_len; + } + + rv = ret.xml; /* caller frees */ + +cleanup: + remoteFreeTypedParameters(args.params.params_val, args.params.params_len); + remoteDriverUnlock(priv); + return rv; + +error: + VIR_FREE(ret.cookie_out.cookie_out_val); + goto cleanup; +} + + +static int +remoteDomainMigratePrepare3Params(virConnectPtr dconn, + virTypedParameterPtr params, + int nparams, + const char *cookiein, + int cookieinlen, + char **cookieout, + int *cookieoutlen, + char **uri_out, + unsigned int flags) +{ + int rv = -1; + remote_domain_migrate_prepare3_params_args args; + remote_domain_migrate_prepare3_params_ret ret; + struct private_data *priv = dconn->privateData; + + remoteDriverLock(priv); + + memset(&args, 0, sizeof(args)); + memset(&ret, 0, sizeof(ret)); + + if (remoteSerializeTypedParameters(params, nparams, + &args.params.params_val, + &args.params.params_len) < 0) { + xdr_free((xdrproc_t) xdr_remote_domain_migrate_prepare3_params_args, + (char *) &args); + goto cleanup; + } + + args.cookie_in.cookie_in_val = (char *)cookiein; + args.cookie_in.cookie_in_len = cookieinlen; + args.flags = flags; + + if (call(dconn, priv, 0, REMOTE_PROC_DOMAIN_MIGRATE_PREPARE3_PARAMS, + (xdrproc_t) xdr_remote_domain_migrate_prepare3_params_args, + (char *) &args, + (xdrproc_t) xdr_remote_domain_migrate_prepare3_params_ret, + (char *) &ret) == -1) + goto cleanup; + + if (ret.cookie_out.cookie_out_len > 0) { + if (!cookieout || !cookieoutlen) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("caller ignores cookieout or cookieoutlen")); + goto error; + } + *cookieout = ret.cookie_out.cookie_out_val; /* Caller frees. */ + *cookieoutlen = ret.cookie_out.cookie_out_len; + } + if (ret.uri_out) { + if (!uri_out) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("caller ignores uri_out")); + goto error; + } + *uri_out = *ret.uri_out; /* Caller frees. */ + } + + rv = 0; + +cleanup: + remoteFreeTypedParameters(args.params.params_val, args.params.params_len); + VIR_FREE(ret.uri_out); + remoteDriverUnlock(priv); + return rv; + +error: + VIR_FREE(ret.cookie_out.cookie_out_val); + if (ret.uri_out) + VIR_FREE(*ret.uri_out); + goto cleanup; +} + + +static int +remoteDomainMigratePrepareTunnel3Params(virConnectPtr dconn, + virStreamPtr st, + virTypedParameterPtr params, + int nparams, + const char *cookiein, + int cookieinlen, + char **cookieout, + int *cookieoutlen, + unsigned int flags) +{ + struct private_data *priv = dconn->privateData; + int rv = -1; + remote_domain_migrate_prepare_tunnel3_params_args args; + remote_domain_migrate_prepare_tunnel3_params_ret ret; + virNetClientStreamPtr netst; + + remoteDriverLock(priv); + + memset(&args, 0, sizeof(args)); + memset(&ret, 0, sizeof(ret)); + + args.cookie_in.cookie_in_val = (char *)cookiein; + args.cookie_in.cookie_in_len = cookieinlen; + args.flags = flags; + + if (remoteSerializeTypedParameters(params, nparams, + &args.params.params_val, + &args.params.params_len) < 0) { + xdr_free((xdrproc_t) xdr_remote_domain_migrate_prepare_tunnel3_params_args, + (char *) &args); + goto cleanup; + } + + if (!(netst = virNetClientStreamNew(priv->remoteProgram, + REMOTE_PROC_DOMAIN_MIGRATE_PREPARE_TUNNEL3_PARAMS, + priv->counter))) + goto cleanup; + + if (virNetClientAddStream(priv->client, netst) < 0) { + virObjectUnref(netst); + goto cleanup; + } + + st->driver = &remoteStreamDrv; + st->privateData = netst; + + if (call(dconn, priv, 0, REMOTE_PROC_DOMAIN_MIGRATE_PREPARE_TUNNEL3_PARAMS, + (xdrproc_t) xdr_remote_domain_migrate_prepare_tunnel3_params_args, + (char *) &args, + (xdrproc_t) xdr_remote_domain_migrate_prepare_tunnel3_params_ret, + (char *) &ret) == -1) { + virNetClientRemoveStream(priv->client, netst); + virObjectUnref(netst); + goto cleanup; + } + + if (ret.cookie_out.cookie_out_len > 0) { + if (!cookieout || !cookieoutlen) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("caller ignores cookieout or cookieoutlen")); + goto error; + } + *cookieout = ret.cookie_out.cookie_out_val; /* Caller frees. */ + *cookieoutlen = ret.cookie_out.cookie_out_len; + } + + rv = 0; + +cleanup: + remoteFreeTypedParameters(args.params.params_val, args.params.params_len); + remoteDriverUnlock(priv); + return rv; + +error: + VIR_FREE(ret.cookie_out.cookie_out_val); + goto cleanup; +} + + +static int +remoteDomainMigratePerform3Params(virDomainPtr dom, + const char *dconnuri, + virTypedParameterPtr params, + int nparams, + const char *cookiein, + int cookieinlen, + char **cookieout, + int *cookieoutlen, + unsigned int flags) +{ + int rv = -1; + remote_domain_migrate_perform3_params_args args; + remote_domain_migrate_perform3_params_ret ret; + struct private_data *priv = dom->conn->privateData; + + remoteDriverLock(priv); + + memset(&args, 0, sizeof(args)); + memset(&ret, 0, sizeof(ret)); + + make_nonnull_domain(&args.dom, dom); + args.dconnuri = dconnuri == NULL ? NULL : (char **) &dconnuri; + args.cookie_in.cookie_in_val = (char *)cookiein; + args.cookie_in.cookie_in_len = cookieinlen; + args.flags = flags; + + if (remoteSerializeTypedParameters(params, nparams, + &args.params.params_val, + &args.params.params_len) < 0) { + xdr_free((xdrproc_t) xdr_remote_domain_migrate_perform3_params_args, + (char *) &args); + goto cleanup; + } + + if (call(dom->conn, priv, 0, REMOTE_PROC_DOMAIN_MIGRATE_PERFORM3_PARAMS, + (xdrproc_t) xdr_remote_domain_migrate_perform3_params_args, + (char *) &args, + (xdrproc_t) xdr_remote_domain_migrate_perform3_params_ret, + (char *) &ret) == -1) + goto cleanup; + + if (ret.cookie_out.cookie_out_len > 0) { + if (!cookieout || !cookieoutlen) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("caller ignores cookieout or cookieoutlen")); + goto error; + } + *cookieout = ret.cookie_out.cookie_out_val; /* Caller frees. */ + *cookieoutlen = ret.cookie_out.cookie_out_len; + } + + rv = 0; + +cleanup: + remoteFreeTypedParameters(args.params.params_val, args.params.params_len); + remoteDriverUnlock(priv); + return rv; + +error: + VIR_FREE(ret.cookie_out.cookie_out_val); + goto cleanup; +} + + +static virDomainPtr +remoteDomainMigrateFinish3Params(virConnectPtr dconn, + virTypedParameterPtr params, + int nparams, + const char *cookiein, + int cookieinlen, + char **cookieout, + int *cookieoutlen, + unsigned int flags, + int cancelled) +{ + remote_domain_migrate_finish3_params_args args; + remote_domain_migrate_finish3_params_ret ret; + struct private_data *priv = dconn->privateData; + virDomainPtr rv = NULL; + + remoteDriverLock(priv); + + memset(&args, 0, sizeof(args)); + memset(&ret, 0, sizeof(ret)); + + args.cookie_in.cookie_in_val = (char *)cookiein; + args.cookie_in.cookie_in_len = cookieinlen; + args.flags = flags; + args.cancelled = cancelled; + + if (remoteSerializeTypedParameters(params, nparams, + &args.params.params_val, + &args.params.params_len) < 0) { + xdr_free((xdrproc_t) xdr_remote_domain_migrate_finish3_params_args, + (char *) &args); + goto cleanup; + } + + if (call(dconn, priv, 0, REMOTE_PROC_DOMAIN_MIGRATE_FINISH3_PARAMS, + (xdrproc_t) xdr_remote_domain_migrate_finish3_params_args, + (char *) &args, + (xdrproc_t) xdr_remote_domain_migrate_finish3_params_ret, + (char *) &ret) == -1) + goto cleanup; + + rv = get_nonnull_domain(dconn, ret.dom); + + if (ret.cookie_out.cookie_out_len > 0) { + if (!cookieout || !cookieoutlen) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("caller ignores cookieout or cookieoutlen")); + goto error; + } + *cookieout = ret.cookie_out.cookie_out_val; /* Caller frees. */ + *cookieoutlen = ret.cookie_out.cookie_out_len; + ret.cookie_out.cookie_out_val = NULL; + ret.cookie_out.cookie_out_len = 0; + } + + xdr_free((xdrproc_t) &xdr_remote_domain_migrate_finish3_params_ret, + (char *) &ret); + +cleanup: + remoteFreeTypedParameters(args.params.params_val, args.params.params_len); + remoteDriverUnlock(priv); + return rv; + +error: + VIR_FREE(ret.cookie_out.cookie_out_val); + goto cleanup; +} + + +static int +remoteDomainMigrateConfirm3Params(virDomainPtr domain, + virTypedParameterPtr params, + int nparams, + const char *cookiein, + int cookieinlen, + unsigned int flags, + int cancelled) +{ + int rv = -1; + remote_domain_migrate_confirm3_params_args args; + struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); + + memset(&args, 0, sizeof(args)); + + make_nonnull_domain(&args.dom, domain); + args.cookie_in.cookie_in_len = cookieinlen; + args.cookie_in.cookie_in_val = (char *) cookiein; + args.flags = flags; + args.cancelled = cancelled; + + if (remoteSerializeTypedParameters(params, nparams, + &args.params.params_val, + &args.params.params_len) < 0) { + xdr_free((xdrproc_t) xdr_remote_domain_migrate_confirm3_params_args, + (char *) &args); + goto cleanup; + } + + if (call(domain->conn, priv, 0, REMOTE_PROC_DOMAIN_MIGRATE_CONFIRM3_PARAMS, + (xdrproc_t) xdr_remote_domain_migrate_confirm3_params_args, + (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) + goto cleanup; + + rv = 0; + +cleanup: + remoteFreeTypedParameters(args.params.params_val, args.params.params_len); + remoteDriverUnlock(priv); + return rv; +} + + static void remoteDomainEventQueue(struct private_data *priv, virDomainEventPtr event) { @@ -6367,6 +6759,12 @@ static virDriver remote_driver = { .nodeGetCPUMap = remoteNodeGetCPUMap, /* 1.0.0 */ .domainFSTrim = remoteDomainFSTrim, /* 1.0.1 */ .domainLxcOpenNamespace = remoteDomainLxcOpenNamespace, /* 1.0.2 */ + .domainMigrateBegin3Params = remoteDomainMigrateBegin3Params, /* 1.0.7 */ + .domainMigratePrepare3Params = remoteDomainMigratePrepare3Params, /* 1.0.7 */ + .domainMigratePrepareTunnel3Params = remoteDomainMigratePrepareTunnel3Params, /* 1.0.7 */ + .domainMigratePerform3Params = remoteDomainMigratePerform3Params, /* 1.0.7 */ + .domainMigrateFinish3Params = remoteDomainMigrateFinish3Params, /* 1.0.7 */ + .domainMigrateConfirm3Params = remoteDomainMigrateConfirm3Params, /* 1.0.7 */ }; static virNetworkDriver network_driver = { -- 1.8.2.1

--- src/qemu/qemu_driver.c | 350 +++++++++++++++++++++++++++++++++++++++------- src/qemu/qemu_migration.c | 162 +++++++++++++++------ src/qemu/qemu_migration.h | 9 ++ 3 files changed, 431 insertions(+), 90 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index a682e36..c040287 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1086,6 +1086,7 @@ qemuConnectSupportsFeature(virConnectPtr conn ATTRIBUTE_UNUSED, int feature) case VIR_DRV_FEATURE_TYPED_PARAM_STRING: case VIR_DRV_FEATURE_XML_MIGRATABLE: case VIR_DRV_FEATURE_MIGRATION_OFFLINE: + case VIR_DRV_FEATURE_MIGRATION_PARAMS: return 1; default: return 0; @@ -9887,21 +9888,18 @@ cleanup: *******************************************************************/ static char * -qemuDomainMigrateBegin3(virDomainPtr domain, - const char *xmlin, - char **cookieout, - int *cookieoutlen, - unsigned long flags, - const char *dname, - unsigned long resource ATTRIBUTE_UNUSED) +qemuDomainMigrateBegin3Internal(virDomainPtr domain, + const char *xmlin, + const char *dname, + char **cookieout, + int *cookieoutlen, + unsigned long flags) { virQEMUDriverPtr driver = domain->conn->privateData; virDomainObjPtr vm; char *xml = NULL; enum qemuDomainAsyncJob asyncJob; - virCheckFlags(QEMU_MIGRATION_FLAGS, NULL); - if (!(vm = qemuDomObjFromDomain(domain))) return NULL; @@ -9969,26 +9967,66 @@ endjob: goto cleanup; } +static char * +qemuDomainMigrateBegin3(virDomainPtr domain, + const char *xmlin, + char **cookieout, + int *cookieoutlen, + unsigned long flags, + const char *dname, + unsigned long resource ATTRIBUTE_UNUSED) +{ + virCheckFlags(QEMU_MIGRATION_FLAGS, NULL); + + return qemuDomainMigrateBegin3Internal(domain, xmlin, dname, + cookieout, cookieoutlen, flags); +} + +static char * +qemuDomainMigrateBegin3Params(virDomainPtr domain, + virTypedParameterPtr params, + int nparams, + char **cookieout, + int *cookieoutlen, + unsigned int flags) +{ + const char *xmlin = NULL; + const char *dname = NULL; + + virCheckFlags(QEMU_MIGRATION_FLAGS, NULL); + if (virTypedParamsValidate(params, nparams, QEMU_MIGRATION_PARAMETERS) < 0) + return NULL; + + if (virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_DEST_XML, + &xmlin) < 0 || + virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_DEST_NAME, + &dname) < 0) + return NULL; + + return qemuDomainMigrateBegin3Internal(domain, xmlin, dname, + cookieout, cookieoutlen, flags); +} + + static int -qemuDomainMigratePrepare3(virConnectPtr dconn, - const char *cookiein, - int cookieinlen, - char **cookieout, - int *cookieoutlen, - const char *uri_in, - char **uri_out, - unsigned long flags, - const char *dname, - unsigned long resource ATTRIBUTE_UNUSED, - const char *dom_xml) +qemuDomainMigratePrepare3Internal(virConnectPtr dconn, + const char *dom_xml, + const char *dname, + const char *uri_in, + const char *cookiein, + int cookieinlen, + char **cookieout, + int *cookieoutlen, + char **uri_out, + unsigned long flags) { virQEMUDriverPtr driver = dconn->privateData; virCapsPtr caps = NULL; virDomainDefPtr def = NULL; int ret = -1; - virCheckFlags(QEMU_MIGRATION_FLAGS, -1); - *uri_out = NULL; if (flags & VIR_MIGRATE_TUNNELLED) { @@ -10032,26 +10070,78 @@ cleanup: return ret; } +static int +qemuDomainMigratePrepare3(virConnectPtr dconn, + const char *cookiein, + int cookieinlen, + char **cookieout, + int *cookieoutlen, + const char *uri_in, + char **uri_out, + unsigned long flags, + const char *dname, + unsigned long resource ATTRIBUTE_UNUSED, + const char *dom_xml) +{ + virCheckFlags(QEMU_MIGRATION_FLAGS, -1); + + return qemuDomainMigratePrepare3Internal(dconn, dom_xml, dname, uri_in, + cookiein, cookieinlen, cookieout, + cookieoutlen, uri_out, flags); +} static int -qemuDomainMigratePrepareTunnel3(virConnectPtr dconn, - virStreamPtr st, +qemuDomainMigratePrepare3Params(virConnectPtr dconn, + virTypedParameterPtr params, + int nparams, const char *cookiein, int cookieinlen, char **cookieout, int *cookieoutlen, - unsigned long flags, - const char *dname, - unsigned long resource ATTRIBUTE_UNUSED, - const char *dom_xml) + char **uri_out, + unsigned int flags) +{ + const char *dom_xml = NULL; + const char *dname = NULL; + const char *uri_in = NULL; + + virCheckFlags(QEMU_MIGRATION_FLAGS, -1); + if (virTypedParamsValidate(params, nparams, QEMU_MIGRATION_PARAMETERS) < 0) + return -1; + + if (virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_DEST_XML, + &dom_xml) < 0 || + virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_DEST_NAME, + &dname) < 0 || + virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_URI, + &uri_in) < 0) + return -1; + + return qemuDomainMigratePrepare3Internal(dconn, dom_xml, dname, uri_in, + cookiein, cookieinlen, cookieout, + cookieoutlen, uri_out, flags); +} + + +static int +qemuDomainMigratePrepareTunnel3Internal(virConnectPtr dconn, + virStreamPtr st, + const char *dom_xml, + const char *dname, + const char *cookiein, + int cookieinlen, + char **cookieout, + int *cookieoutlen, + unsigned long flags) { virQEMUDriverPtr driver = dconn->privateData; virCapsPtr caps = NULL; virDomainDefPtr def = NULL; int ret = -1; - virCheckFlags(QEMU_MIGRATION_FLAGS, -1); - if (!dom_xml) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("no domain XML passed")); @@ -10093,6 +10183,58 @@ cleanup: return ret; } +static int +qemuDomainMigratePrepareTunnel3(virConnectPtr dconn, + virStreamPtr st, + const char *cookiein, + int cookieinlen, + char **cookieout, + int *cookieoutlen, + unsigned long flags, + const char *dname, + unsigned long resource ATTRIBUTE_UNUSED, + const char *dom_xml) +{ + virCheckFlags(QEMU_MIGRATION_FLAGS, -1); + + return qemuDomainMigratePrepareTunnel3Internal(dconn, st, dom_xml, dname, + cookiein, cookieinlen, + cookieout, cookieoutlen, + flags); +} + +static int +qemuDomainMigratePrepareTunnel3Params(virConnectPtr dconn, + virStreamPtr st, + virTypedParameterPtr params, + int nparams, + const char *cookiein, + int cookieinlen, + char **cookieout, + int *cookieoutlen, + unsigned int flags) +{ + const char *dom_xml = NULL; + const char *dname = NULL; + + virCheckFlags(QEMU_MIGRATION_FLAGS, -1); + if (virTypedParamsValidate(params, nparams, QEMU_MIGRATION_PARAMETERS) < 0) + return -1; + + if (virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_DEST_XML, + &dom_xml) < 0 || + virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_DEST_NAME, + &dname) < 0) + return -1; + + return qemuDomainMigratePrepareTunnel3Internal(dconn, st, dom_xml, dname, + cookiein, cookieinlen, + cookieout, cookieoutlen, + flags); +} + static int qemuDomainMigratePerform3(virDomainPtr dom, @@ -10121,6 +10263,51 @@ qemuDomainMigratePerform3(virDomainPtr dom, flags, dname, resource, true); } +static int +qemuDomainMigratePerform3Params(virDomainPtr dom, + const char *dconnuri, + virTypedParameterPtr params, + int nparams, + const char *cookiein, + int cookieinlen, + char **cookieout, + int *cookieoutlen, + unsigned int flags) +{ + virQEMUDriverPtr driver = dom->conn->privateData; + virDomainObjPtr vm; + const char *dom_xml = NULL; + const char *dname = NULL; + const char *uri = NULL; + unsigned long long bandwidth = 0; + + virCheckFlags(QEMU_MIGRATION_FLAGS, -1); + if (virTypedParamsValidate(params, nparams, QEMU_MIGRATION_PARAMETERS) < 0) + return -1; + + if (virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_DEST_XML, + &dom_xml) < 0 || + virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_DEST_NAME, + &dname) < 0 || + virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_URI, + &uri) < 0 || + virTypedParamsGetULLong(params, nparams, + VIR_MIGRATE_PARAM_BANDWIDTH, + &bandwidth) < 0) + return -1; + + if (!(vm = qemuDomObjFromDomain(dom))) + return -1; + + return qemuMigrationPerform(driver, dom->conn, vm, dom_xml, + dconnuri, uri, cookiein, cookieinlen, + cookieout, cookieoutlen, + flags, dname, bandwidth, true); +} + static virDomainPtr qemuDomainMigrateFinish3(virConnectPtr dconn, @@ -10136,32 +10323,66 @@ qemuDomainMigrateFinish3(virConnectPtr dconn, { virQEMUDriverPtr driver = dconn->privateData; virDomainObjPtr vm; - virDomainPtr dom = NULL; virCheckFlags(QEMU_MIGRATION_FLAGS, NULL); - vm = virDomainObjListFindByName(driver->domains, dname); - if (!vm) { + if (!(vm = virDomainObjListFindByName(driver->domains, dname))) { virReportError(VIR_ERR_NO_DOMAIN, _("no domain with matching name '%s'"), dname); - goto cleanup; + return NULL; } - dom = qemuMigrationFinish(driver, dconn, vm, - cookiein, cookieinlen, - cookieout, cookieoutlen, - flags, cancelled, true); + return qemuMigrationFinish(driver, dconn, vm, + cookiein, cookieinlen, + cookieout, cookieoutlen, + flags, cancelled, true); +} -cleanup: - return dom; +static virDomainPtr +qemuDomainMigrateFinish3Params(virConnectPtr dconn, + virTypedParameterPtr params, + int nparams, + const char *cookiein, + int cookieinlen, + char **cookieout, + int *cookieoutlen, + unsigned int flags, + int cancelled) +{ + virQEMUDriverPtr driver = dconn->privateData; + virDomainObjPtr vm; + const char *dname = NULL; + + virCheckFlags(QEMU_MIGRATION_FLAGS, NULL); + if (virTypedParamsValidate(params, nparams, QEMU_MIGRATION_PARAMETERS) < 0) + return NULL; + + if (virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_DEST_NAME, + &dname) < 0) + return NULL; + + if (!dname || + !(vm = virDomainObjListFindByName(driver->domains, dname))) { + virReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching name '%s'"), + NULLSTR(dname)); + return NULL; + } + + return qemuMigrationFinish(driver, dconn, vm, + cookiein, cookieinlen, + cookieout, cookieoutlen, + flags, cancelled, true); } + static int -qemuDomainMigrateConfirm3(virDomainPtr domain, - const char *cookiein, - int cookieinlen, - unsigned long flags, - int cancelled) +qemuDomainMigrateConfirm3Internal(virDomainPtr domain, + const char *cookiein, + int cookieinlen, + unsigned long flags, + int cancelled) { virQEMUDriverPtr driver = domain->conn->privateData; virDomainObjPtr vm; @@ -10169,8 +10390,6 @@ qemuDomainMigrateConfirm3(virDomainPtr domain, enum qemuMigrationJobPhase phase; virQEMUDriverConfigPtr cfg = NULL; - virCheckFlags(QEMU_MIGRATION_FLAGS, -1); - if (!(vm = qemuDomObjFromDomain(domain))) return -1; @@ -10209,6 +10428,37 @@ cleanup: return ret; } +static int +qemuDomainMigrateConfirm3(virDomainPtr domain, + const char *cookiein, + int cookieinlen, + unsigned long flags, + int cancelled) +{ + virCheckFlags(QEMU_MIGRATION_FLAGS, -1); + + return qemuDomainMigrateConfirm3Internal(domain, cookiein, cookieinlen, + flags, cancelled); +} + +static int +qemuDomainMigrateConfirm3Params(virDomainPtr domain, + virTypedParameterPtr params, + int nparams, + const char *cookiein, + int cookieinlen, + unsigned int flags, + int cancelled) +{ + virCheckFlags(QEMU_MIGRATION_FLAGS, -1); + + if (virTypedParamsValidate(params, nparams, QEMU_MIGRATION_PARAMETERS) < 0) + return -1; + + return qemuDomainMigrateConfirm3Internal(domain, cookiein, cookieinlen, + flags, cancelled); +} + static int qemuNodeDeviceGetPciInfo(virNodeDevicePtr dev, @@ -15378,6 +15628,12 @@ static virDriver qemuDriver = { .nodeGetCPUMap = qemuNodeGetCPUMap, /* 1.0.0 */ .domainFSTrim = qemuDomainFSTrim, /* 1.0.1 */ .domainOpenChannel = qemuDomainOpenChannel, /* 1.0.2 */ + .domainMigrateBegin3Params = qemuDomainMigrateBegin3Params, /* 1.0.7 */ + .domainMigratePrepare3Params = qemuDomainMigratePrepare3Params, /* 1.0.7 */ + .domainMigratePrepareTunnel3Params = qemuDomainMigratePrepareTunnel3Params, /* 1.0.7 */ + .domainMigratePerform3Params = qemuDomainMigratePerform3Params, /* 1.0.7 */ + .domainMigrateFinish3Params = qemuDomainMigrateFinish3Params, /* 1.0.7 */ + .domainMigrateConfirm3Params = qemuDomainMigrateConfirm3Params, /* 1.0.7 */ }; diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 8de38fe..6243d75 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -55,6 +55,7 @@ #include "viruri.h" #include "virhook.h" #include "virstring.h" +#include "virtypedparam.h" #define VIR_FROM_THIS VIR_FROM_QEMU @@ -3306,16 +3307,18 @@ cleanup: * from libvirt.c, but running in source libvirtd context, * instead of client app context & also adding in tunnel * handling */ -static int doPeer2PeerMigrate3(virQEMUDriverPtr driver, - virConnectPtr sconn, - virConnectPtr dconn, - virDomainObjPtr vm, - const char *xmlin, - const char *dconnuri, - const char *uri, - unsigned long flags, - const char *dname, - unsigned long resource) +static int +doPeer2PeerMigrate3(virQEMUDriverPtr driver, + virConnectPtr sconn, + virConnectPtr dconn, + const char *dconnuri, + virDomainObjPtr vm, + const char *xmlin, + const char *dname, + const char *uri, + unsigned long long bandwidth, + bool useParams, + unsigned long flags) { virDomainPtr ddomain = NULL; char *uri_out = NULL; @@ -3326,15 +3329,18 @@ static int doPeer2PeerMigrate3(virQEMUDriverPtr driver, int cookieoutlen = 0; int ret = -1; virErrorPtr orig_err = NULL; - bool cancelled; + bool cancelled = true; virStreamPtr st = NULL; unsigned int destflags = flags & ~VIR_MIGRATE_ABORT_ON_ERROR; - VIR_DEBUG("driver=%p, sconn=%p, dconn=%p, vm=%p, xmlin=%s, " - "dconnuri=%s, uri=%s, flags=%lx, dname=%s, resource=%lu", - driver, sconn, dconn, vm, NULLSTR(xmlin), - NULLSTR(dconnuri), NULLSTR(uri), flags, - NULLSTR(dname), resource); + virTypedParameterPtr params = NULL; + int nparams = 0; + int maxparams = 0; + + VIR_DEBUG("driver=%p, sconn=%p, dconn=%p, dconnuri=%s, vm=%p, xmlin=%s, " + "dname=%s, uri=%s, bandwidth=%llu, useParams=%d, flags=%lx", + driver, sconn, dconn, NULLSTR(dconnuri), vm, NULLSTR(xmlin), + NULLSTR(dname), NULLSTR(uri), bandwidth, useParams, flags); /* Unlike the virDomainMigrateVersion3 counterpart, we don't need * to worry about auto-setting the VIR_MIGRATE_CHANGE_PROTECTION @@ -3346,6 +3352,28 @@ static int doPeer2PeerMigrate3(virQEMUDriverPtr driver, if (!dom_xml) goto cleanup; + if (useParams) { + if (virTypedParamsAddString(¶ms, &nparams, &maxparams, + VIR_MIGRATE_PARAM_DEST_XML, dom_xml) < 0) + goto cleanup; + + if (dname && + virTypedParamsAddString(¶ms, &nparams, &maxparams, + VIR_MIGRATE_PARAM_DEST_NAME, dname) < 0) + goto cleanup; + + if (uri && + virTypedParamsAddString(¶ms, &nparams, &maxparams, + VIR_MIGRATE_PARAM_URI, uri) < 0) + goto cleanup; + + if (bandwidth && + virTypedParamsAddULLong(¶ms, &nparams, &maxparams, + VIR_MIGRATE_PARAM_BANDWIDTH, + bandwidth) < 0) + goto cleanup; + } + if (virDomainObjGetState(vm, NULL) == VIR_DOMAIN_PAUSED) flags |= VIR_MIGRATE_PAUSED; @@ -3359,16 +3387,27 @@ static int doPeer2PeerMigrate3(virQEMUDriverPtr driver, goto cleanup; qemuDomainObjEnterRemote(vm); - ret = dconn->driver->domainMigratePrepareTunnel3 - (dconn, st, cookiein, cookieinlen, - &cookieout, &cookieoutlen, - destflags, dname, resource, dom_xml); + if (useParams) { + ret = dconn->driver->domainMigratePrepareTunnel3Params + (dconn, st, params, nparams, cookiein, cookieinlen, + &cookieout, &cookieoutlen, destflags); + } else { + ret = dconn->driver->domainMigratePrepareTunnel3 + (dconn, st, cookiein, cookieinlen, &cookieout, &cookieoutlen, + destflags, dname, bandwidth, dom_xml); + } qemuDomainObjExitRemote(vm); } else { qemuDomainObjEnterRemote(vm); - ret = dconn->driver->domainMigratePrepare3 - (dconn, cookiein, cookieinlen, &cookieout, &cookieoutlen, - uri, &uri_out, destflags, dname, resource, dom_xml); + if (useParams) { + ret = dconn->driver->domainMigratePrepare3Params + (dconn, params, nparams, cookiein, cookieinlen, + &cookieout, &cookieoutlen, &uri_out, destflags); + } else { + ret = dconn->driver->domainMigratePrepare3 + (dconn, cookiein, cookieinlen, &cookieout, &cookieoutlen, + uri, &uri_out, destflags, dname, bandwidth, dom_xml); + } qemuDomainObjExitRemote(vm); } VIR_FREE(dom_xml); @@ -3383,11 +3422,15 @@ static int doPeer2PeerMigrate3(virQEMUDriverPtr driver, goto finish; } - if (!(flags & VIR_MIGRATE_TUNNELLED) && - (uri_out == NULL)) { + if (uri_out) { + uri = uri_out; + if (useParams && + virTypedParamsReplaceString(¶ms, &nparams, + VIR_MIGRATE_PARAM_URI, uri_out) < 0) + goto finish; + } else if (!uri && !(flags & VIR_MIGRATE_TUNNELLED)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("domainMigratePrepare3 did not set uri")); - cancelled = true; goto finish; } @@ -3396,23 +3439,24 @@ static int doPeer2PeerMigrate3(virQEMUDriverPtr driver, * running, but in paused state until the destination can * confirm migration completion. */ - VIR_DEBUG("Perform3 %p uri=%s uri_out=%s", sconn, uri, uri_out); + VIR_DEBUG("Perform3 %p uri=%s", sconn, NULLSTR(uri)); qemuMigrationJobSetPhase(driver, vm, QEMU_MIGRATION_PHASE_PERFORM3); VIR_FREE(cookiein); cookiein = cookieout; cookieinlen = cookieoutlen; cookieout = NULL; cookieoutlen = 0; - if (flags & VIR_MIGRATE_TUNNELLED) + if (flags & VIR_MIGRATE_TUNNELLED) { ret = doTunnelMigrate(driver, vm, st, cookiein, cookieinlen, &cookieout, &cookieoutlen, - flags, resource, dconn); - else - ret = doNativeMigrate(driver, vm, uri_out, + flags, bandwidth, dconn); + } else { + ret = doNativeMigrate(driver, vm, uri, cookiein, cookieinlen, &cookieout, &cookieoutlen, - flags, resource, dconn); + flags, bandwidth, dconn); + } /* Perform failed. Make sure Finish doesn't overwrite the error */ if (ret < 0) { @@ -3440,12 +3484,29 @@ finish: cookieinlen = cookieoutlen; cookieout = NULL; cookieoutlen = 0; - dname = dname ? dname : vm->def->name; - qemuDomainObjEnterRemote(vm); - ddomain = dconn->driver->domainMigrateFinish3 - (dconn, dname, cookiein, cookieinlen, &cookieout, &cookieoutlen, - dconnuri, uri_out ? uri_out : uri, destflags, cancelled); - qemuDomainObjExitRemote(vm); + + if (useParams) { + if (virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_DEST_NAME, NULL) <= 0 && + virTypedParamsReplaceString(¶ms, &nparams, + VIR_MIGRATE_PARAM_DEST_NAME, + vm->def->name) < 0) { + ddomain = NULL; + } else { + qemuDomainObjEnterRemote(vm); + ddomain = dconn->driver->domainMigrateFinish3Params + (dconn, params, nparams, cookiein, cookieinlen, + &cookieout, &cookieoutlen, destflags, cancelled); + qemuDomainObjExitRemote(vm); + } + } else { + dname = dname ? dname : vm->def->name; + qemuDomainObjEnterRemote(vm); + ddomain = dconn->driver->domainMigrateFinish3 + (dconn, dname, cookiein, cookieinlen, &cookieout, &cookieoutlen, + dconnuri, uri, destflags, cancelled); + qemuDomainObjExitRemote(vm); + } /* If ddomain is NULL, then we were unable to start * the guest on the target, and must restart on the @@ -3501,7 +3562,7 @@ finish: VIR_FREE(uri_out); VIR_FREE(cookiein); VIR_FREE(cookieout); - + virTypedParamsFree(params, nparams); return ret; } @@ -3523,6 +3584,7 @@ static int doPeer2PeerMigrate(virQEMUDriverPtr driver, virErrorPtr orig_err = NULL; bool offline = false; virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + bool useParams; VIR_DEBUG("driver=%p, sconn=%p, vm=%p, xmlin=%s, dconnuri=%s, " "uri=%s, flags=%lx, dname=%s, resource=%lu", @@ -3557,6 +3619,8 @@ static int doPeer2PeerMigrate(virQEMUDriverPtr driver, */ *v3proto = VIR_DRV_SUPPORTS_FEATURE(dconn->driver, dconn, VIR_DRV_FEATURE_MIGRATION_V3); + useParams = VIR_DRV_SUPPORTS_FEATURE(dconn->driver, dconn, + VIR_DRV_FEATURE_MIGRATION_PARAMS); if (flags & VIR_MIGRATE_OFFLINE) offline = VIR_DRV_SUPPORTS_FEATURE(dconn->driver, dconn, VIR_DRV_FEATURE_MIGRATION_OFFLINE); @@ -3568,6 +3632,17 @@ static int doPeer2PeerMigrate(virQEMUDriverPtr driver, goto cleanup; } + /* Only xmlin, dname, uri, and bandwidth parameters can be used with + * old-style APIs. */ +#if 0 + if (!useParams && /* any new parameter */) { + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", + _("Migration APIs with extensible parameters are not " + "supported but extended parameters were passed")); + goto cleanup; + } +#endif + if (flags & VIR_MIGRATE_OFFLINE && !offline) { virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", _("offline migration is not supported by " @@ -3589,12 +3664,13 @@ static int doPeer2PeerMigrate(virQEMUDriverPtr driver, * Therefore it is safe to clear the bit here. */ flags &= ~VIR_MIGRATE_CHANGE_PROTECTION; - if (*v3proto) - ret = doPeer2PeerMigrate3(driver, sconn, dconn, vm, xmlin, - dconnuri, uri, flags, dname, resource); - else + if (*v3proto) { + ret = doPeer2PeerMigrate3(driver, sconn, dconn, dconnuri, vm, xmlin, + dname, uri, resource, useParams, flags); + } else { ret = doPeer2PeerMigrate2(driver, sconn, dconn, vm, dconnuri, flags, dname, resource); + } cleanup: orig_err = virSaveLastError(); diff --git a/src/qemu/qemu_migration.h b/src/qemu/qemu_migration.h index 5b21ca2..e44cf3b 100644 --- a/src/qemu/qemu_migration.h +++ b/src/qemu/qemu_migration.h @@ -41,6 +41,15 @@ VIR_MIGRATE_COMPRESSED | \ VIR_MIGRATE_ABORT_ON_ERROR) +/* All supported migration parameters and their types. */ +# define QEMU_MIGRATION_PARAMETERS \ + VIR_MIGRATE_PARAM_URI, VIR_TYPED_PARAM_STRING, \ + VIR_MIGRATE_PARAM_DEST_NAME, VIR_TYPED_PARAM_STRING, \ + VIR_MIGRATE_PARAM_DEST_XML, VIR_TYPED_PARAM_STRING, \ + VIR_MIGRATE_PARAM_BANDWIDTH, VIR_TYPED_PARAM_ULLONG, \ + NULL + + enum qemuMigrationJobPhase { QEMU_MIGRATION_PHASE_NONE = 0, QEMU_MIGRATION_PHASE_PERFORM2, -- 1.8.2.1

On Tue, Jun 18, 2013 at 04:05:53PM +0200, Jiri Denemark wrote:
--- src/qemu/qemu_driver.c | 350 +++++++++++++++++++++++++++++++++++++++------- src/qemu/qemu_migration.c | 162 +++++++++++++++------ src/qemu/qemu_migration.h | 9 ++ 3 files changed, 431 insertions(+), 90 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index a682e36..c040287 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1086,6 +1086,7 @@ qemuConnectSupportsFeature(virConnectPtr conn ATTRIBUTE_UNUSED, int feature) case VIR_DRV_FEATURE_TYPED_PARAM_STRING: case VIR_DRV_FEATURE_XML_MIGRATABLE: case VIR_DRV_FEATURE_MIGRATION_OFFLINE: + case VIR_DRV_FEATURE_MIGRATION_PARAMS: return 1; default: return 0; @@ -9887,21 +9888,18 @@ cleanup: *******************************************************************/
static char * -qemuDomainMigrateBegin3(virDomainPtr domain, - const char *xmlin, - char **cookieout, - int *cookieoutlen, - unsigned long flags, - const char *dname, - unsigned long resource ATTRIBUTE_UNUSED) +qemuDomainMigrateBegin3Internal(virDomainPtr domain, + const char *xmlin, + const char *dname, + char **cookieout, + int *cookieoutlen, + unsigned long flags) { virQEMUDriverPtr driver = domain->conn->privateData; virDomainObjPtr vm; char *xml = NULL; enum qemuDomainAsyncJob asyncJob;
- virCheckFlags(QEMU_MIGRATION_FLAGS, NULL); - if (!(vm = qemuDomObjFromDomain(domain))) return NULL;
@@ -9969,26 +9967,66 @@ endjob: goto cleanup; }
+static char * +qemuDomainMigrateBegin3(virDomainPtr domain, + const char *xmlin, + char **cookieout, + int *cookieoutlen, + unsigned long flags, + const char *dname, + unsigned long resource ATTRIBUTE_UNUSED) +{ + virCheckFlags(QEMU_MIGRATION_FLAGS, NULL); + + return qemuDomainMigrateBegin3Internal(domain, xmlin, dname, + cookieout, cookieoutlen, flags); +} + +static char * +qemuDomainMigrateBegin3Params(virDomainPtr domain, + virTypedParameterPtr params, + int nparams, + char **cookieout, + int *cookieoutlen, + unsigned int flags) +{ + const char *xmlin = NULL; + const char *dname = NULL; + + virCheckFlags(QEMU_MIGRATION_FLAGS, NULL); + if (virTypedParamsValidate(params, nparams, QEMU_MIGRATION_PARAMETERS) < 0) + return NULL; + + if (virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_DEST_XML, + &xmlin) < 0 || + virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_DEST_NAME, + &dname) < 0) + return NULL; + + return qemuDomainMigrateBegin3Internal(domain, xmlin, dname, + cookieout, cookieoutlen, flags); +}
This change isn't going to fly with the access control check requirements. We mandate that the conversion from virDomainPtr to virDomainDefPtr must always happen in the method that is registered in the driver table. ie you must call qemuDomObjFromDomain() directly from qemuDomainMigrateBegin3 and qemuDomainMigrateBegin3Params and then pass the 'def' into qemuDomainMigrateBegin3Internal Likewise for all the other methods you change in a similar way here. The test suite for the access control code will fail with your patch as it is. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

--- src/qemu/qemu_driver.c | 99 +++++++---------------------------------------- src/qemu/qemu_migration.c | 98 +++++++++++++++++++++++++++++++++++++++++----- src/qemu/qemu_migration.h | 1 + 3 files changed, 105 insertions(+), 93 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index c040287..14397f5 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -9888,86 +9888,6 @@ cleanup: *******************************************************************/ static char * -qemuDomainMigrateBegin3Internal(virDomainPtr domain, - const char *xmlin, - const char *dname, - char **cookieout, - int *cookieoutlen, - unsigned long flags) -{ - virQEMUDriverPtr driver = domain->conn->privateData; - virDomainObjPtr vm; - char *xml = NULL; - enum qemuDomainAsyncJob asyncJob; - - if (!(vm = qemuDomObjFromDomain(domain))) - return NULL; - - if ((flags & VIR_MIGRATE_CHANGE_PROTECTION)) { - if (qemuMigrationJobStart(driver, vm, QEMU_ASYNC_JOB_MIGRATION_OUT) < 0) - goto cleanup; - asyncJob = QEMU_ASYNC_JOB_MIGRATION_OUT; - } else { - if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) - goto cleanup; - asyncJob = QEMU_ASYNC_JOB_NONE; - } - - if (!virDomainObjIsActive(vm) && !(flags & VIR_MIGRATE_OFFLINE)) { - virReportError(VIR_ERR_OPERATION_INVALID, - "%s", _("domain is not running")); - goto endjob; - } - - /* Check if there is any ejected media. - * We don't want to require them on the destination. - */ - if (!(flags & VIR_MIGRATE_OFFLINE) && - qemuDomainCheckEjectableMedia(driver, vm, asyncJob) < 0) - goto endjob; - - if (!(xml = qemuMigrationBegin(driver, vm, xmlin, dname, - cookieout, cookieoutlen, - flags))) - goto endjob; - - if ((flags & VIR_MIGRATE_CHANGE_PROTECTION)) { - /* We keep the job active across API calls until the confirm() call. - * This prevents any other APIs being invoked while migration is taking - * place. - */ - if (virQEMUCloseCallbacksSet(driver->closeCallbacks, vm, domain->conn, - qemuMigrationCleanup) < 0) - goto endjob; - if (qemuMigrationJobContinue(vm) == 0) { - vm = NULL; - virReportError(VIR_ERR_OPERATION_FAILED, - "%s", _("domain disappeared")); - VIR_FREE(xml); - if (cookieout) - VIR_FREE(*cookieout); - } - } else { - goto endjob; - } - -cleanup: - if (vm) - virObjectUnlock(vm); - return xml; - -endjob: - if ((flags & VIR_MIGRATE_CHANGE_PROTECTION)) { - if (qemuMigrationJobFinish(driver, vm) == 0) - vm = NULL; - } else { - if (qemuDomainObjEndJob(driver, vm) == 0) - vm = NULL; - } - goto cleanup; -} - -static char * qemuDomainMigrateBegin3(virDomainPtr domain, const char *xmlin, char **cookieout, @@ -9976,10 +9896,16 @@ qemuDomainMigrateBegin3(virDomainPtr domain, const char *dname, unsigned long resource ATTRIBUTE_UNUSED) { + virDomainObjPtr vm; + virCheckFlags(QEMU_MIGRATION_FLAGS, NULL); - return qemuDomainMigrateBegin3Internal(domain, xmlin, dname, - cookieout, cookieoutlen, flags); + if (!(vm = qemuDomObjFromDomain(domain))) + return NULL; + + return qemuMigrationBegin(domain->conn->privateData, + domain->conn, vm, xmlin, dname, + cookieout, cookieoutlen, flags); } static char * @@ -9992,6 +9918,7 @@ qemuDomainMigrateBegin3Params(virDomainPtr domain, { const char *xmlin = NULL; const char *dname = NULL; + virDomainObjPtr vm; virCheckFlags(QEMU_MIGRATION_FLAGS, NULL); if (virTypedParamsValidate(params, nparams, QEMU_MIGRATION_PARAMETERS) < 0) @@ -10005,8 +9932,12 @@ qemuDomainMigrateBegin3Params(virDomainPtr domain, &dname) < 0) return NULL; - return qemuDomainMigrateBegin3Internal(domain, xmlin, dname, - cookieout, cookieoutlen, flags); + if (!(vm = qemuDomObjFromDomain(domain))) + return NULL; + + return qemuMigrationBegin(domain->conn->privateData, + domain->conn, vm, xmlin, dname, + cookieout, cookieoutlen, flags); } diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 6243d75..c0b721a 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -39,6 +39,7 @@ #include "qemu_capabilities.h" #include "qemu_command.h" #include "qemu_cgroup.h" +#include "qemu_hotplug.h" #include "domain_audit.h" #include "virlog.h" @@ -1924,14 +1925,16 @@ cleanup: return vm; } + /* The caller is supposed to lock the vm and start a migration job. */ -char *qemuMigrationBegin(virQEMUDriverPtr driver, - virDomainObjPtr vm, - const char *xmlin, - const char *dname, - char **cookieout, - int *cookieoutlen, - unsigned long flags) +static char * +qemuMigrationBeginPhase(virQEMUDriverPtr driver, + virDomainObjPtr vm, + const char *xmlin, + const char *dname, + char **cookieout, + int *cookieoutlen, + unsigned long flags) { char *rv = NULL; qemuMigrationCookiePtr mig = NULL; @@ -2025,6 +2028,83 @@ cleanup: return rv; } +char * +qemuMigrationBegin(virQEMUDriverPtr driver, + virConnectPtr conn, + virDomainObjPtr vm, + const char *xmlin, + const char *dname, + char **cookieout, + int *cookieoutlen, + unsigned long flags) +{ + char *xml = NULL; + enum qemuDomainAsyncJob asyncJob; + + if ((flags & VIR_MIGRATE_CHANGE_PROTECTION)) { + if (qemuMigrationJobStart(driver, vm, QEMU_ASYNC_JOB_MIGRATION_OUT) < 0) + goto cleanup; + asyncJob = QEMU_ASYNC_JOB_MIGRATION_OUT; + } else { + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + asyncJob = QEMU_ASYNC_JOB_NONE; + } + + if (!virDomainObjIsActive(vm) && !(flags & VIR_MIGRATE_OFFLINE)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto endjob; + } + + /* Check if there is any ejected media. + * We don't want to require them on the destination. + */ + if (!(flags & VIR_MIGRATE_OFFLINE) && + qemuDomainCheckEjectableMedia(driver, vm, asyncJob) < 0) + goto endjob; + + if (!(xml = qemuMigrationBeginPhase(driver, vm, xmlin, dname, + cookieout, cookieoutlen, + flags))) + goto endjob; + + if ((flags & VIR_MIGRATE_CHANGE_PROTECTION)) { + /* We keep the job active across API calls until the confirm() call. + * This prevents any other APIs being invoked while migration is taking + * place. + */ + if (virQEMUCloseCallbacksSet(driver->closeCallbacks, vm, conn, + qemuMigrationCleanup) < 0) + goto endjob; + if (qemuMigrationJobContinue(vm) == 0) { + vm = NULL; + virReportError(VIR_ERR_OPERATION_FAILED, + "%s", _("domain disappeared")); + VIR_FREE(xml); + if (cookieout) + VIR_FREE(*cookieout); + } + } else { + goto endjob; + } + +cleanup: + if (vm) + virObjectUnlock(vm); + return xml; + +endjob: + if ((flags & VIR_MIGRATE_CHANGE_PROTECTION)) { + if (qemuMigrationJobFinish(driver, vm) == 0) + vm = NULL; + } else { + if (qemuDomainObjEndJob(driver, vm) == 0) + vm = NULL; + } + goto cleanup; +} + /* Prepare is the first step, and it runs on the destination host. */ @@ -3347,8 +3427,8 @@ doPeer2PeerMigrate3(virQEMUDriverPtr driver, * bit here, because we are already running inside the context of * a single job. */ - dom_xml = qemuMigrationBegin(driver, vm, xmlin, dname, - &cookieout, &cookieoutlen, flags); + dom_xml = qemuMigrationBeginPhase(driver, vm, xmlin, dname, + &cookieout, &cookieoutlen, flags); if (!dom_xml) goto cleanup; diff --git a/src/qemu/qemu_migration.h b/src/qemu/qemu_migration.h index e44cf3b..ef7307e 100644 --- a/src/qemu/qemu_migration.h +++ b/src/qemu/qemu_migration.h @@ -94,6 +94,7 @@ virDomainObjPtr qemuMigrationCleanup(virQEMUDriverPtr driver, virConnectPtr conn); char *qemuMigrationBegin(virQEMUDriverPtr driver, + virConnectPtr conn, virDomainObjPtr vm, const char *xmlin, const char *dname, -- 1.8.2.1

--- src/qemu/qemu_driver.c | 277 +++++++++++----------------------------------- src/qemu/qemu_migration.c | 70 +++++++++++- src/qemu/qemu_migration.h | 31 ++---- 3 files changed, 145 insertions(+), 233 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 14397f5..80ec379 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -9670,57 +9670,28 @@ qemuDomainMigratePrepareTunnel(virConnectPtr dconn, const char *dom_xml) { virQEMUDriverPtr driver = dconn->privateData; - virCapsPtr caps = NULL; - virDomainDefPtr def = NULL; - int ret = -1; virCheckFlags(QEMU_MIGRATION_FLAGS, -1); - if (!dom_xml) { - virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("no domain XML passed")); - goto cleanup; - } if (!(flags & VIR_MIGRATE_TUNNELLED)) { - virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("PrepareTunnel called but no TUNNELLED flag set")); - goto cleanup; - } - if (st == NULL) { - virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("tunnelled migration requested but NULL stream passed")); - goto cleanup; + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("PrepareTunnel called but no TUNNELLED flag set")); + return -1; } if (virLockManagerPluginUsesState(driver->lockManager)) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Cannot use migrate v2 protocol with lock manager %s"), virLockManagerPluginGetName(driver->lockManager)); - goto cleanup; - } - - if (!(caps = virQEMUDriverGetCapabilities(driver, false))) - goto cleanup; - - if (!(def = virDomainDefParseString(dom_xml, caps, driver->xmlopt, - QEMU_EXPECTED_VIRT_TYPES, - VIR_DOMAIN_XML_INACTIVE))) - goto cleanup; - - if (dname) { - VIR_FREE(def->name); - if (VIR_STRDUP(def->name, dname) < 0) - goto cleanup; + return -1; } - ret = qemuMigrationPrepareTunnel(driver, dconn, - NULL, 0, NULL, NULL, /* No cookies in v2 */ - st, &def, flags); - -cleanup: - virDomainDefFree(def); - virObjectUnref(caps); - return ret; + /* Do not use cookies in v2 protocol, since the cookie + * length was not sufficiently large, causing failures + * migrating between old & new libvirtd + */ + return qemuMigrationPrepare(dconn, dom_xml, dname, NULL, st, + NULL, 0, NULL, NULL, NULL, flags); } /* Prepare is the first step, and it runs on the destination host. @@ -9739,63 +9710,32 @@ qemuDomainMigratePrepare2(virConnectPtr dconn, const char *dom_xml) { virQEMUDriverPtr driver = dconn->privateData; - virCapsPtr caps = NULL; - virDomainDefPtr def = NULL; - int ret = -1; virCheckFlags(QEMU_MIGRATION_FLAGS, -1); - *uri_out = NULL; - - if (virLockManagerPluginUsesState(driver->lockManager)) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Cannot use migrate v2 protocol with lock manager %s"), - virLockManagerPluginGetName(driver->lockManager)); - goto cleanup; - } - if (flags & VIR_MIGRATE_TUNNELLED) { /* this is a logical error; we never should have gotten here with * VIR_MIGRATE_TUNNELLED set */ - virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("Tunnelled migration requested but invalid RPC method called")); - goto cleanup; + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Tunnelled migration requested but invalid RPC " + "method called")); + return -1; } - if (!dom_xml) { + if (virLockManagerPluginUsesState(driver->lockManager)) { virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("no domain XML passed")); - goto cleanup; - } - - if (!(caps = virQEMUDriverGetCapabilities(driver, false))) - goto cleanup; - - if (!(def = virDomainDefParseString(dom_xml, caps, driver->xmlopt, - QEMU_EXPECTED_VIRT_TYPES, - VIR_DOMAIN_XML_INACTIVE))) - goto cleanup; - - if (dname) { - VIR_FREE(def->name); - if (VIR_STRDUP(def->name, dname) < 0) - goto cleanup; + _("Cannot use migrate v2 protocol with lock manager %s"), + virLockManagerPluginGetName(driver->lockManager)); + return -1; } /* Do not use cookies in v2 protocol, since the cookie * length was not sufficiently large, causing failures * migrating between old & new libvirtd */ - ret = qemuMigrationPrepareDirect(driver, dconn, - NULL, 0, NULL, NULL, /* No cookies */ - uri_in, uri_out, - &def, flags); - -cleanup: - virDomainDefFree(def); - virObjectUnref(caps); - return ret; + return qemuMigrationPrepare(dconn, dom_xml, dname, uri_in, NULL, + NULL, 0, NULL, NULL, uri_out, flags); } @@ -9942,66 +9882,6 @@ qemuDomainMigrateBegin3Params(virDomainPtr domain, static int -qemuDomainMigratePrepare3Internal(virConnectPtr dconn, - const char *dom_xml, - const char *dname, - const char *uri_in, - const char *cookiein, - int cookieinlen, - char **cookieout, - int *cookieoutlen, - char **uri_out, - unsigned long flags) -{ - virQEMUDriverPtr driver = dconn->privateData; - virCapsPtr caps = NULL; - virDomainDefPtr def = NULL; - int ret = -1; - - *uri_out = NULL; - - if (flags & VIR_MIGRATE_TUNNELLED) { - /* this is a logical error; we never should have gotten here with - * VIR_MIGRATE_TUNNELLED set - */ - virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("Tunnelled migration requested but invalid RPC method called")); - goto cleanup; - } - - if (!dom_xml) { - virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("no domain XML passed")); - goto cleanup; - } - - if (!(caps = virQEMUDriverGetCapabilities(driver, false))) - goto cleanup; - - if (!(def = virDomainDefParseString(dom_xml, caps, driver->xmlopt, - QEMU_EXPECTED_VIRT_TYPES, - VIR_DOMAIN_XML_INACTIVE))) - goto cleanup; - - if (dname) { - VIR_FREE(def->name); - if (VIR_STRDUP(def->name, dname) < 0) - goto cleanup; - } - - ret = qemuMigrationPrepareDirect(driver, dconn, - cookiein, cookieinlen, - cookieout, cookieoutlen, - uri_in, uri_out, - &def, flags); - -cleanup: - virDomainDefFree(def); - virObjectUnref(caps); - return ret; -} - -static int qemuDomainMigratePrepare3(virConnectPtr dconn, const char *cookiein, int cookieinlen, @@ -10016,9 +9896,19 @@ qemuDomainMigratePrepare3(virConnectPtr dconn, { virCheckFlags(QEMU_MIGRATION_FLAGS, -1); - return qemuDomainMigratePrepare3Internal(dconn, dom_xml, dname, uri_in, - cookiein, cookieinlen, cookieout, - cookieoutlen, uri_out, flags); + if (flags & VIR_MIGRATE_TUNNELLED) { + /* this is a logical error; we never should have gotten here with + * VIR_MIGRATE_TUNNELLED set + */ + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Tunnelled migration requested but invalid RPC " + "method called")); + return -1; + } + + return qemuMigrationPrepare(dconn, dom_xml, dname, uri_in, NULL, + cookiein, cookieinlen, cookieout, + cookieoutlen, uri_out, flags); } static int @@ -10037,6 +9927,17 @@ qemuDomainMigratePrepare3Params(virConnectPtr dconn, const char *uri_in = NULL; virCheckFlags(QEMU_MIGRATION_FLAGS, -1); + + if (flags & VIR_MIGRATE_TUNNELLED) { + /* this is a logical error; we never should have gotten here with + * VIR_MIGRATE_TUNNELLED set + */ + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Tunnelled migration requested but invalid RPC " + "method called")); + return -1; + } + if (virTypedParamsValidate(params, nparams, QEMU_MIGRATION_PARAMETERS) < 0) return -1; @@ -10051,70 +9952,13 @@ qemuDomainMigratePrepare3Params(virConnectPtr dconn, &uri_in) < 0) return -1; - return qemuDomainMigratePrepare3Internal(dconn, dom_xml, dname, uri_in, - cookiein, cookieinlen, cookieout, - cookieoutlen, uri_out, flags); + return qemuMigrationPrepare(dconn, dom_xml, dname, uri_in, NULL, + cookiein, cookieinlen, cookieout, + cookieoutlen, uri_out, flags); } static int -qemuDomainMigratePrepareTunnel3Internal(virConnectPtr dconn, - virStreamPtr st, - const char *dom_xml, - const char *dname, - const char *cookiein, - int cookieinlen, - char **cookieout, - int *cookieoutlen, - unsigned long flags) -{ - virQEMUDriverPtr driver = dconn->privateData; - virCapsPtr caps = NULL; - virDomainDefPtr def = NULL; - int ret = -1; - - if (!dom_xml) { - virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("no domain XML passed")); - goto cleanup; - } - if (!(flags & VIR_MIGRATE_TUNNELLED)) { - virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("PrepareTunnel called but no TUNNELLED flag set")); - goto cleanup; - } - if (st == NULL) { - virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("tunnelled migration requested but NULL stream passed")); - goto cleanup; - } - - if (!(caps = virQEMUDriverGetCapabilities(driver, false))) - goto cleanup; - - if (!(def = virDomainDefParseString(dom_xml, caps, driver->xmlopt, - QEMU_EXPECTED_VIRT_TYPES, - VIR_DOMAIN_XML_INACTIVE))) - goto cleanup; - - if (dname) { - VIR_FREE(def->name); - if (VIR_STRDUP(def->name, dname) < 0) - goto cleanup; - } - - ret = qemuMigrationPrepareTunnel(driver, dconn, - cookiein, cookieinlen, - cookieout, cookieoutlen, - st, &def, flags); - -cleanup: - virDomainDefFree(def); - virObjectUnref(caps); - return ret; -} - -static int qemuDomainMigratePrepareTunnel3(virConnectPtr dconn, virStreamPtr st, const char *cookiein, @@ -10128,10 +9972,15 @@ qemuDomainMigratePrepareTunnel3(virConnectPtr dconn, { virCheckFlags(QEMU_MIGRATION_FLAGS, -1); - return qemuDomainMigratePrepareTunnel3Internal(dconn, st, dom_xml, dname, - cookiein, cookieinlen, - cookieout, cookieoutlen, - flags); + if (!(flags & VIR_MIGRATE_TUNNELLED)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("PrepareTunnel called but no TUNNELLED flag set")); + return -1; + } + + return qemuMigrationPrepare(dconn, dom_xml, dname, NULL, st, + cookiein, cookieinlen, cookieout, + cookieoutlen, NULL, flags); } static int @@ -10149,6 +9998,13 @@ qemuDomainMigratePrepareTunnel3Params(virConnectPtr dconn, const char *dname = NULL; virCheckFlags(QEMU_MIGRATION_FLAGS, -1); + + if (!(flags & VIR_MIGRATE_TUNNELLED)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("PrepareTunnel called but no TUNNELLED flag set")); + return -1; + } + if (virTypedParamsValidate(params, nparams, QEMU_MIGRATION_PARAMETERS) < 0) return -1; @@ -10160,10 +10016,9 @@ qemuDomainMigratePrepareTunnel3Params(virConnectPtr dconn, &dname) < 0) return -1; - return qemuDomainMigratePrepareTunnel3Internal(dconn, st, dom_xml, dname, - cookiein, cookieinlen, - cookieout, cookieoutlen, - flags); + return qemuMigrationPrepare(dconn, dom_xml, dname, NULL, st, + cookiein, cookieinlen, cookieout, + cookieoutlen, NULL, flags); } diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index c0b721a..7559625 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -2424,7 +2424,7 @@ endjob: * This version starts an empty VM listening on a localhost TCP port, and * sets up the corresponding virStream to handle the incoming data. */ -int +static int qemuMigrationPrepareTunnel(virQEMUDriverPtr driver, virConnectPtr dconn, const char *cookiein, @@ -2450,7 +2450,7 @@ qemuMigrationPrepareTunnel(virQEMUDriverPtr driver, } -int +static int qemuMigrationPrepareDirect(virQEMUDriverPtr driver, virConnectPtr dconn, const char *cookiein, @@ -2477,6 +2477,8 @@ qemuMigrationPrepareDirect(virQEMUDriverPtr driver, cookieout, cookieoutlen, NULLSTR(uri_in), uri_out, *def, flags); + *uri_out = NULL; + /* The URI passed in may be NULL or a string "tcp://somehostname:port". * * If the URI passed in is NULL then we allocate a port number @@ -2580,6 +2582,70 @@ cleanup: return ret; } +int +qemuMigrationPrepare(virConnectPtr dconn, + const char *dom_xml, + const char *dname, + const char *uri_in, + virStreamPtr st, + const char *cookiein, + int cookieinlen, + char **cookieout, + int *cookieoutlen, + char **uri_out, + unsigned long flags) +{ + virQEMUDriverPtr driver = dconn->privateData; + virCapsPtr caps = NULL; + virDomainDefPtr def = NULL; + int ret = -1; + + if ((flags & VIR_MIGRATE_TUNNELLED) && !st) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("tunnelled migration requested but NULL stream " + "passed")); + goto cleanup; + } + + if (!dom_xml) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("no domain XML passed")); + goto cleanup; + } + + if (!(caps = virQEMUDriverGetCapabilities(driver, false))) + goto cleanup; + + if (!(def = virDomainDefParseString(dom_xml, caps, driver->xmlopt, + QEMU_EXPECTED_VIRT_TYPES, + VIR_DOMAIN_XML_INACTIVE))) + goto cleanup; + + if (dname) { + VIR_FREE(def->name); + if (VIR_STRDUP(def->name, dname) < 0) + goto cleanup; + } + + if (flags & VIR_MIGRATE_TUNNELLED) { + ret = qemuMigrationPrepareTunnel(driver, dconn, + cookiein, cookieinlen, + cookieout, cookieoutlen, + st, &def, flags); + } else { + ret = qemuMigrationPrepareDirect(driver, dconn, + cookiein, cookieinlen, + cookieout, cookieoutlen, + uri_in, uri_out, + &def, flags); + } + +cleanup: + virDomainDefFree(def); + virObjectUnref(caps); + return ret; +} + enum qemuMigrationDestinationType { MIGRATION_DEST_HOST, diff --git a/src/qemu/qemu_migration.h b/src/qemu/qemu_migration.h index ef7307e..2e03b78 100644 --- a/src/qemu/qemu_migration.h +++ b/src/qemu/qemu_migration.h @@ -102,26 +102,17 @@ char *qemuMigrationBegin(virQEMUDriverPtr driver, int *cookieoutlen, unsigned long flags); -int qemuMigrationPrepareTunnel(virQEMUDriverPtr driver, - virConnectPtr dconn, - const char *cookiein, - int cookieinlen, - char **cookieout, - int *cookieoutlen, - virStreamPtr st, - virDomainDefPtr *def, - unsigned long flags); - -int qemuMigrationPrepareDirect(virQEMUDriverPtr driver, - virConnectPtr dconn, - const char *cookiein, - int cookieinlen, - char **cookieout, - int *cookieoutlen, - const char *uri_in, - char **uri_out, - virDomainDefPtr *def, - unsigned long flags); +int qemuMigrationPrepare(virConnectPtr dconn, + const char *dom_xml, + const char *dname, + const char *uri_in, + virStreamPtr st, + const char *cookiein, + int cookieinlen, + char **cookieout, + int *cookieoutlen, + char **uri_out, + unsigned long flags); int qemuMigrationPerform(virQEMUDriverPtr driver, virConnectPtr conn, -- 1.8.2.1

--- src/qemu/qemu_driver.c | 69 +++----------- src/qemu/qemu_migration.c | 225 ++++++++++++++++++++++++++++------------------ src/qemu/qemu_migration.h | 2 +- 3 files changed, 152 insertions(+), 144 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 80ec379..29210a5 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -10164,67 +10164,21 @@ qemuDomainMigrateFinish3Params(virConnectPtr dconn, static int -qemuDomainMigrateConfirm3Internal(virDomainPtr domain, - const char *cookiein, - int cookieinlen, - unsigned long flags, - int cancelled) -{ - virQEMUDriverPtr driver = domain->conn->privateData; - virDomainObjPtr vm; - int ret = -1; - enum qemuMigrationJobPhase phase; - virQEMUDriverConfigPtr cfg = NULL; - - if (!(vm = qemuDomObjFromDomain(domain))) - return -1; - - cfg = virQEMUDriverGetConfig(driver); - - if (!qemuMigrationJobIsActive(vm, QEMU_ASYNC_JOB_MIGRATION_OUT)) - goto cleanup; - - if (cancelled) - phase = QEMU_MIGRATION_PHASE_CONFIRM3_CANCELLED; - else - phase = QEMU_MIGRATION_PHASE_CONFIRM3; - - qemuMigrationJobStartPhase(driver, vm, phase); - virQEMUCloseCallbacksUnset(driver->closeCallbacks, vm, - qemuMigrationCleanup); - - ret = qemuMigrationConfirm(driver, domain->conn, vm, - cookiein, cookieinlen, - flags, cancelled); - - if (qemuMigrationJobFinish(driver, vm) == 0) { - vm = NULL; - } else if (!virDomainObjIsActive(vm) && - (!vm->persistent || (flags & VIR_MIGRATE_UNDEFINE_SOURCE))) { - if (flags & VIR_MIGRATE_UNDEFINE_SOURCE) - virDomainDeleteConfig(cfg->configDir, cfg->autostartDir, vm); - qemuDomainRemoveInactive(driver, vm); - vm = NULL; - } - -cleanup: - if (vm) - virObjectUnlock(vm); - virObjectUnref(cfg); - return ret; -} - -static int qemuDomainMigrateConfirm3(virDomainPtr domain, const char *cookiein, int cookieinlen, unsigned long flags, int cancelled) { + virDomainObjPtr vm; + virCheckFlags(QEMU_MIGRATION_FLAGS, -1); - return qemuDomainMigrateConfirm3Internal(domain, cookiein, cookieinlen, - flags, cancelled); + if (!(vm = qemuDomObjFromDomain(domain))) + return -1; + + return qemuMigrationConfirm(domain->conn->privateData, domain->conn, + vm, cookiein, cookieinlen, flags, cancelled); } static int @@ -10236,13 +10190,18 @@ qemuDomainMigrateConfirm3Params(virDomainPtr domain, unsigned int flags, int cancelled) { + virDomainObjPtr vm; + virCheckFlags(QEMU_MIGRATION_FLAGS, -1); if (virTypedParamsValidate(params, nparams, QEMU_MIGRATION_PARAMETERS) < 0) return -1; - return qemuDomainMigrateConfirm3Internal(domain, cookiein, cookieinlen, - flags, cancelled); + if (!(vm = qemuDomObjFromDomain(domain))) + return -1; + + return qemuMigrationConfirm(domain->conn->privateData, domain->conn, + vm, cookiein, cookieinlen, flags, cancelled); } diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 7559625..a63af70 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -2647,6 +2647,140 @@ cleanup: } +static int +qemuMigrationConfirmPhase(virQEMUDriverPtr driver, + virConnectPtr conn, + virDomainObjPtr vm, + const char *cookiein, + int cookieinlen, + unsigned int flags, + int retcode) +{ + qemuMigrationCookiePtr mig; + virDomainEventPtr event = NULL; + int rv = -1; + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + + VIR_DEBUG("driver=%p, conn=%p, vm=%p, cookiein=%s, cookieinlen=%d, " + "flags=%x, retcode=%d", + driver, conn, vm, NULLSTR(cookiein), cookieinlen, + flags, retcode); + + virCheckFlags(QEMU_MIGRATION_FLAGS, -1); + + qemuMigrationJobSetPhase(driver, vm, + retcode == 0 + ? QEMU_MIGRATION_PHASE_CONFIRM3 + : QEMU_MIGRATION_PHASE_CONFIRM3_CANCELLED); + + if (!(mig = qemuMigrationEatCookie(driver, vm, cookiein, cookieinlen, 0))) + goto cleanup; + + if (flags & VIR_MIGRATE_OFFLINE) + goto done; + + /* Did the migration go as planned? If yes, kill off the + * domain object, but if no, resume CPUs + */ + if (retcode == 0) { + /* If guest uses SPICE and supports seamless migration we have to hold + * up domain shutdown until SPICE server transfers its data */ + qemuMigrationWaitForSpice(driver, vm); + + qemuProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_MIGRATED, + VIR_QEMU_PROCESS_STOP_MIGRATED); + virDomainAuditStop(vm, "migrated"); + + event = virDomainEventNewFromObj(vm, + VIR_DOMAIN_EVENT_STOPPED, + VIR_DOMAIN_EVENT_STOPPED_MIGRATED); + } else { + + /* cancel any outstanding NBD jobs */ + qemuMigrationCancelDriveMirror(mig, driver, vm); + + /* run 'cont' on the destination, which allows migration on qemu + * >= 0.10.6 to work properly. This isn't strictly necessary on + * older qemu's, but it also doesn't hurt anything there + */ + if (qemuProcessStartCPUs(driver, vm, conn, + VIR_DOMAIN_RUNNING_MIGRATED, + QEMU_ASYNC_JOB_MIGRATION_OUT) < 0) { + if (virGetLastError() == NULL) + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("resume operation failed")); + goto cleanup; + } + + event = virDomainEventNewFromObj(vm, + VIR_DOMAIN_EVENT_RESUMED, + VIR_DOMAIN_EVENT_RESUMED_MIGRATED); + if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0) { + VIR_WARN("Failed to save status on vm %s", vm->def->name); + goto cleanup; + } + } + +done: + qemuMigrationCookieFree(mig); + rv = 0; + +cleanup: + if (event) + qemuDomainEventQueue(driver, event); + virObjectUnref(cfg); + return rv; +} + +int +qemuMigrationConfirm(virQEMUDriverPtr driver, + virConnectPtr conn, + virDomainObjPtr vm, + const char *cookiein, + int cookieinlen, + unsigned int flags, + int cancelled) +{ + int ret = -1; + enum qemuMigrationJobPhase phase; + virQEMUDriverConfigPtr cfg = NULL; + + cfg = virQEMUDriverGetConfig(driver); + + if (!qemuMigrationJobIsActive(vm, QEMU_ASYNC_JOB_MIGRATION_OUT)) + goto cleanup; + + if (cancelled) + phase = QEMU_MIGRATION_PHASE_CONFIRM3_CANCELLED; + else + phase = QEMU_MIGRATION_PHASE_CONFIRM3; + + qemuMigrationJobStartPhase(driver, vm, phase); + virQEMUCloseCallbacksUnset(driver->closeCallbacks, vm, + qemuMigrationCleanup); + + ret = qemuMigrationConfirmPhase(driver, conn, vm, + cookiein, cookieinlen, + flags, cancelled); + + if (qemuMigrationJobFinish(driver, vm) == 0) { + vm = NULL; + } else if (!virDomainObjIsActive(vm) && + (!vm->persistent || (flags & VIR_MIGRATE_UNDEFINE_SOURCE))) { + if (flags & VIR_MIGRATE_UNDEFINE_SOURCE) + virDomainDeleteConfig(cfg->configDir, cfg->autostartDir, vm); + qemuDomainRemoveInactive(driver, vm); + vm = NULL; + } + +cleanup: + if (vm) + virObjectUnlock(vm); + virObjectUnref(cfg); + return ret; +} + + enum qemuMigrationDestinationType { MIGRATION_DEST_HOST, MIGRATION_DEST_CONNECT_HOST, @@ -3680,9 +3814,9 @@ finish: cookieinlen = cookieoutlen; cookieout = NULL; cookieoutlen = 0; - ret = qemuMigrationConfirm(driver, sconn, vm, - cookiein, cookieinlen, - flags, cancelled); + ret = qemuMigrationConfirmPhase(driver, sconn, vm, + cookiein, cookieinlen, + flags, cancelled); /* If Confirm3 returns -1, there's nothing more we can * do, but fortunately worst case is that there is a * domain left in 'paused' state on source. @@ -4365,91 +4499,6 @@ cleanup: } -int qemuMigrationConfirm(virQEMUDriverPtr driver, - virConnectPtr conn, - virDomainObjPtr vm, - const char *cookiein, - int cookieinlen, - unsigned int flags, - int retcode) -{ - qemuMigrationCookiePtr mig; - virDomainEventPtr event = NULL; - int rv = -1; - virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); - - VIR_DEBUG("driver=%p, conn=%p, vm=%p, cookiein=%s, cookieinlen=%d, " - "flags=%x, retcode=%d", - driver, conn, vm, NULLSTR(cookiein), cookieinlen, - flags, retcode); - - virCheckFlags(QEMU_MIGRATION_FLAGS, -1); - - qemuMigrationJobSetPhase(driver, vm, - retcode == 0 - ? QEMU_MIGRATION_PHASE_CONFIRM3 - : QEMU_MIGRATION_PHASE_CONFIRM3_CANCELLED); - - if (!(mig = qemuMigrationEatCookie(driver, vm, cookiein, cookieinlen, 0))) - goto cleanup; - - if (flags & VIR_MIGRATE_OFFLINE) - goto done; - - /* Did the migration go as planned? If yes, kill off the - * domain object, but if no, resume CPUs - */ - if (retcode == 0) { - /* If guest uses SPICE and supports seamless migration we have to hold - * up domain shutdown until SPICE server transfers its data */ - qemuMigrationWaitForSpice(driver, vm); - - qemuProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_MIGRATED, - VIR_QEMU_PROCESS_STOP_MIGRATED); - virDomainAuditStop(vm, "migrated"); - - event = virDomainEventNewFromObj(vm, - VIR_DOMAIN_EVENT_STOPPED, - VIR_DOMAIN_EVENT_STOPPED_MIGRATED); - } else { - - /* cancel any outstanding NBD jobs */ - qemuMigrationCancelDriveMirror(mig, driver, vm); - - /* run 'cont' on the destination, which allows migration on qemu - * >= 0.10.6 to work properly. This isn't strictly necessary on - * older qemu's, but it also doesn't hurt anything there - */ - if (qemuProcessStartCPUs(driver, vm, conn, - VIR_DOMAIN_RUNNING_MIGRATED, - QEMU_ASYNC_JOB_MIGRATION_OUT) < 0) { - if (virGetLastError() == NULL) - virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("resume operation failed")); - goto cleanup; - } - - event = virDomainEventNewFromObj(vm, - VIR_DOMAIN_EVENT_RESUMED, - VIR_DOMAIN_EVENT_RESUMED_MIGRATED); - if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0) { - VIR_WARN("Failed to save status on vm %s", vm->def->name); - goto cleanup; - } - } - -done: - qemuMigrationCookieFree(mig); - rv = 0; - -cleanup: - if (event) - qemuDomainEventQueue(driver, event); - virObjectUnref(cfg); - return rv; -} - - /* Helper function called while vm is active. */ int qemuMigrationToFile(virQEMUDriverPtr driver, virDomainObjPtr vm, diff --git a/src/qemu/qemu_migration.h b/src/qemu/qemu_migration.h index 2e03b78..65663e9 100644 --- a/src/qemu/qemu_migration.h +++ b/src/qemu/qemu_migration.h @@ -146,7 +146,7 @@ int qemuMigrationConfirm(virQEMUDriverPtr driver, const char *cookiein, int cookieinlen, unsigned int flags, - int retcode); + int cancelled); bool qemuMigrationIsAllowed(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainDefPtr def, bool remote, -- 1.8.2.1

--- src/libvirt.c | 177 +++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 133 insertions(+), 44 deletions(-) diff --git a/src/libvirt.c b/src/libvirt.c index d6ded5e..12387ef 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -4776,15 +4776,22 @@ finish: * Src: Confirm * - Kill off VM if success, resume if failed * + * If useParams is true, params and nparams contain migration parameters and + * we know it's safe to call the API which supports extensible parameters. + * Otherwise, we have to use xmlin, dname, uri, and bandwidth and pass them + * to the old-style APIs. */ static virDomainPtr -virDomainMigrateVersion3(virDomainPtr domain, - virConnectPtr dconn, - const char *xmlin, - unsigned long flags, - const char *dname, - const char *uri, - unsigned long bandwidth) +virDomainMigrateVersion3Full(virDomainPtr domain, + virConnectPtr dconn, + const char *xmlin, + const char *dname, + const char *uri, + unsigned long long bandwidth, + virTypedParameterPtr params, + int nparams, + bool useParams, + unsigned int flags) { virDomainPtr ddomain = NULL; char *uri_out = NULL; @@ -4800,47 +4807,82 @@ virDomainMigrateVersion3(virDomainPtr domain, unsigned long protection = 0; bool notify_source = true; unsigned int destflags = flags & ~VIR_MIGRATE_ABORT_ON_ERROR; + int state; + virTypedParameterPtr tmp; - VIR_DOMAIN_DEBUG(domain, "dconn=%p xmlin=%s, flags=%lx, " - "dname=%s, uri=%s, bandwidth=%lu", - dconn, NULLSTR(xmlin), flags, - NULLSTR(dname), NULLSTR(uri), bandwidth); + VIR_DOMAIN_DEBUG(domain, + "dconn=%p, xmlin=%s, dname=%s, uri=%s, bandwidth=%llu, " + "params=%p, nparams=%d, useParams=%d, flags=%x", + dconn, NULLSTR(xmlin), NULLSTR(dname), NULLSTR(uri), + bandwidth, params, nparams, useParams, flags); + VIR_TYPED_PARAMS_DEBUG(params, nparams); - if (!domain->conn->driver->domainMigrateBegin3 || - !domain->conn->driver->domainMigratePerform3 || - !domain->conn->driver->domainMigrateConfirm3 || - !dconn->driver->domainMigratePrepare3 || - !dconn->driver->domainMigrateFinish3) { + if ((!useParams && + (!domain->conn->driver->domainMigrateBegin3 || + !domain->conn->driver->domainMigratePerform3 || + !domain->conn->driver->domainMigrateConfirm3 || + !dconn->driver->domainMigratePrepare3 || + !dconn->driver->domainMigrateFinish3)) || + (useParams && + (!domain->conn->driver->domainMigrateBegin3Params || + !domain->conn->driver->domainMigratePerform3Params || + !domain->conn->driver->domainMigrateConfirm3Params || + !dconn->driver->domainMigratePrepare3Params || + !dconn->driver->domainMigrateFinish3Params))) { virLibConnError(VIR_ERR_INTERNAL_ERROR, __FUNCTION__); - virDispatchError(domain->conn); return NULL; } + if (virTypedParamsCopy(&tmp, params, nparams) < 0) + return NULL; + params = tmp; + if (VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn, VIR_DRV_FEATURE_MIGRATE_CHANGE_PROTECTION)) protection = VIR_MIGRATE_CHANGE_PROTECTION; VIR_DEBUG("Begin3 %p", domain->conn); - dom_xml = domain->conn->driver->domainMigrateBegin3 - (domain, xmlin, &cookieout, &cookieoutlen, - flags | protection, dname, bandwidth); + if (useParams) { + dom_xml = domain->conn->driver->domainMigrateBegin3Params + (domain, params, nparams, &cookieout, &cookieoutlen, + flags | protection); + } else { + dom_xml = domain->conn->driver->domainMigrateBegin3 + (domain, xmlin, &cookieout, &cookieoutlen, + flags | protection, dname, bandwidth); + } if (!dom_xml) goto done; - ret = virDomainGetInfo(domain, &info); - if (ret == 0 && info.state == VIR_DOMAIN_PAUSED) { - flags |= VIR_MIGRATE_PAUSED; + if (useParams) { + /* If source is new enough to support extensible migration parameters, + * it's certainly new enough to support virDomainGetState. */ + ret = virDomainGetState(domain, &state, NULL, 0); + } else { + ret = virDomainGetInfo(domain, &info); + state = info.state; } + if (ret == 0 && state == VIR_DOMAIN_PAUSED) + flags |= VIR_MIGRATE_PAUSED; - VIR_DEBUG("Prepare3 %p flags=%lx", dconn, flags); + VIR_DEBUG("Prepare3 %p flags=%x", dconn, flags); cookiein = cookieout; cookieinlen = cookieoutlen; cookieout = NULL; cookieoutlen = 0; - ret = dconn->driver->domainMigratePrepare3 - (dconn, cookiein, cookieinlen, &cookieout, &cookieoutlen, - uri, &uri_out, destflags, dname, bandwidth, dom_xml); - VIR_FREE(dom_xml); + if (useParams) { + if (virTypedParamsReplaceString(¶ms, &nparams, + VIR_MIGRATE_PARAM_DEST_XML, + dom_xml) < 0) + goto done; + ret = dconn->driver->domainMigratePrepare3Params + (dconn, params, nparams, cookiein, cookieinlen, + &cookieout, &cookieoutlen, &uri_out, destflags); + } else { + ret = dconn->driver->domainMigratePrepare3 + (dconn, cookiein, cookieinlen, &cookieout, &cookieoutlen, + uri, &uri_out, destflags, dname, bandwidth, dom_xml); + } if (ret == -1) { if (protection) { /* Begin already started a migration job so we need to cancel it by @@ -4853,14 +4895,20 @@ virDomainMigrateVersion3(virDomainPtr domain, } } - if (uri == NULL && uri_out == NULL) { + /* Did domainMigratePrepare3 change URI? */ + if (uri_out) { + uri = uri_out; + if (useParams && + virTypedParamsReplaceString(¶ms, &nparams, + VIR_MIGRATE_PARAM_URI, + uri_out) < 0) + goto finish; + } else if (!uri && + virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_URI, &uri) <= 0) { virLibConnError(VIR_ERR_INTERNAL_ERROR, "%s", _("domainMigratePrepare3 did not set uri")); - virDispatchError(domain->conn); - goto finish; } - if (uri_out) - uri = uri_out; /* Did domainMigratePrepare3 change URI? */ if (flags & VIR_MIGRATE_OFFLINE) { VIR_DEBUG("Offline migration, skipping Perform phase"); @@ -4882,10 +4930,16 @@ virDomainMigrateVersion3(virDomainPtr domain, cookieout = NULL; cookieoutlen = 0; /* dconnuri not relevant in non-P2P modes, so left NULL here */ - ret = domain->conn->driver->domainMigratePerform3 - (domain, NULL, cookiein, cookieinlen, - &cookieout, &cookieoutlen, NULL, - uri, flags | protection, dname, bandwidth); + if (useParams) { + ret = domain->conn->driver->domainMigratePerform3Params + (domain, NULL, params, nparams, cookiein, cookieinlen, + &cookieout, &cookieoutlen, flags | protection); + } else { + ret = domain->conn->driver->domainMigratePerform3 + (domain, NULL, cookiein, cookieinlen, + &cookieout, &cookieoutlen, NULL, + uri, flags | protection, dname, bandwidth); + } /* Perform failed. Make sure Finish doesn't overwrite the error */ if (ret < 0) { @@ -4914,10 +4968,24 @@ finish: cookieinlen = cookieoutlen; cookieout = NULL; cookieoutlen = 0; - dname = dname ? dname : domain->name; - ddomain = dconn->driver->domainMigrateFinish3 - (dconn, dname, cookiein, cookieinlen, &cookieout, &cookieoutlen, - NULL, uri, destflags, cancelled); + if (useParams) { + if (virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_DEST_NAME, NULL) <= 0 && + virTypedParamsReplaceString(¶ms, &nparams, + VIR_MIGRATE_PARAM_DEST_NAME, + domain->name) < 0) { + domain = NULL; + } else { + ddomain = dconn->driver->domainMigrateFinish3Params + (dconn, params, nparams, cookiein, cookieinlen, + &cookieout, &cookieoutlen, destflags, cancelled); + } + } else { + dname = dname ? dname : domain->name; + ddomain = dconn->driver->domainMigrateFinish3 + (dconn, dname, cookiein, cookieinlen, &cookieout, &cookieoutlen, + NULL, uri, destflags, cancelled); + } /* If ddomain is NULL, then we were unable to start * the guest on the target, and must restart on the @@ -4948,9 +5016,15 @@ confirm: cookieinlen = cookieoutlen; cookieout = NULL; cookieoutlen = 0; - ret = domain->conn->driver->domainMigrateConfirm3 - (domain, cookiein, cookieinlen, - flags | protection, cancelled); + if (useParams) { + ret = domain->conn->driver->domainMigrateConfirm3Params + (domain, params, nparams, cookiein, cookieinlen, + flags | protection, cancelled); + } else { + ret = domain->conn->driver->domainMigrateConfirm3 + (domain, cookiein, cookieinlen, + flags | protection, cancelled); + } /* If Confirm3 returns -1, there's nothing more we can * do, but fortunately worst case is that there is a * domain left in 'paused' state on source. @@ -4966,12 +5040,27 @@ confirm: virSetError(orig_err); virFreeError(orig_err); } + VIR_FREE(dom_xml); VIR_FREE(uri_out); VIR_FREE(cookiein); VIR_FREE(cookieout); + virTypedParamsFree(params, nparams); return ddomain; } +static virDomainPtr +virDomainMigrateVersion3(virDomainPtr domain, + virConnectPtr dconn, + const char *xmlin, + unsigned long flags, + const char *dname, + const char *uri, + unsigned long bandwidth) +{ + return virDomainMigrateVersion3Full(domain, dconn, xmlin, dname, uri, + bandwidth, NULL, 0, false, flags); +} + /* * In normal migration, the libvirt client co-ordinates communication -- 1.8.2.1

On 06/18/2013 10:05 AM, Jiri Denemark wrote:
--- src/libvirt.c | 177 +++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 133 insertions(+), 44 deletions(-)
<...snip...>
+ if (useParams) { + if (virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_DEST_NAME, NULL) <= 0 && + virTypedParamsReplaceString(¶ms, &nparams, + VIR_MIGRATE_PARAM_DEST_NAME, + domain->name) < 0) { + domain = NULL;
^^^ The setting of NULL right here causes Coverity problems later... 4979 virTypedParamsReplaceString(¶ms, &nparams, 4980 VIR_MIGRATE_PARAM_DEST_NAME, 4981 domain->name) < 0) { (45) Event assign_zero: Assigning: "domain" = "NULL". Also see events: [var_deref_op] 4982 domain = NULL;
+ } else { + ddomain = dconn->driver->domainMigrateFinish3Params + (dconn, params, nparams, cookiein, cookieinlen, + &cookieout, &cookieoutlen, destflags, cancelled); + } + } else { + dname = dname ? dname : domain->name; + ddomain = dconn->driver->domainMigrateFinish3 + (dconn, dname, cookiein, cookieinlen, &cookieout, &cookieoutlen, + NULL, uri, destflags, cancelled); + }
/* If ddomain is NULL, then we were unable to start * the guest on the target, and must restart on the
Later domain->conn is referenced: (52) Event cond_true: Condition "notify_source", taking true branch 5017 if (notify_source) { (53) Event var_deref_op: Dereferencing null pointer "domain". Also see events: [assign_zero] 5018 VIR_DEBUG("Confirm3 %p ret=%d domain=%p", domain->conn, ret, domain); 5019 VIR_FREE(cookiein); 5020 cookiein = cookieout; John

On Tue, Jun 25, 2013 at 06:56:35 -0400, John Ferlan wrote:
On 06/18/2013 10:05 AM, Jiri Denemark wrote:
--- src/libvirt.c | 177 +++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 133 insertions(+), 44 deletions(-)
<...snip...>
+ if (useParams) { + if (virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_DEST_NAME, NULL) <= 0 && + virTypedParamsReplaceString(¶ms, &nparams, + VIR_MIGRATE_PARAM_DEST_NAME, + domain->name) < 0) { + domain = NULL;
^^^ The setting of NULL right here causes Coverity problems later...
Oops, that's a nasty typo. Fixed by the following patch which I pushed as trivial: diff --git a/src/libvirt.c b/src/libvirt.c index 2dc96a9..bc1694a 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -4979,7 +4979,7 @@ finish: virTypedParamsReplaceString(¶ms, &nparams, VIR_MIGRATE_PARAM_DEST_NAME, domain->name) < 0) { - domain = NULL; + ddomain = NULL; } else { ddomain = dconn->driver->domainMigrateFinish3Params (dconn, params, nparams, cookiein, cookieinlen,

--- src/libvirt.c | 108 ++++++++++++++++++++++++++++++---------------------------- 1 file changed, 56 insertions(+), 52 deletions(-) diff --git a/src/libvirt.c b/src/libvirt.c index 12387ef..7c60600 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -5070,73 +5070,69 @@ virDomainMigrateVersion3(virDomainPtr domain, * only talks to the source libvirtd instance. The source libvirtd * then opens its own connection to the destination and co-ordinates * migration itself. + * + * If useParams is true, params and nparams contain migration parameters and + * we know it's safe to call the API which supports extensible parameters. + * Otherwise, we have to use xmlin, dname, uri, and bandwidth and pass them + * to the old-style APIs. */ static int -virDomainMigratePeer2Peer(virDomainPtr domain, - const char *xmlin, - unsigned long flags, - const char *dname, - const char *dconnuri, - const char *uri, - unsigned long bandwidth) +virDomainMigratePeer2PeerFull(virDomainPtr domain, + const char *dconnuri, + const char *xmlin, + const char *dname, + const char *uri, + unsigned long long bandwidth, + virTypedParameterPtr params, + int nparams, + bool useParams, + unsigned int flags) { virURIPtr tempuri = NULL; - VIR_DOMAIN_DEBUG(domain, "xmlin=%s, flags=%lx, dname=%s, " - "dconnuri=%s, uri=%s, bandwidth=%lu", - NULLSTR(xmlin), flags, NULLSTR(dname), - NULLSTR(dconnuri), NULLSTR(uri), bandwidth); - if (!domain->conn->driver->domainMigratePerform) { - virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); - virDispatchError(domain->conn); - return -1; - } + VIR_DOMAIN_DEBUG(domain, + "dconnuri=%s, xmlin=%s, dname=%s, uri=%s, bandwidth=%llu " + "params=%p, nparams=%d, useParams=%d, flags=%x", + dconnuri, NULLSTR(xmlin), NULLSTR(dname), NULLSTR(uri), + bandwidth, params, nparams, useParams, flags); + VIR_TYPED_PARAMS_DEBUG(params, nparams); - if (!(tempuri = virURIParse(dconnuri))) { - virDispatchError(domain->conn); + if ((useParams && !domain->conn->driver->domainMigratePerform3Params) || + (!useParams && + !domain->conn->driver->domainMigratePerform && + !domain->conn->driver->domainMigratePerform3)) { + virLibConnError(VIR_ERR_INTERNAL_ERROR, __FUNCTION__); return -1; } - if (!tempuri->server) { - virReportInvalidArg(dconnuri, - _("unable to parse server from dconnuri in %s"), - __FUNCTION__); - virDispatchError(domain->conn); - virURIFree(tempuri); + if (!(tempuri = virURIParse(dconnuri))) return -1; - } - if (STRPREFIX(tempuri->server, "localhost")) { + if (!tempuri->server || STRPREFIX(tempuri->server, "localhost")) { virReportInvalidArg(dconnuri, _("unable to parse server from dconnuri in %s"), __FUNCTION__); - virDispatchError(domain->conn); virURIFree(tempuri); return -1; } virURIFree(tempuri); - /* Perform the migration. The driver isn't supposed to return - * until the migration is complete. - */ - if (VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn, - VIR_DRV_FEATURE_MIGRATION_V3)) { + if (useParams) { + VIR_DEBUG("Using migration protocol 3 with extensible parameters"); + return domain->conn->driver->domainMigratePerform3Params + (domain, dconnuri, params, nparams, + NULL, 0, NULL, NULL, flags); + } else if (VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn, + VIR_DRV_FEATURE_MIGRATION_V3)) { VIR_DEBUG("Using migration protocol 3"); - return domain->conn->driver->domainMigratePerform3(domain, - xmlin, - NULL, /* cookiein */ - 0, /* cookieinlen */ - NULL, /* cookieoutlen */ - NULL, /* cookieoutlen */ - dconnuri, - uri, - flags, - dname, - bandwidth); + return domain->conn->driver->domainMigratePerform3 + (domain, xmlin, NULL, 0, NULL, NULL, dconnuri, + uri, flags, dname, bandwidth); } else { VIR_DEBUG("Using migration protocol 2"); if (xmlin) { - virLibConnError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Unable to change target guest XML during migration")); + virLibConnError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", + _("Unable to change target guest XML " + "during migration")); return -1; } if (uri) { @@ -5144,16 +5140,24 @@ virDomainMigratePeer2Peer(virDomainPtr domain, _("Unable to override peer2peer migration URI")); return -1; } - return domain->conn->driver->domainMigratePerform(domain, - NULL, /* cookie */ - 0, /* cookielen */ - dconnuri, - flags, - dname, - bandwidth); + return domain->conn->driver->domainMigratePerform + (domain, NULL, 0, dconnuri, flags, dname, bandwidth); } } +static int +virDomainMigratePeer2Peer(virDomainPtr domain, + const char *xmlin, + unsigned long flags, + const char *dname, + const char *dconnuri, + const char *uri, + unsigned long bandwidth) +{ + return virDomainMigratePeer2PeerFull(domain, dconnuri, xmlin, dname, uri, + bandwidth, NULL, 0, false, flags); +} + /* * In normal migration, the libvirt client co-ordinates communication -- 1.8.2.1

On 18.06.2013 16:05, Jiri Denemark wrote:
--- src/libvirt.c | 108 ++++++++++++++++++++++++++++++---------------------------- 1 file changed, 56 insertions(+), 52 deletions(-)
This is the last patch I reviewed for today. So far I've not spotted anything obviously wrong. So hold your fingers crossed :) Michal

This patch introduces two new APIs virDomainMigrate3 and virDomainMigrateToURI3 that may be used in place of their older variants. These new APIs take optional migration parameters (such as bandwidth, domain XML, ...) in an array of virTypedParameters, which makes adding new parameters easier as there's no need to introduce new APIs whenever a new migration parameter needs to be added. Both APIs are backward compatible and will automatically use older migration calls in case the new calls are not supported as long as the typed parameters array does not contain any parameter which was not supported by the older calls. --- include/libvirt/libvirt.h.in | 10 ++ python/generator.py | 2 + src/libvirt.c | 376 +++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 6 + 4 files changed, 394 insertions(+) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 35bffea..5f3a006 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1255,6 +1255,11 @@ virDomainPtr virDomainMigrate2(virDomainPtr domain, virConnectPtr dconn, const char *dxml, unsigned long flags, const char *dname, const char *uri, unsigned long bandwidth); +virDomainPtr virDomainMigrate3(virDomainPtr domain, + virConnectPtr dconn, + virTypedParameterPtr params, + unsigned int nparams, + unsigned int flags); int virDomainMigrateToURI (virDomainPtr domain, const char *duri, unsigned long flags, const char *dname, @@ -1267,6 +1272,11 @@ int virDomainMigrateToURI2(virDomainPtr domain, unsigned long flags, const char *dname, unsigned long bandwidth); +int virDomainMigrateToURI3(virDomainPtr domain, + const char *dconnuri, + virTypedParameterPtr params, + unsigned int nparams, + unsigned int flags); int virDomainMigrateSetMaxDowntime (virDomainPtr domain, unsigned long long downtime, diff --git a/python/generator.py b/python/generator.py index 8c380bb..da642eb 100755 --- a/python/generator.py +++ b/python/generator.py @@ -458,6 +458,8 @@ skip_impl = ( 'virNodeGetMemoryParameters', 'virNodeSetMemoryParameters', 'virNodeGetCPUMap', + 'virDomainMigrate3', + 'virDomainMigrateToURI3', ) lxc_skip_impl = ( diff --git a/src/libvirt.c b/src/libvirt.c index 7c60600..fdb9fa1 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -5061,6 +5061,17 @@ virDomainMigrateVersion3(virDomainPtr domain, bandwidth, NULL, 0, false, flags); } +static virDomainPtr +virDomainMigrateVersion3Params(virDomainPtr domain, + virConnectPtr dconn, + virTypedParameterPtr params, + int nparams, + unsigned int flags) +{ + return virDomainMigrateVersion3Full(domain, dconn, NULL, NULL, NULL, 0, + params, nparams, true, flags); +} + /* * In normal migration, the libvirt client co-ordinates communication @@ -5158,6 +5169,17 @@ virDomainMigratePeer2Peer(virDomainPtr domain, bandwidth, NULL, 0, false, flags); } +static int +virDomainMigratePeer2PeerParams(virDomainPtr domain, + const char *dconnuri, + virTypedParameterPtr params, + int nparams, + unsigned int flags) +{ + return virDomainMigratePeer2PeerFull(domain, dconnuri, NULL, NULL, NULL, 0, + params, nparams, true, flags); +} + /* * In normal migration, the libvirt client co-ordinates communication @@ -5700,6 +5722,208 @@ error: /** + * virDomainMigrate3: + * @domain: a domain object + * @dconn: destination host (a connection object) + * @params: (optional) migration parameters + * @nparams: (optional) number of migration parameters in @params + * @flags: bitwise-OR of virDomainMigrateFlags + * + * Migrate the domain object from its current host to the destination host + * given by dconn (a connection to the destination host). + * + * See virDomainMigrateFlags documentation for description of individual flags. + * + * VIR_MIGRATE_TUNNELLED and VIR_MIGRATE_PEER2PEER are not supported by this + * API, use virDomainMigrateToURI3 instead. + * + * If you want to copy non-shared storage within migration you + * can use either VIR_MIGRATE_NON_SHARED_DISK or + * VIR_MIGRATE_NON_SHARED_INC as they are mutually exclusive. + * + * 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 +virDomainMigrate3(virDomainPtr domain, + virConnectPtr dconn, + virTypedParameterPtr params, + unsigned int nparams, + unsigned int flags) +{ + virDomainPtr ddomain = NULL; + const char *compatParams[] = { VIR_MIGRATE_PARAM_URI, + VIR_MIGRATE_PARAM_DEST_NAME, + VIR_MIGRATE_PARAM_DEST_XML, + VIR_MIGRATE_PARAM_BANDWIDTH }; + const char *uri = NULL; + const char *dname = NULL; + const char *dxml = NULL; + unsigned long long bandwidth = 0; + + VIR_DOMAIN_DEBUG(domain, "dconn=%p, params=%p, nparms=%u flags=%x", + dconn, params, nparams, flags); + VIR_TYPED_PARAMS_DEBUG(params, nparams); + + virResetLastError(); + + /* First checkout the source */ + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return NULL; + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + /* Now checkout the destination */ + if (!VIR_IS_CONNECT(dconn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + goto error; + } + if (dconn->flags & VIR_CONNECT_RO) { + /* NB, deliberately report error against source object, not dest */ + virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (flags & VIR_MIGRATE_NON_SHARED_DISK && + flags & VIR_MIGRATE_NON_SHARED_INC) { + virReportInvalidArg(flags, + _("flags 'shared disk' and 'shared incremental' " + "in %s are mutually exclusive"), + __FUNCTION__); + goto error; + } + if (flags & (VIR_MIGRATE_PEER2PEER | VIR_MIGRATE_TUNNELLED)) { + virReportInvalidArg(flags, "%s", + _("use virDomainMigrateToURI3 for peer-to-peer " + "migration")); + goto error; + } + + if (flags & VIR_MIGRATE_OFFLINE) { + if (!VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn, + VIR_DRV_FEATURE_MIGRATION_OFFLINE)) { + virLibConnError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", + _("offline migration is not supported by " + "the source host")); + goto error; + } + if (!VIR_DRV_SUPPORTS_FEATURE(dconn->driver, dconn, + VIR_DRV_FEATURE_MIGRATION_OFFLINE)) { + virLibConnError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", + _("offline migration is not supported by " + "the destination host")); + goto error; + } + } + + /* Change protection requires support only on source side, and + * is only needed in v3 migration, which automatically re-adds + * the flag for just the source side. We mask it out to allow + * migration from newer source to an older destination that + * rejects the flag. */ + if (flags & VIR_MIGRATE_CHANGE_PROTECTION && + !VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn, + VIR_DRV_FEATURE_MIGRATE_CHANGE_PROTECTION)) { + virLibConnError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", + _("cannot enforce change protection")); + goto error; + } + flags &= ~VIR_MIGRATE_CHANGE_PROTECTION; + + /* Prefer extensible API but fall back to older migration APIs if params + * only contains parameters which were supported by the older API. */ + if (VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn, + VIR_DRV_FEATURE_MIGRATION_PARAMS) && + VIR_DRV_SUPPORTS_FEATURE(dconn->driver, dconn, + VIR_DRV_FEATURE_MIGRATION_PARAMS)) { + VIR_DEBUG("Using migration protocol 3 with extensible parameters"); + ddomain = virDomainMigrateVersion3Params(domain, dconn, params, + nparams, flags); + goto done; + } + + if (!virTypedParamsCheck(params, nparams, compatParams, + ARRAY_CARDINALITY(compatParams))) { + virLibConnError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", + _("Migration APIs with extensible parameters are not " + "supported but extended parameters were passed")); + goto error; + } + + if (virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_URI, &uri) < 0 || + virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_DEST_NAME, &dname) < 0 || + virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_DEST_XML, &dxml) < 0 || + virTypedParamsGetULLong(params, nparams, + VIR_MIGRATE_PARAM_BANDWIDTH, &bandwidth) < 0) { + goto error; + } + + if (VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn, + VIR_DRV_FEATURE_MIGRATION_V3) && + VIR_DRV_SUPPORTS_FEATURE(dconn->driver, dconn, + VIR_DRV_FEATURE_MIGRATION_V3)) { + VIR_DEBUG("Using migration protocol 3"); + ddomain = virDomainMigrateVersion3(domain, dconn, dxml, 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)) { + VIR_DEBUG("Using migration protocol 2"); + if (dxml) { + virLibConnError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", + _("Unable to change target guest XML during " + "migration")); + goto error; + } + ddomain = virDomainMigrateVersion2(domain, dconn, flags, + dname, uri, bandwidth); + } else 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)) { + VIR_DEBUG("Using migration protocol 1"); + if (dxml) { + virLibConnError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", + _("Unable to change target guest XML during " + "migration")); + goto error; + } + ddomain = virDomainMigrateVersion1(domain, dconn, flags, + dname, uri, bandwidth); + } else { + /* This driver does not support any migration method */ + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + +done: + if (ddomain == NULL) + goto error; + + return ddomain; + +error: + virDispatchError(domain->conn); + return NULL; +} + + +/** * virDomainMigrateToURI: * @domain: a domain object * @duri: mandatory URI for the destination host @@ -6004,6 +6228,158 @@ error: } +/** + * virDomainMigrateToURI3: + * @domain: a domain object + * @dconnuri: (optional) URI for target libvirtd if @flags includes VIR_MIGRATE_PEER2PEER + * @params: (optional) migration parameters + * @nparams: (optional) number of migration parameters in @params + * @flags: bitwise-OR of virDomainMigrateFlags + * + * Migrate the domain object from its current host to the destination host + * given by URI. + * + * See virDomainMigrateFlags documentation for description of individual flags. + * + * The operation of this API hinges on the VIR_MIGRATE_PEER2PEER flag. + * + * If the VIR_MIGRATE_PEER2PEER flag is set, the @dconnuri parameter must be a + * valid libvirt connection URI, by which the source libvirt daemon can connect + * to the destination libvirt. + * + * If the VIR_MIGRATE_PEER2PEER flag is NOT set, then @dconnuri must be NULL + * and VIR_MIGRATE_PARAM_URI migration parameter must be filled in with + * hypervisor specific URI used to initiate the migration. This is called + * "direct" migration. + * + * VIR_MIGRATE_TUNNELLED requires that VIR_MIGRATE_PEER2PEER be set. + * + * If you want to copy non-shared storage within migration you + * can use either VIR_MIGRATE_NON_SHARED_DISK or + * VIR_MIGRATE_NON_SHARED_INC as they are mutually exclusive. + * + * There are many limitations on migration imposed by the underlying + * technology - for example it may not be possible to migrate between + * different processors even with the same architecture, or between + * different types of hypervisor. + * + * Returns 0 if the migration succeeded, -1 upon error. + */ +int +virDomainMigrateToURI3(virDomainPtr domain, + const char *dconnuri, + virTypedParameterPtr params, + unsigned int nparams, + unsigned int flags) +{ + bool compat; + const char *compatParams[] = { VIR_MIGRATE_PARAM_URI, + VIR_MIGRATE_PARAM_DEST_NAME, + VIR_MIGRATE_PARAM_DEST_XML, + VIR_MIGRATE_PARAM_BANDWIDTH }; + const char *uri = NULL; + const char *dname = NULL; + const char *dxml = NULL; + unsigned long long bandwidth = 0; + + VIR_DOMAIN_DEBUG(domain, "dconnuri=%s, params=%p, nparms=%u flags=%x", + NULLSTR(dconnuri), params, nparams, flags); + VIR_TYPED_PARAMS_DEBUG(params, nparams); + + virResetLastError(); + + /* First checkout the source */ + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (flags & VIR_MIGRATE_NON_SHARED_DISK && + flags & VIR_MIGRATE_NON_SHARED_INC) { + virReportInvalidArg(flags, + _("flags 'shared disk' and 'shared incremental' " + "in %s are mutually exclusive"), + __FUNCTION__); + goto error; + } + + compat = virTypedParamsCheck(params, nparams, compatParams, + ARRAY_CARDINALITY(compatParams)); + + if (virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_URI, &uri) < 0 || + virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_DEST_NAME, &dname) < 0 || + virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_DEST_XML, &dxml) < 0 || + virTypedParamsGetULLong(params, nparams, + VIR_MIGRATE_PARAM_BANDWIDTH, &bandwidth) < 0) { + goto error; + } + + if (flags & VIR_MIGRATE_PEER2PEER) { + if (!VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn, + VIR_DRV_FEATURE_MIGRATION_P2P)) { + virLibConnError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("Peer-to-peer migration is not supported by " + "the connection driver")); + goto error; + } + + if (VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn, + VIR_DRV_FEATURE_MIGRATION_PARAMS)) { + VIR_DEBUG("Using peer2peer migration with extensible parameters"); + if (virDomainMigratePeer2PeerParams(domain, dconnuri, params, + nparams, flags) < 0) + goto error; + } else if (compat) { + VIR_DEBUG("Using peer2peer migration"); + if (virDomainMigratePeer2Peer(domain, dxml, flags, dname, + dconnuri, uri, bandwidth) < 0) + goto error; + } else { + virLibConnError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", + _("Peer-to-peer migration with extensible " + "parameters is not supported but extended " + "parameters were passed")); + goto error; + } + } else { + if (!VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn, + VIR_DRV_FEATURE_MIGRATION_DIRECT)) { + /* Cannot do a migration with only the perform step */ + virLibConnError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("Direct migration is not supported by the" + " connection driver")); + goto error; + } + + if (!compat) { + virLibConnError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", + _("Direct migration does not support extensible " + "parameters")); + goto error; + } + + VIR_DEBUG("Using direct migration"); + if (virDomainMigrateDirect(domain, dxml, flags, + dname, uri, bandwidth) < 0) + goto error; + } + + return 0; + +error: + virDispatchError(domain->conn); + return -1; +} + + /* * Not for public use. This function is part of the internal * implementation of migration in the remote case. diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 4ee2d27..d47218a 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -621,4 +621,10 @@ LIBVIRT_1.0.6 { virGetLastErrorMessage; } LIBVIRT_1.0.5; +LIBVIRT_1.0.7 { + global: + virDomainMigrate3; + virDomainMigrateToURI3; +} LIBVIRT_1.0.6; + # .... define new API here using predicted next version number .... -- 1.8.2.1

On Tue, Jun 18, 2013 at 04:05:59PM +0200, Jiri Denemark wrote:
This patch introduces two new APIs virDomainMigrate3 and virDomainMigrateToURI3 that may be used in place of their older variants. These new APIs take optional migration parameters (such as bandwidth, domain XML, ...) in an array of virTypedParameters, which makes adding new parameters easier as there's no need to introduce new APIs whenever a new migration parameter needs to be added. Both APIs are backward compatible and will automatically use older migration calls in case the new calls are not supported as long as the typed parameters array does not contain any parameter which was not supported by the older calls. --- include/libvirt/libvirt.h.in | 10 ++ python/generator.py | 2 + src/libvirt.c | 376 +++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 6 + 4 files changed, 394 insertions(+)
ACK, Lets hope this really is the last variant we need to add for migration :-) Arguably we could merge the Migrate3 and MigrateToURI3 variants into one, but that'd complicate life more than is desired, so leave it as is with them split. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

The patch implements wrappers for virDomainMigrate3 and virDomainMigrateToURI3. --- python/libvirt-override-api.xml | 18 ++++ python/libvirt-override.c | 218 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 236 insertions(+) diff --git a/python/libvirt-override-api.xml b/python/libvirt-override-api.xml index 155ab36..9a88215 100644 --- a/python/libvirt-override-api.xml +++ b/python/libvirt-override-api.xml @@ -527,6 +527,24 @@ <arg name='flags' type='unsigned int' info='flags, currently unused, pass 0.'/> <return type='unsigned long' info='current max migration speed, or None in case of error'/> </function> + <function name='virDomainMigrate3' file='python'> + <info>Migrate the domain object from its current host to the destination host + given by dconn (a connection to the destination host).</info> + <arg name='domain' type='virDomainPtr' info='a domain object'/> + <arg name='dconn' type='virConnectPtr' info='pointer to the destination host connection'/> + <arg name='params' type='char *' info='dictionary with migration parameters'/> + <arg name='flags' type='unsigned int' info='an OR'ed set of virDomainMigrateFlags'/> + <return type='virDomainPtr' info='a new domain object or NULL in case of failure'/> + </function> + <function name='virDomainMigrateToURI3' file='python'> + <info>Migrate the domain object from its current host to the destination host + given by URI.</info> + <arg name='domain' type='virDomainPtr' info='a domain object'/> + <arg name='dconnuri' type='virConnectPtr' info='URI for target libvirtd if @flags includes VIR_MIGRATE_PEER2PEER'/> + <arg name='params' type='char *' info='dictionary with migration parameters'/> + <arg name='flags' type='unsigned int' info='an OR'ed set of virDomainMigrateFlags'/> + <return type='int' info='0 in case of success, -1 in case of failure.'/> + </function> <function name='virDomainSetBlockIoTune' file='python'> <info>Change the I/O tunables for a block device</info> <arg name='dom' type='virDomainPtr' info='pointer to the domain'/> diff --git a/python/libvirt-override.c b/python/libvirt-override.c index b6462c2..5c5586d 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -258,6 +258,160 @@ cleanup: return NULL; } + +typedef struct { + const char *name; + int type; +} virPyTypedParamsHint; +typedef virPyTypedParamsHint *virPyTypedParamsHintPtr; + +/* Automatically convert dict into type parameters based on types reported + * by python. All integer types are converted into LLONG (in case of a negative + * value) or ULLONG (in case of a positive value). If you need different + * handling, use @hints to explicitly specify what types should be used for + * specific parameters. + */ +static int +ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3) +virPyDictToTypedParams(PyObject *dict, + virTypedParameterPtr *ret_params, + int *ret_nparams, + virPyTypedParamsHintPtr hints, + int nhints) +{ + PyObject *key; + PyObject *value; +#if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION <= 4 + int pos = 0; +#else + Py_ssize_t pos = 0; +#endif + virTypedParameterPtr params = NULL; + int i; + int n = 0; + int max = 0; + int ret = -1; + + *ret_params = NULL; + *ret_nparams = 0; + + if (PyDict_Size(dict) < 0) + return -1; + + while (PyDict_Next(dict, &pos, &key, &value)) { + char *keystr; + int type = -1; + + if (!(keystr = PyString_AsString(key))) + goto cleanup; + + for (i = 0; i < nhints; i++) { + if (STREQ(hints[i].name, keystr)) { + type = hints[i].type; + break; + } + } + + if (type == -1) { + if (PyString_Check(value)) { + type = VIR_TYPED_PARAM_STRING; + } else if (PyBool_Check(value)) { + type = VIR_TYPED_PARAM_BOOLEAN; + } else if (PyLong_Check(value)) { + unsigned long long ull = PyLong_AsUnsignedLongLong(value); + if (ull == (unsigned long long) -1 && PyErr_Occurred()) + type = VIR_TYPED_PARAM_LLONG; + else + type = VIR_TYPED_PARAM_ULLONG; + } else if (PyInt_Check(value)) { + if (PyInt_AS_LONG(value) < 0) + type = VIR_TYPED_PARAM_LLONG; + else + type = VIR_TYPED_PARAM_ULLONG; + } else if (PyFloat_Check(value)) { + type = VIR_TYPED_PARAM_DOUBLE; + } + } + + if (type == -1) { + PyErr_Format(PyExc_TypeError, + "Unknown type of \"%s\" field", keystr); + goto cleanup; + } + + switch ((virTypedParameterType) type) { + case VIR_TYPED_PARAM_INT: + { + int val; + if (libvirt_intUnwrap(value, &val) < 0 || + virTypedParamsAddInt(¶ms, &n, &max, keystr, val) < 0) + goto cleanup; + break; + } + case VIR_TYPED_PARAM_UINT: + { + unsigned int val; + if (libvirt_uintUnwrap(value, &val) < 0 || + virTypedParamsAddUInt(¶ms, &n, &max, keystr, val) < 0) + goto cleanup; + break; + } + case VIR_TYPED_PARAM_LLONG: + { + long long val; + if (libvirt_longlongUnwrap(value, &val) < 0 || + virTypedParamsAddLLong(¶ms, &n, &max, keystr, val) < 0) + goto cleanup; + break; + } + case VIR_TYPED_PARAM_ULLONG: + { + unsigned long long val; + if (libvirt_ulonglongUnwrap(value, &val) < 0 || + virTypedParamsAddULLong(¶ms, &n, &max, keystr, val) < 0) + goto cleanup; + break; + } + case VIR_TYPED_PARAM_DOUBLE: + { + double val; + if (libvirt_doubleUnwrap(value, &val) < 0 || + virTypedParamsAddDouble(¶ms, &n, &max, keystr, val) < 0) + goto cleanup; + break; + } + case VIR_TYPED_PARAM_BOOLEAN: + { + bool val; + if (libvirt_boolUnwrap(value, &val) < 0 || + virTypedParamsAddBoolean(¶ms, &n, &max, keystr, val) < 0) + goto cleanup; + break; + } + case VIR_TYPED_PARAM_STRING: + { + char *val = PyString_AsString(value); + if (!val || + virTypedParamsAddString(¶ms, &n, &max, keystr, val) < 0) + goto cleanup; + break; + } + case VIR_TYPED_PARAM_LAST: + break; /* unreachable */ + } + } + + *ret_params = params; + *ret_nparams = n; + params = NULL; + ret = 0; + +cleanup: + virTypedParamsFree(params, n); + return ret; +} + + /* * Utility function to retrieve the number of node CPUs present. * It first tries virGetNodeCPUMap, which will return the @@ -6495,6 +6649,68 @@ libvirt_virDomainMigrateGetMaxSpeed(PyObject *self ATTRIBUTE_UNUSED, PyObject *a } static PyObject * +libvirt_virDomainMigrate3(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) +{ + PyObject *pyobj_domain; + virDomainPtr domain; + PyObject *pyobj_dconn; + virConnectPtr dconn; + PyObject *dict; + unsigned int flags; + virTypedParameterPtr params; + int nparams; + virDomainPtr ddom = NULL; + + if (!PyArg_ParseTuple(args, (char *) "OOOi:virDomainMigrate3", + &pyobj_domain, &pyobj_dconn, &dict, &flags)) + return NULL; + + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + dconn = (virConnectPtr) PyvirConnect_Get(pyobj_dconn); + + if (virPyDictToTypedParams(dict, ¶ms, &nparams, NULL, 0) < 0) + return NULL; + + LIBVIRT_BEGIN_ALLOW_THREADS; + ddom = virDomainMigrate3(domain, dconn, params, nparams, flags); + LIBVIRT_END_ALLOW_THREADS; + + virTypedParamsFree(params, nparams); + return libvirt_virDomainPtrWrap(ddom); +} + +static PyObject * +libvirt_virDomainMigrateToURI3(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) +{ + PyObject *pyobj_domain; + virDomainPtr domain; + char *dconnuri; + PyObject *dict; + unsigned int flags; + virTypedParameterPtr params; + int nparams; + int ret = -1; + + if (!PyArg_ParseTuple(args, (char *) "OzOi:virDomainMigrate3", + &pyobj_domain, &dconnuri, &dict, &flags)) + return NULL; + + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + + if (virPyDictToTypedParams(dict, ¶ms, &nparams, NULL, 0) < 0) + return NULL; + + LIBVIRT_BEGIN_ALLOW_THREADS; + ret = virDomainMigrateToURI3(domain, dconnuri, params, nparams, flags); + LIBVIRT_END_ALLOW_THREADS; + + virTypedParamsFree(params, nparams); + return libvirt_intWrap(ret); +} + +static PyObject * libvirt_virDomainBlockPeek(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { PyObject *py_retval = NULL; @@ -6876,6 +7092,8 @@ static PyMethodDef libvirtMethods[] = { {(char *) "virDomainSendKey", libvirt_virDomainSendKey, METH_VARARGS, NULL}, {(char *) "virDomainMigrateGetCompressionCache", libvirt_virDomainMigrateGetCompressionCache, METH_VARARGS, NULL}, {(char *) "virDomainMigrateGetMaxSpeed", libvirt_virDomainMigrateGetMaxSpeed, METH_VARARGS, NULL}, + {(char *) "virDomainMigrate3", libvirt_virDomainMigrate3, METH_VARARGS, NULL}, + {(char *) "virDomainMigrateToURI3", libvirt_virDomainMigrateToURI3, METH_VARARGS, NULL}, {(char *) "virDomainBlockPeek", libvirt_virDomainBlockPeek, METH_VARARGS, NULL}, {(char *) "virDomainMemoryPeek", libvirt_virDomainMemoryPeek, METH_VARARGS, NULL}, {(char *) "virDomainGetDiskErrors", libvirt_virDomainGetDiskErrors, METH_VARARGS, NULL}, -- 1.8.2.1

--- tools/virsh-domain.c | 77 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 54 insertions(+), 23 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 7f9b9e3..2226b5c 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -8345,15 +8345,15 @@ doMigrate(void *opaque) char ret = '1'; virDomainPtr dom = NULL; const char *desturi = NULL; - const char *migrateuri = NULL; - const char *dname = NULL; + const char *opt = NULL; unsigned int flags = 0; vshCtrlData *data = opaque; vshControl *ctl = data->ctl; const vshCmd *cmd = data->cmd; - const char *xmlfile = NULL; - char *xml = NULL; sigset_t sigmask, oldsigmask; + virTypedParameterPtr params = NULL; + int nparams = 0; + int maxparams = 0; sigemptyset(&sigmask); sigaddset(&sigmask, SIGINT); @@ -8363,11 +8363,40 @@ doMigrate(void *opaque) if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) goto out; - if (vshCommandOptStringReq(ctl, cmd, "desturi", &desturi) < 0 || - vshCommandOptStringReq(ctl, cmd, "migrateuri", &migrateuri) < 0 || - vshCommandOptStringReq(ctl, cmd, "dname", &dname) < 0 || - vshCommandOptStringReq(ctl, cmd, "xml", &xmlfile) < 0) + if (vshCommandOptStringReq(ctl, cmd, "desturi", &desturi) < 0) + goto out; + + if (vshCommandOptStringReq(ctl, cmd, "migrateuri", &opt) < 0) + goto out; + else if (opt && + virTypedParamsAddString(¶ms, &nparams, &maxparams, + VIR_MIGRATE_PARAM_URI, opt) < 0) + goto save_error; + + if (vshCommandOptStringReq(ctl, cmd, "dname", &opt) < 0) goto out; + else if (opt && + virTypedParamsAddString(¶ms, &nparams, &maxparams, + VIR_MIGRATE_PARAM_DEST_NAME, opt) < 0) + goto save_error; + + if (vshCommandOptStringReq(ctl, cmd, "xml", &opt) < 0) { + goto out; + } else if (opt) { + char *xml; + + if (virFileReadAll(opt, 1024 * 1024, &xml) < 0) { + vshError(ctl, _("cannot read file '%s'"), opt); + goto save_error; + } + + if (virTypedParamsAddString(¶ms, &nparams, &maxparams, + VIR_MIGRATE_PARAM_DEST_XML, xml) < 0) { + VIR_FREE(xml); + goto save_error; + } + VIR_FREE(xml); + } if (vshCommandOptBool(cmd, "live")) flags |= VIR_MIGRATE_LIVE; @@ -8406,23 +8435,19 @@ doMigrate(void *opaque) if (vshCommandOptBool(cmd, "abort-on-error")) flags |= VIR_MIGRATE_ABORT_ON_ERROR; - if (xmlfile && - virFileReadAll(xmlfile, 8192, &xml) < 0) { - vshError(ctl, _("file '%s' doesn't exist"), xmlfile); - goto out; - } - if ((flags & VIR_MIGRATE_PEER2PEER) || vshCommandOptBool(cmd, "direct")) { /* migrateuri doesn't make sense for tunnelled migration */ - if (flags & VIR_MIGRATE_TUNNELLED && migrateuri != NULL) { - vshError(ctl, "%s", _("migrate: Unexpected migrateuri for peer2peer/direct migration")); + if (flags & VIR_MIGRATE_TUNNELLED && + virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_URI, NULL) == 1) { + vshError(ctl, "%s", _("migrate: Unexpected migrateuri for " + "peer2peer/direct migration")); goto out; } - if (virDomainMigrateToURI2(dom, desturi, migrateuri, - xml, flags, dname, 0) == 0) + if (virDomainMigrateToURI3(dom, desturi, params, nparams, flags) == 0) ret = '0'; } else { /* For traditional live migration, connect to the destination host directly. */ @@ -8430,10 +8455,10 @@ doMigrate(void *opaque) virDomainPtr ddom = NULL; dconn = virConnectOpenAuth(desturi, virConnectAuthPtrDefault, 0); - if (!dconn) goto out; + if (!dconn) + goto out; - ddom = virDomainMigrate2(dom, dconn, xml, flags, dname, migrateuri, 0); - if (ddom) { + if ((ddom = virDomainMigrate3(dom, dconn, params, nparams, flags))) { virDomainFree(ddom); ret = '0'; } @@ -8443,9 +8468,15 @@ doMigrate(void *opaque) out: pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL); out_sig: - if (dom) virDomainFree(dom); - VIR_FREE(xml); + virTypedParamsFree(params, nparams); + if (dom) + virDomainFree(dom); ignore_value(safewrite(data->writefd, &ret, sizeof(ret))); + return; + +save_error: + vshSaveLibvirtError(); + goto out; } static void -- 1.8.2.1

On 18.06.2013 16:06, Jiri Denemark wrote:
--- tools/virsh-domain.c | 77 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 54 insertions(+), 23 deletions(-)
diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 7f9b9e3..2226b5c 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -8345,15 +8345,15 @@ doMigrate(void *opaque) char ret = '1'; virDomainPtr dom = NULL; const char *desturi = NULL; - const char *migrateuri = NULL; - const char *dname = NULL; + const char *opt = NULL; unsigned int flags = 0; vshCtrlData *data = opaque; vshControl *ctl = data->ctl; const vshCmd *cmd = data->cmd; - const char *xmlfile = NULL; - char *xml = NULL; sigset_t sigmask, oldsigmask; + virTypedParameterPtr params = NULL; + int nparams = 0; + int maxparams = 0;
sigemptyset(&sigmask); sigaddset(&sigmask, SIGINT); @@ -8363,11 +8363,40 @@ doMigrate(void *opaque) if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) goto out;
- if (vshCommandOptStringReq(ctl, cmd, "desturi", &desturi) < 0 || - vshCommandOptStringReq(ctl, cmd, "migrateuri", &migrateuri) < 0 || - vshCommandOptStringReq(ctl, cmd, "dname", &dname) < 0 || - vshCommandOptStringReq(ctl, cmd, "xml", &xmlfile) < 0) + if (vshCommandOptStringReq(ctl, cmd, "desturi", &desturi) < 0) + goto out; + + if (vshCommandOptStringReq(ctl, cmd, "migrateuri", &opt) < 0) + goto out; + else if (opt &&
You can just drop these 'else' and hence make the code more readable.
+ virTypedParamsAddString(¶ms, &nparams, &maxparams, + VIR_MIGRATE_PARAM_URI, opt) < 0) + goto save_error; + + if (vshCommandOptStringReq(ctl, cmd, "dname", &opt) < 0) goto out; + else if (opt && + virTypedParamsAddString(¶ms, &nparams, &maxparams, + VIR_MIGRATE_PARAM_DEST_NAME, opt) < 0) + goto save_error; + + if (vshCommandOptStringReq(ctl, cmd, "xml", &opt) < 0) { + goto out; + } else if (opt) { + char *xml; + + if (virFileReadAll(opt, 1024 * 1024, &xml) < 0) { + vshError(ctl, _("cannot read file '%s'"), opt); + goto save_error;
'save_error' is not on the list of allowed labels (http://libvirt.org/hacking.html#goto) but it makes sense here.
+ } + + if (virTypedParamsAddString(¶ms, &nparams, &maxparams, + VIR_MIGRATE_PARAM_DEST_XML, xml) < 0) { + VIR_FREE(xml); + goto save_error; + } + VIR_FREE(xml); + }
if (vshCommandOptBool(cmd, "live")) flags |= VIR_MIGRATE_LIVE; @@ -8406,23 +8435,19 @@ doMigrate(void *opaque) if (vshCommandOptBool(cmd, "abort-on-error")) flags |= VIR_MIGRATE_ABORT_ON_ERROR;
- if (xmlfile && - virFileReadAll(xmlfile, 8192, &xml) < 0) { - vshError(ctl, _("file '%s' doesn't exist"), xmlfile); - goto out; - } - if ((flags & VIR_MIGRATE_PEER2PEER) || vshCommandOptBool(cmd, "direct")) {
/* migrateuri doesn't make sense for tunnelled migration */ - if (flags & VIR_MIGRATE_TUNNELLED && migrateuri != NULL) { - vshError(ctl, "%s", _("migrate: Unexpected migrateuri for peer2peer/direct migration")); + if (flags & VIR_MIGRATE_TUNNELLED && + virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_URI, NULL) == 1) { + vshError(ctl, "%s", _("migrate: Unexpected migrateuri for " + "peer2peer/direct migration")); goto out; }
- if (virDomainMigrateToURI2(dom, desturi, migrateuri, - xml, flags, dname, 0) == 0) + if (virDomainMigrateToURI3(dom, desturi, params, nparams, flags) == 0) ret = '0'; } else { /* For traditional live migration, connect to the destination host directly. */ @@ -8430,10 +8455,10 @@ doMigrate(void *opaque) virDomainPtr ddom = NULL;
dconn = virConnectOpenAuth(desturi, virConnectAuthPtrDefault, 0); - if (!dconn) goto out; + if (!dconn) + goto out;
- ddom = virDomainMigrate2(dom, dconn, xml, flags, dname, migrateuri, 0); - if (ddom) { + if ((ddom = virDomainMigrate3(dom, dconn, params, nparams, flags))) { virDomainFree(ddom); ret = '0'; } @@ -8443,9 +8468,15 @@ doMigrate(void *opaque) out: pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL); out_sig: - if (dom) virDomainFree(dom); - VIR_FREE(xml); + virTypedParamsFree(params, nparams); + if (dom) + virDomainFree(dom); ignore_value(safewrite(data->writefd, &ret, sizeof(ret))); + return; + +save_error: + vshSaveLibvirtError(); + goto out; }
static void

The parameter specifies connection parameters to use for migrating client's connection to domain's graphical console. --- include/libvirt/libvirt.h.in | 21 +++++++++++++++++++++ tools/virsh-domain.c | 11 +++++++++++ tools/virsh.pod | 18 +++++++++++++++++- 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 5f3a006..a25c7d2 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1246,6 +1246,27 @@ typedef enum { */ #define VIR_MIGRATE_PARAM_BANDWIDTH "bandwidth" +/** + * VIR_MIGRATE_PARAM_GRAPHICS_URI: + * + * virDomainMigrate* params field: URI to use for migrating client's connection + * to domain's graphical console as VIR_TYPED_PARAM_STRING. If specified, the + * client will be asked to automatically reconnect using these parameters + * instead of the automatically computed ones. This can be useful if, e.g., the + * client does not have a direct access to the network virtualization hosts are + * connected to and needs to connect through a proxy. The URI is formed as + * follows: + * + * protocol://hostname[:port]/[?parameters] + * + * where protocol is either "spice" or "vnc" and parameters is a list of + * protocol specific parameters separated by '&'. Currently recognized + * parameters are "tlsPort" and "tlsSubject". For example, + * + * spice://target.host.com:1234/?tlsPort=4567 + */ +#define VIR_MIGRATE_PARAM_GRAPHICS_URI "graphics_uri" + /* Domain migration. */ virDomainPtr virDomainMigrate (virDomainPtr domain, virConnectPtr dconn, diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 2226b5c..873f123 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -8324,6 +8324,10 @@ static const vshCmdOptDef opts_migrate[] = { .type = VSH_OT_DATA, .help = N_("migration URI, usually can be omitted") }, + {.name = "graphicsuri", + .type = VSH_OT_DATA, + .help = N_("graphics URI to be used for seamless graphics migration") + }, {.name = "dname", .type = VSH_OT_DATA, .help = N_("rename to new name during migration (if supported)") @@ -8373,6 +8377,13 @@ doMigrate(void *opaque) VIR_MIGRATE_PARAM_URI, opt) < 0) goto save_error; + if (vshCommandOptStringReq(ctl, cmd, "graphicsuri", &opt) < 0) + goto out; + else if (opt && + virTypedParamsAddString(¶ms, &nparams, &maxparams, + VIR_MIGRATE_PARAM_GRAPHICS_URI, opt) < 0) + goto save_error; + if (vshCommandOptStringReq(ctl, cmd, "dname", &opt) < 0) goto out; else if (opt && diff --git a/tools/virsh.pod b/tools/virsh.pod index f4746cc..94fe897 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -1044,7 +1044,7 @@ stats. [I<--persistent>] [I<--undefinesource>] [I<--suspend>] [I<--copy-storage-all>] [I<--copy-storage-inc>] [I<--change-protection>] [I<--unsafe>] [I<--verbose>] [I<--compressed>] [I<--abort-on-error>] -I<domain> I<desturi> [I<migrateuri>] [I<dname>] +I<domain> I<desturi> [I<migrateuri>] [I<graphicsuri>] [I<dname>] [I<--timeout> B<seconds>] [I<--xml> B<file>] Migrate domain to another host. Add I<--live> for live migration; <--p2p> @@ -1136,6 +1136,22 @@ order to comply with local firewall policies. =back +Optional I<graphicsuri> overrides connection parameters used for automatically +reconnecting a graphical clients at the end of migration. If omitted, libvirt +will compute the parameters based on target host IP address. In case the +client does not have a direct access to the network virtualization hosts are +connected to and needs to connect through a proxy, I<graphicsuri> may be used +to specify the address the client should connect to. The URI is formed as +follows: + + protocol://hostname[:port]/[?parameters] + +where protocol is either "spice" or "vnc" and parameters is a list of protocol +specific parameters separated by '&'. Currently recognized parameters are +"tlsPort" and "tlsSubject". For example, + + spice://target.host.com:1234/?tlsPort=4567 + =item B<migrate-setmaxdowntime> I<domain> I<downtime> Set maximum tolerable downtime for a domain which is being live-migrated to -- 1.8.2.1

--- src/qemu/qemu_driver.c | 14 ++-- src/qemu/qemu_migration.c | 185 ++++++++++++++++++++++++++++++++-------------- src/qemu/qemu_migration.h | 2 + 3 files changed, 139 insertions(+), 62 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 29210a5..f7824b6 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -9778,7 +9778,7 @@ qemuDomainMigratePerform(virDomainPtr dom, * Consume any cookie we were able to decode though */ ret = qemuMigrationPerform(driver, dom->conn, vm, - NULL, dconnuri, uri, cookie, cookielen, + NULL, dconnuri, uri, NULL, cookie, cookielen, NULL, NULL, /* No output cookies in v2 */ flags, dname, resource, false); @@ -10044,7 +10044,7 @@ qemuDomainMigratePerform3(virDomainPtr dom, return -1; return qemuMigrationPerform(driver, dom->conn, vm, xmlin, - dconnuri, uri, cookiein, cookieinlen, + dconnuri, uri, NULL, cookiein, cookieinlen, cookieout, cookieoutlen, flags, dname, resource, true); } @@ -10065,6 +10065,7 @@ qemuDomainMigratePerform3Params(virDomainPtr dom, const char *dom_xml = NULL; const char *dname = NULL; const char *uri = NULL; + const char *graphicsuri = NULL; unsigned long long bandwidth = 0; virCheckFlags(QEMU_MIGRATION_FLAGS, -1); @@ -10082,15 +10083,18 @@ qemuDomainMigratePerform3Params(virDomainPtr dom, &uri) < 0 || virTypedParamsGetULLong(params, nparams, VIR_MIGRATE_PARAM_BANDWIDTH, - &bandwidth) < 0) + &bandwidth) < 0 || + virTypedParamsGetString(params, nparams, + VIR_MIGRATE_PARAM_GRAPHICS_URI, + &graphicsuri) < 0) return -1; if (!(vm = qemuDomObjFromDomain(dom))) return -1; return qemuMigrationPerform(driver, dom->conn, vm, dom_xml, - dconnuri, uri, cookiein, cookieinlen, - cookieout, cookieoutlen, + dconnuri, uri, graphicsuri, + cookiein, cookieinlen, cookieout, cookieoutlen, flags, dname, bandwidth, true); } diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index a63af70..ce27c92 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -1785,44 +1785,87 @@ cancel: static int qemuDomainMigrateGraphicsRelocate(virQEMUDriverPtr driver, virDomainObjPtr vm, - qemuMigrationCookiePtr cookie) + qemuMigrationCookiePtr cookie, + const char *graphicsuri) { qemuDomainObjPrivatePtr priv = vm->privateData; - int ret; - char *listenAddress; + int ret = -1; + const char *listenAddress = NULL; virSocketAddr addr; + virURIPtr uri = NULL; + int type = -1; + int port = -1; + int tlsPort = -1; + const char *tlsSubject = NULL; - if (!cookie) + if (!cookie || (!cookie->graphics && !graphicsuri)) return 0; - if (!cookie->graphics) - return 0; + if (graphicsuri && !(uri = virURIParse(graphicsuri))) + goto cleanup; + + if (cookie->graphics) { + type = cookie->graphics->type; + + listenAddress = cookie->graphics->listen; + + if (!listenAddress || + (virSocketAddrParse(&addr, listenAddress, AF_UNSPEC) > 0 && + virSocketAddrIsWildcard(&addr))) + listenAddress = cookie->remoteHostname; + + port = cookie->graphics->port; + tlsPort = cookie->graphics->tlsPort; + tlsSubject = cookie->graphics->tlsSubject; + } + + if (uri) { + int i; + + if ((type = virDomainGraphicsTypeFromString(uri->scheme)) < 0) { + virReportError(VIR_ERR_INVALID_ARG, + _("unknown graphics type %s"), uri->scheme); + goto cleanup; + } + + if (uri->server) + listenAddress = uri->server; + if (uri->port > 0) + port = uri->port; + + for (i = 0; i < uri->paramsCount; i++) { + virURIParamPtr param = uri->params + i; + + if (STRCASEEQ(param->name, "tlsPort")) { + if (virStrToLong_i(param->value, NULL, 10, &tlsPort) < 0) { + virReportError(VIR_ERR_INVALID_ARG, + _("invalid tlsPort number: %s"), + param->value); + goto cleanup; + } + } else if (STRCASEEQ(param->name, "tlsSubject")) { + tlsSubject = param->value; + } + } + } /* QEMU doesn't support VNC relocation yet, so * skip it to avoid generating an error */ - if (cookie->graphics->type != VIR_DOMAIN_GRAPHICS_TYPE_SPICE) - return 0; - - listenAddress = cookie->graphics->listen; - - if (!listenAddress || - (virSocketAddrParse(&addr, listenAddress, AF_UNSPEC) > 0 && - virSocketAddrIsWildcard(&addr))) - listenAddress = cookie->remoteHostname; + if (type != VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { + ret = 0; + goto cleanup; + } - ret = qemuDomainObjEnterMonitorAsync(driver, vm, - QEMU_ASYNC_JOB_MIGRATION_OUT); - if (ret == 0) { - ret = qemuMonitorGraphicsRelocate(priv->mon, - cookie->graphics->type, - listenAddress, - cookie->graphics->port, - cookie->graphics->tlsPort, - cookie->graphics->tlsSubject); + if (qemuDomainObjEnterMonitorAsync(driver, vm, + QEMU_ASYNC_JOB_MIGRATION_OUT) == 0) { + ret = qemuMonitorGraphicsRelocate(priv->mon, type, listenAddress, + port, tlsPort, tlsSubject); qemuDomainObjExitMonitor(driver, vm); } +cleanup: + virURIFree(uri); return ret; } @@ -3068,7 +3111,8 @@ qemuMigrationRun(virQEMUDriverPtr driver, unsigned long flags, unsigned long resource, qemuMigrationSpecPtr spec, - virConnectPtr dconn) + virConnectPtr dconn, + const char *graphicsuri) { int ret = -1; unsigned int migrate_flags = QEMU_MONITOR_MIGRATE_BACKGROUND; @@ -3083,10 +3127,11 @@ qemuMigrationRun(virQEMUDriverPtr driver, VIR_DEBUG("driver=%p, vm=%p, cookiein=%s, cookieinlen=%d, " "cookieout=%p, cookieoutlen=%p, flags=%lx, resource=%lu, " - "spec=%p (dest=%d, fwd=%d)", + "spec=%p (dest=%d, fwd=%d), dconn=%p, graphicsuri=%s", driver, vm, NULLSTR(cookiein), cookieinlen, cookieout, cookieoutlen, flags, resource, - spec, spec->destType, spec->fwdType); + spec, spec->destType, spec->fwdType, dconn, + NULLSTR(graphicsuri)); if (flags & VIR_MIGRATE_NON_SHARED_DISK) { migrate_flags |= QEMU_MONITOR_MIGRATE_NON_SHARED_DISK; @@ -3112,7 +3157,7 @@ qemuMigrationRun(virQEMUDriverPtr driver, if (!mig) goto cleanup; - if (qemuDomainMigrateGraphicsRelocate(driver, vm, mig) < 0) + if (qemuDomainMigrateGraphicsRelocate(driver, vm, mig, graphicsuri) < 0) VIR_WARN("unable to provide data for graphics client relocation"); /* this will update migrate_flags on success */ @@ -3306,7 +3351,8 @@ static int doNativeMigrate(virQEMUDriverPtr driver, int *cookieoutlen, unsigned long flags, unsigned long resource, - virConnectPtr dconn) + virConnectPtr dconn, + const char *graphicsuri) { qemuDomainObjPrivatePtr priv = vm->privateData; virURIPtr uribits = NULL; @@ -3314,9 +3360,11 @@ static int doNativeMigrate(virQEMUDriverPtr driver, qemuMigrationSpec spec; VIR_DEBUG("driver=%p, vm=%p, uri=%s, cookiein=%s, cookieinlen=%d, " - "cookieout=%p, cookieoutlen=%p, flags=%lx, resource=%lu", + "cookieout=%p, cookieoutlen=%p, flags=%lx, resource=%lu, " + "graphicsuri=%s", driver, vm, uri, NULLSTR(cookiein), cookieinlen, - cookieout, cookieoutlen, flags, resource); + cookieout, cookieoutlen, flags, resource, + NULLSTR(graphicsuri)); if (STRPREFIX(uri, "tcp:") && !STRPREFIX(uri, "tcp://")) { char *tmp; @@ -3342,7 +3390,8 @@ static int doNativeMigrate(virQEMUDriverPtr driver, spec.fwdType = MIGRATION_FWD_DIRECT; ret = qemuMigrationRun(driver, vm, cookiein, cookieinlen, cookieout, - cookieoutlen, flags, resource, &spec, dconn); + cookieoutlen, flags, resource, &spec, dconn, + graphicsuri); if (spec.destType == MIGRATION_DEST_FD) VIR_FORCE_CLOSE(spec.dest.fd.qemu); @@ -3362,7 +3411,8 @@ static int doTunnelMigrate(virQEMUDriverPtr driver, int *cookieoutlen, unsigned long flags, unsigned long resource, - virConnectPtr dconn) + virConnectPtr dconn, + const char *graphicsuri) { qemuDomainObjPrivatePtr priv = vm->privateData; virNetSocketPtr sock = NULL; @@ -3371,9 +3421,11 @@ static int doTunnelMigrate(virQEMUDriverPtr driver, virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); VIR_DEBUG("driver=%p, vm=%p, st=%p, cookiein=%s, cookieinlen=%d, " - "cookieout=%p, cookieoutlen=%p, flags=%lx, resource=%lu", + "cookieout=%p, cookieoutlen=%p, flags=%lx, resource=%lu, " + "graphicsuri=%s", driver, vm, st, NULLSTR(cookiein), cookieinlen, - cookieout, cookieoutlen, flags, resource); + cookieout, cookieoutlen, flags, resource, + NULLSTR(graphicsuri)); if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_MIGRATE_QEMU_FD) && !virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_MIGRATE_QEMU_UNIX) && @@ -3427,7 +3479,8 @@ static int doTunnelMigrate(virQEMUDriverPtr driver, } ret = qemuMigrationRun(driver, vm, cookiein, cookieinlen, cookieout, - cookieoutlen, flags, resource, &spec, dconn); + cookieoutlen, flags, resource, &spec, dconn, + graphicsuri); cleanup: if (spec.destType == MIGRATION_DEST_FD) { @@ -3533,12 +3586,12 @@ static int doPeer2PeerMigrate2(virQEMUDriverPtr driver, if (flags & VIR_MIGRATE_TUNNELLED) ret = doTunnelMigrate(driver, vm, st, NULL, 0, NULL, NULL, - flags, resource, dconn); + flags, resource, dconn, NULL); else ret = doNativeMigrate(driver, vm, uri_out, cookie, cookielen, NULL, NULL, /* No out cookie with v2 migration */ - flags, resource, dconn); + flags, resource, dconn, NULL); /* Perform failed. Make sure Finish doesn't overwrite the error */ if (ret < 0) @@ -3596,6 +3649,7 @@ doPeer2PeerMigrate3(virQEMUDriverPtr driver, const char *xmlin, const char *dname, const char *uri, + const char *graphicsuri, unsigned long long bandwidth, bool useParams, unsigned long flags) @@ -3618,9 +3672,11 @@ doPeer2PeerMigrate3(virQEMUDriverPtr driver, int maxparams = 0; VIR_DEBUG("driver=%p, sconn=%p, dconn=%p, dconnuri=%s, vm=%p, xmlin=%s, " - "dname=%s, uri=%s, bandwidth=%llu, useParams=%d, flags=%lx", + "dname=%s, uri=%s, graphicsuri=%s, bandwidth=%llu, " + "useParams=%d, flags=%lx", driver, sconn, dconn, NULLSTR(dconnuri), vm, NULLSTR(xmlin), - NULLSTR(dname), NULLSTR(uri), bandwidth, useParams, flags); + NULLSTR(dname), NULLSTR(uri), NULLSTR(graphicsuri), bandwidth, + useParams, flags); /* Unlike the virDomainMigrateVersion3 counterpart, we don't need * to worry about auto-setting the VIR_MIGRATE_CHANGE_PROTECTION @@ -3652,6 +3708,12 @@ doPeer2PeerMigrate3(virQEMUDriverPtr driver, VIR_MIGRATE_PARAM_BANDWIDTH, bandwidth) < 0) goto cleanup; + + if (graphicsuri && + virTypedParamsAddString(¶ms, &nparams, &maxparams, + VIR_MIGRATE_PARAM_GRAPHICS_URI, + graphicsuri) < 0) + goto cleanup; } if (virDomainObjGetState(vm, NULL) == VIR_DOMAIN_PAUSED) @@ -3730,12 +3792,12 @@ doPeer2PeerMigrate3(virQEMUDriverPtr driver, ret = doTunnelMigrate(driver, vm, st, cookiein, cookieinlen, &cookieout, &cookieoutlen, - flags, bandwidth, dconn); + flags, bandwidth, dconn, graphicsuri); } else { ret = doNativeMigrate(driver, vm, uri, cookiein, cookieinlen, &cookieout, &cookieoutlen, - flags, bandwidth, dconn); + flags, bandwidth, dconn, graphicsuri); } /* Perform failed. Make sure Finish doesn't overwrite the error */ @@ -3853,6 +3915,7 @@ static int doPeer2PeerMigrate(virQEMUDriverPtr driver, const char *xmlin, const char *dconnuri, const char *uri, + const char *graphicsuri, unsigned long flags, const char *dname, unsigned long resource, @@ -3867,9 +3930,10 @@ static int doPeer2PeerMigrate(virQEMUDriverPtr driver, bool useParams; VIR_DEBUG("driver=%p, sconn=%p, vm=%p, xmlin=%s, dconnuri=%s, " - "uri=%s, flags=%lx, dname=%s, resource=%lu", + "uri=%s, graphicsuri=%s, flags=%lx, dname=%s, resource=%lu", driver, sconn, vm, NULLSTR(xmlin), NULLSTR(dconnuri), - NULLSTR(uri), flags, NULLSTR(dname), resource); + NULLSTR(uri), NULLSTR(graphicsuri), flags, NULLSTR(dname), + resource); /* the order of operations is important here; we make sure the * destination side is completely setup before we touch the source @@ -3946,7 +4010,8 @@ static int doPeer2PeerMigrate(virQEMUDriverPtr driver, if (*v3proto) { ret = doPeer2PeerMigrate3(driver, sconn, dconn, dconnuri, vm, xmlin, - dname, uri, resource, useParams, flags); + dname, uri, graphicsuri, resource, + useParams, flags); } else { ret = doPeer2PeerMigrate2(driver, sconn, dconn, vm, dconnuri, flags, dname, resource); @@ -3978,6 +4043,7 @@ qemuMigrationPerformJob(virQEMUDriverPtr driver, const char *xmlin, const char *dconnuri, const char *uri, + const char *graphicsuri, const char *cookiein, int cookieinlen, char **cookieout, @@ -4013,13 +4079,13 @@ qemuMigrationPerformJob(virQEMUDriverPtr driver, if ((flags & (VIR_MIGRATE_TUNNELLED | VIR_MIGRATE_PEER2PEER))) { ret = doPeer2PeerMigrate(driver, conn, vm, xmlin, - dconnuri, uri, flags, dname, + dconnuri, uri, graphicsuri, flags, dname, resource, &v3proto); } else { qemuMigrationJobSetPhase(driver, vm, QEMU_MIGRATION_PHASE_PERFORM2); ret = doNativeMigrate(driver, vm, uri, cookiein, cookieinlen, cookieout, cookieoutlen, - flags, resource, NULL); + flags, resource, NULL, NULL); } if (ret < 0) goto endjob; @@ -4093,6 +4159,7 @@ qemuMigrationPerformPhase(virQEMUDriverPtr driver, virConnectPtr conn, virDomainObjPtr vm, const char *uri, + const char *graphicsuri, const char *cookiein, int cookieinlen, char **cookieout, @@ -4120,7 +4187,7 @@ qemuMigrationPerformPhase(virQEMUDriverPtr driver, resume = virDomainObjGetState(vm, NULL) == VIR_DOMAIN_RUNNING; ret = doNativeMigrate(driver, vm, uri, cookiein, cookieinlen, cookieout, cookieoutlen, - flags, resource, NULL); + flags, resource, NULL, graphicsuri); if (ret < 0 && resume && virDomainObjGetState(vm, NULL) == VIR_DOMAIN_PAUSED) { @@ -4177,6 +4244,7 @@ qemuMigrationPerform(virQEMUDriverPtr driver, const char *xmlin, const char *dconnuri, const char *uri, + const char *graphicsuri, const char *cookiein, int cookieinlen, char **cookieout, @@ -4187,12 +4255,13 @@ qemuMigrationPerform(virQEMUDriverPtr driver, bool v3proto) { VIR_DEBUG("driver=%p, conn=%p, vm=%p, xmlin=%s, dconnuri=%s, " - "uri=%s, cookiein=%s, cookieinlen=%d, cookieout=%p, " - "cookieoutlen=%p, flags=%lx, dname=%s, resource=%lu, v3proto=%d", + "uri=%s, graphicsuri=%s, " + "cookiein=%s, cookieinlen=%d, cookieout=%p, cookieoutlen=%p, " + "flags=%lx, dname=%s, resource=%lu, v3proto=%d", driver, conn, vm, NULLSTR(xmlin), NULLSTR(dconnuri), - NULLSTR(uri), NULLSTR(cookiein), cookieinlen, - cookieout, cookieoutlen, flags, NULLSTR(dname), - resource, v3proto); + NULLSTR(uri), NULLSTR(graphicsuri), + NULLSTR(cookiein), cookieinlen, cookieout, cookieoutlen, + flags, NULLSTR(dname), resource, v3proto); if ((flags & (VIR_MIGRATE_TUNNELLED | VIR_MIGRATE_PEER2PEER))) { if (cookieinlen) { @@ -4202,9 +4271,9 @@ qemuMigrationPerform(virQEMUDriverPtr driver, } return qemuMigrationPerformJob(driver, conn, vm, xmlin, dconnuri, uri, - cookiein, cookieinlen, cookieout, - cookieoutlen, flags, dname, resource, - v3proto); + graphicsuri, cookiein, cookieinlen, + cookieout, cookieoutlen, + flags, dname, resource, v3proto); } else { if (dconnuri) { virReportError(VIR_ERR_INTERNAL_ERROR, @@ -4214,12 +4283,14 @@ qemuMigrationPerform(virQEMUDriverPtr driver, if (v3proto) { return qemuMigrationPerformPhase(driver, conn, vm, uri, + graphicsuri, cookiein, cookieinlen, cookieout, cookieoutlen, flags, resource); } else { return qemuMigrationPerformJob(driver, conn, vm, xmlin, dconnuri, - uri, cookiein, cookieinlen, + uri, graphicsuri, + cookiein, cookieinlen, cookieout, cookieoutlen, flags, dname, resource, v3proto); } diff --git a/src/qemu/qemu_migration.h b/src/qemu/qemu_migration.h index 65663e9..a67a3fe 100644 --- a/src/qemu/qemu_migration.h +++ b/src/qemu/qemu_migration.h @@ -47,6 +47,7 @@ VIR_MIGRATE_PARAM_DEST_NAME, VIR_TYPED_PARAM_STRING, \ VIR_MIGRATE_PARAM_DEST_XML, VIR_TYPED_PARAM_STRING, \ VIR_MIGRATE_PARAM_BANDWIDTH, VIR_TYPED_PARAM_ULLONG, \ + VIR_MIGRATE_PARAM_GRAPHICS_URI, VIR_TYPED_PARAM_STRING, \ NULL @@ -120,6 +121,7 @@ int qemuMigrationPerform(virQEMUDriverPtr driver, const char *xmlin, const char *dconnuri, const char *uri, + const char *graphicsuri, const char *cookiein, int cookieinlen, char **cookieout, -- 1.8.2.1

On 18.06.2013 16:05, Jiri Denemark wrote:
Current migration APIs support a fixed set of optional parameters and whenever there is a need for a new parameter, a whole bunch of new internal and external APIs have to be introduced. This patch set introduces new extensible APIs that use virTypedParameters for passing optional parameters to a migration protocol. Thus making it possible to introduce new parameters without the need for new APIs.
The first 7 patches provide some basic infrastructure needed by the following patches. Patches 8 to 19 add support for the new extensible APIs and patches 20 and 21 make use of the new APIs to introduce additional migration parameter.
Have fun.
Jiri Denemark (21): 1 Rename virTypedParameterArrayValidate as virTypedParamsValidate 2 util: Emit proper error code in virTypedParamsValidate 3 Introduce virTypedParamsCheck internal API 4 Introduce virTypedParamsReplaceString internal API 5 Introduce VIR_TYPED_PARAMS_DEBUG macro for dumping typed params 6 Log input type parameters in API entry points 7 Introduce virTypedParamsCopy internal API 8 Introduce migration parameters 9 New internal migration APIs with extensible parameters 10 Implement extensible migration APIs in remote driver 11 Implement extensible migration APIs in qemu driver 12 qemu: Move internals of Begin phase to qemu_migration.c 13 qemu: Move internals of Prepare phase to qemu_migration.c 14 qemu: Move internals of Confirm phase to qemu_migration.c 15 Adapt virDomainMigrateVersion3 for extensible migration APIs 16 Adapt virDomainMigratePeer2Peer for extensible migration APIs 17 Extensible migration APIs 18 python: Add bindings for extensible migration APIs 19 virsh: Use extensible migration APIs 20 Introduce VIR_MIGRATE_PARAM_GRAPHICS_URI parameter 21 qemu: Implement support for VIR_MIGRATE_PARAM_GRAPHICS_URI
daemon/remote.c | 331 ++++++++++++- docs/apibuild.py | 11 +- docs/hvsupport.pl | 7 + include/libvirt/libvirt.h.in | 87 ++++ python/generator.py | 2 + python/libvirt-override-api.xml | 18 + python/libvirt-override.c | 218 +++++++++ src/driver.h | 67 +++ src/esx/esx_driver.c | 24 +- src/libvirt.c | 1022 ++++++++++++++++++++++++++++++++++----- src/libvirt_internal.h | 59 +++ src/libvirt_private.syms | 14 +- src/libvirt_public.syms | 6 + src/libxl/libxl_driver.c | 12 +- src/lxc/lxc_driver.c | 40 +- src/nodeinfo.c | 16 +- src/openvz/openvz_driver.c | 16 +- src/qemu/qemu_driver.c | 633 ++++++++++++------------ src/qemu/qemu_migration.c | 730 ++++++++++++++++++++-------- src/qemu/qemu_migration.h | 45 +- src/remote/remote_driver.c | 398 +++++++++++++++ src/remote/remote_protocol.x | 96 +++- src/remote_protocol-structs | 107 ++++ src/test/test_driver.c | 8 +- src/util/virtypedparam.c | 163 ++++++- src/util/virtypedparam.h | 36 +- src/xen/xen_hypervisor.c | 12 +- tools/virsh-domain.c | 88 +++- tools/virsh.pod | 18 +- 29 files changed, 3548 insertions(+), 736 deletions(-)
I've not spotted anything obviously wrong in the patch set. But see my inline comments to patches. Esp. in 05/21 please do report OOM error for virAsprintf (I know I promised virAsprintf to report OOM error and I got patches ready, but haven't posted yet). ACK series Michal

On Thu, Jun 20, 2013 at 10:42:11 +0200, Michal Privoznik wrote:
I've not spotted anything obviously wrong in the patch set. But see my inline comments to patches. Esp. in 05/21 please do report OOM error for virAsprintf (I know I promised virAsprintf to report OOM error and I got patches ready, but haven't posted yet).
ACK series
Thanks, I fixed the nits but I'll wait for Daniel to double check sanity of the proposed APIs before pushing. Jirka

On Thu, Jun 20, 2013 at 11:59:09 +0200, Jiri Denemark wrote:
On Thu, Jun 20, 2013 at 10:42:11 +0200, Michal Privoznik wrote:
I've not spotted anything obviously wrong in the patch set. But see my inline comments to patches. Esp. in 05/21 please do report OOM error for virAsprintf (I know I promised virAsprintf to report OOM error and I got patches ready, but haven't posted yet).
ACK series
Thanks, I fixed the nits but I'll wait for Daniel to double check sanity of the proposed APIs before pushing.
I pushed all patches except for qemu driver implementation as that needs to be modified because of the recently pushed ACL support. Jirka
participants (4)
-
Daniel P. Berrange
-
Jiri Denemark
-
John Ferlan
-
Michal Privoznik