Repository: ansible/ansible

Issue:
interfaces_file: need more than 1 value to unpack
<!---
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 -->
interfaces_file
##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
ansible 2.4.0.0
  config file = /Users/bwatson/Projects/[removed]/ansible.cfg
  configured module search path = [u'/Users/bwatson/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /Users/bwatson/Projects/[removed]/venv/lib/python2.7/site-packages/ansible
  executable location = /Users/bwatson/Projects/[removed]/venv/bin/ansible
  python version = 2.7.12 (default, Jun 29 2016, 14:05:02) [GCC 4.2.1 Compatible Apple LLVM 7.3.0 (clang-703.0.31)]
```

##### CONFIGURATION
<!---
If using Ansible 2.4 or above, paste the results of "ansible-config dump --only-changed"

Otherwise, mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).
$ ansible-config dump --only-changed
DEFAULT_REMOTE_TMP(/Users/bwatson/Projects/[removed]/ansible.cfg) = /tmp
HOST_KEY_CHECKING(/Users/bwatson/Projects/[removed]/ansible.cfg) = False
RETRY_FILES_ENABLED(/Users/bwatson/Projects/[removed]/ansible.cfg) = False
-->

##### 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.
Also mention the specific version of what you are trying to control,
e.g. if this is a network bug the version of firmware on the network device.
-->
Host: OS X Python 2.7.12
Target: Cumulus Linux 3.1.1
##### SUMMARY
<!--- Explain the problem briefly -->
Using the interfaces_file task causes an error: ValueError: need more than 1 value to unpack
##### 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.
-->
Install ansible 2.4.0, run the example task shown in interfaces_file documentation.
<!--- Paste example playbooks or commands between quotes below -->
```yaml
---
- hosts: all
  become: true
  gather_facts: true

  tasks:
    - interfaces_file:
        dest: /etc/network/interfaces
        iface: eth1
        option: mtu
        value: 8000
        backup: yes
        state: present
      register: eth1_cfg
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
I would expect the mtu for eth1 to be changed to 8000.
##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->
An error is raised and the task fails. (This happens for any option. I tested multiple)
<!--- Paste verbatim command output between quotes below -->
```
$ ansible-playbook interfaces_test.yml -i 172.20.116.11, -u admin -vv
ansible-playbook 2.4.0.0
  config file = /Users/bwatson/Projects/[removed]/ansible.cfg
  configured module search path = [u'/Users/bwatson/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /Users/bwatson/Projects/[removed]/venv/lib/python2.7/site-packages/ansible
  executable location = /Users/bwatson/Projects/[removed]/venv/bin/ansible-playbook
  python version = 2.7.12 (default, Jun 29 2016, 14:05:02) [GCC 4.2.1 Compatible Apple LLVM 7.3.0 (clang-703.0.31)]
Using /Users/bwatson/Projects/[removed]/ansible.cfg as config file

PLAYBOOK: interfaces_test.yml ***************************************************************************************************
1 plays in interfaces_test.yml

PLAY [all] **********************************************************************************************************************

TASK [Gathering Facts] **********************************************************************************************************
ok: [172.20.116.11]
META: ran handlers

TASK [interfaces_file] **********************************************************************************************************
task path: /Users/bwatson/Projects/[removed]/interfaces_test.yml:17
fatal: [172.20.116.11]: FAILED! => {"changed": false, "failed": true, "module_stderr": "Shared connection to 172.20.116.11 closed.\r\n", "module_stdout": "Traceback (most recent call last):\r\n  File \"/tmp/ansible_GxJ8k1/ansible_module_interfaces_file.py\", line 385, in <module>\r\n    main()\r\n  File \"/tmp/ansible_GxJ8k1/ansible_module_interfaces_file.py\", line 366, in main\r\n    lines, ifaces = read_interfaces_file(module, dest)\r\n  File \"/tmp/ansible_GxJ8k1/ansible_module_interfaces_file.py\", line 177, in read_interfaces_file\r\n    return read_interfaces_lines(module, f)\r\n  File \"/tmp/ansible_GxJ8k1/ansible_module_interfaces_file.py\", line 211, in read_interfaces_lines\r\n    iface_name, address_family_name, method_name = words[1:4]\r\nValueError: need more than 1 value to unpack\r\n", "msg": "MODULE FAILURE", "rc": 0}

PLAY RECAP **********************************************************************************************************************
172.20.116.11              : ok=1    changed=0    unreachable=0    failed=1
```



Discussion:
@hryamzik
Thanks @bobbywatson3, I'll take a look late next week once I'll get back from vacation. Looks wired as tests were green on dev branch.

Can I also ask you to run with `-vvvv` to get more details?

P.S.: please also share your `/etc/network/interfaces`, I'll check it with tests first.
needs_info
I’m away from my computer for the next few days. I’m guessing you weren’t able to duplicate this?

The interfaces file was super basic, just a loop back and eth0 configured for dhcp. 
Yep, it just works for me. Or predictably fails if I have eth0 in config and try to edit eth1.
Ok, I was able to figure out what's causing the bug. If there is an interface that doesn't have "inet static" or "inet dhcp" on the end of it, this bug will be encountered.

For example, this interfaces file works fine:
/etc/network/interfaces
```
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

source /etc/network/interfaces.d/*.intf

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
auto eth0
iface eth0 inet dhcp
    vrf mgmt
```

This one does not:
```
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

source /etc/network/interfaces.d/*.intf

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
auto eth0
iface eth0 inet dhcp
    vrf mgmt

auto mgmt
iface mgmt
    address 127.0.0.1/8
    vrf-table auto
```

Note that the bottom "mgmt" interface does not have "inet dhcp" or "inet static" after it. The mgmt interface is a requirement for configuring a management VRF.
Also note that this is still present in 2.4.2.0
I'm not sure if that's a valid file. I'll have to verify how it works in c code that reads these files.
This is how the management VRF must be configured on Cumulus Linux: https://docs.cumulusnetworks.com/m/view-rendered-page.action?abstractPageId=7113152
I see, this module is based on debian c code.
Cumulus is also Debian based. Do you think there's a compatibility issue?
In the official Ubuntu documentation, example interface files have both `br0` and `lo` without address family name (inet) or method specified: http://manpages.ubuntu.com/manpages/xenial/man5/interfaces.5.html

In the module's current state, it would be unable to parse these files at all.

Resolving patch:
diff --git a/lib/ansible/modules/system/interfaces_file.py b/lib/ansible/modules/system/interfaces_file.py
--- a/lib/ansible/modules/system/interfaces_file.py
+++ b/lib/ansible/modules/system/interfaces_file.py
@@ -208,14 +208,15 @@ def read_interfaces_lines(module, line_strings):
                 "down": [],
                 "post-up": []
             }
-            iface_name, address_family_name, method_name = words[1:4]
-            if len(words) != 4:
-                module.fail_json(msg="Incorrect number of parameters (%d) in line %d, must be exectly 3" % (len(words), i))
-                # TODO: put line and count parameters
-                return None, None
-
-            currif['address_family'] = address_family_name
-            currif['method'] = method_name
+            iface_name = words[1]
+            try:
+                currif['address_family'] = words[2]
+            except IndexError:
+                currif['address_family'] = None
+            try:
+                currif['method'] = words[3]
+            except IndexError:
+                currif['method'] = None
 
             ifaces[iface_name] = currif
             lines.append({'line': line, 'iface': iface_name, 'line_type': 'iface', 'params': currif})


Test patch: