Merge remote-tracking branch 'origin/dev' into feature/915_PRI-31_Packaging_Deployment
# Conflicts: # docs/source/about.rst # src/primaite/main.py # src/primaite/nodes/node.py
This commit is contained in:
@@ -31,7 +31,7 @@ An inheritance model has been adopted in order to model nodes. All nodes have th
|
|||||||
* Name
|
* Name
|
||||||
* Type (e.g. computer, switch, RTU - enumeration)
|
* Type (e.g. computer, switch, RTU - enumeration)
|
||||||
* Priority (P1, P2, P3, P4 or P5 - enumeration)
|
* Priority (P1, P2, P3, P4 or P5 - enumeration)
|
||||||
* Hardware State (ON, OFF, RESETTING - enumeration)
|
* Hardware State (ON, OFF, RESETTING, SHUTTING_DOWN, BOOTING - enumeration)
|
||||||
|
|
||||||
Active Nodes also have the following attributes (Class: Active Node):
|
Active Nodes also have the following attributes (Class: Active Node):
|
||||||
|
|
||||||
@@ -106,6 +106,8 @@ The status changes that can be made to a node are as follows:
|
|||||||
* ON
|
* ON
|
||||||
* OFF
|
* OFF
|
||||||
* RESETTING - when a status of resetting is entered, the node will automatically exit this state after a number of steps (as defined by the nodeResetDuration configuration item) after which it returns to an ON state
|
* RESETTING - when a status of resetting is entered, the node will automatically exit this state after a number of steps (as defined by the nodeResetDuration configuration item) after which it returns to an ON state
|
||||||
|
* BOOTING
|
||||||
|
* SHUTTING_DOWN
|
||||||
|
|
||||||
* Active Nodes and Service Nodes:
|
* Active Nodes and Service Nodes:
|
||||||
|
|
||||||
@@ -254,6 +256,8 @@ For the nodes, the following values are represented:
|
|||||||
* 1 = ON
|
* 1 = ON
|
||||||
* 2 = OFF
|
* 2 = OFF
|
||||||
* 3 = RESETTING
|
* 3 = RESETTING
|
||||||
|
* 4 = SHUTTING_DOWN
|
||||||
|
* 5 = BOOTING
|
||||||
|
|
||||||
* SoftwareState:
|
* SoftwareState:
|
||||||
|
|
||||||
@@ -303,7 +307,7 @@ Each ``node_info`` contains the following:
|
|||||||
.. code-block::
|
.. code-block::
|
||||||
|
|
||||||
[
|
[
|
||||||
hardware_state (0=none, 1=ON, 2=OFF, 3=RESETTING)
|
hardware_state (0=none, 1=ON, 2=OFF, 3=RESETTING, 4=SHUTTING_DOWN, 5=BOOTING)
|
||||||
software_state (0=none, 1=GOOD, 2=PATCHING, 3=COMPROMISED)
|
software_state (0=none, 1=GOOD, 2=PATCHING, 3=COMPROMISED)
|
||||||
file_system_state (0=none, 1=GOOD, 2=CORRUPT, 3=DESTROYED, 4=REPAIRING, 5=RESTORING)
|
file_system_state (0=none, 1=GOOD, 2=CORRUPT, 3=DESTROYED, 4=REPAIRING, 5=RESTORING)
|
||||||
service1_state (0=none, 1=GOOD, 2=PATCHING, 3=COMPROMISED)
|
service1_state (0=none, 1=GOOD, 2=PATCHING, 3=COMPROMISED)
|
||||||
|
|||||||
@@ -85,7 +85,10 @@ class ConfigValuesMain(object):
|
|||||||
# Patching / Reset
|
# Patching / Reset
|
||||||
self.os_patching_duration = 0 # The time taken to patch the OS
|
self.os_patching_duration = 0 # The time taken to patch the OS
|
||||||
self.node_reset_duration = 0 # The time taken to reset a node (hardware)
|
self.node_reset_duration = 0 # The time taken to reset a node (hardware)
|
||||||
|
self.node_booting_duration = 0 # The Time taken to turn on the node
|
||||||
|
self.node_shutdown_duration = 0 # The time taken to turn off the node
|
||||||
self.service_patching_duration = 0 # The time taken to patch a service
|
self.service_patching_duration = 0 # The time taken to patch a service
|
||||||
self.file_system_repairing_limit = 0 # The time take to repair a file
|
self.file_system_repairing_limit = 0 # The time take to repair a file
|
||||||
self.file_system_restoring_limit = 0 # The time take to restore a file
|
self.file_system_restoring_limit = 0 # The time take to restore a file
|
||||||
self.file_system_scanning_limit = 0 # The time taken to scan the file system
|
self.file_system_scanning_limit = 0 # The time taken to scan the file system
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ class HardwareState(Enum):
|
|||||||
ON = 1
|
ON = 1
|
||||||
OFF = 2
|
OFF = 2
|
||||||
RESETTING = 3
|
RESETTING = 3
|
||||||
|
SHUTTING_DOWN = 4
|
||||||
|
BOOTING = 5
|
||||||
|
|
||||||
|
|
||||||
class SoftwareState(Enum):
|
class SoftwareState(Enum):
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Crown Copyright (C) Dstl 2022. DEFCON 703. Shared in confidence.
|
# Crown Copyright (C) Dstl 2022. DEFCON 703. Shared in confidence.
|
||||||
"""Main environment module containing the PRIMmary AI Training Evironment (Primaite) class."""
|
"""Main environment module containing the PRIMmary AI Training Evironment (Primaite) class."""
|
||||||
|
import time
|
||||||
import copy
|
import copy
|
||||||
import csv
|
import csv
|
||||||
import logging
|
import logging
|
||||||
|
|||||||
@@ -210,3 +210,17 @@ class ActiveNode(Node):
|
|||||||
self.file_system_state_observed = self.file_system_state_actual
|
self.file_system_state_observed = self.file_system_state_actual
|
||||||
self.file_system_scanning = False
|
self.file_system_scanning = False
|
||||||
self.file_system_scanning_count = 0
|
self.file_system_scanning_count = 0
|
||||||
|
|
||||||
|
def update_resetting_status(self):
|
||||||
|
"""Updates the reset count & makes software and file state to GOOD."""
|
||||||
|
super().update_resetting_status()
|
||||||
|
if self.resetting_count <= 0:
|
||||||
|
self.file_system_state_actual = FileSystemState.GOOD
|
||||||
|
self.software_state = SoftwareState.GOOD
|
||||||
|
|
||||||
|
def update_booting_status(self):
|
||||||
|
"""Updates the booting software and file state to GOOD."""
|
||||||
|
super().update_booting_status()
|
||||||
|
if self.booting_count <= 0:
|
||||||
|
self.file_system_state_actual = FileSystemState.GOOD
|
||||||
|
self.software_state = SoftwareState.GOOD
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ class Node:
|
|||||||
self.hardware_state: HardwareState = hardware_state
|
self.hardware_state: HardwareState = hardware_state
|
||||||
self.resetting_count: int = 0
|
self.resetting_count: int = 0
|
||||||
self.config_values: TrainingConfig = config_values
|
self.config_values: TrainingConfig = config_values
|
||||||
|
self.booting_count: int = 0
|
||||||
|
self.shutting_down_count: int = 0
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
"""Returns the name of the node."""
|
"""Returns the name of the node."""
|
||||||
@@ -42,11 +44,12 @@ class Node:
|
|||||||
|
|
||||||
def turn_on(self):
|
def turn_on(self):
|
||||||
"""Sets the node state to ON."""
|
"""Sets the node state to ON."""
|
||||||
self.hardware_state = HardwareState.ON
|
self.hardware_state = HardwareState.BOOTING
|
||||||
|
self.booting_count = self.config_values.node_booting_duration
|
||||||
def turn_off(self):
|
def turn_off(self):
|
||||||
"""Sets the node state to OFF."""
|
"""Sets the node state to OFF."""
|
||||||
self.hardware_state = HardwareState.OFF
|
self.hardware_state = HardwareState.OFF
|
||||||
|
self.shutting_down_count = self.config_values.node_shutdown_duration
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
"""Sets the node state to Resetting and starts the reset count."""
|
"""Sets the node state to Resetting and starts the reset count."""
|
||||||
@@ -59,3 +62,17 @@ class Node:
|
|||||||
if self.resetting_count <= 0:
|
if self.resetting_count <= 0:
|
||||||
self.resetting_count = 0
|
self.resetting_count = 0
|
||||||
self.hardware_state = HardwareState.ON
|
self.hardware_state = HardwareState.ON
|
||||||
|
|
||||||
|
def update_booting_status(self):
|
||||||
|
"""Updates the booting count"""
|
||||||
|
self.booting_count -= 1
|
||||||
|
if self.booting_count <= 0:
|
||||||
|
self.booting_count = 0
|
||||||
|
self.hardware_state = HardwareState.ON
|
||||||
|
|
||||||
|
def update_shutdown_status(self):
|
||||||
|
"""Updates the shutdown count"""
|
||||||
|
self.shutting_down_count -= 1
|
||||||
|
if self.shutting_down_count <= 0:
|
||||||
|
self.shutting_down_count = 0
|
||||||
|
self.hardware_state = HardwareState.OFF
|
||||||
|
|||||||
@@ -188,3 +188,15 @@ class ServiceNode(ActiveNode):
|
|||||||
for service_key, service_value in self.services.items():
|
for service_key, service_value in self.services.items():
|
||||||
if service_value.software_state == SoftwareState.PATCHING:
|
if service_value.software_state == SoftwareState.PATCHING:
|
||||||
service_value.reduce_patching_count()
|
service_value.reduce_patching_count()
|
||||||
|
|
||||||
|
def update_resetting_status(self):
|
||||||
|
super().update_resetting_status()
|
||||||
|
if self.resetting_count <= 0:
|
||||||
|
for service in self.services.values():
|
||||||
|
service.software_state = SoftwareState.GOOD
|
||||||
|
|
||||||
|
def update_booting_status(self):
|
||||||
|
super().update_booting_status()
|
||||||
|
if self.booting_count <= 0:
|
||||||
|
for service in self.services.values():
|
||||||
|
service.software_state =SoftwareState.GOOD
|
||||||
|
|||||||
@@ -83,6 +83,8 @@ greenIerBlocked: -10
|
|||||||
# Patching / Reset durations
|
# Patching / Reset durations
|
||||||
osPatchingDuration: 5 # The time taken to patch the OS
|
osPatchingDuration: 5 # The time taken to patch the OS
|
||||||
nodeResetDuration: 5 # The time taken to reset a node (hardware)
|
nodeResetDuration: 5 # The time taken to reset a node (hardware)
|
||||||
|
nodeBootingDuration: 3 # The Time taken to turn on the node
|
||||||
|
nodeShutdownDuration: 2 # The time taken to turn off the node
|
||||||
servicePatchingDuration: 5 # The time taken to patch a service
|
servicePatchingDuration: 5 # The time taken to patch a service
|
||||||
fileSystemRepairingLimit: 5 # The time take to repair the file system
|
fileSystemRepairingLimit: 5 # The time take to repair the file system
|
||||||
fileSystemRestoringLimit: 5 # The time take to restore the file system
|
fileSystemRestoringLimit: 5 # The time take to restore the file system
|
||||||
|
|||||||
103
tests/test_resetting_node.py
Normal file
103
tests/test_resetting_node.py
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
"""Used to test Active Node functions."""
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from primaite.common.enums import FileSystemState, HardwareState, SoftwareState, NodeType, Priority
|
||||||
|
from primaite.common.service import Service
|
||||||
|
from primaite.nodes.active_node import ActiveNode
|
||||||
|
from primaite.common.config_values_main import ConfigValuesMain
|
||||||
|
from primaite.nodes.service_node import ServiceNode
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"starting_operating_state, expected_operating_state",
|
||||||
|
[
|
||||||
|
(HardwareState.RESETTING, HardwareState.ON)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_node_resets_correctly(starting_operating_state, expected_operating_state):
|
||||||
|
"""
|
||||||
|
Tests that a node resets correctly.
|
||||||
|
"""
|
||||||
|
active_node = ActiveNode(
|
||||||
|
node_id = "0",
|
||||||
|
name = "node",
|
||||||
|
node_type = NodeType.COMPUTER,
|
||||||
|
priority = Priority.P1,
|
||||||
|
hardware_state = starting_operating_state,
|
||||||
|
ip_address = "192.168.0.1",
|
||||||
|
software_state = SoftwareState.COMPROMISED,
|
||||||
|
file_system_state = FileSystemState.CORRUPT,
|
||||||
|
config_values = ConfigValuesMain(),
|
||||||
|
)
|
||||||
|
|
||||||
|
for x in range(5):
|
||||||
|
active_node.update_resetting_status()
|
||||||
|
|
||||||
|
assert active_node.software_state == SoftwareState.GOOD
|
||||||
|
assert active_node.file_system_state_actual == FileSystemState.GOOD
|
||||||
|
assert active_node.hardware_state == expected_operating_state
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"operating_state, expected_operating_state",
|
||||||
|
[
|
||||||
|
(HardwareState.BOOTING, HardwareState.ON)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_node_boots_correctly(operating_state, expected_operating_state):
|
||||||
|
"""
|
||||||
|
Tests that a node boots correctly.
|
||||||
|
"""
|
||||||
|
service_node = ServiceNode(
|
||||||
|
node_id = 0,
|
||||||
|
name = "node",
|
||||||
|
node_type = "COMPUTER",
|
||||||
|
priority = "1",
|
||||||
|
hardware_state = operating_state,
|
||||||
|
ip_address = "192.168.0.1",
|
||||||
|
software_state = SoftwareState.GOOD,
|
||||||
|
file_system_state = "GOOD",
|
||||||
|
config_values = 1,
|
||||||
|
)
|
||||||
|
service_attributes = Service(
|
||||||
|
name = "node",
|
||||||
|
port = "80",
|
||||||
|
software_state = SoftwareState.COMPROMISED
|
||||||
|
)
|
||||||
|
service_node.add_service(
|
||||||
|
service_attributes
|
||||||
|
)
|
||||||
|
|
||||||
|
for x in range(5):
|
||||||
|
service_node.update_booting_status()
|
||||||
|
|
||||||
|
assert service_attributes.software_state == SoftwareState.GOOD
|
||||||
|
assert service_node.hardware_state == expected_operating_state
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"operating_state, expected_operating_state",
|
||||||
|
[
|
||||||
|
(HardwareState.SHUTTING_DOWN, HardwareState.OFF)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_node_shutdown_correctly(operating_state, expected_operating_state):
|
||||||
|
"""
|
||||||
|
Tests that a node shutdown correctly.
|
||||||
|
"""
|
||||||
|
active_node = ActiveNode(
|
||||||
|
node_id = 0,
|
||||||
|
name = "node",
|
||||||
|
node_type = "COMPUTER",
|
||||||
|
priority = "1",
|
||||||
|
hardware_state = operating_state,
|
||||||
|
ip_address = "192.168.0.1",
|
||||||
|
software_state = SoftwareState.GOOD,
|
||||||
|
file_system_state = "GOOD",
|
||||||
|
config_values = 1,
|
||||||
|
)
|
||||||
|
|
||||||
|
for x in range(5):
|
||||||
|
active_node.update_shutdown_status()
|
||||||
|
|
||||||
|
assert active_node.hardware_state == expected_operating_state
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user