The BIG-IP local traffic management system includes a feature called rate shaping which allows you to enforce a throughput policy on incoming traffic.  Throughput policies are useful for prioritizing and restricting bandwidth on selected traffic patterns. 

Rate shaping can be useful for an e-commerce site that has preferred clients and would like to offer higher throughput for preferred customers, and lower throughput for other site traffic.

This article will discuss how to create and manage rate classes as well as how to assign them to virtual servers.

The rate shaping feature works by first queuing selected packets under a rate class, and then dequeuing the packets at the indicated rate and in the indicated order specified by the rate class.  A rate class is a rate-shaping policy that defines throughput limitatations and a packet scheduling method to be applied to all traffic handled by that rate class.

Usage

The following code samples will build a PowerShell command line application allowing control over Rate Classes.  This program takes as input the bigip, username, and password as well as a subcommand and optional parameters.  Usage is displayed with the Write-Usage function

param (
  $g_bigip = $null,
  $g_uid = $null,
  $g_pwd = $null,
  $g_cmd = $null,
  $g_name = $null,
  $g_arg1 = $null,
  $g_arg2 = $null,
  $g_arg3 = $null,
  $g_arg4 = $null
);

Set-PSDebug -strict;

#-------------------------------------------------------------------------
# function Write-Usage
#-------------------------------------------------------------------------
function Write-Usage()
{
  Write-Host @"
Usage: Rateclass.ps1 host uid pwd [options]
  options
  -------
  list
     - Get a list of rate classess
  create name rate [UNIT_BPS|UNIT_KBPS|UNIT_MBPS]
     - Create a Rate class with the given max throughput rates.
  delete name
     - Delete the specified rate class.
  setbaserate name rate [UNIT_BPS|UNIT_KBPS|UNIT_MBPS]
     - Set the maximum throughput to allot to traffic for this rate class.
  setburstsize name size
     - Set the maximum number of bytes that traffic is allowed to burst beyond the base rate.
  setceilingrate name rate [UNIT_BPS|UNIT_KBPS|UNIT_MBPS]
     - Set how far beyond the base rate the traffic is allowed to flow when bursting.
  setdirection name [DIRECTION_ANY|DIRECTION_CLIENT|DIRECTION_SERVER]
     - Set the direction that the rate class is applied.
  setparent name parent
     - Set the parent rate class that this class is allowed to borrow from.
  setqueuetype name [QUEUE_NONE|QUEUE_STOCHASTIC_FAIR|QUEUE_PRIORITY_FIFO]
     - Set the type of queue used by the specified rate class.
  setvsrateclass name virtual
     - Set the rate class for the specified virtual server.
  removerateclass name virtual
     - Remove the rate class from the specified virtual server.
"@;
  exit;
}

Initialization

As is with all of my iControl PowerShell scripts, validation is made as to whether the iControlSnapin is loaded into the current powershell context.  The Initialize-F5.iControl cmdlet is then called to setup the connection to the BIG-IP for subsequent calls.

The main application logic checks for the passed in command and then passes control to one of the location functions defined below

function Do-Initialize()
{
  if ( (Get-PSSnapin | Where-Object { $_.Name -eq "iControlSnapIn"}) -eq $null )
  {
    Add-PSSnapIn iControlSnapIn
  }
  $success = Initialize-F5.iControl -HostName $g_bigip -Username $g_uid -Password $g_pwd;
  
  return $success;
}

if ( ($g_bigip -eq $null) -or ($g_uid -eq $null) -or ($g_pwd -eq $null) )
{
  Write-Usage;
}

if ( Do-Initialize )
{
  switch ($g_cmd)
  {
    "" {
      Get-RateClassList;
    }
  "list" {
      Get-RateClassList $g_name;
    }
    "create" {
      Create-RateClass $g_name $g_arg1 $g_arg2;
    }
    "delete" {
      Delete-RateClass $g_name;
    }
    "setbaserate" {
      Set-RateClassBaseRate $g_name $g_arg1 $g_arg2;
    }
    "setburstsize" {
      Set-RateclassBurstSize $g_name $g_arg1;
    }
    "setceilingrate" {
      Set-RateClassCeilingRate $g_name $g_arg1 $g_arg2;
    }
    "setdirection" {
      Set-RateClassDirection $g_name $g_arg1;
    }
    "setparent" {
      Set-RateClassParent $g_name $g_arg1;
    }
    "setqueuetype" {
      Set-RateClassQueueType $g_name $g_arg1;
    }
    "setvsrateclass" {
      Set-VirtualServerRateClass $g_name $g_arg1;
    }
    "removevsrateclass" {
      Remove-VirtualServerRateClass $g_name $g_arg1;
    }
    default {
      Write-Usage;
    }
    
  }
}
else
{
  Write-Error "ERROR: iControl subsystem not initialized"
}

Retrieving Rate Class Information

The following function will either query the information about all the rate classes, or if a rate class name is passed in as an argument, it will get the information for just that one.  All of the configurable attributes will be displayed.  I will discuss each attribute in the following sections below.  Finally after the rate class attributes are displayed, I will do a reverse lookup on the Virtual Servers to see if any of them are using the specific rate class.  If a Virtual Server is configured with the given rate class, it will be displayed to the user as well.  In my example below, you will see that I have no rate classes defined so it will return an empty list.

function Get-RateClassList()
{
  param([string]$rc = $null );
  if ( $rc )
  {
    $rc_list = ,$rc;
  }
  else
  {
    $rc_list = (Get-F5.iControl).LocalLBRateClass.get_list();
  }
  $vs_list = (Get-F5.iControl).LocalLBVirtualServer.get_list();
  $vsrc_list = (Get-F5.iControl).LocalLBVirtualServer.get_rate_class($vs_list);
  
  Write-Host "Rate Classes:";
  if ( $rc_list )
  {
    $base_rates = (Get-F5.iControl).LocalLBRateClass.get_base_rate( $rc_list );
    $burst_sizes = (Get-F5.iControl).LocalLBRateClass.get_burst_size( $rc_list );
    $ceiling_rates = (Get-F5.iControl).LocalLBRateClass.get_ceiling_rate( $rc_list );
    $directions = (Get-F5.iControl).LocalLBRateClass.get_direction( $rc_list );
    $parents = (Get-F5.iControl).LocalLBRateClass.get_parent( $rc_list );
    $queue_types = (Get-F5.iControl).LocalLBRateClass.get_queue_type( $rc_list );
    for($i=0; $i -lt $rc_list.Length; $i++)
    {
      Write-Host @"
--------------------
Rate Class $($rc_list[$i])
  Base Rate    : $($base_rates[$i].rate) $($base_rates[$i].unit)
  Burst Size   : $($burst_sizes[$i])
  Ceiling Rate : $($ceiling_rates[$i].rate) $($ceiling_rates[$i].unit)
  Direction    : $($directions[$i])
  Parent       : $($parents[$i])
  Queue Type   : $($queue_types[$i])
"@

	  Write-Host "  Virtuals     :";
      for($j=0; $j -lt $vsrc_list.Length; $j++)
      {
        if ( $vsrc_list[$j] -eq $rc_list[$i] )
		{
		  Write-Host "                 $($vs_list[$j])"
		}
      }
    }
  }
}
PS C:\> .\PsRateClass.ps1 bigip user pass
Rate Classes:

Creating a Rate Class

The only attribute needed to create a rate class is the base rate unit.  This is defined as a rate value along with a unit of UNIT_BPS, UNIT_KBPS, or UNIT_MBPS for bits per second, kilobits per second, or megabits per second respectively.  The following function takes these values as arguments and then call the iControl.LocalLBRateClass.create method with the rate class name along with the rate unit structure.  In my example below, a rate class named "myrc" is created with a base rate of 100 megabits per second.  You'll see the default values for the other attributes below.  Since this is a new rate class, it is not assigned to any virtual servers, so that list will be empty.

function Create-RateClass()
{
  param([string]$name = $null, [int]$rate = 0, [string]$unit = "UNIT_BPS");
  if ( $name -and $rate )
  {
    $rateunit = New-Object -TypeName iControl.LocalLBRateClassRateUnit;
    $rateunit.rate = $rate;
    $rateunit.unit = $unit;
    (Get-F5.iControl).LocalLBRateClass.create( (,$name), (,$rateunit) );
    Write-Host "Rate Class $name created...";
    Get-RateClassList $name;
  }
}
PS C:\> .\PsRateClass.ps1 bigip user pass create myrc 100 UNIT_MBPS
Rate Classes:
-------------------
Rate Class myrc
  Base Rate    : 100 UNIT_MBPS
  Burst Size   : 0
  Ceiling Rate : 100 UNIT_MBPS
  Direction    : DIRECTION_ANY
  Parent       : 
  Queue Type   : QUEUE_STOCHASTIC_FAIR
  Virtuals     : 

Deleting a Rate Class

Well, if you want to create a rate class, it make sense that you may want to delete one.  This can be done with the local Delete-RateClass function.  It just takes the name of a rate class and calls the iControl.LocalLBRateClass.delete_rate_class() method.  The Get-RateClass function is then called to list out the remaining rate classes.

function Delete-RateClass()
{
  param([string]$name = $null);
  if ( $name )
  {
    (Get-F5.iControl).LocalLBRateClass.delete_rate_class( (,$name) );
    Write-Host "Rate Class $name deleted...";
    Get-RateClassList;
  }
}
PS C:\> .\PsRateClass.ps1 bigip user pass delete myrc
------------------
Rate Classes:

Assigning the Rate Class Base Rate

The base rate specifies the throughput rate allowed for traffic that the rate class handles.  Packets are generally not allowed to exceed the specified rate.  You can use the iControl.LocalLBRateClass.set_base_rate() method to assign a new base rate to a rate class.  You'll notice that this method is very similar to the create() method illustrated above.  In the below example, I'll change the previously defined base rate of 100 mbps to 100 kbps.  Notice that the base rate is changed but the ceiling rate remains the original value.  To update the ceiling rate, you will need to modify that separately as described below.

function Set-RateClassBaseRate()
{
  param([string]$name = $null, [int]$rate = 0, [string]$unit = "UNIT_BPS");
  if ( $name -and $rate )
  {
    $rateunit = New-Object -TypeName iControl.LocalLBRateClassRateUnit;
    $rateunit.rate = $rate;
    $rateunit.unit = $unit;
    (Get-F5.iControl).LocalLBRateClass.set_base_rate( (,$name), (,$rateunit) );
    Get-RateClassList $name;
  }
}
PS C:\> .\PsRateClass.ps1 bigip user pass setbaserate 100 UNIT_KBPS
Rate Classes:
--------------------
Rate Class myrc
  Base Rate    : 100 UNIT_KBPS
  Burst Size   : 0
  Ceiling Rate : 100 UNIT_MBPS
  Direction    : DIRECTION_ANY
  Parent       :
  Queue Type   : QUEUE_STOCHASTIC_FAIR
  Virtuals     :

 Assigning a Burst Size

The burst size is the maximum number of bytes that traffic is allowed to burst beyond the base rate, before needing to borrow bandwidth.  When this value is set to 0, no bursting is allowed.  You use the burst size setting when you want to allow the rate of traffic flow that  a rate class controls to exceed the base rate.  Exceeding the base rate is known as bursting.  When you configure a rate class to allow bursting, the BIG-IP system saves any unused bandwidth and uses that bandwidth later to enable the rate of traffic to flow to temporarily exceed the base rate.  Specifying a burst size is useful for smoothing out traffic patterns that tend to fluctuate or exceed the base rate such as HTTP traffic.

The following function allows you to set the burst rate (in bytes) by calling the iControl.LocalLBRateClass.set_burst_size() method.  I will set the burst rate to 1000 bytes for the previously defined rate class.

function Set-RateClassBurstSize()
{
  param([string]$name = $null, [int]$size = 0);
  if ( $name -and $size )
  {
    (Get-F5.iControl).LocalLBRateClass.set_burst_size( (,$name), (,$size) );
    Get-RateClassList $name;
  }
}
PS C:\> .\PsRateClass.ps1 bigip user pass setburstsize myrc 1000
Rate Classes:
--------------------
Rate Class myrc
  Base Rate    : 100 UNIT_KBPS
  Burst Size   : 1000
  Ceiling Rate : 100 UNIT_MBPS
  Direction    : DIRECTION_ANY
  Parent       :
  Queue Type   : QUEUE_STOCHASTIC_FAIR
  Virtuals     :

Setting the Ceiling Rate

The ceiling rate setting specifies the absolute limit at which traffic is allowed to flow when bursting or borrowing.  Just like for the base rate, you can specify the ceiling rate in bits per second, kilobits per second, or megabits per second.  The ceiling rate must be equal to or greater than the base rate.  If the ceiling rate equals the base rate, traffic throughput can never exceed the base rate.

The Set-RateClassCelingRate function takes as input the rate class name along with a ceiling rate and unit and then calls the iControl.LocalLBRateClass.set_ceiling_rate() method with those parameters.  The Get-RateClass function is then called to show that the changes took effect.

function Set-RateClassCeilingRate()
{
  param([string]$name = $null, [int]$rate = 0, [string]$unit = "UNIT_BPS");
  if ( $name -and $rate )
  {
    $rateunit = New-Object -TypeName iControl.LocalLBRateClassRateUnit;
    $rateunit.rate = $rate;
    $rateunit.unit = $unit;
    (Get-F5.iControl).LocalLBRateClass.set_ceiling_rate( (,$name), (,$rateunit) );
    Get-RateClassList $name;
  }
}
PS C:\> .\PsRateClass.ps1 bigip user pass setceilingrate myrc 1000 UNIT_MBPS
Rate Classes:
--------------------
Rate Class myrc
  Base Rate    : 100 UNIT_KBPS
  Burst Size   : 1000
  Ceiling Rate : 1000 UNIT_MBPS
  Direction    : DIRECTION_ANY
  Parent       :
  Queue Type   : QUEUE_STOCHASTIC_FAIR
  Virtuals     :

Setting the Rate Class Direction

Using the direction setting, you can apply a rate class to traffic going to a client, to a server, or to both client and server.   Specifying direction is useful in cases where the nature of traffic is directionally based.  For example, if you offer an FTP service to external clients, you might be more interested in limiting throughput for those clients uploading files to your site than you are for clients downloading files from your site.  In this case, you would want a rate class based on traffic going to your server.

In the following example, I'll change the direction on this rate class from the default value of DIRECTION_ANY to DIRECTION_SERVER to allow this rate class to only work on traffic going to the destination server.

function Set-RateClassDirection()
{
  param([string]$name = $null, [string]$direction = "DIRECTION_ANY");
  if ( $name )
  {
    (Get-F5.iControl).LocalLBRateClass.set_direction( (,$name), (,$direction) );
    Get-RateClassList $name;
  }
}
PS C:\> .\PsRateClass.ps1 bigip user pass setdirection myrc DIRECTION_SERVER
Rate Classes:
--------------------
Rate Class myrc
  Base Rate    : 100 UNIT_KBPS
  Burst Size   : 1000
  Ceiling Rate : 1000 UNIT_MBPS
  Direction    : DIRECTION_SERVER
  Parent       :
  Queue Type   : QUEUE_STOCHASTIC_FAIR
  Virtuals     :

 Assigning a Rate Class Parent Class

When you create a rate class, you can use the Parent Class setting to specify that the rate class has a parent class.  This allows the rate class to borrow unused bandwidth from that parent class.  A child class can borrow unused bandwidth from its parent, but a parent class cannot borrow from a child class.  Borrowing is also not possible between two child classes of the same parent class or between two unrelated rate classes.  A parent class can itself have a parent class, provided that you do not create a circular dependency (ie. when a rate class is direct, or indirect, child of itself).

The following example will set the parent class of the "myrc" rate class to the rate class "baserc".

function Set-RateClassParent()
{
  param([string]$name = $null, [string]$parent = "");
  if ( $name )
  {
    (Get-F5.iControl).LocalLBRateClass.set_parent( (,$name), (,$parent) );
    Get-RateClassList $name;
  }
}
PS C:\> .\PsRateClass.ps1 bigip user pass setparent myrc baserc
Rate Classes:
--------------------
Rate Class myrc
  Base Rate    : 100 UNIT_KBPS
  Burst Size   : 1000
  Ceiling Rate : 1000 UNIT_MBPS
  Direction    : DIRECTION_SERVER
  Parent       : baserc
  Queue Type   : QUEUE_STOCHASTIC_FAIR
  Virtuals     :

Setting the Queue Type

The Queue Type (or Queue Discipline) setting determines the method and order in which the BIG-IP system dequeues packets.  The type can be QUEUE_STOCHASTIC_FAIR or QUEUE_PRIORITY_FIFO. 

Stochastic Fair Queuing is a queueing method that queues traffic under a set of many lists, choosing the specific list based on a periodically changing hash of the connection information.  This results in traffic from the same connection always being queued in the same list.  It then dequeues traffic from the set of lists in a round-robin fashion.  The overall effect is that fairness of dequeueing is achieved because one high-speed connection cannot monopolize the queue at the expense of slower connections.

Priority FIFO is a queueing method that queues all trafffic under a set of five lists based on the Type of Service (ToS) field of the traffic.  Four of the lists correspond to the four possible ToS values (Minimum delay, Maximum througput, Maximum reliability, and Minimum cost).  the fifth list represents traffic with no ToS value.  The PFIFO method then processes these five lists in a way that attempts to preserve the meaning of the ToS field as much as possible.

In the following example, I'll set the queue type of the myrc rate class from the default QUEUE_STOCHASTIC_FAIR to QUEUE_PRIORITY_FIFO with the local Set-RateClassQueueType function.  This calls the iControl.LocalLBRateClass.set_queue_type() method.

function Set-RateClassQueueType()
{
  param([string]$name = $null, [string]$type = "QUEUE_STOCHASTIC_FAIR");
  if ( $name )
  {
    (Get-F5.iControl).LocalLBRateClass.set_queue_type( (,$name), (,$type) );
    Get-RateClassList $name;
  }
}
PS C:\> .\PsRateClass.ps1 bigip user pass setqueuetype myrc QUEUE_PRIORITY_FIFO
Rate Classes:
--------------------
Rate Class myrc
  Base Rate    : 100 UNIT_KBPS
  Burst Size   : 1000
  Ceiling Rate : 1000 UNIT_MBPS
  Direction    : DIRECTION_SERVER
  Parent       : baserc
  Queue Type   : QUEUE_PRIORITY_FIFO
  Virtuals     :

 Assigning a Rate Class to a Virtual Server

Having a rate class is good and all, but it doesn't really do anything until you allow traffic to pass through it.  This can be done by assigning it as an attriute to a Virtual Server.  The iControl.LocalLBVirtualServer.set_rate_class() takes a list of virtual server name and the corresponding rate class names to assign to those virtual servers.  The following example will assign the "myrc" rate class to the Virtual Server "xpbert-http".  After the call is made, you will see in the listing that the reverse lookup through the virtual servers showed this rate class was applied to virtual server xpbert-http.

function Set-VirtualServerRateClass()
{
  param([string]$name = $null, [string]$vs = $null);
  if ( $name )
  {
    (Get-F5.iControl).LocalLBVirtualServer.set_rate_class( (,$vs), (,$name) );
    Get-RateClassList $name;
  }
}
PS C:\> .\PsRateClass.ps1 bigip user pass setvsrateclass myrc xpbert-http
Rate Classes:
--------------------
Rate Class myrc
  Base Rate    : 100 UNIT_KBPS
  Burst Size   : 1000
  Ceiling Rate : 1000 UNIT_MBPS
  Direction    : DIRECTION_SERVER
  Parent       : baserc
  Queue Type   : QUEUE_PRIORITY_FIFO
  Virtuals     :
                 xpbert-http

 Removing a Rate Class from a Virtual Server

There is no corresponding remove_rate_class method in the LocalLBVirtualServer interface.  Instead, if you want to remove the rate class, you just pass in an empty string for the rate class name to the set_rate_class() method.  The following example will remove the rate class "myrc" from virtual server "xpbert-http";

function Remove-VirtualServerRateClass()
{
  param([string]$name = $null, [string]$vs = $null);
  if ( $name )
  {
    (Get-F5.iControl).LocalLBVirtualServer.set_rate_class( (,$vs), (,"") );
    Get-RateClassList $name;
  }
}
PS C:\> .\PsRateClass.ps1 bigip user pass removevsrateclass myrc xpbert-http
Rate Classes:
--------------------
Rate Class myrc
  Base Rate    : 100 UNIT_KBPS
  Burst Size   : 1000
  Ceiling Rate : 1000 UNIT_MBPS
  Direction    : DIRECTION_SERVER
  Parent       : baserc
  Queue Type   : QUEUE_PRIORITY_FIFO
  Virtuals     :

Conclusion

You know have all the tools you need to create, modify, and apply a rate class to start controlling the traffic flow to and from your servers.

The code for this article can be found under the PsRateClass entry in the iControl CodeShare.

Get the Flash Player to see this player.