ES1_VolumeUtils.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 and objects for managing volumes (containers) in a folder

#>


<#
.SYNOPSIS
    Issue a Close command to open volume(s) in the archive and folder specified
.DESCRIPTION
    Issue a Close command to open volume(s) in the archive and folder specified.
    The process of closing and "burning" the volume is handled by archive servers on a scheduled basis. The completion of the close
    operation could take some time.
    A warning is issued if there are are active policies and activities as those may cause unexpected results.

.PARAMETER ArchiveName
    The name of the archive connection

.PARAMETER ArchiveFolder
    Optional , if provided only volumes in this folder will be closed.

.OUTPUTS
    List of volumes which were closed

.EXAMPLE
    $closed = Close-ES1Volume -ArchiveName Archive1 -ArchiveFolder 5Year
#>

Function Close-ES1Volume {
[CmdletBinding(SupportsShouldProcess=$true,
               ConfirmImpact=�High�)]
PARAM( [Parameter(Mandatory=$true)]
        [Alias('archive')]
        [string] $ArchiveName,        
        [Parameter(Mandatory=$false)]
        [Alias('name')]
        [string] $ArchiveFolder
        )


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
    }
}

PROCESS {
    $asmgr = new-object -ComObject ExAsAdminAPI.CoExASAdminAPI
    $asmgr.SetTraceInfo($Global:S1LogFile)
    $asmgr.Initialize()

    $closedVolumes=@()

    # Check for active Policy and activities that could conflict with a close
    $activities = Get-ES1Activities 
    [bool] $activePolicy=$false

    foreach ($activity in $activities)
    {
        
        if ($activity.state -eq [EMC.Interop.ExBase.exActivityState]::exActivityState_Active)
        {
            Write-Warning "Policy: $($activity.Policy), Activity: $($activity.Name) is Active."
            $activePolicy=$true
        }
    }

    if ($activePolicy -and (-not ($pscmdlet.ShouldContinue( "Closing volumes with active Policies may have undesired effects. Continue ?", "Activity Still Active !"))))
    {

        break;            
    }

    try {
        # Get the volume flag enumeration strings/names
        $volStates=[enum]::GetNames([EMC.Interop.ExAsBaseAPI.exASVolumeFlags])

        $repo=$asmgr.GetRepository($ArchiveName)
        $repo.SetTraceInfo($Global:S1LogFile)
    
        $folderplan=$repo.GetFolderPlan()
        $folders = @($folderplan.EnumerateArchiveFolders())

        # If an archive folder was specified only process that one...
        if ($ArchiveFolder )
        {
            foreach ($folder in $folders)
            {
             if (($folder.Name -eq $ArchiveFolder) )
                {
                    $folders = @($folder)
                    break;
                }
            }
        }

        # dummy object because need to pass something but its not actually used
        $asFilter = $repo.CreateNewObject([EMC.Interop.ExAsAdminAPI.exASObjectType]::exASObjectType_ArchiveFolder)

        foreach ($folder in $folders)
        {
            $containerfolders=$folder.EnumerateContainerFolders()
            foreach ($cf in $containerfolders)
            {
                $cf.SetTraceInfo($Global:S1LogFile)

                $volumes=$cf.EnumerateVolumes($asFilter)

                # loop through all the volumes and if it is open then close it.
                foreach($volume in $volumes)
                {
                    $roles = @()
                       for ($i = 0 ; $i -lt ($volStates.Length); $i++)
                    {
                        [int] $bits = [EMC.Interop.ExAsBaseAPI.exASVolumeFlags]$volStates[$i]

                        if ($volume.Flags -band $bits)
                        {
                            $roles += ([EMC.Interop.ExAsBaseAPI.exASVolumeFlags]$volStates[$i]).ToString().Substring(14)
                        }

                    }

                    Write-Verbose "Volume: $($volume.UNCPath) FolderID: $($volume.FolderID) Flags: $($volume.Flags) $($roles) "

                    # if the volume is not "RecordPending" and its not "Closed", close it
                    if (($volume.Flags -band [EMC.Interop.ExAsBaseAPI.exASVolumeFlags]::exASVolumeFlagRecordPending) -and `
                        ((-not ($volume.Flags -band [EMC.Interop.ExAsBaseAPI.exASVolumeFlags]::exASVolumeFlagClosed))) )
                    {
                            Write-Verbose "Issueing Close Volume: $($volume.UNCPath) Flags: $($volume.Flags) $($roles) "
                            
                            if ($pscmdlet.ShouldProcess($volume.VolumeName)) 
                            {
                                $folderplan.CloseVolume($volume.FolderID, $volume.VolumeName)
                                $closedVolumes += $volume
                            }
                
                    }
                    elseif (($volume.Flags -band [EMC.Interop.ExAsBaseAPI.exASVolumeFlags]::exASVolumeFlagRecordPending) -or `
                            ($volume.Flags -band [EMC.Interop.ExAsBaseAPI.exASVolumeFlags]::exASVolumeFlagActiveRecording) )
                    {   
                
                        # Display a volume is "RecordPending" or "ActiveRecording"
                        Write-Verbose "Close in progress Volume: $($volume.UNCPath) FolderID: $($volume.FolderID) Flags: $($volume.Flags), $($roles)"

                    }
                }          

            }
    
        }
            
        }
        catch 
        {
            Write-Error $_
        }
        finally
        {
            [System.Runtime.Interopservices.Marshal]::ReleaseComObject($folderplan) > $null
            [System.Runtime.Interopservices.Marshal]::ReleaseComObject($repo) > $null
        }
      
      $closedVolumes
}
END {}
}

<#
.SYNOPSIS
    Get a list of volumes in the given state(s) from the archive and folder specified
.DESCRIPTION
    Get a list of volumes in the given state(s) from the archive and folder specified
    The input States are some convenience states to simplify the various combinations and interpretation of the
    [EMC.Interop.ExAsBaseAPI.exASVolumeFlags] values. The VolumeFlags are bitmask represented by those state flags,
    some of which are transient states.

.PARAMETER ArchiveName
    The name of the archive connection

.PARAMETER ArchiveFolder
    Optional , if provided only volumes in the folder and requested states will be returned

.PARAMETER States
    List of states. The States correlate to some common combinations of the [EMC.Interop.ExAsBaseAPI.exASVolumeFlags]
    enum of the volume flag bits.
    OPEN will return volumes with RecordPending bit set or Closed bit not set
    CLOSED will return volumes with the Closed bit set
    PENDING will return volumes with the RecordPending bit set
    FAILED will return volumes with the FailedRecord bit set
    DELETED will return volumes with any of the RemovalPending, DeletedFromSQL or DeletedFromStorage bits set.
.OUTPUTS
    List of volumes

.EXAMPLE
    $openVolume = Get-ES1VolumeByState -ArchiveName Archive1 -ArchiveFolder 5Year -States OPEN
    
    Get a list of open volumes from the 5Year folder

.EXAMPLE
    Get-ES1VolumeByState -ArchiveName Archive1 -States FAILED,DELETED

    Get a list of failed and deleted volumes from an entire archive
    
#>

Function Get-ES1VolumeByState {
[CmdletBinding()]
PARAM( [Parameter(Mandatory=$true)]
        [Alias('archive')]
        [string] $ArchiveName,        
        [Parameter(Mandatory=$false)]
        [Alias('name')]
        [string] $ArchiveFolder,
        [Parameter(Mandatory=$true)]
        [ValidateSet ("OPEN","CLOSED","PENDING", "FAILED", "DELETED")]
        [string[]] $States
        )


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
    }
}

