Blame SOURCES/ansible-freeipa-0.1.12-Add-support-for-option-name_from_ip-in-ipadnszone-mo_rhbz#1845056.patch

c235c4
From abbd15e6f50718119b4dd0380913d2d646eb7638 Mon Sep 17 00:00:00 2001
c235c4
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
c235c4
Date: Mon, 3 Aug 2020 19:23:07 -0300
c235c4
Subject: [PATCH] Add support for option `name_from_ip` in ipadnszone module.
c235c4
c235c4
IPA CLI has an option `name_from_ip` that provide a name for a zone
c235c4
from the reverse IP address, so that it can be used to, for example,
c235c4
manage PTR DNS records.
c235c4
c235c4
This patch adds a similar attribute to ipadnszone module, where it
c235c4
will try to find the proper zone name, using DNS resolve, or provide
c235c4
a sane default, if a the zone name cannot be resolved.
c235c4
c235c4
The option `name_from_ip` must be used instead of `name` in playbooks,
c235c4
and it is a string, and not a list.
c235c4
c235c4
A new example playbook was added:
c235c4
c235c4
    playbooks/dnszone/dnszone-reverse-from-ip.yml
c235c4
c235c4
A new test playbook was added:
c235c4
c235c4
    tests/dnszone/test_dnszone_name_from_ip.yml
c235c4
---
c235c4
 README-dnszone.md                             |   3 +-
c235c4
 playbooks/dnszone/dnszone-reverse-from-ip.yml |  10 ++
c235c4
 plugins/modules/ipadnszone.py                 |  65 +++++++++-
c235c4
 tests/dnszone/test_dnszone_name_from_ip.yml   | 112 ++++++++++++++++++
c235c4
 4 files changed, 186 insertions(+), 4 deletions(-)
c235c4
 create mode 100644 playbooks/dnszone/dnszone-reverse-from-ip.yml
c235c4
 create mode 100644 tests/dnszone/test_dnszone_name_from_ip.yml
c235c4
c235c4
diff --git a/README-dnszone.md b/README-dnszone.md
c235c4
index 9c9b12c..48b019a 100644
c235c4
--- a/README-dnszone.md
c235c4
+++ b/README-dnszone.md
c235c4
@@ -163,7 +163,8 @@ Variable | Description | Required
c235c4
 -------- | ----------- | --------
c235c4
 `ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
c235c4
 `ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
c235c4
-`name` \| `zone_name` | The zone name string or list of strings. | yes
c235c4
+`name` \| `zone_name` | The zone name string or list of strings. | no
c235c4
+`name_from_ip` | Derive zone name from reverse of IP (PTR). | no
c235c4
 `forwarders` | The list of forwarders dicts. Each `forwarders` dict entry has:| no
c235c4
   | `ip_address` - The IPv4 or IPv6 address of the DNS server. | yes
c235c4
   | `port` - The custom port that should be used on this server. | no
c235c4
diff --git a/playbooks/dnszone/dnszone-reverse-from-ip.yml b/playbooks/dnszone/dnszone-reverse-from-ip.yml
c235c4
new file mode 100644
c235c4
index 0000000..5693872
c235c4
--- /dev/null
c235c4
+++ b/playbooks/dnszone/dnszone-reverse-from-ip.yml
c235c4
@@ -0,0 +1,10 @@
c235c4
+---
c235c4
+- name: Playbook to ensure DNS zone exist
c235c4
+  hosts: ipaserver
c235c4
+  become: true
c235c4
+
c235c4
+  tasks:
c235c4
+  - name: Ensure zone exist, finding zone name from IP address.
c235c4
+    ipadnszone:
c235c4
+      ipaadmin_password: SomeADMINpassword
c235c4
+      name_from_ip: 10.1.2.3
c235c4
diff --git a/plugins/modules/ipadnszone.py b/plugins/modules/ipadnszone.py
c235c4
index c5e812a..901bfef 100644
c235c4
--- a/plugins/modules/ipadnszone.py
c235c4
+++ b/plugins/modules/ipadnszone.py
c235c4
@@ -43,6 +43,10 @@ options:
c235c4
     required: true
