When you have a lot of hosts, with a lot of LUNs, it can be difficult to keep abreast of the status of the paths for them. I have encountered issues where a path was unknowingly marked as dead, plus it’s generally a good idea to ensure that your storage paths are actually available.
Consequentially, I searched for a PowerCLI script that would give me a simple report of the status of each of the LUN paths to each of the HBAs on my hosts. I found John Milner’s post to be very helpful, and it gave me exactly the results that I wanted. However, it took forever to execute…almost 30 minutes for just one of my clusters (to be fair, that cluster has 12 hosts with > 100 LUNs and two paths to each).
Using his script as an example, and keeping a good bit of the formatting code, I have modified his script to use views of the host objects and cull the information from there. This makes it significantly faster…what took 28 minutes before now takes about 30 seconds.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
$views = Get-View -ViewType "HostSystem" -Property Name,Config.StorageDevice $result = @() foreach ($view in $views | Sort-Object -Property Name) { Write-Host "Checking" $view.Name $view.Config.StorageDevice.ScsiTopology.Adapter |?{ $_.Adapter -like "*FibreChannelHba*" } | %{ $hba = $_.Adapter.Split("-")[2] $active = 0 $standby = 0 $dead = 0 $_.Target | %{ $_.Lun | %{ $id = $_.ScsiLun $multipathInfo = $view.Config.StorageDevice.MultipathInfo.Lun | ?{ $_.Lun -eq $id } $a = [ARRAY]($multipathInfo.Path | ?{ $_.PathState -like "active" }) $s = [ARRAY]($multipathInfo.Path | ?{ $_.PathState -like "standby" }) $d = [ARRAY]($multipathInfo.Path | ?{ $_.PathState -like "dead" }) $active += $a.Count $standby += $s.Count $dead += $d.Count } } $result += "{0},{1},{2},{3},{4}" -f $view.Name.Split(".")[0], $hba, $active, $dead, $standby } } ConvertFrom-Csv -Header "VMHost", "HBA", "Active", "Dead", "Standby" -InputObject $result | ft -AutoSize |
The result looks exactly like the original:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
VMHost HBA Active Dead Standby ------ --- ------ ---- ------- host1 vmhba1 9 0 9 host1 vmhba2 9 0 9 host2 vmhba1 9 0 9 host2 vmhba2 9 0 9 host3 vmhba1 9 0 9 host3 vmhba2 9 0 9 host4 vmhba1 9 0 9 host4 vmhba2 9 0 9 host5 vmhba1 9 0 9 host5 vmhba2 9 0 9 host6 vmhba1 9 0 9 host6 vmhba2 9 0 9 host7 vmhba1 9 0 9 host7 vmhba2 9 0 9 host8 vmhba1 10 0 10 host8 vmhba2 10 0 10 |
This is also helpful, for me, because each of my hosts should have the same number of active and standby paths…if there is more of one or the other, that’s an indication that a path is missing, or there is an extra for some reason. For example, the above is a single cluster, so they should all be identical. Multipathing appears to be good because there are the same number of active and standby paths. However, one host has an extra pair of paths…a simple check revealed that an old LUN had not been removed…no damage done, but very easy to spot with this script.
Thanks to John for doing all the hard work with formatting!
Hi, can we do run this script on a cluster rather than running across the board ?
Thanks
Hi Vcpguy,
You can limit to a single cluster by adjusting the
Get-View
line at the very top to include aSearchRoot
parameter. For example:The above will limit the hosts to a cluster with the name “some Cluster”.
Hope that helps and thanks for reading!
Andrew
getting below error while running
ConvertFrom-Csv : Cannot validate argument on parameter ‘InputObject’. The argu
ment is null, empty, or an element of the argument collection contains a null v
alue. Supply a collection that does not contain any null values and then try th
e command again.
At C:UsersmurtiDesktopHBA.PS1:34 char:82
+ ConvertFrom-Csv -Header “VMHost”, “HBA”, “Active”, “Dead”, “Standby” -InputOb
ject <<<< $result | ft -AutoSize
+ CategoryInfo : InvalidData: (:) [ConvertFrom-Csv], ParameterBin
dingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.Power
Shell.Commands.ConvertFromCsvCommand
I’m having an issue with this script duplicating the total # of paths. Have you seen this before? Also how could I get the cluster info back like John’s original script?
Thanks
Andrew,
The only thing that comes to mind is if each HBA sees two, or more, paths to the LUNs. Aside from that, I’m drawing a blank. Can you give any more details of your environment?
Thanks for reading,
Andrew
What if your path selection is round robin, are you still supposed to see the same number of Active and StandBy paths?
Hi All,
I received the below error and I edited the last time as listed below and the script worked. Thanks.
Error :
——–
“Microsoft.PowerShell.Commands.Internal.Format.FormatStartData” is
not legal or not in the correct sequence. This is likely caused by a user-specified “format-table”
command which is conflicting with the default formatting.
+ CategoryInfo : InvalidData: (:) [out-lineoutput], InvalidOperationException
+ FullyQualifiedErrorId : ConsoleLineOutputOutOfSequencePacket,Microsoft.PowerShell.Commands.O
utLineOutputCommand
Change code :
ConvertFrom-Csv -Header “VMHost”, “HBA”, “Active”, “Dead”, “Standby” -InputObject $result | Format-Table -AutoSize | Out-Default
Sorry for the necro-post here, but if anyone is experiencing an issue where the total number of paths is more than expected, it’s because you have multiple paths to the same LUN (for example, through 2 different fabrics, or through 2 different controller heads). In my case, I was seeing a quadrupling of the expected paths as my fabrics are very redundant.
The fix for this is to change this code:
$_.Target | %{
$_.Lun | %{
$id = $_.ScsiLun
$multipathInfo = $view.Config.StorageDevice.MultipathInfo.Lun | ?{ $_.Lun -eq $id }
$a = [ARRAY]($multipathInfo.Path | ?{ $_.PathState -like "active" })
$s = [ARRAY]($multipathInfo.Path | ?{ $_.PathState -like "standby" })
$d = [ARRAY]($multipathInfo.Path | ?{ $_.PathState -like "dead" })
$active += $a.Count
$standby += $s.Count
$dead += $d.Count
}
}
to simply this:
$multipathInfo = $view.Config.StorageDevice.MultipathInfo.Lun | ? { $_.Path.Adapter -like "*FibreChannelHba*" }
$a = [ARRAY]($multipathInfo.Path | ?{ $_.PathState -like "active" })
$s = [ARRAY]($multipathInfo.Path | ?{ $_.PathState -like "standby" })
$d = [ARRAY]($multipathInfo.Path | ?{ $_.PathState -like "dead" })
$active += $a.Count
$standby += $s.Count
$dead += $d.Count
The extra foreach loops (the %{ blocks, for those curious) end up causing the script to double, triple, quadruple (or more, depending on the actual number of paths to each LUN) the count of actual paths.
Also note the duplicate adapter filter here; if your host has additional onboard adapters for things like the local drive array, it’ll show up as an extra path where you may or may not care (remove everything after the pipe in the $multipathInfo line if you do).
Excellent article, thanks Andrew. And, also to Ryan J’s post as well – I was getting double/triple numbers on my report.
I’m getting also too much paths per hba.
Check out this solution http://vblog.hochsticher.de/?p=334 this should count correctly 😉
Hi Christoph,
Thanks for reading, and thank you for contributing! Great work on your script.
Andrew
Hi Andrew,
Thanks!
Yeah, it’s an old topic but still important 😉
How can i Export the result to csv ?
Hello Prabhu,
Since the result is already technically a CSV before it’s formatted for display, all you would need to do is pipe
$result
toOut-File
at the end.Hope that helps.
Andrew
Doesn’t work with EMC PPVE , any solution out there ?
great, thank you!
Hi all,
I found this page very helpful and used the script to help me with identifying path states during a update of our disk array. However, I did notice (as others have too) that I seemed to be getting too many paths returned in my output. So I took some time working out what the issue was with the script. I ended up re-writing the script to use “foreach” instead of foreach-object, just personal preference really. My script seems to return the right number of paths now.
#connect to vIServers
if ($global:DefaultVIServers.Count -ne 2) {
Connect-vIServer -Server server01.my.domain
Connect-vIServer -Server server02.my.domain
}
else {
Write-Host “Already connected to” $global:DefaultVIServers -ForegroundColor Green
}
#get all views from all datacentres
$views = @()
foreach ($server in $global:DefaultVIServers) {
$server.Name
$views += Get-View -ViewType “HostSystem” -Property Name, Config.StorageDevice -Server $server.name
}
$views = $views | sort name
#build array and export to clipboard
$index = 0
$array = @()
foreach ($view in $views) {
$index ++
write-host “Processing” $view.name $index “of” $views.Count -ForegroundColor Yellow
$hbas = $view.Config.StorageDevice.ScsiTopology.Adapter |?{ $_.Adapter -like “*FibreChannelHba*” }
write-host `t “Found” $hbas.count “HBAs” -ForegroundColor DarkYellow
foreach ($adapter in $hbas) {
$hba = $null
$hba = $adapter.adapter.split(“-“)[2]
$state = $null
$state = @()
$table = “” | select ESXHostName, Cluster, HBA_Adapter, HBA_Status, HBA_TargetPathCount, ActiveCount, DeadCount, StandbyCount
$table.ESXHostName = $View.Name
$table.HBA_Adapter = $hba
foreach ($target in $adapter.Target) {
write-host `t`t “Target:” $target.Target -ForegroundColor Magenta
foreach ($lun in $target.Lun) {
#write-host `t `t `t $lun.Key -ForegroundColor DarkMagenta
$id = $lun.ScsiLun.Split(“-“)[2]
$multipathInfo = $view.Config.StorageDevice.MultipathInfo.Lun | Where-Object { $_.id -eq $id }
$state += $multipathInfo.Path | Where-Object {$_.adapter -like “*$hba*”}
}
}
$grouped = $state | Group-Object state
$table.Cluster = (get-cluster -VMHost $view.Name).name
$table.HBA_Status = (Get-VMHostHba -VMHost $view.Name -Device $hba).Status
$table.HBA_TargetPathCount = $adapter.Target.Count
$table.ActiveCount = ($grouped| Where-Object {$_.name -like “*active*”}).count
$table.DeadCount = ($grouped| Where-Object {$_.name -like “*dead*”}).count
$table.StandbyCount = ($grouped| Where-Object {$_.name -like “*stand*”}).count
$array += $table
}
}
$array | ft -AutoSize
$array | ConvertTo-Csv -Delimiter “`t” -NoTypeInformation | clip.exe