Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
934cfbab46 |
Generated
+8
-8
@@ -2836,7 +2836,7 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
|
||||
|
||||
[[package]]
|
||||
name = "openpxe"
|
||||
version = "0.7.2"
|
||||
version = "0.7.3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -2858,7 +2858,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "openpxe-core"
|
||||
version = "0.7.2"
|
||||
version = "0.7.3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
@@ -2885,7 +2885,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "openpxe-dhcp-proxy"
|
||||
version = "0.7.2"
|
||||
version = "0.7.3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -2902,7 +2902,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "openpxe-http-api"
|
||||
version = "0.7.2"
|
||||
version = "0.7.3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -2938,7 +2938,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "openpxe-ipxe-assets"
|
||||
version = "0.7.2"
|
||||
version = "0.7.3"
|
||||
dependencies = [
|
||||
"openpxe-core",
|
||||
"rust-embed",
|
||||
@@ -2948,7 +2948,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "openpxe-iso-store"
|
||||
version = "0.7.2"
|
||||
version = "0.7.3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bcrypt",
|
||||
@@ -2977,7 +2977,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "openpxe-tftp"
|
||||
version = "0.7.2"
|
||||
version = "0.7.3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -2991,7 +2991,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "openpxe-webui"
|
||||
version = "0.7.2"
|
||||
version = "0.7.3"
|
||||
|
||||
[[package]]
|
||||
name = "p256"
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.7.2"
|
||||
version = "0.7.3"
|
||||
edition = "2021"
|
||||
rust-version = "1.95"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
@@ -920,6 +920,8 @@ tr.unbootable td:first-child { border-left: 3px solid var(--warn); }
|
||||
the same 16px below its form. */
|
||||
.card .body > label.field:has(+ button) { margin-bottom: 0; }
|
||||
|
||||
/* v0.7.2: inline list filter (Available images / Unattended files). */
|
||||
.list-search { padding: 14px 16px 0; }
|
||||
.list-search input { width: 100%; }
|
||||
/* v0.7.2: inline list filter above a table (Available images). The input
|
||||
is wrapped in a label.field so it borrows the standard text-field chrome
|
||||
and matches every other input in the app; this wrapper just insets it
|
||||
from the card edges so it lines up with the header text above. */
|
||||
.list-search { padding: 14px 16px; }
|
||||
|
||||
+35
-38
@@ -439,17 +439,18 @@
|
||||
el('div', {class:'v'}, net.server_ip || '?'),
|
||||
el('div', {class:'k'}, 'NIC name'),
|
||||
el('div', {class:'v'}, net.nic_name || '(auto-detect failed)'),
|
||||
// v0.7.2: physical link details (operstate · speed · duplex ·
|
||||
// port MAC) so the operator can confirm WHICH port answers
|
||||
// PXE in multi-NIC / trunked environments.
|
||||
el('div', {class:'k'}, 'Link'),
|
||||
el('div', {class:'v'}, net.nic_link || '—'),
|
||||
el('div', {class:'k'}, 'Subnet mask'),
|
||||
el('div', {class:'v'}, net.subnet_mask || '?'),
|
||||
el('div', {class:'k'}, 'Gateway'),
|
||||
el('div', {class:'v'}, net.gateway || '?'),
|
||||
el('div', {class:'k'}, 'Public base URL'),
|
||||
el('div', {class:'v'}, net.public_base_url),
|
||||
// v0.7.2: physical link details (operstate · speed · duplex ·
|
||||
// port MAC) so the operator can confirm WHICH port answers PXE
|
||||
// in multi-NIC / trunked environments. Kept last — the joined
|
||||
// value runs long, so it wraps cleanly at the bottom of the list.
|
||||
el('div', {class:'k'}, 'Link'),
|
||||
el('div', {class:'v'}, net.nic_link || '—'),
|
||||
]),
|
||||
el('p', {class:'msg'},
|
||||
'Server IP, NIC, mask, and gateway are auto-detected at startup. ' +
|
||||
@@ -896,7 +897,7 @@
|
||||
// v0.7.2: client-side filter over the image table. Rows travel in
|
||||
// (row, password-editor) pairs; filtering hides both, and an open
|
||||
// editor stays closed for filtered-out rows.
|
||||
const isoSearch = el('input', {type:'search', placeholder:'Filter images… (name, family, category, source)',
|
||||
const isoSearch = el('input', {type:'search', placeholder:'Filter images by name, type, or source',
|
||||
spellcheck:'false', oninput: () => {
|
||||
const q = isoSearch.value.trim().toLowerCase();
|
||||
for (let k = 0; k + 1 < rowsAndEditors.length; k += 2) {
|
||||
@@ -1277,14 +1278,14 @@
|
||||
unattDrop, unattFile, unattMsg,
|
||||
// v0.7.2: filter for big answer-file libraries.
|
||||
unattendedFiles.length > 1 ? (() => {
|
||||
const search = el('input', {type:'search', placeholder:'Filter files… (name, kind)',
|
||||
spellcheck:'false', style:'margin-top:14px', oninput: () => {
|
||||
const search = el('input', {type:'search', placeholder:'Filter files by name or kind',
|
||||
spellcheck:'false', oninput: () => {
|
||||
const q = search.value.trim().toLowerCase();
|
||||
unattRows.forEach(r => {
|
||||
r.style.display = (!q || (r.dataset.search || '').includes(q)) ? '' : 'none';
|
||||
});
|
||||
}});
|
||||
return search;
|
||||
return el('label', {class:'field', style:'margin-top:14px;margin-bottom:0'}, search);
|
||||
})() : null,
|
||||
el('div', {style:'margin-top:16px;display:grid;gap:8px'}, unattRows),
|
||||
el('p', {class:'msg', style:'margin-top:14px'},
|
||||
@@ -1332,7 +1333,9 @@
|
||||
el('h2', {}, 'Available images'),
|
||||
el('span', {class:'sub'}, isos.length + ' image' + (isos.length === 1 ? '' : 's')),
|
||||
]),
|
||||
isos.length > 1 ? el('div', {class:'list-search'}, isoSearch) : null,
|
||||
isos.length > 1
|
||||
? el('div', {class:'list-search'}, el('label', {class:'field', style:'margin-bottom:0'}, isoSearch))
|
||||
: null,
|
||||
isoTable,
|
||||
]),
|
||||
]), unattendedAdvanced]);
|
||||
@@ -1358,7 +1361,7 @@
|
||||
{id: '_tools_menu', title: '↳ Tools menu (built-in)'},
|
||||
];
|
||||
|
||||
const macInput = el('input', {type:'text', placeholder:'aa:bb:cc:dd:ee:ff or prefix aa:bb:cc', spellcheck:'false'});
|
||||
const macInput = el('input', {type:'text', placeholder:'aa:bb:cc:dd:ee:ff or aa:bb:cc', spellcheck:'false'});
|
||||
const labelInput = el('input', {type:'text', placeholder:'optional, e.g. "rack-3 spine"'});
|
||||
// v0.7.2: the former separate "Boot rules" card folded into this
|
||||
// form. A full MAC with no architecture saves a per-host pin
|
||||
@@ -1476,38 +1479,32 @@
|
||||
el('div', {class:'card'}, [
|
||||
el('header', {}, el('h2', {}, 'Pin MAC to boot target')),
|
||||
el('div', {class:'body'}, [
|
||||
el('div', {class:'form-row cols-3'}, [
|
||||
el('label', {class:'field'}, [
|
||||
el('span', {class:'name'}, 'MAC address or prefix'),
|
||||
macInput,
|
||||
el('span', {class:'hint'}, 'Full MAC pins one machine; a prefix (OUI) makes a group rule.'),
|
||||
]),
|
||||
// v0.7.3: MAC · Label · Architecture · Boot binary share one
|
||||
// 4-up row so the controls line up across the page; the
|
||||
// per-field guidance that used to sit under them moved into the
|
||||
// note below to keep the inputs flush. Target spans full width
|
||||
// on its own line beneath them.
|
||||
el('div', {class:'form-row'}, [
|
||||
el('label', {class:'field'}, [el('span', {class:'name'}, 'MAC address or prefix'), macInput]),
|
||||
el('label', {class:'field'}, [el('span', {class:'name'}, 'Label (optional)'), labelInput]),
|
||||
el('label', {class:'field'}, [
|
||||
el('span', {class:'name'}, 'Architecture (optional)'),
|
||||
archSel,
|
||||
el('span', {class:'hint'}, 'Selecting one makes a group rule for that firmware.'),
|
||||
]),
|
||||
el('label', {class:'field', style:'grid-column:1 / -1'}, [
|
||||
el('span', {class:'name'}, 'Target'),
|
||||
targetSel,
|
||||
el('span', {class:'hint'},
|
||||
'Built-in shortcuts skip the menu entirely. Per-ISO entries chain straight to the boot script.'),
|
||||
]),
|
||||
el('label', {class:'field'}, [
|
||||
el('span', {class:'name'}, 'Boot binary (optional)'),
|
||||
binSel,
|
||||
el('span', {class:'hint'}, 'Pin Secure Boot racks to "shim" — zero failed boot cycles.'),
|
||||
]),
|
||||
el('label', {class:'field'}, [el('span', {class:'name'}, 'Architecture (optional)'), archSel]),
|
||||
el('label', {class:'field'}, [el('span', {class:'name'}, 'Boot binary (optional)'), binSel]),
|
||||
]),
|
||||
el('label', {class:'field', style:'margin-top:14px'}, [
|
||||
el('span', {class:'name'}, 'Target'),
|
||||
targetSel,
|
||||
]),
|
||||
el('div', {style:'margin-top:16px'}, profileFields.wrap),
|
||||
upsertBtn, msg,
|
||||
el('p', {class:'msg', style:'margin-top:14px'},
|
||||
'When a matching client requests boot.ipxe, OpenPXE short-circuits ' +
|
||||
'past the interactive menu and chains directly. Decision order: ' +
|
||||
'exact MAC pin → first matching group rule → menu. ' +
|
||||
'If an unattended file is selected on a pin, the matching kernel ' +
|
||||
'argument is injected and the hostname/IP are templated into the answer file.'),
|
||||
'A full MAC pins one machine; a MAC prefix (OUI) or an architecture ' +
|
||||
'saves a first-match group rule. Pin the boot binary to “shim” for ' +
|
||||
'Secure Boot racks — zero failed boot cycles. When a matching client ' +
|
||||
'requests boot.ipxe, OpenPXE short-circuits past the interactive menu ' +
|
||||
'and chains directly; decision order is exact MAC pin → first matching ' +
|
||||
'group rule → menu. If an unattended file is selected on a pin, the ' +
|
||||
'matching kernel argument is injected and the hostname/IP are templated ' +
|
||||
'into the answer file.'),
|
||||
]),
|
||||
]),
|
||||
el('div', {class:'card'}, [
|
||||
|
||||
Reference in New Issue
Block a user