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