Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert "Revert "Recursive statuses"" #298

Merged
merged 6 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
18 changes: 5 additions & 13 deletions src/drunc/controller/children_interface/grpc_child.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,21 +90,13 @@ def start_listening(self, bdesc):
)

def get_status(self, token) -> Response:
from druncschema.controller_pb2 import Status
from drunc.utils.grpc_utils import unpack_any

status = unpack_any(
send_command(
controller = self.controller,
token = token,
command = 'get_status',
data = None
).data,
Status
return send_command(
controller = self.controller,
token = token,
command = 'status',
data = None
)

return status

def terminate(self):
if self.channel:
self.channel.close()
Expand Down
12 changes: 10 additions & 2 deletions src/drunc/controller/children_interface/rest_api_child.py
Original file line number Diff line number Diff line change
Expand Up @@ -446,14 +446,22 @@ def get_endpoint(self):

def get_status(self, token):
from druncschema.controller_pb2 import Status
from druncschema.request_response_pb2 import Response, ResponseFlag
from drunc.utils.grpc_utils import pack_to_any

return Status(
name = self.name,
status = Status(
state = self.state.get_operational_state(),
sub_state = 'idle' if not self.state.get_executing_command() else 'executing_cmd',
in_error = self.state.in_error() or not self.commander.ping(), # meh
included = self.state.included(),
)
return Response(
name = self.name,
token = None,
data = pack_to_any(status),
flag = ResponseFlag.EXECUTED_SUCCESSFULLY,
children = [],
)

def propagate_command(self, command:str, data, token:Token) -> Response:
from druncschema.request_response_pb2 import ResponseFlag
Expand Down
84 changes: 6 additions & 78 deletions src/drunc/controller/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from druncschema.generic_pb2 import PlainText, PlainTextVector
from druncschema.broadcast_pb2 import BroadcastType
from druncschema.controller_pb2_grpc import ControllerServicer
from druncschema.controller_pb2 import Status, ChildrenStatus
from druncschema.controller_pb2 import Status
from druncschema.controller_pb2 import FSMCommand, FSMCommandResponse, FSMResponseFlag

from drunc.controller.children_interface.child_node import ChildNode
Expand Down Expand Up @@ -158,26 +158,12 @@ def __init__(self, configuration, name:str, session:str, token:Token):
),

CommandDescription(
name = 'get_children_status',
data_type = ['generic_pb2.PlainText','None'],
help = 'Get the status of all the children. Only get the status from the child if provided in the request.',
return_type = 'controller_pb2.ChildrenStatus'
),

CommandDescription(
name = 'get_status',
name = 'status',
data_type = ['None'],
help = 'Get the status of self',
return_type = 'controller_pb2.Status'
),

CommandDescription(
name = 'ls',
data_type = ['None'],
help = 'List the children',
return_type = 'generic_pb2.PlainTextVector'
),

CommandDescription(
name = 'describe_fsm',
data_type = ['generic_pb2.PlainText', 'None'],
Expand Down Expand Up @@ -447,75 +433,17 @@ def propagate_to_child(child, command, command_data, token, response_lock, respo
action=ActionType.READ,
system=SystemType.CONTROLLER
) # 2nd step
@unpack_request_data_to(pass_token=True) # 3rd step
def get_children_status(self, token:Token) -> Response:
#from drunc.controller.utils import get_status_message
cs = []
for n in self.children_nodes:
try:
cs += [n.get_status(token)]
except Exception as e: # TEMPORARY hack
from druncschema.controller_pb2 import Status
cs += [
Status(
name = n.name,
state = 'unknown',
sub_state = 'unknown',
in_error = True,
)
]

response = ChildrenStatus(
children_status = cs
)
return Response(
name = self.name,
token = None,
data = pack_to_any(response),
flag = ResponseFlag.EXECUTED_SUCCESSFULLY,
children = [],
)

# ORDER MATTERS!
@broadcasted # outer most wrapper 1st step
@authentified_and_authorised(
action=ActionType.READ,
system=SystemType.CONTROLLER
) # 2nd step
@unpack_request_data_to(None) # 3rd step
def get_status(self) -> Response:
@unpack_request_data_to(None, pass_token=True) # 3rd step
def status(self, token:Token) -> Response:
from drunc.controller.utils import get_status_message
status = get_status_message(self.stateful_node)
status.name = self.name

return Response (
name = self.name,
token = None,
token = token,
data = pack_to_any(status),
flag = ResponseFlag.EXECUTED_SUCCESSFULLY,
children = [],
)


# ORDER MATTERS!
@broadcasted # outer most wrapper 1st step
@authentified_and_authorised(
action=ActionType.READ,
system=SystemType.CONTROLLER
) # 2nd step
@unpack_request_data_to(None) # 3rd step
def ls(self) -> PlainTextVector:
nodes = [node.name for node in self.children_nodes]
response = PlainTextVector(
text = nodes
)

return Response (
name = self.name,
token = None,
data = pack_to_any(response),
flag = ResponseFlag.EXECUTED_SUCCESSFULLY,
children = [],
children = [n.get_status(token) for n in self.children_nodes]
)


Expand Down
12 changes: 3 additions & 9 deletions src/drunc/controller/controller_driver.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from druncschema.request_response_pb2 import Request, Response, Description
from druncschema.generic_pb2 import PlainText, PlainTextVector
from druncschema.controller_pb2 import Status, ChildrenStatus
from druncschema.controller_pb2 import Status

from drunc.utils.grpc_utils import unpack_any
from drunc.utils.shell_utils import GRPCDriver
Expand All @@ -27,14 +27,8 @@ def describe_fsm(self, key:str=None) -> Description: # key can be: a state name,
input = PlainText(text = key)
return self.send_command('describe_fsm', data = input, outformat = FSMCommandsDescription)

def ls(self) -> Description:
return self.send_command('ls', outformat = PlainTextVector)

def get_status(self) -> Description:
return self.send_command('get_status', outformat = Status)

def get_children_status(self) -> Description:
return self.send_command('get_children_status', outformat = ChildrenStatus)
def status(self) -> Description:
return self.send_command('status', outformat = Status)

def take_control(self) -> Description:
return self.send_command('take_control', outformat = PlainText)
Expand Down
56 changes: 5 additions & 51 deletions src/drunc/controller/interface/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,6 @@ def list_transitions(obj:ControllerContext, all:bool) -> None:
obj.print('\nUse [yellow]help <command>[/] for more information on a command.\n')



@click.command('ls')
@click.pass_obj
def ls(obj:ControllerContext) -> None:
children = obj.get_driver('controller').ls().data
if not children: return
obj.print(children.text)



@click.command('wait')
@click.argument("sleep_time", type=int, default=1)
@click.pass_obj
Expand All @@ -44,51 +34,15 @@ def wait(obj:ControllerContext, sleep_time:int) -> None:
obj.print(f"Command [green]wait[/green] ran for {sleep_time} seconds.")




@click.command('status')
@click.pass_obj
def status(obj:ControllerContext) -> None:
from druncschema.controller_pb2 import Status, ChildrenStatus
status = obj.get_driver('controller').get_status().data

if not status: return
statuses = obj.get_driver('controller').status()

from drunc.controller.interface.shell_utils import format_bool, tree_prefix

from rich.table import Table
t = Table(title=f'{status.name} status')
t.add_column('Name')
t.add_column('State')
t.add_column('Substate')
t.add_column('In error', justify='center')
t.add_column('Included', justify='center')
t.add_row(
status.name,
status.state,
status.sub_state,
format_bool(status.in_error, false_is_good = True),
format_bool(status.included),
)

statuses = obj.get_driver('controller').get_children_status().data

if not statuses:
statuses = []

how_many = len(statuses.children_status)

for i, c_status in enumerate(statuses.children_status):
first_column = tree_prefix(i, how_many)+c_status.name

t.add_row(
first_column,
c_status.state,
c_status.sub_state,
format_bool(c_status.in_error, false_is_good=True),
format_bool(c_status.included)
)
obj.print(t)
obj.print_status_summary()
return
from drunc.controller.interface.shell_utils import print_status_table
print_status_table(obj,statuses)

@click.command('connect')
@click.argument('controller_address', type=str)
Expand Down
5 changes: 2 additions & 3 deletions src/drunc/controller/interface/shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,10 @@ def controller_shell(ctx, controller_address:str, log_level:str) -> None:
transitions = ctx.obj.get_driver('controller').describe_fsm(key="all-transitions").data

from drunc.controller.interface.commands import (
list_transitions, ls, status, connect, take_control, surrender_control, who_am_i, who_is_in_charge, fsm, include, exclude, wait
describe, status, connect, take_control, surrender_control, who_am_i, who_is_in_charge, fsm, include, exclude, wait
)

ctx.command.add_command(list_transitions, 'list-transitions')
ctx.command.add_command(ls, 'ls')
ctx.command.add_command(describe, 'describe')
ctx.command.add_command(status, 'status')
ctx.command.add_command(connect, 'connect')
ctx.command.add_command(take_control, 'take-control')
Expand Down
44 changes: 42 additions & 2 deletions src/drunc/controller/interface/shell_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,41 @@
import logging
log = logging.getLogger('controller_shell_utils')


def print_status_table(obj, statuses):
from druncschema.controller_pb2 import Status
if not statuses: return

if type(statuses.data) != Status:
from google.protobuf.any_pb2 import Any
data_type = statuses.data.TypeName() if type(statuses.data) == Any else type(statuses.data)
obj.print(f'Could not get the status of the controller, got a \'{data_type}\' instead')
return

from drunc.controller.interface.shell_utils import format_bool, tree_prefix
from rich.table import Table

t = Table(title=f'Status')
t.add_column('Name')
t.add_column('State')
t.add_column('Substate')
t.add_column('In error', justify='center')
t.add_column('Included', justify='center')

def add_status_to_table(status, table, prefix):
table.add_row(
prefix+status.name,
status.data.state,
status.data.sub_state,
format_bool(status.data.in_error, false_is_good = True),
format_bool(status.data.included),
)
for child in status.children:
add_status_to_table(child, table, prefix=prefix+' ')
add_status_to_table(statuses, t, prefix='')
obj.print(t)
obj.print_status_summary()

def controller_cleanup_wrapper(ctx):
def controller_cleanup():
# remove the shell from the controller broadcast list
Expand Down Expand Up @@ -93,8 +128,8 @@ def controller_setup(ctx, controller_address):

ctx.print('Connected to the controller')

children = ctx.get_driver('controller').ls().data
ctx.print(f'{desc.name}.{desc.session}\'s children :family:: {children.text}')
# children = ctx.get_driver('controller').ls().data
# ctx.print(f'{desc.name}.{desc.session}\'s children :family:: {children.text}')

ctx.info(f'Taking control of the controller as {ctx.get_token()}')
try:
Expand Down Expand Up @@ -313,6 +348,11 @@ def add_to_table(table, response, prefix=''):
add_to_table(t, result)
obj.print(t)

statuses = obj.get_driver('controller').status()

from drunc.controller.interface.shell_utils import print_status_table
print_status_table(obj, statuses)


from druncschema.controller_pb2 import FSMCommandDescription

Expand Down
4 changes: 1 addition & 3 deletions src/drunc/unified_shell/shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,11 +184,9 @@ def cleanup():


from drunc.controller.interface.commands import (
list_transitions, ls, status, connect, take_control, surrender_control, who_am_i, who_is_in_charge, fsm, include, exclude, wait
status, connect, take_control, surrender_control, who_am_i, who_is_in_charge, fsm, include, exclude, wait
)

ctx.command.add_command(list_transitions, 'list-transitions')
ctx.command.add_command(ls, 'ls')
ctx.command.add_command(status, 'status')
ctx.command.add_command(connect, 'connect')
ctx.command.add_command(take_control, 'take-control')
Expand Down
13 changes: 6 additions & 7 deletions src/drunc/utils/shell_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ def get_driver(self, name:str=None) -> GRPCDriver:
raise DruncShellException(f'More than one driver in this context')
return list(self._drivers.values())[0]
except KeyError:
self._log.error(f'FSM Commands cannot be sent until the Session is booted')
self._log.error(f'Controller-specific commands cannot be sent until the session is booted')
raise SystemExit(1) # used to avoid having to catch multiple Attribute errors when this function gets called

def get_token(self) -> Token:
Expand Down Expand Up @@ -302,13 +302,12 @@ def critical(self, *args, **kwargs) -> None:


def print_status_summary(self) -> None:
status = self.get_driver('controller').get_status().data.state
available_actions = [command.name.replace("_", "-") for command in self.get_driver('controller').describe_fsm().data.commands]
if status.find('(') == -1:
self.print(f"Current FSM status is [green]{status}[/green]. Available transitions are [green]{'[/green], [green]'.join(available_actions)}[/green].")
else:
status = self.get_driver('controller').status().data
if status.in_error:
self.print(f"[red] FSM is in error ({status})[/red], not currently accepting new commands.")
return
else:
available_actions = [command.name for command in self.get_driver('controller').describe_fsm().data.commands]
self.print(f"Current FSM status is [green]{status.state}[/green]. Available transitions are [green]{'[/green], [green]'.join(available_actions)}[/green].")


def create_dummy_token_from_uname() -> Token:
Expand Down