A change is as good as a ReST - UNetLab and the API


Hit a bit of a hiccup with my CCIE Security lab topology. It got wiped. Not happy to be honest, but you have to take the rough with the smooth sometimes.

So, now I have to rebuild it. From scratch. It's a ball ache, but needs to be done. I like to think I am a positive person, so let's try and put a positive spin on this. Can I recreate it in a more interesting way?

Maybe.

Since UNetLab 0.8.6-1 we have a ReST API (Representative State Transfer). Now in the interest of full disclosure I should point out that I know fuck-all about ReST - apart from the fact that it's supported in ASAs, and mans that you can call functions via a URL - so, can I recreate some of the CCIE lab using ReST?

First of all, where is the api? It's a file under the html folder - which makes sense as we need to access it through a browser:
root@unl01:~# ls /opt/unetlab/html/
api.php  configs  favicon.ico  files  images  includes  templates  themes
root@unl01:~#
Looking through the api.php file (too much to reprint here), there are some quick wins to get us started. We can list our folders, using the URL: "http://<unetlab server>/api/folders/" (note that my IP is 192.168.0.16 and that I have moved the output around for ease of reading):
{"code":200,"status":"success","message":"Successfully listed path (60007).",
"data":{"folders":[
{"name":"802101","path":"\/802101"},
{"name":"CCNA","path":"\/CCNA"},
{"name":"CheckPoint","path":"\/CheckPoint"},
{"name":"RS","path":"\/RS"},
{"name":"Security LAB","path":"\/Security LAB"},
{"name":"SP LAB","path":"\/SP LAB"},
{"name":"Test","path":"\/Test"},
{"name":"Thunderhead","path":"\/Thunderhead"}]
,"labs":[
{"file":"11routers.unl","path":"\/11routers.unl"},
{"file":"Active_ASA_test.unl","path":"\/Active_ASA_test.unl"},
{"file":"DMVPN.unl","path":"\/DMVPN.unl"},
{"file":"sdf-test.unl","path":"\/sdf-test.unl"},
{"file":"sdf-Win10-UCS.unl","path":"\/sdf-Win10-UCS.unl"},
{"file":"Test Lab custom topology.unl","path":"\/Test Lab custom topology.unl"}]}}
I started to recreate the Security topology, so already have the .unl file created, and it's in the 802101 directory. We can look at the contents of this by changing the URL to "http://192.168.0.16/api/folders/802101":
{"code":200,"status":"success","message":"Successfully listed path (60007)."
,"data":
{"folders":[{"name":"..","path":"\/"}],"labs":[
{"file":"BGP for Cisco Networks.unl","path":"\/802101\/BGP for Cisco Networks.unl"},
{"file":"CCIE Security.unl","path":"\/802101\/CCIE Security.unl"},
{"file":"MPLS for Cisco Networks.unl","path":"\/802101\/MPLS for Cisco Networks.unl"},
{"file":"VPNs and NAT for Cisco Networks.unl","path":"\/802101\/VPNs and NAT for Cisco Networks.unl"}]}}
OK, getting nearer.

Now, what happens if we look at this file via the api, by calling the URL "http://192.168.0.16/api/labs/802101/CCIE%20Security.unl":
{"code":200,"status":"success","message":"Lab has been loaded (60020)."
,"data":{"author":"Stuart Fordham","description":"","body":"",
"filename":"CCIE Security.unl","id":"4dd7e0ac-cd86-4ab7-aab8-dfc8744afb7a",
"name":"CCIE Security","version":"1"}}
OK, we can drill down. can we see if we have any existing objects?

Using the API to configure UNetLab

Adding nodes isn't quite as easy as the above examples. From the api.php code, the important bit looks to be:
 
} else if (preg_match('/^\/[A-Za-z0-9_+\/\\s-]+\.unl\/nodes$/', $s)) {
if (isset($p['count'])) {
 // count cannot be set from API
  unset($p['count']);
 }
 $output = apiAddLabNode($lab, $p, $o);
So, we need to pass three variables, the lab, which should already be in the URL, p, and o. These then get passed to another php file (api_nodes.php). So, what are p and o?
 $event = json_decode($app -> request() -> getBody());
 $p = json_decode(json_encode($event), True); // Reading options from POST/PUT
 $s = '/'.implode('/', $path);
 $o = False;
So after a bit of playing around I had to have a rethink. This should all be done using CURL commands, it'll make it much more programmable. So I headed down that route. Because this entails leaving the browser, we lose the fact that we are logged in, so need to log in again. After a could of hours blindly bashing away at the keyboard, I found this excellent guide to ReST: https://docs.phalconphp.com/en/latest/reference/tutorial-rest.html. Give it a read, it's very good. This made life much easier, and I was able to log in. More importantly, I got a good idea of the syntax, which is, you know, kind of important:
Stuarts-MacBook-Pro:~ stuart$ curl -i -X POST -d '{"username":"admin","password":"unl"}' http://192.168.0.16/api/auth/login
HTTP/1.1 200 OK
Date: Sun, 13 Mar 2016 19:38:46 GMT
Server: Apache/2.4.7 (Ubuntu)
X-Powered-By: PHP/5.5.9-1ubuntu4.14
X-Powered-By: Unified Networking Lab API
Cache-Control: post-check=0, pre-check=0
Pragma: no-cache
Set-Cookie: unetlab_session=4ad092ad-6ec1-471a-a24f-a9fab738583c; domain=192.168.0.16; path=/api/; expires=Mon, 13-Mar-3600 19:38:46 UTC
Content-Length: 67
Content-Type: application/json

{"code":200,"status":"success","message":"User logged in (90013)."}Stuarts-MacBook-Pro:~ stuart$ 
Stuarts-MacBook-Pro:~ stuart$ 
The problem here is that subsequent commands all failed due to authentication. Back to the Googles...

ReST Authentication and how to stay authenticated

I then found this web page: https://www.drupal.org/node/1795770. We can push the authentication data into a text file, and then call it later on. This works nicely. Note though that I am on a different machine now:
[root@spoon ~]# curl -X POST -i -H "Content-type: application/json" -c cookies.txt -X POST http://192.168.3.239/api/auth/login -d '
> {
> "username":"admin",
> "password":"unl"
> }
> '
HTTP/1.1 200 OK
Date: Mon, 14 Mar 2016 10:35:15 GMT
Server: Apache/2.4.7 (Ubuntu)
X-Powered-By: PHP/5.5.9-1ubuntu4.11
X-Powered-By: Unified Networking Lab API
Cache-Control: post-check=0, pre-check=0
Pragma: no-cache
Set-Cookie: unetlab_session=916665da-cfcb-4230-b5df-01b5f0bf78a9; domain=192.168.3.239; path=/api/; expires=Tue, 14-Mar-3600 10:35:15 UTC
Content-Length: 67
Content-Type: application/json

{"code":200,"status":"success","message":"User logged in (90013)."}
[root@spoon ~]#
We get authenticated, and the information is stored in a file called cookies.txt. We then us the -b command to call this file, if we don't use the -i flag then we get cleaner output:
[root@spoon ~]# curl -b cookies.txt -X GET http://192.168.3.239/api/folders/
{"code":200,"status":"success","message":"Successfully listed path (60007).","data":{"folders":[{"name":"CCNA_and_Beyond","path":"\/CCNA_and_Beyond"},{"name":"Pedro","path":"\/Pedro"},{"name":"RS","path":"\/RS"},{"name":"SP","path":"\/SP"}],"labs":[]}}You have new mail in /var/spool/mail/root
[root@spoon ~]#
OK, so this is a different machine, so I need to start from scratch. Let's try and create a folder.

Creating folders in UNetLab using ReST

Making sure that we pass the authentication data to UNL, we should be able to create a folder by specifying the name and the path (leaving the path empty if you want it at the top level):
[root@spoon ~]#  curl -X POST  -H "Content-type: application/json" -b cookies.txt  http://192.168.3.239/api/folders -d '
> {
> "name":"802101",
> "path":""
> }
> '
{"code":200,"status":"success","message":"Folder has been created (60014)."}
[root@spoon ~]#
So, does this appear in the folder list? Yes, it does!
[root@spoon ~]# curl -b cookies.txt -X GET http://192.168.3.239/api/folders/
{"code":200,"status":"success","message":"Successfully listed path (60007).","data":{"folders":[{"name":"802101","path":"\/802101"},{"name":"CCNA_and_Beyond","path":"\/CCNA_and_Beyond"},{"name":"Pedro","path":"\/Pedro"},{"name":"RS","path":"\/RS"},{"name":"SP","path":"\/SP"}],"labs":[]}}
[root@spoon ~]#
Wonderful, now lets try and create the CCIE Security lab!

Creating labs in UNetLab using the API

I won't show the misses I have had in trying to gauge the correct commands, but eventually I got it. The key is to look at the GUI and work out whats needed:
[root@spoon ~]#  curl -X POST  -H "Content-type: application/json" -b cookies.txt  http://192.168.3.239/api/labs -d '
> {
> "path":"/802101",
> "name":"CCIE Security",
> "version":"1",
> "author":"Stuart",
> "description":"CCIE"
> }
> '
{"code":200,"status":"success","message":"Lab has been created (60019)."}
[root@spoon ~]#
We can confirm this from the GUI - but note that this will mean that we have to authenticate again (might be worthwhile setting up different accounts for the API use, and checking in the GUI to save time):


OK, so now we have the folder, and the lab created. Let's add an object!

Creating nodes in UNL using ReST

After a few trials I managed to come down to this:
curl -X POST  -H "Content-type: application/json" -b cookies.txt http://192.168.3.239/api/labs/802101/CCIE%20Security.unl/nodes -d '
 {
 "type":"iol",
 "template":"iol",
 "image":"L3-ADVENTERPRISEK9-M-15.5-2T.bin",
 "name":"R1",
 "icon":"Router.png",
 "nvram":"1024",
 "ram":"256",
 "ethernet":"1",
 "serial":"0",
 "delay":"0",
 "config":"Unconfigured"
 }
 '
Which got me this:
{"code":201,"status":"success","message":"Lab has been saved (60023)."}
We can even see it on the GUI:


OK, so now we can add nodes to our lab. let's add a network:

Creating Networks using ReST

[root@spoon ~]#  curl -X POST  -H "Content-type: application/json" -b cookies.txt http://192.168.3.239/api/labs/802101/CCIE%20Security.unl/networks -d '
 {
 "type":"bridge",
 "name":"Net1"
 }
 '
{"code":201,"status":"success","message":"Network has been added to the lab (60006)."}
[root@spoon ~]#
Now we can add the network to our interface:



So, with a bit of digging about, it's now pretty simple to create folders and labs, and to add nodes and networks to the labs.

It's taken me about half a day to get this far, but it opens up a world of possibilities. This means that it could be scripted, and maybe even be able to create a topology in something like Visio and then call a script to create the lab in UNetLab... at least that's the idea.

CCIE #49337, author of CCNA and Beyond, BGP for Cisco Networks, MPLS for Cisco Networks, VPNs and NAT for Cisco Networks.

Related Posts

Previous
Next Post »