Встал острый вопрос в необходимости бэкапирования виртуальных машин в Hyper-V.
Бесплатных продуктов увы я не нашёл.
Нашел скрипт HyperV Backup Tool на сайте sysadmins.ru, который постоянно полудохлый (сайт), скачал скрипт и ... отложил на потом. Всем вроде бы и хорош, но чтобы восстановить ВМ, необходимо юзать пресловутый скрипт, который найти в инете, в тот момент когда sysadmins.ru "лежит", проблематично, при условии, что восстанавливать необходимо немедленно. Это не совсем удобно, жизненные случаи полны сюрпризов и без этого.
Мне попался весьма интересный скриптец HVExport.ps1 на Power Shell.
Полный экспорт состояния машины – настройки, диски, снимки, сохранённые состояния, сетевые подключения.
Совместим со встроенным экспортом/импортом Hyper-V 2008 и 2008 R2. Машину можно импортировать средствами любого Hyper-V сервера.
Экспорт работающих машин без остановки.
Можно пропускать некоторые vhd-диски при экспорте. Например, у вас есть сервер, на диске D которого лежат только базы данных. Если у баз есть собственные резервные копии, то диск D можно пропустить, и базы потом восстановить отдельно.
Для Server 2008 R2 – более скоростное небуферизированное копирование файлов (xcopy /j).
HVExport.ps1 VirtualSystemName Destination [Subfolder] [/SkipVhd [vhd1][, vhd2]…]
VirtualSystemName – Имя виртуальной машины, под которым она записана в диспетчере Hyper-V.
Destination – Путь к папке, в которую будет экспортирована виртуальная машина.
Subfolder – Имя подпапки, которая будет создана в Destination. Если имя содержит звёздочку, то она будет заменена на текущую дату и время. Имя по умолчанию VirtualSystemName_*
/SkipVhd [vhd1][, vhd2] … – Имена VHD-файлов дисков, которые нужно пропустить при экспорте. Если нужно пропустить разностный (.avhd) диск, то укажите только имя корневого VHD-файла.
Если имена или пути содержат пробелы или запятые, заключайте имена в кавычки.
Примеры:
PS: снапшотить и бэкапить контроллеры домена таким образом крайне не рекомендуется, т.к. можно легко нарваться на USN rollback, DC необходимо бекапить штатными средствами ОС, как вариант (на крайний случай) забить на убитый виртуальный DC и поднять новый, с учетом наличия рабочего "железного" DC в сети, в последствии с удалением из AD мертвого DC.
Сам скрипт: Скачать
Бесплатных продуктов увы я не нашёл.
Нашел скрипт HyperV Backup Tool на сайте sysadmins.ru, который постоянно полудохлый (сайт), скачал скрипт и ... отложил на потом. Всем вроде бы и хорош, но чтобы восстановить ВМ, необходимо юзать пресловутый скрипт, который найти в инете, в тот момент когда sysadmins.ru "лежит", проблематично, при условии, что восстанавливать необходимо немедленно. Это не совсем удобно, жизненные случаи полны сюрпризов и без этого.
Мне попался весьма интересный скриптец HVExport.ps1 на Power Shell.
Основные возможности:
Совместим со встроенным экспортом/импортом Hyper-V 2008 и 2008 R2. Машину можно импортировать средствами любого Hyper-V сервера.
Экспорт работающих машин без остановки.
Можно пропускать некоторые vhd-диски при экспорте. Например, у вас есть сервер, на диске D которого лежат только базы данных. Если у баз есть собственные резервные копии, то диск D можно пропустить, и базы потом восстановить отдельно.
Для Server 2008 R2 – более скоростное небуферизированное копирование файлов (xcopy /j).
Использование:
VirtualSystemName – Имя виртуальной машины, под которым она записана в диспетчере Hyper-V.
Destination – Путь к папке, в которую будет экспортирована виртуальная машина.
Subfolder – Имя подпапки, которая будет создана в Destination. Если имя содержит звёздочку, то она будет заменена на текущую дату и время. Имя по умолчанию VirtualSystemName_*
/SkipVhd [vhd1][, vhd2] … – Имена VHD-файлов дисков, которые нужно пропустить при экспорте. Если нужно пропустить разностный (.avhd) диск, то укажите только имя корневого VHD-файла.
Если имена или пути содержат пробелы или запятые, заключайте имена в кавычки.
Примеры:
HVExport.ps1 Server01 D:\Backups HVExport.ps1 «Domain controller» "D:\Backups\Virtual Servers" HVExport.ps1 Server01 \\nas\Backups /SkipVhds server-01-data.vhd HVExport.ps1 Server01 \\nas\Backups Server01Today /SkipVhds server-01-data.vhd HVExport.ps1 Server01 \\nas\Backups /SkipVhds server-01-data.vhd, "all profiles.vhd"Решил проверить не деле, закинул скрипт на сервер, подключился к серверу по RDP, в cmd выполнил powershell.exe, запустил скрипт, отработал на УРА. Осталось дело за автоматизацией через планировщик заданий. Как вариант SCHTASKS /? из коммандной строки сервера, либо удаленно через оснастку mmc. Так же необходимо сделать ротацию бэкапов.
PS: снапшотить и бэкапить контроллеры домена таким образом крайне не рекомендуется, т.к. можно легко нарваться на USN rollback, DC необходимо бекапить штатными средствами ОС, как вариант (на крайний случай) забить на убитый виртуальный DC и поднять новый, с учетом наличия рабочего "железного" DC в сети, в последствии с удалением из AD мертвого DC.
Сам скрипт: Скачать
Write-Host "Скрипт экспорта Hyper-V"
Write-Host "(c) Экспресс АйТи, 2011"
$help = "
HVExport.ps1 VirtualSystemName Destination [Subfolder] [/SkipVhd [vhd1][, vhd2]...]
VirtualSystemName Имя виртуальной машины, под которым она записана
в диспетчере Hyper-V.
Destination Путь к папке, в которую будет экспортирована
виртуальная машина.
Subfolder Имя подпапки, которая будет создана в Destination.
Если имя содержит звёздочку, то она будет заменена
на текущую дату и время. Имя по умолчанию
VirtualSystemName_*
/SkipVhd [vhd1][, vhd2] ...
Имена VHD-файлов дисков, которые нужно пропустить
при экспорте. Если нужно пропустить разностный (.avhd)
диск, то укажите только имя корневого VHD-файла.
Если имена или пути содержат пробелы или запятые, заключайте имена в кавычки
Примеры:
HVExport.ps1 Server01 D:\Backups
HVExport.ps1 `"Domain controller`" `"D:\Backups\Virtual Servers`"
HVExport.ps1 Server01 \\nas\Backups /SkipVhds server-01-data.vhd
HVExport.ps1 Server01 \\nas\Backups Server01Today /SkipVhds server-01-data.vhd
HVExport.ps1 Server01 \\nas\Backups /SkipVhds server-01-data.vhd, `"all profiles.vhd`"
"
$commandLineHelp = "Наберите HVExport.ps1 /? для помощи."
# =================================================================
# Список аргументов и ключей командной строки
$knownSwitches = "skipVhd"
$argumentsList = "virtualServer,destination,subfolder"
# =================================================================
# Конвертирование аргументов и ключей командной строки в переменные
$knownSwitchesLower = $knownSwitches.ToLower().Split(',')
$knownSwitches = $knownSwitches.Split(',')
$argumentsList = $argumentsList.Split(',')
$unknownSwitch = $null
$warningCode = 0
$key = $null
$argumentsCount = 0
foreach($arg in $args) {
if(($arg.GetType() -eq [string]) -and ($arg.StartsWith("/") -or $arg.StartsWith("-"))) {
$key = $arg.Substring(1)
$index = [Array]::IndexOf($knownSwitchesLower, $key.ToLower())
if($index -eq -1) {
$unknownSwitch = $key
}
else {
$key = $knownSwitches[$index]
Invoke-Expression "`$$key = `$true"
}
}
else {
if($key) {
Invoke-Expression "`$$key = `$arg"
$key = $null
}
else {
$argument = $argumentsList[$argumentsCount]
$argumentsCount++
Invoke-Expression "`$$argument = `$arg"
}
}
}
# =================================================================
# Проверка аргументов и ключей
if((-not $virtualServer) -or ($virtualServer -eq "?") -or ($unknownSwitch -eq "?")) {
$help
exit
}
function CriticalError([int]$exitCode, $message) {
Write-Host ""
Write-Host "ОШИБКА : $message"
exit $exitCode
}
function ShowWarning($message) {
Write-Host ""
Write-Host "ПРЕДУПРЕЖДЕНИЕ : $message"
}
if($unknownSwitch) {
CriticalError 11 "Ключ командной строки '$unknownSwitch' не поддерживается.
$commandLineHelp"
}
if(-not $destination) {
CriticalError 11 "Не достаточно аргументов в командной строке.
$commandLineHelp"
}
if(-not $subfolder) {
$subfolder = $virtualServer + "_*"
}
$subfolder = $subfolder.Replace("*", [DateTime]::Now.ToString("ddMMyyyy_HHmm"))
$backupFolder = [System.IO.Path]::Combine($destination, $subfolder)
$skipVhdLower = @()
if($skipVhd) {
foreach($vhd in $skipVhd) {
$skipVhdLower += $vhd.ToLower()
}
}
# =================================================================
# Сбор информации
Write-Host "Получение информации о виртуальной машине $virtualServer ... " -NoNewline
$vm = Get-WmiObject -Namespace "root\virtualization" -Query "Select * From Msvm_ComputerSystem Where ElementName='$virtualServer'"
if(-not $vm) {
CriticalError 12 "Bиртуальная машина $virtualServer не найдена."
}
Write-Host "ОК"
$vmId = $vm.Name
$vmSaved = $vm.EnabledState -eq 32769
$currentAndSnapshots = Get-WmiObject -Namespace "root\virtualization" -Query "SELECT * FROM Msvm_VirtualSystemSettingData WHERE SystemName='$vmId'"
$globalSettings = Get-WmiObject -Namespace "root\virtualization" -Query "Select * From Msvm_VirtualSystemGlobalSettingData Where ElementName='$virtualServer'"
$imageManagement = Get-WmiObject Msvm_ImageManagementService -Namespace "root\virtualization"
$vmRoot = [System.IO.Path]::Combine($globalSettings.ExternalDataRoot, "Virtual Machines")
$snapshotRoot = [System.IO.Path]::Combine($globalSettings.SnapshotDataRoot, "Snapshots")
Write-Host "Корневая папка в.м. : $vmRoot"
Write-Host "Корневая папка сников : $snapshotRoot"
Write-Host ""
Write-Host "Поиск жёстких дисков виртуальной машины ... " -NoNewline
$parentsByVhds = @{}
$vhdsByDriveIds = @{}
$infosByVhds = @{}
$rootVhdsToExport = @()
$diffVhdsToExport = @()
foreach($settings in $currentAndSnapshots) {
$vmResources = Get-WmiObject -Namespace "root\virtualization" -Query "ASSOCIATORS OF {$settings} WHERE AssocClass=Msvm_VirtualSystemSettingDataComponent"
foreach($resource in $vmResources) {
if(($resource.ResourceType -ne 21) -or ($resource.ResourceSubType -ne "Microsoft Virtual Hard Disk") -or (-not $resource.Connection)) {
continue
}
$resourceVhd = $resource.Connection[0]
$drive = [Wmi]$resource.Parent
$vhdsByDriveIds[$drive.InstanceID.ToLower()] = $resourceVhd.ToLower()
$vhd = $resourceVhd
while($vhd) {
if(-not (Test-Path "$vhd")) {
ShowWarning "Образ жёсткого диска $resourceVhd не найден"
$warningCode = 3
$root = $null
break
}
$vhdInfo = $imageManagement.GetVirtualHardDiskInfo($vhd)
$infosByVhds[$vhd.ToLower()] = $vhdInfo.Info
$infoDocument = New-Object System.Xml.XmlDocument
$infoDocument.LoadXml($vhdInfo.Info)
$parentPathNode = $infoDocument.SelectSingleNode("//PROPERTY[@NAME = 'ParentPath']/VALUE/child::text()")
$root = $vhd
if($parentPathNode -and $parentPathNode.Value) {
$parent = $parentPathNode.Value
}
else {
$parent = $null
}
$parentsByVhds[$vhd.ToLower()] = $parent
$root = $vhd
$vhd = $parent
}
if(-not $root) {
continue
}
$root = $root.ToLower()
if([Array]::IndexOf($skipVhdLower, [System.IO.Path]::GetFileName($root)) -eq -1) {
if([Array]::IndexOf($rootVhdsToExport, $root) -eq -1) {
$rootVhdsToExport += $root
}
$vhd = $resourceVhd
while($vhd) {
$vhd = $vhd.ToLower()
if(($vhd -ne $root) -and ([Array]::IndexOf($diffVhdsToExport, $vhd) -eq -1)) {
$diffVhdsToExport += $vhd
}
$vhd = $parentsByVhds[$vhd]
}
}
}
}
if(-not $rootVhdsToExport) {
ShowWarning "Нет жёстких дисков для экспорта.
Диски не найдены, либо все пропущены."
$warningCode = 1
}
else {
$rootCount = $rootVhdsToExport.Length
Write-Host "$rootCount шт."
foreach($root in $rootVhdsToExport) {
Write-Host "$root"
}
$avhdCount = $diffVhdsToExport.Length
Write-Host "Дифференциальных (.avhd) дисков : $avhdCount шт."
}
$allVhdsToExport = @()
$allVhdsToExport += $rootVhdsToExport
$allVhdsToExport += $diffVhdsToExport
# =================================================================
# Очистка старых теневых копий
Write-Host "Проверка ссылок на теневые копии"
$shadowCopyLinkPrefix = "ExIT_"
$pattern = ".*<SYMLINKD>[ ]*$shadowCopyLinkPrefix(?<PID>[0-9]*)[ ]*\[(?<Volume>.*)\]"
$regex = New-Object System.Text.RegularExpressions.Regex $pattern
$volumes = Get-WmiObject -Query "SELECT * FROM Win32_Volume WHERE DriveType=3"
$shadowCopies = Get-WmiObject Win32_ShadowCopy
foreach($volume in $volumes) {
$drive = $volume.DriveLetter
if(-not ($drive -and (Test-Path $drive\$shadowCopyLinkPrefix*))) {
continue
}
$folderList = cmd /c dir $drive\$shadowCopyLinkPrefix*
foreach($line in $folderList) {
$match = $regex.Match($line)
if($match.Success) {
$creatorPid = $match.Groups["PID"].Value
$linkedVolume = $match.Groups["Volume"].Value.TrimEnd('\')
$linkRoot = "$drive\$shadowCopyLinkPrefix$creatorPid"
$item = get-item $linkRoot
$linkCreationTime =[System.Management.ManagementDateTimeconverter]::ToDmtfDateTime($item.CreationTime)
$process = Get-WmiObject -Query "SELECT * FROM Win32_Process WHERE Handle='$creatorPid' AND CreationDate < '$linkCreationTime'"
if($process) {
$name = $process.Name
Write-Host "Ссылка $linkRoot используется $creatorPid $name"
}
else {
Write-Host "Ссылка $linkRoot создана процессом, который уже завершил работу"
Write-Host "Удаление ссылки"
$temp = cmd /c rmdir $linkRoot
foreach($shadowCopy in $shadowCopies) {
if($shadowCopy.DeviceObject.TrimEnd('\') -ne $linkedVolume) {
continue
}
$shadowCopyId = $shadowCopy.ID
Write-Host "Удаление теневой копии $shadowCopyId"
$temp = vssadmin delete shadows /shadow=$shadowCopyId /Quiet
}
}
}
}
}
# =================================================================
# Экспорт
function WriteValue([System.Xml.XmlWriter]$writer, $value, [string]$arraySuffix) {
if($value -eq $null) {
return
}
$writer.WriteStartElement("VALUE" + $arraySuffix)
if(-not $nullOut) {
if($arraySuffix) {
foreach($item in $value) {
WriteValue $writer $item $null
}
}
else {
$valueString = $value.ToString()
if($value.GetType() -eq [Boolean]) {
$valueString = $valueString.ToUpper()
}
$writer.WriteString($valueString)
}
}
$writer.WriteString($null)
$writer.WriteEndElement()
}
function WriteObject([System.Xml.XmlWriter]$writer, [Wmi]$wmiObject, [Hashtable]$mandatoryValues) {
$writer.WriteWhitespace([Environment]::NewLine)
$writer.WriteWhitespace(" ")
$writer.WriteStartElement("VALUE.OBJECT")
$writer.WriteStartElement("INSTANCE")
$writer.WriteAttributeString("CLASSNAME", $wmiObject.ClassPath.ClassName)
foreach($property in $wmiObject.Properties) {
$arraySuffix = ""
if($property.IsArray) {
$arraySuffix = ".ARRAY"
}
$writer.WriteStartElement("PROPERTY" + $arraySuffix)
$writer.WriteAttributeString("NAME", $property.Name)
if($property.IsLocal) {
$writer.WriteAttributeString("TYPE", $property.Type.ToString().ToLower())
$value = $property.Value
if($mandatoryValues -and $mandatoryValues.ContainsKey($property.Name)) {
$value = $mandatoryValues[$property.Name]
}
WriteValue $writer $value $arraySuffix
}
else {
$writer.WriteAttributeString("PROPAGATED", "true")
$writer.WriteAttributeString("TYPE", $property.Type.ToString().ToLower())
}
$writer.WriteString($null)
$writer.WriteEndElement()
}
$writer.WriteEndElement()
$writer.WriteWhitespace([Environment]::NewLine)
$writer.WriteEndElement()
}
$shadowCopies = New-Object System.Collections.Hashtable
$win32ShadowCopy = [WmiClass]"root\cimv2:Win32_ShadowCopy"
$linkFolder = $shadowCopyLinkPrefix + [System.Diagnostics.Process]::GetCurrentProcess().Id.ToString() + "\"
function GetShadow([string]$source) {
$drive = $source.Substring(0, 3).ToLower()
$linkRoot = $drive + $linkFolder
$resultPath = $linkRoot + $source.Substring(3)
if(-not $shadowCopies.ContainsKey($drive)) {
Write-Host "Создание теневой копии тома $drive"
$result = $win32ShadowCopy.Create($drive, "ClientAccessible")
if($result.ReturnValue -ne 0) {
$description = switch ($result.ReturnValue) {
1 {"Access denied."}
2 {"Invalid argument."}
3 {"Specified volume not found."}
4 {"Specified volume not supported."}
5 {"Unsupported shadow copy context."}
6 {"Insufficient storage."}
7 {"Volume is in use."}
8 {"Maximum number of shadow copies reached."}
9 {"Another shadow copy operation is already in progress."}
10 {"Shadow copy provider vetoed the operation."}
11 {"Shadow copy provider not registered."}
12 {"Shadow copy provider failure."}
default {"Unknown error."}
}
CriticalError 13 "Не удалось создать теневую копию тома $drive. $description"
}
$shadowId = $result.ShadowID
$shadowCopy = Get-WmiObject -query "SELECT * FROM Win32_ShadowCopy WHERE ID='$shadowId'"
$shadowCopyDevice = $shadowCopy.DeviceObject
if(-not $shadowCopyDevice.EndsWith("\")) {
$shadowCopyDevice += "\"
}
$temp = cmd /c mklink /d $linkRoot $shadowCopyDevice
if(-not (Test-Path "$resultPath")) {
CriticalError 14 "Не удалось подключить диск с теневой копией $shadowCopyDevice"
}
Write-Host "Теневая копия подключена как $linkRoot"
$shadowCopies.Add($drive, $shadowCopy)
}
return $resultPath
}
$xcopyKeys = "/y /c"
$is2008r2 = [Environment]::OSVersion.Version -ge (New-Object Version 6, 1)
if($is2008r2) {
$xcopyKeys += " /j"
}
function CallXcopy([string]$source, [string]$destination) {
Invoke-Expression "xcopy `"$source`" `"$destination`" $xcopyKeys"
}
function ShadowCopy([string]$source, [string]$destination) {
$sourceShadow = GetShadow $source
CallXcopy $sourceShadow $destination
}
if(Test-Path "$backupFolder") {
Remove-Item $backupFolder -Recurse -Force
}
$temp = New-Item $backupFolder -ItemType Directory
function CreateSubFolder([string]$root, [string]$subfolder) {
$path = [System.IO.Path]::Combine($root, $subfolder)
$temp = New-Item $path -ItemType Directory
return $path
}
$vmBackupFolder = CreateSubFolder $backupFolder "Virtual Machines"
$snapshotsBackupFolder = CreateSubFolder $backupFolder "Snapshots"
$vhdsBackupFolder = CreateSubFolder $backupFolder "Virtual Hard Disks"
Write-Host ""
Write-Host "Начало экспорта настроек"
$writerSettings = New-Object System.Xml.XmlWriterSettings
$writerSettings.Encoding = [System.Text.Encoding]::Unicode
$writerSettings.Indent = $false
foreach($settings in $currentAndSnapshots) {
Write-Host ""
$current = $settings.SettingType -eq 3
$settingsId = $settings.InstanceID.Substring(10)
if($current) {
$sourceFolder = $vmRoot
$expFolder = $vmBackupFolder
Write-Host "Экспорт активных настроек"
}
else {
$sourceFolder = $snapshotRoot
$expFolder = $snapshotsBackupFolder
Write-Host "Экспорт снимка $settingsId"
}
$writer = [System.Xml.XmlWriter]::Create([System.IO.Path]::Combine($expFolder, $settingsId + ".exp"), $writerSettings)
$writer.WriteStartDocument($true)
$writer.WriteWhitespace([Environment]::NewLine)
$writer.WriteStartElement("DECLARATIONS")
$writer.WriteWhitespace([Environment]::NewLine)
$writer.WriteWhitespace(" ")
$writer.WriteStartElement("DECLGROUP")
$stateSourcePath = [System.IO.Path]::Combine($sourceFolder, $settingsId)
$binSourcePath = [System.IO.Path]::Combine($stateSourcePath, "$settingsId.bin")
$vsvSourcePath = [System.IO.Path]::Combine($stateSourcePath, "$settingsId.vsv")
$hasState = (Test-Path "$binSourcePath") -and (Test-Path "$vsvSourcePath")
if($current) {
$hasState = $hasState -and $vmSaved
if($is2008r2) {
$exportSettingsValues = @{ `
Caption = "Virtual System Export Setting Data"; `
CopySnapshotConfiguration = 0; `
CopyVmRuntimeInformation = $true; `
CopyVmStorage = $true; `
CreateVmExportSubdirectory = $true; `
Description = "Microsoft Virtual System Export Setting Data"; `
ElementName = "Microsoft Virtual System Export Setting Data"; `
InstanceID = "Microsoft:$vmId"; `
SnapshotVirtualSystem = $null
}
$exportSettings = Set-WmiInstance Msvm_VirtualSystemExportSettingData -Arguments $exportSettingsValues -Namespace "root\virtualization"
WriteObject $writer $exportSettings @{ CreateVmExportSubdirectory = $true }
}
WriteObject $writer $globalSettings @{ ExternalDataRoot = ""; SnapshotDataRoot = "" }
}
WriteObject $writer $settings
$vmResources = Get-WmiObject -Namespace "root\virtualization" -Query "ASSOCIATORS OF {$settings} WHERE AssocClass=Msvm_VirtualSystemSettingDataComponent"
foreach($resource in $vmResources) {
if(($resource.ResourceType -eq 22) -and ($resource.ResourceSubType -eq "Microsoft Synthetic Disk Drive")) {
$attachedVhd = $vhdsByDriveIds[$resource.InstanceID].ToLower()
if([Array]::IndexOf($allVhdsToExport, $attachedVhd) -eq -1) {
continue
}
}
$isVhd = ($resource.ResourceType -eq 21) -and ($resource.ResourceSubType -eq "Microsoft Virtual Hard Disk") -and $resource.Connection
if($isVhd) {
$vhd = $resource.Connection[0].ToLower()
if([Array]::IndexOf($allVhdsToExport, $vhd) -eq -1) {
continue
}
}
WriteObject $writer $resource
if($isVhd -and $is2008r2) {
while($parentsByVhds[$vhd]) {
$info = $infosByVhds[$vhd]
$writer.WriteWhitespace([Environment]::NewLine)
$writer.WriteWhitespace(" ")
$writer.WriteStartElement("VALUE.OBJECT")
$writer.WriteRaw($info)
$writer.WriteWhitespace([Environment]::NewLine)
$writer.WriteEndElement()
$vhd = $parentsByVhds[$vhd].ToLower()
}
}
if(($resource.ResourceType -eq 10) -and ($resource.Connection)) {
$switchPort = $null
$switchPort = [WMI]$resource.Connection[0]
if($switchPort) {
$switchPortName = $switchPort.Name
$switchName = $switchPort.SystemName
$switch = Get-WmiObject -Namespace "root\virtualization" -Query "SELECT * FROM Msvm_VirtualSwitch WHERE Name='$switchName'"
$vlanEndpointSetting = Get-WmiObject -Namespace "root\virtualization" -Query "SELECT * FROM Msvm_VLANEndpointSettingData WHERE ElementName='$switchPortName'"
if($switch -and $vlanEndpointSetting) {
WriteObject $writer $switchPort
WriteObject $writer $switch
WriteObject $writer $vlanEndpointSetting
}
}
}
}
$writer.WriteWhitespace([Environment]::NewLine)
$writer.WriteWhitespace(" ")
$writer.WriteEndElement()
$writer.WriteWhitespace([Environment]::NewLine)
$writer.WriteEndElement()
$writer.WriteEndDocument()
$writer.Close()
if($hasState -or (-not $is2008r2)) {
$stateExpPath = CreateSubFolder $expFolder $settingsId
if($hasState) {
Write-Host "Экспорт сохранённого состояния"
ShadowCopy $stateSourcePath $stateExpPath
}
}
}
Write-Host ""
Write-Host "Конец экспорта настроек"
Write-Host ""
Write-Host "Начало копирования VHD"
foreach($vhd in $rootVhdsToExport) {
$vhdExported = [System.IO.Path]::Combine($vhdsBackupFolder, [System.IO.Path]::GetFileName($vhd))
$fileStub = New-Item $vhdExported -ItemType File
ShadowCopy $vhd $vhdExported
}
$avhdsBackupFolder = $vhdsBackupFolder
if(-not $is2008r2){
$avhdsBackupFolder = CreateSubFolder $snapshotsBackupFolder $vmId
}
foreach($vhd in $diffVhdsToExport) {
$vhdExported = [System.IO.Path]::Combine($avhdsBackupFolder, [System.IO.Path]::GetFileName($vhd))
$fileStub = New-Item $vhdExported -ItemType File
ShadowCopy $vhd $vhdExported
}
Write-Host ""
Write-Host "Конец копирования VHD"
Write-Host ""
Write-Host "Удаление теневых копий"
foreach($drive in $shadowCopies.Keys) {
$linkRoot = $drive + $linkFolder
$temp = cmd /c rmdir $linkRoot
$shadowCopyId = $shadowCopies[$drive].ID
$temp = vssadmin delete shadows /shadow=$shadowCopyId /Quiet
$shadowCopy = Get-WmiObject -query "SELECT * FROM Win32_ShadowCopy WHERE ID='$shadowCopyId'"
if($shadowCopy) {
ShowWarning "Не удалось удалить теневую копию $shadowCopy."
$warningCode = 2
}
}
Write-Host ""
Write-Host "Запись config.xml"
$writerSettings.Indent = $true
$writer = [System.Xml.XmlWriter]::Create([System.IO.Path]::Combine($backupFolder, "config.xml"), $writerSettings)
function WriteConfigValue([string]$name, [string]$type, [string]$value = $null) {
$writer.WriteStartElement($name)
$writer.WriteAttributeString("type", $type)
$writer.WriteString($value)
$writer.WriteEndElement()
}
$writer.WriteStartElement("configuration")
WriteConfigValue "SnapShotDataRoot" "string"
WriteConfigValue "ExternalDataRoot" "string"
foreach($root in $rootVhdsToExport) {
$writer.WriteStartElement("vhd")
WriteConfigValue "source" "string" $root
WriteConfigValue "target" "string"
$writer.WriteEndElement()
}
WriteConfigValue "VmStateCopied" "bool" "true"
$writer.WriteEndElement()
$writer.WriteEndDocument()
$writer.Close()
Write-Host ""
Write-Host "Экспорт завершён"
exit $warningCode
Комментариев нет:
Отправить комментарий