Différences entre les versions de « Migration VM de kvm vers hyperv »
De BlaxWiki
Aller à la navigationAller à la recherche| Ligne 34 : | Ligne 34 : | ||
</pre> | </pre> | ||
=== Script === | === Script KVM === | ||
<pre> | <pre> | ||
| Ligne 249 : | Ligne 249 : | ||
</pre> | </pre> | ||
=== Script HyperV === | |||
<pre> | |||
# Import VM | |||
# convert vhdx | |||
# Move VM | |||
# Ajout VM dans cluster | |||
$ErrorActionPreference = "Stop" | |||
# Demandes des informations | |||
Write-Output "Choisir le chemin de la VM a importer :" | |||
$InputVmSrcPath=ls Z: | Select FullName | Out-GridView -OutputMode Single -Title "Choisir le chemin de la VM a importer :" | |||
Write-Output $InputVmSrcPath.Fullname | |||
if (-not (Test-Path -Path "$($InputVmSrcPath.FullName)\Virtual Machines\*.xml")) { | |||
Write-Error -Message "Erreur : XML de la VM non trouvé dans le dossier Virtual Machines" | |||
exit | |||
} | |||
Write-Output "Choisir le chemin d'export :" | |||
$InputVmDestPath=ls C:\ClusterStorage | Select FullName | Out-GridView -OutputMode Single -Title "Choisir où exporter la VM :" | |||
Write-Output $InputVmDestPath.Fullname | |||
### Import | |||
Write-Output "Début vérification compatibilité" | |||
$ConfigPath="$($InputVmSrcPath.FullName)\Virtual Machines\*.xml" | Resolve-Path | |||
Write-Output "Utilisation config : $ConfigPath" | |||
$report=Compare-VM -Register -Path $ConfigPath | |||
$report.Incompatibilities | Where-Object {$_.MessageID –eq 33012} | ForEach-Object {Connect-VMNetworkAdapter $_.Source –Switchname “Vswitch_CSN”} | |||
Write-Output "Import de la VM" | |||
$VM=Import-VM -CompatibilityReport $report | |||
Write-Output "Fin import de la VM $($VM.Name)" | |||
### Update config | |||
Write-Output "Update configuration de la VM" | |||
Update-VMVersion -Name $VM.Name -Force | |||
Write-Output "Fin update" | |||
### Création dossiers | |||
Write-Output "Création des dossiers dans le ClusterStorage" | |||
New-Item -ItemType "Directory" -path $InputVmDestPath.FullName -Name $VM.Name -ErrorAction Ignore | |||
New-Item -ItemType "Directory" -path "$($InputVmDestPath.FullName)\$($VM.Name)" -Name "Snapshots" -ErrorAction Ignore | |||
New-Item -ItemType "Directory" -path "$($InputVmDestPath.FullName)\$($VM.Name)" -Name "Virtual Hard Disks" -ErrorAction Ignore | |||
New-Item -ItemType "Directory" -path "$($InputVmDestPath.FullName)\$($VM.Name)" -Name "Virtual Machines" -ErrorAction Ignore | |||
Write-Output "Fin de la création des dossiers" | |||
### Conversion disque | |||
Foreach ($vhd in ($VM | Get-VMHardDiskDrive)) { | |||
$vhdName=$vhd.Path | Split-path -Leaf -Resolve | |||
$vhdxPath="$($InputVmDestPath.FullName)\$($VM.Name)\Virtual Hard Disks\$($vhdName)x" | |||
Write-Output "Conversion du disque $($vhd.Path) vers $vhdxPath" | |||
Convert-VHD -Path $($vhd.Path) -DestinationPath $vhdxPath -VHDType "Fixed" | |||
Write-Output "Fin conversion de $($vhd.Path)" | |||
Write-Output "Remplacement du VHD dans la configuration de VM" | |||
$vhd | Set-VMHardDiskDrive -Path $vhdxPath | |||
} | |||
### Déplacement VM | |||
Write-Output "Déplacement de la VM dans $($InputVmDestPath.FullName)" | |||
$VM | Move-VMStorage -VirtualMachinePath "$($InputVmDestPath.FullName)\$($VM.Name)\Virtual Machines" ` | |||
-SmartPagingFilePath "$($InputVmDestPath.FullName)\$($VM.Name)\Virtual Machines" ` | |||
-SnapshotFilePath "$($InputVmDestPath.FullName)\$($VM.Name)\Snapshots" | |||
Write-Output "Fin déplacement" | |||
### Ajout Cluster | |||
Write-Output "Ajout de la VM au cluster" | |||
$VM | Add-ClusterVirtualMachineRole | |||
Write-Output "VM ajoutée" | |||
Write-Output "Fin des opérations du script" | |||
Write-Warning "Il faut mettre manuellement les VLANS sur les interfaces réseaux de la VM" | |||
</pre> | |||
=== Script HyperV-Bis === | |||
<pre> | |||
# Import xml de libvirt | |||
# convert raw en VHD | |||
# convert vhd en vhdx | |||
# Ajout VM dans cluster | |||
$ErrorActionPreference = "Stop" | |||
# Demandes des informations | |||
Write-Output "Choisir le chemin de la VM a importer :" | |||
$InputVmSrcPath=ls Z: | Select FullName | Out-GridView -OutputMode Single -Title "Choisir le chemin de la VM a importer :" | |||
Write-Output $InputVmSrcPath.Fullname | |||
if (-not (Test-Path -Path "$($InputVmSrcPath.FullName)\*.xml")) { | |||
Write-Error -Message "Erreur : XML de la VM non trouvé" | |||
exit | |||
} | |||
Write-Output "Choisir le chemin d'export :" | |||
$InputVmDestPath=ls C:\ClusterStorage | Select FullName | Out-GridView -OutputMode Single -Title "Choisir où exporter la VM :" | |||
Write-Output $InputVmDestPath.Fullname | |||
### Lecture XML | |||
$ConfigPath="$($InputVmSrcPath.FullName)\*.xml" | Resolve-Path | |||
[xml]$XmlDocument = Get-Content -Path $ConfigPath | |||
$VMName=$XmlDocument.domain.name | |||
$VMCpu=$XmlDocument.domain.vcpu.'#text' | |||
$VMMemory="$($XmlDocument.domain.memory.'#text')$($($XmlDocument.domain.memory.unit).Replace('iB','B'))"/1 | |||
if (($VMMemory/2MB) % 2 -ne 0) { ## The VM memory size need to be a multiple of 2MB | |||
$VMMemory=([Math]::Ceiling($VMMemory/2MB)*2MB) | |||
} | |||
$VMDisks=$XmlDocument.domain.devices.disk | |||
$VMInterfaces=$XmlDocument.domain.devices.interface | |||
$VMPath="$($InputVmDestPath.FullName)\$VMName" | |||
### Check presence fichier disks | |||
Foreach ($VMDisk in $VMDisks) { | |||
if ($VMDisk.device -eq 'disk') { | |||
if ($VMDisk.source.dev -match '/dev/(?<volume>[^/]+)/(?<name>.+)') { | |||
$name=$Matches.name | |||
if (Test-Path -PathType Leaf -Path "$($InputVmSrcPath.FullName)\$name.raw") { | |||
Write-Output "$name.raw trouvé" | |||
} | |||
else { | |||
Write-Error "Fichier $name.raw non trouvé dans $($InputVmSrcPath.FullName) !" | |||
Exit | |||
} | |||
} | |||
} | |||
} | |||
### Création dossiers | |||
Write-Output "Création des dossiers dans le ClusterStorage" | |||
New-Item -ItemType "Directory" -path $InputVmDestPath.FullName -Name $VMName -ErrorAction Ignore | |||
New-Item -ItemType "Directory" -path $VMPath -Name "Snapshots" -ErrorAction Ignore | |||
New-Item -ItemType "Directory" -path $VMPath -Name "Virtual Hard Disks" -ErrorAction Ignore | |||
New-Item -ItemType "Directory" -path $VMPath -Name "Virtual Machines" -ErrorAction Ignore | |||
Write-Output "Fin de la création des dossiers" | |||
### Creation VM | |||
$VM=New-VM -Name $VMName -Generation 1 -MemoryStartupBytes $VMMemory -Path $InputVmDestPath.FullName -BootDevice IDE -Verbose | |||
$VM | Set-VMProcessor -Count $VMCpu -verbose | |||
$VM | Remove-VMNetworkAdapter | |||
$VM | Get-VMDvdDrive | Remove-VMDvdDrive | |||
### Ajout interfaces | |||
Foreach ($VMInterface in $VMInterfaces) { | |||
if ($VMInterface.source.bridge -match '(?<sw>[a-zA-Z]+)(?<vlan>\d+)') { | |||
if ($VMInterface.mac.address) { | |||
Write-Output "Ajout interface $($VMInterface.mac.address) VLAN $($Matches.vlan)" | |||
$adp=$VM | Add-VMNetworkAdapter -StaticMacAddress $VMInterface.mac.address -SwitchName 'vswitch_csn' -Name "Interface VLAN $($Matches.vlan)" -Passthru -Verbose | |||
} | |||
else { | |||
Write-Output "Ajout interface VLAN $($Matches.vlan)" | |||
$adp=$VM | Add-VMNetworkAdapter -DynamicMacAddress $true -SwitchName 'vswitch_csn' -Name "Interface VLAN $($Matches.vlan)" -Passthru -Verbose | |||
} | |||
$adp | Set-VMNetworkAdapterVlan -Access -VlanId $Matches.vlan -Verbose | |||
} | |||
else { | |||
Write-Error "Erreur ajout interface $($VMInterface.mac.address)" | |||
} | |||
} | |||
### Ajout disks | |||
Foreach ($VMDisk in $VMDisks) { | |||
if ($VMDisk.device -eq 'disk') { | |||
if ($VMDisk.source.dev -match '/dev/(?<volume>[^/]+)/(?<name>.+)') { | |||
$name=$Matches.name | |||
Write-Output "$name.raw : Conversion du raw en VHD avec qemu-convert" | |||
& qemu-img.exe convert "$($InputVmSrcPath.FullName)\$name.raw" -O vpc -o subformat=fixed "$($InputVmSrcPath.FullName)\$name.vhd" | |||
Write-Output "$name.vhd : Suppression du sparse flag" | |||
& fsutil sparse setflag "$($InputVmSrcPath.FullName)\$name.vhd" 0 | |||
Write-Output "$name.vhd : Conversion du VHD en VHDX + mise dans ClusterVolume" | |||
$VHDXPath="$VMPath\Virtual Hard Disks\$name.vhdx" | |||
Convert-VHD -Path "$($InputVmSrcPath.FullName)\$name.vhd" -DestinationPath $vhdxPath -VHDType "Fixed" | |||
Write-Output "Ajout du disque $name dans $VHDXPath" | |||
$VM | Add-VMHardDiskDrive -Path $VHDXPath | |||
} | |||
else { | |||
Write-Warning "Impossible de matcher automatiquement $($VMDisk.source.dev). Faire a la main" | |||
} | |||
} | |||
else { | |||
Write-Output "Ignore ajout type non-disk" | |||
} | |||
} | |||
### Ajout Cluster | |||
Write-Output "Ajout de la VM au cluster" | |||
$VM | Add-ClusterVirtualMachineRole | |||
Write-Output "VM ajoutée" | |||
Write-Output "Fin des opérations du script" | |||
</pre> | |||
[[Catégorie:Script]] | [[Catégorie:Script]] | ||
Version du 11 mars 2019 à 11:07
Ce script sert à migrer une VM qui se trouve sur un Kvm vers un HyperV
Infos / Prérequis
1. Sur le kvm source, vérifier que le montage cifs vers l hyperv est OK (sinon mount -t cifs -o user=admin-agarik //172.30.107.99/Z$ /export) 2. Shutdown de la VM : 3. Lancement du script d'export sur le KVM hébergeant la machine, executer le script : /export/Scripts/mig.py --debug nom-de-la-VM Au cas où il y a un problème après la copie de TOUS les disques, il est possible de relancer avec l'option --no-copy Si on a besoin de faire des modifications a la main avant l'import, il est possible d'inhiber le processus de unmount/kpart -dv/losetup pour garder le montage avec l'option --no-clean. Il faudra faire les opérations manuellement Si le script dit VM not found on this HV, il faut faire un virsh define /etc/libvirt/qemu/le-nom-de-la-vm.xml et relancer le script 4. Imports dans hyperv Aller sur HV01.csn.fr, Cliquer sur le raccourci "Import depuis KVM" sur hv01 et choissisez la VM et le cluster volume Laissez les opérations se dérouler Si le script met plus de 15minutes à s'executer sans changer d'état, il faut le stopper, nettoyer la configuration de VM sur hv01 si elle s'est déja créée et relancer le script En cas de soucis, laisser la fenetre powershell avec l'erreur ouverte pour pouvoir Debug Cas connu : Error: "This document already has a 'DocumentElement' node." -> il y a plusieurs xml dans le dossier d’export, il faut supprimer le mauvais Cas connu : un problème a la création d’une interface réseau -> il faut éditer le xml (depuis le kvm) et ajouter l’adresse mac de l’AO au niveau de l’interface, exemple : <interface type='bridge'> <mac address='52:54:00:e1:df:a9'/> Allumer la VM sur le nouvel HV En cas de problème de boot, faire Echap lors du démarrage de grub, appuyer sur e pour editer la premiere entrée, vérifier que les lignes correspondent bien a celle du fichier /export/<nom de la VM>/files/menu.lst ou /export/<nom de la VM>/files/grub.cfg << Fichier: 1) appuyé sur e.png >> << Fichier: 2) editer kernet et initrd.png >> << Fichier: 3) editer kernel.png >> << Fichier: 4) editer initrd + boot.png >> Si ça marche toujours pas, on passe au rollback. Tu éteints la VM sur l’hyper-V, tu redémarres la VM sur le kvm avec pcs et tu fais préviens le cdp/srs et aussi le client ---
Script KVM
#!/usr/bin/env python
from optparse import OptionParser
import logging, subprocess, os, errno, atexit, time, re, tempfile
import xml.etree.ElementTree
logging.basicConfig(format='%(asctime)s - %(levelname)s - %(message)s', level=logging.INFO)
force_flag=False
#
srcVM=""
dstPath=""
noCopy_flag=False
doNotClean_flag=False
exportedRaw=[]
mountedPartPath=[]
def main():
global srcVM, noCopy_flag, doNotClean_flag
parser = OptionParser(usage="usage: %prog [options] VMname", version="%prog 1.0")
parser.add_option("-f", "--force",
action="store_true",
dest="force_flag",
default=False,
help="force")
parser.add_option("-n", "--no-copy",
action="store_true",
dest="noCopy_flag",
default=False,
help="Do not copy disk")
parser.add_option("-d", "--debug",
action="store_true",
dest="debug_flag",
default=False,
help="Print debug")
parser.add_option("-X", "--no-clean",
action="store_true",
dest="noclean_flag",
default=False,
help="No umount/no kpartx -d")
(options, args) = parser.parse_args()
if len(args) != 1:
parser.error("wrong number of arguments")
logging.debug("Starting script")
srcVM=args[0]
noCopy_flag=options.noCopy_flag
doNotClean_flag=options.noclean_flag
if options.debug_flag:
logging.getLogger().setLevel(logging.DEBUG)
check_VM()
exportXML()
copyDisk()
systemPart=getSystemPartition()
if not systemPart:
logging.critical("Could not find system partition")
exit()
linux_vers=detectLinuxVersion(systemPart)
if linux_vers == "unknown":
logging.critical("Could not determine Linux Version")
exit()
logging.info("VM - Linux Version : {0}".format(linux_vers))
filesPath=dstPath+"/files"
if not os.path.exists(filesPath):
try:
os.makedirs(filesPath)
except OSError as e:
logging.critical("main: Error creating {0} : {1}".format(filesPath,e))
exit()
#TODO : faire le reste
# modifie le fstab
#refait le truc udev
#installe un nouveau kernel
#refait le grub.conf bien
def check_VM():
"""Check existence of the VM"""
logging.debug("check_VM: Checking VM existence with virsh list")
proc = subprocess.Popen(['/usr/bin/virsh','list --all --name'], stdout=subprocess.PIPE)
tmp = proc.stdout.read()
VMs = tmp.splitlines()
logging.debug("check_VM: Found {0} VM on HV".format(len(VMs)))
if srcVM in VMs:
logging.info("check_VM: Found VM : {0}".format(srcVM))
else:
logging.critical("check_VM: VM {0} not found on this HV".format(srcVM))
exit()
def exportXML():
"""Export libvirt XML config to /export"""
global dstPath
dstPath = "/export/{0}".format(srcVM)
logging.debug("exportXML: Checking dir")
if not os.path.exists(dstPath):
logging.debug("exportXML: {0} does not exist, creating it".format(dstPath))
try:
os.makedirs(dstPath)
except OSError as e:
logging.critical("exportXML: Error creating {0} : {1}".format(dstPath,e))
exit()
else:
logging.debug("exportXML: {0} exists".format(dstPath))
logging.info("exportXML: exporting VM conf")
os.system("/usr/bin/virsh dumpxml {0} > {1}/{0}.xml".format(srcVM, dstPath))
def copyDisk():
"""Read XML conf to find all disk and copy them to export"""
global exportedRaw
disks=[]
logging.info("copyDisk: Searching for disk in XML")
tree = xml.etree.ElementTree.parse("{0}/{1}.xml".format(dstPath,srcVM))
root = tree.getroot()
for vmdisk in root.findall("./devices/disk"):
if vmdisk.attrib['device'] == 'disk':
srcDiskPath=vmdisk.find('source').attrib['dev']
logging.info("copyDisk: Found disk {0}".format(srcDiskPath))
disks.append(srcDiskPath)
for disk in disks:
dstRaw="{0}/{1}.raw".format(dstPath,os.path.basename(disk))
logging.info("copyDisk : Copying {0} to {1}".format(disk,dstRaw))
exportedRaw.append(dstRaw)
if noCopy_flag:
logging.info("copyDisk : option no-copy - skipping disk")
else:
#os.system("/bin/dd if={0} of={1} bs=2M".format(disk,dstRaw))
logging.info("copyDisk : temp")
def getSystemPartition():
"""Mount the system partition of the VM and return the path"""
logging.info("mountSystemPartition : Searching & mounting VM root partition")
for raw in exportedRaw:
logging.debug("mountSystemPartition : Searching {0}".format(raw))
mappings=run_kpartx(raw)
for mapping in mappings:
path="/dev/mapper/{0}".format(mapping)
# if kpartx fails
if not os.path.exists(path):
logging.critical("mountSystemPartition : {0} does not exist ! kpartx error?".format(path))
exit()
# Verify that there is a filesystem on the mapping
proc = subprocess.Popen(['/usr/bin/file','-s',path], stdout=subprocess.PIPE)
tmp = proc.stdout.read()
if "filesystem" not in tmp:
logging.debug("mountSystemPartition : ignoring {0} file return : \"{1}\"".format(path,tmp))
else:
tmpDir=tempfile.mkdtemp()
logging.debug("mountSystemPartition : mounting {0} file on {1}".format(path,tmpDir))
os.system("/bin/mount {0} {1}".format(path,tmpDir))
if not doNotClean_flag: atexit.register(undo_mount,tmpDir)
if os.path.exists(tmpDir+"/etc/passwd"):
logging.info("mountSystemPartition : Found system partition : {0}".format(path))
return tmpDir
return
def run_kpartx(raw):
"""Run kpartx and return a list of partition mappings"""
cleanMappings = []
proc = subprocess.Popen(['/sbin/kpartx','-av','{0}'.format(raw)], stdout=subprocess.PIPE)
tmp = proc.stdout.read()
output = tmp.splitlines()
if not doNotClean_flag: atexit.register(undo_kpartx,raw)
mapping_regex = re.compile(r'^add map ')
mappings = filter(mapping_regex.search, output)
for mapping in mappings:
m=mapping.split()[2]
logging.debug("run_kpartx : Mapping created : {0}".format(m))
cleanMappings.append(m)
return cleanMappings
def undo_kpartx(raw):
logging.debug("undo_kpartx : unmapping {0}".format(raw))
time.sleep(1)
os.system("kpartx -d {0} > /dev/null".format(raw))
def undo_mount(tmpDir):
logging.debug("undo_mount : unmounting {0}".format(tmpDir))
os.system("/bin/umount {0}".format(tmpDir))
os.system("/bin/rmdir {0}".format(tmpDir))
def detectLinuxVersion(root):
"""return centos6/centos7/debian7 or unknown"""
if os.path.exists(root+"/etc/redhat-release"):
file=open(root+"/etc/redhat-release", "r")
line=file.readline()
file.close()
if line.startswith("CentOS release 6"):
return "centos6"
if line.startswith("CentOS Linux release 7"):
return "centos7"
if os.path.exists(root+"/etc/debian_version"):
file=open(root+"/etc/debian_version", "r")
line=file.readline()
file.close()
if line.startswith("7."):
return "debian7"
return "unknown"
def changeFstab(root,filespath):
logging.debug("changeFstab : Copying original file to {0}/fstab.orig".format(filespath))
os.system("/bin/cp {0}/etc/fstab {1}/fstab.orig".format(root,filespath))
if __name__ == '__main__':
main()
Script HyperV
# Import VM
# convert vhdx
# Move VM
# Ajout VM dans cluster
$ErrorActionPreference = "Stop"
# Demandes des informations
Write-Output "Choisir le chemin de la VM a importer :"
$InputVmSrcPath=ls Z: | Select FullName | Out-GridView -OutputMode Single -Title "Choisir le chemin de la VM a importer :"
Write-Output $InputVmSrcPath.Fullname
if (-not (Test-Path -Path "$($InputVmSrcPath.FullName)\Virtual Machines\*.xml")) {
Write-Error -Message "Erreur : XML de la VM non trouvé dans le dossier Virtual Machines"
exit
}
Write-Output "Choisir le chemin d'export :"
$InputVmDestPath=ls C:\ClusterStorage | Select FullName | Out-GridView -OutputMode Single -Title "Choisir où exporter la VM :"
Write-Output $InputVmDestPath.Fullname
### Import
Write-Output "Début vérification compatibilité"
$ConfigPath="$($InputVmSrcPath.FullName)\Virtual Machines\*.xml" | Resolve-Path
Write-Output "Utilisation config : $ConfigPath"
$report=Compare-VM -Register -Path $ConfigPath
$report.Incompatibilities | Where-Object {$_.MessageID –eq 33012} | ForEach-Object {Connect-VMNetworkAdapter $_.Source –Switchname “Vswitch_CSN”}
Write-Output "Import de la VM"
$VM=Import-VM -CompatibilityReport $report
Write-Output "Fin import de la VM $($VM.Name)"
### Update config
Write-Output "Update configuration de la VM"
Update-VMVersion -Name $VM.Name -Force
Write-Output "Fin update"
### Création dossiers
Write-Output "Création des dossiers dans le ClusterStorage"
New-Item -ItemType "Directory" -path $InputVmDestPath.FullName -Name $VM.Name -ErrorAction Ignore
New-Item -ItemType "Directory" -path "$($InputVmDestPath.FullName)\$($VM.Name)" -Name "Snapshots" -ErrorAction Ignore
New-Item -ItemType "Directory" -path "$($InputVmDestPath.FullName)\$($VM.Name)" -Name "Virtual Hard Disks" -ErrorAction Ignore
New-Item -ItemType "Directory" -path "$($InputVmDestPath.FullName)\$($VM.Name)" -Name "Virtual Machines" -ErrorAction Ignore
Write-Output "Fin de la création des dossiers"
### Conversion disque
Foreach ($vhd in ($VM | Get-VMHardDiskDrive)) {
$vhdName=$vhd.Path | Split-path -Leaf -Resolve
$vhdxPath="$($InputVmDestPath.FullName)\$($VM.Name)\Virtual Hard Disks\$($vhdName)x"
Write-Output "Conversion du disque $($vhd.Path) vers $vhdxPath"
Convert-VHD -Path $($vhd.Path) -DestinationPath $vhdxPath -VHDType "Fixed"
Write-Output "Fin conversion de $($vhd.Path)"
Write-Output "Remplacement du VHD dans la configuration de VM"
$vhd | Set-VMHardDiskDrive -Path $vhdxPath
}
### Déplacement VM
Write-Output "Déplacement de la VM dans $($InputVmDestPath.FullName)"
$VM | Move-VMStorage -VirtualMachinePath "$($InputVmDestPath.FullName)\$($VM.Name)\Virtual Machines" `
-SmartPagingFilePath "$($InputVmDestPath.FullName)\$($VM.Name)\Virtual Machines" `
-SnapshotFilePath "$($InputVmDestPath.FullName)\$($VM.Name)\Snapshots"
Write-Output "Fin déplacement"
### Ajout Cluster
Write-Output "Ajout de la VM au cluster"
$VM | Add-ClusterVirtualMachineRole
Write-Output "VM ajoutée"
Write-Output "Fin des opérations du script"
Write-Warning "Il faut mettre manuellement les VLANS sur les interfaces réseaux de la VM"
Script HyperV-Bis
# Import xml de libvirt
# convert raw en VHD
# convert vhd en vhdx
# Ajout VM dans cluster
$ErrorActionPreference = "Stop"
# Demandes des informations
Write-Output "Choisir le chemin de la VM a importer :"
$InputVmSrcPath=ls Z: | Select FullName | Out-GridView -OutputMode Single -Title "Choisir le chemin de la VM a importer :"
Write-Output $InputVmSrcPath.Fullname
if (-not (Test-Path -Path "$($InputVmSrcPath.FullName)\*.xml")) {
Write-Error -Message "Erreur : XML de la VM non trouvé"
exit
}
Write-Output "Choisir le chemin d'export :"
$InputVmDestPath=ls C:\ClusterStorage | Select FullName | Out-GridView -OutputMode Single -Title "Choisir où exporter la VM :"
Write-Output $InputVmDestPath.Fullname
### Lecture XML
$ConfigPath="$($InputVmSrcPath.FullName)\*.xml" | Resolve-Path
[xml]$XmlDocument = Get-Content -Path $ConfigPath
$VMName=$XmlDocument.domain.name
$VMCpu=$XmlDocument.domain.vcpu.'#text'
$VMMemory="$($XmlDocument.domain.memory.'#text')$($($XmlDocument.domain.memory.unit).Replace('iB','B'))"/1
if (($VMMemory/2MB) % 2 -ne 0) { ## The VM memory size need to be a multiple of 2MB
$VMMemory=([Math]::Ceiling($VMMemory/2MB)*2MB)
}
$VMDisks=$XmlDocument.domain.devices.disk
$VMInterfaces=$XmlDocument.domain.devices.interface
$VMPath="$($InputVmDestPath.FullName)\$VMName"
### Check presence fichier disks
Foreach ($VMDisk in $VMDisks) {
if ($VMDisk.device -eq 'disk') {
if ($VMDisk.source.dev -match '/dev/(?<volume>[^/]+)/(?<name>.+)') {
$name=$Matches.name
if (Test-Path -PathType Leaf -Path "$($InputVmSrcPath.FullName)\$name.raw") {
Write-Output "$name.raw trouvé"
}
else {
Write-Error "Fichier $name.raw non trouvé dans $($InputVmSrcPath.FullName) !"
Exit
}
}
}
}
### Création dossiers
Write-Output "Création des dossiers dans le ClusterStorage"
New-Item -ItemType "Directory" -path $InputVmDestPath.FullName -Name $VMName -ErrorAction Ignore
New-Item -ItemType "Directory" -path $VMPath -Name "Snapshots" -ErrorAction Ignore
New-Item -ItemType "Directory" -path $VMPath -Name "Virtual Hard Disks" -ErrorAction Ignore
New-Item -ItemType "Directory" -path $VMPath -Name "Virtual Machines" -ErrorAction Ignore
Write-Output "Fin de la création des dossiers"
### Creation VM
$VM=New-VM -Name $VMName -Generation 1 -MemoryStartupBytes $VMMemory -Path $InputVmDestPath.FullName -BootDevice IDE -Verbose
$VM | Set-VMProcessor -Count $VMCpu -verbose
$VM | Remove-VMNetworkAdapter
$VM | Get-VMDvdDrive | Remove-VMDvdDrive
### Ajout interfaces
Foreach ($VMInterface in $VMInterfaces) {
if ($VMInterface.source.bridge -match '(?<sw>[a-zA-Z]+)(?<vlan>\d+)') {
if ($VMInterface.mac.address) {
Write-Output "Ajout interface $($VMInterface.mac.address) VLAN $($Matches.vlan)"
$adp=$VM | Add-VMNetworkAdapter -StaticMacAddress $VMInterface.mac.address -SwitchName 'vswitch_csn' -Name "Interface VLAN $($Matches.vlan)" -Passthru -Verbose
}
else {
Write-Output "Ajout interface VLAN $($Matches.vlan)"
$adp=$VM | Add-VMNetworkAdapter -DynamicMacAddress $true -SwitchName 'vswitch_csn' -Name "Interface VLAN $($Matches.vlan)" -Passthru -Verbose
}
$adp | Set-VMNetworkAdapterVlan -Access -VlanId $Matches.vlan -Verbose
}
else {
Write-Error "Erreur ajout interface $($VMInterface.mac.address)"
}
}
### Ajout disks
Foreach ($VMDisk in $VMDisks) {
if ($VMDisk.device -eq 'disk') {
if ($VMDisk.source.dev -match '/dev/(?<volume>[^/]+)/(?<name>.+)') {
$name=$Matches.name
Write-Output "$name.raw : Conversion du raw en VHD avec qemu-convert"
& qemu-img.exe convert "$($InputVmSrcPath.FullName)\$name.raw" -O vpc -o subformat=fixed "$($InputVmSrcPath.FullName)\$name.vhd"
Write-Output "$name.vhd : Suppression du sparse flag"
& fsutil sparse setflag "$($InputVmSrcPath.FullName)\$name.vhd" 0
Write-Output "$name.vhd : Conversion du VHD en VHDX + mise dans ClusterVolume"
$VHDXPath="$VMPath\Virtual Hard Disks\$name.vhdx"
Convert-VHD -Path "$($InputVmSrcPath.FullName)\$name.vhd" -DestinationPath $vhdxPath -VHDType "Fixed"
Write-Output "Ajout du disque $name dans $VHDXPath"
$VM | Add-VMHardDiskDrive -Path $VHDXPath
}
else {
Write-Warning "Impossible de matcher automatiquement $($VMDisk.source.dev). Faire a la main"
}
}
else {
Write-Output "Ignore ajout type non-disk"
}
}
### Ajout Cluster
Write-Output "Ajout de la VM au cluster"
$VM | Add-ClusterVirtualMachineRole
Write-Output "VM ajoutée"
Write-Output "Fin des opérations du script"