[libvirt] [PATCH 0/4] Allow changing guest XML during QEMU migration

This patch series updates the QEMU driver to allow it to take advantage of the migration v3 protocol support for changing guest XML during migration. eg virsh migrate --xml migtest.xml migtest qemu+ssh://somehost/system NB the XML is strictly validated for guest ABI compatibility so only things like host NIC device names or host disk file paths can be changed

To allow a client app to pass in custom XML during migration of a guest it is neccessary to ensure the guest ABI remains unchanged. The virDomainDefCheckABIStablity method accepts two virDomainDefPtr structs and compares everything in them that could impact the guest machine ABI * src/conf/domain_conf.c, src/conf/domain_conf.h, src/libvirt_private.syms: Add virDomainDefCheckABIStablity * src/conf/cpu_conf.c, src/conf/cpu_conf.h: Add virCPUDefIsEqual * src/util/sysinfo.c, src/util/sysinfo.h: Add virSysinfoIsEqual --- src/conf/cpu_conf.c | 91 +++++ src/conf/cpu_conf.h | 9 +- src/conf/domain_conf.c | 881 +++++++++++++++++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 11 +- src/libvirt_private.syms | 1 + src/util/sysinfo.c | 60 +++- src/util/sysinfo.h | 11 +- 7 files changed, 1051 insertions(+), 13 deletions(-) diff --git a/src/conf/cpu_conf.c b/src/conf/cpu_conf.c index 98d598a..77d0976 100644 --- a/src/conf/cpu_conf.c +++ b/src/conf/cpu_conf.c @@ -35,6 +35,9 @@ virReportErrorHelper(VIR_FROM_CPU, code, __FILE__, \ __FUNCTION__, __LINE__, __VA_ARGS__) +VIR_ENUM_IMPL(virCPU, VIR_CPU_TYPE_LAST, + "host", "guest", "auto") + VIR_ENUM_IMPL(virCPUMatch, VIR_CPU_MATCH_LAST, "minimum", "exact", @@ -446,3 +449,91 @@ no_memory: virReportOOMError(); return -1; } + +bool +virCPUDefIsEqual(virCPUDefPtr src, + virCPUDefPtr dst) +{ + bool identical = false; + int i; + + if (!src && !dst) + return true; + + if ((src && !dst) || (!src && dst)) { + virCPUReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Target CPU does not match source")); + goto cleanup; + } + + if (src->type != dst->type) { + virCPUReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target CPU type %s does not match source %s"), + virCPUTypeToString(dst->type), + virCPUTypeToString(src->type)); + goto cleanup; + } + + if (STRNEQ_NULLABLE(src->arch, dst->arch)) { + virCPUReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target CPU arch %s does not match source %s"), + NULLSTR(dst->arch), NULLSTR(src->arch)); + goto cleanup; + } + + if (STRNEQ_NULLABLE(src->model, dst->model)) { + virCPUReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target CPU model %s does not match source %s"), + NULLSTR(dst->model), NULLSTR(src->model)); + goto cleanup; + } + + if (STRNEQ_NULLABLE(src->vendor, dst->vendor)) { + virCPUReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target CPU vendor %s does not match source %s"), + NULLSTR(dst->vendor), NULLSTR(src->vendor)); + goto cleanup; + } + + if (src->sockets != dst->sockets) { + virCPUReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target CPU sockets %d does not match source %d"), + dst->sockets, src->sockets); + goto cleanup; + } + + if (src->cores != dst->cores) { + virCPUReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target CPU cores %d does not match source %d"), + dst->cores, src->cores); + goto cleanup; + } + + if (src->threads != dst->threads) { + virCPUReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target CPU threads %d does not match source %d"), + dst->threads, src->threads); + goto cleanup; + } + + if (src->nfeatures != dst->nfeatures) { + virCPUReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target CPU feature count %zu does not match source %zu"), + dst->nfeatures, src->nfeatures); + goto cleanup; + } + + for (i = 0 ; i < src->nfeatures ; i++) { + if (STRNEQ(src->features[i].name, dst->features[i].name)) { + virCPUReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target CPU feature %s does not match source %s"), + dst->features[i].name, src->features[i].name); + goto cleanup; + } + } + + identical = true; + +cleanup: + return identical; +} diff --git a/src/conf/cpu_conf.h b/src/conf/cpu_conf.h index 055887c..ecd4e10 100644 --- a/src/conf/cpu_conf.h +++ b/src/conf/cpu_conf.h @@ -31,9 +31,13 @@ enum virCPUType { VIR_CPU_TYPE_HOST, VIR_CPU_TYPE_GUEST, - VIR_CPU_TYPE_AUTO + VIR_CPU_TYPE_AUTO, + + VIR_CPU_TYPE_LAST }; +VIR_ENUM_DECL(virCPU) + enum virCPUMatch { VIR_CPU_MATCH_MINIMUM, VIR_CPU_MATCH_EXACT, @@ -96,6 +100,9 @@ enum virCPUFormatFlags { * in host capabilities */ }; +bool +virCPUDefIsEqual(virCPUDefPtr src, + virCPUDefPtr dst); char * virCPUDefFormat(virCPUDefPtr def, diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 8ff155b..a9a4655 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -268,9 +268,6 @@ VIR_ENUM_IMPL(virDomainMemballoonModel, VIR_DOMAIN_MEMBALLOON_MODEL_LAST, "xen", "none") -VIR_ENUM_IMPL(virDomainSysinfo, VIR_DOMAIN_SYSINFO_LAST, - "smbios") - VIR_ENUM_IMPL(virDomainSmbiosMode, VIR_DOMAIN_SMBIOS_LAST, "none", "emulate", @@ -4364,7 +4361,7 @@ virSysinfoParseXML(const xmlNodePtr node, _("sysinfo must contain a type attribute")); goto error; } - if ((def->type = virDomainSysinfoTypeFromString(type)) < 0) { + if ((def->type = virSysinfoTypeFromString(type)) < 0) { virDomainReportError(VIR_ERR_INTERNAL_ERROR, _("unknown sysinfo type '%s'"), type); goto error; @@ -6521,6 +6518,882 @@ virDomainObjPtr virDomainObjParseFile(virCapsPtr caps, } +static bool virDomainTimerDefCheckABIStability(virDomainTimerDefPtr src, + virDomainTimerDefPtr dst) +{ + bool identical = false; + + if (src->name != dst->name) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target timer %s does not match source %s"), + virDomainTimerNameTypeToString(dst->name), + virDomainTimerNameTypeToString(src->name)); + goto cleanup; + } + + if (src->present != dst->present) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target timer presence %d does not match source %d"), + dst->present, src->present); + goto cleanup; + } + + if (src->name == VIR_DOMAIN_TIMER_NAME_TSC) { + if (src->frequency != dst->frequency) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target TSC frequency %lu does not match source %lu"), + dst->frequency, src->frequency); + goto cleanup; + } + + if (src->mode != dst->mode) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target TSC mode %s does not match source %s"), + virDomainTimerModeTypeToString(dst->mode), + virDomainTimerModeTypeToString(src->mode)); + goto cleanup; + } + } + + identical = true; + +cleanup: + return identical; +} + + +static bool virDomainDeviceInfoCheckABIStability(virDomainDeviceInfoPtr src, + virDomainDeviceInfoPtr dst) +{ + bool identical = false; + + if (src->type != dst->type) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target device address type %s does not match source %s"), + virDomainDeviceAddressTypeToString(dst->type), + virDomainDeviceAddressTypeToString(src->type)); + goto cleanup; + } + + switch (src->type) { + case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI: + if (src->addr.pci.domain != dst->addr.pci.domain || + src->addr.pci.bus != dst->addr.pci.bus || + src->addr.pci.slot != dst->addr.pci.slot || + src->addr.pci.function != dst->addr.pci.function) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target device PCI address %04x:%02x:%02x.%02x does not match source %04x:%02x:%02x.%02x"), + dst->addr.pci.domain, dst->addr.pci.bus, + dst->addr.pci.slot, dst->addr.pci.function, + src->addr.pci.domain, src->addr.pci.bus, + src->addr.pci.slot, src->addr.pci.function); + goto cleanup; + } + break; + + case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE: + if (src->addr.drive.controller != dst->addr.drive.controller || + src->addr.drive.bus != dst->addr.drive.bus || + src->addr.drive.unit != dst->addr.drive.unit) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target device drive address %d:%d:%d does not match source %d:%d:%d"), + dst->addr.drive.controller, dst->addr.drive.bus, + dst->addr.drive.unit, + src->addr.drive.controller, src->addr.drive.bus, + src->addr.drive.unit); + goto cleanup; + } + break; + + case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL: + if (src->addr.vioserial.controller != dst->addr.vioserial.controller || + src->addr.vioserial.bus != dst->addr.vioserial.bus || + src->addr.vioserial.port != dst->addr.vioserial.port) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target device virtio serial address %d:%d:%d does not match source %d:%d:%d"), + dst->addr.vioserial.controller, dst->addr.vioserial.bus, + dst->addr.vioserial.port, + src->addr.vioserial.controller, src->addr.vioserial.bus, + src->addr.vioserial.port); + goto cleanup; + } + break; + + case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID: + if (src->addr.ccid.controller != dst->addr.ccid.controller || + src->addr.ccid.slot != dst->addr.ccid.slot) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target device ccid address %d:%d does not match source %d:%d"), + dst->addr.ccid.controller, + dst->addr.ccid.slot, + src->addr.ccid.controller, + src->addr.ccid.slot); + goto cleanup; + } + break; + } + + identical = true; + +cleanup: + return identical; +} + + +static bool virDomainDiskDefCheckABIStability(virDomainDiskDefPtr src, + virDomainDiskDefPtr dst) +{ + bool identical = false; + + if (src->device != dst->device) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target disk device %s does not match source %s"), + virDomainDiskDeviceTypeToString(dst->device), + virDomainDiskDeviceTypeToString(src->device)); + goto cleanup; + } + + if (src->bus != dst->bus) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target disk bus %s does not match source %s"), + virDomainDiskBusTypeToString(dst->bus), + virDomainDiskBusTypeToString(src->bus)); + goto cleanup; + } + + if (STRNEQ(src->dst, dst->dst)) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target disk %s does not match source %s"), + dst->dst, src->dst); + goto cleanup; + } + + if (STRNEQ_NULLABLE(src->serial, dst->serial)) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target disk serial %s does not match source %s"), + NULLSTR(dst->serial), NULLSTR(src->serial)); + goto cleanup; + } + + if (src->readonly != dst->readonly || src->shared != dst->shared) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Target disk access mode does not match source")); + goto cleanup; + } + + if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) + goto cleanup; + + identical = true; + +cleanup: + return identical; +} + + +static bool virDomainControllerDefCheckABIStability(virDomainControllerDefPtr src, + virDomainControllerDefPtr dst) +{ + bool identical = false; + + if (src->type != dst->type) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target controller type %s does not match source %s"), + virDomainControllerTypeToString(dst->type), + virDomainControllerTypeToString(src->type)); + goto cleanup; + } + + if (src->idx != dst->idx) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target controller index %d does not match source %d"), + dst->idx, src->idx); + goto cleanup; + } + + if (src->model != dst->model) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target controller model %d does not match source %d"), + dst->model, src->model); + goto cleanup; + } + + if (src->type == VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL) { + if (src->opts.vioserial.ports != dst->opts.vioserial.ports) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target controller ports %d does not match source %d"), + dst->opts.vioserial.ports, src->opts.vioserial.ports); + goto cleanup; + } + + if (src->opts.vioserial.vectors != dst->opts.vioserial.vectors) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target controller vectors %d does not match source %d"), + dst->opts.vioserial.vectors, src->opts.vioserial.vectors); + goto cleanup; + } + } + + if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) + goto cleanup; + + identical = true; + +cleanup: + return identical; +} + + +static bool virDomainFsDefCheckABIStability(virDomainFSDefPtr src, + virDomainFSDefPtr dst) +{ + bool identical = false; + + if (STRNEQ(src->dst, dst->dst)) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target filesystem guest target %s does not match source %s"), + dst->dst, src->dst); + goto cleanup; + } + + if (src->readonly != dst->readonly) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Target filesystem access mode does not match source")); + goto cleanup; + } + + if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) + goto cleanup; + + identical = true; + +cleanup: + return identical; +} + + +static bool virDomainNetDefCheckABIStability(virDomainNetDefPtr src, + virDomainNetDefPtr dst) +{ + bool identical = false; + + if (memcmp(src->mac, dst->mac, VIR_MAC_BUFLEN) != 0) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target network card mac %02x:%02x:%02x:%02x:%02x:%02x" + "does not match source %02x:%02x:%02x:%02x:%02x:%02x"), + dst->mac[0], dst->mac[2], dst->mac[2], + dst->mac[3], dst->mac[4], dst->mac[5], + src->mac[0], src->mac[2], src->mac[2], + src->mac[3], src->mac[4], src->mac[5]); + goto cleanup; + } + + if (STRNEQ_NULLABLE(src->model, dst->model)) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target network card model %s does not match source %s"), + NULLSTR(dst->model), NULLSTR(src->model)); + goto cleanup; + } + + if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) + goto cleanup; + + identical = true; + +cleanup: + return identical; +} + + +static bool virDomainInputDefCheckABIStability(virDomainInputDefPtr src, + virDomainInputDefPtr dst) +{ + bool identical = false; + + if (src->type != dst->type) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target input device type %s does not match source %s"), + virDomainInputTypeToString(dst->type), + virDomainInputTypeToString(src->type)); + goto cleanup; + } + + if (src->bus != dst->bus) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target input device bus %s does not match source %s"), + virDomainInputBusTypeToString(dst->bus), + virDomainInputBusTypeToString(src->bus)); + goto cleanup; + } + + if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) + goto cleanup; + + identical = true; + +cleanup: + return identical; +} + + +static bool virDomainSoundDefCheckABIStability(virDomainSoundDefPtr src, + virDomainSoundDefPtr dst) +{ + bool identical = false; + + if (src->model != dst->model) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target sound card model %s does not match source %s"), + virDomainSoundModelTypeToString(dst->model), + virDomainSoundModelTypeToString(src->model)); + goto cleanup; + } + + if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) + goto cleanup; + + identical = true; + +cleanup: + return identical; +} + + +static bool virDomainVideoDefCheckABIStability(virDomainVideoDefPtr src, + virDomainVideoDefPtr dst) +{ + bool identical = false; + + if (src->type != dst->type) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target video card model %s does not match source %s"), + virDomainVideoTypeToString(dst->type), + virDomainVideoTypeToString(src->type)); + goto cleanup; + } + + if (src->vram != dst->vram) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target video card vram %u does not match source %u"), + dst->vram, src->vram); + goto cleanup; + } + + if (src->heads != dst->heads) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target video card heads %u does not match source %u"), + dst->heads, src->heads); + goto cleanup; + } + + if ((src->accel && !dst->accel) || + (!src->accel && dst->accel)) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Target video card acceleration does not match source")); + goto cleanup; + } + + if (src->accel) { + if (src->accel->support2d != dst->accel->support2d) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target video card 2d accel %u does not match source %u"), + dst->accel->support2d, src->accel->support2d); + goto cleanup; + } + + if (src->accel->support3d != dst->accel->support3d) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target video card 3d accel %u does not match source %u"), + dst->accel->support3d, src->accel->support3d); + goto cleanup; + } + } + + if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) + goto cleanup; + + identical = true; + +cleanup: + return identical; +} + + +static bool virDomainHostdevDefCheckABIStability(virDomainHostdevDefPtr src, + virDomainHostdevDefPtr dst) +{ + bool identical = false; + + if (src->mode != dst->mode) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target host device mode %s does not match source %s"), + virDomainHostdevModeTypeToString(dst->mode), + virDomainHostdevModeTypeToString(src->mode)); + goto cleanup; + } + + if (src->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) { + if (src->source.subsys.type != dst->source.subsys.type) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target host device subsystem %s does not match source %s"), + virDomainHostdevSubsysTypeToString(dst->source.subsys.type), + virDomainHostdevSubsysTypeToString(src->source.subsys.type)); + goto cleanup; + } + } + + if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) + goto cleanup; + + identical = true; + +cleanup: + return identical; +} + + +static bool virDomainSmartcardDefCheckABIStability(virDomainSmartcardDefPtr src, + virDomainSmartcardDefPtr dst) +{ + bool identical = false; + + if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) + goto cleanup; + + identical = true; + +cleanup: + return identical; +} + + +static bool virDomainSerialDefCheckABIStability(virDomainChrDefPtr src, + virDomainChrDefPtr dst) +{ + bool identical = false; + + if (src->target.port != dst->target.port) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target serial port %d does not match source %d"), + dst->target.port, src->target.port); + goto cleanup; + } + + if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) + goto cleanup; + + identical = true; + +cleanup: + return identical; +} + + +static bool virDomainParallelDefCheckABIStability(virDomainChrDefPtr src, + virDomainChrDefPtr dst) +{ + bool identical = false; + + if (src->target.port != dst->target.port) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target serial port %d does not match source %d"), + dst->target.port, src->target.port); + goto cleanup; + } + + if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) + goto cleanup; + + identical = true; + +cleanup: + return identical; +} + + +static bool virDomainChannelDefCheckABIStability(virDomainChrDefPtr src, + virDomainChrDefPtr dst) +{ + bool identical = false; + + if (src->targetType != dst->targetType) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target channel type %s does not match source %s"), + virDomainChrChannelTargetTypeToString(dst->targetType), + virDomainChrChannelTargetTypeToString(src->targetType)); + goto cleanup; + } + + switch (src->targetType) { + case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO: + if (STRNEQ(src->target.name, dst->target.name)) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target channel name %s does not match source %s"), + dst->target.name, src->target.name); + goto cleanup; + } + break; + case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_GUESTFWD: + if (memcmp(src->target.addr, dst->target.addr, sizeof(src->target.addr)) != 0) { + char *saddr = virSocketFormatAddrFull(src->target.addr, true, ":"); + char *daddr = virSocketFormatAddrFull(dst->target.addr, true, ":"); + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target channel addr %s does not match source %s"), + NULLSTR(daddr), NULLSTR(saddr)); + VIR_FREE(saddr); + VIR_FREE(daddr); + goto cleanup; + } + break; + } + + if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) + goto cleanup; + + identical = true; + +cleanup: + return identical; +} + + +static bool virDomainConsoleDefCheckABIStability(virDomainChrDefPtr src, + virDomainChrDefPtr dst) +{ + bool identical = false; + + if (src->targetType != dst->targetType) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target console type %s does not match source %s"), + virDomainChrConsoleTargetTypeToString(dst->targetType), + virDomainChrConsoleTargetTypeToString(src->targetType)); + goto cleanup; + } + + if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) + goto cleanup; + + identical = true; + +cleanup: + return identical; +} + + +static bool virDomainWatchdogDefCheckABIStability(virDomainWatchdogDefPtr src, + virDomainWatchdogDefPtr dst) +{ + bool identical = false; + + if (src->model != dst->model) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target watchdog model %s does not match source %s"), + virDomainWatchdogModelTypeToString(dst->model), + virDomainWatchdogModelTypeToString(src->model)); + goto cleanup; + } + + if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) + goto cleanup; + + identical = true; + +cleanup: + return identical; +} + + +static bool virDomainMemballoonDefCheckABIStability(virDomainMemballoonDefPtr src, + virDomainMemballoonDefPtr dst) +{ + bool identical = false; + + if (src->model != dst->model) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target balloon model %s does not match source %s"), + virDomainMemballoonModelTypeToString(dst->model), + virDomainMemballoonModelTypeToString(src->model)); + goto cleanup; + } + + if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) + goto cleanup; + + identical = true; + +cleanup: + return identical; +} + + +/* This compares two configurations and looks for any differences + * which will affect the guest ABI. This is primarily to allow + * validation of custom XML config passed in during migration + */ +bool virDomainDefCheckABIStability(virDomainDefPtr src, + virDomainDefPtr dst) +{ + bool identical = false; + int i; + + if (src->virtType != dst->virtType) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target domain virt type %s does not match source %s"), + virDomainVirtTypeToString(dst->virtType), + virDomainVirtTypeToString(src->virtType)); + goto cleanup; + } + + if (memcmp(src->uuid, dst->uuid, VIR_UUID_BUFLEN) != 0) { + char uuidsrc[VIR_UUID_STRING_BUFLEN]; + char uuiddst[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(src->uuid, uuidsrc); + virUUIDFormat(dst->uuid, uuiddst); + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target domain uuid %s does not match source %s"), + uuiddst, uuidsrc); + goto cleanup; + } + + if (src->vcpus != dst->vcpus) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target domain vpu count %d does not match source %d"), + dst->vcpus, src->vcpus); + goto cleanup; + } + if (src->maxvcpus != dst->maxvcpus) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target domain vpu max %d does not match source %d"), + dst->maxvcpus, src->maxvcpus); + goto cleanup; + } + + if (STRNEQ(src->os.type, dst->os.type)) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target domain OS type %s does not match source %s"), + dst->os.type, src->os.type); + goto cleanup; + } + if (STRNEQ(src->os.arch, dst->os.arch)) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target domain architecture %s does not match source %s"), + dst->os.arch, src->os.arch); + goto cleanup; + } + if (STRNEQ(src->os.machine, dst->os.machine)) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target domain OS type %s does not match source %s"), + dst->os.machine, src->os.machine); + goto cleanup; + } + + if (src->os.smbios_mode != dst->os.smbios_mode) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target domain SMBIOS mode %s does not match source %s"), + virDomainSmbiosModeTypeToString(dst->os.smbios_mode), + virDomainSmbiosModeTypeToString(src->os.smbios_mode)); + goto cleanup; + } + + if (src->features != dst->features) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target domain features %d does not match source %d"), + dst->features, src->features); + goto cleanup; + } + + if (src->clock.ntimers != dst->clock.ntimers) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Target domain timers do not match source")); + goto cleanup; + } + + for (i = 0 ; i < src->clock.ntimers ; i++) { + if (!virDomainTimerDefCheckABIStability(src->clock.timers[i], dst->clock.timers[i])) + goto cleanup; + } + + if (!virCPUDefIsEqual(src->cpu, dst->cpu)) + goto cleanup; + + if (!virSysinfoIsEqual(src->sysinfo, dst->sysinfo)) + goto cleanup; + + if (src->ndisks != dst->ndisks) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target domain disk count %d does not match source %d"), + dst->ndisks, src->ndisks); + goto cleanup; + } + + for (i = 0 ; i < src->ndisks ; i++) + if (!virDomainDiskDefCheckABIStability(src->disks[i], dst->disks[i])) + goto cleanup; + + if (src->ncontrollers != dst->ncontrollers) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target domain disk controller count %d does not match source %d"), + dst->ncontrollers, src->ncontrollers); + goto cleanup; + } + + for (i = 0 ; i < src->ncontrollers ; i++) + if (!virDomainControllerDefCheckABIStability(src->controllers[i], dst->controllers[i])) + goto cleanup; + + if (src->nfss != dst->nfss) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target domain filesystem count %d does not match source %d"), + dst->nfss, src->nfss); + goto cleanup; + } + + for (i = 0 ; i < src->nfss ; i++) + if (!virDomainFsDefCheckABIStability(src->fss[i], dst->fss[i])) + goto cleanup; + + if (src->nnets != dst->nnets) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target domain net card count %d does not match source %d"), + dst->nnets, src->nnets); + goto cleanup; + } + + for (i = 0 ; i < src->nnets ; i++) + if (!virDomainNetDefCheckABIStability(src->nets[i], dst->nets[i])) + goto cleanup; + + if (src->ninputs != dst->ninputs) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target domain input device count %d does not match source %d"), + dst->ninputs, src->ninputs); + goto cleanup; + } + + for (i = 0 ; i < src->ninputs ; i++) + if (!virDomainInputDefCheckABIStability(src->inputs[i], dst->inputs[i])) + goto cleanup; + + if (src->nsounds != dst->nsounds) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target domain sound card count %d does not match source %d"), + dst->nsounds, src->nsounds); + goto cleanup; + } + + for (i = 0 ; i < src->nsounds ; i++) + if (!virDomainSoundDefCheckABIStability(src->sounds[i], dst->sounds[i])) + goto cleanup; + + if (src->nvideos != dst->nvideos) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target domain video card count %d does not match source %d"), + dst->nvideos, src->nvideos); + goto cleanup; + } + + for (i = 0 ; i < src->nvideos ; i++) + if (!virDomainVideoDefCheckABIStability(src->videos[i], dst->videos[i])) + goto cleanup; + + if (src->nhostdevs != dst->nhostdevs) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target domain host device count %d does not match source %d"), + dst->nhostdevs, src->nhostdevs); + goto cleanup; + } + + for (i = 0 ; i < src->nhostdevs ; i++) + if (!virDomainHostdevDefCheckABIStability(src->hostdevs[i], dst->hostdevs[i])) + goto cleanup; + + if (src->nsmartcards != dst->nsmartcards) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target domain smartcard count %d does not match source %d"), + dst->nsmartcards, src->nsmartcards); + goto cleanup; + } + + for (i = 0 ; i < src->nsmartcards ; i++) + if (!virDomainSmartcardDefCheckABIStability(src->smartcards[i], dst->smartcards[i])) + goto cleanup; + + if (src->nserials != dst->nserials) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target domain serial port count %d does not match source %d"), + dst->nserials, src->nserials); + goto cleanup; + } + + for (i = 0 ; i < src->nserials ; i++) + if (!virDomainSerialDefCheckABIStability(src->serials[i], dst->serials[i])) + goto cleanup; + + if (src->nparallels != dst->nparallels) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target domain parallel port count %d does not match source %d"), + dst->nparallels, src->nparallels); + goto cleanup; + } + + for (i = 0 ; i < src->nparallels ; i++) + if (!virDomainParallelDefCheckABIStability(src->parallels[i], dst->parallels[i])) + goto cleanup; + + if (src->nchannels != dst->nchannels) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target domain channel count %d does not match source %d"), + dst->nchannels, src->nchannels); + goto cleanup; + } + + for (i = 0 ; i < src->nchannels ; i++) + if (!virDomainChannelDefCheckABIStability(src->channels[i], dst->channels[i])) + goto cleanup; + + if ((!src->console && dst->console) || + (src->console && !dst->console)) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target domain console count %d does not match source %d"), + dst->console ? 1 : 0, src->console ? 1 : 0); + goto cleanup; + } + + if (src->console && + !virDomainConsoleDefCheckABIStability(src->console, dst->console)) + goto cleanup; + + if ((!src->watchdog && dst->watchdog) || + (src->watchdog && !dst->watchdog)) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target domain watchdog count %d does not match source %d"), + dst->watchdog ? 1 : 0, src->watchdog ? 1 : 0); + goto cleanup; + } + + if (src->watchdog && + !virDomainWatchdogDefCheckABIStability(src->watchdog, dst->watchdog)) + goto cleanup; + + if ((!src->memballoon && dst->memballoon) || + (src->memballoon && !dst->memballoon)) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target domain memory balloon count %d does not match source %d"), + dst->memballoon ? 1 : 0, src->memballoon ? 1 : 0); + goto cleanup; + } + + if (src->memballoon && + !virDomainMemballoonDefCheckABIStability(src->memballoon, dst->memballoon)) + goto cleanup; + + identical = true; + +cleanup: + return identical; +} + + static int virDomainDefMaybeAddController(virDomainDefPtr def, int type, int idx) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index d4245d8..47d17dd 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1097,7 +1097,12 @@ virDomainVcpupinDefPtr virDomainVcpupinFindByVcpu(virDomainVcpupinDefPtr *def, int nvcpupin, int vcpu); -/* Guest VM main configuration */ +/* + * Guest VM main configuration + * + * NB: if adding to this struct, virDomainDefCheckABIStability + * may well need an update + */ typedef struct _virDomainDef virDomainDef; typedef virDomainDef *virDomainDefPtr; struct _virDomainDef { @@ -1343,6 +1348,9 @@ virDomainDefPtr virDomainDefParseNode(virCapsPtr caps, virDomainObjPtr virDomainObjParseFile(virCapsPtr caps, const char *filename); +bool virDomainDefCheckABIStability(virDomainDefPtr src, + virDomainDefPtr dst); + int virDomainDefAddImplicitControllers(virDomainDefPtr def); char *virDomainDefFormat(virDomainDefPtr def, @@ -1500,7 +1508,6 @@ VIR_ENUM_DECL(virDomainChrTcpProtocol) VIR_ENUM_DECL(virDomainChrSpicevmc) VIR_ENUM_DECL(virDomainSoundModel) VIR_ENUM_DECL(virDomainMemballoonModel) -VIR_ENUM_DECL(virDomainSysinfo) VIR_ENUM_DECL(virDomainSmbiosMode) VIR_ENUM_DECL(virDomainWatchdogModel) VIR_ENUM_DECL(virDomainWatchdogAction) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 4cb8dda..c1bac23 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -224,6 +224,7 @@ virDomainControllerTypeToString; virDomainCpuSetFormat; virDomainCpuSetParse; virDomainDefAddImplicitControllers; +virDomainDefCheckABIStability; virDomainDefClearDeviceAliases; virDomainDefClearPCIAddresses; virDomainDefFormat; diff --git a/src/util/sysinfo.c b/src/util/sysinfo.c index d929073..70da532 100644 --- a/src/util/sysinfo.c +++ b/src/util/sysinfo.c @@ -33,7 +33,6 @@ #include "virterror_internal.h" #include "sysinfo.h" #include "util.h" -#include "conf/domain_conf.h" #include "logging.h" #include "memory.h" #include "command.h" @@ -46,6 +45,9 @@ #define SYSINFO_SMBIOS_DECODER "dmidecode" +VIR_ENUM_IMPL(virSysinfo, VIR_SYSINFO_LAST, + "smbios"); + /** * virSysinfoDefFree: * @def: a sysinfo structure @@ -131,7 +133,7 @@ virSysinfoRead(void) { if (VIR_ALLOC(ret) < 0) goto no_memory; - ret->type = VIR_DOMAIN_SYSINFO_SMBIOS; + ret->type = VIR_SYSINFO_SMBIOS; base = outbuf; @@ -230,7 +232,7 @@ no_memory: char * virSysinfoFormat(virSysinfoDefPtr def, const char *prefix) { - const char *type = virDomainSysinfoTypeToString(def->type); + const char *type = virSysinfoTypeToString(def->type); virBuffer buf = VIR_BUFFER_INITIALIZER; size_t len = strlen(prefix); @@ -326,4 +328,56 @@ virSysinfoFormat(virSysinfoDefPtr def, const char *prefix) return virBufferContentAndReset(&buf); } +bool virSysinfoIsEqual(virSysinfoDefPtr src, + virSysinfoDefPtr dst) +{ + bool identical = false; + + if (!src && !dst) + return true; + + if ((src && !dst) || (!src && dst)) { + virSmbiosReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Target sysinfo does not match source")); + goto cleanup; + } + + if (src->type != dst->type) { + virSmbiosReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target sysinfo %s does not match source %s"), + virSysinfoTypeToString(dst->type), + virSysinfoTypeToString(src->type)); + goto cleanup; + } + +#define CHECK_FIELD(name, desc) \ + do { \ + if (STRNEQ_NULLABLE(src->name, dst->name)) { \ + virSmbiosReportError(VIR_ERR_CONFIG_UNSUPPORTED, \ + _("Target sysinfo " desc " %s does not match source %s"), \ + src->name, dst->name); \ + } \ + } while (0) + + CHECK_FIELD(bios_vendor, "BIOS vendor"); + CHECK_FIELD(bios_version, "BIOS version"); + CHECK_FIELD(bios_date, "BIOS date"); + CHECK_FIELD(bios_release, "BIOS release"); + + CHECK_FIELD(system_manufacturer, "system vendor"); + CHECK_FIELD(system_product, "system product"); + CHECK_FIELD(system_version, "system version"); + CHECK_FIELD(system_serial, "system serial"); + CHECK_FIELD(system_uuid, "system uuid"); + CHECK_FIELD(system_sku, "system sku"); + CHECK_FIELD(system_family, "system family"); + +#undef CHECK_FIELD + + identical = true; + +cleanup: + return identical; +} + #endif /* !WIN32 */ diff --git a/src/util/sysinfo.h b/src/util/sysinfo.h index 66a59db..f69b76c 100644 --- a/src/util/sysinfo.h +++ b/src/util/sysinfo.h @@ -27,10 +27,10 @@ # include "internal.h" # include "util.h" -enum virDomainSysinfoType { - VIR_DOMAIN_SYSINFO_SMBIOS, +enum virSysinfoType { + VIR_SYSINFO_SMBIOS, - VIR_DOMAIN_SYSINFO_LAST + VIR_SYSINFO_LAST }; typedef struct _virSysinfoDef virSysinfoDef; @@ -59,4 +59,9 @@ void virSysinfoDefFree(virSysinfoDefPtr def); char *virSysinfoFormat(virSysinfoDefPtr def, const char *prefix) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); +bool virSysinfoIsEqual(virSysinfoDefPtr src, + virSysinfoDefPtr dst); + +VIR_ENUM_DECL(virSysinfo) + #endif /* __VIR_SYSINFOS_H__ */ -- 1.7.4.4

2011/5/27 Daniel P. Berrange <berrange@redhat.com>:
To allow a client app to pass in custom XML during migration of a guest it is neccessary to ensure the guest ABI remains unchanged. The virDomainDefCheckABIStablity method accepts two virDomainDefPtr structs and compares everything in them that could impact the guest machine ABI
* src/conf/domain_conf.c, src/conf/domain_conf.h, src/libvirt_private.syms: Add virDomainDefCheckABIStablity * src/conf/cpu_conf.c, src/conf/cpu_conf.h: Add virCPUDefIsEqual * src/util/sysinfo.c, src/util/sysinfo.h: Add virSysinfoIsEqual --- src/conf/cpu_conf.c | 91 +++++ src/conf/cpu_conf.h | 9 +- src/conf/domain_conf.c | 881 +++++++++++++++++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 11 +- src/libvirt_private.syms | 1 + src/util/sysinfo.c | 60 +++- src/util/sysinfo.h | 11 +- 7 files changed, 1051 insertions(+), 13 deletions(-)
diff --git a/src/conf/cpu_conf.c b/src/conf/cpu_conf.c index 98d598a..77d0976 100644 --- a/src/conf/cpu_conf.c +++ b/src/conf/cpu_conf.c
@@ -446,3 +449,91 @@ no_memory: virReportOOMError(); return -1; } + +bool +virCPUDefIsEqual(virCPUDefPtr src, + virCPUDefPtr dst) +{ + bool identical = false; + int i;
+ for (i = 0 ; i < src->nfeatures ; i++) { + if (STRNEQ(src->features[i].name, dst->features[i].name)) { + virCPUReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target CPU feature %s does not match source %s"), + dst->features[i].name, src->features[i].name); + goto cleanup; + }
I think you need to compare features[i].policy here too.
+ } + + identical = true; + +cleanup: + return identical; +}
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 8ff155b..a9a4655 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c
+static bool virDomainNetDefCheckABIStability(virDomainNetDefPtr src, + virDomainNetDefPtr dst) +{ + bool identical = false; + + if (memcmp(src->mac, dst->mac, VIR_MAC_BUFLEN) != 0) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target network card mac %02x:%02x:%02x:%02x:%02x:%02x" + "does not match source %02x:%02x:%02x:%02x:%02x:%02x"), + dst->mac[0], dst->mac[2], dst->mac[2],
Shouldn't this be dst->mac[0], dst->mac[1], dst->mac[2]?
+ dst->mac[3], dst->mac[4], dst->mac[5], + src->mac[0], src->mac[2], src->mac[2],
Copy-n-paste error here.
diff --git a/src/util/sysinfo.c b/src/util/sysinfo.c index d929073..70da532 100644 --- a/src/util/sysinfo.c +++ b/src/util/sysinfo.c
@@ -326,4 +328,56 @@ virSysinfoFormat(virSysinfoDefPtr def, const char *prefix) return virBufferContentAndReset(&buf); }
+bool virSysinfoIsEqual(virSysinfoDefPtr src, + virSysinfoDefPtr dst) +{ + bool identical = false; + + if (!src && !dst) + return true; + + if ((src && !dst) || (!src && dst)) { + virSmbiosReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Target sysinfo does not match source")); + goto cleanup; + } + + if (src->type != dst->type) { + virSmbiosReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target sysinfo %s does not match source %s"), + virSysinfoTypeToString(dst->type), + virSysinfoTypeToString(src->type)); + goto cleanup; + } + +#define CHECK_FIELD(name, desc) \
This needs to be '# define CHECK_FIELD(name, desc)' to please syntax-check.
+ do { \ + if (STRNEQ_NULLABLE(src->name, dst->name)) { \ + virSmbiosReportError(VIR_ERR_CONFIG_UNSUPPORTED, \ + _("Target sysinfo " desc " %s does not match source %s"), \
Are you sure that this insertion of dest into the string in this way will work properly with gettext? I think _("Target sysinfo %s %s does not match source %s"), desc will work better.
+ src->name, dst->name); \
You used STRNEQ_NULLABLE as src->name, dst->name could be NULL, so you need to use NULLSTR(src->name), NULLSTR(dst->name) here too.
+ } \ + } while (0) + + CHECK_FIELD(bios_vendor, "BIOS vendor"); + CHECK_FIELD(bios_version, "BIOS version"); + CHECK_FIELD(bios_date, "BIOS date"); + CHECK_FIELD(bios_release, "BIOS release"); + + CHECK_FIELD(system_manufacturer, "system vendor"); + CHECK_FIELD(system_product, "system product"); + CHECK_FIELD(system_version, "system version"); + CHECK_FIELD(system_serial, "system serial"); + CHECK_FIELD(system_uuid, "system uuid"); + CHECK_FIELD(system_sku, "system sku"); + CHECK_FIELD(system_family, "system family"); + +#undef CHECK_FIELD
s/#undef CHECK_FIELD/# undef CHECK_FIELD/
+ + identical = true; + +cleanup: + return identical; +} + #endif /* !WIN32 */
ACK with those nits fixed. Matthias

On Tue, May 31, 2011 at 11:52:20AM +0200, Matthias Bolte wrote:
2011/5/27 Daniel P. Berrange <berrange@redhat.com>:
To allow a client app to pass in custom XML during migration of a guest it is neccessary to ensure the guest ABI remains unchanged. The virDomainDefCheckABIStablity method accepts two virDomainDefPtr structs and compares everything in them that could impact the guest machine ABI
* src/conf/domain_conf.c, src/conf/domain_conf.h, src/libvirt_private.syms: Add virDomainDefCheckABIStablity * src/conf/cpu_conf.c, src/conf/cpu_conf.h: Add virCPUDefIsEqual * src/util/sysinfo.c, src/util/sysinfo.h: Add virSysinfoIsEqual --- src/conf/cpu_conf.c | 91 +++++ src/conf/cpu_conf.h | 9 +- src/conf/domain_conf.c | 881 +++++++++++++++++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 11 +- src/libvirt_private.syms | 1 + src/util/sysinfo.c | 60 +++- src/util/sysinfo.h | 11 +- 7 files changed, 1051 insertions(+), 13 deletions(-)
diff --git a/src/conf/cpu_conf.c b/src/conf/cpu_conf.c index 98d598a..77d0976 100644 --- a/src/conf/cpu_conf.c +++ b/src/conf/cpu_conf.c
@@ -446,3 +449,91 @@ no_memory: virReportOOMError(); return -1; } + +bool +virCPUDefIsEqual(virCPUDefPtr src, + virCPUDefPtr dst) +{ + bool identical = false; + int i;
+ for (i = 0 ; i < src->nfeatures ; i++) { + if (STRNEQ(src->features[i].name, dst->features[i].name)) { + virCPUReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target CPU feature %s does not match source %s"), + dst->features[i].name, src->features[i].name); + goto cleanup; + }
I think you need to compare features[i].policy here too.
+ } + + identical = true; + +cleanup: + return identical; +}
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 8ff155b..a9a4655 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c
+static bool virDomainNetDefCheckABIStability(virDomainNetDefPtr src, + virDomainNetDefPtr dst) +{ + bool identical = false; + + if (memcmp(src->mac, dst->mac, VIR_MAC_BUFLEN) != 0) { + virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target network card mac %02x:%02x:%02x:%02x:%02x:%02x" + "does not match source %02x:%02x:%02x:%02x:%02x:%02x"), + dst->mac[0], dst->mac[2], dst->mac[2],
Shouldn't this be dst->mac[0], dst->mac[1], dst->mac[2]?
+ dst->mac[3], dst->mac[4], dst->mac[5], + src->mac[0], src->mac[2], src->mac[2],
Copy-n-paste error here.
diff --git a/src/util/sysinfo.c b/src/util/sysinfo.c index d929073..70da532 100644 --- a/src/util/sysinfo.c +++ b/src/util/sysinfo.c
@@ -326,4 +328,56 @@ virSysinfoFormat(virSysinfoDefPtr def, const char *prefix) return virBufferContentAndReset(&buf); }
+bool virSysinfoIsEqual(virSysinfoDefPtr src, + virSysinfoDefPtr dst) +{ + bool identical = false; + + if (!src && !dst) + return true; + + if ((src && !dst) || (!src && dst)) { + virSmbiosReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Target sysinfo does not match source")); + goto cleanup; + } + + if (src->type != dst->type) { + virSmbiosReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target sysinfo %s does not match source %s"), + virSysinfoTypeToString(dst->type), + virSysinfoTypeToString(src->type)); + goto cleanup; + } + +#define CHECK_FIELD(name, desc) \
This needs to be '# define CHECK_FIELD(name, desc)' to please syntax-check.
+ do { \ + if (STRNEQ_NULLABLE(src->name, dst->name)) { \ + virSmbiosReportError(VIR_ERR_CONFIG_UNSUPPORTED, \ + _("Target sysinfo " desc " %s does not match source %s"), \
Are you sure that this insertion of dest into the string in this way will work properly with gettext? I think
_("Target sysinfo %s %s does not match source %s"), desc
will work better.
+ src->name, dst->name); \
You used STRNEQ_NULLABLE as src->name, dst->name could be NULL, so you need to use
NULLSTR(src->name), NULLSTR(dst->name)
here too.
+ } \ + } while (0) + + CHECK_FIELD(bios_vendor, "BIOS vendor"); + CHECK_FIELD(bios_version, "BIOS version"); + CHECK_FIELD(bios_date, "BIOS date"); + CHECK_FIELD(bios_release, "BIOS release"); + + CHECK_FIELD(system_manufacturer, "system vendor"); + CHECK_FIELD(system_product, "system product"); + CHECK_FIELD(system_version, "system version"); + CHECK_FIELD(system_serial, "system serial"); + CHECK_FIELD(system_uuid, "system uuid"); + CHECK_FIELD(system_sku, "system sku"); + CHECK_FIELD(system_family, "system family"); + +#undef CHECK_FIELD
s/#undef CHECK_FIELD/# undef CHECK_FIELD/
+ + identical = true; + +cleanup: + return identical; +} + #endif /* !WIN32 */
ACK with those nits fixed.
Thanks, I've pushed this series with those fixes 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 :|

