summaryrefslogtreecommitdiffhomepage
path: root/modules/luci-mod-network
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2019-09-12 11:06:38 +0200
committerJo-Philipp Wich <jo@mein.io>2019-09-12 11:09:57 +0200
commit82743b3bd4b4603f2c6b85566d07182cb0f1ce52 (patch)
tree9444cba040a334821e6593c13541792c718084ea /modules/luci-mod-network
parent5fd21bc9efedb29ff8376f8043ed2a4b74788d6b (diff)
luci-mod-network: reimplement switch configuration as client side view
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Diffstat (limited to 'modules/luci-mod-network')
-rw-r--r--modules/luci-mod-network/htdocs/luci-static/resources/view/network/switch.js369
-rw-r--r--modules/luci-mod-network/luasrc/controller/admin/network.lua15
-rw-r--r--modules/luci-mod-network/luasrc/model/cbi/admin_network/vlan.lua389
-rw-r--r--modules/luci-mod-network/luasrc/view/admin_network/switch_status.htm62
4 files changed, 370 insertions, 465 deletions
diff --git a/modules/luci-mod-network/htdocs/luci-static/resources/view/network/switch.js b/modules/luci-mod-network/htdocs/luci-static/resources/view/network/switch.js
new file mode 100644
index 000000000..b281bb180
--- /dev/null
+++ b/modules/luci-mod-network/htdocs/luci-static/resources/view/network/switch.js
@@ -0,0 +1,369 @@
+'use strict';
+'require rpc';
+'require uci';
+'require form';
+'require network';
+
+function parse_portvalue(section_id) {
+ var ports = L.toArray(uci.get('network', section_id, 'ports'));
+
+ for (var i = 0; i < ports.length; i++) {
+ var m = ports[i].match(/^(\d+)([tu]?)/);
+
+ if (m && m[1] == this.option)
+ return m[2] || 'u';
+ }
+
+ return '';
+}
+
+function validate_portvalue(section_id, value) {
+ if (value != 'u')
+ return true;
+
+ var sections = this.section.cfgsections();
+
+ for (var i = 0; i < sections.length; i++) {
+ if (sections[i] == section_id)
+ continue;
+
+ if (this.formvalue(sections[i]) == 'u')
+ return _('%s is untagged in multiple VLANs!').format(this.title);
+ }
+
+ return true;
+}
+
+function update_interfaces(old_ifname, new_ifname) {
+ var interfaces = uci.sections('network', 'interface');
+
+ for (var i = 0; i < interfaces.length; i++) {
+ var old_ifnames = L.toArray(interfaces[i].ifname),
+ new_ifnames = [],
+ changed = false;
+
+ for (var j = 0; j < old_ifnames.length; j++) {
+ if (old_ifnames[j] == old_ifname) {
+ new_ifnames.push(new_ifname);
+ changed = true;
+ }
+ else {
+ new_ifnames.push(old_ifnames[j]);
+ }
+ }
+
+ if (changed) {
+ uci.set('network', interfaces[i]['.name'], 'ifname', new_ifnames.join(' '));
+
+ L.ui.addNotification(null, E('p', _('Interface %q device auto-migrated from %q to %q.')
+ .replace(/%q/g, '"%s"').format(interfaces[i]['.name'], old_ifname, new_ifname)));
+ }
+ }
+}
+
+function render_port_status(node, portstate) {
+ if (!node)
+ return null;
+
+ if (!portstate.link)
+ L.dom.content(node, [
+ E('img', { src: L.resource('icons/port_down.png') }),
+ E('br'),
+ _('no link')
+ ]);
+ else
+ L.dom.content(node, [
+ E('img', { src: L.resource('icons/port_up.png') }),
+ E('br'),
+ '%d'.format(portstate.speed) + _('baseT'),
+ E('br'),
+ portstate.duplex ? _('full-duplex') : _('half-duplex')
+ ]);
+
+ return node;
+}
+
+function update_port_status(topologies) {
+ var tasks = [];
+
+ for (var switch_name in topologies)
+ tasks.push(callSwconfigPortState(switch_name).then(L.bind(function(switch_name, ports) {
+ for (var i = 0; i < ports.length; i++) {
+ var node = document.querySelector('[data-switch="%s"][data-port="%d"]'.format(switch_name, ports[i].port));
+ render_port_status(node, ports[i]);
+ }
+ }, topologies[switch_name], switch_name)));
+
+ return Promise.all(tasks);
+}
+
+var callSwconfigFeatures = rpc.declare({
+ object: 'luci',
+ method: 'getSwconfigFeatures',
+ params: [ 'switch' ],
+ expect: { '': {} }
+});
+
+var callSwconfigPortState = rpc.declare({
+ object: 'luci',
+ method: 'getSwconfigPortState',
+ params: [ 'switch' ],
+ expect: { result: [] }
+});
+
+return L.view.extend({
+ load: function() {
+ return network.getSwitchTopologies().then(function(topologies) {
+ var tasks = [];
+
+ for (var switch_name in topologies) {
+ tasks.push(callSwconfigFeatures(switch_name).then(L.bind(function(features) {
+ this.features = features;
+ }, topologies[switch_name])));
+ tasks.push(callSwconfigPortState(switch_name).then(L.bind(function(ports) {
+ this.portstate = ports;
+ }, topologies[switch_name])));
+ }
+
+ return Promise.all(tasks).then(function() { return topologies });
+ });
+ },
+
+ render: function(topologies) {
+ var m, s, o;
+
+ m = new form.Map('network', _('Switch'), _('The network ports on this device can be combined to several <abbr title=\"Virtual Local Area Network\">VLAN</abbr>s in which computers can communicate directly with each other. <abbr title=\"Virtual Local Area Network\">VLAN</abbr>s are often used to separate different network segments. Often there is by default one Uplink port for a connection to the next greater network like the internet and other ports for a local network.'));
+
+ var switchSections = uci.sections('network', 'switch');
+
+ for (var i = 0; i < switchSections.length; i++) {
+ var switchSection = switchSections[i],
+ sid = switchSection['.name'],
+ switch_name = switchSection.name || sid,
+ topology = topologies[switch_name];
+
+ if (!topology) {
+ L.ui.addNotification(null, _('Switch %q has an unknown topology - the VLAN settings might not be accurate.').replace(/%q/, switch_name));
+
+ topology = {
+ features: {},
+ netdevs: {
+ 5: 'eth0'
+ },
+ ports: [
+ { num: 0, label: 'Port 1' },
+ { num: 1, label: 'Port 2' },
+ { num: 2, label: 'Port 3' },
+ { num: 3, label: 'Port 4' },
+ { num: 4, label: 'Port 5' },
+ { num: 5, label: 'CPU (eth0)', device: 'eth0', need_tag: false }
+ ]
+ };
+ }
+
+ var feat = topology.features,
+ min_vid = feat.min_vid || 0,
+ max_vid = feat.max_vid || 16,
+ num_vlans = feat.num_vlans || 16,
+ switch_title = _('Switch %q').replace(/%q/, '"%s"'.format(switch_name)),
+ vlan_title = _('VLANs on %q').replace(/%q/, '"%s"'.format(switch_name));
+
+ if (feat.switch_title) {
+ switch_title += ' (%s)'.format(feat.switch_title);
+ vlan_title += ' (%s)'.format(feat.switch_title);
+ }
+
+ s = m.section(form.NamedSection, sid, 'switch', switch_title);
+ s.addremove = false;
+
+ if (feat.vlan_option)
+ s.option(form.Flag, feat.vlan_option, _('Enable VLAN functionality'));
+
+ if (feat.learning_option) {
+ o = s.option(form.Flag, feat.learning_option, _('Enable learning and aging'));
+ o.default = o.enabled;
+ }
+
+ if (feat.jumbo_option) {
+ o = s.option(form.Flag, feat.jumbo_option, _('Enable Jumbo Frame passthrough'));
+ o.enabled = '3';
+ o.rmempty = true;
+ }
+
+ if (feat.mirror_option) {
+ s.option(form.Flag, 'enable_mirror_rx', _('Enable mirroring of incoming packets'));
+ s.option(form.Flag, 'enable_mirror_tx', _('Enable mirroring of outgoing packets'));
+
+ var sp = s.option(form.ListValue, 'mirror_source_port', _('Mirror source port')),
+ mp = s.option(form.ListValue, 'mirror_monitor_port', _('Mirror monitor port'));
+
+ sp.depends('enable_mirror_rx', '1');
+ sp.depends('enable_mirror_tx', '1');
+
+ mp.depends('enable_mirror_rx', '1');
+ mp.depends('enable_mirror_tx', '1');
+
+ for (var j = 0; j < topology.ports.length; j++) {
+ sp.value(topology.ports[j].num, topology.ports[j].label);
+ mp.value(topology.ports[j].num, topology.ports[j].label);
+ }
+ }
+
+ s = m.section(form.TableSection, 'switch_vlan', vlan_title);
+ s.anonymous = true;
+ s.addremove = true;
+ s.addbtntitle = _('Add VLAN');
+ s.topology = topology;
+ s.device = switch_name;
+
+ s.filter = function(section_id) {
+ var device = uci.get('network', section_id, 'device');
+ return (device == switch_name);
+ };
+
+ s.cfgsections = function() {
+ var sections = form.TableSection.prototype.cfgsections.apply(this);
+
+ return sections.sort(function(a, b) {
+ var vidA = feat.vid_option ? uci.get('network', a, feat.vid_option) : null,
+ vidB = feat.vid_option ? uci.get('network', b, feat.vid_option) : null;
+
+ vidA = +(vidA != null ? vidA : uci.get('network', a, 'vlan') || 9999);
+ vidB = +(vidB != null ? vidB : uci.get('network', b, 'vlan') || 9999);
+
+ return (vidA - vidB);
+ });
+ };
+
+ s.handleAdd = function(ev) {
+ var sections = uci.sections('network', 'switch_vlan'),
+ section_id = uci.add('network', 'switch_vlan'),
+ max_vlan = 0,
+ max_vid = 0;
+
+ for (var j = 0; j < sections.length; j++) {
+ if (sections[j].device != s.device)
+ continue;
+
+ var vlan = +sections[j].vlan,
+ vid = feat.vid_option ? +sections[j][feat.vid_option] : null;
+
+ if (vlan > max_vlan)
+ max_vlan = vlan;
+
+ if (vid > max_vid)
+ max_vid = vid;
+ }
+
+ uci.set('network', section_id, 'device', s.device);
+ uci.set('network', section_id, 'vlan', max_vlan + 1);
+
+ if (feat.vid_option)
+ uci.set('network', section_id, feat.vid_option, max_vid + 1);
+
+ return this.map.save(null, true);
+ };
+
+ var port_opts = [];
+
+ o = s.option(form.Value, feat.vid_option || 'vlan', 'VLAN ID');
+ o.rmempty = false;
+ o.forcewrite = true;
+ o.vlan_used = {};
+ o.datatype = 'range(%u,%u)'.format(min_vid, feat.vid_option ? 4094 : num_vlans - 1);
+ o.description = _('Port status:');
+
+ o.validate = function(section_id, value) {
+ var v = +value,
+ m = feat.vid_option ? 4094 : num_vlans - 1;
+
+ if (isNaN(v) || v < min_vid || v > m)
+ return _('Invalid VLAN ID given! Only IDs between %d and %d are allowed.').format(min_vid, m);
+
+ var sections = this.section.cfgsections();
+
+ for (var i = 0; i < sections.length; i++) {
+ if (sections[i] == section_id)
+ continue;
+
+ if (this.formvalue(sections[i]) == v)
+ return _('Invalid VLAN ID given! Only unique IDs are allowed');
+ }
+
+ return true;
+ };
+
+ o.write = function(section_id, value) {
+ var topology = this.section.topology,
+ values = [];
+
+ for (var i = 0; i < port_opts.length; i++) {
+ var tagging = port_opts[i].formvalue(section_id),
+ portspec = Array.isArray(topology.ports) ? topology.ports[i] : null;
+
+ if (tagging == 't')
+ values.push(port_opts[i].option + tagging);
+ else if (tagging == 'u')
+ values.push(port_opts[i].option);
+
+ if (portspec && portspec.device) {
+ var old_tag = port_opts[i].cfgvalue(section_id),
+ old_vid = this.cfgvalue(section_id);
+
+ if (old_tag != tagging || old_vid != value) {
+ var old_ifname = portspec.device + (old_tag != 'u' ? '.' + old_vid : ''),
+ new_ifname = portspec.device + (tagging != 'u' ? '.' + value : '');
+
+ if (old_ifname != new_ifname)
+ update_interfaces(old_ifname, new_ifname);
+ }
+ }
+ }
+
+ if (feat.vlan4k_option)
+ uci.set('network', sid, feat.vlan4k_option, '1');
+
+ uci.set('network', section_id, 'ports', values.join(' '));
+
+ return form.Value.prototype.write.apply(this, [section_id, value]);
+ };
+
+ o.cfgvalue = function(section_id) {
+ var value = feat.vid_option ? uci.get('network', section_id, feat.vid_option) : null;
+ return (value || uci.get('network', section_id, 'vlan'));
+ };
+
+ for (var j = 0; Array.isArray(topology.ports) && j < topology.ports.length; j++) {
+ var portspec = topology.ports[j],
+ portstate = Array.isArray(topology.portstate) ? topology.portstate[portspec.num] : null;
+
+ o = s.option(form.ListValue, String(portspec.num), portspec.label);
+ o.value('', _('off'));
+
+ if (!portspec.need_tag)
+ o.value('u', _('untagged'));
+
+ o.value('t', _('tagged'));
+
+ o.cfgvalue = parse_portvalue;
+ o.validate = validate_portvalue;
+ o.write = function() {};
+
+ o.description = render_port_status(E('small', {
+ 'data-switch': switch_name,
+ 'data-port': portspec.num
+ }), portstate);
+
+ port_opts.push(o);
+ }
+
+ port_opts.sort(function(a, b) {
+ return a.option < b.option;
+ });
+ }
+
+ L.Poll.add(L.bind(update_port_status, m, topologies));
+
+ return m.render();
+ }
+});
diff --git a/modules/luci-mod-network/luasrc/controller/admin/network.lua b/modules/luci-mod-network/luasrc/controller/admin/network.lua
index a381bbc61..f8623be93 100644
--- a/modules/luci-mod-network/luasrc/controller/admin/network.lua
+++ b/modules/luci-mod-network/luasrc/controller/admin/network.lua
@@ -18,13 +18,7 @@ function index()
end)
if has_switch then
- page = node("admin", "network", "vlan")
- page.target = cbi("admin_network/vlan")
- page.title = _("Switch")
- page.order = 20
-
- page = entry({"admin", "network", "switch_status"}, call("switch_status"), nil)
- page.leaf = true
+ entry({"admin", "network", "switch"}, view("network/switch"), _("Switch"), 20)
end
@@ -271,13 +265,6 @@ function wifi_reconnect(radio)
end
end
-function switch_status(switches)
- local s = require "luci.tools.status"
-
- luci.http.prepare_content("application/json")
- luci.http.write_json(s.switch_status(switches))
-end
-
function diag_command(cmd, addr)
if addr and addr:match("^[a-zA-Z0-9%-%.:_]+$") then
luci.http.prepare_content("text/plain")
diff --git a/modules/luci-mod-network/luasrc/model/cbi/admin_network/vlan.lua b/modules/luci-mod-network/luasrc/model/cbi/admin_network/vlan.lua
deleted file mode 100644
index edeb193ef..000000000
--- a/modules/luci-mod-network/luasrc/model/cbi/admin_network/vlan.lua
+++ /dev/null
@@ -1,389 +0,0 @@
--- Copyright 2008 Steven Barth <steven@midlink.org>
--- Copyright 2010-2011 Jo-Philipp Wich <jow@openwrt.org>
--- Licensed to the public under the Apache License 2.0.
-
-m = Map("network", translate("Switch"), translate("The network ports on this device can be combined to several <abbr title=\"Virtual Local Area Network\">VLAN</abbr>s in which computers can communicate directly with each other. <abbr title=\"Virtual Local Area Network\">VLAN</abbr>s are often used to separate different network segments. Often there is by default one Uplink port for a connection to the next greater network like the internet and other ports for a local network."))
-
-local fs = require "nixio.fs"
-local ut = require "luci.util"
-local nw = require "luci.model.network"
-local switches = { }
-
-nw.init(m.uci)
-
-local topologies = nw:get_switch_topologies() or {}
-
-local update_interfaces = function(old_ifname, new_ifname)
- local info = { }
-
- m.uci:foreach("network", "interface", function(section)
- local old_ifnames = section.ifname
- local new_ifnames = { }
- local cur_ifname
- local changed = false
- for cur_ifname in luci.util.imatch(old_ifnames) do
- if cur_ifname == old_ifname then
- new_ifnames[#new_ifnames+1] = new_ifname
- changed = true
- else
- new_ifnames[#new_ifnames+1] = cur_ifname
- end
- end
- if changed then
- m.uci:set("network", section[".name"], "ifname", table.concat(new_ifnames, " "))
-
- info[#info+1] = translatef("Interface %q device auto-migrated from %q to %q.",
- section[".name"], old_ifname, new_ifname)
- end
- end)
-
- if #info > 0 then
- m.message = (m.message and m.message .. "\n" or "") .. table.concat(info, "\n")
- end
-end
-
-local vlan_already_created
-
-m.uci:foreach("network", "switch",
- function(x)
- local sid = x['.name']
- local switch_name = x.name or sid
- local has_vlan = nil
- local has_learn = nil
- local has_vlan4k = nil
- local has_jumbo3 = nil
- local has_mirror = nil
- local min_vid = 0
- local max_vid = 16
- local num_vlans = 16
-
- local switch_title
- local enable_vlan4k = false
-
- local topo = topologies[switch_name]
-
- if not topo then
- m.message = translatef("Switch %q has an unknown topology - the VLAN settings might not be accurate.", switch_name)
- topo = {
- ports = {
- { num = 0, label = "Port 1" },
- { num = 1, label = "Port 2" },
- { num = 2, label = "Port 3" },
- { num = 3, label = "Port 4" },
- { num = 4, label = "Port 5" },
- { num = 5, label = "CPU (eth0)", tagged = false }
- }
- }
- end
-
- -- Parse some common switch properties from swconfig help output.
- local swc = io.popen("swconfig dev %s help 2>/dev/null" % ut.shellquote(switch_name))
- if swc then
-
- local is_port_attr = false
- local is_vlan_attr = false
-
- while true do
- local line = swc:read("*l")
- if not line then break end
-
- if line:match("^%s+%-%-vlan") then
- is_vlan_attr = true
-
- elseif line:match("^%s+%-%-port") then
- is_vlan_attr = false
- is_port_attr = true
-
- elseif line:match("cpu @") then
- switch_title = line:match("^switch%d: %w+%((.-)%)")
- num_vlans = tonumber(line:match("vlans: (%d+)")) or 16
- min_vid = 1
-
- elseif line:match(": pvid") or line:match(": tag") or line:match(": vid") then
- if is_vlan_attr then has_vlan4k = line:match(": (%w+)") end
-
- elseif line:match(": enable_vlan4k") then
- enable_vlan4k = true
-
- elseif line:match(": enable_vlan") then
- has_vlan = "enable_vlan"
-
- elseif line:match(": enable_learning") then
- has_learn = "enable_learning"
-
- elseif line:match(": enable_mirror_rx") then
- has_mirror = "enable_mirror_rx"
-
- elseif line:match(": max_length") then
- has_jumbo3 = "max_length"
- end
- end
-
- swc:close()
- end
-
-
- -- Switch properties
- s = m:section(NamedSection, x['.name'], "switch",
- switch_title and translatef("Switch %q (%s)", switch_name, switch_title)
- or translatef("Switch %q", switch_name))
-
- s.addremove = false
-
- if has_vlan then
- s:option(Flag, has_vlan, translate("Enable VLAN functionality"))
- end
-
- if has_learn then
- x = s:option(Flag, has_learn, translate("Enable learning and aging"))
- x.default = x.enabled
- end
-
- if has_jumbo3 then
- x = s:option(Flag, has_jumbo3, translate("Enable Jumbo Frame passthrough"))
- x.enabled = "3"
- x.rmempty = true
- end
-
- -- Does this switch support port mirroring?
- if has_mirror then
- s:option(Flag, "enable_mirror_rx", translate("Enable mirroring of incoming packets"))
- s:option(Flag, "enable_mirror_tx", translate("Enable mirroring of outgoing packets"))
-
- local sp = s:option(ListValue, "mirror_source_port", translate("Mirror source port"))
- local mp = s:option(ListValue, "mirror_monitor_port", translate("Mirror monitor port"))
-
- sp:depends("enable_mirror_tx", "1")
- sp:depends("enable_mirror_rx", "1")
-
- mp:depends("enable_mirror_tx", "1")
- mp:depends("enable_mirror_rx", "1")
-
- local _, pt
- for _, pt in ipairs(topo.ports) do
- sp:value(pt.num, pt.label)
- mp:value(pt.num, pt.label)
- end
- end
-
- -- VLAN table
- s = m:section(TypedSection, "switch_vlan",
- switch_title and translatef("VLANs on %q (%s)", switch_name, switch_title)
- or translatef("VLANs on %q", switch_name))
-
- s.template = "cbi/tblsection"
- s.addremove = true
- s.anonymous = true
-
- -- Filter by switch
- s.filter = function(self, section)
- local device = m:get(section, "device")
- return (device and device == switch_name)
- end
-
- -- Override cfgsections callback to enforce row ordering by vlan id.
- s.cfgsections = function(self)
- local osections = TypedSection.cfgsections(self)
- local sections = { }
- local section
-
- for _, section in luci.util.spairs(
- osections,
- function(a, b)
- return (tonumber(m:get(osections[a], has_vlan4k or "vlan")) or 9999)
- < (tonumber(m:get(osections[b], has_vlan4k or "vlan")) or 9999)
- end
- ) do
- sections[#sections+1] = section
- end
-
- return sections
- end
-
- -- When creating a new vlan, preset it with the highest found vid + 1.
- s.create = function(self, section, origin)
- -- VLAN has already been created for another switch
- if vlan_already_created then
- return
-
- -- VLAN add button was pressed in an empty VLAN section so only
- -- accept the create event if our switch is without existing VLANs
- elseif origin == "" then
- local is_empty_switch = true
-
- m.uci:foreach("network", "switch_vlan",
- function(s)
- if s.device == switch_name then
- is_empty_switch = false
- return false
- end
- end)
-
- if not is_empty_switch then
- return
- end
-
- -- VLAN was created for another switch
- elseif m:get(origin, "device") ~= switch_name then
- return
- end
-
- local sid = TypedSection.create(self, section)
-
- local max_nr = 0
- local max_id = 0
-
- m.uci:foreach("network", "switch_vlan",
- function(s)
- if s.device == switch_name then
- local nr = tonumber(s.vlan)
- local id = has_vlan4k and tonumber(s[has_vlan4k])
- if nr ~= nil and nr > max_nr then max_nr = nr end
- if id ~= nil and id > max_id then max_id = id end
- end
- end)
-
- m:set(sid, "device", switch_name)
- m:set(sid, "vlan", max_nr + 1)
-
- if has_vlan4k then
- m:set(sid, has_vlan4k, max_id + 1)
- end
-
- vlan_already_created = true
-
- return sid
- end
-
-
- local port_opts = { }
- local untagged = { }
-
- -- Parse current tagging state from the "ports" option.
- local portvalue = function(self, section)
- local pt
- for pt in (m:get(section, "ports") or ""):gmatch("%w+") do
- local pc, tu = pt:match("^(%d+)([tu]*)")
- if pc == self.option then return (#tu > 0) and tu or "u" end
- end
- return ""
- end
-
- -- Validate port tagging. Ensure that a port is only untagged once,
- -- bail out if not.
- local portvalidate = function(self, value, section)
- -- ensure that the ports appears untagged only once
- if value == "u" then
- if not untagged[self.option] then
- untagged[self.option] = true
- else
- return nil,
- translatef("%s is untagged in multiple VLANs!", self.title)
- end
- end
- return value
- end
-
-
- local vid = s:option(Value, has_vlan4k or "vlan", "VLAN ID")
- local mx_vid = has_vlan4k and 4094 or (num_vlans - 1)
-
- vid.rmempty = false
- vid.forcewrite = true
- vid.vlan_used = { }
- vid.datatype = "and(uinteger,range("..min_vid..","..mx_vid.."))"
-
- -- Validate user provided VLAN ID, make sure its within the bounds
- -- allowed by the switch.
- vid.validate = function(self, value, section)
- local v = tonumber(value)
- local m = has_vlan4k and 4094 or (num_vlans - 1)
- if v ~= nil and v >= min_vid and v <= m then
- if not self.vlan_used[v] then
- self.vlan_used[v] = true
- return value
- else
- return nil,
- translatef("Invalid VLAN ID given! Only unique IDs are allowed")
- end
- else
- return nil,
- translatef("Invalid VLAN ID given! Only IDs between %d and %d are allowed.", min_vid, m)
- end
- end
-
- -- When writing the "vid" or "vlan" option, serialize the port states
- -- as well and write them as "ports" option to uci.
- vid.write = function(self, section, new_vid)
- local o
- local p = { }
- for _, o in ipairs(port_opts) do
- local new_tag = o:formvalue(section)
- if new_tag == "t" then
- p[#p+1] = o.option .. new_tag
- elseif new_tag == "u" then
- p[#p+1] = o.option
- end
-
- if o.info and o.info.device then
- local old_tag = o:cfgvalue(section)
- local old_vid = self:cfgvalue(section)
- if old_tag ~= new_tag or old_vid ~= new_vid then
- local old_ifname = (old_tag == "u") and o.info.device
- or "%s.%s" %{ o.info.device, old_vid }
-
- local new_ifname = (new_tag == "u") and o.info.device
- or "%s.%s" %{ o.info.device, new_vid }
-
- if old_ifname ~= new_ifname then
- update_interfaces(old_ifname, new_ifname)
- end
- end
- end
- end
-
- if enable_vlan4k then
- m:set(sid, "enable_vlan4k", "1")
- end
-
- m:set(section, "ports", table.concat(p, " "))
- return Value.write(self, section, new_vid)
- end
-
- -- Fallback to "vlan" option if "vid" option is supported but unset.
- vid.cfgvalue = function(self, section)
- return m:get(section, has_vlan4k or "vlan")
- or m:get(section, "vlan")
- end
-
- local _, pt
- for _, pt in ipairs(topo.ports) do
- local po = s:option(ListValue, tostring(pt.num), pt.label)
-
- po:value("", translate("off"))
-
- if not pt.tagged then
- po:value("u", translate("untagged"))
- end
-
- po:value("t", translate("tagged"))
-
- po.cfgvalue = portvalue
- po.validate = portvalidate
- po.write = function() end
- po.info = pt
-
- port_opts[#port_opts+1] = po
- end
-
- table.sort(port_opts, function(a, b) return a.option < b.option end)
- switches[#switches+1] = switch_name
- end
-)
-
--- Switch status template
-s = m:section(SimpleSection)
-s.template = "admin_network/switch_status"
-s.switches = switches
-
-return m
diff --git a/modules/luci-mod-network/luasrc/view/admin_network/switch_status.htm b/modules/luci-mod-network/luasrc/view/admin_network/switch_status.htm
deleted file mode 100644
index 6e741b419..000000000
--- a/modules/luci-mod-network/luasrc/view/admin_network/switch_status.htm
+++ /dev/null
@@ -1,62 +0,0 @@
-<script type="text/javascript">//<![CDATA[
- var switches = [ '<%=table.concat(self.switches, "', '")%>' ],
- tables = document.querySelectorAll('.cbi-section-table');
-
- function add_status_row(table) {
- var first_row = table.querySelector('.cbi-section-table-row');
- if (first_row.classList.contains('port-status'))
- return first_row;
-
- var status_row = first_row.parentNode.insertBefore(
- E('div', { 'class': first_row.className }), first_row);
-
- first_row.querySelectorAll('.td').forEach(function(td) {
- status_row.appendChild(td.cloneNode(false));
- status_row.lastElementChild.removeAttribute('data-title');
- });
-
- status_row.firstElementChild.innerHTML = '<%:Port status:%>';
- status_row.classList.add('port-status') ;
-
- return status_row;
- }
-
- XHR.poll(-1, '<%=url('admin/network/switch_status')%>/' + switches.join(','), null,
- function(x, st)
- {
- for (var i = 0; i < switches.length; i++)
- {
- var ports = st[switches[i]];
- var tr = add_status_row(tables[i]);
-
- if (tr && ports && ports.length)
- {
- for (var j = 0; j < ports.length; j++)
- {
- var th = tr.querySelector('[data-name="%d"]'.format(j));
-
- if (!th)
- continue;
-
- if (ports[j].link)
- {
- th.innerHTML = String.format(
- '<small><img src="<%=resource%>/icons/port_up.png" />' +
- '<br />%d<%:baseT%><br />%s</small>',
- ports[j].speed, ports[j].duplex
- ? '<%:full-duplex%>' : '<%:half-duplex%>'
- );
- }
- else
- {
- th.innerHTML = String.format(
- '<small><img src="<%=resource%>/icons/port_down.png" />' +
- '<br /><%:no link%></small>'
- );
- }
- }
- }
- }
- }
- );
-//]]></script>