Skip to content

Commit

Permalink
Merge pull request #3509 from GNS3/release-v2.2.43
Browse files Browse the repository at this point in the history
Release v2.2.43
  • Loading branch information
grossmj authored Sep 19, 2023
2 parents 7512ffe + 97b777c commit a989026
Show file tree
Hide file tree
Showing 37 changed files with 1,690 additions and 193 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Change Log

## 2.2.43 19/09/2023

* Add KiTTY to preconfigured telnet consoles. Fixes #3507
* Fix generic icon in Wayland. Ref #3501
* Support for appliance format version 8.
* Use importlib instead of pkg_resources
* Upgrade to PyQt 5.15.9 and pywin32
* Add support for appliance version 8 format

## 2.2.42 09/08/2023

* Use the system's certificate store for SSL connections
Expand Down
3 changes: 3 additions & 0 deletions gns3/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ def __init__(self, argv, hdpi=True):

super().__init__(argv)

# this is tell Wayland what is the name of the desktop file (gns3.desktop)
self.setDesktopFileName("gns3")

# this info is necessary for QSettings
self.setOrganizationName("GNS3")
self.setOrganizationDomain("gns3.net")
Expand Down
2 changes: 1 addition & 1 deletion gns3/crash_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class CrashReport:
Report crash to a third party service
"""

DSN = "https://bae0411a1718612ee8c25cdb12ec7f02@o19455.ingest.sentry.io/38506"
DSN = "https://57454675a266a9d705fd505947a81b5c@o19455.ingest.sentry.io/38506"
_instance = None

def __init__(self):
Expand Down
103 changes: 68 additions & 35 deletions gns3/dialogs/appliance_wizard.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,11 @@ def __init__(self, parent, path):
self.setWindowTitle("Install {} appliance".format(self._appliance["name"]))

# add a custom button to show appliance information
self.setButtonText(QtWidgets.QWizard.CustomButton1, "&Appliance info")
self.setOption(QtWidgets.QWizard.HaveCustomButton1, True)
self.customButtonClicked.connect(self._showApplianceInfoSlot)
if self._appliance["registry_version"] < 8:
# FIXME: show appliance info for v8
self.setButtonText(QtWidgets.QWizard.CustomButton1, "&Appliance info")
self.setOption(QtWidgets.QWizard.HaveCustomButton1, True)
self.customButtonClicked.connect(self._showApplianceInfoSlot)

# customize the server selection
self.uiRemoteRadioButton.toggled.connect(self._remoteServerToggledSlot)
Expand Down Expand Up @@ -144,18 +146,9 @@ def initializePage(self, page_id):
if self.page(page_id) == self.uiServerWizardPage:

Controller.instance().getSymbols(self._getSymbolsCallback)

if "qemu" in self._appliance:
emulator_type = "qemu"
elif "iou" in self._appliance:
emulator_type = "iou"
elif "docker" in self._appliance:
emulator_type = "docker"
elif "dynamips" in self._appliance:
emulator_type = "dynamips"
else:
QtWidgets.QMessageBox.warning(self, "Appliance", "Could not determine the emulator type")

template_type = self._appliance.template_type()
if not template_type:
raise ApplianceError("No template type found for appliance {}".format(self._appliance["name"]))
is_mac = ComputeManager.instance().localPlatform().startswith("darwin")
is_win = ComputeManager.instance().localPlatform().startswith("win")

Expand All @@ -173,11 +166,11 @@ def initializePage(self, page_id):
if ComputeManager.instance().localPlatform() is None:
self.uiLocalRadioButton.setEnabled(False)
elif is_mac or is_win:
if emulator_type == "qemu":
if template_type == "qemu":
# disallow usage of the local server because Qemu has issues on OSX and Windows
if not LocalConfig.instance().experimental():
self.uiLocalRadioButton.setEnabled(False)
elif emulator_type != "dynamips":
elif template_type != "dynamips":
self.uiLocalRadioButton.setEnabled(False)

if ComputeManager.instance().vmCompute():
Expand All @@ -195,27 +188,55 @@ def initializePage(self, page_id):

elif self.page(page_id) == self.uiFilesWizardPage:
if Controller.instance().isRemote() or self._compute_id != "local":
self._registry.getRemoteImageList(self._appliance.emulator(), self._compute_id)
self._registry.getRemoteImageList(self._appliance.template_type(), self._compute_id)
else:
self.images_changed_signal.emit()

elif self.page(page_id) == self.uiQemuWizardPage:
if self._appliance['qemu'].get('kvm', 'require') == 'require':
if self._appliance.template_properties().get('kvm', 'require') == 'require':
self._server_check = False
Qemu.instance().getQemuCapabilitiesFromServer(self._compute_id, qpartial(self._qemuServerCapabilitiesCallback))
else:
self._server_check = True
Qemu.instance().getQemuBinariesFromServer(self._compute_id, qpartial(self._getQemuBinariesFromServerCallback), [self._appliance["qemu"]["arch"]])
if self._appliance["registry_version"] >= 8:
qemu_platform = self._appliance.template_properties()["platform"]
else:
qemu_platform = self._appliance.template_properties()["arch"]
Qemu.instance().getQemuBinariesFromServer(self._compute_id, qpartial(self._getQemuBinariesFromServerCallback), [qemu_platform])

elif self.page(page_id) == self.uiInstructionsPage:

installation_instructions = self._appliance.get("installation_instructions", "No installation instructions available")
self.uiInstructionsTextEdit.setText(installation_instructions.strip())

elif self.page(page_id) == self.uiUsageWizardPage:
self.uiUsageTextEdit.setText("The template will be available in the {} category.\n\n{}".format(self._appliance["category"].replace("_", " "), self._appliance.get("usage", "")))
# TODO: allow taking these info fields at the version level in v8
category = self._appliance["category"].replace("_", " ")
usage = self._appliance.get("usage", "No usage information available")
if self._appliance["registry_version"] >= 8:
default_username = self._appliance.get("default_username")
default_password = self._appliance.get("default_password")
if default_username and default_password:
usage += "\n\nDefault username: {}\nDefault password: {}".format(default_username, default_password)

usage_info = """
The template will be available in the {} category.
Usage: {}
""".format(category, usage)

self.uiUsageTextEdit.setText(usage_info.strip())

def _qemuServerCapabilitiesCallback(self, result, error=None, *args, **kwargs):
"""
Check if the server supports KVM or not
"""

if error is None and "kvm" in result and self._appliance["qemu"]["arch"] in result["kvm"]:
if self._appliance["registry_version"] >= 8:
qemu_platform = self._appliance.template_properties()["platform"]
else:
qemu_platform = self._appliance.template_properties()["arch"]
if error is None and "kvm" in result and qemu_platform in result["kvm"]:
self._server_check = True
else:
if error:
Expand All @@ -236,7 +257,7 @@ def _imageUploadedCallback(self, result, error=False, context=None, **kwargs):
log.error("Error while uploading image '{}': {}".format(image_path, result["message"]))
else:
log.info("Image '{}' has been successfully uploaded".format(image_path))
self._registry.getRemoteImageList(self._appliance.emulator(), self._compute_id)
self._registry.getRemoteImageList(self._appliance.template_type(), self._compute_id)

def _showApplianceInfoSlot(self):
"""
Expand Down Expand Up @@ -407,7 +428,7 @@ def _refreshDialogWorker(self):

