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/conf_mode') 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