Repository: ansible/ansible

Issue:
The ios_linkagg search for interfaces may return wrongs interfaces name
<!---
Verify first that your issue/request is not already reported on GitHub.
THIS FORM WILL BE READ BY A MACHINE, COMPLETE ALL SECTIONS AS DESCRIBED.
Also test if the latest release, and devel branch are affected too.
ALWAYS add information AFTER (OUTSIDE) these html comments.
Otherwise it may end up being automatically closed by our bot. -->

##### SUMMARY
<!--- Explain the problem briefly -->
We are trying to create a Port-channel with the ios_linkagg module, we found the way the interfaces are parsed with the regexp seems wrong, and it takes wrong interface name in other configuration section than the parent section.

##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
<!--- Insert, BELOW THIS COMMENT, the name of the module, plugin, task or feature.
Do not include extra details here, e.g. "vyos_command" not "the network module vyos_command" or the full path-->
ios_linkagg

##### ANSIBLE VERSION
<!--- Paste, BELOW THIS COMMENT, verbatim output from "ansible --version" between quotes below -->
```
ansible 2.5.3
  config file = /local/home/ta-admin-ng5898b/ansible.cfg
  configured module search path = [u'/local/home/ta-admin-ng5898b/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /dns/development/ctrebuchet_sandbox/zion_ansible/lib/python2.7/site-packages/ansible
  executable location = /dns/development/ctrebuchet_sandbox/zion_ansible/bin/ansible
  python version = 2.7.8 (default, Oct  9 2014, 10:48:46) [GCC 4.4.7 20120313 (Red Hat 4.4.7-4)]
```

##### CONFIGURATION
<!--- If using Ansible 2.4 or above, paste, BELOW THIS COMMENT, 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).-->

##### OS / ENVIRONMENT
<!--- Mention, BELOW THIS COMMENT, 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.-->
Red Hat Enterprise Linux Server release 6.9 (Santiago)
##### 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 -->
- Given a configuration like below:
```bash
!
interface TenGigabitEthernet1/5/1
 no switchport
 no ip address
 no cdp enable
 channel-group 1 mode on
!
interface TenGigabitEthernet1/5/2
 no switchport
 no ip address
 no cdp enable
 channel-group 1 mode on
!
interface TenGigabitEthernet1/5/3
 no switchport
 no ip address
 no cdp enable
 dual-active fast-hello
!
interface TenGigabitEthernet1/5/4
 description Link to m880gbca1
 no switchport
 mtu 9216
 no ip address
 logging event link-status
 logging event bundle-status
 channel-group 11 mode active
!
interface TenGigabitEthernet1/5/5
 no switchport
 no ip address
 shutdown
!
interface TenGigabitEthernet1/5/6
 no switchport
 no ip address
 logging event link-status
 logging event bundle-status
 shutdown
!
interface TenGigabitEthernet1/5/7
 no switchport
 no ip address
 logging event link-status
 logging event bundle-status
 shutdown
!
interface TenGigabitEthernet1/5/8
 no switchport
 no ip address
 shutdown
!
interface TenGigabitEthernet1/5/9
 no switchport
 no ip address
 shutdown
!
interface TenGigabitEthernet1/5/10
 no switchport
 no ip address
 shutdown
!
interface TenGigabitEthernet1/5/11
 no switchport
 no ip address
 shutdown
!
interface TenGigabitEthernet1/5/12
 no switchport
 no ip address
 shutdown
!
interface TenGigabitEthernet1/5/13
 no switchport
 no ip address
 shutdown
!
interface TenGigabitEthernet1/5/14
 no switchport
 no ip address
 shutdown
!
interface TenGigabitEthernet1/5/15
 no switchport
 no ip address
 shutdown
!
interface TenGigabitEthernet1/5/16
 no switchport
 no ip address
 shutdown
!
interface mgmt0
 ip address 10.126.127.51 255.255.255.0
!
interface TenGigabitEthernet2/5/1
 no switchport
 no ip address
 no cdp enable
 channel-group 2 mode on
!
interface TenGigabitEthernet2/5/2
 no switchport
 no ip address
 no cdp enable
 channel-group 2 mode on
!
interface TenGigabitEthernet2/5/3
 no switchport
 no ip address
 no cdp enable
 dual-active fast-hello
!
interface TenGigabitEthernet2/5/4
 description Link to m880gbca1
 no switchport
 mtu 9216
 no ip address
 logging event link-status
 logging event bundle-status
 channel-group 11 mode active
!
interface TenGigabitEthernet2/5/5
 no switchport
 no ip address
 shutdown
!
interface TenGigabitEthernet2/5/6
 no switchport
 no ip address
 logging event link-status
 logging event bundle-status
 shutdown
!
interface TenGigabitEthernet2/5/7
 no switchport
 no ip address
 logging event link-status
 logging event bundle-status
 shutdown
!
interface TenGigabitEthernet2/5/8
 no switchport
 no ip address
 shutdown
!
interface TenGigabitEthernet2/5/9
 no switchport
 no ip address
 shutdown
!
interface TenGigabitEthernet2/5/10
 no switchport
 no ip address
 shutdown
!
interface TenGigabitEthernet2/5/11
 no switchport
 no ip address
 shutdown
!
interface TenGigabitEthernet2/5/12
 no switchport
 no ip address
 shutdown
!
interface TenGigabitEthernet2/5/13
 no switchport
 no ip address
 shutdown
!
interface TenGigabitEthernet2/5/14
 no switchport
 no ip address
 shutdown
!
interface TenGigabitEthernet2/5/15
 no switchport
 no ip address
 shutdown
!
interface TenGigabitEthernet2/5/16
 no switchport
 no ip address
 shutdown
!
interface Vlan1
 no ip address
 shutdown
!
router ospf 1
 router-id 10.126.16.4
 passive-interface default
 no passive-interface Port-channel11
 network 0.0.0.0 255.255.255.255 area 0
!
```
- Given a task like below:

```yaml
 - name: 4/ create link aggregation group.
    ios_linkagg:
      group: "{{ item.group }}"
      state: present
    loop: "{{ network_interfaces }}"
```
- Given variables like below:

```yaml
network_interfaces:
    - name: "Port-channel100"
      group: 100
      interface_1: "TenGigabitEthernet1/5/6"
      interface_2: "TenGigabitEthernet2/5/6"

    - name: "Port-channel101"
      group: "101"
      interface_1: "TenGigabitEthernet1/5/7"
      interface_2: "TenGigabitEthernet2/5/7"

```


<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
- The PO is created
##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->
- The PO is not created
<!--- Paste verbatim command output between quotes below -->
- When the playbook run we get this error:
```bash
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: TypeError: expected string or buffer
failed: [smm88-mockup-cudi-1.mgt.airbus.corp] (item={'name': 'Port-channel100', 'group': 100, 'interface_1': 'TenGigabitEthernet1/5/6', 'interface_2': 'TenGigabitEthernet2/5/6'}) => changed=false 
  item:
    group: 100
    interface_1: TenGigabitEthernet1/5/6
    interface_2: TenGigabitEthernet2/5/6
    name: Port-channel100
  module_stderr: |-
    Traceback (most recent call last):
      File "/tmp/ansible_vTMuhg/ansible_module_ios_linkagg.py", line 315, in <module>
        main()
      File "/tmp/ansible_vTMuhg/ansible_module_ios_linkagg.py", line 302, in main
        have = map_config_to_obj(module)
      File "/tmp/ansible_vTMuhg/ansible_module_ios_linkagg.py", line 254, in map_config_to_obj
        obj.update(get_channel(module, config, group))
      File "/tmp/ansible_vTMuhg/ansible_module_ios_linkagg.py", line 237, in get_channel
        channel['mode'] = parse_mode(module, config, group, member)
      File "/tmp/ansible_vTMuhg/ansible_module_ios_linkagg.py", line 204, in parse_mode
        match_int = re.findall(r'interface {0}\n'.format(member), body, re.M)
      File "/usr/lib/python2.7/re.py", line 181, in findall
        return _compile(pattern, flags).findall(string)
    TypeError: expected string or buffer
  module_stdout: ''
  msg: MODULE FAILURE
  rc: 1
```

- It seems that changing the regexp at the get_channel function did the tricks

https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/network/ios/ios_linkagg.py#L230

```python

def get_channel(module, config, group):
    match = re.findall(r'interface (\S+)', config, re.M)

def get_channel(module, config, group):
    match = re.findall(r'^interface (\S+)', config, re.M)

```
- With a "global" findall wrongs interfaces names (default and Port-channel11) are taken from the router ospf section.

```yaml
!
router ospf 1
 router-id 10.126.16.4
 passive-interface default
 no passive-interface Port-channel11
 network 0.0.0.0 255.255.255.255 area 0
!
```
```python
>>> matches = re.findall(r'interface (\S+)', config, re.M)
>>> matches
['Loopback0', 'Loopback1', 'Loopback12008', 'Port-channel1', 'Port-channel2', 'Port-channel11', 'Port-channel12', 'Tunnel1', 'TenGigabitEthernet1/5/1', 'TenGigabitEthernet1/5/2', 'TenGigabitEthernet1/5/3', 'TenGigabitEthernet1/5/4', 'TenGigabitEthernet1/5/5', 'TenGigabitEthernet1/5/6', 'TenGigabitEthernet1/5/7', 'TenGigabitEthernet1/5/8', 'TenGigabitEthernet1/5/9', 'TenGigabitEthernet1/5/10', 'TenGigabitEthernet1/5/11', 'TenGigabitEthernet1/5/12', 'TenGigabitEthernet1/5/13', 'TenGigabitEthernet1/5/14', 'TenGigabitEthernet1/5/15', 'TenGigabitEthernet1/5/16', 'mgmt0', 'TenGigabitEthernet2/5/1', 'TenGigabitEthernet2/5/2', 'TenGigabitEthernet2/5/3', 'TenGigabitEthernet2/5/4', 'TenGigabitEthernet2/5/5', 'TenGigabitEthernet2/5/6', 'TenGigabitEthernet2/5/7', 'TenGigabitEthernet2/5/8', 'TenGigabitEthernet2/5/9', 'TenGigabitEthernet2/5/10', 'TenGigabitEthernet2/5/11', 'TenGigabitEthernet2/5/12', 'TenGigabitEthernet2/5/13', 'TenGigabitEthernet2/5/14', 'TenGigabitEthernet2/5/15', 'TenGigabitEthernet2/5/16', 'Vlan1', 'default', 'Port-channel11', 'mgmt0']
```
- Changing the regexp to take only line which begin with interface works.

```python
>>> matches = re.findall(r'^interface (\S+)', config, re.M)
>>> matches
['Loopback0', 'Loopback1', 'Loopback12008', 'Port-channel1', 'Port-channel2', 'Port-channel11', 'Port-channel12', 'Tunnel1', 'TenGigabitEthernet1/5/1', 'TenGigabitEthernet1/5/2', 'TenGigabitEthernet1/5/3', 'TenGigabitEthernet1/5/4', 'TenGigabitEthernet1/5/5', 'TenGigabitEthernet1/5/6', 'TenGigabitEthernet1/5/7', 'TenGigabitEthernet1/5/8', 'TenGigabitEthernet1/5/9', 'TenGigabitEthernet1/5/10', 'TenGigabitEthernet1/5/11', 'TenGigabitEthernet1/5/12', 'TenGigabitEthernet1/5/13', 'TenGigabitEthernet1/5/14', 'TenGigabitEthernet1/5/15', 'TenGigabitEthernet1/5/16', 'mgmt0', 'TenGigabitEthernet2/5/1', 'TenGigabitEthernet2/5/2', 'TenGigabitEthernet2/5/3', 'TenGigabitEthernet2/5/4', 'TenGigabitEthernet2/5/5', 'TenGigabitEthernet2/5/6', 'TenGigabitEthernet2/5/7', 'TenGigabitEthernet2/5/8', 'TenGigabitEthernet2/5/9', 'TenGigabitEthernet2/5/10', 'TenGigabitEthernet2/5/11', 'TenGigabitEthernet2/5/12', 'TenGigabitEthernet2/5/13', 'TenGigabitEthernet2/5/14', 'TenGigabitEthernet2/5/15', 'TenGigabitEthernet2/5/16', 'Vlan1']
```



Discussion:
Files identified in the description:
* [lib/ansible/modules/network/ios/ios_linkagg.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/network/ios/ios_linkagg.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @privateip @rcarrillocruz @trishnaguha
[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/network/ios/ios_linkagg.py b/lib/ansible/modules/network/ios/ios_linkagg.py
--- a/lib/ansible/modules/network/ios/ios_linkagg.py
+++ b/lib/ansible/modules/network/ios/ios_linkagg.py
@@ -227,7 +227,7 @@ def parse_members(module, config, group):
 
 
 def get_channel(module, config, group):
-    match = re.findall(r'interface (\S+)', config, re.M)
+    match = re.findall(r'^interface (\S+)', config, re.M)
 
     if not match:
         return {}


Test patch: