Manage your GNS3 network labs programmatically with gns3fy

Overview

Much of the time invested as a Network Engineer is performing network simulations for reasons like studying for a certification, trying out a proof-of-concept or testing design patterns, or even validate configuration changes before pushing to production!

One of the best platforms out there for network device simulation is GNS3, which stands for Graphical Network Simulator-3. It is commonly implemented with a GNS3 server ,where the network nodes simulations, topology and projects are stored and processed, and a GUI application on the user computer to interact with it.

The GNS3 server provides a really good REST API that we can leverage for programability purposes.

In this post I will write about gns3fy, a python interface of the GNS3 server and its elements that is easy and reliable to use. Network engineers can use it for multiple purposes like creating scripts, use it in automated pipelines (CI/CD systems) or even local testing.

Installation and requirements

It can be installed using pip:

pip install gns3fy

You also need to have successful connection with the GNS3 server REST API where your project is located. In this example I will use a remote server:

  • Server URL: http://dev-gn3server
  • Port: 3080
  • Lab name (project in GNS3): test_lab

Main objects of the library

The library exports 4 main objects for you to interact with:

  • Gns3Connector: Is a connector type object that performs the main HTTP transactions and operations against the server REST API
  • Project: Interacts with a specific GNS3 project
  • Node: Interacts with a specific node of a project
  • Link: Interacts with a specific link of a project

For more information you can take a look at the API Reference - gns3fy

Connect to the server and collect the project

Ok, enough of introductions and let’s get down to it. First we need to define a connector object and a project object from it.

from gns3fy import Gns3Connector, Project, Node, Link

SERVER_URL = "http://dev-gns3server:3080"
# Define the connector object, by default its port is 3080
server = Gns3Connector(url=SERVER_URL)

# Verify connectivity by checking the server version
print(server.get_verion())
{'local': False, 'version': '2.2.0b4'}

# Now obtain a project from the server
lab = Project(name="test_lab", connector=server)
lab.get()

# Show some of its attributes
print(f"{lab.name}: {lab.id} -- Status {lab.status}")
# test_lab: 4b21dfb3-675a-4efa-8613-2f7fb32e76f -- Status: closed

Lets open it

lab.open()

print(lab.status)
# opened

Now we can interact with the topology inside it.

Turning up the nodes

You can verify the nodes inside a project under the attribute lab.nodes, which gives a list of the Node(s) objects available . There is also a handy method you can use to list a summary of nodes inside a project:

print(lab.nodes_summary())
# veos-1: stopped -- Console: 5028 -- ID: ea3a788d-51ee-4efb-a7aa-02db682999ac
# veos-2: stopped -- Console: 5030 -- ID: c3b87554-72b6-4c21-b7d4-0b4679f192e8

We have 2 nodes. Lets start them up with a delay of 30 seconds between them (this is usually good for large scenarios or devices that take up too much resources when booting up)

import time

for node in lab.nodes:
    node.start()
    time.sleep(30)

# Now check again the status of the nodes
lab.get_nodes()
print(lab.nodes_summary())
# veos-1: started -- Console: 5028 -- ID: ea3a788d-51ee-4efb-a7aa-02db682999ac
# veos-2: started -- Console: 5030 -- ID: c3b87554-72b6-4c21-b7d4-0b4679f192e8

Node properties

Each nodes and link on a project has its own properties and attributes. For example you can inspect an specific node and retrieve its properties:

from pprint import pprint

veos1 = lab.get_node("veos-1")
pprint(veos1.properties)
# {'adapter_type': 'e1000',
#  'adapters': 13,
#  'category': 'router',
#  'console_type': 'telnet',
#  'cpu_throttling': 0,
#  'cpus': 2,
#  'custom_adapters': [],
#  'first_port_name': 'Management1',
#  'hda_disk_image': 'vEOS-lab-4.21.5F.vmdk',
#  ...

# To verify its ports
pprint(veos1.ports)
# [{'adapter_number': 0,
#   'adapter_type': 'e1000',
#   'data_link_types': {'Ethernet': 'DLT_EN10MB'},
#   'link_type': 'ethernet',
#   'mac_address': '0c:53:2e:99:ac:00',
#   'name': 'Ethernet0',
#   'port_number': 0,
#   'short_name': 'e0'},
#  ...

Now let’s get a feel of how the links are set up in the project. For this we can use the links_summary method

print(lab.links_summary())
# veos-1: Ethernet1 ---- veos-2: Ethernet1

Ok, so let’s try creating another link between the nodes

lab.create_link("veos-1", "Ethernet1", "veos-2", "Ethernet3")
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-25-c5afeaa1b3bc> in <module>
----> 1 lab.create_link("veos-1", "Ethernet1", "veos-2", "Ethernet3")

~/miniconda3/envs/ansible-netsetup/lib/python3.7/site-packages/gns3fy/gns3fy.py in create_link(self, node_a, port_a, node_b, port_b)
   1446                 _matches.append(_l)
   1447         if _matches:
-> 1448             raise ValueError(f"At least one port is used, ID: {_matches[0].link_id}")
   1449
   1450         # Now create the link!

ValueError: At least one port is used, ID: ab81059b-b454-490a-914f-535a898a281c

Oops, I had a typo and port veos-1 Ethernet1 is already used. Let’s fix that

lab.create_link("veos-1", "Ethernet3", "veos-2", "Ethernet3")
# Created Link-ID: d583f011-6d52-4871-a37b-fcc5a44ee76b -- Type: ethernet

# Now lets verify all the links
lab.links_summary()
# veos-1: Ethernet1 ---- veos-2: Ethernet1
# veos-1: Ethernet3 ---- veos-2: Ethernet3

Great! we have successfully created a link on the project.

I hope you have seen how easy is to interact with the GNS3 server REST API using gns3fy!