In this post we’ll look at how to create arbitrary topologies and push configuration to Nodes in UNetlab via REST SDK. We’ll conclude by extending our sample application to create and configure a 3-node topology and enable full connectivity between all nodes.
Extracting Node’s UUID
In the previous post we have learned how to create a Node. To perform further actions on it we need to know it’s UUID. According to HTTP specification
201 - Created response SHOULD return a
Location header with resource URI, which would contain resource UUID. However, UNetLab’s implementation does not return a Location header so we need to extract that information ourselves. To do that we’ll use the previously defined
.get_nodes() method which returns all attributes of all configured Nodes in the following format:
The best place to extract UUID would be when Node is being created. After the
Create request has been sent to a server we’ll send another
Read request and extract all attributes of a Node based on its name.
1 2 3 4 5 6 7 8 9 10 11
To extract data from the payload we need to call
.json() on the returned HTTP response and look for the
data key inside that JSON object. The returned value will contain all attributes including the UUID and access URL which we’ll use later. To help us find a Node object matching a name we’ll use a helper function defined below:
1 2 3 4 5
Needless to say that we MUST have unique names for all nodes otherwise it won’t be possible to do the matching. It’s quite a safe assumption to make in most cases however no built-in error checking will be performed by the REST SDK to prevent you from doing it.
Before we start connecting Nodes together we need to create a Network. As per the design, UnlNet will be a class holding a pointer to the UnlLab object which created it. The structure of the class will be very similar to UnlNode.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
Connecting Nodes to a network
Official Unetlab API guide is still under development and doesn’t specify how to connect a Node to a network. If you want to find out the syntax for this or any other unspecified API call you can always try that in a Web GUI while capturing traffic with Wireshark. That is how I’ve discovered that to connect a Node to a network we need to send an Update request with payload containing mapping between Node’s interface ID and Network ID.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
The ID of an interface “Ethernet x/y” of an IOU device can be easily calculated based on the formula
id = x + (y * 16) as described here. This will be accomplished with yet another helper function:
1 2 3
Connecting Nodes to each other
To create multi-access topologies we would need to maintain an internal mapping between Node’s interface and the network it’s attached to. However, if we assume that all links are point-to-point, we can not only simplify our implementation but also enable REST client to ignore the notion of a network all together. We’ll simply assume that when device A connects to B our implementation will create a network called
A_B in the background and connect both devices to it. This method will perform two separate REST calls and thus will return both responses in a tuple:
1 2 3 4 5 6 7 8
Assuming all links are point-to-point certainly decreases visibility of created networks and we would not be able to perform selective changes on them in the future. However it is a safe assumption to make for 99% of the networks that I’m dealing with.
Node Start, Stop and Delete
These simple actions can easily be coded using TDD. I will omit the actual implementation and simply provide unit tests for readers to exercise their TDD skills again.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
lab_cleanup() method is simply a shortcut to
stop_nodes() followed by
As always, link to full code is available at the end of this post.
Pushing configuration to Nodes
At this point of time UnetLab does not support configuration import so we’re stuck with the only access method available - telnet. To push configuration into the Node we’re gonna have to establish a telnet session to Node’s URI (which we’ve extracted earlier) and write all configuration into that session.
1 2 3 4 5
Another helper function
enable and appends
end to make configuration suitable for pasting into the new IOU device.
1 2 3 4 5 6 7 8 9
The biggest problem is that Nodes, when started, take some time to boot before we can access the CLI prompt. To overcome that I had to implement a dirty hack in a form of
send_and_wait() helper function that simulates pressing the
Enter button every 0.1 second until it sees a CLI prompt (either
1 2 3 4 5 6 7 8 9
Let’s hope that UNL team will implement config import soon so that we can get rid of this kludgy workaround.
Extending our sample app
At this stage we’ve got all the code to finish our sample app. The goal is to create and configure the following 3-node topology:
We’ll assume that all configs will be stored as text files under the
./config directory and will have device names as their filename. A helper function
read_file will read the contents of a configuration text file into a Python string.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
When you run this app for the first time, the lab with 3 nodes will be spun up and configured. When you get to the
PRESS ANY KEY prompt you can login into Web GUI and navigate to lab
test_1 and validate that all configs have been pushed and devices can ping each other’s loopbacks.
All code from this post can be found in my public repository on Github