mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-07 00:37:36 +00:00
360 lines
14 KiB
Go
360 lines
14 KiB
Go
package vmware
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/unifiedresources"
|
|
)
|
|
|
|
func TestProviderRecords_ProjectCanonicalVMwareResources(t *testing.T) {
|
|
previous := IsFeatureEnabled()
|
|
SetFeatureEnabled(true)
|
|
t.Cleanup(func() { SetFeatureEnabled(previous) })
|
|
|
|
collectedAt := time.Date(2026, time.March, 30, 18, 15, 0, 0, time.UTC)
|
|
provider := NewProvider(InventorySnapshot{
|
|
ConnectionID: "vc-1",
|
|
ConnectionName: "Lab VC",
|
|
VCenterHost: "vc.lab.local",
|
|
CollectedAt: collectedAt,
|
|
Hosts: []InventoryHost{{
|
|
Host: "host-101",
|
|
Name: "esxi-01.lab.local",
|
|
ConnectionState: "CONNECTED",
|
|
PowerState: "POWERED_ON",
|
|
HostUUID: "uuid-host-1",
|
|
DatacenterID: "datacenter-1",
|
|
DatacenterName: "DC1",
|
|
ComputeResourceID: "domain-c101",
|
|
ComputeResourceName: "Prod Compute",
|
|
ClusterID: "domain-c101",
|
|
ClusterName: "Prod Compute",
|
|
FolderID: "group-h4",
|
|
FolderName: "Prod Hosts",
|
|
DatastoreIDs: []string{"datastore-11"},
|
|
DatastoreNames: []string{"nvme-primary"},
|
|
OverallStatus: "yellow",
|
|
Metrics: &InventoryMetrics{
|
|
CPUPercent: float64Ptr(21.4),
|
|
MemoryPercent: float64Ptr(63.2),
|
|
MemoryUsedBytes: int64Ptr(27144105984),
|
|
MemoryTotalBytes: int64Ptr(42949672960),
|
|
NetInBytesPerSecond: float64Ptr(1024),
|
|
NetOutBytesPerSecond: float64Ptr(2048),
|
|
DiskReadBytesPerSecond: float64Ptr(4096),
|
|
DiskWriteBytesPerSecond: float64Ptr(8192),
|
|
},
|
|
TriggeredAlarms: []InventoryAlarm{{
|
|
Alarm: "alarm-11",
|
|
Name: "Host connection degraded",
|
|
OverallStatus: "yellow",
|
|
TriggeredAt: collectedAt.Add(-2 * time.Minute),
|
|
}},
|
|
RecentTasks: []InventoryTask{{
|
|
Task: "task-11",
|
|
Name: "Reconnect host",
|
|
State: "running",
|
|
StartedAt: collectedAt.Add(-1 * time.Minute),
|
|
}},
|
|
}},
|
|
VMs: []InventoryVM{{
|
|
VM: "vm-201",
|
|
Name: "app-01",
|
|
PowerState: "POWERED_ON",
|
|
CPUCount: 4,
|
|
MemorySizeMiB: 8192,
|
|
DatacenterID: "datacenter-1",
|
|
DatacenterName: "DC1",
|
|
ComputeResourceID: "domain-c101",
|
|
ComputeResourceName: "Prod Compute",
|
|
ClusterID: "domain-c101",
|
|
ClusterName: "Prod Compute",
|
|
FolderID: "group-v7",
|
|
FolderName: "Production VMs",
|
|
ResourcePoolID: "resgroup-22",
|
|
ResourcePoolName: "Tier 1",
|
|
RuntimeHostID: "host-101",
|
|
RuntimeHostName: "esxi-01.lab.local",
|
|
DatastoreIDs: []string{"datastore-11"},
|
|
DatastoreNames: []string{"nvme-primary"},
|
|
InstanceUUID: "vm-instance-201",
|
|
BIOSUUID: "vm-bios-201",
|
|
GuestOSFamily: "LINUX",
|
|
GuestHostname: "app-01.internal",
|
|
GuestIPAddresses: []string{"10.0.0.21"},
|
|
OverallStatus: "green",
|
|
Metrics: &InventoryMetrics{
|
|
CPUPercent: float64Ptr(38.1),
|
|
MemoryPercent: float64Ptr(57.5),
|
|
MemoryUsedBytes: int64Ptr(5033164800),
|
|
MemoryTotalBytes: int64Ptr(8589934592),
|
|
NetInBytesPerSecond: float64Ptr(512),
|
|
NetOutBytesPerSecond: float64Ptr(768),
|
|
DiskReadBytesPerSecond: float64Ptr(1536),
|
|
DiskWriteBytesPerSecond: float64Ptr(2048),
|
|
},
|
|
TriggeredAlarms: []InventoryAlarm{{
|
|
Alarm: "alarm-21",
|
|
Name: "VM replication fault",
|
|
OverallStatus: "red",
|
|
TriggeredAt: collectedAt.Add(-3 * time.Minute),
|
|
}},
|
|
RecentTasks: []InventoryTask{{
|
|
Task: "task-21",
|
|
Name: "Create snapshot",
|
|
State: "success",
|
|
StartedAt: collectedAt.Add(-5 * time.Minute),
|
|
}},
|
|
SnapshotCount: 2,
|
|
}},
|
|
Datastores: []InventoryDatastore{{
|
|
Datastore: "datastore-11",
|
|
Name: "nvme-primary",
|
|
Type: "VMFS",
|
|
FreeSpace: 40,
|
|
Capacity: 100,
|
|
DatacenterID: "datacenter-1",
|
|
DatacenterName: "DC1",
|
|
FolderID: "group-s4",
|
|
FolderName: "Shared Datastores",
|
|
HostIDs: []string{"host-101"},
|
|
HostNames: []string{"esxi-01.lab.local"},
|
|
VMIDs: []string{"vm-201"},
|
|
VMNames: []string{"app-01"},
|
|
Accessible: boolPtr(true),
|
|
MultipleHostAccess: boolPtr(true),
|
|
MaintenanceMode: "normal",
|
|
URL: "ds:///vmfs/volumes/datastore-11/",
|
|
OverallStatus: "yellow",
|
|
RecentTasks: []InventoryTask{{
|
|
Task: "task-31",
|
|
Name: "Refresh datastore",
|
|
State: "queued",
|
|
StartedAt: collectedAt.Add(-4 * time.Minute),
|
|
}},
|
|
}},
|
|
})
|
|
|
|
records := provider.Records()
|
|
if len(records) != 3 {
|
|
t.Fatalf("expected 3 VMware records, got %d", len(records))
|
|
}
|
|
|
|
hostRecord := records[0]
|
|
if hostRecord.SourceID != "vc-1:host:host-101" {
|
|
t.Fatalf("host source id = %q, want vc-1:host:host-101", hostRecord.SourceID)
|
|
}
|
|
if hostRecord.Resource.Type != unifiedresources.ResourceTypeAgent {
|
|
t.Fatalf("host resource type = %q, want %q", hostRecord.Resource.Type, unifiedresources.ResourceTypeAgent)
|
|
}
|
|
if hostRecord.Resource.Status != unifiedresources.StatusWarning {
|
|
t.Fatalf("host status = %q, want %q", hostRecord.Resource.Status, unifiedresources.StatusWarning)
|
|
}
|
|
if hostRecord.Resource.VMware == nil || hostRecord.Resource.VMware.ManagedObjectID != "host-101" {
|
|
t.Fatalf("expected VMware host metadata, got %+v", hostRecord.Resource.VMware)
|
|
}
|
|
if got := hostRecord.Resource.VMware.ActiveAlarmCount; got != 1 {
|
|
t.Fatalf("host active alarm count = %d, want 1", got)
|
|
}
|
|
if got := hostRecord.Resource.VMware.RecentTaskSummary; got != "Reconnect host (running)" {
|
|
t.Fatalf("host recent task summary = %q, want %q", got, "Reconnect host (running)")
|
|
}
|
|
if got := hostRecord.Resource.VMware.ClusterName; got != "Prod Compute" {
|
|
t.Fatalf("host cluster name = %q, want Prod Compute", got)
|
|
}
|
|
if got := hostRecord.Resource.VMware.DatastoreNames; len(got) != 1 || got[0] != "nvme-primary" {
|
|
t.Fatalf("host datastore names = %#v, want [nvme-primary]", got)
|
|
}
|
|
if hostRecord.Resource.Metrics == nil || hostRecord.Resource.Metrics.CPU == nil || hostRecord.Resource.Metrics.Memory == nil {
|
|
t.Fatalf("expected VMware host metrics, got %+v", hostRecord.Resource.Metrics)
|
|
}
|
|
if got := hostRecord.Resource.Metrics.CPU.Percent; got != 21.4 {
|
|
t.Fatalf("host cpu percent = %v, want 21.4", got)
|
|
}
|
|
if got := hostRecord.Resource.Metrics.NetOut.Value; got != 2048 {
|
|
t.Fatalf("host net out = %v, want 2048", got)
|
|
}
|
|
if len(hostRecord.Resource.Incidents) != 1 || hostRecord.Resource.Incidents[0].Code != "vmware_alarm_state" {
|
|
t.Fatalf("expected host VMware incident projection, got %+v", hostRecord.Resource.Incidents)
|
|
}
|
|
if hostRecord.Identity.DMIUUID != "uuid-host-1" {
|
|
t.Fatalf("host identity DMI UUID = %q, want uuid-host-1", hostRecord.Identity.DMIUUID)
|
|
}
|
|
if got := hostRecord.Identity.ClusterName; got != "Prod Compute" {
|
|
t.Fatalf("host cluster name = %q, want Prod Compute", got)
|
|
}
|
|
if hostRecord.Resource.LastSeen != collectedAt {
|
|
t.Fatalf("host last seen = %v, want %v", hostRecord.Resource.LastSeen, collectedAt)
|
|
}
|
|
|
|
vmRecord := records[1]
|
|
if vmRecord.SourceID != "vc-1:vm:vm-201" {
|
|
t.Fatalf("vm source id = %q, want vc-1:vm:vm-201", vmRecord.SourceID)
|
|
}
|
|
if vmRecord.Resource.Type != unifiedresources.ResourceTypeVM {
|
|
t.Fatalf("vm resource type = %q, want %q", vmRecord.Resource.Type, unifiedresources.ResourceTypeVM)
|
|
}
|
|
if vmRecord.Resource.VMware == nil || vmRecord.Resource.VMware.CPUCount != 4 {
|
|
t.Fatalf("expected VMware VM metadata with cpu count, got %+v", vmRecord.Resource.VMware)
|
|
}
|
|
if vmRecord.Resource.Status != unifiedresources.StatusWarning {
|
|
t.Fatalf("vm status = %q, want %q", vmRecord.Resource.Status, unifiedresources.StatusWarning)
|
|
}
|
|
if got := vmRecord.Resource.VMware.ActiveAlarmSummary; got != "VM replication fault (red)" {
|
|
t.Fatalf("vm active alarm summary = %q, want %q", got, "VM replication fault (red)")
|
|
}
|
|
if got := vmRecord.Resource.VMware.SnapshotCount; got != 2 {
|
|
t.Fatalf("vm snapshot count = %d, want 2", got)
|
|
}
|
|
if got := vmRecord.Resource.ParentName; got != "esxi-01.lab.local" {
|
|
t.Fatalf("vm parent name = %q, want esxi-01.lab.local", got)
|
|
}
|
|
if got := vmRecord.ParentSourceID; got != "vc-1:host:host-101" {
|
|
t.Fatalf("vm parent source id = %q, want vc-1:host:host-101", got)
|
|
}
|
|
if got := vmRecord.Resource.VMware.RuntimeHostName; got != "esxi-01.lab.local" {
|
|
t.Fatalf("vm runtime host name = %q, want esxi-01.lab.local", got)
|
|
}
|
|
if got := vmRecord.Resource.VMware.InstanceUUID; got != "vm-instance-201" {
|
|
t.Fatalf("vm instance uuid = %q, want vm-instance-201", got)
|
|
}
|
|
if got := vmRecord.Identity.MachineID; got != "vm-instance-201" {
|
|
t.Fatalf("vm machine id = %q, want vm-instance-201", got)
|
|
}
|
|
if got := vmRecord.Identity.IPAddresses; len(got) != 1 || got[0] != "10.0.0.21" {
|
|
t.Fatalf("vm identity IPs = %#v, want [10.0.0.21]", got)
|
|
}
|
|
if vmRecord.Resource.Metrics == nil || vmRecord.Resource.Metrics.CPU == nil || vmRecord.Resource.Metrics.Memory == nil {
|
|
t.Fatalf("expected VMware VM metrics, got %+v", vmRecord.Resource.Metrics)
|
|
}
|
|
if got := vmRecord.Resource.Metrics.Memory.Percent; got != 57.5 {
|
|
t.Fatalf("vm memory percent = %v, want 57.5", got)
|
|
}
|
|
if got := vmRecord.Resource.Metrics.DiskWrite.Value; got != 2048 {
|
|
t.Fatalf("vm disk write = %v, want 2048", got)
|
|
}
|
|
if len(vmRecord.Resource.Incidents) != 1 || vmRecord.Resource.Incidents[0].Code != "vmware_alarm_state" {
|
|
t.Fatalf("expected VMware VM incident projection, got %+v", vmRecord.Resource.Incidents)
|
|
}
|
|
|
|
datastoreRecord := records[2]
|
|
if datastoreRecord.SourceID != "vc-1:datastore:datastore-11" {
|
|
t.Fatalf("datastore source id = %q, want vc-1:datastore:datastore-11", datastoreRecord.SourceID)
|
|
}
|
|
if datastoreRecord.Resource.Type != unifiedresources.ResourceTypeStorage {
|
|
t.Fatalf("datastore resource type = %q, want %q", datastoreRecord.Resource.Type, unifiedresources.ResourceTypeStorage)
|
|
}
|
|
if datastoreRecord.Resource.Storage == nil || datastoreRecord.Resource.Storage.Platform != "vmware-vsphere" {
|
|
t.Fatalf("expected canonical VMware storage metadata, got %+v", datastoreRecord.Resource.Storage)
|
|
}
|
|
if datastoreRecord.Resource.Status != unifiedresources.StatusWarning {
|
|
t.Fatalf("datastore status = %q, want %q", datastoreRecord.Resource.Status, unifiedresources.StatusWarning)
|
|
}
|
|
if datastoreRecord.Resource.Metrics == nil || datastoreRecord.Resource.Metrics.Disk == nil {
|
|
t.Fatal("expected datastore disk metrics to be populated")
|
|
}
|
|
if got := datastoreRecord.Resource.Metrics.Disk.Percent; got != 60 {
|
|
t.Fatalf("datastore disk usage percent = %v, want 60", got)
|
|
}
|
|
if len(datastoreRecord.Resource.Incidents) != 1 || datastoreRecord.Resource.Incidents[0].Code != "vmware_health_state" {
|
|
t.Fatalf("expected VMware datastore health incident, got %+v", datastoreRecord.Resource.Incidents)
|
|
}
|
|
if got := datastoreRecord.Resource.VMware.RecentTaskSummary; got != "Refresh datastore (queued)" {
|
|
t.Fatalf("datastore recent task summary = %q, want %q", got, "Refresh datastore (queued)")
|
|
}
|
|
if !datastoreRecord.Resource.Storage.Shared {
|
|
t.Fatal("expected datastore storage meta to mark the datastore as shared")
|
|
}
|
|
if got := datastoreRecord.Resource.Storage.Nodes; len(got) != 1 || got[0] != "esxi-01.lab.local" {
|
|
t.Fatalf("datastore storage nodes = %#v, want [esxi-01.lab.local]", got)
|
|
}
|
|
if got := datastoreRecord.Resource.Storage.ConsumerCount; got != 1 {
|
|
t.Fatalf("datastore consumer count = %d, want 1", got)
|
|
}
|
|
if got := datastoreRecord.Resource.Storage.TopConsumers; len(got) != 1 || got[0].Name != "app-01" {
|
|
t.Fatalf("datastore top consumers = %#v, want app-01", got)
|
|
}
|
|
if datastoreRecord.Resource.VMware.DatastoreAccessible == nil || !*datastoreRecord.Resource.VMware.DatastoreAccessible {
|
|
t.Fatalf("datastore accessible = %#v, want true", datastoreRecord.Resource.VMware.DatastoreAccessible)
|
|
}
|
|
}
|
|
|
|
func TestProviderActivityChanges_ProjectCanonicalTimelineEntries(t *testing.T) {
|
|
previous := IsFeatureEnabled()
|
|
SetFeatureEnabled(true)
|
|
t.Cleanup(func() { SetFeatureEnabled(previous) })
|
|
|
|
collectedAt := time.Date(2026, time.March, 30, 18, 20, 0, 0, time.UTC)
|
|
provider := NewProvider(InventorySnapshot{
|
|
ConnectionID: "vc-1",
|
|
ConnectionName: "Lab VC",
|
|
VCenterHost: "vc.lab.local",
|
|
CollectedAt: collectedAt,
|
|
Hosts: []InventoryHost{{
|
|
Host: "host-101",
|
|
Name: "esxi-01.lab.local",
|
|
RecentTasks: []InventoryTask{{
|
|
Task: "task-11",
|
|
Name: "Reconnect host",
|
|
State: "running",
|
|
StartedAt: collectedAt.Add(-1 * time.Minute),
|
|
}},
|
|
}},
|
|
VMs: []InventoryVM{{
|
|
VM: "vm-201",
|
|
Name: "app-01",
|
|
RecentEvents: []InventoryEvent{{
|
|
Event: "event-201",
|
|
Type: "VmMessageEvent",
|
|
Message: "Snapshot completed successfully",
|
|
User: "vpxuser",
|
|
CreatedAt: collectedAt.Add(-2 * time.Minute),
|
|
}},
|
|
}},
|
|
Datastores: []InventoryDatastore{{
|
|
Datastore: "datastore-11",
|
|
Name: "nvme-primary",
|
|
RecentEvents: []InventoryEvent{{
|
|
Event: "event-301",
|
|
Type: "DatastoreRenamedEvent",
|
|
Message: "Datastore metadata refreshed",
|
|
User: "storage-admin",
|
|
CreatedAt: collectedAt.Add(-3 * time.Minute),
|
|
}},
|
|
}},
|
|
})
|
|
|
|
changes := provider.ActivityChanges()
|
|
if len(changes) != 3 {
|
|
t.Fatalf("expected 3 VMware activity changes, got %d", len(changes))
|
|
}
|
|
if changes[0].Kind != unifiedresources.ChangeActivity {
|
|
t.Fatalf("latest change kind = %q, want %q", changes[0].Kind, unifiedresources.ChangeActivity)
|
|
}
|
|
if changes[0].SourceType != unifiedresources.SourcePlatformEvent {
|
|
t.Fatalf("latest change source type = %q, want %q", changes[0].SourceType, unifiedresources.SourcePlatformEvent)
|
|
}
|
|
if changes[0].SourceAdapter != unifiedresources.AdapterVMware {
|
|
t.Fatalf("latest change source adapter = %q, want %q", changes[0].SourceAdapter, unifiedresources.AdapterVMware)
|
|
}
|
|
if changes[0].ResourceID != "vc-1:host:host-101" {
|
|
t.Fatalf("latest change resource ID = %q, want vc-1:host:host-101", changes[0].ResourceID)
|
|
}
|
|
if changes[0].OccurredAt == nil || !changes[0].OccurredAt.Equal(collectedAt.Add(-1*time.Minute)) {
|
|
t.Fatalf("latest change occurred_at = %v, want %v", changes[0].OccurredAt, collectedAt.Add(-1*time.Minute))
|
|
}
|
|
if got := changes[0].Metadata[unifiedresources.MetadataActivityType]; got != "vmware_task" {
|
|
t.Fatalf("latest change activity_type = %#v, want vmware_task", got)
|
|
}
|
|
if got := changes[1].Metadata["vmwareEventType"]; got != "VmMessageEvent" {
|
|
t.Fatalf("vm change event type = %#v, want VmMessageEvent", got)
|
|
}
|
|
if got := changes[2].Metadata["vmwareEventUser"]; got != "storage-admin" {
|
|
t.Fatalf("datastore change user = %#v, want storage-admin", got)
|
|
}
|
|
}
|
|
|
|
func boolPtr(value bool) *bool {
|
|
return &value
|
|
}
|