github.com/vmware/govmomi@v0.37.2/property/wait.go (about)

     1  /*
     2  Copyright (c) 2015-2024 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 property
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  
    23  	"github.com/vmware/govmomi/vim25/types"
    24  )
    25  
    26  // WaitOptions defines options for a property collector's WaitForUpdatesEx
    27  // method.
    28  type WaitOptions struct {
    29  	Options          *types.WaitOptions
    30  	PropagateMissing bool
    31  	Truncated        bool
    32  }
    33  
    34  // WaitFilter provides helpers to construct a types.CreateFilter for use with property.Wait
    35  type WaitFilter struct {
    36  	types.CreateFilter
    37  	WaitOptions
    38  }
    39  
    40  // Add a new ObjectSpec and PropertySpec to the WaitFilter
    41  func (f *WaitFilter) Add(obj types.ManagedObjectReference, kind string, ps []string, set ...types.BaseSelectionSpec) *WaitFilter {
    42  	spec := types.ObjectSpec{
    43  		Obj:       obj,
    44  		SelectSet: set,
    45  	}
    46  
    47  	pset := types.PropertySpec{
    48  		Type:    kind,
    49  		PathSet: ps,
    50  	}
    51  
    52  	if len(ps) == 0 {
    53  		pset.All = types.NewBool(true)
    54  	}
    55  
    56  	f.Spec.ObjectSet = append(f.Spec.ObjectSet, spec)
    57  
    58  	f.Spec.PropSet = append(f.Spec.PropSet, pset)
    59  
    60  	return f
    61  }
    62  
    63  // Wait creates a new WaitFilter and calls the specified function for each ObjectUpdate via WaitForUpdates
    64  func Wait(ctx context.Context, c *Collector, obj types.ManagedObjectReference, ps []string, f func([]types.PropertyChange) bool) error {
    65  	filter := new(WaitFilter).Add(obj, obj.Type, ps)
    66  
    67  	return WaitForUpdates(ctx, c, filter, func(updates []types.ObjectUpdate) bool {
    68  		for _, update := range updates {
    69  			if f(update.ChangeSet) {
    70  				return true
    71  			}
    72  		}
    73  
    74  		return false
    75  	})
    76  }
    77  
    78  // WaitForUpdates waits for any of the specified properties of the specified
    79  // managed object to change. It calls the specified function for every update it
    80  // receives. If this function returns false, it continues waiting for
    81  // subsequent updates. If this function returns true, it stops waiting and
    82  // returns.
    83  //
    84  // To only receive updates for the specified managed object, the function
    85  // creates a new property collector and calls CreateFilter. A new property
    86  // collector is required because filters can only be added, not removed.
    87  //
    88  // If the Context is canceled, a call to CancelWaitForUpdates() is made and its
    89  // error value is returned. The newly created collector is destroyed before this
    90  // function returns (both in case of success or error).
    91  //
    92  // By default, ObjectUpdate.MissingSet faults are not propagated to the returned
    93  // error, set WaitFilter.PropagateMissing=true to enable MissingSet fault
    94  // propagation.
    95  func WaitForUpdates(
    96  	ctx context.Context,
    97  	c *Collector,
    98  	filter *WaitFilter,
    99  	onUpdatesFn func([]types.ObjectUpdate) bool) (result error) {
   100  
   101  	pc, err := c.Create(ctx)
   102  	if err != nil {
   103  		return err
   104  	}
   105  
   106  	// Attempt to destroy the collector using the background context, as the
   107  	// specified context may have timed out or have been canceled.
   108  	defer func() {
   109  		if err := pc.Destroy(context.Background()); err != nil {
   110  			if result == nil {
   111  				result = err
   112  			} else {
   113  				result = fmt.Errorf(
   114  					"destroy property collector failed with %s after failing to wait for updates: %w",
   115  					err,
   116  					result)
   117  			}
   118  		}
   119  	}()
   120  
   121  	// Create a property filter for the property collector.
   122  	if _, err := pc.CreateFilter(ctx, filter.CreateFilter); err != nil {
   123  		return err
   124  	}
   125  
   126  	return pc.WaitForUpdatesEx(ctx, filter.WaitOptions, onUpdatesFn)
   127  }
   128  
   129  // WaitForUpdates waits for any of the specified properties of the specified
   130  // managed object to change. It calls the specified function for every update it
   131  // receives. If this function returns false, it continues waiting for
   132  // subsequent updates. If this function returns true, it stops waiting and
   133  // returns.
   134  //
   135  // If the Context is canceled, a call to CancelWaitForUpdates() is made and its
   136  // error value is returned.
   137  //
   138  // By default, ObjectUpdate.MissingSet faults are not propagated to the returned
   139  // error, set WaitFilter.PropagateMissing=true to enable MissingSet fault
   140  // propagation.
   141  func WaitForUpdatesEx(
   142  	ctx context.Context,
   143  	pc *Collector,
   144  	filter *WaitFilter,
   145  	onUpdatesFn func([]types.ObjectUpdate) bool) (result error) {
   146  
   147  	// Create a property filter for the property collector.
   148  	pf, err := pc.CreateFilter(ctx, filter.CreateFilter)
   149  	if err != nil {
   150  		return err
   151  	}
   152  
   153  	// Destroy the filter using the background context, as the specified context
   154  	// may have timed out or have been canceled.
   155  	defer func() {
   156  		if err := pf.Destroy(context.Background()); err != nil {
   157  			if result == nil {
   158  				result = err
   159  			} else {
   160  				result = fmt.Errorf(
   161  					"destroy property filter failed with %s after failing to wait for updates: %w",
   162  					err,
   163  					result)
   164  			}
   165  		}
   166  
   167  	}()
   168  
   169  	return pc.WaitForUpdatesEx(ctx, filter.WaitOptions, onUpdatesFn)
   170  }