@@ -11,6 +11,7 @@ import (
1111 "log"
1212 "net"
1313 "reflect"
14+ "sort"
1415 "strconv"
1516 "strings"
1617 "time"
@@ -41,7 +42,7 @@ type VirtualMachine interface {
4142 Reconfigure (spec types.VirtualMachineConfigSpec ) error
4243 Customize (spec types.CustomizationSpec ) error
4344 ResizeDisk (diskSize int64 ) ([]types.BaseVirtualDeviceConfigSpec , error )
44- WaitForIP (ctx context.Context , ipNet * net.IPNet ) (string , error )
45+ WaitForIP (ctx context.Context , ipNet * net.IPNet , adapterIndex * int ) (string , error )
4546 PowerOn () error
4647 PowerOff () error
4748 IsPoweredOff () (bool , error )
@@ -831,30 +832,136 @@ func (vm *VirtualMachineDriver) PowerOn() error {
831832 return err
832833}
833834
834- // WaitForIP waits for the virtual machine to get an IP address.
835- func (vm * VirtualMachineDriver ) WaitForIP (ctx context.Context , ipNet * net.IPNet ) (string , error ) {
836- netIP , err := vm .vm .WaitForNetIP (ctx , false )
835+ // WaitForIP waits for the virtual machine to get an IP address on a guest network adapter.
836+ // When adapterIndex is nil, returns when any ethernet adapter has a matching address (not all adapters).
837+ // When adapterIndex is set, waits only on ethernet-{index}.
838+ func (vm * VirtualMachineDriver ) WaitForIP (ctx context.Context , ipNet * net.IPNet , adapterIndex * int ) (string , error ) {
839+ if adapterIndex != nil {
840+ device := fmt .Sprintf ("ethernet-%d" , * adapterIndex )
841+ netIP , err := vm .vm .WaitForNetIP (ctx , false , device )
842+ if err != nil {
843+ return "" , err
844+ }
845+ return selectIPFromNetIP (netIP , ipNet ), nil
846+ }
847+
848+ return vm .waitForAnyNetIP (ctx , ipNet )
849+ }
850+
851+ func (vm * VirtualMachineDriver ) waitForAnyNetIP (ctx context.Context , ipNet * net.IPNet ) (string , error ) {
852+ macs , err := vm .waitForEthernetMACs (ctx )
837853 if err != nil {
838854 return "" , err
839855 }
840856
841- for _ , ips := range netIP {
842- for _ , ip := range ips {
843- parseIP := net .ParseIP (ip )
844- if ipNet != nil && ! ipNet .Contains (parseIP ) {
845- // IP address is not in the expected range.
857+ p := property .DefaultCollector (vm .vm .Client ())
858+ var ip string
859+ err = property .Wait (ctx , p , vm .vm .Reference (), []string {"guest.net" }, func (pc []types.PropertyChange ) bool {
860+ netIP := guestNetIPFromPropertyChange (pc , macs , false )
861+ if candidate := selectIPFromNetIP (netIP , ipNet ); candidate != "" {
862+ ip = candidate
863+ return true
864+ }
865+ return false
866+ })
867+ if err != nil {
868+ return "" , err
869+ }
870+
871+ return ip , nil
872+ }
873+
874+ func (vm * VirtualMachineDriver ) waitForEthernetMACs (ctx context.Context ) (map [string ][]string , error ) {
875+ macs := make (map [string ][]string )
876+ p := property .DefaultCollector (vm .vm .Client ())
877+
878+ err := property .Wait (ctx , p , vm .vm .Reference (), []string {"config.hardware.device" }, func (pc []types.PropertyChange ) bool {
879+ for _ , c := range pc {
880+ if c .Op != types .PropertyChangeOpAssign {
846881 continue
847882 }
848- // Default to IPv4 if no IPNet is provided.
849- if ipNet == nil && parseIP .To4 () == nil {
883+
884+ devices := object .VirtualDeviceList (c .Val .(types.ArrayOfVirtualDevice ).VirtualDevice )
885+ for _ , d := range devices {
886+ if nic , ok := d .(types.BaseVirtualEthernetCard ); ok {
887+ mac := strings .ToLower (nic .GetVirtualEthernetCard ().MacAddress )
888+ if mac == "" {
889+ return false
890+ }
891+ macs [mac ] = nil
892+ }
893+ }
894+ }
895+
896+ return true
897+ })
898+
899+ return macs , err
900+ }
901+
902+ func guestNetIPFromPropertyChange (pc []types.PropertyChange , macs map [string ][]string , v4 bool ) map [string ][]string {
903+ netIP := make (map [string ][]string , len (macs ))
904+ for mac := range macs {
905+ netIP [mac ] = nil
906+ }
907+
908+ for _ , c := range pc {
909+ if c .Op != types .PropertyChangeOpAssign {
910+ continue
911+ }
912+
913+ nics := c .Val .(types.ArrayOfGuestNicInfo ).GuestNicInfo
914+ for _ , nic := range nics {
915+ mac := strings .ToLower (nic .MacAddress )
916+ if mac == "" || nic .IpConfig == nil {
850917 continue
851918 }
852- return ip , nil
919+
920+ for _ , addr := range nic .IpConfig .IpAddress {
921+ if _ , ok := macs [mac ]; ! ok {
922+ continue
923+ }
924+ if v4 && net .ParseIP (addr .IpAddress ).To4 () == nil {
925+ continue
926+ }
927+ netIP [mac ] = append (netIP [mac ], addr .IpAddress )
928+ }
853929 }
854930 }
855931
856- // Unable to find an IP address.
857- return "" , nil
932+ return netIP
933+ }
934+
935+ func selectIPFromNetIP (netIP map [string ][]string , ipNet * net.IPNet ) string {
936+ keys := make ([]string , 0 , len (netIP ))
937+ for mac := range netIP {
938+ keys = append (keys , mac )
939+ }
940+ sort .Strings (keys )
941+
942+ for _ , mac := range keys {
943+ for _ , ip := range netIP [mac ] {
944+ if ipMatchesFilter (ip , ipNet ) {
945+ return ip
946+ }
947+ }
948+ }
949+
950+ return ""
951+ }
952+
953+ func ipMatchesFilter (ip string , ipNet * net.IPNet ) bool {
954+ parseIP := net .ParseIP (ip )
955+ if parseIP == nil {
956+ return false
957+ }
958+ if ipNet != nil && ! ipNet .Contains (parseIP ) {
959+ return false
960+ }
961+ if ipNet == nil && parseIP .To4 () == nil {
962+ return false
963+ }
964+ return true
858965}
859966
860967// PowerOff stops the virtual machine and waits for the operation to complete.
0 commit comments