Enterprise-grade VMDK inspection and pre-migration risk analysis.
The VMDK Inspector performs comprehensive validation of VMDK files before migration to detect:
This prevents migration failures by catching issues early.
# Inspect single VMDK
./scripts/vmdk_inspect.py /path/to/disk.vmdk
# Inspect multiple VMDKs
./scripts/vmdk_inspect.py /vms/*.vmdk
# JSON output for automation
./scripts/vmdk_inspect.py --json /path/to/disk.vmdk
from hyper2kvm.validation import VMDKInspector, RiskLevel
from pathlib import Path
# Create inspector
inspector = VMDKInspector()
# Inspect VMDK
result = inspector.inspect(Path("/path/to/disk.vmdk"))
# Check results
if result.has_fatal_risks:
print("FATAL: Migration will fail!")
for risk in result.risks:
if risk.level == RiskLevel.FATAL:
print(f" - {risk.message}")
elif result.has_high_risks:
print("WARNING: High risk of boot failure")
# Boot mode detection
if result.boot_mode == BootMode.UEFI:
print("UEFI detected - use OVMF firmware in libvirt")
# Generate libvirt config
xml = inspector.generate_libvirt_config(
result,
"/var/lib/libvirt/images/disk.qcow2"
)
print(xml)
| Level | Description | Action Required |
|---|---|---|
| FATAL | Migration will fail | Must fix before migration |
| HIGH | Boot failure likely | Initramfs rebuild needed |
| MEDIUM | Minor compatibility issue | Review configuration |
| INFO | Informational only | No action required |
Problem: VMDK has parentCID != ffffffff
Fix: Consolidate snapshots in VMware before migration
# In vSphere:
# Right-click VM β Snapshots β Consolidate
Problem: BusLogic controller detected
Fix: No fix available - BusLogic not supported on KVM. Change controller in VMware first.
Problem: Guest expects lsilogic but KVM uses virtio
Fix: hyper2kvm automatically rebuilds initramfs with virtio drivers
Problem: UEFI guest detected
Action: Use OVMF firmware in libvirt domain
<os>
<type arch='x86_64' machine='pc-q35'>hvm</type>
<loader readonly='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>
<nvram>/var/lib/libvirt/qemu/nvram/VM_NAME_VARS.fd</nvram>
</os>
Problem: Descriptor references extent file that doesnβt exist
Fix: Verify VMDK files are complete before migration
Problem: Extent file smaller than expected
Fix: Re-export VMDK from vSphere
The VMDK Inspector can automatically remediate controller mismatches by injecting virtio drivers into the guest initramfs.
# Detect and fix controller mismatch in one command
./scripts/vmdk_inspect.py --auto-fix --output fixed.qcow2 disk.vmdk
# Output:
π§ Applying automatic fix for controller mismatch...
Controller: lsilogic β virtio
Action: Injecting virtio drivers into initramfs
π Generated fix configuration:
/tmp/vmdk-fix-abc123.yaml
β
Fix applied successfully!
Output: fixed.qcow2
The fixed image has virtio drivers in initramfs.
Boot this VM on KVM - it will use virtio-blk/virtio-scsi controllers.
Enable automatic controller remediation in your migration config:
cmd: local
vmdk: /path/to/vm-with-lsilogic.vmdk
output_dir: ./output
to_output: vm-fixed.qcow2
# Enable automatic controller fix
vmdk_auto_fix_controller: true
# These are enabled automatically when controller mismatch detected:
# regen_initramfs: true (auto-enabled)
# initramfs_add_drivers: [virtio, virtio_blk, virtio_scsi, virtio_net, virtio_pci] (auto-added)
verbose: 2
When vmdk_auto_fix_controller: true is set:
β Can Auto-Fix:
β Cannot Auto-Fix:
Specify additional drivers beyond the defaults:
cmd: local
vmdk: /path/to/vm.vmdk
to_output: vm-custom.qcow2
# Enable auto-fix
vmdk_auto_fix_controller: true
# Add custom drivers (replaces defaults)
initramfs_add_drivers:
- virtio
- virtio_blk
- virtio_scsi
- virtio_net
- virtio_pci
- e1000e # Intel NIC
- nvme # NVMe storage
- megaraid_sas # RAID controller
Fix multiple VMs with controller mismatches:
cmd: local
parallel_processing: true
# Apply auto-fix to all VMs
vmdk_auto_fix_controller: true
vms:
- vmdk: /data/vm1-lsilogic.vmdk
to_output: vm1-fixed.qcow2
- vmdk: /data/vm2-ide.vmdk
to_output: vm2-fixed.qcow2
- vmdk: /data/vm3-virtio.vmdk
to_output: vm3.qcow2 # No fix needed
output_dir: ./batch-output
For fleet-wide VMDK scanning without early exit:
# Scan all VMDKs, never fail (even with FATAL risks)
./scripts/vmdk_inspect.py --no-fail --json "/vmfs/volumes/*/*.vmdk" > fleet-report.json
# Exit code: 0 β
(complete scan regardless of risks)
# Filter problematic VMs
jq '.[] | select(.risks[].level == "FATAL")' fleet-report.json
Use cases:
| Code | Meaning |
|---|---|
0 |
No issues or only INFO/MEDIUM (or βno-fail mode) |
2 |
HIGH risk detected |
3 |
FATAL risk detected |
Use in scripts:
#!/bin/bash
# Option 1: Fail on risks (default)
if ! ./scripts/vmdk_inspect.py disk.vmdk; then
echo "Pre-migration validation failed!"
exit 1
fi
# Option 2: Auto-fix controller mismatches
if ./scripts/vmdk_inspect.py disk.vmdk 2>&1 | grep -q "Controller.*mismatch"; then
echo "Controller mismatch detected - applying auto-fix"
./scripts/vmdk_inspect.py --auto-fix --output fixed.qcow2 disk.vmdk
else
echo "No fix needed - direct conversion"
hyper2kvm --config migration.yaml
fi
# Option 3: Inventory mode (never fail)
./scripts/vmdk_inspect.py --no-fail --json "*.vmdk" > inventory.json
[
{
"file": "/path/to/disk.vmdk",
"size_gb": 50.0,
"adapter": "lsilogic",
"boot_mode": "UEFI",
"risks": [
{
"level": "HIGH",
"message": "Controller 'lsilogic' β initramfs may require rebuild",
"component": "controller"
},
{
"level": "HIGH",
"message": "UEFI firmware detected - libvirt domain MUST use OVMF",
"component": "boot"
}
]
}
]
The VMDK Inspector is automatically used during pre-migration validation:
from hyper2kvm.validation import VMDKInspector
from hyper2kvm.orchestrator import Orchestrator
# Inspector runs before conversion
inspector = VMDKInspector(logger)
result = inspector.inspect(vmdk_path)
if result.has_fatal_risks:
raise MigrationError("Pre-migration validation failed")
Uses virt-inspector from libguestfs to detect UEFI:
# Manual boot mode check
virt-inspector --no-applications --no-icon disk.vmdk
If libguestfs is not installed, boot mode detection is skipped (INFO risk added).
# Fedora/RHEL
sudo dnf install libguestfs-tools
# Ubuntu/Debian
sudo apt install libguestfs-tools
$ ./scripts/vmdk_inspect.py test.vmdk
=== /vms/test.vmdk ===
Size : 20.0 GB
Adapter : lsilogic
Boot mode : BIOS
[HIGH] Controller 'lsilogic' β initramfs may require rebuild
[INFO] Legacy CHS geometry present (ignored by modern kernels)
Suggested libvirt XML:
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2' cache='none' io='native'/>
<source file='/var/lib/libvirt/images/disk.qcow2'/>
<target dev='vda' bus='virtio'/>
</disk>
Exit code: 2 (HIGH risk)
$ ./scripts/vmdk_inspect.py snapshot.vmdk
=== /vms/snapshot.vmdk ===
Size : 10.0 GB
Adapter : lsilogic
Boot mode : BIOS
[FATAL] Snapshot chain detected (parentCID != ffffffff)
Exit code: 3 (FATAL risk)
$ ./scripts/vmdk_inspect.py uefi-guest.vmdk
=== /vms/uefi-guest.vmdk ===
Size : 40.0 GB
Adapter : lsilogic
Boot mode : UEFI
[HIGH] UEFI firmware detected - libvirt domain MUST use OVMF
[HIGH] Controller mismatch: guest expects 'lsilogic', KVM will use virtio
Suggested libvirt XML:
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2' cache='none' io='native'/>
<source file='/var/lib/libvirt/images/disk.qcow2'/>
<target dev='vda' bus='virtio'/>
</disk>
<!-- UEFI firmware configuration (REQUIRED for UEFI guests) -->
<os>
<type arch='x86_64' machine='pc-q35'>hvm</type>
<loader readonly='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>
<nvram>/var/lib/libvirt/qemu/nvram/VM_NAME_VARS.fd</nvram>
</os>
class VMDKInspector:
def __init__(self, logger: Optional[logging.Logger] = None)
def inspect(self, vmdk_path: Path) -> VMDKInspectionResult
def generate_libvirt_config(
self,
result: VMDKInspectionResult,
converted_image_path: str
) -> str
@dataclass
class VMDKInspectionResult:
path: Path
valid: bool
# Metadata
create_type: Optional[str]
parent_cid: Optional[str]
adapter_type: Optional[str]
thin_provisioned: bool
# Size
sectors: Optional[int]
extent_type: Optional[str]
extent_file: Optional[str]
# Boot mode
boot_mode: BootMode # BIOS | UEFI | UNKNOWN
# Risks
risks: List[Risk]
# Properties
@property
def size_gb(self) -> Optional[float]
@property
def has_fatal_risks(self) -> bool
@property
def has_high_risks(self) -> bool
@property
def max_risk_level(self) -> Optional[RiskLevel]
def to_dict(self) -> Dict[str, Any]