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.
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 tornado.websocket
from tornado.escape import json_decode
from ..jsonrpc import JSONRPCParseError, JSONRPCInvalidRequest, JSONRPCMethodNotFound
from ..jsonrpc import JSONRPCParseError, JSONRPCInvalidRequest, JSONRPCMethodNotFound, JSONRPCNotification
import logging
log = logging.getLogger(__name__)
@ -59,7 +59,7 @@ class JSONRPCWebSocket(tornado.websocket.WebSocketHandler):
return self._session_id
@classmethod
def dispatch_message(cls, message):
def dispatch_message(cls, stream, message):
"""
Sends a message to Websocket client
@ -71,7 +71,13 @@ class JSONRPCWebSocket(tornado.websocket.WebSocketHandler):
# ZMQ responses are encoded in JSON
# 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]
jsonrpc_response = json_message[1]
@ -94,8 +100,8 @@ class JSONRPCWebSocket(tornado.websocket.WebSocketHandler):
# Make sure the destination is not already registered
# by another module for instance
assert destination not in cls.destinations
log.info("registering {} as a destination for {}".format(destination,
module))
log.debug("registering {} as a destination for the {} module".format(destination,
module))
cls.destinations[destination] = module
def open(self):
@ -119,8 +125,8 @@ class JSONRPCWebSocket(tornado.websocket.WebSocketHandler):
request = json_decode(message)
jsonrpc_version = request["jsonrpc"]
method = request["method"]
# warning: notifications cannot be sent by a client because check for an "id" here
request_id = request["id"]
# This is a JSON-RPC notification if request_id is None
request_id = request.get("id")
except:
return self.write_message(JSONRPCParseError()())
@ -128,7 +134,11 @@ class JSONRPCWebSocket(tornado.websocket.WebSocketHandler):
return self.write_message(JSONRPCInvalidRequest()())
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]
# ZMQ requests are encoded in JSON
@ -136,7 +146,7 @@ class JSONRPCWebSocket(tornado.websocket.WebSocketHandler):
zmq_request = [self.session_id, request]
# Route to the correct module
self.zmq_router.send_string(module, zmq.SNDMORE)
# Send the encoded JSON request
# Send the JSON request
self.zmq_router.send_json(zmq_request)
def on_close(self):
@ -146,3 +156,14 @@ class JSONRPCWebSocket(tornado.websocket.WebSocketHandler):
log.info("Websocket client {} disconnected".format(self.session_id))
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)
"""
def __init__(self, method, params=None, request_id=str(uuid.uuid1())):
def __init__(self, method, params=None, request_id=None):
JSONRPCObject.__init__(self)
if request_id == None:
request_id = str(uuid.uuid4())
self.id = request_id
self.method = method
if params:

@ -24,7 +24,7 @@ import gns3server
# command line options
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("ipc", default=False, help="use IPC for module communication", type=bool)

@ -79,7 +79,7 @@ class ModuleManager(object):
if issubclass(module_class[1], IModule):
# make sure the module class has IModule as a parent
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])
self._modules.append(info)
except:

@ -92,6 +92,10 @@ class IModule(multiprocessing.Process):
stream.on_recv(callback)
return stream
# def add_periodic_callback(self, callback, time):
#
# self.test = zmq.eventloop.ioloop.PeriodicCallback(callback, time, self._ioloop).start()
def run(self):
"""
Starts the event loop
@ -158,9 +162,9 @@ class IModule(multiprocessing.Process):
# add session to the response
response = [self._current_session, jsonrpc_response]
log.info("ZeroMQ client ({}) sending JSON-RPC custom error {} for call id {}".format(self.name,
message,
self._current_call_id))
log.info("ZeroMQ client ({}) sending JSON-RPC custom error: {} for call id {}".format(self.name,
message,
self._current_call_id))
self._stream.send_json(response)
def _decode_request(self, request):
@ -188,7 +192,12 @@ class IModule(multiprocessing.Process):
return
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):
"""

