Hello there.
I need to set the IOPS limit on VMDKs with particular Storage Policy assigned, and the amount of IOPS should be set depending on the size of the VMDK. For example, 2 IOPS per gigabyte for Tier1, 1 IOPS per gigabyte for Tier2 and 0.5 IOPS per GB for Tier3. There will be 3 Policies total (maybe 4 later), and disks with Policy, which is different from the described three tiers (or with Default policy), should not get a limit.
I have written such script:
#Set the IOPS per Gigabyte for particular Storage Policy ("Tier1", "Tier2" and "Tier3" in my case and 2, 1 and 0.5 IOPS per GB accordingly)
$Tier1IOPSperGB = 2
$Tier2IOPSperGB = 1
$Tier3IOPSperGB = 0.5
#To store credentials in the XML file you should get credentials in a variable:
#$credentials = Get-Credential
#An then export it into file (which can be used only on the same machine):
#$credentials | Export-Clixml -Path "C:\Users\anatoliy\credentials.cred"
#Import credentials from XML file
$credentials = Import-Clixml -Path "C:\Users\anatoliy\credentials.cred"
#Connect the vCenter
Connect-VIServer vcsa1.nokogerra.lab -Credential $credentials
#Names of the VMs to be excluded from setting the IOPS limit are stored in the CSV. You can use a full name of the VM or a wildcard ("excludevms" if a column name):
#excludevms
#exclude1-test
#*clude2*
$csv = Import-Csv "C:\Users\anatoliy\Desktop\ExcludeVMs.csv"
#Fill the string array with names of the VMs to be excluded
foreach ($statement in $csv) {[string[]]$exclude += Get-VM -Name $statement.excludevms}
#Now get all VMs of the Virtual infrastructure except the VMs, which names were specified in the CSV file
$vms = Get-VM | ?{$exclude -notcontains $_.Name}
foreach ($vm in $vms){
#For each VM get all VMDKs
$VMDisk = $vm | Get-HardDisk
#For each VMDK get it's Storage Policy
foreach ($disk in $VMDisk){
$Policy = ($disk | get-SpbmEntityConfiguration).StoragePolicy.Name
#If "Tier1" Policy is assigned for the disk AND the correct limit is not already set
if (($Policy -eq "Tier1") -and
(($disk.Parent.ExtensionData.Config.Hardware.Device | where {($_ -is [VMware.Vim.VirtualDisk]) -and ($_.deviceinfo.label -eq $disk.name)}).StorageIOAllocation.Limit -ne ($disk.CapacityGB * $Tier1IOPSperGB))) {
#then set the limit, which depends of the disk size and Storage Policy
$disk.Parent | Get-VMResourceConfiguration | Set-VMResourceConfiguration -Disk $disk -DiskLimitIOPerSecond ($disk.CapacityGB * $Tier1IOPSperGB)
}
#Same for "Tier2"
elseif (($Policy -eq "Tier2") -and
(($disk.Parent.ExtensionData.Config.Hardware.Device | where {($_ -is [VMware.Vim.VirtualDisk]) -and ($_.deviceinfo.label -eq $disk.name)}).StorageIOAllocation.Limit -ne ($disk.CapacityGB * $Tier2IOPSperGB))) {
$disk.Parent | Get-VMResourceConfiguration | Set-VMResourceConfiguration -Disk $disk -DiskLimitIOPerSecond ($disk.CapacityGB * $Tier2IOPSperGB)}
#Same for "Tier3"
elseif (($Policy -eq "Tier3") -and
(($disk.Parent.ExtensionData.Config.Hardware.Device | where {($_ -is [VMware.Vim.VirtualDisk]) -and ($_.deviceinfo.label -eq $disk.name)}).StorageIOAllocation.Limit -ne ($disk.CapacityGB * $Tier3IOPSperGB))) {
$disk.Parent | Get-VMResourceConfiguration | Set-VMResourceConfiguration -Disk $disk -DiskLimitIOPerSecond ($disk.CapacityGB * $Tier3IOPSperGB)}
#Do nothing for other policies
else {Write-Host "Doing nothing"}
}
}
Remove-Variable exclude -Force -ErrorAction SilentlyContinue
Remove-Variable vms -Force -ErrorAction SilentlyContinue
Disconnect-VIServer vcsa1.nokogerra.lab -Confirm:$false
It seems that the script works properly, however I still need help to make some things:
1. I need to "trim" the size of a VMDK, which is returned by "$disk.CapacityGB". For example, if the disk size somehow became 100.825367 GB, then it should be counted as 101 GB. Actually, if it will be counted as 100GB it will be OK too, the difference in 2 IO per second is not significant.
2. I think, I need some report, which will cover the script execution result. To be honest, I'm still not sure how it should look like, may be an email with errors? I'm planning to run this script periodically (every day, I guess), so I need some info if the execution was sucessful.
3. Any idea how to optimize this script in any other way? I'm a pretty bad writer, so it would be nice to get some advice
No problem.
I meant that since you are already handling objects produced by Get-HardDisk, you don't have to test if the object is a VirtualDisk.
Your If test could be
#For each VM get all VMDKs
$VMDisk = $vm | Get-HardDisk
#For each VMDK get it's Storage Policy
foreach ($disk in $VMDisk){
$Policy = ($disk | get-SpbmEntityConfiguration).StoragePolicy.Name
#If "Tier1" Policy is assigned for the disk AND the correct limit is not already set
if ($Policy -eq "Tier1") {
if ((([math]::Round($disk.CapacityGB) * $Tier1IOPSperGB) -le $Tier1MinimumIOPS) -and
(($disk.Parent.ExtensionData.Config.Hardware.Device | where {($_.deviceinfo.label -eq $disk.name)}).StorageIOAllocation.Limit -ne $Tier1MinimumIOPS)){
$disk.Parent | Get-VMResourceConfiguration | Set-VMResourceConfiguration -Disk $disk -DiskLimitIOPerSecond $Tier1MinimumIOPS}
elseif ((([math]::Round($disk.CapacityGB) * $Tier1IOPSperGB) -gt $Tier1MinimumIOPS) -and
(($disk.Parent.ExtensionData.Config.Hardware.Device | where {($_.deviceinfo.label -eq $disk.name)}).StorageIOAllocation.Limit -ne ([math]::Round($disk.CapacityGB) * $Tier1IOPSperGB))){
$disk.Parent | Get-VMResourceConfiguration | Set-VMResourceConfiguration -Disk $disk -DiskLimitIOPerSecond ([math]::Round($disk.CapacityGB) * $Tier1IOPSperGB)}
else {}
}
#Same for "Tier2"
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
1. Use the Round method
[math]::Round($disk.CapacityGB)
2.You could use a try-catch construct.
If there is a terminating error, the code in catch block will be executed.
You can force a terminating error by adding an ErrorAction on most cmdlets.
Get-VM -ErrorAction Stop
}
catch{
Write-Error "Something went wrong"
}
3. No real "big" timesavers at first sight.
You could convert the script to work with vSphere objects (Get-View and ExtensionData), but it would make the code more complex.
A smaller one, since you are already sure you are looking at HardDisk object, there is really no need to test $_ -is [VMware.Vim.VirtualDisk]
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Hello.
My apologies for the delay, I couldn't try your tips before.
1. [math]::Round works perfectly, thank you.
2. I've read few articles about try/catch, but I'm still not sure how to use it in my case. Where should I put "try", and when to put "catch". Also, it seems, that I should not use "stop" error action, if I want the script to continue after a non-terminating exception. I guess, it is just too hard for me.
3. > A smaller one, since you are already sure you are looking at HardDisk object, there is really no need to test $_ -is [VMware.Vim.VirtualDisk]
Do you mean, that I can change this expression:
$disk.Parent.ExtensionData.Config.Hardware.Device | where {($_ -is [VMware.Vim.VirtualDisk]) -and ($_.deviceinfo.label -eq $disk.name)}
to this one:
$disk.Parent.ExtensionData.Config.Hardware.Device | where {$_.deviceinfo.label -eq $disk.name}
So the script will rely only on the object name, not on the object type?
Also I'm looking a way to add the additional condition to the script: if the calculated IOPS limit (IOPSperGB * CapacityGB) is lower (or equal) than the LowWatermark (which will be set in the script's beginning), then use the LowWatermark value for the disk IOPS limit. However, I'm not sure how to do it. Can I just add "if/else" block into the parent "if" block?
update:
Okay, my last question is seems to be solved now. For example:
How it was:
foreach ($vm in $vms){
#For each VM get all VMDKs
$VMDisk = $vm | Get-HardDisk
#For each VMDK get it's Storage Policy
foreach ($disk in $VMDisk){
$Policy = ($disk | get-SpbmEntityConfiguration).StoragePolicy.Name
#If "Tier1" Policy is assigned for the disk AND the correct limit is not already set
if (($Policy -eq "Tier1") -and
(($disk.Parent.ExtensionData.Config.Hardware.Device | where {($_ -is [VMware.Vim.VirtualDisk]) -and ($_.deviceinfo.label -eq $disk.name)}).StorageIOAllocation.Limit -ne ([math]::Round($disk.CapacityGB) * $Tier1IOPSperGB))) {
#then set the limit, which depends of the disk size and Storage Policy
$disk.Parent | Get-VMResourceConfiguration | Set-VMResourceConfiguration -Disk $disk -DiskLimitIOPerSecond ([math]::Round($disk.CapacityGB) * $Tier1IOPSperGB)
}
#Same for "Tier2"
elseif (($Policy -eq "Tier2") -and
...............
And how it became:
foreach ($vm in $vms){
#For each VM get all VMDKs
$VMDisk = $vm | Get-HardDisk
#For each VMDK get it's Storage Policy
foreach ($disk in $VMDisk){
$Policy = ($disk | get-SpbmEntityConfiguration).StoragePolicy.Name
#If "Tier1" Policy is assigned for the disk AND the correct limit is not already set
if ($Policy -eq "Tier1") {
if ((([math]::Round($disk.CapacityGB) * $Tier1IOPSperGB) -le $Tier1MinimumIOPS) -and
(($disk.Parent.ExtensionData.Config.Hardware.Device | where {($_ -is [VMware.Vim.VirtualDisk]) -and ($_.deviceinfo.label -eq $disk.name)}).StorageIOAllocation.Limit -ne $Tier1MinimumIOPS)){
$disk.Parent | Get-VMResourceConfiguration | Set-VMResourceConfiguration -Disk $disk -DiskLimitIOPerSecond $Tier1MinimumIOPS}
elseif ((([math]::Round($disk.CapacityGB) * $Tier1IOPSperGB) -gt $Tier1MinimumIOPS) -and
(($disk.Parent.ExtensionData.Config.Hardware.Device | where {($_ -is [VMware.Vim.VirtualDisk]) -and ($_.deviceinfo.label -eq $disk.name)}).StorageIOAllocation.Limit -ne ([math]::Round($disk.CapacityGB) * $Tier1IOPSperGB))){
$disk.Parent | Get-VMResourceConfiguration | Set-VMResourceConfiguration -Disk $disk -DiskLimitIOPerSecond ([math]::Round($disk.CapacityGB) * $Tier1IOPSperGB)}
else {}
}
#Same for "Tier2"
elseif (($Policy -eq "Tier2") {
................
Feels like it is working fine.
No problem.
I meant that since you are already handling objects produced by Get-HardDisk, you don't have to test if the object is a VirtualDisk.
Your If test could be
#For each VM get all VMDKs
$VMDisk = $vm | Get-HardDisk
#For each VMDK get it's Storage Policy
foreach ($disk in $VMDisk){
$Policy = ($disk | get-SpbmEntityConfiguration).StoragePolicy.Name
#If "Tier1" Policy is assigned for the disk AND the correct limit is not already set
if ($Policy -eq "Tier1") {
if ((([math]::Round($disk.CapacityGB) * $Tier1IOPSperGB) -le $Tier1MinimumIOPS) -and
(($disk.Parent.ExtensionData.Config.Hardware.Device | where {($_.deviceinfo.label -eq $disk.name)}).StorageIOAllocation.Limit -ne $Tier1MinimumIOPS)){
$disk.Parent | Get-VMResourceConfiguration | Set-VMResourceConfiguration -Disk $disk -DiskLimitIOPerSecond $Tier1MinimumIOPS}
elseif ((([math]::Round($disk.CapacityGB) * $Tier1IOPSperGB) -gt $Tier1MinimumIOPS) -and
(($disk.Parent.ExtensionData.Config.Hardware.Device | where {($_.deviceinfo.label -eq $disk.name)}).StorageIOAllocation.Limit -ne ([math]::Round($disk.CapacityGB) * $Tier1IOPSperGB))){
$disk.Parent | Get-VMResourceConfiguration | Set-VMResourceConfiguration -Disk $disk -DiskLimitIOPerSecond ([math]::Round($disk.CapacityGB) * $Tier1IOPSperGB)}
else {}
}
#Same for "Tier2"
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
My Try-Catch remark was primarily to handle some edge cases.
Like for example there are no VMs, or there is a VM without a harddisk.
This could produce errors, which you could avoid by using a try-catch, and provide a meaningful message to the user of the script.
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Hello there!
I'm not sure if I should create another thread for this question, so I'll try to ask it here: I need to collect some results of this script processing.
Actually, I need to colelct VM names, disks (if possible), IOPS limit (if possible) and Storage Policy (if possible) into csv or html file, but for only those VMs, which were modified.
The script block is here [PowerShell] set iops - Pastebin.com .
I guess it should looks like
$vmreport += $vm
$diskreport += $disk
etc, and then push it into a csv or something.
But this will collect all processed VMs and disks, not only those, which were modidfed.
Too hard for me.
Try something like this.
foreach ($vm in $vms) {
#For each VM get all VMDKs
$VMDisk = $vm | Get-HardDisk
#For each VMDK get it's Storage Policy
foreach ($disk in $VMDisk) {
$obj = [ordered]@{
VM = $vm.Name
Disk = $disk.Name
Tier = ''
OldIOPS = ''
NewIOPS = ''
}
$Policy = ($disk | get-SpbmEntityConfiguration).StoragePolicy.Name
if ($Tier1, $Tier2, $Tier3 -contains $Policy) {
$currentIOPS = ($disk.Parent.ExtensionData.Config.Hardware.Device | Where-Object { ($_ -is [VMware.Vim.VirtualDisk]) -and ($_.deviceinfo.label -eq $disk.name) }).StorageIOAllocation.Limit
$obj.OldIOPS = $currentIOPS
#If "Tier1" Policy is assigned for the disk:
if ($Policy -eq $Tier1) {
$obj.Tier = $Tier1
#If the calculated IOPS limit is lower or equal than Minimum IOPS watermark for Tier1 and the IOPS limit is not already set equal to Tier1MinimumIOPS, then set the limit:
if ((([math]::Truncate(($disk.CapacityGB) * $Tier1IOPSperGB)) -le $Tier1MinimumIOPS) -and ($currentIOPS -ne $Tier1MinimumIOPS)) {
$obj.NewIOPS = $Tier1MinimumIOPS
$disk.Parent | Get-VMResourceConfiguration | Set-VMResourceConfiguration -Disk $disk -DiskLimitIOPerSecond $Tier1MinimumIOPS
}
#If the calculated IOPS limit it greater than Minimum IOPS watermark for Tier1 and the IOPS limit is not already set equal to this calculated value, then set the limit:
elseif ((([math]::Truncate(($disk.CapacityGB) * $Tier1IOPSperGB)) -gt $Tier1MinimumIOPS) -and ($currentIOPS -ne ([math]::Truncate(($disk.CapacityGB) * $Tier1IOPSperGB)))) {
$obj.NewIOPS = [math]::Truncate(($disk.CapacityGB) * $Tier1IOPSperGB)
$disk.Parent | Get-VMResourceConfiguration | Set-VMResourceConfiguration -Disk $disk -DiskLimitIOPerSecond ([math]::Truncate(($disk.CapacityGB) * $Tier1IOPSperGB))
}
}
#Same for "Tier2"
elseif ($Policy -eq $Tier2) {
$obj.Tier = $Tier2
if ((([math]::Truncate(($disk.CapacityGB) * $Tier2IOPSperGB)) -le $Tier2MinimumIOPS) -and ($currentIOPS -ne $Tier2MinimumIOPS)) {
$obj.NewIOPS = $Tier2MinimumIOPS
$disk.Parent | Get-VMResourceConfiguration | Set-VMResourceConfiguration -Disk $disk -DiskLimitIOPerSecond $Tier2MinimumIOPS
} elseif ((([math]::Truncate(($disk.CapacityGB) * $Tier2IOPSperGB)) -gt $Tier2MinimumIOPS) -and ($currentIOPS -ne ([math]::Truncate(($disk.CapacityGB) * $Tier2IOPSperGB)))) {
$obj.NewIOPS = [math]::Truncate(($disk.CapacityGB) * $Tier2IOPSperGB)
$disk.Parent | Get-VMResourceConfiguration | Set-VMResourceConfiguration -Disk $disk -DiskLimitIOPerSecond ([math]::Truncate(($disk.CapacityGB) * $Tier2IOPSperGB))
}
}
#Same for "Tier3"
elseif ($Policy -eq $Tier3) {
$obj.Tier = $Tier3
if ((([math]::Truncate(($disk.CapacityGB) * $Tier3IOPSperGB)) -le $Tier3MinimumIOPS) -and ($currentIOPS -ne $Tier3MinimumIOPS)) {
$obj.NewIOPS = $Tier3MinimumIOPS
$disk.Parent | Get-VMResourceConfiguration | Set-VMResourceConfiguration -Disk $disk -DiskLimitIOPerSecond $Tier3MinimumIOPS
} elseif ((([math]::Truncate(($disk.CapacityGB) * $Tier3IOPSperGB)) -gt $Tier3MinimumIOPS) -and ($currentIOPS -ne ([math]::Truncate(($disk.CapacityGB) * $Tier3IOPSperGB)))) {
$obj.NewIOPS = [math]::Truncate(($disk.CapacityGB) * $Tier3IOPSperGB)
$disk.Parent | Get-VMResourceConfiguration | Set-VMResourceConfiguration -Disk $disk -DiskLimitIOPerSecond ([math]::Truncate(($disk.CapacityGB) * $Tier3IOPSperGB))
}
}
$report += New-Object -TypeName PSObject -Property $obj
}
}
}
$report | Export-Csv -Path .\report.csv -NotypeInformation -UseCulture
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Thank you a lot.
The thing you suggested, gives me a full report, and then I can use this block to get a report only for modified VMs/Disks:
$csv = import-csv "C:\Users\user\Desktop\Set IOPS limit per VMDK\report.csv"
$noemptys = foreach($line in $csv){
if(-not($line.NewIOPS -like '')){
$line
}
}
$noemptys | export-csv "C:\Users\user\Desktop\Set IOPS limit per VMDK\report-mod.csv" -NoTypeInformation