From 4a75bb5a6027e1444c9ba0dbf48af30514b077bf Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 22 Dec 2019 15:34:49 +0100 Subject: lldp: T393: first op mode command version --- op-mode-definitions/lldp.xml | 37 ++++++++++ src/op_mode/lldp_op.py | 164 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 201 insertions(+) create mode 100644 op-mode-definitions/lldp.xml create mode 100755 src/op_mode/lldp_op.py diff --git a/op-mode-definitions/lldp.xml b/op-mode-definitions/lldp.xml new file mode 100644 index 000000000..105bfe237 --- /dev/null +++ b/op-mode-definitions/lldp.xml @@ -0,0 +1,37 @@ + + + + + + + Show LLDP (Link Layer Discovery Protocol) + + + + + Show LLDP neighbors + + ${vyos_op_scripts_dir}/lldp_op.py --all + + + + Show LLDP neighbor details + + /usr/sbin/lldpctl -f plain + + + + Show LLDP for specified interface + + + + + ${vyos_op_scripts_dir}/lldp_op.py --interface $4 + + + + + + + + diff --git a/src/op_mode/lldp_op.py b/src/op_mode/lldp_op.py new file mode 100755 index 000000000..4d8fdbc99 --- /dev/null +++ b/src/op_mode/lldp_op.py @@ -0,0 +1,164 @@ +#!/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 subprocess import Popen, PIPE, STDOUT +from tabulate import tabulate + +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' + p = Popen(command, stdout=PIPE, stderr=STDOUT, shell=True) + tmp = p.communicate()[0].strip() + return tmp.decode() + +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) -- cgit v1.2.3