Skip to content
Snippets Groups Projects
mpsd_software.py 8.83 KiB
Newer Older
  • Learn to ignore specific revisions
  • """mpsd-software: entry point for command line application."""
    
    import argparse
    
    import os
    
    from pathlib import Path
    
    from mpsd_software_manager import __version__, command_name
    
    from .cmds.available import get_available_package_sets, get_available_releases
    from .cmds.init import initialise_environment
    from .cmds.install import install_environment
    from .cmds.prepare import prepare_environment
    from .cmds.remove import remove_environment
    from .cmds.status import environment_status
    from .utils.filesystem_utils import get_root_dir
    from .utils.logging import (
        get_log_file_path,
        record_script_execution_summary,
        set_up_logging,
    )
    
    # command_name = Path(sys.argv[0]).name
    
    Hans Fangohr's avatar
    Hans Fangohr committed
    about_intro = f"""
    
    Hans Fangohr's avatar
    Hans Fangohr committed
    Build software as on MPSD HPC.
    
    Hans Fangohr's avatar
    Hans Fangohr committed
    
        This tool builds software package sets (including toolchains for Octopus).
        It follows recipes as used on the MPSD HPC system and the (spack-based)
        Octopus buildbot. Compiled software is organised into MPSD software release
        versions (such as `dev-23a`) and CPU microarchitecture (such as `sandybridge`).
    
    Hans Fangohr's avatar
    Hans Fangohr committed
        Compiled packages and toolchains can be activated and used via `module load` as
        on the HPC system.
    
    Hans Fangohr's avatar
    Hans Fangohr committed
        Further documentation is available in the README.rst file, online at
    
        https://gitlab.gwdg.de/mpsd-cs/mpsd-software-manager/-/blob/main/README.rst
    
    Hans Fangohr's avatar
    Hans Fangohr committed
    
    Command line usage:
    
    
    Hans Fangohr's avatar
    Hans Fangohr committed
    
    """
    
    
    about_epilog = f"""
    
    
    Examples:
    
    
        1. Query what releases are available for installation
    
           $> {command_name} available
    
        2. Query what package sets and toolchains are available for installation in
    
           $> {command_name} available dev-23a
    
        3. Install foss2022a-serial toolchain from the dev-23a release
    
           $> {command_name} install dev-23a foss2022a-serial
    
        4. Check what package sets and toolchains are installed from release dev-23a
    
           $> {command_name} status dev-23a
    
    Hans Fangohr's avatar
    Hans Fangohr committed
           The `status` command also displays the `module use` command needed to load
           the created modules.
    
    # TODO @Ashwin
    # Martin: Do we still need this function?
    # It seems to be similar to 'prepare'; the parser does not offer it.
    
    def start_new_environment(release, from_release, target_dir):
    
        """Start new MPSD software environment version."""
    
        msg = f"Starting new release {release} from {from_release} to {target_dir}"
    
    def main():
    
        """Execute main entry point."""
    
    Hans Fangohr's avatar
    Hans Fangohr committed
        parser = argparse.ArgumentParser(
            description=about_intro,
            epilog=about_epilog,
            formatter_class=argparse.RawDescriptionHelpFormatter,
        )
    
    Hans Fangohr's avatar
    Hans Fangohr committed
            "-l",
            dest="loglevel",
            choices=["warning", "info", "debug"],
            required=False,
            default="warning",
            help="Set the log level",
        )
    
    Hans Fangohr's avatar
    Hans Fangohr committed
        parser.add_argument("--version", action="version", version=__version__)
    
    Ashwin Kumar Karnad's avatar
    Ashwin Kumar Karnad committed
        subparsers = parser.add_subparsers(
    
            dest="action",
            title="actions",
            description="valid actions",  # required=True
    
    Ashwin Kumar Karnad's avatar
    Ashwin Kumar Karnad committed
        )
    
        subparsers.required = True
        list_of_cmds = [
    
            ("init", "Initialise the MPSD software instance in the current directory"),
    
            ("available", "What is available for installation?"),
    
            ("install", "Install a software environment"),
    
            # ("reinstall", "Reinstall a package_set"),
    
            ("remove", "Remove a package set"),
    
            # ("start-new", "Start a new MPSD software release version"),
    
            ("status", "Show status: what is installed?"),
            ("prepare", "Prepare installation of MPSD-release (dev only)"),
    
        ]
        for cmd, help_text in list_of_cmds:
            subp = subparsers.add_parser(cmd, help=help_text)
    
    
            if cmd == "start-new":
                subp.add_argument(
                    "--from-release",
                    dest="from_release",
                    type=str,
                    required=True,
                    help="Release version to start from",
                )
                subp.add_argument(
                    "--to-release",
                    dest="to_release",
                    type=str,
                    required=True,
                    help="Release version to create",
                )
    
                # most commands except need a release version
    
                if cmd in ["install", "prepare", "reinstall", "remove"]:
    
                    subp.add_argument(
                        "release",
                        type=str,
                        help="Release version to prepare, install, reinstall or remove",
                    )
    
                elif cmd in ["available", "status"]:
    
                    # for some commands the release version is optional
                    subp.add_argument(
                        "release",
                        type=str,
                        nargs="?",
                        help="Release version to prepare, install, reinstall or remove",
                    )
    
    
                if cmd in ["install", "reinstall", "remove"]:
    
    Ashwin Kumar Karnad's avatar
    Ashwin Kumar Karnad committed
                    # "install" command needs additional documentation
    
                        f"One or more package sets (like toolchains) to be {cmd}ed. "
    
                        "Use 'ALL' to refer to all available package sets."
    
                    subp.add_argument(
    
                        "package_set",  # first option defines attribute
                        # name `args.package_set` in `args = parser_args()`
    
    Ashwin Kumar Karnad's avatar
    Ashwin Kumar Karnad committed
                        nargs="+",
    
                    # TODO Move the enable-build-cache flag to only 'install' cmd
    
                    subp.add_argument(
                        "--enable-build-cache",
                        action="store_true",
    
                            "Enable Spack build cache. Useful for reinstallation but "
                            "consumes time and disk space."
    
                if cmd in ["status"]:
                    subp.add_argument(
    
                        "package_set",
    
                        type=str,
                        nargs="?",
                        default="NONE",
                        help="Package set to show status for.",
                    )
    
        # Carry out the action
        args = parser.parse_args()
    
        # Set up logging without file handle:
        # this is used in the init action and for logging the
        # get_root_dir() function
        set_up_logging(args.loglevel)
    
        # Check if the action is init
        # if so, call the init function and exit
        if args.action == "init":
    
            initialise_environment(Path(os.getcwd()))
    
        # if a release version is specified:
        if args.release:
            # sanity check for common mistakes in command line arguments
            if args.release.endswith("/"):  # happens easily with autocompletion
                args.release = args.release.removesuffix("/")
                logging.warning(f"Removed trailing slash from release: {args.release}")
    
        # root_dir is the place where this MPSD software instance has its root
    
        root_dir = get_root_dir()
    
        # set up logging filename: we record activities that change the installation
    
        if args.action in ["init", "install", "prepare", "reinstall", "remove"]:
    
            apex_log_file = get_log_file_path(
    
                args.release,
                args.action,
                root_dir,
            )
        # some commands do not write any log_files:
        elif args.action in ["available", "status"]:
    
            apex_log_file = None
    
            raise NotImplementedError(
                f"Should never happen: unknown args.action={args.action}"
            )
    
        set_up_logging(
            args.loglevel,
    
            apex_log_file,
    
        if args.action not in ["status", "available"]:
            # record the script execution summary only if
            # the action is one that changes files on disk
            record_script_execution_summary(root_dir, apex_log_file)
    
        # Check the command and run related function
    
        if args.action == "remove":
    
    Ashwin Kumar Karnad's avatar
    Ashwin Kumar Karnad committed
            remove_environment(args.release, root_dir, args.package_set)
    
        elif args.action == "start-new":
    
            start_new_environment(args.from_release, args.to_release, root_dir)
    
        elif args.action == "install":
    
    Ashwin Kumar Karnad's avatar
    Ashwin Kumar Karnad committed
            install_environment(
    
                args.release, args.package_set, root_dir, args.enable_build_cache
    
    Ashwin Kumar Karnad's avatar
    Ashwin Kumar Karnad committed
            )
    
        elif args.action == "status":
    
            environment_status(args.release, root_dir, args.package_set)
    
        elif args.action == "prepare":
    
            prepare_environment(args.release, root_dir)
    
    Hans Fangohr's avatar
    Hans Fangohr committed
        elif args.action == "available":
    
            if args.release:
                get_available_package_sets(args.release)
            else:
                get_available_releases(print_result=True)
                sys.exit(0)
    
    Hans Fangohr's avatar
    Hans Fangohr committed
        else:
    
            message = f"No known action found (args.action={args.action}). Should probably never happen."
    
    Hans Fangohr's avatar
    Hans Fangohr committed
            logging.error(message)
            raise NotImplementedError(message)
    
    # TODO Martin: This can be removed (will do that separately)
    
    if __name__ == "__main__":