11 function parse_portvalue(section_id) {
12 var ports = L.toArray(uci.get('network', section_id, 'ports'));
14 for (var i = 0; i < ports.length; i++) {
15 var m = ports[i].match(/^(\d+)([tu]?)/);
17 if (m && m[1] == this.option)
24 function validate_portvalue(section_id, value) {
28 var sections = this.section.cfgsections();
30 for (var i = 0; i < sections.length; i++) {
31 if (sections[i] == section_id)
34 if (this.formvalue(sections[i]) == 'u')
35 return _('%s is untagged in multiple VLANs!').format(this.title);
41 function update_interfaces(old_ifname, new_ifname) {
42 var interfaces = uci.sections('network', 'interface');
44 for (var i = 0; i < interfaces.length; i++) {
45 var old_ifnames = L.toArray(interfaces[i].ifname),
49 for (var j = 0; j < old_ifnames.length; j++) {
50 if (old_ifnames[j] == old_ifname) {
51 new_ifnames.push(new_ifname);
55 new_ifnames.push(old_ifnames[j]);
60 uci.set('network', interfaces[i]['.name'], 'ifname', new_ifnames.join(' '));
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)));
68 function render_port_status(node, portstate) {
72 if (!portstate || !portstate.link)
74 E('img', { src: L.resource('icons/port_down.png') }),
80 E('img', { src: L.resource('icons/port_up.png') }),
82 '%d'.format(portstate.speed) + _('baseT'),
84 portstate.duplex ? _('full-duplex') : _('half-duplex')
90 function update_port_status(topologies) {
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]);
99 }, topologies[switch_name], switch_name)));
101 return Promise.all(tasks);
104 var callSwconfigFeatures = rpc.declare({
106 method: 'getSwconfigFeatures',
107 params: [ 'switch' ],
111 var callSwconfigPortState = rpc.declare({
113 method: 'getSwconfigPortState',
114 params: [ 'switch' ],
115 expect: { result: [] }
120 return network.getSwitchTopologies().then(function(topologies) {
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])));
132 return Promise.all(tasks).then(function() { return topologies });
136 render: function(topologies) {
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.'));
141 var switchSections = uci.sections('network', 'switch');
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];
150 ui.addNotification(null, _('Switch %q has an unknown topology - the VLAN settings might not be accurate.').replace(/%q/, switch_name));
152 topologies[switch_name] = topology = {
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 }
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));
175 if (feat.switch_title) {
176 switch_title += ' (%s)'.format(feat.switch_title);
177 vlan_title += ' (%s)'.format(feat.switch_title);
180 s = m.section(form.NamedSection, sid, 'switch', switch_title);
183 if (feat.vlan_option)
184 s.option(form.Flag, feat.vlan_option, _('Enable VLAN functionality'));
186 if (feat.learning_option) {
187 o = s.option(form.Flag, feat.learning_option, _('Enable learning and aging'));
188 o.default = o.enabled;
191 if (feat.jumbo_option) {
192 o = s.option(form.Flag, feat.jumbo_option, _('Enable Jumbo Frame passthrough'));
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'));
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'));
204 sp.depends('enable_mirror_rx', '1');
205 sp.depends('enable_mirror_tx', '1');
207 mp.depends('enable_mirror_rx', '1');
208 mp.depends('enable_mirror_tx', '1');
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);
216 s = m.section(form.TableSection, 'switch_vlan', vlan_title);
219 s.addbtntitle = _('Add VLAN');
220 s.topology = topology;
221 s.device = switch_name;
223 s.filter = function(section_id) {
224 var device = uci.get('network', section_id, 'device');
225 return (device == switch_name);
228 s.cfgsections = function() {
229 var sections = form.TableSection.prototype.cfgsections.apply(this);
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;
235 vidA = +(vidA != null ? vidA : uci.get('network', a, 'vlan') || 9999);
236 vidB = +(vidB != null ? vidB : uci.get('network', b, 'vlan') || 9999);
238 return (vidA - vidB);
242 s.handleAdd = function(ev) {
243 var sections = uci.sections('network', 'switch_vlan'),
244 section_id = uci.add('network', 'switch_vlan'),
248 for (var j = 0; j < sections.length; j++) {
249 if (sections[j].device != s.device)
252 var vlan = +sections[j].vlan,
253 vid = feat.vid_option ? +sections[j][feat.vid_option] : null;
262 uci.set('network', section_id, 'device', s.device);
263 uci.set('network', section_id, 'vlan', max_vlan + 1);
266 uci.set('network', section_id, feat.vid_option, max_vid + 1);
268 return this.map.save(null, true);
273 o = s.option(form.Value, feat.vid_option || 'vlan', 'VLAN ID');
277 o.datatype = 'range(%u,%u)'.format(min_vid, feat.vid_option ? 4094 : num_vlans - 1);
278 o.description = _('Port status:');
280 o.validate = function(section_id, value) {
282 m = feat.vid_option ? 4094 : num_vlans - 1;
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);
287 var sections = this.section.cfgsections();
289 for (var i = 0; i < sections.length; i++) {
290 if (sections[i] == section_id)
293 if (this.formvalue(sections[i]) == v)
294 return _('Invalid VLAN ID given! Only unique IDs are allowed');
300 o.write = function(section_id, value) {
301 var topology = this.section.topology,
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;
309 values.push(port_opts[i].option + tagging);
310 else if (tagging == 'u')
311 values.push(port_opts[i].option);
313 if (portspec && portspec.device) {
314 var old_tag = port_opts[i].cfgvalue(section_id),
315 old_vid = this.cfgvalue(section_id);
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 : '');
321 if (old_ifname != new_ifname)
322 update_interfaces(old_ifname, new_ifname);
327 if (feat.vlan4k_option)
328 uci.set('network', sid, feat.vlan4k_option, '1');
330 uci.set('network', section_id, 'ports', values.join(' '));
332 return form.Value.prototype.write.apply(this, [section_id, value]);
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'));
340 s.option(form.Value, 'description', _('Description'));
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;
346 o = s.option(form.ListValue, String(portspec.num), portspec.label);
347 o.value('', _('off'));
349 if (!portspec.need_tag)
350 o.value('u', _('untagged'));
352 o.value('t', _('tagged'));
354 o.cfgvalue = parse_portvalue;
355 o.validate = validate_portvalue;
356 o.write = function() {};
358 o.description = render_port_status(E('small', {
359 'data-switch': switch_name,
360 'data-port': portspec.num
366 port_opts.sort(function(a, b) {
367 return a.option > b.option;
371 poll.add(L.bind(update_port_status, m, topologies));