From 08106e20448429f1e5e34e8ef7c9047eb13b9261 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Fri, 27 May 2011 10:47:30 +0100 Subject: [PATCH] Add an API for comparing the ABI of two guest configurations 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 | 99 +++++ 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, 1059 insertions(+), 13 deletions(-) diff --git a/src/conf/cpu_conf.c b/src/conf/cpu_conf.c index 98d598a979..14e93db977 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,99 @@ 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; + } + + if (src->features[i].policy != dst->features[i].policy) { + virCPUReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target CPU feature policy %s does not match source %s"), + virCPUFeaturePolicyTypeToString(dst->features[i].policy), + virCPUFeaturePolicyTypeToString(src->features[i].policy)); + goto cleanup; + } + } + + identical = true; + +cleanup: + return identical; +} diff --git a/src/conf/cpu_conf.h b/src/conf/cpu_conf.h index 055887c909..ecd4e10849 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 1632dc0859..77ab1e5343 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", @@ -4363,7 +4360,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; @@ -6564,6 +6561,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[1], dst->mac[2], + dst->mac[3], dst->mac[4], dst->mac[5], + src->mac[0], src->mac[1], 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 8ea1ca4de6..4d21cdb69e 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1096,7 +1096,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 { @@ -1342,6 +1347,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, @@ -1503,7 +1511,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 321df2a024..a46cac451b 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 d9290732c5..ff07151f38 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 %s %s does not match source %s"), \ + desc, NULLSTR(src->name), NULLSTR(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 66a59db70b..f69b76c11a 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__ */