Name update
This commit is contained in:
@@ -31,12 +31,17 @@ pub async fn run_command(
|
||||
) -> impl IntoResponse {
|
||||
let line = req.command.trim();
|
||||
if line.is_empty() {
|
||||
return (StatusCode::OK, Json(json!({ "output": HELP_TEXT, "ok": true })));
|
||||
return (
|
||||
StatusCode::OK,
|
||||
Json(json!({ "output": HELP_TEXT, "ok": true })),
|
||||
);
|
||||
}
|
||||
|
||||
// Echo the typed command into the live log so the Terminal tab shows
|
||||
// operator activity in-band with server-emitted log lines.
|
||||
state.log_bus.push("info", "openpxe::terminal", format!("> {line}"));
|
||||
state
|
||||
.log_bus
|
||||
.push("info", "openpxe::terminal", format!("> {line}"));
|
||||
|
||||
let argv = shell_split(line);
|
||||
if argv.is_empty() {
|
||||
@@ -55,7 +60,11 @@ pub async fn run_command(
|
||||
// so reading the live tail tells the same story as scrolling the
|
||||
// terminal pane.
|
||||
let mirror = if output.len() > 1024 {
|
||||
format!("{}\n... ({} bytes truncated)", &output[..1024], output.len() - 1024)
|
||||
format!(
|
||||
"{}\n... ({} bytes truncated)",
|
||||
&output[..1024],
|
||||
output.len() - 1024
|
||||
)
|
||||
} else {
|
||||
output.clone()
|
||||
};
|
||||
@@ -78,7 +87,7 @@ async fn dispatch(state: &AppState, argv: &[String]) -> Result<String, String> {
|
||||
"status" => Ok(status_text(state)),
|
||||
"isos" | "images" => Ok(isos_text(state)),
|
||||
"clients" => Ok(clients_text(state)),
|
||||
"queue" => gate_command(state, tail).await,
|
||||
"queue" => queue_command(state, tail).await,
|
||||
"nfs" => nfs_command(state, tail).await,
|
||||
"smb" => smb_command(state, tail).await,
|
||||
"log" => log_command(state, tail),
|
||||
@@ -96,7 +105,7 @@ async fn dispatch(state: &AppState, argv: &[String]) -> Result<String, String> {
|
||||
fn status_text(s: &AppState) -> String {
|
||||
let isos = s.iso_store.list();
|
||||
let clients = s.clients.list();
|
||||
let gates = s.queue.list();
|
||||
let queue_entries = s.queue.list();
|
||||
let smb = s.smb.as_ref().map(|m| m.snapshot());
|
||||
let nfs = s.nfs.list();
|
||||
let nfs_active = nfs.iter().filter(|m| m.mounted).count();
|
||||
@@ -112,13 +121,23 @@ fn status_text(s: &AppState) -> String {
|
||||
nfs mounts: {n_total} configured ({n_active} active)\n",
|
||||
ver = env!("CARGO_PKG_VERSION"),
|
||||
base = s.public_base_url,
|
||||
nic = if s.nic_name.is_empty() { "?" } else { s.nic_name.as_str() },
|
||||
nic = if s.nic_name.is_empty() {
|
||||
"?"
|
||||
} else {
|
||||
s.nic_name.as_str()
|
||||
},
|
||||
up = uptime_string(s),
|
||||
n_isos = isos.len(),
|
||||
n_local = isos.iter().filter(|i| matches!(i.source, openpxe_iso_store::IsoSource::Local)).count(),
|
||||
n_nfs = isos.iter().filter(|i| !matches!(i.source, openpxe_iso_store::IsoSource::Local)).count(),
|
||||
n_local = isos
|
||||
.iter()
|
||||
.filter(|i| matches!(i.source, openpxe_iso_store::IsoSource::Local))
|
||||
.count(),
|
||||
n_nfs = isos
|
||||
.iter()
|
||||
.filter(|i| !matches!(i.source, openpxe_iso_store::IsoSource::Local))
|
||||
.count(),
|
||||
n_clients = clients.len(),
|
||||
n_entries = gates.len(),
|
||||
n_entries = queue_entries.len(),
|
||||
smb = smb.map_or_else(|| "(disabled)".into(), |s| format!("{s:?}")),
|
||||
n_total = nfs.len(),
|
||||
n_active = nfs_active,
|
||||
@@ -159,11 +178,7 @@ fn clients_text(s: &AppState) -> String {
|
||||
return "(no clients yet)".into();
|
||||
}
|
||||
let mut out = String::new();
|
||||
let _ = writeln!(
|
||||
out,
|
||||
"{:<19} {:<16} {:<8} LAST SEEN",
|
||||
"MAC", "IP", "EVENTS"
|
||||
);
|
||||
let _ = writeln!(out, "{:<19} {:<16} {:<8} LAST SEEN", "MAC", "IP", "EVENTS");
|
||||
for c in clients {
|
||||
let ip = c.last_ip.map_or_else(|| "-".into(), |i| i.to_string());
|
||||
let _ = writeln!(
|
||||
@@ -180,35 +195,35 @@ fn clients_text(s: &AppState) -> String {
|
||||
out
|
||||
}
|
||||
|
||||
// ── gate ───────────────────────────────────────────────────────────────
|
||||
// ── queue ──────────────────────────────────────────────────────────────
|
||||
|
||||
// `async` for symmetry with the other dispatch helpers — gate operations
|
||||
// `async` for symmetry with the other dispatch helpers — queue operations
|
||||
// are sync today but might grow to await on a database in a future phase.
|
||||
#[allow(clippy::unused_async)]
|
||||
async fn gate_command(s: &AppState, args: &[String]) -> Result<String, String> {
|
||||
async fn queue_command(s: &AppState, args: &[String]) -> Result<String, String> {
|
||||
match args.first().map(String::as_str) {
|
||||
None | Some("list") => {
|
||||
let gs = s.queue.list();
|
||||
if gs.is_empty() {
|
||||
return Ok("(no gates)".into());
|
||||
let entries = s.queue.list();
|
||||
if entries.is_empty() {
|
||||
return Ok("(queue empty)".into());
|
||||
}
|
||||
let mut out = String::new();
|
||||
for g in gs {
|
||||
for entry in entries {
|
||||
let _ = writeln!(
|
||||
out,
|
||||
"#{:<3} {:<19} {:<16} target={}",
|
||||
g.position,
|
||||
g.mac,
|
||||
g.id,
|
||||
g.assigned_target.unwrap_or_else(|| "-".into())
|
||||
entry.position,
|
||||
entry.mac,
|
||||
entry.id,
|
||||
entry.assigned_target.unwrap_or_else(|| "-".into())
|
||||
);
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
Some("assign-all") => {
|
||||
let target = args.get(1).ok_or_else(|| {
|
||||
"usage: gate assign-all <iso_boot_entry_id>".to_string()
|
||||
})?;
|
||||
let target = args
|
||||
.get(1)
|
||||
.ok_or_else(|| "usage: queue assign-all <iso_boot_entry_id>".to_string())?;
|
||||
let found = s
|
||||
.iso_store
|
||||
.list()
|
||||
@@ -219,30 +234,32 @@ async fn gate_command(s: &AppState, args: &[String]) -> Result<String, String> {
|
||||
}
|
||||
let ids: Vec<_> = s.queue.list().into_iter().map(|g| g.id).collect();
|
||||
let n = s.queue.assign(&ids, target);
|
||||
Ok(format!("assigned {n} gates -> {target}"))
|
||||
Ok(format!("assigned {n} queue entries -> {target}"))
|
||||
}
|
||||
Some("assign") => {
|
||||
let entry_id = args
|
||||
.get(1)
|
||||
.ok_or_else(|| "usage: gate assign <entry_id> <iso_boot_entry_id>".to_string())?;
|
||||
.ok_or_else(|| "usage: queue assign <entry_id> <iso_boot_entry_id>".to_string())?;
|
||||
let target = args
|
||||
.get(2)
|
||||
.ok_or_else(|| "usage: gate assign <entry_id> <iso_boot_entry_id>".to_string())?;
|
||||
.ok_or_else(|| "usage: queue assign <entry_id> <iso_boot_entry_id>".to_string())?;
|
||||
let n = s.queue.assign(std::slice::from_ref(entry_id), target);
|
||||
if n == 0 {
|
||||
return Err(format!("no such gate: {entry_id}"));
|
||||
return Err(format!("no such queue entry: {entry_id}"));
|
||||
}
|
||||
Ok(format!("assigned 1 gate -> {target}"))
|
||||
Ok(format!("assigned 1 queue entry -> {target}"))
|
||||
}
|
||||
Some("release") => {
|
||||
let entry_id = args.get(1).ok_or_else(|| "usage: gate release <entry_id>".to_string())?;
|
||||
let entry_id = args
|
||||
.get(1)
|
||||
.ok_or_else(|| "usage: queue release <entry_id>".to_string())?;
|
||||
match s.queue.release(entry_id) {
|
||||
Some(_) => Ok(format!("released {entry_id}")),
|
||||
None => Err(format!("no such gate: {entry_id}")),
|
||||
None => Err(format!("no such queue entry: {entry_id}")),
|
||||
}
|
||||
}
|
||||
Some(other) => Err(format!(
|
||||
"unknown gate subcommand: {other}\ntry: gate [list|assign-all|assign|release]"
|
||||
"unknown queue subcommand: {other}\ntry: queue [list|assign-all|assign|release]"
|
||||
)),
|
||||
}
|
||||
}
|
||||
@@ -294,7 +311,9 @@ async fn nfs_command(s: &AppState, args: &[String]) -> Result<String, String> {
|
||||
let version = match args.get(2).map(String::as_str) {
|
||||
Some("v3") => openpxe_iso_store::NfsVersion::V3,
|
||||
Some("v41") | None => openpxe_iso_store::NfsVersion::V41,
|
||||
Some(other) => return Err(format!("unknown nfs version: {other} (expect v3 or v41)")),
|
||||
Some(other) => {
|
||||
return Err(format!("unknown nfs version: {other} (expect v3 or v41)"))
|
||||
}
|
||||
};
|
||||
let read_only = !matches!(args.get(3).map(String::as_str), Some("rw"));
|
||||
let req = openpxe_iso_store::NfsAddRequest {
|
||||
@@ -309,14 +328,18 @@ async fn nfs_command(s: &AppState, args: &[String]) -> Result<String, String> {
|
||||
}
|
||||
}
|
||||
Some("unmount") => {
|
||||
let id = args.get(1).ok_or_else(|| "usage: nfs unmount <id>".to_string())?;
|
||||
let id = args
|
||||
.get(1)
|
||||
.ok_or_else(|| "usage: nfs unmount <id>".to_string())?;
|
||||
match s.nfs.remove(id).await {
|
||||
Ok(()) => Ok(format!("unmounted {id}")),
|
||||
Err(e) => Err(format!("unmount failed: {e}")),
|
||||
}
|
||||
}
|
||||
Some("scan") => {
|
||||
let id = args.get(1).ok_or_else(|| "usage: nfs scan <id>".to_string())?;
|
||||
let id = args
|
||||
.get(1)
|
||||
.ok_or_else(|| "usage: nfs scan <id>".to_string())?;
|
||||
match s.nfs.rescan(id).await {
|
||||
Ok(n) => Ok(format!("re-scanned {id}: {n} isos")),
|
||||
Err(e) => Err(format!("scan failed: {e}")),
|
||||
@@ -368,10 +391,7 @@ fn log_command(s: &AppState, args: &[String]) -> Result<String, String> {
|
||||
Ok("log buffer cleared".into())
|
||||
}
|
||||
Some("tail") => {
|
||||
let n: usize = args
|
||||
.get(1)
|
||||
.and_then(|v| v.parse().ok())
|
||||
.unwrap_or(20);
|
||||
let n: usize = args.get(1).and_then(|v| v.parse().ok()).unwrap_or(20);
|
||||
let lines = s.log_bus.recent();
|
||||
let start = lines.len().saturating_sub(n);
|
||||
let mut out = String::new();
|
||||
@@ -462,10 +482,10 @@ OpenPXE terminal — available commands:
|
||||
|
||||
isos list registered ISOs
|
||||
clients list PXE clients seen this session
|
||||
gate list list gated-deployment queue
|
||||
gate assign <entry_id> <target> assign one gate to a boot entry
|
||||
gate assign-all <target> assign every waiting gate
|
||||
gate release <entry_id> release one gate
|
||||
queue list list queued clients
|
||||
queue assign <entry_id> <target> assign one queued client to a boot entry
|
||||
queue assign-all <target> assign every waiting client
|
||||
queue release <entry_id> release one queued client
|
||||
|
||||
nfs list list NFS mounts
|
||||
nfs mount <s>:<e> [v3|v41] [ro|rw] add and mount an NFS share
|
||||
@@ -521,4 +541,10 @@ mod tests {
|
||||
assert_eq!(truncate("hi", 10), "hi");
|
||||
assert_eq!(truncate("longerthanfive", 5), "long…");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn help_uses_queue_language() {
|
||||
assert!(HELP_TEXT.contains("queue list"));
|
||||
assert!(HELP_TEXT.contains("queued clients"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user