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:

  1. Get vCPU count from domain XML
  2. Call strategy.setCpuPinning(vcpuCount)
  3. Receive CpuPinningConfig with cputune and cpu elements
  4. 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 (from StorageVol.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:

  • idehd (hda, hdb, ...)
  • satasd (sda, sdb, ...)
  • virtiovd (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:

  1. Filter devices by bus type ('sd', 'vd', 'hd')
  2. Sort alphabetically
  3. 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 name
  • model: '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 autoport
  • autoport: 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) or dnf 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=on or amd_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:

  1. Build XML from JavaScript object
  2. Log generated XML for debugging
  3. 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

  1. Always use actual volume paths from StorageVol.getPath(), never hardcode
  2. Enable UEFI for modern operating systems (Windows 10+, recent Linux)
  3. Use virtio devices for best performance (disk, network)
  4. Enable CPU pinning for better performance and core detection in guest OS
  5. Add both guest agent and InfiniService channels for full management capabilities
  6. Use SPICE over VNC for better features and performance
  7. Enable high-resolution graphics for better desktop experience
  8. Add USB tablet for better mouse synchronization
  9. Enable TPM 2.0 for Windows 11 (required) and Windows 10 (recommended)
  10. 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])