PureStorage.CBS.AVS.VVOLS.Replication.ps1

function Sync-VvolReplicationGroup {
    [CmdletBinding()]
    Param(
        [Parameter(mandatory = $true)]
        [String]$ReplicationGroupID,

        [Parameter(mandatory = $true)]
        [String]$PointInTimeReplicaName,

        [Parameter(Mandatory = $True)]
        $vCenterServer,
      
        [Parameter(Mandatory = $true)]
        [String]$AVSCloudName,

        [Parameter(Mandatory = $true)]
        [String]$AVSResourceGroup
    )

    $PointInTimeReplica = Get-SpbmPointInTimeReplica -Server $vCenterServer -Name $PointInTimeReplicaName -ErrorAction Ignore
    if ($PointInTimeReplica) {
        throw "Point-in-time replica '$PointInTimeReplicaName' already exists."
    }

    $repGroup = Get-SpbmReplicationGroup -Server $vCenterServer -ID $ReplicationGroupID
    if (-not $repGroup) {
        throw "Could not find replication group '$ReplicationGroupID'..."
    }
    elseif ($repGroup.State -ne "Target") {
        throw "Replication group '$ReplicationGroupID' need to be the Target replication group."
    }

    # wait for rep groups to sync
    $retryCount = 10
    $TimeoutInSecs = 10
    do {
        Write-Progress -Activity "Start sync replication group" -Status "50% Complete:" -PercentComplete 50
        $params = @{
            ReplicationGroupId     = $ReplicationGroupID
            PointInTimeReplicaName = $PointInTimeReplicaName
        }
        Invoke-RunScript -RunCommandName  "Sync-ReplicationGroup" -RunCommandModule "Microsoft.AVS.VVOLS" -Parameters $params `
            -AVSCloudName $AVSCloudName -AVSResourceGroup $AVSResourceGroup -ErrorAction Ignore
        Start-Sleep -Seconds $TimeoutInSecs
        Get-SpbmPointInTimeReplica -name $PointInTimeReplicaName -ErrorAction SilentlyContinue -ErrorVariable stopError
        $retryCount--
    } while ($stopError -and $retryCount -gt 0)

    if ($stopError) {
        throw 'Took too long to sync. Giving up.'
    }
}
function Start-VvolReplicationGroupFailover {  
    [CmdletBinding()]
    Param(
        [Parameter(mandatory = $true)]
        [String]$ClusterName,

        [Parameter(mandatory = $true)]
        [String]$ReplicationGroupID,

        [Parameter(mandatory = $false)]
        [String]$PointInTimeReplicaName,

        [Parameter(mandatory = $false)]
        [bool]$PowerOn,

        [Parameter(mandatory = $false)]
        [bool]$TestFailover,

        [Parameter(Mandatory = $True)]
        $vCenterServer,
        
        [Parameter(Mandatory = $true)]
        [String]$AVSCloudName,

        [Parameter(Mandatory = $true)]
        [String]$AVSResourceGroup
    )
    ## check that the cluster exists ##
    $Cluster = Get-Cluster -Server $vCenterServer -Name $ClusterName -ErrorAction Stop
    if (-not $Cluster) {
        throw "Could not find cluster '$ClusterName'..."
    }

    $repGroup = Get-SpbmReplicationGroup -ID $ReplicationGroupID

    if (-not $repGroup) {
        throw "Could not find replication group '$ReplicationGroupID'."
    }
    elseif ($repGroup.State -ne "Target") {
        throw "Replication group '$ReplicationGroupID' need to be a replication group in 'Target' state."
    }

    if ($PointInTimeReplicaName) {
        $PointInTimeReplica = Get-SpbmPointInTimeReplica -Server $vCenterServer -Name $PointInTimeReplicaName -ErrorAction Ignore
        if (-not $PointInTimeReplica) {
            throw "Could not find point-in-time replica '$PointInTimeReplicaName'."
        }
    }

    #creates groupid tag to tag new VMs
    $cat = Get-TagCategory -Name "PCBS-Rep-Group-ID" -ErrorAction SilentlyContinue
    if (-not $cat) {
        $cat = New-TagCategory -Name "PCBS-Rep-Group-ID" -EntityType "VirtualMachine"
    }
    $tag = Get-Tag -Name $ReplicationGroupID -Category $cat -ErrorAction SilentlyContinue
    if (-not $tag) {
        $tag = New-Tag -Name $ReplicationGroupID -Category $cat
    }

    $params = @{
        ReplicationGroupId     = $ReplicationGroupID
        TestFailover           = $TestFailover
        PointInTimeReplicaName = $PointInTimeReplicaName
    }
    Write-Progress -Activity "Start failover" -Status "50% Complete:" -PercentComplete 50
    Invoke-RunScript -RunCommandName  "Start-ReplicationFailover" -RunCommandModule "Microsoft.AVS.VVOLS" -Parameters $params `
        -AVSCloudName $AVSCloudName -AVSResourceGroup $AVSResourceGroup -GetNamedOutputs

    $ReplicatedVMFiles = (Get-Variable -Name NamedOutputs).Value
    # Might need change after MSFT published the new package
    $ReplicatedVMFiles.psobject.Properties | ForEach-Object {
        # create VMs on the failover vcenter
        Write-Host "Creating vm from vm file $($_.value)..."
        $newvm = New-VM -VMFilePath $_.value -ResourcePool $ClusterName
        # assign rep group tag to new vm
        New-TagAssignment -Tag $tag -Entity $newvm | Out-Null

        ## Starting the VM if PowerOn param is true ##
        if ($PowerOn) {
            $newvm | Start-VM -ErrorAction SilentlyContinue -ErrorVariable hasQuestion | out-null
            if ($hasQuestion) {
                ## there is a question that is asked if the VM is copied or moved. This question must be answered ##
                $newvm | Get-VMQuestion | Set-VMQuestion -Option '*copied*' -Confirm:$false
            }
        }
    }
  
}
  
function Start-VvolCleanupSourceReplicationGroupForFailover {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true)]
        [String]$ReplicationGroupID,

        [Parameter(Mandatory = $false)]
        [bool]$RemoveFromDisk,

        [Parameter(Mandatory = $True)]
        $vCenterServer,
      
        [Parameter(Mandatory = $true)]
        [String]$AVSCloudName,

        [Parameter(Mandatory = $true)]
        [String]$AVSResourceGroup
    )
  
    ## Setting the Replication Group Variable ##
    $repGroup = Get-SpbmReplicationGroup -Server $vCenterServer -ID $ReplicationGroupID

    if (-not $repGroup) {
        throw "Could not find replication group '$ReplicationGroupID'."
    }
    elseif ($repGroup.State -ne "Source") {
        throw "Replication group '$ReplicationGroupID' need to be the Source replication group."
    }
  
    ## Get VMs related to replication Group ##
    $relatedVMs = Get-VM -RelatedObject $repGroup
  
    foreach ($vm in $relatedVMs) {
        # stop VMs
        # if we fail to stop it's probably powered off already so we can ignore, if it's not it will fail on the next step
        $vm | Stop-VM -Confirm:$false -ErrorAction SilentlyContinue
  
        # unregister VMs
        # if VM exists but we cannot remove it, we should not continue
        if ($RemoveFromDisk) {
            Write-Verbose "Removing VM files from disk for VM $($vm.Name)"
            $vm | Remove-VM -Confirm:$false -DeletePermanently -ErrorAction Stop
        }
        else {
            Write-Verbose "Unregister VM $($vm.Name) from inventory"
            $vm | Remove-VM -Confirm:$false -ErrorAction Stop
        }
    }
  
    # remove tag category created on previous failovers
    $cat = Get-TagCategory -Server $vCenterServer -Name "PCBS-Rep-Group-ID" -ErrorAction SilentlyContinue
    if ($cat) {
        $tag = Get-Tag -Server $vCenterServer -Category $cat -ErrorAction SilentlyContinue
        if (-not $tag) {
            Remove-TagCategory -Server $vCenterServer -Category $cat -Confirm:$false
        }
    }
}
  
function Remove-ReplicationTags {
    [CmdletBinding()]
    Param(
        [Parameter(mandatory = $true)]
        [String]$ReplicationGroupID,

        [Parameter(Mandatory = $True)]
        $vCenterServer
    )
  
    $cat = Get-TagCategory -Server $vCenterServer -Name "PCBS-Rep-Group-ID" -ErrorAction SilentlyContinue
    if ($cat) {
        $tag = Get-Tag -Server $vCenterServer -Name $ReplicationGroupID -Category $cat -ErrorAction SilentlyContinue
        if ($tag) {
            # cleanup tags created by previous failovers
            Remove-Tag -Server $vCenterServer -Tag $tag -Confirm:$false
        }
    }
}
  
function Get-VMsByReplicationTags {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true)]
        [String]$ReplicationGroupID,

        [Parameter(Mandatory = $True)]
        $vCenterServer
    )
  
    $cat = Get-TagCategory -Server $vCenterServer -Name "PCBS-Rep-Group-ID" -ErrorAction SilentlyContinue
    $tag = Get-Tag -Server $vCenterServer -Name $ReplicationGroupID -Category $cat -ErrorAction SilentlyContinue
    if (-not $tag) {
        return $null
    }
    # get vms list
    $vms = Get-VM -Server $vCenterServer -Tag $tag
    return $vms
}

function Stop-VvolReplicationGroupFailoverTest {
    <#
    .SYNOPSIS
      Stops test failover operation against group
    .DESCRIPTION
      Issue test failover operation against group
    .INPUTS
      replication group ID,
    .OUTPUTS
      N.A.
    .NOTES
      Version: 1.0
    .EXAMPLE
      Stop-VvolReplicationGroupFailoverTest -RepGroupID myGroupId
      #>

  
    [CmdletBinding()]
    Param(
        [Parameter(mandatory = $true)]
        [String]$ReplicationGroupID,

        [Parameter(Mandatory = $True)]
        $vCenterServer,
      
        [Parameter(Mandatory = $true)]
        [String]$AVSCloudName,

        [Parameter(Mandatory = $true)]
        [String]$AVSResourceGroup
    )
  
    ## Setting the Replication Group Variable ##
    $repGroup = Get-SpbmReplicationGroup -Server $vCenterServer -ID $ReplicationGroupID

    if (-not $repGroup) {
        throw "Could not find replication group '$ReplicationGroupID'."
    }
    elseif ($repGroup.State -ne "InTest") {
        throw "Replication group '$ReplicationGroupID' need to be the InTest replication group."
    }

    # get vms list
    $vms = Get-VMsByReplicationTags -vCenterServer $vCenterServer -ReplicationGroupID $ReplicationGroupID

    foreach ($vm in $vms) {
        # stop VMs
        # if we fail to stop it's probably powered off already so we can ignore, if it's not it will fail on the next step
        $vm | Stop-VM -Confirm:$false -ErrorAction SilentlyContinue

        # unregister VMs
        # if VM exists but we cannot remove it, we should not continue
        $vm | Remove-VM -DeletePermanently:$true -Confirm:$false -ErrorAction Stop
    }
    Remove-ReplicationTags -vCenterServer $vCenterServer -ReplicationGroupID $ReplicationGroupID

    $retryCount = 5
    $TimeoutInSecs = 20
  
    do {  
        # Issue test failover stop
        # can fail saying that resource is still in use, but works if we retry
        $params = @{
            ReplicationGroupId = $ReplicationGroupID
        }
        Write-Progress -Activity "Stop failover" -Status "50% Complete:" -PercentComplete 50
        Invoke-RunScript -RunCommandName  "Stop-ReplicationTestFailover" -RunCommandModule "Microsoft.AVS.VVOLS" -Parameters $params `
            -AVSCloudName $AVSCloudName -AVSResourceGroup $AVSResourceGroup -ErrorAction SilentlyContinue
        $SPBMGroup = Get-SpbmReplicationGroup -ID $ReplicationGroupID

        # wait sometimes for vms to cleanup
        Start-Sleep -Seconds $TimeoutInSecs
    } while ($SPBMGroup.state -eq "InTest" -and $retryCount -gt 0)
}
  
function Start-VvolReprotectReplicationGroup {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true)]
        [String]$ReplicationGroupID,

        [Parameter(Mandatory = $false)]
        [String]$PolicyName,
      
        [Parameter(Mandatory = $True)]
        $vCenterServer,
      
        [Parameter(Mandatory = $true)]
        [String]$AVSCloudName,

        [Parameter(Mandatory = $true)]
        [String]$AVSResourceGroup
    )
  
    ## Setting the Replication Group Variable ##
    $failoverGroup = Get-SpbmReplicationGroup -Server $vCenterServer -ID $ReplicationGroupID

    if (-not $failoverGroup) {
        throw "Could not find replication group '$ReplicationGroupID'."
    }
    elseif ($failoverGroup.State -ne "FailedOver") {
        throw "Replication group '$ReplicationGroupID' need to be the FailedOver replication group."
    }
  
    ## Running the reverse replication group operation to reverse the source and target status for the Replication Group ##
    Write-Progress -Activity "Start failover reverse" -Status "50% Complete:" -PercentComplete 50
    $params = @{
        ReplicationGroupId = $ReplicationGroupID
    }
    Invoke-RunScript -RunCommandName  "Start-ReplicationReverse" -RunCommandModule "Microsoft.AVS.VVOLS" -Parameters $params `
        -AVSCloudName $AVSCloudName -AVSResourceGroup $AVSResourceGroup -GetNamedOutputs

    # Might need change after MSFT published the new package
    $new_source_group = (Get-Variable -Name NamedOutputs).Value.new_source_group
    
    # get vms list
    $cat = Get-TagCategory -Server $vCenterServer -Name "PCBS-Rep-Group-ID" -ErrorAction SilentlyContinue
    $tag = Get-Tag -Server $vCenterServer -Name $ReplicationGroupID -Category $cat -ErrorAction SilentlyContinue

    if (-not $tag) {
        throw "VM tag '$ReplicationGroupID' is missing. Failed to find VMs to reprotect."
    }
    # get vms list
    $vms = Get-VM -Tag $tag
  
    ## Setting the New VM to a variable on the recovery vCenter Server
    $relevantObjs = @()
    foreach ($newvm in $vms) {
        $relevantObjs += $newvm
        $HD1 = $newvm | Get-HardDisk
        $relevantObjs += $HD1
    }
  
    ## get relevant SPBM-related configuration data of Virtual Machine, Hard Disk, and Datastore objects
    $spbmConfig = $relevantObjs | Get-SpbmEntityConfiguration
  
    ## Resetting the storage policy for the VM and each virtual disk to the "VVol No Requirements Policy" ##
    Write-Host "Resetting the storage policy for the VM and each virtual disk to the 'VVol No Requirements Policy'..."
    $noReqPolicy = Get-SpbmStoragePolicy -name 'VVol No Requirements Policy'
    $spbmConfig | Set-SpbmEntityConfiguration -StoragePolicy $noReqPolicy
  
    if ($PolicyName) {
        Write-Host "Re-protecting VMs with policy $PolicyName..."
        ## Setting the Variables for the replication group and storage policy that we want to use to re-protect the VM to the previous source/protected site ##
        $new_policy = Get-SpbmStoragePolicy -name $PolicyName -ErrorAction Ignore
        if (-not $new_policy) {
            throw "Could not find storage policy '$PolicyName'..."
        }
        $new_rg = $new_policy | Get-SpbmReplicationGroup -Name $new_source_group
  
        ## Applying the Storage Policy and Replication group to the VMs to complete the Re-protect process ##
        $spbmConfig | Set-SpbmEntityConfiguration -StoragePolicy $new_policy -ReplicationGroup $new_rg
    }
    Remove-ReplicationTags -ReplicationGroupID $ReplicationGroupID -vCenterServer $vCenterServer
}
# SIG # Begin signature block
# MIIn+AYJKoZIhvcNAQcCoIIn6TCCJ+UCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUkP2J1NplD0k4cZJ1h47nrZ/c
# M8WggiEoMIIFjTCCBHWgAwIBAgIQDpsYjvnQLefv21DiCEAYWjANBgkqhkiG9w0B
# AQwFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD
# VQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVk
# IElEIFJvb3QgQ0EwHhcNMjIwODAxMDAwMDAwWhcNMzExMTA5MjM1OTU5WjBiMQsw
# CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
# ZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQw
# ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz
# 7MKnJS7JIT3yithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS
# 5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7
# bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfI
# SKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jH
# trHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14
# Ztk6MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2
# h4mXaXpI8OCiEhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt
# 6zPZxd9LBADMfRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPR
# iQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ER
# ElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4K
# Jpn15GkvmB0t9dmpsh3lGwIDAQABo4IBOjCCATYwDwYDVR0TAQH/BAUwAwEB/zAd
# BgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wHwYDVR0jBBgwFoAUReuir/SS
# y4IxLVGLp6chnfNtyA8wDgYDVR0PAQH/BAQDAgGGMHkGCCsGAQUFBwEBBG0wazAk
# BggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsGAQUFBzAC
# hjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURS
# b290Q0EuY3J0MEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0
# LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwEQYDVR0gBAowCDAGBgRV
# HSAAMA0GCSqGSIb3DQEBDAUAA4IBAQBwoL9DXFXnOF+go3QbPbYW1/e/Vwe9mqyh
# hyzshV6pGrsi+IcaaVQi7aSId229GhT0E0p6Ly23OO/0/4C5+KH38nLeJLxSA8hO
# 0Cre+i1Wz/n096wwepqLsl7Uz9FDRJtDIeuWcqFItJnLnU+nBgMTdydE1Od/6Fmo
# 8L8vC6bp8jQ87PcDx4eo0kxAGTVGamlUsLihVo7spNU96LHc/RzY9HdaXFSMb++h
# UD38dglohJ9vytsgjTVgHAIDyyCwrFigDkBjxZgiwbJZ9VVrzyerbHbObyMt9H5x
# aiNrIv8SuFQtJ37YOtnwtoeW/VvRXKwYw02fc7cBqZ9Xql4o4rmUMIIGrjCCBJag
# AwIBAgIQBzY3tyRUfNhHrP0oZipeWzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQG
# EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
# cnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIw
# MzIzMDAwMDAwWhcNMzcwMzIyMjM1OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UE
# ChMORGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQg
# UlNBNDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEF
# AAOCAg8AMIICCgKCAgEAxoY1BkmzwT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCw
# zIP5WvYRoUQVQl+kiPNo+n3znIkLf50fng8zH1ATCyZzlm34V6gCff1DtITaEfFz
# sbPuK4CEiiIY3+vaPcQXf6sZKz5C3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ
# 7Gnf2ZCHRgB720RBidx8ald68Dd5n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7
# QKxfst5Kfc71ORJn7w6lY2zkpsUdzTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/teP
# c5OsLDnipUjW8LAxE6lXKZYnLvWHpo9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCY
# OjgRs/b2nuY7W+yB3iIU2YIqx5K/oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9K
# oRxrOMUp88qqlnNCaJ+2RrOdOqPVA+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6
# dSgkQe1CvwWcZklSUPRR8zZJTYsg0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM
# 1+mYSlg+0wOI/rOP015LdhJRk8mMDDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbC
# dLI/Hgl27KtdRnXiYKNYCQEoAA6EVO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbEC
# AwEAAaOCAV0wggFZMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1N
# hS9zKXaaL3WMaiCPnshvMB8GA1UdIwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9P
# MA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcB
# AQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggr
# BgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1
# c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGln
# aWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAI
# BgZngQwBBAIwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7Zv
# mKlEIgF+ZtbYIULhsBguEE0TzzBTzr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI
# 2AvlXFvXbYf6hCAlNDFnzbYSlm/EUExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/ty
# dBTX/6tPiix6q4XNQ1/tYLaqT5Fmniye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVP
# ulr3qRCyXen/KFSJ8NWKcXZl2szwcqMj+sAngkSumScbqyQeJsG33irr9p6xeZmB
# o1aGqwpFyd/EjaDnmPv7pp1yr8THwcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc
# 6UsCUqc3fpNTrDsdCEkPlM05et3/JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3c
# HXg65J6t5TRxktcma+Q4c6umAU+9Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0d
# KNPH+ejxmF/7K9h+8kaddSweJywm228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZP
# J/tgZxahZrrdVcA6KYawmKAr7ZVBtzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLe
# Mt8EifAAzV3C+dAjfwAL5HYCJtnwZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDy
# Divl1vupL0QVSucTDh3bNzgaoSv27dZ8/DCCBrAwggSYoAMCAQICEAitQLJg0pxM
# n17Nqb2TrtkwDQYJKoZIhvcNAQEMBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoT
# DERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UE
# AxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTIxMDQyOTAwMDAwMFoXDTM2
# MDQyODIzNTk1OVowaTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ
# bmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBS
# U0E0MDk2IFNIQTM4NCAyMDIxIENBMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC
# AgoCggIBANW0L0LQKK14t13VOVkbsYhC9TOM6z2Bl3DFu8SFJjCfpI5o2Fz16zQk
# B+FLT9N4Q/QX1x7a+dLVZxpSTw6hV/yImcGRzIEDPk1wJGSzjeIIfTR9TIBXEmtD
# mpnyxTsf8u/LR1oTpkyzASAl8xDTi7L7CPCK4J0JwGWn+piASTWHPVEZ6JAheEUu
# oZ8s4RjCGszF7pNJcEIyj/vG6hzzZWiRok1MghFIUmjeEL0UV13oGBNlxX+yT4Us
# SKRWhDXW+S6cqgAV0Tf+GgaUwnzI6hsy5srC9KejAw50pa85tqtgEuPo1rn3MeHc
# reQYoNjBI0dHs6EPbqOrbZgGgxu3amct0r1EGpIQgY+wOwnXx5syWsL/amBUi0nB
# k+3htFzgb+sm+YzVsvk4EObqzpH1vtP7b5NhNFy8k0UogzYqZihfsHPOiyYlBrKD
# 1Fz2FRlM7WLgXjPy6OjsCqewAyuRsjZ5vvetCB51pmXMu+NIUPN3kRr+21CiRshh
# WJj1fAIWPIMorTmG7NS3DVPQ+EfmdTCN7DCTdhSmW0tddGFNPxKRdt6/WMtyEClB
# 8NXFbSZ2aBFBE1ia3CYrAfSJTVnbeM+BSj5AR1/JgVBzhRAjIVlgimRUwcwhGug4
# GXxmHM14OEUwmU//Y09Mu6oNCFNBfFg9R7P6tuyMMgkCzGw8DFYRAgMBAAGjggFZ
# MIIBVTASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBRoN+Drtjv4XxGG+/5h
# ewiIZfROQjAfBgNVHSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8B
# Af8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwdwYIKwYBBQUHAQEEazBpMCQG
# CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKG
# NWh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290
# RzQuY3J0MEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNv
# bS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3JsMBwGA1UdIAQVMBMwBwYFZ4EMAQMw
# CAYGZ4EMAQQBMA0GCSqGSIb3DQEBDAUAA4ICAQA6I0Q9jQh27o+8OpnTVuACGqX4
# SDTzLLbmdGb3lHKxAMqvbDAnExKekESfS/2eo3wm1Te8Ol1IbZXVP0n0J7sWgUVQ
# /Zy9toXgdn43ccsi91qqkM/1k2rj6yDR1VB5iJqKisG2vaFIGH7c2IAaERkYzWGZ
# gVb2yeN258TkG19D+D6U/3Y5PZ7Umc9K3SjrXyahlVhI1Rr+1yc//ZDRdobdHLBg
# XPMNqO7giaG9OeE4Ttpuuzad++UhU1rDyulq8aI+20O4M8hPOBSSmfXdzlRt2V0C
# FB9AM3wD4pWywiF1c1LLRtjENByipUuNzW92NyyFPxrOJukYvpAHsEN/lYgggnDw
# zMrv/Sk1XB+JOFX3N4qLCaHLC+kxGv8uGVw5ceG+nKcKBtYmZ7eS5k5f3nqsSc8u
# pHSSrds8pJyGH+PBVhsrI/+PteqIe3Br5qC6/To/RabE6BaRUotBwEiES5ZNq0RA
# 443wFSjO7fEYVgcqLxDEDAhkPDOPriiMPMuPiAsNvzv0zh57ju+168u38HcT5uco
# P6wSrqUvImxB+YJcFWbMbA7KxYbD9iYzDAdLoNMHAmpqQDBISzSoUSC7rRuFCOJZ
# DW3KBVAr6kocnqX9oKcfBnTn8tZSkP2vhUgh+Vc7tJwD7YZF9LRhbr9o4iZghurI
# r6n+lB3nYxs6hlZ4TjCCBsIwggSqoAMCAQICEAVEr/OUnQg5pr/bP1/lYRYwDQYJ
# KoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ
# bmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2
# IFRpbWVTdGFtcGluZyBDQTAeFw0yMzA3MTQwMDAwMDBaFw0zNDEwMTMyMzU5NTla
# MEgxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjEgMB4GA1UE
# AxMXRGlnaUNlcnQgVGltZXN0YW1wIDIwMjMwggIiMA0GCSqGSIb3DQEBAQUAA4IC
# DwAwggIKAoICAQCjU0WHHYOOW6w+VLMj4M+f1+XS512hDgncL0ijl3o7Kpxn3GIV
# WMGpkxGnzaqyat0QKYoeYmNp01icNXG/OpfrlFCPHCDqx5o7L5Zm42nnaf5bw9Yr
# IBzBl5S0pVCB8s/LB6YwaMqDQtr8fwkklKSCGtpqutg7yl3eGRiF+0XqDWFsnf5x
# XsQGmjzwxS55DxtmUuPI1j5f2kPThPXQx/ZILV5FdZZ1/t0QoRuDwbjmUpW1R9d4
# KTlr4HhZl+NEK0rVlc7vCBfqgmRN/yPjyobutKQhZHDr1eWg2mOzLukF7qr2JPUd
# vJscsrdf3/Dudn0xmWVHVZ1KJC+sK5e+n+T9e3M+Mu5SNPvUu+vUoCw0m+PebmQZ
# BzcBkQ8ctVHNqkxmg4hoYru8QRt4GW3k2Q/gWEH72LEs4VGvtK0VBhTqYggT02ke
# fGRNnQ/fztFejKqrUBXJs8q818Q7aESjpTtC/XN97t0K/3k0EH6mXApYTAA+hWl1
# x4Nk1nXNjxJ2VqUk+tfEayG66B80mC866msBsPf7Kobse1I4qZgJoXGybHGvPrhv
# ltXhEBP+YUcKjP7wtsfVx95sJPC/QoLKoHE9nJKTBLRpcCcNT7e1NtHJXwikcKPs
# CvERLmTgyyIryvEoEyFJUX4GZtM7vvrrkTjYUQfKlLfiUKHzOtOKg8tAewIDAQAB
# o4IBizCCAYcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/
# BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
# MB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1UdDgQWBBSltu8T
# 5+/N0GSh1VapZTGj3tXjSTBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsMy5k
# aWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0
# YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEFBQcwAYYYaHR0
# cDovL29jc3AuZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRwOi8vY2FjZXJ0
# cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGlt
# ZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCBGtbeoKm1mBe8cI1P
# ijxonNgl/8ss5M3qXSKS7IwiAqm4z4Co2efjxe0mgopxLxjdTrbebNfhYJwr7e09
# SI64a7p8Xb3CYTdoSXej65CqEtcnhfOOHpLawkA4n13IoC4leCWdKgV6hCmYtld5
# j9smViuw86e9NwzYmHZPVrlSwradOKmB521BXIxp0bkrxMZ7z5z6eOKTGnaiaXXT
# UOREEr4gDZ6pRND45Ul3CFohxbTPmJUaVLq5vMFpGbrPFvKDNzRusEEm3d5al08z
# jdSNd311RaGlWCZqA0Xe2VC1UIyvVr1MxeFGxSjTredDAHDezJieGYkD6tSRN+9N
# UvPJYCHEVkft2hFLjDLDiOZY4rbbPvlfsELWj+MXkdGqwFXjhr+sJyxB0JozSqg2
# 1Llyln6XeThIX8rC3D0y33XWNmdaifj2p8flTzU8AL2+nCpseQHc2kTmOt44Owde
# OVj0fHMxVaCAEcsUDH6uvP6k63llqmjWIso765qCNVcoFstp8jKastLYOrixRoZr
# uhf9xHdsFWyuq69zOuhJRrfVf8y2OMDY7Bz1tqG4QyzfTkx9HmhwwHcK1ALgXGC7
# KP845VJa1qwXIiNO9OzTF/tQa/8Hdx9xl0RBybhG02wyfFgvZ0dl5Rtztpn5aywG
# Ru9BHvDwX+Db2a2QgESvgBBBijCCB2cwggVPoAMCAQICEATd+82EVAN2YngfhA+f
# z/UwDQYJKoZIhvcNAQELBQAwaTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD
# ZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IENvZGUgU2ln
# bmluZyBSU0E0MDk2IFNIQTM4NCAyMDIxIENBMTAeFw0yMzEwMDQwMDAwMDBaFw0y
# NjExMTUyMzU5NTlaMG8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u
# MREwDwYDVQQHEwhCZWxsZXZ1ZTEbMBkGA1UEChMSUHVyZSBTdG9yYWdlLCBJbmMu
# MRswGQYDVQQDExJQdXJlIFN0b3JhZ2UsIEluYy4wggIiMA0GCSqGSIb3DQEBAQUA
# A4ICDwAwggIKAoICAQCdhXqOLFS3HR5KD2RtAOzGdwKU0mMGGHfU7qUo1YFvDCN8
# vF/X8LDhouGtsZPdIfd298orsXHfXYElTgBo91gba7SqKBWi9xdXTqMR5vpt41K/
# a554AgiQp02nfYwuspZoAGnt//mDJ6ErP1jUFiWuwHsYsxk0gFEayp5xIKzmj3q4
# 9g+AenKpktbDn6HPpXZPdvg+g+GR9lPpiJo7Z40SIqzaacJsVcl5MhPfbFdLeP1s
# n0MBW3BiYLyz4CEUq8IA2vJ2557N0uB0UzWERE31brL0mBn5gB1g8Zij9VsI9J5+
# Q+THKYIgwknlnXFiSwQhQbJ3Cn7IVotei1M/D011XjUR66kNHm02VVDsbxX92xLf
# qIX7BZ0e6shMsOFVakkdM00nXhfRscDkRqEQ+IwgC3vcyJgp/QRX0SfWaaD5G0fi
# ECMBZtmq5hijTJ18MAW2KaFePW0PIn9IRnoXS3tx9coXVJMTFwnLYdIukelF4jIW
# 779IP5lQH7IBNHS01BgysjWVaQhPYxWZYtsxyRUX3gVRjFChhOtBNCAy2S+YYjUS
# TOM7CdUNTtCARX/HgcRYxxU7UTOYXPYyabdQu3mFF8yD5YNkarlgc4TQ+H1PWnIU
# l7pq3P0ZSaE5Est24ApVi6wlZC/Q3jQRKPziRg8x7Zv1TZX8TfxPDmE0Nsd+BwID
# AQABo4ICAzCCAf8wHwYDVR0jBBgwFoAUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHQYD
# VR0OBBYEFCvH/lBQxrVtiuuihv+e6+2VgDPXMD4GA1UdIAQ3MDUwMwYGZ4EMAQQB
# MCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAOBgNV
# HQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwgbUGA1UdHwSBrTCBqjBT
# oFGgT4ZNaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0
# Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcmwwU6BRoE+GTWh0dHA6
# Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5n
# UlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMIGUBggrBgEFBQcBAQSBhzCBhDAkBggr
# BgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFwGCCsGAQUFBzAChlBo
# dHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2Rl
# U2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNydDAJBgNVHRMEAjAAMA0GCSqG
# SIb3DQEBCwUAA4ICAQCrjkZxw1B2w/pYu4siB36x5J9Xate13IDt57bxZvc7OGgz
# limeUq1/HObzW4Vej9tESBpT6a5FBuVSXQXvYQntQczEFBBksRXyad3tx5/xElHA
# LaE6BCZUtPKu3/CSrgsvVi7OgWNybOFWF2Xk9K1djImG55J7jOY+8ZKegUSlHPjB
# 8HF9G4VdS85L2SuFaRzOMTEIW+h0Ihkp6Js1rbe0YLZu/ad6VWFKoX++FDg3cbM8
# FLf482p+XCmuX/qZuFmySYDQQ4jvNiecEiyZ4m6HUryx9Fagc0NBADiOJc1R2p2I
# QbBasyndhn8KWlGSudJ+uCfuzD6ukGVe4kOpYlqkzVeOscetaY0/5v+896yP4FA8
# NS68I2eMuKbis2ouOIrAVkNPdymBjaEW1U6q979upeEG22UjjrRkq5qSdO+nk2tK
# NL1ZIc92bqIs132yuwVZ6A7Dvez03VSitT2UVBMz0BKNy1EnZ4hjqBrApU+Bbcwc
# 7nPV9hKKbEFKCcCNLpkAP8SCVX6r7qMyqYhAl+XKSfCkMpxRD2LykRup5mz54cQP
# RPoy86iVhFhWUez1O3t371sgYulMuxaff5mXK3xlzYZUHpJGkOYntQ2VlqUpl/VO
# KcNTXWnuPOyuUZY0b9tWU0Ofs8Imp7+lULJ7XUbrJoY1bUa22ce912PVBsWOojGC
# BjowggY2AgEBMH0waTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ
# bmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBS
# U0E0MDk2IFNIQTM4NCAyMDIxIENBMQIQBN37zYRUA3ZieB+ED5/P9TAJBgUrDgMC
# GgUAoHAwEAYKKwYBBAGCNwIBDDECMAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcC
# AQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYE
# FA2UOoTRiKvNZgWqk6UjWBxy7g4+MA0GCSqGSIb3DQEBAQUABIICAEZ0JzxVJQJE
# wnrX85gH6HlD4eREDgWZiH3FrfMgKwElZulpBzHvMl2Nzjm5LIIPSr+bOZeSLu7R
# QWsMrZdMni35AnH/bCkBnnGO2s8fvywFobAmeErNzqNpFENYPI2pJfEUBz3+L6BP
# l1IODpAR3rW9qG2e7mjXE8ksYn5J6cRc/1KpGxUWriRzkJzZuQ3vf4RrqApv31dh
# NoUjR/NbrBuXMV1YQCVchm0HWTIY8mQrO6lhWNs2OrhM1W16qmcrndX8aVId1Vc4
# dWXC8XkPAWYXt7nEF/rnraM6jhUYnFjwf8dmd5l8SGtCazH8ZsX7T+5PowN6zCnA
# g8KVTlkdYdIekS4zBQ1DTba9AvkpMNey1jMgcUk1vRn19LIzPHw34Y/AYmXSNMYT
# 6jFjAf879h3UgEP+4gY27qaLlRNrAaQeEY/Xg9ppibpXohFmey5/SpKjjchlkA8a
# 7M41h+6raZhkLEDJjLrhYlc6nw3Ya7G96vIrEphbgNxPIzQ9+5fRpSsaU78c8zbL
# kXEdWAkehKEMY/mI6hJg82e7MbAkGQP5fvaYsVQ0Fw9u7tvFMJOJxvduTil1xwUW
# ePO4B23+EsMW3IGhNcmQ/7fN+fB9gonqKU9d0GPTa3tuoPwCoTth7r5GgakXbMwV
# GDX8ONAoKy9DJi4gyfkGnBT8PE1VlvtcoYIDIDCCAxwGCSqGSIb3DQEJBjGCAw0w
# ggMJAgEBMHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMu
# MTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRp
# bWVTdGFtcGluZyBDQQIQBUSv85SdCDmmv9s/X+VhFjANBglghkgBZQMEAgEFAKBp
# MBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTI0MDEx
# MDAxMTM1NlowLwYJKoZIhvcNAQkEMSIEIHX3y0KmngSF/hkAF0+3qXmb0c+qJk2S
# fnsjEgAZCBo8MA0GCSqGSIb3DQEBAQUABIICAEHsh3wsTk+7xJunJ95LG//o2cf1
# EITH44PEk5qXvIYX9IwzulG4gYhNqJyycg8aw59adZKI8RmgJBkHiFLuXCTuZrXk
# D/9d47okNcMIB2EUtypyCS0T0TXvanp6xE+g/pQ/iU/4OF9OquFthoZaMgzRYY7D
# NZo/IjxathNQo8ka51MeUU0LtPGNA5IJQRyN6Aqepytl4ka7TBKyKfEUwkF+nPdy
# JBh/II5fGPlIZGTL7Bv8HC46466eLWf0TUwfeFaZPUzm9ExiQdKZZ//JWZUqEY0n
# SewasbdD+nhvt49QWGJAAzpc7ps+URW76VuRZyLY8AVxkxp4HSv2ITaT/MuRLWsY
# UaOWWkBkwCcgre9XLYsS925ah0g0ovsX9D/HjwAcCImY4DNVnjWj4TSg16YtdNc+
# o42bn8xy+pYxxvYLuMkTbF9eO5Pyn/NZEFUMBulfnf3S79e4uX0yQjCxTIA5oNY0
# 2IAzGNhyc6pLv/5QHHK2VDJp/IEByyx/s7NUtn+/yGZ13Fmqvbhoq2H+NYLgqTQ9
# tQxwH1z4xqOwo+Xj/wBCBzkdQcka7373XpSeEn8cBIcZsa7bps5jMDc71Gn4Pn2M
# ptTHK2vZ14kZzZEWz0rb1mNGMzJqNp+yihYEIU7rDN5xNzD/OstzCSifOi+4RcTZ
# e0QHDsBFLE/Ujr2/
# SIG # End signature block