Note: pycontrol v1 is deprecated. A new article and script have been added to work through the bigsuds library.

I started this series of tech tips by building the foundation of a route management application which grabs the static routing table from the BIG-IP utilizing the python implementation of iControl called pyControl.  The last tip focused on cli options and deleting routes.  This time, we'll add another extension to add routes to the BIG-IP.

New iControl Method -> add_static_route

Previously we've used the delete_static_route and get_static_route methods, as well as several other methods for getting the route gateways.  The route gateway options is what makes this function (adding routes) more difficult than the route removal function.  The parameters for add_static route are:

  • routes (requiring destination and netmask)
  • attributes (requiring gateway, pool_name, and vlan_name)

We really only want to specify one of the attributes for a route, so we'll set the other two as empty strings when passing to the BIG-IP via the iControl method.

New CLI Option

If you recall from the last article, I moved the required information from arguments to options:

        from optparse import OptionParser
        parser = OptionParser()
       
        parser.add_option("-d",  "--device",  action="store", type="string",  dest="host")
        parser.add_option("-u", "--username",  action="store", type="string",  dest="uname")
        parser.add_option("-r",  "--delRoute",  action="store",  type="string",  dest="delrouteFileName")
        parser.add_option("-a",  "--addRoute",  action="store",  type="string",  dest="addrouteFileName")
        (options, args) = parser.parse_args()

        if options.delrouteFileName:
            del_tmmRoutes(rt,  options.delrouteFileName)

The -a/--addRoute option was present, but no code was included to do anything with that option if it was supplied.  Here's the code in the main section of the script to activate the option:

        if options.addrouteFileName:
            add_tmmRoutes(rt,  options.addrouteFileName, p, v)

You'll notice that we are passing two additional objects to the add_tmmRoutes function than we do for the del_tmmRoutes function.  This is because we might set routes to a vlan or a pool, so we need to ensure those objects exist before trying to create a route that references them.  This requires two additional interfaces: Networking.VLAN and LocalLB.Pool.  These are added to the wsdl_files and loaded to variables:

        b = pc.BIGIP(
                 hostname = options.host,
                 username   = options.uname,
                 password   = upass,
                 wsdl_files = ['Networking.RouteTable', 'LocalLB.Pool', 'Networking.VLAN']
                )
               
        rt = b.Networking_RouteTable
        p = b.LocalLB_Pool
        v = b.Networking_VLAN

Finally, we need to supply a file with routing information.  After trying some different approaches (thanks Joe, Don, Colin, & Matt for the insight) I settled on this format:

destination, netmask, route_type*, gateway (ex: 1.1.20.0, 255.255.255.0, pool, myPool)

*Note: route_type must be specified as pool, vlan, gateway, or reject.  If reject is specified, just submit a trailing comma (ex 1.1.1.20,255.255.255.0,reject,).

Building the add_tmmRoutes Function

 The first thing we need to do is pre-load a list of valid pools and vlans.  This is where our other methods come in:

    pools = pool.get_list()['return']
    vlans = vlan.get_list()['return']

Next, we need to open the file for reading in the route information, set the headers, and prep the lists for the route and attribute dictionaries:

    routefile = open(filename, 'r')
    headers = routefile.readline().strip().split(',')
    rte_hdrs = headers[:2]
    atr_hdrs = ['gateway','pool_name','vlan_name']
    rtes = []
    atrs = []

In the for loop, building the route dictionary is identical to the del_tmmRoutes function, but we don't want to store the route if the attributes don't exist, so we'll need to do some error checking by looking for the route type and checking against our pool and vlan lists accordingly.  If all is well, store the route and attribute information.

    for line in routefile:
        ln = line.strip().split(',')
        rte = ln[:2]
        atr = ln[-2:]
        if atr[0] == 'pool':
            if atr[1] in pools:
                atrs.append(dict(zip(atr_hdrs, ['',atr[1],''])))
                rtes.append(dict(zip(rte_hdrs, rte)))
            else:
                print "Pool ", atr[1], " does not exist"
        elif atr[0] == 'vlan':
            if atr[1] in vlans:
                atrs.append(dict(zip(atr_hdrs, ['','',atr[1]])))
                rtes.append(dict(zip(rte_hdrs, rte)))
            else:
                print "Vlan ", atr[1], " does not exist"
        elif atr[0] == 'gateway':
            atrs.append(dict(zip(atr_hdrs, [atr[1],'',''])))
            rtes.append(dict(zip(rte_hdrs, rte)))
        elif atr[0] == 'reject':
            atrs.append(dict(zip(atr_hdrs, ['','',''])))
            rtes.append(dict(zip(rte_hdrs, rte)))

Once we've iterated through the file, we combine the routes and attributes so we can loop through them and set the routes on the BIG-IP:

    combined = zip(rtes, atrs)
 
    for x in combined:
        xl = list(x)
        obj.add_static_route(routes = [xl[0]], attributes = [xl[1]])

You'll notice above I converted x to a list.  The iControl method had difficulty with the tuple, converting it to a list made it happy (thanks Matt).  Whew.  That's it!

Trial Run

 Now that we've made it through the coding, we'll use this routes file, aptly called routes.txt:

destination,netmask,route_type,gateway
1.1.10.0,255.255.255.0,gateway,10.10.20.1
1.1.20.0,255.255.255.0,pool,gateway_pool
1.1.30.0,255.255.255.0,vlan,isp_att
1.1.40.0,255.255.255.0,vlan,nonvlan
1.1.50.0,255.255.255.0,pool,nonpool
1.1.60.0,255.255.255.0,reject,

And running the script, we get the desired output:

vadmin@vadmin:~$ python2.5 ltmRouteManager.py -u admin -d 10.10.20.5 -a routes.txt
admin, enter your Password:
Loading WSDL: Networking.RouteTable.wsdl
Loading WSDL: LocalLB.Pool.wsdl
Loading WSDL: Networking.VLAN.wsdl
Vlan  nonvlan  does not exist
Pool  nonpool  does not exist

 

TMM IP Routes: (net mask ip)
 0.0.0.0 0.0.0.0 10.10.20.1
 1.1.10.0 255.255.255.0 10.10.20.1

 

TMM Pool Routes: (net mask pool)
 1.1.20.0 255.255.255.0 gateway_pool

 

TMM Vlan Routes: (net mask vlan)
 1.1.30.0 255.255.255.0 isp_att

 

TMM Rejected Routes: (net mask)
 1.1.60.0 255.255.255.0

The wiki entry for the pyControl route management script has been updated to reflect these changes.