From a914f494a84382fdbeb30f118a6ff09480264c51 Mon Sep 17 00:00:00 2001 From: Paul Neumann Date: Thu, 29 Nov 2018 20:05:17 +0100 Subject: [PATCH] ios_facts: Gather CDP neighbor data (#49129) * ios_facts: Gather CDP neighbor data * ios_facts: Create tests for ansible_net_neighbors --- lib/ansible/modules/network/ios/ios_facts.py | 47 +++++++++++++++-- .../network/ios/fixtures/ios_facts_show_cdp | 4 ++ .../ios_facts_show_cdp_neighbors_detail | 40 +++++++++++++++ .../network/ios/fixtures/ios_facts_show_lldp | 6 +++ .../ios_facts_show_lldp_neighbors_detail | 50 +++++++++++++++++++ .../modules/network/ios/test_ios_facts.py | 18 +++++++ 6 files changed, 162 insertions(+), 3 deletions(-) create mode 100644 test/units/modules/network/ios/fixtures/ios_facts_show_cdp create mode 100644 test/units/modules/network/ios/fixtures/ios_facts_show_cdp_neighbors_detail create mode 100644 test/units/modules/network/ios/fixtures/ios_facts_show_lldp_neighbors_detail diff --git a/lib/ansible/modules/network/ios/ios_facts.py b/lib/ansible/modules/network/ios/ios_facts.py index dd968dfed5..33c3ce220a 100644 --- a/lib/ansible/modules/network/ios/ios_facts.py +++ b/lib/ansible/modules/network/ios/ios_facts.py @@ -142,7 +142,9 @@ ansible_net_interfaces: returned: when interfaces is configured type: dict 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 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 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.six import iteritems from ansible.module_utils.six.moves import zip @@ -294,7 +297,8 @@ class Interfaces(FactsBase): 'show interfaces', 'show ip interface', 'show ipv6 interface', - 'show lldp' + 'show lldp', + 'show cdp' ] def populate(self): @@ -302,6 +306,7 @@ class Interfaces(FactsBase): self.facts['all_ipv4_addresses'] = list() self.facts['all_ipv6_addresses'] = list() + self.facts['neighbors'] = {} data = self.responses[0] if data: @@ -324,7 +329,15 @@ class Interfaces(FactsBase): if data and not any(err in data for err in lldp_errs): neighbors = self.run(['show lldp neighbors detail']) 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): facts = dict() @@ -387,6 +400,7 @@ class Interfaces(FactsBase): intf = self.parse_lldp_intf(entry) if intf is None: return facts + intf = normalize_interface(intf) if intf not in facts: facts[intf] = list() fact = dict() @@ -395,6 +409,23 @@ class Interfaces(FactsBase): facts[intf].append(fact) 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): parsed = dict() key = '' @@ -476,6 +507,16 @@ class Interfaces(FactsBase): if match: 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( default=Default, diff --git a/test/units/modules/network/ios/fixtures/ios_facts_show_cdp b/test/units/modules/network/ios/fixtures/ios_facts_show_cdp new file mode 100644 index 0000000000..c5fff7d278 --- /dev/null +++ b/test/units/modules/network/ios/fixtures/ios_facts_show_cdp @@ -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 diff --git a/test/units/modules/network/ios/fixtures/ios_facts_show_cdp_neighbors_detail b/test/units/modules/network/ios/fixtures/ios_facts_show_cdp_neighbors_detail new file mode 100644 index 0000000000..b3d5453dd2 --- /dev/null +++ b/test/units/modules/network/ios/fixtures/ios_facts_show_cdp_neighbors_detail @@ -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 diff --git a/test/units/modules/network/ios/fixtures/ios_facts_show_lldp b/test/units/modules/network/ios/fixtures/ios_facts_show_lldp index e69de29bb2..09847c318c 100644 --- a/test/units/modules/network/ios/fixtures/ios_facts_show_lldp +++ b/test/units/modules/network/ios/fixtures/ios_facts_show_lldp @@ -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 diff --git a/test/units/modules/network/ios/fixtures/ios_facts_show_lldp_neighbors_detail b/test/units/modules/network/ios/fixtures/ios_facts_show_lldp_neighbors_detail new file mode 100644 index 0000000000..adc4f30e46 --- /dev/null +++ b/test/units/modules/network/ios/fixtures/ios_facts_show_lldp_neighbors_detail @@ -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 + + diff --git a/test/units/modules/network/ios/test_ios_facts.py b/test/units/modules/network/ios/test_ios_facts.py index cb5f82539e..b1154b2ff4 100644 --- a/test/units/modules/network/ios/test_ios_facts.py +++ b/test/units/modules/network/ios/test_ios_facts.py @@ -19,6 +19,7 @@ __metaclass__ = type from units.compat.mock import patch from ansible.modules.network.ios import ios_facts +from ansible.module_utils.six import assertCountEqual from units.modules.utils import set_module_args from .ios_module import TestIosModule, load_fixture @@ -87,3 +88,20 @@ class TestIosFactsModule(TestIosModule): self.assertEqual( 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'}] + )