Merge pull request #76 from ZertoPublic/Refactor-Copy-ZertoVpg

Refactor Copy-ZertoVpg and Add Tests
This commit is contained in:
Wes Carroll
2020-04-20 16:19:54 -04:00
committed by GitHub
6 changed files with 178 additions and 41 deletions
+1 -25
View File
@@ -2,28 +2,4 @@
### Zerto Virtual Manager ### Zerto Virtual Manager
* Addressed a reported [issue](https://github.com/ZertoPublic/ZertoApiWrapper/issues/60) in the `Get-ZertoRecoveryReport` function where the `-VpgIdentifier` parameter was not working. This parameter is not accepted by the API as a valid filter and is ignored. This parameter has been removed from the function. * Refactored `Copy-ZertoVpg` functionality to leverage identifier and name maps and eliminate `where-object` searches.
* Addressed a reported [issue](https://github.com/ZertoPublic/ZertoApiWrapper/issues/61) where the `Export-ZertoVpgNicSetting` function would not properly execute when run against a VM with no NICs attached.
* In reviewing the `Export-ZertoVpgNicSetting`, a review of the `Import-ZertoVpgNicSetting` was completed and it was determined to update the import logic based on various test cases. Along with this, it is now possible to reset the NIC settings to the default state with the `Import-ZertoVpgNicSetting` command. Please review the [Import-ZertoVpgNicSetting help](https://github.com/ZertoPublic/ZertoApiWrapper/blob/master/docs/Import-ZertoVmNicSetting.md) to review the updated options and import logic.
* Refactored the `Checkpoint-ZertoVpg` command to allow pipeline input (ByValue and ByProperty) for the VpgName parameter.
* Corrected a parameter typo in the `Get-ZertoVpgSetting` function. The misspelt parameter was added as an alias to ensure any existing scripts using the parameter continue to function.
* Refactored the `Get-ZertoVpg` command to remove repetitive commands and variables that are no longer required.
* Moved `Invoke-ZertoRestRequest` and `Invoke-ZARestRequest` to be public functions. As there become more and more scenarios where there are not prebuilt functions to accomplish complex specialized tasks, it became apparent that these functions could be leveraged to make the experience and workflow easier.
* Updated the `Install-ZertoVra` logic to ensure that the target datastore is available on the target host. There isn't currently any method to validate the target network, but if that becomes available in a later version of the API, the function will be updated.
* Updated the `Install-ZertoVra` function to allow for installation of the VRA using the host password method. Please review the [Install-ZertoVra](https://github.com/ZertoPublic/ZertoApiWrapper/blob/master/docs/Install-ZertoVra.md) documentation for syntax and examples.
* Updated the `Edit-ZertoVra` function to allow for modification of the associated ESX host password if the need arises. Please review the [Edit-ZertoVra](https://github.com/ZertoPublic/ZertoApiWrapper/blob/master/docs/Edit-ZertoVra.md) documentation for syntax and examples.
* Added a new function, `Set-ZertoUserCredential`, to allow the updating of the username and password used to connect the Zerto Virtual Manager to the paired hypervisor. Please see the [Set-ZertoUserCredential](https://github.com/ZertoPublic/ZertoApiWrapper/blob/master/docs/Set-ZertoUserCredential.md) help for additional information.
* With the release of [Zerto 8.0](https://www.zerto.com/zerto-8-0-general-availability/) some additional API endpoints have become available.
* Updated `Get-ZertoVirtualizationSite` to add the `-repository` parameter to enable returning information for LTR repositories.
* Updated `Get-ZertoVpgSetting` to add the `-ltr` parameter to enable returning information for current LTR settings for the selected VPG.
### Zerto Analytics
* Added several functions for the newly added [Zerto Analytics](https://analytics.zerto.com) Planner.
* `Get-ZAPlannerSite` Help can be found here: [Get-ZAPlannerSite](https://github.com/ZertoPublic/ZertoApiWrapper/blob/master/docs/Get-ZAPlannerSite.md)
* `Get-ZAPlannerStatsReport` Help can be found here: [Get-ZAPlannerStatsReport](https://github.com/ZertoPublic/ZertoApiWrapper/blob/master/docs/Get-ZAPlannerStatsReport.md)
* `Get-ZAPlannerJournalSizeReport` Help can be found here: [Get-ZAPlannerJournalSizeReport](https://github.com/ZertoPublic/ZertoApiWrapper/blob/master/docs/Get-ZAPlannerJournalSizeReport.md)
* `Get-ZAPlannerNetworkPerformanceReport` Help can be found here: [Get-ZAPlannerNetworkPerformanceReport](https://github.com/ZertoPublic/ZertoApiWrapper/blob/master/docs/Get-ZAPlannerNetworkPerformanceReport.md)
* `Get-ZAPlannerWanReport` Help can be found here: [Get-ZAPlannerWanReport](https://github.com/ZertoPublic/ZertoApiWrapper/blob/master/docs/Get-ZAPlannerWanReport.md)
* `Get-ZAPlannerZcasReport` Help can be found here: [Get-ZAPlannerZcasReport](https://github.com/ZertoPublic/ZertoApiWrapper/blob/master/docs/Get-ZAPlannerZcasReport.md)
* Created `Get-ZAProtectedVm` and `Get-ZAProtectedVmReport` functions to leverage the newly released Zerto Analytics APIs for this data. Help files can be found here: [Get-ZAProtectedVm](https://github.com/ZertoPublic/ZertoApiWrapper/blob/master/docs/Get-ZAProtectedVm.md) and [Get-ZAProtectedVmReport](https://github.com/ZertoPublic/ZertoApiWrapper/blob/master/docs/Get-ZAProtectedVmReport.md)
+71
View File
@@ -48,7 +48,78 @@ Describe $global:function -Tag 'Unit', 'Source', 'Built' {
} }
Context "$($global:function)::Function Unit Tests" { Context "$($global:function)::Function Unit Tests" {
Mock -ModuleName ZertoApiWrapper -CommandName Get-ZertoVpg -ParameterFilter {
$vpgName -eq "MyVpg"
} {
return (Get-Content "$global:here\Mocks\GetVpg.json" -Raw | ConvertFrom-Json)
} -Verifiable
Mock -ModuleName ZertoApiWrapper -CommandName Get-ZertoVpg -ParameterFilter {
$vpgName -eq "NotAVpg"
} {
return $null
} -Verifiable
Mock -ModuleName ZertoApiWrapper -CommandName Get-ZertoUnprotectedVm {
return (Get-Content "$global:here\Mocks\UnprotectedVms.json" -Raw | ConvertFrom-Json)
} -Verifiable
Mock -ModuleName ZertoApiWrapper -CommandName Get-ZertoProtectedVm {
return (Get-Content "$global:here\Mocks\ProtectedVms.json" -Raw | ConvertFrom-Json)
} -Verifiable
Mock -ModuleName ZertoApiWrapper -CommandName Invoke-ZertoRestRequest -ParameterFilter {
$uri -eq "vpgSettings/copyVpgSettings"
} {
return (Get-Content "$global:here\Mocks\VpgId.txt")
} -Verifiable
Mock -ModuleName ZertoApiWrapper -CommandName Invoke-ZertoRestRequest -ParameterFilter {
$uri -eq "vpgSettings/9607f923-00a7-477b-8b04-26a386214455/vms"
} {
return $null
} -Verifiable
Mock -ModuleName ZertoApiWrapper -CommandName Invoke-ZertoRestRequest -ParameterFilter {
$uri -eq "vpgSettings/9607f923-00a7-477b-8b04-26a386214455"
} {
return (Get-Content "$global:here\Mocks\CopyVpgSettings.json" -Raw | ConvertFrom-Json)
} -Verifiable
Mock -ModuleName ZertoApiWrapper -CommandName Save-ZertoVpgSetting {
return (Get-Content "$global:here\Mocks\TaskId.txt")
} -Verifiable
Mock -ModuleName ZertoApiWrapper -CommandName Get-Map -ParameterFilter {
$null -ne $InputObject[0].VpgName
} {
@{
"WindowsBox" = "d809de8e-deb7-45cc-b620-08030a1143e1.vm-90"
"CentOS-Test" = "d809de8e-deb7-45cc-b620-08030a1143e1.vm-88"
"Application01" = "d809de8e-deb7-45cc-b620-08030a1143e1.vm-35"
"sql01-test" = "d809de8e-deb7-45cc-b620-08030a1143e1.vm-73"
"jenkins" = "d809de8e-deb7-45cc-b620-08030a1143e1.vm-75"
}
} -Verifiable
Mock -ModuleName ZertoApiWrapper -CommandName Get-Map -ParameterFilter {
$null -eq $InputObject[0].VpgName
} {
@{
"Win2019Template" = "d809de8e-deb7-45cc-b620-08030a1143e1.vm-79"
"Ubuntu01" = "d809de8e-deb7-45cc-b620-08030a1143e1.vm-34"
"WinTemplate" = "d809de8e-deb7-45cc-b620-08030a1143e1.vm-25"
"sql01-prod" = "d809de8e-deb7-45cc-b620-08030a1143e1.vm-87"
"nczvm.nc.lab" = "d809de8e-deb7-45cc-b620-08030a1143e1.vm-30"
}
} -Verifiable
It "Should throw an error when no VPG is found" {
{ Copy-ZertoVpg -SourceVpgName "NotAVpg" -NewVpgName "NewVpg" -VMs 'sql01-prod', 'Ubuntu01' } | Should Throw "Unable to find a VPG with the name:"
}
It "Returns a TaskIdentifier when called correctly" {
Copy-ZertoVpg -SourceVpgName "MyVpg" -NewVpgName "NewVpg" -VMs 'sql01-prod', 'Ubuntu01' | Should -Be "7e79035e-fb8c-47fe-815c-12ddd41708e6.3e4cdd0d-1064-4022-921f-6265ad6d335a"
}
It "Should warn when VM is not found" {
$results = Copy-ZertoVpg -SourceVpgName "MyVpg" -NewVpgName "NewVpg" -VMs 'sql01-prod', 'Ubuntu01', 'DoesNotExist' 3>&1
$results[0].ToString() | Should -Match 'Unable to find VM with Name DoesNotExist. Skipping.'
}
Assert-VerifiableMock
} }
} }
+79
View File
@@ -0,0 +1,79 @@
{
"Basic": {
"JournalHistoryInHours": 24,
"Name": "Test-SQL_Copy_1",
"Priority": "Medium",
"ProtectedSiteIdentifier": "15aa0d43-69cd-400a-8b99-fe94bbac3e19",
"RecoverySiteIdentifier": "8e1c9f53-4973-4a4a-b2dd-1ebb293614d8",
"RpoInSeconds": 300,
"ServiceProfileIdentifier": null,
"TestIntervalInMinutes": 262080,
"UseWanCompression": true,
"ZorgIdentifier": null
},
"BootGroups": {
"BootGroups": [
"@{BootDelayInSeconds=0; BootGroupIdentifier=00000000-0000-0000-0000-000000000000; Name=Default}"
]
},
"Journal": {
"DatastoreIdentifier": null,
"Limitation": {
"HardLimitInMB": 153600,
"HardLimitInPercent": 0,
"WarningThresholdInMB": 115200,
"WarningThresholdInPercent": 0
}
},
"LongTermRetention": null,
"Networks": {
"Failover": {
"Hypervisor": "@{DefaultNetworkIdentifier=09db6c5b-b956-430f-9589-b58876ca377a.network-20}",
"PublicCloud": null,
"VCD": null
},
"FailoverTest": {
"Hypervisor": "@{DefaultNetworkIdentifier=09db6c5b-b956-430f-9589-b58876ca377a.network-20}",
"PublicCloud": null,
"VCD": null
}
},
"Protected": {
"VCD": null
},
"Recovery": {
"DefaultDatastoreClusterIdentifier": "09db6c5b-b956-430f-9589-b58876ca377a.group-p44",
"DefaultDatastoreIdentifier": null,
"DefaultFolderIdentifier": "09db6c5b-b956-430f-9589-b58876ca377a.group-v3",
"DefaultHostClusterIdentifier": "09db6c5b-b956-430f-9589-b58876ca377a.domain-c7",
"DefaultHostIdentifier": null,
"PublicCloud": null,
"ResourcePoolIdentifier": null,
"VCD": null
},
"Scripting": {
"PostBackup": null,
"PostRecovery": {
"Command": null,
"Parameters": null,
"TimeoutInSeconds": 300
},
"PreRecovery": {
"Command": null,
"Parameters": null,
"TimeoutInSeconds": 300
}
},
"Vms": [
{
"BootGroupIdentifier": "00000000-0000-0000-0000-000000000000",
"Journal": "@{DatastoreIdentifier=; Limitation=}",
"Nics": "",
"Recovery": "@{DatastoreClusterIdentifier=09db6c5b-b956-430f-9589-b58876ca377a.group-p44; DatastoreIdentifier=; FolderIdentifier=09db6c5b-b956-430f-9589-b58876ca377a.group-v3; HostClusterIdentifier=09db6c5b-b956-430f-9589-b58876ca377a.domain-c7; HostIdentifier=; PublicCloud=; ResourcePoolIdentifier=; VCD=}",
"VmIdentifier": "d809de8e-deb7-45cc-b620-08030a1143e1.vm-87",
"Volumes": ""
}
],
"VpgIdentifier": null,
"VpgSettingsIdentifier": "0be951ef-229a-401c-9e0d-bd8a5baea19a"
}
+18
View File
@@ -0,0 +1,18 @@
[
{
"VmIdentifier": "d809de8e-deb7-45cc-b620-08030a1143e1.vm-87",
"VmName": "sql01-prod"
},
{
"VmIdentifier": "d809de8e-deb7-45cc-b620-08030a1143e1.vm-34",
"VmName": "Ubuntu01"
},
{
"VmIdentifier": "d809de8e-deb7-45cc-b620-08030a1143e1.vm-79",
"VmName": "Win2019Template"
},
{
"VmIdentifier": "d809de8e-deb7-45cc-b620-08030a1143e1.vm-25",
"VmName": "WinTemplate"
}
]
+1
View File
@@ -0,0 +1 @@
9607f923-00a7-477b-8b04-26a386214455
+8 -16
View File
@@ -25,32 +25,25 @@ function Copy-ZertoVpg {
process { process {
$VpgIdToCopy = @{ VpgIdentifier = (Get-ZertoVpg -vpgName $SourceVpgName).vpgIdentifier } $VpgIdToCopy = @{ VpgIdentifier = (Get-ZertoVpg -vpgName $SourceVpgName).vpgIdentifier }
if ( $null -eq $VpgIdToCopy.VpgIdentifier ) { if ( $null -eq $VpgIdToCopy.VpgIdentifier ) {
Write-Error -Message "Unable to find a VPG with the name: $SourceVpgName. Please check the name and try again." Throw "Unable to find a VPG with the name: $SourceVpgName. Please check the name and try again."
Break
} elseif ($VpgIdToCopy.VpgIdentifier.Count -gt 1) {
Write-Error -Message "More than one VPG was returned when searching for the VPG name: $SourceVpgName. Please try again."
Break
} }
$BaseUri = "vpgSettings/copyVpgSettings" $BaseUri = "vpgSettings/copyVpgSettings"
$UnprotectedVms = Get-ZertoUnprotectedVm $VmsMap = Get-Map -InputObject (Get-ZertoUnprotectedVm) -Key 'VmName' -Value 'VmIdentifier'
$ProtectedVms = Get-ZertoProtectedVm $VmsMap += Get-Map -InputObject (Get-ZertoProtectedVm) -Key 'VmName' -Value 'VmIdentifier'
$VMsToAdd = foreach ($VM in $VMs) { $VMsToAdd = foreach ($VM in $VMs) {
if ($UnprotectedVms.VmName -contains $VM) { if ($VmsMap.Keys -contains $VM) {
$VmId = $UnprotectedVms | Where-Object { $_.VmName -like $VM } | Select-Object -ExpandProperty VmIdentifier [PSCustomObject]@{
} elseif ($ProtectedVms.VmName -contains $VM) { VmIdentifier = $VmsMap[$VM]
$VmId = $ProtectedVms | Where-Object { $_.VmName -like $VM } | Select-Object -ExpandProperty VmIdentifier }
} else { } else {
Write-Warning -Message "Unable to find VM with Name $VM. Skipping." Write-Warning -Message "Unable to find VM with Name $VM. Skipping."
} }
$returnObject = New-Object PSObject
$returnObject | Add-Member -MemberType NoteProperty -Name "VmIdentifier" -Value $VmId
$returnObject
} }
if ($PSCmdlet.ShouldProcess("$VMsToAdd", "Copying $SourceVpgName to $NewVpgName with Settings")) { if ($PSCmdlet.ShouldProcess("$VMsToAdd", "Copying $SourceVpgName to $NewVpgName with Settings")) {
$NewVpgId = Invoke-ZertoRestRequest -Uri $BaseUri -Body ($VpgIdToCopy | ConvertTo-Json) -Method "POST" $NewVpgId = Invoke-ZertoRestRequest -Uri $BaseUri -Body ($VpgIdToCopy | ConvertTo-Json) -Method "POST"
$Uri = "{0}/{1}/vms" -f "vpgSettings", $NewVpgId $Uri = "{0}/{1}/vms" -f "vpgSettings", $NewVpgId
foreach ($VM in $VMsToAdd) { foreach ($VM in $VMsToAdd) {
$null = Invoke-ZertoRestRequest -Uri $Uri -Body ($VM | ConvertTo-Json) -Method "POST" $null = Invoke-ZertoRestRequest -Uri $Uri -Body ($VM | ConvertTo-Json) -Method "POST" -ErrorAction Stop
} }
$Uri = "vpgSettings/{0}" -f $NewVpgId $Uri = "vpgSettings/{0}" -f $NewVpgId
$CurrentSettings = Invoke-ZertoRestRequest -Uri $Uri $CurrentSettings = Invoke-ZertoRestRequest -Uri $Uri
@@ -58,7 +51,6 @@ function Copy-ZertoVpg {
$Null = Invoke-ZertoRestRequest -Uri $Uri -Method "Put" -Body $($CurrentSettings | ConvertTo-Json -Depth 20) $Null = Invoke-ZertoRestRequest -Uri $Uri -Method "Put" -Body $($CurrentSettings | ConvertTo-Json -Depth 20)
Save-ZertoVpgSetting -vpgSettingsIdentifier $NewVpgId Save-ZertoVpgSetting -vpgSettingsIdentifier $NewVpgId
} }
} }
end { end {