mirror of
https://github.com/GNS3/gns3-server
synced 2024-11-24 09:18:08 +00:00
Handle startup-config push using base64
Dynamips working directory management Random port selection for the ZeroMQ server TCP & UDP port allocation in a range with improvements Update Dynamips to 0.2.11 (for the tests) Focus on Python3 development (stop trying to be compatible with Python 2.x) More error/bug catching
This commit is contained in:
parent
687d5b75ab
commit
89888ae7bf
@ -82,8 +82,8 @@ class ModuleManager(object):
|
|||||||
log.info("loading {} module".format(module_class[0].lower()))
|
log.info("loading {} module".format(module_class[0].lower()))
|
||||||
info = Module(name=module_class[0].lower(), cls=module_class[1])
|
info = Module(name=module_class[0].lower(), cls=module_class[1])
|
||||||
self._modules.append(info)
|
self._modules.append(info)
|
||||||
except:
|
except Exception as e:
|
||||||
log.warning("error while analyzing {} package directory".format(name))
|
log.critical("error while analyzing {} package directory".format(name), exc_info=1)
|
||||||
finally:
|
finally:
|
||||||
if file:
|
if file:
|
||||||
file.close()
|
file.close()
|
||||||
|
@ -93,9 +93,16 @@ class IModule(multiprocessing.Process):
|
|||||||
stream.on_recv(callback)
|
stream.on_recv(callback)
|
||||||
return stream
|
return stream
|
||||||
|
|
||||||
# def add_periodic_callback(self, callback, time):
|
def add_periodic_callback(self, callback, time):
|
||||||
#
|
"""
|
||||||
# self.test = zmq.eventloop.ioloop.PeriodicCallback(callback, time, self._ioloop).start()
|
Adds a periodic callback to the ioloop.
|
||||||
|
|
||||||
|
:param callback: callback to be called
|
||||||
|
:param time: frequency when the callback is executed
|
||||||
|
"""
|
||||||
|
|
||||||
|
periodic_callback = zmq.eventloop.ioloop.PeriodicCallback(callback, time, self._ioloop)
|
||||||
|
return periodic_callback
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""
|
"""
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
Dynamips server module.
|
Dynamips server module.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import tempfile
|
||||||
from gns3server.modules import IModule
|
from gns3server.modules import IModule
|
||||||
import gns3server.jsonrpc as jsonrpc
|
import gns3server.jsonrpc as jsonrpc
|
||||||
|
|
||||||
@ -99,12 +101,16 @@ class Dynamips(IModule):
|
|||||||
IModule.__init__(self, name=name, args=args, kwargs=kwargs)
|
IModule.__init__(self, name=name, args=args, kwargs=kwargs)
|
||||||
|
|
||||||
self._hypervisor_manager = None
|
self._hypervisor_manager = None
|
||||||
|
self._remote_server = False
|
||||||
self._routers = {}
|
self._routers = {}
|
||||||
self._ethernet_switches = {}
|
self._ethernet_switches = {}
|
||||||
self._frame_relay_switches = {}
|
self._frame_relay_switches = {}
|
||||||
self._atm_switches = {}
|
self._atm_switches = {}
|
||||||
self._ethernet_hubs = {}
|
self._ethernet_hubs = {}
|
||||||
|
|
||||||
|
#self._callback = self.add_periodic_callback(self.test, 1000)
|
||||||
|
#self._callback.start()
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
"""
|
"""
|
||||||
Properly stops the module.
|
Properly stops the module.
|
||||||
@ -148,6 +154,8 @@ class Dynamips(IModule):
|
|||||||
self._frame_relay_switches.clear()
|
self._frame_relay_switches.clear()
|
||||||
self._atm_switches.clear()
|
self._atm_switches.clear()
|
||||||
|
|
||||||
|
self._hypervisor_manager = None
|
||||||
|
self._remote_server = False
|
||||||
log.info("dynamips module has been reset")
|
log.info("dynamips module has been reset")
|
||||||
|
|
||||||
@IModule.route("dynamips.settings")
|
@IModule.route("dynamips.settings")
|
||||||
@ -155,6 +163,12 @@ class Dynamips(IModule):
|
|||||||
"""
|
"""
|
||||||
Set or update settings.
|
Set or update settings.
|
||||||
|
|
||||||
|
Mandatory request parameters:
|
||||||
|
- path (path to the Dynamips executable)
|
||||||
|
|
||||||
|
Optional request parameters:
|
||||||
|
- working_dir (path to a working directory)
|
||||||
|
|
||||||
:param request: JSON request
|
:param request: JSON request
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -167,9 +181,32 @@ class Dynamips(IModule):
|
|||||||
#TODO: JSON schema validation
|
#TODO: JSON schema validation
|
||||||
# starts the hypervisor manager if it hasn't been started yet
|
# starts the hypervisor manager if it hasn't been started yet
|
||||||
if not self._hypervisor_manager:
|
if not self._hypervisor_manager:
|
||||||
#TODO: working dir support
|
dynamips_path = request["path"]
|
||||||
log.info("starting the hypervisor manager with Dynamips working directory set to '{}'".format("/tmp"))
|
|
||||||
self._hypervisor_manager = HypervisorManager(request["path"], "/tmp")
|
if "working_dir" in request:
|
||||||
|
working_dir = request["working_dir"]
|
||||||
|
log.info("this server is local")
|
||||||
|
else:
|
||||||
|
self._remote_server = True
|
||||||
|
log.info("this server is remote")
|
||||||
|
try:
|
||||||
|
working_dir = tempfile.mkdtemp(prefix="gns3-remote-server-")
|
||||||
|
working_dir = os.path.join(working_dir, "dynamips")
|
||||||
|
os.makedirs(working_dir)
|
||||||
|
log.info("temporary working directory created: {}".format(working_dir))
|
||||||
|
except EnvironmentError as e:
|
||||||
|
raise DynamipsError("Could not create temporary working directory: {}".format(e))
|
||||||
|
|
||||||
|
#TODO: check if executable
|
||||||
|
if not os.path.exists(dynamips_path):
|
||||||
|
raise DynamipsError("Dynamips executable {} doesn't exist".format(working_dir))
|
||||||
|
|
||||||
|
#TODO: check if writable
|
||||||
|
if not os.path.exists(working_dir):
|
||||||
|
raise DynamipsError("Working directory {} doesn't exist".format(working_dir))
|
||||||
|
|
||||||
|
log.info("starting the hypervisor manager with Dynamips working directory set to '{}'".format(working_dir))
|
||||||
|
self._hypervisor_manager = HypervisorManager(dynamips_path, working_dir)
|
||||||
|
|
||||||
# apply settings to the hypervisor manager
|
# apply settings to the hypervisor manager
|
||||||
for name, value in request.items():
|
for name, value in request.items():
|
||||||
@ -305,6 +342,28 @@ class Dynamips(IModule):
|
|||||||
router.ghost_status = 2
|
router.ghost_status = 2
|
||||||
router.ghost_file = ghost_instance
|
router.ghost_file = ghost_instance
|
||||||
|
|
||||||
|
# def get_base64_config(self, config_path, router):
|
||||||
|
# """
|
||||||
|
# Get the base64 encoded config from a file.
|
||||||
|
# Replaces %h by the router name.
|
||||||
|
#
|
||||||
|
# :param config_path: path to the configuration file.
|
||||||
|
# :param router: Router instance.
|
||||||
|
#
|
||||||
|
# :returns: base64 encoded string
|
||||||
|
# """
|
||||||
|
#
|
||||||
|
# try:
|
||||||
|
# with open(config_path, "r") as f:
|
||||||
|
# log.info("opening configuration file: {}".format(config_path))
|
||||||
|
# config = f.read()
|
||||||
|
# config = '!\n' + config.replace('\r', "")
|
||||||
|
# config = config.replace('%h', router.name)
|
||||||
|
# encoded = ("").join(base64.encodestring(config.encode("utf-8")).decode("utf-8").split())
|
||||||
|
# return encoded
|
||||||
|
# except EnvironmentError as e:
|
||||||
|
# raise DynamipsError("Cannot parse {}: {}".format(config_path, e))
|
||||||
|
|
||||||
@IModule.route("dynamips.nio.get_interfaces")
|
@IModule.route("dynamips.nio.get_interfaces")
|
||||||
def nio_get_interfaces(self, request):
|
def nio_get_interfaces(self, request):
|
||||||
"""
|
"""
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import base64
|
||||||
from gns3server.modules import IModule
|
from gns3server.modules import IModule
|
||||||
from ..dynamips_error import DynamipsError
|
from ..dynamips_error import DynamipsError
|
||||||
|
|
||||||
@ -121,8 +122,13 @@ class VM(object):
|
|||||||
platform = request["platform"]
|
platform = request["platform"]
|
||||||
image = request["image"]
|
image = request["image"]
|
||||||
ram = request["ram"]
|
ram = request["ram"]
|
||||||
|
hypervisor = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
|
if not self._hypervisor_manager:
|
||||||
|
raise DynamipsError("Dynamips manager is not started")
|
||||||
|
|
||||||
hypervisor = self._hypervisor_manager.allocate_hypervisor_for_router(image, ram)
|
hypervisor = self._hypervisor_manager.allocate_hypervisor_for_router(image, ram)
|
||||||
|
|
||||||
router = PLATFORMS[platform](hypervisor, name)
|
router = PLATFORMS[platform](hypervisor, name)
|
||||||
@ -158,11 +164,14 @@ class VM(object):
|
|||||||
self.set_ghost_ios(router)
|
self.set_ghost_ios(router)
|
||||||
|
|
||||||
except DynamipsError as e:
|
except DynamipsError as e:
|
||||||
hypervisor.decrease_memory_load(ram)
|
dynamips_stdout = ""
|
||||||
if hypervisor.memory_load == 0 and not hypervisor.devices:
|
if hypervisor:
|
||||||
hypervisor.stop()
|
hypervisor.decrease_memory_load(ram)
|
||||||
self._hypervisor_manager.hypervisors.remove(hypervisor)
|
if hypervisor.memory_load == 0 and not hypervisor.devices:
|
||||||
self.send_custom_error(str(e))
|
hypervisor.stop()
|
||||||
|
self._hypervisor_manager.hypervisors.remove(hypervisor)
|
||||||
|
dynamips_stdout = hypervisor.read_stdout()
|
||||||
|
self.send_custom_error(str(e) + dynamips_stdout)
|
||||||
return
|
return
|
||||||
|
|
||||||
response = {"name": router.name,
|
response = {"name": router.name,
|
||||||
@ -330,6 +339,7 @@ class VM(object):
|
|||||||
|
|
||||||
Optional request parameters:
|
Optional request parameters:
|
||||||
- any setting to update
|
- any setting to update
|
||||||
|
- startup_config_base64 (startup-config base64 encoded)
|
||||||
|
|
||||||
Response parameters:
|
Response parameters:
|
||||||
- same as original request
|
- same as original request
|
||||||
@ -346,6 +356,32 @@ class VM(object):
|
|||||||
router_id = request["id"]
|
router_id = request["id"]
|
||||||
router = self._routers[router_id]
|
router = self._routers[router_id]
|
||||||
|
|
||||||
|
try:
|
||||||
|
# a new startup-config has been pushed
|
||||||
|
if "startup_config_base64" in request:
|
||||||
|
config = base64.decodestring(request["startup_config_base64"].encode("utf-8")).decode("utf-8")
|
||||||
|
config = "!\n" + config.replace("\r", "")
|
||||||
|
config = config.replace('%h', router.name)
|
||||||
|
config_dir = os.path.join(router.hypervisor.working_dir, "configs")
|
||||||
|
if not os.path.exists(config_dir):
|
||||||
|
try:
|
||||||
|
os.makedirs(config_dir)
|
||||||
|
except EnvironmentError as e:
|
||||||
|
raise DynamipsError("Could not create configs directory: {}".format(e))
|
||||||
|
config_path = os.path.join(config_dir, "{}.cfg".format(router.name))
|
||||||
|
try:
|
||||||
|
with open(config_path, "w") as f:
|
||||||
|
log.info("saving startup-config to {}".format(config_path))
|
||||||
|
f.write(config)
|
||||||
|
except EnvironmentError as e:
|
||||||
|
raise DynamipsError("Could not save the configuration {}: {}".format(config_path, e))
|
||||||
|
request["startup_config"] = "configs" + os.sep + os.path.basename(config_path)
|
||||||
|
if "startup_config" in request:
|
||||||
|
router.set_config(request["startup_config"])
|
||||||
|
except DynamipsError as e:
|
||||||
|
self.send_custom_error(str(e))
|
||||||
|
return
|
||||||
|
|
||||||
# update the settings
|
# update the settings
|
||||||
for name, value in request.items():
|
for name, value in request.items():
|
||||||
if hasattr(router, name) and getattr(router, name) != value:
|
if hasattr(router, name) and getattr(router, name) != value:
|
||||||
@ -370,7 +406,7 @@ class VM(object):
|
|||||||
if router.slots[slot_id]:
|
if router.slots[slot_id]:
|
||||||
try:
|
try:
|
||||||
router.slot_remove_binding(slot_id)
|
router.slot_remove_binding(slot_id)
|
||||||
except:
|
except DynamipsError as e:
|
||||||
self.send_custom_error(str(e))
|
self.send_custom_error(str(e))
|
||||||
return
|
return
|
||||||
elif name.startswith("wic") and value in WIC_MATRIX:
|
elif name.startswith("wic") and value in WIC_MATRIX:
|
||||||
@ -387,7 +423,11 @@ class VM(object):
|
|||||||
elif name.startswith("wic") and value == None:
|
elif name.startswith("wic") and value == None:
|
||||||
wic_slot_id = int(name[-1])
|
wic_slot_id = int(name[-1])
|
||||||
if router.slots[0].wics and router.slots[0].wics[wic_slot_id]:
|
if router.slots[0].wics and router.slots[0].wics[wic_slot_id]:
|
||||||
router.uninstall_wic(wic_slot_id)
|
try:
|
||||||
|
router.uninstall_wic(wic_slot_id)
|
||||||
|
except DynamipsError as e:
|
||||||
|
self.send_custom_error(str(e))
|
||||||
|
return
|
||||||
|
|
||||||
# Update the ghost IOS file in case the RAM size has changed
|
# Update the ghost IOS file in case the RAM size has changed
|
||||||
if self._hypervisor_manager.ghost_ios_support:
|
if self._hypervisor_manager.ghost_ios_support:
|
||||||
@ -396,6 +436,40 @@ class VM(object):
|
|||||||
# for now send back the original request
|
# for now send back the original request
|
||||||
self.send_response(request)
|
self.send_response(request)
|
||||||
|
|
||||||
|
@IModule.route("dynamips.vm.save_config")
|
||||||
|
def vm_save_config(self, request):
|
||||||
|
"""
|
||||||
|
Save the configs for a VM (router).
|
||||||
|
|
||||||
|
Mandatory request parameters:
|
||||||
|
- id (vm identifier)
|
||||||
|
"""
|
||||||
|
|
||||||
|
if request == None:
|
||||||
|
self.send_param_error()
|
||||||
|
return
|
||||||
|
|
||||||
|
#TODO: JSON schema validation for the request
|
||||||
|
log.debug("received request {}".format(request))
|
||||||
|
router_id = request["id"]
|
||||||
|
router = self._routers[router_id]
|
||||||
|
try:
|
||||||
|
if router.startup_config:
|
||||||
|
#TODO: handle private-config
|
||||||
|
startup_config_base64, _ = router.extract_config()
|
||||||
|
if startup_config_base64:
|
||||||
|
try:
|
||||||
|
config = base64.decodestring(startup_config_base64.encode("utf-8")).decode("utf-8")
|
||||||
|
config = "!\n" + config.replace("\r", "")
|
||||||
|
config_path = os.path.join(router.hypervisor.working_dir, router.startup_config)
|
||||||
|
with open(config_path, "w") as f:
|
||||||
|
log.info("saving startup-config to {}".format(router.startup_config))
|
||||||
|
f.write(config)
|
||||||
|
except EnvironmentError as e:
|
||||||
|
raise DynamipsError("Could not save the configuration {}: {}".format(config_path, e))
|
||||||
|
except DynamipsError as e:
|
||||||
|
log.warn("could not save config to {}: {}".format(router.startup_config, e))
|
||||||
|
|
||||||
@IModule.route("dynamips.vm.idlepcs")
|
@IModule.route("dynamips.vm.idlepcs")
|
||||||
def vm_idlepcs(self, request):
|
def vm_idlepcs(self, request):
|
||||||
"""
|
"""
|
||||||
|
@ -33,6 +33,7 @@ class DynamipsHypervisor(object):
|
|||||||
"""
|
"""
|
||||||
Creates a new connection to a Dynamips server (also called hypervisor)
|
Creates a new connection to a Dynamips server (also called hypervisor)
|
||||||
|
|
||||||
|
:param working_dir: working directory
|
||||||
:param host: the hostname or ip address string of the Dynamips server
|
:param host: the hostname or ip address string of the Dynamips server
|
||||||
:param port: the tcp port integer (defaults to 7200)
|
:param port: the tcp port integer (defaults to 7200)
|
||||||
:param timeout: timeout integer for how long to wait for a response to commands sent to the
|
:param timeout: timeout integer for how long to wait for a response to commands sent to the
|
||||||
@ -43,7 +44,7 @@ class DynamipsHypervisor(object):
|
|||||||
error_re = re.compile(r"""^2[0-9]{2}-""")
|
error_re = re.compile(r"""^2[0-9]{2}-""")
|
||||||
success_re = re.compile(r"""^1[0-9]{2}\s{1}""")
|
success_re = re.compile(r"""^1[0-9]{2}\s{1}""")
|
||||||
|
|
||||||
def __init__(self, host, port=7200, timeout=30.0):
|
def __init__(self, working_dir, host, port=7200, timeout=30.0):
|
||||||
|
|
||||||
self._host = host
|
self._host = host
|
||||||
self._port = port
|
self._port = port
|
||||||
@ -51,7 +52,7 @@ class DynamipsHypervisor(object):
|
|||||||
self._devices = []
|
self._devices = []
|
||||||
self._ghosts = {}
|
self._ghosts = {}
|
||||||
self._jitsharing_groups = {}
|
self._jitsharing_groups = {}
|
||||||
self._working_dir = ""
|
self._working_dir = working_dir
|
||||||
self._baseconsole = 2000
|
self._baseconsole = 2000
|
||||||
self._baseaux = 2100
|
self._baseaux = 2100
|
||||||
self._baseudp = 10000
|
self._baseudp = 10000
|
||||||
@ -155,6 +156,7 @@ class DynamipsHypervisor(object):
|
|||||||
# encase working_dir in quotes to protect spaces in the path
|
# encase working_dir in quotes to protect spaces in the path
|
||||||
self.send("hypervisor working_dir {}".format('"' + working_dir + '"'))
|
self.send("hypervisor working_dir {}".format('"' + working_dir + '"'))
|
||||||
self._working_dir = working_dir
|
self._working_dir = working_dir
|
||||||
|
log.debug("working directory set to {}".format(self._working_dir))
|
||||||
|
|
||||||
def save_config(self, filename):
|
def save_config(self, filename):
|
||||||
"""
|
"""
|
||||||
@ -332,6 +334,40 @@ class DynamipsHypervisor(object):
|
|||||||
|
|
||||||
return self._port
|
return self._port
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def find_unused_port(start_port, end_port, host='127.0.0.1', socket_type="TCP"):
|
||||||
|
"""
|
||||||
|
Finds an unused port in a range.
|
||||||
|
|
||||||
|
:param start_port: first port in the range
|
||||||
|
:param end_port: last port in the range
|
||||||
|
:param host: host/address for bind()
|
||||||
|
:param socket_type: TCP (default) or UDP
|
||||||
|
"""
|
||||||
|
|
||||||
|
if socket_type == "UDP":
|
||||||
|
socket_type = socket.SOCK_DGRAM
|
||||||
|
else:
|
||||||
|
socket_type = socket.SOCK_STREAM
|
||||||
|
|
||||||
|
for port in range(start_port, end_port):
|
||||||
|
if port > end_port:
|
||||||
|
raise DynamipsError("Could not find a free port between {0} and {1}".format(start_port, end_port))
|
||||||
|
try:
|
||||||
|
if ":" in host:
|
||||||
|
# IPv6 address support
|
||||||
|
s = socket.socket(socket.AF_INET6, socket_type)
|
||||||
|
else:
|
||||||
|
s = socket.socket(socket.AF_INET, socket_type)
|
||||||
|
# the port is available if bind is a success
|
||||||
|
s.bind((host, port))
|
||||||
|
return port
|
||||||
|
except socket.error as e:
|
||||||
|
if e.errno == errno.EADDRINUSE: # socket already in use
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
raise DynamipsError("Could not find an unused port: {}".format(e))
|
||||||
|
|
||||||
def allocate_udp_port(self, max_port=100):
|
def allocate_udp_port(self, max_port=100):
|
||||||
"""
|
"""
|
||||||
Allocates a new UDP port for creating an UDP NIO.
|
Allocates a new UDP port for creating an UDP NIO.
|
||||||
@ -342,28 +378,14 @@ class DynamipsHypervisor(object):
|
|||||||
:returns: port number (integer)
|
:returns: port number (integer)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
#FIXME: better check for IPv6
|
|
||||||
start_port = self._current_udp_port
|
start_port = self._current_udp_port
|
||||||
end_port = start_port + max_port
|
end_port = start_port + max_port
|
||||||
for port in range(start_port, end_port):
|
allocated_port = DynamipsHypervisor.find_unused_port(start_port, end_port, self._host, socket_type="UDP")
|
||||||
if port > end_port:
|
if allocated_port - self._current_udp_port > 1:
|
||||||
raise DynamipsError("Could not find a free port between {0} and {1}".format(start_port, max_port))
|
self._current_udp_port += allocated_port - self._current_udp_port
|
||||||
try:
|
else:
|
||||||
if self.host.__contains__(':'):
|
self._current_udp_port += 1
|
||||||
# IPv6 address support
|
return allocated_port
|
||||||
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
|
|
||||||
return port
|
|
||||||
except socket.error as e:
|
|
||||||
if e.errno == errno.EADDRINUSE: # socket already in use
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
raise DynamipsError("UDP port allocation: {}".format(e))
|
|
||||||
|
|
||||||
def send_raw(self, string):
|
def send_raw(self, string):
|
||||||
"""
|
"""
|
||||||
|
@ -22,11 +22,12 @@ Represents a Dynamips hypervisor and starts/stops the associated Dynamips proces
|
|||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
import subprocess
|
import subprocess
|
||||||
import logging
|
|
||||||
|
|
||||||
from .dynamips_hypervisor import DynamipsHypervisor
|
from .dynamips_hypervisor import DynamipsHypervisor
|
||||||
|
from .dynamips_error import DynamipsError
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
import logging
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Hypervisor(DynamipsHypervisor):
|
class Hypervisor(DynamipsHypervisor):
|
||||||
@ -34,26 +35,25 @@ class Hypervisor(DynamipsHypervisor):
|
|||||||
Hypervisor.
|
Hypervisor.
|
||||||
|
|
||||||
:param path: path to Dynamips executable
|
:param path: path to Dynamips executable
|
||||||
:param workingdir: working directory
|
:param working_dir: working directory
|
||||||
:param port: port for this hypervisor
|
:param port: port for this hypervisor
|
||||||
:param host: host/address for this hypervisor
|
:param host: host/address for this hypervisor
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_instance_count = 0
|
_instance_count = 0
|
||||||
|
|
||||||
def __init__(self, path, workingdir, host, port):
|
def __init__(self, path, working_dir, host, port):
|
||||||
|
|
||||||
DynamipsHypervisor.__init__(self, host, port)
|
DynamipsHypervisor.__init__(self, working_dir, host, port)
|
||||||
|
|
||||||
# create an unique ID
|
# create an unique ID
|
||||||
self._id = Hypervisor._instance_count
|
self._id = Hypervisor._instance_count
|
||||||
Hypervisor._instance_count += 1
|
Hypervisor._instance_count += 1
|
||||||
|
|
||||||
self._path = path
|
self._path = path
|
||||||
self._workingdir = workingdir
|
|
||||||
self._command = []
|
self._command = []
|
||||||
self._process = None
|
self._process = None
|
||||||
self._stdout = None
|
self._stdout_file = ""
|
||||||
|
|
||||||
# settings used the load-balance hypervisors
|
# settings used the load-balance hypervisors
|
||||||
# (for the hypervisor manager)
|
# (for the hypervisor manager)
|
||||||
@ -130,26 +130,6 @@ class Hypervisor(DynamipsHypervisor):
|
|||||||
|
|
||||||
self._host = host
|
self._host = host
|
||||||
|
|
||||||
@property
|
|
||||||
def workingdir(self):
|
|
||||||
"""
|
|
||||||
Returns the working directory used to start the Dynamips hypervisor.
|
|
||||||
|
|
||||||
:returns: path to a working directory
|
|
||||||
"""
|
|
||||||
|
|
||||||
return(self._workingdir)
|
|
||||||
|
|
||||||
@workingdir.setter
|
|
||||||
def workingdir(self, workingdir):
|
|
||||||
"""
|
|
||||||
Sets the working directory used to start the Dynamips hypervisor.
|
|
||||||
|
|
||||||
:param workingdir: path to a working directory
|
|
||||||
"""
|
|
||||||
|
|
||||||
self._workingdir = workingdir
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def image_ref(self):
|
def image_ref(self):
|
||||||
"""
|
"""
|
||||||
@ -210,20 +190,18 @@ class Hypervisor(DynamipsHypervisor):
|
|||||||
|
|
||||||
self._command = self._build_command()
|
self._command = self._build_command()
|
||||||
try:
|
try:
|
||||||
logger.info("Starting Dynamips: {}".format(self._command))
|
log.info("starting Dynamips: {}".format(self._command))
|
||||||
# TODO: create unique filename for stdout
|
self._stdout_file = os.path.join(self._working_dir, "dynamips-{}.log".format(self._port))
|
||||||
self.stdout_file = os.path.join(self._workingdir, "dynamips.log")
|
log.info("logging to {}".format(self._stdout_file))
|
||||||
fd = open(self.stdout_file, "w")
|
with open(self._stdout_file, "w") as fd:
|
||||||
# TODO: check for exceptions and if process has already been started
|
self._process = subprocess.Popen(self._command,
|
||||||
self._process = subprocess.Popen(self._command,
|
stdout=fd,
|
||||||
stdout=fd,
|
stderr=subprocess.STDOUT,
|
||||||
stderr=subprocess.STDOUT,
|
cwd=self._working_dir)
|
||||||
cwd=self._workingdir)
|
log.info("Dynamips started PID={}".format(self._process.pid))
|
||||||
logger.info("Dynamips started PID={}".format(self._process.pid))
|
except EnvironmentError as e:
|
||||||
except OSError as e:
|
log.error("could not start Dynamips: {}".format(e))
|
||||||
logger.error("Could not start Dynamips: {}".format(e))
|
raise DynamipsError("could not start Dynamips: {}".format(e))
|
||||||
finally:
|
|
||||||
fd.close()
|
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
"""
|
"""
|
||||||
@ -232,7 +210,7 @@ class Hypervisor(DynamipsHypervisor):
|
|||||||
|
|
||||||
if self.is_running():
|
if self.is_running():
|
||||||
DynamipsHypervisor.stop(self)
|
DynamipsHypervisor.stop(self)
|
||||||
logger.info("Stopping Dynamips PID={}".format(self._process.pid))
|
log.info("stopping Dynamips PID={}".format(self._process.pid))
|
||||||
# give some time for the hypervisor to properly stop.
|
# give some time for the hypervisor to properly stop.
|
||||||
# time to delete UNIX NIOs for instance.
|
# time to delete UNIX NIOs for instance.
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
@ -245,9 +223,13 @@ class Hypervisor(DynamipsHypervisor):
|
|||||||
Only use when the process has been stopped or has crashed.
|
Only use when the process has been stopped or has crashed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# TODO: check for exceptions
|
output = ""
|
||||||
with open(self.stdout_file) as file:
|
if self._stdout_file:
|
||||||
output = file.read()
|
try:
|
||||||
|
with open(self._stdout_file) as file:
|
||||||
|
output = file.read()
|
||||||
|
except EnvironmentError as e:
|
||||||
|
log.warn("could not read {}: {}".format(self._stdout_file, e))
|
||||||
return output
|
return output
|
||||||
|
|
||||||
def is_running(self):
|
def is_running(self):
|
||||||
|
@ -19,8 +19,8 @@
|
|||||||
Manages Dynamips hypervisors (load-balancing etc.)
|
Manages Dynamips hypervisors (load-balancing etc.)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from .hypervisor import Hypervisor
|
from .hypervisor import Hypervisor
|
||||||
|
from .dynamips_error import DynamipsError
|
||||||
import socket
|
import socket
|
||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
@ -33,7 +33,7 @@ class HypervisorManager(object):
|
|||||||
Manages Dynamips hypervisors.
|
Manages Dynamips hypervisors.
|
||||||
|
|
||||||
:param path: path to the Dynamips executable
|
:param path: path to the Dynamips executable
|
||||||
:param workingdir: path to a working directory
|
:param working_dir: path to a working directory
|
||||||
:param host: host/address for hypervisors to listen to
|
:param host: host/address for hypervisors to listen to
|
||||||
:param base_port: base TCP port for hypervisors
|
:param base_port: base TCP port for hypervisors
|
||||||
:param base_console: base TCP port for consoles
|
:param base_console: base TCP port for consoles
|
||||||
@ -43,7 +43,7 @@ class HypervisorManager(object):
|
|||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
path,
|
path,
|
||||||
workingdir,
|
working_dir,
|
||||||
host='127.0.0.1',
|
host='127.0.0.1',
|
||||||
base_hypervisor_port=7200,
|
base_hypervisor_port=7200,
|
||||||
base_console_port=2000,
|
base_console_port=2000,
|
||||||
@ -52,7 +52,7 @@ class HypervisorManager(object):
|
|||||||
|
|
||||||
self._hypervisors = []
|
self._hypervisors = []
|
||||||
self._path = path
|
self._path = path
|
||||||
self._workingdir = workingdir
|
self._working_dir = working_dir
|
||||||
self._host = host
|
self._host = host
|
||||||
self._base_hypervisor_port = base_hypervisor_port
|
self._base_hypervisor_port = base_hypervisor_port
|
||||||
self._current_port = self._base_hypervisor_port
|
self._current_port = self._base_hypervisor_port
|
||||||
@ -108,25 +108,29 @@ class HypervisorManager(object):
|
|||||||
log.info("Dynamips path set to {}".format(self._path))
|
log.info("Dynamips path set to {}".format(self._path))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def workingdir(self):
|
def working_dir(self):
|
||||||
"""
|
"""
|
||||||
Returns the Dynamips working directory path.
|
Returns the Dynamips working directory path.
|
||||||
|
|
||||||
:returns: path to Dynamips working directory
|
:returns: path to Dynamips working directory
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self._workingdir
|
return self._working_dir
|
||||||
|
|
||||||
@workingdir.setter
|
@working_dir.setter
|
||||||
def workingdir(self, workingdir):
|
def working_dir(self, working_dir):
|
||||||
"""
|
"""
|
||||||
Sets a new path to the Dynamips working directory.
|
Sets a new path to the Dynamips working directory.
|
||||||
|
|
||||||
:param workingdir: path to Dynamips working directory
|
:param working_dir: path to Dynamips working directory
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self._workingdir = workingdir
|
self._working_dir = working_dir
|
||||||
log.info("working directory set to {}".format(self._workingdir))
|
log.info("working directory set to {}".format(self._working_dir))
|
||||||
|
|
||||||
|
# update all existing hypervisors with the new working directory
|
||||||
|
for hypervisor in self._hypervisors:
|
||||||
|
hypervisor.working_dir = working_dir
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def base_hypervisor_port(self):
|
def base_hypervisor_port(self):
|
||||||
@ -406,16 +410,12 @@ class HypervisorManager(object):
|
|||||||
# try to connect for 10 seconds
|
# try to connect for 10 seconds
|
||||||
while(time.time() - begin < 10.0):
|
while(time.time() - begin < 10.0):
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
sock = None
|
|
||||||
try:
|
try:
|
||||||
sock = socket.create_connection((host, port), timeout)
|
with socket.create_connection((host, port), timeout):
|
||||||
|
pass
|
||||||
except socket.error as e:
|
except socket.error as e:
|
||||||
last_exception = e
|
last_exception = e
|
||||||
#time.sleep(0.01)
|
|
||||||
continue
|
continue
|
||||||
finally:
|
|
||||||
if sock:
|
|
||||||
sock.close()
|
|
||||||
connection_success = True
|
connection_success = True
|
||||||
break
|
break
|
||||||
|
|
||||||
@ -426,6 +426,25 @@ class HypervisorManager(object):
|
|||||||
else:
|
else:
|
||||||
log.info("Dynamips server ready after {:.4f} seconds".format(time.time() - begin))
|
log.info("Dynamips server ready after {:.4f} seconds".format(time.time() - begin))
|
||||||
|
|
||||||
|
def allocate_tcp_port(self, max_port=100):
|
||||||
|
"""
|
||||||
|
Allocates a new TCP port for a Dynamips hypervisor.
|
||||||
|
|
||||||
|
:param max_port: maximum number of port to scan in
|
||||||
|
order to find one available for use.
|
||||||
|
|
||||||
|
:returns: port number (integer)
|
||||||
|
"""
|
||||||
|
|
||||||
|
start_port = self._current_port
|
||||||
|
end_port = start_port + max_port
|
||||||
|
allocated_port = Hypervisor.find_unused_port(start_port, end_port, self._host)
|
||||||
|
if allocated_port - self._current_port > 1:
|
||||||
|
self._current_port += allocated_port - self._current_port
|
||||||
|
else:
|
||||||
|
self._current_port += 1
|
||||||
|
return allocated_port
|
||||||
|
|
||||||
def start_new_hypervisor(self):
|
def start_new_hypervisor(self):
|
||||||
"""
|
"""
|
||||||
Creates a new Dynamips process and start it.
|
Creates a new Dynamips process and start it.
|
||||||
@ -433,15 +452,23 @@ class HypervisorManager(object):
|
|||||||
:returns: the new hypervisor instance
|
:returns: the new hypervisor instance
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
port = self.allocate_tcp_port()
|
||||||
|
# working_dir = os.path.join(self._working_dir, "instance-{}".format(port))
|
||||||
|
# if not os.path.exists(working_dir):
|
||||||
|
# try:
|
||||||
|
# os.makedirs(working_dir)
|
||||||
|
# except EnvironmentError as e:
|
||||||
|
# raise DynamipsError("{}".format(e))
|
||||||
|
|
||||||
hypervisor = Hypervisor(self._path,
|
hypervisor = Hypervisor(self._path,
|
||||||
self._workingdir,
|
self._working_dir,
|
||||||
self._host,
|
self._host,
|
||||||
self._current_port)
|
port)
|
||||||
|
|
||||||
log.info("creating new hypervisor {}:{}".format(hypervisor.host, hypervisor.port))
|
log.info("creating new hypervisor {}:{}".format(hypervisor.host, hypervisor.port))
|
||||||
hypervisor.start()
|
hypervisor.start()
|
||||||
|
|
||||||
self.wait_for_hypervisor(self._host, self._current_port)
|
self.wait_for_hypervisor(self._host, port)
|
||||||
log.info("hypervisor {}:{} has successfully started".format(hypervisor.host, hypervisor.port))
|
log.info("hypervisor {}:{} has successfully started".format(hypervisor.host, hypervisor.port))
|
||||||
|
|
||||||
hypervisor.connect()
|
hypervisor.connect()
|
||||||
@ -450,7 +477,6 @@ class HypervisorManager(object):
|
|||||||
hypervisor.baseudp = self._current_base_udp_port
|
hypervisor.baseudp = self._current_base_udp_port
|
||||||
self._current_base_udp_port += self._udp_incrementation_per_hypervisor
|
self._current_base_udp_port += self._udp_incrementation_per_hypervisor
|
||||||
self._hypervisors.append(hypervisor)
|
self._hypervisors.append(hypervisor)
|
||||||
self._current_port += 1
|
|
||||||
return hypervisor
|
return hypervisor
|
||||||
|
|
||||||
def allocate_hypervisor_for_router(self, router_ios_image, router_ram):
|
def allocate_hypervisor_for_router(self, router_ios_image, router_ram):
|
||||||
|
@ -20,7 +20,6 @@ Interface for Dynamips virtual ATM bridge module ("atm_bridge").
|
|||||||
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L622
|
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L622
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from ..dynamips_error import DynamipsError
|
from ..dynamips_error import DynamipsError
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,7 +20,6 @@ Interface for Dynamips virtual ATM switch module ("atmsw").
|
|||||||
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L593
|
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L593
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from ..dynamips_error import DynamipsError
|
from ..dynamips_error import DynamipsError
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
@ -20,8 +20,6 @@ Interface for Dynamips NIO bridge module ("nio_bridge").
|
|||||||
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L538
|
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L538
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
|
|
||||||
class Bridge(object):
|
class Bridge(object):
|
||||||
"""
|
"""
|
||||||
@ -36,6 +34,7 @@ class Bridge(object):
|
|||||||
def __init__(self, hypervisor, name):
|
def __init__(self, hypervisor, name):
|
||||||
|
|
||||||
self._hypervisor = hypervisor
|
self._hypervisor = hypervisor
|
||||||
|
self._allocated_names.append(name)
|
||||||
self._name = '"' + name + '"' # put name into quotes to protect spaces
|
self._name = '"' + name + '"' # put name into quotes to protect spaces
|
||||||
self._hypervisor.send("nio_bridge create {}".format(self._name))
|
self._hypervisor.send("nio_bridge create {}".format(self._name))
|
||||||
self._hypervisor.devices.append(self)
|
self._hypervisor.devices.append(self)
|
||||||
|
@ -20,7 +20,6 @@ Interface for Dynamips virtual Cisco 1700 instances module ("c1700")
|
|||||||
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L428
|
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L428
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from .router import Router
|
from .router import Router
|
||||||
from ..adapters.c1700_mb_1fe import C1700_MB_1FE
|
from ..adapters.c1700_mb_1fe import C1700_MB_1FE
|
||||||
from ..adapters.c1700_mb_wic1 import C1700_MB_WIC1
|
from ..adapters.c1700_mb_wic1 import C1700_MB_WIC1
|
||||||
|
@ -20,7 +20,6 @@ Interface for Dynamips virtual Cisco 2600 instances module ("c2600")
|
|||||||
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L404
|
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L404
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from .router import Router
|
from .router import Router
|
||||||
from ..adapters.c2600_mb_1e import C2600_MB_1E
|
from ..adapters.c2600_mb_1e import C2600_MB_1E
|
||||||
from ..adapters.c2600_mb_2e import C2600_MB_2E
|
from ..adapters.c2600_mb_2e import C2600_MB_2E
|
||||||
|
@ -20,7 +20,6 @@ Interface for Dynamips virtual Cisco 2691 instances module ("c2691")
|
|||||||
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L387
|
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L387
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from .router import Router
|
from .router import Router
|
||||||
from ..adapters.gt96100_fe import GT96100_FE
|
from ..adapters.gt96100_fe import GT96100_FE
|
||||||
|
|
||||||
|
@ -20,7 +20,6 @@ Interface for Dynamips virtual Cisco 3600 instances module ("c3600")
|
|||||||
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L366
|
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L366
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from .router import Router
|
from .router import Router
|
||||||
from ..adapters.leopard_2fe import Leopard_2FE
|
from ..adapters.leopard_2fe import Leopard_2FE
|
||||||
|
|
||||||
|
@ -20,7 +20,6 @@ Interface for Dynamips virtual Cisco 3725 instances module ("c3725")
|
|||||||
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L346
|
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L346
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from .router import Router
|
from .router import Router
|
||||||
from ..adapters.gt96100_fe import GT96100_FE
|
from ..adapters.gt96100_fe import GT96100_FE
|
||||||
|
|
||||||
|
@ -20,7 +20,6 @@ Interface for Dynamips virtual Cisco 3745 instances module ("c3745")
|
|||||||
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L326
|
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L326
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from .router import Router
|
from .router import Router
|
||||||
from ..adapters.gt96100_fe import GT96100_FE
|
from ..adapters.gt96100_fe import GT96100_FE
|
||||||
|
|
||||||
|
@ -20,7 +20,6 @@ Interface for Dynamips virtual Cisco 7200 instances module ("c7200")
|
|||||||
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L294
|
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L294
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from ..dynamips_error import DynamipsError
|
from ..dynamips_error import DynamipsError
|
||||||
from .router import Router
|
from .router import Router
|
||||||
from ..adapters.c7200_io_2fe import C7200_IO_2FE
|
from ..adapters.c7200_io_2fe import C7200_IO_2FE
|
||||||
|
@ -20,8 +20,6 @@ Interface for Dynamips virtual Ethernet switch module ("ethsw").
|
|||||||
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L558
|
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L558
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from ..dynamips_error import DynamipsError
|
from ..dynamips_error import DynamipsError
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
@ -20,7 +20,6 @@ Interface for Dynamips virtual Frame-Relay switch module.
|
|||||||
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L642
|
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L642
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from ..dynamips_error import DynamipsError
|
from ..dynamips_error import DynamipsError
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
Hub object that uses the Bridge interface to create a hub with ports.
|
Hub object that uses the Bridge interface to create a hub with ports.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from .bridge import Bridge
|
from .bridge import Bridge
|
||||||
from ..dynamips_error import DynamipsError
|
from ..dynamips_error import DynamipsError
|
||||||
|
|
||||||
@ -53,7 +52,6 @@ class Hub(Bridge):
|
|||||||
break
|
break
|
||||||
name_id += 1
|
name_id += 1
|
||||||
|
|
||||||
self._allocated_names.append(name)
|
|
||||||
self._mapping = {}
|
self._mapping = {}
|
||||||
Bridge.__init__(self, hypervisor, name)
|
Bridge.__init__(self, hypervisor, name)
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ Interface for Dynamips virtual Machine module ("vm")
|
|||||||
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L77
|
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L77
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from ..dynamips_hypervisor import DynamipsHypervisor
|
||||||
from ..dynamips_error import DynamipsError
|
from ..dynamips_error import DynamipsError
|
||||||
import time
|
import time
|
||||||
import sys
|
import sys
|
||||||
@ -68,6 +68,8 @@ class Router(object):
|
|||||||
self._name = '"' + name + '"' # put name into quotes to protect spaces
|
self._name = '"' + name + '"' # put name into quotes to protect spaces
|
||||||
self._platform = platform
|
self._platform = platform
|
||||||
self._image = ""
|
self._image = ""
|
||||||
|
self._startup_config = ""
|
||||||
|
self._private_config = ""
|
||||||
self._ram = 128 # Megabytes
|
self._ram = 128 # Megabytes
|
||||||
self._nvram = 128 # Kilobytes
|
self._nvram = 128 # Kilobytes
|
||||||
self._mmap = True
|
self._mmap = True
|
||||||
@ -100,8 +102,12 @@ class Router(object):
|
|||||||
log.info("router {platform} {name} [id={id}] has been created".format(name=self._name,
|
log.info("router {platform} {name} [id={id}] has been created".format(name=self._name,
|
||||||
platform=platform,
|
platform=platform,
|
||||||
id=self._id))
|
id=self._id))
|
||||||
self.console = self._hypervisor.baseconsole + self._id
|
|
||||||
self.aux = self._hypervisor.baseaux + self._id
|
# allocate and check that console and aux ports are unused
|
||||||
|
console_port = (self._hypervisor.baseconsole - 1) + self._id
|
||||||
|
self.console = DynamipsHypervisor.find_unused_port(console_port, console_port + 1, self._hypervisor.host)
|
||||||
|
aux_port = (self._hypervisor.baseaux - 1) + self._id
|
||||||
|
self.aux = DynamipsHypervisor.find_unused_port(aux_port, aux_port + 1, self._hypervisor.host)
|
||||||
|
|
||||||
# get the default base MAC address
|
# get the default base MAC address
|
||||||
self._mac_addr = self._hypervisor.send("{platform} get_mac_addr {name}".format(platform=self._platform,
|
self._mac_addr = self._hypervisor.send("{platform} get_mac_addr {name}".format(platform=self._platform,
|
||||||
@ -130,6 +136,8 @@ class Router(object):
|
|||||||
|
|
||||||
router_defaults = {"platform": self._platform,
|
router_defaults = {"platform": self._platform,
|
||||||
"image": self._image,
|
"image": self._image,
|
||||||
|
"startup_config": self._startup_config,
|
||||||
|
"private_config": self._private_config,
|
||||||
"ram": self._ram,
|
"ram": self._ram,
|
||||||
"nvram": self._nvram,
|
"nvram": self._nvram,
|
||||||
"mmap": self._mmap,
|
"mmap": self._mmap,
|
||||||
@ -249,7 +257,7 @@ class Router(object):
|
|||||||
Deletes this router.
|
Deletes this router.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self._hypervisor.send("vm delete {}".format(self._name))
|
self._hypervisor.send("vm clean_delete {}".format(self._name))
|
||||||
self._hypervisor.devices.remove(self)
|
self._hypervisor.devices.remove(self)
|
||||||
|
|
||||||
log.info("router {name} [id={id}] has been deleted".format(name=self._name, id=self._id))
|
log.info("router {name} [id={id}] has been deleted".format(name=self._name, id=self._id))
|
||||||
@ -384,6 +392,46 @@ class Router(object):
|
|||||||
|
|
||||||
self._image = image
|
self._image = image
|
||||||
|
|
||||||
|
@property
|
||||||
|
def startup_config(self):
|
||||||
|
"""
|
||||||
|
Returns the startup-config for this router.
|
||||||
|
|
||||||
|
:returns: path to startup-config file
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self._startup_config
|
||||||
|
|
||||||
|
@startup_config.setter
|
||||||
|
def startup_config(self, startup_config):
|
||||||
|
"""
|
||||||
|
Sets the startup-config for this router.
|
||||||
|
|
||||||
|
:param startup_config: path to startup-config file
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._startup_config = startup_config
|
||||||
|
|
||||||
|
@property
|
||||||
|
def private_config(self):
|
||||||
|
"""
|
||||||
|
Returns the private-config for this router.
|
||||||
|
|
||||||
|
:returns: path to private-config file
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self._private_config
|
||||||
|
|
||||||
|
@private_config.setter
|
||||||
|
def private_config(self, private_config):
|
||||||
|
"""
|
||||||
|
Sets the private-config for this router.
|
||||||
|
|
||||||
|
:param private_config: path to private-config file
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._private_config = private_config
|
||||||
|
|
||||||
def set_config(self, startup_config, private_config=''):
|
def set_config(self, startup_config, private_config=''):
|
||||||
"""
|
"""
|
||||||
Sets the config files that are pushed to startup-config and
|
Sets the config files that are pushed to startup-config and
|
||||||
@ -394,18 +442,20 @@ class Router(object):
|
|||||||
(keep existing data when if an empty string)
|
(keep existing data when if an empty string)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self._hypervisor.send("vm set_config {name} {startup} {private}".format(name=self._name,
|
if self._startup_config != startup_config or self._private_config != private_config:
|
||||||
startup='"' + startup_config + '"',
|
|
||||||
private='"' + private_config + '"'))
|
|
||||||
|
|
||||||
log.info("router {name} [id={id}]: has a startup-config set: {startup}".format(name=self._name,
|
self._hypervisor.send("vm set_config {name} {startup} {private}".format(name=self._name,
|
||||||
id=self._id,
|
startup='"' + startup_config + '"',
|
||||||
startup='"' + startup_config + '"'))
|
private='"' + private_config + '"'))
|
||||||
|
|
||||||
if private_config:
|
log.info("router {name} [id={id}]: has a startup-config set: {startup}".format(name=self._name,
|
||||||
log.info("router {name} [id={id}]: has a private-config set: {private}".format(name=self._name,
|
|
||||||
id=self._id,
|
id=self._id,
|
||||||
private='"' + private_config + '"'))
|
startup='"' + startup_config + '"'))
|
||||||
|
|
||||||
|
if private_config:
|
||||||
|
log.info("router {name} [id={id}]: has a private-config set: {private}".format(name=self._name,
|
||||||
|
id=self._id,
|
||||||
|
private='"' + private_config + '"'))
|
||||||
|
|
||||||
def extract_config(self):
|
def extract_config(self):
|
||||||
"""
|
"""
|
||||||
|
@ -49,9 +49,16 @@ class Server(object):
|
|||||||
self._host = host
|
self._host = host
|
||||||
self._port = port
|
self._port = port
|
||||||
if ipc:
|
if ipc:
|
||||||
self._zmq_port = 0 # this forces module to use IPC for communications
|
self._zmq_port = 0 # this forces to use IPC for communications with the ZeroMQ server
|
||||||
else:
|
else:
|
||||||
self._zmq_port = port + 1 # this server port + 1
|
try:
|
||||||
|
# let the OS find an unused port for the ZeroMQ server
|
||||||
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||||
|
sock.bind(('127.0.0.1', 0))
|
||||||
|
self._zmq_port = sock.getsockname()[1]
|
||||||
|
except socket.error as e:
|
||||||
|
log.warn("could not pick up a random port for the ZeroMQ server: {}".format(e))
|
||||||
|
self._zmq_port = port + 1 # let's try this server port + 1
|
||||||
self._ipc = ipc
|
self._ipc = ipc
|
||||||
self._modules = []
|
self._modules = []
|
||||||
|
|
||||||
|
Binary file not shown.
@ -18,9 +18,9 @@ def test_host(hypervisor):
|
|||||||
assert hypervisor.host == "127.0.0.1"
|
assert hypervisor.host == "127.0.0.1"
|
||||||
|
|
||||||
|
|
||||||
def test_workingdir(hypervisor):
|
def test_working_dir(hypervisor):
|
||||||
|
|
||||||
assert hypervisor.workingdir == "/tmp"
|
assert hypervisor.working_dir == "/tmp"
|
||||||
|
|
||||||
|
|
||||||
def test_path(hypervisor):
|
def test_path(hypervisor):
|
||||||
|
Loading…
Reference in New Issue
Block a user