PureMetrics.psm1
# Using using module .\Progress.psm1 # # Module Declarations $FEHeader = @("Host","Date","Time","Zone","Initiator-WWN","Target","Target-WWN","IOps","Relative-IO"); $Script:PureMetricsCSV = "PureMetrics.csv"; # # Non-Exported Functions . $PSScriptRoot\Local\Get-ArrayInfoXML.ps1 . $PSScriptRoot\Local\Export-PSCredentials.ps1 . $PSScriptRoot\Local\Import-PSCredentials.ps1 # Utility Functions function Convert-Ztime { param ( #expected format:2016-10-30T00:19:32Z [Parameter(Position=0, Mandatory=$True, ValueFromPipeline=$True)] [ValidatePattern("^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z")] [string]$zulustr ) $t1 = $zulustr.Replace("-",""); $t2 = $t1.Replace("T",""); $t1 = $t2.Replace(":",""); $t2 = $t1.Replace("Z",""); $localtime = [DateTime]::ParseExact($t2,"yyyyMMddHHmmss",$null,"AssumeUniversal") return $localtime; } function Set-Credentials { Param ([string]$borg, [string]$username) $credBase = "-" + $borg + "-" + $env:COMPUTERNAME + $credExt; $Script:credfilename = $Global:CRPath + "\" + $username + $credBase; if (!(Test-Path $Script:credfilename)) { # create cache credential Write-Host "Pure Credentials require for $borg Storage" -ForegroundColor Green Export-PSCredential $username $credBase Start-Sleep 3; } $Script:cred = Import-PSCredential $Script:credfilename $Script:org = $borg; } function Get-InitiatorCount { begin { [int]$objcnt = 0; $Script:InitCnt = 0;} process { $objcnt++ } end { $Script:InitCnt = $objcnt } } filter isNumeric() { return $_ -is [byte] -or $_ -is [int16] -or $_ -is [int32] -or $_ -is [int64] ` -or $_ -is [sbyte] -or $_ -is [uint16] -or $_ -is [uint32] -or $_ -is [uint64] ` -or $_ -is [float] -or $_ -is [double] -or $_ -is [decimal] } # # Report Functions function Get-SGMetrics-Summary { param ([string]$sgID, [int]$count, [string]$sid4) begin { $tsmax = "0"; $IOmax = "0"; $IOsum = 0; $RIOmax = "0"; $RIOsum = 0; $WIOmax = "0"; $MBmax = "0"; $MBsum = 0; $RMBmax = "0"; $RMBsum = 0; $WMBmax = "0"; $WMBsum = 0; $RTmax = "0"; $RTsum = 0; $RRTmax = "0"; $RRTsum = 0; $WRTmax = "0"; $WRTsum = 0; $RPmax = "0"; $RPsum = 0; $WPmax = "0"; $WPsum = 0; $IOSmax = "0"; $IOSsum = 0; $RSmax = "0"; $RSsum = 0; $WSmax = "0"; $WSsum = 0; $BSmax = "0"; $BSsum = 0; $XSTmax = "0"; $XSTsum = 0; $mSTmax = "0"; $MSTsum = 0; } process { if ($_."IO (ps)" -gt $IOmax) { $IOmax = $_."IO (ps)"; } $IOsum += $_."IO (ps)"; if ($_."Read IO (ps)" -gt $RIOmax) { $RIOmax = $_."Read IO (ps)"; } $RIOsum += $_."Read IO (ps)"; if ($_."Write IO (ps)" -gt $WIOmax) { $WIOmax = $_."Write IO (ps)"; } $WIOsum += $_."Write IO (ps)"; if ($_."MB (ps)" -gt $MBmax) { $MBmax = $_."MB (ps)"; } $MBsum += $_."MB (ps)"; if ($_."Read MB (ps)" -gt $RMBmax) { $RMBmax = $_."Read MB (ps)"; } $RMBsum += $_."Read MB (ps)"; if ($_."Write MB (ps)" -gt $WMBmax) { $WMBmax = $_."Write MB (ps)"; } $WMBsum += $_."Write MB (ps)"; if ($_."Response Time (ms)" -gt $RTmax) { $RTmax = $_."Response Time (ms)"; } $RTsum += $_."Response Time (ms)"; if ($_."ReadRT(ms)" -gt $RRTmax) { $RRTmax = $_."ReadRT(ms)"; } $RRTsum += $_."ReadRT(ms)"; if ($_."WriteRT(ms)" -gt $WRTmax) { $WRTmax = $_."WriteRT(ms)"; } $WRTsum += $_."WriteRT(ms)"; if ($_."Read %" -gt $RPmax) { $RPmax = $_."Read %"; } $RPsum += $_."Read %"; if ($_."Write %" -gt $WPmax) { $WPmax = $_."Write %"; } $WPsum += $_."Write %"; if ($_."AvgIOSize" -gt $IOSmax) { $IOSmax = $_."AvgIOSize"; } $IOSsum += $_."AvgIOSize"; if ($_."AvgReadSize" -gt $RSmax) { $RSmax = $_."AvgReadSize"; } $RSsum += $_."AvgReadSize"; if ($_."AvgWriteSize" -gt $WSmax) { $WSmax = $_."AvgWriteSize"; } $WSsum += $_."AvgWriteSize"; <# if ($_."BlockSize" -gt $BSmax) { $BSmax = $_."BlockSize"; } $BSsum += $_."BlockSize"; if ($_."MaxIOServiceTime" -gt $XSTmax) { $XSTmax = $_."MaxIOServiceTime"; } $XSTsum += $_."MaxIOServiceTime"; if ($_."MinIOServiceTime" -gt $mSTmax) { $mSTmax = $_."MinIOServiceTime"; } $mSTsum += $_."MinIOServiceTime"; #> } end { $Sum = New-Object -typename PSObject; $Sum | Add-Member -MemberType NoteProperty -Name "TimeStamp" -Value $_.timestamp $Sum | Add-Member -MemberType NoteProperty -Name "VMAX" -Value $sid4 $Sum | Add-Member -MemberType NoteProperty -Name "SG" -Value $sgID #$va6 = $val.ToString().PadLeft(6,'0'); $IOavg = [Math]::Round(($IOsum / $count),1); $Sum | Add-Member -MemberType NoteProperty -Name "IO Max (ps)" -Value $IOmax $Sum | Add-Member -MemberType NoteProperty -Name "IO Avg (ps)" -Value $IOavg $RIOavg = [Math]::Round(($RIOsum / $count),1); $Sum | Add-Member -MemberType NoteProperty -Name "Read IO Max(ps)" -Value $RIOmax $Sum | Add-Member -MemberType NoteProperty -Name "Read IO Avg(ps)" -Value $RIOavg $WIOavg = [Math]::Round(($WIOsum / $count),1); $Sum | Add-Member -MemberType NoteProperty -Name "Write IO Max(ps)" -Value $WIOmax $Sum | Add-Member -MemberType NoteProperty -Name "Write IO Avg(ps)" -Value $WIOavg $MBavg = [Math]::Round(($MBsum / $count),1); $Sum | Add-Member -MemberType NoteProperty -Name "MB Max(ps)" -Value $MBmax $Sum | Add-Member -MemberType NoteProperty -Name "MB Avg(ps)" -Value $MBavg $RMBavg = [Math]::Round(($RMBsum / $count),1); $Sum | Add-Member -MemberType NoteProperty -Name "Read MB Max(ps)" -Value $RMBmax $Sum | Add-Member -MemberType NoteProperty -Name "Read MB Avg(ps)" -Value $RMBavg $WMBavg = [Math]::Round(($WMBsum / $count),1); $Sum | Add-Member -MemberType NoteProperty -Name "Write MB Max(ps)" -Value $WMBmax $Sum | Add-Member -MemberType NoteProperty -Name "Write MB Min(ps)" -Value $WMBavg $RTavg = [Math]::Round(($RTsum / $count),1); $Sum | Add-Member -MemberType NoteProperty -Name "Response Time Max(ms)" -Value $RTmax $Sum | Add-Member -MemberType NoteProperty -Name "Response Time Avg(ms)" -Value $RTavg $RRTavg = [Math]::Round(($RTsum / $count),1); $Sum | Add-Member -MemberType NoteProperty -Name "Read RT Max(ms)" -Value $RRTmax $Sum | Add-Member -MemberType NoteProperty -Name "Read RT Avg(ms)" -Value $RRTavg $WRTavg = [Math]::Round(($WRTsum / $count),1); $Sum | Add-Member -MemberType NoteProperty -Name "Write RT Max(ms)" -Value $WRTmax $Sum | Add-Member -MemberType NoteProperty -Name "Write RT Avg(ms)" -Value $WRTavg $RPavg = [Math]::Round(($RPsum / $count),1); $Sum | Add-Member -MemberType NoteProperty -Name "Read Max%" -Value $RPmax $Sum | Add-Member -MemberType NoteProperty -Name "Read Avg%" -Value $RPavg $WPavg = [Math]::Round(($WPsum / $count),1); $va8 = "$WPmax / $WPAvg" $Sum | Add-Member -MemberType NoteProperty -Name "Write Max%" -Value $WPmax $Sum | Add-Member -MemberType NoteProperty -Name "Write Avg%" -Value $WPavg $IOSavg = [Math]::Round(($IOSsum / $count),1); $Sum | Add-Member -MemberType NoteProperty -Name "AvgIOSize Max" -Value $IOSmax $Sum | Add-Member -MemberType NoteProperty -Name "AvgIOSize Avg" -Value $IOSavg $RSavg = [Math]::Round(($RSsum / $count),1); $Sum | Add-Member -MemberType NoteProperty -Name "AvgReadSize Max" -Value $RSmax $Sum | Add-Member -MemberType NoteProperty -Name "AvgReadSize Avg" -Value $RSavg $WSavg = [Math]::Round(($WSsum / $count),1); $Sum | Add-Member -MemberType NoteProperty -Name "AvgWriteSize Max" -Value $WSmax $Sum | Add-Member -MemberType NoteProperty -Name "AvgWriteSize Avg" -Value $WSavg <# $BSavg = [Math]::Round(($BSsum / $count),1); $va8 = "$BSmax / $BSAvg" $Sum | Add-Member -MemberType NoteProperty -Name "BlockSize" -Value $va8 $XSTavg = [Math]::Round(($XSTsum / $count),1); $va8 = "$XSTmax / $XSTAvg" $Sum | Add-Member -MemberType NoteProperty -Name "MaxIOServiceTime" -Value $va8 $mSTavg = [Math]::Round(($mSTsum / $count),1); $va8 = "$mSTmax / $mSTAvg" $Sum | Add-Member -MemberType NoteProperty -Name "MinIOServiceTime" -Value $va8 #> if ($csv) { $capmet = Get-SG-CapInfo $sgID $CapGB, $Vols = $capmet.Split("_"); $va6 = [Math]::Round($CapGB,1) $Sum | Add-Member -MemberType NoteProperty -Name "Capacity (gb)" -Value $va6 $va6 = [Math]::Round($Vols,1) $Sum | Add-Member -MemberType NoteProperty -Name "Volumes" -Value $va6 } if ($csv) { $Script:SGMetSum += $Sum } if ($con) { return $Sum }; } } function Show-IOMetrics { begin {} process { $Met = New-Object -typename PSObject if ($report -match "Summary") { $Met | Add-Member -MemberType NoteProperty -Name "Array" -Value $Script:ArrayNam; $Met | Add-Member -MemberType NoteProperty -Name "Model" -Value $Script:ArrayMod; } $ztime = $_.time $ltime = Convert-Ztime $ztime; $Met | Add-Member -MemberType NoteProperty -Name "TimeStamp" -Value $ltime $wps = $_.writes_per_sec; $rps = $_.reads_per_sec; $iorate = $wps + $rps; $Met | Add-Member -MemberType NoteProperty -Name "IO (ps)" -Value $iorate #$readrt = [Math]::Round($_.RESPONSE_TIME_READ,1); $Met | Add-Member -MemberType NoteProperty -Name "Read (ps)" -Value $rps #$writert = [Math]::Round($_.RESPONSE_TIME_WRITE,1); $Met | Add-Member -MemberType NoteProperty -Name "Write (ps)" -Value $wps $Met | Add-Member -MemberType NoteProperty -Name "Read (us)" -Value $_.usec_per_read_op #$writert = [Math]::Round($_.RESPONSE_TIME_WRITE,1); $Met | Add-Member -MemberType NoteProperty -Name "Write (us)" -Value $_.usec_per_write_op if ($report -ne "Summary") { $Met | Add-Member -MemberType NoteProperty -Name "QDepth" -Value $_.queue_depth } $readmbps = $_.output_per_sec / (1024 * 1024); $readmb = [Math]::Round($readmbps); $Met | Add-Member -MemberType NoteProperty -Name "Reads (MB)" -Value $readmb $writembps = $_.input_per_sec / (1024 * 1024); $writemb = [Math]::Round($writembps); $Met | Add-Member -MemberType NoteProperty -Name "Writes (MB)" -Value $writemb; $Script:IOMetrics += $Met } end { if ($report -ne "Summary") { if ($con) { Write-Host "IO Metrics for $pureName" -ForegroundColor Yellow $Script:IOMetrics | Format-Table -AutoSize } else { $title = "IO Metrics for $pureName" $Script:IOMetrics | Out-GridView -Title $title } } } } function Get-PureIOMetrics { begin { $PureMetrics = @();} process { $PureMet = New-Object -typename PSObject $PureMet | Add-Member -MemberType NoteProperty -Name "Array" -Value $Script:ArrayNam; $PureMet | Add-Member -MemberType NoteProperty -Name "Model" -Value $Script:ArrayMod; $ztime = $_.time $ltime = Convert-Ztime $ztime; $PureMet | Add-Member -MemberType NoteProperty -Name "Time Stamp" -Value $ltime $wps = $_.writes_per_sec; $rps = $_.reads_per_sec; $iorate = $wps + $rps; $PureMet | Add-Member -MemberType NoteProperty -Name "IO (ps)" -Value $iorate $rms = $_.usec_per_read_op / 1000; $readrt = [Math]::Round($rms,1); $PureMet | Add-Member -MemberType NoteProperty -Name "Read RT (ms)" -Value $readrt; $wms = $_.usec_per_write_op / 1000; $writert = [Math]::Round($wms,1); $PureMet | Add-Member -MemberType NoteProperty -Name "Write RT (ms)" -Value $writert $readmbps = $_.output_per_sec / (1024 * 1024); $readmb = [Math]::Round($readmbps); $PureMet | Add-Member -MemberType NoteProperty -Name "Reads (MB)" -Value $readmb $writembps = $_.input_per_sec / (1024 * 1024); $writemb = [Math]::Round($writembps); $PureMet | Add-Member -MemberType NoteProperty -Name "Writes (MB)" -Value $writemb; $readpct1 = ($rps/$iorate) * 100; $readpct2 = [Math]::Round($readpct1); $PureMet | Add-Member -MemberType NoteProperty -Name "READ %" -Value $readpct2 $PureMet | Add-Member -MemberType NoteProperty -Name "HIT %" -Value "N/A" $PureMetrics += $PureMet } end { $Script:SumMetrics += $PureMetrics[$PureMetrics.Length - 1]; } } function Show-HGMetrics { param ([string]$hosttype) begin {} process { $Met = New-Object -typename PSObject $ztime = $_.time $ltime = Convert-Ztime $ztime; $Met | Add-Member -MemberType NoteProperty -Name "TimeStamp" -Value $ltime $Met | Add-Member -MemberType NoteProperty -Name "Host" -Value $_.name if ($hosttype -match "Group") { $Met | Add-Member -MemberType NoteProperty -Name "HG" -Value "Y" } else { $Met | Add-Member -MemberType NoteProperty -Name "HG" -Value "N" } $wps = $_.writes_per_sec; $rps = $_.reads_per_sec; $iorate = $wps + $rps; $Met | Add-Member -MemberType NoteProperty -Name "IO (ps)" -Value $iorate $Met | Add-Member -MemberType NoteProperty -Name "Read (ps)" -Value $rps $Met | Add-Member -MemberType NoteProperty -Name "Write (ps)" -Value $wps $Met | Add-Member -MemberType NoteProperty -Name "Read (us)" -Value $_.usec_per_read_op $Met | Add-Member -MemberType NoteProperty -Name "Write (us)" -Value $_.usec_per_write_op $Met | Add-Member -MemberType NoteProperty -Name "Input (ps)" -Value $_.input_per_sec $Met | Add-Member -MemberType NoteProperty -Name "Output (ps)" -Value $_.output_per_sec $Script:HGMetrics += $Met } end { if ($hosttype -match "Host") { if ($con) { Write-Host "Host & Host Group Metrics for $pureName" -ForegroundColor Yellow $Script:HGMetrics | Sort-Object Timestamp, "IO (ps)" | Format-Table -AutoSize } else { $title = "Host & Host Group Metrics for $pureName" $Script:HGMetrics | Sort-Object Timestamp, "IO (ps)" -Descending | Out-GridView -Title $title } } } } function Get-PureArrayIP { param([string]$arrayName) $doc = Get-ArrayInfoXML; $doc.SelectNodes("//Array") | Where-Object {$_.Name -match $arrayName} | Get-PureXMLObj; [string]$ip = $Script:PSArrays[0].ArrayIP; $Script:usr = $Script:PSArrays[0].UserName; $Script:org = $Script:PSArrays[0].Org; $ipx = $ip.Trim(); return $ipx; } function Get-FrontEndObj { param ([string]$feStr) $feStr | Set-Content gpm.txt # remove header $a = (get-content gpm.txt) $a = $a[1..($a.count - 1)] # replace spaces with comma $a | ForEach-Object {$_.Trim() -replace "\s+",","} | set-content gpm.csv $a1 = (get-content gpm.csv) # generate valid CSV file $a1 | ForEach-Object { # generate valid CSV file $b = $_; if ($b.Length -gt 2) { $c = $_.SubString(0,1) if ($c -match '^[0-9]') { # Append needed commas $newline = ",,,," + $b + "," $newline | Add-Content gpm1.csv } else { $newline = $b.Trim() -replace ",-",""; $newline += "," $newline | Add-Content gpm1.csv } } } $feObj = Import-CSV gpm1.csv -Header $FEHeader $feObj | ForEach-Object { # Add hostname to entries w/o $nm = $_.Host if ($_.Host -ne "") { $hostnm = $_.Host; } else { $_.Host = $hostnm } } #Remove temp files Remove-Item gpm.txt Remove-Item gpm.csv Remove-Item gpm1.csv return $feObj } function Get-PureXMLObj { begin {$Script:PSArrays = @();} process { $PSArray = New-Object -typename PSObject $PSArray | Add-Member -MemberType NoteProperty -Name "Org" -Value $_.Org $PSArray | Add-Member -MemberType NoteProperty -Name "Data Center" -Value $_.DataCenter $PSArray | Add-Member -MemberType NoteProperty -Name "Array" -Value $_.Name $PSArray | Add-Member -MemberType NoteProperty -Name "ArrayIP" -Value $_.remote $PSArray | Add-Member -MemberType NoteProperty -Name "Model" -Value $_.Model $PSArray | Add-Member -MemberType NoteProperty -Name "Usage" -Value $_.usage $PSArray | Add-Member -MemberType NoteProperty -Name "UserName" -Value $_.username $Script:PSArrays += $PSArray } # return an array of PureSystem arrays end { $Script:PSArrays } } function Set-PureArray { param ([string]$ip, [string]$borg, [string]$account) # Get or Set Credentials $account Set-Credentials $borg $account try { $Script:pure = New-PfaArray -EndPoint $ip -Credentials $Script:cred -IgnoreCertificateError } catch { Write-Host $_ -ForegroundColor "Red" Write-Host "New-PfaArray -EndPoint $ip failed"; return $null } } function Select-PureArray { $doc = Get-ArrayInfoXML; $title = "Select a PureSystem array for the report"; [int]$objcnt = 0; $doc.SelectNodes("//Array") | Where-Object {$_.Class -match "Pure"} | Get-PureXMLObj | Sort-Object Org, "Data Center", Name | Out-GridView -Title $title -OutputMode Single | ForEach-Object { if ($objcnt -eq 0) { $arrayName = $_.Array $arrayIP = $_.ArrayIP $arrayOrg = $_.Org $arrayAcct = $_.UserName } $objcnt++; } if ($arrayName -eq $null) { Write-Host " selection cancelled" -ForegroundColor Red Write-Host "Thanks for trying! Bye" -ForegroundColor Blue return $null } Set-PureArray $arrayIP $arrayOrg $arrayAcct return $arrayName } function Select-Report { $reports = @(); $reports += [pscustomobject]@{Report="ArrayMetrics"; Description = " Performance metrics for PureSystem $pureName"} $reports += [pscustomobject]@{Report="FrontEnd"; Description = " Front End port performance metrics PureSystem $pureName"}; $reports += [pscustomobject]@{Report="StorageGroup"; Description = " Performance metrics for Hosts and Host Groups"}; $reports += [pscustomobject]@{Report="Summary"; Description = " IO Performance Summary of all Pure Storage arrays"}; $title = "Select a PureSystem Performance Report" $selectA_report = $reports | Out-GridView -Title $title -OutputMode Single; if ($selectA_report -eq $null) { Write-Host " Selection Cancelled" -ForegroundColor Red Write-Host "Thanks for trying! Bye" -ForegroundColor Blue return "Cancel" } else { return $selectA_report.report } } function Get-Arrays { param ([ProgressManager]$pm) begin { } process { $Script:ArrayNam = $_.Name; $Script:ArrayMod = $_.Model; $Script:org = $_.Org; $Script:usr = $_.username; $aryIP = $_.Remote; $PSCmdlet.WriteProgress($pm.GetCurrentProgressRecord(2, "Getting Pure credential Info")) Set-PureArray $aryIP $Script:org $Script:usr $PSCmdlet.WriteProgress( $pm.GetCurrentProgressRecord(3, "Pure Metric data for $Script:ArrayNam")) $Script:IOMetrics = @(); if ($reports -eq "Summary") { Get-PfaArrayIOMetrics -array $Script:pure -timerange "1h" | Show-IOMetrics; $Script:SumMetrics += $Script:IOMetrics[$Script:IOMetrics.Length - 1]; } else { Get-PfaArrayIOMetrics -array $Script:pure -timerange "1h" | Get-PureIOMetrics; } } end { } } function Get-Summary { [long]$totalTasks = 3; $pm = [ProgressManager]::new("Retrieving Pure metric data...", " ", $totalTasks) $PSCmdlet.WriteProgress( $pm.GetCurrentProgressRecord(1, "Gathering Pure array Info form the XMLDB")) $xmlDB = Get-ArrayInfoXML; if ([string]::IsNullOrEmpty($org)) { $xmlDB.SelectNodes("//Array") | Where-Object {$_.Class -match "Pure"} | Get-Arrays $pm } else { $xmlDB.SelectNodes("//Array") | Where-Object {$_.Class -match "Pure" -and $_.org -match $org} | Get-Arrays $pm } $PSCmdlet.WriteProgress($pm.GetCompletedRecord()); $Script:SumMetrics | Format-Table -AutoSize $metCSVfile = "$PWD" + "\" + $Script:org + "-" + $Script:PureMetricsCSV; if (Test-Path $metCSVfile) { Remove-Item -Path $metCSVfile } $Script:SumMetrics | Export-CSV -Path $metCSVfile -Delimiter "," -Append -NoTypeInformation [int]$totalIOPS = 0; [int]$totalRead = 0; [int]$totalWritten = 0; [int]$sz = $Script:SumMetrics.Length - 1; for ($i=0; $i -le $sz; $i++) { $totalIOPS += $Script:SumMetrics[$i]."IO (ps)" $totalRead += $Script:SumMetrics[$i]."Reads (MB)" $totalWritten += $Script:SumMetrics[$i]."Writes (MB)" } Write-Host " Total IOPS: $totalIOPS Reads (MB): $totalRead Writes (MB): $totalWritten" -ForegroundColor Green } # # # Exported Functions <# .SYNOPSIS Reports on PureSystem array performance data by executing RESTAPI calls from the Pure Storage PowerShell SDK to a specific PureArray. .DESCRIPTION Reports PureSystem array metrics. A table of existing arrays is presented for selection of the desired array for reporting. .PARAMETER pureNAME PureSystem array name. Default "select" - A table of arrays will be presented for selection. .PARAMETER timeRange One of the following values: 1h,3h,24h,7d,30d,90d,1y Default: 1h By default 1 hour of data samples will be presented. .PARAMETER report Default 'select' to present a table of reports for selection. ArrayMetrics - Displays performance metrics for PureSystem array. FrontEnd - Displays front end port IO Balance metrics for a PureSystem array. StorageGroup - Displays a list of Hosts and Groups along with performance metrics of each from a PursSystem array. Summary - Displays summarized view of IO Performance metrics and the display is updated every 6 minutes. .PARAMETER sg Name of the Host or Host Group for the Performance report. .PARAMETER con Output to the console rather than Out-Gridview. .PARAMETER csv For SGPerformance report. Output to summary csv file in the 'SGInfo' folder. (NOT IMPLEMENTED) .PARAMETER repeat For the Summary report. The report will be repeated every 6 minutes. .INPUTS ArrayInfo\ArrayInfo.xml Reads the file to present a table for selecting the desired PureSystem array. .INPUTS \pureuser-emc.xml Contains encrypted credentials for the pureuser account. If the file is not present, there will be prompt for the credentials and the file will be regenerated. The file can only be decrypted on system it was created on. .OUTPUTS Output defaults to Out-GridView unless the -con option is specified. .OUTPUTS \pureuser.enc.xml Pureuser encrypted credentials. .EXAMPLE Get-VePureMetrics Input parameters are provided though a series of Out-Gridview selections. .EXAMPLE Get-VePureMetrics bowpur002 -report FrontEnd Displays the FrontEnd report for bowpur002. .EXAMPLE Get-Pure-Metrics bowpur002 -report ArrayMetrics Displays one hour of array metrics for bowpur002. .EXAMPLE Get-Pure-Metrics bowpur002 -report StorageGroups Display Hosts & Host Groups metrics. .EXAMPLE Get-Pure-Metrics -report Summary Displays a summary of the ArrayMetrics report for all Pure Storage arrays.f The display is updated every 6 minutes with new metrics values. .NOTES Requires Installation of PureSystem PowerShell module. Execute the following to install PureStorage PowerShell module. Install-Module -Name PureStoragePowerShellSDK. Author: Craig Dayton 0.0.2.0 07/10/2017 : cadayton : Converted to cmdlet exported from the PureMetrics module. Updated: 03/31/2017 Added a summary report. Updated: 01/05/2017 Cached credential file now machine specific. Updated: 11/15/2016 FrontEnd Report modified to count initiators & CSV output. Updated: 11/01/2016 Input validations added. Updated: 10/31/2016 Initial release .LINK https://github.com/cadayton/Venom .LINK http://venom.readthedocs.io #> Function Get-VePureMetrics { # Get-VePureMetrics Params [cmdletbinding()] Param ( [Parameter(Position=0, Mandatory=$False, ValueFromPipeline=$True)] # [ValidatePattern("^[a-zA-Z0-9]{8}")] [string]$pureName = "select", # expected choices: 1h, 3h, 24h, 7d, 30d, 90d, 1y [Parameter(Position=1, Mandatory=$False, ValueFromPipeline=$True)] [ValidatePattern("^[1h,3h,24h,7d,30d,90d,1y]{2,3}$")] [string]$timeRange = "1h", [Parameter(Position=2, Mandatory=$False, ValueFromPipeline=$True)] [ValidatePattern("^[a-zA-Z0-9]{3,30}")] [string]$report = "select", [Parameter(Position=3, Mandatory=$False, ValueFromPipeline=$True)] [string]$sg, [Parameter(Position=5, Mandatory=$False, ValueFromPipeline=$True)] [switch]$con, [Parameter(Position=6, Mandatory=$False, ValueFromPipeline=$True)] [switch]$csv, [Parameter(Position=7, Mandatory=$False, ValueFromPipeline=$True)] [bool]$repeat =$true, [Parameter(Position=8, Mandatory=$False, ValueFromPipeline=$True)] [string]$org = $null ) # Write-Host "Get-VePureMetrics version 0.0.2.0" -ForegroundColor Green if ($pureName -match "select" -and $report -ne "Summary" -and $report -ne "Summary1") { $pureName = Select-PureArray; if ([String]::IsNullOrEmpty($pureName)) {$report = "Cancel"} } elseif ($report -ne "Summary" -and $report -ne "Summary1") { # Verify array exists [string]$pureIP = Get-PureArrayIP $pureName; $pureIP=$pureIP.Trim(); if ([string]::IsNullOrEmpty($pureIP)) { Write-Host "PureSystem $pureName does not exist in the XMLDB" -ForegroundColor Red Write-Host "The XMLDB may need to be updated" -ForegroundColor Yellow $report = "Cancel" } else { Set-PureArray $pureIP $Script:org $Script:usr } } if ($report -match "select") { $report = Select-Report } switch -Wildcard ($report) { "ArrayMetrics" { $Script:IOMetrics = @(); Write-Host "Generating array metrics report for $pureName ..." -ForegroundColor Green Get-PfaArrayIOMetrics -array $Script:pure -timerange $timeRange | Show-IOMetrics } "Summary*" { $Script:SumMetrics = @(); Get-Summary; if ($repeat) { $StopWatch = New-Object -TypeName System.Diagnostics.Stopwatch $StopWatch.Start() $ticker = $null; try { While ($repeat) { if ($ticker -eq $null) { Write-Host " "; Write-Host "Refresh in 6 minutes " -NoNewLine -ForegroundColor Yellow Write-Host " Ctrl-C to exit" } $ticker = $StopWatch.Elapsed.ToString(); <# $curPos = $host.UI.RawUI.CursorPosition $y = $curPos.Y - 1; #$curPos.X = 2; $curPos.Y = $y; $host.UI.RawUI.CursorPosition = $curPos #> Start-Sleep -Seconds 10; if ($StopWatch.Elapsed.minutes -ge 6) { Clear-Host; $Script:SumMetrics = @(); Get-Summary; $StopWatch.ReStart(); $ticker = $null; } } } catch { } finally { #Write-Host "Performing clean up work" $StopWatch.Stop(); } } return $null; } "FrontEnd" { [long]$totalTasks = 3; $pm = [ProgressManager]::new("Generating Front End report for $pureName ...", " ", $totalTasks) $PSCmdlet.WriteProgress( $pm.GetCurrentProgressRecord(1, "Gathering $pureName Info from the XMLDB")) [string]$pureIP = Get-PureArrayIP $pureName; $pureIP=$pureIP.Trim(); $PSCmdlet.WriteProgress( $pm.GetCurrentProgressRecord(2, "Requesting metric data from $pureName @ $pureIP")) try { $fe = New-PfaCLICommand -EndPoint $pureIP -Credentials $Script:cred -CommandText "purehost monitor --balance" } catch { Write-Host $_ -ForegroundColor "Red" $PSCmdlet.WriteProgress($pm.GetCompletedRecord()); return $null } $feObj = Get-FrontEndObj $fe; $title = "Front End Report for PureSystem, $pureName"; if ($con) { Write-Host $title -ForegroundColor Yellow $PSCmdlet.WriteProgress($pm.GetCompletedRecord()); $feObj | Format-Table -AutoSize; } elseif ($csv) { if (!(Test-Path $Global:FAPath)) { New-Item -Path $PWD\$Global:FADir -ItemType Directory | Out-Null; } $PSCmdlet.WriteProgress( $pm.GetCurrentProgressRecord(2, "Creating CSV file")) $csvPath = "$Global:FAInfo" + $Script:org + "-" + $pureName + "-Report.csv" $feObj | Export-Csv -Path $csvPath -NoTypeInformation $feObj | Sort-Object -property Initiator-WWN -unique | Get-InitiatorCount $PSCmdlet.WriteProgress($pm.GetCompletedRecord()); Write-Host "Front-Report for $pureName with $Script:InitCnt Initiators saved to $csvPath" -ForegroundColor Green; } else { $PSCmdlet.WriteProgress( $pm.GetCurrentProgressRecord(3, "Generating report output & counting initiators")) $feObj | Sort-Object -property Initiator-WWN -unique | Get-InitiatorCount Write-Host "$pureName has $Script:InitCnt Initiators" -ForegroundColor Green $PSCmdlet.WriteProgress($pm.GetCompletedRecord()); $feObj | Out-GridView -Title $title; } } "StorageGroup" { $Script:HGMetrics = @(); Write-Host "Generating Host & Host Group metric report for $pureName ..." -ForegroundColor Green Get-PfaAllHostGroupIOMetrics -array $Script:pure | Show-HGMetrics "Group" Get-PfaAllHostIOMetrics -array $Script:pure | Show-HGMetrics "Host" } "Cancel" { } Default { Write-Host "Report logic not defined for $report" -ForegroundColor Red } } } # |