PureStorage.CBS.VVolUtil.ps1
function Set-PCBSVmHostiSCSI{ <# .SYNOPSIS Configure FlashArray iSCSI target information on ESXi host .DESCRIPTION Takes in an ESXi host and configures FlashArray iSCSI target info #> Param( [Parameter(Mandatory=$true)] [VMware.VimAutomation.ViCore.Types.V1.Inventory.VMHost]$Esxi, [Parameter(Mandatory=$true,ValueFromPipeline=$True)] $Flasharray ) Begin { $allESXitargets = @() } Process { if ($esxi.ExtensionData.Runtime.ConnectionState -ne "connected") { Write-Warning "Host $($esxi.NetworkInfo.HostName) is not in a connected state and cannot be configured." return } $ESXitargets = @() $faiSCSItargets = Get-Pfa2NetworkInterface -Array $FlashArray |Where-Object {$_.services -eq "iscsi"} |Where-Object {$_.enabled -eq $true} | Where-Object {$null -ne $_.Eth.address} if ($null -eq $faiSCSItargets) { throw "The target FlashArray does not currently have any iSCSI targets configured." } $iscsi = $esxi |Get-VMHostStorage if ($iscsi.SoftwareIScsiEnabled -ne $true) { $esxi | Get-vmhoststorage |Set-VMHostStorage -SoftwareIScsiEnabled $True |out-null } foreach ($faiSCSItarget in $faiSCSItargets) { $iscsiadapter = $esxi | Get-VMHostHba -Type iScsi | Where-Object {$_.Model -eq "iSCSI Software Adapter"} if (!(Get-IScsiHbaTarget -IScsiHba $iscsiadapter -Type Send -ErrorAction stop | Where-Object {$_.Address -cmatch $faiSCSItarget.Eth.address})) { New-IScsiHbaTarget -IScsiHba $iscsiadapter -Address $faiSCSItarget.Eth.address -ErrorAction stop } $esxcli = $esxi |Get-esxcli -v2 $iscsiargs = $esxcli.iscsi.adapter.discovery.sendtarget.param.get.CreateArgs() $iscsiargs.adapter = $iscsiadapter.Device $iscsiargs.address = $faiSCSItarget.Eth.address $delayedAck = $esxcli.iscsi.adapter.discovery.sendtarget.param.get.invoke($iscsiargs) |where-object {$_.name -eq "DelayedAck"} $loginTimeout = $esxcli.iscsi.adapter.discovery.sendtarget.param.get.invoke($iscsiargs) |where-object {$_.name -eq "LoginTimeout"} if ($delayedAck.Current -eq "true") { $iscsiargs = $esxcli.iscsi.adapter.discovery.sendtarget.param.set.CreateArgs() $iscsiargs.adapter = $iscsiadapter.Device $iscsiargs.address = $faiSCSItarget.Eth.address $iscsiargs.value = "false" $iscsiargs.key = "DelayedAck" $esxcli.iscsi.adapter.discovery.sendtarget.param.set.invoke($iscsiargs) |out-null } if ($loginTimeout.Current -ne "30") { $iscsiargs = $esxcli.iscsi.adapter.discovery.sendtarget.param.set.CreateArgs() $iscsiargs.adapter = $iscsiadapter.Device $iscsiargs.address = $faiSCSItarget.Eth.address $iscsiargs.value = "30" $iscsiargs.key = "LoginTimeout" $esxcli.iscsi.adapter.discovery.sendtarget.param.set.invoke($iscsiargs) |out-null } $ESXitargets += Get-IScsiHbaTarget -IScsiHba $iscsiadapter -Type Send -ErrorAction stop | Where-Object {$_.Address -cmatch $faiSCSItarget.Eth.address} } $allESXitargets += $ESXitargets } End { return $allESXitargets } } function New-PCBSHostFromVmHost { <# .SYNOPSIS Create a FlashArray host from an ESXi vmhost object .DESCRIPTION Takes in a vCenter ESXi host and creates a FlashArray host #> Param( [Parameter(Mandatory=$true)] [VMware.VimAutomation.ViCore.Types.V1.Inventory.VMHost]$Esxi, [Parameter(Mandatory=$true,ValueFromPipeline=$True)] $Flasharray ) $ArrayName = Get-ArrayName -FlashArray $FlashArray Write-Verbose "Creating FlashArray $ArrayName host for VMHost $($Esxi.Name)..." $newFaHost = $null try { $newFaHost = Get-PCBSHostFromVmHost -flasharray $flasharray -esxi $esxi -ErrorAction Stop } catch {} if ($null -eq $newFaHost) { Set-PCBSVMHostiSCSI -esxi $esxi -flasharray $flasharray -ErrorAction Stop | Out-Null $iscsiadapter = $esxi | Get-VMHostHBA -Type iscsi | Where-Object { $_.Model -eq "iSCSI Software Adapter" } if ($null -eq $iscsiadapter) { throw "No Software iSCSI adapter found on host $($esxi.NetworkInfo.HostName)." } else { $iqn = $iscsiadapter.ExtensionData.IScsiName } try { try { $newFaHost = New-Pfa2Host -Array $FlashArray -Name ($esxi.NetworkInfo.HostName) -Iqns $iqn -ErrorAction Stop } catch { if (($PSItem.ToString() | convertfrom-json).msg -eq "Host already exists.") { $randName = $esxi.NetworkInfo.HostName + (Get-Random -Maximum 99999 -Minimum 10000).ToString() $newFaHost = New-Pfa2Host -Array $FlashArray -Name $($randName) -Iqns $iqn } else { throw ($PSItem.ToString() | convertfrom-json).msg } } $arrayInfo = Get-Pfa2Array -Array $FlashArray $majorVersion = [int](($arrayInfo.Version.Split('.'))[0]) if ($majorVersion -ge 5) { Update-Pfa2Host -Array $FlashArray -Name $($newFaHost.name) -Personality "esxi" | Out-Null } } catch { Write-Error "$_" return $null } } else { if ($newFaHost.wwn.count -gt 0) { throw "The host $($esxi.NetworkInfo.HostName) is already configured on array $Arrayname with FibreChannel. Multiple-protocols at once are not supported by VMware." } } return $newFaHost } function Get-PCBSHostFromVmHost { <# .SYNOPSIS Gets a FlashArray host object from a ESXi vmhost object .DESCRIPTION Takes in a vmhost and returns a matching FA host if found #> Param( [Parameter(Mandatory=$true,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.Inventory.VMHost]$Esxi, [Parameter(Mandatory=$true,ValueFromPipeline=$True)] $Flasharray ) $iscsiadapter = $esxi | Get-VMHostHBA -Type iscsi | Where-Object {$_.Model -eq "iSCSI Software Adapter"} $fahosts = Get-Pfa2Host -Array $FlashArray | Where-Object {$_.IsLocal -eq $True} $ArrayName = Get-ArrayName -FlashArray $FlashArray if ($null -ne $iscsiadapter) { $iqn = $iscsiadapter.ExtensionData.IScsiName foreach ($fahost in $fahosts) { if ($fahost.iqns.count -ge 1) { foreach ($fahostiqn in $fahost.iqns) { if ($iqn.ToLower() -eq $fahostiqn.ToLower()) { $faHostMatch = $fahost break } } } } } if ($null -ne $faHostMatch) { return $faHostMatch } else { throw "No matching host for $($esxi.Name) could be found on the FlashArray $ArrayName" } } function Get-PCBSHostGroupfromVcCluster { <# .SYNOPSIS Retrieves a FA host group from an ESXi cluster .DESCRIPTION Takes in a vCenter Cluster and retrieves corresonding host group #> Param( [Parameter(Mandatory=$true,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.Inventory.Cluster]$Cluster, [Parameter(Mandatory=$True)] $Flasharray ) $esxiHosts = $cluster |Get-VMHost $faHostGroups = @() $faHostGroupNames = @() $ArrayName = Get-ArrayName -FlashArray $FlashArray foreach ($esxiHost in $esxiHosts) { try { $faHost = $esxiHost | Get-PCBSHostFromVmHost -flasharray $flasharray if ($null -ne $faHost.HostGroup.Name) { if ($faHostGroupNames.contains($faHost.HostGroup.Name)) { continue } else { $faHostGroupNames += $faHost.HostGroup.Name $faHostGroup = Get-Pfa2HostGroup -Array $Flasharray -Name $($faHost.HostGroup.Name) -ErrorAction stop | Where-Object {$_.IsLocal -eq $True} $faHostGroups += $faHostGroup } } } catch{ continue } } if ($null -eq $faHostGroup) { throw "No host group found for cluster $($Cluster.Name) on $ArrayName." } if ($faHostGroups.count -gt 1) { Write-Warning -Message "Cluster $($Cluster.Name) spans more than one host group. The recommendation is to have only one host group per cluster" } return $faHostGroups } function Remove-PCBSUnusedHosts { Param ( [Parameter(Mandatory=$true)] [VMware.VimAutomation.ViCore.Types.V1.Inventory.Cluster]$Cluster, [Parameter(Mandatory=$True)] $Flasharray ) $hostGroups = Get-PCBSHostGroupfromVcCluster -flasharray $Flasharray -cluster $cluster $faHostList = @() foreach ($hostGroup in $hostGroups) { $faHosts = Get-Pfa2Host -Array $Flasharray | Where-Object{$_.HostGroup.Name -eq $hostGroup.Name} | Where-Object {$_.IsLocal -eq $True} $faHostList = $faHostList + $faHosts } $vmHosts = $Cluster | Get-VMHost foreach ($vmHost in $vmHosts) { $faHost = Get-PCBSHostFromVmHost -Flasharray $FlashArray -Esxi $vmHost -ErrorAction Ignore # Remove used hosts from the list $faHostList = $faHostList | Where-Object {$_.Name -ne $faHost.Name} } foreach ($faHost in $faHostList) { Write-Host "Removing unused host '$faHost.Name' ..." Update-Pfa2Host -Array $Flasharray -Name $faHost.Name -HostGroupName '' Remove-Pfa2Host -Array $Flasharray -Name $faHost.Name } } function New-PCBSHostGroupfromVcCluster { <# .SYNOPSIS Create a host group from an ESXi cluster .DESCRIPTION Takes in a vCenter Cluster and creates hosts (if needed) and host group #> Param( [Parameter(Mandatory=$true)] [VMware.VimAutomation.ViCore.Types.V1.Inventory.Cluster]$Cluster, [Parameter(Mandatory=$True)] $Flasharray ) $updated_hosts = @() # We have to use try catch here as adding -ErrorAction SilentlyContinue still throw terminating error try { $hostGroup = Get-PCBSHostGroupfromVcCluster -flasharray $Flasharray -cluster $cluster } catch {} if ($hostGroup.count -gt 1) { throw "The cluster already is configured on the FlashArray and spans more than one host group. This cmdlet does not support a multi-hostgroup configuration." } if ($null -ne $hostGroup) { $clustername = $hostGroup.name } $esxiHosts = $cluster |Get-VMHost $HostMap = @{} foreach ($esxiHost in $esxiHosts) { $faHost = $null try { $faHost = Get-PCBSHostFromVmHost -flasharray $Flasharray -esxi $esxiHost -ErrorAction Ignore if ($null -ne $faHost.HostGroup.Name) { if ($null -ne $hostGroup) { if ($hostGroup.name -ne $faHost.HostGroup.Name) { Write-Warning "The host $($faHost.name) already exists and is already in the host group $($faHost.hgroup)." } } } } catch { Write-Host "$_" } if ($null -ne $faHost) { if ($fahost.wwn.count -ge 1) { throw "The host $($esxiHost.NetworkInfo.HostName) is already configured on the FlashArray for FC. Mixed mode is not supported by VMware." } } if ($null -eq $faHost) { try { $faHost = New-PCBSHostFromVmHost -flasharray $Flasharray -ErrorAction Stop -esxi $esxiHost $HostMap[$faHost.Name] = $esxiHost $updated_hosts += $esxiHost.Name } catch { Write-Error $Global:Error[0] throw "Could not create host. Cannot create host group." } } else { $HostMap[$faHost.name] = $esxiHost.Name } } #FlashArray only supports Alphanumeric or the dash - character in host group names. Checking for VMware cluster name compliance and removing invalid characters. if ($null -eq $hostGroup) { if ($cluster.Name -match "^[a-zA-Z0-9\-]+$") { $clustername = $cluster.Name } else { $clustername = $cluster.Name -replace "[^\w\-]", "" $clustername = $clustername -replace "[_]", "" $clustername = $clustername -replace " ", "" } $hg = $null $hg = Get-Pfa2HostGroup -Array $Flasharray -Name $clustername -ErrorAction SilentlyContinue | Where-Object {$_.IsLocal -eq $True} if ($null -ne $hg) { if ($hg.hosts.count -ne 0) { #if host group name is already in use and has only unexpected hosts i will create a new one with a random number at the end $nameRandom = Get-random -Minimum 1000 -Maximum 9999 $clustername = "$($clustername)-$($nameRandom)" $hostGroup = New-Pfa2HostGroup -Array $Flasharray -Name $clustername -ErrorAction stop } else { $hostGroup = $hg } } else { #if there is no host group, it will be created $hostGroup = New-Pfa2HostGroup -Array $Flasharray -Name $clustername -ErrorAction stop } } $faHostNames = @() foreach ($faHostName in $HostMap.Keys) { $faHost = Get-Pfa2Host -Array $Flasharray -Name $faHostName | Where-Object {$_.IsLocal -eq $True} if ($null -eq $faHost.HostGroup.Name) { $faHostNames += $faHostName } } #any hosts that are not already in the host group will be added if ($faHostNames.count -gt 0) { foreach ($faHostName in $faHostNames) { Write-Host "Adding $faHostName to $clustername..." Update-Pfa2Host -Array $Flasharray -Name $faHostName -HostGroupName $clustername | Out-Null if (-not $updated_hosts -contains $HostMap[$faHostName]) { $updated_hosts += $HostMap[$faHostName] } } } return $updated_hosts } function Get-PCBSConnectionOfDatastore { <# .SYNOPSIS Takes in a vVol or VMFS datastore, FlashArray connections and returns the correct connection. .DESCRIPTION Takes in a vVol or VMFS datastore, FlashArray connections and returns the correct connection or $null if not connected. #> Param( [Parameter(Mandatory=$True,ValueFromPipeline=$True)] $Flasharray, [Parameter(Mandatory=$true,ValueFromPipeline=$True)] [ValidateScript({ if (($_.Type -ne 'VMFS') -and ($_.Type -ne 'VVOL')) { throw "The entered datastore is not a VMFS or vVol datastore. It is type $($_.Type). Please only enter a VMFS or vVol datastore" } else { $true } })] [VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.Datastore]$Datastore ) if ($datastore.Type -eq 'VMFS') { $lun = $datastore.ExtensionData.Info.Vmfs.Extent.DiskName |select-object -unique if ($lun -like 'naa.624a9370*') { $volserial = ($lun.ToUpper()).substring(12) $pureVolumes = Get-Pfa2Volume -Array $Flasharray $purevol = $purevolumes | where-object { $_.serial -eq $volserial } if ($null -ne $purevol.name) { return $flasharray } } else { throw "This VMFS is not hosted on FlashArray storage." } } elseif ($datastore.Type -eq 'VVOL') { $datastoreArraySerial = $datastore.ExtensionData.Info.VvolDS.StorageArray[0].uuid.Substring(16) $arraySerial = (Get-Pfa2Array -Array $Flasharray).id if ($arraySerial -eq $datastoreArraySerial) { return $flasharray } } # The datastore was not found on any of the FlashArray connections. return $null } function Get-ArrayID { Param( [Parameter(Mandatory=$true)] $FlashArray ) $ArrayID = "com.purestorage:" + (Get-Pfa2Array -Array $Flasharray).id return $arrayID } function Get-ArrayName { Param( [Parameter(Mandatory=$true)] $FlashArray ) $ArrayName = (Get-Pfa2Array -Array $Flasharray).Name return $ArrayName } function Get-ArrayOUI { Param ( [Parameter(Mandatory=$true)] [string]$ArrayID ) $ArrayOui = $ArrayID.substring(16,36) $ArrayOui = $ArrayOui.replace("-","") $ArrayOui = $ArrayOui.Substring(0,16) return $ArrayOui } function Get-RootStorageContainerID { Param( [Parameter(Mandatory=$true)] [string]$ArrayID ) $md5 = new-object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider $utf8 = new-object -TypeName System.Text.UTF8Encoding $hash = $md5.ComputeHash($utf8.GetBytes($ArrayID.substring(16,36))) $hash2 = $md5.ComputeHash(($hash)) $hash2[6] = $hash2[6] -band 0x0f $hash2[6] = $hash2[6] -bor 0x30 $hash2[8] = $hash2[8] -band 0x3f $hash2[8] = $hash2[8] -bor 0x80 $newGUID = (new-object -TypeName System.Guid -ArgumentList (,$hash2)).Guid $fixedGUID = $newGUID.Substring(18) $scId = $newGUID.Substring(6,2) + $newGUID.Substring(4,2) + $newGUID.Substring(2,2) + $newGUID.Substring(0,2) + "-" + $newGUID.Substring(11,2) + $newGUID.Substring(9,2) + "-" + $newGUID.Substring(16,2) + $newGUID.Substring(14,2) + $fixedGUID $scId = $scId.Replace("-","") $scId = "vvol:" + $scId.Insert(16,"-") return $scId } function Rescan-HBA { Param( $ESXIHosts, $FlashArray ) foreach ($esxiRescan in $ESXIHosts) { $hbas = $esxiRescan |get-PCBSHostFromVmHost -flasharray $FlashArray if ($hbas.iqns.count -ge 1) { $hbaType = "iSCSI" } elseif ($hbas.wwn.count -ge 1) { $hbaType = "FibreChannel" } $esxiHost = $esxiRescan.ExtensionData $storageSystem = Get-View -Id $esxiHost.ConfigManager.StorageSystem Write-Host "Rescanning HBAs for $($esxiRescan.Name)..." $hbas = ($esxiRescan |Get-VMHostHba |where-object {$_.Type -eq $hbaType}).device foreach ($hba in $hbas) { $storageSystem.rescanHba($hba) } } } function Find-ProtocolEndpoint { Param ( $Esxi, $PEVolumes, $multiStorageContainersSupport, $UseDefaultStore, $arrayOui ) $esxcli = $esxi |Get-EsxCli -v2 $hostProtocolEndpoint = $esxcli.storage.core.device.list.invoke() |where-object {$_.IsVVOLPE -eq $true} foreach ($hostPE in $hostProtocolEndpoint) { $peID = $hostPE.Device.Substring(12,24) $peID = $peID.Substring(0,16) if ($peID -eq $arrayOui) { if ($multiStorageContainersSupport -and -not $UseDefaultStore) { if ($PEVolumes.Count -gt 0){ foreach ($peVolume in $PEVolumes) { $volumeSerial = $peVolume.Serial.ToLower() if ($hostPe.Device -like "*$volumeSerial*"){ return $hostPE } } } } } else { return $hostPE } } return $null } function Update-PCBSVASAProvider { Param ( $FlashArray, [PScredential] $FlashArrayCredential, [switch] $Refresh ) $vasaProviders = @() $MaxRetryCount = 10 $mgmtIPs = Get-Pfa2NetworkInterface | Where-Object {$_.services -eq "management" -and $_.Enabled -and $_.Name -like "ct*"} $registeredController = @() $ArrayName = Get-ArrayName -FlashArray $FlashArray foreach ($mgmtIP in $mgmtIPs) { $vasaRegistered = $false $retry_count = 0 $controller_name = ($mgmtIp.Name.Split("."))[0] if ($registeredController -contains $controller_name) { continue } $provider_name = "$ArrayName-$controller_name" if ($Refresh) { Write-Host "Removing provider $provider_name ..." Get-VasaProvider -Name $provider_name |Remove-VasaProvider -Confirm:$false } do { $vasaProvider = Get-VasaProvider -Name $provider_name -ErrorAction Ignore if ($vasaProvider) { Write-Host "VASA Provider already exists for $ArrayName controller $controller_name ..." if ($vasaProvider.status -eq "online") { $vasaRegistered = $True } elseif ($vasaProvider.status -eq "offline") { # VASA provider can be offline if: # - Array is not accessible (in this case, we will get an error when trying to connect) # - Certificate is expired # - Certificate is invalid (for example a new certificate uploaded to the Array) Write-Warning "VASA provider $provider_name status is $($vasaProvider.status)" # No need to check for expiry time as the cert might not be expired but invalid. Remove the certificate and re-register the VASA provider $cert_name = "vasa-$controller_name" Write-Host "Removing certificate $cert_name from FlashArray: $ArrayName ..." Remove-Pfa2Certificate -Name $cert_name Write-Host "Creating self-signed certificate $cert_name on FlashArray: $ArrayName ..." New-Pfa2Certificate -Name $cert_name -CommonName $mgmtIP.Eth.Address -Organization "Pure Storage" -OrganizationalUnit "Pure Storage" | Out-Null Write-Host "Removing VASA provider $provider_name ..." Remove-VasaProvider -Provider $vasaProvider -Confirm:$false $vasaRegistered = $False } else { throw "Could not update VASA provider. VASA provider $provider_name status is $($vasaProvider.status)." } } else { Write-Host "Creating VASA Provider for $ArrayName controller $controller_name ..." try { $vasaProviders += New-VasaProvider -Name ($provider_name) -Credential $FlashArrayCredential -Url ("https://$($mgmtIP.Eth.Address):8084") -force -ErrorAction Stop $vasaRegistered = $True Write-Host "VASA Provider for $ArrayName-$controller_name created successufully." } catch { if ($retry_count -lt $MaxRetryCount) { Write-Warning "Failed to register VASA provider $provider_name with error $_ Retrying ..." $vasaRegistered = $false Start-Sleep -Seconds 10 } else { throw "Failed to register VASA provider $provider_name with error $_" } } } $retry_count = $retry_count + 1 } while ($vasaRegistered -ne $true -and $retry_count -lt $MaxRetryCount) if (-not $vasaRegistered) { throw "Failed to register VASA provider $provider_name after $retry_count tries." } else { $registeredController += $controller_name } } # Certificate refresh on all hosts in vCenter Update-ESXCertificates return $vasaProviders } function Wait-PCBSVvolDatastore { Param( [Parameter(Mandatory=$true)] [string]$DatastoreName ) $retries = 0 # Datastore will not be available immediately. The retry logic keeps query the status of the datastore while ($retries -lt 5) { $latestDatastore = (Get-Datastore | Where-Object {$_.Name -eq $DatastoreName}) if ("Available" -ne $latestDatastore.State -or -not $latestDatastore.Accessible) { $retries++ Start-Sleep -s 5 } else { Write-Host "The datastore '$($latestDatastore.Name)' is available" break } Write-Host "Waiting for the datastore '$($latestDatastore.Name)' to be available..." } if ("Available" -ne $latestDatastore.State -or -not $latestDatastore.Accessible) { throw "The datastore '$($datastore.Name)' is either not available or not acessible." } } function Mount-PCBSVvolDatastore { <# .SYNOPSIS Mounts a FlashArray VVol Datastore to a host or cluster .DESCRIPTION Mounts a FlashArray VVol Datastore to a cluster, connects a PE to the cluster if not present. The datastore will be created if it does not already exist. The function will return the exting (or newly created) datastore #> Param( [Parameter(Mandatory=$True)] $Flasharray, [Parameter(Mandatory=$True)] [PSCredential]$FlasharrayCredential, [Parameter(Mandatory=$false)] [string]$DatastoreName, [Parameter(Mandatory=$false)] [bool]$UseDefaultStore, [Parameter(Mandatory=$true,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.Inventory.Cluster]$Cluster ) $multiStorageContainersSupport = $false $arrayVersion = ((Get-Pfa2Array -Array $Flasharray).Version).Split('.') $arrayVersionObject = [Version]::new($arrayVersion[0],$arrayVersion[1],$arrayVersion[2]) $baseVersionObject = [Version]::new("6","4","1") $multiStorageContainersSupport = $arrayVersionObject -ge $baseVersionObject $arrayID = Get-ArrayID -FlashArray $Flasharray $arrayOui = Get-ArrayOUI -ArrayID $arrayID $ArrayName = Get-ArrayName -FlashArray $FlashArray if ($True -eq $UseDefaultStore) { $scId = Get-RootStorageContainerID -ArrayID $arrayID $datastoreExists = Get-Datastore |Where-Object {$_.Type -eq "VVol"} |Where-Object {$_.ExtensionData.Info.VVolds.Scid -eq $scId} if ($null -eq $datastoreExists) { if (-not $DatastoreName) { $DatastoreName = $ArrayName + "-vvol-DS" } } else { if (-not $DatastoreName) { $DatastoreName = $datastoreExists.Name } } } else { # Using a multi-storage container Pod datastore if (-not $multiStorageContainersSupport) { throw "Non default containers can only be used for purity version 6.4.1 or later" } if (-not $DatastoreName) { throw "Datastore name must be provided when using non default containers." } # See if a pod was already created $pod = Get-Pfa2Pod -Array $Flasharray -Name $DatastoreName -ErrorAction Ignore if (-not $pod) { Write-Host "Creating a pod $DatastoreName ..." $pod = New-Pfa2Pod -Name $DatastoreName -ErrorAction Stop } else { # Get list of protocol endpoints in the pod $PEVolumes = Get-Pfa2Volume -Array $Flasharray | Where-Object {($_.Pod.Name -eq $DatastoreName) -and ($_.Subtype -eq "protocol_endpoint")} } $pod_id = $pod.id.Replace("-","") $scid = "vvol:"+$pod_id.substring(0,16)+"-"+$pod_id.substring(16) } Write-Verbose "Using storage container ID '$scid'..." $datastoreExists = Get-Datastore |Where-Object {$_.Type -eq "VVol"} |Where-Object {$_.ExtensionData.Info.VVolds.Scid -eq $scId} if ($null -ne $datastoreExists) { if ($DatastoreName -ne $datastoreExists.Name) { throw "A datastore '$($datastoreExists.Name)' already exists. Will not be able to create a datastore with the name '$DatastoreName." } Write-Host "A datastore already exists. Using datastore $($datastoreExists.Name)..." $datastore = $datastoreExists } $esxiHosts = $cluster |Get-VMHost foreach ($esxi in $esxiHosts) { $hostPE = Find-ProtocolEndpoint -Esxi $esxi -PEVolumes $PEVolumes -multiStorageContainersSupport $multiStorageContainersSupport -UseDefaultStore $UseDefaultStore -arrayOui $arrayOui $foundPE = $null -ne $hostPE if ($foundPE -eq $false) { if ($null -ne $datastore) { $fa = Get-PCBSConnectionOfDatastore -FlashArray $Flasharray -datastore $datastore -ErrorAction Ignore if ($null -eq $fa) { throw "No protocol endpoints found on the host $($esxi.name) for this array. Attempt to provision a PE failed as no valid PowerShell connections found for the array. Please either provision the protocol endpoint or connect the array to an existing PE." } } $hGroup = Get-PCBSHostGroupfromVcCluster -cluster $cluster -flasharray $fa -ErrorAction Stop $allPEs = Get-Pfa2Volume -Array $Flasharray | Where-Object {$_.Subtype -eq "protocol_endpoint"} -ErrorAction Stop if (($null -eq $protocolEndpoint) -or ($protocolEndpoint -eq "")) { $protocolEndpoint = "pure-protocol-endpoint" if ($multiStorageContainersSupport -and -not $UseDefaultStore) { $ProtocolEndpoint = "$DatastoreName::$ProtocolEndpoint" } } $pe = $allPEs |Where-Object {$_.name -eq $protocolEndpoint} if ($null -eq $pe) { $pe = New-Pfa2Volume -Array $Flasharray -Name $protocolEndpoint -Subtype "protocol_endpoint" } try { New-Pfa2Connection -Array $FlashArray -HostGroupNames $hGroup.name -VolumeNames $pe.name -ErrorAction Stop } catch { if ($_.Exception -notlike "*Connection already exists.*") { throw $_.Exception } } # Refresh VASA Providers $provider_id = (Get-VasaStorageArray -Id ("com.purestorage:" + (Get-Pfa2Array).id)).provider.id Write-Host "Refreshing VASA Provider..." $out = Get-VasaProvider -id $provider_id -Refresh Write-Host "Refreshed VASA provider $($out.Name) ($($out.Id)" } $datastoreSystem = Get-View -Id $esxi.ExtensionData.ConfigManager.DatastoreSystem $spec = New-Object VMware.Vim.HostDatastoreSystemVvolDatastoreSpec $spec.Name = $datastoreName $spec.ScId = $scId $foundDatastore = $esxi |get-datastore |Where-Object {$_.Type -eq "VVol"} |Where-Object {$_.ExtensionData.Info.VVolds.Scid -eq $scId} if ($null -eq $foundDatastore -or $foundDatastore.Name -ne $DatastoreName) { $datastore = Get-Datastore -Id ($datastoreSystem.CreateVvolDatastore($spec)) } else { $datastore = $foundDatastore } $foundDatastore = $null } return $datastore } # SIG # Begin signature block # MIIjUAYJKoZIhvcNAQcCoIIjQTCCIz0CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUNm/tdwPmvOV63mEDKr6uSJeB # yTOggh12MIIFMDCCBBigAwIBAgIQBAkYG1/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 # AQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFLQ0ry5aOtdo0vuA9OA5Xfap5ru5MA0G # CSqGSIb3DQEBAQUABIIBADqkzJ/kxgbBS/F3Bjt8b+dl9rfmIth8vpxk7rXy/VGm # e1oC3elshT+eFvqiCvT3wmngWNVDypowRiUbCc3cMfCuAHtLgkK9GPl1seucoCbw # DyUhtFGJVUdNwwo813VR6PUPYSdj4bFsKj0Yovf60KJAEFCR7qYIXYjcOzWa9lIX # KcdizhlWFOoKky+Ampr1kUfzRAJ+itx8u16LCpN1SNurKgdSIY4RIbQmrmrat+78 # p8HOIhJED60VMoFPJc3Z0eu5e75UJQwjPVYqS3a1HoVFtb/OmZNnHHIZqY7ZXVRc # 3GHa3nxuKi5OZVcRTa+prE8uyNbWRtkISqqv5FzAbZqhggMgMIIDHAYJKoZIhvcN # AQkGMYIDDTCCAwkCAQEwdzBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl # cnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBT # SEEyNTYgVGltZVN0YW1waW5nIENBAhAMTWlyS5T6PCpKPSkHgD1aMA0GCWCGSAFl # AwQCAQUAoGkwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUx # DxcNMjMwMjAzMDExMzU0WjAvBgkqhkiG9w0BCQQxIgQgVRmhGjiF0q/qks/NoROb # KGnV9zfJxNroe6ljAeT6A3MwDQYJKoZIhvcNAQEBBQAEggIAceTtslw+2hpdQ1n1 # AeZN+aHTEas+4UlhcLGJLsujZ2+LADCrfxAYmYU5YTN52MPJJ1vZq96XIqF0bnmW # 9vYjDhUE17dzKI3HZSZ0KMjPmu0ynagZ3scTM3o4t6c5mZuP4KigKI7zev7bUmOj # SVmiaP0ezA3riKVTRPNWnXH8sQK0WS/F0yicEB31cBkoaC0NJqoUqwXXyA3XA7yS # mmEoBdua207VfCv+nmAP3Zb0mRY2DvCkyovWTXj3AZNkXPb+oB1xYzLLBScrxdcL # iUpajQ5irWCXOzj8pFhvlYEiYzWu0wecHmMdZeVzmsOnFxJk87ULIgxZDnW3snwt # 0veCpj5Bc04pZWXmoC+R8QI+QYiYesZf0Jh309Hn9xCpgDAzZ8k3HyqDIq4YErnS # JBr9BgQwprcHu6f7YI2q9qBavrqkN4dcpTl9GaV+XRIVRVvO3K8aFyMyvlOCn7I0 # C/9eGOtIGUiBotq8Nr4qsHjgtM5sX0wlfAGexkOeNpeXicwbGxZVFAr03tWafCrU # h/OC4DzGGhxii8cFS7VVWfDFTlJ21ZCY4IV5LJ+05lrm+DCTlCoRbTR5xU6ZDJ50 # kiDidSJYrHTIYRRx8h/bns0P46k2MmjYnBp7B7M/+cKprKFxgtQ8sHsHt0ygruyL # TlwE2i8QLjYUAG2Tua9qt3/dKrY= # SIG # End signature block |