Repository: ansible/ansible

Issue:
INI lookup fails if default is specified
<!---
Please do not report issues/requests related to Ansible modules here !!

Report them to the appropriate modules-core or modules-extras project:
  - https://github.com/ansible/ansible-modules-core/issues
  - https://github.com/ansible/ansible-modules-extras/issues

Also verify first that your issue/request is not already reported in GitHub
-->


##### ISSUE TYPE
<!--- Pick one below and delete the rest: -->
 - Bug Report

##### COMPONENT NAME
<!--- Name of the plugin/task/feature -->
- lookup('ini', ...)

##### 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 Ubuntu 16.04
- Managing Ubuntu 16.04

##### SUMMARY
<!--- Explain the problem briefly -->
If you specify the `default` field in an INI lookup, the lookup always fails with the error:
```
fatal: [default]: FAILED! => {"failed": true, "msg": "too many values to unpack"}
```

##### STEPS TO REPRODUCE
<!---
For bugs, show exactly how to reproduce the problem.
For new features, show how the feature would be used.
-->
```
- name: This works
  debug: msg="{{ lookup('ini', 'KeyName section=SectionName file=filename.ini') }}"

- name: This should work, but doesn't
  debug: msg="{{ lookup('ini', 'KeyName section=SectionName file=filename.ini default=XXX') }}"

- name: This doesn't work either
  debug: msg="{{ lookup('ini', 'NonExistentKey section=SectionName file=filename.ini default=XXX') }}"

- name: We don't expect this to work
  debug: msg="{{ lookup('ini', 'NonExistentKey section=SectionName file=filename.ini') }}"
```

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
This is the output from Ansible 2.1.1:
```
TASK [php7-cli : This works] ***************************************************
ok: [default] => {
    "msg": "Value"
}

TASK [php7-cli : This should work, but doesn't] ********************************
ok: [default] => {
    "msg": "Value"
}

TASK [php7-cli : This doesn't work either] *************************************
ok: [default] => {
    "msg": "[]"
}

TASK [php7-cli : We don't expect this to work] *********************************
ok: [default] => {
    "msg": "[]"
}
```

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->
```
TASK [php7-cli : This works] ***************************************************
ok: [default] => {
    "msg": "php7.0-bcmath"
}

TASK [php7-cli : This should work, but doesn't] ********************************
fatal: [default]: FAILED! => {
    "failed": true, 
    "msg": "too many values to unpack"
}
```

##### WORKAROUND
Downgrade to Ansible 2.1.1.



Discussion:
I can reproduce this with the devel branch

```
[fedora-23:lookup_ini_18369 (master %)]$ cat test.yml 
---
- hosts: localhost
  tasks:
    - name: This works
      debug: msg="{{ lookup('ini', 'KeyName section=SectionName file=filename.ini') }}"

    - name: This should work, but doesn't
      debug: msg="{{ lookup('ini', 'KeyName section=SectionName file=filename.ini default=XXX') }}"

    - name: This doesn't work either
      debug: msg="{{ lookup('ini', 'NonExistentKey section=SectionName file=filename.ini default=XXX') }}"

    - name: We don't expect this to work
      debug: msg="{{ lookup('ini', 'NonExistentKey section=SectionName file=filename.ini') }}"
[fedora-23:lookup_ini_18369 (master %)]$ cat filename.ini 
[SectionName]
file=ddddfilename.ini
[fedora-23:lookup_ini_18369 (master %)]$ ansible-playbook -vvvv test.yml 
Using /home/adrian/.ansible.cfg as config file
 [WARNING]: provided hosts list is empty, only localhost is available

Loading callback plugin default of type stdout, v2.0 from /home/adrian/src/ansible/lib/ansible/plugins/callback/__init__.pyc

PLAYBOOK: test.yml ********************************************************************************************************************************************************************************************************************
1 plays in test.yml

PLAY [localhost] **********************************************************************************************************************************************************************************************************************

TASK [setup] **************************************************************************************************************************************************************************************************************************
Using module file /home/adrian/src/ansible/lib/ansible/modules/core/system/setup.py
<127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: adrian
<127.0.0.1> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1478530448.12-177352847997456 `" && echo ansible-tmp-1478530448.12-177352847997456="` echo $HOME/.ansible/tmp/ansible-tmp-1478530448.12-177352847997456 `" ) && sleep 0'
<127.0.0.1> PUT /tmp/tmpBFjlcU TO /home/adrian/.ansible/tmp/ansible-tmp-1478530448.12-177352847997456/setup.py
<127.0.0.1> EXEC /bin/sh -c 'chmod u+x /home/adrian/.ansible/tmp/ansible-tmp-1478530448.12-177352847997456/ /home/adrian/.ansible/tmp/ansible-tmp-1478530448.12-177352847997456/setup.py && sleep 0'
<127.0.0.1> EXEC /bin/sh -c '/usr/bin/python /home/adrian/.ansible/tmp/ansible-tmp-1478530448.12-177352847997456/setup.py; rm -rf "/home/adrian/.ansible/tmp/ansible-tmp-1478530448.12-177352847997456/" > /dev/null 2>&1 && sleep 0'
ok: [localhost]

TASK [This works] *********************************************************************************************************************************************************************************************************************
task path: /home/adrian/src/ansible-bug-repro/lookup_ini_18369/test.yml:4
ok: [localhost] => {
    "msg": []
}

TASK [This should work, but doesn't] **************************************************************************************************************************************************************************************************
task path: /home/adrian/src/ansible-bug-repro/lookup_ini_18369/test.yml:7
fatal: [localhost]: FAILED! => {
    "failed": true, 
    "msg": "too many values to unpack"
}
    to retry, use: --limit @/home/adrian/src/ansible-bug-repro/lookup_ini_18369/test.retry

PLAY RECAP ****************************************************************************************************************************************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=1   

[fedora-23:lookup_ini_18369 (master %)]$ ansible --version
ansible 2.3.0 (devel 5aaf1d1a15) last updated 2016/11/07 09:32:34 (GMT -400)
  lib/ansible/modules/core: (detached HEAD 2584fca0ae) last updated 2016/11/04 13:15:56 (GMT -400)
  lib/ansible/modules/extras: (detached HEAD a1dcbf9ce5) last updated 2016/11/04 13:15:56 (GMT -400)
  config file = /home/adrian/.ansible.cfg
  configured module search path = Default w/o overrides


```

bisect points this to 4ba60d00c8d7e62912a37ec24f90f6e5d0770c4d

```
commit 4ba60d00c8d7e62912a37ec24f90f6e5d0770c4d
Author: jctanner <tanner.jc@gmail.com>
Date:   Tue Jun 28 13:24:57 2016 -0400

    Refactor the parameter splitting in ini lookup to handle more path formats (#16477)

    Refactor the parameter splitting in ini lookup to handle more path formats.

    Fixes #16468
```

reproducer and bisect script at https://github.com/alikins/ansible-bug-repro/tree/master/lookup_ini_18369


Resolving patch:
diff --git a/lib/ansible/plugins/lookup/ini.py b/lib/ansible/plugins/lookup/ini.py
--- a/lib/ansible/plugins/lookup/ini.py
+++ b/lib/ansible/plugins/lookup/ini.py
@@ -36,7 +36,7 @@
 def _parse_params(term):
     '''Safely split parameter term to preserve spaces'''
 
-    keys = ['key', 'type', 'section', 'file', 're']
+    keys = ['key', 'type', 'section', 'file', 're', 'default']
     params = {}
     for k in keys:
         params[k] = ''


Test patch: