Files
Redflag/aggregator-agent/internal/orchestrator/system_scanner.go
Fimeg eccc38d7c9 feat: separate data classification architecture
- Create separate scanner interfaces for storage, system, and docker data
- Add dedicated endpoints for metrics and docker images instead of misclassifying as updates
- Implement proper database tables for storage metrics and docker images
- Fix storage/system metrics appearing incorrectly as package updates
- Add scanner types with proper data structures for each subsystem
- Update agent handlers to use correct endpoints for each data type
2025-11-03 21:44:48 -05:00

192 lines
5.2 KiB
Go

package orchestrator
import (
"fmt"
"time"
"github.com/Fimeg/RedFlag/aggregator-agent/internal/client"
"github.com/Fimeg/RedFlag/aggregator-agent/internal/system"
)
// SystemScanner scans system metrics (CPU, memory, processes, uptime)
type SystemScanner struct {
agentVersion string
}
// NewSystemScanner creates a new system scanner
func NewSystemScanner(agentVersion string) *SystemScanner {
return &SystemScanner{
agentVersion: agentVersion,
}
}
// IsAvailable always returns true since system scanning is always available
func (s *SystemScanner) IsAvailable() bool {
return true
}
// ScanSystem collects system information and returns proper system metrics
func (s *SystemScanner) ScanSystem() ([]SystemMetric, error) {
sysInfo, err := system.GetSystemInfo(s.agentVersion)
if err != nil {
return nil, fmt.Errorf("failed to get system info: %w", err)
}
// Convert system info to proper SystemMetric format
var metrics []SystemMetric
// CPU info metric
cpuMetric := SystemMetric{
MetricName: "system-cpu",
MetricType: "cpu",
CurrentValue: fmt.Sprintf("%d cores, %d threads", sysInfo.CPUInfo.Cores, sysInfo.CPUInfo.Threads),
AvailableValue: sysInfo.CPUInfo.ModelName,
Severity: "low",
Metadata: map[string]interface{}{
"cpu_model": sysInfo.CPUInfo.ModelName,
"cpu_cores": sysInfo.CPUInfo.Cores,
"cpu_threads": sysInfo.CPUInfo.Threads,
},
}
metrics = append(metrics, cpuMetric)
// Memory info metric
memMetric := SystemMetric{
MetricName: "system-memory",
MetricType: "memory",
CurrentValue: fmt.Sprintf("%.1f%% used", sysInfo.MemoryInfo.UsedPercent),
AvailableValue: fmt.Sprintf("%d GB total", sysInfo.MemoryInfo.Total/(1024*1024*1024)),
Severity: determineMemorySeverity(sysInfo.MemoryInfo.UsedPercent),
Metadata: map[string]interface{}{
"memory_total": sysInfo.MemoryInfo.Total,
"memory_used": sysInfo.MemoryInfo.Used,
"memory_available": sysInfo.MemoryInfo.Available,
"memory_used_percent": sysInfo.MemoryInfo.UsedPercent,
},
}
metrics = append(metrics, memMetric)
// Process count metric
processMetric := SystemMetric{
MetricName: "system-processes",
MetricType: "processes",
CurrentValue: fmt.Sprintf("%d processes", sysInfo.RunningProcesses),
AvailableValue: "n/a",
Severity: "low",
Metadata: map[string]interface{}{
"process_count": sysInfo.RunningProcesses,
},
}
metrics = append(metrics, processMetric)
// Uptime metric
uptimeMetric := SystemMetric{
MetricName: "system-uptime",
MetricType: "uptime",
CurrentValue: sysInfo.Uptime,
AvailableValue: "n/a",
Severity: "low",
Metadata: map[string]interface{}{
"uptime": sysInfo.Uptime,
},
}
metrics = append(metrics, uptimeMetric)
// Reboot required metric (if applicable)
if sysInfo.RebootRequired {
rebootMetric := SystemMetric{
MetricName: "system-reboot",
MetricType: "reboot",
CurrentValue: "required",
AvailableValue: "n/a",
Severity: "important",
Metadata: map[string]interface{}{
"reboot_required": true,
"reboot_reason": sysInfo.RebootReason,
},
}
metrics = append(metrics, rebootMetric)
}
return metrics, nil
}
// Name returns the scanner name
func (s *SystemScanner) Name() string {
return "System Metrics Reporter"
}
// --- Legacy Compatibility Methods ---
// Scan collects system information and returns it as "updates" for reporting (LEGACY)
// This method is kept for backwards compatibility with the old Scanner interface
func (s *SystemScanner) Scan() ([]client.UpdateReportItem, error) {
metrics, err := s.ScanSystem()
if err != nil {
return nil, err
}
// Convert proper SystemMetric back to legacy UpdateReportItem format
var items []client.UpdateReportItem
for _, metric := range metrics {
item := client.UpdateReportItem{
PackageName: metric.MetricName,
CurrentVersion: metric.CurrentValue,
AvailableVersion: metric.AvailableValue,
PackageType: "system",
Severity: metric.Severity,
PackageDescription: fmt.Sprintf("System %s: %s", metric.MetricType, metric.MetricName),
Metadata: metric.Metadata,
}
items = append(items, item)
}
return items, nil
}
// --- Typed Scanner Implementation ---
// GetType returns the scanner type
func (s *SystemScanner) GetType() ScannerType {
return ScannerTypeSystem
}
// ScanTyped returns typed results (new implementation)
func (s *SystemScanner) ScanTyped() (TypedScannerResult, error) {
startTime := time.Now()
metrics, err := s.ScanSystem()
if err != nil {
return TypedScannerResult{
ScannerName: s.Name(),
ScannerType: ScannerTypeSystem,
Error: err,
Status: "failed",
Duration: time.Since(startTime).Milliseconds(),
}, err
}
return TypedScannerResult{
ScannerName: s.Name(),
ScannerType: ScannerTypeSystem,
SystemData: metrics,
Status: "success",
Duration: time.Since(startTime).Milliseconds(),
}, nil
}
// determineMemorySeverity returns severity based on memory usage percentage
func determineMemorySeverity(usedPercent float64) string {
switch {
case usedPercent >= 95:
return "critical"
case usedPercent >= 90:
return "important"
case usedPercent >= 80:
return "moderate"
default:
return "low"
}
}