Reformat everything.

This commit is contained in:
Felix Fontein
2025-11-01 12:08:41 +01:00
parent 3f2213791a
commit 340ff8586d
1008 changed files with 61301 additions and 58309 deletions

View File

@@ -204,42 +204,40 @@ class RecordManager:
def __init__(self, module):
self.module = module
if module.params['key_name']:
if module.params["key_name"]:
try:
self.keyring = dns.tsigkeyring.from_text({
module.params['key_name']: module.params['key_secret']
})
self.keyring = dns.tsigkeyring.from_text({module.params["key_name"]: module.params["key_secret"]})
except TypeError:
module.fail_json(msg='Missing key_secret')
module.fail_json(msg="Missing key_secret")
except binascii_error as e:
module.fail_json(msg=f'TSIG key error: {e}')
module.fail_json(msg=f"TSIG key error: {e}")
else:
self.keyring = None
if module.params['key_algorithm'] == 'hmac-md5':
self.algorithm = 'HMAC-MD5.SIG-ALG.REG.INT'
if module.params["key_algorithm"] == "hmac-md5":
self.algorithm = "HMAC-MD5.SIG-ALG.REG.INT"
else:
self.algorithm = module.params['key_algorithm']
self.algorithm = module.params["key_algorithm"]
if module.params['zone'] is None:
if module.params['record'][-1] != '.':
self.module.fail_json(msg='record must be absolute when omitting zone parameter')
if module.params["zone"] is None:
if module.params["record"][-1] != ".":
self.module.fail_json(msg="record must be absolute when omitting zone parameter")
self.zone = self.lookup_zone()
else:
self.zone = module.params['zone']
self.zone = module.params["zone"]
if self.zone[-1] != '.':
self.zone += '.'
if self.zone[-1] != ".":
self.zone += "."
if module.params['record'][-1] != '.':
if module.params["record"][-1] != ".":
self.fqdn = f"{module.params['record']}.{self.zone}"
else:
self.fqdn = module.params['record']
self.fqdn = module.params["record"]
if self.module.params['type'].lower() == 'txt' and self.module.params['value'] is not None:
self.value = list(map(self.txt_helper, self.module.params['value']))
if self.module.params["type"].lower() == "txt" and self.module.params["value"] is not None:
self.value = list(map(self.txt_helper, self.module.params["value"]))
else:
self.value = self.module.params['value']
self.value = self.module.params["value"]
self.dns_rc = 0
@@ -249,23 +247,29 @@ class RecordManager:
return f'"{entry}"'
def lookup_zone(self):
name = dns.name.from_text(self.module.params['record'])
name = dns.name.from_text(self.module.params["record"])
while True:
query = dns.message.make_query(name, dns.rdatatype.SOA)
if self.keyring:
query.use_tsig(keyring=self.keyring, algorithm=self.algorithm)
try:
if self.module.params['protocol'] == 'tcp':
lookup = dns.query.tcp(query, self.module.params['server'], timeout=10, port=self.module.params['port'])
if self.module.params["protocol"] == "tcp":
lookup = dns.query.tcp(
query, self.module.params["server"], timeout=10, port=self.module.params["port"]
)
else:
lookup = dns.query.udp(query, self.module.params['server'], timeout=10, port=self.module.params['port'])
lookup = dns.query.udp(
query, self.module.params["server"], timeout=10, port=self.module.params["port"]
)
except (dns.tsig.PeerBadKey, dns.tsig.PeerBadSignature) as e:
self.module.fail_json(msg=f'TSIG update error ({e.__class__.__name__}): {e}')
self.module.fail_json(msg=f"TSIG update error ({e.__class__.__name__}): {e}")
except (socket_error, dns.exception.Timeout) as e:
self.module.fail_json(msg=f'DNS server error: ({e.__class__.__name__}): {e}')
self.module.fail_json(msg=f"DNS server error: ({e.__class__.__name__}): {e}")
if lookup.rcode() in [dns.rcode.SERVFAIL, dns.rcode.REFUSED]:
self.module.fail_json(msg=f"Zone lookup failure: '{self.module.params['server']}' will not "
f"respond to queries regarding '{self.module.params['record']}'.")
self.module.fail_json(
msg=f"Zone lookup failure: '{self.module.params['server']}' will not "
f"respond to queries regarding '{self.module.params['record']}'."
)
# If the response contains an Answer SOA RR whose name matches the queried name,
# this is the name of the zone in which the record needs to be inserted.
for rr in lookup.answer:
@@ -284,18 +288,22 @@ class RecordManager:
def __do_update(self, update):
response = None
try:
if self.module.params['protocol'] == 'tcp':
response = dns.query.tcp(update, self.module.params['server'], timeout=10, port=self.module.params['port'])
if self.module.params["protocol"] == "tcp":
response = dns.query.tcp(
update, self.module.params["server"], timeout=10, port=self.module.params["port"]
)
else:
response = dns.query.udp(update, self.module.params['server'], timeout=10, port=self.module.params['port'])
response = dns.query.udp(
update, self.module.params["server"], timeout=10, port=self.module.params["port"]
)
except (dns.tsig.PeerBadKey, dns.tsig.PeerBadSignature) as e:
self.module.fail_json(msg=f'TSIG update error ({e.__class__.__name__}): {e}')
self.module.fail_json(msg=f"TSIG update error ({e.__class__.__name__}): {e}")
except (socket_error, dns.exception.Timeout) as e:
self.module.fail_json(msg=f'DNS server error: ({e.__class__.__name__}): {e}')
self.module.fail_json(msg=f"DNS server error: ({e.__class__.__name__}): {e}")
return response
def create_or_update_record(self):
result = {'changed': False, 'failed': False}
result = {"changed": False, "failed": False}
exists = self.record_exists()
if exists in [0, 2]:
@@ -305,20 +313,20 @@ class RecordManager:
if exists == 0:
self.dns_rc = self.create_record()
if self.dns_rc != 0:
result['msg'] = f"Failed to create DNS record (rc: {int(self.dns_rc)})"
result["msg"] = f"Failed to create DNS record (rc: {int(self.dns_rc)})"
elif exists == 2:
self.dns_rc = self.modify_record()
if self.dns_rc != 0:
result['msg'] = f"Failed to update DNS record (rc: {int(self.dns_rc)})"
result["msg"] = f"Failed to update DNS record (rc: {int(self.dns_rc)})"
if self.dns_rc != 0:
result['failed'] = True
result["failed"] = True
else:
result['changed'] = True
result["changed"] = True
else:
result['changed'] = False
result["changed"] = False
return result
@@ -326,14 +334,11 @@ class RecordManager:
update = dns.update.Update(self.zone, keyring=self.keyring, keyalgorithm=self.algorithm)
for entry in self.value:
try:
update.add(self.module.params['record'],
self.module.params['ttl'],
self.module.params['type'],
entry)
update.add(self.module.params["record"], self.module.params["ttl"], self.module.params["type"], entry)
except AttributeError:
self.module.fail_json(msg='value needed when state=present')
self.module.fail_json(msg="value needed when state=present")
except dns.exception.SyntaxError:
self.module.fail_json(msg='Invalid/malformed value')
self.module.fail_json(msg="Invalid/malformed value")
response = self.__do_update(update)
return dns.message.Message.rcode(response)
@@ -341,52 +346,53 @@ class RecordManager:
def modify_record(self):
update = dns.update.Update(self.zone, keyring=self.keyring, keyalgorithm=self.algorithm)
if self.module.params['type'].upper() == 'NS':
if self.module.params["type"].upper() == "NS":
# When modifying a NS record, Bind9 silently refuses to delete all the NS entries for a zone:
# > 09-May-2022 18:00:50.352 client @0x7fe7dd1f9568 192.168.1.3#45458/key rndc_ddns_ansible:
# > updating zone 'lab/IN': attempt to delete all SOA or NS records ignored
# https://gitlab.isc.org/isc-projects/bind9/-/blob/v9_18/lib/ns/update.c#L3304
# Let's perform dns inserts and updates first, deletes after.
query = dns.message.make_query(self.module.params['record'], self.module.params['type'])
query = dns.message.make_query(self.module.params["record"], self.module.params["type"])
if self.keyring:
query.use_tsig(keyring=self.keyring, algorithm=self.algorithm)
try:
if self.module.params['protocol'] == 'tcp':
lookup = dns.query.tcp(query, self.module.params['server'], timeout=10, port=self.module.params['port'])
if self.module.params["protocol"] == "tcp":
lookup = dns.query.tcp(
query, self.module.params["server"], timeout=10, port=self.module.params["port"]
)
else:
lookup = dns.query.udp(query, self.module.params['server'], timeout=10, port=self.module.params['port'])
lookup = dns.query.udp(
query, self.module.params["server"], timeout=10, port=self.module.params["port"]
)
except (dns.tsig.PeerBadKey, dns.tsig.PeerBadSignature) as e:
self.module.fail_json(msg=f'TSIG update error ({e.__class__.__name__}): {e}')
self.module.fail_json(msg=f"TSIG update error ({e.__class__.__name__}): {e}")
except (socket_error, dns.exception.Timeout) as e:
self.module.fail_json(msg=f'DNS server error: ({e.__class__.__name__}): {e}')
self.module.fail_json(msg=f"DNS server error: ({e.__class__.__name__}): {e}")
lookup_result = lookup.answer[0] if lookup.answer else lookup.authority[0]
entries_to_remove = [n.to_text() for n in lookup_result.items if n.to_text() not in self.value]
else:
update.delete(self.module.params['record'], self.module.params['type'])
update.delete(self.module.params["record"], self.module.params["type"])
for entry in self.value:
try:
update.add(self.module.params['record'],
self.module.params['ttl'],
self.module.params['type'],
entry)
update.add(self.module.params["record"], self.module.params["ttl"], self.module.params["type"], entry)
except AttributeError:
self.module.fail_json(msg='value needed when state=present')
self.module.fail_json(msg="value needed when state=present")
except dns.exception.SyntaxError:
self.module.fail_json(msg='Invalid/malformed value')
self.module.fail_json(msg="Invalid/malformed value")
if self.module.params['type'].upper() == 'NS':
if self.module.params["type"].upper() == "NS":
for entry in entries_to_remove:
update.delete(self.module.params['record'], self.module.params['type'], entry)
update.delete(self.module.params["record"], self.module.params["type"], entry)
response = self.__do_update(update)
return dns.message.Message.rcode(response)
def remove_record(self):
result = {'changed': False, 'failed': False}
result = {"changed": False, "failed": False}
if self.record_exists() == 0:
return result
@@ -396,38 +402,38 @@ class RecordManager:
self.module.exit_json(changed=True)
update = dns.update.Update(self.zone, keyring=self.keyring, keyalgorithm=self.algorithm)
update.delete(self.module.params['record'], self.module.params['type'])
update.delete(self.module.params["record"], self.module.params["type"])
response = self.__do_update(update)
self.dns_rc = dns.message.Message.rcode(response)
if self.dns_rc != 0:
result['failed'] = True
result['msg'] = f"Failed to delete record (rc: {int(self.dns_rc)})"
result["failed"] = True
result["msg"] = f"Failed to delete record (rc: {int(self.dns_rc)})"
else:
result['changed'] = True
result["changed"] = True
return result
def record_exists(self):
update = dns.update.Update(self.zone, keyring=self.keyring, keyalgorithm=self.algorithm)
try:
update.present(self.module.params['record'], self.module.params['type'])
update.present(self.module.params["record"], self.module.params["type"])
except dns.rdatatype.UnknownRdatatype as e:
self.module.fail_json(msg=f'Record error: {e}')
self.module.fail_json(msg=f"Record error: {e}")
response = self.__do_update(update)
self.dns_rc = dns.message.Message.rcode(response)
if self.dns_rc == 0:
if self.module.params['state'] == 'absent':
if self.module.params["state"] == "absent":
return 1
for entry in self.value:
try:
update.present(self.module.params['record'], self.module.params['type'], entry)
update.present(self.module.params["record"], self.module.params["type"], entry)
except AttributeError:
self.module.fail_json(msg='value needed when state=present')
self.module.fail_json(msg="value needed when state=present")
except dns.exception.SyntaxError:
self.module.fail_json(msg='Invalid/malformed value')
self.module.fail_json(msg="Invalid/malformed value")
response = self.__do_update(update)
self.dns_rc = dns.message.Message.rcode(response)
if self.dns_rc == 0:
@@ -441,76 +447,85 @@ class RecordManager:
return 0
def ttl_changed(self):
query = dns.message.make_query(self.fqdn, self.module.params['type'])
query = dns.message.make_query(self.fqdn, self.module.params["type"])
if self.keyring:
query.use_tsig(keyring=self.keyring, algorithm=self.algorithm)
try:
if self.module.params['protocol'] == 'tcp':
lookup = dns.query.tcp(query, self.module.params['server'], timeout=10, port=self.module.params['port'])
if self.module.params["protocol"] == "tcp":
lookup = dns.query.tcp(query, self.module.params["server"], timeout=10, port=self.module.params["port"])
else:
lookup = dns.query.udp(query, self.module.params['server'], timeout=10, port=self.module.params['port'])
lookup = dns.query.udp(query, self.module.params["server"], timeout=10, port=self.module.params["port"])
except (dns.tsig.PeerBadKey, dns.tsig.PeerBadSignature) as e:
self.module.fail_json(msg=f'TSIG update error ({e.__class__.__name__}): {e}')
self.module.fail_json(msg=f"TSIG update error ({e.__class__.__name__}): {e}")
except (socket_error, dns.exception.Timeout) as e:
self.module.fail_json(msg=f'DNS server error: ({e.__class__.__name__}): {e}')
self.module.fail_json(msg=f"DNS server error: ({e.__class__.__name__}): {e}")
if lookup.rcode() != dns.rcode.NOERROR:
self.module.fail_json(msg='Failed to lookup TTL of existing matching record.')
self.module.fail_json(msg="Failed to lookup TTL of existing matching record.")
current_ttl = lookup.answer[0].ttl if lookup.answer else lookup.authority[0].ttl
return current_ttl != self.module.params['ttl']
return current_ttl != self.module.params["ttl"]
def main():
tsig_algs = ['HMAC-MD5.SIG-ALG.REG.INT', 'hmac-md5', 'hmac-sha1', 'hmac-sha224',
'hmac-sha256', 'hmac-sha384', 'hmac-sha512']
tsig_algs = [
"HMAC-MD5.SIG-ALG.REG.INT",
"hmac-md5",
"hmac-sha1",
"hmac-sha224",
"hmac-sha256",
"hmac-sha384",
"hmac-sha512",
]
module = AnsibleModule(
argument_spec=dict(
state=dict(default='present', choices=['present', 'absent'], type='str'),
server=dict(required=True, type='str'),
port=dict(default=53, type='int'),
key_name=dict(type='str'),
key_secret=dict(type='str', no_log=True),
key_algorithm=dict(default='hmac-md5', choices=tsig_algs, type='str'),
zone=dict(type='str'),
record=dict(required=True, type='str'),
type=dict(default='A', type='str'),
ttl=dict(default=3600, type='int'),
value=dict(type='list', elements='str'),
protocol=dict(default='tcp', choices=['tcp', 'udp'], type='str')
state=dict(default="present", choices=["present", "absent"], type="str"),
server=dict(required=True, type="str"),
port=dict(default=53, type="int"),
key_name=dict(type="str"),
key_secret=dict(type="str", no_log=True),
key_algorithm=dict(default="hmac-md5", choices=tsig_algs, type="str"),
zone=dict(type="str"),
record=dict(required=True, type="str"),
type=dict(default="A", type="str"),
ttl=dict(default=3600, type="int"),
value=dict(type="list", elements="str"),
protocol=dict(default="tcp", choices=["tcp", "udp"], type="str"),
),
supports_check_mode=True
supports_check_mode=True,
)
if not HAVE_DNSPYTHON:
module.fail_json(msg=missing_required_lib('dnspython'), exception=DNSPYTHON_IMP_ERR)
module.fail_json(msg=missing_required_lib("dnspython"), exception=DNSPYTHON_IMP_ERR)
if len(module.params["record"]) == 0:
module.fail_json(msg='record cannot be empty.')
module.fail_json(msg="record cannot be empty.")
record = RecordManager(module)
result = {}
if module.params["state"] == 'absent':
if module.params["state"] == "absent":
result = record.remove_record()
elif module.params["state"] == 'present':
elif module.params["state"] == "present":
result = record.create_or_update_record()
result['dns_rc'] = record.dns_rc
result['dns_rc_str'] = dns.rcode.to_text(record.dns_rc)
if result['failed']:
result["dns_rc"] = record.dns_rc
result["dns_rc_str"] = dns.rcode.to_text(record.dns_rc)
if result["failed"]:
module.fail_json(**result)
else:
result['record'] = dict(zone=record.zone,
record=module.params['record'],
type=module.params['type'],
ttl=module.params['ttl'],
value=record.value)
result["record"] = dict(
zone=record.zone,
record=module.params["record"],
type=module.params["type"],
ttl=module.params["ttl"],
value=record.value,
)
module.exit_json(**result)
if __name__ == '__main__':
if __name__ == "__main__":
main()