Repository: ansible/ansible

Issue:
eos_facts: TypeError: expected string or buffer in populate_filesystems
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
eos_facts

##### ANSIBLE VERSION
```
ansible-playbook 2.4.0 (devel 224b5d5283) last updated 2017/04/03 13:46:39 (GMT +100)
  config file = /Users/lynchg/git/arista/veos/single_switch/ansible.cfg
  configured module search path = [u'/Users/lynchg/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  python version = 2.7.13 (default, Dec 18 2016, 07:03:34) [GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)]
```

##### CONFIGURATION

##### OS / ENVIRONMENT
Mac OS Yosemite
pip virtualenv
Python 2.7.13
Virtualbox
Vagrant
vEOS-lab-4.17.5M / 4.18.0F

##### SUMMARY
Using eos_facts against an Arista vEOS switch gives a TypeError

##### STEPS TO REPRODUCE
- Download and add an arista vEOS-lab box to vagrant
- Create the following Vagrantfile

```ruby
# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure('2') do |config|

  config.vm.define 'switch-1' do |node|
    node.vm.box = 'vEOS-lab'
    node.vm.box_download_insecure = true

    node.ssh.insert_key = false

    # Disable synced folders
    node.vm.synced_folder '.', '/vagrant', disabled: true

    node.vm.provider 'virtualbox' do |v, override|
      v.memory = 2048
      v.cpus = 1
    end

    # add additional interfaces
    # use link-local addresses to satisfy the Vagrantfile config parser.  They will not be used by vEOS.
    node.vm.network 'private_network', virtualbox__hostonly: 'vboxnet0', ip: '192.168.56.201', auto_config: false

    # configure hostname and eAPI
    # because vagrant uses nic1 for configuration, and Management1 is on eth0/nic1
    # we reserve eth1/nic2 for mgmt
    node.vm.provision 'shell', inline: <<-SCRIPT
      FastCli -p 15 -c "configure
      hostname switch-1
      username eapiuser privilege 15 role network-admin secret icanttellyou
      management api http-commands
        no shutdown
        no protocol https
        protocol http
        exit
      interface Ethernet1
        no switchport
        ip address 192.168.56.201/24
      end
      copy running-config startup-config"
    SCRIPT

  end
end
```
- create the following play.yml

```yaml
---
- hosts: switch-1
  connection: local
  gather_facts: no
  vars:
    eos_connection:
      host: "{{ ansible_ssh_host }}"
      port: "80"
      username: "eapiuser"
      password: "icanttellyou"
      transport: eapi
      use_ssl: false
      validate_certs: false
  tasks:
    - eos_facts:
        gather_subset: all
        provider: "{{ eos_connection }}"
      register: result

    - debug: var=result
```

