2018-10-15 21:09:40 -04:00
#!/usr/bin/env python3
2020-02-08 19:16:19 -05:00
# DNSAggregatorInstance.py - Class implementing a DNS aggregator and run by pvcnoded
2018-10-15 21:09:40 -04:00
# Part of the Parallel Virtual Cluster (PVC) system
#
2023-12-29 11:16:59 -05:00
# Copyright (C) 2018-2024 Joshua M. Boniface <joshua@boniface.me>
2018-10-15 21:09:40 -04:00
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
2021-03-25 16:57:17 -04:00
# the Free Software Foundation, version 3.
2018-10-15 21:09:40 -04:00
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
###############################################################################
import time
2018-11-18 15:36:54 -05:00
import dns . zone
import dns . query
2019-05-20 22:40:07 -04:00
import psycopg2
2018-10-15 21:09:40 -04:00
2020-08-11 11:46:41 -04:00
from threading import Thread , Event
2021-06-01 12:17:25 -04:00
import daemon_lib . common as common
2018-10-15 21:09:40 -04:00
2020-11-07 14:45:24 -05:00
2018-10-15 21:09:40 -04:00
class DNSAggregatorInstance ( object ) :
# Initialization function
2021-05-31 19:53:29 -04:00
def __init__ ( self , config , logger ) :
2018-10-15 21:09:40 -04:00
self . config = config
self . logger = logger
2018-11-18 15:36:54 -05:00
self . dns_networks = dict ( )
self . is_active = False
2018-10-15 21:09:40 -04:00
2019-05-20 22:40:07 -04:00
self . dns_server_daemon = PowerDNSInstance ( self )
self . dns_axfr_daemon = AXFRDaemonInstance ( self )
2018-10-15 21:09:40 -04:00
2018-11-18 15:36:54 -05:00
# Start up the PowerDNS instance
def start_aggregator ( self ) :
2019-05-20 22:40:07 -04:00
# Restart the SQL connection
2018-11-18 15:36:54 -05:00
self . dns_server_daemon . start ( )
self . dns_axfr_daemon . start ( )
2019-05-20 22:40:07 -04:00
self . is_active = True
2018-10-15 21:09:40 -04:00
2018-11-18 15:36:54 -05:00
# Stop the PowerDNS instance
def stop_aggregator ( self ) :
2019-05-20 22:40:07 -04:00
self . is_active = False
2018-11-18 15:36:54 -05:00
self . dns_axfr_daemon . stop ( )
self . dns_server_daemon . stop ( )
2018-10-15 21:09:40 -04:00
2018-11-18 15:36:54 -05:00
def add_network ( self , network ) :
2019-05-20 22:40:07 -04:00
self . dns_networks [ network ] = DNSNetworkInstance ( self , network )
2018-11-18 15:36:54 -05:00
self . dns_networks [ network ] . add_network ( )
self . dns_axfr_daemon . update_networks ( self . dns_networks )
2018-10-15 21:09:40 -04:00
2018-11-18 15:36:54 -05:00
def remove_network ( self , network ) :
if self . dns_networks [ network ] :
self . dns_networks [ network ] . remove_network ( )
del self . dns_networks [ network ]
self . dns_axfr_daemon . update_networks ( self . dns_networks )
2018-10-15 21:09:40 -04:00
2020-11-07 14:45:24 -05:00
2018-11-18 15:36:54 -05:00
class PowerDNSInstance ( object ) :
# Initialization function
2019-05-20 22:40:07 -04:00
def __init__ ( self , aggregator ) :
self . aggregator = aggregator
self . config = self . aggregator . config
self . logger = self . aggregator . logger
2018-11-18 15:36:54 -05:00
self . dns_server_daemon = None
2018-10-15 21:09:40 -04:00
2018-11-18 15:36:54 -05:00
# Floating upstreams
2021-11-06 03:02:43 -04:00
self . cluster_floatingipaddr , self . cluster_cidrnetmask = self . config [
" cluster_floating_ip "
] . split ( " / " )
self . upstream_floatingipaddr , self . upstream_cidrnetmask = self . config [
" upstream_floating_ip "
] . split ( " / " )
2018-11-18 15:36:54 -05:00
def start ( self ) :
2021-11-06 03:02:43 -04:00
self . logger . out ( " Starting PowerDNS zone aggregator " , state = " i " )
2018-10-15 21:09:40 -04:00
# Define the PowerDNS config
dns_configuration = [
2020-11-07 13:11:03 -05:00
# Option # Explanation
2021-11-06 03:02:43 -04:00
" --no-config " ,
" --daemon=no " , # Start directly
" --guardian=yes " , # Use a guardian
" --disable-syslog=yes " , # Log only to stdout (which is then captured)
" --disable-axfr=no " , # Allow AXFRs
" --allow-axfr-ips=0.0.0.0/0 " , # Allow AXFRs to anywhere
" --local-address= {} , {} " . format (
self . cluster_floatingipaddr , self . upstream_floatingipaddr
) , # Listen on floating IPs
" --local-port=53 " , # On port 53
" --log-dns-details=on " , # Log details
" --loglevel=3 " , # Log info
" --master=yes " , # Enable master mode
" --slave=yes " , # Enable slave mode
" --slave-renotify=yes " , # Renotify out for our slaved zones
" --version-string=powerdns " , # Set the version string
" --default-soa-name=dns.pvc.local " , # Override dnsmasq's invalid name
" --socket-dir= {} " . format (
self . config [ " pdns_dynamic_directory " ]
) , # Standard socket directory
" --launch=gpgsql " , # Use the PostgreSQL backend
" --gpgsql-host= {} " . format (
self . config [ " pdns_postgresql_host " ]
) , # PostgreSQL instance
" --gpgsql-port= {} " . format (
self . config [ " pdns_postgresql_port " ]
) , # Default port
" --gpgsql-dbname= {} " . format (
self . config [ " pdns_postgresql_dbname " ]
) , # Database name
" --gpgsql-user= {} " . format ( self . config [ " pdns_postgresql_user " ] ) , # User name
" --gpgsql-password= {} " . format (
self . config [ " pdns_postgresql_password " ]
) , # User password
" --gpgsql-dnssec=no " , # Do DNSSEC elsewhere
2018-10-15 21:09:40 -04:00
]
# Start the pdns process in a thread
self . dns_server_daemon = common . run_os_daemon (
2021-11-06 03:02:43 -04:00
" /usr/sbin/pdns_server {} " . format ( " " . join ( dns_configuration ) ) ,
2018-10-15 21:09:40 -04:00
environment = None ,
2021-11-06 03:02:43 -04:00
logfile = " {} /pdns-aggregator.log " . format ( self . config [ " pdns_log_directory " ] ) ,
2018-10-15 21:09:40 -04:00
)
2019-05-20 22:40:07 -04:00
if self . dns_server_daemon :
2021-11-06 03:02:43 -04:00
self . logger . out ( " Successfully started PowerDNS zone aggregator " , state = " o " )
2019-06-25 22:31:04 -04:00
2018-11-18 15:36:54 -05:00
def stop ( self ) :
2018-10-15 21:09:40 -04:00
if self . dns_server_daemon :
2021-11-06 03:02:43 -04:00
self . logger . out ( " Stopping PowerDNS zone aggregator " , state = " i " )
2018-10-17 20:05:22 -04:00
# Terminate, then kill
2021-11-06 03:02:43 -04:00
self . dns_server_daemon . signal ( " term " )
2018-10-17 20:05:22 -04:00
time . sleep ( 0.2 )
2021-11-06 03:02:43 -04:00
self . dns_server_daemon . signal ( " kill " )
self . logger . out ( " Successfully stopped PowerDNS zone aggregator " , state = " o " )
2018-11-18 15:36:54 -05:00
2020-11-07 14:45:24 -05:00
2018-11-18 15:36:54 -05:00
class DNSNetworkInstance ( object ) :
# Initialization function
2019-05-20 22:40:07 -04:00
def __init__ ( self , aggregator , network ) :
self . aggregator = aggregator
self . config = self . aggregator . config
self . logger = self . aggregator . logger
self . sql_conn = None
2018-11-18 15:36:54 -05:00
self . network = network
# Add a new network to the aggregator database
def add_network ( self ) :
network_domain = self . network . domain
self . logger . out (
2021-11-06 03:02:43 -04:00
" Adding entry for client domain {} " . format ( network_domain ) ,
prefix = " DNS aggregator " ,
state = " o " ,
2018-11-18 15:36:54 -05:00
)
# Connect to the database
2019-05-20 22:40:07 -04:00
self . sql_conn = psycopg2 . connect (
" host= ' {} ' port= ' {} ' dbname= ' {} ' user= ' {} ' password= ' {} ' sslmode= ' disable ' " . format (
2021-11-06 03:02:43 -04:00
self . config [ " pdns_postgresql_host " ] ,
self . config [ " pdns_postgresql_port " ] ,
self . config [ " pdns_postgresql_dbname " ] ,
self . config [ " pdns_postgresql_user " ] ,
self . config [ " pdns_postgresql_password " ] ,
2019-05-20 22:40:07 -04:00
)
)
sql_curs = self . sql_conn . cursor ( )
2018-11-18 15:36:54 -05:00
# Try to access the domains entry
2021-11-06 03:02:43 -04:00
sql_curs . execute ( " SELECT * FROM domains WHERE name= %s " , ( network_domain , ) )
2019-05-20 22:40:07 -04:00
results = sql_curs . fetchone ( )
2018-11-18 15:36:54 -05:00
# If we got back a result, don't try to add the domain to the DB
if results :
write_domain = False
else :
write_domain = True
2019-05-20 22:40:07 -04:00
# Write the domain to the database if we're active
if self . aggregator . is_active and write_domain :
sql_curs . execute (
" INSERT INTO domains (name, type, account, notified_serial) VALUES ( %s , ' MASTER ' , ' internal ' , 0) " ,
2021-11-06 03:02:43 -04:00
( network_domain , ) ,
2018-11-18 15:36:54 -05:00
)
2019-05-20 22:40:07 -04:00
self . sql_conn . commit ( )
2018-11-18 15:36:54 -05:00
2021-11-06 03:02:43 -04:00
sql_curs . execute ( " SELECT id FROM domains WHERE name= %s " , ( network_domain , ) )
2019-05-20 22:40:07 -04:00
domain_id = sql_curs . fetchone ( )
2018-11-18 15:36:54 -05:00
2019-05-20 22:40:07 -04:00
sql_curs . execute (
2018-11-18 15:36:54 -05:00
"""
INSERT INTO records ( domain_id , name , content , type , ttl , prio ) VALUES
( % s , % s , % s , % s , % s , % s )
""" ,
2021-11-06 03:02:43 -04:00
(
domain_id ,
network_domain ,
" nsX. {d} root. {d} 1 10800 1800 86400 86400 " . format (
d = self . config [ " upstream_domain " ]
) ,
" SOA " ,
86400 ,
0 ,
) ,
2018-11-18 15:36:54 -05:00
)
2019-12-08 23:32:03 -05:00
if self . network . name_servers :
ns_servers = self . network . name_servers
else :
2021-11-06 03:02:43 -04:00
ns_servers = [ " pvc-dns. {} " . format ( self . config [ " upstream_domain " ] ) ]
2019-12-08 23:32:03 -05:00
2018-11-18 15:36:54 -05:00
for ns_server in ns_servers :
2019-05-20 22:40:07 -04:00
sql_curs . execute (
2018-11-18 15:36:54 -05:00
"""
INSERT INTO records ( domain_id , name , content , type , ttl , prio ) VALUES
( % s , % s , % s , % s , % s , % s )
""" ,
2021-11-06 03:02:43 -04:00
( domain_id , network_domain , ns_server , " NS " , 86400 , 0 ) ,
2018-11-18 15:36:54 -05:00
)
2019-06-25 22:31:04 -04:00
2019-05-20 22:40:07 -04:00
self . sql_conn . commit ( )
self . sql_conn . close ( )
self . sql_conn = None
2018-11-18 15:36:54 -05:00
# Remove a deleted network from the aggregator database
def remove_network ( self ) :
network_domain = self . network . domain
self . logger . out (
2021-11-06 03:02:43 -04:00
" Removing entry for client domain {} " . format ( network_domain ) ,
prefix = " DNS aggregator " ,
state = " o " ,
2018-11-18 15:36:54 -05:00
)
2019-05-20 22:40:07 -04:00
2018-11-18 15:36:54 -05:00
# Connect to the database
2019-05-20 22:40:07 -04:00
self . sql_conn = psycopg2 . connect (
" host= ' {} ' port= ' {} ' dbname= ' {} ' user= ' {} ' password= ' {} ' sslmode= ' disable ' " . format (
2021-11-06 03:02:43 -04:00
self . config [ " pdns_postgresql_host " ] ,
self . config [ " pdns_postgresql_port " ] ,
self . config [ " pdns_postgresql_dbname " ] ,
self . config [ " pdns_postgresql_user " ] ,
self . config [ " pdns_postgresql_password " ] ,
2019-05-20 22:40:07 -04:00
)
2018-11-18 15:36:54 -05:00
)
2019-05-20 22:40:07 -04:00
sql_curs = self . sql_conn . cursor ( )
2018-11-18 15:36:54 -05:00
2019-05-20 22:40:07 -04:00
# Get the domain ID
2021-11-06 03:02:43 -04:00
sql_curs . execute ( " SELECT id FROM domains WHERE name= %s " , ( network_domain , ) )
2019-05-20 22:40:07 -04:00
domain_id = sql_curs . fetchone ( )
# Delete the domain from the database if we're active
if self . aggregator . is_active and domain_id :
2021-11-06 03:02:43 -04:00
sql_curs . execute ( " DELETE FROM domains WHERE id= %s " , ( domain_id , ) )
sql_curs . execute ( " DELETE FROM records WHERE domain_id= %s " , ( domain_id , ) )
2018-11-18 15:36:54 -05:00
2019-05-20 22:40:07 -04:00
self . sql_conn . commit ( )
self . sql_conn . close ( )
self . sql_conn = None
2018-11-18 15:36:54 -05:00
class AXFRDaemonInstance ( object ) :
# Initialization function
2019-05-20 22:40:07 -04:00
def __init__ ( self , aggregator ) :
self . aggregator = aggregator
self . config = self . aggregator . config
self . logger = self . aggregator . logger
self . dns_networks = self . aggregator . dns_networks
2020-08-11 11:46:41 -04:00
self . thread_stopper = Event ( )
2018-11-18 15:36:54 -05:00
self . thread = None
2019-05-20 22:40:07 -04:00
self . sql_conn = None
2018-11-18 15:36:54 -05:00
def update_networks ( self , dns_networks ) :
self . dns_networks = dns_networks
def start ( self ) :
2019-05-20 22:40:07 -04:00
# Create the thread
2018-11-18 15:36:54 -05:00
self . thread_stopper . clear ( )
2020-08-11 11:46:41 -04:00
self . thread = Thread ( target = self . run , args = ( ) , kwargs = { } )
2019-05-20 22:40:07 -04:00
# Start a local instance of the SQL connection
# Trying to use the instance from the main DNS Aggregator can result in connection failures
# after the leader transitions
self . sql_conn = psycopg2 . connect (
" host= ' {} ' port= ' {} ' dbname= ' {} ' user= ' {} ' password= ' {} ' sslmode= ' disable ' " . format (
2021-11-06 03:02:43 -04:00
self . config [ " pdns_postgresql_host " ] ,
self . config [ " pdns_postgresql_port " ] ,
self . config [ " pdns_postgresql_dbname " ] ,
self . config [ " pdns_postgresql_user " ] ,
self . config [ " pdns_postgresql_password " ] ,
2019-05-20 22:40:07 -04:00
)
)
2019-06-25 22:31:04 -04:00
2019-05-20 22:40:07 -04:00
# Start the thread
2020-11-06 19:44:14 -05:00
self . thread . start ( )
2018-11-18 15:36:54 -05:00
def stop ( self ) :
self . thread_stopper . set ( )
2019-05-20 22:40:07 -04:00
if self . sql_conn :
self . sql_conn . close ( )
self . sql_conn = None
2018-11-18 15:36:54 -05:00
def run ( self ) :
# Wait for all the DNSMASQ instances to actually start
2019-05-20 22:40:07 -04:00
time . sleep ( 5 )
2018-11-18 15:36:54 -05:00
while not self . thread_stopper . is_set ( ) :
# We do this for each network
for network , instance in self . dns_networks . items ( ) :
2019-08-01 13:11:45 -04:00
# Set up our SQL cursor
2020-02-19 16:29:59 -05:00
try :
sql_curs = self . sql_conn . cursor ( )
2020-11-06 18:55:10 -05:00
except Exception :
2020-02-19 16:29:59 -05:00
time . sleep ( 0.5 )
continue
2019-08-01 13:11:45 -04:00
2018-11-18 15:36:54 -05:00
# Set up our basic variables
domain = network . domain
2021-11-06 03:02:43 -04:00
if network . ip4_gateway != " None " :
2018-11-18 15:36:54 -05:00
dnsmasq_ip = network . ip4_gateway
else :
dnsmasq_ip = network . ip6_gateway
2018-12-05 21:38:28 -05:00
#
2018-11-18 15:36:54 -05:00
# Get an AXFR from the dnsmasq instance and list of records
2018-12-05 21:38:28 -05:00
#
2018-11-18 15:36:54 -05:00
try :
2018-11-27 22:18:59 -05:00
axfr = dns . query . xfr ( dnsmasq_ip , domain , lifetime = 5.0 )
z = dns . zone . from_xfr ( axfr )
2018-11-18 15:36:54 -05:00
records_raw = [ z [ n ] . to_text ( n ) for n in z . nodes . keys ( ) ]
2018-12-05 23:54:54 -05:00
except Exception as e :
2023-12-27 12:58:36 -05:00
self . logger . out (
" {} {} ( {} ) " . format ( e , dnsmasq_ip , domain ) ,
state = " d " ,
prefix = " dns-aggregator " ,
)
2018-11-18 15:36:54 -05:00
continue
# Fix the formatting because it's useless
# reference: ['@ 600 IN SOA . . 4 1200 180 1209600 600\n@ 600 IN NS .', 'test3 600 IN A 10.1.1.203\ntest3 600 IN AAAA 2001:b23e:1113:0:5054:ff:fe5c:f131', etc.]
# We don't really care about dnsmasq's terrible SOA or NS records which are in [0]
2021-11-06 03:02:43 -04:00
string_records = " \n " . join ( records_raw [ 1 : ] )
2018-11-18 15:36:54 -05:00
# Split into individual records
records_new = list ( )
2021-11-06 03:02:43 -04:00
for element in string_records . split ( " \n " ) :
2018-11-18 15:36:54 -05:00
if element :
record = element . split ( )
# Handle space-containing data elements
if domain not in record [ 0 ] :
2021-11-06 03:02:43 -04:00
name = " {} . {} " . format ( record [ 0 ] , domain )
2018-11-18 15:36:54 -05:00
else :
name = record [ 0 ]
2021-11-06 03:02:43 -04:00
entry = " {} {} IN {} {} " . format (
name , record [ 1 ] , record [ 3 ] , " " . join ( record [ 4 : ] )
)
2018-11-18 15:36:54 -05:00
records_new . append ( entry )
2018-12-05 21:38:28 -05:00
#
2018-11-18 15:36:54 -05:00
# Get the current zone from the database
2018-12-05 21:38:28 -05:00
#
2019-08-01 13:11:45 -04:00
try :
2021-11-06 03:02:43 -04:00
sql_curs . execute ( " SELECT id FROM domains WHERE name= %s " , ( domain , ) )
2019-08-01 13:11:45 -04:00
domain_id = sql_curs . fetchone ( )
sql_curs . execute (
2021-11-06 03:02:43 -04:00
" SELECT * FROM records WHERE domain_id= %s " , ( domain_id , )
2019-08-01 13:11:45 -04:00
)
results = list ( sql_curs . fetchall ( ) )
2023-12-27 12:58:36 -05:00
self . logger . out (
" SQL query results: {} " . format ( results ) ,
state = " d " ,
prefix = " dns-aggregator " ,
)
2019-08-01 13:11:45 -04:00
except Exception as e :
2021-11-06 03:02:43 -04:00
self . logger . out (
" ERROR: Failed to obtain DNS records from database: {} " . format (
e
)
)
2018-11-18 15:36:54 -05:00
# Fix the formatting because it's useless for comparison
# reference: ((10, 28, 'testnet01.i.bonilan.net', 'SOA', 'nsX.pvc.local root.pvc.local 1 10800 1800 86400 86400', 86400, 0, None, 0, None, 1), etc.)
records_old = list ( )
records_old_ids = list ( )
2019-08-01 13:11:45 -04:00
if not results :
2023-12-27 12:58:36 -05:00
self . logger . out (
" No results found, skipping. " ,
state = " d " ,
prefix = " dns-aggregator " ,
)
2019-08-01 13:11:45 -04:00
continue
2018-11-18 15:36:54 -05:00
for record in results :
2018-12-05 23:54:54 -05:00
# Skip the non-A
2019-08-01 13:11:45 -04:00
r_id = record [ 0 ]
r_name = record [ 2 ]
r_ttl = record [ 5 ]
r_type = record [ 3 ]
r_data = record [ 4 ]
2018-11-18 15:36:54 -05:00
# Assemble a list element in the same format as the AXFR data
2021-11-06 03:02:43 -04:00
entry = " {} {} IN {} {} " . format ( r_name , r_ttl , r_type , r_data )
2023-12-27 12:58:36 -05:00
self . logger . out (
" Found record: {} " . format ( entry ) ,
state = " d " ,
prefix = " dns-aggregator " ,
)
# Skip non-A or AAAA records
if r_type != " A " and r_type != " AAAA " :
2021-11-06 03:02:43 -04:00
self . logger . out (
2023-12-27 12:58:36 -05:00
' Skipping record {} , not A or AAAA: " {} " ' . format (
entry , r_type
) ,
2021-11-06 03:02:43 -04:00
state = " d " ,
prefix = " dns-aggregator " ,
)
2019-08-01 13:11:45 -04:00
continue
2018-11-18 15:36:54 -05:00
records_old . append ( entry )
2019-08-01 13:11:45 -04:00
records_old_ids . append ( r_id )
2018-11-18 15:36:54 -05:00
records_new . sort ( )
records_old . sort ( )
2023-12-27 12:58:36 -05:00
self . logger . out (
" New: {} " . format ( records_new ) ,
state = " d " ,
prefix = " dns-aggregator " ,
)
self . logger . out (
" Old: {} " . format ( records_old ) ,
state = " d " ,
prefix = " dns-aggregator " ,
)
2019-08-01 13:11:45 -04:00
2018-11-18 15:36:54 -05:00
# Find the differences between the lists
# Basic check one: are they completely equal
if records_new != records_old :
# Get set elements
in_new = set ( records_new )
in_old = set ( records_old )
in_new_not_in_old = in_new - in_old
in_old_not_in_new = in_old - in_new
2023-12-27 12:58:36 -05:00
self . logger . out (
" New but not old: {} " . format ( in_new_not_in_old ) ,
state = " d " ,
prefix = " dns-aggregator " ,
)
self . logger . out (
" Old but not new: {} " . format ( in_old_not_in_new ) ,
state = " d " ,
prefix = " dns-aggregator " ,
)
2019-08-01 13:11:45 -04:00
2018-11-18 15:36:54 -05:00
# Go through the old list
2020-11-07 13:11:03 -05:00
remove_records = list ( ) # list of database IDs
2018-11-18 15:36:54 -05:00
for i in range ( len ( records_old ) ) :
record_id = records_old_ids [ i ]
record = records_old [ i ]
splitrecord = records_old [ i ] . split ( )
# If the record is not in the new list, remove it
if record in in_old_not_in_new :
remove_records . append ( record_id )
# Go through the new elements
for newrecord in in_new_not_in_old :
splitnewrecord = newrecord . split ( )
2018-11-18 17:29:35 -05:00
# If there's a name and type match with different content, remove the old one
2021-11-06 03:02:43 -04:00
if (
splitrecord [ 0 ] == splitnewrecord [ 0 ]
and splitrecord [ 3 ] == splitnewrecord [ 3 ]
) :
2018-11-18 15:36:54 -05:00
remove_records . append ( record_id )
changed = False
if len ( remove_records ) > 0 :
# Remove the invalid old records
for record_id in remove_records :
2023-12-27 12:58:36 -05:00
self . logger . out (
" Removing record: {} " . format ( record_id ) ,
state = " d " ,
prefix = " dns-aggregator " ,
)
2019-05-20 22:40:07 -04:00
sql_curs . execute (
2021-11-06 03:02:43 -04:00
" DELETE FROM records WHERE id= %s " , ( record_id , )
2018-11-18 15:36:54 -05:00
)
changed = True
if len ( in_new_not_in_old ) > 0 :
# Add the new records
for record in in_new_not_in_old :
# [NAME, TTL, 'IN', TYPE, DATA]
record = record . split ( )
2019-08-01 13:11:45 -04:00
r_name = record [ 0 ]
r_ttl = record [ 1 ]
r_type = record [ 3 ]
r_data = record [ 4 ]
2023-12-27 12:58:36 -05:00
self . logger . out (
" Add record: {} " . format ( name ) ,
state = " d " ,
prefix = " dns-aggregator " ,
)
2019-12-19 10:45:06 -05:00
try :
sql_curs . execute (
" INSERT INTO records (domain_id, name, ttl, type, prio, content) VALUES ( %s , %s , %s , %s , %s , %s ) " ,
2021-11-06 03:02:43 -04:00
( domain_id , r_name , r_ttl , r_type , 0 , r_data ) ,
2019-12-19 10:45:06 -05:00
)
changed = True
except psycopg2 . IntegrityError as e :
2023-12-27 12:58:36 -05:00
self . logger . out (
" Failed to add record due to {} : {} " . format (
e , name
) ,
state = " d " ,
prefix = " dns-aggregator " ,
)
2021-06-14 17:15:40 -04:00
except psycopg2 . errors . InFailedSqlTransaction as e :
2023-12-27 12:58:36 -05:00
self . logger . out (
" Failed to add record due to {} : {} " . format (
e , name
) ,
state = " d " ,
prefix = " dns-aggregator " ,
)
2018-11-18 15:36:54 -05:00
if changed :
# Increase SOA serial
2019-05-20 22:40:07 -04:00
sql_curs . execute (
" SELECT content FROM records WHERE domain_id= %s AND type= ' SOA ' " ,
2021-11-06 03:02:43 -04:00
( domain_id , ) ,
2018-11-18 15:36:54 -05:00
)
2019-05-20 22:40:07 -04:00
soa_record = list ( sql_curs . fetchone ( ) ) [ 0 ] . split ( )
2018-11-18 15:36:54 -05:00
current_serial = int ( soa_record [ 2 ] )
new_serial = current_serial + 1
soa_record [ 2 ] = str ( new_serial )
2023-12-27 12:58:36 -05:00
self . logger . out (
" Records changed; bumping SOA: {} " . format ( new_serial ) ,
state = " d " ,
prefix = " dns-aggregator " ,
)
2019-05-20 22:40:07 -04:00
sql_curs . execute (
" UPDATE records SET content= %s WHERE domain_id= %s AND type= ' SOA ' " ,
2021-11-06 03:02:43 -04:00
( " " . join ( soa_record ) , domain_id ) ,
2018-11-18 15:36:54 -05:00
)
# Commit all the previous changes
2023-12-27 12:58:36 -05:00
self . logger . out (
" Committing database changes and reloading PDNS " ,
state = " d " ,
prefix = " dns-aggregator " ,
)
2019-08-01 13:11:45 -04:00
try :
self . sql_conn . commit ( )
except Exception as e :
2021-11-06 03:02:43 -04:00
self . logger . out (
" ERROR: Failed to commit DNS aggregator changes: {} " . format (
e
) ,
state = " e " ,
)
2019-06-25 22:31:04 -04:00
2018-11-18 15:36:54 -05:00
# Reload the domain
common . run_os_command (
2021-11-06 03:02:43 -04:00
" /usr/bin/pdns_control --socket-dir= {} reload {} " . format (
self . config [ " pdns_dynamic_directory " ] , domain
2018-11-18 15:36:54 -05:00
) ,
2021-11-06 03:02:43 -04:00
background = False ,
2018-11-18 15:36:54 -05:00
)
# Wait for 10 seconds
time . sleep ( 10 )