Repository: ansible/ansible

Issue:
[cloudflare_dns] "value" - required parameter during deleting record
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
cloudflare_dns - manage Cloudflare DNS records

##### ANSIBLE VERSION
ansible 2.3.0.0
  config file = 
  configured module search path = Default w/o overrides
  python version = 2.7.5 (default, Nov  6 2016, 00:28:07) [GCC 4.8.5 20150623 (Red Hat 4.8.5-11)]

##### CONFIGURATION
[defaults]
ansible_managed = Ansible managed: {file} modified on %Y-%m-%d %H:%M:%S by {uid} on {host}
remote_user = user
force_color = 1
nocolor = 0
private_key_file=/path/to/key
deprecation_warnings=False
forks = 100

[ssh_connection]
pipelining = True
ssh_args = -o ControlMaster=auto -o ControlPersist=30m -o StrictHostKeyChecking=no
control_path = /tmp/ansible-ssh-%%h-%%p-%%r

[privilege_escalation]
become = True

##### OS / ENVIRONMENT
NAME="CentOS Linux"
VERSION="7 (Core)"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="7"
PRETTY_NAME="CentOS Linux 7 (Core)"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:centos:centos:7"
HOME_URL="https://www.centos.org/"
BUG_REPORT_URL="https://bugs.centos.org/"

CENTOS_MANTISBT_PROJECT="CentOS-7"
CENTOS_MANTISBT_PROJECT_VERSION="7"
REDHAT_SUPPORT_PRODUCT="centos"
REDHAT_SUPPORT_PRODUCT_VERSION="7"

##### SUMMARY
We work with cloudflare.com service and use cloudflare_dns(ansible module) for maintenance our dns records. 
According ansible docs we shouldn't specify "value" parameter during delete records. But if value isn't specify - task is failed.
 
According api docs for cloudflare service, we shouldn't pass this(value) parameter during deleting cname record ([](https://api.cloudflare.com/#dns-records-for-a-zone-delete-dns-record)): 
`DELETE /zones/:zone_identifier/dns_records/:identifier`.

So I would be very grateful if you make "value" parameter isn't required during deleting cname records.

##### STEPS TO REPRODUCE
When we're deleting CNAME records we use this task:
```
- name: delete cloudflare CNAME rr
  cloudflare_dns:
    zone: "our_zone"
    record: "api-test"
    type: CNAME
    ttl: 1
    state: "delete"
    account_email: "{{ cloudflare_email }}"
    account_api_token: "{{ cloudflare_token }}"
```
run command:
`# ansible-playbook -i ec2.py cloudflare.yml`

out:

TASK [cloudflare : update | delete cloudflare CNAME rr] ***************************************************************************
fatal: [localhost]: FAILED! => {"changed": false, "failed": true, "msg": "value of state must be one of: present,absent, got: delete"}
	to retry, use: --limit @/tmp/devops-ansible/cloudflare.retry

BR. Mykola Novikov


Discussion:
@karambolishe Thank you for reporting this issue. I see `delete` for state option in your playbook, can you please try using `absent` instead.
sorry my example above, I wrote by hand)  
My playbook looks like:

- name: update | delete cloudflare CNAME rr
  cloudflare_dns:
    zone: "{{ zone_name }}"
    record: "{{ fqdn }}"
    type: CNAME
    value: "{{ elb_facts.elbs.0.dns_name }}"
    ttl: 1
    state: absent
    account_email: "{{ cloudflare_email }}"
    account_api_token: "{{ cloudflare_token }}" 
Here is the culprit.

https://github.com/ansible/ansible/blob/02cd88169764232fd63c776456178fe61d3a214a/lib/ansible/modules/net_tools/cloudflare_dns.py#L617-L628

Created https://github.com/ansible/ansible/pull/31740 as a suggested fix.

Resolving patch:
diff --git a/lib/ansible/modules/net_tools/cloudflare_dns.py b/lib/ansible/modules/net_tools/cloudflare_dns.py
--- a/lib/ansible/modules/net_tools/cloudflare_dns.py
+++ b/lib/ansible/modules/net_tools/cloudflare_dns.py
@@ -473,7 +473,8 @@ def delete_dns_records(self, **kwargs):
         content = params['value']
         search_record = params['record']
         if params['type'] == 'SRV':
-            content = str(params['weight']) + '\t' + str(params['port']) + '\t' + params['value']
+            if not (params['value'] is None or params['value'] == ''):
+                content = str(params['weight']) + '\t' + str(params['port']) + '\t' + params['value']
             search_record = params['service'] + '.' + params['proto'] + '.' + params['record']
         if params['solo']:
             search_value = None
@@ -616,22 +617,20 @@ def main():
         ),
         supports_check_mode=True,
         required_if=([
-            ('state', 'present', ['record', 'type']),
-            ('type', 'MX', ['priority', 'value']),
-            ('type', 'SRV', ['port', 'priority', 'proto', 'service', 'value', 'weight']),
-            ('type', 'A', ['value']),
-            ('type', 'AAAA', ['value']),
-            ('type', 'CNAME', ['value']),
-            ('type', 'TXT', ['value']),
-            ('type', 'NS', ['value']),
-            ('type', 'SPF', ['value'])
+            ('state', 'present', ['record', 'type', 'value']),
+            ('state', 'absent', ['record']),
+            ('type', 'SRV', ['proto', 'service']),
         ]
         ),
-        required_one_of=(
-            [['record', 'value', 'type']]
-        )
     )
 
+    if module.params['type'] == 'SRV':
+        if not ((module.params['weight'] is not None and module.params['port'] is not None
+                 and not (module.params['value'] is None or module.params['value'] == ''))
+                or (module.params['weight'] is None and module.params['port'] is None
+                    and (module.params['value'] is None or module.params['value'] == ''))):
+            module.fail_json(msg="For SRV records the params weight, port and value all need to be defined, or not at all.")
+
     changed = False
     cf_api = CloudflareAPI(module)
 


Test patch: