From e5605296391dcec5b8ffab1a15e8a31d6452fb8c Mon Sep 17 00:00:00 2001 From: Marek Wolan Date: Tue, 26 Mar 2024 12:06:23 +0000 Subject: [PATCH 1/6] #2418 Add Printer and Wireless router to config parser --- src/primaite/game/game.py | 15 +++++++++++++-- .../network/hardware/nodes/host/server.py | 6 ++++++ tests/assets/configs/test_primaite_session.yaml | 6 ++++++ .../test_primaite_session.py | 2 +- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/primaite/game/game.py b/src/primaite/game/game.py index 05b76679..6ba7e63c 100644 --- a/src/primaite/game/game.py +++ b/src/primaite/game/game.py @@ -15,10 +15,11 @@ from primaite.game.science import graph_has_cycle, topological_sort from primaite.simulator.network.hardware.base import NodeOperatingState from primaite.simulator.network.hardware.nodes.host.computer import Computer from primaite.simulator.network.hardware.nodes.host.host_node import NIC -from primaite.simulator.network.hardware.nodes.host.server import Server +from primaite.simulator.network.hardware.nodes.host.server import Printer, Server from primaite.simulator.network.hardware.nodes.network.firewall import Firewall from primaite.simulator.network.hardware.nodes.network.router import Router from primaite.simulator.network.hardware.nodes.network.switch import Switch +from primaite.simulator.network.hardware.nodes.network.wireless_router import WirelessRouter from primaite.simulator.network.nmne import set_nmne_config from primaite.simulator.network.transmission.transport_layer import Port from primaite.simulator.sim_container import Simulation @@ -273,8 +274,18 @@ class PrimaiteGame: new_node = Router.from_config(node_cfg) elif n_type == "firewall": new_node = Firewall.from_config(node_cfg) + elif n_type == "wireless_router": + new_node = WirelessRouter.from_config(node_cfg) + elif n_type == "printer": + new_node = Printer( + hostname=node_cfg["hostname"], + ip_address=node_cfg["ip_address"], + subnet_mask=node_cfg["subnet_mask"], + ) else: - _LOGGER.warning(f"invalid node type {n_type} in config") + msg = f"invalid node type {n_type} in config" + _LOGGER.error(msg) + raise ValueError(msg) if "services" in node_cfg: for service_cfg in node_cfg["services"]: new_service = None diff --git a/src/primaite/simulator/network/hardware/nodes/host/server.py b/src/primaite/simulator/network/hardware/nodes/host/server.py index 9f5157ad..593cd0dd 100644 --- a/src/primaite/simulator/network/hardware/nodes/host/server.py +++ b/src/primaite/simulator/network/hardware/nodes/host/server.py @@ -28,3 +28,9 @@ class Server(HostNode): * Applications: * Web Browser """ + + +class Printer(HostNode): + """Printer? I don't even know her!.""" + + # TODO: Implement printer-specific behaviour diff --git a/tests/assets/configs/test_primaite_session.yaml b/tests/assets/configs/test_primaite_session.yaml index 121cc7f1..f4ae4783 100644 --- a/tests/assets/configs/test_primaite_session.yaml +++ b/tests/assets/configs/test_primaite_session.yaml @@ -681,6 +681,12 @@ simulation: - ref: client_2_dns_client type: DNSClient + - ref: HP_LaserJet_Pro_4102fdn_printer + type: printer + hostname: HP_LaserJet_Pro_4102fdn_printer + ip_address: 192.168.10.99 + subnet_mask: 255.255.255.0 + links: - ref: router_1___switch_1 endpoint_a_ref: router_1 diff --git a/tests/e2e_integration_tests/test_primaite_session.py b/tests/e2e_integration_tests/test_primaite_session.py index c45a4690..7febe39a 100644 --- a/tests/e2e_integration_tests/test_primaite_session.py +++ b/tests/e2e_integration_tests/test_primaite_session.py @@ -29,7 +29,7 @@ class TestPrimaiteSession: assert session.env assert session.env.game.simulation.network - assert len(session.env.game.simulation.network.nodes) == 10 + assert len(session.env.game.simulation.network.nodes) == 11 @pytest.mark.skip(reason="Session is not being maintained and will be removed in the subsequent beta release.") @pytest.mark.parametrize("temp_primaite_session", [[CFG_PATH]], indirect=True) From dca26b832db8a0674a87df4409eab286c5362890 Mon Sep 17 00:00:00 2001 From: Marek Wolan Date: Tue, 26 Mar 2024 13:21:22 +0000 Subject: [PATCH 2/6] #2418 Fix wireless router from config --- .../network/hardware/nodes/network/router.py | 2 +- .../hardware/nodes/network/wireless_router.py | 69 ++++++++++++++++++- .../assets/configs/test_primaite_session.yaml | 27 ++++++++ .../test_primaite_session.py | 9 ++- 4 files changed, 103 insertions(+), 4 deletions(-) diff --git a/src/primaite/simulator/network/hardware/nodes/network/router.py b/src/primaite/simulator/network/hardware/nodes/network/router.py index d2b47c1a..de308547 100644 --- a/src/primaite/simulator/network/hardware/nodes/network/router.py +++ b/src/primaite/simulator/network/hardware/nodes/network/router.py @@ -1418,7 +1418,7 @@ class Router(NetworkNode): :return: Configured router. :rtype: Router """ - router = Router( + router = cls( hostname=cfg["hostname"], num_ports=int(cfg.get("num_ports", "5")), operating_state=NodeOperatingState.ON diff --git a/src/primaite/simulator/network/hardware/nodes/network/wireless_router.py b/src/primaite/simulator/network/hardware/nodes/network/wireless_router.py index 3e8d715f..4bd3d101 100644 --- a/src/primaite/simulator/network/hardware/nodes/network/wireless_router.py +++ b/src/primaite/simulator/network/hardware/nodes/network/wireless_router.py @@ -1,10 +1,14 @@ +from ipaddress import IPv4Address from typing import Any, Dict, Union from pydantic import validate_call from primaite.simulator.network.airspace import AirSpaceFrequency, IPWirelessNetworkInterface -from primaite.simulator.network.hardware.nodes.network.router import Router, RouterInterface +from primaite.simulator.network.hardware.node_operating_state import NodeOperatingState +from primaite.simulator.network.hardware.nodes.network.router import ACLAction, Router, RouterInterface from primaite.simulator.network.transmission.data_link_layer import Frame +from primaite.simulator.network.transmission.network_layer import IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port from primaite.utils.validators import IPV4Address @@ -209,3 +213,66 @@ class WirelessRouter(Router): raise NotImplementedError( "Please use the 'configure_wireless_access_point' and 'configure_router_interface' functions." ) + + @classmethod + def from_config(cls, cfg: Dict) -> "WirelessRouter": + """Generate the wireless router from config. + + Schema: + - hostname (str): unique name for this router. + - router_interface (dict): The values should be another dict specifying + - ip_address (str) + - subnet_mask (str) + - wireless_access_point (dict): Dict with + - ip address, + - subnet mask, + - frequency, (string: either WIFI_2_4 or WIFI_5) + - acl (dict): Dict with integers from 1 - max_acl_rules as keys. The key defines the position within the ACL + where the rule will be added (lower number is resolved first). The values should describe valid ACL + Rules as: + - action (str): either PERMIT or DENY + - src_port (str, optional): the named port such as HTTP, HTTPS, or POSTGRES_SERVER + - dst_port (str, optional): the named port such as HTTP, HTTPS, or POSTGRES_SERVER + - protocol (str, optional): the named IP protocol such as ICMP, TCP, or UDP + - src_ip_address (str, optional): IP address octet written in base 10 + - dst_ip_address (str, optional): IP address octet written in base 10 + + :param cfg: Config dictionary + :type cfg: Dict + :return: WirelessRouter instance. + :rtype: WirelessRouter + """ + operating_state = ( + NodeOperatingState.ON if not (p := cfg.get("operating_state")) else NodeOperatingState[p.upper()] + ) + router = cls(hostname=cfg["hostname"], operating_state=operating_state) + if "router_interface" in cfg: + ip_address = cfg["router_interface"]["ip_address"] + subnet_mask = cfg["router_interface"]["subnet_mask"] + router.configure_router_interface(ip_address=ip_address, subnet_mask=subnet_mask) + if "wireless_access_point" in cfg: + ip_address = cfg["wireless_access_point"]["ip_address"] + subnet_mask = cfg["wireless_access_point"]["subnet_mask"] + frequency = AirSpaceFrequency[cfg["wireless_access_point"]["frequency"]] + router.configure_wireless_access_point(ip_address=ip_address, subnet_mask=subnet_mask, frequency=frequency) + + if "acl" in cfg: + for r_num, r_cfg in cfg["acl"].items(): + router.acl.add_rule( + action=ACLAction[r_cfg["action"]], + src_port=None if not (p := r_cfg.get("src_port")) else Port[p], + dst_port=None if not (p := r_cfg.get("dst_port")) else Port[p], + protocol=None if not (p := r_cfg.get("protocol")) else IPProtocol[p], + src_ip_address=r_cfg.get("src_ip"), + dst_ip_address=r_cfg.get("dst_ip"), + position=r_num, + ) + if "routes" in cfg: + for route in cfg.get("routes"): + router.route_table.add_route( + address=IPv4Address(route.get("address")), + subnet_mask=IPv4Address(route.get("subnet_mask", "255.255.255.0")), + next_hop_ip_address=IPv4Address(route.get("next_hop_ip_address")), + metric=float(route.get("metric", 0)), + ) + return router diff --git a/tests/assets/configs/test_primaite_session.yaml b/tests/assets/configs/test_primaite_session.yaml index f4ae4783..11db08a7 100644 --- a/tests/assets/configs/test_primaite_session.yaml +++ b/tests/assets/configs/test_primaite_session.yaml @@ -687,6 +687,33 @@ simulation: ip_address: 192.168.10.99 subnet_mask: 255.255.255.0 + - ref: router_2 + type: wireless_router + hostname: router_2 + router_interface: + ip_address: 192.169.1.1 + subnet_mask: 255.255.255.0 + wireless_access_point: + ip_address: 192.169.1.1 + subnet_mask: 255.255.255.0 + frequency: WIFI_2_4 + acl: + 0: + action: PERMIT + src_port: POSTGRES_SERVER + dst_port: POSTGRES_SERVER + 1: + action: PERMIT + src_port: DNS + dst_port: DNS + 22: + action: PERMIT + src_port: ARP + dst_port: ARP + 23: + action: PERMIT + protocol: ICMP + links: - ref: router_1___switch_1 endpoint_a_ref: router_1 diff --git a/tests/e2e_integration_tests/test_primaite_session.py b/tests/e2e_integration_tests/test_primaite_session.py index 7febe39a..32f134a3 100644 --- a/tests/e2e_integration_tests/test_primaite_session.py +++ b/tests/e2e_integration_tests/test_primaite_session.py @@ -1,6 +1,8 @@ import pydantic import pytest +from primaite.simulator.network.hardware.nodes.host.server import Printer +from primaite.simulator.network.hardware.nodes.network.wireless_router import WirelessRouter from tests import TEST_ASSETS_ROOT from tests.conftest import TempPrimaiteSession @@ -11,7 +13,6 @@ MISCONFIGURED_PATH = TEST_ASSETS_ROOT / "configs/bad_primaite_session.yaml" MULTI_AGENT_PATH = TEST_ASSETS_ROOT / "configs/multi_agent_session.yaml" -# @pytest.mark.skip(reason="no way of currently testing this") class TestPrimaiteSession: @pytest.mark.parametrize("temp_primaite_session", [[CFG_PATH]], indirect=True) def test_creating_session(self, temp_primaite_session): @@ -29,7 +30,11 @@ class TestPrimaiteSession: assert session.env assert session.env.game.simulation.network - assert len(session.env.game.simulation.network.nodes) == 11 + assert len(session.env.game.simulation.network.nodes) == 12 + wireless = session.env.game.simulation.network.get_node_by_hostname("router_2") + assert isinstance(wireless, WirelessRouter) + printer = session.env.game.simulation.network.get_node_by_hostname("HP_LaserJet_Pro_4102fdn_printer") + assert isinstance(printer, Printer) @pytest.mark.skip(reason="Session is not being maintained and will be removed in the subsequent beta release.") @pytest.mark.parametrize("temp_primaite_session", [[CFG_PATH]], indirect=True) From 0e90e6a262024a81411fc332c2fbd1767604831b Mon Sep 17 00:00:00 2001 From: Marek Wolan Date: Tue, 26 Mar 2024 16:23:39 +0000 Subject: [PATCH 3/6] #2418 - Change test config --- tests/assets/configs/test_primaite_session.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/assets/configs/test_primaite_session.yaml b/tests/assets/configs/test_primaite_session.yaml index 11db08a7..4adbb225 100644 --- a/tests/assets/configs/test_primaite_session.yaml +++ b/tests/assets/configs/test_primaite_session.yaml @@ -694,7 +694,7 @@ simulation: ip_address: 192.169.1.1 subnet_mask: 255.255.255.0 wireless_access_point: - ip_address: 192.169.1.1 + ip_address: 192.170.1.1 subnet_mask: 255.255.255.0 frequency: WIFI_2_4 acl: From 9a8a42f3ecf8ee57b09de465d26e91b494e98c73 Mon Sep 17 00:00:00 2001 From: Marek Wolan Date: Tue, 26 Mar 2024 21:48:17 +0000 Subject: [PATCH 4/6] #2418 - add wildcard masks and from_config tests to routers --- .../network/hardware/nodes/network/router.py | 2 + .../hardware/nodes/network/wireless_router.py | 2 + .../_network/_hardware/nodes/test_router.py | 111 ++++++++++++++++++ .../_hardware/nodes/test_wireless_router.py | 97 +++++++++++++++ 4 files changed, 212 insertions(+) create mode 100644 tests/unit_tests/_primaite/_simulator/_network/_hardware/nodes/test_router.py create mode 100644 tests/unit_tests/_primaite/_simulator/_network/_hardware/nodes/test_wireless_router.py diff --git a/src/primaite/simulator/network/hardware/nodes/network/router.py b/src/primaite/simulator/network/hardware/nodes/network/router.py index de308547..102eb7dc 100644 --- a/src/primaite/simulator/network/hardware/nodes/network/router.py +++ b/src/primaite/simulator/network/hardware/nodes/network/router.py @@ -1441,6 +1441,8 @@ class Router(NetworkNode): protocol=None if not (p := r_cfg.get("protocol")) else IPProtocol[p], src_ip_address=r_cfg.get("src_ip"), dst_ip_address=r_cfg.get("dst_ip"), + src_wildcard_mask=r_cfg.get("src_wildcard_mask"), + dst_wildcard_mask=r_cfg.get("dst_wildcard_mask"), position=r_num, ) if "routes" in cfg: diff --git a/src/primaite/simulator/network/hardware/nodes/network/wireless_router.py b/src/primaite/simulator/network/hardware/nodes/network/wireless_router.py index 4bd3d101..62332269 100644 --- a/src/primaite/simulator/network/hardware/nodes/network/wireless_router.py +++ b/src/primaite/simulator/network/hardware/nodes/network/wireless_router.py @@ -265,6 +265,8 @@ class WirelessRouter(Router): protocol=None if not (p := r_cfg.get("protocol")) else IPProtocol[p], src_ip_address=r_cfg.get("src_ip"), dst_ip_address=r_cfg.get("dst_ip"), + src_wildcard_mask=r_cfg.get("src_wildcard_mask"), + dst_wildcard_mask=r_cfg.get("dst_wildcard_mask"), position=r_num, ) if "routes" in cfg: diff --git a/tests/unit_tests/_primaite/_simulator/_network/_hardware/nodes/test_router.py b/tests/unit_tests/_primaite/_simulator/_network/_hardware/nodes/test_router.py new file mode 100644 index 00000000..be74a721 --- /dev/null +++ b/tests/unit_tests/_primaite/_simulator/_network/_hardware/nodes/test_router.py @@ -0,0 +1,111 @@ +from ipaddress import IPv4Address + +from primaite.simulator.network.hardware.nodes.network.router import ACLAction, Router +from primaite.simulator.network.transmission.network_layer import IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port + + +def test_wireless_router_from_config(): + cfg = { + "ref": "router_1", + "type": "router", + "hostname": "router_1", + "num_ports": 6, + "ports": { + 1: { + "ip_address": "192.168.1.1", + "subnet_mask": "255.255.255.0", + }, + 2: { + "ip_address": "192.168.2.1", + "subnet_mask": "255.255.255.0", + }, + }, + "acl": { + 0: { + "action": "PERMIT", + "src_port": "POSTGRES_SERVER", + "dst_port": "POSTGRES_SERVER", + }, + 1: { + "action": "PERMIT", + "protocol": "ICMP", + }, + 2: { + "action": "PERMIT", + "src_ip": "100.100.100.1", + "dst_ip": "100.100.101.1", + }, + 3: { + "action": "PERMIT", + "src_ip": "100.100.102.0", + "dst_ip": "100.100.103.0", + "src_wildcard_mask": "0.0.0.255", + "dst_wildcard_mask": "0.0.0.255", + }, + 20: { + "action": "DENY", + }, + }, + } + + rt = Router.from_config(cfg=cfg) + + assert rt.num_ports == 6 + + assert rt.network_interface[1].ip_address == IPv4Address("192.168.1.1") + assert rt.network_interface[1].subnet_mask == IPv4Address("255.255.255.0") + + assert rt.network_interface[2].ip_address == IPv4Address("192.168.2.1") + assert rt.network_interface[2].subnet_mask == IPv4Address("255.255.255.0") + + assert not rt.network_interface[3].enabled + assert not rt.network_interface[4].enabled + assert not rt.network_interface[5].enabled + assert not rt.network_interface[6].enabled + + r0 = rt.acl.acl[0] + assert r0.action == ACLAction.PERMIT + assert r0.src_port == r0.dst_port == Port.POSTGRES_SERVER + assert r0.src_ip_address == r0.dst_ip_address == r0.dst_wildcard_mask == r0.src_wildcard_mask == r0.protocol == None + + r1 = rt.acl.acl[1] + assert r1.action == ACLAction.PERMIT + assert r1.protocol == IPProtocol.ICMP + assert ( + r1.src_ip_address + == r1.dst_ip_address + == r1.dst_wildcard_mask + == r1.src_wildcard_mask + == r1.src_port + == r1.dst_port + == None + ) + + r2 = rt.acl.acl[2] + assert r2.action == ACLAction.PERMIT + assert r2.src_ip_address == IPv4Address("100.100.100.1") + assert r2.dst_ip_address == IPv4Address("100.100.101.1") + assert r2.src_wildcard_mask == r2.dst_wildcard_mask == None + assert r2.src_port == r2.dst_port == r2.protocol == None + + r3 = rt.acl.acl[3] + assert r3.action == ACLAction.PERMIT + assert r3.src_ip_address == IPv4Address("100.100.102.0") + assert r3.dst_ip_address == IPv4Address("100.100.103.0") + assert r3.src_wildcard_mask == IPv4Address("0.0.0.255") + assert r3.dst_wildcard_mask == IPv4Address("0.0.0.255") + assert r3.src_port == r3.dst_port == r3.protocol == None + + r20 = rt.acl.acl[20] + assert r20.action == ACLAction.DENY + assert ( + r20.src_ip_address + == r20.dst_ip_address + == r20.src_wildcard_mask + == r20.dst_wildcard_mask + == r20.src_port + == r20.dst_port + == r20.protocol + == None + ) diff --git a/tests/unit_tests/_primaite/_simulator/_network/_hardware/nodes/test_wireless_router.py b/tests/unit_tests/_primaite/_simulator/_network/_hardware/nodes/test_wireless_router.py new file mode 100644 index 00000000..494f5a15 --- /dev/null +++ b/tests/unit_tests/_primaite/_simulator/_network/_hardware/nodes/test_wireless_router.py @@ -0,0 +1,97 @@ +from ipaddress import IPv4Address + +from primaite.simulator.network.hardware.nodes.network.router import ACLAction +from primaite.simulator.network.hardware.nodes.network.wireless_router import WirelessRouter +from primaite.simulator.network.transmission.network_layer import IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port + + +def test_wireless_router_from_config(): + cfg = { + "ref": "router_2", + "type": "wireless_router", + "hostname": "router_2", + "router_interface": { + "ip_address": "192.168.1.1", + "subnet_mask": "255.255.255.0", + }, + "wireless_access_point": { + "ip_address": "192.170.1.1", + "subnet_mask": "255.255.255.0", + "frequency": "WIFI_2_4", + }, + "acl": { + 0: { + "action": "PERMIT", + "src_port": "POSTGRES_SERVER", + "dst_port": "POSTGRES_SERVER", + }, + 1: { + "action": "PERMIT", + "protocol": "ICMP", + }, + 2: { + "action": "PERMIT", + "src_ip": "100.100.100.1", + "dst_ip": "100.100.101.1", + }, + 3: { + "action": "PERMIT", + "src_ip": "100.100.102.0", + "dst_ip": "100.100.103.0", + "src_wildcard_mask": "0.0.0.255", + "dst_wildcard_mask": "0.0.0.255", + }, + 20: { + "action": "DENY", + }, + }, + } + + rt = WirelessRouter.from_config(cfg=cfg) + + r0 = rt.acl.acl[0] + assert r0.action == ACLAction.PERMIT + assert r0.src_port == r0.dst_port == Port.POSTGRES_SERVER + assert r0.src_ip_address == r0.dst_ip_address == r0.dst_wildcard_mask == r0.src_wildcard_mask == r0.protocol == None + + r1 = rt.acl.acl[1] + assert r1.action == ACLAction.PERMIT + assert r1.protocol == IPProtocol.ICMP + assert ( + r1.src_ip_address + == r1.dst_ip_address + == r1.dst_wildcard_mask + == r1.src_wildcard_mask + == r1.src_port + == r1.dst_port + == None + ) + + r2 = rt.acl.acl[2] + assert r2.action == ACLAction.PERMIT + assert r2.src_ip_address == IPv4Address("100.100.100.1") + assert r2.dst_ip_address == IPv4Address("100.100.101.1") + assert r2.src_wildcard_mask == r2.dst_wildcard_mask == None + assert r2.src_port == r2.dst_port == r2.protocol == None + + r3 = rt.acl.acl[3] + assert r3.action == ACLAction.PERMIT + assert r3.src_ip_address == IPv4Address("100.100.102.0") + assert r3.dst_ip_address == IPv4Address("100.100.103.0") + assert r3.src_wildcard_mask == IPv4Address("0.0.0.255") + assert r3.dst_wildcard_mask == IPv4Address("0.0.0.255") + assert r3.src_port == r3.dst_port == r3.protocol == None + + r20 = rt.acl.acl[20] + assert r20.action == ACLAction.DENY + assert ( + r20.src_ip_address + == r20.dst_ip_address + == r20.src_wildcard_mask + == r20.dst_wildcard_mask + == r20.src_port + == r20.dst_port + == r20.protocol + == None + ) From 368542df03361a721fba64149174f626cf9e4637 Mon Sep 17 00:00:00 2001 From: Marek Wolan Date: Tue, 26 Mar 2024 21:51:27 +0000 Subject: [PATCH 5/6] #2418 - Add printer and wireless router as node types in network show --- src/primaite/simulator/network/container.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/primaite/simulator/network/container.py b/src/primaite/simulator/network/container.py index 0e970c3d..5ec47052 100644 --- a/src/primaite/simulator/network/container.py +++ b/src/primaite/simulator/network/container.py @@ -8,6 +8,7 @@ from prettytable import MARKDOWN, PrettyTable from primaite import getLogger from primaite.simulator.core import RequestManager, RequestType, SimComponent from primaite.simulator.network.hardware.base import Link, Node, WiredNetworkInterface +from primaite.simulator.network.hardware.nodes.host.server import Printer from primaite.simulator.system.applications.application import Application from primaite.simulator.system.services.service import Service @@ -110,6 +111,16 @@ class Network(SimComponent): """The Firewalls in the Network.""" return [node for node in self.nodes.values() if node.__class__.__name__ == "Firewall"] + @property + def printer_nodes(self) -> List[Node]: + """The printers on the network.""" + return [node for node in self.nodes.values() if isinstance(node, Printer)] + + @property + def wireless_router_nodes(self) -> List[Node]: + """The Routers in the Network.""" + return [node for node in self.nodes.values() if node.__class__.__name__ == "WirelessRouter"] + def show(self, nodes: bool = True, ip_addresses: bool = True, links: bool = True, markdown: bool = False): """ Print tables describing the Network. @@ -128,6 +139,8 @@ class Network(SimComponent): "Switch": self.switch_nodes, "Server": self.server_nodes, "Computer": self.computer_nodes, + "Printer": self.printer_nodes, + "Wireless Router": self.wireless_routers, } if nodes: table = PrettyTable(["Node", "Type", "Operating State"]) From 27bee58bf755cdaa555c998cc4bee6463fc56b3f Mon Sep 17 00:00:00 2001 From: Marek Wolan Date: Wed, 27 Mar 2024 00:00:06 +0000 Subject: [PATCH 6/6] #2418 Fix broken property --- src/primaite/simulator/network/container.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/primaite/simulator/network/container.py b/src/primaite/simulator/network/container.py index 5ec47052..a4079fb8 100644 --- a/src/primaite/simulator/network/container.py +++ b/src/primaite/simulator/network/container.py @@ -140,7 +140,7 @@ class Network(SimComponent): "Server": self.server_nodes, "Computer": self.computer_nodes, "Printer": self.printer_nodes, - "Wireless Router": self.wireless_routers, + "Wireless Router": self.wireless_router_nodes, } if nodes: table = PrettyTable(["Node", "Type", "Operating State"])