mirror of
https://github.com/GNS3/gns3-gui.git
synced 2026-06-06 02:32:04 +03:00
Compare commits
85 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2744e669b4 | ||
|
|
6ab2d63bdc | ||
|
|
0de6bfe7e1 | ||
|
|
f144103bca | ||
|
|
c0b26aff48 | ||
|
|
9601e4e6f2 | ||
|
|
88708c2a8d | ||
|
|
8eff12194d | ||
|
|
b0520b2bd4 | ||
|
|
17d2c023bf | ||
|
|
ce9fdea0a0 | ||
|
|
24d7dacb4e | ||
|
|
bb36765407 | ||
|
|
250db92ce0 | ||
|
|
d59ec39505 | ||
|
|
5e9ae04dc1 | ||
|
|
ddb0fccda3 | ||
|
|
9b22a52f14 | ||
|
|
948878bfdd | ||
|
|
7340abbaa9 | ||
|
|
4ea0528bf2 | ||
|
|
49005e6add | ||
|
|
5484c039b5 | ||
|
|
daaf71b6d2 | ||
|
|
450f0e006b | ||
|
|
a6a967fbde | ||
|
|
1a6293709e | ||
|
|
2ed53225e0 | ||
|
|
b8798fbda5 | ||
|
|
368de32faa | ||
|
|
98d01cbfa0 | ||
|
|
ad62bb7832 | ||
|
|
637061663a | ||
|
|
c137198985 | ||
|
|
946efb61de | ||
|
|
4c610acfa4 | ||
|
|
37f74824f1 | ||
|
|
5ccf8c414d | ||
|
|
913f0d5e4a | ||
|
|
061bac0cc6 | ||
|
|
ec59cd87bd | ||
|
|
05d9ee8499 | ||
|
|
a72ece5c18 | ||
|
|
63baa2eff0 | ||
|
|
b91fd4a0c2 | ||
|
|
718217e332 | ||
|
|
c202c5e4be | ||
|
|
71830dd69f | ||
|
|
37a7fdfa68 | ||
|
|
0efe006cad | ||
|
|
4a663a5910 | ||
|
|
a559bd4ae4 | ||
|
|
5ebb3011d3 | ||
|
|
81300fd40e | ||
|
|
d4dda2a285 | ||
|
|
5a4342d4b8 | ||
|
|
94fc5e6c4f | ||
|
|
a3e81fbf2e | ||
|
|
514eb97eac | ||
|
|
7637039cb2 | ||
|
|
ac989b191b | ||
|
|
c971cef31b | ||
|
|
c1af2df780 | ||
|
|
eaaa141be9 | ||
|
|
226169cdc6 | ||
|
|
42a4c89f20 | ||
|
|
1482b0e804 | ||
|
|
8ebe3435c4 | ||
|
|
a1cd34d7c4 | ||
|
|
1e4a44135c | ||
|
|
a407f1ec90 | ||
|
|
faab113384 | ||
|
|
c158b7fc46 | ||
|
|
16de9e830f | ||
|
|
25c625c0bb | ||
|
|
bf42d1a355 | ||
|
|
1c0f3493ee | ||
|
|
c3c1f87c5e | ||
|
|
6b80914385 | ||
|
|
a114d9ace7 | ||
|
|
4dca4d057a | ||
|
|
17af21e29a | ||
|
|
7fbce0266d | ||
|
|
d5cdbdbf90 | ||
|
|
e5a790f4b2 |
@@ -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=
|
||||
|
||||
57
CHANGELOG
57
CHANGELOG
@@ -1,5 +1,62 @@
|
||||
# Change Log
|
||||
|
||||
## 2.1.7 12/06/2018
|
||||
|
||||
* Do not try to update link if it is being deleted. Fixes #2483.
|
||||
* Fix can't add SVG image to project. Fixes #2502
|
||||
* Remove unwanted trailing characters and other white spaces when reading .md5sum files. Fixes #2498.
|
||||
* Update interface sequence number check. Fixes #2491.
|
||||
* Logo should not have context menu, Fixes: #2507
|
||||
* Update logo position only when changes, Fixes: #2506
|
||||
|
||||
## 2.1.6 22/05/2018
|
||||
|
||||
* Ask for global variables when project is loaded
|
||||
* Add/Edit global variables of project
|
||||
* Rename tabs at Edit Project
|
||||
* Global variables tab on Edit project
|
||||
* Support of supplier logo and url
|
||||
* Add missing crowdfunder name in About dialog.
|
||||
* Project variables and supplier
|
||||
* No timeout when duplicating a project.
|
||||
* No timeout when restoring snapshot.
|
||||
* Add advanced settings for docker and ExtraHosts param, Ref. #2482
|
||||
* Replace "not supported" by "none" in topology summary view.
|
||||
|
||||
## 2.1.5 18/04/2018
|
||||
|
||||
* Fix Qemu binary list locks when a version is deleted. Fixes #2474.
|
||||
* Fix invalid answer from the PyPi server. Fixes #2473.
|
||||
* Fix wrong wizard page name.
|
||||
* Grid size support for projects. Fixes #2469.
|
||||
* Remove 'include INSTALL' from MANIFEST. Fixes #2470.
|
||||
* Check for valid IP address and prevent to run on non-Windows platforms.
|
||||
|
||||
## 2.1.4 12/03/2018
|
||||
|
||||
* Update node on server on any change, Fixes: #2429
|
||||
* Mark IOU layer 1 keepalive messages feature as non-functional. Fixes #2431.
|
||||
* Images refresh when added via settings, Fixes:#2423
|
||||
* Emit project_loaded_signal after project creation
|
||||
* Add option Show interface labels on new project, Ref. #2308
|
||||
* Improve finding pyuic3.exe on Windows
|
||||
* Use debug for error downloading file messages. Fixes #2398.
|
||||
* Refresh buttons in the cloud node to query the server for available interfaces. Fixes #2416.
|
||||
* Handle Certifacte Error, Ref. gns3-server#1262
|
||||
* Backward compatibility for tests, Ref. #2405?
|
||||
* Use UTF-8 for IOURC file migration.
|
||||
* Look for symbols on controller, Ref. #2405
|
||||
* Display an error message if Telnet console program cannot be executed.
|
||||
|
||||
## 2.1.3 19/01/2018
|
||||
|
||||
* Change messages when there are different client and server versions. Fixes #2391.
|
||||
* Fix "Transport selection via DSN is deprecated" message. Sync is configured with HTTPTransport.
|
||||
* Refresh CPU/RAM info every 1 second. Ref #2262.
|
||||
* Only check for AVG on Windows
|
||||
* Improve the search for VBoxManage.
|
||||
* Allow telnet console to node with name containing double quotes. Fixes #2371.
|
||||
|
||||
## 2.1.2 08/01/2018
|
||||
|
||||
* Update VMware promotion in setup wizard.
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Run tests inside a container
|
||||
FROM ubuntu:yakkety
|
||||
FROM ubuntu:17.10
|
||||
|
||||
MAINTAINER GNS3 Team
|
||||
|
||||
#ENV DEBIAN_FRONTEND noninteractive
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y --force-yes python3.5 python3-pyqt5 python3-pip python3-pyqt5.qtsvg python3-pyqt5.qtwebsockets python3.5-dev xvfb
|
||||
RUN apt-get install -y --force-yes python3.6 python3-pyqt5 python3-pip python3-pyqt5.qtsvg python3-pyqt5.qtwebsockets python3.6-dev xvfb
|
||||
RUN apt-get clean
|
||||
|
||||
|
||||
@@ -19,4 +19,4 @@ ADD . /src
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
CMD xvfb-run python3.5 -m pytest -vv
|
||||
CMD xvfb-run python3.6 -m pytest -vv
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
include README.rst
|
||||
include AUTHORS
|
||||
include INSTALL
|
||||
include LICENSE
|
||||
include MANIFEST.in
|
||||
include requirements.txt
|
||||
|
||||
@@ -55,7 +55,7 @@ class ComputeManager(QtCore.QObject):
|
||||
def _refreshComputesSlot(self):
|
||||
if self._refreshingComputes:
|
||||
return
|
||||
if self._controller.connected() and datetime.datetime.now().timestamp() - self._last_computes_refresh > 5:
|
||||
if self._controller.connected() and datetime.datetime.now().timestamp() - self._last_computes_refresh > 1:
|
||||
self._last_computes_refresh = datetime.datetime.now().timestamp()
|
||||
self._refreshingComputes = True
|
||||
self._controller.get("/computes", self._listComputesCallback, showProgress=False, timeout=30)
|
||||
@@ -84,6 +84,7 @@ class ComputeManager(QtCore.QObject):
|
||||
Called when we received data from a compute
|
||||
node.
|
||||
"""
|
||||
|
||||
self._last_computes_refresh = datetime.datetime.now().timestamp()
|
||||
|
||||
new_node = False
|
||||
|
||||
@@ -237,13 +237,9 @@ class Controller(QtCore.QObject):
|
||||
if not self._http_client:
|
||||
return
|
||||
|
||||
m = hashlib.md5()
|
||||
m.update(url.encode())
|
||||
if ".svg" in url:
|
||||
extension = ".svg"
|
||||
else:
|
||||
extension = ".png"
|
||||
path = os.path.join(self._cache_directory, m.hexdigest() + extension)
|
||||
|
||||
path = self.getStaticCachedPath(url)
|
||||
|
||||
if os.path.exists(path):
|
||||
callback(path)
|
||||
elif path in self._static_asset_download_queue:
|
||||
@@ -263,8 +259,7 @@ class Controller(QtCore.QObject):
|
||||
self.getStatic(fallback, callback)
|
||||
fallback_used = True
|
||||
if fallback_used:
|
||||
log.error("Error while downloading file: {}".format(url))
|
||||
log.error("Error while downloading file: {}".format(url))
|
||||
log.debug("Error while downloading file: {}".format(url))
|
||||
del self._static_asset_download_queue[path]
|
||||
return
|
||||
try:
|
||||
@@ -278,6 +273,21 @@ class Controller(QtCore.QObject):
|
||||
callback(path)
|
||||
del self._static_asset_download_queue[path]
|
||||
|
||||
def getStaticCachedPath(self, url):
|
||||
"""
|
||||
Returns static cached (hashed) path
|
||||
:param url:
|
||||
:return:
|
||||
"""
|
||||
m = hashlib.md5()
|
||||
m.update(url.encode())
|
||||
if ".svg" in url:
|
||||
extension = ".svg"
|
||||
else:
|
||||
extension = ".png"
|
||||
path = os.path.join(self._cache_directory, m.hexdigest() + extension)
|
||||
return path
|
||||
|
||||
def getSymbolIcon(self, symbol_id, callback, fallback=None):
|
||||
"""
|
||||
Get a QIcon for a symbol from the controller
|
||||
@@ -298,6 +308,9 @@ class Controller(QtCore.QObject):
|
||||
icon.addFile(path)
|
||||
callback(icon)
|
||||
|
||||
def getSymbols(self, callback):
|
||||
self.get('/symbols', callback=callback)
|
||||
|
||||
def deleteProject(self, project_id, callback=None):
|
||||
Controller.instance().delete("/projects/{}".format(project_id), qpartial(self._deleteProjectCallback, callback=callback, project_id=project_id))
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ class CrashReport:
|
||||
Report crash to a third party service
|
||||
"""
|
||||
|
||||
DSN = "sync+https://5e56cf6924c94ad594e040a66735b112:3103c4e6e6564d68ab7623e9911b4db2@sentry.io/38506"
|
||||
DSN = "sync+https://806956d411fc4cdf8cda8f27b08079a8:a1d1a6aaa57f4c46a7db02d89f5fab95@sentry.io/38506"
|
||||
if hasattr(sys, "frozen"):
|
||||
cacert = get_resource("cacert.pem")
|
||||
if cacert is not None and os.path.isfile(cacert):
|
||||
|
||||
@@ -34,6 +34,9 @@ from ..controller import Controller
|
||||
from ..local_config import LocalConfig
|
||||
from ..image_upload_manager import ImageUploadManager
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ApplianceWizard(QtWidgets.QWizard, Ui_ApplianceWizard):
|
||||
images_changed_signal = QtCore.Signal()
|
||||
@@ -41,6 +44,7 @@ class ApplianceWizard(QtWidgets.QWizard, Ui_ApplianceWizard):
|
||||
|
||||
def __init__(self, parent, path):
|
||||
super().__init__(parent)
|
||||
|
||||
self.setupUi(self)
|
||||
self.images_changed_signal.connect(self._refreshVersions)
|
||||
self.versions_changed_signal.connect(self._versionRefreshedSlot)
|
||||
@@ -86,6 +90,10 @@ class ApplianceWizard(QtWidgets.QWizard, Ui_ApplianceWizard):
|
||||
|
||||
self.uiServerWizardPage.isComplete = self._uiServerWizardPage_isComplete
|
||||
|
||||
# symbols loaded from controller
|
||||
self._symbols = []
|
||||
|
||||
|
||||
def initializePage(self, page_id):
|
||||
"""
|
||||
Initialize Wizard pages.
|
||||
@@ -110,6 +118,8 @@ class ApplianceWizard(QtWidgets.QWizard, Ui_ApplianceWizard):
|
||||
type = "dynamips"
|
||||
|
||||
if self.page(page_id) == self.uiInfoWizardPage:
|
||||
Controller.instance().getSymbols(self._getSymbolsCallback)
|
||||
|
||||
self.uiInfoWizardPage.setTitle(self._appliance["product_name"])
|
||||
self.uiDescriptionLabel.setText(self._appliance["description"])
|
||||
|
||||
@@ -316,6 +326,12 @@ class ApplianceWizard(QtWidgets.QWizard, Ui_ApplianceWizard):
|
||||
self.uiApplianceVersionTreeWidget.resizeColumnToContents(1)
|
||||
self._refreshing = False
|
||||
|
||||
def _getSymbolsCallback(self, result, error=False, **kwargs):
|
||||
if error:
|
||||
log.warning("Cannot load symbols from controller")
|
||||
else:
|
||||
self._symbols = result
|
||||
|
||||
def _refreshDialogWorker(self):
|
||||
"""
|
||||
Scan local directory in order to found the images on disk
|
||||
@@ -482,7 +498,10 @@ class ApplianceWizard(QtWidgets.QWizard, Ui_ApplianceWizard):
|
||||
if "qemu" in appliance_configuration:
|
||||
appliance_configuration["qemu"]["path"] = self.uiQemuListComboBox.currentData()
|
||||
|
||||
worker = WaitForLambdaWorker(lambda: config.add_appliance(appliance_configuration, self._compute_id), allowed_exceptions=[ConfigException, OSError])
|
||||
worker = WaitForLambdaWorker(
|
||||
lambda: config.add_appliance(appliance_configuration, self._compute_id, self._symbols),
|
||||
allowed_exceptions=[ConfigException, OSError])
|
||||
|
||||
progress_dialog = ProgressDialog(worker, "Add appliance", "Install the appliance...", None, busy=True, parent=self)
|
||||
progress_dialog.show()
|
||||
if not progress_dialog.exec_():
|
||||
|
||||
@@ -95,13 +95,14 @@ class DoctorDialog(QtWidgets.QDialog, Ui_DoctorDialog):
|
||||
def checkAVGInstalled(self):
|
||||
"""Checking if AVG software is not installed"""
|
||||
|
||||
for proc in psutil.process_iter():
|
||||
try:
|
||||
psinfo = proc.as_dict(["exe"])
|
||||
if psinfo["exe"] and "AVG\\" in psinfo["exe"]:
|
||||
return (2, "AVG has known issues with GNS3, even after you disable it. You must whitelist dynamips.exe in the AVG preferences.")
|
||||
except psutil.NoSuchProcess:
|
||||
pass
|
||||
if sys.platform.startswith("win32"):
|
||||
for proc in psutil.process_iter():
|
||||
try:
|
||||
psinfo = proc.as_dict(["exe"])
|
||||
if psinfo["exe"] and "AVG\\" in psinfo["exe"]:
|
||||
return (2, "AVG has known issues with GNS3, even after you disable it. You must whitelist dynamips.exe in the AVG preferences.")
|
||||
except psutil.NoSuchProcess:
|
||||
pass
|
||||
return (0, None)
|
||||
|
||||
def checkFreeRam(self):
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from ..qt import QtWidgets
|
||||
from ..qt import QtWidgets, QtCore, qslot, qpartial
|
||||
from ..topology import Topology
|
||||
from ..ui.edit_project_dialog_ui import Ui_EditProjectDialog
|
||||
|
||||
@@ -36,6 +36,68 @@ class EditProjectDialog(QtWidgets.QDialog, Ui_EditProjectDialog):
|
||||
self.uiProjectAutoStartCheckBox.setChecked(self._project.autoStart())
|
||||
self.uiSceneWidthSpinBox.setValue(self._project.sceneWidth())
|
||||
self.uiSceneHeightSpinBox.setValue(self._project.sceneHeight())
|
||||
self.uiGridSizeSpinBox.setValue(self._project.gridSize())
|
||||
|
||||
self.uiGlobalVariablesGrid.setAlignment(QtCore.Qt.AlignTop)
|
||||
|
||||
self.uiNewVarButton = QtWidgets.QPushButton('Add new variable', self)
|
||||
self.uiNewVarButton.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
|
||||
self.uiNewVarButton.clicked.connect(self.onAddNewVariable)
|
||||
self.uiGlobalVariablesGrid.addWidget(self.uiNewVarButton, 0, 3, QtCore.Qt.AlignRight)
|
||||
|
||||
self._variables = self.setUpVariables()
|
||||
self.updateGlobalVariables()
|
||||
|
||||
def setUpVariables(self):
|
||||
new_variable = {"name": "", "value": ""}
|
||||
variables = self._project.variables()
|
||||
|
||||
if variables is not None:
|
||||
variables.append(new_variable)
|
||||
else:
|
||||
variables = [new_variable]
|
||||
return variables
|
||||
|
||||
def updateGlobalVariables(self):
|
||||
while True:
|
||||
item = self.uiGlobalVariablesGrid.takeAt(1)
|
||||
if item is None:
|
||||
break
|
||||
elif item.widget():
|
||||
item.widget().deleteLater()
|
||||
|
||||
for i, variable in enumerate(self._variables, start=1):
|
||||
nameLabel = QtWidgets.QLabel()
|
||||
nameLabel.setText("Name:")
|
||||
self.uiGlobalVariablesGrid.addWidget(nameLabel, i, 0)
|
||||
|
||||
nameEdit = QtWidgets.QLineEdit()
|
||||
nameEdit.setText(variable.get("name", ""))
|
||||
nameEdit.textChanged.connect(qpartial(self.onNameChange, variable))
|
||||
self.uiGlobalVariablesGrid.addWidget(nameEdit, i, 1)
|
||||
|
||||
valueLabel = QtWidgets.QLabel()
|
||||
valueLabel.setText("Value:")
|
||||
self.uiGlobalVariablesGrid.addWidget(valueLabel, i, 2)
|
||||
|
||||
valueEdit = QtWidgets.QLineEdit()
|
||||
valueEdit.setText(variable.get("value", ""))
|
||||
valueEdit.textChanged.connect(qpartial(self.onValueChange, variable))
|
||||
self.uiGlobalVariablesGrid.addWidget(valueEdit, i, 3)
|
||||
|
||||
@qslot
|
||||
def onAddNewVariable(self, event):
|
||||
self._variables += [{"name": "", "value": ""}]
|
||||
self.updateGlobalVariables()
|
||||
|
||||
def onNameChange(self, variable, text):
|
||||
variable["name"] = text
|
||||
|
||||
def onValueChange(self, variable, text):
|
||||
variable["value"] = text
|
||||
|
||||
def _cleanVariables(self):
|
||||
return [v for v in self._variables if v.get("name", "").strip() != ""]
|
||||
|
||||
def done(self, result):
|
||||
"""
|
||||
@@ -51,5 +113,7 @@ class EditProjectDialog(QtWidgets.QDialog, Ui_EditProjectDialog):
|
||||
self._project.setAutoStart(self.uiProjectAutoStartCheckBox.isChecked())
|
||||
self._project.setSceneHeight(self.uiSceneHeightSpinBox.value())
|
||||
self._project.setSceneWidth(self.uiSceneWidthSpinBox.value())
|
||||
self._project.setGridSize(self.uiGridSizeSpinBox.value())
|
||||
self._project.setVariables(self._cleanVariables())
|
||||
self._project.update()
|
||||
super().done(result)
|
||||
|
||||
@@ -106,6 +106,7 @@ class ProjectDialog(QtWidgets.QDialog, Ui_ProjectDialog):
|
||||
Controller.instance().deleteProject(project_id)
|
||||
|
||||
def _duplicateProjectSlot(self):
|
||||
|
||||
if len(self.uiProjectsTreeWidget.selectedItems()) == 0:
|
||||
QtWidgets.QMessageBox.critical(self, "Duplicate project", "No project selected")
|
||||
return
|
||||
@@ -135,12 +136,16 @@ class ProjectDialog(QtWidgets.QDialog, Ui_ProjectDialog):
|
||||
if Controller.instance().isRemote():
|
||||
Controller.instance().post("/projects/{project_id}/duplicate".format(project_id=project_id),
|
||||
self._duplicateCallback,
|
||||
body={"name": name})
|
||||
body={"name": name},
|
||||
progressText="Duplicating project '{}'...".format(name),
|
||||
timeout=None)
|
||||
else:
|
||||
project_location = os.path.join(Topology.instance().projectsDirPath(), name)
|
||||
Controller.instance().post("/projects/{project_id}/duplicate".format(project_id=project_id),
|
||||
self._duplicateCallback,
|
||||
body={"name": name, "path": project_location})
|
||||
body={"name": name, "path": project_location},
|
||||
progressText="Duplicating project '{}'...".format(name),
|
||||
timeout=None)
|
||||
|
||||
def _duplicateCallback(self, result, error=False, **kwargs):
|
||||
if error:
|
||||
|
||||
88
gns3/dialogs/project_welcome_dialog.py
Normal file
88
gns3/dialogs/project_welcome_dialog.py
Normal file
@@ -0,0 +1,88 @@
|
||||
# -*- 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/>.
|
||||
|
||||
import copy
|
||||
|
||||
from gns3.qt import QtWidgets, QtCore, qpartial
|
||||
from gns3.ui.project_welcome_dialog_ui import Ui_ProjectWelcomeDialog
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ProjectWelcomeDialog(QtWidgets.QDialog, Ui_ProjectWelcomeDialog):
|
||||
"""
|
||||
This dialog shows when project is imported and global variables assigned to the project are missing.
|
||||
"""
|
||||
|
||||
def __init__(self, parent, project):
|
||||
|
||||
super().__init__(parent)
|
||||
self._project = project
|
||||
self.setupUi(self)
|
||||
self.uiOkButton.clicked.connect(self._okButtonClickedSlot)
|
||||
self.gridLayout.setAlignment(QtCore.Qt.AlignTop)
|
||||
self.label.setOpenExternalLinks(True)
|
||||
|
||||
self._variables = self._getVariables(project)
|
||||
|
||||
self._loadReadme()
|
||||
self._addMisingVariablesEdits()
|
||||
|
||||
def _getVariables(self, project):
|
||||
variables = copy.copy(self._project.variables())
|
||||
if variables is None:
|
||||
variables = []
|
||||
return variables
|
||||
|
||||
def _addMisingVariablesEdits(self):
|
||||
missing = [v for v in self._variables if v.get("value", "").strip() == ""]
|
||||
for i, variable in enumerate(missing, start=0):
|
||||
nameLabel = QtWidgets.QLabel()
|
||||
nameLabel.setText(variable.get("name", ""))
|
||||
self.gridLayout.addWidget(nameLabel, i, 0)
|
||||
|
||||
valueEdit = QtWidgets.QLineEdit()
|
||||
valueEdit.setText(variable.get("value", ""))
|
||||
valueEdit.textChanged.connect(qpartial(self.onValueChange, variable))
|
||||
self.gridLayout.addWidget(valueEdit, i, 1)
|
||||
|
||||
def _loadReadme(self):
|
||||
self._project.get("/files/README.txt", self._loadedReadme)
|
||||
|
||||
def _loadedReadme(self, result, error=False, raw_body=None, context={}, **kwargs):
|
||||
if not error:
|
||||
self.label.setText(raw_body.decode("utf-8"))
|
||||
|
||||
def onValueChange(self, variable, text):
|
||||
variable["value"] = text
|
||||
|
||||
def _okButtonClickedSlot(self):
|
||||
missing = [v for v in self._variables if v.get("value", "").strip() == ""]
|
||||
if len(missing) > 0:
|
||||
reply = QtWidgets.QMessageBox.warning(
|
||||
self, 'Missing values',
|
||||
'Are you sure you want to continue without providing missing values?',
|
||||
QtWidgets.QMessageBox.Yes,
|
||||
QtWidgets.QMessageBox.No)
|
||||
if reply == QtWidgets.QMessageBox.No:
|
||||
return
|
||||
|
||||
self._project.setVariables(self._variables)
|
||||
self._project.update()
|
||||
self.accept()
|
||||
|
||||
@@ -22,8 +22,6 @@ Dialog to manage the snapshots.
|
||||
from ..qt import QtCore, QtWidgets
|
||||
from ..ui.snapshots_dialog_ui import Ui_SnapshotsDialog
|
||||
from ..controller import Controller
|
||||
from ..utils.progress_dialog import ProgressDialog
|
||||
from ..utils.create_snapshot_worker import CreateSnapshotWorker
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
@@ -87,21 +85,21 @@ class SnapshotsDialog(QtWidgets.QDialog, Ui_SnapshotsDialog):
|
||||
|
||||
snapshot_name, ok = QtWidgets.QInputDialog.getText(self, "Snapshot", "Snapshot name:", QtWidgets.QLineEdit.Normal, "Unnamed")
|
||||
if ok and snapshot_name and self._project:
|
||||
snapshot_worker = CreateSnapshotWorker(self._project, snapshot_name)
|
||||
snapshot_worker.finished.connect(self._createSnapshotsCallback)
|
||||
Controller.instance().post("/projects/{}/snapshots".format(self._project.id()),
|
||||
self._createSnapshotsCallback,
|
||||
{"name": snapshot_name},
|
||||
progressText="Creation of snapshot '{}' in progress...".format(snapshot_name),
|
||||
timeout=None)
|
||||
|
||||
progress_dialog = ProgressDialog(snapshot_worker, "Snapshot progress", "Creation of snapshot in progress...",
|
||||
"Cancel", busy=True, parent=self, create_thread=False, cancelable=True)
|
||||
progress_dialog.show()
|
||||
progress_dialog.exec_()
|
||||
|
||||
|
||||
def _createSnapshotsCallback(self):
|
||||
def _createSnapshotsCallback(self, result, error=False, server=None, context={}, **kwargs):
|
||||
if error:
|
||||
if result:
|
||||
log.error(result["message"])
|
||||
else:
|
||||
log.error("Cannot create snapshot of project")
|
||||
return
|
||||
self._listSnapshots()
|
||||
|
||||
def _createSnapshotsErrorCallback(self, message, error):
|
||||
log.error(message)
|
||||
|
||||
def _deleteSnapshotSlot(self):
|
||||
"""
|
||||
Slot to delete a snapshot.
|
||||
@@ -113,6 +111,7 @@ class SnapshotsDialog(QtWidgets.QDialog, Ui_SnapshotsDialog):
|
||||
Controller.instance().delete("/projects/{}/snapshots/{}".format(self._project.id(), snapshot_id), self._deleteSnapshotsCallback)
|
||||
|
||||
def _deleteSnapshotsCallback(self, result, error=False, server=None, context={}, **kwargs):
|
||||
|
||||
if error:
|
||||
if result:
|
||||
log.error(result["message"])
|
||||
@@ -135,13 +134,16 @@ class SnapshotsDialog(QtWidgets.QDialog, Ui_SnapshotsDialog):
|
||||
|
||||
:param snapshot_id: id of the snapshot
|
||||
"""
|
||||
reply = QtWidgets.QMessageBox.question(self, "Snapshots", "This will discard any changes made to your project since the snapshot was taken?", QtWidgets.QMessageBox.Ok, QtWidgets.QMessageBox.Cancel)
|
||||
|
||||
reply = QtWidgets.QMessageBox.question(self, "Snapshots", "This will discard any changes made to your project since the snapshot was taken, would you like to proceed?", QtWidgets.QMessageBox.Ok, QtWidgets.QMessageBox.Cancel)
|
||||
if reply == QtWidgets.QMessageBox.Cancel:
|
||||
return
|
||||
|
||||
Controller.instance().post("/projects/{}/snapshots/{}/restore".format(self._project.id(), snapshot_id), self._restoreSnapshotsCallback, timeout=300)
|
||||
Controller.instance().post("/projects/{}/snapshots/{}/restore".format(self._project.id(), snapshot_id),
|
||||
self._restoreSnapshotsCallback, progressText="Restoring snapshot...", timeout=None)
|
||||
|
||||
def _restoreSnapshotsCallback(self, result, error=False, server=None, context={}, **kwargs):
|
||||
|
||||
if error:
|
||||
if result:
|
||||
log.error(result["message"])
|
||||
|
||||
@@ -60,6 +60,7 @@ from .items.rectangle_item import RectangleItem
|
||||
from .items.line_item import LineItem
|
||||
from .items.ellipse_item import EllipseItem
|
||||
from .items.image_item import ImageItem
|
||||
from .items.logo_item import LogoItem
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -88,6 +89,7 @@ class GraphicsView(QtWidgets.QGraphicsView):
|
||||
self._adding_line = False
|
||||
self._newlink = None
|
||||
self._dragging = False
|
||||
self._grid_size = 75
|
||||
self._last_mouse_position = None
|
||||
self._topology = Topology.instance()
|
||||
self._background_warning_msgbox = QtWidgets.QErrorMessage(self)
|
||||
@@ -127,6 +129,24 @@ class GraphicsView(QtWidgets.QGraphicsView):
|
||||
factor = zoom / 100.
|
||||
self.scale(factor, factor)
|
||||
|
||||
def setGridSize(self, grid_size):
|
||||
"""
|
||||
Sets the grid size.
|
||||
|
||||
:param grid_size: integer
|
||||
"""
|
||||
|
||||
self._grid_size = grid_size
|
||||
|
||||
def gridSize(self):
|
||||
"""
|
||||
Returns the grid size
|
||||
|
||||
:returns: integer
|
||||
"""
|
||||
|
||||
return self._grid_size
|
||||
|
||||
def setEnabled(self, enabled):
|
||||
|
||||
if enabled is False:
|
||||
@@ -275,6 +295,10 @@ class GraphicsView(QtWidgets.QGraphicsView):
|
||||
self.scene().addItem(image_item)
|
||||
self._topology.addDrawing(image_item)
|
||||
|
||||
def addLogo(self, logo_path, logo_url):
|
||||
logo_item = LogoItem(logo_path, logo_url, self._topology.project())
|
||||
self.scene().addItem(logo_item)
|
||||
|
||||
def addLink(self, source_node, source_port, destination_node, destination_port, **link_data):
|
||||
"""
|
||||
Creates a Link instance representing a connection between 2 devices.
|
||||
@@ -395,12 +419,16 @@ class GraphicsView(QtWidgets.QGraphicsView):
|
||||
"""
|
||||
|
||||
is_not_link = True
|
||||
is_not_logo = True
|
||||
|
||||
item = self.itemAt(event.pos())
|
||||
if item and sip.isdeleted(item):
|
||||
return
|
||||
|
||||
if item and (isinstance(item, LinkItem) or isinstance(item.parentItem(), LinkItem)):
|
||||
is_not_link = False
|
||||
if item and (isinstance(item, LogoItem) or isinstance(item.parentItem(), LogoItem)):
|
||||
is_not_logo = False
|
||||
else:
|
||||
for it in self.scene().items():
|
||||
if isinstance(it, LinkItem):
|
||||
@@ -420,7 +448,7 @@ class GraphicsView(QtWidgets.QGraphicsView):
|
||||
item.setSelected(False)
|
||||
else:
|
||||
item.setSelected(True)
|
||||
elif is_not_link and event.button() == QtCore.Qt.RightButton and not self._adding_link:
|
||||
elif is_not_link and is_not_logo and event.button() == QtCore.Qt.RightButton and not self._adding_link:
|
||||
if item and not sip.isdeleted(item):
|
||||
# Prevent right clicking on a selected item from de-selecting all other items
|
||||
if not item.isSelected():
|
||||
@@ -1547,21 +1575,21 @@ class GraphicsView(QtWidgets.QGraphicsView):
|
||||
def drawBackground(self, painter, rect):
|
||||
super().drawBackground(painter, rect)
|
||||
if self._main_window.uiShowGridAction.isChecked():
|
||||
gridSize = 75
|
||||
grid_size = self.gridSize()
|
||||
painter.save()
|
||||
painter.setPen(QtGui.QPen(QtGui.QColor(190, 190, 190)))
|
||||
|
||||
left = int(rect.left()) - (int(rect.left()) % gridSize)
|
||||
top = int(rect.top()) - (int(rect.top()) % gridSize)
|
||||
left = int(rect.left()) - (int(rect.left()) % grid_size)
|
||||
top = int(rect.top()) - (int(rect.top()) % grid_size)
|
||||
|
||||
x = left
|
||||
while x < rect.right():
|
||||
painter.drawLine(x, rect.top(), x, rect.bottom())
|
||||
x += gridSize
|
||||
x += grid_size
|
||||
y = top
|
||||
while y < rect.bottom():
|
||||
painter.drawLine(rect.left(), y, rect.right(), y)
|
||||
y += gridSize
|
||||
y += grid_size
|
||||
painter.restore()
|
||||
|
||||
def toggleUiDeviceMenu(self):
|
||||
|
||||
@@ -391,21 +391,22 @@ class HTTPClient(QtCore.QObject):
|
||||
return
|
||||
|
||||
if params["version"].split("-")[0] != __version__.split("-")[0]:
|
||||
msg = "Client version {} differs with server version {}".format(__version__, params["version"])
|
||||
log.error(msg)
|
||||
msg = "Client version {} is not the same as server version {}".format(__version__, params["version"])
|
||||
# Stable release
|
||||
if __version_info__[3] == 0:
|
||||
log.error(msg)
|
||||
for request, callback in self._query_waiting_connections:
|
||||
if callback is not None:
|
||||
callback({"message": msg}, error=True, server=server)
|
||||
return
|
||||
# We don't allow different major version to interact even with dev build
|
||||
elif parse_version(__version__)[:2] != parse_version(params["version"])[:2]:
|
||||
log.error(msg)
|
||||
for request, callback in self._query_waiting_connections:
|
||||
if callback is not None:
|
||||
callback({"message": msg}, error=True, server=server)
|
||||
return
|
||||
log.warning("Use a different client and server version can create bugs. Use it at your own risk.")
|
||||
log.warning("{}\nUsing different versions may result in unexpected problems. Please use at your own risk.".format(msg))
|
||||
|
||||
self._connected = True
|
||||
self._retry = 0
|
||||
|
||||
@@ -188,11 +188,11 @@ class DrawingItem:
|
||||
|
||||
def itemChange(self, change, value):
|
||||
if change == QtWidgets.QGraphicsItem.ItemPositionHasChanged and self.isActive() and self._main_window.uiSnapToGridAction.isChecked():
|
||||
GRID_SIZE = 75
|
||||
grid_size = self._graphics_view.gridSize()
|
||||
mid_x = self.boundingRect().width() / 2
|
||||
tmp_x = (GRID_SIZE * round((self.x() + mid_x) / GRID_SIZE)) - mid_x
|
||||
tmp_x = (grid_size * round((self.x() + mid_x) / grid_size)) - mid_x
|
||||
mid_y = self.boundingRect().height() / 2
|
||||
tmp_y = (GRID_SIZE * round((self.y() + mid_y) / GRID_SIZE)) - mid_y
|
||||
tmp_y = (grid_size * round((self.y() + mid_y) / grid_size)) - mid_y
|
||||
if tmp_x != self.x() and tmp_y != self.y():
|
||||
self.setPos(tmp_x, tmp_y)
|
||||
|
||||
|
||||
136
gns3/items/logo_item.py
Normal file
136
gns3/items/logo_item.py
Normal file
@@ -0,0 +1,136 @@
|
||||
# -*- 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/>.
|
||||
|
||||
|
||||
import urllib.parse
|
||||
|
||||
from ..qt import QtCore, QtGui, QtWidgets, QtSvg
|
||||
from ..qt.qimage_svg_renderer import QImageSvgRenderer
|
||||
from ..controller import Controller
|
||||
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LogoItem(QtSvg.QGraphicsSvgItem):
|
||||
"""
|
||||
Margin for the logo
|
||||
"""
|
||||
MARGIN = 20
|
||||
|
||||
"""
|
||||
Logo for the scene.
|
||||
|
||||
:param logo_path: Path to the logo (remote)
|
||||
:param logo_url: URL which needs to be open user clicks on the logo
|
||||
:param project: Current project
|
||||
"""
|
||||
|
||||
def __init__(self, logo_path, logo_url, project):
|
||||
super().__init__()
|
||||
|
||||
self._logo_path = logo_path
|
||||
self._logo_url = logo_url
|
||||
self._project = project
|
||||
|
||||
# Temporary symbol during loading
|
||||
renderer = QImageSvgRenderer(":/icons/reload.svg")
|
||||
renderer.setObjectName("symbol_loading")
|
||||
self.setSharedRenderer(renderer)
|
||||
|
||||
effect = QtWidgets.QGraphicsColorizeEffect()
|
||||
effect.setColor(QtGui.QColor("black"))
|
||||
effect.setStrength(0.8)
|
||||
self.setGraphicsEffect(effect)
|
||||
self.graphicsEffect().setEnabled(False)
|
||||
|
||||
# set graphical settings for this item
|
||||
self.setFlag(QtWidgets.QGraphicsItem.ItemIsFocusable)
|
||||
self.setFlag(QtWidgets.QGraphicsItem.ItemSendsGeometryChanges)
|
||||
self.setAcceptHoverEvents(True)
|
||||
|
||||
from ..main_window import MainWindow
|
||||
self._main_window = MainWindow.instance()
|
||||
self._settings = self._main_window.uiGraphicsView.settings()
|
||||
|
||||
self.updatePosition()
|
||||
|
||||
self._main_window.uiGraphicsView.viewport().installEventFilter(self)
|
||||
|
||||
remote_file = urllib.parse.quote('project-files/images/{}'.format(logo_path))
|
||||
|
||||
Controller.instance().getStatic(
|
||||
'/projects/{}/files/{}'.format(project.id(), remote_file),
|
||||
self.updateImage
|
||||
)
|
||||
|
||||
# make it the last one
|
||||
self.setZValue(-2)
|
||||
|
||||
def eventFilter(self, source, event):
|
||||
if event.type() == QtCore.QEvent.Paint:
|
||||
self.updatePosition()
|
||||
return QtWidgets.QWidget.eventFilter(self, source, event)
|
||||
|
||||
|
||||
def updateImage(self, local_path):
|
||||
renderer = QImageSvgRenderer(local_path)
|
||||
renderer.setObjectName("project_logo")
|
||||
self.setSharedRenderer(renderer)
|
||||
|
||||
|
||||
def updatePosition(self):
|
||||
"""
|
||||
Updates position to be located in the right bottom corner
|
||||
"""
|
||||
logo_rect = self.boundingRect()
|
||||
width = self._main_window.uiGraphicsView.viewport().width()
|
||||
height = self._main_window.uiGraphicsView.viewport().height()
|
||||
rect = self._main_window.uiGraphicsView.mapToScene(QtCore.QRect(0, 0, width, height)).boundingRect()
|
||||
x = rect.x() + rect.width() - self.MARGIN - logo_rect.width()
|
||||
y = rect.y() + rect.height() - self.MARGIN - logo_rect.height()
|
||||
|
||||
# update only when changes
|
||||
if [int(self.x()), int(self.y())] != [int(x), int(y)]:
|
||||
self.setX(x)
|
||||
self.setY(y)
|
||||
self.update()
|
||||
|
||||
def hoverEnterEvent(self, event):
|
||||
"""
|
||||
Handles all hover enter events for this item.
|
||||
|
||||
:param event: QGraphicsSceneHoverEvent instance
|
||||
"""
|
||||
|
||||
if self._logo_url is not None:
|
||||
self.graphicsEffect().setEnabled(True)
|
||||
|
||||
def hoverLeaveEvent(self, event):
|
||||
"""
|
||||
Handles all hover leave events for this item.
|
||||
|
||||
:param event: QGraphicsSceneHoverEvent instance
|
||||
"""
|
||||
|
||||
self.graphicsEffect().setEnabled(False)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
url = QtCore.QUrl(self._logo_url)
|
||||
if not QtGui.QDesktopServices.openUrl(url):
|
||||
QtWidgets.QMessageBox.warning(self, 'Open Url', 'Could not open url')
|
||||
@@ -41,7 +41,6 @@ class NodeItem(QtSvg.QGraphicsSvgItem):
|
||||
"""
|
||||
|
||||
show_layer = False
|
||||
GRID_SIZE = 75
|
||||
|
||||
def __init__(self, node):
|
||||
super().__init__()
|
||||
@@ -108,18 +107,19 @@ class NodeItem(QtSvg.QGraphicsSvgItem):
|
||||
self.createdSlot(node.id())
|
||||
|
||||
def _snapToGrid(self):
|
||||
|
||||
grid_size = self._main_window.uiGraphicsView.gridSize()
|
||||
mid_x = self.boundingRect().width() / 2
|
||||
x = (self.GRID_SIZE * round((self.x() + mid_x) / self.GRID_SIZE)) - mid_x
|
||||
x = (grid_size * round((self.x() + mid_x) / grid_size)) - mid_x
|
||||
mid_y = self.boundingRect().height() / 2
|
||||
y = (self.GRID_SIZE * round((self.y() + mid_y) / self.GRID_SIZE)) - mid_y
|
||||
y = (grid_size * round((self.y() + mid_y) / grid_size)) - mid_y
|
||||
self.setPos(x, y)
|
||||
|
||||
def updateNode(self):
|
||||
"""
|
||||
Sync change to the node
|
||||
"""
|
||||
if self._initialized:
|
||||
self._node.setGraphics(self)
|
||||
self._node.setGraphics(self)
|
||||
|
||||
@qslot
|
||||
def setSymbol(self, symbol):
|
||||
@@ -458,10 +458,11 @@ class NodeItem(QtSvg.QGraphicsSvgItem):
|
||||
"""
|
||||
|
||||
if change == QtWidgets.QGraphicsItem.ItemPositionChange and self.isActive() and self._main_window.uiSnapToGridAction.isChecked():
|
||||
grid_size = self._main_window.uiGraphicsView.gridSize()
|
||||
mid_x = self.boundingRect().width() / 2
|
||||
value.setX((self.GRID_SIZE * round((value.x() + mid_x) / self.GRID_SIZE)) - mid_x)
|
||||
value.setX((grid_size * round((value.x() + mid_x) / grid_size)) - mid_x)
|
||||
mid_y = self.boundingRect().height() / 2
|
||||
value.setY((self.GRID_SIZE * round((value.y() + mid_y) / self.GRID_SIZE)) - mid_y)
|
||||
value.setY((grid_size * round((value.y() + mid_y) / grid_size)) - mid_y)
|
||||
|
||||
# dynamically change the renderer when this node item is selected/unselected.
|
||||
if change == QtWidgets.QGraphicsItem.ItemSelectedChange:
|
||||
|
||||
20
gns3/link.py
20
gns3/link.py
@@ -76,6 +76,7 @@ class Link(QtCore.QObject):
|
||||
self._destination_label = None
|
||||
self._link_id = link_id
|
||||
self._capturing = False
|
||||
self._deleting = False
|
||||
self._capture_file_path = None
|
||||
self._capture_file = None
|
||||
self._initialized = False
|
||||
@@ -158,7 +159,7 @@ class Link(QtCore.QObject):
|
||||
self._updateLabels()
|
||||
|
||||
def update(self):
|
||||
if not self._link_id:
|
||||
if not self._link_id or self.deleting():
|
||||
return
|
||||
body = self._prepareParams()
|
||||
Controller.instance().put("/projects/{project_id}/links/{link_id}".format(project_id=self._source_node.project().id(), link_id=self._link_id), self.updateLinkCallback, body=body)
|
||||
@@ -244,6 +245,19 @@ class Link(QtCore.QObject):
|
||||
def link_id(self):
|
||||
return self._link_id
|
||||
|
||||
def deleting(self):
|
||||
"""
|
||||
Is the link being deleted
|
||||
"""
|
||||
return self._deleting
|
||||
|
||||
def setDeleting(self):
|
||||
"""
|
||||
Mark this link as being deleted
|
||||
"""
|
||||
|
||||
self._deleting = True
|
||||
|
||||
def capturing(self):
|
||||
"""
|
||||
Is a capture running on the link?
|
||||
@@ -306,8 +320,10 @@ class Link(QtCore.QObject):
|
||||
if skip_controller:
|
||||
self._linkDeletedCallback({})
|
||||
else:
|
||||
self.setDeleting()
|
||||
Controller.instance().delete("/projects/{project_id}/links/{link_id}".format(project_id=self.project().id(),
|
||||
link_id=self._link_id), self._linkDeletedCallback)
|
||||
link_id=self._link_id),
|
||||
self._linkDeletedCallback)
|
||||
|
||||
def _linkDeletedCallback(self, result, error=False, **kwargs):
|
||||
"""
|
||||
|
||||
@@ -265,11 +265,13 @@ class LocalConfig(QtCore.QObject):
|
||||
if "version" not in self._settings or parse_version(self._settings["version"]) < parse_version("2.0.0"):
|
||||
if "IOU" in self._settings and "iourc_path" in self._settings["IOU"] and "iourc_content" not in self._settings["IOU"]:
|
||||
try:
|
||||
with open(self._settings["IOU"]["iourc_path"], "r") as f:
|
||||
with open(self._settings["IOU"]["iourc_path"], "r", encoding="utf-8") as f:
|
||||
self._settings["IOU"]["iourc_content"] = f.read().replace("\r\n", "\n")
|
||||
del self._settings["IOU"]["iourc_path"]
|
||||
except OSError as e:
|
||||
log.warn("Can't import IOU licence {}: {}".format(self._settings["IOU"]["iourc_path"], str(e)))
|
||||
log.warning("Can't import IOU licence {}: {}".format(self._settings["IOU"]["iourc_path"], str(e)))
|
||||
except UnicodeDecodeError as e:
|
||||
log.warning("Non ascii characters in iourc file {}, please remove them: {}".format(self._settings["IOU"]["iourc_path"], str(e)))
|
||||
|
||||
def _readConfig(self, config_path):
|
||||
"""
|
||||
@@ -317,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:
|
||||
@@ -475,6 +477,21 @@ class LocalConfig(QtCore.QObject):
|
||||
settings["direct_file_upload"] = value
|
||||
self.saveSectionSettings("MainWindow", settings)
|
||||
|
||||
def showInterfaceLabelsOnNewProject(self):
|
||||
"""
|
||||
:returns: Boolean. True if show_interface_labels_on_new_project is enabled
|
||||
"""
|
||||
|
||||
from gns3.settings import GRAPHICS_VIEW_SETTINGS
|
||||
return self.loadSectionSettings("GraphicsView", GRAPHICS_VIEW_SETTINGS) \
|
||||
.get("show_interface_labels_on_new_project", False)
|
||||
|
||||
def setShowInterfaceLabelsOnNewProject(self, value):
|
||||
from gns3.settings import GRAPHICS_VIEW_SETTINGS
|
||||
settings = self.loadSectionSettings("GraphicsView", GRAPHICS_VIEW_SETTINGS)
|
||||
settings["show_interface_labels_on_new_project"] = value
|
||||
self.saveSectionSettings("GraphicsView", settings)
|
||||
|
||||
@staticmethod
|
||||
def instance():
|
||||
"""
|
||||
|
||||
@@ -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", "")
|
||||
|
||||
@@ -54,6 +54,7 @@ from .dialogs.new_appliance_dialog import NewApplianceDialog
|
||||
from .dialogs.notif_dialog import NotifDialog, NotifDialogHandler
|
||||
from .status_bar import StatusBarHandler
|
||||
from .registry.appliance import ApplianceError
|
||||
from .appliance_manager import ApplianceManager
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -69,6 +70,9 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
# signal to tell the view if the user is adding a link or not
|
||||
adding_link_signal = QtCore.pyqtSignal(bool)
|
||||
|
||||
# Signal of settings updates
|
||||
settings_updated_signal = QtCore.Signal()
|
||||
|
||||
def __init__(self, parent=None, open_file=None):
|
||||
"""
|
||||
:param open_file: Open this file instead of asking for a new project
|
||||
@@ -110,6 +114,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
self._local_config_timer.timeout.connect(local_config.checkConfigChanged)
|
||||
self._local_config_timer.start(1000) # milliseconds
|
||||
self._analytics_client = AnalyticsClient()
|
||||
self._appliance_manager = ApplianceManager()
|
||||
|
||||
# restore the geometry and state of the main window.
|
||||
self.restoreGeometry(QtCore.QByteArray().fromBase64(self._settings["geometry"].encode()))
|
||||
@@ -271,6 +276,9 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
# connect the signal to the view
|
||||
self.adding_link_signal.connect(self.uiGraphicsView.addingLinkSlot)
|
||||
|
||||
# connect to the signal when settings change
|
||||
self.settings_updated_signal.connect(self.settingsChangedSlot)
|
||||
|
||||
def _loadSettings(self):
|
||||
"""
|
||||
Loads the settings from the persistent settings file.
|
||||
@@ -303,6 +311,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
self._settings.update(new_settings)
|
||||
# save the settings
|
||||
LocalConfig.instance().saveSectionSettings(self.__class__.__name__, self._settings)
|
||||
self.settings_updated_signal.emit()
|
||||
|
||||
def _openWebInterfaceActionSlot(self):
|
||||
if Controller.instance().connected():
|
||||
@@ -491,6 +500,17 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
self._project_dialog = None
|
||||
self._refreshVisibleWidgets()
|
||||
|
||||
@qslot
|
||||
def settingsChangedSlot(self, *args):
|
||||
"""
|
||||
Called when settings are updated
|
||||
"""
|
||||
# It covers case when project is not set
|
||||
# and we need to refresh appliance manager
|
||||
project = Topology.instance().project()
|
||||
if project is None:
|
||||
self._appliance_manager.instance().refresh()
|
||||
|
||||
def _refreshVisibleWidgets(self):
|
||||
"""
|
||||
Refresh widgets that should be visible or not
|
||||
@@ -611,7 +631,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
for item in self.uiGraphicsView.scene().items():
|
||||
if isinstance(item, LinkItem):
|
||||
item.adjust()
|
||||
|
||||
|
||||
def _updateZoomSettings(self, zoom=None):
|
||||
"""
|
||||
Updates zoom settings
|
||||
|
||||
@@ -19,9 +19,11 @@ 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
|
||||
|
||||
#FIXME: removed TraceNG
|
||||
MODULES = [Builtin, VPCS, Dynamips, IOU, Qemu, VirtualBox, VMware, Docker]
|
||||
|
||||
@@ -58,18 +58,19 @@ class Cloud(Node):
|
||||
if "interfaces" in result:
|
||||
self._interfaces = result["interfaces"].copy()
|
||||
|
||||
def update(self, new_settings):
|
||||
def update(self, new_settings, force=False):
|
||||
"""
|
||||
Updates the settings for this cloud.
|
||||
|
||||
:param new_settings: settings dictionary
|
||||
:param force: force this node to update
|
||||
"""
|
||||
|
||||
params = {}
|
||||
for name, value in new_settings.items():
|
||||
if name in self._settings and self._settings[name] != value:
|
||||
params[name] = value
|
||||
if params:
|
||||
if params or force:
|
||||
self._update(params)
|
||||
|
||||
def _updateCallback(self, result):
|
||||
|
||||
@@ -50,6 +50,7 @@ class CloudConfigurationPage(QtWidgets.QWidget, Ui_cloudConfigPageWidget):
|
||||
self.uiEthernetWarningPushButton.clicked.connect(self._EthernetWarningSlot)
|
||||
self.uiAddEthernetPushButton.clicked.connect(self._EthernetAddSlot)
|
||||
self.uiAddAllEthernetPushButton.clicked.connect(self._EthernetAddAllSlot)
|
||||
self.uiRefreshEthernetPushButton.clicked.connect(self._EthernetRefreshSlot)
|
||||
self.uiDeleteEthernetPushButton.clicked.connect(self._EthernetDeleteSlot)
|
||||
|
||||
# connect TAP slots
|
||||
@@ -57,6 +58,7 @@ class CloudConfigurationPage(QtWidgets.QWidget, Ui_cloudConfigPageWidget):
|
||||
self.uiTAPListWidget.itemSelectionChanged.connect(self._TAPChangedSlot)
|
||||
self.uiAddTAPPushButton.clicked.connect(self._TAPAddSlot)
|
||||
self.uiAddAllTAPPushButton.clicked.connect(self._TAPAddAllSlot)
|
||||
self.uiRefreshTAPPushButton.clicked.connect(self._TAPRefreshSlot)
|
||||
self.uiDeleteTAPPushButton.clicked.connect(self._TAPDeleteSlot)
|
||||
|
||||
# connect UDP slots
|
||||
@@ -74,6 +76,16 @@ class CloudConfigurationPage(QtWidgets.QWidget, Ui_cloudConfigPageWidget):
|
||||
icon = QtGui.QIcon(':/icons/dialog-warning.svg')
|
||||
self.uiEthernetWarningPushButton.setIcon(icon)
|
||||
|
||||
def _refreshInterfaces(self):
|
||||
"""
|
||||
Refresh the network interfaces.
|
||||
"""
|
||||
|
||||
if self._node:
|
||||
self._interfaces = self._node.interfaces()
|
||||
self._loadNetworkInterfaces(self._interfaces)
|
||||
self._node.updated_signal.disconnect(self._refreshInterfaces)
|
||||
|
||||
def _EthernetChangedSlot(self):
|
||||
"""
|
||||
Enables the use of the delete button.
|
||||
@@ -121,6 +133,15 @@ class CloudConfigurationPage(QtWidgets.QWidget, Ui_cloudConfigPageWidget):
|
||||
interface = self.uiEthernetComboBox.itemText(index)
|
||||
self._EthernetAddSlot(interface)
|
||||
|
||||
def _EthernetRefreshSlot(self):
|
||||
"""
|
||||
Refresh all Ethernet interfaces.
|
||||
"""
|
||||
|
||||
if self._node:
|
||||
self._node.update({}, force=True)
|
||||
self._node.updated_signal.connect(self._refreshInterfaces)
|
||||
|
||||
def _EthernetDeleteSlot(self):
|
||||
"""
|
||||
Deletes the selected Ethernet interface.
|
||||
@@ -199,6 +220,15 @@ class CloudConfigurationPage(QtWidgets.QWidget, Ui_cloudConfigPageWidget):
|
||||
interface = self.uiTAPComboBox.itemText(index)
|
||||
self._TAPAddSlot(interface)
|
||||
|
||||
def _TAPRefreshSlot(self):
|
||||
"""
|
||||
Refresh all TAP interfaces.
|
||||
"""
|
||||
|
||||
if self._node:
|
||||
self._node.update({}, force=True)
|
||||
self._node.updated_signal.connect(self._refreshInterfaces)
|
||||
|
||||
def _TAPDeleteSlot(self):
|
||||
"""
|
||||
Deletes a TAP interface.
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>821</width>
|
||||
<height>363</height>
|
||||
<width>1000</width>
|
||||
<height>378</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -57,7 +57,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="4">
|
||||
<item row="0" column="5">
|
||||
<widget class="QPushButton" name="uiDeleteEthernetPushButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
@@ -67,7 +67,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="5">
|
||||
<item row="1" column="0" colspan="6">
|
||||
<widget class="QListWidget" name="uiEthernetListWidget">
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
@@ -91,6 +91,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="4">
|
||||
<widget class="QPushButton" name="uiRefreshEthernetPushButton">
|
||||
<property name="text">
|
||||
<string>&Refresh</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<zorder>uiEthernetListWidget</zorder>
|
||||
<zorder>uiEthernetComboBox</zorder>
|
||||
@@ -99,13 +106,14 @@
|
||||
<zorder>uiAddAllEthernetPushButton</zorder>
|
||||
<zorder>uiShowSpecialInterfacesCheckBox</zorder>
|
||||
<zorder>uiEthernetWarningPushButton</zorder>
|
||||
<zorder>uiRefreshEthernetPushButton</zorder>
|
||||
</widget>
|
||||
<widget class="QWidget" name="TAPTab">
|
||||
<attribute name="title">
|
||||
<string>TAP interfaces</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="1" column="4">
|
||||
<item row="1" column="5">
|
||||
<widget class="QPushButton" name="uiDeleteTAPPushButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
@@ -115,7 +123,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="5">
|
||||
<item row="2" column="0" colspan="6">
|
||||
<widget class="QListWidget" name="uiTAPListWidget">
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
@@ -142,7 +150,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="4">
|
||||
<item row="0" column="1" colspan="5">
|
||||
<widget class="QComboBox" name="uiTAPComboBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
@@ -165,6 +173,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="4">
|
||||
<widget class="QPushButton" name="uiRefreshTAPPushButton">
|
||||
<property name="text">
|
||||
<string>&Refresh</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="UDPTab">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/modules/builtin/ui/cloud_configuration_page.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.9
|
||||
# Created by: PyQt5 UI code generator 5.5.1
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
@@ -11,7 +11,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
class Ui_cloudConfigPageWidget(object):
|
||||
def setupUi(self, cloudConfigPageWidget):
|
||||
cloudConfigPageWidget.setObjectName("cloudConfigPageWidget")
|
||||
cloudConfigPageWidget.resize(821, 363)
|
||||
cloudConfigPageWidget.resize(1000, 378)
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout(cloudConfigPageWidget)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.uiTabWidget = QtWidgets.QTabWidget(cloudConfigPageWidget)
|
||||
@@ -39,11 +39,11 @@ class Ui_cloudConfigPageWidget(object):
|
||||
self.uiDeleteEthernetPushButton = QtWidgets.QPushButton(self.EthernetTab)
|
||||
self.uiDeleteEthernetPushButton.setEnabled(False)
|
||||
self.uiDeleteEthernetPushButton.setObjectName("uiDeleteEthernetPushButton")
|
||||
self.gridLayout_3.addWidget(self.uiDeleteEthernetPushButton, 0, 4, 1, 1)
|
||||
self.gridLayout_3.addWidget(self.uiDeleteEthernetPushButton, 0, 5, 1, 1)
|
||||
self.uiEthernetListWidget = QtWidgets.QListWidget(self.EthernetTab)
|
||||
self.uiEthernetListWidget.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
||||
self.uiEthernetListWidget.setObjectName("uiEthernetListWidget")
|
||||
self.gridLayout_3.addWidget(self.uiEthernetListWidget, 1, 0, 1, 5)
|
||||
self.gridLayout_3.addWidget(self.uiEthernetListWidget, 1, 0, 1, 6)
|
||||
self.uiEthernetWarningPushButton = QtWidgets.QPushButton(self.EthernetTab)
|
||||
self.uiEthernetWarningPushButton.setText("")
|
||||
self.uiEthernetWarningPushButton.setObjectName("uiEthernetWarningPushButton")
|
||||
@@ -51,6 +51,9 @@ class Ui_cloudConfigPageWidget(object):
|
||||
self.uiShowSpecialInterfacesCheckBox = QtWidgets.QCheckBox(self.EthernetTab)
|
||||
self.uiShowSpecialInterfacesCheckBox.setObjectName("uiShowSpecialInterfacesCheckBox")
|
||||
self.gridLayout_3.addWidget(self.uiShowSpecialInterfacesCheckBox, 2, 0, 1, 2)
|
||||
self.uiRefreshEthernetPushButton = QtWidgets.QPushButton(self.EthernetTab)
|
||||
self.uiRefreshEthernetPushButton.setObjectName("uiRefreshEthernetPushButton")
|
||||
self.gridLayout_3.addWidget(self.uiRefreshEthernetPushButton, 0, 4, 1, 1)
|
||||
self.uiEthernetListWidget.raise_()
|
||||
self.uiEthernetComboBox.raise_()
|
||||
self.uiAddEthernetPushButton.raise_()
|
||||
@@ -58,6 +61,7 @@ class Ui_cloudConfigPageWidget(object):
|
||||
self.uiAddAllEthernetPushButton.raise_()
|
||||
self.uiShowSpecialInterfacesCheckBox.raise_()
|
||||
self.uiEthernetWarningPushButton.raise_()
|
||||
self.uiRefreshEthernetPushButton.raise_()
|
||||
self.uiTabWidget.addTab(self.EthernetTab, "")
|
||||
self.TAPTab = QtWidgets.QWidget()
|
||||
self.TAPTab.setObjectName("TAPTab")
|
||||
@@ -66,11 +70,11 @@ class Ui_cloudConfigPageWidget(object):
|
||||
self.uiDeleteTAPPushButton = QtWidgets.QPushButton(self.TAPTab)
|
||||
self.uiDeleteTAPPushButton.setEnabled(False)
|
||||
self.uiDeleteTAPPushButton.setObjectName("uiDeleteTAPPushButton")
|
||||
self.gridLayout_2.addWidget(self.uiDeleteTAPPushButton, 1, 4, 1, 1)
|
||||
self.gridLayout_2.addWidget(self.uiDeleteTAPPushButton, 1, 5, 1, 1)
|
||||
self.uiTAPListWidget = QtWidgets.QListWidget(self.TAPTab)
|
||||
self.uiTAPListWidget.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
||||
self.uiTAPListWidget.setObjectName("uiTAPListWidget")
|
||||
self.gridLayout_2.addWidget(self.uiTAPListWidget, 2, 0, 1, 5)
|
||||
self.gridLayout_2.addWidget(self.uiTAPListWidget, 2, 0, 1, 6)
|
||||
self.uiTAPLineEdit = QtWidgets.QLineEdit(self.TAPTab)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
@@ -91,10 +95,13 @@ class Ui_cloudConfigPageWidget(object):
|
||||
self.uiTAPComboBox.setInsertPolicy(QtWidgets.QComboBox.InsertAlphabetically)
|
||||
self.uiTAPComboBox.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
|
||||
self.uiTAPComboBox.setObjectName("uiTAPComboBox")
|
||||
self.gridLayout_2.addWidget(self.uiTAPComboBox, 0, 1, 1, 4)
|
||||
self.gridLayout_2.addWidget(self.uiTAPComboBox, 0, 1, 1, 5)
|
||||
self.uiAddAllTAPPushButton = QtWidgets.QPushButton(self.TAPTab)
|
||||
self.uiAddAllTAPPushButton.setObjectName("uiAddAllTAPPushButton")
|
||||
self.gridLayout_2.addWidget(self.uiAddAllTAPPushButton, 1, 3, 1, 1)
|
||||
self.uiRefreshTAPPushButton = QtWidgets.QPushButton(self.TAPTab)
|
||||
self.uiRefreshTAPPushButton.setObjectName("uiRefreshTAPPushButton")
|
||||
self.gridLayout_2.addWidget(self.uiRefreshTAPPushButton, 1, 4, 1, 1)
|
||||
self.uiTabWidget.addTab(self.TAPTab, "")
|
||||
self.UDPTab = QtWidgets.QWidget()
|
||||
self.UDPTab.setObjectName("UDPTab")
|
||||
@@ -241,11 +248,13 @@ class Ui_cloudConfigPageWidget(object):
|
||||
self.uiDeleteEthernetPushButton.setText(_translate("cloudConfigPageWidget", "&Delete"))
|
||||
self.uiEthernetListWidget.setSortingEnabled(True)
|
||||
self.uiShowSpecialInterfacesCheckBox.setText(_translate("cloudConfigPageWidget", "&Show special Ethernet interfaces"))
|
||||
self.uiRefreshEthernetPushButton.setText(_translate("cloudConfigPageWidget", "&Refresh"))
|
||||
self.uiTabWidget.setTabText(self.uiTabWidget.indexOf(self.EthernetTab), _translate("cloudConfigPageWidget", "Ethernet interfaces"))
|
||||
self.uiDeleteTAPPushButton.setText(_translate("cloudConfigPageWidget", "&Delete"))
|
||||
self.uiTAPListWidget.setSortingEnabled(True)
|
||||
self.uiAddTAPPushButton.setText(_translate("cloudConfigPageWidget", "&Add"))
|
||||
self.uiAddAllTAPPushButton.setText(_translate("cloudConfigPageWidget", "&Add all"))
|
||||
self.uiRefreshTAPPushButton.setText(_translate("cloudConfigPageWidget", "&Refresh"))
|
||||
self.uiTabWidget.setTabText(self.uiTabWidget.indexOf(self.TAPTab), _translate("cloudConfigPageWidget", "TAP interfaces"))
|
||||
self.uiUDPTunnelSettingsGroupBox.setTitle(_translate("cloudConfigPageWidget", "UDP tunnel settings"))
|
||||
self.uiRemoteHostLineEdit.setText(_translate("cloudConfigPageWidget", "127.0.0.1"))
|
||||
|
||||
@@ -135,6 +135,6 @@ class DockerVMWizard(VMWizard, Ui_DockerVMWizard):
|
||||
"name": name,
|
||||
"environment": self.uiEnvironmentTextEdit.toPlainText(),
|
||||
"start_command": start_command,
|
||||
"console_type": self.uiConsoleTypeComboBox.currentText()
|
||||
"console_type": self.uiConsoleTypeComboBox.currentText(),
|
||||
}
|
||||
return settings
|
||||
|
||||
@@ -49,7 +49,8 @@ class DockerVM(Node):
|
||||
"console_type": DOCKER_CONTAINER_SETTINGS["console_type"],
|
||||
"console_resolution": DOCKER_CONTAINER_SETTINGS["console_resolution"],
|
||||
"console_http_port": DOCKER_CONTAINER_SETTINGS["console_http_port"],
|
||||
"console_http_path": DOCKER_CONTAINER_SETTINGS["console_http_path"]}
|
||||
"console_http_path": DOCKER_CONTAINER_SETTINGS["console_http_path"],
|
||||
"extra_hosts": DOCKER_CONTAINER_SETTINGS["extra_hosts"]}
|
||||
|
||||
self.settings().update(docker_vm_settings)
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ class DockerVMConfigurationPage(QtWidgets.QWidget, Ui_dockerVMConfigPageWidget):
|
||||
self.uiConsoleResolutionComboBox.setCurrentIndex(self.uiConsoleResolutionComboBox.findText(settings["console_resolution"]))
|
||||
self.uiConsoleHttpPortSpinBox.setValue(settings["console_http_port"])
|
||||
self.uiHttpConsolePathLineEdit.setText(settings["console_http_path"])
|
||||
self.uiExtraHostsTextEdit.setText(settings["extra_hosts"])
|
||||
|
||||
if not group:
|
||||
self.uiNameLineEdit.setText(settings["name"])
|
||||
@@ -128,6 +129,7 @@ class DockerVMConfigurationPage(QtWidgets.QWidget, Ui_dockerVMConfigPageWidget):
|
||||
settings["console_resolution"] = self.uiConsoleResolutionComboBox.currentText()
|
||||
settings["console_http_port"] = self.uiConsoleHttpPortSpinBox.value()
|
||||
settings["console_http_path"] = self.uiHttpConsolePathLineEdit.text()
|
||||
settings["extra_hosts"] = self.uiExtraHostsTextEdit.toPlainText()
|
||||
|
||||
if not group:
|
||||
adapters = self.uiAdapterSpinBox.value()
|
||||
|
||||
@@ -61,7 +61,6 @@ class DockerVMPreferencesPage(QtWidgets.QWidget, Ui_DockerVMPreferencesPageWidge
|
||||
return section_item
|
||||
|
||||
def _refreshInfo(self, docker_image):
|
||||
|
||||
self.uiDockerVMInfoTreeWidget.clear()
|
||||
|
||||
# fill out the General section
|
||||
@@ -79,6 +78,9 @@ class DockerVMPreferencesPage(QtWidgets.QWidget, Ui_DockerVMPreferencesPageWidge
|
||||
if docker_image["environment"]:
|
||||
QtWidgets.QTreeWidgetItem(section_item, ["Environment:", str(docker_image["environment"])])
|
||||
|
||||
if docker_image["extra_hosts"]:
|
||||
QtWidgets.QTreeWidgetItem(section_item, ["Extra hosts:", str(docker_image["extra_hosts"])])
|
||||
|
||||
self.uiDockerVMInfoTreeWidget.expandAll()
|
||||
self.uiDockerVMInfoTreeWidget.resizeColumnToContents(0)
|
||||
self.uiDockerVMInfoTreeWidget.resizeColumnToContents(1)
|
||||
|
||||
@@ -38,5 +38,6 @@ DOCKER_CONTAINER_SETTINGS = {
|
||||
"console_type": "telnet",
|
||||
"console_resolution": "1024x768",
|
||||
"console_http_port": 80,
|
||||
"console_http_path": "/"
|
||||
"console_http_path": "/",
|
||||
"extra_hosts": ""
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>613</width>
|
||||
<height>519</height>
|
||||
<height>524</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -24,7 +24,16 @@
|
||||
<string>General settings</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="margin">
|
||||
<property name="leftMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<item row="0" column="1">
|
||||
@@ -263,6 +272,45 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="advancedTab">
|
||||
<attribute name="title">
|
||||
<string>Advanced</string>
|
||||
</attribute>
|
||||
<widget class="QLabel" name="uiExtraHostsLabel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>10</y>
|
||||
<width>152</width>
|
||||
<height>82</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Extra hosts added to
|
||||
/etc/hosts file.
|
||||
(hostname:IP, one per line)</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QTextEdit" name="uiExtraHostsTextEdit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>168</x>
|
||||
<y>10</y>
|
||||
<width>413</width>
|
||||
<height>82</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/modules/docker/ui/docker_vm_configuration_page.ui'
|
||||
# Form implementation generated from reading ui file '/home/dominik/projects/gns3-gui/gns3/modules/docker/ui/docker_vm_configuration_page.ui'
|
||||
#
|
||||
# Created: Thu Jan 5 14:49:45 2017
|
||||
# by: PyQt5 UI code generator 5.2.1
|
||||
# Created by: PyQt5 UI code generator 5.8.2
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
@@ -12,7 +11,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
class Ui_dockerVMConfigPageWidget(object):
|
||||
def setupUi(self, dockerVMConfigPageWidget):
|
||||
dockerVMConfigPageWidget.setObjectName("dockerVMConfigPageWidget")
|
||||
dockerVMConfigPageWidget.resize(613, 519)
|
||||
dockerVMConfigPageWidget.resize(613, 524)
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout(dockerVMConfigPageWidget)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.uiTabWidget = QtWidgets.QTabWidget(dockerVMConfigPageWidget)
|
||||
@@ -122,6 +121,21 @@ class Ui_dockerVMConfigPageWidget(object):
|
||||
self.uiHttpConsolePathLineEdit.setObjectName("uiHttpConsolePathLineEdit")
|
||||
self.gridLayout.addWidget(self.uiHttpConsolePathLineEdit, 9, 1, 1, 1)
|
||||
self.uiTabWidget.addTab(self.tab, "")
|
||||
self.advancedTab = QtWidgets.QWidget()
|
||||
self.advancedTab.setObjectName("advancedTab")
|
||||
self.uiExtraHostsLabel = QtWidgets.QLabel(self.advancedTab)
|
||||
self.uiExtraHostsLabel.setGeometry(QtCore.QRect(10, 10, 152, 82))
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.uiExtraHostsLabel.sizePolicy().hasHeightForWidth())
|
||||
self.uiExtraHostsLabel.setSizePolicy(sizePolicy)
|
||||
self.uiExtraHostsLabel.setWordWrap(True)
|
||||
self.uiExtraHostsLabel.setObjectName("uiExtraHostsLabel")
|
||||
self.uiExtraHostsTextEdit = QtWidgets.QTextEdit(self.advancedTab)
|
||||
self.uiExtraHostsTextEdit.setGeometry(QtCore.QRect(168, 10, 413, 82))
|
||||
self.uiExtraHostsTextEdit.setObjectName("uiExtraHostsTextEdit")
|
||||
self.uiTabWidget.addTab(self.advancedTab, "")
|
||||
self.verticalLayout.addWidget(self.uiTabWidget)
|
||||
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.verticalLayout.addItem(spacerItem)
|
||||
@@ -164,4 +178,8 @@ class Ui_dockerVMConfigPageWidget(object):
|
||||
self.uiConsoleResolutionLabel.setText(_translate("dockerVMConfigPageWidget", "VNC console resolution:"))
|
||||
self.label_2.setText(_translate("dockerVMConfigPageWidget", "HTTP path:"))
|
||||
self.uiTabWidget.setTabText(self.uiTabWidget.indexOf(self.tab), _translate("dockerVMConfigPageWidget", "General settings"))
|
||||
self.uiExtraHostsLabel.setText(_translate("dockerVMConfigPageWidget", "Extra hosts added to \n"
|
||||
"/etc/hosts file.\n"
|
||||
"(hostname:IP, one per line)"))
|
||||
self.uiTabWidget.setTabText(self.uiTabWidget.indexOf(self.advancedTab), _translate("dockerVMConfigPageWidget", "Advanced"))
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>569</width>
|
||||
<height>503</height>
|
||||
<width>767</width>
|
||||
<height>685</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -181,7 +181,7 @@
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable layer 1 keepalive messages (testing only)</string>
|
||||
<string>Enable layer 1 keepalive messages (non-functional)</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/modules/iou/ui/iou_device_configuration_page.ui'
|
||||
#
|
||||
# Created: Thu Jan 5 14:49:45 2017
|
||||
# by: PyQt5 UI code generator 5.2.1
|
||||
# Created by: PyQt5 UI code generator 5.5.1
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
@@ -12,7 +11,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
class Ui_iouDeviceConfigPageWidget(object):
|
||||
def setupUi(self, iouDeviceConfigPageWidget):
|
||||
iouDeviceConfigPageWidget.setObjectName("iouDeviceConfigPageWidget")
|
||||
iouDeviceConfigPageWidget.resize(569, 503)
|
||||
iouDeviceConfigPageWidget.resize(767, 685)
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout(iouDeviceConfigPageWidget)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.uiTabWidget = QtWidgets.QTabWidget(iouDeviceConfigPageWidget)
|
||||
@@ -209,7 +208,7 @@ class Ui_iouDeviceConfigPageWidget(object):
|
||||
self.uiPrivateConfigToolButton.setText(_translate("iouDeviceConfigPageWidget", "&Browse..."))
|
||||
self.uiDefaultNameFormatLabel.setText(_translate("iouDeviceConfigPageWidget", "Default name format:"))
|
||||
self.uiOtherSettingsGroupBox.setTitle(_translate("iouDeviceConfigPageWidget", "Other settings"))
|
||||
self.uiL1KeepalivesCheckBox.setText(_translate("iouDeviceConfigPageWidget", "Enable layer 1 keepalive messages (testing only)"))
|
||||
self.uiL1KeepalivesCheckBox.setText(_translate("iouDeviceConfigPageWidget", "Enable layer 1 keepalive messages (non-functional)"))
|
||||
self.uiDefaultValuesCheckBox.setText(_translate("iouDeviceConfigPageWidget", "Use default IOU values for memories"))
|
||||
self.uiRamLabel.setText(_translate("iouDeviceConfigPageWidget", "RAM size:"))
|
||||
self.uiRamSpinBox.setSuffix(_translate("iouDeviceConfigPageWidget", " MB"))
|
||||
|
||||
@@ -25,9 +25,9 @@ import re
|
||||
from collections import OrderedDict
|
||||
from gns3.modules.qemu.dialogs.qemu_image_wizard import QemuImageWizard
|
||||
from gns3.dialogs.symbol_selection_dialog import SymbolSelectionDialog
|
||||
from gns3.ports.port_name_factory import StandardPortNameFactory
|
||||
from gns3.node import Node
|
||||
from gns3.qt import QtCore, QtWidgets, qpartial
|
||||
from gns3.modules.module_error import ModuleError
|
||||
from gns3.dialogs.node_properties_dialog import ConfigurationError
|
||||
from gns3.image_manager import ImageManager
|
||||
|
||||
@@ -281,8 +281,12 @@ class QemuVMConfigurationPage(QtWidgets.QWidget, Ui_QemuVMConfigPageWidget):
|
||||
if index != -1:
|
||||
self.uiQemuListComboBox.setCurrentIndex(index)
|
||||
else:
|
||||
QtWidgets.QMessageBox.critical(self, "Qemu", "Could not find {} in the Qemu binaries list".format(qemu_path))
|
||||
self.uiQemuListComboBox.clear()
|
||||
index = self.uiQemuListComboBox.findData("{path}".format(path=os.path.basename(qemu_path)), flags=QtCore.Qt.MatchEndsWith)
|
||||
self.uiQemuListComboBox.setCurrentIndex(index)
|
||||
if index == -1:
|
||||
QtWidgets.QMessageBox.warning(self, "Qemu","Could not find '{}' in the Qemu binaries list, please select a new binary".format(qemu_path))
|
||||
else:
|
||||
QtWidgets.QMessageBox.warning(self, "Qemu","Could not find '{}' in the Qemu binaries list, an alternative path has been selected".format(qemu_path))
|
||||
|
||||
def _cpuThrottlingChangedSlot(self, state):
|
||||
"""
|
||||
@@ -322,11 +326,7 @@ class QemuVMConfigurationPage(QtWidgets.QWidget, Ui_QemuVMConfigPageWidget):
|
||||
QtWidgets.QMessageBox.warning(self, "Qemu", "Server {} is not running, cannot retrieve the QEMU binaries list".format(settings["server"]))
|
||||
else:
|
||||
callback = qpartial(self._getQemuBinariesFromServerCallback, qemu_path=settings["qemu_path"])
|
||||
try:
|
||||
Qemu.instance().getQemuBinariesFromServer(self._compute_id, callback)
|
||||
except ModuleError as e:
|
||||
QtWidgets.QMessageBox.critical(self, "Qemu", "Error while getting the QEMU binaries list: {}".format(e))
|
||||
self.uiQemuListComboBox.clear()
|
||||
Qemu.instance().getQemuBinariesFromServer(self._compute_id, callback)
|
||||
|
||||
if not group:
|
||||
# set the device name
|
||||
@@ -497,25 +497,29 @@ class QemuVMConfigurationPage(QtWidgets.QWidget, Ui_QemuVMConfigPageWidget):
|
||||
|
||||
symbol_path = self.uiSymbolLineEdit.text()
|
||||
settings["symbol"] = symbol_path
|
||||
|
||||
settings["category"] = self.uiCategoryComboBox.itemData(self.uiCategoryComboBox.currentIndex())
|
||||
|
||||
port_name_format = self.uiPortNameFormatLineEdit.text()
|
||||
if '{0}' not in port_name_format and '{port0}' not in port_name_format and '{port1}' not in port_name_format:
|
||||
QtWidgets.QMessageBox.critical(self, "Port name format", "The format must contain at least {0}, {port0} or {port1}")
|
||||
else:
|
||||
settings["port_name_format"] = self.uiPortNameFormatLineEdit.text()
|
||||
|
||||
port_segment_size = self.uiPortSegmentSizeSpinBox.value()
|
||||
if port_segment_size and '{1}' not in port_name_format and '{segment0}' not in port_name_format and '{segment1}' not in port_name_format:
|
||||
QtWidgets.QMessageBox.critical(self, "Port name format", "If the segment size is not 0, the format must contain {1}, {segment0} or {segment1}")
|
||||
else:
|
||||
settings["port_segment_size"] = port_segment_size
|
||||
first_port_name = self.uiFirstPortNameLineEdit.text().strip()
|
||||
|
||||
settings["first_port_name"] = self.uiFirstPortNameLineEdit.text().strip()
|
||||
try:
|
||||
StandardPortNameFactory(self.uiAdaptersSpinBox.value(), first_port_name, port_name_format, port_segment_size)
|
||||
except (ValueError, KeyError):
|
||||
QtWidgets.QMessageBox.critical(self, "Invalid format", "Invalid port name format")
|
||||
raise ConfigurationError()
|
||||
|
||||
if self.uiQemuListComboBox.count():
|
||||
settings["port_name_format"] = self.uiPortNameFormatLineEdit.text()
|
||||
settings["port_segment_size"] = port_segment_size
|
||||
settings["first_port_name"] = first_port_name
|
||||
|
||||
if self.uiQemuListComboBox.currentIndex() != -1:
|
||||
qemu_path = self.uiQemuListComboBox.itemData(self.uiQemuListComboBox.currentIndex())
|
||||
settings["qemu_path"] = qemu_path
|
||||
else:
|
||||
QtWidgets.QMessageBox.critical(self, "Qemu binary", "Please select a Qemu binary")
|
||||
if node:
|
||||
raise ConfigurationError()
|
||||
|
||||
settings["boot_priority"] = self.uiBootPriorityComboBox.itemData(self.uiBootPriorityComboBox.currentIndex())
|
||||
settings["console_type"] = self.uiConsoleTypeComboBox.currentText().lower()
|
||||
|
||||
249
gns3/modules/traceng/__init__.py
Normal file
249
gns3/modules/traceng/__init__.py
Normal 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
|
||||
0
gns3/modules/traceng/dialogs/__init__.py
Normal file
0
gns3/modules/traceng/dialogs/__init__.py
Normal file
86
gns3/modules/traceng/dialogs/traceng_node_wizard.py
Normal file
86
gns3/modules/traceng/dialogs/traceng_node_wizard.py
Normal 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
|
||||
0
gns3/modules/traceng/pages/__init__.py
Normal file
0
gns3/modules/traceng/pages/__init__.py
Normal file
155
gns3/modules/traceng/pages/traceng_node_configuration_page.py
Normal file
155
gns3/modules/traceng/pages/traceng_node_configuration_page.py
Normal file
@@ -0,0 +1,155 @@
|
||||
# -*- 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"])
|
||||
self.uiDefaultDestinationLineEdit.setText(settings["default_destination"])
|
||||
else:
|
||||
self.uiIPAddressLabel.hide()
|
||||
self.uiIPAddressLineEdit.hide()
|
||||
self.uiDefaultDestinationLabel.hide()
|
||||
self.uiDefaultDestinationLineEdit.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().strip()
|
||||
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()
|
||||
|
||||
settings["default_destination"] = self.uiDefaultDestinationLineEdit.text().strip()
|
||||
|
||||
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
|
||||
189
gns3/modules/traceng/pages/traceng_node_preferences_page.py
Normal file
189
gns3/modules/traceng/pages/traceng_node_preferences_page.py
Normal 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)
|
||||
127
gns3/modules/traceng/pages/traceng_preferences_page.py
Normal file
127
gns3/modules/traceng/pages/traceng_preferences_page.py
Normal 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)
|
||||
36
gns3/modules/traceng/settings.py
Normal file
36
gns3/modules/traceng/settings.py
Normal file
@@ -0,0 +1,36 @@
|
||||
# -*- 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_destination": "",
|
||||
"default_name_format": "TraceNG{0}",
|
||||
"console_type": "none",
|
||||
"symbol": ":/symbols/traceng.svg",
|
||||
"category": Node.end_devices,
|
||||
}
|
||||
172
gns3/modules/traceng/traceng_node.py
Normal file
172
gns3/modules/traceng/traceng_node.py
Normal file
@@ -0,0 +1,172 @@
|
||||
# -*- 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": "",
|
||||
"default_destination": ""}
|
||||
|
||||
self._last_destination = ""
|
||||
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
|
||||
|
||||
if self._last_destination:
|
||||
destination = self._last_destination
|
||||
else:
|
||||
destination = self.settings()["default_destination"]
|
||||
destination, ok = QtWidgets.QInputDialog.getText(self.parent(), "TraceNG", "Destination host or IP address:", text=destination)
|
||||
if ok:
|
||||
if not destination:
|
||||
QtWidgets.QMessageBox.critical(self, "TraceNG", "Please provide a host or IP address to trace")
|
||||
return
|
||||
ip_address = self.settings()["ip_address"]
|
||||
if destination == ip_address:
|
||||
QtWidgets.QMessageBox.critical(self, "TraceNG", "Destination cannot be the same as this node IP address ({})".format(ip_address))
|
||||
return
|
||||
self._last_destination = destination
|
||||
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"
|
||||
0
gns3/modules/traceng/ui/__init__.py
Normal file
0
gns3/modules/traceng/ui/__init__.py
Normal file
119
gns3/modules/traceng/ui/traceng_node_configuration_page.ui
Executable file
119
gns3/modules/traceng/ui/traceng_node_configuration_page.ui
Executable file
@@ -0,0 +1,119 @@
|
||||
<?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>846</width>
|
||||
<height>340</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="1" column="0">
|
||||
<widget class="QLabel" name="uiIPAddressLabel">
|
||||
<property name="text">
|
||||
<string>IP address:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QLabel" name="uiDefaultDestinationLabel">
|
||||
<property name="text">
|
||||
<string>Default destination:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="3">
|
||||
<widget class="QLabel" name="uiDefaultNameFormatLabel">
|
||||
<property name="text">
|
||||
<string>Default name format:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="3">
|
||||
<widget class="QLineEdit" name="uiDefaultNameFormatLineEdit"/>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="uiSymbolLabel">
|
||||
<property name="text">
|
||||
<string>Symbol:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="3">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="uiSymbolLineEdit"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="uiSymbolToolButton">
|
||||
<property name="text">
|
||||
<string>&Browse...</string>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextOnly</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QLabel" name="uiCategoryLabel">
|
||||
<property name="text">
|
||||
<string>Category:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="3">
|
||||
<widget class="QComboBox" name="uiCategoryComboBox"/>
|
||||
</item>
|
||||
<item row="6" column="1" colspan="3">
|
||||
<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>
|
||||
<item row="2" column="3">
|
||||
<widget class="QLineEdit" name="uiDefaultDestinationLineEdit"/>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="QLineEdit" name="uiIPAddressLineEdit"/>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QLineEdit" name="uiNameLineEdit"/>
|
||||
</item>
|
||||
</layout>
|
||||
<zorder>uiNameLabel</zorder>
|
||||
<zorder>uiNameLineEdit</zorder>
|
||||
<zorder>uiDefaultNameFormatLabel</zorder>
|
||||
<zorder>uiDefaultNameFormatLineEdit</zorder>
|
||||
<zorder>uiSymbolLabel</zorder>
|
||||
<zorder>uiCategoryLabel</zorder>
|
||||
<zorder>uiCategoryComboBox</zorder>
|
||||
<zorder>uiIPAddressLabel</zorder>
|
||||
<zorder>uiIPAddressLineEdit</zorder>
|
||||
<zorder>uiDefaultDestinationLabel</zorder>
|
||||
<zorder>uiDefaultDestinationLineEdit</zorder>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,87 @@
|
||||
# -*- 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(846, 340)
|
||||
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.uiIPAddressLabel = QtWidgets.QLabel(TraceNGNodeConfigPageWidget)
|
||||
self.uiIPAddressLabel.setObjectName("uiIPAddressLabel")
|
||||
self.gridLayout.addWidget(self.uiIPAddressLabel, 1, 0, 1, 1)
|
||||
self.uiDefaultDestinationLabel = QtWidgets.QLabel(TraceNGNodeConfigPageWidget)
|
||||
self.uiDefaultDestinationLabel.setObjectName("uiDefaultDestinationLabel")
|
||||
self.gridLayout.addWidget(self.uiDefaultDestinationLabel, 2, 0, 1, 2)
|
||||
self.uiDefaultNameFormatLabel = QtWidgets.QLabel(TraceNGNodeConfigPageWidget)
|
||||
self.uiDefaultNameFormatLabel.setObjectName("uiDefaultNameFormatLabel")
|
||||
self.gridLayout.addWidget(self.uiDefaultNameFormatLabel, 3, 0, 1, 3)
|
||||
self.uiDefaultNameFormatLineEdit = QtWidgets.QLineEdit(TraceNGNodeConfigPageWidget)
|
||||
self.uiDefaultNameFormatLineEdit.setObjectName("uiDefaultNameFormatLineEdit")
|
||||
self.gridLayout.addWidget(self.uiDefaultNameFormatLineEdit, 3, 3, 1, 1)
|
||||
self.uiSymbolLabel = QtWidgets.QLabel(TraceNGNodeConfigPageWidget)
|
||||
self.uiSymbolLabel.setObjectName("uiSymbolLabel")
|
||||
self.gridLayout.addWidget(self.uiSymbolLabel, 4, 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, 4, 3, 1, 1)
|
||||
self.uiCategoryLabel = QtWidgets.QLabel(TraceNGNodeConfigPageWidget)
|
||||
self.uiCategoryLabel.setObjectName("uiCategoryLabel")
|
||||
self.gridLayout.addWidget(self.uiCategoryLabel, 5, 0, 1, 2)
|
||||
self.uiCategoryComboBox = QtWidgets.QComboBox(TraceNGNodeConfigPageWidget)
|
||||
self.uiCategoryComboBox.setObjectName("uiCategoryComboBox")
|
||||
self.gridLayout.addWidget(self.uiCategoryComboBox, 5, 3, 1, 1)
|
||||
spacerItem = QtWidgets.QSpacerItem(263, 212, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.gridLayout.addItem(spacerItem, 6, 1, 1, 3)
|
||||
self.uiDefaultDestinationLineEdit = QtWidgets.QLineEdit(TraceNGNodeConfigPageWidget)
|
||||
self.uiDefaultDestinationLineEdit.setObjectName("uiDefaultDestinationLineEdit")
|
||||
self.gridLayout.addWidget(self.uiDefaultDestinationLineEdit, 2, 3, 1, 1)
|
||||
self.uiIPAddressLineEdit = QtWidgets.QLineEdit(TraceNGNodeConfigPageWidget)
|
||||
self.uiIPAddressLineEdit.setObjectName("uiIPAddressLineEdit")
|
||||
self.gridLayout.addWidget(self.uiIPAddressLineEdit, 1, 3, 1, 1)
|
||||
self.uiNameLineEdit = QtWidgets.QLineEdit(TraceNGNodeConfigPageWidget)
|
||||
self.uiNameLineEdit.setObjectName("uiNameLineEdit")
|
||||
self.gridLayout.addWidget(self.uiNameLineEdit, 0, 3, 1, 1)
|
||||
self.uiNameLabel.raise_()
|
||||
self.uiNameLineEdit.raise_()
|
||||
self.uiDefaultNameFormatLabel.raise_()
|
||||
self.uiDefaultNameFormatLineEdit.raise_()
|
||||
self.uiSymbolLabel.raise_()
|
||||
self.uiCategoryLabel.raise_()
|
||||
self.uiCategoryComboBox.raise_()
|
||||
self.uiIPAddressLabel.raise_()
|
||||
self.uiIPAddressLineEdit.raise_()
|
||||
self.uiDefaultDestinationLabel.raise_()
|
||||
self.uiDefaultDestinationLineEdit.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.uiDefaultDestinationLabel.setText(_translate("TraceNGNodeConfigPageWidget", "Default destination:"))
|
||||
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:"))
|
||||
|
||||
160
gns3/modules/traceng/ui/traceng_node_preferences_page.ui
Normal file
160
gns3/modules/traceng/ui/traceng_node_preferences_page.ui
Normal 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>&New</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="uiEditTraceNGPushButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Edit</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="uiDeleteTraceNGPushButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&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>
|
||||
83
gns3/modules/traceng/ui/traceng_node_preferences_page_ui.py
Normal file
83
gns3/modules/traceng/ui/traceng_node_preferences_page_ui.py
Normal 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"))
|
||||
|
||||
140
gns3/modules/traceng/ui/traceng_node_wizard.ui
Normal file
140
gns3/modules/traceng/ui/traceng_node_wizard.ui
Normal 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>
|
||||
93
gns3/modules/traceng/ui/traceng_node_wizard_ui.py
Normal file
93
gns3/modules/traceng/ui/traceng_node_wizard_ui.py
Normal 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:"))
|
||||
|
||||
126
gns3/modules/traceng/ui/traceng_preferences_page.ui
Executable file
126
gns3/modules/traceng/ui/traceng_preferences_page.ui
Executable 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>&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>
|
||||
67
gns3/modules/traceng/ui/traceng_preferences_page_ui.py
Normal file
67
gns3/modules/traceng/ui/traceng_preferences_page_ui.py
Normal 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"))
|
||||
|
||||
@@ -23,12 +23,10 @@ import os
|
||||
import sys
|
||||
import shutil
|
||||
|
||||
from gns3.qt import QtWidgets
|
||||
from gns3.local_server_config import LocalServerConfig
|
||||
from gns3.local_config import LocalConfig
|
||||
|
||||
from ..module import Module
|
||||
from ..module_error import ModuleError
|
||||
from .virtualbox_vm import VirtualBoxVM
|
||||
from .settings import VBOX_SETTINGS
|
||||
from .settings import VBOX_VM_SETTINGS
|
||||
@@ -78,7 +76,8 @@ class VirtualBox(Module):
|
||||
vboxmanage_path_osx = "/Applications/VirtualBox.app/Contents/MacOS/VBoxManage"
|
||||
if os.path.exists(vboxmanage_path_osx):
|
||||
vboxmanage_path = vboxmanage_path_osx
|
||||
else:
|
||||
|
||||
if vboxmanage_path is None:
|
||||
vboxmanage_path = shutil.which("vboxmanage")
|
||||
|
||||
if vboxmanage_path is None:
|
||||
|
||||
@@ -22,6 +22,7 @@ Configuration page for VirtualBox VMs.
|
||||
from gns3.qt import QtWidgets
|
||||
from gns3.dialogs.node_properties_dialog import ConfigurationError
|
||||
from gns3.dialogs.symbol_selection_dialog import SymbolSelectionDialog
|
||||
from gns3.ports.port_name_factory import StandardPortNameFactory
|
||||
from gns3.node import Node
|
||||
|
||||
from ..ui.virtualbox_vm_configuration_page_ui import Ui_virtualBoxVMConfigPageWidget
|
||||
@@ -178,21 +179,21 @@ class VirtualBoxVMConfigurationPage(QtWidgets.QWidget, Ui_virtualBoxVMConfigPage
|
||||
|
||||
symbol_path = self.uiSymbolLineEdit.text()
|
||||
settings["symbol"] = symbol_path
|
||||
|
||||
settings["category"] = self.uiCategoryComboBox.itemData(self.uiCategoryComboBox.currentIndex())
|
||||
|
||||
port_name_format = self.uiPortNameFormatLineEdit.text()
|
||||
if '{0}' not in port_name_format and '{port0}' not in port_name_format and '{port1}' not in port_name_format:
|
||||
QtWidgets.QMessageBox.critical(self, "Port name format", "The format must contain at least {0}, {port0} or {port1}")
|
||||
else:
|
||||
settings["port_name_format"] = self.uiPortNameFormatLineEdit.text()
|
||||
|
||||
port_segment_size = self.uiPortSegmentSizeSpinBox.value()
|
||||
if port_segment_size and '{1}' not in port_name_format and '{segment0}' not in port_name_format and '{segment1}' not in port_name_format:
|
||||
QtWidgets.QMessageBox.critical(self, "Port name format", "If the segment size is not 0, the format must contain {1}, {segment0} or {segment1}")
|
||||
else:
|
||||
settings["port_segment_size"] = port_segment_size
|
||||
first_port_name = self.uiFirstPortNameLineEdit.text().strip()
|
||||
|
||||
settings["first_port_name"] = self.uiFirstPortNameLineEdit.text().strip()
|
||||
try:
|
||||
StandardPortNameFactory(self.uiAdaptersSpinBox.value(), first_port_name, port_name_format, port_segment_size)
|
||||
except (ValueError, KeyError):
|
||||
QtWidgets.QMessageBox.critical(self, "Invalid format", "Invalid port name format")
|
||||
raise ConfigurationError()
|
||||
|
||||
settings["port_name_format"] = self.uiPortNameFormatLineEdit.text()
|
||||
settings["port_segment_size"] = port_segment_size
|
||||
settings["first_port_name"] = first_port_name
|
||||
|
||||
settings["ram"] = self.uiVMRamSpinBox.value()
|
||||
settings["adapter_type"] = self.uiAdapterTypesComboBox.currentText()
|
||||
|
||||
@@ -50,7 +50,7 @@ class VMwareVMWizard(VMWizard, Ui_VMwareVMWizard):
|
||||
if super().validateCurrentPage() is False:
|
||||
return False
|
||||
|
||||
if self.currentPage() == self.uiVirtualBoxWizardPage:
|
||||
if self.currentPage() == self.uiVMwareWizardPage:
|
||||
if not self.uiVMListComboBox.count():
|
||||
QtWidgets.QMessageBox.critical(self, "VMware VMs", "There is no VMware VM available!")
|
||||
return False
|
||||
@@ -59,7 +59,7 @@ class VMwareVMWizard(VMWizard, Ui_VMwareVMWizard):
|
||||
def initializePage(self, page_id):
|
||||
|
||||
super().initializePage(page_id)
|
||||
if self.page(page_id) == self.uiVirtualBoxWizardPage:
|
||||
if self.page(page_id) == self.uiVMwareWizardPage:
|
||||
self.uiVMListComboBox.clear()
|
||||
Controller.instance().getCompute("/vmware/vms", self._compute_id, self._getVMwareVMsFromServerCallback, progressText="Listing VMware VMs...")
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ Configuration page for VMware VMs.
|
||||
from gns3.qt import QtWidgets
|
||||
from gns3.dialogs.node_properties_dialog import ConfigurationError
|
||||
from gns3.dialogs.symbol_selection_dialog import SymbolSelectionDialog
|
||||
from gns3.ports.port_name_factory import StandardPortNameFactory
|
||||
from gns3.node import Node
|
||||
|
||||
from ..ui.vmware_vm_configuration_page_ui import Ui_VMwareVMConfigPageWidget
|
||||
@@ -175,21 +176,21 @@ class VMwareVMConfigurationPage(QtWidgets.QWidget, Ui_VMwareVMConfigPageWidget):
|
||||
|
||||
symbol_path = self.uiSymbolLineEdit.text()
|
||||
settings["symbol"] = symbol_path
|
||||
|
||||
settings["category"] = self.uiCategoryComboBox.itemData(self.uiCategoryComboBox.currentIndex())
|
||||
|
||||
port_name_format = self.uiPortNameFormatLineEdit.text()
|
||||
if '{0}' not in port_name_format and '{port0}' not in port_name_format and '{port1}' not in port_name_format:
|
||||
QtWidgets.QMessageBox.critical(self, "Port name format", "The format must contain at least {0}, {port0} or {port1}")
|
||||
else:
|
||||
settings["port_name_format"] = self.uiPortNameFormatLineEdit.text()
|
||||
|
||||
port_segment_size = self.uiPortSegmentSizeSpinBox.value()
|
||||
if port_segment_size and '{1}' not in port_name_format and '{segment0}' not in port_name_format and '{segment1}' not in port_name_format:
|
||||
QtWidgets.QMessageBox.critical(self, "Port name format", "If the segment size is not 0, the format must contain {1}, {segment0} or {segment1}")
|
||||
else:
|
||||
settings["port_segment_size"] = port_segment_size
|
||||
first_port_name = self.uiFirstPortNameLineEdit.text().strip()
|
||||
|
||||
settings["first_port_name"] = self.uiFirstPortNameLineEdit.text().strip()
|
||||
try:
|
||||
StandardPortNameFactory(self.uiAdaptersSpinBox.value(), first_port_name, port_name_format, port_segment_size)
|
||||
except (ValueError, KeyError):
|
||||
QtWidgets.QMessageBox.critical(self, "Invalid format", "Invalid port name format")
|
||||
raise ConfigurationError()
|
||||
|
||||
settings["port_name_format"] = self.uiPortNameFormatLineEdit.text()
|
||||
settings["port_segment_size"] = port_segment_size
|
||||
settings["first_port_name"] = first_port_name
|
||||
|
||||
settings["adapter_type"] = self.uiAdapterTypesComboBox.currentText()
|
||||
settings["use_any_adapter"] = self.uiUseAnyAdapterCheckBox.isChecked()
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>598</width>
|
||||
<width>755</width>
|
||||
<height>453</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -81,7 +81,7 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWizardPage" name="uiVirtualBoxWizardPage">
|
||||
<widget class="QWizardPage" name="uiVMwareWizardPage">
|
||||
<property name="title">
|
||||
<string>VMware Virtual Machine</string>
|
||||
</property>
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/modules/vmware/ui/vmware_vm_wizard.ui'
|
||||
#
|
||||
# Created: Tue Sep 20 17:45:46 2016
|
||||
# by: PyQt5 UI code generator 5.2.1
|
||||
# Created by: PyQt5 UI code generator 5.5.1
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
@@ -12,7 +11,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
class Ui_VMwareVMWizard(object):
|
||||
def setupUi(self, VMwareVMWizard):
|
||||
VMwareVMWizard.setObjectName("VMwareVMWizard")
|
||||
VMwareVMWizard.resize(598, 453)
|
||||
VMwareVMWizard.resize(755, 453)
|
||||
VMwareVMWizard.setModal(True)
|
||||
self.uiServerWizardPage = QtWidgets.QWizardPage()
|
||||
self.uiServerWizardPage.setObjectName("uiServerWizardPage")
|
||||
@@ -48,14 +47,14 @@ class Ui_VMwareVMWizard(object):
|
||||
self.gridLayout_8.addWidget(self.uiRemoteServersLabel, 0, 0, 1, 1)
|
||||
self.gridLayout_2.addWidget(self.uiRemoteServersGroupBox, 1, 0, 1, 1)
|
||||
VMwareVMWizard.addPage(self.uiServerWizardPage)
|
||||
self.uiVirtualBoxWizardPage = QtWidgets.QWizardPage()
|
||||
self.uiVirtualBoxWizardPage.setObjectName("uiVirtualBoxWizardPage")
|
||||
self.gridLayout = QtWidgets.QGridLayout(self.uiVirtualBoxWizardPage)
|
||||
self.uiVMwareWizardPage = QtWidgets.QWizardPage()
|
||||
self.uiVMwareWizardPage.setObjectName("uiVMwareWizardPage")
|
||||
self.gridLayout = QtWidgets.QGridLayout(self.uiVMwareWizardPage)
|
||||
self.gridLayout.setObjectName("gridLayout")
|
||||
self.uiVMListLabel = QtWidgets.QLabel(self.uiVirtualBoxWizardPage)
|
||||
self.uiVMListLabel = QtWidgets.QLabel(self.uiVMwareWizardPage)
|
||||
self.uiVMListLabel.setObjectName("uiVMListLabel")
|
||||
self.gridLayout.addWidget(self.uiVMListLabel, 0, 0, 1, 1)
|
||||
self.uiVMListComboBox = QtWidgets.QComboBox(self.uiVirtualBoxWizardPage)
|
||||
self.uiVMListComboBox = QtWidgets.QComboBox(self.uiVMwareWizardPage)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
@@ -63,11 +62,11 @@ class Ui_VMwareVMWizard(object):
|
||||
self.uiVMListComboBox.setSizePolicy(sizePolicy)
|
||||
self.uiVMListComboBox.setObjectName("uiVMListComboBox")
|
||||
self.gridLayout.addWidget(self.uiVMListComboBox, 0, 1, 1, 1)
|
||||
self.uiBaseVMCheckBox = QtWidgets.QCheckBox(self.uiVirtualBoxWizardPage)
|
||||
self.uiBaseVMCheckBox = QtWidgets.QCheckBox(self.uiVMwareWizardPage)
|
||||
self.uiBaseVMCheckBox.setEnabled(True)
|
||||
self.uiBaseVMCheckBox.setObjectName("uiBaseVMCheckBox")
|
||||
self.gridLayout.addWidget(self.uiBaseVMCheckBox, 1, 0, 1, 2)
|
||||
VMwareVMWizard.addPage(self.uiVirtualBoxWizardPage)
|
||||
VMwareVMWizard.addPage(self.uiVMwareWizardPage)
|
||||
|
||||
self.retranslateUi(VMwareVMWizard)
|
||||
QtCore.QMetaObject.connectSlotsByName(VMwareVMWizard)
|
||||
@@ -82,8 +81,8 @@ class Ui_VMwareVMWizard(object):
|
||||
self.uiLocalRadioButton.setText(_translate("VMwareVMWizard", "Run this VMware VM on my local computer"))
|
||||
self.uiRemoteServersGroupBox.setTitle(_translate("VMwareVMWizard", "Remote servers"))
|
||||
self.uiRemoteServersLabel.setText(_translate("VMwareVMWizard", "Run on server:"))
|
||||
self.uiVirtualBoxWizardPage.setTitle(_translate("VMwareVMWizard", "VMware Virtual Machine"))
|
||||
self.uiVirtualBoxWizardPage.setSubTitle(_translate("VMwareVMWizard", "Please choose a VMware virtual machine from the list."))
|
||||
self.uiVMwareWizardPage.setTitle(_translate("VMwareVMWizard", "VMware Virtual Machine"))
|
||||
self.uiVMwareWizardPage.setSubTitle(_translate("VMwareVMWizard", "Please choose a VMware virtual machine from the list."))
|
||||
self.uiVMListLabel.setText(_translate("VMwareVMWizard", "VM list:"))
|
||||
self.uiBaseVMCheckBox.setText(_translate("VMwareVMWizard", "Use as a linked base VM (experimental)"))
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
19
gns3/node.py
19
gns3/node.py
@@ -100,15 +100,11 @@ class Node(BaseNode):
|
||||
for key in data:
|
||||
if key not in self._settings or self._settings[key] != data[key]:
|
||||
changed = True
|
||||
|
||||
if not changed:
|
||||
return
|
||||
|
||||
# If it's the initialization we don't resend it
|
||||
# to the server
|
||||
if self._settings["x"] is not None:
|
||||
self._update(data)
|
||||
else:
|
||||
self._settings.update(data)
|
||||
self._update(data)
|
||||
|
||||
def setSymbol(self, symbol):
|
||||
self._settings["symbol"] = symbol
|
||||
@@ -227,10 +223,9 @@ class Node(BaseNode):
|
||||
Update the node on the controller
|
||||
"""
|
||||
|
||||
if self.initialized():
|
||||
log.debug("{} is updating settings: {}".format(self.name(), params))
|
||||
body = self._prepareBody(params)
|
||||
self.controllerHttpPut("/nodes/{node_id}".format(node_id=self._node_id), self.updateNodeCallback, body=body, timeout=timeout, showProgress=False)
|
||||
log.debug("{} is updating settings: {}".format(self.name(), params))
|
||||
body = self._prepareBody(params)
|
||||
self.controllerHttpPut("/nodes/{node_id}".format(node_id=self._node_id), self.updateNodeCallback, body=body, timeout=timeout, showProgress=False)
|
||||
|
||||
def updateNodeCallback(self, result, error=False, **kwargs):
|
||||
"""
|
||||
@@ -263,6 +258,7 @@ class Node(BaseNode):
|
||||
node_id=self._node_id),
|
||||
self._duplicateCallback,
|
||||
body=body,
|
||||
progressText="Duplicating node {}...".format(self.name()),
|
||||
timeout=None)
|
||||
|
||||
def _duplicateCallback(self, result, error=False, **kwargs):
|
||||
@@ -355,7 +351,6 @@ class Node(BaseNode):
|
||||
return False
|
||||
|
||||
result = self._parseResponse(result)
|
||||
self._created = True
|
||||
self._createCallback(result)
|
||||
|
||||
if self._loading:
|
||||
@@ -386,6 +381,8 @@ class Node(BaseNode):
|
||||
"""
|
||||
|
||||
if not skip_controller:
|
||||
for link in self.links():
|
||||
link.setDeleting()
|
||||
self.controllerHttpDelete("/nodes/{node_id}".format(node_id=self._node_id), self._deleteCallback)
|
||||
else:
|
||||
self.deleted_signal.emit()
|
||||
|
||||
@@ -131,7 +131,6 @@ class NodesView(QtWidgets.QTreeWidget):
|
||||
item.setData(1, QtCore.Qt.UserRole, "appliance_template")
|
||||
item.setSizeHint(0, QtCore.QSize(32, 32))
|
||||
Controller.instance().getSymbolIcon(appliance.get("symbol"), qpartial(self._setItemIcon, item), fallback=":/symbols/" + appliance["category"] + ".svg")
|
||||
|
||||
self.sortByColumn(0, QtCore.Qt.AscendingOrder)
|
||||
|
||||
def _setItemIcon(self, item, icon):
|
||||
|
||||
@@ -316,8 +316,10 @@ class GeneralPreferencesPage(QtWidgets.QWidget, Ui_GeneralPreferencesPageWidget)
|
||||
|
||||
self.uiSceneWidthSpinBox.setValue(settings["scene_width"])
|
||||
self.uiSceneHeightSpinBox.setValue(settings["scene_height"])
|
||||
self.uiGridSizeSpinBox.setValue(settings["grid_size"])
|
||||
self.uiRectangleSelectedItemCheckBox.setChecked(settings["draw_rectangle_selected_item"])
|
||||
self.uiDrawLinkStatusPointsCheckBox.setChecked(settings["draw_link_status_points"])
|
||||
self.uiShowInterfaceLabelsOnNewProject.setChecked(settings["show_interface_labels_on_new_project"])
|
||||
|
||||
qt_font = QtGui.QFont()
|
||||
if qt_font.fromString(settings["default_label_font"]):
|
||||
@@ -378,8 +380,10 @@ class GeneralPreferencesPage(QtWidgets.QWidget, Ui_GeneralPreferencesPageWidget)
|
||||
|
||||
new_graphics_view_settings = {"scene_width": self.uiSceneWidthSpinBox.value(),
|
||||
"scene_height": self.uiSceneHeightSpinBox.value(),
|
||||
"grid_size": self.uiGridSizeSpinBox.value(),
|
||||
"draw_rectangle_selected_item": self.uiRectangleSelectedItemCheckBox.isChecked(),
|
||||
"draw_link_status_points": self.uiDrawLinkStatusPointsCheckBox.isChecked(),
|
||||
"show_interface_labels_on_new_project": self.uiShowInterfaceLabelsOnNewProject.isChecked(),
|
||||
"default_label_font": self.uiDefaultLabelStylePlainTextEdit.font().toString(),
|
||||
"default_label_color": self._default_label_color.name()}
|
||||
MainWindow.instance().uiGraphicsView.setSettings(new_graphics_view_settings)
|
||||
|
||||
61
gns3/ports/port_name_factory.py
Normal file
61
gns3/ports/port_name_factory.py
Normal file
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class StandardPortNameFactory:
|
||||
"""
|
||||
Generate default port names.
|
||||
"""
|
||||
|
||||
def __new__(cls, ethernet_adapters, first_port_name, port_name_format, port_segment_size):
|
||||
ports = []
|
||||
adapter_number = interface_number = segment_number = 0
|
||||
|
||||
for adapter_number in range(adapter_number, ethernet_adapters + adapter_number):
|
||||
if first_port_name and adapter_number == 0:
|
||||
port_name = first_port_name
|
||||
else:
|
||||
port_name = port_name_format.format(interface_number,
|
||||
segment_number,
|
||||
adapter=adapter_number,
|
||||
**cls._generate_replacement(interface_number, segment_number))
|
||||
interface_number += 1
|
||||
if port_segment_size:
|
||||
if interface_number % port_segment_size == 0:
|
||||
segment_number += 1
|
||||
interface_number = 0
|
||||
else:
|
||||
segment_number += 1
|
||||
ports.append(port_name)
|
||||
return ports
|
||||
|
||||
@staticmethod
|
||||
def _generate_replacement(interface_number, segment_number):
|
||||
"""
|
||||
This will generate replacement string for
|
||||
{port0} => {port9}
|
||||
{segment0} => {segment9}
|
||||
"""
|
||||
|
||||
replacements = {}
|
||||
for i in range(0, 9):
|
||||
replacements["port" + str(i)] = interface_number + i
|
||||
replacements["segment" + str(i)] = segment_number + i
|
||||
return replacements
|
||||
@@ -49,6 +49,7 @@ class Project(QtCore.QObject):
|
||||
# Called when project is fully loaded
|
||||
project_loaded_signal = QtCore.Signal()
|
||||
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self._id = None
|
||||
@@ -60,14 +61,20 @@ class Project(QtCore.QObject):
|
||||
self._auto_open = False
|
||||
self._auto_close = False
|
||||
|
||||
graphic_settings = LocalConfig.instance().loadSectionSettings(self.__class__.__name__, GRAPHICS_VIEW_SETTINGS)
|
||||
config = LocalConfig.instance()
|
||||
|
||||
graphic_settings = LocalConfig.instance().loadSectionSettings("GraphicsView", GRAPHICS_VIEW_SETTINGS)
|
||||
self._scene_width = graphic_settings["scene_width"]
|
||||
self._scene_height = graphic_settings["scene_height"]
|
||||
self._zoom = graphic_settings.get("zoom", None)
|
||||
self._show_layers = graphic_settings.get("show_layers", False)
|
||||
self._snap_to_grid = graphic_settings.get("snap_to_grid", False)
|
||||
self._show_grid = graphic_settings.get("show_grid", False)
|
||||
self._grid_size = graphic_settings.get("grid_size", 75)
|
||||
self._show_interface_labels = graphic_settings.get("show_interface_labels", False)
|
||||
self._show_interface_labels_on_new_project = config.showInterfaceLabelsOnNewProject()
|
||||
self._variables = None
|
||||
self._supplier = None
|
||||
|
||||
self._name = "untitled"
|
||||
self._filename = None
|
||||
@@ -176,6 +183,21 @@ class Project(QtCore.QObject):
|
||||
"""
|
||||
return self._show_grid
|
||||
|
||||
def setGridSize(self, grid_size):
|
||||
"""
|
||||
Sets the grid size
|
||||
"""
|
||||
|
||||
self._grid_size = grid_size
|
||||
|
||||
def gridSize(self):
|
||||
"""
|
||||
Returns the grid size
|
||||
:return: integer
|
||||
"""
|
||||
|
||||
return self._grid_size
|
||||
|
||||
def setShowInterfaceLabels(self, show_interface_labels):
|
||||
"""
|
||||
Sets show interface labels mode
|
||||
@@ -189,6 +211,32 @@ class Project(QtCore.QObject):
|
||||
"""
|
||||
return self._show_interface_labels
|
||||
|
||||
def setVariables(self, variables):
|
||||
"""
|
||||
Sets variables of project
|
||||
"""
|
||||
self._variables = variables
|
||||
|
||||
def variables(self):
|
||||
"""
|
||||
Returns variables assigned to the project
|
||||
:return: boolean
|
||||
"""
|
||||
return self._variables
|
||||
|
||||
def setSupplier(self, supplier):
|
||||
"""
|
||||
Sets supplier of project
|
||||
"""
|
||||
self._supplier = supplier
|
||||
|
||||
def supplier(self):
|
||||
"""
|
||||
Returns supplier
|
||||
:return: boolean
|
||||
"""
|
||||
return self._supplier
|
||||
|
||||
def setName(self, name):
|
||||
"""
|
||||
Set project name
|
||||
@@ -265,7 +313,11 @@ class Project(QtCore.QObject):
|
||||
"""
|
||||
Duplicate a project
|
||||
"""
|
||||
Controller.instance().post("/projects/{project_id}/duplicate".format(project_id=self._id), qpartial(self._duplicateCallback, callback), body={"name": name, "path": path}, timeout=None)
|
||||
Controller.instance().post("/projects/{project_id}/duplicate".format(project_id=self._id),
|
||||
qpartial(self._duplicateCallback, callback),
|
||||
body={"name": name, "path": path},
|
||||
progressText="Duplicating project '{}'...".format(name),
|
||||
timeout=None)
|
||||
|
||||
def _duplicateCallback(self, callback, result, error=False, **kwargs):
|
||||
if error:
|
||||
@@ -373,7 +425,8 @@ class Project(QtCore.QObject):
|
||||
"""
|
||||
body = {
|
||||
"name": self._name,
|
||||
"path": self.filesDir()
|
||||
"path": self.filesDir(),
|
||||
"show_interface_labels": self._show_interface_labels_on_new_project
|
||||
}
|
||||
Controller.instance().post("/projects", self._projectCreatedCallback, body=body)
|
||||
|
||||
@@ -392,7 +445,10 @@ class Project(QtCore.QObject):
|
||||
"show_layers": self._show_layers,
|
||||
"snap_to_grid": self._snap_to_grid,
|
||||
"show_grid": self._show_grid,
|
||||
"show_interface_labels": self._show_interface_labels
|
||||
"grid_size": self._grid_size,
|
||||
"show_interface_labels": self._show_interface_labels,
|
||||
"variables": self._variables,
|
||||
"supplier": self._supplier
|
||||
}
|
||||
self.put("", self._projectUpdatedCallback, body=body)
|
||||
|
||||
@@ -412,7 +468,9 @@ class Project(QtCore.QObject):
|
||||
self._closed = False
|
||||
self._closing = False
|
||||
self._startListenNotifications()
|
||||
|
||||
self.project_updated_signal.emit()
|
||||
self.project_loaded_signal.emit()
|
||||
|
||||
def _parseResponse(self, result):
|
||||
"""
|
||||
@@ -431,6 +489,12 @@ class Project(QtCore.QObject):
|
||||
self._show_layers = result.get("show_layers", False)
|
||||
self._snap_to_grid = result.get("snap_to_grid", False)
|
||||
self._show_grid = result.get("show_grid", False)
|
||||
self._variables = result.get("variables", None)
|
||||
self._supplier = result.get("supplier", None)
|
||||
|
||||
grid_size = result.get("grid_size", None)
|
||||
if grid_size:
|
||||
self._grid_size = grid_size
|
||||
self._show_interface_labels = result.get("show_interface_labels", False)
|
||||
|
||||
def load(self, path=None):
|
||||
|
||||
@@ -60,6 +60,14 @@ class QImageSvgRenderer(QtSvg.QSvgRenderer):
|
||||
res = super().load(path_or_data)
|
||||
# If we can't render a SVG we load and base64 the image to create a SVG
|
||||
if self.isValid():
|
||||
if not path_or_data.startswith(":") and path_exists:
|
||||
try:
|
||||
with open(path_or_data, "rb") as f:
|
||||
self._svg = f.read().decode()
|
||||
except UnicodeError as e:
|
||||
log.error("Could not decode '{}' content: {}".format(path_or_data, e))
|
||||
except OSError as e:
|
||||
log.error("Could not read '{}': {}".format(path_or_data, e))
|
||||
return res
|
||||
except ET.ParseError:
|
||||
pass
|
||||
|
||||
@@ -20,7 +20,10 @@
|
||||
import json
|
||||
import os
|
||||
import urllib
|
||||
import shutil
|
||||
from ssl import CertificateError
|
||||
|
||||
from gns3.controller import Controller
|
||||
from ..local_config import LocalConfig
|
||||
from ..local_server_config import LocalServerConfig
|
||||
from ..settings import LOCAL_SERVER_SETTINGS
|
||||
@@ -96,14 +99,18 @@ class Config:
|
||||
return False
|
||||
return True
|
||||
|
||||
def add_appliance(self, appliance_config, server):
|
||||
def add_appliance(self, appliance_config, server, controller_symbols=None):
|
||||
"""
|
||||
Add appliance to the user configuration
|
||||
|
||||
:param appliance_config: Dictionary with appliance configuration
|
||||
:param server
|
||||
:param controller_symbols: Symbols located on controller
|
||||
"""
|
||||
|
||||
if controller_symbols is None:
|
||||
controller_symbols = []
|
||||
|
||||
new_config = {
|
||||
"server": server,
|
||||
"name": appliance_config["name"]
|
||||
@@ -124,7 +131,7 @@ class Config:
|
||||
new_config["category"] = 1
|
||||
|
||||
if "symbol" in appliance_config:
|
||||
new_config["symbol"] = self._set_symbol(appliance_config["symbol"])
|
||||
new_config["symbol"] = self._set_symbol(appliance_config["symbol"], controller_symbols)
|
||||
|
||||
if new_config.get("symbol") is None:
|
||||
if appliance_config["category"] == "guest":
|
||||
@@ -167,6 +174,7 @@ class Config:
|
||||
new_config["console_type"] = appliance_config["docker"].get("console_type", "telnet")
|
||||
new_config["console_http_port"] = appliance_config["docker"].get("console_http_port", 80)
|
||||
new_config["console_http_path"] = appliance_config["docker"].get("console_http_path", "/")
|
||||
new_config["extra_hosts"] = appliance_config["docker"].get("extra_hosts", "")
|
||||
self._config["Docker"]["containers"].append(new_config)
|
||||
|
||||
def _add_dynamips_config(self, new_config, appliance_config):
|
||||
@@ -276,9 +284,9 @@ class Config:
|
||||
self._config["Qemu"].setdefault("vms", [])
|
||||
self._config["Qemu"]["vms"].append(new_config)
|
||||
|
||||
def _set_symbol(self, symbol):
|
||||
def _set_symbol(self, symbol, controller_symbols):
|
||||
"""
|
||||
Download symbol for the web if need
|
||||
Check if exists on controller or download symbol from the web if needed
|
||||
"""
|
||||
|
||||
# GNS3 builtin symbol
|
||||
@@ -289,11 +297,25 @@ class Config:
|
||||
if os.path.exists(path):
|
||||
return os.path.basename(path)
|
||||
|
||||
is_symbol_on_controller = len([s for s in controller_symbols
|
||||
if s['symbol_id'] == symbol]) > 0
|
||||
|
||||
if is_symbol_on_controller:
|
||||
cached = Controller.instance().getStaticCachedPath(symbol)
|
||||
if os.path.exists(cached):
|
||||
try:
|
||||
shutil.copy(cached, path)
|
||||
except IOError as e:
|
||||
log.warning("Cannot copy cached symbol from `{}` to `{}` due `{}`".format(
|
||||
cached, path, str(e)
|
||||
))
|
||||
return symbol
|
||||
|
||||
url = "https://raw.githubusercontent.com/GNS3/gns3-registry/master/symbols/{}".format(symbol)
|
||||
try:
|
||||
urllib.request.urlretrieve(url, path)
|
||||
return os.path.basename(path)
|
||||
except OSError:
|
||||
except (OSError, CertificateError):
|
||||
return None
|
||||
|
||||
def _relative_image_path(self, image_dir_type, path):
|
||||
|
||||
@@ -97,7 +97,7 @@ class Image:
|
||||
|
||||
if os.path.exists(self.path + ".md5sum"):
|
||||
with open(self.path + ".md5sum", encoding="utf-8") as f:
|
||||
self._md5sum = f.read()
|
||||
self._md5sum = f.read().strip()
|
||||
return self._md5sum
|
||||
|
||||
if not os.path.isfile(self.path):
|
||||
|
||||
@@ -131,6 +131,10 @@
|
||||
"console_http_path": {
|
||||
"description": "Path of the web interface",
|
||||
"type": "string"
|
||||
},
|
||||
"extra_hosts": {
|
||||
"description": "Hosts which will be written to /etc/hosts into container" ,
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
||||
@@ -281,6 +281,7 @@ NODES_VIEW_SETTINGS = {
|
||||
GRAPHICS_VIEW_SETTINGS = {
|
||||
"scene_width": 2000,
|
||||
"scene_height": 1000,
|
||||
"grid_size": 75,
|
||||
"draw_rectangle_selected_item": False,
|
||||
"draw_link_status_points": True,
|
||||
"default_label_font": "TypeWriter,10,-1,5,75,0,0,0,0,0",
|
||||
@@ -289,7 +290,8 @@ GRAPHICS_VIEW_SETTINGS = {
|
||||
"show_layers": False,
|
||||
"snap_to_grid": False,
|
||||
"show_grid": False,
|
||||
"show_interface_labels": False
|
||||
"show_interface_labels": False,
|
||||
"show_interface_labels_on_new_project": False
|
||||
}
|
||||
|
||||
LOCAL_SERVER_SETTINGS = {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
Functions to start external console terminals.
|
||||
"""
|
||||
|
||||
from .qt import QtCore, QtWidgets
|
||||
from .qt import QtCore
|
||||
|
||||
import os
|
||||
import sys
|
||||
@@ -58,7 +58,7 @@ class ConsoleThread(QtCore.QThread):
|
||||
try:
|
||||
args = shlex.split(command)
|
||||
except ValueError:
|
||||
self.consoleError.emit("Syntax error in command: {}".format(command))
|
||||
self.consoleError.emit("Syntax error in command: '{}'".format(command))
|
||||
return
|
||||
subprocess.call(args, env=os.environ)
|
||||
|
||||
@@ -70,7 +70,7 @@ class ConsoleThread(QtCore.QThread):
|
||||
# replace the place-holders by the actual values
|
||||
command = self._command.replace("%h", host)
|
||||
command = command.replace("%p", str(port))
|
||||
command = command.replace("%d", self._name)
|
||||
command = command.replace("%d", self._name.replace('"', '\\"'))
|
||||
command = command.replace("%i", self._node.project().id())
|
||||
command = command.replace("%n", str(self._node.id()))
|
||||
command = command.replace("%c", Controller.instance().httpClient().fullUrl())
|
||||
@@ -83,8 +83,7 @@ class ConsoleThread(QtCore.QThread):
|
||||
try:
|
||||
self.exec_command(command)
|
||||
except (OSError, subprocess.SubprocessError) as e:
|
||||
pass
|
||||
# log.warning('could not start Telnet console "{}": {}'.format(self._command, e))
|
||||
self.consoleError.emit("Could not start Telnet console with command '{}': {}".format(command, e))
|
||||
finally:
|
||||
log.debug('Telnet console {}:{} closed'.format(host, port))
|
||||
if sys.platform.startswith("darwin") and "osascript" in command:
|
||||
@@ -115,4 +114,4 @@ def nodeTelnetConsole(node, port, command=None):
|
||||
|
||||
|
||||
def _consoleErrorSlot(message):
|
||||
QtWidgets.QMessageBox.critical(MainWindow.instance(), "Error", message)
|
||||
log.error(message)
|
||||
|
||||
@@ -30,6 +30,7 @@ from .utils.progress_dialog import ProgressDialog
|
||||
from .utils.export_project_worker import ExportProjectWorker
|
||||
from .utils.import_project_worker import ImportProjectWorker
|
||||
from .dialogs.file_editor_dialog import FileEditorDialog
|
||||
from .dialogs.project_welcome_dialog import ProjectWelcomeDialog
|
||||
|
||||
from .modules import MODULES
|
||||
from .modules.module_error import ModuleError
|
||||
@@ -134,12 +135,16 @@ class Topology(QtCore.QObject):
|
||||
|
||||
self.project_changed_signal.emit()
|
||||
|
||||
|
||||
def _projectUpdatedSlot(self):
|
||||
if not self._project or not self._project.filesDir() or not self._project.filename():
|
||||
return
|
||||
self._main_window.setWindowTitle("{name} - GNS3".format(name=self._project.name()))
|
||||
project_file = os.path.join(self._project.filesDir(), self._project.filename())
|
||||
self._main_window.uiGraphicsView.setSceneSize(self._project.sceneWidth(), self._project.sceneHeight())
|
||||
self._main_window.uiGraphicsView.setGridSize(self._project.gridSize())
|
||||
self._main_window.uiShowGridAction.setChecked(self._project.showGrid())
|
||||
self._main_window.showGrid(self._project.showGrid())
|
||||
if os.path.exists(project_file):
|
||||
self._main_window.updateRecentFileSettings(project_file)
|
||||
self._main_window.updateRecentFileActions()
|
||||
@@ -153,6 +158,7 @@ class Topology(QtCore.QObject):
|
||||
self._main_window.uiShowLayersAction.setChecked(self._project.showLayers())
|
||||
self._main_window.showLayers(self._project.showLayers())
|
||||
|
||||
self._main_window.uiGraphicsView.setGridSize(self._project.gridSize())
|
||||
self._main_window.uiShowGridAction.setChecked(self._project.showGrid())
|
||||
self._main_window.showGrid(self._project.showGrid())
|
||||
|
||||
@@ -164,12 +170,29 @@ class Topology(QtCore.QObject):
|
||||
|
||||
self._main_window.uiGraphicsView.setZoom(self._project.zoom())
|
||||
|
||||
supplier = self._project.supplier()
|
||||
if supplier:
|
||||
self._main_window.uiGraphicsView.addLogo(
|
||||
supplier.get('logo', None),
|
||||
supplier.get('url', None)
|
||||
)
|
||||
|
||||
self._displayProjectWelcomeDialog()
|
||||
|
||||
def _displayProjectWelcomeDialog(self):
|
||||
variables = self.project().variables()
|
||||
if variables:
|
||||
missing = [v for v in variables if v.get("value", "").strip() == ""]
|
||||
if len(missing) > 0:
|
||||
dialog = ProjectWelcomeDialog(self._main_window, self.project())
|
||||
dialog.show()
|
||||
dialog.exec_()
|
||||
|
||||
def createLoadProject(self, project_settings):
|
||||
"""
|
||||
Create load a project based on settings, not on the .gns3
|
||||
"""
|
||||
self.setProject(None)
|
||||
|
||||
from .project import Project
|
||||
project = Project()
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ class TopologyNodeItem(QtWidgets.QTreeWidgetItem):
|
||||
if self._node.consoleType() and self._node.console():
|
||||
self.setText(1, "{} {}:{}".format(self._node.consoleType(), self._node.consoleHost(), self._node.console()))
|
||||
else:
|
||||
self.setText(1, "not supported")
|
||||
self.setText(1, "none")
|
||||
self.refreshLinks()
|
||||
self._parent.invisibleRootItem().sortChildren(0, QtCore.Qt.AscendingOrder)
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>407</width>
|
||||
<height>249</height>
|
||||
<width>465</width>
|
||||
<height>272</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -106,21 +106,21 @@ p, li { white-space: pre-wrap; }
|
||||
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'Noto Sans'; font-size:9pt; font-weight:400; font-style:normal;">
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:11pt; font-weight:600;">Developers</span></p>
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Ubuntu'; font-size:11pt;"><br /></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:11pt;">Jeremy Grossmann</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:11pt;">Julien Duponchelle</span></p>
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Ubuntu'; font-size:11pt;"><br /></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:11pt; font-weight:600;">Contributors</span></p>
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Ubuntu'; font-size:11pt; font-weight:600;"><br /></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:11pt;">James E. Carpenter (IOU support)</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:11pt;">Daniel Lintott (NET file import)</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:11pt;">Marc Weisel (Mac OS X packaging)</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:11pt;">Alexey Eromenko (VirtualBox support)</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:11pt;">Vasil Rangelov (Qemu &amp; GUI improvements)</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:11pt;">Bernhard Ehlers (IOU NVRAM import/export)</span></p>
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Ubuntu'; font-size:11pt;"><br /></p></body></html></string>
|
||||
</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;">
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Developers</span></p>
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Jeremy Grossmann</p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Julien Duponchelle</p>
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Contributors</span></p>
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600;"><br /></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">James E. Carpenter (IOU support)</p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Daniel Lintott (NET file import)</p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Marc Weisel (Mac OS X packaging)</p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Alexey Eromenko (VirtualBox support)</p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Vasil Rangelov (Qemu &amp; GUI improvements)</p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Bernhard Ehlers (IOU NVRAM import/export)</p>
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html></string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextBrowserInteraction</set>
|
||||
@@ -143,7 +143,8 @@ p, li { white-space: pre-wrap; }
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="plainText">
|
||||
<string>James Borden
|
||||
<string>Mark Fahy
|
||||
James Borden
|
||||
Tenzin Rigdol Oshoe
|
||||
Brian Jacobson
|
||||
Chad Hoevenaars
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,8 +9,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>549</width>
|
||||
<height>234</height>
|
||||
<width>955</width>
|
||||
<height>387</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -21,99 +21,126 @@
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="uiProjectNameLabel">
|
||||
<property name="text">
|
||||
<string>Project Name:</string>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="uiGeneralTab">
|
||||
<attribute name="title">
|
||||
<string>General</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="uiGeneralGrid">
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="uiSceneWidthLabel">
|
||||
<property name="text">
|
||||
<string>Scene width:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0" colspan="3">
|
||||
<widget class="QCheckBox" name="uiProjectAutoCloseCheckBox">
|
||||
<property name="text">
|
||||
<string>Leave this project running in the background when closing GNS3</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="uiProjectNameLabel">
|
||||
<property name="text">
|
||||
<string>Project Name:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QSpinBox" name="uiSceneWidthSpinBox">
|
||||
<property name="suffix">
|
||||
<string> px</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>500</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>1000000</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QSpinBox" name="uiGridSizeSpinBox"/>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QSpinBox" name="uiSceneHeightSpinBox">
|
||||
<property name="suffix">
|
||||
<string> px</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>500</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>1000000</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="uiSceneHeightLabel">
|
||||
<property name="text">
|
||||
<string>Scene height:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="uiProjectNameLineEdit"/>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="uiGridSizeLabel">
|
||||
<property name="text">
|
||||
<string>Grid size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="3">
|
||||
<widget class="QCheckBox" name="uiProjectAutoOpenCheckBox">
|
||||
<property name="text">
|
||||
<string>Open this project in the background when GNS3 server starts</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0" colspan="3">
|
||||
<widget class="QCheckBox" name="uiProjectAutoStartCheckBox">
|
||||
<property name="text">
|
||||
<string>Start all nodes when this project is opened</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="uiGlobalVariablesTab">
|
||||
<attribute name="title">
|
||||
<string>Global variables</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="uiGlobalVariablesGrid"/>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="uiProjectNameLineEdit"/>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="3">
|
||||
<widget class="QCheckBox" name="uiProjectAutoCloseCheckBox">
|
||||
<property name="text">
|
||||
<string>Leave this project running in the background when closing GNS3</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QSpinBox" name="uiSceneHeightSpinBox">
|
||||
<property name="suffix">
|
||||
<string> px</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>500</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>1000000</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="3">
|
||||
<widget class="QCheckBox" name="uiProjectAutoOpenCheckBox">
|
||||
<property name="text">
|
||||
<string>Open this project in the background when GNS3 server starts</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="3">
|
||||
<widget class="QCheckBox" name="uiProjectAutoStartCheckBox">
|
||||
<property name="text">
|
||||
<string>Start all nodes when this project is opened</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="uiSceneWidthSpinBox">
|
||||
<property name="suffix">
|
||||
<string> px</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>500</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>1000000</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0" colspan="3">
|
||||
<item row="1" column="0">
|
||||
<widget class="QDialogButtonBox" name="uiButtonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="uiSceneWidthLabel">
|
||||
<property name="text">
|
||||
<string>Scene width:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="uiSceneHeightLabel">
|
||||
<property name="text">
|
||||
<string>Scene height:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/ui/edit_project_dialog.ui'
|
||||
# Form implementation generated from reading ui file '/home/dominik/projects/gns3-gui/gns3/ui/edit_project_dialog.ui'
|
||||
#
|
||||
# Created: Sat Oct 8 14:06:27 2016
|
||||
# by: PyQt5 UI code generator 5.2.1
|
||||
# Created by: PyQt5 UI code generator 5.8.2
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
@@ -13,50 +12,69 @@ class Ui_EditProjectDialog(object):
|
||||
def setupUi(self, EditProjectDialog):
|
||||
EditProjectDialog.setObjectName("EditProjectDialog")
|
||||
EditProjectDialog.setWindowModality(QtCore.Qt.ApplicationModal)
|
||||
EditProjectDialog.resize(549, 234)
|
||||
EditProjectDialog.resize(955, 387)
|
||||
EditProjectDialog.setModal(True)
|
||||
self.gridLayout = QtWidgets.QGridLayout(EditProjectDialog)
|
||||
self.gridLayout.setObjectName("gridLayout")
|
||||
self.uiProjectNameLabel = QtWidgets.QLabel(EditProjectDialog)
|
||||
self.uiProjectNameLabel.setObjectName("uiProjectNameLabel")
|
||||
self.gridLayout.addWidget(self.uiProjectNameLabel, 0, 0, 1, 1)
|
||||
self.uiProjectNameLineEdit = QtWidgets.QLineEdit(EditProjectDialog)
|
||||
self.uiProjectNameLineEdit.setObjectName("uiProjectNameLineEdit")
|
||||
self.gridLayout.addWidget(self.uiProjectNameLineEdit, 0, 1, 1, 1)
|
||||
self.uiProjectAutoCloseCheckBox = QtWidgets.QCheckBox(EditProjectDialog)
|
||||
self.tabWidget = QtWidgets.QTabWidget(EditProjectDialog)
|
||||
self.tabWidget.setObjectName("tabWidget")
|
||||
self.uiGeneralTab = QtWidgets.QWidget()
|
||||
self.uiGeneralTab.setObjectName("uiGeneralTab")
|
||||
self.uiGeneralGrid = QtWidgets.QGridLayout(self.uiGeneralTab)
|
||||
self.uiGeneralGrid.setObjectName("uiGeneralGrid")
|
||||
self.uiSceneWidthLabel = QtWidgets.QLabel(self.uiGeneralTab)
|
||||
self.uiSceneWidthLabel.setObjectName("uiSceneWidthLabel")
|
||||
self.uiGeneralGrid.addWidget(self.uiSceneWidthLabel, 2, 0, 1, 1)
|
||||
self.uiProjectAutoCloseCheckBox = QtWidgets.QCheckBox(self.uiGeneralTab)
|
||||
self.uiProjectAutoCloseCheckBox.setObjectName("uiProjectAutoCloseCheckBox")
|
||||
self.gridLayout.addWidget(self.uiProjectAutoCloseCheckBox, 5, 0, 1, 3)
|
||||
self.uiSceneHeightSpinBox = QtWidgets.QSpinBox(EditProjectDialog)
|
||||
self.uiSceneHeightSpinBox.setMinimum(500)
|
||||
self.uiSceneHeightSpinBox.setMaximum(1000000)
|
||||
self.uiSceneHeightSpinBox.setObjectName("uiSceneHeightSpinBox")
|
||||
self.gridLayout.addWidget(self.uiSceneHeightSpinBox, 2, 1, 1, 1)
|
||||
self.uiProjectAutoOpenCheckBox = QtWidgets.QCheckBox(EditProjectDialog)
|
||||
self.uiProjectAutoOpenCheckBox.setObjectName("uiProjectAutoOpenCheckBox")
|
||||
self.gridLayout.addWidget(self.uiProjectAutoOpenCheckBox, 3, 0, 1, 3)
|
||||
self.uiProjectAutoStartCheckBox = QtWidgets.QCheckBox(EditProjectDialog)
|
||||
self.uiProjectAutoStartCheckBox.setObjectName("uiProjectAutoStartCheckBox")
|
||||
self.gridLayout.addWidget(self.uiProjectAutoStartCheckBox, 4, 0, 1, 3)
|
||||
self.uiSceneWidthSpinBox = QtWidgets.QSpinBox(EditProjectDialog)
|
||||
self.uiGeneralGrid.addWidget(self.uiProjectAutoCloseCheckBox, 9, 0, 1, 3)
|
||||
self.uiProjectNameLabel = QtWidgets.QLabel(self.uiGeneralTab)
|
||||
self.uiProjectNameLabel.setObjectName("uiProjectNameLabel")
|
||||
self.uiGeneralGrid.addWidget(self.uiProjectNameLabel, 1, 0, 1, 1)
|
||||
self.uiSceneWidthSpinBox = QtWidgets.QSpinBox(self.uiGeneralTab)
|
||||
self.uiSceneWidthSpinBox.setMinimum(500)
|
||||
self.uiSceneWidthSpinBox.setMaximum(1000000)
|
||||
self.uiSceneWidthSpinBox.setObjectName("uiSceneWidthSpinBox")
|
||||
self.gridLayout.addWidget(self.uiSceneWidthSpinBox, 1, 1, 1, 1)
|
||||
self.uiGeneralGrid.addWidget(self.uiSceneWidthSpinBox, 2, 1, 1, 1)
|
||||
self.uiGridSizeSpinBox = QtWidgets.QSpinBox(self.uiGeneralTab)
|
||||
self.uiGridSizeSpinBox.setObjectName("uiGridSizeSpinBox")
|
||||
self.uiGeneralGrid.addWidget(self.uiGridSizeSpinBox, 4, 1, 1, 1)
|
||||
self.uiSceneHeightSpinBox = QtWidgets.QSpinBox(self.uiGeneralTab)
|
||||
self.uiSceneHeightSpinBox.setMinimum(500)
|
||||
self.uiSceneHeightSpinBox.setMaximum(1000000)
|
||||
self.uiSceneHeightSpinBox.setObjectName("uiSceneHeightSpinBox")
|
||||
self.uiGeneralGrid.addWidget(self.uiSceneHeightSpinBox, 3, 1, 1, 1)
|
||||
self.uiSceneHeightLabel = QtWidgets.QLabel(self.uiGeneralTab)
|
||||
self.uiSceneHeightLabel.setObjectName("uiSceneHeightLabel")
|
||||
self.uiGeneralGrid.addWidget(self.uiSceneHeightLabel, 3, 0, 1, 1)
|
||||
self.uiProjectNameLineEdit = QtWidgets.QLineEdit(self.uiGeneralTab)
|
||||
self.uiProjectNameLineEdit.setObjectName("uiProjectNameLineEdit")
|
||||
self.uiGeneralGrid.addWidget(self.uiProjectNameLineEdit, 1, 1, 1, 1)
|
||||
self.uiGridSizeLabel = QtWidgets.QLabel(self.uiGeneralTab)
|
||||
self.uiGridSizeLabel.setObjectName("uiGridSizeLabel")
|
||||
self.uiGeneralGrid.addWidget(self.uiGridSizeLabel, 4, 0, 1, 1)
|
||||
self.uiProjectAutoOpenCheckBox = QtWidgets.QCheckBox(self.uiGeneralTab)
|
||||
self.uiProjectAutoOpenCheckBox.setObjectName("uiProjectAutoOpenCheckBox")
|
||||
self.uiGeneralGrid.addWidget(self.uiProjectAutoOpenCheckBox, 7, 0, 1, 3)
|
||||
self.uiProjectAutoStartCheckBox = QtWidgets.QCheckBox(self.uiGeneralTab)
|
||||
self.uiProjectAutoStartCheckBox.setObjectName("uiProjectAutoStartCheckBox")
|
||||
self.uiGeneralGrid.addWidget(self.uiProjectAutoStartCheckBox, 8, 0, 1, 3)
|
||||
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.uiGeneralGrid.addItem(spacerItem, 10, 0, 1, 1)
|
||||
self.tabWidget.addTab(self.uiGeneralTab, "")
|
||||
self.uiGlobalVariablesTab = QtWidgets.QWidget()
|
||||
self.uiGlobalVariablesTab.setObjectName("uiGlobalVariablesTab")
|
||||
self.uiGlobalVariablesGrid = QtWidgets.QGridLayout(self.uiGlobalVariablesTab)
|
||||
self.uiGlobalVariablesGrid.setObjectName("uiGlobalVariablesGrid")
|
||||
self.tabWidget.addTab(self.uiGlobalVariablesTab, "")
|
||||
self.gridLayout.addWidget(self.tabWidget, 0, 0, 1, 1)
|
||||
self.uiButtonBox = QtWidgets.QDialogButtonBox(EditProjectDialog)
|
||||
self.uiButtonBox.setOrientation(QtCore.Qt.Horizontal)
|
||||
self.uiButtonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
|
||||
self.uiButtonBox.setObjectName("uiButtonBox")
|
||||
self.gridLayout.addWidget(self.uiButtonBox, 6, 0, 1, 3)
|
||||
self.uiSceneWidthLabel = QtWidgets.QLabel(EditProjectDialog)
|
||||
self.uiSceneWidthLabel.setObjectName("uiSceneWidthLabel")
|
||||
self.gridLayout.addWidget(self.uiSceneWidthLabel, 1, 0, 1, 1)
|
||||
self.uiSceneHeightLabel = QtWidgets.QLabel(EditProjectDialog)
|
||||
self.uiSceneHeightLabel.setObjectName("uiSceneHeightLabel")
|
||||
self.gridLayout.addWidget(self.uiSceneHeightLabel, 2, 0, 1, 1)
|
||||
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.gridLayout.addItem(spacerItem, 7, 0, 1, 1)
|
||||
self.gridLayout.addWidget(self.uiButtonBox, 1, 0, 1, 1)
|
||||
|
||||
self.retranslateUi(EditProjectDialog)
|
||||
self.tabWidget.setCurrentIndex(0)
|
||||
self.uiButtonBox.accepted.connect(EditProjectDialog.accept)
|
||||
self.uiButtonBox.rejected.connect(EditProjectDialog.reject)
|
||||
QtCore.QMetaObject.connectSlotsByName(EditProjectDialog)
|
||||
@@ -64,12 +82,15 @@ class Ui_EditProjectDialog(object):
|
||||
def retranslateUi(self, EditProjectDialog):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
EditProjectDialog.setWindowTitle(_translate("EditProjectDialog", "Edit project"))
|
||||
self.uiProjectNameLabel.setText(_translate("EditProjectDialog", "Project Name:"))
|
||||
self.uiSceneWidthLabel.setText(_translate("EditProjectDialog", "Scene width:"))
|
||||
self.uiProjectAutoCloseCheckBox.setText(_translate("EditProjectDialog", "Leave this project running in the background when closing GNS3"))
|
||||
self.uiProjectNameLabel.setText(_translate("EditProjectDialog", "Project Name:"))
|
||||
self.uiSceneWidthSpinBox.setSuffix(_translate("EditProjectDialog", " px"))
|
||||
self.uiSceneHeightSpinBox.setSuffix(_translate("EditProjectDialog", " px"))
|
||||
self.uiSceneHeightLabel.setText(_translate("EditProjectDialog", "Scene height:"))
|
||||
self.uiGridSizeLabel.setText(_translate("EditProjectDialog", "Grid size:"))
|
||||
self.uiProjectAutoOpenCheckBox.setText(_translate("EditProjectDialog", "Open this project in the background when GNS3 server starts"))
|
||||
self.uiProjectAutoStartCheckBox.setText(_translate("EditProjectDialog", "Start all nodes when this project is opened"))
|
||||
self.uiSceneWidthSpinBox.setSuffix(_translate("EditProjectDialog", " px"))
|
||||
self.uiSceneWidthLabel.setText(_translate("EditProjectDialog", "Scene width:"))
|
||||
self.uiSceneHeightLabel.setText(_translate("EditProjectDialog", "Scene height:"))
|
||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.uiGeneralTab), _translate("EditProjectDialog", "General"))
|
||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.uiGlobalVariablesTab), _translate("EditProjectDialog", "Global variables"))
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1324</width>
|
||||
<height>738</height>
|
||||
<height>741</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -647,7 +647,24 @@
|
||||
<property name="margin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<item row="9" column="0" colspan="2">
|
||||
<item row="6" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="uiRectangleSelectedItemCheckBox">
|
||||
<property name="text">
|
||||
<string>Draw a rectangle when an item is selected</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="uiSceneWidthLabel">
|
||||
<property name="text">
|
||||
<string>Default width:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="0" colspan="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QPushButton" name="uiDefaultLabelFontPushButton">
|
||||
@@ -678,13 +695,6 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="uiSceneWidthLabel">
|
||||
<property name="text">
|
||||
<string>Default width:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="uiSceneHeightLabel">
|
||||
<property name="text">
|
||||
@@ -692,55 +702,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="uiRectangleSelectedItemCheckBox">
|
||||
<property name="text">
|
||||
<string>Draw a rectangle when an item is selected</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QCheckBox" name="uiDrawLinkStatusPointsCheckBox">
|
||||
<property name="text">
|
||||
<string>Draw link status points</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="uiLabelPreviewLabel">
|
||||
<property name="text">
|
||||
<string>Default label style:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0" colspan="2">
|
||||
<widget class="QPlainTextEdit" name="uiDefaultLabelStylePlainTextEdit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>50</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="plainText">
|
||||
<string>AaBbYyZz</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QSpinBox" name="uiSceneHeightSpinBox">
|
||||
<property name="suffix">
|
||||
@@ -760,6 +721,58 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<widget class="QLabel" name="uiLabelPreviewLabel">
|
||||
<property name="text">
|
||||
<string>Default label style:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QCheckBox" name="uiDrawLinkStatusPointsCheckBox">
|
||||
<property name="text">
|
||||
<string>Draw link status points</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0" colspan="2">
|
||||
<widget class="QPlainTextEdit" name="uiDefaultLabelStylePlainTextEdit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>50</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="plainText">
|
||||
<string>AaBbYyZz</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="0">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>5</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QSpinBox" name="uiSceneWidthSpinBox">
|
||||
<property name="suffix">
|
||||
@@ -779,23 +792,33 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<item row="8" column="0">
|
||||
<widget class="QCheckBox" name="uiShowInterfaceLabelsOnNewProject">
|
||||
<property name="text">
|
||||
<string>Show interface labels on new project</string>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>5</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<widget class="QLabel" name="uiGridSizeLabel">
|
||||
<property name="text">
|
||||
<string>If you want to change the size of the current project. Via the project menu you can edit it.</string>
|
||||
<string>Grid size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QSpinBox" name="uiGridSizeSpinBox">
|
||||
<property name="minimum">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>75</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -11,7 +11,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
class Ui_GeneralPreferencesPageWidget(object):
|
||||
def setupUi(self, GeneralPreferencesPageWidget):
|
||||
GeneralPreferencesPageWidget.setObjectName("GeneralPreferencesPageWidget")
|
||||
GeneralPreferencesPageWidget.resize(1324, 738)
|
||||
GeneralPreferencesPageWidget.resize(1324, 741)
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout(GeneralPreferencesPageWidget)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.uiMiscTabWidget = QtWidgets.QTabWidget(GeneralPreferencesPageWidget)
|
||||
@@ -307,6 +307,13 @@ class Ui_GeneralPreferencesPageWidget(object):
|
||||
self.gridLayout_8 = QtWidgets.QGridLayout(self.uiSceneTab)
|
||||
self.gridLayout_8.setContentsMargins(10, 10, 10, 10)
|
||||
self.gridLayout_8.setObjectName("gridLayout_8")
|
||||
self.uiRectangleSelectedItemCheckBox = QtWidgets.QCheckBox(self.uiSceneTab)
|
||||
self.uiRectangleSelectedItemCheckBox.setChecked(True)
|
||||
self.uiRectangleSelectedItemCheckBox.setObjectName("uiRectangleSelectedItemCheckBox")
|
||||
self.gridLayout_8.addWidget(self.uiRectangleSelectedItemCheckBox, 6, 0, 1, 2)
|
||||
self.uiSceneWidthLabel = QtWidgets.QLabel(self.uiSceneTab)
|
||||
self.uiSceneWidthLabel.setObjectName("uiSceneWidthLabel")
|
||||
self.gridLayout_8.addWidget(self.uiSceneWidthLabel, 0, 0, 1, 1)
|
||||
self.horizontalLayout_5 = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout_5.setObjectName("horizontalLayout_5")
|
||||
self.uiDefaultLabelFontPushButton = QtWidgets.QPushButton(self.uiSceneTab)
|
||||
@@ -317,24 +324,24 @@ class Ui_GeneralPreferencesPageWidget(object):
|
||||
self.horizontalLayout_5.addWidget(self.uiDefaultLabelColorPushButton)
|
||||
spacerItem6 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||
self.horizontalLayout_5.addItem(spacerItem6)
|
||||
self.gridLayout_8.addLayout(self.horizontalLayout_5, 9, 0, 1, 2)
|
||||
self.uiSceneWidthLabel = QtWidgets.QLabel(self.uiSceneTab)
|
||||
self.uiSceneWidthLabel.setObjectName("uiSceneWidthLabel")
|
||||
self.gridLayout_8.addWidget(self.uiSceneWidthLabel, 0, 0, 1, 1)
|
||||
self.gridLayout_8.addLayout(self.horizontalLayout_5, 11, 0, 1, 2)
|
||||
self.uiSceneHeightLabel = QtWidgets.QLabel(self.uiSceneTab)
|
||||
self.uiSceneHeightLabel.setObjectName("uiSceneHeightLabel")
|
||||
self.gridLayout_8.addWidget(self.uiSceneHeightLabel, 2, 0, 1, 1)
|
||||
self.uiRectangleSelectedItemCheckBox = QtWidgets.QCheckBox(self.uiSceneTab)
|
||||
self.uiRectangleSelectedItemCheckBox.setChecked(True)
|
||||
self.uiRectangleSelectedItemCheckBox.setObjectName("uiRectangleSelectedItemCheckBox")
|
||||
self.gridLayout_8.addWidget(self.uiRectangleSelectedItemCheckBox, 5, 0, 1, 2)
|
||||
self.uiSceneHeightSpinBox = QtWidgets.QSpinBox(self.uiSceneTab)
|
||||
self.uiSceneHeightSpinBox.setMinimum(500)
|
||||
self.uiSceneHeightSpinBox.setMaximum(1000000)
|
||||
self.uiSceneHeightSpinBox.setSingleStep(100)
|
||||
self.uiSceneHeightSpinBox.setProperty("value", 1000)
|
||||
self.uiSceneHeightSpinBox.setObjectName("uiSceneHeightSpinBox")
|
||||
self.gridLayout_8.addWidget(self.uiSceneHeightSpinBox, 3, 0, 1, 2)
|
||||
self.uiLabelPreviewLabel = QtWidgets.QLabel(self.uiSceneTab)
|
||||
self.uiLabelPreviewLabel.setObjectName("uiLabelPreviewLabel")
|
||||
self.gridLayout_8.addWidget(self.uiLabelPreviewLabel, 9, 0, 1, 1)
|
||||
self.uiDrawLinkStatusPointsCheckBox = QtWidgets.QCheckBox(self.uiSceneTab)
|
||||
self.uiDrawLinkStatusPointsCheckBox.setChecked(True)
|
||||
self.uiDrawLinkStatusPointsCheckBox.setObjectName("uiDrawLinkStatusPointsCheckBox")
|
||||
self.gridLayout_8.addWidget(self.uiDrawLinkStatusPointsCheckBox, 6, 0, 1, 1)
|
||||
self.uiLabelPreviewLabel = QtWidgets.QLabel(self.uiSceneTab)
|
||||
self.uiLabelPreviewLabel.setObjectName("uiLabelPreviewLabel")
|
||||
self.gridLayout_8.addWidget(self.uiLabelPreviewLabel, 7, 0, 1, 1)
|
||||
self.gridLayout_8.addWidget(self.uiDrawLinkStatusPointsCheckBox, 7, 0, 1, 1)
|
||||
self.uiDefaultLabelStylePlainTextEdit = QtWidgets.QPlainTextEdit(self.uiSceneTab)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
@@ -344,14 +351,9 @@ class Ui_GeneralPreferencesPageWidget(object):
|
||||
self.uiDefaultLabelStylePlainTextEdit.setMaximumSize(QtCore.QSize(16777215, 50))
|
||||
self.uiDefaultLabelStylePlainTextEdit.setReadOnly(True)
|
||||
self.uiDefaultLabelStylePlainTextEdit.setObjectName("uiDefaultLabelStylePlainTextEdit")
|
||||
self.gridLayout_8.addWidget(self.uiDefaultLabelStylePlainTextEdit, 8, 0, 1, 2)
|
||||
self.uiSceneHeightSpinBox = QtWidgets.QSpinBox(self.uiSceneTab)
|
||||
self.uiSceneHeightSpinBox.setMinimum(500)
|
||||
self.uiSceneHeightSpinBox.setMaximum(1000000)
|
||||
self.uiSceneHeightSpinBox.setSingleStep(100)
|
||||
self.uiSceneHeightSpinBox.setProperty("value", 1000)
|
||||
self.uiSceneHeightSpinBox.setObjectName("uiSceneHeightSpinBox")
|
||||
self.gridLayout_8.addWidget(self.uiSceneHeightSpinBox, 3, 0, 1, 2)
|
||||
self.gridLayout_8.addWidget(self.uiDefaultLabelStylePlainTextEdit, 10, 0, 1, 2)
|
||||
spacerItem7 = QtWidgets.QSpacerItem(20, 5, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.gridLayout_8.addItem(spacerItem7, 12, 0, 1, 1)
|
||||
self.uiSceneWidthSpinBox = QtWidgets.QSpinBox(self.uiSceneTab)
|
||||
self.uiSceneWidthSpinBox.setMinimum(500)
|
||||
self.uiSceneWidthSpinBox.setMaximum(1000000)
|
||||
@@ -359,11 +361,19 @@ class Ui_GeneralPreferencesPageWidget(object):
|
||||
self.uiSceneWidthSpinBox.setProperty("value", 2000)
|
||||
self.uiSceneWidthSpinBox.setObjectName("uiSceneWidthSpinBox")
|
||||
self.gridLayout_8.addWidget(self.uiSceneWidthSpinBox, 1, 0, 1, 2)
|
||||
spacerItem7 = QtWidgets.QSpacerItem(20, 5, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.gridLayout_8.addItem(spacerItem7, 10, 0, 1, 1)
|
||||
self.label_2 = QtWidgets.QLabel(self.uiSceneTab)
|
||||
self.label_2.setObjectName("label_2")
|
||||
self.gridLayout_8.addWidget(self.label_2, 4, 0, 1, 1)
|
||||
self.uiShowInterfaceLabelsOnNewProject = QtWidgets.QCheckBox(self.uiSceneTab)
|
||||
self.uiShowInterfaceLabelsOnNewProject.setObjectName("uiShowInterfaceLabelsOnNewProject")
|
||||
self.gridLayout_8.addWidget(self.uiShowInterfaceLabelsOnNewProject, 8, 0, 1, 1)
|
||||
self.uiGridSizeLabel = QtWidgets.QLabel(self.uiSceneTab)
|
||||
self.uiGridSizeLabel.setObjectName("uiGridSizeLabel")
|
||||
self.gridLayout_8.addWidget(self.uiGridSizeLabel, 4, 0, 1, 1)
|
||||
self.uiGridSizeSpinBox = QtWidgets.QSpinBox(self.uiSceneTab)
|
||||
self.uiGridSizeSpinBox.setMinimum(10)
|
||||
self.uiGridSizeSpinBox.setMaximum(100)
|
||||
self.uiGridSizeSpinBox.setSingleStep(10)
|
||||
self.uiGridSizeSpinBox.setProperty("value", 75)
|
||||
self.uiGridSizeSpinBox.setObjectName("uiGridSizeSpinBox")
|
||||
self.gridLayout_8.addWidget(self.uiGridSizeSpinBox, 5, 0, 1, 2)
|
||||
self.uiMiscTabWidget.addTab(self.uiSceneTab, "")
|
||||
self.uiMiscTab = QtWidgets.QWidget()
|
||||
self.uiMiscTab.setObjectName("uiMiscTab")
|
||||
@@ -491,17 +501,18 @@ class Ui_GeneralPreferencesPageWidget(object):
|
||||
"</body></html>"))
|
||||
self.uiSPICEConsolePreconfiguredCommandPushButton.setText(_translate("GeneralPreferencesPageWidget", "&Edit"))
|
||||
self.uiMiscTabWidget.setTabText(self.uiMiscTabWidget.indexOf(self.uiSPICETab), _translate("GeneralPreferencesPageWidget", "SPICE"))
|
||||
self.uiRectangleSelectedItemCheckBox.setText(_translate("GeneralPreferencesPageWidget", "Draw a rectangle when an item is selected"))
|
||||
self.uiSceneWidthLabel.setText(_translate("GeneralPreferencesPageWidget", "Default width:"))
|
||||
self.uiDefaultLabelFontPushButton.setText(_translate("GeneralPreferencesPageWidget", "&Select default font"))
|
||||
self.uiDefaultLabelColorPushButton.setText(_translate("GeneralPreferencesPageWidget", "&Select default color"))
|
||||
self.uiSceneWidthLabel.setText(_translate("GeneralPreferencesPageWidget", "Default width:"))
|
||||
self.uiSceneHeightLabel.setText(_translate("GeneralPreferencesPageWidget", "Default height:"))
|
||||
self.uiRectangleSelectedItemCheckBox.setText(_translate("GeneralPreferencesPageWidget", "Draw a rectangle when an item is selected"))
|
||||
self.uiDrawLinkStatusPointsCheckBox.setText(_translate("GeneralPreferencesPageWidget", "Draw link status points"))
|
||||
self.uiLabelPreviewLabel.setText(_translate("GeneralPreferencesPageWidget", "Default label style:"))
|
||||
self.uiDefaultLabelStylePlainTextEdit.setPlainText(_translate("GeneralPreferencesPageWidget", "AaBbYyZz"))
|
||||
self.uiSceneHeightSpinBox.setSuffix(_translate("GeneralPreferencesPageWidget", " pixels"))
|
||||
self.uiLabelPreviewLabel.setText(_translate("GeneralPreferencesPageWidget", "Default label style:"))
|
||||
self.uiDrawLinkStatusPointsCheckBox.setText(_translate("GeneralPreferencesPageWidget", "Draw link status points"))
|
||||
self.uiDefaultLabelStylePlainTextEdit.setPlainText(_translate("GeneralPreferencesPageWidget", "AaBbYyZz"))
|
||||
self.uiSceneWidthSpinBox.setSuffix(_translate("GeneralPreferencesPageWidget", " pixels"))
|
||||
self.label_2.setText(_translate("GeneralPreferencesPageWidget", "If you want to change the size of the current project. Via the project menu you can edit it."))
|
||||
self.uiShowInterfaceLabelsOnNewProject.setText(_translate("GeneralPreferencesPageWidget", "Show interface labels on new project"))
|
||||
self.uiGridSizeLabel.setText(_translate("GeneralPreferencesPageWidget", "Grid size:"))
|
||||
self.uiMiscTabWidget.setTabText(self.uiMiscTabWidget.indexOf(self.uiSceneTab), _translate("GeneralPreferencesPageWidget", "Topology view"))
|
||||
self.uiCheckForUpdateCheckBox.setText(_translate("GeneralPreferencesPageWidget", "Automatically check for update"))
|
||||
self.uiCrashReportCheckBox.setText(_translate("GeneralPreferencesPageWidget", "Send anonymous crash reports"))
|
||||
|
||||
99
gns3/ui/project_welcome_dialog.ui
Executable file
99
gns3/ui/project_welcome_dialog.ui
Executable file
@@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ProjectWelcomeDialog</class>
|
||||
<widget class="QDialog" name="ProjectWelcomeDialog">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::WindowModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>607</width>
|
||||
<height>308</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Welcome</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="rightMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>Loading.. Please wait.</p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout"/>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="uiOkButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../../resources/resources.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
60
gns3/ui/project_welcome_dialog_ui.py
Normal file
60
gns3/ui/project_welcome_dialog_ui.py
Normal file
@@ -0,0 +1,60 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file '/home/dominik/projects/gns3-gui/gns3/ui/project_welcome_dialog.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.8.2
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
class Ui_ProjectWelcomeDialog(object):
|
||||
def setupUi(self, ProjectWelcomeDialog):
|
||||
ProjectWelcomeDialog.setObjectName("ProjectWelcomeDialog")
|
||||
ProjectWelcomeDialog.setWindowModality(QtCore.Qt.WindowModal)
|
||||
ProjectWelcomeDialog.resize(607, 308)
|
||||
ProjectWelcomeDialog.setModal(True)
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout(ProjectWelcomeDialog)
|
||||
self.verticalLayout.setContentsMargins(-1, -1, 12, -1)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.label = QtWidgets.QLabel(ProjectWelcomeDialog)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
|
||||
self.label.setSizePolicy(sizePolicy)
|
||||
self.label.setTextFormat(QtCore.Qt.RichText)
|
||||
self.label.setScaledContents(False)
|
||||
self.label.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop)
|
||||
self.label.setWordWrap(True)
|
||||
self.label.setObjectName("label")
|
||||
self.verticalLayout.addWidget(self.label)
|
||||
self.gridLayout = QtWidgets.QGridLayout()
|
||||
self.gridLayout.setObjectName("gridLayout")
|
||||
self.verticalLayout.addLayout(self.gridLayout)
|
||||
self.horizontalLayout = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout.setSpacing(20)
|
||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||
self.horizontalLayout.addItem(spacerItem)
|
||||
self.uiOkButton = QtWidgets.QDialogButtonBox(ProjectWelcomeDialog)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.uiOkButton.sizePolicy().hasHeightForWidth())
|
||||
self.uiOkButton.setSizePolicy(sizePolicy)
|
||||
self.uiOkButton.setOrientation(QtCore.Qt.Horizontal)
|
||||
self.uiOkButton.setStandardButtons(QtWidgets.QDialogButtonBox.Ok)
|
||||
self.uiOkButton.setObjectName("uiOkButton")
|
||||
self.horizontalLayout.addWidget(self.uiOkButton)
|
||||
self.verticalLayout.addLayout(self.horizontalLayout)
|
||||
|
||||
self.retranslateUi(ProjectWelcomeDialog)
|
||||
QtCore.QMetaObject.connectSlotsByName(ProjectWelcomeDialog)
|
||||
|
||||
def retranslateUi(self, ProjectWelcomeDialog):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
ProjectWelcomeDialog.setWindowTitle(_translate("ProjectWelcomeDialog", "Welcome"))
|
||||
self.label.setText(_translate("ProjectWelcomeDialog", "<html><head/><body><p>Loading.. Please wait.</p></body></html>"))
|
||||
|
||||
from . import resources_rc
|
||||
158856
gns3/ui/resources_rc.py
158856
gns3/ui/resources_rc.py
File diff suppressed because it is too large
Load Diff
@@ -73,6 +73,9 @@ class UpdateManager(QtCore.QObject):
|
||||
request = QtNetwork.QNetworkRequest(QtCore.QUrl(url))
|
||||
request.setRawHeader(b'User-Agent', b'GNS3 Check For Update')
|
||||
request.setAttribute(QtNetwork.QNetworkRequest.User, user_attribute)
|
||||
if parse_version(QtCore.QT_VERSION_STR) >= parse_version("5.6.0") and parse_version(QtCore.PYQT_VERSION_STR) >= parse_version("5.6.0"):
|
||||
# follow redirects only supported starting with Qt 5.6.0
|
||||
request.setAttribute(QtNetwork.QNetworkRequest.FollowRedirectsAttribute, True)
|
||||
reply = self._network_manager.get(request)
|
||||
reply.finished.connect(finished_slot)
|
||||
log.debug('Download %s', url)
|
||||
@@ -90,7 +93,7 @@ class UpdateManager(QtCore.QObject):
|
||||
self._parent = parent
|
||||
|
||||
if hasattr(sys, "frozen") and LocalConfig.instance().experimental():
|
||||
url = 'https://pypi.python.org/pypi/gns3-gui/json'
|
||||
url = 'https://pypi.org/pypi/gns3-gui/json'
|
||||
self._get(url, self._pypiReplySlot)
|
||||
else:
|
||||
self._get('http://update.gns3.net', self._gns3UpdateReplySlot)
|
||||
@@ -130,7 +133,9 @@ class UpdateManager(QtCore.QObject):
|
||||
body = bytes(network_reply.readAll()).decode("utf-8")
|
||||
body = json.loads(body)
|
||||
except (UnicodeEncodeError, ValueError) as e:
|
||||
log.warning("Invalid answer from the PyPi server")
|
||||
log.warning("Invalid answer from the PyPi server: {}".format(e))
|
||||
QtWidgets.QMessageBox.critical(self._parent, "Check For Update", "Invalid answer from PyPi server")
|
||||
return
|
||||
|
||||
last_version = self._getLastMinorVersionFromPyPiReply(body)
|
||||
if parse_version(last_version) > parse_version(version.__version__):
|
||||
@@ -154,6 +159,7 @@ class UpdateManager(QtCore.QObject):
|
||||
|
||||
If no valid version is found it's return the current.
|
||||
"""
|
||||
|
||||
current_version = parse_version(version.__version__)
|
||||
for release in sorted(body['releases'].keys(), reverse=True):
|
||||
release_version = parse_version(release)
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (C) 2017 GNS3 Technologies Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from ..qt import QtCore
|
||||
from ..controller import Controller
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CreateSnapshotWorker(QtCore.QObject):
|
||||
"""
|
||||
Export snapshot
|
||||
"""
|
||||
|
||||
# signals to update the progress dialog.
|
||||
error = QtCore.pyqtSignal(str, bool)
|
||||
finished = QtCore.pyqtSignal()
|
||||
updated = QtCore.pyqtSignal(int)
|
||||
|
||||
canceled = QtCore.pyqtSignal()
|
||||
|
||||
|
||||
def __init__(self, project, snapshot_name):
|
||||
super().__init__()
|
||||
self._project = project
|
||||
self._snapshot_name = snapshot_name
|
||||
|
||||
def run(self):
|
||||
if self._project:
|
||||
Controller.instance().post(
|
||||
"/projects/{}/snapshots".format(self._project.id()),
|
||||
self._createSnapshotsCallback,
|
||||
{"name": self._snapshot_name},
|
||||
timeout=None,
|
||||
showProgress=False,
|
||||
eventsHandler=self
|
||||
)
|
||||
|
||||
def _createSnapshotsCallback(self, content, error=False, server=None, context={}, **kwargs):
|
||||
if error:
|
||||
if content:
|
||||
self.error.emit(content["message"], True)
|
||||
else:
|
||||
self.error.emit("Cannot create snapshot of project", True)
|
||||
self.finished.emit()
|
||||
return
|
||||
self.finished.emit()
|
||||
|
||||
def cancel(self):
|
||||
log.warning("Snapshot `{}` creation has been canceled by user.".format(self._snapshot_name))
|
||||
self.canceled.emit()
|
||||
@@ -15,9 +15,16 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# __version__ is a human-readable version number.
|
||||
|
||||
__version__ = "2.1.2"
|
||||
__version_info__ = (2, 1, 2, 0)
|
||||
# __version_info__ is a four-tuple for programmatic comparison. The first
|
||||
# three numbers are the components of the version number. The fourth
|
||||
# is zero for an official release, positive for a development branch,
|
||||
# or negative for a release candidate or beta (after the base version
|
||||
# number has been incremented)
|
||||
|
||||
__version__ = "2.1.7"
|
||||
__version_info__ = (2, 1, 7, 0)
|
||||
|
||||
# If it's a git checkout try to add the commit
|
||||
if "dev" in __version__:
|
||||
|
||||
@@ -55,5 +55,5 @@ def vncConsole(host, port, command):
|
||||
args = shlex.split(command)
|
||||
subprocess.Popen(args, env=os.environ)
|
||||
except (OSError, ValueError, subprocess.SubprocessError) as e:
|
||||
log.warning('could not start VNC program "{}": {}'.format(command, e))
|
||||
log.error("Could not start VNC program with command '{}': {}".format(command, e))
|
||||
raise
|
||||
|
||||
BIN
resources/icons/traceng.png
Normal file
BIN
resources/icons/traceng.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.7 KiB |
@@ -61,6 +61,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>
|
||||
|
||||
@@ -22,6 +22,9 @@ if sys.platform.startswith('win'):
|
||||
PATH = os.path.join(PATH, "bin")
|
||||
PYUIC = os.path.join(PATH, "pyuic5")
|
||||
PYRCC = os.path.join(PATH, "pyrcc5")
|
||||
|
||||
if not os.path.exists(PYUIC):
|
||||
PYUIC = "pyuic5.exe"
|
||||
else:
|
||||
PYUIC = shutil.which("pyuic5")
|
||||
PYRCC = shutil.which("pyrcc5")
|
||||
|
||||
@@ -145,6 +145,7 @@ def test_add_appliance_docker(empty_config, iou_l3):
|
||||
"adapters": 16,
|
||||
"start_command": "",
|
||||
"environment": "",
|
||||
'extra_hosts': "",
|
||||
"usage": "By default all interfaces are connected to the br0",
|
||||
"console_type": "telnet",
|
||||
"console_http_port": 80,
|
||||
|
||||
@@ -306,7 +306,7 @@ def test_callbackConnect_major_version_invalid(http_client):
|
||||
http_client._query_waiting_connections.append((None, mock))
|
||||
http_client._callbackConnect(params)
|
||||
assert http_client._connected is False
|
||||
mock.assert_called_with({"message": "Client version {} differs with server version 1.2.3".format(__version__)}, error=True, server=None)
|
||||
mock.assert_called_with({"message": "Client version {} is not the same as server version 1.2.3".format(__version__)}, error=True, server=None)
|
||||
|
||||
|
||||
def test_callbackConnect_minor_version_invalid(http_client):
|
||||
@@ -323,7 +323,7 @@ def test_callbackConnect_minor_version_invalid(http_client):
|
||||
if __version_info__[3] == 0:
|
||||
http_client._callbackConnect(params)
|
||||
assert http_client._connected is False
|
||||
mock.assert_called_with({"message": "Client version {} differs with server version {}".format(__version__, new_version)}, error=True, server=None)
|
||||
mock.assert_called_with({"message": "Client version {} is not the same as server version {}".format(__version__, new_version)}, error=True, server=None)
|
||||
else:
|
||||
http_client._callbackConnect(params)
|
||||
assert http_client._connected is True
|
||||
|
||||
43
tests/test_main_window.py
Normal file
43
tests/test_main_window.py
Normal file
@@ -0,0 +1,43 @@
|
||||
# -*- 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/>.
|
||||
|
||||
import pytest
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def real_main_window():
|
||||
from gns3.main_window import MainWindow
|
||||
return MainWindow()
|
||||
|
||||
|
||||
def test_main_window_settings_changed_signal(real_main_window):
|
||||
settings = real_main_window.settings()
|
||||
signal = MagicMock()
|
||||
real_main_window.settings_updated_signal = signal
|
||||
real_main_window.setSettings(settings)
|
||||
assert signal.emit.called
|
||||
|
||||
|
||||
def test_main_window_settings_changed_slot(real_main_window):
|
||||
instance = MagicMock()
|
||||
manager = MagicMock()
|
||||
manager.instance.return_value = instance
|
||||
real_main_window._appliance_manager = manager
|
||||
real_main_window.settingsChangedSlot()
|
||||
assert instance.refresh.called
|
||||
@@ -141,3 +141,39 @@ def test_updatePorts_PortChange(vpcs_device):
|
||||
])
|
||||
assert port == vpcs_device._ports[0]
|
||||
assert port.status() == Port.started
|
||||
|
||||
|
||||
def test_node_setGraphics(vpcs_device):
|
||||
node = MagicMock(
|
||||
pos=MagicMock(
|
||||
return_value=MagicMock(
|
||||
x=MagicMock(
|
||||
return_value=10
|
||||
),
|
||||
y=MagicMock(
|
||||
return_value=20
|
||||
)
|
||||
)
|
||||
),
|
||||
zValue=MagicMock(
|
||||
return_value=2
|
||||
),
|
||||
symbol=MagicMock(
|
||||
return_value="symbol.svg"
|
||||
)
|
||||
)
|
||||
with patch('gns3.base_node.BaseNode.controllerHttpPut') as mock:
|
||||
vpcs_device.setGraphics(node)
|
||||
assert mock.call_count == 1
|
||||
args, kwargs = mock.call_args
|
||||
assert args[0] == "/nodes/{node_id}".format(node_id=vpcs_device.node_id())
|
||||
|
||||
# second call should not make an update
|
||||
vpcs_device.setSettingValue('x', 10)
|
||||
vpcs_device.setSettingValue('y', 20)
|
||||
vpcs_device.setSettingValue('z', 2)
|
||||
vpcs_device.setSettingValue('symbol', "symbol.svg")
|
||||
vpcs_device.setSettingValue('label', node.label().dump())
|
||||
|
||||
vpcs_device.setGraphics(node)
|
||||
assert mock.call_count == 1
|
||||
@@ -41,7 +41,8 @@ def test_project_create(tmpdir, controller):
|
||||
assert args[0] == "POST"
|
||||
assert args[1] == "/projects"
|
||||
assert kwargs["body"] == {"name": "test",
|
||||
"path": str(tmpdir)}
|
||||
"path": str(tmpdir),
|
||||
"show_interface_labels": False}
|
||||
|
||||
args[2]({"project_id": uuid, "name": "test"})
|
||||
|
||||
@@ -127,8 +128,6 @@ def test_project_delete_on_created_project(controller):
|
||||
|
||||
|
||||
def test_project_destroy(controller):
|
||||
|
||||
|
||||
project = Project()
|
||||
project.setId(str(uuid4()))
|
||||
project.destroy()
|
||||
@@ -139,3 +138,49 @@ def test_project_destroy(controller):
|
||||
|
||||
assert args[0] == "DELETE"
|
||||
assert args[1] == "/projects/{project_id}".format(project_id=project.id())
|
||||
|
||||
|
||||
def test_project_variables():
|
||||
project = Project()
|
||||
project.setVariables([{'name': 'TEST'}])
|
||||
|
||||
variables = project.variables()
|
||||
assert variables == [{'name': 'TEST'}]
|
||||
|
||||
|
||||
def test_project_supplier():
|
||||
project = Project()
|
||||
project.setSupplier({'logo': 'test.png', 'url': 'http://domain'})
|
||||
|
||||
supplier = project.supplier()
|
||||
assert supplier == {'logo': 'test.png', 'url': 'http://domain'}
|
||||
|
||||
|
||||
def test_project_parse_response():
|
||||
result = {
|
||||
'project_id': 'projectid',
|
||||
'name': 'projectname',
|
||||
'filename': 'filename.gns3',
|
||||
'variables': [{'name': 'TEST'}],
|
||||
'supplier': {'logo': 'test.png', 'url': 'http://domain'}
|
||||
}
|
||||
project = Project()
|
||||
project._parseResponse(result)
|
||||
assert project.id() == 'projectid'
|
||||
assert project.name() == 'projectname'
|
||||
assert project.filename() == 'filename.gns3'
|
||||
assert project.variables() == [{'name': 'TEST'}]
|
||||
assert project.supplier() == {'logo': 'test.png', 'url': 'http://domain'}
|
||||
|
||||
|
||||
def test_project_update(controller):
|
||||
project = Project()
|
||||
project.setVariables([{'name': 'TEST'}])
|
||||
project.setSupplier({'logo': 'test.png', 'url': 'http://domain'})
|
||||
project.update()
|
||||
mock = controller._http_client.createHTTPQuery
|
||||
args, kwargs = mock.call_args
|
||||
body = kwargs['body']
|
||||
assert body['variables'] == [{'name': 'TEST'}]
|
||||
assert body['supplier'] == {'logo': 'test.png', 'url': 'http://domain'}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user