func TestWaitForUpdates(t *testing.T) { folder := esx.RootFolder s := New(NewServiceInstance(esx.ServiceContent, folder)) ts := s.NewServer() defer ts.Close() ctx := context.Background() c, err := govmomi.NewClient(ctx, ts.URL, true) if err != nil { t.Fatal(err) } cb := func(once bool) func([]types.PropertyChange) bool { return func(pc []types.PropertyChange) bool { if len(pc) != 1 { t.Fail() } c := pc[0] if c.Op != types.PropertyChangeOpAssign { t.Fail() } if c.Name != "name" { t.Fail() } if c.Val.(string) != folder.Name { t.Fail() } return once } } pc := property.DefaultCollector(c.Client) props := []string{"name"} err = property.Wait(ctx, pc, folder.Reference(), props, cb(true)) if err != nil { t.Error(err) } // incremental updates not yet suppported err = property.Wait(ctx, pc, folder.Reference(), props, cb(false)) if err == nil { t.Error("expected error") } // test object not found Map.Remove(folder.Reference()) err = property.Wait(ctx, pc, folder.Reference(), props, cb(true)) if err == nil { t.Error("expected error") } }
func (v VirtualMachine) WaitForIP(ctx context.Context) (string, error) { var ip string p := property.DefaultCollector(v.c) err := property.Wait(ctx, p, v.Reference(), []string{"guest.ipAddress"}, func(pc []types.PropertyChange) bool { for _, c := range pc { if c.Name != "guest.ipAddress" { continue } if c.Op != types.PropertyChangeOpAssign { continue } if c.Val == nil { continue } ip = c.Val.(string) return true } return false }) if err != nil { return "", err } return ip, nil }
// WaitForMac will wait until VM get mac for all attached nics. // Returns map "Virtual Network Name": "nic MAC address" func (vm VirtualMachine) WaitForMAC(ctx context.Context) (map[string]string, error) { devices, err := vm.Device(ctx) if err != nil { log.Errorf("Unable to get device listing for VM") return nil, err } nics := devices.SelectByType(&types.VirtualEthernetCard{}) macs := make(map[string]string) // device name:network name nicMappings := make(map[string]string) for _, nic := range nics { if n, ok := nic.(types.BaseVirtualEthernetCard); ok { netName, err := vm.getNetworkName(ctx, n) if err != nil { log.Errorf("failed to get network name: %s", err) return nil, err } macs[netName] = "" nicMappings[devices.Name(nic)] = netName } else { log.Errorf("Failed to get network name of vNIC: %v", nic) return nil, err } } p := property.DefaultCollector(vm.Session.Vim25()) // Wait for all NICs to have a MacAddress, which may not be generated yet. err = property.Wait(ctx, p, vm.Reference(), []string{"config.hardware.device"}, func(pc []types.PropertyChange) bool { for _, c := range pc { if c.Op != types.PropertyChangeOpAssign { continue } changedDevices := c.Val.(types.ArrayOfVirtualDevice).VirtualDevice for _, device := range changedDevices { if nic, ok := device.(types.BaseVirtualEthernetCard); ok { mac := nic.GetVirtualEthernetCard().MacAddress if mac == "" { continue } netName := nicMappings[devices.Name(device)] macs[netName] = mac } } } for key, value := range macs { if value == "" { log.Debugf("Didn't get mac address for nic on %s, continue", key) return false } } return true }) return macs, err }
func (p *eventProcessor) run(ctx context.Context, tail bool) error { if len(p.tailers) == 0 { return nil } var err error var collectors []types.ManagedObjectReference for _, t := range p.tailers { collectors = append(collectors, t.collector) } if len(p.tailers) > 1 { // create and populate a ListView viewMgr := view.NewManager(p.mgr.Client()) var listView *view.ListView listView, err = viewMgr.CreateListView(ctx, collectors) if err != nil { return err } count := 0 // Retrieve the property from the objects in the ListView err = property.WaitForView(ctx, property.DefaultCollector(p.mgr.Client()), listView.Reference(), collectors[0], []string{latestPageProp}, func(c types.ManagedObjectReference, pc []types.PropertyChange) bool { if err = p.process(c, pc); err != nil { return false } count++ if count == len(collectors) && !tail { return true } return false }) return err } // only one object to follow err = property.Wait(ctx, property.DefaultCollector(p.mgr.Client()), collectors[0], []string{latestPageProp}, func(pc []types.PropertyChange) bool { if err = p.process(collectors[0], pc); err != nil { return false } if !tail { return true } return false }) return err }
// WaitForExtraConfig waits until key shows up with the expected value inside the ExtraConfig func (vm *VirtualMachine) WaitForExtraConfig(ctx context.Context, waitFunc func(pc []types.PropertyChange) bool) error { // Get the default collector p := property.DefaultCollector(vm.Vim25()) // Wait on config.extraConfig // https://www.vmware.com/support/developer/vc-sdk/visdk2xpubs/ReferenceGuide/vim.vm.ConfigInfo.html err := property.Wait(ctx, p, vm.Reference(), []string{"config.extraConfig"}, waitFunc) if err != nil { log.Errorf("Property collector error: %s", err) return err } return nil }
// Wait waits for a task to finish with either success or failure. It does so // by waiting for the "info" property of task managed object to change. The // function returns when it finds the task in the "success" or "error" state. // In the former case, the return value is nil. In the latter case the return // value is an instance of this package's Error struct. // // Any error returned while waiting for property changes causes the function to // return immediately and propagate the error. // // If the progress.Sinker argument is specified, any progress updates for the // task are sent here. The completion percentage is passed through directly. // The detail for the progress update is set to an empty string. If the task // finishes in the error state, the error instance is passed through as well. // Note that this error is the same error that is returned by this function. // func Wait(ctx context.Context, ref types.ManagedObjectReference, pc *property.Collector, s progress.Sinker) (*types.TaskInfo, error) { cb := &taskCallback{} // Include progress sink if specified if s != nil { cb.ch = s.Sink() defer close(cb.ch) } err := property.Wait(ctx, pc, ref, []string{"info"}, cb.fn) if err != nil { return nil, err } return cb.info, cb.err }
func (o HttpNfcLease) Wait(ctx context.Context) (*types.HttpNfcLeaseInfo, error) { var lease mo.HttpNfcLease pc := property.DefaultCollector(o.c) err := property.Wait(ctx, pc, o.Reference(), []string{"state", "info", "error"}, func(pc []types.PropertyChange) bool { done := false for _, c := range pc { if c.Val == nil { continue } switch c.Name { case "error": val := c.Val.(types.LocalizedMethodFault) lease.Error = &val done = true case "info": val := c.Val.(types.HttpNfcLeaseInfo) lease.Info = &val case "state": lease.State = c.Val.(types.HttpNfcLeaseState) if lease.State != types.HttpNfcLeaseStateInitializing { done = true } } } return done }) if err != nil { return nil, err } if lease.State == types.HttpNfcLeaseStateReady { return lease.Info, nil } if lease.Error != nil { return nil, errors.New(lease.Error.LocalizedMessage) } return nil, fmt.Errorf("unexpected nfc lease state: %s", lease.State) }
// Wait for the VirtualMachine to change to the desired power state. func (v VirtualMachine) WaitForPowerState(ctx context.Context, state types.VirtualMachinePowerState) error { p := property.DefaultCollector(v.c) err := property.Wait(ctx, p, v.Reference(), []string{PropRuntimePowerState}, func(pc []types.PropertyChange) bool { for _, c := range pc { if c.Name != PropRuntimePowerState { continue } if c.Val == nil { continue } ps := c.Val.(types.VirtualMachinePowerState) if ps == state { return true } } return false }) return err }
func singleObjectEvents(ctx context.Context, m Manager, object types.ManagedObjectReference, pageSize int32, tail bool, force bool, prop []string, f func([]types.BaseEvent) error) error { filter := types.EventFilterSpec{ Entity: &types.EventFilterSpecByEntity{ Entity: object, Recursion: types.EventFilterSpecRecursionOptionAll, }, } collector, err := m.CreateCollectorForEvents(ctx, filter) if err != nil { return fmt.Errorf("[%#v] %s", object, err) } defer collector.Destroy(ctx) err = collector.SetPageSize(ctx, pageSize) if err != nil { return err } return property.Wait(ctx, property.DefaultCollector(m.Client()), collector.Reference(), prop, func(pc []types.PropertyChange) bool { for _, u := range pc { if u.Name != prop[0] { continue } if u.Val == nil { continue } f(u.Val.(types.ArrayOfEvent).Event) } if !tail { return true } return false }) }
// WaitForNetIP waits for the VM guest.net property to report an IP address for all VM NICs. // Only consider IPv4 addresses if the v4 param is true. // Returns a map with MAC address as the key and IP address list as the value. func (v VirtualMachine) WaitForNetIP(ctx context.Context, v4 bool) (map[string][]string, error) { macs := make(map[string][]string) p := property.DefaultCollector(v.c) // Wait for all NICs to have a MacAddress, which may not be generated yet. err := property.Wait(ctx, p, v.Reference(), []string{"config.hardware.device"}, func(pc []types.PropertyChange) bool { for _, c := range pc { if c.Op != types.PropertyChangeOpAssign { continue } devices := c.Val.(types.ArrayOfVirtualDevice).VirtualDevice for _, device := range devices { if nic, ok := device.(types.BaseVirtualEthernetCard); ok { mac := nic.GetVirtualEthernetCard().MacAddress if mac == "" { return false } macs[mac] = nil } } } return true }) err = property.Wait(ctx, p, v.Reference(), []string{"guest.net"}, func(pc []types.PropertyChange) bool { for _, c := range pc { if c.Op != types.PropertyChangeOpAssign { continue } nics := c.Val.(types.ArrayOfGuestNicInfo).GuestNicInfo for _, nic := range nics { mac := nic.MacAddress if mac == "" || nic.IpConfig == nil { continue } for _, ip := range nic.IpConfig.IpAddress { if _, ok := macs[mac]; !ok { continue // Ignore any that don't correspond to a VM device } if v4 && net.ParseIP(ip.IpAddress).To4() == nil { continue // Ignore non IPv4 address } macs[mac] = append(macs[mac], ip.IpAddress) } } } for _, ips := range macs { if len(ips) == 0 { return false } } return true }) if err != nil { return nil, err } return macs, nil }
// Wait dispatches to property.Wait. func (c *Client) Wait(ctx context.Context, obj types.ManagedObjectReference, ps []string, f func([]types.PropertyChange) bool) error { return property.Wait(ctx, c.PropertyCollector(), obj, ps, f) }
func TestCreateVm(t *testing.T) { ctx := context.Background() for _, model := range []*Model{ESX(), VPX()} { defer model.Remove() err := model.Create() if err != nil { t.Fatal(err) } s := model.Service.NewServer() defer s.Close() c, err := govmomi.NewClient(ctx, s.URL, true) if err != nil { t.Fatal(err) } p := property.DefaultCollector(c.Client) spec := types.VirtualMachineConfigSpec{ // Note: real ESX allows the VM to be created without a GuestId, // but will power on will fail. GuestId: string(types.VirtualMachineGuestOsIdentifierOtherGuest), } steps := []func(){ func() { spec.Name = "test" }, func() { spec.Files = &types.VirtualMachineFileInfo{ VmPathName: fmt.Sprintf("[LocalDS_0] %s/%s.vmx", spec.Name, spec.Name), } }, } finder := find.NewFinder(c.Client, false) dc, err := finder.DefaultDatacenter(ctx) if err != nil { t.Fatal(err) } finder.SetDatacenter(dc) folders, err := dc.Folders(ctx) if err != nil { t.Fatal(err) } hosts, err := finder.HostSystemList(ctx, "*/*") if err != nil { t.Fatal(err) } nhosts := len(hosts) host := hosts[rand.Intn(nhosts)] pool, err := host.ResourcePool(ctx) if err != nil { t.Fatal(err) } if nhosts == 1 { // test the default path against the ESX model host = nil } vmFolder := folders.VmFolder // expecting CreateVM to fail until all steps are taken for _, step := range steps { task, cerr := vmFolder.CreateVM(ctx, spec, pool, host) if cerr != nil { t.Fatal(err) } _, cerr = task.WaitForResult(ctx, nil) if cerr == nil { t.Error("expected error") } step() } task, err := vmFolder.CreateVM(ctx, spec, pool, host) if err != nil { t.Fatal(err) } info, err := task.WaitForResult(ctx, nil) if err != nil { t.Fatal(err) } vm := object.NewVirtualMachine(c.Client, info.Result.(types.ManagedObjectReference)) name, err := vm.ObjectName(ctx) if err != nil { t.Fatal(err) } if name != spec.Name { t.Errorf("name=%s", name) } _, err = vm.Device(ctx) if err != nil { t.Fatal(err) } recreate := func(context.Context) (*object.Task, error) { return vmFolder.CreateVM(ctx, spec, pool, nil) } ops := []struct { method func(context.Context) (*object.Task, error) state types.VirtualMachinePowerState fail bool }{ // Powered off by default {nil, types.VirtualMachinePowerStatePoweredOff, false}, // Create with same .vmx path should fail {recreate, "", true}, // Off -> On == ok {vm.PowerOn, types.VirtualMachinePowerStatePoweredOn, false}, // On -> On == fail {vm.PowerOn, types.VirtualMachinePowerStatePoweredOn, true}, // On -> Off == ok {vm.PowerOff, types.VirtualMachinePowerStatePoweredOff, false}, // Off -> Off == fail {vm.PowerOff, types.VirtualMachinePowerStatePoweredOff, true}, // Off -> On == ok {vm.PowerOn, types.VirtualMachinePowerStatePoweredOn, false}, // Destroy == fail (power is On) {vm.Destroy, types.VirtualMachinePowerStatePoweredOn, true}, // On -> Off == ok {vm.PowerOff, types.VirtualMachinePowerStatePoweredOff, false}, // Destroy == ok (power is Off) {vm.Destroy, "", false}, } for i, op := range ops { if op.method != nil { task, err = op.method(ctx) if err != nil { t.Fatal(err) } err = task.Wait(ctx) if op.fail { if err == nil { t.Errorf("%d: expected error", i) } } else { if err != nil { t.Errorf("%d: %s", i, err) } } } if len(op.state) != 0 { state, err := vm.PowerState(ctx) if err != nil { t.Fatal(err) } if state != op.state { t.Errorf("state=%s", state) } err = property.Wait(ctx, p, vm.Reference(), []string{object.PropRuntimePowerState}, func(pc []types.PropertyChange) bool { for _, c := range pc { switch v := c.Val.(type) { case types.VirtualMachinePowerState: if v != op.state { t.Errorf("state=%s", v) } default: t.Errorf("unexpected type %T", v) } } return false }) } } } }