Monday, September 30, 2013

A10 Networks aXAPI - Part 1 Introduction

I recently joined A10 Networks as Professional Services Engineer based in the Pacific Northwest. Of course, the first thing I looked for is how to use Python to automate configuration tasks on A10. A quick search shows that A10 has an aXAPI interface that utilizes REST Web services calls to make configuration changes. More information can be found here, http://www.a10networks.com/products/axseries-aXAPI.php.

Here is a quick illustration on how it works:

1. Get a session ID via HTTPS call with username and password.
2. Construct your HTTPS post body in the format specified per guide (XML or JSon supported).
3. Make POST request to the device.

Figure 1. Source http://www.a10networks.com/products/axseries-aXAPI.php.



As I was going thru some simple exercise to do some load balancing, I tried to convert that into simple Python. Basically this is what I wanted to do: 

1. Create two real servers, s1@10.0.1.128 and s2@10.0.1.129. 
2. Create a service group that binds the two real servers into a server farm. 
3. Create a virtual IP that is client facing and answers to port 80. 

Here are the corresponding Python code that maps to the steps above: 

Step 1. Get the Session ID
#!/usr/bin/env python
#
# v1, September 27, 2013
# by Eric Chou
#
# Reference: AX_aXAPI_Ref_v2-20121010.pdf
#
import httplib, json, urllib, urllib2
# Gets the session ID to host 172.31.31.121 (Student 12)
c = httplib.HTTPSConnection("172.31.31.121")
c.request("GET", "/services/rest/V2/?method=authenticate&username=admin&password=a10&format=json")
response = c.getresponse()
data = json.loads(response.read())
session_id = data['session_id']
print "Session Created. Session ID: " + session_id

Step 2. Construct the HTTP Post body

Note that I re-use the session ID because it has not timed out yet. I skip the repeated steps to create more because the only thing that is different is the HTTP body. Full script is at the end of the post. 
###############################
#  Step 1. Create Servers     #
###############################
# Create slb servers s1 10.0.2.128
# Construct HTTP URL and Post Body
post_body = json.dumps(
{"server":
         {
          "name": "s1",
          "host": "10.0.2.128",
          "health_monitor": "(default)",
          "port_list": [
           {"port_num": 80,
            "protocol": 2,}
          ],
          }  
}
)
url = "https://172.31.31.121/services/rest/V2/?&session_id=" + session_id + "&format=json&method=slb.server.create"
print "URL Created. URL: " + url + " body: " + post_body 
(repeat and rinse for all the necessary configuration tasks)

Step 3. Make the request 

# Making request
req = urllib2.Request(url, post_body)
rsp = urllib2.urlopen(req)
content = rsp.read()
print "Result: " + content

That is it! Simple, elegant, and repeatable with minimal effort. :)

Here is the device blank configuration before script: 

AX12#sh run | s slb
AX12#
AX12#

Here is the output after execution: 
>>> 
Session Created. Session ID: 69be80219842402d45f0488c52b782
URL Created. URL: https://172.31.31.121/services/rest/V2/?&session_id=69be80219842402d45f0488c52b782&format=json&method=slb.server.create body: {"server": {"host": "10.0.2.128", "name": "s1", "port_list": [{"protocol": 2, "port_num": 80}], "health_monitor": "(default)"}}
Result: {"response": {"status": "OK"}}
URL Created. URL: https://172.31.31.121/services/rest/V2/?&session_id=69be80219842402d45f0488c52b782&format=json&method=slb.server.create body: {"server": {"host": "10.0.2.129", "name": "s2", "port_list": [{"protocol": 2, "port_num": 80}], "health_monitor": "(default)"}}
Result: {"response": {"status": "OK"}}
URL Created. URL: https://172.31.31.121/services/rest/V2/?&session_id=69be80219842402d45f0488c52b782&format=json&method=slb.service_group.create body: {"service_group": {"protocol": 2, "name": "http", "member_list": [{"port": 80, "server": "s1"}, {"port": 80, "server": "s2"}], "health_monitor": "ping"}}
Result: {"response": {"status": "OK"}}
URL Created. URL: https://172.31.31.121/services/rest/V2/?&session_id=69be80219842402d45f0488c52b782&format=json&method=slb.virtual_server.create body: {"virtual_server": {"subnet": {"mask_len": 24, "address": "10.0.1.122"}, "vport_list": [{"service_group": "http", "protocol": 2, "port": 80}], "name": "vip1"}}
Result: {"response": {"status": "OK"}}
>>>

Device configuration after: 

AX12#sh run | s slb
slb server s1 10.0.2.128
   conn-limit 8000000 no-logging
   port 80  tcp
slb server s2 10.0.2.129
   conn-limit 8000000 no-logging
   port 80  tcp
slb service-group http tcp
    health-check ping
    member s1:80
    member s2:80
slb virtual-server vip1 10.0.1.122 /24
   port 80  tcp
      service-group http
AX12#

And now I can see the fantastic web page by making a request to the VIP serviced by s1: 


aXAPI is pretty cool. I will probably play with it more and post my progress here. 

Happy Coding! Leave me a comment on how you'd like to see aXAPI integration scripts. :)

Here is the full script: 

Note. As you can see, the next step to optimize would be to make the HTTP body construct a function to cut down the code. Also use multiprocess to thread would be nice too. 

#!/usr/bin/env python

#
# v1, September 27, 2013
# by Eric Chou
#
# Reference: AX_aXAPI_Ref_v2-20121010.pdf
#

import httplib, json, urllib, urllib2

# Gets the session ID to host 172.31.31.121 (Student 12)
c = httplib.HTTPSConnection("172.31.31.121")
c.request("GET", "/services/rest/V2/?method=authenticate&username=admin&password=a10&format=json")
response = c.getresponse()
data = json.loads(response.read())
session_id = data['session_id']
print "Session Created. Session ID: " + session_id

###############################
#  Step 1. Create Servers     #
###############################

# Create slb servers s1 10.0.2.128 
# Construct HTTP URL and Post Body
post_body = json.dumps(
{"server":
         {
          "name": "s1",
          "host": "10.0.2.128",
          "health_monitor": "(default)",
          "port_list": [
           {"port_num": 80,
            "protocol": 2,}
          ],
          }     
}
)
url = "https://172.31.31.121/services/rest/V2/?&session_id=" + session_id + "&format=json&method=slb.server.create"
print "URL Created. URL: " + url + " body: " + post_body 

# Making request 
req = urllib2.Request(url, post_body)
rsp = urllib2.urlopen(req)
content = rsp.read()
print "Result: " + content

# Create slb server s2 10.0.2.129
post_body = json.dumps(
{"server":
         {
          "name": "s2",
          "host": "10.0.2.129",
          "health_monitor": "(default)",
          "port_list": [
           {"port_num": 80,
            "protocol": 2,}
          ],
          }     
}
)
url = "https://172.31.31.121/services/rest/V2/?&session_id=" + session_id + "&format=json&method=slb.server.create"
print "URL Created. URL: " + url + " body: " + post_body 

# Making request 
req = urllib2.Request(url, post_body)
rsp = urllib2.urlopen(req)
content = rsp.read()
print "Result: " + content

####################################
#  Step 2. Create Service Group    #
####################################

# Create service group http with member server s1 and s2
post_body = json.dumps(
{"service_group":
         {
          "name": "http",
          "protocol": 2,
          "health_monitor": "ping",
          "member_list": [
           {"server": "s1",
            "port": 80,},
           {"server": "s2",
            "port": 80,},
          ],
          }     
}
)
url = "https://172.31.31.121/services/rest/V2/?&session_id=" + session_id + "&format=json&method=slb.service_group.create"
print "URL Created. URL: " + url + " body: " + post_body 

# Making request 
req = urllib2.Request(url, post_body)
rsp = urllib2.urlopen(req)
content = rsp.read()
print "Result: " + content

#####################################
#  Step 3. Create Virtual Server    #
#####################################

# Create virtual server
post_body = json.dumps(
{"virtual_server":
         {
          "name": "vip1",
          "subnet":
           {
            "address": "10.0.1.122",
            "mask_len": 24,
           },
          "vport_list": [
           {"protocol": 2,
            "port": 80,
            "service_group": "http",
            },
          ],
          }  
}
)
url = "https://172.31.31.121/services/rest/V2/?&session_id=" + session_id + "&format=json&method=slb.virtual_server.create"
print "URL Created. URL: " + url + " body: " + post_body 

# Making request 
req = urllib2.Request(url, post_body)
rsp = urllib2.urlopen(req)
content = rsp.read()
print "Result: " + content








1 comment:

  1. I want to point out that user Lukasa on Reddit,http://www.reddit.com/r/Python/comments/1njuk5/python_for_network_engineers/, (probably correctly) pointed out that the code can be cut considerably by using Request (http://docs.python-requests.org/en/latest/). I will need to check it out! Thanks!

    ReplyDelete