Git initial commit

This commit is contained in:
Lars Hahn 2023-08-20 10:30:22 +02:00
commit 196861470a
14 changed files with 364 additions and 0 deletions

9
LICENSE Executable file
View File

@ -0,0 +1,9 @@
MIT License
Copyright (c) <year> <copyright holders>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

3
README.md Executable file
View File

@ -0,0 +1,3 @@
# cloud-backup
Ansible role repository in order to setup backup clientside tasks (what should be backuped) and masterside tasks (where should be backuped from where).

69
defaults/main.yml Executable file
View File

@ -0,0 +1,69 @@
---
cloud_apps: /opt
cloud_storage: /opt/storage
cloud_stage: prod
cloud_update: false
ssh_port: 1802
# clients push only daily backup
# server (!= client) will then pull daily backups
backup_client: true
# Server should pull 1 hour later the backups.
backup_hour_shift: 1
backup_times_weekday: "*"
backup_times_hour:
- 6
- 10
- 14
- 18
- 22
backup_times_minute: 0
backup_keep_days: 7
backup_owner: backup
backup_owner_ssh_key: "ssh-rsa THEREISSUCHRSAKEYYET"
backup_group: "{{ backup_owner }}"
backup_permission: 0770
backup_hosts:
- name: server1-name
domain: host1.my-domain.tld
ip: 001.112.223.123
- name: server2-name
domain: host2.my-domain.tld
backup_folder: "{{ cloud_apps }}/backup"
#backup_client_scripts:
# - /path/to/script -param
backup_client_scripts: []
#backup_client_script_envs:
# - var: VAR_NAME
# path: /some/path
# - var: VAR_NAME
# value: some_value
backup_client_script_envs: []
backup_lock: "{{ backup_folder }}/backup.lock"
#backup_targets:
# db:
# - type: mariadb
# name: db_name_mdb
# - type: pgsql
# name: db_name_pg
# mail:
# - type: file
# path: /path/to/some/file
# - type: mariadb
# name: db_name_mdb_mail
# file:
# - /path/to/some/file
# - /path/to/some/dir
backup_targets:
db: []
mail: []
file: []

1
handlers/main.yml Executable file
View File

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

16
meta/main.yml Executable file
View File

@ -0,0 +1,16 @@
---
galaxy_info:
role_name: backup
namespace: hahn-cloud
author: Lars Hahn
company: Data Learning
license: MIT
description: Role to setup a backup system + configuration.
min_ansible_version: 2.7
platforms:
- name: Debian
versions:
- 10
galaxy_tags:
- backup
dependencies: []

38
tasks/client.yml Executable file
View File

@ -0,0 +1,38 @@
---
- name: generate backup ssh user
user:
name: "{{ backup_owner }}"
group: "{{ backup_group }}"
comment: Backup user
shell: /bin/sh
- name: Setup User ssh_key
authorized_key:
user: "{{ backup_owner }}"
key: "{{ backup_owner_ssh_key }}"
- name: generate config lists
template:
src: backup/conf/{{ conf }}.list.j2
dest: "{{ backup_conf }}/{{ conf }}.list"
loop:
- filedir
- db
- mail
loop_control:
loop_var: conf
label: "{{ conf }}.list"
- name: setup backup script
template:
src: backup/backup_on_client.sh.j2
dest: "{{ backup_folder }}/backup.sh"
mode: 0740
- name: setup cronjob for client backup generation
cron:
name: "backup"
weekday: "{{ backup_times_weekday }}"
hour: "{{ backup_times_hour | join(',') }}"
minute: "{{ backup_times_minute }}"
job: "{{ backup_folder }}/backup.sh"

34
tasks/main.yml Executable file
View File

@ -0,0 +1,34 @@
---
# Basics
- name: install requirements for backup system
apt:
update_cache: yes
state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}"
install_recommends: yes
pkg:
- rsync
- name: setup storage folders for backup
file:
state: directory
path: "{{ dir.value.path }}"
owner: "{{ backup_owner }}"
group: "{{ backup_group }}"
mode: "{{ backup_permission }}"
with_dict: "{{ backup_storage }}"
loop_control:
loop_var: dir
label: "{{ dir.key }}"
- name: setup conf folder
file:
state: directory
path: "{{ backup_folder }}/conf"
- name: setup client backup structure
include_tasks: client.yml
when: backup_client
- name: setup server backup structure
include_tasks: server.yml
when: not backup_client

27
tasks/server.yml Executable file
View File

@ -0,0 +1,27 @@
---
- name: analyse backup hours for pulling server
set_fact:
backup_times_server_hour: "{{ backup_times_server_hour | default([]) + [item + backup_hour_shift] }}"
loop: "{{ backup_times_hour }}"
- name: generate backup user ssh key
user:
name: "{{ backup_owner }}"
generate_ssh_key: yes
ssh_key_type: rsa
ssh_key_bits: 4096
home: "{{ backup_folder }}"
- name: template main server backup pull script
template:
src: backup/backup_from_clients.sh.j2
dest: "{{ backup_folder }}/pull-backup.sh"
mode: 0740
- name: setup cronjob for server backup pull
cron:
name: "pull backups"
weekday: "{{ backup_times_weekday }}"
hour: "{{ backup_times_server_hour | join(',') }}"
minute: "{{ backup_times_minute }}"
job: "{{ backup_folder }}/pull-backup.sh"

View File

@ -0,0 +1,68 @@
#!/bin/bash
set -e -o pipefail
cd {{ backup_folder }}
if [ -f {{ backup_lock }} ]; then
echo "Backup lock! Cannot procceed as lock '{{ backup_lock }}' exist."
echo "Associated PID is $(cat {{ backup_lock }})"
exit 1
fi
echo "$$" > {{ backup_lock }}
BKPTIMESTAMP=$(date +"%y-%m-%d.%H")
BKP=$BKPTIMESTAMP"_backup"
# generate rolling folder
{% for target in backup_targets %}
{% for host in backup_hosts %}
{% if 'domain' in host %}
mkdir -p {{ target }}/{{ host.name + '_' + host.domain }}/$BKP
{% elif 'ip' in host %}
mkdir -p {{ target }}/{{ host.name + '_' + host.ip }}/$BKP
{% endif %}
{% endfor %}
{% endfor %}
# pull backups
{% for target in backup_targets %}
{% for host in backup_hosts %}
{% if 'domain' in host %}
rsync --rsh='ssh -p{{ ssh_port }} -i ~{{ backup_owner }}/.ssh/id_rsa' -chav --stats --ignore-missing-args {{ backup_owner }}@{{ host.domain }}:{{ backup_folder }}/{{ target }}.tgz {{ target }}/{{ host.name + '_' + host.domain }}/$BKP
{% elif 'ip' in host %}
rsync --rsh='ssh -p{{ ssh_port }} -i ~{{ backup_owner }}/.ssh/id_rsa' -chav --stats --ignore-missing-args {{ backup_owner }}@{{ host.ip }}:{{ backup_folder }}/{{ target }}.tgz {{ target }}/{{ host.name + '_' + host.ip }}/$BKP
{% endif %}
{% endfor %}
{% endfor %}
# link to latest
{% for target in backup_targets %}
{% for host in backup_hosts %}
{% if 'domain' in host %}
ln -sf {{ backup_folder }}/{{ target }}/{{ host.name + '_' + host.domain }}/$BKP {{ backup_folder }}/{{ target }}/{{ host.name + '_' + host.domain }}/LATEST
{% elif 'ip' in host %}
ln -sf {{ backup_folder }}/{{ target }}/{{ host.name + '_' + host.ip }}/$BKP {{ backup_folder }}/{{ target }}/{{ host.name + '_' + host.ip }}/LATEST
{% endif %}
{% endfor %}
{% endfor %}
# keep only last {{ backup_keep_days * ( backup_times_hour | length ) }}
{% for target in backup_targets %}
{% for host in backup_hosts %}
{% if 'domain' in host %}
DELETE_LIST=$(ls -1dtr {{ target }}/{{ host.name + '_' + host.domain }}/* | head -n -{{ backup_keep_days * ( backup_times_hour | length ) }})
{% elif 'ip' in host %}
DELETE_LIST=$(ls -1dtr {{ target }}/{{ host.name + '_' + host.ip }}/* | head -n -{{ backup_keep_days * ( backup_times_hour | length ) }})
{% endif %}
if (( $( echo $DELETE_LIST | wc -w ) > 0 )); then
rm -rf $DELETE_LIST
fi
{% endfor %}
{% endfor %}
rm {{ backup_lock }}

View File

@ -0,0 +1,72 @@
#!/bin/bash
set -e -o pipefail
if [ -f {{ backup_lock }} ]; then
echo "Backup lock! Cannot procceed as lock '{{ backup_lock }}' exist."
echo "Associated PID is $(cat {{ backup_lock }})"
exit 1
fi
echo "$$" > {{ backup_lock }}
cd {{ backup_folder }}
# Load script envs
echo "Load script environment variables"
{% for scr_env in backup_client_script_envs %}
{% if 'path' in scr_env %}
export {{ scr_env.var }}={{ scr_env.path }}:${{ scr_env.var }}
{% else %}
export {{ scr_env.var }}={{ scr_env.value }}
{% endif %}
{% endfor %}
# Perform client pre backup scripts for
echo "Perform pre-backup scripts"
{% for cmd in backup_client_scripts %}
{{ cmd }};
{% endfor %}
# Backup files and directories
echo "Copy files and directories"
while read DIRFILE; do
echo " - Copy $DIRFILE"
BACKUP_SUBDIR=$(dirname $DIRFILE)
mkdir -p "{{ backup_storage.file.path }}/$BACKUP_SUBDIR"
cp -r -u $DIRFILE "{{ backup_storage.file.path }}/$DIRFILE"
done < {{ backup_conf }}/filedir.list
rm -f file.tgz #remove old backup
tar czfv file.tgz file
rm -rf file/*
# Backup databases
echo "Generate DB dumps"
while read DBS; do
DBTYPE=$(echo $DBS | cut -f 1 -d "{{ backup_conf_db_sep }}" );
DBNAME=$(echo $DBS | cut -f 2 -d "{{ backup_conf_db_sep }}" );
if [ $DBTYPE == "mariadb" ] || [ $DBTYPE == "mysql" ]; then
echo "- dump to {{ backup_storage.db.path }}/$DBTYPE/$DBNAME.sql"
mkdir -p {{ backup_storage.db.path }}/$DBTYPE;
mysqldump $DBNAME | gzip -c > {{ backup_storage.db.path }}/$DBTYPE/$DBNAME.sql.gz;
elif [ $DBTYPE == "pgsql" ]; then
echo "- dump to {{ backup_storage.db.path }}/$DBTYPE/$DBNAME.sql"
mkdir -p {{ backup_storage.db.path }}/$DBTYPE;
sudo -u postgres bash -c "pg_dump $DBNAME | gzip -c > /tmp/$DBNAME.sql.gz";
mv /tmp/$DBNAME.sql.gz /backup/db/$DBTYPE/$DBNAME.sql.gz;
else
echo "- ERROR! DB type '$DBTYPE' for '$DBNAME' is unknown to backup."
fi
done < {{ backup_conf }}/db.list
rm -f db.tgz #remove old backup
tar czfv db.tgz db
rm -rf db/*
#Mail
rm -f mail.tgz #remove old backup
tar czfv mail.tgz mail
rm -rf mail/*
rm {{ backup_lock }}
chown {{ backup_owner }}:{{ backup_group }} {{ backup_folder }}/*.tgz

View File

@ -0,0 +1,5 @@
{% if 'db' in backup_targets %}
{% for db in backup_targets.db %}
{{ db.type }}{{ backup_conf_db_sep }}{{ db.name }}
{% endfor %}
{% endif %}

View File

@ -0,0 +1,5 @@
{% if 'file' in backup_targets %}
{% for filedir in backup_targets.file %}
{{ filedir }}
{% endfor %}
{% endif %}

View File

@ -0,0 +1,5 @@
{% if 'mail' in backup_targets %}
{% for mail in backup_targets.mail %}
{{ mail }}
{% endfor %}
{% endif %}

12
vars/main.yml Executable file
View File

@ -0,0 +1,12 @@
---
backup_conf: "{{ backup_folder }}/conf"
backup_conf_db_sep: ":"
backup_storage:
mail:
path: "{{ backup_folder }}/mail"
db:
path: "{{ backup_folder }}/db"
file:
path: "{{ backup_folder }}/file"