In the vCenter Client, since vSphere 4, you can find a Storage Views tab on several of the VI containers. The data in these Storage Views is collected and provided by the vCenter Storage Monitoring plug-in.
Have a look at David Davis‘s post, called Using VMware vSphere Storage Views, for more information on what you can do with the Storage Views.
Some time ago I got a question from Andrew how the Multipathing Status presented in the Storage Views could be detected and reported upon by a PowerCLI script. What looked rather simple at first, turned out to be a bit more difficult than I anticipated.
The primary reason I created this function, is that it will allow to schedule the creation of the Storage Views – Datastores values at regular intervals. A feature that is unfortunately missing from the vCenter Client plug-in.
With those saved values it is easy to produce reports that show who is consuming the storage over time.
The script
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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
function Get-StorageViewDatastore{ <# .SYNOPSIS Retrieve the "Storage Views - Show all Datastores" values .DESCRIPTION The function calculates and returns all the values that you get in the Storage Views - Show all Datastores panel. .NOTES Author: Luc Dekens .PARAMETER VMHost Specify the ESX(i) server for which you want to retrieve the Storage Views values. .PARAMETER Cluster Specify the Cluster for which you want to retrieve the Storage Views values. .PARAMETER Datacenter Specify the Datacenter for which you want to retrieve the Storage Views values. .PARAMETER VM Specify the VM for which you want to retrieve the Storage Views values. .PARAMETER ResourcePool Specify the ResourcePool for which you want to retrieve the Storage Views values. .EXAMPLE PS> Get-StorageViewDatastore -VMHost MyCluster .EXAMPLE PS> Get-Datacenter MyDC | Get-StorageViewDatastore #> [CmdletBinding()] param( [parameter(ParameterSetName="VMHost",Mandatory = $true)] [PSObject]$VMHost, [parameter(ParameterSetName="Cluster",Mandatory = $true)] [PSObject]$Cluster, [parameter(ParameterSetName="Datacenter",Mandatory = $true)] [PSObject]$Datacenter, [parameter(ParameterSetName="VM",Mandatory = $true)] [PSObject]$VM, [parameter(ParameterSetName="ResourcePool",Mandatory = $true)] [PSObject]$ResourcePool ) process{ # Get the datastores and ESX(i) servers switch($PSCmdlet.ParameterSetName){ "Datacenter" { if($Datacenter.GetType().Name -eq "String"){ $Datacenter = Get-Datacenter -Name $Datacenter } $vms = Get-VM -Location $Datacenter $esx = Get-VMHost -Location $Datacenter $datastores = Get-Datastore -VMHost $esx } "Cluster" { if($Cluster.GetType().Name -eq "String"){ $Cluster = Get-Cluster -Name $Cluster } $vms = Get-VM -Location $cluster $esx = Get-VMHost -Location $Cluster $datastores = Get-Datastore -VMHost $esx } "VMHost" { if($VMHost.GetType().Name -eq "String"){ $VMHost = Get-VMHost -Name $VMHost } $vms = Get-VM -Location $VMHost $esx = $VMHost $datastores = Get-Datastore -VMHost $VMHost } "VM" { if($VM.GetType().Name -eq "String"){ $VM = Get-VM -Name $VM } $vms = $vm $esx = $vm.VMHost $datastores = Get-Datastore -Name ($vms | Get-HardDisk -DiskType flat | %{$_.Filename.Split(']')[0].TrimStart('[')} | Sort-Object -Unique) | Sort-Object -Property Name } "ResourcePool" { if($ResourcePool.GetType().Name -eq "String"){ $ResourcePool = Get-ResourcePool -Name $ResourcePool } $vms = Get-VM -Location $ResourcePool $esx = $vms | %{$_.VMHost} | Sort-Object -Property Name -Unique $datastores = Get-Datastore -Name ($vms | Get-HardDisk -DiskType flat | %{$_.Filename.Split(']')[0].TrimStart('[')} | Sort-Object -Unique) | Sort-Object -Property Name } } # Create some helper collections $esxMoRef = $esx | %{$_.ExtensionData.MoRef} $vmMoRef = $vms | %{$_.ExtensionData.MoRef} # Set up a HBA hash table $hbaFCNodeWWN = @{} Get-VMHostHba -VMHost $esx -Type FibreChannel | %{ $hbaFCNodeWWN["{0:X}" -f $_.NodeWorldWideName] = 0 } # Handle all datastores foreach($ds in $datastores){ $mpStatus = $usedSpace = $snapSpace = "" if($ds.Type -eq "VMFS"){ $lun = $ds.ExtensionData.Info.Vmfs.Extent | %{$_.DiskName} | %{Get-ScsiLun -CanonicalName $_ -VmHost $esx -ErrorAction SilentlyContinue} $hbaFCCopy = $hbaFCNodeWWN.Clone() # Count Active and Standby paths # If there are 2 or more paths, the multi-path status is fully redundant Get-ScsiLunPath -ScsiLun $lun | where {"Active" ,"Standby" -contains $_.State} | %{ $hbaFCCopy[$_.LunPath.Split(':')[0].Split('.')[1]] += 1 } if(($hbaFCCopy.Values | where {$_} | Measure-Object).Count -gt 1){ $mpStatus = "Full Redundancy" } else{ $mpStatus = "Partial/No Redundancy" } } # Get the total used, snapshot, swap, shared and other space $usedSpace = 0 $snapSpace = 0 $vdiskSpace = 0 $swapSpace = 0 $otherSpace = 0 $sharedSpace = 0 if($ds.ExtensionData.Vm){ Get-View $ds.ExtensionData.Vm | where {$vmMoRef -contains $_.MoRef -and $esxMoRef -contains $_.Runtime.Host} | %{ $snapIndex = @() $dsPattern = '\[' + $ds.Name + '\]' $usedSpace += ($_.Storage.PerDatastoreUsage | where {$_.Datastore -eq $ds.ExtensionData.MoRef}).Committed if($_.LayoutEx.Snapshot){ $snapIndex = $_.layoutEx.Disk | %{$_.Chain[1..($_.Chain.Count - 1)]} | %{$_.FileKey} } $snapIndex += $_.layoutEx.File | where {"snapshotList","snapshotData" -contains $_.Type -and $_.Name -match $dsPattern} | %{$_.Key} if($snapIndex){ $snapSpace += ($_.LayoutEx.File | where {$snapIndex -contains $_.Key -and $_.Name -match $ds.Name} | Measure-Object -Property Size -Sum).Sum } $vdiskIndex = $_.layoutEx.File | where {"diskDescriptor","diskExtent" -contains $_.Type -and $_.Name -match $dsPattern} | %{$_.Key} if($vdiskIndex){ $vdiskSpace += ($_.LayoutEx.File | where {$vdiskIndex -contains $_.Key} | Measure-Object -Property Size -Sum).Sum } $swapIndex = $_.LayoutEx.File | where {$_.Type -eq "swap" -and $_.Name -match $dsPattern} | %{$_.Key} if($swapIndex){ $swapSpace += ($_.LayoutEx.File | where {$swapIndex -contains $_.Key} | Measure-Object -Property Size -Sum).Sum } $otherIndex = $_.LayoutEx.File | where {"log","config","extendedConfig","nvram","core" -contains $_.Type -and $_.Name -match $dsPattern} | %{$_.Key} if($otherIndex){ $otherSpace += ($_.LayoutEx.File | where {$otherIndex -contains $_.Key} | Measure-Object -Property Size -Sum).Sum } $vmds = $_.Storage.PerDatastoreUsage | where {$_.Datastore -eq $ds.ExtensionData.MoRef} if($vmds){ $sharedSpace += ($vmds.Committed - $vmds.Unshared) } } } # Collect all values for Storage Views - Datastores New-Object PSObject -Property @{ VMHost = $esx.Name Datastore = $ds.Name "File System Type" = $ds.Type.Replace("NFS","NAS") "Connectivity Status" = &{if($ds.Accessible){"Up"}else{"Down"}} "Multipathing Status" = $mpStatus Capacity = Get-FriendlyUnit -Value ($ds.CapacityMB * 1MB) Free = Get-FriendlyUnit -Value ($ds.FreeSpaceMB * 1MB) Used = Get-FriendlyUnit -Value $usedSpace Snapshot = Get-FriendlyUnit -Value $snapSpace vDisk = Get-FriendlyUnit -Value ($vdiskSpace - $snapSpace) Swap = Get-FriendlyUnit -Value $swapSpace Other = Get-FriendlyUnit -Value $otherSpace Shared = Get-FriendlyUnit -Value $sharedSpace } } } } |
Annotations
Line 29-40: The function uses 5 parameter sets that allow you to get the Storage Views – Datastores values for a VM, an ESX(i) server, a ResourcePool, a Cluster or a Datacenter.
Line 44-85: For each of the parameter sets, this Switch block calculates the VMs, the ESX(i) host(s) and the datastore(s) that need to be queried. Notice that each of the 5 parameter sets support a kind of Object By Name (OBN), that means you can pass the name or the object itself.
Line 88-89: The script will use these MoRef collections later on to determine if a specific VM needs to be included in the calculations for the Storage Views values.
Line 92-95: To determine the Multipathing Status the script first creates a hash table for all the FC HBAs in the ESX(i) servers. The key for this hash table is the WWN, in hex format, for the HBA.
Line 98-171: The main loop over all the datastores that the script needs to handle.
Line 101-102: The script collects all the LUNs that are part of the datastore.
Line 103: The Multipathing logic works with a copy of the hash table that was created earlier.
Line 106-108: In the hash table the script increments the value for each LUN path that is Active or Standby.
Line 109-114: If a LUN has more then 1 path, the Multipathing Status is considered as Full Redundancy. Otherwise the status is Partial/No Redundancy.
Line 118-153: The script calculates all values that are related to the storage in use by the VMs.
Line 125: For a VM to be taken into account, the VM must use storage on the datastore and must be hosted by one of the ESX(i) host.
Line 127: This Regular Expression will be used later on to verify the a specific file is located on the datastore. Note that the square brackets need to be escaped.
Line 128: The Space Used value is the sum of all the Committed space for each of the VMs on that datastore.
Line 129-135: The space used by snapshots is the sum of all the VMDK snapshot files and the snapshotList and the snapshotData files.
Line 132: Note that we check for each file used for the Snapshot Space calculation, if it is located on the datastore with the -match operator. This is required since the administrator or user can define that snapshot files need to be stored on another datastore.
Line 136-139: All the files used for the vDisks of a VM.
Line 140-143: All swap related files
Line 144-147: All other files that are used by the VM.
Line 148-151: This block calculates the Shared Space for a VM. Examples of shared space usage are linked clones that are created from a snapshot with the createNewChildDiskBacking option for the diskMoveType (see the VirtualMachineRelocateSpec).
Line 156-170: Create the object, per datastore, with all the values. To have similar values that are presented in the Storage Views panel, the script uses the Get-FriendlyUnits function from my Friendly Units post.
Line 166: Note that for the Virtual Disk Space value we need to subtract the Snapshot Space from the total disk space.
Samples
You can pass the function the name of the object for which you want the Storage Views – Datastore values.
1 2 3 4 5 6 7 8 9 10 11 |
Get-StorageViewDatastore -Cluster MyCluster | Select-Object Datastore,"File System Type","Connectivity Status","Multipathing Status", @{N="Capacity";E={"{0,7:f2} {1,2}" -f $_.Capacity.Value,$_.Capacity.Unit}}, @{N="Free Space";E={"{0,7:f2} {1,2}" -f $_.Free.Value,$_.Free.Unit}}, @{N="Space Used";E={if($_.Used.Value){"{0,7:f2} {1,2}" -f $_.Used.Value,$_.Used.Unit}}}, @{N="Snapshot Space";E={if($_.Snapshot.Value){"{0,7:f2} {1,2}" -f $_.Snapshot.Value,$_.Snapshot.Unit}}}, @{N="Virtual Disk Space";E={if($_.vDisk.Value){"{0,7:f2} {1,2}" -f $_.vDisk.Value,$_.vDisk.Unit}}}, @{N="Swap Space";E={if($_.Swap.Value){"{0,7:f2} {1,2}" -f $_.Swap.Value,$_.Swap.Unit}}}, @{N="Other VM Space";E={if($_.Other.Value){"{0,7:f2} {1,2}" -f $_.Other.Value,$_.Other.Unit}}}, @{N="Shared Space";E={if($_.Shared.Value){"{0,7:f2} {1,2}" -f $_.Shared.Value,$_.Shared.Unit}}} | Format-Table -AutoSize |
Or you can pass the actual object.
1 2 3 4 5 6 7 8 9 10 11 12 |
$dc = Get-Datacenter -Name MyDC Get-StorageViewDatastore -Datacenter $dc | Select-Object Datastore,"File System Type","Connectivity Status","Multipathing Status", @{N="Capacity";E={"{0,7:f2} {1,2}" -f $_.Capacity.Value,$_.Capacity.Unit}}, @{N="Free Space";E={"{0,7:f2} {1,2}" -f $_.Free.Value,$_.Free.Unit}}, @{N="Space Used";E={if($_.Used.Value){"{0,7:f2} {1,2}" -f $_.Used.Value,$_.Used.Unit}}}, @{N="Snapshot Space";E={if($_.Snapshot.Value){"{0,7:f2} {1,2}" -f $_.Snapshot.Value,$_.Snapshot.Unit}}}, @{N="Virtual Disk Space";E={if($_.vDisk.Value){"{0,7:f2} {1,2}" -f $_.vDisk.Value,$_.vDisk.Unit}}}, @{N="Swap Space";E={if($_.Swap.Value){"{0,7:f2} {1,2}" -f $_.Swap.Value,$_.Swap.Unit}}}, @{N="Other VM Space";E={if($_.Other.Value){"{0,7:f2} {1,2}" -f $_.Other.Value,$_.Other.Unit}}}, @{N="Shared Space";E={if($_.Shared.Value){"{0,7:f2} {1,2}" -f $_.Shared.Value,$_.Shared.Unit}}} | Export-Csv "C:\SV-MyDC.csv" -NoTypeInformation -UseCulture |
The previous example also showed how the results can be saved to a CSV file.
Such a CSV file looks as follows.
Enjoy the function !
Kiran
Taking long time tho.. thanks for sharing
Brad S
Great script any way to increase the performance? Running it seems to take forever =)
Islander
LucD,
I am still lost, I do not understand how to run this script, if you can explained one more time to me, I copied and saved the script as a getstorage-view.PS1 executed and does not do anything.
I’ve looking for a a script that tells me what are the status of my SAN, my SAN currently is overflowing, because file overallocation, I would like to know how may I approach this problem. I found two scripts that are closed to what I am looking for, if may be there is a way to incorporate some values that will do the trick, here are the two:
Here are the line of code that I would like to incorporate:
get-vm |sort | % {$_.name ; ($_|get-harddisk | % {$_.filename;$_.capacitykb})}
That is driving me crazy, again the main problem is that many of my LUN’s are running out of space, but if could identify, the VMDK’s or Template files, where and what are they, I should be able to find a solution.
First Script:
Import-Module VIProperty
$users = “user@xyz.xyz” # List of users to email your report to (separate by comma)
$fromemail = “user@xyz.xyz”
$server = “smtp.server.xyz” #enter your own SMTP server DNS name / IP address here
$datastore = $null
$Report = @()
$AllDatastores = Get-Datastore | Where {$_.Name -notlike “datastore*”} | Sort Name
foreach ($datastore in $AllDatastores) {
$Report += “”
$row = “” | Select Name, FreeGB, CapacityGB, ProvisionedGB, PercentFree
$row.Name = $datastore.Name
$row.FreeGB = $datastore.FreeGB
$row.CapacityGB = $datastore.CapacityGB
$row.ProvisionedGB = $datastore.ProvisionedGB
$row.PercentFree = $datastore.PercentFree
$Report += $row | ConvertTo-Html
#List VMs on the datastore if applicable
If ($datastore.ExtensionData.Vm.Count -gt 0){
$VMList = Get-View -Id $datastore.ExtensionData.Vm -Property Name | Select Name
$Report += “”
$Report += “VMs on this Datastore:”
$VMTable = @()
foreach ($vm in $VMList) {
$VMContentRow = “” | Select Name, PowerState
$tmp = Get-VM $vm.Name
$VMContentRow.Name = $tmp.Name
Write-Host $tmp.Name
if ($tmp.PowerState -eq “PoweredOn”) {
$VMContentRow.PowerState = $tmp.PowerState
}
else {
$VMContentRow.PowerState = $tmp.PowerState
}
$VMTable += $VMContentRow
}
$Report += $VMTable | ConvertTo-Html -Fragment
}
else {
$Report += “”
$Report += “VMs on this Datastore:”
$Report += “Name”
$Report += “No VMs on this Datastore”
}
$Report += “” #Line break for clarity
$Report += “”
}
# HTML formatting for email
$HTMLmessage = @”
Datastore Report
body{font: .8em “”Lucida Grande””, Tahoma, Arial, Helvetica, sans-serif;}
ol{margin:0;padding: 0 1.5em;}
TABLE{border-width: 1px;border-style: solid;border-color: black;}
TH{border-width: 1px;padding: 3px;border-style: solid;border-color: black;}
TD{text-align:center;border-width: 1px;padding: 5px;border-style: solid;border-color: black;}
table{color:#000;background:#99CCFF;border-collapse:collapse;width:647px;border:3px solid #900;}
thead{}
thead th{padding:1em 1em .5em;border-bottom:1px dotted #FFF;font-size:120%;text-align:left;}
thead tr{}
td{padding:.5em 1em;}
tfoot{}
tfoot td{padding-bottom:1.5em;}
tfoot tr{}
#middle{background-color:#900;}
$Report
“@
$HTMLmessage | Out-File C:\DSReport.html #Also write to an HTML report on disk – with basic formatting, in addition to the email report you are sent.
#Send the final report!
send-mailmessage -from $fromemail -to $users -subject “Datastore Report” -BodyAsHTML -body $HTMLmessage -smtpServer $server
NOTE: here is the line of code that I am struggling with, I do not know where to place it:
get-vm |sort | % {$_.name ; ($_|get-harddisk | % {$_.filename;$_.capacitykb})}
Here is a second script, it does the same as the first one, but this one has less lines with pretty much the same approach:
Script 2:
Import-Module VIProperty
$users = “yourmail@yourdomain.com” # List of users to email your report to (separate by comma)
$fromemail = “yourmail@yourdomain.com”
$server = “yourmailserver.com” #enter your own SMTP server DNS name / IP address here
$Report = @()
## done to exclude local datastores?
$AllDatastores = Get-Datastore | Where {$_.Name -notlike “datastore*”} | Sort Name
foreach ($datastore in $AllDatastores) {
$Report += “”
$row = New-Object -Type PSObject -Property @{
Name = $datastore.Name
FreeGB = $datastore.FreeGB
CapacityGB = $datastore.CapacityGB
PercentFree = $datastore.PercentFree
} ## end new-object
$Report += $row | ConvertTo-Html
#List VMs on the datastore if applicable
$Report += “”
$Report += “VMs/Templates on this Datastore:”
if ($datastore.ExtensionData.Vm.Count -gt 0){
$VMViewList = Get-View -Id $datastore.ExtensionData.Vm -Property Name,Runtime.PowerState,Config.Template
$VMTable = @()
foreach ($viewVM in $VMViewList) {
Write-Host $viewVM.Name
$VMContentRow = New-Object -Type PSObject -Property @{
Name = $viewVM.Name
PowerState = $viewVM.Runtime.PowerState
Other = if ($viewVM.Config.Template) {“Template”} else {“VM”}
} ## end new-object
$VMTable += $VMContentRow
}
$Report += $VMTable | ConvertTo-Html -Fragment
} ## end if
else {
$Report += ‘None on this Datastore’
} ## end else
$Report += “” #Line break for clarity
$Report += “”
} ## end foreach
$Report | Set-Content C:\DSReport.html #Also write to an HTML report on disk – with basic formatting, in addition to the email report you are sent.
Again, I would like to incorporate this line of code into the script:
get-vm |sort | % {$_.name ; ($_|get-harddisk | % {$_.filename;$_.capacitykb})}
Any assistance would be greatly appreciated.
Thanks,
Rue
By specifying the HBA type as “Fiberchannel’ won’t this give an incorrect multipath status for iscsi connections ?
Jen
Hi, I am not sure where to post this question, I hope this is ok 😉 …
I am trying to take datastore information, including the datacenter name, and export it to SQLServer. I am using both functions out-datatable and write-datatable which Marc van Orsouw has posted and someone else has modified (https://blogs.technet.com/b/heyscriptingguy/archive/2010/11/01/use-powershell-to-collect-server-data-and-write-to-sql.aspx).
I am getting all datastores and putting them in to an array with the datacenter name. However, the datacenter name has a hyphen (datacenter-xx). This will not write to sql because it is not in quotes I am told by my db admin. I have not been able to figure out how to get around this, would you help? Thanks!
LucD
Hi Jen, no problem.
I suppose your question is answered in the Remove the hyphen of the datacenter name for export purposes thread on the PowerCLI Community.
Sallu
This may be a stupid question but not sure how to run this script, its very important for me to make this script working.
can somebody tell me how do I run this script, and in which part of the script do I need to supply the values!!
LucD
@Sallu, there are no stupid questions 😉
The script is written as a function. In this function there are 5 parameter sets, each with 1 parameter, the vSphere object for which you want to generate the Storage View. That vSphere object can be an ESX(i) host, a Datacenter, a Cluster, a ResourcePool or a VM.
In the Samples section there are a couple of sample calls.
But you need to make the function ‘known’ to the PowerShell engine.
. ./DSView.ps1
Get-StorageViewDatastore -Datacenter MyDatacenter
I hope this helped you in running the function.
bs
great job!
Marcel Nobel
Another great script. Bedankt!