Home > LUN, PowerShell, report, vSphere > LUN report – datastore, RDM and node visibility

LUN report – datastore, RDM and node visibility

When you are running multi-node vSphere clusters, you probably already had the experience that one or more of your LUNs were not visible on all nodes. Now you can try to find out which LUN is missing on which node the hard way through the vSphere client. Or you can use the force of PowerCLI and run a script that will report all this in a handy spreadsheet.

To make the script as flexible as possible it should be able to handle any n-node cluster. And as you some of you might know, the Export-CSV cmdlet has some problems with variable length rows. Luckily there is a handy solution I already used in my yadr – A vDisk reporter post.

The script

param($clusName,$csvName=("C:\Temp\" + $clusName + "-LUN.csv"))

$rndNum = Get-Random -Maximum 99999

$LunInfoDef = @"
	public string ClusterName;
	public string CanonicalName;
	public string UsedBy;
	public string SizeMB;
"@
$LunInfoDef = "public struct LunInfo" + $rndNum + "{`n" + $LunInfoDef

$esxServers = Get-Cluster $clusName | Get-VMHost | Sort-Object -Property Name
$esxServers | %{
	$LunInfoDef += ("`n`tpublic string " + ($_.Name.Split(".")[0]) + ";")
}
$LunInfoDef += "`n}"

Add-Type -Language CsharpVersion3 -TypeDefinition $LunInfoDef

$scsiTab = @{}
$esxServers | %{
	$esxImpl = $_

# Get SCSI LUNs
	$esxImpl | Get-ScsiLun | where {$_.LunType -eq "Disk"} | %{

		$key = $esxImpl.Name.Split(".")[0] + "-" + $_.CanonicalName.Split(".")[1]
		if(!$scsiTab.ContainsKey($key)){

			$scsiTab[$key] = $_.CanonicalName,"",$_.CapacityMB
		}
	}

# Get the VMFS datastores
	$esxImpl | Get-Datastore | where {$_.Type -eq "VMFS"} | Get-View | %{
		$dsName = $_.Name
		$_.Info.Vmfs.Extent | %{
			$key = $esxImpl.Name.Split(".")[0] + "-" + $_.DiskName.Split(".")[1]
			$scsiTab[$key] = $scsiTab[$key][0], $dsName, $scsiTab[$key][2]
		}
	}
}

# Get the RDM disks
Get-Cluster $clusName | Get-VM | Get-View | %{
	$vm = $_
	$vm.Config.Hardware.Device | where {$_.gettype().Name -eq "VirtualDisk"} | %{
		if("physicalMode","virtualmode" -contains $_.Backing.CompatibilityMode){
			$disk = $_.Backing.LunUuid.Substring(10,32)
			$key = (Get-View $vm.Runtime.Host).Name.Split(".")[0] + "-" + $disk
			$scsiTab[$key][1] = $vm.Name + "/" + $_.DeviceInfo.Label
		}
	}
}

$scsiTab.GetEnumerator() | Group-Object -Property {$_.Key.Split("-")[1]} | %{
	$lun = New-Object ("LunInfo" + $rndNum)
	$lun.ClusterName = $clusName
	$_.Group | %{
		$esxName = $_.Key.Split("-")[0]
		$lun.$esxName = "ok"
		if(!$lun.CanonicalName){$lun.CanonicalName = $_.Value[0]}
		if(!$lun.UsedBy){$lun.UsedBy = $_.Value[1]}
		if(!$lun.SizeMB){$lun.SizeMB = $_.Value[2]}

	}
	$lun
} | Export-Csv $csvName -NoTypeInformation -UseCulture
Invoke-Item $csvName

Annotations

Line 1: The definition of the parameters that can be used with the script. Note that the second parameter takes a default when is not present.

Line 3: If you would run the script multiple times from the same PowerCLI prompt you would get an error saying “Add-Type : Cannot add type. The type name ‘LunInfo’ already exists.“. To avoid this error the script generates on each run a random number between 1 and 99999, which it appends to the type name. This should, unless you have thousands of clusters avoid the error message.

Line 5-19: Since the number of nodes is unknown before the script is actually running, I create a new type, called LunInfon (where n is the random number generated before).

Line 14-16: The new type will contain a property for each node in the cluster. The name of this property is the first qualifier of the FQDN of the host.

Line 21: The script keeps all the information it will gather in a hash table called $scsiTab. The key into the hash table is composed from short hostname and the Canonical name of the LUN.

Line 26-33: From the information provided by the Get-ScsiLun cmdlet, the script will populate the hash table. In this loop the CanonicalName and the Capacity of the LUN are stored in the hash table.

Line 36-43: From the Datastore object the script finds out for all datastores in the cluster which LUN they use.

Line 45-55: The script runs through all the virtual machines on the cluster and checks all virtual disks if they are RDM backed. If the script encounters a RDM it stores the name of the virtual machine and the label of the hard disk in the hash table.

Line 57-68: All the information in the hash table is now grouped per Canonical name. For each Canonical name the script creates a LunInfon object and stores the information from the hash table in that object.

Line 69-70: The array with LunInfon objects is exported to a CSV file and the CSV file is opened.

Usage

You save the script as a .ps1 file. In the example screenshots below I used the name Cluster-LUN-list.ps1.

Start the PowerCLI prompt and make sure that you are connected to the vCenter.

From the PowerCLI prompt you can call the script with the name of a cluster as a parameter.

In this case the script will create the CSV file as C:\Temp\MyCluster17-LUN.csv.

Or you pass the name of the CSV file as a second parameter to the script.

In this case the script will store the report in LunRpt.csv on the root of the D-partition.

Note that some local disks seem to have the same Canonical name on all nodes in a cluster. I experienced that with some IBM RAID Controllers. In that case the entries for those disks will not be correct in the report. In itself this is not really a problem since those disks are local and no LUNs from the SAN.

The report

The following screenshot shows a report made for a 4-node cluster.

You can clearly see which LUNs are:

  • only visible on 1 of the nodes
  • used as RDM disks
  • used for datastores
  • still available

This report will surely make it easier to talk with your Storage Administrator ;-)

  1. Roey1206
    August 28th, 2010 at 21:52 | #1

    Hi lucD,

    The script failed to run at $esxImpl | get-scsiLun | where {$_.Luntype -eq “Disk”} ….

    the error message:
    Get-ScsiLun : Value cannot be null.
    Parameter name: array.

    I tried to run this command with the script… just “get-vmhost | get-scsilun”
    and it failed again with the same error message…

    any idea way?

    Thanks!
    Roey

    • admin
      August 28th, 2010 at 23:46 | #2

      @Roey1206 What do you see when you just do ‘Get-VMHost’ ?
      And what does ‘Get-Cluster $clusName | Get-VMHost’ return ?

  2. James
    August 13th, 2010 at 21:19 | #3

    Can you run this against all the clusters in vCenter or do you have to specify a specific cluster?

    • August 13th, 2010 at 22:10 | #4

      Hi James, the script, as it is, only runs against a specific cluster specified in the first parameter.
      But it’s quite easy to run against all your clusters. Something like this for example


      Get-Cluster | %{.\Cluster-Lun-List.ps1 $_.Name}

      This will produce a separate CSV file for each cluster.

  3. Pcli
    June 24th, 2010 at 14:59 | #5

    Can we use Export-Xls rather than usage of | Export-Csv $csvName -NoTypeInformation -UseCulture

    • June 25th, 2010 at 02:58 | #6

      Sure you can.
      Make sure the function Export-Xls is available, define a new param (for example $xlsName instead of $csvName) and then make the last line something like this:
      | Export-Xls -Path $xlsName -WorksheetName "LUN report" -AppendWorksheet:$false

      You can have a worksheet for each of your clusters in one spreadsheet. Great idea.

  4. Ossie
    April 20th, 2010 at 07:47 | #7

    @Ossie
    Hi Luc
    Thanks for that. Looks like wait and see for the moment.
    Cheers
    Ossie

  5. Ossie
    April 19th, 2010 at 14:38 | #8

    @Ossie
    BTW – PowerCLI is VMware vSphere PowerCLI 4.0 U1 build 208462
    ESX and ESXi is Build 236512

    • April 19th, 2010 at 15:03 | #9

      Ossie, I just tested the Get-ScsiLun cmdlet on a non-clustered ESXi build 244038. And it returned the data correctly.
      There is a thread on the PowerCLI Community that mentions the same problem, but there has been no solution yet. :-(

  6. Ossie
    April 19th, 2010 at 14:23 | #10

    Hi Luc

    When I execute Get-VMHost | Get-ScsiLun on ESXi I get an error,

    Get-ScsiLun : Object reference not set to an instance of an object.
    At line:1 char:45
    + Get-VMHost xxxxxxxxxxx | Get-ScsiLun <<<<
    + CategoryInfo : NotSpecified: (:) [Get-ScsiLun], NullReferenceEx
    ception
    + FullyQualifiedErrorId : System.NullReferenceException,VMware.VimAutomati
    on.VimAutomation.Commands.Host.GetScsiLun

    but if I do the same on an ESX host, it lists out all the LUNs.

    Cheers
    Ossie

  7. Ossie
    April 19th, 2010 at 11:27 | #11

    Hi Luc

    Could it be that Cluster is of ESXi hosts? Just tried on another cluster of 4 ESX hosts and it worked OK.

    Cheers
    Ossie

    • April 19th, 2010 at 11:46 | #12

      Ossie,
      Line 22 is the start of a loop over all ESX servers that are in the cluster.
      Line 23 stores the loop variable ($_) in another variable, called $esxImpl.

      Unfortunately I don’t have a cluster of ESXi hosts handy for testing right now.
      Can you check if

      Get-VMHost "one-of-the-ESXi-hosts" | Get-ScsiLun

      returns anything ?

  8. Ossie
    April 19th, 2010 at 10:34 | #13

    Hi Luc

    Thanks for the reply. I did think that might be the issue so as you suggested had tried just the Get-Cluster “cluster name with spaces” | Get-VMHost and it returns the single host in this cluster, with power status and other stats.
    Obviously others have the script working OK so I’m not sure what could be wrong here. BTW -What is the purpose of lines 22 and 23?
    Thanks again
    Ossie

  9. Ossie
    April 19th, 2010 at 05:03 | #14

    Hi LucD
    Trying to run your script and getting the below errors. Not being familiar with Powershell scripting I was wondering if you could point out what I may be doing incorrectly.
    Thanks

    Get-ScsiLun : Object reference not set to an instance of an object.
    At D:\Scripts\LUN-report.ps1:26 char:24
    + $esxImpl | Get-ScsiLun <<<< | where {$_.LunType -eq "Disk"} | %{
    + CategoryInfo : NotSpecified: (:) [Get-ScsiLun], NullReferenceEx
    ception
    + FullyQualifiedErrorId : System.NullReferenceException,VMware.VimAutomati
    on.VimAutomation.Commands.Host.GetScsiLun

    Cannot index into a null array.
    At D:\Scripts\LUN-report.ps1:40 char:36
    + $scsiTab[$key] = $scsiTab[$key][ <<<< 0], $dsName, $scsiTab[$key]

    • April 19th, 2010 at 07:31 | #15

      Hi Ossie,
      It looks as if there is no ESX server found for the clustername you passed to the script. Could you check if this returns one or more ESX servers ?

      Get-Cluster "clustername" | Get-VMHost

      Where “clustername” is the first parameter you passed in the script call. That would be MyCluster17 in the Usage section sample calls.
      Luc.

  10. Fred
    April 16th, 2010 at 17:18 | #16

    Why are you putting *screenshots* of a *console session* ? Wouldn’t copying the text be easier (go through a graphics program, crop the image, save then import the image in your blogging software VS copying two lines from the console)? Not to mention consume less bandwidth?

    Other than those details, looks neat.

    • April 16th, 2010 at 18:08 | #17

      Thanks for the advice Fred.
      I’ll try to remember it for future posts.

  11. Vuong Pham
    April 12th, 2010 at 22:43 | #18

    works great.. when the cluster names don’t have spaces in the names! ;-)

    (okay I didn’t get it to work 100% but the yadr works perfect – just no cluster names. I ran it against the entire vcenter. Sorted the CSV file for RDM=True and viewed the path name to infer the LUN connection/Host / cluster relationship.

    Very handy! Thanks! lots to (more) to learn!

    • April 13th, 2010 at 07:22 | #19

      Did you try placing the cluster name between quotes ?
      Something like this

      .\Cluster-LUN-list.ps1 "My Cluster"

  1. No trackbacks yet.