diff --git a/.gitignore b/.gitignore index c2cb81d9..d63cf29f 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,4 @@ docs/_build #VPCS vpcs.hist +startup.vpcs diff --git a/docs/api/examples/post_vpcs.txt b/docs/api/examples/post_vpcs.txt index 1e529a62..bcd8a539 100644 --- a/docs/api/examples/post_vpcs.txt +++ b/docs/api/examples/post_vpcs.txt @@ -9,7 +9,7 @@ POST /vpcs HTTP/1.1 HTTP/1.1 200 CONNECTION: close -CONTENT-LENGTH: 185 +CONTENT-LENGTH: 213 CONTENT-TYPE: application/json DATE: Thu, 08 Jan 2015 16:09:15 GMT SERVER: Python/3.4 aiohttp/0.13.1 @@ -20,5 +20,6 @@ X-ROUTE: /vpcs "name": "PC TEST 1", "project_uuid": "a1e920ca-338a-4e9f-b363-aa607b09dd80", "script_file": null, - "uuid": "21e9a130-450e-41e4-8864-e5c83ba7aa80" + "startup_script": null, + "uuid": "009a7260-e44c-4349-8df7-08668a3c4e17" } diff --git a/gns3server/handlers/vpcs_handler.py b/gns3server/handlers/vpcs_handler.py index 5d4e9ac8..007b95dd 100644 --- a/gns3server/handlers/vpcs_handler.py +++ b/gns3server/handlers/vpcs_handler.py @@ -46,7 +46,8 @@ class VPCSHandler: request.json["project_uuid"], request.json.get("uuid"), console=request.json.get("console"), - script_file=request.json.get("script_file")) + script_file=request.json.get("script_file"), + startup_script=request.json.get("startup_script")) response.json(vm) @classmethod diff --git a/gns3server/modules/vpcs/vpcs_vm.py b/gns3server/modules/vpcs/vpcs_vm.py index c92f703c..cabc6df4 100644 --- a/gns3server/modules/vpcs/vpcs_vm.py +++ b/gns3server/modules/vpcs/vpcs_vm.py @@ -54,9 +54,10 @@ class VPCSVM(BaseVM): :param manager: parent VM Manager :param console: TCP console port :param script_file: A VPCS startup script + :param startup_script: Content of vpcs startup script file """ - def __init__(self, name, uuid, project, manager, console=None, script_file=None): + def __init__(self, name, uuid, project, manager, console=None, script_file=None, startup_script=None): super().__init__(name, uuid, project, manager) @@ -71,6 +72,8 @@ class VPCSVM(BaseVM): # VPCS settings self._script_file = script_file + if startup_script is not None: + self.startup_script = startup_script self._ethernet_adapter = EthernetAdapter() # one adapter with 1 Ethernet interface if self._console is not None: @@ -107,7 +110,8 @@ class VPCSVM(BaseVM): "uuid": self._uuid, "console": self._console, "project_uuid": self.project.uuid, - "script_file": self.script_file} + "script_file": self.script_file, + "startup_script": self.startup_script} @property def console(self): @@ -146,6 +150,33 @@ class VPCSVM(BaseVM): new_name=new_name)) BaseVM.name = new_name + @property + def startup_script(self): + """Return the content of the current startup script""" + if self._script_file is None: + return None + try: + with open(self._script_file) as f: + return f.read() + except OSError as e: + raise VPCSError("Can't read VPCS startup file '{}'".format(self._script_file)) + + @startup_script.setter + def startup_script(self, startup_script): + """ + Update the startup script + + :param startup_script The content of the vpcs startup script + """ + + if self._script_file is None: + self._script_file = os.path.join(self.working_dir, 'startup.vpcs') + try: + with open(self._script_file, '+w') as f: + f.write(startup_script) + except OSError as e: + raise VPCSError("Can't write VPCS startup file '{}'".format(self._script_file)) + @asyncio.coroutine def _check_vpcs_version(self): """ @@ -165,8 +196,8 @@ class VPCSVM(BaseVM): @asyncio.coroutine def _get_vpcs_welcome(self): - proc = yield from asyncio.create_subprocess_exec(' '.join([self._path, "-v"]), stdout=asyncio.subprocess.PIPE, cwd=self.working_dir) - out = yield from proc.stdout.readline() + proc = yield from asyncio.create_subprocess_exec(self._path, "-v", stdout=asyncio.subprocess.PIPE, cwd=self.working_dir) + out = yield from proc.stdout.read() return out.decode("utf-8") @asyncio.coroutine @@ -357,8 +388,6 @@ class VPCSVM(BaseVM): nio = self._ethernet_adapter.get_nio(0) if nio: - print(nio) - print(isinstance(nio, NIO_UDP)) if isinstance(nio, NIO_UDP): # UDP tunnel command.extend(["-s", str(nio.lport)]) # source UDP port diff --git a/gns3server/schemas/vpcs.py b/gns3server/schemas/vpcs.py index ea54ee0c..a0190ee6 100644 --- a/gns3server/schemas/vpcs.py +++ b/gns3server/schemas/vpcs.py @@ -54,6 +54,10 @@ VPCS_CREATE_SCHEMA = { "description": "VPCS startup script", "type": ["string", "null"] }, + "startup_script": { + "description": "Content of the VPCS startup script", + "type": ["string", "null"] + }, }, "additionalProperties": False, "required": ["name", "project_uuid"] @@ -152,6 +156,10 @@ VPCS_OBJECT_SCHEMA = { "description": "VPCS startup script", "type": ["string", "null"] }, + "startup_script": { + "description": "Content of the VPCS startup script", + "type": ["string", "null"] + }, }, "additionalProperties": False, "required": ["name", "uuid", "console", "project_uuid"] diff --git a/tests/api/test_vpcs.py b/tests/api/test_vpcs.py index 0df6882f..7b13e389 100644 --- a/tests/api/test_vpcs.py +++ b/tests/api/test_vpcs.py @@ -16,6 +16,7 @@ # along with this program. If not, see . import pytest +import os from tests.api.base import server, loop, project from tests.utils import asyncio_patch from unittest.mock import patch @@ -38,13 +39,25 @@ def test_vpcs_create(server, project): assert response.json["script_file"] is None -def test_vpcs_create_script_file(server, project): - response = server.post("/vpcs", {"name": "PC TEST 1", "project_uuid": project.uuid, "script_file": "/tmp/test"}) +def test_vpcs_create_script_file(server, project, tmpdir): + path = os.path.join(str(tmpdir), "test") + with open(path, 'w+') as f: + f.write("ip 192.168.1.2") + response = server.post("/vpcs", {"name": "PC TEST 1", "project_uuid": project.uuid, "script_file": path}) assert response.status == 200 assert response.route == "/vpcs" assert response.json["name"] == "PC TEST 1" assert response.json["project_uuid"] == project.uuid - assert response.json["script_file"] == "/tmp/test" + assert response.json["script_file"] == path + + +def test_vpcs_create_startup_script(server, project): + response = server.post("/vpcs", {"name": "PC TEST 1", "project_uuid": project.uuid, "startup_script": "ip 192.168.1.2\necho TEST"}) + assert response.status == 200 + assert response.route == "/vpcs" + assert response.json["name"] == "PC TEST 1" + assert response.json["project_uuid"] == project.uuid + assert response.json["startup_script"] == "ip 192.168.1.2\necho TEST" def test_vpcs_create_port(server, project): diff --git a/tests/modules/vpcs/test_vpcs_vm.py b/tests/modules/vpcs/test_vpcs_vm.py index 4e4f5c96..a26b99b9 100644 --- a/tests/modules/vpcs/test_vpcs_vm.py +++ b/tests/modules/vpcs/test_vpcs_vm.py @@ -17,6 +17,7 @@ import pytest import asyncio +import os from tests.utils import asyncio_patch # TODO: Move loop to util @@ -111,3 +112,27 @@ def test_port_remove_nio_binding(vm): nio = vm.port_add_nio_binding(0, {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) 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.vpcs') + assert os.path.exists(filepath) + with open(filepath) as f: + assert f.read() == content + + +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.vpcs') + assert os.path.exists(filepath) + with open(filepath) as f: + assert f.read() == content + + +def test_get_startup_script(vm): + content = "echo GNS3 VPCS\nip 192.168.1.2\n" + vm.startup_script = content + assert vm.startup_script == content