HyperV script de copie de snapshot

De BlaxWiki
Aller à la navigationAller à la recherche

Ceci est un script windows permettant de faire un snapshot des vm sur un hyperV. Il est appelé en crontab. Ce script fait appel à 2 sous script

Description

Bien que le log d'execution soit remonté dans la supervision, il est possible de consulter le fichier de log localement sur infra-hv1.fbd : C:\Agarik\Scripts\HyperVBackup\HyperVBackup.log

Configuration (à effectuer dans le bloc "CONFIGURATION" présent dans le script) :

# Which cluster shared volume (CSV) to backup
$strVolumePath = "C:\ClusterStorage\Volume1"

# Where to mount the snapshot ($strSnapshotDrive) and where to find the VM
($strSourcePath)
$strSnapshotDrive = "Z:"
$strSourcePath = $strSnapshotDrive

# Where to backup
$strBackupPath = "\\infra-backup\f$\VirtualMachines"

# What to backup
$arrVirtualMachines = @("wsus.fbd","txt-iis.fbd","txt-app.fbd","txt-sql1.fbd","ba-iis.fbd","ba-sql1.fbd","ba-app.fbd","ad2.fbd","ad1.fbd")

# Retention
$intRetention = 7


Le script effectue les actions suivantes :

- ouverture fichier de log C:\Agarik\Scripts\HyperVBackup\HyperVBackup.log
- verification de l'état de $strVolumePath, (BackupState = 0 (None) sinon on quitte avec une erreur)
- preparation de $strVolumePath a être snapshoté via l'appel au binaire $strExePCSV (qui appelle la fonctione ClusterPrepareSharedVolumeForBackup, voir David Couderc si le binaire a 
un problème, la création d'un binaire a été nécessaire car aucun langage "non compilé" ne permet l'appel à cette fonction)
- verification de l'état de $strVolumePath (BackupState = 1 (InProgress) sinon on quitte avec une erreur)
- creation du snapshot et montage en tant que Z: (avec diskshadow.exe)
- archivage des precedents backups dans la limite de $intRetention
- pour chaque VM dans la liste $arrVirtualMachines, backup vers $strBackupPath
- demontage du lecteur Z: et suppression du snapshot (avec diskshadow.exe)
- envoi du resultat à la supervision (avec vc_send) 

HyperVBackup.ps1

# ------------------------------------------------------------------
# ABOUT

# ABOUT::Author
# Yoann Moriot

# ABOUT::Version
# 1.5 2012/09/12 Suppression du bloc de backup des VMs ad1 et ad2 (pas de consistance du backup effectué de cette manière)
# 1.4 2012/07/26 Ajout d'un bloc pour le backup des VMs ad1 et ad2
# 1.3 2012/04/25 Improved output display
# 1.2 2012/04/24 Added $script:arrErrLog to store errors 
#				 and output them on top of the log
# 1.1 2012/04/23 Added Operia integration with vc_send
# 1.0 2012/04/19

# /ABOUT
# ------------------------------------------------------------------

# ------------------------------------------------------------------
# CONFIGURATION

# Monitoring
$strVCSend = "C:\Agarik\Vision\bin\vc_send.exe"
$strVCDest = "10.252.15.235"
$strVCHost = "infra-backup.fbd"
$strVCService = "backup"
$strVCColor = "red"
$intVCValidity = 1560
$strVCMessage = ""

# Which cluster shared volume (CSV) to backup
$strVolumePath = "C:\ClusterStorage\Volume2"

# Where to mount the snapshot ($strSnapshotDrive) and where to find the VM ($strSourcePath)
$strSnapshotDrive = "Z:"
$strSourcePath = $strSnapshotDrive

# Where to backup
$strBackupPath = "\\infra-backup\f$\VirtualMachines"

# What to backup
$arrVirtualMachines = @("wsus.fbd","cas-e2k10.fbd","txt-iis.fbd","txt-app.fbd","txt-sql1.fbd","ba-iis.fbd","ba-sql1.fbd","ba-app.fbd","vm-admin.fbd","mediacontact.fbd","sicompta_iis1.fbd","sicompta_sql1.fbd","sicompta_tse1.fbd")

# Retention
$intRetention = 5

# /CONFIGURATION
# ------------------------------------------------------------------

# ------------------------------------------------------------------
# SYSTEM

$bDEBUG = 0 # 0 = disabled, 1 = enabled