c235c4
     type: list
c235c4
     alises: ["zone_name"]
c235c4
+  name_from_ip:
c235c4
+    description: Derive zone name from reverse of IP (PTR).
c235c4
+    required: false
c235c4
+    type: str
c235c4
   forwarders:
c235c4
     description: The list of global DNS forwarders.
c235c4
     required: false
c235c4
@@ -197,6 +201,12 @@ from ansible.module_utils.ansible_freeipa_module import (
c235c4
     is_ipv6_addr,
c235c4
     is_valid_port,
c235c4
 )  # noqa: E402
c235c4
+import netaddr
c235c4
+import six
c235c4
+
c235c4
+
c235c4
+if six.PY3:
c235c4
+    unicode = str
c235c4
 
c235c4
 
c235c4
 class DNSZoneModule(FreeIPABaseModule):
c235c4
@@ -354,6 +364,31 @@ class DNSZoneModule(FreeIPABaseModule):
c235c4
         if not zone and self.ipa_params.skip_nameserver_check is not None:
c235c4
             return self.ipa_params.skip_nameserver_check
c235c4
 
c235c4
+    def __reverse_zone_name(self, ipaddress):
c235c4
+        """
c235c4
+        Infer reverse zone name from an ip address.
c235c4
+
c235c4
+        This function uses the same heuristics as FreeIPA to infer the zone
c235c4
+        name from ip.
c235c4
+        """
c235c4
+        try:
c235c4
+            ip = netaddr.IPAddress(str(ipaddress))
c235c4
+        except (netaddr.AddrFormatError, ValueError):
c235c4
+            net = netaddr.IPNetwork(ipaddress)
c235c4
+            items = net.ip.reverse_dns.split('.')
c235c4
+            prefixlen = net.prefixlen
c235c4
+            ip_version = net.version
c235c4
+        else:
c235c4
+            items = ip.reverse_dns.split('.')
c235c4
+            prefixlen = 24 if ip.version == 4 else 64
c235c4
+            ip_version = ip.version
c235c4
+        if ip_version == 4:
c235c4
+            return u'.'.join(items[4 - prefixlen // 8:])
c235c4
+        elif ip_version == 6:
c235c4
+            return u'.'.join(items[32 - prefixlen // 4:])
c235c4
+        else:
c235c4
+            self.fail_json(msg="Invalid IP version for reverse zone.")
c235c4
+
c235c4
     def get_zone(self, zone_name):
c235c4
         get_zone_args = {"idnsname": zone_name, "all": True}
c235c4
         response = self.api_command("dnszone_find", args=get_zone_args)
c235c4
@@ -368,14 +403,33 @@ class DNSZoneModule(FreeIPABaseModule):
c235c4
         return zone, is_zone_active
c235c4
 
c235c4
     def get_zone_names(self):
c235c4
-        if len(self.ipa_params.name) > 1 and self.ipa_params.state != "absent":
c235c4
+        zone_names = self.__get_zone_names_from_params()
c235c4
+        if len(zone_names) > 1 and self.ipa_params.state != "absent":
c235c4
             self.fail_json(
c235c4
                 msg=("Please provide a single name. Multiple values for 'name'"
c235c4
                      "can only be supplied for state 'absent'.")
c235c4
             )
c235c4
 
c235c4
+        return zone_names
c235c4
+
c235c4
+    def __get_zone_names_from_params(self):
c235c4
+        if not self.ipa_params.name:
c235c4
+            return [self.__reverse_zone_name(self.ipa_params.name_from_ip)]
c235c4
         return self.ipa_params.name
c235c4
 
c235c4
+    def check_ipa_params(self):
c235c4
+        if not self.ipa_params.name and not self.ipa_params.name_from_ip:
c235c4
+            self.fail_json(
c235c4
+                msg="Either `name` or `name_from_ip` must be provided."
c235c4
+            )
c235c4
+        if self.ipa_params.state != "present" and self.ipa_params.name_from_ip:
c235c4
+            self.fail_json(
c235c4
+                msg=(
c235c4
+                    "Cannot use argument `name_from_ip` with state `%s`."
c235c4
+                    % self.ipa_params.state
c235c4
+                )
c235c4
+            )
c235c4
+
c235c4
     def define_ipa_commands(self):
c235c4
         for zone_name in self.get_zone_names():
c235c4
             # Look for existing zone in IPA
c235c4
@@ -434,8 +488,9 @@ def get_argument_spec():
c235c4
         ipaadmin_principal=dict(type="str", default="admin"),
c235c4
         ipaadmin_password=dict(type="str", required=False, no_log=True),
c235c4
         name=dict(
c235c4
-            type="list", default=None, required=True, aliases=["zone_name"]
c235c4
+            type="list", default=None, required=False, aliases=["zone_name"]
c235c4
         ),
c235c4
+        name_from_ip=dict(type="str", default=None, required=False),
c235c4
         forwarders=dict(
c235c4
             type="list",
c235c4
             default=None,
c235c4
@@ -475,7 +530,11 @@ def get_argument_spec():
c235c4
 
c235c4
 
c235c4
 def main():
c235c4
-    DNSZoneModule(argument_spec=get_argument_spec()).ipa_run()
c235c4
+    DNSZoneModule(
c235c4
+        argument_spec=get_argument_spec(),
c235c4
+        mutually_exclusive=[["name", "name_from_ip"]],
c235c4
+        required_one_of=[["name", "name_from_ip"]],
c235c4
+    ).ipa_run()
c235c4
 
c235c4
 
c235c4
 if __name__ == "__main__":
c235c4
diff --git a/tests/dnszone/test_dnszone_name_from_ip.yml b/tests/dnszone/test_dnszone_name_from_ip.yml
c235c4
new file mode 100644
c235c4
index 0000000..9bd2eb0
c235c4
--- /dev/null
c235c4
+++ b/tests/dnszone/test_dnszone_name_from_ip.yml
c235c4
@@ -0,0 +1,112 @@
c235c4
+---
c235c4
+- name: Test dnszone
c235c4
+  hosts: ipaserver
c235c4
+  become: yes
c235c4
+  gather_facts: yes
c235c4
+
c235c4
+  tasks:
c235c4
+
c235c4
+  # Setup
c235c4
+  - name: Ensure zone is absent.
c235c4
+    ipadnszone:
c235c4
+      ipaadmin_password: SomeADMINpassword
c235c4
+      name: "{{ item }}"
c235c4
+      state: absent
c235c4
+    with_items:
c235c4
+      - 2.0.192.in-addr.arpa.
c235c4
+      - 0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.f.ip6.arpa.
c235c4
+      - 1.0.0.0.e.f.a.c.8.b.d.0.1.0.0.2.ip6.arpa.
c235c4
+
c235c4
+  # tests
c235c4
+  - name: Ensure zone exists for reverse IP.
c235c4
+    ipadnszone:
c235c4
+      ipaadmin_password: SomeADMINpassword
c235c4
+      name_from_ip: 192.0.2.3/24
c235c4
+    register: ipv4_zone
c235c4
+    failed_when: not ipv4_zone.changed or ipv4_zone.failed
c235c4
+
c235c4
+  - name: Ensure zone exists for reverse IP, again.
c235c4
+    ipadnszone:
c235c4
+      ipaadmin_password: SomeADMINpassword
c235c4
+      name_from_ip: 192.0.2.3/24
c235c4
+    register: result
c235c4
+    failed_when: result.changed or result.failed
c235c4
+
c235c4
+  - name: Ensure zone exists for reverse IP, given the zone name.
c235c4
+    ipadnszone:
c235c4
+      ipaadmin_password: SomeADMINpassword
c235c4
+      name: "{{ ipv4_zone.dnszone.name }}"
c235c4
+    register: result
c235c4
+    failed_when: result.changed or result.failed
c235c4
+
c235c4
+  - name: Modify existing zone, using `name_from_ip`.
c235c4
+    ipadnszone:
c235c4
+      ipaadmin_password: SomeADMINpassword
c235c4
+      name_from_ip: 192.0.2.3/24
c235c4
+      default_ttl: 1234
c235c4
+    register: result
c235c4
+    failed_when: not result.changed
c235c4
+
c235c4
+  - name: Modify existing zone, using `name_from_ip`, again.
c235c4
+    ipadnszone:
c235c4
+      ipaadmin_password: SomeADMINpassword
c235c4
+      name_from_ip: 192.0.2.3/24
c235c4
+      default_ttl: 1234
c235c4
+    register: result
c235c4
+    failed_when: result.changed or result.failed
c235c4
+
c235c4
+  - name: Ensure ipv6 zone exists for reverse IPv6.
c235c4
+    ipadnszone:
c235c4
+      ipaadmin_password: SomeADMINpassword
c235c4
+      name_from_ip: fd00::0001
c235c4
+    register: ipv6_zone
c235c4
+    failed_when: not ipv6_zone.changed or ipv6_zone.failed
c235c4
+
c235c4
+  # - debug:
c235c4
+  #     msg: "{{ipv6_zone}}"
c235c4
+
c235c4
+  - name: Ensure ipv6 zone was created.
c235c4
+    ipadnszone:
c235c4
+      ipaadmin_password: SomeADMINpassword
c235c4
+      name: "{{ ipv6_zone.dnszone.name }}"
c235c4
+    register: result
c235c4
+    failed_when: result.changed or result.failed
c235c4
+
c235c4
+  - name: Ensure ipv6 zone exists for reverse IPv6, again.
c235c4
+    ipadnszone:
c235c4
+      ipaadmin_password: SomeADMINpassword
c235c4
+      name_from_ip: fd00::0001
c235c4
+    register: result
c235c4
+    failed_when: result.changed
c235c4
+
c235c4
+  - name: Ensure second ipv6 zone exists for reverse IPv6.
c235c4
+    ipadnszone:
c235c4
+      ipaadmin_password: SomeADMINpassword
c235c4
+      name_from_ip: 2001:db8:cafe:1::1
c235c4
+    register: ipv6_sec_zone
c235c4
+    failed_when: not ipv6_sec_zone.changed or ipv6_zone.failed
c235c4
+
c235c4
+  - name: Ensure second ipv6 zone was created.
c235c4
+    ipadnszone:
c235c4
+      ipaadmin_password: SomeADMINpassword
c235c4
+      name: "{{ ipv6_sec_zone.dnszone.name }}"
c235c4
+    register: result
c235c4
+    failed_when: result.changed or result.failed
c235c4
+
c235c4
+  - name: Ensure second ipv6 zone exists for reverse IPv6, again.
c235c4
+    ipadnszone:
c235c4
+      ipaadmin_password: SomeADMINpassword
c235c4
+      name_from_ip: 2001:db8:cafe:1::1
c235c4
+    register: result
c235c4
+    failed_when: result.changed
c235c4
+
c235c4
+  # Cleanup
c235c4
+  - name: Ensure zone is absent.
c235c4
+    ipadnszone:
c235c4
+      ipaadmin_password: SomeADMINpassword
c235c4
+      name: "{{ item }}"
c235c4
+      state: absent
c235c4
+    with_items:
c235c4
+      - "{{ ipv6_zone.dnszone.name }}"
c235c4
+      - "{{ ipv6_sec_zone.dnszone.name }}"
c235c4
+      - "{{ ipv4_zone.dnszone.name }}"
c235c4
-- 
c235c4
2.26.2
c235c4
c235c4
From 531e544b30e69f436d14c4ce18c67998c1a0774b Mon Sep 17 00:00:00 2001
c235c4
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
c235c4
Date: Wed, 5 Aug 2020 15:13:46 -0300
c235c4
Subject: [PATCH] Added support for client defined result data in
c235c4
 FReeIPABaseModule
c235c4
c235c4
Modified support for processing result of IPA API commands so that
c235c4
client code can define its own processing and add return values to
c235c4
self.exit_args based on command result.
c235c4
c235c4
If a subclass need to process the result of IPA API commands it should
c235c4
override the method `process_command_result`. The default implementation
c235c4
will simply evaluate if `changed` should be true.
c235c4
---
c235c4
 .../module_utils/ansible_freeipa_module.py    | 22 +++++++++++++------
c235c4
 plugins/modules/ipadnszone.py                 |  8 +++++++
c235c4
 2 files changed, 23 insertions(+), 7 deletions(-)
c235c4
c235c4
diff --git a/plugins/module_utils/ansible_freeipa_module.py b/plugins/module_utils/ansible_freeipa_module.py
c235c4
index 4799e5a..30302b4 100644
c235c4
--- a/plugins/module_utils/ansible_freeipa_module.py
c235c4
+++ b/plugins/module_utils/ansible_freeipa_module.py
c235c4
@@ -619,7 +619,7 @@ class FreeIPABaseModule(AnsibleModule):
c235c4
         if exc_val:
c235c4
             self.fail_json(msg=str(exc_val))
c235c4
 
c235c4
-        self.exit_json(changed=self.changed, user=self.exit_args)
c235c4
+        self.exit_json(changed=self.changed, **self.exit_args)
c235c4
 
c235c4
     def get_command_errors(self, command, result):
c235c4
         """Look for erros into command results."""
c235c4
@@ -658,14 +658,22 @@ class FreeIPABaseModule(AnsibleModule):
c235c4
             except Exception as excpt:
c235c4
                 self.fail_json(msg="%s: %s: %s" % (command, name, str(excpt)))
c235c4
             else:
c235c4
-                if "completed" in result:
c235c4
-                    if result["completed"] > 0:
c235c4
-                        self.changed = True
c235c4
-                else:
c235c4
-                    self.changed = True
c235c4
-
c235c4
+                self.process_command_result(name, command, args, result)
c235c4
             self.get_command_errors(command, result)
c235c4
 
c235c4
+    def process_command_result(self, name, command, args, result):
c235c4
+        """
c235c4
+        Process an API command result.
c235c4
+
c235c4
+        This method can be overriden in subclasses, and change self.exit_values
c235c4
+        to return data in the result for the controller.
c235c4
+        """
c235c4
+        if "completed" in result:
c235c4
+            if result["completed"] > 0:
c235c4
+                self.changed = True
c235c4
+        else:
c235c4
+            self.changed = True
c235c4
+
c235c4
     def require_ipa_attrs_change(self, command_args, ipa_attrs):
c235c4
         """
c235c4
         Compare given args with current object attributes.
c235c4
diff --git a/plugins/modules/ipadnszone.py b/plugins/modules/ipadnszone.py
c235c4
index 901bfef..6a90fa2 100644
c235c4
--- a/plugins/modules/ipadnszone.py
c235c4
+++ b/plugins/modules/ipadnszone.py
c235c4
@@ -472,6 +472,14 @@ class DNSZoneModule(FreeIPABaseModule):
c235c4
                 }
c235c4
                 self.add_ipa_command("dnszone_mod", zone_name, args)
c235c4
 
c235c4
+    def process_command_result(self, name, command, args, result):
c235c4
+        super(DNSZoneModule, self).process_command_result(
c235c4
+            name, command, args, result
c235c4
+        )
c235c4
+        if command == "dnszone_add" and self.ipa_params.name_from_ip:
c235c4
+            dnszone_exit_args = self.exit_args.setdefault('dnszone', {})
c235c4
+            dnszone_exit_args['name'] = name
c235c4
+
c235c4
 
c235c4
 def get_argument_spec():
c235c4
     forwarder_spec = dict(
c235c4
-- 
c235c4
2.26.2
c235c4
c235c4
From 41e8226d0c03e06816626d78cecbc2aebf547691 Mon Sep 17 00:00:00 2001
c235c4
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
c235c4
Date: Wed, 5 Aug 2020 15:14:43 -0300
c235c4
Subject: [PATCH] Return the zone_name when adding a zone with name_from_ip.
c235c4
c235c4
When adding a zone using the option name_from_ip, the user have
c235c4
little control over the final name of the zone, and if this name
c235c4
is to be used in further processing in a playbook it might lead to
c235c4
errors if the inferred name does not match what the user wanted to.
c235c4
c235c4
By returning the actual inferred zone name, the name can be safely
c235c4
used for other tasks in the playbook.
c235c4
---
c235c4
 README-dnszone.md                             | 11 +++++++++++
c235c4
 playbooks/dnszone/dnszone-reverse-from-ip.yml |  7 ++++++-
c235c4
 plugins/modules/ipadnszone.py                 |  8 ++++++++
c235c4
 3 files changed, 25 insertions(+), 1 deletion(-)
c235c4
c235c4
diff --git a/README-dnszone.md b/README-dnszone.md
c235c4
index 48b019a..3f4827b 100644
c235c4
--- a/README-dnszone.md
c235c4
+++ b/README-dnszone.md
c235c4
@@ -190,6 +190,17 @@ Variable | Description | Required
c235c4
 `skip_nameserver_check` | Force DNS zone creation even if nameserver is not resolvable | no
c235c4
 
c235c4
 
c235c4
+Return Values
c235c4
+=============
c235c4
+
c235c4
+ipadnszone
c235c4
+----------
c235c4
+
c235c4
+Variable | Description | Returned When
c235c4
+-------- | ----------- | -------------
c235c4
+`dnszone` | DNS Zone dict with zone name infered from `name_from_ip`. 
Options: | If `state` is `present`, `name_from_ip` is used, and a zone was created.
c235c4
+  | `name` - The name of the zone created, inferred from `name_from_ip`. | Always
c235c4
+
c235c4
 Authors
c235c4
 =======
c235c4
 
c235c4
diff --git a/playbooks/dnszone/dnszone-reverse-from-ip.yml b/playbooks/dnszone/dnszone-reverse-from-ip.yml
c235c4
index 5693872..218a318 100644
c235c4
--- a/playbooks/dnszone/dnszone-reverse-from-ip.yml
c235c4
+++ b/playbooks/dnszone/dnszone-reverse-from-ip.yml
c235c4
@@ -7,4 +7,9 @@
c235c4
   - name: Ensure zone exist, finding zone name from IP address.
c235c4
     ipadnszone:
c235c4
       ipaadmin_password: SomeADMINpassword
c235c4
-      name_from_ip: 10.1.2.3
c235c4
+      name_from_ip: 10.1.2.3/24
c235c4
+    register: result
c235c4
+
c235c4
+  - name: Zone name inferred from `name_from_ip`
c235c4
+    debug:
c235c4
+      msg: "Zone created: {{ result.dnszone.name }}"
c235c4
diff --git a/plugins/modules/ipadnszone.py b/plugins/modules/ipadnszone.py
c235c4
index 6a90fa2..93eac07 100644
c235c4
--- a/plugins/modules/ipadnszone.py
c235c4
+++ b/plugins/modules/ipadnszone.py
c235c4
@@ -192,6 +192,14 @@ EXAMPLES = """
c235c4
 """
c235c4
 
c235c4
 RETURN = """
c235c4
+dnszone:
c235c4
+  description: DNS Zone dict with zone name infered from `name_from_ip`.
c235c4
+  returned:
c235c4
+    If `state` is `present`, `name_from_ip` is used, and a zone was created.
c235c4
+  options:
c235c4
+    name:
c235c4
+      description: The name of the zone created, inferred from `name_from_ip`.
c235c4
+      returned: always
c235c4
 """
c235c4
 
c235c4
 from ipapython.dnsutil import DNSName  # noqa: E402
c235c4
-- 
c235c4
2.26.2
c235c4
c235c4
From 46bbc7bbd7a4e01d07b0390aee8c799aaa5ac895 Mon Sep 17 00:00:00 2001
c235c4
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
c235c4
Date: Mon, 17 Aug 2020 15:52:38 -0300
c235c4
Subject: [PATCH] Document usage of `name_from_ip`.
c235c4
c235c4
Since `name_from_ip` has a similar, but not equal, behavior to `name`,
c235c4
and as the inferred DNS zone might depend on DNS configuration and
c235c4
can be different than the user expects, it has some limited usage,
c235c4
and the user must be aware of its effects.
c235c4
c235c4
This change to the documentation enhance the documentation including
c235c4
more details on the attribute usage.
c235c4
---
c235c4
 README-dnszone.md             | 42 ++++++++++++++++++++++++++++++++++-
c235c4
 plugins/modules/ipadnszone.py |  4 +++-
c235c4
 2 files changed, 44 insertions(+), 2 deletions(-)
c235c4
c235c4
diff --git a/README-dnszone.md b/README-dnszone.md
c235c4
index 3f4827b..c5a7ab3 100644
c235c4
--- a/README-dnszone.md
c235c4
+++ b/README-dnszone.md
c235c4
@@ -152,6 +152,46 @@ Example playbook to remove a zone:
c235c4
 
c235c4
 ```
c235c4
 
c235c4
+Example playbook to create a zone for reverse DNS lookup, from an IP address:
c235c4
+
c235c4
+```yaml
c235c4
+
c235c4
+---
c235c4
+- name: dnszone present
c235c4
+  hosts: ipaserver
c235c4
+  become: true
c235c4
+
c235c4
+  tasks:
c235c4
+  - name: Ensure zone for reverse DNS lookup is present.
c235c4
+    ipadnszone:
c235c4
+      ipaadmin_password: SomeADMINpassword
c235c4
+      name_from_ip: 192.168.1.2
c235c4
+      state: present
c235c4
+```
c235c4
+
c235c4
+Note that, on the previous example the zone created with `name_from_ip` might be "1.168.192.in-addr.arpa.", "168.192.in-addr.arpa.", or "192.in-addr.arpa.", depending on the DNS response the system get while querying for zones, and for this reason, when creating a zone using `name_from_ip`, the inferred zone name is returned to the controller, in the attribute `dnszone.name`. Since the zone inferred might not be what a user expects, `name_from_ip` can only be used with `state: present`. To have more control over the zone name, the prefix length for the IP address can be provided.
c235c4
+
c235c4
+Example playbook to create a zone for reverse DNS lookup, from an IP address, given the prefix length and displaying the resulting zone name:
c235c4
+
c235c4
+```yaml
c235c4
+
c235c4
+---
c235c4
+- name: dnszone present
c235c4
+  hosts: ipaserver
c235c4
+  become: true
c235c4
+
c235c4
+  tasks:
c235c4
+      - name: Ensure zone for reverse DNS lookup is present.
c235c4
+    ipadnszone:
c235c4
+      ipaadmin_password: SomeADMINpassword
c235c4
+      name_from_ip: 192.168.1.2/24
c235c4
+      state: present
c235c4
+    register: result
c235c4
+  - name: Display inferred zone name.
c235c4
+    debug:
c235c4
+      msg: "Zone name: {{ result.dnszone.name }}"
c235c4
+```
c235c4
+
c235c4
 
c235c4
 Variables
c235c4
 =========
c235c4
@@ -164,7 +204,7 @@ Variable | Description | Required
c235c4
 `ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
c235c4
 `ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
c235c4
 `name` \| `zone_name` | The zone name string or list of strings. | no
c235c4
-`name_from_ip` | Derive zone name from reverse of IP (PTR). | no
c235c4
+`name_from_ip` | Derive zone name from reverse of IP (PTR). Can only be used with `state: present`. | no
c235c4
 `forwarders` | The list of forwarders dicts. Each `forwarders` dict entry has:| no
c235c4
   | `ip_address` - The IPv4 or IPv6 address of the DNS server. | yes
c235c4
   | `port` - The custom port that should be used on this server. | no
c235c4
diff --git a/plugins/modules/ipadnszone.py b/plugins/modules/ipadnszone.py
c235c4
index 93eac07..ff6bfff 100644
c235c4
--- a/plugins/modules/ipadnszone.py
c235c4
+++ b/plugins/modules/ipadnszone.py
c235c4
@@ -44,7 +44,9 @@ options:
c235c4
     type: list
c235c4
     alises: ["zone_name"]
c235c4
   name_from_ip:
c235c4
-    description: Derive zone name from reverse of IP (PTR).
c235c4
+    description: |
c235c4
+      Derive zone name from reverse of IP (PTR).
c235c4
+      Can only be used with `state: present`.
c235c4
     required: false
c235c4
     type: str
c235c4
   forwarders:
c235c4
-- 
c235c4
2.26.2
c235c4