In my last post I have introduced vCloud Availability – a Disaster Recovery extension for vCloud Director and also have shown how service providers can monitor the key components of the solution.
Today I will show how tenants can orchestrate failover of their VMs to the cloud with PowerCLI without the need to have access to their on-prem vCenter Server which in case of disaster might be down.
vCloud Availability is extending vCloud APIs with new API calls that can be used to gather information about ongoing replications and provide fail over, test failover and cleanup test failover actions.
I have created the following PowerCLI cmdlets that demonstrate usage of the new APIs.
Get-VRReplication
The function returns one (based on its name) or all tenant configured replications, displays their status and also vCloud vApp identifier which is important for additional orchestration as I will show later below.
FailOver-VRReplication
This function needs an Replication object (which can be obtain with the previous command) as an input and performs real replication to the cloud. Additional parameters specifie if the recovered VM should be powered on or if the task should run asynchronously.
TestFailover-VRReplication
This is identical function to the previous one, however it performs only Test Failover to the cloud. This means replication still goes on and the recovered VM is connected to a test network in the cloud instead of the production one. Input parameters are identical.
TestCleanup-VRRreplication
This command cleans up replication that is in Test Recovery State.
Orchestration
As I hinted above, these new cmdlets can be combined with existing vCloud PowerCLI cmdlets to orchestrate complex workflows that require changes on the recovered VMs. As an example I am showing simple script that recovers one VM and changes its IP address. The code is heavily commented to explain each step.
#Replication name matches the protected VM name $Name = "VM1" #New IP addresses(s) - can be array of multiple entries, if VM has multiple NICs [array] $IPAddress = '192.168.1.150' #First we need to get the replication object $Replication = Get-VRReplication $Name #Now we can perform the failover however we will not power-on the VM yet. FailOver-VRReplication -Replication $Replication -PowerOn $false #Now we need to find the recovered VM within the vApp. There is always 1:1 relationship between vApp and recovery VM. $VM = Get-CIVApp -Id $Replication.VappId | Get-CIVM #We will go through all VM NICs and change their IP allocation mode from DHCP to manual and set new IP address $i = 0 foreach ($NetworkAdapter in $VM|Get-CINetworkAdapter) { Set-CINetworkAdapter $NetworkAdapter -IPAddressAllocationMode Manual -IPAddress $IPAddress[$i] $i++ } #Guest customization must be enabled so new IP addresses are assigned within the Guest OS upon first boot $GuestCustomization = $VM.ExtensionData.Section | Where {$_.GetType() -like "*GuestCustomizationSection"} $GuestCustomization.Enabled = $True $Result = $GuestCustomization.UpdateServerData() #We can finaly start the VM Start-CIVM $VM -RunAsync:$true
And here are the PowerShell functions:
Function Get-VRReplication { <# .SYNOPSIS Collects specific or all replications in particular organization .DESCRIPTION Collects specific or all replications in particular organization, their compliance status and other information .EXAMPLE PS C:\> Connect-CIServer -Org ACME PS C:\> Get-VRReplication PS C:\> Get-VRReplication VM1 .EXAMPLE PS C:\> Get-VRReplication -Org ACME -Name VM1 .NOTES Author: Tomas Fojta #> [CmdletBinding()] param( [Parameter(Mandatory=$false,Position=1)] [String]$Name, [Parameter(Mandatory=$false,Position=2)] [String]$Org ) if (-not $global:DefaultCIServers) {Connect-CIServer} If ($Org -eq "") {$Org = $global:DefaultCIServers[0].Org} $VRReplications = @() $Uri = $global:DefaultCIServers[0].ServiceUri.AbsoluteUri + ((get-org $org).id).Replace("urn:vcloud:org:","org/") + "/replications" $head = @{"x-vcloud-authorization"=$global:DefaultCIServers[0].SessionSecret} + @{"Accept"="application/*+xml;version=20.0;vr-version=3.0"} $r = Invoke-WebRequest -URI $Uri -Method Get -Headers $head -ErrorAction:Stop [xml]$sxml = $r.Content foreach ($Replication in $sxml.References.Reference) { $n = @{} | select Name,href,Rpo,ReplicationState,CurrentRpoViolation,TestRecoveryState,RecoveryState,VappId,VrServerInfo,ReplicaSpaceRequirements, Instance $n.href = $Replication.href $r = Invoke-WebRequest -URI $Replication.href -Method Get -Headers $head -ErrorAction:Stop [xml]$sxml = $r.Content $n.name = $sxml.ReplicationGroup.name $n.Rpo = $sxml.ReplicationGroup.Rpo $n.ReplicationState = $sxml.ReplicationGroup.ReplicationState $n.CurrentRpoViolation = $sxml.ReplicationGroup.CurrentRpoViolation $n.TestRecoveryState = $sxml.ReplicationGroup.TestRecoveryState $n.RecoveryState = $sxml.ReplicationGroup.RecoveryState $n.VappId = $sxml.ReplicationGroup.PlaceholderVappId $n.VrServerInfo = $sxml.ReplicationGroup.VrServerInfo.Uuid $VRReplications += $n } if ( $name ) { $VRReplications | ? { $_.name -eq $name } } else { $VRReplications} } Function FailOver-VRReplication { <# .SYNOPSIS Fails over replicated VM in the cloud .DESCRIPTION Fails over replicated VM in the cloud. The input must be replication object, power-on (default = true) and RunAsync (default false) booleans .EXAMPLE PS C:\> Connect-CIServer -Org ACME PS C:\> FailOver-VRReplication (Get-VRReplication VM1) .EXAMPLE PS C:\> FailOver-VRReplication -Replication (Get-VRReplication -Org ACME -Name VM1) -PowerOn:$False -RunAsync:$True .NOTES Author: Tomas Fojta #> [CmdletBinding()] param( [Parameter(Mandatory=$true,Position=1)] $Replication, [Parameter(Mandatory=$false,Position=2)] [boolean]$PowerOn=$true, [Parameter(Mandatory=$false,Position=3)] [boolean]$RunAsync=$false ) if (-not $global:DefaultCIServers) {Connect-CIServer} If ($Org -eq "") {$Org = $global:DefaultCIServers[0].Org} $Uri = $Replication.href + "/action/failover" $head = @{"x-vcloud-authorization"=$global:DefaultCIServers[0].SessionSecret} + @{"Accept"="application/*+xml;version=20.0;vr-version=4.0"} + @{"Content-Type"="application/vnd.vmware.hcs.failoverParams+xml"} if ($PowerOn -eq $False) {$body = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <ns2:FailoverParams xmlns="http://www.vmware.com/vcloud/v1.5" xmlns:ns2="http://www.vmware.com/vr/v6.0" xmlns:ns3="http://schemas.dmtf.org/ovf/envelope/1" xmlns:ns4="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" xmlns:ns5="http://schemas.dmtf.org/wbem/wscim/1/common" xmlns:ns6="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:ns7="http://schemas.dmtf.org/ovf/environment/1"> <ns2:PowerOn>false</ns2:PowerOn> </ns2:FailoverParams>'} else {$body = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <ns2:FailoverParams xmlns="http://www.vmware.com/vcloud/v1.5" xmlns:ns2="http://www.vmware.com/vr/v6.0" xmlns:ns3="http://schemas.dmtf.org/ovf/envelope/1" xmlns:ns4="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" xmlns:ns5="http://schemas.dmtf.org/wbem/wscim/1/common" xmlns:ns6="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:ns7="http://schemas.dmtf.org/ovf/environment/1"> <ns2:PowerOn>true</ns2:PowerOn> </ns2:FailoverParams>'} $r = Invoke-WebRequest -URI $Uri -Method Post -Headers $head -Body $Body -ErrorAction:Stop [xml]$sxml = $r.Content $Uri = $sxml.Task.href if ($RunAsync -eq $false) { Do { $r = Invoke-WebRequest -URI $Uri -Method Get -Headers $head -ErrorAction:Stop [xml]$sxml = $r.Content $Progress = $sxml.Task.Progress Write-Progress -Activity FailOver -Status 'Progress->' -PercentComplete $Progress Start-Sleep -s 5 } until ($Progress -eq 100) } } Function TestFailOver-VRReplication { <# .SYNOPSIS Performs test failover of replicated VM in the cloud .DESCRIPTION Performs test failover replicated VM in the cloud. The input must be replication object, power-on (default = true) and RunAsync (default false) booleans .EXAMPLE PS C:\> Connect-CIServer -Org ACME PS C:\> TestFailOver-VRReplication (Get-VRReplication VM1) -PowerOn:$False .EXAMPLE PS C:\> TestFailOver-VRReplication -Replication (Get-VRReplication -Org ACME -Name VM1) -RunAsync:$True .NOTES Author: Tomas Fojta #> [CmdletBinding()] param( [Parameter(Mandatory=$true,Position=1)] $Replication, [Parameter(Mandatory=$false,Position=2)] [boolean]$PowerOn=$true, [Parameter(Mandatory=$false,Position=3)] [boolean]$RunAsync=$false ) if (-not $global:DefaultCIServers) {Connect-CIServer} If ($Org -eq "") {$Org = $global:DefaultCIServers[0].Org} $Uri = $Replication.href + "/action/testFailover" $head = @{"x-vcloud-authorization"=$global:DefaultCIServers[0].SessionSecret} + @{"Accept"="application/*+xml;version=20.0;vr-version=4.0"} + @{"Content-Type"="application/vnd.vmware.hcs.failoverParams+xml"} if ($PowerOn -eq $False) {$body = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <ns2:TestFailoverParams xmlns="http://www.vmware.com/vcloud/v1.5" xmlns:ns2="http://www.vmware.com/vr/v6.0" xmlns:ns3="http://schemas.dmtf.org/ovf/envelope/1" xmlns:ns4="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" xmlns:ns5="http://schemas.dmtf.org/wbem/wscim/1/common" xmlns:ns6="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:ns7="http://schemas.dmtf.org/ovf/environment/1"> <ns2:PowerOn>false</ns2:PowerOn> <ns2:Synchronize>false</ns2:Synchronize> </ns2:TestFailoverParams>'} else {$body = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <ns2:TestFailoverParams xmlns="http://www.vmware.com/vcloud/v1.5" xmlns:ns2="http://www.vmware.com/vr/v6.0" xmlns:ns3="http://schemas.dmtf.org/ovf/envelope/1" xmlns:ns4="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" xmlns:ns5="http://schemas.dmtf.org/wbem/wscim/1/common" xmlns:ns6="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:ns7="http://schemas.dmtf.org/ovf/environment/1"> <ns2:PowerOn>true</ns2:PowerOn> <ns2:Synchronize>false</ns2:Synchronize> </ns2:TestFailoverParams>'} $r = Invoke-WebRequest -URI $Uri -Method Post -Headers $head -Body $Body -ErrorAction:Stop [xml]$sxml = $r.Content $Uri = $sxml.Task.href if ($RunAsync -eq $false) { Do { $r = Invoke-WebRequest -URI $Uri -Method Get -Headers $head -ErrorAction:Stop [xml]$sxml = $r.Content $Progress = $sxml.Task.Progress Write-Progress -Activity 'Test FailOver' -Status 'Progress->' -PercentComplete $Progress Start-Sleep -s 5 } until ($Progress -eq 100) } } Function TestCleanup-VRReplication { <# .SYNOPSIS Performs test replication cleanup in the cloud. .DESCRIPTION Performs test replication cleanup in the cloud. The input must be replication object and optional RunAsync (default false) boolean. .EXAMPLE PS C:\> Connect-CIServer -Org ACME PS C:\> TeastCleanup-VRReplication (Get-VRReplication VM1) .EXAMPLE PS C:\> TestCleanup-VRReplication -Replication (Get-VRReplication -Org ACME -Name VM1) -RunAsync:$True .NOTES Author: Tomas Fojta #> [CmdletBinding()] param( [Parameter(Mandatory=$true,Position=1)] $Replication, [Parameter(Mandatory=$false,Position=2)] [boolean]$RunAsync=$false ) if (-not $global:DefaultCIServers) {Connect-CIServer} If ($Org -eq "") {$Org = $global:DefaultCIServers[0].Org} $Uri = $Replication.href + "/action/testCleanup" $head = @{"x-vcloud-authorization"=$global:DefaultCIServers[0].SessionSecret} + @{"Accept"="application/*+xml;version=20.0;vr-version=4.0"} + @{"Content-Type"="application/vnd.vmware.hcs.failoverParams+xml"} $body = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns2:SyncParams xmlns="http://www.vmware.com/vcloud/v1.5" xmlns:ns2="http://www.vmware.com/vr/v6.0" xmlns:ns3="http://schemas.dmtf.org/ovf/envelope/1" xmlns:ns4="http://schemas.dmtf.org/wbem/wscim/1/cimschema/ 2/CIM_VirtualSystemSettingData" xmlns:ns5="http://schemas.dmtf.org/wbem/wscim/1/common" xmlns:ns6="http://schemas.dmtf.org/wbem/wscim/1/cimschema/ 2/CIM_ResourceAllocationSettingData" xmlns:ns7="http://schemas.dmtf.org/ovf/environment/1"> <ns2:RepeatOngoingOnlineSync>false</ns2:RepeatOngoingOnlineSync> </ns2:SyncParams>' $r = Invoke-WebRequest -URI $Uri -Method Post -Headers $head -Body $Body -ErrorAction:Stop [xml]$sxml = $r.Content $Uri = $sxml.Task.href if ($RunAsync -eq $false) { Do { $r = Invoke-WebRequest -URI $Uri -Method Get -Headers $head -ErrorAction:Stop [xml]$sxml = $r.Content $Progress = $sxml.Task.Progress Write-Progress -Activity 'Test Cleanup' -Status 'Progress->' -PercentComplete $Progress Start-Sleep -s 5 } until ($Progress -eq 100) } }
