X-Git-Url: https://git.archive.openwrt.org/?p=project%2Fluci.git;a=blobdiff_plain;f=modules%2Fadmin-full%2Fluasrc%2Fmodel%2Fcbi%2Fadmin_network%2Fvlan.lua;h=8fcf9af9827d0c64ce29809840214053d1b1a848;hp=7e9db5ea1b6bb6a3a6c75bfb13da0333ab1e054b;hb=c5557fc90b082c88cb2322fffc6c672330a21494;hpb=2cb4a823a2ba7b889fb64227033b47cbe5abbb11 diff --git a/modules/admin-full/luasrc/model/cbi/admin_network/vlan.lua b/modules/admin-full/luasrc/model/cbi/admin_network/vlan.lua index 7e9db5ea1..8fcf9af98 100644 --- a/modules/admin-full/luasrc/model/cbi/admin_network/vlan.lua +++ b/modules/admin-full/luasrc/model/cbi/admin_network/vlan.lua @@ -2,7 +2,7 @@ LuCI - Lua Configuration Interface Copyright 2008 Steven Barth -Copyright 2010 Jo-Philipp Wich +Copyright 2010-2011 Jo-Philipp Wich Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -10,21 +10,28 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -$Id$ ]]-- -m = Map("network", translate("Switch"), translate("The network ports on your router can be combined to several VLANs in which computers can communicate directly with each other. VLANs 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.")) +m = Map("network", translate("Switch"), translate("The network ports on this device can be combined to several VLANs in which computers can communicate directly with each other. VLANs 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 switches = { } m.uci:foreach("network", "switch", function(x) - local switch_name = x.name or x['.name'] + 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_ptpvid = nil + local has_jumbo3 = nil + local min_vid = 0 local max_vid = 16 local num_vlans = 16 - local num_ports = 5 - local cpu_port = 5 + local num_ports = 6 + local cpu_port = tonumber(fs.readfile("/proc/switch/eth0/cpuport") or 5) + local switch_title local enable_vlan4k = false -- Parse some common switch properties from swconfig help output. @@ -45,21 +52,30 @@ m.uci:foreach("network", "switch", is_vlan_attr = false is_port_attr = true - elseif line:match("^Switch %d+:") then + elseif line:match("cpu @") then + switch_title = line:match("^switch%d: %w+%((.-)%)") num_ports, cpu_port, num_vlans = line:match("ports: (%d+) %(cpu @ (%d+)%), vlans: (%d+)") - num_ports = tonumber(num_ports or 5) - num_vlans = tonumber(num_vlans or 16) - cpu_port = tonumber(cpu_port or 5) + num_ports = tonumber(num_ports) or 6 + num_vlans = tonumber(num_vlans) or 16 + cpu_port = tonumber(cpu_port) or 5 + 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 - if is_port_attr then has_ptpvid = 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(": max_length") then + has_jumbo3 = "max_length" end end @@ -67,52 +83,44 @@ m.uci:foreach("network", "switch", end - -- The PVID options (if any) are added to this table so that - -- section create below can add the just created vlan to the - -- choice list of the PVID options... - local pvid_opts = { } + -- 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)) - -- This function re-reads all existing vlan ids and populates - -- PVID options choice lists - local function populate_pvids() - local vlan_ids = { } - m.uci:foreach("network", "switch_vlan", - function(s) - local vid = s[has_vlan4k or "vlan"] or s["vlan"] - if vid ~= nil then - vlan_ids[#vlan_ids+1] = vid - end - end) + s.addremove = false - local opt, vid - for _, opt in ipairs(pvid_opts) do - opt:reset_values() - opt:value("", translate("none")) - for _, vid in luci.util.vspairs(vlan_ids) do - opt:value(vid, translatef("VLAN %d", tonumber(vid))) - end - end + if has_vlan then + s:option(Flag, has_vlan, translate("Enable VLAN functionality")) end - -- Switch properties - s = m:section(NamedSection, x['.name'], "switch", translatef("Switch %q", switch_name)) - s.addremove = false - - s:option(Flag, "enable", translate("Enable this switch")).default = "1" - s:option(Flag, "enable_vlan", translate("Enable VLAN functionality")).default = "1" + if has_learn then + x = s:option(Flag, has_learn, translate("Enable learning and aging")) + x.default = x.enabled + end - if enable_vlan4k then - s:option(Flag, "enable_vlan4k", translate("Enable 4K VLANs")) + if has_jumbo3 then + x = s:option(Flag, has_jumbo3, translate("Enable Jumbo Frame passthrough")) + x.enabled = "3" + x.rmempty = true end - s:option(Flag, "reset", translate("Reset switch during setup")).default = "1" -- VLAN table - s = m:section(TypedSection, "switch_vlan", translatef("VLANs on %q", switch_name)) + 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) @@ -122,8 +130,8 @@ m.uci:foreach("network", "switch", for _, section in luci.util.spairs( osections, function(a, b) - return (tonumber(m.uci:get("network", osections[a], has_vlan4k or "vlan")) or 9999) - < (tonumber(m.uci:get("network", osections[b], has_vlan4k or "vlan")) or 9999) + 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 @@ -133,8 +141,12 @@ m.uci:foreach("network", "switch", end -- When creating a new vlan, preset it with the highest found vid + 1. - -- Repopulate the PVID choice lists afterwards. - s.create = function(self, section) + s.create = function(self, section, origin) + -- Filter by switch + if m:get(origin, "device") ~= switch_name then + return + end + local sid = TypedSection.create(self, section) local max_nr = 0 @@ -142,34 +154,24 @@ m.uci:foreach("network", "switch", m.uci:foreach("network", "switch_vlan", function(s) - 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 + 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.uci:set("network", sid, "vlan", max_nr + 1) + m:set(sid, "device", switch_name) + m:set(sid, "vlan", max_nr + 1) if has_vlan4k then - m.uci:set("network", sid, has_vlan4k, max_id + 1) + m:set(sid, has_vlan4k, max_id + 1) end - -- add newly created vlan to the pvid choice list - populate_pvids() - return sid end - -- Repopulate PVId choice lists if a vlan gets removed. - s.remove = function(self, section) - local rv = TypedSection.remove(self, section) - - -- repopulate pvid choices - populate_pvids() - - return rv - end - local port_opts = { } local untagged = { } @@ -177,7 +179,7 @@ m.uci:foreach("network", "switch", -- Parse current tagging state from the "ports" option. local portvalue = function(self, section) local pt - for pt in (m.uci:get("network", section, "ports") or ""):gmatch("%w+") do + 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 @@ -191,7 +193,7 @@ m.uci:foreach("network", "switch", if value == "u" then if not untagged[self.option] then untagged[self.option] = true - else + elseif min_vid > 0 or tonumber(self.option) ~= cpu_port then -- enable multiple untagged cpu ports due to weird broadcom default setup return nil, translatef("Port %d is untagged in multiple VLANs!", tonumber(self.option) + 1) end @@ -200,21 +202,30 @@ m.uci:foreach("network", "switch", end - local vid = s:option(Value, has_vlan4k or "vlan", "VLAN ID") + local vid = s:option(Value, has_vlan4k or "vlan", "VLAN ID", "
" % switch_name) + 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 > 0 and v <= m then - return value + 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.", 1, m) + translatef("Invalid VLAN ID given! Only IDs between %d and %d are allowed.", min_vid, m) end end @@ -233,25 +244,35 @@ m.uci:foreach("network", "switch", end end - m.uci:set("network", section, "ports", table.concat(p, " ")) + if enable_vlan4k then + m:set(sid, "enable_vlan4k", "1") + end + + m:set(section, "ports", table.concat(p, " ")) return Value.write(self, section, value) end -- Fallback to "vlan" option if "vid" option is supported but unset. vid.cfgvalue = function(self, section) - return m.uci:get("network", section, has_vlan4k or "vlan") - or m.uci:get("network", section, "vlan") + return m:get(section, has_vlan4k or "vlan") + or m:get(section, "vlan") end -- Build per-port off/untagged/tagged choice lists. local pt for pt = 0, num_ports - 1 do - local po = s:option(ListValue, tostring(pt), - (pt == cpu_port) and translate("CPU") or translatef("Port %d", (pt + 1))) + local title + if pt == cpu_port then + title = translate("CPU") + else + title = translatef("Port %d", pt) + end - po:value("", translate("off")) - po:value("u" % pt, translate("untagged")) - po:value("t" % pt, translate("tagged")) + local po = s:option(ListValue, tostring(pt), title) + + po:value("", translate("off")) + po:value("u", translate("untagged")) + po:value("t", translate("tagged")) po.cfgvalue = portvalue po.validate = portvalidate @@ -260,111 +281,13 @@ m.uci:foreach("network", "switch", port_opts[#port_opts+1] = po end - - -- Does this switch support PVIDs? - if has_ptpvid then - - -- Spawn a "virtual" section. We just attach it to the global - -- switch section here, the overrides below take care of writing - -- the actual values to the correct uci sections. - s = m:section(TypedSection, "switch", - translatef("Port PVIDs on %q", switch_name), - translate("Port PVIDs specify " .. - "the default VLAN ID added to received untagged frames.")) - - s.template = "cbi/tblsection" - s.addremove = false - s.anonymous = true - - -- Build port list, store pointers to the option objects in the - -- pvid_opts array so that other callbacks can repopulate their - -- choice lists. - local pt - for pt = 0, num_ports - 1 do - local po = s:option(ListValue, tostring(pt), - (pt == cpu_port) and translate("CPU") or translatef("Port %d", (pt + 1))) - - -- When cbi queries the current config value for this post, - -- lookup the associated switch_port section (if any) and - -- return its "pvid" or "vlan" option value. - po.cfgvalue = function(self, section) - local val - m.uci:foreach("network", "switch_port", - function(s) - if s.port == self.option then - val = s[has_ptpvid] - return false - end - end) - return val - end - - -- On write, find the actual switch_port section associated - -- to this port and set the value there. Create a new - -- switch_port section for this port if there is none yet. - po.write = function(self, section, value) - local found = false - - m.uci:foreach("network", "switch_port", - function(s) - if s.port == self.option then - m.uci:set("network", s['.name'], has_ptpvid, value) - found = true - return false - end - end) - - if not found then - m.uci:section("network", "switch_port", nil, { - ["port"] = self.option, - [has_ptpvid] = value - }) - end - end - - -- If the user cleared the PVID value on this port, find - -- the associated switch_port section and clear it. - -- If the section does not contain any other unrelated - -- options (like led or blinkrate) then remove it completely, - -- else just clear out the "pvid" option. - po.remove = function(self, section) - m.uci:foreach("network", "switch_port", - function(s) - if s.port == self.option then - local k, found - local empty = true - - for k, _ in pairs(s) do - if k:sub(1,1) ~= "." and k ~= "port" and k ~= has_ptpvid then - empty = false - break - end - end - - if empty then - m.uci:delete("network", s['.name']) - else - m.uci:delete("network", s['.name'], has_ptpvid) - end - - return false - end - end) - end - - -- The referenced VLAN might just have been removed, simply - -- return "" (none) in this case to avoid triggering a - -- validation error. - po.validate = function(...) - return ListValue.validate(...) or "" - end - - pvid_opts[#pvid_opts+1] = po - end - - populate_pvids() - 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