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  }