ios_facts: Gather CDP neighbor data (#49129)

* ios_facts: Gather CDP neighbor data

* ios_facts: Create tests for ansible_net_neighbors
pull/49333/head
Paul Neumann 6 years ago committed by Nathaniel Case
parent a51eca364f
commit a914f494a8

@ -142,7 +142,9 @@ ansible_net_interfaces:
returned: when interfaces is configured returned: when interfaces is configured
type: dict type: dict
ansible_net_neighbors: ansible_net_neighbors:
description: The list of LLDP neighbors from the remote device description:
- The list of CDP and LLDP neighbors from the remote device. If both,
CDP and LLDP neighbor data is present on one port, CDP is preferred.
returned: when interfaces is configured returned: when interfaces is configured
type: dict type: dict
""" """
@ -150,6 +152,7 @@ import re
from ansible.module_utils.network.ios.ios import run_commands from ansible.module_utils.network.ios.ios import run_commands
from ansible.module_utils.network.ios.ios import ios_argument_spec, check_args from ansible.module_utils.network.ios.ios import ios_argument_spec, check_args
from ansible.module_utils.network.ios.ios import normalize_interface
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.six import iteritems from ansible.module_utils.six import iteritems
from ansible.module_utils.six.moves import zip from ansible.module_utils.six.moves import zip
@ -294,7 +297,8 @@ class Interfaces(FactsBase):
'show interfaces', 'show interfaces',
'show ip interface', 'show ip interface',
'show ipv6 interface', 'show ipv6 interface',
'show lldp' 'show lldp',
'show cdp'
] ]
def populate(self): def populate(self):
@ -302,6 +306,7 @@ class Interfaces(FactsBase):
self.facts['all_ipv4_addresses'] = list() self.facts['all_ipv4_addresses'] = list()
self.facts['all_ipv6_addresses'] = list() self.facts['all_ipv6_addresses'] = list()
self.facts['neighbors'] = {}
data = self.responses[0] data = self.responses[0]
if data: if data:
@ -324,7 +329,15 @@ class Interfaces(FactsBase):
if data and not any(err in data for err in lldp_errs): if data and not any(err in data for err in lldp_errs):
neighbors = self.run(['show lldp neighbors detail']) neighbors = self.run(['show lldp neighbors detail'])
if neighbors: if neighbors:
self.facts['neighbors'] = self.parse_neighbors(neighbors[0]) self.facts['neighbors'].update(self.parse_neighbors(neighbors[0]))
data = self.responses[4]
cdp_errs = ['CDP is not enabled']
if data and not any(err in data for err in cdp_errs):
cdp_neighbors = self.run(['show cdp neighbors detail'])
if cdp_neighbors:
self.facts['neighbors'].update(self.parse_cdp_neighbors(cdp_neighbors[0]))
def populate_interfaces(self, interfaces): def populate_interfaces(self, interfaces):
facts = dict() facts = dict()
@ -387,6 +400,7 @@ class Interfaces(FactsBase):
intf = self.parse_lldp_intf(entry) intf = self.parse_lldp_intf(entry)
if intf is None: if intf is None:
return facts return facts
intf = normalize_interface(intf)
if intf not in facts: if intf not in facts:
facts[intf] = list() facts[intf] = list()
fact = dict() fact = dict()
@ -395,6 +409,23 @@ class Interfaces(FactsBase):
facts[intf].append(fact) facts[intf].append(fact)
return facts return facts
def parse_cdp_neighbors(self, neighbors):
facts = dict()
for entry in neighbors.split('-------------------------'):
if entry == '':
continue
intf_port = self.parse_cdp_intf_port(entry)
if intf_port is None:
return facts
intf, port = intf_port
if intf not in facts:
facts[intf] = list()
fact = dict()
fact['host'] = self.parse_cdp_host(entry)
fact['port'] = port
facts[intf].append(fact)
return facts
def parse_interfaces(self, data): def parse_interfaces(self, data):
parsed = dict() parsed = dict()
key = '' key = ''
@ -476,6 +507,16 @@ class Interfaces(FactsBase):
if match: if match:
return match.group(1) return match.group(1)
def parse_cdp_intf_port(self, data):
match = re.search(r'^Interface: (.+), Port ID \(outgoing port\): (.+)$', data, re.M)
if match:
return match.group(1), match.group(2)
def parse_cdp_host(self, data):
match = re.search(r'^Device ID: (.+)$', data, re.M)
if match:
return match.group(1)
FACT_SUBSETS = dict( FACT_SUBSETS = dict(
default=Default, default=Default,

@ -0,0 +1,4 @@
Global CDP information:
Sending CDP packets every 60 seconds
Sending a holdtime value of 180 seconds
Sending CDPv2 advertisements is enabled

@ -0,0 +1,40 @@
-------------------------
Device ID: R2
Entry address(es):
IP address: 10.0.0.3
Platform: cisco CSR1000V, Capabilities: Router IGMP
Interface: GigabitEthernet1, Port ID (outgoing port): GigabitEthernet2
Holdtime : 149 sec
Version :
Cisco IOS Software [Everest], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Version 16.6.4, RELEASE SOFTWARE (fc3)
Technical Support: http://www.cisco.com/techsupport
Copyright (c) 1986-2018 by Cisco Systems, Inc.
Compiled Sun 08-Jul-18 04:30 by mcpre
advertisement version: 2
Duplex: full
Management address(es):
IP address: 10.0.0.3
-------------------------
Device ID: R3
Entry address(es):
IP address: 10.0.0.4
Platform: cisco CSR1000V, Capabilities: Router IGMP
Interface: GigabitEthernet1, Port ID (outgoing port): GigabitEthernet3
Holdtime : 149 sec
Version :
Cisco IOS Software [Everest], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Version 16.6.4, RELEASE SOFTWARE (fc3)
Technical Support: http://www.cisco.com/techsupport
Copyright (c) 1986-2018 by Cisco Systems, Inc.
Compiled Sun 08-Jul-18 04:30 by mcpre
advertisement version: 2
Duplex: full
Management address(es):
IP address: 10.0.0.4
Total cdp entries displayed : 2

@ -0,0 +1,6 @@
Global LLDP Information:
Status: ACTIVE
LLDP advertisements are sent every 30 seconds
LLDP hold time advertised is 120 seconds
LLDP interface reinitialisation delay is 2 seconds

@ -0,0 +1,50 @@
------------------------------------------------
Local Intf: Gi1
Chassis id: 001e.14d4.5300
Port id: Gi3
Port Description: GigabitEthernet3
System Name: R3
System Description:
Cisco IOS Software [Everest], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Version 16.6.4, RELEASE SOFTWARE (fc3)
Technical Support: http://www.cisco.com/techsupport
Copyright (c) 1986-2018 by Cisco Systems, Inc.
Compiled Sun 08-Jul-18 04:30 by
Time remaining: 116 seconds
System Capabilities: B,R
Enabled Capabilities: R
Management Addresses:
IP: 10.0.0.4
Auto Negotiation - not supported
Physical media capabilities - not advertised
Media Attachment Unit type - not advertised
Vlan ID: - not advertised
------------------------------------------------
Local Intf: Gi3
Chassis id: 001e.e6c9.6d00
Port id: Gi1
Port Description: GigabitEthernet1
System Name: Rtest
System Description:
Cisco IOS Software [Everest], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Version 16.6.4, RELEASE SOFTWARE (fc3)
Technical Support: http://www.cisco.com/techsupport
Copyright (c) 1986-2018 by Cisco Systems, Inc.
Compiled Sun 08-Jul-18 04:30 by
Time remaining: 116 seconds
System Capabilities: B,R
Enabled Capabilities: R
Management Addresses:
IP: 10.3.0.3
Auto Negotiation - not supported
Physical media capabilities - not advertised
Media Attachment Unit type - not advertised
Vlan ID: - not advertised
Total entries displayed: 2

@ -19,6 +19,7 @@ __metaclass__ = type
from units.compat.mock import patch from units.compat.mock import patch
from ansible.modules.network.ios import ios_facts from ansible.modules.network.ios import ios_facts
from ansible.module_utils.six import assertCountEqual
from units.modules.utils import set_module_args from units.modules.utils import set_module_args
from .ios_module import TestIosModule, load_fixture from .ios_module import TestIosModule, load_fixture
@ -87,3 +88,20 @@ class TestIosFactsModule(TestIosModule):
self.assertEqual( self.assertEqual(
result['ansible_facts']['ansible_net_filesystems_info']['bootflash:']['spacefree_kb'], 6453180.0 result['ansible_facts']['ansible_net_filesystems_info']['bootflash:']['spacefree_kb'], 6453180.0
) )
def test_ios_facts_neighbors(self):
set_module_args(dict(gather_subset='interfaces'))
result = self.execute_module()
assertCountEqual(
self,
result['ansible_facts']['ansible_net_neighbors'].keys(), ['GigabitEthernet1', 'GigabitEthernet3']
)
assertCountEqual(
self,
result['ansible_facts']['ansible_net_neighbors']['GigabitEthernet1'],
[{'host': 'R2', 'port': 'GigabitEthernet2'}, {'host': 'R3', 'port': 'GigabitEthernet3'}]
)
assertCountEqual(
self,
result['ansible_facts']['ansible_net_neighbors']['GigabitEthernet3'], [{'host': 'Rtest', 'port': 'Gi1'}]
)

Loading…
Cancel
Save