github.com/vmware/govmomi@v0.43.0/view/task_view.go (about)

     1  /*
     2  Copyright (c) 2017 VMware, Inc. All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package view
    18  
    19  import (
    20  	"context"
    21  
    22  	"github.com/vmware/govmomi/property"
    23  	"github.com/vmware/govmomi/vim25/types"
    24  )
    25  
    26  // TaskView extends ListView such that it can follow a ManagedEntity's recentTask updates.
    27  type TaskView struct {
    28  	*ListView
    29  
    30  	Follow bool
    31  
    32  	Watch *types.ManagedObjectReference
    33  }
    34  
    35  // CreateTaskView creates a new ListView that optionally watches for a ManagedEntity's recentTask updates.
    36  func (m Manager) CreateTaskView(ctx context.Context, watch *types.ManagedObjectReference) (*TaskView, error) {
    37  	l, err := m.CreateListView(ctx, nil)
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  
    42  	tv := &TaskView{
    43  		ListView: l,
    44  		Watch:    watch,
    45  	}
    46  
    47  	return tv, nil
    48  }
    49  
    50  // Collect calls function f for each Task update.
    51  func (v TaskView) Collect(ctx context.Context, f func([]types.TaskInfo)) error {
    52  	// Using TaskHistoryCollector would be less clunky, but it isn't supported on ESX at all.
    53  	ref := v.Reference()
    54  	filter := new(property.WaitFilter).Add(ref, "Task", []string{"info"}, v.TraversalSpec())
    55  
    56  	if v.Watch != nil {
    57  		filter.Add(*v.Watch, v.Watch.Type, []string{"recentTask"})
    58  	}
    59  
    60  	pc := property.DefaultCollector(v.Client())
    61  
    62  	completed := make(map[string]bool)
    63  
    64  	return property.WaitForUpdates(ctx, pc, filter, func(updates []types.ObjectUpdate) bool {
    65  		var infos []types.TaskInfo
    66  		var prune []types.ManagedObjectReference
    67  		var tasks []types.ManagedObjectReference
    68  		var reset func()
    69  
    70  		for _, update := range updates {
    71  			for _, change := range update.ChangeSet {
    72  				if change.Name == "recentTask" {
    73  					tasks = change.Val.(types.ArrayOfManagedObjectReference).ManagedObjectReference
    74  					if len(tasks) != 0 {
    75  						reset = func() {
    76  							_, _ = v.Reset(ctx, tasks)
    77  
    78  							// Remember any tasks we've reported as complete already,
    79  							// to avoid reporting multiple times when Reset is triggered.
    80  							rtasks := make(map[string]bool)
    81  							for i := range tasks {
    82  								if _, ok := completed[tasks[i].Value]; ok {
    83  									rtasks[tasks[i].Value] = true
    84  								}
    85  							}
    86  							completed = rtasks
    87  						}
    88  					}
    89  
    90  					continue
    91  				}
    92  
    93  				info, ok := change.Val.(types.TaskInfo)
    94  				if !ok {
    95  					continue
    96  				}
    97  
    98  				if !completed[info.Task.Value] {
    99  					infos = append(infos, info)
   100  				}
   101  
   102  				if v.Follow && info.CompleteTime != nil {
   103  					prune = append(prune, info.Task)
   104  					completed[info.Task.Value] = true
   105  				}
   106  			}
   107  		}
   108  
   109  		if len(infos) != 0 {
   110  			f(infos)
   111  		}
   112  
   113  		if reset != nil {
   114  			reset()
   115  		} else if len(prune) != 0 {
   116  			_, _ = v.Remove(ctx, prune)
   117  		}
   118  
   119  		if len(tasks) != 0 && len(infos) == 0 {
   120  			return false
   121  		}
   122  
   123  		return !v.Follow
   124  	})
   125  }