Add iDRAC Console wall, power control, and proxy embeds.

Operators can power nodes via OME jobs and view multiple live iDRAC
consoles in-panel (4/8 wall) through a same-origin reverse proxy;
Present decks updated for the new remote-ops story.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
root
2026-07-19 00:16:19 +02:00
parent 795707e47b
commit 03e207b46d
9 changed files with 2066 additions and 31 deletions
+581 -1
View File
@@ -2100,7 +2100,7 @@ a.conn-link:hover {
background: rgba(0,20,40,0.35);
border-radius: 8px;
overflow: auto;
max-height: min(56vh, 520px);
max-height: min(70vh, 680px);
}
.kpi-list { padding: 0.35rem; }
.kpi-detail { padding: 0.75rem; }
@@ -2136,6 +2136,83 @@ a.conn-link:hover {
.kpi-detail h3 { margin: 0 0 0.35rem; color: var(--dell-bright); font-size: 1rem; }
.kpi-detail .meta { margin: 0 0 0.65rem; color: var(--muted); font-family: var(--mono); font-size: 0.72rem; }
.kpi-actions { display: flex; flex-wrap: wrap; gap: 0.35rem; margin: 0.65rem 0; }
.power-actions {
display: flex;
flex-wrap: wrap;
gap: 0.35rem;
width: 100%;
margin: 0.15rem 0 0.25rem;
}
.btn.power-on {
background: linear-gradient(135deg, #1a7a3c, #2db85a);
border-color: #2db85a;
color: #fff;
}
.btn.power-on:hover { filter: brightness(1.08); }
.btn.power-off {
background: linear-gradient(135deg, #8a1f1f, #c43a3a);
border-color: #c43a3a;
color: #fff;
}
.btn.power-off:hover { filter: brightness(1.08); }
.btn.idrac-console,
button.btn.idrac-console {
background: linear-gradient(135deg, #c45a12, #e87a2a) !important;
border-color: #e87a2a !important;
color: #fff !important;
font-weight: 600;
text-decoration: none !important;
}
.btn.idrac-console:hover {
filter: brightness(1.08);
color: #fff !important;
text-decoration: none !important;
}
.btn.idrac-console:disabled {
opacity: 0.45;
filter: none;
}
.connect-tile.idrac-console {
border-color: rgba(232, 122, 42, 0.55);
background: rgba(196, 90, 18, 0.18);
}
.connect-tile.idrac-console .ct-ico,
.connect-tile.idrac-console .ct-t { color: #ffb06a; }
.connect-tile.danger .ct-ico { color: #e86a6a; }
.connect-tile.danger:hover {
border-color: rgba(200, 70, 70, 0.55);
box-shadow: 0 0 0 1px rgba(200, 70, 70, 0.25);
}
/* iDRAC console embed modal */
#idrac-console-modal .idrac-console-card {
width: min(1120px, 96vw);
max-height: 94vh;
display: flex;
flex-direction: column;
padding-bottom: 0.85rem;
}
.idrac-console-toolbar {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
margin: 0.35rem 0 0.5rem;
}
.idrac-console-frame-wrap {
flex: 1;
min-height: min(68vh, 720px);
border: 1px solid var(--border);
border-radius: 8px;
overflow: hidden;
background: #0a0e14;
}
#idrac-console-frame {
width: 100%;
height: min(68vh, 720px);
border: 0;
background: #0a0e14;
}
.kpi-kv { display: grid; gap: 0.3rem; margin-top: 0.5rem; }
.kpi-kv .row {
display: grid;
@@ -8232,3 +8309,506 @@ html[data-theme="light"] .chip.active {
background: linear-gradient(135deg, #ef4444, #b91c1c) !important;
color: #fff !important;
}
/* ===== Console · iDRAC wall ===== */
.btn.console-btn {
background: linear-gradient(135deg, rgba(196, 90, 18, 0.35), rgba(232, 122, 42, 0.55));
border-color: rgba(232, 122, 42, 0.75);
color: #ffe8d2;
font-weight: 600;
}
.btn.console-btn:hover { filter: brightness(1.08); color: #fff; }
.console-drawer.drawer,
.drawer.console-drawer {
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100vw !important;
max-width: 100vw !important;
height: 100vh !important;
max-height: 100vh !important;
border-left: none;
border-right: none;
transform: translateY(110%);
overflow: hidden;
box-sizing: border-box;
display: flex;
flex-direction: column;
background:
radial-gradient(ellipse 60% 40% at 8% 0%, rgba(232, 122, 42, 0.18), transparent 55%),
radial-gradient(ellipse 50% 45% at 92% 10%, rgba(0, 168, 232, 0.16), transparent 50%),
radial-gradient(ellipse 40% 35% at 50% 100%, rgba(61, 255, 160, 0.08), transparent 45%),
#050b12;
z-index: 80;
}
.console-drawer.drawer.open { transform: translateY(0); }
.console-drawer.console-ready .console-tile {
animation: consoleTileIn 0.55s cubic-bezier(0.22, 1, 0.36, 1) both;
}
.console-drawer.console-ready .console-tile:nth-child(1) { animation-delay: 0.04s; }
.console-drawer.console-ready .console-tile:nth-child(2) { animation-delay: 0.1s; }
.console-drawer.console-ready .console-tile:nth-child(3) { animation-delay: 0.16s; }
.console-drawer.console-ready .console-tile:nth-child(4) { animation-delay: 0.22s; }
@keyframes consoleTileIn {
from { opacity: 0; transform: translateY(18px) scale(0.97); }
to { opacity: 1; transform: none; }
}
.console-aurora {
pointer-events: none;
position: absolute;
inset: 0;
background:
linear-gradient(120deg, transparent 30%, rgba(255, 154, 60, 0.06) 50%, transparent 70%),
linear-gradient(300deg, transparent 40%, rgba(0, 168, 232, 0.05) 55%, transparent 75%);
background-size: 200% 200%;
animation: consoleAurora 14s ease-in-out infinite;
opacity: 0.9;
}
@keyframes consoleAurora {
0%, 100% { background-position: 0% 40%, 100% 60%; }
50% { background-position: 100% 60%, 0% 40%; }
}
.console-head {
position: relative;
z-index: 2;
flex-wrap: wrap;
gap: 0.75rem;
align-items: flex-start;
border-bottom: 1px solid rgba(255, 154, 60, 0.22);
padding: 0.75rem 1rem;
}
.console-title {
display: block;
font-weight: 700;
font-size: 1.05rem;
letter-spacing: 0.02em;
color: #ffb06a;
}
.console-sub {
margin: 0.1rem 0 0;
font-size: 0.72rem;
color: #9eb4c8;
}
.console-head-actions {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.45rem;
margin-left: auto;
}
.console-stats {
display: flex;
flex-wrap: wrap;
gap: 0.35rem;
}
.cs-pill {
font-size: 0.68rem;
font-family: var(--mono);
padding: 0.28rem 0.55rem;
border-radius: 999px;
border: 1px solid rgba(255, 255, 255, 0.12);
background: rgba(0, 0, 0, 0.28);
color: #d7e6f4;
}
.cs-pill strong { color: #fff; margin-right: 0.2rem; }
.cs-pill.live {
border-color: rgba(61, 255, 160, 0.45);
box-shadow: 0 0 12px rgba(61, 255, 160, 0.15);
}
.console-density .chip.active {
background: #e87a2a;
border-color: #e87a2a;
color: #041018;
}
.console-body {
position: relative;
z-index: 2;
flex: 1;
min-height: 0;
display: grid;
grid-template-columns: min(340px, 32vw) 1fr;
gap: 0;
}
.console-fleet {
display: flex;
flex-direction: column;
min-height: 0;
border-right: 1px solid rgba(255, 154, 60, 0.18);
background: rgba(0, 12, 24, 0.45);
}
.console-fleet-tools {
padding: 0.65rem 0.75rem 0.4rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.console-fleet-tools input[type="search"] {
width: 100%;
box-sizing: border-box;
margin-bottom: 0.45rem;
background: rgba(0, 0, 0, 0.35);
border: 1px solid rgba(255, 154, 60, 0.28);
color: #fff;
border-radius: 8px;
padding: 0.45rem 0.6rem;
font-size: 0.8rem;
}
.console-subnet-chips {
display: flex;
flex-wrap: wrap;
gap: 0.3rem;
max-height: 5.5rem;
overflow: auto;
margin-bottom: 0.35rem;
}
.console-subnet-chips .chip {
display: inline-flex;
align-items: center;
gap: 0.3rem;
font-size: 0.66rem;
}
.console-chip-dot,
.console-net-dot {
width: 0.45rem;
height: 0.45rem;
border-radius: 50%;
background: var(--chip, var(--net, #e87a2a));
box-shadow: 0 0 8px var(--chip, var(--net, #e87a2a));
flex-shrink: 0;
}
.console-fleet-list {
flex: 1;
min-height: 0;
overflow: auto;
padding: 0.5rem 0.65rem 1rem;
}
.console-fleet-group {
margin: 0.65rem 0 0.3rem;
padding-left: 0.35rem;
border-left: 3px solid var(--net, #e87a2a);
}
.cfg-label {
font-size: 0.66rem;
color: #c5d6e6;
font-family: var(--mono);
}
.cfg-label code { color: #ffb06a; }
.console-fleet-card {
appearance: none;
width: 100%;
text-align: left;
display: grid;
gap: 0.15rem;
margin-bottom: 0.35rem;
padding: 0.55rem 0.6rem;
border-radius: 10px;
border: 1px solid rgba(255, 255, 255, 0.08);
background: rgba(8, 18, 30, 0.75);
color: inherit;
cursor: grab;
transition: border-color 0.15s, transform 0.15s, box-shadow 0.15s;
}
.console-fleet-card:hover {
border-color: rgba(232, 122, 42, 0.55);
transform: translateY(-1px);
box-shadow: 0 8px 22px rgba(0, 0, 0, 0.35);
}
.console-fleet-card.dragging { opacity: 0.45; }
.console-fleet-card.selected {
border-color: #e87a2a;
box-shadow: 0 0 0 1px rgba(232, 122, 42, 0.35);
}
.console-fleet-card.is-live {
border-color: rgba(61, 255, 160, 0.28);
}
.console-fleet-card.on-wall {
background: linear-gradient(135deg, rgba(232, 122, 42, 0.12), rgba(0, 40, 70, 0.4));
}
.cfc-top {
display: flex;
justify-content: space-between;
gap: 0.4rem;
align-items: baseline;
}
.cfc-top strong {
font-size: 0.78rem;
color: #fff;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.cfc-status {
font-size: 0.58rem;
font-weight: 700;
letter-spacing: 0.06em;
color: #7a8a9a;
}
.console-fleet-card.is-live .cfc-status { color: #3dffa0; }
.cfc-meta, .cfc-ip, .cfc-hint, .cfc-wall {
font-size: 0.66rem;
color: #9eb4c8;
font-family: var(--mono);
}
.cfc-wall { color: #ffb06a; }
.console-stage {
min-height: 0;
overflow: auto;
padding: 0.75rem;
display: grid;
gap: 0.75rem;
align-content: stretch;
}
.console-stage.density-medium {
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
}
.console-stage.density-small {
grid-template-columns: repeat(4, 1fr);
grid-template-rows: 1fr;
}
@media (max-width: 1100px) {
.console-body { grid-template-columns: 1fr; }
.console-fleet { max-height: 38vh; border-right: none; border-bottom: 1px solid rgba(255, 154, 60, 0.18); }
.console-stage.density-small {
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
}
}
.console-tile {
position: relative;
min-height: 0;
display: flex;
flex-direction: column;
border-radius: 14px;
border: 1px solid rgba(255, 255, 255, 0.1);
background: rgba(4, 12, 22, 0.82);
overflow: hidden;
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35);
}
.console-tile.is-filled {
border-color: color-mix(in srgb, var(--net, #e87a2a) 55%, transparent);
box-shadow:
0 0 0 1px color-mix(in srgb, var(--net, #e87a2a) 25%, transparent),
0 14px 36px rgba(0, 0, 0, 0.4);
}
.console-tile.is-live::after {
content: "";
position: absolute;
top: 10px;
right: 10px;
width: 8px;
height: 8px;
border-radius: 50%;
background: #3dffa0;
box-shadow: 0 0 12px #3dffa0;
z-index: 3;
pointer-events: none;
}
.console-tile.drag-over {
outline: 2px dashed #e87a2a;
outline-offset: -4px;
background: rgba(232, 122, 42, 0.08);
}
.console-tile.dragging { opacity: 0.5; }
.console-tile.is-empty .console-dropzone {
flex: 1;
display: grid;
place-content: center;
text-align: center;
gap: 0.25rem;
padding: 1.25rem;
background:
repeating-linear-gradient(
-45deg,
transparent,
transparent 10px,
rgba(255, 154, 60, 0.03) 10px,
rgba(255, 154, 60, 0.03) 20px
);
}
.cd-kicker {
font-size: 0.65rem;
letter-spacing: 0.14em;
text-transform: uppercase;
color: #e87a2a;
}
.console-dropzone strong {
font-size: 1rem;
color: #f2f7fc;
}
.console-dropzone p {
margin: 0;
font-size: 0.72rem;
color: #8aa0b4;
max-width: 22rem;
}
.console-tile-chrome {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 0.5rem;
padding: 0.45rem 0.55rem 0.25rem;
background: linear-gradient(180deg, rgba(0, 0, 0, 0.35), transparent);
cursor: grab;
}
.ctc-id { min-width: 0; display: grid; gap: 0.2rem; }
.ctc-slot {
font-size: 0.58rem;
letter-spacing: 0.08em;
color: #8aa0b4;
font-family: var(--mono);
}
.ctc-name {
font-size: 0.82rem;
color: #fff;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.console-net-badge {
display: inline-flex;
align-items: center;
gap: 0.35rem;
font-size: 0.62rem;
font-family: var(--mono);
padding: 0.15rem 0.45rem;
border-radius: 999px;
border: 1px solid color-mix(in srgb, var(--net, #e87a2a) 50%, transparent);
background: color-mix(in srgb, var(--net, #e87a2a) 14%, transparent);
color: #e8f0f8;
width: fit-content;
max-width: 100%;
}
.console-net-badge em {
font-style: normal;
font-weight: 700;
letter-spacing: 0.04em;
}
.console-net-badge em.on { color: #3dffa0; }
.console-net-badge em.off { color: #ff7a7a; }
.ctc-actions {
display: flex;
flex-wrap: wrap;
gap: 0.25rem;
justify-content: flex-end;
}
.ctc-actions .btn.compact {
padding: 0.2rem 0.45rem;
font-size: 0.68rem;
min-height: 0;
}
.console-tile-meta {
display: flex;
flex-wrap: wrap;
gap: 0.45rem;
padding: 0 0.55rem 0.35rem;
font-size: 0.62rem;
font-family: var(--mono);
color: #9eb4c8;
}
.console-tile-meta .on { color: #3dffa0; }
.console-tile-meta .off { color: #ff8a8a; }
.console-frame-wrap {
flex: 1;
min-height: 140px;
background: #0a0e14;
border-top: 1px solid rgba(255, 255, 255, 0.06);
}
.console-frame-wrap iframe {
width: 100%;
height: 100%;
min-height: 160px;
border: 0;
background: #0a0e14;
}
.console-stage.density-medium .console-frame-wrap { min-height: 220px; }
.console-stage.density-medium .console-frame-wrap iframe { min-height: 240px; }
.console-stage.density-small .ctc-name { font-size: 0.72rem; }
.console-stage.density-small .console-tile-meta { display: none; }
/* Fullscreen popup */
.console-fs-modal .console-fs-card {
width: min(1280px, 98vw);
max-height: 96vh;
display: flex;
flex-direction: column;
padding-bottom: 0.75rem;
}
.console-fs-head { margin-bottom: 0.5rem; }
.console-fs-frame-wrap {
flex: 1;
min-height: min(78vh, 820px);
border-radius: 10px;
overflow: hidden;
border: 1px solid rgba(232, 122, 42, 0.35);
background: #0a0e14;
}
#console-fs-frame {
width: 100%;
height: min(78vh, 820px);
border: 0;
}
html[data-theme="light"] .console-drawer.drawer {
background:
radial-gradient(ellipse 60% 40% at 8% 0%, rgba(232, 122, 42, 0.12), transparent 55%),
radial-gradient(ellipse 50% 45% at 92% 10%, rgba(0, 118, 206, 0.1), transparent 50%),
#f4f7fb;
}
html[data-theme="light"] .console-fleet {
background: rgba(255, 255, 255, 0.72);
}
html[data-theme="light"] .console-fleet-card {
background: #fff;
color: #102033;
}
html[data-theme="light"] .cfc-top strong { color: #102033; }
html[data-theme="light"] .console-tile {
background: #fff;
}
html[data-theme="light"] .ctc-name { color: #102033; }
/* Console wall · 4 / 8 screens + drag shields */
.console-stage.slots-4.density-medium {
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
}
.console-stage.slots-4.density-small {
grid-template-columns: repeat(4, 1fr);
grid-template-rows: 1fr;
}
.console-stage.slots-8.density-medium,
.console-stage.slots-8.density-small {
grid-template-columns: repeat(4, 1fr);
grid-template-rows: 1fr 1fr;
}
.console-stage.slots-8 .console-frame-wrap { min-height: 120px; }
.console-stage.slots-8 .console-frame-wrap iframe { min-height: 140px; }
.console-stage.slots-8.density-medium .console-frame-wrap { min-height: 160px; }
.console-stage.slots-8.density-medium .console-frame-wrap iframe { min-height: 170px; }
.console-frame-wrap { position: relative; }
.console-drop-shield {
display: none;
position: absolute;
inset: 0;
z-index: 2;
background: transparent;
}
.console-drawer.is-dragging .console-drop-shield { display: block; }
.console-drawer.is-dragging .console-frame-wrap iframe { pointer-events: none; }
.cfc-actions { margin-top: 0.25rem; }
.cfc-actions .btn.compact {
padding: 0.2rem 0.5rem;
font-size: 0.66rem;
min-height: 0;
}
.console-fleet-card { cursor: grab; }