mirror of
https://github.com/GNS3/gns3-gui.git
synced 2026-06-05 02:02:08 +03:00
Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05d9ee8499 | ||
|
|
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=
|
||||
|
||||
25
CHANGELOG
25
CHANGELOG
@@ -1,5 +1,30 @@
|
||||
# Change Log
|
||||
|
||||
## 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
|
||||
|
||||
@@ -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://b892f3e4a56e4443ad16cfe3d4c6d602:ca3ad57e613441ab95ea15a45e9e5435@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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -118,8 +118,7 @@ class NodeItem(QtSvg.QGraphicsSvgItem):
|
||||
"""
|
||||
Sync change to the node
|
||||
"""
|
||||
if self._initialized:
|
||||
self._node.setGraphics(self)
|
||||
self._node.setGraphics(self)
|
||||
|
||||
@qslot
|
||||
def setSymbol(self, symbol):
|
||||
|
||||
@@ -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):
|
||||
"""
|
||||
@@ -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():
|
||||
"""
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -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:
|
||||
|
||||
16
gns3/node.py
16
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):
|
||||
"""
|
||||
@@ -355,7 +350,6 @@ class Node(BaseNode):
|
||||
return False
|
||||
|
||||
result = self._parseResponse(result)
|
||||
self._created = True
|
||||
self._createCallback(result)
|
||||
|
||||
if self._loading:
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -318,6 +318,7 @@ class GeneralPreferencesPage(QtWidgets.QWidget, Ui_GeneralPreferencesPageWidget)
|
||||
self.uiSceneHeightSpinBox.setValue(settings["scene_height"])
|
||||
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"]):
|
||||
@@ -380,6 +381,7 @@ class GeneralPreferencesPage(QtWidgets.QWidget, Ui_GeneralPreferencesPageWidget)
|
||||
"scene_height": self.uiSceneHeightSpinBox.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)
|
||||
|
||||
@@ -60,7 +60,9 @@ 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)
|
||||
@@ -68,6 +70,7 @@ class Project(QtCore.QObject):
|
||||
self._snap_to_grid = graphic_settings.get("snap_to_grid", False)
|
||||
self._show_grid = graphic_settings.get("show_grid", False)
|
||||
self._show_interface_labels = graphic_settings.get("show_interface_labels", False)
|
||||
self._show_interface_labels_on_new_project = config.showInterfaceLabelsOnNewProject()
|
||||
|
||||
self._name = "untitled"
|
||||
self._filename = None
|
||||
@@ -373,7 +376,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)
|
||||
|
||||
@@ -413,6 +417,7 @@ class Project(QtCore.QObject):
|
||||
self._closing = False
|
||||
self._startListenNotifications()
|
||||
self.project_updated_signal.emit()
|
||||
self.project_loaded_signal.emit()
|
||||
|
||||
def _parseResponse(self, result):
|
||||
"""
|
||||
|
||||
@@ -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":
|
||||
@@ -276,9 +283,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 +296,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):
|
||||
|
||||
@@ -289,7 +289,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,7 +30,16 @@
|
||||
<string>General</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<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>
|
||||
@@ -238,7 +247,16 @@
|
||||
<string>Binary images</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||
<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>
|
||||
@@ -372,7 +390,16 @@
|
||||
<string>Console applications</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<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>
|
||||
@@ -483,7 +510,16 @@
|
||||
<string>VNC</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<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>
|
||||
@@ -644,10 +680,36 @@
|
||||
<string>Topology view</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_8">
|
||||
<property name="margin">
|
||||
<property name="leftMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<item row="9" column="0" colspan="2">
|
||||
<property name="topMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<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="0" column="0">
|
||||
<widget class="QLabel" name="uiSceneWidthLabel">
|
||||
<property name="text">
|
||||
<string>Default width:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0" colspan="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QPushButton" name="uiDefaultLabelFontPushButton">
|
||||
@@ -678,13 +740,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 +747,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 +766,58 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QLabel" name="uiLabelPreviewLabel">
|
||||
<property name="text">
|
||||
<string>Default label style:</string>
|
||||
</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="9" 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="11" 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,19 +837,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" 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="4" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
@@ -799,6 +844,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QCheckBox" name="uiShowInterfaceLabelsOnNewProject">
|
||||
<property name="text">
|
||||
<string>Show interface labels on new project</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="uiMiscTab">
|
||||
@@ -806,7 +858,16 @@
|
||||
<string>Miscellaneous</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<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>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/ui/general_preferences_page.ui'
|
||||
# Form implementation generated from reading ui file 'C:\Users\dominik.ziajka\projects\gns3-gui\gns3\ui\general_preferences_page.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.5.1
|
||||
# Created by: PyQt5 UI code generator 5.8
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
@@ -270,6 +270,7 @@ class Ui_GeneralPreferencesPageWidget(object):
|
||||
self.uiSPICETab = QtWidgets.QWidget()
|
||||
self.uiSPICETab.setObjectName("uiSPICETab")
|
||||
self.gridLayout_2 = QtWidgets.QGridLayout(self.uiSPICETab)
|
||||
self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
|
||||
self.gridLayout_2.setObjectName("gridLayout_2")
|
||||
self.uiSPICEConsoleSettingsGroupBox = QtWidgets.QGroupBox(self.uiSPICETab)
|
||||
self.uiSPICEConsoleSettingsGroupBox.setObjectName("uiSPICEConsoleSettingsGroupBox")
|
||||
@@ -307,6 +308,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, 5, 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 +325,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, 10, 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, 8, 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.uiDefaultLabelStylePlainTextEdit = QtWidgets.QPlainTextEdit(self.uiSceneTab)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
@@ -344,14 +352,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, 9, 0, 1, 2)
|
||||
spacerItem7 = QtWidgets.QSpacerItem(20, 5, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.gridLayout_8.addItem(spacerItem7, 11, 0, 1, 1)
|
||||
self.uiSceneWidthSpinBox = QtWidgets.QSpinBox(self.uiSceneTab)
|
||||
self.uiSceneWidthSpinBox.setMinimum(500)
|
||||
self.uiSceneWidthSpinBox.setMaximum(1000000)
|
||||
@@ -359,11 +362,12 @@ 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, 7, 0, 1, 1)
|
||||
self.uiMiscTabWidget.addTab(self.uiSceneTab, "")
|
||||
self.uiMiscTab = QtWidgets.QWidget()
|
||||
self.uiMiscTab.setObjectName("uiMiscTab")
|
||||
@@ -491,17 +495,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.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"))
|
||||
|
||||
@@ -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.4"
|
||||
__version_info__ = (2, 1, 4, 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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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"})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user