# -*- coding: utf-8 -*-
#
# Adrian Brzezinski <adrbxx@gmail.com>
# License: GPLv2+
#
# Replace (or remove) records in DNS zone for systems created (or removed) by Cobbler
#
# DNS toolkit for Python
# - python-dnspython (Debian)
# - python-dns (RH/CentOS)
import dns.query
import dns.tsigkeyring
import dns.update
import dns.resolver
import time
from cobbler.cexceptions import CX
logf = None
[docs]def nslog(msg):
"""
Log a message to the logger.
:param msg: The message to log.
"""
if logf is not None:
logf.write(msg)
[docs]def register() -> str:
"""
This method is the obligatory Cobbler registration hook.
:return: The trigger name or an empty string.
"""
if __name__ == "cobbler.modules.nsupdate_add_system_post":
return "/var/lib/cobbler/triggers/add/system/post/*"
elif __name__ == "cobbler.modules.nsupdate_delete_system_pre":
return "/var/lib/cobbler/triggers/delete/system/pre/*"
else:
return ''
[docs]def run(api, args, logger):
"""
This method executes the trigger, meaning in this case that it updates the dns configuration.
:param api: The api to read metadata from.
:param args: Metadata to log.
:param logger: The logger to audit the action with.
:return: "0" on success or a skipped task. If the task failed or problems occurred then an exception is raised.
"""
global logf
action = None
if __name__ == "cobbler.modules.nsupdate_add_system_post":
action = "replace"
elif __name__ == "cobbler.modules.nsupdate_delete_system_pre":
action = "delete"
else:
return 0
settings = api.settings()
if not settings.nsupdate_enabled:
return 0
# read our settings
if str(settings.nsupdate_log) is not None:
logf = open(str(settings.nsupdate_log), "a+")
nslog(">> starting %s %s\n" % (__name__, args))
if str(settings.nsupdate_tsig_key) is not None:
keyring = dns.tsigkeyring.from_text({
str(settings.nsupdate_tsig_key[0]): str(settings.nsupdate_tsig_key[1])
})
else:
keyring = None
if str(settings.nsupdate_tsig_algorithm) is not None:
keyring_algo = str(settings.nsupdate_tsig_algorithm)
else:
keyring_algo = "HMAC-MD5.SIG-ALG.REG.INT"
# nslog( " algo %s, key %s : %s \n" % (keyring_algo,str(settings.nsupdate_tsig_key[0]),
# str(settings.nsupdate_tsig_key[1])) )
# get information about this system
system = api.find_system(args[0])
# process all interfaces and perform dynamic update for those with --dns-name
for (name, interface) in list(system.interfaces.items()):
host = interface["dns_name"]
host_ip = interface["ip_address"]
if not system.is_management_supported(cidr_ok=False):
continue
if not host:
continue
if host.find(".") == -1:
continue
domain = ".".join(host.split(".")[1:]) # get domain from host name
host = host.split(".")[0] # strip domain
nslog("processing interface %s : %s\n" % (name, interface))
nslog("lookup for '%s' domain master nameserver... " % domain)
# get master nameserver ip address
answers = dns.resolver.query(domain + '.', dns.rdatatype.SOA)
soa_mname = answers[0].mname
soa_mname_ip = None
for rrset in answers.response.additional:
if rrset.name == soa_mname:
soa_mname_ip = str(rrset.items[0].address)
if soa_mname_ip is None:
ip = dns.resolver.query(soa_mname, "A")
for answer in ip:
soa_mname_ip = answer.to_text()
nslog("%s [%s]\n" % (soa_mname, soa_mname_ip))
nslog("%s dns record for %s.%s [%s] .. " % (action, host, domain, host_ip))
# try to update zone with new record
update = dns.update.Update(domain + '.', keyring=keyring, keyalgorithm=keyring_algo)
if action == "replace":
update.replace(host, 3600, dns.rdatatype.A, host_ip)
update.replace(host, 3600, dns.rdatatype.TXT, '"cobbler (date: %s)"' % (time.strftime("%c")))
else:
update.delete(host, dns.rdatatype.A, host_ip)
update.delete(host, dns.rdatatype.TXT)
try:
response = dns.query.tcp(update, soa_mname_ip)
rcode_txt = dns.rcode.to_text(response.rcode())
except dns.tsig.PeerBadKey:
nslog("failed (refused key)\n>> done\n")
logf.close()
raise CX("nsupdate failed, server '%s' refusing our key" % soa_mname)
nslog('response code: %s\n' % rcode_txt)
# notice user about update failure
if response.rcode() != dns.rcode.NOERROR:
nslog('>> done\n')
logf.close()
raise CX("nsupdate failed (response: %s, name: %s.%s, ip %s, name server %s)"
% (rcode_txt, host, domain, host_ip, soa_mname))
nslog('>> done\n')
logf.close()
return 0