diff --git a/gns3server/controller/vm.py b/gns3server/controller/vm.py index 3ec469c5..1c90c956 100644 --- a/gns3server/controller/vm.py +++ b/gns3server/controller/vm.py @@ -83,7 +83,21 @@ class VM: data["name"] = self._name data["console"] = self._console data["console_type"] = self._console_type - yield from self._hypervisor.post("/projects/{}/{}/vms".format(self._project.id, self._vm_type), data=data) + + # None properties should be send. Because it can mean the emulator doesn't support it + for key in list(data.keys()): + if data[key] is None: + del data[key] + + response = yield from self._hypervisor.post("/projects/{}/{}/vms".format(self._project.id, self._vm_type), data=data) + + for key, value in response.json.items(): + if key == "console": + self._console = value + elif key in ["console_type", "name", "vm_id"]: + pass + else: + self._properties[key] = value @asyncio.coroutine def post(self, path, data={}): diff --git a/gns3server/hypervisor/dynamips/nodes/router.py b/gns3server/hypervisor/dynamips/nodes/router.py index 0b301d0a..50a45734 100644 --- a/gns3server/hypervisor/dynamips/nodes/router.py +++ b/gns3server/hypervisor/dynamips/nodes/router.py @@ -133,6 +133,7 @@ class Router(BaseVM): "disk1": self._disk1, "auto_delete_disks": self._auto_delete_disks, "console": self.console, + "console_type": "telnet", "aux": self.aux, "mac_addr": self._mac_addr, "system_id": self._system_id} diff --git a/gns3server/schemas/dynamips_vm.py b/gns3server/schemas/dynamips_vm.py index 9bf2f121..d59a02aa 100644 --- a/gns3server/schemas/dynamips_vm.py +++ b/gns3server/schemas/dynamips_vm.py @@ -135,6 +135,10 @@ VM_CREATE_SCHEMA = { "minimum": 1, "maximum": 65535 }, + "console_type": { + "description": "console type", + "enum": ["telnet"] + }, "aux": { "description": "auxiliary console TCP port", "type": "integer", @@ -361,6 +365,10 @@ VM_UPDATE_SCHEMA = { "minimum": 1, "maximum": 65535 }, + "console_type": { + "description": "console type", + "enum": ["telnet"] + }, "aux": { "description": "auxiliary console TCP port", "type": "integer", @@ -609,6 +617,10 @@ VM_OBJECT_SCHEMA = { "minimum": 1, "maximum": 65535 }, + "console_type": { + "description": "console type", + "enum": ["telnet"] + }, "aux": { "description": "auxiliary console TCP port", "type": ["integer", "null"], @@ -737,7 +749,7 @@ VM_OBJECT_SCHEMA = { }, }, "additionalProperties": False, - "required": ["name", "vm_id", "project_id", "dynamips_id"] + "required": ["name", "vm_id", "project_id", "dynamips_id", "console", "console_type"] } VM_CONFIGS_SCHEMA = { diff --git a/tests/controller/test_project.py b/tests/controller/test_project.py index 10adb62e..23cc8101 100644 --- a/tests/controller/test_project.py +++ b/tests/controller/test_project.py @@ -19,6 +19,7 @@ import pytest import aiohttp from unittest.mock import MagicMock +from tests.utils import AsyncioMagicMock from gns3server.controller.project import Project @@ -40,10 +41,15 @@ def test_json(tmpdir): def test_addVM(async_run): hypervisor = MagicMock() project = Project() + + response = MagicMock() + response.json = {"console": 2048} + hypervisor.post = AsyncioMagicMock(return_value=response) + vm = async_run(project.addVM(hypervisor, None, name="test", vm_type="vpcs", properties={"startup_config": "test.cfg"})) + hypervisor.post.assert_called_with('/projects/{}/vpcs/vms'.format(project.id), - data={'console': None, - 'vm_id': vm.id, + data={'vm_id': vm.id, 'console_type': 'telnet', 'startup_config': 'test.cfg', 'name': 'test'}) @@ -52,6 +58,11 @@ def test_addVM(async_run): def test_getVM(async_run): hypervisor = MagicMock() project = Project() + + response = MagicMock() + response.json = {"console": 2048} + hypervisor.post = AsyncioMagicMock(return_value=response) + vm = async_run(project.addVM(hypervisor, None, name="test", vm_type="vpcs", properties={"startup_config": "test.cfg"})) assert project.getVM(vm.id) == vm @@ -62,6 +73,11 @@ def test_getVM(async_run): def test_addLink(async_run): hypervisor = MagicMock() project = Project() + + response = MagicMock() + response.json = {"console": 2048} + hypervisor.post = AsyncioMagicMock(return_value=response) + vm1 = async_run(project.addVM(hypervisor, None, name="test1", vm_type="vpcs", properties={"startup_config": "test.cfg"})) vm2 = async_run(project.addVM(hypervisor, None, name="test2", vm_type="vpcs", properties={"startup_config": "test.cfg"})) link = async_run(project.addLink()) @@ -73,6 +89,11 @@ def test_addLink(async_run): def test_getLink(async_run): hypervisor = MagicMock() project = Project() + + response = MagicMock() + response.json = {"console": 2048} + hypervisor.post = AsyncioMagicMock(return_value=response) + link = async_run(project.addLink()) assert project.getLink(link.id) == link diff --git a/tests/controller/test_vm.py b/tests/controller/test_vm.py index be7de1f6..0759a7e5 100644 --- a/tests/controller/test_vm.py +++ b/tests/controller/test_vm.py @@ -17,16 +17,19 @@ import pytest import uuid +import asyncio from unittest.mock import MagicMock +from tests.utils import AsyncioMagicMock + from gns3server.controller.vm import VM from gns3server.controller.project import Project @pytest.fixture def hypervisor(): - s = MagicMock() + s = AsyncioMagicMock() s.id = "http://test.com:42" return s @@ -64,15 +67,44 @@ def test_init_without_uuid(project, hypervisor): def test_create(vm, hypervisor, project, async_run): + vm._console = 2048 + + response = MagicMock() + response.json = {"console": 2048} + hypervisor.post = AsyncioMagicMock(return_value=response) + async_run(vm.create()) data = { - "console": None, + "console": 2048, "console_type": "vnc", "vm_id": vm.id, "startup_script": "echo test", "name": "demo" } hypervisor.post.assert_called_with("/projects/{}/vpcs/vms".format(vm.project.id), data=data) + assert vm._console == 2048 + assert vm._properties == {"startup_script": "echo test"} + + +def test_create_without_console(vm, hypervisor, project, async_run): + """ + None properties should be send. Because it can mean the emulator doesn"t support it + """ + + response = MagicMock() + response.json = {"console": 2048, "test_value": "success"} + hypervisor.post = AsyncioMagicMock(return_value=response) + + async_run(vm.create()) + data = { + "console_type": "vnc", + "vm_id": vm.id, + "startup_script": "echo test", + "name": "demo" + } + hypervisor.post.assert_called_with("/projects/{}/vpcs/vms".format(vm.project.id), data=data) + assert vm._console == 2048 + assert vm._properties == {"test_value": "success", "startup_script": "echo test"} def test_post(vm, hypervisor, async_run): diff --git a/tests/handlers/api/controller/test_link.py b/tests/handlers/api/controller/test_link.py index 0e3a8538..335d74f8 100644 --- a/tests/handlers/api/controller/test_link.py +++ b/tests/handlers/api/controller/test_link.py @@ -27,7 +27,7 @@ import pytest from unittest.mock import patch, MagicMock, PropertyMock -from tests.utils import asyncio_patch +from tests.utils import asyncio_patch, AsyncioMagicMock from gns3server.handlers.api.controller.project_handler import ProjectHandler from gns3server.controller import Controller @@ -49,6 +49,10 @@ def project(http_controller, async_run): def test_create_link(http_controller, tmpdir, project, hypervisor, async_run): + response = MagicMock() + response.json = {"console": 2048} + hypervisor.post = AsyncioMagicMock(return_value=response) + vm1 = async_run(project.addVM(hypervisor, None)) vm2 = async_run(project.addVM(hypervisor, None)) diff --git a/tests/handlers/api/controller/test_vm.py b/tests/handlers/api/controller/test_vm.py index bd69e0c2..f181c14b 100644 --- a/tests/handlers/api/controller/test_vm.py +++ b/tests/handlers/api/controller/test_vm.py @@ -26,8 +26,8 @@ import aiohttp import pytest -from unittest.mock import patch, MagicMock, PropertyMock -from tests.utils import asyncio_patch +from unittest.mock import patch, MagicMock +from tests.utils import asyncio_patch, AsyncioMagicMock from gns3server.handlers.api.controller.project_handler import ProjectHandler from gns3server.controller import Controller @@ -47,12 +47,16 @@ def project(http_controller, async_run): def test_create_vm(http_controller, tmpdir, project, hypervisor): + response = MagicMock() + response.json = {"console": 2048} + hypervisor.post = AsyncioMagicMock(return_value=response) + response = http_controller.post("/projects/{}/vms".format(project.id), { "name": "test", "vm_type": "vpcs", "hypervisor_id": "example.com", "properties": { - "startup_script": "echo test" + "startup_script": "echo test" } }, example=True) assert response.status == 201 diff --git a/tests/utils.py b/tests/utils.py index 507f35a3..a20dc98c 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -16,7 +16,7 @@ # along with this program. If not, see . import asyncio -from unittest.mock import patch, MagicMock +import unittest.mock class _asyncio_patch: @@ -38,7 +38,7 @@ class _asyncio_patch: def __enter__(self): """Used when enter in the with block""" - self._patcher = patch(self.function, return_value=self._fake_anwser()) + self._patcher = unittest.mock.patch(self.function, return_value=self._fake_anwser()) mock_class = self._patcher.start() return mock_class @@ -64,13 +64,26 @@ def asyncio_patch(function, *args, **kwargs): return _asyncio_patch(function, *args, **kwargs) -class AsyncioMagicMock(MagicMock): +class AsyncioMagicMock(unittest.mock.MagicMock): """ Magic mock returning coroutine """ + def __init__(self, return_value=None, **kwargs): if return_value: future = asyncio.Future() future.set_result(return_value) kwargs["return_value"] = future super().__init__(**kwargs) + + def _get_child_mock(self, **kw): + """Create the child mocks for attributes and return value. + By default child mocks will be the same type as the parent. + Subclasses of Mock may want to override this to customize the way + child mocks are made. + For non-callable mocks the callable variant will be used (rather than + any custom subclass). + + Original code: https://github.com/python/cpython/blob/121f86338111e49c547a55eb7f26db919bfcbde9/Lib/unittest/mock.py + """ + return AsyncioMagicMock(**kw)