Example #1
0
def main():
    """Command line interface for the ``qpass`` program."""
    # Initialize logging to the terminal.
    coloredlogs.install()
    # Prepare for command line argument parsing.
    action = show_matching_entry
    program_opts = dict(exclude_list=[])
    show_opts = dict(filters=[], use_clipboard=is_clipboard_supported())
    verbosity = 0
    # Parse the command line arguments.
    try:
        options, arguments = getopt.gnu_getopt(
            sys.argv[1:],
            "elnp:f:x:vqh",
            ["edit", "list", "no-clipboard", "password-store=", "filter=", "exclude=", "verbose", "quiet", "help"],
        )
        for option, value in options:
            if option in ("-e", "--edit"):
                action = edit_matching_entry
            elif option in ("-l", "--list"):
                action = list_matching_entries
            elif option in ("-n", "--no-clipboard"):
                show_opts["use_clipboard"] = False
            elif option in ("-p", "--password-store"):
                stores = program_opts.setdefault("stores", [])
                stores.append(PasswordStore(directory=value))
            elif option in ("-f", "--filter"):
                show_opts["filters"].append(value)
            elif option in ("-x", "--exclude"):
                program_opts["exclude_list"].append(value)
            elif option in ("-v", "--verbose"):
                coloredlogs.increase_verbosity()
                verbosity += 1
            elif option in ("-q", "--quiet"):
                coloredlogs.decrease_verbosity()
                verbosity -= 1
            elif option in ("-h", "--help"):
                usage(__doc__)
                return
            else:
                raise Exception("Unhandled option! (programming error)")
        if not (arguments or action == list_matching_entries):
            usage(__doc__)
            return
    except Exception as e:
        warning("Error: %s", e)
        sys.exit(1)
    # Execute the requested action.
    try:
        show_opts["quiet"] = verbosity < 0
        kw = show_opts if action == show_matching_entry else {}
        action(QuickPass(**program_opts), arguments, **kw)
    except PasswordStoreError as e:
        # Known issues don't get a traceback.
        logger.error("%s", e)
        sys.exit(1)
    except KeyboardInterrupt:
        # If the user interrupted an interactive prompt they most likely did so
        # intentionally, so there's no point in generating more output here.
        sys.exit(1)
Example #2
0
def cryptdisks_stop_cli():
    """
    Usage: cryptdisks-stop-fallback NAME

    Reads /etc/crypttab and locks the encrypted filesystem with the given NAME.

    This program emulates the functionality of Debian's cryptdisks_stop program,
    but it only supports LUKS encryption and a small subset of the available
    encryption options.
    """
    # Enable logging to the terminal and system log.
    coloredlogs.install(syslog=True)
    # Get the name of the encrypted filesystem from the command line arguments
    # and show a simple usage message when no name is given as an argument.
    try:
        target = sys.argv[1]
    except IndexError:
        usage(dedent(cryptdisks_stop_cli.__doc__))
    else:
        # Call our Python implementation of `cryptdisks_stop'.
        try:
            cryptdisks_stop(target)
        except ValueError as e:
            # cryptdisks_stop() raises ValueError when the given target isn't
            # listed in /etc/crypttab. This doesn't deserve a traceback on the
            # terminal.
            warning("Error: %s", e)
            sys.exit(1)
        except Exception as e:
            # Any other exceptions are logged to the terminal and system log.
            logger.exception("Aborting due to exception!")
            sys.exit(1)
Example #3
0
def main():
    """Command line interface for the ``coloredlogs`` program."""
    actions = []
    try:
        # Parse the command line arguments.
        options, arguments = getopt.getopt(sys.argv[1:], 'cdh', [
            'convert', 'to-html', 'demo', 'help',
        ])
        # Map command line options to actions.
        for option, value in options:
            if option in ('-c', '--convert', '--to-html'):
                actions.append(functools.partial(convert_command_output, *arguments))
                arguments = []
            elif option in ('-d', '--demo'):
                actions.append(demonstrate_colored_logging)
            elif option in ('-h', '--help'):
                usage(__doc__)
                return
            else:
                assert False, "Programming error: Unhandled option!"
        if not actions:
            usage(__doc__)
            return
    except Exception as e:
        warning("Error: %s", e)
        sys.exit(1)
    for function in actions:
        function()
Example #4
0
def parse_arguments(arguments):
    """
    Parse the command line arguments.

    :param arguments: A list of strings with command line arguments.
    :returns: ``True`` if a dry run was requested, ``False`` otherwise.
    """
    dry_run = False
    try:
        options, arguments = getopt.gnu_getopt(
            arguments, 'nvqh', ['dry-run', 'verbose', 'quiet', 'help'])
        for option, value in options:
            if option in ('-n', '--dry-run'):
                dry_run = True
            elif option in ('-v', '--verbose'):
                coloredlogs.increase_verbosity()
            elif option in ('-q', '--quiet'):
                coloredlogs.decrease_verbosity()
            elif option in ('-h', '--help'):
                usage(USAGE_TEXT)
                sys.exit(0)
            else:
                assert False, "Unhandled option!"
        return dry_run
    except Exception as e:
        warning("Error: Failed to parse command line arguments! (%s)", e)
        sys.exit(1)
Example #5
0
def main():
    """Command line interface for the ``auto-adjust-display-brightness`` program."""
    # Initialize logging to the terminal.
    coloredlogs.install()
    # Parse the command line arguments.
    step_brightness = None
    try:
        options, arguments = getopt.getopt(
            sys.argv[1:], 'fvqh', ['force', 'verbose', 'quiet', 'help'])
        for option, value in options:
            if option in ('-f', '--force'):
                step_brightness = False
            elif option in ('-v', '--verbose'):
                coloredlogs.increase_verbosity()
            elif option in ('-q', '--quiet'):
                coloredlogs.decrease_verbosity()
            elif option in ('-h', '--help'):
                usage(__doc__)
                return
            else:
                assert False, "Unhandled option!"
    except Exception as e:
        warning("Failed to parse command line arguments! (%s)", e)
        sys.exit(1)
    # Load the configuration file(s).
    try:
        config = load_config()
    except ConfigurationError as e:
        warning("%s", e)
        sys.exit(1)
    # Determine whether to change the brightness at once or gradually.
    if step_brightness is None:
        if find_system_uptime() < 60 * 5:
            logger.info(
                "Changing brightness at once (system has just booted).")
            step_brightness = False
        else:
            logger.info(
                "Changing brightness gradually (system has been running for a while)."
            )
            step_brightness = True
    else:
        logger.info("Changing brightness at once (-f or --force was given).")
    # Change the brightness of the configured display(s).
    dark_outside = is_it_dark_outside(
        latitude=float(config['location']['latitude']),
        longitude=float(config['location']['longitude']),
        elevation=float(config['location']['elevation']))
    method = 'decrease_brightness' if dark_outside else 'increase_brightness'
    num_success, num_failed = 0, 0
    for controller in config['controllers']:
        try:
            getattr(controller, method)(10 if step_brightness else 100)
            num_success += 1
        except Exception as e:
            logger.warning("Failed to change brightness of %s! (%s)",
                           controller, e)
            num_failed += 1
    if num_failed > 0 and num_success == 0:
        sys.exit(1)
Example #6
0
def main():
    """Command line interface for the ``dwim`` program."""
    from dwim import DEFAULT_PROFILE, dwim
    # Initialize logging to the terminal.
    coloredlogs.install()
    # Define the command line option defaults.
    profile_script = DEFAULT_PROFILE
    # Parse the command line arguments.
    try:
        options, _ = getopt.getopt(sys.argv[1:], 'c:vqh', [
            'config=', 'verbose', 'quiet', 'help',
        ])
        for option, value in options:
            if option in ('-c', '--config'):
                profile_script = value
            elif option in ('-v', '--verbose'):
                coloredlogs.increase_verbosity()
            elif option in ('-q', '--quiet'):
                coloredlogs.decrease_verbosity()
            elif option in ('-h', '--help'):
                usage(__doc__)
                sys.exit(0)
    except Exception as e:
        warning("Error: Failed to parse command line arguments! (%s)", e)
        sys.exit(1)
    # Execute the requested action(s).
    try:
        dwim(profile_script)
    except Exception:
        logger.exception("Caught a fatal exception! Terminating ..")
        sys.exit(1)
Example #7
0
def parse_arguments(arguments):
    """
    Parse the command line arguments.

    :param arguments: A list of strings with command line options and/or arguments.
    :returns: A list of strings with the positional arguments.
    """
    try:
        options, arguments = getopt.gnu_getopt(arguments, 'vqh', [
            'verbose', 'quiet', 'help'
        ])
        for option, value in options:
            if option in ('-v', '--verbose'):
                coloredlogs.increase_verbosity()
            elif option in ('-q', '--quiet'):
                coloredlogs.decrease_verbosity()
            elif option in ('-h', '--help'):
                usage(USAGE_TEXT)
                sys.exit(0)
            else:
                assert False, "Unhandled option!"
        if not arguments:
            usage(USAGE_TEXT)
            sys.exit(0)
        return arguments
    except Exception as e:
        warning("Error: Failed to parse command line arguments! (%s)", e)
        sys.exit(1)
Example #8
0
def ensure_root_privileges():
    """
    Make sure we have root privileges.
    """
    if os.getuid() != 0:
        warning("Error: Please run this command as root!")
        sys.exit(1)
Example #9
0
def main():
    """Command line interface for the ``coloredlogs`` program."""
    actions = []
    try:
        # Parse the command line arguments.
        options, arguments = getopt.getopt(sys.argv[1:], 'cdh', [
            'convert', 'to-html', 'demo', 'help',
        ])
        # Map command line options to actions.
        for option, value in options:
            if option in ('-c', '--convert', '--to-html'):
                actions.append(functools.partial(convert_command_output, *arguments))
                arguments = []
            elif option in ('-d', '--demo'):
                actions.append(demonstrate_colored_logging)
            elif option in ('-h', '--help'):
                usage(__doc__)
                return
            else:
                assert False, "Programming error: Unhandled option!"
        if not actions:
            usage(__doc__)
            return
    except Exception as e:
        warning("Error: %s", e)
        sys.exit(1)
    for function in actions:
        function()
Example #10
0
def main():
    """Command line interface for the ``negotiator-guest`` program."""
    # Initialize logging to the terminal and system log.
    coloredlogs.install(syslog=True)
    # Parse the command line arguments.
    list_commands = False
    execute_command = None
    start_daemon = False
    timeout = DEFAULT_TIMEOUT
    character_device = None
    try:
        options, arguments = getopt.getopt(sys.argv[1:], 'le:dt:c:vqh', [
            'list-commands', 'execute=', 'daemon', 'timeout=',
            'character-device=', 'verbose', 'quiet', 'help'
        ])
        for option, value in options:
            if option in ('-l', '--list-commands'):
                list_commands = True
            elif option in ('-e', '--execute'):
                execute_command = value
            elif option in ('-d', '--daemon'):
                start_daemon = True
            elif option in ('-t', '--timeout'):
                timeout = int(value)
            elif option in ('-c', '--character-device'):
                character_device = value
            elif option in ('-v', '--verbose'):
                coloredlogs.increase_verbosity()
            elif option in ('-q', '--quiet'):
                coloredlogs.decrease_verbosity()
            elif option in ('-h', '--help'):
                usage(__doc__)
                sys.exit(0)
        if not (list_commands or execute_command or start_daemon):
            usage(__doc__)
            sys.exit(0)
    except Exception:
        warning("Error: Failed to parse command line arguments!")
        sys.exit(1)
    # Start the guest daemon.
    try:
        if not character_device:
            channel_name = HOST_TO_GUEST_CHANNEL_NAME if start_daemon else GUEST_TO_HOST_CHANNEL_NAME
            character_device = find_character_device(channel_name)
        ga = GuestAgent(character_device)
        if start_daemon:
            ga.enter_main_loop()
        elif list_commands:
            with TimeOut(timeout):
                print('\n'.join(ga.call_remote_method('list_commands')))
        elif execute_command:
            with TimeOut(timeout):
                timer = Timer()
                output = ga.call_remote_method('execute', *shlex.split(execute_command), capture=True)
                logger.debug("Took %s to execute remote command.", timer)
                print(output.rstrip())
    except Exception:
        logger.exception("Caught a fatal exception! Terminating ..")
        sys.exit(1)
def main():
    """Command line interface for ``debuntu-nodejs-installer``."""
    # Initialize logging to the terminal and system log.
    coloredlogs.install(syslog=True)
    silence_urllib_logger()
    # Parse the command line arguments.
    action = None
    context_opts = dict()
    installer_opts = dict()
    try:
        options, arguments = getopt.getopt(sys.argv[1:], 'iV:s:r:vqh', [
            'install',
            'version=',
            'sources-file=',
            'remote-host=',
            'verbose',
            'quiet',
            'help',
        ])
        for option, value in options:
            if option in ('-i', '--install'):
                action = 'install'
            elif option in ('-V', '--version'):
                installer_opts['nodejs_version'] = value
            elif option in ('-s', '--sources-file'):
                installer_opts['sources_file'] = value
            elif option in ('-r', '--remote-host'):
                context_opts['ssh_alias'] = value
            elif option in ('-v', '--verbose'):
                coloredlogs.increase_verbosity()
            elif option in ('-q', '--quiet'):
                coloredlogs.decrease_verbosity()
            elif option in ('-h', '--help'):
                usage(__doc__)
                sys.exit(0)
            else:
                raise Exception("Unhandled option!")
        if arguments:
            raise Exception(
                "This program doesn't accept any positional arguments!")
        if not action:
            usage(__doc__)
            sys.exit(0)
    except Exception as e:
        warning("Failed to parse command line arguments! (%s)", e)
        sys.exit(1)
    # Execute the requested action.
    context = create_context(**context_opts)
    try:
        installer = NodeInstaller(context=context, **installer_opts)
        getattr(installer, action)()
    except (UnsupportedSystemError, ExternalCommandFailed) as e:
        logger.error("%s", e)
        sys.exit(1)
    except Exception:
        logger.exception("Encountered unexpected exception on %s!", context)
        sys.exit(1)
Example #12
0
def main():
    """Command line interface for the ``humanfriendly`` program."""
    enable_ansi_support()
    try:
        options, arguments = getopt.getopt(sys.argv[1:], 'cd:l:n:s:bt:h', [
            'run-command',
            'format-table',
            'delimiter=',
            'format-length=',
            'format-number=',
            'format-size=',
            'binary',
            'format-timespan=',
            'parse-length=',
            'parse-size=',
            'demo',
            'help',
        ])
    except Exception as e:
        warning("Error: %s", e)
        sys.exit(1)
    actions = []
    delimiter = None
    should_format_table = False
    binary = any(o in ('-b', '--binary') for o, v in options)
    for option, value in options:
        if option in ('-d', '--delimiter'):
            delimiter = value
        elif option == '--parse-size':
            actions.append(functools.partial(print_parsed_size, value))
        elif option == '--parse-length':
            actions.append(functools.partial(print_parsed_length, value))
        elif option in ('-c', '--run-command'):
            actions.append(functools.partial(run_command, arguments))
        elif option in ('-l', '--format-length'):
            actions.append(functools.partial(print_formatted_length, value))
        elif option in ('-n', '--format-number'):
            actions.append(functools.partial(print_formatted_number, value))
        elif option in ('-s', '--format-size'):
            actions.append(
                functools.partial(print_formatted_size, value, binary))
        elif option == '--format-table':
            should_format_table = True
        elif option in ('-t', '--format-timespan'):
            actions.append(functools.partial(print_formatted_timespan, value))
        elif option == '--demo':
            actions.append(demonstrate_ansi_formatting)
        elif option in ('-h', '--help'):
            usage(__doc__)
            return
    if should_format_table:
        actions.append(functools.partial(print_formatted_table, delimiter))
    if not actions:
        usage(__doc__)
        return
    for partial in actions:
        partial()
Example #13
0
def main():
    """Command line interface for the ``qpass`` program."""
    # Initialize logging to the terminal.
    coloredlogs.install()
    # Prepare for command line argument parsing.
    action = show_matching_entry
    program_opts = dict()
    show_opts = dict(use_clipboard=is_clipboard_supported())
    # Parse the command line arguments.
    try:
        options, arguments = getopt.gnu_getopt(sys.argv[1:], 'elnp:vqh', [
            'edit',
            'list',
            'no-clipboard',
            'password-store=',
            'verbose',
            'quiet',
            'help',
        ])
        for option, value in options:
            if option in ('-e', '--edit'):
                action = edit_matching_entry
            elif option in ('-l', '--list'):
                action = list_matching_entries
            elif option in ('-n', '--no-clipboard'):
                show_opts['use_clipboard'] = False
            elif option in ('-p', '--password-store'):
                stores = program_opts.setdefault('stores', [])
                stores.append(PasswordStore(directory=value))
            elif option in ('-v', '--verbose'):
                coloredlogs.increase_verbosity()
            elif option in ('-q', '--quiet'):
                coloredlogs.decrease_verbosity()
            elif option in ('-h', '--help'):
                usage(__doc__)
                return
            else:
                raise Exception("Unhandled option! (programming error)")
        if not (arguments or action == list_matching_entries):
            usage(__doc__)
            return
    except Exception as e:
        warning("Error: %s", e)
        sys.exit(1)
    # Execute the requested action.
    try:
        action(QuickPass(**program_opts), arguments,
               **(show_opts if action == show_matching_entry else {}))
    except PasswordStoreError as e:
        # Known issues don't get a traceback.
        logger.error("%s", e)
        sys.exit(1)
    except KeyboardInterrupt:
        # If the user interrupted an interactive prompt they most likely did so
        # intentionally, so there's no point in generating more output here.
        sys.exit(1)
Example #14
0
def main():
    """Command line interface for the ``negotiator-host`` program."""
    # Initialize logging to the terminal and system log.
    coloredlogs.install(syslog=True)
    # Parse the command line arguments.
    actions = []
    context = Context()
    try:
        options, arguments = getopt.getopt(sys.argv[1:], 'gce:t:dvqh', [
            'list-guests', 'list-commands', 'execute=', 'timeout=', 'daemon',
            'verbose', 'quiet', 'help'
        ])
        for option, value in options:
            if option in ('-g', '--list-guests'):
                actions.append(context.print_guest_names)
            elif option in ('-c', '--list-commands'):
                assert len(arguments) == 1, \
                    "Please provide the name of a guest as the 1st and only positional argument!"
                actions.append(
                    functools.partial(context.print_commands, arguments[0]))
            elif option in ('-e', '--execute'):
                assert len(arguments) == 1, \
                    "Please provide the name of a guest as the 1st and only positional argument!"
                actions.append(
                    functools.partial(context.execute_command, arguments[0],
                                      value))
            elif option in ('-t', '--timeout'):
                context.timeout = int(value)
            elif option in ('-d', '--daemon'):
                actions.append(HostDaemon)
            elif option in ('-v', '--verbose'):
                coloredlogs.increase_verbosity()
            elif option in ('-q', '--quiet'):
                coloredlogs.decrease_verbosity()
            elif option in ('-h', '--help'):
                usage(__doc__)
                sys.exit(0)
        if not actions:
            usage(__doc__)
            sys.exit(0)
    except Exception:
        warning("Failed to parse command line arguments!")
        sys.exit(1)
    # Execute the requested action(s).
    try:
        for action in actions:
            action()
    except GuestDiscoveryError as e:
        # Don't spam the logs with tracebacks when the libvirt daemon is down.
        logger.error("%s", e)
        sys.exit(1)
    except Exception:
        # Do log a traceback for `unexpected' exceptions.
        logger.exception("Caught a fatal exception! Terminating ..")
        sys.exit(1)
Example #15
0
def main():
    """Command line interface for ``debuntu-kernel-manager``."""
    # Initialize logging to the terminal and system log.
    coloredlogs.install(syslog=True)
    # Parse the command line arguments.
    action = 'render_summary'
    context_opts = dict()
    manager_opts = dict()
    try:
        options, arguments = getopt.getopt(sys.argv[1:], 'cfp:r:vqh', [
            'clean',
            'remove',
            'force',
            'preserve-count=',
            'remote-host=',
            'verbose',
            'quiet',
            'help',
        ])
        for option, value in options:
            if option in ('-c', '--clean', '--remove'):
                action = 'cleanup_packages'
            elif option in ('-f', '--force'):
                manager_opts['force'] = True
            elif option in ('-p', '--preserve-count'):
                manager_opts['preserve_count'] = int(value)
            elif option in ('-r', '--remote-host'):
                context_opts['ssh_alias'] = value
            elif option in ('-v', '--verbose'):
                coloredlogs.increase_verbosity()
            elif option in ('-q', '--quiet'):
                coloredlogs.decrease_verbosity()
            elif option in ('-h', '--help'):
                usage(__doc__)
                return
            else:
                raise Exception("Unhandled option!")
        # Any positional arguments are passed to apt-get.
        manager_opts['apt_options'] = arguments
    except Exception as e:
        warning("Failed to parse command line arguments! (%s)", e)
        sys.exit(1)
    # Execute the requested action(s).
    context = create_context(**context_opts)
    try:
        manager = KernelPackageManager(context=context, **manager_opts)
        getattr(manager, action)()
    except (CleanupError, ExternalCommandFailed) as e:
        logger.error("%s", e)
        sys.exit(1)
    except Exception:
        logger.exception("Encountered unexpected exception on %s!", context)
        sys.exit(1)
Example #16
0
def main():
    """Command line interface for the ``update-dotdee`` program."""
    # Initialize logging to the terminal and system log.
    coloredlogs.install(syslog=True)
    # Parse the command line arguments.
    context_opts = {}
    program_opts = {}
    try:
        options, arguments = getopt.getopt(sys.argv[1:], 'fur:vqh', [
            'force',
            'use-sudo',
            'remote-host=',
            'verbose',
            'quiet',
            'help',
        ])
        for option, value in options:
            if option in ('-f', '--force'):
                program_opts['force'] = True
            elif option in ('-u', '--use-sudo'):
                context_opts['sudo'] = True
            elif option in ('-r', '--remote-host'):
                context_opts['ssh_alias'] = value
            elif option in ('-v', '--verbose'):
                coloredlogs.increase_verbosity()
            elif option in ('-q', '--quiet'):
                coloredlogs.decrease_verbosity()
            elif option in ('-h', '--help'):
                usage(__doc__)
                sys.exit(0)
            else:
                # Programming error...
                assert False, "Unhandled option!"
        if not arguments:
            usage(__doc__)
            sys.exit(0)
        if len(arguments) != 1:
            raise Exception(
                "Expected a filename as the first and only argument!")
        program_opts['filename'] = arguments[0]
    except Exception as e:
        warning("Error: %s", e)
        sys.exit(1)
    # Run the program.
    try:
        # Initialize the execution context.
        program_opts['context'] = create_context(**context_opts)
        # Initialize the program and update the file.
        UpdateDotDee(**program_opts).update_file()
    except Exception as e:
        logger.exception("Encountered unexpected exception, aborting!")
        sys.exit(1)
Example #17
0
def main():
    """Command line interface for the ``negotiator-host`` program."""
    # Initialize logging to the terminal and system log.
    coloredlogs.install(syslog=True)
    # Parse the command line arguments.
    actions = []
    context = Context()
    try:
        options, arguments = getopt.getopt(sys.argv[1:], 'gce:t:dvqh', [
            'list-guests', 'list-commands', 'execute=', 'timeout=', 'daemon',
            'verbose', 'quiet', 'help'
        ])
        for option, value in options:
            if option in ('-g', '--list-guests'):
                actions.append(context.print_guest_names)
            elif option in ('-c', '--list-commands'):
                assert len(arguments) == 1, \
                    "Please provide the name of a guest as the 1st and only positional argument!"
                actions.append(functools.partial(context.print_commands, arguments[0]))
            elif option in ('-e', '--execute'):
                assert len(arguments) == 1, \
                    "Please provide the name of a guest as the 1st and only positional argument!"
                actions.append(functools.partial(context.execute_command, arguments[0], value))
            elif option in ('-t', '--timeout'):
                context.timeout = int(value)
            elif option in ('-d', '--daemon'):
                actions.append(HostDaemon)
            elif option in ('-v', '--verbose'):
                coloredlogs.increase_verbosity()
            elif option in ('-q', '--quiet'):
                coloredlogs.decrease_verbosity()
            elif option in ('-h', '--help'):
                usage(__doc__)
                sys.exit(0)
        if not actions:
            usage(__doc__)
            sys.exit(0)
    except Exception:
        warning("Failed to parse command line arguments!")
        sys.exit(1)
    # Execute the requested action(s).
    try:
        for action in actions:
            action()
    except GuestDiscoveryError as e:
        # Don't spam the logs with tracebacks when the libvirt daemon is down.
        logger.error("%s", e)
        sys.exit(1)
    except Exception:
        # Do log a traceback for `unexpected' exceptions.
        logger.exception("Caught a fatal exception! Terminating ..")
        sys.exit(1)
Example #18
0
def main():
    """Command line interface for ``debuntu-kernel-manager``."""
    # Initialize logging to the terminal and system log.
    coloredlogs.install(syslog=True)
    # Parse the command line arguments.
    action = "render_summary"
    context_opts = dict()
    manager_opts = dict()
    try:
        options, arguments = getopt.getopt(
            sys.argv[1:],
            "cfp:r:vqh",
            [
                "clean", "remove", "force", "preserve-count=", "remote-host=",
                "verbose", "quiet", "help"
            ],
        )
        for option, value in options:
            if option in ("-c", "--clean", "--remove"):
                action = "cleanup_packages"
            elif option in ("-f", "--force"):
                manager_opts["force"] = True
            elif option in ("-p", "--preserve-count"):
                manager_opts["preserve_count"] = int(value)
            elif option in ("-r", "--remote-host"):
                context_opts["ssh_alias"] = value
            elif option in ("-v", "--verbose"):
                coloredlogs.increase_verbosity()
            elif option in ("-q", "--quiet"):
                coloredlogs.decrease_verbosity()
            elif option in ("-h", "--help"):
                usage(__doc__)
                return
            else:
                raise Exception("Unhandled option!")
        # Any positional arguments are passed to apt-get.
        manager_opts["apt_options"] = arguments
    except Exception as e:
        warning("Failed to parse command line arguments! (%s)", e)
        sys.exit(1)
    # Execute the requested action(s).
    context = create_context(**context_opts)
    try:
        manager = KernelPackageManager(context=context, **manager_opts)
        getattr(manager, action)()
    except (CleanupError, ExternalCommandFailed) as e:
        logger.error("%s", e)
        sys.exit(1)
    except Exception:
        logger.exception("Encountered unexpected exception on %s!", context)
        sys.exit(1)
Example #19
0
def main():
    """Command line interface for the ``humanfriendly`` program."""
    try:
        options, arguments = getopt.getopt(sys.argv[1:], 'cd:l:n:s:bt:h', [
            'run-command', 'format-table', 'delimiter=', 'format-length=',
            'format-number=', 'format-size=', 'binary', 'format-timespan=',
            'parse-length=', 'parse-size=', 'demo', 'help',
        ])
    except Exception as e:
        warning("Error: %s", e)
        sys.exit(1)
    actions = []
    delimiter = None
    should_format_table = False
    binary = any(o in ('-b', '--binary') for o, v in options)
    for option, value in options:
        if option in ('-d', '--delimiter'):
            delimiter = value
        elif option == '--parse-size':
            actions.append(functools.partial(print_parsed_size, value))
        elif option == '--parse-length':
            actions.append(functools.partial(print_parsed_length, value))
        elif option in ('-c', '--run-command'):
            actions.append(functools.partial(run_command, arguments))
        elif option in ('-l', '--format-length'):
            actions.append(functools.partial(print_formatted_length, value))
        elif option in ('-n', '--format-number'):
            actions.append(functools.partial(print_formatted_number, value))
        elif option in ('-s', '--format-size'):
            actions.append(functools.partial(print_formatted_size, value, binary))
        elif option == '--format-table':
            should_format_table = True
        elif option in ('-t', '--format-timespan'):
            actions.append(functools.partial(print_formatted_timespan, value))
        elif option == '--demo':
            actions.append(demonstrate_ansi_formatting)
        elif option in ('-h', '--help'):
            usage(__doc__)
            return
    if should_format_table:
        actions.append(functools.partial(print_formatted_table, delimiter))
    if not actions:
        usage(__doc__)
        return
    for partial in actions:
        partial()
def main():
    """Command line interface for ``reboot-remote-system``."""
    # Initialize logging to the terminal and system log.
    coloredlogs.install(syslog=True)
    # Parse the command line arguments.
    do_shell = False
    try:
        options, arguments = getopt.gnu_getopt(sys.argv[1:], 'svqh', [
            'shell',
            'verbose',
            'quiet',
            'help',
        ])
        for option, value in options:
            if option in ('-s', '--shell'):
                do_shell = True
            elif option in ('-v', '--verbose'):
                coloredlogs.increase_verbosity()
            elif option in ('-q', '--quiet'):
                coloredlogs.decrease_verbosity()
            elif option in ('-h', '--help'):
                usage(__doc__)
                sys.exit(0)
            else:
                raise Exception("Unhandled option!")
        if not arguments:
            usage(__doc__)
            sys.exit(0)
        elif len(arguments) > 1:
            raise Exception("only one positional argument allowed")
    except Exception as e:
        warning("Failed to parse command line arguments! (%s)", e)
        sys.exit(1)
    # Reboot the remote system.
    try:
        account = RemoteAccount(arguments[0])
        context = get_post_context(arguments[0])
        reboot_remote_system(context=context, name=account.ssh_alias)
        if do_shell:
            start_interactive_shell(context)
    except EncryptedSystemError as e:
        logger.error("Aborting due to error: %s", e)
        sys.exit(2)
    except Exception:
        logger.exception("Aborting due to unexpected exception!")
        sys.exit(3)
Example #21
0
def prompt_for_confirmation(question, default=None, padding=True):
    """
    Prompt the user for confirmation.

    :param question: The text that explains what the user is confirming (a string).
    :param default: The default value (a boolean) or :data:`None`.
    :param padding: Refer to the documentation of :func:`prompt_for_input()`.
    :returns: - If the user enters 'yes' or 'y' then :data:`True` is returned.
              - If the user enters 'no' or 'n' then :data:`False`  is returned.
              - If the user doesn't enter any text or standard input is not
                connected to a terminal (which makes it impossible to prompt
                the user) the value of the keyword argument ``default`` is
                returned (if that value is not :data:`None`).
    :raises: - Any exceptions raised by :func:`retry_limit()`.
             - Any exceptions raised by :func:`prompt_for_input()`.

    When `default` is :data:`False` and the user doesn't enter any text an
    error message is printed and the prompt is repeated:

    >>> prompt_for_confirmation("Are you sure?")
     <BLANKLINE>
     Are you sure? [y/n]
     <BLANKLINE>
     Error: Please enter 'yes' or 'no' (there's no default choice).
     <BLANKLINE>
     Are you sure? [y/n]

    The same thing happens when the user enters text that isn't recognized:

    >>> prompt_for_confirmation("Are you sure?")
     <BLANKLINE>
     Are you sure? [y/n] about what?
     <BLANKLINE>
     Error: Please enter 'yes' or 'no' (the text 'about what?' is not recognized).
     <BLANKLINE>
     Are you sure? [y/n]
    """
    # Generate the text for the prompt.
    prompt_text = prepare_prompt_text(question, bold=True)
    # Append the valid replies (and default reply) to the prompt text.
    hint = "[Y/n]" if default else "[y/N]" if default is not None else "[y/n]"
    prompt_text += " %s " % prepare_prompt_text(hint, color=HIGHLIGHT_COLOR)
    # Loop until a valid response is given.
    logger.debug("Requesting interactive confirmation from terminal: %r", ansi_strip(prompt_text).rstrip())
    for attempt in retry_limit():
        reply = prompt_for_input(prompt_text, '', padding=padding, strip=True)
        if reply.lower() in ('y', 'yes'):
            logger.debug("Confirmation granted by reply (%r).", reply)
            return True
        elif reply.lower() in ('n', 'no'):
            logger.debug("Confirmation denied by reply (%r).", reply)
            return False
        elif (not reply) and default is not None:
            logger.debug("Default choice selected by empty reply (%r).",
                         "granted" if default else "denied")
            return default
        else:
            details = ("the text '%s' is not recognized" % reply
                       if reply else "there's no default choice")
            logger.debug("Got %s reply (%s), retrying (%i/%i) ..",
                         "invalid" if reply else "empty", details,
                         attempt, MAX_ATTEMPTS)
            warning("{indent}Error: Please enter 'yes' or 'no' ({details}).",
                    indent=' ' if padding else '', details=details)
Example #22
0
def prompt_for_choice(choices, default=None, padding=True):
    """
    Prompt the user to select a choice from a group of options.

    :param choices: A sequence of strings with available options.
    :param default: The default choice if the user simply presses Enter
                    (expected to be a string, defaults to :data:`None`).
    :param padding: Refer to the documentation of :func:`prompt_for_input()`.
    :returns: The string corresponding to the user's choice.
    :raises: - :exc:`~exceptions.ValueError` if `choices` is an empty sequence.
             - Any exceptions raised by :func:`retry_limit()`.
             - Any exceptions raised by :func:`prompt_for_input()`.

    When no options are given an exception is raised:

    >>> prompt_for_choice([])
    Traceback (most recent call last):
      File "humanfriendly/prompts.py", line 148, in prompt_for_choice
        raise ValueError("Can't prompt for choice without any options!")
    ValueError: Can't prompt for choice without any options!

    If a single option is given the user isn't prompted:

    >>> prompt_for_choice(['only one choice'])
    'only one choice'

    Here's what the actual prompt looks like by default:

    >>> prompt_for_choice(['first option', 'second option'])
     <BLANKLINE>
      1. first option
      2. second option
     <BLANKLINE>
     Enter your choice as a number or unique substring (Control-C aborts): second
     <BLANKLINE>
    'second option'

    If you don't like the whitespace (empty lines and indentation):

    >>> prompt_for_choice(['first option', 'second option'], padding=False)
     1. first option
     2. second option
    Enter your choice as a number or unique substring (Control-C aborts): first
    'first option'
    """
    indent = ' ' if padding else ''
    # Make sure we can use 'choices' more than once (i.e. not a generator).
    choices = list(choices)
    if len(choices) == 1:
        # If there's only one option there's no point in prompting the user.
        logger.debug("Skipping interactive prompt because there's only option (%r).", choices[0])
        return choices[0]
    elif not choices:
        # We can't render a choice prompt without any options.
        raise ValueError("Can't prompt for choice without any options!")
    # Generate the prompt text.
    prompt_text = ('\n\n' if padding else '\n').join([
        # Present the available choices in a user friendly way.
        "\n".join([
            (u" %i. %s" % (i, choice)) + (" (default choice)" if choice == default else "")
            for i, choice in enumerate(choices, start=1)
        ]),
        # Instructions for the user.
        "Enter your choice as a number or unique substring (Control-C aborts): ",
    ])
    prompt_text = prepare_prompt_text(prompt_text, bold=True)
    # Loop until a valid choice is made.
    logger.debug("Requesting interactive choice on terminal (options are %s) ..",
                 concatenate(map(repr, choices)))
    for attempt in retry_limit():
        reply = prompt_for_input(prompt_text, '', padding=padding, strip=True)
        if not reply and default is not None:
            logger.debug("Default choice selected by empty reply (%r).", default)
            return default
        elif reply.isdigit():
            index = int(reply) - 1
            if 0 <= index < len(choices):
                logger.debug("Option (%r) selected by numeric reply (%s).", choices[index], reply)
                return choices[index]
        # Check for substring matches.
        matches = []
        for choice in choices:
            lower_reply = reply.lower()
            lower_choice = choice.lower()
            if lower_reply == lower_choice:
                # If we have an 'exact' match we return it immediately.
                logger.debug("Option (%r) selected by reply (exact match).", choice)
                return choice
            elif lower_reply in lower_choice and len(lower_reply) > 0:
                # Otherwise we gather substring matches.
                matches.append(choice)
        if len(matches) == 1:
            # If a single choice was matched we return it.
            logger.debug("Option (%r) selected by reply (substring match on %r).", matches[0], reply)
            return matches[0]
        else:
            # Give the user a hint about what went wrong.
            if matches:
                details = format("text '%s' matches more than one choice: %s", reply, concatenate(matches))
            elif reply.isdigit():
                details = format("number %i is not a valid choice", int(reply))
            elif reply and not reply.isspace():
                details = format("text '%s' doesn't match any choices", reply)
            else:
                details = "there's no default choice"
            logger.debug("Got %s reply (%s), retrying (%i/%i) ..",
                         "invalid" if reply else "empty", details,
                         attempt, MAX_ATTEMPTS)
            warning("%sError: Invalid input (%s).", indent, details)
Example #23
0
def main():
    """Command line interface for the ``py2deb`` program."""
    # Configure terminal output.
    coloredlogs.install()
    try:
        # Initialize a package converter.
        converter = PackageConverter()
        # Parse and validate the command line options.
        options, arguments = getopt.getopt(sys.argv[1:], 'c:r:yvh', [
            'config=', 'repository=', 'use-system-package=', 'name-prefix=',
            'no-name-prefix=', 'rename=', 'install-prefix=',
            'install-alternative=', 'python-callback=', 'report-dependencies=',
            'yes', 'verbose', 'help',
        ])
        control_file_to_update = None
        for option, value in options:
            if option in ('-c', '--config'):
                converter.load_configuration_file(value)
            elif option in ('-r', '--repository'):
                converter.set_repository(value)
            elif option == '--use-system-package':
                python_package_name, _, debian_package_name = value.partition(',')
                converter.use_system_package(python_package_name, debian_package_name)
            elif option == '--name-prefix':
                converter.set_name_prefix(value)
            elif option == '--no-name-prefix':
                converter.rename_package(value, value)
            elif option == '--rename':
                python_package_name, _, debian_package_name = value.partition(',')
                converter.rename_package(python_package_name, debian_package_name)
            elif option == '--install-prefix':
                converter.set_install_prefix(value)
            elif option == '--install-alternative':
                link, _, path = value.partition(',')
                converter.install_alternative(link, path)
            elif option == '--python-callback':
                converter.set_python_callback(value)
            elif option == '--report-dependencies':
                control_file_to_update = value
                if not os.path.isfile(control_file_to_update):
                    msg = "The given control file doesn't exist! (%s)"
                    raise Exception(msg % control_file_to_update)
            elif option in ('-y', '--yes'):
                converter.set_auto_install(True)
            elif option in ('-v', '--verbose'):
                coloredlogs.increase_verbosity()
            elif option in ('-h', '--help'):
                usage(__doc__)
                return
            else:
                assert False, "Unhandled option!"
    except Exception as e:
        warning("Failed to parse command line arguments: %s", e)
        sys.exit(1)
    # Convert the requested package(s).
    try:
        if arguments:
            archives, relationships = converter.convert(arguments)
            if relationships and control_file_to_update:
                patch_control_file(control_file_to_update, dict(depends=relationships))
        else:
            usage(__doc__)
    except Exception:
        logger.exception("Caught an unhandled exception!")
        sys.exit(1)
Example #24
0
def main():
    """Command line interface for the ``negotiator-guest`` program."""
    # Initialize logging to the terminal and system log.
    coloredlogs.install(syslog=True)
    # Parse the command line arguments.
    list_commands = False
    execute_command = None
    start_daemon = False
    timeout = DEFAULT_TIMEOUT
    character_device = None
    try:
        options, arguments = getopt.getopt(sys.argv[1:], 'le:dt:c:vqh', [
            'list-commands', 'execute=', 'daemon', 'timeout=',
            'character-device=', 'verbose', 'quiet', 'help'
        ])
        for option, value in options:
            if option in ('-l', '--list-commands'):
                list_commands = True
            elif option in ('-e', '--execute'):
                execute_command = value
            elif option in ('-d', '--daemon'):
                start_daemon = True
            elif option in ('-t', '--timeout'):
                timeout = int(value)
            elif option in ('-c', '--character-device'):
                character_device = value
            elif option in ('-v', '--verbose'):
                coloredlogs.increase_verbosity()
            elif option in ('-q', '--quiet'):
                coloredlogs.decrease_verbosity()
            elif option in ('-h', '--help'):
                usage(__doc__)
                sys.exit(0)
        if not (list_commands or execute_command or start_daemon):
            usage(__doc__)
            sys.exit(0)
    except Exception:
        warning("Error: Failed to parse command line arguments!")
        sys.exit(1)
    # Start the guest daemon.
    try:
        if not character_device:
            channel_name = HOST_TO_GUEST_CHANNEL_NAME if start_daemon else GUEST_TO_HOST_CHANNEL_NAME
            character_device = find_character_device(channel_name)
        if start_daemon:
            agent = GuestAgent(character_device=character_device, retry=False)
            agent.enter_main_loop()
        elif list_commands:
            with TimeOut(timeout):
                agent = GuestAgent(character_device, retry=True)
                print('\n'.join(agent.call_remote_method('list_commands')))
        elif execute_command:
            with TimeOut(timeout):
                timer = Timer()
                agent = GuestAgent(character_device, retry=True)
                output = agent.call_remote_method(
                    'execute', *shlex.split(execute_command), capture=True)
                logger.debug("Took %s to execute remote command.", timer)
                print(output.rstrip())
    except Exception:
        logger.exception("Caught a fatal exception! Terminating ..")
        sys.exit(1)
def main():
    """Command line interface for the ``apt-mirror-updater`` program."""
    # Initialize logging to the terminal and system log.
    coloredlogs.install(syslog=True)
    # Command line option defaults.
    context = LocalContext()
    updater = AptMirrorUpdater(context=context)
    limit = MAX_MIRRORS
    actions = []
    # Parse the command line arguments.
    try:
        options, arguments = getopt.getopt(sys.argv[1:], 'r:fblc:aux:m:vqh', [
            'remote-host=',
            'find-current-mirror',
            'find-best-mirror',
            'list-mirrors',
            'change-mirror',
            'auto-change-mirror',
            'update',
            'update-package-lists',
            'exclude=',
            'max=',
            'verbose',
            'quiet',
            'help',
        ])
        for option, value in options:
            if option in ('-r', '--remote-host'):
                if actions:
                    msg = "The %s option should be the first option given on the command line!"
                    raise Exception(msg % option)
                context = RemoteContext(value)
                updater = AptMirrorUpdater(context=context)
            elif option in ('-f', '--find-current-mirror'):
                actions.append(
                    functools.partial(report_current_mirror, updater))
            elif option in ('-b', '--find-best-mirror'):
                actions.append(functools.partial(report_best_mirror, updater))
            elif option in ('-l', '--list-mirrors'):
                actions.append(
                    functools.partial(report_available_mirrors, updater))
            elif option in ('-c', '--change-mirror'):
                actions.append(functools.partial(updater.change_mirror, value))
            elif option in ('-a', '--auto-change-mirror'):
                actions.append(updater.change_mirror)
            elif option in ('-u', '--update', '--update-package-lists'):
                actions.append(updater.smart_update)
            elif option in ('-x', '--exclude'):
                actions.insert(0,
                               functools.partial(updater.ignore_mirror, value))
            elif option in ('-m', '--max'):
                limit = int(value)
            elif option in ('-v', '--verbose'):
                coloredlogs.increase_verbosity()
            elif option in ('-q', '--quiet'):
                coloredlogs.decrease_verbosity()
            elif option in ('-h', '--help'):
                usage(__doc__)
                return
            else:
                assert False, "Unhandled option!"
        if not actions:
            usage(__doc__)
            return
        # Propagate options to the Python API.
        updater.max_mirrors = limit
    except Exception as e:
        warning("Error: Failed to parse command line arguments! (%s)" % e)
        sys.exit(1)
    # Perform the requested action(s).
    try:
        for callback in actions:
            callback()
    except Exception:
        logger.exception("Encountered unexpected exception! Aborting ..")
        sys.exit(1)
Example #26
0
def main():
    """Command line interface for the ``crypto-drive-manager`` program."""
    # Initialize logging to the terminal and system log.
    coloredlogs.install(syslog=True)
    # Define command line option defaults.
    image_file = '/root/encryption-keys.img'
    mapper_name = 'encryption-keys'
    mount_point = '/mnt/keys'
    install_workaround = False
    # Parse the command line arguments.
    try:
        options, arguments = getopt.getopt(sys.argv[1:], 'i:n:m:vqh', [
            'image-file=', 'mapper-name=', 'mount-point=',
            'install-systemd-workaround',
            'verbose', 'quiet', 'help',
        ])
        for option, value in options:
            if option in ('-i', '--image-file'):
                image_file = value
            elif option in ('-n', '--mapper-name'):
                mapper_name = value
            elif option in ('-m', '--mount-point'):
                mount_point = value
            elif option == '--install-systemd-workaround':
                install_workaround = True
            elif option in ('-v', '--verbose'):
                coloredlogs.increase_verbosity()
            elif option in ('-q', '--quiet'):
                coloredlogs.decrease_verbosity()
            elif option in ('-h', '--help'):
                usage(__doc__)
                return
            else:
                assert False, "Unhandled option!"
    except Exception as e:
        warning("Error: Failed to parse command line arguments! (%s)", e)
        sys.exit(1)
    # Make sure we're running as root (after parsing the command
    # line so that root isn't required to list the usage message).
    if os.getuid() != 0:
        warning("Error: Please run this command as root!")
        sys.exit(1)
    # Decide if the systemd workaround is being executed based on sys.argv[0].
    if systemd_workaround_requested():
        update_systemd_services()
        # In this case none of the other code should run.
        return
    # Check if the operator wants to install the systemd workaround.
    if install_workaround:
        install_systemd_workaround()
    # Initialize the keys device and use it to unlock the managed drives?
    # Only if we didn't just install the systemd workaround OR the operator
    # requested to unlock specific drives.
    if (not install_workaround) or arguments:
        try:
            initialize_keys_device(
                image_file=image_file,
                mapper_name=mapper_name,
                mount_point=mount_point,
                volumes=arguments,
            )
        except KeyboardInterrupt:
            logger.error("Interrupted by Control-C, terminating ..")
            sys.exit(1)
        except Exception:
            logger.exception("Terminating due to unexpected exception!")
            sys.exit(1)
Example #27
0
def main():
    """Command line interface for the ``apt-smart`` program."""
    # Initialize logging to the terminal and system log.
    coloredlogs.install(syslog=True)
    # Command line option defaults.
    context = LocalContext()
    updater = AptMirrorUpdater(context=context)
    limit = MAX_MIRRORS
    url_char_len = URL_CHAR_LEN
    actions = []
    # Parse the command line arguments.
    try:
        options, arguments = getopt.getopt(sys.argv[1:],
                                           'r:fF:blL:c:aux:m:vVR:qh', [
                                               'remote-host=',
                                               'find-current-mirror',
                                               'find-best-mirror',
                                               'file-to-read=',
                                               'list-mirrors',
                                               'url-char-len=',
                                               'change-mirror',
                                               'auto-change-mirror',
                                               'update',
                                               'update-package-lists',
                                               'exclude=',
                                               'max=',
                                               'verbose',
                                               'version',
                                               'create-chroot=',
                                               'quiet',
                                               'help',
                                           ])
        for option, value in options:
            if option in ('-r', '--remote-host'):
                if actions:
                    msg = "The %s option should be the first option given on the command line!"
                    raise Exception(msg % option)
                context = RemoteContext(value)
                updater = AptMirrorUpdater(context=context)
            elif option in ('-f', '--find-current-mirror'):
                actions.append(
                    functools.partial(report_current_mirror, updater))
            elif option in ('-F', '--file-to-read'):
                updater.custom_mirror_file_path = value
            elif option in ('-b', '--find-best-mirror'):
                actions.append(functools.partial(report_best_mirror, updater))
            elif option in ('-l', '--list-mirrors'):
                actions.append(
                    functools.partial(report_available_mirrors, updater))
            elif option in ('-L', '--url-char-len'):
                url_char_len = int(value)
            elif option in ('-c', '--change-mirror'):
                if value.strip().startswith(('http://', 'https://', 'ftp://')):
                    actions.append(
                        functools.partial(updater.change_mirror, value))
                else:
                    raise Exception("\'%s\' is not a valid mirror URL" % value)
            elif option in ('-a', '--auto-change-mirror'):
                actions.append(updater.change_mirror)
            elif option in ('-u', '--update', '--update-package-lists'):
                actions.append(updater.smart_update)
            elif option in ('-x', '--exclude'):
                actions.insert(0,
                               functools.partial(updater.ignore_mirror, value))
            elif option in ('-m', '--max'):
                limit = int(value)
            elif option in ('-v', '--verbose'):
                coloredlogs.increase_verbosity()
            elif option in ('-V', '--version'):
                output("Version: %s on Python %i.%i", updater_version,
                       sys.version_info[0], sys.version_info[1])
                return
            elif option in ('-R', '--create-chroot'):
                actions.append(functools.partial(updater.create_chroot, value))
            elif option in ('-q', '--quiet'):
                coloredlogs.decrease_verbosity()
            elif option in ('-h', '--help'):
                usage(__doc__)
                return
            else:
                assert False, "Unhandled option!"
        if not actions:
            usage(__doc__)
            return
        # Propagate options to the Python API.
        updater.max_mirrors = limit
        updater.url_char_len = url_char_len
    except Exception as e:
        warning("Error: Failed to parse command line arguments! (%s)" % e)
        sys.exit(1)
    # Perform the requested action(s).
    try:
        for callback in actions:
            callback()
    except Exception:
        logger.exception("Encountered unexpected exception! Aborting ..")
        sys.exit(1)
Example #28
0
def prompt_for_choice(choices, default=None, padding=True):
    """
    Prompt the user to select a choice from a group of options.

    :param choices: A sequence of strings with available options.
    :param default: The default choice if the user simply presses Enter
                    (expected to be a string, defaults to :data:`None`).
    :param padding: Refer to the documentation of :func:`prompt_for_input()`.
    :returns: The string corresponding to the user's choice.
    :raises: - :exc:`~exceptions.ValueError` if `choices` is an empty sequence.
             - Any exceptions raised by :func:`retry_limit()`.
             - Any exceptions raised by :func:`prompt_for_input()`.

    When no options are given an exception is raised:

    >>> prompt_for_choice([])
    Traceback (most recent call last):
      File "humanfriendly/prompts.py", line 148, in prompt_for_choice
        raise ValueError("Can't prompt for choice without any options!")
    ValueError: Can't prompt for choice without any options!

    If a single option is given the user isn't prompted:

    >>> prompt_for_choice(['only one choice'])
    'only one choice'

    Here's what the actual prompt looks like by default:

    >>> prompt_for_choice(['first option', 'second option'])
     <BLANKLINE>
      1. first option
      2. second option
     <BLANKLINE>
     Enter your choice as a number or unique substring (Control-C aborts): second
     <BLANKLINE>
    'second option'

    If you don't like the whitespace (empty lines and indentation):

    >>> prompt_for_choice(['first option', 'second option'], padding=False)
     1. first option
     2. second option
    Enter your choice as a number or unique substring (Control-C aborts): first
    'first option'
    """
    indent = ' ' if padding else ''
    # Make sure we can use 'choices' more than once (i.e. not a generator).
    choices = list(choices)
    if len(choices) == 1:
        # If there's only one option there's no point in prompting the user.
        logger.debug("Skipping interactive prompt because there's only option (%r).", choices[0])
        return choices[0]
    elif not choices:
        # We can't render a choice prompt without any options.
        raise ValueError("Can't prompt for choice without any options!")
    # Generate the prompt text.
    prompt_text = ('\n\n' if padding else '\n').join([
        # Present the available choices in a user friendly way.
        "\n".join([
            (u" %i. %s" % (i, choice)) + (" (default choice)" if choice == default else "")
            for i, choice in enumerate(choices, start=1)
        ]),
        # Instructions for the user.
        "Enter your choice as a number or unique substring (Control-C aborts): ",
    ])
    if terminal_supports_colors():
        prompt_text = ansi_wrap(prompt_text, bold=True, readline_hints=True)
    # Loop until a valid choice is made.
    logger.debug("Requesting interactive choice on terminal (options are %s) ..",
                 concatenate(map(repr, choices)))
    for attempt in retry_limit():
        reply = prompt_for_input(prompt_text, '', padding=padding, strip=True)
        if not reply and default is not None:
            logger.debug("Default choice selected by empty reply (%r).", default)
            return default
        elif reply.isdigit():
            index = int(reply) - 1
            if 0 <= index < len(choices):
                logger.debug("Option (%r) selected by numeric reply (%s).", choices[index], reply)
                return choices[index]
        # Check for substring matches.
        matches = []
        for choice in choices:
            lower_reply = reply.lower()
            lower_choice = choice.lower()
            if lower_reply == lower_choice:
                # If we have an 'exact' match we return it immediately.
                logger.debug("Option (%r) selected by reply (exact match).", choice, reply)
                return choice
            elif lower_reply in lower_choice and len(lower_reply) > 0:
                # Otherwise we gather substring matches.
                matches.append(choice)
        if len(matches) == 1:
            # If a single choice was matched we return it.
            logger.debug("Option (%r) selected by reply (substring match on %r).", matches[0], reply)
            return matches[0]
        else:
            # Give the user a hint about what went wrong.
            if matches:
                details = format("text '%s' matches more than one choice: %s", reply, concatenate(matches))
            elif reply.isdigit():
                details = format("number %i is not a valid choice", int(reply))
            elif reply and not reply.isspace():
                details = format("text '%s' doesn't match any choices", reply)
            else:
                details = "there's no default choice"
            logger.debug("Got %s reply (%s), retrying (%i/%i) ..",
                         "invalid" if reply else "empty", details,
                         attempt, MAX_ATTEMPTS)
            warning("%sError: Invalid input (%s).", indent, details)
Example #29
0
def main():
    """The command line interface."""
    # Initialize logging to the terminal and system log.
    coloredlogs.install(syslog=True)
    # Parse the command line options.
    try:
        options, arguments = getopt.gnu_getopt(
            sys.argv[1:],
            "b:m:Wc:B:l:nvqh",
            [
                "block-size=",
                "hash-method=",
                "whole-file",
                "concurrency=",
                "benchmark=",
                "listen=",
                "dry-run",
                "verbose",
                "quiet",
                "help",
            ],
        )
    except Exception as e:
        warning("Error: %s", e)
        sys.exit(1)
    # Command line option defaults.
    client_opts = {}
    server_opts = {}
    # Map parsed options to variables.
    for option, value in options:
        if option in ("-b", "--block-size"):
            client_opts["block_size"] = parse_size(value)
        elif option in ("-m", "--hash-method"):
            client_opts["hash_method"] = value
        elif option in ("-W", "--whole-file"):
            client_opts["delta_transfer"] = False
        elif option in ("-c", "--concurrency"):
            client_opts["concurrency"] = int(value)
            server_opts["concurrency"] = int(value)
        elif option in ("-B", "--benchmark"):
            client_opts["benchmark"] = int(value)
        elif option in ("-l", "--listen"):
            server_opts["address"] = value
        elif option in ("-n", "--dry-run"):
            client_opts["dry_run"] = True
        elif option in ("-v", "--verbose"):
            coloredlogs.increase_verbosity()
        elif option in ("-q", "--quiet"):
            coloredlogs.decrease_verbosity()
        elif option in ("-h", "--help"):
            usage(__doc__)
            sys.exit(0)
    # Execute the requested action.
    try:
        if arguments:
            if len(arguments) != 2:
                warning("Error: Two positional arguments expected!")
                sys.exit(1)
            client_opts['source'] = arguments[0]
            client_opts['target'] = arguments[1]
            run_client(**client_opts)
        else:
            run_server(**server_opts)
    except Exception:
        logger.exception("Program terminating due to exception!")
        sys.exit(1)
Example #30
0
def main():
    """Command line interface for the ``rsync-system-backup`` program."""
    # Initialize logging to the terminal and system log.
    coloredlogs.install(syslog=True)
    # Parse the command line arguments.
    context_opts = dict()
    program_opts = dict()
    dest_opts = dict()
    try:
        options, arguments = getopt.gnu_getopt(sys.argv[1:], 'bsrm:c:t:i:unx:fvqh', [
            'backup', 'snapshot', 'rotate', 'mount=', 'crypto=', 'tunnel=',
            'ionice=', 'no-sudo', 'dry-run', 'multi-fs', 'exclude=', 'force',
            'disable-notifications', 'verbose', 'quiet', 'help',
        ])
        for option, value in options:
            if option in ('-b', '--backup'):
                enable_explicit_action(program_opts, 'backup_enabled')
            elif option in ('-s', '--snapshot'):
                enable_explicit_action(program_opts, 'snapshot_enabled')
            elif option in ('-r', '--rotate'):
                enable_explicit_action(program_opts, 'rotate_enabled')
            elif option in ('-m', '--mount'):
                program_opts['mount_point'] = value
            elif option in ('-c', '--crypto'):
                program_opts['crypto_device'] = value
            elif option in ('-t', '--tunnel'):
                ssh_user, _, value = value.rpartition('@')
                ssh_alias, _, port_number = value.partition(':')
                tunnel_opts = dict(
                    ssh_alias=ssh_alias,
                    ssh_user=ssh_user,
                    # The port number of the rsync daemon.
                    remote_port=RSYNCD_PORT,
                )
                if port_number:
                    # The port number of the SSH server.
                    tunnel_opts['port'] = int(port_number)
                dest_opts['ssh_tunnel'] = SecureTunnel(**tunnel_opts)
            elif option in ('-i', '--ionice'):
                value = value.lower().strip()
                validate_ionice_class(value)
                program_opts['ionice'] = value
            elif option in ('-u', '--no-sudo'):
                program_opts['sudo_enabled'] = False
            elif option in ('-n', '--dry-run'):
                logger.info("Performing a dry run (because of %s option) ..", option)
                program_opts['dry_run'] = True
            elif option in ('-f', '--force'):
                program_opts['force'] = True
            elif option in ('-x', '--exclude'):
                program_opts.setdefault('exclude_list', [])
                program_opts['exclude_list'].append(value)
            elif option == '--multi-fs':
                program_opts['multi_fs'] = True
            elif option == '--disable-notifications':
                program_opts['notifications_enabled'] = False
            elif option in ('-v', '--verbose'):
                coloredlogs.increase_verbosity()
            elif option in ('-q', '--quiet'):
                coloredlogs.decrease_verbosity()
            elif option in ('-h', '--help'):
                usage(__doc__)
                return
            else:
                raise Exception("Unhandled option! (programming error)")
        if len(arguments) > 2:
            msg = "Expected one or two positional arguments! (got %i)"
            raise Exception(msg % len(arguments))
        if len(arguments) == 2:
            # Get the source from the first of two arguments.
            program_opts['source'] = arguments.pop(0)
        if arguments:
            # Get the destination from the second (or only) argument.
            dest_opts['expression'] = arguments[0]
            program_opts['destination'] = Destination(**dest_opts)
        elif not os.environ.get('RSYNC_MODULE_PATH'):
            # Show a usage message when no destination is given.
            usage(__doc__)
            return
    except Exception as e:
        warning("Error: %s", e)
        sys.exit(1)
    try:
        # Inject the source context into the program options.
        program_opts['source_context'] = create_context(**context_opts)
        # Initialize the program with the command line
        # options and execute the requested action(s).
        RsyncSystemBackup(**program_opts).execute()
    except Exception as e:
        if isinstance(e, RsyncSystemBackupError):
            # Special handling when the backup disk isn't available.
            if isinstance(e, MissingBackupDiskError):
                # Check if we're connected to a terminal to decide whether the
                # error should be propagated or silenced, the idea being that
                # rsync-system-backup should keep quiet when it's being run
                # from cron and the backup disk isn't available.
                if not connected_to_terminal():
                    logger.info("Skipping backup: %s", e)
                    sys.exit(0)
            # Known problems shouldn't produce
            # an intimidating traceback to users.
            logger.error("Aborting due to error: %s", e)
        else:
            # Unhandled exceptions do get a traceback,
            # because it may help fix programming errors.
            logger.exception("Aborting due to unhandled exception!")
        sys.exit(1)
Example #31
0
def main():
    """Command line interface for ``update-samples.py``."""
    # Enable verbose logging to the terminal.
    coloredlogs.install(level='INFO')
    # Validate the command line arguments.
    arguments = sys.argv[1:]
    if len(arguments) != 1:
        warning(
            "Please provide the SSH alias of a test host (a single argument).")
        sys.exit(1)
    # Construct the remote context.
    context = RemoteContext(ssh_alias=arguments[0])
    # Prepare Linux kernel packages on the test host.
    context.execute(
        'apt-get',
        'install',
        '--yes',
        'linux-headers-3.13.0-63',
        'linux-headers-3.13.0-63-generic',
        'linux-headers-3.13.0-88',
        'linux-headers-generic-lts-xenial',
        'linux-image-3.13.0-63-generic',
        'linux-image-3.13.0-73-generic',
        'linux-image-4.4.0-21',
        'linux-image-4.4.0-21-generic',
        'linux-image-extra-3.13.0-63-generic',
        'linux-image-generic-lts-wily',
        'linux-image-generic-lts-xenial',
        environment=dict(DEBIAN_FRONTEND='noninteractive'),
        sudo=True,
        tty=False,
    )
    samples_directory = os.path.join(os.path.dirname(__file__), '..', 'docs')
    os.environ['COLOREDLOGS_FIELD_STYLES'] = ';'.join([
        'asctime=green',
        'levelname=black,bold',
    ])
    os.environ['COLOREDLOGS_LEVEL_STYLES'] = ';'.join([
        'verbose=green',
        'warning=yellow',
        'error=red',
    ])
    os.environ['COLOREDLOGS_LOG_FORMAT'] = ' '.join([
        '%(asctime)s',
        '%(levelname)s',
        '%(message)s',
    ])
    # Capture a run that is expected to fail.
    with CaptureOutput() as capturer:
        execute(
            'debuntu-kernel-manager',
            '--remote-host=%s' % context.ssh_alias,
            '--remove',
            '--verbose',
            check=False,
            tty=False,
        )
        capture_html(
            capturer,
            os.path.join(samples_directory, 'sanity-check-says-no.html'))
    # Capture a run that is expected to succeed.
    with CaptureOutput() as capturer:
        execute(
            'debuntu-kernel-manager',
            '--remote-host=%s' % context.ssh_alias,
            '--remove',
            '--force',
            '--verbose',
            '--',
            '--dry-run',
            '--quiet',
            '--quiet',
            tty=False,
        )
        capture_html(capturer,
                     os.path.join(samples_directory, 'operator-says-yes.html'))
Example #32
0
def main():
    """Command line interface for the ``deb-pkg-tools`` program."""
    # Configure logging output.
    coloredlogs.install()
    # Command line option defaults.
    prompt = True
    actions = []
    control_file = None
    control_fields = {}
    directory = None
    # Initialize the package cache.
    cache = get_default_cache()
    # Parse the command line options.
    try:
        options, arguments = getopt.getopt(
            sys.argv[1:], 'i:c:C:p:s:b:u:a:d:w:yvh', [
                'inspect=', 'collect=', 'check=', 'patch=', 'set=', 'build=',
                'update-repo=', 'activate-repo=', 'deactivate-repo=',
                'with-repo=', 'gc', 'garbage-collect', 'yes', 'verbose', 'help'
            ])
        for option, value in options:
            if option in ('-i', '--inspect'):
                actions.append(
                    functools.partial(show_package_metadata, archive=value))
            elif option in ('-c', '--collect'):
                directory = check_directory(value)
            elif option in ('-C', '--check'):
                actions.append(
                    functools.partial(check_package,
                                      archive=value,
                                      cache=cache))
            elif option in ('-p', '--patch'):
                control_file = os.path.abspath(value)
                assert os.path.isfile(
                    control_file), "Control file does not exist!"
            elif option in ('-s', '--set'):
                name, _, value = value.partition(':')
                control_fields[name] = value.strip()
            elif option in ('-b', '--build'):
                actions.append(
                    functools.partial(
                        build_package,
                        check_directory(value),
                        repository=tempfile.gettempdir(),
                    ))
            elif option in ('-u', '--update-repo'):
                actions.append(
                    functools.partial(update_repository,
                                      directory=check_directory(value),
                                      cache=cache))
            elif option in ('-a', '--activate-repo'):
                actions.append(
                    functools.partial(activate_repository,
                                      check_directory(value)))
            elif option in ('-d', '--deactivate-repo'):
                actions.append(
                    functools.partial(deactivate_repository,
                                      check_directory(value)))
            elif option in ('-w', '--with-repo'):
                actions.append(
                    functools.partial(with_repository_wrapper,
                                      directory=check_directory(value),
                                      command=arguments,
                                      cache=cache))
            elif option in ('--gc', '--garbage-collect'):
                actions.append(
                    functools.partial(cache.collect_garbage, force=True))
            elif option in ('-y', '--yes'):
                prompt = False
            elif option in ('-v', '--verbose'):
                coloredlogs.increase_verbosity()
            elif option in ('-h', '--help'):
                usage(__doc__)
                return
        # We delay the patch_control_file() and collect_packages() partials
        # until all command line options have been parsed, to ensure that the
        # order of the command line options doesn't matter.
        if control_file:
            if not control_fields:
                raise Exception(
                    "Please specify one or more control file fields to patch!")
            actions.append(
                functools.partial(patch_control_file, control_file,
                                  control_fields))
        if directory:
            actions.append(
                functools.partial(collect_packages,
                                  archives=arguments,
                                  directory=directory,
                                  prompt=prompt,
                                  cache=cache))
    except Exception as e:
        warning("Error: %s", e)
        sys.exit(1)
    # Execute the selected action.
    try:
        if actions:
            for action in actions:
                action()
            cache.collect_garbage()
        else:
            usage(__doc__)
    except Exception:
        logger.exception("An error occurred! Aborting..")
        sys.exit(1)
Example #33
0
def prompt_for_confirmation(question, default=None, padding=True):
    """
    Prompt the user for confirmation.

    :param question: The text that explains what the user is confirming (a string).
    :param default: The default value (a boolean) or :data:`None`.
    :param padding: Refer to the documentation of :func:`prompt_for_input()`.
    :returns: - If the user enters 'yes' or 'y' then :data:`True` is returned.
              - If the user enters 'no' or 'n' then :data:`False`  is returned.
              - If the user doesn't enter any text or standard input is not
                connected to a terminal (which makes it impossible to prompt
                the user) the value of the keyword argument ``default`` is
                returned (if that value is not :data:`None`).
    :raises: - Any exceptions raised by :func:`retry_limit()`.
             - Any exceptions raised by :func:`prompt_for_input()`.

    When `default` is :data:`False` and the user doesn't enter any text an
    error message is printed and the prompt is repeated:

    >>> prompt_for_confirmation("Are you sure?")
     <BLANKLINE>
     Are you sure? [y/n]
     <BLANKLINE>
     Error: Please enter 'yes' or 'no' (there's no default choice).
     <BLANKLINE>
     Are you sure? [y/n]

    The same thing happens when the user enters text that isn't recognized:

    >>> prompt_for_confirmation("Are you sure?")
     <BLANKLINE>
     Are you sure? [y/n] about what?
     <BLANKLINE>
     Error: Please enter 'yes' or 'no' (the text 'about what?' is not recognized).
     <BLANKLINE>
     Are you sure? [y/n]
    """
    # Generate the text for the prompt.
    prompt_text = question
    if terminal_supports_colors():
        prompt_text = ansi_wrap(prompt_text, bold=True, readline_hints=True)
    # Append the valid replies (and default reply) to the prompt text.
    hint = "[Y/n]" if default else "[y/N]" if default is not None else "[y/n]"
    if terminal_supports_colors():
        hint = ansi_wrap(hint, color=HIGHLIGHT_COLOR, readline_hints=True)
    prompt_text += " %s " % hint
    # Loop until a valid response is given.
    logger.debug("Requesting interactive confirmation from terminal: %r", ansi_strip(prompt_text).rstrip())
    for attempt in retry_limit():
        reply = prompt_for_input(prompt_text, '', padding=padding, strip=True)
        if reply.lower() in ('y', 'yes'):
            logger.debug("Confirmation granted by reply (%r).", reply)
            return True
        elif reply.lower() in ('n', 'no'):
            logger.debug("Confirmation denied by reply (%r).", reply)
            return False
        elif (not reply) and default is not None:
            logger.debug("Default choice selected by empty reply (%r).",
                         "granted" if default else "denied")
            return default
        else:
            details = ("the text '%s' is not recognized" % reply
                       if reply else "there's no default choice")
            logger.debug("Got %s reply (%s), retrying (%i/%i) ..",
                         "invalid" if reply else "empty", details,
                         attempt, MAX_ATTEMPTS)
            warning("{indent}Error: Please enter 'yes' or 'no' ({details}).",
                    indent=' ' if padding else '', details=details)
