mirror of
				https://github.com/Ysurac/openmptcprouter-feeds.git
				synced 2025-03-09 15:40:03 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			370 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			370 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
'use strict';
 | 
						|
'require ui';
 | 
						|
'require rpc';
 | 
						|
'require uci';
 | 
						|
'require form';
 | 
						|
'require network';
 | 
						|
 | 
						|
function parse_portvalue(section_id) {
 | 
						|
	var ports = L.toArray(uci.get('network', section_id, 'ports'));
 | 
						|
 | 
						|
	for (var i = 0; i < ports.length; i++) {
 | 
						|
		var m = ports[i].match(/^(\d+)([tu]?)/);
 | 
						|
 | 
						|
		if (m && m[1] == this.option)
 | 
						|
			return m[2] || 'u';
 | 
						|
	}
 | 
						|
 | 
						|
	return '';
 | 
						|
}
 | 
						|
 | 
						|
function validate_portvalue(section_id, value) {
 | 
						|
	if (value != 'u')
 | 
						|
		return true;
 | 
						|
 | 
						|
	var sections = this.section.cfgsections();
 | 
						|
 | 
						|
	for (var i = 0; i < sections.length; i++) {
 | 
						|
		if (sections[i] == section_id)
 | 
						|
			continue;
 | 
						|
 | 
						|
		if (this.formvalue(sections[i]) == 'u')
 | 
						|
			return _('%s is untagged in multiple VLANs!').format(this.title);
 | 
						|
	}
 | 
						|
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
function update_interfaces(old_ifname, new_ifname) {
 | 
						|
	var interfaces = uci.sections('network', 'interface');
 | 
						|
 | 
						|
	for (var i = 0; i < interfaces.length; i++) {
 | 
						|
		var old_ifnames = L.toArray(interfaces[i].ifname),
 | 
						|
		    new_ifnames = [],
 | 
						|
		    changed = false;
 | 
						|
 | 
						|
		for (var j = 0; j < old_ifnames.length; j++) {
 | 
						|
			if (old_ifnames[j] == old_ifname) {
 | 
						|
				new_ifnames.push(new_ifname);
 | 
						|
				changed = true;
 | 
						|
			}
 | 
						|
			else {
 | 
						|
				new_ifnames.push(old_ifnames[j]);
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if (changed) {
 | 
						|
			uci.set('network', interfaces[i]['.name'], 'ifname', new_ifnames.join(' '));
 | 
						|
 | 
						|
			ui.addNotification(null, E('p', _('Interface %q device auto-migrated from %q to %q.')
 | 
						|
				.replace(/%q/g, '"%s"').format(interfaces[i]['.name'], old_ifname, new_ifname)));
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
function render_port_status(node, portstate) {
 | 
						|
	if (!node)
 | 
						|
		return null;
 | 
						|
 | 
						|
	if (!portstate.link)
 | 
						|
		L.dom.content(node, [
 | 
						|
			E('img', { src: L.resource('icons/port_down.png') }),
 | 
						|
			E('br'),
 | 
						|
			_('no link')
 | 
						|
		]);
 | 
						|
	else
 | 
						|
		L.dom.content(node, [
 | 
						|
			E('img', { src: L.resource('icons/port_up.png') }),
 | 
						|
			E('br'),
 | 
						|
			'%d'.format(portstate.speed) + _('baseT'),
 | 
						|
			E('br'),
 | 
						|
			portstate.duplex ? _('full-duplex') : _('half-duplex')
 | 
						|
		]);
 | 
						|
 | 
						|
	return node;
 | 
						|
}
 | 
						|
 | 
						|
function update_port_status(topologies) {
 | 
						|
	var tasks = [];
 | 
						|
 | 
						|
	for (var switch_name in topologies)
 | 
						|
		tasks.push(callSwconfigPortState(switch_name).then(L.bind(function(switch_name, ports) {
 | 
						|
			for (var i = 0; i < ports.length; i++) {
 | 
						|
				var node = document.querySelector('[data-switch="%s"][data-port="%d"]'.format(switch_name, ports[i].port));
 | 
						|
				render_port_status(node, ports[i]);
 | 
						|
			}
 | 
						|
		}, topologies[switch_name], switch_name)));
 | 
						|
 | 
						|
	return Promise.all(tasks);
 | 
						|
}
 | 
						|
 | 
						|
var callSwconfigFeatures = rpc.declare({
 | 
						|
	object: 'luci',
 | 
						|
	method: 'getSwconfigFeatures',
 | 
						|
	params: [ 'switch' ],
 | 
						|
	expect: { '': {} }
 | 
						|
});
 | 
						|
 | 
						|
var callSwconfigPortState = rpc.declare({
 | 
						|
	object: 'luci',
 | 
						|
	method: 'getSwconfigPortState',
 | 
						|
	params: [ 'switch' ],
 | 
						|
	expect: { result: [] }
 | 
						|
});
 | 
						|
 | 
						|
return L.view.extend({
 | 
						|
	load: function() {
 | 
						|
		return network.getSwitchTopologies().then(function(topologies) {
 | 
						|
			var tasks = [];
 | 
						|
 | 
						|
			for (var switch_name in topologies) {
 | 
						|
				tasks.push(callSwconfigFeatures(switch_name).then(L.bind(function(features) {
 | 
						|
					this.features = features;
 | 
						|
				}, topologies[switch_name])));
 | 
						|
				tasks.push(callSwconfigPortState(switch_name).then(L.bind(function(ports) {
 | 
						|
					this.portstate = ports;
 | 
						|
				}, topologies[switch_name])));
 | 
						|
			}
 | 
						|
 | 
						|
			return Promise.all(tasks).then(function() { return topologies });
 | 
						|
		});
 | 
						|
	},
 | 
						|
 | 
						|
	render: function(topologies) {
 | 
						|
		var m, s, o;
 | 
						|
 | 
						|
		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.'));
 | 
						|
 | 
						|
		var switchSections = uci.sections('network', 'switch');
 | 
						|
 | 
						|
		for (var i = 0; i < switchSections.length; i++) {
 | 
						|
			var switchSection   = switchSections[i],
 | 
						|
			    sid             = switchSection['.name'],
 | 
						|
			    switch_name     = switchSection.name || sid,
 | 
						|
			    topology        = topologies[switch_name];
 | 
						|
 | 
						|
			if (!topology) {
 | 
						|
				ui.addNotification(null, _('Switch %q has an unknown topology - the VLAN settings might not be accurate.').replace(/%q/, switch_name));
 | 
						|
 | 
						|
				topology = {
 | 
						|
					features: {},
 | 
						|
					netdevs: {
 | 
						|
						5: 'eth0'
 | 
						|
					},
 | 
						|
					ports: [
 | 
						|
						{ num: 0, label: 'Port 1' },
 | 
						|
						{ num: 1, label: 'Port 2' },
 | 
						|
						{ num: 2, label: 'Port 3' },
 | 
						|
						{ num: 3, label: 'Port 4' },
 | 
						|
						{ num: 4, label: 'Port 5' },
 | 
						|
						{ num: 5, label: 'CPU (eth0)', device: 'eth0', need_tag: false }
 | 
						|
					]
 | 
						|
				};
 | 
						|
			}
 | 
						|
 | 
						|
			var feat = topology.features,
 | 
						|
			    min_vid = feat.min_vid || 0,
 | 
						|
			    max_vid = feat.max_vid || 16,
 | 
						|
			    num_vlans = feat.num_vlans || 16,
 | 
						|
			    switch_title = _('Switch %q').replace(/%q/, '"%s"'.format(switch_name)),
 | 
						|
			    vlan_title = _('VLANs on %q').replace(/%q/, '"%s"'.format(switch_name));
 | 
						|
 | 
						|
			if (feat.switch_title) {
 | 
						|
				switch_title += ' (%s)'.format(feat.switch_title);
 | 
						|
				vlan_title += ' (%s)'.format(feat.switch_title);
 | 
						|
			}
 | 
						|
 | 
						|
			s = m.section(form.NamedSection, sid, 'switch', switch_title);
 | 
						|
			s.addremove = false;
 | 
						|
 | 
						|
			if (feat.vlan_option)
 | 
						|
				s.option(form.Flag, feat.vlan_option, _('Enable VLAN functionality'));
 | 
						|
 | 
						|
			if (feat.learning_option) {
 | 
						|
				o = s.option(form.Flag, feat.learning_option, _('Enable learning and aging'));
 | 
						|
				o.default = o.enabled;
 | 
						|
			}
 | 
						|
 | 
						|
			if (feat.jumbo_option) {
 | 
						|
				o = s.option(form.Flag, feat.jumbo_option, _('Enable Jumbo Frame passthrough'));
 | 
						|
				o.enabled = '3';
 | 
						|
				o.rmempty = true;
 | 
						|
			}
 | 
						|
 | 
						|
			if (feat.mirror_option) {
 | 
						|
				s.option(form.Flag, 'enable_mirror_rx', _('Enable mirroring of incoming packets'));
 | 
						|
				s.option(form.Flag, 'enable_mirror_tx', _('Enable mirroring of outgoing packets'));
 | 
						|
 | 
						|
				var sp = s.option(form.ListValue, 'mirror_source_port', _('Mirror source port')),
 | 
						|
				    mp = s.option(form.ListValue, 'mirror_monitor_port', _('Mirror monitor port'));
 | 
						|
 | 
						|
				sp.depends('enable_mirror_rx', '1');
 | 
						|
				sp.depends('enable_mirror_tx', '1');
 | 
						|
 | 
						|
				mp.depends('enable_mirror_rx', '1');
 | 
						|
				mp.depends('enable_mirror_tx', '1');
 | 
						|
 | 
						|
				for (var j = 0; j < topology.ports.length; j++) {
 | 
						|
					sp.value(topology.ports[j].num, topology.ports[j].label);
 | 
						|
					mp.value(topology.ports[j].num, topology.ports[j].label);
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			s = m.section(form.TableSection, 'switch_vlan', vlan_title);
 | 
						|
			s.anonymous = true;
 | 
						|
			s.addremove = true;
 | 
						|
			s.addbtntitle = _('Add VLAN');
 | 
						|
			s.topology = topology;
 | 
						|
			s.device = switch_name;
 | 
						|
 | 
						|
			s.filter = function(section_id) {
 | 
						|
				var device = uci.get('network', section_id, 'device');
 | 
						|
				return (device == switch_name);
 | 
						|
			};
 | 
						|
 | 
						|
			s.cfgsections = function() {
 | 
						|
				var sections = form.TableSection.prototype.cfgsections.apply(this);
 | 
						|
 | 
						|
				return sections.sort(function(a, b) {
 | 
						|
					var vidA = feat.vid_option ? uci.get('network', a, feat.vid_option) : null,
 | 
						|
					    vidB = feat.vid_option ? uci.get('network', b, feat.vid_option) : null;
 | 
						|
 | 
						|
					vidA = +(vidA != null ? vidA : uci.get('network', a, 'vlan') || 9999);
 | 
						|
					vidB = +(vidB != null ? vidB : uci.get('network', b, 'vlan') || 9999);
 | 
						|
 | 
						|
					return (vidA - vidB);
 | 
						|
				});
 | 
						|
			};
 | 
						|
 | 
						|
			s.handleAdd = function(ev) {
 | 
						|
				var sections = uci.sections('network', 'switch_vlan'),
 | 
						|
				    section_id = uci.add('network', 'switch_vlan'),
 | 
						|
				    max_vlan = 0,
 | 
						|
				    max_vid = 0;
 | 
						|
 | 
						|
				for (var j = 0; j < sections.length; j++) {
 | 
						|
					if (sections[j].device != s.device)
 | 
						|
						continue;
 | 
						|
 | 
						|
					var vlan = +sections[j].vlan,
 | 
						|
					    vid = feat.vid_option ? +sections[j][feat.vid_option] : null;
 | 
						|
 | 
						|
					if (vlan > max_vlan)
 | 
						|
						max_vlan = vlan;
 | 
						|
 | 
						|
					if (vid > max_vid)
 | 
						|
						max_vid = vid;
 | 
						|
				}
 | 
						|
 | 
						|
				uci.set('network', section_id, 'device', s.device);
 | 
						|
				uci.set('network', section_id, 'vlan', max_vlan + 1);
 | 
						|
 | 
						|
				if (feat.vid_option)
 | 
						|
					uci.set('network', section_id, feat.vid_option, max_vid + 1);
 | 
						|
 | 
						|
				return this.map.save(null, true);
 | 
						|
			};
 | 
						|
 | 
						|
			var port_opts = [];
 | 
						|
 | 
						|
			o = s.option(form.Value, feat.vid_option || 'vlan', 'VLAN ID');
 | 
						|
			o.rmempty = false;
 | 
						|
			o.forcewrite = true;
 | 
						|
			o.vlan_used = {};
 | 
						|
			o.datatype = 'range(%u,%u)'.format(min_vid, feat.vid_option ? 4094 : num_vlans - 1);
 | 
						|
			o.description = _('Port status:');
 | 
						|
 | 
						|
			o.validate = function(section_id, value) {
 | 
						|
				var v = +value,
 | 
						|
				    m = feat.vid_option ? 4094 : num_vlans - 1;
 | 
						|
 | 
						|
				if (isNaN(v) || v < min_vid || v > m)
 | 
						|
					return _('Invalid VLAN ID given! Only IDs between %d and %d are allowed.').format(min_vid, m);
 | 
						|
 | 
						|
				var sections = this.section.cfgsections();
 | 
						|
 | 
						|
				for (var i = 0; i < sections.length; i++) {
 | 
						|
					if (sections[i] == section_id)
 | 
						|
						continue;
 | 
						|
 | 
						|
					if (this.formvalue(sections[i]) == v)
 | 
						|
						return _('Invalid VLAN ID given! Only unique IDs are allowed');
 | 
						|
				}
 | 
						|
 | 
						|
				return true;
 | 
						|
			};
 | 
						|
 | 
						|
			o.write = function(section_id, value) {
 | 
						|
				var topology = this.section.topology,
 | 
						|
				    values = [];
 | 
						|
 | 
						|
				for (var i = 0; i < port_opts.length; i++) {
 | 
						|
					var tagging = port_opts[i].formvalue(section_id),
 | 
						|
					    portspec = Array.isArray(topology.ports) ? topology.ports[i] : null;
 | 
						|
 | 
						|
					if (tagging == 't')
 | 
						|
						values.push(port_opts[i].option + tagging);
 | 
						|
					else if (tagging == 'u')
 | 
						|
						values.push(port_opts[i].option);
 | 
						|
 | 
						|
					if (portspec && portspec.device) {
 | 
						|
						var old_tag = port_opts[i].cfgvalue(section_id),
 | 
						|
						    old_vid = this.cfgvalue(section_id);
 | 
						|
 | 
						|
						if (old_tag != tagging || old_vid != value) {
 | 
						|
							var old_ifname = portspec.device + (old_tag != 'u' ? '.' + old_vid : ''),
 | 
						|
							    new_ifname = portspec.device + (tagging != 'u' ? '.' + value : '');
 | 
						|
 | 
						|
							if (old_ifname != new_ifname)
 | 
						|
								update_interfaces(old_ifname, new_ifname);
 | 
						|
						}
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				if (feat.vlan4k_option)
 | 
						|
					uci.set('network', sid, feat.vlan4k_option, '1');
 | 
						|
 | 
						|
				uci.set('network', section_id, 'ports', values.join(' '));
 | 
						|
 | 
						|
				return form.Value.prototype.write.apply(this, [section_id, value]);
 | 
						|
			};
 | 
						|
 | 
						|
			o.cfgvalue = function(section_id) {
 | 
						|
				var value = feat.vid_option ? uci.get('network', section_id, feat.vid_option) : null;
 | 
						|
				return (value || uci.get('network', section_id, 'vlan'));
 | 
						|
			};
 | 
						|
 | 
						|
			for (var j = 0; Array.isArray(topology.ports) && j < topology.ports.length; j++) {
 | 
						|
				var portspec = topology.ports[j],
 | 
						|
				    portstate = Array.isArray(topology.portstate) ? topology.portstate[portspec.num] : null;
 | 
						|
 | 
						|
				o = s.option(form.ListValue, String(portspec.num), portspec.label);
 | 
						|
				o.value('', _('off'));
 | 
						|
 | 
						|
				if (!portspec.need_tag)
 | 
						|
					o.value('u', _('untagged'));
 | 
						|
 | 
						|
				o.value('t', _('tagged'));
 | 
						|
 | 
						|
				o.cfgvalue = parse_portvalue;
 | 
						|
				o.validate = validate_portvalue;
 | 
						|
				o.write    = function() {};
 | 
						|
 | 
						|
				o.description = render_port_status(E('small', {
 | 
						|
					'data-switch': switch_name,
 | 
						|
					'data-port': portspec.num
 | 
						|
				}), portstate);
 | 
						|
 | 
						|
				port_opts.push(o);
 | 
						|
			}
 | 
						|
 | 
						|
			port_opts.sort(function(a, b) {
 | 
						|
				return a.option < b.option;
 | 
						|
			});
 | 
						|
		}
 | 
						|
 | 
						|
		L.Poll.add(L.bind(update_port_status, m, topologies));
 | 
						|
 | 
						|
		return m.render();
 | 
						|
	}
 | 
						|
});
 |