通过VMware vCenter利用脚本备份VM

This artical will be published in English as well: http://www.cnblogs.com/LarryAtCNBlog/p/4613320.html

如果你的环境里刚好有VMware vCenter,里面加了一堆ESX(i)服务器的话,那以下的脚本是有帮助的。

vCenter本身是有计划备份的功能的,但是比较遗憾的是功能并不能满足我使用的需求,我也更偏向于自己做脚本控制。

以下是tree的一个sample输出,脚本只有3个,都以ps1结尾。

│  Backup-VM.ps1
│  Starter.ps1
│
└─vCenter01
        _Configuration.ps1

下面是脚本的作用描述,

Starter.ps1 - 由task scheduler调用,在其所属的目录搜索所有_Configuration.ps1文件,然后异步调用Backup-VM.ps1,将_Configuration.ps1的目录传入
Backup-VM.ps1 - 接收唯一参数-vCenterFolder,指向_Configuration.ps1所在的文件夹
_Configuration.ps1 - 配置文件,可以有多个,依据其配置文件不同指向不同的vCenter server,备份不同的VM,多个vCenter的job建多个_Configuration.ps1即可

下面是脚本内容及解释

Starter.ps1

# 进入script所在的文件夹
Set-Location (Get-Item $MyInvocation.MyCommand.Definition).Directory

# 搜索目录下所有的_Configuration.ps1文件
Get-ChildItem -Filter ‘_Configuration.ps1‘ -Recurse | %{
    # 调用Backup-VM.ps1,将_Configuration.ps1的目录传入
    Start-Process -FilePath ‘powershell.exe‘ -ArgumentList @(‘-File‘, ‘Backup-VM.ps1‘, ‘-vCenterFolder‘, "`"$($_.DirectoryName)`"")
}

_Configuration.ps1

# 该_Configuration.ps1是否启用,置成$false脚本则不会执行
$Enable = $true

# 下面只是一个VM配备格式的样本
# VM表示该VM在vCenter里的名字
# Host表示要把该VM往哪个ESX(i)上备份
# Datastore表示目标ESX(i)上的storage名称
<#
Sample:
$Entries = @(
@{VM = ‘VMName01‘; Host = ‘TargetESXiServerName02‘; Datastore = ‘TargetESXiServerName02:storage1‘; Reserve = 1;},
@{VM = ‘VMName02‘; Host = ‘TargetESXiServerName01‘; Datastore = ‘TargetESXiServerName01:storage2‘; Reserve = 1;}
)
#>

# 实际数据填在下面,sample如上
$Entries = @(

)

# vCenter服务器名或ip地址
$vCenter = ‘vCenter01‘

# 邮件设置部分,只有当出现需要关注的错误时,邮件部分才会被触发,正常备份完成不会触发
$From =  "$($env:COMPUTERNAME)@test.com"
$To = "[email protected]"
$Subject = "VMs backup completed with errors - $vCenter"
$SmtpServer = ‘mailgateway‘

Backup-VM.ps1 - 自动判断VM的网络,如果使用vSphere Distributed Switch,会切换成API备份,如果是普通的vSphere Standard Switch网络,用New-VM命令即可完成备份。(使用VDS后,New-VM命令会失败,除非目标ESXi服务器上的网络配置和当前ESXi包括同样的VDS)

PARAM(
    [parameter(Mandatory=$true)]
    [string]$vCenterFolder
)

# 进入_Configuration.ps1所在的目录
Set-Location -Path $vCenterFolder

$Date = Get-Date
$strDate = $Date.ToString("yyyy-MM-dd")
$strLogFile = "${strDate}.log"

# 载入_Configuration.ps1里的variable
. ‘.\_Configuration.ps1‘

# 定义一个写日志的函数
function Add-Log
{
    PARAM(
        [String]$Path,
        [String]$Value,
        [String]$Type = ‘Info‘
    )
    $Type = $Type.ToUpper()
    $Date = Get-Date
    Write-Host "$($Date.ToString(‘[HH:mm:ss] ‘))[$Type] $Value" -ForegroundColor $(
        switch($Type)
        {
            ‘WARNING‘ {‘Yellow‘}
            ‘Error‘ {‘Red‘}
            default {‘White‘}
        }
    )
    if($Path){
        Add-Content -LiteralPath $Path -Value "$($Date.ToString(‘[HH:mm:ss] ‘))[$Type] $Value" -ErrorAction:SilentlyContinue
    }
}

Add-Log -Path $strLogFile -Value ‘New backup started‘
# 判断该_Configuration.ps1是否配置为启用
if(!$Enable)
{
    Add-Log -Path $strLogFile -Value ‘Repository disabled‘
    exit
}

# $vCenter是必要信息,没有它脚本没有办法执行
if(!$vCenter)
{
    Add-Log -Path $strLogFile -Value ‘vCenter variable is null, can not continue‘ -Type Error
    $Alert = $true
}
else
{
    Add-Log -Path $strLogFile -Value "vCenter: [$vCenter]"
}

# 找必要的snapin,早期版本的PowerCli没有VDS的操作snapin,显示一些提示以确保运行环境正常
if(!(Get-PSSnapin -Name ‘*VMware.VimAutomation.Vds*‘ -Registered -ErrorAction:SilentlyContinue))
{
    Add-Log -Path $strLogFile -Value ‘This script is built from [VMware vSphere PowerCLI 5.5], suggest to run on the version‘ -Type Error
    Add-Log -Path $strLogFile -Value ‘PSSnapin [VMware.VimAutomation.Vds] is not found, which could cause backup failure‘ -Type Error
    Add-Log -Path $strLogFile -Value ‘Installer path: [\\Server\VMware\vSphere\VMware-PowerCLI-5.5.0-1295336.exe]‘ -Type Info
    exit
}

# 载入snapin
if(!(Get-PSSnapin ‘*vmware*‘ -ErrorAction:SilentlyContinue))
{
    Add-PSSnapin *vmware*
    if(!$?)
    {
        Add-Log -Path $strLogFile -Value ‘Failed to add vmware pssnapin‘ -Type Error
        Add-Log -Path $strLogFile -Value $Error[0] -Type Error
        exit
    }
}

# 连接到vCenter,假如出现错误,把$Alert置成$true为最后发出告警邮件
Connect-VIServer -Server $vCenter -Force
if(!$?)
{
    Add-Log -Path $strLogFile -Value ‘Failed to connect to vCenter, cause:‘ -Type Error
    Add-Log -Path $strLogFile -Value $Error[0] -Type Error
    $Alert = $true
}

$Tasks = @()
# 对每一项配置的VM备份做循环
foreach($e in $Entries)
{
    Add-Log -Path $strLogFile -Value "Start doing backup for: [$($e.VM)]"
    # 添加新的VM名称为旧名+_ScriptBackup_+当时的日期
    $VMNew = "$($e.VM)_ScriptBackup_$strDate"
    $e.NewVM = $VMNew
    $VM = $null
    $VM = @(Get-VM -Name $e.VM -ErrorAction:SilentlyContinue)
    if(!$VM)
    {
        Add-Log -Path $strLogFile -Value ‘Capture none VM, does the VM exists?‘ -Type Warning
        continue
    }
    if($VM.Count -ge 2)
    {
        Add-Log -Path $strLogFile -Value "Capture [$($VM.Count)] VM, duplicated VMs?: [$(($VM | %{$_.Id}) -join ‘], [‘)]" -Type Warning
        continue
    }
    $VM = $VM[0]

    # 执行到此处说明VM在vCenter里存在并唯一,脚本没有抓到多个VM
    $VMHost = $null
    $VMHost = @(Get-VMHost -Name $e.Host -ErrorAction:SilentlyContinue)
    if(!$VMHost)
    {
        Add-Log -Path $strLogFile -Value "Capture none VMHost, does the VMHost exists?: [$($e.Host)]" -Type Warning
        continue
    }
    if($VMHost.Count -ge 2)
    {
        Add-Log -Path $strLogFile -Value "Capture [$($VMHost.Count)] VMHost, duplicated VMHosts?: [$(($VMHost | %{$_.Id}) -join ‘], [‘)]" -Type Warning
        continue
    }
    $VMHost = $VMHost[0]

    # 执行到此处说明VMHost在vCenter里存在并唯一,脚本没有抓到多个VMHost
    $Datastore = $null
    $Datastore = @($VMHost | Get-Datastore -Name $e.Datastore -ErrorAction:SilentlyContinue)
    if(!$Datastore)
    {
        Add-Log -Path $strLogFile -Value "Capture none Datastore, does the Datastore exists on VMHost?: [$($e.Datastore)]" -Type Warning
        continue
    }
    if($Datastore.Count -ge 2)
    {
        Add-Log -Path $strLogFile -Value "Capture [$($Datastore.Count)] Datastore, duplicated Datastores?: [$(($Datastore | %{$_.Id}) -join ‘], [‘)]" -Type Warning
        continue
    }
    $Datastore = $Datastore[0]

    # 执行到此处说明Datastore在vCenter里存在并唯一,脚本没有抓到多个Datastore
    Add-Log -Path $strLogFile -Value "INFO[OLDName][NewName][Host][Datastore]: [$($VM.Name)][$VMNew][$($VMHost.Name)][$($Datastore.Name)]"

    # 判断VM是否使用了VDS
    $VDS = $null
    $VDS = $VM | Get-VDSwitch -ErrorAction:SilentlyContinue
    if(!$VDS)
    {
        # 如果没有使用VDS,则可以直接用New-VM命令备份
        Add-Log -Path $strLogFile -Value ‘VDSwitch not found on the VM, use commandlet [New-VM] to clone‘
        $Task = $null
        $Task = New-VM -Name $VMNew -VM $VM -VMHost $VMHost -Datastore $Datastore -RunAsync -ErrorAction:SilentlyContinue
        if(!$?)
        {
            Add-Log -Path $strLogFile -Value ‘New-VM failed, cause:‘ -Type Warning
            Add-Log -Path $strLogFile -Value $Error[0] -Type Warning
            continue
        }
        Add-Log -Path $strLogFile -Value "Task launched: [$($Task.Id)]"
        $Tasks += $Task.Id
    }
    else
    {
        # 如果使用VDS,则需要使用API自行备份
        Add-Log -Path $strLogFile -Value ‘VDSwitch found on the VM, need to use 2nd way to clone VM‘
        Add-Log -Path $strLogFile -Value "VDS [Name][KEY]: [$(($VDS | %{$_.Name}) -join ‘;‘)][$(($VDS | %{$_.Key}) -join ‘;‘)]"
        $TargetVDS = $null
        $TargetVDS = @($VMHost | Get-VDSwitch -ErrorAction:SilentlyContinue)
        # 在目标VMHost上搜索VDS,如果目标VMHost上没有VDS,clone没有办法继续
        if(!$TargetVDS)
        {
            Add-Log -Path $strLogFile -Value ‘Target VMHost server has no VDSwitch, VM which uses a VDS is unable to clone to the VMHost‘ -Type Warning
            continue
        }
        $TargetVDS = $TargetVDS[-1]
        # 在目标VMHost上搜索到了VDS,抓取出port数量最多的一个
        $TargetVDSGroup = @($TargetVDS | Get-VDPortgroup | Sort-Object NumPorts)[-1]
        Add-Log -Path $strLogFile -Value "Target VDS randomly picked [Name][Key]: [$($TargetVDS.Name)][$($TargetVDS.Key)]"

        # API必要参数,VMHost默认的Pool即可
        $Pool = $null
        $Pool = @($VMHost | Get-ResourcePool)[0]
        if(!$Pool)
        {
            Add-Log -Path $strLogFile -Value ‘No resource pool found from VMHost, please use Get-ResourcePool to find resource pool on the VMhost‘ -Type Warning
            continue
        }

        # 抓取VM上的网卡对象,并对其修改,修改成为目标VMHost上的VDS
        $VMNic = $null
        $VMNic = $VM.ExtensionData.Config.Hardware.Device | ?{$_.DeviceInfo.Label -imatch ‘Network adapter‘}
        if($VMNic.Count -ge 2)
        {
            Add-Log -Path $strLogFile -Value ‘The VM has more than 2 network adapters, not supported‘ -Type Warning
            continue
        }
        $VMNicBacking = $VMNic.Backing
        $VMNicBacking.Port.SwitchUuid = $TargetVDS.Key
        $VMNicBacking.Port.PortgroupKey = $TargetVDSGroup.Key
        $VMNicBacking.Port.PortKey = ‘‘
        $VMNicBacking.Port.ConnectionCookie = ‘‘

        # API必要参数
        $spec = New-Object VMware.Vim.VirtualMachineCloneSpec
        $spec.Config = New-Object VMware.Vim.VirtualMachineConfigSpec
        $nicDev = New-Object VMware.Vim.VirtualDeviceConfigSpec
        $nicDev.Operation = ‘edit‘
        $nicDev.Device = $VMNic
        $nicDev.Device.Backing = $VMNicBacking
        $spec.Config.DeviceChange = $nicDev
        $spec.Config.DeviceChange[0].Device.Backing.Port.PortKey = ‘‘
        $spec.Location = New-Object VMware.Vim.VirtualMachineRelocateSpec
        $spec.Location.Host = $VMHost.ExtensionData.MoRef
        $spec.Location.Datastore = $Datastore.ExtensionData.MoRef
        $spec.Location.Pool = $Pool.ExtensionData.MoRef
        $spec.PowerOn = $false
        $spec.Template = $false

        Add-Log -Path $strLogFile -Value ‘Trying to get datacenter object, this could potentially cause a dead loop!‘
        # 为了得到下面的API必要参数$Folder,需要先得到VMHost所在的Datacenter
        $Datacenter = $VMHost.Parent
        while($Datacenter.Id -notmatch ‘Datacenter‘)
        {
            $Datacenter = $Datacenter.Parent
        }
        # API必要参数
        $Folder = $Datacenter | Get-Folder -Name ‘Discovered virtual machine‘
        Add-Log -Path $strLogFile -Value ‘Did not fail into a dead loop!‘

        # 调用API触发VM clone task
        $Task = $null
        $Task = $VM.ExtensionData.CloneVM_Task($Folder.ExtensionData.MoRef, $VMNew, $spec)
        if(!$?)
        {
            Add-Log -Path $strLogFile -Value ‘CloneVM_Task failed, cause:‘ -Type Warning
            Add-Log -Path $strLogFile -Value $Error[0] -Type Warning
            continue
        }
        Add-Log -Path $strLogFile -Value "Task launched: [$($Task.Type)-$($Task.Value)]"
        $Tasks += "$($Task.Type)-$($Task.Value)"
    }
}

$Tasks = @($Tasks | ?{$_})
Add-Log -Path $strLogFile -Value "Waiting for tasks to complete, count: [$($Tasks.Count)]"
while($Tasks)
{
    # 由于clone是异步进行,脚本每5分钟进行一次跟踪,由于vCenter默认完成的Task会在15分钟后清除
    # 因此下面的sleep时间最好不要超过15分钟,否则有可能抓不到Task的成功失败的信息
    # 调试的时候可以改成10秒一检测
    Start-Sleep -Seconds 300
    $Tasks = Get-Task -Id $Tasks -ErrorAction:SilentlyContinue
    if(!$?)
    {
        Add-Log -Path $strLogFile -Value ‘Failed to refresh Task states, cause:‘ -Type Warning
        Add-Log -Path $strLogFile -Value $Error[0] -Type Warning
    }
    $Tasks = @(
        $Tasks | %{
            if($_.State -ne ‘Running‘)
            {
                Add-Log -Path $strLogFile -Value "Task completed [ID][State]: [$($_.Id)][$($_.State)]"
                if($_.State -eq ‘Error‘)
                {
                    Add-Log -Path $strLogFile -Value $_.ExtensionData.Info.Error.LocalizedMessage -Type Warning
                }
            }
            else
            {
                Add-Log -Path $strLogFile -Value "Task running [ID][% Complete]: [$($_.Id)][$($_.PercentComplete)%]"
                $_.Id
            }
        }
    )
}

# 所有Task都完成后,脚本会开始校验VM是否成功备份
Add-Log -Path $strLogFile -Value ‘Verification start‘
foreach($e in $Entries)
{
    if((Get-VM -Name $e.NewVM -ErrorAction:SilentlyContinue) -and $?)
    {
        # 备份在vCenter里找到,说明备份成功
        Add-Log -Path $strLogFile -Value "[VM][NewVM]: [$($e.VM)][$($e.NewVM)] -- New VM found in vCenter"
        if($e.Reserve)
        {
            # 如果设置了Reserve的值,脚本会根据该值找到当前vCenter里所有的备份,然后删掉多余的备份VM
            $VMBackups = $null
            $VMBackupsRemoval = $null
            $VMBackups = Get-VM -Name "$($e.VM)_ScriptBackup_*" | ?{$_.Name -imatch ‘_ScriptBackup_\d{4}-\d{2}-\d{2}$‘} -ErrorAction:SilentlyContinue
            $VMBackups = @($VMBackups | Sort-Object ‘Name‘)
            Add-Log -Path $strLogFile -Value "Old VM backups captured: [$($VMBackups.Count)][$(($VMBackups | %{$_.Name}) -join ‘;‘)]"
            $i = $VMBackups.Count - $e.Reserve
            if($i -le 0)
            {
                $i = 0
                Add-Log -Path $strLogFile -Value ‘No old backups available to be removeds‘
            }
            if($i -gt 0)
            {
                Add-Log -Path $strLogFile -Value "VM old backups can be removed count: [$i]"
                $VMBackupsRemoval = $VMBackups[0..(--$i)]
                Remove-VM -VM $VMBackupsRemoval -DeletePermanently -Confirm:$false
            }
        }
    }
    else
    {
        # 备份没有在vCenter里找到,说明备份失败
        Add-Log -Path $strLogFile -Value "[VM][NewVM]: [$($e.VM)][$($e.NewVM)] -- New VM not found in vCenter, backup failure?" -Type Warning
        $Alert = $true
    }
}

Add-Log -Path $strLogFile -Value ‘All done!‘

# 脚本中出现了需要人工检查的错误,则会发出邮件
if($Alert)
{
    Send-MailMessage -To $To -From $From -SmtpServer $SmtpServer -Subject $Subject -Attachments $strLogFile
    if(!$?)
    {
        Add-Log -Path $strLogFile -Value ‘Failed to send email, cause:‘ -Type Warning
        Add-Log -Path $strLogFile -Value $Error[0] -Type Warning
    }
}

API的参考如下,

https://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/

当然,许久之前也参考过部分VMware社区的文章~

时间: 2024-10-15 08:24:15

通过VMware vCenter利用脚本备份VM的相关文章

VMware Esxi 5.1备份和恢复VM

VMware Esxi 5.1备份和恢复VM的方式很多,下面介绍2种方式: 一.使用vSphere Client对VM进行直接备份和恢复: 备份:1.打开侧边栏物理资源池,选中物理机,然后选择右边标题栏上面的"配置"--"存储器"--"datastore1"--右键--"浏览数据存储"--选中VM所在的文件夹--把数据存储文件下载到本地计算机. 恢复:2.打开侧边栏物理资源池,选中物理机,然后选择右边标题栏上面的"配

VMware虚拟化三种备份和恢复方法的对比分析

服务器虚拟化,尤其是VMware形式的服务器虚拟化使IT人员获益良多,这么说一点也不为过.据我们所见,服务器虚拟化能解决服务器扩张.资源消耗.服务器扩张.能源消耗.高可用性等相关问题.服务器虚拟化也使我们有更多的时间解决其它的迫切问题,如企业资源预案升级.存储项目再三迁移或者为什么我的青蛙总不回家的问题.尽管VMware提供封装技术和抽象技术,使我们受益匪浅,但数据保护领域发生的基本变革也带来了各项挑战.即使出现了VMware虚拟化,备份人员依然是牢骚最多的IT人员.最大的挑战在于保证数据的一致

【VMware虚拟化解决方案】备份VMWare ESXi虚拟机

备份VMWare ESXi虚拟机 VMware Data Recovery(简称VDR)介绍: VMware DataRecovery是vSphere新提供的数据备份功能,是一种基于磁盘的数据备份方式,不支持以磁带为目标的备份.VDR由vc插件.运行在ESX主机上的虚拟机以及备份存储这三个部件组成.通过在vc上的插件以向导的方式进行配置和调度备份任务. VMwareData Recovery 可创建虚拟机备份,同时不会中断虚拟机的使用或其提供的数据和服务.Data Recovery会管理现有备份

Vmware Vcenter Converter 详细安装配置及功能介绍

Vmware vCenter Converter Standalone是一种用于将虚拟机和物理机转换为VMware虚拟机的可扩展解决方案(vmware vsphere converter可以自动化和简化物理机到虚拟机及虚拟机格式之间的转化过程,就是一个P2V.v2p的迁移克隆过程).它支持将windows和linux操作系统用作源,可以执行若干转换任务:总结为:vmware vcenter converter可以将正在运行的远程物理机和虚拟机作为虚拟机导入到vCenter Server管理的独立

烂泥:vcenter通过模板部署vm

前一篇文章我们介绍了有关vcenter5.5的安装与配置,这篇文章我们再来介绍下,如何通过vcenter的vm模板来部署虚拟机以及在部署过程遇到问题的解决方法. 一.生成VM模板 要生成vm模板,我们首先要安装好一台虚拟机,在此我安装好了一台centos6.6的机器.如下: 要把一台vm做成模板,需要在vm关机状态下进行操作.如下: 选择虚拟机-"模板"-"转换成模板". 通过以上两张图的对比,我们可以发现vm在做成vm模板前后的图标是不一样的.这样我们的vm模板就

VMware vCenter Converter系统要求

VMware vCenter Converter是windows程序,可以安装在任何服务器操作系统或者桌面级操作系统.VMware vCenter Converter包含多个组件,每个组件对系统的要求都不同. VMware vCenter Converter安装系统要求 VMware vCenter Converter Standalone组件只能安装在Windows操作系统上.Converter Standalone支持Windows和Linux操作系统用作源,用于已打开电源计算机的转换和虚拟

Vmware vCenter Server的学习见解

Vmware vCenterServer5.1及EXSI功能简介 Vmware vCenter Server介绍说明: 利用 VMware vCenterServer,您可以集中管理多个 VMware  ESXi 主机及其虚拟机.安装.配置和管理 vCenter Server 不当可能会导致管理效率降低,或者致使 VMware ESXi 主机和虚拟机停机. 一.Vmware vCenter Server的体系结构 Vmware vcenter的管理平台: vCenter Server 是充当 E

Vmware vCenter 配置标准虚拟机交换机

VMwarevSphere Client配置标准虚拟交换机 1.实验资源需求 Vcenter server正常启动,服务正常 Esxi正常启动 为Esxi增加一块网卡 Vmwaresphere client 正常连接VC 网络互通 2.实验目标 1.   查看当前标准虚拟交换机的配置 2.   创建一个标准虚拟交换机和一个虚拟机端口组 3.   将您的虚拟机连接到虚拟交换机端口组 3.实验配置步骤 查看当前标准虚拟交换机的配置 在此任务中,通过 VMware vSphere Client 查看当

公司虚拟化平台VMware vCenter Server无法连接故障排查解决

1.环境: 系统:Windows 2008R2 vCenter版本:VMware vCenter Server 5.1.799731 数据库:Oracle 11.2.0 2.故障描述: 用VMware vSphere Client客户端无法登录,提示"出现未知连接错误.(由于连接故障,请求失败.无法连接到远程服务器)" 排错过程: 1.mstsc远程桌面到服务器. 打开服务器管理器--服务,找到vCenter的服务VMware VirtualCenter Server,发现服务没有启动