This commit implements per-node temperature monitoring control and fixes a critical bug where partial node updates were destroying existing configuration. Backend changes: - Add TemperatureMonitoringEnabled field (*bool) to PVEInstance, PBSInstance, and PMGInstance - Update monitor.go to check per-node temperature setting with global fallback - Convert all NodeConfigRequest boolean fields to *bool pointers - Add nil checks in HandleUpdateNode to prevent overwriting unmodified fields - Fix critical bug where partial updates zeroed out MonitorVMs, MonitorContainers, etc. - Update NodeResponse, NodeFrontend, and StateSnapshot to include temperature setting - Fix HandleAddNode and test connection handlers to use pointer-based boolean fields Frontend changes: - Add temperatureMonitoringEnabled to Node interface and config types - Create per-node temperature monitoring toggle handler with optimistic updates - Update NodeModal to wire up per-node temperature toggle - Add isTemperatureMonitoringEnabled helper to check effective monitoring state - Update ConfiguredNodeTables to show/hide temperature badge based on monitoring state - Update NodeSummaryTable to conditionally show temperature column - Pass globalTemperatureMonitoringEnabled prop through component tree The critical bug fix ensures that when updating a single field (like temperature monitoring), the backend only modifies that specific field instead of zeroing out all other boolean configuration fields.
163 lines
5.9 KiB
Go
163 lines
5.9 KiB
Go
package models
|
|
|
|
import "time"
|
|
|
|
// StateSnapshot represents a snapshot of the state without mutex
|
|
type StateSnapshot struct {
|
|
Nodes []Node `json:"nodes"`
|
|
VMs []VM `json:"vms"`
|
|
Containers []Container `json:"containers"`
|
|
DockerHosts []DockerHost `json:"dockerHosts"`
|
|
RemovedDockerHosts []RemovedDockerHost `json:"removedDockerHosts"`
|
|
Hosts []Host `json:"hosts"`
|
|
Storage []Storage `json:"storage"`
|
|
CephClusters []CephCluster `json:"cephClusters"`
|
|
PhysicalDisks []PhysicalDisk `json:"physicalDisks"`
|
|
PBSInstances []PBSInstance `json:"pbs"`
|
|
PMGInstances []PMGInstance `json:"pmg"`
|
|
PBSBackups []PBSBackup `json:"pbsBackups"`
|
|
PMGBackups []PMGBackup `json:"pmgBackups"`
|
|
Backups Backups `json:"backups"`
|
|
ReplicationJobs []ReplicationJob `json:"replicationJobs"`
|
|
Metrics []Metric `json:"metrics"`
|
|
PVEBackups PVEBackups `json:"pveBackups"`
|
|
Performance Performance `json:"performance"`
|
|
ConnectionHealth map[string]bool `json:"connectionHealth"`
|
|
Stats Stats `json:"stats"`
|
|
ActiveAlerts []Alert `json:"activeAlerts"`
|
|
RecentlyResolved []ResolvedAlert `json:"recentlyResolved"`
|
|
LastUpdate time.Time `json:"lastUpdate"`
|
|
TemperatureMonitoringEnabled bool `json:"temperatureMonitoringEnabled"`
|
|
}
|
|
|
|
// GetSnapshot returns a snapshot of the current state without mutex
|
|
func (s *State) GetSnapshot() StateSnapshot {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
|
|
pbsBackups := append([]PBSBackup{}, s.PBSBackups...)
|
|
pmgBackups := append([]PMGBackup{}, s.PMGBackups...)
|
|
pveBackups := PVEBackups{
|
|
BackupTasks: append([]BackupTask{}, s.PVEBackups.BackupTasks...),
|
|
StorageBackups: append([]StorageBackup{}, s.PVEBackups.StorageBackups...),
|
|
GuestSnapshots: append([]GuestSnapshot{}, s.PVEBackups.GuestSnapshots...),
|
|
}
|
|
|
|
// Create a snapshot without mutex
|
|
snapshot := StateSnapshot{
|
|
Nodes: append([]Node{}, s.Nodes...),
|
|
VMs: append([]VM{}, s.VMs...),
|
|
Containers: append([]Container{}, s.Containers...),
|
|
DockerHosts: append([]DockerHost{}, s.DockerHosts...),
|
|
RemovedDockerHosts: append([]RemovedDockerHost{}, s.RemovedDockerHosts...),
|
|
Hosts: append([]Host{}, s.Hosts...),
|
|
Storage: append([]Storage{}, s.Storage...),
|
|
CephClusters: append([]CephCluster{}, s.CephClusters...),
|
|
PhysicalDisks: append([]PhysicalDisk{}, s.PhysicalDisks...),
|
|
PBSInstances: append([]PBSInstance{}, s.PBSInstances...),
|
|
PMGInstances: append([]PMGInstance{}, s.PMGInstances...),
|
|
PBSBackups: pbsBackups,
|
|
PMGBackups: pmgBackups,
|
|
Backups: Backups{
|
|
PVE: pveBackups,
|
|
PBS: pbsBackups,
|
|
PMG: pmgBackups,
|
|
},
|
|
ReplicationJobs: append([]ReplicationJob{}, s.ReplicationJobs...),
|
|
Metrics: append([]Metric{}, s.Metrics...),
|
|
PVEBackups: pveBackups,
|
|
Performance: s.Performance,
|
|
ConnectionHealth: make(map[string]bool),
|
|
Stats: s.Stats,
|
|
ActiveAlerts: append([]Alert{}, s.ActiveAlerts...),
|
|
RecentlyResolved: append([]ResolvedAlert{}, s.RecentlyResolved...),
|
|
LastUpdate: s.LastUpdate,
|
|
TemperatureMonitoringEnabled: s.TemperatureMonitoringEnabled,
|
|
}
|
|
|
|
// Copy map
|
|
for k, v := range s.ConnectionHealth {
|
|
snapshot.ConnectionHealth[k] = v
|
|
}
|
|
|
|
return snapshot
|
|
}
|
|
|
|
// ToFrontend converts a StateSnapshot to frontend format with proper tag handling
|
|
func (s StateSnapshot) ToFrontend() StateFrontend {
|
|
// Convert nodes
|
|
nodes := make([]NodeFrontend, len(s.Nodes))
|
|
for i, n := range s.Nodes {
|
|
nodes[i] = n.ToFrontend()
|
|
}
|
|
|
|
// Convert VMs
|
|
vms := make([]VMFrontend, len(s.VMs))
|
|
for i, v := range s.VMs {
|
|
vms[i] = v.ToFrontend()
|
|
}
|
|
|
|
// Convert containers
|
|
containers := make([]ContainerFrontend, len(s.Containers))
|
|
for i, c := range s.Containers {
|
|
containers[i] = c.ToFrontend()
|
|
}
|
|
|
|
dockerHosts := make([]DockerHostFrontend, len(s.DockerHosts))
|
|
for i, host := range s.DockerHosts {
|
|
dockerHosts[i] = host.ToFrontend()
|
|
}
|
|
|
|
removedDockerHosts := make([]RemovedDockerHostFrontend, len(s.RemovedDockerHosts))
|
|
for i, entry := range s.RemovedDockerHosts {
|
|
removedDockerHosts[i] = entry.ToFrontend()
|
|
}
|
|
|
|
hosts := make([]HostFrontend, len(s.Hosts))
|
|
for i, host := range s.Hosts {
|
|
hosts[i] = host.ToFrontend()
|
|
}
|
|
|
|
// Convert storage
|
|
storage := make([]StorageFrontend, len(s.Storage))
|
|
for i, st := range s.Storage {
|
|
storage[i] = st.ToFrontend()
|
|
}
|
|
|
|
// Convert Ceph clusters
|
|
cephClusters := make([]CephClusterFrontend, len(s.CephClusters))
|
|
for i, cluster := range s.CephClusters {
|
|
cephClusters[i] = cluster.ToFrontend()
|
|
}
|
|
|
|
replicationJobs := make([]ReplicationJobFrontend, len(s.ReplicationJobs))
|
|
for i, job := range s.ReplicationJobs {
|
|
replicationJobs[i] = job.ToFrontend()
|
|
}
|
|
|
|
return StateFrontend{
|
|
Nodes: nodes,
|
|
VMs: vms,
|
|
Containers: containers,
|
|
DockerHosts: dockerHosts,
|
|
RemovedDockerHosts: removedDockerHosts,
|
|
Hosts: hosts,
|
|
Storage: storage,
|
|
CephClusters: cephClusters,
|
|
PhysicalDisks: s.PhysicalDisks,
|
|
PBS: s.PBSInstances,
|
|
PMG: s.PMGInstances,
|
|
PBSBackups: s.PBSBackups,
|
|
PMGBackups: s.PMGBackups,
|
|
Backups: s.Backups,
|
|
ReplicationJobs: replicationJobs,
|
|
ActiveAlerts: s.ActiveAlerts,
|
|
Metrics: make(map[string]any),
|
|
PVEBackups: s.PVEBackups,
|
|
Performance: make(map[string]any),
|
|
ConnectionHealth: s.ConnectionHealth,
|
|
Stats: make(map[string]any),
|
|
LastUpdate: s.LastUpdate.Unix() * 1000, // JavaScript timestamp
|
|
TemperatureMonitoringEnabled: s.TemperatureMonitoringEnabled,
|
|
}
|
|
}
|