@ -0,0 +1,266 @@ | |||||
Author: Pekka Helenius <pekka.helenius@fjordtek.com> | |||||
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 <http://www.gnu.org/licenses/>. | |||||
+ | |||||
+""" | |||||
+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 | |||||
@ -0,0 +1,432 @@ | |||||
Author: Pekka Helenius <pekka.helenius@fjordtek.com> | |||||
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 @@ | |||||
<string>General settings</string> | |||||
</attribute> | |||||
<layout class="QGridLayout" name="gridLayout_4"> | |||||
- <item row="0" column="0"> | |||||
- <widget class="QLabel" name="uiNameLabel"> | |||||
+ <item row="1" column="0"> | |||||
+ <widget class="QLabel" name="uiDefaultNameFormatLabel"> | |||||
<property name="text"> | |||||
- <string>Name:</string> | |||||
+ <string>Default name format:</string> | |||||
</property> | |||||
</widget> | |||||
</item> | |||||
- <item row="0" column="1"> | |||||
- <widget class="QLineEdit" name="uiNameLineEdit"/> | |||||
+ <item row="8" column="2"> | |||||
+ <widget class="QComboBox" name="uiOnCloseComboBox"/> | |||||
</item> | |||||
- <item row="1" column="0"> | |||||
- <widget class="QLabel" name="uiDefaultNameFormatLabel"> | |||||
+ <item row="0" column="0"> | |||||
+ <widget class="QLabel" name="uiNameLabel"> | |||||
<property name="text"> | |||||
- <string>Default name format:</string> | |||||
+ <string>Name:</string> | |||||
</property> | |||||
</widget> | |||||
</item> | |||||
- <item row="1" column="1"> | |||||
- <widget class="QLineEdit" name="uiDefaultNameFormatLineEdit"/> | |||||
- </item> | |||||
<item row="2" column="0"> | |||||
<widget class="QLabel" name="uiSymbolLabel"> | |||||
<property name="text"> | |||||
@@ -51,64 +48,23 @@ | |||||
</property> | |||||
</widget> | |||||
</item> | |||||
- <item row="2" column="1"> | |||||
- <layout class="QHBoxLayout" name="horizontalLayout_7"> | |||||
- <item> | |||||
- <widget class="QLineEdit" name="uiSymbolLineEdit"/> | |||||
- </item> | |||||
- <item> | |||||
- <widget class="QToolButton" name="uiSymbolToolButton"> | |||||
- <property name="text"> | |||||
- <string>&Browse...</string> | |||||
- </property> | |||||
- <property name="toolButtonStyle"> | |||||
- <enum>Qt::ToolButtonTextOnly</enum> | |||||
- </property> | |||||
- </widget> | |||||
- </item> | |||||
- </layout> | |||||
+ <item row="7" column="2"> | |||||
+ <widget class="QComboBox" name="uiBootPriorityComboBox"/> | |||||
</item> | |||||
- <item row="3" column="0"> | |||||
- <widget class="QLabel" name="uiCategoryLabel"> | |||||
- <property name="text"> | |||||
- <string>Category:</string> | |||||
+ <item row="6" column="2"> | |||||
+ <widget class="QComboBox" name="uiQemuListComboBox"> | |||||
+ <property name="sizePolicy"> | |||||
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> | |||||
+ <horstretch>0</horstretch> | |||||
+ <verstretch>0</verstretch> | |||||
+ </sizepolicy> | |||||
</property> | |||||
</widget> | |||||
</item> | |||||
- <item row="3" column="1"> | |||||
+ <item row="3" column="2"> | |||||
<widget class="QComboBox" name="uiCategoryComboBox"/> | |||||
</item> | |||||
- <item row="4" column="0"> | |||||
- <widget class="QLabel" name="uiRamLabel"> | |||||
- <property name="text"> | |||||
- <string>RAM:</string> | |||||
- </property> | |||||
- </widget> | |||||
- </item> | |||||
- <item row="4" column="1"> | |||||
- <widget class="QSpinBox" name="uiRamSpinBox"> | |||||
- <property name="suffix"> | |||||
- <string> MB</string> | |||||
- </property> | |||||
- <property name="minimum"> | |||||
- <number>32</number> | |||||
- </property> | |||||
- <property name="maximum"> | |||||
- <number>65535</number> | |||||
- </property> | |||||
- <property name="value"> | |||||
- <number>256</number> | |||||
- </property> | |||||
- </widget> | |||||
- </item> | |||||
- <item row="5" column="0"> | |||||
- <widget class="QLabel" name="uiCPULabel"> | |||||
- <property name="text"> | |||||
- <string>vCPUs:</string> | |||||
- </property> | |||||
- </widget> | |||||
- </item> | |||||
- <item row="5" column="1"> | |||||
+ <item row="5" column="2"> | |||||
<widget class="QSpinBox" name="uiCPUSpinBox"> | |||||
<property name="minimum"> | |||||
<number>1</number> | |||||
@@ -125,16 +81,33 @@ | |||||
</property> | |||||
</widget> | |||||
</item> | |||||
- <item row="6" column="1"> | |||||
- <widget class="QComboBox" name="uiQemuListComboBox"> | |||||
- <property name="sizePolicy"> | |||||
- <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> | |||||
- <horstretch>0</horstretch> | |||||
- <verstretch>0</verstretch> | |||||
- </sizepolicy> | |||||
+ <item row="8" column="0"> | |||||
+ <widget class="QLabel" name="uiOnCloseLabel"> | |||||
+ <property name="text"> | |||||
+ <string>On close:</string> | |||||
</property> | |||||
</widget> | |||||
</item> | |||||
+ <item row="9" column="0"> | |||||
+ <widget class="QLabel" name="uiConsoleTypeLabel"> | |||||
+ <property name="text"> | |||||
+ <string>Console type:</string> | |||||
+ </property> | |||||
+ </widget> | |||||
+ </item> | |||||
+ <item row="11" column="2"> | |||||
+ <spacer name="spacer"> | |||||
+ <property name="orientation"> | |||||
+ <enum>Qt::Vertical</enum> | |||||
+ </property> | |||||
+ <property name="sizeHint" stdset="0"> | |||||
+ <size> | |||||
+ <width>263</width> | |||||
+ <height>94</height> | |||||
+ </size> | |||||
+ </property> | |||||
+ </spacer> | |||||
+ </item> | |||||
<item row="7" column="0"> | |||||
<widget class="QLabel" name="uiBootPriorityLabel"> | |||||
<property name="text"> | |||||
@@ -142,27 +115,67 @@ | |||||
</property> | |||||
</widget> | |||||
</item> | |||||
- <item row="7" column="1"> | |||||
- <widget class="QComboBox" name="uiBootPriorityComboBox"/> | |||||
+ <item row="4" column="2"> | |||||
+ <widget class="QSpinBox" name="uiRamSpinBox"> | |||||
+ <property name="suffix"> | |||||
+ <string> MB</string> | |||||
+ </property> | |||||
+ <property name="minimum"> | |||||
+ <number>32</number> | |||||
+ </property> | |||||
+ <property name="maximum"> | |||||
+ <number>65535</number> | |||||
+ </property> | |||||
+ <property name="value"> | |||||
+ <number>256</number> | |||||
+ </property> | |||||
+ </widget> | |||||
</item> | |||||
- <item row="8" column="0"> | |||||
- <widget class="QLabel" name="uiOnCloseLabel"> | |||||
+ <item row="0" column="2"> | |||||
+ <widget class="QLineEdit" name="uiNameLineEdit"/> | |||||
+ </item> | |||||
+ <item row="3" column="0"> | |||||
+ <widget class="QLabel" name="uiCategoryLabel"> | |||||
<property name="text"> | |||||
- <string>On close:</string> | |||||
+ <string>Category:</string> | |||||
</property> | |||||
</widget> | |||||
</item> | |||||
- <item row="8" column="1"> | |||||
- <widget class="QComboBox" name="uiOnCloseComboBox"/> | |||||
+ <item row="1" column="2"> | |||||
+ <widget class="QLineEdit" name="uiDefaultNameFormatLineEdit"/> | |||||
</item> | |||||
- <item row="9" column="0"> | |||||
- <widget class="QLabel" name="uiConsoleTypeLabel"> | |||||
+ <item row="4" column="0"> | |||||
+ <widget class="QLabel" name="uiRamLabel"> | |||||
<property name="text"> | |||||
- <string>Console type:</string> | |||||
+ <string>RAM:</string> | |||||
</property> | |||||
</widget> | |||||
</item> | |||||
- <item row="9" column="1"> | |||||
+ <item row="2" column="2"> | |||||
+ <layout class="QHBoxLayout" name="horizontalLayout_7"> | |||||
+ <item> | |||||
+ <widget class="QLineEdit" name="uiSymbolLineEdit"/> | |||||
+ </item> | |||||
+ <item> | |||||
+ <widget class="QToolButton" name="uiSymbolToolButton"> | |||||
+ <property name="text"> | |||||
+ <string>&Browse...</string> | |||||
+ </property> | |||||
+ <property name="toolButtonStyle"> | |||||
+ <enum>Qt::ToolButtonTextOnly</enum> | |||||
+ </property> | |||||
+ </widget> | |||||
+ </item> | |||||
+ </layout> | |||||
+ </item> | |||||
+ <item row="5" column="0"> | |||||
+ <widget class="QLabel" name="uiCPULabel"> | |||||
+ <property name="text"> | |||||
+ <string>vCPUs:</string> | |||||
+ </property> | |||||
+ </widget> | |||||
+ </item> | |||||
+ <item row="9" column="2"> | |||||
<layout class="QHBoxLayout" name="horizontalLayout_2"> | |||||
<item> | |||||
<widget class="QComboBox" name="uiConsoleTypeComboBox"> | |||||
@@ -202,18 +215,15 @@ | |||||
</item> | |||||
</layout> | |||||
</item> | |||||
- <item row="10" column="1"> | |||||
- <spacer name="spacer"> | |||||
- <property name="orientation"> | |||||
- <enum>Qt::Vertical</enum> | |||||
- </property> | |||||
- <property name="sizeHint" stdset="0"> | |||||
- <size> | |||||
- <width>263</width> | |||||
- <height>94</height> | |||||
- </size> | |||||
+ <item row="10" column="2"> | |||||
+ <widget class="QLineEdit" name="uiEnvvarLineEdit"/> | |||||
+ </item> | |||||
+ <item row="10" column="0"> | |||||
+ <widget class="QLabel" name="uiEnvvarLabel"> | |||||
+ <property name="text"> | |||||
+ <string>Environment variables:</string> | |||||
</property> | |||||
- </spacer> | |||||
+ </widget> | |||||
</item> | |||||
</layout> | |||||
</widget> | |||||
@@ -470,7 +480,16 @@ | |||||
<string>CD/DVD</string> | |||||
</attribute> | |||||
<layout class="QVBoxLayout" name="verticalLayout_4"> | |||||
- <property name="margin"> | |||||
+ <property name="leftMargin"> | |||||
+ <number>10</number> | |||||
+ </property> | |||||
+ <property name="topMargin"> | |||||
+ <number>10</number> | |||||
+ </property> | |||||
+ <property name="rightMargin"> | |||||
+ <number>10</number> | |||||
+ </property> | |||||
+ <property name="bottomMargin"> | |||||
<number>10</number> | |||||
</property> | |||||
<item> | |||||
--- 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" | |||||
@ -0,0 +1,107 @@ | |||||
Author: Pekka Helenius <pekka.helenius@fjordtek.com> | |||||
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 | |||||
} |
@ -0,0 +1,130 @@ | |||||
Author: Pekka Helenius <pekka.helenius@fjordtek.com> | |||||
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)]) | |||||