for version in self._appliance["versions"]:
for image in version["images"].values():
img = self._registry.search_image_file(self._appliance.emulator(),
img = self._registry.search_image_file(self._appliance.template_type(),
image["filename"],
image.get("md5sum"),
image.get("filesize"),
Expand Down Expand Up @@ -519,7 +540,7 @@ def _importPushButtonClickedSlot(self, *args):
if len(path) == 0:
return

image = Image(self._appliance.emulator(), path, filename=disk["filename"])
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",
Expand Down Expand Up @@ -554,7 +575,11 @@ def _getQemuBinariesFromServerCallback(self, result, error=False, **kwargs):
if self.uiQemuListComboBox.count() == 1:
self.next()
else:
i = self.uiQemuListComboBox.findData(self._appliance["qemu"]["arch"], flags=QtCore.Qt.MatchEndsWith)
if self._appliance["registry_version"] >= 8:
qemu_platform = self._appliance.template_properties()["platform"]
else:
qemu_platform = self._appliance.template_properties()["arch"]
i = self.uiQemuListComboBox.findData(qemu_platform, flags=QtCore.Qt.MatchEndsWith)
if i != -1:
self.uiQemuListComboBox.setCurrentIndex(i)

Expand All @@ -567,8 +592,8 @@ def _install(self, version):

if version is None:
appliance_configuration = self._appliance.copy()
if "docker" not in appliance_configuration:
# only Docker do not have version
if self._appliance.template_type() != "docker":
# only Docker do not have versions
return False
else:
try:
Expand All @@ -585,10 +610,15 @@ def _install(self, version):
return False
appliance_configuration["name"] = appliance_configuration["name"].strip()

if "qemu" in appliance_configuration:
if self._appliance["registry_version"] >= 8:
if "settings" in appliance_configuration:
for settings in appliance_configuration["settings"]:
if settings["template_type"] == "qemu":
settings["template_properties"]["path"] = self.uiQemuListComboBox.currentData()
elif "qemu" in appliance_configuration:
appliance_configuration["qemu"]["path"] = self.uiQemuListComboBox.currentData()

new_template = ApplianceToTemplate().new_template(appliance_configuration, self._compute_id, self._symbols, parent=self)
new_template = ApplianceToTemplate().new_template(appliance_configuration, self._compute_id, version, self._symbols, parent=self)
TemplateManager.instance().createTemplate(Template(new_template), callback=self._templateCreatedCallback)
return False

Expand Down Expand Up @@ -632,7 +662,7 @@ def _uploadImages(self, name, version):
if not Controller.instance().isRemote() and self._compute_id == "local" and image["path"].startswith(ImageManager.instance().getDirectory()):
log.debug("{} is already on the local server".format(image["path"]))
return
image = Image(self._appliance.emulator(), image["path"], filename=image["filename"])
image = Image(self._appliance.template_type(), image["path"], filename=image["filename"])
image_upload_manager = ImageUploadManager(image, Controller.instance(), self._compute_id, self._applianceImageUploadedCallback, LocalConfig.instance().directFileUpload())
image_upload_manager.upload()
self._image_uploading_count += 1
Expand All @@ -649,12 +679,16 @@ def _applianceImageUploadedCallback(self, result, error=False, context=None, **k

def nextId(self):
if self.currentPage() == self.uiServerWizardPage:
if "docker" in self._appliance:
if self._appliance.template_type() == "docker":
# skip Qemu binary selection and files pages if this is a Docker appliance
return super().nextId() + 2
elif "qemu" not in self._appliance:
return super().nextId() + 3
elif self._appliance.template_type() != "qemu":
# skip the Qemu binary selection page if not a Qemu appliance
return super().nextId() + 1
if self.currentPage() == self.uiQemuWizardPage:
if not self._appliance.get("installation_instructions"):
# skip the installation instructions page if there are no instructions
return super().nextId() + 1
return super().nextId()

def validateCurrentPage(self):
Expand Down Expand Up @@ -722,7 +756,6 @@ def validateCurrentPage(self):

elif self.currentPage() == self.uiQemuWizardPage:
# validate the Qemu

if self._server_check is False:
QtWidgets.QMessageBox.critical(self, "Checking for KVM support", "Please wait for the server to reply...")
return False
Expand Down
10 changes: 0 additions & 10 deletions gns3/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,6 @@
except Exception as e:
print("Fail update installation: {}".format(str(e)))


# WARNING
# Due to buggy user machines we choose to put this as the first loading modules
# otherwise the egg cache is initialized in his standard location and
# if is not writetable the application crash. It's the user fault
# because one day the user as used sudo to run an egg and break his
# filesystem permissions, but it's a common mistake.
from gns3.utils.get_resource import get_resource


import datetime
import traceback
import time
Expand Down
1 change: 1 addition & 0 deletions gns3/modules/iou/pages/iou_device_configuration_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ def __init__(self):
self.uiPrivateConfigToolButton.hide()

# location of the base config templates
# FIXME: this does not work
self._base_iou_l2_config_template = get_resource(os.path.join("configs", "iou_l2_base_startup-config.txt"))
self._base_iou_l3_config_template = get_resource(os.path.join("configs", "iou_l3_base_startup-config.txt"))
self._default_configs_dir = LocalServer.instance().localServerSettings()["configs_path"]
Expand Down
Loading

0 comments on commit a989026

Please sign in to comment.