From 27a40b6b1f884de5bb231fead1e6e41be7cac5cd Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 22 Dec 2019 13:57:30 +0100 Subject: lldp: T393: first running version of lldpd --- src/conf_mode/lldp.py | 225 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 135 insertions(+), 90 deletions(-) (limited to 'src') diff --git a/src/conf_mode/lldp.py b/src/conf_mode/lldp.py index 27749c81c..88a4a57a4 100755 --- a/src/conf_mode/lldp.py +++ b/src/conf_mode/lldp.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2017 VyOS maintainers and contributors +# Copyright (C) 2017-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 @@ -13,23 +13,36 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# -# import re import sys +import os +import jinja2 +from copy import deepcopy from vyos.config import Config from vyos import ConfigError +# Please be careful if you edit the template. +config_file = "/etc/default/lldpd" +lldp_tmpl = """ +### Autogenerated by lldp.py ### +DAEMON_ARGS="{% if snmp %}-x {% endif %}{% if addr %}-m {{ addr }} {% endif %}{% if cdp %}-c {% endif %}{% if edp %}-e {% endif %}{% if fdp %}-f {% endif %}{% if sonmp %}-s{% endif %}" + +""" + +default_config_data = { + "options": '', + "interface_list": '', + "location": '' +} def get_options(config): options = {} config.set_level('service lldp') - options['listen_vlan'] = config.exists('listen-vlan') - options["addr"] = config.return_value('management-address') + options['listen_vlan'] = config.exists('listen-vlan') + options['addr'] = config.return_value('management-address') snmp = config.exists('snmp enable') options["snmp"] = snmp @@ -39,75 +52,74 @@ def get_options(config): config.set_level('service lldp') config.set_level('service lldp legacy-protocols') - options["cdp"] = config.exists("cdp") - options["edp"] = config.exists("edp") - options["fdp"] = config.exists("fdp") - options["sonmp"] = config.exists("sonmp") - return options + options['cdp'] = config.exists('cdp') + options['edp'] = config.exists('edp') + options['fdp'] = config.exists('fdp') + options['sonmp'] = config.exists('sonmp') + return options def get_interface_list(config): config.set_level('service lldp') intfs_names = config.list_nodes('interface') if len(intfs_names) < 0: return 0 + interface_list = [] for name in intfs_names: - config.set_level("service lldp interface {0}".format(name)) + config.set_level('service lldp interface {0}'.format(name)) disable = config.exists('disable') intf = { - "name": name, - "disable": disable + 'name': name, + 'disable': disable } interface_list.append(intf) return interface_list def get_location_intf(config, name): - path = "service lldp interface {0}".format(name) + path = 'service lldp interface {0}'.format(name) config.set_level(path) - if config.exists("location"): + if config.exists('location'): return 0 - config.set_level("{} location".format(path)) + + config.set_level('{} location'.format(path)) civic_based = {} elin = None coordinate_based = {} if config.exists('civic-based'): - config.set_level("{} location civic-based".format(path)) - cc = config.return_value("country-code") - civic_based["country_code"] = cc - civic_based["ca_type"] = [] + config.set_level('{} location civic-based'.format(path)) + cc = config.return_value('country-code') + civic_based['country_code'] = cc + civic_based['ca_type'] = [] ca_types_names = config.list_nodes('ca-type') if ca_types_names: for ca_types_name in ca_types_names: - config.set_level("{0} location civic-based ca-type {1}".format(path, ca_types_name)) + config.set_level('{0} location civic-based ca-type {1}'.format(path, ca_types_name)) ca_val = config.return_value('ca-value') ca_type = { - "name": ca_types_name, - "ca_val": ca_val + 'name': ca_types_name, + 'ca_val': ca_val } - civic_based["ca_type"].append(ca_type) - - elif config.exists("elin"): - elin = config.return_value("elin") - - elif config.exists("coordinate-based"): - config.set_level("{} location coordinate-based".format(path)) - alt = config.return_value("altitude") - lat = config.return_value("latitude") - long = config.return_value("longitude") - datum = config.return_value("datum") - coordinate_based["altitude"] = alt - coordinate_based["latitude"] = lat - coordinate_based["longitude"] = long - coordinate_based["datum"] = datum + civic_based['ca_type'].append(ca_type) + + elif config.exists('elin'): + elin = config.return_value('elin') + + elif config.exists('coordinate-based'): + config.set_level('{} location coordinate-based'.format(path)) + + coordinate_based['altitude'] = config.return_value('altitude') + coordinate_based['latitude'] = config.return_value('latitude') + coordinate_based['longitude'] = config.return_value('longitude') + coordinate_based['datum'] = config.return_value('datum') intf = { - "name": name, - "civic_based": civic_based, - "elin": elin, - "coordinate_based": coordinate_based + 'name': name, + 'civic_based': civic_based, + 'elin': elin, + 'coordinate_based': coordinate_based } return intf @@ -118,92 +130,125 @@ def get_location(config): intfs_names = config.list_nodes('interface') if len(intfs_names) < 0: return 0 - if config.exists("disable"): + + if config.exists('disable'): return 0 + intfs_location = [] for name in intfs_names: intf = get_location_intf(config, name) intfs_location.append(intf) + return intfs_location def get_config(): + lldp = deepcopy(default_config_data) conf = Config() - options = get_options(conf) - interface_list = get_interface_list(conf) - location = get_location(conf) - lldp = {"options": options, "interface_list": interface_list, "location": location} - return lldp + if not conf.exists('service lldp'): + return None + else: + lldp['options'] = get_options(conf) + lldp['interface_list'] = get_interface_list(conf) + lldp['location'] = get_location(conf) + + return lldp def verify(lldp): + # bail out early - looks like removal from running config + if lldp is None: + return # check location - for location in lldp["location"]: - + for location in lldp['location']: # check civic-based - if len(location["civic_based"]) > 0: - if len(location["coordinate_based"]) > 0 or location["elin"]: - raise ConfigError("Can only configure 1 location type for interface {0}".format(location["name"])) + if len(location['civic_based']) > 0: + if len(location['coordinate_based']) > 0 or location['elin']: + raise ConfigError('Can only configure 1 location type for interface {0}'.format(location['name'])) # check country-code - if not location["civic_based"]["country_code"]: - raise ConfigError("Invalid location for interface {0}: must configure the country code".format(location["name"])) + if not location['civic_based']['country_code']: + raise ConfigError('Invalid location for interface {0}:\n' \ + 'must configure the country code'.format(location['name'])) - if not re.match(r"^[a-zA-Z]{2}$", location["civic_based"]["country_code"]): - raise ConfigError("Invalid location for interface {0}: country-code must be 2 characters".format(location["name"])) + if not re.match(r'^[a-zA-Z]{2}$', location['civic_based']['country_code']): + raise ConfigError('Invalid location for interface {0}:\n' \ + 'country-code must be 2 characters'.format(location['name'])) # check ca-type - if len(location["civic_based"]["ca_type"]) < 0: - raise ConfigError("Invalid location for interface {0}: must define at least 1 ca-type".format(location["name"])) + if len(location['civic_based']['ca_type']) < 0: + raise ConfigError('Invalid location for interface {0}:\n' \ + 'must define at least 1 ca-type'.format(location['name'])) - for ca_type in location["civic_based"]["ca_type"]: - if not int(ca_type["name"]) in range(0, 129): - raise ConfigError("Invalid location for interface {0}: ca-type must between 0-128".format(location["name"])) + for ca_type in location['civic_based']['ca_type']: + if not int(ca_type['name']) in range(0, 129): + raise ConfigError('Invalid location for interface {0}:\n' \ + 'ca-type must between 0-128'.format(location['name'])) - if not ca_type["ca_val"]: - raise ConfigError("Invalid location for interface {0}: must configure the ca-value for ca-type {1}".format(location["name"],ca_type["name"])) + if not ca_type['ca_val']: + raise ConfigError('Invalid location for interface {0}:\n' \ + 'must configure the ca-value for ca-type {1}'.format(location["name"],ca_type['name'])) # check coordinate-based - elif len(location["coordinate_based"]) > 0: + elif len(location['coordinate_based']) > 0: # check longitude and latitude - if not location["coordinate_based"]["longitude"]: - raise ConfigError("Must define longitude for interface {0}".format(location["name"])) + if not location['coordinate_based']['longitude']: + raise ConfigError('Must define longitude for interface {0}'.format(location['name'])) - if not location["coordinate_based"]["latitude"]: - raise ConfigError("Must define latitude for interface {0}".format(location["name"])) + if not location['coordinate_based']['latitude']: + raise ConfigError('Must define latitude for interface {0}'.format(location['name'])) - if not re.match(r"^(\d+)(\.\d+)?[nNsS]$", location["coordinate_based"]["latitude"]): - raise ConfigError("Invalid location for interface {0}: latitude should be a number followed by S or N".format(location["name"])) + if not re.match(r'^(\d+)(\.\d+)?[nNsS]$', location['coordinate_based']['latitude']): + raise ConfigError('Invalid location for interface {0}:\n' \ + 'latitude should be a number followed by S or N'.format(location['name'])) - if not re.match(r"^(\d+)(\.\d+)?[eEwW]$", location["coordinate_based"]["longitude"]): - raise ConfigError("Invalid location for interface {0}: longitude should be a number followed by E or W".format(location["name"])) + if not re.match(r'^(\d+)(\.\d+)?[eEwW]$', location['coordinate_based']['longitude']): + raise ConfigError('Invalid location for interface {0}:\n' \ + 'longitude should be a number followed by E or W'.format(location['name'])) # check altitude and datum if exist - if location["coordinate_based"]["altitude"]: - if not re.match(r"^[-+0-9\.]+$", location["coordinate_based"]["altitude"]): - raise ConfigError("Invalid location for interface {0}: altitude should be a positive or negative number".format(location["name"])) + if location['coordinate_based']['altitude']: + if not re.match(r'^[-+0-9\.]+$', location['coordinate_based']['altitude']): + raise ConfigError('Invalid location for interface {0}:\n' \ + 'altitude should be a positive or negative number'.format(location['name'])) - if location["coordinate_based"]["datum"]: - if not re.match(r"^(WGS84|NAD83|MLLW)$", location["coordinate_based"]["datum"]): - raise ConfigError("Invalid location for interface {0}: datum should be WGS84, NAD83, or MLLW".format(location["name"])) + if location['coordinate_based']['datum']: + if not re.match(r'^(WGS84|NAD83|MLLW)$', location['coordinate_based']['datum']): + raise ConfigError("Invalid location for interface {0}:\n' \ + 'datum should be WGS84, NAD83, or MLLW".format(location['name'])) # check elin - elif len(location["elin"]) > 0: - if not re.match(r"^[0-9]{10,25}$", location["elin"]): - raise ConfigError("Invalid location for interface {0}: ELIN number must be between 10-25 numbers".format(location["name"])) + elif location['elin']: + if not re.match(r'^[0-9]{10,25}$', location['elin']): + raise ConfigError('Invalid location for interface {0}:\n' \ + 'ELIN number must be between 10-25 numbers'.format(location['name'])) # check options - if lldp["options"]["snmp"]: - if not lldp["options"]["sys_snmp"]: - raise ConfigError("SNMP must be configured to enable LLDP SNMP") + if lldp['options']['snmp']: + if not lldp['options']['sys_snmp']: + raise ConfigError('SNMP must be configured to enable LLDP SNMP') + + +def generate(lldp): + # bail out early - looks like removal from running config + if lldp is None: + return + tmpl = jinja2.Template(lldp_tmpl) + config_text = tmpl.render(lldp['options']) + with open(config_file, 'w') as f: + f.write(config_text) -def generate(config): - pass +def apply(lldp): + if lldp: + # start/restart lldp service + os.system('sudo systemctl restart lldpd.service') + else: + # LLDP service has been terminated + os.system('sudo systemctl stop lldpd.service') + os.unlink(config_file) -def apply(config): - pass if __name__ == '__main__': try: -- cgit v1.2.3 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 (limited to 'src') 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 From 2cac6f3ce09ddb5140e364e86476feba4a8ed285 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 22 Dec 2019 19:36:00 +0100 Subject: lldp: T393: add config options to /etc/lldpd.d --- src/conf_mode/lldp.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/conf_mode/lldp.py b/src/conf_mode/lldp.py index 88a4a57a4..16bb329c4 100755 --- a/src/conf_mode/lldp.py +++ b/src/conf_mode/lldp.py @@ -27,7 +27,16 @@ from vyos import ConfigError config_file = "/etc/default/lldpd" lldp_tmpl = """ ### Autogenerated by lldp.py ### -DAEMON_ARGS="{% if snmp %}-x {% endif %}{% if addr %}-m {{ addr }} {% endif %}{% if cdp %}-c {% endif %}{% if edp %}-e {% endif %}{% if fdp %}-f {% endif %}{% if sonmp %}-s{% endif %}" +DAEMON_ARGS="-M4{% if snmp %} -x{% endif %}{% if addr %} -m {{ addr }}{% endif %}{% if cdp %} -c{% endif %}{% if edp %} -e{% endif %}{% if fdp %} -f{% endif %}{% if sonmp %} -s{% endif %}" + +""" + +vyos_config_file = "/etc/lldpd.d/01-vyos.conf" +vyos_tmpl = """ +### Autogenerated by lldp.py ### + +configure system platform VyOS +configure system description "VyOS {{ description }}" """ @@ -234,11 +243,23 @@ def generate(lldp): if lldp is None: return + version = 'unknown' + with open('/opt/vyatta/etc/version', 'r') as f: + tmp = f.read() + version = tmp.split()[1] + + lldp['options']['description'] = version + tmpl = jinja2.Template(lldp_tmpl) config_text = tmpl.render(lldp['options']) with open(config_file, 'w') as f: f.write(config_text) + tmpl = jinja2.Template(vyos_tmpl) + config_text = tmpl.render(lldp['options']) + with open(vyos_config_file, 'w') as f: + f.write(config_text) + def apply(lldp): if lldp: -- cgit v1.2.3 From 6aca7f73449ea79d4899ca34f0ae17e9c6aaeaab Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 22 Dec 2019 19:48:54 +0100 Subject: lldp: T393: support listen interfaces --- src/conf_mode/lldp.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/conf_mode/lldp.py b/src/conf_mode/lldp.py index 16bb329c4..62bb6a52e 100755 --- a/src/conf_mode/lldp.py +++ b/src/conf_mode/lldp.py @@ -37,6 +37,9 @@ vyos_tmpl = """ configure system platform VyOS configure system description "VyOS {{ description }}" +{% if listen_on %} +configure system interface pattern "{{ listen_on | join(",") }}" +{% endif %} """ @@ -66,6 +69,10 @@ def get_options(config): options['fdp'] = config.exists('fdp') options['sonmp'] = config.exists('sonmp') + # start with an unknown version information + options['description'] = 'unknown' + options['listen_on'] = [] + return options def get_interface_list(config): @@ -243,18 +250,28 @@ def generate(lldp): if lldp is None: return - version = 'unknown' with open('/opt/vyatta/etc/version', 'r') as f: tmp = f.read() - version = tmp.split()[1] + lldp['options']['description'] = tmp.split()[1] + + + # generate listen on interfaces + for intf in lldp['interface_list']: + tmp = '' + # add exclamation mark if interface is disabled + if intf['disable']: + tmp = '!' - lldp['options']['description'] = version + tmp += intf['name'] + lldp['options']['listen_on'].append(tmp) + # generate /etc/default/lldpd tmpl = jinja2.Template(lldp_tmpl) config_text = tmpl.render(lldp['options']) with open(config_file, 'w') as f: f.write(config_text) + # generate /etc/lldpd.d/01-vyos.conf tmpl = jinja2.Template(vyos_tmpl) config_text = tmpl.render(lldp['options']) with open(vyos_config_file, 'w') as f: -- cgit v1.2.3 From 718d115c994ea67b30589f1e50de1463fd254e79 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 22 Dec 2019 20:11:04 +0100 Subject: lldp: T393: use flat dictionary when generating configs --- src/conf_mode/lldp.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/conf_mode/lldp.py b/src/conf_mode/lldp.py index 62bb6a52e..1b7676274 100755 --- a/src/conf_mode/lldp.py +++ b/src/conf_mode/lldp.py @@ -27,7 +27,7 @@ from vyos import ConfigError config_file = "/etc/default/lldpd" lldp_tmpl = """ ### Autogenerated by lldp.py ### -DAEMON_ARGS="-M4{% if snmp %} -x{% endif %}{% if addr %} -m {{ addr }}{% endif %}{% if cdp %} -c{% endif %}{% if edp %} -e{% endif %}{% if fdp %} -f{% endif %}{% if sonmp %} -s{% endif %}" +DAEMON_ARGS="-M 4{% if options.snmp %} -x{% endif %}{% if options.addr %} -m {{ options.addr }}{% endif %}{% if options.cdp %} -c{% endif %}{% if options.edp %} -e{% endif %}{% if options.fdp %} -f{% endif %}{% if options.sonmp %} -s{% endif %}" """ @@ -36,9 +36,9 @@ vyos_tmpl = """ ### Autogenerated by lldp.py ### configure system platform VyOS -configure system description "VyOS {{ description }}" +configure system description "VyOS {{ options.description }}" {% if listen_on %} -configure system interface pattern "{{ listen_on | join(",") }}" +configure system interface pattern "{{ options.listen_on | join(",") }}" {% endif %} """ @@ -267,13 +267,13 @@ def generate(lldp): # generate /etc/default/lldpd tmpl = jinja2.Template(lldp_tmpl) - config_text = tmpl.render(lldp['options']) + config_text = tmpl.render(lldp) with open(config_file, 'w') as f: f.write(config_text) # generate /etc/lldpd.d/01-vyos.conf tmpl = jinja2.Template(vyos_tmpl) - config_text = tmpl.render(lldp['options']) + config_text = tmpl.render(lldp) with open(vyos_config_file, 'w') as f: f.write(config_text) -- cgit v1.2.3 From f1eae1ba095319aca3e8e2ccbcd81758d7e1a638 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 22 Dec 2019 20:11:35 +0100 Subject: lldp: T393: support parsing MED values --- src/conf_mode/lldp.py | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/conf_mode/lldp.py b/src/conf_mode/lldp.py index 1b7676274..15c0f7812 100755 --- a/src/conf_mode/lldp.py +++ b/src/conf_mode/lldp.py @@ -96,8 +96,6 @@ def get_interface_list(config): def get_location_intf(config, name): path = 'service lldp interface {0}'.format(name) config.set_level(path) - if config.exists('location'): - return 0 config.set_level('{} location'.format(path)) civic_based = {} @@ -106,19 +104,16 @@ def get_location_intf(config, name): if config.exists('civic-based'): config.set_level('{} location civic-based'.format(path)) - cc = config.return_value('country-code') - civic_based['country_code'] = cc + civic_based['country_code'] = config.return_value('country-code') civic_based['ca_type'] = [] - ca_types_names = config.list_nodes('ca-type') - if ca_types_names: - for ca_types_name in ca_types_names: - config.set_level('{0} location civic-based ca-type {1}'.format(path, ca_types_name)) - ca_val = config.return_value('ca-value') - ca_type = { - 'name': ca_types_name, - 'ca_val': ca_val - } - civic_based['ca_type'].append(ca_type) + for ca_types_name in config.list_nodes('ca-type'): + config.set_level('{} location civic-based ca-type {}'.format(path, ca_types_name)) + ca_val = config.return_value('ca-value') + ca_type = { + 'name': ca_types_name, + 'ca_val': ca_val + } + civic_based['ca_type'].append(ca_type) elif config.exists('elin'): elin = config.return_value('elin') -- cgit v1.2.3 From 2f0119a2aa6cf6375657e7ab4b16f5b750b45f68 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 22 Dec 2019 20:17:11 +0100 Subject: lldp: T393: add Emergency Location Identifier Number (ELIN) support --- interface-definitions/lldp.xml.in | 13 +++++++++++++ src/conf_mode/lldp.py | 9 ++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/interface-definitions/lldp.xml.in b/interface-definitions/lldp.xml.in index 4c927f603..8aaaaf102 100644 --- a/interface-definitions/lldp.xml.in +++ b/interface-definitions/lldp.xml.in @@ -233,6 +233,19 @@ + + + ECS ELIN (Emergency location identifier number) + + 0-9999999999 + Emergency Call Service ELIN number (between 10-25 numbers) + + + [0-9]{10,25}$ + + ELIN number must be between 10-25 numbers + + diff --git a/src/conf_mode/lldp.py b/src/conf_mode/lldp.py index 15c0f7812..be7880c55 100755 --- a/src/conf_mode/lldp.py +++ b/src/conf_mode/lldp.py @@ -41,6 +41,13 @@ configure system description "VyOS {{ options.description }}" configure system interface pattern "{{ options.listen_on | join(",") }}" {% endif %} +{% for loc in location %} +{%- if loc.elin %} +configure ports {{ loc.name }} med location elin "{{ loc.elin }}" +{%- endif %} +{% endfor %} + + """ default_config_data = { @@ -99,7 +106,7 @@ def get_location_intf(config, name): config.set_level('{} location'.format(path)) civic_based = {} - elin = None + elin = '' coordinate_based = {} if config.exists('civic-based'): -- cgit v1.2.3 From e4b56e2988d3c5599702e858bd2b9ea8ed0ec9a1 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 22 Dec 2019 21:36:37 +0100 Subject: lldp: T393: support IPv6 management address --- interface-definitions/lldp.xml.in | 10 +++++++--- src/conf_mode/lldp.py | 17 ++++++++--------- 2 files changed, 15 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/interface-definitions/lldp.xml.in b/interface-definitions/lldp.xml.in index 8aaaaf102..b44f4baf6 100644 --- a/interface-definitions/lldp.xml.in +++ b/interface-definitions/lldp.xml.in @@ -283,13 +283,17 @@ - IPv4 management address + Management IP Address ipv4 - IP address to listen for incoming connections + IPv4 Management Address + + + ipv6 + IPv6 Management Address - + diff --git a/src/conf_mode/lldp.py b/src/conf_mode/lldp.py index be7880c55..9fd32d83f 100755 --- a/src/conf_mode/lldp.py +++ b/src/conf_mode/lldp.py @@ -27,7 +27,7 @@ from vyos import ConfigError config_file = "/etc/default/lldpd" lldp_tmpl = """ ### Autogenerated by lldp.py ### -DAEMON_ARGS="-M 4{% if options.snmp %} -x{% endif %}{% if options.addr %} -m {{ options.addr }}{% endif %}{% if options.cdp %} -c{% endif %}{% if options.edp %} -e{% endif %}{% if options.fdp %} -f{% endif %}{% if options.sonmp %} -s{% endif %}" +DAEMON_ARGS="-M 4{% if options.snmp %} -x{% endif %}{% if options.cdp %} -c{% endif %}{% if options.edp %} -e{% endif %}{% if options.fdp %} -f{% endif %}{% if options.sonmp %} -s{% endif %}" """ @@ -37,17 +37,17 @@ vyos_tmpl = """ configure system platform VyOS configure system description "VyOS {{ options.description }}" -{% if listen_on %} +{%- if listen_on -%} configure system interface pattern "{{ options.listen_on | join(",") }}" -{% endif %} - -{% for loc in location %} -{%- if loc.elin %} +{%- endif %} +{% if options.addr -%} +configure system ip management pattern "{{ options.addr }}" +{%- endif %} +{%- for loc in location -%} +{%- if loc.elin -%} configure ports {{ loc.name }} med location elin "{{ loc.elin }}" {%- endif %} {% endfor %} - - """ default_config_data = { @@ -289,7 +289,6 @@ def apply(lldp): os.system('sudo systemctl stop lldpd.service') os.unlink(config_file) - if __name__ == '__main__': try: c = get_config() -- cgit v1.2.3 From 246c72b2ae948d1721320abb16a10aa6e0c8eadc Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 22 Dec 2019 21:56:40 +0100 Subject: fixup --- src/conf_mode/lldp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/conf_mode/lldp.py b/src/conf_mode/lldp.py index 9fd32d83f..8c57eacd3 100755 --- a/src/conf_mode/lldp.py +++ b/src/conf_mode/lldp.py @@ -44,7 +44,7 @@ configure system interface pattern "{{ options.listen_on | join(",") }}" configure system ip management pattern "{{ options.addr }}" {%- endif %} {%- for loc in location -%} -{%- if loc.elin -%} +{%- if loc.elin %} configure ports {{ loc.name }} med location elin "{{ loc.elin }}" {%- endif %} {% endfor %} -- cgit v1.2.3 From 7158bc3cc82e1a66473ef6c65e95e00f64d42b92 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 22 Dec 2019 22:12:42 +0100 Subject: lldp: T393: support both ELIN and ccordinate based location service in MED --- src/conf_mode/lldp.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/conf_mode/lldp.py b/src/conf_mode/lldp.py index 8c57eacd3..da01de56f 100755 --- a/src/conf_mode/lldp.py +++ b/src/conf_mode/lldp.py @@ -47,6 +47,11 @@ configure system ip management pattern "{{ options.addr }}" {%- if loc.elin %} configure ports {{ loc.name }} med location elin "{{ loc.elin }}" {%- endif %} +{%- if loc.coordinate_based %} +configure ports {{ loc.name }} med location coordinate {% if loc.coordinate_based.latitude %}latitude {{ loc.coordinate_based.latitude }}{% endif %} {% if loc.coordinate_based.longitude %}longitude {{ loc.coordinate_based.longitude }}{% endif %} {% if loc.coordinate_based.altitude %}altitude {{ loc.coordinate_based.altitude }} m{% endif %} {% if loc.coordinate_based.datum %}datum {{ loc.coordinate_based.datum }}{% endif %} +{%- endif %} + + {% endfor %} """ @@ -122,16 +127,22 @@ def get_location_intf(config, name): } civic_based['ca_type'].append(ca_type) - elif config.exists('elin'): + if config.exists('elin'): elin = config.return_value('elin') - elif config.exists('coordinate-based'): + if config.exists('coordinate-based'): config.set_level('{} location coordinate-based'.format(path)) - coordinate_based['altitude'] = config.return_value('altitude') coordinate_based['latitude'] = config.return_value('latitude') coordinate_based['longitude'] = config.return_value('longitude') - coordinate_based['datum'] = config.return_value('datum') + + coordinate_based['altitude'] = '0' + if config.exists('altitude'): + coordinate_based['altitude'] = config.return_value('altitude') + + coordinate_based['datum'] = 'WGS84' + if config.exists('datum'): + coordinate_based['datum'] = config.return_value('datum') intf = { 'name': name, -- cgit v1.2.3