VMware Cloud Community
Akopylov
Commander
Commander
Jump to solution

Set IOPS limit on VMDK depending on Storage Policy and disk size

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 Smiley Happy

1 Solution

Accepted Solutions
LucD
Leadership
Leadership
Jump to solution

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

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 {($_.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

View solution in original post

7 Replies
LucD
Leadership
Leadership
Jump to solution

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.

try{

    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

Akopylov
Commander
Commander
Jump to solution

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.

0 Kudos
LucD
Leadership
Leadership
Jump to solution

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

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 {($_.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

LucD
Leadership
Leadership
Jump to solution

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

Akopylov
Commander
Commander
Jump to solution

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.

0 Kudos
LucD
Leadership
Leadership
Jump to solution

Try something like this.

$report = @()

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

Akopylov
Commander
Commander
Jump to solution

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

0 Kudos