General work on the Dynamips backend (need to finish and polish).

pull/11/head
grossmj 10 years ago
parent 2516bf80a8
commit cef29e53aa

@ -2,4 +2,4 @@ GNS3-server
=========== ===========
GNS3 server manages emulators such as Dynamips, VirtualBox or Qemu/KVM. GNS3 server manages emulators such as Dynamips, VirtualBox or Qemu/KVM.
Clients like the GNS3 GUI controls the server using an API over Websockets. Clients like the GNS3 GUI controls the server using a JSON-RPC API over Websockets.

@ -23,7 +23,7 @@ import zmq
import uuid import uuid
import tornado.websocket import tornado.websocket
from tornado.escape import json_decode from tornado.escape import json_decode
from ..jsonrpc import JSONRPCParseError, JSONRPCInvalidRequest, JSONRPCMethodNotFound from ..jsonrpc import JSONRPCParseError, JSONRPCInvalidRequest, JSONRPCMethodNotFound, JSONRPCNotification
import logging import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -59,7 +59,7 @@ class JSONRPCWebSocket(tornado.websocket.WebSocketHandler):
return self._session_id return self._session_id
@classmethod @classmethod
def dispatch_message(cls, message): def dispatch_message(cls, stream, message):
""" """
Sends a message to Websocket client Sends a message to Websocket client
@ -71,7 +71,13 @@ class JSONRPCWebSocket(tornado.websocket.WebSocketHandler):
# ZMQ responses are encoded in JSON # ZMQ responses are encoded in JSON
# format is a JSON array: [session ID, JSON-RPC response] # format is a JSON array: [session ID, JSON-RPC response]
json_message = json_decode(message[1]) try:
json_message = json_decode(message[1])
except ValueError as e:
stream.send_string("Cannot decode message!")
log.critical("Couldn't decode message: {}".format(e))
return
session_id = json_message[0] session_id = json_message[0]
jsonrpc_response = json_message[1] jsonrpc_response = json_message[1]
@ -94,8 +100,8 @@ class JSONRPCWebSocket(tornado.websocket.WebSocketHandler):
# Make sure the destination is not already registered # Make sure the destination is not already registered
# by another module for instance # by another module for instance
assert destination not in cls.destinations assert destination not in cls.destinations
log.info("registering {} as a destination for {}".format(destination, log.debug("registering {} as a destination for the {} module".format(destination,
module)) module))
cls.destinations[destination] = module cls.destinations[destination] = module
def open(self): def open(self):
@ -119,8 +125,8 @@ class JSONRPCWebSocket(tornado.websocket.WebSocketHandler):
request = json_decode(message) request = json_decode(message)
jsonrpc_version = request["jsonrpc"] jsonrpc_version = request["jsonrpc"]
method = request["method"] method = request["method"]
# warning: notifications cannot be sent by a client because check for an "id" here # This is a JSON-RPC notification if request_id is None
request_id = request["id"] request_id = request.get("id")
except: except:
return self.write_message(JSONRPCParseError()()) return self.write_message(JSONRPCParseError()())
@ -128,7 +134,11 @@ class JSONRPCWebSocket(tornado.websocket.WebSocketHandler):
return self.write_message(JSONRPCInvalidRequest()()) return self.write_message(JSONRPCInvalidRequest()())
if method not in self.destinations: if method not in self.destinations:
return self.write_message(JSONRPCMethodNotFound(request_id)()) if request_id:
return self.write_message(JSONRPCMethodNotFound(request_id)())
else:
# This is a notification, silently ignore this error...
return
module = self.destinations[method] module = self.destinations[method]
# ZMQ requests are encoded in JSON # ZMQ requests are encoded in JSON
@ -136,7 +146,7 @@ class JSONRPCWebSocket(tornado.websocket.WebSocketHandler):
zmq_request = [self.session_id, request] zmq_request = [self.session_id, request]
# Route to the correct module # Route to the correct module
self.zmq_router.send_string(module, zmq.SNDMORE) self.zmq_router.send_string(module, zmq.SNDMORE)
# Send the encoded JSON request # Send the JSON request
self.zmq_router.send_json(zmq_request) self.zmq_router.send_json(zmq_request)
def on_close(self): def on_close(self):
@ -146,3 +156,14 @@ class JSONRPCWebSocket(tornado.websocket.WebSocketHandler):
log.info("Websocket client {} disconnected".format(self.session_id)) log.info("Websocket client {} disconnected".format(self.session_id))
self.clients.remove(self) self.clients.remove(self)
# Reset the modules if there are no clients anymore
# Modules must implement a reset destination
if not self.clients:
for destination, module in self.destinations.items():
if destination.endswith("reset"):
# Route to the correct module
self.zmq_router.send_string(module, zmq.SNDMORE)
# Send the JSON request
notification = JSONRPCNotification(destination)()
self.zmq_router.send_json([self.session_id, notification])

@ -159,8 +159,10 @@ class JSONRPCRequest(JSONRPCObject):
:param request_id: JSON-RPC identifier (generated by default) :param request_id: JSON-RPC identifier (generated by default)
""" """
def __init__(self, method, params=None, request_id=str(uuid.uuid1())): def __init__(self, method, params=None, request_id=None):
JSONRPCObject.__init__(self) JSONRPCObject.__init__(self)
if request_id == None:
request_id = str(uuid.uuid4())
self.id = request_id self.id = request_id
self.method = method self.method = method
if params: if params:

@ -24,7 +24,7 @@ import gns3server
# command line options # command line options
from tornado.options import define from tornado.options import define
define("host", default="127.0.0.1", help="run on the given host/IP address", type=str) define("host", default="0.0.0.0", help="run on the given host/IP address", type=str)
define("port", default=8000, help="run on the given port", type=int) define("port", default=8000, help="run on the given port", type=int)
define("ipc", default=False, help="use IPC for module communication", type=bool) define("ipc", default=False, help="use IPC for module communication", type=bool)

@ -79,7 +79,7 @@ class ModuleManager(object):
if issubclass(module_class[1], IModule): if issubclass(module_class[1], IModule):
# make sure the module class has IModule as a parent # make sure the module class has IModule as a parent
if module_class[1].__module__ == name: if module_class[1].__module__ == name:
log.info("found and loading {} module".format(module_class[0].lower())) log.info("loading {} module".format(module_class[0].lower()))
info = Module(name=module_class[0].lower(), cls=module_class[1]) info = Module(name=module_class[0].lower(), cls=module_class[1])
self._modules.append(info) self._modules.append(info)
except: except:

@ -92,6 +92,10 @@ class IModule(multiprocessing.Process):
stream.on_recv(callback) stream.on_recv(callback)
return stream return stream
# def add_periodic_callback(self, callback, time):
#
# self.test = zmq.eventloop.ioloop.PeriodicCallback(callback, time, self._ioloop).start()
def run(self): def run(self):
""" """
Starts the event loop Starts the event loop
@ -158,9 +162,9 @@ class IModule(multiprocessing.Process):
# add session to the response # add session to the response
response = [self._current_session, jsonrpc_response] response = [self._current_session, jsonrpc_response]
log.info("ZeroMQ client ({}) sending JSON-RPC custom error {} for call id {}".format(self.name, log.info("ZeroMQ client ({}) sending JSON-RPC custom error: {} for call id {}".format(self.name,
message, message,
self._current_call_id)) self._current_call_id))
self._stream.send_json(response) self._stream.send_json(response)
def _decode_request(self, request): def _decode_request(self, request):
@ -188,7 +192,12 @@ class IModule(multiprocessing.Process):
return return
log.debug("Routing request to {}: {}".format(destination, request[1])) log.debug("Routing request to {}: {}".format(destination, request[1]))
self.destination[destination](self, params)
try:
self.destination[destination](self, params)
except Exception as e:
log.error("uncaught exception {type}".format(type=type(e)), exc_info=1)
self.send_custom_error("uncaught exception {type}: {string}".format(type=type(e), string=str(e)))
def destinations(self): def destinations(self):
""" """

@ -15,13 +15,18 @@
# 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/>.
"""
Dynamips server module.
"""
from gns3server.modules import IModule from gns3server.modules import IModule
import gns3server.jsonrpc as jsonrpc import gns3server.jsonrpc as jsonrpc
from .hypervisor import Hypervisor from .hypervisor import Hypervisor
from .hypervisor_manager import HypervisorManager from .hypervisor_manager import HypervisorManager
from .dynamips_error import DynamipsError from .dynamips_error import DynamipsError
# nodes # Nodes
from .nodes.router import Router from .nodes.router import Router
from .nodes.c1700 import C1700 from .nodes.c1700 import C1700
from .nodes.c2600 import C2600 from .nodes.c2600 import C2600
@ -37,7 +42,7 @@ from .nodes.atm_bridge import ATMBridge
from .nodes.frame_relay_switch import FrameRelaySwitch from .nodes.frame_relay_switch import FrameRelaySwitch
from .nodes.hub import Hub from .nodes.hub import Hub
# adapters # Adapters
from .adapters.c7200_io_2fe import C7200_IO_2FE from .adapters.c7200_io_2fe import C7200_IO_2FE
from .adapters.c7200_io_fe import C7200_IO_FE from .adapters.c7200_io_fe import C7200_IO_FE
from .adapters.c7200_io_ge_e import C7200_IO_GE_E from .adapters.c7200_io_ge_e import C7200_IO_GE_E
@ -71,35 +76,188 @@ from .nios.nio_fifo import NIO_FIFO
from .nios.nio_mcast import NIO_Mcast from .nios.nio_mcast import NIO_Mcast
from .nios.nio_null import NIO_Null from .nios.nio_null import NIO_Null
from .backends import vm
from .backends import ethsw
from .backends import ethhub
from .backends import frsw
from .backends import atmsw
import logging import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class Dynamips(IModule): class Dynamips(IModule):
"""
Dynamips module.
:param name: module name
:param args: arguments for the module
:param kwargs: named arguments for the module
"""
def __init__(self, name=None, args=(), kwargs={}): def __init__(self, name=None, args=(), kwargs={}):
IModule.__init__(self, name=name, args=args, kwargs=kwargs) IModule.__init__(self, name=name, args=args, kwargs=kwargs)
# start the hypervisor manager self._hypervisor_manager = None
#self._hypervisor_manager = HypervisorManager("/usr/bin/dynamips", "/tmp") self._routers = {}
self._ethernet_switches = {}
self._frame_relay_switches = {}
self._atm_switches = {}
self._ethernet_hubs = {}
# def __del__(self):
#
# self._hypervisor_manager.stop_all_hypervisors()
@IModule.route("dynamips.reset")
def reset(self, request):
"""
Resets the module.
:param request: JSON request
"""
# stop all Dynamips hypervisors
self._hypervisor_manager.stop_all_hypervisors()
# resets the instance counters
Router.reset()
EthernetSwitch.reset()
Hub.reset()
FrameRelaySwitch.reset()
ATMSwitch.reset()
NIO_UDP.reset()
NIO_UDP_auto.reset()
NIO_UNIX.reset()
NIO_VDE.reset()
NIO_TAP.reset()
NIO_GenericEthernet.reset()
NIO_LinuxEthernet.reset()
NIO_FIFO.reset()
NIO_Mcast.reset()
NIO_Null.reset()
self._routers.clear()
self._ethernet_switches.clear()
self._frame_relay_switches.clear()
self._atm_switches.clear()
log.info("dynamips module has been reset")
@IModule.route("dynamips.settings")
def settings(self, request):
"""
Set or update settings.
:param request: JSON request
"""
print("Create")
if not self._hypervisor_manager:
self._hypervisor_manager = HypervisorManager(request["path"], "/tmp")
for name, value in request.items():
if hasattr(self._hypervisor_manager, name) and getattr(self._hypervisor_manager, name) != value:
setattr(self._hypervisor_manager, name, value)
@IModule.route("dynamips.echo") @IModule.route("dynamips.echo")
def echo(self, request): def echo(self, request):
"""
Echo end point for testing purposes.
:param request: JSON request
"""
if request == None: if request == None:
self.send_param_error() self.send_param_error()
return else:
log.debug("received request {}".format(request)) log.debug("received request {}".format(request))
self.send_response(request) self.send_response(request)
@IModule.route("dynamips.create_vm") def create_nio(self, node, request):
def create_vm(self, request):
print("Create VM!") nio = None
log.debug("received request {}".format(request)) if request["nio"] == "NIO_UDP":
self.send_response(request) lport = request["lport"]
rhost = request["rhost"]
@IModule.route("dynamips.start_vm") rport = request["rport"]
def start_vm(self, request): nio = NIO_UDP(node.hypervisor, lport, rhost, rport)
print("Start VM!") elif request["nio"] == "NIO_GenericEthernet":
log.debug("received request {}".format(request)) ethernet_device = request["ethernet_device"]
self.send_response(request) nio = NIO_GenericEthernet(node.hypervisor, ethernet_device)
elif request["nio"] == "NIO_LinuxEthernet":
ethernet_device = request["ethernet_device"]
nio = NIO_LinuxEthernet(node.hypervisor, ethernet_device)
elif request["nio"] == "NIO_TAP":
tap_device = request["tap_device"]
nio = NIO_TAP(node.hypervisor, tap_device)
elif request["nio"] == "NIO_UNIX":
local_file = request["local_file"]
remote_file = request["remote_file"]
nio = NIO_UNIX(node.hypervisor, local_file, remote_file)
elif request["nio"] == "NIO_VDE":
control_file = request["control_file"]
local_file = request["local_file"]
nio = NIO_VDE(node.hypervisor, control_file, local_file)
elif request["nio"] == "NIO_Null":
nio = NIO_Null(node.hypervisor)
return nio
def allocate_udp_port(self, node):
"""
Allocates a UDP port in order to create an UDP NIO.
:param node: the node that needs to allocate an UDP port
"""
port = node.hypervisor.allocate_udp_port()
host = node.hypervisor.host
log.info("{} [id={}] has allocated UDP port {} with host {}".format(node.name,
node.id,
port,
host))
response = {"lport": port,
"lhost": host}
return response
def set_ghost_ios(self, router):
if not router.mmap:
raise DynamipsError("mmap support is required to enable ghost IOS support")
ghost_instance = router.formatted_ghost_file()
all_ghosts = []
# search of an existing ghost instance across all hypervisors
for hypervisor in self._hypervisor_manager.hypervisors:
all_ghosts.extend(hypervisor.ghosts)
if ghost_instance not in all_ghosts:
# create a new ghost IOS instance
ghost = Router(router.hypervisor, "ghost-" + ghost_instance, router.platform, ghost_flag=True)
ghost.image = router.image
# for 7200s, the NPE must be set when using an NPE-G2.
if router.platform == "c7200":
ghost.npe = router.npe
ghost.ghost_status = 1
ghost.ghost_file = ghost_instance
ghost.ram = router.ram
ghost.start()
ghost.stop()
ghost.delete()
router.ghost_status = 2
router.ghost_file = ghost_instance
@IModule.route("dynamips.nio.get_interfaces")
def nio_get_interfaces(self, request):
"""
Get all the network interfaces on this host.
:param request: JSON request
"""
import netifaces
self.send_response(netifaces.interfaces())

@ -124,6 +124,17 @@ class Adapter(object):
self._ports[port_id] = None self._ports[port_id] = None
def get_nio(self, port_id):
"""
Returns the NIO assigned to a port.
:params port_id: port ID (integer)
:returns: NIO object
"""
return self._ports[port_id]
@property @property
def ports(self): def ports(self):
""" """

@ -33,11 +33,3 @@ class C1700_MB_1FE(Adapter):
def removable(self): def removable(self):
return False return False
def interface_type(self):
return "FastEthernet"
def medium(self):
return "Ethernet"

@ -29,16 +29,8 @@ class C1700_MB_WIC1(Adapter):
def __str__(self): def __str__(self):
return "C1700_MB_WIC1" return "C1700-MB-WIC1"
def removable(self): def removable(self):
return False return False
def interface_type(self):
return "N/A"
def medium(self):
return "N/A"

@ -33,11 +33,3 @@ class C2600_MB_1E(Adapter):
def removable(self): def removable(self):
return False return False
def interface_type(self):
return "Ethernet"
def medium(self):
return "Ethernet"

@ -34,11 +34,3 @@ class C2600_MB_1FE(Adapter):
def removable(self): def removable(self):
return False return False
def interface_type(self):
return "FastEthernet"
def medium(self):
return "Ethernet"

@ -33,11 +33,3 @@ class C2600_MB_2E(Adapter):
def removable(self): def removable(self):
return False return False
def interface_type(self):
return "Ethernet"
def medium(self):
return "Ethernet"

@ -33,11 +33,3 @@ class C2600_MB_2FE(Adapter):
def removable(self): def removable(self):
return False return False
def interface_type(self):
return "FastEthernet"
def medium(self):
return "Ethernet"

@ -28,16 +28,8 @@ class C7200_IO_2FE(Adapter):
def __str__(self): def __str__(self):
return "C7200_IO_2FE" return "C7200-IO-2FE"
def removable(self): def removable(self):
return False return False
def interface_type(self):
return "FastEthernet"
def medium(self):
return "Ethernet"

@ -28,16 +28,8 @@ class C7200_IO_FE(Adapter):
def __str__(self): def __str__(self):
return "C7200_IO_FE" return "C7200-IO-FE"
def removable(self): def removable(self):
return False return False
def interface_type(self):
return "FastEthernet"
def medium(self):
return "Ethernet"

@ -33,11 +33,3 @@ class C7200_IO_GE_E(Adapter):
def removable(self): def removable(self):
return False return False
def interface_type(self):
return "GigabitEthernet"
def medium(self):
return "Ethernet"

@ -30,11 +30,3 @@ class GT96100_FE(Adapter):
def removable(self): def removable(self):
return False return False
def interface_type(self):
return "FastEthernet"
def medium(self):
return "Ethernet"

@ -34,11 +34,3 @@ class Leopard_2FE(Adapter):
def removable(self): def removable(self):
return False return False
def interface_type(self):
return "FastEthernet"
def medium(self):
return "Ethernet"

@ -29,11 +29,3 @@ class NM_16ESW(Adapter):
def __str__(self): def __str__(self):
return "NM-16ESW" return "NM-16ESW"
def interface_type(self):
return "FastEthernet"
def medium(self):
return "Ethernet"

@ -29,11 +29,3 @@ class NM_1E(Adapter):
def __str__(self): def __str__(self):
return "NM-1E" return "NM-1E"
def interface_type(self):
return "Ethernet"
def medium(self):
return "Ethernet"

@ -29,11 +29,3 @@ class NM_1FE_TX(Adapter):
def __str__(self): def __str__(self):
return "NM-1FE-TX" return "NM-1FE-TX"
def interface_type(self):
return "FastEthernet"
def medium(self):
return "Ethernet"

@ -29,11 +29,3 @@ class NM_4E(Adapter):
def __str__(self): def __str__(self):
return "NM-4E" return "NM-4E"
def interface_type(self):
return "Ethernet"
def medium(self):
return "Ethernet"

@ -29,11 +29,3 @@ class NM_4T(Adapter):
def __str__(self): def __str__(self):
return "NM-4T" return "NM-4T"
def interface_type(self):
return "Serial"
def medium(self):
return "Serial"

@ -29,11 +29,3 @@ class PA_2FE_TX(Adapter):
def __str__(self): def __str__(self):
return "PA-2FE-TX" return "PA-2FE-TX"
def interface_type(self):
return "FastEthernet"
def medium(self):
return "Ethernet"

@ -29,11 +29,3 @@ class PA_4E(Adapter):
def __str__(self): def __str__(self):
return "PA-4E" return "PA-4E"
def interface_type(self):
return "Ethernet"
def medium(self):
return "Ethernet"

@ -29,11 +29,3 @@ class PA_4T(Adapter):
def __str__(self): def __str__(self):
return "PA-4T+" return "PA-4T+"
def interface_type(self):
return "Serial"
def medium(self):
return "Serial"

@ -29,11 +29,3 @@ class PA_8E(Adapter):
def __str__(self): def __str__(self):
return "PA-8E" return "PA-8E"
def interface_type(self):
return "Ethernet"
def medium(self):
return "Ethernet"

@ -29,11 +29,3 @@ class PA_8T(Adapter):
def __str__(self): def __str__(self):
return "PA-8T" return "PA-8T"
def interface_type(self):
return "Serial"
def medium(self):
return "Serial"

@ -29,11 +29,3 @@ class PA_A1(Adapter):
def __str__(self): def __str__(self):
return "PA-A1" return "PA-A1"
def interface_type(self):
return "ATM"
def medium(self):
return "Serial"

@ -29,11 +29,3 @@ class PA_FE_TX(Adapter):
def __str__(self): def __str__(self):
return "PA-FE-TX" return "PA-FE-TX"
def interface_type(self):
return "FastEthernet"
def medium(self):
return "Ethernet"

@ -29,11 +29,3 @@ class PA_GE(Adapter):
def __str__(self): def __str__(self):
return "PA-GE" return "PA-GE"
def interface_type(self):
return "GigabitEthernet"
def medium(self):
return "Ethernet"

@ -29,11 +29,3 @@ class PA_POS_OC3(Adapter):
def __str__(self): def __str__(self):
return "PA-POS-OC3" return "PA-POS-OC3"
def interface_type(self):
return "POS"
def medium(self):
return "SONET"

@ -29,14 +29,6 @@ class WIC_1ENET(object):
return "WIC-1ENET" return "WIC-1ENET"
def interface_type(self):
return "Ethernet"
def medium(self):
return "Ethernet"
@property @property
def interfaces(self): def interfaces(self):
""" """

@ -29,14 +29,6 @@ class WIC_1T(object):
return "WIC-1T" return "WIC-1T"
def interface_type(self):
return "Serial"
def medium(self):
return "Serial"
@property @property
def interfaces(self): def interfaces(self):
""" """

@ -29,14 +29,6 @@ class WIC_2T(object):
return "WIC-2T" return "WIC-2T"
def interface_type(self):
return "Serial"
def medium(self):
return "Serial"
@property @property
def interfaces(self): def interfaces(self):
""" """

@ -0,0 +1,201 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2014 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 re
from gns3server.modules import IModule
from ..nodes.atm_switch import ATMSwitch
from ..dynamips_error import DynamipsError
import logging
log = logging.getLogger(__name__)
class ATMSW(object):
@IModule.route("dynamips.atmsw.create")
def atmsw_create(self, request):
"""
Creates a new ATM switch.
:param request: JSON request
"""
name = None
if request and "name" in request:
name = request["name"]
try:
hypervisor = self._hypervisor_manager.allocate_hypervisor_for_switch()
atmsw = ATMSwitch(hypervisor, name)
except DynamipsError as e:
self.send_custom_error(str(e))
return
response = {"name": atmsw.name,
"id": atmsw.id}
self._atm_switches[atmsw.id] = atmsw
self.send_response(response)
@IModule.route("dynamips.atmsw.delete")
def atmsw_delete(self, request):
"""
Deletes a ATM switch.
:param request: JSON request
"""
#TODO: JSON schema validation for the request
log.debug("received request {}".format(request))
atmsw_id = request["id"]
atmsw = self._atm_switches[atmsw_id]
try:
atmsw.delete()
except DynamipsError as e:
self.send_custom_error(str(e))
return
self.send_response(request)
@IModule.route("dynamips.atmsw.update")
def atmsw_update(self, request):
"""
Updates a ATM switch.
:param request: JSON request
"""
#TODO: JSON schema validation for the request
log.debug("received request {}".format(request))
atmsw_id = request["id"]
atmsw = self._atm_switches[atmsw_id]
self.send_response(request)
@IModule.route("dynamips.atmsw.allocate_udp_port")
def atmsw_allocate_udp_port(self, request):
"""
Allocates a UDP port in order to create an UDP NIO for an
ATM switch.
:param request: JSON request
"""
#TODO: JSON schema validation for the request
log.debug("received request {}".format(request))
atmsw_id = request["id"]
atmsw = self._atm_switches[atmsw_id]
try:
# allocate a new UDP port
response = self.allocate_udp_port(atmsw)
except DynamipsError as e:
self.send_custom_error(str(e))
return
self.send_response(response)
@IModule.route("dynamips.atmsw.add_nio")
def atmsw_add_nio(self, request):
"""
Adds an NIO (Network Input/Output) for an ATM switch.
:param request: JSON request
"""
#TODO: JSON schema validation for the request
log.debug("received request {}".format(request))
atmsw_id = request["id"]
atmsw = self._atm_switches[atmsw_id]
port = request["port"]
mapping = request["mapping"]
try:
nio = self.create_nio(atmsw, request)
except DynamipsError as e:
self.send_custom_error(str(e))
return
try:
atmsw.add_nio(nio, port)
pvc_entry = re.compile(r"""^([0-9]*):([0-9]*):([0-9]*)$""")
for source, destination in mapping.items():
match_source_pvc = pvc_entry.search(source)
match_destination_pvc = pvc_entry.search(destination)
if match_source_pvc and match_destination_pvc:
# add the virtual channels mapped with this port/nio
source_port, source_vpi, source_vci = map(int, match_source_pvc.group(1, 2, 3))
destination_port, destination_vpi, destination_vci = map(int, match_destination_pvc.group(1, 2, 3))
if atmsw.has_port(destination_port):
if (source_port, source_vpi, source_vci) not in atmsw.mapping and \
(destination_port, destination_vpi, destination_vci) not in atmsw.mapping:
atmsw.map_pvc(source_port, source_vpi, source_vci, destination_port, destination_vpi, destination_vci)
atmsw.map_pvc(destination_port, destination_vpi, destination_vci, source_port, source_vpi, source_vci)
else:
# add the virtual paths mapped with this port/nio
source_port, source_vpi = map(int, source.split(':'))
destination_port, destination_vpi = map(int, destination.split(':'))
if atmsw.has_port(destination_port):
if (source_port, source_vpi) not in atmsw.mapping and (destination_port, destination_vpi) not in atmsw.mapping:
atmsw.map_vp(source_port, source_vpi, destination_port, destination_vpi)
atmsw.map_vp(destination_port, destination_vpi, source_port, source_vpi)
except DynamipsError as e:
self.send_custom_error(str(e))
return
# for now send back the original request
self.send_response(request)
@IModule.route("dynamips.atmsw.delete_nio")
def atmsw_delete_nio(self, request):
"""
Deletes an NIO (Network Input/Output).
:param request: JSON request
"""
#TODO: JSON schema validation for the request
log.debug("received request {}".format(request))
atmsw_id = request["id"]
atmsw = self._atm_switches[atmsw_id]
port = request["port"]
try:
for source, destination in atmsw.mapping.copy().items():
if len(source) == 3 and len(destination) == 3:
# remove the virtual channels mapped with this port/nio
source_port, source_vpi, source_vci = source
destination_port, destination_vpi, destination_vci = destination
if port == source_port:
atmsw.unmap_pvc(source_port, source_vpi, source_vci, destination_port, destination_vpi, destination_vci)
atmsw.unmap_pvc(destination_port, destination_vpi, destination_vci, source_port, source_vpi, source_vci)
else:
# remove the virtual paths mapped with this port/nio
source_port, source_vpi = source
destination_port, destination_vpi = destination
if port == source_port:
atmsw.unmap_vp(source_port, source_vpi, destination_port, destination_vpi)
atmsw.unmap_vp(destination_port, destination_vpi, source_port, source_vpi)
nio = atmsw.remove_nio(port)
nio.delete()
except DynamipsError as e:
self.send_custom_error(str(e))
return
# for now send back the original request
self.send_response(request)

@ -0,0 +1,162 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2014 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 ..nodes.hub import Hub
from ..dynamips_error import DynamipsError
import logging
log = logging.getLogger(__name__)
class ETHHUB(object):
@IModule.route("dynamips.ethhub.create")
def ethhub_create(self, request):
"""
Creates a new Ethernet switch.
:param request: JSON request
"""
name = None
if request and "name" in request:
name = request["name"]
try:
hypervisor = self._hypervisor_manager.allocate_hypervisor_for_switch()
ethhub = Hub(hypervisor, name)
except DynamipsError as e:
self.send_custom_error(str(e))
return
response = {"name": ethhub.name,
"id": ethhub.id}
self._ethernet_hubs[ethhub.id] = ethhub
self.send_response(response)
@IModule.route("dynamips.ethhub.delete")
def ethhub_delete(self, request):
"""
Deletes a Ethernet hub.
:param request: JSON request
"""
#TODO: JSON schema validation for the request
log.debug("received request {}".format(request))
ethhub_id = request["id"]
ethhub = self._ethernet_hubs[ethhub_id]
try:
ethhub.delete()
except DynamipsError as e:
self.send_custom_error(str(e))
return
self.send_response(request)
@IModule.route("dynamips.ethhub.update")
def ethhub_update(self, request):
"""
Updates a Ethernet hub.
:param request: JSON request
"""
#TODO: JSON schema validation for the request
log.debug("received request {}".format(request))
ethhub_id = request["id"]
ethhub = self._ethernet_hubs[ethhub_id]
#ports = request["ports"]
self.send_response(request)
@IModule.route("dynamips.ethhub.allocate_udp_port")
def ethhub_allocate_udp_port(self, request):
"""
Allocates a UDP port in order to create an UDP NIO for an
Ethernet hub.
:param request: JSON request
"""
#TODO: JSON schema validation for the request
log.debug("received request {}".format(request))
ethhub_id = request["id"]
ethhub = self._ethernet_hubs[ethhub_id]
try:
# allocate a new UDP port
response = self.allocate_udp_port(ethhub)
except DynamipsError as e:
self.send_custom_error(str(e))
return
self.send_response(response)
@IModule.route("dynamips.ethhub.add_nio")
def ethhub_add_nio(self, request):
"""
Adds an NIO (Network Input/Output) for an Ethernet hub.
:param request: JSON request
"""
#TODO: JSON schema validation for the request
log.debug("received request {}".format(request))
ethhub_id = request["id"]
ethhub = self._ethernet_hubs[ethhub_id]
port = request["port"]
try:
nio = self.create_nio(ethhub, request)
except DynamipsError as e:
self.send_custom_error(str(e))
return
try:
ethhub.add_nio(nio, port)
except DynamipsError as e:
self.send_custom_error(str(e))
return
# for now send back the original request
self.send_response(request)
@IModule.route("dynamips.ethhub.delete_nio")
def ethsw_delete_nio(self, request):
"""
Deletes an NIO (Network Input/Output).
:param request: JSON request
"""
#TODO: JSON schema validation for the request
log.debug("received request {}".format(request))
ethhub_id = request["id"]
ethhub = self._ethernet_hubs[ethhub_id]
port = request["port"]
try:
nio = ethhub.remove_nio(port)
nio.delete()
except DynamipsError as e:
self.send_custom_error(str(e))
return
# for now send back the original request
self.send_response(request)

@ -0,0 +1,180 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2014 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 ..nodes.ethernet_switch import EthernetSwitch
from ..dynamips_error import DynamipsError
import logging
log = logging.getLogger(__name__)
class ETHSW(object):
@IModule.route("dynamips.ethsw.create")
def ethsw_create(self, request):
"""
Creates a new Ethernet switch.
:param request: JSON request
"""
name = None
if request and "name" in request:
name = request["name"]
try:
hypervisor = self._hypervisor_manager.allocate_hypervisor_for_switch()
ethsw = EthernetSwitch(hypervisor, name)
except DynamipsError as e:
self.send_custom_error(str(e))
return
response = {"name": ethsw.name,
"id": ethsw.id}
self._ethernet_switches[ethsw.id] = ethsw
self.send_response(response)
@IModule.route("dynamips.ethsw.delete")
def ethsw_delete(self, request):
"""
Deletes a Ethernet switch.
:param request: JSON request
"""
#TODO: JSON schema validation for the request
log.debug("received request {}".format(request))
ethsw_id = request["id"]
ethsw = self._ethernet_switches[ethsw_id]
try:
ethsw.delete()
except DynamipsError as e:
self.send_custom_error(str(e))
return
self.send_response(request)
@IModule.route("dynamips.ethsw.update")
def ethsw_update(self, request):
"""
Updates a Ethernet switch.
:param request: JSON request
"""
#TODO: JSON schema validation for the request
log.debug("received request {}".format(request))
ethsw_id = request["id"]
ethsw = self._ethernet_switches[ethsw_id]
ports = request["ports"]
for port, info in ports.items():
vlan = info["vlan"]
port_type = info["type"]
if port_type == "access":
ethsw.set_access_port(int(port), vlan)
elif port_type == "dot1q":
ethsw.set_dot1q_port(int(port), vlan)
elif port_type == "qinq":
ethsw.set_qinq_port(int(port), vlan)
self.send_response(request)
@IModule.route("dynamips.ethsw.allocate_udp_port")
def ethsw_allocate_udp_port(self, request):
"""
Allocates a UDP port in order to create an UDP NIO for an
Ethernet switch.
:param request: JSON request
"""
#TODO: JSON schema validation for the request
log.debug("received request {}".format(request))
ethsw_id = request["id"]
ethsw = self._ethernet_switches[ethsw_id]
try:
# allocate a new UDP port
response = self.allocate_udp_port(ethsw)
except DynamipsError as e:
self.send_custom_error(str(e))
return
self.send_response(response)
@IModule.route("dynamips.ethsw.add_nio")
def ethsw_add_nio(self, request):
"""
Adds an NIO (Network Input/Output) for an Ethernet switch.
:param request: JSON request
"""
#TODO: JSON schema validation for the request
log.debug("received request {}".format(request))
ethsw_id = request["id"]
ethsw = self._ethernet_switches[ethsw_id]
port = request["port"]
vlan = request["vlan"]
port_type = request["port_type"]
try:
nio = self.create_nio(ethsw, request)
except DynamipsError as e:
self.send_custom_error(str(e))
return
try:
ethsw.add_nio(nio, port)
if port_type == "access":
ethsw.set_access_port(port, vlan)
elif port_type == "dot1q":
ethsw.set_dot1q_port(port, vlan)
elif port_type == "qinq":
ethsw.set_qinq_port(port, vlan)
except DynamipsError as e:
self.send_custom_error(str(e))
return
# for now send back the original request
self.send_response(request)
@IModule.route("dynamips.ethsw.delete_nio")
def ethsw_delete_nio(self, request):
"""
Deletes an NIO (Network Input/Output).
:param request: JSON request
"""
#TODO: JSON schema validation for the request
log.debug("received request {}".format(request))
ethsw_id = request["id"]
ethsw = self._ethernet_switches[ethsw_id]
port = request["port"]
try:
nio = ethsw.remove_nio(port)
nio.delete()
except DynamipsError as e:
self.send_custom_error(str(e))
return
# for now send back the original request
self.send_response(request)

@ -0,0 +1,179 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2014 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 ..nodes.frame_relay_switch import FrameRelaySwitch
from ..dynamips_error import DynamipsError
import logging
log = logging.getLogger(__name__)
class FRSW(object):
@IModule.route("dynamips.frsw.create")
def frsw_create(self, request):
"""
Creates a new Frame-Relay switch.
:param request: JSON request
"""
name = None
if request and "name" in request:
name = request["name"]
try:
hypervisor = self._hypervisor_manager.allocate_hypervisor_for_switch()
frsw = FrameRelaySwitch(hypervisor, name)
except DynamipsError as e:
self.send_custom_error(str(e))
return
response = {"name": frsw.name,
"id": frsw.id}
self._frame_relay_switches[frsw.id] = frsw
self.send_response(response)
@IModule.route("dynamips.frsw.delete")
def frsw_delete(self, request):
"""
Deletes a Frame Relay switch.
:param request: JSON request
"""
#TODO: JSON schema validation for the request
log.debug("received request {}".format(request))
frsw_id = request["id"]
frsw = self._frame_relay_switches[frsw_id]
try:
frsw.delete()
except DynamipsError as e:
self.send_custom_error(str(e))
return
self.send_response(request)
@IModule.route("dynamips.frsw.update")
def frsw_update(self, request):
"""
Updates a Frame Relay switch.
:param request: JSON request
"""
#TODO: JSON schema validation for the request
log.debug("received request {}".format(request))
frsw_id = request["id"]
frsw = self._frame_relay_switches[frsw_id]
self.send_response(request)
@IModule.route("dynamips.frsw.allocate_udp_port")
def frsw_allocate_udp_port(self, request):
"""
Allocates a UDP port in order to create an UDP NIO for an
Frame Relay switch.
:param request: JSON request
"""
#TODO: JSON schema validation for the request
log.debug("received request {}".format(request))
frsw_id = request["id"]
frsw = self._frame_relay_switches[frsw_id]
try:
# allocate a new UDP port
response = self.allocate_udp_port(frsw)
except DynamipsError as e:
self.send_custom_error(str(e))
return
self.send_response(response)
@IModule.route("dynamips.frsw.add_nio")
def frsw_add_nio(self, request):
"""
Adds an NIO (Network Input/Output) for an Frame-Relay switch.
:param request: JSON request
"""
#TODO: JSON schema validation for the request
log.debug("received request {}".format(request))
frsw_id = request["id"]
frsw = self._frame_relay_switches[frsw_id]
port = request["port"]
mapping = request["mapping"]
try:
nio = self.create_nio(frsw, request)
except DynamipsError as e:
self.send_custom_error(str(e))
return
try:
frsw.add_nio(nio, port)
# add the VCs mapped with this port/nio
for source, destination in mapping.items():
source_port, source_dlci = map(int, source.split(':'))
destination_port, destination_dlci = map(int, destination.split(':'))
if frsw.has_port(destination_port):
if (source_port, source_dlci) not in frsw.mapping and (destination_port, destination_dlci) not in frsw.mapping:
frsw.map_vc(source_port, source_dlci, destination_port, destination_dlci)
frsw.map_vc(destination_port, destination_dlci, source_port, source_dlci)
except DynamipsError as e:
self.send_custom_error(str(e))
return
# for now send back the original request
self.send_response(request)
@IModule.route("dynamips.frsw.delete_nio")
def frsw_delete_nio(self, request):
"""
Deletes an NIO (Network Input/Output).
:param request: JSON request
"""
#TODO: JSON schema validation for the request
log.debug("received request {}".format(request))
frsw_id = request["id"]
frsw = self._frame_relay_switches[frsw_id]
port = request["port"]
try:
# remove the VCs mapped with this port/nio
for source, destination in frsw.mapping.copy().items():
source_port, source_dlci = source
destination_port, destination_dlci = destination
if port == source_port:
frsw.unmap_vc(source_port, source_dlci, destination_port, destination_dlci)
frsw.unmap_vc(destination_port, destination_dlci, source_port, source_dlci)
nio = frsw.remove_nio(port)
nio.delete()
except DynamipsError as e:
self.send_custom_error(str(e))
return
# for now send back the original request
self.send_response(request)

