mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-23 20:25:56 +00:00
Prune vSphere drawer sections to operator signal
The VIRTUAL HARDWARE section was rendering 20+ rows including capability toggles (CPU/memory hot-add) and boot configuration (EFI legacy boot, boot delay, boot retry, boot order, default setup mode) that operators don't act on from a monitoring surface. The GUEST section dumped Host/Instance/BIOS UUIDs that are API-shaped identifiers, not operator scan candidates. The TOOLS section padded out with install metadata (install type, upgrade policy, auto-update capability, attempt count, reboot components / time) that's the same kind of noise. Trim each section to the rows an operator actually reads: - Hardware: Guest OS, Hardware version, CPU topology, Memory size, Upgrade attention, Upgrade error, Instant clone frozen (when true), Enter setup mode (when true). Capability toggles and default boot config stay in the raw payload. - Guest: Guest OS, Guest hostname, Guest IPs. UUIDs and datastoreUrl drop. - State: Connection state replaces the redundant Power row (power is already on the workload row and SYSTEM card). - Tools: Run state, Version status (only when not current), Version, Guest reboot (only when requested), Last install error. Install metadata stays in the raw payload. Drop the now-unused formatMilliseconds and bootOrderLabel helpers and their ResourceVMwareBootDevice import. Test assertions are updated to match the trimmed shape with comments explaining the operator-signal principle.
This commit is contained in:
+5
-5
@@ -554,13 +554,13 @@ describe('ResourceDetailDrawer runtime and identity cards', () => {
|
||||
expect(within(section).getByText('Pending')).toBeInTheDocument();
|
||||
expect(within(section).getByText('CPU topology')).toBeInTheDocument();
|
||||
expect(within(section).getByText('4 vCPU · 2 cores/socket')).toBeInTheDocument();
|
||||
expect(within(section).getByText('Boot order')).toBeInTheDocument();
|
||||
expect(within(section).getByText('Disk 2000 -> Ethernet 4000')).toBeInTheDocument();
|
||||
// Boot order, install type, and the default-case Version status row no
|
||||
// longer render: they live in the raw API payload and the drawer keeps
|
||||
// hardware/tools sections focused on actionable operator state.
|
||||
expect(within(section).queryByText('Boot order')).toBeNull();
|
||||
expect(within(section).queryByText('Open VM Tools')).toBeNull();
|
||||
expect(within(section).getByText('Run state')).toBeInTheDocument();
|
||||
expect(within(section).getByText('Running')).toBeInTheDocument();
|
||||
expect(within(section).getByText('Version status')).toBeInTheDocument();
|
||||
expect(within(section).getByText('Current')).toBeInTheDocument();
|
||||
expect(within(section).getByText('Open VM Tools')).toBeInTheDocument();
|
||||
expect(within(section).getByText('Guest reboot')).toBeInTheDocument();
|
||||
expect(within(section).getByText('Requested')).toBeInTheDocument();
|
||||
expect(within(section).getByText('Hard disk 1')).toBeInTheDocument();
|
||||
|
||||
+11
-24
@@ -162,43 +162,30 @@ describe('resourceDetailDrawerVmwareModel', () => {
|
||||
(section) => section.id === 'hardware',
|
||||
);
|
||||
|
||||
// Hardware section is deliberately pruned to operator-actionable rows.
|
||||
// Capability toggles (CPU/memory hot-add) and boot configuration (EFI
|
||||
// legacy boot, boot delay, boot retry, boot order, setup mode default)
|
||||
// stay in the raw API payload; the drawer only surfaces them when
|
||||
// they're in an attention state (Upgrade status pending,
|
||||
// Instant clone frozen, Enter setup mode toggled).
|
||||
expect(hardware?.rows).toEqual([
|
||||
{ label: 'Guest OS', value: 'Ubuntu 64' },
|
||||
{ label: 'Hardware version', value: 'VMX 20' },
|
||||
{ label: 'Upgrade status', value: 'Pending', tone: 'warning' },
|
||||
{ label: 'Upgrade policy', value: 'After Clean Shutdown' },
|
||||
{ label: 'Upgrade target', value: 'VMX 21' },
|
||||
{ label: 'Instant clone frozen', value: 'No', tone: 'default' },
|
||||
{ label: 'CPU topology', value: '4 vCPU · 2 cores/socket' },
|
||||
{ label: 'CPU hot-add', value: 'Yes' },
|
||||
{ label: 'CPU hot-remove', value: 'No' },
|
||||
{ label: 'Memory size', value: '8 GB' },
|
||||
{ label: 'Memory hot-add', value: 'Yes' },
|
||||
{ label: 'Memory hot-add increment', value: '256 MB' },
|
||||
{ label: 'Memory hot-add limit', value: '16 GB' },
|
||||
{ label: 'Boot type', value: 'EFI' },
|
||||
{ label: 'EFI legacy boot', value: 'No' },
|
||||
{ label: 'Boot network protocol', value: 'IPv4' },
|
||||
{ label: 'Boot delay', value: '5000 ms' },
|
||||
{ label: 'Boot retry', value: 'Yes' },
|
||||
{ label: 'Boot retry delay', value: '10000 ms' },
|
||||
{ label: 'Enter setup mode', value: 'No', tone: 'default' },
|
||||
{ label: 'Boot order', value: 'Disk 2000 -> Ethernet 4000' },
|
||||
{ label: 'Upgrade status', value: 'Pending', tone: 'warning' },
|
||||
]);
|
||||
|
||||
const tools = buildVMwareDetailSections('vm', vmware).find((section) => section.id === 'tools');
|
||||
|
||||
// Tools section is pruned to operator-actionable rows: Run state,
|
||||
// Version status (only when not current), Version, Guest reboot (only
|
||||
// when requested), and Last install error. Install metadata stays in
|
||||
// the raw payload.
|
||||
expect(tools?.rows).toEqual([
|
||||
{ label: 'Run state', value: 'Running', tone: 'default' },
|
||||
{ label: 'Version status', value: 'Current', tone: 'default' },
|
||||
{ label: 'Version', value: '12.4.0' },
|
||||
{ label: 'Install type', value: 'Open VM Tools' },
|
||||
{ label: 'Upgrade policy', value: 'Manual' },
|
||||
{ label: 'Auto update supported', value: 'Yes' },
|
||||
{ label: 'Install attempts', value: '1' },
|
||||
{ label: 'Guest reboot', value: 'Requested', tone: 'warning' },
|
||||
{ label: 'Reboot components', value: 'drivers', tone: 'warning' },
|
||||
{ label: 'Reboot requested at', value: '2026-03-30 18:20 UTC', tone: 'warning' },
|
||||
]);
|
||||
|
||||
const disks = buildVMwareDetailSections('vm', vmware).find((section) => section.id === 'disks');
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type {
|
||||
ResourceType,
|
||||
ResourceVMwareBootDevice,
|
||||
ResourceVMwareHardware,
|
||||
ResourceVMwareMeta,
|
||||
ResourceVMwareNetworkAdapter,
|
||||
@@ -93,11 +92,6 @@ const formatMiB = (value?: number): string => {
|
||||
return formatCapacityBytes(value * 1024 * 1024).replace(/\.0 ([A-Z]+)/, ' $1');
|
||||
};
|
||||
|
||||
const formatMilliseconds = (value?: number): string => {
|
||||
if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) return '';
|
||||
return `${value} ms`;
|
||||
};
|
||||
|
||||
const countSnapshotTree = (snapshots?: ResourceVMwareSnapshot[]): number =>
|
||||
(snapshots ?? []).reduce(
|
||||
(total, snapshot) => total + 1 + countSnapshotTree(snapshot.children),
|
||||
@@ -216,18 +210,6 @@ const hardwareSummary = (hardware?: ResourceVMwareHardware): string => {
|
||||
return formatEnumLabel(hardware.version);
|
||||
};
|
||||
|
||||
const bootDeviceLabel = (device: ResourceVMwareBootDevice): string => {
|
||||
const type = formatEnumLabel(device.type);
|
||||
const details = [
|
||||
asTrimmedString(device.nic),
|
||||
...(device.disks ?? []).map((disk) => asTrimmedString(disk)),
|
||||
].filter(Boolean);
|
||||
return details.length > 0 ? `${type} ${details.join(', ')}` : type;
|
||||
};
|
||||
|
||||
const bootOrderLabel = (devices?: ResourceVMwareBootDevice[]): string =>
|
||||
(devices ?? []).map(bootDeviceLabel).filter(Boolean).join(' -> ');
|
||||
|
||||
const cpuTopologyLabel = (vmware: ResourceVMwareMeta): string => {
|
||||
const parts = [];
|
||||
if (
|
||||
@@ -250,6 +232,9 @@ const cpuTopologyLabel = (vmware: ResourceVMwareMeta): string => {
|
||||
const hardwareRows = (vmware?: ResourceVMwareMeta): ResourceDetailDrawerVMwareRow[] => {
|
||||
if (!vmware?.hardware) return [];
|
||||
const hardware = vmware.hardware;
|
||||
const upgradeStatus = asTrimmedString(hardware.upgradeStatus).toUpperCase();
|
||||
const upgradeAttention =
|
||||
Boolean(upgradeStatus) && !['NONE', 'OK'].includes(upgradeStatus);
|
||||
return filterNonEmptyRows([
|
||||
{
|
||||
label: 'Guest OS',
|
||||
@@ -259,21 +244,24 @@ const hardwareRows = (vmware?: ResourceVMwareMeta): ResourceDetailDrawerVMwareRo
|
||||
label: 'Hardware version',
|
||||
value: formatEnumLabel(hardware.version),
|
||||
},
|
||||
{
|
||||
label: 'CPU topology',
|
||||
value: cpuTopologyLabel(vmware),
|
||||
},
|
||||
{
|
||||
label: 'Memory size',
|
||||
value: formatMiB(vmware?.memorySizeMib),
|
||||
},
|
||||
// Upgrade and clone status only surface when something is actionable;
|
||||
// when everything is in the default state these rows resolve to empty
|
||||
// strings and filterNonEmptyRows drops them. Capability toggles
|
||||
// (CPU/memory hot-add, boot config) live in the raw API and stay out
|
||||
// of the operator drawer because they don't change minute to minute
|
||||
// and operators don't act on them from Pulse.
|
||||
{
|
||||
label: 'Upgrade status',
|
||||
value: formatEnumLabel(hardware.upgradeStatus),
|
||||
tone: getWarningTone(
|
||||
Boolean(asTrimmedString(hardware.upgradeStatus)) &&
|
||||
!['NONE', 'OK'].includes(asTrimmedString(hardware.upgradeStatus).toUpperCase()),
|
||||
),
|
||||
},
|
||||
{
|
||||
label: 'Upgrade policy',
|
||||
value: formatEnumLabel(hardware.upgradePolicy),
|
||||
},
|
||||
{
|
||||
label: 'Upgrade target',
|
||||
value: formatEnumLabel(hardware.upgradeVersion),
|
||||
value: upgradeAttention ? formatEnumLabel(hardware.upgradeStatus) : '',
|
||||
tone: getWarningTone(upgradeAttention),
|
||||
},
|
||||
{
|
||||
label: 'Upgrade error',
|
||||
@@ -282,70 +270,14 @@ const hardwareRows = (vmware?: ResourceVMwareMeta): ResourceDetailDrawerVMwareRo
|
||||
},
|
||||
{
|
||||
label: 'Instant clone frozen',
|
||||
value: formatBoolLabel(hardware.instantCloneFrozen),
|
||||
value: hardware.instantCloneFrozen === true ? formatBoolLabel(true) : '',
|
||||
tone: getWarningTone(hardware.instantCloneFrozen === true),
|
||||
},
|
||||
{
|
||||
label: 'CPU topology',
|
||||
value: cpuTopologyLabel(vmware),
|
||||
},
|
||||
{
|
||||
label: 'CPU hot-add',
|
||||
value: formatBoolLabel(hardware.cpuHotAddEnabled),
|
||||
},
|
||||
{
|
||||
label: 'CPU hot-remove',
|
||||
value: formatBoolLabel(hardware.cpuHotRemoveEnabled),
|
||||
},
|
||||
{
|
||||
label: 'Memory size',
|
||||
value: formatMiB(vmware?.memorySizeMib),
|
||||
},
|
||||
{
|
||||
label: 'Memory hot-add',
|
||||
value: formatBoolLabel(hardware.memoryHotAddEnabled),
|
||||
},
|
||||
{
|
||||
label: 'Memory hot-add increment',
|
||||
value: formatMiB(hardware.memoryHotAddIncrementMib),
|
||||
},
|
||||
{
|
||||
label: 'Memory hot-add limit',
|
||||
value: formatMiB(hardware.memoryHotAddLimitMib),
|
||||
},
|
||||
{
|
||||
label: 'Boot type',
|
||||
value: formatEnumLabel(hardware.bootType),
|
||||
},
|
||||
{
|
||||
label: 'EFI legacy boot',
|
||||
value: formatBoolLabel(hardware.efiLegacyBoot),
|
||||
},
|
||||
{
|
||||
label: 'Boot network protocol',
|
||||
value: formatEnumLabel(hardware.bootNetworkProtocol),
|
||||
},
|
||||
{
|
||||
label: 'Boot delay',
|
||||
value: formatMilliseconds(hardware.bootDelayMilliseconds),
|
||||
},
|
||||
{
|
||||
label: 'Boot retry',
|
||||
value: formatBoolLabel(hardware.bootRetry),
|
||||
},
|
||||
{
|
||||
label: 'Boot retry delay',
|
||||
value: formatMilliseconds(hardware.bootRetryDelayMilliseconds),
|
||||
},
|
||||
{
|
||||
label: 'Enter setup mode',
|
||||
value: formatBoolLabel(hardware.enterSetupMode),
|
||||
value: hardware.enterSetupMode === true ? formatBoolLabel(true) : '',
|
||||
tone: getWarningTone(hardware.enterSetupMode === true),
|
||||
},
|
||||
{
|
||||
label: 'Boot order',
|
||||
value: bootOrderLabel(hardware.bootDevices),
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
@@ -363,6 +295,14 @@ const toolsSummary = (tools?: ResourceVMwareTools): string => {
|
||||
|
||||
const toolsRows = (tools?: ResourceVMwareTools): ResourceDetailDrawerVMwareRow[] => {
|
||||
if (!tools) return [];
|
||||
const versionStatus = asTrimmedString(tools.versionStatus).toUpperCase();
|
||||
const versionAttention =
|
||||
Boolean(versionStatus) && !['CURRENT', 'OK'].includes(versionStatus);
|
||||
// Drawer surfaces what an operator scans for: is Tools running, is its
|
||||
// version current, has the guest asked for a reboot, did the last install
|
||||
// error. Install metadata (install type, upgrade policy, auto-update
|
||||
// capability, attempt count, reboot components / time) stays in the raw
|
||||
// API; we only surface it when something is actionable.
|
||||
return filterNonEmptyRows([
|
||||
{
|
||||
label: 'Run state',
|
||||
@@ -374,55 +314,18 @@ const toolsRows = (tools?: ResourceVMwareTools): ResourceDetailDrawerVMwareRow[]
|
||||
},
|
||||
{
|
||||
label: 'Version status',
|
||||
value: formatEnumLabel(tools.versionStatus),
|
||||
tone: getWarningTone(
|
||||
Boolean(asTrimmedString(tools.versionStatus)) &&
|
||||
!['CURRENT', 'OK'].includes(asTrimmedString(tools.versionStatus).toUpperCase()),
|
||||
),
|
||||
value: versionAttention ? formatEnumLabel(tools.versionStatus) : '',
|
||||
tone: getWarningTone(versionAttention),
|
||||
},
|
||||
{
|
||||
label: 'Version',
|
||||
value: asTrimmedString(tools.version),
|
||||
},
|
||||
{
|
||||
label: 'Install type',
|
||||
value: formatEnumLabel(tools.installType),
|
||||
},
|
||||
{
|
||||
label: 'Upgrade policy',
|
||||
value: formatEnumLabel(tools.upgradePolicy),
|
||||
},
|
||||
{
|
||||
label: 'Auto update supported',
|
||||
value: formatBoolLabel(tools.autoUpdateSupported),
|
||||
},
|
||||
{
|
||||
label: 'Install attempts',
|
||||
value:
|
||||
typeof tools.installAttemptCount === 'number' && Number.isFinite(tools.installAttemptCount)
|
||||
? String(tools.installAttemptCount)
|
||||
: '',
|
||||
},
|
||||
{
|
||||
label: 'Guest reboot',
|
||||
value:
|
||||
tools.guestRebootRequested === undefined
|
||||
? ''
|
||||
: tools.guestRebootRequested
|
||||
? 'Requested'
|
||||
: 'Not requested',
|
||||
value: tools.guestRebootRequested === true ? 'Requested' : '',
|
||||
tone: getWarningTone(tools.guestRebootRequested === true),
|
||||
},
|
||||
{
|
||||
label: 'Reboot components',
|
||||
value: (tools.guestRebootComponents ?? []).filter(Boolean).join(', '),
|
||||
tone: getWarningTone((tools.guestRebootComponents ?? []).length > 0),
|
||||
},
|
||||
{
|
||||
label: 'Reboot requested at',
|
||||
value: formatSnapshotDate(tools.guestRebootRequestTime),
|
||||
tone: getWarningTone(Boolean(tools.guestRebootRequestTime)),
|
||||
},
|
||||
{
|
||||
label: 'Last install error',
|
||||
value: asTrimmedString(tools.errorMessage),
|
||||
@@ -595,13 +498,13 @@ export const buildVMwareDetailSections = (
|
||||
value: asTrimmedString(vmware.overallStatus),
|
||||
tone: getStatusTone(vmware.overallStatus),
|
||||
},
|
||||
{
|
||||
label: 'Power',
|
||||
value: asTrimmedString(vmware.powerState),
|
||||
},
|
||||
// Power state is already conveyed by the workload row's status dot and
|
||||
// the SYSTEM card. We only resurface Connection state here because it
|
||||
// matters when an ESXi host is disconnected and the row dot alone can't
|
||||
// tell you why.
|
||||
{
|
||||
label: 'Connection state',
|
||||
value: asTrimmedString(vmware.connectionState),
|
||||
value: formatEnumLabel(vmware.connectionState),
|
||||
},
|
||||
{
|
||||
label: 'Datastore type',
|
||||
@@ -662,19 +565,11 @@ export const buildVMwareDetailSections = (
|
||||
},
|
||||
]);
|
||||
|
||||
// UUID fields (host / instance / BIOS) and datastoreUrl are API-shaped
|
||||
// identifiers an operator never types or compares from the drawer; they
|
||||
// are still available in the raw resource payload. Keep guest identity
|
||||
// human-readable.
|
||||
const guestRows = filterNonEmptyRows([
|
||||
{
|
||||
label: 'Host UUID',
|
||||
value: asTrimmedString(vmware.hostUuid),
|
||||
},
|
||||
{
|
||||
label: 'Instance UUID',
|
||||
value: asTrimmedString(vmware.instanceUuid),
|
||||
},
|
||||
{
|
||||
label: 'BIOS UUID',
|
||||
value: asTrimmedString(vmware.biosUuid),
|
||||
},
|
||||
{
|
||||
label: 'Guest OS',
|
||||
value: asTrimmedString(vmware.guestOsFamily),
|
||||
@@ -687,10 +582,6 @@ export const buildVMwareDetailSections = (
|
||||
label: 'Guest IPs',
|
||||
value: (vmware.guestIpAddresses ?? []).filter(Boolean).join(', '),
|
||||
},
|
||||
{
|
||||
label: 'Datastore URL',
|
||||
value: asTrimmedString(vmware.datastoreUrl),
|
||||
},
|
||||
]);
|
||||
|
||||
const networkRows =
|
||||
|
||||
Reference in New Issue
Block a user