Full Code of PowerDNS/pdns-ansible for AI

master 1b7d296d0d0f cached
79 files
181.6 KB
51.6k tokens
72 symbols
1 requests
Download .txt
Showing preview only (201K chars total). Download the full file or copy to clipboard to get everything.
Repository: PowerDNS/pdns-ansible
Branch: master
Commit: 1b7d296d0d0f
Files: 79
Total size: 181.6 KB

Directory structure:
gitextract_axlon5gn/

├── .ansible-lint
├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       └── main.yml
├── .gitignore
├── .yamllint
├── CHANGELOG.md
├── LICENSE
├── README.md
├── defaults/
│   └── main.yml
├── handlers/
│   └── main.yml
├── meta/
│   └── main.yml
├── molecule/
│   ├── pdns-48/
│   │   ├── converge.yml
│   │   └── molecule.yml
│   ├── pdns-49/
│   │   ├── converge.yml
│   │   └── molecule.yml
│   ├── pdns-50/
│   │   ├── converge.yml
│   │   └── molecule.yml
│   ├── pdns-master/
│   │   ├── converge.yml
│   │   └── molecule.yml
│   ├── pdns-os-repos/
│   │   ├── converge.yml
│   │   └── molecule.yml
│   └── resources/
│       ├── Dockerfile.archlinux-systemd.j2
│       ├── Dockerfile.debian-systemd.j2
│       ├── Dockerfile.el-systemd.j2
│       ├── cleanup.yml
│       ├── create.yml
│       ├── destroy.yml
│       ├── prepare.yml
│       ├── tests/
│       │   ├── all/
│       │   │   └── test_common.py
│       │   ├── backend-bind/
│       │   │   └── test_backend_bind.py
│       │   ├── backend-lmdb/
│       │   │   └── test_backend_lmdb.py
│       │   ├── backend-mariadb/
│       │   │   └── test_backend_mariadb.py
│       │   ├── backend-mysql/
│       │   │   └── test_backend_mysql.py
│       │   ├── backend-postgresql/
│       │   │   └── test_backend_postgresql.py
│       │   ├── backend-sqlite/
│       │   │   └── test_backend_sqlite.py
│       │   ├── backend-zones/
│       │   │   └── test_backend_zones.py
│       │   ├── repo-48/
│       │   │   └── test_repo_48.py
│       │   ├── repo-49/
│       │   │   └── test_repo_49.py
│       │   ├── repo-50/
│       │   │   └── test_repo_50.py
│       │   ├── repo-master/
│       │   │   └── test_repo_master.py
│       │   ├── service-mask/
│       │   │   └── test_service_mask.py
│       │   ├── systemd-no-override/
│       │   │   └── test_override.py
│       │   └── systemd-override/
│       │       └── test_override.py
│       └── vars/
│           ├── molecule.yml
│           ├── pdns-backend-bind.yml
│           ├── pdns-backend-lmdb.yml
│           ├── pdns-backend-mariadb.yml
│           ├── pdns-backend-mysql.yml
│           ├── pdns-backend-postgresql.yml
│           ├── pdns-backend-sqlite3.yml
│           ├── pdns-common.yml
│           ├── pdns-no-overrides.yml
│           ├── pdns-os-repos.yml
│           ├── pdns-repo-48.yml
│           ├── pdns-repo-49.yml
│           ├── pdns-repo-50.yml
│           └── pdns-repo-master.yml
├── requirements.yml
├── tasks/
│   ├── configure.yml
│   ├── database-lmdb.yml
│   ├── database-mysql.yml
│   ├── database-pgsql.yml
│   ├── database-sqlite3.yml
│   ├── inspect.yml
│   ├── install.yml
│   ├── main.yml
│   ├── repo-Debian.yml
│   ├── repo-RedHat.yml
│   └── selinux.yml
├── templates/
│   ├── override-service.systemd.conf.j2
│   ├── pdns.conf.j2
│   └── pdns.pin.j2
├── test-requirements.txt
├── tox.ini
└── vars/
    ├── Archlinux.yml
    ├── Debian.yml
    ├── RedHat.yml
    ├── Ubuntu-20.yml
    └── main.yml

================================================
FILE CONTENTS
================================================

================================================
FILE: .ansible-lint
================================================
---

skip_list: []


================================================
FILE: .github/dependabot.yml
================================================
---
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates

version: 2
updates:
  - package-ecosystem: github-actions
    directory: "/"
    schedule:
      interval: daily
      timezone: Europe/Amsterdam
    open-pull-requests-limit: 3
  - package-ecosystem: pip
    directory: "/"
    schedule:
      interval: daily
      timezone: Europe/Amsterdam
    open-pull-requests-limit: 3


================================================
FILE: .github/workflows/main.yml
================================================
---
on:
  push:
  pull_request:
  schedule:
    - cron: '33 5 * * 0'

jobs:
  Lint:
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/checkout@v6
      - name: Run ansible-lint
        uses: ansible/ansible-lint@main
  Tests:
    name: Test role on different ansible versions
    runs-on: ubuntu-22.04
    strategy:
      fail-fast: false
      matrix:
        ansible:
          - "2.15"
          - "2.16"
        scenario:
          - pdns-48
          - pdns-49
          - pdns-50
          - pdns-master
          - pdns-os-repos
    steps:
      - name: checkout
        uses: actions/checkout@v6
      - name: Install python
        uses: actions/setup-python@v5
        with:
          python-version: "3.11"
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install tox tox-gh-actions
      - name: Run the tests
        run: tox -- molecule test -s ${{ matrix.scenario }}
        env:
          ANSIBLE: ${{ matrix.ansible }}


================================================
FILE: .gitignore
================================================
### Ansible ###
*.retry
.ansible_cache
.ansible

### Python ###
# Byte-compiled / optimized / DLL files
.pytest_cache/
__pycache__/
*.py[cod]
*$py.class

### Molecule ###
.tox
.cache
.molecule
.vagrant

#venv
venv


================================================
FILE: .yamllint
================================================
---
# Based on ansible-lint config
extends: default

ignore: |
  .tox/
  .git/
  .venv/
  venv/
  .cache/
  .pytest_cache/

rules:
  braces:
    max-spaces-inside: 1
    level: error
  brackets:
    max-spaces-inside: 1
    level: error
  colons:
    max-spaces-after: -1
    level: error
  commas:
    max-spaces-after: -1
    level: error
  comments:
    min-spaces-from-content: 1
  comments-indentation: disable
  document-start: enable
  empty-lines:
    max: 3
    level: error
  hyphens:
    level: error
  indentation: enable
  key-duplicates: enable
  line-length: disable
  new-line-at-end-of-file: enable
  new-lines:
    type: unix
  octal-values:
    forbid-implicit-octal: true
    forbid-explicit-octal: true
  trailing-spaces: disable
  truthy:
    allowed-values: ["true", "false"]
    check-keys: false


================================================
FILE: CHANGELOG.md
================================================
## v1.10.0 (2026-02-24)

NEW FEATURES:
- Add role-level package state controls for PowerDNS, debug symbols, backend packages, and backend dependency packages:
  `pdns_package_state`, `pdns_debug_symbols_package_state`, `pdns_backends_packages_state`,
  `pdns_mysql_packages_state`, `pdns_pgsql_packages_state`, and `pdns_sqlite_package_state`.
- Add role documentation section describing standard tags (`install`, `config`, `service`, `repository`).

IMPROVEMENTS:
- Add explicit task/handler tags across installation, repository, configuration, and service flows to support predictable partial runs.
- Refactor MySQL, PostgreSQL, and SQLite database tasks into clearer management blocks with explicit package-state handling.
- Improve role behavior when `pdns_package_state: absent` by skipping runtime configuration/service tasks while still allowing dependency/package removal paths.
- Normalize defaults/documentation booleans (`true`/`false`) and fix minor typos.
- Include `hostname` in the EL Molecule Docker image package set.

## v1.9.0 (2026-02-23)

