ES1_SuspendResume.psm1

<#
    .NOTES
    ===========================================================================
 
    Copyright � 2018 Dell Inc. or its subsidiaries. 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
    ===========================================================================
    THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
    WHETHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
    IF THIS CODE AND INFORMATION IS MODIFIED, THE ENTIRE RISK OF USE OR RESULTS IN
    CONNECTION WITH THE USE OF THIS CODE AND INFORMATION REMAINS WITH THE USER.

    .DESCRIPTION
        Functions for Suspending and Resuming SourceOne services (etc) for backups and other
        things. Essentially a port of the vbs scripts that are distributed with SourceOne

#>


#requires -Version 4

#
# Archive Server state enums for reference:
#
# EMC.Interop.ExASBaseAPI.exAServerStates exAServerAvailable 1
# EMC.Interop.ExASBaseAPI.exAServerStates exAServerSuspendingForBackup 2
# EMC.Interop.ExASBaseAPI.exAServerStates exAServerSuspendedForBackup 3
# EMC.Interop.ExASBaseAPI.exAServerStates exAServerFailedSuspendRequest 4
# EMC.Interop.ExASBaseAPI.exAServerStates exAServerResumingFromBackup 5
# EMC.Interop.ExASBaseAPI.exAServerStates exAServerFailedResumeRequest 6


function Suspend-ES1NativeArchives
{
<#
.SYNOPSIS
    Suspends all native archives for backup.
.DESCRIPTION
    Suspends all native archives for backup and waits for suspended state to occur.
    This is a port of the "ES1_NativeArchiveSuspend.vbs" script. Logging goes to "ES1_NativeArchiveSuspend.log"
    in the standard SourceOne logs directory.

.EXAMPLE
    Suspend-ES1NativeArchives

#>

[CmdletBinding()]
PARAM( )
BEGIN {

        $MyDebug = $false
        # Do both these checks to take advantage of internal parsing of syntaxes like -Debug:$false
        if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey("Debug") -and $PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent)
        {
            $DebugPreference = "Continue"
            Write-Debug "Debug Output activated"
            # for convenience
            $MyDebug = $true
        }

    try {
         [bool] $loaded = Add-ES1Types #-ErrorAction SilentlyContinue

        if (-not $loaded )
        {
            Write-Error 'Error loading SourceOne Objects and Types'
            break
        }
    }
    catch
    {
        Write-Error $_ 
        break
    }

    # Create a TraceObject and logfile

    $LOGFILE="ES1_NativeArchiveSuspend"
    $logger = new-object -ComObject ExTrace.CoExTrace

    # Identify file to write status messages to
    $logger.Init($LOGFILE, 0)

}

PROCESS{
    
    #
    # TODO - Make this parameters ! - Jay R.
    #

    # the amount of time before the suspend times out, in seconds.
    # Change this value to shorten or lengthen the wait, as
    # appropriate to your needs.
    $timeout=3600 

    # the amount of time in seconds between polls
    $pollinterval=10 

    # the amount of time in minutes before a server's heartbeat is
    # considered unresponsive
    $unresponsive=30 

    try
    {
 
        $asmgr = new-object -ComObject ExAsAdminAPI.CoExASAdminAPI
        $asmgr.SetTraceInfo($LOGFILE)
        $asmgr.Initialize()

        $repos=$asmgr.EnumerateRepositories()

        #
        # Tell all the repositories (archives) to suspend for backup
        # This can take a little time so we wait up to
        foreach ($repo in $repos)
        {
            $repo.SuspendForBackup()          
            $repoState = $repo.GetBackupState()


            if ($repoState -eq [int] [EMC.Interop.ExASBaseAPI.exAServerStates]::exAServerSuspendedForBackup) 
            {
                Write-Debug "Archive: $($repo.Name) Already suspended for backup"
                $logger.TraceInfoStr("Archive: $($repo.Name) Already suspended for backup",$PSCmdlet.MyInvocation.ScriptLineNumber, `
                        "SourceOne_POSH", $PSCmdlet.MyInvocation.MyCommand)
            }
            else
            {
                $logger.TraceInfoStr("Waiting for Archive: $($repo.Name) to suspend ...",$PSCmdlet.MyInvocation.ScriptLineNumber,"SourceOne_POSH", `
                            $PSCmdlet.MyInvocation.MyCommand)

                Write-Debug "Waiting for Archive: $($repo.Name) to suspend ..."
            }

        }
    
    try {
            # This will throw if the wait period expires....
            Wait-ArchiveState -state exAServerSuspendedForBackup  -pollinterval $pollinterval -timeout $timeout -unresponsive $unresponsive -logname $LOGFILE
        }
        catch {
            throw $_
        }
        
      
    }
    catch 
    {
        throw $_
    }
    finally
    {
        [System.Runtime.Interopservices.Marshal]::ReleaseComObject($asmgr)  > $null 
        [System.Runtime.Interopservices.Marshal]::ReleaseComObject($logger)  > $null 
    }
}

END{
}

}

