CLI

Command line user interface for the node component

Attributes

Classes

Functions

launch_cli

launch_cli()

Parses command line input for the node component and launches node accordingly.

Source code in fedbiomed/node/cli.py
def launch_cli():
    """Parses command line input for the node component and launches node accordingly.
    """



    cli = CommonCLI()
    cli.set_environ(environ=environ)
    cli.initialize_certificate_parser()
    # cli.initialize_create_configuration(config_class=NodeConfig)


    # Register description for CLI
    cli.description = f'{__intro__}:A CLI app for fedbiomed researchers.'

    cli.parser.add_argument('-a', '--add',
                            help='Add and configure local dataset (interactive)',
                            action='store_true')
    cli.parser.add_argument('-am', '--add-mnist',
                            help='Add MNIST local dataset (non-interactive)',
                            type=str, nargs='?', const='', metavar='path_mnist',
                            action='store')
    # this option provides a json file describing the data to add
    cli.parser.add_argument('-adff', '--add-dataset-from-file',
                            help='Add a local dataset described by json file (non-interactive)',
                            type=str,
                            action='store')
    cli.parser.add_argument('-d', '--delete',
                            help='Delete existing local dataset (interactive)',
                            action='store_true')
    cli.parser.add_argument('-da', '--delete-all',
                            help='Delete all existing local datasets (non interactive)',
                            action='store_true')
    cli.parser.add_argument('-dm', '--delete-mnist',
                            help='Delete existing MNIST local dataset (non-interactive)',
                            action='store_true')
    cli.parser.add_argument('-l', '--list',
                            help='List my shared_data',
                            action='store_true')
    cli.parser.add_argument('-s', '--start-node',
                            help='Start fedbiomed node.',
                            action='store_true')
    cli.parser.add_argument('-rtp', '--register-training-plan',
                            help='Register and approve a training plan from a local file.',
                            action='store_true')
    cli.parser.add_argument('-atp', '--approve-training-plan',
                            help='Approve a training plan (requested, default or registered)',
                            action='store_true')
    cli.parser.add_argument('-rjtp', '--reject-training-plan',
                            help='Reject a training plan (requested, default or registered)',
                            action='store_true')
    cli.parser.add_argument('-utp', '--update-training-plan',
                            help='Update training plan file (for a training plan registered from a local file)',
                            action='store_true')
    cli.parser.add_argument('-dtp', '--delete-training-plan',
                            help='Delete a training plan from database (not for default training plans)',
                            action='store_true')
    cli.parser.add_argument('-ltps', '--list-training-plans',
                            help='List all training plans (requested, default or registered)',
                            action='store_true')
    cli.parser.add_argument('-vtp', '--view-training-plan',
                            help='View a training plan source code (requested, default or registered)',
                            action='store_true')
    cli.parser.add_argument('-g', '--gpu',
                            help='Use of a GPU device, if any available (default: dont use GPU)',
                            action='store_true')
    cli.parser.add_argument('-gn', '--gpu-num',
                            help='Use GPU device with the specified number instead of default device, if available',
                            type=int,
                            action='store')
    cli.parser.add_argument('-go', '--gpu-only',
                            help='Force use of a GPU device, if any available, even if researcher doesnt ' +
                                 'request it (default: dont use GPU)',
                            action='store_true')



    print(__intro__)
    print('\t- 🆔 Your node ID:', environ['NODE_ID'], '\n')

    # Parse CLI arguments after the arguments are ready
    cli.parse_args()

    if cli.arguments.add:
        add_database()
    elif cli.arguments.add_mnist is not None:
        add_database(interactive=False, path=cli.arguments.add_mnist)
    elif cli.arguments.add_dataset_from_file is not None:
        print("Dataset description file provided: adding these data")
        try:
            with open(cli.arguments.add_dataset_from_file) as json_file:
                data = json.load(json_file)
        except:
            logger.critical("cannot read dataset json file: " + cli.arguments.add_dataset_from_file)
            sys.exit(-1)

        # verify that json file is complete
        for k in ["path", "data_type", "description", "tags", "name"]:
            if k not in data:
                logger.critical("dataset json file corrupted: " + cli.arguments.add_dataset_from_file)

        # dataset path can be defined:
        # - as an absolute path -> take it as it is
        # - as a relative path  -> add the ROOT_DIR in front of it
        # - using an OS environment variable -> transform it
        #
        elements = data["path"].split(os.path.sep)
        if elements[0].startswith("$"):
            # expand OS environment variable
            var = elements[0][1:]
            if var in os.environ:
                var = os.environ[var]
                elements[0] = var
            else:
                logger.info("Unknown env var: " + var)
                elements[0] = ""
        elif elements[0]:
            # p is relative (does not start with /)
            # prepend with topdir
            elements = [environ["ROOT_DIR"]] + elements

        # rebuild the path with these (eventually) new elements
        data["path"] = os.path.join(os.path.sep, *elements)

        # add the dataset to local database (not interactive)
        add_database(interactive=False,
                     path=data["path"],
                     data_type=data["data_type"],
                     description=data["description"],
                     tags=data["tags"],
                     name=data["name"],
                     dataset_parameters=data.get("dataset_parameters")
                     )

    elif cli.arguments.list:
        print('Listing your data available')
        data = dataset_manager.list_my_data(verbose=True)
        if len(data) == 0:
            print('No data has been set up.')
    elif cli.arguments.delete:
        delete_database()
    elif cli.arguments.delete_all:
        delete_all_database()
    elif cli.arguments.delete_mnist:
        delete_database(interactive=False)
    elif cli.arguments.register_training_plan:
        register_training_plan()
    elif cli.arguments.approve_training_plan:
        approve_training_plan()
    elif cli.arguments.reject_training_plan:
        reject_training_plan()
    elif cli.arguments.update_training_plan:
        update_training_plan()
    elif cli.arguments.delete_training_plan:
        delete_training_plan()
    elif cli.arguments.list_training_plans:
        tp_security_manager.list_training_plans(verbose=True)
    elif cli.arguments.view_training_plan:
        view_training_plan()
    elif cli.arguments.start_node:
        # convert to node arguments structure format expected in Round()
        node_args = {
            'gpu': (cli.arguments.gpu_num is not None) or (cli.arguments.gpu is True) or
                   (cli.arguments.gpu_only is True),
            'gpu_num': cli.arguments.gpu_num,
            'gpu_only': (cli.arguments.gpu_only is True)
        }

        launch_node(node_args)

launch_node

launch_node(node_args=None)

Launches a node in a separate process.

Process ends when user triggers a KeyboardInterrupt exception (CTRL+C).

Parameters:

Name Type Description Default
node_args Union[dict, None]

Command line arguments for node See Round() for details.

None
Source code in fedbiomed/node/cli.py
def launch_node(node_args: Union[dict, None] = None):
    """Launches a node in a separate process.

    Process ends when user triggers a KeyboardInterrupt exception (CTRL+C).

    Args:
        node_args: Command line arguments for node
            See `Round()` for details.
    """

    p = Process(target=_manage_node, name='node-' + environ['NODE_ID'], args=(node_args,))
    p.daemon = True
    p.start()

    logger.info("Node started as process with pid = " + str(p.pid))
    try:
        print('To stop press Ctrl + C.')
        p.join()
    except KeyboardInterrupt:
        p.terminate()

        # give time to the node to send message
        time.sleep(1)
        while p.is_alive():
            logger.info("Terminating process id =" + str(p.pid))
            time.sleep(1)

        # (above) p.exitcode returns None if not finished yet
        logger.info('Exited with code ' + str(p.exitcode))

        sys.exit(0)

main

main()

Entry point for the node.

Source code in fedbiomed/node/cli.py
def main():
    """Entry point for the node.
    """
    try:
        launch_cli()
    except KeyboardInterrupt:
        # send error message to researcher via logger.error()
        logger.critical('Operation cancelled by user.')