Repository: ansible/ansible

Issue:
sysctl module does not apply kernel parameters for CentOS7
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and master branch are affected too.
-->

##### ISSUE TYPE
<!--- Pick one below and delete the rest: -->
 - Bug Report

##### COMPONENT NAME
<!--- Name of the module/plugin/task/feature -->
sysctl module

##### ANSIBLE VERSION
<!--- Paste verbatim output from “ansible --version” between quotes below -->
```
ansible 2.2.0.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = Default w/o overrides
```

##### CONFIGURATION
<!---
Mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).
-->


##### OS / ENVIRONMENT
<!---
Mention the OS you are running Ansible from, and the OS you are
managing, or say “N/A” for anything that is not platform-specific.
-->
running from windows subsystem for linux (win10):
# lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 14.04.5 LTS
Release:        14.04
Codename:       trusty

managing CentOS7:
# cat /etc/redhat-release
CentOS Linux release 7.3.1611 (Core)

##### SUMMARY
<!--- Explain the problem briefly -->
Trying to apply kernel parameters to centos7 host (as described in http://docs.ansible.com/ansible/sysctl_module.html). And nothing happens, /etc/sysctl.conf remains the same (tried to change file name with sysctl_file -> new file had only "=" inside). 

##### STEPS TO REPRODUCE
<!---
For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used.
-->

<!--- Paste example playbooks or commands between quotes below -->
```yaml
---
- hosts: '{{ target }}'
  gather_facts: yes
  tasks:

  - name: Set kernel parameters
    sysctl: name= {{ item.pname }} value= {{ item.value }} state=present sysctl_file=/usr/lib/sysctl.d/60-sysctl.conf sysctl_set=yes reload=yes
    with_items:
      - { pname: 'fs.aio-max-nr', value: '22222222' }
      - { pname: 'fs.file-max', value: '11111111' }

```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
kernel parameters permanently changed and reloaded
##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->
playbook worked, parameters remains the same
<!--- Paste verbatim command output between quotes below -->
```
Using /etc/ansible/ansible.cfg as config file

PLAYBOOK: 01-init-dev-centos7.yml **********************************************
1 plays in 01-init-dev-centos7.yml

PLAY [[u'pprb01']] *************************************************************

TASK [setup] *******************************************************************
Using module file /usr/lib/python2.7/dist-packages/ansible/modules/core/system/setup.py
<pprb01> ESTABLISH SSH CONNECTION FOR USER: None
<pprb01> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/ansible-ssh-%h-%p-%r pprb01 '/bin/sh -c '"'"'( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1484230471.91-255180108104212 `" && echo ansible-tmp-1484230471.91-255180108104212="` echo $HOME/.ansible/tmp/ansible-tmp-1484230471.91-255180108104212 `" ) && sleep 0'"'"''
<pprb01> PUT /tmp/tmpJEt4PA TO /root/.ansible/tmp/ansible-tmp-1484230471.91-255180108104212/setup.py
<pprb01> SSH: EXEC sftp -b - -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/ansible-ssh-%h-%p-%r '[pprb01]'
<pprb01> ESTABLISH SSH CONNECTION FOR USER: None
<pprb01> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/ansible-ssh-%h-%p-%r pprb01 '/bin/sh -c '"'"'chmod u+x /root/.ansible/tmp/ansible-tmp-1484230471.91-255180108104212/ /root/.ansible/tmp/ansible-tmp-1484230471.91-255180108104212/setup.py && sleep 0'"'"''
<pprb01> ESTABLISH SSH CONNECTION FOR USER: None
<pprb01> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/ansible-ssh-%h-%p-%r -tt pprb01 '/bin/sh -c '"'"'/usr/bin/python /root/.ansible/tmp/ansible-tmp-1484230471.91-255180108104212/setup.py; rm -rf "/root/.ansible/tmp/ansible-tmp-1484230471.91-255180108104212/" > /dev/null 2>&1 && sleep 0'"'"''
ok: [pprb01]

TASK [Set kernel parameters] ***************************************************
task path: /mnt/c/Users/igoncharov/ansible/pprb/01-init-dev-centos7.yml:6
Using module file /usr/lib/python2.7/dist-packages/ansible/modules/core/system/sysctl.py
<pprb01> ESTABLISH SSH CONNECTION FOR USER: None
<pprb01> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/ansible-ssh-%h-%p-%r pprb01 '/bin/sh -c '"'"'( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1484230472.58-135407387098666 `" && echo ansible-tmp-1484230472.58-135407387098666="` echo $HOME/.ansible/tmp/ansible-tmp-1484230472.58-135407387098666 `" ) && sleep 0'"'"''
<pprb01> PUT /tmp/tmppOi_12 TO /root/.ansible/tmp/ansible-tmp-1484230472.58-135407387098666/sysctl.py
<pprb01> SSH: EXEC sftp -b - -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/ansible-ssh-%h-%p-%r '[pprb01]'
<pprb01> ESTABLISH SSH CONNECTION FOR USER: None
<pprb01> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/ansible-ssh-%h-%p-%r pprb01 '/bin/sh -c '"'"'chmod u+x /root/.ansible/tmp/ansible-tmp-1484230472.58-135407387098666/ /root/.ansible/tmp/ansible-tmp-1484230472.58-135407387098666/sysctl.py && sleep 0'"'"''
<pprb01> ESTABLISH SSH CONNECTION FOR USER: None
<pprb01> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/ansible-ssh-%h-%p-%r -tt pprb01 '/bin/sh -c '"'"'/usr/bin/python /root/.ansible/tmp/ansible-tmp-1484230472.58-135407387098666/sysctl.py; rm -rf "/root/.ansible/tmp/ansible-tmp-1484230472.58-135407387098666/" > /dev/null 2>&1 && sleep 0'"'"''
changed: [pprb01] => (item={u'pname': u'fs.aio-max-nr', u'value': u'22222222'}) => {
    "changed": true,
    "invocation": {
        "module_args": {
            "ignoreerrors": false,
            "name": "",
            "reload": true,
            "state": "present",
            "sysctl_file": "/usr/lib/sysctl.d/60-sysctl.conf",
            "sysctl_set": true,
            "value": ""
        },
        "module_name": "sysctl"
    },
    "item": {
        "pname": "fs.aio-max-nr",
        "value": "22222222"
    }
}
Using module file /usr/lib/python2.7/dist-packages/ansible/modules/core/system/sysctl.py
<pprb01> ESTABLISH SSH CONNECTION FOR USER: None
<pprb01> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/ansible-ssh-%h-%p-%r pprb01 '/bin/sh -c '"'"'( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1484230472.99-88814108002804 `" && echo ansible-tmp-1484230472.99-88814108002804="` echo $HOME/.ansible/tmp/ansible-tmp-1484230472.99-88814108002804 `" ) && sleep 0'"'"''
<pprb01> PUT /tmp/tmpZ30QNo TO /root/.ansible/tmp/ansible-tmp-1484230472.99-88814108002804/sysctl.py
<pprb01> SSH: EXEC sftp -b - -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/ansible-ssh-%h-%p-%r '[pprb01]'
<pprb01> ESTABLISH SSH CONNECTION FOR USER: None
<pprb01> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/ansible-ssh-%h-%p-%r pprb01 '/bin/sh -c '"'"'chmod u+x /root/.ansible/tmp/ansible-tmp-1484230472.99-88814108002804/ /root/.ansible/tmp/ansible-tmp-1484230472.99-88814108002804/sysctl.py && sleep 0'"'"''
<pprb01> ESTABLISH SSH CONNECTION FOR USER: None
<pprb01> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/ansible-ssh-%h-%p-%r -tt pprb01 '/bin/sh -c '"'"'/usr/bin/python /root/.ansible/tmp/ansible-tmp-1484230472.99-88814108002804/sysctl.py; rm -rf "/root/.ansible/tmp/ansible-tmp-1484230472.99-88814108002804/" > /dev/null 2>&1 && sleep 0'"'"''
changed: [pprb01] => (item={u'pname': u'fs.file-max', u'value': u'11111111'}) => {
    "changed": true,
    "invocation": {
        "module_args": {
            "ignoreerrors": false,
            "name": "",
            "reload": true,
            "state": "present",
            "sysctl_file": "/usr/lib/sysctl.d/60-sysctl.conf",
            "sysctl_set": true,
            "value": ""
        },
        "module_name": "sysctl"
    },
    "item": {
        "pname": "fs.file-max",
        "value": "11111111"
    }
}

PLAY RECAP *********************************************************************
pprb01                     : ok=2    changed=1    unreachable=0    failed=0
```



Discussion:
cc @davixx
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Your playbook is wrong, there are spaces after the `=` for name and value → boom.

This works for me (also never write to /usr!):
```
---
- hosts: '{{ target }}'
  gather_facts: yes
  tasks:

  - name: Set kernel parameters
    sysctl: name={{ item.pname }} value={{ item.value }} state=present sysctl_file=/etc/sysctl.d/60-sysctl.conf sysctl_set=yes reload=yes
    with_items:
      - { pname: 'fs.aio-max-nr', value: '22222222' }
      - { pname: 'fs.file-max', value: '11111111' }
```

Resolving patch:
diff --git a/lib/ansible/modules/system/sysctl.py b/lib/ansible/modules/system/sysctl.py
--- a/lib/ansible/modules/system/sysctl.py
+++ b/lib/ansible/modules/system/sysctl.py
@@ -386,9 +386,21 @@ def main():
             ignoreerrors = dict(default=False, type='bool'),
             sysctl_file = dict(default='/etc/sysctl.conf', type='path')
         ),
-        supports_check_mode=True
+        supports_check_mode=True,
+        required_if=[('state', 'present', ['value'])],
     )
 
+    if module.params['name'] is None:
+        module.fail_json(msg="name can not be None")
+    if module.params['state'] == 'present' and module.params['value'] is None:
+        module.fail_json(msg="value can not be None")
+
+    # In case of in-line params
+    if module.params['name'] == '':
+        module.fail_json(msg="name can not be blank")
+    if module.params['state'] == 'present' and module.params['value'] == '':
+        module.fail_json(msg="value can not be blank")
+
     result = SysctlModule(module)
 
     module.exit_json(changed=result.changed)


Test patch: