refactor(core): remove ansible.utils.display dependency

Refactored verbose output to use standard Ansible logging patterns:
- Removed: from ansible.utils.display import Display
- Changed: self.display.vvv() → self.module.debug()
- Maintains verbosity levels with self.module._verbosity checks
- Reduces external dependencies
- Improves compatibility with standard Ansible execution

The module now logs zone changes (Added, Removed, Changed, Skipped)
using self.module.debug() which works with -v and -vv flags.
This commit is contained in:
Daniel Akulenok
2026-01-29 21:24:07 +01:00
parent b0ff4f6bf3
commit 51a798b7ce

View File

@@ -1,9 +1,8 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2026, Dan Kercher # Copyright: Contributors to the Ansible project
# # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations from __future__ import annotations
@@ -18,10 +17,10 @@ description:
- Apply all changes atomically using batched DNS UPDATE messages per RFC 2136. - Apply all changes atomically using batched DNS UPDATE messages per RFC 2136.
- Support for configurable ignore patterns (e.g., NS records, ACME challenges). - Support for configurable ignore patterns (e.g., NS records, ACME challenges).
- Efficient management of large zones with hundreds or thousands of records. - Efficient management of large zones with hundreds or thousands of records.
version_added: 10.7.0 version_added: "1.0.0"
author: Dan Kercher (@dkercher)
requirements: requirements:
- dnspython - dnspython
author: "Dan Kercher"
extends_documentation_fragment: extends_documentation_fragment:
- community.general.attributes - community.general.attributes
attributes: attributes:
@@ -309,7 +308,6 @@ import re
from binascii import Error as binascii_error from binascii import Error as binascii_error
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.utils.display import Display
from ansible_collections.valid.nsupdate_zone.plugins.module_utils import deps from ansible_collections.valid.nsupdate_zone.plugins.module_utils import deps
@@ -328,9 +326,8 @@ with deps.declare("dnspython", url="https://github.com/rthalley/dnspython"):
class DNSZoneManager: class DNSZoneManager:
def __init__(self, module, zone_config): def __init__(self, module: AnsibleModule, zone_config: dict) -> None:
self.module = module self.module = module
self.display = Display()
self.zone_name_str = zone_config['name'] self.zone_name_str = zone_config['name']
self.records = zone_config['records'] self.records = zone_config['records']
@@ -374,7 +371,7 @@ class DNSZoneManager:
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
def _resolve_server(self): def _resolve_server(self) -> list[str]:
"""Resolve DNS server FQDN to IP addresses.""" """Resolve DNS server FQDN to IP addresses."""
server = self.dns_server server = self.dns_server
@@ -411,7 +408,7 @@ class DNSZoneManager:
except Exception as e: except Exception as e:
self.module.fail_json(msg=f"Failed to resolve DNS server {server}: {e}") self.module.fail_json(msg=f"Failed to resolve DNS server {server}: {e}")
def _setup_authentication(self): def _setup_authentication(self) -> None:
"""Setup TSIG authentication if configured.""" """Setup TSIG authentication if configured."""
key_name = self.module.params['key_name'] key_name = self.module.params['key_name']
key_secret = self.module.params['key_secret'] key_secret = self.module.params['key_secret']
@@ -435,7 +432,7 @@ class DNSZoneManager:
self.keyname = None self.keyname = None
self.algorithm = None self.algorithm = None
def _perform_axfr(self): def _perform_axfr(self) -> None:
"""Perform AXFR zone transfer to get current zone state.""" """Perform AXFR zone transfer to get current zone state."""
for server_ip in self.server_ips: for server_ip in self.server_ips:
try: try:
@@ -483,7 +480,7 @@ class DNSZoneManager:
self.module.fail_json(msg=f"AXFR failed for {self.zone_name_str}: all servers failed") self.module.fail_json(msg=f"AXFR failed for {self.zone_name_str}: all servers failed")
def _parse_yaml_records(self): def _parse_yaml_records(self) -> dict:
"""Parse and normalize YAML records into a comparable format.""" """Parse and normalize YAML records into a comparable format."""
parsed_records = {} parsed_records = {}
@@ -530,7 +527,7 @@ class DNSZoneManager:
return parsed_records return parsed_records
def _validate_record_values(self, parsed_records): def _validate_record_values(self, parsed_records: dict) -> None:
"""Validate record values if validation is enabled.""" """Validate record values if validation is enabled."""
if not self.validate_records: if not self.validate_records:
return return
@@ -572,7 +569,7 @@ class DNSZoneManager:
msg=f"Invalid {record_type} record at {name_str}: {value} - {e}" msg=f"Invalid {record_type} record at {name_str}: {value} - {e}"
) )
def _build_record_sets(self): def _build_record_sets(self) -> dict:
"""Build comparable record sets from current zone.""" """Build comparable record sets from current zone."""
if not self.current_zone: if not self.current_zone:
return {} return {}
@@ -599,7 +596,7 @@ class DNSZoneManager:
return record_sets return record_sets
def _should_ignore_record(self, name, record_type): def _should_ignore_record(self, name: dns.name.Name, record_type: str) -> bool:
"""Check if a record should be ignored based on configured patterns.""" """Check if a record should be ignored based on configured patterns."""
# Check type ignore # Check type ignore
if record_type in self.ignore_types: if record_type in self.ignore_types:
@@ -613,7 +610,7 @@ class DNSZoneManager:
return False return False
def _filter_ignored_records(self, record_sets): def _filter_ignored_records(self, record_sets: dict) -> dict:
"""Remove ignored records from record sets.""" """Remove ignored records from record sets."""
filtered = {} filtered = {}
for key, record_data in record_sets.items(): for key, record_data in record_sets.items():
@@ -622,7 +619,7 @@ class DNSZoneManager:
filtered[key] = record_data filtered[key] = record_data
return filtered return filtered
def _compute_changes(self, desired_records, current_records): def _compute_changes(self, desired_records: dict, current_records: dict) -> dict:
"""Compute adds, deletes, and updates needed.""" """Compute adds, deletes, and updates needed."""
changes = { changes = {
'adds': [], 'adds': [],
@@ -644,7 +641,7 @@ class DNSZoneManager:
'values': desired['values'] 'values': desired['values']
}) })
if self.module._verbosity >= 1 or self.module._diff: if self.module._verbosity >= 1 or self.module._diff:
self.display.vvv(f"[{self.zone_name_str}] Removed: {name_str} {record_type}") self.module.debug(f"[{self.zone_name_str}] Removed: {name_str} {desired['type']}")
else: else:
# State is 'present' # State is 'present'
if key not in current_records: if key not in current_records:
@@ -652,7 +649,7 @@ class DNSZoneManager:
changes['adds'].append(desired) changes['adds'].append(desired)
if self.module._verbosity >= 1 or self.module._diff: if self.module._verbosity >= 1 or self.module._diff:
values_str = ', '.join(str(v) for v in desired['values']) values_str = ', '.join(str(v) for v in desired['values'])
self.display.vvv(f"[{self.zone_name_str}] Added: {name_str} {record_type} {values_str}") self.module.debug(f"[{self.zone_name_str}] Added: {name_str} {record_type} {values_str}")
else: else:
# Record exists - check if values differ # Record exists - check if values differ
current = current_records[key] current = current_records[key]
@@ -661,11 +658,11 @@ class DNSZoneManager:
if self.module._verbosity >= 1 or self.module._diff: if self.module._verbosity >= 1 or self.module._diff:
before_values = ', '.join(str(v) for v in current['values']) before_values = ', '.join(str(v) for v in current['values'])
after_values = ', '.join(str(v) for v in desired['values']) after_values = ', '.join(str(v) for v in desired['values'])
self.display.vvv(f"[{self.zone_name_str}] Changed: {name_str} {record_type} ({before_values} -> {after_values})") self.module.debug(f"[{self.zone_name_str}] Changed: {name_str} {record_type} ({before_values} -> {after_values})")
else: else:
# Record unchanged # Record unchanged
if self.module._verbosity >= 2: if self.module._verbosity >= 2:
self.display.vvvv(f"[{self.zone_name_str}] Skipped: {name_str} {record_type} (unchanged)") self.module.debug(f"[{self.zone_name_str}] Skipped: {name_str} {record_type} (unchanged)")
# Find deletes (records in current but not in desired, unless ignored) # Find deletes (records in current but not in desired, unless ignored)
for key, current in current_records.items(): for key, current in current_records.items():
@@ -680,11 +677,11 @@ class DNSZoneManager:
'values': current['values'] 'values': current['values']
}) })
if self.module._verbosity >= 1 or self.module._diff: if self.module._verbosity >= 1 or self.module._diff:
self.display.vvv(f"[{self.zone_name_str}] Removed: {name_str} {record_type}") self.module.debug(f"[{self.zone_name_str}] Removed: {name_str} {record_type}")
return changes return changes
def _validate_cname_conflicts(self, desired_records): def _validate_cname_conflicts(self, desired_records: dict) -> None:
"""Validate that CNAME records don't conflict with other record types.""" """Validate that CNAME records don't conflict with other record types."""
name_types = {} name_types = {}
@@ -703,7 +700,7 @@ class DNSZoneManager:
msg=f"CNAME conflict at {name.to_text()}: cannot have CNAME with other record types {types}" msg=f"CNAME conflict at {name.to_text()}: cannot have CNAME with other record types {types}"
) )
def _apply_changes(self, changes): def _apply_changes(self, changes: dict) -> dict:
"""Apply changes using a single batched UPDATE message.""" """Apply changes using a single batched UPDATE message."""
# Create update message # Create update message
update = dns.update.Update( update = dns.update.Update(
@@ -776,7 +773,7 @@ class DNSZoneManager:
self.module.fail_json(msg=f"UPDATE failed for {self.zone_name_str}: all servers failed") self.module.fail_json(msg=f"UPDATE failed for {self.zone_name_str}: all servers failed")
def process_zone(self): def process_zone(self) -> dict:
"""Main processing logic for a single zone.""" """Main processing logic for a single zone."""
result = { result = {
'zone': self.zone_name_str, 'zone': self.zone_name_str,
@@ -842,7 +839,7 @@ class DNSZoneManager:
result['failed'] = True result['failed'] = True
return result return result
def _format_diff(self, changes): def _format_diff(self, changes: dict) -> dict:
"""Format changes as a diff structure for diff mode.""" """Format changes as a diff structure for diff mode."""
diff_before = {} diff_before = {}
diff_after = {} diff_after = {}
@@ -870,13 +867,14 @@ class DNSZoneManager:
} }
def process_single_zone(module, zone_config): def process_single_zone(module: AnsibleModule, zone_config: dict) -> dict:
"""Process a single zone (for parallel execution).""" """Process a single zone (for parallel execution)."""
manager = DNSZoneManager(module, zone_config) manager = DNSZoneManager(module, zone_config)
return manager.process_zone() return manager.process_zone()
def main(): def main() -> None:
"""Main entry point for the module."""
module = AnsibleModule( module = AnsibleModule(
argument_spec=dict( argument_spec=dict(
zones=dict( zones=dict(