Merge branch '2.1' into 2.2

# Conflicts:
#	gns3/ui/resources_rc.py
This commit is contained in:
grossmj
2018-03-30 12:16:31 +07:00
25 changed files with 1866 additions and 6 deletions

View File

@@ -11,6 +11,8 @@ before_deploy:
- sudo pip install urllib3[secure]
deploy:
provider: pypi
edge:
branch: v1.8.45
user: noplay
password:
secure: FofcqlJjgqf2jaDaXpLHeigVoexbrOz3WwnDuiJpwJxeFUlPY8s2cQs/Bm+dzxzZaOaGiVE0A83v/Xa10yD5tflThHt4sqYJK3iQCinA7wgeAlDimB4xrWUNplfNJZ/Eod5Ssa++E02W+3i29PxpXY//mjCY7qDxaoxul1gnFJY=

View File

@@ -319,7 +319,7 @@ class LocalConfig(QtCore.QObject):
"""
if Controller.instance().connected() and self._settings_retrieved_from_controller:
# We save only non user specific sections
section_to_save_on_controller = ["Builtin", "Docker", "IOU", "Qemu", "VMware", "VPCS", "VirtualBox", "GraphicsView", "Dynamips"]
section_to_save_on_controller = ["Builtin", "Docker", "IOU", "Qemu", "VMware", "VPCS", "TraceNG", "VirtualBox", "GraphicsView", "Dynamips"]
controller_settings = {}
for key, val in self._settings.items():
if key in section_to_save_on_controller:

View File

@@ -145,7 +145,8 @@ def main():
frozen_dirs = [
frozen_dir,
os.path.normpath(os.path.join(frozen_dir, 'dynamips')),
os.path.normpath(os.path.join(frozen_dir, 'vpcs'))
os.path.normpath(os.path.join(frozen_dir, 'vpcs')),
os.path.normpath(os.path.join(frozen_dir, 'traceng'))
]
os.environ["PATH"] = os.pathsep.join(frozen_dirs) + os.pathsep + os.environ.get("PATH", "")

View File

@@ -19,9 +19,10 @@ from gns3.modules.builtin import Builtin
from gns3.modules.dynamips import Dynamips
from gns3.modules.iou import IOU
from gns3.modules.vpcs import VPCS
from gns3.modules.traceng import TraceNG
from gns3.modules.virtualbox import VirtualBox
from gns3.modules.qemu import Qemu
from gns3.modules.vmware import VMware
from gns3.modules.docker import Docker
MODULES = [Builtin, VPCS, Dynamips, IOU, Qemu, VirtualBox, VMware, Docker]
MODULES = [Builtin, VPCS, Dynamips, IOU, Qemu, VirtualBox, VMware, Docker, TraceNG]

View File

@@ -0,0 +1,249 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2018 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/>.
"""
TraceNG module implementation.
"""
import os
import copy
import shutil
from gns3.local_config import LocalConfig
from gns3.local_server_config import LocalServerConfig
from ..module import Module
from .traceng_node import TraceNGNode
from .settings import TRACENG_SETTINGS
from .settings import TRACENG_NODES_SETTINGS
import logging
log = logging.getLogger(__name__)
class TraceNG(Module):
"""
TraceNG module.
"""
def __init__(self):
super().__init__()
self._settings = {}
self._nodes = []
self._traceng_nodes = {}
self._working_dir = ""
self._loadSettings()
def configChangedSlot(self):
# load the settings
self._loadSettings()
def _loadSettings(self):
"""
Loads the settings from the persistent settings file.
"""
self._settings = LocalConfig.instance().loadSectionSettings(self.__class__.__name__, TRACENG_SETTINGS)
if not os.path.exists(self._settings["traceng_path"]):
traceng_path = shutil.which("traceng")
if traceng_path:
self._settings["traceng_path"] = os.path.abspath(traceng_path)
else:
self._settings["traceng_path"] = ""
self._loadTraceNGNodes()
def _saveSettings(self):
"""
Saves the settings to the persistent settings file.
"""
# save the settings
LocalConfig.instance().saveSectionSettings(self.__class__.__name__, self._settings)
server_settings = {}
if self._settings["traceng_path"]:
# save some settings to the server config file
server_settings["traceng_path"] = os.path.normpath(self._settings["traceng_path"])
config = LocalServerConfig.instance()
config.saveSettings(self.__class__.__name__, server_settings)
def _loadTraceNGNodes(self):
"""
Load the TraceNG nodes from the persistent settings file.
"""
self._traceng_nodes = {}
settings = LocalConfig.instance().settings()
if "nodes" in settings.get(self.__class__.__name__, {}):
for node in settings[self.__class__.__name__]["nodes"]:
name = node.get("name")
server = node.get("server")
key = "{server}:{name}".format(server=server, name=name)
if key in self._traceng_nodes or not name or not server:
continue
node_settings = TRACENG_NODES_SETTINGS.copy()
node_settings.update(node)
self._traceng_nodes[key] = node_settings
def _saveTraceNGNodes(self):
"""
Saves the TraceNG nodes to the persistent settings file.
"""
self._settings["nodes"] = list(self._traceng_nodes.values())
self._saveSettings()
def addNode(self, node):
"""
Adds a node to this module.
:param node: Node instance
"""
self._nodes.append(node)
def removeNode(self, node):
"""
Removes a node from this module.
:param node: Node instance
"""
if node in self._nodes:
self._nodes.remove(node)
def settings(self):
"""
Returns the module settings
:returns: module settings (dictionary)
"""
return self._settings
def setSettings(self, settings):
"""
Sets the module settings
:param settings: module settings (dictionary)
"""
self._settings.update(settings)
self._saveSettings()
def instantiateNode(self, node_class, server, project):
"""
Instantiate a new node.
:param node_class: Node object
:param server: HTTPClient instance
:param project: Project instance
"""
# create an instance of the node class
return node_class(self, server, project)
def reset(self):
"""
Resets the module.
"""
self._nodes.clear()
@staticmethod
def getNodeType(name, platform=None):
if name == "traceng":
return TraceNGNode
return None
@staticmethod
def vmConfigurationPage():
from .pages.traceng_node_configuration_page import TraceNGNodeConfigurationPage
return TraceNGNodeConfigurationPage
def VMs(self):
"""
Returns list of TraceNG nodes
"""
return self._traceng_nodes
def setVMs(self, new_traceng_nodes):
"""
Sets TraceNG list
:param new_traceng_vms: TraceNG node list
"""
self._traceng_nodes = new_traceng_nodes.copy()
self._saveTraceNGNodes()
@staticmethod
def classes():
"""
Returns all the node classes supported by this module.
:returns: list of classes
"""
return [TraceNGNode]
def nodes(self):
"""
Returns all the node data necessary to represent a node
in the nodes view and create a node on the scene.
"""
nodes = []
# Add a default TraceNG not linked to a specific server
nodes.append(
{
"class": TraceNGNode.__name__,
"name": "TraceNG",
"categories": [TraceNGNode.end_devices],
"symbol": TraceNGNode.defaultSymbol(),
"builtin": True
}
)
return nodes
@staticmethod
def preferencePages():
"""
:returns: QWidget object list
"""
from .pages.traceng_preferences_page import TraceNGPreferencesPage
from .pages.traceng_node_preferences_page import TraceNGNodePreferencesPage
return [TraceNGPreferencesPage, TraceNGNodePreferencesPage]
@staticmethod
def instance():
"""
Singleton to return only on instance of TraceNG module.
:returns: instance of TraceNG
"""
if not hasattr(TraceNG, "_instance"):
TraceNG._instance = TraceNG()
return TraceNG._instance

View File

View File

@@ -0,0 +1,86 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2018 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/>.
"""
Wizard for TraceNG nodes.
"""
import sys
import ipaddress
from gns3.qt import QtGui, QtWidgets
from gns3.node import Node
from gns3.dialogs.vm_wizard import VMWizard
from ..ui.traceng_node_wizard_ui import Ui_TraceNGNodeWizard
class TraceNGNodeWizard(VMWizard, Ui_TraceNGNodeWizard):
"""
Wizard to create a TraceNG node template.
:param parent: parent widget
"""
def __init__(self, traceng_nodes, parent):
super().__init__(traceng_nodes, parent)
self.setPixmap(QtWidgets.QWizard.LogoPixmap, QtGui.QPixmap(":/icons/traceng.png"))
self.uiNameWizardPage.registerField("name*", self.uiNameLineEdit)
# TraceNG is only supported on a local server
self.uiRemoteRadioButton.setEnabled(False)
self.uiVMRadioButton.setEnabled(False)
def validateCurrentPage(self):
"""
Validates the server.
"""
if super().validateCurrentPage() is False:
return False
if self.currentPage() == self.uiNameWizardPage:
if not sys.platform.startswith("win"):
QtWidgets.QMessageBox.critical(self, "TraceNG", "TraceNG can only run on Windows with a local server")
return False
ip_address = self.uiIPAddressLineEdit.text()
if ip_address:
try:
ipaddress.IPv4Address(ip_address)
except ipaddress.AddressValueError:
QtWidgets.QMessageBox.critical(self, "IP address", "Invalid IP address format")
return False
return True
def getSettings(self):
"""
Returns the settings set in this Wizard.
:return: settings dict
"""
settings = {"name": self.uiNameLineEdit.text(),
"ip_address": self.uiIPAddressLineEdit.text(),
"symbol": ":/symbols/traceng.svg",
"category": Node.end_devices,
"server": self._compute_id}
return settings

View File

View File

@@ -0,0 +1,150 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2018 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/>.
"""
Configuration page for TraceNG nodes
"""
import ipaddress
from gns3.qt import QtWidgets
from gns3.local_server import LocalServer
from gns3.node import Node
from gns3.controller import Controller
from ..ui.traceng_node_configuration_page_ui import Ui_TraceNGNodeConfigPageWidget
from gns3.dialogs.symbol_selection_dialog import SymbolSelectionDialog
from gns3.dialogs.node_properties_dialog import ConfigurationError
class TraceNGNodeConfigurationPage(QtWidgets.QWidget, Ui_TraceNGNodeConfigPageWidget):
"""
QWidget configuration page for TraceNG nodes.
"""
def __init__(self):
super().__init__()
self.setupUi(self)
self.uiSymbolToolButton.clicked.connect(self._symbolBrowserSlot)
self._default_configs_dir = LocalServer.instance().localServerSettings()["configs_path"]
if Controller.instance().isRemote():
self.uiScriptFileToolButton.hide()
# add the categories
for name, category in Node.defaultCategories().items():
self.uiCategoryComboBox.addItem(name, category)
def _symbolBrowserSlot(self):
"""
Slot to open the symbol browser and select a new symbol.
"""
symbol_path = self.uiSymbolLineEdit.text()
dialog = SymbolSelectionDialog(self, symbol=symbol_path)
dialog.show()
if dialog.exec_():
new_symbol_path = dialog.getSymbol()
self.uiSymbolLineEdit.setText(new_symbol_path)
self.uiSymbolLineEdit.setToolTip('<img src="{}"/>'.format(new_symbol_path))
def loadSettings(self, settings, node=None, group=False):
"""
Loads the TraceNG node settings.
:param settings: the settings (dictionary)
:param node: Node instance
:param group: indicates the settings apply to a group of routers
"""
if not group:
self.uiNameLineEdit.setText(settings["name"])
self.uiIPAddressLineEdit.setText(settings["ip_address"])
else:
self.uiIPAddressLabel.hide()
self.uiIPAddressLineEdit.hide()
self.uiNameLabel.hide()
self.uiNameLineEdit.hide()
if not node:
# these are template settings
# rename the label from "Name" to "Template name"
self.uiNameLabel.setText("Template name:")
# load the default name format
self.uiDefaultNameFormatLineEdit.setText(settings["default_name_format"])
# load the symbol
self.uiSymbolLineEdit.setText(settings["symbol"])
self.uiSymbolLineEdit.setToolTip('<img src="{}"/>'.format(settings["symbol"]))
# load the category
index = self.uiCategoryComboBox.findData(settings["category"])
if index != -1:
self.uiCategoryComboBox.setCurrentIndex(index)
else:
self.uiDefaultNameFormatLabel.hide()
self.uiDefaultNameFormatLineEdit.hide()
self.uiSymbolLabel.hide()
self.uiSymbolLineEdit.hide()
self.uiSymbolToolButton.hide()
self.uiCategoryComboBox.hide()
self.uiCategoryLabel.hide()
self.uiCategoryComboBox.hide()
def saveSettings(self, settings, node=None, group=False):
"""
Saves the TraceNG node settings.
:param settings: the settings (dictionary)
:param node: Node instance
:param group: indicates the settings apply to a group of routers
"""
# these settings cannot be shared by nodes and updated
# in the node properties dialog.
if not group:
# set the node name
name = self.uiNameLineEdit.text()
if not name:
QtWidgets.QMessageBox.critical(self, "Name", "TraceNG node name cannot be empty!")
else:
settings["name"] = name
ip_address = self.uiIPAddressLineEdit.text()
if ip_address:
try:
ipaddress.IPv4Address(ip_address)
settings["ip_address"] = ip_address
except ipaddress.AddressValueError:
QtWidgets.QMessageBox.critical(self, "IP address", "Invalid IP address format")
if node:
raise ConfigurationError()
if not node:
default_name_format = self.uiDefaultNameFormatLineEdit.text().strip()
if '{0}' not in default_name_format and '{id}' not in default_name_format:
QtWidgets.QMessageBox.critical(self, "Default name format", "The default name format must contain at least {0} or {id}")
else:
settings["default_name_format"] = default_name_format
symbol_path = self.uiSymbolLineEdit.text()
settings["symbol"] = symbol_path
settings["category"] = self.uiCategoryComboBox.itemData(self.uiCategoryComboBox.currentIndex())
return settings

View File

@@ -0,0 +1,189 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2018 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/>.
"""
Configuration page for TraceNG node preferences.
"""
import copy
from gns3.qt import QtCore, QtWidgets, qpartial
from gns3.main_window import MainWindow
from gns3.dialogs.configuration_dialog import ConfigurationDialog
from gns3.compute_manager import ComputeManager
from gns3.controller import Controller
from .. import TraceNG
from ..settings import TRACENG_NODES_SETTINGS
from ..ui.traceng_node_preferences_page_ui import Ui_TraceNGNodePageWidget
from ..pages.traceng_node_configuration_page import TraceNGNodeConfigurationPage
from ..dialogs.traceng_node_wizard import TraceNGNodeWizard
class TraceNGNodePreferencesPage(QtWidgets.QWidget, Ui_TraceNGNodePageWidget):
"""
QWidget preference page for TraceNG node preferences.
"""
def __init__(self):
super().__init__()
self.setupUi(self)
self._main_window = MainWindow.instance()
self._traceng_nodes = {}
self._items = []
self.uiNewTraceNGPushButton.clicked.connect(self._newTraceNGSlot)
self.uiEditTraceNGPushButton.clicked.connect(self._editTraceNGSlot)
self.uiDeleteTraceNGPushButton.clicked.connect(self._deleteTraceNGSlot)
self.uiTraceNGTreeWidget.itemSelectionChanged.connect(self._tracengChangedSlot)
def _createSectionItem(self, name):
section_item = QtWidgets.QTreeWidgetItem(self.uiTraceNGInfoTreeWidget)
section_item.setText(0, name)
font = section_item.font(0)
font.setBold(True)
section_item.setFont(0, font)
return section_item
def _refreshInfo(self, traceng_node):
self.uiTraceNGInfoTreeWidget.clear()
# fill out the General section
section_item = self._createSectionItem("General")
QtWidgets.QTreeWidgetItem(section_item, ["Template name:", traceng_node["name"]])
QtWidgets.QTreeWidgetItem(section_item, ["IP address:", traceng_node["ip_address"]])
QtWidgets.QTreeWidgetItem(section_item, ["Default name format:", traceng_node["default_name_format"]])
try:
QtWidgets.QTreeWidgetItem(section_item, ["Server:", ComputeManager.instance().getCompute(traceng_node["server"]).name()])
except KeyError:
pass
self.uiTraceNGInfoTreeWidget.expandAll()
self.uiTraceNGInfoTreeWidget.resizeColumnToContents(0)
self.uiTraceNGInfoTreeWidget.resizeColumnToContents(1)
self.uiTraceNGTreeWidget.setMaximumWidth(self.uiTraceNGTreeWidget.sizeHintForColumn(0) + 20)
def _tracengChangedSlot(self):
"""
Loads a selected TraceNG node template from the tree widget.
"""
selection = self.uiTraceNGTreeWidget.selectedItems()
self.uiDeleteTraceNGPushButton.setEnabled(len(selection) != 0)
single_selected = len(selection) == 1
self.uiEditTraceNGPushButton.setEnabled(single_selected)
if single_selected:
key = selection[0].data(0, QtCore.Qt.UserRole)
traceng_node = self._traceng_nodes[key]
self._refreshInfo(traceng_node)
else:
self.uiTraceNGInfoTreeWidget.clear()
def _newTraceNGSlot(self):
"""
Creates a new TraceNG node template.
"""
wizard = TraceNGNodeWizard(self._traceng_nodes, parent=self)
wizard.show()
if wizard.exec_():
new_traceng_node_settings = wizard.getSettings()
key = "{server}:{name}".format(server=new_traceng_node_settings["server"], name=new_traceng_node_settings["name"])
self._traceng_nodes[key] = TRACENG_NODES_SETTINGS.copy()
self._traceng_nodes[key].update(new_traceng_node_settings)
item = QtWidgets.QTreeWidgetItem(self.uiTraceNGTreeWidget)
item.setText(0, self._traceng_nodes[key]["name"])
Controller.instance().getSymbolIcon(self._traceng_nodes[key]["symbol"], qpartial(self._setItemIcon, item))
item.setData(0, QtCore.Qt.UserRole, key)
self._items.append(item)
self.uiTraceNGTreeWidget.setCurrentItem(item)
def _editTraceNGSlot(self):
"""
Edits a TraceNG node template.
"""
item = self.uiTraceNGTreeWidget.currentItem()
if item:
key = item.data(0, QtCore.Qt.UserRole)
traceng_node = self._traceng_nodes[key]
dialog = ConfigurationDialog(traceng_node["name"], traceng_node, TraceNGNodeConfigurationPage(), parent=self)
dialog.show()
if dialog.exec_():
# update the icon
Controller.instance().getSymbolIcon(traceng_node["symbol"], qpartial(self._setItemIcon, item))
if traceng_node["name"] != item.text(0):
new_key = "{server}:{name}".format(server=traceng_node["server"], name=traceng_node["name"])
if new_key in self._traceng_nodes:
QtWidgets.QMessageBox.critical(self, "TraceNG node", "TraceNG node name {} already exists for server {}".format(traceng_node["name"],
traceng_node["server"]))
traceng_node["name"] = item.text(0)
return
self._traceng_nodes[new_key] = self._traceng_nodes[key]
del self._traceng_nodes[key]
item.setText(0, traceng_node["name"])
item.setData(0, QtCore.Qt.UserRole, new_key)
self._refreshInfo(traceng_node)
def _deleteTraceNGSlot(self):
"""
Deletes a TraceNG node template.
"""
for item in self.uiTraceNGTreeWidget.selectedItems():
if item:
key = item.data(0, QtCore.Qt.UserRole)
del self._traceng_nodes[key]
self.uiTraceNGTreeWidget.takeTopLevelItem(self.uiTraceNGTreeWidget.indexOfTopLevelItem(item))
def loadPreferences(self):
"""
Loads the TraceNG node preferences.
"""
traceng_module = TraceNG.instance()
self._traceng_nodes = copy.deepcopy(traceng_module.VMs())
self._items.clear()
for key, node in self._traceng_nodes.items():
item = QtWidgets.QTreeWidgetItem(self.uiTraceNGTreeWidget)
item.setText(0, node["name"])
Controller.instance().getSymbolIcon(node["symbol"], qpartial(self._setItemIcon, item))
item.setData(0, QtCore.Qt.UserRole, key)
self._items.append(item)
if self._items:
self.uiTraceNGTreeWidget.setCurrentItem(self._items[0])
self.uiTraceNGTreeWidget.sortByColumn(0, QtCore.Qt.AscendingOrder)
self.uiTraceNGTreeWidget.setMaximumWidth(self.uiTraceNGTreeWidget.sizeHintForColumn(0) + 20)
def _setItemIcon(self, item, icon):
item.setIcon(0, icon)
self.uiTraceNGTreeWidget.setMaximumWidth(self.uiTraceNGTreeWidget.sizeHintForColumn(0) + 20)
def savePreferences(self):
"""
Saves the TraceNG node preferences.
"""
TraceNG.instance().setVMs(self._traceng_nodes)

View File

@@ -0,0 +1,127 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2018 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/>.
"""
Configuration page for TraceNG preferences.
"""
import os
import sys
import shutil
from gns3.qt import QtWidgets
from .. import TraceNG
from ..ui.traceng_preferences_page_ui import Ui_TraceNGPreferencesPageWidget
from ..settings import TRACENG_SETTINGS
class TraceNGPreferencesPage(QtWidgets.QWidget, Ui_TraceNGPreferencesPageWidget):
"""
QWidget preference page for TraceNG
"""
def __init__(self):
super().__init__()
self.setupUi(self)
# connect signals
self.uiRestoreDefaultsPushButton.clicked.connect(self._restoreDefaultsSlot)
self.uiTraceNGPathToolButton.clicked.connect(self._tracengPathBrowserSlot)
def _tracengPathBrowserSlot(self):
"""
Slot to open a file browser and select traceng
"""
filter = ""
if sys.platform.startswith("win"):
filter = "Executable (*.exe);;All files (*.*)"
traceng_path = shutil.which("traceng")
path, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Select TraceNG", traceng_path, filter)
if not path:
return
if self._checkTraceNGPath(path):
self.uiTraceNGPathLineEdit.setText(os.path.normpath(path))
def _checkTraceNGPath(self, path):
"""
Checks that the TraceNG path is valid.
:param path: TraceNG path
:returns: boolean
"""
if not os.path.exists(path):
QtWidgets.QMessageBox.critical(self, "TraceNG", '"{}" does not exist'.format(path))
return False
if not os.access(path, os.X_OK):
QtWidgets.QMessageBox.critical(self, "TraceNG", "{} is not an executable".format(os.path.basename(path)))
return False
return True
def _restoreDefaultsSlot(self):
"""
Slot to populate the page widgets with the default settings.
"""
self._populateWidgets(TRACENG_SETTINGS)
def _useLocalServerSlot(self, state):
"""
Slot to enable or not local server settings.
"""
if state:
self.uiTraceNGPathLineEdit.setEnabled(True)
self.uiTraceNGPathToolButton.setEnabled(True)
else:
self.uiTraceNGPathLineEdit.setEnabled(False)
self.uiTraceNGPathToolButton.setEnabled(False)
def _populateWidgets(self, settings):
"""
Populates the widgets with the settings.
:param settings: TraceNG settings
"""
self.uiTraceNGPathLineEdit.setText(settings["traceng_path"])
def loadPreferences(self):
"""
Loads TraceNG preferences.
"""
traceng_settings = TraceNG.instance().settings()
self._populateWidgets(traceng_settings)
def savePreferences(self):
"""
Saves TraceNG preferences.
"""
traceng_path = self.uiTraceNGPathLineEdit.text().strip()
if traceng_path and not self._checkTraceNGPath(traceng_path):
return
new_settings = {"traceng_path": traceng_path}
TraceNG.instance().setSettings(new_settings)

View File

@@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2018 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/>.
"""
Default TraceNG settings.
"""
from gns3.node import Node
TRACENG_SETTINGS = {
"traceng_path": "",
}
TRACENG_NODES_SETTINGS = {
"name": "",
"ip_address": "",
"default_name_format": "TraceNG{0}",
"console_type": "none",
"symbol": ":/symbols/traceng.svg",
"category": Node.end_devices,
}

View File

@@ -0,0 +1,161 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2018 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/>.
"""
TraceNG node implementation.
"""
from gns3.node import Node
from gns3.qt import QtWidgets
import logging
log = logging.getLogger(__name__)
class TraceNGNode(Node):
"""
TraceNG node.
:param module: parent module for this node
:param server: GNS3 server instance
:param project: Project instance
"""
URL_PREFIX = "traceng"
def __init__(self, module, server, project):
super().__init__(module, server, project)
traceng_settings = {"console_host": None,
"console": None,
"console_type": "none",
"ip_address": ""}
self.settings().update(traceng_settings)
def update(self, new_settings):
"""
Updates the settings for this TraceNG node.
:param new_settings: settings dictionary
"""
params = {}
for name, value in new_settings.items():
if name in self._settings and self._settings[name] != value:
params[name] = value
if params:
self._update(params)
def start(self):
"""
Starts this node instance.
"""
if self.isStarted():
log.debug("{} is already running".format(self.name()))
return
destination, ok = QtWidgets.QInputDialog.getText(self.parent(), "TraceNG", "Destination host or IP address:")
if ok:
if not destination:
QtWidgets.QMessageBox.critical(self, "TraceNG", "Please provide a host or IP address to trace")
return
params = {"destination": destination}
log.debug("{} is starting".format(self.name()))
self.controllerHttpPost("/nodes/{node_id}/start".format(node_id=self._node_id), self._startCallback, body=params, timeout=None, progressText="{} is starting".format(self.name()))
def info(self):
"""
Returns information about this TraceNG node.
:returns: formatted string
"""
if self.status() == Node.started:
state = "started"
else:
state = "stopped"
info = """Node {name} is {state}
Local node ID is {id}
Server's VPCS node ID is {node_id}
TraceNG's server runs on {host}, console is on port {console}
""".format(name=self.name(),
id=self.id(),
node_id=self._node_id,
state=state,
host=self.compute().name(),
console=self._settings["console"])
port_info = ""
for port in self._ports:
if port.isFree():
port_info += " {port_name} is empty\n".format(port_name=port.name())
else:
port_info += " {port_name} {port_description}\n".format(port_name=port.name(),
port_description=port.description())
return info + port_info
def console(self):
"""
Returns the console port for this TraceNG node.
:returns: port (integer)
"""
return self._settings["console"]
def configPage(self):
"""
Returns the configuration page widget to be used by the node properties dialog.
:returns: QWidget object
"""
from .pages.traceng_node_configuration_page import TraceNGNodeConfigurationPage
return TraceNGNodeConfigurationPage
@staticmethod
def defaultSymbol():
"""
Returns the default symbol path for this node.
:returns: symbol path (or resource).
"""
return ":/symbols/traceng.svg"
@staticmethod
def symbolName():
return "TraceNG"
@staticmethod
def categories():
"""
Returns the node categories the node is part of (used by the node panel).
:returns: list of node categories
"""
return [Node.end_devices]
def __str__(self):
return "TraceNG node"

View File

View File

@@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TraceNGNodeConfigPageWidget</class>
<widget class="QWidget" name="TraceNGNodeConfigPageWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>565</width>
<height>390</height>
</rect>
</property>
<property name="windowTitle">
<string>TraceNG node configuration</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="uiNameLabel">
<property name="text">
<string>Name:</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLineEdit" name="uiNameLineEdit"/>
</item>
<item row="1" column="0" colspan="2">
<widget class="QLabel" name="uiIPAddressLabel">
<property name="text">
<string>IP address:</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="uiIPAddressLineEdit"/>
</item>
<item row="2" column="0" colspan="2">
<widget class="QLabel" name="uiDefaultNameFormatLabel">
<property name="text">
<string>Default name format:</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLineEdit" name="uiDefaultNameFormatLineEdit"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="uiSymbolLabel">
<property name="text">
<string>Symbol:</string>
</property>
</widget>
</item>
<item row="3" column="2">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLineEdit" name="uiSymbolLineEdit"/>
</item>
<item>
<widget class="QToolButton" name="uiSymbolToolButton">
<property name="text">
<string>&amp;Browse...</string>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextOnly</enum>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0">
<widget class="QLabel" name="uiCategoryLabel">
<property name="text">
<string>Category:</string>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QComboBox" name="uiCategoryComboBox"/>
</item>
<item row="5" column="1" colspan="2">
<spacer name="spacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>263</width>
<height>212</height>
</size>
</property>
</spacer>
</item>
</layout>
<zorder>uiNameLabel</zorder>
<zorder>uiNameLineEdit</zorder>
<zorder>uiDefaultNameFormatLabel</zorder>
<zorder>uiDefaultNameFormatLineEdit</zorder>
<zorder>uiSymbolLabel</zorder>
<zorder>uiCategoryLabel</zorder>
<zorder>uiCategoryComboBox</zorder>
<zorder>uiSymbolLineEdit</zorder>
<zorder>uiSymbolToolButton</zorder>
<zorder>uiIPAddressLabel</zorder>
<zorder>uiIPAddressLineEdit</zorder>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,80 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/modules/traceng/ui/traceng_node_configuration_page.ui'
#
# Created by: PyQt5 UI code generator 5.5.1
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_TraceNGNodeConfigPageWidget(object):
def setupUi(self, TraceNGNodeConfigPageWidget):
TraceNGNodeConfigPageWidget.setObjectName("TraceNGNodeConfigPageWidget")
TraceNGNodeConfigPageWidget.resize(565, 390)
self.gridLayout = QtWidgets.QGridLayout(TraceNGNodeConfigPageWidget)
self.gridLayout.setObjectName("gridLayout")
self.uiNameLabel = QtWidgets.QLabel(TraceNGNodeConfigPageWidget)
self.uiNameLabel.setObjectName("uiNameLabel")
self.gridLayout.addWidget(self.uiNameLabel, 0, 0, 1, 1)
self.uiNameLineEdit = QtWidgets.QLineEdit(TraceNGNodeConfigPageWidget)
self.uiNameLineEdit.setObjectName("uiNameLineEdit")
self.gridLayout.addWidget(self.uiNameLineEdit, 0, 2, 1, 1)
self.uiIPAddressLabel = QtWidgets.QLabel(TraceNGNodeConfigPageWidget)
self.uiIPAddressLabel.setObjectName("uiIPAddressLabel")
self.gridLayout.addWidget(self.uiIPAddressLabel, 1, 0, 1, 2)
self.uiIPAddressLineEdit = QtWidgets.QLineEdit(TraceNGNodeConfigPageWidget)
self.uiIPAddressLineEdit.setObjectName("uiIPAddressLineEdit")
self.gridLayout.addWidget(self.uiIPAddressLineEdit, 1, 2, 1, 1)
self.uiDefaultNameFormatLabel = QtWidgets.QLabel(TraceNGNodeConfigPageWidget)
self.uiDefaultNameFormatLabel.setObjectName("uiDefaultNameFormatLabel")
self.gridLayout.addWidget(self.uiDefaultNameFormatLabel, 2, 0, 1, 2)
self.uiDefaultNameFormatLineEdit = QtWidgets.QLineEdit(TraceNGNodeConfigPageWidget)
self.uiDefaultNameFormatLineEdit.setObjectName("uiDefaultNameFormatLineEdit")
self.gridLayout.addWidget(self.uiDefaultNameFormatLineEdit, 2, 2, 1, 1)
self.uiSymbolLabel = QtWidgets.QLabel(TraceNGNodeConfigPageWidget)
self.uiSymbolLabel.setObjectName("uiSymbolLabel")
self.gridLayout.addWidget(self.uiSymbolLabel, 3, 0, 1, 1)
self.horizontalLayout_7 = QtWidgets.QHBoxLayout()
self.horizontalLayout_7.setObjectName("horizontalLayout_7")
self.uiSymbolLineEdit = QtWidgets.QLineEdit(TraceNGNodeConfigPageWidget)
self.uiSymbolLineEdit.setObjectName("uiSymbolLineEdit")
self.horizontalLayout_7.addWidget(self.uiSymbolLineEdit)
self.uiSymbolToolButton = QtWidgets.QToolButton(TraceNGNodeConfigPageWidget)
self.uiSymbolToolButton.setToolButtonStyle(QtCore.Qt.ToolButtonTextOnly)
self.uiSymbolToolButton.setObjectName("uiSymbolToolButton")
self.horizontalLayout_7.addWidget(self.uiSymbolToolButton)
self.gridLayout.addLayout(self.horizontalLayout_7, 3, 2, 1, 1)
self.uiCategoryLabel = QtWidgets.QLabel(TraceNGNodeConfigPageWidget)
self.uiCategoryLabel.setObjectName("uiCategoryLabel")
self.gridLayout.addWidget(self.uiCategoryLabel, 4, 0, 1, 1)
self.uiCategoryComboBox = QtWidgets.QComboBox(TraceNGNodeConfigPageWidget)
self.uiCategoryComboBox.setObjectName("uiCategoryComboBox")
self.gridLayout.addWidget(self.uiCategoryComboBox, 4, 2, 1, 1)
spacerItem = QtWidgets.QSpacerItem(263, 212, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.gridLayout.addItem(spacerItem, 5, 1, 1, 2)
self.uiNameLabel.raise_()
self.uiNameLineEdit.raise_()
self.uiDefaultNameFormatLabel.raise_()
self.uiDefaultNameFormatLineEdit.raise_()
self.uiSymbolLabel.raise_()
self.uiCategoryLabel.raise_()
self.uiCategoryComboBox.raise_()
self.uiSymbolLineEdit.raise_()
self.uiSymbolToolButton.raise_()
self.uiIPAddressLabel.raise_()
self.uiIPAddressLineEdit.raise_()
self.retranslateUi(TraceNGNodeConfigPageWidget)
QtCore.QMetaObject.connectSlotsByName(TraceNGNodeConfigPageWidget)
def retranslateUi(self, TraceNGNodeConfigPageWidget):
_translate = QtCore.QCoreApplication.translate
TraceNGNodeConfigPageWidget.setWindowTitle(_translate("TraceNGNodeConfigPageWidget", "TraceNG node configuration"))
self.uiNameLabel.setText(_translate("TraceNGNodeConfigPageWidget", "Name:"))
self.uiIPAddressLabel.setText(_translate("TraceNGNodeConfigPageWidget", "IP address:"))
self.uiDefaultNameFormatLabel.setText(_translate("TraceNGNodeConfigPageWidget", "Default name format:"))
self.uiSymbolLabel.setText(_translate("TraceNGNodeConfigPageWidget", "Symbol:"))
self.uiSymbolToolButton.setText(_translate("TraceNGNodeConfigPageWidget", "&Browse..."))
self.uiCategoryLabel.setText(_translate("TraceNGNodeConfigPageWidget", "Category:"))

View File

@@ -0,0 +1,160 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TraceNGNodePageWidget</class>
<widget class="QWidget" name="TraceNGNodePageWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>546</width>
<height>455</height>
</rect>
</property>
<property name="windowTitle">
<string>TraceNG nodes</string>
</property>
<property name="accessibleName">
<string>TraceNG node templates</string>
</property>
<property name="accessibleDescription">
<string/>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QTreeWidget" name="uiTraceNGTreeWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>160</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
<widget class="QWidget" name="layoutWidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTreeWidget" name="uiTraceNGInfoTreeWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="indentation">
<number>10</number>
</property>
<property name="allColumnsShowFocus">
<bool>true</bool>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>1</string>
</property>
</column>
<column>
<property name="text">
<string>2</string>
</property>
</column>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QPushButton" name="uiNewTraceNGPushButton">
<property name="text">
<string>&amp;New</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="uiEditTraceNGPushButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>&amp;Edit</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="uiDeleteTraceNGPushButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>&amp;Delete</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>uiNewTraceNGPushButton</tabstop>
<tabstop>uiDeleteTraceNGPushButton</tabstop>
</tabstops>
<resources/>
<connections/>
<designerdata>
<property name="gridDeltaX">
<number>10</number>
</property>
<property name="gridDeltaY">
<number>10</number>
</property>
<property name="gridSnapX">
<bool>true</bool>
</property>
<property name="gridSnapY">
<bool>true</bool>
</property>
<property name="gridVisible">
<bool>true</bool>
</property>
</designerdata>
</ui>

View File

@@ -0,0 +1,83 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/modules/traceng/ui/traceng_node_preferences_page.ui'
#
# Created by: PyQt5 UI code generator 5.5.1
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_TraceNGNodePageWidget(object):
def setupUi(self, TraceNGNodePageWidget):
TraceNGNodePageWidget.setObjectName("TraceNGNodePageWidget")
TraceNGNodePageWidget.resize(546, 455)
TraceNGNodePageWidget.setAccessibleDescription("")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(TraceNGNodePageWidget)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.splitter = QtWidgets.QSplitter(TraceNGNodePageWidget)
self.splitter.setOrientation(QtCore.Qt.Horizontal)
self.splitter.setObjectName("splitter")
self.uiTraceNGTreeWidget = QtWidgets.QTreeWidget(self.splitter)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.uiTraceNGTreeWidget.sizePolicy().hasHeightForWidth())
self.uiTraceNGTreeWidget.setSizePolicy(sizePolicy)
self.uiTraceNGTreeWidget.setMaximumSize(QtCore.QSize(160, 16777215))
font = QtGui.QFont()
font.setPointSize(11)
font.setBold(True)
font.setWeight(75)
self.uiTraceNGTreeWidget.setFont(font)
self.uiTraceNGTreeWidget.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.uiTraceNGTreeWidget.setIconSize(QtCore.QSize(32, 32))
self.uiTraceNGTreeWidget.setRootIsDecorated(False)
self.uiTraceNGTreeWidget.setObjectName("uiTraceNGTreeWidget")
self.uiTraceNGTreeWidget.headerItem().setText(0, "1")
self.uiTraceNGTreeWidget.header().setVisible(False)
self.layoutWidget = QtWidgets.QWidget(self.splitter)
self.layoutWidget.setObjectName("layoutWidget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.layoutWidget)
self.verticalLayout.setObjectName("verticalLayout")
self.uiTraceNGInfoTreeWidget = QtWidgets.QTreeWidget(self.layoutWidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.uiTraceNGInfoTreeWidget.sizePolicy().hasHeightForWidth())
self.uiTraceNGInfoTreeWidget.setSizePolicy(sizePolicy)
self.uiTraceNGInfoTreeWidget.setIndentation(10)
self.uiTraceNGInfoTreeWidget.setAllColumnsShowFocus(True)
self.uiTraceNGInfoTreeWidget.setObjectName("uiTraceNGInfoTreeWidget")
self.uiTraceNGInfoTreeWidget.header().setVisible(False)
self.verticalLayout.addWidget(self.uiTraceNGInfoTreeWidget)
self.horizontalLayout_5 = QtWidgets.QHBoxLayout()
self.horizontalLayout_5.setObjectName("horizontalLayout_5")
self.uiNewTraceNGPushButton = QtWidgets.QPushButton(self.layoutWidget)
self.uiNewTraceNGPushButton.setObjectName("uiNewTraceNGPushButton")
self.horizontalLayout_5.addWidget(self.uiNewTraceNGPushButton)
self.uiEditTraceNGPushButton = QtWidgets.QPushButton(self.layoutWidget)
self.uiEditTraceNGPushButton.setEnabled(False)
self.uiEditTraceNGPushButton.setObjectName("uiEditTraceNGPushButton")
self.horizontalLayout_5.addWidget(self.uiEditTraceNGPushButton)
self.uiDeleteTraceNGPushButton = QtWidgets.QPushButton(self.layoutWidget)
self.uiDeleteTraceNGPushButton.setEnabled(False)
self.uiDeleteTraceNGPushButton.setObjectName("uiDeleteTraceNGPushButton")
self.horizontalLayout_5.addWidget(self.uiDeleteTraceNGPushButton)
self.verticalLayout.addLayout(self.horizontalLayout_5)
self.verticalLayout_2.addWidget(self.splitter)
self.retranslateUi(TraceNGNodePageWidget)
QtCore.QMetaObject.connectSlotsByName(TraceNGNodePageWidget)
TraceNGNodePageWidget.setTabOrder(self.uiNewTraceNGPushButton, self.uiDeleteTraceNGPushButton)
def retranslateUi(self, TraceNGNodePageWidget):
_translate = QtCore.QCoreApplication.translate
TraceNGNodePageWidget.setWindowTitle(_translate("TraceNGNodePageWidget", "TraceNG nodes"))
TraceNGNodePageWidget.setAccessibleName(_translate("TraceNGNodePageWidget", "TraceNG node templates"))
self.uiTraceNGInfoTreeWidget.headerItem().setText(0, _translate("TraceNGNodePageWidget", "1"))
self.uiTraceNGInfoTreeWidget.headerItem().setText(1, _translate("TraceNGNodePageWidget", "2"))
self.uiNewTraceNGPushButton.setText(_translate("TraceNGNodePageWidget", "&New"))
self.uiEditTraceNGPushButton.setText(_translate("TraceNGNodePageWidget", "&Edit"))
self.uiDeleteTraceNGPushButton.setText(_translate("TraceNGNodePageWidget", "&Delete"))

View File

@@ -0,0 +1,140 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TraceNGNodeWizard</class>
<widget class="QWizard" name="TraceNGNodeWizard">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>706</width>
<height>452</height>
</rect>
</property>
<property name="windowTitle">
<string>New TraceNG node template</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<widget class="QWizardPage" name="uiServerWizardPage">
<property name="title">
<string>Server</string>
</property>
<property name="subTitle">
<string>Please choose a server type to run your new TraceNG node.</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QGroupBox" name="uiServerTypeGroupBox">
<property name="title">
<string>Server type</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QRadioButton" name="uiRemoteRadioButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Run the TraceNG node on a remote computer</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="uiVMRadioButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Run the TraceNG node on the GNS3 VM</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="uiLocalRadioButton">
<property name="text">
<string>Run the TraceNG node on your local computer</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="uiRemoteServersGroupBox">
<property name="title">
<string>Remote server</string>
</property>
<layout class="QGridLayout" name="gridLayout_7">
<item row="0" column="0">
<widget class="QLabel" name="uiRemoteServersLabel">
<property name="text">
<string>Run on:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="uiRemoteServersComboBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QWizardPage" name="uiNameWizardPage">
<property name="title">
<string>Name and IP address</string>
</property>
<property name="subTitle">
<string>Please choose a descriptive name and IP address for the new TraceNG node.</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="uiNameLabel">
<property name="text">
<string>Name:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="uiNameLineEdit"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="uiIPAddressLabel">
<property name="text">
<string>IP address:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="uiIPAddressLineEdit">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<tabstops>
<tabstop>uiNameLineEdit</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,93 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/modules/traceng/ui/traceng_node_wizard.ui'
#
# Created by: PyQt5 UI code generator 5.5.1
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_TraceNGNodeWizard(object):
def setupUi(self, TraceNGNodeWizard):
TraceNGNodeWizard.setObjectName("TraceNGNodeWizard")
TraceNGNodeWizard.resize(706, 452)
TraceNGNodeWizard.setModal(True)
self.uiServerWizardPage = QtWidgets.QWizardPage()
self.uiServerWizardPage.setObjectName("uiServerWizardPage")
self.gridLayout_2 = QtWidgets.QGridLayout(self.uiServerWizardPage)
self.gridLayout_2.setObjectName("gridLayout_2")
self.uiServerTypeGroupBox = QtWidgets.QGroupBox(self.uiServerWizardPage)
self.uiServerTypeGroupBox.setObjectName("uiServerTypeGroupBox")
self.verticalLayout = QtWidgets.QVBoxLayout(self.uiServerTypeGroupBox)
self.verticalLayout.setObjectName("verticalLayout")
self.uiRemoteRadioButton = QtWidgets.QRadioButton(self.uiServerTypeGroupBox)
self.uiRemoteRadioButton.setEnabled(False)
self.uiRemoteRadioButton.setChecked(False)
self.uiRemoteRadioButton.setObjectName("uiRemoteRadioButton")
self.verticalLayout.addWidget(self.uiRemoteRadioButton)
self.uiVMRadioButton = QtWidgets.QRadioButton(self.uiServerTypeGroupBox)
self.uiVMRadioButton.setEnabled(False)
self.uiVMRadioButton.setObjectName("uiVMRadioButton")
self.verticalLayout.addWidget(self.uiVMRadioButton)
self.uiLocalRadioButton = QtWidgets.QRadioButton(self.uiServerTypeGroupBox)
self.uiLocalRadioButton.setChecked(True)
self.uiLocalRadioButton.setObjectName("uiLocalRadioButton")
self.verticalLayout.addWidget(self.uiLocalRadioButton)
self.gridLayout_2.addWidget(self.uiServerTypeGroupBox, 0, 0, 1, 1)
self.uiRemoteServersGroupBox = QtWidgets.QGroupBox(self.uiServerWizardPage)
self.uiRemoteServersGroupBox.setObjectName("uiRemoteServersGroupBox")
self.gridLayout_7 = QtWidgets.QGridLayout(self.uiRemoteServersGroupBox)
self.gridLayout_7.setObjectName("gridLayout_7")
self.uiRemoteServersLabel = QtWidgets.QLabel(self.uiRemoteServersGroupBox)
self.uiRemoteServersLabel.setObjectName("uiRemoteServersLabel")
self.gridLayout_7.addWidget(self.uiRemoteServersLabel, 0, 0, 1, 1)
self.uiRemoteServersComboBox = QtWidgets.QComboBox(self.uiRemoteServersGroupBox)
self.uiRemoteServersComboBox.setEnabled(False)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.uiRemoteServersComboBox.sizePolicy().hasHeightForWidth())
self.uiRemoteServersComboBox.setSizePolicy(sizePolicy)
self.uiRemoteServersComboBox.setObjectName("uiRemoteServersComboBox")
self.gridLayout_7.addWidget(self.uiRemoteServersComboBox, 0, 1, 1, 1)
self.gridLayout_2.addWidget(self.uiRemoteServersGroupBox, 1, 0, 1, 1)
TraceNGNodeWizard.addPage(self.uiServerWizardPage)
self.uiNameWizardPage = QtWidgets.QWizardPage()
self.uiNameWizardPage.setObjectName("uiNameWizardPage")
self.gridLayout = QtWidgets.QGridLayout(self.uiNameWizardPage)
self.gridLayout.setObjectName("gridLayout")
self.uiNameLabel = QtWidgets.QLabel(self.uiNameWizardPage)
self.uiNameLabel.setObjectName("uiNameLabel")
self.gridLayout.addWidget(self.uiNameLabel, 0, 0, 1, 1)
self.uiNameLineEdit = QtWidgets.QLineEdit(self.uiNameWizardPage)
self.uiNameLineEdit.setObjectName("uiNameLineEdit")
self.gridLayout.addWidget(self.uiNameLineEdit, 0, 1, 1, 1)
self.uiIPAddressLabel = QtWidgets.QLabel(self.uiNameWizardPage)
self.uiIPAddressLabel.setObjectName("uiIPAddressLabel")
self.gridLayout.addWidget(self.uiIPAddressLabel, 1, 0, 1, 1)
self.uiIPAddressLineEdit = QtWidgets.QLineEdit(self.uiNameWizardPage)
self.uiIPAddressLineEdit.setText("")
self.uiIPAddressLineEdit.setObjectName("uiIPAddressLineEdit")
self.gridLayout.addWidget(self.uiIPAddressLineEdit, 1, 1, 1, 1)
TraceNGNodeWizard.addPage(self.uiNameWizardPage)
self.retranslateUi(TraceNGNodeWizard)
QtCore.QMetaObject.connectSlotsByName(TraceNGNodeWizard)
def retranslateUi(self, TraceNGNodeWizard):
_translate = QtCore.QCoreApplication.translate
TraceNGNodeWizard.setWindowTitle(_translate("TraceNGNodeWizard", "New TraceNG node template"))
self.uiServerWizardPage.setTitle(_translate("TraceNGNodeWizard", "Server"))
self.uiServerWizardPage.setSubTitle(_translate("TraceNGNodeWizard", "Please choose a server type to run your new TraceNG node."))
self.uiServerTypeGroupBox.setTitle(_translate("TraceNGNodeWizard", "Server type"))
self.uiRemoteRadioButton.setText(_translate("TraceNGNodeWizard", "Run the TraceNG node on a remote computer"))
self.uiVMRadioButton.setText(_translate("TraceNGNodeWizard", "Run the TraceNG node on the GNS3 VM"))
self.uiLocalRadioButton.setText(_translate("TraceNGNodeWizard", "Run the TraceNG node on your local computer"))
self.uiRemoteServersGroupBox.setTitle(_translate("TraceNGNodeWizard", "Remote server"))
self.uiRemoteServersLabel.setText(_translate("TraceNGNodeWizard", "Run on:"))
self.uiNameWizardPage.setTitle(_translate("TraceNGNodeWizard", "Name and IP address"))
self.uiNameWizardPage.setSubTitle(_translate("TraceNGNodeWizard", "Please choose a descriptive name and IP address for the new TraceNG node."))
self.uiNameLabel.setText(_translate("TraceNGNodeWizard", "Name:"))
self.uiIPAddressLabel.setText(_translate("TraceNGNodeWizard", "IP address:"))

View File

@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TraceNGPreferencesPageWidget</class>
<widget class="QWidget" name="TraceNGPreferencesPageWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>623</width>
<height>280</height>
</rect>
</property>
<property name="windowTitle">
<string>TraceNG</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QTabWidget" name="uiTabWidget">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="uiGeneralSettingsTabWidget">
<attribute name="title">
<string>Local settings</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="margin">
<number>10</number>
</property>
<item>
<widget class="QLabel" name="uiTraceNGPathLabel">
<property name="text">
<string>Path to TraceNG executable:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLineEdit" name="uiTraceNGPathLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="uiTraceNGPathToolButton">
<property name="text">
<string>&amp;Browse...</string>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextOnly</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="spacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>138</width>
<height>17</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="uiRestoreDefaultsPushButton">
<property name="text">
<string>Restore defaults</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
<designerdata>
<property name="gridDeltaX">
<number>10</number>
</property>
<property name="gridDeltaY">
<number>10</number>
</property>
<property name="gridSnapX">
<bool>true</bool>
</property>
<property name="gridSnapY">
<bool>true</bool>
</property>
<property name="gridVisible">
<bool>true</bool>
</property>
</designerdata>
</ui>

View File

@@ -0,0 +1,67 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/modules/traceng/ui/traceng_preferences_page.ui'
#
# Created by: PyQt5 UI code generator 5.5.1
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_TraceNGPreferencesPageWidget(object):
def setupUi(self, TraceNGPreferencesPageWidget):
TraceNGPreferencesPageWidget.setObjectName("TraceNGPreferencesPageWidget")
TraceNGPreferencesPageWidget.resize(623, 280)
self.verticalLayout_2 = QtWidgets.QVBoxLayout(TraceNGPreferencesPageWidget)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.uiTabWidget = QtWidgets.QTabWidget(TraceNGPreferencesPageWidget)
self.uiTabWidget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.uiTabWidget.setObjectName("uiTabWidget")
self.uiGeneralSettingsTabWidget = QtWidgets.QWidget()
self.uiGeneralSettingsTabWidget.setObjectName("uiGeneralSettingsTabWidget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.uiGeneralSettingsTabWidget)
self.verticalLayout.setContentsMargins(10, 10, 10, 10)
self.verticalLayout.setObjectName("verticalLayout")
self.uiTraceNGPathLabel = QtWidgets.QLabel(self.uiGeneralSettingsTabWidget)
self.uiTraceNGPathLabel.setObjectName("uiTraceNGPathLabel")
self.verticalLayout.addWidget(self.uiTraceNGPathLabel)
self.horizontalLayout_5 = QtWidgets.QHBoxLayout()
self.horizontalLayout_5.setObjectName("horizontalLayout_5")
self.uiTraceNGPathLineEdit = QtWidgets.QLineEdit(self.uiGeneralSettingsTabWidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.uiTraceNGPathLineEdit.sizePolicy().hasHeightForWidth())
self.uiTraceNGPathLineEdit.setSizePolicy(sizePolicy)
self.uiTraceNGPathLineEdit.setObjectName("uiTraceNGPathLineEdit")
self.horizontalLayout_5.addWidget(self.uiTraceNGPathLineEdit)
self.uiTraceNGPathToolButton = QtWidgets.QToolButton(self.uiGeneralSettingsTabWidget)
self.uiTraceNGPathToolButton.setToolButtonStyle(QtCore.Qt.ToolButtonTextOnly)
self.uiTraceNGPathToolButton.setObjectName("uiTraceNGPathToolButton")
self.horizontalLayout_5.addWidget(self.uiTraceNGPathToolButton)
self.verticalLayout.addLayout(self.horizontalLayout_5)
spacerItem = QtWidgets.QSpacerItem(20, 5, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout.addItem(spacerItem)
self.uiTabWidget.addTab(self.uiGeneralSettingsTabWidget, "")
self.verticalLayout_2.addWidget(self.uiTabWidget)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
spacerItem1 = QtWidgets.QSpacerItem(138, 17, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_2.addItem(spacerItem1)
self.uiRestoreDefaultsPushButton = QtWidgets.QPushButton(TraceNGPreferencesPageWidget)
self.uiRestoreDefaultsPushButton.setObjectName("uiRestoreDefaultsPushButton")
self.horizontalLayout_2.addWidget(self.uiRestoreDefaultsPushButton)
self.verticalLayout_2.addLayout(self.horizontalLayout_2)
self.retranslateUi(TraceNGPreferencesPageWidget)
self.uiTabWidget.setCurrentIndex(0)
QtCore.QMetaObject.connectSlotsByName(TraceNGPreferencesPageWidget)
def retranslateUi(self, TraceNGPreferencesPageWidget):
_translate = QtCore.QCoreApplication.translate
TraceNGPreferencesPageWidget.setWindowTitle(_translate("TraceNGPreferencesPageWidget", "TraceNG"))
self.uiTraceNGPathLabel.setText(_translate("TraceNGPreferencesPageWidget", "Path to TraceNG executable:"))
self.uiTraceNGPathToolButton.setText(_translate("TraceNGPreferencesPageWidget", "&Browse..."))
self.uiTabWidget.setTabText(self.uiTabWidget.indexOf(self.uiGeneralSettingsTabWidget), _translate("TraceNGPreferencesPageWidget", "Local settings"))
self.uiRestoreDefaultsPushButton.setText(_translate("TraceNGPreferencesPageWidget", "Restore defaults"))

View File

@@ -77,10 +77,10 @@ class VPCS(Module):
# save the settings
LocalConfig.instance().saveSectionSettings(self.__class__.__name__, self._settings)
server_settings = copy.copy(self._settings)
if server_settings["vpcs_path"]:
server_settings = {}
if self._settings["vpcs_path"]:
# save some settings to the server config file
server_settings["vpcs_path"] = os.path.normpath(server_settings["vpcs_path"])
server_settings["vpcs_path"] = os.path.normpath(self._settings["vpcs_path"])
config = LocalServerConfig.instance()
config.saveSettings(self.__class__.__name__, server_settings)

BIN
resources/icons/traceng.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -64,6 +64,7 @@
<file>icons/virtualbox.png</file>
<file>icons/qemu.svg</file>
<file>icons/rtv.png</file>
<file>icons/traceng.png</file>
<file>icons/image.svg</file>
<file>icons/node_conception.svg</file>
<file>icons/raise_z_value.svg</file>