Home > PowerCLI, PowerShell, RDM, report, Thin Provisioning, vSphere > yadr – A vdisk reporter

yadr – A vdisk reporter

I know there are already numerous scripts to report on virtual hard disks and most of them without a doubt much better then what I came up with for this post.

The reason I started with this script was a question in the PowerCLI Community from Alan in his Thin Provisioned Disks post. He wanted to know if you could get the provisioned and the allocated disk size for a thin provisioned virtual disk.

From past experience I knew that the answer was to be found in the layoutEx property of the VirtualMachine object.

But while I was writing the script I saw opportunities to expand the script to make the produced report the “mother of all virtual disk reports”. Or at least an attempt to include much more information in the produced report ;-)

The layoutEx property

The layoutEx property, that came with API 4.0, contains a wealth of information concerning the virtual disks connected to your guests.

Since you can find back the mapping to all individual files that constitute a virtual disk,  it is the ideal property to show the actual storage used by each virtual disk and the snapshots.

The following graphic shows how the three major properties in the VirtualMachineFileLayoutEx object are organised. The values in the graphic are from a virtual machine, with one virtual hard disk, and two snapshots.

Handling of snapshots

I assume that most of you know what happens in the background with a virtual disk when you take a snapshot of a virtual machine. The following graphic is small aide-mémoire for understanding the basics.

If you look closely at the Snapshot table (first graphic) you will notice that an entry points to the files, or chain of files, before the snapshot was taken.

For example, Snapshot 2 results in the FileKey sequence 0, 1, 2 and 3. While the actual new files that are created for Snapshot 2, are those with FileKey 4 and 5.

Since I wanted to know how much datastore space Snapshot 2 was taking, I decided to let the script look for the files linked to Snapshot 2. In this case that would be the files with FileKey 4 and 5.

The script

There were a few problems to be solved:

  1. Not all virtual machines have the same number of snapshots. If the data needs to be exported to a CSV file this poses a problem with the Export-Csv cmdlet. The solution I took:
    • For all guests determine the number of snapshots, if any, they have and take the maximum of these numbers.
    • Define a new object, I called it DiskInfo in the script, and define a sufficient number of snapshot-related properties (Name and Size) so that the guest with the highest number of snapshots can be represented.
    • Define the new object in C# and use the Add-Type cmdlet to create the new type.
  2. Like I mentioned earlier, I wanted to display the actual datastore space a specific snapshot takes. For that I had to find the FileKey values that are not in the Snapshot table. My reasoning for this was that I wanted to know how much datastore space would become free when a specific snapshot is removed.
