mirror of
https://github.com/GNS3/gns3-server
synced 2025-01-01 11:40:56 +00:00
Polishing base server implementation
This commit is contained in:
parent
f4e51ea74f
commit
2f23a092e3
0
gns3server/handlers/__init__.py
Normal file
0
gns3server/handlers/__init__.py
Normal file
@ -22,10 +22,10 @@ STOMP protocol over Websockets
|
|||||||
import zmq
|
import zmq
|
||||||
import uuid
|
import uuid
|
||||||
import tornado.websocket
|
import tornado.websocket
|
||||||
from .version import __version__
|
|
||||||
from tornado.escape import json_decode
|
from tornado.escape import json_decode
|
||||||
from .stomp import frame as stomp_frame
|
from ..version import __version__
|
||||||
from .stomp import protocol as stomp_protocol
|
from ..stomp import frame as stomp_frame
|
||||||
|
from ..stomp import protocol as stomp_protocol
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -48,12 +48,15 @@ class StompWebSocket(tornado.websocket.WebSocketHandler):
|
|||||||
def __init__(self, application, request, zmq_router):
|
def __init__(self, application, request, zmq_router):
|
||||||
tornado.websocket.WebSocketHandler.__init__(self, application, request)
|
tornado.websocket.WebSocketHandler.__init__(self, application, request)
|
||||||
self._session_id = str(uuid.uuid4())
|
self._session_id = str(uuid.uuid4())
|
||||||
|
self._connected = False
|
||||||
self.zmq_router = zmq_router
|
self.zmq_router = zmq_router
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def session_id(self):
|
def session_id(self):
|
||||||
"""
|
"""
|
||||||
Session ID uniquely representing a Websocket client
|
Session ID uniquely representing a Websocket client
|
||||||
|
|
||||||
|
:returns: the session id
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self._session_id
|
return self._session_id
|
||||||
@ -63,7 +66,7 @@ class StompWebSocket(tornado.websocket.WebSocketHandler):
|
|||||||
"""
|
"""
|
||||||
Sends a message to Websocket client
|
Sends a message to Websocket client
|
||||||
|
|
||||||
:param message: message from a module
|
:param message: message from a module (received via ZeroMQ)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Module name that is replying
|
# Module name that is replying
|
||||||
@ -117,6 +120,7 @@ class StompWebSocket(tornado.websocket.WebSocketHandler):
|
|||||||
else:
|
else:
|
||||||
self.write_message(self.stomp.connected(self.session_id,
|
self.write_message(self.stomp.connected(self.session_id,
|
||||||
'gns3server/' + __version__))
|
'gns3server/' + __version__))
|
||||||
|
self._connected = True
|
||||||
|
|
||||||
def stomp_handle_send(self, frame):
|
def stomp_handle_send(self, frame):
|
||||||
"""
|
"""
|
||||||
@ -212,6 +216,11 @@ class StompWebSocket(tornado.websocket.WebSocketHandler):
|
|||||||
if frame.cmd == stomp_protocol.CMD_STOMP or frame.cmd == stomp_protocol.CMD_CONNECT:
|
if frame.cmd == stomp_protocol.CMD_STOMP or frame.cmd == stomp_protocol.CMD_CONNECT:
|
||||||
self.stomp_handle_connect(frame)
|
self.stomp_handle_connect(frame)
|
||||||
|
|
||||||
|
# Do not enforce that the client must have send a
|
||||||
|
# STOMP CONNECT frame for now (need to refactor unit tests)
|
||||||
|
#elif not self._connected:
|
||||||
|
# self.stomp_error("Not connected")
|
||||||
|
|
||||||
elif frame.cmd == stomp_protocol.CMD_SEND:
|
elif frame.cmd == stomp_protocol.CMD_SEND:
|
||||||
self.stomp_handle_send(frame)
|
self.stomp_handle_send(frame)
|
||||||
|
|
26
gns3server/handlers/version_handler.py
Normal file
26
gns3server/handlers/version_handler.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright (C) 2013 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 tornado.web
|
||||||
|
from ..version import __version__
|
||||||
|
|
||||||
|
|
||||||
|
class VersionHandler(tornado.web.RequestHandler):
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
response = {'version': __version__}
|
||||||
|
self.write(response)
|
@ -47,7 +47,6 @@ class Module(object):
|
|||||||
def name(self, new_name):
|
def name(self, new_name):
|
||||||
self._name = new_name
|
self._name = new_name
|
||||||
|
|
||||||
#@property
|
|
||||||
def cls(self):
|
def cls(self):
|
||||||
return self._cls
|
return self._cls
|
||||||
|
|
||||||
@ -93,7 +92,7 @@ class ModuleManager(object):
|
|||||||
"""
|
"""
|
||||||
Returns all modules.
|
Returns all modules.
|
||||||
|
|
||||||
:return: list of Module objects
|
:returns: list of Module objects
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self._modules
|
return self._modules
|
||||||
@ -105,7 +104,8 @@ class ModuleManager(object):
|
|||||||
:param module: module to activate (Module object)
|
:param module: module to activate (Module object)
|
||||||
:param args: args passed to the module
|
:param args: args passed to the module
|
||||||
:param kwargs: kwargs passed to the module
|
:param kwargs: kwargs passed to the module
|
||||||
:return: instantiated module class
|
|
||||||
|
:returns: instantiated module class
|
||||||
"""
|
"""
|
||||||
|
|
||||||
module_class = module.cls()
|
module_class = module.cls()
|
||||||
|
@ -15,6 +15,10 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Base class (interface) for modules
|
||||||
|
"""
|
||||||
|
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import zmq
|
import zmq
|
||||||
|
|
||||||
@ -44,16 +48,16 @@ class IModule(multiprocessing.Process):
|
|||||||
self._current_session = None
|
self._current_session = None
|
||||||
self._current_destination = None
|
self._current_destination = None
|
||||||
|
|
||||||
def setup(self):
|
def _setup(self):
|
||||||
"""
|
"""
|
||||||
Sets up PyZMQ and creates the stream to handle requests
|
Sets up PyZMQ and creates the stream to handle requests
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self._context = zmq.Context()
|
self._context = zmq.Context()
|
||||||
self._ioloop = zmq.eventloop.ioloop.IOLoop.instance()
|
self._ioloop = zmq.eventloop.ioloop.IOLoop.instance()
|
||||||
self._stream = self.create_stream(self._host, self._port, self.decode_request)
|
self._stream = self._create_stream(self._host, self._port, self._decode_request)
|
||||||
|
|
||||||
def create_stream(self, host=None, port=0, callback=None):
|
def _create_stream(self, host=None, port=0, callback=None):
|
||||||
"""
|
"""
|
||||||
Creates a new ZMQ stream
|
Creates a new ZMQ stream
|
||||||
"""
|
"""
|
||||||
@ -82,10 +86,10 @@ class IModule(multiprocessing.Process):
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""
|
"""
|
||||||
Sets up everything and starts the event loop
|
Starts the event loop
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.setup()
|
self._setup()
|
||||||
try:
|
try:
|
||||||
self._ioloop.start()
|
self._ioloop.start()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
@ -102,6 +106,8 @@ class IModule(multiprocessing.Process):
|
|||||||
def send_response(self, response):
|
def send_response(self, response):
|
||||||
"""
|
"""
|
||||||
Sends a response back to the requester
|
Sends a response back to the requester
|
||||||
|
|
||||||
|
:param response:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# add session and destination to the response
|
# add session and destination to the response
|
||||||
@ -109,9 +115,11 @@ class IModule(multiprocessing.Process):
|
|||||||
log.debug("ZeroMQ client ({}) sending: {}".format(self.name, response))
|
log.debug("ZeroMQ client ({}) sending: {}".format(self.name, response))
|
||||||
self._stream.send_json(response)
|
self._stream.send_json(response)
|
||||||
|
|
||||||
def decode_request(self, request):
|
def _decode_request(self, request):
|
||||||
"""
|
"""
|
||||||
Decodes the request to JSON
|
Decodes the request to JSON
|
||||||
|
|
||||||
|
:param request: request from ZeroMQ server
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -132,7 +140,9 @@ class IModule(multiprocessing.Process):
|
|||||||
|
|
||||||
def destinations(self):
|
def destinations(self):
|
||||||
"""
|
"""
|
||||||
Channels handled by this modules.
|
Destinations handled by this module.
|
||||||
|
|
||||||
|
:returns: list of destinations
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.destination.keys()
|
return self.destination.keys()
|
||||||
@ -141,6 +151,8 @@ class IModule(multiprocessing.Process):
|
|||||||
def route(cls, destination):
|
def route(cls, destination):
|
||||||
"""
|
"""
|
||||||
Decorator to register a destination routed to a method
|
Decorator to register a destination routed to a method
|
||||||
|
|
||||||
|
:param destination: destination to be routed
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def wrapper(method):
|
def wrapper(method):
|
||||||
|
50
gns3server/modules/dynamips/__init__.py
Normal file
50
gns3server/modules/dynamips/__init__.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright (C) 2013 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 gns3server.modules import IModule
|
||||||
|
from .hypervisor import Hypervisor
|
||||||
|
from .hypervisor_manager import HypervisorManager
|
||||||
|
from .dynamips_error import DynamipsError
|
||||||
|
from .nodes.router import Router
|
||||||
|
|
||||||
|
import logging
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Dynamips(IModule):
|
||||||
|
|
||||||
|
def __init__(self, name=None, args=(), kwargs={}):
|
||||||
|
IModule.__init__(self, name=name, args=args, kwargs=kwargs)
|
||||||
|
#self._hypervisor_manager = HypervisorManager("/usr/bin/dynamips", "/tmp")
|
||||||
|
|
||||||
|
@IModule.route("dynamips/echo")
|
||||||
|
def echo(self, request):
|
||||||
|
print("Echo!")
|
||||||
|
log.debug("received request {}".format(request))
|
||||||
|
self.send_response(request)
|
||||||
|
|
||||||
|
@IModule.route("dynamips/create_vm")
|
||||||
|
def create_vm(self, request):
|
||||||
|
print("Create VM!")
|
||||||
|
log.debug("received request {}".format(request))
|
||||||
|
self.send_response(request)
|
||||||
|
|
||||||
|
@IModule.route("dynamips/start_vm")
|
||||||
|
def start_vm(self, request):
|
||||||
|
print("Start VM!")
|
||||||
|
log.debug("received request {}".format(request))
|
||||||
|
self.send_response(request)
|
@ -15,31 +15,29 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Set up and run the server
|
||||||
|
"""
|
||||||
|
|
||||||
import zmq
|
import zmq
|
||||||
from zmq.eventloop import ioloop, zmqstream
|
from zmq.eventloop import ioloop, zmqstream
|
||||||
ioloop.install()
|
ioloop.install()
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import errno
|
||||||
import functools
|
import functools
|
||||||
import socket
|
import socket
|
||||||
import tornado.ioloop
|
import tornado.ioloop
|
||||||
import tornado.web
|
import tornado.web
|
||||||
import tornado.autoreload
|
import tornado.autoreload
|
||||||
from .version import __version__
|
from .handlers.stomp_websocket import StompWebSocket
|
||||||
from .stomp_websocket import StompWebSocket
|
from .handlers.version_handler import VersionHandler
|
||||||
from .module_manager import ModuleManager
|
from .module_manager import ModuleManager
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class VersionHandler(tornado.web.RequestHandler):
|
|
||||||
|
|
||||||
def get(self):
|
|
||||||
response = {'version': __version__}
|
|
||||||
self.write(response)
|
|
||||||
|
|
||||||
|
|
||||||
class Server(object):
|
class Server(object):
|
||||||
|
|
||||||
# built-in handlers
|
# built-in handlers
|
||||||
@ -57,7 +55,8 @@ class Server(object):
|
|||||||
self._modules = []
|
self._modules = []
|
||||||
|
|
||||||
def load_modules(self):
|
def load_modules(self):
|
||||||
"""Loads the plugins
|
"""
|
||||||
|
Loads the modules
|
||||||
"""
|
"""
|
||||||
|
|
||||||
cwd = os.path.dirname(os.path.abspath(__file__))
|
cwd = os.path.dirname(os.path.abspath(__file__))
|
||||||
@ -86,7 +85,7 @@ class Server(object):
|
|||||||
print("Starting server on port {}".format(self._port))
|
print("Starting server on port {}".format(self._port))
|
||||||
tornado_app.listen(self._port)
|
tornado_app.listen(self._port)
|
||||||
except socket.error as e:
|
except socket.error as e:
|
||||||
if e.errno is 48: # socket already in use
|
if e.errno == errno.EADDRINUSE: # socket already in use
|
||||||
logging.critical("socket in use for port {}".format(self._port))
|
logging.critical("socket in use for port {}".format(self._port))
|
||||||
raise SystemExit
|
raise SystemExit
|
||||||
|
|
||||||
@ -106,7 +105,7 @@ class Server(object):
|
|||||||
Creates the ZeroMQ router socket to send
|
Creates the ZeroMQ router socket to send
|
||||||
requests to modules.
|
requests to modules.
|
||||||
|
|
||||||
:returns: ZeroMQ socket
|
:returns: ZeroMQ router socket
|
||||||
"""
|
"""
|
||||||
|
|
||||||
context = zmq.Context()
|
context = zmq.Context()
|
||||||
|
@ -106,6 +106,7 @@ class Frame(object):
|
|||||||
|
|
||||||
:param lines: Frame preamble lines
|
:param lines: Frame preamble lines
|
||||||
:param offset: To start parsing at the given offset
|
:param offset: To start parsing at the given offset
|
||||||
|
|
||||||
:returns: Headers in dict header:value
|
:returns: Headers in dict header:value
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -124,6 +125,7 @@ class Frame(object):
|
|||||||
Parses a frame
|
Parses a frame
|
||||||
|
|
||||||
:params frame: The frame data to be parsed
|
:params frame: The frame data to be parsed
|
||||||
|
|
||||||
:returns: STOMP Frame object
|
:returns: STOMP Frame object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -85,6 +85,7 @@ class serverProtocol(object):
|
|||||||
|
|
||||||
:param session: A session identifier that uniquely identifies the session.
|
:param session: A session identifier that uniquely identifies the session.
|
||||||
:param server: A field that contains information about the STOMP server.
|
:param server: A field that contains information about the STOMP server.
|
||||||
|
|
||||||
:returns: STOMP Frame object
|
:returns: STOMP Frame object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -109,6 +110,7 @@ class serverProtocol(object):
|
|||||||
:param body: Data to be added in the frame body
|
:param body: Data to be added in the frame body
|
||||||
:param content_type: MIME type which describes the format of the body
|
:param content_type: MIME type which describes the format of the body
|
||||||
:param message_id: Unique identifier for that message
|
:param message_id: Unique identifier for that message
|
||||||
|
|
||||||
:returns: STOMP Frame object
|
:returns: STOMP Frame object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -133,6 +135,7 @@ class serverProtocol(object):
|
|||||||
Sends an acknowledgment for client frame that requests a receipt.
|
Sends an acknowledgment for client frame that requests a receipt.
|
||||||
|
|
||||||
:param receipt_id: Receipt ID to send back to the client
|
:param receipt_id: Receipt ID to send back to the client
|
||||||
|
|
||||||
:returns: STOMP Frame object
|
:returns: STOMP Frame object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -147,6 +150,7 @@ class serverProtocol(object):
|
|||||||
:param message: Short description of the error
|
:param message: Short description of the error
|
||||||
:param body: Detailed information
|
:param body: Detailed information
|
||||||
:param content_type: MIME type which describes the format of the body
|
:param content_type: MIME type which describes the format of the body
|
||||||
|
|
||||||
:returns: STOMP Frame object
|
:returns: STOMP Frame object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -176,6 +180,7 @@ class clientProtocol(object):
|
|||||||
|
|
||||||
:param host: Host name that the socket was established against.
|
:param host: Host name that the socket was established against.
|
||||||
:param accept_version: The versions of the STOMP protocol the client supports.
|
:param accept_version: The versions of the STOMP protocol the client supports.
|
||||||
|
|
||||||
:returns: STOMP Frame object
|
:returns: STOMP Frame object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -195,6 +200,7 @@ class clientProtocol(object):
|
|||||||
Disconnects to a STOMP server.
|
Disconnects to a STOMP server.
|
||||||
|
|
||||||
:param receipt: unique identifier
|
:param receipt: unique identifier
|
||||||
|
|
||||||
:returns: STOMP Frame object
|
:returns: STOMP Frame object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -211,6 +217,7 @@ class clientProtocol(object):
|
|||||||
:param destination: Destination string
|
:param destination: Destination string
|
||||||
:param body: Data to be added in the frame body
|
:param body: Data to be added in the frame body
|
||||||
:param content_type: MIME type which describes the format of the body
|
:param content_type: MIME type which describes the format of the body
|
||||||
|
|
||||||
:returns: STOMP Frame object
|
:returns: STOMP Frame object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -7,6 +7,9 @@ import time
|
|||||||
|
|
||||||
@pytest.fixture(scope="session", autouse=True)
|
@pytest.fixture(scope="session", autouse=True)
|
||||||
def server(request):
|
def server(request):
|
||||||
|
"""
|
||||||
|
Starts GNS3 server for all the tests.
|
||||||
|
"""
|
||||||
|
|
||||||
cwd = os.path.dirname(os.path.abspath(__file__))
|
cwd = os.path.dirname(os.path.abspath(__file__))
|
||||||
server_script = os.path.join(cwd, "../gns3server/main.py")
|
server_script = os.path.join(cwd, "../gns3server/main.py")
|
||||||
|
@ -5,6 +5,10 @@ from ws4py.client.tornadoclient import TornadoWebSocketClient
|
|||||||
from gns3server.stomp import frame as stomp_frame
|
from gns3server.stomp import frame as stomp_frame
|
||||||
from gns3server.stomp import protocol as stomp_protocol
|
from gns3server.stomp import protocol as stomp_protocol
|
||||||
|
|
||||||
|
"""
|
||||||
|
Tests STOMP protocol over Websockets
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Stomp(AsyncTestCase):
|
class Stomp(AsyncTestCase):
|
||||||
|
|
||||||
@ -16,6 +20,10 @@ class Stomp(AsyncTestCase):
|
|||||||
AsyncTestCase.setUp(self)
|
AsyncTestCase.setUp(self)
|
||||||
|
|
||||||
def test_connect(self):
|
def test_connect(self):
|
||||||
|
"""
|
||||||
|
Sends a STOMP CONNECT frame and
|
||||||
|
check for a STOMP CONNECTED frame.
|
||||||
|
"""
|
||||||
|
|
||||||
request = self.stomp.connect("localhost")
|
request = self.stomp.connect("localhost")
|
||||||
AsyncWSRequest(self.URL, self.io_loop, self.stop, request)
|
AsyncWSRequest(self.URL, self.io_loop, self.stop, request)
|
||||||
@ -25,6 +33,11 @@ class Stomp(AsyncTestCase):
|
|||||||
assert frame.cmd == stomp_protocol.CMD_CONNECTED
|
assert frame.cmd == stomp_protocol.CMD_CONNECTED
|
||||||
|
|
||||||
def test_protocol_negotiation_failure(self):
|
def test_protocol_negotiation_failure(self):
|
||||||
|
"""
|
||||||
|
Sends a STOMP CONNECT frame with protocol version 1.0 required
|
||||||
|
and check for a STOMP ERROR sent back by the server which supports
|
||||||
|
STOMP version 1.2 only.
|
||||||
|
"""
|
||||||
|
|
||||||
request = self.stomp.connect("localhost", accept_version='1.0')
|
request = self.stomp.connect("localhost", accept_version='1.0')
|
||||||
AsyncWSRequest(self.URL, self.io_loop, self.stop, request)
|
AsyncWSRequest(self.URL, self.io_loop, self.stop, request)
|
||||||
@ -34,6 +47,9 @@ class Stomp(AsyncTestCase):
|
|||||||
assert frame.cmd == stomp_protocol.CMD_ERROR
|
assert frame.cmd == stomp_protocol.CMD_ERROR
|
||||||
|
|
||||||
def test_malformed_frame(self):
|
def test_malformed_frame(self):
|
||||||
|
"""
|
||||||
|
Sends an empty frame and check for a STOMP ERROR.
|
||||||
|
"""
|
||||||
|
|
||||||
request = b""
|
request = b""
|
||||||
AsyncWSRequest(self.URL, self.io_loop, self.stop, request)
|
AsyncWSRequest(self.URL, self.io_loop, self.stop, request)
|
||||||
@ -43,6 +59,10 @@ class Stomp(AsyncTestCase):
|
|||||||
assert frame.cmd == stomp_protocol.CMD_ERROR
|
assert frame.cmd == stomp_protocol.CMD_ERROR
|
||||||
|
|
||||||
def test_send(self):
|
def test_send(self):
|
||||||
|
"""
|
||||||
|
Sends a STOMP SEND frame with a message and a destination
|
||||||
|
and check for a STOMP MESSAGE with echoed message and destination.
|
||||||
|
"""
|
||||||
|
|
||||||
destination = "dynamips/echo"
|
destination = "dynamips/echo"
|
||||||
message = {"ping": "test"}
|
message = {"ping": "test"}
|
||||||
@ -57,6 +77,10 @@ class Stomp(AsyncTestCase):
|
|||||||
assert message == json_reply
|
assert message == json_reply
|
||||||
|
|
||||||
def test_unimplemented_frame(self):
|
def test_unimplemented_frame(self):
|
||||||
|
"""
|
||||||
|
Sends an STOMP BEGIN frame which is not implemented by the server
|
||||||
|
and check for a STOMP ERROR frame.
|
||||||
|
"""
|
||||||
|
|
||||||
frame = stomp_frame.Frame(stomp_protocol.CMD_BEGIN)
|
frame = stomp_frame.Frame(stomp_protocol.CMD_BEGIN)
|
||||||
request = frame.encode()
|
request = frame.encode()
|
||||||
@ -67,6 +91,11 @@ class Stomp(AsyncTestCase):
|
|||||||
assert frame.cmd == stomp_protocol.CMD_ERROR
|
assert frame.cmd == stomp_protocol.CMD_ERROR
|
||||||
|
|
||||||
def test_disconnect(self):
|
def test_disconnect(self):
|
||||||
|
"""
|
||||||
|
Sends a STOMP DISCONNECT frame is a receipt id
|
||||||
|
and check for a STOMP RECEIPT frame with the same receipt id
|
||||||
|
confirming the disconnection.
|
||||||
|
"""
|
||||||
|
|
||||||
myid = str(uuid.uuid4())
|
myid = str(uuid.uuid4())
|
||||||
request = self.stomp.disconnect(myid)
|
request = self.stomp.disconnect(myid)
|
||||||
@ -79,6 +108,9 @@ class Stomp(AsyncTestCase):
|
|||||||
|
|
||||||
|
|
||||||
class AsyncWSRequest(TornadoWebSocketClient):
|
class AsyncWSRequest(TornadoWebSocketClient):
|
||||||
|
"""
|
||||||
|
Very basic Websocket client for the tests
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, url, io_loop, callback, message):
|
def __init__(self, url, io_loop, callback, message):
|
||||||
TornadoWebSocketClient.__init__(self, url, io_loop=io_loop)
|
TornadoWebSocketClient.__init__(self, url, io_loop=io_loop)
|
||||||
|
@ -1,37 +1,40 @@
|
|||||||
from tornado.testing import AsyncHTTPTestCase
|
from tornado.testing import AsyncHTTPTestCase
|
||||||
|
from tornado.escape import json_decode
|
||||||
from gns3server.server import VersionHandler
|
from gns3server.server import VersionHandler
|
||||||
from gns3server._compat import urlencode
|
from gns3server.version import __version__
|
||||||
import tornado.web
|
import tornado.web
|
||||||
import json
|
|
||||||
|
|
||||||
# URL to test
|
"""
|
||||||
URL = "/version"
|
Tests for the web server version handler
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class TestVersionHandler(AsyncHTTPTestCase):
|
class TestVersionHandler(AsyncHTTPTestCase):
|
||||||
|
|
||||||
|
URL = "/version"
|
||||||
|
|
||||||
def get_app(self):
|
def get_app(self):
|
||||||
return tornado.web.Application([(URL, VersionHandler)])
|
|
||||||
|
return tornado.web.Application([(self.URL, VersionHandler)])
|
||||||
|
|
||||||
def test_endpoint(self):
|
def test_endpoint(self):
|
||||||
self.http_client.fetch(self.get_url(URL), self.stop)
|
"""
|
||||||
|
Tests if the response HTTP code is 200 (success)
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.http_client.fetch(self.get_url(self.URL), self.stop)
|
||||||
response = self.wait()
|
response = self.wait()
|
||||||
assert response.code == 200
|
assert response.code == 200
|
||||||
|
|
||||||
# def test_post(self):
|
def test_received_version(self):
|
||||||
# data = urlencode({'test': 'works'})
|
"""
|
||||||
# req = tornado.httpclient.HTTPRequest(self.get_url(URL),
|
Tests if the returned content type is JSON and
|
||||||
# method='POST',
|
if the received version is the same as the server
|
||||||
# body=data)
|
"""
|
||||||
# self.http_client.fetch(req, self.stop)
|
|
||||||
# response = self.wait()
|
|
||||||
# assert response.code == 200
|
|
||||||
#
|
|
||||||
# def test_endpoint_differently(self):
|
|
||||||
# self.http_client.fetch(self.get_url(URL), self.stop)
|
|
||||||
# response = self.wait()
|
|
||||||
# assert(response.headers['Content-Type'].startswith('application/json'))
|
|
||||||
# assert(response.body != "")
|
|
||||||
# body = json.loads(response.body.decode('utf-8'))
|
|
||||||
# assert body['version'] == "0.1.dev"
|
|
||||||
|
|
||||||
|
self.http_client.fetch(self.get_url(self.URL), self.stop)
|
||||||
|
response = self.wait()
|
||||||
|
assert(response.headers['Content-Type'].startswith('application/json'))
|
||||||
|
assert(response.body)
|
||||||
|
body = json_decode(response.body)
|
||||||
|
assert body['version'] == __version__
|
||||||
|
Loading…
Reference in New Issue
Block a user