@@ -10,6 +10,7 @@ import (
1010 "fmt"
1111 "log"
1212 "net"
13+ "sort"
1314 "strconv"
1415 "strings"
1516 "time"
@@ -40,7 +41,7 @@ type VirtualMachine interface {
4041 Reconfigure (spec types.VirtualMachineConfigSpec ) error
4142 Customize (spec types.CustomizationSpec ) error
4243 ResizeDisk (diskSize int64 ) ([]types.BaseVirtualDeviceConfigSpec , error )
43- WaitForIP (ctx context.Context , ipNet * net.IPNet ) (string , error )
44+ WaitForIP (ctx context.Context , ipNet * net.IPNet , adapterIndex * int ) (string , error )
4445 PowerOn () error
4546 PowerOff () error
4647 IsPoweredOff () (bool , error )
@@ -803,30 +804,136 @@ func (vm *VirtualMachineDriver) PowerOn() error {
803804 return err
804805}
805806
806- // WaitForIP waits for the virtual machine to get an IP address.
807- func (vm * VirtualMachineDriver ) WaitForIP (ctx context.Context , ipNet * net.IPNet ) (string , error ) {
808- netIP , err := vm .vm .WaitForNetIP (ctx , false )
807+ // WaitForIP waits for the virtual machine to get an IP address on a guest network adapter.
808+ // When adapterIndex is nil, returns when any ethernet adapter has a matching address (not all adapters).
809+ // When adapterIndex is set, waits only on ethernet-{index}.
810+ func (vm * VirtualMachineDriver ) WaitForIP (ctx context.Context , ipNet * net.IPNet , adapterIndex * int ) (string , error ) {
811+ if adapterIndex != nil {
812+ device := fmt .Sprintf ("ethernet-%d" , * adapterIndex )
813+ netIP , err := vm .vm .WaitForNetIP (ctx , false , device )
814+ if err != nil {
815+ return "" , err
816+ }
817+ return selectIPFromNetIP (netIP , ipNet ), nil
818+ }
819+
820+ return vm .waitForAnyNetIP (ctx , ipNet )
821+ }
822+
823+ func (vm * VirtualMachineDriver ) waitForAnyNetIP (ctx context.Context , ipNet * net.IPNet ) (string , error ) {
824+ macs , err := vm .waitForEthernetMACs (ctx )
809825 if err != nil {
810826 return "" , err
811827 }
812828
813- for _ , ips := range netIP {
814- for _ , ip := range ips {
815- parseIP := net .ParseIP (ip )
816- if ipNet != nil && ! ipNet .Contains (parseIP ) {
817- // IP address is not in the expected range.
829+ p := property .DefaultCollector (vm .vm .Client ())
830+ var ip string
831+ err = property .Wait (ctx , p , vm .vm .Reference (), []string {"guest.net" }, func (pc []types.PropertyChange ) bool {
832+ netIP := guestNetIPFromPropertyChange (pc , macs , false )
833+ if candidate := selectIPFromNetIP (netIP , ipNet ); candidate != "" {
834+ ip = candidate
835+ return true
836+ }
837+ return false
838+ })
839+ if err != nil {
840+ return "" , err
841+ }
842+
843+ return ip , nil
844+ }
845+
846+ func (vm * VirtualMachineDriver ) waitForEthernetMACs (ctx context.Context ) (map [string ][]string , error ) {
847+ macs := make (map [string ][]string )
848+ p := property .DefaultCollector (vm .vm .Client ())
849+
850+ err := property .Wait (ctx , p , vm .vm .Reference (), []string {"config.hardware.device" }, func (pc []types.PropertyChange ) bool {
851+ for _ , c := range pc {
852+ if c .Op != types .PropertyChangeOpAssign {
818853 continue
819854 }
820- // Default to IPv4 if no IPNet is provided.
821- if ipNet == nil && parseIP .To4 () == nil {
855+
856+ devices := object .VirtualDeviceList (c .Val .(types.ArrayOfVirtualDevice ).VirtualDevice )
857+ for _ , d := range devices {
858+ if nic , ok := d .(types.BaseVirtualEthernetCard ); ok {
859+ mac := strings .ToLower (nic .GetVirtualEthernetCard ().MacAddress )
860+ if mac == "" {
861+ return false
862+ }
863+ macs [mac ] = nil
864+ }
865+ }
866+ }
867+
868+ return true
869+ })
870+
871+ return macs , err
872+ }
873+
874+ func guestNetIPFromPropertyChange (pc []types.PropertyChange , macs map [string ][]string , v4 bool ) map [string ][]string {
875+ netIP := make (map [string ][]string , len (macs ))
876+ for mac := range macs {
877+ netIP [mac ] = nil
878+ }
879+
880+ for _ , c := range pc {
881+ if c .Op != types .PropertyChangeOpAssign {
882+ continue
883+ }
884+
885+ nics := c .Val .(types.ArrayOfGuestNicInfo ).GuestNicInfo
886+ for _ , nic := range nics {
887+ mac := strings .ToLower (nic .MacAddress )
888+ if mac == "" || nic .IpConfig == nil {
822889 continue
823890 }
824- return ip , nil
891+
892+ for _ , addr := range nic .IpConfig .IpAddress {
893+ if _ , ok := macs [mac ]; ! ok {
894+ continue
895+ }
896+ if v4 && net .ParseIP (addr .IpAddress ).To4 () == nil {
897+ continue
898+ }
899+ netIP [mac ] = append (netIP [mac ], addr .IpAddress )
900+ }
825901 }
826902 }
827903
828- // Unable to find an IP address.
829- return "" , nil
904+ return netIP
905+ }
906+
907+ func selectIPFromNetIP (netIP map [string ][]string , ipNet * net.IPNet ) string {
908+ keys := make ([]string , 0 , len (netIP ))
909+ for mac := range netIP {
910+ keys = append (keys , mac )
911+ }
912+ sort .Strings (keys )
913+
914+ for _ , mac := range keys {
915+ for _ , ip := range netIP [mac ] {
916+ if ipMatchesFilter (ip , ipNet ) {
917+ return ip
918+ }
919+ }
920+ }
921+
922+ return ""
923+ }
924+
925+ func ipMatchesFilter (ip string , ipNet * net.IPNet ) bool {
926+ parseIP := net .ParseIP (ip )
927+ if parseIP == nil {
928+ return false
929+ }
930+ if ipNet != nil && ! ipNet .Contains (parseIP ) {
931+ return false
932+ }
933+ if ipNet == nil && parseIP .To4 () == nil {
934+ return false
935+ }
936+ return true
830937}
831938
832939// PowerOff stops the virtual machine and waits for the operation to complete.
0 commit comments