##### EXPECTED RESULTS
I should seem something like the following
```
ok: [switch-1] => {
    "changed": false,
    "result": {
        "ansible_facts": {
            "ansible_net_all_ipv4_addresses": [
                "********.0.2.15",
                "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER"
            ],
            "ansible_net_all_ipv6_addresses": [],
            "ansible_net_config": {
                "cmds": {
                    "! boot system flash:/vEOS-lab.swi": null,
                    "aaa authorization exec default local": null,
                    "aaa root secret sha512 $6$upXd4fzyxWlXQE0H$dyOq4lq5SNIXBDDJ8c6Dskog2LLTEHsL2SgD1amQMwFfJ/QT9hlpziTqtG0m7ZAImzF8F6.G0Wjq7Ob4ZwiG41": null,
                    "event-handler dhclient": {
                        "cmds": {
                            "action bash sudo /mnt/flash/initialize_ma1.sh": null,
                            "trigger on-boot": null
                        },
                        "comments": []
                    },
                    "hostname switch-1": null,
                    "interface Ethernet1": {
                        "cmds": {
                            "ip address 192.168.56.201/24": null,
                            "no switchport": null
                        },
                        "comments": []
                    },
                    "interface Management1": {
                        "cmds": {
                            "ip address 10.0.2.15/24": null
                        },
                        "comments": []
                    },
                    "management api http-commands": {
                        "cmds": {
                            "no protocol https": null,
                            "no shutdown": null,
                            "protocol http": null
                        },
                        "comments": []
                    },
                    "no ip routing": null,
                    "spanning-tree mode mstp": null,
                    "transceiver qsfp default-mode 4x10G": null,
                    "username admin privilege 15 role network-admin secret sha512 $6$mR3qiYpuA7UB5Too$Fb0evmXxRG87y0aalNBRXulePKc5QBbJnUKYcH7.DzEuI19mLBQ5Uua8QLg4qNLJVQsZaghcsaEO/Pjn31rhi.": null,
                    "username eapiuser privilege 15 role network-admin secret sha512 $6$VELDkFzgr/rwS.Dn$h0LlXoFkNlys85HnjF3r2huzaM49XGc39yfCn2RXvv7Bm56gGA6e4GT.bRo3fCGsziQuOiO0VBaFif5A304u51": null,
                    "username vagrant privilege 15 role network-admin secret sha512 $6$9KSEHe7DZ/ky5JTz$KhrPqTbx4sX4TeOdlCHe5shUqGvAUfiS2GVUmDSEgjLGjwQ1HRcNhqjEVaynfd4h6boTE2E2QYA4.aKHak8us.": null
                },
                "comments": [],
                "header": [
                    "! device: switch-1 (vEOS, EOS-4.17.5M)\n!\n"
                ]
            },
            "ansible_net_filesystems": [
                "file:",
                "flash:",
                "system:"
            ],
            "ansible_net_fqdn": "switch-1",
            "ansible_net_gather_subset": [
                "hardware",
                "default",
                "interfaces",
                "config"
            ],
            "ansible_net_hostname": "switch-1",
            "ansible_net_image": "flash:/vEOS-lab.swi",
            "ansible_net_interfaces": {
                "Ethernet1": {
                    "bandwidth": 0,
                    "description": "",
                    "duplex": "duplexFull",
                    "ipv4": {
                        "address": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
                        "masklen": 24
                    },
                    "lineprotocol": "up",
                    "macaddress": "08:00:27:0c:31:79",
                    "mtu": 1500,
                    "operstatus": "connected",
                    "type": "routed"
                },
                "Management1": {
                    "bandwidth": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
                    "description": "",
                    "duplex": "duplexFull",
                    "ipv4": {
                        "address": "********.0.2.15",
                        "masklen": 24
                    },
                    "lineprotocol": "up",
                    "macaddress": "08:00:27:47:87:83",
                    "mtu": 1500,
                    "operstatus": "connected",
                    "type": "routed"
                }
            },
            "ansible_net_memfree_mb": 945,
            "ansible_net_memtotal_mb": 1849,
            "ansible_net_model": "vEOS",
            "ansible_net_neighbors": {},
            "ansible_net_serialnum": "",
            "ansible_net_version": "4.17.5M"
        },
        "changed": false
    }
}
META: ran handlers
META: ran handlers

PLAY RECAP ****************************************************************************************************************************
switch-1                   : ok=2    changed=0    unreachable=0    failed=0
```

##### ACTUAL RESULTS

I get the following

```
TASK [eos_facts] **********************************************************************************************************************
task path: /Users/lynchg/git/arista/veos/single_switch/tst.yml:23
<192.168.56.201> connection transport is eapi
Using module file /Users/lynchg/git/github.com/ansible/ansible/lib/ansible/modules/network/eos/eos_facts.py
<192.168.56.201> ESTABLISH LOCAL CONNECTION FOR USER: lynchg
<192.168.56.201> EXEC /bin/sh -c 'echo ~ && sleep 0'
<192.168.56.201> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /Users/lynchg/.ansible/tmp/ansible-tmp-1491224118.54-180014088550831 `" && echo ansible-tmp-1491224118.54-180014088550831="` echo /Users/lynchg/.ansible/tmp/ansible-tmp-1491224118.54-180014088550831 `" ) && sleep 0'
<192.168.56.201> PUT /var/folders/y9/tdhfsb6n3nvg1frs229pq4cds79p1v/T/tmp7j07b9 TO /Users/lynchg/.ansible/tmp/ansible-tmp-1491224118.54-180014088550831/eos_facts.py
<192.168.56.201> EXEC /bin/sh -c 'chmod u+x /Users/lynchg/.ansible/tmp/ansible-tmp-1491224118.54-180014088550831/ /Users/lynchg/.ansible/tmp/ansible-tmp-1491224118.54-180014088550831/eos_facts.py && sleep 0'
<192.168.56.201> EXEC /bin/sh -c '/usr/bin/python /Users/lynchg/.ansible/tmp/ansible-tmp-1491224118.54-180014088550831/eos_facts.py; rm -rf "/Users/lynchg/.ansible/tmp/ansible-tmp-1491224118.54-180014088550831/" > /dev/null 2>&1 && sleep 0'
The full traceback is:
Traceback (most recent call last):
  File "/var/folders/y9/tdhfsb6n3nvg1frs229pq4cds79p1v/T/ansible_tUAZjP/ansible_module_eos_facts.py", line 378, in <module>
    main()
  File "/var/folders/y9/tdhfsb6n3nvg1frs229pq4cds79p1v/T/ansible_tUAZjP/ansible_module_eos_facts.py", line 366, in main
    inst.populate()
  File "/var/folders/y9/tdhfsb6n3nvg1frs229pq4cds79p1v/T/ansible_tUAZjP/ansible_module_eos_facts.py", line 199, in populate
    self.facts.update(self.populate_filesystems())
  File "/var/folders/y9/tdhfsb6n3nvg1frs229pq4cds79p1v/T/ansible_tUAZjP/ansible_module_eos_facts.py", line 205, in populate_filesystems
    fs = re.findall(r'^Directory of (.+)/', data, re.M)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/re.py", line 181, in findall
    return _compile(pattern, flags).findall(string)
TypeError: expected string or buffer

fatal: [switch-1]: FAILED! => {
    "changed": false,
    "failed": true,
    "module_stderr": "Traceback (most recent call last):\n  File \"/var/folders/y9/tdhfsb6n3nvg1frs229pq4cds79p1v/T/ansible_tUAZjP/ansible_module_eos_facts.py\", line 378, in <module>\n    main()\n  File \"/var/folders/y9/tdhfsb6n3nvg1frs229pq4cds79p1v/T/ansible_tUAZjP/ansible_module_eos_facts.py\", line 366, in main\n    inst.populate()\n  File \"/var/folders/y9/tdhfsb6n3nvg1frs229pq4cds79p1v/T/ansible_tUAZjP/ansible_module_eos_facts.py\", line 199, in populate\n    self.facts.update(self.populate_filesystems())\n  File \"/var/folders/y9/tdhfsb6n3nvg1frs229pq4cds79p1v/T/ansible_tUAZjP/ansible_module_eos_facts.py\", line 205, in populate_filesystems\n    fs = re.findall(r'^Directory of (.+)/', data, re.M)\n  File \"/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/re.py\", line 181, in findall\n    return _compile(pattern, flags).findall(string)\nTypeError: expected string or buffer\n",
    "module_stdout": "",
    "msg": "MODULE FAILURE",
    "rc": 0
}

PLAY RECAP ****************************************************************************************************************************
switch-1                   : ok=0    changed=0    unreachable=0    failed=1
```

Investigated, the issue seems to be https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/network/eos/eos_facts.py#L203

```
print(type(data)) #      "module_stdout": "<type 'dict'>\n",
print(data.keys()) #     "module_stdout": "[u'messages']\n",
```

Changing the line to `data = self.responses[0]['messages'][0]`, i.e.

```python
    def populate_filesystems(self):
        # data = self.responses[0]
        data = self.responses[0]['messages'][0]
        fs = re.findall(r'^Directory of (.+)/', data, re.M)
        return dict(filesystems=fs)
```
works


Discussion:
cc @privateip
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
@halberom Thanks for the bug report and analysis.

Marking as Milestone 2.3

Resolving patch:
diff --git a/lib/ansible/modules/network/eos/eos_facts.py b/lib/ansible/modules/network/eos/eos_facts.py
--- a/lib/ansible/modules/network/eos/eos_facts.py
+++ b/lib/ansible/modules/network/eos/eos_facts.py
@@ -201,6 +201,10 @@ def populate(self):
 
     def populate_filesystems(self):
         data = self.responses[0]
+
+        if isinstance(data, dict):
+            data = data['messages'][0]
+
         fs = re.findall(r'^Directory of (.+)/', data, re.M)
         return dict(filesystems=fs)
 


Test patch: