4 Replies Latest reply on Oct 8, 2019 7:45 AM by LucD

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

    Akopylov Expert

      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

        • 1. Re: Set IOPS limit on VMDK depending on Storage Policy and disk size
          LucD Guru
          User ModeratorsvExpertCommunity Warriors

          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]

          1 person found this helpful
          • 2. Re: Set IOPS limit on VMDK depending on Storage Policy and disk size
            Akopylov Expert

            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.

            • 3. Re: Set IOPS limit on VMDK depending on Storage Policy and disk size
              LucD Guru
              User ModeratorsvExpertCommunity Warriors

              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" 

              • 4. Re: Set IOPS limit on VMDK depending on Storage Policy and disk size
                LucD Guru
                User ModeratorsCommunity WarriorsvExpert

                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.

                1 person found this helpful