From 3ce3f925ae7ae3db2584f1a3e4edbde2fdbe23de Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Fri, 2 Sep 2016 14:39:38 +0200 Subject: [PATCH] Fix creation of link when reloading a project Fix https://github.com/GNS3/gns3-gui/issues/1457 --- gns3server/controller/link.py | 10 ++ .../handlers/api/controller/link_handler.py | 1 - tests/controller/test_controller.py | 71 -------- tests/controller/test_link.py | 38 +---- tests/controller/test_project.py | 4 +- tests/controller/test_project_open.py | 161 ++++++++++++++++++ tests/controller/test_topology.py | 3 +- tests/controller/test_udp_link.py | 11 +- 8 files changed, 192 insertions(+), 107 deletions(-) create mode 100644 tests/controller/test_project_open.py diff --git a/gns3server/controller/link.py b/gns3server/controller/link.py index b40228ff..4d04e1ef 100644 --- a/gns3server/controller/link.py +++ b/gns3server/controller/link.py @@ -41,6 +41,14 @@ class Link: self._capturing = False self._capture_file_name = None self._streaming_pcap = None + self._created = False + + @property + def created(self): + """ + :returns: True the link has been created on the computes + """ + return self._created @asyncio.coroutine def add_node(self, node, adapter_number, port_number, label=None): @@ -70,6 +78,8 @@ class Link: }) if len(self._nodes) == 2: + yield from self.create() + self._created = True self._project.controller.notification.emit("link.created", self.__json__()) self._project.dump() diff --git a/gns3server/handlers/api/controller/link_handler.py b/gns3server/handlers/api/controller/link_handler.py index 462302bb..20fc97cf 100644 --- a/gns3server/handlers/api/controller/link_handler.py +++ b/gns3server/handlers/api/controller/link_handler.py @@ -69,7 +69,6 @@ class LinkHandler: node.get("adapter_number", 0), node.get("port_number", 0), label=node.get("label")) - yield from link.create() response.set_status(201) response.json(link) diff --git a/tests/controller/test_controller.py b/tests/controller/test_controller.py index 483b92bd..87e3cec1 100644 --- a/tests/controller/test_controller.py +++ b/tests/controller/test_controller.py @@ -338,77 +338,6 @@ def test_stop_vm(controller, async_run): assert mock.called -def test_load_project(controller, async_run, tmpdir): - data = { - "name": "Experience", - "project_id": "c8d07a5a-134f-4c3f-8599-e35eac85eb17", - "revision": 5, - "type": "topology", - "version": "2.0.0dev1", - "topology": { - "drawings": [], - "computes": [ - { - "compute_id": "my_remote", - "host": "127.0.0.1", - "name": "My remote", - "port": 3080, - "protocol": "http", - } - ], - "links": [ - { - "link_id": "c44331d2-2da4-490d-9aad-7f5c126ae271", - "nodes": [ - {"node_id": "c067b922-7f77-4680-ac00-0226c6583598", "adapter_number": 0, "port_number": 0}, - {"node_id": "50d66d7b-0dd7-4e9f-b720-6eb621ae6543", "adapter_number": 0, "port_number": 0}, - ], - } - ], - "nodes": [ - { - "compute_id": "my_remote", - "name": "PC2", - "node_id": "c067b922-7f77-4680-ac00-0226c6583598", - "node_type": "vpcs", - "properties": { - "startup_script": "set pcname PC2\n", - "startup_script_path": "startup.vpc" - }, - }, - { - "compute_id": "my_remote", - "name": "PC1", - "node_id": "50d66d7b-0dd7-4e9f-b720-6eb621ae6543", - "node_type": "vpcs", - "properties": { - "startup_script": "set pcname PC1\n", - "startup_script_path": "startup.vpc" - }, - } - ] - } - } - with open(str(tmpdir / "test.gns3"), "w+") as f: - json.dump(data, f) - controller.add_compute = AsyncioMagicMock() - controller._computes["my_remote"] = MagicMock() - - with asyncio_patch("gns3server.controller.node.Node.create") as mock_node_create: - project = async_run(controller.load_project(str(tmpdir / "test.gns3"))) - - assert project._topology_file() == str(tmpdir / "test.gns3") - controller.add_compute.assert_called_with(compute_id='my_remote', host='127.0.0.1', name='My remote', port=3080, protocol='http') - project = controller.get_project('c8d07a5a-134f-4c3f-8599-e35eac85eb17') - assert project.name == "Experience" - assert project.path == str(tmpdir) - link = project.get_link("c44331d2-2da4-490d-9aad-7f5c126ae271") - assert len(link.nodes) == 2 - - node1 = project.get_node("50d66d7b-0dd7-4e9f-b720-6eb621ae6543") - assert node1.name == "PC1" - - def test_get_free_project_name(controller, async_run): async_run(controller.add_project(project_id=str(uuid.uuid4()), name="Test")) diff --git a/tests/controller/test_link.py b/tests/controller/test_link.py index 0aa4988a..db496e4b 100644 --- a/tests/controller/test_link.py +++ b/tests/controller/test_link.py @@ -46,6 +46,7 @@ def link(async_run, project, compute): node2 = Node(project, compute, "node2", node_type="qemu") link = Link(project) + link.create = AsyncioMagicMock() async_run(link.add_node(node1, 0, 4)) async_run(link.add_node(node2, 1, 3)) return link @@ -61,6 +62,7 @@ def test_add_node(async_run, project, compute): node1 = Node(project, compute, "node1", node_type="qemu") link = Link(project) + link.create = AsyncioMagicMock() link._project.controller.notification.emit = MagicMock() project.dump = AsyncioMagicMock() async_run(link.add_node(node1, 0, 4)) @@ -81,41 +83,13 @@ def test_add_node(async_run, project, compute): assert project.dump.called assert not link._project.controller.notification.emit.called - # We call link.created only when both side are created - node2 = Node(project, compute, "node2", node_type="qemu") - async_run(link.add_node(node2, 0, 4)) - - link._project.controller.notification.emit.assert_called_with("link.created", link.__json__()) - - -def test_add_node(async_run, project, compute): - node1 = Node(project, compute, "node1", node_type="qemu") - - link = Link(project) - link._project.controller.notification.emit = MagicMock() - project.dump = AsyncioMagicMock() - async_run(link.add_node(node1, 0, 4)) - assert link._nodes == [ - { - "node": node1, - "adapter_number": 0, - "port_number": 4, - 'label': { - 'y': -10, - 'text': '0/4', - 'x': -10, - 'rotation': 0, - 'style': 'font-size: 10; font-style: Verdana' - } - } - ] - assert project.dump.called - assert not link._project.controller.notification.emit.called + assert not link.create.called # We call link.created only when both side are created node2 = Node(project, compute, "node2", node_type="qemu") async_run(link.add_node(node2, 0, 4)) + assert link.create.called link._project.controller.notification.emit.assert_called_with("link.created", link.__json__()) @@ -124,6 +98,7 @@ def test_add_node_cloud(async_run, project, compute): node2 = Node(project, compute, "node2", node_type="cloud") link = Link(project) + link.create = AsyncioMagicMock() link._project.controller.notification.emit = MagicMock() async_run(link.add_node(node1, 0, 4)) @@ -138,6 +113,7 @@ def test_add_node_cloud_to_cloud(async_run, project, compute): node2 = Node(project, compute, "node2", node_type="cloud") link = Link(project) + link.create = AsyncioMagicMock() link._project.controller.notification.emit = MagicMock() async_run(link.add_node(node1, 0, 4)) @@ -150,6 +126,7 @@ def test_json(async_run, project, compute): node2 = Node(project, compute, "node2", node_type="qemu") 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__() == { @@ -238,6 +215,7 @@ def test_default_capture_file_name(project, compute, async_run): node2 = Node(project, compute, "w0.rld", node_type="qemu") link = Link(project) + link.create = AsyncioMagicMock() async_run(link.add_node(node1, 0, 4)) async_run(link.add_node(node2, 1, 3)) assert link.default_capture_file_name() == "Hello_0-4_to_w0rld_1-3.pcap" diff --git a/tests/controller/test_project.py b/tests/controller/test_project.py index 5194162b..23914bef 100644 --- a/tests/controller/test_project.py +++ b/tests/controller/test_project.py @@ -254,7 +254,9 @@ def test_addLink(async_run, project, controller): controller._notification = MagicMock() link = async_run(project.add_link()) async_run(link.add_node(vm1, 3, 1)) - async_run(link.add_node(vm2, 4, 2)) + with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock_udp_create: + async_run(link.add_node(vm2, 4, 2)) + assert mock_udp_create.called assert len(link._nodes) == 2 controller.notification.emit.assert_any_call("link.created", link.__json__()) diff --git a/tests/controller/test_project_open.py b/tests/controller/test_project_open.py new file mode 100644 index 00000000..babab288 --- /dev/null +++ b/tests/controller/test_project_open.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python +# +# Copyright (C) 2016 GNS3 Technologies Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import json +import pytest + +from tests.utils import asyncio_patch, AsyncioMagicMock + +from gns3server.controller.compute import Compute + +@pytest.fixture +def demo_topology(): + """ + A topology with two VPCS connected and a rectangle + """ + return { + "auto_close": True, + "auto_open": False, + "auto_start": False, + "name": "demo", + "project_id": "3c1be6f9-b4ba-4737-b209-63c47c23359f", + "revision": 5, + "topology": { + "computes": [ + { + "compute_id": "local", + "host": "127.0.0.1", + "name": "atlantis", + "port": 3080, + "protocol": "http" + } + ], + "drawings": [ + { + "drawing_id": "48bdaa23-326a-4de0-bf7d-cc22709689ec", + "rotation": 0, + "svg": "", + "x": -226, + "y": 57, + "z": 0 + } + ], + "links": [ + { + "link_id": "5a3e3a64-e853-4055-9503-4a14e01290f1", + "nodes": [ + { + "adapter_number": 0, + "label": { + "rotation": 0, + "style": "font-family: TypeWriter;font-size: 10;font-weight: bold;fill: #000000;fill-opacity: 1.0;", + "text": "Ethernet0", + "x": 72, + "y": 32 + }, + "node_id": "64ba8408-afbf-4b66-9cdd-1fd854427478", + "port_number": 0 + }, + { + "adapter_number": 0, + "label": { + "rotation": 0, + "style": "font-family: TypeWriter;font-size: 10;font-weight: bold;fill: #000000;fill-opacity: 1.0;", + "text": "Ethernet0", + "x": -7, + "y": 26 + }, + "node_id": "748bcd89-624a-40eb-a8d3-1d2e85c99b51", + "port_number": 0 + } + ] + } + ], + "nodes": [ + { + "compute_id": "local", + "console": 5000, + "console_type": "telnet", + "height": 59, + "label": { + "rotation": 0, + "style": "font-family: TypeWriter;font-size: 10;font-weight: bold;fill: #000000;fill-opacity: 1.0;", + "text": "PC1", + "x": 18, + "y": -25 + }, + "name": "PC1", + "node_id": "64ba8408-afbf-4b66-9cdd-1fd854427478", + "node_type": "vpcs", + "properties": { + "startup_script": "", + "startup_script_path": "startup.vpc" + }, + "symbol": ":/symbols/computer.svg", + "width": 65, + "x": -300, + "y": -118, + "z": 1 + }, + { + "compute_id": "local", + "console": 5001, + "console_type": "telnet", + "height": 59, + "label": { + "rotation": 0, + "style": "font-family: TypeWriter;font-size: 10;font-weight: bold;fill: #000000;fill-opacity: 1.0;", + "text": "PC2", + "x": 18, + "y": -25 + }, + "name": "PC2", + "node_id": "748bcd89-624a-40eb-a8d3-1d2e85c99b51", + "node_type": "vpcs", + "properties": { + "startup_script": "", + "startup_script_path": "startup.vpc" + }, + "symbol": ":/symbols/computer.svg", + "width": 65, + "x": -71, + "y": -98, + "z": 1 + } + ] + }, + "type": "topology", + "version": "2.0.0" + } + + +def test_open(controller, tmpdir, demo_topology, async_run, http_server): + with open(str(tmpdir / "demo.gns3"), "w+") as f: + json.dump(demo_topology, f) + + controller._computes["local"] = Compute("local", controller=controller, host=http_server[0], port=http_server[1]) + + project = async_run(controller.load_project(str(tmpdir / "demo.gns3"))) + assert project.status == "opened" + assert len(project.computes) == 1 + assert len(project.nodes) == 2 + assert project.nodes["64ba8408-afbf-4b66-9cdd-1fd854427478"].name == "PC1" + assert len(project.links) == 1 + assert project.links["5a3e3a64-e853-4055-9503-4a14e01290f1"].created + assert len(project.drawings) == 1 + + assert project.name == "demo" diff --git a/tests/controller/test_topology.py b/tests/controller/test_topology.py index 550b2a6e..80735cee 100644 --- a/tests/controller/test_topology.py +++ b/tests/controller/test_topology.py @@ -60,7 +60,8 @@ def test_basic_topology(tmpdir, async_run, controller): link = async_run(project.add_link()) async_run(link.add_node(node1, 0, 0)) - async_run(link.add_node(node2, 0, 0)) + with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock_udp_create: + async_run(link.add_node(node2, 0, 0)) drawing = async_run(project.add_drawing(svg="")) diff --git a/tests/controller/test_udp_link.py b/tests/controller/test_udp_link.py index 33a21c4f..4a905ce8 100644 --- a/tests/controller/test_udp_link.py +++ b/tests/controller/test_udp_link.py @@ -19,7 +19,7 @@ import pytest import asyncio import aiohttp from unittest.mock import MagicMock -from tests.utils import asyncio_patch +from tests.utils import asyncio_patch, AsyncioMagicMock from gns3server.controller.project import Project from gns3server.controller.udp_link import UDPLink @@ -49,7 +49,6 @@ def test_create(async_run, project): link = UDPLink(project) async_run(link.add_node(node1, 0, 4)) - async_run(link.add_node(node2, 3, 1)) @asyncio.coroutine def compute1_callback(path, data={}): @@ -75,7 +74,7 @@ def test_create(async_run, project): compute1.host = "example.com" compute2.post.side_effect = compute2_callback compute2.host = "example.org" - async_run(link.create()) + async_run(link.add_node(node2, 3, 1)) compute1.post.assert_any_call("/projects/{}/vpcs/nodes/{}/adapters/0/ports/4/nio".format(project.id, node1.id), data={ "lport": 1024, @@ -99,6 +98,7 @@ def test_delete(async_run, project): node2 = Node(project, compute2, "node2", node_type="vpcs") link = UDPLink(project) + link.create = AsyncioMagicMock() async_run(link.add_node(node1, 0, 4)) async_run(link.add_node(node2, 3, 1)) @@ -120,6 +120,7 @@ def test_choose_capture_side(async_run, project): node_iou = Node(project, compute2, "node2", node_type="iou") link = UDPLink(project) + link.create = AsyncioMagicMock() async_run(link.add_node(node_vpcs, 0, 4)) async_run(link.add_node(node_iou, 3, 1)) @@ -129,6 +130,7 @@ def test_choose_capture_side(async_run, project): node_vpcs2 = Node(project, compute1, "node4", node_type="vpcs") link = UDPLink(project) + link.create = AsyncioMagicMock() async_run(link.add_node(node_vpcs, 0, 4)) async_run(link.add_node(node_vpcs2, 3, 1)) @@ -137,6 +139,7 @@ def test_choose_capture_side(async_run, project): node_iou2 = Node(project, compute2, "node6", node_type="iou") link = UDPLink(project) + link.create = AsyncioMagicMock() async_run(link.add_node(node_iou, 0, 4)) async_run(link.add_node(node_iou2, 3, 1)) @@ -150,6 +153,7 @@ def test_capture(async_run, project): node_iou = Node(project, compute1, "I1", node_type="iou") link = UDPLink(project) + link.create = AsyncioMagicMock() async_run(link.add_node(node_vpcs, 0, 4)) async_run(link.add_node(node_iou, 3, 1)) @@ -171,6 +175,7 @@ def test_read_pcap_from_source(project, async_run): compute1 = MagicMock() link = UDPLink(project) + link.create = AsyncioMagicMock() async_run(link.add_node(compute1, 0, 4)) async_run(link.add_node(compute1, 3, 1))