github.com/vmware/govmomi@v0.51.0/view/task_view.go (about) 1 // © Broadcom. All Rights Reserved. 2 // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. 3 // SPDX-License-Identifier: Apache-2.0 4 5 package view 6 7 import ( 8 "context" 9 10 "github.com/vmware/govmomi/property" 11 "github.com/vmware/govmomi/vim25/types" 12 ) 13 14 // TaskView extends ListView such that it can follow a ManagedEntity's recentTask updates. 15 type TaskView struct { 16 *ListView 17 18 Follow bool 19 20 Watch *types.ManagedObjectReference 21 } 22 23 // CreateTaskView creates a new ListView that optionally watches for a ManagedEntity's recentTask updates. 24 func (m Manager) CreateTaskView(ctx context.Context, watch *types.ManagedObjectReference) (*TaskView, error) { 25 l, err := m.CreateListView(ctx, nil) 26 if err != nil { 27 return nil, err 28 } 29 30 tv := &TaskView{ 31 ListView: l, 32 Watch: watch, 33 } 34 35 return tv, nil 36 } 37 38 // Collect calls function f for each Task update. 39 func (v TaskView) Collect(ctx context.Context, f func([]types.TaskInfo)) error { 40 // Using TaskHistoryCollector would be less clunky, but it isn't supported on ESX at all. 41 ref := v.Reference() 42 filter := new(property.WaitFilter).Add(ref, "Task", []string{"info"}, v.TraversalSpec()) 43 44 if v.Watch != nil { 45 filter.Add(*v.Watch, v.Watch.Type, []string{"recentTask"}) 46 } 47 48 pc := property.DefaultCollector(v.Client()) 49 50 completed := make(map[string]bool) 51 52 return property.WaitForUpdates(ctx, pc, filter, func(updates []types.ObjectUpdate) bool { 53 var infos []types.TaskInfo 54 var prune []types.ManagedObjectReference 55 var tasks []types.ManagedObjectReference 56 var reset func() 57 58 for _, update := range updates { 59 for _, change := range update.ChangeSet { 60 if change.Name == "recentTask" { 61 tasks = change.Val.(types.ArrayOfManagedObjectReference).ManagedObjectReference 62 if len(tasks) != 0 { 63 reset = func() { 64 _, _ = v.Reset(ctx, tasks) 65 66 // Remember any tasks we've reported as complete already, 67 // to avoid reporting multiple times when Reset is triggered. 68 rtasks := make(map[string]bool) 69 for i := range tasks { 70 if _, ok := completed[tasks[i].Value]; ok { 71 rtasks[tasks[i].Value] = true 72 } 73 } 74 completed = rtasks 75 } 76 } 77 78 continue 79 } 80 81 info, ok := change.Val.(types.TaskInfo) 82 if !ok { 83 continue 84 } 85 86 if !completed[info.Task.Value] { 87 infos = append(infos, info) 88 } 89 90 if v.Follow && info.CompleteTime != nil { 91 prune = append(prune, info.Task) 92 completed[info.Task.Value] = true 93 } 94 } 95 } 96 97 if len(infos) != 0 { 98 f(infos) 99 } 100 101 if reset != nil { 102 reset() 103 } else if len(prune) != 0 { 104 _, _ = v.Remove(ctx, prune) 105 } 106 107 if len(tasks) != 0 && len(infos) == 0 { 108 return false 109 } 110 111 return !v.Follow 112 }) 113 }