XML Generation & Domain Configuration
Complete guide to libvirt domain XML generation - XMLGenerator class, domain structure, configuration options
XML Generation & Domain Configuration
Introduction
The XMLGenerator class is Infinibay's comprehensive XML builder for libvirt domain configurations. Spanning 980+ lines of code (backend/app/utils/VirtManager/xmlGenerator.ts), it generates valid libvirt domain XML for KVM/QEMU virtual machines with support for memory configuration, vCPU management, storage devices, networking, graphics protocols (SPICE/VNC), advanced features (UEFI, TPM 2.0), GPU passthrough, and VirtIO channels.
The class integrates seamlessly with CreateMachineService during VM creation, providing a fluent interface for building complex domain configurations. It handles XML normalization for loading existing configurations, NUMA-aware CPU pinning, high-resolution graphics, and all aspects of modern virtualization.
XMLGenerator Architecture
Class Structure
Lines 16-55
The XMLGenerator follows the builder pattern for fluent configuration:
const xmlGenerator = new XMLGenerator(name, id, os);
xmlGenerator.setMemory(8); // 8 GB
xmlGenerator.setVCPUs(4);
xmlGenerator.addDisk('/path/to/disk.qcow2', 'virtio', 100);
const xml = xmlGenerator.generate(); // Generate XML string
Core Properties
- xml: Internal domain XML object (JavaScript representation, not string)
- id: Machine UUID (used for file paths, socket names, UEFI NVRAM)
- os: Operating system type (affects configuration choices like Hyper-V enlightenments)
Design Pattern
- Builder pattern: Chain method calls to configure VM incrementally
- Fluent interface: Most methods return void, modifying internal state
- Final generation:
generate()converts JavaScript object to XML string using xml2js
Domain XML Structure
Root Element
Lines 22-51
<domain type='kvm'>
<name>vm-name</name>
<metadata>
<!-- libosinfo metadata for OS detection -->
</metadata>
<devices>
<!-- Array of device configurations -->
</devices>
</domain>
- type: 'kvm' (KVM virtualization for hardware acceleration)
- name: VM name (machine.internalName)
- metadata: libosinfo metadata helps guest tools detect OS version
- devices: Array of device configurations (disks, network, graphics, etc.)
OS Configuration
Line 52
<os>
<type arch='x86_64' machine='q35'>hvm</type>
<boot dev='hd'/>
<boot dev='cdrom'/>
</os>
- type: 'hvm' (hardware virtual machine for full virtualization)
- arch: 'x86_64' (64-bit x86 architecture)
- machine: 'q35' (modern chipset with PCIe support, better than legacy i440fx)
Initial Devices
Lines 38-49
Every domain starts with a SATA controller (index 0) for disk/CDROM devices:
<controller type='sata' index='0'/>
XML Normalization System
Purpose
Lines 57-72
The normalization system ensures external XML (from existing VMs or database) is compatible with XMLGenerator methods. It converts all single elements to arrays for consistency.
Load Method
Lines 61-72
xmlGenerator.load(externalXml);
Accepts:
- Full XML object with
<domain>root - Just the domain part (auto-wraps)
Calls normalizeXmlStructure() to ensure array structure.
Normalization Process
Lines 79-238
The normalizer converts all relevant elements to arrays:
Top-level properties:
- name, uuid, memory, vcpu, os, features, devices → arrays
OS properties:
- type, boot, loader, nvram, firmware.feature → arrays
Device properties:
- disk, controller, interface, channel, graphics, video, input, hostdev, etc. → arrays
Nested properties:
- driver, source, target, address → arrays
CPU tuning:
- cputune.vcpupin → array
Clock:
- clock.timer → array
Power management:
- on_poweroff, on_reboot, on_crash → arrays
Why Normalization?
xml2js has inconsistent behavior:
- Single element: Parsed as object
{ type: 'hvm' } - Multiple elements: Parsed as array
[{ type: 'hvm' }, { type: 'loader' }]
XMLGenerator expects arrays for consistency. Normalization ensures code like xml.domain.devices[0].disk.forEach(...) works whether there's one disk or many.
Memory Configuration
setMemory Method
Lines 259-291
xmlGenerator.setMemory(8); // 8 GB
Conversion: GB → KiB (1 GB = 1024 * 1024 KiB = 1048576 KiB)
Sets:
<memory>: Maximum memory (in KiB)<currentMemory>: Current allocated memory (same as maximum)
Adds virtio memballoon device for dynamic memory adjustment:
<memballoon model='virtio'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x08' function='0x0'/>
</memballoon>
NUMA Memory Distribution
Lines 266-290
If NUMA topology exists in CPU configuration, memory is distributed across NUMA cells:
Single cell: All memory to first cell Multiple cells: Evenly distribute with remainder to first cell
Example: 8192 MiB across 2 cells → 4096 MiB per cell
Updates cell memory and unit attributes in cpu.numa.cell array.
CPU Configuration
setVCPUs Method
Lines 293-327
xmlGenerator.setVCPUs(4); // 4 vCPUs
Sets:
- vCPU count with
placement='static'(CPUs don't move between physical cores) - CPU mode: 'host-passthrough' (exposes all host CPU features to guest)
- check: 'none' (skip CPU compatibility checks—trust configuration)
Hypervisor Features
Lines 304-310
For Windows guests, add Hyper-V enlightenments for better performance:
<hyperv>
<relaxed state='on'/>
<vapic state='on'/>
<spinlocks state='on' retries='8191'/>
</hyperv>
- relaxed: Relaxed timing for better performance (reduces timer-related VM exits)
- vapic: Virtual APIC for efficient interrupt handling
- spinlocks: Spinlock optimization with 8191 retries before yielding
Clock Configuration
Lines 311-321
<clock offset='localtime'>
<timer name='rtc' tickpolicy='catchup'/>
<timer name='pit' tickpolicy='delay'/>
<timer name='hpet' present='no'/>
<timer name='hypervclock' present='yes'/>
</clock>
- offset: 'localtime' (use local time instead of UTC—better for Windows)
- rtc: Real-time clock with catchup policy (catch up if VM was paused)
- pit: Programmable interval timer with delay policy
- hpet: High precision event timer (disabled—not needed with modern timers)
- hypervclock: Hyper-V clock (enabled for Windows for better time synchronization)
Power Management
Lines 323-326
<pm>
<suspend-to-mem enabled='no'/>
<suspend-to-disk enabled='no'/>
</pm>
Disables suspend-to-mem and suspend-to-disk to prevent unexpected VM suspension.
CPU Pinning and Optimization
setCpuPinningOptimization Method
Lines 329-406
xmlGenerator.setCpuPinningOptimization(strategy?);
Parameters:
strategy: Optional CPU pinning strategy (defaults to HybridRandomStrategy)
Process:
- Get vCPU count from domain XML
- Call
strategy.setCpuPinning(vcpuCount) - Receive
CpuPinningConfigwith cputune and cpu elements - Apply configuration to domain XML
Configuration Application
Lines 352-402
cputune: vCPU to physical CPU mappings
<cputune>
<vcpupin vcpu='0' cpuset='0'/>
<vcpupin vcpu='1' cpuset='1'/>
<vcpupin vcpu='2' cpuset='8'/>
<vcpupin vcpu='3' cpuset='9'/>
</cputune>
cpu: CPU mode and attributes
<cpu mode='host-model' match='exact' check='none'>
<topology sockets='2' cores='2' threads='1'/>
<cache mode='emulate' level='3'/>
<maxphysaddr mode='emulate' bits='42'/>
<numa>
<cell id='0' cpus='0-1' memory='4096' unit='MiB'/>
<cell id='1' cpus='2-3' memory='4096' unit='MiB'/>
</numa>
</cpu>
Logging
Line 405: Log CPU configuration for debugging:
CPU configuration applied: {"cputune":...,"cpu":...}
See CPU Pinning Strategies for detailed strategy documentation.
Boot Configuration
setBootOrder Method
Lines 409-411
xmlGenerator.setBootOrder(['hd', 'cdrom']);
Creates boot elements in specified order:
<os>
<boot dev='hd'/>
<boot dev='cdrom'/>
</os>
Boot devices: 'fd' (floppy), 'hd' (hard disk), 'cdrom', 'network'
Order matters: First device is tried first. Example: Boot from hard disk, fallback to CDROM for installation.
UEFI Boot
Lines 491-513
xmlGenerator.setUEFI();
Enables UEFI firmware (required for Windows 11, recommended for Windows 10 and modern Linux):
OVMF firmware search paths:
/usr/share/OVMF/OVMF_CODE.ms.fd/usr/share/OVMF/OVMF_CODE_4M.ms.fd/usr/share/edk2/ovmf/OVMF_CODE.ms.fd/usr/share/qemu/OVMF_CODE.ms.fd
NVRAM path: ${INFINIBAY_BASE_DIR}/uefi/${machineId}_VARS.fd
Configuration:
<os>
<loader readonly='yes' type='pflash' secure='yes'>/usr/share/OVMF/OVMF_CODE.ms.fd</loader>
<nvram>/opt/infinibay/uefi/abc-123_VARS.fd</nvram>
</os>
Throws error if OVMF not found. Install OVMF package:
- Ubuntu/Debian:
apt install ovmf - Fedora/RHEL:
dnf install edk2-ovmf
Storage Configuration
addDisk Method
Lines 515-539
xmlGenerator.addDisk('/var/lib/libvirt/images/vm.qcow2', 'virtio', 100);
Parameters:
path: Volume path (fromStorageVol.getPath(), never hardcode!)bus: 'ide', 'sata', or 'virtio' (virtio recommended for best performance)size: Size in GB (for metadata only, doesn't resize actual file)
Bus to device mapping:
ide→hd(hda, hdb, ...)sata→sd(sda, sdb, ...)virtio→vd(vda, vdb, ...)
Configuration:
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2' cache='writeback' discard='unmap' iothread='4'/>
<source file='/var/lib/libvirt/images/vm.qcow2'/>
<target dev='vda' bus='virtio'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/>
</disk>
- driver.cache: 'writeback' (better performance, less safe—writes cached before disk)
- driver.discard: 'unmap' (TRIM support for thin provisioning)
- driver.iothread: 4 threads for better I/O performance
getNextBus Method
Lines 644-668
Automatically assigns next available device letter:
Example: If sda, sdb, vda exist → getNextBus('sd') returns 'sdc'
Process:
- Filter devices by bus type ('sd', 'vd', 'hd')
- Sort alphabetically
- Increment last character (sdb → sdc)
addCDROM Method
Lines 572-593
xmlGenerator.addCDROM('/path/to/iso', 'sata');
Similar to addDisk but:
device='cdrom'(not 'disk')type='raw'(ISOs are raw format, not qcow2)<readonly/>element (CDROMs are read-only)
Used for installation media and VirtIO drivers.
addVirtIODrivers Method
Lines 567-570
await xmlGenerator.addVirtIODrivers();
Resolves VirtIO ISO path using VirtIOPathResolver and adds as CDROM. Required for Windows to detect virtio disks and network adapters.
Windows Setup: Looks for drivers in E:\viostor\w10\amd64 (or similar) when installing.
Deprecated setStorage Method
Lines 546-553
// DEPRECATED: Don't use this
xmlGenerator.setStorage(100, '/hardcoded/path.qcow2');
Warning: Hardcodes disk path—bad practice!
Use instead: addDisk() with actual volume path from StorageVol.getPath().
Network Configuration
addNetworkInterface Method
Lines 413-433
xmlGenerator.addNetworkInterface('default', 'virtio');
Parameters:
network: Network name or bridge namemodel: 'virtio' (recommended), 'e1000' (legacy Intel adapter)
Auto-detection:
- Network starts with 'br' or 'virbr' → type='bridge', source.bridge
- Otherwise → type='network', source.network
Configuration:
<interface type='network'>
<source network='default'/>
<model type='virtio'/>
<address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
</interface>
addNetwork Method
Lines 555-565
xmlGenerator.addNetwork(NetworkModel.VIRTIO, 'default');
Parameters:
model: NetworkModel enum (VIRTIO or E1000)network: Network name
Adds network interface with virtio model and multi-queue support:
<interface type='network'>
<source network='default'/>
<model type='virtio'/>
<driver name='vhost' queues='4'/>
</interface>
Note: Current implementation always uses virtio model with vhost driver regardless of the provided model parameter.
Multi-queue (queues='4'): Better performance with multiple vCPUs (parallel packet processing).
Firewall Filter Integration
Lines 435-464
xmlGenerator.addNWFilter('filter-name', 100); // Custom priority
xmlGenerator.addDepartmentNWFilter('dept-filter'); // Priority 100
xmlGenerator.addVMNWFilter('vm-filter'); // Priority 200
Adds nwfilter reference to all network interfaces:
<interface type='network'>
<source network='default'/>
<filterref filter='vm-filter'>
<parameter name='CTRL_IP_LEARNING' value='any'/>
<parameter name='priority' value='200'/>
</filterref>
</interface>
Priority:
- Department filter: 100 (higher priority, evaluated first)
- VM filter: 200 (lower priority, can override department rules)
Important: Only the VM filter is attached to the interface in the VM XML. The VM filter inherits from the department filter via a <filterref> element in the filter definition itself (not in the VM XML). Libvirt does NOT support multiple <filterref> elements in a single interface.
Applies to: Both network and bridge type interfaces
Graphics Configuration
addSPICE Method
Lines 910-979
const password = xmlGenerator.addSPICE(true, true); // enableAudio, enableOpenGL
Returns: Random 8-character alphanumeric password
Configuration:
<graphics type='spice' autoport='yes' listen='0.0.0.0' passwd='abc12345'>
<listen type='address' address='0.0.0.0'/>
<image compression='auto_glz'/>
<jpeg compression='auto'/>
<zlib compression='auto'/>
<streaming mode='filter'/>
<clipboard copypaste='yes'/>
<filetransfer enable='yes'/>
<mouse mode='server'/>
<gl enable='yes' rendernode='/dev/dri/renderD128'/>
</graphics>
Features:
- autoport: Libvirt assigns port automatically (5900-5999 range)
- Compression: GLZ (images), JPEG (photos), zlib (general data)
- Video streaming: Optimizes video content
- Clipboard sharing: Copy/paste between host and guest
- File transfer: Drag-and-drop files
- Mouse mode: 'server' (better with GPU drivers)
- OpenGL: Hardware acceleration via rendernode
Audio redirection (lines 965-975):
<channel type='spicevmc'>
<target type='virtio' name='com.redhat.spice.0'/>
</channel>
See SPICE Configuration for complete details.
addVNC Method
Lines 595-620
const password = xmlGenerator.addVNC(5900, true, '0.0.0.0');
Parameters:
port: Port number or -1 for autoportautoport: Enable automatic port allocation (default: true)listen: Listen address (default: '0.0.0.0')
Returns: Random 8-character password
Configuration:
<graphics type='vnc' port='5900' autoport='yes' listen='0.0.0.0' passwd='xyz98765'>
<listen type='address' address='0.0.0.0'/>
</graphics>
Less feature-rich than SPICE: No compression, file transfer, audio, or advanced features. Use for compatibility or troubleshooting.
enableHighResolutionGraphics Method
Lines 684-728
xmlGenerator.enableHighResolutionGraphics(512, 'qxl'); // 512 MB VRAM, QXL driver
QXL driver (lines 689-707):
<video>
<model type='qxl' ram='1048576' vram='524288' vgamem='262144' heads='1'/>
<accel accel3d='yes' accel2d='yes'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/>
</video>
- type: 'qxl' (SPICE-optimized graphics driver)
- ram: vramSize * 2 * 1024 KiB (1024 MB for 512 MB VRAM—caching)
- vram: vramSize * 1024 KiB (512 MB—video RAM)
- vgamem: vramSize * 1024 / 2 KiB (256 MB—VGA memory)
- accel: Separate element with accel3d='yes' and accel2d='yes' attributes (hardware acceleration)
VirtIO driver (lines 708-724):
<video>
<model type='virtio'>
<acceleration accel3d='yes'/>
</model>
<gl enable='yes' rendernode='/dev/dri/renderD128'/>
</video>
Note: "Works, but performance is not the best"—QXL preferred for SPICE.
Device Configuration
TPM (Trusted Platform Module)
Lines 466-474
xmlGenerator.enableTPM('2.0'); // Version: '1.2' or '2.0'
Configuration:
<tpm model='tpm-tis'>
<backend type='emulator' version='2.0'/>
</tpm>
- model: 'tpm-tis' (TPM Interface Specification)
- backend: type='emulator' (software TPM, no hardware required)
- version: '2.0' (default, required for Windows 11)
Features
Lines 476-489
xmlGenerator.enableFeatures();
Enables common VM features:
<features>
<acpi/>
<apic/>
<kvm>
<hidden state='on'/>
</kvm>
<hyperv>
<relaxed state='on'/>
<vapic state='on'/>
<spinlocks state='on' retries='8191'/>
</hyperv>
</features>
- acpi: Advanced Configuration and Power Interface (power management)
- apic: Advanced Programmable Interrupt Controller (interrupts)
- kvm.hidden: Hide KVM from guest (prevents anti-VM detection)
- hyperv: Hyper-V enlightenments (better Windows performance)
Input Devices
Lines 732-743
xmlGenerator.enableInputTablet();
Adds USB tablet input device:
<input type='tablet' bus='usb'>
<address type='usb' bus='0' port='1'/>
</input>
Purpose: Improves mouse synchronization between host and guest. Better cursor tracking than PS/2 mouse (no "grab" needed).
Audio
Lines 895-901
xmlGenerator.addAudioDevice();
Adds ICH9 audio device:
<sound model='ich9'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
</sound>
Model: 'ich9' (Intel HD Audio—modern, better than ac97 or es1370)
Works with SPICE audio redirection for complete audio passthrough.
VirtIO Channels
Guest Agent Channel
Lines 745-774
xmlGenerator.addGuestAgentChannel();
Adds QEMU guest agent channel:
<channel type='unix'>
<source mode='bind' path='/var/lib/libvirt/qemu/channel/target/domain-vm-name/org.qemu.guest_agent.0'/>
<target type='virtio' name='org.qemu.guest_agent.0'/>
<address type='virtio-serial' controller='0' bus='0' port='1'/>
</channel>
Type: 'unix' socket Purpose: Execute commands in guest, get guest info (IP address, disk usage, running processes, etc.)
Requires: QEMU guest agent installed in guest:
- Windows: Install from virtio-win ISO
- Linux:
apt install qemu-guest-agent(Ubuntu) ordnf install qemu-guest-agent(Fedora)
InfiniService Channel
Lines 776-857
xmlGenerator.addInfiniServiceChannel();
Adds Infinibay agent channel:
Socket path: ${INFINIBAY_BASE_DIR}/sockets/${machineId}.socket
Creates sockets directory if not exists (mode 0o755)
Configuration:
<channel type='unix'>
<source mode='bind' path='/opt/infinibay/sockets/abc-123.socket'/>
<target type='virtio' name='org.infinibay.agent'/>
<address type='virtio-serial' controller='0' bus='0' port='2'/>
</channel>
Permissions: mode='0666' (read/write for all—allows guest agent to connect)
Auto-increments port: If multiple channels, ports increment (2, 3, 4, ...)
Purpose: Bidirectional communication for:
- Performance metrics collection
- Command execution
- Configuration management
- Application deployment
GPU Passthrough
addGPUPassthrough Method
Lines 864-893
xmlGenerator.addGPUPassthrough('0000:B4:00.0'); // PCI bus address
PCI address format: 0000:B4:00.0 → domain:bus:slot.function
Parses address and creates hostdev configuration:
<hostdev mode='subsystem' type='pci' managed='yes'>
<source>
<address domain='0x0000' bus='0xB4' slot='0x00' function='0x0'/>
</source>
<address type='pci' domain='0x0000' bus='0x05' slot='0x00' function='0x0'/>
</hostdev>
- mode: 'subsystem' (PCI device passthrough)
- type: 'pci'
- managed: 'yes' (libvirt manages driver unbinding/rebinding automatically)
Requirements:
- IOMMU enabled in BIOS and kernel (
intel_iommu=onoramd_iommu=on) - GPU not in use by host (unbind drivers first)
- Proper IOMMU groups (GPU isolated)
XML Generation
generate Method
Lines 626-632
const xml = xmlGenerator.generate();
Converts internal XML object to XML string using xml2js.Builder:
Process:
- Build XML from JavaScript object
- Log generated XML for debugging
- Return complete libvirt domain XML string
Output: Valid XML ready for VirtualMachine.defineXml().
getXmlObject Method
Lines 255-257
const xmlObj = xmlGenerator.getXmlObject();
Returns internal XML object (JavaScript representation).
Use case: Storing in database (MachineConfiguration.xml) as JSON for later modification without re-parsing.
Helper Methods
getStoragePath (lines 670-673): Returns disk path (deprecated, use volume path from storage service)
getUefiVarFile (lines 675-677): Returns UEFI NVRAM file path (${INFINIBAY_BASE_DIR}/uefi/${machineId}_VARS.fd)
getDisks (lines 679-681): Returns array of disk file paths
disablePowerManagement (lines 903-908): Disable suspend-to-mem and suspend-to-disk
Usage Example
From createMachineService.ts lines 431-542
// Initialize XMLGenerator
const xmlGenerator = new XMLGenerator(machineName, machine.id, machine.os);
// Configure resources
xmlGenerator.setMemory(ram);
xmlGenerator.setVCPUs(cores);
xmlGenerator.setCpuPinningOptimization(); // NUMA-aware CPU pinning
// Configure storage
xmlGenerator.addDisk(diskPath, 'virtio', storage);
// Configure boot
xmlGenerator.setUEFI();
xmlGenerator.enableTPM('2.0');
xmlGenerator.setBootDevice(['hd', 'cdrom']);
// Configure network
xmlGenerator.addNetworkInterface('default', 'virtio');
xmlGenerator.addVMNWFilter(vmFilterName);
// Configure graphics
const spicePassword = xmlGenerator.addSPICE(true, false); // Audio enabled, OpenGL disabled
xmlGenerator.enableHighResolutionGraphics(512, 'qxl');
xmlGenerator.enableInputTablet();
// Configure channels
xmlGenerator.addGuestAgentChannel();
xmlGenerator.addInfiniServiceChannel();
// Add installation media
if (newIsoPath) {
xmlGenerator.addCDROM(newIsoPath, 'sata');
await xmlGenerator.addVirtIODrivers();
}
// Add GPU if specified
if (pciBus) {
xmlGenerator.addGPUPassthrough(pciBus);
}
// Generate final XML
const xml = xmlGenerator.generate();
Best Practices
- Always use actual volume paths from
StorageVol.getPath(), never hardcode - Enable UEFI for modern operating systems (Windows 10+, recent Linux)
- Use virtio devices for best performance (disk, network)
- Enable CPU pinning for better performance and core detection in guest OS
- Add both guest agent and InfiniService channels for full management capabilities
- Use SPICE over VNC for better features and performance
- Enable high-resolution graphics for better desktop experience
- Add USB tablet for better mouse synchronization
- Enable TPM 2.0 for Windows 11 (required) and Windows 10 (recommended)
- Use Q35 chipset (default) instead of legacy i440fx for modern features
Mermaid Diagram: XML Generation Process
flowchart TD
Start([Start]) --> CreateGen[Create XMLGenerator<br/>name, id, os]
CreateGen --> SetMemory[Set Memory<br/>GB to KiB conversion]
SetMemory --> SetVCPUs[Set VCPUs<br/>count, placement, mode]
SetVCPUs --> SetPinning[Set CPU Pinning<br/>strategy, NUMA topology]
SetPinning --> EnableUEFI[Enable UEFI<br/>find OVMF, set loader/nvram]
EnableUEFI --> EnableTPM[Enable TPM 2.0]
EnableTPM --> AddDisk[Add Disk<br/>volume path, bus, size]
AddDisk --> AddNetwork[Add Network Interface<br/>detect bridge/network]
AddNetwork --> AddFilter[Add NWFilter<br/>VM filter with priority 200]
AddFilter --> SetBoot[Set Boot Order<br/>hd, cdrom]
SetBoot --> AddSPICE[Add SPICE Graphics<br/>password, compression, features]
AddSPICE --> EnableHiRes[Enable High-Res Graphics<br/>QXL, VRAM]
EnableHiRes --> EnableTablet[Enable Input Tablet]
EnableTablet --> AddGuestAgent[Add Guest Agent Channel<br/>port 1]
AddGuestAgent --> AddInfini[Add InfiniService Channel<br/>port 2, socket path]
AddInfini --> AddCDROM[Add CDROM<br/>installation ISO]
AddCDROM --> AddVirtIO[Add VirtIO Drivers<br/>ISO]
AddVirtIO --> CheckGPU{GPU passthrough<br/>specified?}
CheckGPU -->|Yes| AddGPU[Add GPU Passthrough<br/>PCI address]
CheckGPU -->|No| AddAudio
AddGPU --> AddAudio[Add Audio Device]
AddAudio --> GenerateXML[Generate XML String<br/>xml2js.Builder]
GenerateXML --> ReturnXML[Return XML]
ReturnXML --> End([End])