bundlewrap/bundles/powerdns/items.py

205 lines
6.5 KiB
Python

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,
}