The PlanVMS format provides granular control over individual VM migration settings, allowing customization of target names, disk configuration, networking, and migration behavior on a per-VM basis. This chapter covers the complete VM customization capabilities.
Overview of PlanVMS Format
What is PlanVMS Format?
PlanVMS format is a structured YAML/JSON format that defines individual VM configurations within a migration plan. It enables:
Per-VM Customization: Different settings for each VM in the same plan
Resource Templates: Custom naming templates for generated resources
Migration Behavior: Individual migration settings and cleanup policies
Target Configuration: Specific target cluster settings per VM
When to Use PlanVMS Format
Complex Migrations: When VMs require different target configurations
Name Normalization: When source VM names need target-specific adjustments
Resource Management: When custom resource naming is required
Hook Integration: When different VMs need different automation hooks
Security Requirements: When VMs have different encryption or security needs
Detailed VM List Format
The PlanVMS format is based on the Forklift API VM specification, verified from the vendor code:
Basic VM Structure
1
2
3
4
# Basic VM entry-name:source-vm-name# Required: Source VM nametargetName:target-vm-name# Optional: Custom target namerootDisk:/dev/sda# Optional: Boot disk selection
All fields are verified from the Forklift API VM struct definition:
Core VM Configuration
Name and Identity
1
2
3
4
5
# Required source VM identifier-name:source-vm-name# Optional custom target nametargetName:custom-target-name
Name Field Requirements:
name: Must match exactly the VM name in the source provider
targetName: Must be valid Kubernetes resource name (DNS-1123 compliant)
Target Power State
1
2
# Control VM power state after migrationtargetPowerState:on# Options: on, off, auto (default)
Power State Options:
on: Start VM after migration completes
off: Keep VM powered off after migration
auto: Match source VM power state (default behavior)
Instance Type Override
1
2
# Override VM resource specificationsinstanceType:high-performance
Selects a predefined InstanceType resource that overrides CPU, memory, and other VM properties.
Disk and Storage Configuration
Root Disk Selection
1
2
# Specify the primary boot diskrootDisk:/dev/sda
Critical for multi-disk VMs to ensure proper boot configuration.
LUKS Disk Encryption
1
2
3
4
5
6
7
# Reference to LUKS encryption keysluks:name:vm-encryption-keysnamespace:security-namespace# Enable automatic TANG/Clevis unlockingnbdeClevis:true
LUKS Configuration:
luks: References a Kubernetes Secret containing LUKS passphrases
nbdeClevis: Enables network-based automatic unlocking using TANG servers
Migration Behavior Configuration
Failure Cleanup Policy
1
2
# Control VM deletion on migration failuredeleteVmOnFailMigration:true
Cleanup Behavior:
true: Delete target VM and resources if migration fails
false: Preserve target VM for troubleshooting (default)
Note: Plan-level setting overrides VM-level setting when enabled.
Hook Integration
1
2
3
4
5
6
7
8
9
10
# Attach migration hooks to specific VMshooks:-step:PreHookhook:name:database-quiescenamespace:migration-hooks-step:PostHookhook:name:health-checknamespace:migration-hooks
Hook Configuration:
step: Hook execution phase (PreHook, PostHook)
hook.name: Name of the Hook resource
hook.namespace: Namespace containing the Hook resource
Go Template Variables Reference
kubectl-mtv provides rich template variables for resource naming, verified from the API documentation:
PVC Name Template Variables
Available in pvcNameTemplate field:
Variable
Description
Example Value
{{.VmName}}
Original source VM name
web-server-01
{{.TargetVmName}}
Final target VM name
web-prod-01
{{.PlanName}}
Migration plan name
production-migration
{{.DiskIndex}}
Disk index (0-based)
0, 1, 2
{{.WinDriveLetter}}
Windows drive letter
c, d, e
{{.RootDiskIndex}}
Index of root/boot disk
0
{{.Shared}}
True if disk is shared
true, false
{{.FileName}}
VMware disk filename
web-server-01.vmdk
PVC Template Examples
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Basic PVC namingpvcNameTemplate:"{{.TargetVmName}}-disk-{{.DiskIndex}}"# Result: web-prod-01-disk-0, web-prod-01-disk-1# Root vs data disk differentiation pvcNameTemplate:"{{ifeq.DiskIndex.RootDiskIndex}}{{.TargetVmName}}-root{{else}}{{.TargetVmName}}-data-{{.DiskIndex}}{{end}}"# Result: web-prod-01-root, web-prod-01-data-1# Shared disk identificationpvcNameTemplate:"{{if.Shared}}shared-{{end}}{{.TargetVmName}}-{{.DiskIndex}}"# Result: web-prod-01-0, shared-web-prod-01-1# Windows drive letter namingpvcNameTemplate:"{{.TargetVmName}}-{{.WinDriveLetter}}-drive"# Result: windows-server-c-drive, windows-server-d-drive# Plan-scoped namingpvcNameTemplate:"{{.PlanName}}-{{.TargetVmName}}-disk{{.DiskIndex}}"# Result: prod-migration-web-prod-01-disk0
# Save as vm-customizations.yaml-name:web-server-01targetName:web-prod-01targetPowerState:onpvcNameTemplate:"{{.TargetVmName}}-{{.DiskIndex}}"-name:database-01targetName:db-prod-01rootDisk:/dev/sdatargetPowerState:oninstanceType:database-serverdeleteVmOnFailMigration:false-name:app-server-01targetName:app-prod-01hooks:-step:PreHookhook:name:app-shutdownnamespace:migration-hooks-step:PostHookhook:name:app-startupnamespace:migration-hooks
Use Custom VM List in Plan
1
2
3
4
5
kubectl mtv create plan --name custom-vm-migration \--source vsphere-prod \--vms @vm-customizations.yaml \--network-mapping prod-network-map \--storage-mapping prod-storage-map
Method 2: Export and Modify Existing Inventory
Export VMs in PlanVMS Format
1
2
3
4
5
6
7
# Export all VMs from provider
kubectl mtv get inventory vms --provider vsphere-prod --output planvms > all-vms.yaml
# Export filtered VMs
kubectl mtv get inventory vms --provider vsphere-prod \--query"where powerState = 'poweredOn' and memoryMB >= 4096"\--output planvms > production-vms.yaml
Modify Exported VMs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Original exported format-name:web-server-01targetName:""rootDisk:""# Modified with customizations-name:web-server-01targetName:web-prod-01rootDisk:/dev/sdatargetPowerState:onpvcNameTemplate:"prod-{{.TargetVmName}}-disk-{{.DiskIndex}}"hooks:-step:PostHookhook:name:web-validationnamespace:migration-hooks
# Template for web servers-name:web-server-01targetName:web-prod-01targetPowerState:onpvcNameTemplate:"web-{{.TargetVmName}}-{{.DiskIndex}}"volumeNameTemplate:"vol-{{.VolumeIndex}}"hooks:-step:PostHookhook:name:web-health-checknamespace:migration-hooks-name:web-server-02targetName:web-prod-02targetPowerState:onpvcNameTemplate:"web-{{.TargetVmName}}-{{.DiskIndex}}"volumeNameTemplate:"vol-{{.VolumeIndex}}"hooks:-step:PostHookhook:name:web-health-checknamespace:migration-hooks
# Windows domain controller-name:dc01targetName:domain-controller-01rootDisk:/dev/sdatargetPowerState:oninstanceType:windows-serverpvcNameTemplate:"{{.TargetVmName}}-{{.WinDriveLetter}}"volumeNameTemplate:"{{.WinDriveLetter}}-drive"hooks:-step:PreHookhook:name:ad-replication-pausenamespace:windows-hooks-step:PostHookhook:name:ad-health-checknamespace:windows-hooks# Windows file server-name:fileserver01targetName:file-server-01rootDisk:/dev/sdatargetPowerState:oninstanceType:file-serverpvcNameTemplate:"{{.TargetVmName}}-{{.WinDriveLetter}}-{{if.Shared}}shared{{else}}local{{end}}"deleteVmOnFailMigration:false
# Web tier-name:web-lb-01targetName:web-loadbalancertargetPowerState:oninstanceType:load-balancerpvcNameTemplate:"web-{{.TargetVmName}}-{{.DiskIndex}}"hooks:-step:PreHookhook:name:drain-connectionsnamespace:web-hooks# Application tier-name:app-server-01targetName:app-primarytargetPowerState:oninstanceType:application-serverpvcNameTemplate:"app-{{.TargetVmName}}-{{.DiskIndex}}"hooks:-step:PreHookhook:name:app-graceful-shutdownnamespace:app-hooks-step:PostHookhook:name:app-health-checknamespace:app-hooks# Data tier-name:cache-redis-01targetName:redis-cachetargetPowerState:oninstanceType:cache-serverpvcNameTemplate:"cache-{{.TargetVmName}}-{{.DiskIndex}}"luks:name:cache-encryptionnamespace:security
Scenario 4: Development Environment Normalization
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Normalize development VM names-name:"DevWebServer01"# Source name with spacestargetName:dev-web-01# Kubernetes-compliant nametargetPowerState:onpvcNameTemplate:"dev-{{.TargetVmName|lower}}-disk{{.DiskIndex}}"-name:"TestDatabase(MySQL)"targetName:test-mysql-dbrootDisk:/dev/sdatargetPowerState:onpvcNameTemplate:"test-{{.TargetVmName}}-{{ifeq.DiskIndex.RootDiskIndex}}os{{else}}data{{end}}"-name:"QA_Environment_App"targetName:qa-app-servertargetPowerState:off# Keep powered off initiallydeleteVmOnFailMigration:true# Clean up failures in test env
Template Functions and Advanced Usage
Built-in Template Functions
kubectl-mtv supports Go template functions for advanced string manipulation:
# Multi-condition PVC namingpvcNameTemplate:"{{ifeq.DiskIndex.RootDiskIndex}}root{{elseif.Shared}}shared-data{{else}}data{{end}}-{{.DiskIndex}}"# Windows vs Linux differentiationvolumeNameTemplate:"{{if.WinDriveLetter}}{{.WinDriveLetter}}-drive{{else}}disk-{{.VolumeIndex}}{{end}}"# Network type-based namingnetworkNameTemplate:"{{ifeq.NetworkType\"Pod\"}}pod{{else}}{{.NetworkName|lower}}{{end}}-{{.NetworkIndex}}"
Validation and Testing
PlanVMS Format Validation
Syntax Validation
1
2
3
4
5
6
7
8
# Validate YAML syntax
yamllint vm-customizations.yaml
# Test with kubectl dry-run
kubectl mtv create plan --name test-validation \--source vsphere-prod \--vms @vm-customizations.yaml \--dry-run=client
Template Testing
1
2
3
4
5
6
7
8
9
# Test template rendering (requires actual plan creation)
kubectl mtv create plan --name template-test \--source vsphere-prod \--vms @template-test.yaml \--namespace test-namespace
# Check generated resource names
kubectl get pvc -n test-namespace
kubectl describe vm template-test-vm -n test-namespace
Field Validation
Required Field Check
1
2
3
4
5
# Minimal valid VM entry-name:source-vm-name# Required# Invalid: missing name-targetName:target-only# Error: name is required
# Create plan with custom VM configurations
kubectl mtv create plan --name customized-migration \--source vsphere-prod \--target openshift-prod \--network-mapping prod-network-map \--storage-mapping prod-storage-map \--migration-type warm \--vms @customized-vms.yaml
# Combine with plan-level settings
kubectl mtv create plan --name comprehensive-migration \--source vsphere-prod \--target-namespace production \--migration-type warm \--preserve-static-ips\--vms @enterprise-vms.yaml
Template Override Behavior
Plan-level templates are overridden by VM-level templates:
1
2
3
4
5
6
7
8
# Plan-level template
kubectl mtv create plan --name plan-template \--pvc-name-template"{{.PlanName}}-{{.VmName}}-{{.DiskIndex}}"\--vms @vms-with-templates.yaml
# VM-level template overrides plan-level# VMs with pvcNameTemplate: use VM template# VMs without pvcNameTemplate: use plan template
# Check for duplicate target namesgrep-n"targetName:" vm-list.yaml | sort-k2# Verify uniqueness in target namespace
kubectl get vm -n target-namespace -o name
Debug PlanVMS Processing
1
2
3
4
5
6
7
8
9
10
# Monitor plan creation with verbosity
kubectl mtv create plan --name debug-planvms \--vms @debug-vms.yaml \-v=2
# Check plan status
kubectl describe plan debug-planvms
# Monitor VM processing
kubectl get vmstatus -n migration-namespace --watch
Best Practices for PlanVMS Usage
Design Principles
Consistency: Use consistent naming patterns across similar VMs
Clarity: Make target names self-documenting
Scalability: Design templates that work for large VM sets
Security: Properly configure LUKS and encryption settings
Operational Guidelines
Version Control: Store PlanVMS files in version control systems
Documentation: Document custom template logic and naming conventions
Testing: Validate PlanVMS configurations in test environments first
Monitoring: Track resource usage of generated PVCs and volumes