Example #34
0
def main():
    """Command line interface for the ``chat-archive`` program."""
    # Enable logging to the terminal.
    coloredlogs.install()
    # Parse the command line options.
    program_opts = dict()
    command_name = None
    try:
        options, arguments = getopt.gnu_getopt(
            sys.argv[1:],
            "C:fl:c:p:vqh",
            [
                "context=",
                "force",
                "log-file=",
                "color=",
                "colour=",
                "profile=",
                "verbose",
                "quiet",
                "help",
            ],
        )
        for option, value in options:
            if option in ("-C", "--context"):
                program_opts["context"] = int(value)
            elif option in ("-f", "--force"):
                program_opts["force"] = True
            elif option in ("-l", "--log-file"):
                handler = logging.FileHandler(parse_path(value))
                handler.setFormatter(
                    logging.Formatter(
                        fmt=
                        "%(asctime)s %(name)s[%(process)d] %(levelname)s %(message)s",
                        datefmt="%Y-%m-%d %H:%M:%S"))
                handler.setLevel(logging.DEBUG)
                logging.root.addHandler(handler)
                logging.root.setLevel(logging.NOTSET)
            elif option in ("-c", "--color", "--colour"):
                mapping = dict(always=True, never=False)
                program_opts["use_colors"] = mapping[
                    value] if value in mapping else coerce_boolean(value)
            elif option in ("-p", "--profile"):
                program_opts["profile_file"] = parse_path(value)
            elif option in ("-v", "--verbose"):
                coloredlogs.increase_verbosity()
            elif option in ("-q", "--quiet"):
                coloredlogs.decrease_verbosity()
            elif option in ("-h", "--help"):
                usage(__doc__)
                sys.exit(0)
            else:
                assert False, "Unhandled option!"
        # Make sure the operator provided a command.
        if not arguments:
            usage(__doc__)
            sys.exit(0)
    except Exception as e:
        warning("Failed to parse command line arguments: %s", e)
        sys.exit(1)
    try:
        # We extract any search keywords from the command line arguments before
        # initializing an instance of the UserInterface class, to enable
        # initialization of the KeywordHighlighter class.
        if arguments[0] == "search":
            program_opts["keywords"] = arguments[1:]
        # Initialize the chat archive.
        with UserInterface(**program_opts) as program:
            # Validate the requested command.
            command_name = arguments.pop(0)
            method_name = "%s_cmd" % command_name
            if not hasattr(program, method_name):
                warning("Error: Invalid command name '%s'!", command_name)
                sys.exit(1)
            # Execute the requested command.
            command_fn = getattr(program, method_name)
            command_fn(arguments)
    except KeyboardInterrupt:
        logger.notice("Interrupted by Control-C ..")
        sys.exit(1)
    except Exception:
        logger.exception("Aborting due to unexpected exception!")
        sys.exit(1)
Example #35
0
def main():
    """Command line interface for ``unlock-remote-system``."""
    # Initialize logging to the terminal and system log.
    coloredlogs.install(syslog=True)
    # Parse the command line arguments.
    program_opts = {}
    identity_file = None
    do_shell = False
    do_watch = False
    watch_all = False
    try:
        options, arguments = getopt.gnu_getopt(sys.argv[1:], 'i:k:p:r:swavqh',
                                               [
                                                   'identity-file=',
                                                   'known-hosts=',
                                                   'password='******'remote-host=',
                                                   'shell',
                                                   'watch',
                                                   'all',
                                                   'verbose',
                                                   'quiet',
                                                   'help',
                                               ])
        for option, value in options:
            if option in ('-i', '--identity-file'):
                identity_file = parse_path(value)
            elif option in ('-k', '--known-hosts'):
                program_opts['known_hosts_file'] = parse_path(value)
            elif option in ('-p', '--password'):
                program_opts['password'] = get_password_from_store(value)
            elif option in ('-r', '--remote-host'):
                program_opts['ssh_proxy'] = value
            elif option in ('-s', '--shell'):
                do_shell = True
            elif option in ('-w', '--watch'):
                do_watch = True
            elif option in ('-a', '--all'):
                watch_all = True
            elif option in ('-v', '--verbose'):
                coloredlogs.increase_verbosity()
            elif option in ('-q', '--quiet'):
                coloredlogs.decrease_verbosity()
            elif option in ('-h', '--help'):
                usage(__doc__)
                sys.exit(0)
            else:
                raise Exception("Unhandled option!")
        if not arguments:
            usage(__doc__)
            sys.exit(0)
        elif len(arguments) > 2:
            raise Exception("only two positional arguments allowed")
        # Create a ConfigLoader object and prepare to pass it to the program to
        # avoid scanning for configuration files more than once (which isn't a
        # real problem but does generate somewhat confusing log output).
        loader = ConfigLoader(program_name='unlock-remote-system')
        program_opts['config_loader'] = loader
        # Check if a single positional argument was given that matches the name
        # of a user defined configuration section.
        if len(arguments) == 1 and arguments[0] in loader.section_names:
            logger.info("Loading configuration section '%s' ..", arguments[0])
            program_opts['config_section'] = arguments[0]
        else:
            # The SSH connection profile of the pre-boot environment
            # is given as the first positional argument.
            program_opts['pre_boot'] = ConnectionProfile(
                expression=arguments[0], identity_file=identity_file)
            # The SSH connection profile of the post-boot environment
            # can be given as the second positional argument, otherwise
            # it will be inferred from the connection profile of
            # the pre-boot environment.
            if len(arguments) == 2:
                program_opts['post_boot'] = ConnectionProfile(
                    expression=arguments[1])
            else:
                # By default we don't use root to login to the post-boot environment.
                program_opts['post_boot'] = ConnectionProfile(
                    expression=arguments[0])
                program_opts['post_boot'].username = find_local_username()
            # Prompt the operator to enter the disk encryption password for the remote host?
            if not program_opts.get('password'):
                program_opts['password'] = prompt_for_password(
                    program_opts['pre_boot'].hostname)
    except Exception as e:
        warning("Failed to parse command line arguments! (%s)", e)
        sys.exit(1)
    # Try to unlock the remote system.
    try:
        if do_watch and watch_all:
            watch_all_systems(loader)
        else:
            with EncryptedSystem(**program_opts) as program:
                if do_watch:
                    program.watch_system()
                else:
                    program.unlock_system()
                    if do_shell:
                        start_interactive_shell(program.post_context)
    except EncryptedSystemError as e:
        logger.error("Aborting due to error: %s", e)
        sys.exit(2)
    except Exception:
        logger.exception("Aborting due to unexpected exception!")
        sys.exit(3)
Example #36
0
def main():
    """Command line interface for the ``executor`` program."""
    # Enable logging to the terminal and system log.
    coloredlogs.install(syslog=True)
    # Command line option defaults.
    command_timeout = 0
    exclusive = False
    fudge_factor = 0
    lock_name = None
    lock_timeout = 0
    # Parse the command line options.
    try:
        options, arguments = getopt.getopt(sys.argv[1:], 'eT:l:t:f:vqh', [
            'exclusive', 'lock-timeout=', 'lock-file=', 'timeout=',
            'fudge-factor=', 'verbose', 'quiet', 'help',
        ])
        for option, value in options:
            if option in ('-e', '--exclusive'):
                exclusive = True
            elif option in ('-T', '--lock-timeout'):
                lock_timeout = parse_timespan(value)
            elif option in ('-l', '--lock-file'):
                lock_name = value
            elif option in ('-t', '--timeout'):
                command_timeout = parse_timespan(value)
            elif option in ('-f', '--fudge-factor'):
                fudge_factor = parse_timespan(value)
            elif option in ('-v', '--verbose'):
                coloredlogs.increase_verbosity()
            elif option in ('-q', '--quiet'):
                coloredlogs.decrease_verbosity()
            elif option in ('-h', '--help'):
                usage(__doc__)
                sys.exit(0)
            else:
                assert False, "Unhandled option!"
        # Make sure the operator provided a program to execute.
        if not arguments:
            usage(__doc__)
            sys.exit(0)
        # Make sure the program actually exists.
        program_name = arguments[0]
        if not os.path.isfile(program_name):
            # Only search the $PATH if the given program name
            # doesn't already include one or more path segments.
            if program_name == os.path.basename(program_name):
                matching_programs = which(program_name)
                if matching_programs:
                    program_name = matching_programs[0]
        # The subprocess.Popen() call later on doesn't search the $PATH so we
        # make sure to give it the absolute pathname to the program.
        arguments[0] = program_name
    except Exception as e:
        warning("Failed to parse command line arguments: %s", e)
        sys.exit(1)
    # Apply the requested fudge factor.
    apply_fudge_factor(fudge_factor)
    # Run the requested command.
    try:
        if exclusive:
            # Select a default lock file name?
            if not lock_name:
                lock_name = os.path.basename(arguments[0])
                logger.debug("Using base name of command as lock file name (%s).", lock_name)
            lock_file = get_lock_path(lock_name)
            lock = InterProcessLock(path=lock_file, logger=logger)
            logger.debug("Trying to acquire exclusive lock: %s", lock_file)
            if lock.acquire(blocking=(lock_timeout > 0), max_delay=lock_timeout):
                logger.info("Successfully acquired exclusive lock: %s", lock_file)
                run_command(arguments, timeout=command_timeout)
            else:
                logger.error("Failed to acquire exclusive lock: %s", lock_file)
                sys.exit(1)
        else:
            run_command(arguments, timeout=command_timeout)
    except ExternalCommandFailed as e:
        logger.error("%s", e.error_message)
        sys.exit(e.command.returncode)
Example #37
0
def main():
    """Command line interface for the ``executor`` program."""
    # Enable logging to the terminal and system log.
    coloredlogs.install(syslog=True)
    # Command line option defaults.
    command_timeout = 0
    exclusive = False
    fudge_factor = 0
    lock_name = None
    lock_timeout = 0
    # Parse the command line options.
    try:
        options, arguments = getopt.getopt(sys.argv[1:], 'eT:l:t:f:vqh', [
            'exclusive', 'lock-timeout=', 'lock-file=', 'timeout=',
            'fudge-factor=', 'verbose', 'quiet', 'help',
        ])
        for option, value in options:
            if option in ('-e', '--exclusive'):
                exclusive = True
            elif option in ('-T', '--lock-timeout'):
                lock_timeout = parse_timespan(value)
            elif option in ('-l', '--lock-file'):
                lock_name = value
            elif option in ('-t', '--timeout'):
                command_timeout = parse_timespan(value)
            elif option in ('-f', '--fudge-factor'):
                fudge_factor = parse_timespan(value)
            elif option in ('-v', '--verbose'):
                coloredlogs.increase_verbosity()
            elif option in ('-q', '--quiet'):
                coloredlogs.decrease_verbosity()
            elif option in ('-h', '--help'):
                usage(__doc__)
                sys.exit(0)
            else:
                assert False, "Unhandled option!"
        # Make sure the operator provided a program to execute.
        if not arguments:
            usage(__doc__)
            sys.exit(0)
        # Make sure the program actually exists.
        program_name = arguments[0]
        if not os.path.isfile(program_name):
            # Only search the $PATH if the given program name
            # doesn't already include one or more path segments.
            if program_name == os.path.basename(program_name):
                matching_programs = which(program_name)
                if matching_programs:
                    program_name = matching_programs[0]
        # The subprocess.Popen() call later on doesn't search the $PATH so we
        # make sure to give it the absolute pathname to the program.
        arguments[0] = program_name
    except Exception as e:
        warning("Failed to parse command line arguments: %s", e)
        sys.exit(1)
    # Apply the requested fudge factor.
    apply_fudge_factor(fudge_factor)
    # Run the requested command.
    try:
        if exclusive:
            # Select a default lock file name?
            if not lock_name:
                lock_name = os.path.basename(arguments[0])
                logger.debug("Using base name of command as lock file name (%s).", lock_name)
            lock_file = get_lock_path(lock_name)
            lock = InterProcessLock(path=lock_file, logger=logger)
            logger.debug("Trying to acquire exclusive lock: %s", lock_file)
            if lock.acquire(blocking=(lock_timeout > 0), max_delay=lock_timeout):
                logger.info("Successfully acquired exclusive lock: %s", lock_file)
                run_command(arguments, timeout=command_timeout)
            else:
                logger.error("Failed to acquire exclusive lock: %s", lock_file)
                sys.exit(1)
        else:
            run_command(arguments, timeout=command_timeout)
    except ExternalCommandFailed as e:
        logger.error("%s", e.error_message)
        sys.exit(e.command.returncode)
Example #38
0
def main():
    """Command line interface for the ``deb-pkg-tools`` program."""
    # Configure logging output.
    coloredlogs.install()
    # Command line option defaults.
    prompt = True
    actions = []
    control_file = None
    control_fields = {}
    directory = None
    # Initialize the package cache.
    cache = get_default_cache()
    # Parse the command line options.
    try:
        options, arguments = getopt.getopt(sys.argv[1:], 'i:c:C:p:s:b:u:a:d:w:yvh', [
            'inspect=', 'collect=', 'check=', 'patch=', 'set=', 'build=',
            'update-repo=', 'activate-repo=', 'deactivate-repo=', 'with-repo=',
            'gc', 'garbage-collect', 'yes', 'verbose', 'help'
        ])
        for option, value in options:
            if option in ('-i', '--inspect'):
                actions.append(functools.partial(show_package_metadata, archive=value))
            elif option in ('-c', '--collect'):
                directory = check_directory(value)
            elif option in ('-C', '--check'):
                actions.append(functools.partial(check_package, archive=value, cache=cache))
            elif option in ('-p', '--patch'):
                control_file = os.path.abspath(value)
                assert os.path.isfile(control_file), "Control file does not exist!"
            elif option in ('-s', '--set'):
                name, _, value = value.partition(':')
                control_fields[name] = value.strip()
            elif option in ('-b', '--build'):
                actions.append(functools.partial(
                    build_package,
                    check_directory(value),
                    repository=tempfile.gettempdir(),
                ))
            elif option in ('-u', '--update-repo'):
                actions.append(functools.partial(update_repository,
                                                 directory=check_directory(value),
                                                 cache=cache))
            elif option in ('-a', '--activate-repo'):
                actions.append(functools.partial(activate_repository, check_directory(value)))
            elif option in ('-d', '--deactivate-repo'):
                actions.append(functools.partial(deactivate_repository, check_directory(value)))
            elif option in ('-w', '--with-repo'):
                actions.append(functools.partial(with_repository_wrapper,
                                                 directory=check_directory(value),
                                                 command=arguments,
                                                 cache=cache))
            elif option in ('--gc', '--garbage-collect'):
                actions.append(functools.partial(cache.collect_garbage, force=True))
            elif option in ('-y', '--yes'):
                prompt = False
            elif option in ('-v', '--verbose'):
                coloredlogs.increase_verbosity()
            elif option in ('-h', '--help'):
                usage(__doc__)
                return
        # We delay the patch_control_file() and collect_packages() partials
        # until all command line options have been parsed, to ensure that the
        # order of the command line options doesn't matter.
        if control_file:
            if not control_fields:
                raise Exception("Please specify one or more control file fields to patch!")
            actions.append(functools.partial(patch_control_file, control_file, control_fields))
        if directory:
            actions.append(functools.partial(collect_packages,
                                             archives=arguments,
                                             directory=directory,
                                             prompt=prompt,
                                             cache=cache))
    except Exception as e:
        warning("Error: %s", e)
        sys.exit(1)
    # Execute the selected action.
    try:
        if actions:
            for action in actions:
                action()
            cache.collect_garbage()
        else:
            usage(__doc__)
    except Exception as e:
        logger.exception("An error occurred!")
        sys.exit(1)
Example #39
0
def main():
    """
    Command line interface for the ``py2deb`` program.
    """
    # Configure terminal output.
    coloredlogs.install()
    try:
        # Initialize a package converter.
        converter = PackageConverter()
        # Parse and validate the command line options.
        options, arguments = getopt.getopt(sys.argv[1:], 'c:r:yvh', [
            'config=', 'repository=', 'name-prefix=', 'no-name-prefix=',
            'rename=', 'install-prefix=', 'install-alternative=',
            'python-callback=', 'report-dependencies=',
            'yes', 'verbose', 'help',
        ])
        control_file_to_update = None
        for option, value in options:
            if option in ('-c', '--config'):
                converter.load_configuration_file(value)
            elif option in ('-r', '--repository'):
                converter.set_repository(value)
            elif option == '--name-prefix':
                converter.set_name_prefix(value)
            elif option == '--no-name-prefix':
                converter.rename_package(value, value)
            elif option == '--rename':
                python_package_name, _, debian_package_name = value.partition(',')
                converter.rename_package(python_package_name, debian_package_name)
            elif option == '--install-prefix':
                converter.set_install_prefix(value)
            elif option == '--install-alternative':
                link, _, path = value.partition(',')
                converter.install_alternative(link, path)
            elif option == '--python-callback':
                converter.set_python_callback(value)
            elif option == '--report-dependencies':
                control_file_to_update = value
                if not os.path.isfile(control_file_to_update):
                    msg = "The given control file doesn't exist! (%s)"
                    raise Exception(msg % control_file_to_update)
            elif option in ('-y', '--yes'):
                converter.set_auto_install(True)
            elif option in ('-v', '--verbose'):
                coloredlogs.increase_verbosity()
            elif option in ('-h', '--help'):
                usage(__doc__)
                return
            else:
                assert False, "Unhandled option!"
    except Exception as e:
        warning("Failed to parse command line arguments: %s", e)
        sys.exit(1)
    # Convert the requested package(s).
    try:
        if arguments:
            archives, relationships = converter.convert(arguments)
            if relationships and control_file_to_update:
                patch_control_file(control_file_to_update, dict(depends=relationships))
        else:
            usage(__doc__)
    except Exception:
        logger.exception("Caught an unhandled exception!")
        sys.exit(1)
Example #40
0
def main():
    """The command line interface of the ``vcs-tool`` program."""
    # Initialize logging to the terminal.
    coloredlogs.install()
    # Command line option defaults.
    repository = None
    revision = None
    actions = []
    # Parse the command line arguments.
    try:
        options, arguments = getopt.gnu_getopt(sys.argv[1:], 'r:dnisume:vqh', [
            'repository=', 'rev=', 'revision=', 'release=', 'find-directory',
            'find-revision-number', 'find-revision-id', 'list-releases',
            'select-release=', 'sum-revisions', 'vcs-control-field', 'update',
            'merge-up', 'export=', 'verbose', 'quiet', 'help',
        ])
        for option, value in options:
            if option in ('-r', '--repository'):
                value = value.strip()
                assert value, "Please specify the name of a repository! (using -r, --repository)"
                repository = coerce_repository(value)
            elif option in ('--rev', '--revision'):
                revision = value.strip()
                assert revision, "Please specify a nonempty revision string!"
            elif option == '--release':
                # TODO Right now --release and --merge-up cannot be combined
                #      because the following statements result in a global
                #      revision id which is immutable. If release objects had
                #      something like an optional `mutable_revision_id' it
                #      should be possible to support the combination of
                #      --release and --merge-up.
                assert repository, "Please specify a repository first!"
                release_id = value.strip()
                assert release_id in repository.releases, "The given release identifier is invalid!"
                revision = repository.releases[release_id].revision.revision_id
            elif option in ('-d', '--find-directory'):
                assert repository, "Please specify a repository first!"
                actions.append(functools.partial(print_directory, repository))
            elif option in ('-n', '--find-revision-number'):
                assert repository, "Please specify a repository first!"
                actions.append(functools.partial(print_revision_number, repository, revision))
            elif option in ('-i', '--find-revision-id'):
                assert repository, "Please specify a repository first!"
                actions.append(functools.partial(print_revision_id, repository, revision))
            elif option == '--list-releases':
                assert repository, "Please specify a repository first!"
                actions.append(functools.partial(print_releases, repository))
            elif option == '--select-release':
                assert repository, "Please specify a repository first!"
                release_id = value.strip()
                assert release_id, "Please specify a nonempty release identifier!"
                actions.append(functools.partial(print_selected_release, repository, release_id))
            elif option in ('-s', '--sum-revisions'):
                assert len(arguments) >= 2, "Please specify one or more repository/revision pairs!"
                actions.append(functools.partial(print_summed_revisions, arguments))
                arguments = []
            elif option == '--vcs-control-field':
                assert repository, "Please specify a repository first!"
                actions.append(functools.partial(print_vcs_control_field, repository, revision))
            elif option in ('-u', '--update'):
                assert repository, "Please specify a repository first!"
                actions.append(functools.partial(repository.update))
            elif option in ('-m', '--merge-up'):
                assert repository, "Please specify a repository first!"
                actions.append(functools.partial(
                    repository.merge_up,
                    target_branch=revision,
                    feature_branch=arguments[0] if arguments else None,
                ))
            elif option in ('-e', '--export'):
                directory = value.strip()
                assert repository, "Please specify a repository first!"
                assert directory, "Please specify the directory where the revision should be exported!"
                actions.append(functools.partial(repository.export, directory, revision))
            elif option in ('-v', '--verbose'):
                coloredlogs.increase_verbosity()
            elif option in ('-q', '--quiet'):
                coloredlogs.decrease_verbosity()
            elif option in ('-h', '--help'):
                usage(__doc__)
                return
        if not actions:
            usage(__doc__)
            return
    except Exception as e:
        warning("Error: %s", e)
        sys.exit(1)
    # Execute the requested action(s).
    try:
        for action in actions:
            action()
    except Exception:
        logger.exception("Failed to execute requested action(s)!")
        sys.exit(1)