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.'),
5 listSwitchNames: L.rpc.declare({
6 object: 'luci2.network',
8 expect: { switches: [ ] }
11 getSwitchInfo: L.rpc.declare({
12 object: 'luci2.network',
13 method: 'switch_info',
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'];
29 getSwitchStatus: L.rpc.declare({
30 object: 'luci2.network',
31 method: 'switch_status',
33 expect: { ports: [ ] }
36 switchPortState: L.cbi.ListValue.extend({
38 [ 'n', L.trc('Switch port state', 'off') ],
39 [ 'u', L.trc('Switch port state', 'untagged') ],
40 [ 't', L.trc('Switch port state', 'tagged') ]
43 init: function(name, options)
47 options.datatype = function(val, elem)
52 var sections = self.ownerSection.getUCISections();
54 for (var i = 0; i < sections.length; i++)
56 var v = self.formvalue(sections[i]['.name']);
60 return L.tr('Port must not be untagged in multiple VLANs');
70 this.callSuper('init', name, options);
73 ucivalue: function(sid)
75 var ports = (this.ownerMap.get('network', sid, 'ports') || '').match(/[0-9]+[tu]?/g);
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';
94 return self.listSwitchNames().then(function(switches) {
97 for (var i = 0; i < switches.length; i++)
98 self.getSwitchInfo(switches[i]);
100 return L.rpc.flush();
101 }).then(function(switches) {
102 var m = new L.cbi.Map('network', {
103 readonly: !self.options.acls.network
106 for (var i = 0; i < switches.length; i++)
108 var swname = switches[i]['switch'];
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;
116 for (var j = 0; j < switches[i].vlan_attrs.length; j++)
118 switch (switches[i].vlan_attrs[j].name)
123 vid_opt = switches[i].vlan_attrs[j].name;
129 for (var j = 0; j < switches[i].port_attrs.length; j++)
131 switch (switches[i].port_attrs[j].name)
134 pvid_opt = switches[i].port_attrs[j].name;
140 var sw = m.section(L.cbi.TypedSection, 'switch', {
141 caption: L.tr('Switch "%s"').format(switches[i].model),
145 sw.filter = function(section) {
146 return (section['.name'] == this.options.swname ||
147 section.name == this.options.swname);
150 for (var j = 0; j < switches[i].attrs.length; j++)
152 switch (switches[i].attrs[j].name)
155 sw.option(L.cbi.CheckboxValue, 'enable_vlan', {
156 caption: L.tr('Enable VLAN functionality')
160 case 'enable_learning':
161 sw.option(L.cbi.CheckboxValue, 'enable_learning', {
162 caption: L.tr('Enable learning and aging'),
169 sw.option(L.cbi.CheckboxValue, 'max_length', {
170 caption: L.tr('Enable Jumbo Frame passthrough'),
176 case 'enable_vlan4k':
177 v4k_opt = switches[i].attrs[j].name;
182 var vlans = m.section(L.cbi.TableSection, 'switch_vlan', {
183 caption: L.tr('VLANs on "%s"').format(switches[i].model),
186 add_caption: L.tr('Add VLAN entry …')
189 vlans.add = function() {
190 var sections = this.getUCISections();
193 for (var j = 0; j < sections.length; j++)
195 var v = this.ownerMap.get('network', sections[j]['.name'], 'vlan');
200 for (var j = 1; j < num_vlans; j++)
202 if (used_vids[j.toString()])
205 var sid = this.ownerMap.add('network', 'switch_vlan');
206 this.ownerMap.set('network', sid, 'device', this.options.swname);
207 this.ownerMap.set('network', sid, 'vlan', j);
212 vlans.filter = function(section) {
213 return (section.device == this.options.swname);
216 vlans.sections = function() {
217 var s = this.callSuper('sections');
219 s.sort(function(a, b) {
220 var x = parseInt(a[vid_opt] || a.vlan);
224 var y = parseInt(b[vid_opt] || b.vlan);
236 var vo = vlans.option(L.cbi.InputValue, vid_opt, {
237 caption: L.tr('VLAN ID'),
238 datatype: function(val) {
239 var sections = vlans.getUCISections();
242 for (var j = 0; j < sections.length; j++)
244 var v = vlans.fields[vid_opt].formvalue(sections[j]['.name']);
249 return L.tr('VLAN ID must be unique');
254 if (val.match(/[^0-9]/))
255 return L.tr('Invalid VLAN ID');
257 val = parseInt(val, 10);
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);
266 vo.ucivalue = function(sid) {
267 var id = this.ownerMap.get('network', sid, vid_opt);
269 if (isNaN(parseInt(id)))
270 id = this.ownerMap.get('network', sid, 'vlan');
275 vo.save = function(sid) {
276 var old_ports = this.ownerMap.get('network', sid, 'ports');
279 for (var j = 0; j < port_opts.length; j++)
281 var v = port_opts[j].formvalue(sid);
283 new_ports += '%s%d%s'.format(
284 new_ports ? ' ' : '', j,
285 (v == 'u') ? '' : 't');
288 if (new_ports != old_ports)
289 this.ownerMap.set('network', sid, 'ports', new_ports);
293 var s = sw.getUCISections();
294 for (var j = 0; j < s.length; j++)
295 this.ownerMap.set('network', s[j]['.name'], v4k_opt, '1');
298 this.callSuper('save', sid);
301 for (var j = 0; j < switches[i].num_ports; j++)
303 var label = L.trc('Switch port label', 'Port %d').format(j);
305 if (j == switches[i].cpu_port)
306 label = L.trc('Switch port label', 'CPU');
308 var po = vlans.option(self.switchPortState, j.toString(), {
309 caption: label + '<br /><small id="portstatus-%s-%d"></small>'.format(swname, j)
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++)
321 var s = L.tr('No link');
326 s = '%dbaseT'.format(ports[j].speed);
327 d = ports[j].full_duplex ? L.tr('Full-duplex') : L.tr('Half-duplex');
330 $('#portstatus-%s-%d'.format(swname, j))
331 .empty().append(s + '<br />' + d);