CLI/Internal.psm1
#################################################################################### ## © 2020,2021 Hewlett Packard Enterprise Development LP ## ## Permission is hereby granted, free of charge, to any person obtaining a ## copy of this software and associated documentation files (the "Software"), ## to deal in the Software without restriction, including without limitation ## the rights to use, copy, modify, merge, publish, distribute, sublicense, ## and/or sell copies of the Software, and to permit persons to whom the ## Software is furnished to do so, subject to the following conditions: ## ## The above copyright notice and this permission notice shall be included ## in all copies or substantial portions of the Software. ## ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ## THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR ## OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ## ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ## OTHER DEALINGS IN THE SOFTWARE. ## ## File Name: Internal.psm1 ## Description: Internal cmdlets ## ## Created: January 2020 ## Last Modified: May 2021 ## History: v3.0 - Created ## v3.1 - Modified to add HPE Alletra 9000 ##################################################################################### $Info = "INFO:" $Debug = "DEBUG:" $global:VSLibraries = Split-Path $MyInvocation.MyCommand.Path [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 ###################################################################################################################### ## FUNCTION Close-Connection ###################################################################################################################### Function Close-Connection { <# .SYNOPSIS Session Management Command to close the connection .DESCRIPTION Session Management Command to close the connection .EXAMPLE Close-Connection .Notes NAME: Close-Connection LASTEDIT: January 2020 KEYWORDS: Close-Connection .Link http://www.hpe.com #Requires PS -Version 3.0 #> [CmdletBinding()] param( [Parameter(Position=0, Mandatory=$false, ValueFromPipeline=$true)] $SANConnection = $global:SANConnection ) Write-DebugLog "Start : in Close-Connection function " "INFO:" if (($global:WsapiConnection) -or ($global:ConnectionType -eq "WSAPI")){ return "A WSAPI Session is enabled. Use Close-WSAPIConnection cmdlet to close and exit from the current WSAPI session" } if (!$SANConnection){ return "No active CLI/PoshSSH session/connection exists" } $SANCOB = $SANConnection $clittype = $SANCOB.CliType $SecnId ="" if($clittype -eq "SshClient") { $SecnId = $SANCOB.SessionId } $global:SANConnection = $null #write-host "$global:SANConnection" $SANConnection = $global:SANConnection #write-host "$SANConnection" if(!$SANConnection) { #check if connection object contents are null/empty $Validate1 = Test-CLIConnection $SANConnection #write-host "$Validate1" if($Validate1 -eq "Failed") { #check if global connection object contents are null/empty $Validate2 = Test-CLIConnection $global:SANConnection #write-host "$Validate2" if($Validate2 -eq "Failed") { Write-DebugLog "Connection object is null/empty or Connection object username, password, IPAaddress are null/empty. Create a valid connection object using New-CLIConnection or New-PoshSshConnection" "ERR:" Write-DebugLog "Stop: Exiting GGet-UserConnection since SAN connection object values are null/empty" $Debug if($clittype -eq "SshClient") { $res = Remove-SSHSession -Index $SecnId } write-host "" # Set to the default prompt as current path if ($global:SANConnection -eq $null) { $global:ConnectionType = $null Function global:prompt {(Get-Location).Path + ">"} } return "Success : Exiting SAN connection session End`n" } } } } # End Function Close-Connection ######################################## ##### FUNCTION Get-CmdList ########### ######################################## Function Get-CmdList{ <# .SYNOPSIS Get list of all HPE Alletra 9000, Primera and 3PAR PowerShell cmdlets .DESCRIPTION Note : This cmdlet (Get-CmdList) is deprecated and will be removed in a subsequent release of PowerShell Toolkit. Consider using the cmdlet (Get-CmdList) instead. Get list of all HPE Alletra 9000, Primera and 3PAR PowerShell cmdlets .EXAMPLE Get-CmdList List all available HPE Alletra 9000, Primera and 3PAR PowerShell cmdlets. .EXAMPLE Get-CmdList -WSAPI List all available HPE Alletra 9000, Primera and 3PAR PowerShell WSAPI cmdlets only. .EXAMPLE Get-CmdList -CLI List all available HPE Alletra 9000, Primera and 3PAR PowerShell CLI cmdlets only. .Notes NAME: Get-CmdList CREATED: 05/14/2015 LASTEDIT: 05/26/2020 KEYWORDS: Get-CmdList .Link http://www.hpe.com #Requires PS -Version 3.0 #> [CmdletBinding()] param( [Parameter(Position=0, Mandatory=$false)] [Switch] $CLI, [Parameter(Position=1, Mandatory=$false)] [Switch] $WSAPI ) $Array = @() #$psToolKitModule = (Get-Module PowerShellToolkitForHPEPrimeraAnd3PAR); $psToolKitModule = (Get-Module HPEStoragePowerShellToolkit); $nestedModules = $psToolKitModule.NestedModules; $noOfNestedModules = $nestedModules.Count; $totalCmdlets = 0; $totalCLICmdlets = 0; $totalWSAPICmdlets = 0; $totalDeprecatedCmdlets = 0; # If chosen to select all WSAPI cmdlets if($WSAPI) { foreach ($nestedModule in $nestedModules[0..$noOfNestedModules]) { $ExpCmdlets = $nestedModule.ExportedCommands; if ($nestedModule.Path.Contains("\WSAPI\")) { foreach ($h in $ExpCmdlets.GetEnumerator()) { $Result1 = "" | Select CmdletName, CmdletType, ModuleVersion, SubModule, Module, Remarks $Result1.CmdletName = $($h.Key); $Result1.ModuleVersion = $psToolKitModule.Version; $Result1.CmdletType = "WSAPI"; $Result1.SubModule = $nestedModule.Name; $Result1.Module = $psToolKitModule.Name; If ($nestedModule.Name -eq "HPE3PARPSToolkit-WSAPI") { $Result1.Remarks = "Deprecated"; $totalDeprecatedCmdlets += 1; } $totalCmdlets += 1; $totalWSAPICmdlets += 1; $Array += $Result1 } } } } # If chosen to select all CLI cmdlets elseif($CLI) { foreach ($nestedModule in $nestedModules[0..$noOfNestedModules]) { $ExpCmdlets = $nestedModule.ExportedCommands; if ($nestedModule.Path.Contains("\CLI\")) { foreach ($h in $ExpCmdlets.GetEnumerator()) { $Result1 = "" | Select CmdletName, CmdletType, ModuleVersion, SubModule, Module, Remarks $Result1.CmdletName = $($h.Key); $Result1.ModuleVersion = $psToolKitModule.Version; $Result1.CmdletType = "CLI"; $Result1.SubModule = $nestedModule.Name; $Result1.Module = $psToolKitModule.Name; If ($nestedModule.Name -eq "HPE3PARPSToolkit-CLI") { $Result1.Remarks = "Deprecated"; $totalDeprecatedCmdlets += 1; } $totalCmdlets += 1; $totalCLICmdlets += 1; $Array += $Result1 } } } } # If chosen to select all cmdlets else { foreach ($nestedModule in $nestedModules[0..$noOfNestedModules]) { if ($nestedModule.Path.Contains("\CLI\") -or $nestedModule.Path.Contains("\WSAPI\")) { $ExpCmdlets = $nestedModule.ExportedCommands; foreach ($h in $ExpCmdlets.GetEnumerator()) { $Result1 = "" | Select CmdletName, CmdletType, ModuleVersion, SubModule, Module, Remarks $Result1.CmdletName = $($h.Key); $Result1.ModuleVersion = $psToolKitModule.Version; $Result1.SubModule = $nestedModule.Name; $Result1.Module = $psToolKitModule.Name; $Result1.CmdletType = if ($nestedModule.Path.Contains("\CLI\")) {"CLI"} else {"WSAPI"} If ($nestedModule.Name -eq "HPE3PARPSToolkit-WSAPI" -or $nestedModule.Name -eq "HPE3PARPSToolkit-CLI") { $Result1.Remarks = "Deprecated"; $totalDeprecatedCmdlets += 1; } $totalCmdlets += 1; $Array += $Result1 } } } } $Array | Format-Table $Array = $null; Write-Host "$totalCmdlets Cmdlets listed. ($totalDeprecatedCmdlets are deprecated)"; }# Ended Get-CmdList ######################################################################### ################### FUNCTION Get-FcPorts ################################ ######################################################################### Function Get-FcPorts { <# .SYNOPSIS Query to get FC ports .DESCRIPTION Get information for FC Ports .PARAMETER SANConnection Connection String to the array .EXAMPLE Get-FcPorts .Notes NAME: Get-FcPorts LASTEDIT: January 2020 KEYWORDS: Get-FcPorts .Link http://www.hpe.com #Requires PS -Version 3.0 #Requires HPE 3PAR cli.exe #> [CmdletBinding()] Param( [Parameter()] [_SANConnection] $SANConnection=$Global:SANConnection ) $plinkresult = Test-PARCli -SANConnection $SANConnection if($plinkresult -match "FAILURE :") { write-debuglog "$plinkresult" "ERR:" return $plinkresult } Write-Host "--------------------------------------`n" Write-host "Controller,WWN" $ListofPorts = Get-HostPorts -SANConnection $SANConnection| where { ( $_.Type -eq "host" ) -and ($_.Protocol -eq "FC")} $Port_Pattern = "(\d):(\d):(\d)" # Pattern matches value of port: 1:2:3 $WWN_Pattern = "([0-9a-f][0-9a-f])" * 8 # Pattern matches value of WWN foreach ($Port in $ListofPorts) { $NSP = $Port.Device #$SW = $NSP.Split(':')[-1] $NSP = $NSP -replace $Port_Pattern , 'N$1:S$2:P$3' $WWN = $Port.Port_WWN $WWN = $WWN -replace $WWN_Pattern , '$1:$2:$3:$4:$5:$6:$7:$8' Write-Host "$NSP,$WWN" Write-host "" } } # END FUNCTION Get-FcPorts ########################################################################### ####################### FUNCTION Get-FcPortsToCsv ######################### ########################################################################### Function Get-FcPortsToCsv { <# .SYNOPSIS Query to get FC ports .DESCRIPTION Get information for FC Ports .PARAMETER ResultFile CSV file created that contains all Ports definitions .PARAMETER Demo Switch to list the commands to be executed .EXAMPLE Get-FcPortsToCsv -ResultFile C:\3PAR-FC.CSV creates C:\3PAR-FC.CSV and stores all FCPorts information .Notes NAME: Get-FcPortsToCsv LASTEDIT: January 2020 KEYWORDS: Get-FcPortsToCsv .Link http://www.hpe.com #Requires PS -Version 3.0 #Requires HPE 3PAR cli.exe #> [CmdletBinding()] Param( [Parameter()] [_SANConnection] $SANConnection = $global:SANConnection, [Parameter()] [String]$ResultFile ) $plinkresult = Test-PARCli -SANConnection $SANConnection if($plinkresult -match "FAILURE :") { write-debuglog "$plinkresult" "ERR:" return $plinkresult } if(!($ResultFile)){ return "FAILURE : Please specify csv file path `n example: -ResultFIle C:\portsfile.csv" } Set-Content -Path $ResultFile -Value "Controller,WWN,SWNumber" $ListofPorts = Get-HostPorts -SANConnection $SANConnection| where { ( $_.Type -eq "host" ) -and ($_.Protocol -eq "FC")} if (!($ListofPorts)){ return "FAILURE : No ports to display" } $Port_Pattern = "(\d):(\d):(\d)" # Pattern matches value of port: 1:2:3 $WWN_Pattern = "([0-9a-f][0-9a-f])" * 8 # Pattern matches value of WWN foreach ($Port in $ListofPorts) { $NSP = $Port.Device $SW = $NSP.Split(':')[-1] if ( [Bool]($SW % 2) ) # Check whether the number is odd { $SwitchNumber = 1 } else { $SwitchNumber = 2 } $NSP = $NSP -replace $Port_Pattern , 'N$1:S$2:P$3' $WWN = $Port.Port_WWN $WWN = $WWN -replace $WWN_Pattern , '$1:$2:$3:$4:$5:$6:$7:$8' Add-Content -Path $ResultFile -Value "$NSP,$WWN,$SwitchNumber" } Write-DebugLog "FC ports are stored in $ResultFile" $Info return "Success: FC ports information stored in $ResultFile" } # END FUNCTION Get-FcPortsToCsv ################################################################## ############# FUNCTION Get-ConnectedSession ###################### ################################################################## function Get-ConnectedSession { <# .SYNOPSIS Command Get-ConnectedSession display connected session detail .DESCRIPTION Command Get-ConnectedSession display connected session detail .EXAMPLE Get-ConnectedSession .Notes NAME: Get-ConnectedSession LASTEDIT: January 2020 KEYWORDS: Get-ConnectedSession .Link http://www.hpe.com #Requires PS -Version 3.0 #> Begin{} Process { return $global:SANConnection } End{} } # END FUNCTION Get-ConnectedSession ############################################################################################################################################ ## FUNCTION New-CLIConnection ############################################################################################################################################ Function New-CLIConnection { <# .SYNOPSIS Builds a SAN Connection object using HPE 3PAR CLI. .DESCRIPTION Creates a SAN Connection object with the specified parameters. No connection is made by this cmdlet call, it merely builds the connection object. .EXAMPLE New-CLIConnection -ArrayNameOrIPAddress 10.1.1.1 -CLIDir "C:\cli.exe" -epwdFile "C:\HPE3parepwdlogin.txt" Creates a SAN Connection object with the specified Array Name or Array IP Address .PARAMETER ArrayNameOrIPAddress Specify Array Name or Array IP Address .PARAMETER CLIDir Specify the absolute path of HPE 3PAR cli.exe. Default is "C:\Program Files (x86)\Hewlett Packard Enterprise\HPE 3PAR CLI\bin" .PARAMETER epwdFile Specify the encrypted password file location , example “c:\HPE3parstoreserv244.txt” To create encrypted password file use “Set-Password” cmdlet .Notes NAME: New-CLIConnection LASTEDIT: January 2020 KEYWORDS: New-CLIConnection .Link http://www.hpe.com #Requires PS -Version 3.0 #Requires HPE 3PAR cli.exe #> [CmdletBinding()] param( [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true)] [System.String] $ArrayNameOrIPAddress=$null, [Parameter(Position=1, Mandatory=$false, ValueFromPipeline=$true)] [System.String] #$CLIDir="C:\Program Files (x86)\Hewlett Packard Enterprise\HPE 3PAR CLI\bin", $CLIDir="C:\Program Files (x86)\Hewlett Packard Enterprise\HPE 3PAR CLI\bin", [Parameter(Position=2, Mandatory=$true, ValueFromPipeline=$true)] [System.String] $epwdFile="C:\HPE3parepwdlogin.txt" ) #Write-DebugLog "start: Entering function New-CLIConnection. Validating IP Address format." $Debug ## Check IP Address Format #if(-not (Test-IPFormat $ArrayNameOrIPAddress)) #{ # Write-DebugLog "Stop: Invalid IP Address $ArrayNameOrIPAddress" "ERR:" # return "Failure : Invalid IP Address $ArrayNameOrIPAddress" #} #Write-DebugLog "Running: Completed validating IP address format." $Debug # -------- Check any active CLI/PoshSSH session exists ------------ starts $check = Test-CLIConnection $global:SANConnection if($check -eq "Success"){ $confirm = Read-Host "An active CLI/PoshSSH session exists.`nDo you want to close the current CLI/PoshSSH session and start a new CLI session (Enter y=yes n=no)" if ($confirm.tolower() -eq 'y') { Close-Connection } elseif ($confirm.tolower() -eq 'n') { return } } # -------- Check any active CLI/PoshSSH session exists ------------ ends # -------- Check any active WSAPI session exists ------------------ starts if($global:WsapiConnection){ $confirm = Read-Host "An active WSAPI session exists.`nDo you want to close the current WSAPI session and start a new CLI session (Enter y=yes n=no)" if ($confirm.tolower() -eq 'y') { Close-WSAPIConnection } elseif ($confirm.tolower() -eq 'n') { return } } # -------- Check any active WSAPI session exists ------------------ ends Write-DebugLog "Running: Authenticating credentials - Invoke-CLI for user $SANUserName and SANIP= $ArrayNameOrIPAddress" $Debug $test = $env:Path $test1 = $test.split(";") if ($test1 -eq $CLIDir) { Write-DebugLog "Running: Environment variable path for $CLIDir already exists" "INFO:" } else { Write-DebugLog "Running: Environment variable path for $CLIDir does not exists, so added $CLIDir to environment" "INFO:" $env:Path += ";$CLIDir" } if (-not (Test-Path -Path $CLIDir )) { Write-DebugLog "Stop: Path for HPE 3PAR cli was not found. Make sure you have installed HPE 3PAR CLI." "ERR:" return "Failure : Path for HPE 3PAR cli was not found. Make sure you have cli.exe file under $CLIDir" } $clifile = $CLIDir + "\cli.exe" if( -not (Test-Path $clifile)) { Write-DebugLog "Stop: Path for HPE 3PAR cli was not found.Please enter only directory path with out cli.exe & Make sure you have installed HPE 3PAR CLI." "ERR:" return "Failure : Path for HPE 3PAR cli was not found,Make sure you have cli.exe file under $CLIDir" } #write-host "Set HPE 3PAR CLI path if not" # Authenticate try { if( -not (Test-Path $epwdFile)) { write-host "Encrypted password file does not exist , creating encrypted password file" Set-Password -CLIDir $CLIDir -ArrayNameOrIPAddress $ArrayNameOrIPAddress -epwdFile $epwdFile Write-DebugLog "Running: Path for encrypted password file was not found. Now created new epwd file." "INFO:" } #write-host "pwd file : $epwdFile" Write-DebugLog "Running: Path for encrypted password file was already exists." "INFO:" $global:epwdFile = $epwdFile $Result9 = Invoke-CLI -DeviceIPAddress $ArrayNameOrIPAddress -CLIDir $CLIDir -epwdFile $epwdFile -cmd "showversion" Write-DebugLog "Running: Executed Invoke-CLI. Check on PS console if there are any errors reported" $Debug if ($Result9 -match "FAILURE") { return $Result9 } } catch { $msg = "In function New-CLIConnection. " $msg+= $_.Exception.ToString() # Write-Exception function is used for exception logging so that it creates a separate exception log file. Write-Exception $msg -error return "Failure : $msg" } $global:SANObjArr += @() #write-host "objarray",$global:SANObjArr if($global:SANConnection) { #write-host "In IF loop" $SANC = New-Object "_SANConnection" # Get the username $connUserName = Get-UserConnectionTemp -ArrayNameOrIPAddress $ArrayNameOrIPAddress -CLIDir $CLIDir -epwdFile $epwdFile -Option current $SANC.UserName = $connUserName.Name $SANC.IPAddress = $ArrayNameOrIPAddress $SANC.CLIDir = $CLIDir $SANC.epwdFile = $epwdFile $SANC.CLIType = "3parcli" $SANC.SessionId = "NULL" $global:SANConnection = $SANC $global:SANObjArr += @($SANC) $SystemDetails = Get-System $SANC.Name = $SystemDetails.Name $SANC.SystemVersion = Get-Version -S -B $SANC.Model = $SystemDetails.Model $SANC.Serial = $SystemDetails.Serial $SANC.TotalCapacityMiB = $SystemDetails.TotalCap $SANC.AllocatedCapacityMiB = $SystemDetails.AllocCap $SANC.FreeCapacityMiB = $SystemDetails.FreeCap $global:ArrayName = $SANC.Name $global:ConnectionType = "CLI" } else { $global:SANObjArr = @() #write-host "In Else loop" $SANC = New-Object "_SANConnection" $connUserName = Get-UserConnectionTemp -ArrayNameOrIPAddress $ArrayNameOrIPAddress -CLIDir $CLIDir -epwdFile $epwdFile -Option current $SANC.UserName = $connUserName.Name $SANC.IPAddress = $ArrayNameOrIPAddress $SANC.CLIDir = $CLIDir $SANC.epwdFile = $epwdFile $SANC.CLIType = "3parcli" $SANC.SessionId = "NULL" #making this object as global $global:SANConnection = $SANC $global:SANObjArr += @($SANC) $SystemDetails = Get-System $SANC.Name = $SystemDetails.Name $SANC.SystemVersion = Get-Version -S -B $SANC.Model = $SystemDetails.Model $SANC.Serial = $SystemDetails.Serial $SANC.TotalCapacityMiB = $SystemDetails.TotalCap $SANC.AllocatedCapacityMiB = $SystemDetails.AllocCap $SANC.FreeCapacityMiB = $SystemDetails.FreeCap $global:ArrayName = $SANC.Name $global:ConnectionType = "CLI" } Write-DebugLog "End: If there are no errors reported on the console then the SAN connection object is set and ready to be used" $Info # Set to the prompt as "Array Name:Connection Type (WSAPI|CLI)\>" Function global:prompt { if ($global:SANConnection -ne $null){ $global:ArrayName + ":" + $global:ConnectionType + "\>" } else{ (Get-Location).Path + "\>" } } return $SANC } # End Function New-CLIConnection ################################################################################ ######################### FUNCTION New-PoshSshConnection ####################### ################################################################################ Function New-PoshSshConnection { <# .SYNOPSIS Builds a SAN Connection object using Posh SSH connection .DESCRIPTION Creates a SAN Connection object with the specified parameters. No connection is made by this cmdlet call, it merely builds the connection object. .EXAMPLE New-PoshSshConnection -SANUserName Administrator -SANPassword mypassword -ArrayNameOrIPAddress 10.1.1.1 Creates a SAN Connection object with the specified Array Name or Array IP Address .EXAMPLE New-PoshSshConnection -SANUserName Administrator -SANPassword mypassword -ArrayNameOrIPAddress 10.1.1.1 -AcceptKey Creates a SAN Connection object with the specified Array Name or Array IP Address .PARAMETER UserName Specify the SAN Administrator user name. .PARAMETER Password Specify the SAN Administrator password .PARAMETER ArrayNameOrIPAddress Specify Array Name or Array IP Address .Notes NAME: New-PoshSshConnection LASTEDIT: January 2020 KEYWORDS: New-PoshSshConnection .Link http://www.hpe.com #Requires PS -Version 3.0 #> [CmdletBinding()] param( [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true, HelpMessage="Enter Array Name or IP Address")] [System.String] $ArrayNameOrIPAddress=$null, [Parameter(Position=1, Mandatory=$true, ValueFromPipeline=$true)] [System.String] $SANUserName=$null, [Parameter(Position=2, Mandatory=$false, ValueFromPipeline=$true)] [System.String] $SANPassword=$null, [Parameter(Position=3, Mandatory=$false, ValueFromPipeline=$true)] [switch] $AcceptKey ) $Session # Check if our module loaded properly if (Get-Module -ListAvailable -Name Posh-SSH) { <# do nothing #> } else { try { # install the module automatically [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 iex (New-Object Net.WebClient).DownloadString("https://gist.github.com/darkoperator/6152630/raw/c67de4f7cd780ba367cccbc2593f38d18ce6df89/instposhsshdev") } catch { #$msg = "Error occurred while installing POSH SSH Module. `nPlease check the internet connection.`nOr Install POSH SSH Module using given Link. `nhttp://www.powershellmagazine.com/2014/07/03/posh-ssh-open-source-ssh-powershell-module/ `n " $msg = "Error occurred while installing POSH SSH Module. `nPlease check if internet is enabled. If internet is enabled and you are getting this error,`n Execute Save-Module -Name Posh-SSH -Path <path Ex D:\xxx> `n Then Install-Module -Name Posh-SSH `n If you are getting error like Save-Module is incorrect then `n Check you Power shell Version and Update to 5.1 for this particular Process `n Or visit https://www.powershellgallery.com/packages/Posh-SSH/2.0.2 `n" return "`n Failure : $msg" } } ##### #Write-DebugLog "start: Entering function New-PoshSshConnection. Validating IP Address format." $Debug ## Check IP Address Format #if(-not (Test-IPFormat $ArrayNameOrIPAddress)) #{ # Write-DebugLog "Stop: Invalid IP Address $ArrayNameOrIPAddress" "ERR:" # return "Failure : Invalid IP Address $ArrayNameOrIPAddress" #} <# # -------- Check any active CLI/PoshSSH session exists ------------ starts if($global:SANConnection){ $confirm = Read-Host "`nAn active CLI/PoshSSH session exists.`nDo you want to close the current CLI/PoshSSH session and start a new PoshSSH session (Enter y=yes n=no)" if ($confirm.tolower() -eq 'y'){ write-host "`nClosing the current CLI/PoshSSH connection." Close-Connection } elseif ($confirm.tolower() -eq 'n'){ return } } # -------- Check any active CLI/PoshSSH session exists ------------ ends # -------- Check any active WSAPI session exists ------------------ starts if($global:WsapiConnection){ $confirm = Read-Host "`nAn active WSAPI session exists.`nDo you want to close the current WSAPI session and start a new PoshSSH session (Enter y=yes n=no)" if ($confirm.tolower() -eq 'y'){ write-host "`nClosing the current WSAPI connection." Close-WSAPIConnection } elseif ($confirm.tolower() -eq 'n'){ return } } # -------- Check any active WSAPI session exists ------------------ ends #> # Authenticate try { if(!($SANPassword)) { $securePasswordStr = Read-Host "SANPassword" -AsSecureString $mycreds = New-Object System.Management.Automation.PSCredential ($SANUserName, $securePasswordStr) } else { $tempstring = convertto-securestring $SANPassword -asplaintext -force $mycreds = New-Object System.Management.Automation.PSCredential ($SANUserName, $tempstring) } try { if($AcceptKey) { #$Session = New-SSHSession -ComputerName $ArrayNameOrIPAddress -Credential (Get-Credential $SANUserName) -AcceptKey $Session = New-SSHSession -ComputerName $ArrayNameOrIPAddress -Credential $mycreds -AcceptKey } else { #$Session = New-SSHSession -ComputerName $ArrayNameOrIPAddress -Credential (Get-Credential $SANUserName) $Session = New-SSHSession -ComputerName $ArrayNameOrIPAddress -Credential $mycreds } } catch { $msg = "In function New-PoshSshConnection. " $msg+= $_.Exception.ToString() # Write-Exception function is used for exception logging so that it creates a separate exception log file. Write-Exception $msg -error return "Failure : $msg" } Write-DebugLog "Running: Executed . Check on PS console if there are any errors reported" $Debug if (!$Session) { return "New-PoshSshConnection command failed to connect the array." } } catch { $msg = "In function New-PoshSshConnection. " $msg+= $_.Exception.ToString() # Write-Exception function is used for exception logging so that it creates a separate exception log file. Write-Exception $msg -error return "Failure : $msg" } $global:SANObjArr += @() $global:SANObjArr1 += @() #write-host "objarray",$global:SANObjArr #write-host "objarray1",$global:SANObjArr1 if($global:SANConnection) { #write-host "In IF loop" $SANC = New-Object "_SANConnection" $SANC.SessionId = $Session.SessionId $SANC.IPAddress = $ArrayNameOrIPAddress $SANC.UserName = $SANUserName $SANC.epwdFile = "Secure String" $SANC.CLIType = "SshClient" $SANC.CLIDir = "Null" $global:SANConnection = $SANC $SystemDetails = Get-System $SANC.Name = $SystemDetails.Name $SANC.SystemVersion = Get-Version -S -B $SANC.Model = $SystemDetails.Model $SANC.Serial = $SystemDetails.Serial $SANC.TotalCapacityMiB = $SystemDetails.TotalCap $SANC.AllocatedCapacityMiB = $SystemDetails.AllocCap $SANC.FreeCapacityMiB = $SystemDetails.FreeCap $global:ArrayName = $SANC.Name $global:ConnectionType = "CLI" ###making multiple object support $SANC1 = New-Object "_TempSANConn" $SANC1.IPAddress = $ArrayNameOrIPAddress $SANC1.UserName = $SANUserName $SANC1.epwdFile = "Secure String" $SANC1.SessionId = $Session.SessionId $SANC1.CLIType = "SshClient" $SANC1.CLIDir = "Null" $global:SANObjArr += @($SANC) $global:SANObjArr1 += @($SANC1) } else { $global:SANObjArr = @() $global:SANObjArr1 = @() #write-host "In Else loop" $SANC = New-Object "_SANConnection" $SANC.IPAddress = $ArrayNameOrIPAddress $SANC.UserName = $SANUserName $SANC.epwdFile = "Secure String" $SANC.SessionId = $Session.SessionId $SANC.CLIType = "SshClient" $SANC.CLIDir = "Null" $global:SANConnection = $SANC $SystemDetails = Get-System $SANC.Name = $SystemDetails.Name $SANC.SystemVersion = Get-Version -S -B $SANC.Model = $SystemDetails.Model $SANC.Serial = $SystemDetails.Serial $SANC.TotalCapacityMiB = $SystemDetails.TotalCap $SANC.AllocatedCapacityMiB = $SystemDetails.AllocCap $SANC.FreeCapacityMiB = $SystemDetails.FreeCap $global:ArrayName = $SANC.Name $global:ConnectionType = "CLI" ###making multiple object support $SANC1 = New-Object "_TempSANConn" $SANC1.IPAddress = $ArrayNameOrIPAddress $SANC1.UserName = $SANUserName $SANC1.epwdFile = "Secure String" $SANC1.SessionId = $Session.SessionId $SANC1.CLIType = "SshClient" $SANC1.CLIDir = "Null" $global:SANObjArr += @($SANC) $global:SANObjArr1 += @($SANC1) } Write-DebugLog "End: If there are no errors reported on the console then the SAN connection object is set and ready to be used" $Info # Set to the prompt as "Array Name:Connection Type (WSAPI|CLI)\>" Function global:prompt { if ($global:SANConnection -ne $null){ $global:ArrayName + ":" + $global:ConnectionType + "\>" } else{ (Get-Location).Path + "\>" } } return $SANC }# End Function New-PoshSshConnection ###################################################################################################################### ## FUNCTION Set-PoshSshConnectionPasswordFile ###################################################################################################################### Function Set-PoshSshConnectionPasswordFile { <# .SYNOPSIS Creates a encrypted password file on client machine to be used by "Set-PoshSshConnectionUsingPasswordFile" .DESCRIPTION Creates an encrypted password file on client machine .EXAMPLE Set-PoshSshConnectionPasswordFile -ArrayNameOrIPAddress "15.1.1.1" -SANUserName "3parDemoUser" -$SANPassword "demoPass1" -epwdFile "C:\hpe3paradmepwd.txt" This examples stores the encrypted password file hpe3paradmepwd.txt on client machine c:\ drive, subsequent commands uses this encryped password file , This example authenticates the entered credentials if correct creates the password file. .PARAMETER SANUserName Specify the SAN SANUserName . .PARAMETER ArrayNameOrIPAddress Specify Array Name or Array IP Address .PARAMETER SANPassword Specify the Password with the Linked IP .PARAMETER epwdFile Specify the file location to create encrypted password file .Notes NAME: Set-PoshSshConnectionPasswordFile LASTEDIT: January 2020 KEYWORDS: Set-PoshSshConnectionPasswordFile .Link http://www.hpe.com #Requires PS -Version 2.0 #> [CmdletBinding()] param( [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true)] [System.String] $ArrayNameOrIPAddress=$null, [Parameter(Position=1, Mandatory=$true, ValueFromPipeline=$true)] [System.String] $SANUserName=$null, [Parameter(Position=2, Mandatory=$false, ValueFromPipeline=$true)] [System.String] $SANPassword=$null, [Parameter(Position=3, Mandatory=$true, ValueFromPipeline=$true)] [System.String] $epwdFile=$null, [Parameter(Position=4, Mandatory=$false, ValueFromPipeline=$true)] [switch] $AcceptKey ) ## Check IP Address Format #if(-not (Test-IPFormat $ArrayNameOrIPAddress)) #{ # Write-DebugLog "Stop: Invalid IP Address $ArrayNameOrIPAddress" "ERR:" # return "FAILURE : Invalid IP Address $ArrayNameOrIPAddress" #} #Write-DebugLog "Running: Completed validating IP address format." $Debug Write-DebugLog "Running: Authenticating credentials - for user $SANUserName and SANIP= $ArrayNameOrIPAddress" $Debug # Authenticate try { if(!($SANPassword)) { $securePasswordStr = Read-Host "SANPassword" -AsSecureString $mycreds = New-Object System.Management.Automation.PSCredential ($SANUserName, $securePasswordStr) $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($securePasswordStr) $tempPwd = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) } else { $tempstring = convertto-securestring $SANPassword -asplaintext -force $mycreds = New-Object System.Management.Automation.PSCredential ($SANUserName, $tempstring) $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($tempstring) $tempPwd = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) } if($AcceptKey) { #$Session = New-SSHSession -ComputerName $ArrayNameOrIPAddress -Credential (Get-Credential $SANUserName) -AcceptKey $Session = New-SSHSession -ComputerName $ArrayNameOrIPAddress -Credential $mycreds -AcceptKey } else { #$Session = New-SSHSession -ComputerName $ArrayNameOrIPAddress -Credential (Get-Credential $SANUserName) $Session = New-SSHSession -ComputerName $ArrayNameOrIPAddress -Credential $mycreds } Write-DebugLog "Running: Executed . Check on PS console if there are any errors reported" $Debug if (!$Session) { return "FAILURE : In function Set-PoshSshConnectionPasswordFile." } else { $RemveResult = Remove-SSHSession -Index $Session.SessionId } $Enc_Pass = Protect-String $tempPwd $Enc_Pass,$ArrayNameOrIPAddress,$SANUserName | Export-CliXml $epwdFile } catch { $msg = "In function Set-PoshSshConnectionPasswordFile. " $msg+= $_.Exception.ToString() Write-Exception $msg -error return "FAILURE : $msg `n credentials incorrect" } Write-DebugLog "Running: encrypted password file has been created successfully and the file location is $epwdFile " "INFO:" return "`n Success : encrypted SANPassword file has been created successfully and the file location : $epwdFile" } # End-of Set-PoshSshConnectionPasswordFile ##################################################################################### # Function Set-PoshSshConnectionUsingPasswordFile ##################################################################################### Function Set-PoshSshConnectionUsingPasswordFile { <# .SYNOPSIS Creates a SAN Connection object using Encrypted password file .DESCRIPTION Creates a SAN Connection object using Encrypted password file. No connection is made by this cmdlet call, it merely builds the connection object. .EXAMPLE Set-PoshSshConnectionUsingPasswordFile -ArrayNameOrIPAddress 10.1.1.1 -SANUserName "3parUser" -epwdFile "C:\HPE3PARepwdlogin.txt" Creates a SAN Connection object with the specified the Array Name or Array IP Address and password file .PARAMETER ArrayNameOrIPAddress Specify Array Name or Array IP Address .PARAMETER SANUserName Specify the SAN UserName. .PARAMETER epwdFile Specify the encrypted password file location , example “c:\hpe3parstoreserv244.txt” To create encrypted password file use “New-3parSSHCONNECTION_PassFile” cmdlet .Notes NAME: Set-PoshSshConnectionUsingPasswordFile LASTEDIT: January 2020 KEYWORDS: Set-PoshSshConnectionUsingPasswordFile .Link http://www.hpe.com #Requires PS -Version 3.0 #Requires HPE 3PAR cli.exe #> [CmdletBinding()] param( [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true)] [System.String] $ArrayNameOrIPAddress=$null, [Parameter(Position=1, Mandatory=$true, ValueFromPipeline=$true)] [System.String] $SANUserName, [Parameter(Position=2, Mandatory=$true, ValueFromPipeline=$true)] [System.String] $epwdFile ) try{ if( -not (Test-Path $epwdFile)) { Write-DebugLog "Running: Path for encrypted password file was not found. Now created new epwd file." "INFO:" return " Encrypted password file does not exist , create encrypted password file using 'Set-3parSSHConnectionPasswordFile' " } Write-DebugLog "Running: Patch for encrypted password file ." "INFO:" $tempFile=$epwdFile $Temp=import-CliXml $tempFile $pass=$temp[0] $ip=$temp[1] $user=$temp[2] if($ip -eq $ArrayNameOrIPAddress) { if($user -eq $SANUserName) { $Passs = UnProtect-String $pass #New-SSHConnection -SANUserName $SANUserName -SANPassword $Passs -ArrayNameOrIPAddress $ArrayNameOrIPAddress -SSHDir "C:\plink" New-PoshSshConnection -ArrayNameOrIPAddress $ArrayNameOrIPAddress -SANUserName $SANUserName -SANPassword $Passs } else { Return "Password file SANUserName $user and entered SANUserName $SANUserName dose not match . " Write-DebugLog "Running: Password file SANUserName $user and entered SANUserName $SANUserName dose not match ." "INFO:" } } else { Return "Password file ip $ip and entered ip $ArrayNameOrIPAddress dose not match" Write-DebugLog "Password file ip $ip and entered ip $ArrayNameOrIPAddress dose not match." "INFO:" } } catch { $msg = "In function Set-PoshSshConnectionUsingPasswordFile. " $msg+= $_.Exception.ToString() # Write-Exception function is used for exception logging so that it creates a separate exception log file. Write-Exception $msg -error return "FAILURE : $msg" } } #End Function Set-PoshSshConnectionUsingPasswordFile ###################################################################################################################### ## FUNCTION Get-UserConnectionTemp ###################################################################################################################### Function Get-UserConnectionTemp { <# .SYNOPSIS Displays information about users who are currently connected (logged in) to the storage system. .DESCRIPTION Displays information about users who are currently connected (logged in) to the storage system. .EXAMPLE Get-UserConnection -ArrayNameOrIPAddress 10.1.1.1 -CLIDir "C:\cli.exe" -epwdFile "C:\HPE3parepwdlogin.txt" -Option current Shows all information about the current connection only. .EXAMPLE Get-UserConnection -ArrayNameOrIPAddress 10.1.1.1 -CLIDir "C:\cli.exe" -epwdFile "C:\HPE3parepwdlogin.txt" Shows information about users who are currently connected (logged in) to the storage system. .PARAMETER ArrayNameOrIPAddress Specify Array Name or Array IP Address .PARAMETER CLIDir Specify the absolute path of HPE 3PAR cli.exe. Default is "C:\Program Files (x86)\Hewlett Packard Enterprise\HPE 3PAR CLI\bin" .PARAMETER epwdFile Specify the encrypted password file , if file does not exists it will create encrypted file using deviceip,username and password .PARAMETER Option current Shows all information about the current connection only. .Notes NAME: Get-UserConnectionTemp LASTEDIT: January 2020 KEYWORDS: Get-UserConnectionTemp .Link http://www.hpe.com #Requires PS -Version 3.0 #Requires HPE 3PAR cli.exe #> [CmdletBinding()] param( [Parameter(Position=0,Mandatory=$false, ValueFromPipeline=$true)] [System.string] $CLIDir="C:\Program Files (x86)\Hewlett Packard Enterprise\HPE 3PAR CLI\bin", [Parameter(Position=1,Mandatory=$true, ValueFromPipeline=$true)] [System.string] $ArrayNameOrIPAddress=$null, [Parameter(Position=2,Mandatory=$true, ValueFromPipeline=$true)] [System.string] $epwdFile ="C:\HPE3parepwdlogin.txt", [Parameter(Position=3,Mandatory=$false, ValueFromPipeline=$true)] [System.string] $Option ) if( Test-Path $epwdFile) { Write-DebugLog "Running: password file was found , it will use the mentioned file" "INFO:" } #$passwordFile = $epwdFile #$cmd1 = $CLIDir+"\showuserconn.bat" $cmd2 = "showuserconn " $options1 = "current" if(!($options1 -eq $option)) { return "Failure : option should be in ( $options1 )" } if($option -eq "current") { $cmd2 += " -current " } #& $cmd1 -sys $ArrayNameOrIPAddress -file $passwordFile $result = Invoke-CLI -DeviceIPAddress $ArrayNameOrIPAddress -CLIDir $CLIDir -epwdFile $epwdFile -cmd $cmd2 $count = $result.count - 3 $tempFile = [IO.Path]::GetTempFileName() Add-Content -Path $tempFile -Value "Id,Name,IP_Addr,Role,Connected_since,Current,Client,ClientName" foreach($s in $result[1..$count]) { $s= [regex]::Replace($s,"^ +","") $s= [regex]::Replace($s," +"," ") $s= [regex]::Replace($s," ",",") $s = $s.trim() Add-Content -Path $tempFile -Value $s } Import-CSV $tempFile del $tempFile } ############################################################################################################################################ ## FUNCTION Test-CLIObject ############################################################################################################################################ Function Test-CLIObject { Param( [string]$ObjectType, [string]$ObjectName , [string]$ObjectMsg = $ObjectType, $SANConnection = $global:SANConnection ) $IsObjectExisted = $True $ObjCmd = $ObjectType -replace ' ', '' $Cmds = "show$ObjCmd $ObjectName" $Result = Invoke-CLICommand -Connection $SANConnection -cmds $Cmds if ($Result -like "no $ObjectMsg listed") { $IsObjectExisted = $false } return $IsObjectExisted } # End FUNCTION Test-CLIObject Export-ModuleMember Close-Connection , Get-CmdList , Get-FcPorts , Get-FcPortsToCsv , Get-ConnectedSession , New-CLIConnection , New-PoshSshConnection , Set-PoshSshConnectionPasswordFile , Set-PoshSshConnectionUsingPasswordFile # SIG # Begin signature block # MIIh0AYJKoZIhvcNAQcCoIIhwTCCIb0CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCDcDtbtYcyfVNG # ixiR0MJDDrd8VAbbVRDq1Dr6ZkXcZ6CCEKswggUpMIIEEaADAgECAhB4Lu4fcD9z # xUgD+jf1OoqlMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAkdCMRswGQYDVQQI # ExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGDAWBgNVBAoT # D1NlY3RpZ28gTGltaXRlZDEkMCIGA1UEAxMbU2VjdGlnbyBSU0EgQ29kZSBTaWdu # aW5nIENBMB4XDTIxMDUyODAwMDAwMFoXDTIyMDUyODIzNTk1OVowgZAxCzAJBgNV # BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlQYWxvIEFsdG8x # KzApBgNVBAoMIkhld2xldHQgUGFja2FyZCBFbnRlcnByaXNlIENvbXBhbnkxKzAp # BgNVBAMMIkhld2xldHQgUGFja2FyZCBFbnRlcnByaXNlIENvbXBhbnkwggEiMA0G # CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDmclZSXJBXA55ijwwFymuq+Y4F/quF # mm2vRdEmjFhzRvTpnGjIYtVcG11ka4JGCROmNVDZGAelnqcXn5DKO710j5SICTBC # 5gXOLwga7usifs21W+lVT0BsZTiUnFu4hEhuFTlahJIEvPGVgO1GBcuItD2QqB4q # 9j15GDI5nGBSzIyJKMctcIalxsTSPG1kiDbLkdfsIivhe9u9m8q6NRqDUaYYQTN+ # /qGCqVNannMapH8tNHqFb6VdzUFI04t7kFtSk00AkdD6qUvA4u8mL2bUXAYz8K5m # nrFs+ckx5Yqdxfx68EO26Bt2qbz/oTHxE6FiVzsDl90bcUAah2l976ebAgMBAAGj # ggGQMIIBjDAfBgNVHSMEGDAWgBQO4TqoUzox1Yq+wbutZxoDha00DjAdBgNVHQ4E # FgQUlC56g+JaYFsl5QWK2WDVOsG+pCEwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB # /wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEQYJYIZIAYb4QgEBBAQDAgQQMEoG # A1UdIARDMEEwNQYMKwYBBAGyMQECAQMCMCUwIwYIKwYBBQUHAgEWF2h0dHBzOi8v # c2VjdGlnby5jb20vQ1BTMAgGBmeBDAEEATBDBgNVHR8EPDA6MDigNqA0hjJodHRw # Oi8vY3JsLnNlY3RpZ28uY29tL1NlY3RpZ29SU0FDb2RlU2lnbmluZ0NBLmNybDBz # BggrBgEFBQcBAQRnMGUwPgYIKwYBBQUHMAKGMmh0dHA6Ly9jcnQuc2VjdGlnby5j # b20vU2VjdGlnb1JTQUNvZGVTaWduaW5nQ0EuY3J0MCMGCCsGAQUFBzABhhdodHRw # Oi8vb2NzcC5zZWN0aWdvLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAY+1n2UUlQU6Z # VoEVaZKqZf/zrM/d7Kbx+S/t8mR2E+uNXStAnwztElqrm3fSr+5LMRzBhrYiSmea # w9c/0c7qFO9mt8RR2q2uj0Huf+oAMh7TMuMKZU/XbT6tS1e15B8ZhtqOAhmCug6s # DuNvoxbMpokYevpa24pYn18ELGXOUKlqNUY2qOs61GVvhG2+V8Hl/pajE7yQ4diz # iP7QjMySms6BtZV5qmjIFEWKY+UTktUcvN4NVA2J0TV9uunDbHRt4xdY8TF/Clgz # Z/MQHJ/X5yX6kupgDeN2t3o+TrColetBnwk/SkJEsUit0JapAiFUx44j4w61Qanb # Zmi0tr8YGDCCBYEwggRpoAMCAQICEDlyRDr5IrdR19NsEN0xNZUwDQYJKoZIhvcN # AQEMBQAwezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3Rl # cjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQx # ITAfBgNVBAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0xOTAzMTIwMDAw # MDBaFw0yODEyMzEyMzU5NTlaMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKTmV3 # IEplcnNleTEUMBIGA1UEBxMLSmVyc2V5IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VS # VFJVU1QgTmV0d29yazEuMCwGA1UEAxMlVVNFUlRydXN0IFJTQSBDZXJ0aWZpY2F0 # aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIAS # ZRc2DsPbCLPQrFcNdu3NJ9NMrVCDYeKqIE0JLWQJ3M6Jn8w9qez2z8Hc8dOx1ns3 # KBErR9o5xrw6GbRfpr19naNjQrZ28qk7K5H44m/Q7BYgkAk+4uh0yRi0kdRiZNt/ # owbxiBhqkCI8vP4T8IcUe/bkH47U5FHGEWdGCFHLhhRUP7wz/n5snP8WnRi9UY41 # pqdmyHJn2yFmsdSbeAPAUDrozPDcvJ5M/q8FljUfV1q3/875PbcstvZU3cjnEjpN # rkyKt1yatLcgPcp/IjSufjtoZgFE5wFORlObM2D3lL5TN5BzQ/Myw1Pv26r+dE5p # x2uMYJPexMcM3+EyrsyTO1F4lWeL7j1W/gzQaQ8bD/MlJmszbfduR/pzQ+V+DqVm # sSl8MoRjVYnEDcGTVDAZE6zTfTen6106bDVc20HXEtqpSQvf2ICKCZNijrVmzyWI # zYS4sT+kOQ/ZAp7rEkyVfPNrBaleFoPMuGfi6BOdzFuC00yz7Vv/3uVzrCM7LQC/ # NVV0CUnYSVgaf5I25lGSDvMmfRxNF7zJ7EMm0L9BX0CpRET0medXh55QH1dUqD79 # dGMvsVBlCeZYQi5DGky08CVHWfoEHpPUJkZKUIGy3r54t/xnFeHJV4QeD2PW6WK6 # 1l9VLupcxigIBCU5uA4rqfJMlxwHPw1S9e3vL4IPAgMBAAGjgfIwge8wHwYDVR0j # BBgwFoAUoBEKIz6W8Qfs4q8p74Klf9AwpLQwHQYDVR0OBBYEFFN5v1qqK0rPVIDh # 2JvAnfKyA2bLMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MBEGA1Ud # IAQKMAgwBgYEVR0gADBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsLmNvbW9k # b2NhLmNvbS9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDA0BggrBgEFBQcBAQQo # MCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmNvbW9kb2NhLmNvbTANBgkqhkiG # 9w0BAQwFAAOCAQEAGIdR3HQhPZyK4Ce3M9AuzOzw5steEd4ib5t1jp5y/uTW/qof # nJYt7wNKfq70jW9yPEM7wD/ruN9cqqnGrvL82O6je0P2hjZ8FODN9Pc//t64tIrw # kZb+/UNkfv3M0gGhfX34GRnJQisTv1iLuqSiZgR2iJFODIkUzqJNyTKzuugUGrxx # 8VvwQQuYAAoiAxDlDLH5zZI3Ge078eQ6tvlFEyZ1r7uq7z97dzvSxAKRPRkA0xdc # Ods/exgNRc2ThZYvXd9ZFk8/Ub3VRRg/7UqO6AZhdCMWtQ1QcydER38QXYkqa4Ux # FMToqWpMgLxqeM+4f452cpkMnf7XkQgWoaNflTCCBfUwggPdoAMCAQICEB2iSDBv # myYY0ILgln0z02owDQYJKoZIhvcNAQEMBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpOZXcgSmVyc2V5MRQwEgYDVQQHEwtKZXJzZXkgQ2l0eTEeMBwGA1UEChMV # VGhlIFVTRVJUUlVTVCBOZXR3b3JrMS4wLAYDVQQDEyVVU0VSVHJ1c3QgUlNBIENl # cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTE4MTEwMjAwMDAwMFoXDTMwMTIzMTIz # NTk1OVowfDELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3Rl # cjEQMA4GA1UEBxMHU2FsZm9yZDEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSQw # IgYDVQQDExtTZWN0aWdvIFJTQSBDb2RlIFNpZ25pbmcgQ0EwggEiMA0GCSqGSIb3 # DQEBAQUAA4IBDwAwggEKAoIBAQCGIo0yhXoYn0nwli9jCB4t3HyfFM/jJrYlZilA # hlRGdDFixRDtsocnppnLlTDAVvWkdcapDlBipVGREGrgS2Ku/fD4GKyn/+4uMyD6 # DBmJqGx7rQDDYaHcaWVtH24nlteXUYam9CflfGqLlR5bYNV+1xaSnAAvaPeX7Wpy # vjg7Y96Pv25MQV0SIAhZ6DnNj9LWzwa0VwW2TqE+V2sfmLzEYtYbC43HZhtKn52B # xHJAteJf7wtF/6POF6YtVbC3sLxUap28jVZTxvC6eVBJLPcDuf4vZTXyIuosB69G # 2flGHNyMfHEo8/6nxhTdVZFuihEN3wYklX0Pp6F8OtqGNWHTAgMBAAGjggFkMIIB # YDAfBgNVHSMEGDAWgBRTeb9aqitKz1SA4dibwJ3ysgNmyzAdBgNVHQ4EFgQUDuE6 # qFM6MdWKvsG7rWcaA4WtNA4wDgYDVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYB # Af8CAQAwHQYDVR0lBBYwFAYIKwYBBQUHAwMGCCsGAQUFBwMIMBEGA1UdIAQKMAgw # BgYEVR0gADBQBgNVHR8ESTBHMEWgQ6BBhj9odHRwOi8vY3JsLnVzZXJ0cnVzdC5j # b20vVVNFUlRydXN0UlNBQ2VydGlmaWNhdGlvbkF1dGhvcml0eS5jcmwwdgYIKwYB # BQUHAQEEajBoMD8GCCsGAQUFBzAChjNodHRwOi8vY3J0LnVzZXJ0cnVzdC5jb20v # VVNFUlRydXN0UlNBQWRkVHJ1c3RDQS5jcnQwJQYIKwYBBQUHMAGGGWh0dHA6Ly9v # Y3NwLnVzZXJ0cnVzdC5jb20wDQYJKoZIhvcNAQEMBQADggIBAE1jUO1HNEphpNve # aiqMm/EAAB4dYns61zLC9rPgY7P7YQCImhttEAcET7646ol4IusPRuzzRl5ARokS # 9At3WpwqQTr81vTr5/cVlTPDoYMot94v5JT3hTODLUpASL+awk9KsY8k9LOBN9O3 # ZLCmI2pZaFJCX/8E6+F0ZXkI9amT3mtxQJmWunjxucjiwwgWsatjWsgVgG10Xkp1 # fqW4w2y1z99KeYdcx0BNYzX2MNPPtQoOCwR/oEuuu6Ol0IQAkz5TXTSlADVpbL6f # ICUQDRn7UJBhvjmPeo5N9p8OHv4HURJmgyYZSJXOSsnBf/M6BZv5b9+If8AjntIe # Q3pFMcGcTanwWbJZGehqjSkEAnd8S0vNcL46slVaeD68u28DECV3FTSK+TbMQ5Lk # uk/xYpMoJVcp+1EZx6ElQGqEV8aynbG8HArafGd+fS7pKEwYfsR7MUFxmksp7As9 # V1DSyt39ngVR5UR43QHesXWYDVQk/fBO4+L4g71yuss9Ou7wXheSaG3IYfmm8SoK # C6W59J7umDIFhZ7r+YMp08Ysfb06dy6LN0KgaoLtO0qqlBCk4Q34F8W2WnkzGJLj # tXX4oemOCiUe5B7xn1qHI/+fpFGe+zmAEc3btcSnqIBv5VPU4OOiwtJbGvoyJi1q # V3AcPKRYLqPzW0sH3DJZ84enGm1YMYIQezCCEHcCAQEwgZAwfDELMAkGA1UEBhMC # R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9y # ZDEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSQwIgYDVQQDExtTZWN0aWdvIFJT # QSBDb2RlIFNpZ25pbmcgQ0ECEHgu7h9wP3PFSAP6N/U6iqUwDQYJYIZIAWUDBAIB # BQCgfDAQBgorBgEEAYI3AgEMMQIwADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIB # BDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQg # qQOCRp2FbX2dyWfFHoaenIevp5gyGw5GJn74zDiv9sowDQYJKoZIhvcNAQEBBQAE # ggEAMyYH5+s1eTVlv4/Ng+rlrFeNXkR/qGqpVDMtoHXMhi85dWwADA9UBP1nqhhx # WdAYoxp+IzVxp8IiBtg3SKzvBpZU4IVIIGy2Od99K8RFr7eqBjlILeocS58EUwFb # nKlIQAkt7jLv9ZpyIocu9j38WpSdms9eHHw10O2fZz6osXQEqnkAqFufCCwEBZNo # 13/ueoO8DhUD2MKMVLxpcrLDMTNgEnby+EtFBQaYh96uxWlUOKX1cSoMV8eDahxP # 0RXY18erYoMIQgDT0LKCPJEuY9bRm1M8kPhxUUo3vyPYFliuVaNY2CaU8TBFnkZs # kB1xwwPHIUvX83jvbou2/vL11qGCDj0wgg45BgorBgEEAYI3AwMBMYIOKTCCDiUG # CSqGSIb3DQEHAqCCDhYwgg4SAgEDMQ0wCwYJYIZIAWUDBAIBMIIBDwYLKoZIhvcN # AQkQAQSggf8EgfwwgfkCAQEGC2CGSAGG+EUBBxcDMDEwDQYJYIZIAWUDBAIBBQAE # IK0F9WNEyXBo9jASz2vcdomkVA7EgiTifrqmFu8xDM5aAhUAhm7JXVTfRgHfNt8u # SYxttl++o+0YDzIwMjEwNjE5MDQxMjQyWjADAgEeoIGGpIGDMIGAMQswCQYDVQQG # EwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNVBAsTFlN5 # bWFudGVjIFRydXN0IE5ldHdvcmsxMTAvBgNVBAMTKFN5bWFudGVjIFNIQTI1NiBU # aW1lU3RhbXBpbmcgU2lnbmVyIC0gRzOgggqLMIIFODCCBCCgAwIBAgIQewWx1Elo # UUT3yYnSnBmdEjANBgkqhkiG9w0BAQsFADCBvTELMAkGA1UEBhMCVVMxFzAVBgNV # BAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3 # b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRo # b3JpemVkIHVzZSBvbmx5MTgwNgYDVQQDEy9WZXJpU2lnbiBVbml2ZXJzYWwgUm9v # dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNjAxMTIwMDAwMDBaFw0zMTAx # MTEyMzU5NTlaMHcxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jw # b3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEoMCYGA1UE # AxMfU3ltYW50ZWMgU0hBMjU2IFRpbWVTdGFtcGluZyBDQTCCASIwDQYJKoZIhvcN # AQEBBQADggEPADCCAQoCggEBALtZnVlVT52Mcl0agaLrVfOwAa08cawyjwVrhpon # ADKXak3JZBRLKbvC2Sm5Luxjs+HPPwtWkPhiG37rpgfi3n9ebUA41JEG50F8eRzL # y60bv9iVkfPw7mz4rZY5Ln/BJ7h4OcWEpe3tr4eOzo3HberSmLU6Hx45ncP0mqj0 # hOHE0XxxxgYptD/kgw0mw3sIPk35CrczSf/KO9T1sptL4YiZGvXA6TMU1t/HgNuR # 7v68kldyd/TNqMz+CfWTN76ViGrF3PSxS9TO6AmRX7WEeTWKeKwZMo8jwTJBG1kO # qT6xzPnWK++32OTVHW0ROpL2k8mc40juu1MO1DaXhnjFoTcCAwEAAaOCAXcwggFz # MA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMGYGA1UdIARfMF0w # WwYLYIZIAYb4RQEHFwMwTDAjBggrBgEFBQcCARYXaHR0cHM6Ly9kLnN5bWNiLmNv # bS9jcHMwJQYIKwYBBQUHAgIwGRoXaHR0cHM6Ly9kLnN5bWNiLmNvbS9ycGEwLgYI # KwYBBQUHAQEEIjAgMB4GCCsGAQUFBzABhhJodHRwOi8vcy5zeW1jZC5jb20wNgYD # VR0fBC8wLTAroCmgJ4YlaHR0cDovL3Muc3ltY2IuY29tL3VuaXZlcnNhbC1yb290 # LmNybDATBgNVHSUEDDAKBggrBgEFBQcDCDAoBgNVHREEITAfpB0wGzEZMBcGA1UE # AxMQVGltZVN0YW1wLTIwNDgtMzAdBgNVHQ4EFgQUr2PWyqNOhXLgp7xB8ymiOH+A # dWIwHwYDVR0jBBgwFoAUtnf6aUhHn1MS1cLqBzJ2B9GXBxkwDQYJKoZIhvcNAQEL # BQADggEBAHXqsC3VNBlcMkX+DuHUT6Z4wW/X6t3cT/OhyIGI96ePFeZAKa3mXfSi # 2VZkhHEwKt0eYRdmIFYGmBmNXXHy+Je8Cf0ckUfJ4uiNA/vMkC/WCmxOM+zWtJPI # TJBjSDlAIcTd1m6JmDy1mJfoqQa3CcmPU1dBkC/hHk1O3MoQeGxCbvC2xfhhXFL1 # TvZrjfdKer7zzf0D19n2A6gP41P3CnXsxnUuqmaFBJm3+AZX4cYO9uiv2uybGB+q # ueM6AL/OipTLAduexzi7D1Kr0eOUA2AKTaD+J20UMvw/l0Dhv5mJ2+Q5FL3a5NPD # 6itas5VYVQR9x5rsIwONhSrS/66pYYEwggVLMIIEM6ADAgECAhB71OWvuswHP6EB # IwQiQU0SMA0GCSqGSIb3DQEBCwUAMHcxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRT # eW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0 # d29yazEoMCYGA1UEAxMfU3ltYW50ZWMgU0hBMjU2IFRpbWVTdGFtcGluZyBDQTAe # Fw0xNzEyMjMwMDAwMDBaFw0yOTAzMjIyMzU5NTlaMIGAMQswCQYDVQQGEwJVUzEd # MBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNVBAsTFlN5bWFudGVj # IFRydXN0IE5ldHdvcmsxMTAvBgNVBAMTKFN5bWFudGVjIFNIQTI1NiBUaW1lU3Rh # bXBpbmcgU2lnbmVyIC0gRzMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQCvDoqq+Ny/aXtUF3FHCb2NPIH4dBV3Z5Cc/d5OAp5LdvblNj5l1SQgbTD53R2D # 6T8nSjNObRaK5I1AjSKqvqcLG9IHtjy1GiQo+BtyUT3ICYgmCDr5+kMjdUdwDLNf # W48IHXJIV2VNrwI8QPf03TI4kz/lLKbzWSPLgN4TTfkQyaoKGGxVYVfR8QIsxLWr # 8mwj0p8NDxlsrYViaf1OhcGKUjGrW9jJdFLjV2wiv1V/b8oGqz9KtyJ2ZezsNvKW # lYEmLP27mKoBONOvJUCbCVPwKVeFWF7qhUhBIYfl3rTTJrJ7QFNYeY5SMQZNlANF # xM48A+y3API6IsW0b+XvsIqbAgMBAAGjggHHMIIBwzAMBgNVHRMBAf8EAjAAMGYG # A1UdIARfMF0wWwYLYIZIAYb4RQEHFwMwTDAjBggrBgEFBQcCARYXaHR0cHM6Ly9k # LnN5bWNiLmNvbS9jcHMwJQYIKwYBBQUHAgIwGRoXaHR0cHM6Ly9kLnN5bWNiLmNv # bS9ycGEwQAYDVR0fBDkwNzA1oDOgMYYvaHR0cDovL3RzLWNybC53cy5zeW1hbnRl # Yy5jb20vc2hhMjU2LXRzcy1jYS5jcmwwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgw # DgYDVR0PAQH/BAQDAgeAMHcGCCsGAQUFBwEBBGswaTAqBggrBgEFBQcwAYYeaHR0 # cDovL3RzLW9jc3Aud3Muc3ltYW50ZWMuY29tMDsGCCsGAQUFBzAChi9odHRwOi8v # dHMtYWlhLndzLnN5bWFudGVjLmNvbS9zaGEyNTYtdHNzLWNhLmNlcjAoBgNVHREE # ITAfpB0wGzEZMBcGA1UEAxMQVGltZVN0YW1wLTIwNDgtNjAdBgNVHQ4EFgQUpRMB # qZ+FzBtuFh5fOzGqeTYAex0wHwYDVR0jBBgwFoAUr2PWyqNOhXLgp7xB8ymiOH+A # dWIwDQYJKoZIhvcNAQELBQADggEBAEaer/C4ol+imUjPqCdLIc2yuaZycGMv41Up # ezlGTud+ZQZYi7xXipINCNgQujYk+gp7+zvTYr9KlBXmgtuKVG3/KP5nz3E/5jMJ # 2aJZEPQeSv5lzN7Ua+NSKXUASiulzMub6KlN97QXWZJBw7c/hub2wH9EPEZcF1rj # pDvVaSbVIX3hgGd+Yqy3Ti4VmuWcI69bEepxqUH5DXk4qaENz7Sx2j6aescixXTN # 30cJhsT8kSWyG5bphQjo3ep0YG5gpVZ6DchEWNzm+UgUnuW/3gC9d7GYFHIUJN/H # ESwfAD/DSxTGZxzMHgajkF9cVIs+4zNbgg/Ft4YCTnGf6WZFP3YxggJaMIICVgIB # ATCBizB3MQswCQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRp # b24xHzAdBgNVBAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxKDAmBgNVBAMTH1N5 # bWFudGVjIFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0ECEHvU5a+6zAc/oQEjBCJBTRIw # CwYJYIZIAWUDBAIBoIGkMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAcBgkq # hkiG9w0BCQUxDxcNMjEwNjE5MDQxMjQyWjAvBgkqhkiG9w0BCQQxIgQgn1nCXi8x # S3t8UUG3g07/UEN4SgWR7rHva2mSFrI0vZEwNwYLKoZIhvcNAQkQAi8xKDAmMCQw # IgQgxHTOdgB9AjlODaXk3nwUxoD54oIBPP72U+9dtx/fYfgwCwYJKoZIhvcNAQEB # BIIBAAVIAKdxPnbWNziq0tQ93CTlfxq2Yv1/fRsB1Pd0SEdvOiY8o47i6IQG3FrE # y712LDSxn5bHg/0Qgmg9O8zPiHRRa7QIpyBi37S9fGb4446GHlG/znpdhnLB5YN8 # 8UaB24ZhmsQhG5cjwVNCdkvAbC45myGVyALvH3xToudET9VR+64BPt+b/6EUHKZ4 # G0UL7PCf6WlehSYGz+smhxiKlbtv5FLpRfnsGnhgPSrVBnAPWF+fisGMfZNtddsb # ZWYHCht3YNkT1lGJDLrRaTmlcDkI8ML2WnWPh0+uoMdS7C8FqZWPsv4D3hnytdxp # 09t9mj24Xr1oho6W9oGJTK7xUvA= # SIG # End signature block |