diff --git a/CHANGELOG b/CHANGELOG
index 625b63f65..e5a55abd0 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,15 @@
# Change Log
+## 2.2.50 21/10/2024
+
+* Fix issue when pid file contains invalid data
+* Add comment to indicate sentry-sdk is optional. Ref https://github.com/GNS3/gns3-server/issues/2423
+* Improve information provided when uploading invalid appliance image. Fixes #3637
+* Use "experimental features" option to force listening for HTTP notification streams. Ref #3579
+* Fix to allow packet capture on more than 6 links. Fixes #3594
+* Support for configuring MAC address in Docker containers
+* Add KRDC to pre-configured VNC console commands
+
## 2.2.49 06/08/2024
* Upgrade jsonschema and sentry-sdk packages
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index 61e9697fb..000000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-version: '{build}-{branch}'
-
-image: Visual Studio 2022
-
-platform: x64
-
-environment:
- PYTHON: "C:\\Python38-x64"
- DISTUTILS_USE_SDK: "1"
-
-install:
- - cinst nmap
- - "%PYTHON%\\python.exe -m pip install -U pip setuptools" # upgrade pip & setuptools first
- - "%PYTHON%\\python.exe -m pip install -r dev-requirements.txt"
- - "%PYTHON%\\python.exe -m pip install -r win-requirements.txt"
-
-build: off
-
-test_script:
- - "%PYTHON%\\python.exe -m pytest -v"
diff --git a/gns3/controller.py b/gns3/controller.py
index 5ea402ce3..a659488d1 100644
--- a/gns3/controller.py
+++ b/gns3/controller.py
@@ -25,6 +25,8 @@
from .symbol import Symbol
from .local_server_config import LocalServerConfig
from .settings import LOCAL_SERVER_SETTINGS
+
+from gns3.local_config import LocalConfig
from gns3.utils import parse_version
import logging
@@ -416,19 +418,23 @@ def _startListenNotifications(self):
self._notification_stream = None
# Qt websocket before Qt 5.6 doesn't support auth
- if parse_version(QtCore.QT_VERSION_STR) < parse_version("5.6.0") or parse_version(QtCore.PYQT_VERSION_STR) < parse_version("5.6.0"):
+ if parse_version(QtCore.QT_VERSION_STR) < parse_version("5.6.0") or parse_version(QtCore.PYQT_VERSION_STR) < parse_version("5.6.0") or LocalConfig.instance().experimental():
+
self._notification_stream = Controller.instance().createHTTPQuery("GET", "/notifications", self._endListenNotificationCallback,
downloadProgressCallback=self._event_received,
networkManager=self._notification_network_manager,
timeout=None,
showProgress=False,
ignoreErrors=True)
+ url = self._http_client.url() + '/notifications'
+ log.info("Listening for controller notifications on '{}'".format(url))
else:
self._notification_stream = self._http_client.connectWebSocket(self._websocket, "/notifications/ws")
self._notification_stream.textMessageReceived.connect(self._websocket_event_received)
self._notification_stream.error.connect(self._websocket_error)
self._notification_stream.sslErrors.connect(self._sslErrorsSlot)
+ log.info("Listening for controller notifications on '{}'".format(self._notification_stream.requestUrl().toString()))
def stopListenNotifications(self):
if self._notification_stream:
diff --git a/gns3/crash_report.py b/gns3/crash_report.py
index 49fac2206..f3bac1cd2 100644
--- a/gns3/crash_report.py
+++ b/gns3/crash_report.py
@@ -50,7 +50,7 @@ class CrashReport:
Report crash to a third party service
"""
- DSN = "https://4cbe2abf0323ef3136a900d624b12567@o19455.ingest.us.sentry.io/38506"
+ DSN = "https://2f7ebda845810e764bfd049a40cc09e3@o19455.ingest.us.sentry.io/38506"
_instance = None
def __init__(self):
diff --git a/gns3/dialogs/appliance_wizard.py b/gns3/dialogs/appliance_wizard.py
index 8835887fb..aed9c61af 100644
--- a/gns3/dialogs/appliance_wizard.py
+++ b/gns3/dialogs/appliance_wizard.py
@@ -543,9 +543,19 @@ def _importPushButtonClickedSlot(self, *args):
image = Image(self._appliance.template_type(), path, filename=disk["filename"])
try:
if "md5sum" in disk and image.md5sum != disk["md5sum"]:
- reply = QtWidgets.QMessageBox.question(self, "Add appliance",
- "This is not the correct file. The MD5 sum is {} and should be {}.\nDo you want to accept it at your own risks?".format(image.md5sum, disk["md5sum"]),
- QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
+ reply = QtWidgets.QMessageBox.question(
+ self,
+ "Add appliance",
+ "This is not the correct file.\n\n"
+ "MD5 checksum\n"
+ f"actual:\t{image.md5sum}\n"
+ f"expected:\t{disk['md5sum']}\n\n"
+ "File size\n"
+ f"actual:\t{image.filesize} bytes\n"
+ f"expected:\t{disk['filesize']} bytes\n\n"
+ "Do you want to accept it at your own risks?",
+ QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No
+ )
if reply == QtWidgets.QMessageBox.No:
return
except OSError as e:
diff --git a/gns3/link.py b/gns3/link.py
index 4a9354ef0..6c761306c 100644
--- a/gns3/link.py
+++ b/gns3/link.py
@@ -23,7 +23,7 @@
from .qt import sip
import uuid
-from .qt import QtCore
+from .qt import QtCore, QtNetwork
from .controller import Controller
@@ -78,6 +78,7 @@ def __init__(self, source_node, source_port, destination_node, destination_port,
self._deleting = False
self._capture_file_path = None
self._capture_file = None
+ self._network_manager = None
self._response_stream = None
self._capture_compute_id = None
self._initialized = False
@@ -117,12 +118,15 @@ def _parseResponse(self, result):
else:
self._capture_file = QtCore.QFile(self._capture_file_path)
self._capture_file.open(QtCore.QFile.WriteOnly)
+ if self._network_manager is None:
+ self._network_manager = QtNetwork.QNetworkAccessManager(self)
self._response_stream = Controller.instance().get("/projects/{project_id}/links/{link_id}/pcap".format(project_id=self.project().id(), link_id=self._link_id),
None,
showProgress=False,
downloadProgressCallback=self._downloadPcapProgress,
ignoreErrors=True, # If something is wrong avoid disconnect us from server
- timeout=None)
+ timeout=None,
+ networkManager=self._network_manager)
log.debug("Has successfully started capturing packets on link {} to '{}'".format(self._link_id, self._capture_file_path))
else:
self._response_stream = None
diff --git a/gns3/local_config.py b/gns3/local_config.py
index 8b9da10c5..9c5102013 100644
--- a/gns3/local_config.py
+++ b/gns3/local_config.py
@@ -483,7 +483,7 @@ def isMainGui():
if os.path.exists(pid_path):
try:
- with open(pid_path) as f:
+ with open(pid_path, encoding="utf-8") as f:
pid = int(f.read())
if pid != my_pid:
try:
@@ -498,9 +498,17 @@ def isMainGui():
return False
else:
return True
- except (OSError, ValueError) as e:
+ except OSError as e:
log.critical("Can't read pid file %s: %s", pid_path, str(e))
return False
+ except ValueError as e:
+ log.warning("Invalid data in pid file %s: %s", pid_path, str(e))
+ try:
+ # try removing the file since it contains invalid data
+ os.remove(pid_path)
+ except OSError:
+ log.critical("Can't remove pid file %s", pid_path)
+ return False
try:
with open(pid_path, 'w+') as f:
diff --git a/gns3/modules/docker/docker_vm.py b/gns3/modules/docker/docker_vm.py
index 525a55bd0..8b31b9e55 100644
--- a/gns3/modules/docker/docker_vm.py
+++ b/gns3/modules/docker/docker_vm.py
@@ -42,6 +42,7 @@ def __init__(self, module, server, project):
docker_vm_settings = {"image": "",
"usage": "",
"adapters": DOCKER_CONTAINER_SETTINGS["adapters"],
+ "mac_address": DOCKER_CONTAINER_SETTINGS["mac_address"],
"custom_adapters": DOCKER_CONTAINER_SETTINGS["custom_adapters"],
"start_command": DOCKER_CONTAINER_SETTINGS["start_command"],
"environment": DOCKER_CONTAINER_SETTINGS["environment"],
@@ -88,6 +89,9 @@ def info(self):
port_name=port.name(),
port_description=port.description())
+ if port.macAddress():
+ port_info += " MAC address is {mac_address}\n".format(mac_address=port.macAddress())
+
usage = "\n" + self._settings.get("usage")
return info + port_info + usage
diff --git a/gns3/modules/docker/pages/docker_vm_configuration_page.py b/gns3/modules/docker/pages/docker_vm_configuration_page.py
index 76b29e619..9de063d3c 100644
--- a/gns3/modules/docker/pages/docker_vm_configuration_page.py
+++ b/gns3/modules/docker/pages/docker_vm_configuration_page.py
@@ -19,6 +19,8 @@
Configuration page for Docker images.
"""
+import re
+
from gns3.qt import QtWidgets
from gns3.node import Node
from gns3.dialogs.custom_adapters_configuration_dialog import CustomAdaptersConfigurationDialog
@@ -69,15 +71,25 @@ def _customAdaptersConfigurationSlot(self):
if self._node:
adapters = self._settings["adapters"]
+ base_mac_address = self._settings["mac_address"]
else:
adapters = self.uiAdapterSpinBox.value()
+ mac = self.uiMacAddrLineEdit.text()
+ if mac != ":::::":
+ if not re.search(r"""^([0-9a-fA-F]{2}[:]){5}[0-9a-fA-F]{2}$""", mac):
+ QtWidgets.QMessageBox.critical(self, "MAC address", "Invalid MAC address (format required: hh:hh:hh:hh:hh:hh)")
+ return
+ else:
+ base_mac_address = mac
+ else:
+ base_mac_address = ""
ports = []
for adapter_number in range(0, adapters):
port_name = "eth{}".format(adapter_number)
ports.append(port_name)
- dialog = CustomAdaptersConfigurationDialog(ports, self._custom_adapters, parent=self)
+ dialog = CustomAdaptersConfigurationDialog(ports, self._custom_adapters, "TAP", {"TAP": "Default"}, base_mac_address, parent=self)
dialog.show()
dialog.exec_()
@@ -150,6 +162,13 @@ def loadSettings(self, settings, node=None, group=False):
self.uiSymbolLineEdit.hide()
self.uiSymbolToolButton.hide()
+ # load the MAC address setting
+ self.uiMacAddrLineEdit.setInputMask("HH:HH:HH:HH:HH:HH;_")
+ if settings["mac_address"]:
+ self.uiMacAddrLineEdit.setText(settings["mac_address"])
+ else:
+ self.uiMacAddrLineEdit.clear()
+
self.uiUsageTextEdit.setPlainText(settings["usage"])
def _networkConfigEditSlot(self):
@@ -199,6 +218,18 @@ def saveSettings(self, settings, node=None, group=False):
else:
settings["name"] = name
+ # check and save the MAC address
+ mac = self.uiMacAddrLineEdit.text()
+ if mac != ":::::":
+ if not re.search(r"""^([0-9a-fA-F]{2}[:]){5}[0-9a-fA-F]{2}$""", mac):
+ QtWidgets.QMessageBox.critical(self, "MAC address", "Invalid MAC address (format required: hh:hh:hh:hh:hh:hh)")
+ if node:
+ raise ConfigurationError()
+ else:
+ settings["mac_address"] = mac
+ else:
+ settings["mac_address"] = None
+
if not node:
# these are template settings
settings["category"] = self.uiCategoryComboBox.itemData(self.uiCategoryComboBox.currentIndex())
diff --git a/gns3/modules/docker/settings.py b/gns3/modules/docker/settings.py
index 0ae3c8ac2..255b5031f 100644
--- a/gns3/modules/docker/settings.py
+++ b/gns3/modules/docker/settings.py
@@ -35,6 +35,7 @@
"name": "",
"image": "",
"adapters": 1,
+ "mac_address": "",
"custom_adapters": [],
"environment": "",
"console_type": "telnet",
diff --git a/gns3/modules/docker/ui/docker_vm_configuration_page.ui b/gns3/modules/docker/ui/docker_vm_configuration_page.ui
index 32f0ab347..251c4e530 100644
--- a/gns3/modules/docker/ui/docker_vm_configuration_page.ui
+++ b/gns3/modules/docker/ui/docker_vm_configuration_page.ui
@@ -6,8 +6,8 @@
0
0
- 938
- 872
+ 504
+ 560
@@ -103,27 +103,37 @@
-
+
+
+ Base MAC:
+
+
+
+ -
+
+
+ -
Custom adapters:
- -
+
-
&Configure custom adapters
- -
+
-
Console type:
- -
+
-
QLayout::SetNoConstraint
@@ -166,14 +176,14 @@
- -
+
-
VNC console resolution:
- -
+
-
-
@@ -227,14 +237,14 @@
- -
+
-
HTTP port in the container:
- -
+
-
1
@@ -244,17 +254,17 @@
- -
+
-
HTTP path:
- -
+
-
- -
+
-
Environment variables:
@@ -268,17 +278,17 @@
- -
+
-
- -
+
-
Network configuration
- -
+
-
Edit
diff --git a/gns3/modules/docker/ui/docker_vm_configuration_page_ui.py b/gns3/modules/docker/ui/docker_vm_configuration_page_ui.py
index 772194205..55ab108b1 100644
--- a/gns3/modules/docker/ui/docker_vm_configuration_page_ui.py
+++ b/gns3/modules/docker/ui/docker_vm_configuration_page_ui.py
@@ -2,7 +2,7 @@
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/modules/docker/ui/docker_vm_configuration_page.ui'
#
-# Created by: PyQt5 UI code generator 5.15.7
+# Created by: PyQt5 UI code generator 5.15.6
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
@@ -14,7 +14,7 @@
class Ui_dockerVMConfigPageWidget(object):
def setupUi(self, dockerVMConfigPageWidget):
dockerVMConfigPageWidget.setObjectName("dockerVMConfigPageWidget")
- dockerVMConfigPageWidget.resize(938, 872)
+ dockerVMConfigPageWidget.resize(504, 560)
self.verticalLayout = QtWidgets.QVBoxLayout(dockerVMConfigPageWidget)
self.verticalLayout.setObjectName("verticalLayout")
self.uiTabWidget = QtWidgets.QTabWidget(dockerVMConfigPageWidget)
@@ -67,15 +67,21 @@ def setupUi(self, dockerVMConfigPageWidget):
self.uiAdapterSpinBox.setMinimum(1)
self.uiAdapterSpinBox.setObjectName("uiAdapterSpinBox")
self.gridLayout.addWidget(self.uiAdapterSpinBox, 5, 1, 1, 1)
+ self.uiMacAddrLabel = QtWidgets.QLabel(self.tab)
+ self.uiMacAddrLabel.setObjectName("uiMacAddrLabel")
+ self.gridLayout.addWidget(self.uiMacAddrLabel, 6, 0, 1, 1)
+ self.uiMacAddrLineEdit = QtWidgets.QLineEdit(self.tab)
+ self.uiMacAddrLineEdit.setObjectName("uiMacAddrLineEdit")
+ self.gridLayout.addWidget(self.uiMacAddrLineEdit, 6, 1, 1, 1)
self.uiCustomAdaptersLabel = QtWidgets.QLabel(self.tab)
self.uiCustomAdaptersLabel.setObjectName("uiCustomAdaptersLabel")
- self.gridLayout.addWidget(self.uiCustomAdaptersLabel, 6, 0, 1, 1)
+ self.gridLayout.addWidget(self.uiCustomAdaptersLabel, 7, 0, 1, 1)
self.uiCustomAdaptersConfigurationPushButton = QtWidgets.QPushButton(self.tab)
self.uiCustomAdaptersConfigurationPushButton.setObjectName("uiCustomAdaptersConfigurationPushButton")
- self.gridLayout.addWidget(self.uiCustomAdaptersConfigurationPushButton, 6, 1, 1, 1)
+ self.gridLayout.addWidget(self.uiCustomAdaptersConfigurationPushButton, 7, 1, 1, 1)
self.uiConsoleTypeLabel = QtWidgets.QLabel(self.tab)
self.uiConsoleTypeLabel.setObjectName("uiConsoleTypeLabel")
- self.gridLayout.addWidget(self.uiConsoleTypeLabel, 7, 0, 1, 1)
+ self.gridLayout.addWidget(self.uiConsoleTypeLabel, 8, 0, 1, 1)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setSizeConstraint(QtWidgets.QLayout.SetNoConstraint)
self.horizontalLayout.setObjectName("horizontalLayout")
@@ -90,10 +96,10 @@ def setupUi(self, dockerVMConfigPageWidget):
self.uiConsoleAutoStartCheckBox = QtWidgets.QCheckBox(self.tab)
self.uiConsoleAutoStartCheckBox.setObjectName("uiConsoleAutoStartCheckBox")
self.horizontalLayout.addWidget(self.uiConsoleAutoStartCheckBox)
- self.gridLayout.addLayout(self.horizontalLayout, 7, 1, 1, 1)
+ self.gridLayout.addLayout(self.horizontalLayout, 8, 1, 1, 1)
self.uiConsoleResolutionLabel = QtWidgets.QLabel(self.tab)
self.uiConsoleResolutionLabel.setObjectName("uiConsoleResolutionLabel")
- self.gridLayout.addWidget(self.uiConsoleResolutionLabel, 8, 0, 1, 1)
+ self.gridLayout.addWidget(self.uiConsoleResolutionLabel, 9, 0, 1, 1)
self.uiConsoleResolutionComboBox = QtWidgets.QComboBox(self.tab)
self.uiConsoleResolutionComboBox.setObjectName("uiConsoleResolutionComboBox")
self.uiConsoleResolutionComboBox.addItem("")
@@ -106,35 +112,35 @@ def setupUi(self, dockerVMConfigPageWidget):
self.uiConsoleResolutionComboBox.addItem("")
self.uiConsoleResolutionComboBox.addItem("")
self.uiConsoleResolutionComboBox.addItem("")
- self.gridLayout.addWidget(self.uiConsoleResolutionComboBox, 8, 1, 1, 1)
+ self.gridLayout.addWidget(self.uiConsoleResolutionComboBox, 9, 1, 1, 1)
self.label = QtWidgets.QLabel(self.tab)
self.label.setObjectName("label")
- self.gridLayout.addWidget(self.label, 9, 0, 1, 1)
+ self.gridLayout.addWidget(self.label, 10, 0, 1, 1)
self.uiConsoleHttpPortSpinBox = QtWidgets.QSpinBox(self.tab)
self.uiConsoleHttpPortSpinBox.setMinimum(1)
self.uiConsoleHttpPortSpinBox.setMaximum(65535)
self.uiConsoleHttpPortSpinBox.setObjectName("uiConsoleHttpPortSpinBox")
- self.gridLayout.addWidget(self.uiConsoleHttpPortSpinBox, 9, 1, 1, 1)
+ self.gridLayout.addWidget(self.uiConsoleHttpPortSpinBox, 10, 1, 1, 1)
self.label_2 = QtWidgets.QLabel(self.tab)
self.label_2.setObjectName("label_2")
- self.gridLayout.addWidget(self.label_2, 10, 0, 1, 1)
+ self.gridLayout.addWidget(self.label_2, 11, 0, 1, 1)
self.uiHttpConsolePathLineEdit = QtWidgets.QLineEdit(self.tab)
self.uiHttpConsolePathLineEdit.setObjectName("uiHttpConsolePathLineEdit")
- self.gridLayout.addWidget(self.uiHttpConsolePathLineEdit, 10, 1, 1, 1)
+ self.gridLayout.addWidget(self.uiHttpConsolePathLineEdit, 11, 1, 1, 1)
self.uiEnvironmentLabel = QtWidgets.QLabel(self.tab)
self.uiEnvironmentLabel.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
self.uiEnvironmentLabel.setWordWrap(False)
self.uiEnvironmentLabel.setObjectName("uiEnvironmentLabel")
- self.gridLayout.addWidget(self.uiEnvironmentLabel, 11, 0, 1, 1)
+ self.gridLayout.addWidget(self.uiEnvironmentLabel, 12, 0, 1, 1)
self.uiEnvironmentTextEdit = QtWidgets.QTextEdit(self.tab)
self.uiEnvironmentTextEdit.setObjectName("uiEnvironmentTextEdit")
- self.gridLayout.addWidget(self.uiEnvironmentTextEdit, 11, 1, 1, 1)
+ self.gridLayout.addWidget(self.uiEnvironmentTextEdit, 12, 1, 1, 1)
self.uiNetworkConfigLabel = QtWidgets.QLabel(self.tab)
self.uiNetworkConfigLabel.setObjectName("uiNetworkConfigLabel")
- self.gridLayout.addWidget(self.uiNetworkConfigLabel, 12, 0, 1, 1)
+ self.gridLayout.addWidget(self.uiNetworkConfigLabel, 13, 0, 1, 1)
self.uiNetworkConfigEditButton = QtWidgets.QPushButton(self.tab)
self.uiNetworkConfigEditButton.setObjectName("uiNetworkConfigEditButton")
- self.gridLayout.addWidget(self.uiNetworkConfigEditButton, 12, 1, 1, 1)
+ self.gridLayout.addWidget(self.uiNetworkConfigEditButton, 13, 1, 1, 1)
self.uiTabWidget.addTab(self.tab, "")
self.tab_2 = QtWidgets.QWidget()
self.tab_2.setObjectName("tab_2")
@@ -186,6 +192,7 @@ def retranslateUi(self, dockerVMConfigPageWidget):
self.uiSymbolToolButton.setText(_translate("dockerVMConfigPageWidget", "&Browse..."))
self.uiCMDLabel.setText(_translate("dockerVMConfigPageWidget", "Start command:"))
self.uiAdapterLabel.setText(_translate("dockerVMConfigPageWidget", "Adapters:"))
+ self.uiMacAddrLabel.setText(_translate("dockerVMConfigPageWidget", "Base MAC:"))
self.uiCustomAdaptersLabel.setText(_translate("dockerVMConfigPageWidget", "Custom adapters:"))
self.uiCustomAdaptersConfigurationPushButton.setText(_translate("dockerVMConfigPageWidget", "&Configure custom adapters"))
self.uiConsoleTypeLabel.setText(_translate("dockerVMConfigPageWidget", "Console type:"))
diff --git a/gns3/project.py b/gns3/project.py
index aa3f602e0..25030836c 100644
--- a/gns3/project.py
+++ b/gns3/project.py
@@ -627,21 +627,24 @@ def _startListenNotifications(self):
return
# Qt websocket before Qt 5.6 doesn't support auth
- if parse_version(QtCore.QT_VERSION_STR) < parse_version("5.6.0") or parse_version(QtCore.PYQT_VERSION_STR) < parse_version("5.6.0"):
+ if parse_version(QtCore.QT_VERSION_STR) < parse_version("5.6.0") or parse_version(QtCore.PYQT_VERSION_STR) < parse_version("5.6.0") or LocalConfig.instance().experimental():
path = "/projects/{project_id}/notifications".format(project_id=self._id)
self._notification_stream = Controller.instance().createHTTPQuery("GET", path, self._endListenNotificationCallback,
- downloadProgressCallback=self._event_received,
- networkManager=self._notification_network_manager,
- timeout=None,
- showProgress=False,
- ignoreErrors=True)
+ downloadProgressCallback=self._event_received,
+ networkManager=self._notification_network_manager,
+ timeout=None,
+ showProgress=False,
+ ignoreErrors=True)
+ url = Controller.instance().getHttpClient().url() + path
+ log.info("Listening for project notifications on '{}'".format(url))
else:
- path = "/projects/{project_id}/notifications/ws".format(project_id=self._id)
- self._notification_stream = Controller.instance().httpClient().connectWebSocket(self._websocket, path)
- self._notification_stream.textMessageReceived.connect(self._websocket_event_received)
- self._notification_stream.error.connect(self._websocket_error)
- self._notification_stream.sslErrors.connect(self._sslErrorsSlot)
+ path = "/projects/{project_id}/notifications/ws".format(project_id=self._id)
+ self._notification_stream = Controller.instance().httpClient().connectWebSocket(self._websocket, path)
+ self._notification_stream.textMessageReceived.connect(self._websocket_event_received)
+ self._notification_stream.error.connect(self._websocket_error)
+ self._notification_stream.sslErrors.connect(self._sslErrorsSlot)
+ log.info("Listening for project notifications on '{}'".format(self._notification_stream.requestUrl().toString()))
def _endListenNotificationCallback(self, result, error=False, **kwargs):
"""
diff --git a/gns3/version.py b/gns3/version.py
index 49bb63f8c..4272b47c0 100644
--- a/gns3/version.py
+++ b/gns3/version.py
@@ -23,8 +23,8 @@
# or negative for a release candidate or beta (after the base version
# number has been incremented)
-__version__ = "2.2.49"
-__version_info__ = (2, 2, 49, 0)
+__version__ = "2.2.50"
+__version_info__ = (2, 2, 50, 0)
if "dev" in __version__:
try:
diff --git a/requirements.txt b/requirements.txt
index 4ef10c35a..71dd3a53e 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,5 +1,5 @@
jsonschema>=4.23,<4.24
-sentry-sdk==2.12,<2.13
+sentry-sdk==2.12,<2.13 # optional dependency
psutil==6.0.0
distro>=1.9.0
truststore>=0.9.1; python_version >= '3.10'