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  }