Repository: ansible/ansible

Issue:
nsupdate zone argument should be optional
##### SUMMARY
It's much easier to write records when nsupdate auto-detects the zone.

At least the cli nsupdate tool allows automatic detection of the zone as documented [here](https://linux.die.net/man/1/nsupdate) (go to `zone {zonename}`). I have not yet tested whether this can easily be done using dnspython.

##### ISSUE TYPE
 - Feature Idea

##### COMPONENT NAME
nsupdate

##### ANSIBLE VERSION
```
ansible 2.5.4
  config file = None
  configured module search path = [u'/Users/user/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/Cellar/ansible/2.5.4/libexec/lib/python2.7/site-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 2.7.15 (default, May  1 2018, 16:44:08) [GCC 4.2.1 Compatible Apple LLVM 9.1.0 (clang-902.0.39.1)]
```

##### CONFIGURATION

##### OS / ENVIRONMENT
N/A

##### STEPS TO REPRODUCE
Simply one argument less.

##### EXPECTED RESULTS
\-

##### ACTUAL RESULTS
\-


Discussion:
Files identified in the description:
* [lib/ansible/modules/net_tools/nsupdate.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/net_tools/nsupdate.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 @nerzhul
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Hello,
The detection is not really possible, except if we run some DNS SOA queries to find which is the root domain, but it's not simple to implement currently

Resolving patch:
diff --git a/lib/ansible/modules/net_tools/nsupdate.py b/lib/ansible/modules/net_tools/nsupdate.py
--- a/lib/ansible/modules/net_tools/nsupdate.py
+++ b/lib/ansible/modules/net_tools/nsupdate.py
@@ -59,10 +59,11 @@
     zone:
         description:
             - DNS record will be modified on this C(zone).
-        required: true
+            - When omitted DNS will be queried to attempt finding the correct zone.
+            - Starting with Ansible 2.7 this parameter is optional.
     record:
         description:
-            - Sets the DNS record to modify.
+            - Sets the DNS record to modify. When zone is omitted this has to be absolute (ending with a dot).
         required: true
     type:
         description:
@@ -172,11 +173,23 @@ class RecordManager(object):
     def __init__(self, module):
         self.module = module
 
-        if module.params['zone'][-1] != '.':
-            self.zone = module.params['zone'] + '.'
+        if module.params['zone'] is None:
+            if module.params['record'][-1] != '.':
+                self.module.fail_json(msg='record must be absolute when omitting zone parameter')
+
+            try:
+                self.zone = dns.resolver.zone_for_name(self.module.params['record']).to_text()
+            except (dns.exception.Timeout, dns.resolver.NoNameservers, dns.resolver.NoRootSOA) as e:
+                self.module.fail_json(msg='Zone resolver error (%s): %s' % (e.__class__.__name__, to_native(e)))
+
+            if self.zone is None:
+                self.module.fail_json(msg='Unable to find zone, dnspython returned None')
         else:
             self.zone = module.params['zone']
 
+            if self.zone[-1] != '.':
+                self.zone += '.'
+
         if module.params['key_name']:
             try:
                 self.keyring = dns.tsigkeyring.from_text({
@@ -332,7 +345,7 @@ def main():
             key_name=dict(required=False, type='str'),
             key_secret=dict(required=False, type='str', no_log=True),
             key_algorithm=dict(required=False, default='hmac-md5', choices=tsig_algs, type='str'),
-            zone=dict(required=True, type='str'),
+            zone=dict(required=False, default=None, type='str'),
             record=dict(required=True, type='str'),
             type=dict(required=False, default='A', type='str'),
             ttl=dict(required=False, default=3600, type='int'),


Test patch: