## Copyright 2014 by Jonas Nagel, LANexpert ## Extracts vSphere VM I/O values including Datastore ID, which allows for correlation of performance data ## to HDS storage LUNs within Hitachi IOPortal. ## ## Prerequisites: The script assumes that the default statistics interval duration for 1 day is 5 min (300 sec). ## It retrieves statistics from vCenter, which are only available if the 5 minute interval stats ## are set to Level 3 (which is the default) or higher. ## Install PowerShell version 2 or above, then install vSphere PowerCLI 5.8 or above, make sure ## you run the command 'Set-ExecutionPolicy RemoteSigned' before executing the script. ## ## Output note: Both CSV files contain the UID of the VMs; this can become the primary key to join both tables ## within IOPortal. Alternatively EntityID can be used, but it might become duplicate across ## multiple vCenter instances. # # Execution: extractVMIOstats.ps1 -interval # Parameter 'interval' defines how many hours back of stats are written to the VMIOReport.csv file. # Allowed values 1 through 24. # A 1 hour interval allows for 20 seconds samples while anything above will fall back to 5 min samples. # # Version 1.0 param($interval) # Define parameters below: # VMware vCenter Server FQDN/IP #$vcenter = "vcserver.lab.zz" $vcenter = "vwbulgaria.democenter.zh" # Define $true for predefined credentials, define $false to log on with current user $fixedCreds = $true # Define vCenter admin username $user = "Democenter\vmioportal" # Password will be prompted the first time and written as hash to cred.txt in the script directory. # Delete the cred.txt file and re-run the script once manually to change the password. # CSV file output directory (leave "." for script directory) $outdir = "G:\development\HiCHperfpkg\data\vmiooutput" ##### Do not modify below this line ##### # if ($interval -lt 1 -or $interval -gt 24) { Write-Error "Error: -interval must be a value between 1 and 24!" Exit } if ($fixedCreds) { # Password will be prompted the first time and written as hash to cred.txt; delete this file to change the password $pass = get-content cred.txt -ErrorAction SilentlyContinue if ($pass -eq $null) { read-host -prompt "Please enter password for vCenter account" -assecurestring | convertfrom-securestring | out-file cred.txt $pass = get-content cred.txt | ConvertTo-SecureString } else { $pass = $pass | ConvertTo-SecureString } # if $creds is not defined, script will use the logged on user to authenticate $creds = new-object -typename System.Management.Automation.PSCredential -argumentlist $user,$pass } # ZIP function function out-zip { param([string]$path,[string]$file) if (-not $path.EndsWith('.zip')) {$path += '.zip'} if (-not (test-path $path)) { set-content $path ("PK" + [char]5 + [char]6 + ("$([char]0)" * 18)) } $ZipFile = (new-object -com shell.application).NameSpace($path) get-item $file | foreach {$ZipFile.CopyHere($_.fullname)} } # Load vSphere PowerCLI SnapIn if ((Get-PSSnapin -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue) -eq $null) { Add-PSsnapin VMware.VimAutomation.Core } # connect vCenter server session if ($creds -ne $null) { Connect-VIServer $vcenter -credential $creds -Force -WarningAction SilentlyContinue | Out-Null } else { Connect-VIServer $vcenter -Force -WarningAction SilentlyContinue | Out-Null } # get all Hitachi datastores $datastores = Get-Datastore $dsList = @() foreach ($ds in $datastores) { # identify device id of datastore $ErrorActionPreference="SilentlyContinue" $dsId = ($ds | Get-View).info.vmfs.extent[0].diskname $ErrorActionPreference="Continue" # old HDS WWN vendor ID used to be 50060e8, current vendor ID is 60060e8 if ($dsID -Match "naa.50060e8" -or $dsID -Match "naa.60060e8") { $dsList += $ds } } # Reference values for sample amount # number of samples = amount of time (with intervalSecs = 20) # 1 = 20 sec # 180 = 1 hour # number of samples = amount of time (with intervalSecs = 300) # 1 = 5 min # 12 = 1 hour # real-time stats not supported, lacks desired metrics for unknown reasons # if ($interval -eq 1) { # $intervalSecs = 20 # $samples = 180 # } else { $intervalSecs = 300 $samples = $interval * 12 # } # dataArray returns performance stats per VM for final export $dataArray = @() # vm2dsArray returns a table to match VMs to Datastore Names and Datastore IDs $vm2dsArray = @() foreach ($ds in $dsList) { $vms = Get-VM -Datastore $ds | Where {$_.PowerState -eq "PoweredOn"} $dsId = ($ds | Get-View).info.vmfs.extent[0].diskname foreach ($vm in $vms) { $vm2ds = New-Object System.Object $vm2ds | Add-Member -MemberType NoteProperty -Name "UID" -Value $vm.Uid $vm2ds | Add-Member -MemberType NoteProperty -Name "EntityID" -Value $vm.Id $vm2ds | Add-Member -MemberType NoteProperty -Name "VMName" -Value $vm.Name $vm2ds | Add-Member -MemberType NoteProperty -Name "DatastoreID" -Value $dsId $vm2ds | Add-Member -MemberType NoteProperty -Name "Datastore" -Value $ds.Name $vm2dsArray += $vm2ds $dataArray += get-stat -entity $vm -stat disk.read.average -IntervalSecs $intervalSecs -maxSamples $samples $dataArray += get-stat -entity $vm -stat disk.write.average -IntervalSecs $intervalSecs -maxSamples $samples $dataArray += get-stat -entity $vm -stat disk.provisioned.latest -IntervalSecs $intervalSecs -maxSamples $samples $dataArray += get-stat -entity $vm -stat disk.used.latest -IntervalSecs $intervalSecs -maxSamples $samples $dataArray += get-stat -entity $vm -stat disk.maxTotalLatency.latest -IntervalSecs $intervalSecs -maxSamples $samples $dataArray += get-stat -entity $vm -stat disk.numberReadAveraged.average -IntervalSecs $intervalSecs -maxSamples $samples $dataArray += get-stat -entity $vm -stat disk.numberWriteAveraged.average -IntervalSecs $intervalSecs -maxSamples $samples } } # Generate time stamp and file name $CurrentDate = Get-Date $CurrentDate = $CurrentDate.ToString('MM-dd-yyyy_hh-mm-ss') # If no path specified assume script path if ($outdir -eq "." -or $outdir -eq "") { $outdir = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition } $file1 = $outdir + "\VM2DSTable_" + $CurrentDate + ".csv" $file2 = $outdir + "\VMIOReport_" + $CurrentDate + ".csv" $vm2dsArray | Export-CSV $file1 -NoType $dataArray | Export-CSV $file2 -NoType # Create ZIP files and remove CSV $files = @($file1, $file2) ForEach ($file In $files) { $zfile = $file.Substring(0,$file.Length-3) + "zip" out-zip -path $zfile -file $file # no other way to avoid race condition of remove-item with asynchronous ZIP function sleep 10 remove-item $file }