diff --git a/builder/openstack/builder.go b/builder/openstack/builder.go index b25229325..ecf37cf73 100644 --- a/builder/openstack/builder.go +++ b/builder/openstack/builder.go @@ -102,6 +102,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe &StepAllocateIp{ FloatingIpPool: b.config.FloatingIpPool, FloatingIp: b.config.FloatingIp, + ReuseIps: b.config.ReuseIps, }, &communicator.StepConnect{ Config: &b.config.RunConfig.Comm, diff --git a/builder/openstack/run_config.go b/builder/openstack/run_config.go index 114936a83..f0bb4dc7c 100644 --- a/builder/openstack/run_config.go +++ b/builder/openstack/run_config.go @@ -23,6 +23,7 @@ type RunConfig struct { RackconnectWait bool `mapstructure:"rackconnect_wait"` FloatingIpPool string `mapstructure:"floating_ip_pool"` FloatingIp string `mapstructure:"floating_ip"` + ReuseIps bool `mapstructure:"reuse_ips"` SecurityGroups []string `mapstructure:"security_groups"` Networks []string `mapstructure:"networks"` UserData string `mapstructure:"user_data"` diff --git a/builder/openstack/step_allocate_ip.go b/builder/openstack/step_allocate_ip.go index f5679bdae..18f385053 100644 --- a/builder/openstack/step_allocate_ip.go +++ b/builder/openstack/step_allocate_ip.go @@ -13,6 +13,7 @@ import ( type StepAllocateIp struct { FloatingIpPool string FloatingIp string + ReuseIps bool } func (s *StepAllocateIp) Run(state multistep.StateBag) multistep.StepAction { @@ -37,36 +38,38 @@ func (s *StepAllocateIp) Run(state multistep.StateBag) multistep.StepAction { if s.FloatingIp != "" { instanceIp.IP = s.FloatingIp } else if s.FloatingIpPool != "" { - // If we have a free floating IP in the pool, use it first - // rather than creating one - ui.Say(fmt.Sprintf("Searching for unassociated floating IP in pool %s", s.FloatingIpPool)) - pager := floatingips.List(client) - err := pager.EachPage(func(page pagination.Page) (bool, error) { - candidates, err := floatingips.ExtractFloatingIPs(page) + // If ReuseIps is set to true and we have a free floating IP in + // the pool, use it first rather than creating one + if s.ReuseIps { + ui.Say(fmt.Sprintf("Searching for unassociated floating IP in pool %s", s.FloatingIpPool)) + pager := floatingips.List(client) + err := pager.EachPage(func(page pagination.Page) (bool, error) { + candidates, err := floatingips.ExtractFloatingIPs(page) - if err != nil { - return false, err // stop and throw error out - } - - for _, candidate := range candidates { - if candidate.Pool != s.FloatingIpPool || candidate.InstanceID != "" { - continue // move to next in list + if err != nil { + return false, err // stop and throw error out } - // In correct pool and able to be allocated - instanceIp.IP = candidate.IP - ui.Message(fmt.Sprintf("Selected floating IP: %s", instanceIp.IP)) - state.Put("floatingip_istemp", false) - return false, nil // stop iterating over pages - } - return true, nil // try the next page - }) + for _, candidate := range candidates { + if candidate.Pool != s.FloatingIpPool || candidate.InstanceID != "" { + continue // move to next in list + } - if err != nil { - err := fmt.Errorf("Error searching for floating ip from pool '%s'", s.FloatingIpPool) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt + // In correct pool and able to be allocated + instanceIp.IP = candidate.IP + ui.Message(fmt.Sprintf("Selected floating IP: %s", instanceIp.IP)) + state.Put("floatingip_istemp", false) + return false, nil // stop iterating over pages + } + return true, nil // try the next page + }) + + if err != nil { + err := fmt.Errorf("Error searching for floating ip from pool '%s'", s.FloatingIpPool) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } } if instanceIp.IP == "" { diff --git a/website/source/docs/builders/openstack.html.md b/website/source/docs/builders/openstack.html.md index 618b280ea..486d29e2d 100644 --- a/website/source/docs/builders/openstack.html.md +++ b/website/source/docs/builders/openstack.html.md @@ -122,6 +122,13 @@ builder. launch the server to create the AMI. If not specified, Packer will use the environment variable `OS_REGION_NAME`, if set. +- `reuse_ips` (boolean) - Whether or not to attempt to reuse existing + unassigned floating ips in the project before allocating a new one. Note + that it is not possible to safely do this concurrently, so if you are + running multiple openstack builds concurrently, or if other processes are + assigning and using floating IPs in the same openstack project while packer + is running, you should not set this to true. Defaults to false. + - `security_groups` (array of strings) - A list of security groups by name to add to this instance.