luci-base: implement luci.model.uci.get_state()
[project/luci.git] / applications / luci-app-mwan3 / luasrc / model / cbi / mwan / interface.lua
1 -- Copyright 2014 Aedan Renner <chipdankly@gmail.com
2 -- Copyright 2018 Florian Eckert <fe@dev.tdt.de>
3 -- Licensed to the public under the GNU General Public License v2.
4
5 dsp = require "luci.dispatcher"
6
7
8 function interfaceWarnings(overview, count, iface_max)
9         local warnings = ""
10         if count <= iface_max then
11                 warnings = string.format("<strong>%s</strong><br />",
12                         translatef("There are currently %d of %d supported interfaces configured", count, iface_max)
13                         )
14         else
15                 warnings = string.format("<strong>%s</strong><br />",
16                         translatef("WARNING: %d interfaces are configured exceeding the maximum of %d!", count, iface_max)
17                         )
18         end
19
20         for i, k in pairs(overview) do
21                 if overview[i]["network"] == false then
22                         warnings = warnings .. string.format("<strong>%s</strong><br />",
23                                         translatef("WARNING: Interface %s are not found in /etc/config/network", i)
24                                         )
25                 end
26
27                 if overview[i]["default_route"] == false then
28                         warnings = warnings .. string.format("<strong>%s</strong><br />",
29                                 translatef("WARNING: Interface %s has no default route in the main routing table", i)
30                                 )
31                 end
32
33                 if overview[i]["reliability"] == false then
34                         warnings = warnings .. string.format("<strong>%s</strong><br />",
35                                 translatef("WARNING: Interface %s has a higher reliability " ..
36                                 "requirement than tracking hosts (%d)", i, overview[i]["tracking"])
37                                 )
38                 end
39
40                 if overview[i]["duplicate_metric"] == true then
41                         warnings = warnings .. string.format("<strong>%s</strong><br />",
42                                 translatef("WARNING: Interface %s has a duplicate metric %s configured", i, overview[i]["metric"])
43                                 )
44                 end
45         end
46
47         return warnings
48 end
49
50 function configCheck()
51         local overview = {}
52         local count = 0
53         local duplicate_metric = {}
54         uci.cursor():foreach("mwan3", "interface",
55                 function (section)
56                         local uci = uci.cursor(nil, "/var/state")
57                         local iface = section[".name"]
58                         overview[iface] = {}
59                         count = count + 1
60                         local network = uci:get("network", iface)
61                         overview[iface]["network"] = false
62                         if network ~= nil then
63                                 overview[iface]["network"] = true
64
65                                 local device = uci:get("network", iface, "ifname")
66                                 if device ~= nil then
67                                         overview[iface]["device"] = device
68                                 end
69
70                                 local metric = uci:get("network", iface, "metric")
71                                 if metric ~= nil then
72                                         overview[iface]["metric"] = metric
73                                         overview[iface]["duplicate_metric"] = false
74                                         for _, m in ipairs(duplicate_metric) do
75                                                 if m == metric then
76                                                         overview[iface]["duplicate_metric"] = true
77                                                 end
78                                         end
79                                         table.insert(duplicate_metric, metric)
80                                 end
81
82                                 local dump = require("luci.util").ubus("network.interface.%s" % iface, "status", {})
83                                 overview[iface]["default_route"] = false
84                                 if dump and dump.route then
85                                         local _, route
86                                         for _, route in ipairs(dump.route) do
87                                                 if dump.route[_].target == "0.0.0.0" then
88                                                         overview[iface]["default_route"] = true
89                                                 end
90                                         end
91                                 end
92                         end
93
94                         local trackingNumber = uci:get("mwan3", iface, "track_ip")
95                         overview[iface]["tracking"] = 0
96                         if trackingNumber and #trackingNumber > 0 then
97                                 overview[iface]["tracking"] = #trackingNumber
98                                 overview[iface]["reliability"] = false
99                                 local reliabilityNumber = tonumber(uci:get("mwan3", iface, "reliability"))
100                                 if reliabilityNumber and reliabilityNumber <= #trackingNumber then
101                                         overview[iface]["reliability"] = true
102                                 end
103                         end
104                 end
105         )
106
107         -- calculate iface_max usage from firewall mmx_mask
108         function bit(p)
109                 return 2 ^ (p - 1)
110         end
111         function hasbit(x, p)
112                 return x % (p + p) >= p
113         end
114         function setbit(x, p)
115                 return hasbit(x, p) and x or x + p
116         end
117
118         local uci = require("uci").cursor(nil, "/var/state")
119         local mmx_mask = uci:get("mwan3", "globals", "mmx_mask") or "0x3F00"
120         local number = tonumber(mmx_mask, 16)
121         local bits = 0
122         local iface_max = 0
123         for i=1,16 do
124                 if hasbit(number, bit(i)) then
125                         bits = bits + 1
126                         iface_max = setbit( iface_max, bit(bits))
127                 end
128         end
129
130         -- subtract blackhole, unreachable and default table from iface_max
131         iface_max = iface_max - 3
132
133         return overview, count, iface_max
134 end
135
136 m5 = Map("mwan3", translate("MWAN - Interfaces"),
137         interfaceWarnings(configCheck()))
138
139 mwan_interface = m5:section(TypedSection, "interface", nil,
140         translate("MWAN supports up to 250 physical and/or logical interfaces<br />" ..
141         "MWAN requires that all interfaces have a unique metric configured in /etc/config/network<br />" ..
142         "Names must match the interface name found in /etc/config/network (see advanced tab)<br />" ..
143         "Names may contain characters A-Z, a-z, 0-9, _ and no spaces<br />" ..
144         "Interfaces may not share the same name as configured members, policies or rules"))
145 mwan_interface.addremove = true
146 mwan_interface.dynamic = false
147 mwan_interface.sectionhead = translate("Interface")
148 mwan_interface.sortable = false
149 mwan_interface.template = "cbi/tblsection"
150 mwan_interface.extedit = dsp.build_url("admin", "network", "mwan", "interface", "%s")
151 function mwan_interface.create(self, section)
152         TypedSection.create(self, section)
153         m5.uci:save("mwan3")
154         luci.http.redirect(dsp.build_url("admin", "network", "mwan", "interface", section))
155 end
156
157 enabled = mwan_interface:option(DummyValue, "enabled", translate("Enabled"))
158 enabled.rawhtml = true
159 function enabled.cfgvalue(self, s)
160         if self.map:get(s, "enabled") == "1" then
161                 return translate("Yes")
162         else
163                 return translate("No")
164         end
165 end
166
167 track_method = mwan_interface:option(DummyValue, "track_method", translate("Tracking method"))
168 track_method.rawhtml = true
169 function track_method.cfgvalue(self, s)
170         local tracked = self.map:get(s, "track_ip")
171         if tracked then
172                 return self.map:get(s, "track_method") or "&#8212;"
173         else
174                 return "&#8212;"
175         end
176 end
177
178 reliability = mwan_interface:option(DummyValue, "reliability", translate("Tracking reliability"))
179 reliability.rawhtml = true
180 function reliability.cfgvalue(self, s)
181         local tracked = self.map:get(s, "track_ip")
182         if tracked then
183                 return self.map:get(s, "reliability") or "&#8212;"
184         else
185                 return "&#8212;"
186         end
187 end
188
189 interval = mwan_interface:option(DummyValue, "interval", translate("Ping interval"))
190 interval.rawhtml = true
191 function interval.cfgvalue(self, s)
192         local tracked = self.map:get(s, "track_ip")
193         if tracked then
194                 local intervalValue = self.map:get(s, "interval")
195                 if intervalValue then
196                         return intervalValue .. "s"
197                 else
198                         return "&#8212;"
199                 end
200         else
201                 return "&#8212;"
202         end
203 end
204
205 down = mwan_interface:option(DummyValue, "down", translate("Interface down"))
206 down.rawhtml = true
207 function down.cfgvalue(self, s)
208         local tracked = self.map:get(s, "track_ip")
209         if tracked then
210                 return self.map:get(s, "down") or "&#8212;"
211         else
212                 return "&#8212;"
213         end
214 end
215
216 up = mwan_interface:option(DummyValue, "up", translate("Interface up"))
217 up.rawhtml = true
218 function up.cfgvalue(self, s)
219         local tracked = self.map:get(s, "track_ip")
220         if tracked then
221                 return self.map:get(s, "up") or "&#8212;"
222         else
223                 return "&#8212;"
224         end
225 end
226
227 metric = mwan_interface:option(DummyValue, "metric", translate("Metric"))
228 metric.rawhtml = true
229 function metric.cfgvalue(self, s)
230         local uci = uci.cursor(nil, "/var/state")
231         local metric = uci:get("network", s, "metric")
232         if metric then
233                 return metric
234         else
235                 return "&#8212;"
236         end
237 end
238
239 return m5