From 1d223c2b63634abe86f7702a64dd83c4fbc272ce Mon Sep 17 00:00:00 2001
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
Date: Mon, 15 Jun 2020 16:14:25 -0300
Subject: [PATCH] Add support for attributes `ip_address` and `port` to
 `forwarders`.

This patch modify the was forwarders are configured, using two attributes,
`ip_address` and `port`, instead of IPA API internal string representation
of `IP port PORT`.
---
 README-dnsforwardzone.md                     |  6 ++-
 plugins/modules/ipadnsforwardzone.py         | 37 ++++++++++++++---
 tests/dnsforwardzone/test_dnsforwardzone.yml | 43 ++++++++++++--------
 3 files changed, 62 insertions(+), 24 deletions(-)

diff --git a/README-dnsforwardzone.md b/README-dnsforwardzone.md
index 81919295..15b2b574 100644
--- a/README-dnsforwardzone.md
+++ b/README-dnsforwardzone.md
@@ -99,8 +99,10 @@ Variable | Description | Required
 `ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
 `ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
 `name` \| `cn` | Zone name (FQDN). | yes if `state` == `present`
-`forwarders` \| `idnsforwarders` |  Per-zone conditional forwarding policy. Possible values are `only`, `first`, `none`) | no
-`forwardpolicy` \| `idnsforwardpolicy` | Per-zone conditional forwarding policy. Set to "none" to disable forwarding to global forwarder for this zone. In that case, conditional zone forwarders are disregarded. | no
+`forwarders` \| `idnsforwarders` |  Per-zone forwarders. A custom port can be specified for each forwarder. Options | no
+&nbsp; | `ip_address`: The forwarder IP address. | yes
+&nbsp; | `port`: The forwarder IP port. | no
+`forwardpolicy` \| `idnsforwardpolicy` | Per-zone conditional forwarding policy. Possible values are `only`, `first`, `none`. Set to "none" to disable forwarding to global forwarder for this zone. In that case, conditional zone forwarders are disregarded. | no
 `skip_overlap_check` | Force DNS zone creation even if it will overlap with an existing zone. Defaults to False. | no
 `action` | Work on group or member level. It can be on of `member` or `dnsforwardzone` and defaults to `dnsforwardzone`. | no
 `state` | The state to ensure. It can be one of `present`, `absent`, `enabled` or `disabled`, default: `present`. | yes
diff --git a/plugins/modules/ipadnsforwardzone.py b/plugins/modules/ipadnsforwardzone.py
index 3968e6a1..8e5c3464 100644
--- a/plugins/modules/ipadnsforwardzone.py
+++ b/plugins/modules/ipadnsforwardzone.py
@@ -54,9 +54,16 @@ options:
   forwarders:
     description:
     - List of the DNS servers to forward to
-    required: true
-    type: list
     aliases: ["idnsforwarders"]
+    options:
+      ip_address:
+        description: Forwarder IP address (either IPv4 or IPv6).
+        required: false
+        type: string
+      port:
+        description: Forwarder port.
+        required: false
+        type: int
   forwardpolicy:
     description: Per-zone conditional forwarding policy
     required: false
@@ -128,6 +135,20 @@ def gen_args(forwarders, forwardpolicy, skip_overlap_check):
     return _args
 
 
+def forwarder_list(forwarders):
+    """Convert the forwarder dict into a list compatible with IPA API."""
+    if forwarders is None:
+        return None
+    fwd_list = []
+    for forwarder in forwarders:
+        if forwarder.get('port', None) is not None:
+            formatter = "{ip_address} port {port}"
+        else:
+            formatter = "{ip_address}"
+        fwd_list.append(formatter.format(**forwarder))
+    return fwd_list
+
+
 def main():
     ansible_module = AnsibleModule(
         argument_spec=dict(
@@ -136,8 +157,13 @@ def main():
             ipaadmin_password=dict(type="str", required=False, no_log=True),
             name=dict(type="list", aliases=["cn"], default=None,
                       required=True),
-            forwarders=dict(type='list', aliases=["idnsforwarders"],
-                            required=False),
+            forwarders=dict(type="list", default=None, required=False,
+                            aliases=["idnsforwarders"], elements='dict',
+                            options=dict(
+                                 ip_address=dict(type='str', required=True),
+                                 port=dict(type='int', required=False,
+                                           default=None),
+                            )),
             forwardpolicy=dict(type='str', aliases=["idnsforwardpolicy"],
                                required=False,
                                choices=['only', 'first', 'none']),
@@ -160,7 +186,8 @@ def main():
                                           "ipaadmin_password")
     names = module_params_get(ansible_module, "name")
     action = module_params_get(ansible_module, "action")
-    forwarders = module_params_get(ansible_module, "forwarders")
+    forwarders = forwarder_list(
+        module_params_get(ansible_module, "forwarders"))
     forwardpolicy = module_params_get(ansible_module, "forwardpolicy")
     skip_overlap_check = module_params_get(ansible_module,
                                            "skip_overlap_check")
diff --git a/tests/dnsforwardzone/test_dnsforwardzone.yml b/tests/dnsforwardzone/test_dnsforwardzone.yml
index d94db9e5..468cd4ce 100644
--- a/tests/dnsforwardzone/test_dnsforwardzone.yml
+++ b/tests/dnsforwardzone/test_dnsforwardzone.yml
@@ -5,7 +5,7 @@
   gather_facts: false
 
   tasks:
-  - name: ensure test forwardzones are absent - prep
+  - name: ensure test forwardzones are absent
     ipadnsforwardzone:
       ipaadmin_password: SomeADMINpassword
       name:
@@ -19,7 +19,7 @@
       state: present
       name: example.com
       forwarders:
-        - 8.8.8.8
+        - ip_address: 8.8.8.8
       forwardpolicy: first
       skip_overlap_check: true
     register: result
@@ -31,7 +31,7 @@
       state: present
       name: example.com
       forwarders:
-        - 8.8.8.8
+        - ip_address: 8.8.8.8
       forwardpolicy: first
       skip_overlap_check: true
     register: result
@@ -43,19 +43,22 @@
       state: present
       name: example.com
       forwarders:
-        - 8.8.8.8
-        - 4.4.4.4
+        - ip_address: 8.8.8.8
+        - ip_address: 4.4.4.4
+          port: 8053
       forwardpolicy: first
       skip_overlap_check: true
     register: result
     failed_when: not result.changed
 
+  - pause:
+
   - name: ensure forwardzone example.com has one forwarder again
     ipadnsforwardzone:
       ipaadmin_password: SomeADMINpassword
       name: example.com
       forwarders:
-        - 8.8.8.8
+        - ip_address: 8.8.8.8
       forwardpolicy: first
       skip_overlap_check: true
       state: present
@@ -67,7 +70,7 @@
       ipaadmin_password: SomeADMINpassword
       name: example.com
       forwarders:
-        - 8.8.8.8
+        - ip_address: 8.8.8.8
       forwardpolicy: first
       skip_overlap_check: false
       state: present
@@ -80,8 +83,9 @@
       state: present
       name: example.com
       forwarders:
-        - 8.8.8.8
-        - 4.4.4.4
+        - ip_address: 8.8.8.8
+        - ip_address: 4.4.4.4
+          port: 8053
       forwardpolicy: only
       skip_overlap_check: false
     register: result
@@ -100,7 +104,7 @@
       name: example.com
       skip_overlap_check: true
       forwarders:
-        - 8.8.8.8
+        - ip_address: 8.8.8.8
     register: result
     failed_when: not result.changed
 
@@ -110,7 +114,8 @@
       state: present
       name: example.com
       forwarders:
-        - 4.4.4.4
+        - ip_address: 4.4.4.4
+          port: 8053
       action: member
     register: result
     failed_when: not result.changed
@@ -121,8 +126,9 @@
       state: present
       name: example.com
       forwarders:
-        - 4.4.4.4
-        - 8.8.8.8
+        - ip_address: 4.4.4.4
+          port: 8053
+        - ip_address: 8.8.8.8
       action: member
     register: result
     failed_when: result.changed
@@ -133,7 +139,7 @@
       state: absent
       name: example.com
       forwarders:
-        - 8.8.8.8
+        - ip_address: 8.8.8.8
       action: member
     register: result
     failed_when: not result.changed
@@ -144,7 +150,8 @@
       state: present
       name: example.com
       forwarders:
-        - 4.4.4.4
+        - ip_address: 4.4.4.4
+          port: 8053
       action: member
     register: result
     failed_when: result.changed
@@ -161,7 +168,8 @@
       state: present
       name: example.com
       forwarders:
-        - 4.4.4.4
+        - ip_address: 4.4.4.4
+          port: 8053
       action: member
       skip_overlap_check: true
     register: result
@@ -179,7 +187,8 @@
       state: disabled
       name: example.com
       forwarders:
-        - 4.4.4.4
+        - ip_address: 4.4.4.4
+          port: 8053
       skip_overlap_check: true
     register: result
     failed_when: not result.changed
-- 
GitLab