PROCESS {
    $asmgr = new-object -ComObject ExAsAdminAPI.CoExASAdminAPI
    $asmgr.SetTraceInfo($Global:S1LogFile)
    $asmgr.Initialize()

    $RetVolumes=@()

    # build a mask of the flags we're looking for
    [int] $volFlags= [EMC.Interop.ExAsBaseAPI.exASVolumeFlags]::exASVolumeFlagDefault
    
    try {
        # Get the volume flag enumeration strings/names
        $volStates=[enum]::GetNames([EMC.Interop.ExAsBaseAPI.exASVolumeFlags])

        $repo=$asmgr.GetRepository($ArchiveName)
        if ($repo -eq $null)
        {
            throw "Error: No archive named $($ArchiveName) found"
        }

        $repo.SetTraceInfo($Global:S1LogFile)
    
        $folderplan=$repo.GetFolderPlan()
        $folders = @($folderplan.EnumerateArchiveFolders())

        # If an archive folder was specified only process that one...
        if ($ArchiveFolder )
        {
            foreach ($folder in $folders)
            {
             if (($folder.Name -eq $ArchiveFolder) )
                {
                    $folders = @($folder)
                    break;
                }
            }
        }

        # dummy object because need to pass something but its not actually used
        $asFilter = $repo.CreateNewObject([EMC.Interop.ExAsAdminAPI.exASObjectType]::exASObjectType_ArchiveFolder)

        foreach ($folder in $folders)
        {
            $containerfolders=$folder.EnumerateContainerFolders()
            foreach ($cf in $containerfolders)
            {
                $volumes=$cf.EnumerateVolumes($asFilter)

                # loop through all the volumes
                foreach($volume in $volumes)
                {
                    $roles = @()
                       for ($i = 0 ; $i -lt ($volStates.Length); $i++)
                    {
                        [int] $bits = [EMC.Interop.ExAsBaseAPI.exASVolumeFlags]$volStates[$i]

                        if ($volume.Flags -band $bits)
                        {
                            $roles += ([EMC.Interop.ExAsBaseAPI.exASVolumeFlags]$volStates[$i]).ToString().Substring(14)
                        }

                    }

                    # add a user readable string representation of the flags
                    $volume | Add-Member NoteProperty -Name "VolumeState" -Value $roles

                    Write-Verbose "Volume: $($volume.UNCPath) FolderID: $($volume.FolderID) Flags: $($volume.Flags) $($roles) "
                    foreach ($state in $States)
                    {
                        if ( ($state -eq 'OPEN') -and `
                        (($volume.Flags -band [EMC.Interop.ExAsBaseAPI.exASVolumeFlags]::exASVolumeFlagRecordPending) -and `
                        ((-not ($volume.Flags -band [EMC.Interop.ExAsBaseAPI.exASVolumeFlags]::exASVolumeFlagClosed))) ))
                        {
                            $RetVolumes += $volume
                            break;
                        }

                        if (($state -eq 'CLOSED') -and ($volume.Flags -band [EMC.Interop.ExAsBaseAPI.exASVolumeFlags]::exASVolumeFlagClosed))
                        {
                            $RetVolumes += $volume
                            break;
                        }

                        if ( ($state -eq 'FAILED') -and `
                        (($volume.Flags -band [EMC.Interop.ExAsBaseAPI.exASVolumeFlags]::exASVolumeFlagFailedRecord) ))
                        {
                            $RetVolumes += $volume
                            break;
                        }
                        if ( ($state -eq 'DELETED') -and `
                        (($volume.Flags -band [EMC.Interop.ExAsBaseAPI.exASVolumeFlags]::exASVolumeFlagRemovalPending) -or `
                         ($volume.Flags -band [EMC.Interop.ExAsBaseAPI.exASVolumeFlags]::exASVolumeFlagDeletedFromStorage) -or `
                         ($volume.Flags -band [EMC.Interop.ExAsBaseAPI.exASVolumeFlags]::exASVolumeFlagDeletedFromSQL) ))
                        {
                            $RetVolumes += $volume
                            break;
                        }
                        if ( ($state -eq 'PENDING') -and `
                        (($volume.Flags -band [EMC.Interop.ExAsBaseAPI.exASVolumeFlags]::exASVolumeFlagRecordPending) ))
                        {
                            $RetVolumes += $volume
                            break;
                        }

                    }

    
                }          

            }
    
        }
            
        }
        catch 
        {
            Write-Error $_
        }
        finally
        {
            if ($folderplan)
            {
                [System.Runtime.Interopservices.Marshal]::ReleaseComObject($folderplan) > $null
            }
            if ($repo)
            {
                [System.Runtime.Interopservices.Marshal]::ReleaseComObject($repo) > $null
            }
        }
      
      $RetVolumes
}
END {}
}

Export-ModuleMember -Function * -Alias *