@ -15,13 +15,18 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Dynamips server module.
"""
from gns3server.modules import IModule
import gns3server.jsonrpc as jsonrpc
from .hypervisor import Hypervisor
from .hypervisor_manager import HypervisorManager
from .dynamips_error import DynamipsError
# nodes
# Nodes
from .nodes.router import Router
from .nodes.c1700 import C1700
from .nodes.c2600 import C2600
@ -37,7 +42,7 @@ from .nodes.atm_bridge import ATMBridge
from .nodes.frame_relay_switch import FrameRelaySwitch
from .nodes.hub import Hub
# adapters
# Adapters
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
@ -71,35 +76,188 @@ from .nios.nio_fifo import NIO_FIFO
from .nios.nio_mcast import NIO_Mcast
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
log = logging.getLogger(__name__)
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={}):
IModule.__init__(self, name=name, args=args, kwargs=kwargs)
# start the hypervisor manager
#self._hypervisor_manager = HypervisorManager("/usr/bin/dynamips", "/tmp")
self._hypervisor_manager = None
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")
def echo(self, request):
"""
Echo end point for testing purposes.
:param request: JSON request
"""
if request == None:
self.send_param_error()
return
log.debug("received request {}".format(request))
self.send_response(request)
@IModule.route("dynamips.create_vm")
def create_vm(self, request):
print("Create VM!")
log.debug("received request {}".format(request))
self.send_response(request)
@IModule.route("dynamips.start_vm")
def start_vm(self, request):
print("Start VM!")
log.debug("received request {}".format(request))
self.send_response(request)
else:
log.debug("received request {}".format(request))
self.send_response(request)
def create_nio(self, node, request):
nio = None
if request["nio"] == "NIO_UDP":
lport = request["lport"]
rhost = request["rhost"]
rport = request["rport"]
nio = NIO_UDP(node.hypervisor, lport, rhost, rport)
elif request["nio"] == "NIO_GenericEthernet":
ethernet_device = request["ethernet_device"]
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
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
def ports(self):
"""

@ -33,11 +33,3 @@ class C1700_MB_1FE(Adapter):
def removable(self):
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):
return "C1700_MB_WIC1"
return "C1700-MB-WIC1"
def removable(self):
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):
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):
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):
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):
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):
return "C7200_IO_2FE"
return "C7200-IO-2FE"
def removable(self):
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):
return "C7200_IO_FE"
return "C7200-IO-FE"
def removable(self):
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):
return False
def interface_type(self):
return "GigabitEthernet"
def medium(self):
return "Ethernet"

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

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

@ -29,11 +29,3 @@ class NM_16ESW(Adapter):
def __str__(self):
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):
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):
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):
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):
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):
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):
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):
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):
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):
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):
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):
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):
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):
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"
def interface_type(self):
return "Ethernet"
def medium(self):
return "Ethernet"
@property
def interfaces(self):
"""

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

@ -29,14 +29,6 @@ class WIC_2T(object):
return "WIC-2T"
def interface_type(self):
return "Serial"
def medium(self):
return "Serial"
@property
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):
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 errno
import re
import logging
from .dynamips_error import DynamipsError
@ -54,6 +55,7 @@ class DynamipsHypervisor(object):
self._baseconsole = 2000
self._baseaux = 2100
self._baseudp = 10000
self._current_udp_port = self._baseudp
self._version = "N/A"
self._timeout = 30
self._socket = None
@ -145,7 +147,7 @@ class DynamipsHypervisor(object):
@working_dir.setter
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
"""
@ -198,7 +200,7 @@ class DynamipsHypervisor(object):
@devices.setter
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.
:param devices: a list of device objects
@ -219,7 +221,7 @@ class DynamipsHypervisor(object):
@baseconsole.setter
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)
"""
@ -239,7 +241,7 @@ class DynamipsHypervisor(object):
@baseaux.setter
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)
"""
@ -259,12 +261,16 @@ class DynamipsHypervisor(object):
@baseudp.setter
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)
"""
self._baseudp = baseudp
self._current_udp_port = self._baseudp
#FIXME
log.info("hypervisor a new base UDP {}".format(self._baseudp))
@property
def ghosts(self):
@ -294,7 +300,7 @@ class DynamipsHypervisor(object):
: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):
"""
@ -326,6 +332,42 @@ class DynamipsHypervisor(object):
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):
"""
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 time
import subprocess
import logging
@ -82,7 +83,7 @@ class Hypervisor(DynamipsHypervisor):
@path.setter
def path(self, path):
"""
Set the path to the Dynamips executable.
Sets the path to the Dynamips executable.
:param path: path to Dynamips
"""
@ -102,7 +103,7 @@ class Hypervisor(DynamipsHypervisor):
@port.setter
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)
"""
@ -122,7 +123,7 @@ class Hypervisor(DynamipsHypervisor):
@host.setter
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)
"""
@ -142,7 +143,7 @@ class Hypervisor(DynamipsHypervisor):
@workingdir.setter
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
"""
@ -163,7 +164,7 @@ class Hypervisor(DynamipsHypervisor):
@image_ref.setter
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).
:param ios_image_name: image reference name
@ -230,8 +231,13 @@ class Hypervisor(DynamipsHypervisor):
"""
if self.is_running():
DynamipsHypervisor.stop(self)
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.wait()
def read_stdout(self):
"""

