Skip to content
Draft
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config/tests/deep-segments-config.data.xml
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@
</rel>
<rel name="detector_configuration" class="DetectorConfig" id="dummy-detector"/>
<rel name="opmon_uri" class="OpMonURI" id="local-opmon-uri"/>
<rel name="resource_manager" class="ResourceManagerConf" id="ResourceManagerConf_test"/>
</obj>


Expand Down
1 change: 1 addition & 0 deletions config/tests/nestedConfig.data.xml
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@
</rel>
<rel name="detector_configuration" class="DetectorConfig" id="dummy-detector"/>
<rel name="opmon_uri" class="OpMonURI" id="local-opmon-uri"/>
<rel name="resource_manager" class="ResourceManagerConf" id="ResourceManagerConf_test"/>
</obj>


Expand Down
6 changes: 6 additions & 0 deletions config/tests/one-controller-config.data.xml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@
<rel name="controller" class="RCApplication" id="controller-0"/>
</obj>

<obj class="ResourceManagerConf" id="ResourceManagerConf_test">
<attr name="address" type="string" val="localhost"/>
<attr name="port" type="u16" val="32009"/>
</obj>

<obj class="Session" id="one-controller-config">
<attr name="data_request_timeout_ms" type="u32" val="1000"/>
<attr name="data_rate_slowdown_factor" type="u32" val="1"/>
Expand All @@ -124,6 +129,7 @@
</rel>
<rel name="detector_configuration" class="DetectorConfig" id="dummy-detector"/>
<rel name="opmon_uri" class="OpMonURI" id="local-opmon-uri"/>
<rel name="resource_manager" class="ResourceManagerConf" id="ResourceManagerConf_test"/>
</obj>


Expand Down
84 changes: 84 additions & 0 deletions src/drunc/unified_shell/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
import sys

import click
import conffwk
import confmodel_dal
from druncschema.process_manager_pb2 import ProcessQuery

from drunc.controller.interface.shell_utils import controller_setup
from drunc.process_manager.interface.context import ProcessManagerContext
from drunc.unified_shell.context import UnifiedShellMode
from drunc.unified_shell.shell_utils import resource_log_tree
from drunc.utils.shell_utils import InterruptedCommand
from drunc.utils.utils import get_logger

Expand All @@ -25,14 +28,58 @@
help="Sleep between app boot, in seconds. This may be useful if you have are using SSHPM, and have SSHD's maxstartups setting set to a low value.",
)
@click.pass_obj
@click.pass_context
def boot(
ctx: click.core.Context,
obj: ProcessManagerContext,
override_logs: bool | None,
sleep_between_app_boot: int | float = 0,
) -> None:
log = get_logger("unified_shell.boot")

# Instantiate the session dal to parse out the managed objects
db = conffwk.Configuration(ctx.obj.configuration_file)
session_dal = db.get_dal(class_name="Session", uid=ctx.obj.configuration_id)
session_name = obj.session_name
user = getpass.getuser()

# Iterate through all the segment nest levels, parse out the requested managed
# objects for that segment, and allocate them to a dict
managed_objects: dict[
str : list(str)
] = {} # segment: list[managed_object_identifier]
managed_objects_present: bool = False
segments = session_dal.segment.segments
while segments:
nested_segments = []
for segment in segments:
managed_objects[segment.id] = confmodel_dal.segment_get_managed_object_tags(
db._obj, ctx.obj.configuration_id, segment.id
)
if managed_objects[segment.id]:
managed_objects_present = True
nested_segments += [nested_segment for nested_segment in segment.segments]
segments = nested_segments
ctx.obj.managed_objects_present = managed_objects_present
ctx.obj.managed_objects = managed_objects

# Split out the segments that have requested resources
empty_segments = [k for k, v in managed_objects.items() if not v]
active_segments = {k: v for k, v in managed_objects.items() if v}

# Log the request of resources if they are used
if ctx.obj.managed_objects_present:
log.info(
"[blue]Placeholder[/blue] Requesting objects in the following segments:"
)
# Note the next 4 lines should be considered to be indented
if active_segments:
resource_log_tree(active_segments, log)
if empty_segments:
log.info(
f"[yellow]Empty segments (skipped):[/yellow] {', '.join(empty_segments)}"
)

