mirror of
https://github.com/GNS3/gns3-server
synced 2024-11-28 03:08:14 +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
|
||||
CONNECTION: close
|
||||
CONTENT-LENGTH: 67
|
||||
CONTENT-LENGTH: 66
|
||||
CONTENT-TYPE: application/json
|
||||
DATE: Thu, 08 Jan 2015 16:09:15 GMT
|
||||
SERVER: Python/3.4 aiohttp/0.13.1
|
||||
@ -17,5 +17,5 @@ X-ROUTE: /vpcs
|
||||
{
|
||||
"console": 4242,
|
||||
"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/>.
|
||||
|
||||
from ..web.route import Route
|
||||
from ..modules.vpcs import VPCS
|
||||
|
||||
# schemas
|
||||
from ..schemas.vpcs import VPCS_CREATE_SCHEMA
|
||||
from ..schemas.vpcs import VPCS_OBJECT_SCHEMA
|
||||
from ..schemas.vpcs import VPCS_ADD_NIO_SCHEMA
|
||||
from ..modules import VPCS
|
||||
|
||||
|
||||
class VPCSHandler(object):
|
||||
@ -40,9 +38,9 @@ class VPCSHandler(object):
|
||||
output=VPCS_OBJECT_SCHEMA)
|
||||
def create(request, response):
|
||||
vpcs = VPCS.instance()
|
||||
i = yield from vpcs.create(request.json)
|
||||
response.json({'name': request.json['name'],
|
||||
"vpcs_id": i,
|
||||
vm = yield from vpcs.create_vm(request.json['name'])
|
||||
response.json({'name': vm.name,
|
||||
"vpcs_id": vm.id,
|
||||
"console": 4242})
|
||||
|
||||
@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.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
|
||||
from ..base import BaseModule
|
||||
from ..vm_manager import VMManager
|
||||
from .vpcs_device import VPCSDevice
|
||||
|
||||
|
||||
class VPCS(BaseModule):
|
||||
class VPCS(VMManager):
|
||||
_VM_CLASS = VPCSDevice
|
||||
|
||||
@asyncio.coroutine
|
||||
def process(self, json):
|
||||
yield from asyncio.sleep(1)
|
||||
return 42
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self, json):
|
||||
i = yield from self.put(json)
|
||||
return i
|
||||
def create_vm(self, name):
|
||||
return super().create_vm(name)
|
||||
|
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
|
||||
|
||||
|
||||
def test_vpcs_create(server):
|
||||
response = server.post('/vpcs', {'name': 'PC TEST 1'}, example=True)
|
||||
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):
|
||||
@asyncio_patch('gns3server.modules.VPCS.create_vm', return_value=84)
|
||||
def test_vpcs_create(server, mock):
|
||||
response = server.post('/vpcs', {'name': 'PC TEST 1'}, example=False)
|
||||
assert response.status == 200
|
||||
assert response.route == '/vpcs'
|
||||
|
Loading…
Reference in New Issue
Block a user