Translated using Weblate (Japanese)
[oweals/luci.git] / modules / luci-mod-network / htdocs / luci-static / resources / view / network / switch.js
1 'use strict';
2 'require view';
3 'require dom';
4 'require poll';
5 'require ui';
6 'require rpc';
7 'require uci';
8 'require form';
9 'require network';
10
11 function parse_portvalue(section_id) {
12         var ports = L.toArray(uci.get('network', section_id, 'ports'));
13
14         for (var i = 0; i < ports.length; i++) {
15                 var m = ports[i].match(/^(\d+)([tu]?)/);
16
17                 if (m && m[1] == this.option)
18                         return m[2] || 'u';
19         }
20
21         return '';
22 }
23
24 function validate_portvalue(section_id, value) {
25         if (value != 'u')
26                 return true;
27
28         var sections = this.section.cfgsections();
29
30         for (var i = 0; i < sections.length; i++) {
31                 if (sections[i] == section_id)
32                         continue;
33
34                 if (this.formvalue(sections[i]) == 'u')
35                         return _('%s is untagged in multiple VLANs!').format(this.title);
36         }
37
38         return true;
39 }
40
41 function update_interfaces(old_ifname, new_ifname) {
42         var interfaces = uci.sections('network', 'interface');
43
44         for (var i = 0; i < interfaces.length; i++) {
45                 var old_ifnames = L.toArray(interfaces[i].ifname),
46                     new_ifnames = [],
47                     changed = false;
48
49                 for (var j = 0; j < old_ifnames.length; j++) {
50                         if (old_ifnames[j] == old_ifname) {
51                                 new_ifnames.push(new_ifname);
52                                 changed = true;
53                         }
54                         else {
55                                 new_ifnames.push(old_ifnames[j]);
56                         }
57                 }
58
59                 if (changed) {
60                         uci.set('network', interfaces[i]['.name'], 'ifname', new_ifnames.join(' '));
61
62                         ui.addNotification(null, E('p', _('Interface %q device auto-migrated from %q to %q.')
63                                 .replace(/%q/g, '"%s"').format(interfaces[i]['.name'], old_ifname, new_ifname)));
64                 }
65         }
66 }
67
68 function render_port_status(node, portstate) {
69         if (!node)
70                 return null;
71
72         if (!portstate || !portstate.link)
73                 dom.content(node, [
74                         E('img', { src: L.resource('icons/port_down.png') }),
75                         E('br'),
76                         _('no link')
77                 ]);
78         else
79                 dom.content(node, [
80                         E('img', { src: L.resource('icons/port_up.png') }),
81                         E('br'),
82                         '%d'.format(portstate.speed) + _('baseT'),
83                         E('br'),
84                         portstate.duplex ? _('full-duplex') : _('half-duplex')
85                 ]);
86
87         return node;
88 }
89
90 function update_port_status(topologies) {
91         var tasks = [];
92
93         for (var switch_name in topologies)
94                 tasks.push(callSwconfigPortState(switch_name).then(L.bind(function(switch_name, ports) {
95                         for (var i = 0; i < ports.length; i++) {
96                                 var node = document.querySelector('[data-switch="%s"][data-port="%d"]'.format(switch_name, ports[i].port));
97                                 render_port_status(node, ports[i]);
98                         }
99                 }, topologies[switch_name], switch_name)));
100
101         return Promise.all(tasks);
102 }
103
104 var callSwconfigFeatures = rpc.declare({
105         object: 'luci',
106         method: 'getSwconfigFeatures',
107         params: [ 'switch' ],
108         expect: { '': {} }
109 });
110
111 var callSwconfigPortState = rpc.declare({
112         object: 'luci',
113         method: 'getSwconfigPortState',
114         params: [ 'switch' ],
115         expect: { result: [] }
116 });
117
118 return view.extend({
119         load: function() {
120                 return network.getSwitchTopologies().then(function(topologies) {
121                         var tasks = [];
122
123                         for (var switch_name in topologies) {
124                                 tasks.push(callSwconfigFeatures(switch_name).then(L.bind(function(features) {
125                                         this.features = features;
126                                 }, topologies[switch_name])));
127                                 tasks.push(callSwconfigPortState(switch_name).then(L.bind(function(ports) {
128                                         this.portstate = ports;
129                                 }, topologies[switch_name])));
130                         }
131
132                         return Promise.all(tasks).then(function() { return topologies });
133                 });
134         },
135
136         render: function(topologies) {
137                 var m, s, o;
138
139                 m = new form.Map('network', _('Switch'), _('The network ports on this device can be combined to several <abbr title=\"Virtual Local Area Network\">VLAN</abbr>s in which computers can communicate directly with each other. <abbr title=\"Virtual Local Area Network\">VLAN</abbr>s 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.'));
140
141                 var switchSections = uci.sections('network', 'switch');
142
143                 for (var i = 0; i < switchSections.length; i++) {
144                         var switchSection   = switchSections[i],
145                             sid             = switchSection['.name'],
146                             switch_name     = switchSection.name || sid,
147                             topology        = topologies[switch_name];
148
149                         if (!topology) {
150                                 ui.addNotification(null, _('Switch %q has an unknown topology - the VLAN settings might not be accurate.').replace(/%q/, switch_name));
151
152                                 topologies[switch_name] = topology = {
153                                         features: {},
154                                         netdevs: {
155                                                 5: 'eth0'
156                                         },
157                                         ports: [
158                                                 { num: 0, label: 'Port 1' },
159                                                 { num: 1, label: 'Port 2' },
160                                                 { num: 2, label: 'Port 3' },
161                                                 { num: 3, label: 'Port 4' },
162                                                 { num: 4, label: 'Port 5' },
163                                                 { num: 5, label: 'CPU (eth0)', device: 'eth0', need_tag: false }
164                                         ]
165                                 };
166                         }
167
168                         var feat = topology.features,
169                             min_vid = feat.min_vid || 0,
170                             max_vid = feat.max_vid || 16,
171                             num_vlans = feat.num_vlans || 16,
172                             switch_title = _('Switch %q').replace(/%q/, '"%s"'.format(switch_name)),
173                             vlan_title = _('VLANs on %q').replace(/%q/, '"%s"'.format(switch_name));
174
175                         if (feat.switch_title) {
176                                 switch_title += ' (%s)'.format(feat.switch_title);
177                                 vlan_title += ' (%s)'.format(feat.switch_title);
178                         }
179
180                         s = m.section(form.NamedSection, sid, 'switch', switch_title);
181                         s.addremove = false;
182
183                         if (feat.vlan_option)
184                                 s.option(form.Flag, feat.vlan_option, _('Enable VLAN functionality'));
185
186                         if (feat.learning_option) {
187                                 o = s.option(form.Flag, feat.learning_option, _('Enable learning and aging'));
188                                 o.default = o.enabled;
189                         }
190
191                         if (feat.jumbo_option) {
192                                 o = s.option(form.Flag, feat.jumbo_option, _('Enable Jumbo Frame passthrough'));
193                                 o.enabled = '3';
194                                 o.rmempty = true;
195                         }
196
197                         if (feat.mirror_option) {
198                                 s.option(form.Flag, 'enable_mirror_rx', _('Enable mirroring of incoming packets'));
199                                 s.option(form.Flag, 'enable_mirror_tx', _('Enable mirroring of outgoing packets'));
200
201                                 var sp = s.option(form.ListValue, 'mirror_source_port', _('Mirror source port')),
202                                     mp = s.option(form.ListValue, 'mirror_monitor_port', _('Mirror monitor port'));
203
204                                 sp.depends('enable_mirror_rx', '1');
205                                 sp.depends('enable_mirror_tx', '1');
206
207                                 mp.depends('enable_mirror_rx', '1');
208                                 mp.depends('enable_mirror_tx', '1');
209
210                                 for (var j = 0; j < topology.ports.length; j++) {
211                                         sp.value(topology.ports[j].num, topology.ports[j].label);
212                                         mp.value(topology.ports[j].num, topology.ports[j].label);
213                                 }
214                         }
215
216                         s = m.section(form.TableSection, 'switch_vlan', vlan_title);
217                         s.anonymous = true;
218                         s.addremove = true;
219                         s.addbtntitle = _('Add VLAN');
220                         s.topology = topology;
221                         s.device = switch_name;
222
223                         s.filter = function(section_id) {
224                                 var device = uci.get('network', section_id, 'device');
225                                 return (device == switch_name);
226                         };
227
228                         s.cfgsections = function() {
229                                 var sections = form.TableSection.prototype.cfgsections.apply(this);
230
231                                 return sections.sort(function(a, b) {
232                                         var vidA = feat.vid_option ? uci.get('network', a, feat.vid_option) : null,
233                                             vidB = feat.vid_option ? uci.get('network', b, feat.vid_option) : null;
234
235                                         vidA = +(vidA != null ? vidA : uci.get('network', a, 'vlan') || 9999);
236                                         vidB = +(vidB != null ? vidB : uci.get('network', b, 'vlan') || 9999);
237
238                                         return (vidA - vidB);
239                                 });
240                         };
241
242                         s.handleAdd = function(ev) {
243                                 var sections = uci.sections('network', 'switch_vlan'),
244                                     section_id = uci.add('network', 'switch_vlan'),
245                                     max_vlan = 0,
246                                     max_vid = 0;
247
248                                 for (var j = 0; j < sections.length; j++) {
249                                         if (sections[j].device != s.device)
250                                                 continue;
251
252                                         var vlan = +sections[j].vlan,
253                                             vid = feat.vid_option ? +sections[j][feat.vid_option] : null;
254
255                                         if (vlan > max_vlan)
256                                                 max_vlan = vlan;
257
258                                         if (vid > max_vid)
259                                                 max_vid = vid;
260                                 }
261
262                                 uci.set('network', section_id, 'device', s.device);
263                                 uci.set('network', section_id, 'vlan', max_vlan + 1);
264
265                                 if (feat.vid_option)
266                                         uci.set('network', section_id, feat.vid_option, max_vid + 1);
267
268                                 return this.map.save(null, true);
269                         };
270
271                         var port_opts = [];
272
273                         o = s.option(form.Value, feat.vid_option || 'vlan', 'VLAN ID');
274                         o.rmempty = false;
275                         o.forcewrite = true;
276                         o.vlan_used = {};
277                         o.datatype = 'range(%u,%u)'.format(min_vid, feat.vid_option ? 4094 : num_vlans - 1);
278                         o.description = _('Port status:');
279
280                         o.validate = function(section_id, value) {
281                                 var v = +value,
282                                     m = feat.vid_option ? 4094 : num_vlans - 1;
283
284                                 if (isNaN(v) || v < min_vid || v > m)
285                                         return _('Invalid VLAN ID given! Only IDs between %d and %d are allowed.').format(min_vid, m);
286
287                                 var sections = this.section.cfgsections();
288
289                                 for (var i = 0; i < sections.length; i++) {
290                                         if (sections[i] == section_id)
291                                                 continue;
292
293                                         if (this.formvalue(sections[i]) == v)
294                                                 return _('Invalid VLAN ID given! Only unique IDs are allowed');
295                                 }
296
297                                 return true;
298                         };
299
300                         o.write = function(section_id, value) {
301                                 var topology = this.section.topology,
302                                     values = [];
303
304                                 for (var i = 0; i < port_opts.length; i++) {
305                                         var tagging = port_opts[i].formvalue(section_id),
306                                             portspec = Array.isArray(topology.ports) ? topology.ports[i] : null;
307
308                                         if (tagging == 't')
309                                                 values.push(port_opts[i].option + tagging);
310                                         else if (tagging == 'u')
311                                                 values.push(port_opts[i].option);
312
313                                         if (portspec && portspec.device) {
314                                                 var old_tag = port_opts[i].cfgvalue(section_id),
315                                                     old_vid = this.cfgvalue(section_id);
316
317                                                 if (old_tag != tagging || old_vid != value) {
318                                                         var old_ifname = portspec.device + (old_tag != 'u' ? '.' + old_vid : ''),
319                                                             new_ifname = portspec.device + (tagging != 'u' ? '.' + value : '');
320
321                                                         if (old_ifname != new_ifname)
322                                                                 update_interfaces(old_ifname, new_ifname);
323                                                 }
324                                         }
325                                 }
326
327                                 if (feat.vlan4k_option)
328                                         uci.set('network', sid, feat.vlan4k_option, '1');
329
330                                 uci.set('network', section_id, 'ports', values.join(' '));
331
332                                 return form.Value.prototype.write.apply(this, [section_id, value]);
333                         };
334
335                         o.cfgvalue = function(section_id) {
336                                 var value = feat.vid_option ? uci.get('network', section_id, feat.vid_option) : null;
337                                 return (value || uci.get('network', section_id, 'vlan'));
338                         };
339
340                         s.option(form.Value, 'description', _('Description'));
341
342                         for (var j = 0; Array.isArray(topology.ports) && j < topology.ports.length; j++) {
343                                 var portspec = topology.ports[j],
344                                     portstate = Array.isArray(topology.portstate) ? topology.portstate[portspec.num] : null;
345
346                                 o = s.option(form.ListValue, String(portspec.num), portspec.label);
347                                 o.value('', _('off'));
348
349                                 if (!portspec.need_tag)
350                                         o.value('u', _('untagged'));
351
352                                 o.value('t', _('tagged'));
353
354                                 o.cfgvalue = parse_portvalue;
355                                 o.validate = validate_portvalue;
356                                 o.write    = function() {};
357
358                                 o.description = render_port_status(E('small', {
359                                         'data-switch': switch_name,
360                                         'data-port': portspec.num
361                                 }), portstate);
362
363                                 port_opts.push(o);
364                         }
365
366                         port_opts.sort(function(a, b) {
367                                 return a.option > b.option;
368                         });
369                 }
370
371                 poll.add(L.bind(update_port_status, m, topologies));
372
373                 return m.render();
374         }
375 });