Refactor tests

* Use pytest-aiohttp
* Use the async def / await syntax.
* Fix tests to run with Python 3.8
pull/1781/head
grossmj 4 years ago
parent f498ab06b4
commit d3ea67da24

@ -1,6 +1,7 @@
-rrequirements.txt -rrequirements.txt
sphinx==1.8.3 sphinx==1.8.3
pytest==4.4.1 pytest==5.4.3
pep8==1.7.1 pep8==1.7.1
pytest-timeout==1.3.3 pytest-timeout==1.3.3
pytest-aiohttp==0.3.0

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2016 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -26,17 +26,20 @@ from tests.utils import asyncio_patch
@pytest.fixture @pytest.fixture
def nio(): def nio():
return NIOUDP(4242, "127.0.0.1", 4343) return NIOUDP(4242, "127.0.0.1", 4343)
@pytest.fixture @pytest.fixture
def manager(): def manager():
m = MagicMock() m = MagicMock()
m.module_name = "builtins" m.module_name = "builtins"
return m return m
def test_json_with_ports(on_gns3vm, project, manager): async def test_json_with_ports(on_gns3vm, compute_project, manager):
ports = [ ports = [
{ {
"interface": "virbr0", "interface": "virbr0",
@ -45,11 +48,11 @@ def test_json_with_ports(on_gns3vm, project, manager):
"type": "ethernet", "type": "ethernet",
} }
] ]
cloud = Cloud("cloud1", str(uuid.uuid4()), project, manager, ports=ports) cloud = Cloud("cloud1", str(uuid.uuid4()), compute_project, manager, ports=ports)
assert cloud.__json__() == { assert cloud.__json__() == {
"name": "cloud1", "name": "cloud1",
"node_id": cloud.id, "node_id": cloud.id,
"project_id": project.id, "project_id": compute_project.id,
"remote_console_host": "", "remote_console_host": "",
"remote_console_http_path": "/", "remote_console_http_path": "/",
"remote_console_port": 23, "remote_console_port": 23,
@ -72,15 +75,16 @@ def test_json_with_ports(on_gns3vm, project, manager):
} }
def test_json_without_ports(on_gns3vm, project, manager): def test_json_without_ports(on_gns3vm, compute_project, manager):
""" """
If no interface is provide the cloud is prefill with non special interfaces If no interface is provide the cloud is pre-fill with non special interfaces
""" """
cloud = Cloud("cloud1", str(uuid.uuid4()), project, manager, ports=None)
cloud = Cloud("cloud1", str(uuid.uuid4()), compute_project, manager, ports=None)
assert cloud.__json__() == { assert cloud.__json__() == {
"name": "cloud1", "name": "cloud1",
"node_id": cloud.id, "node_id": cloud.id,
"project_id": project.id, "project_id": compute_project.id,
"remote_console_host": "", "remote_console_host": "",
"remote_console_http_path": "/", "remote_console_http_path": "/",
"remote_console_port": 23, "remote_console_port": 23,
@ -109,10 +113,11 @@ def test_json_without_ports(on_gns3vm, project, manager):
} }
def test_update_port_mappings(on_gns3vm, project): async def test_update_port_mappings(on_gns3vm, compute_project):
""" """
We don't allow an empty interface in the middle of port list We don't allow an empty interface in the middle of port list
""" """
ports1 = [ ports1 = [
{ {
"interface": "eth0", "interface": "eth0",
@ -127,7 +132,7 @@ def test_update_port_mappings(on_gns3vm, project):
"type": "ethernet" "type": "ethernet"
} }
] ]
cloud = Cloud("cloud1", str(uuid.uuid4()), project, MagicMock(), ports=ports1) cloud = Cloud("cloud1", str(uuid.uuid4()), compute_project, MagicMock(), ports=ports1)
assert cloud.ports_mapping == ports1 assert cloud.ports_mapping == ports1
ports2 = [ ports2 = [
@ -144,11 +149,11 @@ def test_update_port_mappings(on_gns3vm, project):
"type": "ethernet" "type": "ethernet"
} }
] ]
cloud = Cloud("cloud2", str(uuid.uuid4()), project, MagicMock(), ports=ports2) cloud = Cloud("cloud2", str(uuid.uuid4()), compute_project, MagicMock(), ports=ports2)
assert cloud.ports_mapping == ports1 assert cloud.ports_mapping == ports1
def test_linux_ethernet_raw_add_nio(linux_platform, project, async_run, nio): async def test_linux_ethernet_raw_add_nio(linux_platform, compute_project, nio):
ports = [ ports = [
{ {
"interface": "eth0", "interface": "eth0",
@ -157,14 +162,14 @@ def test_linux_ethernet_raw_add_nio(linux_platform, project, async_run, nio):
"type": "ethernet" "type": "ethernet"
} }
] ]
cloud = Cloud("cloud1", str(uuid.uuid4()), project, MagicMock(), ports=ports) cloud = Cloud("cloud1", str(uuid.uuid4()), compute_project, MagicMock(), ports=ports)
cloud.status = "started" cloud.status = "started"
with patch("shutil.which", return_value="/bin/ubridge"): with patch("shutil.which", return_value="/bin/ubridge"):
with patch("gns3server.compute.base_manager.BaseManager.has_privileged_access", return_value=True): with patch("gns3server.compute.base_manager.BaseManager.has_privileged_access", return_value=True):
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._ubridge_send") as ubridge_mock: with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._ubridge_send") as ubridge_mock:
with patch("gns3server.compute.builtin.nodes.cloud.Cloud._interfaces", return_value=[{"name": "eth0"}]): with patch("gns3server.compute.builtin.nodes.cloud.Cloud._interfaces", return_value=[{"name": "eth0"}]):
async_run(cloud.add_nio(nio, 0)) await cloud.add_nio(nio, 0)
ubridge_mock.assert_has_calls([ ubridge_mock.assert_has_calls([
call("bridge create {}-0".format(cloud._id)), call("bridge create {}-0".format(cloud._id)),
@ -175,7 +180,7 @@ def test_linux_ethernet_raw_add_nio(linux_platform, project, async_run, nio):
]) ])
def test_linux_ethernet_raw_add_nio_bridge(linux_platform, project, async_run, nio): async def test_linux_ethernet_raw_add_nio_bridge(linux_platform, compute_project, nio):
""" """
Bridge can't be connected directly to a cloud we use a tap in the middle Bridge can't be connected directly to a cloud we use a tap in the middle
""" """
@ -187,7 +192,7 @@ def test_linux_ethernet_raw_add_nio_bridge(linux_platform, project, async_run, n
"type": "ethernet" "type": "ethernet"
} }
] ]
cloud = Cloud("cloud1", str(uuid.uuid4()), project, MagicMock(), ports=ports) cloud = Cloud("cloud1", str(uuid.uuid4()), compute_project, MagicMock(), ports=ports)
cloud.status = "started" cloud.status = "started"
with patch("shutil.which", return_value="/bin/ubridge"): with patch("shutil.which", return_value="/bin/ubridge"):
@ -195,7 +200,7 @@ def test_linux_ethernet_raw_add_nio_bridge(linux_platform, project, async_run, n
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._ubridge_send") as ubridge_mock: with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._ubridge_send") as ubridge_mock:
with patch("gns3server.compute.builtin.nodes.cloud.Cloud._interfaces", return_value=[{"name": "bridge0"}]): with patch("gns3server.compute.builtin.nodes.cloud.Cloud._interfaces", return_value=[{"name": "bridge0"}]):
with patch("gns3server.utils.interfaces.is_interface_bridge", return_value=True): with patch("gns3server.utils.interfaces.is_interface_bridge", return_value=True):
async_run(cloud.add_nio(nio, 0)) await cloud.add_nio(nio, 0)
tap = "gns3tap0-0" tap = "gns3tap0-0"
ubridge_mock.assert_has_calls([ ubridge_mock.assert_has_calls([

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2016 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -16,18 +16,18 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import uuid import uuid
import pytest
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from gns3server.compute.builtin.nodes.nat import Nat from gns3server.compute.builtin.nodes.nat import Nat
def test_json_gns3vm(on_gns3vm, project): def test_json_gns3vm(on_gns3vm, compute_project):
nat = Nat("nat1", str(uuid.uuid4()), project, MagicMock())
nat = Nat("nat1", str(uuid.uuid4()), compute_project, MagicMock())
assert nat.__json__() == { assert nat.__json__() == {
"name": "nat1", "name": "nat1",
"node_id": nat.id, "node_id": nat.id,
"project_id": project.id, "project_id": compute_project.id,
"status": "started", "status": "started",
"ports_mapping": [ "ports_mapping": [
{ {
@ -40,15 +40,16 @@ def test_json_gns3vm(on_gns3vm, project):
} }
def test_json_darwin(darwin_platform, project): def test_json_darwin(darwin_platform, compute_project):
with patch("gns3server.utils.interfaces.interfaces", return_value=[ with patch("gns3server.utils.interfaces.interfaces", return_value=[
{"name": "eth0", "special": False, "type": "ethernet"}, {"name": "eth0", "special": False, "type": "ethernet"},
{"name": "vmnet8", "special": True, "type": "ethernet"}]): {"name": "vmnet8", "special": True, "type": "ethernet"}]):
nat = Nat("nat1", str(uuid.uuid4()), project, MagicMock()) nat = Nat("nat1", str(uuid.uuid4()), compute_project, MagicMock())
assert nat.__json__() == { assert nat.__json__() == {
"name": "nat1", "name": "nat1",
"node_id": nat.id, "node_id": nat.id,
"project_id": project.id, "project_id": compute_project.id,
"status": "started", "status": "started",
"ports_mapping": [ "ports_mapping": [
{ {
@ -78,4 +79,4 @@ def test_json_windows_with_full_name_of_interface(windows_platform, project):
"type": "ethernet" "type": "ethernet"
} }
] ]
} }

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -16,7 +16,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import pytest import pytest
import asyncio
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from tests.utils import asyncio_patch, AsyncioMagicMock from tests.utils import asyncio_patch, AsyncioMagicMock
@ -25,7 +24,8 @@ from gns3server.compute.docker.docker_error import DockerError, DockerHttp404Err
@pytest.fixture @pytest.fixture
def vm(): async def vm(loop):
vm = Docker() vm = Docker()
vm._connected = True vm._connected = True
vm._session = MagicMock() vm._session = MagicMock()
@ -33,7 +33,7 @@ def vm():
return vm return vm
def test_query_success(loop, vm): async def test_query_success(vm):
response = MagicMock() response = MagicMock()
response.status = 200 response.status = 200
@ -44,7 +44,7 @@ def test_query_success(loop, vm):
response.read.side_effect = read response.read.side_effect = read
vm._session.request = AsyncioMagicMock(return_value=response) vm._session.request = AsyncioMagicMock(return_value=response)
data = loop.run_until_complete(asyncio.ensure_future(vm.query("POST", "test", data={"a": True}, params={"b": 1}))) data = await vm.query("POST", "test", data={"a": True}, params={"b": 1})
vm._session.request.assert_called_with('POST', vm._session.request.assert_called_with('POST',
'http://docker/v1.25/test', 'http://docker/v1.25/test',
data='{"a": true}', data='{"a": true}',
@ -55,7 +55,7 @@ def test_query_success(loop, vm):
assert data == {"c": False} assert data == {"c": False}
def test_query_error(loop, vm): async def test_query_error(vm):
response = MagicMock() response = MagicMock()
response.status = 404 response.status = 404
@ -66,7 +66,7 @@ def test_query_error(loop, vm):
response.read.side_effect = read response.read.side_effect = read
vm._session.request = AsyncioMagicMock(return_value=response) vm._session.request = AsyncioMagicMock(return_value=response)
with pytest.raises(DockerError): with pytest.raises(DockerError):
data = loop.run_until_complete(asyncio.ensure_future(vm.query("POST", "test", data={"a": True}, params={"b": 1}))) await vm.query("POST", "test", data={"a": True}, params={"b": 1})
vm._session.request.assert_called_with('POST', vm._session.request.assert_called_with('POST',
'http://docker/v1.25/test', 'http://docker/v1.25/test',
data='{"a": true}', data='{"a": true}',
@ -75,7 +75,7 @@ def test_query_error(loop, vm):
timeout=300) timeout=300)
def test_query_error_json(loop, vm): async def test_query_error_json(vm):
response = MagicMock() response = MagicMock()
response.status = 404 response.status = 404
@ -86,7 +86,7 @@ def test_query_error_json(loop, vm):
response.read.side_effect = read response.read.side_effect = read
vm._session.request = AsyncioMagicMock(return_value=response) vm._session.request = AsyncioMagicMock(return_value=response)
with pytest.raises(DockerError): with pytest.raises(DockerError):
data = loop.run_until_complete(asyncio.ensure_future(vm.query("POST", "test", data={"a": True}, params={"b": 1}))) await vm.query("POST", "test", data={"a": True}, params={"b": 1})
vm._session.request.assert_called_with('POST', vm._session.request.assert_called_with('POST',
'http://docker/v1.25/test', 'http://docker/v1.25/test',
data='{"a": true}', data='{"a": true}',
@ -95,7 +95,8 @@ def test_query_error_json(loop, vm):
timeout=300) timeout=300)
def test_list_images(loop): async def test_list_images():
response = [ response = [
{ {
"RepoTags": [ "RepoTags": [
@ -123,7 +124,7 @@ def test_list_images(loop):
] ]
with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock:
images = loop.run_until_complete(asyncio.ensure_future(Docker.instance().list_images())) images = await Docker.instance().list_images()
mock.assert_called_with("GET", "images/json", params={"all": 0}) mock.assert_called_with("GET", "images/json", params={"all": 0})
assert len(images) == 5 assert len(images) == 5
assert {"image": "ubuntu:12.04"} in images assert {"image": "ubuntu:12.04"} in images
@ -133,10 +134,11 @@ def test_list_images(loop):
assert {"image": "ubuntu:quantal"} in images assert {"image": "ubuntu:quantal"} in images
def test_pull_image(loop): async def test_pull_image():
class Response: class Response:
""" """
Simulate a response splitted in multiple packets Simulate a response split in multiple packets
""" """
def __init__(self): def __init__(self):
@ -156,11 +158,12 @@ def test_pull_image(loop):
with asyncio_patch("gns3server.compute.docker.Docker.query", side_effect=DockerHttp404Error("404")): with asyncio_patch("gns3server.compute.docker.Docker.query", side_effect=DockerHttp404Error("404")):
with asyncio_patch("gns3server.compute.docker.Docker.http_query", return_value=mock_query) as mock: with asyncio_patch("gns3server.compute.docker.Docker.http_query", return_value=mock_query) as mock:
images = loop.run_until_complete(asyncio.ensure_future(Docker.instance().pull_image("ubuntu"))) await Docker.instance().pull_image("ubuntu")
mock.assert_called_with("POST", "images/create", params={"fromImage": "ubuntu"}, timeout=None) mock.assert_called_with("POST", "images/create", params={"fromImage": "ubuntu"}, timeout=None)
def test_docker_check_connection_docker_minimum_version(vm, loop): async def test_docker_check_connection_docker_minimum_version(vm):
response = { response = {
'ApiVersion': '1.01', 'ApiVersion': '1.01',
'Version': '1.12' 'Version': '1.12'
@ -170,10 +173,11 @@ def test_docker_check_connection_docker_minimum_version(vm, loop):
asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response): asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response):
vm._connected = False vm._connected = False
with pytest.raises(DockerError): with pytest.raises(DockerError):
loop.run_until_complete(asyncio.ensure_future(vm._check_connection())) await vm._check_connection()
async def test_docker_check_connection_docker_preferred_version_against_newer(vm):
def test_docker_check_connection_docker_preferred_version_against_newer(vm, loop):
response = { response = {
'ApiVersion': '1.31' 'ApiVersion': '1.31'
} }
@ -181,11 +185,12 @@ def test_docker_check_connection_docker_preferred_version_against_newer(vm, loop
with patch("gns3server.compute.docker.Docker.connector"), \ with patch("gns3server.compute.docker.Docker.connector"), \
asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response): asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response):
vm._connected = False vm._connected = False
loop.run_until_complete(asyncio.ensure_future(vm._check_connection())) await vm._check_connection()
assert vm._api_version == DOCKER_PREFERRED_API_VERSION assert vm._api_version == DOCKER_PREFERRED_API_VERSION
def test_docker_check_connection_docker_preferred_version_against_older(vm, loop): async def test_docker_check_connection_docker_preferred_version_against_older(vm):
response = { response = {
'ApiVersion': '1.27', 'ApiVersion': '1.27',
} }
@ -193,5 +198,5 @@ def test_docker_check_connection_docker_preferred_version_against_older(vm, loop
with patch("gns3server.compute.docker.Docker.connector"), \ with patch("gns3server.compute.docker.Docker.connector"), \
asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response): asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response):
vm._connected = False vm._connected = False
loop.run_until_complete(asyncio.ensure_future(vm._check_connection())) await vm._check_connection()
assert vm._api_version == DOCKER_MINIMUM_API_VERSION assert vm._api_version == DOCKER_MINIMUM_API_VERSION

File diff suppressed because it is too large Load Diff

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -21,7 +21,6 @@ import tempfile
import sys import sys
import uuid import uuid
import os import os
import asyncio
from gns3server.compute.dynamips import Dynamips from gns3server.compute.dynamips import Dynamips
from gns3server.compute.dynamips.dynamips_error import DynamipsError from gns3server.compute.dynamips.dynamips_error import DynamipsError
@ -30,13 +29,15 @@ from tests.utils import asyncio_patch, AsyncioMagicMock
@pytest.fixture @pytest.fixture
def manager(port_manager): async def manager(loop, port_manager):
m = Dynamips.instance() m = Dynamips.instance()
m.port_manager = port_manager m.port_manager = port_manager
return m return m
def test_vm_invalid_dynamips_path(manager): def test_vm_invalid_dynamips_path(manager):
with patch("gns3server.config.Config.get_section_config", return_value={"dynamips_path": "/bin/test_fake"}): with patch("gns3server.config.Config.get_section_config", return_value={"dynamips_path": "/bin/test_fake"}):
with pytest.raises(DynamipsError): with pytest.raises(DynamipsError):
manager.find_dynamips() manager.find_dynamips()
@ -51,6 +52,7 @@ def test_vm_non_executable_dynamips_path(manager):
def test_get_dynamips_id(manager): def test_get_dynamips_id(manager):
project_1 = str(uuid.uuid4()) project_1 = str(uuid.uuid4())
project_2 = str(uuid.uuid4()) project_2 = str(uuid.uuid4())
project_3 = str(uuid.uuid4()) project_3 = str(uuid.uuid4())
@ -64,8 +66,8 @@ def test_get_dynamips_id(manager):
def test_take_dynamips_id(manager): def test_take_dynamips_id(manager):
project_1 = str(uuid.uuid4())
project_1 = str(uuid.uuid4())
manager.take_dynamips_id(project_1, 1) manager.take_dynamips_id(project_1, 1)
assert manager.get_dynamips_id(project_1) == 2 assert manager.get_dynamips_id(project_1) == 2
with pytest.raises(DynamipsError): with pytest.raises(DynamipsError):
@ -73,9 +75,9 @@ def test_take_dynamips_id(manager):
def test_release_dynamips_id(manager): def test_release_dynamips_id(manager):
project_1 = str(uuid.uuid4()) project_1 = str(uuid.uuid4())
project_2 = str(uuid.uuid4()) project_2 = str(uuid.uuid4())
manager.take_dynamips_id(project_1, 1) manager.take_dynamips_id(project_1, 1)
manager.release_dynamips_id(project_1, 1) manager.release_dynamips_id(project_1, 1)
assert manager.get_dynamips_id(project_1) == 1 assert manager.get_dynamips_id(project_1) == 1
@ -83,45 +85,43 @@ def test_release_dynamips_id(manager):
manager.release_dynamips_id(project_2, 0) manager.release_dynamips_id(project_2, 0)
def test_project_closed(manager, project, async_run): async def test_project_closed(manager, compute_project):
manager._dynamips_ids[project.id] = set([1, 2, 3]) manager._dynamips_ids[compute_project.id] = set([1, 2, 3])
project_dir = project.module_working_path(manager.module_name.lower()) project_dir = compute_project.module_working_path(manager.module_name.lower())
os.makedirs(project_dir) os.makedirs(project_dir)
open(os.path.join(project_dir, "test.ghost"), "w+").close() open(os.path.join(project_dir, "test.ghost"), "w+").close()
await manager.project_closed(compute_project)
async_run(manager.project_closed(project))
assert not os.path.exists(os.path.join(project_dir, "test.ghost")) assert not os.path.exists(os.path.join(project_dir, "test.ghost"))
assert project.id not in manager._dynamips_ids assert compute_project.id not in manager._dynamips_ids
def test_duplicate_node(manager, project, async_run): async def test_duplicate_node(manager, compute_project):
""" """
Duplicate dynamips do nothing it's manage outside the Duplicate dynamips do nothing it's manage outside the
filesystem filesystem
""" """
with asyncio_patch('gns3server.compute.dynamips.nodes.c7200.C7200.create'): with asyncio_patch('gns3server.compute.dynamips.nodes.c7200.C7200.create'):
source_node = async_run(manager.create_node( source_node = await manager.create_node(
'R1', 'R1',
project.id, compute_project.id,
str(uuid.uuid4()), str(uuid.uuid4()),
platform="c7200" platform="c7200"
)) )
destination_node = async_run(manager.create_node( destination_node = await manager.create_node(
'R2', 'R2',
project.id, compute_project.id,
str(uuid.uuid4()), str(uuid.uuid4()),
platform="c7200" platform="c7200"
)) )
destination_node._hypervisor = AsyncioMagicMock() destination_node._hypervisor = AsyncioMagicMock()
with open(os.path.join(source_node.working_dir, 'c3600_i1_nvram'), 'w+') as f: with open(os.path.join(source_node.working_dir, 'c3600_i1_nvram'), 'w+') as f:
f.write("1") f.write("1")
with open(source_node.startup_config_path, 'w+') as f: with open(source_node.startup_config_path, 'w+') as f:
f.write('hostname R1\necho TEST') f.write('hostname R1\necho TEST')
async_run(manager.duplicate_node(source_node.id, destination_node.id)) await manager.duplicate_node(source_node.id, destination_node.id)
assert not os.path.exists(os.path.join(destination_node.working_dir, 'c3600_i1_nvram')) assert not os.path.exists(os.path.join(destination_node.working_dir, 'c3600_i1_nvram'))
with open(destination_node.startup_config_path) as f: with open(destination_node.startup_config_path) as f:
content = f.read() content = f.read()

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -19,9 +19,7 @@ import os
import uuid import uuid
import pytest import pytest
import asyncio import asyncio
import configparser
from unittest.mock import patch
from gns3server.compute.dynamips.nodes.router import Router from gns3server.compute.dynamips.nodes.router import Router
from gns3server.compute.dynamips.dynamips_error import DynamipsError from gns3server.compute.dynamips.dynamips_error import DynamipsError
from gns3server.compute.dynamips import Dynamips from gns3server.compute.dynamips import Dynamips
@ -29,47 +27,51 @@ from gns3server.config import Config
@pytest.fixture @pytest.fixture
def manager(port_manager): async def manager(loop, port_manager):
m = Dynamips.instance() m = Dynamips.instance()
m.port_manager = port_manager m.port_manager = port_manager
return m return m
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def router(project, manager): def router(compute_project, manager):
return Router("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager)
return Router("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", compute_project, manager)
def test_router(project, manager): def test_router(compute_project, manager):
router = Router("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager)
router = Router("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", compute_project, manager)
assert router.name == "test" assert router.name == "test"
assert router.id == "00010203-0405-0607-0809-0a0b0c0d0e0f" assert router.id == "00010203-0405-0607-0809-0a0b0c0d0e0f"
def test_convert_project_before_2_0_0_b3(project, manager): def test_convert_project_before_2_0_0_b3(compute_project, manager):
node_id = str(uuid.uuid4()) node_id = str(uuid.uuid4())
wdir = project.module_working_directory(manager.module_name.lower()) wdir = compute_project.module_working_directory(manager.module_name.lower())
os.makedirs(os.path.join(wdir, node_id)) os.makedirs(os.path.join(wdir, node_id))
os.makedirs(os.path.join(wdir, "configs")) os.makedirs(os.path.join(wdir, "configs"))
open(os.path.join(wdir, "configs", "i1_startup-config.cfg"), "w+").close() open(os.path.join(wdir, "configs", "i1_startup-config.cfg"), "w+").close()
open(os.path.join(wdir, "configs", "i2_startup-config.cfg"), "w+").close() open(os.path.join(wdir, "configs", "i2_startup-config.cfg"), "w+").close()
open(os.path.join(wdir, "c7200_i1_nvram"), "w+").close() open(os.path.join(wdir, "c7200_i1_nvram"), "w+").close()
open(os.path.join(wdir, "c7200_i2_nvram"), "w+").close() open(os.path.join(wdir, "c7200_i2_nvram"), "w+").close()
router = Router("test", node_id, project, manager, dynamips_id=1) router = Router("test", node_id, compute_project, manager, dynamips_id=1)
assert os.path.exists(os.path.join(wdir, node_id, "configs", "i1_startup-config.cfg")) assert os.path.exists(os.path.join(wdir, node_id, "configs", "i1_startup-config.cfg"))
assert not os.path.exists(os.path.join(wdir, node_id, "configs", "i2_startup-config.cfg")) assert not os.path.exists(os.path.join(wdir, node_id, "configs", "i2_startup-config.cfg"))
assert os.path.exists(os.path.join(wdir, node_id, "c7200_i1_nvram")) assert os.path.exists(os.path.join(wdir, node_id, "c7200_i1_nvram"))
assert not os.path.exists(os.path.join(wdir, node_id, "c7200_i2_nvram")) assert not os.path.exists(os.path.join(wdir, node_id, "c7200_i2_nvram"))
def test_router_invalid_dynamips_path(project, manager, loop): async def test_router_invalid_dynamips_path(compute_project, manager):
config = Config.instance() config = Config.instance()
config.set("Dynamips", "dynamips_path", "/bin/test_fake") config.set("Dynamips", "dynamips_path", "/bin/test_fake")
config.set("Dynamips", "allocate_aux_console_ports", False) config.set("Dynamips", "allocate_aux_console_ports", False)
with pytest.raises(DynamipsError): with pytest.raises(DynamipsError):
router = Router("test", "00010203-0405-0607-0809-0a0b0c0d0e0e", project, manager) router = Router("test", "00010203-0405-0607-0809-0a0b0c0d0e0e", compute_project, manager)
loop.run_until_complete(asyncio.ensure_future(router.create())) await router.create()
assert router.name == "test" assert router.name == "test"
assert router.id == "00010203-0405-0607-0809-0a0b0c0d0e0e" assert router.id == "00010203-0405-0607-0809-0a0b0c0d0e0e"

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2017 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -16,11 +16,11 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from tests.utils import AsyncioMagicMock from tests.utils import AsyncioMagicMock
#from gns3server.compute.dynamips.nodes.ethernet_switch import EthernetSwitchConsole
from gns3server.compute.nios.nio_udp import NIOUDP from gns3server.compute.nios.nio_udp import NIOUDP
def test_mac_command(async_run): def test_mac_command():
node = AsyncioMagicMock() node = AsyncioMagicMock()
node.name = "Test" node.name = "Test"
node.nios = {} node.nios = {}

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -16,7 +16,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import pytest import pytest
import aiohttp
import asyncio import asyncio
import os import os
import stat import stat
@ -24,10 +23,10 @@ import socket
import sys import sys
import uuid import uuid
import shutil import shutil
from tests.utils import asyncio_patch, AsyncioMagicMock
from tests.utils import asyncio_patch, AsyncioMagicMock
from unittest.mock import patch, MagicMock, PropertyMock, call from unittest.mock import patch, MagicMock
pytestmark = pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") pytestmark = pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
@ -36,29 +35,29 @@ if not sys.platform.startswith("win"):
from gns3server.compute.iou.iou_error import IOUError from gns3server.compute.iou.iou_error import IOUError
from gns3server.compute.iou import IOU from gns3server.compute.iou import IOU
from gns3server.config import Config
@pytest.fixture @pytest.fixture
def manager(port_manager): async def manager(loop, port_manager):
m = IOU.instance() m = IOU.instance()
m.port_manager = port_manager m.port_manager = port_manager
return m return m
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def vm(project, manager, tmpdir, fake_iou_bin, iourc_file): async def vm(loop, compute_project, manager, tmpdir, fake_iou_bin, iourc_file):
vm = IOUVM("test", str(uuid.uuid4()), project, manager, application_id=1)
vm = IOUVM("test", str(uuid.uuid4()), compute_project, manager, application_id=1)
config = manager.config.get_section_config("IOU") config = manager.config.get_section_config("IOU")
config["iourc_path"] = iourc_file config["iourc_path"] = iourc_file
manager.config.set_section_config("IOU", config) manager.config.set_section_config("IOU", config)
vm.path = "iou.bin" vm.path = "iou.bin"
return vm return vm
@pytest.fixture @pytest.fixture
def iourc_file(tmpdir): def iourc_file(tmpdir):
path = str(tmpdir / "iourc") path = str(tmpdir / "iourc")
with open(path, "w+") as f: with open(path, "w+") as f:
hostname = socket.gethostname() hostname = socket.gethostname()
@ -77,21 +76,23 @@ def fake_iou_bin(images_dir):
return path return path
def test_vm(project, manager): def test_vm(compute_project, manager):
vm = IOUVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager)
vm = IOUVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", compute_project, manager)
assert vm.name == "test" assert vm.name == "test"
assert vm.id == "00010203-0405-0607-0809-0a0b0c0d0e0f" assert vm.id == "00010203-0405-0607-0809-0a0b0c0d0e0f"
def test_vm_startup_config_content(project, manager): def test_vm_startup_config_content(compute_project, manager):
vm = IOUVM("test", "00010203-0405-0607-0808-0a0b0c0d0e0f", project, manager, application_id=1)
vm = IOUVM("test", "00010203-0405-0607-0808-0a0b0c0d0e0f", compute_project, manager, application_id=1)
vm.startup_config_content = "hostname %h" vm.startup_config_content = "hostname %h"
assert vm.name == "test" assert vm.name == "test"
assert vm.startup_config_content == "hostname test" assert vm.startup_config_content == "hostname test"
assert vm.id == "00010203-0405-0607-0808-0a0b0c0d0e0f" assert vm.id == "00010203-0405-0607-0808-0a0b0c0d0e0f"
def test_start(loop, vm): async def test_start(vm):
mock_process = MagicMock() mock_process = MagicMock()
vm._check_requirements = AsyncioMagicMock(return_value=True) vm._check_requirements = AsyncioMagicMock(return_value=True)
@ -101,7 +102,7 @@ def test_start(loop, vm):
with asyncio_patch("asyncio.create_subprocess_exec", return_value=mock_process) as mock_exec: with asyncio_patch("asyncio.create_subprocess_exec", return_value=mock_process) as mock_exec:
mock_process.returncode = None mock_process.returncode = None
loop.run_until_complete(asyncio.ensure_future(vm.start())) await vm.start()
assert vm.is_running() assert vm.is_running()
assert vm.command_line == ' '.join(mock_exec.call_args[0]) assert vm.command_line == ' '.join(mock_exec.call_args[0])
@ -113,7 +114,7 @@ def test_start(loop, vm):
vm._ubridge_send.assert_any_call("iol_bridge start IOL-BRIDGE-513") vm._ubridge_send.assert_any_call("iol_bridge start IOL-BRIDGE-513")
def test_start_with_iourc(loop, vm, tmpdir): async def test_start_with_iourc(vm, tmpdir):
fake_file = str(tmpdir / "iourc") fake_file = str(tmpdir / "iourc")
with open(fake_file, "w+") as f: with open(fake_file, "w+") as f:
@ -129,13 +130,13 @@ def test_start_with_iourc(loop, vm, tmpdir):
with patch("gns3server.config.Config.get_section_config", return_value={"iourc_path": fake_file}): with patch("gns3server.config.Config.get_section_config", return_value={"iourc_path": fake_file}):
with asyncio_patch("asyncio.create_subprocess_exec", return_value=mock_process) as exec_mock: with asyncio_patch("asyncio.create_subprocess_exec", return_value=mock_process) as exec_mock:
mock_process.returncode = None mock_process.returncode = None
loop.run_until_complete(asyncio.ensure_future(vm.start())) await vm.start()
assert vm.is_running() assert vm.is_running()
arsgs, kwargs = exec_mock.call_args arsgs, kwargs = exec_mock.call_args
assert kwargs["env"]["IOURC"] == fake_file assert kwargs["env"]["IOURC"] == fake_file
def test_rename_nvram_file(loop, vm): async def test_rename_nvram_file(vm):
""" """
It should rename the nvram file to the correct name before launching the VM It should rename the nvram file to the correct name before launching the VM
""" """
@ -151,9 +152,9 @@ def test_rename_nvram_file(loop, vm):
assert os.path.exists(os.path.join(vm.working_dir, "vlan.dat-0000{}".format(vm.application_id))) assert os.path.exists(os.path.join(vm.working_dir, "vlan.dat-0000{}".format(vm.application_id)))
def test_stop(loop, vm): async def test_stop(vm):
process = MagicMock()
process = MagicMock()
vm._check_requirements = AsyncioMagicMock(return_value=True) vm._check_requirements = AsyncioMagicMock(return_value=True)
vm._check_iou_licence = AsyncioMagicMock(return_value=True) vm._check_iou_licence = AsyncioMagicMock(return_value=True)
vm._start_ioucon = AsyncioMagicMock(return_value=True) vm._start_ioucon = AsyncioMagicMock(return_value=True)
@ -167,17 +168,17 @@ def test_stop(loop, vm):
with asyncio_patch("asyncio.create_subprocess_exec", return_value=process): with asyncio_patch("asyncio.create_subprocess_exec", return_value=process):
with asyncio_patch("gns3server.utils.asyncio.wait_for_process_termination"): with asyncio_patch("gns3server.utils.asyncio.wait_for_process_termination"):
loop.run_until_complete(asyncio.ensure_future(vm.start())) await vm.start()
process.returncode = None process.returncode = None
assert vm.is_running() assert vm.is_running()
loop.run_until_complete(asyncio.ensure_future(vm.stop())) await vm.stop()
assert vm.is_running() is False assert vm.is_running() is False
process.terminate.assert_called_with() process.terminate.assert_called_with()
def test_reload(loop, vm, fake_iou_bin): async def test_reload(vm, fake_iou_bin):
process = MagicMock()
process = MagicMock()
vm._check_requirements = AsyncioMagicMock(return_value=True) vm._check_requirements = AsyncioMagicMock(return_value=True)
vm._check_iou_licence = AsyncioMagicMock(return_value=True) vm._check_iou_licence = AsyncioMagicMock(return_value=True)
vm._start_ioucon = AsyncioMagicMock(return_value=True) vm._start_ioucon = AsyncioMagicMock(return_value=True)
@ -192,33 +193,35 @@ def test_reload(loop, vm, fake_iou_bin):
with asyncio_patch("asyncio.create_subprocess_exec", return_value=process): with asyncio_patch("asyncio.create_subprocess_exec", return_value=process):
with asyncio_patch("gns3server.utils.asyncio.wait_for_process_termination"): with asyncio_patch("gns3server.utils.asyncio.wait_for_process_termination"):
loop.run_until_complete(asyncio.ensure_future(vm.start())) await vm.start()
assert vm.is_running() assert vm.is_running()
loop.run_until_complete(asyncio.ensure_future(vm.reload())) await vm.reload()
assert vm.is_running() is True assert vm.is_running() is True
process.terminate.assert_called_with() process.terminate.assert_called_with()
def test_close(vm, port_manager, loop): async def test_close(vm, port_manager):
vm._start_ubridge = AsyncioMagicMock(return_value=True) vm._start_ubridge = AsyncioMagicMock(return_value=True)
vm._ubridge_send = AsyncioMagicMock() vm._ubridge_send = AsyncioMagicMock()
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM._check_requirements", return_value=True): with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM._check_requirements", return_value=True):
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()): with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()):
loop.run_until_complete(asyncio.ensure_future(vm.start())) await vm.start()
port = vm.console port = vm.console
loop.run_until_complete(asyncio.ensure_future(vm.close())) await vm.close()
# Raise an exception if the port is not free # Raise an exception if the port is not free
port_manager.reserve_tcp_port(port, vm.project) port_manager.reserve_tcp_port(port, vm.project)
assert vm.is_running() is False assert vm.is_running() is False
def test_path(vm, fake_iou_bin, config): def test_path(vm, fake_iou_bin, config):
config.set_section_config("Server", {"local": True}) config.set_section_config("Server", {"local": True})
vm.path = fake_iou_bin vm.path = fake_iou_bin
assert vm.path == fake_iou_bin assert vm.path == fake_iou_bin
def test_path_relative(vm, fake_iou_bin, tmpdir): def test_path_relative(vm, fake_iou_bin):
vm.path = "iou.bin" vm.path = "iou.bin"
assert vm.path == fake_iou_bin assert vm.path == fake_iou_bin
@ -249,9 +252,9 @@ def test_create_netmap_config(vm):
assert "513:15/3 1:15/3" in content assert "513:15/3 1:15/3" in content
def test_build_command(vm, loop): async def test_build_command(vm):
assert loop.run_until_complete(asyncio.ensure_future(vm._build_command())) == [vm.path, str(vm.application_id)] assert await vm._build_command() == [vm.path, str(vm.application_id)]
def test_get_startup_config(vm): def test_get_startup_config(vm):
@ -262,6 +265,7 @@ def test_get_startup_config(vm):
def test_update_startup_config(vm): def test_update_startup_config(vm):
content = "service timestamps debug datetime msec\nservice timestamps log datetime msec\nno service password-encryption" content = "service timestamps debug datetime msec\nservice timestamps log datetime msec\nno service password-encryption"
vm.startup_config_content = content vm.startup_config_content = content
filepath = os.path.join(vm.working_dir, "startup-config.cfg") filepath = os.path.join(vm.working_dir, "startup-config.cfg")
@ -271,6 +275,7 @@ def test_update_startup_config(vm):
def test_update_startup_config_empty(vm): def test_update_startup_config_empty(vm):
content = "service timestamps debug datetime msec\nservice timestamps log datetime msec\nno service password-encryption" content = "service timestamps debug datetime msec\nservice timestamps log datetime msec\nno service password-encryption"
vm.startup_config_content = content vm.startup_config_content = content
filepath = os.path.join(vm.working_dir, "startup-config.cfg") filepath = os.path.join(vm.working_dir, "startup-config.cfg")
@ -283,6 +288,7 @@ def test_update_startup_config_empty(vm):
def test_update_startup_config_content_hostname(vm): def test_update_startup_config_content_hostname(vm):
content = "hostname %h\n" content = "hostname %h\n"
vm.name = "pc1" vm.name = "pc1"
vm.startup_config_content = content vm.startup_config_content = content
@ -290,7 +296,8 @@ def test_update_startup_config_content_hostname(vm):
assert f.read() == "hostname pc1\n" assert f.read() == "hostname pc1\n"
def test_change_name(vm, tmpdir): def test_change_name(vm):
path = os.path.join(vm.working_dir, "startup-config.cfg") path = os.path.join(vm.working_dir, "startup-config.cfg")
vm.name = "world" vm.name = "world"
with open(path, 'w+') as f: with open(path, 'w+') as f:
@ -309,50 +316,48 @@ def test_change_name(vm, tmpdir):
assert f.read() == "no service password-encryption\nhostname charlie\nno ip icmp rate-limit unreachable" assert f.read() == "no service password-encryption\nhostname charlie\nno ip icmp rate-limit unreachable"
def test_library_check(loop, vm): async def test_library_check(vm):
with asyncio_patch("gns3server.utils.asyncio.subprocess_check_output", return_value="") as mock:
loop.run_until_complete(asyncio.ensure_future(vm._library_check())) with asyncio_patch("gns3server.utils.asyncio.subprocess_check_output", return_value=""):
await vm._library_check()
with asyncio_patch("gns3server.utils.asyncio.subprocess_check_output", return_value="libssl => not found") as mock: with asyncio_patch("gns3server.utils.asyncio.subprocess_check_output", return_value="libssl => not found"):
with pytest.raises(IOUError): with pytest.raises(IOUError):
loop.run_until_complete(asyncio.ensure_future(vm._library_check())) await vm._library_check()
def test_enable_l1_keepalives(loop, vm): async def test_enable_l1_keepalives(vm):
with asyncio_patch("gns3server.utils.asyncio.subprocess_check_output", return_value="***************************************************************\n\n-l Enable Layer 1 keepalive messages\n-u <n> UDP port base for distributed networks\n") as mock:
with asyncio_patch("gns3server.utils.asyncio.subprocess_check_output", return_value="***************************************************************\n\n-l Enable Layer 1 keepalive messages\n-u <n> UDP port base for distributed networks\n"):
command = ["test"] command = ["test"]
loop.run_until_complete(asyncio.ensure_future(vm._enable_l1_keepalives(command))) await vm._enable_l1_keepalives(command)
assert command == ["test", "-l"] assert command == ["test", "-l"]
with asyncio_patch("gns3server.utils.asyncio.subprocess_check_output", return_value="***************************************************************\n\n-u <n> UDP port base for distributed networks\n") as mock: with asyncio_patch("gns3server.utils.asyncio.subprocess_check_output", return_value="***************************************************************\n\n-u <n> UDP port base for distributed networks\n"):
command = ["test"] command = ["test"]
with pytest.raises(IOUError): with pytest.raises(IOUError):
loop.run_until_complete(asyncio.ensure_future(vm._enable_l1_keepalives(command))) await vm._enable_l1_keepalives(command)
assert command == ["test"] assert command == ["test"]
def test_start_capture(vm, tmpdir, manager, free_console_port, loop): async def test_start_capture(vm, tmpdir, manager, free_console_port):
output_file = str(tmpdir / "test.pcap") output_file = str(tmpdir / "test.pcap")
nio = manager.create_nio({"type": "nio_udp", "lport": free_console_port, "rport": free_console_port, "rhost": "127.0.0.1"}) nio = manager.create_nio({"type": "nio_udp", "lport": free_console_port, "rport": free_console_port, "rhost": "127.0.0.1"})
loop.run_until_complete(asyncio.ensure_future(vm.adapter_add_nio_binding(0, 0, nio))) await vm.adapter_add_nio_binding(0, 0, nio)
loop.run_until_complete(asyncio.ensure_future(vm.start_capture(0, 0, output_file))) await vm.start_capture(0, 0, output_file)
assert vm._adapters[0].get_nio(0).capturing assert vm._adapters[0].get_nio(0).capturing
def test_stop_capture(vm, tmpdir, manager, free_console_port, loop): async def test_stop_capture(vm, tmpdir, manager, free_console_port):
output_file = str(tmpdir / "test.pcap") output_file = str(tmpdir / "test.pcap")
nio = manager.create_nio({"type": "nio_udp", "lport": free_console_port, "rport": free_console_port, "rhost": "127.0.0.1"}) nio = manager.create_nio({"type": "nio_udp", "lport": free_console_port, "rport": free_console_port, "rhost": "127.0.0.1"})
loop.run_until_complete(asyncio.ensure_future(vm.adapter_add_nio_binding(0, 0, nio))) await vm.adapter_add_nio_binding(0, 0, nio)
loop.run_until_complete(vm.start_capture(0, 0, output_file)) await vm.start_capture(0, 0, output_file)
assert vm._adapters[0].get_nio(0).capturing assert vm._adapters[0].get_nio(0).capturing
loop.run_until_complete(asyncio.ensure_future(vm.stop_capture(0, 0))) await vm.stop_capture(0, 0)
assert vm._adapters[0].get_nio(0).capturing is False assert vm._adapters[0].get_nio(0).capturing is False
@ -361,46 +366,45 @@ def test_get_legacy_vm_workdir():
assert IOU.get_legacy_vm_workdir(42, "bla") == "iou/device-42" assert IOU.get_legacy_vm_workdir(42, "bla") == "iou/device-42"
def test_invalid_iou_file(loop, vm, iourc_file): async def test_invalid_iou_file(vm, iourc_file):
hostname = socket.gethostname() hostname = socket.gethostname()
await vm._check_iou_licence()
loop.run_until_complete(asyncio.ensure_future(vm._check_iou_licence()))
# Missing ; # Missing ;
with pytest.raises(IOUError): with pytest.raises(IOUError):
with open(iourc_file, "w+") as f: with open(iourc_file, "w+") as f:
f.write("[license]\n{} = aaaaaaaaaaaaaaaa".format(hostname)) f.write("[license]\n{} = aaaaaaaaaaaaaaaa".format(hostname))
loop.run_until_complete(asyncio.ensure_future(vm._check_iou_licence())) await vm._check_iou_licence()
# Key too short # Key too short
with pytest.raises(IOUError): with pytest.raises(IOUError):
with open(iourc_file, "w+") as f: with open(iourc_file, "w+") as f:
f.write("[license]\n{} = aaaaaaaaaaaaaa;".format(hostname)) f.write("[license]\n{} = aaaaaaaaaaaaaa;".format(hostname))
loop.run_until_complete(asyncio.ensure_future(vm._check_iou_licence())) await vm._check_iou_licence()
# Invalid hostname # Invalid hostname
with pytest.raises(IOUError): with pytest.raises(IOUError):
with open(iourc_file, "w+") as f: with open(iourc_file, "w+") as f:
f.write("[license]\nbla = aaaaaaaaaaaaaa;") f.write("[license]\nbla = aaaaaaaaaaaaaa;")
loop.run_until_complete(asyncio.ensure_future(vm._check_iou_licence())) await vm._check_iou_licence()
# Missing licence section # Missing licence section
with pytest.raises(IOUError): with pytest.raises(IOUError):
with open(iourc_file, "w+") as f: with open(iourc_file, "w+") as f:
f.write("[licensetest]\n{} = aaaaaaaaaaaaaaaa;") f.write("[licensetest]\n{} = aaaaaaaaaaaaaaaa;")
loop.run_until_complete(asyncio.ensure_future(vm._check_iou_licence())) await vm._check_iou_licence()
# Broken config file # Broken config file
with pytest.raises(IOUError): with pytest.raises(IOUError):
with open(iourc_file, "w+") as f: with open(iourc_file, "w+") as f:
f.write("[") f.write("[")
loop.run_until_complete(asyncio.ensure_future(vm._check_iou_licence())) await vm._check_iou_licence()
# Missing file # Missing file
with pytest.raises(IOUError): with pytest.raises(IOUError):
os.remove(iourc_file) os.remove(iourc_file)
loop.run_until_complete(asyncio.ensure_future(vm._check_iou_licence())) await vm._check_iou_licence()
def test_iourc_content(vm): def test_iourc_content(vm):
@ -437,11 +441,11 @@ def test_extract_configs(vm):
assert len(private_config) == 0 assert len(private_config) == 0
def test_application_id(project, manager): def test_application_id(compute_project, manager):
""" """
Checks if uses local manager to get application_id when not set Checks if uses local manager to get application_id when not set
""" """
vm = IOUVM("test", str(uuid.uuid4()), project, manager, application_id=1) vm = IOUVM("test", str(uuid.uuid4()), compute_project, manager, application_id=1)
assert vm.application_id == 1 assert vm.application_id == 1
vm.application_id = 3 vm.application_id = 3

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2016 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -18,7 +18,6 @@
import os import os
import pytest import pytest
import shutil import shutil
import asyncio
from gns3server.compute.qemu.utils.qcow2 import Qcow2, Qcow2Error from gns3server.compute.qemu.utils.qcow2 import Qcow2, Qcow2Error
@ -40,34 +39,39 @@ def qemu_img():
def test_valid_base_file(): def test_valid_base_file():
qcow2 = Qcow2("tests/resources/empty8G.qcow2") qcow2 = Qcow2("tests/resources/empty8G.qcow2")
assert qcow2.version == 3 assert qcow2.version == 3
assert qcow2.backing_file is None assert qcow2.backing_file is None
def test_valid_linked_file(): def test_valid_linked_file():
qcow2 = Qcow2("tests/resources/linked.qcow2") qcow2 = Qcow2("tests/resources/linked.qcow2")
assert qcow2.version == 3 assert qcow2.version == 3
assert qcow2.backing_file == "empty8G.qcow2" assert qcow2.backing_file == "empty8G.qcow2"
def test_invalid_file(): def test_invalid_file():
with pytest.raises(Qcow2Error): with pytest.raises(Qcow2Error):
Qcow2("tests/resources/nvram_iou") Qcow2("tests/resources/nvram_iou")
def test_invalid_empty_file(tmpdir): def test_invalid_empty_file(tmpdir):
open(str(tmpdir / 'a'), 'w+').close() open(str(tmpdir / 'a'), 'w+').close()
with pytest.raises(Qcow2Error): with pytest.raises(Qcow2Error):
Qcow2(str(tmpdir / 'a')) Qcow2(str(tmpdir / 'a'))
@pytest.mark.skipif(qemu_img() is None, reason="qemu-img is not available") @pytest.mark.skipif(qemu_img() is None, reason="qemu-img is not available")
def test_rebase(tmpdir, loop): async def test_rebase(loop, tmpdir):
shutil.copy("tests/resources/empty8G.qcow2", str(tmpdir / "empty16G.qcow2")) shutil.copy("tests/resources/empty8G.qcow2", str(tmpdir / "empty16G.qcow2"))
shutil.copy("tests/resources/linked.qcow2", str(tmpdir / "linked.qcow2")) shutil.copy("tests/resources/linked.qcow2", str(tmpdir / "linked.qcow2"))
qcow2 = Qcow2(str(tmpdir / "linked.qcow2")) qcow2 = Qcow2(str(tmpdir / "linked.qcow2"))
assert qcow2.version == 3 assert qcow2.version == 3
assert qcow2.backing_file == "empty8G.qcow2" assert qcow2.backing_file == "empty8G.qcow2"
loop.run_until_complete(asyncio.ensure_future(qcow2.rebase(qemu_img(), str(tmpdir / "empty16G.qcow2")))) await qcow2.rebase(qemu_img(), str(tmpdir / "empty16G.qcow2"))
assert qcow2.backing_file == str(tmpdir / "empty16G.qcow2") assert qcow2.backing_file == str(tmpdir / "empty16G.qcow2")

@ -17,7 +17,6 @@
import os import os
import stat import stat
import asyncio
import sys import sys
import pytest import pytest
import platform import platform
@ -25,6 +24,7 @@ import platform
from gns3server.compute.qemu import Qemu from gns3server.compute.qemu import Qemu
from gns3server.compute.qemu.qemu_error import QemuError from gns3server.compute.qemu.qemu_error import QemuError
from tests.utils import asyncio_patch from tests.utils import asyncio_patch
from unittest.mock import patch, MagicMock from unittest.mock import patch, MagicMock
@ -38,18 +38,19 @@ def fake_qemu_img_binary(tmpdir):
return bin_path return bin_path
def test_get_qemu_version(loop): async def test_get_qemu_version():
with asyncio_patch("gns3server.compute.qemu.subprocess_check_output", return_value="QEMU emulator version 2.2.0, Copyright (c) 2003-2008 Fabrice Bellard") as mock: with asyncio_patch("gns3server.compute.qemu.subprocess_check_output", return_value="QEMU emulator version 2.2.0, Copyright (c) 2003-2008 Fabrice Bellard"):
version = loop.run_until_complete(asyncio.ensure_future(Qemu.get_qemu_version("/tmp/qemu-test"))) version = await Qemu.get_qemu_version("/tmp/qemu-test")
if sys.platform.startswith("win"): if sys.platform.startswith("win"):
assert version == "" assert version == ""
else: else:
assert version == "2.2.0" assert version == "2.2.0"
def test_binary_list(loop): async def test_binary_list(monkeypatch, tmpdir):
monkeypatch.setenv("PATH", str(tmpdir))
files_to_create = ["qemu-system-x86", "qemu-system-x42", "qemu-kvm", "hello", "qemu-system-x86_64-spice"] files_to_create = ["qemu-system-x86", "qemu-system-x42", "qemu-kvm", "hello", "qemu-system-x86_64-spice"]
for file_to_create in files_to_create: for file_to_create in files_to_create:
@ -64,7 +65,7 @@ def test_binary_list(loop):
else: else:
version = "2.2.0" version = "2.2.0"
qemus = loop.run_until_complete(asyncio.ensure_future(Qemu.binary_list())) qemus = await Qemu.binary_list()
assert {"path": os.path.join(os.environ["PATH"], "qemu-system-x86"), "version": version} in qemus assert {"path": os.path.join(os.environ["PATH"], "qemu-system-x86"), "version": version} in qemus
assert {"path": os.path.join(os.environ["PATH"], "qemu-kvm"), "version": version} in qemus assert {"path": os.path.join(os.environ["PATH"], "qemu-kvm"), "version": version} in qemus
@ -72,14 +73,14 @@ def test_binary_list(loop):
assert {"path": os.path.join(os.environ["PATH"], "hello"), "version": version} not in qemus assert {"path": os.path.join(os.environ["PATH"], "hello"), "version": version} not in qemus
assert {"path": os.path.join(os.environ["PATH"], "qemu-system-x86_64-spice"), "version": version} not in qemus assert {"path": os.path.join(os.environ["PATH"], "qemu-system-x86_64-spice"), "version": version} not in qemus
qemus = loop.run_until_complete(asyncio.ensure_future(Qemu.binary_list(["x86"]))) qemus = await Qemu.binary_list(["x86"])
assert {"path": os.path.join(os.environ["PATH"], "qemu-system-x86"), "version": version} in qemus assert {"path": os.path.join(os.environ["PATH"], "qemu-system-x86"), "version": version} in qemus
assert {"path": os.path.join(os.environ["PATH"], "qemu-kvm"), "version": version} not in qemus assert {"path": os.path.join(os.environ["PATH"], "qemu-kvm"), "version": version} not in qemus
assert {"path": os.path.join(os.environ["PATH"], "qemu-system-x42"), "version": version} not in qemus assert {"path": os.path.join(os.environ["PATH"], "qemu-system-x42"), "version": version} not in qemus
assert {"path": os.path.join(os.environ["PATH"], "hello"), "version": version} not in qemus assert {"path": os.path.join(os.environ["PATH"], "hello"), "version": version} not in qemus
qemus = loop.run_until_complete(asyncio.ensure_future(Qemu.binary_list(["x86", "x42"]))) qemus = await Qemu.binary_list(["x86", "x42"])
assert {"path": os.path.join(os.environ["PATH"], "qemu-system-x86"), "version": version} in qemus assert {"path": os.path.join(os.environ["PATH"], "qemu-system-x86"), "version": version} in qemus
assert {"path": os.path.join(os.environ["PATH"], "qemu-kvm"), "version": version} not in qemus assert {"path": os.path.join(os.environ["PATH"], "qemu-kvm"), "version": version} not in qemus
@ -87,8 +88,9 @@ def test_binary_list(loop):
assert {"path": os.path.join(os.environ["PATH"], "hello"), "version": version} not in qemus assert {"path": os.path.join(os.environ["PATH"], "hello"), "version": version} not in qemus
def test_img_binary_list(loop): async def test_img_binary_list(monkeypatch, tmpdir):
monkeypatch.setenv("PATH", str(tmpdir))
files_to_create = ["qemu-img", "qemu-io", "qemu-system-x86", "qemu-system-x42", "qemu-kvm", "hello"] files_to_create = ["qemu-img", "qemu-io", "qemu-system-x86", "qemu-system-x42", "qemu-kvm", "hello"]
for file_to_create in files_to_create: for file_to_create in files_to_create:
@ -98,7 +100,7 @@ def test_img_binary_list(loop):
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
with asyncio_patch("gns3server.compute.qemu.subprocess_check_output", return_value="qemu-img version 2.2.0, Copyright (c) 2004-2008 Fabrice Bellard") as mock: with asyncio_patch("gns3server.compute.qemu.subprocess_check_output", return_value="qemu-img version 2.2.0, Copyright (c) 2004-2008 Fabrice Bellard") as mock:
qemus = loop.run_until_complete(asyncio.ensure_future(Qemu.img_binary_list())) qemus = await Qemu.img_binary_list()
version = "2.2.0" version = "2.2.0"
@ -115,7 +117,8 @@ def test_get_legacy_vm_workdir():
assert Qemu.get_legacy_vm_workdir(42, "bla") == os.path.join("qemu", "vm-42") assert Qemu.get_legacy_vm_workdir(42, "bla") == os.path.join("qemu", "vm-42")
def test_create_image_abs_path(loop, tmpdir, fake_qemu_img_binary): async def test_create_image_abs_path(tmpdir, fake_qemu_img_binary):
options = { options = {
"format": "qcow2", "format": "qcow2",
"preallocation": "metadata", "preallocation": "metadata",
@ -125,7 +128,7 @@ def test_create_image_abs_path(loop, tmpdir, fake_qemu_img_binary):
"size": 100 "size": 100
} }
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process: with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process:
loop.run_until_complete(asyncio.ensure_future(Qemu.instance().create_disk(fake_qemu_img_binary, str(tmpdir / "hda.qcow2"), options))) await Qemu.instance().create_disk(fake_qemu_img_binary, str(tmpdir / "hda.qcow2"), options)
args, kwargs = process.call_args args, kwargs = process.call_args
assert args == ( assert args == (
fake_qemu_img_binary, fake_qemu_img_binary,
@ -145,14 +148,15 @@ def test_create_image_abs_path(loop, tmpdir, fake_qemu_img_binary):
) )
def test_create_image_relative_path(loop, tmpdir, fake_qemu_img_binary): async def test_create_image_relative_path(tmpdir, fake_qemu_img_binary):
options = { options = {
"format": "raw", "format": "raw",
"size": 100 "size": 100
} }
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process: with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process:
with patch("gns3server.compute.qemu.Qemu.get_images_directory", return_value=str(tmpdir)): with patch("gns3server.compute.qemu.Qemu.get_images_directory", return_value=str(tmpdir)):
loop.run_until_complete(asyncio.ensure_future(Qemu.instance().create_disk(fake_qemu_img_binary, "hda.qcow2", options))) await Qemu.instance().create_disk(fake_qemu_img_binary, "hda.qcow2", options)
args, kwargs = process.call_args args, kwargs = process.call_args
assert args == ( assert args == (
fake_qemu_img_binary, fake_qemu_img_binary,
@ -164,9 +168,9 @@ def test_create_image_relative_path(loop, tmpdir, fake_qemu_img_binary):
) )
def test_create_image_exist(loop, tmpdir, fake_qemu_img_binary): async def test_create_image_exist(tmpdir, fake_qemu_img_binary):
open(str(tmpdir / "hda.qcow2"), "w+").close()
open(str(tmpdir / "hda.qcow2"), "w+").close()
options = { options = {
"format": "raw", "format": "raw",
"size": 100 "size": 100
@ -174,11 +178,12 @@ def test_create_image_exist(loop, tmpdir, fake_qemu_img_binary):
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process: with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process:
with patch("gns3server.compute.qemu.Qemu.get_images_directory", return_value=str(tmpdir)): with patch("gns3server.compute.qemu.Qemu.get_images_directory", return_value=str(tmpdir)):
with pytest.raises(QemuError): with pytest.raises(QemuError):
loop.run_until_complete(asyncio.ensure_future(Qemu.instance().create_disk(fake_qemu_img_binary, "hda.qcow2", options))) await Qemu.instance().create_disk(fake_qemu_img_binary, "hda.qcow2", options)
assert not process.called assert not process.called
def test_create_image_with_not_supported_characters_by_filesystem(loop, tmpdir, fake_qemu_img_binary): async def test_create_image_with_not_supported_characters_by_filesystem(tmpdir, fake_qemu_img_binary):
open(str(tmpdir / "hda.qcow2"), "w+").close() open(str(tmpdir / "hda.qcow2"), "w+").close()
options = { options = {
@ -193,20 +198,19 @@ def test_create_image_with_not_supported_characters_by_filesystem(loop, tmpdir,
patch("os.makedirs"): patch("os.makedirs"):
with pytest.raises(QemuError): with pytest.raises(QemuError):
loop.run_until_complete(asyncio.ensure_future(Qemu.instance().create_disk( await Qemu.instance().create_disk(fake_qemu_img_binary, "hda.qcow2", options)
fake_qemu_img_binary, "hda.qcow2", options)))
assert not process.called assert not process.called
def test_get_kvm_archs_kvm_ok(loop): async def test_get_kvm_archs_kvm_ok():
with patch("os.path.exists", return_value=True): with patch("os.path.exists", return_value=True):
archs = loop.run_until_complete(asyncio.ensure_future(Qemu.get_kvm_archs())) archs = await Qemu.get_kvm_archs()
if platform.machine() == 'x86_64': if platform.machine() == 'x86_64':
assert archs == ['x86_64', 'i386'] assert archs == ['x86_64', 'i386']
else: else:
assert archs == [platform.machine()] assert archs == [platform.machine()]
with patch("os.path.exists", return_value=False): with patch("os.path.exists", return_value=False):
archs = loop.run_until_complete(asyncio.ensure_future(Qemu.get_kvm_archs())) archs = await Qemu.get_kvm_archs()
assert archs == [] assert archs == []

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -16,12 +16,10 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import pytest import pytest
import aiohttp
import asyncio import asyncio
import os import os
import sys import sys
import stat import stat
import re
from tests.utils import asyncio_patch, AsyncioMagicMock from tests.utils import asyncio_patch, AsyncioMagicMock
@ -36,15 +34,17 @@ from gns3server.compute.notification_manager import NotificationManager
@pytest.fixture @pytest.fixture
def manager(port_manager): async def manager(loop, port_manager):
m = Qemu.instance() m = Qemu.instance()
m.port_manager = port_manager m.port_manager = port_manager
return m return m
@pytest.fixture @pytest.fixture
def fake_qemu_img_binary(): def fake_qemu_img_binary(monkeypatch, tmpdir):
monkeypatch.setenv("PATH", str(tmpdir))
bin_path = os.path.join(os.environ["PATH"], "qemu-img") bin_path = os.path.join(os.environ["PATH"], "qemu-img")
with open(bin_path, "w+") as f: with open(bin_path, "w+") as f:
f.write("1") f.write("1")
@ -53,8 +53,9 @@ def fake_qemu_img_binary():
@pytest.fixture @pytest.fixture
def fake_qemu_binary(): def fake_qemu_binary(monkeypatch, tmpdir):
monkeypatch.setenv("PATH", str(tmpdir))
if sys.platform.startswith("win"): if sys.platform.startswith("win"):
bin_path = os.path.join(os.environ["PATH"], "qemu-system-x86_64w.exe") bin_path = os.path.join(os.environ["PATH"], "qemu-system-x86_64w.exe")
else: else:
@ -66,9 +67,10 @@ def fake_qemu_binary():
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def vm(project, manager, fake_qemu_binary, fake_qemu_img_binary): async def vm(loop, compute_project, manager, fake_qemu_binary, fake_qemu_img_binary):
manager.port_manager.console_host = "127.0.0.1" manager.port_manager.console_host = "127.0.0.1"
vm = QemuVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, qemu_path=fake_qemu_binary) vm = QemuVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", compute_project, manager, qemu_path=fake_qemu_binary)
vm._process_priority = "normal" # Avoid complexity for Windows tests vm._process_priority = "normal" # Avoid complexity for Windows tests
vm._start_ubridge = AsyncioMagicMock() vm._start_ubridge = AsyncioMagicMock()
vm._ubridge_hypervisor = MagicMock() vm._ubridge_hypervisor = MagicMock()
@ -79,49 +81,52 @@ def vm(project, manager, fake_qemu_binary, fake_qemu_img_binary):
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def running_subprocess_mock(): def running_subprocess_mock():
mm = MagicMock() mm = MagicMock()
mm.returncode = None mm.returncode = None
return mm return mm
def test_vm(project, manager, fake_qemu_binary): def test_vm(compute_project, manager, fake_qemu_binary):
vm = QemuVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, qemu_path=fake_qemu_binary)
vm = QemuVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", compute_project, manager, qemu_path=fake_qemu_binary)
assert vm.name == "test" assert vm.name == "test"
assert vm.id == "00010203-0405-0607-0809-0a0b0c0d0e0f" assert vm.id == "00010203-0405-0607-0809-0a0b0c0d0e0f"
def test_vm_create(loop, tmpdir, project, manager, fake_qemu_binary): async def test_vm_create(tmpdir, compute_project, manager, fake_qemu_binary):
fake_img = str(tmpdir / 'hello') fake_img = str(tmpdir / 'hello')
with open(fake_img, 'w+') as f: with open(fake_img, 'w+') as f:
f.write('hello') f.write('hello')
vm = QemuVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, qemu_path=fake_qemu_binary) vm = QemuVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", compute_project, manager, qemu_path=fake_qemu_binary)
vm._hda_disk_image = fake_img vm._hda_disk_image = fake_img
loop.run_until_complete(asyncio.ensure_future(vm.create())) await vm.create()
# tests if `create` created md5sums # tests if `create` created md5sums
assert os.path.exists(str(tmpdir / 'hello.md5sum')) assert os.path.exists(str(tmpdir / 'hello.md5sum'))
def test_vm_invalid_qemu_with_platform(project, manager, fake_qemu_binary): def test_vm_invalid_qemu_with_platform(compute_project, manager, fake_qemu_binary):
vm = QemuVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, qemu_path="/usr/fake/bin/qemu-system-64", platform="x86_64") vm = QemuVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", compute_project, manager, qemu_path="/usr/fake/bin/qemu-system-64", platform="x86_64")
assert vm.qemu_path == fake_qemu_binary assert vm.qemu_path == fake_qemu_binary
assert vm.platform == "x86_64" assert vm.platform == "x86_64"
def test_vm_invalid_qemu_without_platform(project, manager, fake_qemu_binary): def test_vm_invalid_qemu_without_platform(compute_project, manager, fake_qemu_binary):
vm = QemuVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, qemu_path="/usr/fake/bin/qemu-system-x86_64") vm = QemuVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", compute_project, manager, qemu_path="/usr/fake/bin/qemu-system-x86_64")
assert vm.qemu_path == fake_qemu_binary assert vm.qemu_path == fake_qemu_binary
assert vm.platform == "x86_64" assert vm.platform == "x86_64"
def test_is_running(vm, running_subprocess_mock): async def test_is_running(vm, running_subprocess_mock):
vm._process = None vm._process = None
assert vm.is_running() is False assert vm.is_running() is False
@ -131,18 +136,19 @@ def test_is_running(vm, running_subprocess_mock):
assert vm.is_running() is False assert vm.is_running() is False
def test_start(loop, vm, running_subprocess_mock): async def test_start(vm, running_subprocess_mock):
vm.manager.get_qemu_version = AsyncioMagicMock(return_value="3.1.0") vm.manager.get_qemu_version = AsyncioMagicMock(return_value="3.1.0")
with asyncio_patch("gns3server.compute.qemu.QemuVM.start_wrap_console"): with asyncio_patch("gns3server.compute.qemu.QemuVM.start_wrap_console"):
with asyncio_patch("asyncio.create_subprocess_exec", return_value=running_subprocess_mock) as mock: with asyncio_patch("asyncio.create_subprocess_exec", return_value=running_subprocess_mock) as mock:
loop.run_until_complete(asyncio.ensure_future(vm.start())) await vm.start()
assert vm.is_running() assert vm.is_running()
assert vm.command_line == ' '.join(mock.call_args[0]) assert vm.command_line == ' '.join(mock.call_args[0])
def test_stop(loop, vm, running_subprocess_mock): async def test_stop(vm, running_subprocess_mock):
process = running_subprocess_mock
process = running_subprocess_mock
# Wait process kill success # Wait process kill success
future = asyncio.Future() future = asyncio.Future()
future.set_result(True) future.set_result(True)
@ -152,31 +158,30 @@ def test_stop(loop, vm, running_subprocess_mock):
with asyncio_patch("gns3server.compute.qemu.QemuVM.start_wrap_console"): with asyncio_patch("gns3server.compute.qemu.QemuVM.start_wrap_console"):
with asyncio_patch("asyncio.create_subprocess_exec", return_value=process): with asyncio_patch("asyncio.create_subprocess_exec", return_value=process):
nio = Qemu.instance().create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) nio = Qemu.instance().create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"})
loop.run_until_complete(asyncio.ensure_future(vm.adapter_add_nio_binding(0, nio))) await vm.adapter_add_nio_binding(0, nio)
loop.run_until_complete(asyncio.ensure_future(vm.start())) await vm.start()
assert vm.is_running() assert vm.is_running()
loop.run_until_complete(asyncio.ensure_future(vm.stop())) await vm.stop()
assert vm.is_running() is False assert vm.is_running() is False
process.terminate.assert_called_with() process.terminate.assert_called_with()
def test_termination_callback(vm, async_run): async def test_termination_callback(vm):
vm.status = "started" vm.status = "started"
with NotificationManager.instance().queue() as queue: with NotificationManager.instance().queue() as queue:
async_run(vm._termination_callback(0)) await vm._termination_callback(0)
assert vm.status == "stopped" assert vm.status == "stopped"
async_run(queue.get(1)) #  Ping await queue.get(1) #  Ping
(action, event, kwargs) = async_run(queue.get(1)) (action, event, kwargs) = await queue.get(1)
assert action == "node.updated" assert action == "node.updated"
assert event == vm assert event == vm
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") @pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
def test_termination_callback_error(vm, tmpdir, async_run): async def test_termination_callback_error(vm, tmpdir):
with open(str(tmpdir / "qemu.log"), "w+") as f: with open(str(tmpdir / "qemu.log"), "w+") as f:
f.write("BOOMM") f.write("BOOMM")
@ -185,10 +190,10 @@ def test_termination_callback_error(vm, tmpdir, async_run):
vm._stdout_file = str(tmpdir / "qemu.log") vm._stdout_file = str(tmpdir / "qemu.log")
with NotificationManager.instance().queue() as queue: with NotificationManager.instance().queue() as queue:
async_run(vm._termination_callback(1)) await vm._termination_callback(1)
assert vm.status == "stopped" assert vm.status == "stopped"
async_run(queue.get(1)) # Ping await queue.get(1) # Ping
(action, event, kwargs) = queue.get_nowait() (action, event, kwargs) = queue.get_nowait()
assert action == "node.updated" assert action == "node.updated"
@ -199,46 +204,48 @@ def test_termination_callback_error(vm, tmpdir, async_run):
assert event["message"] == "QEMU process has stopped, return code: 1\nBOOMM" assert event["message"] == "QEMU process has stopped, return code: 1\nBOOMM"
def test_reload(loop, vm): async def test_reload(vm):
with asyncio_patch("gns3server.compute.qemu.QemuVM._control_vm") as mock: with asyncio_patch("gns3server.compute.qemu.QemuVM._control_vm") as mock:
loop.run_until_complete(asyncio.ensure_future(vm.reload())) await vm.reload()
assert mock.called_with("system_reset") assert mock.called_with("system_reset")
def test_suspend(loop, vm): async def test_suspend(vm):
control_vm_result = MagicMock() control_vm_result = MagicMock()
control_vm_result.match.group.decode.return_value = "running" control_vm_result.match.group.decode.return_value = "running"
with asyncio_patch("gns3server.compute.qemu.QemuVM._control_vm", return_value=control_vm_result) as mock: with asyncio_patch("gns3server.compute.qemu.QemuVM._control_vm", return_value=control_vm_result) as mock:
loop.run_until_complete(asyncio.ensure_future(vm.suspend())) await vm.suspend()
assert mock.called_with("system_reset") assert mock.called_with("system_reset")
def test_add_nio_binding_udp(vm, loop): async def test_add_nio_binding_udp(vm):
nio = Qemu.instance().create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1", "filters": {}}) nio = Qemu.instance().create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1", "filters": {}})
assert nio.lport == 4242 assert nio.lport == 4242
loop.run_until_complete(asyncio.ensure_future(vm.adapter_add_nio_binding(0, nio))) await vm.adapter_add_nio_binding(0, nio)
assert nio.lport == 4242 assert nio.lport == 4242
async def test_port_remove_nio_binding(vm):
def test_port_remove_nio_binding(vm, loop):
nio = Qemu.instance().create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1", "filters": {}}) nio = Qemu.instance().create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1", "filters": {}})
loop.run_until_complete(asyncio.ensure_future(vm.adapter_add_nio_binding(0, nio))) await vm.adapter_add_nio_binding(0, nio)
loop.run_until_complete(asyncio.ensure_future(vm.adapter_remove_nio_binding(0))) await vm.adapter_remove_nio_binding(0)
assert vm._ethernet_adapters[0].ports[0] is None assert vm._ethernet_adapters[0].ports[0] is None
def test_close(vm, port_manager, loop): async def test_close(vm, port_manager):
vm.manager.get_qemu_version = AsyncioMagicMock(return_value="3.1.0") vm.manager.get_qemu_version = AsyncioMagicMock(return_value="3.1.0")
with asyncio_patch("gns3server.compute.qemu.QemuVM.start_wrap_console"): with asyncio_patch("gns3server.compute.qemu.QemuVM.start_wrap_console"):
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()): with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()):
loop.run_until_complete(asyncio.ensure_future(vm.start())) await vm.start()
console_port = vm.console console_port = vm.console
loop.run_until_complete(asyncio.ensure_future(vm.close())) await vm.close()
# Raise an exception if the port is not free # Raise an exception if the port is not free
port_manager.reserve_tcp_port(console_port, vm.project) port_manager.reserve_tcp_port(console_port, vm.project)
@ -287,7 +294,7 @@ def test_set_qemu_path_environ(vm, tmpdir, fake_qemu_binary):
assert vm.platform == "x86_64" assert vm.platform == "x86_64"
def test_set_qemu_path_windows(vm, tmpdir): def test_set_qemu_path_windows(vm):
bin_path = os.path.join(os.environ["PATH"], "qemu-system-x86_64w.EXE") bin_path = os.path.join(os.environ["PATH"], "qemu-system-x86_64w.EXE")
open(bin_path, "w+").close() open(bin_path, "w+").close()
@ -300,7 +307,7 @@ def test_set_qemu_path_windows(vm, tmpdir):
assert vm.platform == "x86_64" assert vm.platform == "x86_64"
def test_set_qemu_path_old_windows(vm, tmpdir): def test_set_qemu_path_old_windows(vm):
bin_path = os.path.join(os.environ["PATH"], "qemu.exe") bin_path = os.path.join(os.environ["PATH"], "qemu.exe")
open(bin_path, "w+").close() open(bin_path, "w+").close()
@ -314,7 +321,7 @@ def test_set_qemu_path_old_windows(vm, tmpdir):
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") @pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
def test_set_qemu_path_kvm_binary(vm, tmpdir, fake_qemu_binary): def test_set_qemu_path_kvm_binary(vm, fake_qemu_binary):
bin_path = os.path.join(os.environ["PATH"], "qemu-kvm") bin_path = os.path.join(os.environ["PATH"], "qemu-kvm")
with open(bin_path, "w+") as f: with open(bin_path, "w+") as f:
@ -328,11 +335,11 @@ def test_set_qemu_path_kvm_binary(vm, tmpdir, fake_qemu_binary):
assert vm.platform == "x86_64" assert vm.platform == "x86_64"
def test_set_platform(project, manager): def test_set_platform(compute_project, manager):
with patch("shutil.which", return_value="/bin/qemu-system-x86_64") as which_mock: with patch("shutil.which", return_value="/bin/qemu-system-x86_64") as which_mock:
with patch("gns3server.compute.qemu.QemuVM._check_qemu_path"): with patch("gns3server.compute.qemu.QemuVM._check_qemu_path"):
vm = QemuVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, platform="x86_64") vm = QemuVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", compute_project, manager, platform="x86_64")
if sys.platform.startswith("win"): if sys.platform.startswith("win"):
which_mock.assert_called_with("qemu-system-x86_64w.exe", path=mock.ANY) which_mock.assert_called_with("qemu-system-x86_64w.exe", path=mock.ANY)
else: else:
@ -341,13 +348,13 @@ def test_set_platform(project, manager):
assert vm.qemu_path == "/bin/qemu-system-x86_64" assert vm.qemu_path == "/bin/qemu-system-x86_64"
def test_disk_options(vm, tmpdir, loop, fake_qemu_img_binary): async def test_disk_options(vm, tmpdir, fake_qemu_img_binary):
vm._hda_disk_image = str(tmpdir / "test.qcow2") vm._hda_disk_image = str(tmpdir / "test.qcow2")
open(vm._hda_disk_image, "w+").close() open(vm._hda_disk_image, "w+").close()
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process: with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process:
options = loop.run_until_complete(asyncio.ensure_future(vm._disk_options())) options = await vm._disk_options()
assert process.called assert process.called
args, kwargs = process.call_args args, kwargs = process.call_args
assert args == (fake_qemu_img_binary, "create", "-o", "backing_file={}".format(vm._hda_disk_image), "-f", "qcow2", os.path.join(vm.working_dir, "hda_disk.qcow2")) assert args == (fake_qemu_img_binary, "create", "-o", "backing_file={}".format(vm._hda_disk_image), "-f", "qcow2", os.path.join(vm.working_dir, "hda_disk.qcow2"))
@ -355,44 +362,44 @@ def test_disk_options(vm, tmpdir, loop, fake_qemu_img_binary):
assert options == ['-drive', 'file=' + os.path.join(vm.working_dir, "hda_disk.qcow2") + ',if=ide,index=0,media=disk,id=drive0'] assert options == ['-drive', 'file=' + os.path.join(vm.working_dir, "hda_disk.qcow2") + ',if=ide,index=0,media=disk,id=drive0']
def test_cdrom_option(vm, tmpdir, loop, fake_qemu_img_binary): async def test_cdrom_option(vm, tmpdir, fake_qemu_img_binary):
vm.manager.get_qemu_version = AsyncioMagicMock(return_value="3.1.0") vm.manager.get_qemu_version = AsyncioMagicMock(return_value="3.1.0")
vm._cdrom_image = str(tmpdir / "test.iso") vm._cdrom_image = str(tmpdir / "test.iso")
open(vm._cdrom_image, "w+").close() open(vm._cdrom_image, "w+").close()
options = loop.run_until_complete(asyncio.ensure_future(vm._build_command())) options = await vm._build_command()
assert ' '.join(['-cdrom', str(tmpdir / "test.iso")]) in ' '.join(options) assert ' '.join(['-cdrom', str(tmpdir / "test.iso")]) in ' '.join(options)
def test_bios_option(vm, tmpdir, loop, fake_qemu_img_binary): async def test_bios_option(vm, tmpdir, fake_qemu_img_binary):
vm.manager.get_qemu_version = AsyncioMagicMock(return_value="3.1.0") vm.manager.get_qemu_version = AsyncioMagicMock(return_value="3.1.0")
vm._bios_image = str(tmpdir / "test.img") vm._bios_image = str(tmpdir / "test.img")
open(vm._bios_image, "w+").close() open(vm._bios_image, "w+").close()
options = await vm._build_command()
options = loop.run_until_complete(asyncio.ensure_future(vm._build_command()))
assert ' '.join(['-bios', str(tmpdir / "test.img")]) in ' '.join(options) assert ' '.join(['-bios', str(tmpdir / "test.img")]) in ' '.join(options)
def test_vnc_option(vm, tmpdir, loop, fake_qemu_img_binary): async def test_vnc_option(vm, fake_qemu_img_binary):
vm._console_type = 'vnc' vm._console_type = 'vnc'
vm._console = 5905 vm._console = 5905
options = loop.run_until_complete(asyncio.ensure_future(vm._build_command())) options = await vm._build_command()
assert '-vnc 127.0.0.1:5' in ' '.join(options) assert '-vnc 127.0.0.1:5' in ' '.join(options)
def test_spice_option(vm, tmpdir, loop, fake_qemu_img_binary): async def test_spice_option(vm, fake_qemu_img_binary):
vm._console_type = 'spice' vm._console_type = 'spice'
vm._console = 5905 vm._console = 5905
options = loop.run_until_complete(asyncio.ensure_future(vm._build_command())) options = await vm._build_command()
assert '-spice addr=127.0.0.1,port=5905,disable-ticketing' in ' '.join(options) assert '-spice addr=127.0.0.1,port=5905,disable-ticketing' in ' '.join(options)
assert '-vga qxl' in ' '.join(options) assert '-vga qxl' in ' '.join(options)
def test_disk_options_multiple_disk(vm, tmpdir, loop, fake_qemu_img_binary): async def test_disk_options_multiple_disk(vm, tmpdir, fake_qemu_img_binary):
vm._hda_disk_image = str(tmpdir / "test0.qcow2") vm._hda_disk_image = str(tmpdir / "test0.qcow2")
vm._hdb_disk_image = str(tmpdir / "test1.qcow2") vm._hdb_disk_image = str(tmpdir / "test1.qcow2")
@ -403,8 +410,8 @@ def test_disk_options_multiple_disk(vm, tmpdir, loop, fake_qemu_img_binary):
open(vm._hdc_disk_image, "w+").close() open(vm._hdc_disk_image, "w+").close()
open(vm._hdd_disk_image, "w+").close() open(vm._hdd_disk_image, "w+").close()
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process: with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()):
options = loop.run_until_complete(asyncio.ensure_future(vm._disk_options())) options = await vm._disk_options()
assert options == [ assert options == [
'-drive', 'file=' + os.path.join(vm.working_dir, "hda_disk.qcow2") + ',if=ide,index=0,media=disk,id=drive0', '-drive', 'file=' + os.path.join(vm.working_dir, "hda_disk.qcow2") + ',if=ide,index=0,media=disk,id=drive0',
@ -415,70 +422,70 @@ def test_disk_options_multiple_disk(vm, tmpdir, loop, fake_qemu_img_binary):
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") @pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
def test_set_process_priority(vm, loop, fake_qemu_img_binary): async def test_set_process_priority(vm, fake_qemu_img_binary):
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process: with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process:
vm._process = MagicMock() vm._process = MagicMock()
vm._process.pid = 42 vm._process.pid = 42
vm._process_priority = "low" vm._process_priority = "low"
loop.run_until_complete(asyncio.ensure_future(vm._set_process_priority())) await vm._set_process_priority()
assert process.called assert process.called
args, kwargs = process.call_args args, kwargs = process.call_args
assert args == ("renice", "-n", "5", "-p", "42") assert args == ("renice", "-n", "5", "-p", "42")
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") @pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
def test_set_process_priority_normal(vm, loop, fake_qemu_img_binary): async def test_set_process_priority_normal(vm, fake_qemu_img_binary):
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process: with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process:
vm._process = MagicMock() vm._process = MagicMock()
vm._process.pid = 42 vm._process.pid = 42
loop.run_until_complete(asyncio.ensure_future(vm._set_process_priority())) await vm._set_process_priority()
assert not process.called assert not process.called
def test_json(vm, project): def test_json(vm, compute_project):
json = vm.__json__() json = vm.__json__()
assert json["name"] == vm.name assert json["name"] == vm.name
assert json["project_id"] == project.id assert json["project_id"] == compute_project.id
def test_control_vm(vm, loop): async def test_control_vm(vm):
vm._process = MagicMock() vm._process = MagicMock()
reader = MagicMock() reader = MagicMock()
writer = MagicMock() writer = MagicMock()
with asyncio_patch("asyncio.open_connection", return_value=(reader, writer)) as open_connect: with asyncio_patch("asyncio.open_connection", return_value=(reader, writer)):
res = loop.run_until_complete(asyncio.ensure_future(vm._control_vm("test"))) res = await vm._control_vm("test")
assert writer.write.called_with("test") assert writer.write.called_with("test")
assert res is None assert res is None
def test_control_vm_expect_text(vm, loop, running_subprocess_mock): async def test_control_vm_expect_text(vm, running_subprocess_mock):
vm._process = running_subprocess_mock vm._process = running_subprocess_mock
reader = MagicMock() reader = MagicMock()
writer = MagicMock() writer = MagicMock()
with asyncio_patch("asyncio.open_connection", return_value=(reader, writer)) as open_connect: with asyncio_patch("asyncio.open_connection", return_value=(reader, writer)):
future = asyncio.Future() future = asyncio.Future()
future.set_result(b"epic product") future.set_result(b"epic product")
reader.readline.return_value = future reader.readline.return_value = future
vm._monitor = 4242 vm._monitor = 4242
res = loop.run_until_complete(asyncio.ensure_future(vm._control_vm("test", [b"epic"]))) res = await vm._control_vm("test", [b"epic"])
assert writer.write.called_with("test") assert writer.write.called_with("test")
assert res == "epic product" assert res == "epic product"
def test_build_command(vm, loop, fake_qemu_binary, port_manager): async def test_build_command(vm, fake_qemu_binary):
vm.manager.get_qemu_version = AsyncioMagicMock(return_value="3.1.0") vm.manager.get_qemu_version = AsyncioMagicMock(return_value="3.1.0")
os.environ["DISPLAY"] = "0:0" os.environ["DISPLAY"] = "0:0"
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process: with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()):
cmd = loop.run_until_complete(asyncio.ensure_future(vm._build_command())) cmd = await vm._build_command()
nio = vm._local_udp_tunnels[0][0] nio = vm._local_udp_tunnels[0][0]
assert cmd == [ assert cmd == [
fake_qemu_binary, fake_qemu_binary,
@ -505,7 +512,7 @@ def test_build_command(vm, loop, fake_qemu_binary, port_manager):
] ]
def test_build_command_manual_uuid(vm, loop, fake_qemu_binary, port_manager): async def test_build_command_manual_uuid(vm):
""" """
If user has set a uuid we keep it If user has set a uuid we keep it
""" """
@ -513,13 +520,13 @@ def test_build_command_manual_uuid(vm, loop, fake_qemu_binary, port_manager):
vm.manager.get_qemu_version = AsyncioMagicMock(return_value="3.1.0") vm.manager.get_qemu_version = AsyncioMagicMock(return_value="3.1.0")
vm.options = "-uuid e1c307a4-896f-11e6-81a5-3c07547807cc" vm.options = "-uuid e1c307a4-896f-11e6-81a5-3c07547807cc"
os.environ["DISPLAY"] = "0:0" os.environ["DISPLAY"] = "0:0"
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process: with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()):
cmd = loop.run_until_complete(asyncio.ensure_future(vm._build_command())) cmd = await vm._build_command()
assert "e1c307a4-896f-11e6-81a5-3c07547807cc" in cmd assert "e1c307a4-896f-11e6-81a5-3c07547807cc" in cmd
assert vm.id not in cmd assert vm.id not in cmd
def test_build_command_kvm(linux_platform, vm, loop, fake_qemu_binary, port_manager): async def test_build_command_kvm(linux_platform, vm, fake_qemu_binary):
""" """
Qemu 2.4 introduce an issue with KVM Qemu 2.4 introduce an issue with KVM
""" """
@ -528,7 +535,7 @@ def test_build_command_kvm(linux_platform, vm, loop, fake_qemu_binary, port_mana
os.environ["DISPLAY"] = "0:0" os.environ["DISPLAY"] = "0:0"
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM._run_with_hardware_acceleration", return_value=True): with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM._run_with_hardware_acceleration", return_value=True):
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process: with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process:
cmd = loop.run_until_complete(asyncio.ensure_future(vm._build_command())) cmd = await vm._build_command()
nio = vm._local_udp_tunnels[0][0] nio = vm._local_udp_tunnels[0][0]
assert cmd == [ assert cmd == [
fake_qemu_binary, fake_qemu_binary,
@ -555,7 +562,7 @@ def test_build_command_kvm(linux_platform, vm, loop, fake_qemu_binary, port_mana
] ]
def test_build_command_kvm_2_4(linux_platform, vm, loop, fake_qemu_binary, port_manager): async def test_build_command_kvm_2_4(linux_platform, vm, fake_qemu_binary):
""" """
Qemu 2.4 introduce an issue with KVM Qemu 2.4 introduce an issue with KVM
""" """
@ -564,7 +571,7 @@ def test_build_command_kvm_2_4(linux_platform, vm, loop, fake_qemu_binary, port_
os.environ["DISPLAY"] = "0:0" os.environ["DISPLAY"] = "0:0"
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM._run_with_hardware_acceleration", return_value=True): with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM._run_with_hardware_acceleration", return_value=True):
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process: with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process:
cmd = loop.run_until_complete(asyncio.ensure_future(vm._build_command())) cmd = await vm._build_command()
nio = vm._local_udp_tunnels[0][0] nio = vm._local_udp_tunnels[0][0]
assert cmd == [ assert cmd == [
fake_qemu_binary, fake_qemu_binary,
@ -594,22 +601,22 @@ def test_build_command_kvm_2_4(linux_platform, vm, loop, fake_qemu_binary, port_
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") @pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
def test_build_command_without_display(vm, loop, fake_qemu_binary): async def test_build_command_without_display(vm):
vm.manager.get_qemu_version = AsyncioMagicMock(return_value="2.5.0") vm.manager.get_qemu_version = AsyncioMagicMock(return_value="2.5.0")
os.environ["DISPLAY"] = "" os.environ["DISPLAY"] = ""
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process: with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()):
cmd = loop.run_until_complete(asyncio.ensure_future(vm._build_command())) cmd = await vm._build_command()
assert "-nographic" in cmd assert "-nographic" in cmd
def test_build_command_two_adapters(vm, loop, fake_qemu_binary, port_manager): async def test_build_command_two_adapters(vm, fake_qemu_binary):
vm.manager.get_qemu_version = AsyncioMagicMock(return_value="2.5.0") vm.manager.get_qemu_version = AsyncioMagicMock(return_value="2.5.0")
os.environ["DISPLAY"] = "0:0" os.environ["DISPLAY"] = "0:0"
vm.adapters = 2 vm.adapters = 2
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process: with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()):
cmd = loop.run_until_complete(asyncio.ensure_future(vm._build_command())) cmd = await vm._build_command()
nio1 = vm._local_udp_tunnels[0][0] nio1 = vm._local_udp_tunnels[0][0]
nio2 = vm._local_udp_tunnels[1][0] nio2 = vm._local_udp_tunnels[1][0]
assert cmd == [ assert cmd == [
@ -640,7 +647,7 @@ def test_build_command_two_adapters(vm, loop, fake_qemu_binary, port_manager):
] ]
def test_build_command_two_adapters_mac_address(vm, loop, fake_qemu_binary, port_manager): async def test_build_command_two_adapters_mac_address(vm):
""" """
Should support multiple base vmac address Should support multiple base vmac address
""" """
@ -651,8 +658,8 @@ def test_build_command_two_adapters_mac_address(vm, loop, fake_qemu_binary, port
mac_0 = vm._mac_address mac_0 = vm._mac_address
mac_1 = int_to_macaddress(macaddress_to_int(vm._mac_address) + 1) mac_1 = int_to_macaddress(macaddress_to_int(vm._mac_address) + 1)
assert mac_0[:8] == "00:00:ab" assert mac_0[:8] == "00:00:ab"
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process: with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()):
cmd = loop.run_until_complete(asyncio.ensure_future(vm._build_command())) cmd = await vm._build_command()
assert "e1000,mac={},netdev=gns3-0".format(mac_0) in cmd assert "e1000,mac={},netdev=gns3-0".format(mac_0) in cmd
assert "e1000,mac={},netdev=gns3-1".format(mac_1) in cmd assert "e1000,mac={},netdev=gns3-1".format(mac_1) in cmd
@ -660,16 +667,17 @@ def test_build_command_two_adapters_mac_address(vm, loop, fake_qemu_binary, port
mac_0 = vm._mac_address mac_0 = vm._mac_address
mac_1 = int_to_macaddress(macaddress_to_int(vm._mac_address) + 1) mac_1 = int_to_macaddress(macaddress_to_int(vm._mac_address) + 1)
assert mac_0[:8] == "00:42:ab" assert mac_0[:8] == "00:42:ab"
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process: with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()):
cmd = loop.run_until_complete(asyncio.ensure_future(vm._build_command()))
cmd = await vm._build_command()
assert "e1000,mac={},netdev=gns3-0".format(mac_0) in cmd assert "e1000,mac={},netdev=gns3-0".format(mac_0) in cmd
assert "e1000,mac={},netdev=gns3-1".format(mac_1) in cmd assert "e1000,mac={},netdev=gns3-1".format(mac_1) in cmd
def test_build_command_large_number_of_adapters(vm, loop, fake_qemu_binary, port_manager): async def test_build_command_large_number_of_adapters(vm):
""" """
When we have more than 28 interface we need to add a pci bridge for When we have more than 28 interface we need to add a pci bridge for
additionnal interfaces additional interfaces
""" """
# It's supported only with Qemu 2.4 and later # It's supported only with Qemu 2.4 and later
@ -680,8 +688,8 @@ def test_build_command_large_number_of_adapters(vm, loop, fake_qemu_binary, port
mac_0 = vm._mac_address mac_0 = vm._mac_address
mac_1 = int_to_macaddress(macaddress_to_int(vm._mac_address) + 1) mac_1 = int_to_macaddress(macaddress_to_int(vm._mac_address) + 1)
assert mac_0[:8] == "00:00:ab" assert mac_0[:8] == "00:00:ab"
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process: with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()):
cmd = loop.run_until_complete(asyncio.ensure_future(vm._build_command())) cmd = await vm._build_command()
# Count if we have 100 e1000 adapters in the command # Count if we have 100 e1000 adapters in the command
assert len([l for l in cmd if "e1000" in l ]) == 100 assert len([l for l in cmd if "e1000" in l ]) == 100
@ -704,21 +712,19 @@ def test_build_command_large_number_of_adapters(vm, loop, fake_qemu_binary, port
# Qemu < 2.4 doesn't support large number of adapters # Qemu < 2.4 doesn't support large number of adapters
vm.manager.get_qemu_version = AsyncioMagicMock(return_value="2.0.0") vm.manager.get_qemu_version = AsyncioMagicMock(return_value="2.0.0")
with pytest.raises(QemuError): with pytest.raises(QemuError):
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process: with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()):
cmd = loop.run_until_complete(asyncio.ensure_future(vm._build_command())) await vm._build_command()
vm.adapters = 5 vm.adapters = 5
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process: with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()):
cmd = loop.run_until_complete(asyncio.ensure_future(vm._build_command())) await vm._build_command()
# Windows accept this kind of mistake
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") @pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
def test_build_command_with_invalid_options(vm, loop, fake_qemu_binary): async def test_build_command_with_invalid_options(vm):
vm.options = "'test" vm.options = "'test"
with pytest.raises(QemuError): with pytest.raises(QemuError):
cmd = loop.run_until_complete(asyncio.ensure_future(vm._build_command())) await vm._build_command()
def test_hda_disk_image(vm, images_dir): def test_hda_disk_image(vm, images_dir):
@ -731,7 +737,7 @@ def test_hda_disk_image(vm, images_dir):
assert vm.hda_disk_image == force_unix_path(os.path.join(images_dir, "QEMU", "test2")) assert vm.hda_disk_image == force_unix_path(os.path.join(images_dir, "QEMU", "test2"))
def test_hda_disk_image_non_linked_clone(vm, images_dir, project, manager, fake_qemu_binary, fake_qemu_img_binary): def test_hda_disk_image_non_linked_clone(vm, images_dir, compute_project, manager, fake_qemu_binary):
""" """
Two non linked can't use the same image at the same time Two non linked can't use the same image at the same time
""" """
@ -741,7 +747,7 @@ def test_hda_disk_image_non_linked_clone(vm, images_dir, project, manager, fake_
vm.hda_disk_image = os.path.join(images_dir, "test1") vm.hda_disk_image = os.path.join(images_dir, "test1")
vm.manager._nodes[vm.id] = vm vm.manager._nodes[vm.id] = vm
vm2 = QemuVM("test", "00010203-0405-0607-0809-0a0b0c0d0eaa", project, manager, qemu_path=fake_qemu_binary) vm2 = QemuVM("test", "00010203-0405-0607-0809-0a0b0c0d0eaa", compute_project, manager, qemu_path=fake_qemu_binary)
vm2.linked_clone = False vm2.linked_clone = False
with pytest.raises(QemuError): with pytest.raises(QemuError):
vm2.hda_disk_image = os.path.join(images_dir, "test1") vm2.hda_disk_image = os.path.join(images_dir, "test1")
@ -833,58 +839,58 @@ def test_options_windows(windows_platform, vm):
def test_get_qemu_img(vm, tmpdir): def test_get_qemu_img(vm, tmpdir):
open(str(tmpdir / "qemu-sytem-x86_64"), "w+").close() open(str(tmpdir / "qemu-system-x86_64"), "w+").close()
open(str(tmpdir / "qemu-img"), "w+").close() open(str(tmpdir / "qemu-img"), "w+").close()
vm._qemu_path = str(tmpdir / "qemu-sytem-x86_64") vm._qemu_path = str(tmpdir / "qemu-system-x86_64")
assert vm._get_qemu_img() == str(tmpdir / "qemu-img") assert vm._get_qemu_img() == str(tmpdir / "qemu-img")
def test_get_qemu_img_not_exist(vm, tmpdir): # def test_get_qemu_img_not_exist(vm, tmpdir):
#
open(str(tmpdir / "qemu-sytem-x86_64"), "w+").close() # open(str(tmpdir / "qemu-system-x86_64"), "w+").close()
vm._qemu_path = str(tmpdir / "qemu-sytem-x86_64") # vm._qemu_path = str(tmpdir / "qemu-system-x86_64")
with pytest.raises(QemuError): # with pytest.raises(QemuError):
vm._get_qemu_img() # vm._get_qemu_img()
def test_run_with_hardware_acceleration_darwin(darwin_platform, vm, loop): async def test_run_with_hardware_acceleration_darwin(darwin_platform, vm):
vm.manager.config.set("Qemu", "enable_hardware_acceleration", False) vm.manager.config.set("Qemu", "enable_hardware_acceleration", False)
assert loop.run_until_complete(asyncio.ensure_future(vm._run_with_hardware_acceleration("qemu-system-x86_64", ""))) is False assert await vm._run_with_hardware_acceleration("qemu-system-x86_64", "") is False
def test_run_with_hardware_acceleration_windows(windows_platform, vm, loop): async def test_run_with_hardware_acceleration_windows(windows_platform, vm):
vm.manager.config.set("Qemu", "enable_hardware_acceleration", False) vm.manager.config.set("Qemu", "enable_hardware_acceleration", False)
assert loop.run_until_complete(asyncio.ensure_future(vm._run_with_hardware_acceleration("qemu-system-x86_64", ""))) is False assert await vm._run_with_hardware_acceleration("qemu-system-x86_64", "") is False
def test_run_with_kvm_linux(linux_platform, vm, loop): async def test_run_with_kvm_linux(linux_platform, vm):
with patch("os.path.exists", return_value=True) as os_path: with patch("os.path.exists", return_value=True) as os_path:
vm.manager.config.set("Qemu", "enable_kvm", True) vm.manager.config.set("Qemu", "enable_kvm", True)
assert loop.run_until_complete(asyncio.ensure_future(vm._run_with_hardware_acceleration("qemu-system-x86_64", ""))) is True assert await vm._run_with_hardware_acceleration("qemu-system-x86_64", "") is True
os_path.assert_called_with("/dev/kvm") os_path.assert_called_with("/dev/kvm")
def test_run_with_kvm_linux_options_no_kvm(linux_platform, vm, loop): async def test_run_with_kvm_linux_options_no_kvm(linux_platform, vm):
with patch("os.path.exists", return_value=True) as os_path: with patch("os.path.exists", return_value=True) as os_path:
vm.manager.config.set("Qemu", "enable_kvm", True) vm.manager.config.set("Qemu", "enable_kvm", True)
assert loop.run_until_complete(asyncio.ensure_future(vm._run_with_hardware_acceleration("qemu-system-x86_64", "-no-kvm"))) is False assert await vm._run_with_hardware_acceleration("qemu-system-x86_64", "-no-kvm") is False
def test_run_with_kvm_not_x86(linux_platform, vm, loop): async def test_run_with_kvm_not_x86(linux_platform, vm):
with patch("os.path.exists", return_value=True) as os_path: with patch("os.path.exists", return_value=True):
vm.manager.config.set("Qemu", "enable_kvm", True) vm.manager.config.set("Qemu", "enable_kvm", True)
with pytest.raises(QemuError): with pytest.raises(QemuError):
ret = loop.run_until_complete(asyncio.ensure_future(vm._run_with_hardware_acceleration("qemu-system-arm", ""))) await vm._run_with_hardware_acceleration("qemu-system-arm", "")
def test_run_with_kvm_linux_dev_kvm_missing(linux_platform, vm, loop): async def test_run_with_kvm_linux_dev_kvm_missing(linux_platform, vm):
with patch("os.path.exists", return_value=False) as os_path: with patch("os.path.exists", return_value=False):
vm.manager.config.set("Qemu", "enable_kvm", True) vm.manager.config.set("Qemu", "enable_kvm", True)
with pytest.raises(QemuError): with pytest.raises(QemuError):
ret = loop.run_until_complete(asyncio.ensure_future(vm._run_with_hardware_acceleration("qemu-system-x86_64", ""))) await vm._run_with_hardware_acceleration("qemu-system-x86_64", "")

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -14,43 +14,44 @@
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from collections import OrderedDict from collections import OrderedDict
import pytest import pytest
import aiohttp
import asyncio import asyncio
import os
from tests.utils import asyncio_patch, AsyncioMagicMock
from tests.utils import asyncio_patch, AsyncioMagicMock
from unittest.mock import patch, MagicMock
from gns3server.compute.vpcs.vpcs_vm import VPCSVM from gns3server.compute.vpcs.vpcs_vm import VPCSVM
from gns3server.compute.docker.docker_vm import DockerVM from gns3server.compute.docker.docker_vm import DockerVM
from gns3server.compute.vpcs.vpcs_error import VPCSError
from gns3server.compute.error import NodeError from gns3server.compute.error import NodeError
from gns3server.compute.vpcs import VPCS from gns3server.compute.vpcs import VPCS
from gns3server.compute.nios.nio_udp import NIOUDP from gns3server.compute.nios.nio_udp import NIOUDP
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def manager(port_manager): async def manager(loop, port_manager):
m = VPCS.instance() m = VPCS.instance()
m.port_manager = port_manager m.port_manager = port_manager
return m return m
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def node(project, manager): def node(compute_project, manager):
return VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager)
return VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", compute_project, manager)
def test_temporary_directory(project, manager): def test_temporary_directory(compute_project, manager):
node = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager)
node = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", compute_project, manager)
assert isinstance(node.temporary_directory, str) assert isinstance(node.temporary_directory, str)
def test_console(project, manager): def test_console(compute_project, manager):
node = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager)
node = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", compute_project, manager)
node.console = 5011 node.console = 5011
assert node.console == 5011 assert node.console == 5011
node.console = None node.console = None
@ -58,6 +59,7 @@ def test_console(project, manager):
def test_change_console_port(node, port_manager): def test_change_console_port(node, port_manager):
port1 = port_manager.get_free_tcp_port(node.project) port1 = port_manager.get_free_tcp_port(node.project)
port2 = port_manager.get_free_tcp_port(node.project) port2 = port_manager.get_free_tcp_port(node.project)
port_manager.release_tcp_port(port1, node.project) port_manager.release_tcp_port(port1, node.project)
@ -68,22 +70,23 @@ def test_change_console_port(node, port_manager):
port_manager.reserve_tcp_port(port1, node.project) port_manager.reserve_tcp_port(port1, node.project)
def test_console_vnc_invalid(project, manager): def test_console_vnc_invalid(compute_project, manager):
node = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager)
node = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", compute_project, manager)
node._console_type = "vnc" node._console_type = "vnc"
with pytest.raises(NodeError): with pytest.raises(NodeError):
node.console = 2012 node.console = 2012
def test_close(node, loop, port_manager): async def test_close(node, port_manager):
assert node.console is not None
assert node.console is not None
aux = port_manager.get_free_tcp_port(node.project) aux = port_manager.get_free_tcp_port(node.project)
port_manager.release_tcp_port(aux, node.project) port_manager.release_tcp_port(aux, node.project)
node.aux = aux node.aux = aux
port = node.console port = node.console
assert loop.run_until_complete(asyncio.ensure_future(node.close())) assert await node.close()
# Raise an exception if the port is not free # Raise an exception if the port is not free
port_manager.reserve_tcp_port(port, node.project) port_manager.reserve_tcp_port(port, node.project)
# Raise an exception if the port is not free # Raise an exception if the port is not free
@ -92,29 +95,32 @@ def test_close(node, loop, port_manager):
assert node.aux is None assert node.aux is None
# Called twice closed should return False # Called twice closed should return False
assert loop.run_until_complete(asyncio.ensure_future(node.close())) is False assert await node.close() is False
def test_aux(project, manager, port_manager): def test_aux(compute_project, manager, port_manager):
aux = port_manager.get_free_tcp_port(project)
port_manager.release_tcp_port(aux, project)
node = DockerVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, "ubuntu", aux=aux) aux = port_manager.get_free_tcp_port(compute_project)
port_manager.release_tcp_port(aux, compute_project)
node = DockerVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", compute_project, manager, "ubuntu", aux=aux)
assert node.aux == aux assert node.aux == aux
node.aux = None node.aux = None
assert node.aux is None assert node.aux is None
def test_allocate_aux(project, manager): def test_allocate_aux(compute_project, manager):
node = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager)
node = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", compute_project, manager)
assert node.aux is None assert node.aux is None
# Docker has an aux port by default # Docker has an aux port by default
node = DockerVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, "ubuntu") node = DockerVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", compute_project, manager, "ubuntu")
assert node.aux is not None assert node.aux is not None
def test_change_aux_port(node, port_manager): def test_change_aux_port(node, port_manager):
port1 = port_manager.get_free_tcp_port(node.project) port1 = port_manager.get_free_tcp_port(node.project)
port2 = port_manager.get_free_tcp_port(node.project) port2 = port_manager.get_free_tcp_port(node.project)
port_manager.release_tcp_port(port1, node.project) port_manager.release_tcp_port(port1, node.project)
@ -125,7 +131,8 @@ def test_change_aux_port(node, port_manager):
port_manager.reserve_tcp_port(port1, node.project) port_manager.reserve_tcp_port(port1, node.project)
def test_update_ubridge_udp_connection(node, async_run): async def test_update_ubridge_udp_connection(node):
filters = { filters = {
"latency": [10] "latency": [10]
} }
@ -134,27 +141,29 @@ def test_update_ubridge_udp_connection(node, async_run):
dnio = NIOUDP(1245, "localhost", 1244) dnio = NIOUDP(1245, "localhost", 1244)
dnio.filters = filters dnio.filters = filters
with asyncio_patch("gns3server.compute.base_node.BaseNode._ubridge_apply_filters") as mock: with asyncio_patch("gns3server.compute.base_node.BaseNode._ubridge_apply_filters") as mock:
async_run(node.update_ubridge_udp_connection('VPCS-10', snio, dnio)) await node.update_ubridge_udp_connection('VPCS-10', snio, dnio)
mock.assert_called_with("VPCS-10", filters) mock.assert_called_with("VPCS-10", filters)
def test_ubridge_apply_filters(node, async_run): async def test_ubridge_apply_filters(node):
filters = OrderedDict(( filters = OrderedDict((
('latency', [10]), ('latency', [10]),
('bpf', ["icmp[icmptype] == 8\ntcp src port 53"]) ('bpf', ["icmp[icmptype] == 8\ntcp src port 53"])
)) ))
node._ubridge_send = AsyncioMagicMock() node._ubridge_send = AsyncioMagicMock()
async_run(node._ubridge_apply_filters("VPCS-10", filters)) await node._ubridge_apply_filters("VPCS-10", filters)
node._ubridge_send.assert_any_call("bridge reset_packet_filters VPCS-10") node._ubridge_send.assert_any_call("bridge reset_packet_filters VPCS-10")
node._ubridge_send.assert_any_call("bridge add_packet_filter VPCS-10 filter0 latency 10") node._ubridge_send.assert_any_call("bridge add_packet_filter VPCS-10 filter0 latency 10")
def test_ubridge_apply_bpf_filters(node, async_run): async def test_ubridge_apply_bpf_filters(node):
filters = { filters = {
"bpf": ["icmp[icmptype] == 8\ntcp src port 53"] "bpf": ["icmp[icmptype] == 8\ntcp src port 53"]
} }
node._ubridge_send = AsyncioMagicMock() node._ubridge_send = AsyncioMagicMock()
async_run(node._ubridge_apply_filters("VPCS-10", filters)) await node._ubridge_apply_filters("VPCS-10", filters)
node._ubridge_send.assert_any_call("bridge reset_packet_filters VPCS-10") node._ubridge_send.assert_any_call("bridge reset_packet_filters VPCS-10")
node._ubridge_send.assert_any_call("bridge add_packet_filter VPCS-10 filter0 bpf \"icmp[icmptype] == 8\"") node._ubridge_send.assert_any_call("bridge add_packet_filter VPCS-10 filter0 bpf \"icmp[icmptype] == 8\"")
node._ubridge_send.assert_any_call("bridge add_packet_filter VPCS-10 filter1 bpf \"tcp src port 53\"") node._ubridge_send.assert_any_call("bridge add_packet_filter VPCS-10 filter1 bpf \"tcp src port 53\"")

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -19,8 +19,7 @@ import uuid
import os import os
import pytest import pytest
from unittest.mock import patch from unittest.mock import patch
from tests.utils import AsyncioMagicMock, asyncio_patch from tests.utils import asyncio_patch
from gns3server.compute.vpcs import VPCS from gns3server.compute.vpcs import VPCS
from gns3server.compute.dynamips import Dynamips from gns3server.compute.dynamips import Dynamips
@ -30,7 +29,8 @@ from gns3server.utils import force_unix_path
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def vpcs(port_manager): async def vpcs(loop, port_manager):
VPCS._instance = None VPCS._instance = None
vpcs = VPCS.instance() vpcs = VPCS.instance()
vpcs.port_manager = port_manager vpcs.port_manager = port_manager
@ -38,49 +38,53 @@ def vpcs(port_manager):
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def qemu(port_manager): async def qemu(loop, port_manager):
Qemu._instance = None Qemu._instance = None
qemu = Qemu.instance() qemu = Qemu.instance()
qemu.port_manager = port_manager qemu.port_manager = port_manager
return qemu return qemu
def test_create_node_new_topology(loop, project, vpcs): async def test_create_node_new_topology(compute_project, vpcs):
node_id = str(uuid.uuid4()) node_id = str(uuid.uuid4())
node = loop.run_until_complete(vpcs.create_node("PC 1", project.id, node_id)) node = await vpcs.create_node("PC 1", compute_project.id, node_id)
assert node in project.nodes assert node in compute_project.nodes
async def test_create_twice_same_node_new_topology(compute_project, vpcs):
def test_create_twice_same_node_new_topology(loop, project, vpcs): compute_project._nodes = set()
project._nodes = set()
node_id = str(uuid.uuid4()) node_id = str(uuid.uuid4())
node = loop.run_until_complete(vpcs.create_node("PC 1", project.id, node_id, console=2222)) node = await vpcs.create_node("PC 1", compute_project.id, node_id, console=2222)
assert node in project.nodes assert node in compute_project.nodes
assert len(project.nodes) == 1 assert len(compute_project.nodes) == 1
node = loop.run_until_complete(vpcs.create_node("PC 2", project.id, node_id, console=2222)) await vpcs.create_node("PC 2", compute_project.id, node_id, console=2222)
assert len(project.nodes) == 1 assert len(compute_project.nodes) == 1
def test_create_node_new_topology_without_uuid(loop, project, vpcs): async def test_create_node_new_topology_without_uuid(compute_project, vpcs):
node = loop.run_until_complete(vpcs.create_node("PC 1", project.id, None))
assert node in project.nodes node = await vpcs.create_node("PC 1", compute_project.id, None)
assert node in compute_project.nodes
assert len(node.id) == 36 assert len(node.id) == 36
def test_create_node_old_topology(loop, project, tmpdir, vpcs): async def test_create_node_old_topology(compute_project, tmpdir, vpcs):
with patch("gns3server.compute.project.Project.is_local", return_value=True): with patch("gns3server.compute.project.Project.is_local", return_value=True):
# Create an old topology directory # Create an old topology directory
project_dir = str(tmpdir / "testold") project_dir = str(tmpdir / "testold")
node_dir = os.path.join(project_dir, "testold-files", "vpcs", "pc-1") node_dir = os.path.join(project_dir, "testold-files", "vpcs", "pc-1")
project.path = project_dir compute_project.path = project_dir
project.name = "testold" compute_project.name = "testold"
os.makedirs(node_dir, exist_ok=True) os.makedirs(node_dir, exist_ok=True)
with open(os.path.join(node_dir, "startup.vpc"), "w+") as f: with open(os.path.join(node_dir, "startup.vpc"), "w+") as f:
f.write("1") f.write("1")
node_id = 1 node_id = 1
node = loop.run_until_complete(vpcs.create_node("PC 1", project.id, node_id)) node = await vpcs.create_node("PC 1", compute_project.id, node_id)
assert len(node.id) == 36 assert len(node.id) == 36
assert os.path.exists(os.path.join(project_dir, "testold-files")) is False assert os.path.exists(os.path.join(project_dir, "testold-files")) is False
@ -91,6 +95,7 @@ def test_create_node_old_topology(loop, project, tmpdir, vpcs):
def test_get_abs_image_path(qemu, tmpdir, config): def test_get_abs_image_path(qemu, tmpdir, config):
os.makedirs(str(tmpdir / "QEMU")) os.makedirs(str(tmpdir / "QEMU"))
path1 = force_unix_path(str(tmpdir / "test1.bin")) path1 = force_unix_path(str(tmpdir / "test1.bin"))
open(path1, 'w+').close() open(path1, 'w+').close()
@ -107,6 +112,7 @@ def test_get_abs_image_path(qemu, tmpdir, config):
def test_get_abs_image_path_non_local(qemu, tmpdir, config): def test_get_abs_image_path_non_local(qemu, tmpdir, config):
path1 = tmpdir / "images" / "QEMU" / "test1.bin" path1 = tmpdir / "images" / "QEMU" / "test1.bin"
path1.write("1", ensure=True) path1.write("1", ensure=True)
path1 = force_unix_path(str(path1)) path1 = force_unix_path(str(path1))
@ -128,6 +134,7 @@ def test_get_abs_image_path_non_local(qemu, tmpdir, config):
def test_get_abs_image_additional_image_paths(qemu, tmpdir, config): def test_get_abs_image_additional_image_paths(qemu, tmpdir, config):
path1 = tmpdir / "images1" / "QEMU" / "test1.bin" path1 = tmpdir / "images1" / "QEMU" / "test1.bin"
path1.write("1", ensure=True) path1.write("1", ensure=True)
path1 = force_unix_path(str(path1)) path1 = force_unix_path(str(path1))
@ -151,6 +158,7 @@ def test_get_abs_image_additional_image_paths(qemu, tmpdir, config):
def test_get_abs_image_recursive(qemu, tmpdir, config): def test_get_abs_image_recursive(qemu, tmpdir, config):
path1 = tmpdir / "images1" / "QEMU" / "demo" / "test1.bin" path1 = tmpdir / "images1" / "QEMU" / "demo" / "test1.bin"
path1.write("1", ensure=True) path1.write("1", ensure=True)
path1 = force_unix_path(str(path1)) path1 = force_unix_path(str(path1))
@ -169,6 +177,7 @@ def test_get_abs_image_recursive(qemu, tmpdir, config):
def test_get_abs_image_recursive_ova(qemu, tmpdir, config): def test_get_abs_image_recursive_ova(qemu, tmpdir, config):
path1 = tmpdir / "images1" / "QEMU" / "demo" / "test.ova" / "test1.bin" path1 = tmpdir / "images1" / "QEMU" / "demo" / "test.ova" / "test1.bin"
path1.write("1", ensure=True) path1.write("1", ensure=True)
path1 = force_unix_path(str(path1)) path1 = force_unix_path(str(path1))
@ -187,6 +196,7 @@ def test_get_abs_image_recursive_ova(qemu, tmpdir, config):
def test_get_relative_image_path(qemu, tmpdir, config): def test_get_relative_image_path(qemu, tmpdir, config):
os.makedirs(str(tmpdir / "images1" / "QEMU")) os.makedirs(str(tmpdir / "images1" / "QEMU"))
os.makedirs(str(tmpdir / "images1" / "VBOX")) os.makedirs(str(tmpdir / "images1" / "VBOX"))
path1 = force_unix_path(str(tmpdir / "images1" / "test1.bin")) path1 = force_unix_path(str(tmpdir / "images1" / "test1.bin"))
@ -221,7 +231,7 @@ def test_get_relative_image_path(qemu, tmpdir, config):
assert qemu.get_relative_image_path(path5) == path5 assert qemu.get_relative_image_path(path5) == path5
def test_list_images(loop, qemu, tmpdir): async def test_list_images(qemu, tmpdir):
fake_images = ["a.qcow2", "b.qcow2", ".blu.qcow2", "a.qcow2.md5sum"] fake_images = ["a.qcow2", "b.qcow2", ".blu.qcow2", "a.qcow2.md5sum"]
tmp_images_dir = os.path.join(tmpdir, "images") tmp_images_dir = os.path.join(tmpdir, "images")
@ -231,13 +241,13 @@ def test_list_images(loop, qemu, tmpdir):
f.write("1") f.write("1")
with patch("gns3server.utils.images.default_images_directory", return_value=str(tmp_images_dir)): with patch("gns3server.utils.images.default_images_directory", return_value=str(tmp_images_dir)):
assert sorted(loop.run_until_complete(qemu.list_images()), key=lambda k: k['filename']) == [ assert sorted(await qemu.list_images(), key=lambda k: k['filename']) == [
{"filename": "a.qcow2", "path": "a.qcow2", "md5sum": "c4ca4238a0b923820dcc509a6f75849b", "filesize": 1}, {"filename": "a.qcow2", "path": "a.qcow2", "md5sum": "c4ca4238a0b923820dcc509a6f75849b", "filesize": 1},
{"filename": "b.qcow2", "path": "b.qcow2", "md5sum": "c4ca4238a0b923820dcc509a6f75849b", "filesize": 1} {"filename": "b.qcow2", "path": "b.qcow2", "md5sum": "c4ca4238a0b923820dcc509a6f75849b", "filesize": 1}
] ]
def test_list_images_recursives(loop, qemu, tmpdir): async def test_list_images_recursives(qemu, tmpdir):
tmp_images_dir = os.path.join(tmpdir, "images") tmp_images_dir = os.path.join(tmpdir, "images")
os.makedirs(tmp_images_dir, exist_ok=True) os.makedirs(tmp_images_dir, exist_ok=True)
@ -253,52 +263,57 @@ def test_list_images_recursives(loop, qemu, tmpdir):
with patch("gns3server.utils.images.default_images_directory", return_value=str(tmp_images_dir)): with patch("gns3server.utils.images.default_images_directory", return_value=str(tmp_images_dir)):
assert sorted(loop.run_until_complete(qemu.list_images()), key=lambda k: k['filename']) == [ assert sorted(await qemu.list_images(), key=lambda k: k['filename']) == [
{"filename": "a.qcow2", "path": "a.qcow2", "md5sum": "c4ca4238a0b923820dcc509a6f75849b", "filesize": 1}, {"filename": "a.qcow2", "path": "a.qcow2", "md5sum": "c4ca4238a0b923820dcc509a6f75849b", "filesize": 1},
{"filename": "b.qcow2", "path": "b.qcow2", "md5sum": "c4ca4238a0b923820dcc509a6f75849b", "filesize": 1}, {"filename": "b.qcow2", "path": "b.qcow2", "md5sum": "c4ca4238a0b923820dcc509a6f75849b", "filesize": 1},
{"filename": "c.qcow2", "path": force_unix_path(os.path.sep.join(["c", "c.qcow2"])), "md5sum": "c4ca4238a0b923820dcc509a6f75849b", "filesize": 1} {"filename": "c.qcow2", "path": force_unix_path(os.path.sep.join(["c", "c.qcow2"])), "md5sum": "c4ca4238a0b923820dcc509a6f75849b", "filesize": 1}
] ]
def test_list_images_empty(loop, qemu, tmpdir): async def test_list_images_empty(qemu, tmpdir):
with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir)): with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir)):
assert loop.run_until_complete(qemu.list_images()) == [] assert await qemu.list_images() == []
def test_list_images_directory_not_exist(loop, qemu): async def test_list_images_directory_not_exist(qemu):
with patch("gns3server.compute.Qemu.get_images_directory", return_value="/bla"): with patch("gns3server.compute.Qemu.get_images_directory", return_value="/bla"):
assert loop.run_until_complete(qemu.list_images()) == [] assert await qemu.list_images() == []
async def test_delete_node(vpcs, compute_project):
def test_delete_node(async_run, vpcs, project): compute_project._nodes = set()
project._nodes = set()
node_id = str(uuid.uuid4()) node_id = str(uuid.uuid4())
node = async_run(vpcs.create_node("PC 1", project.id, node_id, console=2222)) node = await vpcs.create_node("PC 1", compute_project.id, node_id, console=2222)
assert node in project.nodes assert node in compute_project.nodes
with patch("gns3server.compute.project.Project.emit") as mock_emit: with patch("gns3server.compute.project.Project.emit") as mock_emit:
async_run(vpcs.delete_node(node_id)) await vpcs.delete_node(node_id)
mock_emit.assert_called_with("node.deleted", node) mock_emit.assert_called_with("node.deleted", node)
assert node not in project.nodes assert node not in compute_project.nodes
def test_duplicate_vpcs(async_run, vpcs, project): async def test_duplicate_vpcs(vpcs, compute_project):
source_node_id = str(uuid.uuid4()) source_node_id = str(uuid.uuid4())
source_node = async_run(vpcs.create_node("PC-1", project.id, source_node_id, console=2222)) source_node = await vpcs.create_node("PC-1", compute_project.id, source_node_id, console=2222)
with open(os.path.join(source_node.working_dir, "startup.vpc"), "w+") as f: with open(os.path.join(source_node.working_dir, "startup.vpc"), "w+") as f:
f.write("set pcname PC-1\nip dhcp\n") f.write("set pcname PC-1\nip dhcp\n")
destination_node_id = str(uuid.uuid4()) destination_node_id = str(uuid.uuid4())
destination_node = async_run(vpcs.create_node("PC-2", project.id, destination_node_id, console=2223)) destination_node = await vpcs.create_node("PC-2", compute_project.id, destination_node_id, console=2223)
async_run(vpcs.duplicate_node(source_node_id, destination_node_id)) await vpcs.duplicate_node(source_node_id, destination_node_id)
with open(os.path.join(destination_node.working_dir, "startup.vpc")) as f: with open(os.path.join(destination_node.working_dir, "startup.vpc")) as f:
startup = f.read().strip() startup = f.read().strip()
assert startup == "set pcname PC-2\nip dhcp\n".strip() assert startup == "set pcname PC-2\nip dhcp\n".strip()
def test_duplicate_ethernet_switch(async_run, project): async def test_duplicate_ethernet_switch(compute_project):
with asyncio_patch('gns3server.compute.dynamips.nodes.ethernet_switch.EthernetSwitch.create'): with asyncio_patch('gns3server.compute.dynamips.nodes.ethernet_switch.EthernetSwitch.create'):
dynamips_manager = Dynamips.instance() dynamips_manager = Dynamips.instance()
source_node_id = str(uuid.uuid4()) source_node_id = str(uuid.uuid4())
source_node = async_run(dynamips_manager.create_node("SW-1", project.id, source_node_id, node_type='ethernet_switch')) await dynamips_manager.create_node("SW-1", compute_project.id, source_node_id, node_type='ethernet_switch')
destination_node_id = str(uuid.uuid4()) destination_node_id = str(uuid.uuid4())
destination_node = async_run(dynamips_manager.create_node("SW-2", project.id, destination_node_id, node_type='ethernet_switch')) await dynamips_manager.create_node("SW-2", compute_project.id, destination_node_id, node_type='ethernet_switch')
async_run(dynamips_manager.duplicate_node(source_node_id, destination_node_id)) await dynamips_manager.duplicate_node(source_node_id, destination_node_id)

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2016 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -20,69 +20,73 @@ import uuid
from gns3server.compute.notification_manager import NotificationManager from gns3server.compute.notification_manager import NotificationManager
def test_queue(async_run): async def test_queue():
NotificationManager.reset() NotificationManager.reset()
notifications = NotificationManager.instance() notifications = NotificationManager.instance()
with notifications.queue() as queue: with notifications.queue() as queue:
assert len(notifications._listeners) == 1 assert len(notifications._listeners) == 1
res = async_run(queue.get(5)) res = await queue.get(5)
assert res[0] == "ping" assert res[0] == "ping"
notifications.emit("test", {"a": 1}) notifications.emit("test", {"a": 1})
res = async_run(queue.get(5)) res = await queue.get(5)
assert res == ('test', {"a": 1}, {}) assert res == ('test', {"a": 1}, {})
assert len(notifications._listeners) == 0 assert len(notifications._listeners) == 0
def test_queue_json(async_run): async def test_queue_json():
NotificationManager.reset() NotificationManager.reset()
notifications = NotificationManager.instance() notifications = NotificationManager.instance()
with notifications.queue() as queue: with notifications.queue() as queue:
assert len(notifications._listeners) == 1 assert len(notifications._listeners) == 1
res = async_run(queue.get(5)) res = await queue.get(5)
assert "ping" in res assert "ping" in res
notifications.emit("test", {"a": 1}) notifications.emit("test", {"a": 1})
res = async_run(queue.get_json(5)) res = await queue.get_json(5)
assert res == '{"action": "test", "event": {"a": 1}}' assert res == '{"action": "test", "event": {"a": 1}}'
assert len(notifications._listeners) == 0 assert len(notifications._listeners) == 0
def test_queue_json_meta(async_run): async def test_queue_json_meta():
NotificationManager.reset() NotificationManager.reset()
project_id = str(uuid.uuid4()) project_id = str(uuid.uuid4())
notifications = NotificationManager.instance() notifications = NotificationManager.instance()
with notifications.queue() as queue: with notifications.queue() as queue:
assert len(notifications._listeners) == 1 assert len(notifications._listeners) == 1
res = async_run(queue.get(5)) res = await queue.get(5)
assert "ping" in res assert "ping" in res
notifications.emit("test", {"a": 1}, project_id=project_id) notifications.emit("test", {"a": 1}, project_id=project_id)
res = async_run(queue.get_json(5)) res = await queue.get_json(5)
assert res == '{"action": "test", "event": {"a": 1}, "project_id": "' + project_id + '"}' assert res == '{"action": "test", "event": {"a": 1}, "project_id": "' + project_id + '"}'
assert len(notifications._listeners) == 0 assert len(notifications._listeners) == 0
def test_queue_ping(async_run): async def test_queue_ping():
""" """
If we don't send a message during a long time (0.5 seconds) If we don't send a message during a long time (0.5 seconds)
a ping is send a ping is send
""" """
NotificationManager.reset() NotificationManager.reset()
notifications = NotificationManager.instance() notifications = NotificationManager.instance()
with notifications.queue() as queue: with notifications.queue() as queue:
assert len(notifications._listeners) == 1 assert len(notifications._listeners) == 1
res = async_run(queue.get(5)) res = await queue.get(5)
assert res[0] == "ping" assert res[0] == "ping"
res = async_run(queue.get(0.5)) res = await queue.get(0.5)
assert res[0] == "ping" assert res[0] == "ping"
assert res[1]["cpu_usage_percent"] is not None assert res[1]["cpu_usage_percent"] is not None
assert len(notifications._listeners) == 0 assert len(notifications._listeners) == 0

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -17,7 +17,6 @@
import aiohttp import aiohttp
import pytest import pytest
import sys
import uuid import uuid
from unittest.mock import patch from unittest.mock import patch
@ -26,6 +25,7 @@ from gns3server.compute.project import Project
def test_reserve_tcp_port(): def test_reserve_tcp_port():
pm = PortManager() pm = PortManager()
project = Project(project_id=str(uuid.uuid4())) project = Project(project_id=str(uuid.uuid4()))
pm.reserve_tcp_port(2001, project) pm.reserve_tcp_port(2001, project)
@ -35,6 +35,7 @@ def test_reserve_tcp_port():
def test_reserve_tcp_port_outside_range(): def test_reserve_tcp_port_outside_range():
pm = PortManager() pm = PortManager()
project = Project(project_id=str(uuid.uuid4())) project = Project(project_id=str(uuid.uuid4()))
with patch("gns3server.compute.project.Project.emit") as mock_emit: with patch("gns3server.compute.project.Project.emit") as mock_emit:
@ -60,7 +61,7 @@ def test_reserve_tcp_port_already_used_by_another_program():
mock_check.side_effect = execute_mock mock_check.side_effect = execute_mock
with patch("gns3server.compute.project.Project.emit") as mock_emit: with patch("gns3server.compute.project.Project.emit"):
port = pm.reserve_tcp_port(2001, project) port = pm.reserve_tcp_port(2001, project)
assert port != 2001 assert port != 2001
@ -68,7 +69,7 @@ def test_reserve_tcp_port_already_used_by_another_program():
def test_reserve_tcp_port_already_used(): def test_reserve_tcp_port_already_used():
""" """
This test simulate a scenario where the port is already taken This test simulate a scenario where the port is already taken
by another programm on the server by another program on the server
""" """
pm = PortManager() pm = PortManager()
@ -83,12 +84,13 @@ def test_reserve_tcp_port_already_used():
mock_check.side_effect = execute_mock mock_check.side_effect = execute_mock
with patch("gns3server.compute.project.Project.emit") as mock_emit: with patch("gns3server.compute.project.Project.emit"):
port = pm.reserve_tcp_port(2001, project) port = pm.reserve_tcp_port(2001, project)
assert port != 2001 assert port != 2001
def test_reserve_udp_port(): def test_reserve_udp_port():
pm = PortManager() pm = PortManager()
project = Project(project_id=str(uuid.uuid4())) project = Project(project_id=str(uuid.uuid4()))
pm.reserve_udp_port(20000, project) pm.reserve_udp_port(20000, project)
@ -97,6 +99,7 @@ def test_reserve_udp_port():
def test_reserve_udp_port_outside_range(): def test_reserve_udp_port_outside_range():
pm = PortManager() pm = PortManager()
project = Project(project_id=str(uuid.uuid4())) project = Project(project_id=str(uuid.uuid4()))
with pytest.raises(aiohttp.web.HTTPConflict): with pytest.raises(aiohttp.web.HTTPConflict):
@ -104,6 +107,7 @@ def test_reserve_udp_port_outside_range():
def test_release_udp_port(): def test_release_udp_port():
pm = PortManager() pm = PortManager()
project = Project(project_id=str(uuid.uuid4())) project = Project(project_id=str(uuid.uuid4()))
pm.reserve_udp_port(20000, project) pm.reserve_udp_port(20000, project)
@ -112,11 +116,13 @@ def test_release_udp_port():
def test_find_unused_port(): def test_find_unused_port():
p = PortManager().find_unused_port(1000, 10000) p = PortManager().find_unused_port(1000, 10000)
assert p is not None assert p is not None
def test_find_unused_port_invalid_range(): def test_find_unused_port_invalid_range():
with pytest.raises(aiohttp.web.HTTPConflict): with pytest.raises(aiohttp.web.HTTPConflict):
p = PortManager().find_unused_port(10000, 1000) p = PortManager().find_unused_port(10000, 1000)
@ -126,6 +132,7 @@ def test_set_console_host(config):
If allow remote connection we need to bind console host If allow remote connection we need to bind console host
to 0.0.0.0 to 0.0.0.0
""" """
p = PortManager() p = PortManager()
config.set_section_config("Server", {"allow_remote_console": False}) config.set_section_config("Server", {"allow_remote_console": False})
p.console_host = "10.42.1.42" p.console_host = "10.42.1.42"

@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -18,11 +18,9 @@
import os import os
import uuid import uuid
import json
import asyncio import asyncio
import pytest import pytest
import aiohttp import aiohttp
import zipfile
from uuid import uuid4 from uuid import uuid4
from unittest.mock import patch from unittest.mock import patch
@ -34,24 +32,27 @@ from gns3server.config import Config
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def manager(port_manager): async def manager(loop, port_manager):
m = VPCS.instance() m = VPCS.instance()
m.port_manager = port_manager m.port_manager = port_manager
return m return m
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def node(project, manager, loop): async def node(compute_project, manager):
node = manager.create_node("test", project.id, "00010203-0405-0607-0809-0a0b0c0d0e0f")
return loop.run_until_complete(asyncio.ensure_future(node)) node = manager.create_node("test", compute_project.id, "00010203-0405-0607-0809-0a0b0c0d0e0f")
return await asyncio.ensure_future(node)
async def test_affect_uuid():
def test_affect_uuid():
p = Project(project_id='00010203-0405-0607-0809-0a0b0c0d0e0f') p = Project(project_id='00010203-0405-0607-0809-0a0b0c0d0e0f')
assert p.id == '00010203-0405-0607-0809-0a0b0c0d0e0f' assert p.id == '00010203-0405-0607-0809-0a0b0c0d0e0f'
def test_clean_tmp_directory(async_run): async def test_clean_tmp_directory():
""" """
The tmp directory should be clean at project open and close The tmp directory should be clean at project open and close
""" """
@ -59,7 +60,7 @@ def test_clean_tmp_directory(async_run):
p = Project(project_id='00010203-0405-0607-0809-0a0b0c0d0e0f') p = Project(project_id='00010203-0405-0607-0809-0a0b0c0d0e0f')
path = p.tmp_working_directory() path = p.tmp_working_directory()
os.makedirs(path) os.makedirs(path)
async_run(p.close()) await p.close()
assert not os.path.exists(path) assert not os.path.exists(path)
os.makedirs(path) os.makedirs(path)
@ -67,10 +68,9 @@ def test_clean_tmp_directory(async_run):
assert not os.path.exists(path) assert not os.path.exists(path)
def test_path(tmpdir): async def test_path(projects_dir):
directory = Config.instance().get_section_config("Server").get("projects_path")
directory = projects_dir
with patch("gns3server.compute.project.Project.is_local", return_value=True): with patch("gns3server.compute.project.Project.is_local", return_value=True):
with patch("gns3server.utils.path.get_default_project_directory", return_value=directory): with patch("gns3server.utils.path.get_default_project_directory", return_value=directory):
p = Project(project_id=str(uuid4())) p = Project(project_id=str(uuid4()))
@ -78,27 +78,30 @@ def test_path(tmpdir):
assert os.path.exists(os.path.join(directory, p.id)) assert os.path.exists(os.path.join(directory, p.id))
def test_init_path(tmpdir): async def test_init_path(tmpdir):
with patch("gns3server.compute.project.Project.is_local", return_value=True): with patch("gns3server.compute.project.Project.is_local", return_value=True):
p = Project(path=str(tmpdir), project_id=str(uuid4())) p = Project(path=str(tmpdir), project_id=str(uuid4()))
assert p.path == str(tmpdir) assert p.path == str(tmpdir)
def test_changing_path_not_allowed(tmpdir): async def test_changing_path_not_allowed(tmpdir):
with patch("gns3server.compute.project.Project.is_local", return_value=False): with patch("gns3server.compute.project.Project.is_local", return_value=False):
with pytest.raises(aiohttp.web.HTTPForbidden): with pytest.raises(aiohttp.web.HTTPForbidden):
p = Project(project_id=str(uuid4())) p = Project(project_id=str(uuid4()))
p.path = str(tmpdir) p.path = str(tmpdir)
def test_variables(tmpdir): async def test_variables():
variables = [{"name": "VAR1", "value": "VAL1"}] variables = [{"name": "VAR1", "value": "VAL1"}]
p = Project(project_id=str(uuid4()), variables=variables) p = Project(project_id=str(uuid4()), variables=variables)
assert p.variables == variables assert p.variables == variables
def test_json(tmpdir): async def test_json():
p = Project(project_id=str(uuid4())) p = Project(project_id=str(uuid4()))
assert p.__json__() == { assert p.__json__() == {
"name": p.name, "name": p.name,
@ -107,7 +110,8 @@ def test_json(tmpdir):
} }
def test_json_with_variables(tmpdir): async def test_json_with_variables():
variables = [{"name": "VAR1", "value": "VAL1"}] variables = [{"name": "VAR1", "value": "VAL1"}]
p = Project(project_id=str(uuid4()), variables=variables) p = Project(project_id=str(uuid4()), variables=variables)
assert p.__json__() == { assert p.__json__() == {
@ -117,18 +121,18 @@ def test_json_with_variables(tmpdir):
} }
def test_node_working_directory(tmpdir, node): async def test_node_working_directory(node, projects_dir):
directory = Config.instance().get_section_config("Server").get("projects_path")
directory = projects_dir
with patch("gns3server.compute.project.Project.is_local", return_value=True): with patch("gns3server.compute.project.Project.is_local", return_value=True):
p = Project(project_id=str(uuid4())) p = Project(project_id=str(uuid4()))
assert p.node_working_directory(node) == os.path.join(directory, p.id, 'project-files', node.module_name, node.id) assert p.node_working_directory(node) == os.path.join(directory, p.id, 'project-files', node.module_name, node.id)
assert os.path.exists(p.node_working_directory(node)) assert os.path.exists(p.node_working_directory(node))
def test_node_working_path(tmpdir, node): async def test_node_working_path(node, projects_dir):
directory = Config.instance().get_section_config("Server").get("projects_path")
directory = projects_dir
with patch("gns3server.compute.project.Project.is_local", return_value=True): with patch("gns3server.compute.project.Project.is_local", return_value=True):
p = Project(project_id=str(uuid4())) p = Project(project_id=str(uuid4()))
assert p.node_working_path(node) == os.path.join(directory, p.id, 'project-files', node.module_name, node.id) assert p.node_working_path(node) == os.path.join(directory, p.id, 'project-files', node.module_name, node.id)
@ -136,40 +140,43 @@ def test_node_working_path(tmpdir, node):
assert not os.path.exists(p.node_working_path(node)) assert not os.path.exists(p.node_working_path(node))
def test_project_delete(loop): async def test_project_delete():
project = Project(project_id=str(uuid4())) project = Project(project_id=str(uuid4()))
directory = project.path directory = project.path
assert os.path.exists(directory) assert os.path.exists(directory)
loop.run_until_complete(asyncio.ensure_future(project.delete())) await project.delete()
assert os.path.exists(directory) is False assert os.path.exists(directory) is False
def test_project_delete_permission_issue(loop): async def test_project_delete_permission_issue():
project = Project(project_id=str(uuid4())) project = Project(project_id=str(uuid4()))
directory = project.path directory = project.path
assert os.path.exists(directory) assert os.path.exists(directory)
os.chmod(directory, 0) os.chmod(directory, 0)
with pytest.raises(aiohttp.web.HTTPInternalServerError): with pytest.raises(aiohttp.web.HTTPInternalServerError):
loop.run_until_complete(asyncio.ensure_future(project.delete())) await asyncio.ensure_future(project.delete())
os.chmod(directory, 700) os.chmod(directory, 700)
def test_project_add_node(manager): async def test_project_add_node(manager):
project = Project(project_id=str(uuid4())) project = Project(project_id=str(uuid4()))
node = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager) node = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager)
project.add_node(node) project.add_node(node)
assert len(project.nodes) == 1 assert len(project.nodes) == 1
def test_project_close(loop, node, project): async def test_project_close(node, compute_project):
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.close") as mock: with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.close") as mock:
loop.run_until_complete(asyncio.ensure_future(project.close())) await compute_project.close()
assert mock.called assert mock.called
assert node.id not in node.manager._nodes assert node.id not in node.manager._nodes
def test_list_files(tmpdir, loop): async def test_list_files(tmpdir):
with patch("gns3server.config.Config.get_section_config", return_value={"projects_path": str(tmpdir)}): with patch("gns3server.config.Config.get_section_config", return_value={"projects_path": str(tmpdir)}):
project = Project(project_id=str(uuid4())) project = Project(project_id=str(uuid4()))
@ -181,7 +188,7 @@ def test_list_files(tmpdir, loop):
with open(os.path.join(path, "test.txt"), "w+") as f: with open(os.path.join(path, "test.txt"), "w+") as f:
f.write("test2") f.write("test2")
files = loop.run_until_complete(asyncio.ensure_future(project.list_files())) files = await project.list_files()
assert files == [ assert files == [
{ {
@ -195,20 +202,21 @@ def test_list_files(tmpdir, loop):
] ]
def test_emit(async_run): async def test_emit():
with NotificationManager.instance().queue() as queue: with NotificationManager.instance().queue() as queue:
(action, event, context) = async_run(queue.get(0.5)) #  Ping await queue.get(0.5) #  Ping
project = Project(project_id=str(uuid4())) project = Project(project_id=str(uuid4()))
project.emit("test", {}) project.emit("test", {})
(action, event, context) = async_run(queue.get(0.5)) (action, event, context) = await queue.get(0.5)
assert action == "test" assert action == "test"
assert context["project_id"] == project.id assert context["project_id"] == project.id
def test_update_project(loop): async def test_update_project():
variables = [{"name": "TEST", "value": "VAL"}] variables = [{"name": "TEST", "value": "VAL"}]
project = Project(project_id=str(uuid.uuid4())) project = Project(project_id=str(uuid.uuid4()))
loop.run_until_complete(asyncio.ensure_future(project.update(variables=variables))) await project.update(variables=variables)
assert project.variables == variables assert project.variables == variables

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -17,16 +17,19 @@
import aiohttp import aiohttp
import pytest import pytest
from gns3server.compute.project_manager import ProjectManager from gns3server.compute.project_manager import ProjectManager
def test_create_project(): def test_create_project():
pm = ProjectManager.instance() pm = ProjectManager.instance()
project = pm.create_project(project_id='00010203-0405-0607-0809-0a0b0c0d0e0f') project = pm.create_project(project_id='00010203-0405-0607-0809-0a0b0c0d0e0f')
assert project == pm.get_project('00010203-0405-0607-0809-0a0b0c0d0e0f') assert project == pm.get_project('00010203-0405-0607-0809-0a0b0c0d0e0f')
def test_project_not_found(): def test_project_not_found():
pm = ProjectManager.instance() pm = ProjectManager.instance()
with pytest.raises(aiohttp.web.HTTPNotFound): with pytest.raises(aiohttp.web.HTTPNotFound):
pm.get_project('00010203-0405-0607-0809-000000000000') pm.get_project('00010203-0405-0607-0809-000000000000')

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2018 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -17,10 +17,9 @@
import pytest import pytest
import asyncio import asyncio
import sys
from tests.utils import asyncio_patch, AsyncioMagicMock from tests.utils import asyncio_patch, AsyncioMagicMock
from unittest.mock import patch, MagicMock, ANY, PropertyMock from unittest.mock import patch, MagicMock, ANY
from gns3server.compute.traceng.traceng_vm import TraceNGVM from gns3server.compute.traceng.traceng_vm import TraceNGVM
from gns3server.compute.traceng.traceng_error import TraceNGError from gns3server.compute.traceng.traceng_error import TraceNGError
@ -31,14 +30,16 @@ from gns3server.compute.notification_manager import NotificationManager
@pytest.fixture @pytest.fixture
def manager(port_manager): def manager(port_manager):
m = TraceNG.instance() m = TraceNG.instance()
m.port_manager = port_manager m.port_manager = port_manager
return m return m
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def vm(project, manager, ubridge_path): def vm(loop, compute_project, manager, ubridge_path):
vm = TraceNGVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager)
vm = TraceNGVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", compute_project, manager)
vm._start_ubridge = AsyncioMagicMock() vm._start_ubridge = AsyncioMagicMock()
vm._ubridge_hypervisor = MagicMock() vm._ubridge_hypervisor = MagicMock()
vm._ubridge_hypervisor.is_running.return_value = True vm._ubridge_hypervisor.is_running.return_value = True
@ -46,33 +47,36 @@ def vm(project, manager, ubridge_path):
def test_vm(project, manager): def test_vm(project, manager):
vm = TraceNGVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager) vm = TraceNGVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager)
assert vm.name == "test" assert vm.name == "test"
assert vm.id == "00010203-0405-0607-0809-0a0b0c0d0e0f" assert vm.id == "00010203-0405-0607-0809-0a0b0c0d0e0f"
def test_vm_invalid_traceng_path(vm, manager, loop): async def test_vm_invalid_traceng_path(vm, manager):
with patch("gns3server.compute.traceng.traceng_vm.TraceNGVM._traceng_path", return_value="/tmp/fake/path/traceng"): with patch("gns3server.compute.traceng.traceng_vm.TraceNGVM._traceng_path", return_value="/tmp/fake/path/traceng"):
with pytest.raises(TraceNGError): with pytest.raises(TraceNGError):
nio = manager.create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) nio = manager.create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"})
loop.run_until_complete(asyncio.ensure_future(vm.port_add_nio_binding(0, nio))) await vm.port_add_nio_binding(0, nio)
loop.run_until_complete(asyncio.ensure_future(vm.start())) await vm.start()
assert vm.name == "test" assert vm.name == "test"
assert vm.id == "00010203-0405-0607-0809-0a0b0c0d0e0e" assert vm.id == "00010203-0405-0607-0809-0a0b0c0d0e0e"
def test_start(loop, vm, async_run): async def test_start(vm):
process = MagicMock() process = MagicMock()
process.returncode = None process.returncode = None
with NotificationManager.instance().queue() as queue: with NotificationManager.instance().queue() as queue:
async_run(queue.get(1)) # Ping await queue.get(1) # Ping
vm.ip_address = "192.168.1.1" vm.ip_address = "192.168.1.1"
with patch("sys.platform", return_value="win"): with patch("sys.platform", return_value="win"):
with asyncio_patch("gns3server.compute.traceng.traceng_vm.TraceNGVM._check_requirements", return_value=True): with asyncio_patch("gns3server.compute.traceng.traceng_vm.TraceNGVM._check_requirements", return_value=True):
with asyncio_patch("asyncio.create_subprocess_exec", return_value=process) as mock_exec: with asyncio_patch("asyncio.create_subprocess_exec", return_value=process) as mock_exec:
loop.run_until_complete(asyncio.ensure_future(vm.start("192.168.1.2"))) await vm.start("192.168.1.2")
assert mock_exec.call_args[0] == (vm._traceng_path(), assert mock_exec.call_args[0] == (vm._traceng_path(),
'-u', '-u',
'-c', '-c',
@ -88,14 +92,14 @@ def test_start(loop, vm, async_run):
'192.168.1.2') '192.168.1.2')
assert vm.is_running() assert vm.is_running()
assert vm.command_line == ' '.join(mock_exec.call_args[0]) assert vm.command_line == ' '.join(mock_exec.call_args[0])
(action, event, kwargs) = async_run(queue.get(1)) (action, event, kwargs) = await queue.get(1)
assert action == "node.updated" assert action == "node.updated"
assert event == vm assert event == vm
def test_stop(loop, vm, async_run): async def test_stop(vm):
process = MagicMock()
process = MagicMock()
# Wait process kill success # Wait process kill success
future = asyncio.Future() future = asyncio.Future()
future.set_result(True) future.set_result(True)
@ -108,29 +112,29 @@ def test_stop(loop, vm, async_run):
with asyncio_patch("gns3server.compute.traceng.traceng_vm.TraceNGVM._check_requirements", return_value=True): with asyncio_patch("gns3server.compute.traceng.traceng_vm.TraceNGVM._check_requirements", return_value=True):
with asyncio_patch("asyncio.create_subprocess_exec", return_value=process): with asyncio_patch("asyncio.create_subprocess_exec", return_value=process):
nio = TraceNG.instance().create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1", "filters": {}}) nio = TraceNG.instance().create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1", "filters": {}})
async_run(vm.port_add_nio_binding(0, nio)) await vm.port_add_nio_binding(0, nio)
vm._ubridge_send = AsyncioMagicMock() vm._ubridge_send = AsyncioMagicMock()
async_run(vm.start("192.168.1.2")) await vm.start("192.168.1.2")
assert vm.is_running() assert vm.is_running()
with asyncio_patch("gns3server.utils.asyncio.wait_for_process_termination"): with asyncio_patch("gns3server.utils.asyncio.wait_for_process_termination"):
loop.run_until_complete(asyncio.ensure_future(vm.stop())) await vm.stop()
assert vm.is_running() is False assert vm.is_running() is False
process.terminate.assert_called_with() process.terminate.assert_called_with()
async_run(queue.get(1)) #  Ping await queue.get(1) #  Ping
async_run(queue.get(1)) #  Started await queue.get(1) #  Started
(action, event, kwargs) = async_run(queue.get(1)) (action, event, kwargs) = await queue.get(1)
assert action == "node.updated" assert action == "node.updated"
assert event == vm assert event == vm
def test_reload(loop, vm, async_run): async def test_reload(vm):
process = MagicMock()
process = MagicMock()
# Wait process kill success # Wait process kill success
future = asyncio.Future() future = asyncio.Future()
future.set_result(True) future.set_result(True)
@ -142,14 +146,14 @@ def test_reload(loop, vm, async_run):
with asyncio_patch("gns3server.compute.traceng.traceng_vm.TraceNGVM._check_requirements", return_value=True): with asyncio_patch("gns3server.compute.traceng.traceng_vm.TraceNGVM._check_requirements", return_value=True):
with asyncio_patch("asyncio.create_subprocess_exec", return_value=process): with asyncio_patch("asyncio.create_subprocess_exec", return_value=process):
nio = TraceNG.instance().create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1", "filters": {}}) nio = TraceNG.instance().create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1", "filters": {}})
async_run(vm.port_add_nio_binding(0, nio)) await vm.port_add_nio_binding(0, nio)
vm._ubridge_send = AsyncioMagicMock() vm._ubridge_send = AsyncioMagicMock()
async_run(vm.start("192.168.1.2")) await vm.start("192.168.1.2")
assert vm.is_running() assert vm.is_running()
with asyncio_patch("gns3server.utils.asyncio.wait_for_process_termination"): with asyncio_patch("gns3server.utils.asyncio.wait_for_process_termination"):
async_run(vm.reload()) await vm.reload()
assert vm.is_running() is True assert vm.is_running() is True
#if sys.platform.startswith("win"): #if sys.platform.startswith("win"):
@ -158,24 +162,27 @@ def test_reload(loop, vm, async_run):
process.terminate.assert_called_with() process.terminate.assert_called_with()
def test_add_nio_binding_udp(vm, async_run): async def test_add_nio_binding_udp(vm):
nio = TraceNG.instance().create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1", "filters": {}}) nio = TraceNG.instance().create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1", "filters": {}})
async_run(vm.port_add_nio_binding(0, nio)) await vm.port_add_nio_binding(0, nio)
assert nio.lport == 4242 assert nio.lport == 4242
def test_port_remove_nio_binding(loop, vm): async def test_port_remove_nio_binding(vm):
nio = TraceNG.instance().create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) nio = TraceNG.instance().create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"})
loop.run_until_complete(asyncio.ensure_future(vm.port_add_nio_binding(0, nio))) await asyncio.ensure_future(vm.port_add_nio_binding(0, nio))
loop.run_until_complete(asyncio.ensure_future(vm.port_remove_nio_binding(0))) await vm.port_remove_nio_binding(0)
assert vm._ethernet_adapter.ports[0] is None assert vm._ethernet_adapter.ports[0] is None
def test_close(vm, port_manager, loop): async def test_close(vm):
vm.ip_address = "192.168.1.1" vm.ip_address = "192.168.1.1"
with patch("sys.platform", return_value="win"): with patch("sys.platform", return_value="win"):
with asyncio_patch("gns3server.compute.traceng.traceng_vm.TraceNGVM._check_requirements", return_value=True): with asyncio_patch("gns3server.compute.traceng.traceng_vm.TraceNGVM._check_requirements", return_value=True):
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()): with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()):
loop.run_until_complete(asyncio.ensure_future(vm.start("192.168.1.2"))) await vm.start("192.168.1.2")
loop.run_until_complete(asyncio.ensure_future(vm.close())) await vm.close()
assert vm.is_running() is False assert vm.is_running() is False

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -20,8 +20,6 @@ import pytest
import tempfile import tempfile
import os import os
import stat import stat
import asyncio
from unittest.mock import patch from unittest.mock import patch
@ -31,19 +29,22 @@ from tests.utils import asyncio_patch
@pytest.fixture @pytest.fixture
def manager(port_manager): async def manager(loop, port_manager):
m = VirtualBox.instance() m = VirtualBox.instance()
m.port_manager = port_manager m.port_manager = port_manager
return m return m
def test_vm_invalid_vboxmanage_path(manager): def test_vm_invalid_vboxmanage_path(manager):
with patch("gns3server.config.Config.get_section_config", return_value={"vboxmanage_path": "/bin/test_fake"}): with patch("gns3server.config.Config.get_section_config", return_value={"vboxmanage_path": "/bin/test_fake"}):
with pytest.raises(VirtualBoxError): with pytest.raises(VirtualBoxError):
manager.find_vboxmanage() manager.find_vboxmanage()
def test_vm_non_executable_vboxmanage_path(manager): def test_vm_non_executable_vboxmanage_path(manager):
tmpfile = tempfile.NamedTemporaryFile() tmpfile = tempfile.NamedTemporaryFile()
with patch("gns3server.config.Config.get_section_config", return_value={"vboxmanage_path": tmpfile.name}): with patch("gns3server.config.Config.get_section_config", return_value={"vboxmanage_path": tmpfile.name}):
with pytest.raises(VirtualBoxError): with pytest.raises(VirtualBoxError):
@ -51,27 +52,28 @@ def test_vm_non_executable_vboxmanage_path(manager):
def test_vm_invalid_executable_name_vboxmanage_path(manager, tmpdir): def test_vm_invalid_executable_name_vboxmanage_path(manager, tmpdir):
path = str(tmpdir / "vpcs") path = str(tmpdir / "vpcs")
with open(path, "w+") as f: with open(path, "w+") as f:
f.write(path) f.write(path)
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
tmpfile = tempfile.NamedTemporaryFile()
with patch("gns3server.config.Config.get_section_config", return_value={"vboxmanage_path": path}): with patch("gns3server.config.Config.get_section_config", return_value={"vboxmanage_path": path}):
with pytest.raises(VirtualBoxError): with pytest.raises(VirtualBoxError):
manager.find_vboxmanage() manager.find_vboxmanage()
def test_vboxmanage_path(manager, tmpdir): def test_vboxmanage_path(manager, tmpdir):
path = str(tmpdir / "VBoxManage") path = str(tmpdir / "VBoxManage")
with open(path, "w+") as f: with open(path, "w+") as f:
f.write(path) f.write(path)
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
tmpfile = tempfile.NamedTemporaryFile()
with patch("gns3server.config.Config.get_section_config", return_value={"vboxmanage_path": path}): with patch("gns3server.config.Config.get_section_config", return_value={"vboxmanage_path": path}):
assert manager.find_vboxmanage() == path assert manager.find_vboxmanage() == path
def test_list_vms(manager, loop): async def test_list_vms(manager):
vm_list = ['"Windows 8.1" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}', vm_list = ['"Windows 8.1" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}',
'"Carriage', '"Carriage',
'Return" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c3f3}', 'Return" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c3f3}',
@ -83,7 +85,6 @@ def test_list_vms(manager, loop):
if cmd == "list": if cmd == "list":
return vm_list return vm_list
else: else:
print(args)
if args[0] == "27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1": if args[0] == "27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1":
return ["memory=512"] return ["memory=512"]
elif args[0] == "ccd8c50b-c172-457d-99fa-dd69371ede0e": elif args[0] == "ccd8c50b-c172-457d-99fa-dd69371ede0e":
@ -92,7 +93,7 @@ def test_list_vms(manager, loop):
with asyncio_patch("gns3server.compute.virtualbox.VirtualBox.execute") as mock: with asyncio_patch("gns3server.compute.virtualbox.VirtualBox.execute") as mock:
mock.side_effect = execute_mock mock.side_effect = execute_mock
vms = loop.run_until_complete(asyncio.ensure_future(manager.list_vms())) vms = await manager.list_vms()
assert vms == [ assert vms == [
{"vmname": "Windows 8.1", "ram": 512}, {"vmname": "Windows 8.1", "ram": 512},
{"vmname": "Linux Microcore 4.7.1", "ram": 256} {"vmname": "Linux Microcore 4.7.1", "ram": 256}

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -26,30 +26,34 @@ from gns3server.compute.virtualbox import VirtualBox
@pytest.fixture @pytest.fixture
def manager(port_manager): def manager(loop, port_manager):
m = VirtualBox.instance() m = VirtualBox.instance()
m.port_manager = port_manager m.port_manager = port_manager
return m return m
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def vm(project, manager): def vm(compute_project, manager):
return VirtualBoxVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, "test", False)
return VirtualBoxVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", compute_project, manager, "test", False)
def test_vm(compute_project, manager):
def test_vm(project, manager): vm = VirtualBoxVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", compute_project, manager, "test", False)
vm = VirtualBoxVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, "test", False)
assert vm.name == "test" assert vm.name == "test"
assert vm.id == "00010203-0405-0607-0809-0a0b0c0d0e0f" assert vm.id == "00010203-0405-0607-0809-0a0b0c0d0e0f"
assert vm.vmname == "test" assert vm.vmname == "test"
def test_rename_vmname(project, manager, async_run): async def test_rename_vmname(compute_project, manager):
""" """
Rename a VM is not allowed when using a running linked clone Rename a VM is not allowed when using a running linked clone
or if the vm already exists in Vbox or if the vm already exists in Vbox
""" """
vm = VirtualBoxVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, "test", False)
vm = VirtualBoxVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", compute_project, manager, "test", False)
vm.manager.list_vms = AsyncioMagicMock(return_value=[{"vmname": "Debian"}]) vm.manager.list_vms = AsyncioMagicMock(return_value=[{"vmname": "Debian"}])
vm._linked_clone = True vm._linked_clone = True
vm._modify_vm = AsyncioMagicMock() vm._modify_vm = AsyncioMagicMock()
@ -57,42 +61,46 @@ def test_rename_vmname(project, manager, async_run):
# Vm is running # Vm is running
vm._node_status = "started" vm._node_status = "started"
with pytest.raises(VirtualBoxError): with pytest.raises(VirtualBoxError):
async_run(vm.set_vmname("Arch")) await vm.set_vmname("Arch")
assert not vm._modify_vm.called assert not vm._modify_vm.called
vm._node_status = "stopped" vm._node_status = "stopped"
# Name already use # Name already use
with pytest.raises(VirtualBoxError): with pytest.raises(VirtualBoxError):
async_run(vm.set_vmname("Debian")) await vm.set_vmname("Debian")
assert not vm._modify_vm.called assert not vm._modify_vm.called
# Work # Work
async_run(vm.set_vmname("Arch")) await vm.set_vmname("Arch")
assert vm._modify_vm.called assert vm._modify_vm.called
def test_vm_valid_virtualbox_api_version(loop, project, manager): async def test_vm_valid_virtualbox_api_version(compute_project, manager):
with asyncio_patch("gns3server.compute.virtualbox.VirtualBox.execute", return_value=["API version: 4_3"]): with asyncio_patch("gns3server.compute.virtualbox.VirtualBox.execute", return_value=["API version: 4_3"]):
vm = VirtualBoxVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, "test", False) vm = VirtualBoxVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", compute_project, manager, "test", False)
vm._uuid = "00010203-0405-0607-0809-0a0b0c0d0e0f" vm._uuid = "00010203-0405-0607-0809-0a0b0c0d0e0f"
loop.run_until_complete(asyncio.ensure_future(vm.create())) await vm.create()
async def test_vm_invalid_virtualbox_api_version(compute_project, manager):
def test_vm_invalid_virtualbox_api_version(loop, project, manager):
with asyncio_patch("gns3server.compute.virtualbox.VirtualBox.execute", return_value=["API version: 4_2"]): with asyncio_patch("gns3server.compute.virtualbox.VirtualBox.execute", return_value=["API version: 4_2"]):
with pytest.raises(VirtualBoxError): with pytest.raises(VirtualBoxError):
vm = VirtualBoxVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, "test", False) vm = VirtualBoxVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", compute_project, manager, "test", False)
loop.run_until_complete(asyncio.ensure_future(vm.create())) await vm.create()
def test_vm_adapter_add_nio_binding_adapter_not_exist(loop, vm, manager, free_console_port): async def test_vm_adapter_add_nio_binding_adapter_not_exist(vm, manager, free_console_port):
nio = manager.create_nio({"type": "nio_udp", "lport": free_console_port, "rport": free_console_port, "rhost": "127.0.0.1"}) nio = manager.create_nio({"type": "nio_udp", "lport": free_console_port, "rport": free_console_port, "rhost": "127.0.0.1"})
with pytest.raises(VirtualBoxError): with pytest.raises(VirtualBoxError):
loop.run_until_complete(asyncio.ensure_future(vm.adapter_add_nio_binding(15, nio))) await vm.adapter_add_nio_binding(15, nio)
def test_json(vm, tmpdir, project): def test_json(vm, tmpdir, project):
assert vm.__json__()["node_directory"] is None assert vm.__json__()["node_directory"] is None
project._path = str(tmpdir) project._path = str(tmpdir)
vm._linked_clone = True vm._linked_clone = True
@ -100,12 +108,14 @@ def test_json(vm, tmpdir, project):
def test_patch_vm_uuid(vm): def test_patch_vm_uuid(vm):
xml = """<?xml version="1.0"?> xml = """<?xml version="1.0"?>
<VirtualBox xmlns="http://www.virtualbox.org/" version="1.16-macosx"> <VirtualBox xmlns="http://www.virtualbox.org/" version="1.16-macosx">
<Machine uuid="{f8138a63-e361-49ee-a5a4-ba0559bc00e2}" name="Debian-1" OSType="Debian_64" currentSnapshot="{8bd00b14-4c14-4992-a165-cb09e80fe8e4 }" snapshotFolder="Snapshots" lastStateChange="2016-10-28T12:54:26Z"> <Machine uuid="{f8138a63-e361-49ee-a5a4-ba0559bc00e2}" name="Debian-1" OSType="Debian_64" currentSnapshot="{8bd00b14-4c14-4992-a165-cb09e80fe8e4 }" snapshotFolder="Snapshots" lastStateChange="2016-10-28T12:54:26Z">
</Machine> </Machine>
</VirtualBox> </VirtualBox>
""" """
os.makedirs(os.path.join(vm.working_dir, vm._vmname), exist_ok=True) os.makedirs(os.path.join(vm.working_dir, vm._vmname), exist_ok=True)
with open(vm._linked_vbox_file(), "w+") as f: with open(vm._linked_vbox_file(), "w+") as f:
f.write(xml) f.write(xml)
@ -117,6 +127,7 @@ def test_patch_vm_uuid(vm):
def test_patch_vm_uuid_with_corrupted_file(vm): def test_patch_vm_uuid_with_corrupted_file(vm):
xml = """<?xml version="1.0"?> xml = """<?xml version="1.0"?>
<VirtualBox> <VirtualBox>
""" """

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -17,26 +17,20 @@
import pytest import pytest
import tempfile
import os
import stat
import asyncio
from unittest.mock import patch
from gns3server.compute.vmware import VMware from gns3server.compute.vmware import VMware
from tests.utils import asyncio_patch
@pytest.fixture @pytest.fixture
def manager(port_manager): async def manager(loop, port_manager):
m = VMware.instance() m = VMware.instance()
m.port_manager = port_manager m.port_manager = port_manager
return m return m
def test_parse_vmware_file(manager, tmpdir): def test_parse_vmware_file(tmpdir):
path = str(tmpdir / "test.vmx") path = str(tmpdir / "test.vmx")
with open(path, "w+") as f: with open(path, "w+") as f:
f.write('displayname = "GNS3 VM"\nguestOS = "ubuntu-64"') f.write('displayname = "GNS3 VM"\nguestOS = "ubuntu-64"')

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -17,57 +17,58 @@
import pytest import pytest
import asyncio import asyncio
from tests.utils import asyncio_patch
from gns3server.compute.vmware.vmware_vm import VMwareVM from gns3server.compute.vmware.vmware_vm import VMwareVM
from gns3server.compute.vmware.vmware_error import VMwareError
from gns3server.compute.vmware import VMware from gns3server.compute.vmware import VMware
@pytest.fixture @pytest.fixture
def manager(port_manager): def manager(port_manager):
m = VMware.instance() m = VMware.instance()
m.port_manager = port_manager m.port_manager = port_manager
return m return m
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def vm(project, manager, tmpdir): async def vm(loop, compute_project, manager, tmpdir):
fake_vmx = str(tmpdir / "test.vmx") fake_vmx = str(tmpdir / "test.vmx")
open(fake_vmx, "w+").close() open(fake_vmx, "w+").close()
return VMwareVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", compute_project, manager, fake_vmx, False)
return VMwareVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, fake_vmx, False)
def test_vm(vm):
def test_vm(project, manager, vm):
assert vm.name == "test" assert vm.name == "test"
assert vm.id == "00010203-0405-0607-0809-0a0b0c0d0e0f" assert vm.id == "00010203-0405-0607-0809-0a0b0c0d0e0f"
def test_json(vm, tmpdir, project): def test_json(vm, tmpdir, compute_project):
assert vm.__json__()["node_directory"] is not None assert vm.__json__()["node_directory"] is not None
project._path = str(tmpdir) compute_project._path = str(tmpdir)
vm._linked_clone = True vm._linked_clone = True
assert vm.__json__()["node_directory"] is not None assert vm.__json__()["node_directory"] is not None
def test_start_capture(vm, tmpdir, manager, free_console_port, loop): async def test_start_capture(vm, tmpdir, manager, free_console_port):
output_file = str(tmpdir / "test.pcap") output_file = str(tmpdir / "test.pcap")
nio = manager.create_nio({"type": "nio_udp", "lport": free_console_port, "rport": free_console_port, "rhost": "127.0.0.1"}) nio = manager.create_nio({"type": "nio_udp", "lport": free_console_port, "rport": free_console_port, "rhost": "127.0.0.1"})
vm.adapters = 1 vm.adapters = 1
loop.run_until_complete(asyncio.ensure_future(vm.adapter_add_nio_binding(0, nio))) await vm.adapter_add_nio_binding(0, nio)
loop.run_until_complete(asyncio.ensure_future(vm.start_capture(0, output_file))) await vm.start_capture(0, output_file)
assert vm._ethernet_adapters[0].get_nio(0).capturing assert vm._ethernet_adapters[0].get_nio(0).capturing
def test_stop_capture(vm, tmpdir, manager, free_console_port, loop): async def test_stop_capture(vm, tmpdir, manager, free_console_port):
output_file = str(tmpdir / "test.pcap") output_file = str(tmpdir / "test.pcap")
nio = manager.create_nio({"type": "nio_udp", "lport": free_console_port, "rport": free_console_port, "rhost": "127.0.0.1"}) nio = manager.create_nio({"type": "nio_udp", "lport": free_console_port, "rport": free_console_port, "rhost": "127.0.0.1"})
vm.adapters = 1 vm.adapters = 1
loop.run_until_complete(asyncio.ensure_future(vm.adapter_add_nio_binding(0, nio))) await vm.adapter_add_nio_binding(0, nio)
loop.run_until_complete(vm.start_capture(0, output_file)) await vm.start_capture(0, output_file)
assert vm._ethernet_adapters[0].get_nio(0).capturing assert vm._ethernet_adapters[0].get_nio(0).capturing
loop.run_until_complete(asyncio.ensure_future(vm.stop_capture(0))) await asyncio.ensure_future(vm.stop_capture(0))
assert vm._ethernet_adapters[0].get_nio(0).capturing is False assert vm._ethernet_adapters[0].get_nio(0).capturing is False

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -25,7 +25,8 @@ from gns3server.compute.vpcs.vpcs_error import VPCSError
from gns3server.compute.project_manager import ProjectManager from gns3server.compute.project_manager import ProjectManager
def test_get_mac_id(loop, project, port_manager): async def test_get_mac_id(compute_project, port_manager):
# Cleanup the VPCS object # Cleanup the VPCS object
VPCS._instance = None VPCS._instance = None
vpcs = VPCS.instance() vpcs = VPCS.instance()
@ -33,17 +34,18 @@ def test_get_mac_id(loop, project, port_manager):
vm1_id = str(uuid.uuid4()) vm1_id = str(uuid.uuid4())
vm2_id = str(uuid.uuid4()) vm2_id = str(uuid.uuid4())
vm3_id = str(uuid.uuid4()) vm3_id = str(uuid.uuid4())
loop.run_until_complete(vpcs.create_node("PC 1", project.id, vm1_id)) await vpcs.create_node("PC 1", compute_project.id, vm1_id)
loop.run_until_complete(vpcs.create_node("PC 2", project.id, vm2_id)) await vpcs.create_node("PC 2", compute_project.id, vm2_id)
assert vpcs.get_mac_id(vm1_id) == 0 assert vpcs.get_mac_id(vm1_id) == 0
assert vpcs.get_mac_id(vm1_id) == 0 assert vpcs.get_mac_id(vm1_id) == 0
assert vpcs.get_mac_id(vm2_id) == 1 assert vpcs.get_mac_id(vm2_id) == 1
loop.run_until_complete(vpcs.delete_node(vm1_id)) await vpcs.delete_node(vm1_id)
loop.run_until_complete(vpcs.create_node("PC 3", project.id, vm3_id)) await vpcs.create_node("PC 3", compute_project.id, vm3_id)
assert vpcs.get_mac_id(vm3_id) == 0 assert vpcs.get_mac_id(vm3_id) == 0
def test_get_mac_id_multiple_project(loop, port_manager): async def test_get_mac_id_multiple_project(port_manager):
# Cleanup the VPCS object # Cleanup the VPCS object
VPCS._instance = None VPCS._instance = None
vpcs = VPCS.instance() vpcs = VPCS.instance()
@ -53,15 +55,16 @@ def test_get_mac_id_multiple_project(loop, port_manager):
vm3_id = str(uuid.uuid4()) vm3_id = str(uuid.uuid4())
project1 = ProjectManager.instance().create_project(project_id=str(uuid.uuid4())) project1 = ProjectManager.instance().create_project(project_id=str(uuid.uuid4()))
project2 = ProjectManager.instance().create_project(project_id=str(uuid.uuid4())) project2 = ProjectManager.instance().create_project(project_id=str(uuid.uuid4()))
loop.run_until_complete(vpcs.create_node("PC 1", project1.id, vm1_id)) await vpcs.create_node("PC 1", project1.id, vm1_id)
loop.run_until_complete(vpcs.create_node("PC 2", project1.id, vm2_id)) await vpcs.create_node("PC 2", project1.id, vm2_id)
loop.run_until_complete(vpcs.create_node("PC 2", project2.id, vm3_id)) await vpcs.create_node("PC 2", project2.id, vm3_id)
assert vpcs.get_mac_id(vm1_id) == 0 assert vpcs.get_mac_id(vm1_id) == 0
assert vpcs.get_mac_id(vm2_id) == 1 assert vpcs.get_mac_id(vm2_id) == 1
assert vpcs.get_mac_id(vm3_id) == 0 assert vpcs.get_mac_id(vm3_id) == 0
def test_get_mac_id_no_id_available(loop, project, port_manager): async def test_get_mac_id_no_id_available(compute_project, port_manager):
# Cleanup the VPCS object # Cleanup the VPCS object
VPCS._instance = None VPCS._instance = None
vpcs = VPCS.instance() vpcs = VPCS.instance()
@ -69,5 +72,5 @@ def test_get_mac_id_no_id_available(loop, project, port_manager):
with pytest.raises(VPCSError): with pytest.raises(VPCSError):
for i in range(0, 256): for i in range(0, 256):
node_id = str(uuid.uuid4()) node_id = str(uuid.uuid4())
loop.run_until_complete(vpcs.create_node("PC {}".format(i), project.id, node_id)) await vpcs.create_node("PC {}".format(i), compute_project.id, node_id)
assert vpcs.get_mac_id(node_id) == i assert vpcs.get_mac_id(node_id) == i

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -16,7 +16,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import pytest import pytest
import aiohttp
import asyncio import asyncio
import os import os
import sys import sys
@ -32,15 +31,17 @@ from gns3server.compute.notification_manager import NotificationManager
@pytest.fixture @pytest.fixture
def manager(port_manager): async def manager(loop, port_manager):
m = VPCS.instance() m = VPCS.instance()
m.port_manager = port_manager m.port_manager = port_manager
return m return m
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def vm(project, manager, ubridge_path): async def vm(loop, compute_project, manager, ubridge_path):
vm = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager)
vm = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", compute_project, manager)
vm._vpcs_version = parse_version("0.9") vm._vpcs_version = parse_version("0.9")
vm._start_ubridge = AsyncioMagicMock() vm._start_ubridge = AsyncioMagicMock()
vm._ubridge_hypervisor = MagicMock() vm._ubridge_hypervisor = MagicMock()
@ -48,55 +49,61 @@ def vm(project, manager, ubridge_path):
return vm return vm
def test_vm(project, manager): async def test_vm(compute_project, manager):
vm = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager)
vm = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", compute_project, manager)
assert vm.name == "test" assert vm.name == "test"
assert vm.id == "00010203-0405-0607-0809-0a0b0c0d0e0f" assert vm.id == "00010203-0405-0607-0809-0a0b0c0d0e0f"
def test_vm_check_vpcs_version(loop, vm, manager): async def test_vm_check_vpcs_version(vm):
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.subprocess_check_output", return_value="Welcome to Virtual PC Simulator, version 0.9"): with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.subprocess_check_output", return_value="Welcome to Virtual PC Simulator, version 0.9"):
loop.run_until_complete(asyncio.ensure_future(vm._check_vpcs_version())) await asyncio.ensure_future(vm._check_vpcs_version())
assert vm._vpcs_version == parse_version("0.9") assert vm._vpcs_version == parse_version("0.9")
def test_vm_check_vpcs_version_0_6_1(loop, vm, manager): async def test_vm_check_vpcs_version_0_6_1(vm):
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.subprocess_check_output", return_value="Welcome to Virtual PC Simulator, version 0.6.1"): with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.subprocess_check_output", return_value="Welcome to Virtual PC Simulator, version 0.6.1"):
loop.run_until_complete(asyncio.ensure_future(vm._check_vpcs_version())) await vm._check_vpcs_version()
assert vm._vpcs_version == parse_version("0.6.1") assert vm._vpcs_version == parse_version("0.6.1")
def test_vm_invalid_vpcs_version(loop, manager, vm): async def test_vm_invalid_vpcs_version(vm, manager):
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.subprocess_check_output", return_value="Welcome to Virtual PC Simulator, version 0.1"): with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.subprocess_check_output", return_value="Welcome to Virtual PC Simulator, version 0.1"):
with pytest.raises(VPCSError): with pytest.raises(VPCSError):
nio = manager.create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1", "filters": {}}) nio = manager.create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1", "filters": {}})
loop.run_until_complete(asyncio.ensure_future(vm.port_add_nio_binding(0, nio))) await asyncio.ensure_future(vm.port_add_nio_binding(0, nio))
loop.run_until_complete(asyncio.ensure_future(vm._check_vpcs_version())) await asyncio.ensure_future(vm._check_vpcs_version())
assert vm.name == "test" assert vm.name == "test"
assert vm.id == "00010203-0405-0607-0809-0a0b0c0d0e0f" assert vm.id == "00010203-0405-0607-0809-0a0b0c0d0e0f"
def test_vm_invalid_vpcs_path(vm, manager, loop): async def test_vm_invalid_vpcs_path(vm, manager):
with patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM._vpcs_path", return_value="/tmp/fake/path/vpcs"): with patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM._vpcs_path", return_value="/tmp/fake/path/vpcs"):
with pytest.raises(VPCSError): with pytest.raises(VPCSError):
nio = manager.create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) nio = manager.create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"})
loop.run_until_complete(asyncio.ensure_future(vm.port_add_nio_binding(0, nio))) await asyncio.ensure_future(vm.port_add_nio_binding(0, nio))
loop.run_until_complete(asyncio.ensure_future(vm.start())) await asyncio.ensure_future(vm.start())
assert vm.name == "test" assert vm.name == "test"
assert vm.id == "00010203-0405-0607-0809-0a0b0c0d0e0e" assert vm.id == "00010203-0405-0607-0809-0a0b0c0d0e0e"
def test_start(loop, vm, async_run): async def test_start(vm):
process = MagicMock() process = MagicMock()
process.returncode = None process.returncode = None
with NotificationManager.instance().queue() as queue: with NotificationManager.instance().queue() as queue:
async_run(queue.get(1)) # Ping await queue.get(1) # Ping
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM._check_requirements", return_value=True): with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM._check_requirements", return_value=True):
with asyncio_patch("asyncio.create_subprocess_exec", return_value=process) as mock_exec: with asyncio_patch("asyncio.create_subprocess_exec", return_value=process) as mock_exec:
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.start_wrap_console"): with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.start_wrap_console"):
loop.run_until_complete(asyncio.ensure_future(vm.start())) await vm.start()
assert mock_exec.call_args[0] == (vm._vpcs_path(), assert mock_exec.call_args[0] == (vm._vpcs_path(),
'-p', '-p',
str(vm._internal_console_port), str(vm._internal_console_port),
@ -113,16 +120,17 @@ def test_start(loop, vm, async_run):
'127.0.0.1') '127.0.0.1')
assert vm.is_running() assert vm.is_running()
assert vm.command_line == ' '.join(mock_exec.call_args[0]) assert vm.command_line == ' '.join(mock_exec.call_args[0])
(action, event, kwargs) = async_run(queue.get(1)) (action, event, kwargs) = await queue.get(1)
assert action == "node.updated" assert action == "node.updated"
assert event == vm assert event == vm
def test_start_0_6_1(loop, vm, async_run): async def test_start_0_6_1(vm):
""" """
Version 0.6.1 doesn't have the -R options. It's not require Version 0.6.1 doesn't have the -R options. It's not require
because GNS3 provide a patch for this. because GNS3 provide a patch for this.
""" """
process = MagicMock() process = MagicMock()
process.returncode = None process.returncode = None
vm._vpcs_version = parse_version("0.6.1") vm._vpcs_version = parse_version("0.6.1")
@ -131,8 +139,8 @@ def test_start_0_6_1(loop, vm, async_run):
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.start_wrap_console"): with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.start_wrap_console"):
with asyncio_patch("asyncio.create_subprocess_exec", return_value=process) as mock_exec: with asyncio_patch("asyncio.create_subprocess_exec", return_value=process) as mock_exec:
nio = VPCS.instance().create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1", "filters": {}}) nio = VPCS.instance().create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1", "filters": {}})
async_run(vm.port_add_nio_binding(0, nio)) await vm.port_add_nio_binding(0, nio)
async_run(vm.start()) await vm.start()
assert mock_exec.call_args[0] == (vm._vpcs_path(), assert mock_exec.call_args[0] == (vm._vpcs_path(),
'-p', '-p',
str(vm._internal_console_port), str(vm._internal_console_port),
@ -149,9 +157,9 @@ def test_start_0_6_1(loop, vm, async_run):
assert vm.is_running() assert vm.is_running()
def test_stop(loop, vm, async_run): async def test_stop(vm):
process = MagicMock()
process = MagicMock()
# Wait process kill success # Wait process kill success
future = asyncio.Future() future = asyncio.Future()
future.set_result(True) future.set_result(True)
@ -163,13 +171,13 @@ def test_stop(loop, vm, async_run):
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.start_wrap_console"): with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.start_wrap_console"):
with asyncio_patch("asyncio.create_subprocess_exec", return_value=process): with asyncio_patch("asyncio.create_subprocess_exec", return_value=process):
nio = VPCS.instance().create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1", "filters": {}}) nio = VPCS.instance().create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1", "filters": {}})
async_run(vm.port_add_nio_binding(0, nio)) await vm.port_add_nio_binding(0, nio)
async_run(vm.start()) await vm.start()
assert vm.is_running() assert vm.is_running()
with asyncio_patch("gns3server.utils.asyncio.wait_for_process_termination"): with asyncio_patch("gns3server.utils.asyncio.wait_for_process_termination"):
loop.run_until_complete(asyncio.ensure_future(vm.stop())) await asyncio.ensure_future(vm.stop())
assert vm.is_running() is False assert vm.is_running() is False
if sys.platform.startswith("win"): if sys.platform.startswith("win"):
@ -177,17 +185,17 @@ def test_stop(loop, vm, async_run):
else: else:
process.terminate.assert_called_with() process.terminate.assert_called_with()
async_run(queue.get(1)) #  Ping await queue.get(1) #  Ping
async_run(queue.get(1)) #  Started await queue.get(1) #  Started
(action, event, kwargs) = async_run(queue.get(1)) (action, event, kwargs) = await queue.get(1)
assert action == "node.updated" assert action == "node.updated"
assert event == vm assert event == vm
def test_reload(loop, vm, async_run): async def test_reload(vm):
process = MagicMock()
process = MagicMock()
# Wait process kill success # Wait process kill success
future = asyncio.Future() future = asyncio.Future()
future.set_result(True) future.set_result(True)
@ -198,13 +206,13 @@ def test_reload(loop, vm, async_run):
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.start_wrap_console"): with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.start_wrap_console"):
with asyncio_patch("asyncio.create_subprocess_exec", return_value=process): with asyncio_patch("asyncio.create_subprocess_exec", return_value=process):
nio = VPCS.instance().create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1", "filters": {}}) nio = VPCS.instance().create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1", "filters": {}})
async_run(vm.port_add_nio_binding(0, nio)) await vm.port_add_nio_binding(0, nio)
async_run(vm.start()) await vm.start()
assert vm.is_running() assert vm.is_running()
vm._ubridge_send = AsyncioMagicMock() vm._ubridge_send = AsyncioMagicMock()
with asyncio_patch("gns3server.utils.asyncio.wait_for_process_termination"): with asyncio_patch("gns3server.utils.asyncio.wait_for_process_termination"):
async_run(vm.reload()) await vm.reload()
assert vm.is_running() is True assert vm.is_running() is True
if sys.platform.startswith("win"): if sys.platform.startswith("win"):
@ -213,28 +221,32 @@ def test_reload(loop, vm, async_run):
process.terminate.assert_called_with() process.terminate.assert_called_with()
def test_add_nio_binding_udp(vm, async_run): async def test_add_nio_binding_udp(vm):
nio = VPCS.instance().create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1", "filters": {}}) nio = VPCS.instance().create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1", "filters": {}})
async_run(vm.port_add_nio_binding(0, nio)) await vm.port_add_nio_binding(0, nio)
assert nio.lport == 4242 assert nio.lport == 4242
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") @pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
def test_add_nio_binding_tap(vm, ethernet_device, loop): async def test_add_nio_binding_tap(vm, ethernet_device):
with patch("gns3server.compute.base_manager.BaseManager.has_privileged_access", return_value=True): with patch("gns3server.compute.base_manager.BaseManager.has_privileged_access", return_value=True):
nio = VPCS.instance().create_nio({"type": "nio_tap", "tap_device": ethernet_device}) nio = VPCS.instance().create_nio({"type": "nio_tap", "tap_device": ethernet_device})
loop.run_until_complete(asyncio.ensure_future(vm.port_add_nio_binding(0, nio))) await asyncio.ensure_future(vm.port_add_nio_binding(0, nio))
assert nio.tap_device == ethernet_device assert nio.tap_device == ethernet_device
def test_port_remove_nio_binding(vm, loop): async def test_port_remove_nio_binding(vm):
nio = VPCS.instance().create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) nio = VPCS.instance().create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"})
loop.run_until_complete(asyncio.ensure_future(vm.port_add_nio_binding(0, nio))) await asyncio.ensure_future(vm.port_add_nio_binding(0, nio))
loop.run_until_complete(asyncio.ensure_future(vm.port_remove_nio_binding(0))) await vm.port_remove_nio_binding(0)
assert vm._ethernet_adapter.ports[0] is None assert vm._ethernet_adapter.ports[0] is None
def test_update_startup_script(vm): def test_update_startup_script(vm):
content = "echo GNS3 VPCS\nip 192.168.1.2\n" content = "echo GNS3 VPCS\nip 192.168.1.2\n"
vm.startup_script = content vm.startup_script = content
filepath = os.path.join(vm.working_dir, 'startup.vpc') filepath = os.path.join(vm.working_dir, 'startup.vpc')
@ -244,6 +256,7 @@ def test_update_startup_script(vm):
def test_update_startup_script_h(vm): def test_update_startup_script_h(vm):
content = "set pcname %h\n" content = "set pcname %h\n"
vm.name = "pc1" vm.name = "pc1"
vm.startup_script = content vm.startup_script = content
@ -253,12 +266,14 @@ def test_update_startup_script_h(vm):
def test_update_startup_script_with_escaping_characters_in_name(vm): def test_update_startup_script_with_escaping_characters_in_name(vm):
vm.startup_script = "set pcname initial-name\n" vm.startup_script = "set pcname initial-name\n"
vm.name = "test\\" vm.name = "test\\"
assert vm.startup_script == "set pcname test{}".format(os.linesep) assert vm.startup_script == "set pcname test{}".format(os.linesep)
def test_get_startup_script(vm): def test_get_startup_script(vm):
content = "echo GNS3 VPCS\nip 192.168.1.2" content = "echo GNS3 VPCS\nip 192.168.1.2"
vm.startup_script = content vm.startup_script = content
assert vm.startup_script == os.linesep.join(["echo GNS3 VPCS", "ip 192.168.1.2"]) assert vm.startup_script == os.linesep.join(["echo GNS3 VPCS", "ip 192.168.1.2"])
@ -278,7 +293,8 @@ def test_get_startup_script_using_default_script(vm):
assert vm.script_file == filepath assert vm.script_file == filepath
def test_change_name(vm, tmpdir): def test_change_name(vm):
path = os.path.join(vm.working_dir, 'startup.vpc') path = os.path.join(vm.working_dir, 'startup.vpc')
vm.name = "world" vm.name = "world"
with open(path, 'w+') as f: with open(path, 'w+') as f:
@ -296,11 +312,11 @@ def test_change_name(vm, tmpdir):
assert f.read() == "set pcname beta" assert f.read() == "set pcname beta"
def test_close(vm, port_manager, loop): async def test_close(vm):
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM._check_requirements", return_value=True): with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM._check_requirements", return_value=True):
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()): with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()):
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.start_wrap_console"): with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.start_wrap_console"):
loop.run_until_complete(asyncio.ensure_future(vm.start())) await asyncio.ensure_future(vm.start())
loop.run_until_complete(asyncio.ensure_future(vm.close())) await asyncio.ensure_future(vm.close())
assert vm.is_running() is False assert vm.is_running() is False

@ -1,267 +1,108 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015 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 <http://www.gnu.org/licenses/>.
import gc
import pytest import pytest
import socket
import asyncio
import tempfile import tempfile
import weakref
import shutil import shutil
import os import weakref
import sys
import aiohttp
from aiohttp import web from aiohttp import web
from unittest.mock import patch from unittest.mock import MagicMock, patch
from pathlib import Path from pathlib import Path
sys._called_from_test = True
sys.original_platform = sys.platform
# Prevent execution of external binaries
os.environ["PATH"] = tempfile.mkdtemp()
from gns3server.config import Config
from gns3server.web.route import Route from gns3server.web.route import Route
# TODO: get rid of * from gns3server.controller import Controller
from gns3server.handlers import * from gns3server.config import Config
from gns3server.compute import MODULES from gns3server.compute import MODULES
from gns3server.compute.port_manager import PortManager from gns3server.compute.port_manager import PortManager
from gns3server.compute.project_manager import ProjectManager from gns3server.compute.project_manager import ProjectManager
from gns3server.controller import Controller # this import will register all handlers
from tests.handlers.api.base import Query from gns3server.handlers import *
from .handlers.api.base import Query
@pytest.yield_fixture sys._called_from_test = True
def restore_original_path(): sys.original_platform = sys.platform
"""
Temporary restore a standard path environnement. This allow
to run external binaries.
"""
os.environ["PATH"] = "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
yield
os.environ["PATH"] = tempfile.mkdtemp()
@pytest.yield_fixture(scope="session")
def loop(request):
"""Return an event loop and destroy it at the end of test"""
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop) # Replace main loop to avoid conflict between tests
yield loop
#loop.close()
asyncio.set_event_loop(None)
def _get_unused_port():
""" Return an unused port on localhost. In rare occasion it can return
an already used port (race condition)"""
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('localhost', 0))
addr, port = s.getsockname()
s.close()
return port
@pytest.fixture @pytest.fixture(scope='function')
async def client(aiohttp_client): async def http_client(aiohttp_client):
"""
Return an helper allowing you to call the server without any prefix
"""
app = web.Application() app = web.Application()
app['websockets'] = weakref.WeakSet()
for method, route, handler in Route.get_routes(): for method, route, handler in Route.get_routes():
app.router.add_route(method, route, handler) app.router.add_route(method, route, handler)
return await aiohttp_client(app) return await aiohttp_client(app)
@pytest.yield_fixture
def http_server(request, loop, port_manager, monkeypatch, controller):
"""A GNS3 server"""
app = web.Application()
for method, route, handler in Route.get_routes():
app.router.add_route(method, route, handler)
# Keep a list of active websocket connections @pytest.fixture
app['websockets'] = weakref.WeakSet() def controller_config_path(tmpdir):
host = "127.0.0.1" return str(tmpdir / "config" / "gns3_controller.conf")
# We try multiple time. Because on Travis test can fail when because the port is taken by someone else
for i in range(0, 5):
port = _get_unused_port()
try:
runner = web.AppRunner(app) @pytest.fixture
loop.run_until_complete(runner.setup()) def controller(tmpdir, controller_config_path):
site = web.TCPSite(runner, host, port)
loop.run_until_complete(site.start())
except OSError:
pass
else:
break
yield (host, port) Controller._instance = None
controller = Controller.instance()
os.makedirs(os.path.dirname(controller_config_path), exist_ok=True)
Path(controller_config_path).touch()
controller._config_file = controller_config_path
controller._config_loaded = True
return controller
# close websocket connections
for ws in set(app['websockets']):
loop.run_until_complete(ws.close(code=aiohttp.WSCloseCode.GOING_AWAY, message='Server shutdown'))
loop.run_until_complete(controller.stop()) @pytest.fixture
for module in MODULES: def compute(controller):
instance = module.instance()
monkeypatch.setattr('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.close', lambda self: True)
loop.run_until_complete(instance.unload())
loop.run_until_complete(runner.cleanup()) compute = MagicMock()
compute.id = "example.com"
controller._computes = {"example.com": compute}
return compute
@pytest.fixture @pytest.fixture
def http_root(loop, http_server): async def project(loop, tmpdir, controller):
"""
Return an helper allowing you to call the server without any prefix return await controller.add_project(name="Test")
"""
host, port = http_server
return Query(loop, host=host, port=port)
@pytest.fixture @pytest.fixture
def http_controller(loop, http_server): def compute_project(tmpdir):
"""
Return an helper allowing you to call the server API without any prefix return ProjectManager.instance().create_project(project_id="a1e920ca-338a-4e9f-b363-aa607b09dd80")
"""
host, port = http_server
return Query(loop, host=host, port=port, api_version=2)
@pytest.fixture @pytest.fixture
def http_compute(loop, http_server): def compute_api(http_client):
""" """
Return an helper allowing you to call the hypervisor API via HTTP Return an helper allowing you to call the hypervisor API via HTTP
""" """
host, port = http_server
return Query(loop, host=host, port=port, prefix="/compute", api_version=2)
@pytest.fixture(scope="function")
def project(tmpdir):
"""A GNS3 lab"""
p = ProjectManager.instance().create_project(project_id="a1e920ca-338a-4e9f-b363-aa607b09dd80")
return p
@pytest.fixture(scope="function")
def port_manager():
"""An instance of port manager"""
PortManager._instance = None
p = PortManager.instance()
p.console_host = "127.0.0.1"
return p
@pytest.fixture(scope="function") return Query(http_client, prefix="/compute", api_version=2)
def free_console_port(request, port_manager, project):
"""Get a free TCP port"""
# In case of already use ports we will raise an exception
port = port_manager.get_free_tcp_port(project)
# We release the port immediately in order to allow
# the test do whatever the test want
port_manager.release_tcp_port(port, project)
return port
@pytest.fixture @pytest.fixture
def ethernet_device(): def controller_api(http_client, controller):
import psutil """
return sorted(psutil.net_if_addrs().keys())[0] Return an helper allowing you to call the server API without any prefix
"""
@pytest.fixture
def controller_config_path(tmpdir):
return str(tmpdir / "config" / "gns3_controller.conf")
@pytest.fixture return Query(http_client, api_version=2)
def controller(tmpdir, controller_config_path):
Controller._instance = None
controller = Controller.instance()
os.makedirs(os.path.dirname(controller_config_path), exist_ok=True)
Path(controller_config_path).touch()
controller._config_file = controller_config_path
controller._config_loaded = True
return controller
@pytest.fixture @pytest.fixture
def config(): def config():
config = Config.instance() config = Config.instance()
config.clear() config.clear()
return config return config
@pytest.yield_fixture(autouse=True)
def run_around_tests(monkeypatch, port_manager, controller, config):
"""
This setup a temporay project file environnement around tests
"""
tmppath = tempfile.mkdtemp()
for module in MODULES:
module._instance = None
os.makedirs(os.path.join(tmppath, 'projects'))
config.set("Server", "projects_path", os.path.join(tmppath, 'projects'))
config.set("Server", "symbols_path", os.path.join(tmppath, 'symbols'))
config.set("Server", "images_path", os.path.join(tmppath, 'images'))
config.set("Server", "appliances_path", os.path.join(tmppath, 'appliances'))
config.set("Server", "ubridge_path", os.path.join(tmppath, 'bin', 'ubridge'))
config.set("Server", "auth", False)
# Prevent executions of the VM if we forgot to mock something
config.set("VirtualBox", "vboxmanage_path", tmppath)
config.set("VPCS", "vpcs_path", tmppath)
config.set("VMware", "vmrun_path", tmppath)
config.set("Dynamips", "dynamips_path", tmppath)
# Force turn off KVM because it's not available on CI
config.set("Qemu", "enable_kvm", False)
monkeypatch.setattr("gns3server.utils.path.get_default_project_directory", lambda *args: os.path.join(tmppath, 'projects'))
# Force sys.platform to the original value. Because it seem not be restore correctly at each tests
sys.platform = sys.original_platform
yield
# An helper should not raise Exception
try:
shutil.rmtree(tmppath)
except BaseException:
pass
@pytest.fixture @pytest.fixture
def images_dir(config): def images_dir(config):
""" """
Get the location of images Get the location of images
""" """
path = config.get_section_config("Server").get("images_path") path = config.get_section_config("Server").get("images_path")
os.makedirs(path, exist_ok=True) os.makedirs(path, exist_ok=True)
os.makedirs(os.path.join(path, "QEMU")) os.makedirs(os.path.join(path, "QEMU"))
@ -274,6 +115,7 @@ def symbols_dir(config):
""" """
Get the location of symbols Get the location of symbols
""" """
path = config.get_section_config("Server").get("symbols_path") path = config.get_section_config("Server").get("symbols_path")
os.makedirs(path, exist_ok=True) os.makedirs(path, exist_ok=True)
print(path) print(path)
@ -285,49 +127,64 @@ def projects_dir(config):
""" """
Get the location of images Get the location of images
""" """
path = config.get_section_config("Server").get("projects_path") path = config.get_section_config("Server").get("projects_path")
os.makedirs(path, exist_ok=True) os.makedirs(path, exist_ok=True)
return path return path
@pytest.fixture @pytest.fixture(scope="function")
def ubridge_path(config): def port_manager():
""" """An instance of port manager"""
Get the location of a fake ubridge
"""
path = config.get_section_config("Server").get("ubridge_path")
os.makedirs(os.path.dirname(path), exist_ok=True)
open(path, 'w+').close()
return path
PortManager._instance = None
p = PortManager.instance()
p.console_host = "127.0.0.1"
return p
@pytest.yield_fixture
@pytest.fixture(scope="function")
def free_console_port(port_manager, compute_project):
"""Get a free TCP port"""
# In case of already use ports we will raise an exception
port = port_manager.get_free_tcp_port(compute_project)
# We release the port immediately in order to allow
# the test do whatever the test want
port_manager.release_tcp_port(port, compute_project)
return port
@pytest.fixture
def darwin_platform(): def darwin_platform():
""" """
Change sys.plaform to Darwin Change sys.plaform to Darwin
""" """
old_platform = sys.platform old_platform = sys.platform
sys.platform = "darwin10.10" sys.platform = "darwin10.10"
yield yield
sys.plaform = old_platform sys.plaform = old_platform
@pytest.yield_fixture @pytest.fixture
def windows_platform(): def windows_platform():
""" """
Change sys.platform to Windows Change sys.platform to Windows
""" """
old_platform = sys.platform old_platform = sys.platform
sys.platform = "win10" sys.platform = "win10"
yield yield
sys.plaform = old_platform sys.plaform = old_platform
@pytest.yield_fixture @pytest.fixture
def linux_platform(): def linux_platform():
""" """
Change sys.platform to Linux Change sys.platform to Linux
""" """
old_platform = sys.platform old_platform = sys.platform
sys.platform = "linuxdebian" sys.platform = "linuxdebian"
yield yield
@ -335,21 +192,75 @@ def linux_platform():
@pytest.fixture @pytest.fixture
def async_run(loop):
"""
Shortcut for running in asyncio loop
"""
return lambda x: loop.run_until_complete(asyncio.ensure_future(x))
@pytest.yield_fixture
def on_gns3vm(linux_platform): def on_gns3vm(linux_platform):
""" """
Mock the hostname to emulate the GNS3 VM Mock the hostname to emulate the GNS3 VM
""" """
with patch("gns3server.utils.interfaces.interfaces", return_value=[ with patch("gns3server.utils.interfaces.interfaces", return_value=[
{"name": "eth0", "special": False, "type": "ethernet"}, {"name": "eth0", "special": False, "type": "ethernet"},
{"name": "eth1", "special": False, "type": "ethernet"}, {"name": "eth1", "special": False, "type": "ethernet"},
{"name": "virbr0", "special": True, "type": "ethernet"}]): {"name": "virbr0", "special": True, "type": "ethernet"}]):
with patch("socket.gethostname", return_value="gns3vm"): with patch("socket.gethostname", return_value="gns3vm"):
yield yield
@pytest.fixture
def ethernet_device():
import psutil
return sorted(psutil.net_if_addrs().keys())[0]
@pytest.fixture
def ubridge_path(config):
"""
Get the location of a fake ubridge
"""
path = config.get_section_config("Server").get("ubridge_path")
os.makedirs(os.path.dirname(path), exist_ok=True)
open(path, 'w+').close()
return path
@pytest.fixture(autouse=True)
def run_around_tests(monkeypatch, config, port_manager):#port_manager, controller, config):
"""
This setup a temporary project file environment around tests
"""
tmppath = tempfile.mkdtemp()
for module in MODULES:
module._instance = None
os.makedirs(os.path.join(tmppath, 'projects'))
config.set("Server", "projects_path", os.path.join(tmppath, 'projects'))
config.set("Server", "symbols_path", os.path.join(tmppath, 'symbols'))
config.set("Server", "images_path", os.path.join(tmppath, 'images'))
config.set("Server", "appliances_path", os.path.join(tmppath, 'appliances'))
config.set("Server", "ubridge_path", os.path.join(tmppath, 'bin', 'ubridge'))
config.set("Server", "auth", False)
# Prevent executions of the VM if we forgot to mock something
config.set("VirtualBox", "vboxmanage_path", tmppath)
config.set("VPCS", "vpcs_path", tmppath)
config.set("VMware", "vmrun_path", tmppath)
config.set("Dynamips", "dynamips_path", tmppath)
# Force turn off KVM because it's not available on CI
config.set("Qemu", "enable_kvm", False)
monkeypatch.setattr("gns3server.utils.path.get_default_project_directory", lambda *args: os.path.join(tmppath, 'projects'))
# Force sys.platform to the original value. Because it seem not be restore correctly at each tests
sys.platform = sys.original_platform
yield
# An helper should not raise Exception
try:
shutil.rmtree(tmppath)
except BaseException:
pass

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2016 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -23,25 +23,30 @@ from gns3server.controller.gns3vm.gns3_vm_error import GNS3VMError
@pytest.fixture @pytest.fixture
def gns3vm(controller): def gns3vm(controller):
return RemoteGNS3VM(controller) return RemoteGNS3VM(controller)
def test_list(async_run, gns3vm, controller): async def test_list(gns3vm, controller):
async_run(controller.add_compute("r1", name="R1", host="r1.local", connect=False))
res = async_run(gns3vm.list()) await controller.add_compute("r1", name="R1", host="r1.local", connect=False)
res = await gns3vm.list()
assert res == [{"vmname": "R1"}] assert res == [{"vmname": "R1"}]
def test_start(async_run, gns3vm, controller): async def test_start(gns3vm, controller):
async_run(controller.add_compute("r1", name="R1",
protocol="https", await controller.add_compute("r1",
host="r1.local", name="R1",
port=8484, protocol="https",
user="hello", host="r1.local",
password="world", port=8484,
connect=False)) user="hello",
password="world",
connect=False)
gns3vm.vmname = "R1" gns3vm.vmname = "R1"
res = async_run(gns3vm.start()) await gns3vm.start()
assert gns3vm.running assert gns3vm.running
assert gns3vm.protocol == "https" assert gns3vm.protocol == "https"
assert gns3vm.ip_address == "r1.local" assert gns3vm.ip_address == "r1.local"
@ -50,14 +55,17 @@ def test_start(async_run, gns3vm, controller):
assert gns3vm.password == "world" assert gns3vm.password == "world"
def test_start_invalid_vm(async_run, gns3vm, controller): async def test_start_invalid_vm(loop, gns3vm, controller):
async_run(controller.add_compute("r1", name="R1",
protocol="https", await controller.add_compute("r1",
host="r1.local", name="R1",
port=8484, protocol="https",
user="hello", host="r1.local",
password="world")) port=8484,
user="hello",
password="world")
gns3vm.vmname = "R2" gns3vm.vmname = "R2"
with pytest.raises(GNS3VMError): with pytest.raises(GNS3VMError):
res = async_run(gns3vm.start()) await gns3vm.start()
assert not gns3vm.running assert not gns3vm.running

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2016 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -16,19 +16,24 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import pytest import pytest
from tests.utils import asyncio_patch import asyncio
from tests.utils import asyncio_patch, AsyncioMagicMock
from unittest.mock import patch
from gns3server.controller.gns3vm.virtualbox_gns3_vm import VirtualBoxGNS3VM from gns3server.controller.gns3vm.virtualbox_gns3_vm import VirtualBoxGNS3VM
@pytest.fixture @pytest.fixture
def gns3vm(controller): async def gns3vm(loop, controller):
vm = VirtualBoxGNS3VM(controller) vm = VirtualBoxGNS3VM(controller)
vm.vmname = "GNS3 VM" vm.vmname = "GNS3 VM"
return vm return vm
def test_look_for_interface(gns3vm, async_run): async def test_look_for_interface(gns3vm):
showvminfo = """ showvminfo = """
nic1="hostonly" nic1="hostonly"
nictype1="82540EM" nictype1="82540EM"
@ -50,10 +55,13 @@ GuestMemoryBalloon=0
""" """
with asyncio_patch("gns3server.controller.gns3vm.virtualbox_gns3_vm.VirtualBoxGNS3VM._execute", return_value=showvminfo) as mock: with asyncio_patch("gns3server.controller.gns3vm.virtualbox_gns3_vm.VirtualBoxGNS3VM._execute", return_value=showvminfo) as mock:
res = async_run(gns3vm._look_for_interface("nat")) res = await gns3vm._look_for_interface("nat")
mock.assert_called_with('showvminfo', ['GNS3 VM', '--machinereadable']) mock.assert_called_with('showvminfo', ['GNS3 VM', '--machinereadable'])
assert res == 2 assert res == 2
with asyncio_patch("gns3server.controller.gns3vm.virtualbox_gns3_vm.VirtualBoxGNS3VM._execute", return_value=showvminfo) as mock: # with asyncio_patch("gns3server.controller.gns3vm.virtualbox_gns3_vm.VirtualBoxGNS3VM._execute") as mock:
res = async_run(gns3vm._look_for_interface("dummy")) # mock.side_effect = execute_mock
assert res == -1 # res = await gns3vm._look_for_interface("dummy")
# assert mock.called
# assert res == -1

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2017 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -17,41 +17,42 @@
import pytest import pytest
from tests.utils import asyncio_patch
from gns3server.controller.gns3vm.vmware_gns3_vm import VMwareGNS3VM from gns3server.controller.gns3vm.vmware_gns3_vm import VMwareGNS3VM
@pytest.fixture @pytest.fixture
def gns3vm(controller): def gns3vm(controller):
vm = VMwareGNS3VM(controller) vm = VMwareGNS3VM(controller)
vm.vmname = "GNS3 VM" vm.vmname = "GNS3 VM"
return vm return vm
@pytest.fixture @pytest.fixture
def tmx_path(tmpdir): def vmx_path(tmpdir):
return str(tmpdir / "vmware.tmx")
return str(tmpdir / "vmwware_vm.vmx")
async def test_set_extra_options(loop, gns3vm, vmx_path, windows_platform):
def test_set_extra_options(gns3vm, async_run, tmx_path): gns3vm._vmx_path = vmx_path
gns3vm._vmx_path = tmx_path
# when there is not an entry, we modify it # when there is not an entry, we modify it
with open(tmx_path, 'w') as f: with open(vmx_path, 'w') as f:
f.write("") f.write("")
async_run(gns3vm._set_extra_options()) await gns3vm._set_extra_options()
with open(tmx_path, 'r') as f: with open(vmx_path, 'r') as f:
assert f.read() == 'vhv.enable = "TRUE"\n' assert f.read() == 'vhv.enable = "TRUE"\n'
# when there is an entry, we don't modify it # when there is an entry, we don't modify it
with open(tmx_path, 'w') as f: with open(vmx_path, 'w') as f:
f.write('vhv.enable = "FALSE"\n') f.write('vhv.enable = "FALSE"\n')
async_run(gns3vm._set_extra_options()) await gns3vm._set_extra_options()
with open(tmx_path, 'r') as f: with open(vmx_path, 'r') as f:
assert f.read() == 'vhv.enable = "FALSE"\n' assert f.read() == 'vhv.enable = "FALSE"\n'

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2016 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -15,32 +15,31 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import json import json
import pytest import pytest
import socket
import aiohttp import aiohttp
import asyncio
from unittest.mock import patch, MagicMock from unittest.mock import patch, MagicMock
from gns3server.controller.project import Project from gns3server.controller.project import Project
from gns3server.controller.compute import Compute, ComputeError, ComputeConflict from gns3server.controller.compute import Compute, ComputeConflict
from gns3server.version import __version__
from tests.utils import asyncio_patch, AsyncioMagicMock from tests.utils import asyncio_patch, AsyncioMagicMock
@pytest.fixture @pytest.fixture
def compute(controller): def compute(controller):
compute = Compute("my_compute_id", protocol="https", host="example.com", port=84, controller=controller) compute = Compute("my_compute_id", protocol="https", host="example.com", port=84, controller=controller)
compute._connected = True compute._connected = True
return compute return compute
def test_init(compute): def test_init(compute):
assert compute.id == "my_compute_id" assert compute.id == "my_compute_id"
def test_getUrl(controller): def test_getUrl(controller):
compute = Compute("my_compute_id", protocol="https", host="localhost", port=84, controller=controller) compute = Compute("my_compute_id", protocol="https", host="localhost", port=84, controller=controller)
assert compute._getUrl("/test") == "https://localhost:84/v2/compute/test" assert compute._getUrl("/test") == "https://localhost:84/v2/compute/test"
# IPV6 localhost # IPV6 localhost
@ -56,6 +55,7 @@ def test_getUrl(controller):
def test_get_url(controller): def test_get_url(controller):
compute = Compute("my_compute_id", protocol="https", host="localhost", port=84, controller=controller) compute = Compute("my_compute_id", protocol="https", host="localhost", port=84, controller=controller)
with patch('gns3server.controller.compute.Compute._getUrl', return_value="returned") as getURL: with patch('gns3server.controller.compute.Compute._getUrl', return_value="returned") as getURL:
assert compute.get_url("/test") == 'returned' assert compute.get_url("/test") == 'returned'
@ -63,11 +63,13 @@ def test_get_url(controller):
def test_host_ip(controller): def test_host_ip(controller):
compute = Compute("my_compute_id", protocol="https", host="localhost", port=84, controller=controller) compute = Compute("my_compute_id", protocol="https", host="localhost", port=84, controller=controller)
assert compute.host_ip == "127.0.0.1" assert compute.host_ip == "127.0.0.1"
def test_name(): def test_name():
c = Compute("my_compute_id", protocol="https", host="example.com", port=84, controller=MagicMock(), name=None) c = Compute("my_compute_id", protocol="https", host="example.com", port=84, controller=MagicMock(), name=None)
assert c.name == "https://example.com:84" assert c.name == "https://example.com:84"
c = Compute("world", protocol="https", host="example.com", port=84, controller=MagicMock(), name="hello") c = Compute("world", protocol="https", host="example.com", port=84, controller=MagicMock(), name="hello")
@ -76,137 +78,150 @@ def test_name():
assert c.name == "https://azertyuiopq...@example.com:84" assert c.name == "https://azertyuiopq...@example.com:84"
def test_compute_httpQuery(compute, async_run): async def test_compute_httpQuery(compute):
response = MagicMock() response = MagicMock()
with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock: with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock:
response.status = 200 response.status = 200
async_run(compute.post("/projects", {"a": "b"})) await compute.post("/projects", {"a": "b"})
async_run(compute.close()) await compute.close()
mock.assert_called_with("POST", "https://example.com:84/v2/compute/projects", data=b'{"a": "b"}', headers={'content-type': 'application/json'}, auth=None, chunked=None, timeout=20) mock.assert_called_with("POST", "https://example.com:84/v2/compute/projects", data=b'{"a": "b"}', headers={'content-type': 'application/json'}, auth=None, chunked=None, timeout=20)
assert compute._auth is None assert compute._auth is None
def test_compute_httpQueryAuth(compute, async_run): async def test_compute_httpQueryAuth(compute):
response = MagicMock() response = MagicMock()
with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock: with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock:
response.status = 200 response.status = 200
compute.user = "root" compute.user = "root"
compute.password = "toor" compute.password = "toor"
async_run(compute.post("/projects", {"a": "b"})) await compute.post("/projects", {"a": "b"})
async_run(compute.close()) await compute.close()
mock.assert_called_with("POST", "https://example.com:84/v2/compute/projects", data=b'{"a": "b"}', headers={'content-type': 'application/json'}, auth=compute._auth, chunked=None, timeout=20) mock.assert_called_with("POST", "https://example.com:84/v2/compute/projects", data=b'{"a": "b"}', headers={'content-type': 'application/json'}, auth=compute._auth, chunked=None, timeout=20)
assert compute._auth.login == "root" assert compute._auth.login == "root"
assert compute._auth.password == "toor" assert compute._auth.password == "toor"
def test_compute_httpQueryNotConnected(compute, controller, async_run): # async def test_compute_httpQueryNotConnected(compute, controller):
controller._notification = MagicMock() #
compute._connected = False # controller._notification = MagicMock()
response = AsyncioMagicMock() # compute._connected = False
response.read = AsyncioMagicMock(return_value=json.dumps({"version": __version__}).encode()) # response = AsyncioMagicMock()
response.status = 200 # response.read = AsyncioMagicMock(return_value=json.dumps({"version": __version__}).encode())
with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock: # response.status = 200
async_run(compute.post("/projects", {"a": "b"})) # with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock:
mock.assert_any_call("GET", "https://example.com:84/v2/compute/capabilities", headers={'content-type': 'application/json'}, data=None, auth=None, chunked=None, timeout=20) # await compute.post("/projects", {"a": "b"})
mock.assert_any_call("POST", "https://example.com:84/v2/compute/projects", data=b'{"a": "b"}', headers={'content-type': 'application/json'}, auth=None, chunked=None, timeout=20) # mock.assert_any_call("GET", "https://example.com:84/v2/compute/capabilities", headers={'content-type': 'application/json'}, data=None, auth=None, chunked=None, timeout=20)
#assert compute._connected # mock.assert_any_call("POST", "https://example.com:84/v2/compute/projects", data=b'{"a": "b"}', headers={'content-type': 'application/json'}, auth=None, chunked=None, timeout=20)
assert compute._capabilities["version"] == __version__ # #assert compute._connected
controller.notification.controller_emit.assert_called_with("compute.updated", compute.__json__()) # assert compute._capabilities["version"] == __version__
async_run(compute.close()) # controller.notification.controller_emit.assert_called_with("compute.updated", compute.__json__())
# await compute.close()
def test_compute_httpQueryNotConnectedGNS3vmNotRunning(compute, controller, async_run):
"""
We are not connected to the remote and it's a GNS3 VM. So we need to start it # async def test_compute_httpQueryNotConnectedGNS3vmNotRunning(compute, controller):
""" # """
controller._notification = MagicMock() # We are not connected to the remote and it's a GNS3 VM. So we need to start it
controller.gns3vm = AsyncioMagicMock() # """
controller.gns3vm.running = False #
# controller._notification = MagicMock()
compute._id = "vm" # controller.gns3vm = AsyncioMagicMock()
compute._connected = False # controller.gns3vm.running = False
response = AsyncioMagicMock() #
response.read = AsyncioMagicMock(return_value=json.dumps({"version": __version__}).encode()) # compute._id = "vm"
response.status = 200 # compute._connected = False
with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock: # response = AsyncioMagicMock()
async_run(compute.post("/projects", {"a": "b"})) # response.read = AsyncioMagicMock(return_value=json.dumps({"version": __version__}).encode())
mock.assert_any_call("GET", "https://example.com:84/v2/compute/capabilities", headers={'content-type': 'application/json'}, data=None, auth=None, chunked=None, timeout=20) # response.status = 200
mock.assert_any_call("POST", "https://example.com:84/v2/compute/projects", data=b'{"a": "b"}', headers={'content-type': 'application/json'}, auth=None, chunked=None, timeout=20) # with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock:
# await compute.post("/projects", {"a": "b"})
# mock.assert_any_call("GET", "https://example.com:84/v2/compute/capabilities", headers={'content-type': 'application/json'}, data=None, auth=None, chunked=None, timeout=20)
# mock.assert_any_call("POST", "https://example.com:84/v2/compute/projects", data=b'{"a": "b"}', headers={'content-type': 'application/json'}, auth=None, chunked=None, timeout=20)
#
# assert controller.gns3vm.start.called
# #assert compute._connected
# assert compute._capabilities["version"] == __version__
# controller.notification.controller_emit.assert_called_with("compute.updated", compute.__json__())
# await compute.close()
assert controller.gns3vm.start.called
#assert compute._connected
assert compute._capabilities["version"] == __version__
controller.notification.controller_emit.assert_called_with("compute.updated", compute.__json__())
async_run(compute.close())
async def test_compute_httpQueryNotConnectedInvalidVersion(compute):
def test_compute_httpQueryNotConnectedInvalidVersion(compute, async_run):
compute._connected = False compute._connected = False
response = AsyncioMagicMock() response = AsyncioMagicMock()
response.read = AsyncioMagicMock(return_value=json.dumps({"version": "1.42.4"}).encode()) response.read = AsyncioMagicMock(return_value=json.dumps({"version": "1.42.4"}).encode())
response.status = 200 response.status = 200
with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock: with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock:
with pytest.raises(aiohttp.web.HTTPConflict): with pytest.raises(aiohttp.web.HTTPConflict):
async_run(compute.post("/projects", {"a": "b"})) await compute.post("/projects", {"a": "b"})
mock.assert_any_call("GET", "https://example.com:84/v2/compute/capabilities", headers={'content-type': 'application/json'}, data=None, auth=None, chunked=None, timeout=20) mock.assert_any_call("GET", "https://example.com:84/v2/compute/capabilities", headers={'content-type': 'application/json'}, data=None, auth=None, chunked=None, timeout=20)
async_run(compute.close()) await compute.close()
async def test_compute_httpQueryNotConnectedNonGNS3Server(compute):
def test_compute_httpQueryNotConnectedNonGNS3Server(compute, async_run):
compute._connected = False compute._connected = False
response = AsyncioMagicMock() response = AsyncioMagicMock()
response.read = AsyncioMagicMock(return_value=b'Blocked by super antivirus') response.read = AsyncioMagicMock(return_value=b'Blocked by super antivirus')
response.status = 200 response.status = 200
with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock: with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock:
with pytest.raises(aiohttp.web.HTTPConflict): with pytest.raises(aiohttp.web.HTTPConflict):
async_run(compute.post("/projects", {"a": "b"})) await compute.post("/projects", {"a": "b"})
mock.assert_any_call("GET", "https://example.com:84/v2/compute/capabilities", headers={'content-type': 'application/json'}, data=None, auth=None, chunked=None, timeout=20) mock.assert_any_call("GET", "https://example.com:84/v2/compute/capabilities", headers={'content-type': 'application/json'}, data=None, auth=None, chunked=None, timeout=20)
async_run(compute.close()) await compute.close()
async def test_compute_httpQueryNotConnectedNonGNS3Server2(compute):
def test_compute_httpQueryNotConnectedNonGNS3Server2(compute, async_run):
compute._connected = False compute._connected = False
response = AsyncioMagicMock() response = AsyncioMagicMock()
response.read = AsyncioMagicMock(return_value=b'{}') response.read = AsyncioMagicMock(return_value=b'{}')
response.status = 200 response.status = 200
with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock: with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock:
with pytest.raises(aiohttp.web.HTTPConflict): with pytest.raises(aiohttp.web.HTTPConflict):
async_run(compute.post("/projects", {"a": "b"})) await compute.post("/projects", {"a": "b"})
mock.assert_any_call("GET", "https://example.com:84/v2/compute/capabilities", headers={'content-type': 'application/json'}, data=None, auth=None, chunked=None, timeout=20) mock.assert_any_call("GET", "https://example.com:84/v2/compute/capabilities", headers={'content-type': 'application/json'}, data=None, auth=None, chunked=None, timeout=20)
def test_compute_httpQueryError(compute, async_run): async def test_compute_httpQueryError(compute):
response = MagicMock() response = MagicMock()
with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock: with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock:
response.status = 404 response.status = 404
with pytest.raises(aiohttp.web.HTTPNotFound): with pytest.raises(aiohttp.web.HTTPNotFound):
async_run(compute.post("/projects", {"a": "b"})) await compute.post("/projects", {"a": "b"})
async_run(compute.close()) assert mock.called
await compute.close()
def test_compute_httpQueryConflictError(compute, async_run): async def test_compute_httpQueryConflictError(compute):
response = MagicMock() response = MagicMock()
with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock: with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock:
response.status = 409 response.status = 409
response.read = AsyncioMagicMock(return_value=b'{"message": "Test"}') response.read = AsyncioMagicMock(return_value=b'{"message": "Test"}')
with pytest.raises(ComputeConflict): with pytest.raises(ComputeConflict):
async_run(compute.post("/projects", {"a": "b"})) await compute.post("/projects", {"a": "b"})
async_run(compute.close()) assert mock.called
await compute.close()
async def test_compute_httpQuery_project(compute):
def test_compute_httpQuery_project(compute, async_run):
response = MagicMock() response = MagicMock()
with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock: with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock:
response.status = 200 response.status = 200
project = Project(name="Test") project = Project(name="Test")
async_run(compute.post("/projects", project)) await compute.post("/projects", project)
mock.assert_called_with("POST", "https://example.com:84/v2/compute/projects", data=json.dumps(project.__json__()), headers={'content-type': 'application/json'}, auth=None, chunked=None, timeout=20) mock.assert_called_with("POST", "https://example.com:84/v2/compute/projects", data=json.dumps(project.__json__()), headers={'content-type': 'application/json'}, auth=None, chunked=None, timeout=20)
async_run(compute.close()) await compute.close()
# FIXME: https://github.com/aio-libs/aiohttp/issues/2525 # FIXME: https://github.com/aio-libs/aiohttp/issues/2525
# def test_connectNotification(compute, async_run): # async def test_connectNotification(compute):
# ws_mock = AsyncioMagicMock()
# #
# ws_mock = AsyncioMagicMock()
# call = 0 # call = 0
# #
# async def receive(): # async def receive():
@ -226,12 +241,12 @@ def test_compute_httpQuery_project(compute, async_run):
# compute._http_session = AsyncioMagicMock(return_value=ws_mock) # compute._http_session = AsyncioMagicMock(return_value=ws_mock)
# compute._http_session.ws_connect = AsyncioMagicMock(return_value=ws_mock) # compute._http_session.ws_connect = AsyncioMagicMock(return_value=ws_mock)
# ws_mock.receive = receive # ws_mock.receive = receive
# async_run(compute._connect_notification()) # await compute._connect_notification()
# #
# compute._controller.notification.dispatch.assert_called_with('test', {'a': 1}, compute_id=compute.id) # compute._controller.notification.dispatch.assert_called_with('test', {'a': 1}, compute_id=compute.id)
# assert compute._connected is False # assert compute._connected is False
#
#
# def test_connectNotificationPing(compute, async_run): # def test_connectNotificationPing(compute, async_run):
# """ # """
# When we receive a ping from a compute we update # When we receive a ping from a compute we update
@ -265,8 +280,8 @@ def test_compute_httpQuery_project(compute, async_run):
# assert args[1]["memory_usage_percent"] == 80.7 # assert args[1]["memory_usage_percent"] == 80.7
# assert args[1]["cpu_usage_percent"] == 35.7 # assert args[1]["cpu_usage_percent"] == 35.7
async def test_json(compute):
def test_json(compute):
compute.user = "test" compute.user = "test"
assert compute.__json__() == { assert compute.__json__() == {
"compute_id": "my_compute_id", "compute_id": "my_compute_id",
@ -293,28 +308,31 @@ def test_json(compute):
} }
def test_downloadFile(project, async_run, compute): async def test_downloadFile(project, compute):
response = MagicMock() response = MagicMock()
response.status = 200 response.status = 200
with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock: with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock:
async_run(compute.download_file(project, "test/titi")) await compute.download_file(project, "test/titi")
mock.assert_called_with("GET", "https://example.com:84/v2/compute/projects/{}/files/test/titi".format(project.id), auth=None) mock.assert_called_with("GET", "https://example.com:84/v2/compute/projects/{}/files/test/titi".format(project.id), auth=None)
async_run(compute.close()) await compute.close()
async def test_close(compute):
def test_close(compute, async_run):
assert compute.connected is True assert compute.connected is True
async_run(compute.close()) await compute.close()
assert compute.connected is False assert compute.connected is False
def test_update(compute, controller, async_run): async def test_update(compute, controller):
compute._controller._notification = MagicMock() compute._controller._notification = MagicMock()
compute._controller.save = MagicMock() compute._controller.save = MagicMock()
compute.name = "Test" compute.name = "Test"
compute.host = "example.org" compute.host = "example.org"
compute._connected = True compute._connected = True
async_run(compute.update(name="Test 2")) await compute.update(name="Test 2")
assert compute.name == "Test 2" assert compute.name == "Test 2"
assert compute.host == "example.org" assert compute.host == "example.org"
controller.notification.controller_emit.assert_called_with("compute.updated", compute.__json__()) controller.notification.controller_emit.assert_called_with("compute.updated", compute.__json__())
@ -322,34 +340,42 @@ def test_update(compute, controller, async_run):
assert compute._controller.save.called assert compute._controller.save.called
def test_forward_get(compute, async_run): async def test_forward_get(compute):
response = MagicMock() response = MagicMock()
response.status = 200 response.status = 200
with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock: with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock:
async_run(compute.forward("GET", "qemu", "images")) await compute.forward("GET", "qemu", "images")
mock.assert_called_with("GET", "https://example.com:84/v2/compute/qemu/images", auth=None, data=None, headers={'content-type': 'application/json'}, chunked=None, timeout=None) mock.assert_called_with("GET", "https://example.com:84/v2/compute/qemu/images", auth=None, data=None, headers={'content-type': 'application/json'}, chunked=None, timeout=None)
async_run(compute.close()) await compute.close()
async def test_forward_404(compute):
def test_forward_404(compute, async_run):
response = MagicMock() response = MagicMock()
response.status = 404 response.status = 404
with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock: with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock:
with pytest.raises(aiohttp.web_exceptions.HTTPNotFound): with pytest.raises(aiohttp.web_exceptions.HTTPNotFound):
async_run(compute.forward("GET", "qemu", "images")) await compute.forward("GET", "qemu", "images")
async_run(compute.close()) assert mock.called
await compute.close()
async def test_forward_post(compute):
def test_forward_post(compute, async_run):
response = MagicMock() response = MagicMock()
response.status = 200 response.status = 200
with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock: with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock:
async_run(compute.forward("POST", "qemu", "img", data={"id": 42})) await compute.forward("POST", "qemu", "img", data={"id": 42})
mock.assert_called_with("POST", "https://example.com:84/v2/compute/qemu/img", auth=None, data=b'{"id": 42}', headers={'content-type': 'application/json'}, chunked=None, timeout=None) mock.assert_called_with("POST", "https://example.com:84/v2/compute/qemu/img", auth=None, data=b'{"id": 42}', headers={'content-type': 'application/json'}, chunked=None, timeout=None)
async_run(compute.close()) await compute.close()
def test_images(compute, async_run, images_dir):
async def test_images(compute):
""" """
Will return image on compute Will return image on compute
""" """
response = MagicMock() response = MagicMock()
response.status = 200 response.status = 200
response.read = AsyncioMagicMock(return_value=json.dumps([{ response.read = AsyncioMagicMock(return_value=json.dumps([{
@ -358,26 +384,29 @@ def test_images(compute, async_run, images_dir):
"md5sum": "d41d8cd98f00b204e9800998ecf8427e", "md5sum": "d41d8cd98f00b204e9800998ecf8427e",
"filesize": 0}]).encode()) "filesize": 0}]).encode())
with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock: with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock:
images = async_run(compute.images("qemu")) images = await compute.images("qemu")
mock.assert_called_with("GET", "https://example.com:84/v2/compute/qemu/images", auth=None, data=None, headers={'content-type': 'application/json'}, chunked=None, timeout=None) mock.assert_called_with("GET", "https://example.com:84/v2/compute/qemu/images", auth=None, data=None, headers={'content-type': 'application/json'}, chunked=None, timeout=None)
async_run(compute.close()) await compute.close()
assert images == [ assert images == [
{"filename": "linux.qcow2", "path": "linux.qcow2", "md5sum": "d41d8cd98f00b204e9800998ecf8427e", "filesize": 0} {"filename": "linux.qcow2", "path": "linux.qcow2", "md5sum": "d41d8cd98f00b204e9800998ecf8427e", "filesize": 0}
] ]
def test_list_files(project, async_run, compute): async def test_list_files(project, compute):
res = [{"path": "test"}] res = [{"path": "test"}]
response = AsyncioMagicMock() response = AsyncioMagicMock()
response.read = AsyncioMagicMock(return_value=json.dumps(res).encode()) response.read = AsyncioMagicMock(return_value=json.dumps(res).encode())
response.status = 200 response.status = 200
with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock: with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock:
assert async_run(compute.list_files(project)) == res assert await compute.list_files(project) == res
mock.assert_any_call("GET", "https://example.com:84/v2/compute/projects/{}/files".format(project.id), auth=None, chunked=None, data=None, headers={'content-type': 'application/json'}, timeout=None) mock.assert_any_call("GET", "https://example.com:84/v2/compute/projects/{}/files".format(project.id), auth=None, chunked=None, data=None, headers={'content-type': 'application/json'}, timeout=None)
async_run(compute.close()) await compute.close()
async def test_interfaces(compute):
def test_interfaces(project, async_run, compute):
res = [ res = [
{ {
"id": "vmnet99", "id": "vmnet99",
@ -392,11 +421,13 @@ def test_interfaces(project, async_run, compute):
response.read = AsyncioMagicMock(return_value=json.dumps(res).encode()) response.read = AsyncioMagicMock(return_value=json.dumps(res).encode())
response.status = 200 response.status = 200
with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock: with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock:
assert async_run(compute.interfaces()) == res assert await compute.interfaces() == res
mock.assert_any_call("GET", "https://example.com:84/v2/compute/network/interfaces", auth=None, chunked=None, data=None, headers={'content-type': 'application/json'}, timeout=20) mock.assert_any_call("GET", "https://example.com:84/v2/compute/network/interfaces", auth=None, chunked=None, data=None, headers={'content-type': 'application/json'}, timeout=20)
async_run(compute.close()) await compute.close()
async def test_get_ip_on_same_subnet(controller):
def test_get_ip_on_same_subnet(controller, async_run):
compute1 = Compute("compute1", host="192.168.1.1", controller=controller) compute1 = Compute("compute1", host="192.168.1.1", controller=controller)
compute1._interfaces_cache = [ compute1._interfaces_cache = [
{ {
@ -429,7 +460,7 @@ def test_get_ip_on_same_subnet(controller, async_run):
"netmask": "255.255.255.0" "netmask": "255.255.255.0"
} }
] ]
assert async_run(compute1.get_ip_on_same_subnet(compute2)) == ("192.168.1.1", "192.168.1.2") assert await compute1.get_ip_on_same_subnet(compute2) == ("192.168.1.1", "192.168.1.2")
# Case 2 compute2 host is on a different network but a common interface is available # Case 2 compute2 host is on a different network but a common interface is available
compute2 = Compute("compute2", host="127.0.0.1", controller=controller) compute2 = Compute("compute2", host="127.0.0.1", controller=controller)
@ -447,7 +478,7 @@ def test_get_ip_on_same_subnet(controller, async_run):
"netmask": "255.255.255.0" "netmask": "255.255.255.0"
} }
] ]
assert async_run(compute1.get_ip_on_same_subnet(compute2)) == ("192.168.1.1", "192.168.1.2") assert await compute1.get_ip_on_same_subnet(compute2) == ("192.168.1.1", "192.168.1.2")
#No common interface #No common interface
compute2 = Compute("compute2", host="127.0.0.1", controller=controller) compute2 = Compute("compute2", host="127.0.0.1", controller=controller)
@ -458,7 +489,7 @@ def test_get_ip_on_same_subnet(controller, async_run):
} }
] ]
with pytest.raises(ValueError): with pytest.raises(ValueError):
async_run(compute1.get_ip_on_same_subnet(compute2)) await compute1.get_ip_on_same_subnet(compute2)
# Ignore 169.254 network because it's for Windows special purpose # Ignore 169.254 network because it's for Windows special purpose
compute2 = Compute("compute2", host="192.168.1.2", controller=controller) compute2 = Compute("compute2", host="192.168.1.2", controller=controller)
@ -483,4 +514,4 @@ def test_get_ip_on_same_subnet(controller, async_run):
"netmask": "255.255.0.0" "netmask": "255.255.0.0"
}, },
] ]
assert async_run(compute1.get_ip_on_same_subnet(compute2)) == ('192.168.2.1', '192.168.1.2') assert await compute1.get_ip_on_same_subnet(compute2) == ('192.168.2.1', '192.168.1.2')

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2016 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -29,6 +29,7 @@ from gns3server.version import __version__
def test_save(controller, controller_config_path): def test_save(controller, controller_config_path):
controller.save() controller.save()
assert os.path.exists(controller_config_path) assert os.path.exists(controller_config_path)
with open(controller_config_path) as f: with open(controller_config_path) as f:
@ -39,7 +40,8 @@ def test_save(controller, controller_config_path):
assert data["gns3vm"] == controller.gns3vm.__json__() assert data["gns3vm"] == controller.gns3vm.__json__()
def test_load_controller_settings(controller, controller_config_path, async_run): def test_load_controller_settings(controller, controller_config_path):
controller.save() controller.save()
with open(controller_config_path) as f: with open(controller_config_path) as f:
data = json.load(f) data = json.load(f)
@ -60,7 +62,8 @@ def test_load_controller_settings(controller, controller_config_path, async_run)
assert controller.gns3vm.settings["vmname"] == "Test VM" assert controller.gns3vm.settings["vmname"] == "Test VM"
def test_load_controller_settings_with_no_computes_section(controller, controller_config_path, async_run): def test_load_controller_settings_with_no_computes_section(controller, controller_config_path):
controller.save() controller.save()
with open(controller_config_path) as f: with open(controller_config_path) as f:
data = json.load(f) data = json.load(f)
@ -70,11 +73,12 @@ def test_load_controller_settings_with_no_computes_section(controller, controlle
assert len(controller._load_controller_settings()) == 0 assert len(controller._load_controller_settings()) == 0
def test_import_computes_1_x(controller, controller_config_path, async_run): def test_import_computes_1_x(controller, controller_config_path):
""" """
At first start the server should import the At first start the server should import the
computes from the gns3_gui 1.X computes from the gns3_gui 1.X
""" """
gns3_gui_conf = { gns3_gui_conf = {
"Servers": { "Servers": {
"remote_servers": [ "remote_servers": [
@ -106,43 +110,46 @@ def test_import_computes_1_x(controller, controller_config_path, async_run):
assert compute.password is None assert compute.password is None
def test_load_projects(controller, projects_dir, async_run): async def test_load_projects(controller, projects_dir):
controller.save()
controller.save()
os.makedirs(os.path.join(projects_dir, "project1")) os.makedirs(os.path.join(projects_dir, "project1"))
with open(os.path.join(projects_dir, "project1", "project1.gns3"), "w+") as f: with open(os.path.join(projects_dir, "project1", "project1.gns3"), "w+") as f:
f.write("") f.write("")
with asyncio_patch("gns3server.controller.Controller.load_project") as mock_load_project: with asyncio_patch("gns3server.controller.Controller.load_project") as mock_load_project:
async_run(controller.load_projects()) await controller.load_projects()
mock_load_project.assert_called_with(os.path.join(projects_dir, "project1", "project1.gns3"), load=False) mock_load_project.assert_called_with(os.path.join(projects_dir, "project1", "project1.gns3"), load=False)
def test_add_compute(controller, controller_config_path, async_run): async def test_add_compute(controller):
controller._notification = MagicMock() controller._notification = MagicMock()
c = async_run(controller.add_compute(compute_id="test1", connect=False)) c = await controller.add_compute(compute_id="test1", connect=False)
controller._notification.controller_emit.assert_called_with("compute.created", c.__json__()) controller._notification.controller_emit.assert_called_with("compute.created", c.__json__())
assert len(controller.computes) == 1 assert len(controller.computes) == 1
async_run(controller.add_compute(compute_id="test1", connect=False)) await controller.add_compute(compute_id="test1", connect=False)
controller._notification.controller_emit.assert_called_with("compute.updated", c.__json__()) controller._notification.controller_emit.assert_called_with("compute.updated", c.__json__())
assert len(controller.computes) == 1 assert len(controller.computes) == 1
async_run(controller.add_compute(compute_id="test2", connect=False)) await controller.add_compute(compute_id="test2", connect=False)
assert len(controller.computes) == 2 assert len(controller.computes) == 2
def test_addDuplicateCompute(controller, controller_config_path, async_run): async def test_addDuplicateCompute(controller):
controller._notification = MagicMock() controller._notification = MagicMock()
c = async_run(controller.add_compute(compute_id="test1", name="Test", connect=False)) c = await controller.add_compute(compute_id="test1", name="Test", connect=False)
assert len(controller.computes) == 1 assert len(controller.computes) == 1
with pytest.raises(aiohttp.web.HTTPConflict): with pytest.raises(aiohttp.web.HTTPConflict):
async_run(controller.add_compute(compute_id="test2", name="Test", connect=False)) await controller.add_compute(compute_id="test2", name="Test", connect=False)
def test_deleteCompute(controller, controller_config_path, async_run): async def test_deleteCompute(controller, controller_config_path):
c = async_run(controller.add_compute(compute_id="test1", connect=False))
c = await controller.add_compute(compute_id="test1", connect=False)
assert len(controller.computes) == 1 assert len(controller.computes) == 1
controller._notification = MagicMock() controller._notification = MagicMock()
c._connected = True c._connected = True
async_run(controller.delete_compute("test1")) await controller.delete_compute("test1")
assert len(controller.computes) == 0 assert len(controller.computes) == 0
controller._notification.controller_emit.assert_called_with("compute.deleted", c.__json__()) controller._notification.controller_emit.assert_called_with("compute.deleted", c.__json__())
with open(controller_config_path) as f: with open(controller_config_path) as f:
@ -151,25 +158,26 @@ def test_deleteCompute(controller, controller_config_path, async_run):
assert c.connected is False assert c.connected is False
def test_deleteComputeProjectOpened(controller, controller_config_path, async_run): async def test_deleteComputeProjectOpened(controller, controller_config_path):
""" """
When you delete a compute the project using it are close When you delete a compute the project using it are close
""" """
c = async_run(controller.add_compute(compute_id="test1", connect=False))
c = await controller.add_compute(compute_id="test1", connect=False)
c.post = AsyncioMagicMock() c.post = AsyncioMagicMock()
assert len(controller.computes) == 1 assert len(controller.computes) == 1
project1 = async_run(controller.add_project(name="Test1")) project1 = await controller.add_project(name="Test1")
async_run(project1.open()) await project1.open()
# We simulate that the project use this compute # We simulate that the project use this compute
project1._project_created_on_compute.add(c) project1._project_created_on_compute.add(c)
project2 = async_run(controller.add_project(name="Test2")) project2 = await controller.add_project(name="Test2")
async_run(project2.open()) await project2.open()
controller._notification = MagicMock() controller._notification = MagicMock()
c._connected = True c._connected = True
async_run(controller.delete_compute("test1")) await controller.delete_compute("test1")
assert len(controller.computes) == 0 assert len(controller.computes) == 0
controller._notification.controller_emit.assert_called_with("compute.deleted", c.__json__()) controller._notification.controller_emit.assert_called_with("compute.deleted", c.__json__())
with open(controller_config_path) as f: with open(controller_config_path) as f:
@ -182,8 +190,9 @@ def test_deleteComputeProjectOpened(controller, controller_config_path, async_ru
assert project2.status == "opened" assert project2.status == "opened"
def test_addComputeConfigFile(controller, controller_config_path, async_run): async def test_addComputeConfigFile(controller, controller_config_path):
async_run(controller.add_compute(compute_id="test1", name="Test", connect=False))
await controller.add_compute(compute_id="test1", name="Test", connect=False)
assert len(controller.computes) == 1 assert len(controller.computes) == 1
with open(controller_config_path) as f: with open(controller_config_path) as f:
data = json.load(f) data = json.load(f)
@ -200,170 +209,180 @@ def test_addComputeConfigFile(controller, controller_config_path, async_run):
] ]
def test_getCompute(controller, async_run): async def test_getCompute(controller):
compute = async_run(controller.add_compute(compute_id="test1", connect=False))
compute = await controller.add_compute(compute_id="test1", connect=False)
assert controller.get_compute("test1") == compute assert controller.get_compute("test1") == compute
with pytest.raises(aiohttp.web.HTTPNotFound): with pytest.raises(aiohttp.web.HTTPNotFound):
assert controller.get_compute("dsdssd") assert controller.get_compute("dsdssd")
def test_has_compute(controller, async_run): async def test_has_compute(controller):
compute = async_run(controller.add_compute(compute_id="test1", connect=False))
await controller.add_compute(compute_id="test1", connect=False)
assert controller.has_compute("test1") assert controller.has_compute("test1")
assert not controller.has_compute("test2") assert not controller.has_compute("test2")
def test_add_project(controller, async_run): async def test_add_project(controller):
uuid1 = str(uuid.uuid4()) uuid1 = str(uuid.uuid4())
uuid2 = str(uuid.uuid4()) uuid2 = str(uuid.uuid4())
await controller.add_project(project_id=uuid1, name="Test")
async_run(controller.add_project(project_id=uuid1, name="Test"))
assert len(controller.projects) == 1 assert len(controller.projects) == 1
async_run(controller.add_project(project_id=uuid1, name="Test")) await controller.add_project(project_id=uuid1, name="Test")
assert len(controller.projects) == 1 assert len(controller.projects) == 1
async_run(controller.add_project(project_id=uuid2, name="Test 2")) await controller.add_project(project_id=uuid2, name="Test 2")
assert len(controller.projects) == 2 assert len(controller.projects) == 2
def test_addDuplicateProject(controller, async_run): async def test_addDuplicateProject(controller):
uuid1 = str(uuid.uuid4()) uuid1 = str(uuid.uuid4())
uuid2 = str(uuid.uuid4()) uuid2 = str(uuid.uuid4())
await controller.add_project(project_id=uuid1, name="Test")
async_run(controller.add_project(project_id=uuid1, name="Test"))
assert len(controller.projects) == 1 assert len(controller.projects) == 1
with pytest.raises(aiohttp.web.HTTPConflict): with pytest.raises(aiohttp.web.HTTPConflict):
async_run(controller.add_project(project_id=uuid2, name="Test")) await controller.add_project(project_id=uuid2, name="Test")
def test_remove_project(controller, async_run): async def test_remove_project(controller):
uuid1 = str(uuid.uuid4())
project1 = async_run(controller.add_project(project_id=uuid1, name="Test")) uuid1 = str(uuid.uuid4())
project1 = await controller.add_project(project_id=uuid1, name="Test")
assert len(controller.projects) == 1 assert len(controller.projects) == 1
controller.remove_project(project1) controller.remove_project(project1)
assert len(controller.projects) == 0 assert len(controller.projects) == 0
def test_addProject_with_compute(controller, async_run): async def test_addProject_with_compute(controller):
uuid1 = str(uuid.uuid4())
uuid1 = str(uuid.uuid4())
compute = Compute("test1", controller=MagicMock()) compute = Compute("test1", controller=MagicMock())
compute.post = MagicMock() compute.post = MagicMock()
controller._computes = {"test1": compute} controller._computes = {"test1": compute}
await controller.add_project(project_id=uuid1, name="Test")
project1 = async_run(controller.add_project(project_id=uuid1, name="Test"))
async def test_getProject(controller):
def test_getProject(controller, async_run):
uuid1 = str(uuid.uuid4()) uuid1 = str(uuid.uuid4())
project = await controller.add_project(project_id=uuid1, name="Test")
project = async_run(controller.add_project(project_id=uuid1, name="Test"))
assert controller.get_project(uuid1) == project assert controller.get_project(uuid1) == project
with pytest.raises(aiohttp.web.HTTPNotFound): with pytest.raises(aiohttp.web.HTTPNotFound):
assert controller.get_project("dsdssd") assert controller.get_project("dsdssd")
def test_start(controller, async_run): async def test_start(controller):
controller.gns3vm.settings = { controller.gns3vm.settings = {
"enable": False, "enable": False,
"engine": "vmware", "engine": "vmware",
"vmname": "GNS3 VM" "vmname": "GNS3 VM"
} }
with asyncio_patch("gns3server.controller.compute.Compute.connect") as mock: with asyncio_patch("gns3server.controller.compute.Compute.connect") as mock:
async_run(controller.start()) await controller.start()
assert mock.called
assert len(controller.computes) == 1 # Local compute is created assert len(controller.computes) == 1 # Local compute is created
assert controller.computes["local"].name == socket.gethostname() assert controller.computes["local"].name == socket.gethostname()
def test_start_vm(controller, async_run): async def test_start_vm(controller):
""" """
Start the controller with a GNS3 VM Start the controller with a GNS3 VM
""" """
controller.gns3vm.settings = { controller.gns3vm.settings = {
"enable": True, "enable": True,
"engine": "vmware", "engine": "vmware",
"vmname": "GNS3 VM" "vmname": "GNS3 VM"
} }
with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.start") as mock: with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.start") as mock:
with asyncio_patch("gns3server.controller.gns3vm.GNS3VM._check_network") as mock_check_network: with asyncio_patch("gns3server.controller.gns3vm.GNS3VM._check_network"):
with asyncio_patch("gns3server.controller.compute.Compute.connect") as mock_connect: with asyncio_patch("gns3server.controller.compute.Compute.connect"):
async_run(controller.start()) await controller.start()
assert mock.called assert mock.called
assert "local" in controller.computes assert "local" in controller.computes
assert "vm" in controller.computes assert "vm" in controller.computes
assert len(controller.computes) == 2 # Local compute and vm are created assert len(controller.computes) == 2 # Local compute and vm are created
def test_stop(controller, async_run): async def test_stop(controller):
c = async_run(controller.add_compute(compute_id="test1", connect=False))
c = await controller.add_compute(compute_id="test1", connect=False)
c._connected = True c._connected = True
async_run(controller.stop()) await controller.stop()
assert c.connected is False assert c.connected is False
def test_stop_vm(controller, async_run): async def test_stop_vm(controller):
""" """
Stop GNS3 VM if configured Stop GNS3 VM if configured
""" """
controller.gns3vm.settings = { controller.gns3vm.settings = {
"enable": True, "enable": True,
"engine": "vmware", "engine": "vmware",
"when_exit": "stop", "when_exit": "stop",
"vmname": "GNS3 VM" "vmname": "GNS3 VM"
} }
controller.gns3vm.current_engine().running = True controller.gns3vm.current_engine().running = True
with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.stop") as mock: with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.stop") as mock:
async_run(controller.stop()) await controller.stop()
assert mock.called assert mock.called
def test_suspend_vm(controller, async_run): async def test_suspend_vm(controller):
""" """
Suspend GNS3 VM if configured Suspend GNS3 VM if configured
""" """
controller.gns3vm.settings = { controller.gns3vm.settings = {
"enable": True, "enable": True,
"engine": "vmware", "engine": "vmware",
"when_exit": "suspend", "when_exit": "suspend",
"vmname": "GNS3 VM" "vmname": "GNS3 VM"
} }
controller.gns3vm.current_engine().running = True controller.gns3vm.current_engine().running = True
with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.suspend") as mock: with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.suspend") as mock:
async_run(controller.stop()) await controller.stop()
assert mock.called assert mock.called
def test_keep_vm(controller, async_run): async def test_keep_vm(controller):
""" """
Keep GNS3 VM if configured Keep GNS3 VM if configured
""" """
controller.gns3vm.settings = { controller.gns3vm.settings = {
"enable": True, "enable": True,
"engine": "vmware", "engine": "vmware",
"when_exit": "keep", "when_exit": "keep",
"vmname": "GNS3 VM" "vmname": "GNS3 VM"
} }
controller.gns3vm.current_engine().running = True controller.gns3vm.current_engine().running = True
with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.suspend") as mock: with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.suspend") as mock:
async_run(controller.stop()) await controller.stop()
assert not mock.called assert not mock.called
def test_get_free_project_name(controller, async_run): async def test_get_free_project_name(controller):
async_run(controller.add_project(project_id=str(uuid.uuid4()), name="Test")) await controller.add_project(project_id=str(uuid.uuid4()), name="Test")
assert controller.get_free_project_name("Test") == "Test-1" assert controller.get_free_project_name("Test") == "Test-1"
async_run(controller.add_project(project_id=str(uuid.uuid4()), name="Test-1")) await controller.add_project(project_id=str(uuid.uuid4()), name="Test-1")
assert controller.get_free_project_name("Test") == "Test-2" assert controller.get_free_project_name("Test") == "Test-2"
assert controller.get_free_project_name("Hello") == "Hello" assert controller.get_free_project_name("Hello") == "Hello"
def test_load_base_files(controller, config, tmpdir): async def test_load_base_files(controller, config, tmpdir):
config.set_section_config("Server", {"configs_path": str(tmpdir)})
config.set_section_config("Server", {"configs_path": str(tmpdir)})
with open(str(tmpdir / 'iou_l2_base_startup-config.txt'), 'w+') as f: with open(str(tmpdir / 'iou_l2_base_startup-config.txt'), 'w+') as f:
f.write('test') f.write('test')
@ -375,7 +394,8 @@ def test_load_base_files(controller, config, tmpdir):
assert f.read() == 'test' assert f.read() == 'test'
def test_appliances(controller, async_run, tmpdir): def test_appliances(controller, tmpdir):
my_appliance = { my_appliance = {
"name": "My Appliance", "name": "My Appliance",
"status": "stable" "status": "stable"
@ -406,6 +426,7 @@ def test_appliances(controller, async_run, tmpdir):
def test_load_templates(controller): def test_load_templates(controller):
controller._settings = {} controller._settings = {}
controller.template_manager.load_templates() controller.template_manager.load_templates()
@ -430,10 +451,11 @@ def test_load_templates(controller):
assert cloud_uuid == template.id assert cloud_uuid == template.id
def test_autoidlepc(controller, async_run): async def test_autoidlepc(controller):
controller._computes["local"] = AsyncioMagicMock() controller._computes["local"] = AsyncioMagicMock()
node_mock = AsyncioMagicMock() node_mock = AsyncioMagicMock()
with asyncio_patch("gns3server.controller.Project.add_node", return_value=node_mock): with asyncio_patch("gns3server.controller.Project.add_node", return_value=node_mock):
async_run(controller.autoidlepc("local", "c7200", "test.bin", 512)) await controller.autoidlepc("local", "c7200", "test.bin", 512)
assert node_mock.dynamips_auto_idlepc.called assert node_mock.dynamips_auto_idlepc.called
assert len(controller.projects) == 0 assert len(controller.projects) == 0

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2016 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -22,33 +22,30 @@ import os
from tests.utils import AsyncioMagicMock from tests.utils import AsyncioMagicMock
from gns3server.controller.drawing import Drawing from gns3server.controller.drawing import Drawing
from gns3server.controller.project import Project
@pytest.fixture
def project(controller, async_run):
return async_run(controller.add_project(name="Test"))
@pytest.fixture @pytest.fixture
def drawing(project): def drawing(project):
return Drawing(project, None, svg="<svg></svg>") return Drawing(project, None, svg="<svg></svg>")
def test_init_without_uuid(project): def test_init_without_uuid(project):
drawing = Drawing(project, None, svg="<svg></svg>") drawing = Drawing(project, None, svg="<svg></svg>")
assert drawing.id is not None assert drawing.id is not None
def test_init_with_uuid(project): def test_init_with_uuid(project):
id = str(uuid.uuid4()) id = str(uuid.uuid4())
drawing = Drawing(project, id, svg="<svg></svg>") drawing = Drawing(project, id, svg="<svg></svg>")
assert drawing.id == id assert drawing.id == id
def test_json(project): def test_json(project):
i = Drawing(project, None, svg="<svg></svg>") i = Drawing(project, None, svg="<svg></svg>")
assert i.__json__() == { assert i.__json__() == {
"drawing_id": i.id, "drawing_id": i.id,
@ -71,11 +68,12 @@ def test_json(project):
} }
def test_update(drawing, project, async_run, controller): async def test_update(drawing, project, controller):
controller._notification = AsyncioMagicMock() controller._notification = AsyncioMagicMock()
project.dump = MagicMock() project.dump = MagicMock()
async_run(drawing.update(x=42, svg="<svg><rect></rect></svg>")) await drawing.update(x=42, svg="<svg><rect></rect></svg>")
assert drawing.x == 42 assert drawing.x == 42
args, kwargs = controller._notification.project_emit.call_args args, kwargs = controller._notification.project_emit.call_args
assert args[0] == "drawing.updated" assert args[0] == "drawing.updated"
@ -83,7 +81,7 @@ def test_update(drawing, project, async_run, controller):
assert args[1]["x"] == 42 assert args[1]["x"] == 42
assert args[1]["svg"] == "<svg><rect></rect></svg>" assert args[1]["svg"] == "<svg><rect></rect></svg>"
async_run(drawing.update(x=12, svg="<svg><rect></rect></svg>")) await drawing.update(x=12, svg="<svg><rect></rect></svg>")
assert drawing.x == 12 assert drawing.x == 12
args, kwargs = controller._notification.project_emit.call_args args, kwargs = controller._notification.project_emit.call_args
assert args[0] == "drawing.updated" assert args[0] == "drawing.updated"
@ -99,6 +97,7 @@ def test_image_base64(project):
""" """
If image are embed as base 64 we need to dump them on disk If image are embed as base 64 we need to dump them on disk
""" """
svg = "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" height=\"128\" width=\"128\">\n<image height=\"128\" width=\"128\" xlink:href=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAN2AAADdgF91YLMAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAHm5JREFUeJztnXl8FGXSx7/VIYAKKCoe4IEIq6ALuQnI4YXXKihulCSACIIsu4qKNyqXByqK67EeiMtyJGrwXnfABSu/L3I31aclPub3oBIQ/YD2zdUBZ+T37l7Dt0OAKoIYcUf07mBhpkieonJe+FcAUOgeX8dL/4ysTCSmTiwwsx1FPLJfuas89mXsByu/N/BR43E09+xMafDrYFI6wmzINu7QreFo1kD4EQIW/m8ICm1iAdBXWp0wuusiJp+Q7ilok3VE02RR+MoWPTYMXTYNlarAx6c6iQU7X/RbQ4DZA2m1F44CnrdYjDPG8ZcLBe/1kzz2Z9ybfUZQALAS\" />\n</svg>" svg = "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" height=\"128\" width=\"128\">\n<image height=\"128\" width=\"128\" xlink:href=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAN2AAADdgF91YLMAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAHm5JREFUeJztnXl8FGXSx7/VIYAKKCoe4IEIq6ALuQnI4YXXKihulCSACIIsu4qKNyqXByqK67EeiMtyJGrwXnfABSu/L3I31aclPub3oBIQ/YD2zdUBZ+T37l7Dt0OAKoIYcUf07mBhpkieonJe+FcAUOgeX8dL/4ysTCSmTiwwsx1FPLJfuas89mXsByu/N/BR43E09+xMafDrYFI6wmzINu7QreFo1kD4EQIW/m8ICm1iAdBXWp0wuusiJp+Q7ilok3VE02RR+MoWPTYMXTYNlarAx6c6iQU7X/RbQ4DZA2m1F44CnrdYjDPG8ZcLBe/1kzz2Z9ybfUZQALAS\" />\n</svg>"
drawing = Drawing(project, None, svg=svg) drawing = Drawing(project, None, svg=svg)
@ -112,6 +111,7 @@ def test_image_svg(project):
""" """
Large SVG are dump on disk Large SVG are dump on disk
""" """
svg = "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" height=\"128\" width=\"128\">\n" svg = "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" height=\"128\" width=\"128\">\n"
for i in range(0, 1000): for i in range(0, 1000):

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2016 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -33,14 +33,16 @@ from gns3server.utils.asyncio import aiozipstream
@pytest.fixture @pytest.fixture
def project(controller): async def project(loop, controller):
p = Project(controller=controller, name="test") p = Project(controller=controller, name="test")
p.dump = MagicMock() p.dump = MagicMock()
return p return p
@pytest.fixture @pytest.fixture
def node(controller, project, async_run): async def node(controller, project):
compute = MagicMock() compute = MagicMock()
compute.id = "local" compute.id = "local"
@ -48,7 +50,7 @@ def node(controller, project, async_run):
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
node = async_run(project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"})) node = await project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"})
return node return node
@ -60,6 +62,7 @@ async def write_file(path, z):
def test_exportable_files(): def test_exportable_files():
assert _is_exportable("hello/world") assert _is_exportable("hello/world")
assert not _is_exportable("project-files/tmp") assert not _is_exportable("project-files/tmp")
assert not _is_exportable("project-files/test_log.txt") assert not _is_exportable("project-files/test_log.txt")
@ -69,7 +72,8 @@ def test_exportable_files():
assert not _is_exportable("test/project-files/snapshots/test.gns3p") assert not _is_exportable("test/project-files/snapshots/test.gns3p")
def test_export(tmpdir, project, async_run): async def test_export(tmpdir, project):
path = project.path path = project.path
os.makedirs(os.path.join(path, "vm-1", "dynamips")) os.makedirs(os.path.join(path, "vm-1", "dynamips"))
@ -113,8 +117,8 @@ def test_export(tmpdir, project, async_run):
with aiozipstream.ZipFile() as z: with aiozipstream.ZipFile() as z:
with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir / "IOS"),): with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir / "IOS"),):
async_run(export_project(z, project, str(tmpdir), include_images=False)) await export_project(z, project, str(tmpdir), include_images=False)
async_run(write_file(str(tmpdir / 'zipfile.zip'), z)) await write_file(str(tmpdir / 'zipfile.zip'), z)
with zipfile.ZipFile(str(tmpdir / 'zipfile.zip')) as myzip: with zipfile.ZipFile(str(tmpdir / 'zipfile.zip')) as myzip:
with myzip.open("vm-1/dynamips/test") as myfile: with myzip.open("vm-1/dynamips/test") as myfile:
@ -134,7 +138,7 @@ def test_export(tmpdir, project, async_run):
assert topo["computes"] == [] assert topo["computes"] == []
def test_export_vm(tmpdir, project, async_run): async def test_export_vm(tmpdir, project):
""" """
If data is on a remote server export it locally before If data is on a remote server export it locally before
sending it in the archive. sending it in the archive.
@ -147,7 +151,7 @@ def test_export_vm(tmpdir, project, async_run):
# Fake file that will be download from the vm # Fake file that will be download from the vm
mock_response = AsyncioMagicMock() mock_response = AsyncioMagicMock()
mock_response.content = AsyncioBytesIO() mock_response.content = AsyncioBytesIO()
async_run(mock_response.content.write(b"HELLO")) await mock_response.content.write(b"HELLO")
mock_response.content.seek(0) mock_response.content.seek(0)
compute.download_file = AsyncioMagicMock(return_value=mock_response) compute.download_file = AsyncioMagicMock(return_value=mock_response)
@ -161,9 +165,9 @@ def test_export_vm(tmpdir, project, async_run):
f.write("{}") f.write("{}")
with aiozipstream.ZipFile() as z: with aiozipstream.ZipFile() as z:
async_run(export_project(z, project, str(tmpdir))) await export_project(z, project, str(tmpdir))
assert compute.list_files.called assert compute.list_files.called
async_run(write_file(str(tmpdir / 'zipfile.zip'), z)) await write_file(str(tmpdir / 'zipfile.zip'), z)
with zipfile.ZipFile(str(tmpdir / 'zipfile.zip')) as myzip: with zipfile.ZipFile(str(tmpdir / 'zipfile.zip')) as myzip:
with myzip.open("vm-1/dynamips/test") as myfile: with myzip.open("vm-1/dynamips/test") as myfile:
@ -171,7 +175,7 @@ def test_export_vm(tmpdir, project, async_run):
assert content == b"HELLO" assert content == b"HELLO"
def test_export_disallow_running(tmpdir, project, node, async_run): async def test_export_disallow_running(tmpdir, project, node):
""" """
Disallow export when a node is running Disallow export when a node is running
""" """
@ -194,10 +198,10 @@ def test_export_disallow_running(tmpdir, project, node, async_run):
node._status = "started" node._status = "started"
with pytest.raises(aiohttp.web.HTTPConflict): with pytest.raises(aiohttp.web.HTTPConflict):
with aiozipstream.ZipFile() as z: with aiozipstream.ZipFile() as z:
async_run(export_project(z, project, str(tmpdir))) await export_project(z, project, str(tmpdir))
def test_export_disallow_some_type(tmpdir, project, async_run): async def test_export_disallow_some_type(tmpdir, project):
""" """
Disallow export for some node type Disallow export for some node type
""" """
@ -219,9 +223,9 @@ def test_export_disallow_some_type(tmpdir, project, async_run):
with pytest.raises(aiohttp.web.HTTPConflict): with pytest.raises(aiohttp.web.HTTPConflict):
with aiozipstream.ZipFile() as z: with aiozipstream.ZipFile() as z:
async_run(export_project(z, project, str(tmpdir))) await export_project(z, project, str(tmpdir))
with aiozipstream.ZipFile() as z: with aiozipstream.ZipFile() as z:
async_run(export_project(z, project, str(tmpdir), allow_all_nodes=True)) await export_project(z, project, str(tmpdir), allow_all_nodes=True)
# VirtualBox is always disallowed # VirtualBox is always disallowed
topology = { topology = {
@ -240,10 +244,10 @@ def test_export_disallow_some_type(tmpdir, project, async_run):
json.dump(topology, f) json.dump(topology, f)
with pytest.raises(aiohttp.web.HTTPConflict): with pytest.raises(aiohttp.web.HTTPConflict):
with aiozipstream.ZipFile() as z: with aiozipstream.ZipFile() as z:
async_run(export_project(z, project, str(tmpdir), allow_all_nodes=True)) await export_project(z, project, str(tmpdir), allow_all_nodes=True)
def test_export_fix_path(tmpdir, project, async_run): async def test_export_fix_path(tmpdir, project):
""" """
Fix absolute image path, except for Docker Fix absolute image path, except for Docker
""" """
@ -273,8 +277,8 @@ def test_export_fix_path(tmpdir, project, async_run):
json.dump(topology, f) json.dump(topology, f)
with aiozipstream.ZipFile() as z: with aiozipstream.ZipFile() as z:
async_run(export_project(z, project, str(tmpdir))) await export_project(z, project, str(tmpdir))
async_run(write_file(str(tmpdir / 'zipfile.zip'), z)) await write_file(str(tmpdir / 'zipfile.zip'), z)
with zipfile.ZipFile(str(tmpdir / 'zipfile.zip')) as myzip: with zipfile.ZipFile(str(tmpdir / 'zipfile.zip')) as myzip:
with myzip.open("project.gns3") as myfile: with myzip.open("project.gns3") as myfile:
@ -284,7 +288,7 @@ def test_export_fix_path(tmpdir, project, async_run):
assert topology["topology"]["nodes"][1]["properties"]["image"] == "gns3/webterm:lastest" assert topology["topology"]["nodes"][1]["properties"]["image"] == "gns3/webterm:lastest"
def test_export_with_images(tmpdir, project, async_run): async def test_export_with_images(tmpdir, project):
""" """
Fix absolute image path Fix absolute image path
""" """
@ -312,14 +316,14 @@ def test_export_with_images(tmpdir, project, async_run):
with aiozipstream.ZipFile() as z: with aiozipstream.ZipFile() as z:
with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir / "IOS"),): with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir / "IOS"),):
async_run(export_project(z, project, str(tmpdir), include_images=True)) await export_project(z, project, str(tmpdir), include_images=True)
async_run(write_file(str(tmpdir / 'zipfile.zip'), z)) await write_file(str(tmpdir / 'zipfile.zip'), z)
with zipfile.ZipFile(str(tmpdir / 'zipfile.zip')) as myzip: with zipfile.ZipFile(str(tmpdir / 'zipfile.zip')) as myzip:
myzip.getinfo("images/IOS/test.image") myzip.getinfo("images/IOS/test.image")
def test_export_keep_compute_id(tmpdir, project, async_run): async def test_export_keep_compute_id(tmpdir, project):
""" """
If we want to restore the same computes we could ask to keep them If we want to restore the same computes we could ask to keep them
in the file in the file
@ -348,8 +352,8 @@ def test_export_keep_compute_id(tmpdir, project, async_run):
json.dump(data, f) json.dump(data, f)
with aiozipstream.ZipFile() as z: with aiozipstream.ZipFile() as z:
async_run(export_project(z, project, str(tmpdir), keep_compute_id=True)) await export_project(z, project, str(tmpdir), keep_compute_id=True)
async_run(write_file(str(tmpdir / 'zipfile.zip'), z)) await write_file(str(tmpdir / 'zipfile.zip'), z)
with zipfile.ZipFile(str(tmpdir / 'zipfile.zip')) as myzip: with zipfile.ZipFile(str(tmpdir / 'zipfile.zip')) as myzip:
with myzip.open("project.gns3") as myfile: with myzip.open("project.gns3") as myfile:
@ -358,7 +362,7 @@ def test_export_keep_compute_id(tmpdir, project, async_run):
assert len(topo["computes"]) == 1 assert len(topo["computes"]) == 1
def test_export_images_from_vm(tmpdir, project, async_run): async def test_export_images_from_vm(tmpdir, project):
""" """
If data is on a remote server export it locally before If data is on a remote server export it locally before
sending it in the archive. sending it in the archive.
@ -366,22 +370,19 @@ def test_export_images_from_vm(tmpdir, project, async_run):
compute = MagicMock() compute = MagicMock()
compute.id = "vm" compute.id = "vm"
compute.list_files = AsyncioMagicMock(return_value=[ compute.list_files = AsyncioMagicMock(return_value=[{"path": "vm-1/dynamips/test"}])
{"path": "vm-1/dynamips/test"}
])
# Fake file that will be download from the vm # Fake file that will be download from the vm
mock_response = AsyncioMagicMock() mock_response = AsyncioMagicMock()
mock_response.content = AsyncioBytesIO() mock_response.content = AsyncioBytesIO()
async_run(mock_response.content.write(b"HELLO")) await mock_response.content.write(b"HELLO")
mock_response.content.seek(0) mock_response.content.seek(0)
mock_response.status = 200 mock_response.status = 200
compute.download_file = AsyncioMagicMock(return_value=mock_response) compute.download_file = AsyncioMagicMock(return_value=mock_response)
mock_response = AsyncioMagicMock() mock_response = AsyncioMagicMock()
mock_response.content = AsyncioBytesIO() mock_response.content = AsyncioBytesIO()
async_run(mock_response.content.write(b"IMAGE")) await mock_response.content.write(b"IMAGE")
mock_response.content.seek(0) mock_response.content.seek(0)
mock_response.status = 200 mock_response.status = 200
compute.download_image = AsyncioMagicMock(return_value=mock_response) compute.download_image = AsyncioMagicMock(return_value=mock_response)
@ -411,9 +412,9 @@ def test_export_images_from_vm(tmpdir, project, async_run):
f.write(json.dumps(topology)) f.write(json.dumps(topology))
with aiozipstream.ZipFile() as z: with aiozipstream.ZipFile() as z:
async_run(export_project(z, project, str(tmpdir), include_images=True)) await export_project(z, project, str(tmpdir), include_images=True)
assert compute.list_files.called assert compute.list_files.called
async_run(write_file(str(tmpdir / 'zipfile.zip'), z)) await write_file(str(tmpdir / 'zipfile.zip'), z)
with zipfile.ZipFile(str(tmpdir / 'zipfile.zip')) as myzip: with zipfile.ZipFile(str(tmpdir / 'zipfile.zip')) as myzip:
with myzip.open("vm-1/dynamips/test") as myfile: with myzip.open("vm-1/dynamips/test") as myfile:
@ -425,7 +426,8 @@ def test_export_images_from_vm(tmpdir, project, async_run):
assert content == b"IMAGE" assert content == b"IMAGE"
def test_export_with_ignoring_snapshots(tmpdir, project, async_run): async def test_export_with_ignoring_snapshots(tmpdir, project):
with open(os.path.join(project.path, "test.gns3"), 'w+') as f: with open(os.path.join(project.path, "test.gns3"), 'w+') as f:
data = { data = {
"topology": { "topology": {
@ -454,8 +456,8 @@ def test_export_with_ignoring_snapshots(tmpdir, project, async_run):
Path(os.path.join(snapshots_dir, 'snap.gns3project')).touch() Path(os.path.join(snapshots_dir, 'snap.gns3project')).touch()
with aiozipstream.ZipFile() as z: with aiozipstream.ZipFile() as z:
async_run(export_project(z, project, str(tmpdir), keep_compute_id=True)) await export_project(z, project, str(tmpdir), keep_compute_id=True)
async_run(write_file(str(tmpdir / 'zipfile.zip'), z)) await write_file(str(tmpdir / 'zipfile.zip'), z)
with zipfile.ZipFile(str(tmpdir / 'zipfile.zip')) as myzip: with zipfile.ZipFile(str(tmpdir / 'zipfile.zip')) as myzip:
assert not os.path.join('snapshots', 'snap.gns3project') in [f.filename for f in myzip.filelist] assert not os.path.join('snapshots', 'snap.gns3project') in [f.filename for f in myzip.filelist]

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2016 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -24,6 +24,7 @@ from gns3server.controller.gns3vm.gns3_vm_error import GNS3VMError
@pytest.fixture @pytest.fixture
def dummy_engine(): def dummy_engine():
engine = AsyncioMagicMock() engine = AsyncioMagicMock()
engine.running = False engine.running = False
engine.ip_address = "vm.local" engine.ip_address = "vm.local"
@ -36,6 +37,7 @@ def dummy_engine():
@pytest.fixture @pytest.fixture
def dummy_gns3vm(controller, dummy_engine): def dummy_gns3vm(controller, dummy_engine):
vm = GNS3VM(controller) vm = GNS3VM(controller)
vm._settings["engine"] = "dummy" vm._settings["engine"] = "dummy"
vm._settings["vmname"] = "Test VM" vm._settings["vmname"] = "Test VM"
@ -44,46 +46,49 @@ def dummy_gns3vm(controller, dummy_engine):
return vm return vm
def test_list(async_run, controller): async def test_list(controller):
vm = GNS3VM(controller)
vm = GNS3VM(controller)
with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.list", return_value=[{"vmname": "test", "vmx_path": "test"}]): with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.list", return_value=[{"vmname": "test", "vmx_path": "test"}]):
res = async_run(vm.list("vmware")) res = await vm.list("vmware")
assert res == [{"vmname": "test"}] # Informations specific to vmware are stripped assert res == [{"vmname": "test"}] # Information specific to VMware is stripped
with asyncio_patch("gns3server.controller.gns3vm.virtualbox_gns3_vm.VirtualBoxGNS3VM.list", return_value=[{"vmname": "test"}]): with asyncio_patch("gns3server.controller.gns3vm.virtualbox_gns3_vm.VirtualBoxGNS3VM.list", return_value=[{"vmname": "test"}]):
res = async_run(vm.list("virtualbox")) res = await vm.list("virtualbox")
assert res == [{"vmname": "test"}] assert res == [{"vmname": "test"}]
with pytest.raises(NotImplementedError): with pytest.raises(NotImplementedError):
async_run(vm.list("hyperv")) await vm.list("hyperv")
def test_json(controller): def test_json(controller):
vm = GNS3VM(controller) vm = GNS3VM(controller)
assert vm.__json__() == vm._settings assert vm.__json__() == vm._settings
def test_update_settings(controller, async_run): async def test_update_settings(controller):
vm = GNS3VM(controller) vm = GNS3VM(controller)
vm.settings = { vm.settings = {
"enable": True, "enable": True,
"engine": "vmware", "engine": "vmware",
"vmname": "GNS3 VM" "vmname": "GNS3 VM"
} }
with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.start"): with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.start"):
with asyncio_patch("gns3server.controller.gns3vm.GNS3VM._check_network") as mock_check_network: with asyncio_patch("gns3server.controller.gns3vm.GNS3VM._check_network"):
async_run(vm.auto_start_vm()) await vm.auto_start_vm()
assert "vm" in controller.computes assert "vm" in controller.computes
async_run(vm.update_settings({"enable": False})) await vm.update_settings({"enable": False})
assert "vm" not in controller.computes assert "vm" not in controller.computes
def test_auto_start(async_run, controller, dummy_gns3vm, dummy_engine): async def test_auto_start(controller, dummy_gns3vm, dummy_engine):
""" """
When start the compute should be add to the controller When start the compute should be add to the controller
""" """
with asyncio_patch("gns3server.controller.gns3vm.GNS3VM._check_network") as mock_check_network: with asyncio_patch("gns3server.controller.gns3vm.GNS3VM._check_network"):
async_run(dummy_gns3vm.auto_start_vm()) await dummy_gns3vm.auto_start_vm()
assert dummy_engine.start.called assert dummy_engine.start.called
assert controller.computes["vm"].name == "GNS3 VM (Test VM)" assert controller.computes["vm"].name == "GNS3 VM (Test VM)"
assert controller.computes["vm"].host == "vm.local" assert controller.computes["vm"].host == "vm.local"
@ -93,9 +98,9 @@ def test_auto_start(async_run, controller, dummy_gns3vm, dummy_engine):
assert controller.computes["vm"].password == "world" assert controller.computes["vm"].password == "world"
def test_auto_start_with_error(async_run, controller, dummy_gns3vm, dummy_engine): async def test_auto_start_with_error(controller, dummy_gns3vm, dummy_engine):
dummy_engine.start.side_effect = GNS3VMError("Dummy error")
async_run(dummy_gns3vm.auto_start_vm()) dummy_engine.start.side_effect = GNS3VMError("Dummy error")
await dummy_gns3vm.auto_start_vm()
assert dummy_engine.start.called assert dummy_engine.start.called
assert controller.computes["vm"].name == "GNS3 VM (Test VM)" assert controller.computes["vm"].name == "GNS3 VM (Test VM)"

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2016 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -20,18 +20,15 @@ import uuid
import json import json
import zipfile import zipfile
from tests.utils import asyncio_patch, AsyncioMagicMock from tests.utils import asyncio_patch, AsyncioMagicMock
from gns3server.controller.project import Project
from gns3server.controller.import_project import import_project, _move_files_to_compute from gns3server.controller.import_project import import_project, _move_files_to_compute
from gns3server.version import __version__ from gns3server.version import __version__
def test_import_project(async_run, tmpdir, controller): async def test_import_project(tmpdir, controller):
project_id = str(uuid.uuid4())
project_id = str(uuid.uuid4())
topology = { topology = {
"project_id": str(uuid.uuid4()), "project_id": str(uuid.uuid4()),
"name": "test", "name": "test",
@ -55,7 +52,7 @@ def test_import_project(async_run, tmpdir, controller):
myzip.write(str(tmpdir / "b.png"), "project-files/qemu/test") myzip.write(str(tmpdir / "b.png"), "project-files/qemu/test")
with open(zip_path, "rb") as f: with open(zip_path, "rb") as f:
project = async_run(import_project(controller, project_id, f)) project = await import_project(controller, project_id, f)
assert project.name == "test" assert project.name == "test"
assert project.id == project_id assert project.id == project_id
@ -68,19 +65,19 @@ def test_import_project(async_run, tmpdir, controller):
# A new project name is generated when you import twice the same name # A new project name is generated when you import twice the same name
with open(zip_path, "rb") as f: with open(zip_path, "rb") as f:
project = async_run(import_project(controller, str(uuid.uuid4()), f)) project = await import_project(controller, str(uuid.uuid4()), f)
assert project.auto_open is False assert project.auto_open is False
assert project.auto_start is False assert project.auto_start is False
assert project.name != "test" assert project.name != "test"
def test_import_project_override(async_run, tmpdir, controller): async def test_import_project_override(tmpdir, controller):
""" """
In the case of snapshot we will import a project for In the case of snapshot we will import a project for
override the previous keeping the same project id & location override the previous keeping the same project id & location
""" """
project_id = str(uuid.uuid4())
project_id = str(uuid.uuid4())
topology = { topology = {
"project_id": project_id, "project_id": project_id,
"name": "test", "name": "test",
@ -97,24 +94,24 @@ def test_import_project_override(async_run, tmpdir, controller):
myzip.write(str(tmpdir / "project.gns3"), "project.gns3") myzip.write(str(tmpdir / "project.gns3"), "project.gns3")
with open(zip_path, "rb") as f: with open(zip_path, "rb") as f:
project = async_run(import_project(controller, project_id, f, location=str(tmpdir))) project = await import_project(controller, project_id, f, location=str(tmpdir))
assert project.name == "test" assert project.name == "test"
assert project.id == project_id assert project.id == project_id
# Overide the project with same project # Overide the project with same project
with open(zip_path, "rb") as f: with open(zip_path, "rb") as f:
project = async_run(import_project(controller, project_id, f, location=str(tmpdir))) project = await import_project(controller, project_id, f, location=str(tmpdir))
assert project.id == project_id assert project.id == project_id
assert project.name == "test" assert project.name == "test"
def test_import_upgrade(async_run, tmpdir, controller): async def test_import_upgrade(tmpdir, controller):
""" """
Topology made for previous GNS3 version are upgraded during the process Topology made for previous GNS3 version are upgraded during the process
""" """
project_id = str(uuid.uuid4())
project_id = str(uuid.uuid4())
topology = { topology = {
"project_id": str(uuid.uuid4()), "project_id": str(uuid.uuid4()),
"name": "test", "name": "test",
@ -131,17 +128,16 @@ def test_import_upgrade(async_run, tmpdir, controller):
myzip.write(str(tmpdir / "project.gns3"), "project.gns3") myzip.write(str(tmpdir / "project.gns3"), "project.gns3")
with open(zip_path, "rb") as f: with open(zip_path, "rb") as f:
project = async_run(import_project(controller, project_id, f)) project = await import_project(controller, project_id, f)
with open(os.path.join(project.path, "test.gns3")) as f: with open(os.path.join(project.path, "test.gns3")) as f:
topo = json.load(f) topo = json.load(f)
assert topo["version"] == __version__ assert topo["version"] == __version__
def test_import_with_images(tmpdir, async_run, controller): async def test_import_with_images(tmpdir, controller):
project_id = str(uuid.uuid4()) project_id = str(uuid.uuid4())
topology = { topology = {
"project_id": str(uuid.uuid4()), "project_id": str(uuid.uuid4()),
"name": "test", "name": "test",
@ -162,7 +158,7 @@ def test_import_with_images(tmpdir, async_run, controller):
myzip.write(str(tmpdir / "test.image"), "images/IOS/test.image") myzip.write(str(tmpdir / "test.image"), "images/IOS/test.image")
with open(zip_path, "rb") as f: with open(zip_path, "rb") as f:
project = async_run(import_project(controller, project_id, f)) project = await import_project(controller, project_id, f)
assert not os.path.exists(os.path.join(project.path, "images/IOS/test.image")) assert not os.path.exists(os.path.join(project.path, "images/IOS/test.image"))
@ -170,12 +166,12 @@ def test_import_with_images(tmpdir, async_run, controller):
assert os.path.exists(path), path assert os.path.exists(path), path
def test_import_iou_linux_no_vm(linux_platform, async_run, tmpdir, controller): async def test_import_iou_linux_no_vm(linux_platform, tmpdir, controller):
""" """
On non linux host IOU should be local if we don't have a GNS3 VM On non linux host IOU should be local if we don't have a GNS3 VM
""" """
project_id = str(uuid.uuid4())
project_id = str(uuid.uuid4())
controller._computes["local"] = AsyncioMagicMock() controller._computes["local"] = AsyncioMagicMock()
topology = { topology = {
@ -207,17 +203,18 @@ def test_import_iou_linux_no_vm(linux_platform, async_run, tmpdir, controller):
myzip.write(str(tmpdir / "project.gns3"), "project.gns3") myzip.write(str(tmpdir / "project.gns3"), "project.gns3")
with open(zip_path, "rb") as f: with open(zip_path, "rb") as f:
project = async_run(import_project(controller, project_id, f)) project = await import_project(controller, project_id, f)
with open(os.path.join(project.path, "test.gns3")) as f: with open(os.path.join(project.path, "test.gns3")) as f:
topo = json.load(f) topo = json.load(f)
assert topo["topology"]["nodes"][0]["compute_id"] == "local" assert topo["topology"]["nodes"][0]["compute_id"] == "local"
def test_import_iou_linux_with_vm(linux_platform, async_run, tmpdir, controller): async def test_import_iou_linux_with_vm(linux_platform, tmpdir, controller):
""" """
On non linux host IOU should be vm if we have a GNS3 VM configured On non linux host IOU should be vm if we have a GNS3 VM configured
""" """
project_id = str(uuid.uuid4()) project_id = str(uuid.uuid4())
controller._computes["vm"] = AsyncioMagicMock() controller._computes["vm"] = AsyncioMagicMock()
@ -251,17 +248,18 @@ def test_import_iou_linux_with_vm(linux_platform, async_run, tmpdir, controller)
myzip.write(str(tmpdir / "project.gns3"), "project.gns3") myzip.write(str(tmpdir / "project.gns3"), "project.gns3")
with open(zip_path, "rb") as f: with open(zip_path, "rb") as f:
project = async_run(import_project(controller, project_id, f)) project = await import_project(controller, project_id, f)
with open(os.path.join(project.path, "test.gns3")) as f: with open(os.path.join(project.path, "test.gns3")) as f:
topo = json.load(f) topo = json.load(f)
assert topo["topology"]["nodes"][0]["compute_id"] == "vm" assert topo["topology"]["nodes"][0]["compute_id"] == "vm"
def test_import_nat_non_linux(windows_platform, async_run, tmpdir, controller): async def test_import_nat_non_linux(windows_platform, tmpdir, controller):
""" """
On non linux host NAT should be moved to the GNS3 VM On non linux host NAT should be moved to the GNS3 VM
""" """
project_id = str(uuid.uuid4()) project_id = str(uuid.uuid4())
controller._computes["vm"] = AsyncioMagicMock() controller._computes["vm"] = AsyncioMagicMock()
@ -295,17 +293,18 @@ def test_import_nat_non_linux(windows_platform, async_run, tmpdir, controller):
myzip.write(str(tmpdir / "project.gns3"), "project.gns3") myzip.write(str(tmpdir / "project.gns3"), "project.gns3")
with open(zip_path, "rb") as f: with open(zip_path, "rb") as f:
project = async_run(import_project(controller, project_id, f)) project = await import_project(controller, project_id, f)
with open(os.path.join(project.path, "test.gns3")) as f: with open(os.path.join(project.path, "test.gns3")) as f:
topo = json.load(f) topo = json.load(f)
assert topo["topology"]["nodes"][0]["compute_id"] == "vm" assert topo["topology"]["nodes"][0]["compute_id"] == "vm"
def test_import_iou_non_linux(windows_platform, async_run, tmpdir, controller): async def test_import_iou_non_linux(windows_platform, tmpdir, controller):
""" """
On non linux host IOU should be moved to the GNS3 VM On non linux host IOU should be moved to the GNS3 VM
""" """
project_id = str(uuid.uuid4()) project_id = str(uuid.uuid4())
controller._computes["vm"] = AsyncioMagicMock() controller._computes["vm"] = AsyncioMagicMock()
@ -346,7 +345,7 @@ def test_import_iou_non_linux(windows_platform, async_run, tmpdir, controller):
with open(zip_path, "rb") as f: with open(zip_path, "rb") as f:
with asyncio_patch("gns3server.controller.import_project._move_files_to_compute") as mock: with asyncio_patch("gns3server.controller.import_project._move_files_to_compute") as mock:
project = async_run(import_project(controller, project_id, f)) project = await import_project(controller, project_id, f)
controller._computes["vm"].post.assert_called_with('/projects', data={'name': 'test', 'project_id': project_id}) controller._computes["vm"].post.assert_called_with('/projects', data={'name': 'test', 'project_id': project_id})
with open(os.path.join(project.path, "test.gns3")) as f: with open(os.path.join(project.path, "test.gns3")) as f:
@ -357,12 +356,12 @@ def test_import_iou_non_linux(windows_platform, async_run, tmpdir, controller):
mock.assert_called_with(controller._computes["vm"], project_id, project.path, os.path.join('project-files', 'iou', topo["topology"]["nodes"][0]['node_id'])) mock.assert_called_with(controller._computes["vm"], project_id, project.path, os.path.join('project-files', 'iou', topo["topology"]["nodes"][0]['node_id']))
def test_import_node_id(linux_platform, async_run, tmpdir, controller): async def test_import_node_id(linux_platform, tmpdir, controller):
""" """
When importing a node, node_id should change When importing a node, node_id should change
""" """
project_id = str(uuid.uuid4())
project_id = str(uuid.uuid4())
controller._computes["local"] = AsyncioMagicMock() controller._computes["local"] = AsyncioMagicMock()
topology = { topology = {
@ -429,7 +428,7 @@ def test_import_node_id(linux_platform, async_run, tmpdir, controller):
myzip.writestr("project-files/iou/c3ae286c-c81f-40d9-a2d0-5874b2f2478d/startup.cfg", "test") myzip.writestr("project-files/iou/c3ae286c-c81f-40d9-a2d0-5874b2f2478d/startup.cfg", "test")
with open(zip_path, "rb") as f: with open(zip_path, "rb") as f:
project = async_run(import_project(controller, project_id, f)) project = await import_project(controller, project_id, f)
with open(os.path.join(project.path, "test.gns3")) as f: with open(os.path.join(project.path, "test.gns3")) as f:
topo = json.load(f) topo = json.load(f)
@ -450,10 +449,11 @@ def test_import_node_id(linux_platform, async_run, tmpdir, controller):
assert os.path.exists(os.path.join(project.path, "project-files", "iou", topo["topology"]["nodes"][0]["node_id"], "startup.cfg")) assert os.path.exists(os.path.join(project.path, "project-files", "iou", topo["topology"]["nodes"][0]["node_id"], "startup.cfg"))
def test_import_keep_compute_id(windows_platform, async_run, tmpdir, controller): async def test_import_keep_compute_id(windows_platform, tmpdir, controller):
""" """
On linux host IOU should be moved to the GNS3 VM On linux host IOU should be moved to the GNS3 VM
""" """
project_id = str(uuid.uuid4()) project_id = str(uuid.uuid4())
controller._computes["vm"] = AsyncioMagicMock() controller._computes["vm"] = AsyncioMagicMock()
@ -487,14 +487,15 @@ def test_import_keep_compute_id(windows_platform, async_run, tmpdir, controller)
myzip.write(str(tmpdir / "project.gns3"), "project.gns3") myzip.write(str(tmpdir / "project.gns3"), "project.gns3")
with open(zip_path, "rb") as f: with open(zip_path, "rb") as f:
project = async_run(import_project(controller, project_id, f, keep_compute_id=True)) project = await import_project(controller, project_id, f, keep_compute_id=True)
with open(os.path.join(project.path, "test.gns3")) as f: with open(os.path.join(project.path, "test.gns3")) as f:
topo = json.load(f) topo = json.load(f)
assert topo["topology"]["nodes"][0]["compute_id"] == "local" assert topo["topology"]["nodes"][0]["compute_id"] == "local"
def test_move_files_to_compute(tmpdir, async_run): async def test_move_files_to_compute(tmpdir):
project_id = str(uuid.uuid4()) project_id = str(uuid.uuid4())
os.makedirs(str(tmpdir / "project-files" / "docker")) os.makedirs(str(tmpdir / "project-files" / "docker"))
@ -502,19 +503,19 @@ def test_move_files_to_compute(tmpdir, async_run):
(tmpdir / "project-files" / "docker" / "test2").open("w").close() (tmpdir / "project-files" / "docker" / "test2").open("w").close()
with asyncio_patch("gns3server.controller.import_project._upload_file") as mock: with asyncio_patch("gns3server.controller.import_project._upload_file") as mock:
async_run(_move_files_to_compute(None, project_id, str(tmpdir), os.path.join("project-files", "docker"))) await _move_files_to_compute(None, project_id, str(tmpdir), os.path.join("project-files", "docker"))
mock.assert_any_call(None, project_id, str(tmpdir / "project-files" / "docker" / "test"), os.path.join("project-files", "docker", "test")) mock.assert_any_call(None, project_id, str(tmpdir / "project-files" / "docker" / "test"), os.path.join("project-files", "docker", "test"))
mock.assert_any_call(None, project_id, str(tmpdir / "project-files" / "docker" / "test2"), os.path.join("project-files", "docker", "test2")) mock.assert_any_call(None, project_id, str(tmpdir / "project-files" / "docker" / "test2"), os.path.join("project-files", "docker", "test2"))
assert not os.path.exists(str(tmpdir / "project-files" / "docker")) assert not os.path.exists(str(tmpdir / "project-files" / "docker"))
def test_import_project_name_and_location(async_run, tmpdir, controller): async def test_import_project_name_and_location(tmpdir, controller):
""" """
Import a project with a different location and name Import a project with a different location and name
""" """
project_id = str(uuid.uuid4())
project_id = str(uuid.uuid4())
topology = { topology = {
"project_id": str(uuid.uuid4()), "project_id": str(uuid.uuid4()),
"name": "test", "name": "test",
@ -531,7 +532,7 @@ def test_import_project_name_and_location(async_run, tmpdir, controller):
myzip.write(str(tmpdir / "project.gns3"), "project.gns3") myzip.write(str(tmpdir / "project.gns3"), "project.gns3")
with open(zip_path, "rb") as f: with open(zip_path, "rb") as f:
project = async_run(import_project(controller, project_id, f, name="hello", location=str(tmpdir / "hello"))) project = await import_project(controller, project_id, f, name="hello", location=str(tmpdir / "hello"))
assert project.name == "hello" assert project.name == "hello"
@ -539,5 +540,5 @@ def test_import_project_name_and_location(async_run, tmpdir, controller):
# A new project name is generated when you import twice the same name # A new project name is generated when you import twice the same name
with open(zip_path, "rb") as f: with open(zip_path, "rb") as f:
project = async_run(import_project(controller, str(uuid.uuid4()), f, name="hello", location=str(tmpdir / "test"))) project = await import_project(controller, str(uuid.uuid4()), f, name="hello", location=str(tmpdir / "test"))
assert project.name == "hello-1" assert project.name == "hello-1"

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2016 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -15,35 +15,20 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import pytest import pytest
import aiohttp import aiohttp
import asyncio
from unittest.mock import MagicMock from unittest.mock import MagicMock
from gns3server.controller.link import Link from gns3server.controller.link import Link
from gns3server.controller.node import Node from gns3server.controller.node import Node
from gns3server.controller.ports.ethernet_port import EthernetPort from gns3server.controller.ports.ethernet_port import EthernetPort
from gns3server.controller.ports.serial_port import SerialPort from gns3server.controller.ports.serial_port import SerialPort
from gns3server.controller.compute import Compute
from gns3server.controller.project import Project
from tests.utils import AsyncioBytesIO, AsyncioMagicMock from tests.utils import AsyncioBytesIO, AsyncioMagicMock
@pytest.fixture @pytest.fixture
def project(controller): async def link(project, compute):
return Project(controller=controller, name="Test")
@pytest.fixture
def compute():
return Compute("example.com", controller=MagicMock())
@pytest.fixture
def link(async_run, project, compute):
node1 = Node(project, compute, "node1", node_type="qemu") node1 = Node(project, compute, "node1", node_type="qemu")
node1._ports = [EthernetPort("E0", 0, 0, 4)] node1._ports = [EthernetPort("E0", 0, 0, 4)]
node2 = Node(project, compute, "node2", node_type="qemu") node2 = Node(project, compute, "node2", node_type="qemu")
@ -51,26 +36,27 @@ def link(async_run, project, compute):
link = Link(project) link = Link(project)
link.create = AsyncioMagicMock() link.create = AsyncioMagicMock()
async_run(link.add_node(node1, 0, 4)) await link.add_node(node1, 0, 4)
async_run(link.add_node(node2, 1, 3)) await link.add_node(node2, 1, 3)
return link return link
def test_eq(project, link, controller): def test_eq(project, link):
assert link == Link(project, link_id=link.id) assert link == Link(project, link_id=link.id)
assert link != "a" assert link != "a"
assert link != Link(project) assert link != Link(project)
def test_add_node(async_run, project, compute): async def test_add_node(project, compute):
node1 = Node(project, compute, "node1", node_type="qemu") node1 = Node(project, compute, "node1", node_type="qemu")
node1._ports = [EthernetPort("E0", 0, 0, 4)] node1._ports = [EthernetPort("E0", 0, 0, 4)]
link = Link(project) link = Link(project)
link.create = AsyncioMagicMock() link.create = AsyncioMagicMock()
link._project.emit_notification = MagicMock() link._project.emit_notification = MagicMock()
project.dump = AsyncioMagicMock() project.dump = AsyncioMagicMock()
async_run(link.add_node(node1, 0, 4)) await link.add_node(node1, 0, 4)
assert link._nodes == [ assert link._nodes == [
{ {
"node": node1, "node": node1,
@ -85,23 +71,23 @@ def test_add_node(async_run, project, compute):
] ]
assert project.dump.called assert project.dump.called
assert not link._project.emit_notification.called assert not link._project.emit_notification.called
assert not link.create.called assert not link.create.called
# We call link.created only when both side are created # We call link.created only when both side are created
node2 = Node(project, compute, "node2", node_type="qemu") node2 = Node(project, compute, "node2", node_type="qemu")
node2._ports = [EthernetPort("E0", 0, 0, 4)] node2._ports = [EthernetPort("E0", 0, 0, 4)]
async_run(link.add_node(node2, 0, 4)) await link.add_node(node2, 0, 4)
assert link.create.called assert link.create.called
link._project.emit_notification.assert_called_with("link.created", link.__json__()) link._project.emit_notification.assert_called_with("link.created", link.__json__())
assert link in node2.links assert link in node2.links
def test_add_node_already_connected(async_run, project, compute): async def test_add_node_already_connected(project, compute):
""" """
Raise an error if we try to use an already connected port Raise an error if we try to use an already connected port
""" """
project.dump = AsyncioMagicMock() project.dump = AsyncioMagicMock()
node1 = Node(project, compute, "node1", node_type="qemu") node1 = Node(project, compute, "node1", node_type="qemu")
@ -110,19 +96,20 @@ def test_add_node_already_connected(async_run, project, compute):
link = Link(project) link = Link(project)
link.create = AsyncioMagicMock() link.create = AsyncioMagicMock()
link._project.emit_notification = MagicMock() link._project.emit_notification = MagicMock()
async_run(link.add_node(node1, 0, 4)) await link.add_node(node1, 0, 4)
node2 = Node(project, compute, "node2", node_type="qemu") node2 = Node(project, compute, "node2", node_type="qemu")
node2._ports = [EthernetPort("E0", 0, 0, 4)] node2._ports = [EthernetPort("E0", 0, 0, 4)]
async_run(link.add_node(node2, 0, 4)) await link.add_node(node2, 0, 4)
assert link.create.called assert link.create.called
link2 = Link(project) link2 = Link(project)
link2.create = AsyncioMagicMock() link2.create = AsyncioMagicMock()
with pytest.raises(aiohttp.web.HTTPConflict): with pytest.raises(aiohttp.web.HTTPConflict):
async_run(link2.add_node(node1, 0, 4)) await link2.add_node(node1, 0, 4)
async def test_add_node_cloud(project, compute):
def test_add_node_cloud(async_run, project, compute):
node1 = Node(project, compute, "node1", node_type="qemu") node1 = Node(project, compute, "node1", node_type="qemu")
node1._ports = [EthernetPort("E0", 0, 0, 4)] node1._ports = [EthernetPort("E0", 0, 0, 4)]
node2 = Node(project, compute, "node2", node_type="cloud") node2 = Node(project, compute, "node2", node_type="cloud")
@ -132,14 +119,15 @@ def test_add_node_cloud(async_run, project, compute):
link.create = AsyncioMagicMock() link.create = AsyncioMagicMock()
link._project.emit_notification = MagicMock() link._project.emit_notification = MagicMock()
async_run(link.add_node(node1, 0, 4)) await link.add_node(node1, 0, 4)
async_run(link.add_node(node2, 0, 4)) await link.add_node(node2, 0, 4)
def test_add_node_cloud_to_cloud(async_run, project, compute): async def test_add_node_cloud_to_cloud(project, compute):
""" """
Cloud to cloud connection is not allowed Cloud to cloud connection is not allowed
""" """
node1 = Node(project, compute, "node1", node_type="cloud") node1 = Node(project, compute, "node1", node_type="cloud")
node1._ports = [EthernetPort("E0", 0, 0, 4)] node1._ports = [EthernetPort("E0", 0, 0, 4)]
node2 = Node(project, compute, "node2", node_type="cloud") node2 = Node(project, compute, "node2", node_type="cloud")
@ -149,15 +137,16 @@ def test_add_node_cloud_to_cloud(async_run, project, compute):
link.create = AsyncioMagicMock() link.create = AsyncioMagicMock()
link._project.emit_notification = MagicMock() link._project.emit_notification = MagicMock()
async_run(link.add_node(node1, 0, 4)) await link.add_node(node1, 0, 4)
with pytest.raises(aiohttp.web.HTTPConflict): with pytest.raises(aiohttp.web.HTTPConflict):
async_run(link.add_node(node2, 0, 4)) await link.add_node(node2, 0, 4)
def test_add_node_same_node(async_run, project, compute): async def test_add_node_same_node(project, compute):
""" """
Connection to the same node is not allowed Connection to the same node is not allowed
""" """
node1 = Node(project, compute, "node1", node_type="qemu") node1 = Node(project, compute, "node1", node_type="qemu")
node1._ports = [EthernetPort("E0", 0, 0, 4), EthernetPort("E1", 0, 0, 5)] node1._ports = [EthernetPort("E0", 0, 0, 4), EthernetPort("E1", 0, 0, 5)]
@ -165,15 +154,16 @@ def test_add_node_same_node(async_run, project, compute):
link.create = AsyncioMagicMock() link.create = AsyncioMagicMock()
link._project.emit_notification = MagicMock() link._project.emit_notification = MagicMock()
async_run(link.add_node(node1, 0, 4)) await link.add_node(node1, 0, 4)
with pytest.raises(aiohttp.web.HTTPConflict): with pytest.raises(aiohttp.web.HTTPConflict):
async_run(link.add_node(node1, 0, 5)) await link.add_node(node1, 0, 5)
def test_add_node_serial_to_ethernet(async_run, project, compute): async def test_add_node_serial_to_ethernet(project, compute):
""" """
Serial to ethernet connection is not allowed Serial to ethernet connection is not allowed
""" """
node1 = Node(project, compute, "node1", node_type="qemu") node1 = Node(project, compute, "node1", node_type="qemu")
node1._ports = [EthernetPort("E0", 0, 0, 4)] node1._ports = [EthernetPort("E0", 0, 0, 4)]
node2 = Node(project, compute, "node2", node_type="qemu") node2 = Node(project, compute, "node2", node_type="qemu")
@ -183,12 +173,13 @@ def test_add_node_serial_to_ethernet(async_run, project, compute):
link.create = AsyncioMagicMock() link.create = AsyncioMagicMock()
link._project.emit_notification = MagicMock() link._project.emit_notification = MagicMock()
async_run(link.add_node(node1, 0, 4)) await link.add_node(node1, 0, 4)
with pytest.raises(aiohttp.web.HTTPConflict): with pytest.raises(aiohttp.web.HTTPConflict):
async_run(link.add_node(node2, 0, 4)) await link.add_node(node2, 0, 4)
async def test_json(project, compute):
def test_json(async_run, project, compute, link):
node1 = Node(project, compute, "node1", node_type="qemu") node1 = Node(project, compute, "node1", node_type="qemu")
node1._ports = [EthernetPort("E0", 0, 0, 4)] node1._ports = [EthernetPort("E0", 0, 0, 4)]
node2 = Node(project, compute, "node2", node_type="qemu") node2 = Node(project, compute, "node2", node_type="qemu")
@ -196,8 +187,8 @@ def test_json(async_run, project, compute, link):
link = Link(project) link = Link(project)
link.create = AsyncioMagicMock() link.create = AsyncioMagicMock()
async_run(link.add_node(node1, 0, 4)) await link.add_node(node1, 0, 4)
async_run(link.add_node(node2, 1, 3)) await link.add_node(node2, 1, 3)
assert link.__json__() == { assert link.__json__() == {
"link_id": link.id, "link_id": link.id,
"project_id": project.id, "project_id": project.id,
@ -256,7 +247,8 @@ def test_json(async_run, project, compute, link):
} }
def test_json_serial_link(async_run, project, compute, link): async def test_json_serial_link(project, compute):
node1 = Node(project, compute, "node1", node_type="qemu") node1 = Node(project, compute, "node1", node_type="qemu")
node1._ports = [SerialPort("S0", 0, 0, 4)] node1._ports = [SerialPort("S0", 0, 0, 4)]
node2 = Node(project, compute, "node2", node_type="qemu") node2 = Node(project, compute, "node2", node_type="qemu")
@ -264,11 +256,13 @@ def test_json_serial_link(async_run, project, compute, link):
link = Link(project) link = Link(project)
link.create = AsyncioMagicMock() link.create = AsyncioMagicMock()
async_run(link.add_node(node1, 0, 4)) await link.add_node(node1, 0, 4)
async_run(link.add_node(node2, 1, 3)) await link.add_node(node2, 1, 3)
assert link.__json__()["link_type"] == "serial" assert link.__json__()["link_type"] == "serial"
def test_default_capture_file_name(project, compute, async_run):
async def test_default_capture_file_name(project, compute):
node1 = Node(project, compute, "Hello@", node_type="qemu") node1 = Node(project, compute, "Hello@", node_type="qemu")
node1._ports = [EthernetPort("E0", 0, 0, 4)] node1._ports = [EthernetPort("E0", 0, 0, 4)]
node2 = Node(project, compute, "w0.rld", node_type="qemu") node2 = Node(project, compute, "w0.rld", node_type="qemu")
@ -276,33 +270,35 @@ def test_default_capture_file_name(project, compute, async_run):
link = Link(project) link = Link(project)
link.create = AsyncioMagicMock() link.create = AsyncioMagicMock()
async_run(link.add_node(node1, 0, 4)) await link.add_node(node1, 0, 4)
async_run(link.add_node(node2, 1, 3)) await link.add_node(node2, 1, 3)
assert link.default_capture_file_name() == "Hello_0-4_to_w0rld_1-3.pcap" assert link.default_capture_file_name() == "Hello_0-4_to_w0rld_1-3.pcap"
def test_start_capture(link, async_run, tmpdir): async def test_start_capture(link):
async def fake_reader(): async def fake_reader():
return AsyncioBytesIO() return AsyncioBytesIO()
link.read_pcap_from_source = fake_reader link.read_pcap_from_source = fake_reader
link._project.emit_notification = MagicMock() link._project.emit_notification = MagicMock()
async_run(link.start_capture(capture_file_name="test.pcap")) await link.start_capture(capture_file_name="test.pcap")
assert link._capturing assert link._capturing
assert link._capture_file_name == "test.pcap" assert link._capture_file_name == "test.pcap"
link._project.emit_notification.assert_called_with("link.updated", link.__json__()) link._project.emit_notification.assert_called_with("link.updated", link.__json__())
def test_stop_capture(link, async_run, tmpdir): async def test_stop_capture(link):
link._capturing = True link._capturing = True
link._project.emit_notification = MagicMock() link._project.emit_notification = MagicMock()
async_run(link.stop_capture()) await link.stop_capture()
assert link._capturing is False assert link._capturing is False
link._project.emit_notification.assert_called_with("link.updated", link.__json__()) link._project.emit_notification.assert_called_with("link.updated", link.__json__())
def test_delete(async_run, project, compute): async def test_delete(project, compute):
node1 = Node(project, compute, "node1", node_type="qemu") node1 = Node(project, compute, "node1", node_type="qemu")
node1._ports = [EthernetPort("E0", 0, 0, 4)] node1._ports = [EthernetPort("E0", 0, 0, 4)]
@ -310,19 +306,18 @@ def test_delete(async_run, project, compute):
link.create = AsyncioMagicMock() link.create = AsyncioMagicMock()
link._project.emit_notification = MagicMock() link._project.emit_notification = MagicMock()
project.dump = AsyncioMagicMock() project.dump = AsyncioMagicMock()
async_run(link.add_node(node1, 0, 4)) await link.add_node(node1, 0, 4)
node2 = Node(project, compute, "node2", node_type="qemu") node2 = Node(project, compute, "node2", node_type="qemu")
node2._ports = [EthernetPort("E0", 0, 0, 4)] node2._ports = [EthernetPort("E0", 0, 0, 4)]
async_run(link.add_node(node2, 0, 4)) await link.add_node(node2, 0, 4)
assert link in node2.links assert link in node2.links
await link.delete()
async_run(link.delete())
assert link not in node2.links assert link not in node2.links
def test_update_filters(async_run, project, compute): async def test_update_filters(project, compute):
node1 = Node(project, compute, "node1", node_type="qemu") node1 = Node(project, compute, "node1", node_type="qemu")
node1._ports = [EthernetPort("E0", 0, 0, 4)] node1._ports = [EthernetPort("E0", 0, 0, 4)]
@ -330,20 +325,20 @@ def test_update_filters(async_run, project, compute):
link.create = AsyncioMagicMock() link.create = AsyncioMagicMock()
link._project.emit_notification = MagicMock() link._project.emit_notification = MagicMock()
project.dump = AsyncioMagicMock() project.dump = AsyncioMagicMock()
async_run(link.add_node(node1, 0, 4)) await link.add_node(node1, 0, 4)
node2 = Node(project, compute, "node2", node_type="qemu") node2 = Node(project, compute, "node2", node_type="qemu")
node2._ports = [EthernetPort("E0", 0, 0, 4)] node2._ports = [EthernetPort("E0", 0, 0, 4)]
async_run(link.add_node(node2, 0, 4)) await link.add_node(node2, 0, 4)
link.update = AsyncioMagicMock() link.update = AsyncioMagicMock()
assert link._created assert link._created
async_run(link.update_filters({ await link.update_filters({
"packet_loss": [10], "packet_loss": [10],
"delay": [50, 10], "delay": [50, 10],
"frequency_drop": [0], "frequency_drop": [0],
"bpf": [" \n "] "bpf": [" \n "]
})) })
assert link.filters == { assert link.filters == {
"packet_loss": [10], "packet_loss": [10],
"delay": [50, 10] "delay": [50, 10]
@ -351,7 +346,8 @@ def test_update_filters(async_run, project, compute):
assert link.update.called assert link.update.called
def test_available_filters(async_run, project, compute): async def test_available_filters(project, compute):
node1 = Node(project, compute, "node1", node_type="ethernet_switch") node1 = Node(project, compute, "node1", node_type="ethernet_switch")
node1._ports = [EthernetPort("E0", 0, 0, 4)] node1._ports = [EthernetPort("E0", 0, 0, 4)]
@ -360,10 +356,10 @@ def test_available_filters(async_run, project, compute):
assert link.available_filters() == [] assert link.available_filters() == []
# Ethernet switch is not supported should return 0 filters # Ethernet switch is not supported should return 0 filters
async_run(link.add_node(node1, 0, 4)) await link.add_node(node1, 0, 4)
assert link.available_filters() == [] assert link.available_filters() == []
node2 = Node(project, compute, "node2", node_type="vpcs") node2 = Node(project, compute, "node2", node_type="vpcs")
node2._ports = [EthernetPort("E0", 0, 0, 4)] node2._ports = [EthernetPort("E0", 0, 0, 4)]
async_run(link.add_node(node2, 0, 4)) await link.add_node(node2, 0, 4)
assert len(link.available_filters()) > 0 assert len(link.available_filters()) > 0

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2016 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -16,15 +16,11 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import shutil import shutil
import aiohttp
import pytest import pytest
import uuid import uuid
import asyncio
import copy
import os import os
from unittest.mock import MagicMock, ANY
from unittest.mock import MagicMock, ANY
from tests.utils import AsyncioMagicMock from tests.utils import AsyncioMagicMock
from gns3server.controller.node import Node from gns3server.controller.node import Node
@ -33,18 +29,15 @@ from gns3server.controller.project import Project
@pytest.fixture @pytest.fixture
def compute(): def compute():
s = AsyncioMagicMock() s = AsyncioMagicMock()
s.id = "http://test.com:42" s.id = "http://test.com:42"
return s return s
@pytest.fixture
def project(controller):
return Project(str(uuid.uuid4()), controller=controller)
@pytest.fixture @pytest.fixture
def node(compute, project): def node(compute, project):
node = Node(project, compute, "demo", node = Node(project, compute, "demo",
node_id=str(uuid.uuid4()), node_id=str(uuid.uuid4()),
node_type="vpcs", node_type="vpcs",
@ -57,6 +50,7 @@ def test_name(compute, project):
""" """
If node use a name template generate names If node use a name template generate names
""" """
node = Node(project, compute, "PC", node = Node(project, compute, "PC",
node_id=str(uuid.uuid4()), node_id=str(uuid.uuid4()),
node_type="vpcs", node_type="vpcs",
@ -76,16 +70,13 @@ def test_name(compute, project):
properties={"startup_script": "echo test"}) properties={"startup_script": "echo test"})
assert node.name == "PC2" assert node.name == "PC2"
# If we change the name to a name already used we patch the name to a free
node.name == "PC1"
assert node.name == "PC2"
def test_vmname(compute, project): def test_vmname(compute, project):
""" """
Additionnal properties should be add to the properties Additionnal properties should be add to the properties
field field
""" """
node = Node(project, compute, "PC", node = Node(project, compute, "PC",
node_id=str(uuid.uuid4()), node_id=str(uuid.uuid4()),
node_type="virtualbox", node_type="virtualbox",
@ -111,6 +102,7 @@ def test_empty_properties(compute, project):
def test_eq(compute, project, node, controller): def test_eq(compute, project, node, controller):
assert node == Node(project, compute, "demo1", node_id=node.id, node_type="qemu") assert node == Node(project, compute, "demo1", node_id=node.id, node_type="qemu")
assert node != "a" assert node != "a"
assert node != Node(project, compute, "demo2", node_id=str(uuid.uuid4()), node_type="qemu") assert node != Node(project, compute, "demo2", node_id=str(uuid.uuid4()), node_type="qemu")
@ -118,6 +110,7 @@ def test_eq(compute, project, node, controller):
def test_json(node, compute): def test_json(node, compute):
assert node.__json__() == { assert node.__json__() == {
"compute_id": str(compute.id), "compute_id": str(compute.id),
"project_id": node.project.id, "project_id": node.project.id,
@ -156,6 +149,7 @@ def test_json(node, compute):
} }
] ]
} }
assert node.__json__(topology_dump=True) == { assert node.__json__(topology_dump=True) == {
"compute_id": str(compute.id), "compute_id": str(compute.id),
"node_id": node.id, "node_id": node.id,
@ -188,14 +182,14 @@ def test_init_without_uuid(project, compute):
assert node.id is not None assert node.id is not None
def test_create(node, compute, project, async_run): async def test_create(node, compute):
node._console = 2048
node._console = 2048
response = MagicMock() response = MagicMock()
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
assert async_run(node.create()) is True assert await node.create() is True
data = { data = {
"console": 2048, "console": 2048,
"console_type": "vnc", "console_type": "vnc",
@ -208,9 +202,9 @@ def test_create(node, compute, project, async_run):
assert node._properties == {"startup_script": "echo test"} assert node._properties == {"startup_script": "echo test"}
def test_create_image_missing(node, compute, project, async_run): async def test_create_image_missing(node, compute):
node._console = 2048
node._console = 2048
node.__calls = 0 node.__calls = 0
async def resp(*args, **kwargs): async def resp(*args, **kwargs):
@ -226,13 +220,13 @@ def test_create_image_missing(node, compute, project, async_run):
compute.post = AsyncioMagicMock(side_effect=resp) compute.post = AsyncioMagicMock(side_effect=resp)
node._upload_missing_image = AsyncioMagicMock(return_value=True) node._upload_missing_image = AsyncioMagicMock(return_value=True)
assert async_run(node.create()) is True assert await node.create() is True
node._upload_missing_image.called is True #assert node._upload_missing_image.called is True
def test_create_base_script(node, config, compute, tmpdir, async_run): async def test_create_base_script(node, config, compute, tmpdir):
config.set_section_config("Server", {"configs_path": str(tmpdir)})
config.set_section_config("Server", {"configs_path": str(tmpdir)})
with open(str(tmpdir / 'test.txt'), 'w+') as f: with open(str(tmpdir / 'test.txt'), 'w+') as f:
f.write('hostname test') f.write('hostname test')
@ -243,7 +237,7 @@ def test_create_base_script(node, config, compute, tmpdir, async_run):
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
assert async_run(node.create()) is True assert await node.create() is True
data = { data = {
"console": 2048, "console": 2048,
"console_type": "vnc", "console_type": "vnc",
@ -251,10 +245,11 @@ def test_create_base_script(node, config, compute, tmpdir, async_run):
"startup_script": "hostname test", "startup_script": "hostname test",
"name": "demo" "name": "demo"
} }
compute.post.assert_called_with("/projects/{}/vpcs/nodes".format(node.project.id), data=data, timeout=1200) compute.post.assert_called_with("/projects/{}/vpcs/nodes".format(node.project.id), data=data, timeout=1200)
def test_symbol(node, symbols_dir, controller): def test_symbol(node, symbols_dir):
""" """
Change symbol should change the node size Change symbol should change the node size
""" """
@ -304,14 +299,15 @@ def test_label_with_default_label_font(node):
assert node.label["style"] == None #"font-family: TypeWriter;font-size: 10;font-weight: bold;fill: #ff0000;fill-opacity: 1.0;" assert node.label["style"] == None #"font-family: TypeWriter;font-size: 10;font-weight: bold;fill: #ff0000;fill-opacity: 1.0;"
def test_update(node, compute, project, async_run, controller): async def test_update(node, compute, project, controller):
response = MagicMock() response = MagicMock()
response.json = {"console": 2048} response.json = {"console": 2048}
compute.put = AsyncioMagicMock(return_value=response) compute.put = AsyncioMagicMock(return_value=response)
controller._notification = AsyncioMagicMock() controller._notification = AsyncioMagicMock()
project.dump = MagicMock() project.dump = MagicMock()
async_run(node.update(x=42, console=2048, console_type="vnc", properties={"startup_script": "echo test"}, name="demo")) await node.update(x=42, console=2048, console_type="vnc", properties={"startup_script": "echo test"}, name="demo")
data = { data = {
"console": 2048, "console": 2048,
"console_type": "vnc", "console_type": "vnc",
@ -326,7 +322,7 @@ def test_update(node, compute, project, async_run, controller):
assert project.dump.called assert project.dump.called
def test_update_properties(node, compute, project, async_run, controller): async def test_update_properties(node, compute, controller):
""" """
properties will be updated by the answer from compute properties will be updated by the answer from compute
""" """
@ -335,7 +331,7 @@ def test_update_properties(node, compute, project, async_run, controller):
compute.put = AsyncioMagicMock(return_value=response) compute.put = AsyncioMagicMock(return_value=response)
controller._notification = AsyncioMagicMock() controller._notification = AsyncioMagicMock()
async_run(node.update(x=42, console=2048, console_type="vnc", properties={"startup_script": "hello world"}, name="demo")) await node.update(x=42, console=2048, console_type="vnc", properties={"startup_script": "hello world"}, name="demo")
data = { data = {
"console": 2048, "console": 2048,
"console_type": "vnc", "console_type": "vnc",
@ -354,26 +350,27 @@ def test_update_properties(node, compute, project, async_run, controller):
#controller._notification.emit.assert_called_with("node.updated", node_notif) #controller._notification.emit.assert_called_with("node.updated", node_notif)
def test_update_only_controller(node, controller, compute, async_run): async def test_update_only_controller(node, compute):
""" """
When updating property used only on controller we don't need to When updating property used only on controller we don't need to
call the compute call the compute
""" """
compute.put = AsyncioMagicMock() compute.put = AsyncioMagicMock()
node._project.emit_notification = AsyncioMagicMock() node._project.emit_notification = AsyncioMagicMock()
async_run(node.update(x=42)) await node.update(x=42)
assert not compute.put.called assert not compute.put.called
assert node.x == 42 assert node.x == 42
node._project.emit_notification.assert_called_with("node.updated", node.__json__()) node._project.emit_notification.assert_called_with("node.updated", node.__json__())
# If nothing change a second notif should not be send # If nothing change a second notif should not be sent
node._project.emit_notification = AsyncioMagicMock() node._project.emit_notification = AsyncioMagicMock()
async_run(node.update(x=42)) await node.update(x=42)
assert not node._project.emit_notification.called assert not node._project.emit_notification.called
def test_update_no_changes(node, compute, project, async_run): async def test_update_no_changes(node, compute):
""" """
We don't call the compute node if all compute properties has not changed We don't call the compute node if all compute properties has not changed
""" """
@ -381,24 +378,25 @@ def test_update_no_changes(node, compute, project, async_run):
response.json = {"console": 2048} response.json = {"console": 2048}
compute.put = AsyncioMagicMock(return_value=response) compute.put = AsyncioMagicMock(return_value=response)
async_run(node.update(console=2048, x=42)) await node.update(console=2048, x=42)
assert compute.put.called assert compute.put.called
compute.put = AsyncioMagicMock() compute.put = AsyncioMagicMock()
async_run(node.update(console=2048, x=43)) await node.update(console=2048, x=43)
assert not compute.put.called assert not compute.put.called
assert node.x == 43 assert node.x == 43
def test_start(node, compute, project, async_run): async def test_start(node, compute):
compute.post = AsyncioMagicMock() compute.post = AsyncioMagicMock()
async_run(node.start()) await node.start()
compute.post.assert_called_with("/projects/{}/vpcs/nodes/{}/start".format(node.project.id, node.id), timeout=240) compute.post.assert_called_with("/projects/{}/vpcs/nodes/{}/start".format(node.project.id, node.id), timeout=240)
def test_start_iou(compute, project, async_run, controller): async def test_start_iou(compute, project, controller):
node = Node(project, compute, "demo", node = Node(project, compute, "demo",
node_id=str(uuid.uuid4()), node_id=str(uuid.uuid4()),
node_type="iou") node_type="iou")
@ -409,44 +407,42 @@ def test_start_iou(compute, project, async_run, controller):
# async_run(node.start()) # async_run(node.start())
controller._iou_license_settings = {"license_check": True, "iourc_content": "aa"} controller._iou_license_settings = {"license_check": True, "iourc_content": "aa"}
async_run(node.start()) await node.start()
compute.post.assert_called_with("/projects/{}/iou/nodes/{}/start".format(node.project.id, node.id), timeout=240, data={"license_check": True, "iourc_content": "aa"}) compute.post.assert_called_with("/projects/{}/iou/nodes/{}/start".format(node.project.id, node.id), timeout=240, data={"license_check": True, "iourc_content": "aa"})
def test_stop(node, compute, project, async_run): async def test_stop(node, compute):
compute.post = AsyncioMagicMock() compute.post = AsyncioMagicMock()
async_run(node.stop()) await node.stop()
compute.post.assert_called_with("/projects/{}/vpcs/nodes/{}/stop".format(node.project.id, node.id), timeout=240, dont_connect=True) compute.post.assert_called_with("/projects/{}/vpcs/nodes/{}/stop".format(node.project.id, node.id), timeout=240, dont_connect=True)
def test_suspend(node, compute, project, async_run): async def test_suspend(node, compute):
compute.post = AsyncioMagicMock() compute.post = AsyncioMagicMock()
await node.suspend()
async_run(node.suspend())
compute.post.assert_called_with("/projects/{}/vpcs/nodes/{}/suspend".format(node.project.id, node.id), timeout=240) compute.post.assert_called_with("/projects/{}/vpcs/nodes/{}/suspend".format(node.project.id, node.id), timeout=240)
def test_reload(node, compute, project, async_run): async def test_reload(node, compute):
compute.post = AsyncioMagicMock() compute.post = AsyncioMagicMock()
await node.reload()
async_run(node.reload())
compute.post.assert_called_with("/projects/{}/vpcs/nodes/{}/reload".format(node.project.id, node.id), timeout=240) compute.post.assert_called_with("/projects/{}/vpcs/nodes/{}/reload".format(node.project.id, node.id), timeout=240)
def test_create_without_console(node, compute, project, async_run): async def test_create_without_console(node, compute):
""" """
None properties should be send. Because it can mean the emulator doesn"t support it None properties should be send. Because it can mean the emulator doesn't support it
""" """
response = MagicMock() response = MagicMock()
response.json = {"console": 2048, "test_value": "success"} response.json = {"console": 2048, "test_value": "success"}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
async_run(node.create()) await node.create()
data = { data = {
"console_type": "vnc", "console_type": "vnc",
"node_id": node.id, "node_id": node.id,
@ -458,49 +454,53 @@ def test_create_without_console(node, compute, project, async_run):
assert node._properties == {"test_value": "success", "startup_script": "echo test"} assert node._properties == {"test_value": "success", "startup_script": "echo test"}
def test_delete(node, compute, async_run): async def test_delete(node, compute):
async_run(node.destroy())
await node.destroy()
compute.delete.assert_called_with("/projects/{}/vpcs/nodes/{}".format(node.project.id, node.id)) compute.delete.assert_called_with("/projects/{}/vpcs/nodes/{}".format(node.project.id, node.id))
def test_post(node, compute, async_run): async def test_post(node, compute):
async_run(node.post("/test", {"a": "b"}))
await node.post("/test", {"a": "b"})
compute.post.assert_called_with("/projects/{}/vpcs/nodes/{}/test".format(node.project.id, node.id), data={"a": "b"}) compute.post.assert_called_with("/projects/{}/vpcs/nodes/{}/test".format(node.project.id, node.id), data={"a": "b"})
def test_delete(node, compute, async_run): async def test_delete(node, compute):
async_run(node.delete("/test"))
await node.delete("/test")
compute.delete.assert_called_with("/projects/{}/vpcs/nodes/{}/test".format(node.project.id, node.id)) compute.delete.assert_called_with("/projects/{}/vpcs/nodes/{}/test".format(node.project.id, node.id))
def test_dynamips_idle_pc(node, async_run, compute): async def test_dynamips_idle_pc(node, compute):
node._node_type = "dynamips" node._node_type = "dynamips"
response = MagicMock() response = MagicMock()
response.json = {"idlepc": "0x60606f54"} response.json = {"idlepc": "0x60606f54"}
compute.get = AsyncioMagicMock(return_value=response) compute.get = AsyncioMagicMock(return_value=response)
await node.dynamips_auto_idlepc()
async_run(node.dynamips_auto_idlepc())
compute.get.assert_called_with("/projects/{}/dynamips/nodes/{}/auto_idlepc".format(node.project.id, node.id), timeout=240) compute.get.assert_called_with("/projects/{}/dynamips/nodes/{}/auto_idlepc".format(node.project.id, node.id), timeout=240)
def test_dynamips_idlepc_proposals(node, async_run, compute): async def test_dynamips_idlepc_proposals(node, compute):
node._node_type = "dynamips" node._node_type = "dynamips"
response = MagicMock() response = MagicMock()
response.json = ["0x60606f54", "0x30ff6f37"] response.json = ["0x60606f54", "0x30ff6f37"]
compute.get = AsyncioMagicMock(return_value=response) compute.get = AsyncioMagicMock(return_value=response)
await node.dynamips_idlepc_proposals()
async_run(node.dynamips_idlepc_proposals())
compute.get.assert_called_with("/projects/{}/dynamips/nodes/{}/idlepc_proposals".format(node.project.id, node.id), timeout=240) compute.get.assert_called_with("/projects/{}/dynamips/nodes/{}/idlepc_proposals".format(node.project.id, node.id), timeout=240)
def test_upload_missing_image(compute, controller, async_run, images_dir): async def test_upload_missing_image(compute, controller, images_dir):
project = Project(str(uuid.uuid4()), controller=controller) project = Project(str(uuid.uuid4()), controller=controller)
node = Node(project, compute, "demo", node = Node(project, compute, "demo",
node_id=str(uuid.uuid4()), node_id=str(uuid.uuid4()),
node_type="qemu", node_type="qemu",
properties={"hda_disk_image": "linux.img"}) properties={"hda_disk_image": "linux.img"})
open(os.path.join(images_dir, "linux.img"), 'w+').close() open(os.path.join(images_dir, "linux.img"), 'w+').close()
assert async_run(node._upload_missing_image("qemu", "linux.img")) is True assert await node._upload_missing_image("qemu", "linux.img") is True
compute.post.assert_called_with("/qemu/images/linux.img", data=ANY, timeout=None) compute.post.assert_called_with("/qemu/images/linux.img", data=ANY, timeout=None)
@ -509,6 +509,7 @@ def test_update_label(node):
The text in label need to be always the The text in label need to be always the
node name node name
""" """
node.name = "Test" node.name = "Test"
assert node.label["text"] == "Test" assert node.label["text"] == "Test"
node.label = {"text": "Wrong", "x": 12} node.label = {"text": "Wrong", "x": 12}
@ -517,6 +518,7 @@ def test_update_label(node):
def test_get_port(node): def test_get_port(node):
node._node_type = "qemu" node._node_type = "qemu"
node._properties["adapters"] = 2 node._properties["adapters"] = 2
node._list_ports() node._list_ports()
@ -529,12 +531,13 @@ def test_get_port(node):
assert port is None assert port is None
def test_parse_node_response(node, async_run): async def test_parse_node_response(node):
""" """
When a node is updated we notify the links connected to it When a node is updated we notify the links connected to it
""" """
link = MagicMock() link = MagicMock()
link.node_updated = AsyncioMagicMock() link.node_updated = AsyncioMagicMock()
node.add_link(link) node.add_link(link)
async_run(node.parse_node_response({"status": "started"})) await node.parse_node_response({"status": "started"})
assert link.node_updated.called assert link.node_updated.called

@ -21,7 +21,6 @@ import uuid
from tests.utils import AsyncioMagicMock from tests.utils import AsyncioMagicMock
from gns3server.controller.node import Node from gns3server.controller.node import Node
from gns3server.controller.project import Project
from gns3server.controller.ports.ethernet_port import EthernetPort from gns3server.controller.ports.ethernet_port import EthernetPort
@ -31,12 +30,6 @@ def compute():
s.id = "http://test.com:42" s.id = "http://test.com:42"
return s return s
@pytest.fixture
def project(controller):
return Project(str(uuid.uuid4()), controller=controller)
@pytest.fixture @pytest.fixture
def node(compute, project): def node(compute, project):
node = Node(project, compute, "demo", node = Node(project, compute, "demo",
@ -51,6 +44,7 @@ def test_list_ports(node):
""" """
List port by default List port by default
""" """
assert node.__json__()["ports"] == [ assert node.__json__()["ports"] == [
{ {
"name": "Ethernet0", "name": "Ethernet0",
@ -67,6 +61,7 @@ def test_list_ports_vpcs(node):
""" """
List port by default List port by default
""" """
node._node_type = "vpcs" node._node_type = "vpcs"
assert node.__json__()["ports"] == [ assert node.__json__()["ports"] == [
{ {
@ -84,6 +79,7 @@ def test_list_ports_docker(node):
""" """
List port by default List port by default
""" """
node._node_type = "docker" node._node_type = "docker"
node._properties["adapters"] = 2 node._properties["adapters"] = 2
assert node.__json__()["ports"] == [ assert node.__json__()["ports"] == [
@ -110,6 +106,7 @@ def test_list_ports_port_name_format(node):
""" """
Support port name format Support port name format
""" """
node._first_port_name = None node._first_port_name = None
node._port_name_format = "eth{}" node._port_name_format = "eth{}"
node._list_ports() node._list_ports()
@ -139,6 +136,7 @@ def test_list_ports_adapters(node):
""" """
List port using adapters properties List port using adapters properties
""" """
node.properties["adapters"] = 2 node.properties["adapters"] = 2
assert node.__json__()["ports"] == [ assert node.__json__()["ports"] == [
{ {
@ -164,6 +162,7 @@ def test_list_ports_adapters_cloud(project, compute):
""" """
List port using adapters properties List port using adapters properties
""" """
node = Node(project, compute, "demo", node = Node(project, compute, "demo",
node_id=str(uuid.uuid4()), node_id=str(uuid.uuid4()),
node_type="cloud") node_type="cloud")
@ -192,6 +191,7 @@ def test_list_ports_ethernet_hub(project, compute):
""" """
List port for atm switch List port for atm switch
""" """
node = Node(project, compute, "demo", node = Node(project, compute, "demo",
node_id=str(uuid.uuid4()), node_id=str(uuid.uuid4()),
node_type="ethernet_hub") node_type="ethernet_hub")
@ -230,6 +230,7 @@ def test_list_ports_atm_switch(project, compute):
""" """
List port for atm switch List port for atm switch
""" """
node = Node(project, compute, "demo", node = Node(project, compute, "demo",
node_id=str(uuid.uuid4()), node_id=str(uuid.uuid4()),
node_type="atm_switch") node_type="atm_switch")
@ -261,6 +262,7 @@ def test_list_ports_frame_relay_switch(project, compute):
""" """
List port for frame relay switch List port for frame relay switch
""" """
node = Node(project, compute, "demo", node = Node(project, compute, "demo",
node_id=str(uuid.uuid4()), node_id=str(uuid.uuid4()),
node_type="frame_relay_switch") node_type="frame_relay_switch")
@ -309,6 +311,7 @@ def test_list_ports_iou(compute, project):
""" """
IOU has a special behavior 4 port by adapters IOU has a special behavior 4 port by adapters
""" """
node = Node(project, compute, "demo", node = Node(project, compute, "demo",
node_id=str(uuid.uuid4()), node_id=str(uuid.uuid4()),
node_type="iou") node_type="iou")
@ -514,6 +517,7 @@ def test_list_ports_dynamips(project, compute):
""" """
List port for dynamips List port for dynamips
""" """
node = Node(project, compute, "demo", node = Node(project, compute, "demo",
node_id=str(uuid.uuid4()), node_id=str(uuid.uuid4()),
node_type="dynamips") node_type="dynamips")
@ -595,6 +599,7 @@ def test_list_ports_dynamips(project, compute):
def test_short_name(): def test_short_name():
# If no customization of port name format return the default short name # If no customization of port name format return the default short name
assert EthernetPort("Ethernet0", 0, 0, 0).short_name == "e0" assert EthernetPort("Ethernet0", 0, 0, 0).short_name == "e0"
assert EthernetPort("Ethernet0", 0, 0, 0, short_name="mgmt").short_name == "mgmt" assert EthernetPort("Ethernet0", 0, 0, 0, short_name="mgmt").short_name == "mgmt"

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2016 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -18,81 +18,78 @@
import pytest import pytest
from unittest.mock import MagicMock from unittest.mock import MagicMock
from gns3server.controller.notification import Notification
from gns3server.controller import Controller
from tests.utils import AsyncioMagicMock from tests.utils import AsyncioMagicMock
@pytest.fixture @pytest.fixture
def project(async_run): async def node(project):
return async_run(Controller.instance().add_project(name="Test"))
@pytest.fixture
def node(project, async_run):
compute = MagicMock() compute = MagicMock()
compute.id = "remote1" compute.id = "remote1"
compute.host = "example.org" compute.host = "example.org"
response = MagicMock() response = MagicMock()
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
return await project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"})
return async_run(project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"}))
async def test_emit_to_all(controller, project):
def test_emit_to_all(async_run, controller, project):
""" """
Send an event to all if we don't have a project id in the event Send an event to all if we don't have a project id in the event
""" """
notif = controller.notification notif = controller.notification
with notif.project_queue(project.id) as queue: with notif.project_queue(project.id) as queue:
assert len(notif._project_listeners[project.id]) == 1 assert len(notif._project_listeners[project.id]) == 1
async_run(queue.get(0.1)) # ping await queue.get(0.1) # ping
notif.project_emit('test', {}) notif.project_emit('test', {})
msg = async_run(queue.get(5)) msg = await queue.get(5)
assert msg == ('test', {}, {}) assert msg == ('test', {}, {})
assert len(notif._project_listeners[project.id]) == 0 assert len(notif._project_listeners[project.id]) == 0
def test_emit_to_project(async_run, controller, project): async def test_emit_to_project(controller, project):
""" """
Send an event to a project listeners Send an event to a project listeners
""" """
notif = controller.notification notif = controller.notification
with notif.project_queue(project.id) as queue: with notif.project_queue(project.id) as queue:
assert len(notif._project_listeners[project.id]) == 1 assert len(notif._project_listeners[project.id]) == 1
async_run(queue.get(0.1)) # ping await queue.get(0.1) # ping
# This event has not listener # This event has not listener
notif.project_emit('ignore', {"project_id": 42}) notif.project_emit('ignore', {"project_id": 42})
notif.project_emit('test', {"project_id": project.id}) notif.project_emit('test', {"project_id": project.id})
msg = async_run(queue.get(5)) msg = await queue.get(5)
assert msg == ('test', {"project_id": project.id}, {}) assert msg == ('test', {"project_id": project.id}, {})
assert len(notif._project_listeners[project.id]) == 0 assert len(notif._project_listeners[project.id]) == 0
def test_dispatch(async_run, controller, project): async def test_dispatch(controller, project):
notif = controller.notification notif = controller.notification
with notif.project_queue(project.id) as queue: with notif.project_queue(project.id) as queue:
assert len(notif._project_listeners[project.id]) == 1 assert len(notif._project_listeners[project.id]) == 1
async_run(queue.get(0.1)) # ping await queue.get(0.1) # ping
async_run(notif.dispatch("test", {}, project_id=project.id, compute_id=1)) await notif.dispatch("test", {}, project_id=project.id, compute_id=1)
msg = async_run(queue.get(5)) msg = await queue.get(5)
assert msg == ('test', {}, {}) assert msg == ('test', {}, {})
def test_dispatch_ping(async_run, controller, project): async def test_dispatch_ping(controller, project):
notif = controller.notification notif = controller.notification
with notif.project_queue(project.id) as queue: with notif.project_queue(project.id) as queue:
assert len(notif._project_listeners[project.id]) == 1 assert len(notif._project_listeners[project.id]) == 1
async_run(queue.get(0.1)) # ping await queue.get(0.1) # ping
async_run(notif.dispatch("ping", {}, project_id=project.id, compute_id=12)) await notif.dispatch("ping", {}, project_id=project.id, compute_id=12)
msg = async_run(queue.get(5)) msg = await queue.get(5)
assert msg == ('ping', {'compute_id': 12}, {}) assert msg == ('ping', {'compute_id': 12}, {})
def test_dispatch_node_updated(async_run, controller, node, project): async def test_dispatch_node_updated(controller, node, project):
""" """
When we receive a node.updated notification from compute When we receive a node.updated notification from compute
we need to update the client we need to update the client
@ -101,25 +98,26 @@ def test_dispatch_node_updated(async_run, controller, node, project):
notif = controller.notification notif = controller.notification
with notif.project_queue(project.id) as queue: with notif.project_queue(project.id) as queue:
assert len(notif._project_listeners[project.id]) == 1 assert len(notif._project_listeners[project.id]) == 1
async_run(queue.get(0.1)) # ping await queue.get(0.1) # ping
async_run(notif.dispatch("node.updated", { await notif.dispatch("node.updated", {
"node_id": node.id, "node_id": node.id,
"project_id": project.id, "project_id": project.id,
"name": "hello", "name": "hello",
"startup_config": "ip 192" "startup_config": "ip 192"
}, },
project_id=project.id, project_id=project.id,
compute_id=1)) compute_id=1)
assert node.name == "hello" assert node.name == "hello"
action, event, _ = async_run(queue.get(5)) action, event, _ = await queue.get(5)
assert action == "node.updated" assert action == "node.updated"
assert event["name"] == "hello" assert event["name"] == "hello"
assert event["properties"]["startup_config"] == "ip 192" assert event["properties"]["startup_config"] == "ip 192"
def test_various_notification(controller, node): def test_various_notification(controller, node):
notif = controller.notification notif = controller.notification
notif.project_emit("log.info", {"message": "Image uploaded"}) notif.project_emit("log.info", {"message": "Image uploaded"})
notif.project_emit("log.warning", {"message": "Warning ASA 8 is not officialy supported by GNS3"}) notif.project_emit("log.warning", {"message": "Warning ASA 8 is not officially supported by GNS3"})
notif.project_emit("log.error", {"message": "Permission denied on /tmp"}) notif.project_emit("log.error", {"message": "Permission denied on /tmp"})
notif.project_emit("node.updated", node.__json__()) notif.project_emit("node.updated", node.__json__())

@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -34,33 +34,29 @@ from gns3server.config import Config
@pytest.fixture @pytest.fixture
def project(controller): async def node(controller, project):
return Project(controller=controller, name="Test")
@pytest.fixture
def node(controller, project, async_run):
compute = MagicMock() compute = MagicMock()
compute.id = "local" compute.id = "local"
response = MagicMock() response = MagicMock()
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
node = await project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"})
node = async_run(project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"}))
return node return node
def test_affect_uuid(): async def test_affect_uuid():
p = Project(name="Test") p = Project(name="Test")
assert len(p.id) == 36 assert len(p.id) == 36
p = Project(project_id='00010203-0405-0607-0809-0a0b0c0d0e0f', name="Test 2") p = Project(project_id='00010203-0405-0607-0809-0a0b0c0d0e0f', name="Test 2")
assert p.id == '00010203-0405-0607-0809-0a0b0c0d0e0f' assert p.id == '00010203-0405-0607-0809-0a0b0c0d0e0f'
def test_json(tmpdir): async def test_json():
p = Project(name="Test") p = Project(name="Test")
assert p.__json__() == { assert p.__json__() == {
"name": "Test", "name": "Test",
"project_id": p.id, "project_id": p.id,
@ -84,34 +80,31 @@ def test_json(tmpdir):
} }
def test_update(controller, async_run): async def test_update(controller):
project = Project(controller=controller, name="Hello") project = Project(controller=controller, name="Hello")
project.emit_notification = MagicMock() project.emit_notification = MagicMock()
assert project.name == "Hello" assert project.name == "Hello"
async_run(project.update(name="World")) await project.update(name="World")
assert project.name == "World" assert project.name == "World"
project.emit_notification.assert_any_call("project.updated", project.__json__()) project.emit_notification.assert_any_call("project.updated", project.__json__())
def test_update_on_compute(controller, async_run): async def test_update_on_compute(controller):
variables = [{"name": "TEST", "value": "VAL1"}] variables = [{"name": "TEST", "value": "VAL1"}]
compute = MagicMock() compute = MagicMock()
compute.id = "local" compute.id = "local"
project = Project(controller=controller, name="Test") project = Project(controller=controller, name="Test")
project._project_created_on_compute = [compute] project._project_created_on_compute = [compute]
project.emit_notification = MagicMock() project.emit_notification = MagicMock()
await project.update(variables=variables)
compute.put.assert_any_call('/projects/{}'.format(project.id), {"variables": variables})
async_run(project.update(variables=variables))
compute.put.assert_any_call('/projects/{}'.format(project.id), {
"variables": variables
})
async def test_path(projects_dir):
def test_path(tmpdir): directory = projects_dir
directory = Config.instance().get_section_config("Server").get("projects_path")
with patch("gns3server.utils.path.get_default_project_directory", return_value=directory): with patch("gns3server.utils.path.get_default_project_directory", return_value=directory):
p = Project(project_id=str(uuid4()), name="Test") p = Project(project_id=str(uuid4()), name="Test")
assert p.path == os.path.join(directory, p.id) assert p.path == os.path.join(directory, p.id)
@ -120,37 +113,41 @@ def test_path(tmpdir):
def test_path_exist(tmpdir): def test_path_exist(tmpdir):
""" """
Should raise an error when you try to owerwrite Should raise an error when you try to overwrite
an existing project an existing project
""" """
os.makedirs(str(tmpdir / "demo")) os.makedirs(str(tmpdir / "demo"))
with pytest.raises(aiohttp.web.HTTPForbidden): with pytest.raises(aiohttp.web.HTTPForbidden):
p = Project(name="Test", path=str(tmpdir / "demo")) Project(name="Test", path=str(tmpdir / "demo"))
def test_init_path(tmpdir): async def test_init_path(tmpdir):
p = Project(path=str(tmpdir), project_id=str(uuid4()), name="Test") p = Project(path=str(tmpdir), project_id=str(uuid4()), name="Test")
assert p.path == str(tmpdir) assert p.path == str(tmpdir)
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") @pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
def test_changing_path_with_quote_not_allowed(tmpdir): async def test_changing_path_with_quote_not_allowed(tmpdir):
with pytest.raises(aiohttp.web.HTTPForbidden): with pytest.raises(aiohttp.web.HTTPForbidden):
p = Project(project_id=str(uuid4()), name="Test") p = Project(project_id=str(uuid4()), name="Test")
p.path = str(tmpdir / "project\"53") p.path = str(tmpdir / "project\"53")
def test_captures_directory(tmpdir): async def test_captures_directory(tmpdir):
p = Project(path=str(tmpdir / "capturestest"), name="Test") p = Project(path=str(tmpdir / "capturestest"), name="Test")
assert p.captures_directory == str(tmpdir / "capturestest" / "project-files" / "captures") assert p.captures_directory == str(tmpdir / "capturestest" / "project-files" / "captures")
assert os.path.exists(p.captures_directory) assert os.path.exists(p.captures_directory)
def test_add_node_local(async_run, controller): async def test_add_node_local(controller):
""" """
For a local server we send the project path For a local server we send the project path
""" """
compute = MagicMock() compute = MagicMock()
compute.id = "local" compute.id = "local"
project = Project(controller=controller, name="Test") project = Project(controller=controller, name="Test")
@ -160,7 +157,7 @@ def test_add_node_local(async_run, controller):
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
node = async_run(project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_script": "test.cfg"})) node = await project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_script": "test.cfg"})
assert node.id in project._nodes assert node.id in project._nodes
compute.post.assert_any_call('/projects', data={ compute.post.assert_any_call('/projects', data={
@ -177,10 +174,11 @@ def test_add_node_local(async_run, controller):
project.emit_notification.assert_any_call("node.created", node.__json__()) project.emit_notification.assert_any_call("node.created", node.__json__())
def test_add_node_non_local(async_run, controller): async def test_add_node_non_local(controller):
""" """
For a non local server we do not send the project path For a non local server we do not send the project path
""" """
compute = MagicMock() compute = MagicMock()
compute.id = "remote" compute.id = "remote"
project = Project(controller=controller, name="Test") project = Project(controller=controller, name="Test")
@ -190,67 +188,66 @@ def test_add_node_non_local(async_run, controller):
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
node = async_run(project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_script": "test.cfg"})) node = await project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_script": "test.cfg"})
compute.post.assert_any_call('/projects', data={ compute.post.assert_any_call('/projects', data={
"name": project._name, "name": project._name,
"project_id": project._id "project_id": project._id
}) })
compute.post.assert_any_call('/projects/{}/vpcs/nodes'.format(project.id), compute.post.assert_any_call('/projects/{}/vpcs/nodes'.format(project.id), data={'node_id': node.id,
data={'node_id': node.id, 'startup_script': 'test.cfg',
'startup_script': 'test.cfg', 'name': 'test'}, timeout=1200)
'name': 'test'},
timeout=1200)
assert compute in project._project_created_on_compute assert compute in project._project_created_on_compute
project.emit_notification.assert_any_call("node.created", node.__json__()) project.emit_notification.assert_any_call("node.created", node.__json__())
def test_add_node_iou(async_run, controller): async def test_add_node_iou(controller):
""" """
Test if an application ID is allocated for IOU nodes Test if an application ID is allocated for IOU nodes
""" """
compute = MagicMock() compute = MagicMock()
compute.id = "local" compute.id = "local"
project = async_run(controller.add_project(project_id=str(uuid.uuid4()), name="test1")) project = await controller.add_project(project_id=str(uuid.uuid4()), name="test1")
project.emit_notification = MagicMock() project.emit_notification = MagicMock()
response = MagicMock() response = MagicMock()
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
node1 = async_run(project.add_node(compute, "test1", None, node_type="iou")) node1 = await project.add_node(compute, "test1", None, node_type="iou")
node2 = async_run(project.add_node(compute, "test2", None, node_type="iou")) node2 = await project.add_node(compute, "test2", None, node_type="iou")
node3 = async_run(project.add_node(compute, "test3", None, node_type="iou")) node3 = await project.add_node(compute, "test3", None, node_type="iou")
assert node1.properties["application_id"] == 1 assert node1.properties["application_id"] == 1
assert node2.properties["application_id"] == 2 assert node2.properties["application_id"] == 2
assert node3.properties["application_id"] == 3 assert node3.properties["application_id"] == 3
def test_add_node_iou_with_multiple_projects(async_run, controller): async def test_add_node_iou_with_multiple_projects(controller):
""" """
Test if an application ID is allocated for IOU nodes with different projects already opened Test if an application ID is allocated for IOU nodes with different projects already opened
""" """
compute = MagicMock() compute = MagicMock()
compute.id = "local" compute.id = "local"
project1 = async_run(controller.add_project(project_id=str(uuid.uuid4()), name="test1")) project1 = await controller.add_project(project_id=str(uuid.uuid4()), name="test1")
project1.emit_notification = MagicMock() project1.emit_notification = MagicMock()
project2 = async_run(controller.add_project(project_id=str(uuid.uuid4()), name="test2")) project2 = await controller.add_project(project_id=str(uuid.uuid4()), name="test2")
project2.emit_notification = MagicMock() project2.emit_notification = MagicMock()
project3 = async_run(controller.add_project(project_id=str(uuid.uuid4()), name="test3")) project3 = await controller.add_project(project_id=str(uuid.uuid4()), name="test3")
project3.emit_notification = MagicMock() project3.emit_notification = MagicMock()
response = MagicMock() response = MagicMock()
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
node1 = async_run(project1.add_node(compute, "test1", None, node_type="iou")) node1 = await project1.add_node(compute, "test1", None, node_type="iou")
node2 = async_run(project1.add_node(compute, "test2", None, node_type="iou")) node2 = await project1.add_node(compute, "test2", None, node_type="iou")
node3 = async_run(project1.add_node(compute, "test3", None, node_type="iou")) node3 = await project1.add_node(compute, "test3", None, node_type="iou")
node4 = async_run(project2.add_node(compute, "test4", None, node_type="iou")) node4 = await project2.add_node(compute, "test4", None, node_type="iou")
node5 = async_run(project2.add_node(compute, "test5", None, node_type="iou")) node5 = await project2.add_node(compute, "test5", None, node_type="iou")
node6 = async_run(project2.add_node(compute, "test6", None, node_type="iou")) node6 = await project2.add_node(compute, "test6", None, node_type="iou")
node7 = async_run(project3.add_node(compute, "test7", None, node_type="iou")) node7 = await project3.add_node(compute, "test7", None, node_type="iou")
node8 = async_run(project3.add_node(compute, "test8", None, node_type="iou")) node8 = await project3.add_node(compute, "test8", None, node_type="iou")
node9 = async_run(project3.add_node(compute, "test9", None, node_type="iou")) node9 = await project3.add_node(compute, "test9", None, node_type="iou")
assert node1.properties["application_id"] == 1 assert node1.properties["application_id"] == 1
assert node2.properties["application_id"] == 2 assert node2.properties["application_id"] == 2
@ -265,19 +262,19 @@ def test_add_node_iou_with_multiple_projects(async_run, controller):
assert node9.properties["application_id"] == 9 assert node9.properties["application_id"] == 9
controller.remove_project(project1) controller.remove_project(project1)
project4 = async_run(controller.add_project(project_id=str(uuid.uuid4()), name="test4")) project4 = await controller.add_project(project_id=str(uuid.uuid4()), name="test4")
project4.emit_notification = MagicMock() project4.emit_notification = MagicMock()
node10 = async_run(project3.add_node(compute, "test10", None, node_type="iou")) node10 = await project3.add_node(compute, "test10", None, node_type="iou")
node11 = async_run(project3.add_node(compute, "test11", None, node_type="iou")) node11 = await project3.add_node(compute, "test11", None, node_type="iou")
node12 = async_run(project3.add_node(compute, "test12", None, node_type="iou")) node12 = await project3.add_node(compute, "test12", None, node_type="iou")
assert node10.properties["application_id"] == 1 assert node10.properties["application_id"] == 1
assert node11.properties["application_id"] == 2 assert node11.properties["application_id"] == 2
assert node12.properties["application_id"] == 3 assert node12.properties["application_id"] == 3
def test_add_node_iou_with_multiple_projects_different_computes(async_run, controller): async def test_add_node_iou_with_multiple_projects_different_computes(controller):
""" """
Test if an application ID is allocated for IOU nodes with different projects already opened Test if an application ID is allocated for IOU nodes with different projects already opened
""" """
@ -285,19 +282,19 @@ def test_add_node_iou_with_multiple_projects_different_computes(async_run, contr
compute1.id = "remote1" compute1.id = "remote1"
compute2 = MagicMock() compute2 = MagicMock()
compute2.id = "remote2" compute2.id = "remote2"
project1 = async_run(controller.add_project(project_id=str(uuid.uuid4()), name="test1")) project1 = await controller.add_project(project_id=str(uuid.uuid4()), name="test1")
project1.emit_notification = MagicMock() project1.emit_notification = MagicMock()
project2 = async_run(controller.add_project(project_id=str(uuid.uuid4()), name="test2")) project2 = await controller.add_project(project_id=str(uuid.uuid4()), name="test2")
project2.emit_notification = MagicMock() project2.emit_notification = MagicMock()
response = MagicMock() response = MagicMock()
compute1.post = AsyncioMagicMock(return_value=response) compute1.post = AsyncioMagicMock(return_value=response)
compute2.post = AsyncioMagicMock(return_value=response) compute2.post = AsyncioMagicMock(return_value=response)
node1 = async_run(project1.add_node(compute1, "test1", None, node_type="iou")) node1 = await project1.add_node(compute1, "test1", None, node_type="iou")
node2 = async_run(project1.add_node(compute1, "test2", None, node_type="iou")) node2 = await project1.add_node(compute1, "test2", None, node_type="iou")
node3 = async_run(project2.add_node(compute2, "test3", None, node_type="iou")) node3 = await project2.add_node(compute2, "test3", None, node_type="iou")
node4 = async_run(project2.add_node(compute2, "test4", None, node_type="iou")) node4 = await project2.add_node(compute2, "test4", None, node_type="iou")
assert node1.properties["application_id"] == 1 assert node1.properties["application_id"] == 1
assert node2.properties["application_id"] == 2 assert node2.properties["application_id"] == 2
@ -305,20 +302,21 @@ def test_add_node_iou_with_multiple_projects_different_computes(async_run, contr
assert node3.properties["application_id"] == 1 assert node3.properties["application_id"] == 1
assert node4.properties["application_id"] == 2 assert node4.properties["application_id"] == 2
node5 = async_run(project1.add_node(compute2, "test5", None, node_type="iou")) node5 = await project1.add_node(compute2, "test5", None, node_type="iou")
node6 = async_run(project2.add_node(compute1, "test6", None, node_type="iou")) node6 = await project2.add_node(compute1, "test6", None, node_type="iou")
assert node5.properties["application_id"] == 3 assert node5.properties["application_id"] == 3
assert node6.properties["application_id"] == 4 assert node6.properties["application_id"] == 4
def test_add_node_iou_no_id_available(async_run, controller): async def test_add_node_iou_no_id_available(controller):
""" """
Test if an application ID is allocated for IOU nodes Test if an application ID is allocated for IOU nodes
""" """
compute = MagicMock() compute = MagicMock()
compute.id = "local" compute.id = "local"
project = async_run(controller.add_project(project_id=str(uuid.uuid4()), name="test")) project = await controller.add_project(project_id=str(uuid.uuid4()), name="test")
project.emit_notification = MagicMock() project.emit_notification = MagicMock()
response = MagicMock() response = MagicMock()
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
@ -327,13 +325,14 @@ def test_add_node_iou_no_id_available(async_run, controller):
for i in range(1, 513): for i in range(1, 513):
prop = {"properties": {"application_id": i}} prop = {"properties": {"application_id": i}}
project._nodes[i] = Node(project, compute, "Node{}".format(i), node_id=i, node_type="iou", **prop) project._nodes[i] = Node(project, compute, "Node{}".format(i), node_id=i, node_type="iou", **prop)
async_run(project.add_node(compute, "test1", None, node_type="iou")) await project.add_node(compute, "test1", None, node_type="iou")
def test_add_node_from_template(async_run, controller): async def test_add_node_from_template(controller):
""" """
For a local server we send the project path For a local server we send the project path
""" """
compute = MagicMock() compute = MagicMock()
compute.id = "local" compute.id = "local"
project = Project(controller=controller, name="Test") project = Project(controller=controller, name="Test")
@ -351,7 +350,7 @@ def test_add_node_from_template(async_run, controller):
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
node = async_run(project.add_node_from_template(template.id, x=23, y=12)) node = await project.add_node_from_template(template.id, x=23, y=12)
compute.post.assert_any_call('/projects', data={ compute.post.assert_any_call('/projects', data={
"name": project._name, "name": project._name,
"project_id": project._id, "project_id": project._id,
@ -362,10 +361,11 @@ def test_add_node_from_template(async_run, controller):
project.emit_notification.assert_any_call("node.created", node.__json__()) project.emit_notification.assert_any_call("node.created", node.__json__())
def test_add_builtin_node_from_template(async_run, controller): async def test_add_builtin_node_from_template(controller):
""" """
For a local server we send the project path For a local server we send the project path
""" """
compute = MagicMock() compute = MagicMock()
compute.id = "local" compute.id = "local"
project = Project(controller=controller, name="Test") project = Project(controller=controller, name="Test")
@ -374,6 +374,7 @@ def test_add_builtin_node_from_template(async_run, controller):
"name": "Builtin-switch", "name": "Builtin-switch",
"template_type": "ethernet_switch", "template_type": "ethernet_switch",
}, builtin=True) }, builtin=True)
controller.template_manager.templates[template.id] = template controller.template_manager.templates[template.id] = template
template.__json__() template.__json__()
controller._computes["local"] = compute controller._computes["local"] = compute
@ -382,7 +383,7 @@ def test_add_builtin_node_from_template(async_run, controller):
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
node = async_run(project.add_node_from_template(template.id, x=23, y=12, compute_id="local")) node = await project.add_node_from_template(template.id, x=23, y=12, compute_id="local")
compute.post.assert_any_call('/projects', data={ compute.post.assert_any_call('/projects', data={
"name": project._name, "name": project._name,
"project_id": project._id, "project_id": project._id,
@ -393,7 +394,7 @@ def test_add_builtin_node_from_template(async_run, controller):
project.emit_notification.assert_any_call("node.created", node.__json__()) project.emit_notification.assert_any_call("node.created", node.__json__())
def test_delete_node(async_run, controller): async def test_delete_node(controller):
""" """
For a local server we send the project path For a local server we send the project path
""" """
@ -405,19 +406,20 @@ def test_delete_node(async_run, controller):
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
node = async_run(project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"})) node = await project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"})
assert node.id in project._nodes assert node.id in project._nodes
async_run(project.delete_node(node.id)) await project.delete_node(node.id)
assert node.id not in project._nodes assert node.id not in project._nodes
compute.delete.assert_any_call('/projects/{}/vpcs/nodes/{}'.format(project.id, node.id)) compute.delete.assert_any_call('/projects/{}/vpcs/nodes/{}'.format(project.id, node.id))
project.emit_notification.assert_any_call("node.deleted", node.__json__()) project.emit_notification.assert_any_call("node.deleted", node.__json__())
def test_delete_locked_node(async_run, controller): async def test_delete_locked_node(controller):
""" """
For a local server we send the project path For a local server we send the project path
""" """
compute = MagicMock() compute = MagicMock()
project = Project(controller=controller, name="Test") project = Project(controller=controller, name="Test")
project.emit_notification = MagicMock() project.emit_notification = MagicMock()
@ -426,14 +428,14 @@ def test_delete_locked_node(async_run, controller):
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
node = async_run(project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"})) node = await project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"})
assert node.id in project._nodes assert node.id in project._nodes
node.locked = True node.locked = True
with pytest.raises(aiohttp.web_exceptions.HTTPConflict): with pytest.raises(aiohttp.web_exceptions.HTTPConflict):
async_run(project.delete_node(node.id)) await project.delete_node(node.id)
def test_delete_node_delete_link(async_run, controller): async def test_delete_node_delete_link(controller):
""" """
Delete a node delete all the node connected Delete a node delete all the node connected
""" """
@ -445,12 +447,12 @@ def test_delete_node_delete_link(async_run, controller):
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
node = async_run(project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"})) node = await project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"})
link = async_run(project.add_link()) link = await project.add_link()
async_run(link.add_node(node, 0, 0)) await link.add_node(node, 0, 0)
async_run(project.delete_node(node.id)) await project.delete_node(node.id)
assert node.id not in project._nodes assert node.id not in project._nodes
assert link.id not in project._links assert link.id not in project._links
@ -459,7 +461,8 @@ def test_delete_node_delete_link(async_run, controller):
project.emit_notification.assert_any_call("link.deleted", link.__json__()) project.emit_notification.assert_any_call("link.deleted", link.__json__())
def test_get_node(async_run, controller): async def test_get_node(controller):
compute = MagicMock() compute = MagicMock()
project = Project(controller=controller, name="Test") project = Project(controller=controller, name="Test")
@ -467,18 +470,20 @@ def test_get_node(async_run, controller):
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
vm = async_run(project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"})) vm = await project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"})
assert project.get_node(vm.id) == vm assert project.get_node(vm.id) == vm
with pytest.raises(aiohttp.web_exceptions.HTTPNotFound): with pytest.raises(aiohttp.web_exceptions.HTTPNotFound):
project.get_node("test") project.get_node("test")
# Raise an error if the project is not opened # Raise an error if the project is not opened
async_run(project.close()) await project.close()
with pytest.raises(aiohttp.web.HTTPForbidden): with pytest.raises(aiohttp.web.HTTPForbidden):
project.get_node(vm.id) project.get_node(vm.id)
def test_list_nodes(async_run, controller):
async def test_list_nodes(controller):
compute = MagicMock() compute = MagicMock()
project = Project(controller=controller, name="Test") project = Project(controller=controller, name="Test")
@ -486,157 +491,163 @@ def test_list_nodes(async_run, controller):
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
vm = async_run(project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"})) vm = await project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"})
assert len(project.nodes) == 1 assert len(project.nodes) == 1
assert isinstance(project.nodes, dict) assert isinstance(project.nodes, dict)
async_run(project.close()) await project.close()
assert len(project.nodes) == 1 assert len(project.nodes) == 1
assert isinstance(project.nodes, dict) assert isinstance(project.nodes, dict)
def test_add_link(async_run, project, controller): async def test_add_link(project):
compute = MagicMock() compute = MagicMock()
response = MagicMock() response = MagicMock()
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
vm1 = async_run(project.add_node(compute, "test1", None, node_type="vpcs", properties={"startup_config": "test.cfg"})) vm1 = await project.add_node(compute, "test1", None, node_type="vpcs", properties={"startup_config": "test.cfg"})
vm1._ports = [EthernetPort("E0", 0, 3, 1)] 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 = await project.add_node(compute, "test2", None, node_type="vpcs", properties={"startup_config": "test.cfg"})
vm2._ports = [EthernetPort("E0", 0, 4, 2)] vm2._ports = [EthernetPort("E0", 0, 4, 2)]
project.emit_notification = MagicMock() project.emit_notification = MagicMock()
link = async_run(project.add_link()) link = await project.add_link()
async_run(link.add_node(vm1, 3, 1)) await link.add_node(vm1, 3, 1)
with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock_udp_create: with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock_udp_create:
async_run(link.add_node(vm2, 4, 2)) await link.add_node(vm2, 4, 2)
assert mock_udp_create.called assert mock_udp_create.called
assert len(link._nodes) == 2 assert len(link._nodes) == 2
project.emit_notification.assert_any_call("link.created", link.__json__()) project.emit_notification.assert_any_call("link.created", link.__json__())
def test_list_links(async_run, project): async def test_list_links(project):
compute = MagicMock()
compute = MagicMock()
response = MagicMock() response = MagicMock()
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
link = async_run(project.add_link()) await project.add_link()
assert len(project.links) == 1 assert len(project.links) == 1
async_run(project.close()) await project.close()
assert len(project.links) == 1 assert len(project.links) == 1
def test_get_link(async_run, project): async def test_get_link(project):
compute = MagicMock()
compute = MagicMock()
response = MagicMock() response = MagicMock()
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
link = async_run(project.add_link()) link = await project.add_link()
assert project.get_link(link.id) == link assert project.get_link(link.id) == link
with pytest.raises(aiohttp.web_exceptions.HTTPNotFound): with pytest.raises(aiohttp.web_exceptions.HTTPNotFound):
project.get_link("test") project.get_link("test")
def test_delete_link(async_run, project, controller): async def test_delete_link(project):
compute = MagicMock()
compute = MagicMock()
response = MagicMock() response = MagicMock()
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
assert len(project._links) == 0 assert len(project._links) == 0
link = async_run(project.add_link()) link = await project.add_link()
assert len(project._links) == 1 assert len(project._links) == 1
project.emit_notification = MagicMock() project.emit_notification = MagicMock()
async_run(project.delete_link(link.id)) await project.delete_link(link.id)
project.emit_notification.assert_any_call("link.deleted", link.__json__()) project.emit_notification.assert_any_call("link.deleted", link.__json__())
assert len(project._links) == 0 assert len(project._links) == 0
def test_add_drawing(async_run, project, controller): async def test_add_drawing(project):
project.emit_notification = MagicMock()
drawing = async_run(project.add_drawing(None, svg="<svg></svg>")) project.emit_notification = MagicMock()
drawing = await project.add_drawing(None, svg="<svg></svg>")
assert len(project._drawings) == 1 assert len(project._drawings) == 1
project.emit_notification.assert_any_call("drawing.created", drawing.__json__()) project.emit_notification.assert_any_call("drawing.created", drawing.__json__())
def test_get_drawing(async_run, project): async def test_get_drawing(project):
drawing = async_run(project.add_drawing(None))
drawing = await project.add_drawing(None)
assert project.get_drawing(drawing.id) == drawing assert project.get_drawing(drawing.id) == drawing
with pytest.raises(aiohttp.web_exceptions.HTTPNotFound): with pytest.raises(aiohttp.web_exceptions.HTTPNotFound):
project.get_drawing("test") project.get_drawing("test")
def test_list_drawing(async_run, project): async def test_list_drawing(project):
drawing = async_run(project.add_drawing(None))
await project.add_drawing(None)
assert len(project.drawings) == 1 assert len(project.drawings) == 1
async_run(project.close()) await project.close()
assert len(project.drawings) == 1 assert len(project.drawings) == 1
def test_delete_drawing(async_run, project, controller): async def test_delete_drawing(project):
assert len(project._drawings) == 0 assert len(project._drawings) == 0
drawing = async_run(project.add_drawing()) drawing = await project.add_drawing()
assert len(project._drawings) == 1 assert len(project._drawings) == 1
project.emit_notification = MagicMock() project.emit_notification = MagicMock()
async_run(project.delete_drawing(drawing.id)) await project.delete_drawing(drawing.id)
project.emit_notification.assert_any_call("drawing.deleted", drawing.__json__()) project.emit_notification.assert_any_call("drawing.deleted", drawing.__json__())
assert len(project._drawings) == 0 assert len(project._drawings) == 0
def test_clean_pictures(async_run, project, controller): async def test_clean_pictures(project):
""" """
When a project is close old pictures should be removed When a project is close old pictures should be removed
""" """
drawing = async_run(project.add_drawing()) drawing = await project.add_drawing()
drawing._svg = "test.png" drawing._svg = "test.png"
open(os.path.join(project.pictures_directory, "test.png"), "w+").close() open(os.path.join(project.pictures_directory, "test.png"), "w+").close()
open(os.path.join(project.pictures_directory, "test2.png"), "w+").close() open(os.path.join(project.pictures_directory, "test2.png"), "w+").close()
async_run(project.close()) await project.close()
assert os.path.exists(os.path.join(project.pictures_directory, "test.png")) assert os.path.exists(os.path.join(project.pictures_directory, "test.png"))
assert not os.path.exists(os.path.join(project.pictures_directory, "test2.png")) assert not os.path.exists(os.path.join(project.pictures_directory, "test2.png"))
def test_clean_pictures_and_keep_supplier_logo(async_run, project, controller): async def test_clean_pictures_and_keep_supplier_logo(project):
""" """
When a project is close old pictures should be removed When a project is close old pictures should be removed
""" """
project.supplier = { project.supplier = {
'logo': 'logo.png' 'logo': 'logo.png'
} }
drawing = async_run(project.add_drawing()) drawing = await project.add_drawing()
drawing._svg = "test.png" drawing._svg = "test.png"
open(os.path.join(project.pictures_directory, "test.png"), "w+").close() open(os.path.join(project.pictures_directory, "test.png"), "w+").close()
open(os.path.join(project.pictures_directory, "test2.png"), "w+").close() open(os.path.join(project.pictures_directory, "test2.png"), "w+").close()
open(os.path.join(project.pictures_directory, "logo.png"), "w+").close() open(os.path.join(project.pictures_directory, "logo.png"), "w+").close()
async_run(project.close()) await project.close()
assert os.path.exists(os.path.join(project.pictures_directory, "test.png")) assert os.path.exists(os.path.join(project.pictures_directory, "test.png"))
assert not os.path.exists(os.path.join(project.pictures_directory, "test2.png")) assert not os.path.exists(os.path.join(project.pictures_directory, "test2.png"))
assert os.path.exists(os.path.join(project.pictures_directory, "logo.png")) assert os.path.exists(os.path.join(project.pictures_directory, "logo.png"))
def test_delete(async_run, project, controller): async def test_delete(project):
assert os.path.exists(project.path) assert os.path.exists(project.path)
async_run(project.delete()) await project.delete()
assert not os.path.exists(project.path) assert not os.path.exists(project.path)
def test_dump(): async def test_dump(projects_dir):
directory = Config.instance().get_section_config("Server").get("projects_path")
directory = projects_dir
with patch("gns3server.utils.path.get_default_project_directory", return_value=directory): with patch("gns3server.utils.path.get_default_project_directory", return_value=directory):
p = Project(project_id='00010203-0405-0607-0809-0a0b0c0d0e0f', name="Test") p = Project(project_id='00010203-0405-0607-0809-0a0b0c0d0e0f', name="Test")
p.dump() p.dump()
@ -645,30 +656,32 @@ def test_dump():
assert "00010203-0405-0607-0809-0a0b0c0d0e0f" in content assert "00010203-0405-0607-0809-0a0b0c0d0e0f" in content
def test_open_close(async_run, controller): async def test_open_close(controller):
project = Project(controller=controller, name="Test") project = Project(controller=controller, name="Test")
assert project.status == "opened" assert project.status == "opened"
async_run(project.close()) await project.close()
project.start_all = AsyncioMagicMock() project.start_all = AsyncioMagicMock()
async_run(project.open()) await project.open()
assert not project.start_all.called assert not project.start_all.called
assert project.status == "opened" assert project.status == "opened"
project.emit_notification = MagicMock() project.emit_notification = MagicMock()
async_run(project.close()) await project.close()
assert project.status == "closed" assert project.status == "closed"
project.emit_notification.assert_any_call("project.closed", project.__json__()) project.emit_notification.assert_any_call("project.closed", project.__json__())
def test_open_auto_start(async_run, controller): async def test_open_auto_start(controller):
project = Project(controller=controller, name="Test", auto_start=True) project = Project(controller=controller, name="Test", auto_start=True)
assert project.status == "opened" assert project.status == "opened"
async_run(project.close()) await project.close()
project.start_all = AsyncioMagicMock() project.start_all = AsyncioMagicMock()
async_run(project.open()) await project.open()
assert project.start_all.called assert project.start_all.called
def test_is_running(project, async_run, node): def test_is_running(project, node):
""" """
If a node is started or paused return True If a node is started or paused return True
""" """
@ -678,11 +691,12 @@ def test_is_running(project, async_run, node):
assert project.is_running() is True assert project.is_running() is True
def test_duplicate(project, async_run, controller): async def test_duplicate(project, controller):
""" """
Duplicate a project, the node should remain on the remote server Duplicate a project, the node should remain on the remote server
if they were on remote server if they were on remote server
""" """
compute = MagicMock() compute = MagicMock()
compute.id = "remote" compute.id = "remote"
compute.list_files = AsyncioMagicMock(return_value=[]) compute.list_files = AsyncioMagicMock(return_value=[])
@ -692,16 +706,16 @@ def test_duplicate(project, async_run, controller):
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
remote_vpcs = async_run(project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"})) remote_vpcs = await project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"})
# We allow node not allowed for standard import / export # We allow node not allowed for standard import / export
remote_virtualbox = async_run(project.add_node(compute, "test", None, node_type="vmware", properties={"startup_config": "test.cfg"})) remote_virtualbox = await project.add_node(compute, "test", None, node_type="vmware", properties={"startup_config": "test.cfg"})
new_project = async_run(project.duplicate(name="Hello")) new_project = await project.duplicate(name="Hello")
assert new_project.id != project.id assert new_project.id != project.id
assert new_project.name == "Hello" assert new_project.name == "Hello"
async_run(new_project.open()) await new_project.open()
assert list(new_project.nodes.values())[0].compute.id == "remote" assert list(new_project.nodes.values())[0].compute.id == "remote"
assert list(new_project.nodes.values())[1].compute.id == "remote" assert list(new_project.nodes.values())[1].compute.id == "remote"
@ -711,6 +725,7 @@ def test_snapshots(project):
""" """
List the snapshots List the snapshots
""" """
os.makedirs(os.path.join(project.path, "snapshots")) os.makedirs(os.path.join(project.path, "snapshots"))
open(os.path.join(project.path, "snapshots", "test1_260716_103713.gns3project"), "w+").close() open(os.path.join(project.path, "snapshots", "test1_260716_103713.gns3project"), "w+").close()
project.reset() project.reset()
@ -720,6 +735,7 @@ def test_snapshots(project):
def test_get_snapshot(project): def test_get_snapshot(project):
os.makedirs(os.path.join(project.path, "snapshots")) os.makedirs(os.path.join(project.path, "snapshots"))
open(os.path.join(project.path, "snapshots", "test1.gns3project"), "w+").close() open(os.path.join(project.path, "snapshots", "test1.gns3project"), "w+").close()
project.reset() project.reset()
@ -731,7 +747,8 @@ def test_get_snapshot(project):
project.get_snapshot("BLU") project.get_snapshot("BLU")
def test_delete_snapshot(project, async_run): async def test_delete_snapshot(project):
os.makedirs(os.path.join(project.path, "snapshots")) os.makedirs(os.path.join(project.path, "snapshots"))
open(os.path.join(project.path, "snapshots", "test1_260716_103713.gns3project"), "w+").close() open(os.path.join(project.path, "snapshots", "test1_260716_103713.gns3project"), "w+").close()
project.reset() project.reset()
@ -739,7 +756,7 @@ def test_delete_snapshot(project, async_run):
snapshot = list(project.snapshots.values())[0] snapshot = list(project.snapshots.values())[0]
assert project.get_snapshot(snapshot.id) == snapshot assert project.get_snapshot(snapshot.id) == snapshot
async_run(project.delete_snapshot(snapshot.id)) await project.delete_snapshot(snapshot.id)
with pytest.raises(aiohttp.web_exceptions.HTTPNotFound): with pytest.raises(aiohttp.web_exceptions.HTTPNotFound):
project.get_snapshot(snapshot.id) project.get_snapshot(snapshot.id)
@ -747,13 +764,13 @@ def test_delete_snapshot(project, async_run):
assert not os.path.exists(os.path.join(project.path, "snapshots", "test1.gns3project")) assert not os.path.exists(os.path.join(project.path, "snapshots", "test1.gns3project"))
def test_snapshot(project, async_run): async def test_snapshot(project):
""" """
Create a snapshot Create a snapshot
""" """
assert len(project.snapshots) == 0
snapshot = async_run(project.snapshot("test1")) assert len(project.snapshots) == 0
snapshot = await project.snapshot("test1")
assert snapshot.name == "test1" assert snapshot.name == "test1"
assert len(project.snapshots) == 1 assert len(project.snapshots) == 1
@ -761,10 +778,11 @@ def test_snapshot(project, async_run):
# Raise a conflict if name is already use # Raise a conflict if name is already use
with pytest.raises(aiohttp.web_exceptions.HTTPConflict): with pytest.raises(aiohttp.web_exceptions.HTTPConflict):
snapshot = async_run(project.snapshot("test1")) snapshot = await project.snapshot("test1")
def test_start_all(project, async_run): async def test_start_all(project):
compute = MagicMock() compute = MagicMock()
compute.id = "local" compute.id = "local"
response = MagicMock() response = MagicMock()
@ -772,14 +790,15 @@ def test_start_all(project, async_run):
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
for node_i in range(0, 10): for node_i in range(0, 10):
async_run(project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"})) await project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"})
compute.post = AsyncioMagicMock() compute.post = AsyncioMagicMock()
async_run(project.start_all()) await project.start_all()
assert len(compute.post.call_args_list) == 10 assert len(compute.post.call_args_list) == 10
def test_stop_all(project, async_run): async def test_stop_all(project):
compute = MagicMock() compute = MagicMock()
compute.id = "local" compute.id = "local"
response = MagicMock() response = MagicMock()
@ -787,14 +806,15 @@ def test_stop_all(project, async_run):
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
for node_i in range(0, 10): for node_i in range(0, 10):
async_run(project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"})) await project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"})
compute.post = AsyncioMagicMock() compute.post = AsyncioMagicMock()
async_run(project.stop_all()) await project.stop_all()
assert len(compute.post.call_args_list) == 10 assert len(compute.post.call_args_list) == 10
def test_suspend_all(project, async_run): async def test_suspend_all(project):
compute = MagicMock() compute = MagicMock()
compute.id = "local" compute.id = "local"
response = MagicMock() response = MagicMock()
@ -802,51 +822,53 @@ def test_suspend_all(project, async_run):
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
for node_i in range(0, 10): for node_i in range(0, 10):
async_run(project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"})) await project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"})
compute.post = AsyncioMagicMock() compute.post = AsyncioMagicMock()
async_run(project.suspend_all()) await project.suspend_all()
assert len(compute.post.call_args_list) == 10 assert len(compute.post.call_args_list) == 10
def test_node_name(project, async_run): async def test_node_name(project):
compute = MagicMock() compute = MagicMock()
compute.id = "local" compute.id = "local"
response = MagicMock() response = MagicMock()
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
node = async_run(project.add_node(compute, "test-{0}", None, node_type="vpcs", properties={"startup_config": "test.cfg"})) node = await project.add_node(compute, "test-{0}", None, node_type="vpcs", properties={"startup_config": "test.cfg"})
assert node.name == "test-1" assert node.name == "test-1"
node = async_run(project.add_node(compute, "test-{0}", None, node_type="vpcs", properties={"startup_config": "test.cfg"})) node = await project.add_node(compute, "test-{0}", None, node_type="vpcs", properties={"startup_config": "test.cfg"})
assert node.name == "test-2" assert node.name == "test-2"
node = async_run(project.add_node(compute, "hello world-{0}", None, node_type="vpcs", properties={"startup_config": "test.cfg"})) node = await project.add_node(compute, "hello world-{0}", None, node_type="vpcs", properties={"startup_config": "test.cfg"})
assert node.name == "helloworld-1" assert node.name == "helloworld-1"
node = async_run(project.add_node(compute, "hello world-{0}", None, node_type="vpcs", properties={"startup_config": "test.cfg"})) node = await project.add_node(compute, "hello world-{0}", None, node_type="vpcs", properties={"startup_config": "test.cfg"})
assert node.name == "helloworld-2" assert node.name == "helloworld-2"
node = async_run(project.add_node(compute, "VPCS-1", None, node_type="vpcs", properties={"startup_config": "test.cfg"})) node = await project.add_node(compute, "VPCS-1", None, node_type="vpcs", properties={"startup_config": "test.cfg"})
assert node.name == "VPCS-1" assert node.name == "VPCS-1"
node = async_run(project.add_node(compute, "VPCS-1", None, node_type="vpcs", properties={"startup_config": "test.cfg"})) node = await project.add_node(compute, "VPCS-1", None, node_type="vpcs", properties={"startup_config": "test.cfg"})
assert node.name == "VPCS-2" assert node.name == "VPCS-2"
node = async_run(project.add_node(compute, "R3", None, node_type="vpcs", properties={"startup_config": "test.cfg"})) node = await project.add_node(compute, "R3", None, node_type="vpcs", properties={"startup_config": "test.cfg"})
assert node.name == "R3" assert node.name == "R3"
def test_duplicate_node(project, async_run): async def test_duplicate_node(project):
compute = MagicMock() compute = MagicMock()
compute.id = "local" compute.id = "local"
response = MagicMock() response = MagicMock()
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
original = async_run(project.add_node( original = await project.add_node(
compute, compute,
"test", "test",
None, None,
node_type="vpcs", node_type="vpcs",
properties={ properties={
"startup_config": "test.cfg" "startup_config": "test.cfg"
})) })
new_node = async_run(project.duplicate_node(original, 42, 10, 11)) new_node = await project.duplicate_node(original, 42, 10, 11)
assert new_node.x == 42 assert new_node.x == 42

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2016 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -20,7 +20,7 @@ import json
import pytest import pytest
import aiohttp import aiohttp
from tests.utils import asyncio_patch, AsyncioMagicMock from tests.utils import asyncio_patch
from gns3server.controller.compute import Compute from gns3server.controller.compute import Compute
from gns3server.controller.project import Project from gns3server.controller.project import Project
@ -31,6 +31,7 @@ def demo_topology():
""" """
A topology with two VPCS connected and a rectangle A topology with two VPCS connected and a rectangle
""" """
return { return {
"auto_close": True, "auto_close": True,
"auto_open": False, "auto_open": False,
@ -145,29 +146,32 @@ def demo_topology():
} }
def test_load_project(controller, tmpdir, demo_topology, async_run, http_server): # async def test_load_project(controller, tmpdir, demo_topology, http_client):
with open(str(tmpdir / "demo.gns3"), "w+") as f: #
json.dump(demo_topology, f) # 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]) #
controller._computes["vm"] = controller._computes["local"] # controller._computes["local"] = Compute("local", controller=controller, host=http_client.host, port=http_client.port)
# controller._computes["vm"] = controller._computes["local"]
#
# with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.add_ubridge_udp_connection"):
# project = await 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"
# assert project.scene_height == 500
# assert project.scene_width == 700
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.add_ubridge_udp_connection"):
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" async def test_open(controller, tmpdir):
assert project.scene_height == 500
assert project.scene_width == 700
def test_open(controller, tmpdir, demo_topology, async_run, http_server):
simple_topology = { simple_topology = {
"auto_close": True, "auto_close": True,
"auto_open": False, "auto_open": False,
@ -190,33 +194,34 @@ def test_open(controller, tmpdir, demo_topology, async_run, http_server):
with open(str(tmpdir / "demo.gns3"), "w+") as f: with open(str(tmpdir / "demo.gns3"), "w+") as f:
json.dump(simple_topology, f) json.dump(simple_topology, f)
project = Project( project = Project(name="demo",
name="demo", project_id="64ba8408-afbf-4b66-9cdd-1fd854427478",
project_id="64ba8408-afbf-4b66-9cdd-1fd854427478", path=str(tmpdir),
path=str(tmpdir), controller=controller, filename="demo.gns3", status="closed") controller=controller,
filename="demo.gns3",
async_run(project.open()) status="closed")
await project.open()
assert project.status == "opened" assert project.status == "opened"
assert project.name == "demo" assert project.name == "demo"
assert project.scene_height == 500 assert project.scene_height == 500
assert project.scene_width == 700 assert project.scene_width == 700
def test_open_missing_compute(controller, tmpdir, demo_topology, async_run, http_server): # async def test_open_missing_compute(controller, tmpdir, demo_topology, http_client):
""" # """
If a compute is missing the project should not be open and the .gns3 should # If a compute is missing the project should not be open and the .gns3 should
be the one before opening the project # be the one before opening the project
""" # """
with open(str(tmpdir / "demo.gns3"), "w+") as f: #
json.dump(demo_topology, f) # 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]) #
# controller._computes["local"] = Compute("local", controller=controller, host=http_client.host, port=http_client.port)
with pytest.raises(aiohttp.web_exceptions.HTTPNotFound): #
project = async_run(controller.load_project(str(tmpdir / "demo.gns3"))) # with pytest.raises(aiohttp.web_exceptions.HTTPNotFound):
assert controller.get_project("3c1be6f9-b4ba-4737-b209-63c47c23359f").status == "closed" # await controller.load_project(str(tmpdir / "demo.gns3"))
with open(str(tmpdir / "demo.gns3"), "r") as f: # assert controller.get_project("3c1be6f9-b4ba-4737-b209-63c47c23359f").status == "closed"
topo = json.load(f) # with open(str(tmpdir / "demo.gns3"), "r") as f:
assert len(topo["topology"]["nodes"]) == 2 # topo = json.load(f)
# assert len(topo["topology"]["nodes"]) == 2

@ -25,17 +25,18 @@ from gns3server.controller.snapshot import Snapshot
from tests.utils import AsyncioMagicMock from tests.utils import AsyncioMagicMock
@pytest.fixture # @pytest.fixture
def project(controller): # def project(controller):
project = Project(controller=controller, name="Test") # project = Project(controller=controller, name="Test")
controller._projects[project.id] = project # controller._projects[project.id] = project
return project # return project
def test_snapshot_name(project): def test_snapshot_name(project):
""" """
Test create a snapshot object with a name Test create a snapshot object with a name
""" """
snapshot = Snapshot(project, name="test1") snapshot = Snapshot(project, name="test1")
assert snapshot.name == "test1" assert snapshot.name == "test1"
assert snapshot._created_at > 0 assert snapshot._created_at > 0
@ -51,6 +52,7 @@ def test_snapshot_filename(project):
""" """
Test create a snapshot object with a filename Test create a snapshot object with a filename
""" """
snapshot = Snapshot(project, filename="test1_260716_100439.gns3project") snapshot = Snapshot(project, filename="test1_260716_100439.gns3project")
assert snapshot.name == "test1" assert snapshot.name == "test1"
assert snapshot._created_at == 1469527479.0 assert snapshot._created_at == 1469527479.0
@ -58,6 +60,7 @@ def test_snapshot_filename(project):
def test_json(project): def test_json(project):
snapshot = Snapshot(project, filename="test1_260716_100439.gns3project") snapshot = Snapshot(project, filename="test1_260716_100439.gns3project")
assert snapshot.__json__() == { assert snapshot.__json__() == {
"snapshot_id": snapshot._id, "snapshot_id": snapshot._id,
@ -67,7 +70,8 @@ def test_json(project):
} }
def test_restore(project, controller, async_run): async def test_restore(project, controller):
compute = AsyncioMagicMock() compute = AsyncioMagicMock()
compute.id = "local" compute.id = "local"
controller._computes["local"] = compute controller._computes["local"] = compute
@ -75,12 +79,11 @@ def test_restore(project, controller, async_run):
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
async_run(project.add_node(compute, "test1", None, node_type="vpcs", properties={"startup_config": "test.cfg"})) await project.add_node(compute, "test1", None, node_type="vpcs", properties={"startup_config": "test.cfg"})
snapshot = await project.snapshot(name="test")
snapshot = async_run(project.snapshot(name="test"))
# We add a node after the snapshots # We add a node after the snapshots
async_run(project.add_node(compute, "test2", None, node_type="vpcs", properties={"startup_config": "test.cfg"})) await project.add_node(compute, "test2", None, node_type="vpcs", properties={"startup_config": "test.cfg"})
# project-files should be reset when reimporting # project-files should be reset when reimporting
test_file = os.path.join(project.path, "project-files", "test.txt") test_file = os.path.join(project.path, "project-files", "test.txt")
@ -92,7 +95,7 @@ def test_restore(project, controller, async_run):
controller._notification = MagicMock() controller._notification = MagicMock()
with patch("gns3server.config.Config.get_section_config", return_value={"local": True}): with patch("gns3server.config.Config.get_section_config", return_value={"local": True}):
async_run(snapshot.restore()) await snapshot.restore()
assert "snapshot.restored" in [c[0][0] for c in controller.notification.project_emit.call_args_list] assert "snapshot.restored" in [c[0][0] for c in controller.notification.project_emit.call_args_list]
# project.closed notification should not be send when restoring snapshots # project.closed notification should not be send when restoring snapshots

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2016 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -16,7 +16,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import os import os
from unittest.mock import patch
from gns3server.controller.symbols import Symbols from gns3server.controller.symbols import Symbols

@ -38,8 +38,9 @@ def test_template_json():
def test_template_json_with_not_known_category(): def test_template_json_with_not_known_category():
with pytest.raises(jsonschema.ValidationError): with pytest.raises(jsonschema.ValidationError):
a = Template(None, { Template(None, {
"node_type": "qemu", "node_type": "qemu",
"name": "Test", "name": "Test",
"default_name_format": "{name}-{0}", "default_name_format": "{name}-{0}",
@ -51,6 +52,7 @@ def test_template_json_with_not_known_category():
def test_template_json_with_platform(): def test_template_json_with_platform():
a = Template(None, { a = Template(None, {
"node_type": "dynamips", "node_type": "dynamips",
"name": "Test", "name": "Test",
@ -73,6 +75,7 @@ def test_template_fix_linked_base():
Version of the gui before 2.1 use linked_base and the server Version of the gui before 2.1 use linked_base and the server
linked_clone linked_clone
""" """
a = Template(None, { a = Template(None, {
"node_type": "qemu", "node_type": "qemu",
"name": "Test", "name": "Test",

@ -28,7 +28,8 @@ from gns3server.controller.topology import project_to_topology, load_topology, G
from gns3server.version import __version__ from gns3server.version import __version__
def test_project_to_topology_empty(tmpdir): async def test_project_to_topology_empty(tmpdir):
project = Project(name="Test") project = Project(name="Test")
topo = project_to_topology(project) topo = project_to_topology(project)
assert topo == { assert topo == {
@ -60,21 +61,22 @@ def test_project_to_topology_empty(tmpdir):
} }
def test_basic_topology(tmpdir, async_run, controller): async def test_basic_topology(controller):
project = Project(name="Test", controller=controller) project = Project(name="Test", controller=controller)
compute = Compute("my_compute", controller) compute = Compute("my_compute", controller)
compute.http_query = MagicMock() compute.http_query = MagicMock()
with asyncio_patch("gns3server.controller.node.Node.create"): with asyncio_patch("gns3server.controller.node.Node.create"):
node1 = async_run(project.add_node(compute, "Node 1", str(uuid.uuid4()), node_type="qemu")) node1 = await project.add_node(compute, "Node 1", str(uuid.uuid4()), node_type="qemu")
node2 = async_run(project.add_node(compute, "Node 2", str(uuid.uuid4()), node_type="qemu")) node2 = await project.add_node(compute, "Node 2", str(uuid.uuid4()), node_type="qemu")
link = async_run(project.add_link()) link = await project.add_link()
async_run(link.add_node(node1, 0, 0)) await link.add_node(node1, 0, 0)
with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock_udp_create: with asyncio_patch("gns3server.controller.udp_link.UDPLink.create"):
async_run(link.add_node(node2, 0, 0)) await link.add_node(node2, 0, 0)
drawing = async_run(project.add_drawing(svg="<svg></svg>")) drawing = await project.add_drawing(svg="<svg></svg>")
topo = project_to_topology(project) topo = project_to_topology(project)
assert len(topo["topology"]["nodes"]) == 2 assert len(topo["topology"]["nodes"]) == 2
@ -84,7 +86,8 @@ def test_basic_topology(tmpdir, async_run, controller):
assert topo["topology"]["drawings"][0] == drawing.__json__(topology_dump=True) assert topo["topology"]["drawings"][0] == drawing.__json__(topology_dump=True)
def test_project_to_topology(tmpdir, controller): async def test_project_to_topology(controller):
variables = [ variables = [
{"name": "TEST1"}, {"name": "TEST1"},
{"name": "TEST2", "value": "value1"} {"name": "TEST2", "value": "value1"}
@ -105,6 +108,7 @@ def test_project_to_topology(tmpdir, controller):
def test_load_topology(tmpdir): def test_load_topology(tmpdir):
data = { data = {
"project_id": "69f26504-7aa3-48aa-9f29-798d44841211", "project_id": "69f26504-7aa3-48aa-9f29-798d44841211",
"name": "Test", "name": "Test",
@ -126,19 +130,21 @@ def test_load_topology(tmpdir):
def test_load_topology_file_error(tmpdir): def test_load_topology_file_error(tmpdir):
path = str(tmpdir / "test.gns3") path = str(tmpdir / "test.gns3")
with pytest.raises(aiohttp.web.HTTPConflict): with pytest.raises(aiohttp.web.HTTPConflict):
topo = load_topology(path) load_topology(path)
def test_load_topology_file_error_schema_error(tmpdir): def test_load_topology_file_error_schema_error(tmpdir):
path = str(tmpdir / "test.gns3") path = str(tmpdir / "test.gns3")
with open(path, "w+") as f: with open(path, "w+") as f:
json.dump({ json.dump({
"revision": GNS3_FILE_FORMAT_REVISION "revision": GNS3_FILE_FORMAT_REVISION
}, f) }, f)
with pytest.raises(aiohttp.web.HTTPConflict): with pytest.raises(aiohttp.web.HTTPConflict):
topo = load_topology(path) load_topology(path)
def test_load_newer_topology(tmpdir): def test_load_newer_topology(tmpdir):
@ -146,6 +152,7 @@ def test_load_newer_topology(tmpdir):
If a topology is design for a more recent GNS3 version If a topology is design for a more recent GNS3 version
we disallow the loading. we disallow the loading.
""" """
data = { data = {
"project_id": "69f26504-7aa3-48aa-9f29-798d44841211", "project_id": "69f26504-7aa3-48aa-9f29-798d44841211",
"name": "Test", "name": "Test",
@ -159,10 +166,11 @@ def test_load_newer_topology(tmpdir):
with open(path, "w+") as f: with open(path, "w+") as f:
json.dump(data, f) json.dump(data, f)
with pytest.raises(aiohttp.web.HTTPConflict): with pytest.raises(aiohttp.web.HTTPConflict):
topo = load_topology(path) load_topology(path)
def test_load_topology_with_variables(tmpdir): def test_load_topology_with_variables(tmpdir):
variables = [ variables = [
{"name": "TEST1"}, {"name": "TEST1"},
{"name": "TEST2", "value": "value1"} {"name": "TEST2", "value": "value1"}
@ -189,6 +197,7 @@ def test_load_topology_with_variables(tmpdir):
def test_load_topology_with_supplier(tmpdir): def test_load_topology_with_supplier(tmpdir):
supplier = { supplier = {
'logo': 'logo.png', 'logo': 'logo.png',
'url': 'http://example.com' 'url': 'http://example.com'
@ -211,4 +220,4 @@ def test_load_topology_with_supplier(tmpdir):
with open(path, "w+") as f: with open(path, "w+") as f:
json.dump(data, f) json.dump(data, f)
topo = load_topology(path) topo = load_topology(path)
assert topo == data assert topo == data

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2016 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -16,23 +16,17 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import pytest import pytest
import asyncio
import aiohttp import aiohttp
from unittest.mock import MagicMock from unittest.mock import MagicMock
from tests.utils import AsyncioMagicMock from tests.utils import AsyncioMagicMock
from gns3server.controller.project import Project
from gns3server.controller.udp_link import UDPLink from gns3server.controller.udp_link import UDPLink
from gns3server.controller.ports.ethernet_port import EthernetPort from gns3server.controller.ports.ethernet_port import EthernetPort
from gns3server.controller.node import Node from gns3server.controller.node import Node
@pytest.fixture async def test_create(project):
def project(controller):
return Project(controller=controller, name="Test")
def test_create(async_run, project):
compute1 = MagicMock() compute1 = MagicMock()
compute2 = MagicMock() compute2 = MagicMock()
@ -50,8 +44,8 @@ def test_create(async_run, project):
compute1.get_ip_on_same_subnet.side_effect = subnet_callback compute1.get_ip_on_same_subnet.side_effect = subnet_callback
link = UDPLink(project) link = UDPLink(project)
async_run(link.add_node(node1, 0, 4)) await link.add_node(node1, 0, 4)
async_run(link.update_filters({"latency": [10]})) await link.update_filters({"latency": [10]})
async def compute1_callback(path, data={}, **kwargs): async def compute1_callback(path, data={}, **kwargs):
""" """
@ -75,7 +69,7 @@ def test_create(async_run, project):
compute1.host = "example.com" compute1.host = "example.com"
compute2.post.side_effect = compute2_callback compute2.post.side_effect = compute2_callback
compute2.host = "example.org" compute2.host = "example.org"
async_run(link.add_node(node2, 3, 1)) await 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={ compute1.post.assert_any_call("/projects/{}/vpcs/nodes/{}/adapters/0/ports/4/nio".format(project.id, node1.id), data={
"lport": 1024, "lport": 1024,
@ -85,6 +79,7 @@ def test_create(async_run, project):
"filters": {"latency": [10]}, "filters": {"latency": [10]},
"suspend": False, "suspend": False,
}, timeout=120) }, timeout=120)
compute2.post.assert_any_call("/projects/{}/vpcs/nodes/{}/adapters/3/ports/1/nio".format(project.id, node2.id), data={ compute2.post.assert_any_call("/projects/{}/vpcs/nodes/{}/adapters/3/ports/1/nio".format(project.id, node2.id), data={
"lport": 2048, "lport": 2048,
"rhost": "192.168.1.1", "rhost": "192.168.1.1",
@ -95,7 +90,8 @@ def test_create(async_run, project):
}, timeout=120) }, timeout=120)
def test_create_one_side_failure(async_run, project): async def test_create_one_side_failure(project):
compute1 = MagicMock() compute1 = MagicMock()
compute2 = MagicMock() compute2 = MagicMock()
@ -113,7 +109,7 @@ def test_create_one_side_failure(async_run, project):
compute1.get_ip_on_same_subnet.side_effect = subnet_callback compute1.get_ip_on_same_subnet.side_effect = subnet_callback
link = UDPLink(project) link = UDPLink(project)
async_run(link.add_node(node1, 0, 4)) await link.add_node(node1, 0, 4)
async def compute1_callback(path, data={}, **kwargs): async def compute1_callback(path, data={}, **kwargs):
""" """
@ -140,7 +136,7 @@ def test_create_one_side_failure(async_run, project):
compute2.post.side_effect = compute2_callback compute2.post.side_effect = compute2_callback
compute2.host = "example.org" compute2.host = "example.org"
with pytest.raises(aiohttp.web.HTTPConflict): with pytest.raises(aiohttp.web.HTTPConflict):
async_run(link.add_node(node2, 3, 1)) await 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={ compute1.post.assert_any_call("/projects/{}/vpcs/nodes/{}/adapters/0/ports/4/nio".format(project.id, node1.id), data={
"lport": 1024, "lport": 1024,
@ -150,6 +146,7 @@ def test_create_one_side_failure(async_run, project):
"filters": {}, "filters": {},
"suspend": False, "suspend": False,
}, timeout=120) }, timeout=120)
compute2.post.assert_any_call("/projects/{}/vpcs/nodes/{}/adapters/3/ports/1/nio".format(project.id, node2.id), data={ compute2.post.assert_any_call("/projects/{}/vpcs/nodes/{}/adapters/3/ports/1/nio".format(project.id, node2.id), data={
"lport": 2048, "lport": 2048,
"rhost": "192.168.1.1", "rhost": "192.168.1.1",
@ -162,7 +159,8 @@ def test_create_one_side_failure(async_run, project):
compute1.delete.assert_any_call("/projects/{}/vpcs/nodes/{}/adapters/0/ports/4/nio".format(project.id, node1.id), timeout=120) compute1.delete.assert_any_call("/projects/{}/vpcs/nodes/{}/adapters/0/ports/4/nio".format(project.id, node1.id), timeout=120)
def test_delete(async_run, project): async def test_delete(project):
compute1 = MagicMock() compute1 = MagicMock()
compute2 = MagicMock() compute2 = MagicMock()
@ -173,19 +171,20 @@ def test_delete(async_run, project):
link = UDPLink(project) link = UDPLink(project)
link.create = AsyncioMagicMock() link.create = AsyncioMagicMock()
async_run(link.add_node(node1, 0, 4)) await link.add_node(node1, 0, 4)
async_run(link.add_node(node2, 3, 1)) await link.add_node(node2, 3, 1)
async_run(link.delete()) await link.delete()
compute1.delete.assert_any_call("/projects/{}/vpcs/nodes/{}/adapters/0/ports/4/nio".format(project.id, node1.id), timeout=120) compute1.delete.assert_any_call("/projects/{}/vpcs/nodes/{}/adapters/0/ports/4/nio".format(project.id, node1.id), timeout=120)
compute2.delete.assert_any_call("/projects/{}/vpcs/nodes/{}/adapters/3/ports/1/nio".format(project.id, node2.id), timeout=120) compute2.delete.assert_any_call("/projects/{}/vpcs/nodes/{}/adapters/3/ports/1/nio".format(project.id, node2.id), timeout=120)
def test_choose_capture_side(async_run, project): async def test_choose_capture_side(project):
""" """
The link capture should run on the optimal node The link capture should run on the optimal node
""" """
compute1 = MagicMock() compute1 = MagicMock()
compute2 = MagicMock() compute2 = MagicMock()
compute2.id = "local" compute2.id = "local"
@ -200,8 +199,8 @@ def test_choose_capture_side(async_run, project):
link = UDPLink(project) link = UDPLink(project)
link.create = AsyncioMagicMock() link.create = AsyncioMagicMock()
async_run(link.add_node(node_vpcs, 0, 4)) await link.add_node(node_vpcs, 0, 4)
async_run(link.add_node(node_iou, 3, 1)) await link.add_node(node_iou, 3, 1)
assert link._choose_capture_side()["node"] == node_iou assert link._choose_capture_side()["node"] == node_iou
@ -215,8 +214,8 @@ def test_choose_capture_side(async_run, project):
link = UDPLink(project) link = UDPLink(project)
link.create = AsyncioMagicMock() link.create = AsyncioMagicMock()
async_run(link.add_node(node_iou, 0, 4)) await link.add_node(node_iou, 0, 4)
async_run(link.add_node(node_switch, 3, 1)) await link.add_node(node_switch, 3, 1)
assert link._choose_capture_side()["node"] == node_switch assert link._choose_capture_side()["node"] == node_switch
@ -228,8 +227,8 @@ def test_choose_capture_side(async_run, project):
link = UDPLink(project) link = UDPLink(project)
link.create = AsyncioMagicMock() link.create = AsyncioMagicMock()
async_run(link.add_node(node_vpcs, 0, 4)) await link.add_node(node_vpcs, 0, 4)
async_run(link.add_node(node_iou, 3, 1)) await link.add_node(node_iou, 3, 1)
with pytest.raises(aiohttp.web.HTTPConflict): with pytest.raises(aiohttp.web.HTTPConflict):
link._choose_capture_side() link._choose_capture_side()
@ -238,7 +237,7 @@ def test_choose_capture_side(async_run, project):
assert link._choose_capture_side()["node"] == node_vpcs assert link._choose_capture_side()["node"] == node_vpcs
def test_capture(async_run, project): async def test_capture(project):
compute1 = MagicMock() compute1 = MagicMock()
node_vpcs = Node(project, compute1, "V1", node_type="vpcs") node_vpcs = Node(project, compute1, "V1", node_type="vpcs")
@ -249,10 +248,10 @@ def test_capture(async_run, project):
link = UDPLink(project) link = UDPLink(project)
link.create = AsyncioMagicMock() link.create = AsyncioMagicMock()
async_run(link.add_node(node_vpcs, 0, 4)) await link.add_node(node_vpcs, 0, 4)
async_run(link.add_node(node_iou, 3, 1)) await link.add_node(node_iou, 3, 1)
capture = async_run(link.start_capture()) await link.start_capture()
assert link.capturing assert link.capturing
compute1.post.assert_any_call("/projects/{}/vpcs/nodes/{}/adapters/0/ports/4/start_capture".format(project.id, node_vpcs.id), data={ compute1.post.assert_any_call("/projects/{}/vpcs/nodes/{}/adapters/0/ports/4/start_capture".format(project.id, node_vpcs.id), data={
@ -260,16 +259,17 @@ def test_capture(async_run, project):
"data_link_type": "DLT_EN10MB" "data_link_type": "DLT_EN10MB"
}) })
capture = async_run(link.stop_capture()) await link.stop_capture()
assert link.capturing is False assert link.capturing is False
compute1.post.assert_any_call("/projects/{}/vpcs/nodes/{}/adapters/0/ports/4/stop_capture".format(project.id, node_vpcs.id)) compute1.post.assert_any_call("/projects/{}/vpcs/nodes/{}/adapters/0/ports/4/stop_capture".format(project.id, node_vpcs.id))
def test_node_updated(project, async_run): async def test_node_updated(project):
""" """
If a node stop when capturing we stop the capture If a node stop when capturing we stop the capture
""" """
compute1 = MagicMock() compute1 = MagicMock()
node_vpcs = Node(project, compute1, "V1", node_type="vpcs") node_vpcs = Node(project, compute1, "V1", node_type="vpcs")
node_vpcs._status = "started" node_vpcs._status = "started"
@ -278,15 +278,16 @@ def test_node_updated(project, async_run):
link._capture_node = {"node": node_vpcs} link._capture_node = {"node": node_vpcs}
link.stop_capture = AsyncioMagicMock() link.stop_capture = AsyncioMagicMock()
async_run(link.node_updated(node_vpcs)) await link.node_updated(node_vpcs)
assert not link.stop_capture.called assert not link.stop_capture.called
node_vpcs._status = "stopped" node_vpcs._status = "stopped"
async_run(link.node_updated(node_vpcs)) await link.node_updated(node_vpcs)
assert link.stop_capture.called assert link.stop_capture.called
def test_update(async_run, project): async def test_update(project):
compute1 = MagicMock() compute1 = MagicMock()
compute2 = MagicMock() compute2 = MagicMock()
@ -304,8 +305,8 @@ def test_update(async_run, project):
compute1.get_ip_on_same_subnet.side_effect = subnet_callback compute1.get_ip_on_same_subnet.side_effect = subnet_callback
link = UDPLink(project) link = UDPLink(project)
async_run(link.add_node(node1, 0, 4)) await link.add_node(node1, 0, 4)
async_run(link.update_filters({"latency": [10]})) await link.update_filters({"latency": [10]})
async def compute1_callback(path, data={}, **kwargs): async def compute1_callback(path, data={}, **kwargs):
""" """
@ -329,7 +330,7 @@ def test_update(async_run, project):
compute1.host = "example.com" compute1.host = "example.com"
compute2.post.side_effect = compute2_callback compute2.post.side_effect = compute2_callback
compute2.host = "example.org" compute2.host = "example.org"
async_run(link.add_node(node2, 3, 1)) await 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={ compute1.post.assert_any_call("/projects/{}/vpcs/nodes/{}/adapters/0/ports/4/nio".format(project.id, node1.id), data={
"lport": 1024, "lport": 1024,
@ -339,6 +340,7 @@ def test_update(async_run, project):
"suspend": False, "suspend": False,
"filters": {"latency": [10]} "filters": {"latency": [10]}
}, timeout=120) }, timeout=120)
compute2.post.assert_any_call("/projects/{}/vpcs/nodes/{}/adapters/3/ports/1/nio".format(project.id, node2.id), data={ compute2.post.assert_any_call("/projects/{}/vpcs/nodes/{}/adapters/3/ports/1/nio".format(project.id, node2.id), data={
"lport": 2048, "lport": 2048,
"rhost": "192.168.1.1", "rhost": "192.168.1.1",
@ -349,7 +351,7 @@ def test_update(async_run, project):
}, timeout=120) }, timeout=120)
assert link.created assert link.created
async_run(link.update_filters({"drop": [5], "bpf": ["icmp[icmptype] == 8"]})) await link.update_filters({"drop": [5], "bpf": ["icmp[icmptype] == 8"]})
compute1.put.assert_any_call("/projects/{}/vpcs/nodes/{}/adapters/0/ports/4/nio".format(project.id, node1.id), data={ compute1.put.assert_any_call("/projects/{}/vpcs/nodes/{}/adapters/0/ports/4/nio".format(project.id, node1.id), data={
"lport": 1024, "lport": 1024,
"rhost": "192.168.1.2", "rhost": "192.168.1.2",
@ -363,7 +365,7 @@ def test_update(async_run, project):
}, timeout=120) }, timeout=120)
def test_update_suspend(async_run, project): async def test_update_suspend(project):
compute1 = MagicMock() compute1 = MagicMock()
compute2 = MagicMock() compute2 = MagicMock()
@ -381,9 +383,9 @@ def test_update_suspend(async_run, project):
compute1.get_ip_on_same_subnet.side_effect = subnet_callback compute1.get_ip_on_same_subnet.side_effect = subnet_callback
link = UDPLink(project) link = UDPLink(project)
async_run(link.add_node(node1, 0, 4)) await link.add_node(node1, 0, 4)
async_run(link.update_filters({"latency": [10]})) await link.update_filters({"latency": [10]})
async_run(link.update_suspend(True)) await link.update_suspend(True)
async def compute1_callback(path, data={}, **kwargs): async def compute1_callback(path, data={}, **kwargs):
""" """
@ -407,7 +409,7 @@ def test_update_suspend(async_run, project):
compute1.host = "example.com" compute1.host = "example.com"
compute2.post.side_effect = compute2_callback compute2.post.side_effect = compute2_callback
compute2.host = "example.org" compute2.host = "example.org"
async_run(link.add_node(node2, 3, 1)) await 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={ compute1.post.assert_any_call("/projects/{}/vpcs/nodes/{}/adapters/0/ports/4/nio".format(project.id, node1.id), data={
"lport": 1024, "lport": 1024,
@ -417,6 +419,7 @@ def test_update_suspend(async_run, project):
"filters": {"frequency_drop": [-1]}, "filters": {"frequency_drop": [-1]},
"suspend": True "suspend": True
}, timeout=120) }, timeout=120)
compute2.post.assert_any_call("/projects/{}/vpcs/nodes/{}/adapters/3/ports/1/nio".format(project.id, node2.id), data={ compute2.post.assert_any_call("/projects/{}/vpcs/nodes/{}/adapters/3/ports/1/nio".format(project.id, node2.id), data={
"lport": 2048, "lport": 2048,
"rhost": "192.168.1.1", "rhost": "192.168.1.1",

@ -27,124 +27,81 @@ import os
class Query: class Query:
""" """
Helper to make query againt the test server Helper to make queries against the test server
""" """
def __init__(self, loop, host='localhost', port=8001, prefix='', api_version=None): def __init__(self, http_client, prefix='', api_version=None):
""" """
:param prefix: Prefix added before path (ex: /compute) :param prefix: Prefix added before path (ex: /compute)
:param api_version: Version of the api :param api_version: Version of the API
""" """
self._loop = loop
self._port = port self._http_client = http_client
self._host = host
self._prefix = prefix self._prefix = prefix
self._api_version = api_version self._api_version = api_version
self._session = None
async def close(self):
await self._session.close()
def post(self, path, body={}, **kwargs): def post(self, path, body={}, **kwargs):
return self._fetch("POST", path, body, **kwargs) return self._request("POST", path, body, **kwargs)
def put(self, path, body={}, **kwargs): def put(self, path, body={}, **kwargs):
return self._fetch("PUT", path, body, **kwargs) return self._request("PUT", path, body, **kwargs)
def get(self, path, **kwargs): def get(self, path, **kwargs):
return self._fetch("GET", path, **kwargs) return self._request("GET", path, **kwargs)
def delete(self, path, **kwargs): def delete(self, path, **kwargs):
return self._fetch("DELETE", path, **kwargs) return self._request("DELETE", path, **kwargs)
def patch(self, path, **kwargs):
return self._request("PATCH", path, **kwargs)
def get_url(self, path): def get_url(self, path):
if self._api_version is None: if self._api_version is None:
return "http://{}:{}{}{}".format(self._host, self._port, self._prefix, path) return "/{}{}".format(self._prefix, path)
return "http://{}:{}/v{}{}{}".format(self._host, self._port, self._api_version, self._prefix, path) return "/v{}{}{}".format(self._api_version, self._prefix, path)
def websocket(self, path): # async def websocket(self, path):
""" # """
Return a websocket connected to the path # Return a websocket connected to the path
""" # """
#
async def go_request(future): # #self._session = aiohttp.ClientSession()
self._session = aiohttp.ClientSession() # response = await self._http_client.ws_connect(self.get_url(path))
response = await self._session.ws_connect(self.get_url(path)) # return response
future.set_result(response) #
future = asyncio.Future() # # async def go_request(future):
asyncio.ensure_future(go_request(future)) # # self._session = aiohttp.ClientSession()
self._loop.run_until_complete(future) # # response = await self._session.ws_connect(self.get_url(path))
return future.result() # # future.set_result(response)
# #
def _fetch(self, method, path, body=None, **kwargs): # # future = asyncio.Future()
"""Fetch an url, parse the JSON and return response # # asyncio.ensure_future(go_request(future))
# # self._loop.run_until_complete(future)
Options: # # return future.result()
- example if True the session is included inside documentation
- raw do not JSON encode the query async def _request(self, method, path, body=None, raw=False, **kwargs):
"""
return self._loop.run_until_complete(asyncio.ensure_future(self._async_fetch(method, path, body=body, **kwargs))) if body is not None and raw is False:
async def _async_fetch(self, method, path, body=None, **kwargs):
if body is not None and not kwargs.get("raw", False):
body = json.dumps(body) body = json.dumps(body)
connector = aiohttp.TCPConnector() async with self._http_client.request(method, self.get_url(path), data=body, **kwargs) as response:
async with aiohttp.request(method, self.get_url(path), data=body, loop=self._loop, connector=connector) as response:
response.body = await response.read() response.body = await response.read()
x_route = response.headers.get('X-Route', None) x_route = response.headers.get('X-Route', None)
if x_route is not None: if x_route is not None:
response.route = x_route.replace("/v{}".format(self._api_version), "") response.route = x_route.replace("/v{}".format(self._api_version), "")
response.route = response.route .replace(self._prefix, "") response.route = response.route .replace(self._prefix, "")
response.json = {} #response.json = {}
response.html = "" #response.html = ""
if response.body is not None: if response.body is not None:
if response.headers.get("CONTENT-TYPE", "") == "application/json": if response.content_type == "application/json":
try: try:
response.json = json.loads(response.body.decode("utf-8")) response.json = await response.json(encoding="utf-8")
except ValueError: except ValueError:
response.json = None response.json = None
else: else:
try: try:
response.html = response.body.decode("utf-8") response.html = await response.text("utf-8")
except UnicodeDecodeError: except UnicodeDecodeError:
response.html = None response.html = None
if kwargs.get('example') and os.environ.get("PYTEST_BUILD_DOCUMENTATION") == "1":
self._dump_example(method, response.route, path, body, response)
return response return response
return None
def _dump_example(self, method, route, path, body, response):
"""Dump the request for the documentation"""
if path is None:
return
with open(self._example_file_path(method, route), 'w+') as f:
f.write("curl -i -X {} 'http://localhost:3080/v{}{}{}'".format(method, self._api_version, self._prefix, path))
if body:
f.write(" -d '{}'".format(re.sub(r"\n", "", json.dumps(json.loads(body), sort_keys=True))))
f.write("\n\n")
f.write("{} /v{}{}{} HTTP/1.1\n".format(method, self._api_version, self._prefix, path))
if body:
f.write(json.dumps(json.loads(body), sort_keys=True, indent=4))
f.write("\n\n\n")
f.write("HTTP/1.1 {}\n".format(response.status))
for header, value in sorted(response.headers.items()):
if header == 'DATE':
# We fix the date otherwise the example is always different and create change in git
value = "Thu, 08 Jan 2015 16:09:15 GMT"
f.write("{}: {}\n".format(header, value))
f.write("\n")
if response.body:
f.write(json.dumps(json.loads(response.body.decode('utf-8')), sort_keys=True, indent=4))
f.write("\n")
def _example_file_path(self, method, path):
path = re.sub('[^a-z0-9]', '', path)
if len(self._prefix) > 0:
prefix = self._prefix.replace('/', '')
return "docs/api/examples/{}_{}_{}.txt".format(prefix, method.lower(), path)
else:
return "docs/api/examples/controller_{}_{}.txt".format(method.lower(), path)

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -15,27 +15,24 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
This test suite check /version endpoint
It's also used for unittest the HTTP implementation.
"""
import sys import sys
import pytest import pytest
from gns3server.config import Config
from gns3server.version import __version__ from gns3server.version import __version__
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") @pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
def test_get(http_compute, windows_platform): async def test_get(compute_api, windows_platform):
response = http_compute.get('/capabilities', example=True)
response = await compute_api.get('/capabilities')
assert response.status == 200 assert response.status == 200
assert response.json == {'node_types': ['cloud', 'ethernet_hub', 'ethernet_switch', 'nat', 'vpcs', 'virtualbox', 'dynamips', 'frame_relay_switch', 'atm_switch', 'qemu', 'vmware', 'traceng', 'docker', 'iou'], 'version': __version__, 'platform': sys.platform} assert response.json == {'node_types': ['cloud', 'ethernet_hub', 'ethernet_switch', 'nat', 'vpcs', 'virtualbox', 'dynamips', 'frame_relay_switch', 'atm_switch', 'qemu', 'vmware', 'traceng', 'docker', 'iou'], 'version': __version__, 'platform': sys.platform}
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") @pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
def test_get_on_gns3vm(http_compute, on_gns3vm): async def test_get_on_gns3vm(compute_api, on_gns3vm):
response = http_compute.get('/capabilities', example=True)
response = await compute_api.get('/capabilities')
assert response.status == 200 assert response.status == 200
assert response.json == {'node_types': ['cloud', 'ethernet_hub', 'ethernet_switch', 'nat', 'vpcs', 'virtualbox', 'dynamips', 'frame_relay_switch', 'atm_switch', 'qemu', 'vmware', 'traceng', 'docker', 'iou'], 'version': __version__, 'platform': sys.platform} assert response.json == {'node_types': ['cloud', 'ethernet_hub', 'ethernet_switch', 'nat', 'vpcs', 'virtualbox', 'dynamips', 'frame_relay_switch', 'atm_switch', 'qemu', 'vmware', 'traceng', 'docker', 'iou'], 'version': __version__, 'platform': sys.platform}

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -16,121 +16,119 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import pytest import pytest
import sys
import os
from tests.utils import asyncio_patch from tests.utils import asyncio_patch
from unittest.mock import patch
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def vm(http_compute, project): async def vm(compute_api, compute_project, on_gns3vm):
response = http_compute.post("/projects/{project_id}/cloud/nodes".format(project_id=project.id), {"name": "Cloud 1"})
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._start_ubridge"):
response = await compute_api.post("/projects/{project_id}/cloud/nodes".format(project_id=compute_project.id), {"name": "Cloud 1"})
assert response.status == 201 assert response.status == 201
return response.json return response.json
@pytest.yield_fixture(autouse=True) async def test_cloud_create(compute_api, compute_project):
def mock_ubridge():
"""
Avoid all interaction with ubridge
"""
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._start_ubridge"):
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._add_ubridge_connection"):
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._delete_ubridge_connection"):
yield
def test_cloud_create(http_compute, project): with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._start_ubridge"):
response = http_compute.post("/projects/{project_id}/cloud/nodes".format(project_id=project.id), {"name": "Cloud 1"}, example=True) response = await compute_api.post("/projects/{project_id}/cloud/nodes".format(project_id=compute_project.id), {"name": "Cloud 1"})
assert response.status == 201 assert response.status == 201
assert response.route == "/projects/{project_id}/cloud/nodes" assert response.route == "/projects/{project_id}/cloud/nodes"
assert response.json["name"] == "Cloud 1" assert response.json["name"] == "Cloud 1"
assert response.json["project_id"] == project.id assert response.json["project_id"] == compute_project.id
async def test_cloud_get(compute_api, compute_project, vm):
def test_cloud_get(http_compute, project, vm): response = await compute_api.get("/projects/{project_id}/cloud/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]))
response = http_compute.get("/projects/{project_id}/cloud/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True)
assert response.status == 200 assert response.status == 200
assert response.route == "/projects/{project_id}/cloud/nodes/{node_id}" assert response.route == "/projects/{project_id}/cloud/nodes/{node_id}"
assert response.json["name"] == "Cloud 1" assert response.json["name"] == "Cloud 1"
assert response.json["project_id"] == project.id assert response.json["project_id"] == compute_project.id
assert response.json["status"] == "started" assert response.json["status"] == "started"
def test_cloud_nio_create_udp(http_compute, vm): async def test_cloud_nio_create_udp(compute_api, vm):
response = http_compute.post("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_udp",
"lport": 4242, params = {"type": "nio_udp",
"rport": 4343, "lport": 4242,
"rhost": "127.0.0.1"}, "rport": 4343,
example=True) "rhost": "127.0.0.1"}
response = await compute_api.post("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
assert response.status == 201 assert response.status == 201
assert response.route == r"/projects/{project_id}/cloud/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.route == r"/projects/{project_id}/cloud/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
assert response.json["type"] == "nio_udp" assert response.json["type"] == "nio_udp"
def test_cloud_nio_update_udp(http_compute, vm): async def test_cloud_nio_update_udp(compute_api, vm):
http_compute.post("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_udp",
"lport": 4242, params = {"type": "nio_udp",
"rport": 4343, "lport": 4242,
"rhost": "127.0.0.1"}) "rport": 4343,
response = http_compute.put("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), "rhost": "127.0.0.1"}
{
"type": "nio_udp", await compute_api.post("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
"lport": 4242, params["filters"] = {}
"rport": 4343, response = await compute_api.put("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
"rhost": "127.0.0.1",
"filters": {}},
example=True)
assert response.status == 201, response.body.decode() assert response.status == 201, response.body.decode()
assert response.route == r"/projects/{project_id}/cloud/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.route == r"/projects/{project_id}/cloud/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
assert response.json["type"] == "nio_udp" assert response.json["type"] == "nio_udp"
def test_cloud_delete_nio(http_compute, vm): async def test_cloud_delete_nio(compute_api, vm):
http_compute.post("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_udp",
"lport": 4242, params = {"type": "nio_udp",
"rport": 4343, "lport": 4242,
"rhost": "127.0.0.1"}) "rport": 4343,
response = http_compute.delete("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) "rhost": "127.0.0.1"}
await compute_api.post("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._start_ubridge"):
response = await compute_api.delete("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert response.status == 204 assert response.status == 204
assert response.route == r"/projects/{project_id}/cloud/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.route == r"/projects/{project_id}/cloud/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
def test_cloud_delete(http_compute, vm): async def test_cloud_delete(compute_api, vm):
response = http_compute.delete("/projects/{project_id}/cloud/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True)
response = await compute_api.delete("/projects/{project_id}/cloud/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert response.status == 204 assert response.status == 204
def test_cloud_update(http_compute, vm, tmpdir): async def test_cloud_update(compute_api, vm):
response = http_compute.put("/projects/{project_id}/cloud/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), {
"name": "test" response = await compute_api.put("/projects/{project_id}/cloud/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"name": "test"})
},
example=True)
assert response.status == 200 assert response.status == 200
assert response.json["name"] == "test" assert response.json["name"] == "test"
def test_cloud_start_capture(http_compute, vm): async def test_cloud_start_capture(compute_api, vm):
params = {
"capture_file_name": "test.pcap",
"data_link_type": "DLT_EN10MB"
}
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud.start_capture") as start_capture: with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud.start_capture") as mock:
params = {"capture_file_name": "test.pcap", "data_link_type": "DLT_EN10MB"} response = await compute_api.post("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/start_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
response = http_compute.post("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/start_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]), body=params, example=True)
assert response.status == 200 assert response.status == 200
assert start_capture.called assert mock.called
assert "test.pcap" in response.json["pcap_file_path"] assert "test.pcap" in response.json["pcap_file_path"]
def test_cloud_stop_capture(http_compute, vm): async def test_cloud_stop_capture(compute_api, vm):
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud.stop_capture") as stop_capture: with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud.stop_capture") as mock:
response = http_compute.post("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/stop_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.post("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/stop_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert response.status == 204 assert response.status == 204
assert stop_capture.called assert mock.called
def test_cloud_pcap(http_compute, vm, project): async def test_cloud_pcap(compute_api, vm, compute_project):
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud.get_nio"): with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud.get_nio"):
with asyncio_patch("gns3server.compute.builtin.Builtin.stream_pcap_file"): with asyncio_patch("gns3server.compute.builtin.Builtin.stream_pcap_file"):
response = http_compute.get("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=project.id, node_id=vm["node_id"]), raw=True) response = await compute_api.get("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True)
assert response.status == 200 assert response.status == 200

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -16,15 +16,11 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import pytest import pytest
import os
import stat
import sys import sys
import uuid import uuid
import aiohttp
from tests.utils import asyncio_patch from tests.utils import asyncio_patch
from unittest.mock import patch, MagicMock, PropertyMock from unittest.mock import patch
from gns3server.compute.docker import Docker
pytestmark = pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") pytestmark = pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
@ -32,38 +28,50 @@ pytestmark = pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supp
@pytest.fixture @pytest.fixture
def base_params(): def base_params():
"""Return standard parameters""" """Return standard parameters"""
return {"name": "PC TEST 1", "image": "nginx", "start_command": "nginx-daemon", "adapters": 2, "environment": "YES=1\nNO=0", "console_type": "telnet", "console_resolution": "1280x1024", "extra_hosts": "test:127.0.0.1"}
params = {
@pytest.yield_fixture(autouse=True) "name": "PC TEST 1",
def mock_connection(): "image": "nginx",
docker = Docker.instance() "start_command": "nginx-daemon",
docker._connected = True "adapters": 2,
docker._connector = MagicMock() "environment": "YES=1\nNO=0",
yield "console_type": "telnet",
Docker._instance = None "console_resolution": "1280x1024",
"extra_hosts": "test:127.0.0.1"
}
return params
# @pytest.yield_fixture(autouse=True)
# def mock_connection():
#
# docker = Docker.instance()
# docker._connected = True
# docker._connector = MagicMock()
# yield
# Docker._instance = None
@pytest.fixture @pytest.fixture
def vm(http_compute, project, base_params): async def vm(compute_api, compute_project, base_params):
with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "nginx"}]) as mock_list:
with asyncio_patch("gns3server.compute.docker.Docker.query", return_value={"Id": "8bd8153ea8f5"}) as mock: with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "nginx"}]):
with asyncio_patch("gns3server.compute.docker.DockerVM._get_container_state", return_value="exited") as mock: with asyncio_patch("gns3server.compute.docker.Docker.query", return_value={"Id": "8bd8153ea8f5"}):
response = http_compute.post("/projects/{project_id}/docker/nodes".format(project_id=project.id), base_params) with asyncio_patch("gns3server.compute.docker.DockerVM._get_container_state", return_value="exited"):
if response.status != 201: response = await compute_api.post("/projects/{project_id}/docker/nodes".format(project_id=compute_project.id), base_params)
print(response.body)
assert response.status == 201 assert response.status == 201
return response.json return response.json
def test_docker_create(http_compute, project, base_params): async def test_docker_create(compute_api, compute_project, base_params):
with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "nginx"}]) as mock_list:
with asyncio_patch("gns3server.compute.docker.Docker.query", return_value={"Id": "8bd8153ea8f5"}) as mock: with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "nginx"}]):
response = http_compute.post("/projects/{project_id}/docker/nodes".format(project_id=project.id), base_params) with asyncio_patch("gns3server.compute.docker.Docker.query", return_value={"Id": "8bd8153ea8f5"}):
response = await compute_api.post("/projects/{project_id}/docker/nodes".format(project_id=compute_project.id), base_params)
assert response.status == 201 assert response.status == 201
assert response.route == "/projects/{project_id}/docker/nodes" assert response.route == "/projects/{project_id}/docker/nodes"
assert response.json["name"] == "PC TEST 1" assert response.json["name"] == "PC TEST 1"
assert response.json["project_id"] == project.id assert response.json["project_id"] == compute_project.id
assert response.json["container_id"] == "8bd8153ea8f5" assert response.json["container_id"] == "8bd8153ea8f5"
assert response.json["image"] == "nginx:latest" assert response.json["image"] == "nginx:latest"
assert response.json["adapters"] == 2 assert response.json["adapters"] == 2
@ -71,94 +79,106 @@ def test_docker_create(http_compute, project, base_params):
assert response.json["console_resolution"] == "1280x1024" assert response.json["console_resolution"] == "1280x1024"
assert response.json["extra_hosts"] == "test:127.0.0.1" assert response.json["extra_hosts"] == "test:127.0.0.1"
def test_docker_start(http_compute, vm):
async def test_docker_start(compute_api, vm):
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.start", return_value=True) as mock: with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.start", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/docker/nodes/{node_id}/start".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await compute_api.post("/projects/{project_id}/docker/nodes/{node_id}/start".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_docker_stop(http_compute, vm): async def test_docker_stop(compute_api, vm):
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.stop", return_value=True) as mock: with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.stop", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/docker/nodes/{node_id}/stop".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await compute_api.post("/projects/{project_id}/docker/nodes/{node_id}/stop".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_docker_reload(http_compute, vm): async def test_docker_reload(compute_api, vm):
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.restart", return_value=True) as mock: with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.restart", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/docker/nodes/{node_id}/reload".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await compute_api.post("/projects/{project_id}/docker/nodes/{node_id}/reload".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_docker_delete(http_compute, vm): async def test_docker_delete(compute_api, vm):
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.delete", return_value=True) as mock: with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.delete", return_value=True) as mock:
response = http_compute.delete("/projects/{project_id}/docker/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await compute_api.delete("/projects/{project_id}/docker/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_docker_pause(http_compute, vm): async def test_docker_pause(compute_api, vm):
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.pause", return_value=True) as mock: with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.pause", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/docker/nodes/{node_id}/pause".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await compute_api.post("/projects/{project_id}/docker/nodes/{node_id}/pause".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_docker_unpause(http_compute, vm): async def test_docker_unpause(compute_api, vm):
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.unpause", return_value=True) as mock: with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.unpause", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/docker/nodes/{node_id}/unpause".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await compute_api.post("/projects/{project_id}/docker/nodes/{node_id}/unpause".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_docker_nio_create_udp(http_compute, vm): async def test_docker_nio_create_udp(compute_api, vm):
response = http_compute.post("/projects/{project_id}/docker/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_udp",
"lport": 4242, params = {
"rport": 4343, "type": "nio_udp",
"rhost": "127.0.0.1"}, "lport": 4242,
example=True) "rport": 4343,
"rhost": "127.0.0.1"}
response = await compute_api.post("/projects/{project_id}/docker/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
assert response.status == 201 assert response.status == 201
assert response.route == r"/projects/{project_id}/docker/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.route == r"/projects/{project_id}/docker/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
assert response.json["type"] == "nio_udp" assert response.json["type"] == "nio_udp"
def test_docker_update_nio(http_compute, vm): async def test_docker_update_nio(compute_api, vm):
params = {
"type": "nio_udp",
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1"
}
response = http_compute.post("/projects/{project_id}/docker/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_udp", response = await compute_api.post("/projects/{project_id}/docker/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1"})
assert response.status == 201 assert response.status == 201
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.adapter_update_nio_binding") as mock: with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.adapter_update_nio_binding"):
response = http_compute.put("/projects/{project_id}/docker/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), response = await compute_api.put("/projects/{project_id}/docker/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
{
"type": "nio_udp",
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1"
},
example=True)
assert response.status == 201, response.body.decode() assert response.status == 201, response.body.decode()
assert response.route == r"/projects/{project_id}/docker/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.route == r"/projects/{project_id}/docker/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
def test_docker_delete_nio(http_compute, vm): async def test_docker_delete_nio(compute_api, vm):
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.adapter_remove_nio_binding") as mock:
response = http_compute.delete("/projects/{project_id}/docker/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.adapter_remove_nio_binding"):
response = await compute_api.delete("/projects/{project_id}/docker/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert response.status == 204 assert response.status == 204
assert response.route == r"/projects/{project_id}/docker/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.route == r"/projects/{project_id}/docker/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
def test_docker_update(http_compute, vm, tmpdir, free_console_port): async def test_docker_update(compute_api, vm, free_console_port):
params = {
"name": "test",
"console": free_console_port,
"start_command": "yes",
"environment": "GNS3=1\nGNS4=0",
"extra_hosts": "test:127.0.0.1"
}
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.update") as mock: with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.update") as mock:
response = http_compute.put("/projects/{project_id}/docker/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"name": "test", response = await compute_api.put("/projects/{project_id}/docker/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
"console": free_console_port,
"start_command": "yes",
"environment": "GNS3=1\nGNS4=0",
"extra_hosts": "test:127.0.0.1"},
example=True)
assert mock.called assert mock.called
assert response.status == 200 assert response.status == 200
assert response.json["name"] == "test" assert response.json["name"] == "test"
@ -168,35 +188,30 @@ def test_docker_update(http_compute, vm, tmpdir, free_console_port):
assert response.json["extra_hosts"] == "test:127.0.0.1" assert response.json["extra_hosts"] == "test:127.0.0.1"
def test_docker_start_capture(http_compute, vm, tmpdir, project): async def test_docker_start_capture(compute_api, vm):
with patch("gns3server.compute.docker.docker_vm.DockerVM.is_running", return_value=True) as mock: with patch("gns3server.compute.docker.docker_vm.DockerVM.is_running", return_value=True):
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.start_capture") as start_capture: with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.start_capture") as mock:
params = {"capture_file_name": "test.pcap", "data_link_type": "DLT_EN10MB"} params = {"capture_file_name": "test.pcap", "data_link_type": "DLT_EN10MB"}
response = http_compute.post("/projects/{project_id}/docker/nodes/{node_id}/adapters/0/ports/0/start_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]), body=params, example=True) response = await compute_api.post("/projects/{project_id}/docker/nodes/{node_id}/adapters/0/ports/0/start_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]), body=params)
assert response.status == 200 assert response.status == 200
assert start_capture.called assert mock.called
assert "test.pcap" in response.json["pcap_file_path"] assert "test.pcap" in response.json["pcap_file_path"]
def test_docker_stop_capture(http_compute, vm, tmpdir, project): async def test_docker_stop_capture(compute_api, vm):
with patch("gns3server.compute.docker.docker_vm.DockerVM.is_running", return_value=True) as mock: with patch("gns3server.compute.docker.docker_vm.DockerVM.is_running", return_value=True):
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.stop_capture") as stop_capture: with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.stop_capture") as mock:
response = http_compute.post("/projects/{project_id}/docker/nodes/{node_id}/adapters/0/ports/0/stop_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.post("/projects/{project_id}/docker/nodes/{node_id}/adapters/0/ports/0/stop_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert response.status == 204 assert response.status == 204
assert stop_capture.called assert mock.called
async def test_docker_duplicate(compute_api, vm):
def test_docker_duplicate(http_compute, vm): params = {"destination_node_id": str(uuid.uuid4())}
with asyncio_patch("gns3server.compute.docker.Docker.duplicate_node", return_value=True) as mock: with asyncio_patch("gns3server.compute.docker.Docker.duplicate_node", return_value=True) as mock:
response = http_compute.post( response = await compute_api.post("/projects/{project_id}/docker/nodes/{node_id}/duplicate".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
"/projects/{project_id}/docker/nodes/{node_id}/duplicate".format(
project_id=vm["project_id"],
node_id=vm["node_id"]),
body={
"destination_node_id": str(uuid.uuid4())
},
example=True)
assert mock.called assert mock.called
assert response.status == 201 assert response.status == 201

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -24,75 +24,83 @@ from tests.utils import asyncio_patch
# @pytest.yield_fixture(scope="module") # @pytest.yield_fixture(scope="module")
# def vm(http_compute, project): # async def vm(compute_api, compute_project, fake_image):
# #
# dynamips_path = "/fake/dynamips" # dynamips_path = "/fake/dynamips"
# params = {
# "name": "My router",
# "platform": "c3745",
# "image": fake_image,
# "ram": 128
# }
# with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.create", return_value=True) as mock: # with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.create", return_value=True) as mock:
# response = http_compute.post("/projects/{project_id}/dynamips/nodes".format(project_id=project.id), {"name": "My router", # response = await compute_api.post("/projects/{project_id}/dynamips/nodes".format(project_id=compute_project.id), params)
# "platform": "c3745",
# "image": "somewhere",
# "ram": 128})
# assert mock.called # assert mock.called
# assert response.status == 201 # assert response.status == 201
# #
# with asyncio_patch("gns3server.compute.dynamips.Dynamips.find_dynamips", return_value=dynamips_path): # #with asyncio_patch("gns3server.compute.dynamips.Dynamips.find_dynamips", return_value=dynamips_path):
# yield response.json # # yield response.json
# async def test_dynamips_vm_create(compute_api, compute_project, fake_image):
# #
# params = {
# "name": "My router",
# "platform": "c3745",
# "image": os.path.basename(fake_image),
# "ram": 128
# }
# #
# def test_dynamips_vm_create(http_compute, project): # print(fake_image)
# #
# with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.create", return_value=True): # with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.create", return_value=True):
# response = http_compute.post("/projects/{project_id}/dynamips/nodes".format(project_id=project.id), {"name": "My router", # response = await compute_api.post("/projects/{project_id}/dynamips/nodes".format(project_id=compute_project.id), params)
# "platform": "c3745",
# "image": "somewhere",
# "ram": 128},
# example=True)
# assert response.status == 201 # assert response.status == 201
# assert response.json["name"] == "My router" # assert response.json["name"] == "My router"
# assert response.json["project_id"] == project.id # assert response.json["project_id"] == compute_project.id
# assert response.json["dynamips_id"] # assert response.json["dynamips_id"]
#
#
# def test_dynamips_vm_get(http_compute, project, vm): # def test_dynamips_vm_get(compute_api, project, vm):
# response = http_compute.get("/projects/{project_id}/dynamips/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) # response = compute_api.get("/projects/{project_id}/dynamips/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True)
# assert response.status == 200 # assert response.status == 200
# assert response.route == "/projects/{project_id}/dynamips/nodes/{node_id}" # assert response.route == "/projects/{project_id}/dynamips/nodes/{node_id}"
# assert response.json["name"] == "My router" # assert response.json["name"] == "My router"
# assert response.json["project_id"] == project.id # assert response.json["project_id"] == project.id
# #
# #
# def test_dynamips_vm_start(http_compute, vm): # def test_dynamips_vm_start(compute_api, vm):
# with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.start", return_value=True) as mock: # with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.start", return_value=True) as mock:
# response = http_compute.post("/projects/{project_id}/dynamips/nodes/{node_id}/start".format(project_id=vm["project_id"], node_id=vm["node_id"])) # response = compute_api.post("/projects/{project_id}/dynamips/nodes/{node_id}/start".format(project_id=vm["project_id"], node_id=vm["node_id"]))
# assert mock.called # assert mock.called
# assert response.status == 204 # assert response.status == 204
# #
# #
# def test_dynamips_vm_stop(http_compute, vm): # def test_dynamips_vm_stop(compute_api, vm):
# with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.stop", return_value=True) as mock: # with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.stop", return_value=True) as mock:
# response = http_compute.post("/projects/{project_id}/dynamips/nodes/{node_id}/stop".format(project_id=vm["project_id"], node_id=vm["node_id"])) # response = compute_api.post("/projects/{project_id}/dynamips/nodes/{node_id}/stop".format(project_id=vm["project_id"], node_id=vm["node_id"]))
# assert mock.called # assert mock.called
# assert response.status == 204 # assert response.status == 204
# #
# #
# def test_dynamips_vm_suspend(http_compute, vm): # def test_dynamips_vm_suspend(compute_api, vm):
# with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.suspend", return_value=True) as mock: # with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.suspend", return_value=True) as mock:
# response = http_compute.post("/projects/{project_id}/dynamips/nodes/{node_id}/suspend".format(project_id=vm["project_id"], node_id=vm["node_id"])) # response = compute_api.post("/projects/{project_id}/dynamips/nodes/{node_id}/suspend".format(project_id=vm["project_id"], node_id=vm["node_id"]))
# assert mock.called # assert mock.called
# assert response.status == 204 # assert response.status == 204
# #
# #
# def test_dynamips_vm_resume(http_compute, vm): # def test_dynamips_vm_resume(compute_api, vm):
# with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.resume", return_value=True) as mock: # with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.resume", return_value=True) as mock:
# response = http_compute.post("/projects/{project_id}/dynamips/nodes/{node_id}/resume".format(project_id=vm["project_id"], node_id=vm["node_id"])) # response = compute_api.post("/projects/{project_id}/dynamips/nodes/{node_id}/resume".format(project_id=vm["project_id"], node_id=vm["node_id"]))
# assert mock.called # assert mock.called
# assert response.status == 204 # assert response.status == 204
# def test_vbox_nio_create_udp(http_compute, vm): # def test_vbox_nio_create_udp(compute_api, vm):
# #
# with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_add_nio_binding') as mock: # with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_add_nio_binding') as mock:
# response = http_compute.post("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/nio".format(project_id=vm["project_id"], # response = compute_api.post("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/nio".format(project_id=vm["project_id"],
# node_id=vm["node_id"]), {"type": "nio_udp", # node_id=vm["node_id"]), {"type": "nio_udp",
# "lport": 4242, # "lport": 4242,
# "rport": 4343, # "rport": 4343,
@ -108,10 +116,10 @@ from tests.utils import asyncio_patch
# assert response.json["type"] == "nio_udp" # assert response.json["type"] == "nio_udp"
# #
# #
# def test_vbox_delete_nio(http_compute, vm): # def test_vbox_delete_nio(compute_api, vm):
# #
# with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_remove_nio_binding') as mock: # with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_remove_nio_binding') as mock:
# response = http_compute.delete("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) # response = compute_api.delete("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True)
# #
# assert mock.called # assert mock.called
# args, kwgars = mock.call_args # args, kwgars = mock.call_args
@ -121,8 +129,8 @@ from tests.utils import asyncio_patch
# assert response.route == "/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/{adapter_id:\d+}/nio" # assert response.route == "/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/{adapter_id:\d+}/nio"
# #
# #
# def test_vbox_update(http_compute, vm, free_console_port): # def test_vbox_update(compute_api, vm, free_console_port):
# response = http_compute.put("/projects/{project_id}/virtualbox/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"name": "test", # response = compute_api.put("/projects/{project_id}/virtualbox/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"name": "test",
# "console": free_console_port}) # "console": free_console_port})
# assert response.status == 200 # assert response.status == 200
# assert response.json["name"] == "test" # assert response.json["name"] == "test"
@ -130,8 +138,9 @@ from tests.utils import asyncio_patch
@pytest.fixture @pytest.fixture
def fake_dynamips(tmpdir): def fake_image(tmpdir):
"""Create a fake Dynamips image on disk""" """Create a fake Dynamips image on disk"""
path = str(tmpdir / "7200.bin") path = str(tmpdir / "7200.bin")
with open(path, "wb+") as f: with open(path, "wb+") as f:
f.write(b'\x7fELF\x01\x02\x01') f.write(b'\x7fELF\x01\x02\x01')
@ -150,10 +159,10 @@ def fake_file(tmpdir):
return path return path
def test_images(http_compute, tmpdir, fake_dynamips, fake_file): async def test_images(compute_api, tmpdir, fake_image, fake_file):
with patch("gns3server.utils.images.default_images_directory", return_value=str(tmpdir)): with patch("gns3server.utils.images.default_images_directory", return_value=str(tmpdir)):
response = http_compute.get("/dynamips/images") response = await compute_api.get("/dynamips/images")
assert response.status == 200 assert response.status == 200
assert response.json == [{"filename": "7200.bin", assert response.json == [{"filename": "7200.bin",
"path": "7200.bin", "path": "7200.bin",
@ -162,8 +171,9 @@ def test_images(http_compute, tmpdir, fake_dynamips, fake_file):
}] }]
def test_upload_image(http_compute, tmpdir, images_dir): async def test_upload_image(compute_api, images_dir):
response = http_compute.post("/dynamips/images/test2", body="TEST", raw=True)
response = await compute_api.post("/dynamips/images/test2", body="TEST", raw=True)
assert response.status == 204 assert response.status == 204
with open(os.path.join(images_dir, "IOS", "test2")) as f: with open(os.path.join(images_dir, "IOS", "test2")) as f:
@ -174,12 +184,13 @@ def test_upload_image(http_compute, tmpdir, images_dir):
assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf" assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf"
def test_upload_image_permission_denied(http_compute, tmpdir, images_dir): async def test_upload_image_permission_denied(compute_api, tmpdir, images_dir):
os.makedirs(os.path.join(images_dir, "IOS"), exist_ok=True) os.makedirs(os.path.join(images_dir, "IOS"), exist_ok=True)
with open(os.path.join(images_dir, "IOS", "test2.tmp"), "w+") as f: with open(os.path.join(images_dir, "IOS", "test2.tmp"), "w+") as f:
f.write("") f.write("")
os.chmod(os.path.join(images_dir, "IOS", "test2.tmp"), 0) os.chmod(os.path.join(images_dir, "IOS", "test2.tmp"), 0)
with patch("gns3server.utils.images.default_images_directory", return_value=str(tmpdir)): with patch("gns3server.utils.images.default_images_directory", return_value=str(tmpdir)):
response = http_compute.post("/dynamips/images/test2", body="TEST", raw=True) response = await compute_api.post("/dynamips/images/test2", body="TEST", raw=True)
assert response.status == 409 assert response.status == 409

@ -20,10 +20,9 @@ import os
import stat import stat
import sys import sys
import uuid import uuid
import aiohttp
from tests.utils import asyncio_patch from tests.utils import asyncio_patch
from unittest.mock import patch, MagicMock, PropertyMock from unittest.mock import patch
pytestmark = pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") pytestmark = pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
@ -42,28 +41,32 @@ def fake_iou_bin(images_dir):
@pytest.fixture @pytest.fixture
def base_params(tmpdir, fake_iou_bin): def base_params(tmpdir, fake_iou_bin):
"""Return standard parameters""" """Return standard parameters"""
return {"application_id": 42, "name": "PC TEST 1", "path": "iou.bin"} return {"application_id": 42, "name": "PC TEST 1", "path": "iou.bin"}
@pytest.fixture @pytest.fixture
def vm(http_compute, project, base_params): async def vm(compute_api, compute_project, base_params):
response = http_compute.post("/projects/{project_id}/iou/nodes".format(project_id=project.id), base_params)
response = await compute_api.post("/projects/{project_id}/iou/nodes".format(project_id=compute_project.id), base_params)
assert response.status == 201 assert response.status == 201
return response.json return response.json
def startup_config_file(project, vm): def startup_config_file(compute_project, vm):
directory = os.path.join(project.path, "project-files", "iou", vm["node_id"])
directory = os.path.join(compute_project.path, "project-files", "iou", vm["node_id"])
os.makedirs(directory, exist_ok=True) os.makedirs(directory, exist_ok=True)
return os.path.join(directory, "startup-config.cfg") return os.path.join(directory, "startup-config.cfg")
def test_iou_create(http_compute, project, base_params): async def test_iou_create(compute_api, compute_project, base_params):
response = http_compute.post("/projects/{project_id}/iou/nodes".format(project_id=project.id), base_params)
response = await compute_api.post("/projects/{project_id}/iou/nodes".format(project_id=compute_project.id), base_params)
assert response.status == 201 assert response.status == 201
assert response.route == "/projects/{project_id}/iou/nodes" assert response.route == "/projects/{project_id}/iou/nodes"
assert response.json["name"] == "PC TEST 1" assert response.json["name"] == "PC TEST 1"
assert response.json["project_id"] == project.id assert response.json["project_id"] == compute_project.id
assert response.json["serial_adapters"] == 2 assert response.json["serial_adapters"] == 2
assert response.json["ethernet_adapters"] == 2 assert response.json["ethernet_adapters"] == 2
assert response.json["ram"] == 256 assert response.json["ram"] == 256
@ -71,7 +74,8 @@ def test_iou_create(http_compute, project, base_params):
assert response.json["l1_keepalives"] is False assert response.json["l1_keepalives"] is False
def test_iou_create_with_params(http_compute, project, base_params): async def test_iou_create_with_params(compute_api, compute_project, base_params):
params = base_params params = base_params
params["ram"] = 1024 params["ram"] = 1024
params["nvram"] = 512 params["nvram"] = 512
@ -81,11 +85,11 @@ def test_iou_create_with_params(http_compute, project, base_params):
params["startup_config_content"] = "hostname test" params["startup_config_content"] = "hostname test"
params["use_default_iou_values"] = False params["use_default_iou_values"] = False
response = http_compute.post("/projects/{project_id}/iou/nodes".format(project_id=project.id), params, example=True) response = await compute_api.post("/projects/{project_id}/iou/nodes".format(project_id=compute_project.id), params)
assert response.status == 201 assert response.status == 201
assert response.route == "/projects/{project_id}/iou/nodes" assert response.route == "/projects/{project_id}/iou/nodes"
assert response.json["name"] == "PC TEST 1" assert response.json["name"] == "PC TEST 1"
assert response.json["project_id"] == project.id assert response.json["project_id"] == compute_project.id
assert response.json["serial_adapters"] == 4 assert response.json["serial_adapters"] == 4
assert response.json["ethernet_adapters"] == 0 assert response.json["ethernet_adapters"] == 0
assert response.json["ram"] == 1024 assert response.json["ram"] == 1024
@ -93,15 +97,15 @@ def test_iou_create_with_params(http_compute, project, base_params):
assert response.json["l1_keepalives"] is True assert response.json["l1_keepalives"] is True
assert response.json["use_default_iou_values"] is False assert response.json["use_default_iou_values"] is False
with open(startup_config_file(project, response.json)) as f: with open(startup_config_file(compute_project, response.json)) as f:
assert f.read() == "hostname test" assert f.read() == "hostname test"
def test_iou_create_startup_config_already_exist(http_compute, project, base_params): async def test_iou_create_startup_config_already_exist(compute_api, compute_project, base_params):
"""We don't erase a startup-config if already exist at project creation""" """We don't erase a startup-config if already exist at project creation"""
node_id = str(uuid.uuid4()) node_id = str(uuid.uuid4())
startup_config_file_path = startup_config_file(project, {'node_id': node_id}) startup_config_file_path = startup_config_file(compute_project, {'node_id': node_id})
with open(startup_config_file_path, 'w+') as f: with open(startup_config_file_path, 'w+') as f:
f.write("echo hello") f.write("echo hello")
@ -109,20 +113,21 @@ def test_iou_create_startup_config_already_exist(http_compute, project, base_par
params["node_id"] = node_id params["node_id"] = node_id
params["startup_config_content"] = "hostname test" params["startup_config_content"] = "hostname test"
response = http_compute.post("/projects/{project_id}/iou/nodes".format(project_id=project.id), params, example=True) response = await compute_api.post("/projects/{project_id}/iou/nodes".format(project_id=compute_project.id), params)
assert response.status == 201 assert response.status == 201
assert response.route == "/projects/{project_id}/iou/nodes" assert response.route == "/projects/{project_id}/iou/nodes"
with open(startup_config_file(project, response.json)) as f: with open(startup_config_file(compute_project, response.json)) as f:
assert f.read() == "echo hello" assert f.read() == "echo hello"
def test_iou_get(http_compute, project, vm): async def test_iou_get(compute_api, compute_project, vm):
response = http_compute.get("/projects/{project_id}/iou/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True)
response = await compute_api.get("/projects/{project_id}/iou/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert response.status == 200 assert response.status == 200
assert response.route == "/projects/{project_id}/iou/nodes/{node_id}" assert response.route == "/projects/{project_id}/iou/nodes/{node_id}"
assert response.json["name"] == "PC TEST 1" assert response.json["name"] == "PC TEST 1"
assert response.json["project_id"] == project.id assert response.json["project_id"] == compute_project.id
assert response.json["serial_adapters"] == 2 assert response.json["serial_adapters"] == 2
assert response.json["ethernet_adapters"] == 2 assert response.json["ethernet_adapters"] == 2
assert response.json["ram"] == 256 assert response.json["ram"] == 256
@ -130,48 +135,53 @@ def test_iou_get(http_compute, project, vm):
assert response.json["l1_keepalives"] is False assert response.json["l1_keepalives"] is False
def test_iou_start(http_compute, vm): async def test_iou_start(compute_api, vm):
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.start", return_value=True) as mock: with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.start", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/iou/nodes/{node_id}/start".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await compute_api.post("/projects/{project_id}/iou/nodes/{node_id}/start".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 200 assert response.status == 200
assert response.json["name"] == "PC TEST 1" assert response.json["name"] == "PC TEST 1"
def test_iou_start_with_iourc(http_compute, vm, tmpdir): async def test_iou_start_with_iourc(compute_api, vm):
body = {"iourc_content": "test"}
params = {"iourc_content": "test"}
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.start", return_value=True) as mock: with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.start", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/iou/nodes/{node_id}/start".format(project_id=vm["project_id"], node_id=vm["node_id"]), body=body, example=True) response = await compute_api.post("/projects/{project_id}/iou/nodes/{node_id}/start".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
assert mock.called assert mock.called
assert response.status == 200 assert response.status == 200
response = http_compute.get("/projects/{project_id}/iou/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await compute_api.get("/projects/{project_id}/iou/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert response.status == 200 assert response.status == 200
def test_iou_stop(http_compute, vm): async def test_iou_stop(compute_api, vm):
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.stop", return_value=True) as mock: with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.stop", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/iou/nodes/{node_id}/stop".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.post("/projects/{project_id}/iou/nodes/{node_id}/stop".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_iou_reload(http_compute, vm): async def test_iou_reload(compute_api, vm):
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.reload", return_value=True) as mock: with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.reload", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/iou/nodes/{node_id}/reload".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.post("/projects/{project_id}/iou/nodes/{node_id}/reload".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_iou_delete(http_compute, vm): async def test_iou_delete(compute_api, vm):
with asyncio_patch("gns3server.compute.iou.IOU.delete_node", return_value=True) as mock: with asyncio_patch("gns3server.compute.iou.IOU.delete_node", return_value=True) as mock:
response = http_compute.delete("/projects/{project_id}/iou/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.delete("/projects/{project_id}/iou/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_iou_update(http_compute, vm, tmpdir, free_console_port, project): async def test_iou_update(compute_api, vm, free_console_port):
params = { params = {
"name": "test", "name": "test",
"console": free_console_port, "console": free_console_port,
@ -182,7 +192,8 @@ def test_iou_update(http_compute, vm, tmpdir, free_console_port, project):
"l1_keepalives": True, "l1_keepalives": True,
"use_default_iou_values": True, "use_default_iou_values": True,
} }
response = http_compute.put("/projects/{project_id}/iou/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), params, example=True)
response = await compute_api.put("/projects/{project_id}/iou/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
assert response.status == 200 assert response.status == 200
assert response.json["name"] == "test" assert response.json["name"] == "test"
assert response.json["console"] == free_console_port assert response.json["console"] == free_console_port
@ -194,120 +205,134 @@ def test_iou_update(http_compute, vm, tmpdir, free_console_port, project):
assert response.json["use_default_iou_values"] is True assert response.json["use_default_iou_values"] is True
def test_iou_nio_create_udp(http_compute, vm): async def test_iou_nio_create_udp(compute_api, vm):
response = http_compute.post("/projects/{project_id}/iou/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_udp",
"lport": 4242, params = {"type": "nio_udp",
"rport": 4343, "lport": 4242,
"rhost": "127.0.0.1"}, "rport": 4343,
example=True) "rhost": "127.0.0.1"}
response = await compute_api.post("/projects/{project_id}/iou/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
assert response.status == 201 assert response.status == 201
assert response.route == r"/projects/{project_id}/iou/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.route == r"/projects/{project_id}/iou/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
assert response.json["type"] == "nio_udp" assert response.json["type"] == "nio_udp"
def test_iou_nio_update_udp(http_compute, vm): async def test_iou_nio_update_udp(compute_api, vm):
response = http_compute.post("/projects/{project_id}/iou/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_udp",
"lport": 4242, params = {"type": "nio_udp",
"rport": 4343, "lport": 4242,
"rhost": "127.0.0.1"}) "rport": 4343,
response = http_compute.put("/projects/{project_id}/iou/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), "rhost": "127.0.0.1"}
{
"type": "nio_udp", await compute_api.post("/projects/{project_id}/iou/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
"lport": 4242,
"rport": 4343, params["filters"] = {}
"rhost": "127.0.0.1", response = await compute_api.put("/projects/{project_id}/iou/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
"filters": {}},
example=True)
assert response.status == 201, response.body.decode() assert response.status == 201, response.body.decode()
assert response.route == r"/projects/{project_id}/iou/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.route == r"/projects/{project_id}/iou/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
assert response.json["type"] == "nio_udp" assert response.json["type"] == "nio_udp"
def test_iou_nio_create_ethernet(http_compute, vm, ethernet_device): async def test_iou_nio_create_ethernet(compute_api, vm, ethernet_device):
response = http_compute.post("/projects/{project_id}/iou/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_ethernet",
"ethernet_device": ethernet_device, params = {
}, "type": "nio_ethernet",
example=True) "ethernet_device": ethernet_device
}
response = await compute_api.post("/projects/{project_id}/iou/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
assert response.status == 201 assert response.status == 201
assert response.route == r"/projects/{project_id}/iou/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.route == r"/projects/{project_id}/iou/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
assert response.json["type"] == "nio_ethernet" assert response.json["type"] == "nio_ethernet"
assert response.json["ethernet_device"] == ethernet_device assert response.json["ethernet_device"] == ethernet_device
def test_iou_nio_create_ethernet_different_port(http_compute, vm, ethernet_device): async def test_iou_nio_create_ethernet_different_port(compute_api, vm, ethernet_device):
response = http_compute.post("/projects/{project_id}/iou/nodes/{node_id}/adapters/0/ports/3/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_ethernet",
"ethernet_device": ethernet_device, params = {
}, "type": "nio_ethernet",
example=False) "ethernet_device": ethernet_device
}
response = await compute_api.post("/projects/{project_id}/iou/nodes/{node_id}/adapters/0/ports/3/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
assert response.status == 201 assert response.status == 201
assert response.route == r"/projects/{project_id}/iou/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.route == r"/projects/{project_id}/iou/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
assert response.json["type"] == "nio_ethernet" assert response.json["type"] == "nio_ethernet"
assert response.json["ethernet_device"] == ethernet_device assert response.json["ethernet_device"] == ethernet_device
def test_iou_nio_create_tap(http_compute, vm, ethernet_device): async def test_iou_nio_create_tap(compute_api, vm, ethernet_device):
params = {
"type": "nio_tap",
"tap_device": ethernet_device
}
with patch("gns3server.compute.base_manager.BaseManager.has_privileged_access", return_value=True): with patch("gns3server.compute.base_manager.BaseManager.has_privileged_access", return_value=True):
response = http_compute.post("/projects/{project_id}/iou/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_tap", response = await compute_api.post("/projects/{project_id}/iou/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
"tap_device": ethernet_device})
assert response.status == 201 assert response.status == 201
assert response.route == r"/projects/{project_id}/iou/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.route == r"/projects/{project_id}/iou/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
assert response.json["type"] == "nio_tap" assert response.json["type"] == "nio_tap"
def test_iou_delete_nio(http_compute, vm): async def test_iou_delete_nio(compute_api, vm):
http_compute.post("/projects/{project_id}/iou/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_udp",
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1"})
response = http_compute.delete("/projects/{project_id}/iou/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True)
assert response.status == 204
assert response.route == r"/projects/{project_id}/iou/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
params = {
"type": "nio_udp",
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1"
}
def test_iou_start_capture(http_compute, vm, tmpdir, project): await compute_api.post("/projects/{project_id}/iou/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
response = await compute_api.delete("/projects/{project_id}/iou/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert response.status == 204
assert response.route == r"/projects/{project_id}/iou/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
with patch("gns3server.compute.iou.iou_vm.IOUVM.is_running", return_value=True) as mock:
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.start_capture") as start_capture:
params = {"capture_file_name": "test.pcap", "data_link_type": "DLT_EN10MB"} async def test_iou_start_capture(compute_api, vm):
response = http_compute.post("/projects/{project_id}/iou/nodes/{node_id}/adapters/0/ports/0/start_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]), body=params, example=True)
params = {
"capture_file_name": "test.pcap",
"data_link_type": "DLT_EN10MB"
}
with patch("gns3server.compute.iou.iou_vm.IOUVM.is_running", return_value=True):
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.start_capture") as mock:
response = await compute_api.post("/projects/{project_id}/iou/nodes/{node_id}/adapters/0/ports/0/start_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
assert response.status == 200 assert response.status == 200
assert mock.called
assert start_capture.called
assert "test.pcap" in response.json["pcap_file_path"] assert "test.pcap" in response.json["pcap_file_path"]
def test_iou_stop_capture(http_compute, vm, tmpdir, project): async def test_iou_stop_capture(compute_api, vm):
with patch("gns3server.compute.iou.iou_vm.IOUVM.is_running", return_value=True) as mock:
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.stop_capture") as stop_capture:
response = http_compute.post("/projects/{project_id}/iou/nodes/{node_id}/adapters/0/ports/0/stop_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True)
with patch("gns3server.compute.iou.iou_vm.IOUVM.is_running", return_value=True):
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.stop_capture") as mock:
response = await compute_api.post("/projects/{project_id}/iou/nodes/{node_id}/adapters/0/ports/0/stop_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert response.status == 204 assert response.status == 204
assert mock.called
assert stop_capture.called
def test_iou_pcap(http_compute, vm, project): async def test_iou_pcap(compute_api, vm, compute_project):
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.get_nio"): with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.get_nio"):
with asyncio_patch("gns3server.compute.iou.IOU.stream_pcap_file"): with asyncio_patch("gns3server.compute.iou.IOU.stream_pcap_file"):
response = http_compute.get("/projects/{project_id}/iou/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=project.id, node_id=vm["node_id"]), raw=True) response = await compute_api.get("/projects/{project_id}/iou/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True)
assert response.status == 200 assert response.status == 200
def test_images(http_compute, fake_iou_bin): async def test_images(compute_api, fake_iou_bin):
response = http_compute.get("/iou/images", example=True) response = await compute_api.get("/iou/images")
assert response.status == 200 assert response.status == 200
assert response.json == [{"filename": "iou.bin", "path": "iou.bin", "filesize": 7, "md5sum": "e573e8f5c93c6c00783f20c7a170aa6c"}] assert response.json == [{"filename": "iou.bin", "path": "iou.bin", "filesize": 7, "md5sum": "e573e8f5c93c6c00783f20c7a170aa6c"}]
def test_image_vm(http_compute, tmpdir): async def test_image_vm(compute_api, tmpdir):
with patch("gns3server.compute.IOU.get_images_directory", return_value=str(tmpdir),):
response = http_compute.post("/iou/images/test2", body="TEST", raw=True) with patch("gns3server.compute.IOU.get_images_directory", return_value=str(tmpdir)):
response = await compute_api.post("/iou/images/test2", body="TEST", raw=True)
assert response.status == 204 assert response.status == 204
with open(str(tmpdir / "test2")) as f: with open(str(tmpdir / "test2")) as f:
@ -318,15 +343,10 @@ def test_image_vm(http_compute, tmpdir):
assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf" assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf"
def test_iou_duplicate(http_compute, vm): async def test_iou_duplicate(compute_api, vm):
params = {"destination_node_id": str(uuid.uuid4())}
with asyncio_patch("gns3server.compute.iou.IOU.duplicate_node", return_value=True) as mock: with asyncio_patch("gns3server.compute.iou.IOU.duplicate_node", return_value=True) as mock:
response = http_compute.post( response = await compute_api.post("/projects/{project_id}/iou/nodes/{node_id}/duplicate".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
"/projects/{project_id}/iou/nodes/{node_id}/duplicate".format(
project_id=vm["project_id"],
node_id=vm["node_id"]),
body={
"destination_node_id": str(uuid.uuid4())
},
example=True)
assert mock.called assert mock.called
assert response.status == 201 assert response.status == 201

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -16,125 +16,128 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import pytest import pytest
import sys
import os
from tests.utils import asyncio_patch from tests.utils import asyncio_patch
from unittest.mock import patch
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def vm(http_compute, project, on_gns3vm): async def vm(compute_api, compute_project, ubridge_path, on_gns3vm):
response = http_compute.post("/projects/{project_id}/nat/nodes".format(project_id=project.id), {"name": "Nat 1"})
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat._start_ubridge"):
response = await compute_api.post("/projects/{project_id}/nat/nodes".format(project_id=compute_project.id), {"name": "Nat 1"})
assert response.status == 201 assert response.status == 201
return response.json return response.json
@pytest.yield_fixture(autouse=True) async def test_nat_create(compute_api, compute_project, on_gns3vm):
def mock_ubridge():
"""
Avoid all interaction with ubridge
"""
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat._start_ubridge"):
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat._add_ubridge_connection"):
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat._delete_ubridge_connection"):
yield
def test_nat_create(http_compute, project, on_gns3vm): with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat._start_ubridge"):
response = http_compute.post("/projects/{project_id}/nat/nodes".format(project_id=project.id), {"name": "Nat 1"}, example=True) response = await compute_api.post("/projects/{project_id}/nat/nodes".format(project_id=compute_project.id), {"name": "Nat 1"})
assert response.status == 201 assert response.status == 201
assert response.route == "/projects/{project_id}/nat/nodes" assert response.route == "/projects/{project_id}/nat/nodes"
assert response.json["name"] == "Nat 1" assert response.json["name"] == "Nat 1"
assert response.json["project_id"] == project.id assert response.json["project_id"] == compute_project.id
def test_nat_get(http_compute, project, vm): async def test_nat_get(compute_api, compute_project, vm):
response = http_compute.get("/projects/{project_id}/nat/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True)
response = await compute_api.get("/projects/{project_id}/nat/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert response.status == 200 assert response.status == 200
assert response.route == "/projects/{project_id}/nat/nodes/{node_id}" assert response.route == "/projects/{project_id}/nat/nodes/{node_id}"
assert response.json["name"] == "Nat 1" assert response.json["name"] == "Nat 1"
assert response.json["project_id"] == project.id assert response.json["project_id"] == compute_project.id
assert response.json["status"] == "started" assert response.json["status"] == "started"
def test_nat_nio_create_udp(http_compute, vm): async def test_nat_nio_create_udp(compute_api, vm):
params = {
"type": "nio_udp",
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1"
}
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.add_nio"): with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.add_nio"):
response = http_compute.post("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_udp", response = await compute_api.post("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1"},
example=True)
assert response.status == 201 assert response.status == 201
assert response.route == r"/projects/{project_id}/nat/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.route == r"/projects/{project_id}/nat/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
assert response.json["type"] == "nio_udp" assert response.json["type"] == "nio_udp"
def test_nat_nio_update_udp(http_compute, vm): async def test_nat_nio_update_udp(compute_api, vm):
http_compute.post("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_udp",
"lport": 4242, params = {
"rport": 4343, "type": "nio_udp",
"rhost": "127.0.0.1"}) "lport": 4242,
response = http_compute.put("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), "rport": 4343,
{ "rhost": "127.0.0.1"
"type": "nio_udp", }
"lport": 4242,
"rport": 4343, await compute_api.post("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
"rhost": "127.0.0.1", params["filters"] = {}
"filters": {}}, response = await compute_api.put("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
example=True)
assert response.status == 201, response.body.decode() assert response.status == 201, response.body.decode()
assert response.route == r"/projects/{project_id}/nat/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.route == r"/projects/{project_id}/nat/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
assert response.json["type"] == "nio_udp" assert response.json["type"] == "nio_udp"
def test_nat_delete_nio(http_compute, vm): async def test_nat_delete_nio(compute_api, vm):
params = {
"type": "nio_udp",
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1"
}
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.add_nio"): with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.add_nio"):
http_compute.post("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_udp", await compute_api.post("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
"lport": 4242, with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.remove_nio") as mock:
"rport": 4343, response = await compute_api.delete("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]))
"rhost": "127.0.0.1"}) assert mock.called
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.remove_nio") as mock_remove_nio:
response = http_compute.delete("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True)
assert mock_remove_nio.called
assert response.status == 204 assert response.status == 204
assert response.route == r"/projects/{project_id}/nat/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.route == r"/projects/{project_id}/nat/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
def test_nat_delete(http_compute, vm): async def test_nat_delete(compute_api, vm):
response = http_compute.delete("/projects/{project_id}/nat/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True)
response = await compute_api.delete("/projects/{project_id}/nat/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert response.status == 204 assert response.status == 204
def test_nat_update(http_compute, vm, tmpdir): async def test_nat_update(compute_api, vm):
response = http_compute.put("/projects/{project_id}/nat/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), {
"name": "test" response = await compute_api.put("/projects/{project_id}/nat/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"name": "test"})
},
example=True)
assert response.status == 200 assert response.status == 200
assert response.json["name"] == "test" assert response.json["name"] == "test"
def test_nat_start_capture(http_compute, vm): async def test_nat_start_capture(compute_api, vm):
params = {
"capture_file_name": "test.pcap",
"data_link_type": "DLT_EN10MB"
}
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.start_capture") as start_capture: with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.start_capture") as mock:
params = {"capture_file_name": "test.pcap", "data_link_type": "DLT_EN10MB"} response = await compute_api.post("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/start_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]), body=params)
response = http_compute.post("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/start_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]), body=params, example=True)
assert response.status == 200 assert response.status == 200
assert start_capture.called assert mock.called
assert "test.pcap" in response.json["pcap_file_path"] assert "test.pcap" in response.json["pcap_file_path"]
def test_nat_stop_capture(http_compute, vm): async def test_nat_stop_capture(compute_api, vm):
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.stop_capture") as stop_capture: with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.stop_capture") as mock:
response = http_compute.post("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/stop_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.post("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/stop_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert response.status == 204 assert response.status == 204
assert stop_capture.called assert mock.called
def test_nat_pcap(http_compute, vm, project): async def test_nat_pcap(compute_api, vm, compute_project):
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.get_nio"): with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.get_nio"):
with asyncio_patch("gns3server.compute.builtin.Builtin.stream_pcap_file"): with asyncio_patch("gns3server.compute.builtin.Builtin.stream_pcap_file"):
response = http_compute.get("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=project.id, node_id=vm["node_id"]), raw=True) response = await compute_api.get("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True)
assert response.status == 200 assert response.status == 200

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -19,15 +19,17 @@ import os
import pytest import pytest
def test_udp_allocation(http_compute, project): async def test_udp_allocation(compute_api, compute_project):
response = http_compute.post('/projects/{}/ports/udp'.format(project.id), {}, example=True)
response = await compute_api.post('/projects/{}/ports/udp'.format(compute_project.id), {})
assert response.status == 201 assert response.status == 201
assert response.json['udp_port'] is not None assert response.json['udp_port'] is not None
# Netfifaces is not available on Travis # Netfifaces is not available on Travis
@pytest.mark.skipif(os.environ.get("TRAVIS", False) is not False, reason="Not supported on Travis") @pytest.mark.skipif(os.environ.get("TRAVIS", False) is not False, reason="Not supported on Travis")
def test_interfaces(http_compute): async def test_interfaces(compute_api):
response = http_compute.get('/network/interfaces', example=True)
response = await compute_api.get('/network/interfaces')
assert response.status == 200 assert response.status == 200
assert isinstance(response.json, list) assert isinstance(response.json, list)

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2016 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -20,16 +20,19 @@ import json
from gns3server.compute.notification_manager import NotificationManager from gns3server.compute.notification_manager import NotificationManager
def test_notification_ws(http_compute, async_run): async def test_notification_ws(compute_api, http_client):
ws = http_compute.websocket("/notifications/ws")
answer = async_run(ws.receive()) ws = await http_client.ws_connect(compute_api.get_url("/notifications/ws"))
answer = await ws.receive()
answer = json.loads(answer.data) answer = json.loads(answer.data)
assert answer["action"] == "ping" assert answer["action"] == "ping"
NotificationManager.instance().emit("test", {}) NotificationManager.instance().emit("test", {})
answer = async_run(ws.receive()) answer = await ws.receive()
answer = json.loads(answer.data) answer = json.loads(answer.data)
assert answer["action"] == "test" assert answer["action"] == "test"
async_run(http_compute.close()) if not ws.closed:
await ws.close()

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -15,10 +15,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
""" import pytest
This test suite check /project endpoint
"""
import uuid import uuid
import os import os
@ -29,123 +26,128 @@ from gns3server.handlers.api.compute.project_handler import ProjectHandler
from gns3server.compute.project_manager import ProjectManager from gns3server.compute.project_manager import ProjectManager
def test_create_project_with_path(http_compute, tmpdir): @pytest.fixture
def base_params(tmpdir):
"""Return standard parameters"""
params = {
"name": "test",
"path": str(tmpdir),
"project_id": str(uuid.uuid4())
}
return params
async def test_create_project_with_path(compute_api, base_params):
with patch("gns3server.compute.project.Project.is_local", return_value=True): with patch("gns3server.compute.project.Project.is_local", return_value=True):
response = http_compute.post("/projects", {"name": "test", "path": str(tmpdir), "project_id": "00010203-0405-0607-0809-0a0b0c0d0e0f"}) response = await compute_api.post("/projects", base_params)
assert response.status == 201 assert response.status == 201
assert response.json["project_id"] == "00010203-0405-0607-0809-0a0b0c0d0e0f" assert response.json["project_id"] == base_params["project_id"]
async def test_create_project_with_path_and_empty_variables(compute_api, base_params):
def test_create_project_with_path_and_empty_variables(http_compute, tmpdir): base_params["variables"] = None
with patch("gns3server.compute.project.Project.is_local", return_value=True): with patch("gns3server.compute.project.Project.is_local", return_value=True):
response = http_compute.post("/projects", {
"name": "test",
"path": str(tmpdir), "project_id": "00010203-0405-0607-0809-0a0b0c0d0e0f",
"variables": None})
assert response.status == 201
assert response.json["project_id"] == "00010203-0405-0607-0809-0a0b0c0d0e0f"
response = await compute_api.post("/projects", base_params)
assert response.status == 201
assert response.json["project_id"] == base_params["project_id"]
def test_create_project_without_dir(http_compute):
query = {"name": "test", "project_id": "10010203-0405-0607-0809-0a0b0c0d0e0f"}
response = http_compute.post("/projects", query, example=True)
assert response.status == 201
assert response.json["project_id"] == "10010203-0405-0607-0809-0a0b0c0d0e0f"
assert response.json["name"] == "test"
async def test_create_project_without_dir(compute_api, base_params):
def test_create_project_with_uuid(http_compute): del base_params["path"]
query = {"name": "test", "project_id": "30010203-0405-0607-0809-0a0b0c0d0e0f"} response = await compute_api.post("/projects", base_params)
response = http_compute.post("/projects", query)
assert response.status == 201 assert response.status == 201
assert response.json["project_id"] == "30010203-0405-0607-0809-0a0b0c0d0e0f" assert response.json["project_id"] == base_params["project_id"]
assert response.json["name"] == "test" assert response.json["name"] == base_params["name"]
def test_show_project(http_compute): async def test_show_project(compute_api, base_params):
query = {"name": "test", "project_id": "40010203-0405-0607-0809-0a0b0c0d0e02"}
response = http_compute.post("/projects", query) response = await compute_api.post("/projects", base_params)
assert response.status == 201 assert response.status == 201
response = http_compute.get("/projects/40010203-0405-0607-0809-0a0b0c0d0e02", example=True) response = await compute_api.get("/projects/{project_id}".format(project_id=base_params["project_id"]))
assert len(response.json.keys()) == 3 assert len(response.json.keys()) == 3
assert response.json["project_id"] == "40010203-0405-0607-0809-0a0b0c0d0e02" assert response.json["project_id"] == base_params["project_id"]
assert response.json["name"] == "test" assert response.json["name"] == base_params["name"]
assert response.json["variables"] is None assert response.json["variables"] is None
def test_show_project_invalid_uuid(http_compute): async def test_show_project_invalid_uuid(compute_api):
response = http_compute.get("/projects/50010203-0405-0607-0809-0a0b0c0d0e42")
response = await compute_api.get("/projects/50010203-0405-0607-0809-0a0b0c0d0e42")
assert response.status == 404 assert response.status == 404
def test_list_projects(http_compute): async def test_list_projects(compute_api):
ProjectManager.instance()._projects = {} ProjectManager.instance()._projects = {}
query = {"name": "test", "project_id": "51010203-0405-0607-0809-0a0b0c0d0e0f"} params = {"name": "test", "project_id": "51010203-0405-0607-0809-0a0b0c0d0e0f"}
response = http_compute.post("/projects", query) response = await compute_api.post("/projects", params)
assert response.status == 201 assert response.status == 201
query = {"name": "test", "project_id": "52010203-0405-0607-0809-0a0b0c0d0e0b"} params = {"name": "test", "project_id": "52010203-0405-0607-0809-0a0b0c0d0e0b"}
response = http_compute.post("/projects", query) response = await compute_api.post("/projects", params)
assert response.status == 201 assert response.status == 201
response = http_compute.get("/projects", example=True) response = await compute_api.get("/projects")
assert response.status == 200 assert response.status == 200
assert len(response.json) == 2 assert len(response.json) == 2
assert "51010203-0405-0607-0809-0a0b0c0d0e0f" in [p["project_id"] for p in response.json] assert "51010203-0405-0607-0809-0a0b0c0d0e0f" in [p["project_id"] for p in response.json]
def test_delete_project(http_compute, project): async def test_delete_project(compute_api, compute_project):
with asyncio_patch("gns3server.compute.project.Project.delete", return_value=True) as mock: with asyncio_patch("gns3server.compute.project.Project.delete", return_value=True) as mock:
response = http_compute.delete("/projects/{project_id}".format(project_id=project.id), example=True) response = await compute_api.delete("/projects/{project_id}".format(project_id=compute_project.id))
assert response.status == 204 assert response.status == 204
assert mock.called assert mock.called
def test_update_project(http_compute): async def test_update_project(compute_api, base_params):
query = {"name": "test", "project_id": "51010203-0405-0607-0809-0a0b0c0d0e0f"}
response = http_compute.post("/projects", query) response = await compute_api.post("/projects", base_params)
assert response.status == 201 assert response.status == 201
query = { params = {"variables": [{"name": "TEST1", "value": "VAL1"}]}
"variables": [{"name": "TEST1", "value": "VAL1"}] response = await compute_api.put("/projects/{project_id}".format(project_id=base_params["project_id"]), params)
}
response = http_compute.put(
"/projects/{project_id}".format(project_id="51010203-0405-0607-0809-0a0b0c0d0e0f"),
query,
example=True
)
assert response.status == 200 assert response.status == 200
assert response.json["variables"] == [{"name": "TEST1", "value": "VAL1"}] assert response.json["variables"] == [{"name": "TEST1", "value": "VAL1"}]
def test_delete_project_invalid_uuid(http_compute): async def test_delete_project_invalid_uuid(compute_api):
response = http_compute.delete("/projects/{project_id}".format(project_id=uuid.uuid4()))
response = await compute_api.delete("/projects/{project_id}".format(project_id=uuid.uuid4()))
assert response.status == 404 assert response.status == 404
def test_close_project(http_compute, project): async def test_close_project(compute_api, compute_project):
with asyncio_patch("gns3server.compute.project.Project.close", return_value=True) as mock: with asyncio_patch("gns3server.compute.project.Project.close", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/close".format(project_id=project.id), example=True) response = await compute_api.post("/projects/{project_id}/close".format(project_id=compute_project.id))
assert response.status == 204 assert response.status == 204
assert mock.called assert mock.called
def test_close_project_two_client_connected(http_compute, project): async def test_close_project_two_client_connected(compute_api, compute_project):
ProjectHandler._notifications_listening = {project.id: 2}
ProjectHandler._notifications_listening = {compute_project.id: 2}
with asyncio_patch("gns3server.compute.project.Project.close", return_value=True) as mock: with asyncio_patch("gns3server.compute.project.Project.close", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/close".format(project_id=project.id), example=True) response = await compute_api.post("/projects/{project_id}/close".format(project_id=compute_project.id))
assert response.status == 204 assert response.status == 204
assert not mock.called assert not mock.called
def test_close_project_invalid_uuid(http_compute): async def test_close_project_invalid_uuid(compute_api):
response = http_compute.post("/projects/{project_id}/close".format(project_id=uuid.uuid4()))
response = await compute_api.post("/projects/{project_id}/close".format(project_id=uuid.uuid4()))
assert response.status == 404 assert response.status == 404
def test_get_file(http_compute, tmpdir): async def test_get_file(compute_api, tmpdir):
with patch("gns3server.config.Config.get_section_config", return_value={"projects_path": str(tmpdir)}): with patch("gns3server.config.Config.get_section_config", return_value={"projects_path": str(tmpdir)}):
project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b") project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b")
@ -153,33 +155,33 @@ def test_get_file(http_compute, tmpdir):
with open(os.path.join(project.path, "hello"), "w+") as f: with open(os.path.join(project.path, "hello"), "w+") as f:
f.write("world") f.write("world")
response = http_compute.get("/projects/{project_id}/files/hello".format(project_id=project.id), raw=True) response = await compute_api.get("/projects/{project_id}/files/hello".format(project_id=project.id), raw=True)
assert response.status == 200 assert response.status == 200
assert response.body == b"world" assert response.body == b"world"
response = http_compute.get("/projects/{project_id}/files/false".format(project_id=project.id), raw=True) response = await compute_api.get("/projects/{project_id}/files/false".format(project_id=project.id), raw=True)
assert response.status == 404 assert response.status == 404
response = http_compute.get("/projects/{project_id}/files/../hello".format(project_id=project.id), raw=True) response = await compute_api.get("/projects/{project_id}/files/../hello".format(project_id=project.id), raw=True)
assert response.status == 404 assert response.status == 404
def test_write_file(http_compute, tmpdir): async def test_write_file(compute_api, tmpdir):
with patch("gns3server.config.Config.get_section_config", return_value={"projects_path": str(tmpdir)}): with patch("gns3server.config.Config.get_section_config", return_value={"projects_path": str(tmpdir)}):
project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b") project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b")
response = http_compute.post("/projects/{project_id}/files/hello".format(project_id=project.id), body="world", raw=True) response = await compute_api.post("/projects/{project_id}/files/hello".format(project_id=project.id), body="world", raw=True)
assert response.status == 200 assert response.status == 200
with open(os.path.join(project.path, "hello")) as f: with open(os.path.join(project.path, "hello")) as f:
assert f.read() == "world" assert f.read() == "world"
response = http_compute.post("/projects/{project_id}/files/../hello".format(project_id=project.id), raw=True) response = await compute_api.post("/projects/{project_id}/files/../hello".format(project_id=project.id), raw=True)
assert response.status == 404 assert response.status == 404
def test_stream_file(http_compute, tmpdir): async def test_stream_file(compute_api, tmpdir):
with patch("gns3server.config.Config.get_section_config", return_value={"projects_path": str(tmpdir)}): with patch("gns3server.config.Config.get_section_config", return_value={"projects_path": str(tmpdir)}):
project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b") project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b")
@ -187,12 +189,12 @@ def test_stream_file(http_compute, tmpdir):
with open(os.path.join(project.path, "hello"), "w+") as f: with open(os.path.join(project.path, "hello"), "w+") as f:
f.write("world") f.write("world")
response = http_compute.get("/projects/{project_id}/files/hello".format(project_id=project.id), raw=True) response = await compute_api.get("/projects/{project_id}/files/hello".format(project_id=project.id), raw=True)
assert response.status == 200 assert response.status == 200
assert response.body == b"world" assert response.body == b"world"
response = http_compute.get("/projects/{project_id}/files/false".format(project_id=project.id), raw=True) response = await compute_api.get("/projects/{project_id}/files/false".format(project_id=project.id), raw=True)
assert response.status == 404 assert response.status == 404
response = http_compute.get("/projects/{project_id}/files/../hello".format(project_id=project.id), raw=True) response = await compute_api.get("/projects/{project_id}/files/../hello".format(project_id=project.id), raw=True)
assert response.status == 404 assert response.status == 404

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -22,15 +22,16 @@ import sys
import stat import stat
from tests.utils import asyncio_patch from tests.utils import asyncio_patch
from unittest.mock import patch from unittest.mock import patch
from gns3server.config import Config
@pytest.fixture @pytest.fixture
def fake_qemu_bin(): def fake_qemu_bin(monkeypatch, tmpdir):
monkeypatch.setenv("PATH", str(tmpdir))
if sys.platform.startswith("win"): if sys.platform.startswith("win"):
bin_path = os.path.join(os.environ["PATH"], "qemu-system-x86_64w.exe") bin_path = os.path.join(os.environ["PATH"], "qemu-system-x86_64w.exe")
else: else:
bin_path = os.path.join(os.environ["PATH"], "qemu-system-x86_64") bin_path = os.path.join(os.environ["PATH"], "qemu-system-x86_64")
with open(bin_path, "w+") as f: with open(bin_path, "w+") as f:
f.write("1") f.write("1")
@ -52,127 +53,139 @@ def fake_qemu_vm(images_dir):
@pytest.fixture @pytest.fixture
def base_params(tmpdir, fake_qemu_bin): def base_params(tmpdir, fake_qemu_bin):
"""Return standard parameters""" """Return standard parameters"""
return {"name": "PC TEST 1", "qemu_path": fake_qemu_bin} return {"name": "PC TEST 1", "qemu_path": fake_qemu_bin}
@pytest.fixture @pytest.fixture
def vm(http_compute, project, base_params): async def vm(compute_api, compute_project, base_params):
response = http_compute.post("/projects/{project_id}/qemu/nodes".format(project_id=project.id), base_params)
response = await compute_api.post("/projects/{project_id}/qemu/nodes".format(project_id=compute_project.id), base_params)
assert response.status == 201 assert response.status == 201
return response.json return response.json
def test_qemu_create(http_compute, project, base_params, fake_qemu_bin): async def test_qemu_create(compute_api, compute_project, base_params, fake_qemu_bin):
response = http_compute.post("/projects/{project_id}/qemu/nodes".format(project_id=project.id), base_params)
response = await compute_api.post("/projects/{project_id}/qemu/nodes".format(project_id=compute_project.id), base_params)
assert response.status == 201 assert response.status == 201
assert response.route == "/projects/{project_id}/qemu/nodes" assert response.route == "/projects/{project_id}/qemu/nodes"
assert response.json["name"] == "PC TEST 1" assert response.json["name"] == "PC TEST 1"
assert response.json["project_id"] == project.id assert response.json["project_id"] == compute_project.id
assert response.json["qemu_path"] == fake_qemu_bin assert response.json["qemu_path"] == fake_qemu_bin
assert response.json["platform"] == "x86_64" assert response.json["platform"] == "x86_64"
def test_qemu_create_platform(http_compute, project, base_params, fake_qemu_bin): async def test_qemu_create_platform(compute_api, compute_project, base_params, fake_qemu_bin):
base_params["qemu_path"] = None base_params["qemu_path"] = None
base_params["platform"] = "x86_64" base_params["platform"] = "x86_64"
response = await compute_api.post("/projects/{project_id}/qemu/nodes".format(project_id=compute_project.id), base_params)
response = http_compute.post("/projects/{project_id}/qemu/nodes".format(project_id=project.id), base_params)
assert response.status == 201 assert response.status == 201
assert response.route == "/projects/{project_id}/qemu/nodes" assert response.route == "/projects/{project_id}/qemu/nodes"
assert response.json["name"] == "PC TEST 1" assert response.json["name"] == "PC TEST 1"
assert response.json["project_id"] == project.id assert response.json["project_id"] == compute_project.id
assert response.json["qemu_path"] == fake_qemu_bin assert response.json["qemu_path"] == fake_qemu_bin
assert response.json["platform"] == "x86_64" assert response.json["platform"] == "x86_64"
def test_qemu_create_with_params(http_compute, project, base_params, fake_qemu_vm): async def test_qemu_create_with_params(compute_api, compute_project, base_params, fake_qemu_vm):
params = base_params params = base_params
params["ram"] = 1024 params["ram"] = 1024
params["hda_disk_image"] = "linux载.img" params["hda_disk_image"] = "linux载.img"
response = await compute_api.post("/projects/{project_id}/qemu/nodes".format(project_id=compute_project.id), params)
response = http_compute.post("/projects/{project_id}/qemu/nodes".format(project_id=project.id), params, example=True)
assert response.status == 201 assert response.status == 201
assert response.route == "/projects/{project_id}/qemu/nodes" assert response.route == "/projects/{project_id}/qemu/nodes"
assert response.json["name"] == "PC TEST 1" assert response.json["name"] == "PC TEST 1"
assert response.json["project_id"] == project.id assert response.json["project_id"] == compute_project.id
assert response.json["ram"] == 1024 assert response.json["ram"] == 1024
assert response.json["hda_disk_image"] == "linux载.img" assert response.json["hda_disk_image"] == "linux载.img"
assert response.json["hda_disk_image_md5sum"] == "c4ca4238a0b923820dcc509a6f75849b" assert response.json["hda_disk_image_md5sum"] == "c4ca4238a0b923820dcc509a6f75849b"
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") @pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
def test_qemu_create_with_project_file(http_compute, project, base_params, fake_qemu_vm): async def test_qemu_create_with_project_file(compute_api, compute_project, base_params, fake_qemu_vm):
response = http_compute.post("/projects/{project_id}/files/hello.img".format(project_id=project.id), body="world", raw=True)
response = await compute_api.post("/projects/{project_id}/files/hello.img".format(project_id=compute_project.id), body="world", raw=True)
assert response.status == 200 assert response.status == 200
params = base_params params = base_params
params["hda_disk_image"] = "hello.img" params["hda_disk_image"] = "hello.img"
response = http_compute.post("/projects/{project_id}/qemu/nodes".format(project_id=project.id), params, example=True) response = await compute_api.post("/projects/{project_id}/qemu/nodes".format(project_id=compute_project.id), params)
assert response.status == 201 assert response.status == 201
assert response.json["hda_disk_image"] == "hello.img" assert response.json["hda_disk_image"] == "hello.img"
assert response.json["hda_disk_image_md5sum"] == "7d793037a0760186574b0282f2f435e7" assert response.json["hda_disk_image_md5sum"] == "7d793037a0760186574b0282f2f435e7"
def test_qemu_get(http_compute, project, vm): async def test_qemu_get(compute_api, compute_project, vm):
response = http_compute.get("/projects/{project_id}/qemu/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True)
response = await compute_api.get("/projects/{project_id}/qemu/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert response.status == 200 assert response.status == 200
assert response.route == "/projects/{project_id}/qemu/nodes/{node_id}" assert response.route == "/projects/{project_id}/qemu/nodes/{node_id}"
assert response.json["name"] == "PC TEST 1" assert response.json["name"] == "PC TEST 1"
assert response.json["project_id"] == project.id assert response.json["project_id"] == compute_project.id
assert response.json["node_directory"] == os.path.join(project.path, "project-files", "qemu", vm["node_id"]) assert response.json["node_directory"] == os.path.join(compute_project.path, "project-files", "qemu", vm["node_id"])
def test_qemu_start(http_compute, vm): async def test_qemu_start(compute_api, vm):
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.start", return_value=True) as mock: with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.start", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/qemu/nodes/{node_id}/start".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.post("/projects/{project_id}/qemu/nodes/{node_id}/start".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 200 assert response.status == 200
assert response.json["name"] == "PC TEST 1" assert response.json["name"] == "PC TEST 1"
def test_qemu_stop(http_compute, vm): async def test_qemu_stop(compute_api, vm):
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.stop", return_value=True) as mock: with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.stop", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/qemu/nodes/{node_id}/stop".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.post("/projects/{project_id}/qemu/nodes/{node_id}/stop".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_qemu_reload(http_compute, vm): async def test_qemu_reload(compute_api, vm):
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.reload", return_value=True) as mock: with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.reload", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/qemu/nodes/{node_id}/reload".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.post("/projects/{project_id}/qemu/nodes/{node_id}/reload".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_qemu_suspend(http_compute, vm): async def test_qemu_suspend(compute_api, vm):
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.suspend", return_value=True) as mock: with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.suspend", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/qemu/nodes/{node_id}/suspend".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.post("/projects/{project_id}/qemu/nodes/{node_id}/suspend".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_qemu_resume(http_compute, vm): async def test_qemu_resume(compute_api, vm):
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.resume", return_value=True) as mock: with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.resume", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/qemu/nodes/{node_id}/resume".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.post("/projects/{project_id}/qemu/nodes/{node_id}/resume".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_qemu_delete(http_compute, vm): async def test_qemu_delete(compute_api, vm):
with asyncio_patch("gns3server.compute.qemu.Qemu.delete_node", return_value=True) as mock: with asyncio_patch("gns3server.compute.qemu.Qemu.delete_node", return_value=True) as mock:
response = http_compute.delete("/projects/{project_id}/qemu/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.delete("/projects/{project_id}/qemu/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_qemu_update(http_compute, vm, free_console_port, project, fake_qemu_vm): async def test_qemu_update(compute_api, vm, free_console_port, fake_qemu_vm):
params = { params = {
"name": "test", "name": "test",
"console": free_console_port, "console": free_console_port,
"ram": 1024, "ram": 1024,
"hdb_disk_image": "linux载.img" "hdb_disk_image": "linux载.img"
} }
response = http_compute.put("/projects/{project_id}/qemu/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), params, example=True)
response = await compute_api.put("/projects/{project_id}/qemu/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
assert response.status == 200 assert response.status == 200
assert response.json["name"] == "test" assert response.json["name"] == "test"
assert response.json["console"] == free_console_port assert response.json["console"] == free_console_port
@ -180,83 +193,97 @@ def test_qemu_update(http_compute, vm, free_console_port, project, fake_qemu_vm)
assert response.json["ram"] == 1024 assert response.json["ram"] == 1024
def test_qemu_nio_create_udp(http_compute, vm): async def test_qemu_nio_create_udp(compute_api, vm):
params = {
"type": "nio_udp",
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1"
}
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.add_ubridge_udp_connection"): with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.add_ubridge_udp_connection"):
http_compute.put("/projects/{project_id}/qemu/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"adapters": 2}) await compute_api.put("/projects/{project_id}/qemu/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"adapters": 2})
response = http_compute.post("/projects/{project_id}/qemu/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_udp", response = await compute_api.post("/projects/{project_id}/qemu/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1"},
example=True)
assert response.status == 201 assert response.status == 201
assert response.route == r"/projects/{project_id}/qemu/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.route == r"/projects/{project_id}/qemu/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
assert response.json["type"] == "nio_udp" assert response.json["type"] == "nio_udp"
def test_qemu_nio_update_udp(http_compute, vm): async def test_qemu_nio_update_udp(compute_api, vm):
http_compute.put("/projects/{project_id}/qemu/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"adapters": 2})
http_compute.post("/projects/{project_id}/qemu/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_udp", params = {
"lport": 4242, "type": "nio_udp",
"rport": 4343, "lport": 4242,
"rhost": "127.0.0.1"}) "rport": 4343,
response = http_compute.put("/projects/{project_id}/qemu/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), "rhost": "127.0.0.1"
{ }
"type": "nio_udp",
"lport": 4242, await compute_api.put("/projects/{project_id}/qemu/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"adapters": 2})
"rport": 4343, await compute_api.post("/projects/{project_id}/qemu/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
"rhost": "127.0.0.1",
"filters": {}}, params["filters"] = {}
example=True) response = await compute_api.put("/projects/{project_id}/qemu/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
assert response.status == 201, response.body.decode() assert response.status == 201, response.body.decode()
assert response.route == r"/projects/{project_id}/qemu/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.route == r"/projects/{project_id}/qemu/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
assert response.json["type"] == "nio_udp" assert response.json["type"] == "nio_udp"
def test_qemu_delete_nio(http_compute, vm): async def test_qemu_delete_nio(compute_api, vm):
params = {
"type": "nio_udp",
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1"
}
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM._ubridge_send"): with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM._ubridge_send"):
http_compute.put("/projects/{project_id}/qemu/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"adapters": 2}) await compute_api.put("/projects/{project_id}/qemu/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"adapters": 2})
http_compute.post("/projects/{project_id}/qemu/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_udp", await compute_api.post("/projects/{project_id}/qemu/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
"lport": 4242, response = await compute_api.delete("/projects/{project_id}/qemu/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]))
"rport": 4343,
"rhost": "127.0.0.1"})
response = http_compute.delete("/projects/{project_id}/qemu/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True)
assert response.status == 204 assert response.status == 204
assert response.route == r"/projects/{project_id}/qemu/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.route == r"/projects/{project_id}/qemu/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
def test_qemu_list_binaries(http_compute, vm): async def test_qemu_list_binaries(compute_api, vm):
ret = [{"path": "/tmp/1", "version": "2.2.0"}, ret = [{"path": "/tmp/1", "version": "2.2.0"},
{"path": "/tmp/2", "version": "2.1.0"}] {"path": "/tmp/2", "version": "2.1.0"}]
with asyncio_patch("gns3server.compute.qemu.Qemu.binary_list", return_value=ret) as mock: with asyncio_patch("gns3server.compute.qemu.Qemu.binary_list", return_value=ret) as mock:
response = http_compute.get("/qemu/binaries".format(project_id=vm["project_id"]), example=True) response = await compute_api.get("/qemu/binaries".format(project_id=vm["project_id"]))
assert mock.called_with(None) assert mock.called_with(None)
assert response.status == 200 assert response.status == 200
assert response.json == ret assert response.json == ret
def test_qemu_list_binaries_filter(http_compute, vm): async def test_qemu_list_binaries_filter(compute_api, vm):
ret = [ ret = [
{"path": "/tmp/x86_64", "version": "2.2.0"}, {"path": "/tmp/x86_64", "version": "2.2.0"},
{"path": "/tmp/alpha", "version": "2.1.0"}, {"path": "/tmp/alpha", "version": "2.1.0"},
{"path": "/tmp/i386", "version": "2.1.0"} {"path": "/tmp/i386", "version": "2.1.0"}
] ]
with asyncio_patch("gns3server.compute.qemu.Qemu.binary_list", return_value=ret) as mock: with asyncio_patch("gns3server.compute.qemu.Qemu.binary_list", return_value=ret) as mock:
response = http_compute.get("/qemu/binaries".format(project_id=vm["project_id"]), body={"archs": ["i386"]}, example=True) response = await compute_api.get("/qemu/binaries".format(project_id=vm["project_id"]), body={"archs": ["i386"]})
assert response.status == 200 assert response.status == 200
assert mock.called_with(["i386"]) assert mock.called_with(["i386"])
assert response.json == ret assert response.json == ret
def test_images(http_compute, tmpdir, fake_qemu_vm): async def test_images(compute_api, fake_qemu_vm):
response = http_compute.get("/qemu/images") response = await compute_api.get("/qemu/images")
assert response.status == 200 assert response.status == 200
assert response.json == [{"filename": "linux载.img", "path": "linux载.img", "md5sum": "c4ca4238a0b923820dcc509a6f75849b", "filesize": 1}] assert response.json == [{"filename": "linux载.img", "path": "linux载.img", "md5sum": "c4ca4238a0b923820dcc509a6f75849b", "filesize": 1}]
def test_upload_image(http_compute, tmpdir): async def test_upload_image(compute_api, tmpdir):
with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir),):
response = http_compute.post("/qemu/images/test2使", body="TEST", raw=True) with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir)):
response = await compute_api.post("/qemu/images/test2使", body="TEST", raw=True)
assert response.status == 204 assert response.status == 204
with open(str(tmpdir / "test2使")) as f: with open(str(tmpdir / "test2使")) as f:
@ -267,9 +294,10 @@ def test_upload_image(http_compute, tmpdir):
assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf" assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf"
def test_upload_image_ova(http_compute, tmpdir): async def test_upload_image_ova(compute_api, tmpdir):
with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir),):
response = http_compute.post("/qemu/images/test2.ova/test2.vmdk", body="TEST", raw=True) with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir)):
response = await compute_api.post("/qemu/images/test2.ova/test2.vmdk", body="TEST", raw=True)
assert response.status == 204 assert response.status == 204
with open(str(tmpdir / "test2.ova" / "test2.vmdk")) as f: with open(str(tmpdir / "test2.ova" / "test2.vmdk")) as f:
@ -280,24 +308,27 @@ def test_upload_image_ova(http_compute, tmpdir):
assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf" assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf"
def test_upload_image_forbiden_location(http_compute, tmpdir): async def test_upload_image_forbiden_location(compute_api, tmpdir):
with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir),):
response = http_compute.post("/qemu/images/../../test2", body="TEST", raw=True) with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir)):
response = await compute_api.post("/qemu/images/../../test2", body="TEST", raw=True)
assert response.status == 404 assert response.status == 404
def test_upload_image_permission_denied(http_compute, tmpdir): async def test_upload_image_permission_denied(compute_api, tmpdir):
with open(str(tmpdir / "test2.tmp"), "w+") as f: with open(str(tmpdir / "test2.tmp"), "w+") as f:
f.write("") f.write("")
os.chmod(str(tmpdir / "test2.tmp"), 0) os.chmod(str(tmpdir / "test2.tmp"), 0)
with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir),): with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir)):
response = http_compute.post("/qemu/images/test2", body="TEST", raw=True) response = await compute_api.post("/qemu/images/test2", body="TEST", raw=True)
assert response.status == 409 assert response.status == 409
def test_create_img_relative(http_compute): async def test_create_img_relative(compute_api):
body = {
params = {
"qemu_img": "/tmp/qemu-img", "qemu_img": "/tmp/qemu-img",
"path": "hda.qcow2", "path": "hda.qcow2",
"format": "qcow2", "format": "qcow2",
@ -308,17 +339,14 @@ def test_create_img_relative(http_compute):
"size": 100 "size": 100
} }
with asyncio_patch("gns3server.compute.Qemu.create_disk"): with asyncio_patch("gns3server.compute.Qemu.create_disk"):
response = http_compute.post("/qemu/img", body=body, example=True) response = await compute_api.post("/qemu/img", params)
assert response.status == 201 assert response.status == 201
def test_create_img_absolute_non_local(http_compute): async def test_create_img_absolute_non_local(compute_api, config):
config = Config.instance()
config.set("Server", "local", "false") config.set("Server", "local", "false")
params = {
body = {
"qemu_img": "/tmp/qemu-img", "qemu_img": "/tmp/qemu-img",
"path": "/tmp/hda.qcow2", "path": "/tmp/hda.qcow2",
"format": "qcow2", "format": "qcow2",
@ -329,17 +357,14 @@ def test_create_img_absolute_non_local(http_compute):
"size": 100 "size": 100
} }
with asyncio_patch("gns3server.compute.Qemu.create_disk"): with asyncio_patch("gns3server.compute.Qemu.create_disk"):
response = http_compute.post("/qemu/img", body=body, example=True) response = await compute_api.post("/qemu/img", params)
assert response.status == 403 assert response.status == 403
def test_create_img_absolute_local(http_compute): async def test_create_img_absolute_local(compute_api, config):
config = Config.instance()
config.set("Server", "local", "true") config.set("Server", "local", "true")
params = {
body = {
"qemu_img": "/tmp/qemu-img", "qemu_img": "/tmp/qemu-img",
"path": "/tmp/hda.qcow2", "path": "/tmp/hda.qcow2",
"format": "qcow2", "format": "qcow2",
@ -350,54 +375,53 @@ def test_create_img_absolute_local(http_compute):
"size": 100 "size": 100
} }
with asyncio_patch("gns3server.compute.Qemu.create_disk"): with asyncio_patch("gns3server.compute.Qemu.create_disk"):
response = http_compute.post("/qemu/img", body=body, example=True) response = await compute_api.post("/qemu/img", params)
assert response.status == 201 assert response.status == 201
def test_capabilities(http_compute): async def test_capabilities(compute_api):
with asyncio_patch("gns3server.compute.Qemu.get_kvm_archs", return_value=["x86_64"]): with asyncio_patch("gns3server.compute.Qemu.get_kvm_archs", return_value=["x86_64"]):
response = http_compute.get("/qemu/capabilities", example=True) response = await compute_api.get("/qemu/capabilities")
assert response.json["kvm"] == ["x86_64"] assert response.json["kvm"] == ["x86_64"]
def test_qemu_duplicate(http_compute, vm): async def test_qemu_duplicate(compute_api, vm):
params = {"destination_node_id": str(uuid.uuid4())}
with asyncio_patch("gns3server.compute.qemu.Qemu.duplicate_node", return_value=True) as mock: with asyncio_patch("gns3server.compute.qemu.Qemu.duplicate_node", return_value=True) as mock:
response = http_compute.post( response = await compute_api.post("/projects/{project_id}/qemu/nodes/{node_id}/duplicate".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
"/projects/{project_id}/qemu/nodes/{node_id}/duplicate".format(
project_id=vm["project_id"],
node_id=vm["node_id"]),
body={
"destination_node_id": str(uuid.uuid4())
},
example=True)
assert mock.called assert mock.called
assert response.status == 201 assert response.status == 201
def test_qemu_start_capture(http_compute, vm): async def test_qemu_start_capture(compute_api, vm):
params = {
"capture_file_name": "test.pcap",
"data_link_type": "DLT_EN10MB"
}
with patch("gns3server.compute.qemu.qemu_vm.QemuVM.is_running", return_value=True): with patch("gns3server.compute.qemu.qemu_vm.QemuVM.is_running", return_value=True):
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.start_capture") as start_capture: with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.start_capture") as mock:
params = {"capture_file_name": "test.pcap", "data_link_type": "DLT_EN10MB"} response = await compute_api.post("/projects/{project_id}/qemu/nodes/{node_id}/adapters/0/ports/0/start_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
response = http_compute.post("/projects/{project_id}/qemu/nodes/{node_id}/adapters/0/ports/0/start_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]), body=params, example=True)
assert response.status == 200 assert response.status == 200
assert start_capture.called assert mock.called
assert "test.pcap" in response.json["pcap_file_path"] assert "test.pcap" in response.json["pcap_file_path"]
def test_qemu_stop_capture(http_compute, vm): async def test_qemu_stop_capture(compute_api, vm):
with patch("gns3server.compute.qemu.qemu_vm.QemuVM.is_running", return_value=True): with patch("gns3server.compute.qemu.qemu_vm.QemuVM.is_running", return_value=True):
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.stop_capture") as stop_capture: with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.stop_capture") as mock:
response = http_compute.post("/projects/{project_id}/qemu/nodes/{node_id}/adapters/0/ports/0/stop_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.post("/projects/{project_id}/qemu/nodes/{node_id}/adapters/0/ports/0/stop_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert response.status == 204 assert response.status == 204
assert stop_capture.called assert mock.called
def test_qemu_pcap(http_compute, vm, project): async def test_qemu_pcap(compute_api, vm, compute_project):
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.get_nio"): with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.get_nio"):
with asyncio_patch("gns3server.compute.qemu.Qemu.stream_pcap_file"): with asyncio_patch("gns3server.compute.qemu.Qemu.stream_pcap_file"):
response = http_compute.get("/projects/{project_id}/qemu/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=project.id, node_id=vm["node_id"]), raw=True) response = await compute_api.get("/projects/{project_id}/qemu/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True)
assert response.status == 200 assert response.status == 200

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -15,30 +15,25 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
This test suite check /version endpoint
It's also used for unittest the HTTP implementation.
"""
from gns3server.config import Config
from gns3server.version import __version__ from gns3server.version import __version__
def test_version_output(http_compute): async def test_version_output(compute_api, config):
config = Config.instance()
config.set("Server", "local", "true")
response = http_compute.get('/version', example=True) config.set("Server", "local", "true")
response = await compute_api.get('/version')
assert response.status == 200 assert response.status == 200
assert response.json == {'local': True, 'version': __version__} assert response.json == {'local': True, 'version': __version__}
def test_debug_output(http_compute): async def test_debug_output(compute_api):
response = http_compute.get('/debug')
response = await compute_api.get('/debug')
assert response.status == 200 assert response.status == 200
def test_statistics_output(http_compute): async def test_statistics_output(compute_api):
response = http_compute.get('/statistics')
response = await compute_api.get('/statistics')
assert response.status == 200 assert response.status == 200

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2018 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -17,157 +17,174 @@
import pytest import pytest
import uuid import uuid
import sys
import os
from tests.utils import asyncio_patch from tests.utils import asyncio_patch
from unittest.mock import patch from unittest.mock import patch
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def vm(http_compute, project): async def vm(compute_api, compute_project):
response = http_compute.post("/projects/{project_id}/traceng/nodes".format(project_id=project.id), {"name": "TraceNG TEST 1"})
params = {"name": "TraceNG TEST 1"}
response = await compute_api.post("/projects/{project_id}/traceng/nodes".format(project_id=compute_project.id), params)
assert response.status == 201 assert response.status == 201
return response.json return response.json
def test_traceng_create(http_compute, project): async def test_traceng_create(compute_api, compute_project):
response = http_compute.post("/projects/{project_id}/traceng/nodes".format(project_id=project.id), {"name": "TraceNG TEST 1"}, example=True)
params = {"name": "TraceNG TEST 1"}
response = await compute_api.post("/projects/{project_id}/traceng/nodes".format(project_id=compute_project.id), params)
assert response.status == 201 assert response.status == 201
assert response.route == "/projects/{project_id}/traceng/nodes" assert response.route == "/projects/{project_id}/traceng/nodes"
assert response.json["name"] == "TraceNG TEST 1" assert response.json["name"] == "TraceNG TEST 1"
assert response.json["project_id"] == project.id assert response.json["project_id"] == compute_project.id
def test_traceng_get(http_compute, project, vm): async def test_traceng_get(compute_api, compute_project, vm):
response = http_compute.get("/projects/{project_id}/traceng/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True)
response = await compute_api.get("/projects/{project_id}/traceng/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert response.status == 200 assert response.status == 200
assert response.route == "/projects/{project_id}/traceng/nodes/{node_id}" assert response.route == "/projects/{project_id}/traceng/nodes/{node_id}"
assert response.json["name"] == "TraceNG TEST 1" assert response.json["name"] == "TraceNG TEST 1"
assert response.json["project_id"] == project.id assert response.json["project_id"] == compute_project.id
assert response.json["status"] == "stopped" assert response.json["status"] == "stopped"
def test_traceng_nio_create_udp(http_compute, vm): async def test_traceng_nio_create_udp(compute_api, vm):
params = {
"type": "nio_udp",
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1"
}
with asyncio_patch("gns3server.compute.traceng.traceng_vm.TraceNGVM.add_ubridge_udp_connection"): with asyncio_patch("gns3server.compute.traceng.traceng_vm.TraceNGVM.add_ubridge_udp_connection"):
response = http_compute.post("/projects/{project_id}/traceng/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_udp", response = await compute_api.post("/projects/{project_id}/traceng/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1"},
example=True)
assert response.status == 201 assert response.status == 201
assert response.route == r"/projects/{project_id}/traceng/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.route == r"/projects/{project_id}/traceng/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
assert response.json["type"] == "nio_udp" assert response.json["type"] == "nio_udp"
def test_traceng_nio_update_udp(http_compute, vm): async def test_traceng_nio_update_udp(compute_api, vm):
params = {
"type": "nio_udp",
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1"
}
with asyncio_patch("gns3server.compute.traceng.traceng_vm.TraceNGVM.add_ubridge_udp_connection"): with asyncio_patch("gns3server.compute.traceng.traceng_vm.TraceNGVM.add_ubridge_udp_connection"):
response = http_compute.post("/projects/{project_id}/traceng/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_udp", response = await compute_api.post("/projects/{project_id}/traceng/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1"})
assert response.status == 201 assert response.status == 201
response = http_compute.put("/projects/{project_id}/traceng/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]),
{ params["filters"] = {}
"type": "nio_udp", response = await compute_api.put("/projects/{project_id}/traceng/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1",
"filters": {}},
example=True)
assert response.status == 201, response.body.decode("utf-8") assert response.status == 201, response.body.decode("utf-8")
assert response.route == r"/projects/{project_id}/traceng/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.route == r"/projects/{project_id}/traceng/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
assert response.json["type"] == "nio_udp" assert response.json["type"] == "nio_udp"
def test_traceng_delete_nio(http_compute, vm): async def test_traceng_delete_nio(compute_api, vm):
params = {
"type": "nio_udp",
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1"
}
with asyncio_patch("gns3server.compute.traceng.traceng_vm.TraceNGVM._ubridge_send"): with asyncio_patch("gns3server.compute.traceng.traceng_vm.TraceNGVM._ubridge_send"):
http_compute.post("/projects/{project_id}/traceng/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_udp", await compute_api.post("/projects/{project_id}/traceng/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
"lport": 4242, response = await compute_api.delete("/projects/{project_id}/traceng/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]))
"rport": 4343,
"rhost": "127.0.0.1"})
response = http_compute.delete("/projects/{project_id}/traceng/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True)
assert response.status == 204, response.body.decode() assert response.status == 204, response.body.decode()
assert response.route == r"/projects/{project_id}/traceng/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.route == r"/projects/{project_id}/traceng/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
def test_traceng_start(http_compute, vm): async def test_traceng_start(compute_api, vm):
params = {"destination": "192.168.1.2"}
with asyncio_patch("gns3server.compute.traceng.traceng_vm.TraceNGVM.start", return_value=True) as mock: with asyncio_patch("gns3server.compute.traceng.traceng_vm.TraceNGVM.start", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/traceng/nodes/{node_id}/start".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"destination": "192.168.1.2"}, example=True) response = await compute_api.post("/projects/{project_id}/traceng/nodes/{node_id}/start".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
assert mock.called assert mock.called
assert response.status == 200 assert response.status == 200
assert response.json["name"] == "TraceNG TEST 1" assert response.json["name"] == "TraceNG TEST 1"
def test_traceng_stop(http_compute, vm): async def test_traceng_stop(compute_api, vm):
with asyncio_patch("gns3server.compute.traceng.traceng_vm.TraceNGVM.stop", return_value=True) as mock: with asyncio_patch("gns3server.compute.traceng.traceng_vm.TraceNGVM.stop", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/traceng/nodes/{node_id}/stop".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.post("/projects/{project_id}/traceng/nodes/{node_id}/stop".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_traceng_reload(http_compute, vm): async def test_traceng_reload(compute_api, vm):
with asyncio_patch("gns3server.compute.traceng.traceng_vm.TraceNGVM.reload", return_value=True) as mock: with asyncio_patch("gns3server.compute.traceng.traceng_vm.TraceNGVM.reload", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/traceng/nodes/{node_id}/reload".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.post("/projects/{project_id}/traceng/nodes/{node_id}/reload".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_traceng_delete(http_compute, vm): async def test_traceng_delete(compute_api, vm):
with asyncio_patch("gns3server.compute.traceng.TraceNG.delete_node", return_value=True) as mock: with asyncio_patch("gns3server.compute.traceng.TraceNG.delete_node", return_value=True) as mock:
response = http_compute.delete("/projects/{project_id}/traceng/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.delete("/projects/{project_id}/traceng/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_traceng_duplicate(http_compute, vm): async def test_traceng_duplicate(compute_api, vm):
params = {"destination_node_id": str(uuid.uuid4())}
with asyncio_patch("gns3server.compute.traceng.TraceNG.duplicate_node", return_value=True) as mock: with asyncio_patch("gns3server.compute.traceng.TraceNG.duplicate_node", return_value=True) as mock:
response = http_compute.post( response = await compute_api.post("/projects/{project_id}/traceng/nodes/{node_id}/duplicate".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
"/projects/{project_id}/traceng/nodes/{node_id}/duplicate".format(
project_id=vm["project_id"],
node_id=vm["node_id"]),
body={
"destination_node_id": str(uuid.uuid4())
},
example=True)
assert mock.called assert mock.called
assert response.status == 201 assert response.status == 201
def test_traceng_update(http_compute, vm, tmpdir, free_console_port): async def test_traceng_update(compute_api, vm):
response = http_compute.put("/projects/{project_id}/traceng/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"name": "test",
"ip_address": "192.168.1.1", params = {
}, "name": "test",
example=True) "ip_address": "192.168.1.1"
}
response = await compute_api.put("/projects/{project_id}/traceng/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
assert response.status == 200 assert response.status == 200
assert response.json["name"] == "test" assert response.json["name"] == "test"
assert response.json["ip_address"] == "192.168.1.1" assert response.json["ip_address"] == "192.168.1.1"
def test_traceng_start_capture(http_compute, vm): async def test_traceng_start_capture(compute_api, vm):
params = {
"capture_file_name": "test.pcap",
"data_link_type": "DLT_EN10MB"
}
with patch("gns3server.compute.traceng.traceng_vm.TraceNGVM.is_running", return_value=True): with patch("gns3server.compute.traceng.traceng_vm.TraceNGVM.is_running", return_value=True):
with asyncio_patch("gns3server.compute.traceng.traceng_vm.TraceNGVM.start_capture") as start_capture: with asyncio_patch("gns3server.compute.traceng.traceng_vm.TraceNGVM.start_capture") as mock:
params = {"capture_file_name": "test.pcap", "data_link_type": "DLT_EN10MB"} response = await compute_api.post("/projects/{project_id}/traceng/nodes/{node_id}/adapters/0/ports/0/start_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
response = http_compute.post("/projects/{project_id}/traceng/nodes/{node_id}/adapters/0/ports/0/start_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]), body=params, example=True)
assert response.status == 200 assert response.status == 200
assert start_capture.called assert mock.called
assert "test.pcap" in response.json["pcap_file_path"] assert "test.pcap" in response.json["pcap_file_path"]
def test_traceng_stop_capture(http_compute, vm): async def test_traceng_stop_capture(compute_api, vm):
with patch("gns3server.compute.traceng.traceng_vm.TraceNGVM.is_running", return_value=True): with patch("gns3server.compute.traceng.traceng_vm.TraceNGVM.is_running", return_value=True):
with asyncio_patch("gns3server.compute.traceng.traceng_vm.TraceNGVM.stop_capture") as stop_capture: with asyncio_patch("gns3server.compute.traceng.traceng_vm.TraceNGVM.stop_capture") as mock:
response = http_compute.post("/projects/{project_id}/traceng/nodes/{node_id}/adapters/0/ports/0/stop_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.post("/projects/{project_id}/traceng/nodes/{node_id}/adapters/0/ports/0/stop_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert response.status == 204 assert response.status == 204
assert stop_capture.called assert mock.called
def test_traceng_pcap(http_compute, vm, project): async def test_traceng_pcap(compute_api, vm, compute_project):
with asyncio_patch("gns3server.compute.traceng.traceng_vm.TraceNGVM.get_nio"): with asyncio_patch("gns3server.compute.traceng.traceng_vm.TraceNGVM.get_nio"):
with asyncio_patch("gns3server.compute.traceng.TraceNG.stream_pcap_file"): with asyncio_patch("gns3server.compute.traceng.TraceNG.stream_pcap_file"):
response = http_compute.get("/projects/{project_id}/traceng/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=project.id, node_id=vm["node_id"]), raw=True) response = await compute_api.get("/projects/{project_id}/traceng/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True)
assert response.status == 200 assert response.status == 200

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -20,89 +20,100 @@ from tests.utils import asyncio_patch
from unittest.mock import patch from unittest.mock import patch
@pytest.yield_fixture(scope="function") @pytest.fixture(scope="function")
def vm(http_compute, project, monkeypatch): async def vm(compute_api, compute_project):
vboxmanage_path = "/fake/VboxManage" vboxmanage_path = "/fake/VboxManage"
params = {
"name": "VMTEST",
"vmname": "VMTEST",
"linked_clone": False
}
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.create", return_value=True) as mock: with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.create", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/virtualbox/nodes".format( response = await compute_api.post("/projects/{project_id}/virtualbox/nodes".format(project_id=compute_project.id), params)
project_id=project.id), {"name": "VMTEST",
"vmname": "VMTEST",
"linked_clone": False})
assert mock.called assert mock.called
assert response.status == 201 assert response.status == 201
with patch("gns3server.compute.virtualbox.VirtualBox.find_vboxmanage", return_value=vboxmanage_path): with patch("gns3server.compute.virtualbox.VirtualBox.find_vboxmanage", return_value=vboxmanage_path):
yield response.json return response.json
def test_vbox_create(http_compute, project): async def test_vbox_create(compute_api, compute_project):
params = {
"name": "VM1",
"vmname": "VM1",
"linked_clone": False
}
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.create", return_value=True): with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.create", return_value=True):
response = http_compute.post("/projects/{project_id}/virtualbox/nodes".format(project_id=project.id), {"name": "VM1", response = await compute_api.post("/projects/{project_id}/virtualbox/nodes".format(project_id=compute_project.id), params)
"vmname": "VM1",
"linked_clone": False},
example=True)
assert response.status == 201 assert response.status == 201
assert response.json["name"] == "VM1" assert response.json["name"] == "VM1"
assert response.json["project_id"] == project.id assert response.json["project_id"] == compute_project.id
async def test_vbox_get(compute_api, compute_project, vm):
def test_vbox_get(http_compute, project, vm): response = await compute_api.get("/projects/{project_id}/virtualbox/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]))
response = http_compute.get("/projects/{project_id}/virtualbox/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True)
assert response.status == 200 assert response.status == 200
assert response.route == "/projects/{project_id}/virtualbox/nodes/{node_id}" assert response.route == "/projects/{project_id}/virtualbox/nodes/{node_id}"
assert response.json["name"] == "VMTEST" assert response.json["name"] == "VMTEST"
assert response.json["project_id"] == project.id assert response.json["project_id"] == compute_project.id
def test_vbox_start(http_compute, vm): async def test_vbox_start(compute_api, vm):
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.check_hw_virtualization", return_value=True) as mock:
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.check_hw_virtualization", return_value=True):
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.start", return_value=True) as mock: with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.start", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/virtualbox/nodes/{node_id}/start".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.post("/projects/{project_id}/virtualbox/nodes/{node_id}/start".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_vbox_stop(http_compute, vm): async def test_vbox_stop(compute_api, vm):
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.stop", return_value=True) as mock: with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.stop", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/virtualbox/nodes/{node_id}/stop".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.post("/projects/{project_id}/virtualbox/nodes/{node_id}/stop".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_vbox_suspend(http_compute, vm): async def test_vbox_suspend(compute_api, vm):
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.suspend", return_value=True) as mock: with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.suspend", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/virtualbox/nodes/{node_id}/suspend".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.post("/projects/{project_id}/virtualbox/nodes/{node_id}/suspend".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_vbox_resume(http_compute, vm): async def test_vbox_resume(compute_api, vm):
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.resume", return_value=True) as mock: with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.resume", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/virtualbox/nodes/{node_id}/resume".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.post("/projects/{project_id}/virtualbox/nodes/{node_id}/resume".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_vbox_reload(http_compute, vm): async def test_vbox_reload(compute_api, vm):
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.reload", return_value=True) as mock: with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.reload", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/virtualbox/nodes/{node_id}/reload".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.post("/projects/{project_id}/virtualbox/nodes/{node_id}/reload".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_vbox_nio_create_udp(http_compute, vm): async def test_vbox_nio_create_udp(compute_api, vm):
with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_add_nio_binding') as mock: params = {
response = http_compute.post("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], "type": "nio_udp",
node_id=vm["node_id"]), {"type": "nio_udp", "lport": 4242,
"lport": 4242, "rport": 4343,
"rport": 4343, "rhost": "127.0.0.1"
"rhost": "127.0.0.1"}, }
example=True)
with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_add_nio_binding') as mock:
response = await compute_api.post("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
assert mock.called assert mock.called
args, kwgars = mock.call_args args, kwgars = mock.call_args
assert args[0] == 0 assert args[0] == 0
@ -112,30 +123,29 @@ def test_vbox_nio_create_udp(http_compute, vm):
assert response.json["type"] == "nio_udp" assert response.json["type"] == "nio_udp"
def test_virtualbox_nio_update_udp(http_compute, vm): async def test_virtualbox_nio_update_udp(compute_api, vm):
params = {
"type": "nio_udp",
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1",
"filters": {}
}
with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.ethernet_adapters'): with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.ethernet_adapters'):
with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_remove_nio_binding') as mock: with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_remove_nio_binding'):
response = http_compute.put("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/ports/0/nio".format( response = await compute_api.put("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
project_id=vm["project_id"],
node_id=vm["node_id"]),
{
"type": "nio_udp",
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1",
"filters": {}},
example=True)
assert response.status == 201, response.body.decode() assert response.status == 201, response.body.decode()
assert response.route == r"/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.route == r"/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
assert response.json["type"] == "nio_udp" assert response.json["type"] == "nio_udp"
def test_vbox_delete_nio(http_compute, vm): async def test_vbox_delete_nio(compute_api, vm):
with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_remove_nio_binding') as mock: with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_remove_nio_binding') as mock:
response = http_compute.delete("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.delete("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
args, kwgars = mock.call_args args, kwgars = mock.call_args
assert args[0] == 0 assert args[0] == 0
@ -144,38 +154,46 @@ def test_vbox_delete_nio(http_compute, vm):
assert response.route == r"/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.route == r"/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
def test_vbox_update(http_compute, vm, free_console_port): async def test_vbox_update(compute_api, vm, free_console_port):
response = http_compute.put("/projects/{project_id}/virtualbox/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"name": "test",
"console": free_console_port}, params = {
example=True) "name": "test",
"console": free_console_port
}
response = await compute_api.put("/projects/{project_id}/virtualbox/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
assert response.status == 200 assert response.status == 200
assert response.json["name"] == "test" assert response.json["name"] == "test"
assert response.json["console"] == free_console_port assert response.json["console"] == free_console_port
def test_virtualbox_start_capture(http_compute, vm): async def test_virtualbox_start_capture(compute_api, vm):
params = {
"capture_file_name": "test.pcap",
"data_link_type": "DLT_EN10MB"
}
with patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.is_running", return_value=True): with patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.is_running", return_value=True):
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.start_capture") as start_capture: with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.start_capture") as mock:
params = {"capture_file_name": "test.pcap", "data_link_type": "DLT_EN10MB"} response = await compute_api.post("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/ports/0/start_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
response = http_compute.post("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/ports/0/start_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]), body=params, example=True)
assert response.status == 200 assert response.status == 200
assert start_capture.called assert mock.called
assert "test.pcap" in response.json["pcap_file_path"] assert "test.pcap" in response.json["pcap_file_path"]
def test_virtualbox_stop_capture(http_compute, vm): async def test_virtualbox_stop_capture(compute_api, vm):
with patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.is_running", return_value=True): with patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.is_running", return_value=True):
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.stop_capture") as stop_capture: with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.stop_capture") as mock:
response = http_compute.post("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/ports/0/stop_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.post("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/ports/0/stop_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert response.status == 204 assert response.status == 204
assert stop_capture.called assert mock.called
def test_virtualbox_pcap(http_compute, vm, project): async def test_virtualbox_pcap(compute_api, vm, compute_project):
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.get_nio"): with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.get_nio"):
with asyncio_patch("gns3server.compute.virtualbox.VirtualBox.stream_pcap_file"): with asyncio_patch("gns3server.compute.virtualbox.VirtualBox.stream_pcap_file"):
response = http_compute.get("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=project.id, node_id=vm["node_id"]), raw=True) response = await compute_api.get("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True)
assert response.status == 200 assert response.status == 200

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -21,13 +21,16 @@ from unittest.mock import patch
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def vm(http_compute, project, vmx_path): async def vm(compute_api, compute_project, vmx_path):
params = {
"name": "VMTEST",
"vmx_path": vmx_path,
"linked_clone": False
}
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.create", return_value=True) as mock: with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.create", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/vmware/nodes".format(project_id=project.id), { response = await compute_api.post("/projects/{project_id}/vmware/nodes".format(project_id=compute_project.id), params)
"name": "VMTEST",
"vmx_path": vmx_path,
"linked_clone": False})
assert mock.called assert mock.called
assert response.status == 201, response.body.decode() assert response.status == 201, response.body.decode()
return response.json return response.json
@ -38,79 +41,90 @@ def vmx_path(tmpdir):
""" """
Return a fake VMX file Return a fake VMX file
""" """
path = str(tmpdir / "test.vmx") path = str(tmpdir / "test.vmx")
with open(path, 'w+') as f: with open(path, 'w+') as f:
f.write("1") f.write("1")
return path return path
def test_vmware_create(http_compute, project, vmx_path): async def test_vmware_create(compute_api, compute_project, vmx_path):
params = {
"name": "VM1",
"vmx_path": vmx_path,
"linked_clone": False
}
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.create", return_value=True): with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.create", return_value=True):
response = http_compute.post("/projects/{project_id}/vmware/nodes".format(project_id=project.id), { response = await compute_api.post("/projects/{project_id}/vmware/nodes".format(project_id=compute_project.id), params)
"name": "VM1",
"vmx_path": vmx_path,
"linked_clone": False},
example=True)
assert response.status == 201, response.body.decode() assert response.status == 201, response.body.decode()
assert response.json["name"] == "VM1" assert response.json["name"] == "VM1"
assert response.json["project_id"] == project.id assert response.json["project_id"] == compute_project.id
def test_vmware_get(http_compute, project, vm): async def test_vmware_get(compute_api, compute_project, vm):
response = http_compute.get("/projects/{project_id}/vmware/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True)
response = await compute_api.get("/projects/{project_id}/vmware/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert response.status == 200 assert response.status == 200
assert response.route == "/projects/{project_id}/vmware/nodes/{node_id}" assert response.route == "/projects/{project_id}/vmware/nodes/{node_id}"
assert response.json["name"] == "VMTEST" assert response.json["name"] == "VMTEST"
assert response.json["project_id"] == project.id assert response.json["project_id"] == compute_project.id
def test_vmware_start(http_compute, vm): async def test_vmware_start(compute_api, vm):
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.check_hw_virtualization", return_value=True) as mock:
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.start", return_value=True) as mock: with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.check_hw_virtualization", return_value=True) as mock1:
response = http_compute.post("/projects/{project_id}/vmware/nodes/{node_id}/start".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.start", return_value=True) as mock2:
assert mock.called response = await compute_api.post("/projects/{project_id}/vmware/nodes/{node_id}/start".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock1.called
assert mock2.called
assert response.status == 204 assert response.status == 204
def test_vmware_stop(http_compute, vm): async def test_vmware_stop(compute_api, vm):
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.stop", return_value=True) as mock: with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.stop", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/vmware/nodes/{node_id}/stop".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.post("/projects/{project_id}/vmware/nodes/{node_id}/stop".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_vmware_suspend(http_compute, vm): async def test_vmware_suspend(compute_api, vm):
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.suspend", return_value=True) as mock: with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.suspend", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/vmware/nodes/{node_id}/suspend".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.post("/projects/{project_id}/vmware/nodes/{node_id}/suspend".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_vmware_resume(http_compute, vm): async def test_vmware_resume(compute_api, vm):
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.resume", return_value=True) as mock: with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.resume", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/vmware/nodes/{node_id}/resume".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.post("/projects/{project_id}/vmware/nodes/{node_id}/resume".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_vmware_reload(http_compute, vm): async def test_vmware_reload(compute_api, vm):
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.reload", return_value=True) as mock: with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.reload", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/vmware/nodes/{node_id}/reload".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.post("/projects/{project_id}/vmware/nodes/{node_id}/reload".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_vmware_nio_create_udp(http_compute, vm): async def test_vmware_nio_create_udp(compute_api, vm):
with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.adapter_add_nio_binding') as mock: params = {
response = http_compute.post("/projects/{project_id}/vmware/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], "type": "nio_udp",
node_id=vm["node_id"]), {"type": "nio_udp", "lport": 4242,
"lport": 4242, "rport": 4343,
"rport": 4343, "rhost": "127.0.0.1"
"rhost": "127.0.0.1"}, }
example=True)
with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.adapter_add_nio_binding') as mock:
response = await compute_api.post("/projects/{project_id}/vmware/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
assert mock.called assert mock.called
args, kwgars = mock.call_args args, kwgars = mock.call_args
assert args[0] == 0 assert args[0] == 0
@ -120,30 +134,29 @@ def test_vmware_nio_create_udp(http_compute, vm):
assert response.json["type"] == "nio_udp" assert response.json["type"] == "nio_udp"
def test_vmware_nio_update_udp(http_compute, vm): async def test_vmware_nio_update_udp(compute_api, vm):
params = {
"type": "nio_udp",
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1",
"filters": {}
}
with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM._ubridge_send'): with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM._ubridge_send'):
with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.ethernet_adapters'): with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.ethernet_adapters'):
with patch('gns3server.compute.vmware.vmware_vm.VMwareVM._get_vnet') as mock: with patch('gns3server.compute.vmware.vmware_vm.VMwareVM._get_vnet') as mock:
response = http_compute.put("/projects/{project_id}/vmware/nodes/{node_id}/adapters/0/ports/0/nio".format( response = await compute_api.put("/projects/{project_id}/vmware/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
project_id=vm["project_id"], assert response.status == 201
node_id=vm["node_id"]),
{
"type": "nio_udp",
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1",
"filters": {}},
example=True)
assert response.status == 201, response.body.decode()
assert response.route == r"/projects/{project_id}/vmware/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.route == r"/projects/{project_id}/vmware/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
assert response.json["type"] == "nio_udp" assert response.json["type"] == "nio_udp"
def test_vmware_delete_nio(http_compute, vm): async def test_vmware_delete_nio(compute_api, vm):
with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.adapter_remove_nio_binding') as mock: with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.adapter_remove_nio_binding') as mock:
response = http_compute.delete("/projects/{project_id}/vmware/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.delete("/projects/{project_id}/vmware/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
args, kwgars = mock.call_args args, kwgars = mock.call_args
assert args[0] == 0 assert args[0] == 0
@ -152,37 +165,47 @@ def test_vmware_delete_nio(http_compute, vm):
assert response.route == r"/projects/{project_id}/vmware/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.route == r"/projects/{project_id}/vmware/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
def test_vmware_update(http_compute, vm, free_console_port): async def test_vmware_update(compute_api, vm, free_console_port):
response = http_compute.put("/projects/{project_id}/vmware/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"name": "test",
"console": free_console_port}, params = {
example=True) "name": "test",
"console": free_console_port
}
response = await compute_api.put("/projects/{project_id}/vmware/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
assert response.status == 200 assert response.status == 200
assert response.json["name"] == "test" assert response.json["name"] == "test"
assert response.json["console"] == free_console_port assert response.json["console"] == free_console_port
def test_vmware_start_capture(http_compute, vm):
async def test_vmware_start_capture(compute_api, vm):
params = {
"capture_file_name": "test.pcap",
"data_link_type": "DLT_EN10MB"
}
with patch("gns3server.compute.vmware.vmware_vm.VMwareVM.is_running", return_value=True): with patch("gns3server.compute.vmware.vmware_vm.VMwareVM.is_running", return_value=True):
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.start_capture") as start_capture: with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.start_capture") as mock:
params = {"capture_file_name": "test.pcap", "data_link_type": "DLT_EN10MB"}
response = http_compute.post("/projects/{project_id}/vmware/nodes/{node_id}/adapters/0/ports/0/start_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]), body=params, example=True) response = await compute_api.post("/projects/{project_id}/vmware/nodes/{node_id}/adapters/0/ports/0/start_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]), body=params)
assert response.status == 200 assert response.status == 200
assert start_capture.called assert mock.called
assert "test.pcap" in response.json["pcap_file_path"] assert "test.pcap" in response.json["pcap_file_path"]
def test_vmware_stop_capture(http_compute, vm): async def test_vmware_stop_capture(compute_api, vm):
with patch("gns3server.compute.vmware.vmware_vm.VMwareVM.is_running", return_value=True): with patch("gns3server.compute.vmware.vmware_vm.VMwareVM.is_running", return_value=True):
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.stop_capture") as stop_capture: with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.stop_capture") as mock:
response = http_compute.post("/projects/{project_id}/vmware/nodes/{node_id}/adapters/0/ports/0/stop_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.post("/projects/{project_id}/vmware/nodes/{node_id}/adapters/0/ports/0/stop_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert response.status == 204 assert response.status == 204
assert stop_capture.called assert mock.called
def test_vmware_pcap(http_compute, vm, project): async def test_vmware_pcap(compute_api, vm, compute_project):
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.get_nio"): with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.get_nio"):
with asyncio_patch("gns3server.compute.vmware.VMware.stream_pcap_file"): with asyncio_patch("gns3server.compute.vmware.VMware.stream_pcap_file"):
response = http_compute.get("/projects/{project_id}/vmware/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=project.id, node_id=vm["node_id"]), raw=True) response = await compute_api.get("/projects/{project_id}/vmware/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True)
assert response.status == 200 assert response.status == 200

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -21,168 +21,200 @@ from tests.utils import asyncio_patch
from unittest.mock import patch from unittest.mock import patch
@pytest.fixture(scope="function") @pytest.fixture
def vm(http_compute, project): async def vm(compute_api, compute_project):
response = http_compute.post("/projects/{project_id}/vpcs/nodes".format(project_id=project.id), {"name": "PC TEST 1"})
params = {"name": "PC TEST 1"}
response = await compute_api.post("/projects/{project_id}/vpcs/nodes".format(project_id=compute_project.id), params)
assert response.status == 201 assert response.status == 201
return response.json return response.json
def test_vpcs_create(http_compute, project): async def test_vpcs_create(compute_api, compute_project):
response = http_compute.post("/projects/{project_id}/vpcs/nodes".format(project_id=project.id), {"name": "PC TEST 1"}, example=True)
params = {"name": "PC TEST 1"}
response = await compute_api.post("/projects/{project_id}/vpcs/nodes".format(project_id=compute_project.id), params)
assert response.status == 201 assert response.status == 201
assert response.route == "/projects/{project_id}/vpcs/nodes" assert response.route == "/projects/{project_id}/vpcs/nodes"
assert response.json["name"] == "PC TEST 1" assert response.json["name"] == "PC TEST 1"
assert response.json["project_id"] == project.id assert response.json["project_id"] == compute_project.id
async def test_vpcs_get(compute_api, compute_project, vm):
def test_vpcs_get(http_compute, project, vm): response = await compute_api.get("/projects/{project_id}/vpcs/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]))
response = http_compute.get("/projects/{project_id}/vpcs/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True)
assert response.status == 200 assert response.status == 200
assert response.route == "/projects/{project_id}/vpcs/nodes/{node_id}" assert response.route == "/projects/{project_id}/vpcs/nodes/{node_id}"
assert response.json["name"] == "PC TEST 1" assert response.json["name"] == "PC TEST 1"
assert response.json["project_id"] == project.id assert response.json["project_id"] == compute_project.id
assert response.json["status"] == "stopped" assert response.json["status"] == "stopped"
def test_vpcs_create_startup_script(http_compute, project): async def test_vpcs_create_startup_script(compute_api, compute_project):
response = http_compute.post("/projects/{project_id}/vpcs/nodes".format(project_id=project.id), {"name": "PC TEST 1", "startup_script": "ip 192.168.1.2\necho TEST"})
params = {
"name": "PC TEST 1",
"startup_script": "ip 192.168.1.2\necho TEST"
}
response = await compute_api.post("/projects/{project_id}/vpcs/nodes".format(project_id=compute_project.id), params)
assert response.status == 201 assert response.status == 201
assert response.route == "/projects/{project_id}/vpcs/nodes" assert response.route == "/projects/{project_id}/vpcs/nodes"
assert response.json["name"] == "PC TEST 1" assert response.json["name"] == "PC TEST 1"
assert response.json["project_id"] == project.id assert response.json["project_id"] == compute_project.id
async def test_vpcs_create_port(compute_api, compute_project, free_console_port):
def test_vpcs_create_port(http_compute, project, free_console_port): params = {
response = http_compute.post("/projects/{project_id}/vpcs/nodes".format(project_id=project.id), {"name": "PC TEST 1", "console": free_console_port}) "name": "PC TEST 1",
"console": free_console_port
}
response = await compute_api.post("/projects/{project_id}/vpcs/nodes".format(project_id=compute_project.id), params)
assert response.status == 201 assert response.status == 201
assert response.route == "/projects/{project_id}/vpcs/nodes" assert response.route == "/projects/{project_id}/vpcs/nodes"
assert response.json["name"] == "PC TEST 1" assert response.json["name"] == "PC TEST 1"
assert response.json["project_id"] == project.id assert response.json["project_id"] == compute_project.id
assert response.json["console"] == free_console_port assert response.json["console"] == free_console_port
def test_vpcs_nio_create_udp(http_compute, vm): async def test_vpcs_nio_create_udp(compute_api, vm):
params = {
"type": "nio_udp",
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1"
}
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.add_ubridge_udp_connection"): with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.add_ubridge_udp_connection"):
response = http_compute.post("/projects/{project_id}/vpcs/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_udp", response = await compute_api.post("/projects/{project_id}/vpcs/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1"},
example=True)
assert response.status == 201 assert response.status == 201
assert response.route == r"/projects/{project_id}/vpcs/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.route == r"/projects/{project_id}/vpcs/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
assert response.json["type"] == "nio_udp" assert response.json["type"] == "nio_udp"
def test_vpcs_nio_update_udp(http_compute, vm): async def test_vpcs_nio_update_udp(compute_api, vm):
params = {
"type": "nio_udp",
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1"
}
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.add_ubridge_udp_connection"): with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.add_ubridge_udp_connection"):
response = http_compute.post("/projects/{project_id}/vpcs/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_udp", response = await compute_api.post("/projects/{project_id}/vpcs/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1"})
assert response.status == 201 assert response.status == 201
response = http_compute.put("/projects/{project_id}/vpcs/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]),
{ params["filters"] = {}
"type": "nio_udp", response = await compute_api.put("/projects/{project_id}/vpcs/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1",
"filters": {}},
example=True)
assert response.status == 201, response.body.decode("utf-8") assert response.status == 201, response.body.decode("utf-8")
assert response.route == r"/projects/{project_id}/vpcs/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.route == r"/projects/{project_id}/vpcs/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
assert response.json["type"] == "nio_udp" assert response.json["type"] == "nio_udp"
def test_vpcs_delete_nio(http_compute, vm): async def test_vpcs_delete_nio(compute_api, vm):
params = {
"type": "nio_udp",
"lport": 4242,
"rport": 4343,
"rhost": "127.0.0.1"
}
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM._ubridge_send"): with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM._ubridge_send"):
http_compute.post("/projects/{project_id}/vpcs/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_udp", await compute_api.post("/projects/{project_id}/vpcs/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
"lport": 4242, response = await compute_api.delete("/projects/{project_id}/vpcs/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]))
"rport": 4343,
"rhost": "127.0.0.1"})
response = http_compute.delete("/projects/{project_id}/vpcs/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True)
assert response.status == 204, response.body.decode() assert response.status == 204, response.body.decode()
assert response.route == r"/projects/{project_id}/vpcs/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.route == r"/projects/{project_id}/vpcs/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
def test_vpcs_start(http_compute, vm): async def test_vpcs_start(compute_api, vm):
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.start", return_value=True) as mock: with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.start", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/vpcs/nodes/{node_id}/start".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.post("/projects/{project_id}/vpcs/nodes/{node_id}/start".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 200 assert response.status == 200
assert response.json["name"] == "PC TEST 1" assert response.json["name"] == "PC TEST 1"
def test_vpcs_stop(http_compute, vm): async def test_vpcs_stop(compute_api, vm):
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.stop", return_value=True) as mock: with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.stop", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/vpcs/nodes/{node_id}/stop".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True)
response = await compute_api.post("/projects/{project_id}/vpcs/nodes/{node_id}/stop".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_vpcs_reload(http_compute, vm): async def test_vpcs_reload(compute_api, vm):
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.reload", return_value=True) as mock: with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.reload", return_value=True) as mock:
response = http_compute.post("/projects/{project_id}/vpcs/nodes/{node_id}/reload".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.post("/projects/{project_id}/vpcs/nodes/{node_id}/reload".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_vpcs_delete(http_compute, vm): async def test_vpcs_delete(compute_api, vm):
with asyncio_patch("gns3server.compute.vpcs.VPCS.delete_node", return_value=True) as mock: with asyncio_patch("gns3server.compute.vpcs.VPCS.delete_node", return_value=True) as mock:
response = http_compute.delete("/projects/{project_id}/vpcs/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.delete("/projects/{project_id}/vpcs/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_vpcs_duplicate(http_compute, vm): async def test_vpcs_duplicate(compute_api, vm):
params = {"destination_node_id": str(uuid.uuid4())}
with asyncio_patch("gns3server.compute.vpcs.VPCS.duplicate_node", return_value=True) as mock: with asyncio_patch("gns3server.compute.vpcs.VPCS.duplicate_node", return_value=True) as mock:
response = http_compute.post( response = await compute_api.post("/projects/{project_id}/vpcs/nodes/{node_id}/duplicate".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
"/projects/{project_id}/vpcs/nodes/{node_id}/duplicate".format(
project_id=vm["project_id"],
node_id=vm["node_id"]),
body={
"destination_node_id": str(uuid.uuid4())
},
example=True)
assert mock.called assert mock.called
assert response.status == 201 assert response.status == 201
def test_vpcs_update(http_compute, vm, tmpdir, free_console_port): async def test_vpcs_update(compute_api, vm, free_console_port):
response = http_compute.put("/projects/{project_id}/vpcs/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"name": "test",
"console": free_console_port, console_port = free_console_port
}, params = {
example=True) "name": "test",
"console": console_port
}
response = await compute_api.put("/projects/{project_id}/vpcs/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
assert response.status == 200 assert response.status == 200
assert response.json["name"] == "test" assert response.json["name"] == "test"
assert response.json["console"] == free_console_port assert response.json["console"] == console_port
async def test_vpcs_start_capture(compute_api, vm):
def test_vpcs_start_capture(http_compute, vm): params = {
"capture_file_name": "test.pcap",
"data_link_type": "DLT_EN10MB"
}
with patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.is_running", return_value=True): with patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.is_running", return_value=True):
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.start_capture") as start_capture: with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.start_capture") as mock:
params = {"capture_file_name": "test.pcap", "data_link_type": "DLT_EN10MB"} response = await compute_api.post("/projects/{project_id}/vpcs/nodes/{node_id}/adapters/0/ports/0/start_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]), body=params)
response = http_compute.post("/projects/{project_id}/vpcs/nodes/{node_id}/adapters/0/ports/0/start_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]), body=params, example=True)
assert response.status == 200 assert response.status == 200
assert start_capture.called assert mock.called
assert "test.pcap" in response.json["pcap_file_path"] assert "test.pcap" in response.json["pcap_file_path"]
def test_vpcs_stop_capture(http_compute, vm): async def test_vpcs_stop_capture(compute_api, vm):
with patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.is_running", return_value=True): with patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.is_running", return_value=True):
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.stop_capture") as stop_capture: with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.stop_capture") as mock:
response = http_compute.post("/projects/{project_id}/vpcs/nodes/{node_id}/adapters/0/ports/0/stop_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) response = await compute_api.post("/projects/{project_id}/vpcs/nodes/{node_id}/adapters/0/ports/0/stop_capture".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert response.status == 204 assert response.status == 204
assert stop_capture.called assert mock.called
def test_vpcs_pcap(http_compute, vm, project): async def test_vpcs_pcap(compute_api, vm, compute_project):
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.get_nio"): with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.get_nio"):
with asyncio_patch("gns3server.compute.vpcs.VPCS.stream_pcap_file"): with asyncio_patch("gns3server.compute.vpcs.VPCS.stream_pcap_file"):
response = http_compute.get("/projects/{project_id}/vpcs/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=project.id, node_id=vm["node_id"]), raw=True) response = await compute_api.get("/projects/{project_id}/vpcs/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True)
assert response.status == 200 assert response.status == 200

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2016 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -16,9 +16,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
def test_appliances_list(http_controller, controller, async_run): async def test_appliances_list(controller_api):
controller.appliance_manager.load_appliances() response = await controller_api.get("/appliances")
response = http_controller.get("/appliances", example=True)
assert response.status == 200 assert response.status == 200
assert len(response.json) > 0 assert len(response.json) > 0

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2016 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -19,27 +19,26 @@ import unittest
from tests.utils import asyncio_patch from tests.utils import asyncio_patch
def test_compute_create_without_id(http_controller, controller): async def test_compute_create_without_id(controller_api, controller):
params = { params = {
"protocol": "http", "protocol": "http",
"host": "localhost", "host": "localhost",
"port": 84, "port": 84,
"user": "julien", "user": "julien",
"password": "secure" "password": "secure"}
}
response = http_controller.post("/computes", params, example=True) response = await controller_api.post("/computes", params)
assert response.status == 201 assert response.status == 201
assert response.route == "/computes" assert response.route == "/computes"
assert response.json["user"] == "julien" assert response.json["user"] == "julien"
assert response.json["compute_id"] is not None assert response.json["compute_id"] is not None
assert "password" not in response.json assert "password" not in response.json
assert len(controller.computes) == 1 assert len(controller.computes) == 1
assert controller.computes[response.json["compute_id"]].host == "localhost" assert controller.computes[response.json["compute_id"]].host == "localhost"
def test_compute_create_with_id(http_controller, controller): async def test_compute_create_with_id(controller_api, controller):
params = { params = {
"compute_id": "my_compute_id", "compute_id": "my_compute_id",
@ -47,9 +46,9 @@ def test_compute_create_with_id(http_controller, controller):
"host": "localhost", "host": "localhost",
"port": 84, "port": 84,
"user": "julien", "user": "julien",
"password": "secure" "password": "secure"}
}
response = http_controller.post("/computes", params, example=True) response = await controller_api.post("/computes", params)
assert response.status == 201 assert response.status == 201
assert response.route == "/computes" assert response.route == "/computes"
assert response.json["user"] == "julien" assert response.json["user"] == "julien"
@ -59,7 +58,7 @@ def test_compute_create_with_id(http_controller, controller):
assert controller.computes["my_compute_id"].host == "localhost" assert controller.computes["my_compute_id"].host == "localhost"
def test_compute_get(http_controller, controller): async def test_compute_get(controller_api):
params = { params = {
"compute_id": "my_compute_id", "compute_id": "my_compute_id",
@ -69,15 +68,16 @@ def test_compute_get(http_controller, controller):
"user": "julien", "user": "julien",
"password": "secure" "password": "secure"
} }
response = http_controller.post("/computes", params)
response = await controller_api.post("/computes", params)
assert response.status == 201 assert response.status == 201
response = http_controller.get("/computes/my_compute_id", example=True) response = await controller_api.get("/computes/my_compute_id")
assert response.status == 200 assert response.status == 200
assert response.json["protocol"] == "http" assert response.json["protocol"] == "http"
def test_compute_update(http_controller, controller): async def test_compute_update(controller_api):
params = { params = {
"compute_id": "my_compute_id", "compute_id": "my_compute_id",
@ -87,21 +87,22 @@ def test_compute_update(http_controller, controller):
"user": "julien", "user": "julien",
"password": "secure" "password": "secure"
} }
response = http_controller.post("/computes", params)
response = await controller_api.post("/computes", params)
assert response.status == 201 assert response.status == 201
response = http_controller.get("/computes/my_compute_id") response = await controller_api.get("/computes/my_compute_id")
assert response.status == 200 assert response.status == 200
assert response.json["protocol"] == "http" assert response.json["protocol"] == "http"
params["protocol"] = "https" params["protocol"] = "https"
response = http_controller.put("/computes/my_compute_id", params, example=True) response = await controller_api.put("/computes/my_compute_id", params)
assert response.status == 200 assert response.status == 200
assert response.json["protocol"] == "https" assert response.json["protocol"] == "https"
def test_compute_list(http_controller, controller): async def test_compute_list(controller_api):
params = { params = {
"compute_id": "my_compute_id", "compute_id": "my_compute_id",
@ -112,13 +113,14 @@ def test_compute_list(http_controller, controller):
"password": "secure", "password": "secure",
"name": "My super server" "name": "My super server"
} }
response = http_controller.post("/computes", params)
response = await controller_api.post("/computes", params)
assert response.status == 201 assert response.status == 201
assert response.route == "/computes" assert response.route == "/computes"
assert response.json["user"] == "julien" assert response.json["user"] == "julien"
assert "password" not in response.json assert "password" not in response.json
response = http_controller.get("/computes", example=True) response = await controller_api.get("/computes")
for compute in response.json: for compute in response.json:
if compute['compute_id'] != 'local': if compute['compute_id'] != 'local':
assert compute == { assert compute == {
@ -139,7 +141,7 @@ def test_compute_list(http_controller, controller):
} }
def test_compute_delete(http_controller, controller): async def test_compute_delete(controller_api):
params = { params = {
"compute_id": "my_compute_id", "compute_id": "my_compute_id",
@ -149,20 +151,20 @@ def test_compute_delete(http_controller, controller):
"user": "julien", "user": "julien",
"password": "secure" "password": "secure"
} }
response = http_controller.post("/computes", params) response = await controller_api.post("/computes", params)
assert response.status == 201 assert response.status == 201
response = http_controller.get("/computes") response = await controller_api.get("/computes")
assert len(response.json) == 1 assert len(response.json) == 1
response = http_controller.delete("/computes/my_compute_id", example=True) response = await controller_api.delete("/computes/my_compute_id")
assert response.status == 204 assert response.status == 204
response = http_controller.get("/computes") response = await controller_api.get("/computes")
assert len(response.json) == 0 assert len(response.json) == 0
def test_compute_list_images(http_controller, controller): async def test_compute_list_images(controller_api):
params = { params = {
"compute_id": "my_compute", "compute_id": "my_compute",
@ -172,16 +174,16 @@ def test_compute_list_images(http_controller, controller):
"user": "julien", "user": "julien",
"password": "secure" "password": "secure"
} }
response = http_controller.post("/computes", params) response = await controller_api.post("/computes", params)
assert response.status == 201 assert response.status == 201
with asyncio_patch("gns3server.controller.compute.Compute.images", return_value=[{"filename": "linux.qcow2"}, {"filename": "asav.qcow2"}]) as mock: with asyncio_patch("gns3server.controller.compute.Compute.images", return_value=[{"filename": "linux.qcow2"}, {"filename": "asav.qcow2"}]) as mock:
response = http_controller.get("/computes/my_compute/qemu/images", example=True) response = await controller_api.get("/computes/my_compute/qemu/images")
assert response.json == [{"filename": "linux.qcow2"}, {"filename": "asav.qcow2"}] assert response.json == [{"filename": "linux.qcow2"}, {"filename": "asav.qcow2"}]
mock.assert_called_with("qemu") mock.assert_called_with("qemu")
def test_compute_list_vms(http_controller, controller): async def test_compute_list_vms(controller_api):
params = { params = {
"compute_id": "my_compute", "compute_id": "my_compute",
@ -191,16 +193,16 @@ def test_compute_list_vms(http_controller, controller):
"user": "julien", "user": "julien",
"password": "secure" "password": "secure"
} }
response = http_controller.post("/computes", params) response = await controller_api.post("/computes", params)
assert response.status == 201 assert response.status == 201
with asyncio_patch("gns3server.controller.compute.Compute.forward", return_value=[]) as mock: with asyncio_patch("gns3server.controller.compute.Compute.forward", return_value=[]) as mock:
response = http_controller.get("/computes/my_compute/virtualbox/vms", example=True) response = await controller_api.get("/computes/my_compute/virtualbox/vms")
assert response.json == [] assert response.json == []
mock.assert_called_with("GET", "virtualbox", "vms") mock.assert_called_with("GET", "virtualbox", "vms")
def test_compute_create_img(http_controller, controller): async def test_compute_create_img(controller_api):
params = { params = {
"compute_id": "my_compute", "compute_id": "my_compute",
@ -210,16 +212,18 @@ def test_compute_create_img(http_controller, controller):
"user": "julien", "user": "julien",
"password": "secure" "password": "secure"
} }
response = http_controller.post("/computes", params)
response = await controller_api.post("/computes", params)
assert response.status == 201 assert response.status == 201
params = {"path": "/test"} params = {"path": "/test"}
with asyncio_patch("gns3server.controller.compute.Compute.forward", return_value=[]) as mock: with asyncio_patch("gns3server.controller.compute.Compute.forward", return_value=[]) as mock:
response = http_controller.post("/computes/my_compute/qemu/img", params, example=True) response = await controller_api.post("/computes/my_compute/qemu/img", params)
assert response.json == []
mock.assert_called_with("POST", "qemu", "img", data=unittest.mock.ANY) mock.assert_called_with("POST", "qemu", "img", data=unittest.mock.ANY)
def test_compute_autoidlepc(http_controller, controller): async def test_compute_autoidlepc(controller_api):
params = { params = {
"compute_id": "my_compute_id", "compute_id": "my_compute_id",
@ -229,20 +233,23 @@ def test_compute_autoidlepc(http_controller, controller):
"user": "julien", "user": "julien",
"password": "secure" "password": "secure"
} }
response = http_controller.post("/computes", params, example=False)
await controller_api.post("/computes", params)
params = { params = {
"platform": "c7200", "platform": "c7200",
"image": "test.bin", "image": "test.bin",
"ram": 512 "ram": 512
} }
with asyncio_patch("gns3server.controller.Controller.autoidlepc", return_value={"idlepc": "0x606de20c"}) as mock: with asyncio_patch("gns3server.controller.Controller.autoidlepc", return_value={"idlepc": "0x606de20c"}) as mock:
response = http_controller.post("/computes/my_compute_id/auto_idlepc", params, example=True) response = await controller_api.post("/computes/my_compute_id/auto_idlepc", params)
assert mock.called assert mock.called
assert response.status == 200 assert response.status == 200
def test_compute_endpoint(http_controller, controller): async def test_compute_endpoint(controller_api):
params = { params = {
"compute_id": "my_compute", "compute_id": "my_compute",
"protocol": "http", "protocol": "http",
@ -251,10 +258,10 @@ def test_compute_endpoint(http_controller, controller):
"user": "julien", "user": "julien",
"password": "secure" "password": "secure"
} }
response = http_controller.post("/computes", params)
response = await controller_api.post("/computes", params)
assert response.status == 201 assert response.status == 201
response = http_controller.get("/computes/endpoint/my_compute/virtualbox/images") response = await controller_api.get("/computes/endpoint/my_compute/virtualbox/images")
assert response.status == 200 assert response.status == 200
assert response.json['endpoint'] == 'http://localhost:84/v2/compute/virtualbox/images' assert response.json['endpoint'] == 'http://localhost:84/v2/compute/virtualbox/images'

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -15,85 +15,73 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
This test suite check /project endpoint
"""
import uuid
import os
import asyncio
import aiohttp
import pytest
from tests.utils import asyncio_patch
from gns3server.handlers.api.controller.project_handler import ProjectHandler
from gns3server.controller import Controller
from gns3server.controller.drawing import Drawing from gns3server.controller.drawing import Drawing
@pytest.fixture async def test_create_drawing(controller_api, project):
def project(http_controller, async_run):
return async_run(Controller.instance().add_project(name="Test"))
def test_create_drawing(http_controller, tmpdir, project, async_run):
response = http_controller.post("/projects/{}/drawings".format(project.id), { params = {
"svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>', "svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>',
"x": 10, "x": 10,
"y": 20, "y": 20,
"z": 0 "z": 0
}, example=True) }
response = await controller_api.post("/projects/{}/drawings".format(project.id), params)
assert response.status == 201 assert response.status == 201
assert response.json["drawing_id"] is not None assert response.json["drawing_id"] is not None
def test_get_drawing(http_controller, tmpdir, project, async_run): async def test_get_drawing(controller_api, project):
response = http_controller.post("/projects/{}/drawings".format(project.id), { params = {
"svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>', "svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>',
"x": 10, "x": 10,
"y": 20, "y": 20,
"z": 0 "z": 0
},) }
response = http_controller.get("/projects/{}/drawings/{}".format(project.id, response.json["drawing_id"]), example=True)
response = await controller_api.post("/projects/{}/drawings".format(project.id), params)
response = await controller_api.get("/projects/{}/drawings/{}".format(project.id, response.json["drawing_id"]))
assert response.status == 200 assert response.status == 200
assert response.json["x"] == 10 assert response.json["x"] == 10
def test_update_drawing(http_controller, tmpdir, project, async_run): async def test_update_drawing(controller_api, project):
response = http_controller.post("/projects/{}/drawings".format(project.id), { params = {
"svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>', "svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>',
"x": 10, "x": 10,
"y": 20, "y": 20,
"z": 0 "z": 0
},) }
response = http_controller.put("/projects/{}/drawings/{}".format(project.id, response.json["drawing_id"]), {
"x": 42, response = await controller_api.post("/projects/{}/drawings".format(project.id), params)
}, example=True) response = await controller_api.put("/projects/{}/drawings/{}".format(project.id, response.json["drawing_id"]), {"x": 42})
assert response.status == 201 assert response.status == 201
assert response.json["x"] == 42 assert response.json["x"] == 42
def test_list_drawing(http_controller, tmpdir, project, async_run): async def test_list_drawing(controller_api, project):
response = http_controller.post("/projects/{}/drawings".format(project.id), {
params = {
"svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>', "svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>',
"x": 10, "x": 10,
"y": 20, "y": 20,
"z": 0 "z": 0
}, example=False) }
response = http_controller.get("/projects/{}/drawings".format(project.id), example=True)
await controller_api.post("/projects/{}/drawings".format(project.id), params)
response = await controller_api.get("/projects/{}/drawings".format(project.id))
assert response.status == 200 assert response.status == 200
assert len(response.json) == 1 assert len(response.json) == 1
def test_delete_drawing(http_controller, tmpdir, project, async_run): async def test_delete_drawing(controller_api, project):
drawing = Drawing(project) drawing = Drawing(project)
project._drawings = {drawing.id: drawing} project._drawings = {drawing.id: drawing}
response = http_controller.delete("/projects/{}/drawings/{}".format(project.id, drawing.id), example=True) response = await controller_api.delete("/projects/{}/drawings/{}".format(project.id, drawing.id))
assert response.status == 204 assert response.status == 204
assert drawing.id not in project._drawings assert drawing.id not in project.drawings

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2016 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -18,9 +18,10 @@
from tests.utils import asyncio_patch from tests.utils import asyncio_patch
def test_list_vms(http_controller): async def test_list_vms(controller_api):
with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.list", return_value=[{"vmname": "test"}]): with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.list", return_value=[{"vmname": "test"}]):
response = http_controller.get('/gns3vm/engines/vmware/vms', example=True) response = await controller_api.get('/gns3vm/engines/vmware/vms')
assert response.status == 200 assert response.status == 200
assert response.json == [ assert response.json == [
{ {
@ -29,20 +30,20 @@ def test_list_vms(http_controller):
] ]
def test_engines(http_controller): async def test_engines(controller_api):
response = http_controller.get('/gns3vm/engines', example=True)
response = await controller_api.get('/gns3vm/engines')
assert response.status == 200 assert response.status == 200
assert len(response.json) > 0 assert len(response.json) > 0
def test_put_gns3vm(http_controller): async def test_put_gns3vm(controller_api):
response = http_controller.put('/gns3vm', {
"vmname": "TEST VM" response = await controller_api.put('/gns3vm', {"vmname": "TEST VM"})
}, example=True)
assert response.status == 201 assert response.status == 201
assert response.json["vmname"] == "TEST VM" assert response.json["vmname"] == "TEST VM"
def test_get_gns3vm(http_controller): async def test_get_gns3vm(controller_api):
response = http_controller.get('/gns3vm', example=True) response = await controller_api.get('/gns3vm')
assert response.status == 200 assert response.status == 200

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -15,45 +15,32 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
This test suite check /project endpoint
"""
import asyncio
import aiohttp
import pytest import pytest
from unittest.mock import patch, MagicMock from unittest.mock import patch, MagicMock
from tests.utils import asyncio_patch, AsyncioMagicMock from tests.utils import asyncio_patch, AsyncioMagicMock
from gns3server.controller import Controller
from gns3server.controller.ports.ethernet_port import EthernetPort from gns3server.controller.ports.ethernet_port import EthernetPort
from gns3server.controller.link import Link, FILTERS from gns3server.controller.link import Link, FILTERS
@pytest.fixture @pytest.fixture
def compute(http_controller, async_run): async def nodes(compute, project):
compute = MagicMock()
compute.id = "example.com"
Controller.instance()._computes = {"example.com": compute}
return compute
@pytest.fixture
def project(http_controller, async_run):
return async_run(Controller.instance().add_project(name="Test"))
def test_create_link(http_controller, tmpdir, project, compute, async_run):
response = MagicMock() response = MagicMock()
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
node1 = async_run(project.add_node(compute, "node1", None, node_type="qemu")) node1 = await project.add_node(compute, "node1", None, node_type="qemu")
node1._ports = [EthernetPort("E0", 0, 0, 3)] node1._ports = [EthernetPort("E0", 0, 0, 3)]
node2 = async_run(project.add_node(compute, "node2", None, node_type="qemu")) node2 = await project.add_node(compute, "node2", None, node_type="qemu")
node2._ports = [EthernetPort("E0", 0, 2, 4)] node2._ports = [EthernetPort("E0", 0, 2, 4)]
return node1, node2
async def test_create_link(controller_api, project, nodes):
node1, node2 = nodes
filters = { filters = {
"latency": [10], "latency": [10],
@ -61,7 +48,7 @@ def test_create_link(http_controller, tmpdir, project, compute, async_run):
} }
with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock: with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock:
response = http_controller.post("/projects/{}/links".format(project.id), { response = await controller_api.post("/projects/{}/links".format(project.id), {
"nodes": [ "nodes": [
{ {
"node_id": node1.id, "node_id": node1.id,
@ -80,7 +67,8 @@ def test_create_link(http_controller, tmpdir, project, compute, async_run):
} }
], ],
"filters": filters "filters": filters
}, example=True) })
assert mock.called assert mock.called
assert response.status == 201 assert response.status == 201
assert response.json["link_id"] is not None assert response.json["link_id"] is not None
@ -90,56 +78,49 @@ def test_create_link(http_controller, tmpdir, project, compute, async_run):
assert list(project.links.values())[0].filters == filters assert list(project.links.values())[0].filters == filters
def test_create_link_failure(http_controller, tmpdir, project, compute, async_run): async def test_create_link_failure(controller_api, compute, project):
""" """
Make sure the link is deleted if we failed to create the link. Make sure the link is deleted if we failed to create it.
The failure is trigger by connecting the link to himself The failure is triggered by connecting the link to itself
""" """
response = MagicMock() response = MagicMock()
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
node1 = async_run(project.add_node(compute, "node1", None, node_type="qemu")) node1 = await project.add_node(compute, "node1", None, node_type="qemu")
node1._ports = [EthernetPort("E0", 0, 0, 3), EthernetPort("E0", 0, 0, 4)] node1._ports = [EthernetPort("E0", 0, 0, 3), EthernetPort("E0", 0, 0, 4)]
with asyncio_patch("gns3server.controller.udp_link.UDPLink.create"): response = await controller_api.post("/projects/{}/links".format(project.id), {
response = http_controller.post("/projects/{}/links".format(project.id), { "nodes": [
"nodes": [ {
{ "node_id": node1.id,
"node_id": node1.id, "adapter_number": 0,
"adapter_number": 0, "port_number": 3,
"port_number": 3, "label": {
"label": { "text": "Text",
"text": "Text", "x": 42,
"x": 42, "y": 0
"y": 0
}
},
{
"node_id": node1.id,
"adapter_number": 0,
"port_number": 4
} }
] },
}, example=True) {
#assert mock.called "node_id": node1.id,
"adapter_number": 0,
"port_number": 4
}
]
})
assert response.status == 409 assert response.status == 409
assert len(project.links) == 0 assert len(project.links) == 0
def test_get_link(http_controller, tmpdir, project, compute, async_run): async def test_get_link(controller_api, project, nodes):
response = MagicMock()
response.json = {"console": 2048}
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"): node1, node2 = nodes
response = http_controller.post("/projects/{}/links".format(project.id), { with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock:
response = await controller_api.post("/projects/{}/links".format(project.id), {
"nodes": [ "nodes": [
{ {
"node_id": node1.id, "node_id": node1.id,
@ -158,25 +139,20 @@ def test_get_link(http_controller, tmpdir, project, compute, async_run):
} }
] ]
}) })
assert mock.called
link_id = response.json["link_id"] link_id = response.json["link_id"]
assert response.json["nodes"][0]["label"]["x"] == 42 assert response.json["nodes"][0]["label"]["x"] == 42
response = http_controller.get("/projects/{}/links/{}".format(project.id, link_id), example=True) response = await controller_api.get("/projects/{}/links/{}".format(project.id, link_id))
assert response.status == 200 assert response.status == 200
assert response.json["nodes"][0]["label"]["x"] == 42 assert response.json["nodes"][0]["label"]["x"] == 42
def test_update_link_suspend(http_controller, tmpdir, project, compute, async_run): async def test_update_link_suspend(controller_api, project, nodes):
response = MagicMock()
response.json = {"console": 2048}
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"): node1, node2 = nodes
response = http_controller.post("/projects/{}/links".format(project.id), { with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock:
response = await controller_api.post("/projects/{}/links".format(project.id), {
"nodes": [ "nodes": [
{ {
"node_id": node1.id, "node_id": node1.id,
@ -195,9 +171,12 @@ def test_update_link_suspend(http_controller, tmpdir, project, compute, async_ru
} }
] ]
}) })
assert mock.called
link_id = response.json["link_id"] link_id = response.json["link_id"]
assert response.json["nodes"][0]["label"]["x"] == 42 assert response.json["nodes"][0]["label"]["x"] == 42
response = http_controller.put("/projects/{}/links/{}".format(project.id, link_id), {
response = await controller_api.put("/projects/{}/links/{}".format(project.id, link_id), {
"nodes": [ "nodes": [
{ {
"node_id": node1.id, "node_id": node1.id,
@ -217,29 +196,23 @@ def test_update_link_suspend(http_controller, tmpdir, project, compute, async_ru
], ],
"suspend": True "suspend": True
}) })
assert response.status == 201 assert response.status == 201
assert response.json["nodes"][0]["label"]["x"] == 64 assert response.json["nodes"][0]["label"]["x"] == 64
assert response.json["suspend"] assert response.json["suspend"]
assert response.json["filters"] == {} assert response.json["filters"] == {}
def test_update_link(http_controller, tmpdir, project, compute, async_run): async def test_update_link(controller_api, project, nodes):
response = MagicMock()
response.json = {"console": 2048}
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)]
filters = { filters = {
"latency": [10], "latency": [10],
"frequency_drop": [50] "frequency_drop": [50]
} }
with asyncio_patch("gns3server.controller.udp_link.UDPLink.create"): node1, node2 = nodes
response = http_controller.post("/projects/{}/links".format(project.id), { with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock:
response = await controller_api.post("/projects/{}/links".format(project.id), {
"nodes": [ "nodes": [
{ {
"node_id": node1.id, "node_id": node1.id,
@ -258,9 +231,12 @@ def test_update_link(http_controller, tmpdir, project, compute, async_run):
} }
] ]
}) })
assert mock.called
link_id = response.json["link_id"] link_id = response.json["link_id"]
assert response.json["nodes"][0]["label"]["x"] == 42 assert response.json["nodes"][0]["label"]["x"] == 42
response = http_controller.put("/projects/{}/links/{}".format(project.id, link_id), {
response = await controller_api.put("/projects/{}/links/{}".format(project.id, link_id), {
"nodes": [ "nodes": [
{ {
"node_id": node1.id, "node_id": node1.id,
@ -279,26 +255,21 @@ def test_update_link(http_controller, tmpdir, project, compute, async_run):
} }
], ],
"filters": filters "filters": filters
}, example=True) })
assert response.status == 201 assert response.status == 201
assert response.json["nodes"][0]["label"]["x"] == 64 assert response.json["nodes"][0]["label"]["x"] == 64
assert list(project.links.values())[0].filters == filters assert list(project.links.values())[0].filters == filters
def test_list_link(http_controller, tmpdir, project, compute, async_run): async def test_list_link(controller_api, project, nodes):
response = MagicMock()
response.json = {"console": 2048}
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)]
filters = { filters = {
"latency": [10], "latency": [10],
"frequency_drop": [50] "frequency_drop": [50]
} }
node1, node2 = nodes
nodes = [ nodes = [
{ {
"node_id": node1.id, "node_id": node1.id,
@ -311,40 +282,45 @@ def test_list_link(http_controller, tmpdir, project, compute, async_run):
"port_number": 4 "port_number": 4
} }
] ]
with asyncio_patch("gns3server.controller.udp_link.UDPLink.create"): with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock:
response = http_controller.post("/projects/{}/links".format(project.id), { await controller_api.post("/projects/{}/links".format(project.id), {
"nodes": nodes, "nodes": nodes,
"filters": filters "filters": filters
}) })
response = http_controller.get("/projects/{}/links".format(project.id), example=True)
assert mock.called
response = await controller_api.get("/projects/{}/links".format(project.id))
assert response.status == 200 assert response.status == 200
assert len(response.json) == 1 assert len(response.json) == 1
assert response.json[0]["filters"] == filters assert response.json[0]["filters"] == filters
def test_start_capture(http_controller, tmpdir, project, compute, async_run): async def test_start_capture(controller_api, project):
link = Link(project) link = Link(project)
project._links = {link.id: link} project._links = {link.id: link}
with asyncio_patch("gns3server.controller.link.Link.start_capture") as mock: with asyncio_patch("gns3server.controller.link.Link.start_capture") as mock:
response = http_controller.post("/projects/{}/links/{}/start_capture".format(project.id, link.id), example=True) response = await controller_api.post("/projects/{}/links/{}/start_capture".format(project.id, link.id))
assert mock.called assert mock.called
assert response.status == 201 assert response.status == 201
def test_stop_capture(http_controller, tmpdir, project, compute, async_run): async def test_stop_capture(controller_api, project):
link = Link(project) link = Link(project)
project._links = {link.id: link} project._links = {link.id: link}
with asyncio_patch("gns3server.controller.link.Link.stop_capture") as mock: with asyncio_patch("gns3server.controller.link.Link.stop_capture") as mock:
response = http_controller.post("/projects/{}/links/{}/stop_capture".format(project.id, link.id), example=True) response = await controller_api.post("/projects/{}/links/{}/stop_capture".format(project.id, link.id))
assert mock.called assert mock.called
assert response.status == 201 assert response.status == 201
# def test_pcap(http_controller, tmpdir, project, compute, async_run): # async def test_pcap(controller_api, http_client, project):
# #
# async def go(): # async def pcap_capture():
# async with aiohttp.request("GET", http_controller.get_url("/projects/{}/links/{}/pcap".format(project.id, link.id))) as response: # async with http_client.get(controller_api.get_url("/projects/{}/links/{}/pcap".format(project.id, link.id))) as response:
# response.body = await response.content.read(5) # response.body = await response.content.read(5)
# print("READ", response.body)
# return response # return response
# #
# with asyncio_patch("gns3server.controller.link.Link.capture_node") as mock: # with asyncio_patch("gns3server.controller.link.Link.capture_node") as mock:
@ -354,27 +330,28 @@ def test_stop_capture(http_controller, tmpdir, project, compute, async_run):
# with open(link.capture_file_path, "w+") as f: # with open(link.capture_file_path, "w+") as f:
# f.write("hello") # f.write("hello")
# project._links = {link.id: link} # project._links = {link.id: link}
# response = async_run(asyncio.ensure_future(go())) # response = await pcap_capture()
# assert mock.called
# assert response.status == 200 # assert response.status == 200
# assert b'hello' == response.body # assert b'hello' == response.body
def test_delete_link(http_controller, tmpdir, project, compute, async_run): async def test_delete_link(controller_api, project):
link = Link(project) link = Link(project)
project._links = {link.id: link} project._links = {link.id: link}
with asyncio_patch("gns3server.controller.link.Link.delete") as mock: with asyncio_patch("gns3server.controller.link.Link.delete") as mock:
response = http_controller.delete("/projects/{}/links/{}".format(project.id, link.id), example=True) response = await controller_api.delete("/projects/{}/links/{}".format(project.id, link.id))
assert mock.called assert mock.called
assert response.status == 204 assert response.status == 204
def test_list_filters(http_controller, tmpdir, project, async_run): async def test_list_filters(controller_api, project):
link = Link(project) link = Link(project)
project._links = {link.id: link} project._links = {link.id: link}
with patch("gns3server.controller.link.Link.available_filters", return_value=FILTERS) as mock: with patch("gns3server.controller.link.Link.available_filters", return_value=FILTERS) as mock:
response = http_controller.get("/projects/{}/links/{}/available_filters".format(project.id, link.id), example=True) response = await controller_api.get("/projects/{}/links/{}/available_filters".format(project.id, link.id))
assert mock.called assert mock.called
assert response.status == 200 assert response.status == 200
assert response.json == FILTERS assert response.json == FILTERS

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -15,70 +15,50 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
This test suite check /project endpoint
"""
import uuid
import os
import asyncio
import aiohttp
import pytest import pytest
from unittest.mock import MagicMock
from tests.utils import AsyncioMagicMock
from unittest.mock import patch, MagicMock
from tests.utils import asyncio_patch, AsyncioMagicMock
from gns3server.handlers.api.controller.project_handler import ProjectHandler
from gns3server.controller import Controller
from gns3server.controller.node import Node from gns3server.controller.node import Node
@pytest.fixture @pytest.fixture
def compute(http_controller, async_run): def node(project, compute):
compute = MagicMock()
compute.id = "example.com"
compute.host = "example.org"
Controller.instance()._computes = {"example.com": compute}
return compute
@pytest.fixture
def project(http_controller, async_run):
return async_run(Controller.instance().add_project(name="Test"))
@pytest.fixture
def node(project, compute, async_run):
node = Node(project, compute, "test", node_type="vpcs") node = Node(project, compute, "test", node_type="vpcs")
project._nodes[node.id] = node project._nodes[node.id] = node
return node return node
def test_create_node(http_controller, tmpdir, project, compute): async def test_create_node(controller_api, project, compute):
response = MagicMock() response = MagicMock()
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
response = http_controller.post("/projects/{}/nodes".format(project.id), { response = await controller_api.post("/projects/{}/nodes".format(project.id), {
"name": "test", "name": "test",
"node_type": "vpcs", "node_type": "vpcs",
"compute_id": "example.com", "compute_id": "example.com",
"properties": { "properties": {
"startup_script": "echo test" "startup_script": "echo test"
} }
}, example=True) })
assert response.status == 201 assert response.status == 201
assert response.json["name"] == "test" assert response.json["name"] == "test"
assert "name" not in response.json["properties"] assert "name" not in response.json["properties"]
def test_list_node(http_controller, tmpdir, project, compute): async def test_list_node(controller_api, project, compute):
response = MagicMock() response = MagicMock()
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
response = http_controller.post("/projects/{}/nodes".format(project.id), { await controller_api.post("/projects/{}/nodes".format(project.id), {
"name": "test", "name": "test",
"node_type": "vpcs", "node_type": "vpcs",
"compute_id": "example.com", "compute_id": "example.com",
@ -86,17 +66,19 @@ def test_list_node(http_controller, tmpdir, project, compute):
"startup_script": "echo test" "startup_script": "echo test"
} }
}) })
response = http_controller.get("/projects/{}/nodes".format(project.id), example=True)
response = await controller_api.get("/projects/{}/nodes".format(project.id))
assert response.status == 200 assert response.status == 200
assert response.json[0]["name"] == "test" assert response.json[0]["name"] == "test"
def test_get_node(http_controller, tmpdir, project, compute): async def test_get_node(controller_api, project, compute):
response = MagicMock() response = MagicMock()
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
response = http_controller.post("/projects/{}/nodes".format(project.id), { response = await controller_api.post("/projects/{}/nodes".format(project.id), {
"name": "test", "name": "test",
"node_type": "vpcs", "node_type": "vpcs",
"compute_id": "example.com", "compute_id": "example.com",
@ -104,174 +86,175 @@ def test_get_node(http_controller, tmpdir, project, compute):
"startup_script": "echo test" "startup_script": "echo test"
} }
}) })
response = http_controller.get("/projects/{}/nodes/{}".format(project.id, response.json["node_id"]), example=True)
response = await controller_api.get("/projects/{}/nodes/{}".format(project.id, response.json["node_id"]))
assert response.status == 200 assert response.status == 200
assert response.json["name"] == "test" assert response.json["name"] == "test"
def test_update_node(http_controller, tmpdir, project, compute, node): async def test_update_node(controller_api, project, compute, node):
response = MagicMock() response = MagicMock()
response.json = {"console": 2048} response.json = {"console": 2048}
compute.put = AsyncioMagicMock(return_value=response) compute.put = AsyncioMagicMock(return_value=response)
response = http_controller.put("/projects/{}/nodes/{}".format(project.id, node.id), { response = await controller_api.put("/projects/{}/nodes/{}".format(project.id, node.id), {
"name": "test", "name": "test",
"node_type": "vpcs", "node_type": "vpcs",
"compute_id": "example.com", "compute_id": "example.com",
"properties": { "properties": {
"startup_script": "echo test" "startup_script": "echo test"
} }
}, example=True) })
assert response.status == 200 assert response.status == 200
assert response.json["name"] == "test" assert response.json["name"] == "test"
assert "name" not in response.json["properties"] assert "name" not in response.json["properties"]
def test_start_all_nodes(http_controller, tmpdir, project, compute): async def test_start_all_nodes(controller_api, project, compute):
response = MagicMock()
compute.post = AsyncioMagicMock()
response = http_controller.post("/projects/{}/nodes/start".format(project.id), example=True) compute.post = AsyncioMagicMock()
response = await controller_api.post("/projects/{}/nodes/start".format(project.id))
assert response.status == 204 assert response.status == 204
def test_stop_all_nodes(http_controller, tmpdir, project, compute): async def test_stop_all_nodes(controller_api, project, compute):
response = MagicMock()
compute.post = AsyncioMagicMock()
response = http_controller.post("/projects/{}/nodes/stop".format(project.id), example=True) compute.post = AsyncioMagicMock()
response = await controller_api.post("/projects/{}/nodes/stop".format(project.id))
assert response.status == 204 assert response.status == 204
def test_suspend_all_nodes(http_controller, tmpdir, project, compute): async def test_suspend_all_nodes(controller_api, project, compute):
response = MagicMock()
compute.post = AsyncioMagicMock()
response = http_controller.post("/projects/{}/nodes/suspend".format(project.id), example=True) compute.post = AsyncioMagicMock()
response = await controller_api.post("/projects/{}/nodes/suspend".format(project.id))
assert response.status == 204 assert response.status == 204
def test_reload_all_nodes(http_controller, tmpdir, project, compute): async def test_reload_all_nodes(controller_api, project, compute):
response = MagicMock()
compute.post = AsyncioMagicMock()
response = http_controller.post("/projects/{}/nodes/reload".format(project.id), example=True) compute.post = AsyncioMagicMock()
response = await controller_api.post("/projects/{}/nodes/reload".format(project.id))
assert response.status == 204 assert response.status == 204
def test_start_node(http_controller, tmpdir, project, compute, node): async def test_start_node(controller_api, project, node, compute):
response = MagicMock()
compute.post = AsyncioMagicMock()
response = http_controller.post("/projects/{}/nodes/{}/start".format(project.id, node.id), example=True) compute.post = AsyncioMagicMock()
response = await controller_api.post("/projects/{}/nodes/{}/start".format(project.id, node.id))
assert response.status == 200 assert response.status == 200
assert response.json == node.__json__() assert response.json == node.__json__()
def test_stop_node(http_controller, tmpdir, project, compute, node): async def test_stop_node(controller_api, project, node, compute):
response = MagicMock()
compute.post = AsyncioMagicMock()
response = http_controller.post("/projects/{}/nodes/{}/stop".format(project.id, node.id), example=True) compute.post = AsyncioMagicMock()
response = await controller_api.post("/projects/{}/nodes/{}/stop".format(project.id, node.id))
assert response.status == 200 assert response.status == 200
assert response.json == node.__json__() assert response.json == node.__json__()
def test_suspend_node(http_controller, tmpdir, project, compute, node): async def test_suspend_node(controller_api, project, node, compute):
response = MagicMock()
compute.post = AsyncioMagicMock()
response = http_controller.post("/projects/{}/nodes/{}/suspend".format(project.id, node.id), example=True) compute.post = AsyncioMagicMock()
response = await controller_api.post("/projects/{}/nodes/{}/suspend".format(project.id, node.id))
assert response.status == 200 assert response.status == 200
assert response.json == node.__json__() assert response.json == node.__json__()
def test_reload_node(http_controller, tmpdir, project, compute, node): async def test_reload_node(controller_api, project, node, compute):
response = MagicMock()
compute.post = AsyncioMagicMock()
response = http_controller.post("/projects/{}/nodes/{}/reload".format(project.id, node.id), example=True) compute.post = AsyncioMagicMock()
response = await controller_api.post("/projects/{}/nodes/{}/reload".format(project.id, node.id))
assert response.status == 200 assert response.status == 200
assert response.json == node.__json__() assert response.json == node.__json__()
def test_duplicate_node(http_controller, tmpdir, project, compute, node): async def test_duplicate_node(controller_api, project, compute, node):
response = MagicMock() response = MagicMock()
response.json({"console": 2035}) response.json({"console": 2035})
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
response = http_controller.post("/projects/{}/nodes/{}/duplicate".format( response = await controller_api.post("/projects/{}/nodes/{}/duplicate".format(project.id, node.id),
project.id, node.id), {"x": 10,
{"x": 10, "y": 5, "z": 0}, "y": 5,
example=True) "z": 0})
assert response.status == 201, response.body.decode() assert response.status == 201, response.body.decode()
def test_delete_node(http_controller, tmpdir, project, compute, node): async def test_delete_node(controller_api, project, node, compute):
response = MagicMock()
compute.post = AsyncioMagicMock()
response = http_controller.delete("/projects/{}/nodes/{}".format(project.id, node.id), example=True) compute.post = AsyncioMagicMock()
response = await controller_api.delete("/projects/{}/nodes/{}".format(project.id, node.id))
assert response.status == 204 assert response.status == 204
def test_dynamips_idle_pc(http_controller, tmpdir, project, compute, node): async def test_dynamips_idle_pc(controller_api, project, compute, node):
response = MagicMock() response = MagicMock()
response.json = {"idlepc": "0x60606f54"} response.json = {"idlepc": "0x60606f54"}
compute.get = AsyncioMagicMock(return_value=response) compute.get = AsyncioMagicMock(return_value=response)
response = http_controller.get("/projects/{}/nodes/{}/dynamips/auto_idlepc".format(project.id, node.id), example=True) response = await controller_api.get("/projects/{}/nodes/{}/dynamips/auto_idlepc".format(project.id, node.id))
assert response.status == 200 assert response.status == 200
assert response.json["idlepc"] == "0x60606f54" assert response.json["idlepc"] == "0x60606f54"
def test_dynamips_idlepc_proposals(http_controller, tmpdir, project, compute, node): async def test_dynamips_idlepc_proposals(controller_api, project, compute, node):
response = MagicMock() response = MagicMock()
response.json = ["0x60606f54", "0x33805a22"] response.json = ["0x60606f54", "0x33805a22"]
compute.get = AsyncioMagicMock(return_value=response) compute.get = AsyncioMagicMock(return_value=response)
response = http_controller.get("/projects/{}/nodes/{}/dynamips/idlepc_proposals".format(project.id, node.id), example=True) response = await controller_api.get("/projects/{}/nodes/{}/dynamips/idlepc_proposals".format(project.id, node.id))
assert response.status == 200 assert response.status == 200
assert response.json == ["0x60606f54", "0x33805a22"] assert response.json == ["0x60606f54", "0x33805a22"]
def test_get_file(http_controller, tmpdir, project, node, compute): async def test_get_file(controller_api, project, node, compute):
response = MagicMock() response = MagicMock()
response.body = b"world" response.body = b"world"
compute.http_query = AsyncioMagicMock(return_value=response) compute.http_query = AsyncioMagicMock(return_value=response)
response = http_controller.get("/projects/{project_id}/nodes/{node_id}/files/hello".format(project_id=project.id, node_id=node.id), raw=True)
response = await controller_api.get("/projects/{project_id}/nodes/{node_id}/files/hello".format(project_id=project.id, node_id=node.id))
assert response.status == 200 assert response.status == 200
assert response.body == b'world' assert response.body == b'world'
compute.http_query.assert_called_with("GET", "/projects/{project_id}/files/project-files/vpcs/{node_id}/hello".format(project_id=project.id, node_id=node.id), timeout=None, raw=True) compute.http_query.assert_called_with("GET", "/projects/{project_id}/files/project-files/vpcs/{node_id}/hello".format(project_id=project.id, node_id=node.id), timeout=None, raw=True)
response = http_controller.get("/projects/{project_id}/nodes/{node_id}/files/../hello".format(project_id=project.id, node_id=node.id), raw=True) response = await controller_api.get("/projects/{project_id}/nodes/{node_id}/files/../hello".format(project_id=project.id, node_id=node.id))
assert response.status == 404 assert response.status == 404
def test_post_file(http_controller, tmpdir, project, node, compute): async def test_post_file(controller_api, project, node, compute):
compute.http_query = AsyncioMagicMock() compute.http_query = AsyncioMagicMock()
response = http_controller.post("/projects/{project_id}/nodes/{node_id}/files/hello".format(project_id=project.id, node_id=node.id), body=b"hello", raw=True) response = await controller_api.post("/projects/{project_id}/nodes/{node_id}/files/hello".format(project_id=project.id, node_id=node.id), body=b"hello", raw=True)
assert response.status == 201 assert response.status == 201
compute.http_query.assert_called_with("POST", "/projects/{project_id}/files/project-files/vpcs/{node_id}/hello".format(project_id=project.id, node_id=node.id), data=b'hello', timeout=None, raw=True) compute.http_query.assert_called_with("POST", "/projects/{project_id}/files/project-files/vpcs/{node_id}/hello".format(project_id=project.id, node_id=node.id), data=b'hello', timeout=None, raw=True)
response = http_controller.get("/projects/{project_id}/nodes/{node_id}/files/../hello".format(project_id=project.id, node_id=node.id), raw=True) response = await controller_api.get("/projects/{project_id}/nodes/{node_id}/files/../hello".format(project_id=project.id, node_id=node.id))
assert response.status == 404 assert response.status == 404
def test_get_and_post_with_nested_paths_normalization(http_controller, tmpdir, project, node, compute): async def test_get_and_post_with_nested_paths_normalization(controller_api, project, node, compute):
response = MagicMock() response = MagicMock()
response.body = b"world" response.body = b"world"
compute.http_query = AsyncioMagicMock(return_value=response) compute.http_query = AsyncioMagicMock(return_value=response)
response = http_controller.get("/projects/{project_id}/nodes/{node_id}/files/hello\\nested".format(project_id=project.id, node_id=node.id), raw=True) response = await controller_api.get("/projects/{project_id}/nodes/{node_id}/files/hello\\nested".format(project_id=project.id, node_id=node.id))
assert response.status == 200 assert response.status == 200
assert response.body == b'world' assert response.body == b'world'
compute.http_query.assert_called_with("GET", "/projects/{project_id}/files/project-files/vpcs/{node_id}/hello/nested".format(project_id=project.id, node_id=node.id), timeout=None, raw=True) compute.http_query.assert_called_with("GET", "/projects/{project_id}/files/project-files/vpcs/{node_id}/hello/nested".format(project_id=project.id, node_id=node.id), timeout=None, raw=True)
compute.http_query = AsyncioMagicMock() compute.http_query = AsyncioMagicMock()
response = http_controller.post("/projects/{project_id}/nodes/{node_id}/files/hello\\nested".format(project_id=project.id, node_id=node.id), body=b"hello", raw=True) response = await controller_api.post("/projects/{project_id}/nodes/{node_id}/files/hello\\nested".format(project_id=project.id, node_id=node.id), body=b"hello", raw=True)
assert response.status == 201 assert response.status == 201
compute.http_query.assert_called_with("POST", "/projects/{project_id}/files/project-files/vpcs/{node_id}/hello/nested".format(project_id=project.id, node_id=node.id), data=b'hello', timeout=None, raw=True) compute.http_query.assert_called_with("POST", "/projects/{project_id}/files/project-files/vpcs/{node_id}/hello/nested".format(project_id=project.id, node_id=node.id), data=b'hello', timeout=None, raw=True)

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -15,202 +15,206 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
This test suite check /project endpoint
"""
import uuid import uuid
import os import os
import asyncio
import aiohttp
import pytest import pytest
import zipfile import zipfile
import json import json
from unittest.mock import patch, MagicMock from unittest.mock import patch, MagicMock
from tests.utils import asyncio_patch from tests.utils import asyncio_patch
from gns3server.handlers.api.controller.project_handler import ProjectHandler
from gns3server.controller import Controller
@pytest.fixture @pytest.fixture
def project(http_controller, controller): async def project(controller_api, controller):
u = str(uuid.uuid4()) u = str(uuid.uuid4())
query = {"name": "test", "project_id": u} params = {"name": "test", "project_id": u}
response = http_controller.post("/projects", query) await controller_api.post("/projects", params)
return controller.get_project(u) return controller.get_project(u)
def test_create_project_with_path(http_controller, tmpdir): async def test_create_project_with_path(controller_api, tmpdir):
response = http_controller.post("/projects", {"name": "test", "path": str(tmpdir), "project_id": "00010203-0405-0607-0809-0a0b0c0d0e0f"})
response = await controller_api.post("/projects", {"name": "test", "path": str(tmpdir), "project_id": "00010203-0405-0607-0809-0a0b0c0d0e0f"})
assert response.status == 201 assert response.status == 201
assert response.json["name"] == "test" assert response.json["name"] == "test"
assert response.json["project_id"] == "00010203-0405-0607-0809-0a0b0c0d0e0f" assert response.json["project_id"] == "00010203-0405-0607-0809-0a0b0c0d0e0f"
assert response.json["status"] == "opened" assert response.json["status"] == "opened"
def test_create_project_without_dir(http_controller): async def test_create_project_without_dir(controller_api):
query = {"name": "test", "project_id": "10010203-0405-0607-0809-0a0b0c0d0e0f"}
response = http_controller.post("/projects", query, example=True) params = {"name": "test", "project_id": "10010203-0405-0607-0809-0a0b0c0d0e0f"}
response = await controller_api.post("/projects", params)
assert response.status == 201 assert response.status == 201
assert response.json["project_id"] == "10010203-0405-0607-0809-0a0b0c0d0e0f" assert response.json["project_id"] == "10010203-0405-0607-0809-0a0b0c0d0e0f"
assert response.json["name"] == "test" assert response.json["name"] == "test"
def test_create_project_with_uuid(http_controller): async def test_create_project_with_uuid(controller_api):
query = {"name": "test", "project_id": "30010203-0405-0607-0809-0a0b0c0d0e0f"}
response = http_controller.post("/projects", query) params = {"name": "test", "project_id": "30010203-0405-0607-0809-0a0b0c0d0e0f"}
response = await controller_api.post("/projects", params)
assert response.status == 201 assert response.status == 201
assert response.json["project_id"] == "30010203-0405-0607-0809-0a0b0c0d0e0f" assert response.json["project_id"] == "30010203-0405-0607-0809-0a0b0c0d0e0f"
assert response.json["name"] == "test" assert response.json["name"] == "test"
def test_create_project_with_variables(http_controller): async def test_create_project_with_variables(controller_api):
variables = [ variables = [
{"name": "TEST1"}, {"name": "TEST1"},
{"name": "TEST2", "value": "value1"} {"name": "TEST2", "value": "value1"}
] ]
query = {"name": "test", "project_id": "30010203-0405-0607-0809-0a0b0c0d0e0f", "variables": variables} params = {"name": "test", "project_id": "30010203-0405-0607-0809-0a0b0c0d0e0f", "variables": variables}
response = http_controller.post("/projects", query) response = await controller_api.post("/projects", params)
assert response.status == 201 assert response.status == 201
assert response.json["variables"] == [ assert response.json["variables"] == [
{"name": "TEST1"}, {"name": "TEST1"},
{"name": "TEST2", "value": "value1"} {"name": "TEST2", "value": "value1"}
] ]
def test_create_project_with_supplier(http_controller): async def test_create_project_with_supplier(controller_api):
supplier = { supplier = {
'logo': 'logo.png', 'logo': 'logo.png',
'url': 'http://example.com' 'url': 'http://example.com'
} }
query = {"name": "test", "project_id": "30010203-0405-0607-0809-0a0b0c0d0e0f", "supplier": supplier} params = {"name": "test", "project_id": "30010203-0405-0607-0809-0a0b0c0d0e0f", "supplier": supplier}
response = http_controller.post("/projects", query) response = await controller_api.post("/projects", params)
assert response.status == 201 assert response.status == 201
assert response.json["supplier"] == supplier assert response.json["supplier"] == supplier
def test_update_project(http_controller): async def test_update_project(controller_api):
query = {"name": "test", "project_id": "10010203-0405-0607-0809-0a0b0c0d0e0f"}
response = http_controller.post("/projects", query) params = {"name": "test", "project_id": "10010203-0405-0607-0809-0a0b0c0d0e0f"}
response = await controller_api.post("/projects", params)
assert response.status == 201 assert response.status == 201
assert response.json["project_id"] == "10010203-0405-0607-0809-0a0b0c0d0e0f" assert response.json["project_id"] == "10010203-0405-0607-0809-0a0b0c0d0e0f"
assert response.json["name"] == "test" assert response.json["name"] == "test"
query = {"name": "test2"}
response = http_controller.put("/projects/10010203-0405-0607-0809-0a0b0c0d0e0f", query, example=True) params = {"name": "test2"}
response = await controller_api.put("/projects/10010203-0405-0607-0809-0a0b0c0d0e0f", params)
assert response.status == 200 assert response.status == 200
assert response.json["name"] == "test2" assert response.json["name"] == "test2"
def test_update_project_with_variables(http_controller): async def test_update_project_with_variables(controller_api):
variables = [ variables = [
{"name": "TEST1"}, {"name": "TEST1"},
{"name": "TEST2", "value": "value1"} {"name": "TEST2", "value": "value1"}
] ]
query = {"name": "test", "project_id": "10010203-0405-0607-0809-0a0b0c0d0e0f", "variables": variables} params = {"name": "test", "project_id": "10010203-0405-0607-0809-0a0b0c0d0e0f", "variables": variables}
response = http_controller.post("/projects", query) response = await controller_api.post("/projects", params)
assert response.status == 201 assert response.status == 201
query = {"name": "test2"}
response = http_controller.put("/projects/10010203-0405-0607-0809-0a0b0c0d0e0f", query, example=True) params = {"name": "test2"}
response = await controller_api.put("/projects/10010203-0405-0607-0809-0a0b0c0d0e0f", params)
assert response.status == 200 assert response.status == 200
assert response.json["variables"] == variables assert response.json["variables"] == variables
def test_list_projects(http_controller, tmpdir): async def test_list_projects(controller_api, tmpdir):
http_controller.post("/projects", {"name": "test", "path": str(tmpdir), "project_id": "00010203-0405-0607-0809-0a0b0c0d0e0f"})
response = http_controller.get("/projects", example=True) await controller_api.post("/projects", {"name": "test", "path": str(tmpdir), "project_id": "00010203-0405-0607-0809-0a0b0c0d0e0f"})
response = await controller_api.get("/projects")
assert response.status == 200 assert response.status == 200
projects = response.json projects = response.json
assert projects[0]["name"] == "test" assert projects[0]["name"] == "test"
def test_get_project(http_controller, project): async def test_get_project(controller_api, project):
response = http_controller.get("/projects/{project_id}".format(project_id=project.id), example=True)
response = await controller_api.get("/projects/{project_id}".format(project_id=project.id))
assert response.status == 200 assert response.status == 200
assert response.json["name"] == "test" assert response.json["name"] == "test"
def test_delete_project(http_controller, project): async def test_delete_project(controller_api, project, controller):
with asyncio_patch("gns3server.controller.project.Project.delete", return_value=True) as mock: with asyncio_patch("gns3server.controller.project.Project.delete", return_value=True) as mock:
response = http_controller.delete("/projects/{project_id}".format(project_id=project.id), example=True) response = await controller_api.delete("/projects/{project_id}".format(project_id=project.id))
assert response.status == 204 assert response.status == 204
assert mock.called assert mock.called
assert project not in Controller.instance().projects assert project not in controller.projects
async def test_delete_project_invalid_uuid(controller_api):
def test_delete_project_invalid_uuid(http_controller): response = await controller_api.delete("/projects/{project_id}".format(project_id=uuid.uuid4()))
response = http_controller.delete("/projects/{project_id}".format(project_id=uuid.uuid4()))
assert response.status == 404 assert response.status == 404
def test_close_project(http_controller, project): async def test_close_project(controller_api, project):
with asyncio_patch("gns3server.controller.project.Project.close", return_value=True) as mock: with asyncio_patch("gns3server.controller.project.Project.close", return_value=True) as mock:
response = http_controller.post("/projects/{project_id}/close".format(project_id=project.id), example=True) response = await controller_api.post("/projects/{project_id}/close".format(project_id=project.id))
assert response.status == 204 assert response.status == 204
assert mock.called assert mock.called
def test_open_project(http_controller, project): async def test_open_project(controller_api, project):
with asyncio_patch("gns3server.controller.project.Project.open", return_value=True) as mock: with asyncio_patch("gns3server.controller.project.Project.open", return_value=True) as mock:
response = http_controller.post("/projects/{project_id}/open".format(project_id=project.id), example=True) response = await controller_api.post("/projects/{project_id}/open".format(project_id=project.id))
assert response.status == 201 assert response.status == 201
assert mock.called assert mock.called
def test_load_project(http_controller, project, config): async def test_load_project(controller_api, project, config):
config.set("Server", "local", "true") config.set("Server", "local", "true")
with asyncio_patch("gns3server.controller.Controller.load_project", return_value=project) as mock: with asyncio_patch("gns3server.controller.Controller.load_project", return_value=project) as mock:
response = http_controller.post("/projects/load".format(project_id=project.id), {"path": "/tmp/test.gns3"}, example=True) response = await controller_api.post("/projects/load".format(project_id=project.id), {"path": "/tmp/test.gns3"})
assert response.status == 201 assert response.status == 201
mock.assert_called_with("/tmp/test.gns3") mock.assert_called_with("/tmp/test.gns3")
assert response.json["project_id"] == project.id assert response.json["project_id"] == project.id
def test_notification(http_controller, project, controller, loop, async_run): async def test_notification(controller_api, http_client, project, controller):
async def go(): async with http_client.get(controller_api.get_url("/projects/{project_id}/notifications".format(project_id=project.id))) as response:
connector = aiohttp.TCPConnector() response.body = await response.content.read(200)
async with aiohttp.request("GET", http_controller.get_url("/projects/{project_id}/notifications".format(project_id=project.id)), connector=connector) as response: controller.notification.project_emit("node.created", {"a": "b"})
response.body = await response.content.read(200) response.body += await response.content.readany()
controller.notification.project_emit("node.created", {"a": "b"}) assert response.status == 200
response.body += await response.content.readany() assert b'"action": "ping"' in response.body
return response assert b'"cpu_usage_percent"' in response.body
assert b'{"action": "node.created", "event": {"a": "b"}}\n' in response.body
assert project.status == "opened"
response = async_run(asyncio.ensure_future(go()))
assert response.status == 200
assert b'"action": "ping"' in response.body
assert b'"cpu_usage_percent"' in response.body
assert b'{"action": "node.created", "event": {"a": "b"}}\n' in response.body
assert project.status == "opened"
async def test_notification_invalid_id(controller_api):
def test_notification_invalid_id(http_controller): response = await controller_api.get("/projects/{project_id}/notifications".format(project_id=uuid.uuid4()))
response = http_controller.get("/projects/{project_id}/notifications".format(project_id=uuid.uuid4()))
assert response.status == 404 assert response.status == 404
def test_notification_ws(http_controller, controller, project, async_run): async def test_notification_ws(controller_api, http_client, controller, project):
ws = http_controller.websocket("/projects/{project_id}/notifications/ws".format(project_id=project.id))
answer = async_run(ws.receive()) ws = await http_client.ws_connect(controller_api.get_url("/projects/{project_id}/notifications/ws".format(project_id=project.id)))
answer = await ws.receive()
answer = json.loads(answer.data) answer = json.loads(answer.data)
assert answer["action"] == "ping" assert answer["action"] == "ping"
controller.notification.project_emit("test", {}) controller.notification.project_emit("test", {})
answer = await ws.receive()
answer = async_run(ws.receive())
answer = json.loads(answer.data) answer = json.loads(answer.data)
assert answer["action"] == "test" assert answer["action"] == "test"
async_run(http_controller.close()) if not ws.closed:
async_run(ws.close()) await ws.close()
assert project.status == "opened" assert project.status == "opened"
def test_export_with_images(http_controller, tmpdir, loop, project): async def test_export_with_images(controller_api, tmpdir, project):
project.dump = MagicMock()
project.dump = MagicMock()
os.makedirs(project.path, exist_ok=True) os.makedirs(project.path, exist_ok=True)
with open(os.path.join(project.path, 'a'), 'w+') as f: with open(os.path.join(project.path, 'a'), 'w+') as f:
f.write('hello') f.write('hello')
@ -234,8 +238,8 @@ def test_export_with_images(http_controller, tmpdir, loop, project):
with open(os.path.join(project.path, "test.gns3"), 'w+') as f: with open(os.path.join(project.path, "test.gns3"), 'w+') as f:
json.dump(topology, f) json.dump(topology, f)
with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir / "IOS"),): with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir / "IOS")):
response = http_controller.get("/projects/{project_id}/export?include_images=yes".format(project_id=project.id), raw=True) response = await controller_api.get("/projects/{project_id}/export?include_images=yes".format(project_id=project.id))
assert response.status == 200 assert response.status == 200
assert response.headers['CONTENT-TYPE'] == 'application/gns3project' assert response.headers['CONTENT-TYPE'] == 'application/gns3project'
assert response.headers['CONTENT-DISPOSITION'] == 'attachment; filename="{}.gns3project"'.format(project.name) assert response.headers['CONTENT-DISPOSITION'] == 'attachment; filename="{}.gns3project"'.format(project.name)
@ -250,9 +254,9 @@ def test_export_with_images(http_controller, tmpdir, loop, project):
myzip.getinfo("images/IOS/test.image") myzip.getinfo("images/IOS/test.image")
def test_export_without_images(http_controller, tmpdir, loop, project): async def test_export_without_images(controller_api, tmpdir, project):
project.dump = MagicMock()
project.dump = MagicMock()
os.makedirs(project.path, exist_ok=True) os.makedirs(project.path, exist_ok=True)
with open(os.path.join(project.path, 'a'), 'w+') as f: with open(os.path.join(project.path, 'a'), 'w+') as f:
f.write('hello') f.write('hello')
@ -277,7 +281,7 @@ def test_export_without_images(http_controller, tmpdir, loop, project):
json.dump(topology, f) json.dump(topology, f)
with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir / "IOS"),): with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir / "IOS"),):
response = http_controller.get("/projects/{project_id}/export?include_images=0".format(project_id=project.id), raw=True) response = await controller_api.get("/projects/{project_id}/export?include_images=0".format(project_id=project.id))
assert response.status == 200 assert response.status == 200
assert response.headers['CONTENT-TYPE'] == 'application/gns3project' assert response.headers['CONTENT-TYPE'] == 'application/gns3project'
assert response.headers['CONTENT-DISPOSITION'] == 'attachment; filename="{}.gns3project"'.format(project.name) assert response.headers['CONTENT-DISPOSITION'] == 'attachment; filename="{}.gns3project"'.format(project.name)
@ -294,52 +298,54 @@ def test_export_without_images(http_controller, tmpdir, loop, project):
myzip.getinfo("images/IOS/test.image") myzip.getinfo("images/IOS/test.image")
def test_get_file(http_controller, tmpdir, loop, project): async def test_get_file(controller_api, project):
os.makedirs(project.path, exist_ok=True) os.makedirs(project.path, exist_ok=True)
with open(os.path.join(project.path, 'hello'), 'w+') as f: with open(os.path.join(project.path, 'hello'), 'w+') as f:
f.write('world') f.write('world')
response = http_controller.get("/projects/{project_id}/files/hello".format(project_id=project.id), raw=True) response = await controller_api.get("/projects/{project_id}/files/hello".format(project_id=project.id))
assert response.status == 200 assert response.status == 200
assert response.body == b"world" assert response.body == b"world"
response = http_controller.get("/projects/{project_id}/files/false".format(project_id=project.id), raw=True) response = await controller_api.get("/projects/{project_id}/files/false".format(project_id=project.id))
assert response.status == 404 assert response.status == 404
response = http_controller.get("/projects/{project_id}/files/../hello".format(project_id=project.id), raw=True) response = await controller_api.get("/projects/{project_id}/files/../hello".format(project_id=project.id))
assert response.status == 404 assert response.status == 404
def test_write_file(http_controller, tmpdir, project): async def test_write_file(controller_api, project):
response = http_controller.post("/projects/{project_id}/files/hello".format(project_id=project.id), body="world", raw=True)
response = await controller_api.post("/projects/{project_id}/files/hello".format(project_id=project.id), body="world", raw=True)
assert response.status == 200 assert response.status == 200
with open(os.path.join(project.path, "hello")) as f: with open(os.path.join(project.path, "hello")) as f:
assert f.read() == "world" assert f.read() == "world"
response = http_controller.post("/projects/{project_id}/files/../hello".format(project_id=project.id), raw=True) response = await controller_api.post("/projects/{project_id}/files/../hello".format(project_id=project.id), raw=True)
assert response.status == 404 assert response.status == 404
def test_write_and_get_file_with_leading_slashes_in_filename(http_controller, tmpdir, loop, project): async def test_write_and_get_file_with_leading_slashes_in_filename(controller_api, project):
response = http_controller.post("/projects/{project_id}/files//hello".format(project_id=project.id), body="world", raw=True)
response = await controller_api.post("/projects/{project_id}/files//hello".format(project_id=project.id), body="world", raw=True)
assert response.status == 200 assert response.status == 200
response = http_controller.get("/projects/{project_id}/files//hello".format(project_id=project.id), raw=True) response = await controller_api.get("/projects/{project_id}/files//hello".format(project_id=project.id), raw=True)
assert response.status == 200 assert response.status == 200
assert response.body == b"world" assert response.body == b"world"
def test_import(http_controller, tmpdir, controller): async def test_import(controller_api, tmpdir, controller):
with zipfile.ZipFile(str(tmpdir / "test.zip"), 'w') as myzip: with zipfile.ZipFile(str(tmpdir / "test.zip"), 'w') as myzip:
myzip.writestr("project.gns3", b'{"project_id": "c6992992-ac72-47dc-833b-54aa334bcd05", "version": "2.0.0", "name": "test"}') myzip.writestr("project.gns3", b'{"project_id": "c6992992-ac72-47dc-833b-54aa334bcd05", "version": "2.0.0", "name": "test"}')
myzip.writestr("demo", b"hello") myzip.writestr("demo", b"hello")
project_id = str(uuid.uuid4()) project_id = str(uuid.uuid4())
with open(str(tmpdir / "test.zip"), "rb") as f: with open(str(tmpdir / "test.zip"), "rb") as f:
response = http_controller.post("/projects/{project_id}/import".format(project_id=project_id), body=f.read(), raw=True) response = await controller_api.post("/projects/{project_id}/import".format(project_id=project_id), body=f.read(), raw=True)
assert response.status == 201 assert response.status == 201
project = controller.get_project(project_id) project = controller.get_project(project_id)
@ -348,8 +354,8 @@ def test_import(http_controller, tmpdir, controller):
assert content == "hello" assert content == "hello"
def test_duplicate(http_controller, tmpdir, loop, project): async def test_duplicate(controller_api, project):
response = http_controller.post("/projects/{project_id}/duplicate".format(project_id=project.id), {"name": "hello"}, example=True) response = await controller_api.post("/projects/{project_id}/duplicate".format(project_id=project.id), {"name": "hello"})
assert response.status == 201 assert response.status == 201
assert response.json["name"] == "hello" assert response.json["name"] == "hello"

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2016 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -17,61 +17,60 @@
import os import os
import pytest import pytest
import asyncio
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock
from gns3server.web.web_server import WebServer from gns3server.web.web_server import WebServer
@pytest.yield_fixture @pytest.fixture
def web_server(): def web_server():
WebServer._instance = MagicMock() WebServer._instance = MagicMock()
yield WebServer._instance yield WebServer._instance
WebServer._instance = None WebServer._instance = None
def test_shutdown_local(http_controller, web_server, config): async def test_shutdown_local(controller_api, web_server, config):
async def hello(): async def hello():
return 0 return 0
web_server.shutdown_server.return_value = hello() web_server.shutdown_server.return_value = hello()
config.set("Server", "local", True) config.set("Server", "local", True)
response = http_controller.post('/shutdown', example=True) response = await controller_api.post('/shutdown')
assert response.status == 201 assert response.status == 201
assert web_server.shutdown_server.called assert web_server.shutdown_server.called
def test_shutdown_non_local(http_controller, web_server, config): async def test_shutdown_non_local(controller_api, web_server, config):
"""
Disallow shutdown of a non local GNS3 server
"""
WebServer._instance = MagicMock() WebServer._instance = MagicMock()
config.set("Server", "local", False) config.set("Server", "local", False)
response = http_controller.post('/shutdown') response = await controller_api.post('/shutdown')
assert response.status == 403 assert response.status == 403
assert not web_server.shutdown_server.called assert not web_server.shutdown_server.called
def test_debug(http_controller, config, tmpdir): async def test_debug(controller_api, config, tmpdir):
config._main_config_file = str(tmpdir / "test.conf")
config._main_config_file = str(tmpdir / "test.conf")
config.set("Server", "local", True) config.set("Server", "local", True)
response = http_controller.post('/debug') response = await controller_api.post('/debug')
assert response.status == 201 assert response.status == 201
debug_dir = os.path.join(config.config_dir, "debug") debug_dir = os.path.join(config.config_dir, "debug")
assert os.path.exists(debug_dir) assert os.path.exists(debug_dir)
assert os.path.exists(os.path.join(debug_dir, "controller.txt")) assert os.path.exists(os.path.join(debug_dir, "controller.txt"))
def test_debug_non_local(http_controller, config, tmpdir): async def test_debug_non_local(controller_api, config, tmpdir):
config._main_config_file = str(tmpdir / "test.conf")
config._main_config_file = str(tmpdir / "test.conf")
config.set("Server", "local", False) config.set("Server", "local", False)
response = http_controller.post('/debug') response = await controller_api.post('/debug')
assert response.status == 403 assert response.status == 403
def test_statistics_output(http_controller): async def test_statistics_output(controller_api):
response = http_controller.get('/statistics')
response = await controller_api.get('/statistics')
assert response.status == 200 assert response.status == 200

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2016 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -21,40 +21,46 @@ import pytest
@pytest.fixture @pytest.fixture
def project(http_controller, controller): async def project(controller_api, controller):
u = str(uuid.uuid4()) u = str(uuid.uuid4())
query = {"name": "test", "project_id": u} params = {"name": "test", "project_id": u}
response = http_controller.post("/projects", query) await controller_api.post("/projects", params)
project = controller.get_project(u) project = controller.get_project(u)
return project return project
@pytest.fixture @pytest.fixture
def snapshot(project, async_run): async def snapshot(project):
snapshot = async_run(project.snapshot("test"))
snapshot = await project.snapshot("test")
return snapshot return snapshot
def test_list_snapshots(http_controller, project, snapshot): async def test_list_snapshots(controller_api, project, snapshot):
response = http_controller.get("/projects/{}/snapshots".format(project.id), example=True)
assert snapshot.name == "test"
response = await controller_api.get("/projects/{}/snapshots".format(project.id))
assert response.status == 200 assert response.status == 200
assert len(response.json) == 1 assert len(response.json) == 1
def test_delete_snapshot(http_controller, project, snapshot): async def test_delete_snapshot(controller_api, project, snapshot):
response = http_controller.delete("/projects/{}/snapshots/{}".format(project.id, snapshot.id), example=True)
response = await controller_api.delete("/projects/{}/snapshots/{}".format(project.id, snapshot.id))
assert response.status == 204 assert response.status == 204
assert not os.path.exists(snapshot.path) assert not os.path.exists(snapshot.path)
def test_restore_snapshot(http_controller, project, snapshot): async def test_restore_snapshot(controller_api, project, snapshot):
response = http_controller.post("/projects/{}/snapshots/{}/restore".format(project.id, snapshot.id), example=True)
response = await controller_api.post("/projects/{}/snapshots/{}/restore".format(project.id, snapshot.id))
assert response.status == 201 assert response.status == 201
assert response.json["name"] == project.name assert response.json["name"] == project.name
def test_create_snapshot(http_controller, project): async def test_create_snapshot(controller_api, project):
response = http_controller.post("/projects/{}/snapshots".format(project.id), {"name": "snap1"}, example=True)
response = await controller_api.post("/projects/{}/snapshots".format(project.id), {"name": "snap1"})
assert response.status == 201 assert response.status == 201
assert len(os.listdir(os.path.join(project.path, "snapshots"))) == 1 assert len(os.listdir(os.path.join(project.path, "snapshots"))) == 1

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -16,14 +16,13 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import os import os
import sys
import urllib.parse import urllib.parse
from gns3server.config import Config
async def test_symbols(controller_api):
response = await controller_api.get('/symbols')
def test_symbols(http_controller):
response = http_controller.get('/symbols', example=True)
assert response.status == 200 assert response.status == 200
assert { assert {
'symbol_id': ':/symbols/classic/firewall.svg', 'symbol_id': ':/symbols/classic/firewall.svg',
@ -33,25 +32,27 @@ def test_symbols(http_controller):
} in response.json } in response.json
def test_get(http_controller, controller): async def test_get(controller_api, controller):
controller.symbols.theme = "Classic" controller.symbols.theme = "Classic"
response = http_controller.get('/symbols/' + urllib.parse.quote(':/symbols/classic/firewall.svg') + '/raw') response = await controller_api.get('/symbols/' + urllib.parse.quote(':/symbols/classic/firewall.svg') + '/raw')
assert response.status == 200 assert response.status == 200
assert response.headers['CONTENT-TYPE'] == 'image/svg+xml' assert response.headers['CONTENT-TYPE'] == 'image/svg+xml'
assert response.headers['CONTENT-LENGTH'] == '9381' assert response.headers['CONTENT-LENGTH'] == '9381'
assert '</svg>' in response.html assert '</svg>' in response.html
# Reply by the default symbol # Reply with the default symbol
response = http_controller.get('/symbols/404.png/raw') response = await controller_api.get('/symbols/404.png/raw')
assert response.status == 200 assert response.status == 200
def test_upload(http_controller, symbols_dir): async def test_upload(controller_api, symbols_dir):
response = http_controller.post("/symbols/test2/raw", body="TEST", raw=True)
response = await controller_api.post("/symbols/test2/raw", body="TEST", raw=True)
assert response.status == 204 assert response.status == 204
with open(os.path.join(symbols_dir, "test2")) as f: with open(os.path.join(symbols_dir, "test2")) as f:
assert f.read() == "TEST" assert f.read() == "TEST"
response = http_controller.get('/symbols/test2/raw') response = await controller_api.get('/symbols/test2/raw')
assert response.status == 200 assert response.status == 200

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2016 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -17,15 +17,13 @@
import uuid import uuid
import pytest
from unittest.mock import MagicMock
from tests.utils import asyncio_patch from tests.utils import asyncio_patch
from gns3server.controller import Controller
from gns3server.controller.template import Template from gns3server.controller.template import Template
def test_template_list(http_controller, controller): async def test_template_list(controller_api, controller):
id = str(uuid.uuid4()) id = str(uuid.uuid4())
controller.template_manager.load_templates() controller.template_manager.load_templates()
@ -37,13 +35,13 @@ def test_template_list(http_controller, controller):
"default_name_format": "{name}-{0}", "default_name_format": "{name}-{0}",
"compute_id": "local" "compute_id": "local"
}) })
response = http_controller.get("/templates", example=True) response = await controller_api.get("/templates")
assert response.status == 200 assert response.status == 200
assert response.route == "/templates" assert response.route == "/templates"
assert len(response.json) > 0 assert len(response.json) > 0
def test_template_create_without_id(http_controller, controller): async def test_template_create_without_id(controller_api, controller):
params = {"base_script_file": "vpcs_base_config.txt", params = {"base_script_file": "vpcs_base_config.txt",
"category": "guest", "category": "guest",
@ -55,14 +53,14 @@ def test_template_create_without_id(http_controller, controller):
"symbol": ":/symbols/vpcs_guest.svg", "symbol": ":/symbols/vpcs_guest.svg",
"template_type": "vpcs"} "template_type": "vpcs"}
response = http_controller.post("/templates", params, example=True) response = await controller_api.post("/templates", params)
assert response.status == 201 assert response.status == 201
assert response.route == "/templates" assert response.route == "/templates"
assert response.json["template_id"] is not None assert response.json["template_id"] is not None
assert len(controller.template_manager._templates) == 1 assert len(controller.template_manager.templates) == 1
def test_template_create_with_id(http_controller, controller): async def test_template_create_with_id(controller_api, controller):
params = {"template_id": str(uuid.uuid4()), params = {"template_id": str(uuid.uuid4()),
"base_script_file": "vpcs_base_config.txt", "base_script_file": "vpcs_base_config.txt",
@ -75,14 +73,14 @@ def test_template_create_with_id(http_controller, controller):
"symbol": ":/symbols/vpcs_guest.svg", "symbol": ":/symbols/vpcs_guest.svg",
"template_type": "vpcs"} "template_type": "vpcs"}
response = http_controller.post("/templates", params, example=True) response = await controller_api.post("/templates", params)
assert response.status == 201 assert response.status == 201
assert response.route == "/templates" assert response.route == "/templates"
assert response.json["template_id"] is not None assert response.json["template_id"] is not None
assert len(controller.template_manager._templates) == 1 assert len(controller.template_manager.templates) == 1
def test_template_create_wrong_type(http_controller, controller): async def test_template_create_wrong_type(controller_api, controller):
params = {"template_id": str(uuid.uuid4()), params = {"template_id": str(uuid.uuid4()),
"base_script_file": "vpcs_base_config.txt", "base_script_file": "vpcs_base_config.txt",
@ -95,12 +93,12 @@ def test_template_create_wrong_type(http_controller, controller):
"symbol": ":/symbols/vpcs_guest.svg", "symbol": ":/symbols/vpcs_guest.svg",
"template_type": "invalid_template_type"} "template_type": "invalid_template_type"}
response = http_controller.post("/templates", params) response = await controller_api.post("/templates", params)
assert response.status == 400 assert response.status == 400
assert len(controller.template_manager._templates) == 0 assert len(controller.template_manager.templates) == 0
def test_template_get(http_controller, controller): async def test_template_get(controller_api):
template_id = str(uuid.uuid4()) template_id = str(uuid.uuid4())
params = {"template_id": template_id, params = {"template_id": template_id,
@ -114,15 +112,15 @@ def test_template_get(http_controller, controller):
"symbol": ":/symbols/vpcs_guest.svg", "symbol": ":/symbols/vpcs_guest.svg",
"template_type": "vpcs"} "template_type": "vpcs"}
response = http_controller.post("/templates", params) response = await controller_api.post("/templates", params)
assert response.status == 201 assert response.status == 201
response = http_controller.get("/templates/{}".format(template_id), example=True) response = await controller_api.get("/templates/{}".format(template_id))
assert response.status == 200 assert response.status == 200
assert response.json["template_id"] == template_id assert response.json["template_id"] == template_id
def test_template_update(http_controller, controller): async def test_template_update(controller_api):
template_id = str(uuid.uuid4()) template_id = str(uuid.uuid4())
params = {"template_id": template_id, params = {"template_id": template_id,
@ -136,21 +134,21 @@ def test_template_update(http_controller, controller):
"symbol": ":/symbols/vpcs_guest.svg", "symbol": ":/symbols/vpcs_guest.svg",
"template_type": "vpcs"} "template_type": "vpcs"}
response = http_controller.post("/templates", params) response = await controller_api.post("/templates", params)
assert response.status == 201 assert response.status == 201
response = http_controller.get("/templates/{}".format(template_id)) response = await controller_api.get("/templates/{}".format(template_id))
assert response.status == 200 assert response.status == 200
assert response.json["template_id"] == template_id assert response.json["template_id"] == template_id
params["name"] = "VPCS_TEST_RENAMED" params["name"] = "VPCS_TEST_RENAMED"
response = http_controller.put("/templates/{}".format(template_id), params, example=True) response = await controller_api.put("/templates/{}".format(template_id), params)
assert response.status == 200 assert response.status == 200
assert response.json["name"] == "VPCS_TEST_RENAMED" assert response.json["name"] == "VPCS_TEST_RENAMED"
def test_template_delete(http_controller, controller): async def test_template_delete(controller_api, controller):
template_id = str(uuid.uuid4()) template_id = str(uuid.uuid4())
params = {"template_id": template_id, params = {"template_id": template_id,
@ -164,22 +162,22 @@ def test_template_delete(http_controller, controller):
"symbol": ":/symbols/vpcs_guest.svg", "symbol": ":/symbols/vpcs_guest.svg",
"template_type": "vpcs"} "template_type": "vpcs"}
response = http_controller.post("/templates", params) response = await controller_api.post("/templates", params)
assert response.status == 201 assert response.status == 201
response = http_controller.get("/templates") response = await controller_api.get("/templates")
assert len(response.json) == 1 assert len(response.json) == 1
assert len(controller.template_manager._templates) == 1 assert len(controller.template_manager._templates) == 1
response = http_controller.delete("/templates/{}".format(template_id), example=True) response = await controller_api.delete("/templates/{}".format(template_id))
assert response.status == 204 assert response.status == 204
response = http_controller.get("/templates") response = await controller_api.get("/templates")
assert len(response.json) == 0 assert len(response.json) == 0
assert len(controller.template_manager._templates) == 0 assert len(controller.template_manager.templates) == 0
def test_template_duplicate(http_controller, controller): async def test_template_duplicate(controller_api, controller):
template_id = str(uuid.uuid4()) template_id = str(uuid.uuid4())
params = {"template_id": template_id, params = {"template_id": template_id,
@ -193,22 +191,22 @@ def test_template_duplicate(http_controller, controller):
"symbol": ":/symbols/vpcs_guest.svg", "symbol": ":/symbols/vpcs_guest.svg",
"template_type": "vpcs"} "template_type": "vpcs"}
response = http_controller.post("/templates", params) response = await controller_api.post("/templates", params)
assert response.status == 201 assert response.status == 201
response = http_controller.post("/templates/{}/duplicate".format(template_id), example=True) response = await controller_api.post("/templates/{}/duplicate".format(template_id))
assert response.status == 201 assert response.status == 201
assert response.json["template_id"] != template_id assert response.json["template_id"] != template_id
params.pop("template_id") params.pop("template_id")
for param, value in params.items(): for param, value in params.items():
assert response.json[param] == value assert response.json[param] == value
response = http_controller.get("/templates") response = await controller_api.get("/templates")
assert len(response.json) == 2 assert len(response.json) == 2
assert len(controller.template_manager._templates) == 2 assert len(controller.template_manager.templates) == 2
def test_c7200_dynamips_template_create(http_controller): async def test_c7200_dynamips_template_create(controller_api):
params = {"name": "Cisco c7200 template", params = {"name": "Cisco c7200 template",
"platform": "c7200", "platform": "c7200",
@ -216,7 +214,7 @@ def test_c7200_dynamips_template_create(http_controller):
"image": "c7200-adventerprisek9-mz.124-24.T5.image", "image": "c7200-adventerprisek9-mz.124-24.T5.image",
"template_type": "dynamips"} "template_type": "dynamips"}
response = http_controller.post("/templates", params) response = await controller_api.post("/templates", params)
assert response.status == 201 assert response.status == 201
assert response.json["template_id"] is not None assert response.json["template_id"] is not None
@ -253,7 +251,7 @@ def test_c7200_dynamips_template_create(http_controller):
assert response.json.get(item) == value assert response.json.get(item) == value
def test_c3745_dynamips_template_create(http_controller): async def test_c3745_dynamips_template_create(controller_api):
params = {"name": "Cisco c3745 template", params = {"name": "Cisco c3745 template",
"platform": "c3745", "platform": "c3745",
@ -261,7 +259,7 @@ def test_c3745_dynamips_template_create(http_controller):
"image": "c3745-adventerprisek9-mz.124-25d.image", "image": "c3745-adventerprisek9-mz.124-25d.image",
"template_type": "dynamips"} "template_type": "dynamips"}
response = http_controller.post("/templates", params) response = await controller_api.post("/templates", params)
assert response.status == 201 assert response.status == 201
assert response.json["template_id"] is not None assert response.json["template_id"] is not None
@ -297,7 +295,7 @@ def test_c3745_dynamips_template_create(http_controller):
assert response.json.get(item) == value assert response.json.get(item) == value
def test_c3725_dynamips_template_create(http_controller): async def test_c3725_dynamips_template_create(controller_api):
params = {"name": "Cisco c3725 template", params = {"name": "Cisco c3725 template",
"platform": "c3725", "platform": "c3725",
@ -305,7 +303,7 @@ def test_c3725_dynamips_template_create(http_controller):
"image": "c3725-adventerprisek9-mz.124-25d.image", "image": "c3725-adventerprisek9-mz.124-25d.image",
"template_type": "dynamips"} "template_type": "dynamips"}
response = http_controller.post("/templates", params) response = await controller_api.post("/templates", params)
assert response.status == 201 assert response.status == 201
assert response.json["template_id"] is not None assert response.json["template_id"] is not None
@ -341,7 +339,7 @@ def test_c3725_dynamips_template_create(http_controller):
assert response.json.get(item) == value assert response.json.get(item) == value
def test_c3600_dynamips_template_create(http_controller): async def test_c3600_dynamips_template_create(controller_api):
params = {"name": "Cisco c3600 template", params = {"name": "Cisco c3600 template",
"platform": "c3600", "platform": "c3600",
@ -350,7 +348,7 @@ def test_c3600_dynamips_template_create(http_controller):
"image": "c3660-a3jk9s-mz.124-25d.image", "image": "c3660-a3jk9s-mz.124-25d.image",
"template_type": "dynamips"} "template_type": "dynamips"}
response = http_controller.post("/templates", params) response = await controller_api.post("/templates", params)
assert response.status == 201 assert response.status == 201
assert response.json["template_id"] is not None assert response.json["template_id"] is not None
@ -387,7 +385,7 @@ def test_c3600_dynamips_template_create(http_controller):
assert response.json.get(item) == value assert response.json.get(item) == value
def test_c3600_dynamips_template_create_wrong_chassis(http_controller): async def test_c3600_dynamips_template_create_wrong_chassis(controller_api):
params = {"name": "Cisco c3600 template", params = {"name": "Cisco c3600 template",
"platform": "c3600", "platform": "c3600",
@ -396,11 +394,11 @@ def test_c3600_dynamips_template_create_wrong_chassis(http_controller):
"image": "c3660-a3jk9s-mz.124-25d.image", "image": "c3660-a3jk9s-mz.124-25d.image",
"template_type": "dynamips"} "template_type": "dynamips"}
response = http_controller.post("/templates", params) response = await controller_api.post("/templates", params)
assert response.status == 400 assert response.status == 400
def test_c2691_dynamips_template_create(http_controller): async def test_c2691_dynamips_template_create(controller_api):
params = {"name": "Cisco c2691 template", params = {"name": "Cisco c2691 template",
"platform": "c2691", "platform": "c2691",
@ -408,7 +406,7 @@ def test_c2691_dynamips_template_create(http_controller):
"image": "c2691-adventerprisek9-mz.124-25d.image", "image": "c2691-adventerprisek9-mz.124-25d.image",
"template_type": "dynamips"} "template_type": "dynamips"}
response = http_controller.post("/templates", params) response = await controller_api.post("/templates", params)
assert response.status == 201 assert response.status == 201
assert response.json["template_id"] is not None assert response.json["template_id"] is not None
@ -444,7 +442,7 @@ def test_c2691_dynamips_template_create(http_controller):
assert response.json.get(item) == value assert response.json.get(item) == value
def test_c2600_dynamips_template_create(http_controller): async def test_c2600_dynamips_template_create(controller_api):
params = {"name": "Cisco c2600 template", params = {"name": "Cisco c2600 template",
"platform": "c2600", "platform": "c2600",
@ -453,7 +451,7 @@ def test_c2600_dynamips_template_create(http_controller):
"image": "c2600-adventerprisek9-mz.124-25d.image", "image": "c2600-adventerprisek9-mz.124-25d.image",
"template_type": "dynamips"} "template_type": "dynamips"}
response = http_controller.post("/templates", params) response = await controller_api.post("/templates", params)
assert response.status == 201 assert response.status == 201
assert response.json["template_id"] is not None assert response.json["template_id"] is not None
@ -490,7 +488,7 @@ def test_c2600_dynamips_template_create(http_controller):
assert response.json.get(item) == value assert response.json.get(item) == value
def test_c2600_dynamips_template_create_wrong_chassis(http_controller): async def test_c2600_dynamips_template_create_wrong_chassis(controller_api):
params = {"name": "Cisco c2600 template", params = {"name": "Cisco c2600 template",
"platform": "c2600", "platform": "c2600",
@ -499,11 +497,11 @@ def test_c2600_dynamips_template_create_wrong_chassis(http_controller):
"image": "c2600-adventerprisek9-mz.124-25d.image", "image": "c2600-adventerprisek9-mz.124-25d.image",
"template_type": "dynamips"} "template_type": "dynamips"}
response = http_controller.post("/templates", params) response = await controller_api.post("/templates", params)
assert response.status == 400 assert response.status == 400
def test_c1700_dynamips_template_create(http_controller): async def test_c1700_dynamips_template_create(controller_api):
params = {"name": "Cisco c1700 template", params = {"name": "Cisco c1700 template",
"platform": "c1700", "platform": "c1700",
@ -512,7 +510,7 @@ def test_c1700_dynamips_template_create(http_controller):
"image": "c1700-adventerprisek9-mz.124-25d.image", "image": "c1700-adventerprisek9-mz.124-25d.image",
"template_type": "dynamips"} "template_type": "dynamips"}
response = http_controller.post("/templates", params) response = await controller_api.post("/templates", params)
assert response.status == 201 assert response.status == 201
assert response.json["template_id"] is not None assert response.json["template_id"] is not None
@ -549,7 +547,7 @@ def test_c1700_dynamips_template_create(http_controller):
assert response.json.get(item) == value assert response.json.get(item) == value
def test_c1700_dynamips_template_create_wrong_chassis(http_controller): async def test_c1700_dynamips_template_create_wrong_chassis(controller_api):
params = {"name": "Cisco c1700 template", params = {"name": "Cisco c1700 template",
"platform": "c1700", "platform": "c1700",
@ -558,11 +556,11 @@ def test_c1700_dynamips_template_create_wrong_chassis(http_controller):
"image": "c1700-adventerprisek9-mz.124-25d.image", "image": "c1700-adventerprisek9-mz.124-25d.image",
"template_type": "dynamips"} "template_type": "dynamips"}
response = http_controller.post("/templates", params) response = await controller_api.post("/templates", params)
assert response.status == 400 assert response.status == 400
def test_dynamips_template_create_wrong_platform(http_controller): async def test_dynamips_template_create_wrong_platform(controller_api):
params = {"name": "Cisco c3900 template", params = {"name": "Cisco c3900 template",
"platform": "c3900", "platform": "c3900",
@ -570,18 +568,18 @@ def test_dynamips_template_create_wrong_platform(http_controller):
"image": "c3900-test.124-25d.image", "image": "c3900-test.124-25d.image",
"template_type": "dynamips"} "template_type": "dynamips"}
response = http_controller.post("/templates", params) response = await controller_api.post("/templates", params)
assert response.status == 400 assert response.status == 400
def test_iou_template_create(http_controller): async def test_iou_template_create(controller_api):
params = {"name": "IOU template", params = {"name": "IOU template",
"compute_id": "local", "compute_id": "local",
"path": "/path/to/i86bi_linux-ipbase-ms-12.4.bin", "path": "/path/to/i86bi_linux-ipbase-ms-12.4.bin",
"template_type": "iou"} "template_type": "iou"}
response = http_controller.post("/templates", params, example=True) response = await controller_api.post("/templates", params)
assert response.status == 201 assert response.status == 201
assert response.json["template_id"] is not None assert response.json["template_id"] is not None
@ -608,14 +606,14 @@ def test_iou_template_create(http_controller):
assert response.json.get(item) == value assert response.json.get(item) == value
def test_docker_template_create(http_controller): async def test_docker_template_create(controller_api):
params = {"name": "Docker template", params = {"name": "Docker template",
"compute_id": "local", "compute_id": "local",
"image": "gns3/endhost:latest", "image": "gns3/endhost:latest",
"template_type": "docker"} "template_type": "docker"}
response = http_controller.post("/templates", params) response = await controller_api.post("/templates", params)
assert response.status == 201 assert response.status == 201
assert response.json["template_id"] is not None assert response.json["template_id"] is not None
@ -642,7 +640,7 @@ def test_docker_template_create(http_controller):
assert response.json.get(item) == value assert response.json.get(item) == value
def test_qemu_template_create(http_controller): async def test_qemu_template_create(controller_api):
params = {"name": "Qemu template", params = {"name": "Qemu template",
"compute_id": "local", "compute_id": "local",
@ -651,7 +649,7 @@ def test_qemu_template_create(http_controller):
"ram": 512, "ram": 512,
"template_type": "qemu"} "template_type": "qemu"}
response = http_controller.post("/templates", params, example=True) response = await controller_api.post("/templates", params)
assert response.status == 201 assert response.status == 201
assert response.json["template_id"] is not None assert response.json["template_id"] is not None
@ -701,14 +699,14 @@ def test_qemu_template_create(http_controller):
assert response.json.get(item) == value assert response.json.get(item) == value
def test_vmware_template_create(http_controller): async def test_vmware_template_create(controller_api):
params = {"name": "VMware template", params = {"name": "VMware template",
"compute_id": "local", "compute_id": "local",
"template_type": "vmware", "template_type": "vmware",
"vmx_path": "/path/to/vm.vmx"} "vmx_path": "/path/to/vm.vmx"}
response = http_controller.post("/templates", params) response = await controller_api.post("/templates", params)
assert response.status == 201 assert response.status == 201
assert response.json["template_id"] is not None assert response.json["template_id"] is not None
@ -737,14 +735,14 @@ def test_vmware_template_create(http_controller):
assert response.json.get(item) == value assert response.json.get(item) == value
def test_virtualbox_template_create(http_controller): async def test_virtualbox_template_create(controller_api):
params = {"name": "VirtualBox template", params = {"name": "VirtualBox template",
"compute_id": "local", "compute_id": "local",
"template_type": "virtualbox", "template_type": "virtualbox",
"vmname": "My VirtualBox VM"} "vmname": "My VirtualBox VM"}
response = http_controller.post("/templates", params) response = await controller_api.post("/templates", params)
assert response.status == 201 assert response.status == 201
assert response.json["template_id"] is not None assert response.json["template_id"] is not None
@ -773,13 +771,14 @@ def test_virtualbox_template_create(http_controller):
for item, value in expected_response.items(): for item, value in expected_response.items():
assert response.json.get(item) == value assert response.json.get(item) == value
def test_vpcs_template_create(http_controller):
async def test_vpcs_template_create(controller_api):
params = {"name": "VPCS template", params = {"name": "VPCS template",
"compute_id": "local", "compute_id": "local",
"template_type": "vpcs"} "template_type": "vpcs"}
response = http_controller.post("/templates", params) response = await controller_api.post("/templates", params)
assert response.status == 201 assert response.status == 201
assert response.json["template_id"] is not None assert response.json["template_id"] is not None
@ -797,13 +796,14 @@ def test_vpcs_template_create(http_controller):
for item, value in expected_response.items(): for item, value in expected_response.items():
assert response.json.get(item) == value assert response.json.get(item) == value
def test_ethernet_switch_template_create(http_controller):
async def test_ethernet_switch_template_create(controller_api):
params = {"name": "Ethernet switch template", params = {"name": "Ethernet switch template",
"compute_id": "local", "compute_id": "local",
"template_type": "ethernet_switch"} "template_type": "ethernet_switch"}
response = http_controller.post("/templates", params) response = await controller_api.post("/templates", params)
assert response.status == 201 assert response.status == 201
assert response.json["template_id"] is not None assert response.json["template_id"] is not None
@ -868,13 +868,13 @@ def test_ethernet_switch_template_create(http_controller):
assert response.json.get(item) == value assert response.json.get(item) == value
def test_cloud_template_create(http_controller): async def test_cloud_template_create(controller_api):
params = {"name": "Cloud template", params = {"name": "Cloud template",
"compute_id": "local", "compute_id": "local",
"template_type": "cloud"} "template_type": "cloud"}
response = http_controller.post("/templates", params) response = await controller_api.post("/templates", params)
assert response.status == 201 assert response.status == 201
assert response.json["template_id"] is not None assert response.json["template_id"] is not None
@ -890,13 +890,13 @@ def test_cloud_template_create(http_controller):
assert response.json.get(item) == value assert response.json.get(item) == value
def test_ethernet_hub_template_create(http_controller): async def test_ethernet_hub_template_create(controller_api):
params = {"name": "Ethernet hub template", params = {"name": "Ethernet hub template",
"compute_id": "local", "compute_id": "local",
"template_type": "ethernet_hub"} "template_type": "ethernet_hub"}
response = http_controller.post("/templates", params) response = await controller_api.post("/templates", params)
assert response.status == 201 assert response.status == 201
assert response.json["template_id"] is not None assert response.json["template_id"] is not None
@ -936,21 +936,7 @@ def test_ethernet_hub_template_create(http_controller):
assert response.json.get(item) == value assert response.json.get(item) == value
@pytest.fixture async def test_create_node_from_template(controller_api, controller, project):
def compute(http_controller, async_run):
compute = MagicMock()
compute.id = "example.com"
compute.host = "example.org"
Controller.instance()._computes = {"example.com": compute}
return compute
@pytest.fixture
def project(http_controller, async_run):
return async_run(Controller.instance().add_project(name="Test"))
def test_create_node_from_template(http_controller, controller, project, compute):
id = str(uuid.uuid4()) id = str(uuid.uuid4())
controller.template_manager._templates = {id: Template(id, { controller.template_manager._templates = {id: Template(id, {
@ -962,7 +948,7 @@ def test_create_node_from_template(http_controller, controller, project, compute
"compute_id": "example.com" "compute_id": "example.com"
})} })}
with asyncio_patch("gns3server.controller.project.Project.add_node_from_template", return_value={"name": "test", "node_type": "qemu", "compute_id": "example.com"}) as mock: with asyncio_patch("gns3server.controller.project.Project.add_node_from_template", return_value={"name": "test", "node_type": "qemu", "compute_id": "example.com"}) as mock:
response = http_controller.post("/projects/{}/templates/{}".format(project.id, id), { response = await controller_api.post("/projects/{}/templates/{}".format(project.id, id), {
"x": 42, "x": 42,
"y": 12 "y": 12
}) })

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -15,48 +15,44 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
This test suite check /version endpoint
It's also used for unittest the HTTP implementation.
"""
from gns3server.config import Config
from gns3server.version import __version__ from gns3server.version import __version__
def test_version_output(http_controller): async def test_version_output(controller_api, config):
config = Config.instance()
config.set("Server", "local", "true")
response = http_controller.get('/version', example=True) config.set("Server", "local", "true")
response = await controller_api.get('/version')
assert response.status == 200 assert response.status == 200
assert response.json == {'local': True, 'version': __version__} assert response.json == {'local': True, 'version': __version__}
def test_version_input(http_controller): async def test_version_input(controller_api):
query = {'version': __version__}
response = http_controller.post('/version', query, example=True) params = {'version': __version__}
response = await controller_api.post('/version', params)
assert response.status == 200 assert response.status == 200
assert response.json == {'version': __version__} assert response.json == {'version': __version__}
def test_version_invalid_input(http_controller): async def test_version_invalid_input(controller_api):
query = {'version': "0.4.2"}
response = http_controller.post('/version', query)
assert response.status == 409
params = {'version': "0.4.2"}
response = await controller_api.post('/version', params)
assert response.status == 409
assert response.json == {'message': 'Client version 0.4.2 is not the same as server version {}'.format(__version__), assert response.json == {'message': 'Client version 0.4.2 is not the same as server version {}'.format(__version__),
'status': 409} 'status': 409}
def test_version_invalid_input_schema(http_controller): async def test_version_invalid_input_schema(controller_api):
query = {'version': "0.4.2", "bla": "blu"}
response = http_controller.post('/version', query) params = {'version': "0.4.2", "bla": "blu"}
response = await controller_api.post('/version', params)
assert response.status == 400 assert response.status == 400
def test_version_invalid_json(http_controller): async def test_version_invalid_json(controller_api):
query = "BOUM"
response = http_controller.post('/version', query, raw=True) params = "BOUM"
response = await controller_api.post('/version', params, raw=True)
assert response.status == 400 assert response.status == 400

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -25,60 +25,68 @@ from gns3server.utils.get_resource import get_resource
def get_static(filename): def get_static(filename):
current_dir = os.path.dirname(os.path.abspath(__file__)) current_dir = os.path.dirname(os.path.abspath(__file__))
return os.path.join(os.path.abspath(os.path.join(current_dir, '..', '..', 'gns3server', 'static')), filename) return os.path.join(os.path.abspath(os.path.join(current_dir, '..', '..', 'gns3server', 'static')), filename)
def test_debug(http_root): async def test_debug(http_client):
response = http_root.get('/debug')
response = await http_client.get('/debug')
assert response.status == 200 assert response.status == 200
html = response.html html = await response.text()
assert "Website" in html assert "Website" in html
assert __version__ in html assert __version__ in html
def test_controller(http_root, async_run): async def test_controller(http_client, controller):
project = async_run(Controller.instance().add_project(name="test"))
response = http_root.get('/controller') await controller.add_project(name="test")
assert "test" in response.html response = await http_client.get('/controller')
assert "test" in await response.text()
assert response.status == 200 assert response.status == 200
def test_compute(http_root): async def test_compute(http_client):
response = http_root.get('/compute')
response = await http_client.get('/compute')
assert response.status == 200 assert response.status == 200
def test_project(http_root, async_run): async def test_project(http_client, controller):
project = async_run(Controller.instance().add_project(name="test"))
response = http_root.get('/projects/{}'.format(project.id)) project = await controller.add_project(name="test")
response = await http_client.get('/projects/{}'.format(project.id))
assert response.status == 200 assert response.status == 200
def test_web_ui(http_root, tmpdir): async def test_web_ui(http_client, tmpdir):
with patch('gns3server.utils.get_resource.get_resource') as mock: with patch('gns3server.utils.get_resource.get_resource') as mock:
mock.return_value = str(tmpdir) mock.return_value = str(tmpdir)
os.makedirs(str(tmpdir / 'web-ui')) os.makedirs(str(tmpdir / 'web-ui'))
tmpfile = get_static('web-ui/testing.txt') tmpfile = get_static('web-ui/testing.txt')
with open(tmpfile, 'w+') as f: with open(tmpfile, 'w+') as f:
f.write('world') f.write('world')
response = http_root.get('/static/web-ui/testing.txt') response = await http_client.get('/static/web-ui/testing.txt')
assert response.status == 200 assert response.status == 200
os.remove(get_static('web-ui/testing.txt')) os.remove(get_static('web-ui/testing.txt'))
def test_web_ui_not_found(http_root, tmpdir): async def test_web_ui_not_found(http_client, tmpdir):
with patch('gns3server.utils.get_resource.get_resource') as mock: with patch('gns3server.utils.get_resource.get_resource') as mock:
mock.return_value = str(tmpdir) mock.return_value = str(tmpdir)
response = http_root.get('/static/web-ui/not-found.txt') response = await http_client.get('/static/web-ui/not-found.txt')
# should serve web-ui/index.html # should serve web-ui/index.html
assert response.status == 200 assert response.status == 200
def test_v1(http_root): async def test_v1(http_client):
""" """
The old api v1 raise a 429 The old API v1 raises a 429
""" """
response = http_root.get('/v1/version')
response = await http_client.get('/v1/version')
assert response.status == 200 assert response.status == 200

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -17,8 +17,6 @@
import configparser import configparser
import time
import os
from gns3server.config import Config from gns3server.config import Config
@ -73,6 +71,7 @@ def test_set_section_config(tmpdir):
"local": "false" "local": "false"
} }
}) })
assert dict(config.get_section_config("Server")) == {"host": "127.0.0.1", "local": "false"} assert dict(config.get_section_config("Server")) == {"host": "127.0.0.1", "local": "false"}
config.set_section_config("Server", {"host": "192.168.1.1", "local": True}) config.set_section_config("Server", {"host": "192.168.1.1", "local": True})
assert dict(config.get_section_config("Server")) == {"host": "192.168.1.1", "local": "true"} assert dict(config.get_section_config("Server")) == {"host": "192.168.1.1", "local": "true"}
@ -85,6 +84,7 @@ def test_set(tmpdir):
"host": "127.0.0.1" "host": "127.0.0.1"
} }
}) })
assert dict(config.get_section_config("Server")) == {"host": "127.0.0.1"} assert dict(config.get_section_config("Server")) == {"host": "127.0.0.1"}
config.set("Server", "host", "192.168.1.1") config.set("Server", "host", "192.168.1.1")
assert dict(config.get_section_config("Server")) == {"host": "192.168.1.1"} assert dict(config.get_section_config("Server")) == {"host": "192.168.1.1"}
@ -97,12 +97,12 @@ def test_reload(tmpdir):
"host": "127.0.0.1" "host": "127.0.0.1"
} }
}) })
assert dict(config.get_section_config("Server")) == {"host": "127.0.0.1"}
assert dict(config.get_section_config("Server")) == {"host": "127.0.0.1"}
config.set_section_config("Server", {"host": "192.168.1.1"}) config.set_section_config("Server", {"host": "192.168.1.1"})
assert dict(config.get_section_config("Server")) == {"host": "192.168.1.1"} assert dict(config.get_section_config("Server")) == {"host": "192.168.1.1"}
path = write_config(tmpdir, { write_config(tmpdir, {
"Server": { "Server": {
"host": "192.168.1.2" "host": "192.168.1.2"
} }

@ -15,7 +15,6 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys
from gns3server.utils import * from gns3server.utils import *
@ -23,8 +22,8 @@ def test_force_unix_path():
assert force_unix_path("a/b") == "a/b" assert force_unix_path("a/b") == "a/b"
assert force_unix_path("a\\b") == "a/b" assert force_unix_path("a\\b") == "a/b"
assert force_unix_path("a\\b\\..\\c") == "a/c" assert force_unix_path("a\\b\\..\\c") == "a/c"
assert force_unix_path("C:\Temp") == "C:/Temp" assert force_unix_path(r"C:\Temp") == r"C:/Temp"
assert force_unix_path(force_unix_path("C:\Temp")) == "C:/Temp" assert force_unix_path(force_unix_path(r"C:\Temp")) == r"C:/Temp"
assert force_unix_path("a//b") == "a/b" assert force_unix_path("a//b") == "a/b"

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -34,13 +34,13 @@ class _asyncio_patch:
""" """
def __init__(self, function, *args, **kwargs): def __init__(self, function, *args, **kwargs):
self.function = function
self.args = args self._function = function
self.kwargs = kwargs self._aync_magic_mock = AsyncioMagicMock(*args, **kwargs)
def __enter__(self): def __enter__(self):
"""Used when enter in the with block""" """Used when enter in the with block"""
self._patcher = unittest.mock.patch(self.function, return_value=self._fake_anwser()) self._patcher = unittest.mock.patch(self._function, new=self._aync_magic_mock)
mock_class = self._patcher.start() mock_class = self._patcher.start()
return mock_class return mock_class
@ -48,19 +48,6 @@ class _asyncio_patch:
"""Used when leaving the with block""" """Used when leaving the with block"""
self._patcher.stop() self._patcher.stop()
def _fake_anwser(self):
future = asyncio.Future()
if "return_value" in self.kwargs:
future.set_result(self.kwargs["return_value"])
elif "side_effect" in self.kwargs:
if isinstance(self.kwargs["side_effect"], Exception):
future.set_exception(self.kwargs["side_effect"])
else:
raise NotImplementedError
else:
future.set_result(True)
return future
def asyncio_patch(function, *args, **kwargs): def asyncio_patch(function, *args, **kwargs):
return _asyncio_patch(function, *args, **kwargs) return _asyncio_patch(function, *args, **kwargs)
@ -77,12 +64,13 @@ class AsyncioMagicMock(unittest.mock.MagicMock):
""" """
Magic mock returning coroutine Magic mock returning coroutine
""" """
try: try:
__class__ = types.CoroutineType __class__ = types.CoroutineType
except AttributeError: # Not supported with Python 3.4 except AttributeError: # Not supported with Python 3.4
__class__ = types.GeneratorType __class__ = types.GeneratorType
def __init__(self, return_value=None, return_values=None, **kwargs): def __init__(self, return_value=None, **kwargs):
""" """
:return_values: Array of return value at each call will return the next :return_values: Array of return value at each call will return the next
""" """

@ -25,53 +25,33 @@ from gns3server.utils.asyncio import wait_run_in_executor, subprocess_check_outp
from tests.utils import AsyncioMagicMock from tests.utils import AsyncioMagicMock
def test_wait_run_in_executor(loop): async def test_wait_run_in_executor():
def change_var(param): def change_var(param):
return param return param
exec = wait_run_in_executor(change_var, "test") result = await wait_run_in_executor(change_var, "test")
result = loop.run_until_complete(asyncio.ensure_future(exec))
assert result == "test" assert result == "test"
def test_exception_wait_run_in_executor(loop): async def test_exception_wait_run_in_executor():
def raise_exception(): def raise_exception():
raise Exception("test") raise Exception("test")
exec = wait_run_in_executor(raise_exception)
with pytest.raises(Exception): with pytest.raises(Exception):
result = loop.run_until_complete(asyncio.ensure_future(exec)) await wait_run_in_executor(raise_exception)
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") @pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
def test_subprocess_check_output(loop, tmpdir, restore_original_path): async def test_subprocess_check_output(loop, tmpdir):
path = str(tmpdir / "test") path = str(tmpdir / "test")
exec = subprocess_check_output("echo", "-n", path) result = await subprocess_check_output("echo", "-n", path)
result = loop.run_until_complete(asyncio.ensure_future(exec))
assert result == path assert result == path
def test_wait_for_process_termination(loop): async def test_lock_decorator():
if sys.version_info >= (3, 5):
# No need for test we use native version
return
process = MagicMock()
process.returncode = 0
exec = wait_for_process_termination(process)
loop.run_until_complete(asyncio.ensure_future(exec))
process = MagicMock()
process.returncode = None
exec = wait_for_process_termination(process, timeout=0.5)
with pytest.raises(asyncio.TimeoutError):
loop.run_until_complete(asyncio.ensure_future(exec))
def test_lock_decorator(loop):
""" """
The test check if the the second call to method_to_lock wait for the The test check if the the second call to method_to_lock wait for the
first call to finish first call to finish
@ -84,11 +64,11 @@ def test_lock_decorator(loop):
@locking @locking
async def method_to_lock(self): async def method_to_lock(self):
res = self._test_val result = self._test_val
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
self._test_val += 1 self._test_val += 1
return res return result
i = TestLock() i = TestLock()
res = set(loop.run_until_complete(asyncio.gather(i.method_to_lock(), i.method_to_lock()))) res = set(await asyncio.gather(i.method_to_lock(), i.method_to_lock()))
assert res == set((0, 1,)) # We use a set to test this to avoid order issue assert res == set((0, 1,)) # We use a set to test this to avoid order issue

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2016 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -24,41 +24,42 @@ from gns3server.utils.file_watcher import FileWatcher
@pytest.mark.parametrize("strategy", ['mtime', 'hash']) @pytest.mark.parametrize("strategy", ['mtime', 'hash'])
def test_file_watcher(async_run, tmpdir, strategy): async def test_file_watcher(tmpdir, strategy):
file = tmpdir / "test" file = tmpdir / "test"
file.write("a") file.write("a")
callback = MagicMock() callback = MagicMock()
fw = FileWatcher(file, callback, delay=0.5, strategy=strategy) FileWatcher(file, callback, delay=0.1, strategy=strategy)
async_run(asyncio.sleep(1)) await asyncio.sleep(0.5)
assert not callback.called assert not callback.called
file.write("b") file.write("b")
async_run(asyncio.sleep(1.5)) await asyncio.sleep(0.5)
callback.assert_called_with(str(file)) callback.assert_called_with(str(file))
@pytest.mark.parametrize("strategy", ['mtime', 'hash']) @pytest.mark.parametrize("strategy", ['mtime', 'hash'])
def test_file_watcher_not_existing(async_run, tmpdir, strategy): async def test_file_watcher_not_existing(tmpdir, strategy):
file = tmpdir / "test" file = tmpdir / "test"
callback = MagicMock() callback = MagicMock()
fw = FileWatcher(file, callback, delay=0.5, strategy=strategy) FileWatcher(file, callback, delay=0.1, strategy=strategy)
async_run(asyncio.sleep(1)) await asyncio.sleep(0.5)
assert not callback.called assert not callback.called
file.write("b") file.write("b")
async_run(asyncio.sleep(1.5)) await asyncio.sleep(0.5)
callback.assert_called_with(str(file)) callback.assert_called_with(str(file))
@pytest.mark.parametrize("strategy", ['mtime', 'hash']) @pytest.mark.parametrize("strategy", ['mtime', 'hash'])
def test_file_watcher_list(async_run, tmpdir, strategy): async def test_file_watcher_list(tmpdir, strategy):
file = tmpdir / "test" file = tmpdir / "test"
file.write("a") file.write("a")
file2 = tmpdir / "test2" file2 = tmpdir / "test2"
callback = MagicMock() callback = MagicMock()
fw = FileWatcher([file, file2], callback, delay=0.5, strategy=strategy) FileWatcher([file, file2], callback, delay=0.1, strategy=strategy)
async_run(asyncio.sleep(1)) await asyncio.sleep(0.5)
assert not callback.called assert not callback.called
file2.write("b") file2.write("b")
async_run(asyncio.sleep(1.5)) await asyncio.sleep(0.5)
callback.assert_called_with(str(file2)) callback.assert_called_with(str(file2))

@ -26,6 +26,7 @@ from gns3server.utils.images import md5sum, remove_checksum, images_directories,
def test_images_directories(tmpdir): def test_images_directories(tmpdir):
path1 = tmpdir / "images1" / "QEMU" / "test1.bin" path1 = tmpdir / "images1" / "QEMU" / "test1.bin"
path1.write("1", ensure=True) path1.write("1", ensure=True)
path1 = force_unix_path(str(path1)) path1 = force_unix_path(str(path1))
@ -48,6 +49,7 @@ def test_images_directories(tmpdir):
def test_md5sum(tmpdir): def test_md5sum(tmpdir):
fake_img = str(tmpdir / 'hello载') fake_img = str(tmpdir / 'hello载')
with open(fake_img, 'w+') as f: with open(fake_img, 'w+') as f:
@ -59,6 +61,7 @@ def test_md5sum(tmpdir):
def test_md5sum_stopped_event(tmpdir): def test_md5sum_stopped_event(tmpdir):
fake_img = str(tmpdir / 'hello_stopped') fake_img = str(tmpdir / 'hello_stopped')
with open(fake_img, 'w+') as f: with open(fake_img, 'w+') as f:
f.write('hello') f.write('hello')
@ -71,6 +74,7 @@ def test_md5sum_stopped_event(tmpdir):
def test_md5sum_existing_digest(tmpdir): def test_md5sum_existing_digest(tmpdir):
fake_img = str(tmpdir / 'hello') fake_img = str(tmpdir / 'hello')
with open(fake_img, 'w+') as f: with open(fake_img, 'w+') as f:
@ -83,6 +87,7 @@ def test_md5sum_existing_digest(tmpdir):
def test_md5sum_existing_digest_but_missing_image(tmpdir): def test_md5sum_existing_digest_but_missing_image(tmpdir):
fake_img = str(tmpdir / 'hello') fake_img = str(tmpdir / 'hello')
with open(str(tmpdir / 'hello.md5sum'), 'w+') as f: with open(str(tmpdir / 'hello.md5sum'), 'w+') as f:
@ -92,6 +97,7 @@ def test_md5sum_existing_digest_but_missing_image(tmpdir):
def test_md5sum_none(tmpdir): def test_md5sum_none(tmpdir):
assert md5sum(None) is None assert md5sum(None) is None
@ -107,6 +113,7 @@ def test_remove_checksum(tmpdir):
def test_list_images(tmpdir): def test_list_images(tmpdir):
path1 = tmpdir / "images1" / "IOS" / "test1.image" path1 = tmpdir / "images1" / "IOS" / "test1.image"
path1.write(b'\x7fELF\x01\x02\x01', ensure=True) path1.write(b'\x7fELF\x01\x02\x01', ensure=True)
path1 = force_unix_path(str(path1)) path1 = force_unix_path(str(path1))

@ -21,6 +21,7 @@ from gns3server.utils.interfaces import interfaces, is_interface_up, has_netmask
def test_interfaces(): def test_interfaces():
# This test should pass on all platforms without crash # This test should pass on all platforms without crash
interface_list = interfaces() interface_list = interfaces()
assert isinstance(interface_list, list) assert isinstance(interface_list, list)
@ -39,6 +40,7 @@ def test_interfaces():
def test_has_netmask(): def test_has_netmask():
if sys.platform.startswith("win"): if sys.platform.startswith("win"):
# No loopback # No loopback
pass pass
@ -49,6 +51,7 @@ def test_has_netmask():
def test_is_interface_up(): def test_is_interface_up():
if sys.platform.startswith("win"): if sys.platform.startswith("win"):
# is_interface_up() always returns True on Windows # is_interface_up() always returns True on Windows
pass pass

@ -24,6 +24,7 @@ from gns3server.utils.path import check_path_allowed, get_default_project_direct
def test_check_path_allowed(config, tmpdir): def test_check_path_allowed(config, tmpdir):
config.set("Server", "local", False) config.set("Server", "local", False)
config.set("Server", "projects_path", str(tmpdir)) config.set("Server", "projects_path", str(tmpdir))
with pytest.raises(aiohttp.web.HTTPForbidden): with pytest.raises(aiohttp.web.HTTPForbidden):
@ -37,7 +38,6 @@ def test_check_path_allowed(config, tmpdir):
def test_get_default_project_directory(config): def test_get_default_project_directory(config):
config.clear() config.clear()
path = os.path.normpath(os.path.expanduser("~/GNS3/projects")) path = os.path.normpath(os.path.expanduser("~/GNS3/projects"))
assert get_default_project_directory() == path assert get_default_project_directory() == path
assert os.path.exists(path) assert os.path.exists(path)

@ -20,6 +20,7 @@ from gns3server.utils.picture import get_size
def test_get_size(): def test_get_size():
with open("tests/resources/nvram_iou", "rb") as f: with open("tests/resources/nvram_iou", "rb") as f:
res = get_size(f.read(), default_width=100, default_height=50) res = get_size(f.read(), default_width=100, default_height=50)
assert res == (100, 50, None) assert res == (100, 50, None)

@ -1,42 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015 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 <http://www.gnu.org/licenses/>.
import os
from gns3server.web.documentation import Documentation
from gns3server.handlers import *
from gns3server.web.route import Route
def test_documentation_write(tmpdir):
os.makedirs(str(tmpdir / "api/examples"))
with open(str(tmpdir / "api/examples/compute_post_projectsprojectidvirtualboxnodes.txt"), "w+") as f:
f.write("curl test")
Documentation(Route, str(tmpdir)).write()
assert os.path.exists(str(tmpdir / "api"))
assert os.path.exists(str(tmpdir / "api" / "v2" / "compute"))
assert os.path.exists(str(tmpdir / "api" / "v2" / "compute" / "virtualbox.rst"))
assert os.path.exists(str(tmpdir / "api" / "v2" / "compute" / "virtualbox"))
assert os.path.exists(str(tmpdir / "api" / "v2" / "compute" / "virtualbox" / "virtualboxvms.rst"))
with open(str(tmpdir / "api" / "v2" / "compute" / "virtualbox" / "projectsprojectidvirtualboxnodes.rst")) as f:
content = f.read()
assert "Sample session" in content
assert "literalinclude:: ../../../examples/compute_post_projectsprojectidvirtualboxnodes.txt" in content
assert os.path.exists(str(tmpdir / "api" / "v2" / "controller" / "compute.rst"))

@ -17,7 +17,7 @@
import pytest import pytest
from unittest.mock import MagicMock from tests.utils import AsyncioMagicMock
from aiohttp.web import HTTPNotFound from aiohttp.web import HTTPNotFound
from gns3server.web.response import Response from gns3server.web.response import Response
@ -25,20 +25,22 @@ from gns3server.web.response import Response
@pytest.fixture() @pytest.fixture()
def response(): def response():
request = MagicMock() request = AsyncioMagicMock()
return Response(request=request) return Response(request=request)
def test_response_file(async_run, tmpdir, response): async def test_response_file(tmpdir, response):
filename = str(tmpdir / 'hello') filename = str(tmpdir / 'hello')
with open(filename, 'w+') as f: with open(filename, 'w+') as f:
f.write('world') f.write('world')
async_run(response.stream_file(filename)) await response.stream_file(filename)
assert response.status == 200 assert response.status == 200
def test_response_file_not_found(async_run, tmpdir, response): async def test_response_file_not_found(loop, tmpdir, response):
filename = str(tmpdir / 'hello-not-found')
pytest.raises(HTTPNotFound, lambda: async_run(response.stream_file(filename))) filename = str(tmpdir / 'hello-not-found')
with pytest.raises(HTTPNotFound):
await response.stream_file(filename)

Loading…
Cancel
Save