mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-24 04:33:17 +00:00
Name vSphere, not Proxmox, as the owner of vSphere VM records (#2560)
A vCenter alarm on a vSphere VM opened a Monitoring menu (Alerts ›
Overview › More › Monitoring) saying "Proxmox remains the inventory
owner", and the vSphere VM drawer's Manage tab said "Proxmox owns this
inventory record … without deleting it from Proxmox".
`describeResourceInventoryOwnership` checked the resource-type list
(`vm`, `system-container`, …) before the platform, so every VM mapped to
Proxmox whatever platform the caller passed. It also matched only the
bare `proxmox` / `vmware` families, while the resource drawers pass
canonical keys (`proxmox-pve`, `vmware-vsphere`, `truenas`).
An explicit platform now decides the owner (proxmox and proxmox-\*,
vmware and vmware-\*, kubernetes, docker, truenas, agent). The
resource-type guess is only a fallback when the platform is empty or
unrecognised, so `('vm', undefined)` still maps to Proxmox.
Verified on a mock stack with a vSphere VM alarm injected into the live
websocket snapshot, at 1440 and 390, and on a real mock vSphere VM
drawer, against main's helper in the same session. Receipt:
`frontend-modern/browser-verification/b1c21df0e17ba92c.json`.
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"version": 1,
|
||||
"base_sha": "25f4131720dad07540378f4d82aa9c33b291e75d",
|
||||
"verified_at": "2026-10-06T09:55:29Z",
|
||||
"result": "passed",
|
||||
"changed_paths": [
|
||||
"frontend-modern/src/utils/resourceMonitoringPolicy.ts"
|
||||
],
|
||||
"content_sha256": {
|
||||
"frontend-modern/src/utils/resourceMonitoringPolicy.ts": "5471b694198b0477b2736b752a20792c03f23dfd177d98000636c115924612a6"
|
||||
},
|
||||
"routes": [
|
||||
"/alerts/overview",
|
||||
"/vmware/overview"
|
||||
],
|
||||
"viewports": [
|
||||
{
|
||||
"width": 1440,
|
||||
"height": 900
|
||||
},
|
||||
{
|
||||
"width": 390,
|
||||
"height": 844
|
||||
}
|
||||
],
|
||||
"states": [
|
||||
"Alerts > Overview on a private mock stack built from main, with one vSphere VM alarm (finance-jump-01, metadata resourceType vm, incidentProvider vmware, incidentCode vmware_alarm_state, message 'VM vm-205 has VMware alarm ...') appended to the live websocket activeAlerts snapshots, because the mock demo scenario clears VM alarms",
|
||||
"Card More > Monitoring menu with the change: header reads 'VMware vSphere remains the inventory owner' and Retire from monitoring reads 'VMware vSphere keeps the inventory record', at 1440 and 390",
|
||||
"Same menu with main's helper swapped back in under Vite in the same session: both lines read Proxmox at 1440 and 390",
|
||||
"vSphere overview, real mock VM finance-jump-01 (platformType vmware-vsphere, no injection), drawer Manage tab: retirement help reads 'vSphere owns this inventory record ... does not delete anything from vCenter'; with main's helper it read 'Proxmox owns this inventory record ... without deleting it from Proxmox'",
|
||||
"Menu at 390 opens as a body portal below the card and stays inside the viewport"
|
||||
],
|
||||
"interactions": [
|
||||
"Proxied the real websocket with Playwright routeWebSocket and appended the vSphere VM alarm to each activeAlerts array; also appended it to the /api/alerts/active REST recovery response",
|
||||
"Clicked More, then Monitoring, on the injected alarm card and read the menu header and the Retire from monitoring description",
|
||||
"Opened the finance-jump-01 drawer from the vSphere overview and switched to Manage",
|
||||
"Swapped resourceMonitoringPolicy.ts to main's version under the running Vite for a same-session before, then restored the change and re-ran"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { describeResourceInventoryOwnership } from '@/utils/resourceMonitoringPolicy';
|
||||
|
||||
const ownerOf = (resourceType?: string, platformType?: string) =>
|
||||
describeResourceInventoryOwnership(resourceType, platformType).ownerLabel;
|
||||
|
||||
describe('describeResourceInventoryOwnership', () => {
|
||||
it('names vSphere as the owner of a VMware VM instead of guessing Proxmox from the type', () => {
|
||||
const ownership = describeResourceInventoryOwnership('vm', 'vmware');
|
||||
|
||||
expect(ownership.ownerLabel).toBe('VMware vSphere');
|
||||
expect(ownership.retirementDescription).toContain('vCenter');
|
||||
expect(ownership.retirementDescription).not.toContain('Proxmox');
|
||||
});
|
||||
|
||||
it('keeps Proxmox as the owner of a VM when no platform is known', () => {
|
||||
expect(ownerOf('vm', undefined)).toBe('Proxmox');
|
||||
expect(ownerOf('vm', '')).toBe('Proxmox');
|
||||
});
|
||||
|
||||
it('lets an explicit platform outrank the resource-type guess', () => {
|
||||
expect(ownerOf('vm', 'vmware-vsphere')).toBe('VMware vSphere');
|
||||
expect(ownerOf('vm', 'truenas')).toBe('TrueNAS');
|
||||
expect(ownerOf('vm', 'kubernetes')).toBe('Kubernetes');
|
||||
expect(ownerOf('system-container', 'docker')).toBe('container runtime');
|
||||
expect(ownerOf('vm', 'agent')).toBe('Pulse agent');
|
||||
});
|
||||
|
||||
it('accepts canonical platform keys as well as platform families', () => {
|
||||
expect(ownerOf('node', 'proxmox')).toBe('Proxmox');
|
||||
expect(ownerOf('vm', 'proxmox-pve')).toBe('Proxmox');
|
||||
expect(ownerOf('pbs', 'proxmox-pbs')).toBe('Proxmox');
|
||||
expect(ownerOf('pmg', 'proxmox-pmg')).toBe('Proxmox');
|
||||
expect(ownerOf('datastore', 'vmware-vsphere')).toBe('VMware vSphere');
|
||||
});
|
||||
|
||||
it('falls back to the resource type when the platform is unknown or unset', () => {
|
||||
expect(ownerOf('pod', 'generic')).toBe('Kubernetes');
|
||||
expect(ownerOf('k8s-node')).toBe('Kubernetes');
|
||||
expect(ownerOf('app-container')).toBe('container runtime');
|
||||
expect(ownerOf('docker-host')).toBe('container runtime');
|
||||
expect(ownerOf('agent')).toBe('Pulse agent');
|
||||
expect(ownerOf('storage', 'unknown')).toBe('source system');
|
||||
});
|
||||
|
||||
it('reports only the Pulse agent as not provider-owned', () => {
|
||||
expect(describeResourceInventoryOwnership('agent').providerOwned).toBe(false);
|
||||
expect(describeResourceInventoryOwnership('vm', 'vmware').providerOwned).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -4,60 +4,86 @@ export interface ResourceInventoryOwnership {
|
||||
retirementDescription: string;
|
||||
}
|
||||
|
||||
const PROXMOX_OWNERSHIP: ResourceInventoryOwnership = {
|
||||
ownerLabel: 'Proxmox',
|
||||
providerOwned: true,
|
||||
retirementDescription:
|
||||
'Proxmox owns this inventory record. Retiring it stops Pulse attention and automation without deleting it from Proxmox. Restore active monitoring here at any time.',
|
||||
};
|
||||
|
||||
const KUBERNETES_OWNERSHIP: ResourceInventoryOwnership = {
|
||||
ownerLabel: 'Kubernetes',
|
||||
providerOwned: true,
|
||||
retirementDescription:
|
||||
'Kubernetes owns this object. Retiring it stops Pulse attention and automation without deleting the object from the cluster.',
|
||||
};
|
||||
|
||||
const VMWARE_OWNERSHIP: ResourceInventoryOwnership = {
|
||||
ownerLabel: 'VMware vSphere',
|
||||
providerOwned: true,
|
||||
retirementDescription:
|
||||
'vSphere owns this inventory record. Retiring it changes Pulse monitoring only and does not delete anything from vCenter.',
|
||||
};
|
||||
|
||||
const TRUENAS_OWNERSHIP: ResourceInventoryOwnership = {
|
||||
ownerLabel: 'TrueNAS',
|
||||
providerOwned: true,
|
||||
retirementDescription:
|
||||
'TrueNAS owns this inventory record. Retiring it changes Pulse monitoring only and does not delete anything from TrueNAS.',
|
||||
};
|
||||
|
||||
const CONTAINER_RUNTIME_OWNERSHIP: ResourceInventoryOwnership = {
|
||||
ownerLabel: 'container runtime',
|
||||
providerOwned: true,
|
||||
retirementDescription:
|
||||
'The container runtime owns this inventory record. Retiring it changes Pulse monitoring only and does not remove the runtime object.',
|
||||
};
|
||||
|
||||
const AGENT_OWNERSHIP: ResourceInventoryOwnership = {
|
||||
ownerLabel: 'Pulse agent',
|
||||
providerOwned: false,
|
||||
retirementDescription:
|
||||
'Retiring this machine stops Pulse attention and automation while keeping its history. Agent removal remains available from Machines.',
|
||||
};
|
||||
|
||||
const SOURCE_SYSTEM_OWNERSHIP: ResourceInventoryOwnership = {
|
||||
ownerLabel: 'source system',
|
||||
providerOwned: true,
|
||||
retirementDescription:
|
||||
'The source system owns this inventory record. Retiring it changes Pulse monitoring only and preserves the resource history.',
|
||||
};
|
||||
|
||||
// Callers pass either a platform family ('proxmox', 'vmware') or a canonical
|
||||
// platform key ('proxmox-pve', 'vmware-vsphere'); both name the same owner.
|
||||
function ownershipForPlatform(platform: string): ResourceInventoryOwnership | undefined {
|
||||
if (platform === 'proxmox' || platform.startsWith('proxmox-')) return PROXMOX_OWNERSHIP;
|
||||
if (platform === 'vmware' || platform.startsWith('vmware-')) return VMWARE_OWNERSHIP;
|
||||
if (platform === 'kubernetes') return KUBERNETES_OWNERSHIP;
|
||||
if (platform === 'docker') return CONTAINER_RUNTIME_OWNERSHIP;
|
||||
if (platform === 'truenas') return TRUENAS_OWNERSHIP;
|
||||
if (platform === 'agent') return AGENT_OWNERSHIP;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Fallback for callers that know only the resource type. Resource types such
|
||||
// as 'vm' are shared across platforms, so this guess must never outrank an
|
||||
// explicit platform.
|
||||
function ownershipForResourceType(type: string): ResourceInventoryOwnership {
|
||||
if (['vm', 'system-container', 'oci-container', 'pbs', 'pmg'].includes(type)) {
|
||||
return PROXMOX_OWNERSHIP;
|
||||
}
|
||||
if (type.startsWith('k8s-') || type === 'pod') return KUBERNETES_OWNERSHIP;
|
||||
if (type.startsWith('docker-') || type === 'app-container') return CONTAINER_RUNTIME_OWNERSHIP;
|
||||
if (type === 'agent') return AGENT_OWNERSHIP;
|
||||
return SOURCE_SYSTEM_OWNERSHIP;
|
||||
}
|
||||
|
||||
export function describeResourceInventoryOwnership(
|
||||
resourceType?: string,
|
||||
platformType?: string,
|
||||
): ResourceInventoryOwnership {
|
||||
const type = (resourceType || '').toLowerCase();
|
||||
const platform = (platformType || '').toLowerCase();
|
||||
const platform = (platformType || '').trim().toLowerCase();
|
||||
|
||||
if (
|
||||
platform === 'proxmox' ||
|
||||
['vm', 'system-container', 'oci-container', 'pbs', 'pmg'].includes(type)
|
||||
) {
|
||||
return {
|
||||
ownerLabel: 'Proxmox',
|
||||
providerOwned: true,
|
||||
retirementDescription:
|
||||
'Proxmox owns this inventory record. Retiring it stops Pulse attention and automation without deleting it from Proxmox. Restore active monitoring here at any time.',
|
||||
};
|
||||
}
|
||||
if (platform === 'kubernetes' || type.startsWith('k8s-') || type === 'pod') {
|
||||
return {
|
||||
ownerLabel: 'Kubernetes',
|
||||
providerOwned: true,
|
||||
retirementDescription:
|
||||
'Kubernetes owns this object. Retiring it stops Pulse attention and automation without deleting the object from the cluster.',
|
||||
};
|
||||
}
|
||||
if (platform === 'vmware') {
|
||||
return {
|
||||
ownerLabel: 'VMware vSphere',
|
||||
providerOwned: true,
|
||||
retirementDescription:
|
||||
'vSphere owns this inventory record. Retiring it changes Pulse monitoring only and does not delete anything from vCenter.',
|
||||
};
|
||||
}
|
||||
if (platform === 'docker' || type.startsWith('docker-') || type === 'app-container') {
|
||||
return {
|
||||
ownerLabel: 'container runtime',
|
||||
providerOwned: true,
|
||||
retirementDescription:
|
||||
'The container runtime owns this inventory record. Retiring it changes Pulse monitoring only and does not remove the runtime object.',
|
||||
};
|
||||
}
|
||||
if (type === 'agent' || platform === 'agent') {
|
||||
return {
|
||||
ownerLabel: 'Pulse agent',
|
||||
providerOwned: false,
|
||||
retirementDescription:
|
||||
'Retiring this machine stops Pulse attention and automation while keeping its history. Agent removal remains available from Machines.',
|
||||
};
|
||||
}
|
||||
return {
|
||||
ownerLabel: 'source system',
|
||||
providerOwned: true,
|
||||
retirementDescription:
|
||||
'The source system owns this inventory record. Retiring it changes Pulse monitoring only and preserves the resource history.',
|
||||
};
|
||||
return ownershipForPlatform(platform) ?? ownershipForResourceType(type);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user