Public/Restore-AGMLibMount.ps1
# Copyright 2022 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. Function Restore-AGMLibMount ([string]$imageid,[string]$imagename,[string]$appname,[string]$appid,[string]$username,[string]$password,[string]$mountmode,[string]$recoverdb,[string]$recoverypoint,[string]$disableschedule,[string]$markdependent,[switch][alias("g")]$guided) { <# .SYNOPSIS Rewinds a mounted image to a previous point in time .EXAMPLE Rewind-AGMLibMount -g Runs a guided menu .EXAMPLE Restore-AGMLibMount -imageid 6384106 -username "oracle" -recoverypoint "2020-09-03 17:02" Rewinds an Oracle Database with image ID 6384106 to the specified recovery point. .EXAMPLE Restore-AGMLibMount -appid 6384030 -username "oracle" Rewinds an Oracle Database with image ID 6384106 to the specified recovery point. .DESCRIPTION A function to rewind a mount to a previous point in time There are two way to determine which image is used. Specify the appname or appid: appid - if specified without an imageID or imagename then the most recent snapshot will be used appname - if specified without an imageID or imagename then the most recent snapshot will be used Otherwise specify a specific image by name or ID: imageid - if specified this image ID will be used to rewind the mounted database imagename - if specified this image name will be used to rewind the mounted database There are many options: username: mandatory for Oracle, optional for SQL password: needs to be base64 encoded mountmode: vrdm, prdm or nfs (VMware only) recoverdb: whether to restore with recovery and start the DB, default is true, options are true or false recoverypoint: if the image has an END pit, specify a date in ISO format like 2020-09-01 19:01:00 disableschedule: by fault this is true. Can specify true or false markdependent: VMware only, can be true or false. Default is false #> if ( (!($AGMSESSIONID)) -or (!($AGMIP)) ) { Get-AGMErrorMessage -messagetoprint "Not logged in or session expired. Please login using Connect-AGM" return } $sessiontest = Get-AGMVersion if ($sessiontest.errormessage) { Get-AGMErrorMessage -messagetoprint "AGM session has expired. Please login again using Connect-AGM" return } # if the user gave us nothing to start work, then ask for a mounted app name if ( (!($appname)) -and (!($imagename)) -and (!($imageid)) -and (!($appid)) ) { $guided = $true Clear-Host Write-host "App selection menu" Write-host "" $activeimagegrab1 = Get-AGMImage -filtervalue characteristic=1 | select-object childapp $activeimagegrab = $activeimagegrab1.childapp | sort-object friendlytype,appname if ($activeimagegrab.id.count -eq 0) { Get-AGMErrorMessage -messagetoprint "There are no Active Images with child apps to list" return } if ($activeimagegrab.id.count -eq 1) { $appname = $activeimagegrab.appname $appid = $activeimagegrab.id $hostid = $activeimagegrab.host.id $apptype = $activeimagegrab.friendlytype write-host "Found only one Child app $appname ($apptype) with App ID $appid when checking for Mounted Apps to rewind" write-host "" } else { $i = 1 foreach ($child in $activeimagegrab) { if ($child.appname) { $appname = $child.appname $appid = $child.id $apptype = $child.friendlytype Write-Host -Object "$i`: $apptype - $appname ($appid)" $i++ } } While ($true) { Write-host "" $listmax = $activeimagegrab.appname.count [int]$appselection = Read-Host "Please select an app (1-$listmax)" if ($appselection -lt 1 -or $appselection -gt $listmax) { Write-Host -Object "Invalid selection. Please enter a number in range [1-$($listmax)]" } else { break } } $appname = $activeimagegrab.appname[($appselection - 1)] $appid = $activeimagegrab.id[($appselection - 1)] $hostid = $activeimagegrab.host.id[($appselection - 1)] } } # if we got a appname lets check it right now if ( ($appname) -and (!($appid)) ) { $appgrab = Get-AGMApplication -filtervalue "appname=$appname" if ($appgrab.id.count -ne 1) { Get-AGMErrorMessage -messagetoprint "Failed to resolve $appname to a unique valid app. Use: `"Get-AGMLibApplicationID $appname`" and try again specifying the learned ID as -appid." return } else { $appid = $appgrab.id $frommount = $appgrab.frommount } if ($frommount -ne "True") { Get-AGMErrorMessage -messagetoprint "App $appname is not a child app. This function is intended to rewind apps created by mounts where frommount equals True" return } } if ( (!($appname)) -and ($appid) ) { $appgrab = Get-AGMApplication -filtervalue "id=$appid" if ($appgrab.id.count -ne 1) { Get-AGMErrorMessage -messagetoprint "Failed to resolve $appid to a unique valid app. Use Get-AGMLibApplicationID to learn the appID nd try again" return } else { $appname = $appgrab.appname $frommount = $appgrab.frommount } if ($frommount -ne "True") { Get-AGMErrorMessage -messagetoprint "App $appname is not a child app. This function is intended to rewind apps created by mounts where frommount equals True" return } } # learn about the image if the user gave it if ($imagename) { $imagegrab = Get-AGMImage -filtervalue backupname=$imagename if ($imagegrab.id.count -eq 0) { Get-AGMErrorMessage -messagetoprint "Failed to find $imagename using: Get-AGMImage -filtervalue backupname=$imagename" return } else { $imageid = $imagegrab.id } } # this if for guided menu if ($guided) { if (!($imagename)) { Write-Host "" Write-Host "Image selection" Write-Host "1`: Use the latest snapshot(default)" Write-Host "2`: Select an image" Write-Host "" [int]$userselection = Read-Host "Please select from this list (1-2)" if (($userselection -eq "") -or ($userselection -eq 1)) { $imagecheck = Get-AGMLibLatestImage $appid if (!($imagecheck.backupname)) { Get-AGMErrorMessage -messagetoprint "Failed to find snapshot for AppID using: Get-AGMLibLatestImage $appid" return } else { $imagegrab = Get-AGMImage -id $imagecheck.id $imagename = $imagegrab.backupname $imageid = $imagegrab.id $consistencydate = $imagegrab.consistencydate $endpit = $imagegrab.endpit $appname = $imagegrab.appname $appid = $imagegrab.application.id } } else { $imagelist1 = Get-AGMImage -filtervalue "appid=$appid&jobclass=snapshot" | select-object -Property backupname,consistencydate,endpit,id | Sort-Object consistencydate if ($imagelist1.id.count -eq 0) { Get-AGMErrorMessage -messagetoprint "Failed to fetch any snapshot Images for appid $appid" return } $imagelist = $imagelist1 | select-object -Property backupname,consistencydate,endpit,id | Sort-Object consistencydate if ($imagelist1.count -eq 1) { $imagegrab = Get-AGMImage -id $($imagelist).id $imageid = $imagelist.id $imagename = $imagegrab.backupname $consistencydate = $imagegrab.consistencydate $endpit = $imagegrab.endpit write-host "Found only one snapshot of childapp $($appname): ID $imageid Imagename: $imagename ConsistencyDate: $consistencydate" } else { Clear-Host Write-Host "Snapshot list. Choose the best consistency date." $i = 1 foreach ($image in $imagelist.consistencydate) { Write-Host -Object "$i`: $image" $i++ } While ($true) { Write-host "" $listmax = $imagelist.Length [int]$imageselection = Read-Host "Please select an image (1-$listmax)" if ($imageselection -lt 1 -or $imageselection -gt $imagelist.Length) { Write-Host -Object "Invalid selection. Please enter a number in range [1-$($imagelist.Length)]" } else { break } } $imageid = $imagelist[($imageselection - 1)].id $imagegrab = Get-AGMImage -id $imageid $imagename = $imagegrab.backupname $consistencydate = $imagegrab.consistencydate $endpit = $imagegrab.endpit } } } # now we check the log date if ($endpit) { Clear-Host $recoverypoint = Read-Host "Roll forward time (hitting enter means no roll-forward)`: $consistencydate to $endpit" if ($recoverypoint) { if ([datetime]$recoverypoint -lt $consistencydate) { Get-AGMErrorMessage -messagetoprint "Specified recovery point $recoverypoint is earlier than image consistency date $consistencydate. Specify an earlier image." return } elseif ([datetime]$recoverypoint -gt $endpit) { Get-AGMErrorMessage -messagetoprint "Specified recovery point $recoverypoint is later than available logs that go to $endpit" return } } } Write-host "" $username = read-host "Username" if ($username) { $passwordenc = Read-Host -AsSecureString "Password" if ($passwordenc.length -ne 0) { $UnsecurePassword = ConvertFrom-SecureString -SecureString $passwordenc -AsPlainText $password = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($UnsecurePassword)) } } $hostgrab = Get-AGMHost -id $hostid $vmtype = $hostgrab.vmtype $transport = $hostgrab.transport $diskpref = $hostgrab.diskpref $vcenterid = $hostgrab.vcenterhost.id #if the VM doesn't have a transport, then the vCenter must have one if ( ($vmtype -eq "vmware") -and (!($transport)) ) { $vcgrab = Get-AGMHost -filtervalue id=$vcenterid $transport = $vcgrab.transport } # if this is a VMTarget if ($vmtype -eq "vmware") { if (($diskpref -eq "BLOCK") -and ($transport -ne "GUESTVMISCSI")) { Clear-Host Write-Host "Mount mode" if ($transport -eq "NFS") { $defaultmode = 3 Write-Host "1`: vrdm" Write-Host "2`: prdm" Write-Host "3`: nfs(default)" } else { $defaultmode = 1 Write-Host "1`: vrdm(default)" Write-Host "2`: prdm" Write-Host "3`: nfs" } Write-Host "" [int]$userselection = Read-Host "Please select from this list (1-3)" if ($userselection -eq "") { $userselection = $defaultmode } if ($userselection -eq 1) { $mountmode = "vrdm" } if ($userselection -eq 2) { $mountmode = "prdm" } if ($userselection -eq 3) { $mountmode = "nfs" } } if ($mountmode -eq "vrdm") { Write-Host "VMDK setting" Write-Host "1`: Don't mark dependent(default)" Write-Host "2`: Mark dependent" Write-Host "" [int]$userselection = Read-Host "Please select from this list (1-2)" if ($userselection -eq "") { $userselection = 1 } if ($userselection -eq 1) { $markdependent = "false" } if ($userselection -eq 2) { $markdependent = "true" } } } Write-Host "Recover database" Write-Host "1`: Recover database after restore(default)" Write-Host "2`: Don't recovery database after restore" Write-Host "" [int]$userselection = Read-Host "Please select from this list (1-2)" if ($userselection -eq "") { $userselection = 1 } if ($userselection -eq 1) { $recoverdb = "true" } if ($userselection -eq 2) { $recoverdb = "false" } Write-Host "1`: Disable Schedule (default)" Write-Host "2`: Leave Schedule enabled" Write-Host "" [int]$userselection = Read-Host "Please select from this list (1-2)" if ($userselection -eq "") { $userselection = 1 } if ($userselection -eq 1) { $notdisableschedule = "false" ; $disableschedule = "true" } if ($userselection -eq 2) { $notdisableschedule = "true" ; $disableschedule = "false"} Clear-Host Write-Host "Guided selection is complete. The values entered resulted in the following command:" Write-Host "" Write-Host -nonewline "Restore-AGMLibMount -imageid $imageid -appname `"$appname`" -appid $appid -recoverdb $recoverdb -disableschedule $disableschedule" if ($markdependent) { Write-Host -nonewline " -markdependent $markdependent" } if ($username) { Write-Host -nonewline " -username `"$username`"" } if ($password) { Write-Host -nonewline " -password `"$password`"" } if ($recoverypoint) { Write-Host -nonewline " -recoverypoint `"$recoverypoint`"" } Write-Host "" Write-Host "1`: Run the command now" Write-Host "2`: Show the JSON used to run this command, but don't run it" Write-Host "3`: Exit without running the command (default)" $userchoice = Read-Host "Please select from this list (1-3)" if (!($userchoice)) { $userchoice = 3 } if ($userchoice -eq 2) { $jsonprint = "yes" } if ($userchoice -eq 3) { return } } if ($recoverypoint) { $recoverytime = Convert-ToUnixDate $recoverypoint } # recovery or not if (!($recoverdb)) { $recoverdb = "true" } if ($disableschedule -eq "true") { $notdisableschedule = "false" } else { $notdisableschedule = "true" } # learn about the image if ((!($imageid)) -and ($appid)) { $imagegrab = Get-AGMLibLatestImage $appid if (!($imagegrab.backupname)) { Get-AGMErrorMessage -messagetoprint "Failed to find snapshot for AppID using: Get-AGMLibLatestImage $appid" return } else { $imageid = $imagegrab.id } } if (!($imageid)) { [string]$imageid = Read-Host "ImageID to restore" } if (!($mountmode)) { $physicalrdm = 0 $rdmmode = "independentvirtual" } else { if ($mountmode -eq "vrdm") { $physicalrdm = 0 if ($markdependent -eq "true") { $rdmmode = "dependentvirtual" } else { $rdmmode = "independentvirtual" } } if ($mountmode -eq "prdm") { $physicalrdm = 1 $rdmmode = "physical" } if ($mountmode -eq "nfs") { $physicalrdm = 2 $rdmmode = "nfs" } } $body = [ordered]@{ recover = $recoverdb; } if ($recoverytime) { $body += @{ recoverytime = [string]$recoverytime } } if ($mountmode) { $body = $body + @{ physicalrdm = $physicalrdm } $body = $body + @{ rdmmode = $rdmmode } } if ($username) { $body = $body + @{ username = $username } } if ($password) { $body = $body + @{ password = $password } } $body = $body + @{ notdisableschedule = $notdisableschedule } $json = $body | ConvertTo-Json if ($jsonprint -eq "yes") { $compressedjson = $body | ConvertTo-Json -compress Write-host "This is the final command:" Write-host "" Write-host "Post-AGMAPIData -endpoint /backup/$imageid/restore -body `'$compressedjson`'" return } Post-AGMAPIData -endpoint /backup/$imageid/restore -body $json } |