Switch virsh migrate over to use virDomainMigrate2 and virDomainMigrateToURI2. This is still compatible with older libvirts, because these methods dynamically choose whether to perform v1, v2 or v3 migration based on declared RPC support from the libvirtd instances Add a --xml arg which allows the user to pass in a custom XML document. This XML document must be ABI compatible with the current *live* XML document for the running guest on the source host. ABI compatibility will be enforced by any driver supporting this function * tools/virsh.c: Add '--xml' arg to migrate command --- tools/virsh.c | 19 +++++++++++++++++-- 1 files changed, 17 insertions(+), 2 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index b43c167..fd32309 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -3786,6 +3786,7 @@ static const vshCmdOptDef opts_migrate[] = { {"migrateuri", VSH_OT_DATA, 0, N_("migration URI, usually can be omitted")}, {"dname", VSH_OT_DATA, 0, N_("rename to new name during migration (if supported)")}, {"timeout", VSH_OT_INT, 0, N_("force guest to suspend if live migration exceeds timeout (in seconds)")}, + {"xml", VSH_OT_STRING, 0, N_("filename containing updated XML for the target")}, {NULL, 0, 0, NULL} }; @@ -3809,6 +3810,8 @@ doMigrate (void *opaque) const vshCmd *cmd = data->cmd; #if HAVE_PTHREAD_SIGMASK sigset_t sigmask, oldsigmask; + const char *xmlfile = NULL; + char *xml = NULL; sigemptyset(&sigmask); sigaddset(&sigmask, SIGINT); @@ -3829,6 +3832,11 @@ doMigrate (void *opaque) goto out; } + if (vshCommandOptString(cmd, "xml", &xmlfile) < 0) { + vshError(ctl, "%s", _("malformed xml argument")); + goto out; + } + if (vshCommandOptBool (cmd, "live")) flags |= VIR_MIGRATE_LIVE; if (vshCommandOptBool (cmd, "p2p")) @@ -3850,6 +3858,12 @@ doMigrate (void *opaque) if (vshCommandOptBool (cmd, "copy-storage-inc")) flags |= VIR_MIGRATE_NON_SHARED_INC; + + if (xmlfile && + virFileReadAll(xmlfile, 8192, &xml) < 0) + goto out; + + if ((flags & VIR_MIGRATE_PEER2PEER) || vshCommandOptBool (cmd, "direct")) { /* For peer2peer migration or direct migration we only expect one URI @@ -3860,7 +3874,7 @@ doMigrate (void *opaque) goto out; } - if (virDomainMigrateToURI (dom, desturi, flags, dname, 0) == 0) + if (virDomainMigrateToURI2(dom, desturi, NULL, xml, flags, dname, 0) == 0) ret = '0'; } else { /* For traditional live migration, connect to the destination host directly. */ @@ -3870,7 +3884,7 @@ doMigrate (void *opaque) dconn = virConnectOpenAuth (desturi, virConnectAuthPtrDefault, 0); if (!dconn) goto out; - ddom = virDomainMigrate (dom, dconn, flags, dname, migrateuri, 0); + ddom = virDomainMigrate2(dom, dconn, xml, flags, dname, migrateuri, 0); if (ddom) { virDomainFree(ddom); ret = '0'; @@ -3884,6 +3898,7 @@ out: out_sig: #endif if (dom) virDomainFree (dom); + VIR_FREE(xml); ignore_value(safewrite(data->writefd, &ret, sizeof(ret))); } -- 1.7.4.4

2011/5/27 Daniel P. Berrange <berrange@redhat.com>:
Switch virsh migrate over to use virDomainMigrate2 and virDomainMigrateToURI2. This is still compatible with older libvirts, because these methods dynamically choose whether to perform v1, v2 or v3 migration based on declared RPC support from the libvirtd instances
Add a --xml arg which allows the user to pass in a custom XML document. This XML document must be ABI compatible with the current *live* XML document for the running guest on the source host. ABI compatibility will be enforced by any driver supporting this function
* tools/virsh.c: Add '--xml' arg to migrate command --- tools/virsh.c | 19 +++++++++++++++++-- 1 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c index b43c167..fd32309 100644 --- a/tools/virsh.c +++ b/tools/virsh.c
@@ -3850,6 +3858,12 @@ doMigrate (void *opaque) if (vshCommandOptBool (cmd, "copy-storage-inc")) flags |= VIR_MIGRATE_NON_SHARED_INC;
+ + if (xmlfile && + virFileReadAll(xmlfile, 8192, &xml) < 0) + goto out; + +
Additional empty lines, could be removed here. ACK. Matthias

Update the qemuDomainMigrateBegin method so that it accepts an optional incoming XML document. This will be validated for ABI compatibility against the current domain config, and if this check passes, will be passed back out for use by the qemuDomainMigratePrepare method on the target * src/qemu/qemu_domain.c, src/qemu/qemu_domain.h, src/qemu/qemu_migration.c: Allow custom XML to be passed --- src/qemu/qemu_domain.c | 26 ++++++++++++++++++-------- src/qemu/qemu_domain.h | 4 ++++ src/qemu/qemu_migration.c | 27 ++++++++++++++++++--------- 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index bcacb18..ddf9e65 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -686,19 +686,14 @@ void qemuDomainObjExitRemoteWithDriver(struct qemud_driver *driver, } -char *qemuDomainFormatXML(struct qemud_driver *driver, - virDomainObjPtr vm, - int flags) +char *qemuDomainDefFormatXML(struct qemud_driver *driver, + virDomainDefPtr def, + int flags) { char *ret = NULL; virCPUDefPtr cpu = NULL; - virDomainDefPtr def; virCPUDefPtr def_cpu; - if ((flags & VIR_DOMAIN_XML_INACTIVE) && vm->newDef) - def = vm->newDef; - else - def = vm->def; def_cpu = def->cpu; /* Update guest CPU requirements according to host CPU */ @@ -723,6 +718,21 @@ cleanup: return ret; } +char *qemuDomainFormatXML(struct qemud_driver *driver, + virDomainObjPtr vm, + int flags) +{ + virDomainDefPtr def; + + if ((flags & VIR_DOMAIN_XML_INACTIVE) && vm->newDef) + def = vm->newDef; + else + def = vm->def; + + return qemuDomainDefFormatXML(driver, def, flags); +} + + void qemuDomainObjTaint(struct qemud_driver *driver, virDomainObjPtr obj, enum virDomainTaintFlags taint, diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 6d24f53..ac60b7e 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -111,6 +111,10 @@ void qemuDomainObjEnterRemoteWithDriver(struct qemud_driver *driver, void qemuDomainObjExitRemoteWithDriver(struct qemud_driver *driver, virDomainObjPtr obj); +char *qemuDomainDefFormatXML(struct qemud_driver *driver, + virDomainDefPtr vm, + int flags); + char *qemuDomainFormatXML(struct qemud_driver *driver, virDomainObjPtr vm, int flags); diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 2f0ed28..8ebbda1 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -875,15 +875,10 @@ char *qemuMigrationBegin(struct qemud_driver *driver, { char *rv = NULL; qemuMigrationCookiePtr mig = NULL; + virDomainDefPtr def = NULL; VIR_DEBUG("driver=%p, vm=%p, xmlin=%s, cookieout=%p, cookieoutlen=%p", driver, vm, NULLSTR(xmlin), cookieout, cookieoutlen); - if (xmlin) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("Passing XML for the target VM is not yet supported")); - goto cleanup; - } - if (!virDomainObjIsActive(vm)) { qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", _("domain is not running")); @@ -901,13 +896,27 @@ char *qemuMigrationBegin(struct qemud_driver *driver, 0) < 0) goto cleanup; - rv = qemuDomainFormatXML(driver, vm, - VIR_DOMAIN_XML_SECURE | - VIR_DOMAIN_XML_UPDATE_CPU); + if (xmlin) { + if (!(def = virDomainDefParseString(driver->caps, xmlin, + VIR_DOMAIN_XML_INACTIVE))) + goto cleanup; + + if (!virDomainDefCheckABIStability(def, vm->def)) + goto cleanup; + + rv = qemuDomainDefFormatXML(driver, def, + VIR_DOMAIN_XML_SECURE | + VIR_DOMAIN_XML_UPDATE_CPU); + } else { + rv = qemuDomainFormatXML(driver, vm, + VIR_DOMAIN_XML_SECURE | + VIR_DOMAIN_XML_UPDATE_CPU); + } cleanup: virDomainObjUnlock(vm); qemuMigrationCookieFree(mig); + virDomainDefFree(def); return rv; } -- 1.7.4.4

2011/5/27 Daniel P. Berrange <berrange@redhat.com>:
Update the qemuDomainMigrateBegin method so that it accepts an optional incoming XML document. This will be validated for ABI compatibility against the current domain config, and if this check passes, will be passed back out for use by the qemuDomainMigratePrepare method on the target
* src/qemu/qemu_domain.c, src/qemu/qemu_domain.h, src/qemu/qemu_migration.c: Allow custom XML to be passed --- src/qemu/qemu_domain.c | 26 ++++++++++++++++++-------- src/qemu/qemu_domain.h | 4 ++++ src/qemu/qemu_migration.c | 27 ++++++++++++++++++--------- 3 files changed, 40 insertions(+), 17 deletions(-)
ACK. Matthias

The virDomainHostdevDef struct contains a 'char *target' field. This is set to 'NULL' when parsing XML and never used / set anywhere else. Clearly it is bogus & unused * src/conf/domain_conf.c, src/conf/domain_conf.h: Remove target from virDomainHostdevDef --- src/conf/domain_conf.c | 2 -- src/conf/domain_conf.h | 1 - 2 files changed, 0 insertions(+), 3 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index a9a4655..5412b7a 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -890,7 +890,6 @@ void virDomainHostdevDefFree(virDomainHostdevDefPtr def) if (!def) return; - VIR_FREE(def->target); virDomainDeviceInfoClear(&def->info); VIR_FREE(def); } @@ -4771,7 +4770,6 @@ virDomainHostdevDefParseXML(const xmlNodePtr node, virReportOOMError(); return NULL; } - def->target = NULL; mode = virXMLPropString(node, "mode"); if (mode) { diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 47d17dd..181226d 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -792,7 +792,6 @@ struct _virDomainHostdevDef { int dummy; } caps; } source; - char* target; int bootIndex; virDomainDeviceInfo info; /* Guest address */ }; -- 1.7.4.4

On Fri, May 27, 2011 at 01:09:06PM +0100, Daniel P. Berrange wrote:
This patch series updates the QEMU driver to allow it to take advantage of the migration v3 protocol support for changing guest XML during migration.
eg
virsh migrate --xml migtest.xml migtest qemu+ssh://somehost/system
NB the XML is strictly validated for guest ABI compatibility so only things like host NIC device names or host disk file paths can be changed
This series caused a build failure on Win32 due to some dodgy #ifdef conditionals, so I pushed this followup commit ef983dfe5ab9c8b126d435f3f3a46bdd6f8f21b6 Author: Daniel P. Berrange <berrange@redhat.com> Date: Tue May 31 14:12:33 2011 +0100 Fix sysinfo/virsh build problems on Win32 The virSysinfoIsEqual method was mistakenly inside a #ifndef WIN32 conditional. The existing virSysinfoFormat is also stubbed out on Win32, even though the code works without any trouble. This breaks XML output on Win32, so the stub is removed. virsh migrate mistakenly had some variables inside the conditional * src/util/sysinfo.c: Build virSysinfoIsEqual on Win32 and remove Win32 stub for virSysinfoFormat * tools/virsh.c: Fix variable declaration on Win32 diff --git a/src/util/sysinfo.c b/src/util/sysinfo.c index ff07151..40ec2e3 100644 --- a/src/util/sysinfo.c +++ b/src/util/sysinfo.c @@ -94,15 +94,6 @@ virSysinfoRead(void) { return NULL; } -char * -virSysinfoFormat(virSysinfoDefPtr def ATTRIBUTE_UNUSED, - const char *prefix ATTRIBUTE_UNUSED) -{ - virReportSystemError(ENOSYS, "%s", - _("Host sysinfo extraction not supported on this platform")); - return NULL; -} - #else /* !WIN32 */ virSysinfoDefPtr @@ -220,6 +211,7 @@ no_memory: ret = NULL; goto cleanup; } +#endif /* !WIN32 */ /** * virSysinfoFormat: @@ -350,7 +342,7 @@ bool virSysinfoIsEqual(virSysinfoDefPtr src, goto cleanup; } -# define CHECK_FIELD(name, desc) \ +#define CHECK_FIELD(name, desc) \ do { \ if (STRNEQ_NULLABLE(src->name, dst->name)) { \ virSmbiosReportError(VIR_ERR_CONFIG_UNSUPPORTED, \ @@ -372,12 +364,10 @@ bool virSysinfoIsEqual(virSysinfoDefPtr src, CHECK_FIELD(system_sku, "system sku"); CHECK_FIELD(system_family, "system family"); -# undef CHECK_FIELD +#undef CHECK_FIELD identical = true; cleanup: return identical; } - -#endif /* !WIN32 */ diff --git a/tools/virsh.c b/tools/virsh.c index 520d16e..dfd5bd2 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -3850,10 +3850,10 @@ doMigrate (void *opaque) vshCtrlData *data = opaque; vshControl *ctl = data->ctl; const vshCmd *cmd = data->cmd; -#if HAVE_PTHREAD_SIGMASK - sigset_t sigmask, oldsigmask; const char *xmlfile = NULL; char *xml = NULL; +#if HAVE_PTHREAD_SIGMASK + sigset_t sigmask, oldsigmask; sigemptyset(&sigmask); sigaddset(&sigmask, SIGINT); 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 :|
participants (2)
-
Daniel P. Berrange
-
Matthias Bolte