diff --git a/builder/vsphere/clone/config.hcl2spec.go b/builder/vsphere/clone/config.hcl2spec.go index 4d3936e78..d0c7d2c00 100644 --- a/builder/vsphere/clone/config.hcl2spec.go +++ b/builder/vsphere/clone/config.hcl2spec.go @@ -51,6 +51,7 @@ type FlatConfig struct { BootOrder *string `mapstructure:"boot_order" cty:"boot_order" hcl:"boot_order"` WaitTimeout *string `mapstructure:"ip_wait_timeout" cty:"ip_wait_timeout" hcl:"ip_wait_timeout"` SettleTimeout *string `mapstructure:"ip_settle_timeout" cty:"ip_settle_timeout" hcl:"ip_settle_timeout"` + WaitAddress *string `mapstructure:"ip_wait_address" cty:"ip_wait_address" hcl:"ip_wait_address"` Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"` PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"` SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"` @@ -154,6 +155,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "boot_order": &hcldec.AttrSpec{Name: "boot_order", Type: cty.String, Required: false}, "ip_wait_timeout": &hcldec.AttrSpec{Name: "ip_wait_timeout", Type: cty.String, Required: false}, "ip_settle_timeout": &hcldec.AttrSpec{Name: "ip_settle_timeout", Type: cty.String, Required: false}, + "ip_wait_address": &hcldec.AttrSpec{Name: "ip_wait_address", Type: cty.String, Required: false}, "communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false}, "pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false}, "ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false}, diff --git a/builder/vsphere/common/step_wait_for_ip.go b/builder/vsphere/common/step_wait_for_ip.go index d5f0db772..6c33b887c 100644 --- a/builder/vsphere/common/step_wait_for_ip.go +++ b/builder/vsphere/common/step_wait_for_ip.go @@ -7,6 +7,7 @@ import ( "context" "fmt" "log" + "net" "time" "github.com/hashicorp/packer/builder/vsphere/driver" @@ -16,7 +17,7 @@ import ( type WaitIpConfig struct { // Amount of time to wait for VM's IP, similar to 'ssh_timeout'. - // Defaults to 30m (30 minutes). See the Goang + // Defaults to 30m (30 minutes). See the Golang // [ParseDuration](https://golang.org/pkg/time/#ParseDuration) documentation // for full details. WaitTimeout time.Duration `mapstructure:"ip_wait_timeout"` @@ -27,6 +28,10 @@ type WaitIpConfig struct { // [ParseDuration](https://golang.org/pkg/time/#ParseDuration) documentation // for full details. SettleTimeout time.Duration `mapstructure:"ip_settle_timeout"` + // Set this to a CIDR address. This will cause the service to wait for an address that is contained in + // this network. Example: "192.168.0.0/24" would look at the address if it was "192.168.0.1". + WaitAddress string `mapstructure:"ip_wait_address"` + ipnet *net.IPNet // WaitTimeout is a total timeout, so even if VM changes IP frequently and it doesn't settle down we will end waiting. } @@ -45,6 +50,14 @@ func (c *WaitIpConfig) Prepare() []error { c.WaitTimeout = 30 * time.Minute } + if c.WaitAddress != "" { + var err error + _, c.ipnet, err = net.ParseCIDR(c.WaitAddress) + if err != nil { + errs = append(errs, fmt.Errorf("unable to parse \"ip_wait_address\": %w", err)) + } + } + return errs } @@ -111,7 +124,7 @@ func doGetIp(vm *driver.VirtualMachine, ctx context.Context, c *WaitIpConfig) (s interval = 1 * time.Second } loop: - ip, err := vm.WaitForIP(ctx) + ip, err := vm.WaitForIP(ctx, c.ipnet) if err != nil { return "", err } diff --git a/builder/vsphere/common/step_wait_for_ip.hcl2spec.go b/builder/vsphere/common/step_wait_for_ip.hcl2spec.go index e8ad9c442..1424a62a4 100644 --- a/builder/vsphere/common/step_wait_for_ip.hcl2spec.go +++ b/builder/vsphere/common/step_wait_for_ip.hcl2spec.go @@ -11,6 +11,7 @@ import ( type FlatWaitIpConfig struct { WaitTimeout *string `mapstructure:"ip_wait_timeout" cty:"ip_wait_timeout" hcl:"ip_wait_timeout"` SettleTimeout *string `mapstructure:"ip_settle_timeout" cty:"ip_settle_timeout" hcl:"ip_settle_timeout"` + WaitAddress *string `mapstructure:"ip_wait_address" cty:"ip_wait_address" hcl:"ip_wait_address"` } // FlatMapstructure returns a new FlatWaitIpConfig. @@ -27,6 +28,7 @@ func (*FlatWaitIpConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "ip_wait_timeout": &hcldec.AttrSpec{Name: "ip_wait_timeout", Type: cty.String, Required: false}, "ip_settle_timeout": &hcldec.AttrSpec{Name: "ip_settle_timeout", Type: cty.String, Required: false}, + "ip_wait_address": &hcldec.AttrSpec{Name: "ip_wait_address", Type: cty.String, Required: false}, } return s } diff --git a/builder/vsphere/driver/vm.go b/builder/vsphere/driver/vm.go index 0296f57b5..72673893f 100644 --- a/builder/vsphere/driver/vm.go +++ b/builder/vsphere/driver/vm.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "log" + "net" "strings" "time" @@ -491,8 +492,39 @@ func (vm *VirtualMachine) PowerOn() error { return err } -func (vm *VirtualMachine) WaitForIP(ctx context.Context) (string, error) { - return vm.vm.WaitForIP(ctx) +func (vm *VirtualMachine) WaitForIP(ctx context.Context, ipNet *net.IPNet) (string, error) { + var ip string + + p := property.DefaultCollector(vm.vm.Client()) + err := property.Wait(ctx, p, vm.vm.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 + } + + i := c.Val.(string) + if ipNet != nil && ipNet.Contains(net.ParseIP(i)) { + // ip address is not in range + continue + } + ip = i + return true + } + + return false + }) + + if err != nil { + return "", err + } + + return ip, nil } func (vm *VirtualMachine) PowerOff() error { diff --git a/builder/vsphere/driver/vm_clone_acc_test.go b/builder/vsphere/driver/vm_clone_acc_test.go index 664103f1f..062eb1d48 100644 --- a/builder/vsphere/driver/vm_clone_acc_test.go +++ b/builder/vsphere/driver/vm_clone_acc_test.go @@ -235,7 +235,7 @@ func startAndStopCheck(t *testing.T, vm *VirtualMachine, config *CloneConfig) { stopper := startVM(t, vm, config.Name) defer stopper() - switch ip, err := vm.WaitForIP(context.TODO()); { + switch ip, err := vm.WaitForIP(context.TODO(), nil); { case err != nil: t.Errorf("Cannot obtain IP address from created vm '%v': %v", config.Name, err) case net.ParseIP(ip) == nil: diff --git a/builder/vsphere/iso/config.hcl2spec.go b/builder/vsphere/iso/config.hcl2spec.go index d741e285b..c847734ae 100644 --- a/builder/vsphere/iso/config.hcl2spec.go +++ b/builder/vsphere/iso/config.hcl2spec.go @@ -72,6 +72,7 @@ type FlatConfig struct { HTTPIP *string `mapstructure:"http_ip" cty:"http_ip" hcl:"http_ip"` WaitTimeout *string `mapstructure:"ip_wait_timeout" cty:"ip_wait_timeout" hcl:"ip_wait_timeout"` SettleTimeout *string `mapstructure:"ip_settle_timeout" cty:"ip_settle_timeout" hcl:"ip_settle_timeout"` + WaitAddress *string `mapstructure:"ip_wait_address" cty:"ip_wait_address" hcl:"ip_wait_address"` Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"` PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"` SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"` @@ -196,6 +197,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "http_ip": &hcldec.AttrSpec{Name: "http_ip", Type: cty.String, Required: false}, "ip_wait_timeout": &hcldec.AttrSpec{Name: "ip_wait_timeout", Type: cty.String, Required: false}, "ip_settle_timeout": &hcldec.AttrSpec{Name: "ip_settle_timeout", Type: cty.String, Required: false}, + "ip_wait_address": &hcldec.AttrSpec{Name: "ip_wait_address", Type: cty.String, Required: false}, "communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false}, "pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false}, "ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false}, diff --git a/website/pages/partials/builder/vsphere/common/WaitIpConfig-not-required.mdx b/website/pages/partials/builder/vsphere/common/WaitIpConfig-not-required.mdx index 3c6b96739..4ce9daf8e 100644 --- a/website/pages/partials/builder/vsphere/common/WaitIpConfig-not-required.mdx +++ b/website/pages/partials/builder/vsphere/common/WaitIpConfig-not-required.mdx @@ -1,7 +1,7 @@ - `ip_wait_timeout` (duration string | ex: "1h5m2s") - Amount of time to wait for VM's IP, similar to 'ssh_timeout'. - Defaults to 30m (30 minutes). See the Goang + Defaults to 30m (30 minutes). See the Golang [ParseDuration](https://golang.org/pkg/time/#ParseDuration) documentation for full details. @@ -11,4 +11,7 @@ 5s(5 seconds). See the Golang [ParseDuration](https://golang.org/pkg/time/#ParseDuration) documentation for full details. + +- `ip_wait_address` (string) - Set this to a CIDR address. This will cause the service to wait for an address that is contained in + this network. Example: "192.168.0.0/24" would look at the address if it was "192.168.0.1". \ No newline at end of file