Public/New-AGMLibLVMMount.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 New-AGMLibLVMMount ([string]$appid,[string]$mountapplianceid,[string]$appname,[string]$targethostname,[string]$targethostid,[string]$imageid,[string]$imagename,[string]$label,[string]$mountmode,[string]$mountaction,[string]$prescript,[string]$postscript,[string]$volumes,[string]$volumemappings,[string]$mapdiskstoallesxhosts,[string]$mountlocation,[switch][alias("g")]$guided,[switch][alias("m")]$monitor,[switch][alias("w")]$wait) 
{
    <#
    .SYNOPSIS
    Mounts a LVM image

    .EXAMPLE
    New-AGMLibLVMMount

    Runs a guided menu to mount an image of a LVM to a host

    .EXAMPLE
    New-AGMLibLVMMount -appid 1425738 -targethostid 1425591 -mountaction specifymountlocation -mountapplianceid 145666187717 -mountlocation "/mnt12"

    Mounts the latest image for appid 1425738 on appliance 145666187717 to target host id 1425591, mounting into mount point /mnt12

    .DESCRIPTION
    A function to mount LVM images to an existing host

    * Image selection can be done three ways:

    1) Run this command in guided mode to learn the available images and select one
    2) Learn the imagename and specify that as part of the command with -imagename
    3) Learn the Appid and Cluster ID for the appliance that will mount the image and then use -appid and -mountapplianceid
    This will use the latest snapshot, StreamSnap or OnVault image on that appliance

    The mount action field is used to determine which mount action to take:
    -mountaction agentmanaged Will mount using the mount points selected by the agent (this is the default behaviour)
    -mountaction specifymountlocation Will mount using the source paths using a specified mount point that is supplied with -mountlocation
    -mountaction nomap Will mount without mapping the drives

    These parameters are optional:
    -label <labelvalue> To assign a label to the mounted image (which is recommended)

    There are two monitoring options:

    -wait This will wait up to 2 minutes for the job to start, checking every 15 seconds to show you the job name
    -monitor Same as -wait but will also run Get-AGMLibFollowJobStatus to monitor the job to completion
    #>


    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)
    {
        $sessiontest
        return
    }

    # if the user gave an AppID lets check it and grab an image, need to expand outside snapshots
    if (($appid) -and ($mountapplianceid) -and (!($imageid)))
    {
        # if we are not running guided mode but we have an appid without imageid, then lets get the latest image on the mountappliance ID
        $imagegrab = Get-AGMImage -filtervalue "appid=$appid&targetuds=$mountapplianceid&jobclass=snapshot&jobclass=StreamSnap&jobclass=OnVault" -sort "consistencydate:desc,jobclasscode:asc" -limit 1
        if ($imagegrab.count -eq 1)
        {   
            $imageid = $imagegrab.id
            $imagename = $imagegrab.backupname
        }
        else 
        {
            Get-AGMErrorMessage -messagetoprint "Failed to fetch a snapshot, StreamSnap or OnVault Image for appid $appid on appliance with clusterID $mountapplianceid"
            return
        }
    }

    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 single App ID. Use Get-AGMLibApplicationID and try again specifying -appid."
            return
        }
        else {
            $appid = $appgrab.id
        }
    }

    # learn about the image
    if ($imagename)
    {
        $imagecheck = Get-AGMImage -filtervalue backupname=$imagename
        if (!($imagecheck))
        {
            Get-AGMErrorMessage -messagetoprint "Failed to find $imagename using: Get-AGMImage -filtervalue backupname=$imagename"
            return
        }
        else 
        {
            $imagegrab = Get-AGMImage -id $imagecheck.id
            $imageid = $imagegrab.id
            $appname = $imagegrab.appname
            $appid = $imagegrab.application.id
        }
    }


    # if the user gave us nothing to start work, then ask for a VMware VM name
    if ( (!($appname)) -and (!($imagename)) -and (!($imageid)) -and (!($appid)) )
    {
        $guided = $true
        Clear-Host
        Write-host "LVM source selection menu"
        Write-host ""
        $appgrab = Get-AGMApplication -filtervalue "apptype=LVM Volume" | sort-object apptype,appname
        if ($appgrab.count -eq 0)
        {
            Get-AGMErrorMessage -messagetoprint "There are no LVM apps to list"
            return
        }
        else 
        {
            $i = 1
            foreach ($app in $appgrab)
            { 
            $app | Add-Member -NotePropertyName select -NotePropertyValue $i
            $app | Add-Member -NotePropertyName appliancename -NotePropertyValue $app.cluster.name
            $app | Add-Member -NotePropertyName hostname -NotePropertyValue $app.host.name
            $i++
            }
            Clear-Host
            write-host "Select an application"
            Write-host ""
            $appgrab | select-object select,apptype,hostname,appname,id,appliancename | Format-table *
            While ($true) 
            {
                Write-host ""
                $listmax = $appgrab.id.count
                [int]$userselection = Read-Host "Please select an app to work with (1-$listmax)"
                if ($userselection -lt 1 -or $userselection -gt $listmax)
                {
                    Write-Host -Object "Invalid selection. Please enter a number in range [1-$($listmax)]"
                } 
                else
                {
                    break
                }
            }
            if ($appgrab.count -eq 1)
            {
                $appname =  $appgrab.appname
                $appid = $appgrab.id
            }
            else
            {
                $appid = $appgrab.id[($userselection - 1)]
                $appname =  $appgrab.appname[($userselection - 1)]
            }
        }
        
        #image selection time
        Clear-Host
        $imagelist = Get-AGMImage -filtervalue "appid=$appid&jobclass=snapshot&jobclass=StreamSnap&jobclass=OnVault"  | Sort-Object consistencydate,jobclasscode | select-object -Property backupname,consistencydate,id,jobclass,cluster,transport
        if ($imagelist.backupname.count -eq 0)
        {
            Get-AGMErrorMessage -messagetoprint "Failed to fetch any snapshot, streamsnap or onvault Images for appid $appid"
            return
        }
        if ($imagelist.backupname.count -eq 1)
        {
            $imagegrab = Get-AGMImage -id ($imagelist).id
            $imagename = $imagegrab.backupname                
            $restorableobjects = $imagegrab.restorableobjects
        } 
        else
        {
            Clear-Host
            Write-Host "Image list. Choose the best consistency date and jobclass."
            $i = 1
            foreach ($image in $imagelist)
            { 
                $image | Add-Member -NotePropertyName select -NotePropertyValue $i
                $image | Add-Member -NotePropertyName appliancename -NotePropertyValue $image.cluster.name
                $i++
            }

            $imagelist | select-object select,consistencydate,jobclass,appliancename,backupname,id,transport | Format-table *
            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                
            $restorableobjects = $imagegrab.restorableobjects
            $mountapplianceid = $imagegrab.cluster.clusterid
            $mountappliancename = $imagegrab.cluster.name
        }
    }

    if ( ($targethostname) -and (!($targethostid)) )
    {
        $hostcheck = Get-AGMHost -filtervalue hostname=$targethostname
        if ($hostcheck.id.count -ne 1)
        { 
            Get-AGMErrorMessage -messagetoprint "Failed to resolve $targethostname to a single host ID. Use Get-AGMLibHostID and try again specifying -targethostid"
            return
        }
        else 
        {
            $hostgrab = Get-AGMHost -id $hostcheck.id
            $targethostid = $hostgrab.id
            $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 we got a target ID lets check it
    if ( ($targethostid) -and (!($targethostname)) )
    {
        $hostgrab = Get-AGMHost -filtervalue id=$targethostid
        if ($hostgrab.id.count -eq -0)
        {
            Get-AGMErrorMessage -messagetoprint "Failed to resolve $targethostid to a single host ID. Use Get-AGMLibHostID and try again specifying -targethostid"
            return
        }
        $targethostname=$hostgrab.hostname
        $vmtype = $hostgrab.vmtype
        $transport = $hostgrab.transport
        $diskpref = $hostgrab.diskpref
        $vcenterid = $hostgrab.vcenterhost.id
        if ( ($vmtype -eq "vmware") -and (!($transport)) )
        {
            $vcgrab = Get-AGMHost -filtervalue id=$vcenterid 
            $transport = $vcgrab.transport
        }
    }
    
    # Guided menu for target selection and moint points and restore points and VMware options
    if ($guided)
    {
        if (!($label))
        {
            Clear-Host
            [string]$label = Read-host "Label"
        }

        # mountedhost menu
        if (!($targethostid))
        {
            $hostgrab = Get-AGMHost -filtervalue "clusterid=$mountapplianceid&hosttype!VMCluster&hosttype!esxhost&hosttype!NetApp 7 Mode&hosttype!NetApp SVM&hosttype!ProxyNASBackupHost&hosttype!Isilon&hasagent=true" | sort-object vmtype,hostname
            if ($hostgrab.id.count -eq -0)
            {
                Get-AGMErrorMessage -messagetoprint "Failed to find any hosts on $mountappliancename"
                return
            }
            if ($hostgrab.id.count -eq 1)
            {
                $targethostid = $hostgrab.id
                $targethostname = $hostgrab.hostname
            } 
            else
            {
                Clear-Host
                Write-Host "Host List."
                $i = 1
                foreach ($hostid in $hostgrab)
                { 
                    $hostid | Add-Member -NotePropertyName select -NotePropertyValue $i
                    if (!($hostid.vmtype))
                    {
                        $hostid | Add-Member -NotePropertyName vmtype -NotePropertyValue "Physical"
                    }
                    $i++
                }

                $hostgrab | select-object select,vmtype,hostname,ostype,id | Format-table *
                While ($true) 
                {
                    Write-host ""
                    $listmax = $hostgrab.count
                    [int]$userselection = Read-Host "Please select a host (1-$listmax)"
                    if ($userselection -lt 1 -or $userselection -gt $hostgrab.Length)
                    {
                        Write-Host -Object "Invalid selection. Please enter a number in range [1-$($hostgrab.count)]"
                    } 
                    else
                    {
                        break
                    }
                }
                $targethostid = $hostgrab.id[($userselection - 1)]
                $targethostname = $hostgrab.hostname[($userselection - 1)]
                $vmtype = $hostgrab.vmtype[($userselection - 1)]
                $transport = $hostgrab.transport[($userselection - 1)]
                $diskpref = $hostgrab.diskpref[($userselection - 1)]
                $vcenterid = $hostgrab.vcenterhostid[($userselection - 1)]
                if ( ($vmtype -eq "vmware") -and (!($transport)) )
                {
                    $vcgrab = Get-AGMHost -filtervalue id=$vcenterid 
                    $transport = $vcgrab.transport
                }
            }
        }
        # scripts
        if (!($prescript))
        {
            [string]$prescript = Read-host "Pre-script path (optional, press enter to skip)"
        }
        if (!($postscript))
        {
            [string]$postscript = Read-host "Post-script path (optional, press enter to skip)"
        }




         # 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"  }
         
                 # map to all ESX host
                 Clear-Host
                 Write-Host "Map to all ESX Hosts"
                 Write-Host "1`: Do not map to all ESX Hosts(default)"
                 Write-Host "2`: Map to all ESX Hosts"
                 Write-Host ""
                 [int]$userselection = Read-Host "Please select from this list (1-2)"
                 if ($userselection -eq "") { $userselection = 1 }
                 if ($userselection -eq 1) {  $mapdiskstoallesxhosts = "false"  }
                 if ($userselection -eq 2) {  $mapdiskstoallesxhosts = "true"  }
             }
         }
         # mount action
         write-host ""
         Write-Host "Mount action" 
         Write-Host "1`: Agent managed mountpoint(default)"
         Write-Host "2`: Specify mount location"
         Write-Host "3`: Map only"
         Write-Host ""
         $userselection = ""
         [int]$userselection = Read-Host "Please select from this list (1-3)"
         if ($userselection -eq "") { $userselection = "agentmanaged" }
         if ($userselection -eq 1) {  $mountaction = "agentmanaged"  }
         if ($userselection -eq 2) {  $mountaction = "specifymountlocation"  }
         if ($userselection -eq 3) {  $mountaction = "maponly"  }



        if ($mountaction -eq "specifymountlocation")
        {
            if (!($mountlocation))
            {
                $mountlocation = Read-Host "Mount location for the image (optional)"
            }
        }

        Clear-Host
        Write-Host "Guided selection is complete. The values entered would result in the following command:"
        Write-Host ""
        Write-Host -nonewline "New-AGMLibLVMMount -appid $appid -targethostid $targethostid -imageid $imageid -mountaction $mountaction" 
        if ($uservolumelistfinal)
        {
            Write-Host -nonewline " -volumes `"$uservolumelistfinal`""
        }
        if ($mountapplianceid)
        {
            Write-Host -nonewline " -mountapplianceid $mountapplianceid"
        }
        if ($volumemappings)
        {
            Write-Host -nonewline " -volumemappings `"$volumemappings`""
        }
        if ($mountmode)
        {
            Write-Host -nonewline " -mountmode $mountmode -mapdiskstoallesxhosts $mapdiskstoallesxhosts"
        }
        if ($mountlocation)
        {
            Write-Host -nonewline " -mountlocation `"$mountlocation`""
        }
        if ($prescript)
        {
            Write-Host -nonewline " -prescript `"$prescript`""
        }
        if ($postscript)
        {
            Write-Host -nonewline " -postscript `"$postscript`""
        }
        if ($label)
        {
            Write-Host -nonewline " -label `"$label`""
        }
        Write-Host ""
        Write-Host "1`: Run the command now (default)"
        Write-Host "2`: Show the JSON used to run this command, but don't run it"
        Write-Host "3`: Exit without running the command. If you save the command, remove the imageid to always use the most recent backup."
        $userchoice = Read-Host "Please select from this list (1-3)"
        if ($userchoice -eq 2)
        {
            $jsonprint = "yes"
        }
        if ($userchoice -eq 3)
        {
            return
        }
    }
    if (!($mountaction))
    {
        $mountaction = "agentmanaged"
    }    


    # if user asked for volumes
    if ($mountaction -eq "agentmanaged")
    {
        # now see if user wants mount points or drives per volume
        $vollist = $restorableobjects | select-object name | sort-object name
        $selectedobjects = @(
            foreach ($volume in $vollist.name)
            {
                [pscustomobject]@{restorableobject=$volume}
            }   
        )     

        $selectedobjects = @()
        foreach ($volume in $vollist.name)
        {
            $selectedobjects = $selectedobjects + [ordered]@{
                restorableobject = $volume
            }
        } 


    }
    
    if (!($imageid))
    {
        [string]$imageid = Read-Host "ImageID to mount"
    }


    if (!($mountmode))
    {
        $physicalrdm = 2
        $rdmmode = "nfs"
    }
    else 
    {
        if ($mountmode -eq "vrdm")
        {
            $physicalrdm = 0
            $rdmmode = "independentvirtual"
        }
        if ($mountmode -eq "prdm")
        {
            $physicalrdm = 1
            $rdmmode = "physical"
        }
        if ($mountmode -eq "nfs")
        {
            $physicalrdm = 2
            $rdmmode = "nfs"
        }
    }

    if ($mapdiskstoallesxhosts)
    {
        if (($mapdiskstoallesxhosts -ne "true") -and  ($mapdiskstoallesxhosts -ne "false"))
        {
            Get-AGMErrorMessage -messagetoprint "The value of Map to all ESX hosts of $mapdiskstoallesxhosts is not valid. Must be true or false"
            return
        }
        $restoreoptions = @(
            @{
                name = 'mapdiskstoallesxhosts'
                value = "$mapdiskstoallesxhosts"
            }
        )
    }

    if ($mountaction -eq "specifymountlocation")
    {
        $restoreoptions  = @(
            @{
                name = 'mountpointperimage'
                value = "$mountlocation"
            }
        )
    }

    if ($mountaction -eq "maponly")
    {
        $restoreoptions = @(
            @{
                name = 'maponly'
                value = $true
            }
        )
    }
    

    # handle script
    if ($prescript)
    {
        $script = @( [ordered]@{ phase = "PRE" ; name = $prescript } )
    }
    if ($postscript)
    {
        $script = @( [ordered]@{ phase = "POST" ; name = $postscript } )
    }
    if (($prescript) -and ($postscript))
    {
        $script = @( [ordered]@{ phase = "PRE" ; name = $prescript } ; [ordered]@{ phase = "POST" ; name = $postscript })
    }

    if (!($label))
    {
        $label = ""
    }

    $body = [ordered]@{
        label = $label;
        host = @{id=$targethostid}
        hostclusterid = $mountapplianceid;
    }
    if ($selectedobjects)
    {
        $body = $body + [ordered]@{ selectedobjects = $selectedobjects }
    }
    if ($restoreoptions)
    {
        $body = $body + [ordered]@{ restoreoptions = $restoreoptions }
    }
    if ($restoreobjectmappings)
    {
        $body = $body + [ordered]@{ restoreobjectmappings = $restoreobjectmappings }
    }
    if ($mountmode)
    {
        $body = $body + @{ physicalrdm = $physicalrdm }
        $body = $body + @{ rdmmode = $rdmmode }
    }
    if ($script)
    {
        $body = $body + [ordered]@{ script = $script }
    }


    $json = $body | ConvertTo-Json

    if ($monitor)
    {
        $wait = $true
    }

    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/mount -body `'$compressedjson`'"
        return
    }

    Post-AGMAPIData  -endpoint /backup/$imageid/mount -body $json
    if ($wait)
    {
        Start-Sleep -s 15
        $i=1
        while ($i -lt 9)
        {
            Clear-Host
            write-host "Checking for a running job for appid $appid against targethostname $targethostname"
            $jobgrab = Get-AGMJob -filtervalue "appid=$appid&jobclasscode=5&isscheduled=False&targethost=$targethostname" -sort queuedate:desc -limit 1 
            if (!($jobgrab.jobname))
            {
                write-host "Job not running yet, will wait 15 seconds and check again. Check $i of 8"
                Start-Sleep -s 15
                $jobgrab = Get-AGMJob -filtervalue "appid=$appid&jobclasscode=5&isscheduled=False&targethost=$targethostname" -sort queuedate:desc -limit 1 
                if (!($jobgrab.jobname))
                {
                    $i++
                }
            }
            else
            {   
                $i=9
                $jobgrab| select-object jobname,status,progress,queuedate,startdate,targethost
                
            }
        }
        if (($jobgrab.jobname) -and ($monitor))
        {
            Get-AGMLibFollowJobStatus $jobgrab.jobname
        }
    }
}