From f0fc6518a0edbb1685825acc5173393b626f8a73 Mon Sep 17 00:00:00 2001 From: Czar Echavez Date: Thu, 23 Nov 2023 21:48:11 +0000 Subject: [PATCH] #2064: add handling of offline service to dns, ftp and database --- .../services/database/database_service.py | 3 ++ .../system/services/dns/dns_server.py | 4 ++ .../system/services/ftp/ftp_server.py | 4 +- .../system/services/web_server/web_server.py | 6 +-- src/primaite/simulator/system/software.py | 3 +- .../system/test_database_on_node.py | 30 ++++++++++++++- .../system/test_dns_client_server.py | 37 +++++++++++++++++++ .../_simulator/_system/_services/test_dns.py | 8 +++- 8 files changed, 86 insertions(+), 9 deletions(-) diff --git a/src/primaite/simulator/system/services/database/database_service.py b/src/primaite/simulator/system/services/database/database_service.py index d7277e1e..e3adb8e1 100644 --- a/src/primaite/simulator/system/services/database/database_service.py +++ b/src/primaite/simulator/system/services/database/database_service.py @@ -173,6 +173,9 @@ class DatabaseService(Service): :param session_id: The session identifier. :return: True if the Status Code is 200, otherwise False. """ + if not super().receive(payload=payload, session_id=session_id, **kwargs): + return False + result = {"status_code": 500, "data": []} if isinstance(payload, dict) and payload.get("type"): if payload["type"] == "connect_request": diff --git a/src/primaite/simulator/system/services/dns/dns_server.py b/src/primaite/simulator/system/services/dns/dns_server.py index 90a350c8..2c8f3003 100644 --- a/src/primaite/simulator/system/services/dns/dns_server.py +++ b/src/primaite/simulator/system/services/dns/dns_server.py @@ -88,10 +88,14 @@ class DNSServer(Service): :return: True if DNS request returns a valid IP, otherwise, False """ + if not super().receive(payload=payload, session_id=session_id, **kwargs): + return False + # The payload should be a DNS packet if not isinstance(payload, DNSPacket): _LOGGER.debug(f"{payload} is not a DNSPacket") return False + # cast payload into a DNS packet payload: DNSPacket = payload if payload.dns_request is not None: diff --git a/src/primaite/simulator/system/services/ftp/ftp_server.py b/src/primaite/simulator/system/services/ftp/ftp_server.py index bc21dec3..cd128339 100644 --- a/src/primaite/simulator/system/services/ftp/ftp_server.py +++ b/src/primaite/simulator/system/services/ftp/ftp_server.py @@ -86,10 +86,10 @@ class FTPServer(FTPServiceABC): prevents an FTP request loop - FTP client and servers can exist on the same node. """ - if payload.status_code is not None: + if not super().receive(payload=payload, session_id=session_id, **kwargs): return False - if not super().receive(payload=payload, session_id=session_id, **kwargs): + if payload.status_code is not None: return False self.send(self._process_ftp_command(payload=payload, session_id=session_id), session_id) diff --git a/src/primaite/simulator/system/services/web_server/web_server.py b/src/primaite/simulator/system/services/web_server/web_server.py index 76176cd8..63df2f7d 100644 --- a/src/primaite/simulator/system/services/web_server/web_server.py +++ b/src/primaite/simulator/system/services/web_server/web_server.py @@ -155,12 +155,12 @@ class WebServer(Service): :param: payload: The payload to send. :param: session_id: The id of the session. Optional. """ + if not super().receive(payload=payload, session_id=session_id, **kwargs): + return False + # check if the payload is an HTTPPacket if not isinstance(payload, HttpRequestPacket): self.sys_log.error("Payload is not an HTTPPacket") return False - if not super().receive(payload=payload, session_id=session_id, **kwargs): - return False - return self._process_http_request(payload=payload, session_id=session_id) diff --git a/src/primaite/simulator/system/software.py b/src/primaite/simulator/system/software.py index c29bec20..830e3d79 100644 --- a/src/primaite/simulator/system/software.py +++ b/src/primaite/simulator/system/software.py @@ -3,7 +3,7 @@ from enum import Enum from ipaddress import IPv4Address from typing import Any, Dict, Optional -from primaite.simulator.core import RequestManager, RequestType, SimComponent +from primaite.simulator.core import _LOGGER, RequestManager, RequestType, SimComponent from primaite.simulator.file_system.file_system import FileSystem, Folder from primaite.simulator.network.hardware.node_operating_state import NodeOperatingState from primaite.simulator.network.transmission.transport_layer import Port @@ -264,5 +264,6 @@ class IOSoftware(Software): """ # return false if node that software is on is off if self.software_manager and self.software_manager.node.operating_state is NodeOperatingState.OFF: + _LOGGER.debug(f"{self.name} Error: {self.software_manager.node.hostname} is not online.") return False return True diff --git a/tests/integration_tests/system/test_database_on_node.py b/tests/integration_tests/system/test_database_on_node.py index 027fae4a..ef2b2956 100644 --- a/tests/integration_tests/system/test_database_on_node.py +++ b/tests/integration_tests/system/test_database_on_node.py @@ -1,9 +1,11 @@ from ipaddress import IPv4Address +from primaite.simulator.network.hardware.node_operating_state import NodeOperatingState from primaite.simulator.network.hardware.nodes.server import Server from primaite.simulator.system.applications.database_client import DatabaseClient from primaite.simulator.system.services.database.database_service import DatabaseService from primaite.simulator.system.services.ftp.ftp_server import FTPServer +from primaite.simulator.system.services.service import ServiceOperatingState def test_database_client_server_connection(uc2_network): @@ -55,7 +57,8 @@ def test_database_client_query(uc2_network): """Tests DB query across the network returns HTTP status 200 and date.""" web_server: Server = uc2_network.get_node_by_hostname("web_server") db_client: DatabaseClient = web_server.software_manager.software["DatabaseClient"] - db_client.connect() + + assert db_client.connected assert db_client.query("SELECT") @@ -92,3 +95,28 @@ def test_restore_backup(uc2_network): assert db_service.restore_backup() is True assert db_service.file_system.get_file(folder_name="database", file_name="database.db") is not None + + +def test_database_client_cannot_query_offline_database_server(uc2_network): + """Tests DB query across the network returns HTTP status 404 when db server is offline.""" + db_server: Server = uc2_network.get_node_by_hostname("database_server") + db_service: DatabaseService = db_server.software_manager.software["DatabaseService"] + + assert db_server.operating_state is NodeOperatingState.ON + assert db_service.operating_state is ServiceOperatingState.RUNNING + + web_server: Server = uc2_network.get_node_by_hostname("web_server") + db_client: DatabaseClient = web_server.software_manager.software["DatabaseClient"] + assert db_client.connected + + assert db_client.query("SELECT") is True + + db_server.power_off() + + for i in range(db_server.shut_down_duration + 1): + uc2_network.apply_timestep(timestep=i) + + assert db_server.operating_state is NodeOperatingState.OFF + assert db_service.operating_state is ServiceOperatingState.STOPPED + + assert db_client.query("SELECT") is False diff --git a/tests/integration_tests/system/test_dns_client_server.py b/tests/integration_tests/system/test_dns_client_server.py index e82d97a4..81a223ef 100644 --- a/tests/integration_tests/system/test_dns_client_server.py +++ b/tests/integration_tests/system/test_dns_client_server.py @@ -1,3 +1,4 @@ +from primaite.simulator.network.hardware.node_operating_state import NodeOperatingState from primaite.simulator.network.hardware.nodes.computer import Computer from primaite.simulator.network.hardware.nodes.server import Server from primaite.simulator.system.services.dns.dns_client import DNSClient @@ -24,3 +25,39 @@ def test_dns_client_server(uc2_network): # arcd.com is registered in dns server and should be saved to cache assert dns_client.check_domain_exists(target_domain="arcd.com") assert dns_client.dns_cache.get("arcd.com", None) is not None + + assert len(dns_client.dns_cache) == 1 + + +def test_dns_client_requests_offline_dns_server(uc2_network): + client_1: Computer = uc2_network.get_node_by_hostname("client_1") + domain_controller: Server = uc2_network.get_node_by_hostname("domain_controller") + + dns_client: DNSClient = client_1.software_manager.software["DNSClient"] + dns_server: DNSServer = domain_controller.software_manager.software["DNSServer"] + + assert dns_client.operating_state == ServiceOperatingState.RUNNING + assert dns_server.operating_state == ServiceOperatingState.RUNNING + + dns_server.show() + + # arcd.com is registered in dns server + assert dns_client.check_domain_exists(target_domain="arcd.com") + assert dns_client.dns_cache.get("arcd.com", None) is not None + + assert len(dns_client.dns_cache) == 1 + dns_client.dns_cache = {} + + domain_controller.power_off() + + for i in range(domain_controller.shut_down_duration + 1): + uc2_network.apply_timestep(timestep=i) + + assert domain_controller.operating_state == NodeOperatingState.OFF + assert dns_server.operating_state == ServiceOperatingState.STOPPED + + # this time it should not cache because dns server is not online + assert dns_client.check_domain_exists(target_domain="arcd.com") is False + assert dns_client.dns_cache.get("arcd.com", None) is None + + assert len(dns_client.dns_cache) == 0 diff --git a/tests/unit_tests/_primaite/_simulator/_system/_services/test_dns.py b/tests/unit_tests/_primaite/_simulator/_system/_services/test_dns.py index dc6df5d4..469c8548 100644 --- a/tests/unit_tests/_primaite/_simulator/_system/_services/test_dns.py +++ b/tests/unit_tests/_primaite/_simulator/_system/_services/test_dns.py @@ -3,6 +3,7 @@ from ipaddress import IPv4Address import pytest from primaite.simulator.network.hardware.base import Node +from primaite.simulator.network.hardware.node_operating_state import NodeOperatingState from primaite.simulator.network.hardware.nodes.computer import Computer from primaite.simulator.network.hardware.nodes.server import Server from primaite.simulator.network.protocols.dns import DNSPacket, DNSReply, DNSRequest @@ -15,10 +16,13 @@ from primaite.simulator.system.services.dns.dns_server import DNSServer @pytest.fixture(scope="function") def dns_server() -> Node: node = Server( - hostname="dns_server", ip_address="192.168.1.10", subnet_mask="255.255.255.0", default_gateway="192.168.1.1" + hostname="dns_server", + ip_address="192.168.1.10", + subnet_mask="255.255.255.0", + default_gateway="192.168.1.1", + operating_state=NodeOperatingState.ON, ) node.software_manager.install(software_class=DNSServer) - node.software_manager.software["DNSServer"].start() return node