This post is for a user over at the VMware Community vRops forums, basically the request was for a report which would give he or she the top 10 VM’s by IOPS over 24h for each datastore.

I developed and tested this on vRops 6.3

By using the suite-api this script dumps every datastore and then executes a report against each datastore, trims the values to the TOP 10 VM’s and then merges all the results into a single file.

While the report doesn’t provide 100% what the user wants, it is as close as I can get without lots of work with the suite-api and reporting… if a VM has more than one datastore attached it will appear multiple times on the report as i am using an aggregate value for IOPS. If a VM does have more than one VMDK.. the user will need to determine which SCSI / VMDK is generating the load on that specific datastore by look at the metrics per disk on the specific VM’s.

The first thing that you will need to do is to import my VM IOPS by Datastore view and report xml into vRops (remove the .txt)

vropsimportviewandreport

Once this is done you will need to prepare the script… first you will need to create a new top level folder with 3 folders within called collection, config and reports

So that you don’t keep being prompted for credentials… save the service or user account credentials to a file with the powershell Get-Credential command.

$cred = Get-Credential
$cred | Export-Clixml -Path "d:\vRops\Config\HOME.xml"

Then create a new file called Top10VMIOPSbyDatastore.ps1 and copy the contents below into it… make sure to update $vRopsAddress,$creds and if required the $ReportID to match your environment.

param
(
    [String]$vRopsAddress = 'vROps.vMan.ch',
    [String]$objtype = 'Datastore',
    [String]$creds = 'HOME',
    [String]$ReportID = '5c7bb333-2e74-440d-8f60-28cc26d61760'
)

#vars
$ScriptPath = (Get-Item -Path ".\" -Verbose).FullName
[DateTime]$RunDate = (Get-date)
$RunDateFile = $RunDate.tostring("yyyyMMdd-HHmmss")
$ReportOutput = $ScriptPath + '\Reports\DatastoreTop10VMsByIOPS' + $RunDateFile + '.csv'

if($creds -gt ""){

    $cred = Import-Clixml -Path "$ScriptPath\config\$creds.xml"

    $vRopsUser = $cred.GetNetworkCredential().Username
    $vRopsPassword = $cred.GetNetworkCredential().Password
    }
    else
    {
    echo "No Credentials Supplied"
    Exit
    }

#Take all certs.
add-type @"
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    public class TrustAllCertsPolicy : ICertificatePolicy {
        public bool CheckValidationResult(
            ServicePoint srvPoint, X509Certificate certificate,
            WebRequest request, int certificateProblem) {
            return true;
        }
    }
"@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy

function Merge-CSVFiles {            
[cmdletbinding()]            
param(            
    [string[]]$CSVFiles,            
    [string]$OutputFile          
)            
$Output = @();            
foreach($CSV in $CSVFiles) {            
    if(Test-Path $CSV) {            
                    
        $FileName = [System.IO.Path]::GetFileName($CSV)            
        $temp = Import-CSV -Path $CSV | select *      
        $Output += $temp            
            
    } else {            
        Write-Warning "$CSV : No such file found"            
    }            
            
}            
$Output | Export-Csv -Path $OutputFile -NoTypeInformation            
Write-Output "$OutputFile successfully created"            
            
}  

Function GetAllObjects([String]$vRopsObjtype, [String]$vRopsServer, $vRopsCreds){

    $ObjectList = @()

    $ContentType = "application/xml;charset=utf-8"
    $header = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
    $header.Add("Accept", 'application/xml')

    #Call vRops API

    $url = "https://$vRopsServer/suite-api/api/adapterkinds/VMWARE/resourcekinds/$vRopsObjtype/resources"

    [xml]$Objects = Invoke-RestMethod -Method GET -uri $url -Credential $vRopsCreds -ContentType $ContentType -Headers $header
    
    ForEach ($Object in $Objects.resources.resource){

        $ObjectList += New-Object PSObject -Property @{

                ResourceID = $Object.identifier
                Name = $Object.resourceKey.name

        }

    }

    Return $ObjectList
}

Function GetReport([String]$vRopsAddress, [String]$resourceID, [String]$resourceName, [String]$Report, $vRopsCreds, $Path){

#RUN Report

$ContentType = "application/xml;charset=utf-8"
$header = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$header.Add("Accept", 'application/xml')

$RunReporturl = 'https://'+$vRopsAddress+'/suite-api/api/reports'

$Body = @"
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ops:report xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ops="http://webservice.vmware.com/vRealizeOpsMgr/1.0/">
    <ops:resourceId>$resourceID</ops:resourceId>
    <ops:reportDefinitionId>$Report</ops:reportDefinitionId>
    <ops:traversalSpec adapterInstanceAssociation="false">
        <ops:name>vSphere Storage</ops:name>
        <ops:rootAdapterKindKey>VMWARE</ops:rootAdapterKindKey>
        <ops:rootResourceKindKey>vSphere World</ops:rootResourceKindKey>
    </ops:traversalSpec>
</ops:report>
"@


[xml]$Data = Invoke-RestMethod -Method POST -uri $RunReporturl -Credential $vRopsCreds -ContentType $ContentType -Headers $header -Body $body

$ReportLink = $Data.report.links.link.href

$ReportLinkurl = 'https://' + $vRopsAddress + $ReportLink

#Check if report is run to download

[xml]$ReportStatus = Invoke-RestMethod -Method GET -uri $ReportLinkurl -Credential $vRopsCreds -ContentType $ContentType -Headers $header


While ($ReportStatus.report.status -ne "COMPLETED") {
    [xml]$ReportStatus = Invoke-RestMethod -Method GET -uri $ReportLinkurl -Credential $vRopsCreds -ContentType $ContentType -Headers $header
    Write-host 'Waiting for' $Env 'report to finish running, current status: '  $ReportStatus.report.status
    Sleep 3
      } # End of block statement


$ReportDownload = $ReportLinkurl + '/download?format=CSV'

$ReportOutputfile = $Path + '\collections\' + $resourceName + '.csv'

Invoke-RestMethod -Method GET -uri $ReportDownload -Credential $vRopsCreds -ContentType $ContentType -Headers $header -OutFile $ReportOutputfile

#Select the first 11 lines of the Report

$Top10 = Get-Content $ReportOutputfile | select -First 11

#Delete the previous file

remove-item $ReportOutputfile -Force 

#add a column to identify the datastore that the Reportmwas generated against.

$Top10 | Out-File $ReportOutputfile

$AddReportDatastore = Import-CSV $ReportOutputfile

$AddReportDatastore | ForEach-Object {  $_ | Add-Member -MemberType NoteProperty -Name DatastoreReport -Value $resourceName -PassThru } | Export-CSV $ReportOutputfile -NoTypeInformation

Write-host "Report for '$resourceName' complete"

return $ReportOutputfile

}


#Start of scriptScript

Write-host 'Starting Script'

$AllDatastores = @()

$AllDatastores = GetAllObjects $objtype $vRopsAddress $cred

$Report = @()

    ForEach ($Datastore in $AllDatastores){

    $Report = GetReport $vRopsAddress $Datastore.ResourceID $Datastore.Name $ReportID $cred $ScriptPath

}

$Output = "$ScriptPath\collections\"

#get all CSV files from this job

$Filearr = Get-ChildItem $Output | 
       Where-Object {$_.Name -like '*.csv'} |
       Foreach-Object { $Output + $_.Name}


#Merge all CSV's into 1 Report

Merge-CSVFiles -CSVFiles $Filearr -OutputFile $ReportOutput

#Delete CSV's used to merge into the Report

Remove-item $Filearr -Force

Remove-Variable * -ErrorAction SilentlyContinue

Once the script is finished you can pick up the file from …\Reports\DatastoreTop10VMsByIOPS-Date.csv , just be aware that a report has been generated for each datastore in the Generated Reports in vRops and you might need to clean up unwanted files.

vropsgeneratedreports