from datetime import datetime from os import listdir from os.path import isfile, join from subprocess import check_output from bundlewrap.utils.ui import io zone_path = join(repo.path, 'data', 'powerdns', 'files', 'bind-zones') nameservers = set() for rnode in sorted(repo.nodes_in_group('dns')): nameservers.add(rnode.metadata.get('powerdns/my_hostname', rnode.metadata.get('hostname'))) my_primary_servers = set() for ips in node.metadata.get('powerdns/my_primary_servers', {}).values(): my_primary_servers.update(ips) directories = { '/etc/powerdns/pdns.d': { 'purge': True, 'needs': { 'pkg_apt:pdns-server', 'pkg_apt:pdns-backend-bind', 'pkg_apt:pdns-backend-pgsql', }, 'triggers': { 'svc_systemd:pdns:restart', }, }, '/var/lib/powerdns/zones': { 'purge': True, 'needs': { 'pkg_apt:pdns-backend-bind', }, } } files = { '/etc/powerdns/pdns.conf': { 'content_type': 'mako', 'context': { 'api_key': node.metadata.get('powerdns/api_key'), 'my_hostname': node.metadata.get('powerdns/my_hostname', node.metadata.get('hostname')), 'is_secondary': node.metadata.get('powerdns/is_secondary', False), 'my_primary_servers': my_primary_servers, 'my_secondary_servers': node.metadata.get('powerdns/my_secondary_servers', set()), }, 'needs': { 'pkg_apt:pdns-server', }, 'triggers': { 'svc_systemd:pdns:restart', }, }, } svc_systemd = { 'pdns': { 'needs': { 'pkg_apt:pdns-server', 'pkg_apt:pdns-backend-bind', 'pkg_apt:pdns-backend-pgsql', }, }, } actions = { 'powerdns_reload_zones': { 'triggered': True, 'command': r'pdns_control rediscover; pdns_control reload; pdns_control notify \*', 'after': { 'svc_systemd:pdns', }, }, } if node.metadata.get('powerdns/features/bind', False): primary_zones = set() for zone in listdir(zone_path): if not isfile(join(zone_path, zone)) or zone.startswith(".") or zone.startswith("_"): continue try: output = check_output(['git', 'log', '-1', '--pretty=%ci']).decode('utf-8').strip() serial = datetime.strptime(output, '%Y-%m-%d %H:%M:%S %z').strftime('%y%m%d%H%M') except Exception as e: io.stderr(f"Error while parsing commit time for {zone} serial: {e!r}") serial = datetime.now().strftime('%y%m%d0000') primary_zones.add(zone) files[f'/var/lib/powerdns/zones/{zone}'] = { 'content_type': 'mako', 'context': { 'NAMESERVERS': '\n'.join(sorted({f'@ IN NS {ns}.' for ns in nameservers})), 'SERIAL': serial, 'metadata_records': node.metadata.get(f'powerdns/bind-zones/{zone}/records', []), }, 'source': f'bind-zones/{zone}', 'test_with': f'named-checkzone {zone} {{}}', 'triggers': { 'action:powerdns_reload_zones', }, 'needed_by': { 'svc_systemd:pdns', }, } files['/etc/powerdns/pdns.d/bind.conf'] = { 'needs': { 'pkg_apt:pdns-backend-bind', }, 'needed_by': { 'svc_systemd:pdns', }, 'triggers': { 'action:powerdns_reload_zones', }, } files['/etc/powerdns/named.conf'] = { 'content_type': 'mako', 'context': { 'zones': primary_zones, }, 'needs': { 'pkg_apt:pdns-backend-bind', }, 'needed_by': { 'svc_systemd:pdns', }, 'triggers': { 'action:powerdns_reload_zones', }, } else: files['/etc/powerdns/named.conf'] = { 'delete': True, 'needed_by': { 'svc_systemd:pdns', }, 'triggers': { 'action:powerdns_reload_zones', }, } if node.metadata.get('powerdns/features/pgsql', node.has_bundle('postgresql')): files['/etc/powerdns/pdns.d/pgsql.conf'] = { 'content_type': 'mako', 'context': { 'password': node.metadata.get('postgresql/roles/powerdns/password'), }, 'needs': { 'pkg_apt:pdns-backend-pgsql', }, 'needed_by': { 'svc_systemd:pdns', }, 'triggers': { 'action:powerdns_reload_zones', }, } actions['powerdns_load_pgsql_schema'] = { 'command': node.metadata.get('postgresql/roles/powerdns/password').format_into('PGPASSWORD={} psql -h 127.0.0.1 -d powerdns -U powerdns -w < /usr/share/pdns-backend-pgsql/schema/schema.pgsql.sql'), 'unless': r'sudo -u postgres psql -d powerdns -c "\dt" | grep domains 2>&1 >/dev/null', 'needs': { 'bundle:postgresql', 'pkg_apt:pdns-backend-pgsql', }, 'needed_by': { 'svc_systemd:pdns', }, } for hostname, ips in node.metadata.get('powerdns/my_primary_servers', {}).items(): for ip in ips: ip_name = ip.replace(':', '-') actions[f'powerdns_ensure_{ip_name}_in_autoprimaries'] = { 'command': f'psql -c "INSERT INTO supermasters (ip, nameserver, account) VALUES (\'{ip}\', \'{hostname}\', \'admin\')" powerdns', 'unless': f'test -n \"$(psql -tAqc "SELECT nameserver FROM supermasters WHERE ip = \'{ip}\'" powerdns)\"', 'triggers': { 'action:powerdns_fix_primaries', }, 'after': { 'action:powerdns_load_pgsql_schema', }, } actions[f'powerdns_ensure_{hostname}_matches_{ip_name}_in_autoprimaries'] = { 'command': f'psql -c "UPDATE supermasters SET nameserver = \'{hostname}\' WHERE ip = \'{ip}\'" powerdns', 'unless': f'bash -c "[ \"$(psql -tAqc "SELECT nameserver FROM supermasters WHERE ip = \'{ip}\'" powerdns)\" == \"{hostname}\" ]"', 'triggers': { 'action:powerdns_fix_primaries', }, 'after': { f'action:powerdns_ensure_{ip_name}_in_autoprimaries', }, } actions['powerdns_fix_primaries'] = { 'command': f'psql -c "UPDATE domains SET master = \'{", ".join(sorted(my_primary_servers))}\'" powerdns', 'triggered': True, }