diff --git a/html/app.js b/html/app.js
index 23916b29..0a50b322 100644
--- a/html/app.js
+++ b/html/app.js
@@ -440,10 +440,10 @@ var CONF_CMDS=[
/^isolate\s+\d{1,2}(\s+(off|\d{1,2}))+$/,
/^stp\s+(on|off)$/,/^stp\s+(prio|hello|maxage|fwd|txhold)\s+\d{1,2}$/,
/^stp\s+version\s+(rstp|stp)$/,
- /^stp\s+port\s+\d{1,2}\s+(on|off)$/,/^stp\s+port\s+\d{1,2}\s+edge\s+(on|off|auto)$/,
- /^stp\s+port\s+\d{1,2}\s+cost\s+\d{1,9}$/,/^stp\s+port\s+\d{1,2}\s+prio\s+\d{1,3}$/,
- /^stp\s+port\s+\d{1,2}\s+guard\s+(none|bpdu|root)$/,/^stp\s+port\s+\d{1,2}\s+filter\s+(on|off)$/,
- /^stp\s+port\s+\d{1,2}\s+p2p\s+(auto|on|off)$/,
+ /^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+(on|off)$/,/^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+edge\s+(on|off|auto)$/,
+ /^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+cost\s+\d{1,9}$/,/^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+prio\s+\d{1,3}$/,
+ /^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+guard\s+(none|bpdu|root)$/,/^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+filter\s+(on|off)$/,
+ /^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+p2p\s+(auto|on|off)$/,
/^igmp\s+(on|off)$/,/^mtu\s+\d{1,2}\s+\d+$/,
/^bw\s+(in|out)\s+\d{1,2}\s+\S+$/,
];
@@ -815,7 +815,7 @@ tabHooks.ports={
};
var STP_PF={EN:1,ADMEDGE:2,AUTOEDGE:4,BPDUG:8,ROOTG:16,FILTER:32,OPEREDGE:64,TRIP:128};
-var stpRows=0,stpCur=null;
+var stpSig="",stpCur=null;
function stpSel(id,opts){
var s=h("select",{class:"in",id:id,onchange:stpTouch});
opts.forEach(function(o){s.appendChild(h("option",{value:o[0],text:o[1]}))});
@@ -826,12 +826,16 @@ function fmtBridge(hex){
if(!hex||hex.length<16)return"-";
return parseInt(hex.slice(0,4),16)+" / "+hex.slice(4).replace(/(..)(?=.)/g,"$1:");
}
+function stpKey(pt){return pt.lag?"L"+pt.lag:String(pt.p)}
+function stpName(pt){return pt.lag?"LAG"+pt.lag+" ("+maskToPorts(pt.mbr).join(",")+")":String(pt.p)}
+function stpPre(k){return k.charAt(0)==="L"?"stp lag "+k.slice(1)+" ":"stp port "+k+" "}
function stpBuild(ports){
var tb=$("stpcfg").tBodies[0],st=$("stpstat").tBodies[0];
+ tb.innerHTML="";st.innerHTML="";
ports.forEach(function(pt){
- var p=pt.p;
+ var p=stpKey(pt);
var tr=tb.insertRow();
- tr.insertCell().textContent=p;
+ tr.insertCell().textContent=stpName(pt);
var sw=h("label",{class:"switch"},[h("input",{type:"checkbox",id:"sten"+p,onchange:stpTouch}),h("i")]);
tr.insertCell().appendChild(sw);
tr.insertCell().appendChild(stpSel("sted"+p,[["auto",t("c_auto")],["on",t("c_on")],["off",t("c_offc")]]));
@@ -844,10 +848,9 @@ function stpBuild(ports){
tr.insertCell().appendChild(stpSel("stpp"+p,[["auto",t("c_auto")],["on",t("c_on")],["off",t("c_offc")]]));
tr.insertCell().appendChild(h("button",{class:"ctl",text:t("c_apply"),onclick:function(){stpApplyPort(p)}}));
var sr=st.insertRow();
- sr.insertCell().textContent=p;
+ sr.insertCell().textContent=stpName(pt);
["ro","st","db","dp","dc","oe","op"].forEach(function(k){sr.insertCell().id="st"+k+p});
});
- stpRows=ports.length;
}
function stpPortVals(pt){
var f=pt.f;
@@ -862,8 +865,9 @@ function stpPortVals(pt){
}
function stpLoad(){
return getJSON("/stp.json").then(function(s){
- s.ports.sort(function(a,b){return a.p-b.p});
- if(!stpRows)stpBuild(s.ports);
+ s.ports.sort(function(a,b){return((a.lag||0)-(b.lag||0))||(a.p-b.p)});
+ var sig=s.ports.map(function(pt){return stpKey(pt)+":"+(pt.mbr||0)}).join();
+ if(sig!==stpSig){stpSig=sig;stpBuild(s.ports);}
stpCur=s;
var en=$("stpen");
if(document.activeElement!==en)en.checked=!!s.on;
@@ -885,7 +889,7 @@ function stpLoad(){
$("stpfwd").value=s.fwd;$("stptxhold").value=s.txhold;
}
s.ports.forEach(function(pt){
- var p=pt.p;
+ var p=stpKey(pt);
var trip=(pt.f&STP_PF.TRIP)?" "+badge(t("stp_trip"),"bad"):"";
$("stro"+p).textContent=s.on&&pt.role?t("stp_r"+pt.role):"-";
$("stst"+p).innerHTML=s.on?badge(t("stp_s"+pt.st),pt.st===3?"ok":"")+trip:"-";
@@ -904,12 +908,12 @@ function stpLoad(){
}
function stpApplyPort(p){
var cur=null;
- (stpCur?stpCur.ports:[]).forEach(function(pt){if(pt.p===p)cur=stpPortVals(pt)});
+ (stpCur?stpCur.ports:[]).forEach(function(pt){if(stpKey(pt)===p)cur=stpPortVals(pt)});
var cost=parseInt($("stco"+p).value,10);
if(isNaN(cost)||cost<0||cost>200000000){toast(t("stp_cost_err"),"err");return;}
var w={en:$("sten"+p).checked,edge:$("sted"+p).value,cost:cost,prio:$("stpr"+p).value,
guard:$("stgu"+p).value,filter:$("stfi"+p).value,p2p:$("stpp"+p).value};
- var cmds=[],pre="stp port "+p+" ";
+ var cmds=[],pre=stpPre(p);
if(!cur||w.edge!==cur.edge)cmds.push(pre+"edge "+w.edge);
if(!cur||w.cost!==cur.cost)cmds.push(pre+"cost "+w.cost);
if(!cur||w.prio!==cur.prio)cmds.push(pre+"prio "+w.prio);
@@ -1615,10 +1619,10 @@ var CONF_OVERWRITE=[
/^eee\s+\d{1,2}\b/,/^eee\b/,/^mirror\b/,
/^lag\s+\d\b/,/^laghash\s+\d\b/,/^isolate\s+\d{1,2}\b/,
/^stp\s+(prio|hello|maxage|fwd|txhold|version)\b/,
- /^stp\s+port\s+\d{1,2}\s+(edge|cost|prio|guard|filter|p2p)\b/,
+ /^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+(edge|cost|prio|guard|filter|p2p)\b/,
/^igmp\b/,/^mtu\s+\d{1,2}\b/,
];
-var CONF_TOGGLE=[/^(syslog)\s+(on|off)$/,/^(stp)\s+(on|off)$/,/^(stp\s+port\s+\d{1,2})\s+(on|off)$/];
+var CONF_TOGGLE=[/^(syslog)\s+(on|off)$/,/^(stp)\s+(on|off)$/,/^(stp\s+(port\s+\d{1,2}|lag\s+[1-4]))\s+(on|off)$/];
function mergeConf(base,texts){
var conf=base.slice();
function drop(rx){conf=conf.filter(function(c){return!rx.test(c)})}