From 11848aa18018c5895ebadfcb6859b1b2025c8b52 Mon Sep 17 00:00:00 2001 From: "Czar.Echavez" Date: Tue, 31 Oct 2023 15:52:44 +0000 Subject: [PATCH 1/9] #1962: keeping track of deleted files --- .../simulator/file_system/file_system.py | 55 +++++++++++++++---- .../_file_system/test_file_system.py | 3 + 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/src/primaite/simulator/file_system/file_system.py b/src/primaite/simulator/file_system/file_system.py index 4ffe6a6d..14a4cf6e 100644 --- a/src/primaite/simulator/file_system/file_system.py +++ b/src/primaite/simulator/file_system/file_system.py @@ -154,7 +154,8 @@ class FileSystemItemABC(SimComponent): """ pass - def restore(self) -> None: + @abstractmethod + def restore(self) -> bool: """Restore the file/folder to the state before it got ruined.""" pass @@ -164,6 +165,8 @@ class FileSystem(SimComponent): folders: Dict[str, Folder] = {} "List containing all the folders in the file system." + deleted_folders: Dict[str, Folder] = {} + "List containing all the folders that have been deleted." _folders_by_name: Dict[str, Folder] = {} sys_log: SysLog sim_root: Path @@ -296,9 +299,11 @@ class FileSystem(SimComponent): self.folders.pop(folder.uuid) self._folders_by_name.pop(folder.name) self.sys_log.info(f"Deleted folder /{folder.name} and its contents") - self._folder_request_manager.remove_request(folder.uuid) + + # add to deleted list folder.remove_all_files() + self.deleted_folders[folder.uuid] = folder else: _LOGGER.debug(f"Cannot delete folder as it does not exist: {folder_name}") @@ -381,8 +386,6 @@ class FileSystem(SimComponent): file = folder.get_file(file_name) if file: folder.remove_file(file) - self._file_request_manager.remove_request(file.uuid) - self.sys_log.info(f"Deleted file /{file.path}") def move_file(self, src_folder_name: str, src_file_name: str, dst_folder_name: str): """ @@ -470,6 +473,8 @@ class Folder(FileSystemItemABC): "Files stored in the folder." _files_by_name: Dict[str, File] = {} "Files by their name as .." + deleted_files: Dict[str, File] = {} + "Files that have been deleted." scan_duration: int = -1 "How many timesteps to complete a scan." @@ -612,6 +617,8 @@ class Folder(FileSystemItemABC): if self.files.get(file.uuid): self.files.pop(file.uuid) self._files_by_name.pop(file.name) + self.deleted_files[file.uuid] = file + self.fs.sys_log.info(f"Removed file {file.name} (id: {file.uuid})") else: _LOGGER.debug(f"File with UUID {file.uuid} was not found.") @@ -626,6 +633,9 @@ class Folder(FileSystemItemABC): def remove_all_files(self): """Removes all the files in the folder.""" + for file_id in self.files: + self.deleted_files[file_id] = self.files[file_id] + self.files = {} self._files_by_name = {} @@ -635,7 +645,7 @@ class Folder(FileSystemItemABC): The method can take a File object or a file id. - :param file: The file to remove + :param file: The file to restore """ pass @@ -741,9 +751,24 @@ class Folder(FileSystemItemABC): self.fs.sys_log.info(f"Repaired folder {self.name} (id: {self.uuid})") return repaired - def restore(self) -> None: - """TODO.""" - pass + def restore(self) -> bool: + """Restore a File by setting the folder and containing files status to FileSystemItemStatus.GOOD.""" + super().restore() + + restored = False + + # iterate through the files in the folder + for file_id in self.files: + file = self.get_file_by_id(file_uuid=file_id) + restored = file.restore() + + # set file status to corrupt if good + if self.health_status == FileSystemItemHealthStatus.CORRUPT: + self.health_status = FileSystemItemHealthStatus.GOOD + restored = True + + self.fs.sys_log.info(f"Restored folder {self.name} (id: {self.uuid})") + return restored def corrupt(self) -> bool: """Corrupt a File by setting the folder and containing files status to FileSystemItemStatus.CORRUPT.""" @@ -910,9 +935,19 @@ class File(FileSystemItemABC): self.folder.fs.sys_log.info(f"Repaired file {self.sim_path if self.sim_path else path}") return True - def restore(self) -> None: + def restore(self) -> bool: """Restore a corrupted File by setting the status to FileSystemItemStatus.GOOD.""" - pass + super().restore() + + restored = False + + if self.health_status == FileSystemItemHealthStatus.CORRUPT: + self.health_status = FileSystemItemHealthStatus.GOOD + restored = True + + path = self.folder.name + "/" + self.name + self.folder.fs.sys_log.info(f"Restored file {self.sim_path if self.sim_path else path}") + return restored def corrupt(self) -> bool: """Corrupt a File by setting the status to FileSystemItemStatus.CORRUPT.""" diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system.py index 12d9b94c..cb398ca9 100644 --- a/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system.py +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system.py @@ -44,6 +44,7 @@ def test_delete_file(file_system): file_system.delete_file(folder_name="root", file_name="test_file.txt") assert len(file_system.folders) == 1 assert len(file_system.get_folder("root").files) == 0 + assert len(file_system.get_folder("root").deleted_files) == 1 def test_delete_non_existent_file(file_system): @@ -70,6 +71,8 @@ def test_delete_folder(file_system): file_system.delete_folder(folder_name="test_folder") assert len(file_system.folders) == 1 + assert len(file_system.deleted_folders) == 1 + def test_deleting_a_non_existent_folder(file_system): file_system.create_folder(folder_name="test_folder") From b67eb1bb3406a03186a62cfb3076890419f86dd8 Mon Sep 17 00:00:00 2001 From: "Czar.Echavez" Date: Thu, 2 Nov 2023 13:14:08 +0000 Subject: [PATCH 2/9] #1962: separating file system into more managable files --- .../simulator/file_system/file_system.py | 144 +---------------- .../file_system/file_system_item_abc.py | 151 ++++++++++++++++++ 2 files changed, 152 insertions(+), 143 deletions(-) create mode 100644 src/primaite/simulator/file_system/file_system_item_abc.py diff --git a/src/primaite/simulator/file_system/file_system.py b/src/primaite/simulator/file_system/file_system.py index 14a4cf6e..02f3c5e6 100644 --- a/src/primaite/simulator/file_system/file_system.py +++ b/src/primaite/simulator/file_system/file_system.py @@ -2,11 +2,8 @@ from __future__ import annotations import hashlib import json -import math import os.path import shutil -from abc import abstractmethod -from enum import Enum from pathlib import Path from typing import Dict, Optional @@ -14,152 +11,13 @@ from prettytable import MARKDOWN, PrettyTable from primaite import getLogger from primaite.simulator.core import RequestManager, RequestType, SimComponent +from primaite.simulator.file_system.file_system_item_abc import FileSystemItemABC, FileSystemItemHealthStatus from primaite.simulator.file_system.file_type import FileType, get_file_type_from_extension from primaite.simulator.system.core.sys_log import SysLog _LOGGER = getLogger(__name__) -def convert_size(size_bytes: int) -> str: - """ - Convert a file size from bytes to a string with a more human-readable format. - - This function takes the size of a file in bytes and converts it to a string representation with appropriate size - units (B, KB, MB, GB, etc.). - - :param size_bytes: The size of the file in bytes. - :return: The human-readable string representation of the file size. - """ - if size_bytes == 0: - return "0 B" - - # Tuple of size units starting from Bytes up to Yottabytes - size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB") - - # Calculate the index (i) that will be used to select the appropriate size unit from size_name - i = int(math.floor(math.log(size_bytes, 1024))) - - # Calculate the adjusted size value (s) in terms of the new size unit - p = math.pow(1024, i) - s = round(size_bytes / p, 2) - - return f"{s} {size_name[i]}" - - -class FileSystemItemHealthStatus(Enum): - """Status of the FileSystemItem.""" - - GOOD = 0 - """File/Folder is OK.""" - - COMPROMISED = 1 - """File/Folder is quarantined.""" - - CORRUPT = 2 - """File/Folder is corrupted.""" - - RESTORING = 3 - """File/Folder is in the process of being restored.""" - - REPAIRING = 3 - """File/Folder is in the process of being repaired.""" - - -class FileSystemItemABC(SimComponent): - """ - Abstract base class for file system items used in the file system simulation. - - :ivar name: The name of the FileSystemItemABC. - """ - - name: str - "The name of the FileSystemItemABC." - - health_status: FileSystemItemHealthStatus = FileSystemItemHealthStatus.GOOD - "Actual status of the current FileSystemItem" - - visible_health_status: FileSystemItemHealthStatus = FileSystemItemHealthStatus.GOOD - "Visible status of the current FileSystemItem" - - previous_hash: Optional[str] = None - "Hash of the file contents or the description state" - - revealed_to_red: bool = False - "If true, the folder/file has been revealed to the red agent." - - def describe_state(self) -> Dict: - """ - Produce a dictionary describing the current state of this object. - - :return: Current state of this object and child objects. - """ - state = super().describe_state() - state["name"] = self.name - state["status"] = self.health_status.name - state["visible_status"] = self.visible_health_status.name - state["previous_hash"] = self.previous_hash - state["revealed_to_red"] = self.revealed_to_red - return state - - def _init_request_manager(self) -> RequestManager: - rm = super()._init_request_manager() - - rm.add_request(name="scan", request_type=RequestType(func=lambda request, context: self.scan())) - rm.add_request(name="checkhash", request_type=RequestType(func=lambda request, context: self.check_hash())) - rm.add_request(name="repair", request_type=RequestType(func=lambda request, context: self.repair())) - rm.add_request(name="restore", request_type=RequestType(func=lambda request, context: self.restore())) - - rm.add_request(name="corrupt", request_type=RequestType(func=lambda request, context: self.corrupt())) - - return rm - - @property - def size_str(self) -> str: - """ - Get the file size in a human-readable string format. - - This property makes use of the :func:`convert_size` function to convert the `self.size` attribute to a string - that is easier to read and understand. - - :return: The human-readable string representation of the file size. - """ - return convert_size(self.size) - - @abstractmethod - def check_hash(self) -> bool: - """ - Checks the has of the file to detect any changes. - - For current implementation, any change in file hash means it is compromised. - - Return False if corruption is detected, otherwise True - """ - pass - - @abstractmethod - def repair(self) -> bool: - """ - Repair the FileSystemItem. - - True if successfully repaired. False otherwise. - """ - pass - - @abstractmethod - def corrupt(self) -> bool: - """ - Corrupt the FileSystemItem. - - True if successfully corrupted. False otherwise. - """ - pass - - @abstractmethod - def restore(self) -> bool: - """Restore the file/folder to the state before it got ruined.""" - pass - - class FileSystem(SimComponent): """Class that contains all the simulation File System.""" diff --git a/src/primaite/simulator/file_system/file_system_item_abc.py b/src/primaite/simulator/file_system/file_system_item_abc.py new file mode 100644 index 00000000..8f61c718 --- /dev/null +++ b/src/primaite/simulator/file_system/file_system_item_abc.py @@ -0,0 +1,151 @@ +from __future__ import annotations + +import math +from abc import abstractmethod +from enum import Enum +from typing import Dict, Optional + +from primaite import getLogger +from primaite.simulator.core import RequestManager, RequestType, SimComponent + +_LOGGER = getLogger(__name__) + + +def convert_size(size_bytes: int) -> str: + """ + Convert a file size from bytes to a string with a more human-readable format. + + This function takes the size of a file in bytes and converts it to a string representation with appropriate size + units (B, KB, MB, GB, etc.). + + :param size_bytes: The size of the file in bytes. + :return: The human-readable string representation of the file size. + """ + if size_bytes == 0: + return "0 B" + + # Tuple of size units starting from Bytes up to Yottabytes + size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB") + + # Calculate the index (i) that will be used to select the appropriate size unit from size_name + i = int(math.floor(math.log(size_bytes, 1024))) + + # Calculate the adjusted size value (s) in terms of the new size unit + p = math.pow(1024, i) + s = round(size_bytes / p, 2) + + return f"{s} {size_name[i]}" + + +class FileSystemItemHealthStatus(Enum): + """Status of the FileSystemItem.""" + + GOOD = 0 + """File/Folder is OK.""" + + COMPROMISED = 1 + """File/Folder is quarantined.""" + + CORRUPT = 2 + """File/Folder is corrupted.""" + + RESTORING = 3 + """File/Folder is in the process of being restored.""" + + REPAIRING = 3 + """File/Folder is in the process of being repaired.""" + + +class FileSystemItemABC(SimComponent): + """ + Abstract base class for file system items used in the file system simulation. + + :ivar name: The name of the FileSystemItemABC. + """ + + name: str + "The name of the FileSystemItemABC." + + health_status: FileSystemItemHealthStatus = FileSystemItemHealthStatus.GOOD + "Actual status of the current FileSystemItem" + + visible_health_status: FileSystemItemHealthStatus = FileSystemItemHealthStatus.GOOD + "Visible status of the current FileSystemItem" + + previous_hash: Optional[str] = None + "Hash of the file contents or the description state" + + revealed_to_red: bool = False + "If true, the folder/file has been revealed to the red agent." + + def describe_state(self) -> Dict: + """ + Produce a dictionary describing the current state of this object. + + :return: Current state of this object and child objects. + """ + state = super().describe_state() + state["name"] = self.name + state["status"] = self.health_status.name + state["visible_status"] = self.visible_health_status.name + state["previous_hash"] = self.previous_hash + state["revealed_to_red"] = self.revealed_to_red + return state + + def _init_request_manager(self) -> RequestManager: + rm = super()._init_request_manager() + + rm.add_request(name="scan", request_type=RequestType(func=lambda request, context: self.scan())) + rm.add_request(name="checkhash", request_type=RequestType(func=lambda request, context: self.check_hash())) + rm.add_request(name="repair", request_type=RequestType(func=lambda request, context: self.repair())) + rm.add_request(name="restore", request_type=RequestType(func=lambda request, context: self.restore())) + + rm.add_request(name="corrupt", request_type=RequestType(func=lambda request, context: self.corrupt())) + + return rm + + @property + def size_str(self) -> str: + """ + Get the file size in a human-readable string format. + + This property makes use of the :func:`convert_size` function to convert the `self.size` attribute to a string + that is easier to read and understand. + + :return: The human-readable string representation of the file size. + """ + return convert_size(self.size) + + @abstractmethod + def check_hash(self) -> bool: + """ + Checks the has of the file to detect any changes. + + For current implementation, any change in file hash means it is compromised. + + Return False if corruption is detected, otherwise True + """ + pass + + @abstractmethod + def repair(self) -> bool: + """ + Repair the FileSystemItem. + + True if successfully repaired. False otherwise. + """ + pass + + @abstractmethod + def corrupt(self) -> bool: + """ + Corrupt the FileSystemItem. + + True if successfully corrupted. False otherwise. + """ + pass + + @abstractmethod + def restore(self) -> bool: + """Restore the file/folder to the state before it got ruined.""" + pass From b2c3e273b75b11c57e3956a7a1441314e16c871a Mon Sep 17 00:00:00 2001 From: "Czar.Echavez" Date: Thu, 2 Nov 2023 15:10:51 +0000 Subject: [PATCH 3/9] #1962: separating file system into more managable files --- src/primaite/simulator/file_system/file.py | 185 +++++ .../simulator/file_system/file_system.py | 661 +++--------------- .../file_system/file_system_item_abc.py | 4 + src/primaite/simulator/file_system/folder.py | 347 +++++++++ .../_simulator/_file_system/test_file.py | 78 +++ .../_file_system/test_file_system.py | 198 +----- .../_file_system/test_file_system_actions.py | 3 +- .../_simulator/_file_system/test_folder.py | 133 ++++ .../_network/_hardware/test_node_actions.py | 3 +- 9 files changed, 841 insertions(+), 771 deletions(-) create mode 100644 src/primaite/simulator/file_system/file.py create mode 100644 src/primaite/simulator/file_system/folder.py create mode 100644 tests/unit_tests/_primaite/_simulator/_file_system/test_file.py create mode 100644 tests/unit_tests/_primaite/_simulator/_file_system/test_folder.py diff --git a/src/primaite/simulator/file_system/file.py b/src/primaite/simulator/file_system/file.py new file mode 100644 index 00000000..0da3c9ab --- /dev/null +++ b/src/primaite/simulator/file_system/file.py @@ -0,0 +1,185 @@ +from __future__ import annotations + +import hashlib +import json +import os.path +from pathlib import Path +from typing import Dict, Optional + +from primaite import getLogger +from primaite.simulator.file_system.file_system_item_abc import FileSystemItemABC, FileSystemItemHealthStatus +from primaite.simulator.file_system.file_type import FileType, get_file_type_from_extension + +_LOGGER = getLogger(__name__) + + +class File(FileSystemItemABC): + """ + Class representing a file in the simulation. + + :ivar Folder folder: The folder in which the file resides. + :ivar FileType file_type: The type of the file. + :ivar Optional[int] sim_size: The simulated file size. + :ivar bool real: Indicates if the file is actually a real file in the Node sim fs output. + :ivar Optional[Path] sim_path: The path if the file is real. + """ + + folder_id: str + "The id of the Folder the File is in." + folder_name: str + "The name of the Folder the file is in." + file_type: FileType + "The type of File." + sim_size: Optional[int] = None + "The simulated file size." + real: bool = False + "Indicates whether the File is actually a real file in the Node sim fs output." + sim_path: Optional[Path] = None + "The Path if real is True." + sim_root: Optional[Path] = None + "Root path of the simulation." + + def __init__(self, **kwargs): + """ + Initialise File class. + + :param name: The name of the file. + :param file_type: The FileType of the file + :param size: The size of the FileSystemItemABC + """ + has_extension = "." in kwargs["name"] + + # Attempt to use the file type extension to set/override the FileType + if has_extension: + extension = kwargs["name"].split(".")[-1] + kwargs["file_type"] = get_file_type_from_extension(extension) + else: + # If the file name does not have a extension, override file type to FileType.UNKNOWN + if not kwargs["file_type"]: + kwargs["file_type"] = FileType.UNKNOWN + if kwargs["file_type"] != FileType.UNKNOWN: + kwargs["name"] = f"{kwargs['name']}.{kwargs['file_type'].name.lower()}" + + # set random file size if none provided + if not kwargs.get("sim_size"): + kwargs["sim_size"] = kwargs["file_type"].default_size + super().__init__(**kwargs) + if self.real: + self.sim_path = self.sim_root / self.path + if not self.sim_path.exists(): + self.sim_path.parent.mkdir(exist_ok=True, parents=True) + with open(self.sim_path, mode="a"): + pass + + self.sys_log.info(f"Created file /{self.path} (id: {self.uuid})") + + @property + def path(self) -> str: + """ + Get the path of the file in the file system. + + :return: The full path of the file. + """ + return f"{self.folder_name}/{self.name}" + + @property + def size(self) -> int: + """ + Get the size of the file in bytes. + + :return: The size of the file in bytes. + """ + if self.real: + return os.path.getsize(self.sim_path) + return self.sim_size + + def describe_state(self) -> Dict: + """Produce a dictionary describing the current state of this object.""" + state = super().describe_state() + state["size"] = self.size + state["file_type"] = self.file_type.name + return state + + def scan(self) -> None: + """Updates the visible statuses of the file.""" + path = self.folder.name + "/" + self.name + self.sys_log.info(f"Scanning file {self.sim_path if self.sim_path else path}") + self.visible_health_status = self.health_status + + def reveal_to_red(self): + """Reveals the folder/file to the red agent.""" + self.revealed_to_red = True + + def check_hash(self) -> bool: + """ + Check if the file has been changed. + + If changed, the file is considered corrupted. + + Return False if corruption is detected, otherwise True + """ + current_hash = None + + # if file is real, read the file contents + if self.real: + with open(self.sim_path, "rb") as f: + file_hash = hashlib.blake2b() + while chunk := f.read(8192): + file_hash.update(chunk) + + current_hash = file_hash.hexdigest() + else: + # otherwise get describe_state dict and hash that + current_hash = hashlib.blake2b(json.dumps(self.describe_state(), sort_keys=True).encode()).hexdigest() + + # if the previous hash is None, set the current hash to previous + if self.previous_hash is None: + self.previous_hash = current_hash + + # if the previous hash and current hash do not match, mark file as corrupted + if self.previous_hash is not current_hash: + self.corrupt() + return False + + return True + + def repair(self) -> bool: + """Repair a corrupted File by setting the status to FileSystemItemStatus.GOOD.""" + super().repair() + + # set file status to good if corrupt + if self.health_status == FileSystemItemHealthStatus.CORRUPT: + self.health_status = FileSystemItemHealthStatus.GOOD + + path = self.folder.name + "/" + self.name + self.sys_log.info(f"Repaired file {self.sim_path if self.sim_path else path}") + return True + + def restore(self) -> bool: + """Restore a corrupted File by setting the status to FileSystemItemStatus.GOOD.""" + super().restore() + + restored = False + + if self.health_status == FileSystemItemHealthStatus.CORRUPT: + self.health_status = FileSystemItemHealthStatus.GOOD + restored = True + + path = self.folder.name + "/" + self.name + self.sys_log.info(f"Restored file {self.sim_path if self.sim_path else path}") + return restored + + def corrupt(self) -> bool: + """Corrupt a File by setting the status to FileSystemItemStatus.CORRUPT.""" + super().corrupt() + + corrupted = False + + # set file status to good if corrupt + if self.health_status == FileSystemItemHealthStatus.GOOD: + self.health_status = FileSystemItemHealthStatus.CORRUPT + corrupted = True + + path = self.folder.name + "/" + self.name + self.sys_log.info(f"Corrupted file {self.sim_path if self.sim_path else path}") + return corrupted diff --git a/src/primaite/simulator/file_system/file_system.py b/src/primaite/simulator/file_system/file_system.py index 02f3c5e6..16c9992c 100644 --- a/src/primaite/simulator/file_system/file_system.py +++ b/src/primaite/simulator/file_system/file_system.py @@ -1,8 +1,5 @@ from __future__ import annotations -import hashlib -import json -import os.path import shutil from pathlib import Path from typing import Dict, Optional @@ -11,8 +8,9 @@ from prettytable import MARKDOWN, PrettyTable from primaite import getLogger from primaite.simulator.core import RequestManager, RequestType, SimComponent -from primaite.simulator.file_system.file_system_item_abc import FileSystemItemABC, FileSystemItemHealthStatus -from primaite.simulator.file_system.file_type import FileType, get_file_type_from_extension +from primaite.simulator.file_system.file import File +from primaite.simulator.file_system.file_type import FileType +from primaite.simulator.file_system.folder import Folder from primaite.simulator.system.core.sys_log import SysLog _LOGGER = getLogger(__name__) @@ -27,7 +25,9 @@ class FileSystem(SimComponent): "List containing all the folders that have been deleted." _folders_by_name: Dict[str, Folder] = {} sys_log: SysLog + "Instance of SysLog used to create system logs." sim_root: Path + "Root path of the simulation." def __init__(self, **kwargs): super().__init__(**kwargs) @@ -86,42 +86,9 @@ class FileSystem(SimComponent): else: print(table.get_string(sortby="Folder")) - def describe_state(self) -> Dict: - """ - Produce a dictionary describing the current state of this object. - - :return: Current state of this object and child objects. - """ - state = super().describe_state() - state["folders"] = {folder.name: folder.describe_state() for folder in self.folders.values()} - return state - - def apply_timestep(self, timestep: int) -> None: - """Apply time step to FileSystem and its child folders and files.""" - super().apply_timestep(timestep=timestep) - - # apply timestep to folders - for folder_id in self.folders: - self.folders[folder_id].apply_timestep(timestep=timestep) - - def scan(self, instant_scan: bool = False): - """ - Scan all the folders (and child files) in the file system. - - :param: instant_scan: If True, the scan is completed instantly and ignores scan duration. Default False. - """ - for folder_id in self.folders: - self.folders[folder_id].scan(instant_scan=instant_scan) - - def reveal_to_red(self, instant_scan: bool = False): - """ - Reveals all the folders (and child files) in the file system to the red agent. - - :param: instant_scan: If True, the scan is completed instantly and ignores scan duration. Default False. - """ - for folder_id in self.folders: - self.folders[folder_id].reveal_to_red(instant_scan=instant_scan) - + ############################################################### + # Folder methods + ############################################################### def create_folder(self, folder_name: str) -> Folder: """ Creates a Folder and adds it to the list of folders. @@ -132,11 +99,10 @@ class FileSystem(SimComponent): if self.get_folder(folder_name): raise Exception(f"Cannot create folder as it already exists: {folder_name}") - folder = Folder(name=folder_name, fs=self) + folder = Folder(name=folder_name, sys_log=self.sys_log) self.folders[folder.uuid] = folder self._folders_by_name[folder.name] = folder - self.sys_log.info(f"Created folder /{folder.name}") self._folder_request_manager.add_request( name=folder.uuid, request_type=RequestType(func=folder._request_manager) ) @@ -174,9 +140,27 @@ class FileSystem(SimComponent): folder = self.get_folder_by_id(folder_uuid=folder_uuid) self.delete_folder(folder_name=folder.name) - def restore_folder(self, folder_id: str): - """TODO.""" - pass + def get_folder(self, folder_name: str) -> Optional[Folder]: + """ + Get a folder by its name if it exists. + + :param folder_name: The folder name. + :return: The matching Folder. + """ + return self._folders_by_name.get(folder_name) + + def get_folder_by_id(self, folder_uuid: str) -> Optional[Folder]: + """ + Get a folder by its uuid if it exists. + + :param: folder_uuid: The folder uuid. + :return: The matching Folder. + """ + return self.folders.get(folder_uuid) + + ############################################################### + # File methods + ############################################################### def create_file( self, @@ -210,12 +194,14 @@ class FileSystem(SimComponent): name=file_name, sim_size=size, file_type=file_type, - folder=folder, + folder_id=folder.uuid, + folder_name=folder.name, real=real, sim_path=self.sim_root if real else None, + sim_root=self.sim_root, + sys_log=self.sys_log, ) folder.add_file(file) - self.sys_log.info(f"Created file /{file.path}") self._file_request_manager.add_request(name=file.uuid, request_type=RequestType(func=file._request_manager)) return file @@ -266,7 +252,7 @@ class FileSystem(SimComponent): dst_folder.add_file(file) if file.real: old_sim_path = file.sim_path - file.sim_path = file.folder.fs.sim_root / file.path + file.sim_path = file.sim_root / file.path file.sim_path.parent.mkdir(exist_ok=True) shutil.move(old_sim_path, file.sim_path) @@ -280,14 +266,64 @@ class FileSystem(SimComponent): """ file = self.get_file(folder_name=src_folder_name, file_name=src_file_name) if file: + # check that dest folder exists dst_folder = self.get_folder(folder_name=dst_folder_name) if not dst_folder: + # create dest folder dst_folder = self.create_folder(dst_folder_name) - new_file = file.make_copy(dst_folder=dst_folder) - dst_folder.add_file(new_file) + + file_copy = File( + folder_id=dst_folder.uuid, + folder_name=dst_folder.name, + **file.model_dump(exclude={"uuid", "folder_id", "folder_name", "sim_path"}), + ) + dst_folder.add_file(file_copy) + if file.real: - new_file.sim_path.parent.mkdir(exist_ok=True) - shutil.copy2(file.sim_path, new_file.sim_path) + file_copy.sim_path.parent.mkdir(exist_ok=True) + shutil.copy2(file.sim_path, file_copy.sim_path) + else: + self.sys_log.error(f"Unable to copy file. {src_file_name} does not exist.") + + def describe_state(self) -> Dict: + """ + Produce a dictionary describing the current state of this object. + + :return: Current state of this object and child objects. + """ + state = super().describe_state() + state["folders"] = {folder.name: folder.describe_state() for folder in self.folders.values()} + return state + + def apply_timestep(self, timestep: int) -> None: + """Apply time step to FileSystem and its child folders and files.""" + super().apply_timestep(timestep=timestep) + + # apply timestep to folders + for folder_id in self.folders: + self.folders[folder_id].apply_timestep(timestep=timestep) + + def scan(self, instant_scan: bool = False): + """ + Scan all the folders (and child files) in the file system. + + :param: instant_scan: If True, the scan is completed instantly and ignores scan duration. Default False. + """ + for folder_id in self.folders: + self.folders[folder_id].scan(instant_scan=instant_scan) + + def reveal_to_red(self, instant_scan: bool = False): + """ + Reveals all the folders (and child files) in the file system to the red agent. + + :param: instant_scan: If True, the scan is completed instantly and ignores scan duration. Default False. + """ + for folder_id in self.folders: + self.folders[folder_id].reveal_to_red(instant_scan=instant_scan) + + def restore_folder(self, folder_id: str): + """TODO.""" + pass def restore_file(self, folder_id: str, file_id: str): """ @@ -302,522 +338,3 @@ class FileSystem(SimComponent): :type: folder_id: str """ pass - - def get_folder(self, folder_name: str) -> Optional[Folder]: - """ - Get a folder by its name if it exists. - - :param folder_name: The folder name. - :return: The matching Folder. - """ - return self._folders_by_name.get(folder_name) - - def get_folder_by_id(self, folder_uuid: str) -> Optional[Folder]: - """ - Get a folder by its uuid if it exists. - - :param: folder_uuid: The folder uuid. - :return: The matching Folder. - """ - return self.folders.get(folder_uuid) - - -class Folder(FileSystemItemABC): - """Simulation Folder.""" - - fs: FileSystem - "The FileSystem the Folder is in." - files: Dict[str, File] = {} - "Files stored in the folder." - _files_by_name: Dict[str, File] = {} - "Files by their name as .." - deleted_files: Dict[str, File] = {} - "Files that have been deleted." - - scan_duration: int = -1 - "How many timesteps to complete a scan." - - red_scan_duration: int = -1 - "How many timesteps to complete reveal to red scan." - - def _init_request_manager(self) -> RequestManager: - rm = super()._init_request_manager() - rm.add_request( - name="delete", - request_type=RequestType(func=lambda request, context: self.remove_file_by_id(file_uuid=request[0])), - ) - return rm - - def describe_state(self) -> Dict: - """ - Produce a dictionary describing the current state of this object. - - :return: Current state of this object and child objects. - """ - state = super().describe_state() - state["files"] = {file.name: file.describe_state() for uuid, file in self.files.items()} - return state - - def show(self, markdown: bool = False): - """ - Display the contents of the Folder in tabular format. - - :param markdown: Whether to display the table in Markdown format or not. Default is `False`. - """ - table = PrettyTable(["File", "Size"]) - if markdown: - table.set_style(MARKDOWN) - table.align = "l" - table.title = f"{self.fs.sys_log.hostname} File System Folder ({self.name})" - for file in self.files.values(): - table.add_row([file.name, file.size_str]) - print(table.get_string(sortby="File")) - - @property - def size(self) -> int: - """ - Calculate and return the total size of all files in the folder. - - :return: The total size of all files in the folder. If no files exist or all have `None` - size, returns 0. - """ - return sum(file.size for file in self.files.values() if file.size is not None) - - def apply_timestep(self, timestep: int): - """ - Apply a single timestep of simulation dynamics to this folder and its files. - - In this instance, if any multi-timestep processes are currently occurring (such as scanning), - then they are brought one step closer to being finished. - - :param timestep: The current timestep number. (Amount of time since simulation episode began) - :type timestep: int - """ - super().apply_timestep(timestep=timestep) - - # scan files each timestep - if self.scan_duration >= 0: - self.scan_duration -= 1 - - if self.scan_duration == 0: - for file_id in self.files: - file = self.get_file_by_id(file_uuid=file_id) - file.scan() - if file.visible_health_status == FileSystemItemHealthStatus.CORRUPT: - self.visible_health_status = FileSystemItemHealthStatus.CORRUPT - - # red scan file at each step - if self.red_scan_duration >= 0: - self.red_scan_duration -= 1 - - if self.red_scan_duration == 0: - self.revealed_to_red = True - for file_id in self.files: - file = self.get_file_by_id(file_uuid=file_id) - file.reveal_to_red() - - # apply timestep to files in folder - for file_id in self.files: - self.files[file_id].apply_timestep(timestep=timestep) - - def get_file(self, file_name: str) -> Optional[File]: - """ - Get a file by its name. - - File name must be the filename and prefix, like 'memo.docx'. - - :param file_name: The file name. - :return: The matching File. - """ - # TODO: Increment read count? - return self._files_by_name.get(file_name) - - def get_file_by_id(self, file_uuid: str) -> File: - """ - Get a file by its uuid. - - :param: file_uuid: The file uuid. - :return: The matching File. - """ - return self.files.get(file_uuid) - - def add_file(self, file: File): - """ - Adds a file to the folder. - - :param File file: The File object to be added to the folder. - :raises Exception: If the provided `file` parameter is None or not an instance of the - `File` class. - """ - if file is None or not isinstance(file, File): - raise Exception(f"Invalid file: {file}") - - # check if file with id already exists in folder - if file.uuid in self.files: - _LOGGER.debug(f"File with id {file.uuid} already exists in folder") - else: - # add to list - self.files[file.uuid] = file - self._files_by_name[file.name] = file - file.folder = self - - def remove_file(self, file: Optional[File]): - """ - Removes a file from the folder list. - - The method can take a File object or a file id. - - :param file: The file to remove - """ - if file is None or not isinstance(file, File): - raise Exception(f"Invalid file: {file}") - - if self.files.get(file.uuid): - self.files.pop(file.uuid) - self._files_by_name.pop(file.name) - self.deleted_files[file.uuid] = file - self.fs.sys_log.info(f"Removed file {file.name} (id: {file.uuid})") - else: - _LOGGER.debug(f"File with UUID {file.uuid} was not found.") - - def remove_file_by_id(self, file_uuid: str): - """ - Remove a file using id. - - :param: file_uuid: The UUID of the file to remove. - """ - file = self.get_file_by_id(file_uuid=file_uuid) - self.remove_file(file=file) - - def remove_all_files(self): - """Removes all the files in the folder.""" - for file_id in self.files: - self.deleted_files[file_id] = self.files[file_id] - - self.files = {} - self._files_by_name = {} - - def restore_file(self, file: Optional[File]): - """ - Restores a file. - - The method can take a File object or a file id. - - :param file: The file to restore - """ - pass - - def quarantine(self): - """Quarantines the File System Folder.""" - pass - - def unquarantine(self): - """Unquarantine of the File System Folder.""" - pass - - def quarantine_status(self) -> bool: - """Returns true if the folder is being quarantined.""" - pass - - def scan(self, instant_scan: bool = False) -> None: - """ - Update Folder visible status. - - :param: instant_scan: If True, the scan is completed instantly and ignores scan duration. Default False. - """ - if instant_scan: - for file_id in self.files: - file = self.get_file_by_id(file_uuid=file_id) - file.scan() - if file.visible_health_status == FileSystemItemHealthStatus.CORRUPT: - self.visible_health_status = FileSystemItemHealthStatus.CORRUPT - return - - if self.scan_duration <= 0: - # scan one file per timestep - self.scan_duration = len(self.files) - self.fs.sys_log.info(f"Scanning folder {self.name} (id: {self.uuid})") - else: - # scan already in progress - self.fs.sys_log.info(f"Scan is already in progress {self.name} (id: {self.uuid})") - - def reveal_to_red(self, instant_scan: bool = False): - """ - Reveals the folders and files to the red agent. - - :param: instant_scan: If True, the scan is completed instantly and ignores scan duration. Default False. - """ - if instant_scan: - self.revealed_to_red = True - for file_id in self.files: - file = self.get_file_by_id(file_uuid=file_id) - file.reveal_to_red() - return - - if self.red_scan_duration <= 0: - # scan one file per timestep - self.red_scan_duration = len(self.files) - self.fs.sys_log.info(f"Folder revealed to red agent: {self.name} (id: {self.uuid})") - else: - # scan already in progress - self.fs.sys_log.info(f"Red Agent Scan is already in progress {self.name} (id: {self.uuid})") - - def check_hash(self) -> bool: - """ - Runs a :func:`check_hash` on all files in the folder. - - If a file under the folder is corrupted, the whole folder is considered corrupted. - - TODO: For now this will just iterate through the files and run :func:`check_hash` and ignores - any other changes to the folder - - Return False if corruption is detected, otherwise True - """ - super().check_hash() - - # iterate through the files and run a check hash - no_corrupted_files = True - - for file_id in self.files: - file = self.get_file_by_id(file_uuid=file_id) - no_corrupted_files = file.check_hash() - - # if one file in the folder is corrupted, set the folder status to corrupted - if not no_corrupted_files: - self.corrupt() - - self.fs.sys_log.info(f"Checking hash of folder {self.name} (id: {self.uuid})") - - return no_corrupted_files - - def repair(self) -> bool: - """Repair a corrupted Folder by setting the folder and containing files status to FileSystemItemStatus.GOOD.""" - super().repair() - - repaired = False - - # iterate through the files in the folder - for file_id in self.files: - file = self.get_file_by_id(file_uuid=file_id) - repaired = file.repair() - - # set file status to good if corrupt - if self.health_status == FileSystemItemHealthStatus.CORRUPT: - self.health_status = FileSystemItemHealthStatus.GOOD - repaired = True - - self.fs.sys_log.info(f"Repaired folder {self.name} (id: {self.uuid})") - return repaired - - def restore(self) -> bool: - """Restore a File by setting the folder and containing files status to FileSystemItemStatus.GOOD.""" - super().restore() - - restored = False - - # iterate through the files in the folder - for file_id in self.files: - file = self.get_file_by_id(file_uuid=file_id) - restored = file.restore() - - # set file status to corrupt if good - if self.health_status == FileSystemItemHealthStatus.CORRUPT: - self.health_status = FileSystemItemHealthStatus.GOOD - restored = True - - self.fs.sys_log.info(f"Restored folder {self.name} (id: {self.uuid})") - return restored - - def corrupt(self) -> bool: - """Corrupt a File by setting the folder and containing files status to FileSystemItemStatus.CORRUPT.""" - super().corrupt() - - corrupted = False - - # iterate through the files in the folder - for file_id in self.files: - file = self.get_file_by_id(file_uuid=file_id) - corrupted = file.corrupt() - - # set file status to corrupt if good - if self.health_status == FileSystemItemHealthStatus.GOOD: - self.health_status = FileSystemItemHealthStatus.CORRUPT - corrupted = True - - self.fs.sys_log.info(f"Corrupted folder {self.name} (id: {self.uuid})") - return corrupted - - -class File(FileSystemItemABC): - """ - Class representing a file in the simulation. - - :ivar Folder folder: The folder in which the file resides. - :ivar FileType file_type: The type of the file. - :ivar Optional[int] sim_size: The simulated file size. - :ivar bool real: Indicates if the file is actually a real file in the Node sim fs output. - :ivar Optional[Path] sim_path: The path if the file is real. - """ - - folder: Folder - "The Folder the File is in." - file_type: FileType - "The type of File." - sim_size: Optional[int] = None - "The simulated file size." - real: bool = False - "Indicates whether the File is actually a real file in the Node sim fs output." - sim_path: Optional[Path] = None - "The Path if real is True." - - def __init__(self, **kwargs): - """ - Initialise File class. - - :param name: The name of the file. - :param file_type: The FileType of the file - :param size: The size of the FileSystemItemABC - """ - has_extension = "." in kwargs["name"] - - # Attempt to use the file type extension to set/override the FileType - if has_extension: - extension = kwargs["name"].split(".")[-1] - kwargs["file_type"] = get_file_type_from_extension(extension) - else: - # If the file name does not have a extension, override file type to FileType.UNKNOWN - if not kwargs["file_type"]: - kwargs["file_type"] = FileType.UNKNOWN - if kwargs["file_type"] != FileType.UNKNOWN: - kwargs["name"] = f"{kwargs['name']}.{kwargs['file_type'].name.lower()}" - - # set random file size if none provided - if not kwargs.get("sim_size"): - kwargs["sim_size"] = kwargs["file_type"].default_size - super().__init__(**kwargs) - if self.real: - self.sim_path = self.folder.fs.sim_root / self.path - if not self.sim_path.exists(): - self.sim_path.parent.mkdir(exist_ok=True, parents=True) - with open(self.sim_path, mode="a"): - pass - - def make_copy(self, dst_folder: Folder) -> File: - """ - Create a copy of the current File object in the given destination folder. - - :param Folder dst_folder: The destination folder for the copied file. - :return: A new File object that is a copy of the current file. - """ - return File(folder=dst_folder, **self.model_dump(exclude={"uuid", "folder", "sim_path"})) - - @property - def path(self) -> str: - """ - Get the path of the file in the file system. - - :return: The full path of the file. - """ - return f"{self.folder.name}/{self.name}" - - @property - def size(self) -> int: - """ - Get the size of the file in bytes. - - :return: The size of the file in bytes. - """ - if self.real: - return os.path.getsize(self.sim_path) - return self.sim_size - - def describe_state(self) -> Dict: - """Produce a dictionary describing the current state of this object.""" - state = super().describe_state() - state["size"] = self.size - state["file_type"] = self.file_type.name - return state - - def scan(self) -> None: - """Updates the visible statuses of the file.""" - path = self.folder.name + "/" + self.name - self.folder.fs.sys_log.info(f"Scanning file {self.sim_path if self.sim_path else path}") - self.visible_health_status = self.health_status - - def reveal_to_red(self): - """Reveals the folder/file to the red agent.""" - self.revealed_to_red = True - - def check_hash(self) -> bool: - """ - Check if the file has been changed. - - If changed, the file is considered corrupted. - - Return False if corruption is detected, otherwise True - """ - current_hash = None - - # if file is real, read the file contents - if self.real: - with open(self.sim_path, "rb") as f: - file_hash = hashlib.blake2b() - while chunk := f.read(8192): - file_hash.update(chunk) - - current_hash = file_hash.hexdigest() - else: - # otherwise get describe_state dict and hash that - current_hash = hashlib.blake2b(json.dumps(self.describe_state(), sort_keys=True).encode()).hexdigest() - - # if the previous hash is None, set the current hash to previous - if self.previous_hash is None: - self.previous_hash = current_hash - - # if the previous hash and current hash do not match, mark file as corrupted - if self.previous_hash is not current_hash: - self.corrupt() - return False - - return True - - def repair(self) -> bool: - """Repair a corrupted File by setting the status to FileSystemItemStatus.GOOD.""" - super().repair() - - # set file status to good if corrupt - if self.health_status == FileSystemItemHealthStatus.CORRUPT: - self.health_status = FileSystemItemHealthStatus.GOOD - - path = self.folder.name + "/" + self.name - self.folder.fs.sys_log.info(f"Repaired file {self.sim_path if self.sim_path else path}") - return True - - def restore(self) -> bool: - """Restore a corrupted File by setting the status to FileSystemItemStatus.GOOD.""" - super().restore() - - restored = False - - if self.health_status == FileSystemItemHealthStatus.CORRUPT: - self.health_status = FileSystemItemHealthStatus.GOOD - restored = True - - path = self.folder.name + "/" + self.name - self.folder.fs.sys_log.info(f"Restored file {self.sim_path if self.sim_path else path}") - return restored - - def corrupt(self) -> bool: - """Corrupt a File by setting the status to FileSystemItemStatus.CORRUPT.""" - super().corrupt() - - corrupted = False - - # set file status to good if corrupt - if self.health_status == FileSystemItemHealthStatus.GOOD: - self.health_status = FileSystemItemHealthStatus.CORRUPT - corrupted = True - - path = self.folder.name + "/" + self.name - self.folder.fs.sys_log.info(f"Corrupted file {self.sim_path if self.sim_path else path}") - return corrupted diff --git a/src/primaite/simulator/file_system/file_system_item_abc.py b/src/primaite/simulator/file_system/file_system_item_abc.py index 8f61c718..c0d65139 100644 --- a/src/primaite/simulator/file_system/file_system_item_abc.py +++ b/src/primaite/simulator/file_system/file_system_item_abc.py @@ -7,6 +7,7 @@ from typing import Dict, Optional from primaite import getLogger from primaite.simulator.core import RequestManager, RequestType, SimComponent +from primaite.simulator.system.core.sys_log import SysLog _LOGGER = getLogger(__name__) @@ -78,6 +79,9 @@ class FileSystemItemABC(SimComponent): revealed_to_red: bool = False "If true, the folder/file has been revealed to the red agent." + sys_log: SysLog + "Used for creating system logs." + def describe_state(self) -> Dict: """ Produce a dictionary describing the current state of this object. diff --git a/src/primaite/simulator/file_system/folder.py b/src/primaite/simulator/file_system/folder.py new file mode 100644 index 00000000..6922cad1 --- /dev/null +++ b/src/primaite/simulator/file_system/folder.py @@ -0,0 +1,347 @@ +from __future__ import annotations + +from typing import Dict, Optional + +from prettytable import MARKDOWN, PrettyTable + +from primaite import getLogger +from primaite.simulator.core import RequestManager, RequestType +from primaite.simulator.file_system.file import File +from primaite.simulator.file_system.file_system_item_abc import FileSystemItemABC, FileSystemItemHealthStatus + +_LOGGER = getLogger(__name__) + + +class Folder(FileSystemItemABC): + """Simulation Folder.""" + + files: Dict[str, File] = {} + "Files stored in the folder." + _files_by_name: Dict[str, File] = {} + "Files by their name as .." + deleted_files: Dict[str, File] = {} + "Files that have been deleted." + + scan_duration: int = -1 + "How many timesteps to complete a scan." + + red_scan_duration: int = -1 + "How many timesteps to complete reveal to red scan." + + def __init__(self, **kwargs): + """ + Initialise Folder class. + + :param name: The name of the folder. + :param sys_log: The SysLog instance to us to create system logs. + """ + super().__init__(**kwargs) + + self.sys_log.info(f"Created file /{self.name} (id: {self.uuid})") + + def _init_request_manager(self) -> RequestManager: + rm = super()._init_request_manager() + rm.add_request( + name="delete", + request_type=RequestType(func=lambda request, context: self.remove_file_by_id(file_uuid=request[0])), + ) + return rm + + def describe_state(self) -> Dict: + """ + Produce a dictionary describing the current state of this object. + + :return: Current state of this object and child objects. + """ + state = super().describe_state() + state["files"] = {file.name: file.describe_state() for uuid, file in self.files.items()} + return state + + def show(self, markdown: bool = False): + """ + Display the contents of the Folder in tabular format. + + :param markdown: Whether to display the table in Markdown format or not. Default is `False`. + """ + table = PrettyTable(["File", "Size"]) + if markdown: + table.set_style(MARKDOWN) + table.align = "l" + table.title = f"{self.sys_log.hostname} File System Folder ({self.name})" + for file in self.files.values(): + table.add_row([file.name, file.size_str]) + print(table.get_string(sortby="File")) + + @property + def size(self) -> int: + """ + Calculate and return the total size of all files in the folder. + + :return: The total size of all files in the folder. If no files exist or all have `None` + size, returns 0. + """ + return sum(file.size for file in self.files.values() if file.size is not None) + + def apply_timestep(self, timestep: int): + """ + Apply a single timestep of simulation dynamics to this folder and its files. + + In this instance, if any multi-timestep processes are currently occurring (such as scanning), + then they are brought one step closer to being finished. + + :param timestep: The current timestep number. (Amount of time since simulation episode began) + :type timestep: int + """ + super().apply_timestep(timestep=timestep) + + # scan files each timestep + if self.scan_duration >= 0: + self.scan_duration -= 1 + + if self.scan_duration == 0: + for file_id in self.files: + file = self.get_file_by_id(file_uuid=file_id) + file.scan() + if file.visible_health_status == FileSystemItemHealthStatus.CORRUPT: + self.visible_health_status = FileSystemItemHealthStatus.CORRUPT + + # red scan file at each step + if self.red_scan_duration >= 0: + self.red_scan_duration -= 1 + + if self.red_scan_duration == 0: + self.revealed_to_red = True + for file_id in self.files: + file = self.get_file_by_id(file_uuid=file_id) + file.reveal_to_red() + + # apply timestep to files in folder + for file_id in self.files: + self.files[file_id].apply_timestep(timestep=timestep) + + def get_file(self, file_name: str) -> Optional[File]: + """ + Get a file by its name. + + File name must be the filename and prefix, like 'memo.docx'. + + :param file_name: The file name. + :return: The matching File. + """ + # TODO: Increment read count? + return self._files_by_name.get(file_name) + + def get_file_by_id(self, file_uuid: str) -> File: + """ + Get a file by its uuid. + + :param: file_uuid: The file uuid. + :return: The matching File. + """ + return self.files.get(file_uuid) + + def add_file(self, file: File): + """ + Adds a file to the folder. + + :param File file: The File object to be added to the folder. + :raises Exception: If the provided `file` parameter is None or not an instance of the + `File` class. + """ + if file is None or not isinstance(file, File): + raise Exception(f"Invalid file: {file}") + + # check if file with id already exists in folder + if file.uuid in self.files: + _LOGGER.debug(f"File with id {file.uuid} already exists in folder") + else: + # add to list + self.files[file.uuid] = file + self._files_by_name[file.name] = file + file.folder = self + + def remove_file(self, file: Optional[File]): + """ + Removes a file from the folder list. + + The method can take a File object or a file id. + + :param file: The file to remove + """ + if file is None or not isinstance(file, File): + raise Exception(f"Invalid file: {file}") + + if self.files.get(file.uuid): + self.files.pop(file.uuid) + self._files_by_name.pop(file.name) + self.deleted_files[file.uuid] = file + self.sys_log.info(f"Removed file {file.name} (id: {file.uuid})") + else: + _LOGGER.debug(f"File with UUID {file.uuid} was not found.") + + def remove_file_by_id(self, file_uuid: str): + """ + Remove a file using id. + + :param: file_uuid: The UUID of the file to remove. + """ + file = self.get_file_by_id(file_uuid=file_uuid) + self.remove_file(file=file) + + def remove_all_files(self): + """Removes all the files in the folder.""" + for file_id in self.files: + self.deleted_files[file_id] = self.files[file_id] + + self.files = {} + self._files_by_name = {} + + def restore_file(self, file: Optional[File]): + """ + Restores a file. + + The method can take a File object or a file id. + + :param file: The file to restore + """ + pass + + def quarantine(self): + """Quarantines the File System Folder.""" + pass + + def unquarantine(self): + """Unquarantine of the File System Folder.""" + pass + + def quarantine_status(self) -> bool: + """Returns true if the folder is being quarantined.""" + pass + + def scan(self, instant_scan: bool = False) -> None: + """ + Update Folder visible status. + + :param: instant_scan: If True, the scan is completed instantly and ignores scan duration. Default False. + """ + if instant_scan: + for file_id in self.files: + file = self.get_file_by_id(file_uuid=file_id) + file.scan() + if file.visible_health_status == FileSystemItemHealthStatus.CORRUPT: + self.visible_health_status = FileSystemItemHealthStatus.CORRUPT + return + + if self.scan_duration <= 0: + # scan one file per timestep + self.scan_duration = len(self.files) + self.sys_log.info(f"Scanning folder {self.name} (id: {self.uuid})") + else: + # scan already in progress + self.sys_log.info(f"Scan is already in progress {self.name} (id: {self.uuid})") + + def reveal_to_red(self, instant_scan: bool = False): + """ + Reveals the folders and files to the red agent. + + :param: instant_scan: If True, the scan is completed instantly and ignores scan duration. Default False. + """ + if instant_scan: + self.revealed_to_red = True + for file_id in self.files: + file = self.get_file_by_id(file_uuid=file_id) + file.reveal_to_red() + return + + if self.red_scan_duration <= 0: + # scan one file per timestep + self.red_scan_duration = len(self.files) + self.sys_log.info(f"Folder revealed to red agent: {self.name} (id: {self.uuid})") + else: + # scan already in progress + self.sys_log.info(f"Red Agent Scan is already in progress {self.name} (id: {self.uuid})") + + def check_hash(self) -> bool: + """ + Runs a :func:`check_hash` on all files in the folder. + + If a file under the folder is corrupted, the whole folder is considered corrupted. + + TODO: For now this will just iterate through the files and run :func:`check_hash` and ignores + any other changes to the folder + + Return False if corruption is detected, otherwise True + """ + super().check_hash() + + # iterate through the files and run a check hash + no_corrupted_files = True + + for file_id in self.files: + file = self.get_file_by_id(file_uuid=file_id) + no_corrupted_files = file.check_hash() + + # if one file in the folder is corrupted, set the folder status to corrupted + if not no_corrupted_files: + self.corrupt() + + self.sys_log.info(f"Checking hash of folder {self.name} (id: {self.uuid})") + + return no_corrupted_files + + def repair(self) -> bool: + """Repair a corrupted Folder by setting the folder and containing files status to FileSystemItemStatus.GOOD.""" + super().repair() + + repaired = False + + # iterate through the files in the folder + for file_id in self.files: + file = self.get_file_by_id(file_uuid=file_id) + repaired = file.repair() + + # set file status to good if corrupt + if self.health_status == FileSystemItemHealthStatus.CORRUPT: + self.health_status = FileSystemItemHealthStatus.GOOD + repaired = True + + self.sys_log.info(f"Repaired folder {self.name} (id: {self.uuid})") + return repaired + + def restore(self) -> bool: + """Restore a File by setting the folder and containing files status to FileSystemItemStatus.GOOD.""" + super().restore() + + restored = False + + # iterate through the files in the folder + for file_id in self.files: + file = self.get_file_by_id(file_uuid=file_id) + restored = file.restore() + + # set file status to corrupt if good + if self.health_status == FileSystemItemHealthStatus.CORRUPT: + self.health_status = FileSystemItemHealthStatus.GOOD + restored = True + + self.sys_log.info(f"Restored folder {self.name} (id: {self.uuid})") + return restored + + def corrupt(self) -> bool: + """Corrupt a File by setting the folder and containing files status to FileSystemItemStatus.CORRUPT.""" + super().corrupt() + + corrupted = False + + # iterate through the files in the folder + for file_id in self.files: + file = self.get_file_by_id(file_uuid=file_id) + corrupted = file.corrupt() + + # set file status to corrupt if good + if self.health_status == FileSystemItemHealthStatus.GOOD: + self.health_status = FileSystemItemHealthStatus.CORRUPT + corrupted = True + + self.sys_log.info(f"Corrupted folder {self.name} (id: {self.uuid})") + return corrupted diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_file.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_file.py new file mode 100644 index 00000000..174c7726 --- /dev/null +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_file.py @@ -0,0 +1,78 @@ +from primaite.simulator.file_system.file import File +from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus +from primaite.simulator.file_system.file_type import FileType + + +def test_create_file_no_extension(file_system): + """Tests that creating a file without an extension sets the file type to FileType.UNKNOWN.""" + file = file_system.create_file(file_name="test_file") + assert len(file_system.folders) is 1 + assert file_system.get_folder("root").get_file("test_file") == file + assert file_system.get_folder("root").get_file("test_file").file_type == FileType.UNKNOWN + assert file_system.get_folder("root").get_file("test_file").size == 0 + + +def test_file_scan(file_system): + """Test the ability to update visible status.""" + file: File = file_system.create_file(file_name="test_file.txt", folder_name="test_folder") + + assert file.health_status == FileSystemItemHealthStatus.GOOD + assert file.visible_health_status == FileSystemItemHealthStatus.GOOD + + file.corrupt() + + assert file.health_status == FileSystemItemHealthStatus.CORRUPT + assert file.visible_health_status == FileSystemItemHealthStatus.GOOD + + file.scan() + + assert file.health_status == FileSystemItemHealthStatus.CORRUPT + assert file.visible_health_status == FileSystemItemHealthStatus.CORRUPT + + +def test_file_reveal_to_red_scan(file_system): + """Test the ability to reveal files to red.""" + file = file_system.create_file(file_name="test_file.txt", folder_name="test_folder") + + assert file.revealed_to_red is False + + file.reveal_to_red() + + assert file.revealed_to_red is True + + +def test_simulated_file_check_hash(file_system): + file: File = file_system.create_file(file_name="test_file.txt", folder_name="test_folder") + + assert file.check_hash() is True + + # change simulated file size + file.sim_size = 0 + assert file.check_hash() is False + assert file.health_status == FileSystemItemHealthStatus.CORRUPT + + +def test_real_file_check_hash(file_system): + file: File = file_system.create_file(file_name="test_file.txt", real=True) + + assert file.check_hash() is True + + # change file content + with open(file.sim_path, "a") as f: + f.write("get hacked scrub lol xD\n") + + assert file.check_hash() is False + assert file.health_status == FileSystemItemHealthStatus.CORRUPT + + +def test_file_corrupt_repair(file_system): + """Test the ability to corrupt and repair files.""" + file: File = file_system.create_file(file_name="test_file.txt", folder_name="test_folder") + + file.corrupt() + + assert file.health_status == FileSystemItemHealthStatus.CORRUPT + + file.repair() + + assert file.health_status == FileSystemItemHealthStatus.GOOD diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system.py index cb398ca9..d26d0a4a 100644 --- a/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system.py +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system.py @@ -1,6 +1,6 @@ import pytest -from primaite.simulator.file_system.file_system import File, FileSystem, FileSystemItemHealthStatus, Folder +from primaite.simulator.file_system.file_system import FileSystem from primaite.simulator.file_system.file_type import FileType @@ -26,15 +26,6 @@ def test_create_file_no_folder(file_system): assert file_system.get_folder("root").get_file("test_file.txt").size == 10 -def test_create_file_no_extension(file_system): - """Tests that creating a file without an extension sets the file type to FileType.UNKNOWN.""" - file = file_system.create_file(file_name="test_file") - assert len(file_system.folders) is 1 - assert file_system.get_folder("root").get_file("test_file") == file - assert file_system.get_folder("root").get_file("test_file").file_type == FileType.UNKNOWN - assert file_system.get_folder("root").get_file("test_file").size == 0 - - def test_delete_file(file_system): """Tests that a file can be deleted.""" file_system.create_file(file_name="test_file.txt") @@ -125,193 +116,6 @@ def test_copy_file(file_system): assert file_system.get_file("dst_folder", "test_file.txt").uuid != original_uuid -@pytest.mark.skip(reason="Implementation for quarantine not needed yet") -def test_folder_quarantine_state(file_system): - """Tests the changing of folder quarantine status.""" - folder = file_system.get_folder("root") - - assert folder.quarantine_status() is False - - folder.quarantine() - assert folder.quarantine_status() is True - - folder.unquarantine() - assert folder.quarantine_status() is False - - -def test_file_corrupt_repair(file_system): - """Test the ability to corrupt and repair files.""" - folder: Folder = file_system.create_folder(folder_name="test_folder") - file: File = file_system.create_file(file_name="test_file.txt", folder_name="test_folder") - - file.corrupt() - - assert folder.health_status == FileSystemItemHealthStatus.GOOD - assert file.health_status == FileSystemItemHealthStatus.CORRUPT - - file.repair() - - assert folder.health_status == FileSystemItemHealthStatus.GOOD - assert file.health_status == FileSystemItemHealthStatus.GOOD - - -def test_folder_corrupt_repair(file_system): - """Test the ability to corrupt and repair folders.""" - folder: Folder = file_system.create_folder(folder_name="test_folder") - file_system.create_file(file_name="test_file.txt", folder_name="test_folder") - - folder.corrupt() - - file = folder.get_file(file_name="test_file.txt") - assert folder.health_status == FileSystemItemHealthStatus.CORRUPT - assert file.health_status == FileSystemItemHealthStatus.CORRUPT - - folder.repair() - - file = folder.get_file(file_name="test_file.txt") - assert folder.health_status == FileSystemItemHealthStatus.GOOD - assert file.health_status == FileSystemItemHealthStatus.GOOD - - -def test_file_scan(file_system): - """Test the ability to update visible status.""" - folder: Folder = file_system.create_folder(folder_name="test_folder") - file: File = file_system.create_file(file_name="test_file.txt", folder_name="test_folder") - - assert file.health_status == FileSystemItemHealthStatus.GOOD - assert file.visible_health_status == FileSystemItemHealthStatus.GOOD - - file.corrupt() - - assert file.health_status == FileSystemItemHealthStatus.CORRUPT - assert file.visible_health_status == FileSystemItemHealthStatus.GOOD - - file.scan() - - assert file.health_status == FileSystemItemHealthStatus.CORRUPT - assert file.visible_health_status == FileSystemItemHealthStatus.CORRUPT - - -def test_folder_scan(file_system): - """Test the ability to update visible status.""" - folder: Folder = file_system.create_folder(folder_name="test_folder") - file_system.create_file(file_name="test_file.txt", folder_name="test_folder") - file_system.create_file(file_name="test_file2.txt", folder_name="test_folder") - - file1: File = folder.get_file_by_id(file_uuid=list(folder.files)[1]) - file2: File = folder.get_file_by_id(file_uuid=list(folder.files)[0]) - - assert folder.health_status == FileSystemItemHealthStatus.GOOD - assert folder.visible_health_status == FileSystemItemHealthStatus.GOOD - assert file1.visible_health_status == FileSystemItemHealthStatus.GOOD - assert file2.visible_health_status == FileSystemItemHealthStatus.GOOD - - folder.corrupt() - - assert folder.health_status == FileSystemItemHealthStatus.CORRUPT - assert folder.visible_health_status == FileSystemItemHealthStatus.GOOD - assert file1.visible_health_status == FileSystemItemHealthStatus.GOOD - assert file2.visible_health_status == FileSystemItemHealthStatus.GOOD - - folder.scan() - - folder.apply_timestep(timestep=0) - - assert folder.health_status == FileSystemItemHealthStatus.CORRUPT - assert folder.visible_health_status == FileSystemItemHealthStatus.GOOD - assert file1.visible_health_status == FileSystemItemHealthStatus.GOOD - assert file2.visible_health_status == FileSystemItemHealthStatus.GOOD - - folder.apply_timestep(timestep=1) - - assert folder.health_status == FileSystemItemHealthStatus.CORRUPT - assert folder.visible_health_status == FileSystemItemHealthStatus.CORRUPT - assert file1.visible_health_status == FileSystemItemHealthStatus.CORRUPT - assert file2.visible_health_status == FileSystemItemHealthStatus.CORRUPT - - -def test_folder_reveal_to_red_scan(file_system): - """Test the ability to reveal files to red.""" - folder: Folder = file_system.create_folder(folder_name="test_folder") - file_system.create_file(file_name="test_file.txt", folder_name="test_folder") - file_system.create_file(file_name="test_file2.txt", folder_name="test_folder") - - file1: File = folder.get_file_by_id(file_uuid=list(folder.files)[1]) - file2: File = folder.get_file_by_id(file_uuid=list(folder.files)[0]) - - assert folder.revealed_to_red is False - assert file1.revealed_to_red is False - assert file2.revealed_to_red is False - - folder.reveal_to_red() - - folder.apply_timestep(timestep=0) - - assert folder.revealed_to_red is False - assert file1.revealed_to_red is False - assert file2.revealed_to_red is False - - folder.apply_timestep(timestep=1) - - assert folder.revealed_to_red is True - assert file1.revealed_to_red is True - assert file2.revealed_to_red is True - - -def test_simulated_file_check_hash(file_system): - file: File = file_system.create_file(file_name="test_file.txt", folder_name="test_folder") - - assert file.check_hash() is True - - # change simulated file size - file.sim_size = 0 - assert file.check_hash() is False - assert file.health_status == FileSystemItemHealthStatus.CORRUPT - - -def test_real_file_check_hash(file_system): - file: File = file_system.create_file(file_name="test_file.txt", folder_name="test_folder", real=True) - - assert file.check_hash() is True - - # change file content - with open(file.sim_path, "a") as f: - f.write("get hacked scrub lol xD\n") - - assert file.check_hash() is False - assert file.health_status == FileSystemItemHealthStatus.CORRUPT - - -def test_simulated_folder_check_hash(file_system): - folder: Folder = file_system.create_folder(folder_name="test_folder") - file_system.create_file(file_name="test_file.txt", folder_name="test_folder") - - assert folder.check_hash() is True - - # change simulated file size - file = folder.get_file(file_name="test_file.txt") - file.sim_size = 0 - assert folder.check_hash() is False - assert folder.health_status == FileSystemItemHealthStatus.CORRUPT - - -def test_real_folder_check_hash(file_system): - folder: Folder = file_system.create_folder(folder_name="test_folder") - file_system.create_file(file_name="test_file.txt", folder_name="test_folder", real=True) - - assert folder.check_hash() is True - - # change simulated file size - file = folder.get_file(file_name="test_file.txt") - - # change file content - with open(file.sim_path, "a") as f: - f.write("get hacked scrub lol xD\n") - - assert folder.check_hash() is False - assert folder.health_status == FileSystemItemHealthStatus.CORRUPT - - @pytest.mark.skip(reason="Skipping until we tackle serialisation") def test_serialisation(file_system): """Test to check that the object serialisation works correctly.""" diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system_actions.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system_actions.py index abfb244a..d902c935 100644 --- a/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system_actions.py +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system_actions.py @@ -2,7 +2,8 @@ from typing import Tuple import pytest -from primaite.simulator.file_system.file_system import File, FileSystem, FileSystemItemHealthStatus, Folder +from primaite.simulator.file_system.file_system import File, FileSystem, Folder +from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus @pytest.fixture(scope="function") diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_folder.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_folder.py new file mode 100644 index 00000000..9a12dbe9 --- /dev/null +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_folder.py @@ -0,0 +1,133 @@ +import pytest + +from primaite.simulator.file_system.file import File +from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus +from primaite.simulator.file_system.folder import Folder + + +@pytest.mark.skip(reason="Implementation for quarantine not needed yet") +def test_folder_quarantine_state(file_system): + """Tests the changing of folder quarantine status.""" + folder = file_system.get_folder("root") + + assert folder.quarantine_status() is False + + folder.quarantine() + assert folder.quarantine_status() is True + + folder.unquarantine() + assert folder.quarantine_status() is False + + +def test_folder_scan(file_system): + """Test the ability to update visible status.""" + folder: Folder = file_system.create_folder(folder_name="test_folder") + file_system.create_file(file_name="test_file.txt", folder_name="test_folder") + file_system.create_file(file_name="test_file2.txt", folder_name="test_folder") + + file1: File = folder.get_file_by_id(file_uuid=list(folder.files)[1]) + file2: File = folder.get_file_by_id(file_uuid=list(folder.files)[0]) + + assert folder.health_status == FileSystemItemHealthStatus.GOOD + assert folder.visible_health_status == FileSystemItemHealthStatus.GOOD + assert file1.visible_health_status == FileSystemItemHealthStatus.GOOD + assert file2.visible_health_status == FileSystemItemHealthStatus.GOOD + + folder.corrupt() + + assert folder.health_status == FileSystemItemHealthStatus.CORRUPT + assert folder.visible_health_status == FileSystemItemHealthStatus.GOOD + assert file1.visible_health_status == FileSystemItemHealthStatus.GOOD + assert file2.visible_health_status == FileSystemItemHealthStatus.GOOD + + folder.scan() + + folder.apply_timestep(timestep=0) + + assert folder.health_status == FileSystemItemHealthStatus.CORRUPT + assert folder.visible_health_status == FileSystemItemHealthStatus.GOOD + assert file1.visible_health_status == FileSystemItemHealthStatus.GOOD + assert file2.visible_health_status == FileSystemItemHealthStatus.GOOD + + folder.apply_timestep(timestep=1) + + assert folder.health_status == FileSystemItemHealthStatus.CORRUPT + assert folder.visible_health_status == FileSystemItemHealthStatus.CORRUPT + assert file1.visible_health_status == FileSystemItemHealthStatus.CORRUPT + assert file2.visible_health_status == FileSystemItemHealthStatus.CORRUPT + + +def test_folder_reveal_to_red_scan(file_system): + """Test the ability to reveal files to red.""" + folder: Folder = file_system.create_folder(folder_name="test_folder") + file_system.create_file(file_name="test_file.txt", folder_name="test_folder") + file_system.create_file(file_name="test_file2.txt", folder_name="test_folder") + + file1: File = folder.get_file_by_id(file_uuid=list(folder.files)[1]) + file2: File = folder.get_file_by_id(file_uuid=list(folder.files)[0]) + + assert folder.revealed_to_red is False + assert file1.revealed_to_red is False + assert file2.revealed_to_red is False + + folder.reveal_to_red() + + folder.apply_timestep(timestep=0) + + assert folder.revealed_to_red is False + assert file1.revealed_to_red is False + assert file2.revealed_to_red is False + + folder.apply_timestep(timestep=1) + + assert folder.revealed_to_red is True + assert file1.revealed_to_red is True + assert file2.revealed_to_red is True + + +def test_folder_corrupt_repair(file_system): + """Test the ability to corrupt and repair folders.""" + folder: Folder = file_system.create_folder(folder_name="test_folder") + file_system.create_file(file_name="test_file.txt", folder_name="test_folder") + + folder.corrupt() + + file = folder.get_file(file_name="test_file.txt") + assert folder.health_status == FileSystemItemHealthStatus.CORRUPT + assert file.health_status == FileSystemItemHealthStatus.CORRUPT + + folder.repair() + + file = folder.get_file(file_name="test_file.txt") + assert folder.health_status == FileSystemItemHealthStatus.GOOD + assert file.health_status == FileSystemItemHealthStatus.GOOD + + +def test_simulated_folder_check_hash(file_system): + folder: Folder = file_system.create_folder(folder_name="test_folder") + file_system.create_file(file_name="test_file.txt", folder_name="test_folder") + + assert folder.check_hash() is True + + # change simulated file size + file = folder.get_file(file_name="test_file.txt") + file.sim_size = 0 + assert folder.check_hash() is False + assert folder.health_status == FileSystemItemHealthStatus.CORRUPT + + +def test_real_folder_check_hash(file_system): + folder: Folder = file_system.create_folder(folder_name="test_folder") + file_system.create_file(file_name="test_file.txt", folder_name="test_folder", real=True) + + assert folder.check_hash() is True + + # change simulated file size + file = folder.get_file(file_name="test_file.txt") + + # change file content + with open(file.sim_path, "a") as f: + f.write("get hacked scrub lol xD\n") + + assert folder.check_hash() is False + assert folder.health_status == FileSystemItemHealthStatus.CORRUPT diff --git a/tests/unit_tests/_primaite/_simulator/_network/_hardware/test_node_actions.py b/tests/unit_tests/_primaite/_simulator/_network/_hardware/test_node_actions.py index 3d6eea3b..1216a0cc 100644 --- a/tests/unit_tests/_primaite/_simulator/_network/_hardware/test_node_actions.py +++ b/tests/unit_tests/_primaite/_simulator/_network/_hardware/test_node_actions.py @@ -1,6 +1,7 @@ import pytest -from primaite.simulator.file_system.file_system import File, FileSystemItemHealthStatus, Folder +from primaite.simulator.file_system.file_system import File, Folder +from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus from primaite.simulator.network.hardware.base import Node, NodeOperatingState from primaite.simulator.system.applications.application import Application from primaite.simulator.system.processes.process import Process From 51713bad7492318c7f369d8f47f74bc4630038e9 Mon Sep 17 00:00:00 2001 From: "Czar.Echavez" Date: Fri, 3 Nov 2023 15:15:18 +0000 Subject: [PATCH 4/9] #1962: split tests into managable files + implement deletion of folders and files + tests --- src/primaite/simulator/file_system/file.py | 56 +++++--- .../simulator/file_system/file_system.py | 74 +++++++++- .../file_system/file_system_item_abc.py | 26 +++- src/primaite/simulator/file_system/folder.py | 120 +++++++++------- .../services/database/database_service.py | 2 +- .../_simulator/_file_system/test_file.py | 12 +- .../_file_system/test_file_actions.py | 89 ++++++++++++ .../_file_system/test_file_system.py | 47 +++++++ .../_file_system/test_file_system_actions.py | 131 +----------------- .../_simulator/_file_system/test_folder.py | 25 +++- .../_file_system/test_folder_actions.py | 103 ++++++++++++++ 11 files changed, 468 insertions(+), 217 deletions(-) create mode 100644 tests/unit_tests/_primaite/_simulator/_file_system/test_file_actions.py create mode 100644 tests/unit_tests/_primaite/_simulator/_file_system/test_folder_actions.py diff --git a/src/primaite/simulator/file_system/file.py b/src/primaite/simulator/file_system/file.py index 0da3c9ab..04502c0f 100644 --- a/src/primaite/simulator/file_system/file.py +++ b/src/primaite/simulator/file_system/file.py @@ -102,15 +102,22 @@ class File(FileSystemItemABC): def scan(self) -> None: """Updates the visible statuses of the file.""" + if self.deleted: + self.sys_log.error(f"Unable to scan deleted file {self.folder_name}/{self.name}") + return + path = self.folder.name + "/" + self.name self.sys_log.info(f"Scanning file {self.sim_path if self.sim_path else path}") self.visible_health_status = self.health_status - def reveal_to_red(self): + def reveal_to_red(self) -> None: """Reveals the folder/file to the red agent.""" + if self.deleted: + self.sys_log.error(f"Unable to reveal deleted file {self.folder_name}/{self.name}") + return self.revealed_to_red = True - def check_hash(self) -> bool: + def check_hash(self) -> None: """ Check if the file has been changed. @@ -118,6 +125,9 @@ class File(FileSystemItemABC): Return False if corruption is detected, otherwise True """ + if self.deleted: + self.sys_log.error(f"Unable to check hash of deleted file {self.folder_name}/{self.name}") + return current_hash = None # if file is real, read the file contents @@ -139,13 +149,12 @@ class File(FileSystemItemABC): # if the previous hash and current hash do not match, mark file as corrupted if self.previous_hash is not current_hash: self.corrupt() - return False - return True - - def repair(self) -> bool: + def repair(self) -> None: """Repair a corrupted File by setting the status to FileSystemItemStatus.GOOD.""" - super().repair() + if self.deleted: + self.sys_log.error(f"Unable to repair deleted file {self.folder_name}/{self.name}") + return # set file status to good if corrupt if self.health_status == FileSystemItemHealthStatus.CORRUPT: @@ -153,33 +162,38 @@ class File(FileSystemItemABC): path = self.folder.name + "/" + self.name self.sys_log.info(f"Repaired file {self.sim_path if self.sim_path else path}") - return True - def restore(self) -> bool: + def restore(self) -> None: """Restore a corrupted File by setting the status to FileSystemItemStatus.GOOD.""" - super().restore() - - restored = False - if self.health_status == FileSystemItemHealthStatus.CORRUPT: self.health_status = FileSystemItemHealthStatus.GOOD - restored = True path = self.folder.name + "/" + self.name self.sys_log.info(f"Restored file {self.sim_path if self.sim_path else path}") - return restored - def corrupt(self) -> bool: + def corrupt(self) -> None: """Corrupt a File by setting the status to FileSystemItemStatus.CORRUPT.""" - super().corrupt() - - corrupted = False + if self.deleted: + self.sys_log.error(f"Unable to corrupt deleted file {self.folder_name}/{self.name}") + return # set file status to good if corrupt if self.health_status == FileSystemItemHealthStatus.GOOD: self.health_status = FileSystemItemHealthStatus.CORRUPT - corrupted = True path = self.folder.name + "/" + self.name self.sys_log.info(f"Corrupted file {self.sim_path if self.sim_path else path}") - return corrupted + + def restore(self) -> bool: + """Determines if the file needs to be repaired or unmarked as deleted.""" + super().restore() + return True + + def delete(self): + """Marks the file as deleted.""" + if self.deleted: + self.sys_log.error(f"Unable to delete an already deleted file {self.folder_name}/{self.name}") + return + + self.deleted = True + self.sys_log.info(f"File deleted {self.folder_name}/{self.name}") diff --git a/src/primaite/simulator/file_system/file_system.py b/src/primaite/simulator/file_system/file_system.py index 16c9992c..7f2d41e5 100644 --- a/src/primaite/simulator/file_system/file_system.py +++ b/src/primaite/simulator/file_system/file_system.py @@ -38,9 +38,21 @@ class FileSystem(SimComponent): def _init_request_manager(self) -> RequestManager: rm = super()._init_request_manager() + self._delete_manager = RequestManager() + self._delete_manager.add_request( + name="file", + request_type=RequestType( + func=lambda request, context: self.delete_file_by_id(folder_uuid=request[0], file_uuid=request[1]) + ), + ) + self._delete_manager.add_request( + name="folder", + request_type=RequestType(func=lambda request, context: self.delete_folder_by_id(folder_uuid=request[0])), + ) + rm.add_request( name="delete", - request_type=RequestType(func=lambda request, context: self.delete_folder_by_id(folder_uuid=request[0])), + request_type=RequestType(func=self._delete_manager), ) self._folder_request_manager = RequestManager() @@ -119,15 +131,18 @@ class FileSystem(SimComponent): return folder = self._folders_by_name.get(folder_name) if folder: + # set folder to deleted state + folder.delete() + # remove from folder list self.folders.pop(folder.uuid) self._folders_by_name.pop(folder.name) - self.sys_log.info(f"Deleted folder /{folder.name} and its contents") # add to deleted list folder.remove_all_files() self.deleted_folders[folder.uuid] = folder + self.sys_log.info(f"Deleted folder /{folder.name} and its contents") else: _LOGGER.debug(f"Cannot delete folder as it does not exist: {folder_name}") @@ -216,7 +231,41 @@ class FileSystem(SimComponent): folder = self.get_folder(folder_name) if folder: return folder.get_file(file_name) - self.sys_log.info(f"file not found /{folder_name}/{file_name}") + self.sys_log.info(f"File not found /{folder_name}/{file_name}") + + def get_file_by_id( + self, file_uuid: str, folder_uuid: Optional[str] = None, include_deleted: Optional[bool] = False + ) -> Optional[File]: + """ + Retrieve a file by its uuid from a specific folder. + + :param: file_uuid: The uuid of the folder where the file resides. + :param: folder_uuid: The uuid of the file to be retrieved, including its extension. + :param: include_deleted: If true, the deleted files will also be checked + :return: An instance of File if it exists, otherwise `None`. + """ + folder = self.folders.get(folder_uuid) + + if folder: + return folder.get_file_by_id(file_uuid=file_uuid, include_deleted=include_deleted) + + # iterate through every folder looking for file + file = None + + for folder_id in self.folders: + folder = self.folders.get(folder_id) + res = folder.get_file_by_id(file_uuid=file_uuid, include_deleted=True) + if res: + file = res + + if include_deleted: + for folder_id in self.deleted_folders: + folder = self.deleted_folders.get(folder_id) + res = folder.get_file_by_id(file_uuid=file_uuid, include_deleted=True) + if res: + file = res + + return file def delete_file(self, folder_name: str, file_name: str): """ @@ -231,6 +280,19 @@ class FileSystem(SimComponent): if file: folder.remove_file(file) + def delete_file_by_id(self, folder_uuid: str, file_uuid: str): + """ + Deletes a file via its uuid. + + :param: folder_uuid: UUID of the folder the file belongs to + :param: file_uuid: UUID of the file to delete + """ + folder = self.get_folder_by_id(folder_uuid=folder_uuid) + + if folder: + file = folder.get_file_by_id(file_uuid=file_uuid) + self.delete_file(folder_name=folder.name, file_name=file.name) + def move_file(self, src_folder_name: str, src_file_name: str, dst_folder_name: str): """ Move a file from one folder to another. @@ -277,7 +339,7 @@ class FileSystem(SimComponent): folder_name=dst_folder.name, **file.model_dump(exclude={"uuid", "folder_id", "folder_name", "sim_path"}), ) - dst_folder.add_file(file_copy) + dst_folder.add_file(file_copy, force=True) if file.real: file_copy.sim_path.parent.mkdir(exist_ok=True) @@ -303,6 +365,10 @@ class FileSystem(SimComponent): for folder_id in self.folders: self.folders[folder_id].apply_timestep(timestep=timestep) + ############################################################### + # Agent actions + ############################################################### + def scan(self, instant_scan: bool = False): """ Scan all the folders (and child files) in the file system. diff --git a/src/primaite/simulator/file_system/file_system_item_abc.py b/src/primaite/simulator/file_system/file_system_item_abc.py index c0d65139..543bb1b7 100644 --- a/src/primaite/simulator/file_system/file_system_item_abc.py +++ b/src/primaite/simulator/file_system/file_system_item_abc.py @@ -82,6 +82,9 @@ class FileSystemItemABC(SimComponent): sys_log: SysLog "Used for creating system logs." + deleted: bool = False + "If true, the FileSystemItem was deleted." + def describe_state(self) -> Dict: """ Produce a dictionary describing the current state of this object. @@ -121,7 +124,17 @@ class FileSystemItemABC(SimComponent): return convert_size(self.size) @abstractmethod - def check_hash(self) -> bool: + def scan(self) -> None: + """Scan the folder/file - updates the visible_health_status.""" + pass + + @abstractmethod + def reveal_to_red(self) -> None: + """Reveal the folder/file to the red agent.""" + pass + + @abstractmethod + def check_hash(self) -> None: """ Checks the has of the file to detect any changes. @@ -132,7 +145,7 @@ class FileSystemItemABC(SimComponent): pass @abstractmethod - def repair(self) -> bool: + def repair(self) -> None: """ Repair the FileSystemItem. @@ -141,7 +154,7 @@ class FileSystemItemABC(SimComponent): pass @abstractmethod - def corrupt(self) -> bool: + def corrupt(self) -> None: """ Corrupt the FileSystemItem. @@ -150,6 +163,11 @@ class FileSystemItemABC(SimComponent): pass @abstractmethod - def restore(self) -> bool: + def restore(self) -> None: """Restore the file/folder to the state before it got ruined.""" pass + + @abstractmethod + def delete(self) -> None: + """Mark the file/folder as deleted.""" + self.deleted = True diff --git a/src/primaite/simulator/file_system/folder.py b/src/primaite/simulator/file_system/folder.py index 6922cad1..e12d4e12 100644 --- a/src/primaite/simulator/file_system/folder.py +++ b/src/primaite/simulator/file_system/folder.py @@ -131,34 +131,45 @@ class Folder(FileSystemItemABC): # TODO: Increment read count? return self._files_by_name.get(file_name) - def get_file_by_id(self, file_uuid: str) -> File: + def get_file_by_id(self, file_uuid: str, include_deleted: Optional[bool] = False) -> File: """ Get a file by its uuid. :param: file_uuid: The file uuid. + :param: include_deleted: If true, the deleted files will also be checked :return: The matching File. """ + if include_deleted: + deleted_file = self.deleted_files.get(file_uuid) + + if deleted_file: + return deleted_file + return self.files.get(file_uuid) - def add_file(self, file: File): + def add_file(self, file: File, force: Optional[bool] = False): """ Adds a file to the folder. - :param File file: The File object to be added to the folder. + :param: file: The File object to be added to the folder. + :param: force: Overwrite file - do not check if uuid or name already exists in folder. Default False. :raises Exception: If the provided `file` parameter is None or not an instance of the `File` class. """ if file is None or not isinstance(file, File): raise Exception(f"Invalid file: {file}") - # check if file with id already exists in folder - if file.uuid in self.files: - _LOGGER.debug(f"File with id {file.uuid} already exists in folder") - else: - # add to list - self.files[file.uuid] = file - self._files_by_name[file.name] = file - file.folder = self + # check if file with id or name already exists in folder + if (force is not True) and file.name in self._files_by_name: + raise Exception(f"File with name {file.name} already exists in folder") + + if (force is not True) and file.uuid in self.files: + raise Exception(f"File with uuid {file.uuid} already exists in folder") + + # add to list + self.files[file.uuid] = file + self._files_by_name[file.name] = file + file.folder = self def remove_file(self, file: Optional[File]): """ @@ -175,6 +186,7 @@ class Folder(FileSystemItemABC): self.files.pop(file.uuid) self._files_by_name.pop(file.name) self.deleted_files[file.uuid] = file + file.delete() self.sys_log.info(f"Removed file {file.name} (id: {file.uuid})") else: _LOGGER.debug(f"File with UUID {file.uuid} was not found.") @@ -191,7 +203,9 @@ class Folder(FileSystemItemABC): def remove_all_files(self): """Removes all the files in the folder.""" for file_id in self.files: - self.deleted_files[file_id] = self.files[file_id] + file = self.files.get(file_id) + file.delete() + self.deleted_files[file_id] = file self.files = {} self._files_by_name = {} @@ -224,6 +238,10 @@ class Folder(FileSystemItemABC): :param: instant_scan: If True, the scan is completed instantly and ignores scan duration. Default False. """ + if self.deleted: + self.sys_log.error(f"Unable to scan deleted folder {self.name}") + return + if instant_scan: for file_id in self.files: file = self.get_file_by_id(file_uuid=file_id) @@ -246,6 +264,10 @@ class Folder(FileSystemItemABC): :param: instant_scan: If True, the scan is completed instantly and ignores scan duration. Default False. """ + if self.deleted: + self.sys_log.error(f"Unable to reveal deleted folder {self.name}") + return + if instant_scan: self.revealed_to_red = True for file_id in self.files: @@ -261,7 +283,7 @@ class Folder(FileSystemItemABC): # scan already in progress self.sys_log.info(f"Red Agent Scan is already in progress {self.name} (id: {self.uuid})") - def check_hash(self) -> bool: + def check_hash(self) -> None: """ Runs a :func:`check_hash` on all files in the folder. @@ -272,14 +294,18 @@ class Folder(FileSystemItemABC): Return False if corruption is detected, otherwise True """ - super().check_hash() + if self.deleted: + self.sys_log.error(f"Unable to check hash of deleted folder {self.name}") + return # iterate through the files and run a check hash no_corrupted_files = True for file_id in self.files: file = self.get_file_by_id(file_uuid=file_id) - no_corrupted_files = file.check_hash() + file.check_hash() + if file.health_status == FileSystemItemHealthStatus.CORRUPT: + no_corrupted_files = False # if one file in the folder is corrupted, set the folder status to corrupted if not no_corrupted_files: @@ -287,61 +313,53 @@ class Folder(FileSystemItemABC): self.sys_log.info(f"Checking hash of folder {self.name} (id: {self.uuid})") - return no_corrupted_files - - def repair(self) -> bool: + def repair(self) -> None: """Repair a corrupted Folder by setting the folder and containing files status to FileSystemItemStatus.GOOD.""" - super().repair() - - repaired = False + if self.deleted: + self.sys_log.error(f"Unable to repair deleted folder {self.name}") + return # iterate through the files in the folder for file_id in self.files: file = self.get_file_by_id(file_uuid=file_id) - repaired = file.repair() + file.repair() # set file status to good if corrupt if self.health_status == FileSystemItemHealthStatus.CORRUPT: self.health_status = FileSystemItemHealthStatus.GOOD - repaired = True + + self.health_status = FileSystemItemHealthStatus.GOOD self.sys_log.info(f"Repaired folder {self.name} (id: {self.uuid})") - return repaired - def restore(self) -> bool: - """Restore a File by setting the folder and containing files status to FileSystemItemStatus.GOOD.""" - super().restore() + def restore(self) -> None: + """ + If a Folder is corrupted, run a repair on the folder and its child files. - restored = False + If the folder is deleted, restore the folder by setting deleted status to False. + """ + pass - # iterate through the files in the folder - for file_id in self.files: - file = self.get_file_by_id(file_uuid=file_id) - restored = file.restore() - - # set file status to corrupt if good - if self.health_status == FileSystemItemHealthStatus.CORRUPT: - self.health_status = FileSystemItemHealthStatus.GOOD - restored = True - - self.sys_log.info(f"Restored folder {self.name} (id: {self.uuid})") - return restored - - def corrupt(self) -> bool: + def corrupt(self) -> None: """Corrupt a File by setting the folder and containing files status to FileSystemItemStatus.CORRUPT.""" - super().corrupt() - - corrupted = False + if self.deleted: + self.sys_log.error(f"Unable to corrupt deleted folder {self.name}") + return # iterate through the files in the folder for file_id in self.files: file = self.get_file_by_id(file_uuid=file_id) - corrupted = file.corrupt() + file.corrupt() - # set file status to corrupt if good - if self.health_status == FileSystemItemHealthStatus.GOOD: - self.health_status = FileSystemItemHealthStatus.CORRUPT - corrupted = True + # set file status to corrupt + self.health_status = FileSystemItemHealthStatus.CORRUPT self.sys_log.info(f"Corrupted folder {self.name} (id: {self.uuid})") - return corrupted + + def delete(self): + """Marks the file as deleted. Prevents agent actions from occuring.""" + if self.deleted: + self.sys_log.error(f"Unable to delete an already deleted folder {self.name}") + return + + self.deleted = True diff --git a/src/primaite/simulator/system/services/database/database_service.py b/src/primaite/simulator/system/services/database/database_service.py index f7333f97..b04174bf 100644 --- a/src/primaite/simulator/system/services/database/database_service.py +++ b/src/primaite/simulator/system/services/database/database_service.py @@ -129,7 +129,7 @@ class DatabaseService(Service): self._conn.close() # replace db file self.file_system.delete_file(folder_name=self.folder.name, file_name="downloads.db") - self.file_system.move_file( + self.file_system.copy_file( src_folder_name="downloads", src_file_name="database.db", dst_folder_name=self.folder.name ) self._db_file = self.file_system.get_file(folder_name=self.folder.name, file_name="database.db") diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_file.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_file.py index 174c7726..94ccde83 100644 --- a/tests/unit_tests/_primaite/_simulator/_file_system/test_file.py +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_file.py @@ -44,24 +44,24 @@ def test_file_reveal_to_red_scan(file_system): def test_simulated_file_check_hash(file_system): file: File = file_system.create_file(file_name="test_file.txt", folder_name="test_folder") - assert file.check_hash() is True - + file.check_hash() + assert file.health_status == FileSystemItemHealthStatus.GOOD # change simulated file size file.sim_size = 0 - assert file.check_hash() is False + file.check_hash() assert file.health_status == FileSystemItemHealthStatus.CORRUPT def test_real_file_check_hash(file_system): file: File = file_system.create_file(file_name="test_file.txt", real=True) - assert file.check_hash() is True - + file.check_hash() + assert file.health_status == FileSystemItemHealthStatus.GOOD # change file content with open(file.sim_path, "a") as f: f.write("get hacked scrub lol xD\n") - assert file.check_hash() is False + file.check_hash() assert file.health_status == FileSystemItemHealthStatus.CORRUPT diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_file_actions.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_actions.py new file mode 100644 index 00000000..f43c6b22 --- /dev/null +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_actions.py @@ -0,0 +1,89 @@ +from typing import Tuple + +import pytest + +from primaite.simulator.file_system.file_system import File, FileSystem, Folder +from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus + + +@pytest.fixture(scope="function") +def populated_file_system(file_system) -> Tuple[FileSystem, Folder, File]: + """Create a file system with a folder and file.""" + folder = file_system.create_folder(folder_name="test_folder") + file = file_system.create_file(folder_name="test_folder", file_name="test_file.txt") + + return file_system, folder, file + + +def test_file_scan_request(populated_file_system): + """Test that an agent can request a file scan.""" + fs, folder, file = populated_file_system + + file.corrupt() + assert file.health_status == FileSystemItemHealthStatus.CORRUPT + assert file.visible_health_status == FileSystemItemHealthStatus.GOOD + + fs.apply_request(request=["file", file.uuid, "scan"]) + + assert file.health_status == FileSystemItemHealthStatus.CORRUPT + assert file.visible_health_status == FileSystemItemHealthStatus.CORRUPT + + +def test_file_checkhash_request(populated_file_system): + """Test that an agent can request a file hash check.""" + fs, folder, file = populated_file_system + + fs.apply_request(request=["file", file.uuid, "checkhash"]) + + assert file.health_status == FileSystemItemHealthStatus.GOOD + file.sim_size = 0 + + fs.apply_request(request=["file", file.uuid, "checkhash"]) + + assert file.health_status == FileSystemItemHealthStatus.CORRUPT + + +def test_file_repair_request(populated_file_system): + """Test that an agent can request a file repair.""" + fs, folder, file = populated_file_system + + file.corrupt() + assert file.health_status == FileSystemItemHealthStatus.CORRUPT + + fs.apply_request(request=["file", file.uuid, "repair"]) + assert file.health_status == FileSystemItemHealthStatus.GOOD + + +def test_file_restore_request(populated_file_system): + pass + + +def test_file_corrupt_request(populated_file_system): + """Test that an agent can request a file corruption.""" + fs, folder, file = populated_file_system + fs.apply_request(request=["file", file.uuid, "corrupt"]) + assert file.health_status == FileSystemItemHealthStatus.CORRUPT + + +def test_deleted_file_cannot_be_interacted_with(populated_file_system): + """Test that actions cannot affect deleted files.""" + fs, folder, file = populated_file_system + assert fs.get_file(folder_name=folder.name, file_name=file.name) is not None + + fs.apply_request(request=["file", file.uuid, "corrupt"]) + assert fs.get_file(folder_name=folder.name, file_name=file.name).health_status == FileSystemItemHealthStatus.CORRUPT + assert ( + fs.get_file(folder_name=folder.name, file_name=file.name).visible_health_status + == FileSystemItemHealthStatus.GOOD + ) + + fs.apply_request(request=["delete", "file", folder.uuid, file.uuid]) + assert fs.get_file(folder_name=folder.name, file_name=file.name) is None + + fs.apply_request(request=["file", file.uuid, "repair"]) + fs.apply_request(request=["file", file.uuid, "scan"]) + + file = folder.deleted_files.get(file.uuid) + + assert file.health_status is not FileSystemItemHealthStatus.GOOD + assert file.visible_health_status is not FileSystemItemHealthStatus.CORRUPT diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system.py index d26d0a4a..03c6ad12 100644 --- a/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system.py +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system.py @@ -1,7 +1,9 @@ import pytest +from primaite.simulator.file_system.file import File from primaite.simulator.file_system.file_system import FileSystem from primaite.simulator.file_system.file_type import FileType +from primaite.simulator.file_system.folder import Folder def test_create_folder_and_file(file_system): @@ -65,6 +67,34 @@ def test_delete_folder(file_system): assert len(file_system.deleted_folders) == 1 +def test_create_duplicate_folder(file_system): + """Test that creating a duplicate folder throws exception.""" + assert len(file_system.folders) == 1 + file_system.create_folder(folder_name="test_folder") + + assert len(file_system.folders) is 2 + with pytest.raises(Exception): + file_system.create_folder(folder_name="test_folder") + + assert len(file_system.folders) is 2 + + +def test_create_duplicate_file(file_system): + """Test that creating a duplicate file throws exception.""" + assert len(file_system.folders) == 1 + file_system.create_folder(folder_name="test_folder") + + assert len(file_system.folders) is 2 + file_system.create_file(file_name="test_file.txt", folder_name="test_folder") + + assert len(file_system.get_folder("test_folder").files) == 1 + + with pytest.raises(Exception): + file_system.create_file(file_name="test_file.txt", folder_name="test_folder") + + assert len(file_system.get_folder("test_folder").files) == 1 + + def test_deleting_a_non_existent_folder(file_system): file_system.create_folder(folder_name="test_folder") assert len(file_system.folders) == 2 @@ -116,6 +146,23 @@ def test_copy_file(file_system): assert file_system.get_file("dst_folder", "test_file.txt").uuid != original_uuid +def test_get_file(file_system): + """Test that files can be retrieved.""" + folder: Folder = file_system.create_folder(folder_name="test_folder") + file1: File = file_system.create_file(file_name="test_file.txt", folder_name="test_folder") + file2: File = file_system.create_file(file_name="test_file2.txt", folder_name="test_folder") + + folder.remove_file(file2) + + assert file_system.get_file_by_id(file_uuid=file1.uuid, folder_uuid=folder.uuid) is not None + assert file_system.get_file_by_id(file_uuid=file2.uuid, folder_uuid=folder.uuid) is None + assert file_system.get_file_by_id(file_uuid=file2.uuid, folder_uuid=folder.uuid, include_deleted=True) is not None + assert file_system.get_file_by_id(file_uuid=file2.uuid, include_deleted=True) is not None + + file_system.delete_folder(folder_name="test_folder") + assert file_system.get_file_by_id(file_uuid=file2.uuid, include_deleted=True) is not None + + @pytest.mark.skip(reason="Skipping until we tackle serialisation") def test_serialisation(file_system): """Test to check that the object serialisation works correctly.""" diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system_actions.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system_actions.py index d902c935..d6bbd285 100644 --- a/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system_actions.py +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system_actions.py @@ -8,139 +8,20 @@ from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHe @pytest.fixture(scope="function") def populated_file_system(file_system) -> Tuple[FileSystem, Folder, File]: - """Test that an agent can request a file scan.""" + """Create a file system with a folder and file.""" folder = file_system.create_folder(folder_name="test_folder") file = file_system.create_file(folder_name="test_folder", file_name="test_file.txt") return file_system, folder, file -def test_file_scan_request(populated_file_system): - """Test that an agent can request a file scan.""" - fs, folder, file = populated_file_system - - file.corrupt() - assert file.health_status == FileSystemItemHealthStatus.CORRUPT - assert file.visible_health_status == FileSystemItemHealthStatus.GOOD - - fs.apply_request(request=["file", file.uuid, "scan"]) - - assert file.health_status == FileSystemItemHealthStatus.CORRUPT - assert file.visible_health_status == FileSystemItemHealthStatus.CORRUPT - - -def test_folder_scan_request(populated_file_system): - """Test that an agent can request a folder scan.""" - fs, folder, file = populated_file_system - fs.create_file(file_name="test_file2.txt", folder_name="test_folder") - - file1: File = folder.get_file_by_id(file_uuid=list(folder.files)[1]) - file2: File = folder.get_file_by_id(file_uuid=list(folder.files)[0]) - - folder.corrupt() - assert folder.health_status == FileSystemItemHealthStatus.CORRUPT - assert folder.visible_health_status == FileSystemItemHealthStatus.GOOD - assert file1.visible_health_status == FileSystemItemHealthStatus.GOOD - assert file2.visible_health_status == FileSystemItemHealthStatus.GOOD - - fs.apply_request(request=["folder", folder.uuid, "scan"]) - - folder.apply_timestep(timestep=0) - - assert folder.health_status == FileSystemItemHealthStatus.CORRUPT - assert folder.visible_health_status == FileSystemItemHealthStatus.GOOD - assert file1.visible_health_status == FileSystemItemHealthStatus.GOOD - assert file2.visible_health_status == FileSystemItemHealthStatus.GOOD - - folder.apply_timestep(timestep=1) - - assert folder.health_status == FileSystemItemHealthStatus.CORRUPT - assert folder.visible_health_status == FileSystemItemHealthStatus.CORRUPT - assert file1.visible_health_status == FileSystemItemHealthStatus.CORRUPT - assert file2.visible_health_status == FileSystemItemHealthStatus.CORRUPT - - -def test_file_checkhash_request(populated_file_system): - """Test that an agent can request a file hash check.""" - fs, folder, file = populated_file_system - - fs.apply_request(request=["file", file.uuid, "checkhash"]) - - assert file.health_status == FileSystemItemHealthStatus.GOOD - file.sim_size = 0 - - fs.apply_request(request=["file", file.uuid, "checkhash"]) - - assert file.health_status == FileSystemItemHealthStatus.CORRUPT - - -def test_folder_checkhash_request(populated_file_system): - """Test that an agent can request a folder hash check.""" - fs, folder, file = populated_file_system - - fs.apply_request(request=["folder", folder.uuid, "checkhash"]) - - assert folder.health_status == FileSystemItemHealthStatus.GOOD - file.sim_size = 0 - - fs.apply_request(request=["folder", folder.uuid, "checkhash"]) - assert folder.health_status == FileSystemItemHealthStatus.CORRUPT - - -def test_file_repair_request(populated_file_system): - """Test that an agent can request a file repair.""" - fs, folder, file = populated_file_system - - file.corrupt() - assert file.health_status == FileSystemItemHealthStatus.CORRUPT - - fs.apply_request(request=["file", file.uuid, "repair"]) - assert file.health_status == FileSystemItemHealthStatus.GOOD - - -def test_folder_repair_request(populated_file_system): - """Test that an agent can request a folder repair.""" - fs, folder, file = populated_file_system - - folder.corrupt() - assert file.health_status == FileSystemItemHealthStatus.CORRUPT - assert folder.health_status == FileSystemItemHealthStatus.CORRUPT - - fs.apply_request(request=["folder", folder.uuid, "repair"]) - assert file.health_status == FileSystemItemHealthStatus.GOOD - assert folder.health_status == FileSystemItemHealthStatus.GOOD - - -def test_file_restore_request(populated_file_system): - pass - - -def test_folder_restore_request(populated_file_system): - pass - - -def test_file_corrupt_request(populated_file_system): - """Test that an agent can request a file corruption.""" - fs, folder, file = populated_file_system - fs.apply_request(request=["file", file.uuid, "corrupt"]) - assert file.health_status == FileSystemItemHealthStatus.CORRUPT - - -def test_folder_corrupt_request(populated_file_system): - """Test that an agent can request a folder corruption.""" - fs, folder, file = populated_file_system - fs.apply_request(request=["folder", folder.uuid, "corrupt"]) - assert file.health_status == FileSystemItemHealthStatus.CORRUPT - assert folder.health_status == FileSystemItemHealthStatus.CORRUPT - - def test_file_delete_request(populated_file_system): """Test that an agent can request a file deletion.""" fs, folder, file = populated_file_system - assert folder.get_file_by_id(file_uuid=file.uuid) is not None + assert fs.get_file(folder_name=folder.name, file_name=file.name) is not None - fs.apply_request(request=["folder", folder.uuid, "delete", file.uuid]) - assert folder.get_file_by_id(file_uuid=file.uuid) is None + fs.apply_request(request=["delete", "file", folder.uuid, file.uuid]) + assert fs.get_file(folder_name=folder.name, file_name=file.name) is None def test_folder_delete_request(populated_file_system): @@ -149,6 +30,6 @@ def test_folder_delete_request(populated_file_system): assert folder.get_file_by_id(file_uuid=file.uuid) is not None assert fs.get_folder_by_id(folder_uuid=folder.uuid) is not None - fs.apply_request(request=["delete", folder.uuid]) + fs.apply_request(request=["delete", "folder", folder.uuid]) assert fs.get_folder_by_id(folder_uuid=folder.uuid) is None - assert folder.get_file_by_id(file_uuid=file.uuid) is None + assert fs.get_file_by_id(folder_uuid=folder.uuid, file_uuid=file.uuid) is None diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_folder.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_folder.py index 9a12dbe9..56f2f6fb 100644 --- a/tests/unit_tests/_primaite/_simulator/_file_system/test_folder.py +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_folder.py @@ -19,6 +19,20 @@ def test_folder_quarantine_state(file_system): assert folder.quarantine_status() is False +def test_folder_get_file(file_system): + """Test that files can be retrieved from the folder.""" + folder: Folder = file_system.create_folder(folder_name="test_folder") + file1: File = file_system.create_file(file_name="test_file.txt", folder_name="test_folder") + file2: File = file_system.create_file(file_name="test_file2.txt", folder_name="test_folder") + + folder.remove_file(file2) + + assert folder.get_file_by_id(file_uuid=file1.uuid) is not None + assert folder.get_file_by_id(file_uuid=file2.uuid) is None + + assert folder.get_file_by_id(file_uuid=file2.uuid, include_deleted=True) is not None + + def test_folder_scan(file_system): """Test the ability to update visible status.""" folder: Folder = file_system.create_folder(folder_name="test_folder") @@ -107,12 +121,13 @@ def test_simulated_folder_check_hash(file_system): folder: Folder = file_system.create_folder(folder_name="test_folder") file_system.create_file(file_name="test_file.txt", folder_name="test_folder") - assert folder.check_hash() is True + folder.check_hash() + assert folder.health_status == FileSystemItemHealthStatus.GOOD # change simulated file size file = folder.get_file(file_name="test_file.txt") file.sim_size = 0 - assert folder.check_hash() is False + folder.check_hash() assert folder.health_status == FileSystemItemHealthStatus.CORRUPT @@ -120,8 +135,8 @@ def test_real_folder_check_hash(file_system): folder: Folder = file_system.create_folder(folder_name="test_folder") file_system.create_file(file_name="test_file.txt", folder_name="test_folder", real=True) - assert folder.check_hash() is True - + folder.check_hash() + assert folder.health_status == FileSystemItemHealthStatus.GOOD # change simulated file size file = folder.get_file(file_name="test_file.txt") @@ -129,5 +144,5 @@ def test_real_folder_check_hash(file_system): with open(file.sim_path, "a") as f: f.write("get hacked scrub lol xD\n") - assert folder.check_hash() is False + folder.check_hash() assert folder.health_status == FileSystemItemHealthStatus.CORRUPT diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_folder_actions.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_folder_actions.py new file mode 100644 index 00000000..05259320 --- /dev/null +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_folder_actions.py @@ -0,0 +1,103 @@ +from typing import Tuple + +import pytest + +from primaite.simulator.file_system.file_system import File, FileSystem, Folder +from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus + + +@pytest.fixture(scope="function") +def populated_file_system(file_system) -> Tuple[FileSystem, Folder, File]: + """Create a file system with a folder and file.""" + folder = file_system.create_folder(folder_name="test_folder") + file = file_system.create_file(folder_name="test_folder", file_name="test_file.txt") + + return file_system, folder, file + + +def test_folder_scan_request(populated_file_system): + """Test that an agent can request a folder scan.""" + fs, folder, file = populated_file_system + fs.create_file(file_name="test_file2.txt", folder_name="test_folder") + + file1: File = folder.get_file_by_id(file_uuid=list(folder.files)[1]) + file2: File = folder.get_file_by_id(file_uuid=list(folder.files)[0]) + + folder.corrupt() + assert folder.health_status == FileSystemItemHealthStatus.CORRUPT + assert folder.visible_health_status == FileSystemItemHealthStatus.GOOD + assert file1.visible_health_status == FileSystemItemHealthStatus.GOOD + assert file2.visible_health_status == FileSystemItemHealthStatus.GOOD + + fs.apply_request(request=["folder", folder.uuid, "scan"]) + + folder.apply_timestep(timestep=0) + + assert folder.health_status == FileSystemItemHealthStatus.CORRUPT + assert folder.visible_health_status == FileSystemItemHealthStatus.GOOD + assert file1.visible_health_status == FileSystemItemHealthStatus.GOOD + assert file2.visible_health_status == FileSystemItemHealthStatus.GOOD + + folder.apply_timestep(timestep=1) + + assert folder.health_status == FileSystemItemHealthStatus.CORRUPT + assert folder.visible_health_status == FileSystemItemHealthStatus.CORRUPT + assert file1.visible_health_status == FileSystemItemHealthStatus.CORRUPT + assert file2.visible_health_status == FileSystemItemHealthStatus.CORRUPT + + +def test_folder_checkhash_request(populated_file_system): + """Test that an agent can request a folder hash check.""" + fs, folder, file = populated_file_system + + fs.apply_request(request=["folder", folder.uuid, "checkhash"]) + + assert folder.health_status == FileSystemItemHealthStatus.GOOD + file.sim_size = 0 + + fs.apply_request(request=["folder", folder.uuid, "checkhash"]) + assert folder.health_status == FileSystemItemHealthStatus.CORRUPT + + +def test_folder_repair_request(populated_file_system): + """Test that an agent can request a folder repair.""" + fs, folder, file = populated_file_system + + folder.corrupt() + assert file.health_status == FileSystemItemHealthStatus.CORRUPT + assert folder.health_status == FileSystemItemHealthStatus.CORRUPT + + fs.apply_request(request=["folder", folder.uuid, "repair"]) + assert file.health_status == FileSystemItemHealthStatus.GOOD + assert folder.health_status == FileSystemItemHealthStatus.GOOD + + +def test_folder_restore_request(populated_file_system): + pass + + +def test_folder_corrupt_request(populated_file_system): + """Test that an agent can request a folder corruption.""" + fs, folder, file = populated_file_system + fs.apply_request(request=["folder", folder.uuid, "corrupt"]) + assert file.health_status == FileSystemItemHealthStatus.CORRUPT + assert folder.health_status == FileSystemItemHealthStatus.CORRUPT + + +def test_deleted_folder_and_its_files_cannot_be_interacted_with(populated_file_system): + """Test that actions cannot affect deleted folder and its child files.""" + fs, folder, file = populated_file_system + assert fs.get_file(folder_name=folder.name, file_name=file.name) is not None + + fs.apply_request(request=["file", file.uuid, "corrupt"]) + assert fs.get_file(folder_name=folder.name, file_name=file.name).health_status == FileSystemItemHealthStatus.CORRUPT + + fs.apply_request(request=["delete", "folder", folder.uuid]) + assert fs.get_file(folder_name=folder.name, file_name=file.name) is None + + fs.apply_request(request=["file", file.uuid, "repair"]) + + deleted_folder = fs.deleted_folders.get(folder.uuid) + deleted_file = deleted_folder.deleted_files.get(file.uuid) + + assert deleted_file.health_status is not FileSystemItemHealthStatus.GOOD From e70ceec716e498b32bbc39d35579a82214b285a8 Mon Sep 17 00:00:00 2001 From: "Czar.Echavez" Date: Mon, 6 Nov 2023 10:22:08 +0000 Subject: [PATCH 5/9] #1962: folder/file restore logic --- src/primaite/simulator/file_system/file.py | 21 +++-- .../simulator/file_system/file_system.py | 78 +++++++++++++++---- src/primaite/simulator/file_system/folder.py | 32 ++++++-- .../_simulator/_file_system/test_file.py | 10 ++- .../_file_system/test_file_system_actions.py | 53 +++++++++++++ 5 files changed, 161 insertions(+), 33 deletions(-) diff --git a/src/primaite/simulator/file_system/file.py b/src/primaite/simulator/file_system/file.py index 04502c0f..d9b02e8e 100644 --- a/src/primaite/simulator/file_system/file.py +++ b/src/primaite/simulator/file_system/file.py @@ -163,14 +163,6 @@ class File(FileSystemItemABC): path = self.folder.name + "/" + self.name self.sys_log.info(f"Repaired file {self.sim_path if self.sim_path else path}") - def restore(self) -> None: - """Restore a corrupted File by setting the status to FileSystemItemStatus.GOOD.""" - if self.health_status == FileSystemItemHealthStatus.CORRUPT: - self.health_status = FileSystemItemHealthStatus.GOOD - - path = self.folder.name + "/" + self.name - self.sys_log.info(f"Restored file {self.sim_path if self.sim_path else path}") - def corrupt(self) -> None: """Corrupt a File by setting the status to FileSystemItemStatus.CORRUPT.""" if self.deleted: @@ -184,10 +176,17 @@ class File(FileSystemItemABC): path = self.folder.name + "/" + self.name self.sys_log.info(f"Corrupted file {self.sim_path if self.sim_path else path}") - def restore(self) -> bool: + def restore(self) -> None: """Determines if the file needs to be repaired or unmarked as deleted.""" - super().restore() - return True + if self.deleted: + self.deleted = False + return + + if self.health_status == FileSystemItemHealthStatus.CORRUPT: + self.health_status = FileSystemItemHealthStatus.GOOD + + path = self.folder.name + "/" + self.name + self.sys_log.info(f"Restored file {self.sim_path if self.sim_path else path}") def delete(self): """Marks the file as deleted.""" diff --git a/src/primaite/simulator/file_system/file_system.py b/src/primaite/simulator/file_system/file_system.py index 7f2d41e5..b1688045 100644 --- a/src/primaite/simulator/file_system/file_system.py +++ b/src/primaite/simulator/file_system/file_system.py @@ -49,12 +49,27 @@ class FileSystem(SimComponent): name="folder", request_type=RequestType(func=lambda request, context: self.delete_folder_by_id(folder_uuid=request[0])), ) - rm.add_request( name="delete", request_type=RequestType(func=self._delete_manager), ) + self._restore_manager = RequestManager() + self._restore_manager.add_request( + name="file", + request_type=RequestType( + func=lambda request, context: self.restore_file(folder_uuid=request[0], file_uuid=request[1]) + ), + ) + self._restore_manager.add_request( + name="folder", + request_type=RequestType(func=lambda request, context: self.restore_folder(folder_uuid=request[0])), + ) + rm.add_request( + name="restore", + request_type=RequestType(func=self._restore_manager), + ) + self._folder_request_manager = RequestManager() rm.add_request("folder", RequestType(func=self._folder_request_manager)) @@ -164,13 +179,19 @@ class FileSystem(SimComponent): """ return self._folders_by_name.get(folder_name) - def get_folder_by_id(self, folder_uuid: str) -> Optional[Folder]: + def get_folder_by_id(self, folder_uuid: str, include_deleted: bool = False) -> Optional[Folder]: """ Get a folder by its uuid if it exists. :param: folder_uuid: The folder uuid. + :param: include_deleted: If true, the deleted folders will also be checked :return: The matching Folder. """ + if include_deleted: + folder = self.deleted_folders.get(folder_uuid) + if folder: + return folder + return self.folders.get(folder_uuid) ############################################################### @@ -244,7 +265,7 @@ class FileSystem(SimComponent): :param: include_deleted: If true, the deleted files will also be checked :return: An instance of File if it exists, otherwise `None`. """ - folder = self.folders.get(folder_uuid) + folder = self.get_folder_by_id(folder_uuid=folder_uuid, include_deleted=include_deleted) if folder: return folder.get_file_by_id(file_uuid=file_uuid, include_deleted=include_deleted) @@ -291,7 +312,11 @@ class FileSystem(SimComponent): if folder: file = folder.get_file_by_id(file_uuid=file_uuid) - self.delete_file(folder_name=folder.name, file_name=file.name) + + if file: + self.delete_file(folder_name=folder.name, file_name=file.name) + else: + self.sys_log.error(f"Unable to delete file that does not exist. (id: {file_uuid})") def move_file(self, src_folder_name: str, src_file_name: str, dst_folder_name: str): """ @@ -387,20 +412,47 @@ class FileSystem(SimComponent): for folder_id in self.folders: self.folders[folder_id].reveal_to_red(instant_scan=instant_scan) - def restore_folder(self, folder_id: str): - """TODO.""" - pass + def restore_folder(self, folder_uuid: str): + """ + Restore a folder. - def restore_file(self, folder_id: str, file_id: str): + Checks the current folder's status and applies the correct fix for the folder. + + :param: folder_uuid: id of the folder to restore + :type: folder_uuid: str + """ + folder = self.get_folder_by_id(folder_uuid=folder_uuid, include_deleted=True) + + if folder is None: + self.sys_log.error(f"Unable to restore folder with uuid {folder_uuid}. Folder does not exist.") + return + + folder.restore() + self.folders[folder.uuid] = folder + self._folders_by_name[folder.name] = folder + + if folder.deleted: + self.deleted_folders.pop(folder.uuid) + + def restore_file(self, folder_uuid: str, file_uuid: str): """ Restore a file. Checks the current file's status and applies the correct fix for the file. - :param: folder_id: id of the folder where the file is stored - :type: folder_id: str + :param: folder_uuid: id of the folder where the file is stored + :type: folder_uuid: str - :param: folder_id: id of the file to restore - :type: folder_id: str + :param: file_uuid: id of the file to restore + :type: file_uuid: str """ - pass + folder = self.get_folder_by_id(folder_uuid=folder_uuid, include_deleted=True) + + if folder: + file = folder.get_file_by_id(file_uuid=file_uuid, include_deleted=True) + + if file is None: + self.sys_log.error(f"Unable to restore file with uuid {file_uuid}. File does not exist.") + return + + folder.restore_file(file_uuid=file_uuid) diff --git a/src/primaite/simulator/file_system/folder.py b/src/primaite/simulator/file_system/folder.py index e12d4e12..f19f4efa 100644 --- a/src/primaite/simulator/file_system/folder.py +++ b/src/primaite/simulator/file_system/folder.py @@ -210,15 +210,24 @@ class Folder(FileSystemItemABC): self.files = {} self._files_by_name = {} - def restore_file(self, file: Optional[File]): + def restore_file(self, file_uuid: str): """ Restores a file. - The method can take a File object or a file id. - - :param file: The file to restore + :param file_uuid: The id of the file to restore """ - pass + # if the file was not deleted, run a repair + file = self.get_file_by_id(file_uuid=file_uuid, include_deleted=True) + if not file: + self.sys_log.error(f"Unable to restore file with uuid {file_uuid}. File does not exist.") + return + + file.restore() + self.files[file.uuid] = file + self._files_by_name[file.name] = file + + if file.deleted: + self.deleted_files.pop(file_uuid) def quarantine(self): """Quarantines the File System Folder.""" @@ -338,7 +347,18 @@ class Folder(FileSystemItemABC): If the folder is deleted, restore the folder by setting deleted status to False. """ - pass + # repair all files + for file_id in self.files: + self.restore_file(file_uuid=file_id) + + deleted_files = self.deleted_files.copy() + for file_id in deleted_files: + self.restore_file(file_uuid=file_id) + + if self.deleted: + self.deleted = False + elif self.health_status == FileSystemItemHealthStatus.CORRUPT: + self.health_status = FileSystemItemHealthStatus.GOOD def corrupt(self) -> None: """Corrupt a File by setting the folder and containing files status to FileSystemItemStatus.CORRUPT.""" diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_file.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_file.py index 94ccde83..32efe029 100644 --- a/tests/unit_tests/_primaite/_simulator/_file_system/test_file.py +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_file.py @@ -65,14 +65,18 @@ def test_real_file_check_hash(file_system): assert file.health_status == FileSystemItemHealthStatus.CORRUPT -def test_file_corrupt_repair(file_system): +def test_file_corrupt_repair_restore(file_system): """Test the ability to corrupt and repair files.""" file: File = file_system.create_file(file_name="test_file.txt", folder_name="test_folder") file.corrupt() - assert file.health_status == FileSystemItemHealthStatus.CORRUPT file.repair() - + assert file.health_status == FileSystemItemHealthStatus.GOOD + + file.corrupt() + assert file.health_status == FileSystemItemHealthStatus.CORRUPT + + file.restore() assert file.health_status == FileSystemItemHealthStatus.GOOD diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system_actions.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system_actions.py index d6bbd285..81616420 100644 --- a/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system_actions.py +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system_actions.py @@ -33,3 +33,56 @@ def test_folder_delete_request(populated_file_system): fs.apply_request(request=["delete", "folder", folder.uuid]) assert fs.get_folder_by_id(folder_uuid=folder.uuid) is None assert fs.get_file_by_id(folder_uuid=folder.uuid, file_uuid=file.uuid) is None + + +def test_file_restore_request(populated_file_system): + """Test that an agent can request that a file can be restored.""" + fs, folder, file = populated_file_system + assert fs.get_file_by_id(folder_uuid=folder.uuid, file_uuid=file.uuid) is not None + + fs.apply_request(request=["delete", "file", folder.uuid, file.uuid]) + assert fs.get_file(folder_name=folder.name, file_name=file.name) is None + assert fs.get_file_by_id(folder_uuid=folder.uuid, file_uuid=file.uuid, include_deleted=True).deleted is True + + fs.apply_request(request=["restore", "file", folder.uuid, file.uuid]) + assert fs.get_file(folder_name=folder.name, file_name=file.name) is not None + assert fs.get_file(folder_name=folder.name, file_name=file.name).deleted is False + + fs.apply_request(request=["file", file.uuid, "corrupt"]) + assert fs.get_file(folder_name=folder.name, file_name=file.name).health_status == FileSystemItemHealthStatus.CORRUPT + + fs.apply_request(request=["restore", "file", folder.uuid, file.uuid]) + assert fs.get_file(folder_name=folder.name, file_name=file.name).health_status == FileSystemItemHealthStatus.GOOD + + +def test_folder_restore_request(populated_file_system): + """Test that an agent can request that a folder can be restored.""" + fs, folder, file = populated_file_system + assert fs.get_folder_by_id(folder_uuid=folder.uuid) is not None + assert fs.get_file_by_id(folder_uuid=folder.uuid, file_uuid=file.uuid) is not None + + # delete folder + fs.apply_request(request=["delete", "folder", folder.uuid]) + assert fs.get_folder(folder_name=folder.name) is None + assert fs.get_folder_by_id(folder_uuid=folder.uuid, include_deleted=True).deleted is True + + assert fs.get_file(folder_name=folder.name, file_name=file.name) is None + assert fs.get_file_by_id(folder_uuid=folder.uuid, file_uuid=file.uuid, include_deleted=True).deleted is True + + # restore folder + fs.apply_request(request=["restore", "folder", folder.uuid]) + assert fs.get_file(folder_name=folder.name, file_name=file.name) is not None + assert fs.get_file(folder_name=folder.name, file_name=file.name).deleted is False + + assert fs.get_file(folder_name=folder.name, file_name=file.name) is not None + assert fs.get_file_by_id(folder_uuid=folder.uuid, file_uuid=file.uuid, include_deleted=True).deleted is False + + # corrupt folder + fs.apply_request(request=["folder", folder.uuid, "corrupt"]) + assert fs.get_folder(folder_name=folder.name).health_status == FileSystemItemHealthStatus.CORRUPT + assert fs.get_file(folder_name=folder.name, file_name=file.name).health_status == FileSystemItemHealthStatus.CORRUPT + + # restore folder + fs.apply_request(request=["restore", "folder", folder.uuid]) + assert fs.get_folder(folder_name=folder.name).health_status == FileSystemItemHealthStatus.GOOD + assert fs.get_file(folder_name=folder.name, file_name=file.name).health_status == FileSystemItemHealthStatus.GOOD From 535c1b19ab27f9dea48006b349e7ce28812867eb Mon Sep 17 00:00:00 2001 From: "Czar.Echavez" Date: Mon, 6 Nov 2023 11:12:06 +0000 Subject: [PATCH 6/9] #1962: attempt to make the timestep actions look neater + adding logic that allows restoring a folder take multiple timesteps --- src/primaite/simulator/file_system/folder.py | 96 +++++++++++++------ .../_file_system/test_file_system_actions.py | 36 ++++++- .../_simulator/_file_system/test_folder.py | 2 + .../_file_system/test_folder_actions.py | 1 + 4 files changed, 104 insertions(+), 31 deletions(-) diff --git a/src/primaite/simulator/file_system/folder.py b/src/primaite/simulator/file_system/folder.py index f19f4efa..67e93e16 100644 --- a/src/primaite/simulator/file_system/folder.py +++ b/src/primaite/simulator/file_system/folder.py @@ -22,11 +22,23 @@ class Folder(FileSystemItemABC): deleted_files: Dict[str, File] = {} "Files that have been deleted." - scan_duration: int = -1 - "How many timesteps to complete a scan." + scan_duration: int = 3 + "How many timesteps to complete a scan. Default 3 timesteps" - red_scan_duration: int = -1 - "How many timesteps to complete reveal to red scan." + scan_countdown: int = 0 + "Time steps needed until scan completion." + + red_scan_duration: int = 3 + "How many timesteps to complete reveal to red scan. Default 3 timesteps" + + red_scan_countdown: int = 0 + "Time steps needed until red scan completion." + + restore_duration: int = 3 + "How many timesteps to complete a restore. Default 3 timesteps" + + restore_countdown: int = 0 + "Time steps needed until restore completion." def __init__(self, **kwargs): """ @@ -55,6 +67,7 @@ class Folder(FileSystemItemABC): """ state = super().describe_state() state["files"] = {file.name: file.describe_state() for uuid, file in self.files.items()} + state["deleted_files"] = {file.name: file.describe_state() for uuid, file in self.deleted_files.items()} return state def show(self, markdown: bool = False): @@ -94,30 +107,57 @@ class Folder(FileSystemItemABC): """ super().apply_timestep(timestep=timestep) - # scan files each timestep - if self.scan_duration >= 0: - self.scan_duration -= 1 + self._scan_timestep() - if self.scan_duration == 0: + self._reveal_to_red_timestep() + + self._restoring_timestep() + + # apply timestep to files in folder + for file_id in self.files: + self.files[file_id].apply_timestep(timestep=timestep) + + def _scan_timestep(self) -> None: + """Apply the scan action timestep.""" + if self.scan_countdown >= 0: + self.scan_countdown -= 1 + + if self.scan_countdown == 0: for file_id in self.files: file = self.get_file_by_id(file_uuid=file_id) file.scan() if file.visible_health_status == FileSystemItemHealthStatus.CORRUPT: self.visible_health_status = FileSystemItemHealthStatus.CORRUPT - # red scan file at each step - if self.red_scan_duration >= 0: - self.red_scan_duration -= 1 + def _reveal_to_red_timestep(self) -> None: + """Apply reveal to red timestep.""" + if self.red_scan_countdown >= 0: + self.red_scan_countdown -= 1 - if self.red_scan_duration == 0: + if self.red_scan_countdown == 0: self.revealed_to_red = True for file_id in self.files: file = self.get_file_by_id(file_uuid=file_id) file.reveal_to_red() - # apply timestep to files in folder - for file_id in self.files: - self.files[file_id].apply_timestep(timestep=timestep) + def _restoring_timestep(self) -> None: + """Apply restoring timestep.""" + if self.restore_countdown >= 0: + self.restore_countdown -= 1 + + if self.restore_countdown == 0: + # repair all files + for file_id in self.files: + self.restore_file(file_uuid=file_id) + + deleted_files = self.deleted_files.copy() + for file_id in deleted_files: + self.restore_file(file_uuid=file_id) + + if self.deleted: + self.deleted = False + elif self.health_status in [FileSystemItemHealthStatus.CORRUPT, FileSystemItemHealthStatus.RESTORING]: + self.health_status = FileSystemItemHealthStatus.GOOD def get_file(self, file_name: str) -> Optional[File]: """ @@ -259,9 +299,9 @@ class Folder(FileSystemItemABC): self.visible_health_status = FileSystemItemHealthStatus.CORRUPT return - if self.scan_duration <= 0: + if self.scan_countdown <= 0: # scan one file per timestep - self.scan_duration = len(self.files) + self.scan_countdown = self.scan_duration self.sys_log.info(f"Scanning folder {self.name} (id: {self.uuid})") else: # scan already in progress @@ -284,9 +324,9 @@ class Folder(FileSystemItemABC): file.reveal_to_red() return - if self.red_scan_duration <= 0: + if self.red_scan_countdown <= 0: # scan one file per timestep - self.red_scan_duration = len(self.files) + self.red_scan_countdown = self.red_scan_duration self.sys_log.info(f"Folder revealed to red agent: {self.name} (id: {self.uuid})") else: # scan already in progress @@ -347,18 +387,16 @@ class Folder(FileSystemItemABC): If the folder is deleted, restore the folder by setting deleted status to False. """ - # repair all files - for file_id in self.files: - self.restore_file(file_uuid=file_id) - - deleted_files = self.deleted_files.copy() - for file_id in deleted_files: - self.restore_file(file_uuid=file_id) - if self.deleted: self.deleted = False - elif self.health_status == FileSystemItemHealthStatus.CORRUPT: - self.health_status = FileSystemItemHealthStatus.GOOD + + if self.restore_countdown <= 0: + self.restore_countdown = self.restore_duration + self.health_status = FileSystemItemHealthStatus.RESTORING + self.sys_log.info(f"Restoring folder: {self.name} (id: {self.uuid})") + else: + # scan already in progress + self.sys_log.info(f"Folder restoration already in progress {self.name} (id: {self.uuid})") def corrupt(self) -> None: """Corrupt a File by setting the folder and containing files status to FileSystemItemStatus.CORRUPT.""" diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system_actions.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system_actions.py index 81616420..29642a8d 100644 --- a/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system_actions.py +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system_actions.py @@ -71,7 +71,25 @@ def test_folder_restore_request(populated_file_system): # restore folder fs.apply_request(request=["restore", "folder", folder.uuid]) + fs.apply_timestep(timestep=0) + assert fs.get_folder(folder_name=folder.name) is not None + assert ( + fs.get_folder_by_id(folder_uuid=folder.uuid, include_deleted=True).health_status + == FileSystemItemHealthStatus.RESTORING + ) + assert fs.get_folder_by_id(folder_uuid=folder.uuid, include_deleted=True).deleted is False + + assert fs.get_file(folder_name=folder.name, file_name=file.name) is None + assert fs.get_file_by_id(folder_uuid=folder.uuid, file_uuid=file.uuid, include_deleted=True).deleted is True + + fs.apply_timestep(timestep=1) + fs.apply_timestep(timestep=2) + assert fs.get_file(folder_name=folder.name, file_name=file.name) is not None + assert ( + fs.get_file(folder_name=folder.name, file_name=file.name).health_status + is not FileSystemItemHealthStatus.RESTORING + ) assert fs.get_file(folder_name=folder.name, file_name=file.name).deleted is False assert fs.get_file(folder_name=folder.name, file_name=file.name) is not None @@ -84,5 +102,19 @@ def test_folder_restore_request(populated_file_system): # restore folder fs.apply_request(request=["restore", "folder", folder.uuid]) - assert fs.get_folder(folder_name=folder.name).health_status == FileSystemItemHealthStatus.GOOD - assert fs.get_file(folder_name=folder.name, file_name=file.name).health_status == FileSystemItemHealthStatus.GOOD + fs.apply_timestep(timestep=0) + assert fs.get_folder(folder_name=folder.name).health_status == FileSystemItemHealthStatus.RESTORING + assert fs.get_file(folder_name=folder.name, file_name=file.name).health_status == FileSystemItemHealthStatus.CORRUPT + + fs.apply_timestep(timestep=1) + fs.apply_timestep(timestep=2) + + assert fs.get_file(folder_name=folder.name, file_name=file.name) is not None + assert ( + fs.get_file(folder_name=folder.name, file_name=file.name).health_status + is not FileSystemItemHealthStatus.RESTORING + ) + assert fs.get_file(folder_name=folder.name, file_name=file.name).deleted is False + + assert fs.get_file(folder_name=folder.name, file_name=file.name) is not None + assert fs.get_file_by_id(folder_uuid=folder.uuid, file_uuid=file.uuid, include_deleted=True).deleted is False diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_folder.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_folder.py index 56f2f6fb..bada2dab 100644 --- a/tests/unit_tests/_primaite/_simulator/_file_system/test_folder.py +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_folder.py @@ -64,6 +64,7 @@ def test_folder_scan(file_system): assert file2.visible_health_status == FileSystemItemHealthStatus.GOOD folder.apply_timestep(timestep=1) + folder.apply_timestep(timestep=2) assert folder.health_status == FileSystemItemHealthStatus.CORRUPT assert folder.visible_health_status == FileSystemItemHealthStatus.CORRUPT @@ -93,6 +94,7 @@ def test_folder_reveal_to_red_scan(file_system): assert file2.revealed_to_red is False folder.apply_timestep(timestep=1) + folder.apply_timestep(timestep=2) assert folder.revealed_to_red is True assert file1.revealed_to_red is True diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_folder_actions.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_folder_actions.py index 05259320..9be4d5d1 100644 --- a/tests/unit_tests/_primaite/_simulator/_file_system/test_folder_actions.py +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_folder_actions.py @@ -39,6 +39,7 @@ def test_folder_scan_request(populated_file_system): assert file2.visible_health_status == FileSystemItemHealthStatus.GOOD folder.apply_timestep(timestep=1) + folder.apply_timestep(timestep=2) assert folder.health_status == FileSystemItemHealthStatus.CORRUPT assert folder.visible_health_status == FileSystemItemHealthStatus.CORRUPT From eba10ae5ef3ebab0d95629b9fb34edc6a89f936a Mon Sep 17 00:00:00 2001 From: "Czar.Echavez" Date: Mon, 6 Nov 2023 11:56:44 +0000 Subject: [PATCH 7/9] #1962: clean up tests + improve the show command which shows the folders and files in file system --- .../simulator/file_system/file_system.py | 16 ++-- src/primaite/simulator/file_system/folder.py | 4 +- .../_file_system/test_file_actions.py | 18 +++- .../_file_system/test_file_system.py | 26 ++++++ .../_file_system/test_file_system_actions.py | 87 +------------------ .../_file_system/test_folder_actions.py | 63 +++++++++++++- 6 files changed, 121 insertions(+), 93 deletions(-) diff --git a/src/primaite/simulator/file_system/file_system.py b/src/primaite/simulator/file_system/file_system.py index b1688045..41f02270 100644 --- a/src/primaite/simulator/file_system/file_system.py +++ b/src/primaite/simulator/file_system/file_system.py @@ -94,7 +94,7 @@ class FileSystem(SimComponent): :param markdown: Flag indicating if output should be in markdown format. :param full: Flag indicating if to show full files. """ - headers = ["Folder", "Size"] + headers = ["Folder", "Size", "Deleted"] if full: headers[0] = "File Path" table = PrettyTable(headers) @@ -102,12 +102,17 @@ class FileSystem(SimComponent): table.set_style(MARKDOWN) table.align = "l" table.title = f"{self.sys_log.hostname} File System" - for folder in self.folders.values(): + folders = {**self.folders, **self.deleted_folders} + for folder in folders.values(): if not full: - table.add_row([folder.name, folder.size_str]) + table.add_row([folder.name, folder.size_str, folder.deleted]) else: - for file in folder.files.values(): - table.add_row([file.path, file.size_str]) + files = {**folder.files, **folder.deleted_files} + if not files: + table.add_row([folder.name, folder.size_str, folder.deleted]) + else: + for file in files.values(): + table.add_row([file.path, file.size_str, file.deleted]) if full: print(table.get_string(sortby="File Path")) else: @@ -380,6 +385,7 @@ class FileSystem(SimComponent): """ state = super().describe_state() state["folders"] = {folder.name: folder.describe_state() for folder in self.folders.values()} + state["deleted_folders"] = {folder.name: folder.describe_state() for folder in self.deleted_folders.values()} return state def apply_timestep(self, timestep: int) -> None: diff --git a/src/primaite/simulator/file_system/folder.py b/src/primaite/simulator/file_system/folder.py index 67e93e16..f0d55ef8 100644 --- a/src/primaite/simulator/file_system/folder.py +++ b/src/primaite/simulator/file_system/folder.py @@ -76,13 +76,13 @@ class Folder(FileSystemItemABC): :param markdown: Whether to display the table in Markdown format or not. Default is `False`. """ - table = PrettyTable(["File", "Size"]) + table = PrettyTable(["File", "Size", "Deleted"]) if markdown: table.set_style(MARKDOWN) table.align = "l" table.title = f"{self.sys_log.hostname} File System Folder ({self.name})" for file in self.files.values(): - table.add_row([file.name, file.size_str]) + table.add_row([file.name, file.size_str, file.deleted]) print(table.get_string(sortby="File")) @property diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_file_actions.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_actions.py index f43c6b22..44354325 100644 --- a/tests/unit_tests/_primaite/_simulator/_file_system/test_file_actions.py +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_actions.py @@ -55,7 +55,23 @@ def test_file_repair_request(populated_file_system): def test_file_restore_request(populated_file_system): - pass + """Test that an agent can request that a file can be restored.""" + fs, folder, file = populated_file_system + assert fs.get_file_by_id(folder_uuid=folder.uuid, file_uuid=file.uuid) is not None + + fs.apply_request(request=["delete", "file", folder.uuid, file.uuid]) + assert fs.get_file(folder_name=folder.name, file_name=file.name) is None + assert fs.get_file_by_id(folder_uuid=folder.uuid, file_uuid=file.uuid, include_deleted=True).deleted is True + + fs.apply_request(request=["restore", "file", folder.uuid, file.uuid]) + assert fs.get_file(folder_name=folder.name, file_name=file.name) is not None + assert fs.get_file(folder_name=folder.name, file_name=file.name).deleted is False + + fs.apply_request(request=["file", file.uuid, "corrupt"]) + assert fs.get_file(folder_name=folder.name, file_name=file.name).health_status == FileSystemItemHealthStatus.CORRUPT + + fs.apply_request(request=["restore", "file", folder.uuid, file.uuid]) + assert fs.get_file(folder_name=folder.name, file_name=file.name).health_status == FileSystemItemHealthStatus.GOOD def test_file_corrupt_request(populated_file_system): diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system.py index 03c6ad12..86baa1f8 100644 --- a/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system.py +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system.py @@ -18,6 +18,8 @@ def test_create_folder_and_file(file_system): assert file_system.get_folder("test_folder").get_file("test_file.txt") + file_system.show(full=True) + def test_create_file_no_folder(file_system): """Tests that creating a file without a folder creates a folder and sets that as the file's parent.""" @@ -27,6 +29,8 @@ def test_create_file_no_folder(file_system): assert file_system.get_folder("root").get_file("test_file.txt").file_type == FileType.TXT assert file_system.get_folder("root").get_file("test_file.txt").size == 10 + file_system.show(full=True) + def test_delete_file(file_system): """Tests that a file can be deleted.""" @@ -39,6 +43,8 @@ def test_delete_file(file_system): assert len(file_system.get_folder("root").files) == 0 assert len(file_system.get_folder("root").deleted_files) == 1 + file_system.show(full=True) + def test_delete_non_existent_file(file_system): """Tests deleting a non existent file.""" @@ -56,6 +62,8 @@ def test_delete_non_existent_file(file_system): # The folder should still have 1 file assert len(file_system.get_folder("root").files) == 1 + file_system.show(full=True) + def test_delete_folder(file_system): file_system.create_folder(folder_name="test_folder") @@ -66,6 +74,8 @@ def test_delete_folder(file_system): assert len(file_system.deleted_folders) == 1 + file_system.show(full=True) + def test_create_duplicate_folder(file_system): """Test that creating a duplicate folder throws exception.""" @@ -78,6 +88,8 @@ def test_create_duplicate_folder(file_system): assert len(file_system.folders) is 2 + file_system.show(full=True) + def test_create_duplicate_file(file_system): """Test that creating a duplicate file throws exception.""" @@ -94,6 +106,8 @@ def test_create_duplicate_file(file_system): assert len(file_system.get_folder("test_folder").files) == 1 + file_system.show(full=True) + def test_deleting_a_non_existent_folder(file_system): file_system.create_folder(folder_name="test_folder") @@ -102,6 +116,8 @@ def test_deleting_a_non_existent_folder(file_system): file_system.delete_folder(folder_name="does not exist!") assert len(file_system.folders) == 2 + file_system.show(full=True) + def test_deleting_root_folder_fails(file_system): assert len(file_system.folders) == 1 @@ -109,6 +125,8 @@ def test_deleting_root_folder_fails(file_system): file_system.delete_folder(folder_name="root") assert len(file_system.folders) == 1 + file_system.show(full=True) + def test_move_file(file_system): """Tests the file move function.""" @@ -127,6 +145,8 @@ def test_move_file(file_system): assert len(file_system.get_folder("dst_folder").files) == 1 assert file_system.get_file("dst_folder", "test_file.txt").uuid == original_uuid + file_system.show(full=True) + def test_copy_file(file_system): """Tests the file copy function.""" @@ -145,6 +165,8 @@ def test_copy_file(file_system): assert len(file_system.get_folder("dst_folder").files) == 1 assert file_system.get_file("dst_folder", "test_file.txt").uuid != original_uuid + file_system.show(full=True) + def test_get_file(file_system): """Test that files can be retrieved.""" @@ -162,6 +184,8 @@ def test_get_file(file_system): file_system.delete_folder(folder_name="test_folder") assert file_system.get_file_by_id(file_uuid=file2.uuid, include_deleted=True) is not None + file_system.show(full=True) + @pytest.mark.skip(reason="Skipping until we tackle serialisation") def test_serialisation(file_system): @@ -172,3 +196,5 @@ def test_serialisation(file_system): deserialised_file_sys = FileSystem.model_validate_json(serialised_file_sys) assert file_system.model_dump_json() == deserialised_file_sys.model_dump_json() + + file_system.show(full=True) diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system_actions.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system_actions.py index 29642a8d..30c0938e 100644 --- a/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system_actions.py +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system_actions.py @@ -23,6 +23,8 @@ def test_file_delete_request(populated_file_system): fs.apply_request(request=["delete", "file", folder.uuid, file.uuid]) assert fs.get_file(folder_name=folder.name, file_name=file.name) is None + fs.show(full=True) + def test_folder_delete_request(populated_file_system): """Test that an agent can request a folder deletion.""" @@ -34,87 +36,4 @@ def test_folder_delete_request(populated_file_system): assert fs.get_folder_by_id(folder_uuid=folder.uuid) is None assert fs.get_file_by_id(folder_uuid=folder.uuid, file_uuid=file.uuid) is None - -def test_file_restore_request(populated_file_system): - """Test that an agent can request that a file can be restored.""" - fs, folder, file = populated_file_system - assert fs.get_file_by_id(folder_uuid=folder.uuid, file_uuid=file.uuid) is not None - - fs.apply_request(request=["delete", "file", folder.uuid, file.uuid]) - assert fs.get_file(folder_name=folder.name, file_name=file.name) is None - assert fs.get_file_by_id(folder_uuid=folder.uuid, file_uuid=file.uuid, include_deleted=True).deleted is True - - fs.apply_request(request=["restore", "file", folder.uuid, file.uuid]) - assert fs.get_file(folder_name=folder.name, file_name=file.name) is not None - assert fs.get_file(folder_name=folder.name, file_name=file.name).deleted is False - - fs.apply_request(request=["file", file.uuid, "corrupt"]) - assert fs.get_file(folder_name=folder.name, file_name=file.name).health_status == FileSystemItemHealthStatus.CORRUPT - - fs.apply_request(request=["restore", "file", folder.uuid, file.uuid]) - assert fs.get_file(folder_name=folder.name, file_name=file.name).health_status == FileSystemItemHealthStatus.GOOD - - -def test_folder_restore_request(populated_file_system): - """Test that an agent can request that a folder can be restored.""" - fs, folder, file = populated_file_system - assert fs.get_folder_by_id(folder_uuid=folder.uuid) is not None - assert fs.get_file_by_id(folder_uuid=folder.uuid, file_uuid=file.uuid) is not None - - # delete folder - fs.apply_request(request=["delete", "folder", folder.uuid]) - assert fs.get_folder(folder_name=folder.name) is None - assert fs.get_folder_by_id(folder_uuid=folder.uuid, include_deleted=True).deleted is True - - assert fs.get_file(folder_name=folder.name, file_name=file.name) is None - assert fs.get_file_by_id(folder_uuid=folder.uuid, file_uuid=file.uuid, include_deleted=True).deleted is True - - # restore folder - fs.apply_request(request=["restore", "folder", folder.uuid]) - fs.apply_timestep(timestep=0) - assert fs.get_folder(folder_name=folder.name) is not None - assert ( - fs.get_folder_by_id(folder_uuid=folder.uuid, include_deleted=True).health_status - == FileSystemItemHealthStatus.RESTORING - ) - assert fs.get_folder_by_id(folder_uuid=folder.uuid, include_deleted=True).deleted is False - - assert fs.get_file(folder_name=folder.name, file_name=file.name) is None - assert fs.get_file_by_id(folder_uuid=folder.uuid, file_uuid=file.uuid, include_deleted=True).deleted is True - - fs.apply_timestep(timestep=1) - fs.apply_timestep(timestep=2) - - assert fs.get_file(folder_name=folder.name, file_name=file.name) is not None - assert ( - fs.get_file(folder_name=folder.name, file_name=file.name).health_status - is not FileSystemItemHealthStatus.RESTORING - ) - assert fs.get_file(folder_name=folder.name, file_name=file.name).deleted is False - - assert fs.get_file(folder_name=folder.name, file_name=file.name) is not None - assert fs.get_file_by_id(folder_uuid=folder.uuid, file_uuid=file.uuid, include_deleted=True).deleted is False - - # corrupt folder - fs.apply_request(request=["folder", folder.uuid, "corrupt"]) - assert fs.get_folder(folder_name=folder.name).health_status == FileSystemItemHealthStatus.CORRUPT - assert fs.get_file(folder_name=folder.name, file_name=file.name).health_status == FileSystemItemHealthStatus.CORRUPT - - # restore folder - fs.apply_request(request=["restore", "folder", folder.uuid]) - fs.apply_timestep(timestep=0) - assert fs.get_folder(folder_name=folder.name).health_status == FileSystemItemHealthStatus.RESTORING - assert fs.get_file(folder_name=folder.name, file_name=file.name).health_status == FileSystemItemHealthStatus.CORRUPT - - fs.apply_timestep(timestep=1) - fs.apply_timestep(timestep=2) - - assert fs.get_file(folder_name=folder.name, file_name=file.name) is not None - assert ( - fs.get_file(folder_name=folder.name, file_name=file.name).health_status - is not FileSystemItemHealthStatus.RESTORING - ) - assert fs.get_file(folder_name=folder.name, file_name=file.name).deleted is False - - assert fs.get_file(folder_name=folder.name, file_name=file.name) is not None - assert fs.get_file_by_id(folder_uuid=folder.uuid, file_uuid=file.uuid, include_deleted=True).deleted is False + fs.show(full=True) diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_folder_actions.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_folder_actions.py index 9be4d5d1..86db147a 100644 --- a/tests/unit_tests/_primaite/_simulator/_file_system/test_folder_actions.py +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_folder_actions.py @@ -74,7 +74,68 @@ def test_folder_repair_request(populated_file_system): def test_folder_restore_request(populated_file_system): - pass + """Test that an agent can request that a folder can be restored.""" + fs, folder, file = populated_file_system + assert fs.get_folder_by_id(folder_uuid=folder.uuid) is not None + assert fs.get_file_by_id(folder_uuid=folder.uuid, file_uuid=file.uuid) is not None + + # delete folder + fs.apply_request(request=["delete", "folder", folder.uuid]) + assert fs.get_folder(folder_name=folder.name) is None + assert fs.get_folder_by_id(folder_uuid=folder.uuid, include_deleted=True).deleted is True + + assert fs.get_file(folder_name=folder.name, file_name=file.name) is None + assert fs.get_file_by_id(folder_uuid=folder.uuid, file_uuid=file.uuid, include_deleted=True).deleted is True + + # restore folder + fs.apply_request(request=["restore", "folder", folder.uuid]) + fs.apply_timestep(timestep=0) + assert fs.get_folder(folder_name=folder.name) is not None + assert ( + fs.get_folder_by_id(folder_uuid=folder.uuid, include_deleted=True).health_status + == FileSystemItemHealthStatus.RESTORING + ) + assert fs.get_folder_by_id(folder_uuid=folder.uuid, include_deleted=True).deleted is False + + assert fs.get_file(folder_name=folder.name, file_name=file.name) is None + assert fs.get_file_by_id(folder_uuid=folder.uuid, file_uuid=file.uuid, include_deleted=True).deleted is True + + fs.apply_timestep(timestep=1) + fs.apply_timestep(timestep=2) + + assert fs.get_file(folder_name=folder.name, file_name=file.name) is not None + assert ( + fs.get_file(folder_name=folder.name, file_name=file.name).health_status + is not FileSystemItemHealthStatus.RESTORING + ) + assert fs.get_file(folder_name=folder.name, file_name=file.name).deleted is False + + assert fs.get_file(folder_name=folder.name, file_name=file.name) is not None + assert fs.get_file_by_id(folder_uuid=folder.uuid, file_uuid=file.uuid, include_deleted=True).deleted is False + + # corrupt folder + fs.apply_request(request=["folder", folder.uuid, "corrupt"]) + assert fs.get_folder(folder_name=folder.name).health_status == FileSystemItemHealthStatus.CORRUPT + assert fs.get_file(folder_name=folder.name, file_name=file.name).health_status == FileSystemItemHealthStatus.CORRUPT + + # restore folder + fs.apply_request(request=["restore", "folder", folder.uuid]) + fs.apply_timestep(timestep=0) + assert fs.get_folder(folder_name=folder.name).health_status == FileSystemItemHealthStatus.RESTORING + assert fs.get_file(folder_name=folder.name, file_name=file.name).health_status == FileSystemItemHealthStatus.CORRUPT + + fs.apply_timestep(timestep=1) + fs.apply_timestep(timestep=2) + + assert fs.get_file(folder_name=folder.name, file_name=file.name) is not None + assert ( + fs.get_file(folder_name=folder.name, file_name=file.name).health_status + is not FileSystemItemHealthStatus.RESTORING + ) + assert fs.get_file(folder_name=folder.name, file_name=file.name).deleted is False + + assert fs.get_file(folder_name=folder.name, file_name=file.name) is not None + assert fs.get_file_by_id(folder_uuid=folder.uuid, file_uuid=file.uuid, include_deleted=True).deleted is False def test_folder_corrupt_request(populated_file_system): From be4a4678770e51899a3919aeae2d3c903f6d5d04 Mon Sep 17 00:00:00 2001 From: "Czar.Echavez" Date: Wed, 8 Nov 2023 10:48:41 +0000 Subject: [PATCH 8/9] #1962: revert pulling from src --- src/primaite/cli.py | 10 ++--- src/primaite/game/agent/GATE_agents.py | 8 ++-- src/primaite/game/agent/actions.py | 4 +- src/primaite/game/agent/interface.py | 6 +-- src/primaite/game/agent/observations.py | 4 +- src/primaite/game/agent/rewards.py | 4 +- src/primaite/game/agent/scripted_agents.py | 2 +- src/primaite/game/session.py | 42 +++++++++---------- src/primaite/main.py | 6 +-- src/primaite/simulator/domain/account.py | 2 +- src/primaite/simulator/domain/controller.py | 4 +- src/primaite/simulator/file_system/file.py | 4 +- .../simulator/file_system/file_system.py | 10 ++--- .../file_system/file_system_item_abc.py | 4 +- src/primaite/simulator/file_system/folder.py | 6 +-- src/primaite/simulator/network/container.py | 12 +++--- .../simulator/network/hardware/base.py | 32 +++++++------- .../network/hardware/nodes/computer.py | 8 ++-- .../network/hardware/nodes/router.py | 12 +++--- .../network/hardware/nodes/server.py | 2 +- .../network/hardware/nodes/switch.py | 6 +-- src/primaite/simulator/network/networks.py | 28 ++++++------- .../simulator/network/protocols/arp.py | 2 +- .../simulator/network/protocols/dns.py | 2 +- .../simulator/network/protocols/ftp.py | 2 +- .../simulator/network/protocols/http.py | 2 +- .../network/transmission/data_link_layer.py | 12 +++--- src/primaite/simulator/sim_container.py | 6 +-- .../system/applications/application.py | 2 +- .../system/applications/database_client.py | 8 ++-- .../system/applications/web_browser.py | 10 ++--- .../simulator/system/core/packet_capture.py | 2 +- .../simulator/system/core/session_manager.py | 14 +++---- .../simulator/system/core/software_manager.py | 20 ++++----- src/primaite/simulator/system/core/sys_log.py | 2 +- .../simulator/system/processes/process.py | 2 +- .../services/database/database_service.py | 14 +++---- .../system/services/dns/dns_client.py | 10 ++--- .../system/services/dns/dns_server.py | 8 ++-- .../system/services/ftp/ftp_client.py | 14 +++---- .../system/services/ftp/ftp_server.py | 10 ++--- .../system/services/ftp/ftp_service.py | 8 ++-- .../red_services/data_manipulation_bot.py | 2 +- .../simulator/system/services/service.py | 4 +- .../system/services/web_server/web_server.py | 10 ++--- src/primaite/simulator/system/software.py | 10 ++--- src/primaite/utils/package_data.py | 2 +- src/primaite/utils/session_output_writer.py | 4 +- tests/conftest.py | 24 +++++------ .../test_uc2_data_manipulation_scenario.py | 10 ++--- .../test_action_integration.py | 12 +++--- .../test_permission_system.py | 4 +- .../game_layer/test_observations.py | 6 +-- .../network/test_frame_transmission.py | 2 +- .../network/test_link_connection.py | 2 +- .../network/test_network_creation.py | 4 +- .../network/test_nic_link_connection.py | 2 +- .../integration_tests/network/test_routing.py | 8 ++-- .../network/test_switched_network.py | 8 ++-- .../system/test_database_on_node.py | 8 ++-- .../system/test_dns_client_server.py | 10 ++--- .../system/test_ftp_client_server.py | 10 ++--- .../system/test_web_client_server.py | 12 +++--- .../_simulator/_domain/test_account.py | 2 +- .../_simulator/_file_system/test_file.py | 6 +-- .../_file_system/test_file_actions.py | 8 ++-- .../_file_system/test_file_system.py | 4 +- .../_file_system/test_file_system_actions.py | 6 +-- .../_simulator/_file_system/test_folder.py | 6 +-- .../_file_system/test_folder_actions.py | 8 ++-- .../_network/_hardware/nodes/test_acl.py | 6 +-- .../_simulator/_network/_hardware/test_nic.py | 2 +- .../_network/_hardware/test_node.py | 2 +- .../_network/_hardware/test_node_actions.py | 10 ++--- .../_transmission/test_data_link_layer.py | 8 ++-- .../_transmission/test_network_layer.py | 2 +- .../_simulator/_network/test_container.py | 2 +- .../_system/_applications/test_web_browser.py | 10 ++--- .../test_data_manipulation_bot.py | 10 ++--- .../_system/_services/test_database.py | 4 +- .../_simulator/_system/_services/test_dns.py | 16 +++---- .../_simulator/_system/_services/test_ftp.py | 16 +++---- .../_system/_services/test_service_actions.py | 4 +- .../_system/_services/test_services.py | 4 +- .../_system/_services/test_web_server.py | 10 ++--- .../_primaite/_simulator/test_core.py | 2 +- .../_simulator/test_sim_conatiner.py | 2 +- 87 files changed, 335 insertions(+), 335 deletions(-) diff --git a/src/primaite/cli.py b/src/primaite/cli.py index bc4aad39..a5b3be46 100644 --- a/src/primaite/cli.py +++ b/src/primaite/cli.py @@ -29,7 +29,7 @@ def reset_notebooks(overwrite: bool = True) -> None: :param overwrite: If True, will overwrite existing demo notebooks. """ - from src.primaite.setup import reset_demo_notebooks + from primaite.setup import reset_demo_notebooks reset_demo_notebooks.run(overwrite) @@ -98,7 +98,7 @@ def setup(overwrite_existing: bool = True) -> None: from arcd_gate.cli import setup as gate_setup from primaite import getLogger - from src.primaite.setup import reset_demo_notebooks, reset_example_configs + from primaite.setup import reset_demo_notebooks, reset_example_configs _LOGGER = getLogger(__name__) @@ -133,9 +133,9 @@ def session( """ from threading import Thread - from src.primaite.config.load import example_config_path - from src.primaite.main import run - from src.primaite.utils.start_gate_server import start_gate_server + from primaite.config.load import example_config_path + from primaite.main import run + from primaite.utils.start_gate_server import start_gate_server server_thread = Thread(target=start_gate_server) server_thread.start() diff --git a/src/primaite/game/agent/GATE_agents.py b/src/primaite/game/agent/GATE_agents.py index ad52edfc..e50d7831 100644 --- a/src/primaite/game/agent/GATE_agents.py +++ b/src/primaite/game/agent/GATE_agents.py @@ -3,10 +3,10 @@ from typing import Dict, Optional, Tuple from gymnasium.core import ActType, ObsType -from src.primaite.game.agent.actions import ActionManager -from src.primaite.game.agent.interface import AbstractGATEAgent, ObsType -from src.primaite.game.agent.observations import ObservationSpace -from src.primaite.game.agent.rewards import RewardFunction +from primaite.game.agent.actions import ActionManager +from primaite.game.agent.interface import AbstractGATEAgent, ObsType +from primaite.game.agent.observations import ObservationSpace +from primaite.game.agent.rewards import RewardFunction class GATERLAgent(AbstractGATEAgent): diff --git a/src/primaite/game/agent/actions.py b/src/primaite/game/agent/actions.py index 9daa09ee..b06013cd 100644 --- a/src/primaite/game/agent/actions.py +++ b/src/primaite/game/agent/actions.py @@ -15,12 +15,12 @@ from typing import Dict, List, Optional, Tuple, TYPE_CHECKING from gymnasium import spaces from primaite import getLogger -from src.primaite.simulator.sim_container import Simulation +from primaite.simulator.sim_container import Simulation _LOGGER = getLogger(__name__) if TYPE_CHECKING: - from src.primaite.game.session import PrimaiteSession + from primaite.game.session import PrimaiteSession class AbstractAction(ABC): diff --git a/src/primaite/game/agent/interface.py b/src/primaite/game/agent/interface.py index affed19c..89f27f3f 100644 --- a/src/primaite/game/agent/interface.py +++ b/src/primaite/game/agent/interface.py @@ -4,9 +4,9 @@ from typing import Dict, List, Optional, Tuple, TypeAlias, Union import numpy as np -from src.primaite.game.agent.actions import ActionManager -from src.primaite.game.agent.observations import ObservationSpace -from src.primaite.game.agent.rewards import RewardFunction +from primaite.game.agent.actions import ActionManager +from primaite.game.agent.observations import ObservationSpace +from primaite.game.agent.rewards import RewardFunction ObsType: TypeAlias = Union[Dict, np.ndarray] diff --git a/src/primaite/game/agent/observations.py b/src/primaite/game/agent/observations.py index fe43afb2..a3bafeea 100644 --- a/src/primaite/game/agent/observations.py +++ b/src/primaite/game/agent/observations.py @@ -5,12 +5,12 @@ from typing import Any, Dict, List, Optional, Tuple, TYPE_CHECKING from gymnasium import spaces from primaite import getLogger -from src.primaite.game.agent.utils import access_from_nested_dict, NOT_PRESENT_IN_STATE +from primaite.game.agent.utils import access_from_nested_dict, NOT_PRESENT_IN_STATE _LOGGER = getLogger(__name__) if TYPE_CHECKING: - from src.primaite.game.session import PrimaiteSession + from primaite.game.session import PrimaiteSession class AbstractObservation(ABC): diff --git a/src/primaite/game/agent/rewards.py b/src/primaite/game/agent/rewards.py index 72a39fee..6c408ff9 100644 --- a/src/primaite/game/agent/rewards.py +++ b/src/primaite/game/agent/rewards.py @@ -29,12 +29,12 @@ from abc import abstractmethod from typing import Dict, List, Tuple, TYPE_CHECKING from primaite import getLogger -from src.primaite.game.agent.utils import access_from_nested_dict, NOT_PRESENT_IN_STATE +from primaite.game.agent.utils import access_from_nested_dict, NOT_PRESENT_IN_STATE _LOGGER = getLogger(__name__) if TYPE_CHECKING: - from src.primaite.game.session import PrimaiteSession + from primaite.game.session import PrimaiteSession class AbstractReward: diff --git a/src/primaite/game/agent/scripted_agents.py b/src/primaite/game/agent/scripted_agents.py index aa1faefc..3748494b 100644 --- a/src/primaite/game/agent/scripted_agents.py +++ b/src/primaite/game/agent/scripted_agents.py @@ -1,5 +1,5 @@ """Agents with predefined behaviours.""" -from src.primaite.game.agent.interface import AbstractScriptedAgent +from primaite.game.agent.interface import AbstractScriptedAgent class GreenWebBrowsingAgent(AbstractScriptedAgent): diff --git a/src/primaite/game/session.py b/src/primaite/game/session.py index e791a0b9..d40d0754 100644 --- a/src/primaite/game/session.py +++ b/src/primaite/game/session.py @@ -9,27 +9,27 @@ from gymnasium.spaces.utils import flatten, flatten_space from pydantic import BaseModel from primaite import getLogger -from src.primaite.game.agent.actions import ActionManager -from src.primaite.game.agent.interface import AbstractAgent, RandomAgent -from src.primaite.game.agent.observations import ObservationSpace -from src.primaite.game.agent.rewards import RewardFunction -from src.primaite.simulator.network.hardware.base import Link, NIC, Node -from src.primaite.simulator.network.hardware.nodes.computer import Computer -from src.primaite.simulator.network.hardware.nodes.router import ACLAction, Router -from src.primaite.simulator.network.hardware.nodes.server import Server -from src.primaite.simulator.network.hardware.nodes.switch import Switch -from src.primaite.simulator.network.transmission.network_layer import IPProtocol -from src.primaite.simulator.network.transmission.transport_layer import Port -from src.primaite.simulator.sim_container import Simulation -from src.primaite.simulator.system.applications.application import Application -from src.primaite.simulator.system.applications.database_client import DatabaseClient -from src.primaite.simulator.system.applications.web_browser import WebBrowser -from src.primaite.simulator.system.services.database.database_service import DatabaseService -from src.primaite.simulator.system.services.dns.dns_client import DNSClient -from src.primaite.simulator.system.services.dns.dns_server import DNSServer -from src.primaite.simulator.system.services.red_services.data_manipulation_bot import DataManipulationBot -from src.primaite.simulator.system.services.service import Service -from src.primaite.simulator.system.services.web_server.web_server import WebServer +from primaite.game.agent.actions import ActionManager +from primaite.game.agent.interface import AbstractAgent, RandomAgent +from primaite.game.agent.observations import ObservationSpace +from primaite.game.agent.rewards import RewardFunction +from primaite.simulator.network.hardware.base import Link, NIC, Node +from primaite.simulator.network.hardware.nodes.computer import Computer +from primaite.simulator.network.hardware.nodes.router import ACLAction, Router +from primaite.simulator.network.hardware.nodes.server import Server +from primaite.simulator.network.hardware.nodes.switch import Switch +from primaite.simulator.network.transmission.network_layer import IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port +from primaite.simulator.sim_container import Simulation +from primaite.simulator.system.applications.application import Application +from primaite.simulator.system.applications.database_client import DatabaseClient +from primaite.simulator.system.applications.web_browser import WebBrowser +from primaite.simulator.system.services.database.database_service import DatabaseService +from primaite.simulator.system.services.dns.dns_client import DNSClient +from primaite.simulator.system.services.dns.dns_server import DNSServer +from primaite.simulator.system.services.red_services.data_manipulation_bot import DataManipulationBot +from primaite.simulator.system.services.service import Service +from primaite.simulator.system.services.web_server.web_server import WebServer _LOGGER = getLogger(__name__) diff --git a/src/primaite/main.py b/src/primaite/main.py index 9f84598c..831419d4 100644 --- a/src/primaite/main.py +++ b/src/primaite/main.py @@ -5,10 +5,10 @@ from pathlib import Path from typing import Optional, Union from primaite import getLogger -from src.primaite.config.load import load -from src.primaite.game.session import PrimaiteSession +from primaite.config.load import load +from primaite.game.session import PrimaiteSession -# from src.primaite.primaite_session import PrimaiteSession +# from primaite.primaite_session import PrimaiteSession _LOGGER = getLogger(__name__) diff --git a/src/primaite/simulator/domain/account.py b/src/primaite/simulator/domain/account.py index c7b06ff2..d235c00e 100644 --- a/src/primaite/simulator/domain/account.py +++ b/src/primaite/simulator/domain/account.py @@ -3,7 +3,7 @@ from enum import Enum from typing import Dict from primaite import getLogger -from src.primaite.simulator.core import SimComponent +from primaite.simulator.core import SimComponent _LOGGER = getLogger(__name__) diff --git a/src/primaite/simulator/domain/controller.py b/src/primaite/simulator/domain/controller.py index aac9010b..e9f3b26d 100644 --- a/src/primaite/simulator/domain/controller.py +++ b/src/primaite/simulator/domain/controller.py @@ -1,8 +1,8 @@ from enum import Enum from typing import Dict, Final, List, Literal, Tuple -from src.primaite.simulator.core import RequestManager, RequestPermissionValidator, RequestType, SimComponent -from src.primaite.simulator.domain.account import Account, AccountType +from primaite.simulator.core import RequestManager, RequestPermissionValidator, RequestType, SimComponent +from primaite.simulator.domain.account import Account, AccountType # placeholder while these objects don't yet exist diff --git a/src/primaite/simulator/file_system/file.py b/src/primaite/simulator/file_system/file.py index baa70667..d9b02e8e 100644 --- a/src/primaite/simulator/file_system/file.py +++ b/src/primaite/simulator/file_system/file.py @@ -7,8 +7,8 @@ from pathlib import Path from typing import Dict, Optional from primaite import getLogger -from src.primaite.simulator.file_system.file_system_item_abc import FileSystemItemABC, FileSystemItemHealthStatus -from src.primaite.simulator.file_system.file_type import FileType, get_file_type_from_extension +from primaite.simulator.file_system.file_system_item_abc import FileSystemItemABC, FileSystemItemHealthStatus +from primaite.simulator.file_system.file_type import FileType, get_file_type_from_extension _LOGGER = getLogger(__name__) diff --git a/src/primaite/simulator/file_system/file_system.py b/src/primaite/simulator/file_system/file_system.py index be7375b1..41f02270 100644 --- a/src/primaite/simulator/file_system/file_system.py +++ b/src/primaite/simulator/file_system/file_system.py @@ -7,11 +7,11 @@ from typing import Dict, Optional from prettytable import MARKDOWN, PrettyTable from primaite import getLogger -from src.primaite.simulator.core import RequestManager, RequestType, SimComponent -from src.primaite.simulator.file_system.file import File -from src.primaite.simulator.file_system.file_type import FileType -from src.primaite.simulator.file_system.folder import Folder -from src.primaite.simulator.system.core.sys_log import SysLog +from primaite.simulator.core import RequestManager, RequestType, SimComponent +from primaite.simulator.file_system.file import File +from primaite.simulator.file_system.file_type import FileType +from primaite.simulator.file_system.folder import Folder +from primaite.simulator.system.core.sys_log import SysLog _LOGGER = getLogger(__name__) diff --git a/src/primaite/simulator/file_system/file_system_item_abc.py b/src/primaite/simulator/file_system/file_system_item_abc.py index cad3aeaa..fbe5f4b3 100644 --- a/src/primaite/simulator/file_system/file_system_item_abc.py +++ b/src/primaite/simulator/file_system/file_system_item_abc.py @@ -6,8 +6,8 @@ from enum import Enum from typing import Dict, Optional from primaite import getLogger -from src.primaite.simulator.core import RequestManager, RequestType, SimComponent -from src.primaite.simulator.system.core.sys_log import SysLog +from primaite.simulator.core import RequestManager, RequestType, SimComponent +from primaite.simulator.system.core.sys_log import SysLog _LOGGER = getLogger(__name__) diff --git a/src/primaite/simulator/file_system/folder.py b/src/primaite/simulator/file_system/folder.py index 82af255b..f0d55ef8 100644 --- a/src/primaite/simulator/file_system/folder.py +++ b/src/primaite/simulator/file_system/folder.py @@ -5,9 +5,9 @@ from typing import Dict, Optional from prettytable import MARKDOWN, PrettyTable from primaite import getLogger -from src.primaite.simulator.core import RequestManager, RequestType -from src.primaite.simulator.file_system.file import File -from src.primaite.simulator.file_system.file_system_item_abc import FileSystemItemABC, FileSystemItemHealthStatus +from primaite.simulator.core import RequestManager, RequestType +from primaite.simulator.file_system.file import File +from primaite.simulator.file_system.file_system_item_abc import FileSystemItemABC, FileSystemItemHealthStatus _LOGGER = getLogger(__name__) diff --git a/src/primaite/simulator/network/container.py b/src/primaite/simulator/network/container.py index d50e4b95..9fbafc29 100644 --- a/src/primaite/simulator/network/container.py +++ b/src/primaite/simulator/network/container.py @@ -6,12 +6,12 @@ from networkx import MultiGraph from prettytable import MARKDOWN, PrettyTable from primaite import getLogger -from src.primaite.simulator.core import RequestManager, RequestType, SimComponent -from src.primaite.simulator.network.hardware.base import Link, NIC, Node, SwitchPort -from src.primaite.simulator.network.hardware.nodes.computer import Computer -from src.primaite.simulator.network.hardware.nodes.router import Router -from src.primaite.simulator.network.hardware.nodes.server import Server -from src.primaite.simulator.network.hardware.nodes.switch import Switch +from primaite.simulator.core import RequestManager, RequestType, SimComponent +from primaite.simulator.network.hardware.base import Link, NIC, Node, SwitchPort +from primaite.simulator.network.hardware.nodes.computer import Computer +from primaite.simulator.network.hardware.nodes.router import Router +from primaite.simulator.network.hardware.nodes.server import Server +from primaite.simulator.network.hardware.nodes.switch import Switch _LOGGER = getLogger(__name__) diff --git a/src/primaite/simulator/network/hardware/base.py b/src/primaite/simulator/network/hardware/base.py index 43ec2363..537cebb2 100644 --- a/src/primaite/simulator/network/hardware/base.py +++ b/src/primaite/simulator/network/hardware/base.py @@ -10,22 +10,22 @@ from typing import Any, Dict, Literal, Optional, Tuple, Union from prettytable import MARKDOWN, PrettyTable from primaite import getLogger -from src.primaite.exceptions import NetworkError -from src.primaite.simulator import SIM_OUTPUT -from src.primaite.simulator.core import RequestManager, RequestType, SimComponent -from src.primaite.simulator.domain.account import Account -from src.primaite.simulator.file_system.file_system import FileSystem -from src.primaite.simulator.network.protocols.arp import ARPEntry, ARPPacket -from src.primaite.simulator.network.transmission.data_link_layer import EthernetHeader, Frame -from src.primaite.simulator.network.transmission.network_layer import ICMPPacket, ICMPType, IPPacket, IPProtocol -from src.primaite.simulator.network.transmission.transport_layer import Port, TCPHeader -from src.primaite.simulator.system.applications.application import Application -from src.primaite.simulator.system.core.packet_capture import PacketCapture -from src.primaite.simulator.system.core.session_manager import SessionManager -from src.primaite.simulator.system.core.software_manager import SoftwareManager -from src.primaite.simulator.system.core.sys_log import SysLog -from src.primaite.simulator.system.processes.process import Process -from src.primaite.simulator.system.services.service import Service +from primaite.exceptions import NetworkError +from primaite.simulator import SIM_OUTPUT +from primaite.simulator.core import RequestManager, RequestType, SimComponent +from primaite.simulator.domain.account import Account +from primaite.simulator.file_system.file_system import FileSystem +from primaite.simulator.network.protocols.arp import ARPEntry, ARPPacket +from primaite.simulator.network.transmission.data_link_layer import EthernetHeader, Frame +from primaite.simulator.network.transmission.network_layer import ICMPPacket, ICMPType, IPPacket, IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port, TCPHeader +from primaite.simulator.system.applications.application import Application +from primaite.simulator.system.core.packet_capture import PacketCapture +from primaite.simulator.system.core.session_manager import SessionManager +from primaite.simulator.system.core.software_manager import SoftwareManager +from primaite.simulator.system.core.sys_log import SysLog +from primaite.simulator.system.processes.process import Process +from primaite.simulator.system.services.service import Service _LOGGER = getLogger(__name__) diff --git a/src/primaite/simulator/network/hardware/nodes/computer.py b/src/primaite/simulator/network/hardware/nodes/computer.py index f378ba04..0480aca9 100644 --- a/src/primaite/simulator/network/hardware/nodes/computer.py +++ b/src/primaite/simulator/network/hardware/nodes/computer.py @@ -1,7 +1,7 @@ -from src.primaite.simulator.network.hardware.base import NIC, Node -from src.primaite.simulator.system.applications.web_browser import WebBrowser -from src.primaite.simulator.system.services.dns.dns_client import DNSClient -from src.primaite.simulator.system.services.ftp.ftp_client import FTPClient +from primaite.simulator.network.hardware.base import NIC, Node +from primaite.simulator.system.applications.web_browser import WebBrowser +from primaite.simulator.system.services.dns.dns_client import DNSClient +from primaite.simulator.system.services.ftp.ftp_client import FTPClient class Computer(Node): diff --git a/src/primaite/simulator/network/hardware/nodes/router.py b/src/primaite/simulator/network/hardware/nodes/router.py index 4761291b..c2a38aba 100644 --- a/src/primaite/simulator/network/hardware/nodes/router.py +++ b/src/primaite/simulator/network/hardware/nodes/router.py @@ -7,12 +7,12 @@ from typing import Dict, List, Optional, Tuple, Union from prettytable import MARKDOWN, PrettyTable -from src.primaite.simulator.core import RequestManager, RequestType, SimComponent -from src.primaite.simulator.network.hardware.base import ARPCache, ICMP, NIC, Node -from src.primaite.simulator.network.transmission.data_link_layer import EthernetHeader, Frame -from src.primaite.simulator.network.transmission.network_layer import ICMPPacket, ICMPType, IPPacket, IPProtocol -from src.primaite.simulator.network.transmission.transport_layer import Port, TCPHeader -from src.primaite.simulator.system.core.sys_log import SysLog +from primaite.simulator.core import RequestManager, RequestType, SimComponent +from primaite.simulator.network.hardware.base import ARPCache, ICMP, NIC, Node +from primaite.simulator.network.transmission.data_link_layer import EthernetHeader, Frame +from primaite.simulator.network.transmission.network_layer import ICMPPacket, ICMPType, IPPacket, IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port, TCPHeader +from primaite.simulator.system.core.sys_log import SysLog class ACLAction(Enum): diff --git a/src/primaite/simulator/network/hardware/nodes/server.py b/src/primaite/simulator/network/hardware/nodes/server.py index 529692a3..b72cc71c 100644 --- a/src/primaite/simulator/network/hardware/nodes/server.py +++ b/src/primaite/simulator/network/hardware/nodes/server.py @@ -1,4 +1,4 @@ -from src.primaite.simulator.network.hardware.nodes.computer import Computer +from primaite.simulator.network.hardware.nodes.computer import Computer class Server(Computer): diff --git a/src/primaite/simulator/network/hardware/nodes/switch.py b/src/primaite/simulator/network/hardware/nodes/switch.py index fd4614ff..fe61509c 100644 --- a/src/primaite/simulator/network/hardware/nodes/switch.py +++ b/src/primaite/simulator/network/hardware/nodes/switch.py @@ -3,9 +3,9 @@ from typing import Dict from prettytable import MARKDOWN, PrettyTable from primaite import getLogger -from src.primaite.exceptions import NetworkError -from src.primaite.simulator.network.hardware.base import Link, Node, SwitchPort -from src.primaite.simulator.network.transmission.data_link_layer import Frame +from primaite.exceptions import NetworkError +from primaite.simulator.network.hardware.base import Link, Node, SwitchPort +from primaite.simulator.network.transmission.data_link_layer import Frame _LOGGER = getLogger(__name__) diff --git a/src/primaite/simulator/network/networks.py b/src/primaite/simulator/network/networks.py index d698f491..25d1bd21 100644 --- a/src/primaite/simulator/network/networks.py +++ b/src/primaite/simulator/network/networks.py @@ -1,19 +1,19 @@ from ipaddress import IPv4Address -from src.primaite.simulator.network.container import Network -from src.primaite.simulator.network.hardware.base import NIC, NodeOperatingState -from src.primaite.simulator.network.hardware.nodes.computer import Computer -from src.primaite.simulator.network.hardware.nodes.router import ACLAction, Router -from src.primaite.simulator.network.hardware.nodes.server import Server -from src.primaite.simulator.network.hardware.nodes.switch import Switch -from src.primaite.simulator.network.transmission.network_layer import IPProtocol -from src.primaite.simulator.network.transmission.transport_layer import Port -from src.primaite.simulator.system.applications.database_client import DatabaseClient -from src.primaite.simulator.system.services.database.database_service import DatabaseService -from src.primaite.simulator.system.services.dns.dns_server import DNSServer -from src.primaite.simulator.system.services.ftp.ftp_server import FTPServer -from src.primaite.simulator.system.services.red_services.data_manipulation_bot import DataManipulationBot -from src.primaite.simulator.system.services.web_server.web_server import WebServer +from primaite.simulator.network.container import Network +from primaite.simulator.network.hardware.base import NIC, NodeOperatingState +from primaite.simulator.network.hardware.nodes.computer import Computer +from primaite.simulator.network.hardware.nodes.router import ACLAction, Router +from primaite.simulator.network.hardware.nodes.server import Server +from primaite.simulator.network.hardware.nodes.switch import Switch +from primaite.simulator.network.transmission.network_layer import IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port +from primaite.simulator.system.applications.database_client import DatabaseClient +from primaite.simulator.system.services.database.database_service import DatabaseService +from primaite.simulator.system.services.dns.dns_server import DNSServer +from primaite.simulator.system.services.ftp.ftp_server import FTPServer +from primaite.simulator.system.services.red_services.data_manipulation_bot import DataManipulationBot +from primaite.simulator.system.services.web_server.web_server import WebServer def client_server_routed() -> Network: diff --git a/src/primaite/simulator/network/protocols/arp.py b/src/primaite/simulator/network/protocols/arp.py index bd5e90e4..7b3e4509 100644 --- a/src/primaite/simulator/network/protocols/arp.py +++ b/src/primaite/simulator/network/protocols/arp.py @@ -5,7 +5,7 @@ from typing import Optional from pydantic import BaseModel -from src.primaite.simulator.network.protocols.packet import DataPacket +from primaite.simulator.network.protocols.packet import DataPacket class ARPEntry(BaseModel): diff --git a/src/primaite/simulator/network/protocols/dns.py b/src/primaite/simulator/network/protocols/dns.py index 4fd9ac68..4f9be51b 100644 --- a/src/primaite/simulator/network/protocols/dns.py +++ b/src/primaite/simulator/network/protocols/dns.py @@ -5,7 +5,7 @@ from typing import Optional from pydantic import BaseModel -from src.primaite.simulator.network.protocols.packet import DataPacket +from primaite.simulator.network.protocols.packet import DataPacket class DNSRequest(BaseModel): diff --git a/src/primaite/simulator/network/protocols/ftp.py b/src/primaite/simulator/network/protocols/ftp.py index 46d2e3b0..9ecc7df8 100644 --- a/src/primaite/simulator/network/protocols/ftp.py +++ b/src/primaite/simulator/network/protocols/ftp.py @@ -1,7 +1,7 @@ from enum import Enum from typing import Any, Optional, Union -from src.primaite.simulator.network.protocols.packet import DataPacket +from primaite.simulator.network.protocols.packet import DataPacket class FTPCommand(Enum): diff --git a/src/primaite/simulator/network/protocols/http.py b/src/primaite/simulator/network/protocols/http.py index e29b4cbf..2dba2614 100644 --- a/src/primaite/simulator/network/protocols/http.py +++ b/src/primaite/simulator/network/protocols/http.py @@ -1,6 +1,6 @@ from enum import Enum -from src.primaite.simulator.network.protocols.packet import DataPacket +from primaite.simulator.network.protocols.packet import DataPacket class HttpRequestMethod(Enum): diff --git a/src/primaite/simulator/network/transmission/data_link_layer.py b/src/primaite/simulator/network/transmission/data_link_layer.py index 9fa940df..fa823a60 100644 --- a/src/primaite/simulator/network/transmission/data_link_layer.py +++ b/src/primaite/simulator/network/transmission/data_link_layer.py @@ -4,12 +4,12 @@ from typing import Any, Optional from pydantic import BaseModel from primaite import getLogger -from src.primaite.simulator.network.protocols.arp import ARPPacket -from src.primaite.simulator.network.protocols.packet import DataPacket -from src.primaite.simulator.network.transmission.network_layer import ICMPPacket, IPPacket, IPProtocol -from src.primaite.simulator.network.transmission.primaite_layer import PrimaiteHeader -from src.primaite.simulator.network.transmission.transport_layer import TCPHeader, UDPHeader -from src.primaite.simulator.network.utils import convert_bytes_to_megabits +from primaite.simulator.network.protocols.arp import ARPPacket +from primaite.simulator.network.protocols.packet import DataPacket +from primaite.simulator.network.transmission.network_layer import ICMPPacket, IPPacket, IPProtocol +from primaite.simulator.network.transmission.primaite_layer import PrimaiteHeader +from primaite.simulator.network.transmission.transport_layer import TCPHeader, UDPHeader +from primaite.simulator.network.utils import convert_bytes_to_megabits _LOGGER = getLogger(__name__) diff --git a/src/primaite/simulator/sim_container.py b/src/primaite/simulator/sim_container.py index af3449a8..8e820ec8 100644 --- a/src/primaite/simulator/sim_container.py +++ b/src/primaite/simulator/sim_container.py @@ -1,8 +1,8 @@ from typing import Dict -from src.primaite.simulator.core import RequestManager, RequestType, SimComponent -from src.primaite.simulator.domain.controller import DomainController -from src.primaite.simulator.network.container import Network +from primaite.simulator.core import RequestManager, RequestType, SimComponent +from primaite.simulator.domain.controller import DomainController +from primaite.simulator.network.container import Network class Simulation(SimComponent): diff --git a/src/primaite/simulator/system/applications/application.py b/src/primaite/simulator/system/applications/application.py index b1ecf680..db323cf6 100644 --- a/src/primaite/simulator/system/applications/application.py +++ b/src/primaite/simulator/system/applications/application.py @@ -2,7 +2,7 @@ from abc import abstractmethod from enum import Enum from typing import Any, Dict, Set -from src.primaite.simulator.system.software import IOSoftware, SoftwareHealthState +from primaite.simulator.system.software import IOSoftware, SoftwareHealthState class ApplicationOperatingState(Enum): diff --git a/src/primaite/simulator/system/applications/database_client.py b/src/primaite/simulator/system/applications/database_client.py index f8a9912a..d021cb78 100644 --- a/src/primaite/simulator/system/applications/database_client.py +++ b/src/primaite/simulator/system/applications/database_client.py @@ -4,10 +4,10 @@ from uuid import uuid4 from prettytable import PrettyTable -from src.primaite.simulator.network.transmission.network_layer import IPProtocol -from src.primaite.simulator.network.transmission.transport_layer import Port -from src.primaite.simulator.system.applications.application import Application, ApplicationOperatingState -from src.primaite.simulator.system.core.software_manager import SoftwareManager +from primaite.simulator.network.transmission.network_layer import IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port +from primaite.simulator.system.applications.application import Application, ApplicationOperatingState +from primaite.simulator.system.core.software_manager import SoftwareManager class DatabaseClient(Application): diff --git a/src/primaite/simulator/system/applications/web_browser.py b/src/primaite/simulator/system/applications/web_browser.py index eb65cea1..ea9c3ac3 100644 --- a/src/primaite/simulator/system/applications/web_browser.py +++ b/src/primaite/simulator/system/applications/web_browser.py @@ -2,11 +2,11 @@ from ipaddress import IPv4Address from typing import Dict, Optional from urllib.parse import urlparse -from src.primaite.simulator.network.protocols.http import HttpRequestMethod, HttpRequestPacket, HttpResponsePacket -from src.primaite.simulator.network.transmission.network_layer import IPProtocol -from src.primaite.simulator.network.transmission.transport_layer import Port -from src.primaite.simulator.system.applications.application import Application -from src.primaite.simulator.system.services.dns.dns_client import DNSClient +from primaite.simulator.network.protocols.http import HttpRequestMethod, HttpRequestPacket, HttpResponsePacket +from primaite.simulator.network.transmission.network_layer import IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port +from primaite.simulator.system.applications.application import Application +from primaite.simulator.system.services.dns.dns_client import DNSClient class WebBrowser(Application): diff --git a/src/primaite/simulator/system/core/packet_capture.py b/src/primaite/simulator/system/core/packet_capture.py index 4368a8d7..2e5ed008 100644 --- a/src/primaite/simulator/system/core/packet_capture.py +++ b/src/primaite/simulator/system/core/packet_capture.py @@ -3,7 +3,7 @@ import logging from pathlib import Path from typing import Any, Dict, List, Optional -from src.primaite.simulator import SIM_OUTPUT +from primaite.simulator import SIM_OUTPUT class _JSONFilter(logging.Filter): diff --git a/src/primaite/simulator/system/core/session_manager.py b/src/primaite/simulator/system/core/session_manager.py index 1f073041..360b5e73 100644 --- a/src/primaite/simulator/system/core/session_manager.py +++ b/src/primaite/simulator/system/core/session_manager.py @@ -5,15 +5,15 @@ from typing import Any, Dict, Optional, Tuple, TYPE_CHECKING, Union from prettytable import MARKDOWN, PrettyTable -from src.primaite.simulator.core import SimComponent -from src.primaite.simulator.network.transmission.data_link_layer import EthernetHeader, Frame -from src.primaite.simulator.network.transmission.network_layer import IPPacket, IPProtocol -from src.primaite.simulator.network.transmission.transport_layer import Port, TCPHeader +from primaite.simulator.core import SimComponent +from primaite.simulator.network.transmission.data_link_layer import EthernetHeader, Frame +from primaite.simulator.network.transmission.network_layer import IPPacket, IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port, TCPHeader if TYPE_CHECKING: - from src.primaite.simulator.network.hardware.base import ARPCache - from src.primaite.simulator.system.core.software_manager import SoftwareManager - from src.primaite.simulator.system.core.sys_log import SysLog + from primaite.simulator.network.hardware.base import ARPCache + from primaite.simulator.system.core.software_manager import SoftwareManager + from primaite.simulator.system.core.sys_log import SysLog class Session(SimComponent): diff --git a/src/primaite/simulator/system/core/software_manager.py b/src/primaite/simulator/system/core/software_manager.py index ef4eca6c..8b8fe599 100644 --- a/src/primaite/simulator/system/core/software_manager.py +++ b/src/primaite/simulator/system/core/software_manager.py @@ -3,18 +3,18 @@ from typing import Any, Dict, List, Optional, Tuple, TYPE_CHECKING, Union from prettytable import MARKDOWN, PrettyTable -from src.primaite.simulator.file_system.file_system import FileSystem -from src.primaite.simulator.network.transmission.network_layer import IPProtocol -from src.primaite.simulator.network.transmission.transport_layer import Port -from src.primaite.simulator.system.applications.application import Application, ApplicationOperatingState -from src.primaite.simulator.system.core.sys_log import SysLog -from src.primaite.simulator.system.services.service import Service, ServiceOperatingState -from src.primaite.simulator.system.software import IOSoftware +from primaite.simulator.file_system.file_system import FileSystem +from primaite.simulator.network.transmission.network_layer import IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port +from primaite.simulator.system.applications.application import Application, ApplicationOperatingState +from primaite.simulator.system.core.sys_log import SysLog +from primaite.simulator.system.services.service import Service, ServiceOperatingState +from primaite.simulator.system.software import IOSoftware if TYPE_CHECKING: - from src.primaite.simulator.system.core.session_manager import SessionManager - from src.primaite.simulator.system.core.sys_log import SysLog - from src.primaite.simulator.network.hardware.base import Node + from primaite.simulator.system.core.session_manager import SessionManager + from primaite.simulator.system.core.sys_log import SysLog + from primaite.simulator.network.hardware.base import Node from typing import Type, TypeVar diff --git a/src/primaite/simulator/system/core/sys_log.py b/src/primaite/simulator/system/core/sys_log.py index 3ea12758..791e0be8 100644 --- a/src/primaite/simulator/system/core/sys_log.py +++ b/src/primaite/simulator/system/core/sys_log.py @@ -3,7 +3,7 @@ from pathlib import Path from prettytable import MARKDOWN, PrettyTable -from src.primaite.simulator import SIM_OUTPUT +from primaite.simulator import SIM_OUTPUT class _NotJSONFilter(logging.Filter): diff --git a/src/primaite/simulator/system/processes/process.py b/src/primaite/simulator/system/processes/process.py index 7c4da425..c4e94845 100644 --- a/src/primaite/simulator/system/processes/process.py +++ b/src/primaite/simulator/system/processes/process.py @@ -2,7 +2,7 @@ from abc import abstractmethod from enum import Enum from typing import Dict -from src.primaite.simulator.system.software import Software +from primaite.simulator.system.software import Software class ProcessOperatingState(Enum): diff --git a/src/primaite/simulator/system/services/database/database_service.py b/src/primaite/simulator/system/services/database/database_service.py index 07eebb36..b04174bf 100644 --- a/src/primaite/simulator/system/services/database/database_service.py +++ b/src/primaite/simulator/system/services/database/database_service.py @@ -6,13 +6,13 @@ from typing import Any, Dict, List, Optional, Union from prettytable import MARKDOWN, PrettyTable -from src.primaite.simulator.file_system.file_system import File -from src.primaite.simulator.network.transmission.network_layer import IPProtocol -from src.primaite.simulator.network.transmission.transport_layer import Port -from src.primaite.simulator.system.core.software_manager import SoftwareManager -from src.primaite.simulator.system.services.ftp.ftp_client import FTPClient -from src.primaite.simulator.system.services.service import Service, ServiceOperatingState -from src.primaite.simulator.system.software import SoftwareHealthState +from primaite.simulator.file_system.file_system import File +from primaite.simulator.network.transmission.network_layer import IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port +from primaite.simulator.system.core.software_manager import SoftwareManager +from primaite.simulator.system.services.ftp.ftp_client import FTPClient +from primaite.simulator.system.services.service import Service, ServiceOperatingState +from primaite.simulator.system.software import SoftwareHealthState class DatabaseService(Service): diff --git a/src/primaite/simulator/system/services/dns/dns_client.py b/src/primaite/simulator/system/services/dns/dns_client.py index 0d09a0dd..266ac4f6 100644 --- a/src/primaite/simulator/system/services/dns/dns_client.py +++ b/src/primaite/simulator/system/services/dns/dns_client.py @@ -2,11 +2,11 @@ from ipaddress import IPv4Address from typing import Dict, Optional from primaite import getLogger -from src.primaite.simulator.network.protocols.dns import DNSPacket, DNSRequest -from src.primaite.simulator.network.transmission.network_layer import IPProtocol -from src.primaite.simulator.network.transmission.transport_layer import Port -from src.primaite.simulator.system.core.software_manager import SoftwareManager -from src.primaite.simulator.system.services.service import Service +from primaite.simulator.network.protocols.dns import DNSPacket, DNSRequest +from primaite.simulator.network.transmission.network_layer import IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port +from primaite.simulator.system.core.software_manager import SoftwareManager +from primaite.simulator.system.services.service import Service _LOGGER = getLogger(__name__) diff --git a/src/primaite/simulator/system/services/dns/dns_server.py b/src/primaite/simulator/system/services/dns/dns_server.py index b1548bb6..90a350c8 100644 --- a/src/primaite/simulator/system/services/dns/dns_server.py +++ b/src/primaite/simulator/system/services/dns/dns_server.py @@ -4,10 +4,10 @@ from typing import Any, Dict, Optional from prettytable import MARKDOWN, PrettyTable from primaite import getLogger -from src.primaite.simulator.network.protocols.dns import DNSPacket -from src.primaite.simulator.network.transmission.network_layer import IPProtocol -from src.primaite.simulator.network.transmission.transport_layer import Port -from src.primaite.simulator.system.services.service import Service +from primaite.simulator.network.protocols.dns import DNSPacket +from primaite.simulator.network.transmission.network_layer import IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port +from primaite.simulator.system.services.service import Service _LOGGER = getLogger(__name__) diff --git a/src/primaite/simulator/system/services/ftp/ftp_client.py b/src/primaite/simulator/system/services/ftp/ftp_client.py index 221f5f7a..3e286da1 100644 --- a/src/primaite/simulator/system/services/ftp/ftp_client.py +++ b/src/primaite/simulator/system/services/ftp/ftp_client.py @@ -1,13 +1,13 @@ from ipaddress import IPv4Address from typing import Optional -from src.primaite.simulator.file_system.file_system import File -from src.primaite.simulator.network.protocols.ftp import FTPCommand, FTPPacket, FTPStatusCode -from src.primaite.simulator.network.transmission.network_layer import IPProtocol -from src.primaite.simulator.network.transmission.transport_layer import Port -from src.primaite.simulator.system.core.software_manager import SoftwareManager -from src.primaite.simulator.system.services.ftp.ftp_service import FTPServiceABC -from src.primaite.simulator.system.services.service import ServiceOperatingState +from primaite.simulator.file_system.file_system import File +from primaite.simulator.network.protocols.ftp import FTPCommand, FTPPacket, FTPStatusCode +from primaite.simulator.network.transmission.network_layer import IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port +from primaite.simulator.system.core.software_manager import SoftwareManager +from primaite.simulator.system.services.ftp.ftp_service import FTPServiceABC +from primaite.simulator.system.services.service import ServiceOperatingState class FTPClient(FTPServiceABC): diff --git a/src/primaite/simulator/system/services/ftp/ftp_server.py b/src/primaite/simulator/system/services/ftp/ftp_server.py index 2f8603d2..23414601 100644 --- a/src/primaite/simulator/system/services/ftp/ftp_server.py +++ b/src/primaite/simulator/system/services/ftp/ftp_server.py @@ -1,11 +1,11 @@ from ipaddress import IPv4Address from typing import Any, Dict, Optional -from src.primaite.simulator.network.protocols.ftp import FTPCommand, FTPPacket, FTPStatusCode -from src.primaite.simulator.network.transmission.network_layer import IPProtocol -from src.primaite.simulator.network.transmission.transport_layer import Port -from src.primaite.simulator.system.services.ftp.ftp_service import FTPServiceABC -from src.primaite.simulator.system.services.service import ServiceOperatingState +from primaite.simulator.network.protocols.ftp import FTPCommand, FTPPacket, FTPStatusCode +from primaite.simulator.network.transmission.network_layer import IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port +from primaite.simulator.system.services.ftp.ftp_service import FTPServiceABC +from primaite.simulator.system.services.service import ServiceOperatingState class FTPServer(FTPServiceABC): diff --git a/src/primaite/simulator/system/services/ftp/ftp_service.py b/src/primaite/simulator/system/services/ftp/ftp_service.py index 393263a4..f2c01544 100644 --- a/src/primaite/simulator/system/services/ftp/ftp_service.py +++ b/src/primaite/simulator/system/services/ftp/ftp_service.py @@ -3,10 +3,10 @@ from abc import ABC from ipaddress import IPv4Address from typing import Optional -from src.primaite.simulator.file_system.file_system import File -from src.primaite.simulator.network.protocols.ftp import FTPCommand, FTPPacket, FTPStatusCode -from src.primaite.simulator.network.transmission.transport_layer import Port -from src.primaite.simulator.system.services.service import Service +from primaite.simulator.file_system.file_system import File +from primaite.simulator.network.protocols.ftp import FTPCommand, FTPPacket, FTPStatusCode +from primaite.simulator.network.transmission.transport_layer import Port +from primaite.simulator.system.services.service import Service class FTPServiceABC(Service, ABC): diff --git a/src/primaite/simulator/system/services/red_services/data_manipulation_bot.py b/src/primaite/simulator/system/services/red_services/data_manipulation_bot.py index 379a9dad..996e6790 100644 --- a/src/primaite/simulator/system/services/red_services/data_manipulation_bot.py +++ b/src/primaite/simulator/system/services/red_services/data_manipulation_bot.py @@ -1,7 +1,7 @@ from ipaddress import IPv4Address from typing import Optional -from src.primaite.simulator.system.applications.database_client import DatabaseClient +from primaite.simulator.system.applications.database_client import DatabaseClient class DataManipulationBot(DatabaseClient): diff --git a/src/primaite/simulator/system/services/service.py b/src/primaite/simulator/system/services/service.py index 35a22d94..e2b04c15 100644 --- a/src/primaite/simulator/system/services/service.py +++ b/src/primaite/simulator/system/services/service.py @@ -2,8 +2,8 @@ from enum import Enum from typing import Dict, Optional from primaite import getLogger -from src.primaite.simulator.core import RequestManager, RequestType -from src.primaite.simulator.system.software import IOSoftware, SoftwareHealthState +from primaite.simulator.core import RequestManager, RequestType +from primaite.simulator.system.software import IOSoftware, SoftwareHealthState _LOGGER = getLogger(__name__) 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 e696800c..5957e4cb 100644 --- a/src/primaite/simulator/system/services/web_server/web_server.py +++ b/src/primaite/simulator/system/services/web_server/web_server.py @@ -2,16 +2,16 @@ from ipaddress import IPv4Address from typing import Any, Dict, Optional from urllib.parse import urlparse -from src.primaite.simulator.network.protocols.http import ( +from primaite.simulator.network.protocols.http import ( HttpRequestMethod, HttpRequestPacket, HttpResponsePacket, HttpStatusCode, ) -from src.primaite.simulator.network.transmission.network_layer import IPProtocol -from src.primaite.simulator.network.transmission.transport_layer import Port -from src.primaite.simulator.system.applications.database_client import DatabaseClient -from src.primaite.simulator.system.services.service import Service +from primaite.simulator.network.transmission.network_layer import IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port +from primaite.simulator.system.applications.database_client import DatabaseClient +from primaite.simulator.system.services.service import Service class WebServer(Service): diff --git a/src/primaite/simulator/system/software.py b/src/primaite/simulator/system/software.py index 379b2335..f2627557 100644 --- a/src/primaite/simulator/system/software.py +++ b/src/primaite/simulator/system/software.py @@ -3,11 +3,11 @@ from enum import Enum from ipaddress import IPv4Address from typing import Any, Dict, Optional -from src.primaite.simulator.core import RequestManager, RequestType, SimComponent -from src.primaite.simulator.file_system.file_system import FileSystem, Folder -from src.primaite.simulator.network.transmission.transport_layer import Port -from src.primaite.simulator.system.core.session_manager import Session -from src.primaite.simulator.system.core.sys_log import SysLog +from primaite.simulator.core import RequestManager, RequestType, SimComponent +from primaite.simulator.file_system.file_system import FileSystem, Folder +from primaite.simulator.network.transmission.transport_layer import Port +from primaite.simulator.system.core.session_manager import Session +from primaite.simulator.system.core.sys_log import SysLog class SoftwareType(Enum): diff --git a/src/primaite/utils/package_data.py b/src/primaite/utils/package_data.py index b2e0f006..ac41e8bc 100644 --- a/src/primaite/utils/package_data.py +++ b/src/primaite/utils/package_data.py @@ -16,7 +16,7 @@ def get_file_path(path: str) -> Path: :Example: - >>> from src.primaite.utils.package_data import get_file_path + >>> from primaite.utils.package_data import get_file_path >>> main_env_config = get_file_path("config/_package_data/training_config_main.yaml") diff --git a/src/primaite/utils/session_output_writer.py b/src/primaite/utils/session_output_writer.py index c3ecfcb2..0eb18038 100644 --- a/src/primaite/utils/session_output_writer.py +++ b/src/primaite/utils/session_output_writer.py @@ -4,13 +4,13 @@ from logging import Logger from typing import Final, List, Tuple, TYPE_CHECKING, Union from primaite import getLogger -from src.primaite.transactions.transaction import Transaction +from primaite.transactions.transaction import Transaction if TYPE_CHECKING: from io import TextIOWrapper from pathlib import Path - from src.primaite.environment.primaite_env import Primaite + from primaite.environment.primaite_env import Primaite _LOGGER: Logger = getLogger(__name__) diff --git a/tests/conftest.py b/tests/conftest.py index 34b7191f..dc749cfc 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -12,14 +12,14 @@ import pytest from primaite import getLogger -# from src.primaite.environment.primaite_env import Primaite -# from src.primaite.primaite_session import PrimaiteSession -from src.primaite.simulator.network.container import Network -from src.primaite.simulator.network.networks import arcd_uc2_network -from src.primaite.simulator.network.transmission.transport_layer import Port -from src.primaite.simulator.system.applications.application import Application -from src.primaite.simulator.system.core.sys_log import SysLog -from src.primaite.simulator.system.services.service import Service +# from primaite.environment.primaite_env import Primaite +# from primaite.primaite_session import PrimaiteSession +from primaite.simulator.network.container import Network +from primaite.simulator.network.networks import arcd_uc2_network +from primaite.simulator.network.transmission.transport_layer import Port +from primaite.simulator.system.applications.application import Application +from primaite.simulator.system.core.sys_log import SysLog +from primaite.simulator.system.services.service import Service from tests.mock_and_patch.get_session_path_mock import get_temp_session_path ACTION_SPACE_NODE_VALUES = 1 @@ -28,8 +28,8 @@ ACTION_SPACE_NODE_ACTION_VALUES = 1 _LOGGER = getLogger(__name__) # PrimAITE v3 stuff -from src.primaite.simulator.file_system.file_system import FileSystem -from src.primaite.simulator.network.hardware.base import Node +from primaite.simulator.file_system.file_system import FileSystem +from primaite.simulator.network.hardware.base import Node class TestService(Service): @@ -121,8 +121,8 @@ def temp_primaite_session(request): .. code:: python - from src.primaite.config.lay_down_config import dos_very_basic_config_path - from src.primaite.config.training_config import main_training_config_path + from primaite.config.lay_down_config import dos_very_basic_config_path + from primaite.config.training_config import main_training_config_path @pytest.mark.parametrize( "temp_primaite_session", [ diff --git a/tests/e2e_integration_tests/test_uc2_data_manipulation_scenario.py b/tests/e2e_integration_tests/test_uc2_data_manipulation_scenario.py index 5fa652b2..13f4d1f3 100644 --- a/tests/e2e_integration_tests/test_uc2_data_manipulation_scenario.py +++ b/tests/e2e_integration_tests/test_uc2_data_manipulation_scenario.py @@ -1,8 +1,8 @@ -from src.primaite.simulator.network.hardware.nodes.computer import Computer -from src.primaite.simulator.network.hardware.nodes.server import Server -from src.primaite.simulator.system.applications.database_client import DatabaseClient -from src.primaite.simulator.system.services.database.database_service import DatabaseService -from src.primaite.simulator.system.services.red_services.data_manipulation_bot import DataManipulationBot +from primaite.simulator.network.hardware.nodes.computer import Computer +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.red_services.data_manipulation_bot import DataManipulationBot def test_data_manipulation(uc2_network): diff --git a/tests/integration_tests/component_creation/test_action_integration.py b/tests/integration_tests/component_creation/test_action_integration.py index 110a5254..a2be923b 100644 --- a/tests/integration_tests/component_creation/test_action_integration.py +++ b/tests/integration_tests/component_creation/test_action_integration.py @@ -1,11 +1,11 @@ import pytest -from src.primaite.simulator.core import RequestType -from src.primaite.simulator.network.hardware.nodes.computer import Computer -from src.primaite.simulator.network.hardware.nodes.server import Server -from src.primaite.simulator.network.hardware.nodes.switch import Switch -from src.primaite.simulator.sim_container import Simulation -from src.primaite.simulator.system.services.database.database_service import DatabaseService +from primaite.simulator.core import RequestType +from primaite.simulator.network.hardware.nodes.computer import Computer +from primaite.simulator.network.hardware.nodes.server import Server +from primaite.simulator.network.hardware.nodes.switch import Switch +from primaite.simulator.sim_container import Simulation +from primaite.simulator.system.services.database.database_service import DatabaseService def test_passing_actions_down(monkeypatch) -> None: diff --git a/tests/integration_tests/component_creation/test_permission_system.py b/tests/integration_tests/component_creation/test_permission_system.py index 8026a01f..bcadebb4 100644 --- a/tests/integration_tests/component_creation/test_permission_system.py +++ b/tests/integration_tests/component_creation/test_permission_system.py @@ -3,8 +3,8 @@ from typing import Dict, List, Literal import pytest -from src.primaite.simulator.core import AllowAllValidator, RequestManager, RequestType, SimComponent -from src.primaite.simulator.domain.controller import AccountGroup, GroupMembershipValidator +from primaite.simulator.core import AllowAllValidator, RequestManager, RequestType, SimComponent +from primaite.simulator.domain.controller import AccountGroup, GroupMembershipValidator @pytest.mark.skip(reason="Action validation is not currently a required feature.") diff --git a/tests/integration_tests/game_layer/test_observations.py b/tests/integration_tests/game_layer/test_observations.py index 1f1e9ac1..97154f62 100644 --- a/tests/integration_tests/game_layer/test_observations.py +++ b/tests/integration_tests/game_layer/test_observations.py @@ -1,8 +1,8 @@ from gymnasium import spaces -from src.primaite.game.agent.observations import FileObservation -from src.primaite.simulator.network.hardware.nodes.computer import Computer -from src.primaite.simulator.sim_container import Simulation +from primaite.game.agent.observations import FileObservation +from primaite.simulator.network.hardware.nodes.computer import Computer +from primaite.simulator.sim_container import Simulation def test_file_observation(): diff --git a/tests/integration_tests/network/test_frame_transmission.py b/tests/integration_tests/network/test_frame_transmission.py index 9b77dd89..7da9fe76 100644 --- a/tests/integration_tests/network/test_frame_transmission.py +++ b/tests/integration_tests/network/test_frame_transmission.py @@ -1,4 +1,4 @@ -from src.primaite.simulator.network.hardware.base import Link, NIC, Node, NodeOperatingState +from primaite.simulator.network.hardware.base import Link, NIC, Node, NodeOperatingState def test_node_to_node_ping(): diff --git a/tests/integration_tests/network/test_link_connection.py b/tests/integration_tests/network/test_link_connection.py index 7f95ae23..0ddf54df 100644 --- a/tests/integration_tests/network/test_link_connection.py +++ b/tests/integration_tests/network/test_link_connection.py @@ -1,4 +1,4 @@ -from src.primaite.simulator.network.hardware.base import Link, NIC, Node, NodeOperatingState +from primaite.simulator.network.hardware.base import Link, NIC, Node, NodeOperatingState def test_link_up(): diff --git a/tests/integration_tests/network/test_network_creation.py b/tests/integration_tests/network/test_network_creation.py index 25fa179c..91218068 100644 --- a/tests/integration_tests/network/test_network_creation.py +++ b/tests/integration_tests/network/test_network_creation.py @@ -1,7 +1,7 @@ import pytest -from src.primaite.simulator.network.container import Network -from src.primaite.simulator.network.hardware.base import NIC, Node +from primaite.simulator.network.container import Network +from primaite.simulator.network.hardware.base import NIC, Node def test_adding_removing_nodes(): diff --git a/tests/integration_tests/network/test_nic_link_connection.py b/tests/integration_tests/network/test_nic_link_connection.py index d6c56acc..228099c6 100644 --- a/tests/integration_tests/network/test_nic_link_connection.py +++ b/tests/integration_tests/network/test_nic_link_connection.py @@ -1,6 +1,6 @@ import pytest -from src.primaite.simulator.network.hardware.base import Link, NIC +from primaite.simulator.network.hardware.base import Link, NIC def test_link_fails_with_same_nic(): diff --git a/tests/integration_tests/network/test_routing.py b/tests/integration_tests/network/test_routing.py index 506c895f..6053c457 100644 --- a/tests/integration_tests/network/test_routing.py +++ b/tests/integration_tests/network/test_routing.py @@ -2,10 +2,10 @@ from typing import Tuple import pytest -from src.primaite.simulator.network.hardware.base import Link, NIC, Node, NodeOperatingState -from src.primaite.simulator.network.hardware.nodes.router import ACLAction, Router -from src.primaite.simulator.network.transmission.network_layer import IPProtocol -from src.primaite.simulator.network.transmission.transport_layer import Port +from primaite.simulator.network.hardware.base import Link, NIC, Node, NodeOperatingState +from primaite.simulator.network.hardware.nodes.router import ACLAction, Router +from primaite.simulator.network.transmission.network_layer import IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port @pytest.fixture(scope="function") diff --git a/tests/integration_tests/network/test_switched_network.py b/tests/integration_tests/network/test_switched_network.py index 44333e52..5b305702 100644 --- a/tests/integration_tests/network/test_switched_network.py +++ b/tests/integration_tests/network/test_switched_network.py @@ -1,7 +1,7 @@ -from src.primaite.simulator.network.hardware.base import Link, NodeOperatingState -from src.primaite.simulator.network.hardware.nodes.computer import Computer -from src.primaite.simulator.network.hardware.nodes.server import Server -from src.primaite.simulator.network.hardware.nodes.switch import Switch +from primaite.simulator.network.hardware.base import Link, NodeOperatingState +from primaite.simulator.network.hardware.nodes.computer import Computer +from primaite.simulator.network.hardware.nodes.server import Server +from primaite.simulator.network.hardware.nodes.switch import Switch def test_switched_network(): diff --git a/tests/integration_tests/system/test_database_on_node.py b/tests/integration_tests/system/test_database_on_node.py index 0bf95e51..92056981 100644 --- a/tests/integration_tests/system/test_database_on_node.py +++ b/tests/integration_tests/system/test_database_on_node.py @@ -1,9 +1,9 @@ from ipaddress import IPv4Address -from src.primaite.simulator.network.hardware.nodes.server import Server -from src.primaite.simulator.system.applications.database_client import DatabaseClient -from src.primaite.simulator.system.services.database.database_service import DatabaseService -from src.primaite.simulator.system.services.ftp.ftp_server import FTPServer +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 def test_database_client_server_connection(uc2_network): diff --git a/tests/integration_tests/system/test_dns_client_server.py b/tests/integration_tests/system/test_dns_client_server.py index dd317ef1..e82d97a4 100644 --- a/tests/integration_tests/system/test_dns_client_server.py +++ b/tests/integration_tests/system/test_dns_client_server.py @@ -1,8 +1,8 @@ -from src.primaite.simulator.network.hardware.nodes.computer import Computer -from src.primaite.simulator.network.hardware.nodes.server import Server -from src.primaite.simulator.system.services.dns.dns_client import DNSClient -from src.primaite.simulator.system.services.dns.dns_server import DNSServer -from src.primaite.simulator.system.services.service import ServiceOperatingState +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 +from primaite.simulator.system.services.dns.dns_server import DNSServer +from primaite.simulator.system.services.service import ServiceOperatingState def test_dns_client_server(uc2_network): diff --git a/tests/integration_tests/system/test_ftp_client_server.py b/tests/integration_tests/system/test_ftp_client_server.py index 1ea86bd4..48dc2960 100644 --- a/tests/integration_tests/system/test_ftp_client_server.py +++ b/tests/integration_tests/system/test_ftp_client_server.py @@ -1,10 +1,10 @@ from ipaddress import IPv4Address -from src.primaite.simulator.network.hardware.nodes.computer import Computer -from src.primaite.simulator.network.hardware.nodes.server import Server -from src.primaite.simulator.system.services.ftp.ftp_client import FTPClient -from src.primaite.simulator.system.services.ftp.ftp_server import FTPServer -from src.primaite.simulator.system.services.service import ServiceOperatingState +from primaite.simulator.network.hardware.nodes.computer import Computer +from primaite.simulator.network.hardware.nodes.server import Server +from primaite.simulator.system.services.ftp.ftp_client import FTPClient +from primaite.simulator.system.services.ftp.ftp_server import FTPServer +from primaite.simulator.system.services.service import ServiceOperatingState def test_ftp_client_store_file_in_server(uc2_network): diff --git a/tests/integration_tests/system/test_web_client_server.py b/tests/integration_tests/system/test_web_client_server.py index d4f4a187..f4546cbf 100644 --- a/tests/integration_tests/system/test_web_client_server.py +++ b/tests/integration_tests/system/test_web_client_server.py @@ -1,9 +1,9 @@ -from src.primaite.simulator.network.hardware.nodes.computer import Computer -from src.primaite.simulator.network.hardware.nodes.server import Server -from src.primaite.simulator.network.protocols.http import HttpStatusCode -from src.primaite.simulator.system.applications.application import ApplicationOperatingState -from src.primaite.simulator.system.applications.web_browser import WebBrowser -from src.primaite.simulator.system.services.service import ServiceOperatingState +from primaite.simulator.network.hardware.nodes.computer import Computer +from primaite.simulator.network.hardware.nodes.server import Server +from primaite.simulator.network.protocols.http import HttpStatusCode +from primaite.simulator.system.applications.application import ApplicationOperatingState +from primaite.simulator.system.applications.web_browser import WebBrowser +from primaite.simulator.system.services.service import ServiceOperatingState def test_web_page_home_page(uc2_network): diff --git a/tests/unit_tests/_primaite/_simulator/_domain/test_account.py b/tests/unit_tests/_primaite/_simulator/_domain/test_account.py index 92f3cfd9..96c34996 100644 --- a/tests/unit_tests/_primaite/_simulator/_domain/test_account.py +++ b/tests/unit_tests/_primaite/_simulator/_domain/test_account.py @@ -1,5 +1,5 @@ """Test the account module of the simulator.""" -from src.primaite.simulator.domain.account import Account, AccountType +from primaite.simulator.domain.account import Account, AccountType def test_account_serialise(): diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_file.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_file.py index ef4b1456..32efe029 100644 --- a/tests/unit_tests/_primaite/_simulator/_file_system/test_file.py +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_file.py @@ -1,6 +1,6 @@ -from src.primaite.simulator.file_system.file import File -from src.primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus -from src.primaite.simulator.file_system.file_type import FileType +from primaite.simulator.file_system.file import File +from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus +from primaite.simulator.file_system.file_type import FileType def test_create_file_no_extension(file_system): diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_file_actions.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_actions.py index a46fd4f9..aa8faa90 100644 --- a/tests/unit_tests/_primaite/_simulator/_file_system/test_file_actions.py +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_actions.py @@ -2,10 +2,10 @@ from typing import Tuple import pytest -from src.primaite.simulator.file_system.file import File -from src.primaite.simulator.file_system.file_system import FileSystem -from src.primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus -from src.primaite.simulator.file_system.folder import Folder +from primaite.simulator.file_system.file import File +from primaite.simulator.file_system.file_system import FileSystem +from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus +from primaite.simulator.file_system.folder import Folder @pytest.fixture(scope="function") diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system.py index 4d712436..4defc80c 100644 --- a/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system.py +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system.py @@ -1,7 +1,7 @@ import pytest -from src.primaite.simulator.file_system.file_system import FileSystem -from src.primaite.simulator.file_system.file_type import FileType +from primaite.simulator.file_system.file_system import FileSystem +from primaite.simulator.file_system.file_type import FileType def test_create_folder_and_file(file_system): diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system_actions.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system_actions.py index 0070c218..1c8513f9 100644 --- a/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system_actions.py +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_system_actions.py @@ -2,9 +2,9 @@ from typing import Tuple import pytest -from src.primaite.simulator.file_system.file import File -from src.primaite.simulator.file_system.file_system import FileSystem -from src.primaite.simulator.file_system.folder import Folder +from primaite.simulator.file_system.file import File +from primaite.simulator.file_system.file_system import FileSystem +from primaite.simulator.file_system.folder import Folder @pytest.fixture(scope="function") diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_folder.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_folder.py index 5b586e03..bada2dab 100644 --- a/tests/unit_tests/_primaite/_simulator/_file_system/test_folder.py +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_folder.py @@ -1,8 +1,8 @@ import pytest -from src.primaite.simulator.file_system.file import File -from src.primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus -from src.primaite.simulator.file_system.folder import Folder +from primaite.simulator.file_system.file import File +from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus +from primaite.simulator.file_system.folder import Folder @pytest.mark.skip(reason="Implementation for quarantine not needed yet") diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_folder_actions.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_folder_actions.py index e058d280..efa74e1f 100644 --- a/tests/unit_tests/_primaite/_simulator/_file_system/test_folder_actions.py +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_folder_actions.py @@ -2,10 +2,10 @@ from typing import Tuple import pytest -from src.primaite.simulator.file_system.file import File -from src.primaite.simulator.file_system.file_system import FileSystem -from src.primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus -from src.primaite.simulator.file_system.folder import Folder +from primaite.simulator.file_system.file import File +from primaite.simulator.file_system.file_system import FileSystem +from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus +from primaite.simulator.file_system.folder import Folder @pytest.fixture(scope="function") diff --git a/tests/unit_tests/_primaite/_simulator/_network/_hardware/nodes/test_acl.py b/tests/unit_tests/_primaite/_simulator/_network/_hardware/nodes/test_acl.py index ac1aee2f..554cba38 100644 --- a/tests/unit_tests/_primaite/_simulator/_network/_hardware/nodes/test_acl.py +++ b/tests/unit_tests/_primaite/_simulator/_network/_hardware/nodes/test_acl.py @@ -1,8 +1,8 @@ from ipaddress import IPv4Address -from src.primaite.simulator.network.hardware.nodes.router import ACLAction, Router -from src.primaite.simulator.network.transmission.network_layer import IPProtocol -from src.primaite.simulator.network.transmission.transport_layer import Port +from primaite.simulator.network.hardware.nodes.router import ACLAction, Router +from primaite.simulator.network.transmission.network_layer import IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port def test_add_rule(): diff --git a/tests/unit_tests/_primaite/_simulator/_network/_hardware/test_nic.py b/tests/unit_tests/_primaite/_simulator/_network/_hardware/test_nic.py index ea2dca0b..1bf2cdbb 100644 --- a/tests/unit_tests/_primaite/_simulator/_network/_hardware/test_nic.py +++ b/tests/unit_tests/_primaite/_simulator/_network/_hardware/test_nic.py @@ -3,7 +3,7 @@ from ipaddress import IPv4Address import pytest -from src.primaite.simulator.network.hardware.base import generate_mac_address, NIC +from primaite.simulator.network.hardware.base import generate_mac_address, NIC def test_mac_address_generation(): diff --git a/tests/unit_tests/_primaite/_simulator/_network/_hardware/test_node.py b/tests/unit_tests/_primaite/_simulator/_network/_hardware/test_node.py index ef2088b1..0e5fb4c7 100644 --- a/tests/unit_tests/_primaite/_simulator/_network/_hardware/test_node.py +++ b/tests/unit_tests/_primaite/_simulator/_network/_hardware/test_node.py @@ -3,7 +3,7 @@ from ipaddress import IPv4Address import pytest -from src.primaite.simulator.network.hardware.base import Node +from primaite.simulator.network.hardware.base import Node def test_node_creation(): diff --git a/tests/unit_tests/_primaite/_simulator/_network/_hardware/test_node_actions.py b/tests/unit_tests/_primaite/_simulator/_network/_hardware/test_node_actions.py index 7f4b3f49..5fe5df16 100644 --- a/tests/unit_tests/_primaite/_simulator/_network/_hardware/test_node_actions.py +++ b/tests/unit_tests/_primaite/_simulator/_network/_hardware/test_node_actions.py @@ -1,10 +1,10 @@ import pytest -from src.primaite.simulator.file_system.file import File -from src.primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus -from src.primaite.simulator.file_system.folder import Folder -from src.primaite.simulator.network.hardware.base import Node, NodeOperatingState -from src.primaite.simulator.system.software import SoftwareHealthState +from primaite.simulator.file_system.file import File +from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus +from primaite.simulator.file_system.folder import Folder +from primaite.simulator.network.hardware.base import Node, NodeOperatingState +from primaite.simulator.system.software import SoftwareHealthState @pytest.fixture diff --git a/tests/unit_tests/_primaite/_simulator/_network/_transmission/test_data_link_layer.py b/tests/unit_tests/_primaite/_simulator/_network/_transmission/test_data_link_layer.py index 35603fd0..f9b89de5 100644 --- a/tests/unit_tests/_primaite/_simulator/_network/_transmission/test_data_link_layer.py +++ b/tests/unit_tests/_primaite/_simulator/_network/_transmission/test_data_link_layer.py @@ -1,9 +1,9 @@ import pytest -from src.primaite.simulator.network.transmission.data_link_layer import EthernetHeader, Frame -from src.primaite.simulator.network.transmission.network_layer import ICMPPacket, IPPacket, IPProtocol, Precedence -from src.primaite.simulator.network.transmission.primaite_layer import AgentSource, DataStatus -from src.primaite.simulator.network.transmission.transport_layer import Port, TCPFlags, TCPHeader, UDPHeader +from primaite.simulator.network.transmission.data_link_layer import EthernetHeader, Frame +from primaite.simulator.network.transmission.network_layer import ICMPPacket, IPPacket, IPProtocol, Precedence +from primaite.simulator.network.transmission.primaite_layer import AgentSource, DataStatus +from primaite.simulator.network.transmission.transport_layer import Port, TCPFlags, TCPHeader, UDPHeader def test_frame_minimal_instantiation(): diff --git a/tests/unit_tests/_primaite/_simulator/_network/_transmission/test_network_layer.py b/tests/unit_tests/_primaite/_simulator/_network/_transmission/test_network_layer.py index 64a88ea5..a7189452 100644 --- a/tests/unit_tests/_primaite/_simulator/_network/_transmission/test_network_layer.py +++ b/tests/unit_tests/_primaite/_simulator/_network/_transmission/test_network_layer.py @@ -1,6 +1,6 @@ import pytest -from src.primaite.simulator.network.transmission.network_layer import ICMPPacket, ICMPType +from primaite.simulator.network.transmission.network_layer import ICMPPacket, ICMPType def test_icmp_minimal_header_creation(): diff --git a/tests/unit_tests/_primaite/_simulator/_network/test_container.py b/tests/unit_tests/_primaite/_simulator/_network/test_container.py index 65dd884f..66bd59a9 100644 --- a/tests/unit_tests/_primaite/_simulator/_network/test_container.py +++ b/tests/unit_tests/_primaite/_simulator/_network/test_container.py @@ -2,7 +2,7 @@ import json import pytest -from src.primaite.simulator.network.container import Network +from primaite.simulator.network.container import Network def test_creating_container(): diff --git a/tests/unit_tests/_primaite/_simulator/_system/_applications/test_web_browser.py b/tests/unit_tests/_primaite/_simulator/_system/_applications/test_web_browser.py index 2f78b476..b2724369 100644 --- a/tests/unit_tests/_primaite/_simulator/_system/_applications/test_web_browser.py +++ b/tests/unit_tests/_primaite/_simulator/_system/_applications/test_web_browser.py @@ -1,10 +1,10 @@ import pytest -from src.primaite.simulator.network.hardware.nodes.computer import Computer -from src.primaite.simulator.network.protocols.http import HttpResponsePacket, HttpStatusCode -from src.primaite.simulator.network.transmission.network_layer import IPProtocol -from src.primaite.simulator.network.transmission.transport_layer import Port -from src.primaite.simulator.system.applications.web_browser import WebBrowser +from primaite.simulator.network.hardware.nodes.computer import Computer +from primaite.simulator.network.protocols.http import HttpResponsePacket, HttpStatusCode +from primaite.simulator.network.transmission.network_layer import IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port +from primaite.simulator.system.applications.web_browser import WebBrowser @pytest.fixture(scope="function") diff --git a/tests/unit_tests/_primaite/_simulator/_system/_services/_red_services/test_data_manipulation_bot.py b/tests/unit_tests/_primaite/_simulator/_system/_services/_red_services/test_data_manipulation_bot.py index 0eca1794..dd785cc1 100644 --- a/tests/unit_tests/_primaite/_simulator/_system/_services/_red_services/test_data_manipulation_bot.py +++ b/tests/unit_tests/_primaite/_simulator/_system/_services/_red_services/test_data_manipulation_bot.py @@ -1,10 +1,10 @@ from ipaddress import IPv4Address -from src.primaite.simulator.network.hardware.base import Node -from src.primaite.simulator.network.networks import arcd_uc2_network -from src.primaite.simulator.network.transmission.network_layer import IPProtocol -from src.primaite.simulator.network.transmission.transport_layer import Port -from src.primaite.simulator.system.services.red_services.data_manipulation_bot import DataManipulationBot +from primaite.simulator.network.hardware.base import Node +from primaite.simulator.network.networks import arcd_uc2_network +from primaite.simulator.network.transmission.network_layer import IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port +from primaite.simulator.system.services.red_services.data_manipulation_bot import DataManipulationBot def test_creation(): diff --git a/tests/unit_tests/_primaite/_simulator/_system/_services/test_database.py b/tests/unit_tests/_primaite/_simulator/_system/_services/test_database.py index 2beef740..7662fbff 100644 --- a/tests/unit_tests/_primaite/_simulator/_system/_services/test_database.py +++ b/tests/unit_tests/_primaite/_simulator/_system/_services/test_database.py @@ -1,7 +1,7 @@ import pytest -from src.primaite.simulator.network.hardware.base import Node -from src.primaite.simulator.system.services.database.database_service import DatabaseService +from primaite.simulator.network.hardware.base import Node +from primaite.simulator.system.services.database.database_service import DatabaseService @pytest.fixture(scope="function") 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 20306a1a..dc6df5d4 100644 --- a/tests/unit_tests/_primaite/_simulator/_system/_services/test_dns.py +++ b/tests/unit_tests/_primaite/_simulator/_system/_services/test_dns.py @@ -2,14 +2,14 @@ from ipaddress import IPv4Address import pytest -from src.primaite.simulator.network.hardware.base import Node -from src.primaite.simulator.network.hardware.nodes.computer import Computer -from src.primaite.simulator.network.hardware.nodes.server import Server -from src.primaite.simulator.network.protocols.dns import DNSPacket, DNSReply, DNSRequest -from src.primaite.simulator.network.transmission.network_layer import IPProtocol -from src.primaite.simulator.network.transmission.transport_layer import Port -from src.primaite.simulator.system.services.dns.dns_client import DNSClient -from src.primaite.simulator.system.services.dns.dns_server import DNSServer +from primaite.simulator.network.hardware.base import Node +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 +from primaite.simulator.network.transmission.network_layer import IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port +from primaite.simulator.system.services.dns.dns_client import DNSClient +from primaite.simulator.system.services.dns.dns_server import DNSServer @pytest.fixture(scope="function") diff --git a/tests/unit_tests/_primaite/_simulator/_system/_services/test_ftp.py b/tests/unit_tests/_primaite/_simulator/_system/_services/test_ftp.py index d2e4f321..d382b8dd 100644 --- a/tests/unit_tests/_primaite/_simulator/_system/_services/test_ftp.py +++ b/tests/unit_tests/_primaite/_simulator/_system/_services/test_ftp.py @@ -2,14 +2,14 @@ from ipaddress import IPv4Address import pytest -from src.primaite.simulator.network.hardware.base import Node -from src.primaite.simulator.network.hardware.nodes.computer import Computer -from src.primaite.simulator.network.hardware.nodes.server import Server -from src.primaite.simulator.network.protocols.ftp import FTPCommand, FTPPacket, FTPStatusCode -from src.primaite.simulator.network.transmission.network_layer import IPProtocol -from src.primaite.simulator.network.transmission.transport_layer import Port -from src.primaite.simulator.system.services.ftp.ftp_client import FTPClient -from src.primaite.simulator.system.services.ftp.ftp_server import FTPServer +from primaite.simulator.network.hardware.base import Node +from primaite.simulator.network.hardware.nodes.computer import Computer +from primaite.simulator.network.hardware.nodes.server import Server +from primaite.simulator.network.protocols.ftp import FTPCommand, FTPPacket, FTPStatusCode +from primaite.simulator.network.transmission.network_layer import IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port +from primaite.simulator.system.services.ftp.ftp_client import FTPClient +from primaite.simulator.system.services.ftp.ftp_server import FTPServer @pytest.fixture(scope="function") diff --git a/tests/unit_tests/_primaite/_simulator/_system/_services/test_service_actions.py b/tests/unit_tests/_primaite/_simulator/_system/_services/test_service_actions.py index 9f64c8d6..6b2ee0a7 100644 --- a/tests/unit_tests/_primaite/_simulator/_system/_services/test_service_actions.py +++ b/tests/unit_tests/_primaite/_simulator/_system/_services/test_service_actions.py @@ -1,5 +1,5 @@ -from src.primaite.simulator.system.services.service import ServiceOperatingState -from src.primaite.simulator.system.software import SoftwareHealthState +from primaite.simulator.system.services.service import ServiceOperatingState +from primaite.simulator.system.software import SoftwareHealthState def test_service_scan(service): diff --git a/tests/unit_tests/_primaite/_simulator/_system/_services/test_services.py b/tests/unit_tests/_primaite/_simulator/_system/_services/test_services.py index ba27e6cc..b32463a2 100644 --- a/tests/unit_tests/_primaite/_simulator/_system/_services/test_services.py +++ b/tests/unit_tests/_primaite/_simulator/_system/_services/test_services.py @@ -1,5 +1,5 @@ -from src.primaite.simulator.system.services.service import ServiceOperatingState -from src.primaite.simulator.system.software import SoftwareHealthState +from primaite.simulator.system.services.service import ServiceOperatingState +from primaite.simulator.system.software import SoftwareHealthState def test_scan(service): diff --git a/tests/unit_tests/_primaite/_simulator/_system/_services/test_web_server.py b/tests/unit_tests/_primaite/_simulator/_system/_services/test_web_server.py index 4a5c488a..e6f0b9d9 100644 --- a/tests/unit_tests/_primaite/_simulator/_system/_services/test_web_server.py +++ b/tests/unit_tests/_primaite/_simulator/_system/_services/test_web_server.py @@ -1,15 +1,15 @@ import pytest -from src.primaite.simulator.network.hardware.nodes.server import Server -from src.primaite.simulator.network.protocols.http import ( +from primaite.simulator.network.hardware.nodes.server import Server +from primaite.simulator.network.protocols.http import ( HttpRequestMethod, HttpRequestPacket, HttpResponsePacket, HttpStatusCode, ) -from src.primaite.simulator.network.transmission.network_layer import IPProtocol -from src.primaite.simulator.network.transmission.transport_layer import Port -from src.primaite.simulator.system.services.web_server.web_server import WebServer +from primaite.simulator.network.transmission.network_layer import IPProtocol +from primaite.simulator.network.transmission.transport_layer import Port +from primaite.simulator.system.services.web_server.web_server import WebServer @pytest.fixture(scope="function") diff --git a/tests/unit_tests/_primaite/_simulator/test_core.py b/tests/unit_tests/_primaite/_simulator/test_core.py index 2e801816..069e6ea2 100644 --- a/tests/unit_tests/_primaite/_simulator/test_core.py +++ b/tests/unit_tests/_primaite/_simulator/test_core.py @@ -3,7 +3,7 @@ from typing import Callable, Dict, List, Literal, Tuple import pytest from pydantic import ValidationError -from src.primaite.simulator.core import SimComponent +from primaite.simulator.core import SimComponent class TestIsolatedSimComponent: diff --git a/tests/unit_tests/_primaite/_simulator/test_sim_conatiner.py b/tests/unit_tests/_primaite/_simulator/test_sim_conatiner.py index 98a204ca..4543259d 100644 --- a/tests/unit_tests/_primaite/_simulator/test_sim_conatiner.py +++ b/tests/unit_tests/_primaite/_simulator/test_sim_conatiner.py @@ -1,4 +1,4 @@ -from src.primaite.simulator.sim_container import Simulation +from primaite.simulator.sim_container import Simulation def test_creating_empty_simulation(): From 6e1f9bd63d65039b780fd997940a51fec1153ef9 Mon Sep 17 00:00:00 2001 From: "Czar.Echavez" Date: Wed, 8 Nov 2023 13:00:07 +0000 Subject: [PATCH 9/9] #1962: add gate installation for docs build pipeline --- .azure/azure-build-deploy-docs-pipeline.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.azure/azure-build-deploy-docs-pipeline.yml b/.azure/azure-build-deploy-docs-pipeline.yml index 0f44b0c8..f60840a7 100644 --- a/.azure/azure-build-deploy-docs-pipeline.yml +++ b/.azure/azure-build-deploy-docs-pipeline.yml @@ -27,7 +27,18 @@ jobs: - script: | pip install -e .[dev] - displayName: 'Install Yawning-Titan for docs autosummary' + displayName: 'Install PrimAITE for docs autosummary' + + - script: | + GATE_WHEEL=$(ls ./GATE/arcd_gate*.whl) + python -m pip install $GATE_WHEEL[dev] + displayName: 'Install GATE' + condition: or(eq( variables['Agent.OS'], 'Linux' ), eq( variables['Agent.OS'], 'Darwin' )) + + - script: | + forfiles /p GATE\ /m *.whl /c "cmd /c python -m pip install @file[dev]" + displayName: 'Install GATE' + condition: eq( variables['Agent.OS'], 'Windows_NT' ) - script: | primaite setup