luci2: adapt views to changed luci2 framework
[project/luci2/ui.git] / luci2 / htdocs / luci2 / view / network.switch.js
1 L.ui.view.extend({
2 title: L.tr('Switch'),
3 description: L.tr('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.'),
4
5 switchPortState: L.cbi.ListValue.extend({
6 choices: [
7 [ 'n', L.trc('Switch port state', 'off') ],
8 [ 'u', L.trc('Switch port state', 'untagged') ],
9 [ 't', L.trc('Switch port state', 'tagged') ]
10 ],
11
12 init: function(name, options)
13 {
14 var self = this;
15
16 options.datatype = function(val, elem)
17 {
18 if (val == 'u')
19 {
20 var u = false;
21 var sections = self.section.sections();
22
23 for (var i = 0; i < sections.length; i++)
24 {
25 var v = self.formvalue(sections[i]['.name']);
26 if (v == 'u')
27 {
28 if (u)
29 return L.tr('Port must not be untagged in multiple VLANs');
30
31 u = true;
32 }
33 }
34 }
35
36 return true;
37 };
38
39 this.callSuper('init', name, options);
40 },
41
42 ucivalue: function(sid)
43 {
44 var ports = (this.map.get('network', sid, 'ports') || '').match(/[0-9]+[tu]?/g);
45
46 if (ports)
47 for (var i = 0; i < ports.length; i++)
48 if (ports[i].match(/^([0-9]+)([tu]?)$/))
49 if (RegExp.$1 == this.name)
50 return RegExp.$2 || 'u';
51
52 return 'n';
53 },
54
55 save: function(sid)
56 {
57 return;
58 }
59 }),
60
61 execute: function() {
62 var self = this;
63 return L.network.listSwitchNames().then(function(switches) {
64 L.rpc.batch();
65
66 for (var i = 0; i < switches.length; i++)
67 L.network.getSwitchInfo(switches[i]);
68
69 return L.rpc.flush();
70 }).then(function(switches) {
71 var m = new L.cbi.Map('network', {
72 readonly: !self.options.acls.network
73 });
74
75 for (var i = 0; i < switches.length; i++)
76 {
77 var swname = switches[i]['switch'];
78
79 var vid_opt = 'vlan';
80 var v4k_opt = undefined;
81 var pvid_opt = undefined;
82 var max_vid = switches[i].num_vlans - 1;
83 var num_vlans = switches[i].num_vlans;
84
85 for (var j = 0; j < switches[i].vlan_attrs.length; j++)
86 {
87 switch (switches[i].vlan_attrs[j].name)
88 {
89 case 'tag':
90 case 'vid':
91 case 'pvid':
92 vid_opt = switches[i].vlan_attrs[j].name;
93 max_vid = 4095;
94 break;
95 }
96 }
97
98 for (var j = 0; j < switches[i].port_attrs.length; j++)
99 {
100 switch (switches[i].port_attrs[j].name)
101 {
102 case 'pvid':
103 pvid_opt = switches[i].port_attrs[j].name;
104 break;
105 }
106 }
107
108
109 var sw = m.section(L.cbi.TypedSection, 'switch', {
110 caption: L.tr('Switch "%s"').format(switches[i].model),
111 swname: swname
112 });
113
114 sw.filter = function(section) {
115 return (section['.name'] == this.options.swname ||
116 section.name == this.options.swname);
117 };
118
119 for (var j = 0; j < switches[i].attrs.length; j++)
120 {
121 switch (switches[i].attrs[j].name)
122 {
123 case 'enable_vlan':
124 sw.option(L.cbi.CheckboxValue, 'enable_vlan', {
125 caption: L.tr('Enable VLAN functionality')
126 });
127 break;
128
129 case 'enable_learning':
130 sw.option(L.cbi.CheckboxValue, 'enable_learning', {
131 caption: L.tr('Enable learning and aging'),
132 initial: true,
133 optional: true
134 });
135 break;
136
137 case 'max_length':
138 sw.option(L.cbi.CheckboxValue, 'max_length', {
139 caption: L.tr('Enable Jumbo Frame passthrough'),
140 enabled: '3',
141 optional: true
142 });
143 break;
144
145 case 'enable_vlan4k':
146 v4k_opt = switches[i].attrs[j].name;
147 break;
148 }
149 }
150
151 var vlans = m.section(L.cbi.TableSection, 'switch_vlan', {
152 caption: L.tr('VLANs on "%s"').format(switches[i].model),
153 swname: swname,
154 addremove: true,
155 add_caption: L.tr('Add VLAN entry …')
156 });
157
158 vlans.add = function() {
159 var sections = this.sections();
160 var used_vids = { };
161
162 for (var j = 0; j < sections.length; j++)
163 {
164 var v = this.map.get('network', sections[j]['.name'], 'vlan');
165 if (v)
166 used_vids[v] = true;
167 }
168
169 for (var j = 1; j < num_vlans; j++)
170 {
171 if (used_vids[j.toString()])
172 continue;
173
174 var sid = this.map.add('network', 'switch_vlan');
175 this.map.set('network', sid, 'device', this.options.swname);
176 this.map.set('network', sid, 'vlan', j);
177 break;
178 }
179 };
180
181 vlans.filter = function(section) {
182 return (section.device == this.options.swname);
183 };
184
185 vlans.sections = function() {
186 var s = this.callSuper('sections');
187
188 s.sort(function(a, b) {
189 var x = parseInt(a[vid_opt] || a.vlan);
190 if (isNaN(x))
191 x = 9999;
192
193 var y = parseInt(b[vid_opt] || b.vlan);
194 if (isNaN(y))
195 y = 9999;
196
197 return (x - y);
198 });
199
200 return s;
201 };
202
203 var port_opts = [ ];
204
205 var vo = vlans.option(L.cbi.InputValue, vid_opt, {
206 caption: L.tr('VLAN ID'),
207 datatype: function(val) {
208 var sections = vlans.sections();
209 var used_vids = { };
210
211 for (var j = 0; j < sections.length; j++)
212 {
213 var v = vlans.fields[vid_opt].formvalue(sections[j]['.name']);
214 if (!v)
215 continue;
216
217 if (used_vids[v])
218 return L.tr('VLAN ID must be unique');
219
220 used_vids[v] = true;
221 }
222
223 if (val.match(/[^0-9]/))
224 return L.tr('Invalid VLAN ID');
225
226 val = parseInt(val, 10);
227
228 if (val < 1 || val > max_vid)
229 return L.tr('VLAN ID must be a value between %u and %u').format(1, max_vid);
230
231 return true;
232 }
233 });
234
235 vo.ucivalue = function(sid) {
236 var id = this.map.get('network', sid, vid_opt);
237
238 if (isNaN(parseInt(id)))
239 id = this.map.get('network', sid, 'vlan');
240
241 return id;
242 };
243
244 vo.save = function(sid) {
245 var old_ports = this.map.get('network', sid, 'ports');
246 var new_ports = '';
247
248 for (var j = 0; j < port_opts.length; j++)
249 {
250 var v = port_opts[j].formvalue(sid);
251 if (v != 'n')
252 new_ports += '%s%d%s'.format(
253 new_ports ? ' ' : '', j,
254 (v == 'u') ? '' : 't');
255 }
256
257 if (new_ports != old_ports)
258 this.map.set('network', sid, 'ports', new_ports);
259
260 if (v4k_opt)
261 {
262 var s = sw.sections();
263 for (var j = 0; j < s.length; j++)
264 this.map.set('network', s[j]['.name'], v4k_opt, '1');
265 }
266
267 this.callSuper('save', sid);
268 };
269
270 for (var j = 0; j < switches[i].num_ports; j++)
271 {
272 var label = L.trc('Switch port label', 'Port %d').format(j);
273
274 if (j == switches[i].cpu_port)
275 label = L.trc('Switch port label', 'CPU');
276
277 var po = vlans.option(self.switchPortState, j.toString(), {
278 caption: label + '<br /><small id="portstatus-%s-%d"></small>'.format(swname, j)
279 });
280
281 port_opts.push(po);
282 }
283 }
284
285 return m.insertInto('#map').then(function() {
286 self.repeat(function() {
287 return L.network.getSwitchStatus(swname).then(function(ports) {
288 for (var j = 0; j < ports.length; j++)
289 {
290 var s = L.tr('No link');
291 var d = '&#160;';
292
293 if (ports[j].link)
294 {
295 s = '%dbaseT'.format(ports[j].speed);
296 d = ports[j].full_duplex ? L.tr('Full-duplex') : L.tr('Half-duplex');
297 }
298
299 $('#portstatus-%s-%d'.format(swname, j))
300 .empty().append(s + '<br />' + d);
301 }
302 });
303 }, 5000);
304 });
305 });
306 }
307 });