function Resume-ES1NativeArchives
{
<#
.SYNOPSIS
    Resumes all native archives from suspend for backup.
.DESCRIPTION
    Resumes all native archives from suspend for backup and waits for available state to occur.
    This is a port of the "ES1_NativeArchiveResume.vbs" script. Logging goes to "ES1_NativeArchiveResume.log"
    in the standard SourceOne logs directory.

.EXAMPLE
    Resume-ES1NativeArchives

#>

[CmdletBinding()]
PARAM( )
BEGIN {

        $MyDebug = $false
        # Do both these checks to take advantage of internal parsing of syntaxes like -Debug:$false
        if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey("Debug") -and $PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent)
        {
            $DebugPreference = "Continue"
            Write-Debug "Debug Output activated"
            # for convenience
            $MyDebug = $true
        }

    try {
         [bool] $loaded = Add-ES1Types #-ErrorAction SilentlyContinue

        if (-not $loaded )
        {
            Write-Error 'Error loading SourceOne Objects and Types'
            break
        }
    }
    catch
    {
        Write-Error $_ 
        break
    }

    # Create a TraceObject and logfile
    $LOGFILE="ES1_NativeArchiveResume"
    
    # Create a TraceObject and logfile
    $logger = new-object -ComObject ExTrace.CoExTrace

    # Identify file to write status messages to
    $logger.Init($LOGFILE, 0)


}

PROCESS{
    
    #
    # TODO - Make this parameters ! - Jay R.
    # the amount of time before the suspend times out, in seconds.
    # Change this value to shorten or lengthen the wait, as
    # appropriate to your needs.
    $timeout=3600

    # the amount of timein seconds between polls
    $pollinterval=10

    # the amount of time in minutes before a server's heartbeat is
    # considered unresponsive
    $unresponsive=30 

    try
    {
 
        $asmgr = new-object -ComObject ExAsAdminAPI.CoExASAdminAPI
        $asmgr.SetTraceInfo($LOGFILE)
        $asmgr.Initialize()

        $repos=$asmgr.EnumerateRepositories()

        foreach ($repo in $repos)
        {
            
            $repoState = $repo.GetBackupState()
    
            if ($repoState -eq [int] [EMC.Interop.ExASBaseAPI.exAServerStates]::exAServerAvailable) 
            {
                Write-Debug "Archive: $($repo.Name) Already available"
                $logger.TraceInfoStr("Archive: $($repo.Name) Already available",$PSCmdlet.MyInvocation.ScriptLineNumber,"SourceOne_POSH", `
                        $PSCmdlet.MyInvocation.MyCommand)
            }
            else
            {
                $repo.ResumeFromBackup()
                $logger.TraceInfoStr("Waiting for Archive: $($repo.Name) to resume",$PSCmdlet.MyInvocation.ScriptLineNumber,"SourceOne_POSH", `
                        $PSCmdlet.MyInvocation.MyCommand)
                Write-Debug "Waiting for Archive: $($repo.Name) to resume ..."
            }

        }

        try {
            # This will throw if the wait period expires....
            Wait-ArchiveState -state exAServerAvailable  -pollinterval $pollinterval -timeout $timeout -unresponsive $unresponsive -logname $LOGFILE
        }
        catch {
            throw $_
        }
    }
    catch 
    {
        throw $_
    }
    finally
    {
        [System.Runtime.Interopservices.Marshal]::ReleaseComObject($asmgr)  > $null 
        [System.Runtime.Interopservices.Marshal]::ReleaseComObject($logger)  > $null 
    }
}

END{
}

}
 function Wait-ArchiveState
 {
 [CmdletBinding()]
PARAM(  [Parameter( Mandatory=$True)]
        [Alias('state')]
        [EMC.Interop.ExASBaseAPI.exAServerStates] $waitState=[EMC.Interop.ExASBaseAPI.exAServerStates]::exAServerAvailable ,
        [Parameter( Mandatory=$True,HelpMessage='The amount of time in seconds between state checks')]
         [int] $pollinterval=10,
        [Parameter( Mandatory=$True, HelpMessage='The total time to wait in seconds before the timing out.')]
         [int] $timeout=3600,
        [Parameter( Mandatory=$True, HelpMessage='The amount of time in minutes before a server is considered unresponsive.')]
         [int] $unresponsive=30,
        [Parameter( Mandatory=$True, HelpMessage='Name of log file')]
         $logname=''
        )
BEGIN {

    
    # Create a TraceObject and logfile
    $logger = new-object -ComObject ExTrace.CoExTrace

    # Identify file to write status messages to
    $logger.Init($logname, 0)


}
PROCESS {
         $startTime = Get-Date

        while($true)
        {
           $allInState = $true
           
           Start-Sleep -Milliseconds ($pollinterval * 1000)

            foreach ($repo in $repos)
            {
                $repoState = $repo.GetBackupState()
                if ($repoState -ne [int] $waitState) 
                {
                    $allInState = $false
                    Write-Debug "Still waiting for Archive: $($repo.Name)"
                    $logger.TraceInfoStr("Still waiting for Archive: $($repo.Name)",$PSCmdlet.MyInvocation.ScriptLineNumber,"SourceOne_POSH", `
                                $PSCmdlet.MyInvocation.MyCommand)
                }

            }
        
           $endTime = Get-Date
           $loopLength = ($endTime - $startTime).TotalMilliseconds
           $timeRemaining = ($timeout * 1000) - $loopLength

           if(($timeRemaining -le 0) -or ($allInState))
           {
              Write-Debug "Done waiting, time remaining $($timeremaining) , All 'in requested state' $($allInState) "
              break
           }
        }
        
        if($allInState)
        {
            $logger.TraceInfoStr("All Archives successfully in state $($waitState.ToString())",$PSCmdlet.MyInvocation.ScriptLineNumber,"SourceOne_POSH", `
                                $PSCmdlet.MyInvocation.MyCommand)
        
        }

        if($timeRemaining -le 0)
        {
            throw "$($PSCmdlet.MyInvocation.MyCommand), timeout expired waiting"
        }
    }


END{

    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($logger)  > $null 
}


 }

#
# Public Exports
#
Export-ModuleMember -Function * -Alias *