diff --git a/.gitignore b/.gitignore index 894a44c..9b9bbd1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,27 @@ -# Byte-compiled / optimized / DLL files +# Compiled source # +################### +*.com +*.class +*.dll +*.exe +*.o +*.so __pycache__/ *.py[cod] *$py.class -# C extensions -*.so - -# Distribution / packaging +# Packages # +############ +# it's better to unpack these files and commit the raw source +# git has its own built in compression methods +*.7z +*.dmg +*.gz +*.iso +*.jar +*.rar +*.tar +*.zip .Python build/ develop-eggs/ @@ -20,24 +35,43 @@ parts/ sdist/ var/ wheels/ +pip-wheel-metadata/ +share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt +# Logs and databases # +###################### +*.log +*.sql +*.sqlite + +# OS generated files # +###################### +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db +*.retry +.vscode +.mypy_cache +.pytest_cache +__pycache__ +prof +.envFile +.env +.eapi.conf +.idea # Unit test / coverage reports htmlcov/ .tox/ +.nox/ .coverage .coverage.* .cache @@ -47,58 +81,13 @@ coverage.xml .hypothesis/ .pytest_cache/ -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - # Sphinx documentation docs/_build/ -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site +# MkDocs +site/ # mypy .mypy_cache/ +.dmypy.json +dmypy.json diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..031d992 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +# gns3fy Change History + +## 0.1.0 + +- Initial Push diff --git a/LICENSE b/LICENSE index 9a3b521..2732caa 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 netpanda +Copyright (c) 2019 David Flores Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 003ad35..4d3e701 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,129 @@ # gns3fy -Python wrapper around GNS3 Server API. +Python wrapper around [GNS3 Server API](http://api.gns3.net/en/2.2/index.html). + +Its main objective is to interact with the GNS3 server in a programatic way, so it can be integrated with the likes of Ansible, docker and scripts. + +## Install + +``` +pip install gns3fy +``` + +### Development version + +Use [poetry](https://github.com/sdispater/poetry) to install the package when cloning it. + +## How it works + +You can start the library and use the `Gns3Connector` object and the `Project` object. + +For example: + +```python +import gns3fy + +# Define the server object to establish the connection +gns3_server = gns3fy.Gns3Connector("http://:3080") + +# Define the lab you want to load and assign the server connector +lab = gns3fy.Project(name="API_TEST", connector=gns3_server) + +# Retrieve its information and display +lab.get() + +print(lab) +# Project(project_id='4b21dfb3-675a-4efa-8613-2f7fb32e76fe', name='API_TEST', status='opened', ... + +# Access the project attributes +print(f"Name: {lab.name} -- Status: {lab.status} -- Is auto_closed?: {lab.auto_close}") +# Name: API_TEST -- Status: closed -- Is auto_closed?: False + +# Open the project +lab.open() +print(lab.status) +# opened + +# Verify the stats +print(lab.stats) +# {'drawings': 0, 'links': 4, 'nodes': 6, 'snapshots': 0} + +# List the names and status of all the nodes in the project +for node in lab.nodes: + print(f"Node: {node.name} -- Node Type: {node.node_type} -- Status: {node.status}") +# Node: Ethernetswitch-1 -- Node Type: ethernet_switch -- Status: started +# ... +``` + +Take a look at the API documentation for complete information about the attributes retrieved. + +### Usage of Node and Link objects + +You have access to the `Node` and `Link` objects as well, this gives you the ability to start, stop, suspend the individual element in a GNS3 project. + +```python +from gns3fy import Node, Link, Gns3Connector + +PROJECT_ID = "" +server = Gns3Connector("http://:3080") + +alpine1 = Node(project_id=PROJECT_ID, name="alpine-1", connector=server) + +alpine1.get() +print(alpine1) +# Node(name='alpine-1', node_type='docker', node_directory= ... + +# And you can access the attributes the same way as the project +print(f"Name: {alpine1.name} -- Status: {alpine1.status} -- Console: {alpine1.console}") +# Name: alpine-1 -- Status: started -- Console: 5005 + +# Stop the node and start (you can just restart it as well) +alpine1.stop() +print(alpine1.status) +# stopped +alpine1.start() +print(alpine1.status) +# started + +# You can also see the Link objects assigned to this node +print(alpine1.links) +# [Link(link_id='4d9f1235-7fd1-466b-ad26-0b4b08beb778', link_type='ethernet', .... + +# And in the same way you can interact with a Link object +link1 = alpine1.links[0] +print(f"Link Type: {link1.link_type} -- Capturing?: {link1.capturing} -- Endpoints: {link1.nodes}") +# Link Type: ethernet -- Capturing?: False -- Endpoints: [{'adapter_number': 2, ... +``` + +### Bonus + +You also have some commodity methods like the `nodes_summary` and `links_summary`, that if used with a library like `tabulate` you can see the following: + +```python +... +from tabulate import tabulate + +nodes_summary = lab.nodes_summar(is_print=False) + +print( + tabulate(nodes_summary, headers=["Node", "Status", "Console Port", "ID"]) +) +# Node Status Console Port ID +# ---------------- -------- -------------- ------------------------------------ +# Ethernetswitch-1 started 5000 da28e1c0-9465-4f7c-b42c-49b2f4e1c64d +# IOU1 started 5001 de23a89a-aa1f-446a-a950-31d4bf98653c +# IOU2 started 5002 0d10d697-ef8d-40af-a4f3-fafe71f5458b +# vEOS-4.21.5F-1 started 5003 8283b923-df0e-4bc1-8199-be6fea40f500 +# alpine-1 started 5005 ef503c45-e998-499d-88fc-2765614b313e +# Cloud-1 started cde85a31-c97f-4551-9596-a3ed12c08498 + +links_summary = lab.links_summary(is_print=False) +print( + tabulate(links_summary, headers=["Node A", "Port A", "Node B", "Port B"]) +) +# Node A Port A Node B Port B +# -------------- ----------- ---------------- ----------- +# IOU1 Ethernet1/0 IOU2 Ethernet1/0 +# vEOS-4.21.5F-1 Management1 Ethernetswitch-1 Ethernet0 +# vEOS-4.21.5F-1 Ethernet1 alpine-1 eth0 +# Cloud-1 eth1 Ethernetswitch-1 Ethernet7 +``` diff --git a/gns3fy/__init__.py b/gns3fy/__init__.py new file mode 100644 index 0000000..78d8cae --- /dev/null +++ b/gns3fy/__init__.py @@ -0,0 +1,3 @@ +from .api import Gns3Connector, Project, Node, Link + +__all__ = ["Gns3Connector", "Project", "Node", "Link"] diff --git a/gns3fy/api.py b/gns3fy/api.py new file mode 100644 index 0000000..00947ef --- /dev/null +++ b/gns3fy/api.py @@ -0,0 +1,840 @@ +import time +import requests +from urllib.parse import urlencode +from requests import ConnectionError, ConnectTimeout, HTTPError +from dataclasses import field +from typing import Optional, Any, Dict, List +from pydantic import validator +from pydantic.dataclasses import dataclass + + +class Config: + validate_assignment = True + # TODO: Not really working.. Need to investigate more and possibly open an issue + extra = "ignore" + + +NODE_TYPES = [ + "cloud", + "nat", + "ethernet_hub", + "ethernet_switch", + "frame_relay_switch", + "atm_switch", + "docker", + "dynamips", + "vpcs", + "traceng", + "virtualbox", + "vmware", + "iou", + "qemu", +] + +CONSOLE_TYPES = [ + "vnc", + "telnet", + "http", + "https", + "spice", + "spice+agent", + "none", + "null", +] + +LINK_TYPES = ["ethernet", "serial"] + + +class Gns3Connector: + """ + Connector to be use for interaction against GNS3 server controller API. + + Attributes: + - `base_url`: url passed + api_version + - `user`: User used for authentication + - `cred`: Password used for authentication + - `verify`: Whether or not to verify SSL + - `api_calls`: Counter of amount of `http_calls` has been performed + - `session`: Requests Session object + + Methods: + - `http_call`: Main method to perform REST API calls + - `error_checker`: Performs basic HTTP code checking + - `get_projects`: Retrieves ALL the projects configured on a GNS3 server + """ + + def __init__(self, url=None, user=None, cred=None, verify=False, api_version=2): + requests.packages.urllib3.disable_warnings() + self.base_url = f"{url.strip('/')}/v{api_version}" + self.user = user + self.headers = {"Content-Type": "application/json"} + self.verify = verify + self.api_calls = 0 + + # Create session object + self.session = requests.Session() + self.session.headers["Accept"] = "application/json" + if self.user: + self.session.auth = (user, cred) + + def http_call( + self, + method, + url, + data=None, + json_data=None, + headers=None, + verify=False, + params=None, + ): + "Standard method to perform calls" + try: + if data: + _response = getattr(self.session, method.lower())( + url, + data=urlencode(data), + headers=headers, + params=params, + verify=verify, + ) + + elif json_data: + _response = getattr(self.session, method.lower())( + url, json=json_data, headers=headers, params=params, verify=verify + ) + + else: + _response = getattr(self.session, method.lower())( + url, headers=headers, params=params, verify=verify + ) + + time.sleep(0.5) + self.api_calls += 1 + + except (ConnectTimeout, ConnectionError) as err: + print( + f"[ERROR] Connection Error, could not perform {method}" + f" operation: {err}" + ) + return False + except HTTPError as err: + print( + f"[ERROR] An unknown error has been encountered: {err} -" + f" {_response.text}" + ) + return False + + return _response + + @staticmethod + def error_checker(response_obj): + err = f"[ERROR][{response_obj.status_code}]: {response_obj.text}" + return err if 400 <= response_obj.status_code <= 599 else False + + def get_projects(self): + "Returns the list of dictionaries of the projects on the server" + return self.http_call("get", url=f"{self.base_url}/projects").json() + + +@dataclass(config=Config) +class Link: + """ + GNS3 Link API object + + Attributes: + http://api.gns3.net/en/2.2/api/v2/controller/link/projectsprojectidlinks.html + + Methods: + - `get`: Retrieves the link information + - `delete`: Deletes the link + """ + + link_id: Optional[str] = None + link_type: Optional[str] = None + project_id: Optional[str] = None + suspend: Optional[bool] = None + nodes: Optional[List[Any]] = None + filters: Optional[Any] = None + capturing: Optional[bool] = None + capture_file_path: Optional[str] = None + capture_file_name: Optional[str] = None + capture_compute_id: Optional[str] = None + + connector: Optional[Any] = field(default=None, repr=False) + + @validator("link_type") + def valid_node_type(cls, value): + if value not in LINK_TYPES: + raise ValueError(f"Not a valid link_type - {value}") + return value + + def _update(self, data_dict): + for k, v in data_dict.items(): + if k in self.__dict__.keys(): + self.__setattr__(k, v) + + def _verify_before_action(self): + if not self.connector: + raise ValueError("Gns3Connector not assigned under 'connector'") + if not self.project_id: + raise ValueError("Need to submit project_id") + if not self.link_id: + raise ValueError("Need to submit link_id") + + def get(self): + """ + Retrieves the information from the link + """ + self._verify_before_action() + + _url = ( + f"{self.connector.base_url}/projects/{self.project_id}/links/{self.link_id}" + ) + _response = self.connector.http_call("get", _url) + _err = Gns3Connector.error_checker(_response) + if _err: + raise ValueError(f"{_err}") + + # Update object + self._update(_response.json()) + + def delete(self): + """ + Deletes a link from the project + """ + self._verify_before_action() + + _url = ( + f"{self.connector.base_url}/projects/{self.project_id}/links/{self.link_id}" + ) + + _response = self.connector.http_call("delete", _url) + _err = Gns3Connector.error_checker(_response) + if _err: + raise ValueError(f"{_err}") + + self.project_id = None + self.link_id = None + + +@dataclass(config=Config) +class Node: + """ + GNS3 Node API object. + + Attributes: + http://api.gns3.net/en/2.2/api/v2/controller/node/projectsprojectidnodes.html + + Methods: + - `get`: Retrieves the node information + - `delete`: Deletes the node + - `get_links`: Retrieves the links of the node + - `start`: Starts the node + - `stop`: Stops the node + - `suspend`: Suspends the node + - `reload`: Reloads the node + """ + + name: Optional[str] = None + project_id: Optional[str] = None + node_id: Optional[str] = None + compute_id: Optional[str] = None + node_type: Optional[str] = None + node_directory: Optional[str] = None + status: Optional[str] = None + ports: Optional[List] = None + port_name_format: Optional[str] = None + port_segment_size: Optional[int] = None + first_port_name: Optional[str] = None + locked: Optional[bool] = None + label: Optional[Any] = None + console: Optional[str] = None + console_host: Optional[str] = None + console_type: Optional[str] = None + console_auto_start: Optional[bool] = None + command_line: Optional[str] = None + custom_adapters: Optional[List[Any]] = None + height: Optional[int] = None + width: Optional[int] = None + symbol: Optional[str] = None + x: Optional[int] = None + y: Optional[int] = None + z: Optional[int] = None + template_id: Optional[str] = None + properties: Optional[Any] = None + + links: List[Link] = field(default_factory=list, repr=False) + connector: Optional[Any] = field(default=None, repr=False) + + @validator("node_type") + def valid_node_type(cls, value): + if value not in NODE_TYPES: + raise ValueError(f"Not a valid node_type - {value}") + return value + + @validator("console_type") + def valid_console_type(cls, value): + if value not in CONSOLE_TYPES: + raise ValueError(f"Not a valid console_type - {value}") + return value + + @validator("status") + def valid_status(cls, value): + if value not in ("stopped", "started", "suspended"): + raise ValueError(f"Not a valid status - {value}") + return value + + def _update(self, data_dict): + for k, v in data_dict.items(): + if k in self.__dict__.keys(): + self.__setattr__(k, v) + + def _verify_before_action(self): + if not self.connector: + raise ValueError("Gns3Connector not assigned under 'connector'") + if not self.project_id: + raise ValueError("Need to submit project_id") + if not self.node_id: + if not self.name: + raise ValueError("Need to either submit node_id or name") + + # Try to retrieve the node_id + _url = f"{self.connector.base_url}/projects/{self.project_id}/nodes" + _response = self.connector.http_call("get", _url) + _err = Gns3Connector.error_checker(_response) + if _err: + raise ValueError(f"{_err}") + + extracted = [node for node in _response.json() if node["name"] == self.name] + if len(extracted) > 1: + raise ValueError( + "Multiple nodes found with same name. Need to submit node_id" + ) + self.node_id = extracted[0]["node_id"] + + def get(self, get_links=True): + """ + Retrieves the node information. + + - `get_links`: When True is also retrieves the links of the node + """ + self._verify_before_action() + + _url = ( + f"{self.connector.base_url}/projects/{self.project_id}/nodes/{self.node_id}" + ) + _response = self.connector.http_call("get", _url) + _err = Gns3Connector.error_checker(_response) + if _err: + raise ValueError(f"{_err}") + + # Update object + self._update(_response.json()) + + if get_links: + self.get_links() + + def get_links(self): + """ + Retrieves the links of the respective node + """ + self._verify_before_action() + + _url = ( + f"{self.connector.base_url}/projects/{self.project_id}/nodes" + f"/{self.node_id}/links" + ) + _response = self.connector.http_call("get", _url) + _err = Gns3Connector.error_checker(_response) + if _err: + raise ValueError(f"{_err}") + + # Create the Link array but cleanup cache if there is one + if self.links: + self.links = [] + for _link in _response.json(): + self.links.append(Link(connector=self.connector, **_link)) + + def start(self): + """ + Starts the node + """ + self._verify_before_action() + + _url = ( + f"{self.connector.base_url}/projects/{self.project_id}/nodes" + f"/{self.node_id}/start" + ) + _response = self.connector.http_call("post", _url) + _err = Gns3Connector.error_checker(_response) + if _err: + raise ValueError(f"{_err}") + + # Update object or perform get if change was not reflected + if _response.json().get("status") == "started": + self._update(_response.json()) + else: + self.get() + + def stop(self): + """ + Stops the node + """ + self._verify_before_action() + + _url = ( + f"{self.connector.base_url}/projects/{self.project_id}/nodes" + f"/{self.node_id}/stop" + ) + _response = self.connector.http_call("post", _url) + _err = Gns3Connector.error_checker(_response) + if _err: + raise ValueError(f"{_err}") + + # Update object or perform get if change was not reflected + if _response.json().get("status") == "stopped": + self._update(_response.json()) + else: + self.get() + + def reload(self): + """ + Reloads the node + """ + self._verify_before_action() + + _url = ( + f"{self.connector.base_url}/projects/{self.project_id}/nodes" + f"/{self.node_id}/reload" + ) + _response = self.connector.http_call("post", _url) + _err = Gns3Connector.error_checker(_response) + if _err: + raise ValueError(f"{_err}") + + # Update object or perform get if change was not reflected + if _response.json().get("status") == "started": + self._update(_response.json()) + else: + self.get() + + def suspend(self): + """ + Suspends the node + """ + self._verify_before_action() + + _url = ( + f"{self.connector.base_url}/projects/{self.project_id}/nodes" + f"/{self.node_id}/suspend" + ) + _response = self.connector.http_call("post", _url) + _err = Gns3Connector.error_checker(_response) + if _err: + raise ValueError(f"{_err}") + + # Update object or perform get if change was not reflected + if _response.json().get("status") == "suspended": + self._update(_response.json()) + else: + self.get() + + def delete(self): + """ + Deletes the node from the project + """ + self._verify_before_action() + + _url = ( + f"{self.connector.base_url}/projects/{self.project_id}/nodes/{self.node_id}" + ) + + _response = self.connector.http_call("delete", _url) + _err = Gns3Connector.error_checker(_response) + if _err: + raise ValueError(f"{_err}") + + self.project_id = None + self.node_id = None + self.name = None + + +@dataclass(config=Config) +class Project: + """ + GNS3 Project API object + + Attributes: + http://api.gns3.net/en/2.2/api/v2/controller/project/projects.html + + Methods: + - `get`: Retrieves the project information + - `open`: Opens up a project + - `close`: Closes a project + - `update`: Updates a project instance + - `delete`: Deletes the project + - `get_nodes`: Retrieves the nodes of the project + - `get_stats`: Retrieves the stats of the project + - `get_links`: Retrieves the links of the project + - `start_nodes`: Starts ALL nodes inside the project + - `stop_nodes`: Stops ALL nodes inside the project + - `suspend_nodes`: Suspends ALL nodes inside the project + - `nodes_summary`: Shows summary of the nodes created inside the project + - `links_summary`: Shows summary of the links created inside the project + """ + + project_id: Optional[str] = None + name: Optional[str] = None + status: Optional[str] = None + path: Optional[str] = None + filename: Optional[str] = None + auto_start: Optional[bool] = None + auto_close: Optional[bool] = None + auto_open: Optional[bool] = None + drawing_grid_size: Optional[int] = None + grid_size: Optional[int] = None + scene_height: Optional[int] = None + scene_width: Optional[int] = None + show_grid: Optional[bool] = None + show_interface_labels: Optional[bool] = None + show_layers: Optional[bool] = None + snap_to_grid: Optional[bool] = None + supplier: Optional[Any] = None + variables: Optional[List] = None + zoom: Optional[int] = None + + stats: Optional[Dict[str, Any]] = None + nodes: List[Node] = field(default_factory=list, repr=False) + links: List[Link] = field(default_factory=list, repr=False) + connector: Optional[Any] = field(default=None, repr=False) + # There are more... but will develop them in due time + + @validator("status") + def valid_status(cls, value): + if value != "opened" and value != "closed": + raise ValueError("status must be 'opened' or 'closed'") + return value + + def _update(self, data_dict): + for k, v in data_dict.items(): + if k in self.__dict__.keys(): + self.__setattr__(k, v) + + def _verify_before_action(self): + if not self.connector: + raise ValueError("Gns3Connector not assigned under 'connector'") + if not self.project_id: + raise ValueError("Need to submit project_id") + + def get(self, get_links=True, get_nodes=True, get_stats=True): + """ + Retrieves the projects information. + + - `get_links`: When true it also queries for the links inside the project + - `get_nodes`: When true it also queries for the nodes inside the project + - `get_stats`: When true it also queries for the stats inside the project + """ + if not self.connector: + raise ValueError("Gns3Connector not assigned under 'connector'") + + # Get projects if no ID was provided by the name + if not self.project_id: + if not self.name: + raise ValueError("Need to submit either project_id or name") + _url = f"{self.connector.base_url}/projects" + # Get all projects and filter the respective project + _response = self.connector.http_call("get", _url) + _err = Gns3Connector.error_checker(_response) + if _err: + raise ValueError(f"{_err}") + + # Filter the respective project + for _project in _response.json(): + if _project.get("name") == self.name: + self.project_id = _project.get("project_id") + + # Get project + _url = f"{self.connector.base_url}/projects/{self.project_id}" + _response = self.connector.http_call("get", _url) + _err = Gns3Connector.error_checker(_response) + if _err: + raise ValueError(f"{_err}") + + # Update object + self._update(_response.json()) + + if get_stats: + self.get_stats() + if get_nodes: + self.get_nodes() + if get_links: + self.get_links() + + def update(self, **kwargs): + """ + Updates the project instance by passing the keyword arguments directly into the + body of the PUT method. + + Example: + lab.update(auto_close=True) + + This will update the project `auto_close` attribute to True + """ + self._verify_before_action() + + _url = f"{self.connector.base_url}/projects/{self.project_id}" + + # TODO: Verify that the passed kwargs are supported ones + _response = self.connector.http_call("put", _url, json_data=kwargs) + _err = Gns3Connector.error_checker(_response) + if _err: + raise ValueError(f"{_err}") + + # Update object + self._update(_response.json()) + + def delete(self): + """ + Deletes the project from the server. It DOES NOT DELETE THE OBJECT locally, it + justs sets the `project_id` and `name` to None + """ + self._verify_before_action() + + _url = f"{self.connector.base_url}/projects/{self.project_id}" + + _response = self.connector.http_call("delete", _url) + _err = Gns3Connector.error_checker(_response) + if _err: + raise ValueError(f"{_err}") + + self.project_id = None + self.name = None + + def close(self): + """ + Closes the project on the server. + """ + self._verify_before_action() + + _url = f"{self.connector.base_url}/projects/{self.project_id}/close" + + _response = self.connector.http_call("post", _url) + _err = Gns3Connector.error_checker(_response) + if _err: + raise ValueError(f"{_err}") + + # Update object + self._update(_response.json()) + + def open(self): + """ + Opens the project on the server. + """ + self._verify_before_action() + + _url = f"{self.connector.base_url}/projects/{self.project_id}/open" + + _response = self.connector.http_call("post", _url) + _err = Gns3Connector.error_checker(_response) + if _err: + raise ValueError(f"{_err}") + + # Update object + self._update(_response.json()) + + def get_stats(self): + """ + Retrieve the stats of the project + """ + self._verify_before_action() + + _url = f"{self.connector.base_url}/projects/{self.project_id}/stats" + + _response = self.connector.http_call("get", _url) + _err = Gns3Connector.error_checker(_response) + if _err: + raise ValueError(f"{_err}") + + # Update object + self.stats = _response.json() + + def get_nodes(self): + """ + Retrieve the nodes of the project + """ + self._verify_before_action() + + _url = f"{self.connector.base_url}/projects/{self.project_id}/nodes" + + _response = self.connector.http_call("get", _url) + _err = Gns3Connector.error_checker(_response) + if _err: + raise ValueError(f"{_err}") + + # Create the Nodes array but cleanup cache if there is one + if self.nodes: + self.nodes = [] + for _node in _response.json(): + _n = Node(connector=self.connector, **_node) + _n.project_id = self.project_id + self.nodes.append(_n) + + def get_links(self): + """ + Retrieve the links of the project + """ + self._verify_before_action() + + _url = f"{self.connector.base_url}/projects/{self.project_id}/links" + + _response = self.connector.http_call("get", _url) + _err = Gns3Connector.error_checker(_response) + if _err: + raise ValueError(f"{_err}") + + # Create the Nodes array but cleanup cache if there is one + if self.links: + self.links = [] + for _link in _response.json(): + _l = Link(connector=self.connector, **_link) + _l.project_id = self.project_id + self.links.append(_l) + + def start_nodes(self, poll_wait_time=5): + """ + Starts all the nodes inside the project. + + - `poll_wait_time` is used as a delay when performing the next query of the + nodes status. + """ + self._verify_before_action() + + _url = f"{self.connector.base_url}/projects/{self.project_id}/nodes/start" + + _response = self.connector.http_call("post", _url) + _err = Gns3Connector.error_checker(_response) + if _err: + raise ValueError(f"{_err}") + + # Update object + time.sleep(poll_wait_time) + self.get_nodes() + + def stop_nodes(self, poll_wait_time=5): + """ + Stops all the nodes inside the project. + + - `poll_wait_time` is used as a delay when performing the next query of the + nodes status. + """ + self._verify_before_action() + + _url = f"{self.connector.base_url}/projects/{self.project_id}/nodes/stop" + + _response = self.connector.http_call("post", _url) + _err = Gns3Connector.error_checker(_response) + if _err: + raise ValueError(f"{_err}") + + # Update object + time.sleep(poll_wait_time) + self.get_nodes() + + def reload_nodes(self, poll_wait_time=5): + """ + Reloads all the nodes inside the project. + + - `poll_wait_time` is used as a delay when performing the next query of the + nodes status. + """ + self._verify_before_action() + + _url = f"{self.connector.base_url}/projects/{self.project_id}/nodes/reload" + + _response = self.connector.http_call("post", _url) + _err = Gns3Connector.error_checker(_response) + if _err: + raise ValueError(f"{_err}") + + # Update object + time.sleep(poll_wait_time) + self.get_nodes() + + def suspend_nodes(self, poll_wait_time=5): + """ + Suspends all the nodes inside the project. + + - `poll_wait_time` is used as a delay when performing the next query of the + nodes status. + """ + self._verify_before_action() + + _url = f"{self.connector.base_url}/projects/{self.project_id}/nodes/suspend" + + _response = self.connector.http_call("post", _url) + _err = Gns3Connector.error_checker(_response) + if _err: + raise ValueError(f"{_err}") + + # Update object + time.sleep(poll_wait_time) + self.get_nodes() + + def nodes_summary(self, is_print=True): + """ + Returns a summary of the nodes insode the project. If `is_print` is False, it + will return a list of tuples like: + + [(node_name, node_status, node_console, node_id) ...] + """ + if not self.nodes: + self.get_nodes() + + _nodes_summary = [] + for _n in self.nodes: + if is_print: + print( + f"{_n.name}: {_n.status} -- Console: {_n.console} -- " + f"ID: {_n.node_id}" + ) + _nodes_summary.append((_n.name, _n.status, _n.console, _n.node_id)) + + return _nodes_summary if not is_print else None + + def links_summary(self, is_print=True): + """ + Returns a summary of the links insode the project. If `is_print` is False, it + will return a list of tuples like: + + [(node_a, port_a, node_b, port_b) ...] + """ + if not self.nodes: + self.get_nodes() + if not self.links: + self.get_links() + + _links_summary = [] + for _l in self.links: + _side_a = _l.nodes[0] + _side_b = _l.nodes[1] + _node_a = [x for x in self.nodes if x.node_id == _side_a["node_id"]][0] + _port_a = [ + x["name"] + for x in _node_a.ports + if x["port_number"] == _side_a["port_number"] + and x["adapter_number"] == _side_a["adapter_number"] + ][0] + _node_b = [x for x in self.nodes if x.node_id == _side_b["node_id"]][0] + _port_b = [ + x["name"] + for x in _node_b.ports + if x["port_number"] == _side_b["port_number"] + and x["adapter_number"] == _side_b["adapter_number"] + ][0] + endpoint_a = f"{_node_a.name}: {_port_a}" + endpoint_b = f"{_node_b.name}: {_port_b}" + if is_print: + print(f"{endpoint_a} ---- {endpoint_b}") + _links_summary.append((_node_a.name, _port_a, _node_b.name, _port_b)) + + return _links_summary if not is_print else None diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..f25932a --- /dev/null +++ b/poetry.lock @@ -0,0 +1,446 @@ +[[package]] +category = "dev" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +name = "appdirs" +optional = false +python-versions = "*" +version = "1.4.3" + +[[package]] +category = "dev" +description = "Disable App Nap on OS X 10.9" +marker = "sys_platform == \"darwin\"" +name = "appnope" +optional = false +python-versions = "*" +version = "0.1.0" + +[[package]] +category = "dev" +description = "Atomic file writes." +name = "atomicwrites" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.3.0" + +[[package]] +category = "dev" +description = "Classes Without Boilerplate" +name = "attrs" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "19.1.0" + +[[package]] +category = "dev" +description = "Specifications for callback functions passed in to an API" +name = "backcall" +optional = false +python-versions = "*" +version = "0.1.0" + +[[package]] +category = "dev" +description = "The uncompromising code formatter." +name = "black" +optional = false +python-versions = ">=3.6" +version = "18.9b0" + +[package.dependencies] +appdirs = "*" +attrs = ">=17.4.0" +click = ">=6.5" +toml = ">=0.9.4" + +[[package]] +category = "main" +description = "Python package for providing Mozilla's CA Bundle." +name = "certifi" +optional = false +python-versions = "*" +version = "2019.6.16" + +[[package]] +category = "main" +description = "Universal encoding detector for Python 2 and 3" +name = "chardet" +optional = false +python-versions = "*" +version = "3.0.4" + +[[package]] +category = "dev" +description = "Composable command line interface toolkit" +name = "click" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "7.0" + +[[package]] +category = "dev" +description = "Cross-platform colored terminal text." +marker = "sys_platform == \"win32\"" +name = "colorama" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "0.4.1" + +[[package]] +category = "dev" +description = "Better living through Python with decorators" +name = "decorator" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*" +version = "4.4.0" + +[[package]] +category = "dev" +description = "Discover and load entry points from installed packages." +name = "entrypoints" +optional = false +python-versions = ">=2.7" +version = "0.3" + +[[package]] +category = "dev" +description = "the modular source code checker: pep8, pyflakes and co" +name = "flake8" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "3.7.8" + +[package.dependencies] +entrypoints = ">=0.3.0,<0.4.0" +mccabe = ">=0.6.0,<0.7.0" +pycodestyle = ">=2.5.0,<2.6.0" +pyflakes = ">=2.1.0,<2.2.0" + +[[package]] +category = "main" +description = "Internationalized Domain Names in Applications (IDNA)" +name = "idna" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "2.8" + +[[package]] +category = "dev" +description = "Read metadata from Python packages" +name = "importlib-metadata" +optional = false +python-versions = ">=2.7,!=3.0,!=3.1,!=3.2,!=3.3" +version = "0.19" + +[package.dependencies] +zipp = ">=0.5" + +[[package]] +category = "dev" +description = "IPython: Productive Interactive Computing" +name = "ipython" +optional = false +python-versions = ">=3.5" +version = "7.7.0" + +[package.dependencies] +appnope = "*" +backcall = "*" +colorama = "*" +decorator = "*" +jedi = ">=0.10" +pexpect = "*" +pickleshare = "*" +prompt-toolkit = ">=2.0.0,<2.1.0" +pygments = "*" +setuptools = ">=18.5" +traitlets = ">=4.2" + +[[package]] +category = "dev" +description = "Vestigial utilities from IPython" +name = "ipython-genutils" +optional = false +python-versions = "*" +version = "0.2.0" + +[[package]] +category = "dev" +description = "An autocompletion tool for Python that can be used for text editors." +name = "jedi" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "0.14.1" + +[package.dependencies] +parso = ">=0.5.0" + +[[package]] +category = "dev" +description = "McCabe checker, plugin for flake8" +name = "mccabe" +optional = false +python-versions = "*" +version = "0.6.1" + +[[package]] +category = "dev" +description = "More routines for operating on iterables, beyond itertools" +name = "more-itertools" +optional = false +python-versions = ">=3.4" +version = "7.2.0" + +[[package]] +category = "dev" +description = "Core utilities for Python packages" +name = "packaging" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "19.1" + +[package.dependencies] +attrs = "*" +pyparsing = ">=2.0.2" +six = "*" + +[[package]] +category = "dev" +description = "A Python Parser" +name = "parso" +optional = false +python-versions = "*" +version = "0.5.1" + +[[package]] +category = "dev" +description = "Pexpect allows easy control of interactive console applications." +marker = "sys_platform != \"win32\"" +name = "pexpect" +optional = false +python-versions = "*" +version = "4.7.0" + +[package.dependencies] +ptyprocess = ">=0.5" + +[[package]] +category = "dev" +description = "Tiny 'shelve'-like database with concurrency support" +name = "pickleshare" +optional = false +python-versions = "*" +version = "0.7.5" + +[[package]] +category = "dev" +description = "plugin and hook calling mechanisms for python" +name = "pluggy" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "0.12.0" + +[package.dependencies] +importlib-metadata = ">=0.12" + +[[package]] +category = "dev" +description = "Library for building powerful interactive command lines in Python" +name = "prompt-toolkit" +optional = false +python-versions = "*" +version = "2.0.9" + +[package.dependencies] +six = ">=1.9.0" +wcwidth = "*" + +[[package]] +category = "dev" +description = "Run a subprocess in a pseudo terminal" +marker = "sys_platform != \"win32\"" +name = "ptyprocess" +optional = false +python-versions = "*" +version = "0.6.0" + +[[package]] +category = "dev" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +name = "py" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.8.0" + +[[package]] +category = "dev" +description = "Python style guide checker" +name = "pycodestyle" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "2.5.0" + +[[package]] +category = "main" +description = "Data validation and settings management using python 3.6 type hinting" +name = "pydantic" +optional = false +python-versions = ">=3.6" +version = "0.31" + +[[package]] +category = "dev" +description = "passive checker of Python programs" +name = "pyflakes" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "2.1.1" + +[[package]] +category = "dev" +description = "Pygments is a syntax highlighting package written in Python." +name = "pygments" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "2.4.2" + +[[package]] +category = "dev" +description = "Python parsing module" +name = "pyparsing" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +version = "2.4.2" + +[[package]] +category = "dev" +description = "pytest: simple powerful testing with Python" +name = "pytest" +optional = false +python-versions = ">=3.5" +version = "5.0.1" + +[package.dependencies] +atomicwrites = ">=1.0" +attrs = ">=17.4.0" +colorama = "*" +importlib-metadata = ">=0.12" +more-itertools = ">=4.0.0" +packaging = "*" +pluggy = ">=0.12,<1.0" +py = ">=1.5.0" +wcwidth = "*" + +[[package]] +category = "main" +description = "Python HTTP for Humans." +name = "requests" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "2.22.0" + +[package.dependencies] +certifi = ">=2017.4.17" +chardet = ">=3.0.2,<3.1.0" +idna = ">=2.5,<2.9" +urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" + +[[package]] +category = "dev" +description = "Python 2 and 3 compatibility utilities" +name = "six" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*" +version = "1.12.0" + +[[package]] +category = "dev" +description = "Python Library for Tom's Obvious, Minimal Language" +name = "toml" +optional = false +python-versions = "*" +version = "0.10.0" + +[[package]] +category = "dev" +description = "Traitlets Python config system" +name = "traitlets" +optional = false +python-versions = "*" +version = "4.3.2" + +[package.dependencies] +decorator = "*" +ipython-genutils = "*" +six = "*" + +[[package]] +category = "main" +description = "HTTP library with thread-safe connection pooling, file post, and more." +name = "urllib3" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4" +version = "1.25.3" + +[[package]] +category = "dev" +description = "Measures number of Terminal column cells of wide-character codes" +name = "wcwidth" +optional = false +python-versions = "*" +version = "0.1.7" + +[[package]] +category = "dev" +description = "Backport of pathlib-compatible object wrapper for zip files" +name = "zipp" +optional = false +python-versions = ">=2.7" +version = "0.5.2" + +[metadata] +content-hash = "f69e7b141942052c2f3891313a0a7e607f34ae74eae2cf527e86c9a9c0c0e0dd" +python-versions = "^3.7" + +[metadata.hashes] +appdirs = ["9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92", "d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"] +appnope = ["5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0", "8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"] +atomicwrites = ["03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4", "75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"] +attrs = ["69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", "f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"] +backcall = ["38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4", "bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2"] +black = ["817243426042db1d36617910df579a54f1afd659adb96fc5032fcf4b36209739", "e030a9a28f542debc08acceb273f228ac422798e5215ba2a791a6ddeaaca22a5"] +certifi = ["046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939", "945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695"] +chardet = ["84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", "fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"] +click = ["2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", "5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"] +colorama = ["05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", "f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"] +decorator = ["86156361c50488b84a3f148056ea716ca587df2f0de1d34750d35c21312725de", "f069f3a01830ca754ba5258fde2278454a0b5b79e0d7f5c13b3b97e57d4acff6"] +entrypoints = ["589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19", "c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"] +flake8 = ["19241c1cbc971b9962473e4438a2ca19749a7dd002dd1a946eaba171b4114548", "8e9dfa3cecb2400b3738a42c54c3043e821682b9c840b0448c0503f781130696"] +idna = ["c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", "ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"] +importlib-metadata = ["23d3d873e008a513952355379d93cbcab874c58f4f034ff657c7a87422fa64e8", "80d2de76188eabfbfcf27e6a37342c2827801e59c4cc14b0371c56fed43820e3"] +ipython = ["1d3a1692921e932751bc1a1f7bb96dc38671eeefdc66ed33ee4cbc57e92a410e", "537cd0176ff6abd06ef3e23f2d0c4c2c8a4d9277b7451544c6cbf56d1c79a83d"] +ipython-genutils = ["72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8", "eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"] +jedi = ["53c850f1a7d3cfcd306cc513e2450a54bdf5cacd7604b74e42dd1f0758eaaf36", "e07457174ef7cb2342ff94fa56484fe41cec7ef69b0059f01d3f812379cb6f7c"] +mccabe = ["ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", "dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"] +more-itertools = ["409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832", "92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4"] +packaging = ["a7ac867b97fdc07ee80a8058fe4435ccd274ecc3b0ed61d852d7d53055528cf9", "c491ca87294da7cc01902edbe30a5bc6c4c28172b5138ab4e4aa1b9d7bfaeafe"] +parso = ["63854233e1fadb5da97f2744b6b24346d2750b85965e7e399bec1620232797dc", "666b0ee4a7a1220f65d367617f2cd3ffddff3e205f3f16a0284df30e774c2a9c"] +pexpect = ["2094eefdfcf37a1fdbfb9aa090862c1a4878e5c7e0e7e7088bdb511c558e5cd1", "9e2c1fd0e6ee3a49b28f95d4b33bc389c89b20af6a1255906e90ff1262ce62eb"] +pickleshare = ["87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca", "9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"] +pluggy = ["0825a152ac059776623854c1543d65a4ad408eb3d33ee114dff91e57ec6ae6fc", "b9817417e95936bf75d85d3f8767f7df6cdde751fc40aed3bb3074cbcb77757c"] +prompt-toolkit = ["11adf3389a996a6d45cc277580d0d53e8a5afd281d0c9ec71b28e6f121463780", "2519ad1d8038fd5fc8e770362237ad0364d16a7650fb5724af6997ed5515e3c1", "977c6583ae813a37dc1c2e1b715892461fcbdaa57f6fc62f33a528c4886c8f55"] +ptyprocess = ["923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0", "d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"] +py = ["64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa", "dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"] +pycodestyle = ["95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56", "e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"] +pydantic = ["1bd13f418ea2200b3b3782543f7cb9efca3ca2b6dc35dbc8090ce75e924a74e8", "2914429657a106b09e4e64d4470c493effad145bfcdd98567d431e3c59d2bff9", "29a941ebda15480624d3c970ffe099cedf6ca1f90748e0ae801626f337063bc6", "534f8d9f4b4c4d107774afff0da039c262e6e83c94c38ba42549b47a5344823d", "6d9448b92dc1ab15918c00ee0915eb50b950f671a92fc25a018e667f61613d75", "817bda425bcdeb664be40a27de9e071d42b59a4e631f69eeb610e249d9d73d5d"] +pyflakes = ["17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0", "d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2"] +pygments = ["71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127", "881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297"] +pyparsing = ["6f98a7b9397e206d78cc01df10131398f1c8b8510a2f4d97d9abd82e1aacdd80", "d9338df12903bbf5d65a0e4e87c2161968b10d2e489652bb47001d82a9b028b4"] +pytest = ["6ef6d06de77ce2961156013e9dff62f1b2688aa04d0dc244299fe7d67e09370d", "a736fed91c12681a7b34617c8fcefe39ea04599ca72c608751c31d89579a3f77"] +requests = ["11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", "9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"] +six = ["3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", "d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"] +toml = ["229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c", "235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e", "f1db651f9657708513243e61e6cc67d101a39bad662eaa9b5546f789338e07a3"] +traitlets = ["9c4bd2d267b7153df9152698efb1050a5d84982d3384a37b2c1f7723ba3e7835", "c6cb5e6f57c5a9bdaa40fa71ce7b4af30298fbab9ece9815b5d995ab6217c7d9"] +urllib3 = ["b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1", "dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232"] +wcwidth = ["3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e", "f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"] +zipp = ["4970c3758f4e89a7857a973b1e2a5d75bcdc47794442f2e2dd4fe8e0466e809a", "8a5712cfd3bb4248015eb3b0b3c54a5f6ee3f2425963ef2a0125b8bc40aafaec"] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..a11cd77 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,21 @@ +[tool.poetry] +name = "gns3fy" +version = "0.1.0" +description = "Python wrapper around GNS3 Server API" +authors = ["David Flores "] +license = "MIT" + +[tool.poetry.dependencies] +python = "^3.7" +requests = "^2.22" +pydantic = "^0.31.0" + +[tool.poetry.dev-dependencies] +ipython = "^7.7" +pytest = "^5.0" +flake8 = "^3.7" +black = {version = "^18.3-alpha.0",allows-prereleases = true} + +[build-system] +requires = ["poetry>=0.12"] +build-backend = "poetry.masonry.api" diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..4eedd61 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,4 @@ +import sys +import os + +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../gns3fy"))) diff --git a/tests/data/__init__.py b/tests/data/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/data/links.json b/tests/data/links.json new file mode 100644 index 0000000..a5def6d --- /dev/null +++ b/tests/data/links.json @@ -0,0 +1,211 @@ +[ + { + "capture_compute_id": null, + "capture_file_name": null, + "capture_file_path": null, + "capturing": false, + "filters": {}, + "link_id": "d7dd01d6-9577-4076-b7f2-911b231044f8", + "link_type": "ethernet", + "nodes": [ + { + "adapter_number": 0, + "label": { + "rotation": 0, + "style": "font-family: TypeWriter;font-size: 10.0;font-weight: bold;fill: #000000;fill-opacity: 1.0;", + "text": "e0/0", + "x": 69, + "y": 27 + }, + "node_id": "de23a89a-aa1f-446a-a950-31d4bf98653c", + "port_number": 0 + }, + { + "adapter_number": 0, + "label": { + "rotation": 0, + "style": "font-family: TypeWriter;font-size: 10.0;font-weight: bold;fill: #000000;fill-opacity: 1.0;", + "text": "e1", + "x": -4, + "y": 18 + }, + "node_id": "da28e1c0-9465-4f7c-b42c-49b2f4e1c64d", + "port_number": 1 + } + ], + "project_id": "4b21dfb3-675a-4efa-8613-2f7fb32e76fe", + "suspend": false + }, + { + "capture_compute_id": null, + "capture_file_name": null, + "capture_file_path": null, + "capturing": false, + "filters": {}, + "link_id": "cda8707a-79e2-4088-a5f8-c1664928453b", + "link_type": "ethernet", + "nodes": [ + { + "adapter_number": 1, + "label": { + "rotation": 0, + "style": "font-family: TypeWriter;font-size: 10.0;font-weight: bold;fill: #000000;fill-opacity: 1.0;", + "text": "e1/0", + "x": 17, + "y": 67 + }, + "node_id": "de23a89a-aa1f-446a-a950-31d4bf98653c", + "port_number": 0 + }, + { + "adapter_number": 1, + "label": { + "rotation": 0, + "style": "font-family: TypeWriter;font-size: 10.0;font-weight: bold;fill: #000000;fill-opacity: 1.0;", + "text": "e1/0", + "x": 42, + "y": -7 + }, + "node_id": "0d10d697-ef8d-40af-a4f3-fafe71f5458b", + "port_number": 0 + } + ], + "project_id": "4b21dfb3-675a-4efa-8613-2f7fb32e76fe", + "suspend": false + }, + { + "capture_compute_id": null, + "capture_file_name": null, + "capture_file_path": null, + "capturing": false, + "filters": {}, + "link_id": "d1f77e00-29d9-483b-988e-1dad66aa0e5f", + "link_type": "ethernet", + "nodes": [], + "project_id": "4b21dfb3-675a-4efa-8613-2f7fb32e76fe", + "suspend": true + }, + { + "capture_compute_id": null, + "capture_file_name": null, + "capture_file_path": null, + "capturing": false, + "filters": {}, + "link_id": "374b409d-90f2-44e8-b70d-a9d0b2844fd5", + "link_type": "ethernet", + "nodes": [], + "project_id": "4b21dfb3-675a-4efa-8613-2f7fb32e76fe", + "suspend": false + }, + { + "capture_compute_id": null, + "capture_file_name": null, + "capture_file_path": null, + "capturing": false, + "filters": {}, + "link_id": "fb27704f-7be5-4152-8ecd-1db6633b2bd9", + "link_type": "ethernet", + "nodes": [ + { + "adapter_number": 0, + "label": { + "rotation": 0, + "style": "font-family: TypeWriter;font-size: 10.0;font-weight: bold;fill: #000000;fill-opacity: 1.0;", + "text": "Management1", + "x": 37, + "y": -9 + }, + "node_id": "8283b923-df0e-4bc1-8199-be6fea40f500", + "port_number": 0 + }, + { + "adapter_number": 0, + "label": { + "rotation": 0, + "style": "font-family: TypeWriter;font-size: 10.0;font-weight: bold;fill: #000000;fill-opacity: 1.0;", + "text": "e0", + "x": 27, + "y": 55 + }, + "node_id": "da28e1c0-9465-4f7c-b42c-49b2f4e1c64d", + "port_number": 0 + } + ], + "project_id": "4b21dfb3-675a-4efa-8613-2f7fb32e76fe", + "suspend": false + }, + { + "capture_compute_id": null, + "capture_file_name": null, + "capture_file_path": null, + "capturing": false, + "filters": {}, + "link_id": "4d9f1235-7fd1-466b-ad26-0b4b08beb778", + "link_type": "ethernet", + "nodes": [ + { + "adapter_number": 2, + "label": { + "rotation": 0, + "style": "font-family: TypeWriter;font-size: 10.0;font-weight: bold;fill: #000000;fill-opacity: 1.0;", + "text": "e1", + "x": 69, + "y": 31 + }, + "node_id": "8283b923-df0e-4bc1-8199-be6fea40f500", + "port_number": 0 + }, + { + "adapter_number": 0, + "label": { + "rotation": 0, + "style": "font-family: TypeWriter;font-size: 10.0;font-weight: bold;fill: #000000;fill-opacity: 1.0;", + "text": "eth0", + "x": -9, + "y": 28 + }, + "node_id": "ef503c45-e998-499d-88fc-2765614b313e", + "port_number": 0 + } + ], + "project_id": "4b21dfb3-675a-4efa-8613-2f7fb32e76fe", + "suspend": false + }, + { + "capture_compute_id": null, + "capture_file_name": null, + "capture_file_path": null, + "capturing": false, + "filters": {}, + "link_id": "52cdd27d-fa97-47e7-ab99-ea810c20e614", + "link_type": "ethernet", + "nodes": [ + { + "adapter_number": 0, + "label": { + "rotation": 0, + "style": "font-family: TypeWriter;font-size: 10.0;font-weight: bold;fill: #000000;fill-opacity: 1.0;", + "text": "eth1", + "x": 8, + "y": 70 + }, + "node_id": "cde85a31-c97f-4551-9596-a3ed12c08498", + "port_number": 1 + }, + { + "adapter_number": 0, + "label": { + "rotation": 0, + "style": "font-family: TypeWriter;font-size: 10.0;font-weight: bold;fill: #000000;fill-opacity: 1.0;", + "text": "e7", + "x": 71, + "y": -1 + }, + "node_id": "da28e1c0-9465-4f7c-b42c-49b2f4e1c64d", + "port_number": 7 + } + ], + "project_id": "4b21dfb3-675a-4efa-8613-2f7fb32e76fe", + "suspend": false + } +] diff --git a/tests/data/links.py b/tests/data/links.py new file mode 100644 index 0000000..000ec09 --- /dev/null +++ b/tests/data/links.py @@ -0,0 +1,79 @@ +LINKS_REPR = [ + ( + "Link(link_id='d7dd01d6-9577-4076-b7f2-911b231044f8', link_type='ethernet'," + " project_id='4b21dfb3-675a-4efa-8613-2f7fb32e76fe', suspend=False, nodes=[" + "{'adapter_number': 0, 'label': {'rotation': 0, 'style': 'font-family:" + " TypeWriter;font-size: 10.0;font-weight: bold;fill: #000000;fill-opacity: 1.0" + ";', 'text': 'e0/0', 'x': 69, 'y': 27}, 'node_id':" + " 'de23a89a-aa1f-446a-a950-31d4bf98653c', 'port_number': 0}, {'adapter_number':" + " 0, 'label': {'rotation': 0, 'style': 'font-family: TypeWriter;font-size: 10.0" + ";font-weight: bold;fill: #000000;fill-opacity: 1.0;', 'text': 'e1', 'x': -4," + " 'y': 18}, 'node_id': 'da28e1c0-9465-4f7c-b42c-49b2f4e1c64d', 'port_number': 1" + "}], filters={}, capturing=False, capture_file_path=None," + " capture_file_name=None, capture_compute_id=None)" + ), + ( + "Link(link_id='cda8707a-79e2-4088-a5f8-c1664928453b', link_type='ethernet'," + " project_id='4b21dfb3-675a-4efa-8613-2f7fb32e76fe', suspend=False, nodes=" + "[{'adapter_number': 1, 'label': {'rotation': 0, 'style': 'font-family:" + " TypeWriter;font-size: 10.0;font-weight: bold;fill: #000000;fill-opacity: 1.0" + ";', 'text': 'e1/0', 'x': 17, 'y': 67}, 'node_id':" + " 'de23a89a-aa1f-446a-a950-31d4bf98653c', 'port_number': 0}, {'adapter_number':" + " 1, 'label': {'rotation': 0, 'style': 'font-family: TypeWriter;font-size: 10.0" + ";font-weight: bold;fill: #000000;fill-opacity: 1.0;', 'text': 'e1/0', 'x': 42," + " 'y': -7}, 'node_id': '0d10d697-ef8d-40af-a4f3-fafe71f5458b', 'port_number': 0" + "}], filters={}, capturing=False, capture_file_path=None," + " capture_file_name=None, capture_compute_id=None)" + ), + ( + "Link(link_id='d1f77e00-29d9-483b-988e-1dad66aa0e5f', link_type='ethernet'," + " project_id='4b21dfb3-675a-4efa-8613-2f7fb32e76fe', suspend=True, nodes=[]," + " filters={}, capturing=False, capture_file_path=None, capture_file_name=None," + " capture_compute_id=None)" + ), + ( + "Link(link_id='374b409d-90f2-44e8-b70d-a9d0b2844fd5', link_type='ethernet'," + " project_id='4b21dfb3-675a-4efa-8613-2f7fb32e76fe', suspend=False, nodes=[]," + " filters={}, capturing=False, capture_file_path=None, capture_file_name=None," + " capture_compute_id=None)" + ), + ( + "Link(link_id='fb27704f-7be5-4152-8ecd-1db6633b2bd9', link_type='ethernet'," + " project_id='4b21dfb3-675a-4efa-8613-2f7fb32e76fe', suspend=False, nodes=" + "[{'adapter_number': 0, 'label': {'rotation': 0, 'style': 'font-family:" + " TypeWriter;font-size: 10.0;font-weight: bold;fill: #000000;fill-opacity: 1.0" + ";', 'text': 'Management1', 'x': 37, 'y': -9}, 'node_id':" + " '8283b923-df0e-4bc1-8199-be6fea40f500', 'port_number': 0}, {'adapter_number':" + " 0, 'label': {'rotation': 0, 'style': 'font-family: TypeWriter;font-size: 10.0" + ";font-weight: bold;fill: #000000;fill-opacity: 1.0;', 'text': 'e0', 'x': 27," + " 'y': 55}, 'node_id': 'da28e1c0-9465-4f7c-b42c-49b2f4e1c64d', 'port_number': 0" + "}], filters={}, capturing=False, capture_file_path=None," + " capture_file_name=None, capture_compute_id=None)" + ), + ( + "Link(link_id='4d9f1235-7fd1-466b-ad26-0b4b08beb778', link_type='ethernet'," + " project_id='4b21dfb3-675a-4efa-8613-2f7fb32e76fe', suspend=False, nodes=" + "[{'adapter_number': 2, 'label': {'rotation': 0, 'style': 'font-family:" + " TypeWriter;font-size: 10.0;font-weight: bold;fill: #000000;fill-opacity: 1.0" + ";', 'text': 'e1', 'x': 69, 'y': 31}, 'node_id':" + " '8283b923-df0e-4bc1-8199-be6fea40f500', 'port_number': 0}, {'adapter_number':" + " 0, 'label': {'rotation': 0, 'style': 'font-family: TypeWriter;font-size: 10.0" + ";font-weight: bold;fill: #000000;fill-opacity: 1.0;', 'text': 'eth0', 'x': -9," + " 'y': 28}, 'node_id': 'ef503c45-e998-499d-88fc-2765614b313e', 'port_number': 0" + "}], filters={}, capturing=False, capture_file_path=None," + " capture_file_name=None, capture_compute_id=None)" + ), + ( + "Link(link_id='52cdd27d-fa97-47e7-ab99-ea810c20e614', link_type='ethernet'," + " project_id='4b21dfb3-675a-4efa-8613-2f7fb32e76fe', suspend=False, nodes=" + "[{'adapter_number': 0, 'label': {'rotation': 0, 'style': 'font-family:" + " TypeWriter;font-size: 10.0;font-weight: bold;fill: #000000;fill-opacity: 1.0" + ";', 'text': 'eth1', 'x': 8, 'y': 70}, 'node_id':" + " 'cde85a31-c97f-4551-9596-a3ed12c08498', 'port_number': 1}, {'adapter_number':" + " 0, 'label': {'rotation': 0, 'style': 'font-family: TypeWriter;font-size: 10.0" + ";font-weight: bold;fill: #000000;fill-opacity: 1.0;', 'text': 'e7', 'x': 71," + " 'y': -1}, 'node_id': 'da28e1c0-9465-4f7c-b42c-49b2f4e1c64d', 'port_number': 7" + "}], filters={}, capturing=False, capture_file_path=None," + " capture_file_name=None, capture_compute_id=None)" + ), +] diff --git a/tests/data/nodes.json b/tests/data/nodes.json new file mode 100644 index 0000000..01a21db --- /dev/null +++ b/tests/data/nodes.json @@ -0,0 +1,1025 @@ +[ + { + "command_line": null, + "compute_id": "local", + "console": 5000, + "console_auto_start": false, + "console_host": "0.0.0.0", + "console_type": "none", + "custom_adapters": [], + "first_port_name": null, + "height": 32, + "label": { + "rotation": 0, + "style": "font-family: TypeWriter;font-size: 10.0;font-weight: bold;fill: #000000;fill-opacity: 1.0;", + "text": "Ethernetswitch-1", + "x": -13, + "y": -25 + }, + "locked": false, + "name": "Ethernetswitch-1", + "node_directory": null, + "node_id": "da28e1c0-9465-4f7c-b42c-49b2f4e1c64d", + "node_type": "ethernet_switch", + "port_name_format": "Ethernet{0}", + "port_segment_size": 0, + "ports": [ + { + "adapter_number": 0, + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "name": "Ethernet0", + "port_number": 0, + "short_name": "e0" + }, + { + "adapter_number": 0, + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "name": "Ethernet1", + "port_number": 1, + "short_name": "e1" + }, + { + "adapter_number": 0, + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "name": "Ethernet2", + "port_number": 2, + "short_name": "e2" + }, + { + "adapter_number": 0, + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "name": "Ethernet3", + "port_number": 3, + "short_name": "e3" + }, + { + "adapter_number": 0, + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "name": "Ethernet4", + "port_number": 4, + "short_name": "e4" + }, + { + "adapter_number": 0, + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "name": "Ethernet5", + "port_number": 5, + "short_name": "e5" + }, + { + "adapter_number": 0, + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "name": "Ethernet6", + "port_number": 6, + "short_name": "e6" + }, + { + "adapter_number": 0, + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "name": "Ethernet7", + "port_number": 7, + "short_name": "e7" + } + ], + "project_id": "4b21dfb3-675a-4efa-8613-2f7fb32e76fe", + "properties": { + "ports_mapping": [ + { + "name": "Ethernet0", + "port_number": 0, + "type": "access", + "vlan": 1 + }, + { + "name": "Ethernet1", + "port_number": 1, + "type": "access", + "vlan": 1 + }, + { + "name": "Ethernet2", + "port_number": 2, + "type": "access", + "vlan": 1 + }, + { + "name": "Ethernet3", + "port_number": 3, + "type": "access", + "vlan": 1 + }, + { + "name": "Ethernet4", + "port_number": 4, + "type": "access", + "vlan": 1 + }, + { + "name": "Ethernet5", + "port_number": 5, + "type": "access", + "vlan": 1 + }, + { + "name": "Ethernet6", + "port_number": 6, + "type": "access", + "vlan": 1 + }, + { + "name": "Ethernet7", + "port_number": 7, + "type": "access", + "vlan": 1 + } + ] + }, + "status": "started", + "symbol": ":/symbols/ethernet_switch.svg", + "template_id": "1966b864-93e7-32d5-965f-001384eec461", + "width": 72, + "x": -11, + "y": -114, + "z": 1 + }, + { + "command_line": "/opt/gns3/images/IOU/L3-ADVENTERPRISEK9-M-15.4-2T.bin 1", + "compute_id": "local", + "console": 5001, + "console_auto_start": false, + "console_host": "0.0.0.0", + "console_type": "telnet", + "custom_adapters": [], + "first_port_name": null, + "height": 60, + "label": { + "rotation": 0, + "style": "font-family: TypeWriter;font-size: 10.0;font-weight: bold;fill: #000000;fill-opacity: 1.0;", + "text": "IOU1", + "x": 13, + "y": -25 + }, + "locked": false, + "name": "IOU1", + "node_directory": "/opt/gns3/projects/4b21dfb3-675a-4efa-8613-2f7fb32e76fe/project-files/iou/de23a89a-aa1f-446a-a950-31d4bf98653c", + "node_id": "de23a89a-aa1f-446a-a950-31d4bf98653c", + "node_type": "iou", + "port_name_format": "Ethernet{segment0}/{port0}", + "port_segment_size": 4, + "ports": [ + { + "adapter_number": 0, + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "name": "Ethernet0/0", + "port_number": 0, + "short_name": "e0/0" + }, + { + "adapter_number": 0, + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "name": "Ethernet0/1", + "port_number": 1, + "short_name": "e0/1" + }, + { + "adapter_number": 0, + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "name": "Ethernet0/2", + "port_number": 2, + "short_name": "e0/2" + }, + { + "adapter_number": 0, + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "name": "Ethernet0/3", + "port_number": 3, + "short_name": "e0/3" + }, + { + "adapter_number": 1, + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "name": "Ethernet1/0", + "port_number": 0, + "short_name": "e1/0" + }, + { + "adapter_number": 1, + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "name": "Ethernet1/1", + "port_number": 1, + "short_name": "e1/1" + }, + { + "adapter_number": 1, + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "name": "Ethernet1/2", + "port_number": 2, + "short_name": "e1/2" + }, + { + "adapter_number": 1, + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "name": "Ethernet1/3", + "port_number": 3, + "short_name": "e1/3" + }, + { + "adapter_number": 2, + "data_link_types": { + "Cisco HDLC": "DLT_C_HDLC", + "Cisco PPP": "DLT_PPP_SERIAL", + "Frame Relay": "DLT_FRELAY" + }, + "link_type": "serial", + "name": "Serial2/0", + "port_number": 0, + "short_name": "s2/0" + }, + { + "adapter_number": 2, + "data_link_types": { + "Cisco HDLC": "DLT_C_HDLC", + "Cisco PPP": "DLT_PPP_SERIAL", + "Frame Relay": "DLT_FRELAY" + }, + "link_type": "serial", + "name": "Serial2/1", + "port_number": 1, + "short_name": "s2/1" + }, + { + "adapter_number": 2, + "data_link_types": { + "Cisco HDLC": "DLT_C_HDLC", + "Cisco PPP": "DLT_PPP_SERIAL", + "Frame Relay": "DLT_FRELAY" + }, + "link_type": "serial", + "name": "Serial2/2", + "port_number": 2, + "short_name": "s2/2" + }, + { + "adapter_number": 2, + "data_link_types": { + "Cisco HDLC": "DLT_C_HDLC", + "Cisco PPP": "DLT_PPP_SERIAL", + "Frame Relay": "DLT_FRELAY" + }, + "link_type": "serial", + "name": "Serial2/3", + "port_number": 3, + "short_name": "s2/3" + }, + { + "adapter_number": 3, + "data_link_types": { + "Cisco HDLC": "DLT_C_HDLC", + "Cisco PPP": "DLT_PPP_SERIAL", + "Frame Relay": "DLT_FRELAY" + }, + "link_type": "serial", + "name": "Serial3/0", + "port_number": 0, + "short_name": "s3/0" + }, + { + "adapter_number": 3, + "data_link_types": { + "Cisco HDLC": "DLT_C_HDLC", + "Cisco PPP": "DLT_PPP_SERIAL", + "Frame Relay": "DLT_FRELAY" + }, + "link_type": "serial", + "name": "Serial3/1", + "port_number": 1, + "short_name": "s3/1" + }, + { + "adapter_number": 3, + "data_link_types": { + "Cisco HDLC": "DLT_C_HDLC", + "Cisco PPP": "DLT_PPP_SERIAL", + "Frame Relay": "DLT_FRELAY" + }, + "link_type": "serial", + "name": "Serial3/2", + "port_number": 2, + "short_name": "s3/2" + }, + { + "adapter_number": 3, + "data_link_types": { + "Cisco HDLC": "DLT_C_HDLC", + "Cisco PPP": "DLT_PPP_SERIAL", + "Frame Relay": "DLT_FRELAY" + }, + "link_type": "serial", + "name": "Serial3/3", + "port_number": 3, + "short_name": "s3/3" + } + ], + "project_id": "4b21dfb3-675a-4efa-8613-2f7fb32e76fe", + "properties": { + "application_id": 1, + "ethernet_adapters": 2, + "l1_keepalives": false, + "md5sum": "50d1c5aaf1976e4622daf9eaa2632212", + "nvram": 64, + "path": "L3-ADVENTERPRISEK9-M-15.4-2T.bin", + "ram": 256, + "serial_adapters": 2, + "usage": "", + "use_default_iou_values": true + }, + "status": "started", + "symbol": ":/symbols/affinity/circle/gray/router.svg", + "template_id": "8504c605-7914-4a8f-9cd4-a2638382db0e", + "width": 60, + "x": -184, + "y": -139, + "z": 1 + }, + { + "command_line": "/opt/gns3/images/IOU/L3-ADVENTERPRISEK9-M-15.4-2T.bin 2", + "compute_id": "local", + "console": 5002, + "console_auto_start": false, + "console_host": "0.0.0.0", + "console_type": "telnet", + "custom_adapters": [], + "first_port_name": null, + "height": 60, + "label": { + "rotation": 0, + "style": "font-family: TypeWriter;font-size: 10.0;font-weight: bold;fill: #000000;fill-opacity: 1.0;", + "text": "IOU2", + "x": 13, + "y": -25 + }, + "locked": false, + "name": "IOU2", + "node_directory": "/opt/gns3/projects/4b21dfb3-675a-4efa-8613-2f7fb32e76fe/project-files/iou/0d10d697-ef8d-40af-a4f3-fafe71f5458b", + "node_id": "0d10d697-ef8d-40af-a4f3-fafe71f5458b", + "node_type": "iou", + "port_name_format": "Ethernet{segment0}/{port0}", + "port_segment_size": 4, + "ports": [ + { + "adapter_number": 0, + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "name": "Ethernet0/0", + "port_number": 0, + "short_name": "e0/0" + }, + { + "adapter_number": 0, + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "name": "Ethernet0/1", + "port_number": 1, + "short_name": "e0/1" + }, + { + "adapter_number": 0, + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "name": "Ethernet0/2", + "port_number": 2, + "short_name": "e0/2" + }, + { + "adapter_number": 0, + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "name": "Ethernet0/3", + "port_number": 3, + "short_name": "e0/3" + }, + { + "adapter_number": 1, + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "name": "Ethernet1/0", + "port_number": 0, + "short_name": "e1/0" + }, + { + "adapter_number": 1, + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "name": "Ethernet1/1", + "port_number": 1, + "short_name": "e1/1" + }, + { + "adapter_number": 1, + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "name": "Ethernet1/2", + "port_number": 2, + "short_name": "e1/2" + }, + { + "adapter_number": 1, + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "name": "Ethernet1/3", + "port_number": 3, + "short_name": "e1/3" + }, + { + "adapter_number": 2, + "data_link_types": { + "Cisco HDLC": "DLT_C_HDLC", + "Cisco PPP": "DLT_PPP_SERIAL", + "Frame Relay": "DLT_FRELAY" + }, + "link_type": "serial", + "name": "Serial2/0", + "port_number": 0, + "short_name": "s2/0" + }, + { + "adapter_number": 2, + "data_link_types": { + "Cisco HDLC": "DLT_C_HDLC", + "Cisco PPP": "DLT_PPP_SERIAL", + "Frame Relay": "DLT_FRELAY" + }, + "link_type": "serial", + "name": "Serial2/1", + "port_number": 1, + "short_name": "s2/1" + }, + { + "adapter_number": 2, + "data_link_types": { + "Cisco HDLC": "DLT_C_HDLC", + "Cisco PPP": "DLT_PPP_SERIAL", + "Frame Relay": "DLT_FRELAY" + }, + "link_type": "serial", + "name": "Serial2/2", + "port_number": 2, + "short_name": "s2/2" + }, + { + "adapter_number": 2, + "data_link_types": { + "Cisco HDLC": "DLT_C_HDLC", + "Cisco PPP": "DLT_PPP_SERIAL", + "Frame Relay": "DLT_FRELAY" + }, + "link_type": "serial", + "name": "Serial2/3", + "port_number": 3, + "short_name": "s2/3" + }, + { + "adapter_number": 3, + "data_link_types": { + "Cisco HDLC": "DLT_C_HDLC", + "Cisco PPP": "DLT_PPP_SERIAL", + "Frame Relay": "DLT_FRELAY" + }, + "link_type": "serial", + "name": "Serial3/0", + "port_number": 0, + "short_name": "s3/0" + }, + { + "adapter_number": 3, + "data_link_types": { + "Cisco HDLC": "DLT_C_HDLC", + "Cisco PPP": "DLT_PPP_SERIAL", + "Frame Relay": "DLT_FRELAY" + }, + "link_type": "serial", + "name": "Serial3/1", + "port_number": 1, + "short_name": "s3/1" + }, + { + "adapter_number": 3, + "data_link_types": { + "Cisco HDLC": "DLT_C_HDLC", + "Cisco PPP": "DLT_PPP_SERIAL", + "Frame Relay": "DLT_FRELAY" + }, + "link_type": "serial", + "name": "Serial3/2", + "port_number": 2, + "short_name": "s3/2" + }, + { + "adapter_number": 3, + "data_link_types": { + "Cisco HDLC": "DLT_C_HDLC", + "Cisco PPP": "DLT_PPP_SERIAL", + "Frame Relay": "DLT_FRELAY" + }, + "link_type": "serial", + "name": "Serial3/3", + "port_number": 3, + "short_name": "s3/3" + } + ], + "project_id": "4b21dfb3-675a-4efa-8613-2f7fb32e76fe", + "properties": { + "application_id": 2, + "ethernet_adapters": 2, + "l1_keepalives": false, + "md5sum": "50d1c5aaf1976e4622daf9eaa2632212", + "nvram": 64, + "path": "L3-ADVENTERPRISEK9-M-15.4-2T.bin", + "ram": 256, + "serial_adapters": 2, + "usage": "", + "use_default_iou_values": true + }, + "status": "started", + "symbol": ":/symbols/affinity/circle/gray/router.svg", + "template_id": "8504c605-7914-4a8f-9cd4-a2638382db0e", + "width": 60, + "x": -183, + "y": 6, + "z": 1 + }, + { + "command_line": "/usr/bin/qemu-system-x86_64 -name vEOS-4.21.5F-1 -m 2048M -smp cpus=2 -enable-kvm -machine smm=off -boot order=c -drive file=/opt/gns3/projects/4b21dfb3-675a-4efa-8613-2f7fb32e76fe/project-files/qemu/8283b923-df0e-4bc1-8199-be6fea40f500/hda_disk.qcow2,if=ide,index=0,media=disk -uuid 8283b923-df0e-4bc1-8199-be6fea40f500 -serial telnet:127.0.0.1:5004,server,nowait -monitor tcp:127.0.0.1:35261,server,nowait -net none -device e1000,mac=0c:76:fe:f5:00:00,netdev=gns3-0 -netdev socket,id=gns3-0,udp=127.0.0.1:10011,localaddr=127.0.0.1:10010 -device e1000,mac=0c:76:fe:f5:00:01,netdev=gns3-1 -netdev socket,id=gns3-1,udp=127.0.0.1:10013,localaddr=127.0.0.1:10012 -device e1000,mac=0c:76:fe:f5:00:02,netdev=gns3-2 -netdev socket,id=gns3-2,udp=127.0.0.1:10015,localaddr=127.0.0.1:10014 -device e1000,mac=0c:76:fe:f5:00:03,netdev=gns3-3 -netdev socket,id=gns3-3,udp=127.0.0.1:10017,localaddr=127.0.0.1:10016 -device e1000,mac=0c:76:fe:f5:00:04,netdev=gns3-4 -netdev socket,id=gns3-4,udp=127.0.0.1:10019,localaddr=127.0.0.1:10018 -device e1000,mac=0c:76:fe:f5:00:05,netdev=gns3-5 -netdev socket,id=gns3-5,udp=127.0.0.1:10021,localaddr=127.0.0.1:10020 -device e1000,mac=0c:76:fe:f5:00:06,netdev=gns3-6 -netdev socket,id=gns3-6,udp=127.0.0.1:10023,localaddr=127.0.0.1:10022 -device e1000,mac=0c:76:fe:f5:00:07,netdev=gns3-7 -netdev socket,id=gns3-7,udp=127.0.0.1:10025,localaddr=127.0.0.1:10024 -device e1000,mac=0c:76:fe:f5:00:08,netdev=gns3-8 -netdev socket,id=gns3-8,udp=127.0.0.1:10027,localaddr=127.0.0.1:10026 -device e1000,mac=0c:76:fe:f5:00:09,netdev=gns3-9 -netdev socket,id=gns3-9,udp=127.0.0.1:10029,localaddr=127.0.0.1:10028 -device e1000,mac=0c:76:fe:f5:00:0a,netdev=gns3-10 -netdev socket,id=gns3-10,udp=127.0.0.1:10031,localaddr=127.0.0.1:10030 -device e1000,mac=0c:76:fe:f5:00:0b,netdev=gns3-11 -netdev socket,id=gns3-11,udp=127.0.0.1:10033,localaddr=127.0.0.1:10032 -device e1000,mac=0c:76:fe:f5:00:0c,netdev=gns3-12 -netdev socket,id=gns3-12,udp=127.0.0.1:10035,localaddr=127.0.0.1:10034 -nographic", + "compute_id": "local", + "console": 5003, + "console_auto_start": false, + "console_host": "0.0.0.0", + "console_type": "telnet", + "custom_adapters": [], + "first_port_name": "Management1", + "height": 60, + "label": { + "rotation": 0, + "style": "font-family: TypeWriter;font-size: 10.0;font-weight: bold;fill: #000000;fill-opacity: 1.0;", + "text": "vEOS-4.21.5F-1", + "x": -15, + "y": -25 + }, + "locked": false, + "name": "vEOS-4.21.5F-1", + "node_directory": "/opt/gns3/projects/4b21dfb3-675a-4efa-8613-2f7fb32e76fe/project-files/qemu/8283b923-df0e-4bc1-8199-be6fea40f500", + "node_id": "8283b923-df0e-4bc1-8199-be6fea40f500", + "node_type": "qemu", + "port_name_format": "Ethernet{0}", + "port_segment_size": 0, + "ports": [ + { + "adapter_number": 0, + "adapter_type": "e1000", + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "mac_address": "0c:76:fe:f5:00:00", + "name": "Management1", + "port_number": 0, + "short_name": "Management1" + }, + { + "adapter_number": 1, + "adapter_type": "e1000", + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "mac_address": "0c:76:fe:f5:00:01", + "name": "Ethernet0", + "port_number": 0, + "short_name": "e0" + }, + { + "adapter_number": 2, + "adapter_type": "e1000", + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "mac_address": "0c:76:fe:f5:00:02", + "name": "Ethernet1", + "port_number": 0, + "short_name": "e1" + }, + { + "adapter_number": 3, + "adapter_type": "e1000", + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "mac_address": "0c:76:fe:f5:00:03", + "name": "Ethernet2", + "port_number": 0, + "short_name": "e2" + }, + { + "adapter_number": 4, + "adapter_type": "e1000", + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "mac_address": "0c:76:fe:f5:00:04", + "name": "Ethernet3", + "port_number": 0, + "short_name": "e3" + }, + { + "adapter_number": 5, + "adapter_type": "e1000", + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "mac_address": "0c:76:fe:f5:00:05", + "name": "Ethernet4", + "port_number": 0, + "short_name": "e4" + }, + { + "adapter_number": 6, + "adapter_type": "e1000", + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "mac_address": "0c:76:fe:f5:00:06", + "name": "Ethernet5", + "port_number": 0, + "short_name": "e5" + }, + { + "adapter_number": 7, + "adapter_type": "e1000", + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "mac_address": "0c:76:fe:f5:00:07", + "name": "Ethernet6", + "port_number": 0, + "short_name": "e6" + }, + { + "adapter_number": 8, + "adapter_type": "e1000", + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "mac_address": "0c:76:fe:f5:00:08", + "name": "Ethernet7", + "port_number": 0, + "short_name": "e7" + }, + { + "adapter_number": 9, + "adapter_type": "e1000", + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "mac_address": "0c:76:fe:f5:00:09", + "name": "Ethernet8", + "port_number": 0, + "short_name": "e8" + }, + { + "adapter_number": 10, + "adapter_type": "e1000", + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "mac_address": "0c:76:fe:f5:00:0a", + "name": "Ethernet9", + "port_number": 0, + "short_name": "e9" + }, + { + "adapter_number": 11, + "adapter_type": "e1000", + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "mac_address": "0c:76:fe:f5:00:0b", + "name": "Ethernet10", + "port_number": 0, + "short_name": "e10" + }, + { + "adapter_number": 12, + "adapter_type": "e1000", + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "mac_address": "0c:76:fe:f5:00:0c", + "name": "Ethernet11", + "port_number": 0, + "short_name": "e11" + } + ], + "project_id": "4b21dfb3-675a-4efa-8613-2f7fb32e76fe", + "properties": { + "adapter_type": "e1000", + "adapters": 13, + "bios_image": "", + "bios_image_md5sum": null, + "boot_priority": "c", + "cdrom_image": "", + "cdrom_image_md5sum": null, + "cpu_throttling": 0, + "cpus": 2, + "hda_disk_image": "vEOS-lab-4.21.5F.vmdk", + "hda_disk_image_md5sum": "5bfd3f1b7b994c73084a38fb1e96e85b", + "hda_disk_interface": "ide", + "hdb_disk_image": "", + "hdb_disk_image_md5sum": null, + "hdb_disk_interface": "ide", + "hdc_disk_image": "", + "hdc_disk_image_md5sum": null, + "hdc_disk_interface": "ide", + "hdd_disk_image": "", + "hdd_disk_image_md5sum": null, + "hdd_disk_interface": "ide", + "initrd": "", + "initrd_md5sum": null, + "kernel_command_line": "", + "kernel_image": "", + "kernel_image_md5sum": null, + "legacy_networking": false, + "linked_clone": true, + "mac_address": "0c:76:fe:f5:00:00", + "on_close": "power_off", + "options": "-nographic", + "platform": "x86_64", + "process_priority": "normal", + "qemu_path": "/usr/bin/qemu-system-x86_64", + "ram": 2048, + "usage": "" + }, + "status": "started", + "symbol": ":/symbols/affinity/square/gray/switch_multilayer.svg", + "template_id": "c6203d4b-d0ce-4951-bf18-c44369d46804", + "width": 60, + "x": -20, + "y": -6, + "z": 1 + }, + { + "command_line": null, + "compute_id": "local", + "console": 5005, + "console_auto_start": false, + "console_host": "0.0.0.0", + "console_type": "telnet", + "custom_adapters": [], + "first_port_name": null, + "height": 60, + "label": { + "rotation": 0, + "style": "font-family: TypeWriter;font-size: 10.0;font-weight: bold;fill: #000000;fill-opacity: 1.0;", + "text": "alpine-1", + "x": 3, + "y": -25 + }, + "locked": false, + "name": "alpine-1", + "node_directory": "/opt/gns3/projects/4b21dfb3-675a-4efa-8613-2f7fb32e76fe/project-files/docker/ef503c45-e998-499d-88fc-2765614b313e", + "node_id": "ef503c45-e998-499d-88fc-2765614b313e", + "node_type": "docker", + "port_name_format": "Ethernet{0}", + "port_segment_size": 0, + "ports": [ + { + "adapter_number": 0, + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "name": "eth0", + "port_number": 0, + "short_name": "eth0" + }, + { + "adapter_number": 1, + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "name": "eth1", + "port_number": 0, + "short_name": "eth1" + } + ], + "project_id": "4b21dfb3-675a-4efa-8613-2f7fb32e76fe", + "properties": { + "adapters": 2, + "aux": 5006, + "console_http_path": "/", + "console_http_port": 80, + "console_resolution": "1024x768", + "container_id": "a2109a13328c2a5f57a7405b43bcf791811f85c1d90267693fe3b57dfefe81d9", + "environment": null, + "extra_hosts": null, + "extra_volumes": [], + "image": "alpine:latest", + "start_command": null, + "usage": "" + }, + "status": "started", + "symbol": ":/symbols/affinity/circle/gray/docker.svg", + "template_id": "847e5333-6ac9-411f-a400-89838584371b", + "width": 60, + "x": 169, + "y": -10, + "z": 1 + }, + { + "command_line": null, + "compute_id": "local", + "console": null, + "console_auto_start": false, + "console_host": "0.0.0.0", + "console_type": null, + "custom_adapters": [], + "first_port_name": null, + "height": 71, + "label": { + "rotation": 0, + "style": "font-family: TypeWriter;font-size: 10.0;font-weight: bold;fill: #000000;fill-opacity: 1.0;", + "text": "Cloud-1", + "x": 54, + "y": -25 + }, + "locked": false, + "name": "Cloud-1", + "node_directory": "/opt/gns3/projects/4b21dfb3-675a-4efa-8613-2f7fb32e76fe/project-files/builtin/cde85a31-c97f-4551-9596-a3ed12c08498", + "node_id": "cde85a31-c97f-4551-9596-a3ed12c08498", + "node_type": "cloud", + "port_name_format": "Ethernet{0}", + "port_segment_size": 0, + "ports": [ + { + "adapter_number": 0, + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "name": "eth0", + "port_number": 0, + "short_name": "eth0" + }, + { + "adapter_number": 0, + "data_link_types": { + "Ethernet": "DLT_EN10MB" + }, + "link_type": "ethernet", + "name": "eth1", + "port_number": 1, + "short_name": "eth1" + } + ], + "project_id": "4b21dfb3-675a-4efa-8613-2f7fb32e76fe", + "properties": { + "interfaces": [ + { + "name": "docker0", + "special": true, + "type": "ethernet" + }, + { + "name": "eth0", + "special": false, + "type": "ethernet" + }, + { + "name": "eth1", + "special": false, + "type": "ethernet" + }, + { + "name": "gns3tap0-0", + "special": true, + "type": "ethernet" + }, + { + "name": "lo", + "special": true, + "type": "ethernet" + }, + { + "name": "virbr0", + "special": true, + "type": "ethernet" + }, + { + "name": "virbr0-nic", + "special": true, + "type": "ethernet" + } + ], + "ports_mapping": [ + { + "interface": "eth0", + "name": "eth0", + "port_number": 0, + "type": "ethernet" + }, + { + "interface": "eth1", + "name": "eth1", + "port_number": 1, + "type": "ethernet" + } + ], + "remote_console_host": "", + "remote_console_http_path": "/", + "remote_console_port": 23, + "remote_console_type": "none" + }, + "status": "started", + "symbol": ":/symbols/cloud.svg", + "template_id": "39e257dc-8412-3174-b6b3-0ee3ed6a43e9", + "width": 159, + "x": 150, + "y": -236, + "z": 1 + } +] diff --git a/tests/data/nodes.py b/tests/data/nodes.py new file mode 100644 index 0000000..71e6275 --- /dev/null +++ b/tests/data/nodes.py @@ -0,0 +1,297 @@ +NODES_REPR = [ + ( + "Node(name='Ethernetswitch-1', project_id='4b21dfb3-675a-4efa-8613-2f7fb32e76fe" + "', node_id='da28e1c0-9465-4f7c-b42c-49b2f4e1c64d', compute_id='local', " + "node_type='ethernet_switch', node_directory=None, status='started', ports=[{'" + "adapter_number': 0, 'data_link_types': {'Ethernet': 'DLT_EN10MB'}, 'link_type" + "': 'ethernet', 'name': 'Ethernet0', 'port_number': 0, 'short_name': 'e0'}, {'" + "adapter_number': 0, 'data_link_types': {'Ethernet': 'DLT_EN10MB'}, 'link_type" + "': 'ethernet', 'name': 'Ethernet1', 'port_number': 1, 'short_name': 'e1'}, {'" + "adapter_number': 0, 'data_link_types': {'Ethernet': 'DLT_EN10MB'}, 'link_type" + "': 'ethernet', 'name': 'Ethernet2', 'port_number': 2, 'short_name': 'e2'}, {'" + "adapter_number': 0, 'data_link_types': {'Ethernet': 'DLT_EN10MB'}, 'link_type" + "': 'ethernet', 'name': 'Ethernet3', 'port_number': 3, 'short_name': 'e3'}, {'" + "adapter_number': 0, 'data_link_types': {'Ethernet': 'DLT_EN10MB'}, 'link_type" + "': 'ethernet', 'name': 'Ethernet4', 'port_number': 4, 'short_name': 'e4'}, {'" + "adapter_number': 0, 'data_link_types': {'Ethernet': 'DLT_EN10MB'}, 'link_type" + "': 'ethernet', 'name': 'Ethernet5', 'port_number': 5, 'short_name': 'e5'}, {'" + "adapter_number': 0, 'data_link_types': {'Ethernet': 'DLT_EN10MB'}, 'link_type" + "': 'ethernet', 'name': 'Ethernet6', 'port_number': 6, 'short_name': 'e6'}, {'" + "adapter_number': 0, 'data_link_types': {'Ethernet': 'DLT_EN10MB'}, 'link_type" + "': 'ethernet', 'name': 'Ethernet7', 'port_number': 7, 'short_name': 'e7'}], " + "port_name_format='Ethernet{0}', port_segment_size=0, first_port_name=None, " + "locked=False, label={'rotation': 0, 'style': 'font-family: TypeWriter;font-" + "size: 10.0;font-weight: bold;fill: #000000;fill-opacity: 1.0;', 'text': '" + "Ethernetswitch-1', 'x': -13, 'y': -25}, console='5000', console_host='0.0.0." + "0', console_type='none', console_auto_start=False, command_line=None, " + "custom_adapters=[], height=32, width=72, symbol=':/symbols/ethernet_switch." + "svg', x=-11, y=-114, z=1, template_id='1966b864-93e7-32d5-965f-001384eec461', " + "properties={'ports_mapping': [{'name': 'Ethernet0', 'port_number': 0, 'type': " + "'access', 'vlan': 1}, {'name': 'Ethernet1', 'port_number': 1, 'type': 'access" + "', 'vlan': 1}, {'name': 'Ethernet2', 'port_number': 2, 'type': 'access', 'vlan" + "': 1}, {'name': 'Ethernet3', 'port_number': 3, 'type': 'access', 'vlan': 1}, " + "{'name': 'Ethernet4', 'port_number': 4, 'type': 'access', 'vlan': 1}, {'name" + "': 'Ethernet5', 'port_number': 5, 'type': 'access', 'vlan': 1}, {'name': '" + "Ethernet6', 'port_number': 6, 'type': 'access', 'vlan': 1}, {'name': '" + "Ethernet7', 'port_number': 7, 'type': 'access', 'vlan': 1}]})" + ), + ( + "Node(name='IOU1', project_id='4b21dfb3-675a-4efa-8613-2f7fb32e76fe', " + "node_id='de23a89a-aa1f-446a-a950-31d4bf98653c', compute_id='local', node_type" + "='iou', node_directory='/opt/gns3/projects/4b21dfb3-675a-4efa-8613-" + "2f7fb32e76fe/project-files/iou/de23a89a-aa1f-446a-a950-31d4bf98653c', status" + "='started', ports=[{'adapter_number': 0, 'data_link_types': {'Ethernet': '" + "DLT_EN10MB'}, 'link_type': 'ethernet', 'name': 'Ethernet0/0', 'port_number': " + "0, 'short_name': 'e0/0'}, {'adapter_number': 0, 'data_link_types': {'Ethernet" + "': 'DLT_EN10MB'}, 'link_type': 'ethernet', 'name': 'Ethernet0/1', 'port_number" + "': 1, 'short_name': 'e0/1'}, {'adapter_number': 0, 'data_link_types': {'" + "Ethernet': 'DLT_EN10MB'}, 'link_type': 'ethernet', 'name': 'Ethernet0/2', '" + "port_number': 2, 'short_name': 'e0/2'}, {'adapter_number': 0, 'data_link_types" + "': {'Ethernet': 'DLT_EN10MB'}, 'link_type': 'ethernet', 'name': 'Ethernet0/3" + "', 'port_number': 3, 'short_name': 'e0/3'}, {'adapter_number': 1, '" + "data_link_types': {'Ethernet': 'DLT_EN10MB'}, 'link_type': 'ethernet', 'name" + "': 'Ethernet1/0', 'port_number': 0, 'short_name': 'e1/0'}, {'adapter_number': " + "1, 'data_link_types': {'Ethernet': 'DLT_EN10MB'}, 'link_type': 'ethernet', '" + "name': 'Ethernet1/1', 'port_number': 1, 'short_name': 'e1/1'}, {'" + "adapter_number': 1, 'data_link_types': {'Ethernet': 'DLT_EN10MB'}, 'link_type" + "': 'ethernet', 'name': 'Ethernet1/2', 'port_number': 2, 'short_name': 'e1/2'}," + " {'adapter_number': 1, 'data_link_types': {'Ethernet': 'DLT_EN10MB'}, '" + "link_type': 'ethernet', 'name': 'Ethernet1/3', 'port_number': 3, 'short_name" + "': 'e1/3'}, {'adapter_number': 2, 'data_link_types': {'Cisco HDLC': '" + "DLT_C_HDLC', 'Cisco PPP': 'DLT_PPP_SERIAL', 'Frame Relay': 'DLT_FRELAY'}, '" + "link_type': 'serial', 'name': 'Serial2/0', 'port_number': 0, 'short_name': 's2" + "/0'}, {'adapter_number': 2, 'data_link_types': {'Cisco HDLC': 'DLT_C_HDLC', '" + "Cisco PPP': 'DLT_PPP_SERIAL', 'Frame Relay': 'DLT_FRELAY'}, 'link_type': '" + "serial', 'name': 'Serial2/1', 'port_number': 1, 'short_name': 's2/1'}, {'" + "adapter_number': 2, 'data_link_types': {'Cisco HDLC': 'DLT_C_HDLC', 'Cisco " + "PPP': 'DLT_PPP_SERIAL', 'Frame Relay': 'DLT_FRELAY'}, 'link_type': 'serial', '" + "name': 'Serial2/2', 'port_number': 2, 'short_name': 's2/2'}, {'adapter_number" + "': 2, 'data_link_types': {'Cisco HDLC': 'DLT_C_HDLC', 'Cisco PPP': '" + "DLT_PPP_SERIAL', 'Frame Relay': 'DLT_FRELAY'}, 'link_type': 'serial', 'name': " + "'Serial2/3', 'port_number': 3, 'short_name': 's2/3'}, {'adapter_number': 3, " + "'data_link_types': {'Cisco HDLC': 'DLT_C_HDLC', 'Cisco PPP': 'DLT_PPP_SERIAL" + "', 'Frame Relay': 'DLT_FRELAY'}, 'link_type': 'serial', 'name': 'Serial3/0', '" + "port_number': 0, 'short_name': 's3/0'}, {'adapter_number': 3, 'data_link_types" + "': {'Cisco HDLC': 'DLT_C_HDLC', 'Cisco PPP': 'DLT_PPP_SERIAL', 'Frame Relay': " + "'DLT_FRELAY'}, 'link_type': 'serial', 'name': 'Serial3/1', 'port_number': 1, '" + "short_name': 's3/1'}, {'adapter_number': 3, 'data_link_types': {'Cisco HDLC': " + "'DLT_C_HDLC', 'Cisco PPP': 'DLT_PPP_SERIAL', 'Frame Relay': 'DLT_FRELAY'}, '" + "link_type': 'serial', 'name': 'Serial3/2', 'port_number': 2, 'short_name': 's3" + "/2'}, {'adapter_number': 3, 'data_link_types': {'Cisco HDLC': 'DLT_C_HDLC', '" + "Cisco PPP': 'DLT_PPP_SERIAL', 'Frame Relay': 'DLT_FRELAY'}, 'link_type': '" + "serial', 'name': 'Serial3/3', 'port_number': 3, 'short_name': 's3/3'}], " + "port_name_format='Ethernet{segment0}/{port0}', port_segment_size=4, " + "first_port_name=None, locked=False, label={'rotation': 0, 'style': 'font-" + "family: TypeWriter;font-size: 10.0;font-weight: bold;fill: #000000;fill-" + "opacity: 1.0;', 'text': 'IOU1', 'x': 13, 'y': -25}, console='5001', " + "console_host='0.0.0.0', console_type='telnet', console_auto_start=False, " + "command_line='/opt/gns3/images/IOU/L3-ADVENTERPRISEK9-M-15.4-2T.bin 1', " + "custom_adapters=[], height=60, width=60, symbol=':/symbols/affinity/circle/" + "gray/router.svg', x=-184, y=-139, z=1, template_id='8504c605-7914-4a8f-9cd4-" + "a2638382db0e', properties={'application_id': 1, 'ethernet_adapters': 2, '" + "l1_keepalives': False, 'md5sum': '50d1c5aaf1976e4622daf9eaa2632212', 'nvram': " + "64, 'path': 'L3-ADVENTERPRISEK9-M-15.4-2T.bin', 'ram': 256, 'serial_adapters" + "': 2, 'usage': '', 'use_default_iou_values': True})" + ), + ( + "Node(name='IOU2', project_id='4b21dfb3-675a-4efa-8613-2f7fb32e76fe', " + "node_id='0d10d697-ef8d-40af-a4f3-fafe71f5458b', compute_id='local', node_type" + "='iou', node_directory='/opt/gns3/projects/4b21dfb3-675a-4efa-8613-" + "2f7fb32e76fe/project-files/iou/0d10d697-ef8d-40af-a4f3-fafe71f5458b', " + "status='started', ports=[{'adapter_number': 0, 'data_link_types': {'Ethernet" + "': 'DLT_EN10MB'}, 'link_type': 'ethernet', 'name': 'Ethernet0/0', 'port_number" + "': 0, 'short_name': 'e0/0'}, {'adapter_number': 0, 'data_link_types': {'" + "Ethernet': 'DLT_EN10MB'}, 'link_type': 'ethernet', 'name': 'Ethernet0/1', '" + "port_number': 1, 'short_name': 'e0/1'}, {'adapter_number': 0, 'data_link_types" + "': {'Ethernet': 'DLT_EN10MB'}, 'link_type': 'ethernet', 'name': 'Ethernet0/2" + "', 'port_number': 2, 'short_name': 'e0/2'}, {'adapter_number': 0, '" + "data_link_types': {'Ethernet': 'DLT_EN10MB'}, 'link_type': 'ethernet', 'name" + "': 'Ethernet0/3', 'port_number': 3, 'short_name': 'e0/3'}, {'adapter_number': " + "1, 'data_link_types': {'Ethernet': 'DLT_EN10MB'}, 'link_type': 'ethernet', '" + "name': 'Ethernet1/0', 'port_number': 0, 'short_name': 'e1/0'}, {'" + "adapter_number': 1, 'data_link_types': {'Ethernet': 'DLT_EN10MB'}, 'link_type" + "': 'ethernet', 'name': 'Ethernet1/1', 'port_number': 1, 'short_name': 'e1/1" + "'}, {'adapter_number': 1, 'data_link_types': {'Ethernet': 'DLT_EN10MB'}, '" + "link_type': 'ethernet', 'name': 'Ethernet1/2', 'port_number': 2, 'short_name" + "': 'e1/2'}, {'adapter_number': 1, 'data_link_types': {'Ethernet': 'DLT_EN10MB" + "'}, 'link_type': 'ethernet', 'name': 'Ethernet1/3', 'port_number': 3, '" + "short_name': 'e1/3'}, {'adapter_number': 2, 'data_link_types': {'Cisco HDLC': " + "'DLT_C_HDLC', 'Cisco PPP': 'DLT_PPP_SERIAL', 'Frame Relay': 'DLT_FRELAY'}, '" + "link_type': 'serial', 'name': 'Serial2/0', 'port_number': 0, 'short_name': 's2" + "/0'}, {'adapter_number': 2, 'data_link_types': {'Cisco HDLC': 'DLT_C_HDLC', '" + "Cisco PPP': 'DLT_PPP_SERIAL', 'Frame Relay': 'DLT_FRELAY'}, 'link_type': '" + "serial', 'name': 'Serial2/1', 'port_number': 1, 'short_name': 's2/1'}, {'" + "adapter_number': 2, 'data_link_types': {'Cisco HDLC': 'DLT_C_HDLC', 'Cisco " + "PPP': 'DLT_PPP_SERIAL', 'Frame Relay': 'DLT_FRELAY'}, 'link_type': 'serial', '" + "name': 'Serial2/2', 'port_number': 2, 'short_name': 's2/2'}, {'adapter_number" + "': 2, 'data_link_types': {'Cisco HDLC': 'DLT_C_HDLC', 'Cisco PPP': '" + "DLT_PPP_SERIAL', 'Frame Relay': 'DLT_FRELAY'}, 'link_type': 'serial', 'name': " + "'Serial2/3', 'port_number': 3, 'short_name': 's2/3'}, {'adapter_number': 3, " + "'data_link_types': {'Cisco HDLC': 'DLT_C_HDLC', 'Cisco PPP': 'DLT_PPP_SERIAL" + "', 'Frame Relay': 'DLT_FRELAY'}, 'link_type': 'serial', 'name': 'Serial3/0', '" + "port_number': 0, 'short_name': 's3/0'}, {'adapter_number': 3, '" + "data_link_types': {'Cisco HDLC': 'DLT_C_HDLC', 'Cisco PPP': 'DLT_PPP_SERIAL', " + "'Frame Relay': 'DLT_FRELAY'}, 'link_type': 'serial', 'name': 'Serial3/1', '" + "port_number': 1, 'short_name': 's3/1'}, {'adapter_number': 3, 'data_link_types" + "': {'Cisco HDLC': 'DLT_C_HDLC', 'Cisco PPP': 'DLT_PPP_SERIAL', 'Frame Relay': " + "'DLT_FRELAY'}, 'link_type': 'serial', 'name': 'Serial3/2', 'port_number': 2, '" + "short_name': 's3/2'}, {'adapter_number': 3, 'data_link_types': {'Cisco HDLC': " + "'DLT_C_HDLC', 'Cisco PPP': 'DLT_PPP_SERIAL', 'Frame Relay': 'DLT_FRELAY'}, '" + "link_type': 'serial', 'name': 'Serial3/3', 'port_number': 3, 'short_name': 's3" + "/3'}], port_name_format='Ethernet{segment0}/{port0}', port_segment_size=4, " + "first_port_name=None, locked=False, label={'rotation': 0, 'style': 'font-" + "family: TypeWriter;font-size: 10.0;font-weight: bold;fill: #000000;fill-" + "opacity: 1.0;', 'text': 'IOU2', 'x': 13, 'y': -25}, console='5002', " + "console_host='0.0.0.0', console_type='telnet', console_auto_start=False, " + "command_line='/opt/gns3/images/IOU/L3-ADVENTERPRISEK9-M-15.4-2T.bin 2', " + "custom_adapters=[], height=60, width=60, symbol=':/symbols/affinity/circle/" + "gray/router.svg', x=-183, y=6, z=1, template_id='8504c605-7914-4a8f-9cd4-" + "a2638382db0e', properties={'application_id': 2, 'ethernet_adapters': 2, '" + "l1_keepalives': False, 'md5sum': '50d1c5aaf1976e4622daf9eaa2632212', 'nvram': " + "64, 'path': 'L3-ADVENTERPRISEK9-M-15.4-2T.bin', 'ram': 256, 'serial_adapters" + "': 2, 'usage': '', 'use_default_iou_values': True})" + ), + ( + "Node(name='vEOS-4.21.5F-1', project_id='4b21dfb3-675a-4efa-8613-2f7fb32e76fe" + "', node_id='8283b923-df0e-4bc1-8199-be6fea40f500', compute_id='local', " + "node_type='qemu', node_directory='/opt/gns3/projects/4b21dfb3-675a-4efa-8613-" + "2f7fb32e76fe/project-files/qemu/8283b923-df0e-4bc1-8199-be6fea40f500', status" + "='started', ports=[{'adapter_number': 0, 'adapter_type': 'e1000', '" + "data_link_types': {'Ethernet': 'DLT_EN10MB'}, 'link_type': 'ethernet', '" + "mac_address': '0c:76:fe:f5:00:00', 'name': 'Management1', 'port_number': 0, " + "'short_name': 'Management1'}, {'adapter_number': 1, 'adapter_type': 'e1000', " + "'data_link_types': {'Ethernet': 'DLT_EN10MB'}, 'link_type': 'ethernet', '" + "mac_address': '0c:76:fe:f5:00:01', 'name': 'Ethernet0', 'port_number': 0, '" + "short_name': 'e0'}, {'adapter_number': 2, 'adapter_type': 'e1000', '" + "data_link_types': {'Ethernet': 'DLT_EN10MB'}, 'link_type': 'ethernet', '" + "mac_address': '0c:76:fe:f5:00:02', 'name': 'Ethernet1', 'port_number': 0, '" + "short_name': 'e1'}, {'adapter_number': 3, 'adapter_type': 'e1000', '" + "data_link_types': {'Ethernet': 'DLT_EN10MB'}, 'link_type': 'ethernet', '" + "mac_address': '0c:76:fe:f5:00:03', 'name': 'Ethernet2', 'port_number': 0, '" + "short_name': 'e2'}, {'adapter_number': 4, 'adapter_type': 'e1000', '" + "data_link_types': {'Ethernet': 'DLT_EN10MB'}, 'link_type': 'ethernet', '" + "mac_address': '0c:76:fe:f5:00:04', 'name': 'Ethernet3', 'port_number': 0, '" + "short_name': 'e3'}, {'adapter_number': 5, 'adapter_type': 'e1000', '" + "data_link_types': {'Ethernet': 'DLT_EN10MB'}, 'link_type': 'ethernet', '" + "mac_address': '0c:76:fe:f5:00:05', 'name': 'Ethernet4', 'port_number': 0, '" + "short_name': 'e4'}, {'adapter_number': 6, 'adapter_type': 'e1000', '" + "data_link_types': {'Ethernet': 'DLT_EN10MB'}, 'link_type': 'ethernet', '" + "mac_address': '0c:76:fe:f5:00:06', 'name': 'Ethernet5', 'port_number': 0, '" + "short_name': 'e5'}, {'adapter_number': 7, 'adapter_type': 'e1000', '" + "data_link_types': {'Ethernet': 'DLT_EN10MB'}, 'link_type': 'ethernet', '" + "mac_address': '0c:76:fe:f5:00:07', 'name': 'Ethernet6', 'port_number': 0, '" + "short_name': 'e6'}, {'adapter_number': 8, 'adapter_type': 'e1000', '" + "data_link_types': {'Ethernet': 'DLT_EN10MB'}, 'link_type': 'ethernet', '" + "mac_address': '0c:76:fe:f5:00:08', 'name': 'Ethernet7', 'port_number': 0, '" + "short_name': 'e7'}, {'adapter_number': 9, 'adapter_type': 'e1000', '" + "data_link_types': {'Ethernet': 'DLT_EN10MB'}, 'link_type': 'ethernet', '" + "mac_address': '0c:76:fe:f5:00:09', 'name': 'Ethernet8', 'port_number': 0, '" + "short_name': 'e8'}, {'adapter_number': 10, 'adapter_type': 'e1000', '" + "data_link_types': {'Ethernet': 'DLT_EN10MB'}, 'link_type': 'ethernet', '" + "mac_address': '0c:76:fe:f5:00:0a', 'name': 'Ethernet9', 'port_number': 0, '" + "short_name': 'e9'}, {'adapter_number': 11, 'adapter_type': 'e1000', '" + "data_link_types': {'Ethernet': 'DLT_EN10MB'}, 'link_type': 'ethernet', '" + "mac_address': '0c:76:fe:f5:00:0b', 'name': 'Ethernet10', 'port_number': 0, '" + "short_name': 'e10'}, {'adapter_number': 12, 'adapter_type': 'e1000', '" + "data_link_types': {'Ethernet': 'DLT_EN10MB'}, 'link_type': 'ethernet', '" + "mac_address': '0c:76:fe:f5:00:0c', 'name': 'Ethernet11', 'port_number': 0, '" + "short_name': 'e11'}], port_name_format='Ethernet{0}', port_segment_size=0, " + "first_port_name='Management1', locked=False, label={'rotation': 0, 'style': '" + "font-family: TypeWriter;font-size: 10.0;font-weight: bold;fill: #000000;fill-" + "opacity: 1.0;', 'text': 'vEOS-4.21.5F-1', 'x': -15, 'y': -25}, console='5003" + "', console_host='0.0.0.0', console_type='telnet', console_auto_start=False, " + "command_line='/usr/bin/qemu-system-x86_64 -name vEOS-4.21.5F-1 -m 2048M -smp " + "cpus=2 -enable-kvm -machine smm=off -boot order=c -drive file=/opt/gns3/" + "projects/4b21dfb3-675a-4efa-8613-2f7fb32e76fe/project-files/qemu/" + "8283b923-df0e-4bc1-8199-be6fea40f500/hda_disk.qcow2,if=ide,index=0,media=disk " + "-uuid 8283b923-df0e-4bc1-8199-be6fea40f500 -serial telnet:127.0.0.1:5004," + "server,nowait -monitor tcp:127.0.0.1:35261,server,nowait -net none -device " + "e1000,mac=0c:76:fe:f5:00:00,netdev=gns3-0 -netdev socket,id=gns3-0,udp=127.0." + "0.1:10011,localaddr=127.0.0.1:10010 -device e1000,mac=0c:76:fe:f5:00:01," + "netdev=gns3-1 -netdev socket,id=gns3-1,udp=127.0.0.1:10013,localaddr=127.0.0.1" + ":10012 -device e1000,mac=0c:76:fe:f5:00:02,netdev=gns3-2 -netdev socket,id=" + "gns3-2,udp=127.0.0.1:10015,localaddr=127.0.0.1:10014 -device e1000,mac=0c:76:" + "fe:f5:00:03,netdev=gns3-3 -netdev socket,id=gns3-3,udp=127.0.0.1:10017," + "localaddr=127.0.0.1:10016 -device e1000,mac=0c:76:fe:f5:00:04,netdev=gns3-4 -" + "netdev socket,id=gns3-4,udp=127.0.0.1:10019,localaddr=127.0.0.1:10018 -device " + "e1000,mac=0c:76:fe:f5:00:05,netdev=gns3-5 -netdev socket,id=gns3-5,udp=127.0.0" + ".1:10021,localaddr=127.0.0.1:10020 -device e1000,mac=0c:76:fe:f5:00:06,netdev=" + "gns3-6 -netdev socket,id=gns3-6,udp=127.0.0.1:10023,localaddr=127.0.0.1:10022 " + "-device e1000,mac=0c:76:fe:f5:00:07,netdev=gns3-7 -netdev socket,id=gns3-7," + "udp=127.0.0.1:10025,localaddr=127.0.0.1:10024 -device e1000,mac=0c:76:fe:f5:" + "00:08,netdev=gns3-8 -netdev socket,id=gns3-8,udp=127.0.0.1:10027,localaddr=" + "127.0.0.1:10026 -device e1000,mac=0c:76:fe:f5:00:09,netdev=gns3-9 -netdev " + "socket,id=gns3-9,udp=127.0.0.1:10029,localaddr=127.0.0.1:10028 -device e1000," + "mac=0c:76:fe:f5:00:0a,netdev=gns3-10 -netdev socket,id=gns3-10,udp=127.0.0.1:" + "10031,localaddr=127.0.0.1:10030 -device e1000,mac=0c:76:fe:f5:00:0b,netdev=" + "gns3-11 -netdev socket,id=gns3-11,udp=127.0.0.1:10033,localaddr=127.0.0.1:" + "10032 -device e1000,mac=0c:76:fe:f5:00:0c,netdev=gns3-12 -netdev socket,id=" + "gns3-12,udp=127.0.0.1:10035,localaddr=127.0.0.1:10034 -nographic', " + "custom_adapters=[], height=60, width=60, symbol=':/symbols/affinity/square/" + "gray/switch_multilayer.svg', x=-20, y=-6, z=1, " + "template_id='c6203d4b-d0ce-4951-bf18-c44369d46804', properties={'adapter_type" + "': 'e1000', 'adapters': 13, 'bios_image': '', 'bios_image_md5sum': None, '" + "boot_priority': 'c', 'cdrom_image': '', 'cdrom_image_md5sum': None, '" + "cpu_throttling': 0, 'cpus': 2, 'hda_disk_image': 'vEOS-lab-4.21.5F.vmdk', " + "'hda_disk_image_md5sum': '5bfd3f1b7b994c73084a38fb1e96e85b', '" + "hda_disk_interface': 'ide', 'hdb_disk_image': '', 'hdb_disk_image_md5sum': " + "None, 'hdb_disk_interface': 'ide', 'hdc_disk_image': '', '" + "hdc_disk_image_md5sum': None, 'hdc_disk_interface': 'ide', 'hdd_disk_image': " + "'', 'hdd_disk_image_md5sum': None, 'hdd_disk_interface': 'ide', 'initrd': '', " + "'initrd_md5sum': None, 'kernel_command_line': '', 'kernel_image': '', '" + "kernel_image_md5sum': None, 'legacy_networking': False, 'linked_clone': True, " + "'mac_address': '0c:76:fe:f5:00:00', 'on_close': 'power_off', 'options': '-" + "nographic', 'platform': 'x86_64', 'process_priority': 'normal', 'qemu_path': " + "'/usr/bin/qemu-system-x86_64', 'ram': 2048, 'usage': ''})" + ), + ( + "Node(name='alpine-1', project_id='4b21dfb3-675a-4efa-8613-2f7fb32e76fe', " + "node_id='ef503c45-e998-499d-88fc-2765614b313e', compute_id='local', node_type" + "='docker', node_directory='/opt/gns3/projects/4b21dfb3-675a-4efa-8613-" + "2f7fb32e76fe/project-files/docker/ef503c45-e998-499d-88fc-2765614b313e', " + "status='started', ports=[{'adapter_number': 0, 'data_link_types': {'Ethernet" + "': 'DLT_EN10MB'}, 'link_type': 'ethernet', 'name': 'eth0', 'port_number': 0, '" + "short_name': 'eth0'}, {'adapter_number': 1, 'data_link_types': {'Ethernet': '" + "DLT_EN10MB'}, 'link_type': 'ethernet', 'name': 'eth1', 'port_number': 0, '" + "short_name': 'eth1'}], port_name_format='Ethernet{0}', port_segment_size=0, " + "first_port_name=None, locked=False, label={'rotation': 0, 'style': 'font-" + "family: TypeWriter;font-size: 10.0;font-weight: bold;fill: #000000;fill-" + "opacity: 1.0;', 'text': 'alpine-1', 'x': 3, 'y': -25}, console='5005', " + "console_host='0.0.0.0', console_type='telnet', console_auto_start=False, " + "command_line=None, custom_adapters=[], height=60, width=60, symbol=':/symbols" + "/affinity/circle/gray/docker.svg', x=169, y=-10, z=1, " + "template_id='847e5333-6ac9-411f-a400-89838584371b', properties={'adapters': 2" + ", 'aux': 5006, 'console_http_path': '/', 'console_http_port': 80, '" + "console_resolution': '1024x768', 'container_id': " + "'a2109a13328c2a5f57a7405b43bcf791811f85c1d90267693fe3b57dfefe81d9', '" + "environment': None, 'extra_hosts': None, 'extra_volumes': [], 'image': '" + "alpine:latest', 'start_command': None, 'usage': ''})" + ), + ( + "Node(name='Cloud-1', project_id='4b21dfb3-675a-4efa-8613-2f7fb32e76fe', " + "node_id='cde85a31-c97f-4551-9596-a3ed12c08498', compute_id='local', node_type" + "='cloud', node_directory='/opt/gns3/projects/4b21dfb3-675a-4efa-8613-" + "2f7fb32e76fe/project-files/builtin/cde85a31-c97f-4551-9596-a3ed12c08498', " + "status='started', ports=[{'adapter_number': 0, 'data_link_types': {'Ethernet" + "': 'DLT_EN10MB'}, 'link_type': 'ethernet', 'name': 'eth0', 'port_number': 0, '" + "short_name': 'eth0'}, {'adapter_number': 0, 'data_link_types': {'Ethernet': '" + "DLT_EN10MB'}, 'link_type': 'ethernet', 'name': 'eth1', 'port_number': 1, '" + "short_name': 'eth1'}], port_name_format='Ethernet{0}', port_segment_size=0, " + "first_port_name=None, locked=False, label={'rotation': 0, 'style': 'font-" + "family: TypeWriter;font-size: 10.0;font-weight: bold;fill: #000000;fill-" + "opacity: 1.0;', 'text': 'Cloud-1', 'x': 54, 'y': -25}, console=None, " + "console_host='0.0.0.0', console_type=None, console_auto_start=False, " + "command_line=None, custom_adapters=[], height=71, width=159, symbol=':/symbols" + "/cloud.svg', x=150, y=-236, z=1, template_id='39e257dc-8412-3174-b6b3-" + "0ee3ed6a43e9', properties={'interfaces': [{'name': 'docker0', 'special': True" + ", 'type': 'ethernet'}, {'name': 'eth0', 'special': False, 'type': 'ethernet'}," + " {'name': 'eth1', 'special': False, 'type': 'ethernet'}, {'name': 'gns3tap0-0" + "', 'special': True, 'type': 'ethernet'}, {'name': 'lo', 'special': True, 'type" + "': 'ethernet'}, {'name': 'virbr0', 'special': True, 'type': 'ethernet'}, {'" + "name': 'virbr0-nic', 'special': True, 'type': 'ethernet'}], 'ports_mapping': " + "[{'interface': 'eth0', 'name': 'eth0', 'port_number': 0, 'type': 'ethernet'}, " + "{'interface': 'eth1', 'name': 'eth1', 'port_number': 1, 'type': 'ethernet'}], " + "'remote_console_host': '', 'remote_console_http_path': '/', '" + "remote_console_port': 23, 'remote_console_type': 'none'})" + ), +] diff --git a/tests/data/projects.json b/tests/data/projects.json new file mode 100644 index 0000000..edf09fc --- /dev/null +++ b/tests/data/projects.json @@ -0,0 +1,56 @@ +[ + { + "auto_close": false, + "auto_open": false, + "auto_start": false, + "drawing_grid_size": 25, + "filename": "test2.gns3", + "grid_size": 75, + "name": "test2", + "path": "/opt/gns3/projects/c9dc56bf-37b9-453b-8f95-2845ce8908e3", + "project_id": "c9dc56bf-37b9-453b-8f95-2845ce8908e3", + "scene_height": 1000, + "scene_width": 2000, + "show_grid": true, + "show_interface_labels": false, + "show_layers": false, + "snap_to_grid": false, + "status": "opened", + "supplier": null, + "variables": null, + "zoom": 100, + "stats": { + "drawings": 0, + "links": 9, + "nodes": 10, + "snapshots": 0 + } + }, + { + "auto_close": false, + "auto_open": false, + "auto_start": true, + "drawing_grid_size": 25, + "filename": "test_api1.gns3", + "grid_size": 75, + "name": "API_TEST", + "path": "/opt/gns3/projects/4b21dfb3-675a-4efa-8613-2f7fb32e76fe", + "project_id": "4b21dfb3-675a-4efa-8613-2f7fb32e76fe", + "scene_height": 1000, + "scene_width": 2000, + "show_grid": false, + "show_interface_labels": false, + "show_layers": false, + "snap_to_grid": false, + "status": "opened", + "supplier": null, + "variables": null, + "zoom": 100, + "stats": { + "drawings": 0, + "links": 4, + "nodes": 6, + "snapshots": 0 + } + } +] diff --git a/tests/data/projects.py b/tests/data/projects.py new file mode 100644 index 0000000..45f6877 --- /dev/null +++ b/tests/data/projects.py @@ -0,0 +1,20 @@ +PROJECTS_REPR = [ + ( + "Project(project_id='c9dc56bf-37b9-453b-8f95-2845ce8908e3', name='test2', " + "status='opened', path='/opt/gns3/projects/c9dc56bf-37b9-453b-8f95-" + "2845ce8908e3', filename='test2.gns3', auto_start=False, auto_close=False, " + "auto_open=False, drawing_grid_size=25, grid_size=75, scene_height=1000, " + "scene_width=2000, show_grid=True, show_interface_labels=False, show_layers=" + "False, snap_to_grid=False, supplier=None, variables=None, zoom=100, stats={'" + "drawings': 0, 'links': 9, 'nodes': 10, 'snapshots': 0})" + ), + ( + "Project(project_id='4b21dfb3-675a-4efa-8613-2f7fb32e76fe', name='API_TEST', " + "status='opened', path='/opt/gns3/projects/4b21dfb3-675a-4efa-8613-" + "2f7fb32e76fe', filename='test_api1.gns3', auto_start=True, auto_close=False, " + "auto_open=False, drawing_grid_size=25, grid_size=75, scene_height=1000, " + "scene_width=2000, show_grid=False, show_interface_labels=False, show_layers=" + "False, snap_to_grid=False, supplier=None, variables=None, zoom=100, stats={'" + "drawings': 0, 'links': 4, 'nodes': 6, 'snapshots': 0})" + ), +] diff --git a/tests/test_api.py b/tests/test_api.py new file mode 100644 index 0000000..d3ce1d9 --- /dev/null +++ b/tests/test_api.py @@ -0,0 +1,46 @@ +import json +import pytest +from pathlib import Path +from gns3fy import Link, Node, Project +from .data import links, nodes, projects + +DATA_FILES = Path(__file__).resolve().parent / "data" + + +@pytest.fixture +def links_data(): + with open(DATA_FILES / "links.json") as fdata: + data = json.load(fdata) + return data + + +@pytest.fixture +def nodes_data(): + with open(DATA_FILES / "nodes.json") as fdata: + data = json.load(fdata) + return data + + +@pytest.fixture +def projects_data(): + with open(DATA_FILES / "projects.json") as fdata: + data = json.load(fdata) + return data + + +class TestLink: + def test_instatiation(self, links_data): + for index, link_data in enumerate(links_data): + assert links.LINKS_REPR[index] == repr(Link(**link_data)) + + +class TestNode: + def test_instatiation(self, nodes_data): + for index, node_data in enumerate(nodes_data): + assert nodes.NODES_REPR[index] == repr(Node(**node_data)) + + +class TestProject: + def test_instatiation(self, projects_data): + for index, project_data in enumerate(projects_data): + assert projects.PROJECTS_REPR[index] == repr(Project(**project_data))