#!/usr/bin/env python3 # # Copyright (C) 2019 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import argparse import jinja2 from xml.dom import minidom from sys import exit from tabulate import tabulate from vyos.util import popen parser = argparse.ArgumentParser() parser.add_argument("-a", "--all", action="store_true", help="Show LLDP neighbors on all interfaces") parser.add_argument("-i", "--interface", action="store", help="Show LLDP neighbors on specific interface") # Please be careful if you edit the template. lldp_out = """Capability Codes: R - Router, B - Bridge, W - Wlan r - Repeater, S - Station D - Docsis, T - Telephone, O - Other Device ID Local Proto Cap Platform Port ID --------- ----- ----- --- -------- ------- {% for n in neighbors -%} {{ "%-25s" | format(n.chassis) }} {{ "%-9s" | format(n.interface) }} {{ "%-6s" | format(n.proto) }} {{ "%-5s" | format(n.cap) }} {{ "%-20s" | format(n.platform) }} {{ n.port }} {% endfor -%} """ def _get_neighbors(): command = '/usr/sbin/lldpcli -f xml show neighbors' out,_ = popen(command) return out def extract_neighbor(neighbor): """ Extract LLDP neighbor information from XML document passed as param neighbor 00:50:56:9d:a6:11 VyOS VyOS unknown 172.18.254.203 fe80::250:56ff:fe9d:a611 00:50:56:9d:a6:11 eth0 120 10GigBaseCX4 - X copper over 8 pair 100-Ohm balanced cable eth0.203 Network Connectivity Device None 4.19.54-amd64-vyos 6.00 VMware-42 1d cf 87 ab 7f da 7e-3 VMware, Inc. VMware Virtual Platform No Asset Tag """ device = { 'interface' : neighbor.getAttribute('name'), 'chassis' : '', 'proto' : neighbor.getAttribute('via'), 'descr' : '', 'cap' : '', 'platform' : '', 'port' : '' } # first change to node and then retrieve and chassis = neighbor.getElementsByTagName('chassis') device['chassis'] = chassis[0].getElementsByTagName('name')[0].firstChild.data # Cisco IOS comes with a ',' remove character .... device['platform'] = chassis[0].getElementsByTagName('descr')[0].firstChild.data[:20].replace(',',' ') # extract capabilities for capability in chassis[0].getElementsByTagName('capability'): # we are only interested in enabled capabilities ... if capability.getAttribute('enabled') == "on": if capability.getAttribute('type') == "Router": device['cap'] += 'R' elif capability.getAttribute('type') == "Bridge": device['cap'] += 'B' elif capability.getAttribute('type') == "Wlan": device['cap'] += 'W' elif capability.getAttribute('type') == "Station": device['cap'] += 'S' elif capability.getAttribute('type') == "Repeater": device['cap'] += 'r' elif capability.getAttribute('type') == "Telephone": device['cap'] += 'T' elif capability.getAttribute('type') == "Docsis": device['cap'] += 'D' elif capability.getAttribute('type') == "Other": device['cap'] += 'O' # first change to node and then retrieve port = neighbor.getElementsByTagName('port') port = port[0].getElementsByTagName('descr')[0].firstChild.data device['port'] = port return device if __name__ == '__main__': args = parser.parse_args() tmp = { 'neighbors' : [] } if args.all: neighbors = minidom.parseString(_get_neighbors()) for neighbor in neighbors.getElementsByTagName('interface'): tmp['neighbors'].append( extract_neighbor(neighbor) ) elif args.interface: neighbors = minidom.parseString(_get_neighbors()) for neighbor in neighbors.getElementsByTagName('interface'): # check if neighbor appeared on proper interface if neighbor.getAttribute('name') == args.interface: tmp['neighbors'].append( extract_neighbor(neighbor) ) else: parser.print_help() exit(1) tmpl = jinja2.Template(lldp_out) config_text = tmpl.render(tmp) print(config_text) exit(0)