@ -45,27 +45,29 @@ class HypervisorManager(object):
path,
workingdir,
host='127.0.0.1',
base_port=7200,
base_console=2000,
base_aux=3000,
base_udp=10000):
base_hypervisor_port=7200,
base_console_port=2000,
base_aux_port=3000,
base_udp_port=10000):
self._hypervisors = []
self._path = path
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._clean_workingdir = False
self._ghost_ios = True
self._mmap = True
self._jit_sharing = False
self._sparsemem = True
self._base_hypervisor_port = base_hypervisor_port
self._current_port = self._base_hypervisor_port
self._base_console_port = base_console_port
self._base_aux_port = base_aux_port
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._group_ios_per_hypervisor = True
self._allocate_hypervisor_per_ios_image = True
def __del__(self):
"""
@ -84,6 +86,263 @@ class HypervisorManager(object):
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
def memory_usage_limit_per_hypervisor(self):
"""
@ -97,15 +356,17 @@ class HypervisorManager(object):
@memory_usage_limit_per_hypervisor.setter
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)
"""
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
def group_ios_per_hypervisor(self):
def allocate_hypervisor_per_ios_image(self):
"""
Returns if router are grouped per hypervisor
based on their IOS image.
@ -113,18 +374,23 @@ class HypervisorManager(object):
:returns: True or False
"""
return self._group_ios_per_hypervisor
return self._allocate_hypervisor_per_ios_image
@group_ios_per_hypervisor.setter
def group_ios_per_hypervisor(self, value):
@allocate_hypervisor_per_ios_image.setter
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.
: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):
"""
@ -172,6 +438,10 @@ class HypervisorManager(object):
log.info("hypervisor {}:{} has successfully started".format(hypervisor.host, hypervisor.port))
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._current_port += 1
return hypervisor
@ -187,16 +457,21 @@ class HypervisorManager(object):
:returns: the allocated hypervisor object
"""
for hypervisor in self._hypervisors:
if self._group_ios_per_hypervisor and hypervisor.image_ref != router_ios_image:
continue
if (hypervisor.memory_load + router_ram) <= self._memory_usage_limit_per_hypervisor:
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))
# allocate an hypervisor for each router by default
if not self._allocate_hypervisor_per_device:
for hypervisor in self._hypervisors:
if self._allocate_hypervisor_per_ios_image:
if not hypervisor.image_ref:
hypervisor.image_ref = router_ios_image
elif hypervisor.image_ref != router_ios_image:
continue
if (hypervisor.memory_load + router_ram) <= self._memory_usage_limit_per_hypervisor:
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
hypervisor = self.start_new_hypervisor()
@ -226,6 +501,22 @@ class HypervisorManager(object):
hypervisor.stop()
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):
"""
Stops all hypervisors.
@ -233,3 +524,4 @@ class HypervisorManager(object):
for hypervisor in self._hypervisors:
hypervisor.stop()
self._hypervisors = []

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

@ -21,6 +21,9 @@ Interface for FIFO NIOs.
from .nio import NIO
import logging
log = logging.getLogger(__name__)
class NIO_FIFO(NIO):
"""
@ -42,6 +45,16 @@ class NIO_FIFO(NIO):
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):
"""
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,
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
import logging
log = logging.getLogger(__name__)
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,
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
def ethernet_device(self):
"""

@ -21,6 +21,9 @@ Interface for Linux Ethernet NIOs (Linux only).
from .nio import NIO
import logging
log = logging.getLogger(__name__)
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,
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
def ethernet_device(self):
"""

@ -21,6 +21,9 @@ Interface for multicast NIOs.
from .nio import NIO
import logging
log = logging.getLogger(__name__)
class NIO_Mcast(NIO):
"""
@ -49,6 +52,18 @@ class NIO_Mcast(NIO):
mgroup=group,
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
def group(self):
"""
@ -82,7 +97,7 @@ class NIO_Mcast(NIO):
@ttl.setter
def ttl(self, ttl):
"""
Set the TTL for the multicast address
Sets the TTL for the multicast address
:param ttl: TTL value
"""

@ -21,6 +21,9 @@ Interface for dummy NIOs (mostly for tests).
from .nio import NIO
import logging
log = logging.getLogger(__name__)
class NIO_Null(NIO):
"""
@ -41,3 +44,12 @@ class NIO_Null(NIO):
self._name = 'nio_null' + str(self._id)
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
import logging
log = logging.getLogger(__name__)
class NIO_TAP(NIO):
"""
@ -45,6 +48,17 @@ class NIO_TAP(NIO):
self._hypervisor.send("nio create_tap {name} {tap}".format(name=self._name,
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
def tap_device(self):
"""

@ -21,6 +21,9 @@ Interface for UDP NIOs.
from .nio import NIO
import logging
log = logging.getLogger(__name__)
class NIO_UDP(NIO):
"""
@ -51,6 +54,19 @@ class NIO_UDP(NIO):
rhost=rhost,
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
def lport(self):
"""

@ -21,6 +21,9 @@ Interface for automatic UDP NIOs.
from .nio import NIO
import logging
log = logging.getLogger(__name__)
class NIO_UDP_auto(NIO):
"""
@ -48,9 +51,22 @@ class NIO_UDP_auto(NIO):
laddr=laddr,
lport_start=lport_start,
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._rport = None
@classmethod
def reset(cls):
"""
Reset the instance count.
"""
cls._instance_count = 0
@property
def laddr(self):
"""
@ -104,3 +120,7 @@ class NIO_UDP_auto(NIO):
rport=rport))
self._raddr = raddr
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
import logging
log = logging.getLogger(__name__)
class NIO_UNIX(NIO):
"""
@ -48,6 +51,18 @@ class NIO_UNIX(NIO):
local=local_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
def local_file(self):
"""

@ -21,6 +21,9 @@ Interface for VDE (Virtual Distributed Ethernet) NIOs (Unix based OSes only).
from .nio import NIO
import logging
log = logging.getLogger(__name__)
class NIO_VDE(NIO):
"""
@ -48,6 +51,18 @@ class NIO_VDE(NIO):
control=control_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
def control_file(self):
"""

@ -23,6 +23,9 @@ http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L593
from __future__ import unicode_literals
from ..dynamips_error import DynamipsError
import logging
log = logging.getLogger(__name__)
class ATMSwitch(object):
"""
@ -32,15 +35,47 @@ class ATMSwitch(object):
: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._name = '"' + name + '"' # put name into quotes to protect spaces
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._nios = {}
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
def name(self):
"""
@ -100,6 +135,11 @@ class ATMSwitch(object):
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,
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
def delete(self):
@ -108,8 +148,22 @@ class ATMSwitch(object):
"""
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)
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):
"""
Adds a NIO as new port on ATM switch.
@ -121,6 +175,11 @@ class ATMSwitch(object):
if port in self._nios:
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
def remove_nio(self, port):
@ -133,7 +192,14 @@ class ATMSwitch(object):
if port not in self._nios:
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]
return nio
def map_vp(self, port1, vpi1, port2, vpi2):
"""
@ -159,6 +225,14 @@ class ATMSwitch(object):
input_vpi=vpi1,
output_nio=nio2,
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)
def unmap_vp(self, port1, vpi1, port2, vpi2):
@ -185,6 +259,14 @@ class ATMSwitch(object):
input_vpi=vpi1,
output_nio=nio2,
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)]
def map_pvc(self, port1, vpi1, vci1, port2, vpi2, vci2):
@ -215,6 +297,16 @@ class ATMSwitch(object):
output_nio=nio2,
output_vpi=vpi2,
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)
def unmap_pvc(self, port1, vpi1, vci1, port2, vpi2, vci2):
@ -245,4 +337,13 @@ class ATMSwitch(object):
output_nio=nio2,
output_vpi=vpi2,
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)]

@ -88,6 +88,7 @@ class Bridge(object):
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,
new_name=new_name))
self._name = new_name
def delete(self):

@ -25,6 +25,9 @@ from .router import Router
from ..adapters.c1700_mb_1fe import C1700_MB_1FE
from ..adapters.c1700_mb_wic1 import C1700_MB_WIC1
import logging
log = logging.getLogger(__name__)
class C1700(Router):
"""
@ -37,7 +40,7 @@ class C1700(Router):
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")
# Set default values for this platform
@ -54,6 +57,27 @@ class C1700(Router):
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):
"""
Returns all c1700 instances
@ -65,7 +89,7 @@ class C1700(Router):
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).
"""
@ -91,7 +115,7 @@ class C1700(Router):
@chassis.setter
def chassis(self, chassis):
"""
Set the chassis.
Sets the chassis.
:param: chassis string:
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,
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._setup_chassis()
@ -115,11 +144,16 @@ class C1700(Router):
@iomem.setter
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
"""
self._hypervisor.send("c1700 set_iomem {name} {size}".format(name=self._name,
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

@ -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_2fe import C2600_MB_2FE
import logging
log = logging.getLogger(__name__)
class C2600(Router):
"""
@ -52,7 +55,7 @@ class C2600(Router):
'2650XM': C2600_MB_1FE,
'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")
# Set default values for this platform
@ -69,6 +72,27 @@ class C2600(Router):
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):
"""
Returns all c2600 instances
@ -80,7 +104,7 @@ class C2600(Router):
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).
"""
@ -100,7 +124,7 @@ class C2600(Router):
@chassis.setter
def chassis(self, chassis):
"""
Set the chassis.
Sets the chassis.
:param: chassis string:
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,
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._setup_chassis()
@ -125,11 +153,16 @@ class C2600(Router):
@iomem.setter
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
"""
self._hypervisor.send("c2600 set_iomem {name} {size}".format(name=self._name,
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

@ -24,6 +24,9 @@ from __future__ import unicode_literals
from .router import Router
from ..adapters.gt96100_fe import GT96100_FE
import logging
log = logging.getLogger(__name__)
class C2691(Router):
"""
@ -33,7 +36,7 @@ class C2691(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")
# Set default values for this platform
@ -47,6 +50,26 @@ class C2691(Router):
self._create_slots(2)
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):
"""
Returns all c2691 instances
@ -69,11 +92,16 @@ class C2691(Router):
@iomem.setter
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
"""
self._hypervisor.send("c2691 set_iomem {name} {size}".format(name=self._name,
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

@ -24,6 +24,9 @@ from __future__ import unicode_literals
from .router import Router
from ..adapters.leopard_2fe import Leopard_2FE
import logging
log = logging.getLogger(__name__)
class C3600(Router):
"""
@ -35,7 +38,7 @@ class C3600(Router):
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")
# Set default values for this platform
@ -52,6 +55,27 @@ class C3600(Router):
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):
"""
Returns all c3600 instances
@ -63,7 +87,7 @@ class C3600(Router):
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).
"""
@ -88,13 +112,18 @@ class C3600(Router):
@chassis.setter
def chassis(self, chassis):
"""
Set the chassis.
Sets the chassis.
:param: chassis string: 3620, 3640 or 3660
"""
self._hypervisor.send("c3600 set_chassis {name} {chassis}".format(name=self._name,
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._setup_chassis()
@ -118,4 +147,9 @@ class C3600(Router):
self._hypervisor.send("c3600 set_iomem {name} {size}".format(name=self._name,
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

@ -24,6 +24,9 @@ from __future__ import unicode_literals
from .router import Router
from ..adapters.gt96100_fe import GT96100_FE
import logging
log = logging.getLogger(__name__)
class C3725(Router):
"""
@ -33,7 +36,7 @@ class C3725(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")
# Set default values for this platform
@ -47,6 +50,26 @@ class C3725(Router):
self._create_slots(3)
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):
"""
Returns all c3725 instances.
@ -69,11 +92,16 @@ class C3725(Router):
@iomem.setter
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
"""
self._hypervisor.send("c3725 set_iomem {name} {size}".format(name=self._name,
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

@ -24,6 +24,9 @@ from __future__ import unicode_literals
from .router import Router
from ..adapters.gt96100_fe import GT96100_FE
import logging
log = logging.getLogger(__name__)
class C3745(Router):
"""
@ -33,7 +36,7 @@ class C3745(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")
# Set default values for this platform
@ -47,6 +50,26 @@ class C3745(Router):
self._create_slots(5)
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):
"""
Returns all c3745 instances.
@ -69,11 +92,16 @@ class C3745(Router):
@iomem.setter
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
"""
self._hypervisor.send("c3745 set_iomem {name} {size}".format(name=self._name,
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

@ -20,13 +20,15 @@ Interface for Dynamips virtual Cisco 7200 instances module ("c7200")
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L294
"""
from __future__ import unicode_literals
from ..dynamips_error import DynamipsError
from .router import Router
from ..adapters.c7200_io_2fe import C7200_IO_2FE
from ..adapters.c7200_io_ge_e import C7200_IO_GE_E
import logging
log = logging.getLogger(__name__)
class C7200(Router):
"""
@ -37,7 +39,7 @@ class C7200(Router):
: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")
# Set default values for this platform
@ -70,6 +72,27 @@ class C7200(Router):
else:
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):
"""
Returns all c7200 instances.
@ -92,7 +115,7 @@ class C7200(Router):
@npe.setter
def npe(self, npe):
"""
Set the NPE model.
Sets the NPE model.
:params npe: NPE model string (e.g. "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,
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
@property
@ -119,13 +147,18 @@ class C7200(Router):
@midplane.setter
def midplane(self, midplane):
"""
Set the midplane model.
Sets the midplane model.
:returns: midplane model string (e.g. "vxr" or "std")
"""
self._hypervisor.send("c7200 set_midplane {name} {midplane}".format(name=self._name,
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
@property
@ -141,7 +174,7 @@ class C7200(Router):
@sensors.setter
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
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,
sensor_id=sensor_id,
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
self._sensors = sensors
@ -172,7 +212,7 @@ class C7200(Router):
@power_supplies.setter
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.
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,
power_supply_id=power_supply_id,
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
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 ..dynamips_error import DynamipsError
import logging
log = logging.getLogger(__name__)
class EthernetSwitch(object):
"""
@ -33,15 +36,47 @@ class EthernetSwitch(object):
: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._name = '"' + name + '"' # put name into quotes to protect spaces
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._nios = {}
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
def name(self):
"""
@ -101,6 +136,11 @@ class EthernetSwitch(object):
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,
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
def delete(self):
@ -109,6 +149,9 @@ class EthernetSwitch(object):
"""
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)
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,
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
def remove_nio(self, port):
@ -131,6 +179,8 @@ class EthernetSwitch(object):
Removes the specified NIO as member of this Ethernet switch.
:param port: allocated port
:returns: the NIO that was bound to the port
"""
if port not in self._nios:
@ -139,14 +189,22 @@ class EthernetSwitch(object):
nio = self._nios[port]
self._hypervisor.send("ethsw remove_nio {name} {nio}".format(name=self._name,
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]
if port in self._mapping:
del self._mapping[port]
return nio
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 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,
nio=nio,
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)
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 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,
nio=nio,
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)
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 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,
nio=nio,
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)
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 ..dynamips_error import DynamipsError
import logging
log = logging.getLogger(__name__)
class FrameRelaySwitch(object):
"""
@ -32,15 +35,47 @@ class FrameRelaySwitch(object):
: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._name = '"' + name + '"' # put name into quotes to protect spaces
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._nios = {}
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
def name(self):
"""
@ -100,6 +135,11 @@ class FrameRelaySwitch(object):
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,
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
def delete(self):
@ -108,8 +148,22 @@ class FrameRelaySwitch(object):
"""
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)
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):
"""
Adds a NIO as new port on Frame Relay switch.
@ -121,6 +175,11 @@ class FrameRelaySwitch(object):
if port in self._nios:
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
def remove_nio(self, port):
@ -128,12 +187,21 @@ class FrameRelaySwitch(object):
Removes the specified NIO as member of this Frame Relay switch.
:param port: allocated port
:returns: the NIO that was bound to the allocated port
"""
if port not in self._nios:
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]
return nio
def map_vc(self, port1, dlci1, port2, dlci2):
"""
@ -159,6 +227,14 @@ class FrameRelaySwitch(object):
input_dlci=dlci1,
output_nio=nio2,
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)
def unmap_vc(self, port1, dlci1, port2, dlci2):
@ -185,4 +261,11 @@ class FrameRelaySwitch(object):
input_dlci=dlci1,
output_nio=nio2,
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)]

@ -23,6 +23,9 @@ from __future__ import unicode_literals
from .bridge import Bridge
from ..dynamips_error import DynamipsError
import logging
log = logging.getLogger(__name__)
class Hub(Bridge):
"""
@ -32,10 +35,41 @@ class Hub(Bridge):
:param name: name for this hub
"""
_instance_count = 1
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 = {}
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
def mapping(self):
@ -47,6 +81,15 @@ class Hub(Bridge):
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):
"""
Adds a NIO as new port on this hub.
@ -59,6 +102,11 @@ class Hub(Bridge):
raise DynamipsError("Port {} isn't free".format(port))
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
def remove_nio(self, port):
@ -66,6 +114,8 @@ class Hub(Bridge):
Removes the specified NIO as member of this hub.
:param port: allocated port
:returns: the NIO that was bound to the allocated port
"""
if port not in self._mapping:
@ -73,4 +123,11 @@ class Hub(Bridge):
nio = self._mapping[port]
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]
return nio

@ -24,29 +24,36 @@ from __future__ import unicode_literals
from ..dynamips_error import DynamipsError
import os
import logging
log = logging.getLogger(__name__)
class Router(object):
"""
Dynamips router
Dynamips router implementation.
:param hypervisor: Dynamips hypervisor object
:param name: name for this router
: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",
1: "shutting down",
2: "running",
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
self._id = Router._instance_count
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._name = '"' + name + '"' # put name into quotes to protect spaces
self._platform = platform
@ -76,12 +83,66 @@ class Router(object):
id=self._id,
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.aux = self._hypervisor.baseaux + self._id
else:
log.info("creating a new ghost IOS file")
Router._instance_count -= 1
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
def id(self):
"""
@ -151,6 +212,11 @@ class Router(object):
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,
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
def delete(self):
@ -161,13 +227,19 @@ class Router(object):
self._hypervisor.send("vm delete {}".format(self._name))
self._hypervisor.devices.remove(self)
log.info("router {name} [id={id}] has been deleted".format(name=self._name, id=self._id))
def start(self):
"""
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):
"""
@ -176,13 +248,16 @@ class Router(object):
"""
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):
"""
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):
"""
@ -190,12 +265,13 @@ class Router(object):
"""
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):
"""
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])
@ -225,7 +301,7 @@ class Router(object):
@jit_sharing_group.setter
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
"""
@ -236,12 +312,16 @@ class Router(object):
self._hypervisor.send("vm set_tsg {name} {group_id}".format(name=self._name,
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._hypervisor.add_jitsharing_group(os.path.basename(self._image), group_id)
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
"""
@ -262,7 +342,7 @@ class Router(object):
@image.setter
def image(self, image):
"""
Set the IOS image for this router.
Sets the IOS image for this router.
There is no default.
:param image: path to IOS image file
@ -271,11 +351,16 @@ class Router(object):
# encase image in quotes to protect spaces in the path
self._hypervisor.send("vm set_ios {name} {image}".format(name=self._name,
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
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.
:param startup_config: path to statup-config file
@ -287,6 +372,15 @@ class Router(object):
startup='"' + startup_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):
"""
Gets the contents of the config files
@ -318,6 +412,13 @@ class Router(object):
startup=startup_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
def ram(self):
"""
@ -331,13 +432,22 @@ class Router(object):
@ram.setter
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)
"""
if self._ram == ram:
return
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._ram = ram
self._hypervisor.increase_memory_load(self._ram)
@ -355,13 +465,21 @@ class Router(object):
@nvram.setter
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)
"""
if self._nvram == nvram:
return
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
@property
@ -389,6 +507,13 @@ class Router(object):
flag = 0
self._hypervisor.send("vm set_ram_mmap {name} {mmap}".format(name=self._name,
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
@property
@ -415,6 +540,13 @@ class Router(object):
flag = 0
self._hypervisor.send("vm set_sparse_mem {name} {sparsemem}".format(name=self._name,
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
@property
@ -430,7 +562,7 @@ class Router(object):
@clock_divisor.setter
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.
: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,
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
@property
@ -453,7 +590,7 @@ class Router(object):
@idlepc.setter
def idlepc(self, idlepc):
"""
Set the idle Pointer Counter (PC)
Sets the idle Pointer Counter (PC)
:param idlepc: idlepc value (string)
"""
@ -465,6 +602,11 @@ class Router(object):
else:
self._hypervisor.send("vm set_idle_pc_online {name} 0 {idlepc}".format(name=self._name,
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
def get_idle_pc_prop(self):
@ -500,7 +642,7 @@ class Router(object):
@idlemax.setter
def idlemax(self, idlemax):
"""
Set CPU idle max value
Sets CPU idle max value
:param idlemax: idle max value (integer)
"""
@ -508,6 +650,12 @@ class Router(object):
if self.is_running(): # router is running
self._hypervisor.send("vm set_idle_max {name} 0 {idlemax}".format(name=self._name,
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
@property
@ -523,7 +671,7 @@ class Router(object):
@idlesleep.setter
def idlesleep(self, idlesleep):
"""
Set CPU idle sleep time value.
Sets CPU idle sleep time value.
:param idlesleep: idle sleep value (integer)
"""
@ -531,6 +679,12 @@ class Router(object):
if self.is_running(): # router is running
self._hypervisor.send("vm set_idle_sleep_time {name} 0 {idlesleep}".format(name=self._name,
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
def show_timer_drift(self):
@ -555,16 +709,21 @@ class Router(object):
@ghost_file.setter
def ghost_file(self, ghost_file):
"""
Set ghost RAM file
Sets ghost RAM file
:ghost_file: path to ghost file
"""
self._hypervisor.send("vm set_ghost_file {name} {ghost_file}".format(name=self._name,
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
# 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:
self._hypervisor.add_ghost(ghost_file, self)
@ -575,8 +734,8 @@ class Router(object):
:returns: formatted ghost_file name (string)
"""
# 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'
# replace specials characters in 'drive:\filename' in Linux and Dynamips in MS Windows or viceversa.
ghost_file = "{}-{}.ghost".format(os.path.basename(self._image), self._ram)
ghost_file = ghost_file.replace('\\', '-').replace('/', '-').replace(':', '-')
return ghost_file
@ -592,7 +751,7 @@ class Router(object):
@ghost_status.setter
def ghost_status(self, ghost_status):
"""
Set ghost RAM status
Sets ghost RAM status
:param ghost_status: state flag indicating status
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,
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
@property
@ -617,7 +780,7 @@ class Router(object):
@exec_area.setter
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
translated by the JIT (they contain the native code
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,
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
@property
@ -642,13 +810,18 @@ class Router(object):
@disk0.setter
def disk0(self, disk0):
"""
Set the size (MB) for PCMCIA disk0.
Sets the size (MB) for PCMCIA disk0.
:param disk0: disk0 size (integer)
"""
self._hypervisor.send("vm set_disk0 {name} {disk0}".format(name=self._name,
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
@property
@ -664,13 +837,18 @@ class Router(object):
@disk1.setter
def disk1(self, disk1):
"""
Set the size (MB) for PCMCIA disk1.
Sets the size (MB) for PCMCIA disk1.
:param disk1: disk1 size (integer)
"""
self._hypervisor.send("vm set_disk1 {name} {disk1}".format(name=self._name,
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
@property
@ -687,13 +865,18 @@ class Router(object):
@confreg.setter
def confreg(self, confreg):
"""
Set the configuration register.
Sets the configuration register.
:param confreg: configuration register value (string)
"""
self._hypervisor.send("vm set_conf_reg {name} {confreg}".format(name=self._name,
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
@property
@ -709,7 +892,7 @@ class Router(object):
@console.setter
def console(self, console):
"""
Set the TCP console port.
Sets the TCP console port.
: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,
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
@property
@ -734,7 +922,7 @@ class Router(object):
@aux.setter
def aux(self, aux):
"""
Set the TCP auxiliary port.
Sets the TCP auxiliary port.
: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,
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
def get_cpu_info(self, cpu_id=0):
@ -801,7 +994,7 @@ class Router(object):
@mac_addr.setter
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)
"""
@ -809,6 +1002,11 @@ class Router(object):
self._hypervisor.send("{platform} set_mac_addr {name} {mac_addr}".format(platform=self._platform,
name=self._name,
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
@property
@ -824,7 +1022,7 @@ class Router(object):
@system_id.setter
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)
"""
@ -832,6 +1030,11 @@ class Router(object):
self._hypervisor.send("{platform} set_system_id {name} {system_id}".format(platform=self._platform,
name=self._name,
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
def get_hardware_info(self):
@ -841,7 +1044,7 @@ class Router(object):
: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,
name=self._name)))
@ -856,7 +1059,7 @@ class Router(object):
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 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,
slot_id=slot_id,
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
# 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,
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):
"""
Removes a slot binding.
Removes a slot binding (a module from a slot).
:param slot_id: slot ID
"""
try:
slot = self._slots[slot_id]
adapter = self._slots[slot_id]
except IndexError:
raise DynamipsError("Slot {slot_id} doesn't exist on router {name}".format(name=self._name,
slot_id=slot_id))
if slot == None:
if adapter == None:
return
#FIXME: check if adapter can be removed!
# Generate an OIR event if the router is running and
# only for c7200, c3600 and c3745 (NM-4T only)
if self.is_running() and self._platform == 'c7200' \
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,
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,
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
def install_wic(self, wic_slot_id, wic):
@ -947,6 +1171,12 @@ class Router(object):
slot_id=slot_id,
wic_slot_id=internal_wic_slot_id,
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)
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,
slot_id=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)
def get_slot_nio_bindings(self, slot_id):
@ -1012,6 +1247,13 @@ class Router(object):
slot_id=slot_id,
port_id=port_id,
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)
adapter.add_nio(port_id, nio)
@ -1021,6 +1263,8 @@ class Router(object):
:param slot_id: slot ID
:param port_id: port ID
:returns: removed NIO object
"""
try:
@ -1033,12 +1277,21 @@ class Router(object):
port_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,
slot_id=slot_id,
port_id=port_id))
self._hypervisor.send("vm slot_remove_nio_binding {name} {slot_id} {port_id}".format(name=self._name,
slot_id=slot_id,
port_id=port_id))
nio = adapter.get_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):
"""
Enables a slot NIO binding.
@ -1052,6 +1305,11 @@ class Router(object):
slot_id=slot_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):
"""
Disables a slot NIO binding.
@ -1065,6 +1323,11 @@ class Router(object):
slot_id=slot_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):
"""
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!
try:
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:
if e.errno == errno.EADDRINUSE: # socket already in use
logging.critical("socket in use for port {}".format(self._port))
@ -92,7 +92,7 @@ class Server(object):
ioloop = tornado.ioloop.IOLoop.instance()
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))
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
pyzmq
netifaces-py3

@ -36,7 +36,7 @@ class JSONRPC(AsyncTestCase):
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))
response = self.wait()
json_response = json_decode(response)

Loading…
Cancel
Save