diff --git a/src/drunc/controller/children_interface/grpc_child.py b/src/drunc/controller/children_interface/grpc_child.py index cf033afb..f263e13c 100644 --- a/src/drunc/controller/children_interface/grpc_child.py +++ b/src/drunc/controller/children_interface/grpc_child.py @@ -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() diff --git a/src/drunc/controller/children_interface/rest_api_child.py b/src/drunc/controller/children_interface/rest_api_child.py index 16ed220b..d146e03a 100644 --- a/src/drunc/controller/children_interface/rest_api_child.py +++ b/src/drunc/controller/children_interface/rest_api_child.py @@ -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 diff --git a/src/drunc/controller/controller.py b/src/drunc/controller/controller.py index 0e09d019..16fb2a66 100644 --- a/src/drunc/controller/controller.py +++ b/src/drunc/controller/controller.py @@ -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 @@ -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'], @@ -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] ) diff --git a/src/drunc/controller/controller_driver.py b/src/drunc/controller/controller_driver.py index c8ae3f3e..b8fdc0a6 100644 --- a/src/drunc/controller/controller_driver.py +++ b/src/drunc/controller/controller_driver.py @@ -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 @@ -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) diff --git a/src/drunc/controller/interface/commands.py b/src/drunc/controller/interface/commands.py index 71e6103f..3ced89d9 100644 --- a/src/drunc/controller/interface/commands.py +++ b/src/drunc/controller/interface/commands.py @@ -24,16 +24,6 @@ def list_transitions(obj:ControllerContext, all:bool) -> None: obj.print('\nUse [yellow]help [/] 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 @@ -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) diff --git a/src/drunc/controller/interface/shell.py b/src/drunc/controller/interface/shell.py index 2bcc25a2..70f890f5 100644 --- a/src/drunc/controller/interface/shell.py +++ b/src/drunc/controller/interface/shell.py @@ -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') diff --git a/src/drunc/controller/interface/shell_utils.py b/src/drunc/controller/interface/shell_utils.py index 0314cf1b..e5b81a2b 100644 --- a/src/drunc/controller/interface/shell_utils.py +++ b/src/drunc/controller/interface/shell_utils.py @@ -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 @@ -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: @@ -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 diff --git a/src/drunc/unified_shell/shell.py b/src/drunc/unified_shell/shell.py index 389ad966..c3fb12c7 100644 --- a/src/drunc/unified_shell/shell.py +++ b/src/drunc/unified_shell/shell.py @@ -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') diff --git a/src/drunc/utils/shell_utils.py b/src/drunc/utils/shell_utils.py index e7f6232f..35d03022 100644 --- a/src/drunc/utils/shell_utils.py +++ b/src/drunc/utils/shell_utils.py @@ -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: @@ -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: