mirror of
https://github.com/GNS3/gns3-gui.git
synced 2026-06-07 19:16:26 +03:00
Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
450fbc9af3 | ||
|
|
469ee8fab8 | ||
|
|
6ccfcaf76e | ||
|
|
520e857874 | ||
|
|
012c7b4241 | ||
|
|
1d71cd5bf0 | ||
|
|
d96277882a | ||
|
|
ecec917752 | ||
|
|
ea9c1a8ee1 | ||
|
|
cfbb09fb57 | ||
|
|
dc8aa1fb92 | ||
|
|
786cc8aa65 | ||
|
|
4a353e08e3 | ||
|
|
2a59013604 | ||
|
|
7732aaf9a5 | ||
|
|
1f566a31cf | ||
|
|
10d75e15da | ||
|
|
17def7e00a | ||
|
|
f68a8ea829 | ||
|
|
50066b2f12 | ||
|
|
21a99d4376 | ||
|
|
f97d3041b8 | ||
|
|
31d6a065b0 | ||
|
|
8f077456b1 | ||
|
|
a29f3e35c0 | ||
|
|
fc3781550a | ||
|
|
d285e62c04 | ||
|
|
44d70de687 | ||
|
|
752c516f82 | ||
|
|
e1ec6c5771 | ||
|
|
e8308869d9 | ||
|
|
484c5abe9d | ||
|
|
fe222b873f | ||
|
|
f8bb6661dd | ||
|
|
0f9aab9230 | ||
|
|
a5cf5e16b7 | ||
|
|
097458d108 | ||
|
|
f4cafac9c7 | ||
|
|
7f132fdc36 | ||
|
|
6b7d629755 | ||
|
|
b7ccc37ea5 | ||
|
|
bbe2826c77 | ||
|
|
68e2a0ee39 | ||
|
|
52418ed94a |
51
CHANGELOG
51
CHANGELOG
@@ -1,5 +1,56 @@
|
||||
# Change Log
|
||||
|
||||
## 2.1.20 29/05/2019
|
||||
|
||||
* Fix KeyError: 'endpoint' issue. Fixes #2802
|
||||
|
||||
## 2.1.19 28/05/2019
|
||||
|
||||
* Fix wrong aligment of symbols in saved/exported projects. Fixes #2800
|
||||
* Replace urllib.request by Qt implementation for local server synchronous check. Fixes #2793
|
||||
* Set grid's minimum to 5. Fixes #2795
|
||||
|
||||
## 2.1.18 22/05/2019
|
||||
|
||||
* Fix error in HTTPConnection.request for Python3.6. Fixes #2793
|
||||
* Catch more OSError/PermissionError when checking md5 on remote images. Fixes #2582
|
||||
* Fix exception when grid size is 0. Fixes #2790
|
||||
* Catch PermissionError when scanning local image directories. Fixes #2791
|
||||
* Revert "Make sure the latest PyQt5 version 5.12.x is used on Windows." Ref #2778
|
||||
|
||||
## 2.1.17 17/05/2019
|
||||
|
||||
* No changes.
|
||||
|
||||
## 2.1.16 15/04/2019
|
||||
|
||||
* Do not make NPF or NPCAP service mandatory to start the local server on Windows.
|
||||
* Fix OverflowError error with progress dialog. Fixes #2767
|
||||
* More fixes for stuck progress window. Fixes #2765
|
||||
* Fix adding multiple devices - stuck progress window. Fixes #2765
|
||||
* Make sure the latest PyQt5 version 5.12.x is used on Windows.
|
||||
* Show a warning when a config export is not supported. Ref #2762
|
||||
|
||||
## 2.1.15 21/03/2019
|
||||
|
||||
* No changes on the GUI.
|
||||
|
||||
## 2.1.14 27/02/2019
|
||||
|
||||
* Better description to why an appliance cannot be installed.
|
||||
|
||||
## 2.1.13 26/02/2019
|
||||
|
||||
* Disable computer hibernation detection mechanism. Ref #2678
|
||||
* Add some advice for request timeout message. Fixes #2652
|
||||
* Show/Hide interface labels when status points are not shown. Fixes #2690
|
||||
* Do not print critical message twice on stderr. Replace QMessageBox calls with no parent by log.error()/log.warning().
|
||||
* Show critical messages before the main window runs.
|
||||
* Avoid using PyQt5.Qt, which imports unneeded stuff. Fixes #2592
|
||||
* Fix SIP import error with recent PyQt versions. Fixes #2709
|
||||
* Upgrade to Qt 5.12. Fixes #2636
|
||||
* Adjust the setup wizard (VMware image size, layouts).
|
||||
|
||||
## 2.1.12 23/01/2019
|
||||
|
||||
* Option to resize SVG symbols that are too big (height above 80px, activated by default). Ref #2674.
|
||||
|
||||
@@ -99,12 +99,14 @@ class ApplianceManager(QtCore.QObject):
|
||||
"x": int(x),
|
||||
"y": int(y)
|
||||
},
|
||||
showProgress=False,
|
||||
timeout=None)
|
||||
else:
|
||||
self._controller.post("/projects/" + project_id + "/appliances/" + appliance_id, self._createNodeFromApplianceCallback, {
|
||||
"x": int(x),
|
||||
"y": int(y)
|
||||
},
|
||||
showProgress=False,
|
||||
timeout=None)
|
||||
return True
|
||||
|
||||
|
||||
@@ -336,13 +336,16 @@ class BaseNode(QtCore.QObject):
|
||||
"""
|
||||
|
||||
if not hasattr(self, "configFiles"):
|
||||
return
|
||||
return False
|
||||
|
||||
for file in self.configFiles():
|
||||
self.controllerHttpGet("/nodes/{node_id}/files/{file}".format(node_id=self._node_id, file=file),
|
||||
self._exportConfigToDirectoryCallback,
|
||||
context={"directory": directory, "file": file},
|
||||
raw=True)
|
||||
|
||||
return True
|
||||
|
||||
def _exportConfigToDirectoryCallback(self, result, error=False, raw_body=None, context={}, **kwargs):
|
||||
"""
|
||||
Callback for exportConfigToDirectory.
|
||||
@@ -352,8 +355,7 @@ class BaseNode(QtCore.QObject):
|
||||
"""
|
||||
|
||||
if error:
|
||||
# The file could be missing if you have not private config for
|
||||
# exemple
|
||||
# The file could be missing if you have not private config for example
|
||||
return
|
||||
export_directory = context["directory"]
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ Handles commands typed in the GNS3 console.
|
||||
import sys
|
||||
import cmd
|
||||
import struct
|
||||
import sip
|
||||
from .qt import sip
|
||||
import json
|
||||
|
||||
from .node import Node
|
||||
|
||||
@@ -16,13 +16,13 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sys
|
||||
import sip
|
||||
from .qt import sip
|
||||
import struct
|
||||
import inspect
|
||||
import datetime
|
||||
import platform
|
||||
|
||||
from .qt import QtCore, Qt
|
||||
from .qt import QtCore
|
||||
from .topology import Topology
|
||||
from .version import __version__
|
||||
from .console_cmd import ConsoleCmd
|
||||
@@ -75,7 +75,7 @@ class ConsoleView(PyCutExt, ConsoleCmd):
|
||||
self.intro = "GNS3 management console.\nRunning GNS3 version {} on {} ({}-bit) with Python {} Qt {} and PyQt {}.\n" \
|
||||
"Copyright (c) 2006-{} GNS3 Technologies.\n" \
|
||||
"Use Help -> GNS3 Doctor to detect common issues." \
|
||||
"".format(__version__, platform.system(), bitness, platform.python_version(), QtCore.QT_VERSION_STR, Qt.PYQT_VERSION_STR, current_year)
|
||||
"".format(__version__, platform.system(), bitness, platform.python_version(), QtCore.QT_VERSION_STR, QtCore.PYQT_VERSION_STR, current_year)
|
||||
|
||||
# Parent class initialization
|
||||
try:
|
||||
|
||||
@@ -213,9 +213,6 @@ class Controller(QtCore.QObject):
|
||||
if self._http_client:
|
||||
return self._http_client.createHTTPQuery(method, path, *args, **kwargs)
|
||||
|
||||
def getSynchronous(self, endpoint, timeout=2):
|
||||
return self._http_client.getSynchronous(endpoint, timeout)
|
||||
|
||||
def connectWebSocket(self, path, *args):
|
||||
return self._http_client.connectWebSocket(path)
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ class CrashReport:
|
||||
Report crash to a third party service
|
||||
"""
|
||||
|
||||
DSN = "https://b4bdc4b7e46f4820a5d4d18b29acd67c:0af433313e464920b31e2c618ab9580d@sentry.io/38506"
|
||||
DSN = "https://84ef3f39811242728d4b151f4d5ca5a4:eb9df95eb15f4672a4abb17ddb8322fe@sentry.io/38506"
|
||||
if hasattr(sys, "frozen"):
|
||||
cacert = get_resource("cacert.pem")
|
||||
if cacert is not None and os.path.isfile(cacert):
|
||||
@@ -132,7 +132,7 @@ class CrashReport:
|
||||
def _add_qt_information(self, context):
|
||||
try:
|
||||
from .qt import QtCore
|
||||
import sip
|
||||
from .qt import sip
|
||||
except ImportError:
|
||||
return context
|
||||
context["psutil:version"] = psutil.__version__
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import sip
|
||||
from ..qt import sip
|
||||
import shutil
|
||||
|
||||
from ..qt import QtWidgets, QtCore, QtGui, qpartial, qslot
|
||||
@@ -571,8 +571,10 @@ class ApplianceWizard(QtWidgets.QWizard, Ui_ApplianceWizard):
|
||||
if version is None:
|
||||
return False
|
||||
appliance = current.data(2, QtCore.Qt.UserRole)
|
||||
if not self._appliance.is_version_installable(version["name"]):
|
||||
QtWidgets.QMessageBox.warning(self, "Appliance", "Sorry, you cannot install {} with missing files".format(appliance["name"]))
|
||||
try:
|
||||
self._appliance.search_images_for_version(version["name"])
|
||||
except ApplianceError as e:
|
||||
QtWidgets.QMessageBox.critical(self, "Appliance", "Cannot install {} version {}: {}".format(appliance["name"], version["name"], e))
|
||||
return False
|
||||
reply = QtWidgets.QMessageBox.question(self, "Appliance", "Would you like to install {} version {}?".format(appliance["name"], version["name"]),
|
||||
QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No)
|
||||
@@ -582,7 +584,7 @@ class ApplianceWizard(QtWidgets.QWizard, Ui_ApplianceWizard):
|
||||
|
||||
elif self.currentPage() == self.uiUsageWizardPage:
|
||||
if self._image_uploading_count > 0:
|
||||
QtWidgets.QMessageBox.critical(self, "Add appliance", "Please wait for image uploading")
|
||||
QtWidgets.QMessageBox.critical(self, "Add appliance", "Please wait for all images to be uploaded...")
|
||||
return False
|
||||
|
||||
current = self.uiApplianceVersionTreeWidget.currentItem()
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
from ..qt import QtGui, QtWidgets, qslot
|
||||
from ..ui.filter_dialog_ui import Ui_FilterDialog
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FilterDialog(QtWidgets.QDialog, Ui_FilterDialog):
|
||||
|
||||
@@ -41,7 +44,7 @@ class FilterDialog(QtWidgets.QDialog, Ui_FilterDialog):
|
||||
|
||||
def _listAvailableFiltersCallback(self, result, error=False, *args, **kwargs):
|
||||
if error:
|
||||
QtWidgets.QMessageBox.warning(None, "Link", "Error while listing information about the link: {}".format(result["message"]))
|
||||
log.warning("Error while listing information about the link: {}".format(result["message"]))
|
||||
return
|
||||
self._filters = result
|
||||
self._initialized = True
|
||||
|
||||
@@ -21,7 +21,7 @@ Graphical view on the scene where items are drawn.
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sip
|
||||
from .qt import sip
|
||||
import sys
|
||||
|
||||
from .qt import QtCore, QtGui, QtNetwork, QtWidgets, qpartial, qslot
|
||||
@@ -1574,7 +1574,7 @@ class GraphicsView(QtWidgets.QGraphicsView):
|
||||
|
||||
def drawBackground(self, painter, rect):
|
||||
super().drawBackground(painter, rect)
|
||||
if self._main_window.uiShowGridAction.isChecked():
|
||||
if self._main_window.uiShowGridAction.isChecked() and self.gridSize():
|
||||
grid_size = self.gridSize()
|
||||
painter.save()
|
||||
painter.setPen(QtGui.QPen(QtGui.QColor(190, 190, 190)))
|
||||
|
||||
@@ -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/>.
|
||||
|
||||
import sip
|
||||
from .qt import sip
|
||||
import json
|
||||
import copy
|
||||
import http
|
||||
@@ -47,7 +47,7 @@ class HTTPClient(QtCore.QObject):
|
||||
"""
|
||||
HTTP client.
|
||||
|
||||
:param settings: Dictionnary with connection information to the server
|
||||
:param settings: Dictionary with connection information to the server
|
||||
:param network_manager: A QT network manager
|
||||
"""
|
||||
|
||||
@@ -209,16 +209,16 @@ class HTTPClient(QtCore.QObject):
|
||||
Called when a query upload progress
|
||||
"""
|
||||
if not sip_is_deleted(HTTPClient._progress_callback):
|
||||
HTTPClient._progress_callback.progress_signal.emit(query_id, str(sent), str(total))
|
||||
HTTPClient._progress_callback.progress_signal.emit(query_id, str(abs(sent)), str(abs(total)))
|
||||
|
||||
def _notify_progress_download(self, query_id, sent, total):
|
||||
"""
|
||||
Called when a query download progress
|
||||
"""
|
||||
if not sip_is_deleted(HTTPClient._progress_callback):
|
||||
# abs() for maxium because sometimes the system send negative
|
||||
# abs() for maximum because sometimes the system send negative
|
||||
# values
|
||||
HTTPClient._progress_callback.progress_signal.emit(query_id, str(sent), str(abs(total)))
|
||||
HTTPClient._progress_callback.progress_signal.emit(query_id, str(abs(sent)), str(abs(total)))
|
||||
|
||||
@classmethod
|
||||
def setProgressCallback(cls, progress_callback):
|
||||
@@ -303,16 +303,17 @@ class HTTPClient(QtCore.QObject):
|
||||
if self._shutdown:
|
||||
return
|
||||
|
||||
# TODO: clean this
|
||||
# We try to detect computer hibernation
|
||||
# if time between two query is too long we trigger a disconnect
|
||||
if self._max_time_difference_between_queries:
|
||||
now = datetime.datetime.now().timestamp()
|
||||
if self._last_query_timestamp is not None and now > self._last_query_timestamp + self._max_time_difference_between_queries:
|
||||
log.warning("Synchronisation lost with the server.")
|
||||
self.disconnect()
|
||||
self._last_query_timestamp = None
|
||||
return
|
||||
self._last_query_timestamp = now
|
||||
# if self._max_time_difference_between_queries:
|
||||
# now = datetime.datetime.now().timestamp()
|
||||
# if self._last_query_timestamp is not None and now > self._last_query_timestamp + self._max_time_difference_between_queries:
|
||||
# log.warning("Synchronisation lost with the server.")
|
||||
# self.disconnect()
|
||||
# self._last_query_timestamp = None
|
||||
# return
|
||||
# self._last_query_timestamp = now
|
||||
|
||||
request = qpartial(self._executeHTTPQuery, method, path, qpartial(callback), body, context,
|
||||
downloadProgressCallback=downloadProgressCallback,
|
||||
@@ -491,7 +492,7 @@ class HTTPClient(QtCore.QObject):
|
||||
|
||||
def _paramsToQueryString(self, params):
|
||||
"""
|
||||
:param params: Dictionnary of query string parameters
|
||||
:param params: Dictionary of query string parameters
|
||||
:returns: String of the query string
|
||||
"""
|
||||
if params == {}:
|
||||
@@ -619,7 +620,7 @@ class HTTPClient(QtCore.QObject):
|
||||
# We check if we received HTTP headers
|
||||
if not sip.isdeleted(response) and response.isRunning() and not len(response.rawHeaderList()) > 0:
|
||||
if not response.error() != QtNetwork.QNetworkReply.NoError:
|
||||
log.warning("Timeout after {} seconds for request {}".format(timeout, response.url().toString()))
|
||||
log.warning("Timeout after {} seconds for request {}. Please check the connection is not blocked by a firewall or an anti-virus.".format(timeout, response.url().toString()))
|
||||
response.abort()
|
||||
|
||||
def disconnect(self):
|
||||
@@ -632,14 +633,14 @@ class HTTPClient(QtCore.QObject):
|
||||
def _requestCanceled(self, response, context):
|
||||
|
||||
if response.isRunning() and not response.error() != QtNetwork.QNetworkReply.NoError:
|
||||
log.warn("Aborting request for {}".format(response.url().toString()))
|
||||
log.warning("Aborting request for {}".format(response.url().toString()))
|
||||
response.abort()
|
||||
if "query_id" in context:
|
||||
self._notify_progress_end_query(context["query_id"])
|
||||
|
||||
def _processError(self, response, server, callback, context, request_body, ignore_errors, error_code):
|
||||
if error_code != QtNetwork.QNetworkReply.NoError:
|
||||
error_message = response.errorString()
|
||||
error_message = "{} ({}:{})".format(response.errorString(), self._host, self._port)
|
||||
|
||||
if not ignore_errors:
|
||||
log.debug("Response error: %s for %s (error: %d)", error_message, response.url().toString(), error_code)
|
||||
@@ -726,46 +727,54 @@ class HTTPClient(QtCore.QObject):
|
||||
e = HttpBadRequest(body)
|
||||
raise e
|
||||
|
||||
def getSynchronous(self, endpoint, timeout=2):
|
||||
def getSynchronous(self, method, endpoint, prefix="/v2", timeout=2):
|
||||
"""
|
||||
Synchronous check if a server is running
|
||||
|
||||
:returns: Tuple (Status code, json of anwser). Status 0 is a non HTTP error
|
||||
:returns: Tuple (Status code, json of answer). Status 0 is a non HTTP error
|
||||
"""
|
||||
try:
|
||||
url = "{protocol}://{host}:{port}/v2/{endpoint}".format(protocol=self._protocol, host=self._host, port=self._port, endpoint=endpoint)
|
||||
|
||||
if self._user is not None and len(self._user) > 0:
|
||||
log.debug("Synchronous get {} with user '{}'".format(url, self._user))
|
||||
auth_handler = urllib.request.HTTPBasicAuthHandler()
|
||||
auth_handler.add_password(realm="GNS3 server",
|
||||
uri=url,
|
||||
user=self._user,
|
||||
passwd=self._password)
|
||||
opener = urllib.request.build_opener(auth_handler)
|
||||
urllib.request.install_opener(opener)
|
||||
else:
|
||||
log.debug("Synchronous get {} (no authentication)".format(url))
|
||||
response = urllib.request.urlopen(url, timeout=timeout)
|
||||
content_type = response.getheader("CONTENT-TYPE")
|
||||
if response.status == 200:
|
||||
host = self._getHostForQuery()
|
||||
|
||||
log.debug("{method} {protocol}://{host}:{port}{prefix}{endpoint}".format(method=method, protocol=self._protocol, host=host, port=self._port, prefix=prefix, endpoint=endpoint))
|
||||
if self._user:
|
||||
url = QtCore.QUrl("{protocol}://{user}@{host}:{port}{prefix}{endpoint}".format(protocol=self._protocol, user=self._user, host=host, port=self._port, prefix=prefix, endpoint=endpoint))
|
||||
else:
|
||||
url = QtCore.QUrl("{protocol}://{host}:{port}{prefix}{endpoint}".format(protocol=self._protocol, host=host, port=self._port, prefix=prefix, endpoint=endpoint))
|
||||
|
||||
request = self._request(url)
|
||||
request = self._addAuth(request)
|
||||
request.setRawHeader(b"User-Agent", "GNS3 QT Client v{version}".format(version=__version__).encode())
|
||||
|
||||
try:
|
||||
response = self._network_manager.sendCustomRequest(request, method.encode())
|
||||
except SystemError as e:
|
||||
log.error("Can't send query: {}".format(str(e)))
|
||||
return
|
||||
|
||||
loop = QtCore.QEventLoop()
|
||||
response.finished.connect(loop.quit)
|
||||
|
||||
if timeout is not None:
|
||||
QtCore.QTimer.singleShot(timeout * 1000, qpartial(self._timeoutSlot, response, timeout))
|
||||
|
||||
if not loop.isRunning():
|
||||
loop.exec_()
|
||||
|
||||
status = response.attribute(QtNetwork.QNetworkRequest.HttpStatusCodeAttribute)
|
||||
if response.error() != QtNetwork.QNetworkReply.NoError:
|
||||
log.debug("Error while connecting to local server {}".format(response.errorString()))
|
||||
return status, None
|
||||
else:
|
||||
content_type = response.header(QtNetwork.QNetworkRequest.ContentTypeHeader)
|
||||
if status == 200:
|
||||
if content_type == "application/json":
|
||||
content = response.read()
|
||||
content = bytes(response.readAll())
|
||||
json_data = json.loads(content.decode("utf-8"))
|
||||
return response.status, json_data
|
||||
return status, json_data
|
||||
else:
|
||||
return response.status, None
|
||||
except http.client.InvalidURL as e:
|
||||
log.warn("Invalid local server url: {}".format(e))
|
||||
return 0, None
|
||||
except urllib.error.URLError:
|
||||
# Connection refused. It's a normal behavior if server is not started
|
||||
return 0, None
|
||||
except urllib.error.HTTPError as e:
|
||||
log.debug("Error during get on {}:{}: {}".format(self.host(), self.port(), e))
|
||||
return e.code, None
|
||||
except (OSError, http.client.BadStatusLine, ValueError) as e:
|
||||
log.debug("Error during get on {}:{}: {}".format(self.host(), self.port(), e))
|
||||
return status, None
|
||||
|
||||
return 0, None
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -106,7 +106,7 @@ class EthernetLinkItem(LinkItem):
|
||||
"""
|
||||
|
||||
QtWidgets.QGraphicsPathItem.paint(self, painter, option, widget)
|
||||
if not self._adding_flag and self._settings["draw_link_status_points"]:
|
||||
if not self._adding_flag:
|
||||
|
||||
# points disappears if nodes are too close to each others.
|
||||
if self.length < 100:
|
||||
@@ -151,7 +151,8 @@ class EthernetLinkItem(LinkItem):
|
||||
else:
|
||||
source_port_label.hide()
|
||||
|
||||
painter.drawPoint(point1)
|
||||
if self._settings["draw_link_status_points"]:
|
||||
painter.drawPoint(point1)
|
||||
|
||||
if self._link.suspended() or self._destination_port.status() == Port.suspended:
|
||||
# link or port is suspended
|
||||
@@ -192,6 +193,7 @@ class EthernetLinkItem(LinkItem):
|
||||
else:
|
||||
destination_port_label.hide()
|
||||
|
||||
painter.drawPoint(point2)
|
||||
if self._settings["draw_link_status_points"]:
|
||||
painter.drawPoint(point2)
|
||||
|
||||
self._drawSymbol()
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
Graphical representation of a node on the QGraphicsScene.
|
||||
"""
|
||||
|
||||
import sip
|
||||
from ..qt import sip
|
||||
|
||||
from ..qt import QtCore, QtGui, QtWidgets, QtSvg, qslot
|
||||
from ..qt.qimage_svg_renderer import QImageSvgRenderer
|
||||
@@ -99,22 +99,11 @@ class NodeItem(QtSvg.QGraphicsSvgItem):
|
||||
|
||||
from ..main_window import MainWindow
|
||||
self._main_window = MainWindow.instance()
|
||||
if self._main_window.uiSnapToGridAction.isChecked():
|
||||
self._snapToGrid()
|
||||
self._settings = self._main_window.uiGraphicsView.settings()
|
||||
|
||||
if node.initialized():
|
||||
self.createdSlot(node.id())
|
||||
|
||||
def _snapToGrid(self):
|
||||
|
||||
grid_size = self._main_window.uiGraphicsView.gridSize()
|
||||
mid_x = self.boundingRect().width() / 2
|
||||
x = (grid_size * round((self.x() + mid_x) / grid_size)) - mid_x
|
||||
mid_y = self.boundingRect().height() / 2
|
||||
y = (grid_size * round((self.y() + mid_y) / grid_size)) - mid_y
|
||||
self.setPos(x, y)
|
||||
|
||||
def updateNode(self):
|
||||
"""
|
||||
Sync change to the node
|
||||
|
||||
@@ -107,7 +107,7 @@ class SerialLinkItem(LinkItem):
|
||||
|
||||
QtWidgets.QGraphicsPathItem.paint(self, painter, option, widget)
|
||||
|
||||
if not self._adding_flag and self._settings["draw_link_status_points"]:
|
||||
if not self._adding_flag:
|
||||
|
||||
# points disappears if nodes are too close to each others.
|
||||
if self.length < 80:
|
||||
@@ -140,7 +140,8 @@ class SerialLinkItem(LinkItem):
|
||||
else:
|
||||
source_port_label.hide()
|
||||
|
||||
painter.drawPoint(self.source_point)
|
||||
if self._settings["draw_link_status_points"]:
|
||||
painter.drawPoint(self.source_point)
|
||||
|
||||
# destination point color
|
||||
if self._link.suspended() or self._destination_port.status() == Port.suspended:
|
||||
@@ -170,6 +171,7 @@ class SerialLinkItem(LinkItem):
|
||||
else:
|
||||
destination_port_label.hide()
|
||||
|
||||
painter.drawPoint(self.destination_point)
|
||||
if self._settings["draw_link_status_points"]:
|
||||
painter.drawPoint(self.destination_point)
|
||||
|
||||
self._drawSymbol()
|
||||
|
||||
@@ -21,10 +21,10 @@ Manages and stores everything needed for a connection between 2 devices.
|
||||
|
||||
import os
|
||||
import re
|
||||
import sip
|
||||
from .qt import sip
|
||||
import uuid
|
||||
|
||||
from .qt import QtCore, QtWidgets
|
||||
from .qt import QtCore
|
||||
from .controller import Controller
|
||||
|
||||
|
||||
@@ -172,7 +172,7 @@ class Link(QtCore.QObject):
|
||||
|
||||
def updateLinkCallback(self, result, error=False, *args, **kwargs):
|
||||
if error:
|
||||
QtWidgets.QMessageBox.warning(None, "Update link", "Error while updating link: {}".format(result["message"]))
|
||||
log.warning("Error while updating link: {}".format(result["message"]))
|
||||
return
|
||||
self._parseResponse(result)
|
||||
|
||||
@@ -222,7 +222,7 @@ class Link(QtCore.QObject):
|
||||
|
||||
def _linkCreatedCallback(self, result, error=False, **kwargs):
|
||||
if error:
|
||||
QtWidgets.QMessageBox.warning(None, "Create link", "Error while creating link: {}".format(result["message"]))
|
||||
log.warning("Error while creating link: {}".format(result["message"]))
|
||||
self.deleteLink(skip_controller=True)
|
||||
return
|
||||
|
||||
|
||||
@@ -216,9 +216,12 @@ class LocalConfig(QtCore.QObject):
|
||||
# settings from 1.6.1 with 1.5.1 you will have an error
|
||||
if "version" in self._settings:
|
||||
if parse_version(self._settings["version"])[:2] > parse_version(__version__)[:2]:
|
||||
QtWidgets.QApplication(sys.argv) # We need to create an application because settings are loaded before Qt init
|
||||
QtWidgets.QMessageBox.critical(None, "Version error", "Your settings are for version {} of GNS3. You cannot use a previous version of GNS3 without risking losing data. If you want to reset delete the settings in {}".format(self._settings["version"], self.configDirectory()))
|
||||
app = QtWidgets.QApplication(sys.argv) # We need to create an application because settings are loaded before Qt init
|
||||
error_message = "Your settings are for version {} of GNS3. You cannot use a previous version of GNS3 without risking losing data. If you want to reset delete the settings in {}".format(self._settings["version"], self.configDirectory())
|
||||
QtWidgets.QMessageBox.critical(False, "Version error", error_message)
|
||||
# Exit immediately not clean but we want to avoid any side effect that could corrupt the file
|
||||
QtCore.QTimer.singleShot(0, app.quit)
|
||||
app.exec_()
|
||||
sys.exit(1)
|
||||
|
||||
if "version" not in self._settings or parse_version(self._settings["version"]) < parse_version("1.4.0alpha1"):
|
||||
|
||||
@@ -36,7 +36,6 @@ from gns3.local_config import LocalConfig
|
||||
from gns3.local_server_config import LocalServerConfig
|
||||
from gns3.utils.wait_for_connection_worker import WaitForConnectionWorker
|
||||
from gns3.utils.progress_dialog import ProgressDialog
|
||||
from gns3.utils.http import getSynchronous
|
||||
from gns3.utils.sudo import sudo
|
||||
from gns3.http_client import HTTPClient
|
||||
from gns3.controller import Controller
|
||||
@@ -372,15 +371,13 @@ class LocalServer(QtCore.QObject):
|
||||
|
||||
if sys.platform.startswith('win'):
|
||||
if not self._checkWindowsService("npf") and not self._checkWindowsService("npcap"):
|
||||
QtWidgets.QMessageBox.critical(self.parent(), "Error", "The NPF or NPCAP service is not installed, please install Winpcap or Npcap and reboot.")
|
||||
return False
|
||||
log.warning("The NPF or NPCAP service is not installed, please install Winpcap or Npcap and reboot.")
|
||||
|
||||
self._port = self._settings["port"]
|
||||
|
||||
# check the local server path
|
||||
local_server_path = self.localServerPath()
|
||||
if not local_server_path:
|
||||
log.warn("No local server is configured")
|
||||
log.warning("No local server is configured")
|
||||
return False
|
||||
if not os.path.isfile(local_server_path):
|
||||
QtWidgets.QMessageBox.critical(self.parent(), "Local server", "Could not find local server {}".format(local_server_path))
|
||||
@@ -517,9 +514,7 @@ class LocalServer(QtCore.QObject):
|
||||
:returns: boolean
|
||||
"""
|
||||
|
||||
status, json_data = getSynchronous(self._settings["protocol"], self._settings["host"], self._port, "version",
|
||||
timeout=2, user=self._settings["user"], password=self._settings["password"])
|
||||
|
||||
status, json_data = HTTPClient(self._settings).getSynchronous("GET", "/version", timeout=2)
|
||||
if status == 401: # Auth issue that need to be solved later
|
||||
return True
|
||||
elif json_data is None:
|
||||
|
||||
@@ -268,7 +268,10 @@ def main():
|
||||
# issue when people run GNS3 from the .dmg
|
||||
if sys.platform.startswith("darwin") and hasattr(sys, "frozen"):
|
||||
if not os.path.realpath(sys.executable).startswith("/Applications"):
|
||||
QtWidgets.QMessageBox.critical(None, "Error", "You need to copy GNS3 in your /Applications folder before using it.")
|
||||
error_message = "GNS3.app must be moved to the '/Applications' folder before it can be used"
|
||||
QtWidgets.QMessageBox.critical(False, "Loading error", error_message)
|
||||
QtCore.QTimer.singleShot(0, app.quit)
|
||||
app.exec_()
|
||||
sys.exit(1)
|
||||
|
||||
global mainwindow
|
||||
@@ -292,7 +295,6 @@ def main():
|
||||
mainwindow.show()
|
||||
|
||||
exit_code = app.exec_()
|
||||
|
||||
signal.signal(signal.SIGINT, orig_sigint)
|
||||
signal.signal(signal.SIGTERM, orig_sigterm)
|
||||
|
||||
@@ -301,7 +303,7 @@ def main():
|
||||
# We force deleting the app object otherwise it's segfault on Fedora
|
||||
del app
|
||||
# We force a full garbage collect before exit
|
||||
# for unknow reason otherwise Qt Segfault on OSX in some
|
||||
# for unknown reason otherwise Qt Segfault on OSX in some
|
||||
# conditions
|
||||
import gc
|
||||
gc.collect()
|
||||
|
||||
@@ -77,7 +77,7 @@ class IOUDevice(Node):
|
||||
return
|
||||
|
||||
log.debug("{} is starting".format(self.name()))
|
||||
self.controllerHttpPost("/nodes/{node_id}/start".format(node_id=self._node_id), self._startCallback, progressText="{} is starting".format(self.name()))
|
||||
self.controllerHttpPost("/nodes/{node_id}/start".format(node_id=self._node_id), self._startCallback, showProgress=False)
|
||||
|
||||
def update(self, new_settings):
|
||||
"""
|
||||
|
||||
@@ -78,9 +78,14 @@ class Module(QtCore.QObject):
|
||||
:param directory: destination directory path
|
||||
"""
|
||||
|
||||
node_names_cannot_export = []
|
||||
for node in self._nodes:
|
||||
if hasattr(node, "initialized") and node.initialized():
|
||||
node.exportConfigToDirectory(directory)
|
||||
if not node.exportConfigToDirectory(directory):
|
||||
node_names_cannot_export.append(node.name())
|
||||
|
||||
if node_names_cannot_export:
|
||||
log.warning("Config export is not supported by the following nodes: {}".format(" ".join(node_names_cannot_export)))
|
||||
|
||||
def importConfigs(self, directory):
|
||||
"""
|
||||
|
||||
@@ -88,7 +88,7 @@ class TraceNGNode(Node):
|
||||
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()))
|
||||
self.controllerHttpPost("/nodes/{node_id}/start".format(node_id=self._node_id), self._startCallback, body=params, timeout=None, showProgress=False)
|
||||
|
||||
def info(self):
|
||||
"""
|
||||
|
||||
@@ -419,7 +419,7 @@ class Node(BaseNode):
|
||||
return
|
||||
|
||||
log.debug("{} is starting".format(self.name()))
|
||||
self.controllerHttpPost("/nodes/{node_id}/start".format(node_id=self._node_id), self._startCallback, timeout=None, progressText="{} is starting".format(self.name()))
|
||||
self.controllerHttpPost("/nodes/{node_id}/start".format(node_id=self._node_id), self._startCallback, timeout=None, showProgress=False)
|
||||
|
||||
def _startCallback(self, result, error=False, **kwargs):
|
||||
"""
|
||||
@@ -445,7 +445,7 @@ class Node(BaseNode):
|
||||
return
|
||||
|
||||
log.debug("{} is stopping".format(self.name()))
|
||||
self.controllerHttpPost("/nodes/{node_id}/stop".format(node_id=self._node_id), self._stopCallback, progressText="{} is stopping".format(self.name()), timeout=None)
|
||||
self.controllerHttpPost("/nodes/{node_id}/stop".format(node_id=self._node_id), self._stopCallback, showProgress=False, timeout=None)
|
||||
|
||||
def _stopCallback(self, result, error=False, **kwargs):
|
||||
"""
|
||||
|
||||
@@ -22,7 +22,7 @@ on the QGraphics scene.
|
||||
|
||||
import tempfile
|
||||
import json
|
||||
import sip
|
||||
from .qt import sip
|
||||
|
||||
from .qt import QtCore, QtGui, QtWidgets, qpartial
|
||||
from .modules import MODULES
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
Base class for port objects.
|
||||
"""
|
||||
|
||||
import sip
|
||||
from ..qt import sip
|
||||
|
||||
from ..qt import qslot
|
||||
|
||||
|
||||
@@ -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/>.
|
||||
|
||||
import sip
|
||||
from .qt import sip
|
||||
import time
|
||||
from contextlib import contextmanager
|
||||
|
||||
@@ -189,7 +189,10 @@ class Progress(QtCore.QObject):
|
||||
# Due to Qt limitations for large numbers (above 32bit int) we calculate "progress" ourselves
|
||||
current, maximum = self._normalize(query['current'], query['maximum'])
|
||||
progress_dialog.setMaximum(maximum)
|
||||
progress_dialog.setValue(current)
|
||||
try:
|
||||
progress_dialog.setValue(current)
|
||||
except OverflowError:
|
||||
progress_dialog.setValue(100)
|
||||
|
||||
if text and query["maximum"] > 1000:
|
||||
text += "\n{} / {}".format(human_filesize(query["current"]), human_filesize(query["maximum"]))
|
||||
|
||||
@@ -322,7 +322,7 @@ class Project(QtCore.QObject):
|
||||
def _duplicateCallback(self, callback, result, error=False, **kwargs):
|
||||
if error:
|
||||
if "message" in result:
|
||||
QtWidgets.QMessageBox.critical(None, "Duplicate project", "Error while duplicating: {}".format(result["message"]))
|
||||
log.error("Error while duplicating project: {}".format(result["message"]))
|
||||
return
|
||||
if callback:
|
||||
callback(result["project_id"])
|
||||
|
||||
@@ -24,7 +24,6 @@ Compatibility layer for Qt bindings, so it is easier to switch to PySide if need
|
||||
|
||||
|
||||
import sys
|
||||
import sip
|
||||
import os
|
||||
import re
|
||||
import inspect
|
||||
@@ -33,12 +32,17 @@ import functools
|
||||
import logging
|
||||
log = logging.getLogger("qt/__init__.py")
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtNetwork, QtWidgets, Qt
|
||||
try:
|
||||
from PyQt5 import sip
|
||||
except ImportError:
|
||||
import sip
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtNetwork, QtWidgets
|
||||
sys.modules[__name__ + '.QtCore'] = QtCore
|
||||
sys.modules[__name__ + '.QtGui'] = QtGui
|
||||
sys.modules[__name__ + '.QtNetwork'] = QtNetwork
|
||||
sys.modules[__name__ + '.QtWidgets'] = QtWidgets
|
||||
sys.modules[__name__ + '.Qt'] = Qt
|
||||
sys.modules[__name__ + '.sip'] = sip
|
||||
|
||||
try:
|
||||
from PyQt5 import QtSvg
|
||||
@@ -115,14 +119,20 @@ class LogQMessageBox(QtWidgets.QMessageBox):
|
||||
if message.startswith("QXcbConnection"): # Qt noise not relevant
|
||||
return
|
||||
LogQMessageBox._get_logger().critical(re.sub(r"<[^<]+?>", "", message), stack_info=LogQMessageBox.stack_info())
|
||||
if sip_is_deleted(parent):
|
||||
if parent is False:
|
||||
# special case to display a QMessageBox before the main window is created.
|
||||
parent = None
|
||||
elif sip_is_deleted(parent):
|
||||
return
|
||||
return super(QtWidgets.QMessageBox, QtWidgets.QMessageBox).critical(parent, title, message, *args)
|
||||
|
||||
@staticmethod
|
||||
def warning(parent, title, message, *args):
|
||||
LogQMessageBox._get_logger().warning(re.sub(r"<[^<]+?>", "", message))
|
||||
if sip_is_deleted(parent):
|
||||
if parent is False:
|
||||
# special case to display a QMessageBox before the main window is created.
|
||||
parent = None
|
||||
elif sip_is_deleted(parent):
|
||||
return
|
||||
return super(QtWidgets.QMessageBox, QtWidgets.QMessageBox).warning(parent, title, message, *args)
|
||||
|
||||
@@ -130,7 +140,7 @@ class LogQMessageBox(QtWidgets.QMessageBox):
|
||||
def _get_logger():
|
||||
"""
|
||||
Return a logger in the context of the caller
|
||||
in order to have the correct informations in the log
|
||||
in order to have the correct information in the log
|
||||
"""
|
||||
if sys.version_info < (3, 5):
|
||||
return logging.getLogger('qt')
|
||||
|
||||
@@ -107,16 +107,16 @@ class Image:
|
||||
try:
|
||||
if not os.path.isfile(self.path):
|
||||
return None
|
||||
m = hashlib.md5()
|
||||
with open(self.path, "rb") as f:
|
||||
while True:
|
||||
buf = f.read(4096)
|
||||
if not buf:
|
||||
break
|
||||
m.update(buf)
|
||||
except (OSError, PermissionError) as e:
|
||||
log.debug("Cannot access '{}': {}".format(self.path, e))
|
||||
return None
|
||||
m = hashlib.md5()
|
||||
with open(self.path, "rb") as f:
|
||||
while True:
|
||||
buf = f.read(4096)
|
||||
if not buf:
|
||||
break
|
||||
m.update(buf)
|
||||
self._md5sum = m.hexdigest()
|
||||
Image._cache[self.path] = self._md5sum
|
||||
return self._md5sum
|
||||
|
||||
@@ -87,11 +87,11 @@ class Registry(QtCore.QObject):
|
||||
|
||||
for directory in self._images_dirs:
|
||||
log.debug("Search images %s (%s) in %s", filename, md5sum, directory)
|
||||
if os.path.exists(directory):
|
||||
for file in os.listdir(directory):
|
||||
if not file.endswith(".md5sum") and not file.startswith("."):
|
||||
path = os.path.join(directory, file)
|
||||
try:
|
||||
try:
|
||||
if os.path.exists(directory):
|
||||
for file in os.listdir(directory):
|
||||
if not file.endswith(".md5sum") and not file.startswith("."):
|
||||
path = os.path.join(directory, file)
|
||||
if os.path.isfile(path):
|
||||
if md5sum is None:
|
||||
if filename == os.path.basename(path):
|
||||
@@ -105,7 +105,6 @@ class Registry(QtCore.QObject):
|
||||
if image.md5sum == md5sum:
|
||||
log.debug("Found images %s (%s) in %s", filename, md5sum, image.path)
|
||||
return image
|
||||
except (OSError, PermissionError) as e:
|
||||
log.error("Cannot scan {}: {}".format(path, e))
|
||||
|
||||
except (OSError, PermissionError) as e:
|
||||
log.error("Cannot scan {}: {}".format(path, e))
|
||||
return None
|
||||
|
||||
@@ -65,7 +65,20 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QSpinBox" name="uiGridSizeSpinBox"/>
|
||||
<widget class="QSpinBox" name="uiGridSizeSpinBox">
|
||||
<property name="minimum">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>75</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QSpinBox" name="uiSceneHeightSpinBox">
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file '/home/dominik/projects/gns3-gui/gns3/ui/edit_project_dialog.ui'
|
||||
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/ui/edit_project_dialog.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.8.2
|
||||
# Created by: PyQt5 UI code generator 5.9
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
@@ -37,6 +37,10 @@ class Ui_EditProjectDialog(object):
|
||||
self.uiSceneWidthSpinBox.setObjectName("uiSceneWidthSpinBox")
|
||||
self.uiGeneralGrid.addWidget(self.uiSceneWidthSpinBox, 2, 1, 1, 1)
|
||||
self.uiGridSizeSpinBox = QtWidgets.QSpinBox(self.uiGeneralTab)
|
||||
self.uiGridSizeSpinBox.setMinimum(5)
|
||||
self.uiGridSizeSpinBox.setMaximum(100)
|
||||
self.uiGridSizeSpinBox.setSingleStep(5)
|
||||
self.uiGridSizeSpinBox.setProperty("value", 75)
|
||||
self.uiGridSizeSpinBox.setObjectName("uiGridSizeSpinBox")
|
||||
self.uiGeneralGrid.addWidget(self.uiGridSizeSpinBox, 4, 1, 1, 1)
|
||||
self.uiSceneHeightSpinBox = QtWidgets.QSpinBox(self.uiGeneralTab)
|
||||
|
||||
@@ -809,13 +809,13 @@
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QSpinBox" name="uiGridSizeSpinBox">
|
||||
<property name="minimum">
|
||||
<number>10</number>
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>10</number>
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>75</number>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/ui/general_preferences_page.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.11.3
|
||||
# Created by: PyQt5 UI code generator 5.9
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
@@ -368,9 +368,9 @@ class Ui_GeneralPreferencesPageWidget(object):
|
||||
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.setMinimum(5)
|
||||
self.uiGridSizeSpinBox.setMaximum(100)
|
||||
self.uiGridSizeSpinBox.setSingleStep(10)
|
||||
self.uiGridSizeSpinBox.setSingleStep(5)
|
||||
self.uiGridSizeSpinBox.setProperty("value", 75)
|
||||
self.uiGridSizeSpinBox.setObjectName("uiGridSizeSpinBox")
|
||||
self.gridLayout_8.addWidget(self.uiGridSizeSpinBox, 5, 0, 1, 2)
|
||||
|
||||
168392
gns3/ui/resources_rc.py
168392
gns3/ui/resources_rc.py
File diff suppressed because it is too large
Load Diff
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>754</width>
|
||||
<height>526</height>
|
||||
<width>800</width>
|
||||
<height>650</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@@ -16,12 +16,6 @@
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>775</width>
|
||||
<height>563</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Setup Wizard</string>
|
||||
</property>
|
||||
@@ -248,97 +242,102 @@
|
||||
<property name="subTitle">
|
||||
<string>In order to run the GNS3 VM you must first have VMware or VirtualBox installed and the GNS3 VM.ova imported with one of these software.</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="uiVirtualizationSoftwarLabel">
|
||||
<property name="text">
|
||||
<string>Virtualization software:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="uiVmwareRadioButton">
|
||||
<property name="toolTip">
|
||||
<string>VMware is recommended to run Qemu based appliances (required for KVM).</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>VMware (recommended)</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="uiVirtualBoxRadioButton">
|
||||
<property name="toolTip">
|
||||
<string>Use VirtualBox if you intend to only use Dynamips, IOU or VPCS.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>VirtualBox</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<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>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="uiVMwareBannerButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normalon>:/images/vmware_fusion_banner.png</normalon>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>454</width>
|
||||
<height>150</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="uiGNS3VMDownloadLinkUrlLabel">
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="uiVirtualizationSoftwarLabel">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>The GNS3 VM can be <a href="https://github.com/GNS3/gns3-gui/releases/download/v1.4.1/GNS3.VM.VMware.Workstation.1.4.1.zip"><span style=" text-decoration: underline; color:#0000ff;">downloaded here</span></a>. Import the VM in your virtualization software and hit refresh.</p></body></html></string>
|
||||
<string>Virtualization software:</string>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2" rowspan="4">
|
||||
<widget class="QPushButton" name="uiVMwareBannerButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../resources/resources.qrc">
|
||||
<normaloff>:/images/vmware_fusion_banner.png</normaloff>:/images/vmware_fusion_banner.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>300</width>
|
||||
<height>150</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QRadioButton" name="uiVmwareRadioButton">
|
||||
<property name="toolTip">
|
||||
<string>VMware is recommended to run Qemu based appliances (required for KVM).</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>VMware (recommended)</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>317</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QRadioButton" name="uiVirtualBoxRadioButton">
|
||||
<property name="toolTip">
|
||||
<string>Use VirtualBox if you intend to only use Dynamips, IOU or VPCS.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>VirtualBox</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="uiVMNameLabel">
|
||||
<property name="text">
|
||||
<string>VM name:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="uiCPULabel">
|
||||
<property name="text">
|
||||
<string>vCPU cores:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<widget class="QLabel" name="uiRAMLabel">
|
||||
<property name="text">
|
||||
<string>RAM size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="uiGNS3VMDownloadLinkUrlLabel">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>The GNS3 VM can be <a href="https://github.com/GNS3/gns3-gui/releases/download/v1.4.1/GNS3.VM.VMware.Workstation.1.4.1.zip"><span style=" text-decoration: underline; color:#0000ff;">downloaded here</span></a></p></body></html></string>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0" colspan="3">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QComboBox" name="uiVMListComboBox">
|
||||
@@ -359,14 +358,7 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="uiCPULabel">
|
||||
<property name="text">
|
||||
<string>vCPU cores:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item row="8" column="0" colspan="3">
|
||||
<widget class="QSpinBox" name="uiCPUSpinBox">
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
@@ -379,14 +371,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="uiRAMLabel">
|
||||
<property name="text">
|
||||
<string>RAM size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item row="10" column="0" colspan="3">
|
||||
<widget class="QSpinBox" name="uiRAMSpinBox">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/ui/setup_wizard.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.9.1
|
||||
# Created by: PyQt5 UI code generator 5.9
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
@@ -11,13 +11,12 @@ from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
class Ui_SetupWizard(object):
|
||||
def setupUi(self, SetupWizard):
|
||||
SetupWizard.setObjectName("SetupWizard")
|
||||
SetupWizard.resize(754, 526)
|
||||
SetupWizard.resize(800, 650)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(SetupWizard.sizePolicy().hasHeightForWidth())
|
||||
SetupWizard.setSizePolicy(sizePolicy)
|
||||
SetupWizard.setMaximumSize(QtCore.QSize(775, 563))
|
||||
SetupWizard.setModal(True)
|
||||
SetupWizard.setWizardStyle(QtWidgets.QWizard.ModernStyle)
|
||||
SetupWizard.setOptions(QtWidgets.QWizard.NoBackButtonOnStartPage)
|
||||
@@ -116,42 +115,42 @@ class Ui_SetupWizard(object):
|
||||
SetupWizard.addPage(self.uiLocalServerStatusWizardPage)
|
||||
self.uiVMWizardPage = QtWidgets.QWizardPage()
|
||||
self.uiVMWizardPage.setObjectName("uiVMWizardPage")
|
||||
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.uiVMWizardPage)
|
||||
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
||||
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout()
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.gridLayout_3 = QtWidgets.QGridLayout(self.uiVMWizardPage)
|
||||
self.gridLayout_3.setObjectName("gridLayout_3")
|
||||
self.uiVirtualizationSoftwarLabel = QtWidgets.QLabel(self.uiVMWizardPage)
|
||||
self.uiVirtualizationSoftwarLabel.setObjectName("uiVirtualizationSoftwarLabel")
|
||||
self.verticalLayout.addWidget(self.uiVirtualizationSoftwarLabel)
|
||||
self.uiVmwareRadioButton = QtWidgets.QRadioButton(self.uiVMWizardPage)
|
||||
self.uiVmwareRadioButton.setChecked(True)
|
||||
self.uiVmwareRadioButton.setObjectName("uiVmwareRadioButton")
|
||||
self.verticalLayout.addWidget(self.uiVmwareRadioButton)
|
||||
self.uiVirtualBoxRadioButton = QtWidgets.QRadioButton(self.uiVMWizardPage)
|
||||
self.uiVirtualBoxRadioButton.setObjectName("uiVirtualBoxRadioButton")
|
||||
self.verticalLayout.addWidget(self.uiVirtualBoxRadioButton)
|
||||
spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.verticalLayout.addItem(spacerItem1)
|
||||
self.horizontalLayout_2.addLayout(self.verticalLayout)
|
||||
self.gridLayout_3.addWidget(self.uiVirtualizationSoftwarLabel, 0, 0, 1, 1)
|
||||
self.uiVMwareBannerButton = QtWidgets.QPushButton(self.uiVMWizardPage)
|
||||
self.uiVMwareBannerButton.setText("")
|
||||
icon = QtGui.QIcon()
|
||||
icon.addPixmap(QtGui.QPixmap(":/images/vmware_fusion_banner.png"), QtGui.QIcon.Normal, QtGui.QIcon.On)
|
||||
icon.addPixmap(QtGui.QPixmap(":/images/vmware_fusion_banner.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.uiVMwareBannerButton.setIcon(icon)
|
||||
self.uiVMwareBannerButton.setIconSize(QtCore.QSize(454, 150))
|
||||
self.uiVMwareBannerButton.setIconSize(QtCore.QSize(300, 150))
|
||||
self.uiVMwareBannerButton.setFlat(True)
|
||||
self.uiVMwareBannerButton.setObjectName("uiVMwareBannerButton")
|
||||
self.horizontalLayout_2.addWidget(self.uiVMwareBannerButton)
|
||||
self.verticalLayout_2.addLayout(self.horizontalLayout_2)
|
||||
self.gridLayout_3.addWidget(self.uiVMwareBannerButton, 0, 2, 4, 1)
|
||||
self.uiVmwareRadioButton = QtWidgets.QRadioButton(self.uiVMWizardPage)
|
||||
self.uiVmwareRadioButton.setChecked(True)
|
||||
self.uiVmwareRadioButton.setObjectName("uiVmwareRadioButton")
|
||||
self.gridLayout_3.addWidget(self.uiVmwareRadioButton, 1, 0, 1, 1)
|
||||
spacerItem1 = QtWidgets.QSpacerItem(317, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||
self.gridLayout_3.addItem(spacerItem1, 1, 1, 1, 1)
|
||||
self.uiVirtualBoxRadioButton = QtWidgets.QRadioButton(self.uiVMWizardPage)
|
||||
self.uiVirtualBoxRadioButton.setObjectName("uiVirtualBoxRadioButton")
|
||||
self.gridLayout_3.addWidget(self.uiVirtualBoxRadioButton, 2, 0, 1, 1)
|
||||
self.uiVMNameLabel = QtWidgets.QLabel(self.uiVMWizardPage)
|
||||
self.uiVMNameLabel.setObjectName("uiVMNameLabel")
|
||||
self.gridLayout_3.addWidget(self.uiVMNameLabel, 5, 0, 1, 1)
|
||||
self.uiCPULabel = QtWidgets.QLabel(self.uiVMWizardPage)
|
||||
self.uiCPULabel.setObjectName("uiCPULabel")
|
||||
self.gridLayout_3.addWidget(self.uiCPULabel, 7, 0, 1, 1)
|
||||
self.uiRAMLabel = QtWidgets.QLabel(self.uiVMWizardPage)
|
||||
self.uiRAMLabel.setObjectName("uiRAMLabel")
|
||||
self.gridLayout_3.addWidget(self.uiRAMLabel, 9, 0, 1, 1)
|
||||
self.uiGNS3VMDownloadLinkUrlLabel = QtWidgets.QLabel(self.uiVMWizardPage)
|
||||
self.uiGNS3VMDownloadLinkUrlLabel.setOpenExternalLinks(True)
|
||||
self.uiGNS3VMDownloadLinkUrlLabel.setObjectName("uiGNS3VMDownloadLinkUrlLabel")
|
||||
self.verticalLayout_2.addWidget(self.uiGNS3VMDownloadLinkUrlLabel)
|
||||
self.uiVMNameLabel = QtWidgets.QLabel(self.uiVMWizardPage)
|
||||
self.uiVMNameLabel.setObjectName("uiVMNameLabel")
|
||||
self.verticalLayout_2.addWidget(self.uiVMNameLabel)
|
||||
self.gridLayout_3.addWidget(self.uiGNS3VMDownloadLinkUrlLabel, 3, 0, 1, 1)
|
||||
self.horizontalLayout = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||
self.uiVMListComboBox = QtWidgets.QComboBox(self.uiVMWizardPage)
|
||||
@@ -165,19 +164,13 @@ class Ui_SetupWizard(object):
|
||||
self.uiRefreshPushButton = QtWidgets.QPushButton(self.uiVMWizardPage)
|
||||
self.uiRefreshPushButton.setObjectName("uiRefreshPushButton")
|
||||
self.horizontalLayout.addWidget(self.uiRefreshPushButton)
|
||||
self.verticalLayout_2.addLayout(self.horizontalLayout)
|
||||
self.uiCPULabel = QtWidgets.QLabel(self.uiVMWizardPage)
|
||||
self.uiCPULabel.setObjectName("uiCPULabel")
|
||||
self.verticalLayout_2.addWidget(self.uiCPULabel)
|
||||
self.gridLayout_3.addLayout(self.horizontalLayout, 6, 0, 1, 3)
|
||||
self.uiCPUSpinBox = QtWidgets.QSpinBox(self.uiVMWizardPage)
|
||||
self.uiCPUSpinBox.setMinimum(1)
|
||||
self.uiCPUSpinBox.setMaximum(128)
|
||||
self.uiCPUSpinBox.setProperty("value", 1)
|
||||
self.uiCPUSpinBox.setObjectName("uiCPUSpinBox")
|
||||
self.verticalLayout_2.addWidget(self.uiCPUSpinBox)
|
||||
self.uiRAMLabel = QtWidgets.QLabel(self.uiVMWizardPage)
|
||||
self.uiRAMLabel.setObjectName("uiRAMLabel")
|
||||
self.verticalLayout_2.addWidget(self.uiRAMLabel)
|
||||
self.gridLayout_3.addWidget(self.uiCPUSpinBox, 8, 0, 1, 3)
|
||||
self.uiRAMSpinBox = QtWidgets.QSpinBox(self.uiVMWizardPage)
|
||||
self.uiRAMSpinBox.setEnabled(True)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
|
||||
@@ -190,7 +183,7 @@ class Ui_SetupWizard(object):
|
||||
self.uiRAMSpinBox.setSingleStep(512)
|
||||
self.uiRAMSpinBox.setProperty("value", 2048)
|
||||
self.uiRAMSpinBox.setObjectName("uiRAMSpinBox")
|
||||
self.verticalLayout_2.addWidget(self.uiRAMSpinBox)
|
||||
self.gridLayout_3.addWidget(self.uiRAMSpinBox, 10, 0, 1, 3)
|
||||
SetupWizard.addPage(self.uiVMWizardPage)
|
||||
self.uiRemoteControllerWizardPage = QtWidgets.QWizardPage()
|
||||
self.uiRemoteControllerWizardPage.setObjectName("uiRemoteControllerWizardPage")
|
||||
@@ -276,11 +269,11 @@ class Ui_SetupWizard(object):
|
||||
self.uiVmwareRadioButton.setText(_translate("SetupWizard", "VMware (recommended)"))
|
||||
self.uiVirtualBoxRadioButton.setToolTip(_translate("SetupWizard", "Use VirtualBox if you intend to only use Dynamips, IOU or VPCS."))
|
||||
self.uiVirtualBoxRadioButton.setText(_translate("SetupWizard", "VirtualBox"))
|
||||
self.uiGNS3VMDownloadLinkUrlLabel.setText(_translate("SetupWizard", "<html><head/><body><p>The GNS3 VM can be <a href=\"https://github.com/GNS3/gns3-gui/releases/download/v1.4.1/GNS3.VM.VMware.Workstation.1.4.1.zip\"><span style=\" text-decoration: underline; color:#0000ff;\">downloaded here</span></a>. Import the VM in your virtualization software and hit refresh.</p></body></html>"))
|
||||
self.uiVMNameLabel.setText(_translate("SetupWizard", "VM name:"))
|
||||
self.uiRefreshPushButton.setText(_translate("SetupWizard", "&Refresh"))
|
||||
self.uiCPULabel.setText(_translate("SetupWizard", "vCPU cores:"))
|
||||
self.uiRAMLabel.setText(_translate("SetupWizard", "RAM size:"))
|
||||
self.uiGNS3VMDownloadLinkUrlLabel.setText(_translate("SetupWizard", "<html><head/><body><p>The GNS3 VM can be <a href=\"https://github.com/GNS3/gns3-gui/releases/download/v1.4.1/GNS3.VM.VMware.Workstation.1.4.1.zip\"><span style=\" text-decoration: underline; color:#0000ff;\">downloaded here</span></a></p></body></html>"))
|
||||
self.uiRefreshPushButton.setText(_translate("SetupWizard", "&Refresh"))
|
||||
self.uiRAMSpinBox.setSuffix(_translate("SetupWizard", " MB"))
|
||||
self.uiRemoteControllerWizardPage.setTitle(_translate("SetupWizard", "Remote server"))
|
||||
self.uiRemoteControllerWizardPage.setSubTitle(_translate("SetupWizard", "Everything will run on a remote server. No data will be saved on this computer."))
|
||||
|
||||
@@ -26,21 +26,22 @@ import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def getSynchronous(protocol, host, port, endpoint, timeout=2, user=None, password=None):
|
||||
# Accept the kwargs but don't use them, this is to fix an issue in Python 3.6: https://github.com/GNS3/gns3-gui/issues/2793
|
||||
def getSynchronous(protocol, host, port, endpoint, timeout=2, user=None, password=None, *args, **kwargs):
|
||||
"""
|
||||
:returns: Tuple (Status code, json of anwser). Status 0 is a non HTTP error
|
||||
"""
|
||||
try:
|
||||
url = "{protocol}://{host}:{port}/v2/{endpoint}".format(protocol=protocol, host=host, port=port, endpoint=endpoint)
|
||||
request = urllib.request.Request(url)
|
||||
|
||||
headers = {}
|
||||
if user is not None and len(user) > 0:
|
||||
log.debug("Synchronous get {} with user '{}'".format(url, user))
|
||||
base64string = base64.encodebytes('{}:{}'.format(user, password).encode()).replace(b'\n', b'')
|
||||
request.add_header("Authorization", "Basic {}".format(base64string.decode()))
|
||||
headers["Authorization"] = "Basic {}".format(base64string.decode())
|
||||
else:
|
||||
log.debug("Synchronous get {} (no authentication)".format(url))
|
||||
|
||||
request = urllib.request.Request(url, headers=headers)
|
||||
if sys.version_info >= (3, 5):
|
||||
ctx = ssl.create_default_context()
|
||||
ctx.check_hostname = False
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
Progress dialog that blocking tasks (file operations, network connections etc.)
|
||||
"""
|
||||
|
||||
import sip
|
||||
from ..qt import sip
|
||||
from gns3.version import __version__
|
||||
from ..qt import QtWidgets, QtCore, qslot
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
Thread showing a progress dialog and running the code from a lambda
|
||||
"""
|
||||
|
||||
import sip
|
||||
from ..qt import sip
|
||||
|
||||
from ..qt import QtCore
|
||||
|
||||
|
||||
@@ -23,8 +23,9 @@
|
||||
# or negative for a release candidate or beta (after the base version
|
||||
# number has been incremented)
|
||||
|
||||
__version__ = "2.1.12"
|
||||
__version_info__ = (2, 1, 12, 0)
|
||||
__version__ = "2.1.20"
|
||||
__version_info__ = (2, 1, 20, 0)
|
||||
|
||||
# If it's a git checkout try to add the commit
|
||||
if "dev" in __version__:
|
||||
try:
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
jsonschema>=2.4.0
|
||||
jsonschema==2.6.0
|
||||
raven>=5.23.0
|
||||
psutil>=2.2.1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
-rrequirements.txt
|
||||
|
||||
PyQt5==5.9 # pyup: ignore
|
||||
PyQt5==5.12 # pyup: ignore
|
||||
pywin32>=223 # pyup: ignore
|
||||
|
||||
Reference in New Issue
Block a user