├── tasks ├── main.yml ├── configure.yml ├── snapraid-runner.yml ├── install-debian.yml └── cron.yml ├── meta └── main.yml ├── .github └── workflows │ └── main.yml ├── templates ├── snapraid.conf.j2 └── snapraid-runner.conf.j2 ├── README.md └── defaults └── main.yml /tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: install snapraid 4 | include_tasks: install-debian.yml 5 | when: ansible_os_family == 'Debian' and snapraid_install 6 | 7 | - name: configure snapraid 8 | include_tasks: configure.yml 9 | 10 | - name: install and configure snapraid-runner 11 | include_tasks: snapraid-runner.yml 12 | when: snapraid_runner 13 | 14 | - name: schedule snapraid 15 | include_tasks: cron.yml 16 | when: not snapraid_runner 17 | -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: Alex Kretzschmar 4 | role_name: snapraid 5 | description: Installs SnapRAID and configures automatic parity runs 6 | issue_tracker_url: https://github.com/ironicbadger/ansible-role-snapraid/issues 7 | license: GPLv2 8 | min_ansible_version: 2.4 9 | platforms: 10 | - name: EL 11 | versions: 12 | - 6 13 | - 7 14 | - name: Fedora 15 | versions: 16 | - all 17 | - name: Debian 18 | versions: 19 | - jessie 20 | - name: Ubuntu 21 | versions: 22 | - trusty 23 | - xenial 24 | - bionic 25 | categories: 26 | - system 27 | - web 28 | dependencies: [] -------------------------------------------------------------------------------- /tasks/configure.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: check valid configuration 4 | block: 5 | - fail: 6 | msg: No data disks defined 7 | when: snapraid_data_disks | length == 0 8 | - fail: 9 | msg: No parity disks defined 10 | when: snapraid_parity_disks | length == 0 11 | - fail: 12 | msg: No content files defined 13 | when: 14 | - snapraid_content_files | length == 0 15 | - snapraid_data_disks | selectattr('content') | length == 0 16 | - snapraid_parity_disks | selectattr('content') | length == 0 17 | 18 | - name: install snapraid config file 19 | template: 20 | src: snapraid.conf.j2 21 | dest: /etc/snapraid.conf 22 | owner: root 23 | group: root 24 | mode: 0775 25 | -------------------------------------------------------------------------------- /tasks/snapraid-runner.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: clone snapraid-runner 4 | git: 5 | repo: https://github.com/Chronial/snapraid-runner.git 6 | dest: /opt/snapraid-runner 7 | 8 | - name: install snapraid-runner configuration file 9 | template: 10 | src: snapraid-runner.conf.j2 11 | dest: "{{ snapraid_runner_conf }}" 12 | owner: root 13 | group: root 14 | mode: 0775 15 | 16 | - name: setup cron job snapraid-runner 17 | cron: 18 | user: "root" 19 | job: "{{ item.job }}" 20 | name: "{{ item.name }}" 21 | weekday: "{{ item.weekday | default ('*') }}" 22 | minute: "{{ item.minute | default ('00')}}" 23 | hour: "{{ item.hour | default ('00') }}" 24 | dom: "{{ item.dom|default('*') }}" 25 | with_items: 26 | - "{{ snapraid_runner_cron_jobs }}" 27 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 'on': 3 | push: 4 | tags: 5 | - '*' 6 | 7 | env: 8 | GALAXY_USERNAME: IronicBadger 9 | 10 | jobs: 11 | 12 | release: 13 | name: Release 14 | runs-on: ubuntu-latest 15 | steps: 16 | 17 | - name: Set up Python 3. 18 | uses: actions/setup-python@v2 19 | with: 20 | python-version: '3.x' 21 | 22 | - name: Install Ansible. 23 | run: pip3 install ansible-core 24 | 25 | # Galaxy uses CamelCase username but GitHub is all lowercase 26 | # This concatenates the versions together to work with 27 | # And triggers an import on Galaxy 28 | - name: Trigger a new import on Galaxy. 29 | run: >- 30 | ansible-galaxy role import --api-key ${{ secrets.GALAXY_API_KEY }} 31 | $(echo ${{ env.GALAXY_USERNAME }}) $(echo ${{ github.repository }} | cut -d/ -f2) -------------------------------------------------------------------------------- /tasks/install-debian.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: check whether snapraid is installed 4 | shell: "dpkg-query -W '{{ snapraid_apt_package_name }}'" 5 | ignore_errors: True 6 | register: is_installed 7 | changed_when: "is_installed.rc != 0" 8 | 9 | - name: install snapraid? 10 | set_fact: 11 | install_snapraid: "{{ snapraid_force_install == true or is_installed.failed == true }}" 12 | 13 | - name: build snapraid | clone git repo 14 | git: 15 | repo: https://github.com/IronicBadger/docker-snapraid.git 16 | dest: /tmp/snapraid 17 | force: yes 18 | when: install_snapraid 19 | 20 | - name: build snapraid | make build script executable 21 | file: 22 | path: /tmp/snapraid/build.sh 23 | mode: 0775 24 | when: install_snapraid 25 | 26 | - name: build snapraid | build .deb package 27 | shell: cd /tmp/snapraid && ./build.sh 28 | when: install_snapraid 29 | 30 | - name: build snapraid | install built .deb 31 | apt: 32 | deb: /tmp/snapraid/build/snapraid-from-source.deb 33 | state: present 34 | when: install_snapraid 35 | -------------------------------------------------------------------------------- /templates/snapraid.conf.j2: -------------------------------------------------------------------------------- 1 | # {{ ansible_managed }} 2 | # 3 | # SnapRAID configuration file 4 | 5 | # Parity location(s) 6 | {% for disk in snapraid_parity_disks %} 7 | {{ loop.index}}-parity {{ disk.path }}/snapraid.parity 8 | {% endfor %} 9 | 10 | # Content file location(s) 11 | {% for content_file in snapraid_content_files %} 12 | content {{ content_file }} 13 | {% endfor %} 14 | {% for disk in snapraid_parity_disks %} 15 | {% if disk.content == true %} 16 | content {{ disk.path }}/snapraid.content 17 | {% endif %} 18 | {% endfor %} 19 | {% for disk in snapraid_data_disks %} 20 | {% if disk.content == true %} 21 | content {{ disk.path }}/.snapraid.content 22 | {% endif %} 23 | {% endfor %} 24 | 25 | # Data disks 26 | {% for disk in snapraid_data_disks %} 27 | data d{{ loop.index }} {{ disk.path }}{{ disk.data_path | default('') }} 28 | {% endfor %} 29 | 30 | # Excludes hidden files and directories (uncomment to enable). 31 | {% if snapraid_config_hidden_files_enabled %} 32 | {{ snapraid_config_hidden_files }} 33 | {% endif %} 34 | 35 | {% for item in snapraid_config_excludes %} 36 | exclude {{ item }} 37 | {% endfor %} 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ansible-role-snapraid 2 | 3 | An ansible role to install and configure [snapraid](https://www.snapraid.it/) and (optionally) [snapraid-runner](https://github.com/Chronial/snapraid-runner). 4 | 5 | ## Features 6 | 7 | - Installation and configuration of `snapraid-runner` to aid scrubbing (optional) 8 | - Automated creation of `sync` and `scrub` jobs 9 | - [Healthchecks.io](https://healthchecks.io/) integration for cron jobs (optional) 10 | 11 | ## Configuration 12 | 13 | This role has [many](./defaults/main.yml) variables which can be configured. 14 | 15 | ### Example 16 | 17 | ```yaml 18 | snapraid_install: false 19 | snapraid_runner: false 20 | 21 | snapraid_data_disks: 22 | - path: /mnt/disk1 23 | content: true 24 | - path: /mnt/disk2 25 | content: true 26 | - path: /mnt/disk3 27 | - path: /mnt/disk4 28 | 29 | snapraid_parity_disks: 30 | - path: /mnt/parity1 31 | content: true 32 | 33 | snapraid_content_files: 34 | - /mnt/other-drive/snapraid.content 35 | - /var/snapraid.content 36 | 37 | snapraid_config_excludes: 38 | - "*.unrecoverable" 39 | - /lost+found/ 40 | - "*.!sync" 41 | - /tmp/ 42 | 43 | snapraid_scrub_schedule: 44 | hour: 5 45 | weekday: 4 46 | ``` 47 | -------------------------------------------------------------------------------- /tasks/cron.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Schedule snapraid sync 4 | cron: 5 | name: snapraid sync 6 | job: snapraid sync {% if snapraid_sync_healthcheck_io_uuid %}&& curl -fsS -m 10 --retry 5 -o /dev/null {{ snapraid_healthcheck_io_host }}/{{ snapraid_sync_healthcheck_io_uuid }}{% endif %} 7 | user: root 8 | weekday: "{{ snapraid_sync_schedule.weekday | default ('*') }}" 9 | minute: "{{ snapraid_sync_schedule.minute | default ('0')}}" 10 | hour: "{{ snapraid_sync_schedule.hour | default ('0') }}" 11 | dom: "{{ snapraid_sync_schedule.dom|default('*') }}" 12 | 13 | - name: Schedule snapraid scrub 14 | cron: 15 | name: snapraid scrub 16 | job: snapraid scrub --plan {{ snapraid_scrub_percent }} --older-than {{ snapraid_scrub_age }} {% if snapraid_scrub_healthcheck_io_uuid %}&& curl -fsS -m 10 --retry 5 -o /dev/null {{ snapraid_healthcheck_io_host }}/{{ snapraid_scrub_healthcheck_io_uuid }}{% endif %} 17 | user: root 18 | weekday: "{{ snapraid_scrub_schedule.weekday | default ('*') }}" 19 | minute: "{{ snapraid_scrub_schedule.minute | default ('0')}}" 20 | hour: "{{ snapraid_scrub_schedule.hour | default ('0') }}" 21 | dom: "{{ snapraid_scrub_schedule.dom|default('*') }}" 22 | -------------------------------------------------------------------------------- /templates/snapraid-runner.conf.j2: -------------------------------------------------------------------------------- 1 | [snapraid] 2 | ; path to the snapraid executable 3 | executable = {{ snapraid_bin_path }} 4 | ; path to the snapraid config to be used 5 | config = {{ snapraid_config_path }} 6 | ; abort operation if there are more deletes than this, set to -1 to disable 7 | deletethreshold = {{ snapraid_runner_delete_threshold }} 8 | ; if you want touch to be run each time 9 | touch = {{ snapraid_runner_touch }} 10 | 11 | [logging] 12 | ; logfile to write to, leave empty to disable 13 | file = {{ snapraid_runner_logfile }} 14 | ; maximum logfile size in KiB, leave empty for infinite 15 | maxsize = 5000 16 | 17 | [email] 18 | ; when to send an email, comma-separated list of [success, error] 19 | sendon = {{ snapraid_runner_email_sendon }} 20 | ; set to false to get full program output via email 21 | short = true 22 | subject = {{ snapraid_runner_email_subject }} 23 | from = {{ snapraid_runner_email_address_from }} 24 | to = {{ snapraid_runner_email_address_to }} 25 | 26 | [smtp] 27 | host = {{ snapraid_runner_smtp_host }} 28 | ; leave empty for default port 29 | port = {{ snapraid_runner_smtp_port }} 30 | ; set to "true" to activate 31 | ssl = {{ snapraid_runner_use_ssl }} 32 | user = {{ snapraid_runner_email_address }} 33 | password = {{ snapraid_runner_email_pass }} 34 | 35 | [scrub] 36 | ; set to true to run scrub after sync 37 | enabled = {{ snapraid_runner_scrub }} 38 | percentage = {{ snapraid_scrub_percent }} 39 | older-than = {{ snapraid_scrub_age }} 40 | -------------------------------------------------------------------------------- /defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | snapraid_install: true 4 | snapraid_runner: true 5 | 6 | snapraid_apt_package_name: snapraid 7 | snapraid_bin_path: /usr/local/bin/snapraid 8 | snapraid_force_install: false 9 | 10 | snapraid_runner_healthcheck_io_uuid: "" 11 | snapraid_healthcheck_io_host: https://hc-ping.com 12 | 13 | snapraid_runner_email_address: "" 14 | snapraid_runner_email_pass: "" 15 | snapraid_runner_email_address_from: "{{ snapraid_runner_email_address }}" 16 | snapraid_runner_email_address_to: "{{ snapraid_runner_email_address }}" 17 | snapraid_runner_email_sendon: "error" 18 | snapraid_runner_email_subject: "[SnapRAID] Status Report:" 19 | 20 | snapraid_runner_smtp_host: smtp.gmail.com 21 | snapraid_runner_smtp_port: 465 22 | snapraid_runner_use_ssl: true 23 | 24 | snapraid_content_files: 25 | - /var/snapraid.content 26 | 27 | snapraid_config_excludes: [] 28 | snapraid_config_hidden_files_enabled: false 29 | snapraid_config_hidden_files: nohidden 30 | snapraid_config_path: /etc/snapraid.conf 31 | 32 | snapraid_runner_path: /opt/snapraid-runner/snapraid-runner 33 | snapraid_runner_conf: "{{ snapraid_runner_path }}.conf" 34 | snapraid_runner_bin: "{{ snapraid_runner_path }}.py" 35 | snapraid_runner_command: "python3 {{ snapraid_runner_bin }} -c {{ snapraid_runner_conf }} {% if snapraid_runner_healthcheck_io_uuid %}&& curl -fsS -m 10 --retry 5 -o /dev/null {{ snapraid_healthcheck_io_host }}/{{ snapraid_runner_healthcheck_io_uuid }} > /dev/null{% endif %}" 36 | snapraid_runner_scrub: true 37 | snapraid_scrub_percent: 22 38 | snapraid_scrub_age: 8 39 | snapraid_runner_touch: true 40 | snapraid_runner_delete_threshold: 250 41 | snapraid_runner_logfile: /var/log/snapraid.log 42 | 43 | snapraid_runner_cron_jobs: 44 | - { job: '{{ snapraid_runner_command }}', name: 'snapraid_runner', weekday: '*', hour: '01' } 45 | 46 | snapraid_sync_schedule: 47 | minute: 0 48 | hour: 0 49 | snapraid_sync_healthcheck_io_uuid: "" 50 | 51 | snapraid_scrub_schedule: 52 | minute: 0 53 | hour: 0 54 | weekday: 0 55 | snapraid_scrub_healthcheck_io_uuid: "" 56 | --------------------------------------------------------------------------------