Repository: ansible/ansible

Issue:
Linode provision errors with "Alert_cpu_threshold must be between 0 and 2000." 
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
linode cloud module - modules/cloud/linode/linode.py

##### ANSIBLE VERSION
```
ansible 2.3.0.0
  config file = 
  configured module search path = Default w/o overrides
  python version = 2.7.13 (default, Apr  4 2017, 08:46:44) [GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)]
```

##### OS / ENVIRONMENT
Master: Mac OS X El Capitan v10.11.6
Managing: Ubuntu 16.04 on Linode
Python: 2.7.13

##### SUMMARY

When trying to provision a linode with the linode module (using a playbook very similar to [this one](https://github.com/marji/ansible-playbook-linode/blob/master/provision.yml)), I get an error:
 
`Alert_cpu_threshold must be between 0 and 2000`

##### STEPS TO REPRODUCE

- Installed pycurl and linode-python via pip
- Updated hosts file to set ansible_python_interpreter
- Export LINODE_API_KEY 
-  Attempt to provision linode with the following settings:

```yaml
tasks:
  - name: Linode | Create linode server via Linode API
    linode:
       name: test
       plan: 1
       datacenter: 7
       distribution: 146
       displaygroup: test
       swap: 512
       wait: yes
       wait_timeout: 600
       state: present
     register: linode
```

Full example, including hosts file is available [in this gist](https://gist.github.com/ErisDS/b8a78f7e86a2b21b466714332a3e059c)

Executed using `ansible-playbook provision.yml -i hosts`

If I update my task to include `alert_cpu_threshold: 1000`, I then get another error:

`Alert_diskio_threshold must be between 0 and 100000`

##### EXPECTED RESULTS

Expected result is that the task should return OK and continue on to the next task. Instead the error prevents the task & all subsequent tasks from running.

##### ACTUAL RESULTS

Not: a linode is created, but not fully provisioned as there is no disk. 

```
PLAY [Provision new linode] **********************************************************************************************************************************************************************

TASK [Linode | Create linode server via Linode API] **********************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"changed": false, "failed": true, "msg": "Alert_cpu_threshold must be between 0 and 2000."}
	to retry, use: --limit @.../ansible/provision.retry

PLAY RECAP ***************************************************************************************************************************************************************************************
localhost                  : ok=0    changed=0    unreachable=0    failed=1 
```



Discussion:
FYI for anyone else coming across this, I have implemented a workaround by reading the defaults from linode.list(), and sending all the default values like:

```
tasks:
    - name: Linode | Create linode server via Linode API
      # http://docs.ansible.com/ansible/linode_module.html
      linode:
        name: testing
        plan: 1
        datacenter: 7
        distribution: 146
        displaygroup: test
        swap: 512
        wait: yes
        wait_timeout: 600
        state: present
        alert_cpu_threshold: 90
        alert_diskio_threshold: 10000
        alert_bwin_threshold: 10
        alert_bwout_threshold: 10
        alert_bwquota_threshold: 80
      register: linode
```
cc @zbal @intheclouddan
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
@ErisDS I'll look into this weekend.

Resolving patch:
diff --git a/lib/ansible/modules/cloud/linode/linode.py b/lib/ansible/modules/cloud/linode/linode.py
--- a/lib/ansible/modules/cloud/linode/linode.py
+++ b/lib/ansible/modules/cloud/linode/linode.py
@@ -60,7 +60,7 @@
   alert_bwin_enabled:
     description:
     - Set status of bandwidth in alerts.
-    default: "True"
+    default: null
     choices: [ "True", "False" ]
     version_added: "2.3"
   alert_bwin_threshold:
@@ -71,7 +71,7 @@
   alert_bwout_enabled:
     description:
     - Set status of bandwidth out alerts.
-    default: "True"
+    default: null
     choices: [ "True", "False" ]
     version_added: "2.3"
   alert_bwout_threshold:
@@ -82,7 +82,7 @@
   alert_bwquota_enabled:
     description:
     - Set status of bandwidth quota alerts as percentage of network transfer quota.
-    default: "True"
+    default: null
     choices: [ "True", "False" ]
     version_added: "2.3"
   alert_bwquota_threshold:
@@ -93,7 +93,7 @@
   alert_cpu_enabled:
     description:
     - Set status of receiving CPU usage alerts.
-    default: "True"
+    default: null
     choices: [ "True", "False" ]
     version_added: "2.3"
   alert_cpu_threshold:
@@ -104,7 +104,7 @@
   alert_diskio_enabled:
     description:
     - Set status of receiving disk IO alerts.
-    default: "True"
+    default: null
     choices: [ "True", "False" ]
     version_added: "2.3"
   alert_diskio_threshold:
@@ -334,10 +334,10 @@ def getInstanceDetails(api, server):
                                         'ip_id': ip['IPADDRESSID']})
     return instance
 
-def linodeServers(module, api, state, name, alert_bwin_enabled, alert_bwin_threshold, alert_bwout_enabled, alert_bwout_threshold,
-                  alert_bwquota_enabled, alert_bwquota_threshold, alert_cpu_enabled, alert_cpu_threshold, alert_diskio_enabled,
-                  alert_diskio_threshold,backupweeklyday, backupwindow, displaygroup, plan, additional_disks, distribution,
-                  datacenter, kernel_id, linode_id, payment_term, password, private_ip, ssh_pub_key, swap, wait, wait_timeout, watchdog):
+def linodeServers(module, api, state, name,
+                  displaygroup, plan, additional_disks, distribution,
+                  datacenter, kernel_id, linode_id, payment_term, password,
+                  private_ip, ssh_pub_key, swap, wait, wait_timeout, watchdog, **kwargs):
     instances = []
     changed = False
     new_server = False
@@ -385,13 +385,8 @@ def linodeServers(module, api, state, name, alert_bwin_enabled, alert_bwin_thres
                 # Update linode Label to match name
                 api.linode_update(LinodeId=linode_id, Label='%s_%s' % (linode_id, name))
                 # Update Linode with Ansible configuration options
-                api.linode_update(LinodeId=linode_id, ALERT_BWIN_ENABLED=alert_bwin_enabled,
-                        ALERT_BWIN_THRESHOLD=alert_bwin_threshold, ALERT_BWOUT_ENABLED=alert_bwout_enabled,
-                        ALERT_BWOUT_THRESHOLD=alert_bwout_threshold, ALERT_BWQUOTA_ENABLED=alert_bwquota_enabled,
-                        ALERT_BWQUOTA_THRESHOLD=alert_bwquota_threshold, ALERT_CPU_ENABLED=alert_cpu_enabled,
-                        ALERT_CPU_THRESHOLD=alert_cpu_threshold, ALERT_DISKIO_ENABLED=alert_diskio_enabled,
-                        ALERT_DISKIO_THRESHOLD=alert_diskio_threshold, BACKUPWEEKLYDAY=backupweeklyday,
-                        BACKUPWINDOW=backupwindow, LPM_DISPLAYGROUP=displaygroup, WATCHDOG=watchdog)
+                api.linode_update(LinodeId=linode_id,
+                        LPM_DISPLAYGROUP=displaygroup, WATCHDOG=watchdog, **kwargs)
                 # Save server
                 servers = api.linode_list(LinodeId=linode_id)
             except Exception as e:
@@ -599,18 +594,19 @@ def main():
                                                      'restarted']),
             api_key = dict(no_log=True),
             name = dict(type='str'),
-            alert_bwin_enabled = dict(type='bool', default=True),
-            alert_bwin_threshold = dict(type='int'),
-            alert_bwout_enabled = dict(type='bool', default=True),
-            alert_bwout_threshold = dict(type='int'),
-            alert_bwquota_enabled = dict(type='bool', default=True),
-            alert_bwquota_threshold = dict(type='int'),
-            alert_cpu_enabled = dict(type='bool', default=True),
-            alert_cpu_threshold = dict(type='int'),
-            alert_diskio_enabled = dict(type='bool', default=True),
-            alert_diskio_threshold = dict(type='int'),
-            backupweeklyday = dict(type='int'),
-            backupwindow = dict(type='int'),
+            alert_bwin_enabled = dict(type='bool', default=None),
+            alert_bwin_threshold = dict(type='int', default=None),
+            alert_bwout_enabled = dict(type='bool', default=None),
+            alert_bwout_threshold = dict(type='int', default=None),
+            alert_bwquota_enabled = dict(type='bool', default=None),
+            alert_bwquota_threshold = dict(type='int', default=None),
+            alert_cpu_enabled = dict(type='bool', default=None),
+            alert_cpu_threshold = dict(type='int', default=None),
+            alert_diskio_enabled = dict(type='bool', default=None),
+            alert_diskio_threshold = dict(type='int', default=None),
+            backupsenabled = dict(type='int', default=None),
+            backupweeklyday = dict(type='int', default=None),
+            backupwindow = dict(type='int', default=None),
             displaygroup = dict(type='str', default=''),
             plan = dict(type='int'),
             additional_disks= dict(type='list'),
@@ -637,15 +633,15 @@ def main():
     state = module.params.get('state')
     api_key = module.params.get('api_key')
     name = module.params.get('name')
-    alert_bwin_enabled = int(module.params.get('alert_bwin_enabled'))
+    alert_bwin_enabled = module.params.get('alert_bwin_enabled')
     alert_bwin_threshold = module.params.get('alert_bwin_threshold')
-    alert_bwout_enabled = int(module.params.get('alert_bwout_enabled'))
+    alert_bwout_enabled = module.params.get('alert_bwout_enabled')
     alert_bwout_threshold = module.params.get('alert_bwout_threshold')
-    alert_bwquota_enabled = int(module.params.get('alert_bwquota_enabled'))
+    alert_bwquota_enabled = module.params.get('alert_bwquota_enabled')
     alert_bwquota_threshold = module.params.get('alert_bwquota_threshold')
-    alert_cpu_enabled = int(module.params.get('alert_cpu_enabled'))
+    alert_cpu_enabled = module.params.get('alert_cpu_enabled')
     alert_cpu_threshold = module.params.get('alert_cpu_threshold')
-    alert_diskio_enabled = int(module.params.get('alert_diskio_enabled'))
+    alert_diskio_enabled = module.params.get('alert_diskio_enabled')
     alert_diskio_threshold = module.params.get('alert_diskio_threshold')
     backupsenabled = module.params.get('backupsenabled')
     backupweeklyday = module.params.get('backupweeklyday')
@@ -666,6 +662,18 @@ def main():
     wait_timeout = int(module.params.get('wait_timeout'))
     watchdog = int(module.params.get('watchdog'))
 
+    kwargs = {}
+    check_items = {'alert_bwin_enabled': alert_bwin_enabled, 'alert_bwin_threshold': alert_bwin_threshold,
+                    'alert_bwout_enabled': alert_bwout_enabled, 'alert_bwout_threshold': alert_bwout_threshold,
+                    'alert_bwquota_enabled': alert_bwquota_enabled, 'alert_bwquota_threshold': alert_bwquota_threshold,
+                    'alert_cpu_enabled': alert_cpu_enabled, 'alert_cpu_threshold': alert_cpu_threshold,
+                    'alert_diskio_enabled': alert_diskio_enabled, 'alert_diskio_threshold': alert_diskio_threshold,
+                    'backupweeklyday': backupweeklyday, 'backupwindow': backupwindow}
+
+    for key, value in check_items.items():
+        if value is not None:
+            kwargs[key] = value
+
     # Setup the api_key
     if not api_key:
         try:
@@ -680,14 +688,11 @@ def main():
     except Exception as e:
         module.fail_json(msg = '%s' % e.value[0]['ERRORMESSAGE'])
 
-    linodeServers(module, api, state, name, alert_bwin_enabled,
-            alert_bwin_threshold, alert_bwout_enabled, alert_bwout_threshold,
-            alert_bwquota_enabled, alert_bwquota_threshold, alert_cpu_enabled,
-            alert_cpu_threshold, alert_diskio_enabled, alert_diskio_threshold,
-            backupweeklyday, backupwindow, displaygroup, plan,
+    linodeServers(module, api, state, name,
+            displaygroup, plan,
             additional_disks, distribution, datacenter, kernel_id, linode_id,
             payment_term, password, private_ip, ssh_pub_key, swap, wait,
-            wait_timeout, watchdog)
+            wait_timeout, watchdog, **kwargs)
 
 # import module snippets
 from ansible.module_utils.basic import *


Test patch: