diff --git a/gns3server/controller/link.py b/gns3server/controller/link.py index 4d04e1ef..e81de665 100644 --- a/gns3server/controller/link.py +++ b/gns3server/controller/link.py @@ -42,6 +42,7 @@ class Link: self._capture_file_name = None self._streaming_pcap = None self._created = False + self._link_type = "ethernet" @property def created(self): @@ -56,11 +57,19 @@ class Link: Add a node to the link """ + port = node.get_port(adapter_number, port_number) + self._link_type = port.link_type + for other_node in self._nodes: if node.node_type in ["nat", "cloud"]: if other_node["node"].node_type in ["nat", "cloud"]: raise aiohttp.web.HTTPConflict(text="It's not allowed to connect a {} to a {}".format(other_node["node"].node_type, node.node_type)) + # Check if user is not connecting serial => ethernet + other_port = other_node["node"].get_port(other_node["adapter_number"], other_node["port_number"]) + if port.link_type != other_port.link_type: + raise aiohttp.web.HTTPConflict(text="It's not allowed to connect a {} to a {}".format(other_port.link_type, port.link_type)) + if label is None: label = { "x": -10, @@ -225,5 +234,6 @@ class Link: "project_id": self._project.id, "capturing": self._capturing, "capture_file_name": self._capture_file_name, - "capture_file_path": self.capture_file_path + "capture_file_path": self.capture_file_path, + "link_type": self._link_type } diff --git a/gns3server/controller/node.py b/gns3server/controller/node.py index d05cf626..c8aa8a2d 100644 --- a/gns3server/controller/node.py +++ b/gns3server/controller/node.py @@ -70,6 +70,7 @@ class Node: self._x = 0 self._y = 0 self._z = 0 + self._ports = None self._symbol = None if node_type == "iou": self._port_name_format = "Ethernet{segment0}/{port0}" @@ -159,6 +160,12 @@ class Node: """ return self._compute.host + @property + def ports(self): + if self._ports is None: + self._list_ports() + return self._ports + @property def x(self): return self._x @@ -303,6 +310,7 @@ class Node: else: setattr(self, prop, kwargs[prop]) + self._list_ports() # We send notif only if object has changed if old_json != self.__json__(): self.project.controller.notification.emit("node.updated", self.__json__()) @@ -333,6 +341,7 @@ class Node: pass else: self._properties[key] = value + self._list_ports() def _node_data(self, properties=None): """ @@ -457,26 +466,38 @@ class Node: """ return (yield from self._compute.get("/projects/{}/{}/nodes/{}/idlepc_proposals".format(self._project.id, self._node_type, self._id), timeout=240)).json + def get_port(self, adapter_number, port_number): + """ + Return the port for this adapter_number and port_number + or raise an HTTPNotFound + """ + for port in self.ports: + if port.adapter_number == adapter_number and port.port_number == port_number: + return port + raise aiohttp.web.HTTPNotFound(text="Port {}/{} for {} not found".format(adapter_number, port_number, self.name)) + def _list_ports(self): """ Generate the list of port display in the client if the compute has sent a list we return it (use by node where you can not personnalize the port naming). """ - ports = [] + self._ports = [] # Some special cases if self._node_type == "atm_switch": for adapter_number in range(0, len(self.properties["mappings"])): - ports.append(PortFactory("ATM{}".format(adapter_number), adapter_number, adapter_number, 0, "atm")) - return ports + self._ports.append(PortFactory("ATM{}".format(adapter_number), adapter_number, adapter_number, 0, "atm")) + return elif self._node_type == "frame_relay_switch": for adapter_number in range(0, len(self.properties["mappings"])): - ports.append(PortFactory("FrameRelay{}".format(adapter_number), adapter_number, adapter_number, 0, "frame_relay")) - return ports + self._ports.append(PortFactory("FrameRelay{}".format(adapter_number), adapter_number, adapter_number, 0, "frame_relay")) + return elif self._node_type == "dynamips": - return DynamipsPortFactory(self.properties) + self._ports = DynamipsPortFactory(self.properties) + return else: - return StandardPortFactory(self.properties, self._port_by_adapter, self._first_port_name, self._port_name_format, self._port_segment_size) + self._ports = StandardPortFactory(self.properties, self._port_by_adapter, self._first_port_name, self._port_name_format, self._port_segment_size) + return def __repr__(self): return "".format(self._node_type, self._name) @@ -533,5 +554,5 @@ class Node: "port_name_format": self._port_name_format, "port_segment_size": self._port_segment_size, "first_port_name": self._first_port_name, - "ports": [port.__json__() for port in self._list_ports()] + "ports": [port.__json__() for port in self.ports] } diff --git a/gns3server/controller/ports/atm_port.py b/gns3server/controller/ports/atm_port.py index fcdd03a8..b40d67b5 100644 --- a/gns3server/controller/ports/atm_port.py +++ b/gns3server/controller/ports/atm_port.py @@ -19,43 +19,31 @@ ATM port for ATM link end points. """ -from .port import Port +from .serial_port import SerialPort -class ATMPort(Port): +class ATMPort(SerialPort): @staticmethod - def longNameType(): + def long_name_type(): """ Returns the long name type for this port. :returns: string """ - return "ATM" - @staticmethod - def shortNameType(): + @property + def short_name_type(self): """ Returns the short name type for this port. :returns: string """ - return "a" - @staticmethod - def linkType(): - """ - Returns the link type to be used to connect this port. - - :returns: string - """ - - return "Serial" - - @staticmethod - def dataLinkTypes(): + @property + def data_link_types(self): """ Returns the supported PCAP DLTs. diff --git a/gns3server/controller/ports/ethernet_port.py b/gns3server/controller/ports/ethernet_port.py index 20740a3e..6847998b 100644 --- a/gns3server/controller/ports/ethernet_port.py +++ b/gns3server/controller/ports/ethernet_port.py @@ -30,7 +30,7 @@ class EthernetPort(Port): """ @staticmethod - def longNameType(): + def long_name_type(): """ Returns the long name type for this port. @@ -39,8 +39,8 @@ class EthernetPort(Port): return "Ethernet" - @staticmethod - def shortNameType(): + @property + def short_name_type(self): """ Returns the short name type for this port. diff --git a/gns3server/controller/ports/fastethernet_port.py b/gns3server/controller/ports/fastethernet_port.py index 82be8541..a8f98780 100644 --- a/gns3server/controller/ports/fastethernet_port.py +++ b/gns3server/controller/ports/fastethernet_port.py @@ -25,7 +25,7 @@ from .port import Port class FastEthernetPort(Port): @staticmethod - def longNameType(): + def long_name_type(): """ Returns the long name type for this port. @@ -34,8 +34,8 @@ class FastEthernetPort(Port): return "FastEthernet" - @staticmethod - def shortNameType(): + @property + def short_name_type(self): """ Returns the short name type for this port. diff --git a/gns3server/controller/ports/frame_relay_port.py b/gns3server/controller/ports/frame_relay_port.py index b6b5b28b..87049917 100644 --- a/gns3server/controller/ports/frame_relay_port.py +++ b/gns3server/controller/ports/frame_relay_port.py @@ -25,7 +25,7 @@ from .serial_port import SerialPort class FrameRelayPort(SerialPort): @staticmethod - def longNameType(): + def long_name_type(): """ Returns the long name type for this port. @@ -34,8 +34,8 @@ class FrameRelayPort(SerialPort): return "FrameRelay" - @staticmethod - def dataLinkTypes(): + @property + def data_link_types(self): """ Returns the supported PCAP DLTs. diff --git a/gns3server/controller/ports/gigabitethernet_port.py b/gns3server/controller/ports/gigabitethernet_port.py index 3dd6df5c..c96b820f 100644 --- a/gns3server/controller/ports/gigabitethernet_port.py +++ b/gns3server/controller/ports/gigabitethernet_port.py @@ -25,7 +25,7 @@ from .port import Port class GigabitEthernetPort(Port): @staticmethod - def longNameType(): + def long_name_type(): """ Returns the long name type for this port. @@ -34,8 +34,8 @@ class GigabitEthernetPort(Port): return "GigabitEthernet" - @staticmethod - def shortNameType(): + @property + def short_name_type(self): """ Returns the short name type for this port. diff --git a/gns3server/controller/ports/port.py b/gns3server/controller/ports/port.py index e0f40aa8..108c0f8a 100644 --- a/gns3server/controller/ports/port.py +++ b/gns3server/controller/ports/port.py @@ -27,8 +27,16 @@ class Port: self._port_number = port_number self._name = name - @staticmethod - def dataLinkTypes(): + @property + def adapter_number(self): + return self._adapter_number + + @property + def port_number(self): + return self._port_number + + @property + def data_link_types(self): """ Returns the supported PCAP DLTs. @@ -36,16 +44,16 @@ class Port: """ return {"Ethernet": "DLT_EN10MB"} - @staticmethod - def linkType(): - return "Ethernet" + @property + def link_type(self): + return "ethernet" def __json__(self): return { "name": self._name, - "short_name": self.shortNameType() + "{}/{}".format(self._interface_number, self._port_number), - "data_link_types": self.dataLinkTypes(), + "short_name": self.short_name_type + "{}/{}".format(self._interface_number, self._port_number), + "data_link_types": self.data_link_types, "port_number": self._port_number, "adapter_number": self._adapter_number, - "link_type": self.linkType().lower() + "link_type": self.link_type } diff --git a/gns3server/controller/ports/port_factory.py b/gns3server/controller/ports/port_factory.py index b85ab3bf..d6a991f1 100644 --- a/gns3server/controller/ports/port_factory.py +++ b/gns3server/controller/ports/port_factory.py @@ -183,7 +183,7 @@ class DynamipsPortFactory: interface_numbers.setdefault(port_class, 0) interface_number = interface_numbers[port_class] for port_number in range(0, cls.ADAPTER_MATRIX[properties[name]]["nb_ports"]): - name = "{}{}/{}".format(port_class.longNameType(), interface_number, port_number) + name = "{}{}/{}".format(port_class.long_name_type(), interface_number, port_number) ports.append(port_class(name, interface_number, adapter_number, port_number)) interface_numbers[port_class] += 1 adapter_number += 1 @@ -191,7 +191,7 @@ class DynamipsPortFactory: port_class = cls.WIC_MATRIX[properties[name]]["port"] if port_class: for port_number in range(0, cls.WIC_MATRIX[properties[name]]["nb_ports"]): - name = "{}{}/{}".format(port_class.longNameType(), 0, wic_port_number) + name = "{}{}/{}".format(port_class.long_name_type(), 0, wic_port_number) ports.append(port_class(name, 0, 0, wic_port_number)) wic_port_number += 1 return ports diff --git a/gns3server/controller/ports/pos_port.py b/gns3server/controller/ports/pos_port.py index aca3e29a..152e1f62 100644 --- a/gns3server/controller/ports/pos_port.py +++ b/gns3server/controller/ports/pos_port.py @@ -19,43 +19,31 @@ POS port for Packet over SONET link end points. """ -from .port import Port +from .serial_port import SerialPort -class POSPort(Port): +class POSPort(SerialPort): @staticmethod - def longNameType(): + def long_name_type(): """ Returns the long name type for this port. :returns: string """ - return "POS" - @staticmethod - def shortNameType(): + @property + def short_name_type(self): """ Returns the short name type for this port. :returns: string """ - return "p" - @staticmethod - def linkType(): - """ - Returns the link type to be used to connect this port. - - :returns: string - """ - - return "Serial" - - @staticmethod - def dataLinkTypes(): + @property + def data_link_types(self): """ Returns the supported PCAP DLTs. diff --git a/gns3server/controller/ports/serial_port.py b/gns3server/controller/ports/serial_port.py index 980f3574..090f3a47 100644 --- a/gns3server/controller/ports/serial_port.py +++ b/gns3server/controller/ports/serial_port.py @@ -25,7 +25,7 @@ from .port import Port class SerialPort(Port): @staticmethod - def longNameType(): + def long_name_type(): """ Returns the long name type for this port. @@ -34,8 +34,8 @@ class SerialPort(Port): return "Serial" - @staticmethod - def shortNameType(): + @property + def short_name_type(self): """ Returns the short name type for this port. @@ -44,18 +44,18 @@ class SerialPort(Port): return "s" - @staticmethod - def linkType(): + @property + def link_type(self): """ Returns the link type to be used to connect this port. :returns: string """ - return "Serial" + return "serial" - @staticmethod - def dataLinkTypes(): + @property + def data_link_types(self): """ Returns the supported PCAP DLTs. diff --git a/gns3server/schemas/link.py b/gns3server/schemas/link.py index ba64e854..13e1af43 100644 --- a/gns3server/schemas/link.py +++ b/gns3server/schemas/link.py @@ -75,6 +75,10 @@ LINK_OBJECT_SCHEMA = { "capture_file_path": { "description": "Read only property. The full path of the capture file if capture is running", "type": ["string", "null"] + }, + "link_type": { + "description": "Type of link", + "enum": ["ethernet", "serial"] } }, "required": ["nodes"], diff --git a/tests/controller/test_link.py b/tests/controller/test_link.py index db496e4b..0c92fdca 100644 --- a/tests/controller/test_link.py +++ b/tests/controller/test_link.py @@ -24,6 +24,8 @@ from unittest.mock import MagicMock from gns3server.controller.link import Link from gns3server.controller.node import Node +from gns3server.controller.ports.ethernet_port import EthernetPort +from gns3server.controller.ports.serial_port import SerialPort from gns3server.controller.compute import Compute from gns3server.controller.project import Project @@ -43,7 +45,9 @@ def compute(): @pytest.fixture def link(async_run, project, compute): node1 = Node(project, compute, "node1", node_type="qemu") + node1._ports = [EthernetPort("E0", 0, 0, 4)] node2 = Node(project, compute, "node2", node_type="qemu") + node2._ports = [EthernetPort("E0", 0, 1, 3)] link = Link(project) link.create = AsyncioMagicMock() @@ -60,6 +64,7 @@ def test_eq(project, link, controller): def test_add_node(async_run, project, compute): node1 = Node(project, compute, "node1", node_type="qemu") + node1._ports = [EthernetPort("E0", 0, 0, 4)] link = Link(project) link.create = AsyncioMagicMock() @@ -87,6 +92,7 @@ def test_add_node(async_run, project, compute): # We call link.created only when both side are created node2 = Node(project, compute, "node2", node_type="qemu") + node2._ports = [EthernetPort("E0", 0, 0, 4)] async_run(link.add_node(node2, 0, 4)) assert link.create.called @@ -95,7 +101,9 @@ def test_add_node(async_run, project, compute): def test_add_node_cloud(async_run, project, compute): node1 = Node(project, compute, "node1", node_type="qemu") + node1._ports = [EthernetPort("E0", 0, 0, 4)] node2 = Node(project, compute, "node2", node_type="cloud") + node2._ports = [EthernetPort("E0", 0, 0, 4)] link = Link(project) link.create = AsyncioMagicMock() @@ -110,7 +118,9 @@ def test_add_node_cloud_to_cloud(async_run, project, compute): Cloud to cloud connection is not allowed """ node1 = Node(project, compute, "node1", node_type="cloud") + node1._ports = [EthernetPort("E0", 0, 0, 4)] node2 = Node(project, compute, "node2", node_type="cloud") + node2._ports = [EthernetPort("E0", 0, 0, 4)] link = Link(project) link.create = AsyncioMagicMock() @@ -121,9 +131,29 @@ def test_add_node_cloud_to_cloud(async_run, project, compute): async_run(link.add_node(node2, 0, 4)) -def test_json(async_run, project, compute): +def test_add_node_serial_to_ethernet(async_run, project, compute): + """ + Serial to ethernet connection is not allowed + """ node1 = Node(project, compute, "node1", node_type="qemu") + node1._ports = [EthernetPort("E0", 0, 0, 4)] node2 = Node(project, compute, "node2", node_type="qemu") + node2._ports = [SerialPort("E0", 0, 0, 4)] + + link = Link(project) + link.create = AsyncioMagicMock() + link._project.controller.notification.emit = MagicMock() + + async_run(link.add_node(node1, 0, 4)) + with pytest.raises(aiohttp.web.HTTPConflict): + async_run(link.add_node(node2, 0, 4)) + + +def test_json(async_run, project, compute, link): + node1 = Node(project, compute, "node1", node_type="qemu") + node1._ports = [EthernetPort("E0", 0, 0, 4)] + node2 = Node(project, compute, "node2", node_type="qemu") + node2._ports = [EthernetPort("E0", 0, 1, 3)] link = Link(project) link.create = AsyncioMagicMock() @@ -158,6 +188,7 @@ def test_json(async_run, project, compute): } } ], + "link_type": "ethernet", "capturing": False, "capture_file_name": None, "capture_file_path": None @@ -193,6 +224,19 @@ def test_json(async_run, project, compute): } +def test_json_serial_link(async_run, project, compute, link): + node1 = Node(project, compute, "node1", node_type="qemu") + node1._ports = [SerialPort("S0", 0, 0, 4)] + node2 = Node(project, compute, "node2", node_type="qemu") + node2._ports = [SerialPort("S0", 0, 1, 3)] + + link = Link(project) + link.create = AsyncioMagicMock() + async_run(link.add_node(node1, 0, 4)) + async_run(link.add_node(node2, 1, 3)) + assert link.__json__()["link_type"] == "serial" + + def test_start_streaming_pcap(link, async_run, tmpdir, project): @asyncio.coroutine def fake_reader(): @@ -212,7 +256,9 @@ def test_start_streaming_pcap(link, async_run, tmpdir, project): def test_default_capture_file_name(project, compute, async_run): node1 = Node(project, compute, "Hello@", node_type="qemu") + node1._ports = [EthernetPort("E0", 0, 0, 4)] node2 = Node(project, compute, "w0.rld", node_type="qemu") + node2._ports = [EthernetPort("E0", 0, 1, 3)] link = Link(project) link.create = AsyncioMagicMock() diff --git a/tests/controller/test_node.py b/tests/controller/test_node.py index 4141ad9d..5e3d06df 100644 --- a/tests/controller/test_node.py +++ b/tests/controller/test_node.py @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import aiohttp import pytest import uuid import asyncio @@ -420,3 +421,15 @@ def test_update_label(node): node.label = {"text": "Wrong", "x": 12} assert node.label["text"] == "Test" assert node.label["x"] == 12 + + +def test_get_port(node): + node.properties["adapters"] = 2 + node._list_ports() + port = node.get_port(0, 0) + assert port.adapter_number == 0 + assert port.port_number == 0 + port = node.get_port(1, 0) + assert port.adapter_number == 1 + with pytest.raises(aiohttp.web.HTTPNotFound): + port = node.get_port(42, 0) diff --git a/tests/controller/test_node_port_name.py b/tests/controller/test_node_port_name.py index 0e8a855b..d4c80fdc 100644 --- a/tests/controller/test_node_port_name.py +++ b/tests/controller/test_node_port_name.py @@ -68,20 +68,25 @@ def test_list_ports_port_name_format(node): """ node._first_port_name = None node._port_name_format = "eth{}" + node._list_ports() assert node.__json__()["ports"][0]["name"] == "eth0" node._port_name_format = "eth{port0}" + node._list_ports() assert node.__json__()["ports"][0]["name"] == "eth0" node._port_name_format = "eth{port1}" + node._list_ports() assert node.__json__()["ports"][0]["name"] == "eth1" node._first_port_name = "" node._port_segment_size = 2 node._port_name_format = "eth{segment0}/{port0}" node.properties["adapters"] = 8 + node._list_ports() assert node.__json__()["ports"][6]["name"] == "eth3/0" assert node.__json__()["ports"][7]["name"] == "eth3/1" node._first_port_name = "mgnt0" + node._list_ports() assert node.__json__()["ports"][0]["name"] == "mgnt0" assert node.__json__()["ports"][1]["name"] == "eth0/0" diff --git a/tests/controller/test_project.py b/tests/controller/test_project.py index 23914bef..3945a269 100644 --- a/tests/controller/test_project.py +++ b/tests/controller/test_project.py @@ -28,6 +28,7 @@ from unittest.mock import patch from uuid import uuid4 from gns3server.controller.project import Project +from gns3server.controller.ports.ethernet_port import EthernetPort from gns3server.config import Config @@ -250,7 +251,9 @@ def test_addLink(async_run, project, controller): compute.post = AsyncioMagicMock(return_value=response) vm1 = async_run(project.add_node(compute, "test1", None, node_type="vpcs", properties={"startup_config": "test.cfg"})) + vm1._ports = [EthernetPort("E0", 0, 3, 1)] vm2 = async_run(project.add_node(compute, "test2", None, node_type="vpcs", properties={"startup_config": "test.cfg"})) + vm2._ports = [EthernetPort("E0", 0, 4, 2)] controller._notification = MagicMock() link = async_run(project.add_link()) async_run(link.add_node(vm1, 3, 1)) diff --git a/tests/controller/test_udp_link.py b/tests/controller/test_udp_link.py index 4a905ce8..ad0f83fe 100644 --- a/tests/controller/test_udp_link.py +++ b/tests/controller/test_udp_link.py @@ -23,6 +23,7 @@ from tests.utils import asyncio_patch, AsyncioMagicMock from gns3server.controller.project import Project from gns3server.controller.udp_link import UDPLink +from gns3server.controller.ports.ethernet_port import EthernetPort from gns3server.controller.node import Node @@ -36,7 +37,9 @@ def test_create(async_run, project): compute2 = MagicMock() node1 = Node(project, compute1, "node1", node_type="vpcs") + node1._ports = [EthernetPort("E0", 0, 0, 4)] node2 = Node(project, compute2, "node2", node_type="vpcs") + node2._ports = [EthernetPort("E0", 0, 3, 1)] @asyncio.coroutine def subnet_callback(compute2): @@ -95,7 +98,9 @@ def test_delete(async_run, project): compute2 = MagicMock() node1 = Node(project, compute1, "node1", node_type="vpcs") + node1._ports = [EthernetPort("E0", 0, 0, 4)] node2 = Node(project, compute2, "node2", node_type="vpcs") + node2._ports = [EthernetPort("E0", 0, 3, 1)] link = UDPLink(project) link.create = AsyncioMagicMock() @@ -117,7 +122,9 @@ def test_choose_capture_side(async_run, project): compute2.id = "local" node_vpcs = Node(project, compute1, "node1", node_type="vpcs") + node_vpcs._ports = [EthernetPort("E0", 0, 0, 4)] node_iou = Node(project, compute2, "node2", node_type="iou") + node_iou._ports = [EthernetPort("E0", 0, 3, 1)] link = UDPLink(project) link.create = AsyncioMagicMock() @@ -127,7 +134,9 @@ def test_choose_capture_side(async_run, project): assert link._choose_capture_side()["node"] == node_iou node_vpcs = Node(project, compute1, "node3", node_type="vpcs") + node_vpcs._ports = [EthernetPort("E0", 0, 0, 4)] node_vpcs2 = Node(project, compute1, "node4", node_type="vpcs") + node_vpcs2._ports = [EthernetPort("E0", 0, 3, 1)] link = UDPLink(project) link.create = AsyncioMagicMock() @@ -136,7 +145,9 @@ def test_choose_capture_side(async_run, project): # Capture should run on the local node node_iou = Node(project, compute1, "node5", node_type="iou") + node_iou._ports = [EthernetPort("E0", 0, 0, 4)] node_iou2 = Node(project, compute2, "node6", node_type="iou") + node_iou2._ports = [EthernetPort("E0", 0, 3, 1)] link = UDPLink(project) link.create = AsyncioMagicMock() @@ -150,7 +161,9 @@ def test_capture(async_run, project): compute1 = MagicMock() node_vpcs = Node(project, compute1, "V1", node_type="vpcs") + node_vpcs._ports = [EthernetPort("E0", 0, 0, 4)] node_iou = Node(project, compute1, "I1", node_type="iou") + node_iou._ports = [EthernetPort("E0", 0, 3, 1)] link = UDPLink(project) link.create = AsyncioMagicMock() diff --git a/tests/handlers/api/controller/test_link.py b/tests/handlers/api/controller/test_link.py index 160240c9..ce7dedbe 100644 --- a/tests/handlers/api/controller/test_link.py +++ b/tests/handlers/api/controller/test_link.py @@ -31,6 +31,7 @@ from tests.utils import asyncio_patch, AsyncioMagicMock from gns3server.handlers.api.controller.project_handler import ProjectHandler from gns3server.controller import Controller +from gns3server.controller.ports.ethernet_port import EthernetPort from gns3server.controller.node import Node from gns3server.controller.link import Link @@ -54,7 +55,9 @@ def test_create_link(http_controller, tmpdir, project, compute, async_run): compute.post = AsyncioMagicMock(return_value=response) node1 = async_run(project.add_node(compute, "node1", None, node_type="qemu")) + node1._ports = [EthernetPort("E0", 0, 0, 3)] node2 = async_run(project.add_node(compute, "node2", None, node_type="qemu")) + node2._ports = [EthernetPort("E0", 0, 2, 4)] with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock: response = http_controller.post("/projects/{}/links".format(project.id), { @@ -89,7 +92,9 @@ def test_update_link(http_controller, tmpdir, project, compute, async_run): compute.post = AsyncioMagicMock(return_value=response) node1 = async_run(project.add_node(compute, "node1", None, node_type="qemu")) + node1._ports = [EthernetPort("E0", 0, 0, 3)] node2 = async_run(project.add_node(compute, "node2", None, node_type="qemu")) + node2._ports = [EthernetPort("E0", 0, 2, 4)] with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock: response = http_controller.post("/projects/{}/links".format(project.id), { @@ -142,7 +147,9 @@ def test_list_link(http_controller, tmpdir, project, compute, async_run): compute.post = AsyncioMagicMock(return_value=response) node1 = async_run(project.add_node(compute, "node1", None, node_type="qemu")) + node1._ports = [EthernetPort("E0", 0, 0, 3)] node2 = async_run(project.add_node(compute, "node2", None, node_type="qemu")) + node2._ports = [EthernetPort("E0", 0, 2, 4)] with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock: response = http_controller.post("/projects/{}/links".format(project.id), {