processes = obj.get_driver("process_manager").ps(
ProcessQuery(user=user, session=session_name)
)
Expand Down Expand Up @@ -97,6 +144,43 @@ def boot(
sys.exit(1)


@click.command("terminate")
@click.pass_obj
@click.pass_context
def terminate(ctx, obj):
"""
Execute the process manager terminate command, but release the resources prior to
doing so
"""

log = get_logger("unified_shell.terminate")

# Get the handle to the managed objects
all_objects = ctx.obj.managed_objects

# Split out the segments that have requested resources
empty_segments = [k for k, v in all_objects.items() if not v]
active_segments = {k: v for k, v in all_objects.items() if v}

# Log the release of requested resources if they were used
if ctx.obj.managed_objects_present:
log.info(
"[blue]Placeholder[/blue] Releasing managed objects in the following segments:"
)

# if ctx.obj.managed_objects_present:all_objects = ctx.obj.managed_objects
if active_segments:
resource_log_tree(active_segments, log)
if empty_segments:
log.info(
f"[yellow]Empty segments (skipped):[/yellow] {', '.join(empty_segments)}"
)
ctx.obj.managed_objects = {}
ctx.obj.managed_objects_present = False

obj.get_driver("process_manager").terminate()


@click.command("start-shell")
@click.pass_obj
@click.pass_context
Expand Down
4 changes: 4 additions & 0 deletions src/drunc/unified_shell/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ def __init__(self):
self.session_name = ""
self.override_logs = True
self.running_mode = UnifiedShellMode.INTERACTIVE
self.managed_objects: dict[
str : list(str)
] = {} # segment: list[managed_object_identifier]
self.managed_objects_present: bool = False
super(UnifiedShellContext, self).__init__()

def reset(self, address_pm: str = ""):
Expand Down
24 changes: 18 additions & 6 deletions src/drunc/unified_shell/shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,10 @@
logs,
ps,
restart,
terminate,
)
from drunc.process_manager.interface.process_manager import run_pm
from drunc.process_manager.utils import get_pm_type_from_name, validate_k8s_session_name
from drunc.unified_shell.commands import boot, start_shell
from drunc.unified_shell.commands import boot, start_shell, terminate
from drunc.unified_shell.context import UnifiedShellMode
from drunc.unified_shell.shell_utils import generate_fsm_sequence_command
from drunc.utils.configuration import ConfTypes, OKSKey
Expand Down Expand Up @@ -307,8 +306,10 @@ def unified_shell(

# Add the unified shell Click commands to the CLI
unified_shell_log.debug("Adding [green]unified_shell[/green] commands")
ctx.command.add_command(boot, "boot")
ctx.obj.dynamic_commands.add("boot")
unified_shell_commands: list[click.Command] = [boot, terminate]
for cmd in unified_shell_commands:
ctx.command.add_command(cmd, format_name_for_cli(cmd.name))
ctx.obj.dynamic_commands.add(format_name_for_cli(cmd.name))

# Add the process manager Click commands to the CLI
unified_shell_log.debug("Adding [green]process_manager[/green] commands")
Expand Down Expand Up @@ -413,7 +414,14 @@ def cleanup():

# Attempt a stateful shutdown of the controller if possible, returning to
# initial state before terminating
if ctx.obj.get_driver("controller", quiet_fail=True):
if (
len(
ctx.obj.get_driver("process_manager")
.ps(ProcessQuery(user=getpass.getuser(), session=ctx.obj.session_name))
.values
)
> 0
) and ctx.obj.get_driver("controller", quiet_fail=True):
try:
if ctx.obj.get_driver("controller").status().status.in_error:
unified_shell_log.warning(
Expand Down Expand Up @@ -455,7 +463,11 @@ def cleanup():

# Terminate any residual processes
if ctx.obj.get_driver("process_manager"):
ctx.obj.get_driver("process_manager").terminate()
terminate_cmd = ctx.command.get_command(ctx, "terminate")
if terminate_cmd:
ctx.invoke(terminate_cmd)
else:
unified_shell_log.error("Command 'terminate' not found.")

# Check if any processes are still running
if (
Expand Down
18 changes: 18 additions & 0 deletions src/drunc/unified_shell/shell_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,21 @@ def generate_fsm_sequence_command(
)(cmd)

return cmd, format_name_for_cli(sequence.id)


def resource_log_tree(data, log, prefix=""):
items = list(data.items())
total = len(items)

for i, (key, value) in enumerate(items):
is_last = i == total - 1
connector = "└── " if is_last else "├── "

# Check if the value is a nested segment (resources stored as dict)
if isinstance(value, dict) and value:
extension = " " if is_last else "│ "
resource_log_tree(value, prefix + extension)

else:
display_val = str(value)
log.info(f"{prefix}{connector}{key}: {display_val}")
Loading