diff --git a/gns3server/controller/udp_link.py b/gns3server/controller/udp_link.py index ceb1da41..5a5176e8 100644 --- a/gns3server/controller/udp_link.py +++ b/gns3server/controller/udp_link.py @@ -69,7 +69,12 @@ class UDPLink(Link): "rport": self._node1_port, "type": "nio_udp" } - yield from node2.post("/adapters/{adapter_number}/ports/{port_number}/nio".format(adapter_number=adapter_number2, port_number=port_number2), data=data) + try: + yield from node2.post("/adapters/{adapter_number}/ports/{port_number}/nio".format(adapter_number=adapter_number2, port_number=port_number2), data=data) + except Exception as e: + # We clean the first NIO + yield from node1.delete("/adapters/{adapter_number}/ports/{port_number}/nio".format(adapter_number=adapter_number1, port_number=port_number1)) + raise e self._created = True @asyncio.coroutine @@ -77,6 +82,8 @@ class UDPLink(Link): """ Delete the link and free the resources """ + if not self._created: + return try: node1 = self._nodes[0]["node"] adapter_number1 = self._nodes[0]["adapter_number"] diff --git a/tests/controller/test_udp_link.py b/tests/controller/test_udp_link.py index ea7995da..a31db818 100644 --- a/tests/controller/test_udp_link.py +++ b/tests/controller/test_udp_link.py @@ -93,6 +93,72 @@ def test_create(async_run, project): }) +def test_create_one_side_failure(async_run, project): + compute1 = MagicMock() + 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): + """ + Fake subnet callback + """ + return ("192.168.1.1", "192.168.1.2") + + compute1.get_ip_on_same_subnet.side_effect = subnet_callback + + link = UDPLink(project) + async_run(link.add_node(node1, 0, 4)) + + @asyncio.coroutine + def compute1_callback(path, data={}): + """ + Fake server + """ + if "/ports/udp" in path: + response = MagicMock() + response.json = {"udp_port": 1024} + return response + + @asyncio.coroutine + def compute2_callback(path, data={}): + """ + Fake server + """ + if "/ports/udp" in path: + response = MagicMock() + response.json = {"udp_port": 2048} + return response + elif "/adapters" in path: + raise aiohttp.web.HTTPConflict(text="Error when creating the NIO") + + compute1.post.side_effect = compute1_callback + compute1.host = "example.com" + compute2.post.side_effect = compute2_callback + compute2.host = "example.org" + with pytest.raises(aiohttp.web.HTTPConflict): + 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, + "rhost": "192.168.1.2", + "rport": 2048, + "type": "nio_udp" + }) + compute2.post.assert_any_call("/projects/{}/vpcs/nodes/{}/adapters/3/ports/1/nio".format(project.id, node2.id), data={ + "lport": 2048, + "rhost": "192.168.1.1", + "rport": 1024, + "type": "nio_udp" + }) + # The link creation has failed we rollback the nio + compute1.delete.assert_any_call("/projects/{}/vpcs/nodes/{}/adapters/0/ports/4/nio".format(project.id, node1.id)) + + def test_delete(async_run, project): compute1 = MagicMock() compute2 = MagicMock()