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