mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-03 05:51:15 +02:00
Rename modules => hypervisor
This commit is contained in:
145 files changed
+308
-307
No files matched your search
@@ -0,0 +1,136 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# 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 pytest
|
||||
import asyncio
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from tests.utils import asyncio_patch
|
||||
from gns3server.hypervisor.docker import Docker
|
||||
from gns3server.hypervisor.docker.docker_error import DockerError
|
||||
|
||||
|
||||
def test_query_success(loop):
|
||||
|
||||
vm = Docker()
|
||||
vm._connector = MagicMock()
|
||||
vm._connected = True
|
||||
response = MagicMock()
|
||||
response.status = 200
|
||||
response.headers = {'CONTENT-TYPE': 'application/json'}
|
||||
|
||||
@asyncio.coroutine
|
||||
def read():
|
||||
return b'{"c": false}'
|
||||
|
||||
response.read.side_effect = read
|
||||
with asyncio_patch("aiohttp.request", return_value=response) as mock:
|
||||
data = loop.run_until_complete(asyncio.async(vm.query("POST", "test", data={"a": True}, params={"b": 1})))
|
||||
mock.assert_called_with('POST',
|
||||
'http://docker/test',
|
||||
connector=vm._connector,
|
||||
data='{"a": true}',
|
||||
headers={'content-type': 'application/json'},
|
||||
params={'b': 1})
|
||||
|
||||
assert data == {"c": False}
|
||||
|
||||
|
||||
def test_query_error(loop):
|
||||
|
||||
vm = Docker()
|
||||
vm._connected = True
|
||||
vm._connector = MagicMock()
|
||||
response = MagicMock()
|
||||
response.status = 404
|
||||
|
||||
@asyncio.coroutine
|
||||
def read():
|
||||
return b"NOT FOUND"
|
||||
|
||||
response.read.side_effect = read
|
||||
with asyncio_patch("aiohttp.request", return_value=response) as mock:
|
||||
with pytest.raises(DockerError):
|
||||
data = loop.run_until_complete(asyncio.async(vm.query("POST", "test", data={"a": True}, params={"b": 1})))
|
||||
mock.assert_called_with('POST',
|
||||
'http://docker/test',
|
||||
connector=vm._connector,
|
||||
data='{"a": true}',
|
||||
headers={'content-type': 'application/json'},
|
||||
params={'b': 1})
|
||||
|
||||
|
||||
def test_query_error_json(loop):
|
||||
|
||||
vm = Docker()
|
||||
vm._connected = True
|
||||
vm._connector = MagicMock()
|
||||
response = MagicMock()
|
||||
response.status = 404
|
||||
|
||||
@asyncio.coroutine
|
||||
def read():
|
||||
return b'{"message": "Error"}'
|
||||
|
||||
response.read.side_effect = read
|
||||
with asyncio_patch("aiohttp.request", return_value=response) as mock:
|
||||
with pytest.raises(DockerError):
|
||||
data = loop.run_until_complete(asyncio.async(vm.query("POST", "test", data={"a": True}, params={"b": 1})))
|
||||
mock.assert_called_with('POST',
|
||||
'http://docker/test',
|
||||
connector=vm._connector,
|
||||
data='{"a": true}',
|
||||
headers={'content-type': 'application/json'},
|
||||
params={'b': 1})
|
||||
|
||||
|
||||
def test_list_images(loop):
|
||||
response = [
|
||||
{
|
||||
"RepoTags": [
|
||||
"ubuntu:12.04",
|
||||
"ubuntu:precise",
|
||||
"ubuntu:latest"
|
||||
],
|
||||
"Id": "8dbd9e392a964056420e5d58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c",
|
||||
"Created": 1365714795,
|
||||
"Size": 131506275,
|
||||
"VirtualSize": 131506275
|
||||
},
|
||||
{
|
||||
"RepoTags": [
|
||||
"ubuntu:12.10",
|
||||
"ubuntu:quantal",
|
||||
"<none>:<none>"
|
||||
],
|
||||
"ParentId": "27cf784147099545",
|
||||
"Id": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
|
||||
"Created": 1364102658,
|
||||
"Size": 24653,
|
||||
"VirtualSize": 180116135
|
||||
}
|
||||
]
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.query", return_value=response) as mock:
|
||||
images = loop.run_until_complete(asyncio.async(Docker.instance().list_images()))
|
||||
mock.assert_called_with("GET", "images/json", params={"all": 0})
|
||||
assert len(images) == 5
|
||||
assert {"image": "ubuntu:12.04"} in images
|
||||
assert {"image": "ubuntu:precise"} in images
|
||||
assert {"image": "ubuntu:latest"} in images
|
||||
assert {"image": "ubuntu:12.10"} in images
|
||||
assert {"image": "ubuntu:quantal"} in images
|
||||
@@ -0,0 +1,784 @@
|
||||
# -*- 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 pytest
|
||||
import uuid
|
||||
import asyncio
|
||||
import os
|
||||
from tests.utils import asyncio_patch, AsyncioMagicMock
|
||||
|
||||
from gns3server.ubridge.ubridge_error import UbridgeNamespaceError
|
||||
from gns3server.hypervisor.docker.docker_vm import DockerVM
|
||||
from gns3server.hypervisor.docker.docker_error import *
|
||||
from gns3server.hypervisor.docker import Docker
|
||||
from gns3server.utils.get_resource import get_resource
|
||||
|
||||
|
||||
from unittest.mock import patch, MagicMock, PropertyMock, call
|
||||
from gns3server.config import Config
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def manager(port_manager):
|
||||
m = Docker.instance()
|
||||
m.port_manager = port_manager
|
||||
return m
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def vm(project, manager):
|
||||
vm = DockerVM("test", str(uuid.uuid4()), project, manager, "ubuntu")
|
||||
vm._cid = "e90e34656842"
|
||||
vm.allocate_aux = False
|
||||
return vm
|
||||
|
||||
|
||||
def test_json(vm, project):
|
||||
assert vm.__json__() == {
|
||||
'container_id': 'e90e34656842',
|
||||
'image': 'ubuntu',
|
||||
'name': 'test',
|
||||
'project_id': project.id,
|
||||
'vm_id': vm.id,
|
||||
'adapters': 1,
|
||||
'console': vm.console,
|
||||
'console_type': 'telnet',
|
||||
'aux': vm.aux,
|
||||
'start_command': vm.start_command,
|
||||
'environment': vm.environment,
|
||||
'vm_directory': vm.working_dir
|
||||
}
|
||||
|
||||
|
||||
def test_start_command(vm):
|
||||
|
||||
vm.start_command = "hello"
|
||||
assert vm.start_command == "hello"
|
||||
vm.start_command = " "
|
||||
assert vm.start_command is None
|
||||
|
||||
|
||||
def test_create(loop, project, manager):
|
||||
|
||||
response = {
|
||||
"Id": "e90e34656806",
|
||||
"Warnings": []
|
||||
}
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.list_images", return_value=[{"image": "ubuntu"}]) as mock_list_images:
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.query", return_value=response) as mock:
|
||||
vm = DockerVM("test", str(uuid.uuid4()), project, manager, "ubuntu")
|
||||
loop.run_until_complete(asyncio.async(vm.create()))
|
||||
mock.assert_called_with("POST", "containers/create", data={
|
||||
"Tty": True,
|
||||
"OpenStdin": True,
|
||||
"StdinOnce": False,
|
||||
"HostConfig":
|
||||
{
|
||||
"CapAdd": ["ALL"],
|
||||
"Binds": ["{}:/gns3:ro".format(get_resource("hypervisor/docker/resources"))],
|
||||
"Privileged": True
|
||||
},
|
||||
"Volumes": {},
|
||||
"NetworkDisabled": True,
|
||||
"Name": "test",
|
||||
"Hostname": "test",
|
||||
"Image": "ubuntu",
|
||||
"Env": [],
|
||||
"Cmd": ["/bin/sh", "/gns3/init.sh", "/bin/sh"]
|
||||
})
|
||||
assert vm._cid == "e90e34656806"
|
||||
|
||||
|
||||
def test_create_vnc(loop, project, manager):
|
||||
|
||||
response = {
|
||||
"Id": "e90e34656806",
|
||||
"Warnings": []
|
||||
}
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.list_images", return_value=[{"image": "ubuntu"}]) as mock_list_images:
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.query", return_value=response) as mock:
|
||||
vm = DockerVM("test", str(uuid.uuid4()), project, manager, "ubuntu", console_type="vnc")
|
||||
vm._start_vnc = MagicMock()
|
||||
vm._display = 42
|
||||
loop.run_until_complete(asyncio.async(vm.create()))
|
||||
mock.assert_called_with("POST", "containers/create", data={
|
||||
"Tty": True,
|
||||
"OpenStdin": True,
|
||||
"StdinOnce": False,
|
||||
"HostConfig":
|
||||
{
|
||||
"CapAdd": ["ALL"],
|
||||
"Binds": [
|
||||
"{}:/gns3:ro".format(get_resource("hypervisor/docker/resources")),
|
||||
'/tmp/.X11-unix/:/tmp/.X11-unix/'
|
||||
],
|
||||
"Privileged": True
|
||||
},
|
||||
"Volumes": {},
|
||||
"NetworkDisabled": True,
|
||||
"Name": "test",
|
||||
"Hostname": "test",
|
||||
"Image": "ubuntu",
|
||||
"Env": ['DISPLAY=:42'],
|
||||
"Cmd": ["/bin/sh", "/gns3/init.sh", "/bin/sh"]
|
||||
})
|
||||
assert vm._start_vnc.called
|
||||
assert vm._cid == "e90e34656806"
|
||||
|
||||
|
||||
def test_create_start_cmd(loop, project, manager):
|
||||
|
||||
response = {
|
||||
"Id": "e90e34656806",
|
||||
"Warnings": []
|
||||
}
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.list_images", return_value=[{"image": "ubuntu"}]) as mock_list_images:
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.query", return_value=response) as mock:
|
||||
vm = DockerVM("test", str(uuid.uuid4()), project, manager, "ubuntu")
|
||||
vm._start_command = "/bin/ls"
|
||||
loop.run_until_complete(asyncio.async(vm.create()))
|
||||
mock.assert_called_with("POST", "containers/create", data={
|
||||
"Tty": True,
|
||||
"OpenStdin": True,
|
||||
"StdinOnce": False,
|
||||
"HostConfig":
|
||||
{
|
||||
"CapAdd": ["ALL"],
|
||||
"Binds": ["{}:/gns3:ro".format(get_resource("hypervisor/docker/resources"))],
|
||||
"Privileged": True
|
||||
},
|
||||
"Volumes": {},
|
||||
"Cmd": ["/bin/sh", "/gns3/init.sh", "/bin/ls"],
|
||||
"NetworkDisabled": True,
|
||||
"Name": "test",
|
||||
"Hostname": "test",
|
||||
"Image": "ubuntu",
|
||||
"Env": []
|
||||
})
|
||||
assert vm._cid == "e90e34656806"
|
||||
|
||||
|
||||
def test_create_environment(loop, project, manager):
|
||||
|
||||
response = {
|
||||
"Id": "e90e34656806",
|
||||
"Warnings": []
|
||||
}
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.list_images", return_value=[{"image": "ubuntu"}]) as mock_list_images:
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.query", return_value=response) as mock:
|
||||
vm = DockerVM("test", str(uuid.uuid4()), project, manager, "ubuntu")
|
||||
vm.environment = "YES=1\nNO=0"
|
||||
loop.run_until_complete(asyncio.async(vm.create()))
|
||||
mock.assert_called_with("POST", "containers/create", data={
|
||||
"Tty": True,
|
||||
"OpenStdin": True,
|
||||
"StdinOnce": False,
|
||||
"HostConfig":
|
||||
{
|
||||
"CapAdd": ["ALL"],
|
||||
"Binds": ["{}:/gns3:ro".format(get_resource("hypervisor/docker/resources"))],
|
||||
"Privileged": True
|
||||
},
|
||||
"Env": ["YES=1", "NO=0"],
|
||||
"Volumes": {},
|
||||
"NetworkDisabled": True,
|
||||
"Name": "test",
|
||||
"Hostname": "test",
|
||||
"Image": "ubuntu",
|
||||
"Cmd": ["/bin/sh", "/gns3/init.sh", "/bin/sh"]
|
||||
})
|
||||
assert vm._cid == "e90e34656806"
|
||||
|
||||
|
||||
def test_create_image_not_available(loop, project, manager):
|
||||
|
||||
call = 0
|
||||
|
||||
@asyncio.coroutine
|
||||
def informations():
|
||||
nonlocal call
|
||||
if call == 0:
|
||||
call += 1
|
||||
raise DockerHttp404Error("missing")
|
||||
else:
|
||||
return {}
|
||||
|
||||
response = {
|
||||
"Id": "e90e34656806",
|
||||
"Warnings": []
|
||||
}
|
||||
|
||||
vm = DockerVM("test", str(uuid.uuid4()), project, manager, "ubuntu")
|
||||
vm._get_image_informations = MagicMock()
|
||||
vm._get_image_informations.side_effect = informations
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.docker.DockerVM.pull_image", return_value=True) as mock_pull:
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.query", return_value=response) as mock:
|
||||
loop.run_until_complete(asyncio.async(vm.create()))
|
||||
mock.assert_called_with("POST", "containers/create", data={
|
||||
"Tty": True,
|
||||
"OpenStdin": True,
|
||||
"StdinOnce": False,
|
||||
"HostConfig":
|
||||
{
|
||||
"CapAdd": ["ALL"],
|
||||
"Binds": ["{}:/gns3:ro".format(get_resource("hypervisor/docker/resources"))],
|
||||
"Privileged": True
|
||||
},
|
||||
"Volumes": {},
|
||||
"NetworkDisabled": True,
|
||||
"Name": "test",
|
||||
"Hostname": "test",
|
||||
"Image": "ubuntu",
|
||||
"Env": [],
|
||||
"Cmd": ["/bin/sh", "/gns3/init.sh", "/bin/sh"]
|
||||
})
|
||||
assert vm._cid == "e90e34656806"
|
||||
mock_pull.assert_called_with("ubuntu")
|
||||
|
||||
|
||||
def test_get_container_state(loop, vm):
|
||||
response = {
|
||||
"State": {
|
||||
"Error": "",
|
||||
"ExitCode": 9,
|
||||
"FinishedAt": "2015-01-06T15:47:32.080254511Z",
|
||||
"OOMKilled": False,
|
||||
"Paused": False,
|
||||
"Pid": 0,
|
||||
"Restarting": False,
|
||||
"Running": True,
|
||||
"StartedAt": "2015-01-06T15:47:32.072697474Z"
|
||||
}
|
||||
}
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.query", return_value=response) as mock:
|
||||
assert loop.run_until_complete(asyncio.async(vm._get_container_state())) == "running"
|
||||
|
||||
response["State"]["Running"] = False
|
||||
response["State"]["Paused"] = True
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.query", return_value=response) as mock:
|
||||
assert loop.run_until_complete(asyncio.async(vm._get_container_state())) == "paused"
|
||||
|
||||
response["State"]["Running"] = False
|
||||
response["State"]["Paused"] = False
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.query", return_value=response) as mock:
|
||||
assert loop.run_until_complete(asyncio.async(vm._get_container_state())) == "exited"
|
||||
|
||||
|
||||
def test_is_running(loop, vm):
|
||||
response = {
|
||||
"State": {
|
||||
"Running": False,
|
||||
"Paused": False
|
||||
}
|
||||
}
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.query", return_value=response) as mock:
|
||||
assert loop.run_until_complete(asyncio.async(vm.is_running())) is False
|
||||
|
||||
response["State"]["Running"] = True
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.query", return_value=response) as mock:
|
||||
assert loop.run_until_complete(asyncio.async(vm.is_running())) is True
|
||||
|
||||
|
||||
def test_pause(loop, vm):
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.query") as mock:
|
||||
loop.run_until_complete(asyncio.async(vm.pause()))
|
||||
|
||||
mock.assert_called_with("POST", "containers/e90e34656842/pause")
|
||||
assert vm.status == "paused"
|
||||
|
||||
|
||||
def test_unpause(loop, vm):
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.query") as mock:
|
||||
loop.run_until_complete(asyncio.async(vm.unpause()))
|
||||
|
||||
mock.assert_called_with("POST", "containers/e90e34656842/unpause")
|
||||
|
||||
|
||||
def test_start(loop, vm, manager, free_console_port):
|
||||
|
||||
assert vm.status != "started"
|
||||
vm.adapters = 1
|
||||
|
||||
vm.allocate_aux = True
|
||||
vm._start_aux = AsyncioMagicMock()
|
||||
|
||||
vm._get_container_state = AsyncioMagicMock(return_value="stopped")
|
||||
vm._start_ubridge = AsyncioMagicMock()
|
||||
vm._get_namespace = AsyncioMagicMock(return_value=42)
|
||||
vm._add_ubridge_connection = AsyncioMagicMock()
|
||||
vm._start_console = AsyncioMagicMock()
|
||||
|
||||
nio = manager.create_nio(0, {"type": "nio_udp", "lport": free_console_port, "rport": free_console_port, "rhost": "127.0.0.1"})
|
||||
loop.run_until_complete(asyncio.async(vm.adapter_add_nio_binding(0, nio)))
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.query") as mock_query:
|
||||
loop.run_until_complete(asyncio.async(vm.start()))
|
||||
|
||||
mock_query.assert_called_with("POST", "containers/e90e34656842/start")
|
||||
vm._add_ubridge_connection.assert_called_once_with(nio, 0, 42)
|
||||
assert vm._start_ubridge.called
|
||||
assert vm._start_console.called
|
||||
assert vm._start_aux.called
|
||||
assert vm.status == "started"
|
||||
|
||||
|
||||
def test_start_namespace_failed(loop, vm, manager, free_console_port):
|
||||
|
||||
assert vm.status != "started"
|
||||
vm.adapters = 1
|
||||
|
||||
nio = manager.create_nio(0, {"type": "nio_udp", "lport": free_console_port, "rport": free_console_port, "rhost": "127.0.0.1"})
|
||||
loop.run_until_complete(asyncio.async(vm.adapter_add_nio_binding(0, nio)))
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.docker.DockerVM._get_container_state", return_value="stopped"):
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.query") as mock_query:
|
||||
with asyncio_patch("gns3server.hypervisor.docker.DockerVM._start_ubridge") as mock_start_ubridge:
|
||||
with asyncio_patch("gns3server.hypervisor.docker.DockerVM._get_namespace", return_value=42) as mock_namespace:
|
||||
with asyncio_patch("gns3server.hypervisor.docker.DockerVM._add_ubridge_connection", side_effect=UbridgeNamespaceError()) as mock_add_ubridge_connection:
|
||||
with asyncio_patch("gns3server.hypervisor.docker.DockerVM._get_log", return_value='Hello not available') as mock_log:
|
||||
|
||||
with pytest.raises(DockerError):
|
||||
loop.run_until_complete(asyncio.async(vm.start()))
|
||||
|
||||
mock_query.assert_any_call("POST", "containers/e90e34656842/start")
|
||||
mock_add_ubridge_connection.assert_called_once_with(nio, 0, 42)
|
||||
assert mock_start_ubridge.called
|
||||
assert vm.status == "stopped"
|
||||
|
||||
|
||||
def test_start_without_nio(loop, vm, manager, free_console_port):
|
||||
"""
|
||||
If no nio exists we will create one.
|
||||
"""
|
||||
|
||||
assert vm.status != "started"
|
||||
vm.adapters = 1
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.docker.DockerVM._get_container_state", return_value="stopped"):
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.query") as mock_query:
|
||||
with asyncio_patch("gns3server.hypervisor.docker.DockerVM._start_ubridge") as mock_start_ubridge:
|
||||
with asyncio_patch("gns3server.hypervisor.docker.DockerVM._get_namespace", return_value=42) as mock_namespace:
|
||||
with asyncio_patch("gns3server.hypervisor.docker.DockerVM._add_ubridge_connection") as mock_add_ubridge_connection:
|
||||
with asyncio_patch("gns3server.hypervisor.docker.DockerVM._start_console") as mock_start_console:
|
||||
loop.run_until_complete(asyncio.async(vm.start()))
|
||||
|
||||
mock_query.assert_called_with("POST", "containers/e90e34656842/start")
|
||||
assert mock_add_ubridge_connection.called
|
||||
assert mock_start_ubridge.called
|
||||
assert mock_start_console.called
|
||||
assert vm.status == "started"
|
||||
|
||||
|
||||
def test_start_unpause(loop, vm, manager, free_console_port):
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.docker.DockerVM._get_container_state", return_value="paused"):
|
||||
with asyncio_patch("gns3server.hypervisor.docker.DockerVM.unpause", return_value="paused") as mock:
|
||||
loop.run_until_complete(asyncio.async(vm.start()))
|
||||
assert mock.called
|
||||
assert vm.status == "started"
|
||||
|
||||
|
||||
def test_restart(loop, vm):
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.query") as mock:
|
||||
loop.run_until_complete(asyncio.async(vm.restart()))
|
||||
|
||||
mock.assert_called_with("POST", "containers/e90e34656842/restart")
|
||||
|
||||
|
||||
def test_stop(loop, vm):
|
||||
vm._ubridge_hypervisor = MagicMock()
|
||||
vm._ubridge_hypervisor.is_running.return_value = True
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.docker.DockerVM._get_container_state", return_value="running"):
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.query") as mock_query:
|
||||
loop.run_until_complete(asyncio.async(vm.stop()))
|
||||
mock_query.assert_called_with("POST", "containers/e90e34656842/stop", params={"t": 5})
|
||||
assert vm._ubridge_hypervisor.stop.called
|
||||
|
||||
|
||||
def test_stop_paused_container(loop, vm):
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.docker.DockerVM._get_container_state", return_value="paused"):
|
||||
with asyncio_patch("gns3server.hypervisor.docker.DockerVM.unpause") as mock_unpause:
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.query") as mock_query:
|
||||
loop.run_until_complete(asyncio.async(vm.stop()))
|
||||
mock_query.assert_called_with("POST", "containers/e90e34656842/stop", params={"t": 5})
|
||||
assert mock_unpause.called
|
||||
|
||||
|
||||
def test_update(loop, vm):
|
||||
|
||||
response = {
|
||||
"Id": "e90e34656806",
|
||||
"Warnings": []
|
||||
}
|
||||
|
||||
original_console = vm.console
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.list_images", return_value=[{"image": "ubuntu"}]) as mock_list_images:
|
||||
with asyncio_patch("gns3server.hypervisor.docker.DockerVM._get_container_state", return_value="stopped"):
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.query", return_value=response) as mock_query:
|
||||
loop.run_until_complete(asyncio.async(vm.update()))
|
||||
|
||||
mock_query.assert_any_call("DELETE", "containers/e90e34656842", params={"force": 1})
|
||||
mock_query.assert_any_call("POST", "containers/create", data={
|
||||
"Tty": True,
|
||||
"OpenStdin": True,
|
||||
"StdinOnce": False,
|
||||
"HostConfig":
|
||||
{
|
||||
"CapAdd": ["ALL"],
|
||||
"Binds": ["{}:/gns3:ro".format(get_resource("hypervisor/docker/resources"))],
|
||||
"Privileged": True
|
||||
},
|
||||
"Volumes": {},
|
||||
"NetworkDisabled": True,
|
||||
"Name": "test",
|
||||
"Hostname": "test",
|
||||
"Image": "ubuntu",
|
||||
"Env": [],
|
||||
"Cmd": ["/bin/sh", "/gns3/init.sh", "/bin/sh"]
|
||||
})
|
||||
assert vm.console == original_console
|
||||
|
||||
|
||||
def test_update_running(loop, vm):
|
||||
|
||||
response = {
|
||||
"Id": "e90e34656806",
|
||||
"Warnings": []
|
||||
}
|
||||
|
||||
original_console = vm.console
|
||||
vm.start = MagicMock()
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.list_images", return_value=[{"image": "ubuntu"}]) as mock_list_images:
|
||||
with asyncio_patch("gns3server.hypervisor.docker.DockerVM._get_container_state", return_value="running"):
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.query", return_value=response) as mock_query:
|
||||
loop.run_until_complete(asyncio.async(vm.update()))
|
||||
|
||||
mock_query.assert_any_call("DELETE", "containers/e90e34656842", params={"force": 1})
|
||||
mock_query.assert_any_call("POST", "containers/create", data={
|
||||
"Tty": True,
|
||||
"OpenStdin": True,
|
||||
"StdinOnce": False,
|
||||
"HostConfig":
|
||||
{
|
||||
"CapAdd": ["ALL"],
|
||||
"Binds": ["{}:/gns3:ro".format(get_resource("hypervisor/docker/resources"))],
|
||||
"Privileged": True
|
||||
},
|
||||
"Volumes": {},
|
||||
"NetworkDisabled": True,
|
||||
"Name": "test",
|
||||
"Hostname": "test",
|
||||
"Image": "ubuntu",
|
||||
"Env": [],
|
||||
"Cmd": ["/bin/sh", "/gns3/init.sh", "/bin/sh"]
|
||||
})
|
||||
|
||||
assert vm.console == original_console
|
||||
assert vm.start.called
|
||||
|
||||
|
||||
def test_delete(loop, vm):
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.docker.DockerVM._get_container_state", return_value="stopped"):
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.query") as mock_query:
|
||||
loop.run_until_complete(asyncio.async(vm.delete()))
|
||||
mock_query.assert_called_with("DELETE", "containers/e90e34656842", params={"force": 1})
|
||||
|
||||
|
||||
def test_close(loop, vm, port_manager):
|
||||
nio = {"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"}
|
||||
nio = vm.manager.create_nio(0, nio)
|
||||
loop.run_until_complete(asyncio.async(vm.adapter_add_nio_binding(0, nio)))
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.docker.DockerVM._get_container_state", return_value="stopped"):
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.query") as mock_query:
|
||||
loop.run_until_complete(asyncio.async(vm.close()))
|
||||
mock_query.assert_called_with("DELETE", "containers/e90e34656842", params={"force": 1})
|
||||
|
||||
assert vm._closed is True
|
||||
assert "4242" not in port_manager.udp_ports
|
||||
|
||||
|
||||
def test_close_vnc(loop, vm, port_manager):
|
||||
|
||||
vm._console_type = "vnc"
|
||||
vm._x11vnc_process = MagicMock()
|
||||
vm._xvfb_process = MagicMock()
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.docker.DockerVM._get_container_state", return_value="stopped"):
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.query") as mock_query:
|
||||
loop.run_until_complete(asyncio.async(vm.close()))
|
||||
mock_query.assert_called_with("DELETE", "containers/e90e34656842", params={"force": 1})
|
||||
|
||||
assert vm._closed is True
|
||||
assert vm._xvfb_process.terminate.called
|
||||
|
||||
|
||||
def test_get_namespace(loop, vm):
|
||||
response = {
|
||||
"State": {
|
||||
"Pid": 42
|
||||
}
|
||||
}
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.query", return_value=response) as mock_query:
|
||||
assert loop.run_until_complete(asyncio.async(vm._get_namespace())) == 42
|
||||
mock_query.assert_called_with("GET", "containers/e90e34656842/json")
|
||||
|
||||
|
||||
def test_add_ubridge_connection(loop, vm):
|
||||
|
||||
nio = {"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"}
|
||||
nio = vm.manager.create_nio(0, nio)
|
||||
nio.startPacketCapture("/tmp/capture.pcap")
|
||||
vm._ubridge_hypervisor = MagicMock()
|
||||
|
||||
loop.run_until_complete(asyncio.async(vm._add_ubridge_connection(nio, 0, 42)))
|
||||
|
||||
calls = [
|
||||
call.send("docker create_veth veth-gns3-ext0 veth-gns3-int0"),
|
||||
call.send('docker move_to_ns veth-gns3-int0 42 eth0'),
|
||||
call.send('bridge create bridge0'),
|
||||
call.send('bridge add_nio_linux_raw bridge0 veth-gns3-ext0'),
|
||||
call.send('bridge add_nio_udp bridge0 4242 127.0.0.1 4343'),
|
||||
call.send('bridge start_capture bridge0 "/tmp/capture.pcap"'),
|
||||
call.send('bridge start bridge0')
|
||||
]
|
||||
# We need to check any_order ortherwise mock is confused by asyncio
|
||||
vm._ubridge_hypervisor.assert_has_calls(calls, any_order=True)
|
||||
|
||||
|
||||
def test_add_ubridge_connection_none_nio(loop, vm):
|
||||
|
||||
nio = None
|
||||
vm._ubridge_hypervisor = MagicMock()
|
||||
|
||||
loop.run_until_complete(asyncio.async(vm._add_ubridge_connection(nio, 0, 42)))
|
||||
|
||||
calls = [
|
||||
call.send("docker create_veth veth-gns3-ext0 veth-gns3-int0"),
|
||||
call.send('docker move_to_ns veth-gns3-int0 42 eth0'),
|
||||
]
|
||||
# We need to check any_order ortherwise mock is confused by asyncio
|
||||
vm._ubridge_hypervisor.assert_has_calls(calls, any_order=True)
|
||||
|
||||
|
||||
def test_add_ubridge_connection_invalid_adapter_number(loop, vm):
|
||||
|
||||
nio = {"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"}
|
||||
nio = vm.manager.create_nio(0, nio)
|
||||
with pytest.raises(DockerError):
|
||||
loop.run_until_complete(asyncio.async(vm._add_ubridge_connection(nio, 12, 42)))
|
||||
|
||||
|
||||
def test_add_ubridge_connection_no_free_interface(loop, vm):
|
||||
|
||||
nio = {"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"}
|
||||
nio = vm.manager.create_nio(0, nio)
|
||||
with pytest.raises(DockerError):
|
||||
|
||||
# We create fake ethernet interfaces for docker
|
||||
interfaces = ["veth-gns3-ext{}".format(index) for index in range(128)]
|
||||
|
||||
with patch("psutil.net_if_addrs", return_value=interfaces):
|
||||
loop.run_until_complete(asyncio.async(vm._add_ubridge_connection(nio, 0, 42)))
|
||||
|
||||
|
||||
def test_delete_ubridge_connection(loop, vm):
|
||||
|
||||
vm._ubridge_hypervisor = MagicMock()
|
||||
nio = {"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"}
|
||||
nio = vm.manager.create_nio(0, nio)
|
||||
|
||||
loop.run_until_complete(asyncio.async(vm._add_ubridge_connection(nio, 0, 42)))
|
||||
loop.run_until_complete(asyncio.async(vm._delete_ubridge_connection(0)))
|
||||
|
||||
calls = [
|
||||
call.send("bridge delete bridge0"),
|
||||
call.send('docker delete_veth veth-gns3-ext0')
|
||||
]
|
||||
vm._ubridge_hypervisor.assert_has_calls(calls, any_order=True)
|
||||
|
||||
|
||||
def test_adapter_add_nio_binding(vm, loop):
|
||||
nio = {"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"}
|
||||
nio = vm.manager.create_nio(0, nio)
|
||||
loop.run_until_complete(asyncio.async(vm.adapter_add_nio_binding(0, nio)))
|
||||
assert vm._ethernet_adapters[0].get_nio(0) == nio
|
||||
|
||||
|
||||
def test_adapter_add_nio_binding_invalid_adapter(vm, loop):
|
||||
nio = {"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"}
|
||||
nio = vm.manager.create_nio(0, nio)
|
||||
with pytest.raises(DockerError):
|
||||
loop.run_until_complete(asyncio.async(vm.adapter_add_nio_binding(12, nio)))
|
||||
|
||||
|
||||
def test_adapter_remove_nio_binding(vm, loop):
|
||||
nio = {"type": "nio_udp",
|
||||
"lport": 4242,
|
||||
"rport": 4343,
|
||||
"rhost": "127.0.0.1"}
|
||||
nio = vm.manager.create_nio(0, nio)
|
||||
loop.run_until_complete(asyncio.async(vm.adapter_add_nio_binding(0, nio)))
|
||||
with asyncio_patch("gns3server.hypervisor.docker.DockerVM._delete_ubridge_connection") as delete_ubridge_mock:
|
||||
loop.run_until_complete(asyncio.async(vm.adapter_remove_nio_binding(0)))
|
||||
assert vm._ethernet_adapters[0].get_nio(0) is None
|
||||
delete_ubridge_mock.assert_called_with(0)
|
||||
|
||||
|
||||
def test_adapter_remove_nio_binding_invalid_adapter(vm, loop):
|
||||
with pytest.raises(DockerError):
|
||||
loop.run_until_complete(asyncio.async(vm.adapter_remove_nio_binding(12)))
|
||||
|
||||
|
||||
def test_pull_image(loop, vm):
|
||||
class Response:
|
||||
"""
|
||||
Simulate a response splitted in multiple packets
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._read = -1
|
||||
|
||||
@asyncio.coroutine
|
||||
def read(self, size):
|
||||
self._read += 1
|
||||
if self._read == 0:
|
||||
return b'{"progress": "0/100",'
|
||||
elif self._read == 1:
|
||||
return '"id": 42}'
|
||||
else:
|
||||
None
|
||||
|
||||
mock_query = MagicMock()
|
||||
mock_query.content.return_value = Response()
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.http_query", return_value=mock_query) as mock:
|
||||
images = loop.run_until_complete(asyncio.async(vm.pull_image("ubuntu")))
|
||||
mock.assert_called_with("POST", "images/create", params={"fromImage": "ubuntu"})
|
||||
|
||||
|
||||
def test_start_capture(vm, tmpdir, manager, free_console_port, loop):
|
||||
|
||||
output_file = str(tmpdir / "test.pcap")
|
||||
nio = manager.create_nio(0, {"type": "nio_udp", "lport": free_console_port, "rport": free_console_port, "rhost": "127.0.0.1"})
|
||||
loop.run_until_complete(asyncio.async(vm.adapter_add_nio_binding(0, nio)))
|
||||
loop.run_until_complete(asyncio.async(vm.start_capture(0, output_file)))
|
||||
assert vm._ethernet_adapters[0].get_nio(0).capturing
|
||||
|
||||
|
||||
def test_stop_capture(vm, tmpdir, manager, free_console_port, loop):
|
||||
|
||||
output_file = str(tmpdir / "test.pcap")
|
||||
nio = manager.create_nio(0, {"type": "nio_udp", "lport": free_console_port, "rport": free_console_port, "rhost": "127.0.0.1"})
|
||||
loop.run_until_complete(asyncio.async(vm.adapter_add_nio_binding(0, nio)))
|
||||
loop.run_until_complete(vm.start_capture(0, output_file))
|
||||
assert vm._ethernet_adapters[0].get_nio(0).capturing
|
||||
loop.run_until_complete(asyncio.async(vm.stop_capture(0)))
|
||||
assert vm._ethernet_adapters[0].get_nio(0).capturing is False
|
||||
|
||||
|
||||
def test_get_log(loop, vm):
|
||||
@asyncio.coroutine
|
||||
def read():
|
||||
return b'Hello\nWorld'
|
||||
|
||||
mock_query = MagicMock()
|
||||
mock_query.read = read
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.http_query", return_value=mock_query) as mock:
|
||||
images = loop.run_until_complete(asyncio.async(vm._get_log()))
|
||||
mock.assert_called_with("GET", "containers/e90e34656842/logs", params={"stderr": 1, "stdout": 1}, data={})
|
||||
|
||||
|
||||
def test_get_image_informations(project, manager, loop):
|
||||
response = {
|
||||
}
|
||||
with asyncio_patch("gns3server.hypervisor.docker.Docker.query", return_value=response) as mock:
|
||||
vm = DockerVM("test", str(uuid.uuid4()), project, manager, "ubuntu")
|
||||
loop.run_until_complete(asyncio.async(vm._get_image_informations()))
|
||||
mock.assert_called_with("GET", "images/ubuntu/json")
|
||||
|
||||
|
||||
def test_mount_binds(vm, tmpdir):
|
||||
image_infos = {
|
||||
"ContainerConfig": {
|
||||
"Volumes": {
|
||||
"/test/experimental": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dst = os.path.join(vm.working_dir, "test/experimental")
|
||||
assert vm._mount_binds(image_infos) == [
|
||||
"{}:/gns3:ro".format(get_resource("hypervisor/docker/resources")),
|
||||
"{}:{}".format(dst, "/test/experimental")
|
||||
]
|
||||
|
||||
assert os.path.exists(dst)
|
||||
|
||||
|
||||
def test_start_vnc(vm, loop):
|
||||
with patch("shutil.which", return_value="/bin/x"):
|
||||
with asyncio_patch("gns3server.hypervisor.docker.docker_vm.wait_for_file_creation") as mock_wait:
|
||||
with asyncio_patch("asyncio.create_subprocess_exec") as mock_exec:
|
||||
loop.run_until_complete(asyncio.async(vm._start_vnc()))
|
||||
assert vm._display is not None
|
||||
mock_exec.assert_any_call("Xvfb", "-nolisten", "tcp", ":{}".format(vm._display), "-screen", "0", "1024x768x16")
|
||||
mock_exec.assert_any_call("x11vnc", "-forever", "-nopw", "-display", "WAIT:{}".format(vm._display), "-rfbport", str(vm.console), "-noncache", "-listen", "127.0.0.1")
|
||||
mock_wait.assert_called_with("/tmp/.X11-unix/X{}".format(vm._display))
|
||||
|
||||
|
||||
def test_start_vnc_xvfb_missing(vm, loop):
|
||||
with pytest.raises(DockerError):
|
||||
loop.run_until_complete(asyncio.async(vm._start_vnc()))
|
||||
|
||||
|
||||
def test_start_aux(vm, loop):
|
||||
|
||||
with asyncio_patch("asyncio.subprocess.create_subprocess_exec", return_value=MagicMock()) as mock_exec:
|
||||
loop.run_until_complete(asyncio.async(vm._start_aux()))
|
||||
@@ -0,0 +1,96 @@
|
||||
# -*- 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 pytest
|
||||
import tempfile
|
||||
import sys
|
||||
import uuid
|
||||
import os
|
||||
import asyncio
|
||||
|
||||
from gns3server.hypervisor.dynamips import Dynamips
|
||||
from gns3server.hypervisor.dynamips.dynamips_error import DynamipsError
|
||||
from unittest.mock import patch
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def manager(port_manager):
|
||||
m = Dynamips.instance()
|
||||
m.port_manager = port_manager
|
||||
return m
|
||||
|
||||
|
||||
def test_vm_invalid_dynamips_path(manager):
|
||||
with patch("gns3server.config.Config.get_section_config", return_value={"dynamips_path": "/bin/test_fake"}):
|
||||
with pytest.raises(DynamipsError):
|
||||
manager.find_dynamips()
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported by Windows")
|
||||
def test_vm_non_executable_dynamips_path(manager):
|
||||
tmpfile = tempfile.NamedTemporaryFile()
|
||||
with patch("gns3server.config.Config.get_section_config", return_value={"dynamips_path": tmpfile.name}):
|
||||
with pytest.raises(DynamipsError):
|
||||
manager.find_dynamips()
|
||||
|
||||
|
||||
def test_get_dynamips_id(manager):
|
||||
project_1 = str(uuid.uuid4())
|
||||
project_2 = str(uuid.uuid4())
|
||||
project_3 = str(uuid.uuid4())
|
||||
|
||||
assert manager.get_dynamips_id(project_1) == 1
|
||||
assert manager.get_dynamips_id(project_1) == 2
|
||||
assert manager.get_dynamips_id(project_2) == 1
|
||||
with pytest.raises(DynamipsError):
|
||||
for dynamips_id in range(1, 4098):
|
||||
manager.get_dynamips_id(project_3)
|
||||
|
||||
|
||||
def test_take_dynamips_id(manager):
|
||||
project_1 = str(uuid.uuid4())
|
||||
|
||||
manager.take_dynamips_id(project_1, 1)
|
||||
assert manager.get_dynamips_id(project_1) == 2
|
||||
with pytest.raises(DynamipsError):
|
||||
manager.take_dynamips_id(project_1, 1)
|
||||
|
||||
|
||||
def test_release_dynamips_id(manager):
|
||||
project_1 = str(uuid.uuid4())
|
||||
project_2 = str(uuid.uuid4())
|
||||
|
||||
manager.take_dynamips_id(project_1, 1)
|
||||
manager.release_dynamips_id(project_1, 1)
|
||||
assert manager.get_dynamips_id(project_1) == 1
|
||||
# Should not crash for 0 id
|
||||
manager.release_dynamips_id(project_2, 0)
|
||||
|
||||
|
||||
def test_project_closed(manager, project, loop):
|
||||
|
||||
manager._dynamips_ids[project.id] = set([1, 2, 3])
|
||||
|
||||
project_dir = project.module_working_path(manager.module_name.lower())
|
||||
os.makedirs(project_dir)
|
||||
open(os.path.join(project_dir, "test.ghost"), "w+").close()
|
||||
|
||||
loop.run_until_complete(asyncio.async(manager.project_closed(project)))
|
||||
|
||||
assert not os.path.exists(os.path.join(project_dir, "test.ghost"))
|
||||
assert project.id not in manager._dynamips_ids
|
||||
@@ -0,0 +1,57 @@
|
||||
# -*- 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 pytest
|
||||
import asyncio
|
||||
import configparser
|
||||
|
||||
from unittest.mock import patch
|
||||
from gns3server.hypervisor.dynamips.nodes.router import Router
|
||||
from gns3server.hypervisor.dynamips.dynamips_error import DynamipsError
|
||||
from gns3server.hypervisor.dynamips import Dynamips
|
||||
from gns3server.config import Config
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def manager(port_manager):
|
||||
m = Dynamips.instance()
|
||||
m.port_manager = port_manager
|
||||
return m
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def router(project, manager):
|
||||
return Router("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager)
|
||||
|
||||
|
||||
def test_router(project, manager):
|
||||
router = Router("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager)
|
||||
assert router.name == "test"
|
||||
assert router.id == "00010203-0405-0607-0809-0a0b0c0d0e0f"
|
||||
|
||||
|
||||
def test_router_invalid_dynamips_path(project, manager, loop):
|
||||
|
||||
config = Config.instance()
|
||||
config.set("Dynamips", "dynamips_path", "/bin/test_fake")
|
||||
config.set("Dynamips", "allocate_aux_console_ports", False)
|
||||
|
||||
with pytest.raises(DynamipsError):
|
||||
router = Router("test", "00010203-0405-0607-0809-0a0b0c0d0e0e", project, manager)
|
||||
loop.run_until_complete(asyncio.async(router.create()))
|
||||
assert router.name == "test"
|
||||
assert router.id == "00010203-0405-0607-0809-0a0b0c0d0e0e"
|
||||
@@ -0,0 +1,81 @@
|
||||
# -*- 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 pytest
|
||||
from unittest.mock import patch
|
||||
import uuid
|
||||
import os
|
||||
import sys
|
||||
|
||||
pytestmark = pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
|
||||
|
||||
if not sys.platform.startswith("win"):
|
||||
from gns3server.hypervisor.iou import IOU
|
||||
from gns3server.hypervisor.iou.iou_error import IOUError
|
||||
|
||||
from gns3server.hypervisor.project_manager import ProjectManager
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def iou(port_manager):
|
||||
# Cleanup the IOU object
|
||||
IOU._instance = None
|
||||
iou = IOU.instance()
|
||||
iou.port_manager = port_manager
|
||||
return iou
|
||||
|
||||
|
||||
def test_get_application_id(loop, project, iou):
|
||||
vm1_id = str(uuid.uuid4())
|
||||
vm2_id = str(uuid.uuid4())
|
||||
vm3_id = str(uuid.uuid4())
|
||||
loop.run_until_complete(iou.create_vm("PC 1", project.id, vm1_id))
|
||||
loop.run_until_complete(iou.create_vm("PC 2", project.id, vm2_id))
|
||||
assert iou.get_application_id(vm1_id) == 1
|
||||
assert iou.get_application_id(vm1_id) == 1
|
||||
assert iou.get_application_id(vm2_id) == 2
|
||||
loop.run_until_complete(iou.delete_vm(vm1_id))
|
||||
loop.run_until_complete(iou.create_vm("PC 3", project.id, vm3_id))
|
||||
assert iou.get_application_id(vm3_id) == 1
|
||||
|
||||
|
||||
def test_get_application_id_multiple_project(loop, iou):
|
||||
vm1_id = str(uuid.uuid4())
|
||||
vm2_id = str(uuid.uuid4())
|
||||
vm3_id = str(uuid.uuid4())
|
||||
project1 = ProjectManager.instance().create_project()
|
||||
project2 = ProjectManager.instance().create_project()
|
||||
loop.run_until_complete(iou.create_vm("PC 1", project1.id, vm1_id))
|
||||
loop.run_until_complete(iou.create_vm("PC 2", project1.id, vm2_id))
|
||||
loop.run_until_complete(iou.create_vm("PC 2", project2.id, vm3_id))
|
||||
assert iou.get_application_id(vm1_id) == 1
|
||||
assert iou.get_application_id(vm2_id) == 2
|
||||
assert iou.get_application_id(vm3_id) == 3
|
||||
|
||||
|
||||
def test_get_application_id_no_id_available(loop, project, iou):
|
||||
with pytest.raises(IOUError):
|
||||
for i in range(1, 513):
|
||||
vm_id = str(uuid.uuid4())
|
||||
loop.run_until_complete(iou.create_vm("PC {}".format(i), project.id, vm_id))
|
||||
assert iou.get_application_id(vm_id) == i
|
||||
|
||||
|
||||
def test_get_images_directory(iou, tmpdir):
|
||||
with patch("gns3server.config.Config.get_section_config", return_value={"images_path": str(tmpdir)}):
|
||||
assert iou.get_images_directory() == str(tmpdir / "IOU")
|
||||
@@ -0,0 +1,431 @@
|
||||
# -*- 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 pytest
|
||||
import aiohttp
|
||||
import asyncio
|
||||
import os
|
||||
import stat
|
||||
import socket
|
||||
import sys
|
||||
import uuid
|
||||
import shutil
|
||||
from tests.utils import asyncio_patch
|
||||
|
||||
|
||||
from unittest.mock import patch, MagicMock, PropertyMock
|
||||
|
||||
pytestmark = pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
|
||||
|
||||
if not sys.platform.startswith("win"):
|
||||
from gns3server.hypervisor.iou.iou_vm import IOUVM
|
||||
from gns3server.hypervisor.iou.iou_error import IOUError
|
||||
from gns3server.hypervisor.iou import IOU
|
||||
|
||||
from gns3server.config import Config
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def manager(port_manager):
|
||||
m = IOU.instance()
|
||||
m.port_manager = port_manager
|
||||
return m
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def vm(project, manager, tmpdir, fake_iou_bin, iourc_file):
|
||||
fake_file = str(tmpdir / "iouyap")
|
||||
with open(fake_file, "w+") as f:
|
||||
f.write("1")
|
||||
|
||||
vm = IOUVM("test", str(uuid.uuid4()), project, manager)
|
||||
config = manager.config.get_section_config("IOU")
|
||||
config["iouyap_path"] = fake_file
|
||||
config["iourc_path"] = iourc_file
|
||||
manager.config.set_section_config("IOU", config)
|
||||
|
||||
vm.path = "iou.bin"
|
||||
return vm
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def iourc_file(tmpdir):
|
||||
path = str(tmpdir / "iourc")
|
||||
with open(path, "w+") as f:
|
||||
hostname = socket.gethostname()
|
||||
f.write("[license]\n{} = aaaaaaaaaaaaaaaa;".format(hostname))
|
||||
return path
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def fake_iou_bin(tmpdir):
|
||||
"""Create a fake IOU image on disk"""
|
||||
|
||||
os.makedirs(str(tmpdir / "IOU"), exist_ok=True)
|
||||
path = str(tmpdir / "IOU" / "iou.bin")
|
||||
with open(path, "w+") as f:
|
||||
f.write('\x7fELF\x01\x01\x01')
|
||||
os.chmod(path, stat.S_IREAD | stat.S_IEXEC)
|
||||
return path
|
||||
|
||||
|
||||
def test_vm(project, manager):
|
||||
vm = IOUVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager)
|
||||
assert vm.name == "test"
|
||||
assert vm.id == "00010203-0405-0607-0809-0a0b0c0d0e0f"
|
||||
|
||||
|
||||
def test_vm_startup_config_content(project, manager):
|
||||
vm = IOUVM("test", "00010203-0405-0607-0808-0a0b0c0d0e0f", project, manager)
|
||||
vm.startup_config_content = "hostname %h"
|
||||
assert vm.name == "test"
|
||||
assert vm.startup_config_content == "hostname test"
|
||||
assert vm.id == "00010203-0405-0607-0808-0a0b0c0d0e0f"
|
||||
|
||||
|
||||
@patch("gns3server.config.Config.get_section_config", return_value={"iouyap_path": "/bin/test_fake"})
|
||||
def test_vm_invalid_iouyap_path(project, manager, loop, fake_iou_bin):
|
||||
with pytest.raises(IOUError):
|
||||
vm = IOUVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0e", project, manager)
|
||||
vm.path = "iou.bin"
|
||||
loop.run_until_complete(asyncio.async(vm.start()))
|
||||
|
||||
|
||||
def test_start(loop, vm, monkeypatch):
|
||||
|
||||
mock_process = MagicMock()
|
||||
with patch("gns3server.hypervisor.iou.iou_vm.IOUVM._check_requirements", return_value=True):
|
||||
with asyncio_patch("gns3server.hypervisor.iou.iou_vm.IOUVM._check_iou_licence", return_value=True):
|
||||
with asyncio_patch("gns3server.hypervisor.iou.iou_vm.IOUVM._start_ioucon", return_value=True):
|
||||
with asyncio_patch("gns3server.hypervisor.iou.iou_vm.IOUVM._start_iouyap", return_value=True):
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=mock_process) as mock_exec:
|
||||
mock_process.returncode = None
|
||||
loop.run_until_complete(asyncio.async(vm.start()))
|
||||
assert vm.is_running()
|
||||
assert vm.command_line == ' '.join(mock_exec.call_args[0])
|
||||
|
||||
|
||||
def test_start_with_iourc(loop, vm, monkeypatch, tmpdir):
|
||||
|
||||
fake_file = str(tmpdir / "iourc")
|
||||
with open(fake_file, "w+") as f:
|
||||
f.write("1")
|
||||
mock_process = MagicMock()
|
||||
with patch("gns3server.config.Config.get_section_config", return_value={"iourc_path": fake_file, "iouyap_path": vm.iouyap_path}):
|
||||
with asyncio_patch("gns3server.hypervisor.iou.iou_vm.IOUVM._check_requirements", return_value=True):
|
||||
with asyncio_patch("gns3server.hypervisor.iou.iou_vm.IOUVM._check_iou_licence", return_value=True):
|
||||
with asyncio_patch("gns3server.hypervisor.iou.iou_vm.IOUVM._start_ioucon", return_value=True):
|
||||
with asyncio_patch("gns3server.hypervisor.iou.iou_vm.IOUVM._start_iouyap", return_value=True):
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=mock_process) as exec_mock:
|
||||
mock_process.returncode = None
|
||||
loop.run_until_complete(asyncio.async(vm.start()))
|
||||
assert vm.is_running()
|
||||
arsgs, kwargs = exec_mock.call_args
|
||||
assert kwargs["env"]["IOURC"] == fake_file
|
||||
|
||||
|
||||
def test_rename_nvram_file(loop, vm, monkeypatch):
|
||||
"""
|
||||
It should rename the nvram file to the correct name before launching the VM
|
||||
"""
|
||||
|
||||
with open(os.path.join(vm.working_dir, "nvram_0000{}".format(vm.application_id + 1)), 'w+') as f:
|
||||
f.write("1")
|
||||
|
||||
with open(os.path.join(vm.working_dir, "vlan.dat-0000{}".format(vm.application_id + 1)), 'w+') as f:
|
||||
f.write("1")
|
||||
|
||||
vm._rename_nvram_file()
|
||||
assert os.path.exists(os.path.join(vm.working_dir, "nvram_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):
|
||||
process = MagicMock()
|
||||
|
||||
# Wait process kill success
|
||||
future = asyncio.Future()
|
||||
future.set_result(True)
|
||||
process.wait.return_value = future
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.iou.iou_vm.IOUVM._check_requirements", return_value=True):
|
||||
with asyncio_patch("gns3server.hypervisor.iou.iou_vm.IOUVM._start_ioucon", return_value=True):
|
||||
with asyncio_patch("gns3server.hypervisor.iou.iou_vm.IOUVM._start_iouyap", return_value=True):
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=process):
|
||||
with asyncio_patch("gns3server.utils.asyncio.wait_for_process_termination"):
|
||||
loop.run_until_complete(asyncio.async(vm.start()))
|
||||
process.returncode = None
|
||||
assert vm.is_running()
|
||||
loop.run_until_complete(asyncio.async(vm.stop()))
|
||||
assert vm.is_running() is False
|
||||
process.terminate.assert_called_with()
|
||||
|
||||
|
||||
def test_reload(loop, vm, fake_iou_bin):
|
||||
process = MagicMock()
|
||||
|
||||
# Wait process kill success
|
||||
future = asyncio.Future()
|
||||
future.set_result(True)
|
||||
process.wait.return_value = future
|
||||
process.returncode = None
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.iou.iou_vm.IOUVM._check_requirements", return_value=True):
|
||||
with asyncio_patch("gns3server.hypervisor.iou.iou_vm.IOUVM._start_ioucon", return_value=True):
|
||||
with asyncio_patch("gns3server.hypervisor.iou.iou_vm.IOUVM._start_iouyap", return_value=True):
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=process):
|
||||
with asyncio_patch("gns3server.utils.asyncio.wait_for_process_termination"):
|
||||
loop.run_until_complete(asyncio.async(vm.start()))
|
||||
assert vm.is_running()
|
||||
loop.run_until_complete(asyncio.async(vm.reload()))
|
||||
assert vm.is_running() is True
|
||||
process.terminate.assert_called_with()
|
||||
|
||||
|
||||
def test_close(vm, port_manager, loop):
|
||||
with asyncio_patch("gns3server.hypervisor.iou.iou_vm.IOUVM._check_requirements", return_value=True):
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()):
|
||||
vm.start()
|
||||
port = vm.console
|
||||
loop.run_until_complete(asyncio.async(vm.close()))
|
||||
# Raise an exception if the port is not free
|
||||
port_manager.reserve_tcp_port(port, vm.project)
|
||||
assert vm.is_running() is False
|
||||
|
||||
|
||||
def test_path(vm, fake_iou_bin):
|
||||
with patch("gns3server.config.Config.get_section_config", return_value={"local": True}):
|
||||
vm.path = fake_iou_bin
|
||||
assert vm.path == fake_iou_bin
|
||||
|
||||
|
||||
def test_path_12_location(vm, fake_iou_bin):
|
||||
|
||||
# In 1.2 users uploaded images to the images roots
|
||||
# after the migration their images are inside images/IOU
|
||||
# but old topologies use old path
|
||||
with patch("gns3server.config.Config.get_section_config", return_value={"local": True}):
|
||||
vm.path = fake_iou_bin.replace("/IOU", "")
|
||||
assert vm.path == fake_iou_bin
|
||||
|
||||
|
||||
def test_path_relative(vm, fake_iou_bin, tmpdir):
|
||||
|
||||
config = Config.instance()
|
||||
config.set("Server", "images_path", str(tmpdir))
|
||||
vm.path = "iou.bin"
|
||||
assert vm.path == fake_iou_bin
|
||||
|
||||
|
||||
def test_path_invalid_bin(vm, tmpdir):
|
||||
|
||||
with patch("gns3server.config.Config.get_section_config", return_value={"local": True}):
|
||||
path = str(tmpdir / "test.bin")
|
||||
with pytest.raises(IOUError):
|
||||
vm.path = path
|
||||
vm._check_requirements()
|
||||
|
||||
with open(path, "w+") as f:
|
||||
f.write("BUG")
|
||||
|
||||
with pytest.raises(IOUError):
|
||||
vm.path = path
|
||||
vm._check_requirements()
|
||||
|
||||
|
||||
def test_create_netmap_config(vm):
|
||||
|
||||
vm._create_netmap_config()
|
||||
netmap_path = os.path.join(vm.working_dir, "NETMAP")
|
||||
|
||||
with open(netmap_path) as f:
|
||||
content = f.read()
|
||||
|
||||
assert "513:0/0 1:0/0" in content
|
||||
assert "513:15/3 1:15/3" in content
|
||||
|
||||
|
||||
def test_build_command(vm, loop):
|
||||
|
||||
assert loop.run_until_complete(asyncio.async(vm._build_command())) == [vm.path, "-L", str(vm.application_id)]
|
||||
|
||||
|
||||
def test_get_startup_config(vm):
|
||||
|
||||
content = "service timestamps debug datetime msec\nservice timestamps log datetime msec\nno service password-encryption"
|
||||
vm.startup_config = content
|
||||
assert vm.startup_config == content
|
||||
|
||||
|
||||
def test_update_startup_config(vm):
|
||||
content = "service timestamps debug datetime msec\nservice timestamps log datetime msec\nno service password-encryption"
|
||||
vm.startup_config_content = content
|
||||
filepath = os.path.join(vm.working_dir, "startup-config.cfg")
|
||||
assert os.path.exists(filepath)
|
||||
with open(filepath) as f:
|
||||
assert f.read() == content
|
||||
|
||||
|
||||
def test_update_startup_config_empty(vm):
|
||||
content = "service timestamps debug datetime msec\nservice timestamps log datetime msec\nno service password-encryption"
|
||||
vm.startup_config_content = content
|
||||
filepath = os.path.join(vm.working_dir, "startup-config.cfg")
|
||||
assert os.path.exists(filepath)
|
||||
with open(filepath) as f:
|
||||
assert f.read() == content
|
||||
vm.startup_config_content = ""
|
||||
with open(filepath) as f:
|
||||
assert f.read() == content
|
||||
|
||||
|
||||
def test_update_startup_config_content_hostname(vm):
|
||||
content = "hostname %h\n"
|
||||
vm.name = "pc1"
|
||||
vm.startup_config_content = content
|
||||
with open(vm.startup_config_file) as f:
|
||||
assert f.read() == "hostname pc1\n"
|
||||
|
||||
|
||||
def test_change_name(vm, tmpdir):
|
||||
path = os.path.join(vm.working_dir, "startup-config.cfg")
|
||||
vm.name = "world"
|
||||
with open(path, 'w+') as f:
|
||||
f.write("hostname world")
|
||||
vm.name = "hello"
|
||||
assert vm.name == "hello"
|
||||
with open(path) as f:
|
||||
assert f.read() == "hostname hello"
|
||||
|
||||
|
||||
def test_library_check(loop, vm):
|
||||
|
||||
with asyncio_patch("gns3server.utils.asyncio.subprocess_check_output", return_value="") as mock:
|
||||
|
||||
loop.run_until_complete(asyncio.async(vm._library_check()))
|
||||
|
||||
with asyncio_patch("gns3server.utils.asyncio.subprocess_check_output", return_value="libssl => not found") as mock:
|
||||
with pytest.raises(IOUError):
|
||||
loop.run_until_complete(asyncio.async(vm._library_check()))
|
||||
|
||||
|
||||
def test_enable_l1_keepalives(loop, 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:
|
||||
|
||||
command = ["test"]
|
||||
loop.run_until_complete(asyncio.async(vm._enable_l1_keepalives(command)))
|
||||
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:
|
||||
|
||||
command = ["test"]
|
||||
with pytest.raises(IOUError):
|
||||
loop.run_until_complete(asyncio.async(vm._enable_l1_keepalives(command)))
|
||||
assert command == ["test"]
|
||||
|
||||
|
||||
def test_start_capture(vm, tmpdir, manager, free_console_port, loop):
|
||||
|
||||
output_file = str(tmpdir / "test.pcap")
|
||||
nio = manager.create_nio(vm.iouyap_path, {"type": "nio_udp", "lport": free_console_port, "rport": free_console_port, "rhost": "127.0.0.1"})
|
||||
loop.run_until_complete(asyncio.async(vm.adapter_add_nio_binding(0, 0, nio)))
|
||||
loop.run_until_complete(asyncio.async(vm.start_capture(0, 0, output_file)))
|
||||
assert vm._adapters[0].get_nio(0).capturing
|
||||
|
||||
|
||||
def test_stop_capture(vm, tmpdir, manager, free_console_port, loop):
|
||||
|
||||
output_file = str(tmpdir / "test.pcap")
|
||||
nio = manager.create_nio(vm.iouyap_path, {"type": "nio_udp", "lport": free_console_port, "rport": free_console_port, "rhost": "127.0.0.1"})
|
||||
loop.run_until_complete(asyncio.async(vm.adapter_add_nio_binding(0, 0, nio)))
|
||||
loop.run_until_complete(vm.start_capture(0, 0, output_file))
|
||||
assert vm._adapters[0].get_nio(0).capturing
|
||||
loop.run_until_complete(asyncio.async(vm.stop_capture(0, 0)))
|
||||
assert vm._adapters[0].get_nio(0).capturing is False
|
||||
|
||||
|
||||
def test_get_legacy_vm_workdir():
|
||||
|
||||
assert IOU.get_legacy_vm_workdir(42, "bla") == "iou/device-42"
|
||||
|
||||
|
||||
def test_invalid_iou_file(loop, vm, iourc_file):
|
||||
|
||||
hostname = socket.gethostname()
|
||||
|
||||
loop.run_until_complete(asyncio.async(vm._check_iou_licence()))
|
||||
|
||||
# Missing ;
|
||||
with pytest.raises(IOUError):
|
||||
with open(iourc_file, "w+") as f:
|
||||
f.write("[license]\n{} = aaaaaaaaaaaaaaaa".format(hostname))
|
||||
loop.run_until_complete(asyncio.async(vm._check_iou_licence()))
|
||||
|
||||
# Key too short
|
||||
with pytest.raises(IOUError):
|
||||
with open(iourc_file, "w+") as f:
|
||||
f.write("[license]\n{} = aaaaaaaaaaaaaa;".format(hostname))
|
||||
loop.run_until_complete(asyncio.async(vm._check_iou_licence()))
|
||||
|
||||
# Invalid hostname
|
||||
with pytest.raises(IOUError):
|
||||
with open(iourc_file, "w+") as f:
|
||||
f.write("[license]\nbla = aaaaaaaaaaaaaa;")
|
||||
loop.run_until_complete(asyncio.async(vm._check_iou_licence()))
|
||||
|
||||
# Missing licence section
|
||||
with pytest.raises(IOUError):
|
||||
with open(iourc_file, "w+") as f:
|
||||
f.write("[licensetest]\n{} = aaaaaaaaaaaaaaaa;")
|
||||
loop.run_until_complete(asyncio.async(vm._check_iou_licence()))
|
||||
|
||||
# Broken config file
|
||||
with pytest.raises(IOUError):
|
||||
with open(iourc_file, "w+") as f:
|
||||
f.write("[")
|
||||
loop.run_until_complete(asyncio.async(vm._check_iou_licence()))
|
||||
|
||||
# Missing file
|
||||
with pytest.raises(IOUError):
|
||||
os.remove(iourc_file)
|
||||
loop.run_until_complete(asyncio.async(vm._check_iou_licence()))
|
||||
|
||||
|
||||
def test_iourc_content(vm):
|
||||
|
||||
vm.iourc_content = "test"
|
||||
|
||||
with open(os.path.join(vm.temporary_directory, "iourc")) as f:
|
||||
assert f.read() == "test"
|
||||
|
||||
|
||||
def test_extract_configs(vm):
|
||||
assert vm.extract_configs() == (None, None)
|
||||
|
||||
with open(os.path.join(vm.working_dir, "nvram_00001"), "w+") as f:
|
||||
f.write("CORRUPTED")
|
||||
assert vm.extract_configs() == (None, None)
|
||||
|
||||
with open(os.path.join(vm.working_dir, "nvram_00001"), "w+") as f:
|
||||
f.write("CORRUPTED")
|
||||
assert vm.extract_configs() == (None, None)
|
||||
|
||||
shutil.copy("tests/resources/nvram_iou", os.path.join(vm.working_dir, "nvram_00001"))
|
||||
|
||||
startup_config, private_config = vm.extract_configs()
|
||||
assert len(startup_config) == 1392
|
||||
assert len(private_config) == 0
|
||||
@@ -0,0 +1,192 @@
|
||||
# -*- 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
|
||||
import stat
|
||||
import asyncio
|
||||
import sys
|
||||
import pytest
|
||||
import platform
|
||||
|
||||
from gns3server.hypervisor.qemu import Qemu
|
||||
from gns3server.hypervisor.qemu.qemu_error import QemuError
|
||||
from tests.utils import asyncio_patch
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def fake_qemu_img_binary(tmpdir):
|
||||
|
||||
bin_path = str(tmpdir / "qemu-img")
|
||||
with open(bin_path, "w+") as f:
|
||||
f.write("1")
|
||||
os.chmod(bin_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
|
||||
return bin_path
|
||||
|
||||
|
||||
def test_get_qemu_version(loop):
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.qemu.subprocess_check_output", return_value="QEMU emulator version 2.2.0, Copyright (c) 2003-2008 Fabrice Bellard") as mock:
|
||||
version = loop.run_until_complete(asyncio.async(Qemu.get_qemu_version("/tmp/qemu-test")))
|
||||
if sys.platform.startswith("win"):
|
||||
assert version == ""
|
||||
else:
|
||||
assert version == "2.2.0"
|
||||
|
||||
|
||||
def test_binary_list(loop):
|
||||
|
||||
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:
|
||||
path = os.path.join(os.environ["PATH"], file_to_create)
|
||||
with open(path, "w+") as f:
|
||||
f.write("1")
|
||||
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.qemu.subprocess_check_output", return_value="QEMU emulator version 2.2.0, Copyright (c) 2003-2008 Fabrice Bellard") as mock:
|
||||
if sys.platform.startswith("win"):
|
||||
version = ""
|
||||
else:
|
||||
version = "2.2.0"
|
||||
|
||||
qemus = loop.run_until_complete(asyncio.async(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-kvm"), "version": version} in qemus
|
||||
assert {"path": os.path.join(os.environ["PATH"], "qemu-system-x42"), "version": version} 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
|
||||
|
||||
qemus = loop.run_until_complete(asyncio.async(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-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"], "hello"), "version": version} not in qemus
|
||||
|
||||
qemus = loop.run_until_complete(asyncio.async(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-kvm"), "version": version} not in qemus
|
||||
assert {"path": os.path.join(os.environ["PATH"], "qemu-system-x42"), "version": version} in qemus
|
||||
assert {"path": os.path.join(os.environ["PATH"], "hello"), "version": version} not in qemus
|
||||
|
||||
|
||||
def test_img_binary_list(loop):
|
||||
|
||||
files_to_create = ["qemu-img", "qemu-io", "qemu-system-x86", "qemu-system-x42", "qemu-kvm", "hello"]
|
||||
|
||||
for file_to_create in files_to_create:
|
||||
path = os.path.join(os.environ["PATH"], file_to_create)
|
||||
with open(path, "w+") as f:
|
||||
f.write("1")
|
||||
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.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.async(Qemu.img_binary_list()))
|
||||
|
||||
version = "2.2.0"
|
||||
|
||||
assert {"path": os.path.join(os.environ["PATH"], "qemu-img"), "version": version} in qemus
|
||||
assert {"path": os.path.join(os.environ["PATH"], "qemu-io"), "version": version} not in qemus
|
||||
assert {"path": os.path.join(os.environ["PATH"], "qemu-system-x86"), "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"], "hello"), "version": version} not in qemus
|
||||
|
||||
|
||||
def test_get_legacy_vm_workdir():
|
||||
|
||||
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):
|
||||
options = {
|
||||
"format": "qcow2",
|
||||
"preallocation": "metadata",
|
||||
"cluster_size": 64,
|
||||
"refcount_bits": 12,
|
||||
"lazy_refcounts": "off",
|
||||
"size": 100
|
||||
}
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process:
|
||||
loop.run_until_complete(asyncio.async(Qemu.instance().create_disk(fake_qemu_img_binary, str(tmpdir / "hda.qcow2"), options)))
|
||||
args, kwargs = process.call_args
|
||||
assert args == (
|
||||
fake_qemu_img_binary,
|
||||
"create",
|
||||
"-f",
|
||||
"qcow2",
|
||||
"-o",
|
||||
"cluster_size=64",
|
||||
"-o",
|
||||
"lazy_refcounts=off",
|
||||
"-o",
|
||||
"preallocation=metadata",
|
||||
"-o",
|
||||
"refcount_bits=12",
|
||||
str(tmpdir / "hda.qcow2"),
|
||||
"100M"
|
||||
)
|
||||
|
||||
|
||||
def test_create_image_relative_path(loop, tmpdir, fake_qemu_img_binary):
|
||||
options = {
|
||||
"format": "raw",
|
||||
"size": 100
|
||||
}
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process:
|
||||
with patch("gns3server.hypervisor.qemu.Qemu.get_images_directory", return_value=str(tmpdir)):
|
||||
loop.run_until_complete(asyncio.async(Qemu.instance().create_disk(fake_qemu_img_binary, "hda.qcow2", options)))
|
||||
args, kwargs = process.call_args
|
||||
assert args == (
|
||||
fake_qemu_img_binary,
|
||||
"create",
|
||||
"-f",
|
||||
"raw",
|
||||
str(tmpdir / "hda.qcow2"),
|
||||
"100M"
|
||||
)
|
||||
|
||||
|
||||
def test_create_image_exist(loop, tmpdir, fake_qemu_img_binary):
|
||||
open(str(tmpdir / "hda.qcow2"), "w+").close()
|
||||
|
||||
options = {
|
||||
"format": "raw",
|
||||
"size": 100
|
||||
}
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process:
|
||||
with patch("gns3server.hypervisor.qemu.Qemu.get_images_directory", return_value=str(tmpdir)):
|
||||
with pytest.raises(QemuError):
|
||||
loop.run_until_complete(asyncio.async(Qemu.instance().create_disk(fake_qemu_img_binary, "hda.qcow2", options)))
|
||||
assert not process.called
|
||||
|
||||
|
||||
def test_get_kvm_archs_kvm_ok(loop):
|
||||
|
||||
with patch("os.path.exists", return_value=True):
|
||||
archs = loop.run_until_complete(asyncio.async(Qemu.get_kvm_archs()))
|
||||
if platform.machine() == 'x86_64':
|
||||
assert archs == ['x86_64', 'i386']
|
||||
else:
|
||||
assert archs == platform.machine()
|
||||
|
||||
with patch("os.path.exists", return_value=False):
|
||||
archs = loop.run_until_complete(asyncio.async(Qemu.get_kvm_archs()))
|
||||
assert archs == []
|
||||
@@ -0,0 +1,585 @@
|
||||
# -*- 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 pytest
|
||||
import aiohttp
|
||||
import asyncio
|
||||
import os
|
||||
import sys
|
||||
import stat
|
||||
import re
|
||||
from tests.utils import asyncio_patch
|
||||
|
||||
|
||||
from unittest import mock
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
from gns3server.hypervisor.qemu.qemu_vm import QemuVM
|
||||
from gns3server.hypervisor.qemu.qemu_error import QemuError
|
||||
from gns3server.hypervisor.qemu import Qemu
|
||||
from gns3server.utils import force_unix_path
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def manager(port_manager):
|
||||
m = Qemu.instance()
|
||||
m.port_manager = port_manager
|
||||
return m
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def fake_qemu_img_binary():
|
||||
|
||||
bin_path = os.path.join(os.environ["PATH"], "qemu-img")
|
||||
with open(bin_path, "w+") as f:
|
||||
f.write("1")
|
||||
os.chmod(bin_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
|
||||
return bin_path
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def fake_qemu_binary():
|
||||
|
||||
if sys.platform.startswith("win"):
|
||||
bin_path = os.path.join(os.environ["PATH"], "qemu-system-x86_64.EXE")
|
||||
else:
|
||||
bin_path = os.path.join(os.environ["PATH"], "qemu-system-x86_64")
|
||||
with open(bin_path, "w+") as f:
|
||||
f.write("1")
|
||||
os.chmod(bin_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
|
||||
return bin_path
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def vm(project, manager, fake_qemu_binary, fake_qemu_img_binary):
|
||||
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._process_priority = "normal" # Avoid complexity for Windows tests
|
||||
return vm
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def running_subprocess_mock():
|
||||
mm = MagicMock()
|
||||
mm.returncode = None
|
||||
return mm
|
||||
|
||||
|
||||
def test_vm(project, manager, fake_qemu_binary):
|
||||
vm = QemuVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, qemu_path=fake_qemu_binary)
|
||||
assert vm.name == "test"
|
||||
assert vm.id == "00010203-0405-0607-0809-0a0b0c0d0e0f"
|
||||
|
||||
|
||||
def test_vm_invalid_qemu_with_platform(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")
|
||||
|
||||
assert vm.qemu_path == fake_qemu_binary
|
||||
assert vm.platform == "x86_64"
|
||||
|
||||
|
||||
def test_vm_invalid_qemu_without_platform(project, manager, fake_qemu_binary):
|
||||
|
||||
vm = QemuVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, qemu_path="/usr/fake/bin/qemu-system-x86_64")
|
||||
|
||||
assert vm.qemu_path == fake_qemu_binary
|
||||
assert vm.platform == "x86_64"
|
||||
|
||||
|
||||
def test_is_running(vm, running_subprocess_mock):
|
||||
|
||||
vm._process = None
|
||||
assert vm.is_running() is False
|
||||
vm._process = running_subprocess_mock
|
||||
assert vm.is_running()
|
||||
vm._process.returncode = -1
|
||||
assert vm.is_running() is False
|
||||
|
||||
|
||||
def test_start(loop, vm, running_subprocess_mock):
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=running_subprocess_mock) as mock:
|
||||
loop.run_until_complete(asyncio.async(vm.start()))
|
||||
assert vm.is_running()
|
||||
assert vm.command_line == ' '.join(mock.call_args[0])
|
||||
|
||||
|
||||
def test_stop(loop, vm, running_subprocess_mock):
|
||||
process = running_subprocess_mock
|
||||
|
||||
# Wait process kill success
|
||||
future = asyncio.Future()
|
||||
future.set_result(True)
|
||||
process.wait.return_value = future
|
||||
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=process):
|
||||
nio = Qemu.instance().create_nio(vm.qemu_path, {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"})
|
||||
vm.adapter_add_nio_binding(0, nio)
|
||||
loop.run_until_complete(asyncio.async(vm.start()))
|
||||
assert vm.is_running()
|
||||
loop.run_until_complete(asyncio.async(vm.stop()))
|
||||
assert vm.is_running() is False
|
||||
process.terminate.assert_called_with()
|
||||
|
||||
|
||||
def test_termination_callback(vm):
|
||||
|
||||
vm.status = "started"
|
||||
queue = vm.project.get_listen_queue()
|
||||
|
||||
vm._termination_callback(0)
|
||||
assert vm.status == "stopped"
|
||||
|
||||
(action, event) = queue.get_nowait()
|
||||
assert action == "vm.stopped"
|
||||
assert event == vm
|
||||
|
||||
with pytest.raises(asyncio.queues.QueueEmpty):
|
||||
queue.get_nowait()
|
||||
|
||||
|
||||
def test_termination_callback_error(vm, tmpdir):
|
||||
|
||||
with open(str(tmpdir / "qemu.log"), "w+") as f:
|
||||
f.write("BOOMM")
|
||||
|
||||
vm.status = "started"
|
||||
vm._stdout_file = str(tmpdir / "qemu.log")
|
||||
queue = vm.project.get_listen_queue()
|
||||
|
||||
vm._termination_callback(1)
|
||||
assert vm.status == "stopped"
|
||||
|
||||
(action, event) = queue.get_nowait()
|
||||
assert action == "vm.stopped"
|
||||
assert event == vm
|
||||
|
||||
(action, event) = queue.get_nowait()
|
||||
assert action == "log.error"
|
||||
assert event["message"] == "QEMU process has stopped, return code: 1\nBOOMM"
|
||||
|
||||
|
||||
def test_reload(loop, vm):
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.qemu.QemuVM._control_vm") as mock:
|
||||
loop.run_until_complete(asyncio.async(vm.reload()))
|
||||
assert mock.called_with("system_reset")
|
||||
|
||||
|
||||
def test_suspend(loop, vm):
|
||||
|
||||
control_vm_result = MagicMock()
|
||||
control_vm_result.match.group.decode.return_value = "running"
|
||||
with asyncio_patch("gns3server.hypervisor.qemu.QemuVM._control_vm", return_value=control_vm_result) as mock:
|
||||
loop.run_until_complete(asyncio.async(vm.suspend()))
|
||||
assert mock.called_with("system_reset")
|
||||
|
||||
|
||||
def test_add_nio_binding_udp(vm, loop):
|
||||
nio = Qemu.instance().create_nio(vm.qemu_path, {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"})
|
||||
loop.run_until_complete(asyncio.async(vm.adapter_add_nio_binding(0, nio)))
|
||||
assert nio.lport == 4242
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
|
||||
def test_add_nio_binding_ethernet(vm, loop, ethernet_device):
|
||||
with patch("gns3server.hypervisor.base_manager.BaseManager.has_privileged_access", return_value=True):
|
||||
nio = Qemu.instance().create_nio(vm.qemu_path, {"type": "nio_generic_ethernet", "ethernet_device": ethernet_device})
|
||||
loop.run_until_complete(asyncio.async(vm.adapter_add_nio_binding(0, nio)))
|
||||
assert nio.ethernet_device == ethernet_device
|
||||
|
||||
|
||||
def test_port_remove_nio_binding(vm, loop):
|
||||
nio = Qemu.instance().create_nio(vm.qemu_path, {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"})
|
||||
loop.run_until_complete(asyncio.async(vm.adapter_add_nio_binding(0, nio)))
|
||||
loop.run_until_complete(asyncio.async(vm.adapter_remove_nio_binding(0)))
|
||||
assert vm._ethernet_adapters[0].ports[0] is None
|
||||
|
||||
|
||||
def test_close(vm, port_manager, loop):
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()):
|
||||
loop.run_until_complete(asyncio.async(vm.start()))
|
||||
|
||||
console_port = vm.console
|
||||
|
||||
loop.run_until_complete(asyncio.async(vm.close()))
|
||||
|
||||
# Raise an exception if the port is not free
|
||||
port_manager.reserve_tcp_port(console_port, vm.project)
|
||||
|
||||
assert vm.is_running() is False
|
||||
|
||||
|
||||
def test_set_qemu_path(vm, tmpdir, fake_qemu_binary):
|
||||
|
||||
# Raise because none
|
||||
with pytest.raises(QemuError):
|
||||
vm.qemu_path = None
|
||||
|
||||
# Should not crash with unicode characters
|
||||
path = str(tmpdir / "\u62FF" / "qemu-system-mips")
|
||||
|
||||
os.makedirs(str(tmpdir / "\u62FF"))
|
||||
|
||||
# Raise because file doesn't exists
|
||||
with pytest.raises(QemuError):
|
||||
vm.qemu_path = path
|
||||
|
||||
with open(path, "w+") as f:
|
||||
f.write("1")
|
||||
|
||||
# Raise because file is not executable
|
||||
if not sys.platform.startswith("win"):
|
||||
with pytest.raises(QemuError):
|
||||
vm.qemu_path = path
|
||||
|
||||
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
|
||||
|
||||
vm.qemu_path = path
|
||||
assert vm.qemu_path == path
|
||||
assert vm.platform == "mips"
|
||||
|
||||
|
||||
def test_set_qemu_path_environ(vm, tmpdir, fake_qemu_binary):
|
||||
|
||||
# It should find the binary in the path
|
||||
vm.qemu_path = "qemu-system-x86_64"
|
||||
|
||||
assert vm.qemu_path == fake_qemu_binary
|
||||
assert vm.platform == "x86_64"
|
||||
|
||||
|
||||
def test_set_qemu_path_windows(vm, tmpdir):
|
||||
|
||||
bin_path = os.path.join(os.environ["PATH"], "qemu-system-x86_64w.EXE")
|
||||
open(bin_path, "w+").close()
|
||||
os.chmod(bin_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
|
||||
|
||||
vm.qemu_path = bin_path
|
||||
|
||||
assert vm.qemu_path == bin_path
|
||||
assert vm.platform == "x86_64"
|
||||
|
||||
|
||||
def test_set_qemu_path_old_windows(vm, tmpdir):
|
||||
|
||||
bin_path = os.path.join(os.environ["PATH"], "qemu.exe")
|
||||
open(bin_path, "w+").close()
|
||||
os.chmod(bin_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
|
||||
|
||||
vm.qemu_path = bin_path
|
||||
|
||||
assert vm.qemu_path == bin_path
|
||||
assert vm.platform == "i386"
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
|
||||
def test_set_qemu_path_kvm_binary(vm, tmpdir, fake_qemu_binary):
|
||||
|
||||
bin_path = os.path.join(os.environ["PATH"], "qemu-kvm")
|
||||
with open(bin_path, "w+") as f:
|
||||
f.write("1")
|
||||
os.chmod(bin_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
|
||||
|
||||
# It should find the binary in the path
|
||||
vm.qemu_path = "qemu-kvm"
|
||||
|
||||
assert vm.qemu_path.endswith("qemu-kvm")
|
||||
assert vm.platform == "x86_64"
|
||||
|
||||
|
||||
def test_set_platform(project, manager):
|
||||
|
||||
with patch("shutil.which", return_value="/bin/qemu-system-x86_64") as which_mock:
|
||||
with patch("gns3server.hypervisor.qemu.QemuVM._check_qemu_path"):
|
||||
vm = QemuVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, platform="x86_64")
|
||||
if sys.platform.startswith("win"):
|
||||
which_mock.assert_called_with("qemu-system-x86_64w.exe", path=mock.ANY)
|
||||
else:
|
||||
which_mock.assert_called_with("qemu-system-x86_64", path=mock.ANY)
|
||||
assert vm.platform == "x86_64"
|
||||
assert vm.qemu_path == "/bin/qemu-system-x86_64"
|
||||
|
||||
|
||||
def test_disk_options(vm, tmpdir, loop, fake_qemu_img_binary):
|
||||
|
||||
vm._hda_disk_image = str(tmpdir / "test.qcow2")
|
||||
open(vm._hda_disk_image, "w+").close()
|
||||
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process:
|
||||
loop.run_until_complete(asyncio.async(vm._disk_options()))
|
||||
assert process.called
|
||||
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"))
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
|
||||
def test_set_process_priority(vm, loop, fake_qemu_img_binary):
|
||||
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process:
|
||||
vm._process = MagicMock()
|
||||
vm._process.pid = 42
|
||||
vm._process_priority = "low"
|
||||
loop.run_until_complete(asyncio.async(vm._set_process_priority()))
|
||||
assert process.called
|
||||
args, kwargs = process.call_args
|
||||
assert args == ("renice", "-n", "5", "-p", "42")
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
|
||||
def test_set_process_priority_normal(vm, loop, fake_qemu_img_binary):
|
||||
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process:
|
||||
vm._process = MagicMock()
|
||||
vm._process.pid = 42
|
||||
loop.run_until_complete(asyncio.async(vm._set_process_priority()))
|
||||
assert not process.called
|
||||
|
||||
|
||||
def test_json(vm, project):
|
||||
|
||||
json = vm.__json__()
|
||||
assert json["name"] == vm.name
|
||||
assert json["project_id"] == project.id
|
||||
|
||||
|
||||
def test_control_vm(vm, loop):
|
||||
|
||||
vm._process = MagicMock()
|
||||
reader = MagicMock()
|
||||
writer = MagicMock()
|
||||
with asyncio_patch("asyncio.open_connection", return_value=(reader, writer)) as open_connect:
|
||||
res = loop.run_until_complete(asyncio.async(vm._control_vm("test")))
|
||||
assert writer.write.called_with("test")
|
||||
assert res is None
|
||||
|
||||
|
||||
def test_control_vm_expect_text(vm, loop, running_subprocess_mock):
|
||||
|
||||
vm._process = running_subprocess_mock
|
||||
reader = MagicMock()
|
||||
writer = MagicMock()
|
||||
with asyncio_patch("asyncio.open_connection", return_value=(reader, writer)) as open_connect:
|
||||
|
||||
future = asyncio.Future()
|
||||
future.set_result(b"epic product")
|
||||
reader.readline.return_value = future
|
||||
|
||||
vm._monitor = 4242
|
||||
res = loop.run_until_complete(asyncio.async(vm._control_vm("test", [b"epic"])))
|
||||
assert writer.write.called_with("test")
|
||||
|
||||
assert res == "epic product"
|
||||
|
||||
|
||||
def test_build_command(vm, loop, fake_qemu_binary, port_manager):
|
||||
|
||||
os.environ["DISPLAY"] = "0:0"
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process:
|
||||
cmd = loop.run_until_complete(asyncio.async(vm._build_command()))
|
||||
assert cmd == [
|
||||
fake_qemu_binary,
|
||||
"-name",
|
||||
"test",
|
||||
"-m",
|
||||
"256M",
|
||||
"-smp",
|
||||
"cpus=1",
|
||||
"-boot",
|
||||
"order=c",
|
||||
"-serial",
|
||||
"telnet:127.0.0.1:{},server,nowait".format(vm.console),
|
||||
"-net",
|
||||
"none",
|
||||
"-device",
|
||||
"e1000,mac=00:00:ab:0e:0f:00"
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
|
||||
def test_build_command_without_display(vm, loop, fake_qemu_binary):
|
||||
|
||||
os.environ["DISPLAY"] = ""
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process:
|
||||
cmd = loop.run_until_complete(asyncio.async(vm._build_command()))
|
||||
assert "-nographic" in cmd
|
||||
|
||||
|
||||
# Windows accept this kind of mistake
|
||||
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
|
||||
def test_build_command_with_invalid_options(vm, loop, fake_qemu_binary):
|
||||
|
||||
vm.options = "'test"
|
||||
with pytest.raises(QemuError):
|
||||
cmd = loop.run_until_complete(asyncio.async(vm._build_command()))
|
||||
|
||||
|
||||
def test_hda_disk_image(vm, tmpdir):
|
||||
|
||||
vm.manager.config.set("Server", "images_path", str(tmpdir))
|
||||
|
||||
vm.hda_disk_image = str(tmpdir / "test")
|
||||
assert vm.hda_disk_image == force_unix_path(str(tmpdir / "test"))
|
||||
vm.hda_disk_image = "test"
|
||||
assert vm.hda_disk_image == force_unix_path(str(tmpdir / "QEMU" / "test"))
|
||||
|
||||
|
||||
def test_hda_disk_image_ova(vm, tmpdir):
|
||||
|
||||
vm.manager.config.set("Server", "images_path", str(tmpdir))
|
||||
|
||||
vm.hda_disk_image = "test.ovf/test.vmdk"
|
||||
assert vm.hda_disk_image == force_unix_path(str(tmpdir / "QEMU" / "test.ovf" / "test.vmdk"))
|
||||
|
||||
|
||||
def test_hdb_disk_image(vm, tmpdir):
|
||||
|
||||
vm.manager.config.set("Server", "images_path", str(tmpdir))
|
||||
|
||||
vm.hdb_disk_image = str(tmpdir / "test")
|
||||
assert vm.hdb_disk_image == force_unix_path(str(tmpdir / "test"))
|
||||
vm.hdb_disk_image = "test"
|
||||
assert vm.hdb_disk_image == force_unix_path(str(tmpdir / "QEMU" / "test"))
|
||||
|
||||
|
||||
def test_hdc_disk_image(vm, tmpdir):
|
||||
|
||||
vm.manager.config.set("Server", "images_path", str(tmpdir))
|
||||
|
||||
vm.hdc_disk_image = str(tmpdir / "test")
|
||||
assert vm.hdc_disk_image == force_unix_path(str(tmpdir / "test"))
|
||||
vm.hdc_disk_image = "test"
|
||||
assert vm.hdc_disk_image == force_unix_path(str(tmpdir / "QEMU" / "test"))
|
||||
|
||||
|
||||
def test_hdd_disk_image(vm, tmpdir):
|
||||
|
||||
vm.manager.config.set("Server", "images_path", str(tmpdir))
|
||||
|
||||
vm.hdd_disk_image = str(tmpdir / "test")
|
||||
assert vm.hdd_disk_image == force_unix_path(str(tmpdir / "test"))
|
||||
vm.hdd_disk_image = "test"
|
||||
assert vm.hdd_disk_image == force_unix_path(str(tmpdir / "QEMU" / "test"))
|
||||
|
||||
|
||||
def test_initrd(vm, tmpdir):
|
||||
|
||||
vm.manager.config.set("Server", "images_path", str(tmpdir))
|
||||
|
||||
with patch("gns3server.hypervisor.project.Project.emit") as mock:
|
||||
vm.initrd = str(tmpdir / "test")
|
||||
assert vm.initrd == force_unix_path(str(tmpdir / "test"))
|
||||
vm.initrd = "test"
|
||||
assert vm.initrd == force_unix_path(str(tmpdir / "QEMU" / "test"))
|
||||
assert not mock.called
|
||||
|
||||
|
||||
def test_initrd_asa(vm, tmpdir):
|
||||
|
||||
vm.manager.config.set("Server", "images_path", str(tmpdir))
|
||||
|
||||
with patch("gns3server.hypervisor.project.Project.emit") as mock:
|
||||
vm.initrd = str(tmpdir / "asa842-initrd.gz")
|
||||
assert vm.initrd == force_unix_path(str(tmpdir / "asa842-initrd.gz"))
|
||||
vm.initrd = "asa842-initrd.gz"
|
||||
assert vm.initrd == force_unix_path(str(tmpdir / "QEMU" / "asa842-initrd.gz"))
|
||||
assert mock.called
|
||||
|
||||
|
||||
def test_options(linux_platform, vm):
|
||||
vm.kvm = False
|
||||
vm.options = "-usb"
|
||||
assert vm.options == "-usb"
|
||||
assert vm.kvm is False
|
||||
|
||||
vm.options = "-no-kvm"
|
||||
assert vm.options == "-no-kvm"
|
||||
|
||||
vm.options = "-enable-kvm"
|
||||
assert vm.options == "-enable-kvm"
|
||||
|
||||
vm.options = "-icount 12"
|
||||
assert vm.options == "-no-kvm -icount 12"
|
||||
|
||||
vm.options = "-icount 12 -no-kvm"
|
||||
assert vm.options == "-icount 12 -no-kvm"
|
||||
|
||||
|
||||
def test_options_windows(windows_platform, vm):
|
||||
vm.options = "-no-kvm"
|
||||
assert vm.options == ""
|
||||
|
||||
vm.options = "-enable-kvm"
|
||||
assert vm.options == ""
|
||||
|
||||
|
||||
def test_get_qemu_img(vm, tmpdir):
|
||||
|
||||
open(str(tmpdir / "qemu-sytem-x86_64"), "w+").close()
|
||||
open(str(tmpdir / "qemu-img"), "w+").close()
|
||||
vm._qemu_path = str(tmpdir / "qemu-sytem-x86_64")
|
||||
assert vm._get_qemu_img() == str(tmpdir / "qemu-img")
|
||||
|
||||
|
||||
def test_get_qemu_img_not_exist(vm, tmpdir):
|
||||
|
||||
open(str(tmpdir / "qemu-sytem-x86_64"), "w+").close()
|
||||
vm._qemu_path = str(tmpdir / "qemu-sytem-x86_64")
|
||||
with pytest.raises(QemuError):
|
||||
vm._get_qemu_img()
|
||||
|
||||
|
||||
def test_run_with_kvm_darwin(darwin_platform, vm):
|
||||
|
||||
vm.manager.config.set("Qemu", "enable_kvm", False)
|
||||
assert vm._run_with_kvm("qemu-system-x86_64", "") is False
|
||||
|
||||
|
||||
def test_run_with_kvm_windows(windows_platform, vm):
|
||||
|
||||
vm.manager.config.set("Qemu", "enable_kvm", False)
|
||||
assert vm._run_with_kvm("qemu-system-x86_64.exe", "") is False
|
||||
|
||||
|
||||
def test_run_with_kvm_linux(linux_platform, vm):
|
||||
|
||||
with patch("os.path.exists", return_value=True) as os_path:
|
||||
vm.manager.config.set("Qemu", "enable_kvm", True)
|
||||
assert vm._run_with_kvm("qemu-system-x86_64", "") is True
|
||||
os_path.assert_called_with("/dev/kvm")
|
||||
|
||||
|
||||
def test_run_with_kvm_linux_options_no_kvm(linux_platform, vm):
|
||||
|
||||
with patch("os.path.exists", return_value=True) as os_path:
|
||||
vm.manager.config.set("Qemu", "enable_kvm", True)
|
||||
assert vm._run_with_kvm("qemu-system-x86_64", "-no-kvm") is False
|
||||
|
||||
|
||||
def test_run_with_kvm_not_x86(linux_platform, vm):
|
||||
|
||||
with patch("os.path.exists", return_value=True) as os_path:
|
||||
vm.manager.config.set("Qemu", "enable_kvm", True)
|
||||
assert vm._run_with_kvm("qemu-system-arm", "") is False
|
||||
|
||||
|
||||
def test_run_with_kvm_linux_dev_kvm_missing(linux_platform, vm):
|
||||
|
||||
with patch("os.path.exists", return_value=False) as os_path:
|
||||
vm.manager.config.set("Qemu", "enable_kvm", True)
|
||||
with pytest.raises(QemuError):
|
||||
vm._run_with_kvm("qemu-system-x86_64", "")
|
||||
@@ -0,0 +1,123 @@
|
||||
# -*- 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 pytest
|
||||
import aiohttp
|
||||
import asyncio
|
||||
import os
|
||||
from tests.utils import asyncio_patch
|
||||
|
||||
|
||||
from unittest.mock import patch, MagicMock
|
||||
from gns3server.hypervisor.vpcs.vpcs_vm import VPCSVM
|
||||
from gns3server.hypervisor.docker.docker_vm import DockerVM
|
||||
from gns3server.hypervisor.vpcs.vpcs_error import VPCSError
|
||||
from gns3server.hypervisor.vm_error import VMError
|
||||
from gns3server.hypervisor.vpcs import VPCS
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def manager(port_manager):
|
||||
m = VPCS.instance()
|
||||
m.port_manager = port_manager
|
||||
return m
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def vm(project, manager):
|
||||
return VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager)
|
||||
|
||||
|
||||
def test_temporary_directory(project, manager):
|
||||
vm = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager)
|
||||
assert isinstance(vm.temporary_directory, str)
|
||||
|
||||
|
||||
def test_console(project, manager):
|
||||
vm = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager)
|
||||
vm.console = 2111
|
||||
assert vm.console == 2111
|
||||
vm.console = None
|
||||
assert vm.console is None
|
||||
|
||||
|
||||
def test_change_console_port(vm, port_manager):
|
||||
port1 = port_manager.get_free_tcp_port(vm.project)
|
||||
port2 = port_manager.get_free_tcp_port(vm.project)
|
||||
port_manager.release_tcp_port(port1, vm.project)
|
||||
port_manager.release_tcp_port(port2, vm.project)
|
||||
vm.console = port1
|
||||
vm.console = port2
|
||||
assert vm.console == port2
|
||||
port_manager.reserve_tcp_port(port1, vm.project)
|
||||
|
||||
|
||||
def test_console_vnc_invalid(project, manager):
|
||||
vm = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager)
|
||||
vm.console_type = "vnc"
|
||||
with pytest.raises(VMError):
|
||||
vm.console = 2012
|
||||
|
||||
|
||||
def test_close(vm, loop, port_manager):
|
||||
assert vm.console is not None
|
||||
|
||||
aux = port_manager.get_free_tcp_port(vm.project)
|
||||
port_manager.release_tcp_port(aux, vm.project)
|
||||
|
||||
vm.aux = aux
|
||||
port = vm.console
|
||||
assert loop.run_until_complete(asyncio.async(vm.close()))
|
||||
# Raise an exception if the port is not free
|
||||
port_manager.reserve_tcp_port(port, vm.project)
|
||||
# Raise an exception if the port is not free
|
||||
port_manager.reserve_tcp_port(aux, vm.project)
|
||||
assert vm.console is None
|
||||
assert vm.aux is None
|
||||
|
||||
# Called twice closed should return False
|
||||
assert loop.run_until_complete(asyncio.async(vm.close())) is False
|
||||
|
||||
|
||||
def test_aux(project, manager, port_manager):
|
||||
aux = port_manager.get_free_tcp_port(project)
|
||||
port_manager.release_tcp_port(aux, project)
|
||||
|
||||
vm = DockerVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, "ubuntu", aux=aux)
|
||||
assert vm.aux == aux
|
||||
vm.aux = None
|
||||
assert vm.aux is None
|
||||
|
||||
|
||||
def test_allocate_aux(project, manager):
|
||||
vm = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager)
|
||||
assert vm.aux is None
|
||||
|
||||
# Docker has an aux port by default
|
||||
vm = DockerVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, "ubuntu")
|
||||
assert vm.aux is not None
|
||||
|
||||
|
||||
def test_change_aux_port(vm, port_manager):
|
||||
port1 = port_manager.get_free_tcp_port(vm.project)
|
||||
port2 = port_manager.get_free_tcp_port(vm.project)
|
||||
port_manager.release_tcp_port(port1, vm.project)
|
||||
port_manager.release_tcp_port(port2, vm.project)
|
||||
vm.aux = port1
|
||||
vm.aux = port2
|
||||
assert vm.aux == port2
|
||||
port_manager.reserve_tcp_port(port1, vm.project)
|
||||
@@ -0,0 +1,195 @@
|
||||
# -*- 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 uuid
|
||||
import os
|
||||
import pytest
|
||||
from unittest.mock import patch
|
||||
|
||||
|
||||
from gns3server.hypervisor.vpcs import VPCS
|
||||
from gns3server.hypervisor.qemu import Qemu
|
||||
from gns3server.hypervisor.vm_error import VMError
|
||||
from gns3server.utils import force_unix_path
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def vpcs(port_manager):
|
||||
VPCS._instance = None
|
||||
vpcs = VPCS.instance()
|
||||
vpcs.port_manager = port_manager
|
||||
return vpcs
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def qemu(port_manager):
|
||||
Qemu._instance = None
|
||||
qemu = Qemu.instance()
|
||||
qemu.port_manager = port_manager
|
||||
return qemu
|
||||
|
||||
|
||||
def test_create_vm_new_topology(loop, project, vpcs):
|
||||
vm_id = str(uuid.uuid4())
|
||||
vm = loop.run_until_complete(vpcs.create_vm("PC 1", project.id, vm_id))
|
||||
assert vm in project.vms
|
||||
|
||||
|
||||
def test_create_twice_same_vm_new_topology(loop, project, vpcs):
|
||||
project._vms = set()
|
||||
vm_id = str(uuid.uuid4())
|
||||
vm = loop.run_until_complete(vpcs.create_vm("PC 1", project.id, vm_id, console=2222))
|
||||
assert vm in project.vms
|
||||
assert len(project.vms) == 1
|
||||
vm = loop.run_until_complete(vpcs.create_vm("PC 2", project.id, vm_id, console=2222))
|
||||
assert len(project.vms) == 1
|
||||
|
||||
|
||||
def test_create_vm_new_topology_without_uuid(loop, project, vpcs):
|
||||
vm = loop.run_until_complete(vpcs.create_vm("PC 1", project.id, None))
|
||||
assert vm in project.vms
|
||||
assert len(vm.id) == 36
|
||||
|
||||
|
||||
def test_create_vm_old_topology(loop, project, tmpdir, vpcs):
|
||||
|
||||
with patch("gns3server.hypervisor.project.Project.is_local", return_value=True):
|
||||
# Create an old topology directory
|
||||
project_dir = str(tmpdir / "testold")
|
||||
vm_dir = os.path.join(project_dir, "testold-files", "vpcs", "pc-1")
|
||||
project.path = project_dir
|
||||
project.name = "testold"
|
||||
os.makedirs(vm_dir, exist_ok=True)
|
||||
with open(os.path.join(vm_dir, "startup.vpc"), "w+") as f:
|
||||
f.write("1")
|
||||
|
||||
vm_id = 1
|
||||
vm = loop.run_until_complete(vpcs.create_vm("PC 1", project.id, vm_id))
|
||||
assert len(vm.id) == 36
|
||||
|
||||
assert os.path.exists(os.path.join(project_dir, "testold-files")) is False
|
||||
|
||||
vm_dir = os.path.join(project_dir, "project-files", "vpcs", vm.id)
|
||||
with open(os.path.join(vm_dir, "startup.vpc")) as f:
|
||||
assert f.read() == "1"
|
||||
|
||||
|
||||
def test_get_abs_image_path(qemu, tmpdir):
|
||||
os.makedirs(str(tmpdir / "QEMU"))
|
||||
path1 = force_unix_path(str(tmpdir / "test1.bin"))
|
||||
open(path1, 'w+').close()
|
||||
|
||||
path2 = force_unix_path(str(tmpdir / "QEMU" / "test2.bin"))
|
||||
open(path2, 'w+').close()
|
||||
|
||||
with patch("gns3server.config.Config.get_section_config", return_value={"images_path": str(tmpdir)}):
|
||||
assert qemu.get_abs_image_path(path1) == path1
|
||||
assert qemu.get_abs_image_path("test1.bin") == path1
|
||||
assert qemu.get_abs_image_path(path2) == path2
|
||||
assert qemu.get_abs_image_path("test2.bin") == path2
|
||||
assert qemu.get_abs_image_path("../test1.bin") == path1
|
||||
|
||||
|
||||
def test_get_abs_image_path_non_local(qemu, tmpdir):
|
||||
path1 = tmpdir / "images" / "QEMU" / "test1.bin"
|
||||
path1.write("1", ensure=True)
|
||||
path1 = force_unix_path(str(path1))
|
||||
|
||||
path2 = tmpdir / "private" / "QEMU" / "test2.bin"
|
||||
path2.write("1", ensure=True)
|
||||
path2 = force_unix_path(str(path2))
|
||||
|
||||
# If non local we can't use path outside images directory
|
||||
with patch("gns3server.config.Config.get_section_config", return_value={"images_path": str(tmpdir / "images"), "local": False}):
|
||||
assert qemu.get_abs_image_path(path1) == path1
|
||||
with pytest.raises(VMError):
|
||||
qemu.get_abs_image_path(path2)
|
||||
with pytest.raises(VMError):
|
||||
qemu.get_abs_image_path("C:\\test2.bin")
|
||||
|
||||
with patch("gns3server.config.Config.get_section_config", return_value={"images_path": str(tmpdir / "images"), "local": True}):
|
||||
assert qemu.get_abs_image_path(path2) == path2
|
||||
|
||||
|
||||
def test_get_relative_image_path(qemu, tmpdir):
|
||||
os.makedirs(str(tmpdir / "QEMU"))
|
||||
path1 = force_unix_path(str(tmpdir / "test1.bin"))
|
||||
open(path1, 'w+').close()
|
||||
|
||||
path2 = force_unix_path(str(tmpdir / "QEMU" / "test2.bin"))
|
||||
open(path2, 'w+').close()
|
||||
|
||||
with patch("gns3server.config.Config.get_section_config", return_value={"images_path": str(tmpdir)}):
|
||||
assert qemu.get_relative_image_path(path1) == path1
|
||||
assert qemu.get_relative_image_path("test1.bin") == path1
|
||||
assert qemu.get_relative_image_path(path2) == "test2.bin"
|
||||
assert qemu.get_relative_image_path("test2.bin") == "test2.bin"
|
||||
assert qemu.get_relative_image_path("../test1.bin") == path1
|
||||
|
||||
|
||||
def test_get_relative_image_path_ova(qemu, tmpdir):
|
||||
os.makedirs(str(tmpdir / "QEMU" / "test.ovf"))
|
||||
path = str(tmpdir / "QEMU" / "test.ovf" / "test.bin")
|
||||
open(path, 'w+').close()
|
||||
|
||||
with patch("gns3server.config.Config.get_section_config", return_value={"images_path": str(tmpdir)}):
|
||||
assert qemu.get_relative_image_path(path) == os.path.join("test.ovf", "test.bin")
|
||||
assert qemu.get_relative_image_path(os.path.join("test.ovf", "test.bin")) == os.path.join("test.ovf", "test.bin")
|
||||
|
||||
|
||||
def test_list_images(loop, qemu, tmpdir):
|
||||
|
||||
fake_images = ["a.bin", "b.bin", ".blu.bin", "a.bin.md5sum"]
|
||||
for image in fake_images:
|
||||
with open(str(tmpdir / image), "w+") as f:
|
||||
f.write("1")
|
||||
|
||||
with patch("gns3server.hypervisor.Qemu.get_images_directory", return_value=str(tmpdir)):
|
||||
assert loop.run_until_complete(qemu.list_images()) == [
|
||||
{"filename": "a.bin", "path": "a.bin"},
|
||||
{"filename": "b.bin", "path": "b.bin"}
|
||||
]
|
||||
|
||||
|
||||
def test_list_images_recursives(loop, qemu, tmpdir):
|
||||
|
||||
fake_images = ["a.bin", "b.bin", ".blu.bin", "a.bin.md5sum"]
|
||||
for image in fake_images:
|
||||
with open(str(tmpdir / image), "w+") as f:
|
||||
f.write("1")
|
||||
os.makedirs(str(tmpdir / "c"))
|
||||
fake_images = ["c.bin", "c.bin.md5sum"]
|
||||
for image in fake_images:
|
||||
with open(str(tmpdir / "c" / image), "w+") as f:
|
||||
f.write("1")
|
||||
|
||||
with patch("gns3server.hypervisor.Qemu.get_images_directory", return_value=str(tmpdir)):
|
||||
assert loop.run_until_complete(qemu.list_images()) == [
|
||||
{"filename": "a.bin", "path": "a.bin"},
|
||||
{"filename": "b.bin", "path": "b.bin"},
|
||||
{"filename": "c.bin", "path": os.path.sep.join(["c", "c.bin"])}
|
||||
]
|
||||
|
||||
|
||||
def test_list_images_empty(loop, qemu, tmpdir):
|
||||
with patch("gns3server.hypervisor.Qemu.get_images_directory", return_value=str(tmpdir)):
|
||||
assert loop.run_until_complete(qemu.list_images()) == []
|
||||
|
||||
|
||||
def test_list_images_directory_not_exist(loop, qemu):
|
||||
with patch("gns3server.hypervisor.Qemu.get_images_directory", return_value="/bla"):
|
||||
assert loop.run_until_complete(qemu.list_images()) == []
|
||||
@@ -0,0 +1,123 @@
|
||||
# -*- 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 aiohttp
|
||||
import pytest
|
||||
import sys
|
||||
from unittest.mock import patch
|
||||
from gns3server.hypervisor.port_manager import PortManager
|
||||
from gns3server.hypervisor.project import Project
|
||||
|
||||
|
||||
def test_reserve_tcp_port():
|
||||
pm = PortManager()
|
||||
project = Project()
|
||||
pm.reserve_tcp_port(2001, project)
|
||||
with patch("gns3server.hypervisor.project.Project.emit") as mock_emit:
|
||||
port = pm.reserve_tcp_port(2001, project)
|
||||
assert port != 2001
|
||||
assert mock_emit.call_args[0][0] == "log.warning"
|
||||
|
||||
|
||||
def test_reserve_tcp_port_outside_range():
|
||||
pm = PortManager()
|
||||
project = Project()
|
||||
with patch("gns3server.hypervisor.project.Project.emit") as mock_emit:
|
||||
port = pm.reserve_tcp_port(80, project)
|
||||
assert port != 80
|
||||
assert mock_emit.call_args[0][0] == "log.warning"
|
||||
|
||||
|
||||
def test_reserve_tcp_port_already_used_by_another_program():
|
||||
"""
|
||||
This test simulate a scenario where the port is already taken
|
||||
by another programm on the server
|
||||
"""
|
||||
|
||||
pm = PortManager()
|
||||
project = Project()
|
||||
with patch("gns3server.hypervisor.port_manager.PortManager._check_port") as mock_check:
|
||||
|
||||
def execute_mock(host, port, *args):
|
||||
if port == 2001:
|
||||
raise OSError("Port is already used")
|
||||
else:
|
||||
return True
|
||||
|
||||
mock_check.side_effect = execute_mock
|
||||
|
||||
with patch("gns3server.hypervisor.project.Project.emit") as mock_emit:
|
||||
port = pm.reserve_tcp_port(2001, project)
|
||||
assert port != 2001
|
||||
assert mock_emit.call_args[0][0] == "log.warning"
|
||||
|
||||
|
||||
def test_reserve_tcp_port_already_used():
|
||||
"""
|
||||
This test simulate a scenario where the port is already taken
|
||||
by another programm on the server
|
||||
"""
|
||||
|
||||
pm = PortManager()
|
||||
project = Project()
|
||||
with patch("gns3server.hypervisor.port_manager.PortManager._check_port") as mock_check:
|
||||
|
||||
def execute_mock(host, port, *args):
|
||||
if port == 2001:
|
||||
raise OSError("Port is already used")
|
||||
else:
|
||||
return True
|
||||
|
||||
mock_check.side_effect = execute_mock
|
||||
|
||||
with patch("gns3server.hypervisor.project.Project.emit") as mock_emit:
|
||||
port = pm.reserve_tcp_port(2001, project)
|
||||
assert port != 2001
|
||||
assert mock_emit.call_args[0][0] == "log.warning"
|
||||
|
||||
|
||||
def test_reserve_udp_port():
|
||||
pm = PortManager()
|
||||
project = Project()
|
||||
pm.reserve_udp_port(10000, project)
|
||||
with pytest.raises(aiohttp.web.HTTPConflict):
|
||||
pm.reserve_udp_port(10000, project)
|
||||
|
||||
|
||||
def test_reserve_udp_port_outside_range():
|
||||
pm = PortManager()
|
||||
project = Project()
|
||||
with pytest.raises(aiohttp.web.HTTPConflict):
|
||||
pm.reserve_udp_port(80, project)
|
||||
|
||||
|
||||
def test_release_udp_port():
|
||||
pm = PortManager()
|
||||
project = Project()
|
||||
pm.reserve_udp_port(10000, project)
|
||||
pm.release_udp_port(10000, project)
|
||||
pm.reserve_udp_port(10000, project)
|
||||
|
||||
|
||||
def test_find_unused_port():
|
||||
p = PortManager().find_unused_port(1000, 10000)
|
||||
assert p is not None
|
||||
|
||||
|
||||
def test_find_unused_port_invalid_range():
|
||||
with pytest.raises(aiohttp.web.HTTPConflict):
|
||||
p = PortManager().find_unused_port(10000, 1000)
|
||||
@@ -0,0 +1,260 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- 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
|
||||
import asyncio
|
||||
import pytest
|
||||
import aiohttp
|
||||
from uuid import uuid4
|
||||
from unittest.mock import patch
|
||||
|
||||
from tests.utils import asyncio_patch
|
||||
from gns3server.hypervisor.project import Project
|
||||
from gns3server.hypervisor.vpcs import VPCS, VPCSVM
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def manager(port_manager):
|
||||
m = VPCS.instance()
|
||||
m.port_manager = port_manager
|
||||
return m
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def vm(project, manager, loop):
|
||||
vm = manager.create_vm("test", project.id, "00010203-0405-0607-0809-0a0b0c0d0e0f")
|
||||
return loop.run_until_complete(asyncio.async(vm))
|
||||
|
||||
|
||||
def test_affect_uuid():
|
||||
p = Project()
|
||||
assert len(p.id) == 36
|
||||
|
||||
p = Project(project_id='00010203-0405-0607-0809-0a0b0c0d0e0f')
|
||||
assert p.id == '00010203-0405-0607-0809-0a0b0c0d0e0f'
|
||||
|
||||
|
||||
def test_path(tmpdir):
|
||||
with patch("gns3server.hypervisor.project.Project.is_local", return_value=True):
|
||||
p = Project(location=str(tmpdir))
|
||||
assert p.path == os.path.join(str(tmpdir), p.id)
|
||||
assert os.path.exists(os.path.join(str(tmpdir), p.id))
|
||||
assert not os.path.exists(os.path.join(p.path, ".gns3_temporary"))
|
||||
|
||||
|
||||
def test_init_path(tmpdir):
|
||||
|
||||
with patch("gns3server.hypervisor.project.Project.is_local", return_value=True):
|
||||
p = Project(path=str(tmpdir))
|
||||
assert p.path == str(tmpdir)
|
||||
|
||||
|
||||
def test_changing_path_temporary_flag(tmpdir):
|
||||
|
||||
with patch("gns3server.hypervisor.project.Project.is_local", return_value=True):
|
||||
p = Project(temporary=True)
|
||||
assert os.path.exists(p.path)
|
||||
original_path = p.path
|
||||
assert os.path.exists(os.path.join(p.path, ".gns3_temporary"))
|
||||
|
||||
p.path = str(tmpdir)
|
||||
|
||||
|
||||
def test_temporary_path():
|
||||
p = Project(temporary=True)
|
||||
assert os.path.exists(p.path)
|
||||
assert os.path.exists(os.path.join(p.path, ".gns3_temporary"))
|
||||
|
||||
|
||||
def test_remove_temporary_flag():
|
||||
p = Project(temporary=True)
|
||||
assert os.path.exists(p.path)
|
||||
assert os.path.exists(os.path.join(p.path, ".gns3_temporary"))
|
||||
p.temporary = False
|
||||
assert not os.path.exists(os.path.join(p.path, ".gns3_temporary"))
|
||||
|
||||
|
||||
def test_changing_location_not_allowed(tmpdir):
|
||||
with patch("gns3server.hypervisor.project.Project.is_local", return_value=False):
|
||||
with pytest.raises(aiohttp.web.HTTPForbidden):
|
||||
p = Project(location=str(tmpdir))
|
||||
|
||||
|
||||
def test_changing_path_not_allowed(tmpdir):
|
||||
with patch("gns3server.hypervisor.project.Project.is_local", return_value=False):
|
||||
with pytest.raises(aiohttp.web.HTTPForbidden):
|
||||
p = Project()
|
||||
p.path = str(tmpdir)
|
||||
|
||||
|
||||
def test_changing_path_with_quote_not_allowed(tmpdir):
|
||||
with patch("gns3server.hypervisor.project.Project.is_local", return_value=True):
|
||||
with pytest.raises(aiohttp.web.HTTPForbidden):
|
||||
p = Project()
|
||||
p.path = str(tmpdir / "project\"53")
|
||||
|
||||
|
||||
def test_json(tmpdir):
|
||||
p = Project()
|
||||
assert p.__json__() == {"name": p.name, "location": p.location, "path": p.path, "project_id": p.id, "temporary": False}
|
||||
|
||||
|
||||
def test_vm_working_directory(tmpdir, vm):
|
||||
with patch("gns3server.hypervisor.project.Project.is_local", return_value=True):
|
||||
p = Project(location=str(tmpdir))
|
||||
assert p.vm_working_directory(vm) == os.path.join(str(tmpdir), p.id, 'project-files', vm.module_name, vm.id)
|
||||
assert os.path.exists(p.vm_working_directory(vm))
|
||||
|
||||
|
||||
def test_mark_vm_for_destruction(vm):
|
||||
project = Project()
|
||||
project.add_vm(vm)
|
||||
project.mark_vm_for_destruction(vm)
|
||||
assert len(project._vms_to_destroy) == 1
|
||||
assert len(project.vms) == 0
|
||||
|
||||
|
||||
def test_commit(manager, loop):
|
||||
project = Project()
|
||||
vm = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager)
|
||||
project.add_vm(vm)
|
||||
directory = project.vm_working_directory(vm)
|
||||
project.mark_vm_for_destruction(vm)
|
||||
assert len(project._vms_to_destroy) == 1
|
||||
assert os.path.exists(directory)
|
||||
loop.run_until_complete(asyncio.async(project.commit()))
|
||||
assert len(project._vms_to_destroy) == 0
|
||||
assert os.path.exists(directory) is False
|
||||
assert len(project.vms) == 0
|
||||
|
||||
|
||||
def test_commit_permission_issue(manager, loop):
|
||||
project = Project()
|
||||
vm = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager)
|
||||
project.add_vm(vm)
|
||||
directory = project.vm_working_directory(vm)
|
||||
project.mark_vm_for_destruction(vm)
|
||||
assert len(project._vms_to_destroy) == 1
|
||||
assert os.path.exists(directory)
|
||||
os.chmod(directory, 0)
|
||||
with pytest.raises(aiohttp.web.HTTPInternalServerError):
|
||||
loop.run_until_complete(asyncio.async(project.commit()))
|
||||
os.chmod(directory, 700)
|
||||
|
||||
|
||||
def test_project_delete(loop):
|
||||
project = Project()
|
||||
directory = project.path
|
||||
assert os.path.exists(directory)
|
||||
loop.run_until_complete(asyncio.async(project.delete()))
|
||||
assert os.path.exists(directory) is False
|
||||
|
||||
|
||||
def test_project_delete_permission_issue(loop):
|
||||
project = Project()
|
||||
directory = project.path
|
||||
assert os.path.exists(directory)
|
||||
os.chmod(directory, 0)
|
||||
with pytest.raises(aiohttp.web.HTTPInternalServerError):
|
||||
loop.run_until_complete(asyncio.async(project.delete()))
|
||||
os.chmod(directory, 700)
|
||||
|
||||
|
||||
def test_project_add_vm(manager):
|
||||
project = Project()
|
||||
vm = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager)
|
||||
project.add_vm(vm)
|
||||
assert len(project.vms) == 1
|
||||
|
||||
|
||||
def test_project_close(loop, vm, project):
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.vpcs.vpcs_vm.VPCSVM.close") as mock:
|
||||
loop.run_until_complete(asyncio.async(project.close()))
|
||||
assert mock.called
|
||||
assert vm.id not in vm.manager._vms
|
||||
|
||||
|
||||
def test_project_close_temporary_project(loop, manager):
|
||||
"""A temporary project is deleted when closed"""
|
||||
|
||||
project = Project(temporary=True)
|
||||
directory = project.path
|
||||
assert os.path.exists(directory)
|
||||
loop.run_until_complete(asyncio.async(project.close()))
|
||||
assert os.path.exists(directory) is False
|
||||
|
||||
|
||||
def test_get_default_project_directory(monkeypatch):
|
||||
|
||||
monkeypatch.undo()
|
||||
project = Project()
|
||||
path = os.path.normpath(os.path.expanduser("~/GNS3/projects"))
|
||||
assert project._get_default_project_directory() == path
|
||||
assert os.path.exists(path)
|
||||
|
||||
|
||||
def test_clean_project_directory(tmpdir):
|
||||
|
||||
# A non anonymous project with uuid.
|
||||
project1 = tmpdir / uuid4()
|
||||
project1.mkdir()
|
||||
|
||||
# A non anonymous project.
|
||||
oldproject = tmpdir / uuid4()
|
||||
oldproject.mkdir()
|
||||
|
||||
# an anonymous project
|
||||
project2 = tmpdir / uuid4()
|
||||
project2.mkdir()
|
||||
tmp = (project2 / ".gns3_temporary")
|
||||
with open(str(tmp), 'w+') as f:
|
||||
f.write("1")
|
||||
|
||||
with patch("gns3server.config.Config.get_section_config", return_value={"project_directory": str(tmpdir)}):
|
||||
Project.clean_project_directory()
|
||||
|
||||
assert os.path.exists(str(project1))
|
||||
assert os.path.exists(str(oldproject))
|
||||
assert not os.path.exists(str(project2))
|
||||
|
||||
|
||||
def test_list_files(tmpdir, loop):
|
||||
|
||||
with patch("gns3server.config.Config.get_section_config", return_value={"project_directory": str(tmpdir)}):
|
||||
project = Project()
|
||||
path = project.path
|
||||
os.makedirs(os.path.join(path, "vm-1", "dynamips"))
|
||||
with open(os.path.join(path, "vm-1", "dynamips", "test.bin"), "w+") as f:
|
||||
f.write("test")
|
||||
open(os.path.join(path, "vm-1", "dynamips", "test.ghost"), "w+").close()
|
||||
with open(os.path.join(path, "test.txt"), "w+") as f:
|
||||
f.write("test2")
|
||||
|
||||
files = loop.run_until_complete(asyncio.async(project.list_files()))
|
||||
|
||||
assert files == [
|
||||
{
|
||||
"path": "test.txt",
|
||||
"md5sum": "ad0234829205b9033196ba818f7a872b"
|
||||
},
|
||||
{
|
||||
"path": os.path.join("vm-1", "dynamips", "test.bin"),
|
||||
"md5sum": "098f6bcd4621d373cade4e832627b4f6"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,32 @@
|
||||
# -*- 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 aiohttp
|
||||
import pytest
|
||||
from gns3server.hypervisor.project_manager import ProjectManager
|
||||
|
||||
|
||||
def test_create_project():
|
||||
pm = ProjectManager.instance()
|
||||
project = pm.create_project(project_id='00010203-0405-0607-0809-0a0b0c0d0e0f')
|
||||
assert project == pm.get_project('00010203-0405-0607-0809-0a0b0c0d0e0f')
|
||||
|
||||
|
||||
def test_project_not_found():
|
||||
pm = ProjectManager.instance()
|
||||
with pytest.raises(aiohttp.web.HTTPNotFound):
|
||||
pm.get_project('00010203-0405-0607-0809-000000000000')
|
||||
@@ -0,0 +1,99 @@
|
||||
# -*- 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 pytest
|
||||
import tempfile
|
||||
import os
|
||||
import stat
|
||||
import asyncio
|
||||
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from gns3server.hypervisor.virtualbox import VirtualBox
|
||||
from gns3server.hypervisor.virtualbox.virtualbox_error import VirtualBoxError
|
||||
from tests.utils import asyncio_patch
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def manager(port_manager):
|
||||
m = VirtualBox.instance()
|
||||
m.port_manager = port_manager
|
||||
return m
|
||||
|
||||
|
||||
def test_vm_invalid_vboxmanage_path(manager):
|
||||
with patch("gns3server.config.Config.get_section_config", return_value={"vboxmanage_path": "/bin/test_fake"}):
|
||||
with pytest.raises(VirtualBoxError):
|
||||
manager.find_vboxmanage()
|
||||
|
||||
|
||||
def test_vm_non_executable_vboxmanage_path(manager):
|
||||
tmpfile = tempfile.NamedTemporaryFile()
|
||||
with patch("gns3server.config.Config.get_section_config", return_value={"vboxmanage_path": tmpfile.name}):
|
||||
with pytest.raises(VirtualBoxError):
|
||||
manager.find_vboxmanage()
|
||||
|
||||
|
||||
def test_vm_invalid_executable_name_vboxmanage_path(manager, tmpdir):
|
||||
path = str(tmpdir / "vpcs")
|
||||
with open(path, "w+") as f:
|
||||
f.write(path)
|
||||
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 pytest.raises(VirtualBoxError):
|
||||
manager.find_vboxmanage()
|
||||
|
||||
|
||||
def test_vboxmanage_path(manager, tmpdir):
|
||||
path = str(tmpdir / "VBoxManage")
|
||||
with open(path, "w+") as f:
|
||||
f.write(path)
|
||||
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}):
|
||||
assert manager.find_vboxmanage() == path
|
||||
|
||||
|
||||
def test_list_images(manager, loop):
|
||||
vm_list = ['"Windows 8.1" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}',
|
||||
'"Carriage',
|
||||
'Return" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}',
|
||||
'',
|
||||
'"<inaccessible>" {42b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}',
|
||||
'"Linux Microcore 4.7.1" {ccd8c50b-c172-457d-99fa-dd69371ede0e}']
|
||||
|
||||
@asyncio.coroutine
|
||||
def execute_mock(cmd, args):
|
||||
if cmd == "list":
|
||||
return vm_list
|
||||
else:
|
||||
if args[0] == "Windows 8.1":
|
||||
return ["memory=512"]
|
||||
elif args[0] == "Linux Microcore 4.7.1":
|
||||
return ["memory=256"]
|
||||
assert False, "Unknow {} {}".format(cmd, args)
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.virtualbox.VirtualBox.execute") as mock:
|
||||
mock.side_effect = execute_mock
|
||||
vms = loop.run_until_complete(asyncio.async(manager.list_images()))
|
||||
assert vms == [
|
||||
{"vmname": "Windows 8.1", "ram": 512},
|
||||
{"vmname": "Linux Microcore 4.7.1", "ram": 256}
|
||||
]
|
||||
@@ -0,0 +1,69 @@
|
||||
# -*- 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 pytest
|
||||
import asyncio
|
||||
from tests.utils import asyncio_patch
|
||||
|
||||
from gns3server.hypervisor.virtualbox.virtualbox_vm import VirtualBoxVM
|
||||
from gns3server.hypervisor.virtualbox.virtualbox_error import VirtualBoxError
|
||||
from gns3server.hypervisor.virtualbox import VirtualBox
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def manager(port_manager):
|
||||
m = VirtualBox.instance()
|
||||
m.port_manager = port_manager
|
||||
return m
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def vm(project, manager):
|
||||
return VirtualBoxVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, "test", False)
|
||||
|
||||
|
||||
def test_vm(project, manager):
|
||||
vm = VirtualBoxVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, "test", False)
|
||||
assert vm.name == "test"
|
||||
assert vm.id == "00010203-0405-0607-0809-0a0b0c0d0e0f"
|
||||
assert vm.vmname == "test"
|
||||
|
||||
|
||||
def test_vm_valid_virtualbox_api_version(loop, project, manager):
|
||||
with asyncio_patch("gns3server.hypervisor.virtualbox.VirtualBox.execute", return_value=["API version: 4_3"]):
|
||||
vm = VirtualBoxVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, "test", False)
|
||||
loop.run_until_complete(asyncio.async(vm.create()))
|
||||
|
||||
|
||||
def test_vm_invalid_virtualbox_api_version(loop, project, manager):
|
||||
with asyncio_patch("gns3server.hypervisor.virtualbox.VirtualBox.execute", return_value=["API version: 4_2"]):
|
||||
with pytest.raises(VirtualBoxError):
|
||||
vm = VirtualBoxVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, "test", False)
|
||||
loop.run_until_complete(asyncio.async(vm.create()))
|
||||
|
||||
|
||||
def test_vm_adapter_add_nio_binding_adapter_not_exist(loop, vm, manager, free_console_port):
|
||||
nio = manager.create_nio(manager.vboxmanage_path, {"type": "nio_udp", "lport": free_console_port, "rport": free_console_port, "rhost": "192.168.1.2"})
|
||||
with pytest.raises(VirtualBoxError):
|
||||
loop.run_until_complete(asyncio.async(vm.adapter_add_nio_binding(15, nio)))
|
||||
|
||||
|
||||
def test_json(vm, tmpdir, project):
|
||||
assert vm.__json__()["vm_directory"] is None
|
||||
project._path = str(tmpdir)
|
||||
vm._linked_clone = True
|
||||
assert vm.__json__()["vm_directory"] is not None
|
||||
@@ -0,0 +1,46 @@
|
||||
# -*- 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 pytest
|
||||
import tempfile
|
||||
import os
|
||||
import stat
|
||||
import asyncio
|
||||
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from gns3server.hypervisor.vmware import VMware
|
||||
from tests.utils import asyncio_patch
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def manager(port_manager):
|
||||
m = VMware.instance()
|
||||
m.port_manager = port_manager
|
||||
return m
|
||||
|
||||
|
||||
def test_parse_vmware_file(manager, tmpdir):
|
||||
path = str(tmpdir / "test.vmx")
|
||||
with open(path, "w+") as f:
|
||||
f.write('displayname = "GNS3 VM"\nguestOS = "ubuntu-64"')
|
||||
|
||||
vmx = VMware.parse_vmware_file(path)
|
||||
assert vmx["displayname"] == "GNS3 VM"
|
||||
assert vmx["guestos"] == "ubuntu-64"
|
||||
@@ -0,0 +1,73 @@
|
||||
# -*- 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 pytest
|
||||
import asyncio
|
||||
from tests.utils import asyncio_patch
|
||||
|
||||
from gns3server.hypervisor.vmware.vmware_vm import VMwareVM
|
||||
from gns3server.hypervisor.vmware.vmware_error import VMwareError
|
||||
from gns3server.hypervisor.vmware import VMware
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def manager(port_manager):
|
||||
m = VMware.instance()
|
||||
m.port_manager = port_manager
|
||||
return m
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def vm(project, manager, tmpdir):
|
||||
fake_vmx = str(tmpdir / "test.vmx")
|
||||
open(fake_vmx, "w+").close()
|
||||
|
||||
return VMwareVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, fake_vmx, False)
|
||||
|
||||
|
||||
def test_vm(project, manager, vm):
|
||||
assert vm.name == "test"
|
||||
assert vm.id == "00010203-0405-0607-0809-0a0b0c0d0e0f"
|
||||
|
||||
|
||||
def test_json(vm, tmpdir, project):
|
||||
assert vm.__json__()["vm_directory"] is not None
|
||||
project._path = str(tmpdir)
|
||||
vm._linked_clone = True
|
||||
assert vm.__json__()["vm_directory"] is not None
|
||||
|
||||
|
||||
def test_start_capture(vm, tmpdir, manager, free_console_port, loop):
|
||||
|
||||
output_file = str(tmpdir / "test.pcap")
|
||||
nio = manager.create_nio(0, {"type": "nio_udp", "lport": free_console_port, "rport": free_console_port, "rhost": "127.0.0.1"})
|
||||
vm.adapters = 1
|
||||
loop.run_until_complete(asyncio.async(vm.adapter_add_nio_binding(0, nio)))
|
||||
loop.run_until_complete(asyncio.async(vm.start_capture(0, output_file)))
|
||||
assert vm._ethernet_adapters[0].get_nio(0).capturing
|
||||
|
||||
|
||||
def test_stop_capture(vm, tmpdir, manager, free_console_port, loop):
|
||||
|
||||
output_file = str(tmpdir / "test.pcap")
|
||||
nio = manager.create_nio(0, {"type": "nio_udp", "lport": free_console_port, "rport": free_console_port, "rhost": "127.0.0.1"})
|
||||
vm.adapters = 1
|
||||
loop.run_until_complete(asyncio.async(vm.adapter_add_nio_binding(0, nio)))
|
||||
loop.run_until_complete(vm.start_capture(0, output_file))
|
||||
assert vm._ethernet_adapters[0].get_nio(0).capturing
|
||||
loop.run_until_complete(asyncio.async(vm.stop_capture(0)))
|
||||
assert vm._ethernet_adapters[0].get_nio(0).capturing is False
|
||||
@@ -0,0 +1,73 @@
|
||||
# -*- 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 pytest
|
||||
import uuid
|
||||
|
||||
|
||||
from gns3server.hypervisor.vpcs import VPCS
|
||||
from gns3server.hypervisor.vpcs.vpcs_error import VPCSError
|
||||
from gns3server.hypervisor.project_manager import ProjectManager
|
||||
|
||||
|
||||
def test_get_mac_id(loop, project, port_manager):
|
||||
# Cleanup the VPCS object
|
||||
VPCS._instance = None
|
||||
vpcs = VPCS.instance()
|
||||
vpcs.port_manager = port_manager
|
||||
vm1_id = str(uuid.uuid4())
|
||||
vm2_id = str(uuid.uuid4())
|
||||
vm3_id = str(uuid.uuid4())
|
||||
loop.run_until_complete(vpcs.create_vm("PC 1", project.id, vm1_id))
|
||||
loop.run_until_complete(vpcs.create_vm("PC 2", 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(vm2_id) == 1
|
||||
loop.run_until_complete(vpcs.delete_vm(vm1_id))
|
||||
loop.run_until_complete(vpcs.create_vm("PC 3", project.id, vm3_id))
|
||||
assert vpcs.get_mac_id(vm3_id) == 0
|
||||
|
||||
|
||||
def test_get_mac_id_multiple_project(loop, port_manager):
|
||||
# Cleanup the VPCS object
|
||||
VPCS._instance = None
|
||||
vpcs = VPCS.instance()
|
||||
vpcs.port_manager = port_manager
|
||||
vm1_id = str(uuid.uuid4())
|
||||
vm2_id = str(uuid.uuid4())
|
||||
vm3_id = str(uuid.uuid4())
|
||||
project1 = ProjectManager.instance().create_project()
|
||||
project2 = ProjectManager.instance().create_project()
|
||||
loop.run_until_complete(vpcs.create_vm("PC 1", project1.id, vm1_id))
|
||||
loop.run_until_complete(vpcs.create_vm("PC 2", project1.id, vm2_id))
|
||||
loop.run_until_complete(vpcs.create_vm("PC 2", project2.id, vm3_id))
|
||||
assert vpcs.get_mac_id(vm1_id) == 0
|
||||
assert vpcs.get_mac_id(vm2_id) == 1
|
||||
assert vpcs.get_mac_id(vm3_id) == 0
|
||||
|
||||
|
||||
def test_get_mac_id_no_id_available(loop, project, port_manager):
|
||||
# Cleanup the VPCS object
|
||||
VPCS._instance = None
|
||||
vpcs = VPCS.instance()
|
||||
vpcs.port_manager = port_manager
|
||||
with pytest.raises(VPCSError):
|
||||
for i in range(0, 256):
|
||||
vm_id = str(uuid.uuid4())
|
||||
loop.run_until_complete(vpcs.create_vm("PC {}".format(i), project.id, vm_id))
|
||||
assert vpcs.get_mac_id(vm_id) == i
|
||||
@@ -0,0 +1,292 @@
|
||||
# -*- 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 pytest
|
||||
import aiohttp
|
||||
import asyncio
|
||||
import os
|
||||
import sys
|
||||
|
||||
from tests.utils import asyncio_patch
|
||||
from pkg_resources import parse_version
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
from gns3server.hypervisor.vpcs.vpcs_vm import VPCSVM
|
||||
from gns3server.hypervisor.vpcs.vpcs_error import VPCSError
|
||||
from gns3server.hypervisor.vpcs import VPCS
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def manager(port_manager):
|
||||
m = VPCS.instance()
|
||||
m.port_manager = port_manager
|
||||
return m
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def vm(project, manager):
|
||||
vm = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager)
|
||||
vm._vpcs_version = parse_version("0.9")
|
||||
return vm
|
||||
|
||||
|
||||
def test_vm(project, manager):
|
||||
vm = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager)
|
||||
assert vm.name == "test"
|
||||
assert vm.id == "00010203-0405-0607-0809-0a0b0c0d0e0f"
|
||||
|
||||
|
||||
def test_vm_check_vpcs_version(loop, vm, manager):
|
||||
with asyncio_patch("gns3server.hypervisor.vpcs.vpcs_vm.subprocess_check_output", return_value="Welcome to Virtual PC Simulator, version 0.9"):
|
||||
loop.run_until_complete(asyncio.async(vm._check_vpcs_version()))
|
||||
assert vm._vpcs_version == parse_version("0.9")
|
||||
|
||||
|
||||
def test_vm_check_vpcs_version_0_6_1(loop, vm, manager):
|
||||
with asyncio_patch("gns3server.hypervisor.vpcs.vpcs_vm.subprocess_check_output", return_value="Welcome to Virtual PC Simulator, version 0.6.1"):
|
||||
loop.run_until_complete(asyncio.async(vm._check_vpcs_version()))
|
||||
assert vm._vpcs_version == parse_version("0.6.1")
|
||||
|
||||
|
||||
def test_vm_invalid_vpcs_version(loop, manager, vm):
|
||||
with asyncio_patch("gns3server.hypervisor.vpcs.vpcs_vm.subprocess_check_output", return_value="Welcome to Virtual PC Simulator, version 0.1"):
|
||||
with pytest.raises(VPCSError):
|
||||
nio = manager.create_nio(vm.vpcs_path, {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"})
|
||||
vm.port_add_nio_binding(0, nio)
|
||||
loop.run_until_complete(asyncio.async(vm._check_vpcs_version()))
|
||||
assert vm.name == "test"
|
||||
assert vm.id == "00010203-0405-0607-0809-0a0b0c0d0e0f"
|
||||
|
||||
|
||||
def test_vm_invalid_vpcs_path(vm, manager, loop):
|
||||
with asyncio_patch("gns3server.hypervisor.vpcs.vpcs_vm.VPCSVM.vpcs_path", return_value="/tmp/fake/path/vpcs"):
|
||||
with pytest.raises(VPCSError):
|
||||
nio = manager.create_nio(vm.vpcs_path, {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"})
|
||||
vm.port_add_nio_binding(0, nio)
|
||||
loop.run_until_complete(asyncio.async(vm.start()))
|
||||
assert vm.name == "test"
|
||||
assert vm.id == "00010203-0405-0607-0809-0a0b0c0d0e0e"
|
||||
|
||||
|
||||
def test_start(loop, vm):
|
||||
process = MagicMock()
|
||||
process.returncode = None
|
||||
queue = vm.project.get_listen_queue()
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.vpcs.vpcs_vm.VPCSVM._check_requirements", return_value=True):
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=process) as mock_exec:
|
||||
nio = VPCS.instance().create_nio(vm.vpcs_path, {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"})
|
||||
vm.port_add_nio_binding(0, nio)
|
||||
loop.run_until_complete(asyncio.async(vm.start()))
|
||||
assert mock_exec.call_args[0] == (vm.vpcs_path,
|
||||
'-p',
|
||||
str(vm.console),
|
||||
'-m', '1',
|
||||
'-i',
|
||||
'1',
|
||||
'-F',
|
||||
'-R',
|
||||
'-s',
|
||||
'4242',
|
||||
'-c',
|
||||
'4243',
|
||||
'-t',
|
||||
'127.0.0.1')
|
||||
assert vm.is_running()
|
||||
assert vm.command_line == ' '.join(mock_exec.call_args[0])
|
||||
(action, event) = queue.get_nowait()
|
||||
assert action == "vm.started"
|
||||
assert event == vm
|
||||
|
||||
|
||||
def test_start_0_6_1(loop, vm):
|
||||
"""
|
||||
Version 0.6.1 doesn't have the -R options. It's not require
|
||||
because GNS3 provide a patch for this.
|
||||
"""
|
||||
process = MagicMock()
|
||||
process.returncode = None
|
||||
queue = vm.project.get_listen_queue()
|
||||
vm._vpcs_version = parse_version("0.6.1")
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.vpcs.vpcs_vm.VPCSVM._check_requirements", return_value=True):
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=process) as mock_exec:
|
||||
nio = VPCS.instance().create_nio(vm.vpcs_path, {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"})
|
||||
vm.port_add_nio_binding(0, nio)
|
||||
loop.run_until_complete(asyncio.async(vm.start()))
|
||||
assert mock_exec.call_args[0] == (vm.vpcs_path,
|
||||
'-p',
|
||||
str(vm.console),
|
||||
'-m', '1',
|
||||
'-i',
|
||||
'1',
|
||||
'-F',
|
||||
'-s',
|
||||
'4242',
|
||||
'-c',
|
||||
'4243',
|
||||
'-t',
|
||||
'127.0.0.1')
|
||||
assert vm.is_running()
|
||||
(action, event) = queue.get_nowait()
|
||||
assert action == "vm.started"
|
||||
assert event == vm
|
||||
|
||||
|
||||
def test_stop(loop, vm):
|
||||
process = MagicMock()
|
||||
|
||||
# Wait process kill success
|
||||
future = asyncio.Future()
|
||||
future.set_result(True)
|
||||
process.wait.return_value = future
|
||||
process.returncode = None
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.vpcs.vpcs_vm.VPCSVM._check_requirements", return_value=True):
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=process):
|
||||
nio = VPCS.instance().create_nio(vm.vpcs_path, {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"})
|
||||
vm.port_add_nio_binding(0, nio)
|
||||
|
||||
loop.run_until_complete(asyncio.async(vm.start()))
|
||||
assert vm.is_running()
|
||||
|
||||
queue = vm.project.get_listen_queue()
|
||||
|
||||
with asyncio_patch("gns3server.utils.asyncio.wait_for_process_termination"):
|
||||
loop.run_until_complete(asyncio.async(vm.stop()))
|
||||
assert vm.is_running() is False
|
||||
|
||||
if sys.platform.startswith("win"):
|
||||
process.send_signal.assert_called_with(1)
|
||||
else:
|
||||
process.terminate.assert_called_with()
|
||||
|
||||
(action, event) = queue.get_nowait()
|
||||
assert action == "vm.stopped"
|
||||
assert event == vm
|
||||
|
||||
|
||||
def test_reload(loop, vm):
|
||||
process = MagicMock()
|
||||
|
||||
# Wait process kill success
|
||||
future = asyncio.Future()
|
||||
future.set_result(True)
|
||||
process.wait.return_value = future
|
||||
process.returncode = None
|
||||
|
||||
with asyncio_patch("gns3server.hypervisor.vpcs.vpcs_vm.VPCSVM._check_requirements", return_value=True):
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=process):
|
||||
nio = VPCS.instance().create_nio(vm.vpcs_path, {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"})
|
||||
vm.port_add_nio_binding(0, nio)
|
||||
loop.run_until_complete(asyncio.async(vm.start()))
|
||||
assert vm.is_running()
|
||||
|
||||
with asyncio_patch("gns3server.utils.asyncio.wait_for_process_termination"):
|
||||
loop.run_until_complete(asyncio.async(vm.reload()))
|
||||
assert vm.is_running() is True
|
||||
|
||||
if sys.platform.startswith("win"):
|
||||
process.send_signal.assert_called_with(1)
|
||||
else:
|
||||
process.terminate.assert_called_with()
|
||||
|
||||
|
||||
def test_add_nio_binding_udp(vm):
|
||||
nio = VPCS.instance().create_nio(vm.vpcs_path, {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"})
|
||||
vm.port_add_nio_binding(0, nio)
|
||||
assert nio.lport == 4242
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
|
||||
def test_add_nio_binding_tap(vm, ethernet_device):
|
||||
with patch("gns3server.hypervisor.base_manager.BaseManager.has_privileged_access", return_value=True):
|
||||
nio = VPCS.instance().create_nio(vm.vpcs_path, {"type": "nio_tap", "tap_device": ethernet_device})
|
||||
vm.port_add_nio_binding(0, nio)
|
||||
assert nio.tap_device == ethernet_device
|
||||
|
||||
|
||||
# def test_add_nio_binding_tap_no_privileged_access(vm):
|
||||
# with patch("gns3server.hypervisor.base_manager.BaseManager.has_privileged_access", return_value=False):
|
||||
# with pytest.raises(aiohttp.web.HTTPForbidden):
|
||||
# nio = VPCS.instance().create_nio(vm.vpcs_path, {"type": "nio_tap", "tap_device": "test"})
|
||||
# vm.port_add_nio_binding(0, nio)
|
||||
# assert vm._ethernet_adapter.ports[0] is None
|
||||
#
|
||||
|
||||
def test_port_remove_nio_binding(vm):
|
||||
nio = VPCS.instance().create_nio(vm.vpcs_path, {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"})
|
||||
vm.port_add_nio_binding(0, nio)
|
||||
vm.port_remove_nio_binding(0)
|
||||
assert vm._ethernet_adapter.ports[0] is None
|
||||
|
||||
|
||||
def test_update_startup_script(vm):
|
||||
content = "echo GNS3 VPCS\nip 192.168.1.2\n"
|
||||
vm.startup_script = content
|
||||
filepath = os.path.join(vm.working_dir, 'startup.vpc')
|
||||
assert os.path.exists(filepath)
|
||||
with open(filepath) as f:
|
||||
assert f.read() == content
|
||||
|
||||
|
||||
def test_update_startup_script_h(vm):
|
||||
content = "setname %h\n"
|
||||
vm.name = "pc1"
|
||||
vm.startup_script = content
|
||||
assert os.path.exists(vm.script_file)
|
||||
with open(vm.script_file) as f:
|
||||
assert f.read() == "setname pc1\n"
|
||||
|
||||
|
||||
def test_get_startup_script(vm):
|
||||
content = "echo GNS3 VPCS\nip 192.168.1.2"
|
||||
vm.startup_script = content
|
||||
assert vm.startup_script == os.linesep.join(["echo GNS3 VPCS", "ip 192.168.1.2"])
|
||||
|
||||
|
||||
def test_get_startup_script_using_default_script(vm):
|
||||
content = "echo GNS3 VPCS\nip 192.168.1.2\n"
|
||||
|
||||
# Reset script file location
|
||||
vm._script_file = None
|
||||
|
||||
filepath = os.path.join(vm.working_dir, 'startup.vpc')
|
||||
with open(filepath, 'wb+') as f:
|
||||
assert f.write(content.encode("utf-8"))
|
||||
|
||||
assert vm.startup_script == content
|
||||
assert vm.script_file == filepath
|
||||
|
||||
|
||||
def test_change_name(vm, tmpdir):
|
||||
path = os.path.join(vm.working_dir, 'startup.vpc')
|
||||
vm.name = "world"
|
||||
with open(path, 'w+') as f:
|
||||
f.write("name world")
|
||||
vm.name = "hello"
|
||||
assert vm.name == "hello"
|
||||
with open(path) as f:
|
||||
assert f.read() == "name hello"
|
||||
|
||||
|
||||
def test_close(vm, port_manager, loop):
|
||||
with asyncio_patch("gns3server.hypervisor.vpcs.vpcs_vm.VPCSVM._check_requirements", return_value=True):
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()):
|
||||
vm.start()
|
||||
loop.run_until_complete(asyncio.async(vm.close()))
|
||||
assert vm.is_running() is False
|
||||
Reference in new issue
Block a user