wait.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. /*
  2. Copyright (c) 2015-2017 VMware, Inc. All Rights Reserved.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package property
  14. import (
  15. "context"
  16. "github.com/vmware/govmomi/vim25/methods"
  17. "github.com/vmware/govmomi/vim25/types"
  18. )
  19. // WaitFilter provides helpers to construct a types.CreateFilter for use with property.Wait
  20. type WaitFilter struct {
  21. types.CreateFilter
  22. Options *types.WaitOptions
  23. }
  24. // Add a new ObjectSpec and PropertySpec to the WaitFilter
  25. func (f *WaitFilter) Add(obj types.ManagedObjectReference, kind string, ps []string, set ...types.BaseSelectionSpec) *WaitFilter {
  26. spec := types.ObjectSpec{
  27. Obj: obj,
  28. SelectSet: set,
  29. }
  30. pset := types.PropertySpec{
  31. Type: kind,
  32. PathSet: ps,
  33. }
  34. if len(ps) == 0 {
  35. pset.All = types.NewBool(true)
  36. }
  37. f.Spec.ObjectSet = append(f.Spec.ObjectSet, spec)
  38. f.Spec.PropSet = append(f.Spec.PropSet, pset)
  39. return f
  40. }
  41. // Wait creates a new WaitFilter and calls the specified function for each ObjectUpdate via WaitForUpdates
  42. func Wait(ctx context.Context, c *Collector, obj types.ManagedObjectReference, ps []string, f func([]types.PropertyChange) bool) error {
  43. filter := new(WaitFilter).Add(obj, obj.Type, ps)
  44. return WaitForUpdates(ctx, c, filter, func(updates []types.ObjectUpdate) bool {
  45. for _, update := range updates {
  46. if f(update.ChangeSet) {
  47. return true
  48. }
  49. }
  50. return false
  51. })
  52. }
  53. // WaitForUpdates waits for any of the specified properties of the specified managed
  54. // object to change. It calls the specified function for every update it
  55. // receives. If this function returns false, it continues waiting for
  56. // subsequent updates. If this function returns true, it stops waiting and
  57. // returns.
  58. //
  59. // To only receive updates for the specified managed object, the function
  60. // creates a new property collector and calls CreateFilter. A new property
  61. // collector is required because filters can only be added, not removed.
  62. //
  63. // If the Context is canceled, a call to CancelWaitForUpdates() is made and its error value is returned.
  64. // The newly created collector is destroyed before this function returns (both
  65. // in case of success or error).
  66. //
  67. func WaitForUpdates(ctx context.Context, c *Collector, filter *WaitFilter, f func([]types.ObjectUpdate) bool) error {
  68. p, err := c.Create(ctx)
  69. if err != nil {
  70. return err
  71. }
  72. // Attempt to destroy the collector using the background context, as the
  73. // specified context may have timed out or have been canceled.
  74. defer p.Destroy(context.Background())
  75. err = p.CreateFilter(ctx, filter.CreateFilter)
  76. if err != nil {
  77. return err
  78. }
  79. req := types.WaitForUpdatesEx{
  80. This: p.Reference(),
  81. Options: filter.Options,
  82. }
  83. for {
  84. res, err := methods.WaitForUpdatesEx(ctx, p.roundTripper, &req)
  85. if err != nil {
  86. if ctx.Err() == context.Canceled {
  87. werr := p.CancelWaitForUpdates(context.Background())
  88. return werr
  89. }
  90. return err
  91. }
  92. set := res.Returnval
  93. if set == nil {
  94. if req.Options != nil && req.Options.MaxWaitSeconds != nil {
  95. return nil // WaitOptions.MaxWaitSeconds exceeded
  96. }
  97. // Retry if the result came back empty
  98. continue
  99. }
  100. req.Version = set.Version
  101. for _, fs := range set.FilterSet {
  102. if f(fs.ObjectSet) {
  103. return nil
  104. }
  105. }
  106. }
  107. }