Moving the mgmt switch into the bottom section so it applies when

the user clicks apply on the specific vlan. In order for this to
work when the user is simultaneously changing the vlan that their
machine is connected through and the mgmt vlan id, we need to be
able to send a batch of commands through to the switch atomically
so the switch can process all of them and the momentary disconnection
of the machine doesn't matter
This commit is contained in:
Rob Emery committed 2026-10-25 09:06:30 +00:00
1 parent f94204e56f
commit a77d219baf
2 files changed
+20 -55

No files matched your search

+17 -55
View File
@@ -46,7 +46,7 @@ st_rxb:"RX bad",st_details:"Details",st_counters:"MIB counters",st_nonzero:"non-
st_autoref:"auto-refresh",st_counter:"Counter",st_value:"Value",st_fail:"failed to load counters", st_autoref:"auto-refresh",st_counter:"Counter",st_value:"Value",st_fail:"failed to load counters",
v_title:"VLANs",v_vid:"VLAN ID",v_members:"Members",v_tagged:"Tagged",v_untagged:"Untagged",v_pvid_on:"PVID on", v_title:"VLANs",v_vid:"VLAN ID",v_members:"Members",v_tagged:"Tagged",v_untagged:"Untagged",v_pvid_on:"PVID on",
v_empty:"No VLANs configured.",v_mgmt:"Management VLAN",v_mgmt_none:"untagged",v_editor:"VLAN editor", v_empty:"No VLANs configured.",v_mgmt:"Management VLAN",v_mgmt_none:"untagged",v_editor:"VLAN editor",
v_optional:"optional",v_setmgmt_q:"Set {n} as the management ID?",v_setmgmt_t:"Make this the management VLAN", v_optional:"optional",v_setmgmt_q:"Set {n} as the management ID?",v_setmgmt_t:"Management VLAN",
v_member:"Member",v_pvid:"PVID",v_add:"Add a new VLAN", v_mgmt_c:"WARNING: You must be able to access this VLAN to continue to manage the switch.", v_member:"Member",v_pvid:"PVID",v_add:"Add a new VLAN", v_mgmt_c:"WARNING: You must be able to access this VLAN to continue to manage the switch.",
v_legend:"U = untagged member, T = tagged member, - = not a member. PVID is automatically assigned based on the untagged membership.", v_legend:"U = untagged member, T = tagged member, - = not a member. PVID is automatically assigned based on the untagged membership.",
v_accept:"Accept",v_ing_all:"All",v_del_t:"Delete VLAN",v_rename:"Rename",v_rename_t:"Rename VLAN", v_accept:"Accept",v_ing_all:"All",v_del_t:"Delete VLAN",v_rename:"Rename",v_rename_t:"Rename VLAN",
@@ -840,7 +840,6 @@ function postCmds(list){
list.forEach(function(c){p=p.then(function(){return postCmd(c,true)})}); list.forEach(function(c){p=p.then(function(){return postCmd(c,true)})});
return p.then(function(){toast(t("t_cmds",{n:list.length}),"ok")}); return p.then(function(){toast(t("t_cmds",{n:list.length}),"ok")});
} }
function toast(msg,cls){ function toast(msg,cls){
var el=h("div",{class:"toast "+(cls||""),text:msg}); var el=h("div",{class:"toast "+(cls||""),text:msg});
$("toasts").appendChild(el); $("toasts").appendChild(el);
@@ -1533,24 +1532,9 @@ function vlanRefresh() {
title: t("v_del_t"), title: t("v_del_t"),
style: "margin-left: 5px;", style: "margin-left: 5px;",
onclick: function() { onclick: function() {
confirmModal(t("v_del_q", { confirmModal(t("v_del_q", { n: v.id }), t("v_del_d"), function() {
n: v.id
}), t("v_del_d"), function() {
postCmd("vlan " + v.id + " d").then(vlanRefresh).catch(function() {}); postCmd("vlan " + v.id + " d").then(vlanRefresh).catch(function() {});
}); });
},
}));
act.appendChild(h("button", {
class: "ctl",
text: "\u2605",
style: "margin-left: 5px;",
title: t("v_setmgmt_t"),
onclick: function() {
confirmModal(t("v_setmgmt_q", {
n: v.id
}), t("v_mgmt_c"), function() {
postCmd("vlan " + v.id + " mgmt").then(vlanRefresh);
});
} }
})); }));
} }
@@ -1571,27 +1555,20 @@ function buildVlanEdit() {
rP.insertCell().textContent = t("v_pvid"); rP.insertCell().textContent = t("v_pvid");
for (var p = 1; p <= S.n; p++)(function(p) { for (var p = 1; p <= S.n; p++)(function(p) {
hd.insertCell().innerHTML = '<b>' + p + '</b>'; hd.insertCell().innerHTML = '<b>' + p + '</b>';
var seg = h("span", { var seg = h("span", { class: "seg", id: "vm" + p });
class: "seg",
id: "vm" + p
});
["-", "U", "T"].forEach(function(s, ix) { ["-", "U", "T"].forEach(function(s, ix) {
seg.appendChild(h("button", { seg.appendChild(h("button", {
text: s, text: s,
"data-v": ix, "data-v": ix,
onclick: function() { onclick: function() {
seg.querySelectorAll("button").forEach(function(b) { seg.querySelectorAll("button").forEach(function(b) { b.classList.remove("on") });
b.classList.remove("on")
});
this.classList.add("on"); this.classList.add("on");
} }
})); }));
}); });
seg.children[0].classList.add("on"); seg.children[0].classList.add("on");
rM.insertCell().appendChild(seg); rM.insertCell().appendChild(seg);
rP.insertCell().appendChild(h("span", { rP.insertCell().appendChild(h("span", { id: "vp" + p }));
id: "vp" + p
}));
})(p); })(p);
} }
@@ -1600,9 +1577,7 @@ function segVal(p) {
} }
function segSet(p, v) { function segSet(p, v) {
$("vm" + p).querySelectorAll("button").forEach(function(b, i) { $("vm" + p).querySelectorAll("button").forEach(function(b, i) { b.classList.toggle("on", i === v) });
b.classList.toggle("on", i === v)
});
} }
function vlanLoad(vid) { function vlanLoad(vid) {
@@ -1623,14 +1598,9 @@ function vlanLoad(vid) {
segSet(p, (mem >> bit) & 1 ? (((unt >> bit) & 1) ? 1 : 2) : 0); segSet(p, (mem >> bit) & 1 ? (((unt >> bit) & 1) ? 1 : 2) : 0);
$("vp" + p).innerText = (!!((pv >> bit) & 1))?"Y":"N"; $("vp" + p).innerText = (!!((pv >> bit) & 1))?"Y":"N";
} }
toast(t("v_loaded", { $("vmgmtpromote").checked = ($("vmgmtcur").dataset.id == vid);
n: vid toast(t("v_loaded", {n: vid}), "ok");
}), "ok"); }).catch(function() {toast(t("v_notfound", {n: vid}), "err")});
}).catch(function() {
toast(t("v_notfound", {
n: vid
}), "err")
});
} }
function vlan2str(i, n, t, u){ function vlan2str(i, n, t, u){
@@ -1650,11 +1620,7 @@ function vlanApply() {
toast(t("v_vid_err"), "err"); toast(t("v_vid_err"), "err");
return; return;
} }
var top_cmds = [], var top_cmds = [], tail_cmds = [], vlansPerPort=[], thisVlanInStructure={id:vid, tagged:[], untagged:[]};
tail_cmds = [],
thisVlanInStructure={id:vid, tagged:[], untagged:[]},
vlansPerPort=[];
for (var p = 1; p <= S.n; p++) { for (var p = 1; p <= S.n; p++) {
var v = segVal(p); var v = segVal(p);
vlansPerPort[p]={t:[],u:[]}; vlansPerPort[p]={t:[],u:[]};
@@ -1676,9 +1642,7 @@ function vlanApply() {
rowTagged = mySplit(rowData.t); rowTagged = mySplit(rowData.t);
if(rowData.id == vid) continue; if(rowData.id == vid) continue;
for(const newVlanPort of thisVlanInStructure.untagged) { for(const newVlanPort of thisVlanInStructure.untagged) {
if(rowUntagged.includes(""+newVlanPort)){ if(rowUntagged.includes(""+newVlanPort)) rowUntagged.splice(rowUntagged.indexOf(""+newVlanPort),1);
rowUntagged.splice(rowUntagged.indexOf(""+newVlanPort),1);
}
} }
for(const rut of rowUntagged) { for(const rut of rowUntagged) {
vlansPerPort[rut].u.push(rowData.id); vlansPerPort[rut].u.push(rowData.id);
@@ -1690,21 +1654,19 @@ function vlanApply() {
} }
tail_cmds.push(vlan2str(vid, vname, thisVlanInStructure.tagged, thisVlanInStructure.untagged)); tail_cmds.push(vlan2str(vid, vname, thisVlanInStructure.tagged, thisVlanInStructure.untagged));
//park all ports with only tagged membership for (p in vlansPerPort){/*park all ports with only tagged membership*/
for (p in vlansPerPort){
if (vlansPerPort[p].u.length == 0 && vlansPerPort[p].t.length > 0) { if (vlansPerPort[p].u.length == 0 && vlansPerPort[p].t.length > 0) {
tail_cmds.push(pvidcmd(0, p)); tail_cmds.push(pvidcmd(0, p));
} }
} }
for(const up of thisVlanInStructure.untagged){ for(const up of thisVlanInStructure.untagged){
tail_cmds.push(pvidcmd(vid,up)); tail_cmds.push(pvidcmd(vid,up));
} }
if($("vmgmtpromote").checked){/*must be the last command*/
var cmds = top_cmds.concat(tail_cmds); tail_cmds.push("vlan " + vid + " mgmt");
console.log(cmds); }
var cmd = top_cmds.concat(tail_cmds);
postCmds(cmds).then(vlanRefresh).catch(function() {}).then(function() { postCmd(cmd.join("\n"),true).then(function(){toast(t("t_cmds",{n:cmd.length}),"ok")}).then(vlanRefresh).catch(function() {}).then(function() {
vlanLoad(vid) vlanLoad(vid)
}); });
} }
+3
View File
@@ -294,6 +294,9 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
<button class="ctl pri" id="vlan_add" data-i18n="v_add">Add a new VLAN</button> <button class="ctl pri" id="vlan_add" data-i18n="v_add">Add a new VLAN</button>
</div> </div>
<div class="card"><h2><span data-i18n="v_editor">VLAN editor</span> <span class="hint" id="vtitle"></span></h2> <div class="card"><h2><span data-i18n="v_editor">VLAN editor</span> <span class="hint" id="vtitle"></span></h2>
<div><label class="row"><span class="lb" style="width:150px;" data-i18n="v_setmgmt_t"></span>
<span class="switch"><input type="checkbox" id="vmgmtpromote"><i></i></span>
</label></div>
<div class="scrollx"><table class="t" id="vedit"><tbody></tbody></table></div> <div class="scrollx"><table class="t" id="vedit"><tbody></tbody></table></div>
<div style="margin-top:10px"><button class="ctl pri" id="vapply" data-i18n="c_apply">Apply</button></div> <div style="margin-top:10px"><button class="ctl pri" id="vapply" data-i18n="c_apply">Apply</button></div>
<p class="small mut" style="margin-top:6px" data-i18n="v_legend">U = untagged member, T = tagged member, - = not a member. PVID assigns this VLAN to untagged ingress traffic.</p> <p class="small mut" style="margin-top:6px" data-i18n="v_legend">U = untagged member, T = tagged member, - = not a member. PVID assigns this VLAN to untagged ingress traffic.</p>