def __init__(self, use_ee_cart = True):
     switch_controller_serv_name = 'pr2_controller_manager/switch_controller'
     list_controllers_serv_name = 'pr2_controller_manager/list_controllers'
     wait_for_service(switch_controller_serv_name)
     wait_for_service(list_controllers_serv_name)
     
     self.switch_controller_service = \
         rospy.ServiceProxy(switch_controller_serv_name, SwitchController)
     self.list_controllers_service = \
         rospy.ServiceProxy(list_controllers_serv_name, ListControllers)
     
     self.controllers = {
             'joint': "_arm_controller",
             'cartesian': "_cart"
             }
     if use_ee_cart:
         self.controllers['cartesian']= "_arm_cart_imped_controller"
Esempio n. 2
0
def perform_backup(full_backup):
    env = load_environment()

    # Create an global exclusive lock so that the backup script
    # cannot be run more than one.
    Lock(die=True).forever()

    config = get_backup_config(env)
    backup_root = os.path.join(env["STORAGE_ROOT"], 'backup')
    backup_cache_dir = os.path.join(backup_root, 'cache')
    backup_dir = os.path.join(backup_root, 'encrypted')

    # Are backups disabled?
    if config["target"] == "off":
        return

    # On the first run, always do a full backup. Incremental
    # will fail. Otherwise do a full backup when the size of
    # the increments since the most recent full backup are
    # large.
    try:
        full_backup = full_backup or should_force_full(config, env)
    except Exception as e:
        # This was the first call to duplicity, and there might
        # be an error already.
        print(e)
        sys.exit(1)

    # Stop services.
    def service_command(service, command, quit=None):
        # Execute silently, but if there is an error then display the output & exit.
        code, ret = shell('check_output',
                          ["/usr/sbin/service", service, command],
                          capture_stderr=True,
                          trap=True)
        if code != 0:
            print(ret)
            if quit:
                sys.exit(code)

    service_command("php7.2-fpm", "stop", quit=True)
    service_command("postfix", "stop", quit=True)
    service_command("dovecot", "stop", quit=True)

    # Execute a pre-backup script that copies files outside the homedir.
    # Run as the STORAGE_USER user, not as root. Pass our settings in
    # environment variables so the script has access to STORAGE_ROOT.
    pre_script = os.path.join(backup_root, 'before-backup')
    if os.path.exists(pre_script):
        shell('check_call',
              ['su', env['STORAGE_USER'], '-c', pre_script, config["target"]],
              env=env)

    # Run a backup of STORAGE_ROOT (but excluding the backups themselves!).
    # --allow-source-mismatch is needed in case the box's hostname is changed
    # after the first backup. See #396.
    try:
        shell('check_call', [
            "/usr/bin/duplicity", "full" if full_backup else "incr",
            "--verbosity", "warning", "--no-print-statistics", "--archive-dir",
            backup_cache_dir, "--exclude", backup_root, "--volsize", "250",
            "--gpg-options", "--cipher-algo=AES256", env["STORAGE_ROOT"],
            config["target"], "--allow-source-mismatch"
        ] + rsync_ssh_options, get_env(env))
    finally:
        # Start services again.
        service_command("dovecot", "start", quit=False)
        service_command("postfix", "start", quit=False)
        service_command("php7.2-fpm", "start", quit=False)

    # Remove old backups. This deletes all backup data no longer needed
    # from more than 3 days ago.
    shell('check_call', [
        "/usr/bin/duplicity", "remove-older-than",
        "%dD" % config["min_age_in_days"], "--verbosity", "error",
        "--archive-dir", backup_cache_dir, "--force", config["target"]
    ] + rsync_ssh_options, get_env(env))

    # From duplicity's manual:
    # "This should only be necessary after a duplicity session fails or is
    # aborted prematurely."
    # That may be unlikely here but we may as well ensure we tidy up if
    # that does happen - it might just have been a poorly timed reboot.
    shell('check_call', [
        "/usr/bin/duplicity", "cleanup", "--verbosity", "error",
        "--archive-dir", backup_cache_dir, "--force", config["target"]
    ] + rsync_ssh_options, get_env(env))

    # Change ownership of backups to the user-data user, so that the after-bcakup
    # script can access them.
    if get_target_type(config) == 'file':
        shell('check_call',
              ["/bin/chown", "-R", env["STORAGE_USER"], backup_dir])

    # Execute a post-backup script that does the copying to a remote server.
    # Run as the STORAGE_USER user, not as root. Pass our settings in
    # environment variables so the script has access to STORAGE_ROOT.
    post_script = os.path.join(backup_root, 'after-backup')
    if os.path.exists(post_script):
        shell('check_call',
              ['su', env['STORAGE_USER'], '-c', post_script, config["target"]],
              env=env)

    # Our nightly cron job executes system status checks immediately after this
    # backup. Since it checks that dovecot and postfix are running, block for a
    # bit (maximum of 10 seconds each) to give each a chance to finish restarting
    # before the status checks might catch them down. See #381.
    wait_for_service(25, True, env, 10)
    wait_for_service(993, True, env, 10)
Esempio n. 3
0
def perform_backup(full_backup):
    env = load_environment()

    exclusive_process("backup")
    config = get_backup_config(env)
    backup_root = os.path.join(env["STORAGE_ROOT"], "backup")
    backup_cache_dir = os.path.join(backup_root, "cache")
    backup_dir = os.path.join(backup_root, "encrypted")

    # Are backups dissbled?
    if config["target"] == "off":
        return

        # In an older version of this script, duplicity was called
        # such that it did not encrypt the backups it created (in
        # backup/duplicity), and instead openssl was called separately
        # after each backup run, creating AES256 encrypted copies of
        # each file created by duplicity in backup/encrypted.
        #
        # We detect the transition by the presence of backup/duplicity
        # and handle it by 'dupliception': we move all the old *un*encrypted
        # duplicity files up out of the backup/duplicity directory (as
        # backup/ is excluded from duplicity runs) in order that it is
        # included in the next run, and we delete backup/encrypted (which
        # duplicity will output files directly to, post-transition).
    old_backup_dir = os.path.join(backup_root, "duplicity")
    migrated_unencrypted_backup_dir = os.path.join(env["STORAGE_ROOT"], "migrated_unencrypted_backup")
    if os.path.isdir(old_backup_dir):
        # Move the old unencrypted files to a new location outside of
        # the backup root so they get included in the next (new) backup.
        # Then we'll delete them. Also so that they do not get in the
        # way of duplicity doing a full backup on the first run after
        # we take care of this.
        shutil.move(old_backup_dir, migrated_unencrypted_backup_dir)

        # The backup_dir (backup/encrypted) now has a new purpose.
        # Clear it out.
        shutil.rmtree(backup_dir)

        # On the first run, always do a full backup. Incremental
        # will fail. Otherwise do a full backup when the size of
        # the increments since the most recent full backup are
        # large.
    try:
        full_backup = full_backup or should_force_full(config, env)
    except Exception as e:
        # This was the first call to duplicity, and there might
        # be an error already.
        print(e)
        sys.exit(1)

        # Stop services.

    def service_command(service, command, quit=None):
        # Execute silently, but if there is an error then display the output & exit.
        code, ret = shell("check_output", ["/usr/sbin/service", service, command], capture_stderr=True, trap=True)
        if code != 0:
            print(ret)
            if quit:
                sys.exit(code)

    service_command("php5-fpm", "stop", quit=True)
    service_command("postfix", "stop", quit=True)
    service_command("dovecot", "stop", quit=True)

    # Execute a pre-backup script that copies files outside the homedir.
    # Run as the STORAGE_USER user, not as root. Pass our settings in
    # environment variables so the script has access to STORAGE_ROOT.
    pre_script = os.path.join(backup_root, "before-backup")
    if os.path.exists(pre_script):
        shell("check_call", ["su", env["STORAGE_USER"], "-c", pre_script, config["target"]], env=env)

        # Run a backup of STORAGE_ROOT (but excluding the backups themselves!).
        # --allow-source-mismatch is needed in case the box's hostname is changed
        # after the first backup. See #396.
    try:
        shell(
            "check_call",
            [
                "/usr/bin/duplicity",
                "full" if full_backup else "incr",
                "--verbosity",
                "warning",
                "--no-print-statistics",
                "--archive-dir",
                backup_cache_dir,
                "--exclude",
                backup_root,
                "--volsize",
                "250",
                "--gpg-options",
                "--cipher-algo=AES256",
                env["STORAGE_ROOT"],
                config["target"],
                "--allow-source-mismatch",
            ],
            get_env(env),
        )
    finally:
        # Start services again.
        service_command("dovecot", "start", quit=False)
        service_command("postfix", "start", quit=False)
        service_command("php5-fpm", "start", quit=False)

        # Once the migrated backup is included in a new backup, it can be deleted.
    if os.path.isdir(migrated_unencrypted_backup_dir):
        shutil.rmtree(migrated_unencrypted_backup_dir)

        # Remove old backups. This deletes all backup data no longer needed
        # from more than 3 days ago.
    shell(
        "check_call",
        [
            "/usr/bin/duplicity",
            "remove-older-than",
            "%dD" % config["min_age_in_days"],
            "--verbosity",
            "error",
            "--archive-dir",
            backup_cache_dir,
            "--force",
            config["target"],
        ],
        get_env(env),
    )

    # From duplicity's manual:
    # "This should only be necessary after a duplicity session fails or is
    # aborted prematurely."
    # That may be unlikely here but we may as well ensure we tidy up if
    # that does happen - it might just have been a poorly timed reboot.
    shell(
        "check_call",
        [
            "/usr/bin/duplicity",
            "cleanup",
            "--verbosity",
            "error",
            "--archive-dir",
            backup_cache_dir,
            "--force",
            config["target"],
        ],
        get_env(env),
    )

    # Change ownership of backups to the user-data user, so that the after-bcakup
    # script can access them.
    if get_target_type(config) == "file":
        shell("check_call", ["/bin/chown", "-R", env["STORAGE_USER"], backup_dir])

        # Execute a post-backup script that does the copying to a remote server.
        # Run as the STORAGE_USER user, not as root. Pass our settings in
        # environment variables so the script has access to STORAGE_ROOT.
    post_script = os.path.join(backup_root, "after-backup")
    if os.path.exists(post_script):
        shell("check_call", ["su", env["STORAGE_USER"], "-c", post_script, config["target"]], env=env)

        # Our nightly cron job executes system status checks immediately after this
        # backup. Since it checks that dovecot and postfix are running, block for a
        # bit (maximum of 10 seconds each) to give each a chance to finish restarting
        # before the status checks might catch them down. See #381.
    wait_for_service(25, True, env, 10)
    wait_for_service(993, True, env, 10)
Esempio n. 4
0
def perform_backup(full_backup):
    env = load_environment()

    exclusive_process("backup")
    config = get_backup_config(env)
    backup_root = os.path.join(env["STORAGE_ROOT"], 'backup')
    backup_cache_dir = os.path.join(backup_root, 'cache')
    backup_dir = os.path.join(backup_root, 'encrypted')

    # Are backups dissbled?
    if config["target"] == "off":
        return

    # In an older version of this script, duplicity was called
    # such that it did not encrypt the backups it created (in
    # backup/duplicity), and instead openssl was called separately
    # after each backup run, creating AES256 encrypted copies of
    # each file created by duplicity in backup/encrypted.
    #
    # We detect the transition by the presence of backup/duplicity
    # and handle it by 'dupliception': we move all the old *un*encrypted
    # duplicity files up out of the backup/duplicity directory (as
    # backup/ is excluded from duplicity runs) in order that it is
    # included in the next run, and we delete backup/encrypted (which
    # duplicity will output files directly to, post-transition).
    old_backup_dir = os.path.join(backup_root, 'duplicity')
    migrated_unencrypted_backup_dir = os.path.join(env["STORAGE_ROOT"], "migrated_unencrypted_backup")
    if os.path.isdir(old_backup_dir):
        # Move the old unencrypted files to a new location outside of
        # the backup root so they get included in the next (new) backup.
        # Then we'll delete them. Also so that they do not get in the
        # way of duplicity doing a full backup on the first run after
        # we take care of this.
        shutil.move(old_backup_dir, migrated_unencrypted_backup_dir)

        # The backup_dir (backup/encrypted) now has a new purpose.
        # Clear it out.
        shutil.rmtree(backup_dir)

    # On the first run, always do a full backup. Incremental
    # will fail. Otherwise do a full backup when the size of
    # the increments since the most recent full backup are
    # large.
    try:
        full_backup = full_backup or should_force_full(config, env)
    except Exception as e:
        # This was the first call to duplicity, and there might
        # be an error already.
        print(e)
        sys.exit(1)

    # Stop services.
    def service_command(service, command, quit=None):
        # Execute silently, but if there is an error then display the output & exit.
        code, ret = shell('check_output', ["/usr/sbin/service", service, command], capture_stderr=True, trap=True)
        if code != 0:
            print(ret)
            if quit:
                sys.exit(code)

    service_command("postfix", "stop", quit=True)
    service_command("dovecot", "stop", quit=True)

    # Execute a pre-backup script that copies files outside the homedir.
    # Run as the STORAGE_USER user, not as root. Pass our settings in
    # environment variables so the script has access to STORAGE_ROOT.
    pre_script = os.path.join(backup_root, 'before-backup')
    if os.path.exists(pre_script):
        shell('check_call',
              ['su', env['STORAGE_USER'], '-c', pre_script, config["target"]],
              env=env)

    # Run a backup of STORAGE_ROOT (but excluding the backups themselves!).
    # --allow-source-mismatch is needed in case the box's hostname is changed
    # after the first backup. See #396.
    try:
        shell('check_call', [
            "/usr/bin/duplicity",
            "full" if full_backup else "incr",
            "--verbosity", "warning", "--no-print-statistics",
            "--archive-dir", backup_cache_dir,
            "--exclude", backup_root,
            "--volsize", "250",
            "--gpg-options", "--cipher-algo=AES256",
            env["STORAGE_ROOT"],
            config["target"],
            "--allow-source-mismatch"
        ],
              get_env(env))
    finally:
        # Start services again.
        service_command("dovecot", "start", quit=False)
        service_command("postfix", "start", quit=False)

    # Once the migrated backup is included in a new backup, it can be deleted.
    if os.path.isdir(migrated_unencrypted_backup_dir):
        shutil.rmtree(migrated_unencrypted_backup_dir)

    # Remove old backups. This deletes all backup data no longer needed
    # from more than 3 days ago.
    shell('check_call', [
        "/usr/bin/duplicity",
        "remove-older-than",
        "%dD" % config["min_age_in_days"],
        "--verbosity", "error",
        "--archive-dir", backup_cache_dir,
        "--force",
        config["target"]
    ],
          get_env(env))

    # From duplicity's manual:
    # "This should only be necessary after a duplicity session fails or is
    # aborted prematurely."
    # That may be unlikely here but we may as well ensure we tidy up if
    # that does happen - it might just have been a poorly timed reboot.
    shell('check_call', [
        "/usr/bin/duplicity",
        "cleanup",
        "--verbosity", "error",
        "--archive-dir", backup_cache_dir,
        "--force",
        config["target"]
    ],
          get_env(env))

    # Change ownership of backups to the user-data user, so that the after-bcakup
    # script can access them.
    if get_target_type(config) == 'file':
        shell('check_call', ["/bin/chown", "-R", env["STORAGE_USER"], backup_dir])

    # Execute a post-backup script that does the copying to a remote server.
    # Run as the STORAGE_USER user, not as root. Pass our settings in
    # environment variables so the script has access to STORAGE_ROOT.
    post_script = os.path.join(backup_root, 'after-backup')
    if os.path.exists(post_script):
        shell('check_call',
              ['su', env['STORAGE_USER'], '-c', post_script, config["target"]],
              env=env)

    # Our nightly cron job executes system status checks immediately after this
    # backup. Since it checks that dovecot and postfix are running, block for a
    # bit (maximum of 10 seconds each) to give each a chance to finish restarting
    # before the status checks might catch them down. See #381.
    wait_for_service(25, True, env, 10)
    wait_for_service(993, True, env, 10)
Esempio n. 5
0
def perform_backup(full_backup):
	env = load_environment()

	exclusive_process("backup")

	backup_root = os.path.join(env["STORAGE_ROOT"], 'backup')
	backup_cache_dir = os.path.join(backup_root, 'cache')
	backup_dir = os.path.join(backup_root, 'encrypted')

	# In an older version of this script, duplicity was called
	# such that it did not encrypt the backups it created (in
	# backup/duplicity), and instead openssl was called separately
	# after each backup run, creating AES256 encrypted copies of
	# each file created by duplicity in backup/encrypted.
	#
	# We detect the transition by the presence of backup/duplicity
	# and handle it by 'dupliception': we move all the old *un*encrypted
	# duplicity files up out of the backup/duplicity directory (as
	# backup/ is excluded from duplicity runs) in order that it is
	# included in the next run, and we delete backup/encrypted (which
	# duplicity will output files directly to, post-transition).
	old_backup_dir = os.path.join(backup_root, 'duplicity')
	migrated_unencrypted_backup_dir = os.path.join(env["STORAGE_ROOT"], "migrated_unencrypted_backup")
	if os.path.isdir(old_backup_dir):
		# Move the old unencrypted files to a new location outside of
		# the backup root so they get included in the next (new) backup.
		# Then we'll delete them. Also so that they do not get in the
		# way of duplicity doing a full backup on the first run after
		# we take care of this.
		shutil.move(old_backup_dir, migrated_unencrypted_backup_dir)

		# The backup_dir (backup/encrypted) now has a new purpose.
		# Clear it out.
		shutil.rmtree(backup_dir)

	# On the first run, always do a full backup. Incremental
	# will fail. Otherwise do a full backup when the size of
	# the increments since the most recent full backup are
	# large.
	full_backup = full_backup or should_force_full(env)

	# Stop services.
	shell('check_call', ["/usr/sbin/service", "dovecot", "stop"])
	shell('check_call', ["/usr/sbin/service", "postfix", "stop"])

	# Get the encryption passphrase. secret_key.txt is 2048 random
	# bits base64-encoded and with line breaks every 65 characters.
	# gpg will only take the first line of text, so sanity check that
	# that line is long enough to be a reasonable passphrase. It
	# only needs to be 43 base64-characters to match AES256's key
	# length of 32 bytes.
	with open(os.path.join(backup_root, 'secret_key.txt')) as f:
		passphrase = f.readline().strip()
	if len(passphrase) < 43: raise Exception("secret_key.txt's first line is too short!")
	env_with_passphrase = { "PASSPHRASE" : passphrase }

	# Run a backup of STORAGE_ROOT (but excluding the backups themselves!).
	# --allow-source-mismatch is needed in case the box's hostname is changed
	# after the first backup. See #396.
	try:
		shell('check_call', [
			"/usr/bin/duplicity",
			"full" if full_backup else "incr",
			"--archive-dir", backup_cache_dir,
			"--exclude", backup_root,
			"--volsize", "250",
			"--gpg-options", "--cipher-algo=AES256",
			env["STORAGE_ROOT"],
			"file://" + backup_dir,
                        "--allow-source-mismatch"
			],
			env_with_passphrase)
	finally:
		# Start services again.
		shell('check_call', ["/usr/sbin/service", "dovecot", "start"])
		shell('check_call', ["/usr/sbin/service", "postfix", "start"])

	# Once the migrated backup is included in a new backup, it can be deleted.
	if os.path.isdir(migrated_unencrypted_backup_dir):
		shutil.rmtree(migrated_unencrypted_backup_dir)

	# Remove old backups. This deletes all backup data no longer needed
	# from more than 3 days ago.
	shell('check_call', [
		"/usr/bin/duplicity",
		"remove-older-than",
		"%dD" % keep_backups_for_days,
		"--archive-dir", backup_cache_dir,
		"--force",
		"file://" + backup_dir
		],
		env_with_passphrase)

	# From duplicity's manual:
	# "This should only be necessary after a duplicity session fails or is
	# aborted prematurely."
	# That may be unlikely here but we may as well ensure we tidy up if
	# that does happen - it might just have been a poorly timed reboot.
	shell('check_call', [
		"/usr/bin/duplicity",
		"cleanup",
		"--archive-dir", backup_cache_dir,
		"--force",
		"file://" + backup_dir
		],
		env_with_passphrase)

	# Change ownership of backups to the user-data user, so that the after-bcakup
	# script can access them.
	shell('check_call', ["/bin/chown", "-R", env["STORAGE_USER"], backup_dir])

	# Execute a post-backup script that does the copying to a remote server.
	# Run as the STORAGE_USER user, not as root. Pass our settings in
	# environment variables so the script has access to STORAGE_ROOT.
	post_script = os.path.join(backup_root, 'after-backup')
	if os.path.exists(post_script):
		shell('check_call',
			['su', env['STORAGE_USER'], '-c', post_script],
			env=env)

	# Our nightly cron job executes system status checks immediately after this
	# backup. Since it checks that dovecot and postfix are running, block for a
	# bit (maximum of 10 seconds each) to give each a chance to finish restarting
	# before the status checks might catch them down. See #381.
	wait_for_service(25, True, env, 10)
	wait_for_service(993, True, env, 10)
Esempio n. 6
0
def perform_backup(full_backup):
	env = load_environment()

	# Create an global exclusive lock so that the backup script
	# cannot be run more than one.
	Lock(die=True).forever()

	config = get_backup_config(env)
	backup_root = os.path.join(env["STORAGE_ROOT"], 'backup')
	backup_cache_dir = os.path.join(backup_root, 'cache')
	backup_dir = os.path.join(backup_root, 'encrypted')

	# Are backups disabled?
	if config["target"] == "off":
		return

	# On the first run, always do a full backup. Incremental
	# will fail. Otherwise do a full backup when the size of
	# the increments since the most recent full backup are
	# large.
	try:
		full_backup = full_backup or should_force_full(config, env)
	except Exception as e:
		# This was the first call to duplicity, and there might
		# be an error already.
		print(e)
		sys.exit(1)

	# Stop services.
	def service_command(service, command, quit=None):
		# Execute silently, but if there is an error then display the output & exit.
		code, ret = shell('check_output', ["/usr/sbin/service", service, command], capture_stderr=True, trap=True)
		if code != 0:
			print(ret)
			if quit:
				sys.exit(code)

	service_command("php7.2-fpm", "stop", quit=True)
	service_command("postfix", "stop", quit=True)
	service_command("dovecot", "stop", quit=True)

	# Execute a pre-backup script that copies files outside the homedir.
	# Run as the STORAGE_USER user, not as root. Pass our settings in
	# environment variables so the script has access to STORAGE_ROOT.
	pre_script = os.path.join(backup_root, 'before-backup')
	if os.path.exists(pre_script):
		shell('check_call',
			['su', env['STORAGE_USER'], '-c', pre_script, config["target"]],
			env=env)

	# Run a backup of STORAGE_ROOT (but excluding the backups themselves!).
	# --allow-source-mismatch is needed in case the box's hostname is changed
	# after the first backup. See #396.
	try:
		shell('check_call', [
			"/usr/bin/duplicity",
			"full" if full_backup else "incr",
			"--verbosity", "warning", "--no-print-statistics",
			"--archive-dir", backup_cache_dir,
			"--exclude", backup_root,
			"--volsize", "250",
			"--gpg-options", "--cipher-algo=AES256",
			env["STORAGE_ROOT"],
			config["target"],
			"--allow-source-mismatch"
			] + rsync_ssh_options,
			get_env(env))
	finally:
		# Start services again.
		service_command("dovecot", "start", quit=False)
		service_command("postfix", "start", quit=False)
		service_command("php7.2-fpm", "start", quit=False)

	# Remove old backups. This deletes all backup data no longer needed
	# from more than 3 days ago.
	shell('check_call', [
		"/usr/bin/duplicity",
		"remove-older-than",
		"%dD" % config["min_age_in_days"],
		"--verbosity", "error",
		"--archive-dir", backup_cache_dir,
		"--force",
		config["target"]
		] + rsync_ssh_options,
		get_env(env))

	# From duplicity's manual:
	# "This should only be necessary after a duplicity session fails or is
	# aborted prematurely."
	# That may be unlikely here but we may as well ensure we tidy up if
	# that does happen - it might just have been a poorly timed reboot.
	shell('check_call', [
		"/usr/bin/duplicity",
		"cleanup",
		"--verbosity", "error",
		"--archive-dir", backup_cache_dir,
		"--force",
		config["target"]
		] + rsync_ssh_options,
		get_env(env))

	# Change ownership of backups to the user-data user, so that the after-bcakup
	# script can access them.
	if get_target_type(config) == 'file':
		shell('check_call', ["/bin/chown", "-R", env["STORAGE_USER"], backup_dir])

	# Execute a post-backup script that does the copying to a remote server.
	# Run as the STORAGE_USER user, not as root. Pass our settings in
	# environment variables so the script has access to STORAGE_ROOT.
	post_script = os.path.join(backup_root, 'after-backup')
	if os.path.exists(post_script):
		shell('check_call',
			['su', env['STORAGE_USER'], '-c', post_script, config["target"]],
			env=env)

	# Our nightly cron job executes system status checks immediately after this
	# backup. Since it checks that dovecot and postfix are running, block for a
	# bit (maximum of 10 seconds each) to give each a chance to finish restarting
	# before the status checks might catch them down. See #381.
	wait_for_service(25, True, env, 10)
	wait_for_service(993, True, env, 10)
Esempio n. 7
0
def perform_backup(full_backup):
    env = load_environment()

    exclusive_process("backup")

    backup_root = os.path.join(env["STORAGE_ROOT"], 'backup')
    backup_cache_dir = os.path.join(backup_root, 'cache')
    backup_dir = os.path.join(backup_root, 'encrypted')

    # In an older version of this script, duplicity was called
    # such that it did not encrypt the backups it created (in
    # backup/duplicity), and instead openssl was called separately
    # after each backup run, creating AES256 encrypted copies of
    # each file created by duplicity in backup/encrypted.
    #
    # We detect the transition by the presence of backup/duplicity
    # and handle it by 'dupliception': we move all the old *un*encrypted
    # duplicity files up out of the backup/duplicity directory (as
    # backup/ is excluded from duplicity runs) in order that it is
    # included in the next run, and we delete backup/encrypted (which
    # duplicity will output files directly to, post-transition).
    old_backup_dir = os.path.join(backup_root, 'duplicity')
    migrated_unencrypted_backup_dir = os.path.join(
        env["STORAGE_ROOT"], "migrated_unencrypted_backup")
    if os.path.isdir(old_backup_dir):
        # Move the old unencrypted files to a new location outside of
        # the backup root so they get included in the next (new) backup.
        # Then we'll delete them. Also so that they do not get in the
        # way of duplicity doing a full backup on the first run after
        # we take care of this.
        shutil.move(old_backup_dir, migrated_unencrypted_backup_dir)

        # The backup_dir (backup/encrypted) now has a new purpose.
        # Clear it out.
        shutil.rmtree(backup_dir)

    # On the first run, always do a full backup. Incremental
    # will fail. Otherwise do a full backup when the size of
    # the increments since the most recent full backup are
    # large.
    full_backup = full_backup or should_force_full(env)

    # Stop services.
    shell('check_call', ["/usr/sbin/service", "dovecot", "stop"])
    shell('check_call', ["/usr/sbin/service", "postfix", "stop"])

    # Get the encryption passphrase. secret_key.txt is 2048 random
    # bits base64-encoded and with line breaks every 65 characters.
    # gpg will only take the first line of text, so sanity check that
    # that line is long enough to be a reasonable passphrase. It
    # only needs to be 43 base64-characters to match AES256's key
    # length of 32 bytes.
    with open(os.path.join(backup_root, 'secret_key.txt')) as f:
        passphrase = f.readline().strip()
    if len(passphrase) < 43:
        raise Exception("secret_key.txt's first line is too short!")
    env_with_passphrase = {"PASSPHRASE": passphrase}

    # Update the backup mirror directory which mirrors the current
    # STORAGE_ROOT (but excluding the backups themselves!).
    try:
        shell('check_call', [
            "/usr/bin/duplicity", "full" if full_backup else "incr",
            "--archive-dir", backup_cache_dir, "--exclude", backup_root,
            "--volsize", "250", "--gpg-options", "--cipher-algo=AES256",
            env["STORAGE_ROOT"], "file://" + backup_dir
        ], env_with_passphrase)
    finally:
        # Start services again.
        shell('check_call', ["/usr/sbin/service", "dovecot", "start"])
        shell('check_call', ["/usr/sbin/service", "postfix", "start"])

    # Once the migrated backup is included in a new backup, it can be deleted.
    if os.path.isdir(migrated_unencrypted_backup_dir):
        shutil.rmtree(migrated_unencrypted_backup_dir)

    # Remove old backups. This deletes all backup data no longer needed
    # from more than 3 days ago.
    shell('check_call', [
        "/usr/bin/duplicity", "remove-older-than",
        "%dD" % keep_backups_for_days, "--archive-dir", backup_cache_dir,
        "--force", "file://" + backup_dir
    ], env_with_passphrase)

    # From duplicity's manual:
    # "This should only be necessary after a duplicity session fails or is
    # aborted prematurely."
    # That may be unlikely here but we may as well ensure we tidy up if
    # that does happen - it might just have been a poorly timed reboot.
    shell('check_call', [
        "/usr/bin/duplicity", "cleanup", "--archive-dir", backup_cache_dir,
        "--force", "file://" + backup_dir
    ], env_with_passphrase)

    # Change ownership of backups to the user-data user, so that the after-bcakup
    # script can access them.
    shell('check_call', ["/bin/chown", "-R", env["STORAGE_USER"], backup_dir])

    # Execute a post-backup script that does the copying to a remote server.
    # Run as the STORAGE_USER user, not as root. Pass our settings in
    # environment variables so the script has access to STORAGE_ROOT.
    post_script = os.path.join(backup_root, 'after-backup')
    if os.path.exists(post_script):
        shell('check_call', ['su', env['STORAGE_USER'], '-c', post_script],
              env=env)

    # Our nightly cron job executes system status checks immediately after this
    # backup. Since it checks that dovecot and postfix are running, block for a
    # bit (maximum of 10 seconds each) to give each a chance to finish restarting
    # before the status checks might catch them down. See #381.
    wait_for_service(25, True, env, 10)
    wait_for_service(993, True, env, 10)