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