Update VPCS instance

This commit is contained in:
Julien Duponchelle 2015-01-21 21:46:16 +01:00
parent 7abb426d04
commit 368d1ff70b
13 changed files with 144 additions and 29 deletions

View File

@ -18,5 +18,5 @@ X-ROUTE: /vpcs/{uuid}
"project_uuid": "a1e920ca-338a-4e9f-b363-aa607b09dd80",
"script_file": null,
"startup_script": null,
"uuid": "40f76457-de2b-4399-8853-a35393a72a2d"
"uuid": "f8155d67-c0bf-4229-be4c-97edaaae7b0b"
}

View File

@ -21,5 +21,5 @@ X-ROUTE: /vpcs
"project_uuid": "a1e920ca-338a-4e9f-b363-aa607b09dd80",
"script_file": null,
"startup_script": null,
"uuid": "a022aa0d-acab-4554-b2a6-6e6f51c9d65e"
"uuid": "5a9aac64-5b62-41bd-955a-fcef90a2fac5"
}

View File

@ -17,6 +17,7 @@
from ..web.route import Route
from ..schemas.vpcs import VPCS_CREATE_SCHEMA
from ..schemas.vpcs import VPCS_UPDATE_SCHEMA
from ..schemas.vpcs import VPCS_OBJECT_SCHEMA
from ..schemas.vpcs import VPCS_NIO_SCHEMA
from ..modules.vpcs import VPCS
@ -67,6 +68,27 @@ class VPCSHandler:
vm = vpcs_manager.get_vm(request.match_info["uuid"])
response.json(vm)
@classmethod
@Route.put(
r"/vpcs/{uuid}",
status_codes={
200: "VPCS instance updated",
409: "Conflict"
},
description="Update a VPCS instance",
input=VPCS_UPDATE_SCHEMA,
output=VPCS_OBJECT_SCHEMA)
def update(request, response):
vpcs_manager = VPCS.instance()
vm = vpcs_manager.get_vm(request.match_info["uuid"])
vm.name = request.json.get("name", vm.name)
vm.console = request.json.get("console", vm.console)
vm.script_file = request.json.get("script_file", vm.script_file)
vm.startup_script = request.json.get("startup_script", vm.startup_script)
response.json(vm)
@classmethod
@Route.post(
r"/vpcs/{uuid}/start",

View File

@ -59,6 +59,10 @@ class BaseVM:
:param new_name: name
"""
log.info("{module} {name} [{uuid}]: renamed to {new_name}".format(module=self.module_name,
name=self._name,
uuid=self.uuid,
new_name=new_name))
self._name = new_name
@property

View File

@ -123,7 +123,17 @@ class VPCSVM(BaseVM):
return self._console
# FIXME: correct way to subclass a property?
@console.setter
def console(self, console):
"""
Change console port
:params console: Console port (integer)
"""
if self._console:
self._manager.port_manager.release_console_port(self._console)
self._console = self._manager.port_manager.reserve_console_port(console)
@BaseVM.name.setter
def name(self, new_name):
"""
@ -133,22 +143,11 @@ class VPCSVM(BaseVM):
"""
if self._script_file:
# update the startup.vpc
config_path = os.path.join(self.working_dir, "startup.vpc")
if os.path.isfile(config_path):
try:
with open(config_path, "r+", errors="replace") as f:
old_config = f.read()
new_config = old_config.replace(self._name, new_name)
f.seek(0)
f.write(new_config)
except OSError as e:
raise VPCSError("Could not amend the configuration {}: {}".format(config_path, e))
content = self.startup_script
content = content.replace(self._name, new_name)
self.startup_script = content
log.info("VPCS {name} [{uuid}]: renamed to {new_name}".format(name=self._name,
uuid=self.uuid,
new_name=new_name))
BaseVM.name = new_name
super(VPCSVM, VPCSVM).name.__set__(self, new_name)
@property
def startup_script(self):
@ -173,7 +172,10 @@ class VPCSVM(BaseVM):
self._script_file = os.path.join(self.working_dir, 'startup.vpcs')
try:
with open(self._script_file, '+w') as f:
f.write(startup_script)
if startup_script is None:
f.write('')
else:
f.write(startup_script)
except OSError as e:
raise VPCSError("Can't write VPCS startup file '{}'".format(self._script_file))

View File

@ -63,6 +63,33 @@ VPCS_CREATE_SCHEMA = {
"required": ["name", "project_uuid"]
}
VPCS_UPDATE_SCHEMA = {
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "Request validation to update a VPCS instance",
"type": "object",
"properties": {
"name": {
"description": "VPCS device name",
"type": ["string", "null"],
"minLength": 1,
},
"console": {
"description": "console TCP port",
"minimum": 1,
"maximum": 65535,
"type": ["integer", "null"]
},
"script_file": {
"description": "VPCS startup script",
"type": ["string", "null"]
},
"startup_script": {
"description": "Content of the VPCS startup script",
"type": ["string", "null"]
},
},
"additionalProperties": False,
}
VPCS_NIO_SCHEMA = {
"$schema": "http://json-schema.org/draft-04/schema#",

View File

@ -45,6 +45,9 @@ class Query:
def post(self, path, body={}, **kwargs):
return self._fetch("POST", path, body, **kwargs)
def put(self, path, body={}, **kwargs):
return self._fetch("PUT", path, body, **kwargs)
def get(self, path, **kwargs):
return self._fetch("GET", path, **kwargs)
@ -147,11 +150,10 @@ def loop(request):
@pytest.fixture(scope="session")
def server(request, loop):
def server(request, loop, port_manager):
port = _get_unused_port()
host = "localhost"
app = web.Application()
port_manager = PortManager("127.0.0.1", False)
for method, route, handler in Route.get_routes():
app.router.add_route(method, route, handler)
for module in MODULES:

View File

@ -20,7 +20,7 @@ This test suite check /project endpoint
"""
from tests.utils import asyncio_patch
from tests.utils import asyncio_patch, port_manager
from tests.api.base import server, loop
from gns3server.version import __version__

View File

@ -20,7 +20,7 @@ This test suite check /version endpoint
It's also used for unittest the HTTP implementation.
"""
from tests.utils import asyncio_patch
from tests.utils import asyncio_patch, port_manager
from tests.api.base import server, loop
from gns3server.version import __version__

View File

@ -16,7 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from tests.api.base import server, loop, project
from tests.utils import asyncio_patch
from tests.utils import asyncio_patch, port_manager
from gns3server.modules.virtualbox.virtualbox_vm import VirtualBoxVM

View File

@ -18,7 +18,7 @@
import pytest
import os
from tests.api.base import server, loop, project
from tests.utils import asyncio_patch
from tests.utils import asyncio_patch, free_console_port, port_manager
from unittest.mock import patch, Mock
from gns3server.modules.vpcs.vpcs_vm import VPCSVM
@ -68,13 +68,13 @@ def test_vpcs_create_startup_script(server, project):
assert response.json["startup_script"] == "ip 192.168.1.2\necho TEST"
def test_vpcs_create_port(server, project):
response = server.post("/vpcs", {"name": "PC TEST 1", "project_uuid": project.uuid, "console": 4242})
def test_vpcs_create_port(server, project, free_console_port):
response = server.post("/vpcs", {"name": "PC TEST 1", "project_uuid": project.uuid, "console": free_console_port})
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["console"] == 4242
assert response.json["console"] == free_console_port
def test_vpcs_nio_create_udp(server, vm):
@ -119,3 +119,18 @@ def test_vpcs_stop(server, vm):
response = server.post("/vpcs/{}/stop".format(vm["uuid"]))
assert mock.called
assert response.status == 200
def test_vpcs_update(server, vm, tmpdir, free_console_port):
path = os.path.join(str(tmpdir), 'startup2.vpcs')
with open(path, 'w+') as f:
f.write(path)
response = server.put("/vpcs/{}".format(vm["uuid"]), {"name": "test",
"console": free_console_port,
"script_file": path,
"startup_script": "ip 192.168.1.1"})
assert response.status == 200
assert response.json["name"] == "test"
assert response.json["console"] == free_console_port
assert response.json["script_file"] == path
assert response.json["startup_script"] == "ip 192.168.1.1"

View File

@ -18,7 +18,7 @@
import pytest
import asyncio
import os
from tests.utils import asyncio_patch
from tests.utils import asyncio_patch, port_manager, free_console_port
# TODO: Move loop to util
from tests.api.base import loop, project
@ -136,3 +136,28 @@ 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
def test_change_console_port(vm, free_console_port):
vm.console = free_console_port
vm.console = free_console_port + 1
assert vm.console == free_console_port
PortManager.instance().reserve_console_port(free_console_port + 1)
def test_change_name(vm, tmpdir):
path = os.path.join(str(tmpdir), 'startup.vpcs')
vm.name = "world"
with open(path, 'w+') as f:
f.write("name world")
vm.script_file = path
vm.name = "hello"
assert vm.name == "hello"
with open(path) as f:
assert f.read() == "name hello"
def test_change_script_file(vm, tmpdir):
path = os.path.join(str(tmpdir), 'startup2.vpcs')
vm.script_file = path
assert vm.script_file == path

View File

@ -16,7 +16,10 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import asyncio
import pytest
from unittest.mock import patch
from gns3server.modules.project_manager import ProjectManager
from gns3server.modules.port_manager import PortManager
class _asyncio_patch:
@ -54,3 +57,18 @@ class _asyncio_patch:
def asyncio_patch(function, *args, **kwargs):
return _asyncio_patch(function, *args, **kwargs)
@pytest.fixture(scope="session")
def port_manager():
return PortManager("127.0.0.1", False)
@pytest.fixture(scope="function")
def free_console_port(request, port_manager):
# In case of already use ports we will raise an exception
port = port_manager.get_free_console_port()
# We release the port immediately in order to allow
# the test do whatever the test want
port_manager.release_console_port(port)
return port