@ -0,0 +1,367 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2014 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 os
from gns3server.modules import IModule
from ..dynamips_error import DynamipsError
from ..nodes.c1700 import C1700
from ..nodes.c2600 import C2600
from ..nodes.c2691 import C2691
from ..nodes.c3600 import C3600
from ..nodes.c3725 import C3725
from ..nodes.c3745 import C3745
from ..nodes.c7200 import C7200
from ..adapters.c7200_io_2fe import C7200_IO_2FE
from ..adapters.c7200_io_fe import C7200_IO_FE
from ..adapters.c7200_io_ge_e import C7200_IO_GE_E
from ..adapters.nm_16esw import NM_16ESW
from ..adapters.nm_1e import NM_1E
from ..adapters.nm_1fe_tx import NM_1FE_TX
from ..adapters.nm_4e import NM_4E
from ..adapters.nm_4t import NM_4T
from ..adapters.pa_2fe_tx import PA_2FE_TX
from ..adapters.pa_4e import PA_4E
from ..adapters.pa_4t import PA_4T
from ..adapters.pa_8e import PA_8E
from ..adapters.pa_8t import PA_8T
from ..adapters.pa_a1 import PA_A1
from ..adapters.pa_fe_tx import PA_FE_TX
from ..adapters.pa_ge import PA_GE
from ..adapters.pa_pos_oc3 import PA_POS_OC3
from ..adapters.wic_1enet import WIC_1ENET
from ..adapters.wic_1t import WIC_1T
from ..adapters.wic_2t import WIC_2T
import logging
log = logging.getLogger(__name__)
ADAPTER_MATRIX = {"C7200_IO_2FE": C7200_IO_2FE,
"C7200_IO_FE": C7200_IO_FE,
"C7200-IO-GE-E": C7200_IO_GE_E,
"NM-16ESW": NM_16ESW,
"NM-1E": NM_1E,
"NM-1FE-TX": NM_1FE_TX,
"NM-4E": NM_4E,
"NM-4T": NM_4T,
"PA-2FE-TX": PA_2FE_TX,
"PA-4E": PA_4E,
"PA-4T+": PA_4T,
"PA-8E": PA_8E,
"PA-8T": PA_8T,
"PA-A1": PA_A1,
"PA-FE-TX": PA_FE_TX,
"PA-GE": PA_GE,
"PA-POS-OC3": PA_POS_OC3}
WIC_MATRIX = {"WIC-1ENET": WIC_1ENET,
"WIC-1T": WIC_1T,
"WIC-2T": WIC_2T}
PLATFORMS = {'c1700': C1700,
'c2600': C2600,
'c2691': C2691,
'c3725': C3725,
'c3745': C3745,
'c3600': C3600,
'c7200': C7200}
class VM(object):
@IModule.route("dynamips.vm.create")
def vm_create(self, request):
"""
Creates a new VM (router).
:param request: JSON request
"""
if request == None:
self.send_param_error()
else:
log.debug("received request {}".format(request))
#TODO: JSON schema validation
#name = request["name"]
platform = request["platform"]
image = request["image"]
ram = request["ram"]
try:
hypervisor = self._hypervisor_manager.allocate_hypervisor_for_router(image, ram)
router = PLATFORMS[platform](hypervisor)
router.ram = ram
router.image = image
router.sparsemem = self._hypervisor_manager.sparse_memory_support
router.mmap = self._hypervisor_manager.mmap_support
# JIT sharing support
if self._hypervisor_manager.jit_sharing_support:
jitsharing_groups = hypervisor.jitsharing_groups
ios_image = os.path.basename(image)
if ios_image in jitsharing_groups:
router.jit_sharing_group = jitsharing_groups[ios_image]
else:
new_jit_group = -1
for jit_group in range(0, 127):
if jit_group not in jitsharing_groups.values():
new_jit_group = jit_group
break
if new_jit_group == -1:
raise DynamipsError("All JIT groups are allocated!")
router.jit_sharing_group = new_jit_group
# Ghost IOS support
if self._hypervisor_manager.ghost_ios_support:
self.set_ghost_ios(router)
except DynamipsError as e:
hypervisor.decrease_memory_load(ram)
if hypervisor.memory_load == 0 and not hypervisor.devices:
hypervisor.stop()
self._hypervisor_manager.hypervisors.remove(hypervisor)
self.send_custom_error(str(e))
return
response = {"name": router.name,
"id": router.id}
defaults = router.defaults()
response.update(defaults)
self._routers[router.id] = router
self.send_response(response)
@IModule.route("dynamips.vm.delete")
def vm_delete(self, request):
"""
Deletes a VM (router).
:param request: JSON request
"""
#TODO: JSON schema validation for the request
log.debug("received request {}".format(request))
router_id = request["id"]
router = self._routers[router_id]
try:
router.delete()
except DynamipsError as e:
self.send_custom_error(str(e))
return
self.send_response(request)
@IModule.route("dynamips.vm.start")
def vm_start(self, request):
"""
Starts a VM (router)
:param request: JSON request
"""
#TODO: JSON schema validation for the request
log.debug("received request {}".format(request))
router_id = request["id"]
router = self._routers[router_id]
try:
router.start()
except DynamipsError as e:
self.send_custom_error(str(e))
return
self.send_response(request)
@IModule.route("dynamips.vm.stop")
def vm_stop(self, request):
"""
Stops a VM (router)
:param request: JSON request
"""
#TODO: JSON schema validation for the request
log.debug("received request {}".format(request))
router_id = request["id"]
router = self._routers[router_id]
try:
router.stop()
except DynamipsError as e:
self.send_custom_error(str(e))
return
self.send_response(request)
@IModule.route("dynamips.vm.suspend")
def vm_suspend(self, request):
"""
Suspends a VM (router)
:param request: JSON request
"""
#TODO: JSON schema validation for the request
log.debug("received request {}".format(request))
router_id = request["id"]
router = self._routers[router_id]
try:
router.suspend()
except DynamipsError as e:
self.send_custom_error(str(e))
return
self.send_response(request)
@IModule.route("dynamips.vm.update")
def vm_update(self, request):
"""
Updates settings for a VM (router).
:param request: JSON request
"""
#TODO: JSON schema validation for the request
log.debug("received request {}".format(request))
router_id = request["id"]
router = self._routers[router_id]
# update the settings
for name, value in request.items():
if hasattr(router, name) and getattr(router, name) != value:
try:
setattr(router, name, value)
except DynamipsError as e:
self.send_custom_error(str(e))
return
elif name.startswith("slot") and value in ADAPTER_MATRIX:
slot_id = int(name[-1])
adapter_name = value
adapter = ADAPTER_MATRIX[adapter_name]()
try:
if router.slots[slot_id] and type(router.slots[slot_id]) != type(adapter):
router.slot_remove_binding(slot_id)
router.slot_add_binding(slot_id, adapter)
except DynamipsError as e:
self.send_custom_error(str(e))
return
elif name.startswith("slot") and value == None:
slot_id = int(name[-1])
if router.slots[slot_id]:
try:
router.slot_remove_binding(slot_id)
except:
self.send_custom_error(str(e))
return
elif name.startswith("wic") and value in WIC_MATRIX:
wic_slot_id = int(name[-1])
wic_name = value
wic = WIC_MATRIX[wic_name]()
try:
if router.slots[0].wics[wic_slot_id] and type(router.slots[0].wics[wic_slot_id]) != type(wic):
router.uninstall_wic(wic_slot_id)
router.install_wic(wic_slot_id, wic)
except DynamipsError as e:
self.send_custom_error(str(e))
return
elif name.startswith("wic") and value == None:
wic_slot_id = int(name[-1])
if router.slots[0].wics and router.slots[0].wics[wic_slot_id]:
router.uninstall_wic(wic_slot_id)
# Update the ghost IOS file in case the RAM size has changed
if self._hypervisor_manager.ghost_ios_support:
self.set_ghost_ios(router)
# for now send back the original request
self.send_response(request)
@IModule.route("dynamips.vm.allocate_udp_port")
def vm_allocate_udp_port(self, request):
"""
Allocates a UDP port in order to create an UDP NIO.
:param request: JSON request
"""
#TODO: JSON schema validation for the request
log.debug("received request {}".format(request))
router_id = request["id"]
router = self._routers[router_id]
try:
# allocate a new UDP port
response = self.allocate_udp_port(router)
except DynamipsError as e:
self.send_custom_error(str(e))
return
self.send_response(response)
@IModule.route("dynamips.vm.add_nio")
def vm_add_nio(self, request):
"""
Adds an NIO (Network Input/Output) for a VM (router).
:param request: JSON request
"""
#TODO: JSON schema validation for the request
log.debug("received request {}".format(request))
router_id = request["id"]
router = self._routers[router_id]
slot = request["slot"]
port = request["port"]
print(request)
try:
nio = self.create_nio(router, request)
except DynamipsError as e:
self.send_custom_error(str(e))
return
try:
router.slot_add_nio_binding(slot, port, nio)
except DynamipsError as e:
self.send_custom_error(str(e))
return
# for now send back the original request
self.send_response(request)
@IModule.route("dynamips.vm.delete_nio")
def vm_delete_nio(self, request):
"""
Deletes an NIO (Network Input/Output).
:param request: JSON request
"""
#TODO: JSON schema validation for the request
log.debug("received request {}".format(request))
router_id = request["id"]
router = self._routers[router_id]
slot = request["slot"]
port = request["port"]
try:
nio = router.slot_remove_nio_binding(slot, port)
nio.delete()
except DynamipsError as e:
self.send_custom_error(str(e))
return
# for now send back the original request
self.send_response(request)

@ -31,3 +31,7 @@ class DynamipsError(Exception):
def __repr__(self): def __repr__(self):
return self._message return self._message
def __str__(self):
return self._message

@ -21,6 +21,7 @@ http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L46
""" """
import socket import socket
import errno
import re import re
import logging import logging
from .dynamips_error import DynamipsError from .dynamips_error import DynamipsError
@ -54,6 +55,7 @@ class DynamipsHypervisor(object):
self._baseconsole = 2000 self._baseconsole = 2000
self._baseaux = 2100 self._baseaux = 2100
self._baseudp = 10000 self._baseudp = 10000
self._current_udp_port = self._baseudp
self._version = "N/A" self._version = "N/A"
self._timeout = 30 self._timeout = 30
self._socket = None self._socket = None
@ -145,7 +147,7 @@ class DynamipsHypervisor(object):
@working_dir.setter @working_dir.setter
def working_dir(self, working_dir): def working_dir(self, working_dir):
""" """
Set the working directory for this hypervisor. Sets the working directory for this hypervisor.
:param working_dir: path to the working directory :param working_dir: path to the working directory
""" """
@ -198,7 +200,7 @@ class DynamipsHypervisor(object):
@devices.setter @devices.setter
def devices(self, devices): def devices(self, devices):
""" """
Set the list of devices managed by this hypervisor instance. Sets the list of devices managed by this hypervisor instance.
This method is for internal use. This method is for internal use.
:param devices: a list of device objects :param devices: a list of device objects
@ -219,7 +221,7 @@ class DynamipsHypervisor(object):
@baseconsole.setter @baseconsole.setter
def baseconsole(self, baseconsole): def baseconsole(self, baseconsole):
""" """
Set the base console TCP port for this hypervisor. Sets the base console TCP port for this hypervisor.
:param baseconsole: base console value (integer) :param baseconsole: base console value (integer)
""" """
@ -239,7 +241,7 @@ class DynamipsHypervisor(object):
@baseaux.setter @baseaux.setter
def baseaux(self, baseaux): def baseaux(self, baseaux):
""" """
Set the base auxiliary TCP port for this hypervisor. Sets the base auxiliary TCP port for this hypervisor.
:param baseaux: base auxiliary port value (integer) :param baseaux: base auxiliary port value (integer)
""" """
@ -259,12 +261,16 @@ class DynamipsHypervisor(object):
@baseudp.setter @baseudp.setter
def baseudp(self, baseudp): def baseudp(self, baseudp):
""" """
Set the next open UDP port for NIOs for this hypervisor. Sets the next open UDP port for NIOs for this hypervisor.
:param baseudp: base UDP port value (integer) :param baseudp: base UDP port value (integer)
""" """
self._baseudp = baseudp self._baseudp = baseudp
self._current_udp_port = self._baseudp
#FIXME
log.info("hypervisor a new base UDP {}".format(self._baseudp))
@property @property
def ghosts(self): def ghosts(self):
@ -294,7 +300,7 @@ class DynamipsHypervisor(object):
:returns: JIT sharing groups dict (image_name -> group number) :returns: JIT sharing groups dict (image_name -> group number)
""" """
return self._ghosts return self._jitsharing_groups
def add_jitsharing_group(self, image_name, group_number): def add_jitsharing_group(self, image_name, group_number):
""" """
@ -326,6 +332,42 @@ class DynamipsHypervisor(object):
return self._port return self._port
def allocate_udp_port(self, max_port=100):
"""
Allocates a new UDP port for creating an UDP NIO.
:param max_port: maximum number of port to scan in
order to find one available for use.
:returns: port number (integer)
"""
#FIXME: better check for IPv6
start_port = self._current_udp_port
print("start port = {}".format(start_port))
end_port = start_port + max_port
for port in range(start_port, end_port):
print(port)
if port > end_port:
raise DynamipsError("Could not find a free port between {0} and {1}".format(start_port, max_port))
try:
if self.host.__contains__(':'):
# IPv6 address support
s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
else:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# the port is available if bind is a success
s.bind((self._host, port))
#FIXME: increment?
self._current_udp_port += 1
print("current port = {}".format(self._current_udp_port))
return port
except socket.error as e:
if e.errno == errno.EADDRINUSE: # socket already in use
continue
else:
raise DynamipsError("UDP port allocation: {}".format(e))
def send_raw(self, string): def send_raw(self, string):
""" """
Sends a raw command to this hypervisor. Use sparingly. Sends a raw command to this hypervisor. Use sparingly.

@ -20,6 +20,7 @@ Represents a Dynamips hypervisor and starts/stops the associated Dynamips proces
""" """
import os import os
import time
import subprocess import subprocess
import logging import logging
@ -82,7 +83,7 @@ class Hypervisor(DynamipsHypervisor):
@path.setter @path.setter
def path(self, path): def path(self, path):
""" """
Set the path to the Dynamips executable. Sets the path to the Dynamips executable.
:param path: path to Dynamips :param path: path to Dynamips
""" """
@ -102,7 +103,7 @@ class Hypervisor(DynamipsHypervisor):
@port.setter @port.setter
def port(self, port): def port(self, port):
""" """
Set the port used to start the Dynamips hypervisor. Sets the port used to start the Dynamips hypervisor.
:param port: port number (integer) :param port: port number (integer)
""" """
@ -122,7 +123,7 @@ class Hypervisor(DynamipsHypervisor):
@host.setter @host.setter
def host(self, host): def host(self, host):
""" """
Set the host (binding) used to start the Dynamips hypervisor. Sets the host (binding) used to start the Dynamips hypervisor.
:param host: host/address (string) :param host: host/address (string)
""" """
@ -142,7 +143,7 @@ class Hypervisor(DynamipsHypervisor):
@workingdir.setter @workingdir.setter
def workingdir(self, workingdir): def workingdir(self, workingdir):
""" """
Set the working directory used to start the Dynamips hypervisor. Sets the working directory used to start the Dynamips hypervisor.
:param workingdir: path to a working directory :param workingdir: path to a working directory
""" """
@ -163,7 +164,7 @@ class Hypervisor(DynamipsHypervisor):
@image_ref.setter @image_ref.setter
def image_ref(self, ios_image_name): def image_ref(self, ios_image_name):
""" """
Set the reference IOS image name Sets the reference IOS image name
(used by the hypervisor manager for load-balancing purposes). (used by the hypervisor manager for load-balancing purposes).
:param ios_image_name: image reference name :param ios_image_name: image reference name
@ -230,8 +231,13 @@ class Hypervisor(DynamipsHypervisor):
""" """
if self.is_running(): if self.is_running():
DynamipsHypervisor.stop(self)
logger.info("Stopping Dynamips PID={}".format(self._process.pid)) logger.info("Stopping Dynamips PID={}".format(self._process.pid))
# give some time for the hypervisor to properly stop.
# time to delete UNIX NIOs for instance.
time.sleep(0.01)
self._process.kill() self._process.kill()
self._process.wait()
def read_stdout(self): def read_stdout(self):
""" """

@ -45,27 +45,29 @@ class HypervisorManager(object):
path, path,
workingdir, workingdir,
host='127.0.0.1', host='127.0.0.1',
base_port=7200, base_hypervisor_port=7200,
base_console=2000, base_console_port=2000,
base_aux=3000, base_aux_port=3000,
base_udp=10000): base_udp_port=10000):
self._hypervisors = [] self._hypervisors = []
self._path = path self._path = path
self._workingdir = workingdir self._workingdir = workingdir
self._base_port = base_port
self._current_port = self._base_port
self._base_console = base_console
self._base_aux = base_aux
self._base_udp = base_udp
self._host = host self._host = host
self._clean_workingdir = False self._base_hypervisor_port = base_hypervisor_port
self._ghost_ios = True self._current_port = self._base_hypervisor_port
self._mmap = True self._base_console_port = base_console_port
self._jit_sharing = False self._base_aux_port = base_aux_port
self._sparsemem = True self._base_udp_port = base_udp_port
self._current_base_udp_port = self._base_udp_port
self._udp_incrementation_per_hypervisor = 100
self._ghost_ios_support = True
self._mmap_support = True
self._jit_sharing_support = False
self._sparse_memory_support = True
self._allocate_hypervisor_per_device = True
self._memory_usage_limit_per_hypervisor = 1024 self._memory_usage_limit_per_hypervisor = 1024
self._group_ios_per_hypervisor = True self._allocate_hypervisor_per_ios_image = True
def __del__(self): def __del__(self):
""" """
@ -84,6 +86,263 @@ class HypervisorManager(object):
return self._hypervisors return self._hypervisors
@property
def path(self):
"""
Returns the Dynamips path.
:returns: path to Dynamips
"""
return self._path
@path.setter
def path(self, path):
"""
Set a new Dynamips path.
:param path: path to Dynamips
"""
self._path = path
log.info("Dynamips path set to {}".format(self._path))
@property
def workingdir(self):
"""
Returns the Dynamips working directory path.
:returns: path to Dynamips working directory
"""
return self._workingdir
@workingdir.setter
def workingdir(self, workingdir):
"""
Sets a new path to the Dynamips working directory.
:param workingdir: path to Dynamips working directory
"""
self._workingdir = workingdir
log.info("working directory set to {}".format(self._workingdir))
@property
def base_hypervisor_port(self):
"""
Returns the base hypervisor port.
:returns: base hypervisor port (integer)
"""
return self._base_hypervisor_port
@base_hypervisor_port.setter
def base_hypervisor_port(self, base_hypervisor_port):
"""
Set a new base hypervisor port.
:param base_hypervisor_port: base hypervisor port (integer)
"""
if self._base_hypervisor_port != base_hypervisor_port:
self._base_hypervisor_port = base_hypervisor_port
self._current_port = self._base_hypervisor_port
log.info("base hypervisor port set to {}".format(self._base_hypervisor_port))
@property
def base_console_port(self):
"""
Returns the base console port.
:returns: base console port (integer)
"""
return self._base_console_port
@base_console_port.setter
def base_console_port(self, base_console_port):
"""
Set a new base console port.
:param base_console_port: base console port (integer)
"""
if self._base_console_port != base_console_port:
self._base_console_port = base_console_port
log.info("base console port set to {}".format(self._base_console_port))
@property
def base_aux_port(self):
"""
Returns the base auxiliary console port.
:returns: base auxiliary console port (integer)
"""
return self._base_aux_port
@base_aux_port.setter
def base_aux_port(self, base_aux_port):
"""
Set a new base auxiliary console port.
:param base_aux_port: base auxiliary console port (integer)
"""
if self._base_aux_port != base_aux_port:
self._base_aux_port = base_aux_port
log.info("base aux port set to {}".format(self._base_aux_port))
@property
def base_udp_port(self):
"""
Returns the base UDP port.
:returns: base UDP port (integer)
"""
return self._base_udp_port
@base_udp_port.setter
def base_udp_port(self, base_udp_port):
"""
Set a new base UDP port.
:param base_udp_port: base UDP port (integer)
"""
if self._base_udp_port != base_udp_port:
self._base_udp_port = base_udp_port
self._current_base_udp_port = self._base_udp_port
log.info("base UDP port set to {}".format(self._base_udp_port))
@property
def ghost_ios_support(self):
"""
Returns either ghost IOS is activated or not.
:returns: boolean
"""
return self._ghost_ios_support
@ghost_ios_support.setter
def ghost_ios_support(self, ghost_ios_support):
"""
Sets ghost IOS support.
:param ghost_ios_support: boolean
"""
if self._ghost_ios_support != ghost_ios_support:
self._ghost_ios_support = ghost_ios_support
if ghost_ios_support:
log.info("ghost IOS support enabled")
else:
log.info("ghost IOS support disabled")
@property
def mmap_support(self):
"""
Returns either mmap is activated or not.
:returns: boolean
"""
return self._mmap_support
@mmap_support.setter
def mmap_support(self, mmap_support):
"""
Sets mmap support.
:param mmap_support: boolean
"""
if self._mmap_support != mmap_support:
self._mmap_support = mmap_support
if mmap_support:
log.info("mmap support enabled")
else:
log.info("mmap support disabled")
@property
def sparse_memory_support(self):
"""
Returns either sparse memory is activated or not.
:returns: boolean
"""
return self._sparse_memory_support
@sparse_memory_support.setter
def sparse_memory_support(self, sparse_memory_support):
"""
Sets sparse memory support.
:param sparse_memory_support: boolean
"""
if self._sparse_memory_support != sparse_memory_support:
self._sparse_memory_support = sparse_memory_support
if sparse_memory_support:
log.info("sparse memory support enabled")
else:
log.info("sparse memory support disabled")
@property
def jit_sharing_support(self):
"""
Returns either JIT sharing is activated or not.
:returns: boolean
"""
return self._jit_sharing_support
@jit_sharing_support.setter
def jit_sharing_support(self, jit_sharing_support):
"""
Sets JIT sharing support.
:param jit_sharing_support: boolean
"""
if self._jit_sharing_support != jit_sharing_support:
self._jit_sharing_support = jit_sharing_support
if jit_sharing_support:
log.info("JIT sharing support enabled")
else:
log.info("JIT sharing support disabled")
@property
def allocate_hypervisor_per_device(self):
"""
Returns either an hypervisor is created for each device.
:returns: True or False
"""
return self._allocate_hypervisor_per_device
@allocate_hypervisor_per_device.setter
def allocate_hypervisor_per_device(self, value):
"""
Sets if an hypervisor is created for each device.
:param value: True or False
"""
if self._allocate_hypervisor_per_device != value:
self._allocate_hypervisor_per_device = value
if value:
log.info("allocating an hypervisor per device enabled")
else:
log.info("allocating an hypervisor per device disabled")
@property @property
def memory_usage_limit_per_hypervisor(self): def memory_usage_limit_per_hypervisor(self):
""" """
@ -97,15 +356,17 @@ class HypervisorManager(object):
@memory_usage_limit_per_hypervisor.setter @memory_usage_limit_per_hypervisor.setter
def memory_usage_limit_per_hypervisor(self, memory_limit): def memory_usage_limit_per_hypervisor(self, memory_limit):
""" """
Set the memory usage limit per hypervisor Sets the memory usage limit per hypervisor
:param memory_limit: memory limit value (integer) :param memory_limit: memory limit value (integer)
""" """
self._memory_usage_limit_per_hypervisor = memory_limit if self._memory_usage_limit_per_hypervisor != memory_limit:
self._memory_usage_limit_per_hypervisor = memory_limit
log.info("memory usage limit per hypervisor set to {}".format(memory_limit))
@property @property
def group_ios_per_hypervisor(self): def allocate_hypervisor_per_ios_image(self):
""" """
Returns if router are grouped per hypervisor Returns if router are grouped per hypervisor
based on their IOS image. based on their IOS image.
@ -113,18 +374,23 @@ class HypervisorManager(object):
:returns: True or False :returns: True or False
""" """
return self._group_ios_per_hypervisor return self._allocate_hypervisor_per_ios_image
@group_ios_per_hypervisor.setter @allocate_hypervisor_per_ios_image.setter
def group_ios_per_hypervisor(self, value): def allocate_hypervisor_per_ios_image(self, value):
""" """
Set if router are grouped per hypervisor Sets if routers are grouped per hypervisor
based on their IOS image. based on their IOS image.
:param value: True or False :param value: True or False
""" """
self._group_ios_per_hypervisor = value if self._allocate_hypervisor_per_ios_image != value:
self._allocate_hypervisor_per_ios_image = value
if value:
log.info("allocating an hypervisor per IOS image enabled")
else:
log.info("allocating an hypervisor per IOS image disabled")
def wait_for_hypervisor(self, host, port, timeout=10): def wait_for_hypervisor(self, host, port, timeout=10):
""" """
@ -172,6 +438,10 @@ class HypervisorManager(object):
log.info("hypervisor {}:{} has successfully started".format(hypervisor.host, hypervisor.port)) log.info("hypervisor {}:{} has successfully started".format(hypervisor.host, hypervisor.port))
hypervisor.connect() hypervisor.connect()
hypervisor.baseconsole = self._base_console_port
hypervisor.baseaux = self._base_aux_port
hypervisor.baseudp = self._current_base_udp_port
self._current_base_udp_port += self._udp_incrementation_per_hypervisor
self._hypervisors.append(hypervisor) self._hypervisors.append(hypervisor)
self._current_port += 1 self._current_port += 1
return hypervisor return hypervisor
@ -187,16 +457,21 @@ class HypervisorManager(object):
:returns: the allocated hypervisor object :returns: the allocated hypervisor object
""" """
for hypervisor in self._hypervisors: # allocate an hypervisor for each router by default
if self._group_ios_per_hypervisor and hypervisor.image_ref != router_ios_image: if not self._allocate_hypervisor_per_device:
continue for hypervisor in self._hypervisors:
if (hypervisor.memory_load + router_ram) <= self._memory_usage_limit_per_hypervisor: if self._allocate_hypervisor_per_ios_image:
current_memory_load = hypervisor.memory_load if not hypervisor.image_ref:
hypervisor.increase_memory_load(router_ram) hypervisor.image_ref = router_ios_image
log.info("allocating existing hypervisor {}:{}, RAM={}+{}".format(hypervisor.host, elif hypervisor.image_ref != router_ios_image:
hypervisor.port, continue
current_memory_load, if (hypervisor.memory_load + router_ram) <= self._memory_usage_limit_per_hypervisor:
router_ram)) current_memory_load = hypervisor.memory_load
hypervisor.increase_memory_load(router_ram)
log.info("allocating existing hypervisor {}:{}, RAM={}+{}".format(hypervisor.host,
hypervisor.port,
current_memory_load,
router_ram))
return hypervisor return hypervisor
hypervisor = self.start_new_hypervisor() hypervisor = self.start_new_hypervisor()
@ -226,6 +501,22 @@ class HypervisorManager(object):
hypervisor.stop() hypervisor.stop()
self._hypervisors.remove(hypervisor) self._hypervisors.remove(hypervisor)
def allocate_hypervisor_for_switch(self):
"""
Allocates a Dynamips hypervisor for a specific switch
:returns: the allocated hypervisor object
"""
# For now always allocate the first hypervisor available,
# in the future we could randomly allocate.
if self._hypervisors:
return self._hypervisors[0]
# no hypervisor, let's start one!
return self.start_new_hypervisor()
def stop_all_hypervisors(self): def stop_all_hypervisors(self):
""" """
Stops all hypervisors. Stops all hypervisors.
@ -233,3 +524,4 @@ class HypervisorManager(object):
for hypervisor in self._hypervisors: for hypervisor in self._hypervisors:
hypervisor.stop() hypervisor.stop()
self._hypervisors = []

@ -22,6 +22,9 @@ http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L451
from ..dynamips_error import DynamipsError from ..dynamips_error import DynamipsError
import logging
log = logging.getLogger(__name__)
class NIO(object): class NIO(object):
""" """
@ -55,6 +58,7 @@ class NIO(object):
""" """
self._hypervisor.send("nio delete {}".format(self._name)) self._hypervisor.send("nio delete {}".format(self._name))
log.info("NIO {name} has been deleted".format(name=self._name))
def rename(self, new_name): def rename(self, new_name):
""" """
@ -65,6 +69,9 @@ class NIO(object):
self._hypervisor.send("nio rename {name} {new_name}".format(name=self._name, self._hypervisor.send("nio rename {name} {new_name}".format(name=self._name,
new_name=new_name)) new_name=new_name))
log.info("NIO {name} renamed to {new_name}".format(name=self._name,
new_name=new_name))
self._name = new_name self._name = new_name
def debug(self, debug): def debug(self, debug):
@ -197,7 +204,7 @@ class NIO(object):
def set_bandwidth(self, bandwidth): def set_bandwidth(self, bandwidth):
""" """
Set bandwidth constraint. Sets bandwidth constraint.
:param bandwidth: bandwidth integer value (in Kb/s) :param bandwidth: bandwidth integer value (in Kb/s)
""" """

@ -21,6 +21,9 @@ Interface for FIFO NIOs.
from .nio import NIO from .nio import NIO
import logging
log = logging.getLogger(__name__)
class NIO_FIFO(NIO): class NIO_FIFO(NIO):
""" """
@ -42,6 +45,16 @@ class NIO_FIFO(NIO):
self._hypervisor.send("nio create_fifo {}".format(self._name)) self._hypervisor.send("nio create_fifo {}".format(self._name))
log.info("NIO FIFO {name} created.".format(name=self._name))
@classmethod
def reset(cls):
"""
Reset the instance count.
"""
cls._instance_count = 0
def crossconnect(self, nio): def crossconnect(self, nio):
""" """
Establishes a cross-connect between this FIFO NIO and another one. Establishes a cross-connect between this FIFO NIO and another one.
@ -51,3 +64,5 @@ class NIO_FIFO(NIO):
self._hypervisor.send("nio crossconnect_fifo {name} {nio}".format(name=self._name, self._hypervisor.send("nio crossconnect_fifo {name} {nio}".format(name=self._name,
nio=nio)) nio=nio))
log.info("NIO FIFO {name} crossconnected with {nio_name}.".format(name=self._name, nio_name=nio.name))

@ -21,6 +21,9 @@ Interface for generic Ethernet NIOs (PCAP library).
from .nio import NIO from .nio import NIO
import logging
log = logging.getLogger(__name__)
class NIO_GenericEthernet(NIO): class NIO_GenericEthernet(NIO):
""" """
@ -45,6 +48,17 @@ class NIO_GenericEthernet(NIO):
self._hypervisor.send("nio create_gen_eth {name} {eth_device}".format(name=self._name, self._hypervisor.send("nio create_gen_eth {name} {eth_device}".format(name=self._name,
eth_device=ethernet_device)) eth_device=ethernet_device))
log.info("NIO Generic Ethernet {name} created with device {device}".format(name=self._name,
device=ethernet_device))
@classmethod
def reset(cls):
"""
Reset the instance count.
"""
cls._instance_count = 0
@property @property
def ethernet_device(self): def ethernet_device(self):
""" """

@ -21,6 +21,9 @@ Interface for Linux Ethernet NIOs (Linux only).
from .nio import NIO from .nio import NIO
import logging
log = logging.getLogger(__name__)
class NIO_LinuxEthernet(NIO): class NIO_LinuxEthernet(NIO):
""" """
@ -45,6 +48,17 @@ class NIO_LinuxEthernet(NIO):
self._hypervisor.send("nio create_linux_eth {name} {eth_device}".format(name=self._name, self._hypervisor.send("nio create_linux_eth {name} {eth_device}".format(name=self._name,
eth_device=ethernet_device)) eth_device=ethernet_device))
log.info("NIO Linux Ethernet {name} created with device {device}".format(name=self._name,
device=ethernet_device))
@classmethod
def reset(cls):
"""
Reset the instance count.
"""
cls._instance_count = 0
@property @property
def ethernet_device(self): def ethernet_device(self):
""" """

@ -21,6 +21,9 @@ Interface for multicast NIOs.
from .nio import NIO from .nio import NIO
import logging
log = logging.getLogger(__name__)
class NIO_Mcast(NIO): class NIO_Mcast(NIO):
""" """
@ -49,6 +52,18 @@ class NIO_Mcast(NIO):
mgroup=group, mgroup=group,
mport=port)) mport=port))
log.info("NIO Multicast {name} created with mgroup={group}, mport={port}".format(name=self._name,
group=group,
port=port))
@classmethod
def reset(cls):
"""
Reset the instance count.
"""
cls._instance_count = 0
@property @property
def group(self): def group(self):
""" """
@ -82,7 +97,7 @@ class NIO_Mcast(NIO):
@ttl.setter @ttl.setter
def ttl(self, ttl): def ttl(self, ttl):
""" """
Set the TTL for the multicast address Sets the TTL for the multicast address
:param ttl: TTL value :param ttl: TTL value
""" """

@ -21,6 +21,9 @@ Interface for dummy NIOs (mostly for tests).
from .nio import NIO from .nio import NIO
import logging
log = logging.getLogger(__name__)
class NIO_Null(NIO): class NIO_Null(NIO):
""" """
@ -41,3 +44,12 @@ class NIO_Null(NIO):
self._name = 'nio_null' + str(self._id) self._name = 'nio_null' + str(self._id)
self._hypervisor.send("nio create_null {}".format(self._name)) self._hypervisor.send("nio create_null {}".format(self._name))
log.info("NIO NULL {name} created.".format(name=self._name))
@classmethod
def reset(cls):
"""
Reset the instance count.
"""
cls._instance_count = 0

@ -21,6 +21,9 @@ Interface for TAP NIOs (UNIX based OSes only).
from .nio import NIO from .nio import NIO
import logging
log = logging.getLogger(__name__)
class NIO_TAP(NIO): class NIO_TAP(NIO):
""" """
@ -45,6 +48,17 @@ class NIO_TAP(NIO):
self._hypervisor.send("nio create_tap {name} {tap}".format(name=self._name, self._hypervisor.send("nio create_tap {name} {tap}".format(name=self._name,
tap=tap_device)) tap=tap_device))
log.info("NIO TAP {name} created with device {device}".format(name=self._name,
device=tap_device))
@classmethod
def reset(cls):
"""
Reset the instance count.
"""
cls._instance_count = 0
@property @property
def tap_device(self): def tap_device(self):
""" """

@ -21,6 +21,9 @@ Interface for UDP NIOs.
from .nio import NIO from .nio import NIO
import logging
log = logging.getLogger(__name__)
class NIO_UDP(NIO): class NIO_UDP(NIO):
""" """
@ -51,6 +54,19 @@ class NIO_UDP(NIO):
rhost=rhost, rhost=rhost,
rport=rport)) rport=rport))
log.info("NIO UDP {name} created with lport={lport}, rhost={rhost}, rport={rport}".format(name=self._name,
lport=lport,
rhost=rhost,
rport=rport))
@classmethod
def reset(cls):
"""
Reset the instance count.
"""
cls._instance_count = 0
@property @property
def lport(self): def lport(self):
""" """

@ -21,6 +21,9 @@ Interface for automatic UDP NIOs.
from .nio import NIO from .nio import NIO
import logging
log = logging.getLogger(__name__)
class NIO_UDP_auto(NIO): class NIO_UDP_auto(NIO):
""" """
@ -48,9 +51,22 @@ class NIO_UDP_auto(NIO):
laddr=laddr, laddr=laddr,
lport_start=lport_start, lport_start=lport_start,
lport_end=lport_end))[0]) lport_end=lport_end))[0])
log.info("NIO UDP AUTO {name} created with laddr={laddr}, lport_start={start}, lport_end={end}".format(name=self._name,
laddr=laddr,
start=lport_start,
end=lport_end))
self._raddr = None self._raddr = None
self._rport = None self._rport = None
@classmethod
def reset(cls):
"""
Reset the instance count.
"""
cls._instance_count = 0
@property @property
def laddr(self): def laddr(self):
""" """
@ -104,3 +120,7 @@ class NIO_UDP_auto(NIO):
rport=rport)) rport=rport))
self._raddr = raddr self._raddr = raddr
self._rport = rport self._rport = rport
log.info("NIO UDP AUTO {name} connected to {raddr}:{rport}".format(name=self._name,
raddr=raddr,
rport=rport))

@ -21,6 +21,9 @@ Interface for UNIX NIOs (Unix based OSes only).
from .nio import NIO from .nio import NIO
import logging
log = logging.getLogger(__name__)
class NIO_UNIX(NIO): class NIO_UNIX(NIO):
""" """
@ -48,6 +51,18 @@ class NIO_UNIX(NIO):
local=local_file, local=local_file,
remote=remote_file)) remote=remote_file))
log.info("NIO UNIX {name} created with local file {local} and remote file {remote}".format(name=self._name,
local=local_file,
remote=remote_file))
@classmethod
def reset(cls):
"""
Reset the instance count.
"""
cls._instance_count = 0
@property @property
def local_file(self): def local_file(self):
""" """

@ -21,6 +21,9 @@ Interface for VDE (Virtual Distributed Ethernet) NIOs (Unix based OSes only).
from .nio import NIO from .nio import NIO
import logging
log = logging.getLogger(__name__)
class NIO_VDE(NIO): class NIO_VDE(NIO):
""" """
@ -48,6 +51,18 @@ class NIO_VDE(NIO):
control=control_file, control=control_file,
local=local_file)) local=local_file))
log.info("NIO VDE {name} created with control={control}, local={local}".format(name=self._name,
control=control_file,
local=local_file))
@classmethod
def reset(cls):
"""
Reset the instance count.
"""
cls._instance_count = 0
@property @property
def control_file(self): def control_file(self):
""" """

@ -23,6 +23,9 @@ http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L593
from __future__ import unicode_literals from __future__ import unicode_literals
from ..dynamips_error import DynamipsError from ..dynamips_error import DynamipsError
import logging
log = logging.getLogger(__name__)
class ATMSwitch(object): class ATMSwitch(object):
""" """
@ -32,15 +35,47 @@ class ATMSwitch(object):
:param name: name for this switch :param name: name for this switch
""" """
def __init__(self, hypervisor, name): _instance_count = 1
def __init__(self, hypervisor, name=None):
# create an unique ID
self._id = ATMSwitch._instance_count
ATMSwitch._instance_count += 1
# let's create a unique name if none has been chosen
if not name:
name = "ATM" + str(self._id)
self._hypervisor = hypervisor self._hypervisor = hypervisor
self._name = '"' + name + '"' # put name into quotes to protect spaces self._name = '"' + name + '"' # put name into quotes to protect spaces
self._hypervisor.send("atmsw create {}".format(self._name)) self._hypervisor.send("atmsw create {}".format(self._name))
log.info("ATM switch {name} [id={id}] has been created".format(name=self._name,
id=self._id))
self._hypervisor.devices.append(self) self._hypervisor.devices.append(self)
self._nios = {} self._nios = {}
self._mapping = {} self._mapping = {}
@classmethod
def reset(cls):
"""
Reset the instance count.
"""
cls._instance_count = 1
@property
def id(self):
"""
Returns the unique ID for this ATM switch.
:returns: id (integer)
"""
return self._id
@property @property
def name(self): def name(self):
""" """
@ -100,6 +135,11 @@ class ATMSwitch(object):
new_name = '"' + new_name + '"' # put the new name into quotes to protect spaces new_name = '"' + new_name + '"' # put the new name into quotes to protect spaces
self._hypervisor.send("atmsw rename {name} {new_name}".format(name=self._name, self._hypervisor.send("atmsw rename {name} {new_name}".format(name=self._name,
new_name=new_name)) new_name=new_name))
log.info("ATM switch {name} [id={id}]: renamed to {new_name}".format(name=self._name,
id=self._id,
new_name=new_name))
self._name = new_name self._name = new_name
def delete(self): def delete(self):
@ -108,8 +148,22 @@ class ATMSwitch(object):
""" """
self._hypervisor.send("atmsw delete {}".format(self._name)) self._hypervisor.send("atmsw delete {}".format(self._name))
log.info("ATM switch {name} [id={id}] has been deleted".format(name=self._name,
id=self._id))
self._hypervisor.devices.remove(self) self._hypervisor.devices.remove(self)
def has_port(self, port):
"""
Checks if a port exists on this ATM switch.
:returns: boolean
"""
if port in self._nios:
return True
return False
def add_nio(self, nio, port): def add_nio(self, nio, port):
""" """
Adds a NIO as new port on ATM switch. Adds a NIO as new port on ATM switch.
@ -121,6 +175,11 @@ class ATMSwitch(object):
if port in self._nios: if port in self._nios:
raise DynamipsError("Port {} isn't free".format(port)) raise DynamipsError("Port {} isn't free".format(port))
log.info("ATM switch {name} [id={id}]: NIO {nio} bound to port {port}".format(name=self._name,
id=self._id,
nio=nio,
port=port))
self._nios[port] = nio self._nios[port] = nio
def remove_nio(self, port): def remove_nio(self, port):
@ -133,7 +192,14 @@ class ATMSwitch(object):
if port not in self._nios: if port not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port)) raise DynamipsError("Port {} is not allocated".format(port))
nio = self._nios[port]
log.info("ATM switch {name} [id={id}]: NIO {nio} removed from port {port}".format(name=self._name,
id=self._id,
nio=nio,
port=port))
del self._nios[port] del self._nios[port]
return nio
def map_vp(self, port1, vpi1, port2, vpi2): def map_vp(self, port1, vpi1, port2, vpi2):
""" """
@ -159,6 +225,14 @@ class ATMSwitch(object):
input_vpi=vpi1, input_vpi=vpi1,
output_nio=nio2, output_nio=nio2,
output_vpi=vpi2)) output_vpi=vpi2))
log.info("ATM switch {name} [id={id}]: VPC from port {port1} VPI {vpi1} to port {port2} VPI {vpi2} created".format(name=self._name,
id=self._id,
port1=port1,
vpi1=vpi1,
port2=port2,
vpi2=vpi2))
self._mapping[(port1, vpi1)] = (port2, vpi2) self._mapping[(port1, vpi1)] = (port2, vpi2)
def unmap_vp(self, port1, vpi1, port2, vpi2): def unmap_vp(self, port1, vpi1, port2, vpi2):
@ -185,6 +259,14 @@ class ATMSwitch(object):
input_vpi=vpi1, input_vpi=vpi1,
output_nio=nio2, output_nio=nio2,
output_vpi=vpi2)) output_vpi=vpi2))
log.info("ATM switch {name} [id={id}]: VPC from port {port1} VPI {vpi1} to port {port2} VPI {vpi2} deleted".format(name=self._name,
id=self._id,
port1=port1,
vpi1=vpi1,
port2=port2,
vpi2=vpi2))
del self._mapping[(port1, vpi1)] del self._mapping[(port1, vpi1)]
def map_pvc(self, port1, vpi1, vci1, port2, vpi2, vci2): def map_pvc(self, port1, vpi1, vci1, port2, vpi2, vci2):
@ -215,6 +297,16 @@ class ATMSwitch(object):
output_nio=nio2, output_nio=nio2,
output_vpi=vpi2, output_vpi=vpi2,
output_vci=vci2)) output_vci=vci2))
log.info("ATM switch {name} [id={id}]: VCC from port {port1} VPI {vpi1} VCI {vci1} to port {port2} VPI {vpi2} VCI {vci2} created".format(name=self._name,
id=self._id,
port1=port1,
vpi1=vpi1,
vci1=vci1,
port2=port2,
vpi2=vpi2,
vci2=vci2))
self._mapping[(port1, vpi1, vci1)] = (port2, vpi2, vci2) self._mapping[(port1, vpi1, vci1)] = (port2, vpi2, vci2)
def unmap_pvc(self, port1, vpi1, vci1, port2, vpi2, vci2): def unmap_pvc(self, port1, vpi1, vci1, port2, vpi2, vci2):
@ -245,4 +337,13 @@ class ATMSwitch(object):
output_nio=nio2, output_nio=nio2,
output_vpi=vpi2, output_vpi=vpi2,
output_vci=vci2)) output_vci=vci2))
log.info("ATM switch {name} [id={id}]: VCC from port {port1} VPI {vpi1} VCI {vci1} to port {port2} VPI {vpi2} VCI {vci2} deleted".format(name=self._name,
id=self._id,
port1=port1,
vpi1=vpi1,
vci1=vci1,
port2=port2,
vpi2=vpi2,
vci2=vci2))
del self._mapping[(port1, vpi1, vci1)] del self._mapping[(port1, vpi1, vci1)]

@ -88,6 +88,7 @@ class Bridge(object):
new_name = '"' + new_name + '"' # put the new name into quotes to protect spaces new_name = '"' + new_name + '"' # put the new name into quotes to protect spaces
self._hypervisor.send("nio_bridge rename {name} {new_name}".format(name=self._name, self._hypervisor.send("nio_bridge rename {name} {new_name}".format(name=self._name,
new_name=new_name)) new_name=new_name))
self._name = new_name self._name = new_name
def delete(self): def delete(self):

@ -25,6 +25,9 @@ from .router import Router
from ..adapters.c1700_mb_1fe import C1700_MB_1FE from ..adapters.c1700_mb_1fe import C1700_MB_1FE
from ..adapters.c1700_mb_wic1 import C1700_MB_WIC1 from ..adapters.c1700_mb_wic1 import C1700_MB_WIC1
import logging
log = logging.getLogger(__name__)
class C1700(Router): class C1700(Router):
""" """
@ -37,7 +40,7 @@ class C1700(Router):
1710 is not supported. 1710 is not supported.
""" """
def __init__(self, hypervisor, name, chassis="1720"): def __init__(self, hypervisor, name=None, chassis="1720"):
Router.__init__(self, hypervisor, name, platform="c1700") Router.__init__(self, hypervisor, name, platform="c1700")
# Set default values for this platform # Set default values for this platform
@ -54,6 +57,27 @@ class C1700(Router):
self._setup_chassis() self._setup_chassis()
def defaults(self):
"""
Returns all the default attribute values for this platform.
:returns: default values (dictionary)
"""
router_defaults = Router.defaults(self)
platform_defaults = {"ram": self._ram,
"nvram": self._nvram,
"disk0": self._disk0,
"disk1": self._disk1,
"chassis": self._chassis,
"iomem": self._iomem,
"clock_divisor": self._clock_divisor}
# update the router defaults with the platform specific defaults
router_defaults.update(platform_defaults)
return router_defaults
def list(self): def list(self):
""" """
Returns all c1700 instances Returns all c1700 instances
@ -65,7 +89,7 @@ class C1700(Router):
def _setup_chassis(self): def _setup_chassis(self):
""" """
Set up the router with the corresponding chassis Sets up the router with the corresponding chassis
(create slots and insert default adapters). (create slots and insert default adapters).
""" """
@ -91,7 +115,7 @@ class C1700(Router):
@chassis.setter @chassis.setter
def chassis(self, chassis): def chassis(self, chassis):
""" """
Set the chassis. Sets the chassis.
:param: chassis string: :param: chassis string:
1720, 1721, 1750, 1751 or 1760 1720, 1721, 1750, 1751 or 1760
@ -99,6 +123,11 @@ class C1700(Router):
self._hypervisor.send("c1700 set_chassis {name} {chassis}".format(name=self._name, self._hypervisor.send("c1700 set_chassis {name} {chassis}".format(name=self._name,
chassis=chassis)) chassis=chassis))
log.info("router {name} [id={id}]: chassis set to {chassis}".format(name=self._name,
id=self._id,
chassis=chassis))
self._chassis = chassis self._chassis = chassis
self._setup_chassis() self._setup_chassis()
@ -115,11 +144,16 @@ class C1700(Router):
@iomem.setter @iomem.setter
def iomem(self, iomem): def iomem(self, iomem):
""" """
Set I/O memory size for this router. Sets I/O memory size for this router.
:param iomem: I/O memory size :param iomem: I/O memory size
""" """
self._hypervisor.send("c1700 set_iomem {name} {size}".format(name=self._name, self._hypervisor.send("c1700 set_iomem {name} {size}".format(name=self._name,
size=iomem)) size=iomem))
log.info("router {name} [id={id}]: I/O memory updated from {old_iomem}% to {new_iomem}%".format(name=self._name,
id=self._id,
old_iomem=self._iomem,
new_iomem=iomem))
self._iomem = iomem self._iomem = iomem

@ -27,6 +27,9 @@ from ..adapters.c2600_mb_2e import C2600_MB_2E
from ..adapters.c2600_mb_1fe import C2600_MB_1FE from ..adapters.c2600_mb_1fe import C2600_MB_1FE
from ..adapters.c2600_mb_2fe import C2600_MB_2FE from ..adapters.c2600_mb_2fe import C2600_MB_2FE
import logging
log = logging.getLogger(__name__)
class C2600(Router): class C2600(Router):
""" """
@ -52,7 +55,7 @@ class C2600(Router):
'2650XM': C2600_MB_1FE, '2650XM': C2600_MB_1FE,
'2651XM': C2600_MB_2FE} '2651XM': C2600_MB_2FE}
def __init__(self, hypervisor, name, chassis="2610"): def __init__(self, hypervisor, name=None, chassis="2610"):
Router.__init__(self, hypervisor, name, platform="c2600") Router.__init__(self, hypervisor, name, platform="c2600")
# Set default values for this platform # Set default values for this platform
@ -69,6 +72,27 @@ class C2600(Router):
self._setup_chassis() self._setup_chassis()
def defaults(self):
"""
Returns all the default attribute values for this platform.
:returns: default values (dictionary)
"""
router_defaults = Router.defaults(self)
platform_defaults = {"ram": self._ram,
"nvram": self._nvram,
"disk0": self._disk0,
"disk1": self._disk1,
"iomem": self._iomem,
"chassis": self._chassis,
"clock_divisor": self._clock_divisor}
# update the router defaults with the platform specific defaults
router_defaults.update(platform_defaults)
return router_defaults
def list(self): def list(self):
""" """
Returns all c2600 instances Returns all c2600 instances
@ -80,7 +104,7 @@ class C2600(Router):
def _setup_chassis(self): def _setup_chassis(self):
""" """
Set up the router with the corresponding chassis Sets up the router with the corresponding chassis
(create slots and insert default adapters). (create slots and insert default adapters).
""" """
@ -100,7 +124,7 @@ class C2600(Router):
@chassis.setter @chassis.setter
def chassis(self, chassis): def chassis(self, chassis):
""" """
Set the chassis. Sets the chassis.
:param: chassis string: :param: chassis string:
2610, 2611, 2620, 2621, 2610XM, 2611XM 2610, 2611, 2620, 2621, 2610XM, 2611XM
@ -109,6 +133,10 @@ class C2600(Router):
self._hypervisor.send("c2600 set_chassis {name} {chassis}".format(name=self._name, self._hypervisor.send("c2600 set_chassis {name} {chassis}".format(name=self._name,
chassis=chassis)) chassis=chassis))
log.info("router {name} [id={id}]: chassis set to {chassis}".format(name=self._name,
id=self._id,
chassis=chassis))
self._chassis = chassis self._chassis = chassis
self._setup_chassis() self._setup_chassis()
@ -125,11 +153,16 @@ class C2600(Router):
@iomem.setter @iomem.setter
def iomem(self, iomem): def iomem(self, iomem):
""" """
Set I/O memory size for this router. Sets I/O memory size for this router.
:param iomem: I/O memory size :param iomem: I/O memory size
""" """
self._hypervisor.send("c2600 set_iomem {name} {size}".format(name=self._name, self._hypervisor.send("c2600 set_iomem {name} {size}".format(name=self._name,
size=iomem)) size=iomem))
log.info("router {name} [id={id}]: I/O memory updated from {old_iomem}% to {new_iomem}%".format(name=self._name,
id=self._id,
old_iomem=self._iomem,
new_iomem=iomem))
self._iomem = iomem self._iomem = iomem

@ -24,6 +24,9 @@ from __future__ import unicode_literals
from .router import Router from .router import Router
from ..adapters.gt96100_fe import GT96100_FE from ..adapters.gt96100_fe import GT96100_FE
import logging
log = logging.getLogger(__name__)
class C2691(Router): class C2691(Router):
""" """
@ -33,7 +36,7 @@ class C2691(Router):
:param name: name for this router :param name: name for this router
""" """
def __init__(self, hypervisor, name): def __init__(self, hypervisor, name=None):
Router.__init__(self, hypervisor, name, platform="c2691") Router.__init__(self, hypervisor, name, platform="c2691")
# Set default values for this platform # Set default values for this platform
@ -47,6 +50,26 @@ class C2691(Router):
self._create_slots(2) self._create_slots(2)
self._slots[0] = GT96100_FE() self._slots[0] = GT96100_FE()
def defaults(self):
"""
Returns all the default attribute values for this platform.
:returns: default values (dictionary)
"""
router_defaults = Router.defaults(self)
platform_defaults = {"ram": self._ram,
"nvram": self._nvram,
"disk0": self._disk0,
"disk1": self._disk1,
"iomem": self._iomem,
"clock_divisor": self._clock_divisor}
# update the router defaults with the platform specific defaults
router_defaults.update(platform_defaults)
return router_defaults
def list(self): def list(self):
""" """
Returns all c2691 instances Returns all c2691 instances
@ -69,11 +92,16 @@ class C2691(Router):
@iomem.setter @iomem.setter
def iomem(self, iomem): def iomem(self, iomem):
""" """
Set I/O memory size for this router. Sets I/O memory size for this router.
:param iomem: I/O memory size :param iomem: I/O memory size
""" """
self._hypervisor.send("c2691 set_iomem {name} {size}".format(name=self._name, self._hypervisor.send("c2691 set_iomem {name} {size}".format(name=self._name,
size=iomem)) size=iomem))
log.info("router {name} [id={id}]: I/O memory updated from {old_iomem}% to {new_iomem}%".format(name=self._name,
id=self._id,
old_iomem=self._iomem,
new_iomem=iomem))
self._iomem = iomem self._iomem = iomem

@ -24,6 +24,9 @@ from __future__ import unicode_literals
from .router import Router from .router import Router
from ..adapters.leopard_2fe import Leopard_2FE from ..adapters.leopard_2fe import Leopard_2FE
import logging
log = logging.getLogger(__name__)
class C3600(Router): class C3600(Router):
""" """
@ -35,7 +38,7 @@ class C3600(Router):
3620, 3640 or 3660 (default = 3640). 3620, 3640 or 3660 (default = 3640).
""" """
def __init__(self, hypervisor, name, chassis="3640"): def __init__(self, hypervisor, name=None, chassis="3640"):
Router.__init__(self, hypervisor, name, platform="c3600") Router.__init__(self, hypervisor, name, platform="c3600")
# Set default values for this platform # Set default values for this platform
@ -52,6 +55,27 @@ class C3600(Router):
self._setup_chassis() self._setup_chassis()
def defaults(self):
"""
Returns all the default attribute values for this platform.
:returns: default values (dictionary)
"""
router_defaults = Router.defaults(self)
platform_defaults = {"ram": self._ram,
"nvram": self._nvram,
"disk0": self._disk0,
"disk1": self._disk1,
"iomem": self._iomem,
"chassis": self._chassis,
"clock_divisor": self._clock_divisor}
# update the router defaults with the platform specific defaults
router_defaults.update(platform_defaults)
return router_defaults
def list(self): def list(self):
""" """
Returns all c3600 instances Returns all c3600 instances
@ -63,7 +87,7 @@ class C3600(Router):
def _setup_chassis(self): def _setup_chassis(self):
""" """
Set up the router with the corresponding chassis Sets up the router with the corresponding chassis
(create slots and insert default adapters). (create slots and insert default adapters).
""" """
@ -88,13 +112,18 @@ class C3600(Router):
@chassis.setter @chassis.setter
def chassis(self, chassis): def chassis(self, chassis):
""" """
Set the chassis. Sets the chassis.
:param: chassis string: 3620, 3640 or 3660 :param: chassis string: 3620, 3640 or 3660
""" """
self._hypervisor.send("c3600 set_chassis {name} {chassis}".format(name=self._name, self._hypervisor.send("c3600 set_chassis {name} {chassis}".format(name=self._name,
chassis=chassis)) chassis=chassis))
log.info("router {name} [id={id}]: chassis set to {chassis}".format(name=self._name,
id=self._id,
chassis=chassis))
self._chassis = chassis self._chassis = chassis
self._setup_chassis() self._setup_chassis()
@ -118,4 +147,9 @@ class C3600(Router):
self._hypervisor.send("c3600 set_iomem {name} {size}".format(name=self._name, self._hypervisor.send("c3600 set_iomem {name} {size}".format(name=self._name,
size=iomem)) size=iomem))
log.info("router {name} [id={id}]: I/O memory updated from {old_iomem}% to {new_iomem}%".format(name=self._name,
id=self._id,
old_iomem=self._iomem,
new_iomem=iomem))
self._iomem = iomem self._iomem = iomem

@ -24,6 +24,9 @@ from __future__ import unicode_literals
from .router import Router from .router import Router
from ..adapters.gt96100_fe import GT96100_FE from ..adapters.gt96100_fe import GT96100_FE
import logging
log = logging.getLogger(__name__)
class C3725(Router): class C3725(Router):
""" """
@ -33,7 +36,7 @@ class C3725(Router):
:param name: name for this router :param name: name for this router
""" """
def __init__(self, hypervisor, name): def __init__(self, hypervisor, name=None):
Router.__init__(self, hypervisor, name, platform="c3725") Router.__init__(self, hypervisor, name, platform="c3725")
# Set default values for this platform # Set default values for this platform
@ -47,6 +50,26 @@ class C3725(Router):
self._create_slots(3) self._create_slots(3)
self._slots[0] = GT96100_FE() self._slots[0] = GT96100_FE()
def defaults(self):
"""
Returns all the default attribute values for this platform.
:returns: default values (dictionary)
"""
router_defaults = Router.defaults(self)
platform_defaults = {"ram": self._ram,
"nvram": self._nvram,
"disk0": self._disk0,
"disk1": self._disk1,
"iomem": self._iomem,
"clock_divisor": self._clock_divisor}
# update the router defaults with the platform specific defaults
router_defaults.update(platform_defaults)
return router_defaults
def list(self): def list(self):
""" """
Returns all c3725 instances. Returns all c3725 instances.
@ -69,11 +92,16 @@ class C3725(Router):
@iomem.setter @iomem.setter
def iomem(self, iomem): def iomem(self, iomem):
""" """
Set I/O memory size for this router. Sets I/O memory size for this router.
:param iomem: I/O memory size :param iomem: I/O memory size
""" """
self._hypervisor.send("c3725 set_iomem {name} {size}".format(name=self._name, self._hypervisor.send("c3725 set_iomem {name} {size}".format(name=self._name,
size=iomem)) size=iomem))
log.info("router {name} [id={id}]: I/O memory updated from {old_iomem}% to {new_iomem}%".format(name=self._name,
id=self._id,
old_iomem=self._iomem,
new_iomem=iomem))
self._iomem = iomem self._iomem = iomem

@ -24,6 +24,9 @@ from __future__ import unicode_literals
from .router import Router from .router import Router
from ..adapters.gt96100_fe import GT96100_FE from ..adapters.gt96100_fe import GT96100_FE
import logging
log = logging.getLogger(__name__)
class C3745(Router): class C3745(Router):
""" """
@ -33,7 +36,7 @@ class C3745(Router):
:param name: name for this router :param name: name for this router
""" """
def __init__(self, hypervisor, name): def __init__(self, hypervisor, name=None):
Router.__init__(self, hypervisor, name, platform="c3745") Router.__init__(self, hypervisor, name, platform="c3745")
# Set default values for this platform # Set default values for this platform
@ -47,6 +50,26 @@ class C3745(Router):
self._create_slots(5) self._create_slots(5)
self._slots[0] = GT96100_FE() self._slots[0] = GT96100_FE()
def defaults(self):
"""
Returns all the default attribute values for this platform.
:returns: default values (dictionary)
"""
router_defaults = Router.defaults(self)
platform_defaults = {"ram": self._ram,
"nvram": self._nvram,
"disk0": self._disk0,
"disk1": self._disk1,
"iomem": self._iomem,
"clock_divisor": self._clock_divisor}
# update the router defaults with the platform specific defaults
router_defaults.update(platform_defaults)
return router_defaults
def list(self): def list(self):
""" """
Returns all c3745 instances. Returns all c3745 instances.
@ -69,11 +92,16 @@ class C3745(Router):
@iomem.setter @iomem.setter
def iomem(self, iomem): def iomem(self, iomem):
""" """
Set I/O memory size for this router. Sets I/O memory size for this router.
:param iomem: I/O memory size :param iomem: I/O memory size
""" """
self._hypervisor.send("c3745 set_iomem {name} {size}".format(name=self._name, self._hypervisor.send("c3745 set_iomem {name} {size}".format(name=self._name,
size=iomem)) size=iomem))
log.info("router {name} [id={id}]: I/O memory updated from {old_iomem}% to {new_iomem}%".format(name=self._name,
id=self._id,
old_iomem=self._iomem,
new_iomem=iomem))
self._iomem = iomem self._iomem = iomem

@ -20,13 +20,15 @@ Interface for Dynamips virtual Cisco 7200 instances module ("c7200")
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L294 http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L294
""" """
from __future__ import unicode_literals from __future__ import unicode_literals
from ..dynamips_error import DynamipsError from ..dynamips_error import DynamipsError
from .router import Router from .router import Router
from ..adapters.c7200_io_2fe import C7200_IO_2FE from ..adapters.c7200_io_2fe import C7200_IO_2FE
from ..adapters.c7200_io_ge_e import C7200_IO_GE_E from ..adapters.c7200_io_ge_e import C7200_IO_GE_E
import logging
log = logging.getLogger(__name__)
class C7200(Router): class C7200(Router):
""" """
@ -37,7 +39,7 @@ class C7200(Router):
:param npe: default NPE :param npe: default NPE
""" """
def __init__(self, hypervisor, name, npe="npe-400"): def __init__(self, hypervisor, name=None, npe="npe-400"):
Router.__init__(self, hypervisor, name, platform="c7200") Router.__init__(self, hypervisor, name, platform="c7200")
# Set default values for this platform # Set default values for this platform
@ -70,6 +72,27 @@ class C7200(Router):
else: else:
self._slots[0] = C7200_IO_2FE() self._slots[0] = C7200_IO_2FE()
def defaults(self):
"""
Returns all the default attribute values for this platform.
:returns: default values (dictionary)
"""
router_defaults = Router.defaults(self)
platform_defaults = {"ram": self._ram,
"nvram": self._nvram,
"disk0": self._disk0,
"disk1": self._disk1,
"npe": self._npe,
"midplane": self._midplane,
"clock_divisor": self._clock_divisor}
# update the router defaults with the platform specific defaults
router_defaults.update(platform_defaults)
return router_defaults
def list(self): def list(self):
""" """
Returns all c7200 instances. Returns all c7200 instances.
@ -92,7 +115,7 @@ class C7200(Router):
@npe.setter @npe.setter
def npe(self, npe): def npe(self, npe):
""" """
Set the NPE model. Sets the NPE model.
:params npe: NPE model string (e.g. "npe-200") :params npe: NPE model string (e.g. "npe-200")
NPE models are npe-100, npe-150, npe-175, npe-200, NPE models are npe-100, npe-150, npe-175, npe-200,
@ -104,6 +127,11 @@ class C7200(Router):
self._hypervisor.send("c7200 set_npe {name} {npe}".format(name=self._name, self._hypervisor.send("c7200 set_npe {name} {npe}".format(name=self._name,
npe=npe)) npe=npe))
log.info("router {name} [id={id}]: NPE updated from {old_npe} to {new_npe}".format(name=self._name,
id=self._id,
old_npe=self._npe,
new_npe=npe))
self._npe = npe self._npe = npe
@property @property
@ -119,13 +147,18 @@ class C7200(Router):
@midplane.setter @midplane.setter
def midplane(self, midplane): def midplane(self, midplane):
""" """
Set the midplane model. Sets the midplane model.
:returns: midplane model string (e.g. "vxr" or "std") :returns: midplane model string (e.g. "vxr" or "std")
""" """
self._hypervisor.send("c7200 set_midplane {name} {midplane}".format(name=self._name, self._hypervisor.send("c7200 set_midplane {name} {midplane}".format(name=self._name,
midplane=midplane)) midplane=midplane))
log.info("router {name} [id={id}]: midplane updated from {old_midplane} to {new_midplane}".format(name=self._name,
id=self._id,
old_midplane=self._midplane,
new_midplane=midplane))
self._midplane = midplane self._midplane = midplane
@property @property
@ -141,7 +174,7 @@ class C7200(Router):
@sensors.setter @sensors.setter
def sensors(self, sensors): def sensors(self, sensors):
""" """
Set the 4 sensors with temperature in degree Celcius. Sets the 4 sensors with temperature in degree Celcius.
:param sensors: list of 4 sensor temperatures corresponding to :param sensors: list of 4 sensor temperatures corresponding to
sensor 1 = I/0 controller inlet sensor 1 = I/0 controller inlet
@ -156,6 +189,13 @@ class C7200(Router):
self._hypervisor.send("c7200 set_temp_sensor {name} {sensor_id} {temp}".format(name=self._name, self._hypervisor.send("c7200 set_temp_sensor {name} {sensor_id} {temp}".format(name=self._name,
sensor_id=sensor_id, sensor_id=sensor_id,
temp=sensor)) temp=sensor))
log.info("router {name} [id={id}]: sensor {sensor_id} temperature updated from {old_temp}C to {new_temp}C".format(name=self._name,
id=self._id,
sensor_id=sensor_id,
old_temp=self._sensors[sensor_id],
new_temp=sensors[sensor_id]))
sensor_id += 1 sensor_id += 1
self._sensors = sensors self._sensors = sensors
@ -172,7 +212,7 @@ class C7200(Router):
@power_supplies.setter @power_supplies.setter
def power_supplies(self, power_supplies): def power_supplies(self, power_supplies):
""" """
Set the 2 power supplies with 0 = off, 1 = on. Sets the 2 power supplies with 0 = off, 1 = on.
:param power_supplies: list of 2 power supplies. :param power_supplies: list of 2 power supplies.
Example: [1, 0] = first power supply is on, second is off. Example: [1, 0] = first power supply is on, second is off.
@ -183,5 +223,11 @@ class C7200(Router):
self._hypervisor.send("c7200 set_power_supply {name} {power_supply_id} {powered_on}".format(name=self._name, self._hypervisor.send("c7200 set_power_supply {name} {power_supply_id} {powered_on}".format(name=self._name,
power_supply_id=power_supply_id, power_supply_id=power_supply_id,
powered_on=power_supply)) powered_on=power_supply))
log.info("router {name} [id={id}]: power supply {power_supply_id} state updated to {powered_on}".format(name=self._name,
id=self._id,
power_supply_id=power_supply_id,
powered_on=power_supply))
power_supply_id += 1 power_supply_id += 1
self._power_supplies = power_supplies self._power_supplies = power_supplies

@ -24,6 +24,9 @@ http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L558
from __future__ import unicode_literals from __future__ import unicode_literals
from ..dynamips_error import DynamipsError from ..dynamips_error import DynamipsError
import logging
log = logging.getLogger(__name__)
class EthernetSwitch(object): class EthernetSwitch(object):
""" """
@ -33,15 +36,47 @@ class EthernetSwitch(object):
:param name: name for this switch :param name: name for this switch
""" """
def __init__(self, hypervisor, name): _instance_count = 1
def __init__(self, hypervisor, name=None):
# create an unique ID
self._id = EthernetSwitch._instance_count
EthernetSwitch._instance_count += 1
# let's create a unique name if none has been chosen
if not name:
name = "SW" + str(self._id)
self._hypervisor = hypervisor self._hypervisor = hypervisor
self._name = '"' + name + '"' # put name into quotes to protect spaces self._name = '"' + name + '"' # put name into quotes to protect spaces
self._hypervisor.send("ethsw create {}".format(self._name)) self._hypervisor.send("ethsw create {}".format(self._name))
log.info("Ethernet switch {name} [id={id}] has been created".format(name=self._name,
id=self._id))
self._hypervisor.devices.append(self) self._hypervisor.devices.append(self)
self._nios = {} self._nios = {}
self._mapping = {} self._mapping = {}
@classmethod
def reset(cls):
"""
Reset the instance count.
"""
cls._instance_count = 1
@property
def id(self):
"""
Returns the unique ID for this Ethernet switch.
:returns: id (integer)
"""
return self._id
@property @property
def name(self): def name(self):
""" """
@ -101,6 +136,11 @@ class EthernetSwitch(object):
new_name = '"' + new_name + '"' # put the new name into quotes to protect spaces new_name = '"' + new_name + '"' # put the new name into quotes to protect spaces
self._hypervisor.send("ethsw rename {name} {new_name}".format(name=self._name, self._hypervisor.send("ethsw rename {name} {new_name}".format(name=self._name,
new_name=new_name)) new_name=new_name))
log.info("Ethernet switch {name} [id={id}]: renamed to {new_name}".format(name=self._name,
id=self._id,
new_name=new_name))
self._name = new_name self._name = new_name
def delete(self): def delete(self):
@ -109,6 +149,9 @@ class EthernetSwitch(object):
""" """
self._hypervisor.send("ethsw delete {}".format(self._name)) self._hypervisor.send("ethsw delete {}".format(self._name))
log.info("Ethernet switch {name} [id={id}] has been deleted".format(name=self._name,
id=self._id))
self._hypervisor.devices.remove(self) self._hypervisor.devices.remove(self)
def add_nio(self, nio, port): def add_nio(self, nio, port):
@ -124,6 +167,11 @@ class EthernetSwitch(object):
self._hypervisor.send("ethsw add_nio {name} {nio}".format(name=self._name, self._hypervisor.send("ethsw add_nio {name} {nio}".format(name=self._name,
nio=nio)) nio=nio))
log.info("Ethernet switch {name} [id={id}]: NIO {nio} bound to port {port}".format(name=self._name,
id=self._id,
nio=nio,
port=port))
self._nios[port] = nio self._nios[port] = nio
def remove_nio(self, port): def remove_nio(self, port):
@ -131,6 +179,8 @@ class EthernetSwitch(object):
Removes the specified NIO as member of this Ethernet switch. Removes the specified NIO as member of this Ethernet switch.
:param port: allocated port :param port: allocated port
:returns: the NIO that was bound to the port
""" """
if port not in self._nios: if port not in self._nios:
@ -139,14 +189,22 @@ class EthernetSwitch(object):
nio = self._nios[port] nio = self._nios[port]
self._hypervisor.send("ethsw remove_nio {name} {nio}".format(name=self._name, self._hypervisor.send("ethsw remove_nio {name} {nio}".format(name=self._name,
nio=nio)) nio=nio))
log.info("Ethernet switch {name} [id={id}]: NIO {nio} removed from port {port}".format(name=self._name,
id=self._id,
nio=nio,
port=port))
del self._nios[port] del self._nios[port]
if port in self._mapping: if port in self._mapping:
del self._mapping[port] del self._mapping[port]
return nio
def set_access_port(self, port, vlan_id): def set_access_port(self, port, vlan_id):
""" """
Set the specified port as an ACCESS port. Sets the specified port as an ACCESS port.
:param port: allocated port :param port: allocated port
:param vlan_id: VLAN number membership :param vlan_id: VLAN number membership
@ -159,11 +217,16 @@ class EthernetSwitch(object):
self._hypervisor.send("ethsw set_access_port {name} {nio} {vlan_id}".format(name=self._name, self._hypervisor.send("ethsw set_access_port {name} {nio} {vlan_id}".format(name=self._name,
nio=nio, nio=nio,
vlan_id=vlan_id)) vlan_id=vlan_id))
log.info("Ethernet switch {name} [id={id}]: port {port} set as an access port in VLAN {vlan_id}".format(name=self._name,
id=self._id,
port=port,
vlan_id=vlan_id))
self._mapping[port] = ("access", vlan_id) self._mapping[port] = ("access", vlan_id)
def set_dot1q_port(self, port, native_vlan): def set_dot1q_port(self, port, native_vlan):
""" """
Set the specified port as a 802.1Q trunk port. Sets the specified port as a 802.1Q trunk port.
:param port: allocated port :param port: allocated port
:param native_vlan: native VLAN for this trunk port :param native_vlan: native VLAN for this trunk port
@ -176,11 +239,17 @@ class EthernetSwitch(object):
self._hypervisor.send("ethsw set_dot1q_port {name} {nio} {native_vlan}".format(name=self._name, self._hypervisor.send("ethsw set_dot1q_port {name} {nio} {native_vlan}".format(name=self._name,
nio=nio, nio=nio,
native_vlan=native_vlan)) native_vlan=native_vlan))
log.info("Ethernet switch {name} [id={id}]: port {port} set as a 802.1Q port with native VLAN {vlan_id}".format(name=self._name,
id=self._id,
port=port,
vlan_id=native_vlan))
self._mapping[port] = ("dot1q", native_vlan) self._mapping[port] = ("dot1q", native_vlan)
def set_qinq_port(self, port, outer_vlan): def set_qinq_port(self, port, outer_vlan):
""" """
Set the specified port as a trunk (QinQ) port. Sets the specified port as a trunk (QinQ) port.
:param port: allocated port :param port: allocated port
:param outer_vlan: outer VLAN (transport VLAN) for this QinQ port :param outer_vlan: outer VLAN (transport VLAN) for this QinQ port
@ -193,6 +262,11 @@ class EthernetSwitch(object):
self._hypervisor.send("ethsw set_qinq_port {name} {nio} {outer_vlan}".format(name=self._name, self._hypervisor.send("ethsw set_qinq_port {name} {nio} {outer_vlan}".format(name=self._name,
nio=nio, nio=nio,
outer_vlan=outer_vlan)) outer_vlan=outer_vlan))
log.info("Ethernet switch {name} [id={id}]: port {port} set as a QinQ port with outer VLAN {vlan_id}".format(name=self._name,
id=self._id,
port=port,
vlan_id=outer_vlan))
self._mapping[port] = ("qinq", outer_vlan) self._mapping[port] = ("qinq", outer_vlan)
def get_mac_addr_table(self): def get_mac_addr_table(self):

@ -23,6 +23,9 @@ http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L642
from __future__ import unicode_literals from __future__ import unicode_literals
from ..dynamips_error import DynamipsError from ..dynamips_error import DynamipsError
import logging
log = logging.getLogger(__name__)
class FrameRelaySwitch(object): class FrameRelaySwitch(object):
""" """
@ -32,15 +35,47 @@ class FrameRelaySwitch(object):
:param name: name for this switch :param name: name for this switch
""" """
def __init__(self, hypervisor, name): _instance_count = 1
def __init__(self, hypervisor, name=None):
# create an unique ID
self._id = FrameRelaySwitch._instance_count
FrameRelaySwitch._instance_count += 1
# let's create a unique name if none has been chosen
if not name:
name = "FR" + str(self._id)
self._hypervisor = hypervisor self._hypervisor = hypervisor
self._name = '"' + name + '"' # put name into quotes to protect spaces self._name = '"' + name + '"' # put name into quotes to protect spaces
self._hypervisor.send("frsw create {}".format(self._name)) self._hypervisor.send("frsw create {}".format(self._name))
log.info("Frame Relay switch {name} [id={id}] has been created".format(name=self._name,
id=self._id))
self._hypervisor.devices.append(self) self._hypervisor.devices.append(self)
self._nios = {} self._nios = {}
self._mapping = {} self._mapping = {}
@classmethod
def reset(cls):
"""
Reset the instance count.
"""
cls._instance_count = 1
@property
def id(self):
"""
Returns the unique ID for this Frame Relay switch.
:returns: id (integer)
"""
return self._id
@property @property
def name(self): def name(self):
""" """
@ -100,6 +135,11 @@ class FrameRelaySwitch(object):
new_name = '"' + new_name + '"' # put the new name into quotes to protect spaces new_name = '"' + new_name + '"' # put the new name into quotes to protect spaces
self._hypervisor.send("frsw rename {name} {new_name}".format(name=self._name, self._hypervisor.send("frsw rename {name} {new_name}".format(name=self._name,
new_name=new_name)) new_name=new_name))
log.info("Frame Relay switch {name} [id={id}]: renamed to {new_name}".format(name=self._name,
id=self._id,
new_name=new_name))
self._name = new_name self._name = new_name
def delete(self): def delete(self):
@ -108,8 +148,22 @@ class FrameRelaySwitch(object):
""" """
self._hypervisor.send("frsw delete {}".format(self._name)) self._hypervisor.send("frsw delete {}".format(self._name))
log.info("Frame Relay switch {name} [id={id}] has been deleted".format(name=self._name,
id=self._id))
self._hypervisor.devices.remove(self) self._hypervisor.devices.remove(self)
def has_port(self, port):
"""
Checks if a port exists on this Frame Relay switch.
:returns: boolean
"""
if port in self._nios:
return True
return False
def add_nio(self, nio, port): def add_nio(self, nio, port):
""" """
Adds a NIO as new port on Frame Relay switch. Adds a NIO as new port on Frame Relay switch.
@ -121,6 +175,11 @@ class FrameRelaySwitch(object):
if port in self._nios: if port in self._nios:
raise DynamipsError("Port {} isn't free".format(port)) raise DynamipsError("Port {} isn't free".format(port))
log.info("Frame Relay switch {name} [id={id}]: NIO {nio} bound to port {port}".format(name=self._name,
id=self._id,
nio=nio,
port=port))
self._nios[port] = nio self._nios[port] = nio
def remove_nio(self, port): def remove_nio(self, port):
@ -128,12 +187,21 @@ class FrameRelaySwitch(object):
Removes the specified NIO as member of this Frame Relay switch. Removes the specified NIO as member of this Frame Relay switch.
:param port: allocated port :param port: allocated port
:returns: the NIO that was bound to the allocated port
""" """
if port not in self._nios: if port not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port)) raise DynamipsError("Port {} is not allocated".format(port))
nio = self._nios[port]
log.info("Frame Relay switch {name} [id={id}]: NIO {nio} removed from port {port}".format(name=self._name,
id=self._id,
nio=nio,
port=port))
del self._nios[port] del self._nios[port]
return nio
def map_vc(self, port1, dlci1, port2, dlci2): def map_vc(self, port1, dlci1, port2, dlci2):
""" """
@ -159,6 +227,14 @@ class FrameRelaySwitch(object):
input_dlci=dlci1, input_dlci=dlci1,
output_nio=nio2, output_nio=nio2,
output_dlci=dlci2)) output_dlci=dlci2))
log.info("Frame Relay switch {name} [id={id}]: VC from port {port1} DLCI {dlci1} to port {port2} DLCI {dlci2} created".format(name=self._name,
id=self._id,
port1=port1,
dlci1=dlci1,
port2=port2,
dlci2=dlci2))
self._mapping[(port1, dlci1)] = (port2, dlci2) self._mapping[(port1, dlci1)] = (port2, dlci2)
def unmap_vc(self, port1, dlci1, port2, dlci2): def unmap_vc(self, port1, dlci1, port2, dlci2):
@ -185,4 +261,11 @@ class FrameRelaySwitch(object):
input_dlci=dlci1, input_dlci=dlci1,
output_nio=nio2, output_nio=nio2,
output_dlci=dlci2)) output_dlci=dlci2))
log.info("Frame Relay switch {name} [id={id}]: VC from port {port1} DLCI {dlci1} to port {port2} DLCI {dlci2} deleted".format(name=self._name,
id=self._id,
port1=port1,
dlci1=dlci1,
port2=port2,
dlci2=dlci2))
del self._mapping[(port1, dlci1)] del self._mapping[(port1, dlci1)]

@ -23,6 +23,9 @@ from __future__ import unicode_literals
from .bridge import Bridge from .bridge import Bridge
from ..dynamips_error import DynamipsError from ..dynamips_error import DynamipsError
import logging
log = logging.getLogger(__name__)
class Hub(Bridge): class Hub(Bridge):
""" """
@ -32,10 +35,41 @@ class Hub(Bridge):
:param name: name for this hub :param name: name for this hub
""" """
_instance_count = 1
def __init__(self, hypervisor, name): def __init__(self, hypervisor, name):
Bridge.__init__(self, hypervisor, name) # create an unique ID
self._id = Hub._instance_count
Hub._instance_count += 1
# let's create a unique name if none has been chosen
if not name:
name = "Hub" + str(self._id)
self._mapping = {} self._mapping = {}
Bridge.__init__(self, hypervisor, name)
log.info("Ethernet hub {name} [id={id}] has been created".format(name=self._name,
id=self._id))
@classmethod
def reset(cls):
"""
Reset the instance count.
"""
cls._instance_count = 1
@property
def id(self):
"""
Returns the unique ID for this Ethernet switch.
:returns: id (integer)
"""
return self._id
@property @property
def mapping(self): def mapping(self):
@ -47,6 +81,15 @@ class Hub(Bridge):
return self._mapping return self._mapping
def delete(self):
"""
Deletes this hub.
"""
Bridge.delete(self)
log.info("Ethernet hub {name} [id={id}] has been deleted".format(name=self._name,
id=self._id))
def add_nio(self, nio, port): def add_nio(self, nio, port):
""" """
Adds a NIO as new port on this hub. Adds a NIO as new port on this hub.
@ -59,6 +102,11 @@ class Hub(Bridge):
raise DynamipsError("Port {} isn't free".format(port)) raise DynamipsError("Port {} isn't free".format(port))
Bridge.add_nio(self, nio) Bridge.add_nio(self, nio)
log.info("Ethernet hub {name} [id={id}]: NIO {nio} bound to port {port}".format(name=self._name,
id=self._id,
nio=nio,
port=port))
self._mapping[port] = nio self._mapping[port] = nio
def remove_nio(self, port): def remove_nio(self, port):
@ -66,6 +114,8 @@ class Hub(Bridge):
Removes the specified NIO as member of this hub. Removes the specified NIO as member of this hub.
:param port: allocated port :param port: allocated port
:returns: the NIO that was bound to the allocated port
""" """
if port not in self._mapping: if port not in self._mapping:
@ -73,4 +123,11 @@ class Hub(Bridge):
nio = self._mapping[port] nio = self._mapping[port]
Bridge.remove_nio(self, nio) Bridge.remove_nio(self, nio)
log.info("Ethernet switch {name} [id={id}]: NIO {nio} removed from port {port}".format(name=self._name,
id=self._id,
nio=nio,
port=port))
del self._mapping[port] del self._mapping[port]
return nio

@ -24,29 +24,36 @@ from __future__ import unicode_literals
from ..dynamips_error import DynamipsError from ..dynamips_error import DynamipsError
import os import os
import logging
log = logging.getLogger(__name__)
class Router(object): class Router(object):
""" """
Dynamips router Dynamips router implementation.
:param hypervisor: Dynamips hypervisor object :param hypervisor: Dynamips hypervisor object
:param name: name for this router :param name: name for this router
:param platform: c7200, c3745, c3725, c3600, c2691, c2600 or c1700 :param platform: c7200, c3745, c3725, c3600, c2691, c2600 or c1700
:param console_flag: create console ports if True. :param ghost_flag: used when creating a ghost IOS.
""" """
_instance_count = 0 _instance_count = 1
_status = {0: "inactive", _status = {0: "inactive",
1: "shutting down", 1: "shutting down",
2: "running", 2: "running",
3: "suspended"} 3: "suspended"}
def __init__(self, hypervisor, name, platform="c7200", console_flag=True): def __init__(self, hypervisor, name=None, platform="c7200", ghost_flag=False):
# create an unique ID # create an unique ID
self._id = Router._instance_count self._id = Router._instance_count
Router._instance_count += 1 Router._instance_count += 1
# let's create a unique name if none has been chosen
if not name:
name = "R" + str(self._id)
self._hypervisor = hypervisor self._hypervisor = hypervisor
self._name = '"' + name + '"' # put name into quotes to protect spaces self._name = '"' + name + '"' # put name into quotes to protect spaces
self._platform = platform self._platform = platform
@ -76,12 +83,66 @@ class Router(object):
id=self._id, id=self._id,
platform=self._platform)) platform=self._platform))
if console_flag: if not ghost_flag:
log.info("router {platform} {name} [id={id}] has been created".format(name=self._name,
platform=platform,
id=self._id))
self.console = self._hypervisor.baseconsole + self._id self.console = self._hypervisor.baseconsole + self._id
self.aux = self._hypervisor.baseaux + self._id self.aux = self._hypervisor.baseaux + self._id
else:
log.info("creating a new ghost IOS file")
Router._instance_count -= 1
self._hypervisor.devices.append(self) self._hypervisor.devices.append(self)
@classmethod
def reset(cls):
"""
Reset the instance count.
"""
cls._instance_count = 1
def defaults(self):
"""
Returns all the default base attribute values for routers.
:returns: default values (dictionary)
"""
router_defaults = {"platform": self._platform,
"image": self._image,
"ram": self._ram,
"nvram": self._nvram,
"mmap": self._mmap,
"sparsemem": self._sparsemem,
"clock_divisor": self._clock_divisor,
"idlepc": self._idlepc,
"idlemax": self._idlemax,
"idlesleep": self._idlesleep,
"exec_area": self._exec_area,
"jit_sharing_group": self._jit_sharing_group,
"disk0": self._disk0,
"disk1": self._disk1,
"confreg": self._confreg,
"console": self._console,
"aux": self._aux,
"mac_addr": self._mac_addr,
"system_id": self._system_id}
slot_id = 0
for slot in self._slots:
if slot:
slot = str(slot)
router_defaults["slot" + str(slot_id)] = slot
slot_id += 1
if self._slots[0] and self._slots[0].wics:
for wic_slot_id in range(0, len(self._slots[0].wics)):
router_defaults["wic" + str(wic_slot_id)] = None
return router_defaults
@property @property
def id(self): def id(self):
""" """
@ -151,6 +212,11 @@ class Router(object):
new_name = '"' + new_name + '"' # put the new name into quotes to protect spaces new_name = '"' + new_name + '"' # put the new name into quotes to protect spaces
self._hypervisor.send("vm rename {name} {new_name}".format(name=self._name, self._hypervisor.send("vm rename {name} {new_name}".format(name=self._name,
new_name=new_name)) new_name=new_name))
log.info("router {name} [id={id}]: renamed to {new_name}".format(name=self._name,
id=self._id,
new_name=new_name))
self._name = new_name self._name = new_name
def delete(self): def delete(self):
@ -161,13 +227,19 @@ class Router(object):
self._hypervisor.send("vm delete {}".format(self._name)) self._hypervisor.send("vm delete {}".format(self._name))
self._hypervisor.devices.remove(self) self._hypervisor.devices.remove(self)
log.info("router {name} [id={id}] has been deleted".format(name=self._name, id=self._id))
def start(self): def start(self):
""" """
Starts this router. Starts this router.
At least the IOS image must be set. At least the IOS image must be set before starting it.
""" """
self._hypervisor.send("vm start {}".format(self._name)) if self.get_status() == "suspended":
self.resume()
else:
self._hypervisor.send("vm start {}".format(self._name))
log.info("router {name} [id={id}] has been started".format(name=self._name, id=self._id))
def stop(self): def stop(self):
""" """
@ -176,13 +248,16 @@ class Router(object):
""" """
self._hypervisor.send("vm stop {}".format(self._name)) self._hypervisor.send("vm stop {}".format(self._name))
log.info("router {name} [id={id}] has been stopped".format(name=self._name, id=self._id))
def suspend(self): def suspend(self):
""" """
Suspends this router Suspends this router
""" """
self._hypervisor.send("vm suspend {}".format(self._name)) if self.get_status() == "running":
self._hypervisor.send("vm suspend {}".format(self._name))
log.info("router {name} [id={id}] has been suspended".format(name=self._name, id=self._id))
def resume(self): def resume(self):
""" """
@ -190,12 +265,13 @@ class Router(object):
""" """
self._hypervisor.send("vm resume {}".format(self._name)) self._hypervisor.send("vm resume {}".format(self._name))
log.info("router {name} [id={id}] has been resumed".format(name=self._name, id=self._id))
def get_status(self): def get_status(self):
""" """
Returns the status of this router Returns the status of this router
:returns: 0=inactive, 1=shutting down, 2=running, 3=suspended :returns: inactive, shutting down, running or suspended.
""" """
status_id = int(self._hypervisor.send("vm get_status {}".format(self._name))[0]) status_id = int(self._hypervisor.send("vm get_status {}".format(self._name))[0])
@ -225,7 +301,7 @@ class Router(object):
@jit_sharing_group.setter @jit_sharing_group.setter
def jit_sharing_group(self, group_id): def jit_sharing_group(self, group_id):
""" """
Set the translation sharing group (unstable). Sets the translation sharing group (unstable).
:param group_id: translation sharing group ID :param group_id: translation sharing group ID
""" """
@ -236,12 +312,16 @@ class Router(object):
self._hypervisor.send("vm set_tsg {name} {group_id}".format(name=self._name, self._hypervisor.send("vm set_tsg {name} {group_id}".format(name=self._name,
group_id=group_id)) group_id=group_id))
log.info("router {name} [id={id}]: set in JIT sharing group {group_id}".format(name=self._name,
id=self._id,
group_id=group_id))
self._jit_sharing_group = group_id self._jit_sharing_group = group_id
self._hypervisor.add_jitsharing_group(os.path.basename(self._image), group_id) self._hypervisor.add_jitsharing_group(os.path.basename(self._image), group_id)
def set_debug_level(self, level): def set_debug_level(self, level):
""" """
Set the debug level for this router (default is 0). Sets the debug level for this router (default is 0).
:param level: level number :param level: level number
""" """
@ -262,7 +342,7 @@ class Router(object):
@image.setter @image.setter
def image(self, image): def image(self, image):
""" """
Set the IOS image for this router. Sets the IOS image for this router.
There is no default. There is no default.
:param image: path to IOS image file :param image: path to IOS image file
@ -271,11 +351,16 @@ class Router(object):
# encase image in quotes to protect spaces in the path # encase image in quotes to protect spaces in the path
self._hypervisor.send("vm set_ios {name} {image}".format(name=self._name, self._hypervisor.send("vm set_ios {name} {image}".format(name=self._name,
image='"' + image + '"')) image='"' + image + '"'))
log.info("router {name} [id={id}]: has a new IOS image set: {image}".format(name=self._name,
id=self._id,
image='"' + image + '"'))
self._image = image self._image = image
def set_config(self, startup_config, private_config=''): def set_config(self, startup_config, private_config=''):
""" """
Set the config files that are pushed to startup-config and Sets the config files that are pushed to startup-config and
private-config in NVRAM when the instance is started. private-config in NVRAM when the instance is started.
:param startup_config: path to statup-config file :param startup_config: path to statup-config file
@ -287,6 +372,15 @@ class Router(object):
startup='"' + startup_config + '"', startup='"' + startup_config + '"',
private='"' + private_config + '"')) private='"' + private_config + '"'))
log.info("router {name} [id={id}]: has a startup-config set: {startup}".format(name=self._name,
id=self._id,
startup='"' + startup_config + '"'))
if private_config:
log.info("router {name} [id={id}]: has a private-config set: {private}".format(name=self._name,
id=self._id,
private='"' + private_config + '"'))
def extract_config(self): def extract_config(self):
""" """
Gets the contents of the config files Gets the contents of the config files
@ -318,6 +412,13 @@ class Router(object):
startup=startup_config, startup=startup_config,
private=private_config)) private=private_config))
log.info("router {name} [id={id}]: new startup-config pushed".format(name=self._name,
id=self._id))
if private_config != '(keep)':
log.info("router {name} [id={id}]: new private-config pushed".format(name=self._name,
id=self._id))
@property @property
def ram(self): def ram(self):
""" """
@ -331,13 +432,22 @@ class Router(object):
@ram.setter @ram.setter
def ram(self, ram): def ram(self, ram):
""" """
Set amount of RAM allocated to this router Sets amount of RAM allocated to this router
:param ram: amount of RAM in Mbytes (integer) :param ram: amount of RAM in Mbytes (integer)
""" """
if self._ram == ram:
return
self._hypervisor.send("vm set_ram {name} {ram}".format(name=self._name, self._hypervisor.send("vm set_ram {name} {ram}".format(name=self._name,
ram=self._ram)) ram=ram))
log.info("router {name} [id={id}]: RAM updated from {old_ram}MB to {new_ram}MB".format(name=self._name,
id=self._id,
old_ram=self._ram,
new_ram=ram))
self._hypervisor.decrease_memory_load(self._ram) self._hypervisor.decrease_memory_load(self._ram)
self._ram = ram self._ram = ram
self._hypervisor.increase_memory_load(self._ram) self._hypervisor.increase_memory_load(self._ram)
@ -355,13 +465,21 @@ class Router(object):
@nvram.setter @nvram.setter
def nvram(self, nvram): def nvram(self, nvram):
""" """
Set amount of NVRAM allocated to this router Sets amount of NVRAM allocated to this router
:param nvram: amount of NVRAM in Kbytes (integer) :param nvram: amount of NVRAM in Kbytes (integer)
""" """
if self._nvram == nvram:
return
self._hypervisor.send("vm set_nvram {name} {nvram}".format(name=self._name, self._hypervisor.send("vm set_nvram {name} {nvram}".format(name=self._name,
nvram=self._nvram)) nvram=nvram))
log.info("router {name} [id={id}]: NVRAM updated from {old_nvram}KB to {new_nvram}KB".format(name=self._name,
id=self._id,
old_nvram=self._nvram,
new_nvram=nvram))
self._nvram = nvram self._nvram = nvram
@property @property
@ -389,6 +507,13 @@ class Router(object):
flag = 0 flag = 0
self._hypervisor.send("vm set_ram_mmap {name} {mmap}".format(name=self._name, self._hypervisor.send("vm set_ram_mmap {name} {mmap}".format(name=self._name,
mmap=flag)) mmap=flag))
if mmap:
log.info("router {name} [id={id}]: mmap enabled".format(name=self._name,
id=self._id))
else:
log.info("router {name} [id={id}]: mmap disabled".format(name=self._name,
id=self._id))
self._mmap = mmap self._mmap = mmap
@property @property
@ -415,6 +540,13 @@ class Router(object):
flag = 0 flag = 0
self._hypervisor.send("vm set_sparse_mem {name} {sparsemem}".format(name=self._name, self._hypervisor.send("vm set_sparse_mem {name} {sparsemem}".format(name=self._name,
sparsemem=flag)) sparsemem=flag))
if sparsemem:
log.info("router {name} [id={id}]: sparse memory enabled".format(name=self._name,
id=self._id))
else:
log.info("router {name} [id={id}]: sparse memory disabled".format(name=self._name,
id=self._id))
self._sparsemem = sparsemem self._sparsemem = sparsemem
@property @property
@ -430,7 +562,7 @@ class Router(object):
@clock_divisor.setter @clock_divisor.setter
def clock_divisor(self, clock_divisor): def clock_divisor(self, clock_divisor):
""" """
Set the clock divisor value. The higher is the value, the faster is the clock in the Sets the clock divisor value. The higher is the value, the faster is the clock in the
virtual machine. The default is 4, but it is often required to adjust it. virtual machine. The default is 4, but it is often required to adjust it.
:param clock_divisor: clock divisor value (integer) :param clock_divisor: clock divisor value (integer)
@ -438,6 +570,11 @@ class Router(object):
self._hypervisor.send("vm set_clock_divisor {name} {clock}".format(name=self._name, self._hypervisor.send("vm set_clock_divisor {name} {clock}".format(name=self._name,
clock=clock_divisor)) clock=clock_divisor))
log.info("router {name} [id={id}]: clock divisor updated from {old_clock} to {new_clock}".format(name=self._name,
id=self._id,
old_clock=self._clock_divisor,
new_clock=clock_divisor))
self._clock_divisor = clock_divisor self._clock_divisor = clock_divisor
@property @property
@ -453,7 +590,7 @@ class Router(object):
@idlepc.setter @idlepc.setter
def idlepc(self, idlepc): def idlepc(self, idlepc):
""" """
Set the idle Pointer Counter (PC) Sets the idle Pointer Counter (PC)
:param idlepc: idlepc value (string) :param idlepc: idlepc value (string)
""" """
@ -465,6 +602,11 @@ class Router(object):
else: else:
self._hypervisor.send("vm set_idle_pc_online {name} 0 {idlepc}".format(name=self._name, self._hypervisor.send("vm set_idle_pc_online {name} 0 {idlepc}".format(name=self._name,
idlepc=idlepc)) idlepc=idlepc))
log.info("router {name} [id={id}]: idle-PC set to {idlepc}".format(name=self._name,
id=self._id,
idlepc=idlepc))
self._idlepc = idlepc self._idlepc = idlepc
def get_idle_pc_prop(self): def get_idle_pc_prop(self):
@ -500,7 +642,7 @@ class Router(object):
@idlemax.setter @idlemax.setter
def idlemax(self, idlemax): def idlemax(self, idlemax):
""" """
Set CPU idle max value Sets CPU idle max value
:param idlemax: idle max value (integer) :param idlemax: idle max value (integer)
""" """
@ -508,6 +650,12 @@ class Router(object):
if self.is_running(): # router is running if self.is_running(): # router is running
self._hypervisor.send("vm set_idle_max {name} 0 {idlemax}".format(name=self._name, self._hypervisor.send("vm set_idle_max {name} 0 {idlemax}".format(name=self._name,
idlemax=idlemax)) idlemax=idlemax))
log.info("router {name} [id={id}]: idlemax updated from {old_idlemax} to {new_idlemax}".format(name=self._name,
id=self._id,
old_idlemax=self._idlemax,
new_idlemax=idlemax))
self._idlemax = idlemax self._idlemax = idlemax
@property @property
@ -523,7 +671,7 @@ class Router(object):
@idlesleep.setter @idlesleep.setter
def idlesleep(self, idlesleep): def idlesleep(self, idlesleep):
""" """
Set CPU idle sleep time value. Sets CPU idle sleep time value.
:param idlesleep: idle sleep value (integer) :param idlesleep: idle sleep value (integer)
""" """
@ -531,6 +679,12 @@ class Router(object):
if self.is_running(): # router is running if self.is_running(): # router is running
self._hypervisor.send("vm set_idle_sleep_time {name} 0 {idlesleep}".format(name=self._name, self._hypervisor.send("vm set_idle_sleep_time {name} 0 {idlesleep}".format(name=self._name,
idlesleep=idlesleep)) idlesleep=idlesleep))
log.info("router {name} [id={id}]: idlesleep updated from {old_idlesleep} to {new_idlesleep}".format(name=self._name,
id=self._id,
old_idlesleep=self._idlesleep,
new_idlesleep=idlesleep))
self._idlesleep = idlesleep self._idlesleep = idlesleep
def show_timer_drift(self): def show_timer_drift(self):
@ -555,16 +709,21 @@ class Router(object):
@ghost_file.setter @ghost_file.setter
def ghost_file(self, ghost_file): def ghost_file(self, ghost_file):
""" """
Set ghost RAM file Sets ghost RAM file
:ghost_file: path to ghost file :ghost_file: path to ghost file
""" """
self._hypervisor.send("vm set_ghost_file {name} {ghost_file}".format(name=self._name, self._hypervisor.send("vm set_ghost_file {name} {ghost_file}".format(name=self._name,
ghost_file=ghost_file)) ghost_file=ghost_file))
log.info("router {name} [id={id}]: ghost file set to {ghost_file}".format(name=self._name,
id=self._id,
ghost_file=ghost_file))
self._ghost_file = ghost_file self._ghost_file = ghost_file
# If this is a ghost instance, track this as a hosted ghost instance by this hypervisor # if this is a ghost instance, track this as a hosted ghost instance by this hypervisor
if self.ghost_status == 1: if self.ghost_status == 1:
self._hypervisor.add_ghost(ghost_file, self) self._hypervisor.add_ghost(ghost_file, self)
@ -575,8 +734,8 @@ class Router(object):
:returns: formatted ghost_file name (string) :returns: formatted ghost_file name (string)
""" """
# Replace specials characters in 'drive:\filename' in Linux and Dynamips in MS Windows or viceversa. # replace specials characters in 'drive:\filename' in Linux and Dynamips in MS Windows or viceversa.
ghost_file = os.path.basename(self._image) + '-' + self._hypervisor.host + '.ghost' ghost_file = "{}-{}.ghost".format(os.path.basename(self._image), self._ram)
ghost_file = ghost_file.replace('\\', '-').replace('/', '-').replace(':', '-') ghost_file = ghost_file.replace('\\', '-').replace('/', '-').replace(':', '-')
return ghost_file return ghost_file
@ -592,7 +751,7 @@ class Router(object):
@ghost_status.setter @ghost_status.setter
def ghost_status(self, ghost_status): def ghost_status(self, ghost_status):
""" """
Set ghost RAM status Sets ghost RAM status
:param ghost_status: state flag indicating status :param ghost_status: state flag indicating status
0 => Do not use IOS ghosting 0 => Do not use IOS ghosting
@ -602,6 +761,10 @@ class Router(object):
self._hypervisor.send("vm set_ghost_status {name} {ghost_status}".format(name=self._name, self._hypervisor.send("vm set_ghost_status {name} {ghost_status}".format(name=self._name,
ghost_status=ghost_status)) ghost_status=ghost_status))
log.info("router {name} [id={id}]: ghost status set to {ghost_status}".format(name=self._name,
id=self._id,
ghost_status=ghost_status))
self._ghost_status = ghost_status self._ghost_status = ghost_status
@property @property
@ -617,7 +780,7 @@ class Router(object):
@exec_area.setter @exec_area.setter
def exec_area(self, exec_area): def exec_area(self, exec_area):
""" """
Set the exec area value. Sets the exec area value.
The exec area is a pool of host memory used to store pages The exec area is a pool of host memory used to store pages
translated by the JIT (they contain the native code translated by the JIT (they contain the native code
corresponding to MIPS code pages). corresponding to MIPS code pages).
@ -627,6 +790,11 @@ class Router(object):
self._hypervisor.send("vm set_exec_area {name} {exec_area}".format(name=self._name, self._hypervisor.send("vm set_exec_area {name} {exec_area}".format(name=self._name,
exec_area=exec_area)) exec_area=exec_area))
log.info("router {name} [id={id}]: exec area updated from {old_exec}MB to {new_exec}MB".format(name=self._name,
id=self._id,
old_exec=self._exec_area,
new_exec=exec_area))
self._exec_area = exec_area self._exec_area = exec_area
@property @property
@ -642,13 +810,18 @@ class Router(object):
@disk0.setter @disk0.setter
def disk0(self, disk0): def disk0(self, disk0):
""" """
Set the size (MB) for PCMCIA disk0. Sets the size (MB) for PCMCIA disk0.
:param disk0: disk0 size (integer) :param disk0: disk0 size (integer)
""" """
self._hypervisor.send("vm set_disk0 {name} {disk0}".format(name=self._name, self._hypervisor.send("vm set_disk0 {name} {disk0}".format(name=self._name,
disk0=disk0)) disk0=disk0))
log.info("router {name} [id={id}]: disk0 updated from {old_disk0}MB to {new_disk0}MB".format(name=self._name,
id=self._id,
old_disk0=self._disk0,
new_disk0=disk0))
self._disk0 = disk0 self._disk0 = disk0
@property @property
@ -664,13 +837,18 @@ class Router(object):
@disk1.setter @disk1.setter
def disk1(self, disk1): def disk1(self, disk1):
""" """
Set the size (MB) for PCMCIA disk1. Sets the size (MB) for PCMCIA disk1.
:param disk1: disk1 size (integer) :param disk1: disk1 size (integer)
""" """
self._hypervisor.send("vm set_disk1 {name} {disk1}".format(name=self._name, self._hypervisor.send("vm set_disk1 {name} {disk1}".format(name=self._name,
disk1=disk1)) disk1=disk1))
log.info("router {name} [id={id}]: disk1 updated from {old_disk1}MB to {new_disk1}MB".format(name=self._name,
id=self._id,
old_disk1=self._disk1,
new_disk1=disk1))
self._disk1 = disk1 self._disk1 = disk1
@property @property
@ -687,13 +865,18 @@ class Router(object):
@confreg.setter @confreg.setter
def confreg(self, confreg): def confreg(self, confreg):
""" """
Set the configuration register. Sets the configuration register.
:param confreg: configuration register value (string) :param confreg: configuration register value (string)
""" """
self._hypervisor.send("vm set_conf_reg {name} {confreg}".format(name=self._name, self._hypervisor.send("vm set_conf_reg {name} {confreg}".format(name=self._name,
confreg=confreg)) confreg=confreg))
log.info("router {name} [id={id}]: confreg updated from {old_confreg} to {new_confreg}".format(name=self._name,
id=self._id,
old_confreg=self._confreg,
new_confreg=confreg))
self._confreg = confreg self._confreg = confreg
@property @property
@ -709,7 +892,7 @@ class Router(object):
@console.setter @console.setter
def console(self, console): def console(self, console):
""" """
Set the TCP console port. Sets the TCP console port.
:param console: console port (integer) :param console: console port (integer)
""" """
@ -719,6 +902,11 @@ class Router(object):
self._hypervisor.send("vm set_con_tcp_port {name} {console}".format(name=self._name, self._hypervisor.send("vm set_con_tcp_port {name} {console}".format(name=self._name,
console=console)) console=console))
log.info("router {name} [id={id}]: console port updated from {old_console} to {new_console}".format(name=self._name,
id=self._id,
old_console=self._console,
new_console=console))
self._console = console self._console = console
@property @property
@ -734,7 +922,7 @@ class Router(object):
@aux.setter @aux.setter
def aux(self, aux): def aux(self, aux):
""" """
Set the TCP auxiliary port. Sets the TCP auxiliary port.
:param aux: console auxiliary port (integer) :param aux: console auxiliary port (integer)
""" """
@ -744,6 +932,11 @@ class Router(object):
self._hypervisor.send("vm set_aux_tcp_port {name} {aux}".format(name=self._name, self._hypervisor.send("vm set_aux_tcp_port {name} {aux}".format(name=self._name,
aux=aux)) aux=aux))
log.info("router {name} [id={id}]: aux port updated from {old_aux} to {new_aux}".format(name=self._name,
id=self._id,
old_aux=self._aux,
new_aux=aux))
self._aux = aux self._aux = aux
def get_cpu_info(self, cpu_id=0): def get_cpu_info(self, cpu_id=0):
@ -801,7 +994,7 @@ class Router(object):
@mac_addr.setter @mac_addr.setter
def mac_addr(self, mac_addr): def mac_addr(self, mac_addr):
""" """
Set the MAC address. Sets the MAC address.
:param mac_addr: a MAC address (hexadecimal format: hh:hh:hh:hh:hh:hh) :param mac_addr: a MAC address (hexadecimal format: hh:hh:hh:hh:hh:hh)
""" """
@ -809,6 +1002,11 @@ class Router(object):
self._hypervisor.send("{platform} set_mac_addr {name} {mac_addr}".format(platform=self._platform, self._hypervisor.send("{platform} set_mac_addr {name} {mac_addr}".format(platform=self._platform,
name=self._name, name=self._name,
mac_addr=mac_addr)) mac_addr=mac_addr))
log.info("router {name} [id={id}]: MAC address updated from {old_mac} to {new_mac}".format(name=self._name,
id=self._id,
old_mac=self._mac_addr,
new_mac=mac_addr))
self._mac_addr = mac_addr self._mac_addr = mac_addr
@property @property
@ -824,7 +1022,7 @@ class Router(object):
@system_id.setter @system_id.setter
def system_id(self, system_id): def system_id(self, system_id):
""" """
Set the system ID. Sets the system ID.
:param system_id: a system ID (also called board processor ID) :param system_id: a system ID (also called board processor ID)
""" """
@ -832,6 +1030,11 @@ class Router(object):
self._hypervisor.send("{platform} set_system_id {name} {system_id}".format(platform=self._platform, self._hypervisor.send("{platform} set_system_id {name} {system_id}".format(platform=self._platform,
name=self._name, name=self._name,
system_id=system_id)) system_id=system_id))
log.info("router {name} [id={id}]: system ID updated from {old_id} to {new_id}".format(name=self._name,
id=self._id,
old_id=self._system_id,
new_id=system_id))
self._system_id = system_id self._system_id = system_id
def get_hardware_info(self): def get_hardware_info(self):
@ -841,7 +1044,7 @@ class Router(object):
:returns: ? (could not test) :returns: ? (could not test)
""" """
# FIXME: nothing returned by Dynamips. # FIXME: nothing returned by Dynamips.
return (self._hypervisor.send("{platform} show_hardware {name}".format(platform=self._platform, return (self._hypervisor.send("{platform} show_hardware {name}".format(platform=self._platform,
name=self._name))) name=self._name)))
@ -856,7 +1059,7 @@ class Router(object):
def slot_add_binding(self, slot_id, adapter): def slot_add_binding(self, slot_id, adapter):
""" """
Adds a slot binding. Adds a slot binding (a module into a slot).
:param slot_id: slot ID :param slot_id: slot ID
:param adapter: device to add in the corresponding slot (object) :param adapter: device to add in the corresponding slot (object)
@ -877,6 +1080,12 @@ class Router(object):
self._hypervisor.send("vm slot_add_binding {name} {slot_id} 0 {adapter}".format(name=self._name, self._hypervisor.send("vm slot_add_binding {name} {slot_id} 0 {adapter}".format(name=self._name,
slot_id=slot_id, slot_id=slot_id,
adapter=adapter)) adapter=adapter))
log.info("router {name} [id={id}]: adapter {adapter} inserted into slot {slot_id}".format(name=self._name,
id=self._id,
adapter=adapter,
slot_id=slot_id))
self._slots[slot_id] = adapter self._slots[slot_id] = adapter
# Generate an OIR event if the router is running and # Generate an OIR event if the router is running and
@ -888,33 +1097,48 @@ class Router(object):
self._hypervisor.send("vm slot_oir_start {name} {slot_id} 0".format(name=self._name, self._hypervisor.send("vm slot_oir_start {name} {slot_id} 0".format(name=self._name,
slot_id=slot_id)) slot_id=slot_id))
log.info("router {name} [id={id}]: OIR start event sent to slot {slot_id}".format(name=self._name,
id=self._id,
slot_id=slot_id))
def slot_remove_binding(self, slot_id): def slot_remove_binding(self, slot_id):
""" """
Removes a slot binding. Removes a slot binding (a module from a slot).
:param slot_id: slot ID :param slot_id: slot ID
""" """
try: try:
slot = self._slots[slot_id] adapter = self._slots[slot_id]
except IndexError: except IndexError:
raise DynamipsError("Slot {slot_id} doesn't exist on router {name}".format(name=self._name, raise DynamipsError("Slot {slot_id} doesn't exist on router {name}".format(name=self._name,
slot_id=slot_id)) slot_id=slot_id))
if slot == None: if adapter == None:
return return
#FIXME: check if adapter can be removed!
# Generate an OIR event if the router is running and # Generate an OIR event if the router is running and
# only for c7200, c3600 and c3745 (NM-4T only) # only for c7200, c3600 and c3745 (NM-4T only)
if self.is_running() and self._platform == 'c7200' \ if self.is_running() and self._platform == 'c7200' \
or (self._platform == 'c3600' and self.chassis == '3660') \ or (self._platform == 'c3600' and self.chassis == '3660') \
or (self._platform == 'c3745' and slot == 'NM-4T'): or (self._platform == 'c3745' and adapter == 'NM-4T'):
self._hypervisor.send("vm slot_oir_stop {name} {slot_id} 0".format(name=self._name, self._hypervisor.send("vm slot_oir_stop {name} {slot_id} 0".format(name=self._name,
slot_id=slot_id)) slot_id=slot_id))
log.info("router {name} [id={id}]: OIR stop event sent to slot {slot_id}".format(name=self._name,
id=self._id,
slot_id=slot_id))
self._hypervisor.send("vm slot_remove_binding {name} {slot_id} 0".format(name=self._name, self._hypervisor.send("vm slot_remove_binding {name} {slot_id} 0".format(name=self._name,
slot_id=slot_id)) slot_id=slot_id))
log.info("router {name} [id={id}]: adapter {adapter} removed from slot {slot_id}".format(name=self._name,
id=self._id,
adapter=adapter,
slot_id=slot_id))
self._slots[slot_id] = None self._slots[slot_id] = None
def install_wic(self, wic_slot_id, wic): def install_wic(self, wic_slot_id, wic):
@ -947,6 +1171,12 @@ class Router(object):
slot_id=slot_id, slot_id=slot_id,
wic_slot_id=internal_wic_slot_id, wic_slot_id=internal_wic_slot_id,
wic=wic)) wic=wic))
log.info("router {name} [id={id}]: {wic} inserted into WIC slot {wic_slot_id}".format(name=self._name,
id=self._id,
wic=wic,
wic_slot_id=wic_slot_id))
adapter.install_wic(wic_slot_id, wic) adapter.install_wic(wic_slot_id, wic)
def uninstall_wic(self, wic_slot_id): def uninstall_wic(self, wic_slot_id):
@ -976,6 +1206,11 @@ class Router(object):
self._hypervisor.send("vm slot_remove_binding {name} {slot_id} {wic_slot_id}".format(name=self._name, self._hypervisor.send("vm slot_remove_binding {name} {slot_id} {wic_slot_id}".format(name=self._name,
slot_id=slot_id, slot_id=slot_id,
wic_slot_id=internal_wic_slot_id)) wic_slot_id=internal_wic_slot_id))
log.info("router {name} [id={id}]: {wic} removed from WIC slot {wic_slot_id}".format(name=self._name,
id=self._id,
wic=adapter.wics[wic_slot_id],
wic_slot_id=wic_slot_id))
adapter.uninstall_wic(wic_slot_id) adapter.uninstall_wic(wic_slot_id)
def get_slot_nio_bindings(self, slot_id): def get_slot_nio_bindings(self, slot_id):
@ -1012,6 +1247,13 @@ class Router(object):
slot_id=slot_id, slot_id=slot_id,
port_id=port_id, port_id=port_id,
nio=nio)) nio=nio))
log.info("router {name} [id={id}]: NIO {nio_name} bound to port {slot_id}/{port_id}".format(name=self._name,
id=self._id,
nio_name=nio.name,
slot_id=slot_id,
port_id=port_id))
self.slot_enable_nio(slot_id, port_id) self.slot_enable_nio(slot_id, port_id)
adapter.add_nio(port_id, nio) adapter.add_nio(port_id, nio)
@ -1021,6 +1263,8 @@ class Router(object):
:param slot_id: slot ID :param slot_id: slot ID
:param port_id: port ID :param port_id: port ID
:returns: removed NIO object
""" """
try: try:
@ -1033,12 +1277,21 @@ class Router(object):
port_id=port_id)) port_id=port_id))
self.slot_disable_nio(slot_id, port_id) self.slot_disable_nio(slot_id, port_id)
self._hypervisor.send("vm slot_remove_binding {name} {slot_id} {port_id}".format(name=self._name, self._hypervisor.send("vm slot_remove_nio_binding {name} {slot_id} {port_id}".format(name=self._name,
slot_id=slot_id, slot_id=slot_id,
port_id=port_id)) port_id=port_id))
nio = adapter.get_nio(port_id)
adapter.remove_nio(port_id) adapter.remove_nio(port_id)
log.info("router {name} [id={id}]: NIO {nio_name} removed from port {slot_id}/{port_id}".format(name=self._name,
id=self._id,
nio_name=nio.name,
slot_id=slot_id,
port_id=port_id))
return nio
def slot_enable_nio(self, slot_id, port_id): def slot_enable_nio(self, slot_id, port_id):
""" """
Enables a slot NIO binding. Enables a slot NIO binding.
@ -1052,6 +1305,11 @@ class Router(object):
slot_id=slot_id, slot_id=slot_id,
port_id=port_id)) port_id=port_id))
log.info("router {name} [id={id}]: NIO enabled on port {slot_id}/{port_id}".format(name=self._name,
id=self._id,
slot_id=slot_id,
port_id=port_id))
def slot_disable_nio(self, slot_id, port_id): def slot_disable_nio(self, slot_id, port_id):
""" """
Disables a slot NIO binding. Disables a slot NIO binding.
@ -1065,6 +1323,11 @@ class Router(object):
slot_id=slot_id, slot_id=slot_id,
port_id=port_id)) port_id=port_id))
log.info("router {name} [id={id}]: NIO disabled on port {slot_id}/{port_id}".format(name=self._name,
id=self._id,
slot_id=slot_id,
port_id=port_id))
def _create_slots(self, numslots): def _create_slots(self, numslots):
""" """
Creates the appropriate number of slots for this router. Creates the appropriate number of slots for this router.

@ -84,7 +84,7 @@ class Server(object):
tornado_app = tornado.web.Application(self.handlers, debug=True) # FIXME: debug mode! tornado_app = tornado.web.Application(self.handlers, debug=True) # FIXME: debug mode!
try: try:
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, address=self._host)
except socket.error as e: except socket.error as e:
if e.errno == errno.EADDRINUSE: # 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))
@ -92,7 +92,7 @@ class Server(object):
ioloop = tornado.ioloop.IOLoop.instance() ioloop = tornado.ioloop.IOLoop.instance()
stream = zmqstream.ZMQStream(router, ioloop) stream = zmqstream.ZMQStream(router, ioloop)
stream.on_recv(JSONRPCWebSocket.dispatch_message) stream.on_recv_stream(JSONRPCWebSocket.dispatch_message)
tornado.autoreload.add_reload_hook(functools.partial(self._cleanup, stop=False)) tornado.autoreload.add_reload_hook(functools.partial(self._cleanup, stop=False))
def signal_handler(signum=None, frame=None): def signal_handler(signum=None, frame=None):

@ -1,49 +0,0 @@
# -*- 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 networkx as nx
class Topology(object):
def __init__(self):
pass
#self._topology = nx.Graph()
def add_node(self, node):
self._topology.add_node(node)
def remove_node(self, node):
self._topology.remove_node(node)
def clear(self):
self._topology.clear()
def __str__(self):
return "GNS3 network topology"
@staticmethod
def instance():
if not hasattr(Topology, "_instance"):
Topology._instance = Topology()
return Topology._instance

@ -1,2 +1,3 @@
tornado tornado
pyzmq pyzmq
netifaces-py3

@ -36,7 +36,7 @@ class JSONRPC(AsyncTestCase):
def test_request_with_invalid_version(self): def test_request_with_invalid_version(self):
request = {"jsonrpc": "1.0", "method": "dynamips.echo", "id": 1} request = {"jsonrpc": 1.0, "method": "dynamips.echo", "id": 1}
AsyncWSRequest(self.URL, self.io_loop, self.stop, json_encode(request)) AsyncWSRequest(self.URL, self.io_loop, self.stop, json_encode(request))
response = self.wait() response = self.wait()
json_response = json_decode(response) json_response = json_decode(response)

Loading…
Cancel
Save