src/conf/domain_capabilities.c
src/conf/domain_conf.c
src/conf/domain_event.c
+src/conf/domain_postparse.c
src/conf/domain_validate.c
src/conf/interface_conf.c
src/conf/netdev_bandwidth_conf.c
#include "datatypes.h"
#include "domain_addr.h"
#include "domain_conf.h"
+#include "domain_postparse.h"
#include "domain_validate.h"
#include "viralloc.h"
#include "virxml.h"
}
-/**
- * virDomainDefCheckUnsupportedMemoryHotplug:
- * @def: domain definition
- *
- * Returns -1 if the domain definition would enable memory hotplug via the
- * <maxMemory> tunable and reports an error. Otherwise returns 0.
- */
-static int
-virDomainDefCheckUnsupportedMemoryHotplug(virDomainDef *def)
-{
- /* memory hotplug tunables are not supported by this driver */
- if (virDomainDefHasMemoryHotplug(def)) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("memory hotplug tunables <maxMemory> are not "
- "supported by this hypervisor driver"));
- return -1;
- }
-
- return 0;
-}
-
-
-/**
- * virDomainDeviceDefCheckUnsupportedMemoryDevice:
- * @dev: device definition
- *
- * Returns -1 if the device definition describes a memory device and reports an
- * error. Otherwise returns 0.
- */
-static int
-virDomainDeviceDefCheckUnsupportedMemoryDevice(virDomainDeviceDef *dev)
-{
- /* This driver doesn't yet know how to handle memory devices */
- if (dev->type == VIR_DOMAIN_DEVICE_MEMORY) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("memory devices are not supported by this driver"));
- return -1;
- }
-
- return 0;
-}
-
-
bool virDomainObjTaint(virDomainObj *obj,
virDomainTaintFlags taint)
{
}
-static int
-virDomainDefRejectDuplicateControllers(virDomainDef *def)
-{
- int max_idx[VIR_DOMAIN_CONTROLLER_TYPE_LAST];
- virBitmap *bitmaps[VIR_DOMAIN_CONTROLLER_TYPE_LAST] = { NULL };
- virDomainControllerDef *cont;
- size_t nbitmaps = 0;
- int ret = -1;
- size_t i;
-
- memset(max_idx, -1, sizeof(max_idx));
-
- for (i = 0; i < def->ncontrollers; i++) {
- cont = def->controllers[i];
- if (cont->idx > max_idx[cont->type])
- max_idx[cont->type] = cont->idx;
- }
-
- /* multiple USB controllers with the same index are allowed */
- max_idx[VIR_DOMAIN_CONTROLLER_TYPE_USB] = -1;
-
- for (i = 0; i < VIR_DOMAIN_CONTROLLER_TYPE_LAST; i++) {
- if (max_idx[i] >= 0)
- bitmaps[i] = virBitmapNew(max_idx[i] + 1);
- nbitmaps++;
- }
-
- for (i = 0; i < def->ncontrollers; i++) {
- cont = def->controllers[i];
-
- if (max_idx[cont->type] == -1)
- continue;
-
- if (virBitmapIsBitSet(bitmaps[cont->type], cont->idx)) {
- virReportError(VIR_ERR_XML_ERROR,
- _("Multiple '%s' controllers with index '%d'"),
- virDomainControllerTypeToString(cont->type),
- cont->idx);
- goto cleanup;
- }
- ignore_value(virBitmapSetBit(bitmaps[cont->type], cont->idx));
- }
-
- ret = 0;
- cleanup:
- for (i = 0; i < nbitmaps; i++)
- virBitmapFree(bitmaps[i]);
- return ret;
-}
-
-static int
-virDomainDefRejectDuplicatePanics(virDomainDef *def)
-{
- bool exists[VIR_DOMAIN_PANIC_MODEL_LAST];
- size_t i;
-
- for (i = 0; i < VIR_DOMAIN_PANIC_MODEL_LAST; i++)
- exists[i] = false;
-
- for (i = 0; i < def->npanics; i++) {
- virDomainPanicModel model = def->panics[i]->model;
- if (exists[model]) {
- virReportError(VIR_ERR_XML_ERROR,
- _("Multiple panic devices with model '%s'"),
- virDomainPanicModelTypeToString(model));
- return -1;
- }
- exists[model] = true;
- }
-
- return 0;
-}
-
-
-static int
-virDomainDefPostParseMemory(virDomainDef *def,
- unsigned int parseFlags)
-{
- size_t i;
- unsigned long long numaMemory = 0;
- unsigned long long hotplugMemory = 0;
-
- /* Attempt to infer the initial memory size from the sum NUMA memory sizes
- * in case ABI updates are allowed or the <memory> element wasn't specified */
- if (def->mem.total_memory == 0 ||
- parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE ||
- parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE_MIGRATION)
- numaMemory = virDomainNumaGetMemorySize(def->numa);
-
- /* calculate the sizes of hotplug memory */
- for (i = 0; i < def->nmems; i++)
- hotplugMemory += def->mems[i]->size;
-
- if (numaMemory) {
- /* update the sizes in XML if nothing was set in the XML or ABI update
- * is supported */
- virDomainDefSetMemoryTotal(def, numaMemory + hotplugMemory);
- } else {
- /* verify that the sum of memory modules doesn't exceed the total
- * memory. This is necessary for virDomainDefGetMemoryInitial to work
- * properly. */
- if (hotplugMemory > def->mem.total_memory) {
- virReportError(VIR_ERR_XML_ERROR, "%s",
- _("Total size of memory devices exceeds the total "
- "memory size"));
- return -1;
- }
- }
-
- if (virDomainDefGetMemoryInitial(def) == 0) {
- virReportError(VIR_ERR_XML_ERROR, "%s",
- _("Memory size must be specified via <memory> or in the "
- "<numa> configuration"));
- return -1;
- }
-
- if (def->mem.cur_balloon > virDomainDefGetMemoryTotal(def) ||
- def->mem.cur_balloon == 0)
- def->mem.cur_balloon = virDomainDefGetMemoryTotal(def);
-
- if ((def->mem.max_memory || def->mem.memory_slots) &&
- !(def->mem.max_memory && def->mem.memory_slots)) {
- virReportError(VIR_ERR_XML_ERROR, "%s",
- _("both maximum memory size and "
- "memory slot count must be specified"));
- return -1;
- }
-
- if (def->mem.max_memory &&
- def->mem.max_memory < virDomainDefGetMemoryTotal(def)) {
- virReportError(VIR_ERR_XML_ERROR, "%s",
- _("maximum memory size must be equal or greater than "
- "the actual memory size"));
- return -1;
- }
-
- return 0;
-}
-
-
-static int
-virDomainDefPostParseOs(virDomainDef *def)
-{
- if (def->os.firmwareFeatures &&
- def->os.firmwareFeatures[VIR_DOMAIN_OS_DEF_FIRMWARE_FEATURE_ENROLLED_KEYS] == VIR_TRISTATE_BOOL_YES) {
-
- if (def->os.firmwareFeatures[VIR_DOMAIN_OS_DEF_FIRMWARE_FEATURE_SECURE_BOOT] == VIR_TRISTATE_BOOL_NO) {
- virReportError(VIR_ERR_XML_DETAIL, "%s",
- _("firmware feature 'enrolled-keys' cannot be enabled when "
- "firmware feature 'secure-boot' is disabled"));
- return -1;
- }
-
- /* For all non-broken firmware builds, enrolled-keys implies
- * secure-boot, and having the Secure Boot keys in the NVRAM file
- * when the firmware doesn't support the Secure Boot feature doesn't
- * make sense anyway. Reflect this fact explicitly in the XML */
- def->os.firmwareFeatures[VIR_DOMAIN_OS_DEF_FIRMWARE_FEATURE_SECURE_BOOT] = VIR_TRISTATE_BOOL_YES;
- }
-
- if (!def->os.loader)
- return 0;
-
- if (def->os.loader->path &&
- def->os.loader->type == VIR_DOMAIN_LOADER_TYPE_NONE) {
- /* By default, loader is type of 'rom' */
- def->os.loader->type = VIR_DOMAIN_LOADER_TYPE_ROM;
- }
-
- return 0;
-}
-
-
-static void
-virDomainDefPostParseMemtune(virDomainDef *def)
-{
- size_t i;
-
- if (virDomainNumaGetNodeCount(def->numa) == 0) {
- /* If guest NUMA is not configured and any hugepage page has nodemask
- * set to "0" free and clear that nodemas, otherwise we would rise
- * an error that there is no guest NUMA node configured. */
- for (i = 0; i < def->mem.nhugepages; i++) {
- ssize_t nextBit;
-
- if (!def->mem.hugepages[i].nodemask)
- continue;
-
- nextBit = virBitmapNextSetBit(def->mem.hugepages[i].nodemask, 0);
- if (nextBit < 0) {
- g_clear_pointer(&def->mem.hugepages[i].nodemask,
- virBitmapFree);
- }
- }
- }
-}
-
-
static int
virDomainDefAddConsoleCompat(virDomainDef *def)
{
}
-static int
-virDomainDefPostParseTimer(virDomainDef *def)
-{
- size_t i;
-
- /* verify settings of guest timers */
- for (i = 0; i < def->clock.ntimers; i++) {
- virDomainTimerDef *timer = def->clock.timers[i];
-
- if (timer->name == VIR_DOMAIN_TIMER_NAME_KVMCLOCK ||
- timer->name == VIR_DOMAIN_TIMER_NAME_HYPERVCLOCK) {
- if (timer->tickpolicy) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("timer %s doesn't support setting of "
- "timer tickpolicy"),
- virDomainTimerNameTypeToString(timer->name));
- return -1;
- }
- }
-
- if (timer->tickpolicy != VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP &&
- (timer->catchup.threshold != 0 ||
- timer->catchup.limit != 0 ||
- timer->catchup.slew != 0)) {
- virReportError(VIR_ERR_XML_ERROR, "%s",
- _("setting of timer catchup policies is only "
- "supported with tickpolicy='catchup'"));
- return -1;
- }
-
- if (timer->name != VIR_DOMAIN_TIMER_NAME_TSC) {
- if (timer->frequency != 0) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("timer %s doesn't support setting of "
- "timer frequency"),
- virDomainTimerNameTypeToString(timer->name));
- return -1;
- }
-
- if (timer->mode) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("timer %s doesn't support setting of "
- "timer mode"),
- virDomainTimerNameTypeToString(timer->name));
- return -1;
- }
- }
-
- if (timer->name != VIR_DOMAIN_TIMER_NAME_PLATFORM &&
- timer->name != VIR_DOMAIN_TIMER_NAME_RTC) {
- if (timer->track != VIR_DOMAIN_TIMER_TRACK_NONE) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("timer %s doesn't support setting of "
- "timer track"),
- virDomainTimerNameTypeToString(timer->name));
- return -1;
- }
- }
- }
-
- return 0;
-}
-
-
-static void
-virDomainDefPostParseGraphics(virDomainDef *def)
-{
- size_t i;
-
- for (i = 0; i < def->ngraphics; i++) {
- virDomainGraphicsDef *graphics = def->graphics[i];
-
- /* If spice graphics is configured without ports and with autoport='no'
- * then we start qemu with Spice to not listen anywhere. Let's convert
- * this configuration to the new listen type='none' which does the
- * same. */
- if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) {
- virDomainGraphicsListenDef *glisten = &graphics->listens[0];
-
- if (glisten->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS &&
- graphics->data.spice.port == 0 &&
- graphics->data.spice.tlsPort == 0 &&
- !graphics->data.spice.autoport) {
- VIR_FREE(glisten->address);
- glisten->type = VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE;
- }
- }
- }
-}
-
/**
* virDomainDriveAddressIsUsedByDisk:
}
-/* Find out the next usable "unit" of a specific controller */
-static int
-virDomainControllerSCSINextUnit(const virDomainDef *def,
- unsigned int controller)
-{
- size_t i;
-
- for (i = 0; i < def->scsiBusMaxUnit; i++) {
- /* Default to assigning addresses using bus = target = 0 */
- const virDomainDeviceDriveAddress addr = {controller, 0, 0, i, 0};
-
- if (!virDomainSCSIDriveAddressIsUsed(def, &addr))
- return i;
- }
-
- return -1;
-}
-
-
-static void
-virDomainHostdevAssignAddress(virDomainXMLOption *xmlopt G_GNUC_UNUSED,
- const virDomainDef *def,
- virDomainHostdevDef *hostdev)
-{
- int next_unit = 0;
- int controller = 0;
-
- /* NB: Do not attempt calling virDomainDefMaybeAddController to
- * automagically add a "new" controller. Doing so will result in
- * qemuDomainFindOrCreateSCSIDiskController "finding" the controller
- * in the domain def list and thus not hotplugging the controller as
- * well as the hostdev in the event that there are either no SCSI
- * controllers defined or there was no space on an existing one.
- *
- * Because we cannot add a controller, then we should not walk the
- * defined controllers list in order to find empty space. Doing
- * so fails to return the valid next unit number for the 2nd
- * hostdev being added to the as yet to be created controller.
- */
- do {
- next_unit = virDomainControllerSCSINextUnit(def, controller);
- if (next_unit < 0)
- controller++;
- } while (next_unit < 0);
-
-
- hostdev->info->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE;
- hostdev->info->addr.drive.controller = controller;
- hostdev->info->addr.drive.bus = 0;
- hostdev->info->addr.drive.target = 0;
- hostdev->info->addr.drive.unit = next_unit;
-}
-
-
-/**
- * virDomainPostParseCheckISCSIPath
- * @srcpath: Source path read (a/k/a, IQN) either disk or hostdev
- *
- * The details of an IQN is defined by RFC 3720 and 3721, but
- * we just need to make sure there's a lun provided. If not
- * provided, then default to zero. For an ISCSI LUN that is
- * is provided by /dev/disk/by-path/... , then that path will
- * have the specific lun requested.
- */
-static void
-virDomainPostParseCheckISCSIPath(char **srcpath)
-{
- char *path = NULL;
-
- if (strchr(*srcpath, '/'))
- return;
-
- path = g_strdup_printf("%s/0", *srcpath);
- g_free(*srcpath);
- *srcpath = g_steal_pointer(&path);
-}
-
-
-static int
-virDomainHostdevDefPostParse(virDomainHostdevDef *dev,
- const virDomainDef *def,
- virDomainXMLOption *xmlopt)
-{
- virDomainHostdevSubsysSCSI *scsisrc;
- virDomainDeviceDriveAddress *addr = NULL;
-
- if (dev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
- return 0;
-
- switch (dev->source.subsys.type) {
- case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI:
- scsisrc = &dev->source.subsys.u.scsi;
- if (scsisrc->protocol == VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_ISCSI) {
- virDomainHostdevSubsysSCSIiSCSI *iscsisrc = &scsisrc->u.iscsi;
- virDomainPostParseCheckISCSIPath(&iscsisrc->src->path);
- }
-
- if (dev->info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
- virDomainHostdevAssignAddress(xmlopt, def, dev);
-
- /* Ensure provided address doesn't conflict with existing
- * scsi disk drive address
- */
- addr = &dev->info->addr.drive;
- if (virDomainDriveAddressIsUsedByDisk(def,
- VIR_DOMAIN_DISK_BUS_SCSI,
- addr)) {
- virReportError(VIR_ERR_XML_ERROR,
- _("SCSI host address controller='%u' "
- "bus='%u' target='%u' unit='%u' in "
- "use by a SCSI disk"),
- addr->controller, addr->bus,
- addr->target, addr->unit);
- return -1;
- }
- break;
- case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV: {
- int model = dev->source.subsys.u.mdev.model;
-
- if (dev->info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
- return 0;
-
- if ((model == VIR_MDEV_MODEL_TYPE_VFIO_PCI &&
- dev->info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) ||
- (model == VIR_MDEV_MODEL_TYPE_VFIO_CCW &&
- dev->info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW)) {
- virReportError(VIR_ERR_XML_ERROR,
- _("Unsupported address type '%s' with mediated "
- "device model '%s'"),
- virDomainDeviceAddressTypeToString(dev->info->type),
- virMediatedDeviceModelTypeToString(model));
- return -1;
- }
- }
-
- case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
- case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
- case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST:
- case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
- break;
- }
-
- return 0;
-}
-
-
-static int
-virDomainChrIsaSerialDefPostParse(virDomainDef *def)
-{
- size_t i;
- size_t isa_serial_count = 0;
- bool used_serial_port[VIR_MAX_ISA_SERIAL_PORTS] = { false };
-
- /* Perform all the required checks. */
- for (i = 0; i < def->nserials; i++) {
- if (def->serials[i]->targetType != VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_ISA_SERIAL)
- continue;
-
- if (isa_serial_count++ >= VIR_MAX_ISA_SERIAL_PORTS ||
- def->serials[i]->target.port >= VIR_MAX_ISA_SERIAL_PORTS) {
- virReportError(VIR_ERR_INTERNAL_ERROR,
- _("Maximum supported number of ISA serial ports is '%d'"),
- VIR_MAX_ISA_SERIAL_PORTS);
- return -1;
- }
-
- if (def->serials[i]->target.port != -1) {
- if (used_serial_port[def->serials[i]->target.port]) {
- virReportError(VIR_ERR_INTERNAL_ERROR,
- _("target port '%d' already allocated"),
- def->serials[i]->target.port);
- return -1;
- }
- used_serial_port[def->serials[i]->target.port] = true;
- }
- }
-
- /* Assign the ports to the devices. */
- for (i = 0; i < def->nserials; i++) {
- size_t j;
-
- if (def->serials[i]->targetType != VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_ISA_SERIAL ||
- def->serials[i]->target.port != -1)
- continue;
-
- for (j = 0; j < VIR_MAX_ISA_SERIAL_PORTS; j++) {
- if (!used_serial_port[j]) {
- def->serials[i]->target.port = j;
- used_serial_port[j] = true;
- break;
- }
- }
- }
-
- return 0;
-}
-
-
-static int
-virDomainChrDefPostParse(virDomainChrDef *chr,
- const virDomainDef *def)
-{
- const virDomainChrDef **arrPtr;
- size_t i, cnt;
-
- virDomainChrGetDomainPtrs(def, chr->deviceType, &arrPtr, &cnt);
-
- if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE &&
- chr->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_NONE) {
- chr->targetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL;
- }
-
- if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL &&
- chr->targetType == VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA_DEBUG &&
- !ARCH_IS_X86(def->os.arch)) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("isa-debug serial type only valid on x86 architecture"));
- return -1;
- }
-
- if (chr->target.port == -1 &&
- (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_PARALLEL ||
- chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL ||
- chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE)) {
- int maxport = -1;
-
- for (i = 0; i < cnt; i++) {
- if (arrPtr[i]->target.port > maxport)
- maxport = arrPtr[i]->target.port;
- }
-
- chr->target.port = maxport + 1;
- }
-
- return 0;
-}
-
-
-static void
-virDomainRNGDefPostParse(virDomainRNGDef *rng)
-{
- /* set default path for virtio-rng "random" backend to /dev/random */
- if (rng->backend == VIR_DOMAIN_RNG_BACKEND_RANDOM &&
- !rng->source.file) {
- rng->source.file = g_strdup("/dev/random");
- }
-}
-
-
-static void
-virDomainDiskExpandGroupIoTune(virDomainDiskDef *disk,
- const virDomainDef *def)
-{
- size_t i;
-
- if (!disk->blkdeviotune.group_name ||
- virDomainBlockIoTuneInfoHasAny(&disk->blkdeviotune))
- return;
-
- for (i = 0; i < def->ndisks; i++) {
- virDomainDiskDef *d = def->disks[i];
-
- if (STRNEQ_NULLABLE(disk->blkdeviotune.group_name, d->blkdeviotune.group_name) ||
- !virDomainBlockIoTuneInfoHasAny(&d->blkdeviotune))
- continue;
-
-
- VIR_FREE(disk->blkdeviotune.group_name);
- virDomainBlockIoTuneInfoCopy(&d->blkdeviotune, &disk->blkdeviotune);
-
- return;
- }
-}
-
-
-static int
-virDomainDiskDefPostParse(virDomainDiskDef *disk,
- const virDomainDef *def,
- virDomainXMLOption *xmlopt)
-{
- if (disk->dst) {
- char *newdst;
-
- /* Work around for compat with Xen driver in previous libvirt releases */
- if ((newdst = g_strdup(STRSKIP(disk->dst, "ioemu:")))) {
- g_free(disk->dst);
- disk->dst = newdst;
- }
- }
-
- /* Force CDROM to be listed as read only */
- if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM)
- disk->src->readonly = true;
-
- if (disk->bus == VIR_DOMAIN_DISK_BUS_NONE) {
- disk->bus = VIR_DOMAIN_DISK_BUS_IDE;
-
- if (disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) {
- disk->bus = VIR_DOMAIN_DISK_BUS_FDC;
- } else if (disk->dst) {
- if (STRPREFIX(disk->dst, "hd"))
- disk->bus = VIR_DOMAIN_DISK_BUS_IDE;
- else if (STRPREFIX(disk->dst, "sd"))
- disk->bus = VIR_DOMAIN_DISK_BUS_SCSI;
- else if (STRPREFIX(disk->dst, "vd"))
- disk->bus = VIR_DOMAIN_DISK_BUS_VIRTIO;
- else if (STRPREFIX(disk->dst, "xvd"))
- disk->bus = VIR_DOMAIN_DISK_BUS_XEN;
- else if (STRPREFIX(disk->dst, "ubd"))
- disk->bus = VIR_DOMAIN_DISK_BUS_UML;
- }
- }
-
- if (disk->snapshot == VIR_DOMAIN_SNAPSHOT_LOCATION_DEFAULT &&
- disk->src->readonly)
- disk->snapshot = VIR_DOMAIN_SNAPSHOT_LOCATION_NO;
-
- if (disk->src->type == VIR_STORAGE_TYPE_NETWORK &&
- disk->src->protocol == VIR_STORAGE_NET_PROTOCOL_ISCSI) {
- virDomainPostParseCheckISCSIPath(&disk->src->path);
- }
-
- if (disk->src->type == VIR_STORAGE_TYPE_NVME) {
- if (disk->src->nvme->managed == VIR_TRISTATE_BOOL_ABSENT)
- disk->src->nvme->managed = VIR_TRISTATE_BOOL_YES;
- }
-
- /* vhost-user doesn't allow us to snapshot, disable snapshots by default */
- if (disk->src->type == VIR_STORAGE_TYPE_VHOST_USER &&
- disk->snapshot == VIR_DOMAIN_SNAPSHOT_LOCATION_DEFAULT) {
- disk->snapshot = VIR_DOMAIN_SNAPSHOT_LOCATION_NO;
- }
-
- if (disk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE &&
- disk->dst &&
- virDomainDiskDefAssignAddress(xmlopt, disk, def) < 0) {
- return -1;
- }
-
- virDomainDiskExpandGroupIoTune(disk, def);
-
- return 0;
-}
-
-
-static void
-virDomainVideoDefPostParse(virDomainVideoDef *video,
- const virDomainDef *def)
-{
- /* Fill out (V)RAM if the driver-specific callback did not do so */
- if (video->ram == 0 && video->type == VIR_DOMAIN_VIDEO_TYPE_QXL)
- video->ram = virDomainVideoDefaultRAM(def, video->type);
- if (video->vram == 0)
- video->vram = virDomainVideoDefaultRAM(def, video->type);
-
- video->ram = VIR_ROUND_UP_POWER_OF_TWO(video->ram);
- video->vram = VIR_ROUND_UP_POWER_OF_TWO(video->vram);
-}
-
-
-static int
-virDomainControllerDefPostParse(virDomainControllerDef *cdev)
-{
- if (cdev->iothread &&
- cdev->model != VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_SCSI &&
- cdev->model != VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_TRANSITIONAL &&
- cdev->model != VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_NON_TRANSITIONAL) {
- virReportError(VIR_ERR_XML_ERROR, "%s",
- _("'iothread' attribute only supported for "
- "virtio scsi controllers"));
- return -1;
- }
-
- return 0;
-}
-
-
-static void
-virDomainVsockDefPostParse(virDomainVsockDef *vsock)
-{
- if (vsock->auto_cid == VIR_TRISTATE_BOOL_ABSENT) {
- if (vsock->guest_cid != 0)
- vsock->auto_cid = VIR_TRISTATE_BOOL_NO;
- else
- vsock->auto_cid = VIR_TRISTATE_BOOL_YES;
- }
-}
-
-
-static int
-virDomainMemoryDefPostParse(virDomainMemoryDef *mem,
- const virDomainDef *def)
-{
- switch (mem->model) {
- case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_PMEM:
- /* Virtio-pmem mandates shared access so that guest writes get
- * reflected in the underlying file. */
- if (mem->access == VIR_DOMAIN_MEMORY_ACCESS_DEFAULT)
- mem->access = VIR_DOMAIN_MEMORY_ACCESS_SHARED;
- break;
-
- case VIR_DOMAIN_MEMORY_MODEL_NVDIMM:
- /* If no NVDIMM UUID was provided in XML, generate one. */
- if (ARCH_IS_PPC64(def->os.arch) &&
- !mem->uuid) {
-
- mem->uuid = g_new0(unsigned char, VIR_UUID_BUFLEN);
- if (virUUIDGenerate(mem->uuid) < 0) {
- virReportError(VIR_ERR_INTERNAL_ERROR,
- "%s", _("Failed to generate UUID"));
- return -1;
- }
- }
- break;
-
- case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_MEM:
- case VIR_DOMAIN_MEMORY_MODEL_DIMM:
- case VIR_DOMAIN_MEMORY_MODEL_NONE:
- case VIR_DOMAIN_MEMORY_MODEL_LAST:
- break;
- }
-
- return 0;
-}
-
-
-static int
-virDomainFSDefPostParse(virDomainFSDef *fs)
-{
- if (fs->accessmode == VIR_DOMAIN_FS_ACCESSMODE_DEFAULT && !fs->sock)
- fs->accessmode = VIR_DOMAIN_FS_ACCESSMODE_PASSTHROUGH;
-
- return 0;
-}
-
-
-static int
-virDomainDeviceDefPostParseCommon(virDomainDeviceDef *dev,
- const virDomainDef *def,
- unsigned int parseFlags G_GNUC_UNUSED,
- virDomainXMLOption *xmlopt)
-{
- int ret = -1;
-
- switch ((virDomainDeviceType)dev->type) {
- case VIR_DOMAIN_DEVICE_CHR:
- ret = virDomainChrDefPostParse(dev->data.chr, def);
- break;
-
- case VIR_DOMAIN_DEVICE_RNG:
- virDomainRNGDefPostParse(dev->data.rng);
- ret = 0;
- break;
-
- case VIR_DOMAIN_DEVICE_DISK:
- ret = virDomainDiskDefPostParse(dev->data.disk, def, xmlopt);
- break;
-
- case VIR_DOMAIN_DEVICE_VIDEO:
- virDomainVideoDefPostParse(dev->data.video, def);
- ret = 0;
- break;
-
- case VIR_DOMAIN_DEVICE_HOSTDEV:
- ret = virDomainHostdevDefPostParse(dev->data.hostdev, def, xmlopt);
- break;
-
- case VIR_DOMAIN_DEVICE_CONTROLLER:
- ret = virDomainControllerDefPostParse(dev->data.controller);
- break;
-
- case VIR_DOMAIN_DEVICE_VSOCK:
- virDomainVsockDefPostParse(dev->data.vsock);
- ret = 0;
- break;
-
- case VIR_DOMAIN_DEVICE_MEMORY:
- ret = virDomainMemoryDefPostParse(dev->data.memory, def);
- break;
-
- case VIR_DOMAIN_DEVICE_FS:
- ret = virDomainFSDefPostParse(dev->data.fs);
- break;
-
- case VIR_DOMAIN_DEVICE_LEASE:
- case VIR_DOMAIN_DEVICE_NET:
- case VIR_DOMAIN_DEVICE_INPUT:
- case VIR_DOMAIN_DEVICE_SOUND:
- case VIR_DOMAIN_DEVICE_WATCHDOG:
- case VIR_DOMAIN_DEVICE_GRAPHICS:
- case VIR_DOMAIN_DEVICE_HUB:
- case VIR_DOMAIN_DEVICE_REDIRDEV:
- case VIR_DOMAIN_DEVICE_SMARTCARD:
- case VIR_DOMAIN_DEVICE_MEMBALLOON:
- case VIR_DOMAIN_DEVICE_NVRAM:
- case VIR_DOMAIN_DEVICE_SHMEM:
- case VIR_DOMAIN_DEVICE_TPM:
- case VIR_DOMAIN_DEVICE_PANIC:
- case VIR_DOMAIN_DEVICE_IOMMU:
- case VIR_DOMAIN_DEVICE_AUDIO:
- ret = 0;
- break;
-
- case VIR_DOMAIN_DEVICE_NONE:
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("unexpected VIR_DOMAIN_DEVICE_NONE"));
- break;
-
- case VIR_DOMAIN_DEVICE_LAST:
- default:
- virReportEnumRangeError(virDomainDeviceType, dev->type);
- break;
- }
-
- return ret;
-}
-
-
-/**
- * virDomainDefRemoveOfflineVcpuPin:
- * @def: domain definition
- *
- * This function removes vcpu pinning information from offline vcpus. This is
- * designed to be used for drivers which don't support offline vcpupin.
- */
-static void
-virDomainDefRemoveOfflineVcpuPin(virDomainDef *def)
-{
- size_t i;
- virDomainVcpuDef *vcpu;
-
- for (i = 0; i < virDomainDefGetVcpusMax(def); i++) {
- vcpu = virDomainDefGetVcpu(def, i);
-
- if (vcpu && !vcpu->online && vcpu->cpumask) {
- g_clear_pointer(&vcpu->cpumask, virBitmapFree);
-
- VIR_WARN("Ignoring unsupported vcpupin for offline vcpu '%zu'", i);
- }
- }
-}
-
-
-static void
-virDomainAssignControllerIndexes(virDomainDef *def)
-{
- /* the index attribute of a controller is optional in the XML, but
- * is required to be valid at any time after parse. When no index
- * is provided for a controller, assign one automatically by
- * looking at what indexes are already used for that controller
- * type in the domain - the unindexed controller gets the lowest
- * unused index.
- */
- size_t outer;
-
- for (outer = 0; outer < def->ncontrollers; outer++) {
- virDomainControllerDef *cont = def->controllers[outer];
- virDomainControllerDef *prev = NULL;
- size_t inner;
-
- if (cont->idx != -1)
- continue;
-
- if (outer > 0 && IS_USB2_CONTROLLER(cont)) {
- /* USB2 controllers are the only exception to the simple
- * "assign the lowest unused index". A group of USB2
- * "companions" should all be at the same index as other
- * USB2 controllers in the group, but only do this
- * automatically if it appears to be the intent. To prove
- * intent: the USB controller on the list just prior to
- * this one must also be a USB2 controller, and there must
- * not yet be a controller with the exact same model of
- * this one and the same index as the previously added
- * controller (e.g., if this controller is a UHCI1, then
- * the previous controller must be an EHCI1 or a UHCI[23],
- * and there must not already be a UHCI1 controller with
- * the same index as the previous controller). If all of
- * these are satisfied, set this controller to the same
- * index as the previous controller.
- */
- int prevIdx;
-
- prevIdx = outer - 1;
- while (prevIdx >= 0 &&
- def->controllers[prevIdx]->type != VIR_DOMAIN_CONTROLLER_TYPE_USB)
- prevIdx--;
- if (prevIdx >= 0)
- prev = def->controllers[prevIdx];
- /* if the last USB controller isn't USB2, that breaks
- * the chain, so we need a new index for this new
- * controller
- */
- if (prev && !IS_USB2_CONTROLLER(prev))
- prev = NULL;
-
- /* if prev != NULL, we've found a potential index to
- * use. Make sure this index isn't already used by an
- * existing USB2 controller of the same model as the new
- * one.
- */
- for (inner = 0; prev && inner < def->ncontrollers; inner++) {
- if (def->controllers[inner]->type == VIR_DOMAIN_CONTROLLER_TYPE_USB &&
- def->controllers[inner]->idx == prev->idx &&
- def->controllers[inner]->model == cont->model) {
- /* we already have a controller of this model with
- * the proposed index, so we need to move to a new
- * index for this controller
- */
- prev = NULL;
- }
- }
- if (prev)
- cont->idx = prev->idx;
- }
- /* if none of the above applied, prev will be NULL */
- if (!prev)
- cont->idx = virDomainControllerFindUnusedIndex(def, cont->type);
- }
-}
-
-
-#define UNSUPPORTED(FEATURE) (!((FEATURE) & xmlopt->config.features))
-/**
- * virDomainDefPostParseCheckFeatures:
- * @def: domain definition
- * @xmlopt: XML parser option object
- *
- * This function checks that the domain configuration is supported according to
- * the supported features for a given hypervisor. See virDomainDefFeatures and
- * virDomainDefParserConfig.
- *
- * Returns 0 on success and -1 on error with an appropriate libvirt error.
- */
-static int
-virDomainDefPostParseCheckFeatures(virDomainDef *def,
- virDomainXMLOption *xmlopt)
-{
- if (UNSUPPORTED(VIR_DOMAIN_DEF_FEATURE_MEMORY_HOTPLUG) &&
- virDomainDefCheckUnsupportedMemoryHotplug(def) < 0)
- return -1;
-
- if (UNSUPPORTED(VIR_DOMAIN_DEF_FEATURE_OFFLINE_VCPUPIN))
- virDomainDefRemoveOfflineVcpuPin(def);
-
- if (UNSUPPORTED(VIR_DOMAIN_DEF_FEATURE_NAME_SLASH)) {
- if (def->name && strchr(def->name, '/')) {
- virReportError(VIR_ERR_XML_ERROR,
- _("name %s cannot contain '/'"), def->name);
- return -1;
- }
- }
-
- if (UNSUPPORTED(VIR_DOMAIN_DEF_FEATURE_INDIVIDUAL_VCPUS) &&
- def->individualvcpus) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("individual CPU state configuration is not supported"));
- return -1;
- }
-
- return 0;
-}
-
-
-/**
- * virDomainDeviceDefPostParseCheckFeatures:
- * @dev: device definition
- * @xmlopt: XML parser option object
- *
- * This function checks that the device configuration is supported according to
- * the supported features for a given hypervisor. See virDomainDefFeatures and
- * virDomainDefParserConfig.
- *
- * Returns 0 on success and -1 on error with an appropriate libvirt error.
- */
-static int
-virDomainDeviceDefPostParseCheckFeatures(virDomainDeviceDef *dev,
- virDomainXMLOption *xmlopt)
-{
- if (UNSUPPORTED(VIR_DOMAIN_DEF_FEATURE_MEMORY_HOTPLUG) &&
- virDomainDeviceDefCheckUnsupportedMemoryDevice(dev) < 0)
- return -1;
-
- if (UNSUPPORTED(VIR_DOMAIN_DEF_FEATURE_NET_MODEL_STRING) &&
- dev->type == VIR_DOMAIN_DEVICE_NET &&
- dev->data.net->modelstr) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("driver does not support net model '%s'"),
- dev->data.net->modelstr);
- return -1;
- }
-
- return 0;
-}
-#undef UNSUPPORTED
-
-
-static int
-virDomainDeviceDefPostParse(virDomainDeviceDef *dev,
- const virDomainDef *def,
- unsigned int flags,
- virDomainXMLOption *xmlopt,
- void *parseOpaque)
-{
- int ret;
-
- if (xmlopt->config.devicesPostParseCallback) {
- ret = xmlopt->config.devicesPostParseCallback(dev, def, flags,
- xmlopt->config.priv,
- parseOpaque);
- if (ret < 0)
- return ret;
- }
-
- if ((ret = virDomainDeviceDefPostParseCommon(dev, def, flags, xmlopt)) < 0)
- return ret;
-
- if (virDomainDeviceDefPostParseCheckFeatures(dev, xmlopt) < 0)
- return -1;
-
- return 0;
-}
-
-static int
-virDomainDeviceDefPostParseOne(virDomainDeviceDef *dev,
- const virDomainDef *def,
- unsigned int flags,
- virDomainXMLOption *xmlopt,
- void *parseOpaque)
-{
- void *data = NULL;
- int ret;
-
- if (!parseOpaque && xmlopt->config.domainPostParseDataAlloc) {
- if (xmlopt->config.domainPostParseDataAlloc(def, flags,
- xmlopt->config.priv,
- &data) < 0)
- return -1;
- parseOpaque = data;
- }
-
- ret = virDomainDeviceDefPostParse(dev, def, flags, xmlopt, parseOpaque);
-
- if (data && xmlopt->config.domainPostParseDataFree)
- xmlopt->config.domainPostParseDataFree(data);
-
- return ret;
-}
-
-
-struct virDomainDefPostParseDeviceIteratorData {
- virDomainXMLOption *xmlopt;
- void *parseOpaque;
- unsigned int parseFlags;
-};
-
-
-static int
-virDomainDefPostParseDeviceIterator(virDomainDef *def,
- virDomainDeviceDef *dev,
- virDomainDeviceInfo *info G_GNUC_UNUSED,
- void *opaque)
-{
- struct virDomainDefPostParseDeviceIteratorData *data = opaque;
- return virDomainDeviceDefPostParse(dev, def,
- data->parseFlags, data->xmlopt,
- data->parseOpaque);
-}
-
-
-static int
-virDomainVcpuDefPostParse(virDomainDef *def)
-{
- virDomainVcpuDef *vcpu;
- size_t maxvcpus = virDomainDefGetVcpusMax(def);
- size_t i;
-
- for (i = 0; i < maxvcpus; i++) {
- vcpu = virDomainDefGetVcpu(def, i);
-
- /* impossible but some compilers don't like it */
- if (!vcpu)
- continue;
-
- switch (vcpu->hotpluggable) {
- case VIR_TRISTATE_BOOL_ABSENT:
- if (vcpu->online)
- vcpu->hotpluggable = VIR_TRISTATE_BOOL_NO;
- else
- vcpu->hotpluggable = VIR_TRISTATE_BOOL_YES;
- break;
-
- case VIR_TRISTATE_BOOL_NO:
- if (!vcpu->online) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("vcpu '%zu' is both offline and not "
- "hotpluggable"), i);
- return -1;
- }
- break;
-
- case VIR_TRISTATE_BOOL_YES:
- case VIR_TRISTATE_BOOL_LAST:
- break;
- }
- }
-
- return 0;
-}
-
-
-static int
-virDomainDefPostParseCPU(virDomainDef *def)
-{
- if (!def->cpu)
- return 0;
-
- if (def->cpu->mode == VIR_CPU_MODE_CUSTOM &&
- !def->cpu->model &&
- def->cpu->check != VIR_CPU_CHECK_DEFAULT) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("check attribute specified for CPU with no model"));
- return -1;
- }
-
- return 0;
-}
-
-
-static int
-virDomainDefCollectBootOrder(virDomainDef *def G_GNUC_UNUSED,
- virDomainDeviceDef *dev G_GNUC_UNUSED,
- virDomainDeviceInfo *info,
- void *data)
-{
- GHashTable *bootHash = data;
- g_autofree char *order = NULL;
-
- if (info->bootIndex == 0)
- return 0;
-
- if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV &&
- dev->data.hostdev->parentnet) {
- /* This hostdev is a child of a higher level device
- * (e.g. interface), and thus already being counted on the
- * list for the other device type.
- */
- return 0;
- }
- order = g_strdup_printf("%u", info->bootIndex);
-
- if (virHashLookup(bootHash, order)) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("boot order '%s' used for more than one device"),
- order);
- return -1;
- }
-
- if (virHashAddEntry(bootHash, order, (void *) 1) < 0)
- return -1;
-
- return 0;
-}
-
-
-static int
-virDomainDefBootOrderPostParse(virDomainDef *def)
-{
- g_autoptr(GHashTable) bootHash = virHashNew(NULL);
-
- if (virDomainDeviceInfoIterate(def, virDomainDefCollectBootOrder, bootHash) < 0)
- return -1;
-
- if (def->os.nBootDevs > 0 && virHashSize(bootHash) > 0) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("per-device boot elements cannot be used"
- " together with os/boot elements"));
- return -1;
- }
-
- if (def->os.nBootDevs == 0 && virHashSize(bootHash) == 0) {
- def->os.nBootDevs = 1;
- def->os.bootDevs[0] = VIR_DOMAIN_BOOT_DISK;
- }
-
- return 0;
-}
-
-
-static int
-virDomainDefPostParseVideo(virDomainDef *def,
- void *opaque)
-{
- if (def->nvideos == 0)
- return 0;
-
- if (def->videos[0]->type == VIR_DOMAIN_VIDEO_TYPE_NONE) {
- char *alias;
-
- /* we don't want to format any values we automatically fill in for
- * videos into the XML, so clear them, but retain any user-assigned
- * alias */
- alias = g_steal_pointer(&def->videos[0]->info.alias);
- virDomainVideoDefClear(def->videos[0]);
- def->videos[0]->type = VIR_DOMAIN_VIDEO_TYPE_NONE;
- def->videos[0]->info.alias = g_steal_pointer(&alias);
- } else {
- virDomainDeviceDef device = {
- .type = VIR_DOMAIN_DEVICE_VIDEO,
- .data.video = def->videos[0],
- };
-
- /* Mark the first video as primary. If the user specified
- * primary="yes", the parser already inserted the device at
- * def->videos[0]
- */
- def->videos[0]->primary = true;
-
- /* videos[0] might have been added in AddImplicitDevices, after we've
- * done the per-device post-parse */
- if (virDomainDefPostParseDeviceIterator(def, &device,
- NULL, opaque) < 0)
- return -1;
- }
-
- return 0;
-}
-
-
-static int
-virDomainDefPostParseCommon(virDomainDef *def,
- struct virDomainDefPostParseDeviceIteratorData *data,
- virDomainXMLOption *xmlopt)
-{
- size_t i;
-
- /* verify init path for container based domains */
- if (def->os.type == VIR_DOMAIN_OSTYPE_EXE && !def->os.init) {
- virReportError(VIR_ERR_XML_ERROR, "%s",
- _("init binary must be specified"));
- return -1;
- }
-
- if (virDomainVcpuDefPostParse(def) < 0)
- return -1;
-
- if (virDomainDefPostParseMemory(def, data->parseFlags) < 0)
- return -1;
-
- if (virDomainDefPostParseOs(def) < 0)
- return -1;
-
- virDomainDefPostParseMemtune(def);
-
- if (virDomainDefRejectDuplicateControllers(def) < 0)
- return -1;
-
- if (virDomainDefRejectDuplicatePanics(def) < 0)
- return -1;
-
- if (def->os.type == VIR_DOMAIN_OSTYPE_HVM &&
- !(data->xmlopt->config.features & VIR_DOMAIN_DEF_FEATURE_NO_BOOT_ORDER) &&
- virDomainDefBootOrderPostParse(def) < 0)
- return -1;
-
- if (virDomainDefPostParseTimer(def) < 0)
- return -1;
-
- if (virDomainDefAddImplicitDevices(def, xmlopt) < 0)
- return -1;
-
- if (virDomainDefPostParseVideo(def, data) < 0)
- return -1;
-
- if (def->nserials != 0) {
- virDomainDeviceDef device = {
- .type = VIR_DOMAIN_DEVICE_CHR,
- .data.chr = def->serials[0],
- };
-
- /* serials[0] might have been added in AddImplicitDevices, after we've
- * done the per-device post-parse */
- if (virDomainDefPostParseDeviceIterator(def, &device, NULL, data) < 0)
- return -1;
- }
-
- /* Implicit SCSI controllers without a defined model might have
- * been added in AddImplicitDevices, after we've done the per-device
- * post-parse. */
- for (i = 0; i < def->ncontrollers; i++) {
- if (def->controllers[i]->model == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_DEFAULT &&
- def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI) {
- virDomainDeviceDef device = {
- .type = VIR_DOMAIN_DEVICE_CONTROLLER,
- .data.controller = def->controllers[i],
- };
- if (virDomainDefPostParseDeviceIterator(def, &device, NULL, data) < 0)
- return -1;
- }
- }
-
- /* clean up possibly duplicated metadata entries */
- virXMLNodeSanitizeNamespaces(def->metadata);
-
- virDomainDefPostParseGraphics(def);
-
- if (virDomainDefPostParseCPU(def) < 0)
- return -1;
-
- return 0;
-}
-
-
-static int
-virDomainDefPostParseCheckFailure(virDomainDef *def,
- unsigned int parseFlags,
- int ret)
-{
- if (ret != 0)
- def->postParseFailed = true;
-
- if (ret <= 0)
- return ret;
-
- if (!(parseFlags & VIR_DOMAIN_DEF_PARSE_ALLOW_POST_PARSE_FAIL))
- return -1;
-
- virResetLastError();
- return 0;
-}
-
-
-int
-virDomainDefPostParse(virDomainDef *def,
- unsigned int parseFlags,
- virDomainXMLOption *xmlopt,
- void *parseOpaque)
-{
- int ret = -1;
- bool localParseOpaque = false;
- struct virDomainDefPostParseDeviceIteratorData data = {
- .xmlopt = xmlopt,
- .parseFlags = parseFlags,
- .parseOpaque = parseOpaque,
- };
-
- def->postParseFailed = false;
-
- /* call the basic post parse callback */
- if (xmlopt->config.domainPostParseBasicCallback) {
- ret = xmlopt->config.domainPostParseBasicCallback(def,
- xmlopt->config.priv);
-
- if (virDomainDefPostParseCheckFailure(def, parseFlags, ret) < 0)
- goto cleanup;
- }
-
- if (!data.parseOpaque &&
- xmlopt->config.domainPostParseDataAlloc) {
- ret = xmlopt->config.domainPostParseDataAlloc(def, parseFlags,
- xmlopt->config.priv,
- &data.parseOpaque);
-
- if (virDomainDefPostParseCheckFailure(def, parseFlags, ret) < 0)
- goto cleanup;
- localParseOpaque = true;
- }
-
- /* this must be done before the hypervisor-specific callback,
- * in case presence of a controller at a specific index is checked
- */
- virDomainAssignControllerIndexes(def);
-
- /* call the domain config callback */
- if (xmlopt->config.domainPostParseCallback) {
- ret = xmlopt->config.domainPostParseCallback(def, parseFlags,
- xmlopt->config.priv,
- data.parseOpaque);
- if (virDomainDefPostParseCheckFailure(def, parseFlags, ret) < 0)
- goto cleanup;
- }
-
- if (virDomainChrIsaSerialDefPostParse(def) < 0)
- return -1;
-
- /* iterate the devices */
- ret = virDomainDeviceInfoIterateFlags(def,
- virDomainDefPostParseDeviceIterator,
- DOMAIN_DEVICE_ITERATE_ALL_CONSOLES |
- DOMAIN_DEVICE_ITERATE_MISSING_INFO,
- &data);
-
- if (virDomainDefPostParseCheckFailure(def, parseFlags, ret) < 0)
- goto cleanup;
-
- if ((ret = virDomainDefPostParseCommon(def, &data, xmlopt)) < 0)
- goto cleanup;
-
- if (xmlopt->config.assignAddressesCallback) {
- ret = xmlopt->config.assignAddressesCallback(def, parseFlags,
- xmlopt->config.priv,
- data.parseOpaque);
- if (virDomainDefPostParseCheckFailure(def, parseFlags, ret) < 0)
- goto cleanup;
- }
-
- if ((ret = virDomainDefPostParseCheckFeatures(def, xmlopt)) < 0)
- goto cleanup;
-
- ret = 0;
-
- cleanup:
- if (localParseOpaque && xmlopt->config.domainPostParseDataFree)
- xmlopt->config.domainPostParseDataFree(data.parseOpaque);
-
- if (ret == 1)
- ret = -1;
-
- return ret;
-}
-
-
bool
virDomainDefHasUSB(const virDomainDef *def)
{
virDomainSCSIDriveAddressIsUsed(const virDomainDef *def,
const virDomainDeviceDriveAddress *addr);
-int virDomainDefPostParse(virDomainDef *def,
- unsigned int parseFlags,
- virDomainXMLOption *xmlopt,
- void *parseOpaque);
bool virDomainDefHasUSB(const virDomainDef *def);
bool virDomainDeviceAliasIsUserAlias(const char *aliasStr);
--- /dev/null
+/*
+ * domain_postparse.c: domain post parsing helpers
+ *
+ * Copyright (C) 2022 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include "domain_postparse.h"
+#include "viralloc.h"
+#include "virlog.h"
+
+#define VIR_FROM_THIS VIR_FROM_DOMAIN
+
+VIR_LOG_INIT("conf.domain_postparse");
+
+static int
+virDomainDefPostParseMemory(virDomainDef *def,
+ unsigned int parseFlags)
+{
+ size_t i;
+ unsigned long long numaMemory = 0;
+ unsigned long long hotplugMemory = 0;
+
+ /* Attempt to infer the initial memory size from the sum NUMA memory sizes
+ * in case ABI updates are allowed or the <memory> element wasn't specified */
+ if (def->mem.total_memory == 0 ||
+ parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE ||
+ parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE_MIGRATION)
+ numaMemory = virDomainNumaGetMemorySize(def->numa);
+
+ /* calculate the sizes of hotplug memory */
+ for (i = 0; i < def->nmems; i++)
+ hotplugMemory += def->mems[i]->size;
+
+ if (numaMemory) {
+ /* update the sizes in XML if nothing was set in the XML or ABI update
+ * is supported */
+ virDomainDefSetMemoryTotal(def, numaMemory + hotplugMemory);
+ } else {
+ /* verify that the sum of memory modules doesn't exceed the total
+ * memory. This is necessary for virDomainDefGetMemoryInitial to work
+ * properly. */
+ if (hotplugMemory > def->mem.total_memory) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Total size of memory devices exceeds the total "
+ "memory size"));
+ return -1;
+ }
+ }
+
+ if (virDomainDefGetMemoryInitial(def) == 0) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Memory size must be specified via <memory> or in the "
+ "<numa> configuration"));
+ return -1;
+ }
+
+ if (def->mem.cur_balloon > virDomainDefGetMemoryTotal(def) ||
+ def->mem.cur_balloon == 0)
+ def->mem.cur_balloon = virDomainDefGetMemoryTotal(def);
+
+ if ((def->mem.max_memory || def->mem.memory_slots) &&
+ !(def->mem.max_memory && def->mem.memory_slots)) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("both maximum memory size and "
+ "memory slot count must be specified"));
+ return -1;
+ }
+
+ if (def->mem.max_memory &&
+ def->mem.max_memory < virDomainDefGetMemoryTotal(def)) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("maximum memory size must be equal or greater than "
+ "the actual memory size"));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int
+virDomainDefPostParseOs(virDomainDef *def)
+{
+ if (def->os.firmwareFeatures &&
+ def->os.firmwareFeatures[VIR_DOMAIN_OS_DEF_FIRMWARE_FEATURE_ENROLLED_KEYS] == VIR_TRISTATE_BOOL_YES) {
+
+ if (def->os.firmwareFeatures[VIR_DOMAIN_OS_DEF_FIRMWARE_FEATURE_SECURE_BOOT] == VIR_TRISTATE_BOOL_NO) {
+ virReportError(VIR_ERR_XML_DETAIL, "%s",
+ _("firmware feature 'enrolled-keys' cannot be enabled when "
+ "firmware feature 'secure-boot' is disabled"));
+ return -1;
+ }
+
+ /* For all non-broken firmware builds, enrolled-keys implies
+ * secure-boot, and having the Secure Boot keys in the NVRAM file
+ * when the firmware doesn't support the Secure Boot feature doesn't
+ * make sense anyway. Reflect this fact explicitly in the XML */
+ def->os.firmwareFeatures[VIR_DOMAIN_OS_DEF_FIRMWARE_FEATURE_SECURE_BOOT] = VIR_TRISTATE_BOOL_YES;
+ }
+
+ if (!def->os.loader)
+ return 0;
+
+ if (def->os.loader->path &&
+ def->os.loader->type == VIR_DOMAIN_LOADER_TYPE_NONE) {
+ /* By default, loader is type of 'rom' */
+ def->os.loader->type = VIR_DOMAIN_LOADER_TYPE_ROM;
+ }
+
+ return 0;
+}
+
+
+static void
+virDomainDefPostParseMemtune(virDomainDef *def)
+{
+ size_t i;
+
+ if (virDomainNumaGetNodeCount(def->numa) == 0) {
+ /* If guest NUMA is not configured and any hugepage page has nodemask
+ * set to "0" free and clear that nodemas, otherwise we would rise
+ * an error that there is no guest NUMA node configured. */
+ for (i = 0; i < def->mem.nhugepages; i++) {
+ ssize_t nextBit;
+
+ if (!def->mem.hugepages[i].nodemask)
+ continue;
+
+ nextBit = virBitmapNextSetBit(def->mem.hugepages[i].nodemask, 0);
+ if (nextBit < 0) {
+ g_clear_pointer(&def->mem.hugepages[i].nodemask,
+ virBitmapFree);
+ }
+ }
+ }
+}
+
+
+static int
+virDomainDefPostParseTimer(virDomainDef *def)
+{
+ size_t i;
+
+ /* verify settings of guest timers */
+ for (i = 0; i < def->clock.ntimers; i++) {
+ virDomainTimerDef *timer = def->clock.timers[i];
+
+ if (timer->name == VIR_DOMAIN_TIMER_NAME_KVMCLOCK ||
+ timer->name == VIR_DOMAIN_TIMER_NAME_HYPERVCLOCK) {
+ if (timer->tickpolicy) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("timer %s doesn't support setting of "
+ "timer tickpolicy"),
+ virDomainTimerNameTypeToString(timer->name));
+ return -1;
+ }
+ }
+
+ if (timer->tickpolicy != VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP &&
+ (timer->catchup.threshold != 0 ||
+ timer->catchup.limit != 0 ||
+ timer->catchup.slew != 0)) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("setting of timer catchup policies is only "
+ "supported with tickpolicy='catchup'"));
+ return -1;
+ }
+
+ if (timer->name != VIR_DOMAIN_TIMER_NAME_TSC) {
+ if (timer->frequency != 0) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("timer %s doesn't support setting of "
+ "timer frequency"),
+ virDomainTimerNameTypeToString(timer->name));
+ return -1;
+ }
+
+ if (timer->mode) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("timer %s doesn't support setting of "
+ "timer mode"),
+ virDomainTimerNameTypeToString(timer->name));
+ return -1;
+ }
+ }
+
+ if (timer->name != VIR_DOMAIN_TIMER_NAME_PLATFORM &&
+ timer->name != VIR_DOMAIN_TIMER_NAME_RTC) {
+ if (timer->track != VIR_DOMAIN_TIMER_TRACK_NONE) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("timer %s doesn't support setting of "
+ "timer track"),
+ virDomainTimerNameTypeToString(timer->name));
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+static void
+virDomainDefPostParseGraphics(virDomainDef *def)
+{
+ size_t i;
+
+ for (i = 0; i < def->ngraphics; i++) {
+ virDomainGraphicsDef *graphics = def->graphics[i];
+
+ /* If spice graphics is configured without ports and with autoport='no'
+ * then we start qemu with Spice to not listen anywhere. Let's convert
+ * this configuration to the new listen type='none' which does the
+ * same. */
+ if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) {
+ virDomainGraphicsListenDef *glisten = &graphics->listens[0];
+
+ if (glisten->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS &&
+ graphics->data.spice.port == 0 &&
+ graphics->data.spice.tlsPort == 0 &&
+ !graphics->data.spice.autoport) {
+ VIR_FREE(glisten->address);
+ glisten->type = VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE;
+ }
+ }
+ }
+}
+
+
+/**
+ * virDomainPostParseCheckISCSIPath
+ * @srcpath: Source path read (a/k/a, IQN) either disk or hostdev
+ *
+ * The details of an IQN is defined by RFC 3720 and 3721, but
+ * we just need to make sure there's a lun provided. If not
+ * provided, then default to zero. For an ISCSI LUN that is
+ * is provided by /dev/disk/by-path/... , then that path will
+ * have the specific lun requested.
+ */
+static void
+virDomainPostParseCheckISCSIPath(char **srcpath)
+{
+ char *path = NULL;
+
+ if (strchr(*srcpath, '/'))
+ return;
+
+ path = g_strdup_printf("%s/0", *srcpath);
+ g_free(*srcpath);
+ *srcpath = g_steal_pointer(&path);
+}
+
+
+/* Find out the next usable "unit" of a specific controller */
+static int
+virDomainControllerSCSINextUnit(const virDomainDef *def,
+ unsigned int controller)
+{
+ size_t i;
+
+ for (i = 0; i < def->scsiBusMaxUnit; i++) {
+ /* Default to assigning addresses using bus = target = 0 */
+ const virDomainDeviceDriveAddress addr = {controller, 0, 0, i, 0};
+
+ if (!virDomainSCSIDriveAddressIsUsed(def, &addr))
+ return i;
+ }
+
+ return -1;
+}
+
+
+static void
+virDomainHostdevAssignAddress(virDomainXMLOption *xmlopt G_GNUC_UNUSED,
+ const virDomainDef *def,
+ virDomainHostdevDef *hostdev)
+{
+ int next_unit = 0;
+ int controller = 0;
+
+ /* NB: Do not attempt calling virDomainDefMaybeAddController to
+ * automagically add a "new" controller. Doing so will result in
+ * qemuDomainFindOrCreateSCSIDiskController "finding" the controller
+ * in the domain def list and thus not hotplugging the controller as
+ * well as the hostdev in the event that there are either no SCSI
+ * controllers defined or there was no space on an existing one.
+ *
+ * Because we cannot add a controller, then we should not walk the
+ * defined controllers list in order to find empty space. Doing
+ * so fails to return the valid next unit number for the 2nd
+ * hostdev being added to the as yet to be created controller.
+ */
+ do {
+ next_unit = virDomainControllerSCSINextUnit(def, controller);
+ if (next_unit < 0)
+ controller++;
+ } while (next_unit < 0);
+
+
+ hostdev->info->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE;
+ hostdev->info->addr.drive.controller = controller;
+ hostdev->info->addr.drive.bus = 0;
+ hostdev->info->addr.drive.target = 0;
+ hostdev->info->addr.drive.unit = next_unit;
+}
+
+
+static int
+virDomainHostdevDefPostParse(virDomainHostdevDef *dev,
+ const virDomainDef *def,
+ virDomainXMLOption *xmlopt)
+{
+ virDomainHostdevSubsysSCSI *scsisrc;
+ virDomainDeviceDriveAddress *addr = NULL;
+
+ if (dev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
+ return 0;
+
+ switch (dev->source.subsys.type) {
+ case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI:
+ scsisrc = &dev->source.subsys.u.scsi;
+ if (scsisrc->protocol == VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_ISCSI) {
+ virDomainHostdevSubsysSCSIiSCSI *iscsisrc = &scsisrc->u.iscsi;
+ virDomainPostParseCheckISCSIPath(&iscsisrc->src->path);
+ }
+
+ if (dev->info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
+ virDomainHostdevAssignAddress(xmlopt, def, dev);
+
+ /* Ensure provided address doesn't conflict with existing
+ * scsi disk drive address
+ */
+ addr = &dev->info->addr.drive;
+ if (virDomainDriveAddressIsUsedByDisk(def,
+ VIR_DOMAIN_DISK_BUS_SCSI,
+ addr)) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("SCSI host address controller='%u' "
+ "bus='%u' target='%u' unit='%u' in "
+ "use by a SCSI disk"),
+ addr->controller, addr->bus,
+ addr->target, addr->unit);
+ return -1;
+ }
+ break;
+ case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV: {
+ int model = dev->source.subsys.u.mdev.model;
+
+ if (dev->info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
+ return 0;
+
+ if ((model == VIR_MDEV_MODEL_TYPE_VFIO_PCI &&
+ dev->info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) ||
+ (model == VIR_MDEV_MODEL_TYPE_VFIO_CCW &&
+ dev->info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW)) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Unsupported address type '%s' with mediated "
+ "device model '%s'"),
+ virDomainDeviceAddressTypeToString(dev->info->type),
+ virMediatedDeviceModelTypeToString(model));
+ return -1;
+ }
+ }
+
+ case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
+ case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
+ case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST:
+ case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
+ break;
+ }
+
+ return 0;
+}
+
+
+static int
+virDomainChrIsaSerialDefPostParse(virDomainDef *def)
+{
+ size_t i;
+ size_t isa_serial_count = 0;
+ bool used_serial_port[VIR_MAX_ISA_SERIAL_PORTS] = { false };
+
+ /* Perform all the required checks. */
+ for (i = 0; i < def->nserials; i++) {
+ if (def->serials[i]->targetType != VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_ISA_SERIAL)
+ continue;
+
+ if (isa_serial_count++ >= VIR_MAX_ISA_SERIAL_PORTS ||
+ def->serials[i]->target.port >= VIR_MAX_ISA_SERIAL_PORTS) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Maximum supported number of ISA serial ports is '%d'"),
+ VIR_MAX_ISA_SERIAL_PORTS);
+ return -1;
+ }
+
+ if (def->serials[i]->target.port != -1) {
+ if (used_serial_port[def->serials[i]->target.port]) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("target port '%d' already allocated"),
+ def->serials[i]->target.port);
+ return -1;
+ }
+ used_serial_port[def->serials[i]->target.port] = true;
+ }
+ }
+
+ /* Assign the ports to the devices. */
+ for (i = 0; i < def->nserials; i++) {
+ size_t j;
+
+ if (def->serials[i]->targetType != VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_ISA_SERIAL ||
+ def->serials[i]->target.port != -1)
+ continue;
+
+ for (j = 0; j < VIR_MAX_ISA_SERIAL_PORTS; j++) {
+ if (!used_serial_port[j]) {
+ def->serials[i]->target.port = j;
+ used_serial_port[j] = true;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+static int
+virDomainChrDefPostParse(virDomainChrDef *chr,
+ const virDomainDef *def)
+{
+ const virDomainChrDef **arrPtr;
+ size_t i, cnt;
+
+ virDomainChrGetDomainPtrs(def, chr->deviceType, &arrPtr, &cnt);
+
+ if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE &&
+ chr->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_NONE) {
+ chr->targetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL;
+ }
+
+ if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL &&
+ chr->targetType == VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA_DEBUG &&
+ !ARCH_IS_X86(def->os.arch)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("isa-debug serial type only valid on x86 architecture"));
+ return -1;
+ }
+
+ if (chr->target.port == -1 &&
+ (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_PARALLEL ||
+ chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL ||
+ chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE)) {
+ int maxport = -1;
+
+ for (i = 0; i < cnt; i++) {
+ if (arrPtr[i]->target.port > maxport)
+ maxport = arrPtr[i]->target.port;
+ }
+
+ chr->target.port = maxport + 1;
+ }
+
+ return 0;
+}
+
+
+static void
+virDomainRNGDefPostParse(virDomainRNGDef *rng)
+{
+ /* set default path for virtio-rng "random" backend to /dev/random */
+ if (rng->backend == VIR_DOMAIN_RNG_BACKEND_RANDOM &&
+ !rng->source.file) {
+ rng->source.file = g_strdup("/dev/random");
+ }
+}
+
+
+static void
+virDomainDiskExpandGroupIoTune(virDomainDiskDef *disk,
+ const virDomainDef *def)
+{
+ size_t i;
+
+ if (!disk->blkdeviotune.group_name ||
+ virDomainBlockIoTuneInfoHasAny(&disk->blkdeviotune))
+ return;
+
+ for (i = 0; i < def->ndisks; i++) {
+ virDomainDiskDef *d = def->disks[i];
+
+ if (STRNEQ_NULLABLE(disk->blkdeviotune.group_name, d->blkdeviotune.group_name) ||
+ !virDomainBlockIoTuneInfoHasAny(&d->blkdeviotune))
+ continue;
+
+
+ VIR_FREE(disk->blkdeviotune.group_name);
+ virDomainBlockIoTuneInfoCopy(&d->blkdeviotune, &disk->blkdeviotune);
+
+ return;
+ }
+}
+
+
+static int
+virDomainDiskDefPostParse(virDomainDiskDef *disk,
+ const virDomainDef *def,
+ virDomainXMLOption *xmlopt)
+{
+ if (disk->dst) {
+ char *newdst;
+
+ /* Work around for compat with Xen driver in previous libvirt releases */
+ if ((newdst = g_strdup(STRSKIP(disk->dst, "ioemu:")))) {
+ g_free(disk->dst);
+ disk->dst = newdst;
+ }
+ }
+
+ /* Force CDROM to be listed as read only */
+ if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM)
+ disk->src->readonly = true;
+
+ if (disk->bus == VIR_DOMAIN_DISK_BUS_NONE) {
+ disk->bus = VIR_DOMAIN_DISK_BUS_IDE;
+
+ if (disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) {
+ disk->bus = VIR_DOMAIN_DISK_BUS_FDC;
+ } else if (disk->dst) {
+ if (STRPREFIX(disk->dst, "hd"))
+ disk->bus = VIR_DOMAIN_DISK_BUS_IDE;
+ else if (STRPREFIX(disk->dst, "sd"))
+ disk->bus = VIR_DOMAIN_DISK_BUS_SCSI;
+ else if (STRPREFIX(disk->dst, "vd"))
+ disk->bus = VIR_DOMAIN_DISK_BUS_VIRTIO;
+ else if (STRPREFIX(disk->dst, "xvd"))
+ disk->bus = VIR_DOMAIN_DISK_BUS_XEN;
+ else if (STRPREFIX(disk->dst, "ubd"))
+ disk->bus = VIR_DOMAIN_DISK_BUS_UML;
+ }
+ }
+
+ if (disk->snapshot == VIR_DOMAIN_SNAPSHOT_LOCATION_DEFAULT &&
+ disk->src->readonly)
+ disk->snapshot = VIR_DOMAIN_SNAPSHOT_LOCATION_NO;
+
+ if (disk->src->type == VIR_STORAGE_TYPE_NETWORK &&
+ disk->src->protocol == VIR_STORAGE_NET_PROTOCOL_ISCSI) {
+ virDomainPostParseCheckISCSIPath(&disk->src->path);
+ }
+
+ if (disk->src->type == VIR_STORAGE_TYPE_NVME) {
+ if (disk->src->nvme->managed == VIR_TRISTATE_BOOL_ABSENT)
+ disk->src->nvme->managed = VIR_TRISTATE_BOOL_YES;
+ }
+
+ /* vhost-user doesn't allow us to snapshot, disable snapshots by default */
+ if (disk->src->type == VIR_STORAGE_TYPE_VHOST_USER &&
+ disk->snapshot == VIR_DOMAIN_SNAPSHOT_LOCATION_DEFAULT) {
+ disk->snapshot = VIR_DOMAIN_SNAPSHOT_LOCATION_NO;
+ }
+
+ if (disk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE &&
+ disk->dst &&
+ virDomainDiskDefAssignAddress(xmlopt, disk, def) < 0) {
+ return -1;
+ }
+
+ virDomainDiskExpandGroupIoTune(disk, def);
+
+ return 0;
+}
+
+
+static void
+virDomainVideoDefPostParse(virDomainVideoDef *video,
+ const virDomainDef *def)
+{
+ /* Fill out (V)RAM if the driver-specific callback did not do so */
+ if (video->ram == 0 && video->type == VIR_DOMAIN_VIDEO_TYPE_QXL)
+ video->ram = virDomainVideoDefaultRAM(def, video->type);
+ if (video->vram == 0)
+ video->vram = virDomainVideoDefaultRAM(def, video->type);
+
+ video->ram = VIR_ROUND_UP_POWER_OF_TWO(video->ram);
+ video->vram = VIR_ROUND_UP_POWER_OF_TWO(video->vram);
+}
+
+
+static int
+virDomainControllerDefPostParse(virDomainControllerDef *cdev)
+{
+ if (cdev->iothread &&
+ cdev->model != VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_SCSI &&
+ cdev->model != VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_TRANSITIONAL &&
+ cdev->model != VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_NON_TRANSITIONAL) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("'iothread' attribute only supported for "
+ "virtio scsi controllers"));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void
+virDomainVsockDefPostParse(virDomainVsockDef *vsock)
+{
+ if (vsock->auto_cid == VIR_TRISTATE_BOOL_ABSENT) {
+ if (vsock->guest_cid != 0)
+ vsock->auto_cid = VIR_TRISTATE_BOOL_NO;
+ else
+ vsock->auto_cid = VIR_TRISTATE_BOOL_YES;
+ }
+}
+
+
+static int
+virDomainMemoryDefPostParse(virDomainMemoryDef *mem,
+ const virDomainDef *def)
+{
+ switch (mem->model) {
+ case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_PMEM:
+ /* Virtio-pmem mandates shared access so that guest writes get
+ * reflected in the underlying file. */
+ if (mem->access == VIR_DOMAIN_MEMORY_ACCESS_DEFAULT)
+ mem->access = VIR_DOMAIN_MEMORY_ACCESS_SHARED;
+ break;
+
+ case VIR_DOMAIN_MEMORY_MODEL_NVDIMM:
+ /* If no NVDIMM UUID was provided in XML, generate one. */
+ if (ARCH_IS_PPC64(def->os.arch) &&
+ !mem->uuid) {
+
+ mem->uuid = g_new0(unsigned char, VIR_UUID_BUFLEN);
+ if (virUUIDGenerate(mem->uuid) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Failed to generate UUID"));
+ return -1;
+ }
+ }
+ break;
+
+ case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_MEM:
+ case VIR_DOMAIN_MEMORY_MODEL_DIMM:
+ case VIR_DOMAIN_MEMORY_MODEL_NONE:
+ case VIR_DOMAIN_MEMORY_MODEL_LAST:
+ break;
+ }
+
+ return 0;
+}
+
+
+static int
+virDomainFSDefPostParse(virDomainFSDef *fs)
+{
+ if (fs->accessmode == VIR_DOMAIN_FS_ACCESSMODE_DEFAULT && !fs->sock)
+ fs->accessmode = VIR_DOMAIN_FS_ACCESSMODE_PASSTHROUGH;
+
+ return 0;
+}
+
+
+static int
+virDomainDeviceDefPostParseCommon(virDomainDeviceDef *dev,
+ const virDomainDef *def,
+ unsigned int parseFlags G_GNUC_UNUSED,
+ virDomainXMLOption *xmlopt)
+{
+ int ret = -1;
+
+ switch ((virDomainDeviceType)dev->type) {
+ case VIR_DOMAIN_DEVICE_CHR:
+ ret = virDomainChrDefPostParse(dev->data.chr, def);
+ break;
+
+ case VIR_DOMAIN_DEVICE_RNG:
+ virDomainRNGDefPostParse(dev->data.rng);
+ ret = 0;
+ break;
+
+ case VIR_DOMAIN_DEVICE_DISK:
+ ret = virDomainDiskDefPostParse(dev->data.disk, def, xmlopt);
+ break;
+
+ case VIR_DOMAIN_DEVICE_VIDEO:
+ virDomainVideoDefPostParse(dev->data.video, def);
+ ret = 0;
+ break;
+
+ case VIR_DOMAIN_DEVICE_HOSTDEV:
+ ret = virDomainHostdevDefPostParse(dev->data.hostdev, def, xmlopt);
+ break;
+
+ case VIR_DOMAIN_DEVICE_CONTROLLER:
+ ret = virDomainControllerDefPostParse(dev->data.controller);
+ break;
+
+ case VIR_DOMAIN_DEVICE_VSOCK:
+ virDomainVsockDefPostParse(dev->data.vsock);
+ ret = 0;
+ break;
+
+ case VIR_DOMAIN_DEVICE_MEMORY:
+ ret = virDomainMemoryDefPostParse(dev->data.memory, def);
+ break;
+
+ case VIR_DOMAIN_DEVICE_FS:
+ ret = virDomainFSDefPostParse(dev->data.fs);
+ break;
+
+ case VIR_DOMAIN_DEVICE_LEASE:
+ case VIR_DOMAIN_DEVICE_NET:
+ case VIR_DOMAIN_DEVICE_INPUT:
+ case VIR_DOMAIN_DEVICE_SOUND:
+ case VIR_DOMAIN_DEVICE_WATCHDOG:
+ case VIR_DOMAIN_DEVICE_GRAPHICS:
+ case VIR_DOMAIN_DEVICE_HUB:
+ case VIR_DOMAIN_DEVICE_REDIRDEV:
+ case VIR_DOMAIN_DEVICE_SMARTCARD:
+ case VIR_DOMAIN_DEVICE_MEMBALLOON:
+ case VIR_DOMAIN_DEVICE_NVRAM:
+ case VIR_DOMAIN_DEVICE_SHMEM:
+ case VIR_DOMAIN_DEVICE_TPM:
+ case VIR_DOMAIN_DEVICE_PANIC:
+ case VIR_DOMAIN_DEVICE_IOMMU:
+ case VIR_DOMAIN_DEVICE_AUDIO:
+ ret = 0;
+ break;
+
+ case VIR_DOMAIN_DEVICE_NONE:
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("unexpected VIR_DOMAIN_DEVICE_NONE"));
+ break;
+
+ case VIR_DOMAIN_DEVICE_LAST:
+ default:
+ virReportEnumRangeError(virDomainDeviceType, dev->type);
+ break;
+ }
+
+ return ret;
+}
+
+
+/**
+ * virDomainDefCheckUnsupportedMemoryHotplug:
+ * @def: domain definition
+ *
+ * Returns -1 if the domain definition would enable memory hotplug via the
+ * <maxMemory> tunable and reports an error. Otherwise returns 0.
+ */
+static int
+virDomainDefCheckUnsupportedMemoryHotplug(virDomainDef *def)
+{
+ /* memory hotplug tunables are not supported by this driver */
+ if (virDomainDefHasMemoryHotplug(def)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("memory hotplug tunables <maxMemory> are not "
+ "supported by this hypervisor driver"));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * virDomainDeviceDefCheckUnsupportedMemoryDevice:
+ * @dev: device definition
+ *
+ * Returns -1 if the device definition describes a memory device and reports an
+ * error. Otherwise returns 0.
+ */
+static int
+virDomainDeviceDefCheckUnsupportedMemoryDevice(virDomainDeviceDef *dev)
+{
+ /* This driver doesn't yet know how to handle memory devices */
+ if (dev->type == VIR_DOMAIN_DEVICE_MEMORY) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("memory devices are not supported by this driver"));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * virDomainDefRemoveOfflineVcpuPin:
+ * @def: domain definition
+ *
+ * This function removes vcpu pinning information from offline vcpus. This is
+ * designed to be used for drivers which don't support offline vcpupin.
+ */
+static void
+virDomainDefRemoveOfflineVcpuPin(virDomainDef *def)
+{
+ size_t i;
+ virDomainVcpuDef *vcpu;
+
+ for (i = 0; i < virDomainDefGetVcpusMax(def); i++) {
+ vcpu = virDomainDefGetVcpu(def, i);
+
+ if (vcpu && !vcpu->online && vcpu->cpumask) {
+ g_clear_pointer(&vcpu->cpumask, virBitmapFree);
+
+ VIR_WARN("Ignoring unsupported vcpupin for offline vcpu '%zu'", i);
+ }
+ }
+}
+
+
+#define UNSUPPORTED(FEATURE) (!((FEATURE) & xmlopt->config.features))
+/**
+ * virDomainDefPostParseCheckFeatures:
+ * @def: domain definition
+ * @xmlopt: XML parser option object
+ *
+ * This function checks that the domain configuration is supported according to
+ * the supported features for a given hypervisor. See virDomainDefFeatures and
+ * virDomainDefParserConfig.
+ *
+ * Returns 0 on success and -1 on error with an appropriate libvirt error.
+ */
+static int
+virDomainDefPostParseCheckFeatures(virDomainDef *def,
+ virDomainXMLOption *xmlopt)
+{
+ if (UNSUPPORTED(VIR_DOMAIN_DEF_FEATURE_MEMORY_HOTPLUG) &&
+ virDomainDefCheckUnsupportedMemoryHotplug(def) < 0)
+ return -1;
+
+ if (UNSUPPORTED(VIR_DOMAIN_DEF_FEATURE_OFFLINE_VCPUPIN))
+ virDomainDefRemoveOfflineVcpuPin(def);
+
+ if (UNSUPPORTED(VIR_DOMAIN_DEF_FEATURE_NAME_SLASH)) {
+ if (def->name && strchr(def->name, '/')) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("name %s cannot contain '/'"), def->name);
+ return -1;
+ }
+ }
+
+ if (UNSUPPORTED(VIR_DOMAIN_DEF_FEATURE_INDIVIDUAL_VCPUS) &&
+ def->individualvcpus) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("individual CPU state configuration is not supported"));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * virDomainDeviceDefPostParseCheckFeatures:
+ * @dev: device definition
+ * @xmlopt: XML parser option object
+ *
+ * This function checks that the device configuration is supported according to
+ * the supported features for a given hypervisor. See virDomainDefFeatures and
+ * virDomainDefParserConfig.
+ *
+ * Returns 0 on success and -1 on error with an appropriate libvirt error.
+ */
+static int
+virDomainDeviceDefPostParseCheckFeatures(virDomainDeviceDef *dev,
+ virDomainXMLOption *xmlopt)
+{
+ if (UNSUPPORTED(VIR_DOMAIN_DEF_FEATURE_MEMORY_HOTPLUG) &&
+ virDomainDeviceDefCheckUnsupportedMemoryDevice(dev) < 0)
+ return -1;
+
+ if (UNSUPPORTED(VIR_DOMAIN_DEF_FEATURE_NET_MODEL_STRING) &&
+ dev->type == VIR_DOMAIN_DEVICE_NET &&
+ dev->data.net->modelstr) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("driver does not support net model '%s'"),
+ dev->data.net->modelstr);
+ return -1;
+ }
+
+ return 0;
+}
+#undef UNSUPPORTED
+
+
+static int
+virDomainDeviceDefPostParse(virDomainDeviceDef *dev,
+ const virDomainDef *def,
+ unsigned int flags,
+ virDomainXMLOption *xmlopt,
+ void *parseOpaque)
+{
+ int ret;
+
+ if (xmlopt->config.devicesPostParseCallback) {
+ ret = xmlopt->config.devicesPostParseCallback(dev, def, flags,
+ xmlopt->config.priv,
+ parseOpaque);
+ if (ret < 0)
+ return ret;
+ }
+
+ if ((ret = virDomainDeviceDefPostParseCommon(dev, def, flags, xmlopt)) < 0)
+ return ret;
+
+ if (virDomainDeviceDefPostParseCheckFeatures(dev, xmlopt) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+int
+virDomainDeviceDefPostParseOne(virDomainDeviceDef *dev,
+ const virDomainDef *def,
+ unsigned int flags,
+ virDomainXMLOption *xmlopt,
+ void *parseOpaque)
+{
+ void *data = NULL;
+ int ret;
+
+ if (!parseOpaque && xmlopt->config.domainPostParseDataAlloc) {
+ if (xmlopt->config.domainPostParseDataAlloc(def, flags,
+ xmlopt->config.priv,
+ &data) < 0)
+ return -1;
+ parseOpaque = data;
+ }
+
+ ret = virDomainDeviceDefPostParse(dev, def, flags, xmlopt, parseOpaque);
+
+ if (data && xmlopt->config.domainPostParseDataFree)
+ xmlopt->config.domainPostParseDataFree(data);
+
+ return ret;
+}
+
+
+struct virDomainDefPostParseDeviceIteratorData {
+ virDomainXMLOption *xmlopt;
+ void *parseOpaque;
+ unsigned int parseFlags;
+};
+
+
+static int
+virDomainDefPostParseDeviceIterator(virDomainDef *def,
+ virDomainDeviceDef *dev,
+ virDomainDeviceInfo *info G_GNUC_UNUSED,
+ void *opaque)
+{
+ struct virDomainDefPostParseDeviceIteratorData *data = opaque;
+ return virDomainDeviceDefPostParse(dev, def,
+ data->parseFlags, data->xmlopt,
+ data->parseOpaque);
+}
+
+
+static int
+virDomainVcpuDefPostParse(virDomainDef *def)
+{
+ virDomainVcpuDef *vcpu;
+ size_t maxvcpus = virDomainDefGetVcpusMax(def);
+ size_t i;
+
+ for (i = 0; i < maxvcpus; i++) {
+ vcpu = virDomainDefGetVcpu(def, i);
+
+ /* impossible but some compilers don't like it */
+ if (!vcpu)
+ continue;
+
+ switch (vcpu->hotpluggable) {
+ case VIR_TRISTATE_BOOL_ABSENT:
+ if (vcpu->online)
+ vcpu->hotpluggable = VIR_TRISTATE_BOOL_NO;
+ else
+ vcpu->hotpluggable = VIR_TRISTATE_BOOL_YES;
+ break;
+
+ case VIR_TRISTATE_BOOL_NO:
+ if (!vcpu->online) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("vcpu '%zu' is both offline and not "
+ "hotpluggable"), i);
+ return -1;
+ }
+ break;
+
+ case VIR_TRISTATE_BOOL_YES:
+ case VIR_TRISTATE_BOOL_LAST:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+static int
+virDomainDefPostParseCPU(virDomainDef *def)
+{
+ if (!def->cpu)
+ return 0;
+
+ if (def->cpu->mode == VIR_CPU_MODE_CUSTOM &&
+ !def->cpu->model &&
+ def->cpu->check != VIR_CPU_CHECK_DEFAULT) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("check attribute specified for CPU with no model"));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int
+virDomainDefCollectBootOrder(virDomainDef *def G_GNUC_UNUSED,
+ virDomainDeviceDef *dev G_GNUC_UNUSED,
+ virDomainDeviceInfo *info,
+ void *data)
+{
+ GHashTable *bootHash = data;
+ g_autofree char *order = NULL;
+
+ if (info->bootIndex == 0)
+ return 0;
+
+ if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV &&
+ dev->data.hostdev->parentnet) {
+ /* This hostdev is a child of a higher level device
+ * (e.g. interface), and thus already being counted on the
+ * list for the other device type.
+ */
+ return 0;
+ }
+ order = g_strdup_printf("%u", info->bootIndex);
+
+ if (virHashLookup(bootHash, order)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("boot order '%s' used for more than one device"),
+ order);
+ return -1;
+ }
+
+ if (virHashAddEntry(bootHash, order, (void *) 1) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+static int
+virDomainDefBootOrderPostParse(virDomainDef *def)
+{
+ g_autoptr(GHashTable) bootHash = virHashNew(NULL);
+
+ if (virDomainDeviceInfoIterate(def, virDomainDefCollectBootOrder, bootHash) < 0)
+ return -1;
+
+ if (def->os.nBootDevs > 0 && virHashSize(bootHash) > 0) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("per-device boot elements cannot be used"
+ " together with os/boot elements"));
+ return -1;
+ }
+
+ if (def->os.nBootDevs == 0 && virHashSize(bootHash) == 0) {
+ def->os.nBootDevs = 1;
+ def->os.bootDevs[0] = VIR_DOMAIN_BOOT_DISK;
+ }
+
+ return 0;
+}
+
+
+static int
+virDomainDefPostParseVideo(virDomainDef *def,
+ void *opaque)
+{
+ if (def->nvideos == 0)
+ return 0;
+
+ if (def->videos[0]->type == VIR_DOMAIN_VIDEO_TYPE_NONE) {
+ char *alias;
+
+ /* we don't want to format any values we automatically fill in for
+ * videos into the XML, so clear them, but retain any user-assigned
+ * alias */
+ alias = g_steal_pointer(&def->videos[0]->info.alias);
+ virDomainVideoDefClear(def->videos[0]);
+ def->videos[0]->type = VIR_DOMAIN_VIDEO_TYPE_NONE;
+ def->videos[0]->info.alias = g_steal_pointer(&alias);
+ } else {
+ virDomainDeviceDef device = {
+ .type = VIR_DOMAIN_DEVICE_VIDEO,
+ .data.video = def->videos[0],
+ };
+
+ /* Mark the first video as primary. If the user specified
+ * primary="yes", the parser already inserted the device at
+ * def->videos[0]
+ */
+ def->videos[0]->primary = true;
+
+ /* videos[0] might have been added in AddImplicitDevices, after we've
+ * done the per-device post-parse */
+ if (virDomainDefPostParseDeviceIterator(def, &device,
+ NULL, opaque) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int
+virDomainDefRejectDuplicateControllers(virDomainDef *def)
+{
+ int max_idx[VIR_DOMAIN_CONTROLLER_TYPE_LAST];
+ virBitmap *bitmaps[VIR_DOMAIN_CONTROLLER_TYPE_LAST] = { NULL };
+ virDomainControllerDef *cont;
+ size_t nbitmaps = 0;
+ int ret = -1;
+ size_t i;
+
+ memset(max_idx, -1, sizeof(max_idx));
+
+ for (i = 0; i < def->ncontrollers; i++) {
+ cont = def->controllers[i];
+ if (cont->idx > max_idx[cont->type])
+ max_idx[cont->type] = cont->idx;
+ }
+
+ /* multiple USB controllers with the same index are allowed */
+ max_idx[VIR_DOMAIN_CONTROLLER_TYPE_USB] = -1;
+
+ for (i = 0; i < VIR_DOMAIN_CONTROLLER_TYPE_LAST; i++) {
+ if (max_idx[i] >= 0)
+ bitmaps[i] = virBitmapNew(max_idx[i] + 1);
+ nbitmaps++;
+ }
+
+ for (i = 0; i < def->ncontrollers; i++) {
+ cont = def->controllers[i];
+
+ if (max_idx[cont->type] == -1)
+ continue;
+
+ if (virBitmapIsBitSet(bitmaps[cont->type], cont->idx)) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Multiple '%s' controllers with index '%d'"),
+ virDomainControllerTypeToString(cont->type),
+ cont->idx);
+ goto cleanup;
+ }
+ ignore_value(virBitmapSetBit(bitmaps[cont->type], cont->idx));
+ }
+
+ ret = 0;
+ cleanup:
+ for (i = 0; i < nbitmaps; i++)
+ virBitmapFree(bitmaps[i]);
+ return ret;
+}
+
+
+static int
+virDomainDefRejectDuplicatePanics(virDomainDef *def)
+{
+ bool exists[VIR_DOMAIN_PANIC_MODEL_LAST];
+ size_t i;
+
+ for (i = 0; i < VIR_DOMAIN_PANIC_MODEL_LAST; i++)
+ exists[i] = false;
+
+ for (i = 0; i < def->npanics; i++) {
+ virDomainPanicModel model = def->panics[i]->model;
+ if (exists[model]) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Multiple panic devices with model '%s'"),
+ virDomainPanicModelTypeToString(model));
+ return -1;
+ }
+ exists[model] = true;
+ }
+
+ return 0;
+}
+
+
+static int
+virDomainDefPostParseCommon(virDomainDef *def,
+ struct virDomainDefPostParseDeviceIteratorData *data,
+ virDomainXMLOption *xmlopt)
+{
+ size_t i;
+
+ /* verify init path for container based domains */
+ if (def->os.type == VIR_DOMAIN_OSTYPE_EXE && !def->os.init) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("init binary must be specified"));
+ return -1;
+ }
+
+ if (virDomainVcpuDefPostParse(def) < 0)
+ return -1;
+
+ if (virDomainDefPostParseMemory(def, data->parseFlags) < 0)
+ return -1;
+
+ if (virDomainDefPostParseOs(def) < 0)
+ return -1;
+
+ virDomainDefPostParseMemtune(def);
+
+ if (virDomainDefRejectDuplicateControllers(def) < 0)
+ return -1;
+
+ if (virDomainDefRejectDuplicatePanics(def) < 0)
+ return -1;
+
+ if (def->os.type == VIR_DOMAIN_OSTYPE_HVM &&
+ !(data->xmlopt->config.features & VIR_DOMAIN_DEF_FEATURE_NO_BOOT_ORDER) &&
+ virDomainDefBootOrderPostParse(def) < 0)
+ return -1;
+
+ if (virDomainDefPostParseTimer(def) < 0)
+ return -1;
+
+ if (virDomainDefAddImplicitDevices(def, xmlopt) < 0)
+ return -1;
+
+ if (virDomainDefPostParseVideo(def, data) < 0)
+ return -1;
+
+ if (def->nserials != 0) {
+ virDomainDeviceDef device = {
+ .type = VIR_DOMAIN_DEVICE_CHR,
+ .data.chr = def->serials[0],
+ };
+
+ /* serials[0] might have been added in AddImplicitDevices, after we've
+ * done the per-device post-parse */
+ if (virDomainDefPostParseDeviceIterator(def, &device, NULL, data) < 0)
+ return -1;
+ }
+
+ /* Implicit SCSI controllers without a defined model might have
+ * been added in AddImplicitDevices, after we've done the per-device
+ * post-parse. */
+ for (i = 0; i < def->ncontrollers; i++) {
+ if (def->controllers[i]->model == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_DEFAULT &&
+ def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI) {
+ virDomainDeviceDef device = {
+ .type = VIR_DOMAIN_DEVICE_CONTROLLER,
+ .data.controller = def->controllers[i],
+ };
+ if (virDomainDefPostParseDeviceIterator(def, &device, NULL, data) < 0)
+ return -1;
+ }
+ }
+
+ /* clean up possibly duplicated metadata entries */
+ virXMLNodeSanitizeNamespaces(def->metadata);
+
+ virDomainDefPostParseGraphics(def);
+
+ if (virDomainDefPostParseCPU(def) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+static int
+virDomainDefPostParseCheckFailure(virDomainDef *def,
+ unsigned int parseFlags,
+ int ret)
+{
+ if (ret != 0)
+ def->postParseFailed = true;
+
+ if (ret <= 0)
+ return ret;
+
+ if (!(parseFlags & VIR_DOMAIN_DEF_PARSE_ALLOW_POST_PARSE_FAIL))
+ return -1;
+
+ virResetLastError();
+ return 0;
+}
+
+
+static void
+virDomainAssignControllerIndexes(virDomainDef *def)
+{
+ /* the index attribute of a controller is optional in the XML, but
+ * is required to be valid at any time after parse. When no index
+ * is provided for a controller, assign one automatically by
+ * looking at what indexes are already used for that controller
+ * type in the domain - the unindexed controller gets the lowest
+ * unused index.
+ */
+ size_t outer;
+
+ for (outer = 0; outer < def->ncontrollers; outer++) {
+ virDomainControllerDef *cont = def->controllers[outer];
+ virDomainControllerDef *prev = NULL;
+ size_t inner;
+
+ if (cont->idx != -1)
+ continue;
+
+ if (outer > 0 && IS_USB2_CONTROLLER(cont)) {
+ /* USB2 controllers are the only exception to the simple
+ * "assign the lowest unused index". A group of USB2
+ * "companions" should all be at the same index as other
+ * USB2 controllers in the group, but only do this
+ * automatically if it appears to be the intent. To prove
+ * intent: the USB controller on the list just prior to
+ * this one must also be a USB2 controller, and there must
+ * not yet be a controller with the exact same model of
+ * this one and the same index as the previously added
+ * controller (e.g., if this controller is a UHCI1, then
+ * the previous controller must be an EHCI1 or a UHCI[23],
+ * and there must not already be a UHCI1 controller with
+ * the same index as the previous controller). If all of
+ * these are satisfied, set this controller to the same
+ * index as the previous controller.
+ */
+ int prevIdx;
+
+ prevIdx = outer - 1;
+ while (prevIdx >= 0 &&
+ def->controllers[prevIdx]->type != VIR_DOMAIN_CONTROLLER_TYPE_USB)
+ prevIdx--;
+ if (prevIdx >= 0)
+ prev = def->controllers[prevIdx];
+ /* if the last USB controller isn't USB2, that breaks
+ * the chain, so we need a new index for this new
+ * controller
+ */
+ if (prev && !IS_USB2_CONTROLLER(prev))
+ prev = NULL;
+
+ /* if prev != NULL, we've found a potential index to
+ * use. Make sure this index isn't already used by an
+ * existing USB2 controller of the same model as the new
+ * one.
+ */
+ for (inner = 0; prev && inner < def->ncontrollers; inner++) {
+ if (def->controllers[inner]->type == VIR_DOMAIN_CONTROLLER_TYPE_USB &&
+ def->controllers[inner]->idx == prev->idx &&
+ def->controllers[inner]->model == cont->model) {
+ /* we already have a controller of this model with
+ * the proposed index, so we need to move to a new
+ * index for this controller
+ */
+ prev = NULL;
+ }
+ }
+ if (prev)
+ cont->idx = prev->idx;
+ }
+ /* if none of the above applied, prev will be NULL */
+ if (!prev)
+ cont->idx = virDomainControllerFindUnusedIndex(def, cont->type);
+ }
+}
+
+
+int
+virDomainDefPostParse(virDomainDef *def,
+ unsigned int parseFlags,
+ virDomainXMLOption *xmlopt,
+ void *parseOpaque)
+{
+ int ret = -1;
+ bool localParseOpaque = false;
+ struct virDomainDefPostParseDeviceIteratorData data = {
+ .xmlopt = xmlopt,
+ .parseFlags = parseFlags,
+ .parseOpaque = parseOpaque,
+ };
+
+ def->postParseFailed = false;
+
+ /* call the basic post parse callback */
+ if (xmlopt->config.domainPostParseBasicCallback) {
+ ret = xmlopt->config.domainPostParseBasicCallback(def,
+ xmlopt->config.priv);
+
+ if (virDomainDefPostParseCheckFailure(def, parseFlags, ret) < 0)
+ goto cleanup;
+ }
+
+ if (!data.parseOpaque &&
+ xmlopt->config.domainPostParseDataAlloc) {
+ ret = xmlopt->config.domainPostParseDataAlloc(def, parseFlags,
+ xmlopt->config.priv,
+ &data.parseOpaque);
+
+ if (virDomainDefPostParseCheckFailure(def, parseFlags, ret) < 0)
+ goto cleanup;
+ localParseOpaque = true;
+ }
+
+ /* this must be done before the hypervisor-specific callback,
+ * in case presence of a controller at a specific index is checked
+ */
+ virDomainAssignControllerIndexes(def);
+
+ /* call the domain config callback */
+ if (xmlopt->config.domainPostParseCallback) {
+ ret = xmlopt->config.domainPostParseCallback(def, parseFlags,
+ xmlopt->config.priv,
+ data.parseOpaque);
+ if (virDomainDefPostParseCheckFailure(def, parseFlags, ret) < 0)
+ goto cleanup;
+ }
+
+ if (virDomainChrIsaSerialDefPostParse(def) < 0)
+ return -1;
+
+ /* iterate the devices */
+ ret = virDomainDeviceInfoIterateFlags(def,
+ virDomainDefPostParseDeviceIterator,
+ DOMAIN_DEVICE_ITERATE_ALL_CONSOLES |
+ DOMAIN_DEVICE_ITERATE_MISSING_INFO,
+ &data);
+
+ if (virDomainDefPostParseCheckFailure(def, parseFlags, ret) < 0)
+ goto cleanup;
+
+ if ((ret = virDomainDefPostParseCommon(def, &data, xmlopt)) < 0)
+ goto cleanup;
+
+ if (xmlopt->config.assignAddressesCallback) {
+ ret = xmlopt->config.assignAddressesCallback(def, parseFlags,
+ xmlopt->config.priv,
+ data.parseOpaque);
+ if (virDomainDefPostParseCheckFailure(def, parseFlags, ret) < 0)
+ goto cleanup;
+ }
+
+ if ((ret = virDomainDefPostParseCheckFeatures(def, xmlopt)) < 0)
+ goto cleanup;
+
+ ret = 0;
+
+ cleanup:
+ if (localParseOpaque && xmlopt->config.domainPostParseDataFree)
+ xmlopt->config.domainPostParseDataFree(data.parseOpaque);
+
+ if (ret == 1)
+ ret = -1;
+
+ return ret;
+}
--- /dev/null
+/*
+ * domain_postparse.h: domain post parsing helpers
+ *
+ * Copyright (C) 2022 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "domain_conf.h"
+#include "virconftypes.h"
+
+int
+virDomainDeviceDefPostParseOne(virDomainDeviceDef *dev,
+ const virDomainDef *def,
+ unsigned int flags,
+ virDomainXMLOption *xmlopt,
+ void *parseOpaque);
+
+int
+virDomainDefPostParse(virDomainDef *def,
+ unsigned int parseFlags,
+ virDomainXMLOption *xmlopt,
+ void *parseOpaque);
'domain_capabilities.c',
'domain_conf.c',
'domain_nwfilter.c',
+ 'domain_postparse.c',
'domain_validate.c',
'moment_conf.c',
'numa_conf.c',
virDomainDefParseFile;
virDomainDefParseNode;
virDomainDefParseString;
-virDomainDefPostParse;
virDomainDefSave;
virDomainDefSetMemoryTotal;
virDomainDefSetVcpus;
virDomainConfVMNWFilterTeardown;
+# conf/domain_postparse.h
+virDomainDefPostParse;
+
+
# conf/domain_validate.h
virDomainActualNetDefValidate;
virDomainDefValidate;
#include "virerror.h"
#include "virlog.h"
#include "domain_conf.h"
+#include "domain_postparse.h"
#include "viralloc.h"
#include "virstring.h"
#include "storage_source_backingstore.h"
#include "xenxs_private.h"
#include "xen_xm.h"
#include "domain_conf.h"
+#include "domain_postparse.h"
#include "xen_common.h"
#define VIR_FROM_THIS VIR_FROM_XENXM
#include "util/virstring.h"
#include "util/virconf.h"
#include "conf/domain_conf.h"
+#include "conf/domain_postparse.h"
#include "virutil.h"
#define VIR_FROM_THIS VIR_FROM_LXC
#include "domain_audit.h"
#include "domain_cgroup.h"
#include "domain_driver.h"
+#include "domain_postparse.h"
#include "domain_validate.h"
#include "virpci.h"
#include "virpidfile.h"
#include "domain_audit.h"
#include "domain_cgroup.h"
#include "domain_nwfilter.h"
+#include "domain_postparse.h"
#include "domain_validate.h"
#include "locking/domain_lock.h"
#include "viruuid.h"
#include "viruri.h"
#include "virstring.h"
#include "virutil.h"
+#include "domain_postparse.h"
VIR_LOG_INIT("vmx.vmx");