From 19503ab4f71f9e8ce7cebb4cb6845e3436bab2e3 Mon Sep 17 00:00:00 2001 From: UmmmAGoodName Date: Wed, 11 Mar 2026 18:49:36 +0100 Subject: [PATCH] Implementet a smarter node_type system --- gns3server/controller/node.py | 27 ++++++---------------- gns3server/controller/node_types.py | 31 +++++++++++++++++++++++++ gns3server/controller/udp_link.py | 35 ++++++++++++++--------------- 3 files changed, 55 insertions(+), 38 deletions(-) create mode 100644 gns3server/controller/node_types.py diff --git a/gns3server/controller/node.py b/gns3server/controller/node.py index 8622b23b7..37dcff665 100644 --- a/gns3server/controller/node.py +++ b/gns3server/controller/node.py @@ -27,6 +27,7 @@ from .controller_error import ( ComputeError, ComputeConflictError ) +from .node_types import BUILTIN_NODE_TYPES from .ports.port_factory import PortFactory, StandardPortFactory, DynamipsPortFactory from ..utils.images import images_directories from ..utils import macaddress_to_int, int_to_macaddress @@ -452,8 +453,7 @@ class Node: if ( prop == "name" and self.status == "started" - and self._node_type - not in ("cloud", "nat", "ethernet_switch", "ethernet_hub", "frame_relay_switch", "atm_switch") + and self._node_type not in BUILTIN_NODE_TYPES ): raise ControllerError("Sorry, it is not possible to rename a node that is already powered on") setattr(self, prop, kwargs[prop]) @@ -541,35 +541,22 @@ class Node: if self._console: # console is optional for builtin nodes data["console"] = self._console - if self._console_type and self._node_type not in ( - "cloud", - "nat", - "ethernet_hub", - "frame_relay_switch", - "atm_switch", - ): + if self._console_type and self._node_type not in (BUILTIN_NODE_TYPES - {"ethernet_switch"}): # console_type is not supported by all builtin nodes excepting Ethernet switch data["console_type"] = self._console_type if self._aux: # aux is optional for builtin nodes data["aux"] = self._aux - if self._aux_type and self._node_type not in ( - "cloud", - "nat", - "ethernet_switch", - "ethernet_hub", - "frame_relay_switch", - "atm_switch", - ): + if self._aux_type and self._node_type not in BUILTIN_NODE_TYPES: # aux_type is not supported by all builtin nodes data["aux_type"] = self._aux_type if self.custom_adapters: data["custom_adapters"] = self.custom_adapters # None properties are not be sent because it can mean the emulator doesn't support it - for key in list(data.keys()): - if data[key] is None or data[key] == {} or key in self.CONTROLLER_ONLY_PROPERTIES: - del data[key] + for key, value in list(data.items()): + if value is None or value == {} or key in self.CONTROLLER_ONLY_PROPERTIES: + del value return data diff --git a/gns3server/controller/node_types.py b/gns3server/controller/node_types.py new file mode 100644 index 000000000..4c4683c62 --- /dev/null +++ b/gns3server/controller/node_types.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# +# Copyright (C) 2026 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 . + +""" +Shared node type constants used across the controller. +""" + +# Node types that are always running (builtin/virtual switch nodes). +# These nodes do not require explicit start/stop and have limited feature support. +BUILTIN_NODE_TYPES = frozenset({ + "cloud", + "nat", + "ethernet_switch", + "ethernet_hub", + "frame_relay_switch", + "atm_switch", +}) \ No newline at end of file diff --git a/gns3server/controller/udp_link.py b/gns3server/controller/udp_link.py index 4788f61ab..9b9172f38 100644 --- a/gns3server/controller/udp_link.py +++ b/gns3server/controller/udp_link.py @@ -18,6 +18,7 @@ from .controller_error import ControllerError, ControllerNotFoundError from .link import Link +from .node_types import BUILTIN_NODE_TYPES class UDPLink(Link): @@ -32,6 +33,18 @@ class UDPLink(Link): Use for the debug exports """ return self._link_data + + def _get_node_filters(self, node1, node2): + """ + Determine which node gets the active filters applied. + + :returns: Tuple of (node1_filters, node2_filters) + """ + filter_node = self._get_filter_node() + return ( + self.get_active_filters() if filter_node == node1 else {}, + self.get_active_filters() if filter_node == node2 else {}, + ) async def create(self): """ @@ -57,13 +70,7 @@ class UDPLink(Link): response = await node2.compute.post(f"/projects/{self._project.id}/ports/udp") self._node2_port = response.json["udp_port"] - node1_filters = {} - node2_filters = {} - filter_node = self._get_filter_node() - if filter_node == node1: - node1_filters = self.get_active_filters() - elif filter_node == node2: - node2_filters = self.get_active_filters() + node1_filters, node2_filters = self._get_node_filters(node1, node2) # Create the tunnel on both side self._link_data.append( @@ -108,13 +115,7 @@ class UDPLink(Link): node1 = self._nodes[0]["node"] node2 = self._nodes[1]["node"] - node1_filters = {} - node2_filters = {} - filter_node = self._get_filter_node() - if filter_node == node1: - node1_filters = self.get_active_filters() - elif filter_node == node2: - node2_filters = self.get_active_filters() + node1_filters, node2_filters = self._get_node_filters(node1, node2) adapter_number1 = self._nodes[0]["adapter_number"] port_number1 = self._nodes[0]["port_number"] @@ -213,18 +214,16 @@ class UDPLink(Link): :returns: Node where the capture should run """ - ALWAYS_RUNNING_NODES_TYPE = ("cloud", "nat", "ethernet_switch", "ethernet_hub", "frame_relay_switch", "atm_switch") - for node in self._nodes: if ( node["node"].compute.id == "local" - and node["node"].node_type in ALWAYS_RUNNING_NODES_TYPE + and node["node"].node_type in BUILTIN_NODE_TYPES and node["node"].status == "started" ): return node for node in self._nodes: - if node["node"].node_type in ALWAYS_RUNNING_NODES_TYPE and node["node"].status == "started": + if node["node"].node_type in BUILTIN_NODE_TYPES and node["node"].status == "started": return node for node in self._nodes: