package orchestrator import ( "fmt" "time" "github.com/Fimeg/RedFlag/aggregator-agent/internal/client" "github.com/Fimeg/RedFlag/aggregator-agent/internal/system" ) // StorageScanner scans disk usage metrics type StorageScanner struct { agentVersion string } // NewStorageScanner creates a new storage scanner func NewStorageScanner(agentVersion string) *StorageScanner { return &StorageScanner{ agentVersion: agentVersion, } } // IsAvailable always returns true since storage scanning is always available func (s *StorageScanner) IsAvailable() bool { return true } // ScanStorage collects disk usage information and returns proper storage metrics func (s *StorageScanner) ScanStorage() ([]StorageMetric, error) { sysInfo, err := system.GetSystemInfo(s.agentVersion) if err != nil { return nil, fmt.Errorf("failed to get system info: %w", err) } if len(sysInfo.DiskInfo) == 0 { return nil, fmt.Errorf("no disk information available") } // Convert disk info to proper StorageMetric format var metrics []StorageMetric for _, disk := range sysInfo.DiskInfo { metric := StorageMetric{ Mountpoint: disk.Mountpoint, Filesystem: disk.Filesystem, Device: disk.Device, DiskType: disk.DiskType, TotalBytes: disk.Total, UsedBytes: disk.Used, AvailableBytes: disk.Available, UsedPercent: disk.UsedPercent, IsRoot: disk.IsRoot, IsLargest: disk.IsLargest, Severity: determineDiskSeverity(disk.UsedPercent), Metadata: map[string]interface{}{ "agent_version": s.agentVersion, "collected_at": sysInfo.Timestamp, }, } metrics = append(metrics, metric) } return metrics, nil } // Name returns the scanner name func (s *StorageScanner) Name() string { return "Disk Usage Reporter" } // --- Legacy Compatibility Methods --- // Scan collects disk usage information and returns it as "updates" for reporting (LEGACY) // This method is kept for backwards compatibility with the old Scanner interface func (s *StorageScanner) Scan() ([]client.UpdateReportItem, error) { metrics, err := s.ScanStorage() if err != nil { return nil, err } // Convert proper StorageMetric back to legacy UpdateReportItem format var items []client.UpdateReportItem for _, metric := range metrics { item := client.UpdateReportItem{ PackageName: fmt.Sprintf("disk-%s", metric.Mountpoint), CurrentVersion: fmt.Sprintf("%.1f%% used", metric.UsedPercent), AvailableVersion: fmt.Sprintf("%d GB available", metric.AvailableBytes/(1024*1024*1024)), PackageType: "storage", Severity: metric.Severity, PackageDescription: fmt.Sprintf("Disk: %s (%s) - %s", metric.Mountpoint, metric.Filesystem, metric.Device), Metadata: metric.Metadata, } items = append(items, item) } return items, nil } // --- Typed Scanner Implementation --- // GetType returns the scanner type func (s *StorageScanner) GetType() ScannerType { return ScannerTypeStorage } // ScanTyped returns typed results (new implementation) func (s *StorageScanner) ScanTyped() (TypedScannerResult, error) { startTime := time.Now() defer func() { // Duration will be set at the end }() metrics, err := s.ScanStorage() if err != nil { return TypedScannerResult{ ScannerName: s.Name(), ScannerType: ScannerTypeStorage, Error: err, Status: "failed", Duration: time.Since(startTime).Milliseconds(), }, err } return TypedScannerResult{ ScannerName: s.Name(), ScannerType: ScannerTypeStorage, StorageData: metrics, Status: "success", Duration: time.Since(startTime).Milliseconds(), }, nil } // determineDiskSeverity returns severity based on disk usage percentage func determineDiskSeverity(usedPercent float64) string { switch { case usedPercent >= 95: return "critical" case usedPercent >= 90: return "important" case usedPercent >= 80: return "moderate" default: return "low" } }