Managing ZoneRunner Resource Records with Bigsuds
Over the last several years, there have been questions internal and external on how to manage ZoneRunner (the GUI tool in F5 DNS that allows you to manage DNS zones and records) resources via the REST interface. But that's a no can do with the iControl REST--it doesn't have that functionality. It was brought to my attention by one of our solutions engineers that a customer is using some methods in the SOAP interface that allows you to do just that...which was news to me! The things you learn... In this article, I'll highlight a few of the methods available to you and work on a sample domain in the python module bigsuds that utilizes the suds SOAP library for communication duties with the BIG-IP iControl SOAP interface. Test Domain & Procedure For demonstration purposes, I'll create a domain in the external view, dctest1.local, with the following attributes that mirrors nearly identically one I created in the GUI: Type: master Zone Name: dctest1.local. Zone File Name: db.external.dctest1.local. Options: allow-update from localhost TTL: 500 SOA: ns1.dctest1.local. Email: hostmaster.ns1.dctest1.local. Serial: 2021092201 Refresh: 10800 Retry: 3600 Expire: 604800 Negative TTL: 60 I'll also add a couple type A records to that domain: name: mail.dctest1.local., address: 10.0.2.25, TTL: 86400 name: www.dctest1.local., address: 10.0.2.80, TTL: 3600 After adding the records, I'll update one of them, changing the IP and the TTL: name: mail.dctest1.local., address: 10.0.2.110, ttl: 900 Then I'll delete the other one: name: www.dctest1.local., address: 10.0.2.80, TTL: 3600 And finally, I'll delete the zone: name: dctest1.local. ZoneRunner Methods All the methods can be found on Clouddocs in the ZoneRunner, Zone, and ResourceRecord method pages. The specific methods we'll use in our highlight real are: Management.ResourceRecord.add_a Management.ResourceRecord.delete_a Management.ResourceRecord.get_rrs Management.ResourceRecord.update_a Management.Zone.add_zone_text Management.Zone.get_zone_v2 Management.Zone.zone_exist With each method, there is a data structure that the interface expects. Each link above provides the details, but let's look at an example with the add_a method. The method requires three parameters, view_zones, a_records, and sync_ptrs, which the image of the table shows below. The boolean is just a True/False value in a list. The reason the list ( [] ) is there for all the attributes is because you can send a single request to update more than one zone, and addmore than one record within each zone if desired. The data structure for view_zones and a_records is in the following two images. Now that we have an idea of what the methods require, let's take a look at some code! Methods In Action First, I import bigsuds and initialize the BIG-IP. The arguments are ordered in bigsuds for host, username, and password. If the default “admin/admin” is used, they are assumed, as is shown here. import bigsuds b = bigsuds.BIGIP(hostname='ltm3.test.local') Next, I need to format the ViewZone data in a native python dictionary, and then I check for the existence of that zone. zone_view = {'view_name': 'external', 'zone_name': 'dctest1.local.' } b.Management.Zone.zone_exist([zone_view]) # [0] Note that the return value, which should be a list of booleans, is a list with a 0. I’m guessing that’s either suds or the bigsuds implementation doing that, but it’s important to note if you’re checking for a boolean False. It’s also necessary to set the booleans as 0 or 1 as well when sending requests to BIG-IP with bigsuds. Now I will create the zone since it does not yet exist. From the add_zone_text method description on Clouddocs, note that I need to supply, in separate parameters, the zone info, the appropriate zone records, and the boolean to sync reverse records or not. zone_add_info = {'view_name': 'external', 'zone_name': 'dctest1.local.', 'zone_type': 'MASTER', 'zone_file': 'db.external.dctest1.local.', 'option_seq': ['allow-update { localhost;};']} zone_add_records = 'dctest1.local. 500 IN SOA ns1.dctest1.local. hostmaster.ns1.dctest1.local. 2021092201 10800 3600 604800 60;\n' \ 'dctest1.local. 3600 IN NS ns1.dctest1.local.;\n' \ 'ns1.dctest1.local. 3600 IN A 10.0.2.1;' b.Management.Zone.add_zone_text([zone_add_info], [[zone_add_records]], [0]) b.Management.Zone.zone_exist([zone_view]) # [1] Note that the strings here require a detailed understanding of DNS record formatting, the individual fields are not parameters that can be set like in the ZoneRunner GUI. But, I am confident there is an abundance of modules that manage DNS formatting in the python ecosystem that could simplify the data structuring. After creating the zone, another check to see if the zone exists results in a true condition. Huzzah! Now I’ll check the zone info and the existing records for that zone. zone = b.Management.Zone.get_zone_v2([zone_view]) for k, v in zone[0].items(): print(f'{k}: {v}') # view_name: external # zone_name: dctest1.local. # zone_type: MASTER # zone_file: "db.external.dctest1.local." # option_seq: ['allow-update { localhost;};'] rrs = b.Management.ResourceRecord.get_rrs([zone_view]) for rr in rrs[0]: print(rr) # dctest1.local. 500 IN SOA ns1.dctest1.local. hostmaster.ns1.dctest1.local. 2021092201 10800 3600 604800 60 # dctest1.local. 3600 IN NS ns1.dctest1.local. # ns1.dctest1.local. 3600 IN A 10.0.2.1 Everything checks outs! Next I’ll create the A records for the mail and www services. I’m going to add a filter to only check for the mail/www services for printing to cut down on the lines, but know that they’re still there going forward. a1 = {'domain_name': 'mail.dctest1.local.', 'ip_address': '10.0.2.25', 'ttl': 86400} a2 = {'domain_name': 'www.dctest1.local.', 'ip_address': '10.0.2.80', 'ttl': 3600} b.Management.ResourceRecord.add_a(view_zones=[zone_view], a_records=[[a1, a2]], sync_ptrs=[0]) rrs = b.Management.ResourceRecord.get_rrs([zone_view]) for rr in rrs[0]: if any(item in rr for item in ['mail', 'www']): print(rr) # mail.dctest1.local. 86400 IN A 10.0.2.25 # www.dctest1.local. 3600 IN A 10.0.2.80 Here you can see that I’m adding two records to the zone specified and not creating the reverse records (not included for brevity, but in prod would be likely). Now I’ll update the mail address and TTL. b.Management.ResourceRecord.update_a([zone_view], [[a1]], [[a1_update]], [0]) rrs = b.Management.ResourceRecord.get_rrs([zone_view]) for rr in rrs[0]: if any(item in rr for item in ['mail', 'www']): print(rr) # mail.dctest1.local. 900 IN A 10.0.2.110 # www.dctest1.local. 3600 IN A 10.0.2.80 You can see that the address and TTL updated as expected. Note that with the update_/N/ methods, you need to provide the old and new, not just the new. Let’s get destruction and delete the www record! b.Management.ResourceRecord.delete_a([zone_view], [[a2]], [0]) rrs = b.Management.ResourceRecord.get_rrs([zone_view]) for rr in rrs[0]: if any(item in rr for item in ['mail', 'www']): print(rr) # mail.dctest1.local. 900 IN A 10.0.2.110 And your web service is now unreachable via DNS. Congratulations! But there’s more damage we can do: it’s time to delete the whole zone. b.Management.Zone.delete_zone([zone_view]) b.Management.Zone.zone_exist([zone_view]) # [0] And that’s a wrap! As I said, it’s been years since I have spent time with the iControl SOAP interface. It’s nice to know that even though most of what we do is done through REST, imperatively or declaratively, that some missing functionality in that interface is still alive and kicking via SOAP. H/T to Scott Huddy for the nudge to investigate this. Questions? Drop me a comment below. Happy coding! A gist of these samples is available on GitHub.1KViews2likes1CommentGraphing Performance Data with bigsuds
I was asked internally about some performance graphs with python, and while I've done some work with python and iControl over the years, but I've yet to take on graphing. Most of the python scripts I've written output results to the console, so there was no need for fancy things like graphics. I was thinking of a web-based app, but decided to stick with a console app and use the matplotlib 2D plotting libary to generate the graphs. In addition to the normal modules I use for a python script (suds, bigsuds, getpass, argparse) interacting with the BIG-IP, there are few additions: base64 - used to decode the iControl stats data returned from BIG-IP time - used to reformat the date/time information returned from BIG-IP from epoch to date-time) csv - used to read the csv file data to pull out the time field to update it numpy - used to convert the string data from the csv file to arrays of float data for use with matplotlib. matplotlib - used for generating the graphs and for the graph date formatting of the timestamps. Two modules that are not imported directly in the script but are required nonetheless for matplotlib: dateutil and pyparsing, so you'll need to install those as well before getting started. Thankfully, Joe wrote on performance graphs for many moons ago in his icontrol 101 #11 article. So the only real work I had to do was python-ize his code, and do my best to one-up his solution! Grabbing the Data This section of the main loop consists of importing all the modules we'll need, initializing the BIG-IP, asking the user what graph they'd like to see, and grabbing that data from the BIG-IP via an iControl call. import bigsuds as pc import getpass import argparse import matplotlib.pyplot as plt import matplotlib.dates as mdates import numpy as np import csv, time, base64 parser = argparse.ArgumentParser() parser.add_argument("-s", "--system", required=True) parser.add_argument("-u", "--username", required=True) args = vars(parser.parse_args()) print "%s, enter your " % args['username'], upass = getpass.getpass() b = pc.BIGIP(args['system'], args['username'], upass) stats = b.System.Statistics graphs = stats.get_performance_graph_list() graph_obj_name = [] count = 1 print "Graph Options" for x in graphs: graph_obj_name.append(x['graph_name']) print "%d> %s, %s" % (count, x['graph_description'], x['graph_name']) count +=1 num_select = int(raw_input("Please select the number for the desired graph data: ")) graphstats = stats.get_performance_graph_csv_statistics( [{'object_name': graph_obj_name[num_select - 1], 'start_time': 0, 'end_time': 0, 'interval': 0, 'maximum_rows': 0}]) In the for loop, I'm just iterating through the data returned by the get_performance_graph_list method and printing it to the console so the user can select the graph they want. Next, I take the user's selection and graph the performance data for that graph with the get_performance_graph_csv_statistics method. Storing and Manipulating the Data Now that I have the data from the BIG-IP, I store it in a .csv file just in case the user would like to use it afterward. Before writing it to said .csv however, I need to b64decode the data as show below. statsFile = "%s_rawStats.csv" % graph_obj_name[num_select-1] f = open(statsFile, "w") f.write(base64.b64decode(graphstats[0]['statistic_data'])) f.close() This results in cvs data that looks like this: timestamp,"Total Phys Memory","OS Used Memory","TMM Alloc Memory","TMM Used Memory","OS Used Swap" 1387226160, nan, nan, nan, nan, nan 1387226400,4.1607127040e+09,4.1204910763e+09,1.7070817280e+09,6.0823478033e+08,2.4167403520e+08 1387226640,4.1607127040e+09,4.1187021824e+09,1.7070817280e+09,6.0832352860e+08,2.4183606613e+08 After storing the raw data, I re-open that file and read it in line by line, reformatting the timestamp and writing that to a new file: outputFile = "%s_modStats.csv" % graph_obj_name[num_select-1] with open(statsFile) as infile, open(outputFile, "w") as outfile: r = csv.reader(infile) w = csv.writer(outfile) w.writerow(next(r)) for row in r: fl = float(row[0]) row[0] = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(fl)) w.writerow(row) This results in updated csv data. timestamp,Total Phys Memory,OS Used Memory,TMM Alloc Memory,TMM Used Memory,OS Used Swap 2013-12-16 14:36:00, nan, nan, nan, nan, nan 2013-12-16 14:40:00,4.1607127040e+09,4.1204910763e+09,1.7070817280e+09,6.0823478033e+08,2.4167403520e+08 2013-12-16 14:44:00,4.1607127040e+09,4.1187021824e+09,1.7070817280e+09,6.0832352860e+08,2.4183606613e+08 I made sure that each graph would end up with its own set of csv files. I'm sure there's a smarter more pythonic way to handle this, but hey, it works. Calling the Graph Functions This is the simplest section of code, an if / elif chain! There is no switch statement in python like in Tcl, but there are other solutions available. The if / elif works just fine, so it remains. if num_select == 1: graph_memory(outputFile) elif num_select == 2: graph_cpu(outputFile) elif num_select == 3: graph_active_connections(outputFile) elif num_select == 4: graph_new_connections(outputFile) elif num_select == 5: graph_throughput(outputFile) elif num_select == 7: graph_http_requests(outputFile) elif num_select == 8: graph_ramcache_util(outputFile) elif num_select == 12: graph_active_ssl(outputFile) else: print "graph function not yet defined, please select 1, 2, 3, 4, 5, 7, 8, or 12." The else is there for the myriad of graphs I have not generated, I'll leave that as an exercise for you! Generating the Graphs On my system, collecting the graphs from the get_performance_graph_list method results in 38 different graphs. Many of these graphs have different number of columns of data, so consolidating all these functions into one and working out the data passing and error correction was not an exercise I was willing to take on. So...lots of duplicate code for each graph function, but again, it works. The graphs I have built functions for are memory, cpu, active and new connections, throughput (bits), http requests, ramcache utilization, and active ssl connections. The function is essentially the same for all of them, the changes are related to number of plot lines, labels, etc. The memory graph function is below. def graph_memory(csvdata): timestamp, total_mem, os_used, tmm_alloc, tmm_used, swap_used = np.genfromtxt(csvdata, delimiter=',', skip_header=2, skip_footer=2, unpack=True, converters= {0: mdates.strpdate2num('%Y-%m-%d %H:%M:%S')}) fig = plt.figure() plt.plot_date(x=timestamp, y=total_mem, fmt='-') plt.plot(timestamp, total_mem, 'k-', label="Total Memory") plt.plot(timestamp, tmm_used, 'r-', label="TMM Used") plt.plot(timestamp, tmm_alloc, 'c-', label="Tmm Allocated") plt.plot(timestamp, os_used, 'g-', label="OS Used") plt.plot(timestamp, swap_used, 'b-', label="OS Used Swap") plt.legend(title="Context", loc='upper left', shadow=True) plt.title('Global Memory') plt.ylabel('GB') plt.show() In this function, numpy (np.genfromtxt) is used to pull in the csv data to arrays that matplotlib can use. The rest of the function is just placing and formatting data for the graph. Running the Script C:\>python stats1.py -s 192.168.1.1 -u citizen_elah citizen_elah, enter your Password: Graph Options. Not all options supported at this time, please select only 1, 2, 3, 4, 5, 7, 8, or 12. 1> Memory Used, memory 2> System CPU Usage %, CPU 3> Active Connections, activecons 4> Total New Connections, newcons 5> Throughput(bits), throughput 6> Throughput(packets), throughputpkts 7> HTTP Requests, httprequests 8> RAM Cache Utilization, ramcache 9> Blade Memory Usage, b0memory 10> Active Connections, detailactcons1 11> Active Connections per Blade, detailactcons2 12> Active SSL Connections, detailactcons3 13> New Connections, detailnewcons1 14> New PVA Connections, detailnewcons2 15> New ClientSSL Profile Connections, detailnewcons3 16> New TCP Accepts/Connects, detailnewcons4 17> Client-side Throughput, detailthroughput1 18> Server-side Throughput, detailthroughput2 19> HTTP Compression Rate, detailthroughput3 20> SSL Transactions/Sec, SSLTPSGraph 21> GTM Requests and Resolutions, GTMGraph 22> GTM Requests, GTMrequests 23> GTM Resolutions, GTMresolutions 24> GTM Resolutions Persisted, GTMpersisted 25> GTM Resolutions Returned to DNS, GTMret2dns 26> GTM Requests per DNS type, GTMRequestBreakdownGraph 27> Active Sessions, act_sessions 28> New Sessions, new_sessions 29> Access Requests, access_requests 30> ACL Actions, acl_stats 31> Active Network Access Connections, NAActiveConnections 32> New Network Access Connections, NANewConnections 33> Network Access Throughput, NAthroughput 34> Rewrite Transactions, rewrite_trans 35> Rewrite Transaction Data, rewrite_bytes 36> GTM requests for IPv4 addresses, GTMarequests 37> GTM requests for IPv6 addresses, GTMaaaarequests 38> CPU Usage, blade0cpuset1 Please select the number for the desired graph data: 1 Wrapping Up This was a fun walk through the matplotlib and the iControl interface to generate some graphs. The script in full is here in the codeshare. Happy coding!345Views0likes2CommentsConfig Backup for F5 Review
Eric Flores, author of Config Backup for F5, will be joining John Wagnon and I for a podcast this evening to discuss his project. This open source project is great for the shops that don't have budget for the robust feature sets of Enterprise Manager and BIG-IQ. The bits are offered as a downloadable tgz, or more attractively for those that want something to just work right out of the box, a VMware appliance! For this review, I opted for the latter. One of the more frustrating this about most small pet projects on github, sourceforge, and the like is the crazy lack of good documentation. That is not the case with Eric's project. The backup appliance documentation left no step undocumented, and the user guide is well documented as well, removing almost all the guess work. Deploying the appliance was very easy and the install process clean and tidy, but a few things I'd clarify in the documentation or enhance: Make sure the nic on the appliance belongs to a network that has access to your BIG-IPs before powering it up, though, the default nic the appliance grabbed in my workstation was a bridged network my BIG-IPs don't belong to. The NTP configuration specified an IP, but it would be nice to support names. It may already as I didn't try to add a name, but it would be good to support both and state that in the configuration utility. The NTP configuration utility had my local time and UTC swapped in the display, but in the appliance web GUI it appears fine. I went through the NTP configuration a second time with similar results, so I'm guessing just a variable swap here? Once the appliance was configured and rebooted, I logged in to the web GUI. This is very straight forward and cleanly laid out very similarly to the F5 GUI. The user guide goes through the user functions (devices, backup jobs, certificates) first, but I'd recommend configuring the settings first. In the Backup Settings, you set the backup time and the user name and password. This is important, as no device you add will be touched until this information is present. After updating the backup settings, I added my device. I added/deleted a couple times before changing my backup time and I was wondering what wasn't working. It turns out the initial discovery of the device isn't done until the backup time occurs. I'd recommend in future enhancements to go ahead and do the device discovery shown in this screen and the Certificates screen when the device is added rather than waiting for the backup window. Other than those few minor nits, this is a fantastic effort on Eric's part, and a great testament to the power of community. Thanks Eric for sharing and we look forward to seeing what enhancements and features you have in store for this project! Technorati Tags: Config Backup for F5,Eric Flores,iControl,bigsuds867Views0likes6CommentsManaging BIG-IP Routes with the Python Bigsuds Library
Perusing the Q&A section of DevCentral a couple days ago, I noticed a question on adding routes from community member Richard: Is there a smart way to add a bunch of routes that doesn't mandate manual entry... (The JRahm paraphrase) There were a couple great answers on using merge or copy/paste, but this jogged the memory that "hey, I've done that before!" A few yeas ago I was just learning python and how to use the iControl API and wrote a three part series on this very thing: pyControl Apps #1 - BIG-IP Routing Table pyControl Apps #2 - Deleting BIG-IP Routes pyControl Apps #3 - Adding BIG-IP Routes That works, but pycontrol v1 (these scripts) and even pycontrol v2 are no longer supported/developed since the bigsuds library was introduced late last year. So I decided to update the script to support the bigsuds library, the python argparse module over the originally used and discontinued optparse module, and finally, using the RouteTableV2 iControl interface instead of the deprecated RouteTable interface. The Main Loop First, the code: if __name__ == "__main__": import bigsuds as pc import getpass import argparse parser = argparse.ArgumentParser() parser.add_argument("-s", "--system", required=True) parser.add_argument("-u", "--username", required=True) parser.add_argument("-d", "--delRoute") parser.add_argument("-a", "--addRoute") args = vars(parser.parse_args()) print "%s, enter your " % args['username'], upass = getpass.getpass() b = pc.BIGIP(args['system'], args['username'], upass) if args['delRoute']: del_tmmRoutes(b.Networking.RouteTable, args['delRoute']) if args['addRoute']: add_tmmRoutes(b.Networking.RouteTableV2, args['addRoute'], b.LocalLB.Pool, b.Networking.VLAN) get_tmmRoutes(b.Networking.RouteTableV2) The main loop remains unchanged in structure. First, I update the iControl reference for the bigsuds library instead of the pycontrol one and update the instantiation arguments. Next, I needed to swap out optparse in favor of argparse to handle the command line arguments for the script. Finally, I updated the iControl references from RouteTable to RouteTableV2, except in the reference to the delete route function. More on that below. The Delete Route Function Beginning in TMOS version 11, the RouteTable iControl interface was deprecated and RouteTableV2 introduced. However, the parameters for the delete_static_route method changed from an address/mask structure to a string, expecting the route name instead of the route address/mask. This is fine if you know the route names, but I haven't yet found an easy way to remove routes solely based on the routing information in the new interface. def del_tmmRoutes(obj, filename): routefile = open(filename, 'r') headers = routefile.readline().strip().split(',') stored_rows = [] for line in routefile: route = line.strip().split(',') stored_rows.append(dict(zip(headers, route))) for route in stored_rows: obj.delete_static_route(routes = [route]) print "Deleting Route %s/%s" % (route['destination'], route['netmask']) The Add Route Function This function just needed a little updating for the change from the add_static_route method to the create_static_route method. Now that routes require names, I had to account for the slice of headers/data I took from the routes in the text file. Structurally there were no changes. One thing I don't yet have working in the new interface is the reject route, so I've removed that code from the function displayed below, though the get routes function below will still display any reject routes in the system. def add_tmmRoutes(obj, filename, pool, vlan): pools = pool.get_list() vlans = vlan.get_list() routefile = open(filename, 'r') headers = routefile.readline().strip().split(',') rt_hdrs = headers[:1] dest_hdrs = headers[1:3] atr_hdrs = ['gateway','pool_name','vlan_name'] rts = [] dests = [] atrs = [] for line in routefile: ln = line.strip().split(',') rt_name = ln[:1] dest = ln[1:3] atr = ln[-2:] if atr[0] == 'pool': if atr[1] in pools: atrs.append(dict(zip(atr_hdrs, ['',atr[1],'']))) dests.append(dict(zip(dest_hdrs, dest))) rts.append(rt_name) 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]]))) dests.append(dict(zip(dest_hdrs, dest))) rts.append(rt_name) else: print "Vlan ", atr[1], " does not exist" elif atr[0] == 'gateway': atrs.append(dict(zip(atr_hdrs, [atr[1],'','']))) dests.append(dict(zip(dest_hdrs, dest))) rts.append(rt_name) combined = zip(rts, dests, atrs) for x in combined: xl = list(x) obj.create_static_route(routes = xl[0], destinations = [xl[1]], attributes = [xl[2]]) The IP Sorting Function This function isn't necessary and can be removed if desired, I just included it to sort all the routes properly. The only update here was to change the data reference (i[2]['address']) in the list comprehension. def sort_ip_dict(ip_list): from IPy import IP ipl = [ (IP(i[2]['address']).int(), i) for i in ip_list] ipl.sort() return [ip[1] for ip in ipl] The Get Routes Function In this function I had to update a couple of the methods specific to the RouteTableV2 interface, work with some changing data types between the ZSI (pycontrol) and suds (bigsuds) libraries. I updated the output a little as well. def get_tmmRoutes(obj): try: tmmStatic = obj.get_static_route_list() tmmRtType = obj.get_static_route_type(routes = tmmStatic) tmmRtDest = obj.get_static_route_destination(routes = tmmStatic) except: "Unable to fetch route information - check trace log" combined = zip(tmmStatic, tmmRtType, tmmRtDest) combined = [list(a) for a in combined] ldict_gw_ip = [] ldict_gw_pool = [] ldict_gw_vlan = [] ldict_gw_reject = [] for x in combined: if x[1] == 'ROUTE_TYPE_GATEWAY': x.append(obj.get_static_route_gateway(routes = [x[0]])[0]) ldict_gw_ip.append(x) if x[1] == 'ROUTE_TYPE_POOL': x.append(obj.get_static_route_pool(routes = [x[0]])[0]) ldict_gw_pool.append(x) if x[1] == 'ROUTE_TYPE_INTERFACE': x.append(obj.get_static_route_vlan(routes = [x[0]])[0]) ldict_gw_vlan.append(x) if x[1] == 'ROUTE_TYPE_REJECT': ldict_gw_reject.append(x) gw_ip = sort_ip_dict(ldict_gw_ip) gw_pool = sort_ip_dict(ldict_gw_pool) gw_vlan = sort_ip_dict(ldict_gw_vlan) gw_reject = sort_ip_dict(ldict_gw_reject) print "\n"*2 print "TMM IP Routes: (Name: Net/Mask -> Gateway IP)" for x in gw_ip: print "\t%s: %s/%s -> %s" % (x[0], x[2]['address'], x[2]['netmask'], x[3]) print "\n"*2 print "TMM Pool Routes: (Name: Net/Mask -> Gateway Pool)" for x in gw_pool: print "\t%s: %s/%s -> %s" % (x[0], x[2]['address'], x[2]['netmask'], x[3]) print "\n"*2 print "TMM Vlan Routes: (Name: Net/Mask -> Gateway Vlan)" for x in gw_vlan: print "\t%s: %s/%s -> %s" % (x[0], x[2]['address'], x[2]['netmask'], x[3]) print "\n"*2 print "TMM Rejected Routes: (Name: Net/Mask)" for x in gw_reject: print "\t%s: %s/%s" % (x[0], x[2]['address'], x[2]['netmask']) The Route File Formats When adding/removing routes, the following file formats are necessary to work with the script. ### Add Routes File Format ### name,address,netmask,route_type,gateway /Common/r5,172.16.1.0,255.255.255.0,pool,/Common/testpool /Common/r6,172.16.2.0,255.255.255.0,vlan,/Common/vmnet3 /Common/r7,172.16.3.0,255.255.255.0,gateway,10.10.10.1 /Common/r8,172.16.4.0,255.255.255.0,gateway,10.10.10.1 ### Delete Routes File Format ### destination,netmask 172.16.1.0,255.255.255.0 172.16.2.0,255.255.255.0 172.16.3.0,255.255.255.0 172.16.4.0,255.255.255.0 The Test Now that the script and the test files are prepared, let's take this for a spin! First, I'll run this without file arguments. C:\>python getRoutes.py -s 192.168.6.5 -u admin admin, enter your Password: TMM IP Routes: (Name: Net/Mask -> Gateway IP) /Common/r.default: 0.0.0.0/0.0.0.0 -> 10.10.10.1 /Common/r1: 65.23.5.88/255.255.255.248 -> 10.10.10.1 /Common/r2: 192.32.32.0/255.255.255.0 -> 10.10.10.1 TMM Pool Routes: (Name: Net/Mask -> Gateway Pool) TMM Vlan Routes: (Name: Net/Mask -> Gateway Vlan) TMM Rejected Routes: (Name: Net/Mask) Now, I'll add some routes (same as shown above in the formats section.) C:\>python getRoutes.py -s 192.168.6.5 -u admin -a routes admin, enter your Password: TMM IP Routes: (Name: Net/Mask -> Gateway IP) /Common/r.default: 0.0.0.0/0.0.0.0 -> 10.10.10.1 /Common/r1: 65.23.5.88/255.255.255.248 -> 10.10.10.1 /Common/r7: 172.16.3.0/255.255.255.0 -> 10.10.10.1 /Common/r8: 172.16.4.0/255.255.255.0 -> 10.10.10.1 /Common/r2: 192.32.32.0/255.255.255.0 -> 10.10.10.1 TMM Pool Routes: (Name: Net/Mask -> Gateway Pool) /Common/r5: 172.16.1.0/255.255.255.0 -> /Common/testpool TMM Vlan Routes: (Name: Net/Mask -> Gateway Vlan) /Common/r6: 172.16.2.0/255.255.255.0 -> /Common/vmnet3 TMM Rejected Routes: (Name: Net/Mask) You can see that routes r5-r8 were added to the system. Now, I'll delete them. C:\>python getRoutes.py -s 192.168.6.5 -u admin -d routedel admin, enter your Password: Deleting Route 172.16.1.0/255.255.255.0 Deleting Route 172.16.2.0/255.255.255.0 Deleting Route 172.16.3.0/255.255.255.0 Deleting Route 172.16.4.0/255.255.255.0 TMM IP Routes: (Name: Net/Mask -> Gateway IP) /Common/r.default: 0.0.0.0/0.0.0.0 -> 10.10.10.1 /Common/r1: 65.23.5.88/255.255.255.248 -> 10.10.10.1 /Common/r2: 192.32.32.0/255.255.255.0 -> 10.10.10.1 TMM Pool Routes: (Name: Net/Mask -> Gateway Pool) TMM Vlan Routes: (Name: Net/Mask -> Gateway Vlan) TMM Rejected Routes: (Name: Net/Mask) Conclusion Hopefully this was a useful exercise in converting pycontrol code to bigsuds. Looks like i have my work cut out for me in converting the rest of the codeshare! This example in full is in the iControl codeshare.287Views0likes0Comments