63427f147f0cb1c6fac9a90c9019dfc958a89330
8 function parse_portvalue(section_id
) {
9 var ports
= L
.toArray(uci
.get('network', section_id
, 'ports'));
11 for (var i
= 0; i
< ports
.length
; i
++) {
12 var m
= ports
[i
].match(/^(\d+)([tu]?)/);
14 if (m
&& m
[1] == this.option
)
21 function validate_portvalue(section_id
, value
) {
25 var sections
= this.section
.cfgsections();
27 for (var i
= 0; i
< sections
.length
; i
++) {
28 if (sections
[i
] == section_id
)
31 if (this.formvalue(sections
[i
]) == 'u')
32 return _('%s is untagged in multiple VLANs!').format(this.title
);
38 function update_interfaces(old_ifname
, new_ifname
) {
39 var interfaces
= uci
.sections('network', 'interface');
41 for (var i
= 0; i
< interfaces
.length
; i
++) {
42 var old_ifnames
= L
.toArray(interfaces
[i
].ifname
),
46 for (var j
= 0; j
< old_ifnames
.length
; j
++) {
47 if (old_ifnames
[j
] == old_ifname
) {
48 new_ifnames
.push(new_ifname
);
52 new_ifnames
.push(old_ifnames
[j
]);
57 uci
.set('network', interfaces
[i
]['.name'], 'ifname', new_ifnames
.join(' '));
59 ui
.addNotification(null, E('p', _('Interface %q device auto-migrated from %q to %q.')
60 .replace(/%q/g, '"%s"').format(interfaces
[i
]['.name'], old_ifname
, new_ifname
)));
65 function render_port_status(node
, portstate
) {
69 if (!portstate
|| !portstate
.link
)
71 E('img', { src
: L
.resource('icons/port_down.png') }),
77 E('img', { src
: L
.resource('icons/port_up.png') }),
79 '%d'.format(portstate
.speed
) + _('baseT'),
81 portstate
.duplex
? _('full-duplex') : _('half-duplex')
87 function update_port_status(topologies
) {
90 for (var switch_name
in topologies
)
91 tasks
.push(callSwconfigPortState(switch_name
).then(L
.bind(function(switch_name
, ports
) {
92 for (var i
= 0; i
< ports
.length
; i
++) {
93 var node
= document
.querySelector('[data-switch="%s"][data-port="%d"]'.format(switch_name
, ports
[i
].port
));
94 render_port_status(node
, ports
[i
]);
96 }, topologies
[switch_name
], switch_name
)));
98 return Promise
.all(tasks
);
101 var callSwconfigFeatures
= rpc
.declare({
103 method
: 'getSwconfigFeatures',
104 params
: [ 'switch' ],
108 var callSwconfigPortState
= rpc
.declare({
110 method
: 'getSwconfigPortState',
111 params
: [ 'switch' ],
112 expect
: { result
: [] }
115 return L
.view
.extend({
117 return network
.getSwitchTopologies().then(function(topologies
) {
120 for (var switch_name
in topologies
) {
121 tasks
.push(callSwconfigFeatures(switch_name
).then(L
.bind(function(features
) {
122 this.features
= features
;
123 }, topologies
[switch_name
])));
124 tasks
.push(callSwconfigPortState(switch_name
).then(L
.bind(function(ports
) {
125 this.portstate
= ports
;
126 }, topologies
[switch_name
])));
129 return Promise
.all(tasks
).then(function() { return topologies
});
133 render: function(topologies
) {
136 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.'));
138 var switchSections
= uci
.sections('network', 'switch');
140 for (var i
= 0; i
< switchSections
.length
; i
++) {
141 var switchSection
= switchSections
[i
],
142 sid
= switchSection
['.name'],
143 switch_name
= switchSection
.name
|| sid
,
144 topology
= topologies
[switch_name
];
147 ui
.addNotification(null, _('Switch %q has an unknown topology - the VLAN settings might not be accurate.').replace(/%q/, switch_name
));
149 topologies
[switch_name
] = topology
= {
155 { num
: 0, label
: 'Port 1' },
156 { num
: 1, label
: 'Port 2' },
157 { num
: 2, label
: 'Port 3' },
158 { num
: 3, label
: 'Port 4' },
159 { num
: 4, label
: 'Port 5' },
160 { num
: 5, label
: 'CPU (eth0)', device
: 'eth0', need_tag
: false }
165 var feat
= topology
.features
,
166 min_vid
= feat
.min_vid
|| 0,
167 max_vid
= feat
.max_vid
|| 16,
168 num_vlans
= feat
.num_vlans
|| 16,
169 switch_title
= _('Switch %q').replace(/%q/, '"%s"'.format(switch_name
)),
170 vlan_title
= _('VLANs on %q').replace(/%q/, '"%s"'.format(switch_name
));
172 if (feat
.switch_title
) {
173 switch_title
+= ' (%s)'.format(feat
.switch_title
);
174 vlan_title
+= ' (%s)'.format(feat
.switch_title
);
177 s
= m
.section(form
.NamedSection
, sid
, 'switch', switch_title
);
180 if (feat
.vlan_option
)
181 s
.option(form
.Flag
, feat
.vlan_option
, _('Enable VLAN functionality'));
183 if (feat
.learning_option
) {
184 o
= s
.option(form
.Flag
, feat
.learning_option
, _('Enable learning and aging'));
185 o
.default = o
.enabled
;
188 if (feat
.jumbo_option
) {
189 o
= s
.option(form
.Flag
, feat
.jumbo_option
, _('Enable Jumbo Frame passthrough'));
194 if (feat
.mirror_option
) {
195 s
.option(form
.Flag
, 'enable_mirror_rx', _('Enable mirroring of incoming packets'));
196 s
.option(form
.Flag
, 'enable_mirror_tx', _('Enable mirroring of outgoing packets'));
198 var sp
= s
.option(form
.ListValue
, 'mirror_source_port', _('Mirror source port')),
199 mp
= s
.option(form
.ListValue
, 'mirror_monitor_port', _('Mirror monitor port'));
201 sp
.depends('enable_mirror_rx', '1');
202 sp
.depends('enable_mirror_tx', '1');
204 mp
.depends('enable_mirror_rx', '1');
205 mp
.depends('enable_mirror_tx', '1');
207 for (var j
= 0; j
< topology
.ports
.length
; j
++) {
208 sp
.value(topology
.ports
[j
].num
, topology
.ports
[j
].label
);
209 mp
.value(topology
.ports
[j
].num
, topology
.ports
[j
].label
);
213 s
= m
.section(form
.TableSection
, 'switch_vlan', vlan_title
);
216 s
.addbtntitle
= _('Add VLAN');
217 s
.topology
= topology
;
218 s
.device
= switch_name
;
220 s
.filter = function(section_id
) {
221 var device
= uci
.get('network', section_id
, 'device');
222 return (device
== switch_name
);
225 s
.cfgsections = function() {
226 var sections
= form
.TableSection
.prototype.cfgsections
.apply(this);
228 return sections
.sort(function(a
, b
) {
229 var vidA
= feat
.vid_option
? uci
.get('network', a
, feat
.vid_option
) : null,
230 vidB
= feat
.vid_option
? uci
.get('network', b
, feat
.vid_option
) : null;
232 vidA
= +(vidA
!= null ? vidA
: uci
.get('network', a
, 'vlan') || 9999);
233 vidB
= +(vidB
!= null ? vidB
: uci
.get('network', b
, 'vlan') || 9999);
235 return (vidA
- vidB
);
239 s
.handleAdd = function(ev
) {
240 var sections
= uci
.sections('network', 'switch_vlan'),
241 section_id
= uci
.add('network', 'switch_vlan'),
245 for (var j
= 0; j
< sections
.length
; j
++) {
246 if (sections
[j
].device
!= s
.device
)
249 var vlan
= +sections
[j
].vlan
,
250 vid
= feat
.vid_option
? +sections
[j
][feat
.vid_option
] : null;
259 uci
.set('network', section_id
, 'device', s
.device
);
260 uci
.set('network', section_id
, 'vlan', max_vlan
+ 1);
263 uci
.set('network', section_id
, feat
.vid_option
, max_vid
+ 1);
265 return this.map
.save(null, true);
270 o
= s
.option(form
.Value
, feat
.vid_option
|| 'vlan', 'VLAN ID');
274 o
.datatype
= 'range(%u,%u)'.format(min_vid
, feat
.vid_option
? 4094 : num_vlans
- 1);
275 o
.description
= _('Port status:');
277 o
.validate = function(section_id
, value
) {
279 m
= feat
.vid_option
? 4094 : num_vlans
- 1;
281 if (isNaN(v
) || v
< min_vid
|| v
> m
)
282 return _('Invalid VLAN ID given! Only IDs between %d and %d are allowed.').format(min_vid
, m
);
284 var sections
= this.section
.cfgsections();
286 for (var i
= 0; i
< sections
.length
; i
++) {
287 if (sections
[i
] == section_id
)
290 if (this.formvalue(sections
[i
]) == v
)
291 return _('Invalid VLAN ID given! Only unique IDs are allowed');
297 o
.write = function(section_id
, value
) {
298 var topology
= this.section
.topology
,
301 for (var i
= 0; i
< port_opts
.length
; i
++) {
302 var tagging
= port_opts
[i
].formvalue(section_id
),
303 portspec
= Array
.isArray(topology
.ports
) ? topology
.ports
[i
] : null;
306 values
.push(port_opts
[i
].option
+ tagging
);
307 else if (tagging
== 'u')
308 values
.push(port_opts
[i
].option
);
310 if (portspec
&& portspec
.device
) {
311 var old_tag
= port_opts
[i
].cfgvalue(section_id
),
312 old_vid
= this.cfgvalue(section_id
);
314 if (old_tag
!= tagging
|| old_vid
!= value
) {
315 var old_ifname
= portspec
.device
+ (old_tag
!= 'u' ? '.' + old_vid
: ''),
316 new_ifname
= portspec
.device
+ (tagging
!= 'u' ? '.' + value
: '');
318 if (old_ifname
!= new_ifname
)
319 update_interfaces(old_ifname
, new_ifname
);
324 if (feat
.vlan4k_option
)
325 uci
.set('network', sid
, feat
.vlan4k_option
, '1');
327 uci
.set('network', section_id
, 'ports', values
.join(' '));
329 return form
.Value
.prototype.write
.apply(this, [section_id
, value
]);
332 o
.cfgvalue = function(section_id
) {
333 var value
= feat
.vid_option
? uci
.get('network', section_id
, feat
.vid_option
) : null;
334 return (value
|| uci
.get('network', section_id
, 'vlan'));
337 s
.option(form
.Value
, 'description', _('Description'));
339 for (var j
= 0; Array
.isArray(topology
.ports
) && j
< topology
.ports
.length
; j
++) {
340 var portspec
= topology
.ports
[j
],
341 portstate
= Array
.isArray(topology
.portstate
) ? topology
.portstate
[portspec
.num
] : null;
343 o
= s
.option(form
.ListValue
, String(portspec
.num
), portspec
.label
);
344 o
.value('', _('off'));
346 if (!portspec
.need_tag
)
347 o
.value('u', _('untagged'));
349 o
.value('t', _('tagged'));
351 o
.cfgvalue
= parse_portvalue
;
352 o
.validate
= validate_portvalue
;
353 o
.write = function() {};
355 o
.description
= render_port_status(E('small', {
356 'data-switch': switch_name
,
357 'data-port': portspec
.num
363 port_opts
.sort(function(a
, b
) {
364 return a
.option
> b
.option
;
368 L
.Poll
.add(L
.bind(update_port_status
, m
, topologies
));