diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 7ea597f..5dd7e89 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,29 @@ Valid.Nsupdate_zone Collection Release Notes .. contents:: Topics +v1.3.4 +====== + +Release Summary +--------------- + +Enhancements to record handling, diff output, and defaults. + +New Features +------------ + +- Add default_ttl parameter to apply a consistent TTL when records omit ttl +- Extend ignore_dnssec_records to ignore CDNSKEY, CDS, and TYPE65534 +- Improve diff output to zone file format with +/- markers and per-record lines + +Bugfixes +-------- + +- Allow extra keys in zones and records by filtering input before processing +- Fix diff output newline rendering by returning lists of lines +- Use correct Ansible diff flag via module._diff +- Improve error reporting with better messages and verbose traceback output + v1.3.3 ====== @@ -18,6 +41,12 @@ New Features - Add ignore_ns_records flag (default: true) to automatically ignore NS records - Add support for extra keys in zones and records parameters via options_ignore_list +New Features +------------ + +- Add ignore_ns_records flag (default: true) to automatically ignore NS records +- Add support for extra keys in zones and records parameters via options_ignore_list + v1.3.2 ====== diff --git a/examples/filter_and_update_zones.yml b/examples/filter_and_update_zones.yml new file mode 100644 index 0000000..d7bc638 --- /dev/null +++ b/examples/filter_and_update_zones.yml @@ -0,0 +1,76 @@ +--- +# Simpler approach: Just extract the fields you need inline +# This is cleaner and easier to understand/maintain + +- name: Clean zones and update DNS + hosts: localhost + gather_facts: false + + vars: + zones_with_extras: + - name: example.com + type: internal + comment: "Main domain" + dns_server: ns1.example.com + records: + - record: 'example.com.' + type: A + value: 192.168.1.1 + ttl: 3600 + comment: "Zone apex" + + - record: www + type: A + value: 192.168.1.10 + comment: "Web server" + ttl: 300 + + tasks: + - name: Extract cleaned zones with only valid keys + set_fact: + cleaned_zones: | + {% set zones = [] %} + {% for zone in zones_with_extras %} + {% set cleaned_zone = { + 'name': zone.name, + 'records': [] + } %} + {% if zone.get('dns_server') %} + {% set _ = cleaned_zone.update({'dns_server': zone.dns_server}) %} + {% endif %} + + {% for record in zone.records %} + {% set cleaned_record = { + 'record': record.record, + 'type': record.type, + 'value': record.value + } %} + {% if record.get('ttl') %} + {% set _ = cleaned_record.update({'ttl': record.ttl}) %} + {% endif %} + {% if record.get('state') %} + {% set _ = cleaned_record.update({'state': record.state}) %} + {% endif %} + {% set _ = cleaned_zone.records.append(cleaned_record) %} + {% endfor %} + + {% set _ = zones.append(cleaned_zone) %} + {% endfor %} + {{ zones }} + + - name: Display cleaned zones + debug: + var: cleaned_zones + + - name: Update DNS zones (no validation errors expected) + valid.nsupdate_zone.nsupdate_zone: + key_name: "nsupdate" + key_secret: "{{ vault_dns_key }}" + zones: "{{ cleaned_zones }}" + register: dns_result + # Uncomment when testing: + # check_mode: yes + + - name: Show results + debug: + var: dns_result.results diff --git a/examples/filter_zones.yml b/examples/filter_zones.yml new file mode 100644 index 0000000..6876c45 --- /dev/null +++ b/examples/filter_zones.yml @@ -0,0 +1,110 @@ +--- +# Example playbook to filter zones variable to only include valid keys +# This is useful if you have extra metadata/comments in your zones definition +# and want to clean it before passing to nsupdate_zone module + +- name: Filter and rebuild zones list with valid keys only + hosts: localhost + gather_facts: false + + vars: + # Example zones with extra keys (comment, type, custom_field, etc.) + zones_with_extras: + - name: example.com + type: internal # <- extra key + comment: "Main domain" # <- extra key + custom_field: foo # <- extra key + dns_server: ns1.example.com + records: + - record: 'example.com.' + type: A + value: 192.168.1.1 + ttl: 3600 + comment: "Zone apex" # <- extra key + + - record: www + type: A + value: 192.168.1.10 + comment: "Web server" # <- extra key + ttl: 300 + + - name: example.org + type: external # <- extra key + dns_server: ns2.example.org + records: + - record: 'example.org.' + type: A + value: 192.168.2.1 + ttl: 3600 + + tasks: + - name: Display original zones (with extra keys) + debug: + var: zones_with_extras + + - name: Filter zones to valid keys only + set_fact: + cleaned_zones: "{{ zones_with_extras | map('dict2items') | map('selectattr', 'key', 'in', zone_valid_keys) | map('list') | map('items2dict') | list }}" + vars: + zone_valid_keys: + - name + - dns_server + - records + + - name: Filter records within each zone to valid keys only + set_fact: + cleaned_zones: | + [ + {%- for zone in cleaned_zones -%} + { + "name": {{ zone.name | to_json }}, + {%- if zone.get('dns_server') %} + "dns_server": {{ zone.dns_server | to_json }}, + {%- endif %} + "records": [ + {%- for record in zone.records -%} + { + "record": {{ record.record | to_json }}, + "type": {{ record.type | to_json }}, + "value": {{ record.value | to_json }} + {%- if record.get('ttl') %}, + "ttl": {{ record.ttl | to_json }} + {%- endif %} + {%- if record.get('state') %}, + "state": {{ record.state | to_json }} + {%- endif %} + }{{ "," if not loop.last }} + {%- endfor %} + ] + }{{ "," if not loop.last }} + {%- endfor %} + ] + + - name: Parse cleaned zones JSON + set_fact: + cleaned_zones: "{{ cleaned_zones | from_json }}" + + - name: Display cleaned zones (only valid keys) + debug: + var: cleaned_zones + + - name: Show the difference + debug: + msg: | + Original zone 0 keys: {{ zones_with_extras[0].keys() | list }} + Cleaned zone 0 keys: {{ cleaned_zones[0].keys() | list }} + + Original record 0 keys: {{ zones_with_extras[0].records[0].keys() | list }} + Cleaned record 0 keys: {{ cleaned_zones[0].records[0].keys() | list }} + + - name: Now you can use cleaned_zones with nsupdate_zone module + debug: + msg: | + The 'cleaned_zones' variable now contains only valid keys and can be passed + directly to the nsupdate_zone module without parameter validation errors: + + - name: Update DNS zones + valid.nsupdate_zone.nsupdate_zone: + key_name: "nsupdate" + key_secret: "your-key" + zones: "{{ cleaned_zones }}" diff --git a/galaxy.yml b/galaxy.yml index 6d4e9cd..ab073fb 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -3,7 +3,7 @@ namespace: "valid" name: "nsupdate_zone" -version: 1.3.3 +version: 1.3.4 readme: README.md authors: - Dan Kercher diff --git a/plugins/modules/nsupdate_zone.py b/plugins/modules/nsupdate_zone.py index f14ef92..77f9039 100644 --- a/plugins/modules/nsupdate_zone.py +++ b/plugins/modules/nsupdate_zone.py @@ -388,6 +388,12 @@ class DNSZoneManager: self.current_zone = None 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 _get_default_ttl(self) -> int: + """Get the effective default TTL, preferring user-specified over SOA minimum.""" + if self.default_ttl is not None: + return self.default_ttl + return self.soa_minimum_ttl def _resolve_server(self) -> list[str]: """Resolve DNS server FQDN to IP addresses.""" @@ -506,7 +512,7 @@ class DNSZoneManager: record_name = record_config['record'] record_type = record_config['type'].upper() record_values = record_config['value'] - record_ttl = record_config.get('ttl', self.soa_minimum_ttl) + record_ttl = record_config.get('ttl', self._get_default_ttl()) record_state = record_config.get('state', 'present') # Normalize record name @@ -880,7 +886,7 @@ class DNSZoneManager: for record in sorted(changes['deletes'], key=lambda r: (r['name'].to_text(), r['type'])): record_name = record['name'].to_text().rstrip('.') record_type = record['type'] - ttl = record.get('ttl', self.soa_minimum_ttl) + ttl = record.get(\'ttl\', self._get_default_ttl()) values = sorted(str(v) for v in record['values']) for value in values: before_lines.append(f"-{record_name:<34} {ttl:<10} {record_type:<10} {value}") @@ -889,7 +895,7 @@ class DNSZoneManager: for record in sorted(changes['adds'], key=lambda r: (r['name'].to_text(), r['type'])): record_name = record['name'].to_text().rstrip('.') record_type = record['type'] - ttl = record.get('ttl', self.soa_minimum_ttl) + ttl = record.get(\'ttl\', self._get_default_ttl()) values = sorted(str(v) for v in record['values']) for value in values: after_lines.append(f"+{record_name:<34} {ttl:<10} {record_type:<10} {value}") @@ -898,8 +904,8 @@ class DNSZoneManager: for record in sorted(changes['updates'], key=lambda r: (r['name'].to_text(), r['type'])): record_name = record['name'].to_text().rstrip('.') record_type = record['type'] - old_ttl = record.get('old_ttl', self.soa_minimum_ttl) - new_ttl = record.get('new_ttl', self.soa_minimum_ttl) + old_ttl = record.get(\'old_ttl\', self._get_default_ttl()) + new_ttl = record.get(\'new_ttl\', self._get_default_ttl()) old_values = sorted(str(v) for v in record['old_values']) new_values = sorted(str(v) for v in record['new_values']) diff --git a/valid-nsupdate_zone-1.3.2.tar.gz b/valid-nsupdate_zone-1.3.2.tar.gz new file mode 100644 index 0000000..71506c4 Binary files /dev/null and b/valid-nsupdate_zone-1.3.2.tar.gz differ diff --git a/valid-nsupdate_zone-1.3.3.tar.gz b/valid-nsupdate_zone-1.3.3.tar.gz new file mode 100644 index 0000000..9e53455 Binary files /dev/null and b/valid-nsupdate_zone-1.3.3.tar.gz differ