VMware Cloud Community
dcoz
Hot Shot
Hot Shot
Jump to solution

inventory of ESX hosts datastores and paths to datastore

Hi Guys,

I have been working on this script to collect the ESX hosts within a cluster and detail the datastores and number of paths to each datastore.

I do get an output but i seem to get redunency in terms of the datastores being listed. What i am trying to get is the Host and within this the datastores and path count to each datastore. Basically trying to make sure there is more than one path to a datastore.

# Function to create the headings for the output
function New-Row{
    param($hostname,$diskname,$paths)

    $row = "" | Select-Object VMHost, DiskNAA, PathCount
    $row.VMHost = $hostname
    $row.DiskNAA = $Diskname
    $row.Pathcount = $paths
    $row
}
$list = get-Cluster "testcluster" | get-VMHost
$diskpaths =@()
foreach ($srv in $list){
$diskpaths += (New-Row $srv.Name $null $null)
$esx = $srv | Get-View
foreach($disk in $esx.Config.StorageDevice.ScsiLun){
                                foreach($lun in $esx.Config.StorageDevice.MultipathInfo.Lun){
                                               $diskpaths += (New-Row null  $disk.CanonicalName $lun.Path.Count)
                                            }
                                            }

}

$diskpaths

Any help would be appreciated.

Dougie

0 Kudos
1 Solution

Accepted Solutions
LucD
Leadership
Leadership
Jump to solution

Under the storageDevice property sits a HostStorageDeviceInfo object.

In this object you have several lists of storage related entries.

The ScsiLun property is an array that holds all the LUNs present on the host. In these entries you will find the CanonicalName for each LUN.

The multiPath property is also an array that holds again all the LUNs together with the Path information for each of these LUNs.

Unfortunately these entries do not contain the CanonicalName for a LUN.

So we have to combine the information of these 2 arrays together.

The outer loop runs through all the LUNs present in the array under the ScsiLun property.

The inner loop runs through all the LUNs present in the array under the multiPath property.

Since we only want to count the paths for 1 specific LUN, we use the "key" of the ScsiLun entry to select only the multiPath entries that correspond with that specific LUN. The ScsiLun "key" is present in the multiPath LUN as the "LUN" property.

So the line

foreach($lun in $esx.Config.StorageDevice.MultipathInfo.Lun | where {$_.Lun -eq $disk.Key}){

will:

  • place all entries from multiPath in the pipeline
  • filter these objects so only the ones whose "LUN" property corresponds with the ScsiLun's "KEY" property
  • pass each selected multiPath LUN to the code block with a foreach statement

I hope I didn't confuse you more :smileygrin:

Updated: nearly forgot to answer your datastore question.

Yes, that is possible.

Have a look at my LUN report – datastore, RDM and node visibility post where I do exactly that.

Your script would become something like this

# Function to create the headings for the output function New-Row{
    param($hostname,$dsname, $diskname,$paths)

    $row = "" | Select-Object VMHost, Datastore, DiskNAA, PathCount
   
$row.VMHost = $hostname
   
$row.Datastore = $dsname
   
$row.DiskNAA = $Diskname
   
$row.Pathcount = $paths
   
$row
}
$list = Get-Cluster | Get-VMHost $datastore = @{} foreach($ds in ($list | Get-Datastore | Sort-Object -Unique)){     if($ds.Extensiondata.Info.Vmfs){         $ds.Extensiondata.Info.Vmfs.Extent | %{             $datastore[$_.DiskName] = $ds.Name         }     } } $diskpaths =@() foreach ($srv in $list){     $diskpaths += (New-Row $srv.Name $null $null $null)     $esx = $srv | Get-View
    foreach($disk in $esx.Config.StorageDevice.ScsiLun){         foreach($lun in $esx.Config.StorageDevice.MultipathInfo.Lun | where {$_.Lun -eq $disk.Key}){             $diskpaths += (New-Row null $datastore[$disk.CanonicalName] $disk.CanonicalName $lun.Path.Count)         }     } } $diskpaths

The script first created a hash tab with the datastore names and as the key it uses the CanonicalName of each extent of the datastore.

Later on, the script uses the CanonicalName from each ScsiLun entry to find, if any, the corresponding datastore.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

View solution in original post

0 Kudos
10 Replies
RvdNieuwendijk
Leadership
Leadership
Jump to solution

If you change the line:

foreach($lun in $esx.Config.StorageDevice.MultipathInfo.Lun){


into:

foreach($lun in ($esx.Config.StorageDevice.MultipathInfo.Lun  | Sort-Object -Unique)){


you will only get unique lines.

Regards, Robert

Blog: https://rvdnieuwendijk.com/ | Twitter: @rvdnieuwendijk | Author of: https://www.packtpub.com/virtualization-and-cloud/learning-powercli-second-edition
AlbertWT
Virtuoso
Virtuoso
Jump to solution

Yes I have tested this script and it returns the number of Path in my each NAA is... 4 for all ?

/* Please feel free to provide any comments or input you may have. */
0 Kudos
LucD
Leadership
Leadership
Jump to solution

You only have to take the paths that belong to a specific LUN.

That is done with the where-clause

# Function to create the headings for the output 
function New-Row{     param($hostname,$diskname,$paths)     $row = "" | Select-Object VMHost, DiskNAA, PathCount
   
$row.VMHost = $hostname
   
$row.DiskNAA = $Diskname
   
$row.Pathcount = $paths
   
$row
}
$list = get-Cluster | get-VMHost $diskpaths =@() foreach ($srv in $list){     $diskpaths += (New-Row $srv.Name $null $null)     $esx = $srv | Get-View
    foreach($disk in $esx.Config.StorageDevice.ScsiLun){         foreach($lun in $esx.Config.StorageDevice.MultipathInfo.Lun | where {$_.Lun -eq $disk.Key}){             $diskpaths += (New-Row null $disk.CanonicalName $lun.Path.Count)         }     } } $diskpaths


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
dcoz
Hot Shot
Hot Shot
Jump to solution

Robert,

lol..so simple

Just what i was looking for.

Thats great thanks.

Dougie

0 Kudos
LucD
Leadership
Leadership
Jump to solution

Simple, but incorrect I'm afraid.

Each LUN comes back with the same number of paths, which is ok if that is indeed your config.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
dcoz
Hot Shot
Hot Shot
Jump to solution

Hi Luc,

I am curious about the line you entered.

$lun in $esx.Config.StorageDevice.MultipathInfo.Lun | where {$_.Lun -eq $disk.Key})

What exactly is this doing and how did it resolve the enumeration issue?

Also instead of having the canonical name in the output is it possible to put the datastore name in?

Thanks
Douglas
0 Kudos
LucD
Leadership
Leadership
Jump to solution

Under the storageDevice property sits a HostStorageDeviceInfo object.

In this object you have several lists of storage related entries.

The ScsiLun property is an array that holds all the LUNs present on the host. In these entries you will find the CanonicalName for each LUN.

The multiPath property is also an array that holds again all the LUNs together with the Path information for each of these LUNs.

Unfortunately these entries do not contain the CanonicalName for a LUN.

So we have to combine the information of these 2 arrays together.

The outer loop runs through all the LUNs present in the array under the ScsiLun property.

The inner loop runs through all the LUNs present in the array under the multiPath property.

Since we only want to count the paths for 1 specific LUN, we use the "key" of the ScsiLun entry to select only the multiPath entries that correspond with that specific LUN. The ScsiLun "key" is present in the multiPath LUN as the "LUN" property.

So the line

foreach($lun in $esx.Config.StorageDevice.MultipathInfo.Lun | where {$_.Lun -eq $disk.Key}){

will:

  • place all entries from multiPath in the pipeline
  • filter these objects so only the ones whose "LUN" property corresponds with the ScsiLun's "KEY" property
  • pass each selected multiPath LUN to the code block with a foreach statement

I hope I didn't confuse you more :smileygrin:

Updated: nearly forgot to answer your datastore question.

Yes, that is possible.

Have a look at my LUN report – datastore, RDM and node visibility post where I do exactly that.

Your script would become something like this

# Function to create the headings for the output function New-Row{
    param($hostname,$dsname, $diskname,$paths)

    $row = "" | Select-Object VMHost, Datastore, DiskNAA, PathCount
   
$row.VMHost = $hostname
   
$row.Datastore = $dsname
   
$row.DiskNAA = $Diskname
   
$row.Pathcount = $paths
   
$row
}
$list = Get-Cluster | Get-VMHost $datastore = @{} foreach($ds in ($list | Get-Datastore | Sort-Object -Unique)){     if($ds.Extensiondata.Info.Vmfs){         $ds.Extensiondata.Info.Vmfs.Extent | %{             $datastore[$_.DiskName] = $ds.Name         }     } } $diskpaths =@() foreach ($srv in $list){     $diskpaths += (New-Row $srv.Name $null $null $null)     $esx = $srv | Get-View
    foreach($disk in $esx.Config.StorageDevice.ScsiLun){         foreach($lun in $esx.Config.StorageDevice.MultipathInfo.Lun | where {$_.Lun -eq $disk.Key}){             $diskpaths += (New-Row null $datastore[$disk.CanonicalName] $disk.CanonicalName $lun.Path.Count)         }     } } $diskpaths

The script first created a hash tab with the datastore names and as the key it uses the CanonicalName of each extent of the datastore.

Later on, the script uses the CanonicalName from each ScsiLun entry to find, if any, the corresponding datastore.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
dcoz
Hot Shot
Hot Shot
Jump to solution

Great explanation Luc and totally understandable.

A couple of scenarios highlighted themselves when running the script.

I had an ESXi host with 2 HBAs. vmhba1 showed no paths or showed paths dead and vmhba 2 showed active connections.

The script produced the paths as 4 quite rightly, but it did highlight how could to find this issue.

As far as i see it, you would have to look at each vmhba and count the paths for each datastore.

Having looked about a bit it seems to me this type of enumeration wouldnt be possible.

Is this correct?

Thanks

Dougie

0 Kudos
LucD
Leadership
Leadership
Jump to solution

I think you can use the operationlState property of the LUN and the pathState for each path, to have a report on the status.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
dcoz
Hot Shot
Hot Shot
Jump to solution

Thanks Luc I will have a look

Dougie

0 Kudos