luci-mod-network: reimplement switch configuration as client side view
authorJo-Philipp Wich <jo@mein.io>
Thu, 12 Sep 2019 09:06:38 +0000 (11:06 +0200)
committerJo-Philipp Wich <jo@mein.io>
Thu, 12 Sep 2019 09:09:57 +0000 (11:09 +0200)
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
modules/luci-mod-network/htdocs/luci-static/resources/view/network/switch.js [new file with mode: 0644]
modules/luci-mod-network/luasrc/controller/admin/network.lua
modules/luci-mod-network/luasrc/model/cbi/admin_network/vlan.lua [deleted file]
modules/luci-mod-network/luasrc/view/admin_network/switch_status.htm [deleted file]

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 (file)
index 0000000..b281bb1
--- /dev/null
@@ -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();
+       }
+});
index a381bbc614213c057560bbd6d876a78bd27dcc3a..f8623be93ef03ccc93f6d227648bb8ac79255548 100644 (file)
@@ -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 (file)
index edeb193..0000000
+++ /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 (file)
index 6e741b4..0000000
+++ /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>