PureStorage.CBS.AVS.VMFS.ps1

function Get-PCBSVolumeFromVMFSDataStore {
    Param(
        [Parameter(mandatory=$true)]
        $FlashArray,

        [Parameter(mandatory=$true)]
        $DataStore
    )
    $Lun = $Datastore.ExtensionData.Info.Vmfs.Extent.DiskName |select-object -unique
    if ($Lun -like 'naa.624a9370*')
    {
        $VolSerial = ($lun.ToUpper()).substring(12)
        $PureVol = Get-Pfa2Volume -Filter "Serial='$VolSerial'"
    }
    else
    {
        throw "This VMFS is not hosted on FlashArray storage."
    }

    return $PureVol
}

function Remove-PfaVmfsDatastore {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true)]
        [String]$ClusterName,

        [Parameter(Mandatory=$true)]
        [String]$DatastoreName,

        [Parameter(ValueFromPipeline=$True)]
        $FlashArray
    )

    $Cluster = Get-Cluster -Name $ClusterName -ErrorAction Ignore
    if (-not $Cluster) {
        throw "Cluster $ClusterName does not exist."
    }

    $Datastore = Get-Datastore -Name $DatastoreName -ErrorAction Ignore
    $PureVol = Get-PCBSVolumeFromVMFSDataStore -FlashArray $FlashArray -Datastore $Datastore

    $Lun = $Datastore.ExtensionData.Info.Vmfs.Extent.DiskName |select-object -unique
    if (-not $Datastore) {
        throw "Datastore ($DatastoreName) does not exist."

    }
    if ("VMFS" -ne $Datastore.Type) {
        throw "Datastore $DatastoreName is of type $($Datastore.Type). This cmdlet can only process VMFS datastores"
    }

    # Remove datastore and its content from vcenter
    $VMHosts = $Cluster | Get-VMHost
    ForEach ($VMHost in $VMHosts) {
        $DatastoreConnectedToHost = Get-Datastore -VMHost $VMHost | Where-Object {$_.name -eq $DatastoreName}
        if ($DatastoreConnectedToHost -ne $null) {
            Write-Host "Removing datastore $DatastoreName..."
            # Remove-Datastore will remove the contents of the datastore so we just need to find a host connected to datastore and remove only once
            $VMs = $Datastore | Get-VM
            if ($VMs -and $VMs.Count -gt 0) {
                $vmNames = $VMs |  Join-String -SingleQuote -Property {$_.Name}  -Separator ", "
                Write-Error "Cannot remove datastore $DatastoreName. It is already in use by $vmNames"
                throw
            }
            Remove-Datastore -Datastore $Datastore -VMHost $VMHost -Confirm:$false -ErrorAction Stop
            $VMHostForDatastore = $VMHost
            break
        }
    }

    # Remove volume from array hostgroup if available
    $FaHg = Get-PCBSHostGroupfromVcCluster -Flasharray $Flasharray -Cluster $Cluster
    $HgConnection = Get-Pfa2Connection -VolumeNames $PureVol.Name -HostGroupNames $FaHg.Name -ErrorAction SilentlyContinue
    if ($HgConnection -ne $null) {
        Write-Host "Removing volume $($PureVol.Name) from host group $($FaHg.Name)..."
        Remove-Pfa2Connection -VolumeNames $PureVol.Name -HostGroupNames $FaHg.Name
    }

    # Remove volume from array host if available
    $FaHosts = Get-PCBSHostFromVmHost -Flasharray $Flasharray -esxi $VMHostForDatastore
    foreach ($FaHost in $FaHosts) {
        $HostConnection = Get-Pfa2Connection -VolumeNames $PureVol.Name -HostNames $FaHost.Name -ErrorAction SilentlyContinue
        if ($HostConnection -ne $null) {
            Write-Host "Removing volume $($PureVol.Name) from host $($FaHost.Name)..."
            Remove-Pfa2Connection -VolumeNames $PureVol.Name -HostNames $FaHost.Name
        }
    }

    # Destroy volume if there is no host/hg connections for the volume
    $Connections = Get-Pfa2Connection -VolumeNames $PureVol.Name
    if ($Connections -eq $null) {
        Write-Host "Destroying volume $($PureVol.Name)..."
        Remove-Pfa2Volume -Name $PureVol.Name
    }

}

function New-PfaVmfsFromSnapshot {
    <#
    .SYNOPSIS
      Mounts a copy of a VMFS datastore to a VMware cluster from a FlashArray snapshot.
    .DESCRIPTION
      Takes in a snapshot name, the corresponding FlashArray, and a cluster. The VMFS copy will be resignatured and mounted.
    .INPUTS
      FlashArray connection, a snapshotName, and a cluster.
    .OUTPUTS
      Returns the new datastore.
    #>


    [CmdletBinding()]
    Param(
            [Parameter(Mandatory=$true,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.Inventory.Cluster]$cluster,

            [Parameter(ValueFromPipeline=$True)]
            $FlashArray,

            [Parameter(Mandatory=$true)]
            [string]$snapName,

            [Parameter(Mandatory=$false)]
            [string]$volumeName
    )
    $volumeSnapshot = Get-Pfa2VolumeSnapshot -Name $snapName -ErrorAction Ignore
    if (-not $volumeSnapshot) {
        throw "Could not find snapshot $snapName."
    }

    if ([string]::IsNullOrWhiteSpace($volumeName))
    {
      $volumeName = $volumeSnapshot.Source.Name + "-"+(Get-Random -Minimum 10000 -Maximum 99999)
    }
    Write-Host "Creating a volume $volumeName..."
    $newVol = New-Pfa2Volume -Array $FlashArray -Name $volumeName -SourceName $snapName
    $hostGroup = Get-PCBSHostGroupfromVcCluster -cluster $cluster -FlashArray $FlashArray
    Write-Host "Connecting volume $volumeName to host group $($hostGroup.Name)..."
    New-Pfa2Connection -VolumeName $volumeName -HostGroupName $hostGroup.name

    $esxi = $cluster | Get-VMHost| where-object {($_.ConnectionState -eq 'Connected')} |Select-Object -last 1
    Write-Host "Rescanning HBA..."
    $esxi | Get-VMHostStorage -RescanAllHba -RescanVMFS -ErrorAction stop |Out-Null
    $hostStorage = get-view -ID $esxi.ExtensionData.ConfigManager.StorageSystem
    $resigVolumes= $hostStorage.QueryUnresolvedVmfsVolume()
    $newNAA =  "naa.624a9370" + $newVol.serial.toLower()
    $deleteVol = $false
    foreach ($resigVolume in $resigVolumes)
    {
        if ($deleteVol -eq $true)
        {
            break
        }
        foreach ($resigExtent in $resigVolume.Extent)
        {
            if ($resigExtent.Device.DiskName -eq $newNAA)
            {
                if ($resigVolume.ResolveStatus.Resolvable -eq $false)
                {
                    if ($resigVolume.ResolveStatus.MultipleCopies -eq $true)
                    {
                        Write-Error "The volume cannot be resignatured as more than one unresignatured copy is present. Deleting and ending."
                        Write-Error "The following volume(s) are presented and need to be removed/resignatured first:"
                        $resigVolume.Extent.Device.DiskName |where-object {$_ -ne $newNAA}
                    }
                    $deleteVol = $true
                    break
                }
                else {
                    $volToResignature = $resigVolume
                    break
                }
            }
        }
    }
    if (($null -eq $volToResignature) -and ($deleteVol -eq $false))
    {
        Write-Error "No unresolved volume found on the created volume. Deleting and ending."
        $deleteVol = $true
    }
    if ($deleteVol -eq $true)
    {
        Write-Host "Removing volume $($newVol.Name)..."
        Remove-Pfa2Connection -Array $FlashArray -HostgroupNames $hostGroup.name -VolumeNames $newVol.name -ErrorAction SilentlyContinue
        Remove-Pfa2Volume  -Array $FlashArray -Name $newVol.name -ErrorAction SilentlyContinue
        Remove-Pfa2Volume  -Array $FlashArray -Name $newVol.name -Eradicate -ErrorAction SilentlyContinue
        throw "Failed to create datastore $volumeName from snapshot $snapName"
    }
    Write-Host "Starting resignature for $volumeName..."
    $esxcli=get-esxcli -VMHost $esxi -v2 -ErrorAction stop
    $resigOp = $esxcli.storage.vmfs.snapshot.resignature.createargs()
    $resigOp.volumelabel = $volToResignature.VmfsLabel
    $esxcli.storage.vmfs.snapshot.resignature.invoke($resigOp) |out-null
    Start-sleep -s 5
    $esxi |  Get-VMHostStorage -RescanVMFS -ErrorAction stop |Out-Null
    $datastores = $esxi| Get-Datastore -ErrorAction stop
    foreach ($ds in $datastores)
    {
        $naa = $ds.ExtensionData.Info.Vmfs.Extent.DiskName
        if ($naa -eq $newNAA)
        {
            $resigds = $ds | Set-Datastore -Name $newVol.name -ErrorAction stop
            return $resigds
        }
    }
  }

function New-PfaVolumeByPrefix {
    Param (
        [Parameter(Mandatory=$true)]
        [string] $Name,


        [ValidateRange(1073741824,68169720922112)] # 1 GB to 62 TB
        [Parameter(Mandatory=$true)]
        [string] $Size,

        [Parameter(Mandatory=$True)]
        $Flasharray
    )
    # Create a volume with the specified name. If the name already exists in the array, use it aas a prefix
    $maxTries = 20
    $count = 0
    $existingVolume = $null
    $volName = $Name
    $existingVolume = Get-Pfa2Volume -Array $Flasharray  -Name $volName -ErrorAction SilentlyContinue
    while ($existingVolume -and $count -lt $maxTries) {
        $count = $count + 1
        $volName = "$Name-$count"
        $existingVolume = Get-Pfa2Volume -Array $Flasharray  -Name $volName -ErrorAction SilentlyContinue
    }
    if ($existingVolume) {
        # We exhausted the max number of tries
        throw "Could note create a volume based on the Name '$Name', please select a unique volume name"
    }
    Write-Host "Creating volume $volName..."
    $vol = New-Pfa2Volume -Array $FlashArray -Name $volName -Provisioned $Size -ErrorAction Stop
    return $vol

}
function New-PfaVmfs {
    <#
    .SYNOPSIS
      Create a new VMFS on a new FlashArray volume
    .DESCRIPTION
      Creates a new FlashArray-based VMFS and presents it to a cluster.
    .INPUTS
      FlashArray connection, a vCenter cluster, a volume size, and name.
    .OUTPUTS
      Returns a VMFS object.
    #>


    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true)]
        [VMware.VimAutomation.ViCore.Types.V1.Inventory.Cluster]$Cluster,

        [Parameter(Mandatory=$True)]
        $Flasharray,

        [Parameter(Mandatory=$true)]
        [string]$Name,

        [ValidateRange(1073741824,68169720922112)] # 1 GB to 62 TB
        [Parameter(Mandatory=$true)]
        [UInt64]$Size
    )

    $datastore = Get-Datastore -Name $Name -ErrorAction Ignore
    if ($datastore) {
        throw "Cannot create a datastore. Datastore '$Name' already exists!."
    }
    $hostGroup = $hostGroup = Get-PCBSHostGroupfromVcCluster -cluster $cluster -FlashArray $FlashArray -ErrorAction Stop
    $newVol = New-PfaVolumeByPrefix -FlashArray $FlashArray -Name $Name -Size $Size -ErrorAction Stop
    New-Pfa2Connection -Array $FlashArray -HostGroupNames $hostGroup.Name -VolumeNames $newVol.Name
    $newNAA = "naa.624a9370" + $newVol.serial.toLower()

    $esxi = $cluster | get-vmhost | where-object { ($_.ConnectionState -eq 'Connected') } | Select-Object -last 1
    $cluster | Get-VMHost | Get-VMHostStorage -RescanAllHba | Out-Null

    Write-Debug -Message "NAA for datastore $Name is $newNAA"
    try {
        Write-Host "Creating datastore $Name ..."
        $newVMFS = $esxi | new-datastore -name $Name -vmfs -Path $newNAA -FileSystemVersion 6 -ErrorAction Stop
    }
    catch {
        Write-Error $Global:Error[0]
        # Cleanup
        Remove-Pfa2Connection -Array $Flasharray -HostGroupNames $hostGroup.Name -VolumeNames $newVol.Name
        Remove-Pfa2Volume -Array $Flasharray -Name $newVol.Name | Out-Null
        Remove-Pfa2Volume -Array $Flasharray -Name $newVol.Name -Eradicate| Out-Null
    }

    return $newVMFS
}


function Set-PfaVmfsCapacity {
    [CmdletBinding()]
    Param(
        [Parameter(Position=0,ValueFromPipeline=$True)]
        $Flasharray,

        [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)]
        [string]$DatastoreName,

        [Parameter(mandatory=$false)]
        [UInt64]$SizeInByte
    )

    $Datastore = Get-Datastore -Name $DatastoreName
    $pureVol = Get-PCBSVolumeFromVMFSDataStore -Flasharray $FlashArray -Datastore $Datastore
    $Size = Get-FriendlySize -SizeInByte $SizeInByte
    $CurrentSize = Get-FriendlySize -SizeInByte $pureVol.Provisioned
    if ($SizeInByte -le $pureVol.Provisioned)
    {
        throw "The new specified size, $Size, is not larger than the current size, $CurrentSize. ESXi does not permit VMFS volumes to be shrunk--please specify a size larger than the existing."
    }
    Write-Host "Increasing the size of the volume $($pureVol.Name) to $Size on Pure Cloud Block Store..."

    # FlashArray only accept byte that is multiple of 512. If not, we round up by 512
    $SizeInByte = [math]::ceiling($SizeInByte/512) * 512

    Update-Pfa2Volume -Name $pureVol.Name -Provisioned $SizeInByte | Out-Null
    ForEach ($DsHost in $Datastore.ExtensionData.Host.Key)
    {
      #had to change this as get-vmhost -datastore spits out a deprecation error.
      Get-VMHost -id "HostSystem-$($dsHost.value)" | Get-VMHostStorage -RescanAllHba -RescanVmfs -ErrorAction Stop  -WarningAction SilentlyContinue |Out-Null
    }
    $ESXiView = Get-View -Id ($Datastore.ExtensionData.Host |Select-Object -last 1 | Select-Object -ExpandProperty Key)
    $DatastoreSystem = Get-View -Id $esxiView.ConfigManager.DatastoreSystem
    $ExpandOptions = $datastoreSystem.QueryVmfsDatastoreExpandOptions($datastore.ExtensionData.MoRef)
    Write-Host "Increasing the size of the datastore $DatastoreName to $Size..."
    $ExpandedDS = $datastoreSystem.ExpandVmfsDatastore($datastore.ExtensionData.MoRef,$expandOptions[0].spec)
}

function Get-FriendlySize {
    [CmdletBinding()]
    Param(
        [Parameter(mandatory=$false)]
        [UInt64]$SizeInByte
    )

    $SizeInGB = $SizeInByte / 1024 / 1024 / 1024

    if ($SizeInGB -ge 1024) {
        return "$($SizeInGB/1024) TB"
    } else {
        return "$SizeInGB GB"
    }


}

# SIG # Begin signature block
# MIIjUAYJKoZIhvcNAQcCoIIjQTCCIz0CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUrvLuQ+w1GJdUUFimEbshTIFM
# bReggh12MIIFMDCCBBigAwIBAgIQBAkYG1/Vu2Z1U0O1b5VQCDANBgkqhkiG9w0B
# AQsFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD
# VQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVk
# IElEIFJvb3QgQ0EwHhcNMTMxMDIyMTIwMDAwWhcNMjgxMDIyMTIwMDAwWjByMQsw
# CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
# ZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQg
# Q29kZSBTaWduaW5nIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
# +NOzHH8OEa9ndwfTCzFJGc/Q+0WZsTrbRPV/5aid2zLXcep2nQUut4/6kkPApfmJ
# 1DcZ17aq8JyGpdglrA55KDp+6dFn08b7KSfH03sjlOSRI5aQd4L5oYQjZhJUM1B0
# sSgmuyRpwsJS8hRniolF1C2ho+mILCCVrhxKhwjfDPXiTWAYvqrEsq5wMWYzcT6s
# cKKrzn/pfMuSoeU7MRzP6vIK5Fe7SrXpdOYr/mzLfnQ5Ng2Q7+S1TqSp6moKq4Tz
# rGdOtcT3jNEgJSPrCGQ+UpbB8g8S9MWOD8Gi6CxR93O8vYWxYoNzQYIH5DiLanMg
# 0A9kczyen6Yzqf0Z3yWT0QIDAQABo4IBzTCCAckwEgYDVR0TAQH/BAgwBgEB/wIB
# ADAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwMweQYIKwYBBQUH
# AQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYI
# KwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFz
# c3VyZWRJRFJvb3RDQS5jcnQwgYEGA1UdHwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmw0
# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaG
# NGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RD
# QS5jcmwwTwYDVR0gBEgwRjA4BgpghkgBhv1sAAIEMCowKAYIKwYBBQUHAgEWHGh0
# dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCgYIYIZIAYb9bAMwHQYDVR0OBBYE
# FFrEuXsqCqOl6nEDwGD5LfZldQ5YMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6en
# IZ3zbcgPMA0GCSqGSIb3DQEBCwUAA4IBAQA+7A1aJLPzItEVyCx8JSl2qB1dHC06
# GsTvMGHXfgtg/cM9D8Svi/3vKt8gVTew4fbRknUPUbRupY5a4l4kgU4QpO4/cY5j
# DhNLrddfRHnzNhQGivecRk5c/5CxGwcOkRX7uq+1UcKNJK4kxscnKqEpKBo6cSgC
# PC6Ro8AlEeKcFEehemhor5unXCBc2XGxDI+7qPjFEmifz0DLQESlE/DmZAwlCEIy
# sjaKJAL+L3J+HNdJRZboWR3p+nRka7LrZkPas7CM1ekN3fYBIM6ZMWM9CBoYs4Gb
# T8aTEAb8B4H6i9r5gkn3Ym6hU/oSlBiFLpKR6mhsRDKyZqHnGKSaZFHvMIIFNzCC
# BB+gAwIBAgIQC4jZOitkx57ksuMgsWXX0jANBgkqhkiG9w0BAQsFADByMQswCQYD
# VQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGln
# aWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgQ29k
# ZSBTaWduaW5nIENBMB4XDTIwMDczMDAwMDAwMFoXDTIzMTAwNDEyMDAwMFowdDEL
# MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDU1vdW50
# YWluIFZpZXcxGzAZBgNVBAoTElB1cmUgU3RvcmFnZSwgSW5jLjEbMBkGA1UEAxMS
# UHVyZSBTdG9yYWdlLCBJbmMuMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
# AQEA6nefE6+A0nNMY82xtkQb+akwI0oxLqEbRY65bE4re+CVQV2xP89/7FIAXooq
# jxNOvrtWicWTGOZjBdAFEXXAUEyu9CkWFOXGLV3/QkcEfY3e3Z3jypa6h1EznSEp
# 3wSQEIVbigi6jR2s7NDnDoSDAKnzcGcSZ8Nz7akXFrN8PAmg3gy8a/rwm2Ko7ClR
# ZUHj1C/OMPXUqiN0Q4FyAsaeFmvg2PX2twxo192WRdNro1dkKNfmDvym2ss6MXcq
# gFEjBcZtHDv3e/i0BjT24Jm1C27IVYZzVf28dqmVfPo7l8bLEHsVKgrRf0VzS2wI
# R08gXiMrohf9tv3AbvtPk7ZaawIDAQABo4IBxTCCAcEwHwYDVR0jBBgwFoAUWsS5
# eyoKo6XqcQPAYPkt9mV1DlgwHQYDVR0OBBYEFJOwFn7S4Lj6QdNnH6tpm3FMFWJI
# MA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzB3BgNVHR8EcDBu
# MDWgM6Axhi9odHRwOi8vY3JsMy5kaWdpY2VydC5jb20vc2hhMi1hc3N1cmVkLWNz
# LWcxLmNybDA1oDOgMYYvaHR0cDovL2NybDQuZGlnaWNlcnQuY29tL3NoYTItYXNz
# dXJlZC1jcy1nMS5jcmwwTAYDVR0gBEUwQzA3BglghkgBhv1sAwEwKjAoBggrBgEF
# BQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAIBgZngQwBBAEwgYQG
# CCsGAQUFBwEBBHgwdjAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQu
# Y29tME4GCCsGAQUFBzAChkJodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGln
# aUNlcnRTSEEyQXNzdXJlZElEQ29kZVNpZ25pbmdDQS5jcnQwDAYDVR0TAQH/BAIw
# ADANBgkqhkiG9w0BAQsFAAOCAQEAMVAa7mXxhBHf0dTzf6LKDncN8KnzB59KBvd0
# KvXZc6FoLHGi2Wg6XBSP+9mdDMMYOkohqstSk7RD+reT8xiptrIkSMcVcTog1Z3e
# JjYTK8B7QsSpuu2lo0RWA5rdvqMJ+lVzbbjteTq+uicP4T/EDwv2q+iPAgpXQD8y
# r084ExDWJtMfhvy0cxh555xx88rvFWOhJnXYiFtjaO9dp7f2TnZRJ44rmB98jc9E
# BR/8GLOi/BhyPiiU4nBv8JIHVP1E5zIt8/9PhfpenmiWBbuuP0YLnzrqRhswtJaq
# jJirYNLYojmINrbvdcpEKGK1AitsnuOjFadLI7bc696Y35lUATCCBY0wggR1oAMC
# AQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZIhvcNAQEMBQAwZTELMAkGA1UEBhMC
# VVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0
# LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJRCBSb290IENBMB4XDTIy
# MDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjELMAkGA1UEBhMCVVMxFTATBgNV
# BAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8G
# A1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MIICIjANBgkqhkiG9w0BAQEF
# AAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBpM+zCpyUuySE98orYWcLhKac9
# WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR0uRf1gU8Ug9SH8aeFaV+vp+p
# VxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0O21x4i0MG+4g1ckgHWMpLc7s
# Xk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53yEioZldXn1RYjgwrt0+nMNlW
# 7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4x7axxLVqGDgDEI3Y1DekLgV9
# iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3VdeGbZOjFEmjNAvwjXWkmkwuap
# oGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1CdoeJl2l6SPDgohIbZpp0yt5L
# HucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJhbesz2cXfSwQAzH0clcOP9yGy
# shG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz0YkH4b235kOkGLimdwHhD5QM
# IR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNBERJb5RBQ6zHFynIWIgnffEx1
# P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+CiaZ9eRpL5gdLfXZqbId5RsC
# AwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOzX44LScV1k
# TN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMA4G
# A1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6
# Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2NhY2VydHMu
# ZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDBFBgNVHR8E
# PjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1
# cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgwBgYEVR0gADANBgkqhkiG9w0BAQwF
# AAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cHvZqsoYcs7IVeqRq7IviHGmlUIu2k
# iHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8UgPITtAq3votVs/59PesMHqai7Je
# 1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTnf+hZqPC/Lwum6fI0POz3A8eHqNJM
# QBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxUjG/voVA9/HYJaISfb8rbII01YBwC
# A8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8jLfR+cWojayL/ErhULSd+2DrZ8LaH
# lv1b0VysGMNNn3O3AamfV6peKOK5lDCCBq4wggSWoAMCAQICEAc2N7ckVHzYR6z9
# KGYqXlswDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERp
# Z2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMY
# RGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTIyMDMyMzAwMDAwMFoXDTM3MDMy
# MjIzNTk1OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMu
# MTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRp
# bWVTdGFtcGluZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMaG
# NQZJs8E9cklRVcclA8TykTepl1Gh1tKD0Z5Mom2gsMyD+Vr2EaFEFUJfpIjzaPp9
# 85yJC3+dH54PMx9QEwsmc5Zt+FeoAn39Q7SE2hHxc7Gz7iuAhIoiGN/r2j3EF3+r
# GSs+QtxnjupRPfDWVtTnKC3r07G1decfBmWNlCnT2exp39mQh0YAe9tEQYncfGpX
# evA3eZ9drMvohGS0UvJ2R/dhgxndX7RUCyFobjchu0CsX7LeSn3O9TkSZ+8OpWNs
# 5KbFHc02DVzV5huowWR0QKfAcsW6Th+xtVhNef7Xj3OTrCw54qVI1vCwMROpVymW
# Jy71h6aPTnYVVSZwmCZ/oBpHIEPjQ2OAe3VuJyWQmDo4EbP29p7mO1vsgd4iFNmC
# KseSv6De4z6ic/rnH1pslPJSlRErWHRAKKtzQ87fSqEcazjFKfPKqpZzQmiftkaz
# nTqj1QPgv/CiPMpC3BhIfxQ0z9JMq++bPf4OuGQq+nUoJEHtQr8FnGZJUlD0UfM2
# SU2LINIsVzV5K6jzRWC8I41Y99xh3pP+OcD5sjClTNfpmEpYPtMDiP6zj9NeS3YS
# UZPJjAw7W4oiqMEmCPkUEBIDfV8ju2TjY+Cm4T72wnSyPx4JduyrXUZ14mCjWAkB
# KAAOhFTuzuldyF4wEr1GnrXTdrnSDmuZDNIztM2xAgMBAAGjggFdMIIBWTASBgNV
# HRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBS6FtltTYUvcyl2mi91jGogj57IbzAf
# BgNVHSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8EBAMCAYYw
# EwYDVR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsGAQUFBzAB
# hhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0dHA6Ly9j
# YWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3J0MEMG
# A1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2Vy
# dFRydXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG
# /WwHATANBgkqhkiG9w0BAQsFAAOCAgEAfVmOwJO2b5ipRCIBfmbW2CFC4bAYLhBN
# E88wU86/GPvHUF3iSyn7cIoNqilp/GnBzx0H6T5gyNgL5Vxb122H+oQgJTQxZ822
# EpZvxFBMYh0MCIKoFr2pVs8Vc40BIiXOlWk/R3f7cnQU1/+rT4osequFzUNf7WC2
# qk+RZp4snuCKrOX9jLxkJodskr2dfNBwCnzvqLx1T7pa96kQsl3p/yhUifDVinF2
# ZdrM8HKjI/rAJ4JErpknG6skHibBt94q6/aesXmZgaNWhqsKRcnfxI2g55j7+6ad
# cq/Ex8HBanHZxhOACcS2n82HhyS7T6NJuXdmkfFynOlLAlKnN36TU6w7HQhJD5TN
# OXrd/yVjmScsPT9rp/Fmw0HNT7ZAmyEhQNC3EyTN3B14OuSereU0cZLXJmvkOHOr
# pgFPvT87eK1MrfvElXvtCl8zOYdBeHo46Zzh3SP9HSjTx/no8Zhf+yvYfvJGnXUs
# HicsJttvFXseGYs2uJPU5vIXmVnKcPA3v5gA3yAWTyf7YGcWoWa63VXAOimGsJig
# K+2VQbc61RWYMbRiCQ8KvYHZE/6/pNHzV9m8BPqC3jLfBInwAM1dwvnQI38AC+R2
# AibZ8GV2QqYphwlHK+Z/GqSFD/yYlvZVVCsfgPrA8g4r5db7qS9EFUrnEw4d2zc4
# GqEr9u3WfPwwggbAMIIEqKADAgECAhAMTWlyS5T6PCpKPSkHgD1aMA0GCSqGSIb3
# DQEBCwUAMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7
# MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1l
# U3RhbXBpbmcgQ0EwHhcNMjIwOTIxMDAwMDAwWhcNMzMxMTIxMjM1OTU5WjBGMQsw
# CQYDVQQGEwJVUzERMA8GA1UEChMIRGlnaUNlcnQxJDAiBgNVBAMTG0RpZ2lDZXJ0
# IFRpbWVzdGFtcCAyMDIyIC0gMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
# ggIBAM/spSY6xqnya7uNwQ2a26HoFIV0MxomrNAcVR4eNm28klUMYfSdCXc9FZYI
# L2tkpP0GgxbXkZI4HDEClvtysZc6Va8z7GGK6aYo25BjXL2JU+A6LYyHQq4mpOS7
# eHi5ehbhVsbAumRTuyoW51BIu4hpDIjG8b7gL307scpTjUCDHufLckkoHkyAHoVW
# 54Xt8mG8qjoHffarbuVm3eJc9S/tjdRNlYRo44DLannR0hCRRinrPibytIzNTLlm
# yLuqUDgN5YyUXRlav/V7QG5vFqianJVHhoV5PgxeZowaCiS+nKrSnLb3T254xCg/
# oxwPUAY3ugjZNaa1Htp4WB056PhMkRCWfk3h3cKtpX74LRsf7CtGGKMZ9jn39cFP
# cS6JAxGiS7uYv/pP5Hs27wZE5FX/NurlfDHn88JSxOYWe1p+pSVz28BqmSEtY+VZ
# 9U0vkB8nt9KrFOU4ZodRCGv7U0M50GT6Vs/g9ArmFG1keLuY/ZTDcyHzL8IuINeB
# rNPxB9ThvdldS24xlCmL5kGkZZTAWOXlLimQprdhZPrZIGwYUWC6poEPCSVT8b87
# 6asHDmoHOWIZydaFfxPZjXnPYsXs4Xu5zGcTB5rBeO3GiMiwbjJ5xwtZg43G7vUs
# fHuOy2SJ8bHEuOdTXl9V0n0ZKVkDTvpd6kVzHIR+187i1Dp3AgMBAAGjggGLMIIB
# hzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggr
# BgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEwHwYDVR0j
# BBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYEFGKK3tBh/I8xFO2X
# C809KpQU31KcMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0
# LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3RhbXBpbmdD
# QS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzABhhhodHRwOi8vb2Nz
# cC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNlcnRzLmRpZ2lj
# ZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3RhbXBp
# bmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAFWqKhrzRvN4Vzcw/HXjT9aFI/H8
# +ZU5myXm93KKmMN31GT8Ffs2wklRLHiIY1UJRjkA/GnUypsp+6M/wMkAmxMdsJiJ
# 3HjyzXyFzVOdr2LiYWajFCpFh0qYQitQ/Bu1nggwCfrkLdcJiXn5CeaIzn0buGqi
# m8FTYAnoo7id160fHLjsmEHw9g6A++T/350Qp+sAul9Kjxo6UrTqvwlJFTU2WZoP
# VNKyG39+XgmtdlSKdG3K0gVnK3br/5iyJpU4GYhEFOUKWaJr5yI+RCHSPxzAm+18
# SLLYkgyRTzxmlK9dAlPrnuKe5NMfhgFknADC6Vp0dQ094XmIvxwBl8kZI4DXNlpf
# lhaxYwzGRkA7zl011Fk+Q5oYrsPJy8P7mxNfarXH4PMFw1nfJ2Ir3kHJU7n/NBBn
# 9iYymHv+XEKUgZSCnawKi8ZLFUrTmJBFYDOA4CPe+AOk9kVH5c64A0JH6EE2cXet
# /aLol3ROLtoeHYxayB6a1cLwxiKoT5u92ByaUcQvmvZfpyeXupYuhVfAYOd4Vn9q
# 78KVmksRAsiCnMkaBXy6cbVOepls9Oie1FqYyJ+/jbsYXEP10Cro4mLueATbvdH7
# WwqocH7wl4R44wgDXUcsY6glOJcB0j862uXl9uab3H4szP8XTE0AotjWAQ64i+7m
# 4HJViSwnGWH2dwGMMYIFRDCCBUACAQEwgYYwcjELMAkGA1UEBhMCVVMxFTATBgNV
# BAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTExMC8G
# A1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUgU2lnbmluZyBDQQIQ
# C4jZOitkx57ksuMgsWXX0jAJBgUrDgMCGgUAoHAwEAYKKwYBBAGCNwIBDDECMAAw
# GQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisG
# AQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFLbJ4V2bgGTNJaApkgxHOim2l2wiMA0G
# CSqGSIb3DQEBAQUABIIBAJoVp7g/9LsGG3xKYscsAsxrBELm7sPrJ4hd6qOZmnMW
# 8rzxYMYd2VVWrdcR8mot8xtOTvC8NHH2Up/nPJzpYIetk1YJVyu5Y5sI6YQtLoUG
# zyP0qdqfPQ4Dm+JQMf8kTn5cW/e84BcIaSLyBoqEalW3e3XBI02ASY8UkfloicXh
# U6rKGXL5vItSPc+KxFesL/MUuuwHWg+qwrZ2JGhpGni7lHmEY9S0GIWZxmzzdySQ
# NtCEu2mprDxl+qjr/TbCXSzWoRZoAqEY2zg1gSWpWTbD80mljgY4yTnwC2mehFxA
# N4dwkcc3J0FLEE5TbE1BI/GBPt9aJsML/ATvprgxhrqhggMgMIIDHAYJKoZIhvcN
# AQkGMYIDDTCCAwkCAQEwdzBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl
# cnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBT
# SEEyNTYgVGltZVN0YW1waW5nIENBAhAMTWlyS5T6PCpKPSkHgD1aMA0GCWCGSAFl
# AwQCAQUAoGkwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUx
# DxcNMjMwMjAzMDExMzU0WjAvBgkqhkiG9w0BCQQxIgQg4gZrt4YYD+pWASWRJ7R/
# F8JzPHpPuazVjPgf4+BGiPcwDQYJKoZIhvcNAQEBBQAEggIAzF7418OfAcAg3+O9
# PQhoOP2NYEhhfcKLmQIi6sXnRQkx7r8t4QuFczxLAbQUOish0nXCnfPkW/bZxyzx
# KAbX2aLpOJZcFSK1PwqxAvkE43PNIAcJbIecMko43h9ybhgCRjYpsr6+Eu3+H5NP
# aTC49j0A0zRg67MY89/xuk5NRqXvDkMtGRf5rLYMpxFUQtORk7m7uHKK6PG8uXEs
# or2RQSScqNqn+asJXtzSwrzy2fm6ca1nEyH7xPqjeaKyfKUec3BEVYjhik+7m4yu
# 6uCScD1qL/0kDc7A+9r9lI8KVOQsy3GYjluC+U4I+HsRyTIG8QYrp5AOJRftKXph
# I84UsC8u7F1fKkXFP/SX3fUbUrjff1ash96asR7WV+pq6wR35aPwIRkEc/NiTjcm
# J78t5Multuc3jDMcBpeo2QDgUzhXH14okGkeQ9qEQ09/EUDDdGZ0XBCVYxHZWVc3
# Roah1IgoMPMdYORrxGkNS8mq5Ote+ts+wSVx8w0CO9lVZwlXW+Oj3EWdP3dZDvXM
# 8Gf0pz7sspMQPCBCyOo0IGpK37SDJx+Ko5KVIMUZbEBhqBmxBLJAq0q2eYdNBgOE
# BZ901URAqyGNPWGlhEm7nSBGyww3dh2uyhdwR0iugujFvOtQEU4wgKxGw7TVvKTI
# x6M6rBNBG6LbmaeFsXLEOj4Fi0k=
# SIG # End signature block