#917 - Integrated the PrimaiteSession into all tests.
- Ran a full pre-commit hook and thus encountered tons of fixes required
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
"""Module for handling configurable observation spaces in PrimAITE."""
|
||||
import logging
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import TYPE_CHECKING, Dict, Final, List, Tuple, Union
|
||||
from typing import Dict, Final, List, Tuple, TYPE_CHECKING, Union
|
||||
|
||||
import numpy as np
|
||||
from gym import spaces
|
||||
@@ -77,7 +77,9 @@ class NodeLinkTable(AbstractObservationComponent):
|
||||
)
|
||||
|
||||
# 3. Initialise Observation with zeroes
|
||||
self.current_observation = np.zeros(observation_shape, dtype=self._DATA_TYPE)
|
||||
self.current_observation = np.zeros(
|
||||
observation_shape, dtype=self._DATA_TYPE
|
||||
)
|
||||
|
||||
def update(self):
|
||||
"""Update the observation based on current environment state.
|
||||
@@ -92,7 +94,9 @@ class NodeLinkTable(AbstractObservationComponent):
|
||||
self.current_observation[item_index][0] = int(node.node_id)
|
||||
self.current_observation[item_index][1] = node.hardware_state.value
|
||||
if isinstance(node, ActiveNode) or isinstance(node, ServiceNode):
|
||||
self.current_observation[item_index][2] = node.software_state.value
|
||||
self.current_observation[item_index][
|
||||
2
|
||||
] = node.software_state.value
|
||||
self.current_observation[item_index][
|
||||
3
|
||||
] = node.file_system_state_observed.value
|
||||
@@ -199,9 +203,16 @@ class NodeStatuses(AbstractObservationComponent):
|
||||
if isinstance(node, ServiceNode):
|
||||
for i, service in enumerate(self.env.services_list):
|
||||
if node.has_service(service):
|
||||
service_states[i] = node.get_service_state(service).value
|
||||
service_states[i] = node.get_service_state(
|
||||
service
|
||||
).value
|
||||
obs.extend(
|
||||
[hardware_state, software_state, file_system_state, *service_states]
|
||||
[
|
||||
hardware_state,
|
||||
software_state,
|
||||
file_system_state,
|
||||
*service_states,
|
||||
]
|
||||
)
|
||||
self.current_observation[:] = obs
|
||||
|
||||
@@ -259,7 +270,9 @@ class LinkTrafficLevels(AbstractObservationComponent):
|
||||
|
||||
# 1. Define the shape of your observation space component
|
||||
shape = (
|
||||
[self._quantisation_levels] * self.env.num_links * self._entries_per_link
|
||||
[self._quantisation_levels]
|
||||
* self.env.num_links
|
||||
* self._entries_per_link
|
||||
)
|
||||
|
||||
# 2. Create Observation space
|
||||
@@ -279,7 +292,9 @@ class LinkTrafficLevels(AbstractObservationComponent):
|
||||
if self._combine_service_traffic:
|
||||
loads = [link.get_current_load()]
|
||||
else:
|
||||
loads = [protocol.get_load() for protocol in link.protocol_list]
|
||||
loads = [
|
||||
protocol.get_load() for protocol in link.protocol_list
|
||||
]
|
||||
|
||||
for load in loads:
|
||||
if load <= 0:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"""Main environment module containing the PRIMmary AI Training Evironment (Primaite) class."""
|
||||
import copy
|
||||
from pathlib import Path
|
||||
from typing import Dict, Tuple, Union, Final
|
||||
from typing import Dict, Final, Tuple, Union
|
||||
|
||||
import networkx as nx
|
||||
import numpy as np
|
||||
@@ -12,8 +12,10 @@ from matplotlib import pyplot as plt
|
||||
|
||||
from primaite import getLogger
|
||||
from primaite.acl.access_control_list import AccessControlList
|
||||
from primaite.agents.utils import is_valid_acl_action_extra, \
|
||||
is_valid_node_action
|
||||
from primaite.agents.utils import (
|
||||
is_valid_acl_action_extra,
|
||||
is_valid_node_action,
|
||||
)
|
||||
from primaite.common.custom_typing import NodeUnion
|
||||
from primaite.common.enums import (
|
||||
ActionType,
|
||||
@@ -24,7 +26,8 @@ from primaite.common.enums import (
|
||||
NodeType,
|
||||
ObservationType,
|
||||
Priority,
|
||||
SoftwareState, SessionType,
|
||||
SessionType,
|
||||
SoftwareState,
|
||||
)
|
||||
from primaite.common.service import Service
|
||||
from primaite.config import training_config
|
||||
@@ -34,15 +37,18 @@ from primaite.environment.reward import calculate_reward_function
|
||||
from primaite.links.link import Link
|
||||
from primaite.nodes.active_node import ActiveNode
|
||||
from primaite.nodes.node import Node
|
||||
from primaite.nodes.node_state_instruction_green import \
|
||||
NodeStateInstructionGreen
|
||||
from primaite.nodes.node_state_instruction_green import (
|
||||
NodeStateInstructionGreen,
|
||||
)
|
||||
from primaite.nodes.node_state_instruction_red import NodeStateInstructionRed
|
||||
from primaite.nodes.passive_node import PassiveNode
|
||||
from primaite.nodes.service_node import ServiceNode
|
||||
from primaite.pol.green_pol import apply_iers, apply_node_pol
|
||||
from primaite.pol.ier import IER
|
||||
from primaite.pol.red_agent_pol import apply_red_agent_iers, \
|
||||
apply_red_agent_node_pol
|
||||
from primaite.pol.red_agent_pol import (
|
||||
apply_red_agent_iers,
|
||||
apply_red_agent_node_pol,
|
||||
)
|
||||
from primaite.transactions.transaction import Transaction
|
||||
from primaite.utils.session_output_writer import SessionOutputWriter
|
||||
|
||||
@@ -59,11 +65,11 @@ class Primaite(Env):
|
||||
ACTION_SPACE_ACL_PERMISSION_VALUES: int = 2
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
training_config_path: Union[str, Path],
|
||||
lay_down_config_path: Union[str, Path],
|
||||
session_path: Path,
|
||||
timestamp_str: str,
|
||||
self,
|
||||
training_config_path: Union[str, Path],
|
||||
lay_down_config_path: Union[str, Path],
|
||||
session_path: Path,
|
||||
timestamp_str: str,
|
||||
):
|
||||
"""
|
||||
The Primaite constructor.
|
||||
@@ -237,27 +243,19 @@ class Primaite(Env):
|
||||
)
|
||||
|
||||
self.episode_av_reward_writer = SessionOutputWriter(
|
||||
self,
|
||||
transaction_writer=False,
|
||||
learning_session=True
|
||||
self, transaction_writer=False, learning_session=True
|
||||
)
|
||||
self.transaction_writer = SessionOutputWriter(
|
||||
self,
|
||||
transaction_writer=True,
|
||||
learning_session=True
|
||||
self, transaction_writer=True, learning_session=True
|
||||
)
|
||||
|
||||
def set_as_eval(self):
|
||||
"""Set the writers to write to eval directories."""
|
||||
self.episode_av_reward_writer = SessionOutputWriter(
|
||||
self,
|
||||
transaction_writer=False,
|
||||
learning_session=False
|
||||
self, transaction_writer=False, learning_session=False
|
||||
)
|
||||
self.transaction_writer = SessionOutputWriter(
|
||||
self,
|
||||
transaction_writer=True,
|
||||
learning_session=False
|
||||
self, transaction_writer=True, learning_session=False
|
||||
)
|
||||
self.episode_count = 0
|
||||
self.step_count = 0
|
||||
@@ -322,9 +320,7 @@ class Primaite(Env):
|
||||
|
||||
# Create a Transaction (metric) object for this step
|
||||
transaction = Transaction(
|
||||
self.agent_identifier,
|
||||
self.episode_count,
|
||||
self.step_count
|
||||
self.agent_identifier, self.episode_count, self.step_count
|
||||
)
|
||||
# Load the initial observation space into the transaction
|
||||
transaction.obs_space_pre = copy.deepcopy(self.env_obs)
|
||||
@@ -354,8 +350,9 @@ class Primaite(Env):
|
||||
self.nodes_post_pol = copy.deepcopy(self.nodes)
|
||||
self.links_post_pol = copy.deepcopy(self.links)
|
||||
# Reference
|
||||
apply_node_pol(self.nodes_reference, self.node_pol,
|
||||
self.step_count) # Node PoL
|
||||
apply_node_pol(
|
||||
self.nodes_reference, self.node_pol, self.step_count
|
||||
) # Node PoL
|
||||
apply_iers(
|
||||
self.network_reference,
|
||||
self.nodes_reference,
|
||||
@@ -404,8 +401,10 @@ class Primaite(Env):
|
||||
# For evaluation, need to trigger the done value = True when
|
||||
# step count is reached in order to prevent neverending episode
|
||||
done = True
|
||||
_LOGGER.info(f"Episode: {self.episode_count}, "
|
||||
f"Average Reward: {self.average_reward}")
|
||||
_LOGGER.info(
|
||||
f"Episode: {self.episode_count}, "
|
||||
f"Average Reward: {self.average_reward}"
|
||||
)
|
||||
# Load the reward into the transaction
|
||||
transaction.reward = reward
|
||||
|
||||
@@ -452,11 +451,11 @@ class Primaite(Env):
|
||||
elif self.training_config.action_type == ActionType.ACL:
|
||||
self.apply_actions_to_acl(_action)
|
||||
elif (
|
||||
len(self.action_dict[_action]) == 6
|
||||
len(self.action_dict[_action]) == 6
|
||||
): # ACL actions in multidiscrete form have len 6
|
||||
self.apply_actions_to_acl(_action)
|
||||
elif (
|
||||
len(self.action_dict[_action]) == 4
|
||||
len(self.action_dict[_action]) == 4
|
||||
): # Node actions in multdiscrete (array) from have len 4
|
||||
self.apply_actions_to_nodes(_action)
|
||||
else:
|
||||
@@ -525,7 +524,7 @@ class Primaite(Env):
|
||||
# Patch (valid action if it's good or compromised)
|
||||
node.set_service_state(
|
||||
self.services_list[service_index],
|
||||
SoftwareState.PATCHING
|
||||
SoftwareState.PATCHING,
|
||||
)
|
||||
else:
|
||||
# Node is not of Service Type
|
||||
@@ -542,7 +541,10 @@ class Primaite(Env):
|
||||
elif property_action == 2:
|
||||
# Repair
|
||||
# You cannot repair a destroyed file system - it needs restoring
|
||||
if node.file_system_state_actual != FileSystemState.DESTROYED:
|
||||
if (
|
||||
node.file_system_state_actual
|
||||
!= FileSystemState.DESTROYED
|
||||
):
|
||||
node.set_file_system_state(FileSystemState.REPAIRING)
|
||||
elif property_action == 3:
|
||||
# Restore
|
||||
@@ -585,8 +587,9 @@ class Primaite(Env):
|
||||
acl_rule_source = "ANY"
|
||||
else:
|
||||
node = list(self.nodes.values())[action_source_ip - 1]
|
||||
if isinstance(node, ServiceNode) or isinstance(node,
|
||||
ActiveNode):
|
||||
if isinstance(node, ServiceNode) or isinstance(
|
||||
node, ActiveNode
|
||||
):
|
||||
acl_rule_source = node.ip_address
|
||||
else:
|
||||
return
|
||||
@@ -595,8 +598,9 @@ class Primaite(Env):
|
||||
acl_rule_destination = "ANY"
|
||||
else:
|
||||
node = list(self.nodes.values())[action_destination_ip - 1]
|
||||
if isinstance(node, ServiceNode) or isinstance(node,
|
||||
ActiveNode):
|
||||
if isinstance(node, ServiceNode) or isinstance(
|
||||
node, ActiveNode
|
||||
):
|
||||
acl_rule_destination = node.ip_address
|
||||
else:
|
||||
return
|
||||
@@ -681,8 +685,9 @@ class Primaite(Env):
|
||||
:return: The observation space, initial observation (zeroed out array with the correct shape)
|
||||
:rtype: Tuple[spaces.Space, np.ndarray]
|
||||
"""
|
||||
self.obs_handler = ObservationsHandler.from_config(self,
|
||||
self.obs_config)
|
||||
self.obs_handler = ObservationsHandler.from_config(
|
||||
self, self.obs_config
|
||||
)
|
||||
|
||||
return self.obs_handler.space, self.obs_handler.current_observation
|
||||
|
||||
@@ -790,7 +795,8 @@ class Primaite(Env):
|
||||
service_port = service["port"]
|
||||
service_state = SoftwareState[service["state"]]
|
||||
node.add_service(
|
||||
Service(service_protocol, service_port, service_state))
|
||||
Service(service_protocol, service_port, service_state)
|
||||
)
|
||||
else:
|
||||
# Bad formatting
|
||||
pass
|
||||
@@ -843,8 +849,9 @@ class Primaite(Env):
|
||||
dest_node_ref: Node = self.nodes_reference[link_destination]
|
||||
|
||||
# Add link to network (reference)
|
||||
self.network_reference.add_edge(source_node_ref, dest_node_ref,
|
||||
id=link_name)
|
||||
self.network_reference.add_edge(
|
||||
source_node_ref, dest_node_ref, id=link_name
|
||||
)
|
||||
|
||||
# Add link to link dictionary (reference)
|
||||
self.links_reference[link_name] = Link(
|
||||
@@ -1120,7 +1127,8 @@ class Primaite(Env):
|
||||
node_id = item["node_id"]
|
||||
node_class = item["node_class"]
|
||||
node_hardware_state: HardwareState = HardwareState[
|
||||
item["hardware_state"]]
|
||||
item["hardware_state"]
|
||||
]
|
||||
|
||||
node: NodeUnion = self.nodes[node_id]
|
||||
node_ref = self.nodes_reference[node_id]
|
||||
@@ -1186,8 +1194,12 @@ class Primaite(Env):
|
||||
# Use MAX to ensure we get them all
|
||||
for node_action in range(4):
|
||||
for service_state in range(self.num_services):
|
||||
action = [node, node_property, node_action,
|
||||
service_state]
|
||||
action = [
|
||||
node,
|
||||
node_property,
|
||||
node_action,
|
||||
service_state,
|
||||
]
|
||||
# check to see if it's a nothing action (has no effect)
|
||||
if is_valid_node_action(action):
|
||||
actions[action_key] = action
|
||||
|
||||
@@ -46,7 +46,9 @@ def calculate_reward_function(
|
||||
)
|
||||
|
||||
# Software State
|
||||
if isinstance(final_node, ActiveNode) or isinstance(final_node, ServiceNode):
|
||||
if isinstance(final_node, ActiveNode) or isinstance(
|
||||
final_node, ServiceNode
|
||||
):
|
||||
reward_value += score_node_os_state(
|
||||
final_node, initial_node, reference_node, config_values
|
||||
)
|
||||
@@ -81,7 +83,8 @@ def calculate_reward_function(
|
||||
reference_blocked = not reference_ier.get_is_running()
|
||||
live_blocked = not ier_value.get_is_running()
|
||||
ier_reward = (
|
||||
config_values.green_ier_blocked * ier_value.get_mission_criticality()
|
||||
config_values.green_ier_blocked
|
||||
* ier_value.get_mission_criticality()
|
||||
)
|
||||
|
||||
if live_blocked and not reference_blocked:
|
||||
@@ -104,7 +107,9 @@ def calculate_reward_function(
|
||||
return reward_value
|
||||
|
||||
|
||||
def score_node_operating_state(final_node, initial_node, reference_node, config_values):
|
||||
def score_node_operating_state(
|
||||
final_node, initial_node, reference_node, config_values
|
||||
):
|
||||
"""
|
||||
Calculates score relating to the hardware state of a node.
|
||||
|
||||
@@ -153,7 +158,9 @@ def score_node_operating_state(final_node, initial_node, reference_node, config_
|
||||
return score
|
||||
|
||||
|
||||
def score_node_os_state(final_node, initial_node, reference_node, config_values):
|
||||
def score_node_os_state(
|
||||
final_node, initial_node, reference_node, config_values
|
||||
):
|
||||
"""
|
||||
Calculates score relating to the Software State of a node.
|
||||
|
||||
@@ -204,7 +211,9 @@ def score_node_os_state(final_node, initial_node, reference_node, config_values)
|
||||
return score
|
||||
|
||||
|
||||
def score_node_service_state(final_node, initial_node, reference_node, config_values):
|
||||
def score_node_service_state(
|
||||
final_node, initial_node, reference_node, config_values
|
||||
):
|
||||
"""
|
||||
Calculates score relating to the service state(s) of a node.
|
||||
|
||||
@@ -276,7 +285,9 @@ def score_node_service_state(final_node, initial_node, reference_node, config_va
|
||||
return score
|
||||
|
||||
|
||||
def score_node_file_system(final_node, initial_node, reference_node, config_values):
|
||||
def score_node_file_system(
|
||||
final_node, initial_node, reference_node, config_values
|
||||
):
|
||||
"""
|
||||
Calculates score relating to the file system state of a node.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user