Utils.VolumeSet.ps1

### Exported cmdlets on this file:
### New-PsbVolumeSet
### Get-PsbVolumeSet
### Remove-PsbVolumeSet
### Get-PsbVolumeSetProtectionGroup

function New-VolumeSetObject {
    Param (
        $Name,
        $Volumetype,
        $Computer,
        $Paths, 
        $vCenter,
        $VMId,
        $Resources
    )
    $objectProperty = [ordered]@{
        Name        = $Name
        Computer    = $Computer
        Volumetype  = $Volumetype
        Paths       = $Paths
        vCenter     = $vCenter
        VMId        = $VMId
        Resources     = $Resources
    }

    $pgObject = New-Object -TypeName psobject -Property $objectProperty
    return $pgObject
}

function ParseMetadataTags {
    Param (
        $Tags
    )

    $tagKey = $tags.Key | Sort-Object | Get-Unique
    if ($tagKey.Count -gt 1) {
        ThrowErrorCode -ErrorCode $ErrorCode_MixedVolSets
    }

    $volumeCount = $null
    $volumeType = $null
    $computer = $null
    $vcenter = $null
    $vmId = $null
    $paths = @()

    foreach ($metadata in $Tags.Value) {
        $metaParts = $metadata -split '#'
        if ($metaParts.Count -ne 6) {ThrowErrorCode -ErrorCode $ErrorCode_CantParseMetaTag -params @($metadataenv) }

        if(-not $volumeCount) {$volumeCount = $metaParts[0]}
        elseif ($volumeCount -ne $metaParts[0]) {ThrowErrorCode -ErrorCode $ErrorCode_InconsistentTags -params @("resource counts",$volumeCount,$metaParts[0])}

        if(-not $volumeType) {$volumeType = $metaParts[1]}
        elseif ($volumeType -ne $metaParts[1]) {ThrowErrorCode -ErrorCode $ErrorCode_InconsistentTags -params @("volume types",$volumeType,$metaParts[1])}
        
        if(-not $computer) {$computer = $metaParts[2]}
        elseif ($computer -ne $metaParts[2]) {ThrowErrorCode -ErrorCode $ErrorCode_InconsistentTags -params @("computers",$computer,$metaParts[2])}

        $paths += $metaParts[3]

        if(-not $vcenter) {$vcenter = $metaParts[4]}
        elseif ($vcenter -ne $metaParts[4]) {ThrowErrorCode -ErrorCode $ErrorCode_InconsistentTags -params @("vCenters",$vcenter,$metaParts[4])}

        if(-not $vmId) {$vmId = $metaParts[5]}
        elseif ($vmId -ne $metaParts[5]) {ThrowErrorCode -ErrorCode $ErrorCode_InconsistentTags -params @("VMs",$vmId,$metaParts[5])}
    }

    if ($volumeCount -ne $Tags.Resource.Count) {
        ThrowErrorCode -ErrorCode $ErrorCode_IncompleteVolSet -params @($volumeCount,$Tags.Resource.Name)
    }

    return New-VolumeSetObject -Name $tagKey -volumeType $volumeType -computer $computer -Paths $paths -vCenter $vCenter -Resources $Tags.Resource -VMId $vmId
}


function New-PgroupObject {
    Param (
        $Name,
        $ExtraVolumesCount,
        $Volumes
    )
    $objectProperty = [ordered]@{
        Name                = $Name
        ExtraVolumesCount   = $ExtraVolumesCount
        Volumes             = $Volumes
    }

    $pgObject = New-Object -TypeName psobject -Property $objectProperty
    return $pgObject
}

function Get-MatchingPgroups {
    Param (
        $VolumesNames,
        $RestClient
    )

    $FunctionName = $MyInvocation.MyCommand
    Write-Log -Level INFO -FunctionName $FunctionName -Msg ($script:EnteringMsg + $(Get-Parameter $MyInvocation))
    
    $matchingPGs = @()
    $VolumesNames = $VolumesNames | Sort-object | Get-Unique
    $FirstVolume = $VolumesNames | Select-object -first 1

    Write-Log -Level INFO -FunctionName $FunctionName -Msg "Volumes: $($VolumesNames)"
    Write-Log -Level INFO -FunctionName $FunctionName -Msg "Volume[0]: $FirstVolume"

    # get all connections (5 rest calls) - do we need paging?
    $pgVolConnections = Get-Pfa2ProtectionGroupVolume -Array $RestClient
    $pgHostConnections = Get-Pfa2ProtectionGroupHost -Array $RestClient
    $pgHGConnections =  Get-Pfa2ProtectionGroupHostGroup -Array $RestClient
    $volConnections =  Get-Pfa2Connection -Array $RestClient #-VolumeNames $VolumesNames
    $hgHostConnections = Get-Pfa2HostGroupHost -Array $RestClient

    Write-Log -Level INFO -FunctionName $FunctionName -Msg "Found connections:`n PG Volume Connections: $($pgVolConnections.Count
                                                                    )`n PG Host Connections: $($pgHostConnections.Count
                                                                    )`n PG HostGroup Connections: $($pgHGConnections.Count
                                                                    )`n Volume Connections: $($volConnections.Count
                                                                    )`n HG Host Connections:$($hgHostConnections.Count)"


    ##### get volume pgroup matches #####
    # get pgs directly connected to volume 1
    [string[]]$pgsVol1 = ($pgVolConnections | Where-Object {$_.Member.Name -eq $FirstVolume}).Group.Name | Where-object {$_ -ne $null}
    foreach ($pg in $pgsVol1) {
        # get volumes directly connected to the pg
        [string[]]$pgVols = ($pgVolConnections | Where-Object {$_.Group.Name -eq $pg}).Member.Name | Where-object {$_ -ne $null}
        # check if all required volumes are connected to pg
        $missingVols = Get-MissingItems -Items $VolumesNames -List $pgVols
        if ($missingVols.Count -eq 0) {
            $Volumes = ($pgVols | Sort-Object | Get-Unique)
            $ExtraVolumesCount = $Volumes.Count - $VolumesNames.Count
            $matchingPGs += New-PgroupObject -Name $pg -ExtraVolumesCount $ExtraVolumesCount -Volumes $Volumes
        }
    }


    ##### get host pgroup matches #####
    # get all hosts connected to vol1
    [string[]]$hostsVol1 = ($volConnections | where-object {$_.Volume.Name -eq $FirstVolume -and $_.Host.Name}).Host.Name | Where-object {$_ -ne $null}
    # get all pgs connected to vol1 through a host
    [string[]]$pgsVol1 = ($pgHostConnections | Where-Object {$_.Member.Name -in $hostsVol1}).Group.Name | Where-object {$_ -ne $null}
    foreach ($pg in $pgsVol1) {
        # get all hosts connected to the pg
        [string[]]$pgHosts = ($pgHostConnections | Where-Object {$_.Group.Name -eq $pg} ).Member.Name | Where-object {$_ -ne $null}
        # get all volumes connected to pg through a host
        [string[]]$pgVols = ($volConnections | where-object {$_.Host.Name -in $pgHosts}).Volume.Name | Where-object {$_ -ne $null}
        
        # check if all required volumes are connected to pg
        $missingVols = Get-MissingItems -Items $VolumesNames -List $pgVols
        if ($missingVols.Count -eq 0) {
            $Volumes = ($pgVols | Sort-Object | Get-Unique)
            $ExtraVolumesCount = $Volumes.Count - $VolumesNames.Count
            $matchingPGs += New-PgroupObject -Name $pg -ExtraVolumesCount $ExtraVolumesCount -Volumes $Volumes
        }
    }

    ##### get host group pgroup matches #####
    # get all host groups directly connected to vol1
    [string[]]$hgsVol1 = ($volConnections | where-object {$_.Volume.Name -eq $FirstVolume -and $_.HostGroup.Name}).HostGroup.Name | Where-object {$_ -ne $null}
    # get host groups connected to vol1 through a host
    $hgsVol1 += ($hgHostConnections | where-object {$_.Member.Name -in $hostsVol1}).Group.Name | Where-object {$_ -ne $null}
    # get all pgs connected to vol1 through a host group
    [string[]]$pgsVol1 = ($pgHGConnections | Where-Object {$_.Member.Name -in $hgsVol1}).Group.Name | Where-object {$_ -ne $null}
    foreach ($pg in $pgsVol1) {
        # get all host groups connected to the pg
        [string[]]$pgHGs = ($pgHGConnections | Where-Object {$_.Group.Name -eq $pg}).Member.Name | Where-object {$_ -ne $null}
        # get all hosts connected to pg through a hg
        [string[]]$HGHosts = ($hgHostConnections | where-object {$_.Group.Name -in $pgHGs}).Member.Name | Where-object {$_ -ne $null}

        # get all volumes connected to pg through a hg
        [string[]]$pgVols = ($volConnections | where-object {$_.HostGroup.Name -in $pgHGs}).Volume.Name | Where-object {$_ -ne $null}
        # get volumes connected to pg through a host in a hg
        $pgVols += ($volConnections | where-object {$_.Host.Name -in $HGHosts}).Volume.Name | Where-object {$_ -ne $null}

        # check if all required volumes are connected to pg
        $missingVols = Get-MissingItems -Items $VolumesNames -List $pgVols
        
        if ($missingVols.Count -eq 0) {
            $Volumes = ($pgVols | Sort-Object | Get-Unique)
            $ExtraVolumesCount = $Volumes.Count - $VolumesNames.Count
            $matchingPGs += New-PgroupObject -Name $pg -ExtraVolumesCount $ExtraVolumesCount -Volumes $Volumes
        }
    }

    return $matchingPGs | sort-object {$_.ExtraVolumesCount}
}

function Get-PgroupVolumes {
    Param (
        $PgroupName,
        $RestClient
    )

    $FunctionName = $MyInvocation.MyCommand
    Write-Log -Level INFO -FunctionName $FunctionName -Msg ($script:EnteringMsg + $(Get-Parameter $MyInvocation))

    # get all connections (5 rest calls) - do we need paging?
    $pgVolConnections = Get-Pfa2ProtectionGroupVolume -Array $RestClient -GroupNames $PgroupName
    $pgHostConnections = Get-Pfa2ProtectionGroupHost -Array $RestClient -GroupNames $PgroupName
    $pgHGConnections =  Get-Pfa2ProtectionGroupHostGroup -Array $RestClient -GroupNames $PgroupName
    
    if($pgHGConnections){
        $pgHostConnections += Get-Pfa2HostGroupHost -Array $RestClient -GroupNames $pgHGConnections.Member.Name
        $pgVolConnections +=  Get-Pfa2Connection -Array $RestClient -HostGroupNames $pgHGConnections.Member.Name
    }
    if($pgHostConnections){
        $pgVolConnections +=  Get-Pfa2Connection -Array $RestClient -HostNames $pgHostConnections.Member.Name
    }

    $volumes = $pgVolConnections.Member | Sort-Object | Get-Unique

    return $volumes
}


function Get-MissingItems {
    Param (
        $Items,
        $List
    )

    $missingItems = @()

    foreach ($item in $Items) {
        if($item -notin $List) {
            $missingItems += $item
        }
    }

    return $missingItems
}


function CheckSameListsOfUniqueElements {
    Param (
        $List1,
        $List2
    )

    if ($List1.Count -ne $List2.Count){
        return $false
    }

    $missingItems = Get-MissingItems -Items $List1 -List $List2

    return ($missingItems.Count -eq 0)
}
# SIG # Begin signature block
# MIIn+AYJKoZIhvcNAQcCoIIn6TCCJ+UCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUK5IpeiNSzIkdmZUR69C/qExm
# eSuggiEoMIIFjTCCBHWgAwIBAgIQDpsYjvnQLefv21DiCEAYWjANBgkqhkiG9w0B
# 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
# FLV5Xrh9/k0uBI1qZR4a9sCYFBfKMA0GCSqGSIb3DQEBAQUABIICADI7pv9g7xwD
# Xfbharg4w4/lGdrTkaKZ3uJaTimKEwFucqKRTTIfIgoJgy51ccFJggQUb0x7d7l6
# LqEPWWIUc5JBImXN/b1WnFpSEUYYVHkivjPR78wg91ANQ0klkhtPtQW3EoU4x21Q
# eErTkqT83J5jkKZhpIlyDf02oM4V+1H2RTg9IthfaCQwL920PiP80dGrChuoU7z9
# B4Zq09cr8/C+eDJ9FVrwNovZIzciUXeklz25sXxgL+eS+UZyvjxLgjmSs0T9Xhda
# taEMOT4S9FmG/By+TIIeDGlT0zeRZvZsNpVvLngxqus9wr5iHKB6CJ7+/A+bzmXC
# GJTCcBFTEMmtTmoNbBp5oiYIt1gf9gypTnsOswRZUjQ4RqYxwYCcxjxtWOkWzXyb
# AybxL+l4wwlW17C+p4Rb1ODMggA+UjatfoYLqyw3C/CCtTOpdOaCied3VdOCH07W
# lbqNDlmeND4/mo4286kb5pcRBkvWSG8LanZ/7PTLasUURrqO0psfR3DFoqoz+S87
# l+qrFHPkjkQ/OiN8g0Ra+CcDmrREi8DLYnUE77TB7uYfQ9QORLYp1jS+EbUQ0PvR
# Ob8erkRFGmzoEmImhH+Dr/RO5QOwWvt3Co5teSLwCWhImcZWV1kwQbZp9/1M+pDV
# anLqME789fARbO4GdWHkdpQW4LbXJ/d2oYIDIDCCAxwGCSqGSIb3DQEJBjGCAw0w
# ggMJAgEBMHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMu
# MTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRp
# bWVTdGFtcGluZyBDQQIQBUSv85SdCDmmv9s/X+VhFjANBglghkgBZQMEAgEFAKBp
# MBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTIzMTEx
# MDAwMjcwMFowLwYJKoZIhvcNAQkEMSIEILadNMN35Gzv8KoETSrV+V/ns08v2yQX
# nRnagkmqS5UMMA0GCSqGSIb3DQEBAQUABIICAFg9/6I+dMU09FylBg/l5Wb5UWTq
# R5JiX/exmUKxVmVS/gWgqQ3FFI8Q/5AsCpkD8Jvwxe/u/CpyPifKWU73VIr0YhVM
# X/azne8SVnGm3IuTdfXAbLqokfQpdUDnpy0oXCrGI3ZyjxDgLe6MBns1P8ZMgVRr
# hXeRACbKPl6e5qoIHySFt6+171H1KmfU3o15zxLecKekp140Ky4vDzRlzZtz5ze5
# pVtmYHX2NhLNG82Tys4wmqWuXFTZUYcSMGRjgVqAkKEHzgOeuwGJBYzpI8cgVz76
# PmlCF3qQEpoNZExrsuejCdG99C9pmoPrXdsKorpUIHip5KX4I3XJ0OWupHuZ5enu
# 2Gl0Es0xwsafypvG1sisb8Ek1peTQJB+bYSU2Im5fKvcQnK26qDiLSs/KD4TvpJI
# ilbWnKaV+b6NxxHQK5xm2A3mc7UHjtW4V0NergUwXbc1WgROxjzWKvBp6IEQrFQz
# 8+6eZNlLL/0BQW0OK7iNkm4JkGBKnOdRQz4eKVhSkHhgo0/t3ZjrCqmxLiO9XTw3
# 909NCMBXbqtT/J4EnN31jhvdA54DPCbAuqyoBgs5B/3gsEecksV+IKnuIDumeJ/h
# xwLPuwc44kl+EaJPqBbqhAcJBaJBEgdP1R9TESjUOC3zMRvw6UEzrwD45DMKP0hW
# b3p66UoU3umuQkCL
# SIG # End signature block