Compare commits

...

44 Commits

Author SHA1 Message Date
grossmj
450fbc9af3 Release v2.1.20 2019-05-29 15:44:25 +07:00
grossmj
469ee8fab8 Fix KeyError: 'endpoint' issue. Fixes #2802 2019-05-28 23:17:55 +07:00
grossmj
6ccfcaf76e Development on 2.1.20dev1 2019-05-28 16:33:43 +07:00
grossmj
520e857874 Release v2.1.19 2019-05-28 15:23:35 +07:00
grossmj
012c7b4241 Fix wrong aligment of symbols in saved/exported projects. Fixes #2800 2019-05-27 16:33:51 +07:00
grossmj
1d71cd5bf0 Replace urllib.request by Qt implementation for local server synchronous check. Fixes #2793 2019-05-27 16:03:55 +07:00
grossmj
d96277882a Set grid's minimum to 5. Fixes #2795 2019-05-23 14:41:53 +07:00
grossmj
ecec917752 Development on 2.1.19dev1 2019-05-23 14:37:37 +07:00
grossmj
ea9c1a8ee1 Release v2.1.18 2019-05-22 16:13:28 +07:00
grossmj
cfbb09fb57 Fix error in HTTPConnection.request for Python3.6. Fixes #2793 2019-05-22 16:05:34 +07:00
grossmj
dc8aa1fb92 Catch more OSError/PermissionError when checking md5 on remote images. Fixes #2582 2019-05-22 15:23:56 +07:00
grossmj
786cc8aa65 Fix exception when grid size is 0. Fixes #2790 2019-05-22 15:13:16 +07:00
grossmj
4a353e08e3 Catch PermissionError when scanning local image directories. Fixes #2791 2019-05-22 14:55:07 +07:00
grossmj
2a59013604 Revert "Make sure the latest PyQt5 version 5.12.x is used on Windows." Ref #2778 2019-05-20 12:01:16 +07:00
grossmj
7732aaf9a5 Release v2.1.17 2019-05-17 15:10:28 +07:00
grossmj
1f566a31cf Development on 2.1.17dev1 2019-04-15 12:41:41 +07:00
grossmj
10d75e15da Release v2.1.16 2019-04-15 12:00:18 +07:00
grossmj
17def7e00a Do not make NPF or NPCAP service mandatory to start the local server on Windows. 2019-04-15 10:33:27 +07:00
grossmj
f68a8ea829 Fix OverflowError error with progress dialog. Fixes #2767 2019-04-13 17:38:43 +07:00
grossmj
50066b2f12 More fixes for stuck progress window. Fixes #2765 2019-04-13 17:10:24 +07:00
grossmj
21a99d4376 Fix adding multiple devices - stuck progress window. Fixes #2765 2019-04-13 17:04:23 +07:00
grossmj
f97d3041b8 Make sure the latest PyQt5 version 5.12.x is used on Windows. 2019-04-13 15:43:23 +07:00
grossmj
31d6a065b0 Show a warning when a config export is not supported. Ref #2762 2019-04-11 15:32:22 +07:00
grossmj
8f077456b1 Development on 2.1.16dev1 2019-03-21 13:56:11 +08:00
grossmj
a29f3e35c0 Release v2.1.15 2019-03-21 11:41:44 +08:00
grossmj
fc3781550a Development on 2.1.15dev1 2019-02-27 15:59:16 +07:00
grossmj
d285e62c04 Release v2.1.14 2019-02-27 14:58:52 +07:00
grossmj
44d70de687 Better description to why an appliance cannot be installed. 2019-02-27 14:51:12 +07:00
grossmj
752c516f82 Development on 2.1.14dev1 2019-02-26 18:09:56 +07:00
grossmj
e1ec6c5771 Merge remote-tracking branch 'origin/2.1' into 2.1 2019-02-26 16:43:27 +07:00
grossmj
e8308869d9 Release v2.1.13 2019-02-26 16:43:14 +07:00
Jeremy Grossmann
484c5abe9d Force jsonschema dependency to 2.6.0
This is to fix this issue when starting the (frozen) application on Windows:

```
  File "C:\Python36-x64\lib\site-packages\jsonschema\validators.py", line 505, in <module>
  File "C:\Python36-x64\lib\site-packages\jsonschema\_utils.py", line 57, in load_schema
  File "C:\Python36-x64\lib\pkgutil.py", line 634, in get_data
OSError: [Errno 34] Result too large: 'jsonschema\\schemas\\draft6.json'
```
2019-02-26 15:28:16 +07:00
grossmj
fe222b873f Disable computer hibernation detection mechanism. Ref #2678 2019-02-22 17:04:12 +07:00
grossmj
f8bb6661dd Add some advice for request timeout message. Fixes #2652 2019-02-20 00:14:15 +07:00
grossmj
0f9aab9230 Merge remote-tracking branch 'origin/2.1' into 2.1 2019-02-19 16:07:51 +07:00
grossmj
a5cf5e16b7 Show/Hide interface labels when status points are not shown. Fixes #2690 2019-02-19 16:07:39 +07:00
Jeremy Grossmann
097458d108 Merge pull request #2715 from GNS3/critical-messages-before-running
Show critical messages before the main window runs. Fixes #2710
2019-02-19 15:19:53 +07:00
grossmj
f4cafac9c7 Do not print critical message twice on stderr.
Replace QMessageBox calls with no parent by log.error()/log.warning().
2019-02-18 22:09:23 +08:00
grossmj
7f132fdc36 Show critical messages before the main window runs. 2019-02-18 11:22:13 +08:00
grossmj
6b7d629755 Avoid using PyQt5.Qt, which imports unneeded stuff. Fixes #2592 2019-02-16 15:05:42 +08:00
grossmj
b7ccc37ea5 Fix SIP import error with recent PyQt versions. Fixes #2709 2019-02-16 14:38:44 +08:00
Jeremy Grossmann
bbe2826c77 Upgrade to Qt 5.12. Fixes #2636 2019-02-12 11:55:09 +08:00
grossmj
68e2a0ee39 Adjust the setup wizard (VMware image size, layouts). 2019-01-27 22:44:56 +08:00
grossmj
52418ed94a Development on 2.1.13dev1 2019-01-23 15:25:48 +08:00
42 changed files with 84563 additions and 84490 deletions

View File

@@ -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.

View File

@@ -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

View File

@@ -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"]

View File

@@ -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

View File

@@ -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:

View File

@@ -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)

View File

@@ -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__

View File

@@ -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()

View File

@@ -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

View File

@@ -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)))

View File

@@ -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

View File

@@ -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()

View File

@@ -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

View File

@@ -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()

View File

@@ -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

View File

@@ -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"):

View File

@@ -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:

View File

@@ -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()

View File

@@ -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):
"""

View File

@@ -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):
"""

View File

@@ -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):
"""

View File

@@ -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):
"""

View File

@@ -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

View File

@@ -19,7 +19,7 @@
Base class for port objects.
"""
import sip
from ..qt import sip
from ..qt import qslot

View File

@@ -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"]))

View File

@@ -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"])

View File

@@ -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')

View File

@@ -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

View File

@@ -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

View File

@@ -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">

View File

@@ -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)

View File

@@ -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>

View File

@@ -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)

File diff suppressed because it is too large Load Diff

View File

@@ -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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The GNS3 VM can be &lt;a href=&quot;https://github.com/GNS3/gns3-gui/releases/download/v1.4.1/GNS3.VM.VMware.Workstation.1.4.1.zip&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;downloaded here&lt;/span&gt;&lt;/a&gt;. Import the VM in your virtualization software and hit refresh.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The GNS3 VM can be &lt;a href=&quot;https://github.com/GNS3/gns3-gui/releases/download/v1.4.1/GNS3.VM.VMware.Workstation.1.4.1.zip&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;downloaded here&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>

View File

@@ -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."))

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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:

View File

@@ -1,3 +1,3 @@
jsonschema>=2.4.0
jsonschema==2.6.0
raven>=5.23.0
psutil>=2.2.1

View File

@@ -1,4 +1,4 @@
-rrequirements.txt
PyQt5==5.9 # pyup: ignore
PyQt5==5.12 # pyup: ignore
pywin32>=223 # pyup: ignore