Repository: ansible/ansible

Issue:
nsupdate handling record values with spaces
##### SUMMARY
When adding a record with values with spaces, for example a TXT record with SPF data like `"v=spf1 mx -all"` it will currently be added as `"v=spf1" "mx" "-all"`. This results in being finally parsed as  `v=spf1mx-all`, breaks the syntax of the record and I wouldn't consider this intuitive behavior.
As I found on https://stackoverflow.com/a/17255241 when double-quoting the record value like `'"v=spf1 mx -all"'` it will properly create the record.

If this is expected behavior it should be documented. Otherwise we should probably wrap it in the nsupdate module.

##### ISSUE TYPE
 - Documentation Report

##### COMPONENT NAME
nsupdate

##### ANSIBLE VERSION
```
2.7
```

##### CONFIGURATION
N/A

##### OS / ENVIRONMENT
N/A

##### STEPS TO REPRODUCE
```yaml
- name: "create spf txt record for {{ record }}"
  nsupdate:
    key_name: "{{ nsupdate_key_name }}"
    key_secret: "{{ nsupdate_key_secret }}"
    key_algorithm: "{{ nsupdate_key_algorithm }}"

    server: "{{ nsupdate_server | default('localhost') }}"
    state: "present"

    record: "{{ record }}"
    type: "txt"
    value: "v=spf1 mx -all"
    ttl: 300
```

##### EXPECTED RESULTS
TXT record with value `"v=spf1 mx -all"`

##### ACTUAL RESULTS
TXT record with value `"v=spf1" "mx" "-all"`


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 --->
Yeah, the current behavior is a bit unintuitive.

If we are going to fix it code wise we will want to consider not breaking existing playbooks. We will also want to support the legitimate special case of TXT records where the value is intended to consist of multiple separate string.

No matter what, I fully agree that at a minimum the syntax should be documented. It could be as simple as adding an example.

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
@@ -207,8 +207,18 @@ def __init__(self, module):
         else:
             self.algorithm = module.params['key_algorithm']
 
+        if self.module.params['type'].lower() == 'txt':
+            self.value = list(map(self.txt_helper, self.module.params['value']))
+        else:
+            self.value = self.module.params['value']
+
         self.dns_rc = 0
 
+    def txt_helper(self, entry):
+        if entry[0] == '"' and entry[-1] == '"':
+            return entry
+        return '"{text}"'.format(text=entry)
+
     def __do_update(self, update):
         response = None
         try:
@@ -249,7 +259,7 @@ def create_or_update_record(self):
 
     def create_record(self):
         update = dns.update.Update(self.zone, keyring=self.keyring, keyalgorithm=self.algorithm)
-        for entry in self.module.params['value']:
+        for entry in self.value:
             try:
                 update.add(self.module.params['record'],
                            self.module.params['ttl'],
@@ -266,7 +276,7 @@ def create_record(self):
     def modify_record(self):
         update = dns.update.Update(self.zone, keyring=self.keyring, keyalgorithm=self.algorithm)
         update.delete(self.module.params['record'], self.module.params['type'])
-        for entry in self.module.params['value']:
+        for entry in self.value:
             try:
                 update.add(self.module.params['record'],
                            self.module.params['ttl'],
@@ -316,7 +326,7 @@ def record_exists(self):
         if self.dns_rc == 0:
             if self.module.params['state'] == 'absent':
                 return 1
-            for entry in self.module.params['value']:
+            for entry in self.value:
                 try:
                     update.present(self.module.params['record'], self.module.params['type'], entry)
                 except AttributeError:
@@ -376,7 +386,7 @@ def main():
                                 record=module.params['record'],
                                 type=module.params['type'],
                                 ttl=module.params['ttl'],
-                                value=module.params['value'])
+                                value=record.value)
 
         module.exit_json(**result)
 


Test patch: