feat(collect): remove power-on/off, add skip-hung for Redfish collection
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>
This commit is contained in:
@@ -18,6 +18,7 @@ import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
@@ -1674,34 +1675,28 @@ func (s *Server) handleCollectProbe(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
message := "Связь с BMC установлена"
|
||||
if result != nil {
|
||||
switch {
|
||||
case !result.HostPoweredOn && result.PowerControlAvailable:
|
||||
message = "Связь с BMC установлена, host выключен. Можно включить перед сбором."
|
||||
case !result.HostPoweredOn:
|
||||
message = "Связь с BMC установлена, host выключен."
|
||||
default:
|
||||
message = "Связь с BMC установлена, host включен."
|
||||
if result.HostPoweredOn {
|
||||
message = "Связь с BMC установлена, host включён."
|
||||
} else {
|
||||
message = "Связь с BMC установлена, host выключен. Данные инвентаря могут быть неполными."
|
||||
}
|
||||
}
|
||||
|
||||
hostPowerState := ""
|
||||
hostPoweredOn := false
|
||||
powerControlAvailable := false
|
||||
reachable := false
|
||||
if result != nil {
|
||||
reachable = result.Reachable
|
||||
hostPowerState = strings.TrimSpace(result.HostPowerState)
|
||||
hostPoweredOn = result.HostPoweredOn
|
||||
powerControlAvailable = result.PowerControlAvailable
|
||||
}
|
||||
|
||||
jsonResponse(w, CollectProbeResponse{
|
||||
Reachable: reachable,
|
||||
Protocol: req.Protocol,
|
||||
HostPowerState: hostPowerState,
|
||||
HostPoweredOn: hostPoweredOn,
|
||||
PowerControlAvailable: powerControlAvailable,
|
||||
Message: message,
|
||||
Reachable: reachable,
|
||||
Protocol: req.Protocol,
|
||||
HostPowerState: hostPowerState,
|
||||
HostPoweredOn: hostPoweredOn,
|
||||
Message: message,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1737,6 +1732,22 @@ func (s *Server) handleCollectCancel(w http.ResponseWriter, r *http.Request) {
|
||||
jsonResponse(w, job.toStatusResponse())
|
||||
}
|
||||
|
||||
func (s *Server) handleCollectSkip(w http.ResponseWriter, r *http.Request) {
|
||||
jobID := strings.TrimSpace(r.PathValue("id"))
|
||||
if !isValidCollectJobID(jobID) {
|
||||
jsonError(w, "Invalid collect job id", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
job, ok := s.jobManager.SkipJob(jobID)
|
||||
if !ok {
|
||||
jsonError(w, "Collect job not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
jsonResponse(w, job.toStatusResponse())
|
||||
}
|
||||
|
||||
func (s *Server) startCollectionJob(jobID string, req CollectRequest) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
if attached := s.jobManager.AttachJobCancel(jobID, cancel); !attached {
|
||||
@@ -1744,6 +1755,11 @@ func (s *Server) startCollectionJob(jobID string, req CollectRequest) {
|
||||
return
|
||||
}
|
||||
|
||||
skipCh := make(chan struct{})
|
||||
var skipOnce sync.Once
|
||||
skipFn := func() { skipOnce.Do(func() { close(skipCh) }) }
|
||||
s.jobManager.AttachJobSkip(jobID, skipFn)
|
||||
|
||||
go func() {
|
||||
connector, ok := s.getCollector(req.Protocol)
|
||||
if !ok {
|
||||
@@ -1811,7 +1827,9 @@ func (s *Server) startCollectionJob(jobID string, req CollectRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
result, err := connector.Collect(ctx, toCollectorRequest(req), emitProgress)
|
||||
collectorReq := toCollectorRequest(req)
|
||||
collectorReq.SkipHungCh = skipCh
|
||||
result, err := connector.Collect(ctx, collectorReq, emitProgress)
|
||||
if err != nil {
|
||||
if ctx.Err() != nil {
|
||||
return
|
||||
@@ -2035,9 +2053,7 @@ func toCollectorRequest(req CollectRequest) collector.Request {
|
||||
Password: req.Password,
|
||||
Token: req.Token,
|
||||
TLSMode: req.TLSMode,
|
||||
PowerOnIfHostOff: req.PowerOnIfHostOff,
|
||||
StopHostAfterCollect: req.StopHostAfterCollect,
|
||||
DebugPayloads: req.DebugPayloads,
|
||||
DebugPayloads: req.DebugPayloads,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user