пятница, 17 августа 2012 г.

Бэкап ВМ в Hyper-V

Встал острый вопрос в необходимости бэкапирования виртуальных машин в 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-файла.

Если имена или пути содержат пробелы или запятые, заключайте имена в кавычки.

Примеры:
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


Комментариев нет:

Отправить комментарий