1
0
mirror of https://github.com/GNS3/gns3-server synced 2024-11-24 09:18:08 +00:00

Merge latest changes from the POC.

This commit is contained in:
Jeremy 2015-01-13 18:26:32 -07:00
parent 6d20a122f8
commit 369cd06279
9 changed files with 245 additions and 97 deletions

View File

@ -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
} }

View File

@ -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

View File

@ -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

View 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

View 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

View 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()

View File

@ -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

View 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

View File

@ -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'