Skip to content

Instantly share code, notes, and snippets.

@tdewin
Last active March 14, 2024 22:24
Show Gist options
  • Save tdewin/6a4738fd74b96b84d3fa7c5be78735fd to your computer and use it in GitHub Desktop.
Save tdewin/6a4738fd74b96b84d3fa7c5be78735fd to your computer and use it in GitHub Desktop.
Experimental migration tool from vSphere to Proxmox using Veeam Instant VM Recovery
# MIT LICENSE
# EXPERIMENTAL RUN STEP BY STEP AND MAKE SURE YOU UNDERSTAND WHAT YOU ARE DOING
# EXPERIMENTAL RUN STEP BY STEP AND MAKE SURE YOU UNDERSTAND WHAT YOU ARE DOING
# EXPERIMENTAL RUN STEP BY STEP AND MAKE SURE YOU UNDERSTAND WHAT YOU ARE DOING
# EXPERIMENTAL RUN STEP BY STEP AND MAKE SURE YOU UNDERSTAND WHAT YOU ARE DOING
# EXPERIMENTAL RUN STEP BY STEP AND MAKE SURE YOU UNDERSTAND WHAT YOU ARE DOING
# By default vPowerNFS does not allow mounting by any host. You need to disable the vPowerNFS IP filter to do so
# https://www.veeam.com/kb1055 -> point 7
# Key Location: HKLM\SOFTWARE\WOW6432Node\Veeam\Veeam NFS\
# Value Name: vPowerNFSDisableIPAuth
# Value Type: DWORD (32-Bit) Value
# Value Data: 1
# Start instant vm recovery, don't replace but do restore to another location
# name <name>_migrate to make sure the vm is unique and doesn't override
# where do you want the disks to live
# pvesh ls storage -o json | jq ".[] | .name"
TARGETSTORAGE="local-lvm"
#get started
#govc ls -json=true network | jq '[.elements[] | .Object] | map({ (.name):{"br":"",vlan:0}}) | add'
read -r -d '' NETMAPPING << EOM
{
"VM Network": {
"br": "vmbr0",
"vlan": 0
}
}
EOM
curl -L -o - "https://github.com/vmware/govmomi/releases/latest/download/govc_$(uname -s)_$(uname -m).tar.gz" | tar -C /usr/local/bin -xvzf - govc
apt update
apt install jq
read -s -p "pass (no echo): " esxipw
read -p "esxihost: " esxihost
export GOVC_INSECURE=1
export GOVC_URL="https://root:$esxipw@$esxihost/sdk"
# This part should be only done once since vPowerNFS can be mounted once
NASSHARE=$(govc ls -json=true datastore | jq '.elements[] | .Object | select(.info.nas) | select(.info.nas.name | contains("VeeamBackup_"))')
NASHOST=$(echo $NASSHARE | jq ".info.nas.remoteHost" -r)
NASPATH=$(echo $NASSHARE | jq ".info.nas.remotePath" -r)
NASNAME=$(echo $NASSHARE | jq ".info.nas.name" -r)
VPOWERPATH="/mnt/$NASNAME"
mkdir -p "/mnt/$NASNAME" $VPOWERPATH
mount -t nfs "$NASHOST:$NASPATH" $VPOWERPATH
# Find the VM that you restore based on the _migrate tag
VMJSON=$(govc ls -json=true vm | jq '.elements[] | select(.Object.name | contains("_migrate")) | .Object')
VMNAME=$(echo $VMJSON | jq -r .name)
govc snapshot.revert -vm $VMNAME
govc snapshot.remove -vm $VMNAME "*"
#refresh
VMJSON=$(govc ls -json=true vm | jq '.elements[] | select(.Object.name | contains("_migrate")) | .Object')
DEVICESJSON=$(govc device.info -json=true -vm $VMNAME)
CPU=$(echo $VMJSON | jq ".config.hardware.numCPU" -r)
CORESSOCK=$(echo $VMJSON | jq ".config.hardware.numCoresPerSocket" -r)
MEM=$(echo $VMJSON | jq ".config.hardware.memoryMB" -r)
GUEST=$(echo $VMJSON | jq ".summary.config.guestId" -r)
#https://pve.proxmox.com/wiki/Manual:_qm.conf
#https://github.com/vmware/govmomi/blob/d62c0ace62357ba156e23b8f3e04667bf3ac6c36/simulator/guest_id.go#L59
#https://github.com/vmware/ansible-vsphere-gos-validation/blob/c6d6b287e73c23472121b2cd22bb982ed5954550/linux/check_os_fullname/guest_id_mappings.json#L103
#GUESTIDMAP=$(curl https://raw.githubusercontent.com/vmware/ansible-vsphere-gos-validation/c6d6b287e73c23472121b2cd22bb982ed5954550/linux/check_os_fullname/guest_id_mappings.json)
#echo $GUESTIDMAP | jq 'to_entries | map({(.value):{"ostype":"l26"}}) | add'
#GUESTMAPPING=$(curl https://gist.githubusercontent.com/tdewin/6a4738fd74b96b84d3fa7c5be78735fd/raw/aa7ebbf201bf8ebcfacb5da0b525714e0fb0db80/vmware-to-proxmox-map.json | jq ".guests")
read -r -d '' GUESTMAPPING << EOM
{
"windows9_64Guest": {
"ostype": "win10"
},
"windows11_64Guest": {
"ostype": "win11"
},
"windows12_64Guest": {
"ostype": "win12"
},
"windows9Server64Guest": {
"ostype": "win10"
},
"windows2019srv_64Guest": {
"ostype":"win10"
},
"windows2019srvNext_64Guest":{
"ostype":"win11"
},
"windows2022srvNext_64Guest":{
"ostype":"win11"
},
"centos7_64Guest": {
"ostype": "l26"
},
"centos8_64Guest": {
"ostype": "l26"
},
"centos9_64Guest": {
"ostype": "l26"
},
"debian10Guest": {
"ostype": "l26"
},
"debian10_64Guest": {
"ostype": "l26"
},
"debian11Guest": {
"ostype": "l26"
},
"debian11_64Guest": {
"ostype": "l26"
},
"debian12Guest": {
"ostype": "l26"
},
"debian12_64Guest": {
"ostype": "l26"
},
"rhel6_64Guest": {
"ostype": "l26"
},
"rhel7_64Guest": {
"ostype": "l26"
},
"rhel8_64Guest": {
"ostype": "l26"
},
"rhel9_64Guest": {
"ostype": "l26"
},
"opensuseGuest": {
"ostype": "l26"
},
"opensuse64Guest": {
"ostype": "l26"
},
"slesGuest": {
"ostype": "l26"
},
"sles12_64Guest": {
"ostype": "l26"
},
"sles15_64Guest": {
"ostype": "l26"
},
"sles16_64Guest": {
"ostype": "l26"
},
"sles64Guest": {
"ostype": "l26"
},
"suseGuest": {
"ostype": "l26"
},
"suse64Guest": {
"ostype": "l26"
},
"ubuntuGuest": {
"ostype": "l26"
},
"ubuntu64Guest": {
"ostype": "l26"
},
"rockylinux_64Guest": {
"ostype": "l26"
},
"almalinux_64Guest": {
"ostype": "l26"
}
}
EOM
#https://github.com/vmware/govmomi/blob/32912767be55bf935264bf2b67c3f78514dcf23b/object/virtual_device_list.go#L66
read -r -d '' NETDEVMAPPING << EOM
{
"VirtualVmxnet3": {
"devtype":"vmxnet3"
},
"VirtualE1000": {
"devtype":"e1000"
},
"VirtualE1000e": {
"devtype":"e1000e"
}
}
EOM
OSTYPE=$(echo $GUESTMAPPING | jq ".[\"$GUEST\"].ostype" -r)
ORIGNAME=$(echo $VMJSON | jq ".name" -r | sed 's/_migrate$//g')
NEXTID=$(pvesh get /cluster/nextid)
BIOSTYPE=$(echo $VMJSON | jq ".config.firmware" -r)
BIOSTYPEMAPPING=$(echo '{"bios":"seabios","efi":"ovmf"}' | jq ".$BIOSTYPE" -r)
qm create $NEXTID --memory $MEM --cores $CORESSOCK --sockets $CPU --bios $BIOSTYPEMAPPING -ostype $OSTYPE --kvm yes --name $ORIGNAME
BIOSUUID=$(echo $VMJSON | jq ".config.uuid" -r)
qm set $NEXTID --smbios1 uuid=$BIOSUUID
NETWORKS=$(echo $DEVICESJSON | jq '[ .devices[] | select(.backing.network)]')
NLEN=$(echo $NETWORKS | jq length)
for i in $(seq 0 1 $(($NLEN-1)))
do
SAMPLENET=$(echo $NETWORKS | jq ".[$i]" -r)
MACADDR=$(echo $SAMPLENET | jq ".macAddress" -r)
VNET=$(echo $SAMPLENET | jq ".backing.deviceName" -r)
VNETMAPBR=$(echo $NETMAPPING | jq ".[\"$VNET\"].br" -r)
VNETMAPVLAN=$(echo $NETMAPPING | jq ".[\"$VNET\"].vlan" -r)
DEVTYPE=$(echo $SAMPLENET | jq ".type" -r)
DEVTYPEMAP=$(echo $NETDEVMAPPING | jq ".$DEVTYPE.devtype" -r)
echo $MACADDR $VNETMAPBR $VNETMAPVLAN $DEVTYPEMAP
qm set $NEXTID -net$i bridge=$VNETMAPBR,trunks=$VNETMAPVLAN,model=$DEVTYPEMAP,macaddr=$MACADDR
done
#DISKS=$(echo $VMJSON | jq '.config.hardware.device[] | select(.backing.fileName | index("vm"))')
DISKS=$(echo $VMJSON | jq '.layout.disk' )
SORTEDDISKS=$(echo $DISKS | jq '. | sort_by(.key)')
DLEN=$(echo $SORTEDDISKS | jq length)
for i in $(seq 0 1 $(($DLEN-1)))
do
SAMPLEDISK=$(echo $VMJSON | jq ".layout.disk[$i] | .diskFile[0]" -r)
DISKPATH=$(echo $SAMPLEDISK | sed "s/\[$NASNAME\] //g")
echo $DISKPATH
[ -f "$VPOWERPATH/$DISKPATH" ] && qm disk import $NEXTID "$VPOWERPATH/$DISKPATH" $TARGETSTORAGE
done
{
"guests": {
"windows9_64Guest": {
"ostype": "win10"
},
"windows11_64Guest": {
"ostype": "win11"
},
"windows12_64Guest": {
"ostype": "win12"
},
"windows9Server64Guest": {
"ostype": "win10"
},
"windows2019srv_64Guest": {
"ostype":"win10"
},
"windows2019srvNext_64Guest":{
"ostype":"win11"
},
"windows2022srvNext_64Guest":{
"ostype":"win11"
},
"centos7_64Guest": {
"ostype": "l26"
},
"centos8_64Guest": {
"ostype": "l26"
},
"centos9_64Guest": {
"ostype": "l26"
},
"debian10Guest": {
"ostype": "l26"
},
"debian10_64Guest": {
"ostype": "l26"
},
"debian11Guest": {
"ostype": "l26"
},
"debian11_64Guest": {
"ostype": "l26"
},
"debian12Guest": {
"ostype": "l26"
},
"debian12_64Guest": {
"ostype": "l26"
},
"rhel6_64Guest": {
"ostype": "l26"
},
"rhel7_64Guest": {
"ostype": "l26"
},
"rhel8_64Guest": {
"ostype": "l26"
},
"rhel9_64Guest": {
"ostype": "l26"
},
"opensuseGuest": {
"ostype": "l26"
},
"opensuse64Guest": {
"ostype": "l26"
},
"slesGuest": {
"ostype": "l26"
},
"sles12_64Guest": {
"ostype": "l26"
},
"sles15_64Guest": {
"ostype": "l26"
},
"sles16_64Guest": {
"ostype": "l26"
},
"sles64Guest": {
"ostype": "l26"
},
"suseGuest": {
"ostype": "l26"
},
"suse64Guest": {
"ostype": "l26"
},
"ubuntuGuest": {
"ostype": "l26"
},
"ubuntu64Guest": {
"ostype": "l26"
},
"rockylinux_64Guest": {
"ostype": "l26"
},
"almalinux_64Guest": {
"ostype": "l26"
}
},
"netdevs": {
"VirtualVmxnet3": {
"devtype":"vmxnet3"
},
"VirtualE1000": {
"devtype":"e1000"
},
"VirtualE1000e": {
"devtype":"e1000e"
}
},
"bios": {"bios":"seabios","efi":"ovmf"}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment