#!/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)