diff --git a/patches/gns3-gui/patch_gns3gui_linkcolors.patch b/patches/gns3-gui/patch_gns3gui_linkcolors.patch new file mode 100644 index 0000000..2679cba --- /dev/null +++ b/patches/gns3-gui/patch_gns3gui_linkcolors.patch @@ -0,0 +1,266 @@ +Author: Pekka Helenius +Date: Sun, 6 Jun 2021 15:15:14 +0200 +Subject: [PATCH] GNS3 GUI: Link colors. + +Add link color support for GNS3 GUI. + +--- a/gns3/items/serial_link_item.py ++++ b/gns3/items/serial_link_item.py +@@ -50,10 +50,16 @@ + + LinkItem.adjust(self) + +- if self._hovered: +- self.setPen(QtGui.QPen(QtCore.Qt.red, self._pen_width + 1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)) +- else: +- self.setPen(QtGui.QPen(QtCore.Qt.darkRed, self._pen_width, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)) ++ try: ++ if self._hovered: ++ self.setPen(QtGui.QPen(QtCore.Qt.red, self._link._link_style["width"] + 1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)) ++ else: ++ self.setPen(QtGui.QPen(QtGui.QColor(self._link._link_style["color"]), self._link._link_style["width"], self._link._link_style["type"], QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)) ++ except: ++ if self._hovered: ++ self.setPen(QtGui.QPen(QtCore.Qt.red, self._pen_width + 1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)) ++ else: ++ self.setPen(QtGui.QPen(QtCore.Qt.darkRed, self._pen_width, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)) + + # get source to destination angle + vector_angle = math.atan2(self.dy, self.dx) +--- a/gns3/items/link_item.py ++++ b/gns3/items/link_item.py +@@ -25,6 +25,7 @@ + + from ..packet_capture import PacketCapture + from ..dialogs.filter_dialog import FilterDialog ++from ..dialogs.style_editor_dialog_link import StyleEditorDialogLink + from ..utils.get_icon import get_icon + + +@@ -40,7 +41,6 @@ + self.parentItem().mousePressEvent(event) + event.accept() + +- + class LinkItem(QtWidgets.QGraphicsPathItem): + + """ +@@ -137,6 +137,21 @@ + def _suspendActionSlot(self, *args): + self._link.toggleSuspend() + ++ @qslot ++ def _styleActionSlot(self, *args): ++ style_dialog = StyleEditorDialogLink(self, self._main_window) ++ style_dialog.show() ++ style_dialog.exec_() ++ ++ def setLinkStyle(self, link_style): ++ self._link._link_style["color"] = link_style["color"] ++ self._link._link_style["width"] = link_style["width"] ++ self._link._link_style["type"] = link_style["type"] ++ ++ # This refers to functions in link.py! ++ self._link.setLinkStyle(link_style) ++ self._link.update() ++ + def delete(self): + """ + Delete this link +@@ -266,6 +281,12 @@ + resume_action.triggered.connect(self._suspendActionSlot) + menu.addAction(resume_action) + ++ # style ++ style_action = QtWidgets.QAction("Style", menu) ++ style_action.setIcon(get_icon("node_conception.svg")) ++ style_action.triggered.connect(self._styleActionSlot) ++ menu.addAction(style_action) ++ + # delete + delete_action = QtWidgets.QAction("Delete", menu) + delete_action.setIcon(get_icon('delete.svg')) +--- a/gns3/items/ethernet_link_item.py ++++ b/gns3/items/ethernet_link_item.py +@@ -1,5 +1,6 @@ + # -*- coding: utf-8 -*- + # ++# Copyright (C) 2019 Pekka Helenius + # Copyright (C) 2014 GNS3 Technologies Inc. + # + # This program is free software: you can redistribute it and/or modify +@@ -51,10 +52,16 @@ + + LinkItem.adjust(self) + +- if self._hovered: +- self.setPen(QtGui.QPen(QtCore.Qt.red, self._pen_width + 1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)) +- else: +- self.setPen(QtGui.QPen(QtCore.Qt.black, self._pen_width, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)) ++ try: ++ if self._hovered: ++ self.setPen(QtGui.QPen(QtCore.Qt.red, self._link._link_style["width"] + 1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)) ++ else: ++ self.setPen(QtGui.QPen(QtGui.QColor(self._link._link_style["color"]), self._link._link_style["width"], self._link._link_style["type"], QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)) ++ except: ++ if self._hovered: ++ self.setPen(QtGui.QPen(QtCore.Qt.red, self._pen_width + 1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)) ++ else: ++ self.setPen(QtGui.QPen(QtGui.QColor("#000000"), self._pen_width, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)) + + # draw a line between nodes + path = QtGui.QPainterPath(self.source) +--- /dev/null 2019-05-22 14:27:07.219999993 +0300 ++++ b/gns3/dialogs/style_editor_dialog_link.py +@@ -0,0 +1,112 @@ ++# -*- coding: utf-8 -*- ++# ++# Copyright (C) 2019 Pekka Helenius ++# Copyright (C) 2014 GNS3 Technologies Inc. ++# ++# This program is free software: you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation, either version 3 of the License, or ++# (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see . ++ ++""" ++Style editor to edit Link items. ++""" ++ ++from ..qt import QtCore, QtWidgets, QtGui ++from ..ui.style_editor_dialog_ui import Ui_StyleEditorDialog ++ ++ ++class StyleEditorDialogLink(QtWidgets.QDialog, Ui_StyleEditorDialog): ++ ++ """ ++ Style editor dialog. ++ ++ :param parent: parent widget ++ :param link: selected link ++ """ ++ ++ def __init__(self, link, parent): ++ ++ super().__init__(parent) ++ self.setupUi(self) ++ ++ self._link = link ++ self._link_style = {} ++ ++ self.uiBorderColorPushButton.clicked.connect(self._setBorderColorSlot) ++ self.uiButtonBox.button(QtWidgets.QDialogButtonBox.Apply).clicked.connect(self._applyPreferencesSlot) ++ ++ self.uiBorderStyleComboBox.addItem("Solid", QtCore.Qt.SolidLine) ++ self.uiBorderStyleComboBox.addItem("Dash", QtCore.Qt.DashLine) ++ self.uiBorderStyleComboBox.addItem("Dot", QtCore.Qt.DotLine) ++ self.uiBorderStyleComboBox.addItem("Dash Dot", QtCore.Qt.DashDotLine) ++ self.uiBorderStyleComboBox.addItem("Dash Dot Dot", QtCore.Qt.DashDotDotLine) ++ ++ self.uiColorLabel.hide() ++ self.uiColorPushButton.hide() ++ self._color = None ++ ++ self.uiRotationLabel.hide() ++ self.uiRotationSpinBox.hide() ++ ++ pen = link.pen() ++ ++ self._border_color = pen.color() ++ self.uiBorderColorPushButton.setStyleSheet("background-color: rgba({}, {}, {}, {});".format(self._border_color.red(), ++ self._border_color.green(), ++ self._border_color.blue(), ++ self._border_color.alpha())) ++ self.uiBorderWidthSpinBox.setValue(pen.width()) ++ index = self.uiBorderStyleComboBox.findData(pen.style()) ++ if index != -1: ++ self.uiBorderStyleComboBox.setCurrentIndex(index) ++ ++ def _setBorderColorSlot(self): ++ """ ++ Slot to select the border color. ++ """ ++ ++ color = QtWidgets.QColorDialog.getColor(self._border_color, self, "Select Color", QtWidgets.QColorDialog.ShowAlphaChannel) ++ if color.isValid(): ++ self._border_color = color ++ self.uiBorderColorPushButton.setStyleSheet("background-color: rgba({}, {}, {}, {});".format(self._border_color.red(), ++ self._border_color.green(), ++ self._border_color.blue(), ++ self._border_color.alpha())) ++ ++ def _applyPreferencesSlot(self): ++ """ ++ Applies the new style settings. ++ """ ++ ++ border_style = QtCore.Qt.PenStyle(self.uiBorderStyleComboBox.itemData(self.uiBorderStyleComboBox.currentIndex())) ++ pen = QtGui.QPen(self._border_color, self.uiBorderWidthSpinBox.value(), border_style, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) ++ ++ self._link.setPen(pen) ++ ++ new_link_style = {} ++ new_link_style["color"] = self._border_color.name() ++ new_link_style["width"] = self.uiBorderWidthSpinBox.value() ++ new_link_style["type"] = border_style ++ ++ # Store values ++ self._link.setLinkStyle(new_link_style) ++ ++ def done(self, result): ++ """ ++ Called when the dialog is closed. ++ ++ :param result: boolean (accepted or rejected) ++ """ ++ ++ if result: ++ self._applyPreferencesSlot() ++ super().done(result) +--- a/gns3/link.py ++++ b/gns3/link.py +@@ -90,6 +90,8 @@ + + self._nodes = [] + ++ self._link_style = {} ++ + self._source_node.addLink(self) + self._destination_node.addLink(self) + +@@ -127,6 +129,8 @@ + self._updateLabels() + if "filters" in result: + self._filters = result["filters"] ++ if "link_style" in result: ++ self._link_style = result["link_style"] + if "suspend" in result: + self._suspend = result["suspend"] + self.updated_link_signal.emit(self._id) +@@ -209,6 +213,7 @@ + } + ], + "filters": self._filters, ++ "link_style": self._link_style, + "suspend": self._suspend + } + if self._source_port.label(): +@@ -462,3 +467,9 @@ + :params filters: List of filters + """ + self._filters = filters ++ ++ def setLinkStyle(self, link_style): ++ """ ++ :params _link_style: Set link style attributes ++ """ ++ self._link_style = link_style + diff --git a/patches/gns3-gui/patch_gns3gui_qemu-envvars.patch b/patches/gns3-gui/patch_gns3gui_qemu-envvars.patch new file mode 100644 index 0000000..557fca2 --- /dev/null +++ b/patches/gns3-gui/patch_gns3gui_qemu-envvars.patch @@ -0,0 +1,432 @@ +Author: Pekka Helenius +Date: Sun, 6 Jun 2021 15:10:45 +0200 +Subject: [PATCH] GNS3 GUI: QEMU environment variables. + +Add support for QEMU environment variables for GNS3 GUI. + +--- a/tests/test_local_config.py ++++ b/tests/test_local_config.py +@@ -285,6 +285,7 @@ + "enable_kvm": True, + "vms": [ + { ++ "env_vars": "", + "kernel_image": "", + "kernel_command_line": "", + "first_port_name": "", +@@ -323,6 +324,7 @@ + "platform": "" + }, + { ++ "env_vars": "", + "kernel_image": "", + "kernel_command_line": "", + "server": "vm", + +--- a/gns3/modules/qemu/settings.py ++++ b/gns3/modules/qemu/settings.py +@@ -27,6 +27,7 @@ + } + + QEMU_VM_SETTINGS = { ++ "env_vars": "", + "name": "", + "default_name_format": "{name}-{0}", + "usage": "", + +--- a/gns3/modules/qemu/qemu_vm.py ++++ b/gns3/modules/qemu/qemu_vm.py +@@ -42,7 +42,8 @@ + + self._linked_clone = True + +- qemu_vm_settings = {"usage": "", ++ qemu_vm_settings = {"env_vars": "", ++ "usage": "", + "qemu_path": "", + "hda_disk_image": "", + "hdb_disk_image": "", +--- a/gns3/modules/qemu/ui/qemu_vm_configuration_page_ui.py ++++ b/gns3/modules/qemu/ui/qemu_vm_configuration_page_ui.py +@@ -63,6 +63,12 @@ + self.uiCPULabel = QtWidgets.QLabel(self.uiGeneralSettingsTab) + self.uiCPULabel.setObjectName("uiCPULabel") + self.gridLayout_4.addWidget(self.uiCPULabel, 5, 0, 1, 1) ++ self.uiEnvvarLabel = QtWidgets.QLabel(self.uiGeneralSettingsTab) ++ self.uiEnvvarLabel.setObjectName("uiEnvvarLabel") ++ self.gridLayout_4.addWidget(self.uiEnvvarLabel, 9, 0, 1, 1) ++ self.uiEnvvarLineEdit = QtWidgets.QLineEdit(self.uiGeneralSettingsTab) ++ self.uiEnvvarLineEdit.setObjectName("uiEnvvarLineEdit") ++ self.gridLayout_4.addWidget(self.uiEnvvarLineEdit, 9, 1, 1, 1) + self.uiCPUSpinBox = QtWidgets.QSpinBox(self.uiGeneralSettingsTab) + self.uiCPUSpinBox.setMinimum(1) + self.uiCPUSpinBox.setMaximum(255) +@@ -93,7 +99,7 @@ + self.gridLayout_4.addWidget(self.uiOnCloseComboBox, 8, 1, 1, 1) + self.uiConsoleTypeLabel = QtWidgets.QLabel(self.uiGeneralSettingsTab) + self.uiConsoleTypeLabel.setObjectName("uiConsoleTypeLabel") +- self.gridLayout_4.addWidget(self.uiConsoleTypeLabel, 9, 0, 1, 1) ++ self.gridLayout_4.addWidget(self.uiConsoleTypeLabel, 10, 0, 1, 1) + self.horizontalLayout_2 = QtWidgets.QHBoxLayout() + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.uiConsoleTypeComboBox = QtWidgets.QComboBox(self.uiGeneralSettingsTab) +@@ -107,9 +113,9 @@ + self.uiConsoleAutoStartCheckBox = QtWidgets.QCheckBox(self.uiGeneralSettingsTab) + self.uiConsoleAutoStartCheckBox.setObjectName("uiConsoleAutoStartCheckBox") + self.horizontalLayout_2.addWidget(self.uiConsoleAutoStartCheckBox) +- self.gridLayout_4.addLayout(self.horizontalLayout_2, 9, 1, 1, 1) ++ self.gridLayout_4.addLayout(self.horizontalLayout_2, 10, 1, 1, 1) + spacerItem = QtWidgets.QSpacerItem(263, 94, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) +- self.gridLayout_4.addItem(spacerItem, 10, 1, 1, 1) ++ self.gridLayout_4.addItem(spacerItem, 11, 1, 1, 1) + self.uiQemutabWidget.addTab(self.uiGeneralSettingsTab, "") + self.uiHddTab = QtWidgets.QWidget() + self.uiHddTab.setObjectName("uiHddTab") +@@ -462,6 +468,7 @@ + self.uiRamLabel.setText(_translate("QemuVMConfigPageWidget", "RAM:")) + self.uiRamSpinBox.setSuffix(_translate("QemuVMConfigPageWidget", " MB")) + self.uiCPULabel.setText(_translate("QemuVMConfigPageWidget", "vCPUs:")) ++ self.uiEnvvarLabel.setText(_translate("QemuVMConfigPageWidget", "Environment variables:")) + self.uiQemuListLabel.setText(_translate("QemuVMConfigPageWidget", "Qemu binary:")) + self.uiBootPriorityLabel.setText(_translate("QemuVMConfigPageWidget", "Boot priority:")) + self.uiOnCloseLabel.setText(_translate("QemuVMConfigPageWidget", "On close:")) + +--- a/gns3/modules/qemu/ui/qemu_vm_configuration_page.ui ++++ b/gns3/modules/qemu/ui/qemu_vm_configuration_page.ui +@@ -24,26 +24,23 @@ + General settings + + +- +- ++ ++ + +- Name: ++ Default name format: + + + +- +- ++ ++ + +- +- ++ ++ + +- Default name format: ++ Name: + + + +- +- +- + + + +@@ -51,64 +48,23 @@ + + + +- +- +- +- +- +- +- +- +- &Browse... +- +- +- Qt::ToolButtonTextOnly +- +- +- +- ++ ++ + +- +- +- +- Category: ++ ++ ++ ++ ++ 0 ++ 0 ++ + + + +- ++ + + +- +- +- +- RAM: +- +- +- +- +- +- +- MB +- +- +- 32 +- +- +- 65535 +- +- +- 256 +- +- +- +- +- +- +- vCPUs: +- +- +- +- ++ + + + 1 +@@ -125,16 +81,33 @@ + + + +- +- +- +- +- 0 +- 0 +- ++ ++ ++ ++ On close: + + + ++ ++ ++ ++ Console type: ++ ++ ++ ++ ++ ++ ++ Qt::Vertical ++ ++ ++ ++ 263 ++ 94 ++ ++ ++ ++ + + + +@@ -142,27 +115,67 @@ + + + +- +- ++ ++ ++ ++ MB ++ ++ ++ 32 ++ ++ ++ 65535 ++ ++ ++ 256 ++ ++ + +- +- ++ ++ ++ ++ ++ + +- On close: ++ Category: + + + +- +- ++ ++ + +- +- ++ ++ + +- Console type: ++ RAM: + + + +- ++ ++ ++ ++ ++ ++ ++ ++ ++ &Browse... ++ ++ ++ Qt::ToolButtonTextOnly ++ ++ ++ ++ ++ ++ ++ ++ ++ vCPUs: ++ ++ ++ ++ + + + +@@ -202,18 +215,15 @@ + + + +- +- +- +- Qt::Vertical +- +- +- +- 263 +- 94 +- ++ ++ ++ ++ ++ ++ ++ Environment variables: + +- ++ + + + +@@ -470,7 +480,16 @@ + CD/DVD + + +- ++ ++ 10 ++ ++ ++ 10 ++ ++ ++ 10 ++ ++ + 10 + + + +--- a/gns3/modules/qemu/pages/qemu_vm_configuration_page.py ++++ b/gns3/modules/qemu/pages/qemu_vm_configuration_page.py +@@ -444,6 +444,8 @@ + self.uiInitrdLineEdit.setText(settings["initrd"]) + self.uiKernelImageLineEdit.setText(settings["kernel_image"]) + else: ++ self.uiEnvvarLabel.hide() ++ self.uiEnvvarLineEdit.hide() + self.uiNameLabel.hide() + self.uiNameLineEdit.hide() + self.uiHddTab.hide() +@@ -542,6 +544,7 @@ + self.uiProcessPriorityComboBox.setCurrentIndex(index) + self.uiQemuOptionsLineEdit.setText(settings["options"]) + self.uiUsageTextEdit.setPlainText(settings["usage"]) ++ self.uiEnvvarLineEdit.setText(settings["env_vars"]) + + def saveSettings(self, settings, node=None, group=False): + """ +@@ -653,4 +656,5 @@ + settings["process_priority"] = self.uiProcessPriorityComboBox.currentText().lower() + settings["options"] = self.uiQemuOptionsLineEdit.text() + settings["usage"] = self.uiUsageTextEdit.toPlainText() ++ settings["env_vars"] = self.uiEnvvarLineEdit.text() + return settings + +--- a/gns3/modules/qemu/pages/qemu_vm_preferences_page.py ++++ b/gns3/modules/qemu/pages/qemu_vm_preferences_page.py +@@ -117,7 +117,7 @@ + QtWidgets.QTreeWidgetItem(section_item, ["Base MAC address:", qemu_vm["mac_address"]]) + + # fill out the Linux boot section +- if qemu_vm["initrd"] or qemu_vm["kernel_image"] or qemu_vm["kernel_command_line"]: ++ if qemu_vm["initrd"] or qemu_vm["kernel_image"] or qemu_vm["kernel_command_line"] or qemu_vm["env_vars"]: + section_item = self._createSectionItem("Linux boot") + if qemu_vm["initrd"]: + QtWidgets.QTreeWidgetItem(section_item, ["Initial RAM disk:", qemu_vm["initrd"]]) +@@ -125,6 +125,8 @@ + QtWidgets.QTreeWidgetItem(section_item, ["Kernel image:", qemu_vm["kernel_image"]]) + if qemu_vm["kernel_command_line"]: + QtWidgets.QTreeWidgetItem(section_item, ["Kernel command line:", qemu_vm["kernel_command_line"]]) ++ if qemu_vm["env_vars"]: ++ QtWidgets.QTreeWidgetItem(section_item, ["Environment variables:", qemu_vm["env_vars"]]) + + # performance section + section_item = self._createSectionItem("Optimizations") +--- a/gns3/schemas/appliance.json ++++ b/gns3/schemas/appliance.json +@@ -329,6 +329,10 @@ + "title": "KVM requirements", + "enum": ["require", "allow", "disable"] + }, ++ "env_vars": { ++ "type": "string", ++ "title": "Environment variables" ++ }, + "options": { + "type": "string", + "title": "Optional additional qemu command line options" + diff --git a/patches/gns3-server/patch_gns3server_linkcolors.patch b/patches/gns3-server/patch_gns3server_linkcolors.patch new file mode 100644 index 0000000..9d51c73 --- /dev/null +++ b/patches/gns3-server/patch_gns3server_linkcolors.patch @@ -0,0 +1,107 @@ +Author: Pekka Helenius +Date: Sun, 6 Jun 2021 15:16:28 +0200 +Subject: [PATCH] GNS3 Server: Link colors. + +Add link color support for GNS3 Server. + +--- a/gns3server/schemas/link.py ++++ b/gns3server/schemas/link.py +@@ -68,6 +68,27 @@ + "type": "boolean", + "description": "Suspend the link" + }, ++ "link_style": { ++ "type": "object", ++ "description": "Link line style", ++ "items": { ++ "type": "object", ++ "properties": { ++ "color": { ++ "description": "Link line color", ++ "type": "string" ++ }, ++ "width": { ++ "description": "Link line width", ++ "type": "integer" ++ }, ++ "type": { ++ "description": "Link line type", ++ "type": "integer" ++ } ++ } ++ } ++ }, + "filters": FILTER_OBJECT_SCHEMA, + "capturing": { + "description": "Read only property. True if a capture running on the link", +--- a/gns3server/handlers/api/controller/link_handler.py ++++ b/gns3server/handlers/api/controller/link_handler.py +@@ -64,6 +64,8 @@ + link = await project.add_link() + if "filters" in request.json: + await link.update_filters(request.json["filters"]) ++ if "link_style" in request.json: ++ await link.update_link_style(request.json["link_style"]) + if "suspend" in request.json: + await link.update_suspend(request.json["suspend"]) + try: +@@ -135,6 +137,8 @@ + link = project.get_link(request.match_info["link_id"]) + if "filters" in request.json: + await link.update_filters(request.json["filters"]) ++ if "link_style" in request.json: ++ await link.update_link_style(request.json["link_style"]) + if "suspend" in request.json: + await link.update_suspend(request.json["suspend"]) + if "nodes" in request.json: +--- a/gns3server/controller/project.py ++++ b/gns3server/controller/project.py +@@ -905,6 +905,8 @@ + link = await self.add_link(link_id=link_data["link_id"]) + if "filters" in link_data: + await link.update_filters(link_data["filters"]) ++ if "link_style" in link_data: ++ await link.update_link_style(link_data["link_style"]) + for node_link in link_data.get("nodes", []): + node = self.get_node(node_link["node_id"]) + port = node.get_port(node_link["adapter_number"], node_link["port_number"]) +--- a/gns3server/controller/link.py ++++ b/gns3server/controller/link.py +@@ -125,6 +125,8 @@ + self._suspended = False + self._filters = {} + ++ self._link_style = {} ++ + @property + def filters(self): + """ +@@ -209,6 +211,13 @@ + self._project.emit_notification("link.updated", self.__json__()) + self._project.dump() + ++ async def update_link_style(self, link_style): ++ if link_style != self._link_style: ++ self._link_style = link_style ++ await self.update() ++ self._project.emit_notification("link.updated", self.__json__()) ++ self._project.dump() ++ + @property + def created(self): + """ +@@ -446,6 +455,7 @@ + "nodes": res, + "link_id": self._id, + "filters": self._filters, ++ "link_style": self._link_style, + "suspend": self._suspended + } + return { +@@ -457,5 +467,6 @@ + "capture_file_path": self.capture_file_path, + "link_type": self._link_type, + "filters": self._filters, ++ "link_style": self._link_style, + "suspend": self._suspended + } diff --git a/patches/gns3-server/patch_gns3server_qemu-envvars.patch b/patches/gns3-server/patch_gns3server_qemu-envvars.patch new file mode 100644 index 0000000..eba8875 --- /dev/null +++ b/patches/gns3-server/patch_gns3server_qemu-envvars.patch @@ -0,0 +1,130 @@ +Author: Pekka Helenius +Date: Sun, 6 Jun 2021 15:09:21 +0200 +Subject: [PATCH] GNS3 Server: QEMU environment variables. + +Add support for QEMU environment variables for GNS3 Server. + +--- a/gns3server/schemas/qemu.py ++++ b/gns3server/schemas/qemu.py +@@ -65,6 +65,10 @@ + "description": "Console type", + "enum": ["telnet", "vnc", "spice"] + }, ++ "env_vars": { ++ "description": "Environment variables", ++ "type": "string", ++ }, + "hda_disk_image": { + "description": "QEMU hda disk image path", + "type": "string", +@@ -250,6 +254,10 @@ + "description": "Whether the VM is a linked clone or not", + "type": "boolean" + }, ++ "env_vars": { ++ "description": "Environment variables", ++ "type": "string", ++ }, + "hda_disk_image": { + "description": "QEMU hda disk image path", + "type": "string", +@@ -434,6 +442,10 @@ + "description": "Platform to emulate", + "enum": QEMU_PLATFORMS + }, ++ "env_vars": { ++ "description": "Environment variables", ++ "type": "string", ++ }, + "hda_disk_image": { + "description": "QEMU hda disk image path", + "type": "string", +@@ -603,6 +615,7 @@ + "qemu_path", + "platform", + "console_type", ++ "env_vars", + "hda_disk_image", + "hdb_disk_image", + "hdc_disk_image", + +--- a/gns3server/schemas/qemu_template.py ++++ b/gns3server/schemas/qemu_template.py +@@ -22,6 +22,11 @@ + + + QEMU_TEMPLATE_PROPERTIES = { ++ "env_vars": { ++ "description": "Environment variables", ++ "type": "string", ++ "default": "" ++ }, + "qemu_path": { + "description": "Path to QEMU", + "type": "string", + +--- a/gns3server/compute/qemu/qemu_vm.py ++++ b/gns3server/compute/qemu/qemu_vm.py +@@ -93,6 +93,7 @@ + else: + self.platform = platform + ++ self._env_vars = "" + self._hda_disk_image = "" + self._hdb_disk_image = "" + self._hdc_disk_image = "" +@@ -219,6 +220,32 @@ + disk_image=value)) + + @property ++ def env_vars(self): ++ """ ++ Returns the environment variables for this QEMU VM. ++ ++ :returns: QEMU env vars ++ """ ++ ++ return self._env_vars ++ ++ @env_vars.setter ++ def env_vars(self, env_vars): ++ """ ++ Sets the environment variables for this QEMU VM. ++ ++ :param env vars: QEMU env vars ++ """ ++ ++ log.info('QEMU VM "{name}" [{id}] has set the QEMU environment variables to {env_vars}'.format(name=self._name, ++ id=self._id, ++ env_vars=env_vars)) ++ if sys.platform.startswith("linux"): ++ self._env_vars = env_vars.strip() ++ else: ++ self._env_vars = "" ++ ++ @property + def hda_disk_image(self): + """ + Returns the hda disk image path for this QEMU VM. +@@ -912,7 +939,7 @@ + with open(self._stdout_file, "w", encoding="utf-8") as fd: + fd.write("Start QEMU with {}\n\nExecution log:\n".format(command_string)) + self.command_line = ' '.join(command) +- self._process = await asyncio.create_subprocess_exec(*command, ++ self._process = await asyncio.create_subprocess_shell(command_string, + stdout=fd, + stderr=subprocess.STDOUT, + cwd=self.working_dir) +@@ -1651,7 +1678,10 @@ + additional_options = additional_options.replace("%vm-id%", self._id) + additional_options = additional_options.replace("%project-id%", self.project.id) + additional_options = additional_options.replace("%project-path%", '"' + self.project.path.replace('"', '\\"') + '"') +- command = [self.qemu_path] ++ command = [] ++ if self._env_vars: ++ command.extend([self._env_vars]) ++ command.extend([self.qemu_path]) + command.extend(["-name", self._name]) + command.extend(["-m", "{}M".format(self._ram)]) + command.extend(["-smp", "cpus={}".format(self._cpus)]) +