mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-21 11:15:36 +00:00
d6fa623035
One vCenter connection spans many ESXi hosts, so the Infrastructure source row now lists them the way Proxmox cluster rows list their nodes: an expandable member list with per-host state, aliases, and last-seen. Members are API-side composition only — no primary marker, no agent connection, and the member subtitle reads 'vSphere host' instead of cluster-node wording. TrueNAS keeps no member composition because that connection monitors exactly one machine. Mock mode now feeds its vCenter and TrueNAS fixtures into the connections aggregator (only when no real instances are configured), so the mock ledger shows the same platform source rows a real deployment would instead of omitting vSphere and TrueNAS entirely.
250 lines
8.3 KiB
Go
250 lines
8.3 KiB
Go
package api
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/config"
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/mock"
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/monitoring"
|
|
)
|
|
|
|
func mockTrueNASConnectionResponses() []trueNASConnectionResponse {
|
|
fixture := mock.DefaultTrueNASConnectionFixture()
|
|
|
|
instance := config.TrueNASInstance{
|
|
ID: fixture.ID,
|
|
Name: fixture.Name,
|
|
Host: fixture.Host,
|
|
Port: fixture.Port,
|
|
APIKey: fixture.APIKey,
|
|
UseHTTPS: fixture.UseHTTPS,
|
|
Enabled: fixture.Enabled,
|
|
PollIntervalSecs: fixture.PollIntervalSeconds,
|
|
}
|
|
instance.ApplyDefaults()
|
|
|
|
observed := &monitoring.TrueNASConnectionObservedSummary{
|
|
Host: fixture.Host,
|
|
ResourceID: fixture.ResourceID,
|
|
Systems: fixture.Systems,
|
|
StoragePools: fixture.StoragePools,
|
|
Datasets: fixture.Datasets,
|
|
Apps: fixture.Apps,
|
|
Disks: fixture.Disks,
|
|
RecoveryArtifacts: fixture.RecoveryArtifacts,
|
|
CollectedAt: mockPlatformTimePointer(fixture.CollectedAt),
|
|
}
|
|
|
|
return []trueNASConnectionResponse{{
|
|
TrueNASInstance: instance.Redacted(),
|
|
Poll: &monitoring.TrueNASConnectionPollStatus{
|
|
IntervalSeconds: instance.EffectivePollIntervalSecs(),
|
|
LastAttemptAt: mockPlatformTimePointer(fixture.CollectedAt),
|
|
LastSuccessAt: mockPlatformTimePointer(fixture.CollectedAt),
|
|
},
|
|
Observed: observed,
|
|
}}
|
|
}
|
|
|
|
func mockVMwareConnectionResponses() []vmwareConnectionResponse {
|
|
fixture := mock.DefaultVMwareConnectionFixture()
|
|
|
|
instance := config.VMwareVCenterInstance{
|
|
ID: fixture.ID,
|
|
Name: fixture.Name,
|
|
Host: fixture.Host,
|
|
Port: fixture.Port,
|
|
Username: fixture.Username,
|
|
Password: fixture.Password,
|
|
Enabled: fixture.Enabled,
|
|
}
|
|
instance.ApplyDefaults()
|
|
|
|
return []vmwareConnectionResponse{{
|
|
VMwareVCenterInstance: instance.Redacted(),
|
|
Poll: &monitoring.VMwareConnectionPollStatus{
|
|
IntervalSeconds: fixture.PollIntervalSeconds,
|
|
LastAttemptAt: mockPlatformTimePointer(fixture.CollectedAt),
|
|
LastSuccessAt: mockPlatformTimePointer(fixture.CollectedAt),
|
|
},
|
|
Observed: &monitoring.VMwareConnectionObservedSummary{
|
|
CollectedAt: mockPlatformTimePointer(fixture.CollectedAt),
|
|
Hosts: fixture.Hosts,
|
|
VMs: fixture.VMs,
|
|
Datastores: fixture.Datastores,
|
|
Networks: fixture.Networks,
|
|
VIRelease: fixture.VIRelease,
|
|
},
|
|
}}
|
|
}
|
|
|
|
// mockVMwareLedgerInputs adapts the mock vCenter fixture into the unified
|
|
// connections aggregator's instance+summary inputs so mock mode presents the
|
|
// same vSphere source row (with member hosts) a real deployment would.
|
|
func mockVMwareLedgerInputs() ([]config.VMwareVCenterInstance, map[string]monitoring.VMwareConnectionSummary) {
|
|
responses := mockVMwareConnectionResponses()
|
|
instances := make([]config.VMwareVCenterInstance, 0, len(responses))
|
|
summaries := make(map[string]monitoring.VMwareConnectionSummary, len(responses))
|
|
for _, response := range responses {
|
|
instances = append(instances, response.VMwareVCenterInstance)
|
|
summaries[response.VMwareVCenterInstance.ID] = monitoring.VMwareConnectionSummary{
|
|
Poll: response.Poll,
|
|
Observed: response.Observed,
|
|
}
|
|
}
|
|
return instances, summaries
|
|
}
|
|
|
|
// mockTrueNASLedgerInputs is the TrueNAS counterpart of
|
|
// mockVMwareLedgerInputs: one source row per mock NAS, no member composition
|
|
// because a TrueNAS connection monitors exactly one machine.
|
|
func mockTrueNASLedgerInputs() ([]config.TrueNASInstance, map[string]monitoring.TrueNASConnectionSummary) {
|
|
responses := mockTrueNASConnectionResponses()
|
|
instances := make([]config.TrueNASInstance, 0, len(responses))
|
|
summaries := make(map[string]monitoring.TrueNASConnectionSummary, len(responses))
|
|
for _, response := range responses {
|
|
instances = append(instances, response.TrueNASInstance)
|
|
summaries[response.TrueNASInstance.ID] = monitoring.TrueNASConnectionSummary{
|
|
Poll: response.Poll,
|
|
Observed: response.Observed,
|
|
}
|
|
}
|
|
return instances, summaries
|
|
}
|
|
|
|
func mockAvailabilityConnectionInputs() ([]config.AvailabilityTarget, map[string]monitoring.AvailabilityProbeStatus) {
|
|
fixtures := mock.AvailabilityFixtures()
|
|
targets := make([]config.AvailabilityTarget, 0, len(fixtures))
|
|
statuses := make(map[string]monitoring.AvailabilityProbeStatus, len(fixtures))
|
|
for _, fixture := range fixtures {
|
|
target := mockAvailabilityConfigTarget(fixture.Target)
|
|
if target.ID == "" {
|
|
continue
|
|
}
|
|
targets = append(targets, target)
|
|
statuses[target.ID] = mockAvailabilityProbeStatus(fixture, target)
|
|
}
|
|
return targets, statuses
|
|
}
|
|
|
|
func mergeAvailabilityTargets(existing, additions []config.AvailabilityTarget) []config.AvailabilityTarget {
|
|
if len(additions) == 0 {
|
|
return existing
|
|
}
|
|
out := make([]config.AvailabilityTarget, 0, len(existing)+len(additions))
|
|
seen := make(map[string]struct{}, len(existing)+len(additions))
|
|
for _, target := range existing {
|
|
target = config.NormalizeAvailabilityTarget(target)
|
|
if target.ID == "" {
|
|
continue
|
|
}
|
|
seen[target.ID] = struct{}{}
|
|
out = append(out, target)
|
|
}
|
|
for _, target := range additions {
|
|
target = config.NormalizeAvailabilityTarget(target)
|
|
if target.ID == "" {
|
|
continue
|
|
}
|
|
if _, ok := seen[target.ID]; ok {
|
|
continue
|
|
}
|
|
seen[target.ID] = struct{}{}
|
|
out = append(out, target)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func mergeAvailabilityStatuses(existing, additions map[string]monitoring.AvailabilityProbeStatus) map[string]monitoring.AvailabilityProbeStatus {
|
|
if len(existing) == 0 && len(additions) == 0 {
|
|
return map[string]monitoring.AvailabilityProbeStatus{}
|
|
}
|
|
out := make(map[string]monitoring.AvailabilityProbeStatus, len(existing)+len(additions))
|
|
for id, status := range existing {
|
|
if id != "" {
|
|
out[id] = status
|
|
}
|
|
}
|
|
for id, status := range additions {
|
|
if id != "" {
|
|
out[id] = status
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func mockAvailabilityTargetResponses() []availabilityTargetResponse {
|
|
fixtures := mock.AvailabilityFixtures()
|
|
responses := make([]availabilityTargetResponse, 0, len(fixtures))
|
|
for _, fixture := range fixtures {
|
|
target := mockAvailabilityConfigTarget(fixture.Target)
|
|
if target.ID == "" {
|
|
continue
|
|
}
|
|
status := mockAvailabilityProbeStatus(fixture, target)
|
|
responses = append(responses, availabilityTargetResponse{
|
|
AvailabilityTarget: target,
|
|
Status: &status,
|
|
})
|
|
}
|
|
return responses
|
|
}
|
|
|
|
func mockAvailabilityTestResponse(targetID string) (availabilityTestResponse, bool) {
|
|
for _, fixture := range mock.AvailabilityFixtures() {
|
|
target := mockAvailabilityConfigTarget(fixture.Target)
|
|
if target.ID != targetID {
|
|
continue
|
|
}
|
|
response := availabilityTestResponse{
|
|
Success: fixture.Available,
|
|
LatencyMillis: fixture.LatencyMillis,
|
|
Error: fixture.LastError,
|
|
}
|
|
return response, true
|
|
}
|
|
return availabilityTestResponse{}, false
|
|
}
|
|
|
|
func mockAvailabilityConfigTarget(target mock.AvailabilityTargetFixture) config.AvailabilityTarget {
|
|
return config.NormalizeAvailabilityTarget(config.AvailabilityTarget{
|
|
ID: target.ID,
|
|
Name: target.Name,
|
|
TargetKind: config.AvailabilityTargetKind(target.TargetKind),
|
|
Address: target.Address,
|
|
Protocol: config.AvailabilityProbeProtocol(target.Protocol),
|
|
Port: target.Port,
|
|
Path: target.Path,
|
|
Enabled: target.Enabled,
|
|
PollIntervalSecs: target.PollIntervalSecs,
|
|
TimeoutMillis: target.TimeoutMillis,
|
|
FailureThreshold: target.FailureThreshold,
|
|
})
|
|
}
|
|
|
|
func mockAvailabilityProbeStatus(fixture mock.AvailabilityFixture, target config.AvailabilityTarget) monitoring.AvailabilityProbeStatus {
|
|
return monitoring.AvailabilityProbeStatus{
|
|
TargetID: target.ID,
|
|
Name: target.DisplayName(),
|
|
TargetKind: string(target.TargetKind),
|
|
Address: target.Address,
|
|
Protocol: string(target.Protocol),
|
|
Enabled: target.Enabled,
|
|
Available: fixture.Available,
|
|
LastChecked: fixture.LastChecked,
|
|
LastSuccess: fixture.LastSuccess,
|
|
LatencyMillis: fixture.LatencyMillis,
|
|
ConsecutiveFailures: fixture.ConsecutiveFailures,
|
|
LastError: fixture.LastError,
|
|
FailureThreshold: target.EffectiveFailureThreshold(),
|
|
}
|
|
}
|
|
|
|
func mockPlatformTimePointer(value time.Time) *time.Time {
|
|
if value.IsZero() {
|
|
return nil
|
|
}
|
|
copied := value
|
|
return &copied
|
|
}
|