$DiskInfoDef = @"
public struct DiskInfo {
	public string VMname;
	public string Label;
	public string Path;
	public bool Thin;
	public bool RDM;
	public string Used;
	public string CapacityMB;
	public string AllocatedMB;
"@

$vms = Get-View -ViewType VirtualMachine
$maxSnapshot = ($vms | %{$_.LayoutEx.Snapshot.Count} | Measure-Object -Maximum).Maximum
1..$maxSnapshot | %{
	$DiskInfoDef += ("`n`tpublic string Snap" + $_ + "MB;")
	$DiskInfoDef += ("`n`tpublic string Snap" + $_ + "Name;")
}
$DiskInfoDef += "`n}"

Add-Type -Language CsharpVersion3 -TypeDefinition $DiskInfoDef

$snapHash = @{}

filter Get-SnapHash{
	$snapHash[$_.Snapshot.Value] = $_.Name
	if($_.ChildSnapshotList){
		$_.ChildSnapShotList | Get-SnapHash
	}
}

$vms | %{
	$vm = $_

	if($vm.Snapshot){
		$vm.Snapshot.RootSnapshotList | Get-SnapHash
	}

	$_.Config.Hardware.Device | where {$_.DeviceInfo.Label -like "Hard disk *"} | %{
		$hd = $_
		$vm.LayoutEx.Disk | where {$_.Key -eq $hd.Key} | %{
			$diskFiles = $_.Chain | %{$_.FileKey} | %{$_}
		}

		$diskAllocated = 0
		$vmdkName = $vm.LayoutEx.File[$diskFiles[0]].Name

		$vm.LayoutEx.File | where {$diskFiles -contains $_.Key} | %{
			$diskAllocated += $_.Size
		}

		$used = "na"
		if($hd.Backing.ThinProvisioned){
			$used = ("{0:P1}" -f ($diskAllocated / 1KB/$hd.CapacityInKB))
		}

		$diskInfo = New-Object DiskInfo
		$diskInfo.VMname = $vm.Name
		$diskInfo.Label = $hd.DeviceInfo.Label
		$diskInfo.Path = $vmdkName
		$diskInfo.Thin = $hd.Backing.ThinProvisioned
		$diskInfo.RDM = $hd.Backing.GetType().Name.Contains("RawDisk")
		$diskInfo.Used = $used
		$diskInfo.CapacityMB = ("{0:N1}" -f ($hd.CapacityInKB/1KB))
		$diskInfo.AllocatedMB = ("{0:N1}" -f ($diskAllocated / 1MB))
		$snapNr = 1
		if($vm.Snapshot){
			$vm.LayoutEx.Snapshot | %{
				$_.Disk | where {$_.Key -eq $hd.Key} | %{
					$prevFiles = $_.Chain | %{$_.FileKey} | %{$_}
				}
				$vm.LayoutEx.Disk | where {$_.Key -eq $hd.Key} | %{
					foreach($chain in $_.Chain){
						if($prevFiles -notcontains $chain.FileKey[0] -and $prevFiles -notcontains $chain.FileKey[1]){
							break
						}
					}
				}
				$snapFiles = $chain.FileKey | %{$_}
				$snapSize = ""
				$snapName = ""
				if($snapFiles){
					$snapAllocated = 0
					$vm.LayoutEx.File | where {$snapFiles -contains $_.Key} | %{
						$snapAllocated += $_.Size
					}
					$snapSize = ("{0:N1}" -f ($snapAllocated / 1MB))
					$snapName = $snapHash[$_.Key.Value]
				}
				$diskInfo.("Snap" + $snapNr + "MB") = $snapSize
				$diskInfo.("Snap" + $snapNr + "Name") = $snapName
				$snapNr++
			}
		}
		$diskInfo
	}
} | Export-Csv "C:\Yadr-report.csv" -UseCulture -NoTypeInformation

Annotations

Line 1-11: The “static” part of the C# code that defines the DiskInfo type.

Line 13: Get all the virtual machines. I used the Get-View cmdlet for speed and also because the script only needs properties from the VirtualMachine object.

Line 14: Determine the highest number of snapshot present on any guest.

Line 15-18: The “dynamic” part of the C# code. For each snapshot this adds two properties, SnapiName and SnapiMB,

Line 19: The “newline” character is not really. Just gives a better view when you dump the content of the $DiskInfoDef variable.

Line 21: The new type is created. Note that when you change anything in the definition, you will most probably have to restart your PS environment, otherwise you will get a message that the type already exists. There is, afaik, no way to get rid of a type definition in the .Net environment.

Line 23-30: This filter will recursively run through the snapshot tree and will create a hash table. The table will later be used to find the snapshot name.

Line 32-98: The main loop.

Line 35-37: Create a hash table from the snapshot tree.

Line 39: Loop through all the virtual disks connected to this virtual machine.

Line 41-43: Get all the FileKeys for this specific virtual disk

Line 46: Get the name of the first disk file.

Line 48-50: Summarise the size of each file that comprises the virtual hard disk.

Line 52-55: Check if the virtual hard disk is Thin Provisioned. If it is, calculate the percentage of storage that is actually allocated on the datastore.

Line 57: Create a new object based on the DiskInfo type.

Line 58-65: Start filling in the values for the properties of the DiskInfo object.

Line 66-94: Handle the snapshots (if present) for this virtual hard disk.

Line 67: Start by checking if there is at least one snapshot for this virtual hard disk.

Line 68: Loop through all the snapshots.

Line 69-71: Get the FileKey for all the files for this virtual hard disk before the snapshot was taken. Store these FileKey values in the $prevFiles array.

Line 72-78: Find the first pair of FileKeys in the chain that is not in the $prevFiles array. These are the files that constitute the snapshot.

Line 79: Store the pair of FileKeys in the array $snapFiles.

Line 82-89: Calculate the total size of the snapshot files and get the name of the snapshot from the hash table created in the beginning of the main loop.

Line 90-91: Store the values in the correct properties in the DiskInfo object.

Line 97: Export the array to a CSV file.

Result

The following screenshot shows a sample run of the script.

There are some interesting results in this sample run.

  • The maximum snapshots any guest has is two.
  • Server1 (row 2-5) has three virtual hard disks. The first one is thin provisioned and uses currently 47.7% of it’s capacity.
  • The other two hard disks of Server1 are RDM disks. Notice that the AllocatedMB column says “zero” because there is not really a point in talking about allocated space for RDM disks.
  • The guest in row 8 has two snapshots.

And last but not least, I suspect there is a bug in the VirtualMachineFileLayoutEx object, more specifically in the Size property of some entries in the Files array.

Have a look at row 20, this disk is not a thin provisioned disk but the AllocatedMB value is less then the CapacityMB value. I would have expected the same value in those two columns.

When I investigate from the console with a simple “ls -l” I see the correct value.

For one reason or another, there are some (very few) virtual disks that have an incorrect size values in the Files table. I couldn’t find out in which cases this seems to happen.

PS: yadr stands for yet another disk reporter

  1. Robert
    January 25th, 2012 at 13:57 | #1

    Thank you for this great script
    Replacing line 13 with:
    $vms = Get-Datacenter $datacenter| Get-VM | Get-View | sort name
    makes more sense as it will not get templates. If you have any template in the collection you get an error as mentioned in some comments here.

    • January 25th, 2012 at 14:14 | #2

      Thanks Robert. That is a great tip.
      I’ll update the script.

  2. October 5th, 2011 at 22:34 | #3

    Is there a way to get snapshot info for VMs that are experiencing snapshot problems, i.e. Broken child parent CID or snapshots not showing up in SnapShot Manager?

    Thanks!!

    MikeP

    • October 5th, 2011 at 22:44 | #4

      @MikeP, I’m afraid not. The script uses the Snapshot property in the VirtualMachine object to find the snapshots.
      If the chain of snapshots is not correct, the script will not show these broken snapshots.
      I suspect you will have to have a look at the headers of some of the .vmdk files to retrieve “broken” snapshots.

  3. Simon
    March 10th, 2011 at 18:48 | #5

    Hi Luc

    Any idea about what the problem is here ?

    Cannot index into a null array.
    At :line:52 char:32
    + $vmdkName = $vm.LayoutEx.File[ <<<< $diskFiles[0]].Name

    (line 52 is line 46 in your script – but all I've added is viserver connect and snapin etc)

    Regs
    Simon

    • March 10th, 2011 at 19:39 | #6

      @Simon, could it be that you run the script against a pre-ESX(i) 4.x server ?
      The LayoutEx property was introduced with ESX(i) 4.x

  4. steve
    February 23rd, 2011 at 20:03 | #7

    Luc,

    I am trying to use your script to return the guest.disk.diskpath and it keeps coming up empty. I defined “public string Drive” in the Diskinfo and added $diskinfo.Drive = $vm.Guest.Disk.DiskInfo and it did not work. I should note that the machine I am testing on has multiple drives.

    • February 23rd, 2011 at 23:13 | #8

      @Steve. The yadr script looks at the virtual disks as they are seen by the ESX(i) host for a specific guest.
      The DiskPath you mention is the path to the partition defined inside the OS running in the guest. This information is provided through the VMware Tools but there is no link to the virtual disks. To make that link the script would need to find a method to link these OS partitions to virtual disks. That is currently not possible with the PowerCLI cmdlets nor by any of the SDK methods.
      Arnim did an excellent write-up on this in his PowerCLI: Match VM and Windows harddisks – Part 2 post.

  5. Paulo Aguiar
    October 14th, 2010 at 23:24 | #9

    @LucD
    Thank you ver much man…
    I envy you btw for be in VMworld I bet is amazing as always
    cheers =)

