Admin UI: Fetch task logs (#7114)
* show task details * loading tasks * task UI works * generic rendering * rendering the export link * removing placementConflicts from task parameters * remove TaskSourceLocation * remove "Server ID" column * rendering balance task source * sources and targets * fix ec task generation * move info * render timeline * simplified worker id * simplify * read task logs from worker * isValidTaskID * address comments * Update weed/worker/tasks/balance/execution.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update weed/worker/tasks/erasure_coding/ec_task.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update weed/worker/tasks/task_log_handler.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix shard ids * plan distributing shard id * rendering planned shards in task details * remove Conflicts * worker logs correctly * pass in dc and rack * task logging * Update weed/admin/maintenance/maintenance_queue.go Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * display log details * logs have fields now * sort field keys * fix link * fix collection filtering * avoid hard coded ec shard counts --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
This commit is contained in:
@@ -48,21 +48,32 @@ func (t *BalanceTask) Execute(ctx context.Context, params *worker_pb.TaskParams)
|
||||
return fmt.Errorf("balance parameters are required")
|
||||
}
|
||||
|
||||
// Get planned destination
|
||||
destNode := balanceParams.DestNode
|
||||
// Get source and destination from unified arrays
|
||||
if len(params.Sources) == 0 {
|
||||
return fmt.Errorf("source is required for balance task")
|
||||
}
|
||||
if len(params.Targets) == 0 {
|
||||
return fmt.Errorf("target is required for balance task")
|
||||
}
|
||||
|
||||
sourceNode := params.Sources[0].Node
|
||||
destNode := params.Targets[0].Node
|
||||
|
||||
if sourceNode == "" {
|
||||
return fmt.Errorf("source node is required for balance task")
|
||||
}
|
||||
if destNode == "" {
|
||||
return fmt.Errorf("destination node is required for balance task")
|
||||
}
|
||||
|
||||
t.GetLogger().WithFields(map[string]interface{}{
|
||||
"volume_id": t.volumeID,
|
||||
"source": t.server,
|
||||
"source": sourceNode,
|
||||
"destination": destNode,
|
||||
"collection": t.collection,
|
||||
}).Info("Starting balance task - moving volume")
|
||||
|
||||
sourceServer := pb.ServerAddress(t.server)
|
||||
sourceServer := pb.ServerAddress(sourceNode)
|
||||
targetServer := pb.ServerAddress(destNode)
|
||||
volumeId := needle.VolumeId(t.volumeID)
|
||||
|
||||
@@ -130,8 +141,16 @@ func (t *BalanceTask) Validate(params *worker_pb.TaskParams) error {
|
||||
return fmt.Errorf("volume ID mismatch: expected %d, got %d", t.volumeID, params.VolumeId)
|
||||
}
|
||||
|
||||
if params.Server != t.server {
|
||||
return fmt.Errorf("source server mismatch: expected %s, got %s", t.server, params.Server)
|
||||
// Validate that at least one source matches our server
|
||||
found := false
|
||||
for _, source := range params.Sources {
|
||||
if source.Node == t.server {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("no source matches expected server %s", t.server)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -105,36 +105,54 @@ func Detection(metrics []*types.VolumeHealthMetrics, clusterInfo *types.ClusterI
|
||||
return nil, nil // Skip this task if destination planning fails
|
||||
}
|
||||
|
||||
// Create typed parameters with destination information
|
||||
task.TypedParams = &worker_pb.TaskParams{
|
||||
TaskId: taskID, // Link to ActiveTopology pending task
|
||||
VolumeId: selectedVolume.VolumeID,
|
||||
Server: selectedVolume.Server,
|
||||
Collection: selectedVolume.Collection,
|
||||
VolumeSize: selectedVolume.Size, // Store original volume size for tracking changes
|
||||
TaskParams: &worker_pb.TaskParams_BalanceParams{
|
||||
BalanceParams: &worker_pb.BalanceTaskParams{
|
||||
DestNode: destinationPlan.TargetNode,
|
||||
EstimatedSize: destinationPlan.ExpectedSize,
|
||||
PlacementScore: destinationPlan.PlacementScore,
|
||||
PlacementConflicts: destinationPlan.Conflicts,
|
||||
ForceMove: false,
|
||||
TimeoutSeconds: 600, // 10 minutes default
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
glog.V(1).Infof("Planned balance destination for volume %d: %s -> %s (score: %.2f)",
|
||||
selectedVolume.VolumeID, selectedVolume.Server, destinationPlan.TargetNode, destinationPlan.PlacementScore)
|
||||
|
||||
// Add pending balance task to ActiveTopology for capacity management
|
||||
|
||||
// Find the actual disk containing the volume on the source server
|
||||
sourceDisk, found := base.FindVolumeDisk(clusterInfo.ActiveTopology, selectedVolume.VolumeID, selectedVolume.Collection, selectedVolume.Server)
|
||||
if !found {
|
||||
return nil, fmt.Errorf("BALANCE: Could not find volume %d (collection: %s) on source server %s - unable to create balance task",
|
||||
selectedVolume.VolumeID, selectedVolume.Collection, selectedVolume.Server)
|
||||
}
|
||||
|
||||
// Create typed parameters with unified source and target information
|
||||
task.TypedParams = &worker_pb.TaskParams{
|
||||
TaskId: taskID, // Link to ActiveTopology pending task
|
||||
VolumeId: selectedVolume.VolumeID,
|
||||
Collection: selectedVolume.Collection,
|
||||
VolumeSize: selectedVolume.Size, // Store original volume size for tracking changes
|
||||
|
||||
// Unified sources and targets - the only way to specify locations
|
||||
Sources: []*worker_pb.TaskSource{
|
||||
{
|
||||
Node: selectedVolume.Server,
|
||||
DiskId: sourceDisk,
|
||||
VolumeId: selectedVolume.VolumeID,
|
||||
EstimatedSize: selectedVolume.Size,
|
||||
DataCenter: selectedVolume.DataCenter,
|
||||
Rack: selectedVolume.Rack,
|
||||
},
|
||||
},
|
||||
Targets: []*worker_pb.TaskTarget{
|
||||
{
|
||||
Node: destinationPlan.TargetNode,
|
||||
DiskId: destinationPlan.TargetDisk,
|
||||
VolumeId: selectedVolume.VolumeID,
|
||||
EstimatedSize: destinationPlan.ExpectedSize,
|
||||
DataCenter: destinationPlan.TargetDC,
|
||||
Rack: destinationPlan.TargetRack,
|
||||
},
|
||||
},
|
||||
|
||||
TaskParams: &worker_pb.TaskParams_BalanceParams{
|
||||
BalanceParams: &worker_pb.BalanceTaskParams{
|
||||
ForceMove: false,
|
||||
TimeoutSeconds: 600, // 10 minutes default
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
glog.V(1).Infof("Planned balance destination for volume %d: %s -> %s",
|
||||
selectedVolume.VolumeID, selectedVolume.Server, destinationPlan.TargetNode)
|
||||
|
||||
// Add pending balance task to ActiveTopology for capacity management
|
||||
targetDisk := destinationPlan.TargetDisk
|
||||
|
||||
err = clusterInfo.ActiveTopology.AddPendingTask(topology.TaskSpec{
|
||||
@@ -220,7 +238,6 @@ func planBalanceDestination(activeTopology *topology.ActiveTopology, selectedVol
|
||||
TargetDC: bestDisk.DataCenter,
|
||||
ExpectedSize: selectedVolume.Size,
|
||||
PlacementScore: bestScore,
|
||||
Conflicts: checkPlacementConflicts(bestDisk, sourceRack, sourceDC),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -253,16 +270,3 @@ func calculateBalanceScore(disk *topology.DiskInfo, sourceRack, sourceDC string,
|
||||
|
||||
return score
|
||||
}
|
||||
|
||||
// checkPlacementConflicts checks for placement rule conflicts
|
||||
func checkPlacementConflicts(disk *topology.DiskInfo, sourceRack, sourceDC string) []string {
|
||||
var conflicts []string
|
||||
|
||||
// For now, implement basic conflict detection
|
||||
// This could be extended with more sophisticated placement rules
|
||||
if disk.Rack == sourceRack && disk.DataCenter == sourceDC {
|
||||
conflicts = append(conflicts, "same_rack_as_source")
|
||||
}
|
||||
|
||||
return conflicts
|
||||
}
|
||||
|
||||
@@ -15,15 +15,13 @@ type TypedTask struct {
|
||||
*base.BaseTypedTask
|
||||
|
||||
// Task state from protobuf
|
||||
sourceServer string
|
||||
destNode string
|
||||
volumeID uint32
|
||||
collection string
|
||||
estimatedSize uint64
|
||||
placementScore float64
|
||||
forceMove bool
|
||||
timeoutSeconds int32
|
||||
placementConflicts []string
|
||||
sourceServer string
|
||||
destNode string
|
||||
volumeID uint32
|
||||
collection string
|
||||
estimatedSize uint64
|
||||
forceMove bool
|
||||
timeoutSeconds int32
|
||||
}
|
||||
|
||||
// NewTypedTask creates a new typed balance task
|
||||
@@ -47,14 +45,20 @@ func (t *TypedTask) ValidateTyped(params *worker_pb.TaskParams) error {
|
||||
return fmt.Errorf("balance_params is required for balance task")
|
||||
}
|
||||
|
||||
// Validate destination node
|
||||
if balanceParams.DestNode == "" {
|
||||
return fmt.Errorf("dest_node is required for balance task")
|
||||
// Validate sources and targets
|
||||
if len(params.Sources) == 0 {
|
||||
return fmt.Errorf("at least one source is required for balance task")
|
||||
}
|
||||
if len(params.Targets) == 0 {
|
||||
return fmt.Errorf("at least one target is required for balance task")
|
||||
}
|
||||
|
||||
// Validate estimated size
|
||||
if balanceParams.EstimatedSize == 0 {
|
||||
return fmt.Errorf("estimated_size must be greater than 0")
|
||||
// Validate that source and target have volume IDs
|
||||
if params.Sources[0].VolumeId == 0 {
|
||||
return fmt.Errorf("source volume_id is required for balance task")
|
||||
}
|
||||
if params.Targets[0].VolumeId == 0 {
|
||||
return fmt.Errorf("target volume_id is required for balance task")
|
||||
}
|
||||
|
||||
// Validate timeout
|
||||
@@ -73,10 +77,13 @@ func (t *TypedTask) EstimateTimeTyped(params *worker_pb.TaskParams) time.Duratio
|
||||
if balanceParams.TimeoutSeconds > 0 {
|
||||
return time.Duration(balanceParams.TimeoutSeconds) * time.Second
|
||||
}
|
||||
}
|
||||
|
||||
// Estimate based on volume size (1 minute per GB)
|
||||
if balanceParams.EstimatedSize > 0 {
|
||||
gbSize := balanceParams.EstimatedSize / (1024 * 1024 * 1024)
|
||||
// Estimate based on volume size from sources (1 minute per GB)
|
||||
if len(params.Sources) > 0 {
|
||||
source := params.Sources[0]
|
||||
if source.EstimatedSize > 0 {
|
||||
gbSize := source.EstimatedSize / (1024 * 1024 * 1024)
|
||||
return time.Duration(gbSize) * time.Minute
|
||||
}
|
||||
}
|
||||
@@ -89,35 +96,30 @@ func (t *TypedTask) EstimateTimeTyped(params *worker_pb.TaskParams) time.Duratio
|
||||
func (t *TypedTask) ExecuteTyped(params *worker_pb.TaskParams) error {
|
||||
// Extract basic parameters
|
||||
t.volumeID = params.VolumeId
|
||||
t.sourceServer = params.Server
|
||||
t.collection = params.Collection
|
||||
|
||||
// Ensure sources and targets are present (should be guaranteed by validation)
|
||||
if len(params.Sources) == 0 {
|
||||
return fmt.Errorf("at least one source is required for balance task (ExecuteTyped)")
|
||||
}
|
||||
if len(params.Targets) == 0 {
|
||||
return fmt.Errorf("at least one target is required for balance task (ExecuteTyped)")
|
||||
}
|
||||
|
||||
// Extract source and target information
|
||||
t.sourceServer = params.Sources[0].Node
|
||||
t.estimatedSize = params.Sources[0].EstimatedSize
|
||||
t.destNode = params.Targets[0].Node
|
||||
// Extract balance-specific parameters
|
||||
balanceParams := params.GetBalanceParams()
|
||||
if balanceParams != nil {
|
||||
t.destNode = balanceParams.DestNode
|
||||
t.estimatedSize = balanceParams.EstimatedSize
|
||||
t.placementScore = balanceParams.PlacementScore
|
||||
t.forceMove = balanceParams.ForceMove
|
||||
t.timeoutSeconds = balanceParams.TimeoutSeconds
|
||||
t.placementConflicts = balanceParams.PlacementConflicts
|
||||
}
|
||||
|
||||
glog.Infof("Starting typed balance task for volume %d: %s -> %s (collection: %s, size: %d bytes)",
|
||||
t.volumeID, t.sourceServer, t.destNode, t.collection, t.estimatedSize)
|
||||
|
||||
// Log placement information
|
||||
if t.placementScore > 0 {
|
||||
glog.V(1).Infof("Placement score: %.2f", t.placementScore)
|
||||
}
|
||||
if len(t.placementConflicts) > 0 {
|
||||
glog.V(1).Infof("Placement conflicts: %v", t.placementConflicts)
|
||||
if !t.forceMove {
|
||||
return fmt.Errorf("placement conflicts detected and force_move is false: %v", t.placementConflicts)
|
||||
}
|
||||
glog.Warningf("Proceeding with balance despite conflicts (force_move=true): %v", t.placementConflicts)
|
||||
}
|
||||
|
||||
// Simulate balance operation with progress updates
|
||||
steps := []struct {
|
||||
name string
|
||||
|
||||
@@ -42,9 +42,12 @@ func RegisterBalanceTask() {
|
||||
if params == nil {
|
||||
return nil, fmt.Errorf("task parameters are required")
|
||||
}
|
||||
if len(params.Sources) == 0 {
|
||||
return nil, fmt.Errorf("at least one source is required for balance task")
|
||||
}
|
||||
return NewBalanceTask(
|
||||
fmt.Sprintf("balance-%d", params.VolumeId),
|
||||
params.Server,
|
||||
params.Sources[0].Node, // Use first source node
|
||||
params.VolumeId,
|
||||
params.Collection,
|
||||
), nil
|
||||
|
||||
Reference in New Issue
Block a user