Add fedora compatibility

Add fedora installation for Framework.
This commit is contained in:
ducoterra
2022-02-06 17:29:55 -05:00
parent 8b3002316f
commit 8fbf384b2d
153 changed files with 1026 additions and 120 deletions

View File

@@ -0,0 +1,29 @@
---
language: python
python: "2.7"
# Use the new container infrastructure
sudo: false
# Install ansible
addons:
apt:
packages:
- python-pip
install:
# Install ansible
- pip install ansible
# Check ansible version
- ansible --version
# Create ansible.cfg with correct roles_path
- printf '[defaults]\nroles_path=../' >ansible.cfg
script:
# Basic role syntax check
- ansible-playbook tests/test.yml -i tests/inventory --syntax-check
notifications:
webhooks: https://galaxy.ansible.com/api/v1/notifications/

View File

@@ -0,0 +1,57 @@
Role Name
=========
Configures automatic BTRFS backups for "/" and "/home"
Requirements
------------
Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
Role Variables
--------------
```yaml
snapshots:
path: /.snapshots
disk:
name: backup0
# uuid: 1d7ce570-e695-47a0-9dda-5f14b5b20e21
uuid: 7c482f9b-2e1a-494c-9a93-ddecd483f2b2
# password: /home/ducoterra/.lukskeys/backup0
password: /home/ducoterra/.lukskeys/manjaro-laptop-iscsi
backups:
- /
- /home
notifications:
user:
name: ducoterra
uid: 1000
```
Dependencies
------------
A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
Example Playbook
----------------
Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
- hosts: servers
roles:
- { role: username.rolename, x: 42 }
License
-------
BSD
Author Information
------------------
An optional section for the role authors to include contact information, or a website (HTML is not allowed).

View File

@@ -0,0 +1,2 @@
---
# defaults file for arch_backup

View File

@@ -0,0 +1,2 @@
---
# handlers file for arch_backup

View File

@@ -0,0 +1,52 @@
galaxy_info:
author: your name
description: your role description
company: your company (optional)
# If the issue tracker for your role is not on github, uncomment the
# next line and provide a value
# issue_tracker_url: http://example.com/issue/tracker
# Choose a valid license ID from https://spdx.org - some suggested licenses:
# - BSD-3-Clause (default)
# - MIT
# - GPL-2.0-or-later
# - GPL-3.0-only
# - Apache-2.0
# - CC-BY-4.0
license: license (GPL-2.0-or-later, MIT, etc)
min_ansible_version: 2.1
# If this a Container Enabled role, provide the minimum Ansible Container version.
# min_ansible_container_version:
#
# Provide a list of supported platforms, and for each platform a list of versions.
# If you don't wish to enumerate all versions for a particular platform, use 'all'.
# To view available platforms and versions (or releases), visit:
# https://galaxy.ansible.com/api/v1/platforms/
#
# platforms:
# - name: Fedora
# versions:
# - all
# - 25
# - name: SomePlatform
# versions:
# - all
# - 1.0
# - 7
# - 99.99
galaxy_tags: []
# List tags for your role here, one per line. A tag is a keyword that describes
# and categorizes the role. Users find roles by searching for tags. Be sure to
# remove the '[]' above, if you add tags to this list.
#
# NOTE: A tag is limited to a single word comprised of alphanumeric characters.
# Maximum 20 tags per role.
dependencies: []
# List your role dependencies here, one per line. Be sure to remove the '[]' above,
# if you add dependencies to this list.

View File

@@ -0,0 +1,43 @@
---
# Backup
- name: Ensure snapshot directory
file:
state: directory
path: "{{ snapshots.path }}"
become: yes
- name: Ensure /usr/local/scripts exists
file:
state: directory
path: '/usr/local/scripts'
become: yes
- name: Template btrfs_backup.sh
ansible.builtin.template:
src: btrfs_backup.sh.j2
dest: /usr/local/scripts/btrfs_backup.sh
owner: root
group: root
mode: '0744'
become: yes
- name: Template open_backup.sh
ansible.builtin.template:
src: open_backup.sh.j2
dest: /usr/local/scripts/open_backup.sh
owner: root
group: root
mode: '0744'
become: yes
- name: Ensure hourly backups of each item in backups
ansible.builtin.cron:
name: "hourly backup of {{ item }}"
minute: "0"
job: "export SOURCE_DIR={{ item }}; /usr/local/scripts/btrfs_backup.sh"
become: yes
loop: "{{ backups }}"
- name: Ensure cronie service started
ansible.builtin.systemd:
name: cronie
state: restarted
daemon_reload: yes
enabled: yes
become: yes

View File

@@ -0,0 +1,251 @@
#!/bin/bash
# 1. Create uuid for backup mount
# 2. Unlock luks-uuid
# 3. Create /tmp/uuid
# 4. Mount /tmp/uuid
# 5. btrfs send
# 5.5 Update
# 6. umount
# 7. rm /tmp/uuid
# 8. luksclose
function exit_success {
# Unmount /tmp/uuid
log "INFO" "Unmounting $BACKUP_DRIVE_MNT"
umount $BACKUP_DRIVE_MNT
# Exit
exit 0
}
function exit_fail {
# Unmount /tmp/uuid
log "INFO" "Unmounting $BACKUP_DRIVE_MNT"
umount $BACKUP_DRIVE_MNT
# Exit
exit 1
}
function get_latest {
DIR=$1
if [ -f $DIR/$LATEST ]; then
echo $(cat $DIR/$LATEST)
else
echo ""
fi
}
function update_latest {
DIR=$1
NAME=$2
echo $2 > $DIR/$LATEST
}
function log {
LEVEL=$1
MESSAGE=$2
echo "$LEVEL: $MESSAGE"
}
function notify {
LEVEL=$1
MESSAGE=$2
log "$LEVEL" "$MESSAGE"
sudo -E -u $USER notify-send "$LEVEL" "$MESSAGE"
}
# Backup info
export BACKUP_DRIVE_UUID={{ disk.uuid }}
export BACKUP_DRIVE_PASSWORD={{ disk.password }}
export BACKUP_DRIVE_TMP_UUID=$(uuidgen)
export BACKUP_DRIVE_NAME=luks-$BACKUP_DRIVE_UUID
# For notifications
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/{{ notifications.user.uid }}/bus
export USER={{ notifications.user.name }}
export SOURCE_DIR=${SOURCE_DIR:=/}
# Fix basename / showing up as "/" -> change to "root"
if [ $(basename $SOURCE_DIR) = / ]; then
export SNAPSHOT_PREFIX=${SNAPSHOT_PREFIX:=root}
else
export SNAPSHOT_PREFIX=${SNAPSHOT_PREFIX:=$(basename $SOURCE_DIR)}
fi
# Set snapshot prefix based on basename
export SNAPSHOT_TIME=$(date +"%y_%m_%d-%H.%M")
export SNAPSHOT_NAME=$SNAPSHOT_PREFIX-$SNAPSHOT_TIME
export SNAPSHOT_DIR=${SNAPSHOT_DIR:=/.snapshots}
export LATEST=$SNAPSHOT_PREFIX-latest
export BACKUP_DRIVE_MNT=/tmp/$BACKUP_DRIVE_TMP_UUID
export BACKUP_DIR=${BACKUP_DIR:=$BACKUP_DRIVE_MNT/$(hostname)}
# Show snapshot settings
echo "SOURCE_DIR" "$SOURCE_DIR"
echo "SNAPSHOT_PREFIX" "$SNAPSHOT_PREFIX"
echo "SNAPSHOT_TIME" "$SNAPSHOT_TIME"
echo "SNAPSHOT_NAME" "$SNAPSHOT_NAME"
echo "SNAPSHOT_DIR" "$SNAPSHOT_DIR"
echo "LATEST" "$LATEST"
echo "BACKUP_DRIVE_MNT" "$BACKUP_DRIVE_MNT"
echo "BACKUP_DIR" "$BACKUP_DIR"
# Create readonly snapshot
log "INFO" "Creating snapshot from $SOURCE_DIR as $SNAPSHOT_DIR/$SNAPSHOT_NAME"
if [ -d $SNAPSHOT_DIR/$SNAPSHOT_NAME ]; then
log "WARN" "Snapshot $SNAPSHOT_DIR/$SNAPSHOT_NAME already created. Skipping"
else
btrfs subvolume snapshot -r $SOURCE_DIR $SNAPSHOT_DIR/$SNAPSHOT_NAME
fi
# Update latest in snapshot dir
log "INFO" "Updating latest in $SNAPSHOT_DIR to $SNAPSHOT_NAME."
update_latest $SNAPSHOT_DIR $SNAPSHOT_NAME
# Unlock backup drive
if [ -L /dev/disk/by-uuid/$BACKUP_DRIVE_UUID ]; then
cryptsetup luksOpen /dev/disk/by-uuid/$BACKUP_DRIVE_UUID $BACKUP_DRIVE_NAME --key-file=$BACKUP_DRIVE_PASSWORD
cryptsetup status /dev/mapper/$BACKUP_DRIVE_NAME
else
log "INFO" "Backup drive $BACKUP_DRIVE_UUID not found"
log "INFO" "Snapshot $SNAPSHOT_NAME completed successfully."
notify "WARN" "Drive $BACKUP_DRIVE_UUID could not be found. Snapshot completed without backup."
exit 0
fi
if [ $? = 0 ]; then
log "INFO" "Drive $BACKUP_DRIVE_UUID unlocked"
else
notify "ERROR" "Drive $BACKUP_DRIVE_UUID could not be unlocked."
exit_fail
fi
# Create /tmp/uuid
log "INFO" "Creating $BACKUP_DRIVE_MNT"
mkdir $BACKUP_DRIVE_MNT
# Mount /tmp/uuid
log "INFO" "Mounting /dev/mapper/$BACKUP_DRIVE_NAME"
mount -t btrfs -o compress=zstd /dev/mapper/$BACKUP_DRIVE_NAME $BACKUP_DRIVE_MNT
if [ $? = 0 ]; then
log "INFO" "Drive $BACKUP_DRIVE_UUID mounted at $BACKUP_DRIVE_MNT"
else
notify "ERROR" "Drive $BACKUP_DRIVE_NAME could not be mounted."
exit_fail
fi
# First check if the snapshot dir has a "latest" snapshot
# This will be needed to send an incremental snapshot
LATEST_SNAPSHOT="$(get_latest $SNAPSHOT_DIR)"
log "INFO" "Latest snapshot is $LATEST_SNAPSHOT"
# Next, check if the backup drive has a "latest" snapshot
LATEST_BACKUP="$(get_latest $BACKUP_DIR)"
log "INFO" "Latest backup is $LATEST_BACKUP"
# Now check if the "latest" snapshots match
# btrfs requires both the sending drive and receiving drive have
# matching parent snapshots.
#
# There are a few scenarios to cover
# 1. Neither the backup drive nor the local snapshot dir have a "latest"
# This can happen if the backup occurs before any snapshots are
# taken. Don't send anything.
# 2. The backup drive has a "latest" but the snapshot dir doesn't
# This can happen when the local drive is restored from backup
# but the snapshot dir didn't copy over. nothing to send.
# 3. The backup drive and snapshot dir have a "latest" and they are the
# same.
# Send backup with parent as normal.
# 4. The snapshot dir has a "latest" but the backup drive doesn't
# This can happen when backing up for the first time. Send the
# snapshot without a parent
# 5. Both the snapshot dir and backup drive have a latest, but they are
# out of sync.
# This can happen when snapshots are taken with the backup drive
# disconnected. There's a few sub-scenarios here:
# a. The snapshot dir has the "latest" snapshot from the backup dir,
# it's just older than the "latest" snapshot in the snapshot dir
# Re-sync the "latest" snapshot dir with the one in the
# backup dir. Send as normal with parents.
# b. The snapshot dir does not have the "latest" snapshot from the
# backup dir.
# Here be dragons. Something went wrong and will likely need
# to be manually reconfigured. Raise a critical alert.
# Scenario 1 and 2
if [ "$LATEST_SNAPSHOT" = "" ]; then
notify "WARN" "Neither the snapshot dir nor the backup drive has a 'latest' snapshot."
exit_success
fi
# Scenario 3
if [ "$LATEST_SNAPSHOT" = "$LATEST_BACKUP" ]; then
log "INFO" "Proceeding with backups as normal."
# Send incremental snapshot
btrfs send -p $SNAPSHOT_DIR/$LATEST_SNAPSHOT $SNAPSHOT_DIR/$SNAPSHOT_NAME | btrfs receive $BACKUP_DIR
if [ $? != 0 ]; then
notify "ERROR" "btrfs send -p $SNAPSHOT_DIR/$LATEST_SNAPSHOT $SNAPSHOT_DIR/$SNAPSHOT_NAME failed."
exit_fail
fi
# Update latest in backup dir
update_latest $BACKUP_DIR $SNAPSHOT_NAME
# Update latest in snapshot dir
update_latest $SNAPSHOT_DIR $SNAPSHOT_NAME
# Exit
sudo -E -u $USER notify-send "Backup completed" "INFO: Backup $SNAPSHOT_NAME completed successfully."
exit_success
fi
# Scenario 4
if [ "$LATEST_BACKUP" = "" ]; then
log "INFO" "No prior backups detected. Sending full backup."
# Send incremental snapshot
btrfs send $SNAPSHOT_DIR/$SNAPSHOT_NAME | btrfs receive $BACKUP_DIR
if [ $? != 0 ]; then
notify "ERROR" "btrfs send $SNAPSHOT_DIR/$SNAPSHOT_NAME failed."
exit_fail
fi
# Update latest in backup dir
update_latest $BACKUP_DIR $SNAPSHOT_NAME
# Update latest in snapshot dir
update_latest $SNAPSHOT_DIR $SNAPSHOT_NAME
# Exit
notify "INFO" "Backup $SNAPSHOT_NAME completed successfully."
exit_success
fi
# Scenario 5a
log "INFO" "Detected drift. Attempting to synchronize latest snapshot with backup. Set to $LATEST_BACKUP."
if [ -d $SNAPSHOT_DIR/$LATEST_BACKUP ]; then
log "INFO" "$LATEST_BACKUP found in snapshot dir. Synchronizing and proceeding."
btrfs send -p $SNAPSHOT_DIR/$LATEST_BACKUP $SNAPSHOT_DIR/$SNAPSHOT_NAME | btrfs receive $BACKUP_DIR
if [ $? != 0 ]; then
notify "ERROR" "btrfs send -p $SNAPSHOT_DIR/$LATEST_SNAPSHOT $SNAPSHOT_DIR/$SNAPSHOT_NAME failed."
exit_fail
fi
# Update latest in backup dir
update_latest $BACKUP_DIR $SNAPSHOT_NAME
# Update latest in snapshot dir
update_latest $SNAPSHOT_DIR $SNAPSHOT_NAME
# Exit
notify "INFO" "Backup $SNAPSHOT_NAME completed successfully."
exit_success
# Scenario 5b
else
log "ERROR" "Something went wrong. $LATEST_BACKUP not found in $SNAPSHOT_DIR."
notify "ERROR" "$LATEST_BACKUP not found in $SNAPSHOT_DIR."
exit_fail
fi

View File

@@ -0,0 +1,47 @@
# Backup info
export BACKUP_DRIVE_UUID={{ disk.uuid }}
export BACKUP_DRIVE_PASSWORD={{ disk.password }}
export BACKUP_DRIVE_NAME=luks-$BACKUP_DRIVE_UUID
export BACKUP_DRIVE_MNT=/mnt/$BACKUP_DRIVE_NAME
# For notifications
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/{{ notifications.user.uid }}/bus
export USER={{ notifications.user.name }}
function log {
LEVEL=$1
MESSAGE=$2
echo "$LEVEL: $MESSAGE"
}
# Unlock backup drive
if [ -L /dev/disk/by-uuid/$BACKUP_DRIVE_UUID ]; then
cryptsetup luksOpen /dev/disk/by-uuid/$BACKUP_DRIVE_UUID $BACKUP_DRIVE_NAME --key-file=$BACKUP_DRIVE_PASSWORD
cryptsetup status /dev/mapper/$BACKUP_DRIVE_NAME
else
log "ERROR" "Drive $BACKUP_DRIVE_UUID could not be found."
exit 1
fi
if [ $? = 0 ]; then
log "INFO" "Drive $BACKUP_DRIVE_UUID unlocked"
else
log "ERROR" "Drive $BACKUP_DRIVE_UUID could not be unlocked."
exit 1
fi
# Create /mnt/uuid
log "INFO" "Creating $BACKUP_DRIVE_NAME"
mkdir -p $BACKUP_DRIVE_MNT
# Mount /mnt/uuid
log "INFO" "Mounting /dev/mapper/$BACKUP_DRIVE_NAME"
mount -t btrfs -o compress=zstd /dev/mapper/$BACKUP_DRIVE_NAME $BACKUP_DRIVE_MNT
if [ $? = 0 ]; then
log "INFO" "Drive $BACKUP_DRIVE_UUID mounted at $BACKUP_DRIVE_MNT"
exit 0
else
log "ERROR" "Drive $BACKUP_DRIVE_NAME could not be mounted."
exit 1
fi

View File

@@ -0,0 +1,2 @@
localhost

View File

@@ -0,0 +1,5 @@
---
- hosts: localhost
remote_user: root
roles:
- arch_backup

View File

@@ -0,0 +1 @@
---