Repository: ansible/ansible

Issue:
ipa_sudorule: repsonse sudorule_add_allow_command: Invalid JSON-RPC request: params[1] (aka options) must be a dict
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
ipa_sudorule
##### ANSIBLE VERSION
```
[abush@localhost cluster_build]$ ansible --version
ansible 2.3.1.0
  config file = /home/abush/Projects/cluster_build/ansible.cfg
  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)]
```

```
[abush@localhost cluster_build]$ ansible --version
ansible 2.4.0
  config file = /home/abush/Projects/cluster_build/ansible.cfg
  configured module search path = [u'/home/abush/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/site-packages/ansible
  executable location = /usr/bin/ansible
  python version = 2.7.5 (default, Nov  6 2016, 00:28:07) [GCC 4.8.5 20150623 (Red Hat 4.8.5-11)]

```
##### CONFIGURATION

##### OS / ENVIRONMENT

##### SUMMARY
The module ipa_sudorule has a bug where it doesn't create the correct json payload to pass to the FreeIPA API.
Creating a new sudorule with a list of commands results in the following error:
`fatal: [ds0.cdh-poc-cluster.internal.cdhnetwork]: FAILED! => {"changed": false, "failed": true, "msg": "repsonse sudorule_add_allow_command: Invalid JSON-RPC request: params[1] (aka options) must be a dict"}`

You can see from the FreeIPA API that it expects a nested dictionary to be passed:
```
[root@ds0 ~]# ipa -vv sudorule_add_allow_command
ipa: INFO: trying https://ds0.cdh-poc-cluster.internal.cdhnetwork/ipa/json
Rule name: testrule
[member sudo command]: su -
[member sudo command group]: 
ipa: INFO: Forwarding 'sudorule_add_allow_command/1' to json server 'https://ds0.cdh-poc-cluster.internal.cdhnetwork/ipa/json'
ipa: INFO: Request: {
    "id": 0, 
    "method": "sudorule_add_allow_command/1", 
    "params": [
        [
            "testrule"
        ], 
        {
            "sudocmd": [
                "su -"
            ], 
            "version": "2.213"
        }
    ]
}
```

However only a list is passed:
https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/identity/ipa/ipa_sudorule.py#L211

##### STEPS TO REPRODUCE
```yaml
- hosts: localhost
  connection: local
  tasks:
  - ipa_sudorule:
      name: test
      cmd:
        - su
      ipa_host: ipa.example.com
      ipa_user: admin
      ipa_pass: topsecret
```

##### EXPECTED RESULTS
Successful execution without error

##### ACTUAL RESULTS
`fatal: [ds0.cdh-poc-cluster.internal.cdhnetwork]: FAILED! => {"changed": false, "failed": true, "msg": "repsonse sudorule_add_allow_command: Invalid JSON-RPC request: params[1] (aka options) must be a dict"}`



Discussion:
cc @Nosmoht
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
diff --git a/lib/ansible/modules/identity/ipa/ipa_sudorule.py b/lib/ansible/modules/identity/ipa/ipa_sudorule.py
--- a/lib/ansible/modules/identity/ipa/ipa_sudorule.py
+++ b/lib/ansible/modules/identity/ipa/ipa_sudorule.py
@@ -208,7 +208,7 @@ def sudorule_remove_host_hostgroup(self, name, item):
         return self.sudorule_remove_host(name=name, item={'hostgroup': item})
 
     def sudorule_add_allow_command(self, name, item):
-        return self._post_json(method='sudorule_add_allow_command', name=name, item=item)
+        return self._post_json(method='sudorule_add_allow_command', name=name, item={'sudocmd': item})
 
     def sudorule_remove_allow_command(self, name, item):
         return self._post_json(method='sudorule_remove_allow_command', name=name, item=item)


Test patch: