From 32d75e9bd17c170f04aaeceb8f24b099e57d42be Mon Sep 17 00:00:00 2001 From: Robert Pufky Date: Mon, 31 Jul 2023 19:33:06 -0700 Subject: [PATCH] Add idempotentcy to pihole role. Added: * pihole_idempotent. This will force role to be idempotent each run. Classic behavior is 'false' (not idempotent); but may now be optionally toggled on. Changed: * Added 'Default' group to blocklist defaults to conform to pihole defaults. * Added 'remove_default_group' to 'pihole_clients' (backwards compatible). Hosts toggling this flag will be removed from the 'Default' group on role application. * Adding clients moved to separate 'add_clients' file enabling management of client and removal of 'Default' group in one loop. * Databases may now be removed on role application to enable idempotent runs. --- defaults/main/blocklist.yml | 19 +++++++++++++++---- defaults/main/main.yml | 16 ++++++++++++++++ tasks/db/add_clients.yml | 28 ++++++++++++++++++++++++++++ tasks/db/clear_databases.yml | 26 ++++++++++++++++++++++++++ tasks/{ => db}/cli.yml | 0 tasks/{ => db}/db.yml | 11 +++++++---- tasks/install.yml | 2 +- tasks/main.yml | 2 +- vars/main.yml | 1 + 9 files changed, 95 insertions(+), 10 deletions(-) create mode 100644 tasks/db/add_clients.yml create mode 100644 tasks/db/clear_databases.yml rename tasks/{ => db}/cli.yml (100%) rename tasks/{ => db}/db.yml (93%) diff --git a/defaults/main/blocklist.yml b/defaults/main/blocklist.yml index 8164b93..4996bb8 100644 --- a/defaults/main/blocklist.yml +++ b/defaults/main/blocklist.yml @@ -4,6 +4,8 @@ ############################################################################### # Define Adlist sources for generating domain blocklists. # +# stevenblack's hosts adlist is automatically added when installing as ID 1. +# # pihole_ad_sources: # - id: int (unique id starting at 1) # address: str (URL for adlist) @@ -13,7 +15,6 @@ # Reference: # * https://docs.pi-hole.net/database/gravity/#adlist-table-adlist -# Pi-Hole currently adds the stevenblack adlist when installing, as ID 1. pihole_ad_sources: - id: 1 address: 'https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts' @@ -43,12 +44,14 @@ pihole_domain_blocklists: [] ############################################################################### # Pi-Hole Clients Table ############################################################################### -# Define Pi-Hole clients. +# Define Pi-Hole clients. All clients are automatically added to the 'Default' +# group in Pi-Hole to automatically enforce blocking. # # pihole_clients: # - id: int (unique id starting at 1) # ip: str (IPv4,IPv6,CIDR) # comment: str (user comment) +# remove_default_group: bool (remove from automatic 'Default' group) # # Reference: # * https://docs.pi-hole.net/database/gravity/#client-table-client @@ -60,6 +63,10 @@ pihole_clients: [] ############################################################################### # Define Pi-Hole group management. # +# Group 'Default' (id 0) is special as it is automatically assigned to domains +# and clients not being a member of other groups. Each newly added client or +# domain gets assigned to group zero when being added. +# # pihole_groups: # - id: int (unique id starting at 1) # enabled: bool (enable use) @@ -69,7 +76,11 @@ pihole_clients: [] # Reference: # * https://docs.pi-hole.net/database/gravity/groups/#group-management -pihole_groups: [] +pihole_groups: + - id: 0 + enabled: true + name: 'Default' + description: 'The default group' ############################################################################### # Pi-Hole Groups Adlist Table @@ -88,7 +99,7 @@ pihole_ad_groups_blocklist: [] ############################################################################### # Pi-Hole Groups Clients Table ############################################################################### -# Define Pi-Hole group clients management. +# Define Pi-Hole group clients blocklists management. # # pihole_ad_groups_blocklist: # - client_id: int (existing client ID) diff --git a/defaults/main/main.yml b/defaults/main/main.yml index 0d2c2c4..ef929f2 100644 --- a/defaults/main/main.yml +++ b/defaults/main/main.yml @@ -28,6 +28,22 @@ pihole_update_enable: false # Disable role behavior change warnings. Default: True. pihole_disable_warning: true +# Enable idempotent pihole operation. Default: False. +# The default behavior before this option existed is the equivalent of 'false'. +# +# Idempotent operation will: +# * Clears all pihole databases and rebuilds them to prevent 'skew'. +# * Service **WILL** be interrupted while role is being applied (use multiple +# pihole instances or disable this). +# * All adlists will be redownloaded. +# * Existing logging/client information will be lost. +# +# Long running pihole instances with large volume of changes to clients/groups +# may see 'skew' between what exists on the pihole server and what the role +# defines. This corrects the issue at the expense of service downtime and +# rebuilding. +pihole_idempotent: false + ############################################################################### # Pi-Hole setupVars.conf ############################################################################### diff --git a/tasks/db/add_clients.yml b/tasks/db/add_clients.yml new file mode 100644 index 0000000..6b0ed20 --- /dev/null +++ b/tasks/db/add_clients.yml @@ -0,0 +1,28 @@ +--- +############################################################################### +# Pi-Hole Add Clients +############################################################################### +# Clients are automatically added to the 'Default' (id=0) group in Pi-Hole upon +# creation, which apply the default blocklists. Add clients and optionally +# remove them from the 'Default' if enabled. +# +# Args: +# client: dict (pihole_clients) +# - id: int (unique id starting at 1) +# ip: str (IPv4,IPv6,CIDR) +# comment: str (user comment) +# remove_default_group: bool (remove from automatic 'Default' group) +# +# Reference: +# * https://docs.pi-hole.net/database/ + +- name: 'db | add client {{ client.id }}' + ansible.builtin.command: '{{ pihole_default_sqlite3 }} "{{ pihole_default_client_insert }} ({{ client.id }}, \"{{ client.ip }}\", \"{{ client.comment }}\")"' + become: true + +- name: 'db | remove client {{ client.id }} from default group' + ansible.builtin.command: '{{ pihole_default_sqlite3 }} "{{ pihole_default_client_groups_delete_default }} {{ client.id }}"' + become: true + when: | + 'remove_default_group' in client and + client.remove_default_group diff --git a/tasks/db/clear_databases.yml b/tasks/db/clear_databases.yml new file mode 100644 index 0000000..663f6a3 --- /dev/null +++ b/tasks/db/clear_databases.yml @@ -0,0 +1,26 @@ +--- +############################################################################### +# Pi-Hole Clear Databases +############################################################################### +# Removes existing databases for Pi-Hole to rebuild on restart. This enables +# idempotent role application for Pi-Hole. +# +# Given the DB triggers and simplistic nature of handling clients; major +# changes to clients and groups (including additions, removals) **WILL** lead +# to a skew in the actual database versus what the role explicitly codifies. +# +# By removing the databases before applying changes, this enables consistent +# application at the expense of recreating the DB's and downloading/rebuilding +# blocklists each run. + +- name: 'clear databases | remove databases' + ansible.builtin.file: + path: '/etc/pihole/{{ item }}' + state: 'absent' + loop: + - 'gravity.db' + - 'pihole-FTL.db' + +- name: 'clear databases | regenerate databases' + ansible.builtin.command: '/etc/.pihole/automated\ install/basic-install.sh --reconfigure --unattended' + changed_when: false diff --git a/tasks/cli.yml b/tasks/db/cli.yml similarity index 100% rename from tasks/cli.yml rename to tasks/db/cli.yml diff --git a/tasks/db.yml b/tasks/db/db.yml similarity index 93% rename from tasks/db.yml rename to tasks/db/db.yml index 7b4f9be..eb95af2 100644 --- a/tasks/db.yml +++ b/tasks/db/db.yml @@ -12,6 +12,9 @@ # Reference: # * https://docs.pi-hole.net/database/ +- ansible.builtin.include_tasks: clear_databases.yml + when: pihole_idempotent + - name: 'db | add adlist source' ansible.builtin.command: '{{ pihole_default_sqlite3 }} "{{ pihole_default_ad_insert }} ({{ item.id }}, \"{{ item.address }}\", {{ item.enabled|int }}, \"{{ item.comment }}\")"' become: true @@ -31,11 +34,11 @@ loop: '{{ pihole_domain_blocklists }}' when: not pihole_use_cli and pihole_domain_blocklists|length > 0 -- name: 'db | add clients' - ansible.builtin.command: '{{ pihole_default_sqlite3 }} "{{ pihole_default_client_insert }} ({{ item.id }}, \"{{ item.ip }}\", \"{{ item.comment }}\")"' - become: true - loop: '{{ pihole_clients }}' +- ansible.builtin.include_tasks: add_clients.yml when: pihole_clients|length > 0 + loop: '{{ pihole_clients }}' + loop_control: + loop_var: client - name: 'db | add groups' ansible.builtin.command: '{{ pihole_default_sqlite3 }} "{{ pihole_default_groups_insert }} ({{ item.id }}, {{ item.enabled|int }}, \"{{ item.name }}\", \"{{ item.description }}\")"' diff --git a/tasks/install.yml b/tasks/install.yml index 92c47a3..d3494c6 100644 --- a/tasks/install.yml +++ b/tasks/install.yml @@ -29,7 +29,7 @@ - name: 'install | create /etc/pihole' ansible.builtin.file: path: '/etc/pihole' - mode: 0755 + mode: 0775 state: 'directory' - name: 'install | WARNING ROLE BEHAVIOR CHANGED' diff --git a/tasks/main.yml b/tasks/main.yml index fe40b96..a1a943b 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -1,4 +1,4 @@ --- - ansible.builtin.import_tasks: install.yml -- ansible.builtin.import_tasks: db.yml +- ansible.builtin.import_tasks: db/db.yml - ansible.builtin.import_tasks: update.yml diff --git a/vars/main.yml b/vars/main.yml index ac40d70..dbfb14c 100644 --- a/vars/main.yml +++ b/vars/main.yml @@ -31,4 +31,5 @@ pihole_default_client_insert: 'insert or replace into \"client\" (id, ip, commen pihole_default_groups_insert: 'insert or replace into \"group\" (id, enabled, name, description) values' pihole_default_ad_groups_insert: 'insert or replace into \"adlist_by_group\" (adlist_id, group_id) values' pihole_default_client_groups_insert: 'insert or replace into \"client_by_group\" (client_id, group_id) values' +pihole_default_client_groups_delete_default: 'delete from \"client_by_group\" where group_id = 0 and client_id = ' pihole_default_domain_groups_insert: 'insert or replace into \"domainlist_by_group\" (domainlist_id, group_id) values'