/* * Copyright (c) 2020 Tano Systems LLC. All Rights Reserved. * Author: Anton Kikin * Copyright (c) 2023-2024. All Rights Reserved. * Paul Donald */ 'use strict'; 'require rpc'; 'require form'; 'require lldpd'; 'require network'; 'require uci'; 'require tools.widgets as widgets'; var callInitList = rpc.declare({ object: 'luci', method: 'getInitList', params: [ 'name' ], expect: { '': {} }, filter: function(res) { for (var k in res) return +res[k].enabled; return null; } }); var callInitAction = rpc.declare({ object: 'luci', method: 'setInitAction', params: [ 'name', 'action' ], expect: { result: false } }); var usage = _('See syntax here.').format('href=https://lldpd.github.io/usage.html target="_blank"'); const validateioentries = function(section_id, value) { if (value) { const emsg = _('Cannot have both interface %s and its exclusion %s'); const a = value.split(' '); const noex = a.filter(el=> !el.startsWith('!')); const ex = a.filter(el=> el.startsWith('!') && !el.startsWith('!!')); for (var i of noex) { for (var e of ex) { if ('!'+i == e){ return emsg.format(i, e); } } } } return true; }; return L.view.extend({ __init__: function() { this.super('__init__', arguments); // Inject CSS var head = document.getElementsByTagName('head')[0]; var css = E('link', { 'href': L.resource('lldpd/lldpd.css') + '?v=#PKG_VERSION', 'rel': 'stylesheet' }); head.appendChild(css); }, load: function() { return Promise.all([ callInitList('lldpd'), lldpd.init(), uci.load('lldpd'), network.getDevices() ]); }, // ----------------------------------------------------------------------------------------- // // Basic Options // // ----------------------------------------------------------------------------------------- /** @private */ populateBasicOptions: function(s, tab, data) { var o; var serviceEnabled = data[0]; var net_devices = data[3]; // Service enable/disable o = s.taboption(tab, form.Flag, 'enabled', _('Enable service')); o.optional = false; o.rmempty = false; o.cfgvalue = function() { return serviceEnabled ? this.enabled : this.disabled; }; o.write = function(section_id, value) { uci.set('lldpd', section_id, 'enabled', value); if (value == '1') { // Enable and start callInitAction('lldpd', 'enable').then(function() { return callInitAction('lldpd', 'start'); }); } else { // Stop and disable callInitAction('lldpd', 'stop').then(function() { return callInitAction('lldpd', 'disable'); }); } }; // System description o = s.taboption(tab, form.Value, 'lldp_description', _('System description'), _('Override %s.').format('system description')); o.placeholder = 'System description'; // System hostname o = s.taboption(tab, form.Value, 'lldp_hostname', _('System hostname'), _('Override %s.').format('system hostname')); o.placeholder = 'System hostname'; /* This function returns the value for a specified key. Used to fill various location fields derived from an lldpd location config string */ function getLocationValueFromConfString(_key) { var inStr = this ? this.section.formvalue('config', 'lldp_location'): uci.get('lldpd', 'config', 'lldp_location'); inStr = inStr ? inStr: ''; const words = inStr.trim().split(/\s+/); /* This function does not assume an order to the key:value parameters. Only that the value comes after its key, so optional keys can be in any order. */ const ix = words.indexOf(_key); if (ix !== -1) { let value = words.slice(ix + 1).join(' '); if (value.startsWith('"')) { const quoteStart = value.indexOf('"'); const quoteEnd = value.indexOf('"', quoteStart + 1); return value.substring(quoteStart + 1, quoteEnd); } return _key == 'altitude' ? words[ix + 1] + ' ' + words[ix + 2] : words[ix + 1]; } else { return ''; // Element not found } }; function write_lldp_location() { var _input = this ? this.section.formvalue('config', '_lldp_location_type'): '1'; if(_input){ if (_input == '1') { /* location coordinate latitude 48.85667N longitude 2.2014E altitude 117.47 m datum WGS84 */ var lat = this.section.formvalue('config', '_coordinate_lat'), lon = this.section.formvalue('config', '_coordinate_lon'), alt = this.section.formvalue('config', '_coordinate_alt'), dat = this.section.formvalue('config', '_coordinate_dat'); if(lat && lon && dat) { uci.set('lldpd', 'config', 'lldp_location', 'coordinate latitude ' + lat + ' longitude ' + lon + ' altitude ' + (alt ? alt:'0 m') + ' datum ' + dat ); } } else if (_input == '2') { /* location address country US street "Commercial Road" city "Roseville" */ var cc = this.section.formvalue('config', '_civic_cc'), city = this.section.formvalue('config', '_civic_city'), str = this.section.formvalue('config', '_civic_str'), bldg = this.section.formvalue('config', '_civic_bldg'), nmbr = this.section.formvalue('config', '_civic_nmbr'), zip = this.section.formvalue('config', '_civic_zip'); uci.set('lldpd', 'config', 'lldp_location', 'address country ' + cc.toUpperCase() + (city ? ' city "' + city + '"': '') + (str ? ' street "' + str + '"': '') + (bldg ? ' building "' + bldg + '"': '') + (nmbr ? ' number "' + nmbr + '"': '') + (zip ? ' zip "' + zip + '"': '')); } else if (_input == '3') { /* location elin 12345 */ var elin = this.section.formvalue('config', '_elin'); if(elin) uci.set('lldpd', 'config', 'lldp_location', 'elin ' + elin); } } }; // Host location o = s.taboption(tab, form.ListValue, '_lldp_location_type', _('Host location type'), _('Override the announced location of the host.')); o.value('1', _('Coordinate based')); o.value('2', _('Civic address')); o.value('3', _('ELIN')); o.rmempty = true; o.write = write_lldp_location; o.load = function(section_id, value) { const loc = uci.get(this.config, section_id, 'lldp_location'); if (!loc) return '1'; if (loc.toLowerCase().includes('coordinate')) { return '1'; } else if (loc.toLowerCase().includes('address country')) { return '2'; } else if (loc.toLowerCase().includes('elin')) { return '3'; } }; o = s.taboption(tab, form.Value, 'lldp_location', _('Raw location config'), _('Raw config string sent to lldpd, starting: [coordinate|address|elin]')); o.placeholder = 'address country EU'; o.rmempty = true; o.write = function(section_id, value) { if (value) { const words = value.trim().split(/\s+/), regex = /^coordinate|^address|^elin/; var start; words.forEach(w=>{ if (w.match(regex)) start = w; }); // Retain string tail from one of the regex keywords return this.super('write', [ section_id, value.substring(value.indexOf(start)) ]); } }; o.validate = function(section_id, value) { if (value) { const words = value.trim().split(/\s+/), regex = /^coordinate|^address|^elin/; var _eval = _("Must contain: 'coordinate ...', 'address ...' or 'elin ...'"); words.forEach(w=>{ if (w.match(regex)) _eval = true; }); return _eval; } return true; }; // Coordinate based o = s.taboption(tab, form.Value, '_coordinate_lat', _('Latitude'), '0 .. 90.000[N|S]'); o.depends({ '_lldp_location_type' : '1'}); o.datatype = "maxlength(20)"; o.validate = function(section_id, value) { if (!value) return true; var valid = _('valid syntax: 0 .. 90.000[N|S]'); valid = (parseFloat(value) >= 0 && parseFloat(value) <= 90) ? /^-?\d+(?:\.\d+)?[NnSs]$/.test(value) ? true : valid : valid; return valid; } o.load = function(section_id, value) { return getLocationValueFromConfString('latitude'); } o.write = write_lldp_location; o = s.taboption(tab, form.Value, '_coordinate_lon', _('Longitude'), '0 .. 180.000[E|W]'); o.depends({ '_lldp_location_type' : '1'}); o.datatype = "maxlength(20)"; o.validate = function(section_id, value) { if (!value) return true; var valid = _('valid syntax: 0 .. 180.000[E|W]'); valid = (parseFloat(value) >= 0 && parseFloat(value) <= 180) ? /^-?\d+(?:\.\d+)?[WwEe]$/.test(value) ? true : valid : valid; return valid; } o.load = function(section_id, value) { return getLocationValueFromConfString('longitude'); } o.write = write_lldp_location; const min_alt = -100000.00, max_alt = 42849672.95; o = s.taboption(tab, form.Value, '_coordinate_alt', _('Altitude'), '%f .. %f [m|f]'.format(min_alt, max_alt)); o.depends({ '_lldp_location_type' : '1'}); o.datatype = 'maxlength(20)'; o.validate = function(section_id, value) { if (!value) return true; var valid = _('valid syntax: %f .. %f [mf]').format(min_alt, max_alt); valid = (parseFloat(value) >= min_alt && parseFloat(value) <= max_alt) ? /^-?\d+(?:\.\d+)?\ [mf]$/.test(value) ? true : valid : valid; return valid; } o.load = function(section_id, value) { return getLocationValueFromConfString('altitude'); } o.write = write_lldp_location; o = s.taboption(tab, form.ListValue, '_coordinate_dat', _('Datum')); o.depends({ '_lldp_location_type' : '1'}); o.value('WGS84'); o.value('NAD83'); o.value('NAD83/MLLW'); o.load = function(section_id, value) { return getLocationValueFromConfString('datum'); } o.write = write_lldp_location; // Civic address based /* ISO 3166-2 CC list officially assigned + exceptional: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements */ o = s.taboption(tab, form.Value, '_civic_cc', _('Country'), '%s'.format('ISO 3166-2 CC').format('https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements')); o.depends({ '_lldp_location_type' : '2'}); o.default = 'EU'; o.placeholder = 'EU'; o.validate = function(section_id, value) { if(!value) return true; var valid = _('Two character CC required'); valid = (value.length == 2) ? /^[A-Z]{2}$/i.test(value) ? true : valid : valid; return valid; } o.load = function(section_id, value) { return getLocationValueFromConfString('country').toUpperCase(); } o.write = write_lldp_location; o = s.taboption(tab, form.Value, '_civic_city', _('City')); o.depends({ '_lldp_location_type' : '2'}); o.datatype = "maxlength(100)"; o.load = function(section_id, value) { return getLocationValueFromConfString('city'); } o.placeholder = _('Gotham'); o.write = write_lldp_location; o = s.taboption(tab, form.Value, '_civic_str', _('Street')); o.depends({ '_lldp_location_type' : '2'}); o.datatype = "maxlength(100)"; o.load = function(section_id, value) { return getLocationValueFromConfString('street'); } o.placeholder = _('Main Street'); o.write = write_lldp_location; o = s.taboption(tab, form.Value, '_civic_bldg', _('Building')); o.depends({ '_lldp_location_type' : '2'}); o.datatype = "maxlength(250)"; o.load = function(section_id, value) { return getLocationValueFromConfString('building'); } o.placeholder = _('Empire State Bldg'); o.write = write_lldp_location; o = s.taboption(tab, form.Value, '_civic_nmbr', _('Number')); o.depends({ '_lldp_location_type' : '2'}); o.datatype = "maxlength(25)"; o.load = function(section_id, value) { return getLocationValueFromConfString('number'); } o.placeholder = _('1A'); o.write = write_lldp_location; o = s.taboption(tab, form.Value, '_civic_zip', _('Post-code')); o.depends({ '_lldp_location_type' : '2'}); o.datatype = "maxlength(25)"; o.load = function(section_id, value) { return getLocationValueFromConfString('zip'); } o.placeholder = '12345'; o.write = write_lldp_location; // ELIN based o = s.taboption(tab, form.Value, '_elin', _('ELIN Address')); o.depends({ '_lldp_location_type' : '3'}); o.datatype = 'and(uinteger,maxlength(25))'; o.load = function(section_id, value) { return getLocationValueFromConfString('elin'); } o.placeholder = '1911'; o.write = write_lldp_location; // Platform o = s.taboption(tab, form.Value, 'lldp_platform', _('System platform description'), _('Override %s.').format('system platform') + '
' + _('The default description is the kernel name (Linux).')); o.placeholder = 'System platform description'; o = s.taboption(tab, form.Flag, 'lldp_capability_advertisements', _('System capability advertisements')); o.default = '1'; //lldpd internal default o.rmempty = false; // Capabilities override o = s.taboption(tab, form.MultiValue, 'lldp_syscapabilities', _('System capabilities'), _('Override %s.').format('system capabilities') + '
' + _('The default is derived from kernel information.')); o.depends({lldp_capability_advertisements: '1'}); o.value('bridge'); o.value('docsis'); o.value('other'); o.value('repeater'); o.value('router'); o.value('station'); o.value('telephone'); o.value('wlan'); o.cfgvalue = function(section_id) { return String(this.super('load', [section_id]) || this.default).split(','); }; o.write = function(section_id, value) { return this.super('write', [ section_id, L.toArray(value).join(',') ]); }; o = s.taboption(tab, form.Flag, 'lldp_mgmt_addr_advertisements', _('System management IO advertisements')); o.default = '1'; //lldpd internal default o.rmempty = false; // Management addresses of this system // This value: lldpd.init handles as a single value, and needs a CSV for lldpd.conf: 'configure system ip management pattern' o = s.taboption(tab, lldpd.CBIMultiIOSelect, 'lldp_mgmt_ip', _('System management IO'), _('Defaults to the first IPv4 and IPv6. ' + 'If an exact IP address is provided, it is used ' + 'as a management address without any check. To ' + 'blacklist IPv6 addresses, use !*:*.') + '
' + usage); o.placeholder = 'Addresses and interfaces'; o.depends({lldp_mgmt_addr_advertisements: '1'}); o.cfgvalue = function(section_id) { const opt = uci.get(this.config, section_id, this.option); return opt ? opt.split(',') : ''; }; net_devices.forEach(nd => { o.value(nd.getName()); o.value('!'+nd.getName()); nd.getIPAddrs().forEach(addr => o.value(addr.split('/')[0], E([], [addr.split('/')[0], ' (', E('strong', {}, nd.getName()), ')']))); nd.getIP6Addrs().forEach(addr => o.value(addr.split('/')[0], E([], [addr.split('/')[0], ' (', E('strong', {}, nd.getName()), ')']))); }); o.value('!*:*'); o.validate = validateioentries; o.write = function(section_id, value, sep) { return this.super('write', [ section_id, value.join(',') ]); } // LLDP tx interval o = s.taboption(tab, form.Value, 'lldp_tx_interval', _('Transmit delay'), _('The delay between ' + 'transmissions of LLDP PDU. The default value ' + 'is 30 seconds.') + '
' + _('Suffix %s for millisecond values.').format('ms')); o.default = 30; o.placeholder = 30; o.rmempty = false; o.validate = function(section_id, value) { const pattern = /^(\d+)(?:ms)?$/; if (!value.match(pattern) || parseInt(value) <= 0) return _('Must be a greater than zero number optionally suffixed with "ms"'); return true; }; // LLDP tx hold o = s.taboption(tab, form.Value, 'lldp_tx_hold', _('Transmit hold value'), _('Determines the transmitted ' + 'packet TTL (== this value * transmit delay). ' + 'The default value is 4 ∴ ' + 'the default TTL is 120 seconds.')); o.datatype = 'uinteger'; o.default = 4; o.placeholder = 4; o.rmempty = false; o.validate = function(section_id, value) { if (value != parseInt(value)) return _('Must be a number'); else if (value <= 0) return _('Transmit hold value must be greater than 0'); return true; }; // Received-only mode (-r) o = s.taboption(tab, form.Flag, 'readonly_mode', _('Receive-only mode'), _("LLDPd won't send any frames; " + 'only listen to neighbors.')); o.rmempty = false; o.optional = false; o.default = '0'; }, // ----------------------------------------------------------------------------------------- // // Network Interfaces // // ----------------------------------------------------------------------------------------- /** @private */ populateIfacesOptions: function(s, tab, data) { var o; var net_devices = data[3]; // Interfaces to listen on // This value: lldpd.init handles as a list value, and produces a CSV for lldpd.conf: 'configure system interface pattern' o = s.taboption(tab, lldpd.CBIMultiIOSelect, 'interface', _('Network IO'), _('Specify which interface (not) to listen upon and send LLDPDU from. ' + 'Absent any value, LLDPd uses all available physical interfaces.')); o.value('*'); net_devices.forEach(nd => { o.value(nd.getName()); o.value('!'+nd.getName()); o.value('!!'+nd.getName()); }); o.value('!*:*'); o.validate = validateioentries; // ChassisID interfaces // This value: lldpd.init handles as a list value, and produces a CSV for the -C param o = s.taboption(tab, lldpd.CBIMultiIOSelect, 'cid_interface', _('Network IO for chassis ID'), _('Specify which interfaces (not) to use for computing chassis ID. ' + 'Absent any value, all interfaces are considered. ' + 'LLDPd takes the first MAC address from all the considered ' + 'interfaces to compute the chassis ID.')); o.value('*'); o.value('!*'); net_devices.forEach(nd => { o.value(nd.getName()); o.value('!'+nd.getName()); o.value('!!'+nd.getName()); }); o.value('!*:*'); o.validate = validateioentries; }, // ----------------------------------------------------------------------------------------- // // Advanced Options // // ----------------------------------------------------------------------------------------- /** @private */ populateAdvancedOptions: function(s, tab, data) { var o; // SNMP agentX socket // **Note**: lldpd is compiled in OpenWrt without SNMP support by default. Setting this action will then cause the lldpd daemon to stop starting and thus lldpd will stop working. To fix this, the value must then be deleted and lldpd restarted. // o = s.taboption(tab, form.Value, 'agentxsocket', // _('SNMP agentX socket path'), // _('If the path to the socket is set, then LLDPd will enable an ' + // 'SNMP subagent using AgentX protocol. This allows you to get ' + // 'information about local system and remote systems through SNMP.')); // o.rmempty = true; // o.placeholder = '/var/run/agentx.sock'; // o.default = ''; // LLDP-MED class o = s.taboption(tab, form.ListValue, 'lldp_class', _('LLDP-MED device class')); o.value('1', _('Generic Endpoint (Class I)')); o.value('2', _('Media Endpoint (Class II)')); o.value('3', _('Communication Device Endpoints (Class III)')); o.value('4', _('Network Connectivity Device (Class IV)')); o.default = '4'; // LLDP-MED policy o = s.taboption(tab, form.Value, 'lldp_policy', _('LLDP-MED policy')); o.depends({lldp_class: '2'}); o.depends({lldp_class: '3'}); o.rmempty = true; o.placeholder = 'application streaming-video'; o.value('application voice'); o.value('application voice unknown'); o.value('application voice-signaling'); o.value('application voice-signaling unknown'); o.value('application guest-voice'); o.value('application guest-voice unknown'); o.value('application guest-voice-signaling'); o.value('application guest-voice-signaling unknown'); o.value('application softphone-voice'); o.value('application softphone-voice unknown'); o.value('application video-conferencing'); o.value('application video-conferencing unknown'); o.value('application streaming-video'); o.value('application streaming-video unknown'); o.value('application video-signaling'); o.value('application video-signaling unknown'); o.validate = function(section_id, value) { if (value && !value.startsWith('application ')) return _('Must start: application ...'); return true; }; // LLDP-MED fast-start o = s.taboption(tab, form.Flag, 'lldpmed_fast_start', _('LLDP-MED fast-start')); // LLDP-MED fast-start o = s.taboption(tab, form.Value, 'lldpmed_fast_start_tx_interval', _('LLDP-MED fast-start tx-interval')); o.depends({lldpmed_fast_start: '1'}); o.datatype = 'uinteger'; o.placeholder = '10'; o.rmempty = true; // LLDP-MED inventory TLV transmission (-i) o = s.taboption(tab, form.Flag, 'lldpmed_no_inventory', _('Disable LLDP-MED inventory TLV transmission'), _('LLDPd will still receive (and publish using SNMP if enabled) ' + 'those LLDP-MED TLV but will not send them. Use this option ' + 'if you do not want to transmit sensitive information like serial numbers.')); o.default = '0'; // Disable advertising of kernel release, version and machine. (-k) o = s.taboption(tab, form.Flag, 'lldp_no_version', _('Disable advertising of kernel release, version and machine'), _('Kernel name (ie: Linux) will still be shared, and Inventory ' + 'software version will be set to %s.').format('Unknown')); o.default = '0'; // Filter neighbors (-H) o = s.taboption(tab, lldpd.cbiFilterSelect, 'filter', _('Specify the behaviour when detecting multiple neighbors'), _('The default filter is 15. Refer to "FILTERING NEIGHBORS".') + '
' + usage); o.default = 15; // Force port ID subtype o = s.taboption(tab, form.ListValue, 'lldp_portidsubtype', _('Force port ID subtype'), _('With this option, you can force the port identifier ' + 'to be the interface name or the MAC address.')); o.value('macaddress', _('Interface MAC address')); o.value('ifname', _('Interface name')); o.default = 'macaddress'; // The destination MAC address used to send LLDPDU o = s.taboption(tab, form.ListValue, 'lldp_agenttype', _('LLDPDU destination MAC'), _('Allows an agent ' + 'to control the propagation of LLDPDUs. By default, the ' + 'MAC address %s is used and limits the propagation ' + 'of the LLDPDU to the nearest bridge.').format('01:80:c2:00:00:0e')); o.value('nearest-bridge', '01:80:c2:00:00:0e (nearest-bridge)'); o.value('nearest-nontpmr-bridge', '01:80:c2:00:00:03 (nearest-nontpmr-bridge)'); o.value('nearest-customer-bridge', '01:80:c2:00:00:00 (nearest-customer-bridge)'); o.default = 'nearest-bridge'; }, // ----------------------------------------------------------------------------------------- // // Protocols Support // // ----------------------------------------------------------------------------------------- /** @private */ populateProtocolsOptions: function(s, tab, data) { var o; o = s.taboption(tab, form.SectionValue, '_protocols', form.TypedSection, 'lldpd'); var ss = o.subsection; ss.anonymous = true; ss.addremove = false; // // LLDPD // Link Layer Discovery Protocol // ss.tab('lldp', _('LLDP')); o = ss.taboption('lldp', form.Flag, 'enable_lldp', _('Enable LLDP')); o.default = '1'; o.rmempty = true; o = ss.taboption('lldp', form.Flag, 'force_lldp', _('Force sending LLDP packets'), _('Even when there is no LLDP peer ' + 'detected but there is a peer speaking another protocol detected.') + '
' + _('By default, LLDP packets are sent when there is a peer speaking ' + 'LLDP detected or when there is no peer at all.')); o.default = '0'; o.rmempty = true; o.depends('enable_lldp', '1'); // // CDP // Cisco Discovery Protocol // ss.tab('cdp', _('CDP')); o = ss.taboption('cdp', form.Flag, 'enable_cdp', _('Enable CDP'), _('Enable the support of CDP protocol to deal with Cisco routers ' + 'that do not speak LLDP')); o.default = '1'; o.rmempty = false; o = ss.taboption('cdp', form.ListValue, 'cdp_version', _('CDP version')); o.value('cdpv1v2', _('CDPv1 and CDPv2')); o.value('cdpv2', _('Only CDPv2')); o.depends('enable_cdp', '1'); o.default = 'cdpv1v2'; o = ss.taboption('cdp', form.Flag, 'force_cdp', _('Send CDP packets even if no CDP peer detected')); o.default = '0'; o.rmempty = true; o.depends('enable_cdp', '1'); o = ss.taboption('cdp', form.Flag, 'force_cdpv2', _('Force sending CDPv2 packets')); o.default = '0'; o.rmempty = true; o.depends({ force_cdp: '1', enable_cdp: '1', cdp_version: 'cdpv1v2' }); // // FDP // Foundry Discovery Protocol // ss.tab('fdp', _('FDP')); o = ss.taboption('fdp', form.Flag, 'enable_fdp', _('Enable FDP'), _('Enable the support of FDP protocol to deal with Foundry routers ' + 'that do not speak LLDP')); o.default = '1'; o.rmempty = false; o = ss.taboption('fdp', form.Flag, 'force_fdp', _('Send FDP packets even if no FDP peer detected')); o.default = '0'; o.rmempty = true; o.depends('enable_fdp', '1'); // // EDP // Extreme Discovery Protocol // ss.tab('edp', _('EDP')); o = ss.taboption('edp', form.Flag, 'enable_edp', _('Enable EDP'), _('Enable the support of EDP protocol to deal with Extreme routers ' + 'and switches that do not speak LLDP.')); o.default = '1'; o.rmempty = false; o = ss.taboption('edp', form.Flag, 'force_edp', _('Send EDP packets even if no EDP peer detected')); o.default = '0'; o.rmempty = true; o.depends('enable_edp', '1'); // // SONMP // SynOptics Network Management Protocol // // a.k.a. // Nortel Topology Discovery Protocol (NTDP) // Nortel Discovery Protocol (NDP) // Bay Network Management Protocol (BNMP) // Bay Discovery Protocol (BDP) // ss.tab('sonmp', _('SONMP (NTDP, NDP, BNMP, BDP)')); o = ss.taboption('sonmp', form.Flag, 'enable_sonmp', _('Enable SONMP'), _('Enable the support of SONMP protocol to deal with Nortel ' + 'routers and switches that do not speak LLDP.')); o.default = '1'; o.rmempty = false; o = ss.taboption('sonmp', form.Flag, 'force_sonmp', _('Send SONMP packets even if no SONMP peer detected')); o.default = '0'; o.rmempty = true; o.depends('enable_sonmp', '1'); }, /** @private */ populateOptions: function(s, data) { var o; s.tab('basic', _('Basic Settings')); this.populateBasicOptions(s, 'basic', data); s.tab('ifaces', _('Network Interfaces')); this.populateIfacesOptions(s, 'ifaces', data); s.tab('advanced', _('Advanced Settings')); this.populateAdvancedOptions(s, 'advanced', data); s.tab('protocols', _('Protocols Support')); this.populateProtocolsOptions(s, 'protocols', data); }, render: function(data) { var m, s; m = new form.Map('lldpd', _('LLDPd Settings'), _('LLDPd is an implementation of IEEE 802.1ab') + ' ' + '(LLDP).' + ' ' + _('On this page you may configure LLDPd parameters.')); s = m.section(form.TypedSection, 'lldpd'); s.addremove = false; s.anonymous = true; this.populateOptions(s, data); return m.render(); }, });