PowerCLI

 View Only
Expand all | Collapse all

Script to Generate IOPS Report

vAdmin

vAdminOct 13, 2013 11:53 PM

  • 1.  Script to Generate IOPS Report

    Posted May 25, 2013 06:38 AM

    Hello,

         I've been looking around for a script but I cant seem to find what i'm looking for. I would like something that would survey all VM's across all clusters and datastores (everything) And would generate a CSV report that show the IOPS usage per VM's.

    I just want to be able to evaluate which VM's in particular are taxing our storage arrays.

    Any help or pointers in the right direction would be appreciated.

    Thanks!



  • 2.  RE: Script to Generate IOPS Report

    Posted May 25, 2013 07:09 AM

    Have you already looked at my Get the maximum IOPS ?



  • 3.  RE: Script to Generate IOPS Report

    Posted May 25, 2013 11:26 PM

    Wow, thank you very much! That was really helpful.

    I plan on setting the minutes for 1440 to show me a daily report of MAX IOPS, is there a way to show the daily average IOPS as well on the next column over? Then export it to CSV, organize by average in decending format and send me an email every morning... Haha. I'm sure I'll be able to string together something using this very helpful script!



  • 4.  RE: Script to Generate IOPS Report
    Best Answer

    Posted May 26, 2013 02:06 PM

    You mean something like this ?

    It collects the IOPS for the complete previous day and send the result via email

    $metrics = "disk.numberwrite.summation","disk.numberread.summation"
    $finish = Get-Date -Hour 0 -Minute 0 -Second 0
    $start = $finish.AddDays(-1)
    $report = @()

    $vms = Get-VM | where {$_.PowerState -eq "PoweredOn"}
    $stats = Get-Stat -Stat $metrics -Entity $vms -Start $start -Finish $finish
    $interval = $stats[0].IntervalSecs

    $lunTab = @{}
    foreach($ds in (Get-Datastore -VM $vms | where {$_.Type -eq "VMFS"})){
     
    $ds.ExtensionData.Info.Vmfs.Extent | %{
       
    $lunTab[$_.DiskName] = $ds.Name
      }
    }

    $report = $stats | Group-Object -Property {$_.Entity.Name},Instance | %{
     
    $readStat = $_.Group |
       
    where{$_.MetricId -eq "disk.numberread.summation"} |
       
    Measure-Object -Property Value -Average -Maximum
     
    $writeStat = $_.Group |
       
    where{$_.MetricId -eq "disk.numberwrite.summation"} |
       
    Measure-Object -Property Value -Average -Maximum
     
    New-Object PSObject -Property @{
       
    VM = $_.Values[0]
       
    Start = $start
       
    Finish = $finish
       
    Disk = $_.Values[1]
       
    IOPSWriteMax = [math]::Round($writeStat.Maximum/$interval,0)
       
    IOPSWriteAvg = [math]::Round($writeStat.Average/$interval,0)
       
    IOPSReadMax = [math]::Round($readStat.Maximum/$interval,0)
       
    IOPSReadAvg = [math]::Round($readStat.Average/$interval,0)
       
    Datastore = $lunTab[$_.Values[1]]
      }
    }

    Send-MailMessage -Subject "IOPS Report" -From lucd@lucd.info `
     
    -To lucd@lucd.info -SmtpServer mail.lucd.info `
     
    -BodyAsHtml -Body ($report | Select VM,Start,Finish,Disk,Datastore,IOPSWriteAvg,
       
    IOPSWriteMax,IOPSReadAvg,IOPSReadMax | ConvertTo-Html | Out-String)


  • 5.  RE: Script to Generate IOPS Report

    Posted May 27, 2013 06:09 AM

    Wow....thanks a lot!

    I ran into an issue when I tried to run it. Any ideas?

    Cannot index into a null array.

    At C:\iopsreport.ps1:8 char:20

    + $interval = $stats[ <<<< 0].IntervalSecs

        + CategoryInfo          : InvalidOperation: (0:Int32) [], RuntimeException

        + FullyQualifiedErrorId : NullArray



  • 6.  RE: Script to Generate IOPS Report

    Posted May 27, 2013 06:30 AM

    That indicates that no data was returned from the Get-Stat cmdlet.

    Were the VM you selected powered on during the interval you selected ?

    Is the Statistics Level set to the required level for the interval you selected ? These counters require statistics level 3.



  • 7.  RE: Script to Generate IOPS Report

    Posted May 28, 2013 02:07 AM

    I ran it again and it worked. Thanks!!

    Do you know of any way to add the Max read and Max write values? As well as the Average Read and Average Write? Giving IOPSAvg and IOPSMax?

    I tried this but I got numbers far higher than I expected.

    $metrics = "disk.numberwrite.summation","disk.numberread.summation"

    $finish = Get-Date -Hour 0 -Minute 0 -Second 0

    $start = $finish.AddDays(-1)

    $report = @()

    $vms = Get-VM | where {$_.PowerState -eq "PoweredOn"}

    $stats = Get-Stat -Stat $metrics -Entity $vms -Start $start -Finish $finish

    $interval = $stats[0].IntervalSecs

    $lunTab = @{}

    foreach($ds in (Get-Datastore -VM $vms | where {$_.Type -eq "VMFS"})){

      $ds.ExtensionData.Info.Vmfs.Extent | %{

        $lunTab[$_.DiskName] = $ds.Name

      }

    }

    $report = $stats | Group-Object -Property {$_.Entity.Name},Instance | %{

      $readStat = $_.Group |

        where{$_.MetricId -eq "disk.numberread.summation"} |

        Measure-Object -Property Value -Average -Maximum

      $writeStat = $_.Group |

        where{$_.MetricId -eq "disk.numberwrite.summation"} |

        Measure-Object -Property Value -Average -Maximum

      New-Object PSObject -Property @{

        VM = $_.Values[0]

        Start = $start

        Finish = $finish

        Disk = $_.Values[1]

        IOPSMax = [math]::Round($writeStat.Maximum+$readStat.Maximum//$interval,0)

        IOPSAvg = [math]::Round($writeStat.Average+$readStat.Average/$interval,0)

        Datastore = $lunTab[$_.Values[1]]

      }

    }

    Send-MailMessage -Subject "IOPS Report" -From asdf@asdf.com `

      -To me@me.com -SmtpServer mail.mailserver.com `

      -BodyAsHtml -Body ($report | Select VM,Start,Finish,Datastore,IOPSAvg,

        IOPSMax | Sort-Object IOPSAvg -descending | ConvertTo-Html | Out-String)



  • 8.  RE: Script to Generate IOPS Report

    Posted May 28, 2013 05:21 AM

    I think that this

        IOPSMax = [math]::Round($writeStat.Maximum+$readStat.Maximum//$interval,0)

        IOPSAvg = [math]::Round($writeStat.Average+$readStat.Average/$interval,0)

    should be like this

        IOPSMax = [math]::Round(($writeStat.Maximum+$readStat.Maximum)/$interval,0)

        IOPSAvg = [math]::Round(($writeStat.Average+$readStat.Average)/$interval,0)



  • 9.  RE: Script to Generate IOPS Report

    Posted May 28, 2013 06:11 PM

    Amazing, Thanks for everything!



  • 10.  RE: Script to Generate IOPS Report

    Posted Oct 13, 2013 11:53 PM

    Thanks Luc, you are awesome !



  • 11.  RE: Script to Generate IOPS Report

    Posted May 12, 2020 05:24 AM

    Hi Lucd , 

    This is not working failing with same error . 



  • 12.  RE: Script to Generate IOPS Report

    Posted May 12, 2020 05:42 AM

    That metric requires Statistics Level 3.

    PS: cross-posting your question doesn't really help.



  • 13.  RE: Script to Generate IOPS Report

    Posted Nov 01, 2023 05:57 PM

    Hi there  ,

    Can I just say thanks for the script and for the comments on this KB formu 

    I want to look at changing the date format

    from

    $finish = Get-Date -Hour 0 -Minute 0 -Second 0
    $start = $finish.AddDays(-1)

    To  

    $metrics = "disk.numberwrite.summation","disk.numberread.summation"
    $startdate = Get-Date "31-02-2023"
    $finishdate = Get-Date "31-03-2023"
    $report = @()

    Is this possible, I'm having issues with either the date format or do I need to add hhmmss as well.

    Thanks



  • 14.  RE: Script to Generate IOPS Report

    Posted Nov 01, 2023 06:54 PM

    The formats that Get-Date accepts are determined by the cultural settings on your station.
    You can check which one that is with

     [cultureinfo]::CurrentCulture

    In en-US several formats are accepted, see the Date parameter description on the Get-Date cmdlet.
    The following will be accepted.
    Note that since Feb 31 does not exist you will also get an error.

    Get-Date '28/02/2023'

    To avoid this you can also use the specific parameters

    Get-Date -Year 2023 -Month 2 -Day 28 -Hour 0 -Minute 0 -Second 0

     



  • 15.  RE: Script to Generate IOPS Report

    Posted Nov 06, 2023 11:28 PM

    Hi    Many Thanks for your last comments, I started to use $start = (Get-Date).AddDays(-5) for example.

    However, I was wondering if you could assist me with the following Script which partly returns the results of VMs and attached disks to those datastores, but I'm getting the following error 

    Is this related the the -Realtime command or [3].IntervalSecs, or I'm i missing quotes from New-Object PSObject -Property @{, from within the script below.

    --------------------

    Error Message

    New-Object PSObject -Property @{
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : NullArray

    ---------------------------------------

    Script I'm using is ----

    $metrics = "virtualdisk.numberwriteaveraged.average","virtualdisk.numberreadaveraged.average"
    $start = (Get-Date).AddDays(-5)

    # Initialize the report variable
    $report = @()

    $vms = Get-VM | where {$_.PowerState -eq "PoweredOn"} 
    $stats = Get-Stat -Realtime -Stat $metrics -Entity $vms -Start $start
    $interval = $stats[3].IntervalSecs


    $hdTab = @{}
    foreach($hd in (Get-Harddisk -VM $vms)){
    $controllerKey = $hd.Extensiondata.ControllerKey
    $controller = $hd.Parent.Extensiondata.Config.Hardware.Device | where{$_.Key -eq $controllerKey}
    $hdTab[$hd.Parent.Name + "/scsi" + $controller.BusNumber + ":" + $hd.Extensiondata.UnitNumber] = $hd.FileName.Split(']')[0].TrimStart('['),$hd.CapacityGB


    }

    $report = $stats | Group-Object -Property {$_.Entity.Name},Instance | %{
    New-Object PSObject -Property @{
    VM_Host = $_.Values[0]
    Disk = $_.Values[1]
    DiskSizeGB = $hdTab[$_.Values[0] + "/"+ $_.Values[1]][1]
    IOPSMaxWrite = ($_.Group |
    where {$_.MetricId -eq "virtualdisk.numberwriteaveraged.average"} |
    Measure-Object -Property Value -Maximum).Maximum / $interval
    IOPSMaxRead = ($_.Group |
    where {$_.MetricId -eq "virtualdisk.numberreadaveraged.average"} |
    Measure-Object -Property Value -Maximum).Maximum / $interval
    IOPSMax = ($_.Group | `
    Group-Object -Property Timestamp | `
    %{$_.Group[0].Value + $_.Group[1].Value} | `
    Measure-Object -Maximum).Maximum / $interval
    IOPSAvg = ($_.Group |
    Group-Object -Property Timestamp |
    %{$_.Group[0].Value + $_.Group[1].Value} | `
    Measure-Object -Average).Average / $interval
    Datastore = $hdTab[$_.Values[0] + "/"+ $_.Values[1]]
    Timestamp = Get-Date

    }
    }

    $report | select -Property IOPSMAX, VM_Host, Disk, DiskSizeGB, IOPSMAXWrite, IOPSMaxRead, IOPSAvg, Datastore, Timestamp | Format-Table



  • 16.  RE: Script to Generate IOPS Report

    Posted Nov 07, 2023 07:36 AM

    Is that all the error message shows?



  • 17.  RE: Script to Generate IOPS Report

    Posted Nov 07, 2023 08:53 AM

    Hi  

    This is the full error message.

    Cannot index into a null array.
    At C:\Script\Max_IOPS.ps1:25 char:5
    + New-Object PSObject -Property @{
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : NullArray

    Cannot index into a null array.
    At C:\Script\Max_IOPS.ps1:25 char:5
    + New-Object PSObject -Property @{
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : NullArray

    Cannot index into a null array.
    At C:\Script\Max_IOPS.ps1.ps1:25 char:5
    + New-Object PSObject -Property @{
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : NullArray

    Cannot index into a null array.
    At C:\Script\Max_IOPS.ps1.ps1:25 char:5
    + New-Object PSObject -Property @{
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : NullArray



  • 18.  RE: Script to Generate IOPS Report

    Posted Nov 07, 2023 09:01 AM

    Where is that $hdTab part coming from?
    I don't see that in this thread.

    Looks like this doesn't take into account when a VM has no harddisks



  • 19.  RE: Script to Generate IOPS Report

    Posted Nov 07, 2023 09:14 AM

    $hdTab = @{}
    foreach($hd in (Get-Harddisk -VM $vms)){
    $controllerKey = $hd.Extensiondata.ControllerKey
    $controller = $hd.Parent.Extensiondata.Config.Hardware.Device | where{$_.Key -eq $controllerKey}
    $hdTab[$hd.Parent.Name + "/scsi" + $controller.BusNumber + ":" + $hd.Extensiondata.UnitNumber] = $hd.FileName.Split(']')[0].TrimStart('['),$hd.CapacityGB


    }

    $report = $stats | Group-Object -Property {$_.Entity.Name},Instance | %{
    New-Object PSObject -Property @{
    VM_Host = $_.Values[0]
    Disk = $_.Values[1]
    DiskSizeGB = $hdTab[$_.Values[0] + "/"+ $_.Values[1]][1]
    IOPSMaxWrite = ($_.Group |
    where {$_.MetricId -eq "virtualdisk.numberwriteaveraged.average"} |
    Measure-Object -Property Value -Maximum).Maximum / $interval
    IOPSMaxRead = ($_.Group |
    where {$_.MetricId -eq "virtualdisk.numberreadaveraged.average"} |
    Measure-Object -Property Value -Maximum).Maximum / $interval
    IOPSMax = ($_.Group | `
    Group-Object -Property Timestamp | `
    %{$_.Group[0].Value + $_.Group[1].Value} | `
    Measure-Object -Maximum).Maximum / $interval
    IOPSAvg = ($_.Group |
    Group-Object -Property Timestamp |
    %{$_.Group[0].Value + $_.Group[1].Value} | `
    Measure-Object -Average).Average / $interval
    Datastore = $hdTab[$_.Values[0] + "/"+ $_.Values[1]][0]
    #Timestamp = $date = (Get-Date).ToString("yyyyMMdd_HHmmss")

    }
    }



  • 20.  RE: Script to Generate IOPS Report

    Posted Nov 07, 2023 09:29 AM

    Is that something you added/changed?
    This thread wasn't about HD IOPS



  • 21.  RE: Script to Generate IOPS Report

    Posted Apr 11, 2016 09:44 PM

    Hi Everybody,

    Great script LucD...

    I had some curious results returned on a few VMs.

    Basically it looks like the get-stat returned an instance for a datastore that is not actually part of the VM. 

    That is the disk ID, naa.60060e801606400000010640023455601 for example, is returned by Get-Stats. This corresponds to datastore PROD-MGT-L001-NL7K, for example,  which I can verify by looking at that datastore's properties, but if I do a get-datastore for the given VM, this datastore is not listed. The VM has no virtual disk on this datastore, there is no iso mapped to the CD-ROM drive from this datastore either. I thought perhaps it was historical data but even if I use the real time switch and get the stats from an hour ago, this disk still shows up as associated with the VMs. Quite puzzling.  Anybody have any ideas on this?

    Anyway I also fiddled with adding the VM's home clustername and primary portgroup name to the report using the following

    $ClusterTab = @{}

    $PortGroupTab = @{}

    foreach($vm in $vms ){

          $ClusterTab[$VM.name] = ($vm.vmhost.parent).name

       $PGName = ($vm | Get-NetworkAdapter).networkname

       If ($PGname.GetType().Name -eq "Object[]")  {

      $PortGroupTab[$VM.Name] = $PGName[0]

       }

       else  {

      $PortGroupTab[$VM.Name] = $PGName

       }

    }

    Need to add the following to the New-Object properties hash list, and add the names to the output select property name list:

    Cluster = $ClusterTab[$_.Values[0]]

    PortGroup = $PortGroupTab[$_.Values[0]]

    Also here is a way to add allocated diskspace aggregated per datastore for each VM.

    But this can make the script take a while.

    $DiskAllocTab = @{}

    Foreach ($vm in $vms)

    {

         $DiskAllocTab[$vm.name] = @{}

         foreach($ds in (Get-Datastore -VM $vm | where {$_.Type -eq "VMFS"}))

         {

              $DiskAllocTab[$vm.name][$ds.name] = @{}

              $Alloc = (Get-HardDisk -VM $vm | where {$_.filename -like "*"+$ds.name+"*"} | Measure-Object -Sum CapacityGB).Sum

              if ($Alloc)  {

                     $DiskAllocTab[$vm.name][$ds.name] = $Alloc

              }

              else {

                     $DiskAllocTab[$vm.name][$ds.name] = 0

              }

         }

    }

    Add the following 3 lines to just before the New-object statement:

    $DiskAllocGB = $null

    Try {$DiskAllocGB = $diskAllocTab[$_.Values[0]][$lunTab[$_.Values[1]]]} Catch {$DiskAllocGB = 0}

    if ($DiskAllocGB -eq $null) {$DiskAllocGB = 0}

    Add the following line to the New-object property hash list:

    DiskAllocGB = $DiskAllocGB

    Then add the property name select statement for output.

    DiskAllocGB

    Cheers All!

    If anybody has any idea why I get that weird anomaly of phantom datastores, drop a line here... I'm really stumped by it.

    Garrett



  • 22.  RE: Script to Generate IOPS Report

    Posted Apr 12, 2016 05:59 AM

    Does this VM has snapshots ?

    If yes, was that datastore perhaps used at the time one of the snapshots was taken ?



  • 23.  RE: Script to Generate IOPS Report

    Posted Apr 12, 2016 08:55 PM

    That was a good thought... but no... no snapshot.

    Check out the attachment. the first command shows the diskname of the datastore in question.

    The second shows the datastores attached to the VM in question.

    The third shows filtered data gathered by the get-stat command.

    Where did it get that instance from?! Pretty weird.



  • 24.  RE: Script to Generate IOPS Report

    Posted Apr 12, 2016 09:04 PM

    Do you by any chance have/had an ISO image mounted on the CD/DVD drive ?

    The name seems to indicate it concerns an ISO repository



  • 25.  RE: Script to Generate IOPS Report

    Posted Apr 12, 2016 09:34 PM

    That is mainly what that datastore is used for... mapping ISO files to a VM's CD-ROM. At the current time the VMs in question have no ISO mapped to the CD-ROM. It is likely that they did have an ISO mapped from this datastore in the past. This would be true for many of the VMs we have. Although as far as I can see the disk metrics for Get-stat do not include the CD-ROM drive, I know this because there are VMs in the report that have the CR-ROM mapped to this datastore and the datastore is not listed for that VM in the report.  It is almost like there was a vdisk for the VM on that datastore at one time and a lingering record of it still remains. So odd...  like something is not updated correctly.



  • 26.  RE: Script to Generate IOPS Report

    Posted Apr 12, 2016 09:50 PM

    I'm going to migrate one of the VMs to a different datastore and see if that clears it up.



  • 27.  RE: Script to Generate IOPS Report

    Posted Apr 13, 2016 06:32 PM

    Migrating one of the VMs to a different datastore fixed it for that VM.  I went to one of the other VMs with the issue and downloaded the VMX file and took a look at it. It has a reference to a virtual disk that is on the datastore in question! Funny thing is that when you edit the VM this disk does not show up. When you do a "get-datastore  for that VM the datastore in question does not show up. But obviously, get-stat is processing this reference in the VMX.  Anyway, LucD, thanks for the suggestions...  always good to have another perspective. At least I know why Get-stat is reporting on that datastore disk instance. I need to go have talk with our VMware admins...