NEW FEATURES:
- Add configuration for flat files ([\#79](https://github.com/PowerDNS/pdns-ansible/pull/79), @sorrowless)
- Allow installation of custom scripts ([\#210](https://github.com/PowerDNS/pdns-ansible/pull/210), @zerwes)
- Add support and tests for Rocky Linux and AlmaLinux ([\#209](https://github.com/PowerDNS/pdns-ansible/pull/209), @romeroalx)
- Add pdns49 repository and CI ([\#213](https://github.com/PowerDNS/pdns-ansible/pull/213), @npmdnl)
- Add pdns50 repository and CI ([\#247](https://github.com/PowerDNS/pdns-ansible/pull/247), @npmdnl)
- Add PostgreSQL backend provisioning and Molecule coverage
  (based in [\#216](https://github.com/PowerDNS/pdns-ansible/pull/216) @dtrdnk,
  [\#211](https://github.com/PowerDNS/pdns-ansible/pull/211) @Exchizz,
  [\#104](https://github.com/PowerDNS/pdns-ansible/pull/104) @commonism)
- Add role-level toggles for PostgreSQL backend bootstrap (`pdns_pgsql_manage_database`, `pdns_pgsql_schema_load`, `pdns_pgsql_schema_on_first_node_only`)
- Add role-level SELinux control via `pdns_manage_selinux` (enabled by default)
- Add service masking support via `pdns_service_masked`
- Add role verbosity toggle (`pdns_verbose`) to control redaction of sensitive SQL task logs
- Add architecture-aware APT repository settings for Debian-family systems (`pdns_apt_repo_arch` map with `amd64`/`arm64`)

IMPROVEMENTS:
- Include `mysql_schema_file` in MySQL import task names ([\#119](https://github.com/PowerDNS/pdns-ansible/pull/119), @zerwes)
- Run MySQL database commands on the first node only for clustered setups ([\#120](https://github.com/PowerDNS/pdns-ansible/pull/120), @zerwes)
- Remove `nolog` from backend install while still hiding passwords in logs ([\#175](https://github.com/PowerDNS/pdns-ansible/pull/175), @zerwes)
- Update `pdns-master` CI configuration and replace Ubuntu Bionic with Focal ([\#207](https://github.com/PowerDNS/pdns-ansible/pull/207), @romeroalx)
- Update SQLite3 backend defaults ([\#220](https://github.com/PowerDNS/pdns-ansible/pull/220), @kleini)
- Fix CI request handling in GitHub Actions ([\#221](https://github.com/PowerDNS/pdns-ansible/pull/221), @romeroalx)
- Upgrade CI tests to newer `molecule` and `ansible-core` versions ([\#230](https://github.com/PowerDNS/pdns-ansible/pull/230), @romeroalx)
- Update examples after variable deprecations ([\#240](https://github.com/PowerDNS/pdns-ansible/pull/240), @henkjan)
- Add Deb822 APT repository support on Debian-family systems while keeping legacy `apt_repo` compatibility
  (based on [\#242](https://github.com/PowerDNS/pdns-ansible/pull/242) @l00d3r,
  [\#246](https://github.com/PowerDNS/pdns-ansible/pull/246) @joshsol1)
- Bump `ansible-lint` to 6.18.0 ([\#190](https://github.com/PowerDNS/pdns-ansible/pull/190), @dependabot[bot])
- Rework MySQL bootstrap workflow for MySQL 8.4/9 and MariaDB compatibility:
  - socket/TCP selection with `pdns_mysql_query_use_socket` and `pdns_mysql_unix_socket`
  - configurable SQL CLI command/flags via `pdns_backends_mysql_cmd` and `pdns_mysql_cli_extra_args`
  - auth plugin and password-update controls via `pdns_mysql_auth_plugin` and `pdns_mysql_user_update_password`
- Improve PostgreSQL bootstrap workflow with socket/TCP selection and first-node-only execution controls
- Improve SQLite schema detection/import by supporting compressed schemas (`.gz`, `.xz`) and additional distro-specific paths
- Improve PowerDNS version detection by parsing both stdout/stderr to handle plugin load noise
- Consolidate OS variable loading order in role (`os_family` -> `distribution` -> major-version overrides)
- Standardize service management on `ansible.builtin.systemd` and apt cache updates through handlers

REMOVED / EOL:
- Drop pdns46 repository (EOL) ([\#208](https://github.com/PowerDNS/pdns-ansible/pull/208), @npmdnl)
- Remove EOL CI targets RHEL 7, Debian 10, and Ubuntu 20.04; add Debian 11, Debian 12, and Ubuntu 24.04 ([\#222](https://github.com/PowerDNS/pdns-ansible/pull/222), @romeroalx, [\#243](https://github.com/PowerDNS/pdns-ansible/pull/243), @romeroalx)
- Drop pdns47 repository (EOL) ([\#247](https://github.com/PowerDNS/pdns-ansible/pull/247), @npmdnl)
- Remove deprecated named-schema generation role components (`tasks/database-named.yml`, `templates/named.conf.j2`, `templates/named.zone.j2`)
- Remove version-specific RedHat vars files in favor of consolidated `vars/RedHat.yml`

BUG FIXES:
- Reorder `selinux.yml` include to resolve issue #122 ([\#123](https://github.com/PowerDNS/pdns-ansible/pull/123), @pixelrebel)
- Add missing closing braces ([\#172](https://github.com/PowerDNS/pdns-ansible/pull/172), @arjenz)
- Fix logging for grant access task ([\#195](https://github.com/PowerDNS/pdns-ansible/pull/195), @zerwes)
- Fix `pdns-os-repos` CI tests ([\#214](https://github.com/PowerDNS/pdns-ansible/pull/214), @romeroalx)
- Add missing RHEL-family packages required for SELinux support ([\#218](https://github.com/PowerDNS/pdns-ansible/pull/218), @vhsantos)
- Move PowerDNS restart logic to handlers ([\#244](https://github.com/PowerDNS/pdns-ansible/pull/244), @valiac)
- Exclude local `.ansible` cache directory from linting ([\#245](https://github.com/PowerDNS/pdns-ansible/pull/245), @valiac)
- Fix SELinux DB-connect boolean activation for both MySQL and PostgreSQL backends (including multi-instance backend names)
- Fix MySQL/MariaDB bootstrap on `caching_sha2_password` by adding required `python*-cryptography` dependencies in role defaults

## v1.8.0 (2023-08-03)

NEW FEATURES:
- Added pdns48 repository and CI ([\#180](https://github.com/PowerDNS/pdns-ansible/pull/180))
- Added support for OL9 ([\#145](https://github.com/PowerDNS/pdns-ansible/pull/145))
- Added pdns47 repository and CI ([\#135](https://github.com/PowerDNS/pdns-ansible/pull/135))
- Replaced Centos8 with OL8 ([\#133](https://github.com/PowerDNS/pdns-ansible/pull/133))
- Added pdns46 repository and CI ([\#117](https://github.com/PowerDNS/pdns-ansible/pull/117))

IMPROVEMENTS:
- Bump versions and various fixes in CI and README.md ([\#179](https://github.com/PowerDNS/pdns-ansible/pull/179)
- Bump versions in requirements.txt ([\#144](https://github.com/PowerDNS/pdns-ansible/pull/144))
- Removal of deprecation warning ([\#121](https://github.com/PowerDNS/pdns-ansible/pull/121))
- Do not restart all servers at once ([\#109](https://github.com/PowerDNS/pdns-ansible/pull/109))
- Prevent logging of password information ([\#106](https://github.com/PowerDNS/pdns-ansible/pull/106))

REMOVED FEATURES:
- Drop pdns45, support for Debian 9 ([\#179](https://github.com/PowerDNS/pdns-ansible/pull/179)) EOL
- Drop Ansible v2.9 - v2.10 - v2.11 from CI  and removed pdns43 and pdns44 ([\#144](https://github.com/PowerDNS/pdns-ansible/pull/144)) for EOL

BUG FIXES:
- Add MySQL schema path with PowerDNS 4.6 and Rocky Linux 8 with EPEL package installation ([\#114](https://github.com/PowerDNS/pdns-ansible/pull/114))

## v1.7.0 (2021-07-01)

NEW FEATURES:
- Create directory, set the ownership and permissions for LMDB databases ([\#95](https://github.com/PowerDNS/pdns-ansible/pull/95))
- Add database schema file detection on the target system with override possibility ([\#100](https://github.com/PowerDNS/pdns-ansible/pull/100))
- Add 4.4 repositories ([\#91](https://github.com/PowerDNS/pdns-ansible/pull/91))

IMPROVEMENTS:
- Use systemd task option `daemon_reload` instead of command task ([\#90](https://github.com/PowerDNS/pdns-ansible/pull/90))

REMOVED FEATURES:
- Drop EL6 support ([\#91](https://github.com/PowerDNS/pdns-ansible/pull/91), [\#94](https://github.com/PowerDNS/pdns-ansible/pull/94))
- Remove 4.1 and 4.2 repositories ([\#101](https://github.com/PowerDNS/pdns-ansible/pull/101))

BUG FIXES:
- Re-instate molecule tests ([\#100](https://github.com/PowerDNS/pdns-ansible/pull/100))

## v1.6.1 (2020-10-01)

BUG FIXES:
- Ensure install does not fail when no overrides are defined ([\#85](https://github.com/PowerDNS/pdns-ansible/pull/85))
- Ensure that `ExecStart` is overridden, not appended to ([\#86](https://github.com/PowerDNS/pdns-ansible/pull/86))

## v1.6.0 (2020-09-18)

BUG FIXES:
- Fix path to MySQL schema for Debian 10 ([\#73](https://github.com/PowerDNS/pdns-ansible/pull/73))

IMPROVEMENTS:
- Allow loading apt key from the ansible server ([\#75](https://github.com/PowerDNS/pdns-ansible/pull/75))
- CentOS 8 support ([\#74](https://github.com/PowerDNS/pdns-ansible/pull/74), [\#81](https://github.com/PowerDNS/pdns-ansible/pull/81))
- Archlinux support ([\#76](https://github.com/PowerDNS/pdns-ansible/pull/76))
- Set the ownership and permissions for config files and databases ([\#82](https://github.com/PowerDNS/pdns-ansible/pull/82))
- Ensure PowerDNS is started as an unprivileged user by default (in line with PowerDNS 4.3+ behaviour)

## v1.5.0 (2019-12-11)

BUG FIXES:
- - Fix the restart of the PowerDNS service in case of instances with different `pdns_service_name` being configured in the same play ([\#70](https://github.com/PowerDNS/pdns-ansible/pull/70))

IMPROVEMENTS:
- Add support to the PowerDNS 4.3.x release ([\#69](https://github.com/PowerDNS/pdns-ansible/pull/69))
- Add support to the PowerDNS 4.2.x release ([\#61](https://github.com/PowerDNS/pdns-ansible/pull/61))
- Install missing SQLite packages ([\#69](https://github.com/PowerDNS/pdns-ansible/pull/69))
- Improved PowerDNS configuration files and directories permissions handling ([\#69](https://github.com/PowerDNS/pdns-ansible/pull/69))
- Stop interpreting 0 & 1 as no & yes in the PowerDNS configuration template ([\#68](https://github.com/PowerDNS/pdns-ansible/pull/68))
- Fix some strings comparisons and variable types issues reported by ansible-lint ([\#66](https://github.com/PowerDNS/pdns-ansible/pull/66))
- Update the CI infrastructure to test the role against the Ansible 2.7, 2.8 and 2.9 releases ([\#67](https://github.com/PowerDNS/pdns-ansible/pull/67))
- Update the CI infrastructure to stop testing against an EOL Ubuntu release ([\#62](https://github.com/PowerDNS/pdns-ansible/pull/62))

## v1.4.0 (2018-12-02)

BUG FIXES:
- Fix handling of lists expansion in the PowerDNS configuration template ([\#55](https://github.com/PowerDNS/pdns-ansible/pull/55))

NEW FEATURES:
- Allow to disable automated restart of the service on configuration changes ([\#54](https://github.com/PowerDNS/pdns-ansible/pull/54))

## v1.3.0 (2018-07-13)

NEW FEATURES:
- Add support to systemd overrides definitions ([\#53](https://github.com/PowerDNS/pdns-ansible/pull/53))

IMPROVEMENTS:
- Implement stricter `pdns_config_dir` and `pdns_config['include-dir']` folders permissions ([\#53](https://github.com/PowerDNS/pdns-ansible/pull/53))
- Improved documentation ([\#52](https://github.com/PowerDNS/pdns-ansible/pull/52))
- Update the CI infrastructure to use molecule 2.14.0 ([\#51](https://github.com/PowerDNS/pdns-ansible/pull/51))
- Improved test coverage of systemd support ([\#49](https://github.com/PowerDNS/pdns-ansible/pull/49))

## v1.2.1 (2018-04-06)

BUG FIXES:
- Fix the name of the PostgreSQL backend on RHEL

## v1.2.0 (2018-04-05)

NEW FEATURES:
- Allow to install PowerDNS debug packages ([\#47](https://github.com/PowerDNS/pdns-ansible/pull/47))

IMPROVEMENTS:
- Improved test-suite ([\#47](https://github.com/PowerDNS/pdns-ansible/pull/47))
- Improved config files permissions handling ([\#45](https://github.com/PowerDNS/pdns-ansible/pull/45))

## v1.1.0 (2017-11-25)

IMPROVEMENTS:
- Implement testing against multiple ansible versions with tox ([\#43](https://github.com/PowerDNS/pdns-ansible/pull/43))

BUG FIXES:
- Fixed test cases and hardened file permissions ([\#42](https://github.com/PowerDNS/pdns-ansible/pull/42))

## v1.0.0 (2017-10-27)

IMPROVEMENTS:
- Implement sorting of the configuration options ([\#35](https://github.com/PowerDNS/pdns-ansible/pull/35), [\#37](https://github.com/PowerDNS/pdns-ansible/pull/37))

BUG FIXES:
- Fix the logic handling the different packages versions for Debian and CentOS ([\#43](https://github.com/PowerDNS/pdns-ansible/pull/43))
- Fix a few typos in the README file ([\#39](https://github.com/PowerDNS/pdns-ansible/pull/39))

## v0.1.1 (2017-10-10)

NEW FEATURES:
- Allow to pin the PowerDNS version to be installed ([\#34](https://github.com/PowerDNS/pdns-ansible/pull/34))

IMPROVEMENTS:
- Add support to the PowerDNS 4.1.x release ([\#33](https://github.com/PowerDNS/pdns-ansible/pull/33))
- Fixing minor linter issues with whitespace ([\#30](https://github.com/PowerDNS/pdns-ansible/pull/30))

BUG FIXES:
- Fix Ubuntu APT repositories pinning ([\#32](https://github.com/PowerDNS/pdns-ansible/pull/32))

## v0.1.0 (2017-06-27)

Initial release.

NEW FEATURES:
- MySQL and SQLite databases initialization
- PowerDNS installation and configuration with RHEL/CentOS and Debian/Ubuntu support
- Continuous testing with TravisCI

IMPROVEMENTS:
- Switch to the MIT License ([\#27](https://github.com/PowerDNS/pdns-ansible/pull/27))
- Overall role refactoring ([\#28](https://github.com/PowerDNS/pdns-ansible/pull/28))


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2017 PowerDNS.COM BV

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.


================================================
FILE: README.md
================================================
# Ansible Role: PowerDNS Authoritative Server

[![Build Status](https://github.com/PowerDNS/pdns-ansible/actions/workflows/main.yml/badge.svg)](https://github.com/PowerDNS/pdns-ansible)
[![License](https://img.shields.io/badge/license-MIT%20License-brightgreen.svg)](https://opensource.org/licenses/MIT)
[![Ansible Role](https://img.shields.io/badge/ansible%20role-PowerDNS.pdns-blue.svg)](https://galaxy.ansible.com/PowerDNS/pdns)
[![GitHub tag](https://img.shields.io/github/tag/PowerDNS/pdns-ansible.svg)](https://github.com/PowerDNS/pdns-ansible/tags)

An Ansible role created by the folks behind PowerDNS to setup the [PowerDNS Authoritative Server](https://docs.powerdns.com/authoritative/).

## Requirements

An Ansible 2.15 or higher installation.

## Dependencies

None.

## Role Variables

Available variables are listed below, along with their default values (see `defaults/main.yml`):

```yaml
pdns_install_repo: ""
```

By default, the PowerDNS Authoritative Server is installed from the software repositories configured on the target hosts.

```yaml
# Install the PowerDNS Authoritative Server from the 'master' official repository
- hosts: all
  roles:
    - { role: PowerDNS.pdns,
        pdns_install_repo: "{{ pdns_auth_powerdns_repo_master }}" }


# Install the PowerDNS Authoritative Server from the '4.8.x' official repository
- hosts: all
  roles:
    - { role: PowerDNS.pdns,
        pdns_install_repo: "{{ pdns_auth_powerdns_repo_48 }}" }

# Install the PowerDNS Authoritative Server from the '4.9.x' official repository
- hosts: all
  roles:
    - { role: PowerDNS.pdns,
        pdns_install_repo: "{{ pdns_auth_powerdns_repo_49 }}" }

# Install the PowerDNS Authoritative Server from the '5.0.x' official repository
- hosts: all
  roles:
    - { role: PowerDNS.pdns,
        pdns_install_repo: "{{ pdns_auth_powerdns_repo_50 }}" }
```

The examples above, show how to install the PowerDNS Authoritative Server from the official PowerDNS repositories
(see the complete list of pre-defined repos in `vars/main.yml`).

```yaml
- hosts: all
  vars:
    pdns_install_repo:
      name: "powerdns" # the name of the repository
      apt_repo_origin: "example.com"  # used to pin the PowerDNS packages to the provided repository
      apt_version: "auth-50"  # deb822 suites suffix (appended to release codename)
      apt_repo: "deb http://example.com/{{ ansible_distribution | lower }} {{ ansible_distribution_release | lower }}/pdns main"
      gpg_key: "http://example.com/MYREPOGPGPUBKEY.asc" # repository public GPG key
      gpg_key_id: "MYREPOGPGPUBKEYID" # to avoid to reimport the key each time the role is executed
      yum_repo_baseurl: "http://example.com/centos/$basearch/$releasever/pdns"
      yum_debug_symbols_repo_baseurl: "http://example.com/centos/$basearch/$releasever/pdns/debug"
  roles:
    - { role: PowerDNS.pdns }
```

It is also possible to install the PowerDNS Authoritative Server from custom repositories as demonstrated in the example above.
**Note:** These repositories are ignored on Arch Linux

When `pdns_install_repo.apt_version` is set, this role configures Debian-family repositories using
`ansible.builtin.deb822_repository` on supported releases (Ubuntu `>=22.04`, Debian `>=11`).
If `apt_version` is omitted, the legacy `apt_repo` string is used with `ansible.builtin.apt_repository`.

```yaml
 pdns_install_epel: true
```

By default, install EPEL to satisfy some PowerDNS Authoritative Server dependencies like `protobuf`.
To skip the installation of EPEL set `pdns_install_epel` to `false`.

```yaml
pdns_package_name: "{{ default_pdns_package_name }}"
```

The name of the PowerDNS Authoritative Server package, `pdns` on RedHat-like systems and `pdns-server` on Debian-like systems.

```yaml
pdns_package_version: ""
```

Optionally, allow to set a specific version of the PowerDNS Authoritative Server package to be installed.

```yaml
pdns_package_state: "present"
```

Desired package state for `pdns_package_name`. Supported values include `present`, `latest`, and `absent`.
When set to `absent`, the role removes packages and skips runtime configuration tasks.

```yaml
pdns_install_debug_symbols_package: false
```

Install the PowerDNS Authoritative Server debug symbols.

```yaml
pdns_debug_symbols_package_name: "{{ default_pdns_debug_symbols_package_name }}"
```

The name of the PowerDNS Authoritative Server debug package to be installed when `pdns_install_debug_symbols_package` is `true`,
`pdns-debuginfo` on RedHat-like systems and `pdns-server-dbg` on Debian-like systems.

```yaml
pdns_debug_symbols_package_state: "{{ pdns_package_state }}"
```

Desired package state for the debug symbols package when it is managed by this role.

```yaml
pdns_user: pdns
pdns_group: pdns
pdns_file_owner: root
pdns_file_group: "{{ pdns_group }}"
```

The user and group the PowerDNS Authoritative Server process will run as. <br />
**NOTE**: This role does not create the user or group as we assume that they've been created
by the package or other roles.

```yaml
pdns_service_name: "pdns"
```

Name of the PowerDNS service.

```yaml
pdns_service_state: "started"
pdns_service_enabled: true
pdns_service_masked: false
```

Allow to specify the desired state of the PowerDNS Authoritative Server service.

```yaml
pdns_disable_handlers: false
```

Disable automated service restart on configuration changes.

```yaml
pdns_manage_selinux: true
```

Enable management of SELinux booleans and ports on SELinux-enabled systems.
Set to `false` to skip SELinux changes entirely.

```yaml
pdns_config_dir: "{{ default_pdns_config_dir }}"
pdns_config_file: "pdns.conf"
```

PowerDNS Authoritative Server configuration file and directory.

```yaml
pdns_config: {}
```

Dictionary containing the PowerDNS Authoritative Server configuration. <br />
**NOTE:** The PowerDNS backends configuration and the `config-dir`, `setuid` and `setgid` directives must be configured through the `pdns_user`, `pdns_group` and `pdns_backends` role variables (see `templates/pdns.conf.j2`).
For example:

```yaml
pdns_config:
  primary: true
  secondary: false
  local-address: '192.0.2.53'
  local-ipv6: '2001:DB8:1::53'
  local-port: '5300'
```

configures PowerDNS Authoritative Server to listen incoming DNS requests on port 5300.

```yaml
pdns_service_overrides:
  User: "{{ pdns_user }}"
  Group: "{{ pdns_group }}"
```

Dict with overrides for the service (systemd only).
This can be used to change any systemd settings in the `[Service]` category.

```yaml
pdns_backends_packages: "{{ default_pdns_backends_packages }}"
pdns_backends_packages_state: "{{ pdns_package_state }}"
pdns_backends:
  bind:
    config: '/dev/null'
```

Dictionary declaring all the backends you'd like to enable. You can use
multiple backends of the same kind by using the `{backend}:{instance_name}` syntax.
For example:

```yaml
pdns_backends:
  'gmysql:one':
    'user': root
    'host': 127.0.0.1
    'password': root
    'dbname': pdns
  'gmysql:two':
    'user': pdns_user
    'host': 192.0.2.15
    'password': my_password
    'dbname': dns
  'bind':
    'config': '/etc/named/named.conf'
    'hybrid': true
    'dnssec-db': '{{ pdns_config_dir }}/dnssec.db'
```

By default this role starts just the bind-backend with an empty config file.
`pdns_backends_packages_state` controls install/update/removal of backend packages.

```yaml
pdns_config_additional_dirs: []
```

Optional list of directories created before `pdns_config_files` are copied.
Each item can be either a path string or an object with `path`, `owner`, `group`, `mode`.
For example:

```yaml
pdns_config_additional_dirs:
  - path: "{{ pdns_config['include-dir'] }}"
    mode: "0775"
  - "{{ pdns_config_dir }}/zones"
  - "/var/lib/powerdns/rpz"
```

```yaml
pdns_config_files: []
```

Optional list of files copied before the service is started.
Each item must define `dest` and one of `src` or `content`.
`dest` can be absolute or relative to `pdns_config_dir`.
Executable backend helper scripts should be shipped via this variable too
(for example with `mode: "0750"`).
For example:

```yaml
pdns_config_files:
  - src: files/pdns/named.conf
    dest: named.conf
    mode: "0640"
  - dest: pipe-backend.py
    mode: "0750"
    content: |
      #!/usr/bin/env python3
      print("example")
```

```yaml
pdns_mysql_manage_database: true
pdns_mysql_databases_credentials: {}
pdns_mysql_query_use_socket: false
pdns_mysql_unix_socket: "/var/run/mysqld/mysqld.sock"
pdns_backends_mysql_cmd: "{{ default_pdns_backends_mysql_cmd }}"
pdns_mysql_cli_extra_args: "{{ default_pdns_mysql_cli_extra_args }}"
pdns_mysql_auth_plugin: ""
pdns_mysql_user_update_password: ""
pdns_mysql_packages: "{{ default_pdns_mysql_packages }}"
pdns_mysql_packages_state: "present"
```

`pdns_mysql_manage_database` controls whether this role performs MySQL/MariaDB bootstrap operations
(database creation, user/grants management and schema checks/import).
Set it to `false` for config-only mode.

Administrative credentials for the MySQL backend used to create the PowerDNS Authoritative Server databases and users.
For example:

```yaml
pdns_mysql_databases_credentials:
  'gmysql:one':
    'priv_user': root
    'priv_password': my_first_password
    'priv_host':
      - "localhost"
      - "%"
  'gmysql:two':
    'priv_user': someprivuser
    'priv_password': my_second_password
    'priv_host':
      - "localhost"
```

Notice that this must only contain the credentials
for the `gmysql` backends provided in `pdns_backends`.

When `pdns_mysql_query_use_socket` is set to `true`, role-internal MySQL operations
(database/user creation and schema load checks/import) use the UNIX socket path defined by
`pdns_mysql_unix_socket` instead of TCP host/port.
`pdns_backends_mysql_cmd` and `pdns_mysql_cli_extra_args` control the MySQL/MariaDB CLI invocation used for schema checks/import.
`pdns_mysql_packages` allows overriding OS-specific MySQL dependency package lists.
`pdns_mysql_packages_state` controls install/update/removal of those dependency packages.

```yaml
pdns_pgsql_manage_database: true
pdns_pgsql_databases_credentials: {}
pdns_pgsql_packages: "{{ default_pdns_pgsql_packages }}"
pdns_pgsql_packages_state: "present"
```

`pdns_pgsql_manage_database` controls whether this role performs PostgreSQL bootstrap operations
(database/user creation and schema checks/import).
Set it to `false` for config-only mode.

Administrative credentials for the PostgreSQL backend used to create the PowerDNS Authoritative Server databases and users.
For example:

```yaml
pdns_pgsql_databases_credentials:
  'gpgsql:one':
    priv_user: postgres
    priv_password: my_first_password
```

Notice that this must only contain the credentials
for the `gpgsql` backends provided in `pdns_backends`.

```yaml
pdns_pgsql_query_use_socket: false
pdns_pgsql_unix_socket: "/var/run/postgresql"
```

When `pdns_pgsql_query_use_socket` is set to `true`, role-internal PostgreSQL operations
(database/user creation and schema load checks/import) use the UNIX socket path defined by
`pdns_pgsql_unix_socket` instead of TCP host/port.
`pdns_pgsql_packages` allows overriding OS-specific PostgreSQL dependency package lists.
`pdns_pgsql_packages_state` controls install/update/removal of those dependency packages.

```yaml
pdns_sqlite_databases_locations: []
```

Locations of the SQLite3 databases that have to be created if using the
`gsqlite3` backend.

```yaml
pdns_sqlite_package_state: "present"
```

Desired package state for the SQLite CLI dependency used during schema bootstrap.

```yaml
pdns_lmdb_databases_locations: []
```

Locations of the LMDB databases that have to be created if using the
`lmdb` backend.

Locations of the mysql, pgsql and sqlite3 base schema.
When set, this value is used and they are not automatically detected.
```yaml
pdns_mysql_schema_load: true
pdns_mysql_schema_file: ''
pdns_mysql_schema_on_first_node_only: true

pdns_pgsql_schema_load: true
pdns_pgsql_schema_file: ''
pdns_pgsql_schema_on_first_node_only: true

pdns_sqlite_schema_file: ''
```

`pdns_mysql_schema_load` and `pdns_pgsql_schema_load` only control schema check/import tasks.
When SQL bootstrap is enabled (`pdns_mysql_manage_database` / `pdns_pgsql_manage_database`) and
administrative credentials are provided, user/database creation still runs even if schema load is disabled.

`pdns_mysql_schema_on_first_node_only` and `pdns_pgsql_schema_on_first_node_only` control
cluster bootstrap execution for shared SQL backends (database/user/grants/schema import).

```yaml
pdns_verbose: "{{ ansible_verbosity | int >= 2 }}"
```

Enable verbose/debug role behavior. This currently controls whether sensitive SQL task details
are hidden in logs (`false`) or visible for troubleshooting (`true`).

## Role Tags

This role uses the following standard tags so filtered runs stay predictable with `--tags` / `--skip-tags`:

- `install`: package/module installation or software provisioning.
- `config`: configuration/state changes (templates, files, directories, settings, data bootstrap).
- `service`: service state management and service-related handlers.
- `repository`: repository/key/pinning setup and repository cache refresh.

Some prerequisite tasks intentionally have multiple tags (for example `install` + `repository`,
or `install` + `config`) so filtered runs include the dependencies required by the selected path.

## Example Playbooks

Run as a primary using the bind backend (when you already have a `named.conf` file):

```yaml
- hosts: ns1.example.net
  roles:
    - { role: PowerDNS.pdns }
  vars:
    pdns_config:
      primary: true
      local-address: '192.0.2.53'
    pdns_backends:
      bind:
        config: '/etc/named/named.conf'
```

Install the latest '50' build of PowerDNS Authoritative Server enabling the MySQL backend.
Provides also the MySQL administrative credentials to automatically create and initialize the PowerDNS Authoritative Server user and database:

```yaml
- hosts: ns2.example.net
  roles:
    - { role: PowerDNS.pdns }
  vars:
    pdns_config:
      primary: true
      secondary: false
      local-address: '192.0.2.77'
    pdns_backends:
      gmysql:
        host: 192.0.2.120
        port: 3306
        user: powerdns
        password: P0w3rDn5
        dbname: pdns
    pdns_mysql_databases_credentials:
      gmysql:
        priv_user: root
        priv_password: myrootpass
        priv_host:
          - "%"
    pdns_install_repo: "{{ pdns_auth_powerdns_repo_50 }}"
```

**NOTE:** In this case the role will use the credentials provided in `pdns_mysql_databases_credentials` to automatically create and initialize the user (`user`, `password`) and database (`dbname`) connecting to the MySQL server (`host`, `port`).

Configure PowerDNS Authoritative Server in 'primary' mode reading zones from two different PostgreSQL databases:

```yaml
- hosts: ns2.example.net
  roles:
    - { role: PowerDNS.pdns }
  vars:
    pdns_config:
      primary: true
      local-port: 5300
      local-address: '192.0.2.111'
    pdns_backends:
      'gpgsql:serverone':
        host: 192.0.2.124
        user: powerdns
        password: P0w3rDn5
        dbname: pdns2
      'gpgsql:otherserver':
        host: 192.0.2.125
        user: root
        password: root
        dbname: dns
```

Configure PowerDNS Authoritative Server to run with the `gsqlite3` backend.
The SQLite database will be created and initialized by the role
in the location specified by the `database_name` variable.

```yaml
- hosts: ns4.example.net
  roles:
    - { role: PowerDNS.pdns }
  vars:
    database_name: '/var/lib/powerdns/pdns.sqlite3'
    pdns_config:
      primary: true
      secondary: false
      local-address: '192.0.2.73'
    pdns_backends:
      gsqlite3:
        database: "{{ database_name }}"
        dnssec: true
    pdns_sqlite_databases_locations:
      - "{{ database_name }}"
```

## Changelog

A detailed changelog of all the changes applied to the role is available [here](./CHANGELOG.md).

## Testing

Tests are performed by [Molecule](http://molecule.readthedocs.org/en/latest/).

    $ pip install tox

To test all the scenarios run

    $ tox

To run a custom molecule command

    $ tox -e ansible216 -- molecule test -s pdns-50

The Molecule backend matrix validates LMDB, SQLite3, MySQL, MariaDB, BIND and PostgreSQL instance profiles.

## License

MIT


================================================
FILE: defaults/main.yml
================================================
---

# By default, no PowerDNS Authoritative Server repository will be configured by the role
pdns_install_repo: ""

# To install the PowerDNS Authoritative Server from the 'master' official repository
# use the following playbook snippet
# - hosts: all
#   roles:
#    - { role: PowerDNS.pdns,
#        pdns_install_repo: "{{ pdns_auth_powerdns_repo_master }}" }
#
# To install the PowerDNS Authoritative Server from the '4.8.x' official repository
# use the following playbook snippet
# - hosts: all
#   roles:
#    - { role: PowerDNS.pdns,
#        pdns_install_repo: "{{ pdns_auth_powerdns_repo_48 }}" }

# To install the PowerDNS Authoritative Server from the '4.9.x' official repository
# use the following playbook snippet
# - hosts: all
#   roles:
#    - { role: PowerDNS.pdns,
#        pdns_install_repo: "{{ pdns_auth_powerdns_repo_49 }}" }

# To install the PowerDNS Authoritative Server from the '5.0.x' official repository
# use the following playbook snippet
# - hosts: all
#   roles:
#    - { role: PowerDNS.pdns,
#        pdns_install_repo: "{{ pdns_auth_powerdns_repo_50 }}" }
#
# To make this role configure a custom repository and install the
# PowerDNS Authoritative Server from it override the `pdns_install_repo` variable
# as follows
# - hosts: all
#   vars:
#     pdns_install_repo:
#       apt_repo_origin: "example.com"  # Pin the PowerDNS packages to the provided repository origin
#       apt_version: "auth-50"          # Deb822 suite suffix (appended to release codename, Ubuntu >=22.04 / Debian >=11)
#       apt_repo: "deb http://example.com/{{ ansible_distribution | lower }} {{ ansible_distribution_release | lower }}/pdns main"
#       gpg_key: "http://example.com/MYREPOGPGPUBKEY.asc" # repository public GPG key
#       gpg_key_id: "MYREPOGPGPUBKEYID" # to avoid to reimport the key each time the role is executed
#       yum_repo_baseurl: "http://example.com/centos/$basearch/$releasever/pdns"
#       name: "powerdns"       # the name of the repository
#   roles:
#    - { role: PowerDNS.pdns }

# Install the EPEL repository.
# EPEL is needed to satisfy some PowerDNS Authoritative Server dependencies like protobuf
pdns_install_epel: true

# The name of the PowerDNS Authoritative Server package
pdns_package_name: "{{ default_pdns_package_name }}"

# Install a specific version of the PowerDNS Authoritative Server package
# NB: The usage of this variable makes only sense on RedHat-like systems,
#     where each YUM repository can contains multiple versions of the same package.
pdns_package_version: ""

# Desired state of the PowerDNS Authoritative Server package.
# Supported values include present, latest and absent.
pdns_package_state: "present"

# Install the PowerDNS Authoritative Server debug symbols package
pdns_install_debug_symbols_package: false

# The name of the PowerDNS Authoritative Server debug symbols package
pdns_debug_symbols_package_name: "{{ default_pdns_debug_symbols_package_name }}"

# Desired state of the debug symbols package when managed by this role.
# Supported values include present, latest and absent.
pdns_debug_symbols_package_state: "{{ pdns_package_state }}"

# The user and group the PowerDNS Authoritative Server process will run as.
# NOTE: at the moment, we don't create a user as we assume the package creates
# a "pdns" user and group. If you change these variables, make sure to create
# the user and groups before applying this role
pdns_user: "pdns"
pdns_group: "pdns"
pdns_file_owner: "root"
pdns_file_group: "{{ pdns_group }}"

# Name of the PowerDNS Authoritative Server Service
pdns_service_name: "pdns"

# State of the PowerDNS Authoritative Server service
pdns_service_state: "started"
pdns_service_enabled: true
pdns_service_masked: false

# When True, disable the automated restart of the PowerDNS service
pdns_disable_handlers: false

# When true, manage SELinux booleans and ports on SELinux-enabled systems.
pdns_manage_selinux: true

# PowerDNS Authoritative Server configuration file and directory
pdns_config_dir: "{{ default_pdns_config_dir }}"
pdns_config_file: "pdns.conf"

# Dict containing all configuration options, except for backend
# configuration and the "config-dir", "setuid" and "setgid" directives.
pdns_config: {}
# pdns_config:
#  primary: true
#  secondary: false
#  local-address: '192.0.2.53'
#  local-ipv6: '2001:DB8:1::53'
#  local-port: '5300'

# Dict with overrides for the service (systemd only)
pdns_service_overrides: "{{ default_pdns_service_overrides }}"
# pdns_service_overrides:
#   LimitNOFILE: 10000

# Dictionary of packages that should be installed to enable the backends.
# backendname: packagename
pdns_backends_packages: "{{ default_pdns_backends_packages }}"

# Desired state of backend packages.
# Supported values include present, latest and absent.
pdns_backends_packages_state: "{{ pdns_package_state }}"

# A dict with all the backends you'd like to configure.
# This default starts just the bind-backend with an empty config file
pdns_backends:
  bind:
    config: '/dev/null'
# pdns_backends:
#   'gmysql:one':
#     'user': root
#     'host': 127.0.0.1
#     'password': root
#     'dbname': pdns
#   'gmysql:two':
#     'user': pdns_user
#     'host': 192.0.2.15
#     'port': 3307
#     'password': my_password
#     'dbname': dns
#   'bind':
#     'config': '/etc/named/named.conf'
#     'hybrid': true
#     'check-interval': 60
#     'dnssec-db': '{{ pdns_config_dir }}/dnssec.db'

# Additional directories to create before writing pdns_config_files.
# Each item can be:
# - a string path
# - or a dict with path/owner/group/mode
#
# Examples:
# pdns_config_additional_dirs:
#   - path: "{{ pdns_config['include-dir'] }}"
#     mode: "0775"
#   - "{{ pdns_config_dir }}/zones"
#   - "/var/lib/powerdns/rpz"
pdns_config_additional_dirs: []

# Files to copy into the PowerDNS configuration directory.
# Each item supports:
# - src: path on the controller
# - content: inline file content
# - dest: destination path (absolute, or relative to pdns_config_dir)
# - owner/group/mode: optional file attributes
# - dir_owner/dir_group/dir_mode: optional parent directory attributes
#
# Examples:
# pdns_config_files:
#   - src: files/pdns/my-named.conf
#     dest: named.conf
#     mode: "0640"
#   - dest: pipe-backend.py
#     mode: "0750"
#     content: |
#       #!/usr/bin/env python3
#       print("backend")
pdns_config_files: []

# Enable role-managed MySQL backend bootstrap tasks.
# When false, the role will only write gmysql backend settings in pdns.conf and won't connect to MySQL/MariaDB.
pdns_mysql_manage_database: true

# Administrative credentials to create the PowerDNS Authoritative Server MySQL backend database and user.
pdns_mysql_databases_credentials: {}
# pdns_mysql_databases_credentials:
#   'gmysql:one':
#     'priv_user': root
#     'priv_password': my_first_password
#     'priv_host':
#       - "localhost"
#       - "%"
#   'gmysql:two':
#     'priv_user': someprivuser
#     'priv_password': my_second_password
#     'priv_host':
#       - "localhost"

# Use a UNIX socket for role-internal MySQL operations (db create, grants, schema checks/import).
pdns_mysql_query_use_socket: false

# Packages needed to install MySQL dependencies.
# By default, OS-specific vars files provide the actual package list.
pdns_mysql_packages: "{{ default_pdns_mysql_packages }}"

# Desired state of MySQL dependency packages.
# Supported values include present, latest and absent.
pdns_mysql_packages_state: "{{ pdns_package_state }}"

# UNIX socket path used when pdns_mysql_query_use_socket is true.
pdns_mysql_unix_socket: "/var/run/mysqld/mysqld.sock"

# MySQL/MariaDB CLI command used for schema checks/import.
pdns_backends_mysql_cmd: "{{ default_pdns_backends_mysql_cmd }}"

# Additional arguments appended to the MySQL/MariaDB CLI command used for schema checks/import.
pdns_mysql_cli_extra_args: "{{ default_pdns_mysql_cli_extra_args }}"

# Authentication plugin used for created/granted MySQL users.
# Keep empty to let the module use legacy password flow (mysql_native_password path).
# Set to caching_sha2_password for MySQL 8.4/9+ servers where mysql_native_password is unavailable.
pdns_mysql_auth_plugin: ""

# Password update strategy for mysql_user task.
# Accepted values: always, on_create, on_new_username.
# Keep empty for role defaults:
# - on_create when pdns_mysql_auth_plugin is set
# - always when pdns_mysql_auth_plugin is empty
pdns_mysql_user_update_password: ""

# Enable role-managed PostgreSQL backend bootstrap tasks.
# When false, the role will only write gpgsql backend settings in pdns.conf and won't connect to PostgreSQL.
pdns_pgsql_manage_database: true

# Administrative credentials to create the PowerDNS Authoritative Server PostgreSQL backend database and user.
pdns_pgsql_databases_credentials: {}
# pdns_pgsql_databases_credentials:
#   gpgsql:
#     priv_user: postgres
#     priv_password: my_privileged_password

# Use a UNIX socket for role-internal PostgreSQL operations (db create, schema checks/import).
pdns_pgsql_query_use_socket: false

# Packages needed to install PostgreSQL dependencies.
# By default, OS-specific vars files provide the actual package list.
pdns_pgsql_packages: "{{ default_pdns_pgsql_packages }}"

# Desired state of PostgreSQL dependency packages.
# Supported values include present, latest and absent.
pdns_pgsql_packages_state: "{{ pdns_package_state }}"

# UNIX socket path used when pdns_pgsql_query_use_socket is true.
pdns_pgsql_unix_socket: "/var/run/postgresql"

# This will create the PowerDNS Authoritative Server backend SQLite database
# in the given locations.
# NOTE: Requires the SQLite CLI tools to be available in the machine and the gsqlite3
# backend to be installed on the machine.
pdns_sqlite_databases_locations: []

# Desired state of the SQLite CLI dependency package.
# Supported values include present, latest and absent.
pdns_sqlite_package_state: "{{ pdns_package_state }}"

# This will create the PowerDNS Authoritative Server backend LMDB database
# in the given locations.
# NOTE: Requires lmdb backend to be installed on the machine.
pdns_lmdb_databases_locations: []

# By default, we'll load the MySQL default schema. Set this to false to disable loading the schema
# (e.g. when importing your own dump later on).
# NOTE: This controls only schema check/import. User/database creation and grants are still executed
# when pdns_mysql_manage_database is true and administrative credentials are provided.
pdns_mysql_schema_load: true

# Override the schema used to initialize the MySQL database
# By default, this role tries to detect the correct file
pdns_mysql_schema_file: ""

# Run MySQL backend bootstrap tasks only on first node?
# This includes database creation, grants and schema import.
# This should be used if you install pdns on a cluster
pdns_mysql_schema_on_first_node_only: true

# By default, we'll load the PostgreSQL default schema. Set this to false to disable loading the schema
# (e.g. when importing your own dump later on).
# NOTE: This controls only schema check/import. User/database creation is still executed
# when pdns_pgsql_manage_database is true and administrative credentials are provided.
pdns_pgsql_schema_load: true

# Override the schema used to initialize the PostgreSQL database
# By default, this role tries to detect the correct file
pdns_pgsql_schema_file: ""

# Run pgsql backend bootstrap tasks only on first node?
# This includes database/user creation and schema import.
# This should be used if you install pdns on a cluster
pdns_pgsql_schema_on_first_node_only: true

# Override the schema used to initialize the SQLite database
# By default, this role tries to detect the correct file
pdns_sqlite_schema_file: ""

# Set debug mode flag from Ansible verbosity
pdns_verbose: "{{ ansible_verbosity | int >= 2 }}"


================================================
FILE: handlers/main.yml
================================================
---
- name: Reload systemd
  ansible.builtin.systemd:
    daemon_reload: true
  listen: reload systemd
  when: not pdns_disable_handlers
  tags:
    - service

- name: Restart PowerDNS
  ansible.builtin.systemd:
    name: "{{ pdns_service_name }}"
    state: restarted
  listen: restart pdns
  when:
    - not pdns_disable_handlers
    - pdns_service_state != 'stopped'
  tags:
    - service

- name: Update the apt cache
  ansible.builtin.apt:
    update_cache: true
  listen: update the apt cache
  tags:
    - install
    - repository


================================================
FILE: meta/main.yml
================================================
---

galaxy_info:
  role_name: "pdns"
  namespace: "powerdns"
  author: PowerDNS Engineering Team
  description: Install and configure the PowerDNS Authoritative DNS Server
  company: PowerDNS.COM BV
  license: MIT
  min_ansible_version: "2.15"
  platforms:
    - name: EL
      versions:
        - "8"
        - "9"
        - "10"
    - name: Debian
      versions:
        - bullseye
        - bookworm
        - trixie
    - name: Ubuntu
      versions:
        - focal
        - jammy
        - noble
    - name: ArchLinux
  galaxy_tags:
    - system
    - dns
    - pdns
    - powerdns
    - auth


================================================
FILE: molecule/pdns-48/converge.yml
================================================
---

- name: PowerDNS 4.8.x LMDB default instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-repo-48.yml
    - ../resources/vars/pdns-backend-lmdb.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_lmdb | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }
  post_tasks:
    - name: Check if the LMDB instance zone exists
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} list-zone {{ pdns_instance.zone }}"
      register: _pdns_backend_zone_check
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: false
      failed_when: false

    - name: Provision the LMDB instance zone
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} create-zone {{ pdns_instance.zone }} ns1.{{ pdns_instance.zone }}"
      register: _pdns_backend_zone_created
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: _pdns_backend_zone_check.rc != 0
      notify: Restart the LMDB instance service
      when: _pdns_backend_zone_check.rc != 0
  handlers:
    - name: Restart the LMDB instance service
      ansible.builtin.systemd:
        name: "{{ pdns_instance.service_name }}"
        state: restarted

- name: PowerDNS 4.8.x SQLite instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-repo-48.yml
    - ../resources/vars/pdns-backend-sqlite3.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_sqlite3 | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }
  post_tasks:
    - name: Check if the SQLite instance zone exists
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} list-zone {{ pdns_instance.zone }}"
      register: _pdns_backend_zone_check
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: false
      failed_when: false

    - name: Provision the SQLite instance zone
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} create-zone {{ pdns_instance.zone }} ns1.{{ pdns_instance.zone }}"
      register: _pdns_backend_zone_created
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: _pdns_backend_zone_check.rc != 0
      notify: Restart the SQLite instance service
      when: _pdns_backend_zone_check.rc != 0
  handlers:
    - name: Restart the SQLite instance service
      ansible.builtin.systemd:
        name: "{{ pdns_instance.service_name }}"
        state: restarted

- name: PowerDNS 4.8.x MySQL instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-repo-48.yml
    - ../resources/vars/pdns-backend-mysql.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_mysql | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }
  post_tasks:
    - name: Check if the MySQL instance zone exists
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} list-zone {{ pdns_instance.zone }}"
      register: _pdns_backend_zone_check
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: false
      failed_when: false

    - name: Provision the MySQL instance zone
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} create-zone {{ pdns_instance.zone }} ns1.{{ pdns_instance.zone }}"
      register: _pdns_backend_zone_created
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: _pdns_backend_zone_check.rc != 0
      notify: Restart the MySQL instance service
      when: _pdns_backend_zone_check.rc != 0
  handlers:
    - name: Restart the MySQL instance service
      ansible.builtin.systemd:
        name: "{{ pdns_instance.service_name }}"
        state: restarted

- name: PowerDNS 4.8.x MariaDB instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-repo-48.yml
    - ../resources/vars/pdns-backend-mariadb.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_mariadb | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }
  post_tasks:
    - name: Check if the MariaDB instance zone exists
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} list-zone {{ pdns_instance.zone }}"
      register: _pdns_backend_zone_check
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: false
      failed_when: false

    - name: Provision the MariaDB instance zone
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} create-zone {{ pdns_instance.zone }} ns1.{{ pdns_instance.zone }}"
      register: _pdns_backend_zone_created
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: _pdns_backend_zone_check.rc != 0
      notify: Restart the MariaDB instance service
      when: _pdns_backend_zone_check.rc != 0
  handlers:
    - name: Restart the MariaDB instance service
      ansible.builtin.systemd:
        name: "{{ pdns_instance.service_name }}"
        state: restarted

- name: PowerDNS 4.8.x Bind instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-repo-48.yml
    - ../resources/vars/pdns-backend-bind.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_bind | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }

- name: PowerDNS 4.8.x PostgreSQL instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-repo-48.yml
    - ../resources/vars/pdns-backend-postgresql.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_pgsql | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }
  post_tasks:
    - name: Check if the PostgreSQL instance zone exists
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} list-zone {{ pdns_instance.zone }}"
      register: _pdns_backend_zone_check
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: false
      failed_when: false

    - name: Provision the PostgreSQL instance zone
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} create-zone {{ pdns_instance.zone }} ns1.{{ pdns_instance.zone }}"
      register: _pdns_backend_zone_created
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: _pdns_backend_zone_check.rc != 0
      notify: Restart the PostgreSQL instance service
      when: _pdns_backend_zone_check.rc != 0
  handlers:
    - name: Restart the PostgreSQL instance service
      ansible.builtin.systemd:
        name: "{{ pdns_instance.service_name }}"
        state: restarted

- name: PowerDNS 4.8.x Hide default service
  hosts: pdns
  vars:
    pdns_service_state: "stopped"
    pdns_service_enabled: "no"
    pdns_service_masked: true
  roles:
    - { role: powerdns.pdns }


================================================
FILE: molecule/pdns-48/molecule.yml
================================================
---

scenario:
  name: pdns-48

driver:
  name: docker

dependency:
  name: galaxy

platforms:
  - name: rockylinux-8
    groups: ["pdns"]
    image: rockylinux:8
    dockerfile_tpl: el-systemd

  - name: rockylinux-9
    groups: ["pdns"]
    image: rockylinux:9
    dockerfile_tpl: el-systemd

  - name: almalinux-8
    groups: ["pdns"]
    image: almalinux:8
    dockerfile_tpl: el-systemd

  - name: almalinux-9
    groups: ["pdns"]
    image: almalinux:9
    dockerfile_tpl: el-systemd

  - name: oraclelinux-8
    groups: ["pdns"]
    image: oraclelinux:8
    dockerfile_tpl: el-systemd

  - name: oraclelinux-9
    groups: ["pdns"]
    image: oraclelinux:9
    dockerfile_tpl: el-systemd

  - name: ubuntu-2204
    groups: ["pdns"]
    image: ubuntu:22.04
    dockerfile_tpl: debian-systemd

  - name: debian-11
    groups: ["pdns"]
    image: debian:11
    dockerfile_tpl: debian-systemd

  - name: debian-12
    groups: ["pdns"]
    image: debian:12
    dockerfile_tpl: debian-systemd

  # In order to run the tests we need
  # a MySQL container to be up & running
  - name: mysql
    image: mysql:8.4.8
    env:
      MYSQL_ROOT_PASSWORD: pdns
      MYSQL_ROOT_HOST: '%'
    # Declaring the container as service,
    # will link it to the others Platforms containers
    # on creation.
    is_service: true

  # Additional service for gmysql tests against MariaDB 10.6
  - name: mariadb
    image: mariadb:10.6
    env:
      MARIADB_ROOT_PASSWORD: pdns
      MARIADB_ROOT_HOST: '%'
    is_service: true

  # PostgreSQL service for gpgsql backend tests
  - name: postgresql
    image: postgres:16
    env:
      POSTGRES_PASSWORD: pdns
    is_service: true

provisioner:
  name: ansible
  options:
    diff: true
    v: true
  config_options:
    defaults:
      gathering: smart
      fact_caching: jsonfile
      fact_caching_connection: .ansible_cache
      fact_caching_timeout: 7200
    ssh_connection:
      pipelining: true
  playbooks:
    # cleanup: ../resources/cleanup.yml
    create: ../resources/create.yml
    destroy: ../resources/destroy.yml
    prepare: ../resources/prepare.yml
  lint: ansible-lint

lint: yamllint defaults tasks meta vars

verifier:
  name: testinfra
  options:
    hosts: "pdns"
    vvv: true
  directory: ../resources/tests/all
  additional_files_or_dirs:
    # path relative to 'directory'
    - ../repo-48/
    - ../backend-sqlite/
    - ../backend-lmdb/
    - ../backend-mysql/
    - ../backend-mariadb/
    - ../backend-postgresql/
    - ../backend-bind/
    - ../backend-zones/
    - ../service-mask/
    - ../systemd-override/


================================================
FILE: molecule/pdns-49/converge.yml
================================================
---

- name: PowerDNS 4.9.x LMDB default instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-repo-49.yml
    - ../resources/vars/pdns-backend-lmdb.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_lmdb | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }
  post_tasks:
    - name: Check if the LMDB instance zone exists
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} list-zone {{ pdns_instance.zone }}"
      register: _pdns_backend_zone_check
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: false
      failed_when: false

    - name: Provision the LMDB instance zone
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} create-zone {{ pdns_instance.zone }} ns1.{{ pdns_instance.zone }}"
      register: _pdns_backend_zone_created
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: _pdns_backend_zone_check.rc != 0
      notify: Restart the LMDB instance service
      when: _pdns_backend_zone_check.rc != 0
  handlers:
    - name: Restart the LMDB instance service
      ansible.builtin.systemd:
        name: "{{ pdns_instance.service_name }}"
        state: restarted

- name: PowerDNS 4.9.x SQLite instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-repo-49.yml
    - ../resources/vars/pdns-backend-sqlite3.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_sqlite3 | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }
  post_tasks:
    - name: Check if the SQLite instance zone exists
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} list-zone {{ pdns_instance.zone }}"
      register: _pdns_backend_zone_check
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: false
      failed_when: false

    - name: Provision the SQLite instance zone
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} create-zone {{ pdns_instance.zone }} ns1.{{ pdns_instance.zone }}"
      register: _pdns_backend_zone_created
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: _pdns_backend_zone_check.rc != 0
      notify: Restart the SQLite instance service
      when: _pdns_backend_zone_check.rc != 0
  handlers:
    - name: Restart the SQLite instance service
      ansible.builtin.systemd:
        name: "{{ pdns_instance.service_name }}"
        state: restarted

- name: PowerDNS 4.9.x MySQL instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-repo-49.yml
    - ../resources/vars/pdns-backend-mysql.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_mysql | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }
  post_tasks:
    - name: Check if the MySQL instance zone exists
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} list-zone {{ pdns_instance.zone }}"
      register: _pdns_backend_zone_check
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: false
      failed_when: false

    - name: Provision the MySQL instance zone
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} create-zone {{ pdns_instance.zone }} ns1.{{ pdns_instance.zone }}"
      register: _pdns_backend_zone_created
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: _pdns_backend_zone_check.rc != 0
      notify: Restart the MySQL instance service
      when: _pdns_backend_zone_check.rc != 0
  handlers:
    - name: Restart the MySQL instance service
      ansible.builtin.systemd:
        name: "{{ pdns_instance.service_name }}"
        state: restarted

- name: PowerDNS 4.9.x MariaDB instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-repo-49.yml
    - ../resources/vars/pdns-backend-mariadb.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_mariadb | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }
  post_tasks:
    - name: Check if the MariaDB instance zone exists
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} list-zone {{ pdns_instance.zone }}"
      register: _pdns_backend_zone_check
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: false
      failed_when: false

    - name: Provision the MariaDB instance zone
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} create-zone {{ pdns_instance.zone }} ns1.{{ pdns_instance.zone }}"
      register: _pdns_backend_zone_created
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: _pdns_backend_zone_check.rc != 0
      notify: Restart the MariaDB instance service
      when: _pdns_backend_zone_check.rc != 0
  handlers:
    - name: Restart the MariaDB instance service
      ansible.builtin.systemd:
        name: "{{ pdns_instance.service_name }}"
        state: restarted

- name: PowerDNS 4.9.x Bind instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-repo-49.yml
    - ../resources/vars/pdns-backend-bind.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_bind | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }

- name: PowerDNS 4.9.x PostgreSQL instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-repo-49.yml
    - ../resources/vars/pdns-backend-postgresql.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_pgsql | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }
  post_tasks:
    - name: Check if the PostgreSQL instance zone exists
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} list-zone {{ pdns_instance.zone }}"
      register: _pdns_backend_zone_check
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: false
      failed_when: false

    - name: Provision the PostgreSQL instance zone
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} create-zone {{ pdns_instance.zone }} ns1.{{ pdns_instance.zone }}"
      register: _pdns_backend_zone_created
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: _pdns_backend_zone_check.rc != 0
      notify: Restart the PostgreSQL instance service
      when: _pdns_backend_zone_check.rc != 0
  handlers:
    - name: Restart the PostgreSQL instance service
      ansible.builtin.systemd:
        name: "{{ pdns_instance.service_name }}"
        state: restarted

- name: PowerDNS 4.9.x Hide default service
  hosts: pdns
  vars:
    pdns_service_state: "stopped"
    pdns_service_enabled: "no"
    pdns_service_masked: true
  roles:
    - { role: powerdns.pdns }


================================================
FILE: molecule/pdns-49/molecule.yml
================================================
---

scenario:
  name: pdns-49

driver:
  name: docker

dependency:
  name: galaxy

platforms:

  - name: rockylinux-8
    groups: ["pdns"]
    image: rockylinux:8
    dockerfile_tpl: el-systemd

  - name: rockylinux-9
    groups: ["pdns"]
    image: rockylinux:9
    dockerfile_tpl: el-systemd

  - name: almalinux-8
    groups: ["pdns"]
    image: almalinux:8
    dockerfile_tpl: el-systemd

  - name: almalinux-9
    groups: ["pdns"]
    image: almalinux:9
    dockerfile_tpl: el-systemd

  - name: oraclelinux-8
    groups: ["pdns"]
    image: oraclelinux:8
    dockerfile_tpl: el-systemd

  - name: oraclelinux-9
    groups: ["pdns"]
    image: oraclelinux:9
    dockerfile_tpl: el-systemd

  - name: ubuntu-2204
    groups: ["pdns"]
    image: ubuntu:22.04
    dockerfile_tpl: debian-systemd

  - name: ubuntu-2404
    groups: ["pdns"]
    image: ubuntu:24.04
    dockerfile_tpl: debian-systemd

  - name: debian-11
    groups: ["pdns"]
    image: debian:11
    dockerfile_tpl: debian-systemd

  - name: debian-12
    groups: ["pdns"]
    image: debian:12
    dockerfile_tpl: debian-systemd

  - name: debian-13
    groups: ["pdns"]
    image: debian:13
    dockerfile_tpl: debian-systemd

  # In order to run the tests we need
  # a MySQL container to be up & running
  - name: mysql
    image: mysql:8.4.8
    env:
      MYSQL_ROOT_PASSWORD: pdns
      MYSQL_ROOT_HOST: '%'
    # Declaring the container as service,
    # will link it to the others Platforms containers
    # on creation.
    is_service: true

  # Additional service for gmysql tests against MariaDB 10.6
  - name: mariadb
    image: mariadb:10.6
    env:
      MARIADB_ROOT_PASSWORD: pdns
      MARIADB_ROOT_HOST: '%'
    is_service: true

  # PostgreSQL service for gpgsql backend tests
  - name: postgresql
    image: postgres:16
    env:
      POSTGRES_PASSWORD: pdns
    is_service: true

provisioner:
  name: ansible
  options:
    diff: true
    v: true
  config_options:
    defaults:
      gathering: smart
      fact_caching: jsonfile
      fact_caching_connection: .ansible_cache
      fact_caching_timeout: 7200
    ssh_connection:
      pipelining: true
  playbooks:
    # cleanup: ../resources/cleanup.yml
    create: ../resources/create.yml
    destroy: ../resources/destroy.yml
    prepare: ../resources/prepare.yml
  lint: ansible-lint

lint: yamllint defaults tasks meta vars

verifier:
  name: testinfra
  options:
    hosts: "pdns"
    vvv: true
  directory: ../resources/tests/all
  additional_files_or_dirs:
    # path relative to 'directory'
    - ../repo-49/
    - ../backend-sqlite/
    - ../backend-lmdb/
    - ../backend-mysql/
    - ../backend-mariadb/
    - ../backend-postgresql/
    - ../backend-bind/
    - ../backend-zones/
    - ../service-mask/
    - ../systemd-override/


================================================
FILE: molecule/pdns-50/converge.yml
================================================
---

- name: PowerDNS 5.0.x LMDB default instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-repo-50.yml
    - ../resources/vars/pdns-backend-lmdb.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_lmdb | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }
  post_tasks:
    - name: Check if the LMDB instance zone exists
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} list-zone {{ pdns_instance.zone }}"
      register: _pdns_backend_zone_check
      become: true
      become_user: pdns
      changed_when: false
      failed_when: false

    - name: Provision the LMDB instance zone
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} create-zone {{ pdns_instance.zone }} ns1.{{ pdns_instance.zone }}"
      register: _pdns_backend_zone_created
      become: true
      become_user: pdns
      changed_when: _pdns_backend_zone_check.rc != 0
      notify: Restart the LMDB instance service
      when: _pdns_backend_zone_check.rc != 0
  handlers:
    - name: Restart the LMDB instance service
      ansible.builtin.systemd:
        name: "{{ pdns_instance.service_name }}"
        state: restarted

- name: PowerDNS 5.0.x SQLite instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-repo-50.yml
    - ../resources/vars/pdns-backend-sqlite3.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_sqlite3 | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }
  post_tasks:
    - name: Check if the SQLite instance zone exists
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} list-zone {{ pdns_instance.zone }}"
      register: _pdns_backend_zone_check
      become: true
      become_user: pdns
      changed_when: false
      failed_when: false

    - name: Provision the SQLite instance zone
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} create-zone {{ pdns_instance.zone }} ns1.{{ pdns_instance.zone }}"
      register: _pdns_backend_zone_created
      become: true
      become_user: pdns
      changed_when: _pdns_backend_zone_check.rc != 0
      notify: Restart the SQLite instance service
      when: _pdns_backend_zone_check.rc != 0
  handlers:
    - name: Restart the SQLite instance service
      ansible.builtin.systemd:
        name: "{{ pdns_instance.service_name }}"
        state: restarted

- name: PowerDNS 5.0.x MySQL instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-repo-50.yml
    - ../resources/vars/pdns-backend-mysql.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_mysql | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }
  post_tasks:
    - name: Check if the MySQL instance zone exists
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} list-zone {{ pdns_instance.zone }}"
      register: _pdns_backend_zone_check
      become: true
      become_user: pdns
      changed_when: false
      failed_when: false

    - name: Provision the MySQL instance zone
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} create-zone {{ pdns_instance.zone }} ns1.{{ pdns_instance.zone }}"
      register: _pdns_backend_zone_created
      become: true
      become_user: pdns
      changed_when: _pdns_backend_zone_check.rc != 0
      notify: Restart the MySQL instance service
      when: _pdns_backend_zone_check.rc != 0
  handlers:
    - name: Restart the MySQL instance service
      ansible.builtin.systemd:
        name: "{{ pdns_instance.service_name }}"
        state: restarted

- name: PowerDNS 5.0.x MariaDB instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-repo-50.yml
    - ../resources/vars/pdns-backend-mariadb.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_mariadb | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }
  post_tasks:
    - name: Check if the MariaDB instance zone exists
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} list-zone {{ pdns_instance.zone }}"
      register: _pdns_backend_zone_check
      become: true
      become_user: pdns
      changed_when: false
      failed_when: false

    - name: Provision the MariaDB instance zone
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} create-zone {{ pdns_instance.zone }} ns1.{{ pdns_instance.zone }}"
      register: _pdns_backend_zone_created
      become: true
      become_user: pdns
      changed_when: _pdns_backend_zone_check.rc != 0
      notify: Restart the MariaDB instance service
      when: _pdns_backend_zone_check.rc != 0
  handlers:
    - name: Restart the MariaDB instance service
      ansible.builtin.systemd:
        name: "{{ pdns_instance.service_name }}"
        state: restarted

- name: PowerDNS 5.0.x Bind instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-repo-50.yml
    - ../resources/vars/pdns-backend-bind.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_bind | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }

- name: PowerDNS 5.0.x PostgreSQL instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-repo-50.yml
    - ../resources/vars/pdns-backend-postgresql.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_pgsql | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }
  post_tasks:
    - name: Check if the PostgreSQL instance zone exists
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} list-zone {{ pdns_instance.zone }}"
      register: _pdns_backend_zone_check
      become: true
      become_user: pdns
      changed_when: false
      failed_when: false

    - name: Provision the PostgreSQL instance zone
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} create-zone {{ pdns_instance.zone }} ns1.{{ pdns_instance.zone }}"
      register: _pdns_backend_zone_created
      become: true
      become_user: pdns
      changed_when: _pdns_backend_zone_check.rc != 0
      notify: Restart the PostgreSQL instance service
      when: _pdns_backend_zone_check.rc != 0
  handlers:
    - name: Restart the PostgreSQL instance service
      ansible.builtin.systemd:
        name: "{{ pdns_instance.service_name }}"
        state: restarted

- name: PowerDNS 5.0.x Hide default service
  hosts: pdns
  vars:
    pdns_service_state: "stopped"
    pdns_service_enabled: "no"
    pdns_service_masked: true
  roles:
    - { role: powerdns.pdns }


================================================
FILE: molecule/pdns-50/molecule.yml
================================================
---

scenario:
  name: pdns-50

driver:
  name: docker

dependency:
  name: galaxy

platforms:

  - name: rockylinux-8
    groups: ["pdns"]
    image: rockylinux:8
    dockerfile_tpl: el-systemd

  - name: rockylinux-9
    groups: ["pdns"]
    image: rockylinux:9
    dockerfile_tpl: el-systemd

  - name: almalinux-8
    groups: ["pdns"]
    image: almalinux:8
    dockerfile_tpl: el-systemd

  - name: almalinux-9
    groups: ["pdns"]
    image: almalinux:9
    dockerfile_tpl: el-systemd

  - name: almalinux-10
    groups: ["pdns"]
    image: almalinux:10
    dockerfile_tpl: el-systemd

  - name: oraclelinux-8
    groups: ["pdns"]
    image: oraclelinux:8
    dockerfile_tpl: el-systemd

  - name: oraclelinux-9
    groups: ["pdns"]
    image: oraclelinux:9
    dockerfile_tpl: el-systemd

  - name: oraclelinux-10
    groups: ["pdns"]
    image: oraclelinux:10
    dockerfile_tpl: el-systemd

  - name: ubuntu-2204
    groups: ["pdns"]
    image: ubuntu:22.04
    dockerfile_tpl: debian-systemd

  - name: ubuntu-2404
    groups: ["pdns"]
    image: ubuntu:24.04
    dockerfile_tpl: debian-systemd

  - name: debian-11
    groups: ["pdns"]
    image: debian:11
    dockerfile_tpl: debian-systemd

  - name: debian-12
    groups: ["pdns"]
    image: debian:12
    dockerfile_tpl: debian-systemd

  - name: debian-13
    groups: ["pdns"]
    image: debian:13
    dockerfile_tpl: debian-systemd

  # In order to run the tests we need
  # a MySQL container to be up & running
  - name: mysql
    image: mysql:8.4.8
    env:
      MYSQL_ROOT_PASSWORD: pdns
      MYSQL_ROOT_HOST: '%'
    # Declaring the container as service,
    # will link it to the others containers on creation
    is_service: true

  # Additional service for gmysql tests against MariaDB 10.6
  - name: mariadb
    image: mariadb:10.6
    env:
      MARIADB_ROOT_PASSWORD: pdns
      MARIADB_ROOT_HOST: '%'
    is_service: true

  # PostgreSQL service for gpgsql backend tests
  - name: postgresql
    image: postgres:16
    env:
      POSTGRES_PASSWORD: pdns
    is_service: true

provisioner:
  name: ansible
  options:
    diff: true
    v: true
  config_options:
    defaults:
      gathering: smart
      fact_caching: jsonfile
      fact_caching_connection: .ansible_cache
      fact_caching_timeout: 7200
    ssh_connection:
      pipelining: true
  playbooks:
    # cleanup: ../resources/cleanup.yml
    create: ../resources/create.yml
    destroy: ../resources/destroy.yml
    prepare: ../resources/prepare.yml
  lint: ansible-lint

lint: yamllint defaults handlers tasks meta vars

verifier:
  name: testinfra
  options:
    hosts: "pdns"
    v: true
  directory: ../resources/tests/all
  additional_files_or_dirs:
    # path relative to 'directory'
    - ../repo-50/
    - ../backend-sqlite/
    - ../backend-lmdb/
    - ../backend-mysql/
    - ../backend-mariadb/
    - ../backend-postgresql/
    - ../backend-bind/
    - ../backend-zones/
    - ../service-mask/
    - ../systemd-override/


================================================
FILE: molecule/pdns-master/converge.yml
================================================
---

- name: PowerDNS Master LMDB default instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-repo-master.yml
    - ../resources/vars/pdns-backend-lmdb.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_lmdb | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }
  post_tasks:
    - name: Check if the LMDB instance zone exists
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} list-zone {{ pdns_instance.zone }}"
      register: _pdns_backend_zone_check
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: false
      failed_when: false

    - name: Provision the LMDB instance zone
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} create-zone {{ pdns_instance.zone }} ns1.{{ pdns_instance.zone }}"
      register: _pdns_backend_zone_created
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: _pdns_backend_zone_check.rc != 0
      notify: Restart the LMDB instance service
      when: _pdns_backend_zone_check.rc != 0
  handlers:
    - name: Restart the LMDB instance service
      ansible.builtin.systemd:
        name: "{{ pdns_instance.service_name }}"
        state: restarted

- name: PowerDNS Master SQLite instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-repo-master.yml
    - ../resources/vars/pdns-backend-sqlite3.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_sqlite3 | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }
  post_tasks:
    - name: Check if the SQLite instance zone exists
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} list-zone {{ pdns_instance.zone }}"
      register: _pdns_backend_zone_check
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: false
      failed_when: false

    - name: Provision the SQLite instance zone
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} create-zone {{ pdns_instance.zone }} ns1.{{ pdns_instance.zone }}"
      register: _pdns_backend_zone_created
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: _pdns_backend_zone_check.rc != 0
      notify: Restart the SQLite instance service
      when: _pdns_backend_zone_check.rc != 0
  handlers:
    - name: Restart the SQLite instance service
      ansible.builtin.systemd:
        name: "{{ pdns_instance.service_name }}"
        state: restarted

- name: PowerDNS Master MySQL instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-repo-master.yml
    - ../resources/vars/pdns-backend-mysql.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_mysql | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }
  post_tasks:
    - name: Check if the MySQL instance zone exists
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} list-zone {{ pdns_instance.zone }}"
      register: _pdns_backend_zone_check
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: false
      failed_when: false

    - name: Provision the MySQL instance zone
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} create-zone {{ pdns_instance.zone }} ns1.{{ pdns_instance.zone }}"
      register: _pdns_backend_zone_created
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: _pdns_backend_zone_check.rc != 0
      notify: Restart the MySQL instance service
      when: _pdns_backend_zone_check.rc != 0
  handlers:
    - name: Restart the MySQL instance service
      ansible.builtin.systemd:
        name: "{{ pdns_instance.service_name }}"
        state: restarted

- name: PowerDNS Master MariaDB instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-repo-master.yml
    - ../resources/vars/pdns-backend-mariadb.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_mariadb | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }
  post_tasks:
    - name: Check if the MariaDB instance zone exists
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} list-zone {{ pdns_instance.zone }}"
      register: _pdns_backend_zone_check
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: false
      failed_when: false

    - name: Provision the MariaDB instance zone
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} create-zone {{ pdns_instance.zone }} ns1.{{ pdns_instance.zone }}"
      register: _pdns_backend_zone_created
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: _pdns_backend_zone_check.rc != 0
      notify: Restart the MariaDB instance service
      when: _pdns_backend_zone_check.rc != 0
  handlers:
    - name: Restart the MariaDB instance service
      ansible.builtin.systemd:
        name: "{{ pdns_instance.service_name }}"
        state: restarted

- name: PowerDNS Master Bind instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-repo-master.yml
    - ../resources/vars/pdns-backend-bind.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_bind | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }

- name: PowerDNS Master PostgreSQL instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-repo-master.yml
    - ../resources/vars/pdns-backend-postgresql.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_pgsql | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }
  post_tasks:
    - name: Check if the PostgreSQL instance zone exists
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} list-zone {{ pdns_instance.zone }}"
      register: _pdns_backend_zone_check
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: false
      failed_when: false

    - name: Provision the PostgreSQL instance zone
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} create-zone {{ pdns_instance.zone }} ns1.{{ pdns_instance.zone }}"
      register: _pdns_backend_zone_created
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: _pdns_backend_zone_check.rc != 0
      notify: Restart the PostgreSQL instance service
      when: _pdns_backend_zone_check.rc != 0
  handlers:
    - name: Restart the PostgreSQL instance service
      ansible.builtin.systemd:
        name: "{{ pdns_instance.service_name }}"
        state: restarted

- name: PowerDNS Master Hide default service
  hosts: pdns
  vars:
    pdns_service_state: "stopped"
    pdns_service_enabled: "no"
    pdns_service_masked: true
  roles:
    - { role: powerdns.pdns }


================================================
FILE: molecule/pdns-master/molecule.yml
================================================
---

scenario:
  name: pdns-master

driver:
  name: docker

dependency:
  name: galaxy

platforms:

  - name: rockylinux-8
    groups: ["pdns"]
    image: rockylinux:8
    dockerfile_tpl: el-systemd

  - name: rockylinux-9
    groups: ["pdns"]
    image: rockylinux:9
    dockerfile_tpl: el-systemd

  - name: almalinux-8
    groups: ["pdns"]
    image: almalinux:8
    dockerfile_tpl: el-systemd

  - name: almalinux-9
    groups: ["pdns"]
    image: almalinux:9
    dockerfile_tpl: el-systemd

  - name: oraclelinux-8
    groups: ["pdns"]
    image: oraclelinux:8
    dockerfile_tpl: el-systemd

  - name: oraclelinux-9
    groups: ["pdns"]
    image: oraclelinux:9
    dockerfile_tpl: el-systemd

  - name: ubuntu-2204
    groups: ["pdns"]
    image: ubuntu:22.04
    dockerfile_tpl: debian-systemd

  - name: ubuntu-2404
    groups: ["pdns"]
    image: ubuntu:24.04
    dockerfile_tpl: debian-systemd

  - name: debian-11
    groups: ["pdns"]
    image: debian:11
    dockerfile_tpl: debian-systemd

  - name: debian-12
    groups: ["pdns"]
    image: debian:12
    dockerfile_tpl: debian-systemd

  # In order to run the tests we need
  # a MySQL container to be up & running
  - name: mysql
    image: mysql:8.4.8
    env:
      MYSQL_ROOT_PASSWORD: pdns
      MYSQL_ROOT_HOST: '%'
    # Declaring the container as service,
    # will link it to the others Platforms containers
    # on creation.
    is_service: true

  # Additional service for gmysql tests against MariaDB 10.6
  - name: mariadb
    image: mariadb:10.6
    env:
      MARIADB_ROOT_PASSWORD: pdns
      MARIADB_ROOT_HOST: '%'
    is_service: true

  # PostgreSQL service for gpgsql backend tests
  - name: postgresql
    image: postgres:16
    env:
      POSTGRES_PASSWORD: pdns
    is_service: true

provisioner:
  name: ansible
  options:
    diff: true
    v: true
  config_options:
    defaults:
      gathering: smart
      fact_caching: jsonfile
      fact_caching_connection: .ansible_cache
      fact_caching_timeout: 7200
    ssh_connection:
      pipelining: true
  playbooks:
    # cleanup: ../resources/cleanup.yml
    create: ../resources/create.yml
    destroy: ../resources/destroy.yml
    prepare: ../resources/prepare.yml
  lint: ansible-lint

lint: yamllint defaults tasks meta vars

verifier:
  name: testinfra
  options:
    hosts: "pdns"
    vvv: true
  directory: ../resources/tests/all
  additional_files_or_dirs:
    # path relative to 'directory'
    - ../repo-master/
    - ../backend-sqlite/
    - ../backend-lmdb/
    - ../backend-mysql/
    - ../backend-mariadb/
    - ../backend-postgresql/
    - ../backend-bind/
    - ../backend-zones/
    - ../service-mask/
    - ../systemd-override/


================================================
FILE: molecule/pdns-os-repos/converge.yml
================================================
---

- name: PowerDNS LMDB default instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-backend-lmdb.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_lmdb | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }
  post_tasks:
    - name: Check if the LMDB instance zone exists
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} list-zone {{ pdns_instance.zone }}"
      register: _pdns_backend_zone_check
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: false
      failed_when: false

    - name: Provision the LMDB instance zone
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} create-zone {{ pdns_instance.zone }} ns1.{{ pdns_instance.zone }}"
      register: _pdns_backend_zone_created
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: _pdns_backend_zone_check.rc != 0
      notify: Restart the LMDB instance service
      when: _pdns_backend_zone_check.rc != 0
  handlers:
    - name: Restart the LMDB instance service
      ansible.builtin.systemd:
        name: "{{ pdns_instance.service_name }}"
        state: restarted

- name: PowerDNS SQLite instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-backend-sqlite3.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_sqlite3 | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }
  post_tasks:
    - name: Check if the SQLite instance zone exists
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} list-zone {{ pdns_instance.zone }}"
      register: _pdns_backend_zone_check
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: false
      failed_when: false

    - name: Provision the SQLite instance zone
      ansible.builtin.command: "pdnsutil --config-name={{ pdns_instance.config_name }} create-zone {{ pdns_instance.zone }} ns1.{{ pdns_instance.zone }}"
      register: _pdns_backend_zone_created
      become: true
      become_user: "{{ pdns_user }}"
      changed_when: _pdns_backend_zone_check.rc != 0
      notify: Restart the SQLite instance service
      when: _pdns_backend_zone_check.rc != 0
  handlers:
    - name: Restart the SQLite instance service
      ansible.builtin.systemd:
        name: "{{ pdns_instance.service_name }}"
        state: restarted

- name: PowerDNS MySQL instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-backend-mysql.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_mysql | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }
  post_tasks:
    - name: Check if the MySQL instance zone exists
      ansible.builtin.command: "sudo -u powerdns -- pdnsutil --config-name={{ pdns_instance.config_name }} list-zone {{ pdns_instance.zone }}"
      register: _pdns_backend_zone_check
      become: true
      changed_when: false
      failed_when: false

    - name: Provision the MySQL instance zone
      ansible.builtin.command: "sudo -u powerdns -- pdnsutil --config-name={{ pdns_instance.config_name }} create-zone {{ pdns_instance.zone }} ns1.{{ pdns_instance.zone }}"
      register: _pdns_backend_zone_created
      become: true
      changed_when: _pdns_backend_zone_check.rc != 0
      notify: Restart the MySQL instance service
      when: _pdns_backend_zone_check.rc != 0
  handlers:
    - name: Restart the MySQL instance service
      ansible.builtin.systemd:
        name: "{{ pdns_instance.service_name }}"
        state: restarted

- name: PowerDNS MariaDB instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-backend-mariadb.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_mariadb | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }
  post_tasks:
    - name: Check if the MariaDB instance zone exists
      ansible.builtin.command: "sudo -u powerdns -- pdnsutil --config-name={{ pdns_instance.config_name }} list-zone {{ pdns_instance.zone }}"
      register: _pdns_backend_zone_check
      become: true
      changed_when: false
      failed_when: false

    - name: Provision the MariaDB instance zone
      ansible.builtin.command: "sudo -u powerdns -- pdnsutil --config-name={{ pdns_instance.config_name }} create-zone {{ pdns_instance.zone }} ns1.{{ pdns_instance.zone }}"
      register: _pdns_backend_zone_created
      become: true
      changed_when: _pdns_backend_zone_check.rc != 0
      notify: Restart the MariaDB instance service
      when: _pdns_backend_zone_check.rc != 0
  handlers:
    - name: Restart the MariaDB instance service
      ansible.builtin.systemd:
        name: "{{ pdns_instance.service_name }}"
        state: restarted

- name: PowerDNS Bind instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-backend-bind.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_bind | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }

- name: PowerDNS PostgreSQL instance
  hosts: pdns
  vars_files:
    - ../resources/vars/pdns-common.yml
    - ../resources/vars/pdns-backend-postgresql.yml
  vars:
    pdns_service_name: "{{ pdns_instance.service_name }}"
    pdns_service_enabled: "{{ pdns_instance.service_enabled }}"
    pdns_config_file: "{{ pdns_instance.config_file }}"
    pdns_config: >-
      {{ pdns_config_common
         | combine(pdns_instance.config_overrides | default({}), recursive=true)
         | combine(pdns_config_overrides_pgsql | default({}), recursive=true) }}
  roles:
    - { role: powerdns.pdns }
  post_tasks:
    - name: Check if the PostgreSQL instance zone exists
      ansible.builtin.command: "sudo -u powerdns -- pdnsutil --config-name={{ pdns_instance.config_name }} list-zone {{ pdns_instance.zone }}"
      register: _pdns_backend_zone_check
      become: true
      changed_when: false
      failed_when: false

    - name: Provision the PostgreSQL instance zone
      ansible.builtin.command: "sudo -u powerdns -- pdnsutil --config-name={{ pdns_instance.config_name }} create-zone {{ pdns_instance.zone }} ns1.{{ pdns_instance.zone }}"
      register: _pdns_backend_zone_created
      become: true
      changed_when: _pdns_backend_zone_check.rc != 0
      notify: Restart the PostgreSQL instance service
      when: _pdns_backend_zone_check.rc != 0
  handlers:
    - name: Restart the PostgreSQL instance service
      ansible.builtin.systemd:
        name: "{{ pdns_instance.service_name }}"
        state: restarted

- name: PowerDNS Hide default service
  hosts: pdns
  vars:
    pdns_service_state: "stopped"
    pdns_service_enabled: "no"
    pdns_service_masked: true
  roles:
    - { role: powerdns.pdns }


================================================
FILE: molecule/pdns-os-repos/molecule.yml
================================================
---

scenario:
  name: pdns-os-repos

driver:
  name: docker

dependency:
  name: galaxy

platforms:

  - name: archlinux
    groups: ["pdns"]
    image: archlinux:base
    dockerfile_tpl: archlinux-systemd

  # In order to run the tests we need
  # a MySQL container to be up & running
  - name: mysql
    image: mysql:8.4.8
    env:
      MYSQL_ROOT_PASSWORD: pdns
      MYSQL_ROOT_HOST: '%'
    # Declaring the container as service,
    # will link it to the others containers on creation
    is_service: true

  # Additional service for gmysql tests against MariaDB 10.6
  - name: mariadb
    image: mariadb:10.6
    env:
      MARIADB_ROOT_PASSWORD: pdns
      MARIADB_ROOT_HOST: '%'
    is_service: true

  # PostgreSQL service for gpgsql backend tests
  - name: postgresql
    image: postgres:16
    env:
      POSTGRES_PASSWORD: pdns
    is_service: true

provisioner:
  name: ansible
  options:
    diff: true
    v: true
  config_options:
    defaults:
      gathering: smart
      fact_caching: jsonfile
      fact_caching_connection: .ansible_cache
      fact_caching_timeout: 7200
    ssh_connection:
      pipelining: true
  playbooks:
    # cleanup: ../resources/cleanup.yml
    create: ../resources/create.yml
    destroy: ../resources/destroy.yml
    prepare: ../resources/prepare.yml
  lint: ansible-lint

lint: yamllint defaults tasks meta vars

verifier:
  name: testinfra
  options:
    hosts: "pdns"
    vvv: true
  directory: ../resources/tests/all
  additional_files_or_dirs:
    # path relative to 'directory'
    - ../backend-sqlite/
    - ../backend-lmdb/
    - ../backend-mysql/
    - ../backend-mariadb/
    - ../backend-postgresql/
    - ../backend-bind/
    - ../backend-zones/
    - ../service-mask/
    - ../systemd-override/


================================================
FILE: molecule/resources/Dockerfile.archlinux-systemd.j2
================================================
# Molecule managed

{% set archlinux_base_image = 'menci/archlinuxarm:latest' if (item.name | lower == 'archlinux' and (molecule_docker_arch | default('')) == 'arm64') else item.image %}
FROM {{ archlinux_base_image }}

RUN pacman -Syu --noconfirm && \
    pacman -S --noconfirm systemd awk bash ca-certificates geoip grep inetutils \
        iproute2 libmaxminddb net-tools procps-ng python python-cryptography \
        python-pip sudo vim yaml-cpp && \
    rm -rf /usr/share/doc/* && \
    rm -rf /usr/share/man/* && \
    pacman -Scc --noconfirm

RUN cd /usr/lib/systemd/system/sysinit.target.wants/; \
    for i in *; do [ $i = systemd-tmpfiles-setup.service ] || rm -f $i; done

RUN rm -f /usr/lib/systemd/system/multi-user.target.wants/* \
    /etc/systemd/system/*.wants/* \
    /usr/lib/systemd/system/local-fs.target.wants/* \
    /usr/lib/systemd/system/sockets.target.wants/*udev* \
    /usr/lib/systemd/system/sockets.target.wants/*initctl* \
    /usr/lib/systemd/system/basic.target.wants/* \
    /usr/lib/systemd/system/anaconda.target.wants/*

# Disable requiretty.
RUN sed -i -e 's/^\(Defaults\s*requiretty\)/#--- \1/'  /etc/sudoers || echo "Defaults !requiretty" > /etc/sudoers.d/molecule

# Create `ansible` user with sudo permissions
ENV ANSIBLE_USER=ansible SUDO_GROUP=wheel
RUN set -xe && \
    groupadd -r ${ANSIBLE_USER} && \
    useradd -m -g ${ANSIBLE_USER} ${ANSIBLE_USER} && \
    usermod -aG ${SUDO_GROUP} ${ANSIBLE_USER} && \
    sed -i "/^# %${SUDO_GROUP}/s/^# //g" /etc/sudoers && \
    sed -i "/^%${SUDO_GROUP}/s/ALL$/NOPASSWD:ALL/g" /etc/sudoers

VOLUME [ "/sys/fs/cgroup", "/tmp", "/run" ]

CMD [ "/usr/lib/systemd/systemd" ]


================================================
FILE: molecule/resources/Dockerfile.debian-systemd.j2
================================================
# Molecule managed

FROM {{ item.image }}

ENV container docker
ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update -y && \
    apt-get install -y --no-install-recommends systemd python3 python3-pip python3-apt sudo adduser bash net-tools iproute2 procps && \
    rm -Rf /usr/share/doc && \
    rm -Rf /usr/share/man && \
    apt-get clean

RUN rm -f /lib/systemd/system/multi-user.target.wants/* \
    /etc/systemd/system/*.wants/* \
    /lib/systemd/system/local-fs.target.wants/* \
    /lib/systemd/system/sockets.target.wants/*udev* \
    /lib/systemd/system/sockets.target.wants/*initctl* \
    /lib/systemd/system/sysinit.target.wants/systemd-tmpfiles-setup* \
    /lib/systemd/system/systemd-update-utmp*

# Disable requiretty.
RUN sed -i -e 's/^\(Defaults\s*requiretty\)/#--- \1/'  /etc/sudoers

# Create `ansible` user with sudo permissions
ENV ANSIBLE_USER=ansible SUDO_GROUP=sudo
RUN set -xe && \
    groupadd -r ${ANSIBLE_USER} && \
    useradd -m -g ${ANSIBLE_USER} ${ANSIBLE_USER} && \
    usermod -aG ${SUDO_GROUP} ${ANSIBLE_USER} && \
    sed -i "/^%${SUDO_GROUP}/s/ALL$/NOPASSWD:ALL/g" /etc/sudoers

VOLUME [ "/sys/fs/cgroup", "/tmp", "/run" ]

CMD [ "/lib/systemd/systemd" ]


================================================
FILE: molecule/resources/Dockerfile.el-systemd.j2
================================================
# Molecule managed

FROM {{ item.image }}

ENV container docker

RUN dnf makecache && \
    dnf install -y systemd python3 python3-pip sudo bash vim iproute procps-ng hostname && \
    rm -Rf /usr/share/doc && \
    rm -Rf /usr/share/man && \
    dnf clean all

RUN cd /lib/systemd/system/sysinit.target.wants/; \
    for i in *; do [ $i = systemd-tmpfiles-setup.service ] || rm -f $i; done

RUN rm -f /lib/systemd/system/multi-user.target.wants/* \
    /etc/systemd/system/*.wants/* \
    /lib/systemd/system/local-fs.target.wants/* \
    /lib/systemd/system/sockets.target.wants/*udev* \
    /lib/systemd/system/sockets.target.wants/*initctl* \
    /lib/systemd/system/basic.target.wants/* \
    /lib/systemd/system/anaconda.target.wants/*

# Disable requiretty.
RUN sed -i -e 's/^\(Defaults\s*requiretty\)/#--- \1/'  /etc/sudoers

# Create `ansible` user with sudo permissions
ENV ANSIBLE_USER=ansible SUDO_GROUP=wheel
RUN set -xe && \
    groupadd -r ${ANSIBLE_USER} && \
    useradd -m -g ${ANSIBLE_USER} ${ANSIBLE_USER} && \
    usermod -aG ${SUDO_GROUP} ${ANSIBLE_USER} && \
    sed -i "/^%${SUDO_GROUP}/s/ALL\$/NOPASSWD:ALL/g" /etc/sudoers

VOLUME [ "/sys/fs/cgroup", "/tmp", "/run" ]

CMD [ "/usr/sbin/init" ]


================================================
FILE: molecule/resources/cleanup.yml
================================================
---

- name: Cleanup role-managed PowerDNS packages
  hosts: pdns
  vars:
    pdns_package_state: absent
    pdns_debug_symbols_package_state: absent
    pdns_backends_packages_state: absent
    pdns_mysql_packages_state: absent
    pdns_pgsql_packages_state: absent
    pdns_sqlite_package_state: absent
    pdns_backends:
      bind: {}
      gmysql: {}
      gpgsql: {}
      gsqlite3: {}
      lmdb: {}
  roles:
    - role: powerdns.pdns


================================================
FILE: molecule/resources/create.yml
================================================
---

- name: Create
  hosts: localhost
  connection: local
  gather_facts: false
  vars_files:
    - vars/molecule.yml
  tasks:

    - name: Detect host architecture for Docker platform selection
      ansible.builtin.set_fact:
        _molecule_host_arch: "{{ lookup('pipe', 'uname -m') | trim | lower }}"
      changed_when: false

    - name: Define Docker architecture/platform mappings
      ansible.builtin.set_fact:
        _molecule_arch_map:
          x86_64: amd64
          amd64: amd64
          aarch64: arm64
          arm64: arm64
          armv8l: arm64
      changed_when: false

    - name: Set Docker target architecture and platform
      ansible.builtin.set_fact:
        molecule_docker_arch: "{{ _molecule_arch_map.get(_molecule_host_arch, _molecule_host_arch) }}"
        molecule_docker_platform: "linux/{{ _molecule_arch_map.get(_molecule_host_arch, _molecule_host_arch) }}"
      changed_when: false

    - name: Get list of service instances
      ansible.builtin.set_fact:
        molecule_service_instances: "{{ molecule_yml.platforms | selectattr('is_service', 'defined') | selectattr('is_service') | list }}"

    - name: Get list of platform instances
      ansible.builtin.set_fact:
        molecule_platform_instances: "{{ molecule_yml.platforms | difference(molecule_service_instances) }}"

    - name: Create Dockerfiles from platform names
      ansible.builtin.template:
        src: "Dockerfile.{{ item.dockerfile_tpl | default('default') }}.j2"
        dest: "{{ molecule_ephemeral_directory }}/Dockerfile_{{ item.name | regex_replace('[^a-zA-Z0-9_]', '_') }}"
        mode: '0644'
      with_items: "{{ molecule_platform_instances }}"
      register: platforms

    - name: Discover local Docker images
      community.docker.docker_image_info:
        name: "molecule_pdns/{{ item.item.name | lower | regex_replace('[^a-z0-9_.-]', '_') }}-{{ molecule_docker_arch }}"
      with_items: "{{ platforms.results }}"
      register: docker_images

    - name: Build an Ansible compatible image
      community.docker.docker_image:
        name: "molecule_pdns/{{ platforms.results[item].item.name | lower | regex_replace('[^a-z0-9_.-]', '_') }}-{{ molecule_docker_arch }}"
        source: build
        build:
          dockerfile: "{{ platforms.results[item].item.dockerfile | default(platforms.results[item].dest) }}"
          path: "{{ molecule_ephemeral_directory }}"
          platform: "{{ platforms.results[item].item.platform | default(molecule_docker_platform) }}"
          pull: true
        force_source: "{{ platforms.results[item].item.force | default(false) | bool }}"
        force_tag: "{{ platforms.results[item].item.force | default(false) | bool }}"
      loop: "{{ range(0, platforms.results | length) | list }}"
      loop_control:
        label: "{{ platforms.results[item].item.name }}"
      when: >-
        (platforms.results[item].get('changed', false) | bool) or
        ((docker_images.results[item].images | default([])) | length == 0) or
        (((docker_images.results[item].images | default([]) | first | default({})).Architecture | default('')) != molecule_docker_arch) or
        (platforms.results[item].item.force | default(false) | bool)

    - name: Create molecule instance(s)
      community.docker.docker_container:
        name: "{{ item.name }}"
        hostname: "{{ item.name }}"
        image: "{{ item.image }}"
        platform: "{{ item.platform | default(molecule_docker_platform) }}"
        state: started
        recreate: false
        env: "{{ item.env | default(omit) }}"
        privileged: "no"
        volumes: "{{ item.volumes | default(omit) }}"
      with_items: "{{ molecule_service_instances }}"

    - name: Create the required Services instance(s)
      community.docker.docker_container:
        name: "{{ item.name }}"
        hostname: "{{ item.name }}"
        image: "molecule_pdns/{{ item.name | lower | regex_replace('[^a-z0-9_.-]', '_') }}-{{ molecule_docker_arch }}"
        platform: "{{ item.platform | default(molecule_docker_platform) }}"
        links: "{{ molecule_service_instances | map(attribute='name') | list }}"
        command: "{{ item.command | default(omit) }}"
        state: started
        recreate: false
        privileged: "yes"
        volumes:
          # Mount the cgroups fs to allow SystemD to run into the containers
          - "/sys/fs/cgroup:/sys/fs/cgroup:rw"
        cgroupns_mode: host
      with_items: "{{ molecule_platform_instances }}"


================================================
FILE: molecule/resources/destroy.yml
================================================
---

- name: Destroy the Molecule Test Resources
  hosts: localhost
  connection: local
  gather_facts: false
  vars_files:
    - vars/molecule.yml
  tasks:
    - name: Destroy the target Platforms instance(s)
      community.docker.docker_container:
        name: "{{ item.name }}"
        state: absent
        force_kill: "{{ item.force_kill | default(true) }}"
      with_items: "{{ molecule_yml.platforms }}"


================================================
FILE: molecule/resources/prepare.yml
================================================
---

- name: Prepare the Molecule Test Resources
  hosts: pdns
  tasks:
    # Make sure the default MySQL and SQLite
    # schemas are installed in /usr/share/doc/
    - name: Disable the YUM 'nodocs' option
      ansible.builtin.lineinfile:
        line: tsflags=nodocs
        dest: /etc/yum.conf
        state: absent
      when: ansible_pkg_mgr == 'yum'

    - name: Disable the APT 'nodoc' option
      ansible.builtin.lineinfile:
        line: path-exclude=/usr/share/doc/*
        dest: /etc/dpkg/dpkg.cfg.d/excludes
        state: absent
      when: ansible_pkg_mgr == 'apt'

    - name: Disable the pacman 'NoExtract' option for docs
      ansible.builtin.replace:
        path: /etc/pacman.conf
        regexp: '^(\s*NoExtract\s*=\s*)usr/share/doc/\*(\s*)'
        replace: '\1\2'
      when: ansible_pkg_mgr == 'pacman'

    - name: Disable the pacman 'NoExtract' doc token in lists
      ansible.builtin.replace:
        path: /etc/pacman.conf
        regexp: '(\s+)usr/share/doc/\*'
        replace: '\1'
      when: ansible_pkg_mgr == 'pacman'

    # Install rsyslog to capture the PowerDNS Recursor log messages
    # when the service is not managed by systemd
    - name: Install rsyslog
      when: ansible_service_mgr != 'systemd'
      block:
        - name: Install rsyslog
          ansible.builtin.package:
            name: rsyslog
            state: present

        - name: Start rsyslog
          ansible.builtin.service:
            name: rsyslog
            state: started

    - name: Install dnspython and acl on Debian/Ubuntu
      ansible.builtin.apt:
        name:
          - python3-dnspython
          - acl
        state: present
        update_cache: true
      when: ansible_os_family == 'Debian'

    - name: Install dnspython on RHEL/Alma/Rocky/OL
      ansible.builtin.dnf:
        name: python3-dns
        state: present
      when: ansible_os_family == 'RedHat'

    - name: Install dnspython on Archlinux
      ansible.builtin.package:
        name: python-dnspython
        state: present
      when: ansible_pkg_mgr == 'pacman'


================================================
FILE: molecule/resources/tests/all/test_common.py
================================================

debian_os = ['debian', 'ubuntu']
rhel_os = ['redhat', 'centos', 'ol', 'rocky', 'almalinux']
archlinux_os = ['arch', 'archarm']


def test_distribution(host):
    assert host.system_info.distribution.lower() in debian_os + rhel_os + \
        archlinux_os


def test_package(host):
    distro = host.system_info.distribution.lower()
    if distro in debian_os:
        assert host.package('pdns-server').is_installed
        return
    if distro in rhel_os:
        assert host.package('pdns').is_installed
        return
    if distro in archlinux_os:
        # testinfra does not map "archarm" to ArchPackage, so query pacman directly
        if distro == 'archarm':
            assert host.run('pacman -Q powerdns').rc == 0
            return
        assert host.package('powerdns').is_installed


def test_service(host):
    # Using Ansible to mitigate some issues with the service test on debian-8
    unit = 'pdns'
    for config_dir in ('/etc/powerdns', '/etc/pdns'):
        if host.file(f'{config_dir}/pdns-lmdb.conf').exists:
            unit = 'pdns@lmdb'
            break

    s = host.ansible('service', f'name={unit} state=started enabled=yes')

    assert s["changed"] is False


================================================
FILE: molecule/resources/tests/backend-bind/test_backend_bind.py
================================================
debian_os = ['debian', 'ubuntu']
rhel_os = ['redhat', 'centos', 'ol', 'rocky', 'almalinux']
archlinux_os = ['arch', 'archarm']


def _pdns_config_dir(host):
    if host.system_info.distribution.lower() in debian_os + archlinux_os:
        return '/etc/powerdns'
    return '/etc/pdns'


def _bind_config_file(host):
    config_dir = _pdns_config_dir(host)
    bind_instance_conf = host.file(f'{config_dir}/pdns-bind.conf')
    if bind_instance_conf.exists:
        return bind_instance_conf
    return host.file(f'{config_dir}/pdns.conf')


def test_config(host):
    config_dir = _pdns_config_dir(host)
    with host.sudo():
        f = _bind_config_file(host)
        assert f.exists
        assert f.contains('launch+=bind')
        assert f.contains(f'bind-config={config_dir}/named.conf')


def test_bind_configuration_files(host):
    config_dir = _pdns_config_dir(host)
    with host.sudo():
        named_conf = host.file(f'{config_dir}/named.conf')
        zone_file = host.file(f'{config_dir}/bind.test.zone')

        assert named_conf.exists
        assert named_conf.contains('zone "bind.test" IN')
        assert named_conf.contains('file "bind.test.zone"')

        assert zone_file.exists
        assert zone_file.contains('SOA ns1.bind.test.')


def test_bind_instance_service_is_active(host):
    cmd = host.run('systemctl is-active pdns@bind')
    assert cmd.rc == 0
    assert cmd.stdout.strip() == 'active'


================================================
FILE: molecule/resources/tests/backend-lmdb/test_backend_lmdb.py
================================================
debian_os = ['debian', 'ubuntu']
rhel_os = ['redhat', 'centos', 'ol', 'rocky', 'almalinux']
archlinux_os = ['arch', 'archarm']


def _pdns_config_dir(host):
    if host.system_info.distribution.lower() in debian_os + archlinux_os:
        return '/etc/powerdns'
    return '/etc/pdns'


def _lmdb_config_file(host):
    config_dir = _pdns_config_dir(host)
    lmdb_instance_conf = host.file(f'{config_dir}/pdns-lmdb.conf')
    if lmdb_instance_conf.exists:
        return lmdb_instance_conf
    return host.file(f'{config_dir}/pdns.conf')


def test_package(host):
    if host.system_info.distribution.lower() in debian_os + rhel_os:
        p = host.package('pdns-backend-lmdb')
        assert p.is_installed


def test_config(host):
    with host.sudo():
        f = _lmdb_config_file(host)

        assert f.exists
        assert f.contains('launch+=lmdb')
        assert f.contains('lmdb-filename=/var/lib/powerdns/pdns.lmdb')


def test_lmdb_instance_service_is_active(host):
    config_dir = _pdns_config_dir(host)
    instance_conf = host.file(f'{config_dir}/pdns-lmdb.conf')
    unit = 'pdns@lmdb' if instance_conf.exists else 'pdns'
    cmd = host.run(f'systemctl is-active {unit}')
    assert cmd.rc == 0
    assert cmd.stdout.strip() == 'active'


================================================
FILE: molecule/resources/tests/backend-mariadb/test_backend_mariadb.py
================================================
debian_os = ['debian', 'ubuntu']
rhel_os = ['redhat', 'centos', 'ol', 'rocky', 'almalinux']
archlinux_os = ['arch', 'archarm']


def _pdns_config_dir(host):
    if host.system_info.distribution.lower() in debian_os + archlinux_os:
        return '/etc/powerdns'
    return '/etc/pdns'


def _mariadb_config_file(host):
    config_dir = _pdns_config_dir(host)
    mariadb_instance_conf = host.file(f'{config_dir}/pdns-mariadb.conf')
    if mariadb_instance_conf.exists:
        return mariadb_instance_conf
    return host.file(f'{config_dir}/pdns.conf')


def test_package(host):
    if host.system_info.distribution.lower() in debian_os + rhel_os:
        p = host.package('pdns-backend-mysql')
        assert p.is_installed


def test_config(host):
    with host.sudo():
        f = _mariadb_config_file(host)

        dbname = host.check_output('hostname -s').replace('.', '_')

        assert f.exists
        assert f.contains('launch+=gmysql:mariadb')
        assert f.contains('gmysql-mariadb-host=mariadb')
        assert f.contains('gmysql-mariadb-password=pdns')
        assert f.contains('gmysql-mariadb-dbname=' + dbname)
        assert f.contains('gmysql-mariadb-user=pdns')


================================================
FILE: molecule/resources/tests/backend-mysql/test_backend_mysql.py
================================================
debian_os = ['debian', 'ubuntu']
rhel_os = ['redhat', 'centos', 'ol', 'rocky', 'almalinux']
archlinux_os = ['arch', 'archarm']


def _pdns_config_dir(host):
    if host.system_info.distribution.lower() in debian_os + archlinux_os:
        return '/etc/powerdns'
    return '/etc/pdns'


def _mysql_config_file(host):
    config_dir = _pdns_config_dir(host)
    mysql_instance_conf = host.file(f'{config_dir}/pdns-mysql.conf')
    if mysql_instance_conf.exists:
        return mysql_instance_conf
    return host.file(f'{config_dir}/pdns.conf')


def test_package(host):
    if host.system_info.distribution.lower() in debian_os + rhel_os:
        p = host.package('pdns-backend-mysql')
        assert p.is_installed


def test_config(host):
    with host.sudo():
        f = _mysql_config_file(host)

        dbname = host.check_output('hostname -s').replace('.', '_')

        assert f.exists
        assert f.contains('launch+=gmysql:mysql')
        assert f.contains('gmysql-mysql-host=mysql')
        assert f.contains('gmysql-mysql-password=pdns')
        assert f.contains('gmysql-mysql-dbname=' + dbname)
        assert f.contains('gmysql-mysql-user=pdns')


================================================
FILE: molecule/resources/tests/backend-postgresql/test_backend_postgresql.py
================================================
debian_os = ['debian', 'ubuntu']
rhel_os = ['redhat', 'centos', 'ol', 'rocky', 'almalinux']
archlinux_os = ['arch', 'archarm']


def _pdns_config_dir(host):
    if host.system_info.distribution.lower() in debian_os + archlinux_os:
        return '/etc/powerdns'
    return '/etc/pdns'


def _postgresql_config_file(host):
    config_dir = _pdns_config_dir(host)
    pgsql_instance_conf = host.file(f'{config_dir}/pdns-postgresql.conf')
    if pgsql_instance_conf.exists:
        return pgsql_instance_conf
    return host.file(f'{config_dir}/pdns.conf')


def test_package(host):
    distribution = host.system_info.distribution.lower()
    if distribution in debian_os:
        package = host.package('pdns-backend-pgsql')
        assert package.is_installed
    if distribution in rhel_os:
        package = host.package('pdns-backend-postgresql')
        assert package.is_installed


def test_config(host):
    with host.sudo():
        f = _postgresql_config_file(host)
        dbname = host.check_output('hostname -s').replace('.', '_')

        assert f.exists
        assert f.contains('launch+=gpgsql')
        assert f.contains('gpgsql-host=postgresql')
        assert f.contains('gpgsql-password=pdns')
        assert f.contains('gpgsql-dbname=' + dbname)
        assert f.contains('gpgsql-user=pdns')


def test_postgresql_instance_service_is_active(host):
    cmd = host.run('systemctl is-active pdns@postgresql')
    assert cmd.rc == 0
    assert cmd.stdout.strip() == 'active'


================================================
FILE: molecule/resources/tests/backend-sqlite/test_backend_sqlite.py
================================================
debian_os = ['debian', 'ubuntu']
rhel_os = ['redhat', 'centos', 'ol', 'rocky', 'almalinux']
archlinux_os = ['arch', 'archarm']


def _pdns_config_dir(host):
    if host.system_info.distribution.lower() in debian_os + archlinux_os:
        return '/etc/powerdns'
    return '/etc/pdns'


def _sqlite_config_file(host):
    config_dir = _pdns_config_dir(host)
    sqlite_instance_conf = host.file(f'{config_dir}/pdns-sqlite.conf')
    if sqlite_instance_conf.exists:
        return sqlite_instance_conf
    return host.file(f'{config_dir}/pdns.conf')


def test_package(host):
    if host.system_info.distribution.lower() in debian_os + rhel_os:
        if host.system_info.distribution.lower() in debian_os:
            p = host.package('pdns-backend-sqlite3')
        if host.system_info.distribution.lower() in rhel_os:
            p = host.package('pdns-backend-sqlite')

        assert p.is_installed


def test_config(host):
    with host.sudo():
        f = _sqlite_config_file(host)
        assert f.exists
        assert f.contains('launch+=gsqlite3')
        assert f.contains('gsqlite3-database=/var/lib/powerdns/pdns.sqlite3')


def test_database_exists(host):
    f = host.file('/var/lib/powerdns/pdns.sqlite3')
    user = 'pdns'
    if host.system_info.distribution.lower() in archlinux_os:
        user = 'powerdns'

    assert f.exists
    assert f.user == user
    assert f.group == user
    assert f.mode == 0o640
    assert f.size > 10000


================================================
FILE: molecule/resources/tests/backend-zones/test_backend_zones.py
================================================
def _normalize_zone(zone):
    return zone.rstrip('.')


def _dns_lookup_rcode(host, zone, port):
    script = (
        "import sys,dns.message,dns.query,dns.rdatatype;"
        "zone=sys.argv[1].rstrip('.');"
        "port=int(sys.argv[2]);"
        "query=dns.message.make_query(zone,dns.rdatatype.SOA);"
        "response=dns.query.udp(query,'127.0.0.1',port=port,timeout=3);"
        "print(int(response.rcode()))"
    )
    command = f'python3 -c "{script}" "{zone}" "{port}"'
    result = host.run(command)
    assert result.rc == 0, result.stderr
    return int(result.stdout.strip())


def _pdnsutil_command(subcommand, zone=None, config_name=''):
    command_parts = ['pdnsutil']
    if config_name:
        command_parts.append(f'--config-name={config_name}')
    command_parts.append(subcommand)
    if zone:
        command_parts.append(zone)
    return ' '.join(command_parts)


def test_backend_zones_are_listed(host):
    expected_zones = (
        ('lmdb.test', 'lmdb'),
        ('sqlite3.test', 'sqlite'),
        ('mysql.test', 'mysql'),
        ('mariadb.test', 'mariadb'),
        ('postgresql.test', 'postgresql'),
    )

    for zone, config_name in expected_zones:
        cmd = host.run(_pdnsutil_command('list-all-zones', config_name=config_name))
        assert cmd.rc == 0
        discovered_zones = {
            _normalize_zone(line.strip())
            for line in cmd.stdout.splitlines()
            if line.strip()
        }
        assert zone in discovered_zones


def test_backend_zones_are_queryable(host):
    expected_zones = (
        ('lmdb.test', 'lmdb'),
        ('sqlite3.test', 'sqlite'),
        ('mysql.test', 'mysql'),
        ('mariadb.test', 'mariadb'),
        ('postgresql.test', 'postgresql'),
    )

    for zone, config_name in expected_zones:
        cmd = host.run(_pdnsutil_command('list-zone', zone=zone, config_name=config_name))
        assert cmd.rc == 0
        assert zone in cmd.stdout


def test_backend_zones_dns_lookup_noerror(host):
    expected_zones = (
        ('lmdb.test', 54),
        ('sqlite3.test', 55),
        ('mysql.test', 56),
        ('mariadb.test', 57),
        ('bind.test', 58),
        ('postgresql.test', 59),
    )

    for zone, port in expected_zones:
        assert _dns_lookup_rcode(host, zone, port) == 0


================================================
FILE: molecule/resources/tests/repo-48/test_repo_48.py
================================================
import re

debian_os = ['debian', 'ubuntu']
rhel_os = ['redhat', 'centos', 'ol', 'rocky', 'almalinux']


def _release_major(host):
    release = host.system_info.release
    match = re.match(r'^(\d+)', release)
    return int(match.group(1)) if match else 0


def _supports_deb822(host):
    distro = host.system_info.distribution.lower()
    major = _release_major(host)
    if distro == 'ubuntu':
        return major >= 22
    if distro == 'debian':
        return True
    return False


def _assert_debian_repo_layout(host):
    distro = host.system_info.distribution.lower()
    if distro not in debian_os:
        return

    sources_file = host.file('/etc/apt/sources.list.d/powerdns-auth-48.sources')
    list_file = host.file('/etc/apt/sources.list.d/powerdns-auth-48.list')

    if _supports_deb822(host):
        assert sources_file.exists
        assert not list_file.exists
    else:
        assert list_file.exists
        assert not sources_file.exists


def _repo_file(host):
    distro = host.system_info.distribution.lower()
    if distro in debian_os:
        if _supports_deb822(host):
            return host.file('/etc/apt/sources.list.d/powerdns-auth-48.sources')
        return host.file('/etc/apt/sources.list.d/powerdns-auth-48.list')
    if distro in rhel_os:
        return host.file('/etc/yum.repos.d/powerdns-auth-48.repo')
    return None


def test_repo_file(host):
    _assert_debian_repo_layout(host)
    f = _repo_file(host)
    assert f is not None
    assert f.exists
    assert f.user == 'root'
    assert f.group == 'root'


def test_pdns_repo(host):
    f = _repo_file(host)
    assert f is not None
    assert f.exists
    assert f.contains('auth-48')


def test_repo_pinning_file(host):
    if host.system_info.distribution.lower() in debian_os:
        f = host.file('/etc/apt/preferences.d/pdns')
        assert f.exists
        assert f.user == 'root'
        assert f.group == 'root'
        f.contains('Package: pdns-*')
        f.contains('Pin: origin repo.powerdns.com')
        f.contains('Pin-Priority: 600')


def test_pdns_version(host):
    cmd = host.run('/usr/sbin/pdns_server --version')

    assert 'PowerDNS Authoritative Server' in cmd.stderr
    assert '4.8' in cmd.stderr


================================================
FILE: molecule/resources/tests/repo-49/test_repo_49.py
================================================
import re

debian_os = ['debian', 'ubuntu']
rhel_os = ['redhat', 'centos', 'ol', 'rocky', 'almalinux']


def _release_major(host):
    release = host.system_info.release
    match = re.match(r'^(\d+)', release)
    return int(match.group(1)) if match else 0


def _supports_deb822(host):
    distro = host.system_info.distribution.lower()
    major = _release_major(host)
    if distro == 'ubuntu':
        return major >= 22
    if distro == 'debian':
        return True
    return False


def _assert_debian_repo_layout(host):
    distro = host.system_info.distribution.lower()
    if distro not in debian_os:
        return

    sources_file = host.file('/etc/apt/sources.list.d/powerdns-auth-49.sources')
    list_file = host.file('/etc/apt/sources.list.d/powerdns-auth-49.list')

    if _supports_deb822(host):
        assert sources_file.exists
        assert not list_file.exists
    else:
        assert list_file.exists
        assert not sources_file.exists


def _repo_file(host):
    distro = host.system_info.distribution.lower()
    if distro in debian_os:
        if _supports_deb822(host):
            return host.file('/etc/apt/sources.list.d/powerdns-auth-49.sources')
        return host.file('/etc/apt/sources.list.d/powerdns-auth-49.list')
    if distro in rhel_os:
        return host.file('/etc/yum.repos.d/powerdns-auth-49.repo')
    return None


def test_repo_file(host):
    _assert_debian_repo_layout(host)
    f = _repo_file(host)
    assert f is not None
    assert f.exists
    assert f.user == 'root'
    assert f.group == 'root'


def test_pdns_repo(host):
    f = _repo_file(host)
    assert f is not None
    assert f.exists
    assert f.contains('auth-49')


def test_repo_pinning_file(host):
    if host.system_info.distribution.lower() in debian_os:
        f = host.file('/etc/apt/preferences.d/pdns')
        assert f.exists
        assert f.user == 'root'
        assert f.group == 'root'
        f.contains('Package: pdns-*')
        f.contains('Pin: origin repo.powerdns.com')
        f.contains('Pin-Priority: 600')


def test_pdns_version(host):
    cmd = host.run('/usr/sbin/pdns_server --version')
    output = f'{cmd.stdout}\n{cmd.stderr}'

    assert 'PowerDNS Authoritative Server' in output
    assert '4.9' in output


================================================
FILE: molecule/resources/tests/repo-50/test_repo_50.py
================================================
import re

debian_os = ['debian', 'ubuntu']
rhel_os = ['redhat', 'centos', 'ol', 'rocky', 'almalinux']


def _release_major(host):
    release = host.system_info.release
    match = re.match(r'^(\d+)', release)
    return int(match.group(1)) if match else 0


def _supports_deb822(host):
    distro = host.system_info.distribution.lower()
    major = _release_major(host)
    if distro == 'ubuntu':
        return major >= 22
    if distro == 'debian':
        return True
    return False


def _assert_debian_repo_layout(host):
    distro = host.system_info.distribution.lower()
    if distro not in debian_os:
        return

    sources_file = host.file('/etc/apt/sources.list.d/powerdns-auth-50.sources')
    list_file = host.file('/etc/apt/sources.list.d/powerdns-auth-50.list')

    if _supports_deb822(host):
        assert sources_file.exists
        assert not list_file.exists
    else:
        assert list_file.exists
        assert not sources_file.exists


def _repo_file(host):
    distro = host.system_info.distribution.lower()
    if distro in debian_os:
        if _supports_deb822(host):
            return host.file('/etc/apt/sources.list.d/powerdns-auth-50.sources')
        return host.file('/etc/apt/sources.list.d/powerdns-auth-50.list')
    if distro in rhel_os:
        return host.file('/etc/yum.repos.d/powerdns-auth-50.repo')
    return None


def test_repo_file(host):
    _assert_debian_repo_layout(host)
    f = _repo_file(host)
    assert f is not None
    assert f.exists
    assert f.user == 'root'
    assert f.group == 'root'


def test_pdns_repo(host):
    f = _repo_file(host)
    assert f is not None
    assert f.exists
    assert f.contains('auth-50')


def test_repo_pinning_file(host):
    if host.system_info.distribution.lower() in debian_os:
        f = host.file('/etc/apt/preferences.d/pdns')
        assert f.exists
        assert f.user == 'root'
        assert f.group == 'root'
        f.contains('Package: pdns-*')
        f.contains('Pin: origin repo.powerdns.com')
        f.contains('Pin-Priority: 600')


def test_pdns_version(host):
    cmd = host.run('/usr/sbin/pdns_server --version')
    output = f'{cmd.stdout}\n{cmd.stderr}'

    assert 'PowerDNS Authoritative Server' in output
    assert '5.0' in output


================================================
FILE: molecule/resources/tests/repo-master/test_repo_master.py
================================================
import re

debian_os = ['debian', 'ubuntu']
rhel_os = ['redhat', 'centos', 'ol', 'rocky', 'almalinux']


def _release_major(host):
    release = host.system_info.release
    match = re.match(r'^(\d+)', release)
    return int(match.group(1)) if match else 0


def _supports_deb822(host):
    distro = host.system_info.distribution.lower()
    major = _release_major(host)
    if distro == 'ubuntu':
        return major >= 22
    if distro == 'debian':
        return True
    return False


def _assert_debian_repo_layout(host):
    distro = host.system_info.distribution.lower()
    if distro not in debian_os:
        return

    sources_file = host.file('/etc/apt/sources.list.d/powerdns-auth-master.sources')
    list_file = host.file('/etc/apt/sources.list.d/powerdns-auth-master.list')

    if _supports_deb822(host):
        assert sources_file.exists
        assert not list_file.exists
    else:
        assert list_file.exists
        assert not sources_file.exists


def _repo_file(host):
    distro = host.system_info.distribution.lower()
    if distro in debian_os:
        if _supports_deb822(host):
            return host.file('/etc/apt/sources.list.d/powerdns-auth-master.sources')
        return host.file('/etc/apt/sources.list.d/powerdns-auth-master.list')
    if distro in rhel_os:
        return host.file('/etc/yum.repos.d/powerdns-auth-master.repo')
    return None


def test_repo_file(host):
    _assert_debian_repo_layout(host)
    f = _repo_file(host)
    assert f is not None
    assert f.exists
    assert f.user == 'root'
    assert f.group == 'root'


def test_pdns_repo(host):
    f = _repo_file(host)
    assert f is not None
    assert f.exists
    assert f.contains('auth-master')


def test_repo_pinning_file(host):
    if host.system_info.distribution.lower() in debian_os:
        f = host.file('/etc/apt/preferences.d/pdns')
        assert f.exists
        assert f.user == 'root'
        assert f.group == 'root'
        f.contains('Package: pdns-*')
        f.contains('Pin: origin repo.powerdns.com')
        f.contains('Pin-Priority: 600')


def test_pdns_version(host):
    cmd = host.run('/usr/sbin/pdns_server --version')

    assert 'PowerDNS Authoritative Server' in cmd.stderr or 'PowerDNS Authoritative Server' in cmd.stdout
    assert 'master' in cmd.stderr or 'master' in cmd.stdout


================================================
FILE: molecule/resources/tests/service-mask/test_service_mask.py
================================================
def test_default_pdns_service_is_masked_and_stopped(host):
    smgr = host.ansible("setup")["ansible_facts"]["ansible_service_mgr"]
    if smgr != 'systemd':
        return

    is_enabled = host.run('systemctl is-enabled pdns')
    assert is_enabled.stdout.strip() == 'masked'

    is_active = host.run('systemctl is-active pdns')
    assert is_active.stdout.strip() != 'active'

    # Port 53 may appear as listening in containerized environments even when no
    # default pdns process is running. Validate behavior instead: querying a
    # zone provisioned on instance backends must not succeed on the default port.
    query = host.run(
        """python3 - <<'PY'
import dns.exception
import dns.message
import dns.query
import dns.rdatatype

query = dns.message.make_query('lmdb.test', dns.rdatatype.SOA)
try:
    response = dns.query.udp(query, '127.0.0.1', port=53, timeout=2)
    print(response.rcode())
except dns.exception.Timeout:
    print('TIMEOUT')
PY"""
    )
    assert query.rc == 0, query.stderr
    assert query.stdout.strip() in ('TIMEOUT', '5')


================================================
FILE: molecule/resources/tests/systemd-no-override/test_override.py
================================================
def test_systemd_override(host):
    smgr = host.ansible("setup")["ansible_facts"]["ansible_service_mgr"]
    if smgr == 'systemd':
        fname = '/etc/systemd/system/pdns.service.d/override.conf'
        f = host.file(fname)

        assert not f.exists


================================================
FILE: molecule/resources/tests/systemd-override/test_override.py
================================================
def test_systemd_override(host):
    smgr = host.ansible("setup")["ansible_facts"]["ansible_service_mgr"]
    if smgr == 'systemd':
        fname = '/etc/systemd/system/pdns.service.d/override.conf'
        for config_dir in ('/etc/powerdns', '/etc/pdns'):
            if host.file(f'{config_dir}/pdns-lmdb.conf').exists:
                fname = '/etc/systemd/system/pdns@lmdb.service.d/override.conf'
                break
        f = host.file(fname)

        assert f.exists
        assert f.user == 'root'
        assert f.group == 'root'
        assert f.contains('LimitCORE=infinity')


================================================
FILE: molecule/resources/vars/molecule.yml
================================================
---
molecule_file: "{{ lookup('env', 'MOLECULE_FILE') }}"
molecule_ephemeral_directory: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}"
molecule_scenario_directory: "{{ lookup('env', 'MOLECULE_SCENARIO_DIRECTORY') }}"
role_file: requirements.yml
requirements_file: requirements.yml
molecule_yml: "{{ lookup('file', molecule_file) | from_yaml }}"


================================================
FILE: molecule/resources/vars/pdns-backend-bind.yml
================================================
---

# Bind backend profile
pdns_backends_bind:
  bind:
    config: "{{ pdns_config_dir }}/named.conf"

pdns_instance:
  service_name: "pdns@bind"
  service_enabled: "no"
  config_file: "pdns-bind.conf"
  config_name: "bind"
  config_overrides:
    local-port: "58"
    webserver-port: "8006"
  zone: "bind.test"

pdns_config_files_bind:
  - dest: named.conf
    mode: "0640"
    content: |
      options {
        directory "{{ pdns_config_dir }}";
      };
      zone "bind.test" IN {
        type master;
        file "bind.test.zone";
      };
  - dest: bind.test.zone
    mode: "0640"
    content: |
      $TTL 60
      @ IN SOA ns1.bind.test. hostmaster.bind.test. 2026022101 3600 1800 1209600 60
      @ IN NS ns1.bind.test.
      ns1 IN A 127.0.0.1
      @ IN A 127.0.0.58

pdns_config_overrides_bind: {}

pdns_backends: "{{ pdns_backends_bind }}"
pdns_config_files: "{{ pdns_config_files_bind }}"


================================================
FILE: molecule/resources/vars/pdns-backend-lmdb.yml
================================================
---

# LMDB backend profile
pdns_backends_lmdb:
  lmdb:
    filename: /var/lib/powerdns/pdns.lmdb

pdns_instance:
  service_name: "pdns@lmdb"
  service_enabled: "yes"
  config_file: "pdns-lmdb.conf"
  config_name: "lmdb"
  config_overrides:
    local-port: "54"
    webserver-port: "8002"

  zone: "lmdb.test"

pdns_lmdb_databases_locations_lmdb:
  - '/var/lib/powerdns/pdns.lmdb'

pdns_config_overrides_lmdb: {}

pdns_backends: "{{ pdns_backends_lmdb }}"
pdns_lmdb_databases_locations: "{{ pdns_lmdb_databases_locations_lmdb }}"


================================================
FILE: molecule/resources/vars/pdns-backend-mariadb.yml
================================================
---

# MariaDB backend profile
pdns_backends_mariadb:
  'gmysql:mariadb':
    host: "mariadb"
    dbname: "{{ ansible_hostname | replace('.', '_') }}"
    user: "pdns_{{ ansible_hostname | replace('.', '_') | replace('-', '_') }}"
    password: pdns

pdns_instance:
  service_name: "pdns@mariadb"
  service_enabled: "no"
  config_file: "pdns-mariadb.conf"
  config_name: "mariadb"
  config_overrides:
    local-port: "57"
    webserver-port: "8005"
  zone: "mariadb.test"

pdns_mysql_databases_credentials_mariadb:
  'gmysql:mariadb':
    priv_user: root
    priv_password: "{{ ansible_env.MARIADB_ENV_MARIADB_ROOT_PASSWORD | default('pdns') }}"
    priv_host:
      - '%'
      - 'localhost'

# Molecule uses a remote MariaDB service container, so connections must use TCP.
pdns_mysql_query_use_socket_mariadb: false
pdns_mysql_schema_on_first_node_only: false
pdns_backends_mysql_cmd: "mysql"
pdns_mysql_cli_extra_args_mariadb: >-
  {{ '--ssl-mode=DISABLED' if ansible_distribution == 'Ubuntu'
     else '--skip-ssl' }}
pdns_mysql_cli_extra_args: "{{ pdns_mysql_cli_extra_args_mariadb }}"

pdns_config_overrides_mariadb: {}

pdns_backends: "{{ pdns_backends_mariadb }}"
pdns_mysql_databases_credentials: "{{ pdns_mysql_databases_credentials_mariadb }}"
pdns_mysql_query_use_socket: "{{ pdns_mysql_query_use_socket_mariadb }}"


================================================
FILE: molecule/resources/vars/pdns-backend-mysql.yml
================================================
---

# MySQL backend profile
pdns_backends_mysql:
  'gmysql:mysql':
    host: "mysql"
    dbname: "{{ ansible_hostname | replace('.', '_') }}"
    user: "pdns_{{ ansible_hostname | replace('.', '_') | replace('-', '_') }}"
    password: pdns

pdns_instance:
  service_name: "pdns@mysql"
  service_enabled: "no"
  config_file: "pdns-mysql.conf"
  config_name: "mysql"
  config_overrides:
    local-port: "56"
    webserver-port: "8004"
  zone: "mysql.test"

pdns_mysql_databases_credentials_mysql:
  'gmysql:mysql':
    priv_user: root
    priv_password: "{{ ansible_env.MYSQL_ENV_MYSQL_ROOT_PASSWORD | default('pdns') }}"
    priv_host:
      - '%'
      - 'localhost'
    # MySQL 8.4/9 disables mysql_native_password by default.
    auth_plugin: caching_sha2_password

# Molecule uses a remote MySQL service container, so connections must use TCP.
pdns_mysql_query_use_socket_mysql: false
pdns_mysql_schema_on_first_node_only: false
pdns_backends_mysql_cmd: "mysql"
pdns_mysql_cli_extra_args_mysql: >-
  {{ '--ssl-mode=DISABLED --get-server-public-key' if ansible_distribution == 'Ubuntu'
     else '--skip-ssl' }}
pdns_mysql_cli_extra_args: "{{ pdns_mysql_cli_extra_args_mysql }}"

pdns_config_overrides_mysql: {}

pdns_backends: "{{ pdns_backends_mysql }}"
pdns_mysql_databases_credentials: "{{ pdns_mysql_databases_credentials_mysql }}"
pdns_mysql_query_use_socket: "{{ pdns_mysql_query_use_socket_mysql }}"


================================================
FILE: molecule/resources/vars/pdns-backend-postgresql.yml
================================================
---

# PostgreSQL backend profile
pdns_backends_pgsql:
  gpgsql:
    host: "postgresql"
    dbname: "{{ ansible_hostname | replace('.', '_') }}"
    user: "pdns_{{ ansible_hostname | replace('.', '_') | replace('-', '_') }}"
    password: pdns

pdns_instance:
  service_name: "pdns@postgresql"
  service_enabled: "no"
  config_file: "pdns-postgresql.conf"
  config_name: "postgresql"
  config_overrides:
    local-port: "59"
    webserver-port: "8007"
  zone: "postgresql.test"

pdns_pgsql_databases_credentials_pgsql:
  gpgsql:
    priv_user: postgres
    priv_password: "{{ ansible_env.POSTGRESQL_ENV_POSTGRES_PASSWORD | default('pdns') }}"

pdns_pgsql_query_use_socket_pgsql: false
pdns_pgsql_schema_on_first_node_only: false

pdns_config_overrides_pgsql: {}

pdns_backends: "{{ pdns_backends_pgsql }}"
pdns_pgsql_databases_credentials: "{{ pdns_pgsql_databases_credentials_pgsql }}"
pdns_pgsql_query_use_socket: "{{ pdns_pgsql_query_use_socket_pgsql }}"


================================================
FILE: molecule/resources/vars/pdns-backend-sqlite3.yml
================================================
---

# SQLite3 backend profile
pdns_backends_sqlite3:
  gsqlite3:
    database: /var/lib/powerdns/pdns.sqlite3
    dnssec: true

pdns_instance:
  service_name: "pdns@sqlite"
  service_enabled: "no"
  config_file: "pdns-sqlite.conf"
  config_name: "sqlite"
  config_overrides:
    local-port: "55"
    webserver-port: "8003"
  zone: "sqlite3.test"

pdns_sqlite_databases_locations_sqlite3:
  - '/var/lib/powerdns/pdns.sqlite3'

pdns_config_overrides_sqlite3: {}

pdns_backends: "{{ pdns_backends_sqlite3 }}"
pdns_sqlite_databases_locations: "{{ pdns_sqlite_databases_locations_sqlite3 }}"


================================================
FILE: molecule/resources/vars/pdns-common.yml
================================================
---

##
# PowerDNS Configuration
##

pdns_config_common:

  # Turns on primary operations
  primary: true

  include-dir: "user_pdns.d"

  # Listen Address
  local-address: "127.0.0.1"
  local-port: "53"

  # API Configuration
  api: true
  api-key: "powerdns"

  # Embedded webserver
  webserver: true
  webserver-address: "127.0.0.1"
  webserver-port: "8001"

pdns_service_overrides:
  LimitCORE: infinity

pdns_config_additional_dirs:
  - path: "{{ pdns_config_common['include-dir'] }}"
    mode: "0775"

pdns_config_files:
  - dest: "{{ pdns_config_common['include-dir'] }}/user.config"
    mode: "0750"
    content: |
      # my additional config file
      version-string=powerdns


================================================
FILE: molecule/resources/vars/pdns-no-overrides.yml
================================================
---

##
# PowerDNS Configuration
##

pdns_config:

  # Turns on primary operations
  primary: true

  # Listen Address
  local-address: "127.0.0.1"
  local-port: "53"

  # API Configuration
  api: true
  api-key: "powerdns"

  # Embedded webserver
  webserver: true
  webserver-address: "0.0.0.0"
  webserver-port: "8001"

pdns_service_overrides: {}


================================================
FILE: molecule/resources/vars/pdns-os-repos.yml
================================================
---

##
# PowerDNS Configuration
##

_pdns_config_base:

  # Listen Address
  local-address: "127.0.0.1"
  local-port: "53"

  # API Configuration
  api: true
  api-key: "powerdns"

  # Embedded webserver
  webserver: true
  webserver-address: "0.0.0.0"
  webserver-port: "8001"

# Turns on master operations
_pdns_config_primary:
  primary: true

pdns_config: "{{ _pdns_config_base | combine(_pdns_config_legacy | default(_pdns_config_primary), recursive=True) }}"

pdns_service_overrides:
  LimitCORE: infinity


================================================
FILE: molecule/resources/vars/pdns-repo-48.yml
================================================
---

##
# PowerDNS 4.8.x Repository
##

pdns_install_repo: "{{ pdns_auth_powerdns_repo_48 }}"


================================================
FILE: molecule/resources/vars/pdns-repo-49.yml
================================================
---

##
# PowerDNS 4.9.x Repository
##

pdns_install_repo: "{{ pdns_auth_powerdns_repo_49 }}"


================================================
FILE: molecule/resources/vars/pdns-repo-50.yml
================================================
---

##
# PowerDNS 5.0.x Repository
##

pdns_install_repo: "{{ pdns_auth_powerdns_repo_50 }}"


================================================
FILE: molecule/resources/vars/pdns-repo-master.yml
================================================
---

##
# PowerDNS Master Repository
##

pdns_install_repo: "{{ pdns_auth_powerdns_repo_master }}"


================================================
FILE: requirements.yml
================================================
---
collections:
  - name: community.mysql
  - name: community.postgresql
  - name: community.general
    version: "<11.0.0"
  - name: community.docker
    version: "<5.0.0"
  - name: ansible.posix


================================================
FILE: tasks/configure.yml
================================================
---
- name: Set up systemd override
  when: ansible_service_mgr == "systemd"
  tags:
    - config
  block:

    - name: Ensure the override directory exists (systemd)
      ansible.builtin.file:
        name: "/etc/systemd/system/{{ pdns_service_name }}.service.d"
        state: directory
        owner: root
        group: root
        mode: "0755"

    - name: Override the PowerDNS Authoritative Server unit (systemd)
      ansible.builtin.template:
        src: "override-service.systemd.conf.j2"
        dest: "/etc/systemd/system/{{ pdns_service_name }}.service.d/override.conf"
        owner: root
        group: root
        mode: "0644"
      notify:
        - reload systemd
        - restart pdns
      when: pdns_service_overrides | length > 0

- name: Ensure that the PowerDNS configuration directory exists
  ansible.builtin.file:
    name: "{{ pdns_config_dir }}"
    state: directory
    owner: "{{ pdns_file_owner }}"
    group: "{{ pdns_file_group }}"
    mode: "0750"
  tags:
    - config

- name: Generate the PowerDNS configuration
  ansible.builtin.template:
    src: pdns.conf.j2
    dest: "{{ pdns_config_dir }}/{{ pdns_config_file }}"
    owner: "{{ pdns_file_owner }}"
    group: "{{ pdns_file_group }}"
    mode: "0640"
  notify: restart pdns
  tags:
    - config

- name: Ensure configured PowerDNS additional directories exist
  ansible.builtin.file:
    name: "{{ item.path | default(item) }}"
    state: directory
    owner: "{{ item.owner | default(pdns_file_owner) }}"
    group: "{{ item.group | default(pdns_file_group) }}"
    mode: "{{ item.mode | default('0750') }}"
  loop: "{{ pdns_config_additional_dirs }}"
  loop_control:
    label: "{{ item.path | default(item) }}"
  notify: restart pdns
  tags:
    - config

- name: Ensure directories for configured PowerDNS extra files exist
  ansible.builtin.file:
    name: "{{ _pdns_config_file_dest | dirname }}"
    state: directory
    owner: "{{ item.dir_owner | default(item.owner | default(pdns_file_owner)) }}"
    group: "{{ item.dir_group | default(item.group | default(pdns_file_group)) }}"
    mode: "{{ item.dir_mode | default('0750') }}"
  vars:
    _pdns_config_file_dest: >-
      {{ item.dest if (item.dest is match('^/')) else (pdns_config_dir ~ '/' ~ item.dest) }}
  loop: "{{ pdns_config_files }}"
  loop_control:
    label: "{{ item.dest }}"
  notify: restart pdns
  tags:
    - config

- name: Copy configured PowerDNS extra files
  ansible.builtin.copy:
    content: "{{ item.content | default(omit) }}"
    src: "{{ item.src | default(omit) }}"
    dest: "{{ _pdns_config_file_dest }}"
    owner: "{{ item.owner | default(pdns_file_owner) }}"
    group: "{{ item.group | default(pdns_file_group) }}"
    mode: "{{ item.mode | default('0640') }}"
  vars:
    _pdns_config_file_dest: >-
      {{ item.dest if (item.dest is match('^/')) else (pdns_config_dir ~ '/' ~ item.dest) }}
  loop: "{{ pdns_config_files }}"
  loop_control:
    label: "{{ item.dest }}"
  notify: restart pdns
  tags:
    - config


================================================
FILE: tasks/database-lmdb.yml
================================================
---

- name: Ensure that the directories containing the PowerDNS LMDB databases exist
  ansible.builtin.file:
    name: "{{ item | dirname }}"
    owner: "{{ pdns_user }}"
    group: "{{ pdns_group }}"
    state: directory
    mode: "0750"
  with_items: "{{ pdns_lmdb_databases_locations }}"
  tags:
    - config


================================================
FILE: tasks/database-mysql.yml
================================================
---

- name: Install the MySQL dependencies
  ansible.builtin.package:
    name: "{{ pdns_mysql_packages }}"
    state: "{{ pdns_mysql_packages_state }}"
  tags:
    - install

- name: Manage the PowerDNS MySQL databases
  when:
    - pdns_package_state != 'absent'
    - pdns_mysql_manage_database | bool
  tags:
    - config
  block:

    - name: Create the PowerDNS MySQL databases
      community.mysql.mysql_db:
        login_user: "{{ item['value']['priv_user'] }}"
        login_password: "{{ item['value']['priv_password'] }}"
        login_host: "{{ item['value']['host'] | default('localhost') if not pdns_mysql_query_use_socket else omit }}"
        login_port: "{{ item['value']['port'] | default('3306') if not pdns_mysql_query_use_socket else omit }}"
        login_unix_socket: "{{ pdns_mysql_unix_socket if pdns_mysql_query_use_socket else omit }}"
        name: "{{ item['value']['dbname'] }}"
        state: present
      when:
        - item.key.split(':')[0] == 'gmysql'
        - item['value']['priv_user'] is defined
        - item['value']['priv_password'] is defined
      run_once: "{{ pdns_mysql_schema_on_first_node_only }}"
      throttle: 1
      no_log: "{{ not pdns_verbose }}"
      with_dict: "{{ pdns_backends | combine(pdns_mysql_databases_credentials, recursive=True) }}"

    - name: Grant PowerDNS access to the MySQL databases
      community.mysql.mysql_user:
        login_user: "{{ item[0]['priv_user'] }}"
        login_password: "{{ item[0]['priv_password'] }}"
        login_host: "{{ item[0]['host'] | default('localhost') if not pdns_mysql_query_use_socket else omit }}"
        login_port: "{{ item[0]['port'] | default('3306') if not pdns_mysql_query_use_socket else omit }}"
        login_unix_socket: "{{ pdns_mysql_unix_socket if pdns_mysql_query_use_socket else omit }}"
        name: "{{ item[0]['user'] }}"
        password: "{{ item[0]['password'] if (_pdns_mysql_auth_plugin_effective | length == 0) else omit }}"
        plugin: "{{ _pdns_mysql_auth_plugin_effective if (_pdns_mysql_auth_plugin_effective | length > 0) else omit }}"
        plugin_auth_string: "{{ item[0]['password'] if (_pdns_mysql_auth_plugin_effective | length > 0) else omit }}"
        update_password: >-
          {{ _pdns_mysql_user_update_password_effective
             if (_pdns_mysql_user_update_password_effective | length > 0)
             else ('on_create' if (_pdns_mysql_auth_plugin_effective | length > 0) else 'always') }}
        host: "{{ item[1] }}"
        priv: "{{ item[0]['dbname'] }}.*:ALL"
        append_privs: true
        state: present
      when: pdns_mysql_databases_credentials | length > 0
      no_log: "{{ not pdns_verbose }}"
      vars:
        _pdns_mysql_auth_plugin_effective: "{{ item[0]['auth_plugin'] | default(pdns_mysql_auth_plugin) }}"
        _pdns_mysql_user_update_password_effective: "{{ item[0]['update_password'] | default(pdns_mysql_user_update_password) }}"
      run_once: "{{ pdns_mysql_schema_on_first_node_only }}"
      throttle: 1
      with_subelements:
        - "{{ pdns_backends | combine(pdns_mysql_databases_credentials, recursive=True) }}"
        - priv_host
        - skip_missing: true

    - name: Check if the MySQL databases are empty
      ansible.builtin.command:
        cmd: >-
          {{ pdns_backends_mysql_cmd }} --user="{{ item['value']['user'] }}" --password="{{ item['value']['password'] }}"
          {{ pdns_mysql_cli_extra_args }}
          {% if pdns_mysql_query_use_socket %} --socket="{{ pdns_mysql_unix_socket }}"
          {% else %} --host="{{ item['value']['host'] | default('localhost') }}" --port="{{ item['value']['port'] | default('3306') }}"{% endif %}
          --batch --skip-column-names
          --execute="SELECT COUNT(DISTINCT table_name) FROM information_schema.columns WHERE table_schema = '{{ item['value']['dbname'] }}'"
      when:
        - pdns_mysql_schema_load
        - item.key.split(':')[0] == 'gmysql'
      with_dict: "{{ pdns_backends }}"
      register: _pdns_check_mysql_db
      no_log: "{{ not pdns_verbose }}"
      changed_when: false

    - name: Determine location of the SQL file
      ansible.builtin.shell:
        cmd: |
          for p in /usr/share/doc/pdns-backend-mysql-{{ _pdns_running_version }}/schema.mysql.sql \
            /usr/share/doc/pdns-backend-mysql/schema.mysql.sql \
            /usr/share/pdns-backend-mysql/schema/schema.mysql.sql \
            /usr/share/dbconfig-common/data/pdns-backend-mysql/install/mysql \
            /usr/share/doc/powerdns/schema.mysql.sql \
            /usr/share/doc/pdns/schema.mysql.sql; do
            if [ -f $p ]; then
              echo $p
              exit 0
            fi
          done
          echo "Can't determine path to MySQL schema">&2
          exit 1
      changed_when: false
      register: _pdns_mysql_schema_file_detected
      when:
        - pdns_mysql_schema_load
        - pdns_mysql_schema_file | length == 0

    - name: Set the schema file variable
      ansible.builtin.set_fact:
        _pdns_mysql_schema_file_to_use: >-
          {{ _pdns_mysql_schema_file_detected.stdout
             if pdns_mysql_schema_file | length == 0 else pdns_mysql_schema_file }}
      when: pdns_mysql_schema_load

    - name: Import the PowerDNS MySQL schema
      ansible.builtin.shell:
        cmd: >-
          {{ pdns_backends_mysql_cmd }}
          --user="{{ item['item']['value']['user'] }}"
          --password="{{ item['item']['value']['password'] }}"
          {{ pdns_mysql_cli_extra_args }}
          {% if pdns_mysql_query_use_socket %}
          --socket="{{ pdns_mysql_unix_socket }}"
          {% else %}
          --host="{{ item['item']['value']['host'] | default('localhost') }}"
          --port="{{ item['item']['value']['port'] | default('3306') }}"
          {% endif %}
          --database="{{ item.item['value']['dbname'] }}"
          < "{{ _pdns_mysql_schema_file_to_use }}"
      no_log: "{{ not pdns_verbose }}"
      run_once: "{{ pdns_mysql_schema_on_first_node_only }}"
      throttle: 1
      changed_when: item['stdout'] == '0'
      when:
        - pdns_mysql_schema_load
        - item['item']['key'].split(':')[0] == 'gmysql'
        - item['stdout'] == '0'
      with_items: "{{ _pdns_check_mysql_db['results'] }}"


================================================
FILE: tasks/database-pgsql.yml
================================================
---

- name: Install the PostgreSQL dependencies
  ansible.builtin.package:
    name: "{{ pdns_pgsql_packages }}"
    state: "{{ pdns_pgsql_packages_state }}"
  tags:
    - install

- name: Manage the PowerDNS PostgreSQL databases
  when:
    - pdns_package_state != 'absent'
    - pdns_pgsql_manage_database | bool
  tags:
    - config
  block:

    - name: Create PowerDNS PostgreSQL users
      community.postgresql.postgresql_user:
        login_user: "{{ item['value']['priv_user'] }}"
        login_password: "{{ item['value']['priv_password'] }}"
        login_host: "{{ item['value']['host'] | default('localhost') if not pdns_pgsql_query_use_socket else omit }}"
        login_port: "{{ item['value']['port'] | default('5432') if not pdns_pgsql_query_use_socket else omit }}"
        login_unix_socket: "{{ pdns_pgsql_unix_socket if pdns_pgsql_query_use_socket else omit }}"
        name: "{{ item['value']['user'] }}"
        password: "{{ item['value']['password'] }}"
        role_attr_flags: "LOGIN,NOSUPERUSER,NOCREATEDB,NOCREATEROLE,NOREPLICATION"
        state: present
      when:
        - item.key.split(':')[0] == 'gpgsql'
        - item['value']['priv_user'] is defined
        - item['value']['priv_password'] is defined
      run_once: "{{ pdns_pgsql_schema_on_first_node_only }}"
      throttle: 1
      no_log: "{{ not pdns_verbose }}"
      with_dict: "{{ pdns_backends | combine(pdns_pgsql_databases_credentials, recursive=True) }}"

    - name: Create the PowerDNS PostgreSQL databases
      community.postgresql.postgresql_db:
        login_user: "{{ item['value']['priv_user'] }}"
        login_password: "{{ item['value']['priv_password'] }}"
        login_host: "{{ item['value']['host'] | default('localhost') if not pdns_pgsql_query_use_socket else omit }}"
        login_port: "{{ item['value']['port'] | default('5432') if not pdns_pgsql_query_use_socket else omit }}"
        login_unix_socket: "{{ pdns_pgsql_unix_socket if pdns_pgsql_query_use_socket else omit }}"
        name: "{{ item['value']['dbname'] }}"
        owner: "{{ item['value']['user'] }}"
        state: present
      when:
        - item.key.split(':')[0] == 'gpgsql'
        - item['value']['priv_user'] is defined
        - item['value']['priv_password'] is defined
      run_once: "{{ pdns_pgsql_schema_on_first_node_only }}"
      throttle: 1
      no_log: "{{ not pdns_verbose }}"
      with_dict: "{{ pdns_backends | combine(pdns_pgsql_databases_credentials, recursive=True) }}"

    - name: Check if the PostgreSQL databases are empty
      community.postgresql.postgresql_query:
        login_db: "{{ item['value']['dbname'] }}"
        login_user: "{{ item['value']['user'] }}"
        login_password: "{{ item['value']['password'] }}"
        login_host: "{{ item['value']['host'] | default('localhost') if not pdns_pgsql_query_use_socket else omit }}"
        login_port: "{{ item['value']['port'] | default('5432') if not pdns_pgsql_query_use_socket else omit }}"
        login_unix_socket: "{{ pdns_pgsql_unix_socket if pdns_pgsql_query_use_socket else omit }}"
        query: >-
          SELECT COUNT(DISTINCT tablename) AS count
          FROM pg_catalog.pg_tables
          WHERE schemaname = 'public';
      when:
        - pdns_pgsql_schema_load
        - item.key.split(':')[0] == 'gpgsql'
      with_dict: "{{ pdns_backends }}"
      register: _pdns_check_pgsql_db
      no_log: "{{ not pdns_verbose }}"
      changed_when: false

    - name: Determine location of the PostgreSQL schema SQL file
      ansible.builtin.shell:
        cmd: |
          for p in \
            /usr/share/doc/pdns-backend-postgresql-{{ _pdns_running_version }}/schema.pgsql.sql \
            /usr/share/doc/pdns-backend-pgsql-{{ _pdns_running_version }}/schema.pgsql.sql \
            /usr/share/doc/pdns-backend-postgresql/schema.pgsql.sql \
            /usr/share/doc/pdns-backend-pgsql/schema.pgsql.sql \
            /usr/share/pdns-backend-pgsql/schema/schema.pgsql.sql \
            /usr/share/dbconfig-common/data/pdns-backend-pgsql/install/pgsql \
            /usr/share/doc/powerdns/schema.pgsql.sql \
            /usr/share/doc/pdns/schema.pgsql.sql; do
            if [ -f "$p" ]; then
              echo "$p"
              exit 0
            fi
          done
          echo "Can't determine path to PostgreSQL schema" >&2
          exit 1
      changed_when: false
      register: _pdns_pgsql_schema_file_detected
      when:
        - pdns_pgsql_schema_load
        - pdns_pgsql_schema_file | length == 0

    - name: Set the PostgreSQL schema file variable
      ansible.builtin.set_fact:
        _pdns_pgsql_schema_file_to_use: >-
          {{ _pdns_pgsql_schema_file_detected.stdout
             if pdns_pgsql_schema_file | length == 0 else pdns_pgsql_schema_file }}
      when: pdns_pgsql_schema_load

    - name: Import the PowerDNS PostgreSQL schema
      community.postgresql.postgresql_db:
        login_user: "{{ item['item']['value']['user'] }}"
        login_password: "{{ item['item']['value']['password'] }}"
        login_host: "{{ item['item']['value']['host'] | default('localhost') if not pdns_pgsql_query_use_socket else omit }}"
        login_port: "{{ item['item']['value']['port'] | default('5432') if not pdns_pgsql_query_use_socket else omit }}"
        login_unix_socket: "{{ pdns_pgsql_unix_socket if pdns_pgsql_query_use_socket else omit }}"
        name: "{{ item['item']['value']['dbname'] }}"
        state: restore
        target: "{{ _pdns_pgsql_schema_file_to_use }}"
      run_once: "{{ pdns_pgsql_schema_on_first_node_only }}"
      throttle: 1
      no_log: "{{ not pdns_verbose }}"
      when:
        - pdns_pgsql_schema_load
        - item['item']['key'].split(':')[0] == 'gpgsql'
        - item['query_result'][0]['count'] | int == 0
      with_items: "{{ _pdns_check_pgsql_db['results'] }}"


================================================
FILE: tasks/database-sqlite3.yml
================================================
---

- name: Install the SQLite dependencies on RedHat
  ansible.builtin.package:
    name: sqlite
    state: "{{ pdns_sqlite_package_state }}"
  when: ansible_os_family == 'RedHat'
  tags:
    - install

- name: Install the SQLite dependencies on Debian
  ansible.builtin.package:
    name: sqlite3
    state: "{{ pdns_sqlite_package_state }}"
  when: ansible_os_family == 'Debian'
  tags:
    - install
    - config

- name: Manage the PowerDNS SQLite databases
  when:
    - pdns_package_state != 'absent'
    - pdns_sqlite_databases_locations | length > 0
  tags:
    - config
  block:
    - name: Ensure that the directories containing the PowerDNS SQLite databases exist
      ansible.builtin.file:
        name: "{{ item | dirname }}"
        owner: "{{ pdns_user }}"
        group: "{{ pdns_group }}"
        state: directory
        mode: "0750"
      with_items: "{{ pdns_sqlite_databases_locations }}"

    - name: Determine location of the SQL file
      ansible.builtin.shell:
        cmd: |
          for p in \
            /usr/share/doc/pdns-backend-sqlite-{{ _pdns_running_version }}/schema.sql \
            /usr/share/doc/pdns-backend-sqlite-{{ _pdns_running_version }}/schema.sqlite3.sql \
            /usr/share/doc/pdns/schema.sqlite3.sql \
            /usr/share/doc/pdns-backend-sqlite3/schema.sqlite3.sql \
            /usr/share/doc/pdns-backend-sqlite/schema.sqlite3.sql \
            /usr/share/doc/powerdns/schema.sqlite3.sql \
            /usr/share/pdns/schema.sqlite3.sql \
            /usr/share/powerdns/schema.sqlite3.sql \
            /usr/share/pdns-backend-sqlite3/schema/schema.sqlite3.sql \
            /usr/share/pdns-backend-sqlite/schema/schema.sqlite3.sql \
            /usr/share/doc/pdns/schema.sqlite3.sql.gz \
            /usr/share/doc/pdns/schema.sqlite3.sql.xz \
            /usr/share/doc/powerdns/schema.sqlite3.sql.gz \
            /usr/share/doc/powerdns/schema.sqlite3.sql.xz; do
            if [ -f "$p" ]; then
              echo "$p"
              exit 0
            fi
          done

          # Fallback for distribution-specific schema locations.
          schema_file="$(find /usr/share/doc /usr/share -maxdepth 6 -type f \
            \( -name "schema.sqlite3.sql" -o -name "schema.sqlite3.sql.gz" -o -name "schema.sqlite3.sql.xz" \) \
            -print -quit 2>/dev/null)"
          if [ -n "$schema_file" ]; then
            echo "$schema_file"
            exit 0
          fi
          echo "Can't determine path to SQLite schema">&2
          exit 1
      changed_when: false
      register: _pdns_sqlite_schema_file_detected
      when: pdns_sqlite_schema_file | length == 0

    - name: Set the schema file variable
      ansible.builtin.set_fact:
        _pdns_sqlite_schema_file_to_use: >-
          {{ _pdns_sqlite_schema_file_detected.stdout
             if pdns_sqlite_schema_file | length == 0 else pdns_sqlite_schema_file }}

    - name: Create the PowerDNS SQLite databases
      ansible.builtin.shell:
        cmd: |
          tmp_schema="$(mktemp)"
          trap 'rm -f "$tmp_schema"' EXIT

          case "{{ _pdns_sqlite_schema_file_to_use }}" in
            *.gz)
              gzip -dc "{{ _pdns_sqlite_schema_file_to_use }}" > "$tmp_schema"
              ;;
            *.xz)
              xz -dc "{{ _pdns_sqlite_schema_file_to_use }}" > "$tmp_schema"
              ;;
            *)
              cp "{{ _pdns_sqlite_schema_file_to_use }}" "$tmp_schema"
              ;;
          esac

          sqlite3 "{{ item }}" < "$tmp_schema"
      args:
        creates: "{{ item }}"
      with_items: "{{ pdns_sqlite_databases_locations }}"

    - name: Check the PowerDNS SQLite databases permissions
      ansible.builtin.file:
        name: "{{ item }}"
        owner: "{{ pdns_user }}"
        group: "{{ pdns_group }}"
        mode: "0640"
        state: file
      with_items: "{{ pdns_sqlite_databases_locations }}"


================================================
FILE: tasks/inspect.yml
================================================
---

- name: Obtain the version of the running PowerDNS instance
  ansible.builtin.command: pdns_server --version
  register: _pdns_version_raw
  check_mode: false
  changed_when: false
  failed_when: false
  tags:
    - config

- name: Extract the PowerDNS version from command output
  ansible.builtin.set_fact:
    _pdns_version: >-
      {{ (_pdns_version_raw.stdout ~ ' ' ~ _pdns_version_raw.stderr)
         | regex_search('[0-9]+\.[0-9]+\.[0-9]+(?:[-._a-zA-Z0-9]+)?')
         | default('') }}
  tags:
    - config

- name: Ensure PowerDNS version was detected
  ansible.builtin.assert:
    that:
      - _pdns_version | length > 0
    fail_msg: >-
      Could not parse PowerDNS version from: stdout='{{ _pdns_version_raw.stdout }}'
      stderr='{{ _pdns_version_raw.stderr }}'
  tags:
    - config

- name: Export the running PowerDNS instance version to a variable
  ansible.builtin.set_fact:
    _pdns_running_version: "{{ _pdns_version | regex_replace('-[.\\d\\w]+$', '') }}"
  tags:
    - config


================================================
FILE: tasks/install.yml
================================================
---
- name: Set up version separator
  when: pdns_package_version | length > 0
  tags:
    - install
  block:
    - name: Prefix the PowerDNS version with the correct separator on RedHat
      ansible.builtin.set_fact:
        _pdns_package_version: "-{{ pdns_package_version }}"
      when: ansible_os_family == 'RedHat'

    - name: Prefix the PowerDNS version with the correct separator on Debian
      ansible.builtin.set_fact:
        _pdns_package_version: "={{ pdns_package_version }}"
      when: ansible_os_family == 'Debian'

- name: Install PowerDNS
  ansible.builtin.package:
    name: >-
      {{ pdns_package_name }}{{ _pdns_package_version | default('')
      if pdns_package_state != 'absent' else '' }}
    state: "{{ pdns_package_state }}"
  notify: reload systemd
  tags:
    - install

- name: Install PowerDNS debug symbols
  ansible.builtin.package:
    name: >-
      {{ pdns_debug_symbols_package_name }}{{ _pdns_package_version | default('')
      if pdns_debug_symbols_package_state != 'absent' else '' }}
    state: "{{ pdns_debug_symbols_package_state }}"
  when:
    - pdns_install_debug_symbols_package | bool or pdns_debug_symbols_package_state == 'absent'
  tags:
    - install

- name: Install PowerDNS backends
  ansible.builtin.package:
    name: >-
      {{ pdns_backends_packages[item.key.split(':')[0]] }}{{ _pdns_package_version | default('')
      if pdns_backends_packages_state != 'absent' else '' }}
    state: "{{ pdns_backends_packages_state }}"
  when: pdns_backends_packages[item.key.split(':')[0]] is defined
  with_dict: "{{ pdns_backends }}"
  loop_control:
    label: "{{ item.key }}"
  tags:
    - install


================================================
FILE: tasks/main.yml
================================================
---

- name: Include OS-specific variables (generic to specific)
  ansible.builtin.include_vars: "{{ role_path }}/vars/{{ item }}"
  loop:
    - "{{ ansible_os_family }}.yml"
    - "{{ ansible_distribution }}.yml"
    - "{{ ansible_os_family }}-{{ ansible_distribution_major_version }}.yml"
    - "{{ ansible_distribution }}-{{ ansible_distribution_major_version }}.yml"
  when:
    - lookup('ansible.builtin.fileglob', role_path ~ '/vars/' ~ item, wantlist=True) | length > 0
  tags:
    - always

- name: Setup repository
  ansible.builtin.include_tasks: "repo-{{ ansible_os_family }}.yml"
  when: "pdns_install_repo | length > 0"
  tags:
    - install
    - repository

- name: Install PowerDNS
  ansible.builtin.include_tasks: install.yml
  tags:
    - install

- name: Get version of PowerDNS instance
  ansible.builtin.include_tasks: inspect.yml
  when: pdns_package_state != 'absent'
  tags:
    - db
    - mysql
    - pgsql
    - sqlite
    - config

- name: Configure SELinux
  ansible.builtin.include_tasks: selinux.yml
  when:
    - pdns_package_state != 'absent'
    - pdns_manage_selinux | bool
    - ansible_selinux is defined
    - ansible_selinux.status == 'enabled'
  tags:
    - selinux
    - config

- name: Install and configure MySQL database
  ansible.builtin.include_tasks: database-mysql.yml
  when:
    - >-
      (
        pdns_package_state != 'absent'
        and pdns_mysql_manage_database | bool
      ) or pdns_mysql_packages_state == 'absent'
    - >-
      (pdns_backends | dict2items
      | selectattr('key', 'match', '^gmysql(:.*)?$')
      | list
      | length) > 0
  tags:
    - install
    - config
    - db
    - mysql

- name: Install and configure PostgreSQL database
  ansible.builtin.include_tasks: database-pgsql.yml
  when:
    - >-
      (
        pdns_package_state != 'absent'
        and pdns_pgsql_manage_database | bool
      ) or pdns_pgsql_packages_state == 'absent'
    - >-
      (pdns_backends | dict2items
      | selectattr('key', 'match', '^gpgsql(:.*)?$')
      | list
      | length) > 0
  tags:
    - install
    - config
    - db
    - pgsql

- name: Install and configure SQlite database
  ansible.builtin.include_tasks: database-sqlite3.yml
  when:
    - >-
      (
        pdns_package_state != 'absent'
        and pdns_sqlite_databases_locations | length > 0
      ) or pdns_sqlite_package_state == 'absent'
  tags:
    - install
    - config
    - db
    - sqlite

- name: Install and configure LMDB database
  ansible.builtin.include_tasks: database-lmdb.yml
  when:
    - pdns_package_state != 'absent'
    - pdns_lmdb_databases_locations | length > 0
  tags:
    - config
    - db
    - lmdb

- name: Build config file
  ansible.builtin.include_tasks: configure.yml
  when: pdns_package_state != 'absent'
  tags:
    - config

- name: Start and enable the PowerDNS service (systemd)
  throttle: 1
  ansible.builtin.systemd:
    name: "{{ pdns_service_name }}"
    state: "{{ pdns_service_state }}"
    enabled: "{{ pdns_service_enabled }}"
    masked: "{{ pdns_service_masked }}"
  when: pdns_package_state != 'absent'
  tags:
    - service


================================================
FILE: tasks/repo-Debian.yml
================================================
---
- name: Check if Deb822 repository format is supported by the current distro
  ansible.builtin.set_fact:
    _pdns_deb822_supported: >-
      {{
        (ansible_distribution == 'Ubuntu' and (ansible_distribution_major_version | int) >= 22) or
        (ansible_distribution == 'Debian' and (ansible_distribution_major_version | int) >= 11)
      }}
  tags:
    - install
    - repository

- name: Configure the PowerDNS APT Repository (Deb822-style)
  when:
    - (pdns_install_repo['apt_version'] | default('')) | length > 0
    - _pdns_deb822_supported
  tags:
    - install
    - repository
  block:
    - name: Install python3-debian (required by deb822_repository)
      ansible.builtin.package:
        name: python3-debian
        state: present

    - name: Add the PowerDNS APT Repository (Deb822-style)
      ansible.builtin.deb822_repository:
        name: "{{ pdns_install_repo['name'] }}"
        types: deb
        uris: "https://{{ pdns_install_repo['apt_repo_origin'] }}/{{ ansible_distribution | lower }}/"
        suites: "{{ ansible_distribution_release | lower }}-{{ pdns_install_repo['apt_version'] }}"
        components: main
        architectures: "{{ pdns_apt_repo_arch }}"
        signed_by: "{{ pdns_install_repo['gpg_key'] }}"
      notify: update the apt cache

    - name: Remove the legacy PowerDNS APT .list file when using Deb822
      ansible.builtin.file:
        path: "/etc/apt/sources.list.d/{{ pdns_install_repo['name'] }}.list"
        state: absent
      notify: update the apt cache

- name: Configure the PowerDNS APT Repository (legacy format)
  when: >-
    ((pdns_install_repo['apt_version'] | default('')) | length == 0) or
    (not _pdns_deb822_supported)
  tags:
    - install
    - repository
  block:
    - name: Install gnupg
      ansible.builtin.package:
        name: gnupg
        state: present

    - name: Check if apt-key is available
      ansible.builtin.stat:
        path: /usr/bin/apt-key
      register: _pdns_apt_key_cmd

    - name: Import the PowerDNS APT Repository key from URL
      ansible.builtin.apt_key:
        url: "{{ pdns_install_repo['gpg_key'] }}"
        id: "{{ pdns_install_repo['gpg_key_id'] | default('') }}"
        state: present
      when:
        - _pdns_apt_key_cmd.stat.exists
        - pdns_install_repo['gpg_key'] is regex("^[a-z]{3,}://")

    - name: Import the PowerDNS APT Repository key from File
      ansible.builtin.apt_key:
        data: "{{ lookup('file', pdns_install_repo['gpg_key']) }}"
        id: "{{ pdns_install_repo['gpg_key_id'] | default('') }}"
        state: present
      when:
        - _pdns_apt_key_cmd.stat.exists
        - not pdns_install_repo['gpg_key'] is regex("^[a-z]{3,}://")

    - name: Import the PowerDNS APT Repository key from URL (without apt-key)
      ansible.builtin.get_url:
        url: "{{ pdns_install_repo['gpg_key'] }}"
        dest: "/etc/apt/trusted.gpg.d/{{ pdns_install_repo['name'] }}.asc"
        owner: root
        group: root
        mode: "0644"
      when:
        - not _pdns_apt_key_cmd.stat.exists
        - pdns_install_repo['gpg_key'] is regex("^[a-z]{3,}://")

    - name: Import the PowerDNS APT Repository key from File (without apt-key)
      ansible.builtin.copy:
        content: "{{ lookup('file', pdns_install_repo['gpg_key']) }}"
        dest: "/etc/apt/trusted.gpg.d/{{ pdns_install_repo['name'] }}.asc"
        owner: root
        group: root
        mode: "0644"
      when:
        - not _pdns_apt_key_cmd.stat.exists
        - not pdns_install_repo['gpg_key'] is regex("^[a-z]{3,}://")

    - name: Add the PowerDNS APT Repository (legacy format)
      ansible.builtin.apt_repository:
        filename: "{{ pdns_install_repo['name'] }}"
        repo: "{{ pdns_install_repo['apt_repo'] }}"
        state: present
      notify: update the apt cache

    - name: Remove the Deb822 PowerDNS APT .sources file when using legacy format
      ansible.builtin.file:
        path: "/etc/apt/sources.list.d/{{ pdns_install_repo['name'] }}.sources"
        state: absent
      notify: update the apt cache

- name: Pin the PowerDNS APT Repository
  ansible.builtin.template:
    src: pdns.pin.j2
    dest: /etc/apt/preferences.d/pdns
    owner: root
    group: root
    mode: "0644"
  tags:
    - install
    - repository

- name: Flush handlers
  ansible.builtin.meta: flush_handlers
  tags:
    - install
    - repository


================================================
FILE: tasks/repo-RedHat.yml
================================================
---

- name: Install EPEL repositories on RedHat-family distributions
  when: pdns_install_epel
  tags:
    - install
    - repository
  block:

    - name: Install epel-release on CentOS
      ansible.builtin.package:
        name: epel-release
        state: present
      when: ansible_distribution in [ 'CentOS', 'Rocky', 'AlmaLinux' ]

    - name: Install epel-release on RHEL
      ansible.builtin.package:
        name: "https://dl.fedoraproject.org/pub/epel/epel-release-latest-{{ ansible_distribution_major_version }}.noarch.rpm"
        state: present
      when: ansible_distribution in [ 'RedHat' ]

    - name: Install epel-release on OracleLinux
      ansible.builtin.package:
        name:
          - "oracle-epel-release-el{{ ansible_distribution_major_version }}"
        state: present

      when: ansible_distribution in [ 'OracleLinux' ]

- name: Install yum-plugin-priorities
  ansible.builtin.package:
    name: yum-plugin-priorities
    state: present
  when:
    - ansible_distribution in [ 'CentOS', 'Rocky', 'AlmaLinux' ]
    - ansible_distribution_major_version | int < 8
  tags:
    - install
    - repository

- name: Install policycoreutils-python-utils to manage an SELinux environment.
  ansible.builtin.package:
    name: policycoreutils-python-utils
    state: present
  when:
    - ansible_distribution in [ 'RedHat', 'CentOS', 'Rocky', 'AlmaLinux', 'OracleLinux' ]
    - ansible_distribution_major_version | int >= 8
  tags:
    - install
    - repository

- name: Add the PowerDNS YUM Repository
  ansible.builtin.yum_repository:
    name: "{{ pdns_install_repo['name'] }}"
    file: "{{ pdns_install_repo['name'] }}"
    description: PowerDNS Authoritative Server
    baseurl: "{{ pdns_install_repo['yum_repo_baseurl'] }}"
    gpgkey: "{{ pdns_install_repo['gpg_key'] }}"
    gpgcheck: true
    priority: "90"
    state: present
  tags:
    - install
    - repository

- name: Add the PowerDNS debug symbols YUM Repository
  ansible.builtin.yum_repository:
    name: "{{ pdns_install_repo['name'] }}-debuginfo"
    file: "{{ pdns_install_repo['name'] }}"
    description: PowerDNS Authoritative Server - debug symbols
    baseurl: "{{ pdns_install_repo['yum_debug_symbols_repo_baseurl'] }}"
    gpgkey: "{{ pdns_install_repo['gpg_key'] }}"
    gpgcheck: true
    priority: "90"
    state: present
  when: pdns_install_debug_symbols_package
  tags:
    - install
    - repository


================================================
FILE: tasks/selinux.yml
================================================
---

- name: Allow mysql connect from pdns in selinux
  ansible.posix.seboolean:
    name: pdns_can_network_connect_db
    state: true
    persistent: true
  when: >-
    (pdns_backends | dict2items
    | selectattr('key', 'search', '^g(mysql|pgsql)(:.*)?$')
    | list
    | length) > 0
  tags:
    - config

- name: Allow pdns to bind to udp high ports
  community.general.seport:
    ports: 10000-20000
    proto: udp
    setype: dns_port_t
    state: present
  tags:
    - config


================================================
FILE: templates/override-service.systemd.conf.j2
================================================
[Service]
{% for k, v in pdns_service_overrides.items() %}
{% if k == 'ExecStart' %}ExecStart=
{% elif k == 'ExecStartPre' %}ExecStartPre=
{% endif %}
{{ k }}={{ v }}
{% endfor %}


================================================
FILE: templates/pdns.conf.j2
================================================
config-dir={{ pdns_config_dir }}
setuid={{ pdns_user }}
setgid={{ pdns_group }}
{% for config_item, value in pdns_config.items() | sort() %}
{% if config_item not in ["config-dir", "launch", "setuid", "setgid"] %}
{% if value is sameas True %}
{{ config_item }}=yes
{% elif value is sameas False %}
{{ config_item }}=no
{% elif value == None %}
{{ config_item }}=
{% elif value is string %}
{{ config_item }}={{ value | string }}
{% elif value is sequence %}
{{ config_item }}={{ value | join(',') }}
{% else %}
{{ config_item }}={{ value | string }}
{% endif %}
{% endif %}
{% endfor %}

launch=

{% for backend in pdns_backends | sort() -%}
launch+={{ backend }}
{% set backend_string = backend | replace(':', '-') %}
{% for backend_item, value in pdns_backends[backend].items() | sort() -%}
{% if value is sameas True %}
{{ backend_string }}-{{ backend_item }}=yes
{% elif value is sameas False %}
{{ backend_string }}-{{ backend_item }}=no
{% elif value == None %}
{{ backend_string }}-{{ backend_item }}=
{% else %}
{{ backend_string }}-{{ backend_item }}={{ value | string }}
{% endif %}
{% endfor %}

{% endfor -%}



================================================
FILE: templates/pdns.pin.j2
================================================
Package: pdns-*
Pin: origin {{ pdns_install_repo['apt_repo_origin'] }}
Pin-Priority: 600


================================================
FILE: test-requirements.txt
================================================
ansible-lint==24.12.2
ansible-compat==24.10.0
yamllint==1.38.0
molecule-plugins[docker]==23.6.0
molecule-plugins[lint]==23.6.0
molecule==24.9.0
pytest-testinfra==10.1.1
docker==7.1.0


================================================
FILE: tox.ini
================================================
[tox]
minversion = 1.8
envlist = ansible{215,216}
skipsdist = true

[gh-actions:env]
ANSIBLE=
  2.15: ansible215
  2.16: ansible216

[testenv]
passenv = *
deps =
    -rtest-requirements.txt
    ansible215: ansible-core>2.15,<2.16
    ansible216: ansible-core>2.16,<2.17
setenv =
  PY_COLORS = 1
commands =
    {posargs:molecule test --all --destroy always}


================================================
FILE: vars/Archlinux.yml
================================================
---

# The name of the PowerDNS package
default_pdns_package_name: "powerdns"

# List of PowerDNS Backends packages. Arch ships all backends in the main package
default_pdns_backends_packages: {}

# The directory where the PowerDNS configuration is located
default_pdns_config_dir: '/etc/powerdns'

# Use MariaDB client for MySQL/MariaDB schema checks/import
default_pdns_backends_mysql_cmd: "mariadb"

# Additional MariaDB CLI arguments used for schema checks/import
default_pdns_mysql_cli_extra_args: "--skip-ssl-verify-server-cert"

# Packages to install for MySQL support
default_pdns_mysql_packages:
  - python-pymysql
  - python-cryptography
  - mariadb-clients

# Packages to install for PostgreSQL support
default_pdns_pgsql_packages:
  - python-psycopg2
  - postgresql

# Other defaults
pdns_user: powerdns
pdns_group: powerdns


================================================
FILE: vars/Debian.yml
================================================
---

# The name of the PowerDNS Authoritative Server package
default_pdns_package_name: "pdns-server"

# The name of the PowerDNS Authoritative Server debug package
default_pdns_debug_symbols_package_name: "pdns-server-dbg"

# Packages needed to install MySQL
default_pdns_mysql_packages:
  - default-mysql-client
  - python3-mysqldb
  - python3-cryptography

# The command used for MySQL/MariaDB schema checks/import
default_pdns_backends_mysql_cmd: "mariadb"
default_pdns_mysql_cli_extra_args: ""

# Packages needed to install PostgreSQL
default_pdns_pgsql_packages:
  - postgresql-client
  - python3-psycopg2

# List of PowerDNS Authoritative Server Backends packages on Debian
default_pdns_backends_packages:
  geo: pdns-backend-geo
  geoip: pdns-backend-geoip
  gmysql: pdns-backend-mysql
  gpgsql: pdns-backend-pgsql
  gsqlite3: pdns-backend-sqlite3
  ldap: pdns-backend-ldap
  lmdb: pdns-backend-lmdb
  lua: pdns-backend-lua
  mydns: pdns-backend-mydns
  pipe: pdns-backend-pipe
  remote: pdns-backend-remote
  tinydns: pdns-backend-tinydns

# The directory where the PowerDNS Authoritative Server configuration is located
default_pdns_config_dir: "/etc/powerdns"


================================================
FILE: vars/RedHat.yml
================================================
---

# The name of the PowerDNS Authoritative Server package
default_pdns_package_name: "pdns"

# Packages needed to install MySQL
default_pdns_mysql_packages:
  - mariadb
  - mariadb-server
  - mariadb-connector-c
  - python3-PyMySQL
  - python3-cryptography
  - perl-DBD-MySQL

# The command used for MySQL/MariaDB schema checks/import
default_pdns_backends_mysql_cmd: "mariadb"

# Additional MariaDB CLI arguments used for schema checks/import
default_pdns_mysql_cli_extra_args: ""

# Packages needed to install PostgreSQL
default_pdns_pgsql_packages:
  - postgresql
  - python3-psycopg2

# The name of the PowerDNS Authoritative Server debug package
default_pdns_debug_symbols_package_name: "pdns-debuginfo"

# List of PowerDNS Authoritative Server backends packages on RedHat
default_pdns_backends_packages:
  geo: pdns-backend-geo
  geoip: pdns-backend-geoip
  gmysql: pdns-backend-mysql
  gpgsql: pdns-backend-postgresql
  gsqlite3: pdns-backend-sqlite
  ldap: pdns-backend-ldap
  lmdb: pdns-backend-lmdb
  lua: pdns-backend-lua
  mydns: pdns-backend-mydns
  pipe: pdns-backend-pipe
  remote: pdns-backend-remote
  tinydns: pdns-backend-tinydns

# The directory where the PowerDNS Authoritative Server configuration is located
default_pdns_config_dir: "/etc/pdns"


================================================
FILE: vars/Ubuntu-20.yml
================================================
---

# Ubuntu 20 uses PyMySQL for Ansible MySQL modules.
default_pdns_mysql_packages:
  - default-mysql-client
  - python3-pymysql
  - python3-cryptography


================================================
FILE: vars/main.yml
================================================
---

pdns_apt_repo_arch_map:
  x86_64: amd64
  amd64: amd64
  aarch64: arm64
  arm64: arm64

pdns_apt_repo_arch: >-
  {{ pdns_apt_repo_arch_map.get((ansible_architecture | default('amd64')) | lower,
                                (ansible_architecture | default('amd64')) | lower) }}

pdns_auth_powerdns_repo_master:
  apt_repo_origin: "repo.powerdns.com"
  apt_version: "auth-master"
  apt_repo: >-
    deb [arch={{ pdns_apt_repo_arch }}] http://repo.powerdns.com/{{ ansible_distribution | lower }}
    {{ ansible_distribution_release | lower }}-auth-master main
  gpg_key: "http://repo.powerdns.com/CBC8B383-pub.asc"
  gpg_key_id: "D47975F8DAE32700A563E64FFF389421CBC8B383"
  yum_repo_baseurl: "http://repo.powerdns.com/centos/$basearch/$releasever/auth-master"
  yum_debug_symbols_repo_baseurl: "http://repo.powerdns.com/centos/$basearch/$releasever/auth-master/debug"
  name: "powerdns-auth-master"

pdns_auth_powerdns_repo_48:
  apt_repo_origin: "repo.powerdns.com"
  apt_version: "auth-48"
  apt_repo: >-
    deb [arch={{ pdns_apt_repo_arch }}] http://repo.powerdns.com/{{ ansible_distribution | lower }}
    {{ ansible_distribution_release | lower }}-auth-48 main
  gpg_key: "http://repo.powerdns.com/FD380FBB-pub.asc"
  gpg_key_id: "9FAAA5577E8FCF62093D036C1B0C6205FD380FBB"
  yum_repo_baseurl: "http://repo.powerdns.com/centos/$basearch/$releasever/auth-48"
  yum_debug_symbols_repo_baseurl: "http://repo.powerdns.com/centos/$basearch/$releasever/auth-48/debug"
  name: "powerdns-auth-48"

pdns_auth_powerdns_repo_49:
  apt_repo_origin: "repo.powerdns.com"
  apt_version: "auth-49"
  apt_repo: >-
    deb [arch={{ pdns_apt_repo_arch }}] http://repo.powerdns.com/{{ ansible_distribution | lower }}
    {{ ansible_distribution_release | lower }}-auth-49 main
  gpg_key: "http://repo.powerdns.com/FD380FBB-pub.asc"
  gpg_key_id: "9FAAA5577E8FCF62093D036C1B0C6205FD380FBB"
  yum_repo_baseurl: "http://repo.powerdns.com/centos/$basearch/$releasever/auth-49"
  yum_debug_symbols_repo_baseurl: "http://repo.powerdns.com/centos/$basearch/$releasever/auth-49/debug"
  name: "powerdns-auth-49"

pdns_auth_powerdns_repo_50:
  apt_repo_origin: "repo.powerdns.com"
  apt_version: "auth-50"
  apt_repo: >-
    deb [arch={{ pdns_apt_repo_arch }}] http://repo.powerdns.com/{{ ansible_distribution | lower }}
    {{ ansible_distribution_release | lower }}-auth-50 main
  gpg_key: "http://repo.powerdns.com/FD380FBB-pub.asc"
  gpg_key_id: "9FAAA5577E8FCF62093D036C1B0C6205FD380FBB"
  yum_repo_baseurl: "http://repo.powerdns.com/centos/$basearch/$releasever/auth-50"
  yum_debug_symbols_repo_baseurl: "http://repo.powerdns.com/centos/$basearch/$releasever/auth-50/debug"
  name: "powerdns-auth-50"

default_pdns_service_overrides:
  User: "{{ pdns_user }}"
  Group: "{{ pdns_group }}"
Download .txt
gitextract_axlon5gn/

├── .ansible-lint
├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       └── main.yml
├── .gitignore
├── .yamllint
├── CHANGELOG.md
├── LICENSE
├── README.md
├── defaults/
│   └── main.yml
├── handlers/
│   └── main.yml
├── meta/
│   └── main.yml
├── molecule/
│   ├── pdns-48/
│   │   ├── converge.yml
│   │   └── molecule.yml
│   ├── pdns-49/
│   │   ├── converge.yml
│   │   └── molecule.yml
│   ├── pdns-50/
│   │   ├── converge.yml
│   │   └── molecule.yml
│   ├── pdns-master/
│   │   ├── converge.yml
│   │   └── molecule.yml
│   ├── pdns-os-repos/
│   │   ├── converge.yml
│   │   └── molecule.yml
│   └── resources/
│       ├── Dockerfile.archlinux-systemd.j2
│       ├── Dockerfile.debian-systemd.j2
│       ├── Dockerfile.el-systemd.j2
│       ├── cleanup.yml
│       ├── create.yml
│       ├── destroy.yml
│       ├── prepare.yml
│       ├── tests/
│       │   ├── all/
│       │   │   └── test_common.py
│       │   ├── backend-bind/
│       │   │   └── test_backend_bind.py
│       │   ├── backend-lmdb/
│       │   │   └── test_backend_lmdb.py
│       │   ├── backend-mariadb/
│       │   │   └── test_backend_mariadb.py
│       │   ├── backend-mysql/
│       │   │   └── test_backend_mysql.py
│       │   ├── backend-postgresql/
│       │   │   └── test_backend_postgresql.py
│       │   ├── backend-sqlite/
│       │   │   └── test_backend_sqlite.py
│       │   ├── backend-zones/
│       │   │   └── test_backend_zones.py
│       │   ├── repo-48/
│       │   │   └── test_repo_48.py
│       │   ├── repo-49/
│       │   │   └── test_repo_49.py
│       │   ├── repo-50/
│       │   │   └── test_repo_50.py
│       │   ├── repo-master/
│       │   │   └── test_repo_master.py
│       │   ├── service-mask/
│       │   │   └── test_service_mask.py
│       │   ├── systemd-no-override/
│       │   │   └── test_override.py
│       │   └── systemd-override/
│       │       └── test_override.py
│       └── vars/
│           ├── molecule.yml
│           ├── pdns-backend-bind.yml
│           ├── pdns-backend-lmdb.yml
│           ├── pdns-backend-mariadb.yml
│           ├── pdns-backend-mysql.yml
│           ├── pdns-backend-postgresql.yml
│           ├── pdns-backend-sqlite3.yml
│           ├── pdns-common.yml
│           ├── pdns-no-overrides.yml
│           ├── pdns-os-repos.yml
│           ├── pdns-repo-48.yml
│           ├── pdns-repo-49.yml
│           ├── pdns-repo-50.yml
│           └── pdns-repo-master.yml
├── requirements.yml
├── tasks/
│   ├── configure.yml
│   ├── database-lmdb.yml
│   ├── database-mysql.yml
│   ├── database-pgsql.yml
│   ├── database-sqlite3.yml
│   ├── inspect.yml
│   ├── install.yml
│   ├── main.yml
│   ├── repo-Debian.yml
│   ├── repo-RedHat.yml
│   └── selinux.yml
├── templates/
│   ├── override-service.systemd.conf.j2
│   ├── pdns.conf.j2
│   └── pdns.pin.j2
├── test-requirements.txt
├── tox.ini
└── vars/
    ├── Archlinux.yml
    ├── Debian.yml
    ├── RedHat.yml
    ├── Ubuntu-20.yml
    └── main.yml
Download .txt
SYMBOL INDEX (72 symbols across 15 files)

FILE: molecule/resources/tests/all/test_common.py
  function test_distribution (line 7) | def test_distribution(host):
  function test_package (line 12) | def test_package(host):
  function test_service (line 28) | def test_service(host):

FILE: molecule/resources/tests/backend-bind/test_backend_bind.py
  function _pdns_config_dir (line 6) | def _pdns_config_dir(host):
  function _bind_config_file (line 12) | def _bind_config_file(host):
  function test_config (line 20) | def test_config(host):
  function test_bind_configuration_files (line 29) | def test_bind_configuration_files(host):
  function test_bind_instance_service_is_active (line 43) | def test_bind_instance_service_is_active(host):

FILE: molecule/resources/tests/backend-lmdb/test_backend_lmdb.py
  function _pdns_config_dir (line 6) | def _pdns_config_dir(host):
  function _lmdb_config_file (line 12) | def _lmdb_config_file(host):
  function test_package (line 20) | def test_package(host):
  function test_config (line 26) | def test_config(host):
  function test_lmdb_instance_service_is_active (line 35) | def test_lmdb_instance_service_is_active(host):

FILE: molecule/resources/tests/backend-mariadb/test_backend_mariadb.py
  function _pdns_config_dir (line 6) | def _pdns_config_dir(host):
  function _mariadb_config_file (line 12) | def _mariadb_config_file(host):
  function test_package (line 20) | def test_package(host):
  function test_config (line 26) | def test_config(host):

FILE: molecule/resources/tests/backend-mysql/test_backend_mysql.py
  function _pdns_config_dir (line 6) | def _pdns_config_dir(host):
  function _mysql_config_file (line 12) | def _mysql_config_file(host):
  function test_package (line 20) | def test_package(host):
  function test_config (line 26) | def test_config(host):

FILE: molecule/resources/tests/backend-postgresql/test_backend_postgresql.py
  function _pdns_config_dir (line 6) | def _pdns_config_dir(host):
  function _postgresql_config_file (line 12) | def _postgresql_config_file(host):
  function test_package (line 20) | def test_package(host):
  function test_config (line 30) | def test_config(host):
  function test_postgresql_instance_service_is_active (line 43) | def test_postgresql_instance_service_is_active(host):

FILE: molecule/resources/tests/backend-sqlite/test_backend_sqlite.py
  function _pdns_config_dir (line 6) | def _pdns_config_dir(host):
  function _sqlite_config_file (line 12) | def _sqlite_config_file(host):
  function test_package (line 20) | def test_package(host):
  function test_config (line 30) | def test_config(host):
  function test_database_exists (line 38) | def test_database_exists(host):

FILE: molecule/resources/tests/backend-zones/test_backend_zones.py
  function _normalize_zone (line 1) | def _normalize_zone(zone):
  function _dns_lookup_rcode (line 5) | def _dns_lookup_rcode(host, zone, port):
  function _pdnsutil_command (line 20) | def _pdnsutil_command(subcommand, zone=None, config_name=''):
  function test_backend_zones_are_listed (line 30) | def test_backend_zones_are_listed(host):
  function test_backend_zones_are_queryable (line 50) | def test_backend_zones_are_queryable(host):
  function test_backend_zones_dns_lookup_noerror (line 65) | def test_backend_zones_dns_lookup_noerror(host):

FILE: molecule/resources/tests/repo-48/test_repo_48.py
  function _release_major (line 7) | def _release_major(host):
  function _supports_deb822 (line 13) | def _supports_deb822(host):
  function _assert_debian_repo_layout (line 23) | def _assert_debian_repo_layout(host):
  function _repo_file (line 39) | def _repo_file(host):
  function test_repo_file (line 50) | def test_repo_file(host):
  function test_pdns_repo (line 59) | def test_pdns_repo(host):
  function test_repo_pinning_file (line 66) | def test_repo_pinning_file(host):
  function test_pdns_version (line 77) | def test_pdns_version(host):

FILE: molecule/resources/tests/repo-49/test_repo_49.py
  function _release_major (line 7) | def _release_major(host):
  function _supports_deb822 (line 13) | def _supports_deb822(host):
  function _assert_debian_repo_layout (line 23) | def _assert_debian_repo_layout(host):
  function _repo_file (line 39) | def _repo_file(host):
  function test_repo_file (line 50) | def test_repo_file(host):
  function test_pdns_repo (line 59) | def test_pdns_repo(host):
  function test_repo_pinning_file (line 66) | def test_repo_pinning_file(host):
  function test_pdns_version (line 77) | def test_pdns_version(host):

FILE: molecule/resources/tests/repo-50/test_repo_50.py
  function _release_major (line 7) | def _release_major(host):
  function _supports_deb822 (line 13) | def _supports_deb822(host):
  function _assert_debian_repo_layout (line 23) | def _assert_debian_repo_layout(host):
  function _repo_file (line 39) | def _repo_file(host):
  function test_repo_file (line 50) | def test_repo_file(host):
  function test_pdns_repo (line 59) | def test_pdns_repo(host):
  function test_repo_pinning_file (line 66) | def test_repo_pinning_file(host):
  function test_pdns_version (line 77) | def test_pdns_version(host):

FILE: molecule/resources/tests/repo-master/test_repo_master.py
  function _release_major (line 7) | def _release_major(host):
  function _supports_deb822 (line 13) | def _supports_deb822(host):
  function _assert_debian_repo_layout (line 23) | def _assert_debian_repo_layout(host):
  function _repo_file (line 39) | def _repo_file(host):
  function test_repo_file (line 50) | def test_repo_file(host):
  function test_pdns_repo (line 59) | def test_pdns_repo(host):
  function test_repo_pinning_file (line 66) | def test_repo_pinning_file(host):
  function test_pdns_version (line 77) | def test_pdns_version(host):

FILE: molecule/resources/tests/service-mask/test_service_mask.py
  function test_default_pdns_service_is_masked_and_stopped (line 1) | def test_default_pdns_service_is_masked_and_stopped(host):

FILE: molecule/resources/tests/systemd-no-override/test_override.py
  function test_systemd_override (line 1) | def test_systemd_override(host):

FILE: molecule/resources/tests/systemd-override/test_override.py
  function test_systemd_override (line 1) | def test_systemd_override(host):
Condensed preview — 79 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (198K chars).
[
  {
    "path": ".ansible-lint",
    "chars": 19,
    "preview": "---\n\nskip_list: []\n"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 435,
    "preview": "---\n# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates\n\nversion: 2"
  },
  {
    "path": ".github/workflows/main.yml",
    "chars": 1006,
    "preview": "---\non:\n  push:\n  pull_request:\n  schedule:\n    - cron: '33 5 * * 0'\n\njobs:\n  Lint:\n    runs-on: ubuntu-22.04\n    steps:"
  },
  {
    "path": ".gitignore",
    "chars": 214,
    "preview": "### Ansible ###\n*.retry\n.ansible_cache\n.ansible\n\n### Python ###\n# Byte-compiled / optimized / DLL files\n.pytest_cache/\n_"
  },
  {
    "path": ".yamllint",
    "chars": 821,
    "preview": "---\n# Based on ansible-lint config\nextends: default\n\nignore: |\n  .tox/\n  .git/\n  .venv/\n  venv/\n  .cache/\n  .pytest_cach"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 14055,
    "preview": "## v1.10.0 (2026-02-24)\n\nNEW FEATURES:\n- Add role-level package state controls for PowerDNS, debug symbols, backend pack"
  },
  {
    "path": "LICENSE",
    "chars": 1072,
    "preview": "MIT License\n\nCopyright (c) 2017 PowerDNS.COM BV\n\nPermission is hereby granted, free of charge, to any person obtaining a"
  },
  {
    "path": "README.md",
    "chars": 16366,
    "preview": "# Ansible Role: PowerDNS Authoritative Server\n\n[![Build Status](https://github.com/PowerDNS/pdns-ansible/actions/workflo"
  },
  {
    "path": "defaults/main.yml",
    "chars": 11840,
    "preview": "---\n\n# By default, no PowerDNS Authoritative Server repository will be configured by the role\npdns_install_repo: \"\"\n\n# T"
  },
  {
    "path": "handlers/main.yml",
    "chars": 538,
    "preview": "---\n- name: Reload systemd\n  ansible.builtin.systemd:\n    daemon_reload: true\n  listen: reload systemd\n  when: not pdns_"
  },
  {
    "path": "meta/main.yml",
    "chars": 602,
    "preview": "---\n\ngalaxy_info:\n  role_name: \"pdns\"\n  namespace: \"powerdns\"\n  author: PowerDNS Engineering Team\n  description: Install"
  },
  {
    "path": "molecule/pdns-48/converge.yml",
    "chars": 8776,
    "preview": "---\n\n- name: PowerDNS 4.8.x LMDB default instance\n  hosts: pdns\n  vars_files:\n    - ../resources/vars/pdns-common.yml\n  "
  },
  {
    "path": "molecule/pdns-48/molecule.yml",
    "chars": 2580,
    "preview": "---\n\nscenario:\n  name: pdns-48\n\ndriver:\n  name: docker\n\ndependency:\n  name: galaxy\n\nplatforms:\n  - name: rockylinux-8\n  "
  },
  {
    "path": "molecule/pdns-49/converge.yml",
    "chars": 8776,
    "preview": "---\n\n- name: PowerDNS 4.9.x LMDB default instance\n  hosts: pdns\n  vars_files:\n    - ../resources/vars/pdns-common.yml\n  "
  },
  {
    "path": "molecule/pdns-49/molecule.yml",
    "chars": 2782,
    "preview": "---\n\nscenario:\n  name: pdns-49\n\ndriver:\n  name: docker\n\ndependency:\n  name: galaxy\n\nplatforms:\n\n  - name: rockylinux-8\n "
  },
  {
    "path": "molecule/pdns-50/converge.yml",
    "chars": 8646,
    "preview": "---\n\n- name: PowerDNS 5.0.x LMDB default instance\n  hosts: pdns\n  vars_files:\n    - ../resources/vars/pdns-common.yml\n  "
  },
  {
    "path": "molecule/pdns-50/molecule.yml",
    "chars": 2976,
    "preview": "---\n\nscenario:\n  name: pdns-50\n\ndriver:\n  name: docker\n\ndependency:\n  name: galaxy\n\nplatforms:\n\n  - name: rockylinux-8\n "
  },
  {
    "path": "molecule/pdns-master/converge.yml",
    "chars": 8807,
    "preview": "---\n\n- name: PowerDNS Master LMDB default instance\n  hosts: pdns\n  vars_files:\n    - ../resources/vars/pdns-common.yml\n "
  },
  {
    "path": "molecule/pdns-master/molecule.yml",
    "chars": 2692,
    "preview": "---\n\nscenario:\n  name: pdns-master\n\ndriver:\n  name: docker\n\ndependency:\n  name: galaxy\n\nplatforms:\n\n  - name: rockylinux"
  },
  {
    "path": "molecule/pdns-os-repos/converge.yml",
    "chars": 8386,
    "preview": "---\n\n- name: PowerDNS LMDB default instance\n  hosts: pdns\n  vars_files:\n    - ../resources/vars/pdns-common.yml\n    - .."
  },
  {
    "path": "molecule/pdns-os-repos/molecule.yml",
    "chars": 1759,
    "preview": "---\n\nscenario:\n  name: pdns-os-repos\n\ndriver:\n  name: docker\n\ndependency:\n  name: galaxy\n\nplatforms:\n\n  - name: archlinu"
  },
  {
    "path": "molecule/resources/Dockerfile.archlinux-systemd.j2",
    "chars": 1661,
    "preview": "# Molecule managed\n\n{% set archlinux_base_image = 'menci/archlinuxarm:latest' if (item.name | lower == 'archlinux' and ("
  },
  {
    "path": "molecule/resources/Dockerfile.debian-systemd.j2",
    "chars": 1196,
    "preview": "# Molecule managed\n\nFROM {{ item.image }}\n\nENV container docker\nENV DEBIAN_FRONTEND=noninteractive\n\nRUN apt-get update -"
  },
  {
    "path": "molecule/resources/Dockerfile.el-systemd.j2",
    "chars": 1219,
    "preview": "# Molecule managed\n\nFROM {{ item.image }}\n\nENV container docker\n\nRUN dnf makecache && \\\n    dnf install -y systemd pytho"
  },
  {
    "path": "molecule/resources/cleanup.yml",
    "chars": 442,
    "preview": "---\n\n- name: Cleanup role-managed PowerDNS packages\n  hosts: pdns\n  vars:\n    pdns_package_state: absent\n    pdns_debug_"
  },
  {
    "path": "molecule/resources/create.yml",
    "chars": 4490,
    "preview": "---\n\n- name: Create\n  hosts: localhost\n  connection: local\n  gather_facts: false\n  vars_files:\n    - vars/molecule.yml\n "
  },
  {
    "path": "molecule/resources/destroy.yml",
    "chars": 414,
    "preview": "---\n\n- name: Destroy the Molecule Test Resources\n  hosts: localhost\n  connection: local\n  gather_facts: false\n  vars_fil"
  },
  {
    "path": "molecule/resources/prepare.yml",
    "chars": 2076,
    "preview": "---\n\n- name: Prepare the Molecule Test Resources\n  hosts: pdns\n  tasks:\n    # Make sure the default MySQL and SQLite\n   "
  },
  {
    "path": "molecule/resources/tests/all/test_common.py",
    "chars": 1194,
    "preview": "\ndebian_os = ['debian', 'ubuntu']\nrhel_os = ['redhat', 'centos', 'ol', 'rocky', 'almalinux']\narchlinux_os = ['arch', 'ar"
  },
  {
    "path": "molecule/resources/tests/backend-bind/test_backend_bind.py",
    "chars": 1428,
    "preview": "debian_os = ['debian', 'ubuntu']\nrhel_os = ['redhat', 'centos', 'ol', 'rocky', 'almalinux']\narchlinux_os = ['arch', 'arc"
  },
  {
    "path": "molecule/resources/tests/backend-lmdb/test_backend_lmdb.py",
    "chars": 1257,
    "preview": "debian_os = ['debian', 'ubuntu']\nrhel_os = ['redhat', 'centos', 'ol', 'rocky', 'almalinux']\narchlinux_os = ['arch', 'arc"
  },
  {
    "path": "molecule/resources/tests/backend-mariadb/test_backend_mariadb.py",
    "chars": 1188,
    "preview": "debian_os = ['debian', 'ubuntu']\nrhel_os = ['redhat', 'centos', 'ol', 'rocky', 'almalinux']\narchlinux_os = ['arch', 'arc"
  },
  {
    "path": "molecule/resources/tests/backend-mysql/test_backend_mysql.py",
    "chars": 1164,
    "preview": "debian_os = ['debian', 'ubuntu']\nrhel_os = ['redhat', 'centos', 'ol', 'rocky', 'almalinux']\narchlinux_os = ['arch', 'arc"
  },
  {
    "path": "molecule/resources/tests/backend-postgresql/test_backend_postgresql.py",
    "chars": 1492,
    "preview": "debian_os = ['debian', 'ubuntu']\nrhel_os = ['redhat', 'centos', 'ol', 'rocky', 'almalinux']\narchlinux_os = ['arch', 'arc"
  },
  {
    "path": "molecule/resources/tests/backend-sqlite/test_backend_sqlite.py",
    "chars": 1456,
    "preview": "debian_os = ['debian', 'ubuntu']\nrhel_os = ['redhat', 'centos', 'ol', 'rocky', 'almalinux']\narchlinux_os = ['arch', 'arc"
  },
  {
    "path": "molecule/resources/tests/backend-zones/test_backend_zones.py",
    "chars": 2301,
    "preview": "def _normalize_zone(zone):\n    return zone.rstrip('.')\n\n\ndef _dns_lookup_rcode(host, zone, port):\n    script = (\n       "
  },
  {
    "path": "molecule/resources/tests/repo-48/test_repo_48.py",
    "chars": 2235,
    "preview": "import re\n\ndebian_os = ['debian', 'ubuntu']\nrhel_os = ['redhat', 'centos', 'ol', 'rocky', 'almalinux']\n\n\ndef _release_ma"
  },
  {
    "path": "molecule/resources/tests/repo-49/test_repo_49.py",
    "chars": 2270,
    "preview": "import re\n\ndebian_os = ['debian', 'ubuntu']\nrhel_os = ['redhat', 'centos', 'ol', 'rocky', 'almalinux']\n\n\ndef _release_ma"
  },
  {
    "path": "molecule/resources/tests/repo-50/test_repo_50.py",
    "chars": 2270,
    "preview": "import re\n\ndebian_os = ['debian', 'ubuntu']\nrhel_os = ['redhat', 'centos', 'ol', 'rocky', 'almalinux']\n\n\ndef _release_ma"
  },
  {
    "path": "molecule/resources/tests/repo-master/test_repo_master.py",
    "chars": 2337,
    "preview": "import re\n\ndebian_os = ['debian', 'ubuntu']\nrhel_os = ['redhat', 'centos', 'ol', 'rocky', 'almalinux']\n\n\ndef _release_ma"
  },
  {
    "path": "molecule/resources/tests/service-mask/test_service_mask.py",
    "chars": 1069,
    "preview": "def test_default_pdns_service_is_masked_and_stopped(host):\n    smgr = host.ansible(\"setup\")[\"ansible_facts\"][\"ansible_se"
  },
  {
    "path": "molecule/resources/tests/systemd-no-override/test_override.py",
    "chars": 257,
    "preview": "def test_systemd_override(host):\n    smgr = host.ansible(\"setup\")[\"ansible_facts\"][\"ansible_service_mgr\"]\n    if smgr =="
  },
  {
    "path": "molecule/resources/tests/systemd-override/test_override.py",
    "chars": 591,
    "preview": "def test_systemd_override(host):\n    smgr = host.ansible(\"setup\")[\"ansible_facts\"][\"ansible_service_mgr\"]\n    if smgr =="
  },
  {
    "path": "molecule/resources/vars/molecule.yml",
    "chars": 352,
    "preview": "---\nmolecule_file: \"{{ lookup('env', 'MOLECULE_FILE') }}\"\nmolecule_ephemeral_directory: \"{{ lookup('env', 'MOLECULE_EPHE"
  },
  {
    "path": "molecule/resources/vars/pdns-backend-bind.yml",
    "chars": 906,
    "preview": "---\n\n# Bind backend profile\npdns_backends_bind:\n  bind:\n    config: \"{{ pdns_config_dir }}/named.conf\"\n\npdns_instance:\n "
  },
  {
    "path": "molecule/resources/vars/pdns-backend-lmdb.yml",
    "chars": 530,
    "preview": "---\n\n# LMDB backend profile\npdns_backends_lmdb:\n  lmdb:\n    filename: /var/lib/powerdns/pdns.lmdb\n\npdns_instance:\n  serv"
  },
  {
    "path": "molecule/resources/vars/pdns-backend-mariadb.yml",
    "chars": 1328,
    "preview": "---\n\n# MariaDB backend profile\npdns_backends_mariadb:\n  'gmysql:mariadb':\n    host: \"mariadb\"\n    dbname: \"{{ ansible_ho"
  },
  {
    "path": "molecule/resources/vars/pdns-backend-mysql.yml",
    "chars": 1412,
    "preview": "---\n\n# MySQL backend profile\npdns_backends_mysql:\n  'gmysql:mysql':\n    host: \"mysql\"\n    dbname: \"{{ ansible_hostname |"
  },
  {
    "path": "molecule/resources/vars/pdns-backend-postgresql.yml",
    "chars": 958,
    "preview": "---\n\n# PostgreSQL backend profile\npdns_backends_pgsql:\n  gpgsql:\n    host: \"postgresql\"\n    dbname: \"{{ ansible_hostname"
  },
  {
    "path": "molecule/resources/vars/pdns-backend-sqlite3.yml",
    "chars": 588,
    "preview": "---\n\n# SQLite3 backend profile\npdns_backends_sqlite3:\n  gsqlite3:\n    database: /var/lib/powerdns/pdns.sqlite3\n    dnsse"
  },
  {
    "path": "molecule/resources/vars/pdns-common.yml",
    "chars": 687,
    "preview": "---\n\n##\n# PowerDNS Configuration\n##\n\npdns_config_common:\n\n  # Turns on primary operations\n  primary: true\n\n  include-dir"
  },
  {
    "path": "molecule/resources/vars/pdns-no-overrides.yml",
    "chars": 350,
    "preview": "---\n\n##\n# PowerDNS Configuration\n##\n\npdns_config:\n\n  # Turns on primary operations\n  primary: true\n\n  # Listen Address\n "
  },
  {
    "path": "molecule/resources/vars/pdns-os-repos.yml",
    "chars": 513,
    "preview": "---\n\n##\n# PowerDNS Configuration\n##\n\n_pdns_config_base:\n\n  # Listen Address\n  local-address: \"127.0.0.1\"\n  local-port: \""
  },
  {
    "path": "molecule/resources/vars/pdns-repo-48.yml",
    "chars": 94,
    "preview": "---\n\n##\n# PowerDNS 4.8.x Repository\n##\n\npdns_install_repo: \"{{ pdns_auth_powerdns_repo_48 }}\"\n"
  },
  {
    "path": "molecule/resources/vars/pdns-repo-49.yml",
    "chars": 94,
    "preview": "---\n\n##\n# PowerDNS 4.9.x Repository\n##\n\npdns_install_repo: \"{{ pdns_auth_powerdns_repo_49 }}\"\n"
  },
  {
    "path": "molecule/resources/vars/pdns-repo-50.yml",
    "chars": 94,
    "preview": "---\n\n##\n# PowerDNS 5.0.x Repository\n##\n\npdns_install_repo: \"{{ pdns_auth_powerdns_repo_50 }}\"\n"
  },
  {
    "path": "molecule/resources/vars/pdns-repo-master.yml",
    "chars": 99,
    "preview": "---\n\n##\n# PowerDNS Master Repository\n##\n\npdns_install_repo: \"{{ pdns_auth_powerdns_repo_master }}\"\n"
  },
  {
    "path": "requirements.yml",
    "chars": 198,
    "preview": "---\ncollections:\n  - name: community.mysql\n  - name: community.postgresql\n  - name: community.general\n    version: \"<11."
  },
  {
    "path": "tasks/configure.yml",
    "chars": 3010,
    "preview": "---\n- name: Set up systemd override\n  when: ansible_service_mgr == \"systemd\"\n  tags:\n    - config\n  block:\n\n    - name: "
  },
  {
    "path": "tasks/database-lmdb.yml",
    "chars": 313,
    "preview": "---\n\n- name: Ensure that the directories containing the PowerDNS LMDB databases exist\n  ansible.builtin.file:\n    name: "
  },
  {
    "path": "tasks/database-mysql.yml",
    "chars": 6280,
    "preview": "---\n\n- name: Install the MySQL dependencies\n  ansible.builtin.package:\n    name: \"{{ pdns_mysql_packages }}\"\n    state: "
  },
  {
    "path": "tasks/database-pgsql.yml",
    "chars": 5851,
    "preview": "---\n\n- name: Install the PostgreSQL dependencies\n  ansible.builtin.package:\n    name: \"{{ pdns_pgsql_packages }}\"\n    st"
  },
  {
    "path": "tasks/database-sqlite3.yml",
    "chars": 3906,
    "preview": "---\n\n- name: Install the SQLite dependencies on RedHat\n  ansible.builtin.package:\n    name: sqlite\n    state: \"{{ pdns_s"
  },
  {
    "path": "tasks/inspect.yml",
    "chars": 1010,
    "preview": "---\n\n- name: Obtain the version of the running PowerDNS instance\n  ansible.builtin.command: pdns_server --version\n  regi"
  },
  {
    "path": "tasks/install.yml",
    "chars": 1658,
    "preview": "---\n- name: Set up version separator\n  when: pdns_package_version | length > 0\n  tags:\n    - install\n  block:\n    - name"
  },
  {
    "path": "tasks/main.yml",
    "chars": 3115,
    "preview": "---\n\n- name: Include OS-specific variables (generic to specific)\n  ansible.builtin.include_vars: \"{{ role_path }}/vars/{"
  },
  {
    "path": "tasks/repo-Debian.yml",
    "chars": 4393,
    "preview": "---\n- name: Check if Deb822 repository format is supported by the current distro\n  ansible.builtin.set_fact:\n    _pdns_d"
  },
  {
    "path": "tasks/repo-RedHat.yml",
    "chars": 2419,
    "preview": "---\n\n- name: Install EPEL repositories on RedHat-family distributions\n  when: pdns_install_epel\n  tags:\n    - install\n  "
  },
  {
    "path": "tasks/selinux.yml",
    "chars": 484,
    "preview": "---\n\n- name: Allow mysql connect from pdns in selinux\n  ansible.posix.seboolean:\n    name: pdns_can_network_connect_db\n "
  },
  {
    "path": "templates/override-service.systemd.conf.j2",
    "chars": 180,
    "preview": "[Service]\n{% for k, v in pdns_service_overrides.items() %}\n{% if k == 'ExecStart' %}ExecStart=\n{% elif k == 'ExecStartPr"
  },
  {
    "path": "templates/pdns.conf.j2",
    "chars": 1123,
    "preview": "config-dir={{ pdns_config_dir }}\nsetuid={{ pdns_user }}\nsetgid={{ pdns_group }}\n{% for config_item, value in pdns_config"
  },
  {
    "path": "templates/pdns.pin.j2",
    "chars": 89,
    "preview": "Package: pdns-*\nPin: origin {{ pdns_install_repo['apt_repo_origin'] }}\nPin-Priority: 600\n"
  },
  {
    "path": "test-requirements.txt",
    "chars": 183,
    "preview": "ansible-lint==24.12.2\nansible-compat==24.10.0\nyamllint==1.38.0\nmolecule-plugins[docker]==23.6.0\nmolecule-plugins[lint]=="
  },
  {
    "path": "tox.ini",
    "chars": 357,
    "preview": "[tox]\nminversion = 1.8\nenvlist = ansible{215,216}\nskipsdist = true\n\n[gh-actions:env]\nANSIBLE=\n  2.15: ansible215\n  2.16:"
  },
  {
    "path": "vars/Archlinux.yml",
    "chars": 837,
    "preview": "---\n\n# The name of the PowerDNS package\ndefault_pdns_package_name: \"powerdns\"\n\n# List of PowerDNS Backends packages. Arc"
  },
  {
    "path": "vars/Debian.yml",
    "chars": 1171,
    "preview": "---\n\n# The name of the PowerDNS Authoritative Server package\ndefault_pdns_package_name: \"pdns-server\"\n\n# The name of the"
  },
  {
    "path": "vars/RedHat.yml",
    "chars": 1271,
    "preview": "---\n\n# The name of the PowerDNS Authoritative Server package\ndefault_pdns_package_name: \"pdns\"\n\n# Packages needed to ins"
  },
  {
    "path": "vars/Ubuntu-20.yml",
    "chars": 156,
    "preview": "---\n\n# Ubuntu 20 uses PyMySQL for Ansible MySQL modules.\ndefault_pdns_mysql_packages:\n  - default-mysql-client\n  - pytho"
  },
  {
    "path": "vars/main.yml",
    "chars": 2776,
    "preview": "---\n\npdns_apt_repo_arch_map:\n  x86_64: amd64\n  amd64: amd64\n  aarch64: arm64\n  arm64: arm64\n\npdns_apt_repo_arch: >-\n  {{"
  }
]

About this extraction

This page contains the full source code of the PowerDNS/pdns-ansible GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 79 files (181.6 KB), approximately 51.6k tokens, and a symbol index with 72 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!