diff --git a/plugins/modules/ipadnsrecord.py b/plugins/modules/ipadnsrecord.py index f1825e68cf3d5c93f07b30ff18b922cc92f3fcd5..002d3b1a6d3c6bfe5a2a63e6b16b23bd260c2b08 100644 --- a/plugins/modules/ipadnsrecord.py +++ b/plugins/modules/ipadnsrecord.py @@ -525,7 +525,6 @@ options: aliases: ["uri_record"] ip_address: description: IP adresses for A ar AAAA. - aliases: ["a_ip_address", "aaaa_ip_address"] required: false type: string create_reverse: @@ -890,6 +889,10 @@ _RECORD_FIELDS = [ "tlsa_rec", "txt_rec", "uri_rec" ] +# The _PART_MAP structure maps ansible-freeipa attributes to their +# FreeIPA API counterparts. The keys are also used to obtain a list +# of all supported DNS record attributes. + _PART_MAP = { 'a_ip_address': 'a_part_ip_address', 'a_create_reverse': 'a_extra_create_reverse', @@ -953,6 +956,10 @@ _PART_MAP = { "uri_weight": "uri_part_weight" } +# _RECORD_PARTS is a structure that maps the attributes that store +# the DNS record in FreeIPA API to the parts and options available +# for these records in the API. + _RECORD_PARTS = { "arecord": ["a_part_ip_address", "a_extra_create_reverse"], "aaaarecord": [ @@ -960,7 +967,7 @@ _RECORD_PARTS = { ], "a6record": ["a6_part_data"], "afsdbrecord": ['afsdb_part_subtype', 'afsdb_part_hostname'], - "cert_rec": [ + "certrecord": [ 'cert_part_type', 'cert_part_key_tag', 'cert_part_algorithm', 'cert_part_certificate_or_crl' ], @@ -1133,33 +1140,20 @@ def configure_module(): return ansible_module -def find_dnsrecord(module, dnszone, name, **records): +def find_dnsrecord(module, dnszone, name): """Find a DNS record based on its name (idnsname).""" - _args = {record: value for record, value in records.items()} - _args["all"] = True - if name != '@': - _args['idnsname'] = to_text(name) + _args = { + "all": True, + "idnsname": to_text(name), + } try: _result = api_command( - module, "dnsrecord_find", to_text(dnszone), _args) + module, "dnsrecord_show", to_text(dnszone), _args) except ipalib.errors.NotFound: return None - if len(_result["result"]) > 1 and name != '@': - module.fail_json( - msg="There is more than one dnsrecord for '%s'," - " zone '%s'" % (name, dnszone)) - else: - if len(_result["result"]) == 1: - return _result["result"][0] - else: - for _res in _result["result"]: - if 'idnsname' in _res: - for x in _res['idnsname']: - if '@' == to_text(x): - return _res - return None + return _result["result"] def check_parameters(module, state, zone_name, record): @@ -1174,10 +1168,21 @@ def check_parameters(module, state, zone_name, record): module.fail_json( msg="Record Type '%s' is not supported." % record_type) - has_record = any(record.get(rec, None) for rec in _RECORD_FIELDS) + # has_record is "True" if the playbook has set any of the full record + # attributes (*record or *_rec). + has_record = any( + (rec in record) or (("%sord" % rec) in record) + for rec in _RECORD_FIELDS + ) + # has_part_record is "True" if the playbook has set any of the + # record field attributes. has_part_record = any(record.get(rec, None) for rec in _PART_MAP) + # some attributes in the playbook may have a special meaning, + # like "ip_address", which is used for either arecord or aaaarecord, + # and has_special is true if any of these attributes is set on + # on the playbook. special_list = ['ip_address'] has_special = any(record.get(rec, None) for rec in special_list) @@ -1286,7 +1291,7 @@ def gen_args(entry): else: for field in _RECORD_FIELDS: - record_value = entry.get(field, None) + record_value = entry.get(field) or entry.get("%sord" % field) if record_value is not None: record_type = field.split('_')[0] rec = "{}record".format(record_type.lower()) @@ -1324,7 +1329,15 @@ def define_commands_for_present_state(module, zone_name, entry, res_find): name = to_text(entry['name']) args = gen_args(entry) - if res_find is None: + for record, fields in _RECORD_PARTS.items(): + part_fields = [f for f in fields if f in args] + if part_fields and record in args: + record_change_request = True + break + else: + record_change_request = False + + if res_find is None and not record_change_request: _commands.append([zone_name, 'dnsrecord_add', args]) else: # Create reverse records for existing records @@ -1346,15 +1359,19 @@ def define_commands_for_present_state(module, zone_name, entry, res_find): module.fail_json(msg="Cannot modify multiple records " "of the same type at once.") - existing = find_dnsrecord(module, zone_name, name, - **{record: args[record][0]}) - if existing is None: - module.fail_json(msg="``%s` not found." % record) + if res_find is None or record not in res_find: + module.fail_json(msg="`%s` not found." % record) else: + search_record = args[record][0] # update DNS record _args = {k: args[k] for k in part_fields if k in args} _args["idnsname"] = to_text(args["idnsname"]) - _args[record] = res_find[record] + for dnsrecord in res_find[record]: + if dnsrecord == search_record: + _args[record] = search_record + break + else: + module.fail_json(msg="`%s` not found." % record) if 'dns_ttl' in args: _args['dns_ttl'] = args['dns_ttl'] _commands.append([zone_name, 'dnsrecord_mod', _args]) @@ -1373,9 +1390,11 @@ def define_commands_for_present_state(module, zone_name, entry, res_find): if record in args: add_list = [] for value in args[record]: - existing = find_dnsrecord(module, zone_name, name, - **{record: value}) - if existing is None: + if ( + res_find is None + or record not in res_find + or value not in res_find[record] + ): add_list.append(value) if add_list: args[record] = add_list @@ -1390,7 +1409,6 @@ def define_commands_for_absent_state(module, zone_name, entry, res_find): if res_find is None: return [] - name = entry['name'] args = gen_args(entry) del_all = args.get('del_all', False) @@ -1404,11 +1422,11 @@ def define_commands_for_absent_state(module, zone_name, entry, res_find): delete_records = False for record, values in records_to_delete.items(): del_list = [] - for value in values: - existing = find_dnsrecord( - module, zone_name, name, **{record: value}) - if existing: - del_list.append(value) + if record in res_find: + for value in values: + for rec_found in res_find[record]: + if rec_found == value: + del_list.append(value) if del_list: args[record] = del_list delete_records = True diff --git a/tests/dnsrecord/test_dnsrecord.yml b/tests/dnsrecord/test_dnsrecord.yml index a0629bb306e88e584121b5463acf38c9eee2e83a..e8cac70e9a026f57bfd2ec86f051e3fc04254c7d 100644 --- a/tests/dnsrecord/test_dnsrecord.yml +++ b/tests/dnsrecord/test_dnsrecord.yml @@ -625,7 +625,7 @@ zone_name: "{{ testzone }}" name: host04 afsdb_subtype: 1 - afsdb_hostname: host04."{{ testzone }}" + afsdb_hostname: "host04.{{ testzone }}" register: result failed_when: not result.changed @@ -635,7 +635,7 @@ zone_name: "{{ testzone }}" name: host04 afsdb_subtype: 1 - afsdb_hostname: host04."{{ testzone }}" + afsdb_hostname: "host04.{{ testzone }}" register: result failed_when: result.changed @@ -645,7 +645,7 @@ zone_name: "{{ testzone }}" name: host04 afsdb_subtype: 2 - afsdb_rec: 1 host04."{{ testzone }}" + afsdb_rec: "1 host04.{{ testzone }}" register: result failed_when: not result.changed @@ -655,7 +655,7 @@ zone_name: "{{ testzone }}" name: host04 afsdb_subtype: 2 - afsdb_rec: 1 host04."{{ testzone }}" + afsdb_rec: "1 host04.{{ testzone }}" register: result failed_when: result.changed @@ -664,7 +664,7 @@ ipaadmin_password: SomeADMINpassword zone_name: "{{ testzone }}" name: host04 - afsdb_rec: 2 host04."{{ testzone }}" + afsdb_rec: "2 host04.{{ testzone }}" state: absent register: result failed_when: not result.changed @@ -674,7 +674,7 @@ ipaadmin_password: SomeADMINpassword zone_name: "{{ testzone }}" name: host04 - afsdb_rec: 2 host04."{{ testzone }}" + afsdb_rec: "2 host04.{{ testzone }}" state: absent register: result failed_when: result.changed @@ -729,7 +729,7 @@ zone_name: "{{ testzone }}" name: host04 kx_preference: 10 - kx_exchanger: keyex."{{ testzone }}" + kx_exchanger: "keyex.{{ testzone }}" register: result failed_when: not result.changed @@ -739,7 +739,7 @@ zone_name: "{{ testzone }}" name: host04 kx_preference: 10 - kx_exchanger: keyex."{{ testzone }}" + kx_exchanger: "keyex.{{ testzone }}" register: result failed_when: result.changed @@ -749,7 +749,7 @@ zone_name: "{{ testzone }}" name: host04 kx_preference: 20 - kx_rec: 10 keyex."{{ testzone }}" + kx_rec: "10 keyex.{{ testzone }}" register: result failed_when: not result.changed @@ -759,7 +759,7 @@ zone_name: "{{ testzone }}" name: host04 kx_preference: 20 - kx_rec: 10 keyex."{{ testzone }}" + kx_rec: "10 keyex.{{ testzone }}" register: result failed_when: result.changed @@ -769,7 +769,7 @@ zone_name: "{{ testzone }}" name: host04 kx_preference: 20 - kx_rec: 20 keyex."{{ testzone }}" + kx_rec: "20 keyex.{{ testzone }}" register: result failed_when: result.changed @@ -778,7 +778,7 @@ ipaadmin_password: SomeADMINpassword zone_name: "{{ testzone }}" name: host04 - kx_rec: 20 keyex."{{ testzone }}" + kx_rec: "20 keyex.{{ testzone }}" state: absent register: result failed_when: not result.changed @@ -788,7 +788,7 @@ ipaadmin_password: SomeADMINpassword zone_name: "{{ testzone }}" name: host04 - kx_rec: 20 keyex."{{ testzone }}" + kx_rec: "20 keyex.{{ testzone }}" state: absent register: result failed_when: result.changed @@ -799,7 +799,7 @@ zone_name: "{{ testzone }}" name: host04 mx_preference: 10 - mx_exchanger: mail."{{ testzone }}" + mx_exchanger: "mail.{{ testzone }}" register: result failed_when: not result.changed @@ -809,7 +809,7 @@ zone_name: "{{ testzone }}" name: host04 mx_preference: 10 - mx_exchanger: mail."{{ testzone }}" + mx_exchanger: "mail.{{ testzone }}" register: result failed_when: result.changed @@ -819,7 +819,7 @@ zone_name: "{{ testzone }}" name: host04 mx_preference: 20 - mx_rec: 10 mail."{{ testzone }}" + mx_rec: "10 mail.{{ testzone }}" register: result failed_when: not result.changed @@ -828,7 +828,7 @@ ipaadmin_password: SomeADMINpassword zone_name: "{{ testzone }}" name: host04 - mx_rec: 20 mail."{{ testzone }}" + mx_rec: "20 mail.{{ testzone }}" state: absent register: result failed_when: not result.changed @@ -838,7 +838,7 @@ ipaadmin_password: SomeADMINpassword zone_name: "{{ testzone }}" name: host04 - mx_rec: 20 mail."{{ testzone }}" + mx_rec: "20 mail.{{ testzone }}" state: absent register: result failed_when: result.changed @@ -900,7 +900,7 @@ zone_name: "{{ testzone }}" name: host04 loc_size: 1.00 - loc_rec: 52 22 23 N 4 53 32 E -2 0 10000 10 + loc_rec: 52 22 23.000 N 4 53 32.000 E -2.00 0.00 10000.00 10.00 register: result failed_when: not result.changed @@ -909,7 +909,7 @@ ipaadmin_password: SomeADMINpassword zone_name: "{{ testzone }}" name: host04 - loc_rec: 52 22 23.000 N 4 53 32.000 E -2.00 1.00 10000 10 + loc_rec: 52 22 23.000 N 4 53 32.000 E -2.00 1.00 10000.00 10.00 state: absent register: result failed_when: not result.changed @@ -919,7 +919,7 @@ ipaadmin_password: SomeADMINpassword zone_name: "{{ testzone }}" name: host04 - loc_rec: 52 22 23.000 N 4 53 32.000 E -2.00 1.00 10000 10 + loc_rec: 52 22 23.000 N 4 53 32.000 E -2.00 1.00 10000.00 10.00 state: absent register: result failed_when: result.changed @@ -992,7 +992,7 @@ srv_priority: 10 srv_weight: 10 srv_port: 5060 - srv_target: sip-server."{{ testzone }}" + srv_target: "sip-server.{{ testzone }}" register: result failed_when: not result.changed @@ -1004,7 +1004,7 @@ srv_priority: 10 srv_weight: 10 srv_port: 5060 - srv_target: sip-server."{{ testzone }}" + srv_target: "sip-server.{{ testzone }}" register: result failed_when: result.changed @@ -1016,8 +1016,8 @@ srv_priority: 4 srv_weight: 10 srv_port: 5060 - srv_target: sip-server."{{ testzone }}" - srv_rec: 10 10 5060 sip-server."{{ testzone }}" + srv_target: "sip-server.{{ testzone }}" + srv_rec: "10 10 5060 sip-server.{{ testzone }}" register: result failed_when: not result.changed @@ -1030,7 +1030,7 @@ srv_weight: 10 srv_port: 5060 srv_target: sip-server."{{ testzone }}" - srv_rec: 10 10 5060 sip-server."{{ testzone }}" + srv_rec: "10 10 5060 sip-server.{{ testzone }}" register: result failed_when: result.changed @@ -1042,7 +1042,7 @@ srv_priority: 2 srv_weight: 20 srv_port: 5060 - srv_target: sip-server."{{ testzone }}" + srv_target: "sip-server.{{ testzone }}" register: result failed_when: not result.changed @@ -1054,7 +1054,7 @@ srv_priority: 2 srv_weight: 20 srv_port: 5060 - srv_target: sip-server."{{ testzone }}" + srv_target: "sip-server.{{ testzone }}" register: result failed_when: result.changed @@ -1063,7 +1063,7 @@ ipaadmin_password: SomeADMINpassword zone_name: "{{ testzone }}" name: _sip._udp - srv_record: 2 20 5060 sip-server."{{ testzone }}" + srv_record: "2 20 5060 sip-server.{{ testzone }}" state: absent register: result failed_when: not result.changed @@ -1073,7 +1073,7 @@ ipaadmin_password: SomeADMINpassword zone_name: "{{ testzone }}" name: _sip._udp - srv_record: 2 20 5060 sip-server."{{ testzone }}" + srv_record: "2 20 5060 sip-server.{{ testzone }}" state: absent register: result failed_when: result.changed @@ -1284,7 +1284,7 @@ name: _ftp._tcp uri_priority: 10 uri_weight: 1 - uri_target: ftp://ftp.host04."{{ testzone }}"/public + uri_target: ftp://ftp.host04.{{ testzone }}/public register: result failed_when: not result.changed @@ -1295,7 +1295,7 @@ name: _ftp._tcp uri_priority: 10 uri_weight: 1 - uri_target: ftp://ftp.host04."{{ testzone }}"/public + uri_target: ftp://ftp.host04.{{ testzone }}/public register: result failed_when: result.changed @@ -1306,13 +1306,13 @@ name: _ftp._tcp uri_priority: 5 uri_weight: 3 - uri_rec: 10 1 ftp://ftp.host04."{{ testzone }}"/public + uri_rec: 10 1 "ftp://ftp.host04.{{ testzone }}/public" register: result failed_when: not result.changed - name: Verify if modification worked. ipadnsrecord: - uri_rec: 10 1 ftp://ftp.host04."{{ testzone }}"/public + uri_rec: 10 1 ftp://ftp.host04.{{ testzone }}/public state: absent register: result failed_when: result.changed @@ -1325,7 +1325,7 @@ name: _ftp._tcp uri_priority: 5 uri_weight: 3 - uri_rec: 5 3 ftp://ftp.host04."{{ testzone }}"/public + uri_rec: 5 3 "ftp://ftp.host04.{{ testzone }}/public" register: result failed_when: result.changed @@ -1334,7 +1334,7 @@ ipaadmin_password: SomeADMINpassword zone_name: "{{ testzone }}" name: _ftp._tcp - uri_rec: 5 3 ftp://ftp.host04."{{ testzone }}"/public + uri_rec: 5 3 "ftp://ftp.host04.{{ testzone }}/public" state: absent register: result failed_when: not result.changed @@ -1344,7 +1344,7 @@ ipaadmin_password: SomeADMINpassword zone_name: "{{ testzone }}" name: _ftp._tcp - uri_rec: 5 3 ftp://ftp.host04."{{ testzone }}"/public + uri_rec: 5 3 "ftp://ftp.host04.{{ testzone }}/public" state: absent register: result failed_when: result.changed diff --git a/tests/dnsrecord/test_dnsrecord_modify_record.yml b/tests/dnsrecord/test_dnsrecord_modify_record.yml new file mode 100644 index 0000000000000000000000000000000000000000..6ffd583b8552f3805b7860b3aeb0e70fb8383595 --- /dev/null +++ b/tests/dnsrecord/test_dnsrecord_modify_record.yml @@ -0,0 +1,180 @@ +--- +- name: Playbook to ensure + hosts: ipaserver + become: no + gather_facts: yes + + tasks: + - name: Setup testing environment. + include_tasks: env_setup.yml + + - name: Add test host. + ipahost: + ipaadmin_password: SomeADMINpassword + name: "iron01.{{ safezone }}" + ip_address: 192.168.1.253 + force: yes + + - name: Cleanup test records. + ipadnsrecord: + ipaadmin_password: SomeADMINpassword + zone_name: safezone.test + records: + - name: iron01 + ns_rec: iron01 + ds_record: + - 1234 3 3 84763786e4213cca9a6938dba5dacd64f87ec216 + - 1234 3 3 84763786e4213cca9a6938dba5dacd64f87ec222 + - 5555 5 5 84763786e4213cca9a6938dba5dacd64f87ec222 + cert_record: + - 1 1234 3 AwIBAgIUb14+Oug2nPMIIBdTCCAR+g + - 2 567 4 AwIBAgIUb14+Oug2nPMIIBdTCCAR+g + state: absent + + - name: Add NS records to test. + ipadnsrecord: + ipaadmin_password: SomeADMINpassword + zone_name: safezone.test + records: + - name: iron01 + ns_rec: iron01 + register: result + failed_when: result.failed or not result.changed + + - name: Add DS records to test. + ipadnsrecord: + ipaadmin_password: SomeADMINpassword + zone_name: safezone.test + records: + - name: iron01 + ds_record: + - 1234 3 3 84763786e4213cca9a6938dba5dacd64f87ec216 + - 1234 3 3 84763786e4213cca9a6938dba5dacd64f87ec222 + register: result + failed_when: result.failed or not result.changed + + - name: Add CERT records to test. + ipadnsrecord: + ipaadmin_password: SomeADMINpassword + zone_name: safezone.test + records: + - name: host01 + cert_record: + - 1 1234 3 AwIBAgIUb14+Oug2nPMIIBdTCCAR+g + - 5 555 4 AwIBAgIUb14+Oug2nPMIIBdTCCAAS+g + register: result + failed_when: result.failed or not result.changed + + - name: Modify CERT record. + ipadnsrecord: + ipaadmin_password: SomeADMINpassword + zone_name: safezone.test + name: host01 + cert_type: 2 + cert_key_tag: 567 + cert_algorithm: 4 + cert_rec: 1 1234 3 AwIBAgIUb14+Oug2nPMIIBdTCCAR+g + register: result + failed_when: result.failed or not result.changed + + - name: Verify modified CERT records exists. + ipadnsrecord: + ipaadmin_password: SomeADMINpassword + zone_name: safezone.test + records: + - name: host01 + cert_record: 2 567 4 AwIBAgIUb14+Oug2nPMIIBdTCCAR+g + register: result + failed_when: result.failed or result.changed + + - name: Verify if old CERT record does not exist. + ipadnsrecord: + ipaadmin_password: SomeADMINpassword + zone_name: safezone.test + records: + - name: host01 + cert_record: 1 1234 3 AwIBAgIUb14+Oug2nPMIIBdTCCAR+g + state: absent + register: result + failed_when: result.failed or result.changed + + - name: Verify if unmodified CERT record does exist. + ipadnsrecord: + ipaadmin_password: SomeADMINpassword + zone_name: safezone.test + records: + - name: host01 + cert_record: 5 555 4 AwIBAgIUb14+Oug2nPMIIBdTCCAAS+g + register: result + failed_when: result.failed or result.changed + + - name: Try to modify the same DS record twice. + ipadnsrecord: + ipaadmin_password: SomeADMINpassword + zone_name: safezone.test + records: + - name: iron01 + ds_key_tag: 5555 + ds_algorithm: 5 + ds_digest_type: 5 + ds_digest: 84763786e4213cca9a6938dba5dacd64f87ec222 + ds_record: 1234 3 3 84763786e4213cca9a6938dba5dacd64f87ec216 + + - name: iron01 + ds_key_tag: 5555 + ds_algorithm: 5 + ds_digest_type: 5 + ds_digest: 84763786e4213cca9a6938dba5dacd64f87ec222 + ds_record: 1234 3 3 84763786e4213cca9a6938dba5dacd64f87ec216 + register: result + failed_when: not result.failed or "DS record does not contain" not in result.msg + + - name: Verify if unmodified DS record still exists. + ipadnsrecord: + ipaadmin_password: SomeADMINpassword + zone_name: safezone.test + records: + - name: iron01 + ds_record: 1234 3 3 84763786e4213cca9a6938dba5dacd64f87ec222 + register: result + failed_when: result.failed or result.changed + + - name: Verify DS record was modified + ipadnsrecord: + ipaadmin_password: SomeADMINpassword + zone_name: safezone.test + records: + - name: iron01 + ds_record: 5555 5 5 84763786e4213cca9a6938dba5dacd64f87ec222 + register: result + failed_when: result.failed or result.changed + + - name: Verify if modified DS record was not created. + ipadnsrecord: + ipaadmin_password: SomeADMINpassword + zone_name: safezone.test + records: + - name: iron01 + ds_record: 1234 3 3 84763786e4213cca9a6938dba5dacd64f87ec216 + state: absent + register: result + failed_when: result.failed or result.changed + + - name: Cleanup test records. + ipadnsrecord: + ipaadmin_password: SomeADMINpassword + zone_name: safezone.test + records: + - name: iron01 + ds_record: + - 1234 3 3 84763786e4213cca9a6938dba5dacd64f87ec216 + - 1234 3 3 84763786e4213cca9a6938dba5dacd64f87ec222 + - 5555 5 5 84763786e4213cca9a6938dba5dacd64f87ec222 + - name: host01 + cert_record: + - 1 1234 3 AwIBAgIUb14+Oug2nPMIIBdTCCAR+g + - 2 567 4 AwIBAgIUb14+Oug2nPMIIBdTCCAR+g + state: absent + # cleanup + - name: Cleanup test environment. + include_tasks: env_cleanup.yml