  6. Paulo Aguiar
    October 14th, 2010 at 19:43 | #10

    Hello LucD:
    After see this amazing script I immediatly try it and worked pretty fine but I am in a need to only use the part where you do the RDM analysis and merge it with my script I am trying to do it but I have been failing could you give me a hand with this buddy?
    This is the script I am actually using:

    Get-VM | Get-VMGuest | ForEach-Object {
    $VMGuest = $_
    $VMGuest.Disks | ForEach-Object {
    $Report = “” | select-Object Hostname,Volume,”Allocated MB”,”Used MB”,”Free MB”
    $Report.Hostname = $VMGuest.VmName
    $Report.Volume = $_.Path
    $Report.”Allocated MB” = (“{0:N1}” -f ($_.Capacity / 1MB))
    #$Report.”Allocated MB” = int $Report.”Allocated MB”
    $Report.”Used MB” = (“{0:N1}” -f (($_.Capacity – $_.FreeSpace)/1MB))
    #$Report.”Used MB” = int $Report.”Used MB”
    $Report.”Free MB” = (“{0:N1}” -f ($_.FreeSpace / 1MB))
    $Report
    }
    }

    • October 14th, 2010 at 22:04 | #11

      @Paulo. Thanks. I’ll have a look how we can incorporate the RDM part in your script.
      First I’m going to sleep of the VMworld fatigue ;-)

  7. MasterPi
    October 6th, 2010 at 03:01 | #12

    @LucD
    Hi LucD,

    yes, i start the PowerCli promt, type Connect-VIServer vcenter
    then i type c:\report.ps1

    there is no errormessage/output on the console and the script finishes in about 10 sec.

    And yes, Get-View -ViewType VirtualMachine works fine

  8. MasterPi
    October 5th, 2010 at 08:16 | #13

    Hi LucD,

    I have a Problem with your script.
    When i run it i get no output. the csv file is created but empty. Any obvius reason why this could happen?

    • October 5th, 2010 at 13:29 | #14

      @MasterPi How did you run the script ? From the PowerCLI prompt ? Any error messages ? Does ‘Get-View -ViewType VirtualMachine’ return anything ?

  9. Sathya
    August 2nd, 2010 at 04:51 | #15

    @LucD anyway no issues, let me try installing PS2.0 and try again.

    Thank you.

  10. Sathya
    July 30th, 2010 at 14:11 | #16

    Hi LucD, now i’m using on vSphere network. i’m getting the below error.
    The term ‘Add-Type’ is not recognized as a cmdlet, function, operable program,
    or script file. Verify the term and try again.
    At D:\yadr.ps1:21 char:9
    + Add-Type <<<< -Language CsharpVersion3 -TypeDefinition $DiskInfoDef
    Export-Csv : A parameter cannot be found that matches parameter name 'UseCultur
    e'.
    At D:\yadr.ps1:97 char:48
    + } | Export-Csv "D:\Yadr-report.csv" -UseCulture <<<< -NoTypeInformation

    • July 30th, 2010 at 14:25 | #17

      @Sathya, that indicates you’re using PowerShell v1. The Add-Type cmdlet, which is essential for this script, requires PowerShell v2. Sorry, should have been indicated in the post.

  11. Sathya
    July 29th, 2010 at 11:34 | #18

    @LucD u r right. i’m running this script against ESX 3.5 Hosts.

  12. Sathya
    July 28th, 2010 at 18:03 | #19

    Hi LucD, i’m having hard time to run this script, getting the below error.
    Cannot index into a null array.
    At C:\yadr.ps1:46 char:44
    + $vmdkName = $vm.LayoutEx.File[$diskFiles[ <<<< 0]].Name
    + CategoryInfo : InvalidOperation: (0:Int32) [], RuntimeException
    + FullyQualifiedErrorId : NullArray

    • July 28th, 2010 at 19:26 | #20

      @Sathya, I suspect you’re running this against ESX 3.x servers ?
      The LayoutEx property is only available since API 4, which means vSphere 4.x

  13. June 22nd, 2010 at 10:24 | #21

    Hi Luc, I´ve encountered an error if the vCenter Installation is localized (i. e. German), so the Hard disk gets back as Festplatte.
    There is an easy change to run the script in any localized VMware environment, I wanna share.

    Just change the line 39

    $_.Config.Hardware.Device | where {$_.DeviceInfo.Label -like “Hard disk *”}

    to

    $_.Config.Hardware.Device | where {$_ -is [VMware.Vim.VirtualDisk]}

  14. Vuong Pham
    May 12th, 2010 at 23:24 | #23

    This is a fantastic YADR!!
    calculations:
    Trying to decipher the data to see how much disk is actually saved via thin disk ..
    I sort out the thin= false

    or figure out what the over-allocated value is.

    Do a rollup report to management.. “with thin disk prov.. we’ve now saved X disk space and this month.. etc” and “translates to $$ saving”

    – more interesting would be to trend this as a rate to calculate how much disk usage is happening even under thin provisioning..

    • May 13th, 2010 at 00:50 | #24

      Thanks, glad you like it. And that you could put it to good use.

  15. April 8th, 2010 at 23:05 | #25

    Need info like in Perl Scripts…
    requires PowerShell V2.0
    requires VMwareVI Toolkit
    requires vSphere 4.0

    • April 9th, 2010 at 07:01 | #26

      You’re absolutely right.
      I’ll check how this can be done and I’ll amend the posts accordingly.
      Thanks.

  16. Andrea
    April 8th, 2010 at 02:42 | #27

    This is great LucD!
    Interestingly I got the following error, looped 7 times in my environment the first time I tried your script:
    “Cannot index into a null array.
    At C:\yadr.ps1:46 char:33
    + $vmdkName = $vm.LayoutEx.File[ <<<< $diskFiles[0]].Name
    + CategoryInfo : InvalidOperation: (2:Int32) [], RuntimeException
    + FullyQualifiedErrorId : NullArray
    "
    I had some templates that had not been labeled as VMs since moving to vSphere. Their "provisioned" and "used" values in VIC were n/a. converting to VMs filled in these values and switching them back to templates kept the values, no more script errors.

    I'm sure there's a quick error check you could do for null values coming in. I just thought I would pass back this info.

    • April 8th, 2010 at 07:59 | #28

      Thanks for the info.
      Do you still have one of those templates ?
      It would be interesting to know if $vm.LayoutEx was equal to $null or if $vm.LayoutEx.File was equal to $null.

      If you still have such a template could you do


      $templateName = "your-template-name"
      Get-View -ViewType "VirtualMachine" -Filter @{"Name"=$templateName} | Select -ExpandProperty LayoutEx

      You may send the result to lucd(at)lucd(dot)info

  17. Brian
    March 30th, 2010 at 14:25 | #29

    Do you have this script that I can download complete? I did a copy/past and have lots of errors. I might be doing something very simple…. just not sure what is wrong.

    • March 30th, 2010 at 17:31 | #30

      When you hover over the box that contains the script, there should appear 4 icons in the top-right of the box. The 2nd icon copies the script.

  18. vmachine
    March 24th, 2010 at 07:01 | #31

    Thanks Luc, awesome work!!

  19. March 23rd, 2010 at 21:04 | #32

    regarding line #20: The other way is dangerous! If you have a vmdk that sais 10GB and a flat-file with 20GB in size, it will work fine – as long as you don’t do svmotion or sometimes snapshots.

    • March 23rd, 2010 at 21:24 | #33

      Would you happen to know what could cause these discrepancies ?

  1. March 24th, 2010 at 10:17 | #1
  2. October 14th, 2010 at 14:27 | #2