Changeset 6286

Show
Ignore:
Timestamp:
10/12/10 06:28:49 (3 years ago)
Author:
jow
Message:

libs: merge libs/uci into libs/core

Location:
luci/trunk
Files:
2 modified
3 moved

Legend:

Unmodified
Added
Removed
  • luci/trunk/contrib/package/luci/Makefile

    r6284 r6286  
    159159 
    160160### Libraries ### 
    161 define Package/luci-uci 
    162   $(call Package/luci/libtemplate) 
    163   DEPENDS+=+libuci-lua 
    164   TITLE:=High-Level UCI API 
    165 endef 
    166  
    167 define Package/luci-uci/install 
    168     $(call Package/luci/install/template,$(1),libs/uci) 
    169 endef 
    170  
    171  
    172161define Package/luci-fastindex 
    173162  $(call Package/luci/libtemplate) 
     
    316305define Package/luci-web 
    317306  $(call Package/luci/libtemplate) 
    318   DEPENDS+=+luci-sys +luci-nixio +luci-uci \ 
     307  DEPENDS+=+luci-sys +luci-nixio +luci-core \ 
    319308    +luci-sgi-cgi +luci-lmo 
    320309  TITLE:=MVC Webframework 
     
    333322define Package/luci-uvl 
    334323  $(call Package/luci/libtemplate) 
    335   DEPENDS+=+luci-sys +luci-uci +luci-core 
     324  DEPENDS+=+luci-sys +luci-core 
    336325  TITLE:=UVL - UCI Validation Layer 
    337326endef 
     
    10761065    PKG_SELECTED_MODULES+=libs/px5g 
    10771066endif 
    1078 ifneq ($(CONFIG_PACKAGE_luci-uci),) 
    1079     PKG_SELECTED_MODULES+=libs/uci 
    1080 endif 
    10811067ifneq ($(CONFIG_PACKAGE_luci-sys),) 
    10821068    PKG_SELECTED_MODULES+=libs/sys 
     
    12931279$(eval $(call BuildPackage,luci-nixio)) 
    12941280$(eval $(call BuildPackage,luci-px5g)) 
    1295 $(eval $(call BuildPackage,luci-uci)) 
    12961281$(eval $(call BuildPackage,luci-sys)) 
    12971282$(eval $(call BuildPackage,luci-web)) 
  • luci/trunk/libs/core/luasrc/model/network.lua

    r5448 r6286  
    484484end 
    485485 
     486--[==[ 
     487#!/usr/bin/lua 
     488 
     489local uci = require "luci.model.uci".cursor_state() 
     490local utl = require "luci.util" 
     491local sys = require "luci.sys" 
     492local lip = require "luci.ip" 
     493local nxo = require "nixio" 
     494local nfs = require "nixio.fs" 
     495 
     496-- patch uci 
     497local x = getmetatable(uci) 
     498 
     499function x:list(...) 
     500    local val = self:get(...) 
     501    local lst = { } 
     502 
     503    if type(val) == "list" then 
     504        local _, v 
     505        for _, v in ipairs(val) do 
     506            local i 
     507            for i in v:gmatch("%S+") do 
     508                lst[#lst+1] = i 
     509            end 
     510        end 
     511    elseif type(val) == "string" then 
     512        local i 
     513        for i in val:gmatch("%S+") do 
     514            lst[#lst+1] = i 
     515        end 
     516    end 
     517 
     518    return lst 
     519end 
     520 
     521 
     522system = utl.class() 
     523 
     524system._switches = { } 
     525system._vlans    = { } 
     526 
     527function system:__init__() 
     528    self._networks = { } 
     529 
     530    uci:foreach("network2", "interface", 
     531        function(s) 
     532            self._networks[#self._networks+1] = system.network(s, self) 
     533        end) 
     534end 
     535 
     536function system:networks() 
     537    local index = 0 
     538    return function() 
     539        if index <= #self._networks then 
     540            index = index + 1 
     541            return self._networks[index] 
     542        else 
     543            return nil 
     544        end 
     545    end 
     546end 
     547 
     548function system:find_network(name) 
     549    local v 
     550    for _, v in ipairs(self._networks) do 
     551        if v:name() == name then 
     552            return v 
     553        end 
     554    end 
     555end 
     556 
     557function system:find_interface(name) 
     558    local v 
     559    for _, v in ipairs(self._networks) do 
     560        local i 
     561        for i in v:interfaces() do 
     562            if i:is_bridge() then 
     563                local p 
     564                for p in i:interfaces() do 
     565                    if p:name() == name then 
     566                        return p 
     567                    end 
     568                end 
     569            end 
     570 
     571            if i:name() == name then 
     572                return i 
     573            end 
     574        end 
     575    end 
     576end 
     577 
     578function system:delete_network(name) 
     579    local i 
     580    for i = 1, #self._networks do 
     581        if self._networks[i]:name() == name then 
     582            local x 
     583 
     584            for x in self._networks[i]:aliases() do 
     585                uci:delete("network2", x:name()) 
     586            end 
     587 
     588            for x in self._networks[i]:routes() do 
     589                uci:delete("network2", x:name()) 
     590            end 
     591 
     592            uci:delete("network2", self._networks[i]) 
     593            table.remove(self._networks, i) 
     594 
     595            return true 
     596        end 
     597    end 
     598    return false 
     599end 
     600 
     601function system:print() 
     602    local v 
     603    for v in self:networks() do 
     604        print(v:name()) 
     605        v:print() 
     606        print("--") 
     607    end 
     608end 
     609 
     610function system.ignore_iface(ifn) 
     611    return (nil ~= ( 
     612        ifn:match("^wlan%d") or  
     613        ifn:match("^ath%d")  or 
     614        ifn:match("^wl%d")   or 
     615        ifn:match("^imq%d")  or 
     616        ifn:match("^br%-")   or 
     617        ifn:match("^/dev/") 
     618    )) 
     619end 
     620 
     621function system.find_wifi_networks(net) 
     622    local lst = { } 
     623    local cnt = 0 
     624 
     625    uci:foreach("wireless", "wifi-iface", 
     626        function(s) 
     627            if s.device and s.network == net then 
     628                lst[#lst+1] = { s.device, s['.name'], cnt } 
     629            end 
     630            cnt = cnt + 1 
     631        end) 
     632 
     633    return lst 
     634end 
     635 
     636function system.find_iface_names(net) 
     637    local lst = { } 
     638 
     639    local val = uci:list("network2", net, "device") 
     640    if #val == 0 or val[1]:match("^/dev/") then 
     641        val = uci:list("network2", net, "ifname") 
     642    end 
     643 
     644    local ifn 
     645    for _, ifn in ipairs(val) do 
     646        if not system.ignore_iface(ifn) then 
     647            lst[#lst+1] = ifn 
     648        end 
     649    end 
     650     
     651    return lst 
     652end 
     653 
     654function system.find_switch(name) 
     655    local swname, swdev, swvlan 
     656 
     657    -- find switch 
     658    uci:foreach("network2", "switch", 
     659        function(s) 
     660            swname = s.name or s['.name'] 
     661 
     662            -- special: rtl8366s is eth0 (wan is eth1) 
     663            if swname == "rtl8366s" then 
     664                swdev = "eth0" 
     665 
     666            -- special: rtl8366rb is eth0 (wan + lan) 
     667            elseif swname == "rtl8366rb" then 
     668                swdev = "eth0" 
     669 
     670            -- treat swname as swdev 
     671            else 
     672                swdev = swname 
     673            end 
     674 
     675            return false 
     676        end) 
     677 
     678    -- find first vlan 
     679    if swdev then 
     680        uci:foreach("network2", "switch_vlan", 
     681            function(s) 
     682                if s.device == swname then 
     683                    local vlan = tonumber(s.vlan) 
     684                    if vlan and (not swvlan or vlan < swvlan) then 
     685                        swvlan = vlan 
     686                    end 
     687                end 
     688            end) 
     689    end 
     690 
     691 
     692    local veth, vlan = name:match("^(%S+)%.(%d+)$") 
     693 
     694    -- have vlan id and matching switch 
     695    if vlan and veth == swdev then 
     696        return swname, swdev, vlan 
     697 
     698    -- have no vlan id but matching switch, assume first switch vlan 
     699    elseif not vlan and name == swdev then 
     700        return swname, swdev, swvlan 
     701 
     702    -- have vlan and no matching switch, assume software vlan 
     703    elseif vlan then 
     704        return nil, veth, vlan 
     705    end 
     706end 
     707 
     708 
     709system.network = utl.class() 
     710 
     711function system.network:__init__(s, sys) 
     712    self._name    = s['.name'] 
     713    self._sys     = sys 
     714    self._routes  = { } 
     715    self._aliases = { } 
     716 
     717    if s.type == "bridge" then 
     718        self._interfaces = { system.network.bridge(s['.name'], self) } 
     719    else 
     720        self._interfaces = { } 
     721 
     722        local ifn 
     723 
     724        -- find wired ifaces 
     725        for _, ifn in ipairs(system.find_iface_names(self._name)) do 
     726            self._interfaces[#self._interfaces+1] = system.network.iface(ifn, self) 
     727        end 
     728 
     729        -- find wifi networks 
     730        for _, ifn in ipairs(system.find_wifi_networks(self._name)) do 
     731            self._interfaces[#self._interfaces+1] = system.network.iface(ifn, self) 
     732        end 
     733    end 
     734 
     735    -- find ipv4 routes 
     736    uci:foreach("network2", "route", 
     737        function(s) 
     738            if s.interface == self._name and s.target then 
     739                self._routes[#self._routes+1] = system.network.route(s, self) 
     740            end 
     741        end) 
     742 
     743    -- find ipv6 routes 
     744    uci:foreach("network2", "route6", 
     745        function(s) 
     746            if s.interface == self._name and s.target then 
     747                self._routes[#self._routes+1] = system.network.route(s, self) 
     748            end 
     749        end) 
     750 
     751    -- find aliases 
     752    uci:foreach("network2", "alias", 
     753        function(s) 
     754            if s.interface == self._name and s.proto then 
     755                self._aliases[#self._aliases+1] = system.network.alias(s, self) 
     756            end 
     757        end) 
     758end 
     759 
     760function system.network:name() 
     761    return self._name 
     762end 
     763 
     764function system.network:system() 
     765    return self._sys 
     766end 
     767 
     768function system.network:interfaces() 
     769    local index = 0 
     770    return function() 
     771        if index <= #self._interfaces then 
     772            index = index + 1 
     773            return self._interfaces[index] 
     774        else 
     775            return nil 
     776        end 
     777    end 
     778end 
     779 
     780function system.network:interface() 
     781    return self._interfaces[1] 
     782end 
     783 
     784function system.network:num_routes() 
     785    return #self._routes 
     786end 
     787 
     788function system.network:routes() 
     789    local index = 0 
     790    return function() 
     791        if index <= #self._routes then 
     792            index = index + 1 
     793            return self._routes[index] 
     794        else 
     795            return nil 
     796        end 
     797    end 
     798end 
     799 
     800function system.network:num_aliases() 
     801    return #self._aliases 
     802end 
     803 
     804function system.network:aliases() 
     805    local index = 0 
     806    return function() 
     807        if index <= #self._aliases then 
     808            index = index + 1 
     809            return self._aliases[index] 
     810        else 
     811            return nil 
     812        end 
     813    end 
     814end 
     815 
     816function system.network:delete_route(rt) 
     817    local i 
     818    for i = 1, #self._routes do 
     819        if self._routes[i]:name() == rt:name() then 
     820            uci:delete("network2", rt:name()) 
     821            table.remove(self._routes, i) 
     822            return true 
     823        end 
     824    end 
     825    return false 
     826end 
     827 
     828function system.network:delete_alias(al) 
     829    local i 
     830    for i = 1, #self._aliases do 
     831        if self._aliases[i]:name() == al:name() then 
     832            uci:delete("network2", al:name()) 
     833            table.remove(self._aliases, i) 
     834            return true 
     835        end 
     836    end 
     837    return false 
     838end 
     839 
     840function system.network:print() 
     841    self:interface():print() 
     842end 
     843 
     844 
     845system.network.iface = utl.class() 
     846 
     847function system.network.iface:__init__(ifn, net, parent) 
     848    self._net    = net 
     849    self._parent = parent 
     850 
     851    -- is a wifi iface 
     852    if type(ifn) == "table" then 
     853        local wifidev, network, index = unpack(ifn) 
     854 
     855        self._name    = "%s.%d" %{ wifidev, index } 
     856        self._wifidev = wifidev 
     857        self._wifinet = index 
     858        self._ifname  = uci:get("wireless", network, "ifname") or self._name 
     859 
     860    -- is a wired iface 
     861    else 
     862        self._name   = ifn 
     863        self._ifname = ifn 
     864 
     865        local switch, swdev, vlan = system.find_switch(self._ifname) 
     866 
     867        if switch then 
     868            self._switch = system.switch(switch, swdev, self) 
     869        end 
     870 
     871        if vlan then 
     872            self._vlan = system.vlan(vlan, self._switch, self) 
     873        end 
     874    end 
     875end 
     876 
     877function system.network.iface:name() 
     878    return self._name 
     879end 
     880 
     881function system.network.iface:parent() 
     882    return self._parent 
     883end 
     884 
     885function system.network.iface:network() 
     886    return self._net 
     887end 
     888 
     889function system.network.iface:is_managed() 
     890    return (self._net ~= nil) 
     891end 
     892 
     893function system.network.iface:is_vlan() 
     894    return (self._vlan ~= nil) 
     895end 
     896 
     897function system.network.iface:is_software_vlan() 
     898    return (not self._switch and self._vlan ~= nil) 
     899end 
     900 
     901function system.network.iface:is_hardware_vlan() 
     902    return (self._switch ~= nil and self._vlan ~= nil) 
     903end 
     904 
     905function system.network.iface:_sysfs(path, default) 
     906    path = "/sys/class/net/%s/%s" %{ self._ifname, path } 
     907 
     908    local data = nfs.readfile(path) 
     909 
     910    if type(default) == "number" then 
     911        return tonumber(data) or default 
     912    elseif data and #data > 0 then 
     913        return data and data:gsub("%s+$", "") or default 
     914    end 
     915 
     916    return default 
     917end 
     918 
     919function system.network.iface:rx_bytes() 
     920    return self:_sysfs("statistics/rx_bytes", 0) 
     921end 
     922 
     923function system.network.iface:tx_bytes() 
     924    return self:_sysfs("statistics/tx_bytes", 0) 
     925end 
     926 
     927function system.network.iface:rx_packets() 
     928    return self:_sysfs("statistics/rx_packets", 0) 
     929end 
     930 
     931function system.network.iface:tx_packets() 
     932    return self:_sysfs("statistics/tx_packets", 0) 
     933end 
     934 
     935function system.network.iface:macaddr() 
     936    return self:_sysfs("address") 
     937end 
     938 
     939function system.network.iface:mtu() 
     940    return self:_sysfs("mtu", 1500) 
     941end 
     942 
     943function system.network.iface:is_bridge() 
     944    return (self:_sysfs("bridge/max_age", 0) > 0) 
     945end 
     946 
     947function system.network.iface:is_bridge_port() 
     948    return (self:_sysfs("brport/port_no", 0) > 0) 
     949end 
     950 
     951function system.network.iface:delete() 
     952    if self._wifidev then 
     953        local cnt = 0 
     954        uci:foreach("wireless", "wifi-iface",  
     955            function(s) 
     956                cnt = cnt + 1 
     957                if s.device == self._wifidev and cnt == self._wifinet then 
     958                    uci:delete("wireless", s['.name']) 
     959                    return false 
     960                end 
     961            end) 
     962    end 
     963end 
     964 
     965function system.network.iface:print() 
     966    if self._wifidev then 
     967        print("  wifi: ", self._wifidev, "net: ", self._wifinet) 
     968    else 
     969        print("  iface: ", self._name) 
     970    end 
     971 
     972    print("    rx: ", self:rx_bytes(), self:rx_packets()) 
     973    print("    tx: ", self:tx_bytes(), self:tx_packets()) 
     974    print("    mtu: ", self:mtu()) 
     975    print("    mac: ", self:macaddr()) 
     976    print("    bridge? ", self:is_bridge()) 
     977    print("    port? ", self:is_bridge_port()) 
     978    print("    swvlan? ", self:is_software_vlan()) 
     979    print("    hwvlan? ", self:is_hardware_vlan()) 
     980 
     981    if self._switch then 
     982        self._switch:print() 
     983    end 
     984 
     985    if self._vlan then 
     986        self._vlan:print() 
     987    end 
     988end 
     989 
     990 
     991system.network.bridge = utl.class(system.network.iface) 
     992 
     993function system.network.bridge:__init__(brn, net) 
     994    self._net    = net 
     995    self._name   = "br-" .. brn 
     996    self._ifname = self._name 
     997    self._interfaces = { } 
     998 
     999    local ifn 
     1000 
     1001    -- find wired ifaces 
     1002    for _, ifn in ipairs(system.find_iface_names(brn)) do 
     1003        self._interfaces[#self._interfaces+1] = system.network.iface(ifn, net, self) 
     1004    end 
     1005 
     1006    -- find wifi networks 
     1007    for _, ifn in ipairs(system.find_wifi_networks(brn)) do 
     1008        self._interfaces[#self._interfaces+1] = system.network.iface(ifn, net, self) 
     1009    end 
     1010end 
     1011 
     1012function system.network.bridge:interfaces() 
     1013    local index = 0 
     1014    return function() 
     1015        if index <= #self._interfaces then 
     1016            index = index + 1 
     1017            return self._interfaces[index] 
     1018        else 
     1019            return nil 
     1020        end 
     1021    end 
     1022end 
     1023 
     1024function system.network.bridge:print() 
     1025    local v 
     1026    for v in self:interfaces() do 
     1027        io.write("  port: ") 
     1028        v:print() 
     1029    end 
     1030    print("  rx: ", self:rx_bytes(), self:rx_packets()) 
     1031    print("  tx: ", self:tx_bytes(), self:tx_packets()) 
     1032    print("  mtu: ", self:mtu()) 
     1033    print("  mac: ", self:macaddr()) 
     1034    print("  bridge? ", self:is_bridge()) 
     1035    print("  port? ", self:is_bridge_port()) 
     1036end 
     1037 
     1038 
     1039system.network.route = utl.class() 
     1040 
     1041function system.network.route:__init__(rt, net) 
     1042    self._net    = net 
     1043    self._name   = rt['.name'] 
     1044    self._ipv6   = (rt['.type'] == "route6") 
     1045    self._mtu    = tonumber(rt.mtu) or (net and net:interface():mtu() or 1500) 
     1046    self._metric = tonumber(rt.metric) or 0 
     1047 
     1048    if self._ipv6 then 
     1049        self._gateway = lip.IPv6(rt.gateway or "::") 
     1050        self._target  = lip.IPv6(rt.target  or "::") 
     1051    else 
     1052        self._gateway = lip.IPv4(rt.gateway or "0.0.0.0") 
     1053        self._target  = lip.IPv4(rt.target  or "0.0.0.0", rt.netmask or "0.0.0.0") 
     1054    end 
     1055end 
     1056 
     1057function system.network.route:name() 
     1058    return self._name 
     1059end 
     1060 
     1061function system.network.route:network() 
     1062    return self._net 
     1063end 
     1064 
     1065function system.network.route:mtu() 
     1066    return self._mtu 
     1067end 
     1068 
     1069function system.network.route:metric() 
     1070    return self._metric 
     1071end 
     1072 
     1073function system.network.route:is_ipv4() 
     1074    return not self._ipv6 
     1075end 
     1076 
     1077function system.network.route:is_ipv6() 
     1078    return self._ipv6 
     1079end 
     1080 
     1081function system.network.route:target() 
     1082    return self._target 
     1083end 
     1084 
     1085function system.network.route:gateway() 
     1086    return self._gateway 
     1087end 
     1088 
     1089 
     1090system.network.alias = utl.class() 
     1091 
     1092function system.network.alias:__init__(a, net) 
     1093    self._net  = net 
     1094    self._name = a['.name'] 
     1095end 
     1096 
     1097 
     1098system.switch = utl.class() 
     1099 
     1100function system.switch:__init__(switch, swdev, net) 
     1101    self._name   = switch 
     1102    self._ifname = swdev 
     1103    self._net    = net 
     1104 
     1105    if not system._switches[switch] then 
     1106        local x = io.popen("swconfig dev %q help 2>/dev/null" % switch) 
     1107        if x then 
     1108            local desc = x:read("*l") 
     1109 
     1110            if desc then 
     1111                local name, num_ports, num_cpu, num_vlans = 
     1112                    desc:match("Switch %d: %S+%((.-)%), ports: (%d+) %(cpu @ (%d+)%), vlans: (%d+)") 
     1113 
     1114                self._model   = name 
     1115                self._ports   = tonumber(num_ports) 
     1116                self._cpuport = tonumber(num_cpu) 
     1117                self._vlans   = tonumber(num_vlans) 
     1118            end 
     1119 
     1120            x:close() 
     1121 
     1122        elseif nfs.access("/proc/switch/%s" % switch) then 
     1123            self._model   = self:_proc("driver", switch) 
     1124            self._ports   = self:_proc_count("port", 6) 
     1125            self._vlans   = self:_proc_count("vlan", 16) 
     1126        end 
     1127 
     1128        -- defaults 
     1129        self._model   = self._model   or switch 
     1130        self._ports   = self._ports   or 6 
     1131        self._vlans   = self._vlans   or 16 
     1132        self._cpuport = self._cpuport or 5 
     1133 
     1134        system._switches[switch] = self 
     1135    else 
     1136        self._model   = system._switches[switch]._model 
     1137        self._ports   = system._switches[switch]._ports 
     1138        self._vlans   = system._switches[switch]._vlans 
     1139        self._cpuport = system._switches[switch]._cpuport 
     1140    end 
     1141end 
     1142 
     1143function system.switch:_proc(path, default) 
     1144    local data = nfs.readfile("/proc/switch/%s/%s" %{ self._name, path }) 
     1145    if data then 
     1146        return data:gsub("%s+$", "") 
     1147    end 
     1148    return default 
     1149end 
     1150 
     1151function system.switch:_proc_count(path, default) 
     1152    local cnt = 0 
     1153    for _ in nfs.dir("/proc/switch/%s/%s" %{ self._name, path }) do 
     1154        cnt = cnt + 1 
     1155    end 
     1156    return cnt > 0 and cnt or default 
     1157end 
     1158 
     1159function system.switch:name() 
     1160    return self._name 
     1161end 
     1162 
     1163function system.switch:model() 
     1164    return self._model 
     1165end 
     1166 
     1167function system.switch:num_possible_vlans() 
     1168    return self._vlans 
     1169end 
     1170 
     1171function system.switch:num_active_vlans() 
     1172    local cnt = 0 
     1173    uci:foreach("network2", "switch_vlan", 
     1174        function(s) 
     1175            if s.device == self._name then cnt = cnt + 1 end 
     1176        end) 
     1177    return cnt 
     1178end 
     1179 
     1180function system.switch:vlans() 
     1181    local index = 0 
     1182    local vlans = { } 
     1183 
     1184    uci:foreach("network2", "switch_vlan", 
     1185        function(s) 
     1186            if s.device == self._name and tonumber(s.vlan) then 
     1187                vlans[#vlans+1] = tonumber(s.vlan) 
     1188            end 
     1189        end) 
     1190 
     1191    return function() 
     1192        if index <= #vlans then 
     1193            index = index + 1 
     1194            return system.vlan(vlans[index], self) 
     1195        else 
     1196            return nil 
     1197        end 
     1198    end 
     1199end 
     1200 
     1201function system.switch:num_ports() 
     1202    return self._ports 
     1203end 
     1204 
     1205function system.switch:delete_vlan(vlan) 
     1206    local rv = false 
     1207 
     1208    uci:foreach("network2", "switch_vlan", 
     1209        function(s) 
     1210            if s.device == self._name and tonumber(s.vlan) == vlan then 
     1211                rv = true 
     1212                uci:delete("network2", s['.name']) 
     1213 
     1214                if system._vlans[s.device] and system._vlans[s.device][vlan] then 
     1215                    table.remove(system._vlans[s.device], vlan) 
     1216                end 
     1217 
     1218                return false 
     1219            end 
     1220        end) 
     1221 
     1222    return rv 
     1223end 
     1224 
     1225function system.switch:print() 
     1226    print("Switch:", self._model) 
     1227    print("  Ports:", self._ports, "Cpu:", self._cpuport) 
     1228    print("  Vlans:", self._vlans) 
     1229end 
     1230 
     1231 
     1232system.vlan = utl.class() 
     1233 
     1234function system.vlan:__init__(vlan, switch, iface) 
     1235    self._vlan   = vlan 
     1236    self._switch = switch 
     1237    self._iface  = iface 
     1238 
     1239    local swid = (switch and switch:name()) or (iface and iface:name()) or "" 
     1240 
     1241    if not system._vlans[swid] or not system._vlans[swid][vlan] then 
     1242        self._ports  = { } 
     1243 
     1244        if switch then 
     1245            uci:foreach("network2", "switch_vlan", 
     1246                function(s) 
     1247                    if s.device == switch:name() and tonumber(s.vlan) == vlan then 
     1248                        local p 
     1249                        for _, p in ipairs(uci:list("network2", s['.name'], "ports")) do 
     1250                            self._ports[#self._ports+1] = system.vlan.port(p, self) 
     1251                        end 
     1252                        self._name = s['.name'] 
     1253                    end 
     1254                end) 
     1255        else 
     1256            self._ports[#self._ports+1] = system.vlan.port("0t", self) 
     1257        end 
     1258 
     1259        system._vlans[swid] = system._vlans[swid] or { } 
     1260        system._vlans[swid][vlan] = self 
     1261    else 
     1262        self._ports = system._vlans[swid][vlan]._ports 
     1263    end 
     1264end 
     1265 
     1266function system.vlan:name() 
     1267    return self._name 
     1268end 
     1269 
     1270function system.vlan:number() 
     1271    return self._vlan 
     1272end 
     1273 
     1274function system.vlan:switch() 
     1275    return self._switch 
     1276end 
     1277 
     1278function system.vlan:interface() 
     1279    return self._iface 
     1280end 
     1281 
     1282function system.vlan:is_software() 
     1283    return (self._switch == nil) 
     1284end 
     1285 
     1286function system.vlan:is_hardware() 
     1287    return not self:is_software() 
     1288end 
     1289 
     1290function system.vlan:num_ports() 
     1291    return #self._ports 
     1292end 
     1293 
     1294function system.vlan:ports() 
     1295    local index = 0 
     1296    return function() 
     1297        if index <= #self._ports then 
     1298            index = index + 1 
     1299            return self._ports[index] 
     1300        else 
     1301            return nil 
     1302        end 
     1303    end 
     1304end 
     1305 
     1306function system.vlan:_update() 
     1307    local i 
     1308    local ports = { } 
     1309 
     1310    for i = 1, #self._ports do 
     1311        ports[#ports+1] = self._ports[i]:string() 
     1312    end 
     1313 
     1314    uci:set("network2", self._name, "ports", table.concat(ports, " ")) 
     1315end 
     1316 
     1317function system.vlan:delete_port(port) 
     1318    if self._switch then 
     1319        local i 
     1320        for i = 1, #self._ports do 
     1321            if self._ports[i]:number() == port then 
     1322                table.remove(self._ports, i) 
     1323                self:_update() 
     1324                return true 
     1325            end 
     1326        end 
     1327    end 
     1328    return false 
     1329end 
     1330 
     1331function system.vlan:print() 
     1332    print(" Vlan:", self._vlan, "Software?", self:is_software()) 
     1333    local p 
     1334    for p in self:ports() do 
     1335        p:print() 
     1336    end 
     1337end 
     1338 
     1339 
     1340system.vlan.port = utl.class() 
     1341 
     1342function system.vlan.port:__init__(port, vlan) 
     1343    local num, tag = port:match("^(%d+)([tu]?)") 
     1344 
     1345    self._vlan   = vlan 
     1346    self._port   = tonumber(num) 
     1347    self._tagged = (tag == "t") 
     1348end 
     1349 
     1350function system.vlan.port:number() 
     1351    return self._port 
     1352end 
     1353 
     1354function system.vlan.port:vlan() 
     1355    return self._vlan 
     1356end 
     1357 
     1358function system.vlan.port:string() 
     1359    return "%i%s" %{ self._port, self._tagged ? "t" : "" } 
     1360end 
     1361 
     1362function system.vlan.port:is_tagged() 
     1363    return self._tagged 
     1364end 
     1365 
     1366function system.vlan.port:print() 
     1367    print("  Port:", self._port, "Tagged:", self._tagged) 
     1368end 
     1369 
     1370 
     1371-- ------------------------------ 
     1372 
     1373local s = system() 
     1374 
     1375s:print() 
     1376 
     1377s:find_network("wan"):print() 
     1378s:find_interface("eth0"):parent():print() 
     1379 
     1380]==]