Add default_ttl parameter and improve diff output to zone file format

- Add default_ttl parameter for records without explicit TTL values
- Change diff format to standard DNS zone file format for better readability
- Remove section headers and use standard zone file notation:
  * - for deleted records
  * + for added records
  * Show both old and new for changed records
- Fix newline handling in diff output (actual newlines instead of escaped strings)
- Align columns for FQDN, TTL, record type, and value
- Include TTL in diff output for all record operations
This commit is contained in:
Daniel Akulenok
2026-01-29 22:16:38 +01:00
parent 5a8e7a3384
commit cd00703a1f

View File

@@ -157,6 +157,12 @@ options:
- Prevents invalid records from being sent to the DNS server. - Prevents invalid records from being sent to the DNS server.
type: bool type: bool
default: true default: true
default_ttl:
description:
- Default TTL (Time To Live) in seconds for records that don't specify a TTL.
- This value is used when a record does not have an explicit O(zones[].records[].ttl) value.
- If not specified, the zone SOA MINIMUM value will be used as the default.
type: int
dns_server: dns_server:
description: description:
- Global DNS server to use for all zones that do not specify their own. - Global DNS server to use for all zones that do not specify their own.
@@ -381,6 +387,7 @@ class DNSZoneManager:
# State # State
self.current_zone = None self.current_zone = None
self.soa_minimum_ttl = 3600 # Default, will be updated from SOA self.soa_minimum_ttl = 3600 # Default, will be updated from SOA
self.default_ttl = module.params.get('default_ttl') # User-specified default, or None to use SOA minimum
def _resolve_server(self) -> list[str]: def _resolve_server(self) -> list[str]:
"""Resolve DNS server FQDN to IP addresses.""" """Resolve DNS server FQDN to IP addresses."""
@@ -865,69 +872,48 @@ class DNSZoneManager:
return result return result
def _format_diff(self, changes: dict) -> dict: def _format_diff(self, changes: dict) -> dict:
"""Format changes as a diff structure for diff mode with human-readable output.""" """Format changes as a diff structure for diff mode using zone file format."""
diff_content = [] diff_lines = []
# Process deletes - records being removed # Process deletes - records being removed
if changes['deletes']: for record in sorted(changes['deletes'], key=lambda r: (r['name'].to_text(), r['type'])):
diff_content.append("DELETED RECORDS:") record_name = record['name'].to_text().rstrip('.')
for record in sorted(changes['deletes'], key=lambda r: (r['name'].to_text(), r['type'])): record_type = record['type']
record_name = record['name'].to_text() ttl = record.get('ttl', self.soa_minimum_ttl)
record_type = record['type'] values = sorted(str(v) for v in record['values'])
ttl = record.get('ttl', 'default') for value in values:
values = sorted(str(v) for v in record['values']) diff_lines.append(f"-{record_name:<35} {ttl:<10} {record_type:<10} {value}")
diff_content.append(f" - {record_name:<50} {record_type:<10} TTL={ttl}")
for value in values:
diff_content.append(f" {value}")
diff_content.append("")
# Process adds - records being added # Process adds - records being added
if changes['adds']: for record in sorted(changes['adds'], key=lambda r: (r['name'].to_text(), r['type'])):
diff_content.append("ADDED RECORDS:") record_name = record['name'].to_text().rstrip('.')
for record in sorted(changes['adds'], key=lambda r: (r['name'].to_text(), r['type'])): record_type = record['type']
record_name = record['name'].to_text() ttl = record.get('ttl', self.soa_minimum_ttl)
record_type = record['type'] values = sorted(str(v) for v in record['values'])
ttl = record.get('ttl', 'default') for value in values:
values = sorted(str(v) for v in record['values']) diff_lines.append(f"+{record_name:<35} {ttl:<10} {record_type:<10} {value}")
diff_content.append(f" + {record_name:<50} {record_type:<10} TTL={ttl}")
for value in values:
diff_content.append(f" {value}")
diff_content.append("")
# Process updates - records being changed # Process updates - records being changed
if changes['updates']: for record in sorted(changes['updates'], key=lambda r: (r['name'].to_text(), r['type'])):
diff_content.append("UPDATED RECORDS:") record_name = record['name'].to_text().rstrip('.')
for record in sorted(changes['updates'], key=lambda r: (r['name'].to_text(), r['type'])): record_type = record['type']
record_name = record['name'].to_text() old_ttl = record.get('old_ttl', self.soa_minimum_ttl)
record_type = record['type'] new_ttl = record.get('new_ttl', self.soa_minimum_ttl)
old_ttl = record.get('old_ttl', 'default') old_values = sorted(str(v) for v in record['old_values'])
new_ttl = record.get('new_ttl', 'default') new_values = sorted(str(v) for v in record['new_values'])
old_values = sorted(str(v) for v in record['old_values'])
new_values = sorted(str(v) for v in record['new_values']) # Show old values with -
for value in old_values:
diff_content.append(f" ~ {record_name:<50} {record_type:<10}") diff_lines.append(f"-{record_name:<35} {old_ttl:<10} {record_type:<10} {value}")
# Show new values with +
# Show TTL change if it changed for value in new_values:
if old_ttl != new_ttl: diff_lines.append(f"+{record_name:<35} {new_ttl:<10} {record_type:<10} {value}")
diff_content.append(f" TTL: {old_ttl} -> {new_ttl}")
else:
diff_content.append(f" TTL: {old_ttl}")
# Show old values
diff_content.append(f" Before:")
for value in old_values:
diff_content.append(f" - {value}")
# Show new values
diff_content.append(f" After:")
for value in new_values:
diff_content.append(f" + {value}")
diff_content.append("")
# Format as before/after with the content as text # Return diff with content - using actual line breaks, not escaped strings
diff_output = '\n'.join(diff_lines) if diff_lines else 'No changes'
return { return {
'before': 'DNS Zone Records (Before)', 'before': diff_output,
'after': '\n'.join(diff_content) if diff_content else 'No changes' 'after': diff_output
} }
@@ -1015,6 +1001,7 @@ def main() -> None:
ignore_soa_records=dict(type='bool', default=True), ignore_soa_records=dict(type='bool', default=True),
ignore_ns_records=dict(type='bool', default=True), ignore_ns_records=dict(type='bool', default=True),
validate_records=dict(type='bool', default=True), validate_records=dict(type='bool', default=True),
default_ttl=dict(type='int'),
dns_server=dict(type='str') dns_server=dict(type='str')
), ),
supports_check_mode=True, supports_check_mode=True,