9df13327aa
Remove power-on and power-off functionality from the Redfish collector;
keep host power-state detection and show a warning in the UI when the
host is powered off before collection starts.
Add a "Пропустить зависшие" (skip hung) button that lets the user abort
stuck Redfish collection phases without losing already-collected data.
Introduces a two-level context model in Collect(): the outer job context
covers the full lifecycle including replay; an inner collectCtx covers
snapshot, prefetch, and plan-B phases only. Closing the skipCh cancels
collectCtx immediately — aborts all in-flight HTTP requests and exits
plan-B loops — then replay runs on whatever rawTree was collected.
Signal path: UI → POST /api/collect/{id}/skip → JobManager.SkipJob()
→ close(skipCh) → goroutine in Collect() → cancelCollect().
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
103 lines
2.9 KiB
Go
103 lines
2.9 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.mchus.pro/mchus/logpile/internal/collector"
|
|
"git.mchus.pro/mchus/logpile/internal/models"
|
|
)
|
|
|
|
type mockConnector struct {
|
|
protocol string
|
|
}
|
|
|
|
func (c *mockConnector) Protocol() string {
|
|
return c.protocol
|
|
}
|
|
|
|
func (c *mockConnector) Probe(ctx context.Context, req collector.Request) (*collector.ProbeResult, error) {
|
|
if strings.Contains(strings.ToLower(req.Host), "fail") {
|
|
return nil, context.DeadlineExceeded
|
|
}
|
|
hostPoweredOn := true
|
|
if strings.Contains(strings.ToLower(req.Host), "off") || strings.Contains(strings.ToLower(req.Username), "off") {
|
|
hostPoweredOn = false
|
|
}
|
|
return &collector.ProbeResult{
|
|
Reachable: true,
|
|
Protocol: c.protocol,
|
|
HostPowerState: map[bool]string{true: "On", false: "Off"}[hostPoweredOn],
|
|
HostPoweredOn: hostPoweredOn,
|
|
SystemPath: "/redfish/v1/Systems/1",
|
|
}, nil
|
|
}
|
|
|
|
func (c *mockConnector) Collect(ctx context.Context, req collector.Request, emit collector.ProgressFn) (*models.AnalysisResult, error) {
|
|
steps := []collector.Progress{
|
|
{
|
|
Status: CollectStatusRunning,
|
|
Progress: 10,
|
|
Message: "Подбор модулей Redfish...",
|
|
ActiveModules: []collector.ModuleActivation{
|
|
{Name: "supermicro", Score: 80},
|
|
{Name: "generic", Score: 10},
|
|
},
|
|
ModuleScores: []collector.ModuleScore{
|
|
{Name: "supermicro", Score: 80, Active: true, Priority: 20},
|
|
{Name: "generic", Score: 10, Active: true, Priority: 100},
|
|
{Name: "hgx-topology", Score: 0, Active: false, Priority: 30},
|
|
},
|
|
DebugInfo: &collector.CollectDebugInfo{
|
|
AdaptiveThrottled: false,
|
|
SnapshotWorkers: 6,
|
|
PrefetchWorkers: 4,
|
|
PhaseTelemetry: []collector.PhaseTelemetry{
|
|
{Phase: "discovery", Requests: 6, Errors: 0, ErrorRate: 0, AvgMS: 120, P95MS: 180},
|
|
},
|
|
},
|
|
},
|
|
{Status: CollectStatusRunning, Progress: 20, Message: "Подключение..."},
|
|
{Status: CollectStatusRunning, Progress: 50, Message: "Сбор инвентаря..."},
|
|
{Status: CollectStatusRunning, Progress: 80, Message: "Нормализация..."},
|
|
}
|
|
for _, step := range steps {
|
|
if !collectorSleep(ctx, 100*time.Millisecond) {
|
|
return nil, ctx.Err()
|
|
}
|
|
if emit != nil {
|
|
emit(step)
|
|
}
|
|
}
|
|
|
|
if strings.Contains(strings.ToLower(req.Host), "fail") {
|
|
return nil, context.DeadlineExceeded
|
|
}
|
|
|
|
return &models.AnalysisResult{
|
|
Events: make([]models.Event, 0),
|
|
FRU: make([]models.FRUInfo, 0),
|
|
Sensors: make([]models.SensorReading, 0),
|
|
Hardware: &models.HardwareConfig{},
|
|
}, nil
|
|
}
|
|
|
|
func testCollectorRegistry() *collector.Registry {
|
|
r := collector.NewRegistry()
|
|
r.Register(&mockConnector{protocol: "redfish"})
|
|
r.Register(&mockConnector{protocol: "ipmi"})
|
|
return r
|
|
}
|
|
|
|
func collectorSleep(ctx context.Context, d time.Duration) bool {
|
|
timer := time.NewTimer(d)
|
|
defer timer.Stop()
|
|
select {
|
|
case <-ctx.Done():
|
|
return false
|
|
case <-timer.C:
|
|
return true
|
|
}
|
|
}
|