# DONT EDIT BELOW THIS LINE
$ERROR_CSV_BACKUPSTATE_CANNOTPREPARE = 1
$ERROR_CSV_BACKUPSTATE_NOTREADY = 2
$ERROR_DS_CREATESNAPSHOT_FAILED = 3
$ERROR_DS_REMOVESNAPSHOT_FAILED = 4
$ERROR_CSV_PREPAREBACKUP_FAILED = 5

$intError = 0

$strScriptDir = Split-Path -Path $MyInvocation.MyCommand.Definition
$strExePCSV = $strScriptDir + "\bin\PCSV.exe" # path to ClusterPrepareSharedVolumeForBackup binary
$strExeDS = "C:\Windows\system32\diskshadow.exe"
$colVMBackup = @{} # will hold the backup result per VM

$strLogFile = $strScriptDir + "\HyperVBackup.log"
$script:arrLog = @()
$script:arrErrLog = @()

# Temp files
$strDSMetadata = $strScriptDir + "\metadata.cab"
$strCreateSnapshot = $strScriptDir + "\scripts\CreateSnapshot.script"
$strRemoveSnapshot = $strScriptDir + "\scripts\RemoveSnapshot.script"

# /SYSTEM
# ------------------------------------------------------------------

# ------------------------------------------------------------------
# FUNCTIONS
# Get-SharedVolume : returns an object containing properties for the $strPath shared volume
# See http://msdn.microsoft.com/en-us/library/windows/desktop/ee342510(v=vs.85).aspx
function Get-SharedVolume( [string]$strPath )
{
	$strPath = $strPath.Replace("\", "\\")
	$strQuery = "select * from MSCluster_ClusterSharedVolume where Name='" + $strPath + "'"
	get-wmiobject -namespace root\MSCluster -query $strQuery
}

function log( [string]$strString )
{
	$strLine = (Get-CurrentTimestamp) + " > " + $strString + "`r`n"
	$script:arrLog += $strLine
	If ( $strString.StartsWith("ERROR") )
	{
		$script:arrErrLog += $strLine
	}
	write-host $strLine
}

function Get-CurrentTimestamp
{
	$objDate = Get-Date
	$strCurrentTimestamp = $objDate.ToShortDateString() + " " + $objDate.ToLongTimeString()
	$strCurrentTimestamp
}

function vc_send
{

	If ( $script:arrErrLog.Length -gt 0 )
	{
		$strVCMessage += "ERRORS : `r`n`r`n"
		ForEach($strLogLine in $script:arrErrLog)
		{
			$strVCMessage += $strLogLine
		}
		$strVCMessage += "`r`n"
		$strVCMessage += "================================================="
		$strVCMessage += "`r`n"
		$strVCMessage += "`r`n"
	}
	ForEach($strLogLine In $script:arrLog)
	{
		$strVCMessage += $strLogLine
	}
	
	& $strVCSend $strVCDest $strVCHost $strVCService $strVCColor $intVCValidity $strVCMessage
	Stop-Transcript
}

# /FUNCTIONS
# ------------------------------------------------------------------

Start-Transcript $strLogFile

# ------------------------------------------------------------------
# PREFLIGHT

# PREFLIGHT::Check
log "-------------------------------------------------"
log "INFO: Checking for $strVolumePath status"
$objSharedVolume = Get-SharedVolume $strVolumePath
If ( $bDEBUG ) { log "DEBUG: objSharedVolume.BackupState = $($objSharedVolume.BackupState)" } # expect : 0 (None)
If ( $bDEBUG ) { log "DEBUG: objSharedVolume.FaultState = $($objSharedVolume.FaultState)" } # expect : 0 (NoFaults)
If ( $objSharedVolume.BackupState -ne 0 ) {
	log "ERROR ($ERROR_CSV_BACKUPSTATE_CANNOTPREPARE): $strVolumePath cannot be prepared for shadow copy (objSharedVolume.BackupState -ne 0)"
	vc_send
	exit $ERROR_CSV_BACKUPSTATE_CANNOTPREPARE
}
log "INFO: $strVolumePath ready to be prepared for snapshot"

# PREFLIGHT::Prepare
log "-------------------------------------------------"
log "INFO: Preparing $strVolumePath for snapshot with $strExePCSV"
# ClusterPrepareSharedVolumeForBackup
& $strExePCSV $strVolumePath
If (! ($?) ) {
	log "ERROR ($ERROR_CSV_PREPAREBACKUP_FAILED): Failed to run $strExePCSV, exit code : $lastexitcode"
	vc_send
	exit $ERROR_CSV_PREPAREFAILED
}
log "INFO: $strVolumePath prepared for snapshot"

log "-------------------------------------------------"
log "INFO: Checking for $strVolumePath status"
$objSharedVolume = Get-SharedVolume $strVolumePath
If ( $bDEBUG ) { log "DEBUG: objSharedVolume.BackupState = $($objSharedVolume.BackupState)" } # expect : 1 (InProgress)
If ( $bDEBUG ) { log "DEBUG: objSharedVolume.FaultState = $($objSharedVolume.FaultState)" } # expect : 1 (NoDirectIO)
If ( $objSharedVolume.BackupState -ne 1 ) {
	log "ERROR ($ERROR_CSV_BACKUPSTATE_NOTREADY): $strVolumePath not ready for shadow copy (objSharedVolume.BackupState -ne 1)"
	vc_send
	exit $ERROR_CSV_BACKUPSTATE_NOTREADY
}
log "INFO: $strVolumePath ready for snapshot"

# PREFLIGHT::BUILD_SCRIPTS
# PREFLIGHT::BUILD_SCRIPTS::CreateSnapshot
$arrCreateSnapshot = @()
$arrCreateSnapshot += "set context persistent nowriters"
$arrCreateSnapshot += "set verbose on"
$arrCreateSnapshot += "set metadata " + $strDSMetadata
$arrCreateSnapshot += "begin backup"
$arrCreateSnapshot += "add volume " + $objSharedVolume.VolumeName + " alias vm"
$arrCreateSnapshot += "create"
$arrCreateSnapshot += "expose %vm% " + $strSnapshotDrive
$arrCreateSnapshot | out-file -Encoding ASCII $strCreateSnapshot

# PREFLIGHT::BUILD_SCRIPTS::RemoveSnapshot
$arrRemoveSnapshot = @()
$arrRemoveSnapshot += "set context persistent nowriters"
$arrRemoveSnapshot += "delete shadows exposed " + $strSnapshotDrive
$arrRemoveSnapshot += "end backup"
$arrRemoveSnapshot | out-file -Encoding ASCII $strRemoveSnapshot

# /PREFLIGHT::BUILD_SCRIPTS

# /PREFLIGHT
# ------------------------------------------------------------------

# ------------------------------------------------------------------
# MAIN

# MAIN::ROTATE_BACKUP
log "-------------------------------------------------"
log "INFO: Rotating backups"
# Delete the backup that reached $intRetention
$strPrevBackupPath = $strBackupPath + "." + $intRetention
If ( Test-Path -Path $strPrevBackupPath ) 
{ 
	log "INFO: Deleting $strPrevBackupPath"
	Remove-Item -Recurse -Force $strPrevBackupPath
	sleep 5
}

# Rotate the backups
For ($i = $intRetention; $i -gt 0; $i--)
{
	If ( $i -eq 1 ) 
	{
		$strPrevBackupPath = $strBackupPath
	}
	Else
	{
		$strPrevBackupPath = $strBackupPath + "." + ($i - 1)
	}
	
	$strNewBackupPath = $strBackupPath + "." + $i

	If ( Test-Path -Path $strPrevBackupPath ) {
		log "INFO: Moving $strPrevBackupPath to $strNewBackupPath"
		Rename-Item $strPrevBackupPath $strNewBackupPath
		sleep 1
	}
}
# /MAIN::ROTATE_BACKUP

# MAIN::CREATE_SNAPSHOT::run_diskshadow
log "-------------------------------------------------"
log "INFO: Creating snapshot of $strVolumePath and mounting it as $strSnapshotDrive"

& $strExeDS -s $strCreateSnapshot
If (! ($?) ) {
	log "ERROR ($ERROR_DS_CREATESNAPSHOT_FAILED): Failed to run $strExeDS /s $strCreateSnapshot, exit code : $lastexitcode"
	vc_send
	exit $ERROR_DS_CREATESNAPSHOT_FAILED
}
log "INFO: $strExeDS -s $strCreateSnapshot ended with exit code $lastexitcode"

# /MAIN::CREATE_SNAPSHOT

# MAIN::BACKUP_VIRTUALMACHINES
ForEach($strVM In $arrVirtualMachines) {

	$strVMSourcePath = $strSourcePath + "\" + $strVM
	$strVMBackupPath = $strBackupPath + "\" + $strVM

	If ( $bDEBUG ) { log "DEBUG: strVMSourcePath = $strVMSourcePath" }
	If ( $bDEBUG ) { log "DEBUG: strVMBackupPath = $strVMBackupPath" }
	
	# Create $colVMBackup entry for $strVM
	$colVM = @{}
	$colVMBackup.Add($strVM, $colVM)
	$colVMBackup[$strVM]["Start"] = Get-CurrentTimestamp
	$colVMBackup[$strVM]["SourceDir"] = $strVMSourcePath
	$colVMBackup[$strVM]["BackupDir"] = $strVMBackupPath
	$colVMBackup[$strVM]["CurrentSize"] = 0
	$colVMBackup[$strVM]["BackupSize"] = 0
	$colVMBackup[$strVM]["End"] = 0
	$colVMBackup[$strVM]["BackupDuration"] = 0
	$colVMBackup[$strVM]["Result"] = 0
	
	log "-------------------------------------------------"
	log "INFO: Starting backup of $strVM"
	# Backup the VM
	If ( ! ( Test-Path -path $strVMSourcePath ) ) {
		$colVMBackup[$strVM]["Result"] = "ERROR: Cannot find " + $strVMSourcePath
		log "ERROR (0): Cannot find $strVMSourcePath, cannot backup $strVM"
		$intError++
	}
	Else
	{
		# Get the current VM size
		$objVMSourcePath = (Get-ChildItem $strVMSourcePath -recurse | Measure-Object -property length -sum)
		$colVMBackup[$strVM]["CurrentSize"] = "{0:N2}" -f ($objVMSourcePath.sum / 1MB) + " MB"
		
		# Copy the VM, copy-item automatically creates destination dir if required
		log "INFO: Copying $strVMSourcePath to $strVMBackupPath"
		Copy-Item $strVMSourcePath -destination $strVMBackupPath -recurse
		If ( $? )
		{
			$colVMBackup[$strVM]["Result"] = "Success"
		}
		Else
		{
			$colVMBackup[$strVM]["Result"] = "ERROR (0): Failed to copy $strVMSourcePath to $strVMBackupPath"
			log "ERROR (0): Failed to copy $strVMSourcePath to $strVMBackupPath"
			$intError++
		}
		
		# Get the backup size
		$objVMBackupPath = (Get-ChildItem $strVMBackupPath -recurse | Measure-Object -property length -sum)
		$colVMBackup[$strVM]["BackupSize"] = "{0:N2}" -f ($objVMBackupPath.sum / 1MB) + " MB"		
			
		$colVMBackup[$strVM]["End"] = Get-CurrentTimestamp
		$colVMBackup[$strVM]["BackupDuration"] = New-Timespan $colVMBackup[$strVM]["Start"] $colVMBackup[$strVM]["End"]
	}
	
	log "INFO: Finished backup of $strVM, BackupSize = $($colVMBackup[$strVM][`"BackupSize`"]), SourceSize = $($colVMBackup[$strVM][`"CurrentSize`"]), BackupDuration = $($colVMBackup[$strVM][`"BackupDuration`"])"
	log "INFO: Result = $($colVMBackup[$strVM][`"Result`"])"
}
# /MAIN::BACKUP_VIRTUALMACHINES

# MAIN::REMOVE_SNAPSHOT::run_diskshadow
log "-------------------------------------------------"
log "INFO: Unmounting $strSnapshotDrive and removing snapshot of $strVolumePath"
& $strExeDS /s $strRemoveSnapshot
If (! ($?) ) {
	log "ERROR ($ERROR_DS_REMOVESNAPSHOT_FAILED): Failed to run $strExeDS /s $strRemoveSnapshot, exit code : $lastexitcode"
	vc_send
	exit $ERROR_DS_REMOVESNAPSHOT_FAILED
}
log "INFO: $strExeDS -s $strRemoveSnapshot ended with exit code $lastexitcode"


# /MAIN::REMOVE_SNAPSHOT

# MAIN::MONITORING
If ( $intError -eq 0 ) { $strVCColor = "green" }
vc_send
exit 0

CreateSnapshot.script

set context persistent nowriters
set verbose on
set metadata C:\Agarik\Scripts\HyperVBackup\metadata.cab
begin backup
add volume \\?\Volume{41bf2ab9-d5fd-4cb7-ae13-a5a6291e3596} alias vm
create
expose %vm% Z:

RemoveSnapshot.script

set context persistent nowriters
delete shadows exposed Z:
end backup