2020-03-27 09:27:18 +00:00
'use strict' ;
2020-04-09 12:39:09 +00:00
'require view' ;
'require dom' ;
'require poll' ;
2020-03-27 09:27:18 +00:00
'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 || ! portstate . link )
2020-04-09 12:39:09 +00:00
dom . content ( node , [
2020-03-27 09:27:18 +00:00
E ( 'img' , { src : L . resource ( 'icons/port_down.png' ) } ) ,
E ( 'br' ) ,
_ ( 'no link' )
] ) ;
else
2020-04-09 12:39:09 +00:00
dom . content ( node , [
2020-03-27 09:27:18 +00:00
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 : [ ] }
} ) ;
2020-04-09 12:39:09 +00:00
return view . extend ( {
2020-03-27 09:27:18 +00:00
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 ) ) ;
topologies [ 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 ;
2023-11-27 15:37:15 +00:00
if ( feat . vlan _option ) {
o = s . option ( form . Flag , feat . vlan _option , _ ( 'Enable VLAN functionality' ) ) ;
o . rmempty = false ;
}
2020-03-27 09:27:18 +00:00
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' ) ;
2023-11-27 15:37:15 +00:00
return ( device == this . device ) ;
2020-03-27 09:27:18 +00:00
} ;
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 ++ ) {
2023-11-27 15:37:15 +00:00
if ( sections [ j ] . device != this . device )
2020-03-27 09:27:18 +00:00
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 ;
}
2023-11-27 15:37:15 +00:00
uci . set ( 'network' , section _id , 'device' , this . device ) ;
2020-03-27 09:27:18 +00:00
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 ) ;
} ;
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 ;
} ;
2023-11-27 15:37:15 +00:00
var port _opts = o . port _opts = [ ] ;
2020-03-27 09:27:18 +00:00
o . write = function ( section _id , value ) {
var topology = this . section . topology ,
values = [ ] ;
2023-11-27 15:37:15 +00:00
for ( var i = 0 ; i < this . port _opts . length ; i ++ ) {
var tagging = this . port _opts [ i ] . formvalue ( section _id ) ,
2020-03-27 09:27:18 +00:00
portspec = Array . isArray ( topology . ports ) ? topology . ports [ i ] : null ;
if ( tagging == 't' )
2023-11-27 15:37:15 +00:00
values . push ( this . port _opts [ i ] . option + tagging ) ;
2020-03-27 09:27:18 +00:00
else if ( tagging == 'u' )
2023-11-27 15:37:15 +00:00
values . push ( this . port _opts [ i ] . option ) ;
2020-03-27 09:27:18 +00:00
if ( portspec && portspec . device ) {
2023-11-27 15:37:15 +00:00
var old _tag = this . port _opts [ i ] . cfgvalue ( section _id ) ,
2020-03-27 09:27:18 +00:00
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' ) ) ;
} ;
s . option ( form . Value , 'description' , _ ( 'Description' ) ) ;
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 ;
} ) ;
}
2020-04-09 12:39:09 +00:00
poll . add ( L . bind ( update _port _status , m , topologies ) ) ;
2020-03-27 09:27:18 +00:00
return m . render ( ) ;
}
} ) ;