package org.ovirt.engine.core.vdsbroker.builder.vminfo; import static org.ovirt.engine.core.common.utils.VmDeviceCommonUtils.updateVmDevicesBootOrder; import static org.ovirt.engine.core.vdsbroker.vdsbroker.IoTuneUtils.ioTuneListFrom; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import javax.annotation.PostConstruct; import javax.inject.Inject; import org.apache.commons.lang.StringUtils; import org.ovirt.engine.core.common.FeatureSupported; import org.ovirt.engine.core.common.businessentities.ArchitectureType; import org.ovirt.engine.core.common.businessentities.ChipsetType; import org.ovirt.engine.core.common.businessentities.DisplayType; import org.ovirt.engine.core.common.businessentities.GraphicsInfo; import org.ovirt.engine.core.common.businessentities.GraphicsType; import org.ovirt.engine.core.common.businessentities.VM; import org.ovirt.engine.core.common.businessentities.VmDevice; import org.ovirt.engine.core.common.businessentities.VmDeviceGeneralType; import org.ovirt.engine.core.common.businessentities.VmDeviceId; import org.ovirt.engine.core.common.businessentities.VmPayload; import org.ovirt.engine.core.common.businessentities.network.Network; import org.ovirt.engine.core.common.businessentities.network.NetworkFilter; import org.ovirt.engine.core.common.businessentities.network.VmInterfaceType; import org.ovirt.engine.core.common.businessentities.network.VmNetworkInterface; import org.ovirt.engine.core.common.businessentities.network.VnicProfile; import org.ovirt.engine.core.common.businessentities.qos.StorageQos; import org.ovirt.engine.core.common.businessentities.storage.CinderDisk; import org.ovirt.engine.core.common.businessentities.storage.Disk; import org.ovirt.engine.core.common.businessentities.storage.DiskImage; import org.ovirt.engine.core.common.businessentities.storage.DiskInterface; import org.ovirt.engine.core.common.businessentities.storage.DiskStorageType; import org.ovirt.engine.core.common.businessentities.storage.DiskVmElement; import org.ovirt.engine.core.common.businessentities.storage.LunDisk; import org.ovirt.engine.core.common.businessentities.storage.PropagateErrors; import org.ovirt.engine.core.common.businessentities.storage.VolumeFormat; import org.ovirt.engine.core.common.config.Config; import org.ovirt.engine.core.common.config.ConfigValues; import org.ovirt.engine.core.common.osinfo.OsRepository; import org.ovirt.engine.core.common.utils.SimpleDependencyInjector; import org.ovirt.engine.core.common.utils.VmCpuCountHelper; import org.ovirt.engine.core.common.utils.VmDeviceCommonUtils; import org.ovirt.engine.core.common.utils.VmDeviceType; import org.ovirt.engine.core.common.utils.customprop.VmPropertiesUtils; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.dao.VmDeviceDao; import org.ovirt.engine.core.dao.network.NetworkDao; import org.ovirt.engine.core.dao.network.VnicProfileDao; import org.ovirt.engine.core.utils.NetworkUtils; import org.ovirt.engine.core.utils.StringMapUtils; import org.ovirt.engine.core.utils.archstrategy.ArchStrategyFactory; import org.ovirt.engine.core.utils.ovf.xml.XmlTextWriter; import org.ovirt.engine.core.vdsbroker.architecture.GetControllerIndices; import org.ovirt.engine.core.vdsbroker.vdsbroker.VdsProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class generates a Libvirt's Domain XML from the internal representation of * a given Virtual Machine within ovirt-engine. * Note that any non-trivial logic that can be extracted into a utility method that could be * used when generating another representation of the VM should reside in {@link VmInfoBuildUtils}. * Also note that there should not be any call to VDSM from this class. If the generated XML * needs to contain information that the engine does not know about then either this information * should be added to the hosts/VMs monitoring or to GetCapabilities, or to represent the data * using place-holders that are replaced by VDSM (see {@link #writeLease(XmlTextWriter, VM)}). */ public class LibvirtVmXmlBuilder { private static final Logger log = LoggerFactory.getLogger(LibvirtVmXmlBuilder.class); // Namespace URIs: public static final String OVIRT_URI = "http://ovirt.org/vm/tune/1.0"; // Namespace prefixes: public static final String OVIRT_PREFIX = "ovirt"; /** Timeout for the boot menu, in milliseconds */ public static final int BOOT_MENU_TIMEOUT = 10000; private static final int LIBVIRT_PORT_AUTOSELECT = -1; @Inject private VmDeviceDao vmDeviceDao; @Inject private VmInfoBuildUtils vmInfoBuildUtils; @Inject private VnicProfileDao vnicProfileDao; @Inject private NetworkDao networkDao; private OsRepository osRepository; private String serialConsolePath; private boolean hypervEnabled; private XmlTextWriter writer; private Map<Guid, StorageQos> qosCache; private String cdInterface; private int payloadIndex; private int cdRomIndex; private VmDevice runOncePayload; private Map<String, Object> createInfo; private VM vm; public LibvirtVmXmlBuilder(Map<String, Object> createInfo, VM vm, VmDevice runOncePayload) { this.createInfo = createInfo; this.vm = vm; this.runOncePayload = runOncePayload; payloadIndex = -1; cdRomIndex = -1; } @PostConstruct private void init() { osRepository = SimpleDependencyInjector.getInstance().get(OsRepository.class); hypervEnabled = osRepository.isHypervEnabled(vm.getVmOsId(), vm.getCompatibilityVersion()); cdInterface = osRepository.getCdInterface( vm.getOs(), vm.getCompatibilityVersion(), ChipsetType.fromMachineType(vm.getEmulatedMachine())); writer = new XmlTextWriter(); qosCache = new HashMap<>(); } public String build() { writeHeader(); writeName(); writeId(); writeMemory(); writeIoThreads(); writeMaxMemory(); writevCpu(); writeMetadata(); writeSystemInfo(); writeClock(); writeFeatures(); writeCpu(); writeNumaTune(); writeDevices(); // note that this must be called after writeDevices to get the serial console, if exists writeOs(); return writer.getStringXML(); } private void writeHeader() { writer.writeStartDocument(false); writer.setPrefix(OVIRT_PREFIX, OVIRT_URI); writer.writeStartElement("domain"); writer.writeAttributeString("type", "kvm"); writer.writeNamespace(OVIRT_PREFIX, OVIRT_URI); } private void writeName() { writer.writeElement("name", vm.getName()); } private void writeId() { writer.writeElement("uuid", vm.getId().toString()); } private void writeMemory() { int memSizeKB = vm.getMemSizeMb() * 1024; writer.writeElement("memory", String.valueOf(memSizeKB)); writer.writeElement("currentMemory", String.valueOf(memSizeKB)); } private void writeIoThreads() { if (vm.getNumOfIoThreads() == 0) { return; } writer.writeElement("iothreads", String.valueOf(vm.getNumOfIoThreads())); } private void writeMaxMemory() { if (!FeatureSupported.hotPlugMemory(vm.getCompatibilityVersion(), vm.getClusterArch()) // the next check is because QEMU fails if memory and maxMemory are the same || vm.getVmMemSizeMb() == vm.getMaxMemorySizeMb()) { return; } writer.writeStartElement("maxMemory"); writer.writeAttributeString("slots", Config.getValue(ConfigValues.MaxMemorySlots).toString()); writer.writeRaw(String.valueOf(vm.getMaxMemorySizeMb() * 1024)); writer.writeEndElement(); } private void writevCpu() { writer.writeStartElement("vcpu"); writer.writeAttributeString("current", String.valueOf(vm.getNumOfCpus())); writer.writeRaw(FeatureSupported.supportedInConfig(ConfigValues.HotPlugCpuSupported, vm.getCompatibilityVersion(), vm.getClusterArch()) ? VmCpuCountHelper.calcMaxVCpu(vm, vm.getClusterCompatibilityVersion()).toString() : String.valueOf(vm.getNumOfCpus())); writer.writeEndElement(); } @SuppressWarnings("incomplete-switch") private void writeCpu() { writer.writeStartElement("cpu"); String cpuType = createInfo.get(VdsProperties.cpuType).toString(); switch(vm.getClusterArch().getFamily()) { case x86: writer.writeAttributeString("match", "exact"); // is this a list of strings??.. switch(cpuType) { case "hostPassthrough": writer.writeAttributeString("mode", "host-passthrough"); break; case "hostModel": writer.writeAttributeString("mode", "host-model"); break; default: writer.writeStartElement("model"); writer.writeRaw(cpuType); // TODO: features writer.writeEndElement(); break; } break; case ppc: writer.writeStartElement("model"); writer.writeRaw(cpuType); writer.writeEndElement(); } if ((boolean) Config.getValue(ConfigValues.SendSMPOnRunVm)) { writer.writeStartElement("topology"); writer.writeAttributeString("cores", Integer.toString(vm.getCpuPerSocket())); writer.writeAttributeString("threads", Integer.toString(vm.getThreadsPerCpu())); int vcpus = FeatureSupported.supportedInConfig(ConfigValues.HotPlugCpuSupported, vm.getCompatibilityVersion(), vm.getClusterArch()) ? VmCpuCountHelper.calcMaxVCpu(vm, vm.getClusterCompatibilityVersion()) : vm.getNumOfCpus(); writer.writeAttributeString("sockets", String.valueOf(vcpus / vm.getCpuPerSocket() / vm.getThreadsPerCpu())); writer.writeEndElement(); } if (StringUtils.isNotEmpty(vm.getCpuPinning())) { writer.writeStartElement("cputune"); for (String pin : vm.getCpuPinning().split("_")) { writer.writeStartElement("vcpupin"); final String[] split = pin.split("#"); writer.writeAttributeString("vcpu", split[0]); writer.writeAttributeString("cpuset", split[1]); writer.writeEndElement(); } writer.writeEndElement(); } else { // TODO Map<String, Object> cpuPinDict = NumaSettingFactory.buildCpuPinningWithNumaSetting(vmNumaNodes, totalVdsNumaNodes); } if (createInfo.containsKey(VdsProperties.VM_NUMA_NODES)) { writer.writeStartElement("numa"); @SuppressWarnings("unchecked") List<Map<String, Object>> createVmNumaNodes = (List<Map<String, Object>>) createInfo.get(VdsProperties.VM_NUMA_NODES); for (Map<String, Object> vmNumaNode : createVmNumaNodes) { writer.writeStartElement("cell"); writer.writeAttributeString("cpus", vmNumaNode.get(VdsProperties.NUMA_NODE_CPU_LIST).toString()); writer.writeAttributeString("memory", String.valueOf(Integer.parseInt((String) vmNumaNode.get(VdsProperties.VM_NUMA_NODE_MEM)) * 1024)); writer.writeEndElement(); } writer.writeEndElement(); } writer.writeEndElement(); } private void writeSystemInfo() { if (vm.getClusterArch().getFamily() != ArchitectureType.x86) { return; } /** <sysinfo type="smbios"> <system> <entry name="manufacturer">Fedora</entry> <entry name="product">Virt-Manager</entry> <entry name="version">0.8.2-3.fc14</entry> <entry name="serial">32dfcb37-5af1-552b-357c-be8c3aa38310</entry> <entry name="uuid">c7a5fdbd-edaf-9455-926a-d65c16db1809</entry> </system> </sysinfo> */ writer.writeStartElement("sysinfo"); writer.writeAttributeString("type", "smbios"); writer.writeStartElement("system"); writer.writeStartElement("entry"); writer.writeAttributeString("name", "manufacturer"); writer.writeRaw("oVirt"); writer.writeEndElement(); writer.writeStartElement("entry"); writer.writeAttributeString("name", "product"); writer.writeRaw("OS-NAME:"); writer.writeEndElement(); writer.writeStartElement("entry"); writer.writeAttributeString("name", "version"); writer.writeRaw("OS-VERSION:"); writer.writeEndElement(); writer.writeStartElement("entry"); writer.writeAttributeString("name", "serial"); writer.writeRaw("HOST-SERIAL:"); writer.writeEndElement(); writer.writeStartElement("entry"); writer.writeAttributeString("name", "uuid"); writer.writeRaw(vm.getId().toString()); writer.writeEndElement(); writer.writeEndElement(); writer.writeEndElement(); } private void writeNumaTune() { if (!createInfo.containsKey(VdsProperties.NUMA_TUNE)) { return; } // <numatune> // <memory mode='strict' nodeset='0-1'/> // <memnode cellid='0' mode='strict' nodeset='1'> // </numatune> @SuppressWarnings("unchecked") Map<String, Object> numaTuneSetting = (Map<String, Object>) createInfo.get(VdsProperties.NUMA_TUNE); String nodeSet = (String) numaTuneSetting.get(VdsProperties.NUMA_TUNE_NODESET); @SuppressWarnings("unchecked") List<Map<String, String>> memNodes = (List<Map<String, String>>) numaTuneSetting.get(VdsProperties.NUMA_TUNE_MEMNODES); if (nodeSet != null || memNodes != null) { writer.writeStartElement("numatune"); String mode = (String) createInfo.get(VdsProperties.NUMA_TUNE_MODE); if (nodeSet != null) { writer.writeStartElement("memory"); writer.writeAttributeString("mode", mode); writer.writeAttributeString("modeset", nodeSet); writer.writeEndElement(); } if (memNodes != null) { for (Map<String, String> memnode : memNodes) { writer.writeStartElement("memnode"); writer.writeAttributeString("mode", mode); writer.writeAttributeString("cellid", (String) memnode.get(VdsProperties.NUMA_TUNE_VM_NODE_INDEX)); writer.writeAttributeString("nodeset", (String) memnode.get(VdsProperties.NUMA_TUNE_NODESET)); writer.writeEndElement(); } } writer.writeEndElement(); } } private void writeOs() { writer.writeStartElement("os"); writer.writeStartElement("type"); writer.writeAttributeString("arch", vm.getClusterArch().toString()); writer.writeAttributeString("machine", vm.getEmulatedMachine() != null ? vm.getEmulatedMachine() : vmInfoBuildUtils.getEmulatedMachineByClusterArch(vm.getClusterArch())); writer.writeRaw("hvm"); writer.writeEndElement(); // No need to the boot section that VDSM defines if (!StringUtils.isEmpty(vm.getInitrdUrl())) { writer.writeElement("initrd", vm.getInitrdUrl()); } if (!StringUtils.isEmpty(vm.getKernelUrl())) { writer.writeElement("kernel", vm.getKernelUrl()); if (!StringUtils.isEmpty(vm.getKernelParams())) { writer.writeElement("cmdline", vm.getKernelParams()); } } if (vm.getClusterArch().getFamily() == ArchitectureType.x86) { writer.writeStartElement("smbios"); writer.writeAttributeString("mode", "sysinfo"); writer.writeEndElement(); } if (vm.isBootMenuEnabled()) { writer.writeStartElement("bootmenu"); writer.writeAttributeString("enable", "yes"); writer.writeAttributeString("timeout", String.valueOf(BOOT_MENU_TIMEOUT)); writer.writeEndElement(); } if (serialConsolePath != null && vm.getClusterArch().getFamily() == ArchitectureType.x86) { writer.writeStartElement("bios"); writer.writeAttributeString("useserial", "yes"); writer.writeEndElement(); } writer.writeEndElement(); } private void writeClock() { // <clock offset="variable" adjustment="-3600"> // <timer name="rtc" tickpolicy="catchup"> // </clock> // for hyperv: // <clock offset="variable" adjustment="-3600"> // <timer name="hypervclock" present="yes"> // <timer name="rtc" tickpolicy="catchup"> // </clock> writer.writeStartElement("clock"); writer.writeAttributeString("offset", "variable"); writer.writeAttributeString("adjustment", String.valueOf(vmInfoBuildUtils.getVmTimeZone(vm))); if (hypervEnabled) { writer.writeStartElement("timer"); writer.writeAttributeString("name", "hypervclock"); writer.writeAttributeString("present", "yes"); writer.writeEndElement(); } writer.writeStartElement("timer"); writer.writeAttributeString("name", "rtc"); writer.writeAttributeString("tickpolicy", "catchup"); writer.writeEndElement(); writer.writeStartElement("timer"); writer.writeAttributeString("name", "pit"); writer.writeAttributeString("tickpolicy", "delay"); writer.writeEndElement(); if (vm.getClusterArch().getFamily() == ArchitectureType.x86) { writer.writeStartElement("timer"); writer.writeAttributeString("name", "hpet"); writer.writeAttributeString("present", "no"); writer.writeEndElement(); } writer.writeEndElement(); } private void writeFeatures() { if (vm.getClusterArch().getFamily() != ArchitectureType.x86) { return; } // Currently only // <features> // <acpi/> // <features/> // for hyperv: // <features> // <acpi/> // <hyperv> // <relaxed state='on'/> // </hyperv> // <features/> boolean acpiEnabled = vm.getAcpiEnable(); if (!acpiEnabled && !hypervEnabled) { return; } writer.writeStartElement("features"); if (acpiEnabled) { writer.writeStartElement("acpi"); writer.writeEndElement(); } if (hypervEnabled) { writer.writeStartElement("hyperv"); writer.writeStartElement("relaxed"); writer.writeAttributeString("state", "on"); writer.writeEndElement(); writer.writeStartElement("vapic"); writer.writeAttributeString("state", "on"); writer.writeEndElement(); writer.writeStartElement("spinlocks"); writer.writeAttributeString("state", "on"); writer.writeAttributeString("retries", "8191"); writer.writeEndElement(); writer.writeEndElement(); } writer.writeEndElement(); } private void writeMetadata() { // <domain xmlns:ovirt="http://ovirt.org/vm/tune/1.0"> // ... // <metadata> // <ovirt:qos xmlns:ovirt=> // </metadata> // ... // </domain> writer.writeStartElement("metadata"); writer.writeStartElement(OVIRT_URI, "qos"); writer.writeEndElement(); writer.writeEndElement(); } private void writeDevices() { List<VmDevice> devices = vmDeviceDao.getVmDeviceByVmId(vm.getId()); // replacement of some devices in run-once mode should eventually be done by the run-command devices = overrideDevicesForRunOnce(devices); writer.writeStartElement("devices"); writeInput(); writeGuestAgentChannels(); if (vm.getClusterArch() == ArchitectureType.ppc64 || vm.getClusterArch() == ArchitectureType.ppc64le) { writeEmulator(); } Map<DiskInterface, Integer> controllerIndexMap = ArchStrategyFactory.getStrategy(vm.getClusterArch()).run(new GetControllerIndices()).returnValue(); int virtioScsiIndex = controllerIndexMap.get(DiskInterface.VirtIO_SCSI); List<VmDevice> interfaceDevices = new ArrayList<>(); List<VmDevice> diskDevices = new ArrayList<>(); List<VmDevice> cdromDevices = new ArrayList<>(); VmDevice floppyDevice = null; boolean spiceExists = false; for (VmDevice device : devices) { if (!device.isPlugged()) { continue; } switch (device.getType()) { case BALLOON: writeBalloon(device); break; case SMARTCARD: writeSmartcard(device); break; case WATCHDOG: writeWatchdog(device); break; case MEMORY: // memory devices are only used for hot-plug break; case VIDEO: writeVideo(device); break; case CONTROLLER: switch(device.getDevice()) { case "virtio-serial": device.getSpecParams().put("index", 0); device.getSpecParams().put("ports", 16); break; case "virtio-scsi": device.setDevice(VdsProperties.Scsi); device.getSpecParams().put("index", virtioScsiIndex++); device.getSpecParams().put("model", "virtio-scsi"); break; } writeController(device); break; case GRAPHICS: writeGraphics(device); spiceExists = spiceExists || device.getDevice().equals("spice"); break; case SOUND: writeSound(device); break; case RNG: writeRng(device); break; case CONSOLE: writeConsole(device); if (device.getSpecParams() != null && "serial".equals(device.getSpecParams().get("consoleType"))) { serialConsolePath = getSerialConsolePath(device); } break; case DISK: switch(VmDeviceType.getByName(device.getDevice())) { case CDROM: cdromDevices.add(device); break; case DISK: diskDevices.add(device); break; case FLOPPY: if (floppyDevice == null || !VmPayload.isPayload(floppyDevice.getSpecParams())) { floppyDevice = device; } break; default: } break; case INTERFACE: interfaceDevices.add(device); break; case REDIR: writeRedir(device); break; case REDIRDEV: break; case CHANNEL: break; case HOSTDEV: break; case UNKNOWN: break; default: break; } } writeSerialConsole(serialConsolePath); writeLease(); if (spiceExists) { writeSpiceVmcChannel(); } updateBootOrder(diskDevices, cdromDevices, interfaceDevices); writeInterfaces(interfaceDevices); writeCdRom(cdromDevices); writeFloppy(floppyDevice); // we must write the disk after writing cd-rom and floppy to know reserved indices writeDisks(diskDevices); writer.writeEndElement(); } private List<VmDevice> overrideDevicesForRunOnce(List<VmDevice> devices) { if (!vm.isRunOnce()) { return devices; } // video device handling DisplayType displayType = vm.getDefaultDisplayType(); if (displayType != null) { // remove existing video device devices = devices.stream() .filter(dev -> dev.getType() != VmDeviceGeneralType.VIDEO) .collect(Collectors.toList()); // add new video device if (displayType != DisplayType.none) { devices.add(vmInfoBuildUtils.createVideoDeviceByDisplayType(displayType, vm.getId())); } } // graphics device handling if (displayType == DisplayType.none || (vm.getGraphicsInfos() != null && !vm.getGraphicsInfos().isEmpty())) { // remove existing graphics devices devices = devices.stream() .filter(dev -> dev.getType() != VmDeviceGeneralType.GRAPHICS) .collect(Collectors.toList()); if (displayType != DisplayType.none) { // add new graphics devices Map<GraphicsType, GraphicsInfo> infos = vm.getGraphicsInfos(); Map<String, Object> specParamsFromVm = new HashMap<>(); vmInfoBuildUtils.addVmGraphicsOptions(infos, specParamsFromVm, vm); devices.addAll(vmInfoBuildUtils.createGraphicsDevices(infos, specParamsFromVm, vm.getId())); } } if (runOncePayload != null) { devices = devices.stream() .filter(dev -> !VmPayload.isPayload(dev.getSpecParams())) .collect(Collectors.toList()); devices.add(runOncePayload); } // the user may specify floppy path while there is no device in the database if (!StringUtils.isEmpty(vm.getFloppyPath()) && !devices.stream().anyMatch(dev -> !dev.getDevice().equals(VmDeviceType.FLOPPY.getName()))) { devices.add(vmInfoBuildUtils.createFloppyDevice(vm)); } return devices; } @SafeVarargs private final void updateBootOrder(List<VmDevice> ... bootableDevices) { List<VmDevice> managedAndPluggedBootableDevices = Arrays.stream(bootableDevices) .flatMap(Collection::stream) .filter(VmDevice::isManaged) .collect(Collectors.toList()); updateVmDevicesBootOrder( vm.getBootSequence(), managedAndPluggedBootableDevices, vm.getInterfaces(), VmDeviceCommonUtils.extractDiskVmElements(vm)); } private void writeLease() { if (vm.getLeaseStorageDomainId() == null) { return; } writer.writeStartElement("lease"); writer.writeElement("key", vm.getId().toString()); writer.writeElement("lockspace", vm.getLeaseStorageDomainId().toString()); writer.writeStartElement("target"); writer.writeAttributeString("offset", String.format("LEASE-OFFSET:%s:%s", vm.getId(), vm.getLeaseStorageDomainId())); writer.writeAttributeString("path", String.format("LEASE-PATH:%s:%s", vm.getId(), vm.getLeaseStorageDomainId())); writer.writeEndElement(); writer.writeEndElement(); } private void writeInterfaces(List<VmDevice> devices) { Map<VmDeviceId, VmNetworkInterface> devIdToNic = vm.getInterfaces().stream() .collect(Collectors.toMap(nic -> new VmDeviceId(nic.getId(), nic.getVmId()), nic -> nic)); devices.forEach(dev -> writeInterface(dev, devIdToNic.get(dev.getId()))); } private void writeDisks(List<VmDevice> devices) { Map<VmDeviceId, VmDevice> deviceIdToDevice = devices.stream() .collect(Collectors.toMap(VmDevice::getId, dev -> dev)); int ideIndex = -1; int scsiIndex = -1; int virtioIndex = -1; DiskInterface cdDiskInterface = DiskInterface.forValue(cdInterface); for (Disk disk : vmInfoBuildUtils.getSortedDisks(vm)) { VmDevice device = deviceIdToDevice.get(new VmDeviceId(disk.getId(), vm.getId())); DiskVmElement dve = disk.getDiskVmElementForVm(vm.getId()); DiskInterface diskInterface = dve.getDiskInterface(); int index = 0; switch(diskInterface) { case IDE: ideIndex++; if (cdDiskInterface == diskInterface) { while (ideIndex == payloadIndex || ideIndex == cdRomIndex) { ideIndex++; } } index = ideIndex; break; case VirtIO: virtioIndex++; if (cdDiskInterface == diskInterface) { while (virtioIndex == payloadIndex || virtioIndex == cdRomIndex) { virtioIndex++; } } index = virtioIndex; break; case SPAPR_VSCSI: case VirtIO_SCSI: scsiIndex++; if (cdDiskInterface == diskInterface) { while (scsiIndex == payloadIndex || scsiIndex == cdRomIndex) { scsiIndex++; } } index = scsiIndex; break; } if (device.isManaged()) { writeManagedDisk(device, disk, dve, index); } // TODO: else } } private void writeConsole(VmDevice device) { // <console type='pty'> // <target type='serial' port='0'/> // </console> // or: // <console type='pty'> // <target type='virtio' port='0'/> // </console> // or: // <console type='unix'> // <source mode='bind' path='/path/to/${vmid}.sock'> // <target type='virtio' port='0'/> // </console> writer.writeStartElement("console"); String path = getSerialConsolePath(device); if (!path.isEmpty()) { writer.writeAttributeString("type", "unix"); writer.writeStartElement("source"); writer.writeAttributeString("path", path); writer.writeAttributeString("mode", "bind"); writer.writeEndElement(); } else { writer.writeAttributeString("type", "pty"); } writer.writeStartElement("target"); Object consoleTypeFromSpecParams = device.getSpecParams().get("consoleType"); String consoleType = consoleTypeFromSpecParams != null ? consoleTypeFromSpecParams.toString() : "virtio"; writer.writeAttributeString("type", consoleType); writer.writeAttributeString("port", "0"); writer.writeEndElement(); writer.writeEndElement(); } public void writeEmulator() { writer.writeStartElement("emulator"); writer.writeAttributeString("text", String.format("/usr/bin/qemu-system-%s", vm.getClusterArch())); writer.writeEndElement(); } private void writeSpiceVmcChannel() { writer.writeStartElement("channel"); writer.writeAttributeString("type", "spicevmc"); writer.writeStartElement("target"); writer.writeAttributeString("type", "virtio"); writer.writeAttributeString("name", "com.redhat.spice.0"); writer.writeEndElement(); writer.writeEndElement(); } private void writeGuestAgentChannels() { // <channel type='unix'> // <target type='virtio' name='org.linux-kvm.port.0'/> // <source mode='bind' path='/tmp/socket'/> // </channel> writer.writeStartElement("channel"); writer.writeAttributeString("type", "unix"); writer.writeStartElement("target"); writer.writeAttributeString("type", "virtio"); writer.writeAttributeString("name", "ovirt-guest-agent.0"); writer.writeEndElement(); writer.writeStartElement("source"); writer.writeAttributeString("mode", "bind"); writer.writeAttributeString("path", String.format("/var/lib/libvirt/qemu/channels/%s.ovirt-guest-agent.0", vm.getId())); writer.writeEndElement(); writer.writeEndElement(); writer.writeStartElement("channel"); writer.writeStartElement("target"); writer.writeAttributeString("type", "virtio"); writer.writeAttributeString("name", "org.qemu.guest_agent.0"); writer.writeEndElement(); writer.writeStartElement("source"); writer.writeAttributeString("mode", "bind"); writer.writeAttributeString("path", String.format("/var/lib/libvirt/qemu/channels/%s.org.qemu.guest_agent.0", vm.getId())); writer.writeEndElement(); writer.writeEndElement(); } private void writeSerialConsole(String path) { if (serialConsolePath == null) { return; } // <serial type='pty'> // <target port='0'> // </serial> // or: // <serial type='unix'> // <source mode='bind' // path='/var/run/ovirt-vmconsole-console/${VMID}.sock'/> // <target port='0'/> // </serial> writer.writeStartElement("serial"); if (!path.isEmpty()) { writer.writeAttributeString("type", "unix"); writer.writeStartElement("source"); writer.writeAttributeString("path", path); writer.writeAttributeString("mode", "bind"); writer.writeEndElement(); } else { writer.writeAttributeString("type", "pty"); } writer.writeStartElement("target"); writer.writeAttributeString("port", "0"); writer.writeEndElement(); writer.writeEndElement(); } private String getSerialConsolePath(VmDevice device) { Object enableSocketFromSpecParams = device.getSpecParams().get("enableSocket"); return enableSocketFromSpecParams != null && Boolean.parseBoolean(enableSocketFromSpecParams.toString()) ? String.format("/var/run/ovirt-vmconsole-console/%s.sock", vm.getId()) : ""; } private void writeRedir(VmDevice device) { // <redirdev bus='usb' type='spicevmc'> // <address type='usb' bus='0' port='1'/> // </redirdev> writer.writeStartElement("redirdev"); writer.writeAttributeString("type", "spicevmc"); writer.writeAttributeString("bus", "usb"); writeDeviceAliasAndAddress(device); writer.writeEndElement(); } private void writeRng(VmDevice device) { // <rng model='virtio'> // <rate period="2000" bytes="1234"/> // <backend model='random'>/dev/random</backend> // </rng> writer.writeStartElement("rng"); writer.writeAttributeString("model", "virtio"); Map<String, Object> specParams = device.getSpecParams(); if (specParams.containsKey("bytes")) { writer.writeStartElement("rate"); writer.writeAttributeString("bytes", specParams.get("bytes").toString()); if (specParams.containsKey("period")) { writer.writeAttributeString("period", specParams.get("period").toString()); } writer.writeEndElement(); } writer.writeStartElement("backend"); writer.writeAttributeString("model", "random"); switch(specParams.get("source").toString()) { case "random": writer.writeRaw("/dev/random"); break; case "urandom": writer.writeRaw("/dev/urandom"); break; case "hwrng": writer.writeRaw("/dev/hwrng"); break; } writer.writeEndElement(); writer.writeEndElement(); } private void writeSound(VmDevice device) { writer.writeStartElement("sound"); writer.writeAttributeString("model", device.getDevice()); writeDeviceAliasAndAddress(device); writer.writeEndElement(); } private void writeGraphics(VmDevice device) { GraphicsType graphicsType = GraphicsType.fromString(device.getDevice()); if (graphicsType == null) { log.error("Unsupported graphics type: {}", device.getDevice()); return; } // <graphics type='spice' port='5900' tlsPort='5901' autoport='yes' // listen='0' keymap='en-us' // passwdValidTo='1970-01-01T00:00:01'> // <listen type='address' address='0'/> // <clipboard copypaste='no'/> // </graphics> // or: // <graphics type='vnc' port='5900' autoport='yes' listen='0' // keymap='en-us' passwdValidTo='1970-01-01T00:00:01'> // <listen type='address' address='0'/> // </graphics> writer.writeStartElement("graphics"); writer.writeAttributeString("type", device.getDevice()); writer.writeAttributeString("port", String.valueOf(LIBVIRT_PORT_AUTOSELECT)); writer.writeAttributeString("autoport", "yes"); // TODO: defaultMode writer.writeAttributeString("passwd", "*****"); writer.writeAttributeString("passwdValidTo", "1970-01-01T00:00:01"); Network displayNetwork = vmInfoBuildUtils.getDisplayNetwork(vm); if (displayNetwork == null) { writer.writeAttributeString("listen", "0"); } switch (graphicsType) { case SPICE: writer.writeAttributeString("tlsPort", String.valueOf(LIBVIRT_PORT_AUTOSELECT)); if (!vm.isSpiceFileTransferEnabled()) { writer.writeStartElement("filetransfer"); writer.writeAttributeString("enable", "no"); writer.writeEndElement(); } if (!vm.isSpiceCopyPasteEnabled()) { writer.writeStartElement("clipboard"); writer.writeAttributeString("copypaste", "no"); writer.writeEndElement(); } if ((boolean) Config.getValue(ConfigValues.SSLEnabled)) { String channels = Config.getValue(ConfigValues.SpiceSecureChannels, vm.getCompatibilityVersion().toString()); for (String channel : channels.split(",")) { writer.writeStartElement("channel"); writer.writeAttributeString("name", channel); writer.writeAttributeString("mode", "secure"); writer.writeEndElement(); } } break; case VNC: writer.writeAttributeString("keymap", vm.getDynamicData().getVncKeyboardLayout() != null ? vm.getDynamicData().getVncKeyboardLayout() : vm.getDefaultVncKeyboardLayout() != null ? vm.getDefaultVncKeyboardLayout() : Config.getValue(ConfigValues.VncKeyboardLayout)); break; } if (displayNetwork != null) { writer.writeStartElement("listen"); String displayIp = (String) device.getSpecParams().get("displayIp"); if (displayIp == null) { writer.writeAttributeString("type", "network"); writer.writeAttributeString("network", String.format("DISPLAY-NETWORK:%s", displayNetwork.getName())); } else { writer.writeAttributeString("type", "address"); writer.writeAttributeString("address", displayIp); } writer.writeEndElement(); } writer.writeEndElement(); } private void writeController(VmDevice device) { writer.writeStartElement("controller"); writer.writeAttributeString("type", device.getDevice()); if (device.getSpecParams().containsKey(VdsProperties.Model)) { writer.writeAttributeString("model", device.getSpecParams().get(VdsProperties.Model).toString()); } if (device.getSpecParams().containsKey(VdsProperties.Index)) { writer.writeAttributeString("index", device.getSpecParams().get(VdsProperties.Index).toString()); } if (device.getSpecParams().containsKey("ports")) { writer.writeAttributeString("ports", device.getSpecParams().get("ports").toString()); } // TODO: master?? writeDeviceAliasAndAddress(device); writer.writeEndElement(); } /** * TODO: * add qemu_drive_cache configurable like in VDSM? */ private void writeManagedDisk( VmDevice device, Disk disk, DiskVmElement dve, int index) { // <disk type='file' device='disk' snapshot='no'> // <driver name='qemu' type='qcow2' cache='none'/> // <source file='/path/to/image'/> // <target dev='hda' bus='ide'/> // <serial>54-a672-23e5b495a9ea</serial> // </disk> writer.writeStartElement("disk"); writeGeneralDiskAttributes(device, disk, dve); writeDiskTarget(dve, index); writeDiskSource(disk); writeDiskDriver(device, disk, dve); writeDeviceAliasAndAddress(device); writeBootOrder(device.getBootOrder()); if (disk.getDiskStorageType() != DiskStorageType.LUN) { writer.writeElement("serial", disk.getId().toString()); } if (device.getReadOnly()) { writer.writeElement("readonly"); } if (device.getSnapshotId() == null && disk.isShareable()) { writer.writeElement("shareable"); } if (disk.getDiskStorageType() == DiskStorageType.IMAGE) { writeIoTune((DiskImage) disk); } if (disk.getDiskStorageType() == DiskStorageType.CINDER /** && case RBD */) { writeNetworkDiskAuth((CinderDisk) disk); } writer.writeEndElement(); } private void writeNetworkDiskAuth(CinderDisk cinderDisk) { Map<String, Object> connectionInfoData = cinderDisk.getCinderConnectionInfo().getData(); boolean authEnabled = (boolean) connectionInfoData.get(VdsProperties.CinderAuthEnabled); if (authEnabled) { writer.writeStartElement("auth"); writer.writeAttributeString("username", (String) connectionInfoData.get(VdsProperties.CinderAuthUsername)); writer.writeStartElement("secret"); writer.writeAttributeString("type", (String) connectionInfoData.get(VdsProperties.CinderSecretType)); writer.writeAttributeString("uuid", (String) connectionInfoData.get(VdsProperties.CinderSecretUuid)); writer.writeEndElement(); writer.writeEndElement(); } } private void writeIoTune(DiskImage diskImage) { if (!qosCache.containsKey(diskImage.getDiskProfileId())) { qosCache.put(diskImage.getDiskProfileId(), vmInfoBuildUtils.loadStorageQos(diskImage)); } StorageQos storageQos = qosCache.get(diskImage.getDiskProfileId()); if (storageQos == null) { return; } writer.writeStartElement("iotune"); ioTuneListFrom(storageQos).forEach(pair -> writer.writeAttributeString(pair.getFirst(), pair.getSecond().toString())); writer.writeEndElement(); } private void writeDiskDriver(VmDevice device, Disk disk, DiskVmElement dve) { writer.writeStartElement("driver"); writer.writeAttributeString("name", "qemu"); if (FeatureSupported.passDiscardSupported(vm.getCompatibilityVersion()) && dve.isPassDiscard()) { writer.writeAttributeString("discard", "unmap"); } if (device.getSpecParams().containsKey("pinToIoThread")) { writer.writeAttributeString("iothread", device.getSpecParams().get("pinToIoThread").toString()); } switch (disk.getDiskStorageType()) { case IMAGE: DiskImage diskImage = (DiskImage) disk; writer.writeAttributeString("io", "threads"); writer.writeAttributeString("type", diskImage.getVolumeFormat() == VolumeFormat.COW ? "qcow2" : "raw"); writer.writeAttributeString("error_policy", disk.getPropagateErrors() == PropagateErrors.On ? "enospace" : "stop"); break; case LUN: writer.writeAttributeString("io", "native"); writer.writeAttributeString("type", "raw"); writer.writeAttributeString("error_policy", disk.getPropagateErrors() == PropagateErrors.On ? "enospace" : "stop"); break; case CINDER: // case RBD writer.writeAttributeString("type", "raw"); writer.writeAttributeString("error_policy", "stop"); writer.writeAttributeString("io", "threads"); break; } if (device.getSnapshotId() != null) { // transient disk /** Force the cache to be writethrough, which is qemu's default. This is done to ensure that we don't ever use cache=none for transient disks, since we create them in /var/run/vdsm which may end up on tmpfs and don't support O_DIRECT, and qemu uses O_DIRECT when cache=none and hence hotplug might fail with error that one can take eternity to debug the reason behind it! */ writer.writeAttributeString("cache", "writethrough"); } else { switch (dve.getDiskInterface()) { case VirtIO: // TODO: if custom property is set... default: writer.writeAttributeString("cache", "none"); } } writer.writeEndElement(); } private void writeDiskSource(Disk disk) { writer.writeStartElement("source"); switch (disk.getDiskStorageType()) { case IMAGE: DiskImage diskImage = (DiskImage) disk; writer.writeAttributeString( "file", String.format("/rhev/data-center/%s/%s/images/%s/%s", diskImage.getStoragePoolId(), diskImage.getStorageIds().get(0), diskImage.getId(), diskImage.getImageId())); break; case LUN: LunDisk lunDisk = (LunDisk) disk; writer.writeAttributeString( "file", String.format("/dev/mapper/%s", lunDisk.getLun().getLUNId())); break; case CINDER: // case RBD CinderDisk cinderDisk = (CinderDisk) disk; Map<String, Object> connectionInfoData = cinderDisk.getCinderConnectionInfo().getData(); writer.writeAttributeString("protocol", cinderDisk.getCinderConnectionInfo().getDriverVolumeType()); writer.writeAttributeString("name", connectionInfoData.get("name").toString()); List<String> hostAddresses = (List<String>) connectionInfoData.get("hosts"); List<String> hostPorts = (List<String>) connectionInfoData.get("ports"); // Looping over hosts addresses to create 'hosts' element // (Cinder should ensure that the addresses and ports lists are synced in order). for (int i = 0; i < hostAddresses.size(); i++) { writer.writeStartElement("host"); writer.writeAttributeString("name", hostAddresses.get(i)); writer.writeAttributeString("port", hostPorts.get(i)); // If no transport is specified, "tcp" is assumed. writer.writeEndElement(); } break; } writer.writeEndElement(); } private void writeDiskTarget(DiskVmElement dve, int index) { writer.writeStartElement("target"); switch (dve.getDiskInterface()) { case IDE: writer.writeAttributeString("dev", vmInfoBuildUtils.makeDiskName("ide", index)); writer.writeAttributeString("bus", "ide"); break; case VirtIO: writer.writeAttributeString("dev", vmInfoBuildUtils.makeDiskName("virtio", index)); writer.writeAttributeString("bus", "virtio"); // TODO: index break; case VirtIO_SCSI: writer.writeAttributeString("dev", vmInfoBuildUtils.makeDiskName("scsi", index)); writer.writeAttributeString("bus", "scsi"); // TODO address break; case SPAPR_VSCSI: // TODO address, name break; default: log.error("Unsupported interface type, ISCSI interface type is not supported."); } writer.writeEndElement(); } private void writeGeneralDiskAttributes(VmDevice device, Disk disk, DiskVmElement dve) { writer.writeAttributeString("snapshot", "no"); switch (disk.getDiskStorageType()) { case IMAGE: writer.writeAttributeString("type", "file"); // TODO type of storage domain break; case LUN: writer.writeAttributeString("type", "block"); break; case CINDER: // case RBD writer.writeAttributeString("type", "network"); break; } switch (dve.getDiskInterface()) { case VirtIO: case IDE: writer.writeAttributeString("device", device.getDevice()); break; case VirtIO_SCSI: if (disk.getDiskStorageType() == DiskStorageType.LUN && disk.isScsiPassthrough()) { writer.writeAttributeString("device", VmDeviceType.LUN.getName()); writer.writeAttributeString("sgio", disk.getSgio().toString().toLowerCase()); } else { writer.writeAttributeString("device", device.getDevice()); } // TODO break; case SPAPR_VSCSI: break; } } private void writeFloppy(VmDevice device) { if (device == null) { return; } // <disk device="floppy" snapshot="no" type="file"> // <source file="/var/run/vdsm/payload/8b5fa6b8-9c57-4d7c-80cb-64537eea560f.6e38a5ccb3c6b2b674086e9d07126a03.img" startupPolicy="optional" /> // <target bus="fdc" dev="fda" /> // <readonly /> // </disk> writer.writeStartElement("disk"); writer.writeAttributeString("type", "file"); writer.writeAttributeString("device", "floppy"); writer.writeAttributeString("snapshot", "no"); writer.writeStartElement("source"); writer.writeAttributeString("file", VmPayload.isPayload(device.getSpecParams()) ? "PAYLOAD:" : vm.getFloppyPath()); writer.writeAttributeString("startupPolicy", "optional"); writer.writeEndElement(); writer.writeStartElement("target"); writer.writeAttributeString("dev", vmInfoBuildUtils.makeDiskName(VdsProperties.Fdc, 0)); // IDE slot 2 is reserved by VDSM to CDROM writer.writeAttributeString("bus", VdsProperties.Fdc); writer.writeEndElement(); writer.writeElement("readonly"); writeDeviceAliasAndAddress(device); writer.writeEndElement(); } private void writeCdRom(List<VmDevice> devices) { // <disk type='file' device='cdrom'> // <driver name='qemu' type='raw'/> // <source startupPolicy='optional'/> // <backingStore/> // <target dev='hdc' bus='ide'/> // <readonly/> // <alias name='ide0-1-0'/> // <address type='drive' controller='0' bus='1' target='0' unit='0'/> // </disk> devices.stream().filter(d -> VmPayload.isPayload(d.getSpecParams())).forEach(device -> { writer.writeStartElement("disk"); writer.writeAttributeString("type", "file"); writer.writeAttributeString("device", "cdrom"); writer.writeAttributeString("snapshot", "no"); writer.writeStartElement("source"); writer.writeAttributeString("file", "PAYLOAD:"); writer.writeAttributeString("startupPolicy", "optional"); writer.writeEndElement(); payloadIndex = VmDeviceCommonUtils.getCdPayloadDeviceIndex(cdInterface); writer.writeStartElement("target"); writer.writeAttributeString("dev", vmInfoBuildUtils.makeDiskName(cdInterface, payloadIndex)); writer.writeAttributeString("bus", cdInterface); writer.writeEndElement(); writer.writeElement("readonly"); if ("scsi".equals(cdInterface)) { int index = VmDeviceCommonUtils.getCdPayloadDeviceIndex(cdInterface); writeAddress(vmInfoBuildUtils.createAddressForScsiDisk(0, index)); } writer.writeEndElement(); }); VmDevice nonPayload = devices.stream() .filter(d -> !VmPayload.isPayload(d.getSpecParams())) .findAny().orElse(null); if (nonPayload != null || (vm.isRunOnce() && !StringUtils.isEmpty(vm.getCdPath()))) { // add a device that points to vm.getCdPath() writer.writeStartElement("disk"); writer.writeAttributeString("type", "file"); writer.writeAttributeString("device", "cdrom"); writer.writeAttributeString("snapshot", "no"); writer.writeStartElement("source"); writer.writeAttributeString("file", vm.getCdPath()); writer.writeAttributeString("startupPolicy", "optional"); writer.writeEndElement(); cdRomIndex = VmDeviceCommonUtils.getCdDeviceIndex(cdInterface); writer.writeStartElement("target"); writer.writeAttributeString("dev", vmInfoBuildUtils.makeDiskName(cdInterface, cdRomIndex)); writer.writeAttributeString("bus", cdInterface); writer.writeEndElement(); writer.writeElement("readonly"); if (nonPayload != null) { writeDeviceAliasAndAddress(nonPayload); writeBootOrder(nonPayload.getBootOrder()); } writer.writeEndElement(); } } private void writeInterface(VmDevice device, VmNetworkInterface nic) { // <interface type="bridge"> // <mac address="aa:bb:dd:dd:aa:bb"/> // <model type="virtio"/> // <source bridge="engine"/> // [<driver name="vhost/qemu" queues="int"/>] // [<filterref filter='filter name'> // [<parameter name='parameter name' value='parameter value'>] // </filterref>] // [<tune><sndbuf>0</sndbuf></tune>] // [<link state='up|down'/>] // [<bandwidth> // [<inbound average="int" [burst="int"] [peak="int"]/>] // [<outbound average="int" [burst="int"] [peak="int"]/>] // </bandwidth>] // </interface> // // -- or -- a slightly different SR-IOV network interface // <interface type='hostdev' managed='no'> // <driver name='vfio'/> // <source> // <address type='pci' domain='0x0000' bus='0x00' slot='0x07' // function='0x0'/> // </source> // <mac address='52:54:00:6d:90:02'/> // <vlan> // <tag id=100/> // </vlan> // <address type='pci' domain='0x0000' bus='0x00' slot='0x07' // function='0x0'/> // <boot order='1'/> // </interface> writer.writeStartElement("interface"); Map<String, String> properties = VmPropertiesUtils.getInstance().getVMProperties( vm.getCompatibilityVersion(), vm.getStaticData()); // TODO: driver switch (device.getDevice()) { case "bridge": writer.writeAttributeString("type", "bridge"); writer.writeStartElement("model"); VmInterfaceType ifaceType = nic.getType() != null ? VmInterfaceType.forValue(nic.getType()) : VmInterfaceType.rtl8139; writer.writeAttributeString("type", ifaceType == VmInterfaceType.pv ? "virtio" : ifaceType.getInternalName()); writer.writeEndElement(); writer.writeStartElement("link"); writer.writeAttributeString("state", nic.isLinked() ? "up" : "down"); writer.writeEndElement(); // The source element looks different when using legacy or OVS bridge writer.writeStartElement("source"); writer.writeAttributeString("bridge", String.format("NIC-BRIDGE:%s", nic.getNetworkName())); writer.writeEndElement(); break; case "hostdev": writer.writeAttributeString("type", "hostdev"); writer.writeAttributeString("managed", "no"); writer.writeStartElement("driver"); writer.writeAttributeString("name", "vfio"); writer.writeEndElement(); VnicProfile vnicProfile = vnicProfileDao.get(nic.getVnicProfileId()); Network network = networkDao.get(vnicProfile.getNetworkId()); if (NetworkUtils.isVlan(network)) { writer.writeStartElement("vlan"); writer.writeStartElement("tag"); writer.writeAttributeString("id", network.getVlanId().toString()); writer.writeEndElement(); writer.writeEndElement(); } // String vfDeviceName = vm.getPassthroughVnicToVfMap().get(nic.getId()); // writer.writeStartElement("$SOURCE:" + vfDeviceName+"$"); // writer.writeEndElement(); break; } writeDeviceAliasAndAddress(device); writeBootOrder(device.getBootOrder()); writer.writeStartElement("mac"); writer.writeAttributeString("address", nic.getMacAddress()); writer.writeEndElement(); NetworkFilter networkFilter = vmInfoBuildUtils.fetchVnicProfileNetworkFilter(nic); if (networkFilter != null) { writer.writeStartElement("filterref"); writer.writeAttributeString("filter", networkFilter.getName()); writer.writeEndElement(); } if (properties.containsKey("sndbuf")) { writer.writeStartElement("tune"); writer.writeStartElement("sndbuf"); writer.writeRaw(properties.get("sndbuf")); writer.writeEndElement(); writer.writeEndElement(); } if (device.getSpecParams().containsKey("inbound") || device.getSpecParams().containsKey("outbound")) { writer.writeStartElement("bandwidth"); Map<String, Object> map = new HashMap<>(); vmInfoBuildUtils.addProfileDataToNic(map, vm, device, nic); @SuppressWarnings("unchecked") Map<String, String> inboundMap = (Map<String, String>) map.get("inbound"); if (inboundMap != null && !inboundMap.isEmpty()) { writer.writeStartElement("inbound"); writer.writeAttributeString("average", inboundMap.get("average")); writer.writeAttributeString("burst", inboundMap.get("burst")); writer.writeAttributeString("peak", inboundMap.get("peak")); writer.writeEndElement(); } @SuppressWarnings("unchecked") Map<String, String> outboundMap = (Map<String, String>) map.get("outbound"); if (outboundMap != null && !outboundMap.isEmpty()) { writer.writeStartElement("outbound"); writer.writeAttributeString("average", outboundMap.get("average")); writer.writeAttributeString("burst", outboundMap.get("burst")); writer.writeAttributeString("peak", outboundMap.get("peak")); writer.writeEndElement(); } writer.writeEndElement(); } writer.writeEndElement(); } private void writeBalloon(VmDevice device) { // <memballoon model='virtio'> // <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> // </memballoon> writer.writeStartElement("membaloon"); writer.writeAttributeString("model", device.getSpecParams().get(VdsProperties.Model).toString()); writeDeviceAliasAndAddress(device); writer.writeEndElement(); } private void writeSmartcard(VmDevice device) { // <smartcard mode='passthrough' type='spicevmc'> // <address/> // </smartcard> writer.writeStartElement("smartcard"); writer.writeAttributeString("mode", device.getSpecParams().get("mode").toString()); writer.writeAttributeString("type", device.getSpecParams().get("type").toString()); writeDeviceAliasAndAddress(device); writer.writeEndElement(); } private void writeWatchdog(VmDevice device) { // <watchdog model='i6300esb' action='reset'> // <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/> // </watchdog> writer.writeStartElement("watchdog"); Object model = device.getSpecParams().get(VdsProperties.Model); writer.writeAttributeString("model", model != null ? model.toString() : "i6300esb"); Object action = device.getSpecParams().get(VdsProperties.action); writer.writeAttributeString("action", action != null ? action.toString() : "none"); writeDeviceAliasAndAddress(device); writer.writeEndElement(); } private void writeMemory(VmDevice device) { // <memory model='dimm'> // <target> // <size unit='KiB'>524287</size> // <node>1</node> // </target> // </memory> writer.writeStartElement("memory"); writer.writeAttributeString("model", "dimm"); writer.writeStartElement("target"); writer.writeStartElement("size"); writer.writeAttributeString("unit", "KiB"); writer.writeRaw(String.valueOf(vm.getMemSizeMb() * 1000)); writer.writeEndElement(); writer.writeElement("node", "1"); // TODO writer.writeEndElement(); writer.writeEndElement(); } private void writeVideo(VmDevice device) { writer.writeStartElement("video"); writer.writeStartElement("model"); writer.writeAttributeString("type", device.getDevice()); Object vram = device.getSpecParams().get(VdsProperties.VIDEO_VRAM); writer.writeAttributeString("vram", vram != null ? vram.toString() : "32768"); Object heads = device.getSpecParams().get(VdsProperties.VIDEO_HEADS); writer.writeAttributeString("heads", heads != null ? heads.toString() : "1"); if (device.getSpecParams().containsKey(VdsProperties.VIDEO_RAM)) { writer.writeAttributeString("ram", device.getSpecParams().get(VdsProperties.VIDEO_RAM).toString()); } if (device.getSpecParams().containsKey(VdsProperties.VIDEO_VGAMEM)) { writer.writeAttributeString("vgamem", device.getSpecParams().get(VdsProperties.VIDEO_VGAMEM).toString()); } writer.writeEndElement(); writeDeviceAliasAndAddress(device); writer.writeEndElement(); } private void writeBootOrder(int order) { if (order > 0) { writer.writeStartElement("boot"); writer.writeAttributeString("order", String.valueOf(order)); writer.writeEndElement(); } } private void writeDeviceAliasAndAddress(VmDevice device) { writeAddress(StringMapUtils.string2Map(device.getAddress())); String alias = device.getAlias(); if (StringUtils.isNotEmpty(alias)) { writer.writeStartElement("alias"); writer.writeAttributeString("name", alias); writer.writeEndElement(); } } private void writeAddress(Map<String, String> addressMap) { if (!addressMap.isEmpty()) { writer.writeStartElement("address"); addressMap.entrySet().forEach(x -> writer.writeAttributeString(x.getKey(), x.getValue())); writer.writeEndElement(); } } private void writeInput() { writer.writeStartElement("input"); boolean tabletEnable = vm.getGraphicsInfos().size() == 1 && vm.getGraphicsInfos().containsKey(GraphicsType.VNC); if (tabletEnable) { writer.writeAttributeString("type", "tablet"); writer.writeAttributeString("bus", "usb"); } else if (vm.getClusterArch().getFamily() == ArchitectureType.x86) { writer.writeAttributeString("type", "mouse"); writer.writeAttributeString("bus", "ps2"); } else { writer.writeAttributeString("type", "mouse"); writer.writeAttributeString("bus", "usb"); } writer.writeEndElement(); } // private int getBootableDiskIndex(Disk disk) { // int index = ArchStrategyFactory.getStrategy(vm.getClusterArch()) // .run(new GetBootableDiskIndex(numOfReservedScsiIndexes)) // .returnValue(); // log.info("Bootable disk '{}' set to index '{}'", disk.getId(), index); // return index; // } }