Repository: ansible-collections/community.general

Issue:
udm_dns_record does not work for managing PTR records
### Summary

I can create an destroy A records in a Univention server with the module. However, managing PTR records does not work, and looking into the code can never have worked. 

### Issue Type

Bug Report

### Component Name

udm_dns_record

### Ansible Version

```console (paste below)
ansible [core 2.11.1]
  config file = None
  configured module search path = ['/Users/sdamm/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /Users/sdamm/.venv/ansible3/lib/python3.9/site-packages/ansible
  ansible collection location = /Users/sdamm/.ansible/collections:/usr/share/ansible/collections
  executable location = /Users/sdamm/.venv/ansible3/bin/ansible
  python version = 3.9.5 (default, May  4 2021, 03:36:27) [Clang 12.0.0 (clang-1200.0.32.29)]
  jinja version = 3.0.1
  libyaml = True
```


### Configuration

```console (paste below)
empty

```


### OS / Environment

Mac OS Big Sur, ansible installed in virtualenv via pip

### Steps to Reproduce

<!--- Paste example playbooks or commands between quotes below -->
```yaml (paste below)
     - name: Generate DNS reverse entry
       udm_dns_record:
         state: present
         name: "192.168.1.222"
         zone: "192.168.1"
         type: ptr_record
         data:
           ptr_record: "wurstwasser.example.com."
```


### Expected Results

I would expect PTR records to be created. Instead the module throws an error. The error comes from looking up the zone in Univention, which does not work due to wrong parameters. But it's not the only error, after solving this one, other ones pop up.

### Actual Results

```console (paste below)
Creating/editing dns entry 192.168.1.222 in zoneName=192.168.1,cn=dns,dc=example,dc=com failed: list index out of range"}
```


### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct


Discussion:
Files identified in the description:
* [`plugins/modules/cloud/univention/udm_dns_record.py`](https://github.com/ansible-collections/community.general/blob/master/plugins/modules/cloud/univention/udm_dns_record.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @keachi
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
diff --git a/plugins/modules/cloud/univention/udm_dns_record.py b/plugins/modules/cloud/univention/udm_dns_record.py
--- a/plugins/modules/cloud/univention/udm_dns_record.py
+++ b/plugins/modules/cloud/univention/udm_dns_record.py
@@ -21,6 +21,7 @@
 requirements:
     - Python >= 2.6
     - Univention
+    - ipaddress (for I(type=ptr_record))
 options:
     state:
         required: false
@@ -33,10 +34,12 @@
         description:
             - "Name of the record, this is also the DNS record. E.g. www for
                www.example.com."
+            - For PTR records this has to be the IP address.
     zone:
         required: true
         description:
             - Corresponding DNS zone for this record, e.g. example.com.
+            - For PTR records this has to be the full reverse zone (for example C(1.1.192.in-addr.arpa)).
     type:
         required: true
         description:
@@ -63,12 +66,29 @@
       a:
          - 192.0.2.1
          - 2001:0db8::42
+
+- name: Create a DNS v4 PTR record on a UCS
+  community.general.udm_dns_record:
+    name: 192.0.2.1
+    zone: 2.0.192.in-addr.arpa
+    type: ptr_record
+    data:
+      ptr_record: "www.example.com."
+
+- name: Create a DNS v6 PTR record on a UCS
+  community.general.udm_dns_record:
+    name: 2001:db8:0:0:0:ff00:42:8329
+    zone: 2.4.0.0.0.0.f.f.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa
+    type: ptr_record
+    data:
+      ptr_record: "www.example.com."
 '''
 
 
 RETURN = '''#'''
 
 HAVE_UNIVENTION = False
+HAVE_IPADDRESS = False
 try:
     from univention.admin.handlers.dns import (
         forward_zone,
@@ -79,6 +99,7 @@
     pass
 
 from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.basic import missing_required_lib
 from ansible_collections.community.general.plugins.module_utils.univention_umc import (
     umc_module_for_add,
     umc_module_for_edit,
@@ -87,6 +108,11 @@
     config,
     uldap,
 )
+try:
+    import ipaddress
+    HAVE_IPADDRESS = True
+except ImportError:
+    pass
 
 
 def main():
@@ -121,14 +147,30 @@ def main():
     changed = False
     diff = None
 
+    workname = name
+    if type == 'ptr_record':
+        if not HAVE_IPADDRESS:
+            module.fail_json(msg=missing_required_lib('ipaddress'))
+        try:
+            if 'arpa' not in zone:
+                raise Exception("Zone must be reversed zone for ptr_record. (e.g. 1.1.192.in-addr.arpa)")
+            ipaddr_rev = ipaddress.ip_address(name).reverse_pointer
+            subnet_offset = ipaddr_rev.find(zone)
+            if subnet_offset == -1:
+                raise Exception("reversed IP address {0} is not part of zone.".format(ipaddr_rev))
+            workname = ipaddr_rev[0:subnet_offset - 1]
+        except Exception as e:
+            module.fail_json(
+                msg='handling PTR record for {0} in zone {1} failed: {2}'.format(name, zone, e)
+            )
+
     obj = list(ldap_search(
-        '(&(objectClass=dNSZone)(zoneName={0})(relativeDomainName={1}))'.format(zone, name),
+        '(&(objectClass=dNSZone)(zoneName={0})(relativeDomainName={1}))'.format(zone, workname),
         attr=['dNSZone']
     ))
-
     exists = bool(len(obj))
     container = 'zoneName={0},cn=dns,{1}'.format(zone, base_dn())
-    dn = 'relativeDomainName={0},{1}'.format(name, container)
+    dn = 'relativeDomainName={0},{1}'.format(workname, container)
 
     if state == 'present':
         try:
@@ -141,13 +183,21 @@ def main():
                 ) or reverse_zone.lookup(
                     config(),
                     uldap(),
-                    '(zone={0})'.format(zone),
+                    '(zoneName={0})'.format(zone),
                     scope='domain',
                 )
+                if len(so) == 0:
+                    raise Exception("Did not find zone '{0}' in Univention".format(zone))
                 obj = umc_module_for_add('dns/{0}'.format(type), container, superordinate=so[0])
             else:
                 obj = umc_module_for_edit('dns/{0}'.format(type), dn)
-            obj['name'] = name
+
+            if type == 'ptr_record':
+                obj['ip'] = name
+                obj['address'] = workname
+            else:
+                obj['name'] = name
+
             for k, v in data.items():
                 obj[k] = v
             diff = obj.diff()


Test patch: