Add ability to uninstall service
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
from random import choice
|
from random import choice
|
||||||
from typing import Dict, Optional
|
from typing import Dict, Optional, Union
|
||||||
|
|
||||||
from primaite import getLogger
|
from primaite import getLogger
|
||||||
from primaite.simulator.core import SimComponent
|
from primaite.simulator.core import SimComponent
|
||||||
@@ -211,7 +211,7 @@ class FileSystem(SimComponent):
|
|||||||
if file is not None:
|
if file is not None:
|
||||||
return file
|
return file
|
||||||
|
|
||||||
def get_folder_by_name(self, folder_name: str) -> FileSystemFolder:
|
def get_folder_by_name(self, folder_name: str) -> Union[FileSystemFolder, None]:
|
||||||
"""
|
"""
|
||||||
Returns a the first folder with a matching name.
|
Returns a the first folder with a matching name.
|
||||||
|
|
||||||
|
|||||||
@@ -1004,6 +1004,7 @@ class Node(SimComponent):
|
|||||||
if service in self:
|
if service in self:
|
||||||
_LOGGER.warning(f"Can't add service {service.uuid} to node {self.uuid}. It's already installed.")
|
_LOGGER.warning(f"Can't add service {service.uuid} to node {self.uuid}. It's already installed.")
|
||||||
return
|
return
|
||||||
|
self.services[service.uuid] = service
|
||||||
service.parent = self
|
service.parent = self
|
||||||
service.install() # Perform any additional setup, such as creating files for this service on the node.
|
service.install() # Perform any additional setup, such as creating files for this service on the node.
|
||||||
_LOGGER.info(f"Added service {service.uuid} to node {self.uuid}")
|
_LOGGER.info(f"Added service {service.uuid} to node {self.uuid}")
|
||||||
|
|||||||
@@ -12,6 +12,21 @@ class DatabaseService(Service):
|
|||||||
"""TODO."""
|
"""TODO."""
|
||||||
return super().describe_state()
|
return super().describe_state()
|
||||||
|
|
||||||
|
def uninstall(self) -> None:
|
||||||
|
"""
|
||||||
|
Undo installation procedure.
|
||||||
|
|
||||||
|
This method deletes files created when installing the database, and the database folder if it is empty.
|
||||||
|
"""
|
||||||
|
super().uninstall()
|
||||||
|
node: Node = self.parent
|
||||||
|
node.file_system.delete_file(self.primary_store)
|
||||||
|
node.file_system.delete_file(self.transaction_log)
|
||||||
|
if self.secondary_store:
|
||||||
|
node.file_system.delete_file(self.secondary_store)
|
||||||
|
if len(self.folder.files) == 0:
|
||||||
|
node.file_system.delete_folder(self.folder)
|
||||||
|
|
||||||
def install(self) -> None:
|
def install(self) -> None:
|
||||||
"""Perform first time install on a node, creating necessary files."""
|
"""Perform first time install on a node, creating necessary files."""
|
||||||
super().install()
|
super().install()
|
||||||
@@ -39,10 +54,16 @@ class DatabaseService(Service):
|
|||||||
# note that this parent.file_system.create_folder call in the future will be authenticated by using permissions
|
# note that this parent.file_system.create_folder call in the future will be authenticated by using permissions
|
||||||
# handler. This permission will be granted based on service account given to the database service.
|
# handler. This permission will be granted based on service account given to the database service.
|
||||||
self.parent: Node
|
self.parent: Node
|
||||||
folder = self.parent.file_system.create_folder(folder_name)
|
self.folder = self.parent.file_system.create_folder(folder_name)
|
||||||
self.parent.file_system.create_file("db_primary_store", db_size, FileSystemFileType.MDF, folder=folder)
|
self.primary_store = self.parent.file_system.create_file(
|
||||||
self.parent.file_system.create_file("db_transaction_log", "1", FileSystemFileType.LDF, folder=folder)
|
"db_primary_store", db_size, FileSystemFileType.MDF, folder=self.folder
|
||||||
if use_secondary_db_file:
|
|
||||||
self.parent.file_system.create_file(
|
|
||||||
"db_secondary_store", secondary_db_size, FileSystemFileType.NDF, folder=folder
|
|
||||||
)
|
)
|
||||||
|
self.transaction_log = self.parent.file_system.create_file(
|
||||||
|
"db_transaction_log", "1", FileSystemFileType.LDF, folder=self.folder
|
||||||
|
)
|
||||||
|
if use_secondary_db_file:
|
||||||
|
self.secondary_store = self.parent.file_system.create_file(
|
||||||
|
"db_secondary_store", secondary_db_size, FileSystemFileType.NDF, folder=self.folder
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.secondary_store = None
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Any, Dict, Set, TYPE_CHECKING
|
from typing import Any, Dict, Set
|
||||||
|
|
||||||
from primaite.simulator.core import Action, ActionManager, SimComponent
|
from primaite.simulator.core import Action, ActionManager, SimComponent
|
||||||
from primaite.simulator.network.transmission.transport_layer import Port
|
from primaite.simulator.network.transmission.transport_layer import Port
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from primaite.simulator.network.hardware.base import Node
|
|
||||||
|
|
||||||
|
|
||||||
class SoftwareType(Enum):
|
class SoftwareType(Enum):
|
||||||
"""
|
"""
|
||||||
@@ -143,11 +140,17 @@ class Software(SimComponent):
|
|||||||
This is an abstract class that should be overwritten by specific applications or services. It must be called
|
This is an abstract class that should be overwritten by specific applications or services. It must be called
|
||||||
after the service is already associate with a node. For example, a service may need to authenticate with a
|
after the service is already associate with a node. For example, a service may need to authenticate with a
|
||||||
server during installation, or create files in the node's filesystem.
|
server during installation, or create files in the node's filesystem.
|
||||||
|
|
||||||
:param node: Node on which this software runs.
|
|
||||||
:type node: Node
|
|
||||||
"""
|
"""
|
||||||
parent: "Node" = self.parent # noqa
|
pass
|
||||||
|
|
||||||
|
def uninstall(self) -> None:
|
||||||
|
"""Uninstall this service from a node.
|
||||||
|
|
||||||
|
This is an abstract class that should be overwritten by applications or services. It must be called after the
|
||||||
|
`install` method has already been run on that node. It should undo any installation steps, for example by
|
||||||
|
deleting files, or contacting a server.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
def scan(self) -> None:
|
def scan(self) -> None:
|
||||||
"""Update the observed health status to match the actual health status."""
|
"""Update the observed health status to match the actual health status."""
|
||||||
|
|||||||
@@ -20,3 +20,37 @@ def test_installing_database():
|
|||||||
node = Node(hostname="db-server")
|
node = Node(hostname="db-server")
|
||||||
|
|
||||||
node.install_service(db)
|
node.install_service(db)
|
||||||
|
|
||||||
|
assert db in node
|
||||||
|
|
||||||
|
file_exists = False
|
||||||
|
for folder in node.file_system.folders.values():
|
||||||
|
for file in folder.files.values():
|
||||||
|
if file.name == "db_primary_store":
|
||||||
|
file_exists = True
|
||||||
|
break
|
||||||
|
if file_exists:
|
||||||
|
break
|
||||||
|
assert file_exists
|
||||||
|
|
||||||
|
|
||||||
|
def test_uninstalling_database():
|
||||||
|
db = DatabaseService(
|
||||||
|
name="SQL-database",
|
||||||
|
health_state_actual=SoftwareHealthState.GOOD,
|
||||||
|
health_state_visible=SoftwareHealthState.GOOD,
|
||||||
|
criticality=SoftwareCriticality.MEDIUM,
|
||||||
|
ports=[
|
||||||
|
Port.SQL_SERVER,
|
||||||
|
],
|
||||||
|
operating_state=ServiceOperatingState.RUNNING,
|
||||||
|
)
|
||||||
|
|
||||||
|
node = Node(hostname="db-server")
|
||||||
|
|
||||||
|
node.install_service(db)
|
||||||
|
|
||||||
|
node.uninstall_service(db)
|
||||||
|
|
||||||
|
assert db not in node
|
||||||
|
assert node.file_system.get_folder_by_name("database") is None
|
||||||
|
|||||||
Reference in New Issue
Block a user