summaryrefslogtreecommitdiffhomepage
path: root/applications/luci-app-lldpd/htdocs/luci-static/resources
diff options
context:
space:
mode:
Diffstat (limited to 'applications/luci-app-lldpd/htdocs/luci-static/resources')
-rw-r--r--applications/luci-app-lldpd/htdocs/luci-static/resources/lldpd.js193
-rw-r--r--applications/luci-app-lldpd/htdocs/luci-static/resources/lldpd/details_hide.svg13
-rw-r--r--applications/luci-app-lldpd/htdocs/luci-static/resources/lldpd/details_show.svg14
-rw-r--r--applications/luci-app-lldpd/htdocs/luci-static/resources/lldpd/lldpd.css121
-rw-r--r--applications/luci-app-lldpd/htdocs/luci-static/resources/view/lldpd/config.js484
-rw-r--r--applications/luci-app-lldpd/htdocs/luci-static/resources/view/lldpd/status.js711
6 files changed, 1536 insertions, 0 deletions
diff --git a/applications/luci-app-lldpd/htdocs/luci-static/resources/lldpd.js b/applications/luci-app-lldpd/htdocs/luci-static/resources/lldpd.js
new file mode 100644
index 0000000000..bc39ed5018
--- /dev/null
+++ b/applications/luci-app-lldpd/htdocs/luci-static/resources/lldpd.js
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2018-2020, Tano Systems LLC. All Rights Reserved.
+ * Anton Kikin <a.kikin@tano-systems.com>
+ */
+
+'use strict';
+'require ui';
+'require form';
+'require network';
+'require session';
+'require uci';
+
+/*
+ * Filter neighbors (-H)
+ *
+ * The filter column means that filtering is enabled
+ * The 1proto column tells that only one protocol will be kept.
+ * The 1neigh column tells that only one neighbor will be kept.
+ *
+ * incoming outgoing
+ * filter 1proto 1neigh filter 1proto 1neigh
+ * 0
+ * 1 x x x x
+ * 2 x x
+ * 3 x x
+ * 4 x x
+ * 5 x
+ * 6 x
+ * 7 x x x x x
+ * 8 x x x
+ * 9 x x x x
+ * 10 x x
+ * 11 x x
+ * 12 x x x x
+ * 13 x x x
+ * 14 x x x x
+ * 15 x x x
+ * 16 x x x x x
+ * 17 x x x x
+ * 18 x x x
+ * 19 x x x
+ */
+var cbiFilterSelect = form.Value.extend({
+ __name__: 'CBI.LLDPD.FilterSelect',
+
+ __init__: function() {
+ this.super('__init__', arguments);
+
+ this.selected = null;
+
+ this.filterVal = [
+ [ 0, 0, 0, 0, 0, 0 ],
+ [ 1, 1, 0, 1, 1, 0 ],
+ [ 1, 1, 0, 0, 0, 0 ],
+ [ 0, 0, 0, 1, 1, 0 ],
+ [ 1, 0, 0, 1, 0, 0 ],
+ [ 1, 0, 0, 0, 0, 0 ],
+ [ 0, 0, 0, 1, 0, 0 ],
+ [ 1, 1, 1, 1, 1, 0 ],
+ [ 1, 1, 1, 0, 0, 0 ],
+ [ 1, 0, 1, 1, 1, 0 ],
+ [ 0, 0, 0, 1, 0, 1 ],
+ [ 1, 0, 1, 0, 0, 0 ],
+ [ 1, 0, 1, 1, 0, 1 ],
+ [ 1, 0, 1, 1, 0, 0 ],
+ [ 1, 1, 0, 1, 0, 1 ],
+ [ 1, 1, 0, 1, 0, 0 ],
+ [ 1, 1, 1, 1, 0, 1 ],
+ [ 1, 1, 1, 1, 0, 0 ],
+ [ 1, 0, 0, 1, 0, 1 ],
+ [ 1, 0, 0, 1, 1, 0 ]
+ ];
+ },
+
+ /** @private */
+ handleRowClick: function(section_id, ev) {
+ var row = ev.currentTarget;
+ var tbody = row.parentNode;
+ var selected = row.getAttribute('data-filter');
+ var input = tbody.querySelector('[id="' + this.cbid(section_id) + '-' + selected + '"]');
+
+ this.selected = selected;
+
+ tbody.querySelectorAll('tr').forEach(function(e) {
+ e.classList.remove('lldpd-filter-selected');
+ });
+
+ input.checked = true;
+ row.classList.add('lldpd-filter-selected');
+ },
+
+ formvalue: function(section_id) {
+ return this.selected || this.cfgvalue(section_id);
+ },
+
+ renderFrame: function(section_id, in_table, option_index, nodes) {
+ var tmp = this.description;
+
+ // Prepend description with table legend
+ this.description =
+ '<ul><li>' + _('E &mdash; enable filter') + '</li>' +
+ '<li>' + _('P &mdash; keep only one protocol') + '</li>' +
+ '<li>' + _('N &mdash; keep only one neighbor') + '</li>' +
+ '</ul>' + this.description;
+
+ var rendered = this.super('renderFrame', arguments);
+
+ // Restore original description
+ this.description = tmp;
+
+ return rendered;
+ },
+
+ renderWidget: function(section_id, option_index, cfgvalue) {
+ var selected = parseInt(cfgvalue) - 1;
+
+ var tbody = [];
+
+ var renderFilterVal = L.bind(function(row, col) {
+ return this.filterVal[row][col] ? '&#x2714;' : '';
+ }, this);
+
+ for (var i = 0; i < this.filterVal.length; i++) {
+ tbody.push(E('tr', {
+ 'class': ((selected == i) ? 'lldpd-filter-selected' : ''),
+ 'click': L.bind(this.handleRowClick, this, section_id),
+ 'data-filter': i,
+ }, [
+ E('td', {}, [
+ E('input', {
+ 'class': 'cbi-input-radio',
+ 'data-update': 'click change',
+ 'type': 'radio',
+ 'id': this.cbid(section_id) + '-' + i,
+ 'name': this.cbid(section_id),
+ 'checked': (selected == i) ? '' : null,
+ 'value': i
+ })
+ ]),
+ E('td', {}, i),
+ E('td', {}, renderFilterVal(i, 0)),
+ E('td', {}, renderFilterVal(i, 1)),
+ E('td', {}, renderFilterVal(i, 2)),
+ E('td', {}, renderFilterVal(i, 3)),
+ E('td', {}, renderFilterVal(i, 4)),
+ E('td', {}, renderFilterVal(i, 5))
+ ]));
+ };
+
+ var table = E('table', { 'class': 'lldpd-filter', 'id': this.cbid(section_id) }, [
+ E('thead', {}, [
+ E('tr', {}, [
+ E('th', { 'rowspan': 2 }),
+ E('th', { 'rowspan': 2 }, _('Filter')),
+ E('th', { 'colspan': 3 }, _('Incoming')),
+ E('th', { 'colspan': 3 }, _('Outgoing'))
+ ]),
+ E('tr', {}, [
+ E('th', {}, 'E'),
+ E('th', {}, 'P'),
+ E('th', {}, 'N'),
+ E('th', {}, 'E'),
+ E('th', {}, 'P'),
+ E('th', {}, 'N'),
+ ])
+ ]),
+ E('tbody', {}, tbody)
+ ]);
+
+ return table;
+ },
+});
+
+function init() {
+ return new Promise(function(resolveFn, rejectFn) {
+ var data = session.getLocalData('luci-app-lldpd');
+ if (data !== null) {
+ return resolveFn();
+ }
+
+ data = {};
+
+ return uci.load('luci').then(function() {
+ session.setLocalData('luci-app-lldpd', data);
+ return resolveFn();
+ });
+ });
+}
+
+return L.Class.extend({
+ cbiFilterSelect: cbiFilterSelect,
+ init: init,
+});
diff --git a/applications/luci-app-lldpd/htdocs/luci-static/resources/lldpd/details_hide.svg b/applications/luci-app-lldpd/htdocs/luci-static/resources/lldpd/details_hide.svg
new file mode 100644
index 0000000000..7607870d58
--- /dev/null
+++ b/applications/luci-app-lldpd/htdocs/luci-static/resources/lldpd/details_hide.svg
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg version="1.1" viewBox="0 0 18 11.12" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+<metadata>
+<rdf:RDF>
+<cc:Work rdf:about="">
+<dc:format>image/svg+xml</dc:format>
+<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+</cc:Work>
+</rdf:RDF>
+</metadata>
+<path d="m 9,11.12 9,-9 L 15.88,0 9,6.88 2.12,0 0,2.12 Z"/>
+<path d="m-9-12h36v36h-36z" fill="none"/>
+</svg>
diff --git a/applications/luci-app-lldpd/htdocs/luci-static/resources/lldpd/details_show.svg b/applications/luci-app-lldpd/htdocs/luci-static/resources/lldpd/details_show.svg
new file mode 100644
index 0000000000..867ef4b998
--- /dev/null
+++ b/applications/luci-app-lldpd/htdocs/luci-static/resources/lldpd/details_show.svg
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg version="1.1" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+<metadata>
+<rdf:RDF>
+<cc:Work rdf:about="">
+<dc:format>image/svg+xml</dc:format>
+<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+<dc:title/>
+</cc:Work>
+</rdf:RDF>
+</metadata>
+<path d="m4.585 2.83 9.17 9.17-9.17 9.17 2.83 2.83 12-12-12-12z"/>
+<path d="m0-24h48v48h-48z" fill="none"/>
+</svg>
diff --git a/applications/luci-app-lldpd/htdocs/luci-static/resources/lldpd/lldpd.css b/applications/luci-app-lldpd/htdocs/luci-static/resources/lldpd/lldpd.css
new file mode 100644
index 0000000000..0f3c0e1b98
--- /dev/null
+++ b/applications/luci-app-lldpd/htdocs/luci-static/resources/lldpd/lldpd.css
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2020, Tano Systems LLC. All Rights Reserved.
+ * Author: Anton Kikin <a.kikin@tano-systems.com>
+ */
+
+/*
+ * Filter select widget
+ */
+table.lldpd-filter td,
+table.lldpd-filter th {
+ border: 1px solid #ccc !important;
+ padding: 2px 10px 2px 10px;
+ text-align: center;
+}
+
+table.lldpd-filter td,
+table.lldpd-filter td input[type="radio"] {
+ vertical-align: middle;
+}
+
+table.lldpd-filter th { font-weight: bold; }
+
+table.lldpd-filter tbody tr {
+ cursor: pointer;
+}
+
+table.lldpd-filter tr.lldpd-filter-selected td {
+ background-color: #eeeeee;
+}
+
+/*
+ * Parameters
+ */
+.lldpd-params {
+ column-count: 2;
+ -moz-column-count: 2;
+ -webkit-column-count: 2;
+ column-gap: 24px;
+ -moz-column-gap: 24px;
+ -webkit-column-gap: 24px;
+}
+
+@media only screen and (max-width: 850px) {
+ .lldpd-params {
+ column-count: 1;
+ -moz-column-count: 1;
+ -webkit-column-count: 1;
+ }
+}
+
+.lldpd-params > div {
+ display: grid;
+ grid-template-columns: 1fr auto;
+ border-bottom: 1px solid #e6e6e6;
+ padding: 0 8px;
+ -webkit-column-break-inside: avoid;
+ -moz-column-break-inside: avoid;
+ column-break-inside: avoid;
+}
+
+.td .lldpd-params > div:last-of-type {
+ border-bottom: none;
+}
+
+.lldpd-params .lldpd-param {
+ margin-right: 10px;
+ font-weight: bold;
+}
+
+.lldpd-params .lldpd-param::after {
+ content: ':';
+}
+
+.lldpd-params .lldpd-param-value {
+ white-space: normal;
+ font-weight: normal;
+ text-align: right;
+}
+
+.td .lldpd-params {
+ column-count: 1;
+ -moz-column-count: 1;
+ -webkit-column-count: 1;
+}
+
+.td .lldpd-params > div {
+ padding: 0;
+}
+
+/*
+ * Status table
+ */
+.lldpd-folded,
+.lldpd-unfolded {
+ width: 100%;
+}
+
+.lldpd-table .tr .td { cursor: pointer; }
+
+.lldpd-protocol-badge {
+ display: inline-block;
+ width: auto !important;
+ width: fit-content !important;
+ width: -moz-fit-content !important;
+ width: -webkit-fit-content !important;
+ box-shadow: 0 1px 3px 0 grey;
+ padding: 0px 8px;
+ border-radius: 5px;
+ width: 100%;
+ background-color: #e6e6e6;
+ border: 0;
+ margin-right: 5px;
+ margin-bottom: 5px;
+ color: #595959;
+}
+
+.lldpd-protocol-badge.lldpd-protocol-lldp { background-color: #b7efcf; border-color: #2abd69; color: #165e34; }
+.lldpd-protocol-badge.lldpd-protocol-cdp { background-color: #b2daf3; border-color: #46a6e2; color: #1a74ac; }
+.lldpd-protocol-badge.lldpd-protocol-fdp { background-color: #f9e3b3; border-color: #b7820f; color: #b7820f; }
+.lldpd-protocol-badge.lldpd-protocol-edp { background-color: #f9e3f9; border-color: #e380e3; color: #b70f82; }
+.lldpd-protocol-badge.lldpd-protocol-sonmp { background-color: #f4ffc4; border-color: #a7ce00; color: #7e9b00; }
diff --git a/applications/luci-app-lldpd/htdocs/luci-static/resources/view/lldpd/config.js b/applications/luci-app-lldpd/htdocs/luci-static/resources/view/lldpd/config.js
new file mode 100644
index 0000000000..a4761f969e
--- /dev/null
+++ b/applications/luci-app-lldpd/htdocs/luci-static/resources/view/lldpd/config.js
@@ -0,0 +1,484 @@
+/*
+ * Copyright (c) 2020 Tano Systems LLC. All Rights Reserved.
+ * Author: Anton Kikin <a.kikin@tano-systems.com>
+ */
+
+'use strict';
+'require rpc';
+'require form';
+'require lldpd';
+'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 }
+});
+
+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')
+ ]);
+ },
+
+ // -----------------------------------------------------------------------------------------
+ //
+ // Basic Options
+ //
+ // -----------------------------------------------------------------------------------------
+
+ /** @private */
+ populateBasicOptions: function(s, tab, data) {
+ var o;
+ var serviceEnabled = data[0];
+
+ // 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('mstpd', section_id, 'enabled', value);
+
+ if (value == '1') {
+ // Enable and start
+ return callInitAction('lldpd', 'enable').then(function() {
+ return callInitAction('lldpd', 'start');
+ });
+ }
+ else {
+ // Stop and disable
+ return callInitAction('lldpd', 'stop').then(function() {
+ return callInitAction('lldpd', 'disable');
+ });
+ }
+ };
+
+ // System description
+ o = s.taboption(tab, form.Value, 'lldp_description',
+ _('System description'),
+ _('Override system description with the provided description.'));
+
+ o.placeholder = 'System description';
+
+ // System hostname
+ o = s.taboption(tab, form.Value, 'lldp_hostname',
+ _('System hostname'),
+ _('Override system hostname with the provided value.'));
+
+ o.placeholder = 'System hostname';
+
+ // Host location
+ o = s.taboption(tab, form.Value, 'lldp_location',
+ _('Host location'),
+ _('Override the location of the host announced by lldp.'));
+
+ o.placeholder = 'address country EU';
+
+ // Platform
+ o = s.taboption(tab, form.Value, 'lldp_platform',
+ _('System platform description'),
+ _('Override the platform description with the provided value. ' +
+ 'The default description is the kernel name (Linux).'));
+
+ o.placeholder = 'System platform description';
+
+ // Management addresses of this system
+ o = s.taboption(tab, form.Value, 'lldp_mgmt_ip',
+ _('Management addresses of this system'),
+ _('Specify the management addresses of this system. ' +
+ 'If not specified, the first IPv4 and the first ' +
+ 'IPv6 are used. If an exact IP address is provided, it is used ' +
+ 'as a management address without any check. If you want to ' +
+ 'blacklist IPv6 addresses, you can use <code>!*:*</code>. ' +
+ 'See more details about available patterns ' +
+ '<a href=\"https://vincentbernat.github.io/lldpd/usage.html\">here</a>.'));
+
+ o.placeholder = 'Management addresses';
+
+ // LLDP tx interval
+ o = s.taboption(tab, form.Value, 'lldp_tx_interval',
+ _('Transmit delay'),
+ _('The transmit delay is the delay between two ' +
+ 'transmissions of LLDP PDU. The default value ' +
+ 'is 30 seconds.'));
+
+ o.datatype = 'uinteger';
+ o.default = 30;
+ o.placeholder = 30;
+ o.rmempty = false;
+
+ o.validate = function(section_id, value) {
+ if (value != parseInt(value))
+ return _('Must be a number');
+ else if (value <= 0)
+ return _('Transmit delay must be greater than 0');
+ return true;
+ };
+
+ // LLDP tx hold
+ o = s.taboption(tab, form.Value, 'lldp_tx_hold',
+ _('Transmit hold value'),
+ _('This value is used to compute the TTL of transmitted ' +
+ 'packets which is the product of this value and of the ' +
+ 'transmit delay. The default value is 4 and therefore ' +
+ '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',
+ _('Enable receive-only mode'),
+ _('With this option, LLDPd will not send any frames. ' +
+ 'It will only listen to neighbors.'));
+
+ o.rmempty = false;
+ o.optional = false;
+ o.default = '0';
+ },
+
+ // -----------------------------------------------------------------------------------------
+ //
+ // Network Interfaces
+ //
+ // -----------------------------------------------------------------------------------------
+
+ /** @private */
+ populateIfacesOptions: function(s, tab, data) {
+ var o;
+
+ // Interfaces to listen on
+ o = s.taboption(tab, widgets.DeviceSelect, 'interface',
+ _('Network interfaces'),
+ _('Specify which interface to listen and send LLDPDU to. ' +
+ 'If no interfaces is specified, LLDPd will use all available physical interfaces.'));
+
+ o.nobridges = true;
+ o.rmempty = true;
+ o.multiple = true;
+ o.nocreate = true;
+ o.noaliases = true;
+ o.networks = null;
+
+ // ChassisID interfaces
+ o = s.taboption(tab, widgets.DeviceSelect, 'cid_interface',
+ _('Network interfaces for chassis ID computing'),
+ _('Specify which interfaces to use for computing chassis ID. ' +
+ 'If no interfaces is specified, all interfaces are considered. ' +
+ 'LLDPd will take the first MAC address from all the considered ' +
+ 'interfaces to compute the chassis ID.'));
+
+ o.nobridges = false;
+ o.rmempty = true;
+ o.multiple = true;
+ o.nocreate = true;
+ o.noaliases = true;
+ o.networks = null;
+ },
+
+ // -----------------------------------------------------------------------------------------
+ //
+ // 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 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 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 don\'t want to transmit sensible 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 \'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. For more details see \"FILTERING NEIGHBORS\" section ' +
+ '<a href=\"https://vincentbernat.github.io/lldpd/usage.html\">here</a>.'));
+
+ 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',
+ _('The destination MAC address used to send LLDPDU'),
+ _('The destination MAC address used to send LLDPDU allows an agent ' +
+ 'to control the propagation of LLDPDUs. By default, the ' +
+ '<code>01:80:c2:00:00:0e</code> MAC address is used and limit the propagation ' +
+ 'of the LLDPDU to the nearest bridge.'));
+
+ 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 to send LLDP packets'),
+ _('Force to send 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 a implementation of IEEE 802.1ab ' +
+ '(<abbr title=\"Link Layer Discovery Protocol\">LLDP</abbr>).') +
+ _('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();
+ },
+});
diff --git a/applications/luci-app-lldpd/htdocs/luci-static/resources/view/lldpd/status.js b/applications/luci-app-lldpd/htdocs/luci-static/resources/view/lldpd/status.js
new file mode 100644
index 0000000000..96847e11c3
--- /dev/null
+++ b/applications/luci-app-lldpd/htdocs/luci-static/resources/view/lldpd/status.js
@@ -0,0 +1,711 @@
+/*
+ * Copyright (c) 2020 Tano Systems. All Rights Reserved.
+ * Author: Anton Kikin <a.kikin@tano-systems.com>
+ */
+
+'use strict';
+'require rpc';
+'require form';
+'require lldpd';
+'require dom';
+'require poll';
+
+var callLLDPStatus = rpc.declare({
+ object: 'lldpd',
+ method: 'getStatus',
+ expect: {}
+});
+
+var dataMap = {
+ local: {
+ localChassis: null,
+ },
+ remote: {
+ neightbors: null,
+ statistics: null,
+ },
+};
+
+return L.view.extend({
+ __init__: function() {
+ this.super('__init__', arguments);
+
+ this.rowsUnfolded = {};
+
+ this.tableNeighbors = E('div', { 'class': 'table lldpd-table' }, [
+ E('div', { 'class': 'tr table-titles' }, [
+ E('div', { 'class': 'th left top' }, _('Local interface')),
+ E('div', { 'class': 'th left top' }, _('Protocol')),
+ E('div', { 'class': 'th left top' }, _('Discovered chassis')),
+ E('div', { 'class': 'th left top' }, _('Discovered port')),
+ ]),
+ E('div', { 'class': 'tr center placeholder' }, [
+ E('div', { 'class': 'td' }, E('em', { 'class': 'spinning' },
+ _('Collecting data...'))),
+ ])
+ ]);
+
+ this.tableStatistics = E('div', { 'class': 'table lldpd-table' }, [
+ E('div', { 'class': 'tr table-titles' }, [
+ E('div', { 'class': 'th left top' }, _('Local interface')),
+ E('div', { 'class': 'th left top' }, _('Protocol')),
+ E('div', { 'class': 'th left top' }, _('Administrative Status')),
+ E('div', { 'class': 'th right top' }, _('Tx')),
+ E('div', { 'class': 'th right top' }, _('Rx')),
+ E('div', { 'class': 'th right top' }, _('Tx discarded')),
+ E('div', { 'class': 'th right top' }, _('Rx unrecognized')),
+ E('div', { 'class': 'th right top' }, _('Ageout count')),
+ E('div', { 'class': 'th right top' }, _('Insert count')),
+ E('div', { 'class': 'th right top' }, _('Delete count')),
+ ]),
+ E('div', { 'class': 'tr center placeholder' }, [
+ E('div', { 'class': 'td' }, E('em', { 'class': 'spinning' },
+ _('Collecting data...'))),
+ ])
+ ]);
+
+ // 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([
+ L.resolveDefault(callLLDPStatus(), {}),
+ lldpd.init(),
+ ]);
+ },
+
+ /** @private */
+ renderParam: function(param, value) {
+ if (typeof value === 'undefined')
+ return '';
+
+ return E('div', {}, [
+ E('span', { 'class': 'lldpd-param' }, param),
+ E('span', { 'class': 'lldpd-param-value' }, value)
+ ]);
+ },
+
+ /** @private */
+ renderAge: function(v) {
+ if (typeof v === 'undefined')
+ return "&#8211;";
+
+ return E('nobr', {}, v);
+ },
+
+ /** @private */
+ renderIdType: function(v) {
+ if (typeof v === 'undefined')
+ return "&#8211;";
+
+ if (v == 'mac')
+ return _('MAC address');
+ else if (v == 'ifname')
+ return _('Interface name');
+ else if (v == 'local')
+ return _('Local ID');
+ else if (v == 'ip')
+ return _('IP address');
+
+ return v;
+ },
+
+ /** @private */
+ renderProtocol: function(v) {
+ if (typeof v === 'undefined' || v == 'unknown')
+ return "&#8211;";
+
+ if (v == 'LLDP')
+ return E('span', { 'class': 'lldpd-protocol-badge lldpd-protocol-lldp' }, v);
+ else if ((v == 'CDPv1') || (v == 'CDPv2'))
+ return E('span', { 'class': 'lldpd-protocol-badge lldpd-protocol-cdp' }, v);
+ else if (v == 'FDP')
+ return E('span', { 'class': 'lldpd-protocol-badge lldpd-protocol-fdp' }, v);
+ else if (v == 'EDP')
+ return E('span', { 'class': 'lldpd-protocol-badge lldpd-protocol-edp' }, v);
+ else if (v == 'SONMP')
+ return E('span', { 'class': 'lldpd-protocol-badge lldpd-protocol-sonmp' }, v);
+ else
+ return E('span', { 'class': 'lldpd-protocol-badge' }, v);
+ },
+
+ /** @private */
+ renderAdminStatus: function(status) {
+ if ((typeof status === 'undefined') || !Array.isArray(status))
+ return '&#8211;';
+
+ if (status[0].value === 'RX and TX')
+ return _('Rx and Tx');
+ else if (status[0].value === 'RX only')
+ return _('Rx only');
+ else if (status[0].value === 'TX only')
+ return _('Tx only');
+ else if (status[0].value === 'disabled')
+ return _('Disabled');
+ else
+ return _('Unknown');
+ },
+
+ /** @private */
+ renderNumber: function(v) {
+ if (parseInt(v))
+ return v;
+
+ return '&#8211;';
+ },
+
+ /** @private */
+ renderPort: function(port) {
+ if (typeof port.port !== 'undefined')
+ {
+ if (typeof port.port[0].descr !== 'undefined' &&
+ typeof port.port[0].id[0].value !== 'undefined' &&
+ port.port[0].descr[0].value !== port.port[0].id[0].value)
+ {
+ return [
+ E('strong', {}, port.port[0].descr[0].value),
+ E('br', {}),
+ port.port[0].id[0].value
+ ];
+ }
+ else
+ {
+ if (typeof port.port[0].descr !== 'undefined')
+ return port.port[0].descr[0].value;
+ else
+ return port.port[0].id[0].value;
+ }
+ }
+ else
+ {
+ return '%s'.format(port.name);
+ }
+ },
+
+ /** @private */
+ renderPortParamTableShort: function(port) {
+ var items = [];
+
+ items.push(this.renderParam(_('Name'), port.name));
+ items.push(this.renderParam(_('Age'), this.renderAge(port.age)));
+
+ return E('div', { 'class': 'lldpd-params' }, items);
+ },
+
+ /** @private */
+ renderPortParamTable: function(port, only_id_and_ttl) {
+ var items = [];
+
+ if (!only_id_and_ttl) {
+ items.push(this.renderParam(_('Name'), port.name));
+ items.push(this.renderParam(_('Age'), this.renderAge(port.age)));
+ }
+
+ if (typeof port.port !== 'undefined')
+ {
+ if (typeof port.port[0].id !== 'undefined')
+ {
+ items.push(this.renderParam(_('Port ID'),
+ port.port[0].id[0].value));
+
+ items.push(this.renderParam(_('Port ID type'),
+ this.renderIdType(port.port[0].id[0].type)));
+ }
+
+ if (typeof port.port[0].descr !== 'undefined')
+ items.push(this.renderParam(_('Port description'),
+ port.port[0].descr[0].value));
+
+ if (typeof port.ttl !== 'undefined')
+ items.push(this.renderParam(_('TTL'), port.ttl[0].ttl));
+ else if (port.port[0].ttl !== 'undefined')
+ items.push(this.renderParam(_('TTL'), port.port[0].ttl[0].value));
+
+ if (typeof port.port[0].mfs !== 'undefined')
+ items.push(this.renderParam(_('MFS'), port.port[0].mfs[0].value));
+ }
+
+ return E('div', { 'class': 'lldpd-params' }, items);
+ },
+
+ /** @private */
+ renderChassis: function(ch) {
+ if (typeof ch.name !== 'undefined' &&
+ typeof ch.descr !== 'undefined' &&
+ typeof ch.name[0].value !== 'undefined' &&
+ typeof ch.descr[0].value !== 'undefined')
+ {
+ return [
+ E('strong', {}, ch.name[0].value),
+ E('br', {}),
+ ch.descr[0].value
+ ];
+ }
+ else if (typeof ch.name !== 'undefined' &&
+ typeof ch.name[0].value !== 'undefined')
+ return E('strong', {}, ch.name[0].value);
+ else if (typeof ch.descr !== 'undefined' &&
+ typeof ch.descr[0].value !== 'undefined')
+ return ch.descr[0].value;
+ else if (typeof ch.id !== 'undefined' &&
+ typeof ch.id[0].value !== 'undefined')
+ return ch.id[0].value;
+ else
+ return _('Unknown');
+ },
+
+ /** @private */
+ renderChassisParamTable: function(ch) {
+ var items = [];
+
+ if (typeof ch.name !== 'undefined')
+ items.push(this.renderParam(_('Name'), ch.name[0].value));
+
+ if (typeof ch.descr !== 'undefined')
+ items.push(this.renderParam(_('Description'), ch.descr[0].value));
+
+ if (typeof ch.id !== 'undefined') {
+ items.push(this.renderParam(_('ID'), ch.id[0].value));
+ items.push(this.renderParam(_('ID type'),
+ this.renderIdType(ch.id[0].type)));
+ }
+
+ // Management addresses
+ if (typeof ch['mgmt-ip'] !== 'undefined') {
+ var ips = '';
+
+ if (ch['mgmt-ip'].length > 0) {
+ // Array of addresses
+ for (var ip = 0; ip < ch["mgmt-ip"].length; ip++)
+ ips += ch['mgmt-ip'][ip].value + '<br />';
+ }
+ else {
+ // One address
+ ips += ch['mgmt-ip'][0].value;
+ }
+
+ items.push(this.renderParam(_('Management IP(s)'), ips));
+ }
+
+ if (typeof ch.capability !== 'undefined') {
+ var caps = '';
+
+ if (ch.capability.length > 0)
+ {
+ // Array of capabilities
+ for (var cap = 0; cap < ch.capability.length; cap++) {
+ caps += ch.capability[cap].type;
+ caps += ' (' + (ch.capability[cap].enabled
+ ? _('enabled') : _('disabled')) + ')';
+ caps += '<br />';
+ }
+ }
+ else
+ {
+ // One capability
+ caps += ch.capability[0].type;
+ caps += ' (' + (ch.capability[0].enabled
+ ? _('enabled') : _('disabled')) + ')';
+ }
+
+ items.push(this.renderParam(_('Capabilities'), caps));
+ }
+
+ return E('div', { 'class': 'lldpd-params' }, items);
+ },
+
+ /** @private */
+ getFoldingImage: function(unfolded) {
+ return L.resource('lldpd/details_' +
+ (unfolded ? 'hide' : 'show') + '.svg');
+ },
+
+ /** @private */
+ generateRowId: function(str) {
+ return str.replace(/[^a-z0-9]/gi, '-');
+ },
+
+ /** @private */
+ handleToggleFoldingRow: function(row, row_id) {
+ var e_img = row.querySelector('img');
+ var e_folded = row.querySelectorAll('.lldpd-folded');
+ var e_unfolded = row.querySelectorAll('.lldpd-unfolded');
+
+ if (e_folded.length != e_unfolded.length)
+ return;
+
+ var do_unfold = (e_folded[0].style.display !== 'none');
+ this.rowsUnfolded[row_id] = do_unfold;
+
+ for (var i = 0; i < e_folded.length; i++)
+ {
+ if (do_unfold)
+ {
+ e_folded[i].style.display = 'none';
+ e_unfolded[i].style.display = 'block';
+ }
+ else
+ {
+ e_folded[i].style.display = 'block';
+ e_unfolded[i].style.display = 'none';
+ }
+ }
+
+ e_img.src = this.getFoldingImage(do_unfold);
+ },
+
+ /** @private */
+ makeFoldingTableRow: function(row, unfolded) {
+ //
+ // row[0] - row id
+ // row[1] - contents for first cell in row
+ // row[2] - contents for second cell in row
+ // ...
+ // row[N] - contents for N-th cell in row
+ //
+ if (row.length < 2)
+ return row;
+
+ for (let i = 1; i < row.length; i++) {
+ if (i == 1) {
+ // Fold/unfold image appears only in first column
+ var dImg = E('div', { 'style': 'padding: 0 8px 0 0;' }, [
+ E('img', { 'width': '16px', 'src': this.getFoldingImage(unfolded) }),
+ ]);
+ }
+
+ if (Array.isArray(row[i])) {
+ // row[i][0] = folded contents
+ // row[i][1] = unfolded contents
+
+ // Folded cell data
+ let dFolded = E('div', {
+ 'class': 'lldpd-folded',
+ 'style': unfolded ? 'display: none;' : 'display: block;'
+ }, row[i][0]);
+
+ // Unfolded cell data
+ let dUnfolded = E('div', {
+ 'class': 'lldpd-unfolded',
+ 'style': unfolded ? 'display: block;' : 'display: none;'
+ }, row[i][1]);
+
+ if (i == 1) {
+ row[i] = E('div', {
+ 'style': 'display: flex; flex-wrap: nowrap;'
+ }, [ dImg, dFolded, dUnfolded ]);
+ }
+ else {
+ row[i] = E('div', {}, [ dFolded, dUnfolded ]);
+ }
+ }
+ else {
+ // row[i] = same content for folded and unfolded states
+
+ if (i == 1) {
+ row[i] = E('div', {
+ 'style': 'display: flex; flex-wrap: nowrap;'
+ }, [ dImg, E('div', row[i]) ]);
+ }
+ }
+ }
+
+ return row;
+ },
+
+ /** @private */
+ makeNeighborsTableRow: function(obj) {
+ if (typeof obj === 'undefined')
+ obj.name = 'Unknown';
+
+ var new_id = obj.name + '-' + obj.rid;
+
+ if (typeof obj.port !== 'undefined') {
+ if (typeof obj.port[0].id !== 'undefined')
+ new_id += "-" + obj.port[0].id[0].value;
+
+ if (typeof obj.port[0].descr !== 'undefined')
+ new_id += "-" + obj.port[0].descr[0].value;
+ }
+
+ var row_id = this.generateRowId(new_id);
+
+ return this.makeFoldingTableRow([
+ row_id,
+ [
+ '%s'.format(obj.name),
+ this.renderPortParamTableShort(obj)
+ ],
+ this.renderProtocol(obj.via),
+ [
+ this.renderChassis(obj.chassis[0]),
+ this.renderChassisParamTable(obj.chassis[0])
+ ],
+ [
+ this.renderPort(obj),
+ this.renderPortParamTable(obj, true)
+ ]
+ ], this.rowsUnfolded[row_id] || false);
+ },
+
+ /** @private */
+ renderInterfaceProtocols: function(iface, neighbors) {
+ if ((typeof iface === 'undefined') ||
+ (typeof neighbors == 'undefined') ||
+ (typeof neighbors.lldp[0] === 'undefined') ||
+ (typeof neighbors.lldp[0].interface === 'undefined'))
+ return "&#8211;";
+
+ var name = iface.name;
+ var protocols = [];
+
+ /* Search protocols for interface <name> */
+ neighbors.lldp[0].interface.forEach(function(n) {
+ if (n.name !== name)
+ return;
+
+ protocols.push(this.renderProtocol(n.via));
+ }.bind(this));
+
+ if (protocols.length > 0)
+ return E('span', {}, protocols);
+ else
+ return "&#8211;";
+ },
+
+ /** @private */
+ makeStatisticsTableRow: function(sobj, iobj, neighbors) {
+ var row_id = this.generateRowId(iobj.name);
+
+ return this.makeFoldingTableRow([
+ row_id,
+ [
+ this.renderPort(iobj), // folded
+ this.renderPortParamTable(iobj, false) // unfolded
+ ],
+ this.renderInterfaceProtocols(iobj, neighbors),
+ this.renderAdminStatus(iobj.status),
+ this.renderNumber(sobj.tx[0].tx),
+ this.renderNumber(sobj.rx[0].rx),
+ this.renderNumber(sobj.rx_discarded_cnt[0].rx_discarded_cnt),
+ this.renderNumber(sobj.rx_unrecognized_cnt[0].rx_unrecognized_cnt),
+ this.renderNumber(sobj.ageout_cnt[0].ageout_cnt),
+ this.renderNumber(sobj.insert_cnt[0].insert_cnt),
+ this.renderNumber(sobj.delete_cnt[0].delete_cnt)
+ ], this.rowsUnfolded[row_id] || false);
+ },
+
+ /** @private */
+ updateTable: function(table, data, placeholder) {
+ var target = isElem(table) ? table : document.querySelector(table);
+
+ if (!isElem(target))
+ return;
+
+ target.querySelectorAll(
+ '.tr.table-titles, .cbi-section-table-titles').forEach(L.bind(function(thead) {
+ var titles = [];
+
+ thead.querySelectorAll('.th').forEach(function(th) {
+ titles.push(th);
+ });
+
+ if (Array.isArray(data)) {
+ var n = 0, rows = target.querySelectorAll('.tr');
+
+ data.forEach(L.bind(function(row) {
+ var id = row[0];
+ var trow = E('div', { 'class': 'tr', 'click': L.bind(function(ev) {
+ this.handleToggleFoldingRow(ev.currentTarget, id);
+ // lldpd_folding_toggle(ev.currentTarget, id);
+ }, this) });
+
+ for (var i = 0; i < titles.length; i++) {
+ var text = (titles[i].innerText || '').trim();
+ var td = trow.appendChild(E('div', {
+ 'class': titles[i].className,
+ 'data-title': (text !== '') ? text : null
+ }, row[i + 1] || ''));
+
+ td.classList.remove('th');
+ td.classList.add('td');
+ }
+
+ trow.classList.add('cbi-rowstyle-%d'.format((n++ % 2) ? 2 : 1));
+
+ if (rows[n])
+ target.replaceChild(trow, rows[n]);
+ else
+ target.appendChild(trow);
+ }, this));
+
+ while (rows[++n])
+ target.removeChild(rows[n]);
+
+ if (placeholder && target.firstElementChild === target.lastElementChild) {
+ var trow = target.appendChild(
+ E('div', { 'class': 'tr placeholder' }));
+
+ var td = trow.appendChild(
+ E('div', { 'class': 'center ' + titles[0].className }, placeholder));
+
+ td.classList.remove('th');
+ td.classList.add('td');
+ }
+ } else {
+ thead.parentNode.style.display = 'none';
+
+ thead.parentNode.querySelectorAll('.tr, .cbi-section-table-row').forEach(function(trow) {
+ if (trow !== thead) {
+ var n = 0;
+ trow.querySelectorAll('.th, .td').forEach(function(td) {
+ if (n < titles.length) {
+ var text = (titles[n++].innerText || '').trim();
+ if (text !== '')
+ td.setAttribute('data-title', text);
+ }
+ });
+ }
+ });
+
+ thead.parentNode.style.display = '';
+ }
+ }, this));
+ },
+
+ /** @private */
+ startPolling: function() {
+ poll.add(L.bind(function() {
+ return callLLDPStatus().then(L.bind(function(data) {
+ this.renderData(data);
+ }, this));
+ }, this));
+ },
+
+ /** @private */
+ renderDataLocalChassis: function(data) {
+ if (data &&
+ typeof data !== 'undefined' &&
+ typeof data['local-chassis'] !== 'undefined' &&
+ typeof data['local-chassis'][0].chassis[0].name !== 'undefined') {
+ return this.renderChassisParamTable(data['local-chassis'][0].chassis[0]);
+ }
+ else {
+ return E('div', { 'class': 'alert-message warning' },
+ _('No data to display'));
+ }
+ },
+
+ /** @private */
+ renderDataNeighbors: function(neighbors) {
+ var rows = [];
+
+ if (neighbors &&
+ typeof neighbors !== 'undefined' &&
+ typeof neighbors.lldp !== 'undefined')
+ {
+ var ifaces = neighbors.lldp[0].interface;
+
+ // Fill table rows
+ if (typeof ifaces !== 'undefined') {
+ for (i = 0; i < ifaces.length; i++)
+ rows.push(this.makeNeighborsTableRow(ifaces[i]));
+ }
+ }
+
+ return rows;
+ },
+
+ /** @private */
+ renderDataStatistics: function(statistics, interfaces, neighbors) {
+ var rows = [];
+
+ if (statistics &&
+ interfaces &&
+ typeof statistics !== 'undefined' &&
+ typeof interfaces !== 'undefined' &&
+ typeof statistics.lldp !== 'undefined' &&
+ typeof interfaces.lldp !== 'undefined')
+ {
+ var sifaces = statistics.lldp[0].interface;
+ var ifaces = interfaces.lldp[0].interface;
+
+ if ((typeof sifaces !== 'undefined') &&
+ (typeof ifaces !== 'undefined')) {
+ for (var i = 0; i < sifaces.length; i++)
+ rows.push(this.makeStatisticsTableRow(sifaces[i], ifaces[i], neighbors));
+ }
+ }
+
+ return rows;
+ },
+
+ /** @private */
+ renderData: function(data) {
+ var r;
+
+ r = this.renderDataLocalChassis(data.chassis);
+ dom.content(document.getElementById('lldpd-local-chassis'), r);
+
+ r = this.renderDataNeighbors(data.neighbors);
+ this.updateTable(this.tableNeighbors, r,
+ _('No data to display'));
+
+ r = this.renderDataStatistics(data.statistics, data.interfaces, data.neighbors);
+ this.updateTable(this.tableStatistics, r,
+ _('No data to display'));
+ },
+
+ render: function(data) {
+ var m, s, ss, o;
+
+ m = new form.JSONMap(dataMap,
+ _('LLDP Status'),
+ _('This page allows you to see discovered LLDP neighbors, ' +
+ 'local interfaces statistics and local chassis information.'));
+
+ s = m.section(form.NamedSection, 'local', 'local',
+ _('Local Chassis'));
+
+ o = s.option(form.DummyValue, 'localChassis');
+ o.render = function() {
+ return E('div', { 'id': 'lldpd-local-chassis' }, [
+ E('em', { 'class': 'spinning' }, _('Collecting data...'))
+ ]);
+ };
+
+ s = m.section(form.NamedSection, 'remote', 'remote');
+
+ s.tab('neighbors', _('Discovered Neighbors'));
+ s.tab('statistics', _('Interface Statistics'));
+
+ o = s.taboption('neighbors', form.DummyValue, 'neighbors');
+ o.render = L.bind(function() {
+ return E('div', { 'class': 'table-wrapper' }, [
+ this.tableNeighbors
+ ]);
+ }, this);
+
+ o = s.taboption('statistics', form.DummyValue, 'statistics');
+ o.render = L.bind(function() {
+ return E('div', { 'class': 'table-wrapper' }, [
+ this.tableStatistics
+ ]);
+ }, this);
+
+ return m.render().then(L.bind(function(rendered) {
+ this.startPolling();
+ return rendered;
+ }, this));
+ },
+
+ handleSaveApply: null,
+ handleSave: null,
+ handleReset: null
+});