mirror of
https://github.com/GNS3/gns3-gui.git
synced 2026-05-17 00:46:01 +03:00
Server working directory management.
Improvements how to handle status for ports & nodes. Fix issues with project/topology management. Push startup-config to servers using base64. Remote servers allocation.
This commit is contained in:
20
gns3/baseconfig.txt
Normal file
20
gns3/baseconfig.txt
Normal file
@@ -0,0 +1,20 @@
|
||||
!
|
||||
hostname %h
|
||||
!
|
||||
no ip domain lookup
|
||||
no ip icmp rate-limit unreachable
|
||||
ip tcp synwait 5
|
||||
!
|
||||
line con 0
|
||||
exec-timeout 0 0
|
||||
logging synchronous
|
||||
privilege level 15
|
||||
no login
|
||||
line aux 0
|
||||
exec-timeout 0 0
|
||||
logging synchronous
|
||||
privilege level 15
|
||||
no login
|
||||
!
|
||||
!
|
||||
end
|
||||
@@ -94,6 +94,14 @@ class GraphicsView(QtGui.QGraphicsView):
|
||||
Port.reset()
|
||||
self._topology.reset()
|
||||
|
||||
def setLocalBaseWorkingDirtoAllModules(self, path):
|
||||
|
||||
try:
|
||||
dynamips = Dynamips.instance()
|
||||
dynamips.setLocalBaseWorkingDir(path)
|
||||
except ModuleError as e:
|
||||
QtGui.QMessageBox.critical(self, "Local working directory", "{}".format(e))
|
||||
|
||||
def _loadSettings(self):
|
||||
"""
|
||||
Loads the settings from the persistent settings file.
|
||||
|
||||
@@ -21,6 +21,7 @@ Graphical representation of an Ethernet link for QGraphicsScene.
|
||||
|
||||
from ..qt import QtCore, QtGui
|
||||
from .link_item import LinkItem
|
||||
from ..ports.port import Port
|
||||
|
||||
|
||||
class EthernetLinkItem(LinkItem):
|
||||
@@ -107,10 +108,10 @@ class EthernetLinkItem(LinkItem):
|
||||
if self.length < 100:
|
||||
return
|
||||
|
||||
if self._source_port.status() == 1:
|
||||
if self._source_port.status() == Port.started:
|
||||
# port is active
|
||||
color = QtCore.Qt.green
|
||||
elif self._source_port.status() == 2:
|
||||
elif self._source_port.status() == Port.suspended:
|
||||
# port is suspended
|
||||
color = QtCore.Qt.yellow
|
||||
else:
|
||||
@@ -163,10 +164,10 @@ class EthernetLinkItem(LinkItem):
|
||||
|
||||
painter.drawPoint(point1)
|
||||
|
||||
if self._destination_port.status() == 1:
|
||||
if self._destination_port.status() == Port.started:
|
||||
# port is active
|
||||
color = QtCore.Qt.green
|
||||
elif self._destination_port.status() == 2:
|
||||
elif self._destination_port.status() == Port.suspended:
|
||||
# port is suspended
|
||||
color = QtCore.Qt.yellow
|
||||
else:
|
||||
|
||||
@@ -134,10 +134,6 @@ class NodeItem(QtSvg.QGraphicsSvgItem):
|
||||
when a the node has started.
|
||||
"""
|
||||
|
||||
ports = self._node.ports()
|
||||
for port in ports:
|
||||
# set ports as started
|
||||
port.setStatus(1)
|
||||
for link in self._links:
|
||||
link.update()
|
||||
|
||||
@@ -147,10 +143,6 @@ class NodeItem(QtSvg.QGraphicsSvgItem):
|
||||
when a the node has stopped.
|
||||
"""
|
||||
|
||||
ports = self._node.ports()
|
||||
for port in ports:
|
||||
# set ports as stopped
|
||||
port.setStatus(0)
|
||||
for link in self._links:
|
||||
link.update()
|
||||
|
||||
@@ -160,10 +152,6 @@ class NodeItem(QtSvg.QGraphicsSvgItem):
|
||||
when a the node has suspended.
|
||||
"""
|
||||
|
||||
ports = self._node.ports()
|
||||
for port in ports:
|
||||
# set ports as suspended
|
||||
port.setStatus(2)
|
||||
for link in self._links:
|
||||
link.update()
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ Graphical representation of a Serial link on the QGraphicsScene.
|
||||
import math
|
||||
from ..qt import QtCore, QtGui
|
||||
from .link_item import LinkItem
|
||||
from ..ports.port import Port
|
||||
|
||||
|
||||
class SerialLinkItem(LinkItem):
|
||||
@@ -108,10 +109,10 @@ class SerialLinkItem(LinkItem):
|
||||
return
|
||||
|
||||
# source point color
|
||||
if self._source_port.status() == 1:
|
||||
if self._source_port.status() == Port.started:
|
||||
# port is active
|
||||
color = QtCore.Qt.green
|
||||
elif self._source_port.status() == 2:
|
||||
elif self._source_port.status() == Port.suspended:
|
||||
# port is suspended
|
||||
color = QtCore.Qt.yellow
|
||||
else:
|
||||
@@ -154,10 +155,10 @@ class SerialLinkItem(LinkItem):
|
||||
#painter.drawPoint(self.source)
|
||||
|
||||
# destination point color
|
||||
if self._destination_port.status() == 1:
|
||||
if self._destination_port.status() == Port.started:
|
||||
# port is active
|
||||
color = QtCore.Qt.green
|
||||
elif self._destination_port.status() == 2:
|
||||
elif self._destination_port.status() == Port.suspended:
|
||||
# port is suspended
|
||||
color = QtCore.Qt.yellow
|
||||
else:
|
||||
|
||||
@@ -24,14 +24,16 @@ import tempfile
|
||||
import socket
|
||||
import shutil
|
||||
import json
|
||||
from .servers import Servers
|
||||
from .qt import QtGui, QtCore
|
||||
from .servers import Servers
|
||||
from .node import Node
|
||||
from .ui.main_window_ui import Ui_MainWindow
|
||||
from .preferences_dialog import PreferencesDialog
|
||||
from .settings import GENERAL_SETTINGS, GENERAL_SETTING_TYPES
|
||||
from .utils.progress_dialog import ProgressDialog
|
||||
from .utils.process_files_thread import ProcessFilesThread
|
||||
from .utils.wait_for_connection_thread import WaitForConnectionThread
|
||||
from .utils.message_box import MessageBox
|
||||
from .items.node_item import NodeItem
|
||||
from .topology import Topology
|
||||
|
||||
@@ -727,7 +729,17 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
Saves a project to another location/name.
|
||||
"""
|
||||
|
||||
#TODO: check if any node are running, cancel if any!
|
||||
# first check if any node that can be started is running
|
||||
topology = Topology.instance()
|
||||
running_nodes = []
|
||||
for node in topology.nodes():
|
||||
if hasattr(node, "start") and node.status() == Node.started:
|
||||
running_nodes.append(node.name())
|
||||
|
||||
if running_nodes:
|
||||
nodes = "\n".join(running_nodes)
|
||||
MessageBox(self, "Save project", "Please stop the following nodes before saving the topology", nodes)
|
||||
return
|
||||
|
||||
if self._temporary_project:
|
||||
destination_file = os.path.join(self._settings["projects_path"], "untitled.net")
|
||||
@@ -759,6 +771,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
return False
|
||||
|
||||
self._deleteTemporaryProject()
|
||||
self._project_files_dir = new_project_files_dir
|
||||
self.uiGraphicsView.setLocalBaseWorkingDirtoAllModules(self._project_files_dir)
|
||||
return self._saveProject(path)
|
||||
|
||||
def _saveProject(self, path):
|
||||
@@ -790,6 +804,17 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
"""
|
||||
|
||||
self.uiGraphicsView.reset()
|
||||
|
||||
project_files_dir = path
|
||||
if path.endswith(".net"):
|
||||
project_files_dir = path[:-4]
|
||||
self._project_files_dir = project_files_dir + "-files"
|
||||
|
||||
if not os.path.exists(self._project_files_dir):
|
||||
QtGui.QMessageBox.warning(self, "Load", "Project files directory doesn't exist: {}".format(self._project_files_dir))
|
||||
|
||||
self.uiGraphicsView.setLocalBaseWorkingDirtoAllModules(self._project_files_dir)
|
||||
|
||||
topology = Topology.instance()
|
||||
try:
|
||||
with open(path, "r") as f:
|
||||
@@ -804,15 +829,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
|
||||
self.uiStatusBar.showMessage("Project loaded {}".format(path), 2000)
|
||||
self._project_path = path
|
||||
|
||||
project_files_dir = path
|
||||
if path.endswith(".net"):
|
||||
project_files_dir = path[:-4]
|
||||
self._project_files_dir = project_files_dir + "-files"
|
||||
|
||||
if not os.path.exists(self._project_files_dir):
|
||||
QtGui.QMessageBox.warning(self, "Load", "Project files directory doesn't exist: {}".format(self._project_files_dir))
|
||||
|
||||
self._setCurrentFile(path)
|
||||
|
||||
def _deleteTemporaryProject(self):
|
||||
@@ -850,6 +866,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
except EnvironmentError as e:
|
||||
QtGui.QMessageBox.critical(self, "Save", "Could not create project: {}".format(e))
|
||||
|
||||
self.uiGraphicsView.setLocalBaseWorkingDirtoAllModules(self._project_files_dir)
|
||||
self._setCurrentFile()
|
||||
|
||||
def _setCurrentFile(self, path=None):
|
||||
|
||||
@@ -20,6 +20,7 @@ Dynamips module implementation.
|
||||
"""
|
||||
|
||||
import socket
|
||||
import os
|
||||
from gns3.qt import QtCore
|
||||
from gns3.servers import Servers
|
||||
from ..module import Module
|
||||
@@ -54,6 +55,7 @@ class Dynamips(Module):
|
||||
self._settings = {}
|
||||
self._ios_images = {}
|
||||
self._servers = []
|
||||
self._working_dir = ""
|
||||
|
||||
# load the settings and IOS images.
|
||||
self._loadSettings()
|
||||
@@ -98,7 +100,7 @@ class Dynamips(Module):
|
||||
settings.setArrayIndex(index)
|
||||
path = settings.value("path", "")
|
||||
image = settings.value("image", "")
|
||||
startup_config = settings.value("image", "")
|
||||
startup_config = settings.value("startup_config", "")
|
||||
platform = settings.value("platform", "")
|
||||
chassis = settings.value("chassis", "")
|
||||
idlepc = settings.value("idlepc", "")
|
||||
@@ -108,7 +110,7 @@ class Dynamips(Module):
|
||||
key = "{server}:{image}".format(server=server, image=image)
|
||||
self._ios_images[key] = {"path": path,
|
||||
"image": image,
|
||||
"startup-config": startup_config,
|
||||
"startup_config": startup_config,
|
||||
"platform": platform,
|
||||
"chassis": chassis,
|
||||
"idlepc": idlepc,
|
||||
@@ -139,6 +141,26 @@ class Dynamips(Module):
|
||||
settings.endArray()
|
||||
settings.endGroup()
|
||||
|
||||
def setLocalBaseWorkingDir(self, path):
|
||||
"""
|
||||
Sets the local base working directory for this module.
|
||||
|
||||
:param path: path to the local working directory
|
||||
"""
|
||||
|
||||
self._working_dir = os.path.join(path, "dynamips")
|
||||
if not os.path.exists(self._working_dir):
|
||||
try:
|
||||
os.makedirs(self._working_dir)
|
||||
except EnvironmentError as e:
|
||||
raise ModuleError("{}".format(e))
|
||||
|
||||
log.info("local working directory for Dynamips module: {}".format(self._working_dir))
|
||||
servers = Servers.instance()
|
||||
server = servers.localServer()
|
||||
if server.connected():
|
||||
self._sendSettings(server)
|
||||
|
||||
def addServer(self, server):
|
||||
"""
|
||||
Adds a server to be used by this module.
|
||||
@@ -224,20 +246,34 @@ class Dynamips(Module):
|
||||
"""
|
||||
|
||||
log.info("sending Dynamips settings to server {}:{}".format(server.host, server.port))
|
||||
server.send_notification("dynamips.settings", self._settings)
|
||||
params = self._settings.copy()
|
||||
# send the local working directory only if this is a local server
|
||||
servers = Servers.instance()
|
||||
if server == servers.localServer():
|
||||
params.update({"working_dir": self._working_dir})
|
||||
server.send_notification("dynamips.settings", params)
|
||||
|
||||
def createNode(self, node_class):
|
||||
def createNode(self, node_class, server=None):
|
||||
"""
|
||||
Creates a new node.
|
||||
|
||||
:param node_class: Node object
|
||||
:param server: optional WebSocketClient instance
|
||||
"""
|
||||
|
||||
log.info("creating node {}".format(node_class))
|
||||
|
||||
# allocate a server for the node
|
||||
# allocate a server for the node if none is given
|
||||
servers = Servers.instance()
|
||||
server = servers.localServer()
|
||||
if self._settings["use_local_server"] and not server:
|
||||
# use the local server
|
||||
server = servers.localServer()
|
||||
elif not server:
|
||||
# pick up a remote server (round-robin method)
|
||||
server = next(iter(servers))
|
||||
if not server:
|
||||
raise ModuleError("No remote server is configured")
|
||||
|
||||
if not server.connected():
|
||||
try:
|
||||
log.info("reconnecting to server {}:{}".format(server.host, server.port))
|
||||
@@ -283,6 +319,8 @@ class Dynamips(Module):
|
||||
# set initial settings like an idle-pc value
|
||||
if ios_image["idlepc"]:
|
||||
settings["idlepc"] = ios_image["idlepc"]
|
||||
if ios_image["startup_config"]:
|
||||
settings["startup_config"] = ios_image["startup_config"]
|
||||
node.setup(ios_image["path"], ios_image["ram"], initial_settings=settings)
|
||||
else:
|
||||
node.setup()
|
||||
@@ -317,13 +355,11 @@ class Dynamips(Module):
|
||||
Returns the object with the corresponding name.
|
||||
|
||||
:param name: object name
|
||||
|
||||
:returns: object or None
|
||||
"""
|
||||
|
||||
if name in globals():
|
||||
return globals()[name]
|
||||
return None
|
||||
raise ModuleError("Dynamips module could not find object {}".format(name))
|
||||
|
||||
@staticmethod
|
||||
def nodes():
|
||||
|
||||
@@ -39,6 +39,7 @@ class ATMSwitch(Node):
|
||||
Node.__init__(self, server)
|
||||
|
||||
log.info("ATM switch is being created")
|
||||
self.setStatus(Node.started) # this is an always-on node
|
||||
self._atmsw_id = None
|
||||
self._ports = []
|
||||
self._module = module
|
||||
@@ -137,6 +138,7 @@ class ATMSwitch(Node):
|
||||
for port_name in ports_to_create:
|
||||
port = SerialPort(port_name)
|
||||
port.setPortNumber(int(port_name))
|
||||
port.setStatus(SerialPort.started)
|
||||
self._ports.append(port)
|
||||
updated = True
|
||||
log.debug("port {} has been added".format(port_name))
|
||||
@@ -296,6 +298,7 @@ class ATMSwitch(Node):
|
||||
for topology_port in ports:
|
||||
port = SerialPort(topology_port["name"])
|
||||
port.setPortNumber(topology_port["port_number"])
|
||||
port.setStatus(SerialPort.started)
|
||||
self._ports.append(port)
|
||||
self._settings["ports"].append(port.portNumber())
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ class EthernetHub(Node):
|
||||
Node.__init__(self, server)
|
||||
|
||||
log.info("Ethernet hub is being created")
|
||||
self.setStatus(Node.started) # this is an always-on node
|
||||
self._ethhub_id = None
|
||||
self._module = module
|
||||
self._ports = []
|
||||
@@ -132,6 +133,7 @@ class EthernetHub(Node):
|
||||
for port_name in ports_to_create:
|
||||
port = EthernetPort(port_name)
|
||||
port.setPortNumber(int(port_name))
|
||||
port.setStatus(EthernetPort.started)
|
||||
self._ports.append(port)
|
||||
updated = True
|
||||
log.debug("port {} has been added".format(port_name))
|
||||
@@ -278,6 +280,7 @@ class EthernetHub(Node):
|
||||
for topology_port in ports:
|
||||
port = EthernetPort(topology_port["name"])
|
||||
port.setPortNumber(topology_port["port_number"])
|
||||
port.setStatus(EthernetPort.started)
|
||||
self._ports.append(port)
|
||||
self._settings["ports"].append(port.portNumber())
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ class EthernetSwitch(Node):
|
||||
Node.__init__(self, server)
|
||||
|
||||
log.info("Ethernet switch is being created")
|
||||
self.setStatus(Node.started) # this is an always-on node
|
||||
self._ethsw_id = None
|
||||
self._module = module
|
||||
self._ports = []
|
||||
@@ -126,6 +127,7 @@ class EthernetSwitch(Node):
|
||||
continue
|
||||
port = EthernetPort(str(port_number))
|
||||
port.setPortNumber(port_number)
|
||||
port.setStatus(EthernetPort.started)
|
||||
self._ports.append(port)
|
||||
updated = True
|
||||
log.debug("port {} has been added".format(port_number))
|
||||
@@ -320,6 +322,7 @@ class EthernetSwitch(Node):
|
||||
port = EthernetPort(topology_port["name"])
|
||||
port.setPortNumber(topology_port["port_number"])
|
||||
port.setId(topology_port["id"])
|
||||
port.setStatus(EthernetPort.started)
|
||||
self._ports.append(port)
|
||||
self._settings["ports"][port.portNumber()] = {"type": topology_port["type"],
|
||||
"vlan": topology_port["vlan"]}
|
||||
|
||||
@@ -39,6 +39,7 @@ class FrameRelaySwitch(Node):
|
||||
Node.__init__(self, server)
|
||||
|
||||
log.info("Frame-Relay switch is being created")
|
||||
self.setStatus(Node.started) # this is an always-on node
|
||||
self._frsw_id = None
|
||||
self._ports = []
|
||||
self._module = module
|
||||
@@ -138,6 +139,7 @@ class FrameRelaySwitch(Node):
|
||||
for port_name in ports_to_create:
|
||||
port = SerialPort(port_name)
|
||||
port.setPortNumber(int(port_name))
|
||||
port.setStatus(SerialPort.started)
|
||||
self._ports.append(port)
|
||||
updated = True
|
||||
log.debug("port {} has been added".format(port_name))
|
||||
@@ -298,6 +300,7 @@ class FrameRelaySwitch(Node):
|
||||
port = SerialPort(topology_port["name"])
|
||||
port.setPortNumber(topology_port["port_number"])
|
||||
port.setId(topology_port["id"])
|
||||
port.setStatus(SerialPort.started)
|
||||
self._ports.append(port)
|
||||
log.info("Frame-Relay switch {} is loading".format(name))
|
||||
self.setup(name)
|
||||
|
||||
@@ -20,7 +20,11 @@ Base class for Dynamips router implementations on the client side.
|
||||
Asynchronously sends JSON messages to the GNS3 server and receives responses with callbacks.
|
||||
"""
|
||||
|
||||
import os
|
||||
import base64
|
||||
from gns3.node import Node
|
||||
from gns3.ports.port import Port
|
||||
|
||||
from ..settings import PLATFORMS_DEFAULT_RAM
|
||||
from ..adapters import ADAPTER_MATRIX
|
||||
from ..wics import WIC_MATRIX
|
||||
@@ -52,6 +56,8 @@ class Router(Node):
|
||||
self._settings = {"name": "",
|
||||
"platform": platform,
|
||||
"image": "",
|
||||
"startup_config": "",
|
||||
"private_config": "",
|
||||
"ram": 128,
|
||||
"nvram": 128,
|
||||
"mmap": True,
|
||||
@@ -284,6 +290,26 @@ class Router(Node):
|
||||
log.debug("router {} has been created".format(self.name()))
|
||||
self.created_signal.emit(self.id())
|
||||
|
||||
def _base64Config(self, config_path):
|
||||
"""
|
||||
Get the base64 encoded config from a file.
|
||||
|
||||
:param config_path: path to the configuration file.
|
||||
|
||||
: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', "")
|
||||
encoded = ("").join(base64.encodestring(config.encode("utf-8")).decode("utf-8").split())
|
||||
return encoded
|
||||
except EnvironmentError as e:
|
||||
log.warn("could not base64 encode {}: {}".format(config_path, e))
|
||||
return ""
|
||||
|
||||
def update(self, new_settings):
|
||||
"""
|
||||
Updates the settings for this router.
|
||||
@@ -296,6 +322,10 @@ class Router(Node):
|
||||
if name in self._settings and self._settings[name] != value:
|
||||
params[name] = value
|
||||
|
||||
if "startup_config" in new_settings and self._settings["startup_config"] != new_settings["startup_config"] \
|
||||
and os.path.exists(new_settings["startup_config"]):
|
||||
params["startup_config_base64"] = self._base64Config(new_settings["startup_config"])
|
||||
|
||||
log.debug("{} is updating settings: {}".format(self.name(), params))
|
||||
self._server.send_message("dynamips.vm.update", params, self._updateCallback)
|
||||
|
||||
@@ -370,6 +400,10 @@ class Router(Node):
|
||||
self.error_signal.emit(self.name(), result["code"], result["message"])
|
||||
else:
|
||||
log.info("{} has started".format(self.name()))
|
||||
self.setStatus(Node.started)
|
||||
for port in self._ports:
|
||||
# set ports as started
|
||||
port.setStatus(Port.started)
|
||||
self.started_signal.emit()
|
||||
|
||||
def stop(self):
|
||||
@@ -393,6 +427,10 @@ class Router(Node):
|
||||
self.error_signal.emit(self.name(), result["code"], result["message"])
|
||||
else:
|
||||
log.info("{} has stopped".format(self.name()))
|
||||
self.setStatus(Node.stopped)
|
||||
for port in self._ports:
|
||||
# set ports as stopped
|
||||
port.setStatus(Port.stopped)
|
||||
self.stopped_signal.emit()
|
||||
|
||||
def suspend(self):
|
||||
@@ -416,6 +454,10 @@ class Router(Node):
|
||||
self.error_signal.emit(self.name(), result["code"], result["message"])
|
||||
else:
|
||||
log.info("{} has suspended".format(self.name()))
|
||||
self.setStatus(Node.suspended)
|
||||
for port in self._ports:
|
||||
# set ports as suspended
|
||||
port.setStatus(Port.suspended)
|
||||
self.suspended_signal.emit()
|
||||
|
||||
def reload(self):
|
||||
@@ -587,6 +629,15 @@ class Router(Node):
|
||||
|
||||
log.debug("{} has deleted a NIO: {}".format(self.name(), result))
|
||||
|
||||
def _saveConfig(self):
|
||||
"""
|
||||
Tells the server to save the router configurations (startup-config and private-config).
|
||||
"""
|
||||
|
||||
params = {"id": self._router_id}
|
||||
log.debug("{} is saving his configuration: {}".format(self.name(), params))
|
||||
self._server.send_notification("dynamips.vm.save_config", params)
|
||||
|
||||
def _slot_info(self):
|
||||
"""
|
||||
Returns information about the slots/ports of this router.
|
||||
@@ -721,12 +772,15 @@ class Router(Node):
|
||||
:returns: representation of the node (dictionary)
|
||||
"""
|
||||
|
||||
# tell the server to save the startup-config and
|
||||
# private-config
|
||||
self._saveConfig()
|
||||
|
||||
router = {"id": self.id(),
|
||||
"type": self.__class__.__name__,
|
||||
"description": str(self),
|
||||
"properties": {},
|
||||
"server_id": self._server.id(),
|
||||
}
|
||||
"server_id": self._server.id()}
|
||||
|
||||
# add the properties
|
||||
for name, value in self._settings.items():
|
||||
|
||||
@@ -96,6 +96,7 @@ class DynamipsPreferencesPage(QtGui.QWidget, Ui_DynamipsPreferencesPageWidget):
|
||||
self.uiBaseHypervisorPortSpinBox.setValue(settings["base_hypervisor_port"])
|
||||
self.uiBaseConsolePortSpinBox.setValue(settings["base_console_port"])
|
||||
self.uiBaseAuxPortSpinBox.setValue(settings["base_aux_port"])
|
||||
self.uiUseLocalServercheckBox.setChecked(settings["use_local_server"])
|
||||
self.uiAllocateHypervisorPerDeviceCheckBox.setChecked(settings["allocate_hypervisor_per_device"])
|
||||
self.uiMemoryUsageLimitPerHypervisorSpinBox.setValue(settings["memory_usage_limit_per_hypervisor"])
|
||||
self.uiAllocateHypervisorPerIOSCheckBox.setChecked(settings["allocate_hypervisor_per_ios_image"])
|
||||
@@ -133,6 +134,7 @@ class DynamipsPreferencesPage(QtGui.QWidget, Ui_DynamipsPreferencesPageWidget):
|
||||
new_settings["base_hypervisor_port"] = self.uiBaseHypervisorPortSpinBox.value()
|
||||
new_settings["base_console_port"] = self.uiBaseConsolePortSpinBox.value()
|
||||
new_settings["base_aux_port"] = self.uiBaseAuxPortSpinBox.value()
|
||||
new_settings["use_local_server"] = self.uiUseLocalServercheckBox.isChecked()
|
||||
new_settings["allocate_hypervisor_per_device"] = self.uiAllocateHypervisorPerDeviceCheckBox.isChecked()
|
||||
new_settings["memory_usage_limit_per_hypervisor"] = self.uiMemoryUsageLimitPerHypervisorSpinBox.value()
|
||||
new_settings["allocate_hypervisor_per_ios_image"] = self.uiAllocateHypervisorPerIOSCheckBox.isChecked()
|
||||
|
||||
@@ -78,7 +78,7 @@ class IOSRouterPreferencesPage(QtGui.QWidget, Ui_IOSRouterPreferencesPageWidget)
|
||||
ios_image = self._ios_images[key]
|
||||
|
||||
self.uiIOSPathLineEdit.setText(ios_image["path"])
|
||||
self.uiStartupConfigLineEdit.setText(ios_image["startup-config"])
|
||||
self.uiStartupConfigLineEdit.setText(ios_image["startup_config"])
|
||||
index = self.uiPlatformComboBox.findText(ios_image["platform"])
|
||||
if index != -1:
|
||||
self.uiPlatformComboBox.setCurrentIndex(index)
|
||||
@@ -142,7 +142,7 @@ class IOSRouterPreferencesPage(QtGui.QWidget, Ui_IOSRouterPreferencesPageWidget)
|
||||
|
||||
self._ios_images[key] = {"path": path,
|
||||
"image": image,
|
||||
"startup-config": startup_config,
|
||||
"startup_config": startup_config,
|
||||
"platform": platform,
|
||||
"chassis": chassis,
|
||||
"idlepc": idlepc,
|
||||
|
||||
@@ -266,6 +266,9 @@ class RouterConfigurationPage(QtGui.QWidget, Ui_routerConfigPageWidget):
|
||||
# load the IOS image name without the full path
|
||||
self.uiIOSImageTextLabel.setText(os.path.basename(settings["image"]))
|
||||
|
||||
# load the startup-config
|
||||
self.uiStartupConfigTextLabel.setText(settings["startup_config"])
|
||||
|
||||
#TODO: startup-config setting
|
||||
#self.uiStartupConfigTextLabel.setText("None")
|
||||
|
||||
|
||||
@@ -35,9 +35,10 @@ else:
|
||||
|
||||
DYNAMIPS_SETTINGS = {
|
||||
"path": DEFAULT_DYNAMIPS_PATH,
|
||||
"base_hypervisor_port": 7200,
|
||||
"base_hypervisor_port": 7201,
|
||||
"base_console_port": 2101,
|
||||
"base_aux_port": 2501,
|
||||
"use_local_server": True,
|
||||
"allocate_hypervisor_per_device": True,
|
||||
"memory_usage_limit_per_hypervisor": 1024,
|
||||
"allocate_hypervisor_per_ios_image": True,
|
||||
@@ -54,6 +55,7 @@ DYNAMIPS_SETTING_TYPES = {
|
||||
"base_hypervisor_port": int,
|
||||
"base_console_port": int,
|
||||
"base_aux_port": int,
|
||||
"use_local_server": bool,
|
||||
"allocate_hypervisor_per_device": bool,
|
||||
"memory_usage_limit_per_hypervisor": int,
|
||||
"allocate_hypervisor_per_ios_image": bool,
|
||||
|
||||
26
gns3/node.py
26
gns3/node.py
@@ -47,6 +47,11 @@ class Node(QtCore.QObject):
|
||||
|
||||
_instance_count = 1
|
||||
|
||||
# node statuses
|
||||
stopped = 0
|
||||
started = 1
|
||||
suspended = 2
|
||||
|
||||
def __init__(self, server=None):
|
||||
|
||||
super(Node, self).__init__()
|
||||
@@ -57,6 +62,7 @@ class Node(QtCore.QObject):
|
||||
|
||||
self._server = server
|
||||
self._initialized = False
|
||||
self._status = 0
|
||||
|
||||
@classmethod
|
||||
def reset(cls):
|
||||
@@ -93,6 +99,26 @@ class Node(QtCore.QObject):
|
||||
|
||||
self._id = new_id
|
||||
|
||||
def status(self):
|
||||
"""
|
||||
Returns the status of this node.
|
||||
0 = stopped, 1 = started, 2 = suspended.
|
||||
|
||||
:returns: node status (integer)
|
||||
"""
|
||||
|
||||
return self._status
|
||||
|
||||
def setStatus(self, status):
|
||||
"""
|
||||
Sets a status for this node.
|
||||
0 = stopped, 1 = started, 2 = suspended.
|
||||
|
||||
:param status: node status (integer)
|
||||
"""
|
||||
|
||||
self._status = status
|
||||
|
||||
def initialized(self):
|
||||
"""
|
||||
Returns if the node has been initialized
|
||||
|
||||
@@ -33,6 +33,11 @@ class Port(object):
|
||||
|
||||
_instance_count = 1
|
||||
|
||||
# port statuses
|
||||
stopped = 0
|
||||
started = 1
|
||||
suspended = 2
|
||||
|
||||
def __init__(self, name, default_nio=None, stub=False):
|
||||
|
||||
# create an unique ID
|
||||
@@ -45,7 +50,7 @@ class Port(object):
|
||||
self._stub = stub
|
||||
self._link_id = None
|
||||
self._description = ""
|
||||
self._status = 0
|
||||
self._status = Port.stopped
|
||||
self._data = {}
|
||||
if default_nio == None:
|
||||
self._default_nio = NIOUDP
|
||||
@@ -100,7 +105,7 @@ class Port(object):
|
||||
def status(self):
|
||||
"""
|
||||
Returns the status of this port.
|
||||
0 = stopped, 1 = active, 2 = suspended.
|
||||
0 = stopped, 1 = started, 2 = suspended.
|
||||
|
||||
:returns: port status (integer)
|
||||
"""
|
||||
@@ -110,7 +115,7 @@ class Port(object):
|
||||
def setStatus(self, status):
|
||||
"""
|
||||
Sets a status for this port.
|
||||
0 = stopped, 1 = active, 2 = suspended.
|
||||
0 = stopped, 1 = started, 2 = suspended.
|
||||
|
||||
:param status: port status (integer)
|
||||
"""
|
||||
|
||||
@@ -22,7 +22,6 @@ vice-versa. Possibility to add PyQt5 in the future as well. Current default is P
|
||||
|
||||
# based on https://gist.github.com/remram44/5985681
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import sys
|
||||
import sip
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ class Servers(object):
|
||||
self._local_server_path = ""
|
||||
self._local_server_proccess = QtCore.QProcess()
|
||||
self._loadSettings()
|
||||
self._remote_server_iter_pos = 0
|
||||
|
||||
def _loadSettings(self):
|
||||
"""
|
||||
@@ -215,6 +216,33 @@ class Servers(object):
|
||||
|
||||
return self._remote_servers
|
||||
|
||||
def __iter__(self):
|
||||
"""
|
||||
Creates a round-robin system to pick up a remote server.
|
||||
"""
|
||||
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
"""
|
||||
Returns the next available remote server.
|
||||
|
||||
:returns: remote server (WebSocketClient instance)
|
||||
"""
|
||||
|
||||
if not self._remote_servers:
|
||||
return None
|
||||
|
||||
server_ids = list(self._remote_servers.keys())
|
||||
server_id = server_ids[self._remote_server_iter_pos]
|
||||
|
||||
if self._remote_server_iter_pos < len(server_ids) - 1:
|
||||
self._remote_server_iter_pos += 1
|
||||
else:
|
||||
self._remote_server_iter_pos = 0
|
||||
|
||||
return self._remote_servers[server_id]
|
||||
|
||||
def save(self):
|
||||
"""
|
||||
Saves the settings.
|
||||
|
||||
@@ -25,6 +25,7 @@ from .qt import QtGui, QtCore
|
||||
from .items.node_item import NodeItem
|
||||
from .modules.dynamips import Dynamips
|
||||
from .modules.module_error import ModuleError
|
||||
from .utils.message_box import MessageBox
|
||||
from .version import __version__
|
||||
|
||||
import logging
|
||||
@@ -184,7 +185,7 @@ class Topology(object):
|
||||
if self._links:
|
||||
topology_links = topology["topology"]["links"] = []
|
||||
for link in self._links:
|
||||
log.info("saving {}".format(link.description().lower()))
|
||||
log.info("saving {}".format(link.description()))
|
||||
topology_links.append(link.dump())
|
||||
|
||||
# finally the servers
|
||||
@@ -235,6 +236,7 @@ class Topology(object):
|
||||
self._node_to_links_mapping[destination_id].append(topology_link)
|
||||
|
||||
# then load the nodes
|
||||
node_errors = []
|
||||
if "nodes" in topology["topology"]:
|
||||
nodes = topology["topology"]["nodes"]
|
||||
for topology_node in nodes:
|
||||
@@ -244,15 +246,14 @@ class Topology(object):
|
||||
#TODO: node setup management with other modules
|
||||
|
||||
# create the node
|
||||
node_class = Dynamips.getNodeClass(topology_node["type"])
|
||||
dynamips = Dynamips.instance()
|
||||
|
||||
# TODO: catch exception
|
||||
try:
|
||||
node_class = Dynamips.getNodeClass(topology_node["type"])
|
||||
dynamips = Dynamips.instance()
|
||||
node = dynamips.createNode(node_class)
|
||||
except ModuleError as e:
|
||||
QtGui.QMessageBox.critical(main_window, "Node creation", "{}".format(e))
|
||||
return
|
||||
node_errors.append(str(e))
|
||||
#QtGui.QMessageBox.critical(main_window, "Node creation", "{}".format(e))
|
||||
continue
|
||||
|
||||
node.setId(topology_node["id"])
|
||||
|
||||
@@ -268,6 +269,10 @@ class Topology(object):
|
||||
view.scene().addItem(node_item)
|
||||
self.addNode(node)
|
||||
|
||||
if node_errors:
|
||||
errors = "\n".join(node_errors)
|
||||
MessageBox(main_window, "Topology", "Errors detected while importing the topology", errors)
|
||||
|
||||
def _nodeCreatedSlot(self, node_id):
|
||||
"""
|
||||
Slot to know when a node has been created.
|
||||
|
||||
33
gns3/utils/message_box.py
Normal file
33
gns3/utils/message_box.py
Normal file
@@ -0,0 +1,33 @@
|
||||
# -*- 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/>.
|
||||
|
||||
"""
|
||||
To show a advanced message box.
|
||||
"""
|
||||
|
||||
from ..qt import QtGui
|
||||
|
||||
|
||||
def MessageBox(parent, title, message, details="", icon=QtGui.QMessageBox.Critical):
|
||||
|
||||
msgbox = QtGui.QMessageBox(parent)
|
||||
msgbox.setWindowTitle(title)
|
||||
msgbox.setText(message)
|
||||
msgbox.setIcon(icon)
|
||||
if details:
|
||||
msgbox.setDetailedText(details)
|
||||
msgbox.exec_()
|
||||
@@ -74,7 +74,8 @@ class ProcessFilesThread(QtCore.QThread):
|
||||
return
|
||||
try:
|
||||
destination_dir = os.path.join(base_dir, directory)
|
||||
os.makedirs(destination_dir)
|
||||
if not os.path.exists(destination_dir):
|
||||
os.makedirs(destination_dir)
|
||||
except EnvironmentError as e:
|
||||
self.error.emit("Could not create directory {}: {}".format(destination_dir, str(e)))
|
||||
return
|
||||
@@ -118,6 +119,7 @@ class ProcessFilesThread(QtCore.QThread):
|
||||
:param directory: path to the directory.
|
||||
"""
|
||||
|
||||
if os.path.isdir(directory):
|
||||
return len(next(os.walk(directory))[2])
|
||||
return 0
|
||||
count = 0
|
||||
for _, _, files in os.walk(directory):
|
||||
count += len(files)
|
||||
return count
|
||||
|
||||
Reference in New Issue
Block a user