github.com/vmware/govmomi@v0.51.0/object/virtual_machine_property_test.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 object_test 6 7 import ( 8 "context" 9 "reflect" 10 "sync" 11 "testing" 12 13 "github.com/vmware/govmomi/object" 14 "github.com/vmware/govmomi/property" 15 "github.com/vmware/govmomi/simulator" 16 "github.com/vmware/govmomi/view" 17 "github.com/vmware/govmomi/vim25" 18 "github.com/vmware/govmomi/vim25/mo" 19 "github.com/vmware/govmomi/vim25/types" 20 ) 21 22 func TestVirtualMachinePropertyExtraConfig(t *testing.T) { 23 key := "guestinfo.test" 24 val := func(v types.AnyType) types.OptionValue { 25 return types.OptionValue{Key: key, Value: v} 26 } 27 f := func(item string) string { 28 return (&mo.Field{Path: "config.extraConfig", Key: key, Item: item}).String() 29 } 30 31 tests := []types.PropertyChange{ 32 {Name: f(""), Val: val("111"), Op: types.PropertyChangeOpAdd}, 33 {Name: f(""), Val: val("222"), Op: types.PropertyChangeOpAssign}, 34 {Name: f("key"), Val: val("333"), Op: types.PropertyChangeOpAssign}, 35 {Name: f("value"), Val: val("444"), Op: types.PropertyChangeOpAssign}, 36 {Name: f(""), Val: val(""), Op: types.PropertyChangeOpRemove}, 37 } 38 39 expect := map[string]string{ 40 f("key"): key, 41 f("value"): "444", 42 } 43 44 simulator.Test(func(ctx context.Context, c *vim25.Client) { 45 kind := []string{"VirtualMachine"} 46 47 m := view.NewManager(c) 48 v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, kind, true) 49 if err != nil { 50 t.Fatal(err) 51 } 52 defer v.Destroy(ctx) 53 54 refs, err := v.Find(ctx, kind, property.Match{}) 55 if err != nil { 56 t.Fatal(err) 57 } 58 vm := object.NewVirtualMachine(c, refs[0]) 59 60 pc := property.DefaultCollector(c) 61 62 for _, test := range tests { 63 t.Logf("%s: %s", test.Op, test.Name) 64 update := make(chan bool) 65 parked := sync.OnceFunc(func() { update <- true }) 66 67 var change *types.PropertyChange 68 cb := func(updates []types.ObjectUpdate) bool { 69 parked() 70 update := updates[0] 71 if update.Kind != types.ObjectUpdateKindModify { 72 return false 73 } 74 change = &update.ChangeSet[0] 75 if change.Op != test.Op { 76 t.Logf("ignore: change Op=%s, test Op=%s", change.Op, test.Op) 77 return false 78 } 79 return true 80 } 81 82 filter := new(property.WaitFilter) 83 filter.Add(v.Reference(), kind[0], []string{test.Name}, v.TraversalSpec()) 84 go func() { 85 werr := property.WaitForUpdates(ctx, pc, filter, cb) 86 if werr != nil { 87 t.Log(werr) 88 } 89 update <- true 90 }() 91 <-update // wait until above go func is parked in WaitForUpdatesEx() 92 93 opt := test.Val.(types.OptionValue) 94 spec := types.VirtualMachineConfigSpec{ 95 ExtraConfig: []types.BaseOptionValue{&opt}, 96 } 97 task, err := vm.Reconfigure(ctx, spec) 98 if err != nil { 99 t.Fatal(err) 100 } 101 if err := task.Wait(ctx); err != nil { 102 t.Fatal(err) 103 } 104 <-update // wait until update is received (cb returns true) 105 106 if change == nil { 107 t.Fatal("no change") 108 } 109 110 if change.Name != test.Name { 111 t.Errorf("Name: %s", change.Name) 112 } 113 114 if change.Op != test.Op { 115 t.Errorf("Op: %s", change.Op) 116 } 117 118 if change.Op == types.PropertyChangeOpRemove { 119 if change.Val != nil { 120 t.Errorf("Val: %#v", change.Val) 121 } 122 continue 123 } 124 125 switch change.Val.(type) { 126 case types.OptionValue: 127 if !reflect.DeepEqual(change.Val, test.Val) { 128 t.Errorf("change.Val: %#v", change.Val) 129 t.Errorf("test.Val: %#v", test.Val) 130 } 131 case string: 132 if expect[change.Name] != change.Val { 133 t.Errorf("Val: %s", change.Val) 134 } 135 default: 136 t.Errorf("unexpected type: %T", change.Val) 137 } 138 } 139 }) 140 } 141 142 func TestVirtualMachinePropertyDevice(t *testing.T) { 143 key := int32(3000) 144 145 f := func(item string) string { 146 return (&mo.Field{Path: "config.hardware.device", Key: key, Item: item}).String() 147 } 148 149 info := types.Description{Label: "cdrom-3000", Summary: "cdrom-3000"} 150 151 tests := []types.PropertyChange{ 152 {Name: f(""), Val: nil, Op: types.PropertyChangeOpAdd}, 153 {Name: f(""), Val: nil, Op: types.PropertyChangeOpAssign}, 154 {Name: f("deviceInfo"), Val: info, Op: types.PropertyChangeOpAssign}, 155 {Name: f("deviceInfo.label"), Val: info.Label, Op: types.PropertyChangeOpAssign}, 156 {Name: f(""), Val: nil, Op: types.PropertyChangeOpRemove}, 157 } 158 159 simulator.Test(func(ctx context.Context, c *vim25.Client) { 160 kind := []string{"VirtualMachine"} 161 162 m := view.NewManager(c) 163 v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, kind, true) 164 if err != nil { 165 t.Fatal(err) 166 } 167 defer v.Destroy(ctx) 168 169 refs, err := v.Find(ctx, kind, property.Match{}) 170 if err != nil { 171 t.Fatal(err) 172 } 173 vm := object.NewVirtualMachine(c, refs[0]) 174 175 pc := property.DefaultCollector(c) 176 177 for _, test := range tests { 178 t.Logf("%s: %s", test.Op, test.Name) 179 update := make(chan bool) 180 parked := sync.OnceFunc(func() { update <- true }) 181 182 var change *types.PropertyChange 183 cb := func(updates []types.ObjectUpdate) bool { 184 parked() 185 update := updates[0] 186 if update.Kind != types.ObjectUpdateKindModify { 187 return false 188 } 189 change = &update.ChangeSet[0] 190 if change.Op != test.Op { 191 t.Logf("ignore: change Op=%s, test Op=%s", change.Op, test.Op) 192 return false 193 } 194 return true 195 } 196 197 filter := new(property.WaitFilter) 198 filter.Add(v.Reference(), kind[0], []string{test.Name}, v.TraversalSpec()) 199 go func() { 200 werr := property.WaitForUpdates(ctx, pc, filter, cb) 201 if werr != nil { 202 t.Log(werr) 203 } 204 update <- true 205 }() 206 <-update // wait until above go func is parked in WaitForUpdatesEx() 207 208 device, err := vm.Device(ctx) 209 if err != nil { 210 t.Fatal(err) 211 } 212 213 switch test.Op { 214 case types.PropertyChangeOpAdd: 215 ide, err := device.FindIDEController("") 216 if err != nil { 217 t.Fatal(err) 218 } 219 cdrom, err := device.CreateCdrom(ide) 220 if err != nil { 221 t.Fatal(err) 222 } 223 cdrom.GetVirtualDevice().Key = key 224 if err = vm.AddDevice(ctx, cdrom); err != nil { 225 t.Fatal(err) 226 } 227 case types.PropertyChangeOpAssign: 228 cdrom := device.FindByKey(key) 229 if err = vm.EditDevice(ctx, cdrom); err != nil { 230 t.Fatal(err) 231 } 232 case types.PropertyChangeOpRemove: 233 cdrom := device.FindByKey(key) 234 if err = vm.RemoveDevice(ctx, false, cdrom); err != nil { 235 t.Fatal(err) 236 } 237 } 238 <-update // wait until update is received (cb returns true) 239 240 if change == nil { 241 t.Fatal("no change") 242 } 243 244 if change.Name != test.Name { 245 t.Errorf("Name: %s", change.Name) 246 } 247 248 if change.Op != test.Op { 249 t.Errorf("Op: %s", change.Op) 250 } 251 252 if change.Op == types.PropertyChangeOpRemove { 253 if change.Val != nil { 254 t.Errorf("Val: %#v", change.Val) 255 } 256 continue 257 } 258 259 if test.Val != nil { 260 if !reflect.DeepEqual(change.Val, test.Val) { 261 t.Errorf("change.Val: %#v", change.Val) 262 t.Errorf("test.Val: %#v", test.Val) 263 } 264 } else { 265 if _, ok := change.Val.(types.VirtualCdrom); !ok { 266 t.Errorf("unexpected type: %T", change.Val) 267 } 268 } 269 } 270 }) 271 }