From 319e87d200e81da1b33437c5f545f395ae063028 Mon Sep 17 00:00:00 2001 From: Marek Wolan Date: Mon, 28 Aug 2023 22:34:20 +0100 Subject: [PATCH] Make changes to the way actions work --- src/primaite/simulator/core.py | 24 ++++++++++++- src/primaite/simulator/domain/controller.py | 24 +++++++------ src/primaite/simulator/network/container.py | 21 +++++++----- src/primaite/simulator/sim_container.py | 34 ++++++++++--------- .../system/applications/application.py | 10 +----- .../simulator/system/services/database.py | 2 ++ .../simulator/system/services/service.py | 12 ------- src/primaite/simulator/system/software.py | 26 +++++++------- 8 files changed, 83 insertions(+), 70 deletions(-) diff --git a/src/primaite/simulator/core.py b/src/primaite/simulator/core.py index 63120ecf..7d8999e8 100644 --- a/src/primaite/simulator/core.py +++ b/src/primaite/simulator/core.py @@ -136,7 +136,7 @@ class SimComponent(BaseModel): if not kwargs.get("uuid"): kwargs["uuid"] = str(uuid4()) super().__init__(**kwargs) - self.action_manager: Optional[ActionManager] = None + self._action_manager: ActionManager = self._init_action_manager() self._parent: Optional["SimComponent"] = None @abstractmethod @@ -153,6 +153,28 @@ class SimComponent(BaseModel): } return state + def _init_action_manager(self) -> ActionManager: + """ + Initialise the action manager for this component. + + When using a hierarchy of components, the child classes should call the parent class's _init_action_manager and + add additional actions on top of the existing generic ones. + + Example usage for inherited classes: + + ..code::python + + class WebBrowser(Application): + def _init_action_manager(self) -> ActionManager: + am = super()._init_action_manager() # all actions generic to any Application get initialised + am.add_action(...) # initialise any actions specific to the web browser + return am + + :return: Actiona manager object belonging to this sim component. + :rtype: ActionManager + """ + return ActionManager() + def apply_action(self, action: List[str], context: Dict = {}) -> None: """ Apply an action to a simulation component. Action data is passed in as a 'namespaced' list of strings. diff --git a/src/primaite/simulator/domain/controller.py b/src/primaite/simulator/domain/controller.py index f772ab22..961ef550 100644 --- a/src/primaite/simulator/domain/controller.py +++ b/src/primaite/simulator/domain/controller.py @@ -85,17 +85,6 @@ class DomainController(SimComponent): def __init__(self, **kwargs): super().__init__(**kwargs) - self.action_manager = ActionManager() - # Action 'account' matches requests like: - # ['account', '', *account_action] - self.action_manager.add_action( - "account", - Action( - func=lambda request, context: self.accounts[request.pop(0)].apply_action(request, context), - validator=GroupMembershipValidator([AccountGroup.DOMAIN_ADMIN]), - ), - ) - def describe_state(self) -> Dict: """ Produce a dictionary describing the current state of this object. @@ -109,6 +98,19 @@ class DomainController(SimComponent): state.update({"accounts": {uuid: acct.describe_state() for uuid, acct in self.accounts.items()}}) return state + def _init_action_manager(self) -> ActionManager: + am = super()._init_action_manager() + # Action 'account' matches requests like: + # ['account', '', *account_action] + am.add_action( + "account", + Action( + func=lambda request, context: self.accounts[request.pop(0)].apply_action(request, context), + validator=GroupMembershipValidator([AccountGroup.DOMAIN_ADMIN]), + ), + ) + return am + def _register_account(self, account: Account) -> None: """TODO.""" ... diff --git a/src/primaite/simulator/network/container.py b/src/primaite/simulator/network/container.py index 1c03358c..d04da987 100644 --- a/src/primaite/simulator/network/container.py +++ b/src/primaite/simulator/network/container.py @@ -17,15 +17,6 @@ class Network(SimComponent): """Initialise the network.""" super().__init__(**kwargs) - self.action_manager = ActionManager() - self.action_manager.add_action( - "node", - Action( - func=lambda request, context: self.nodes[request.pop(0)].apply_action(request, context), - validator=AllowAllValidator(), - ), - ) - def describe_state(self) -> Dict: """ Produce a dictionary describing the current state of this object. @@ -44,6 +35,18 @@ class Network(SimComponent): ) return state + def _init_action_manager(self) -> ActionManager: + am = super()._init_action_manager() + + am.add_action( + "node", + Action( + func=lambda request, context: self.nodes[request.pop(0)].apply_action(request, context), + validator=AllowAllValidator(), + ), + ) + return am + def add_node(self, node: Node) -> None: """ Add an existing node to the network. diff --git a/src/primaite/simulator/sim_container.py b/src/primaite/simulator/sim_container.py index 319defe4..8f676e6f 100644 --- a/src/primaite/simulator/sim_container.py +++ b/src/primaite/simulator/sim_container.py @@ -21,22 +21,6 @@ class Simulation(SimComponent): super().__init__(**kwargs) - self.action_manager = ActionManager() - # pass through network actions to the network objects - self.action_manager.add_action( - "network", - Action( - func=lambda request, context: self.network.apply_action(request, context), validator=AllowAllValidator() - ), - ) - # pass through domain actions to the domain object - self.action_manager.add_action( - "domain", - Action( - func=lambda request, context: self.domain.apply_action(request, context), validator=AllowAllValidator() - ), - ) - def describe_state(self) -> Dict: """ Produce a dictionary describing the current state of this object. @@ -54,3 +38,21 @@ class Simulation(SimComponent): } ) return state + + def _init_action_manager(self) -> ActionManager: + am = super()._init_action_manager() + # pass through network actions to the network objects + am.add_action( + "network", + Action( + func=lambda request, context: self.network.apply_action(request, context), validator=AllowAllValidator() + ), + ) + # pass through domain actions to the domain object + am.add_action( + "domain", + Action( + func=lambda request, context: self.domain.apply_action(request, context), validator=AllowAllValidator() + ), + ) + return am diff --git a/src/primaite/simulator/system/applications/application.py b/src/primaite/simulator/system/applications/application.py index 37748560..6a07f00f 100644 --- a/src/primaite/simulator/system/applications/application.py +++ b/src/primaite/simulator/system/applications/application.py @@ -1,6 +1,6 @@ from abc import abstractmethod from enum import Enum -from typing import Any, Dict, List, Set +from typing import Any, Dict, Set from primaite.simulator.system.software import IOSoftware @@ -53,14 +53,6 @@ class Application(IOSoftware): ) return state - def apply_action(self, action: List[str]) -> None: - """ - Applies a list of actions to the Application. - - :param action: A list of actions to apply. - """ - pass - def reset_component_for_episode(self, episode: int): """ Resets the Application component for a new episode. diff --git a/src/primaite/simulator/system/services/database.py b/src/primaite/simulator/system/services/database.py index 29e3f242..720967e7 100644 --- a/src/primaite/simulator/system/services/database.py +++ b/src/primaite/simulator/system/services/database.py @@ -28,6 +28,8 @@ class DatabaseService(Service): :param folder_name: Name of the folder which will be setup to hold the db files, defaults to "database" :type folder_name: str, optional """ + # note that this parent.file_system.create_folder call in the future will be authenticated by using permissions + # handler. This permission will be granted based on service account given to the database service. folder = self.parent.file_system.create_folder(folder_name) self.parent.file_system.create_file("db_primary_store", db_size, FileSystemFileType.MDF, folder=folder) self.parent.file_system.create_file("db_transaction_log", "1", FileSystemFileType.LDF, folder=folder) diff --git a/src/primaite/simulator/system/services/service.py b/src/primaite/simulator/system/services/service.py index ed2aa23b..eafff3f0 100644 --- a/src/primaite/simulator/system/services/service.py +++ b/src/primaite/simulator/system/services/service.py @@ -2,7 +2,6 @@ from abc import abstractmethod from enum import Enum from typing import Any, Dict, List -from primaite.simulator.network.hardware.base import Node from primaite.simulator.system.software import IOSoftware @@ -33,17 +32,6 @@ class Service(IOSoftware): operating_state: ServiceOperatingState "The current operating state of the Service." - @abstractmethod - def __init__(self, parent_node: Node, **kwargs): - """Create the service on a node. - - :param parent_node: The node on which this service runs. - :type parent_node: Node - """ - super().__init__(**kwargs) - self.parent: Node = parent_node - self.parent.software_manager.add_service(self) - @abstractmethod def describe_state(self) -> Dict: """ diff --git a/src/primaite/simulator/system/software.py b/src/primaite/simulator/system/software.py index a2acd9fb..8e931cad 100644 --- a/src/primaite/simulator/system/software.py +++ b/src/primaite/simulator/system/software.py @@ -1,6 +1,6 @@ from abc import abstractmethod from enum import Enum -from typing import Any, Dict, List, Set +from typing import Any, Dict, Set from primaite.simulator.core import SimComponent from primaite.simulator.network.transmission.transport_layer import Port @@ -98,17 +98,6 @@ class Software(SimComponent): ) return state - def apply_action(self, action: List[str]) -> None: - """ - Applies a list of actions to the software. - - The specifics of how these actions are applied should be implemented in subclasses. - - :param action: A list of actions to apply. - :type action: List[str] - """ - pass - def reset_component_for_episode(self, episode: int): """ Resets the software component for a new episode. @@ -119,6 +108,19 @@ class Software(SimComponent): """ pass + def set_health_state(self, health_state: SoftwareHealthState) -> None: + """ + Assign a new health state to this software. + + Note: this should only be possible when the software is currently running, but the software base class has no + operating state, only subclasses do. So subclasses will need to implement this check. TODO: check if this should + be changed so that the base Software class has a running attr. + + :param health_state: New health state to assign to the software + :type health_state: SoftwareHealthState + """ + self.health_state_actual = health_state + class IOSoftware(Software): """