Compare commits
No commits in common. '26a4d649d3062f1468d8203390e07aeb5803d7c4' and 'da712ef7b0dbb43c6669ae968668541d4a69a157' have entirely different histories.
26a4d649d3
...
da712ef7b0
@ -1,38 +0,0 @@
|
|||||||
---
|
|
||||||
arr_portainer_stack_name: vpn-stack
|
|
||||||
arr_vpn_image: "ghcr.io/tprasadtp/protonwire"
|
|
||||||
arr_vpn_tag: "7.2.0"
|
|
||||||
|
|
||||||
# Qbittorrent
|
|
||||||
qbittorrent_image: lscr.io/linuxserver/qbittorrent
|
|
||||||
qbittorrent_tag: 4.5.3
|
|
||||||
qbittorrent_container_name: qbittorrent
|
|
||||||
qbittorrent_puid: 1000
|
|
||||||
qbittorrent_pgid: 1000
|
|
||||||
qbittorrent_exposed_web_ui_port: 15000
|
|
||||||
qbittorrent_exposed_download_port: 6881
|
|
||||||
|
|
||||||
|
|
||||||
# Radarr
|
|
||||||
radarr_image: lscr.io/linuxserver/radarr
|
|
||||||
radarr_tag: 4.5.2
|
|
||||||
radarr_container_name: radarr
|
|
||||||
radarr_exposed_port: 7878
|
|
||||||
radarr_puid: 1000
|
|
||||||
radarr_pgid: 1000
|
|
||||||
|
|
||||||
# Sonarr
|
|
||||||
sonarr_image: lscr.io/linuxserver/sonarr
|
|
||||||
sonarr_tag: 3.0.10
|
|
||||||
sonarr_exposed_port: 8989
|
|
||||||
sonarr_container_name: sonarr
|
|
||||||
sonarr_puid: 1000
|
|
||||||
sonarr_pgid: 1000
|
|
||||||
|
|
||||||
# Jackett
|
|
||||||
jackett_image: lscr.io/linuxserver/jackett
|
|
||||||
jackett_tag: 0.21.235
|
|
||||||
jackett_exposed_port: 9117
|
|
||||||
jackett_container_name: jackett
|
|
||||||
jackett_puid: 1000
|
|
||||||
jackett_pgid: 1000
|
|
||||||
@ -1,137 +0,0 @@
|
|||||||
---
|
|
||||||
- name: "Arr | Restore any missing volumes from S3"
|
|
||||||
ansible.builtin.include_role:
|
|
||||||
name: chatton.docker_backup.docker_s3_volume_restore
|
|
||||||
vars:
|
|
||||||
docker_backup_s3_volume: "{{ item }}"
|
|
||||||
with_items:
|
|
||||||
- name: "{{ arr_portainer_stack_name }}_qbittorrent_config"
|
|
||||||
- name: "{{ arr_portainer_stack_name }}_radarr_config"
|
|
||||||
- name: "{{ arr_portainer_stack_name }}_sonarr_config"
|
|
||||||
- name: "{{ arr_portainer_stack_name }}_jackett_config"
|
|
||||||
|
|
||||||
- name: "Arr | Update Portainer."
|
|
||||||
chatton.portainer.portainer_stack:
|
|
||||||
username: '{{ portainer_user }}'
|
|
||||||
password: '{{ portainer.password }}'
|
|
||||||
base_url: '{{ portainer_base_url }}'
|
|
||||||
stack_name: '{{ arr_portainer_stack_name }}'
|
|
||||||
endpoint_id: '{{ portainer_endpoint }}'
|
|
||||||
state: present
|
|
||||||
definition:
|
|
||||||
version: "3.1"
|
|
||||||
services:
|
|
||||||
protonwire:
|
|
||||||
container_name: protonwire
|
|
||||||
image: "{{ arr_vpn_image }}:{{ arr_vpn_tag }}"
|
|
||||||
init: true
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
# Quote this value as server name can contain '#'.
|
|
||||||
PROTONVPN_SERVER: "{{ vpn.protonvpn_server }}"
|
|
||||||
# Set this to 1 to show debug logs for issue forms.
|
|
||||||
DEBUG: "1"
|
|
||||||
# Set this to 0 to disable kill-switch.
|
|
||||||
KILL_SWITCH: "0"
|
|
||||||
# follow instructions here https://github.com/tprasadtp/protonvpn-docker
|
|
||||||
WIREGUARD_PRIVATE_KEY: "{{ vpn.wireguard_private_key }}"
|
|
||||||
LAN_NETWORK: "{{ vpn.lan_network }}"
|
|
||||||
cap_add:
|
|
||||||
- NET_ADMIN
|
|
||||||
# sysctl net.ipv4.conf.all.rp_filter is mandatory!
|
|
||||||
# net.ipv6.conf.all.disable_ipv6 disables IPv6 as protonVPN does not support IPv6.
|
|
||||||
# 'net.*' sysctls are not required on application containers,
|
|
||||||
# as they share network stack with protonwire container.
|
|
||||||
sysctls:
|
|
||||||
net.ipv4.conf.all.rp_filter: 2
|
|
||||||
net.ipv6.conf.all.disable_ipv6: 1
|
|
||||||
volumes:
|
|
||||||
- type: tmpfs
|
|
||||||
target: /tmp
|
|
||||||
ports:
|
|
||||||
# qbittorrent
|
|
||||||
- "{{ qbittorrent_exposed_web_ui_port }}:15000"
|
|
||||||
- "{{ qbittorrent_exposed_download_port }}:6881"
|
|
||||||
- "{{ qbittorrent_exposed_download_port }}:6881/udp"
|
|
||||||
# radarr
|
|
||||||
- "{{ radarr_exposed_port }}:7878"
|
|
||||||
# sonarr
|
|
||||||
- "{{ sonarr_exposed_port }}:8989"
|
|
||||||
# jackett
|
|
||||||
- "{{ jackett_exposed_port }}:9117"
|
|
||||||
dns:
|
|
||||||
- 1.1.1.1
|
|
||||||
- 8.8.8.8
|
|
||||||
|
|
||||||
qbittorrent:
|
|
||||||
labels: "{{ backup_labels}}"
|
|
||||||
depends_on:
|
|
||||||
- protonwire
|
|
||||||
image: "{{ qbittorrent_image }}:{{ qbittorrent_tag }}"
|
|
||||||
container_name: "{{ qbittorrent_container_name }}"
|
|
||||||
network_mode: "service:protonwire"
|
|
||||||
environment:
|
|
||||||
- "PUID={{ qbittorrent_puid }}"
|
|
||||||
- "PGID={{ qbittorrent_pgid }}"
|
|
||||||
- TZ=Europe/London
|
|
||||||
- "WEBUI_PORT=15000"
|
|
||||||
volumes:
|
|
||||||
- qbittorrent_config:/config
|
|
||||||
- "{{ directories.downloads_dir }}:/downloads"
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
radarr:
|
|
||||||
labels: "{{ backup_labels}}"
|
|
||||||
depends_on:
|
|
||||||
- protonwire
|
|
||||||
image: "{{ radarr_image }}:{{ radarr_tag }}"
|
|
||||||
container_name: "{{ radarr_container_name }}"
|
|
||||||
network_mode: "service:protonwire"
|
|
||||||
environment:
|
|
||||||
- "PUID={{ radarr_puid }}"
|
|
||||||
- "PGID={{ radarr_pgid }}"
|
|
||||||
- TZ=Europe/London
|
|
||||||
volumes:
|
|
||||||
- radarr_config:/config
|
|
||||||
- "{{ directories.movies_dir }}:/movies"
|
|
||||||
- "{{ directories.downloads_dir }}:/downloads"
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
sonarr:
|
|
||||||
depends_on:
|
|
||||||
- protonwire
|
|
||||||
image: "{{ sonarr_image }}:{{ sonarr_tag }}"
|
|
||||||
labels: "{{ backup_labels}}"
|
|
||||||
container_name: "{{ sonarr_container_name }}"
|
|
||||||
network_mode: "service:protonwire"
|
|
||||||
environment:
|
|
||||||
- "PUID={{ sonarr_puid }}"
|
|
||||||
- "PGID={{ sonarr_pgid }}"
|
|
||||||
- TZ=Europe/London
|
|
||||||
volumes:
|
|
||||||
- sonarr_config:/config
|
|
||||||
- "{{ directories.tv_dir }}:/tv"
|
|
||||||
- "{{ directories.downloads_dir }}:/downloads"
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
jackett:
|
|
||||||
labels: "{{ backup_labels}}"
|
|
||||||
depends_on:
|
|
||||||
- protonwire
|
|
||||||
image: "{{ jackett_image }}:{{ jackett_tag }}"
|
|
||||||
container_name: "{{ jackett_container_name }}"
|
|
||||||
network_mode: "service:protonwire"
|
|
||||||
environment:
|
|
||||||
- "PUID={{ jackett_puid }}"
|
|
||||||
- "PGID={{ jackett_pgid }}"
|
|
||||||
- TZ=Europe/London
|
|
||||||
- AUTO_UPDATE=true
|
|
||||||
volumes:
|
|
||||||
- jackett_config:/config
|
|
||||||
- "{{ directories.downloads_dir }}:/downloads"
|
|
||||||
restart: unless-stopped
|
|
||||||
volumes:
|
|
||||||
qbittorrent_config: {}
|
|
||||||
radarr_config: {}
|
|
||||||
sonarr_config: {}
|
|
||||||
jackett_config: {}
|
|
||||||
@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
dashdot_state: present
|
|
||||||
dashdot_image: mauricenino/dashdot
|
dashdot_image: mauricenino/dashdot
|
||||||
dashdot_tag: latest
|
dashdot_tag: latest
|
||||||
dashdot_expose_port: 3010
|
dashdot_expose_port: 3010
|
||||||
dashdot_portainer_stack_name: dashdot
|
dashdot_portainer_stack_name: dashdot
|
||||||
dashdot_container_name: dashdot
|
dashdot_container_name: dashdot
|
||||||
|
dashdot_restart_policy: unless-stopped
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
glances_state: present
|
|
||||||
glances_image: nicolargo/glances
|
glances_image: nicolargo/glances
|
||||||
glances_tag: latest-alpine
|
glances_tag: latest-alpine
|
||||||
glances_expose_port: 8083
|
glances_expose_port: 8083
|
||||||
glances_portainer_stack_name: glances
|
glances_portainer_stack_name: glances
|
||||||
glances_container_name: glances
|
glances_container_name: glances
|
||||||
|
glances_restart_policy: unless-stopped
|
||||||
glances_pid: host
|
glances_pid: host
|
||||||
|
|||||||
@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
gotify_state: present
|
|
||||||
gotify_image: gotify/server
|
|
||||||
gotify_tag: 2.2.5
|
|
||||||
gotify_expose_port: 7875
|
|
||||||
gotify_portainer_stack_name: gotify
|
|
||||||
gotify_container_name: gotify
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
---
|
|
||||||
- name: "Gotify | Restore any missing volumes from S3"
|
|
||||||
ansible.builtin.include_role:
|
|
||||||
name: chatton.docker_backup.docker_s3_volume_restore
|
|
||||||
vars:
|
|
||||||
docker_backup_s3_volume:
|
|
||||||
name: "{{ gotify_portainer_stack_name }}_data"
|
|
||||||
|
|
||||||
- name: "Gotify | Update Portainer."
|
|
||||||
chatton.portainer.portainer_stack:
|
|
||||||
username: '{{ portainer_user }}'
|
|
||||||
password: '{{ portainer.password }}'
|
|
||||||
base_url: '{{ portainer_base_url }}'
|
|
||||||
stack_name: '{{ gotify_portainer_stack_name }}'
|
|
||||||
endpoint_id: '{{ portainer_endpoint }}'
|
|
||||||
state: "{{ gotify_state }}"
|
|
||||||
definition:
|
|
||||||
version: "3.1"
|
|
||||||
services:
|
|
||||||
gotify:
|
|
||||||
labels: "{{ backup_labels }}"
|
|
||||||
image: "{{ gotify_image}}:{{ gotify_tag }}"
|
|
||||||
container_name: "{{ gotify_container_name }}"
|
|
||||||
volumes:
|
|
||||||
- data:/app/data
|
|
||||||
ports:
|
|
||||||
- "{{ gotify_expose_port }}:80"
|
|
||||||
restart: "{{ restart_policy }}"
|
|
||||||
volumes:
|
|
||||||
data: {}
|
|
||||||
@ -1,7 +1,22 @@
|
|||||||
---
|
---
|
||||||
linkding_state: present
|
|
||||||
linkding_image: sissbruecker/linkding
|
linkding_image: sissbruecker/linkding
|
||||||
linkding_tag: latest
|
linkding_tag: latest
|
||||||
|
linkding_backup_enabled: true
|
||||||
|
linkding_backup_schedule: "nightly"
|
||||||
linkding_expose_port: 9090
|
linkding_expose_port: 9090
|
||||||
linkding_portainer_stack_name: linkding
|
linkding_portainer_stack_name: linkding
|
||||||
linkding_container_name: linkding
|
linkding_container_name: linkding
|
||||||
|
linkding_restart_policy: unless-stopped
|
||||||
|
|
||||||
|
# Environment variables
|
||||||
|
linkding_websocket_enabled: true
|
||||||
|
linkding_sends_allowed: true
|
||||||
|
linkding_emergency_access_allowed: true
|
||||||
|
linkding_domain: "https://vault.cianhatton.ie"
|
||||||
|
linkding_signups_allowed: false
|
||||||
|
linkding_webvault_enabled: true
|
||||||
|
|
||||||
|
# Backup variables
|
||||||
|
linkding_docker_backup_restore_force: false
|
||||||
|
linkding_docker_backup_restore_latest_s3_key: true
|
||||||
|
linkding_docker_backup_fail_on_no_s3_backups: false
|
||||||
|
|||||||
@ -1,8 +0,0 @@
|
|||||||
---
|
|
||||||
pihole_state: present
|
|
||||||
pihole_image: pihole/pihole
|
|
||||||
pihole_tag: 2022.09.2
|
|
||||||
pihole_portainer_stack_name: pihole
|
|
||||||
pihole_container_name: pihole
|
|
||||||
# this variable MUST be set. See the tasks for the expected value.
|
|
||||||
pihole_volumes: {}
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
---
|
|
||||||
- name: "Pihole | Restore any missing volumes from S3"
|
|
||||||
ansible.builtin.include_role:
|
|
||||||
name: chatton.docker_backup.docker_s3_volume_restore
|
|
||||||
vars:
|
|
||||||
docker_backup_s3_volume: "{{ item }}"
|
|
||||||
with_items:
|
|
||||||
- name: "{{ pihole_portainer_stack_name }}_{{ inventory_hostname_short }}_app"
|
|
||||||
- name: "{{ pihole_portainer_stack_name }}_{{ inventory_hostname_short }}_dns"
|
|
||||||
|
|
||||||
- name: Setting host facts using complex arguments
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
vol_one: "{{ inventory_hostname_short }}_app"
|
|
||||||
|
|
||||||
- name: "Pihole | Update Portainer."
|
|
||||||
chatton.portainer.portainer_stack:
|
|
||||||
username: '{{ portainer_user }}'
|
|
||||||
password: '{{ portainer.password }}'
|
|
||||||
base_url: '{{ portainer_base_url }}'
|
|
||||||
stack_name: '{{ pihole_portainer_stack_name }}'
|
|
||||||
endpoint_id: '{{ portainer_endpoint }}'
|
|
||||||
state: "{{ pihole_state }}"
|
|
||||||
definition:
|
|
||||||
version: "3"
|
|
||||||
services:
|
|
||||||
pihole:
|
|
||||||
labels: "{{ backup_labels }}"
|
|
||||||
container_name: "{{ pihole_container_name }}"
|
|
||||||
image: "{{ pihole_image }}:{{ pihole_tag }}"
|
|
||||||
ports:
|
|
||||||
- "53:53/tcp"
|
|
||||||
- "53:53/udp"
|
|
||||||
- "85:80/tcp"
|
|
||||||
environment:
|
|
||||||
WEBPASSWORD: '{{ pihole_web_password }}'
|
|
||||||
DNSMASQ_LISTENING: "all"
|
|
||||||
volumes:
|
|
||||||
- '{{ inventory_hostname_short }}_app:/etc/pihole'
|
|
||||||
- '{{ inventory_hostname_short }}_dns:/etc/dnsmasq.d'
|
|
||||||
restart: unless-stopped
|
|
||||||
volumes: "{{ pihole_volumes }}"
|
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
version: "3.1"
|
||||||
|
services:
|
||||||
|
gotify:
|
||||||
|
labels:
|
||||||
|
ie.cianhatton.backup.enabled: "true"
|
||||||
|
ie.cianhatton.backup.schedule: "{{ backups.schedule_keys.nightly }}"
|
||||||
|
image: gotify/server
|
||||||
|
container_name: gotify
|
||||||
|
volumes:
|
||||||
|
- data:/app/data
|
||||||
|
ports:
|
||||||
|
- 7875:80
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
data:
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
minio:
|
||||||
|
image: "{{ template_vars.image }}:{{ template_vars.tag }}"
|
||||||
|
ports:
|
||||||
|
# The web console
|
||||||
|
- "11000:9000"
|
||||||
|
# The API
|
||||||
|
- "11001:9001"
|
||||||
|
volumes:
|
||||||
|
- minio_storage:/data
|
||||||
|
environment:
|
||||||
|
# specified in the vault file
|
||||||
|
MINIO_ROOT_USER: "{{ minio_username }}"
|
||||||
|
MINIO_ROOT_PASSWORD: "{{ minio_password }}"
|
||||||
|
MINIO_API_ROOT_ACCESS: "on"
|
||||||
|
MINIO_BROWSER_REDIRECT_URL: http://192.168.178.42:11000
|
||||||
|
# enable encryption
|
||||||
|
MINIO_KMS_AUTO_ENCRYPTION: "on"
|
||||||
|
MINIO_KMS_SECRET_KEY: "minio-key:{{ minio_encryption_key }}"
|
||||||
|
|
||||||
|
command: server --console-address ":9001" /data
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
minio_storage: {}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
version: "3"
|
||||||
|
services:
|
||||||
|
pihole:
|
||||||
|
labels:
|
||||||
|
ie.cianhatton.backup.enabled: "true"
|
||||||
|
ie.cianhatton.backup.schedule: "{{ backups.schedule_keys.nightly }}"
|
||||||
|
container_name: pihole
|
||||||
|
image: "pihole/pihole:2022.09.2"
|
||||||
|
ports:
|
||||||
|
- "53:53/tcp"
|
||||||
|
- "53:53/udp"
|
||||||
|
- "85:80/tcp"
|
||||||
|
environment:
|
||||||
|
WEBPASSWORD: '{{ pihole_web_password }}'
|
||||||
|
DNSMASQ_LISTENING: "all"
|
||||||
|
volumes:
|
||||||
|
- '{{ inventory_hostname_short }}_app:/etc/pihole'
|
||||||
|
- '{{ inventory_hostname_short }}_dns:/etc/dnsmasq.d'
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
{{ inventory_hostname_short }}_app:
|
||||||
|
{{ inventory_hostname_short }}_dns:
|
||||||
@ -0,0 +1,169 @@
|
|||||||
|
---
|
||||||
|
version: "3"
|
||||||
|
services:
|
||||||
|
{% if template_vars.vpn == "protonwire" %}
|
||||||
|
protonwire:
|
||||||
|
container_name: protonwire
|
||||||
|
image: ghcr.io/tprasadtp/protonwire:7.2.0
|
||||||
|
init: true
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
# Quote this value as server name can contain '#'.
|
||||||
|
PROTONVPN_SERVER: "{{ vpn.protonvpn_server }}"
|
||||||
|
# Set this to 1 to show debug logs for issue forms.
|
||||||
|
DEBUG: "1"
|
||||||
|
# Set this to 0 to disable kill-switch.
|
||||||
|
KILL_SWITCH: "0"
|
||||||
|
|
||||||
|
# follow instructions here https://github.com/tprasadtp/protonvpn-docker
|
||||||
|
WIREGUARD_PRIVATE_KEY: "{{ vpn.wireguard_private_key }}"
|
||||||
|
LAN_NETWORK: "{{ vpn.lan_network }}"
|
||||||
|
cap_add:
|
||||||
|
- NET_ADMIN
|
||||||
|
# sysctl net.ipv4.conf.all.rp_filter is mandatory!
|
||||||
|
# net.ipv6.conf.all.disable_ipv6 disables IPv6 as protonVPN does not support IPv6.
|
||||||
|
# 'net.*' sysctls are not required on application containers,
|
||||||
|
# as they share network stack with protonwire container.
|
||||||
|
sysctls:
|
||||||
|
net.ipv4.conf.all.rp_filter: 2
|
||||||
|
net.ipv6.conf.all.disable_ipv6: 1
|
||||||
|
volumes:
|
||||||
|
- type: tmpfs
|
||||||
|
target: /tmp
|
||||||
|
ports:
|
||||||
|
# qbittorrent
|
||||||
|
- 15000:15000
|
||||||
|
- 6881:6881
|
||||||
|
- 6881:6881/udp
|
||||||
|
# radarr
|
||||||
|
- 7878:7878
|
||||||
|
# sonarr
|
||||||
|
- 8989:8989
|
||||||
|
# jackett
|
||||||
|
- 9117:9117
|
||||||
|
dns:
|
||||||
|
- 1.1.1.1
|
||||||
|
- 8.8.8.8
|
||||||
|
{% elif template_vars.vpn == "surfshark" %}
|
||||||
|
surfshark:
|
||||||
|
image: ilteoood/docker-surfshark
|
||||||
|
container_name: surfshark
|
||||||
|
environment:
|
||||||
|
- SURFSHARK_USER={{ vpn.surfshark_username }}
|
||||||
|
- SURFSHARK_PASSWORD={{ vpn.surfshark_password }}
|
||||||
|
# must specify LAN_NETWORK otherwise you will not be able
|
||||||
|
# to access ports which are exposed here.
|
||||||
|
- LAN_NETWORK={{ vpn.lan_network }}
|
||||||
|
cap_add:
|
||||||
|
- NET_ADMIN
|
||||||
|
devices:
|
||||||
|
- /dev/net/tun
|
||||||
|
ports:
|
||||||
|
# qbittorrent
|
||||||
|
- 15000:15000
|
||||||
|
- 6881:6881
|
||||||
|
- 6881:6881/udp
|
||||||
|
# radarr
|
||||||
|
- 7878:7878
|
||||||
|
# sonarr
|
||||||
|
- 8989:8989
|
||||||
|
# jackett
|
||||||
|
- 9117:9117
|
||||||
|
restart: unless-stopped
|
||||||
|
dns:
|
||||||
|
- 1.1.1.1
|
||||||
|
- 8.8.8.8
|
||||||
|
{% endif %}
|
||||||
|
{% if template_vars.qbittorrent.enabled %}
|
||||||
|
qbittorrent:
|
||||||
|
labels:
|
||||||
|
ie.cianhatton.backup.enabled: "true"
|
||||||
|
ie.cianhatton.backup.schedule: "{{ backups.schedule_keys.nightly }}"
|
||||||
|
depends_on:
|
||||||
|
- {{ template_vars.vpn }}
|
||||||
|
image: "{{ template_vars.qbittorrent.image }}:{{ template_vars.qbittorrent.tag }}"
|
||||||
|
container_name: qbittorrent
|
||||||
|
network_mode: "service:{{ template_vars.vpn }}"
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=Europe/London
|
||||||
|
- WEBUI_PORT=15000
|
||||||
|
volumes:
|
||||||
|
- qbittorrent_config:/config
|
||||||
|
- {{ directories.downloads_dir }}:/downloads
|
||||||
|
restart: unless-stopped
|
||||||
|
{% endif %}
|
||||||
|
{% if template_vars.radarr.enabled %}
|
||||||
|
radarr:
|
||||||
|
labels:
|
||||||
|
ie.cianhatton.backup.enabled: "true"
|
||||||
|
ie.cianhatton.backup.schedule: "{{ backups.schedule_keys.nightly }}"
|
||||||
|
depends_on:
|
||||||
|
- {{ template_vars.vpn }}
|
||||||
|
image: "{{ template_vars.radarr.image }}:{{ template_vars.radarr.tag }}"
|
||||||
|
container_name: radarr
|
||||||
|
network_mode: "service:{{ template_vars.vpn }}"
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=Europe/London
|
||||||
|
volumes:
|
||||||
|
- radarr_config:/config
|
||||||
|
- {{ directories.movies_dir }}:/movies
|
||||||
|
- {{ directories.downloads_dir }}:/downloads
|
||||||
|
restart: unless-stopped
|
||||||
|
{% endif %}
|
||||||
|
{% if template_vars.sonarr.enabled %}
|
||||||
|
sonarr:
|
||||||
|
depends_on:
|
||||||
|
- {{ template_vars.vpn }}
|
||||||
|
image: "{{ template_vars.sonarr.image }}:{{ template_vars.sonarr.tag }}"
|
||||||
|
labels:
|
||||||
|
ie.cianhatton.backup.enabled: "true"
|
||||||
|
ie.cianhatton.backup.schedule: "{{ backups.schedule_keys.nightly }}"
|
||||||
|
container_name: sonarr
|
||||||
|
network_mode: "service:{{ template_vars.vpn }}"
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=Europe/London
|
||||||
|
volumes:
|
||||||
|
- sonarr_config:/config
|
||||||
|
- {{ directories.tv_dir }}:/tv
|
||||||
|
- {{ directories.downloads_dir }}:/downloads
|
||||||
|
restart: unless-stopped
|
||||||
|
{% endif %}
|
||||||
|
{% if template_vars.jackett.enabled %}
|
||||||
|
jackett:
|
||||||
|
labels:
|
||||||
|
ie.cianhatton.backup.enabled: "true"
|
||||||
|
ie.cianhatton.backup.schedule: "{{ backups.schedule_keys.nightly }}"
|
||||||
|
depends_on:
|
||||||
|
- {{ template_vars.vpn }}
|
||||||
|
image: "{{ template_vars.jackett.image }}:{{ template_vars.jackett.tag }}"
|
||||||
|
container_name: jackett
|
||||||
|
network_mode: "service:{{ template_vars.vpn }}"
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=Europe/London
|
||||||
|
- AUTO_UPDATE=true
|
||||||
|
volumes:
|
||||||
|
- jackett_config:/config
|
||||||
|
- {{ directories.downloads_dir }}:/downloads
|
||||||
|
restart: unless-stopped
|
||||||
|
{% endif %}
|
||||||
|
volumes:
|
||||||
|
{% if template_vars.jackett.enabled %}
|
||||||
|
jackett_config:
|
||||||
|
{% endif %}
|
||||||
|
{% if template_vars.qbittorrent.enabled %}
|
||||||
|
qbittorrent_config:
|
||||||
|
{% endif %}
|
||||||
|
{% if template_vars.radarr.enabled %}
|
||||||
|
radarr_config:
|
||||||
|
{% endif %}
|
||||||
|
{% if template_vars.sonarr.enabled %}
|
||||||
|
sonarr_config:
|
||||||
|
{% endif %}
|
||||||
@ -1,293 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
# Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
|
|
||||||
#
|
|
||||||
# Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
# purpose with or without fee is hereby granted, provided that the above
|
|
||||||
# copyright notice and this permission notice appear in all copies.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import ctypes
|
|
||||||
import errno
|
|
||||||
import fnmatch
|
|
||||||
import io
|
|
||||||
import os
|
|
||||||
import shlex
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
_libc = ctypes.CDLL("libc.so.6",use_errno=True)
|
|
||||||
_lgetxattr = _libc.lgetxattr
|
|
||||||
_lgetxattr.argtypes = [ctypes.c_char_p,ctypes.c_char_p,ctypes.c_void_p,ctypes.c_size_t]
|
|
||||||
def lgetxattr(path,name):
|
|
||||||
if type(path) == str:
|
|
||||||
path = path.encode(errors='backslashreplace')
|
|
||||||
if type(name) == str:
|
|
||||||
name = name.encode(errors='backslashreplace')
|
|
||||||
length = 64
|
|
||||||
while True:
|
|
||||||
buf = ctypes.create_string_buffer(length)
|
|
||||||
res = _lgetxattr(path,name,buf,ctypes.c_size_t(length))
|
|
||||||
if res >= 0:
|
|
||||||
return buf.raw[0:res].decode(errors='backslashreplace')
|
|
||||||
else:
|
|
||||||
err = ctypes.get_errno()
|
|
||||||
if err == errno.ERANGE:
|
|
||||||
length *= 2
|
|
||||||
elif err == errno.ENODATA:
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
raise IOError(err,os.strerror(err),path)
|
|
||||||
|
|
||||||
|
|
||||||
def ismergerfs(path):
|
|
||||||
try:
|
|
||||||
lgetxattr(path,'user.mergerfs.version')
|
|
||||||
return True
|
|
||||||
except IOError as e:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def mergerfs_control_file(basedir):
|
|
||||||
if basedir == '/':
|
|
||||||
return None
|
|
||||||
ctrlfile = os.path.join(basedir,'.mergerfs')
|
|
||||||
if os.path.exists(ctrlfile):
|
|
||||||
return ctrlfile
|
|
||||||
else:
|
|
||||||
dirname = os.path.dirname(basedir)
|
|
||||||
return mergerfs_control_file(dirname)
|
|
||||||
|
|
||||||
|
|
||||||
def mergerfs_srcmounts(ctrlfile):
|
|
||||||
srcmounts = lgetxattr(ctrlfile,'user.mergerfs.srcmounts')
|
|
||||||
srcmounts = srcmounts.split(':')
|
|
||||||
return srcmounts
|
|
||||||
|
|
||||||
|
|
||||||
def match(filename,matches):
|
|
||||||
for match in matches:
|
|
||||||
if fnmatch.fnmatch(filename,match):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def exclude_by_size(filepath,exclude_lt,exclude_gt):
|
|
||||||
try:
|
|
||||||
st = os.lstat(filepath)
|
|
||||||
if exclude_lt and st.st_size < exclude_lt:
|
|
||||||
return True
|
|
||||||
if exclude_gt and st.st_size > exclude_gt:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def find_a_file(src,
|
|
||||||
relpath,
|
|
||||||
file_includes,file_excludes,
|
|
||||||
path_includes,path_excludes,
|
|
||||||
exclude_lt,exclude_gt):
|
|
||||||
basepath = os.path.join(src,relpath)
|
|
||||||
for (dirpath,dirnames,filenames) in os.walk(basepath):
|
|
||||||
for filename in filenames:
|
|
||||||
filepath = os.path.join(dirpath,filename)
|
|
||||||
if match(filename,file_excludes):
|
|
||||||
continue
|
|
||||||
if match(filepath,path_excludes):
|
|
||||||
continue
|
|
||||||
if not match(filename,file_includes):
|
|
||||||
continue
|
|
||||||
if not match(filepath,path_includes):
|
|
||||||
continue
|
|
||||||
if exclude_by_size(filepath,exclude_lt,exclude_gt):
|
|
||||||
continue
|
|
||||||
return os.path.relpath(filepath,src)
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def execute(args):
|
|
||||||
return subprocess.call(args)
|
|
||||||
|
|
||||||
|
|
||||||
def print_args(args):
|
|
||||||
quoted = [shlex.quote(arg) for arg in args]
|
|
||||||
print(' '.join(quoted))
|
|
||||||
|
|
||||||
|
|
||||||
def build_move_file(src,dst,relfile):
|
|
||||||
frompath = os.path.join(src,'./',relfile)
|
|
||||||
topath = dst+'/'
|
|
||||||
args = ['rsync',
|
|
||||||
'-avlHAXWE',
|
|
||||||
'--relative',
|
|
||||||
'--progress',
|
|
||||||
'--remove-source-files',
|
|
||||||
frompath,
|
|
||||||
topath]
|
|
||||||
return args
|
|
||||||
|
|
||||||
|
|
||||||
def freespace_percentage(srcmounts):
|
|
||||||
lfsp = []
|
|
||||||
for srcmount in srcmounts:
|
|
||||||
vfs = os.statvfs(srcmount)
|
|
||||||
avail = vfs.f_bavail * vfs.f_frsize
|
|
||||||
total = vfs.f_blocks * vfs.f_frsize
|
|
||||||
per = avail / total
|
|
||||||
lfsp.append((srcmount,per))
|
|
||||||
return sorted(lfsp, key=lambda x: x[1])
|
|
||||||
|
|
||||||
|
|
||||||
def all_within_range(l,n):
|
|
||||||
if len(l) == 0 or len(l) == 1:
|
|
||||||
return True
|
|
||||||
return (abs(l[0][1] - l[-1][1]) <= n)
|
|
||||||
|
|
||||||
|
|
||||||
def human_to_bytes(s):
|
|
||||||
m = s[-1]
|
|
||||||
if m == 'K':
|
|
||||||
i = int(s[0:-1]) * 1024
|
|
||||||
elif m == 'M':
|
|
||||||
i = int(s[0:-1]) * 1024 * 1024
|
|
||||||
elif m == 'G':
|
|
||||||
i = int(s[0:-1]) * 1024 * 1024 * 1024
|
|
||||||
elif m == 'T':
|
|
||||||
i = int(s[0:-1]) * 1024 * 1024 * 1024 * 1024
|
|
||||||
else:
|
|
||||||
i = int(s)
|
|
||||||
|
|
||||||
return i
|
|
||||||
|
|
||||||
|
|
||||||
def buildargparser():
|
|
||||||
parser = argparse.ArgumentParser(description='balance files on a mergerfs mount based on percentage drive filled')
|
|
||||||
parser.add_argument('dir',
|
|
||||||
type=str,
|
|
||||||
help='starting directory')
|
|
||||||
parser.add_argument('-p',
|
|
||||||
dest='percentage',
|
|
||||||
type=float,
|
|
||||||
default=2.0,
|
|
||||||
help='percentage range of freespace (default 2.0)')
|
|
||||||
parser.add_argument('-i','--include',
|
|
||||||
dest='include',
|
|
||||||
type=str,
|
|
||||||
action='append',
|
|
||||||
default=[],
|
|
||||||
help='fnmatch compatible file filter (can use multiple times)')
|
|
||||||
parser.add_argument('-e','--exclude',
|
|
||||||
dest='exclude',
|
|
||||||
type=str,
|
|
||||||
action='append',
|
|
||||||
default=[],
|
|
||||||
help='fnmatch compatible file filter (can use multiple times)')
|
|
||||||
parser.add_argument('-I','--include-path',
|
|
||||||
dest='includepath',
|
|
||||||
type=str,
|
|
||||||
action='append',
|
|
||||||
default=[],
|
|
||||||
help='fnmatch compatible path filter (can use multiple times)')
|
|
||||||
parser.add_argument('-E','--exclude-path',
|
|
||||||
dest='excludepath',
|
|
||||||
type=str,
|
|
||||||
action='append',
|
|
||||||
default=[],
|
|
||||||
help='fnmatch compatible path filter (can use multiple times)')
|
|
||||||
parser.add_argument('-s',
|
|
||||||
dest='excludelt',
|
|
||||||
type=str,
|
|
||||||
default='0',
|
|
||||||
help='exclude files smaller than <int>[KMGT] bytes')
|
|
||||||
parser.add_argument('-S',
|
|
||||||
dest='excludegt',
|
|
||||||
type=str,
|
|
||||||
default='0',
|
|
||||||
help='exclude files larger than <int>[KMGT] bytes')
|
|
||||||
return parser
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer,
|
|
||||||
encoding='utf8',
|
|
||||||
errors="backslashreplace",
|
|
||||||
line_buffering=True)
|
|
||||||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer,
|
|
||||||
encoding='utf8',
|
|
||||||
errors="backslashreplace",
|
|
||||||
line_buffering=True)
|
|
||||||
|
|
||||||
parser = buildargparser()
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
args.dir = os.path.realpath(args.dir)
|
|
||||||
|
|
||||||
ctrlfile = mergerfs_control_file(args.dir)
|
|
||||||
if not ismergerfs(ctrlfile):
|
|
||||||
print("%s is not a mergerfs mount" % args.dir)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
relpath = ''
|
|
||||||
mntpoint = os.path.dirname(ctrlfile)
|
|
||||||
if args.dir != mntpoint:
|
|
||||||
relpath = os.path.relpath(args.dir,mntpoint)
|
|
||||||
|
|
||||||
file_includes = ['*'] if not args.include else args.include
|
|
||||||
file_excludes = args.exclude
|
|
||||||
path_includes = ['*'] if not args.includepath else args.includepath
|
|
||||||
path_excludes = args.excludepath
|
|
||||||
exclude_lt = human_to_bytes(args.excludelt)
|
|
||||||
exclude_gt = human_to_bytes(args.excludegt)
|
|
||||||
srcmounts = mergerfs_srcmounts(ctrlfile)
|
|
||||||
percentage = args.percentage / 100
|
|
||||||
|
|
||||||
try:
|
|
||||||
l = freespace_percentage(srcmounts)
|
|
||||||
while not all_within_range(l,percentage):
|
|
||||||
todrive = l[-1][0]
|
|
||||||
relfilepath = None
|
|
||||||
while not relfilepath and len(l):
|
|
||||||
fromdrive = l[0][0]
|
|
||||||
del l[0]
|
|
||||||
relfilepath = find_a_file(fromdrive,
|
|
||||||
relpath,
|
|
||||||
file_includes,file_excludes,
|
|
||||||
path_includes,path_excludes,
|
|
||||||
exclude_lt,exclude_gt)
|
|
||||||
if len(l) == 0:
|
|
||||||
print('Could not find file to transfer: exiting...')
|
|
||||||
break
|
|
||||||
if fromdrive == todrive:
|
|
||||||
print('Source drive == target drive: exiting...')
|
|
||||||
break
|
|
||||||
|
|
||||||
args = build_move_file(fromdrive,todrive,relfilepath)
|
|
||||||
print('file: {}\nfrom: {}\nto: {}'.format(relfilepath,fromdrive,todrive))
|
|
||||||
print_args(args)
|
|
||||||
rv = execute(args)
|
|
||||||
if rv:
|
|
||||||
print('ERROR - exited with exit code: {}'.format(rv))
|
|
||||||
break
|
|
||||||
l = freespace_percentage(srcmounts)
|
|
||||||
print('Branches within {:.1%} range: '.format(percentage))
|
|
||||||
for (branch,percentage) in l:
|
|
||||||
print(' * {}: {:.2%} free'.format(branch,percentage))
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
print("exiting: CTRL-C pressed")
|
|
||||||
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
Loading…
Reference in New Issue