예제 #1
0
def restic_command(context: Context, restic: Restic, profile: Profile,
                   console: Console):
    console.info("Starting '{}'".format(restic.command))
    full_command = context.get_command_prefix() + context.get_restic_path(
    ) + " " + restic.get_command()
    shell_command(full_command, console, allow_stdin=profile.stdin)
    console.info("Finished '{}'".format(restic.command))
예제 #2
0
def cleanup_old_backups(context: Context, profile: Profile, console: Console):
    restic_retention = Restic(constants.COMMAND_FORGET)
    restic_retention.extend_arguments(profile.get_retention_flags())
    forget_command = context.get_command_prefix() + context.get_restic_path(
    ) + " " + restic_retention.get_forget_command()
    console.info("Cleaning up repository using retention information")
    shell_command(forget_command, console)
예제 #3
0
def check(context: Context, profile: Profile, console: Console):
    restic_check = Restic(constants.COMMAND_CHECK)
    restic_check.extend_arguments(
        profile.get_command_flags(constants.COMMAND_CHECK))
    check_command = context.get_command_prefix() + context.get_restic_path(
    ) + " " + restic_check.get_check_command()
    console.info("Checking repository consistency")
    shell_command(check_command, console)
예제 #4
0
def initialize_repository(context: Context, profile: Profile,
                          console: Console):
    restic_init = Restic(constants.COMMAND_INIT)
    restic_init.extend_arguments(
        profile.get_command_flags(constants.COMMAND_INIT))
    init_command = context.get_command_prefix() + context.get_restic_path(
    ) + " " + restic_init.get_init_command()
    console.info("Initializing repository (if not existing)")
    shell_command(init_command,
                  console,
                  exit_on_returncode=False,
                  display_stderr=False)
예제 #5
0
    def load_context_from_command_line(self, argv: list):
        try:
            short_options = self._get_short_options()
            long_options = self._get_long_options()
            self.opts, self.args = getopt(argv[1:], short_options,
                                          long_options)

        except GetoptError as err:
            console = Console()
            console.error("Error in the command arguments: " + err.msg)
            console.usage(argv[0])
            exit(2)

        for option, argument in self.opts:
            if option in self._get_possible_options_for('help'):
                Console().usage(argv[0])
                exit()

            elif option in self._get_possible_options_for('quiet'):
                self.quiet = True

            elif option in self._get_possible_options_for('verbose'):
                self.verbose = True

            elif option in self._get_possible_options_for('config'):
                self.configuration_file = argument

            elif option in self._get_possible_options_for('name'):
                self.profile_name = argument

            elif option in self._get_possible_options_for('no-ansi'):
                self.ansi = False

            else:
                assert False, "unhandled option"
예제 #6
0
def shell_command(command: str,
                  console: Console,
                  exit_on_returncode=True,
                  display_stderr=True,
                  allow_stdin=False):
    try:
        stdin_ = DEVNULL
        if allow_stdin:
            stdin_ = None

        stderr_ = None
        if not display_stderr:
            stderr_ = DEVNULL

        console.debug("Starting shell command: " + command)
        returncode = call(command, shell=True, stdin=stdin_, stderr=stderr_)
        if returncode != 0 and exit_on_returncode:
            exit(returncode)

    except KeyboardInterrupt:
        exit()
예제 #7
0
def run_restic(base_dir: str, context: Context, profiles: dict,
               console: Console):
    file_search = FileSearch(base_dir)
    config = Config(profiles, file_search)
    profile = Profile(config, context.profile_name)
    restic = Restic()
    if context.args:
        # A command was passed as an argument (it has to be the first one after the options)
        restic.command = context.args[0]

    # Build list of arguments to pass to restic
    if constants.SECTION_CONFIGURATION_GLOBAL in profiles:
        context.set_global_context(config)

    try:
        if context.profile_name in profiles:
            profile.set_common_configuration()

            # there's no default command yet
            if not restic.command:
                restic.command = context.default_command

            # we might need the init command so we prepare it
            profile.set_command_configuration('init')

            # we might also need the check command
            profile.set_command_configuration('check')

            # if the command is backup, we need to load the retention model
            if restic.command == constants.COMMAND_BACKUP:
                profile.set_retention_configuration()

            profile.set_command_configuration(restic.command)

            # environment variables
            env = config.get_environment(context.profile_name)
            if env:
                for key, value in env.items():
                    console.debug(
                        "Setting environment variable {}".format(key))
                    # When running a group of backups, we might want to put back/remove the environment variable after
                    environ[key] = value

        profile.set_verbosity(context.quiet, context.verbose)
        restic.extend_arguments(profile.get_command_flags(restic.command))

    except FileNotFoundError as error:
        console.error("Error in profile [{}]: {}".format(
            context.profile_name, str(error)))
        exit(2)

    except ConfigError as error:
        console.error("Error in profile [{}]: {}".format(
            error.section, error.message))
        exit(2)

    # check that we have the minimum information we need
    if not profile.repository:
        console.error(
            "Error in profile [{}]: a repository is needed in the configuration."
            .format(context.profile_name))
        exit(2)

    # this is the leftover from the command line
    restic.extend_arguments(context.args[1:])

    if profile.initialize:
        initialize_repository(context, profile, console)

    if profile.run_before:
        run_commands(profile.run_before, console)

    if profile.check_before:
        check(context, profile, console)

    if profile.forget_before:
        cleanup_old_backups(context, profile, console)

    restic_command(context, restic, profile, console)

    if profile.forget_after:
        cleanup_old_backups(context, profile, console)

    if profile.check_after:
        check(context, profile, console)

    if profile.run_after:
        run_commands(profile.run_after, console)
예제 #8
0
def main():
    '''
    This is main
    '''
    context = Context(constants.ARGUMENTS_DEFINITION)
    context.load_context_from_command_line(argv)

    console = Console(context.quiet, context.verbose, context.ansi)

    valid_configuration_file = find_configuration_file(
        context.configuration_file)
    if valid_configuration_file is not None:
        console.debug("Using configuration file " + valid_configuration_file)
        try:
            profiles = toml.load(valid_configuration_file)
        except toml.decoder.TomlDecodeError as err:
            console.error(
                "An error occured while loading the configuration file:")
            console.error(str(err))
            exit(2)
    else:
        console.warning(
            "Configuration file '{}' was not found in either the current directory, home directory or any of these locations:\n{}"
            .format(context.configuration_file,
                    get_default_configuration_locations()))
        exit(2)

    base_dir = dirname(valid_configuration_file)
    # todo: allow group of groups
    groups = Groups(profiles)
    if groups.exists(context.profile_name):
        group_name = context.profile_name
        for profile_name in groups.get_profiles(context.profile_name):
            console.debug("Starting profile [{}] from group [{}]".format(
                profile_name, group_name))
            context.profile_name = profile_name
            run_restic(base_dir, context, profiles, console)
    else:
        # single profile
        run_restic(base_dir, context, profiles, console)