mirror of
https://github.com/GNS3/gns3-server
synced 2024-11-28 11:18:11 +00:00
Merge latest changes from the POC.
This commit is contained in:
parent
6d20a122f8
commit
369cd06279
@ -8,7 +8,7 @@ POST /vpcs HTTP/1.1
|
|||||||
|
|
||||||
HTTP/1.1 200
|
HTTP/1.1 200
|
||||||
CONNECTION: close
|
CONNECTION: close
|
||||||
CONTENT-LENGTH: 67
|
CONTENT-LENGTH: 66
|
||||||
CONTENT-TYPE: application/json
|
CONTENT-TYPE: application/json
|
||||||
DATE: Thu, 08 Jan 2015 16:09:15 GMT
|
DATE: Thu, 08 Jan 2015 16:09:15 GMT
|
||||||
SERVER: Python/3.4 aiohttp/0.13.1
|
SERVER: Python/3.4 aiohttp/0.13.1
|
||||||
@ -17,5 +17,5 @@ X-ROUTE: /vpcs
|
|||||||
{
|
{
|
||||||
"console": 4242,
|
"console": 4242,
|
||||||
"name": "PC TEST 1",
|
"name": "PC TEST 1",
|
||||||
"vpcs_id": 42
|
"vpcs_id": 1
|
||||||
}
|
}
|
||||||
|
@ -16,12 +16,10 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from ..web.route import Route
|
from ..web.route import Route
|
||||||
from ..modules.vpcs import VPCS
|
|
||||||
|
|
||||||
# schemas
|
|
||||||
from ..schemas.vpcs import VPCS_CREATE_SCHEMA
|
from ..schemas.vpcs import VPCS_CREATE_SCHEMA
|
||||||
from ..schemas.vpcs import VPCS_OBJECT_SCHEMA
|
from ..schemas.vpcs import VPCS_OBJECT_SCHEMA
|
||||||
from ..schemas.vpcs import VPCS_ADD_NIO_SCHEMA
|
from ..schemas.vpcs import VPCS_ADD_NIO_SCHEMA
|
||||||
|
from ..modules import VPCS
|
||||||
|
|
||||||
|
|
||||||
class VPCSHandler(object):
|
class VPCSHandler(object):
|
||||||
@ -40,9 +38,9 @@ class VPCSHandler(object):
|
|||||||
output=VPCS_OBJECT_SCHEMA)
|
output=VPCS_OBJECT_SCHEMA)
|
||||||
def create(request, response):
|
def create(request, response):
|
||||||
vpcs = VPCS.instance()
|
vpcs = VPCS.instance()
|
||||||
i = yield from vpcs.create(request.json)
|
vm = yield from vpcs.create_vm(request.json['name'])
|
||||||
response.json({'name': request.json['name'],
|
response.json({'name': vm.name,
|
||||||
"vpcs_id": i,
|
"vpcs_id": vm.id,
|
||||||
"console": 4242})
|
"console": 4242})
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -1,66 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# Copyright (C) 2015 GNS3 Technologies Inc.
|
|
||||||
#
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
import asyncio
|
|
||||||
|
|
||||||
|
|
||||||
#TODO: make this more generic (not json but *args?)
|
|
||||||
class BaseModule(object):
|
|
||||||
_instance = None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def instance(cls):
|
|
||||||
if cls._instance is None:
|
|
||||||
cls._instance = cls()
|
|
||||||
asyncio.async(cls._instance.run())
|
|
||||||
return cls._instance
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self._queue = asyncio.Queue()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def destroy(cls):
|
|
||||||
future = asyncio.Future()
|
|
||||||
cls._instance._queue.put_nowait((future, None, ))
|
|
||||||
yield from asyncio.wait([future])
|
|
||||||
cls._instance = None
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
|
||||||
def put(self, json):
|
|
||||||
|
|
||||||
future = asyncio.Future()
|
|
||||||
self._queue.put_nowait((future, json, ))
|
|
||||||
yield from asyncio.wait([future])
|
|
||||||
return future.result()
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
|
||||||
def run(self):
|
|
||||||
|
|
||||||
while True:
|
|
||||||
future, json = yield from self._queue.get()
|
|
||||||
if json is None:
|
|
||||||
future.set_result(True)
|
|
||||||
break
|
|
||||||
try:
|
|
||||||
result = yield from self.process(json)
|
|
||||||
future.set_result(result)
|
|
||||||
except Exception as e:
|
|
||||||
future.set_exception(e)
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
|
||||||
def process(self, json):
|
|
||||||
raise NotImplementedError
|
|
99
gns3server/modules/base_vm.py
Normal file
99
gns3server/modules/base_vm.py
Normal file
@ -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 asyncio
|
||||||
|
from .vm_error import VMError
|
||||||
|
|
||||||
|
|
||||||
|
class BaseVM:
|
||||||
|
|
||||||
|
def __init__(self, name, identifier):
|
||||||
|
self._queue = asyncio.Queue()
|
||||||
|
self._name = name
|
||||||
|
self._id = identifier
|
||||||
|
self._created = asyncio.Future()
|
||||||
|
self._worker = asyncio.async(self._run())
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
"""
|
||||||
|
Returns the unique ID for this VM.
|
||||||
|
|
||||||
|
:returns: id (integer)
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self._id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""
|
||||||
|
Returns the name for this VM.
|
||||||
|
|
||||||
|
:returns: name (string)
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def _execute(self, subcommand, args):
|
||||||
|
"""Called when we receive an event"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def _create(self):
|
||||||
|
"""Called when the run loop start"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def _run(self, timeout=60):
|
||||||
|
|
||||||
|
try:
|
||||||
|
yield from self._create()
|
||||||
|
self._created.set_result(True)
|
||||||
|
except VMError as e:
|
||||||
|
self._created.set_exception(e)
|
||||||
|
return
|
||||||
|
|
||||||
|
while True:
|
||||||
|
future, subcommand, args = yield from self._queue.get()
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
yield from asyncio.wait_for(self._execute(subcommand, args), timeout=timeout)
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
raise VMError("{} has timed out after {} seconds!".format(subcommand, timeout))
|
||||||
|
future.set_result(True)
|
||||||
|
except Exception as e:
|
||||||
|
future.set_exception(e)
|
||||||
|
|
||||||
|
def wait_for_creation(self):
|
||||||
|
return self._created
|
||||||
|
|
||||||
|
def put(self, *args):
|
||||||
|
"""
|
||||||
|
Add to the processing queue of the VM
|
||||||
|
|
||||||
|
:returns: future
|
||||||
|
"""
|
||||||
|
|
||||||
|
future = asyncio.Future()
|
||||||
|
try:
|
||||||
|
args.insert(0, future)
|
||||||
|
self._queue.put_nowait(args)
|
||||||
|
except asyncio.qeues.QueueFull:
|
||||||
|
raise VMError("Queue is full")
|
||||||
|
return future
|
20
gns3server/modules/vm_error.py
Normal file
20
gns3server/modules/vm_error.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# -*- 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/>.
|
||||||
|
|
||||||
|
|
||||||
|
class VMError(Exception):
|
||||||
|
pass
|
89
gns3server/modules/vm_manager.py
Normal file
89
gns3server/modules/vm_manager.py
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
# -*- 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 asyncio
|
||||||
|
import aiohttp
|
||||||
|
|
||||||
|
from .vm_error import VMError
|
||||||
|
|
||||||
|
|
||||||
|
class VMManager:
|
||||||
|
"""
|
||||||
|
Base class for all VMManager.
|
||||||
|
Responsible of management of a VM pool
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._vms = {}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def instance(cls):
|
||||||
|
"""
|
||||||
|
Singleton to return only one instance of Manager.
|
||||||
|
|
||||||
|
:returns: instance of Manager
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not hasattr(cls, "_instance"):
|
||||||
|
cls._instance = cls()
|
||||||
|
return cls._instance
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@asyncio.coroutine
|
||||||
|
def destroy(cls):
|
||||||
|
cls._instance = None
|
||||||
|
|
||||||
|
def _get_vm_instance(self, vm_id):
|
||||||
|
"""
|
||||||
|
Returns a VM instance.
|
||||||
|
|
||||||
|
:param vm_id: VM identifier
|
||||||
|
|
||||||
|
:returns: VM instance
|
||||||
|
"""
|
||||||
|
|
||||||
|
if vm_id not in self._vms:
|
||||||
|
raise aiohttp.web.HTTPNotFound(text="ID {} doesn't exist".format(vm_id))
|
||||||
|
return self._vms[vm_id]
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def create_vm(self, vmname, identifier=None):
|
||||||
|
if not identifier:
|
||||||
|
for i in range(1, 1024):
|
||||||
|
if i not in self._vms:
|
||||||
|
identifier = i
|
||||||
|
break
|
||||||
|
if identifier == 0:
|
||||||
|
raise VMError("Maximum number of VM instances reached")
|
||||||
|
else:
|
||||||
|
if identifier in self._vms:
|
||||||
|
raise VMError("VM identifier {} is already used by another VM instance".format(identifier))
|
||||||
|
vm = self._VM_CLASS(vmname, identifier)
|
||||||
|
yield from vm.wait_for_creation()
|
||||||
|
self._vms[vm.id] = vm
|
||||||
|
return vm
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def start_vm(self, vm_id):
|
||||||
|
vm = self._get_vm_instance(vm_id)
|
||||||
|
yield from vm.start()
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def stop_vm(self, vm_id):
|
||||||
|
vm = self._get_vm_instance(vm_id)
|
||||||
|
yield from vm.stop()
|
@ -19,19 +19,12 @@
|
|||||||
VPCS server module.
|
VPCS server module.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import asyncio
|
from ..vm_manager import VMManager
|
||||||
|
from .vpcs_device import VPCSDevice
|
||||||
from ..base import BaseModule
|
|
||||||
|
|
||||||
|
|
||||||
class VPCS(BaseModule):
|
class VPCS(VMManager):
|
||||||
|
_VM_CLASS = VPCSDevice
|
||||||
|
|
||||||
@asyncio.coroutine
|
def create_vm(self, name):
|
||||||
def process(self, json):
|
return super().create_vm(name)
|
||||||
yield from asyncio.sleep(1)
|
|
||||||
return 42
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
|
||||||
def create(self, json):
|
|
||||||
i = yield from self.put(json)
|
|
||||||
return i
|
|
||||||
|
23
gns3server/modules/vpcs/vpcs_device.py
Normal file
23
gns3server/modules/vpcs/vpcs_device.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# -*- 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/>.
|
||||||
|
|
||||||
|
|
||||||
|
from ..base_vm import BaseVM
|
||||||
|
|
||||||
|
|
||||||
|
class VPCSDevice(BaseVM):
|
||||||
|
pass
|
@ -20,16 +20,8 @@ from tests.utils import asyncio_patch
|
|||||||
from gns3server import modules
|
from gns3server import modules
|
||||||
|
|
||||||
|
|
||||||
def test_vpcs_create(server):
|
@asyncio_patch('gns3server.modules.VPCS.create_vm', return_value=84)
|
||||||
response = server.post('/vpcs', {'name': 'PC TEST 1'}, example=True)
|
def test_vpcs_create(server, mock):
|
||||||
assert response.status == 200
|
|
||||||
assert response.route == '/vpcs'
|
|
||||||
assert response.json['name'] == 'PC TEST 1'
|
|
||||||
assert response.json['vpcs_id'] == 42
|
|
||||||
|
|
||||||
|
|
||||||
@asyncio_patch('demoserver.modules.VPCS.create', return_value=84)
|
|
||||||
def test_vpcs_mock(server, mock):
|
|
||||||
response = server.post('/vpcs', {'name': 'PC TEST 1'}, example=False)
|
response = server.post('/vpcs', {'name': 'PC TEST 1'}, example=False)
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
assert response.route == '/vpcs'
|
assert response.route == '/vpcs'
|
||||||
|
Loading…
Reference in New Issue
Block a user