Merge pull request #919 from nebula/manage_floating_ips

[OpenStack] Add flexible IP pool/address handling.
This commit is contained in:
Mitchell Hashimoto 2014-04-30 16:54:13 -07:00
commit 4a725409af
4 changed files with 103 additions and 15 deletions

View File

@ -92,6 +92,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
Flavor: b.config.Flavor,
SourceImage: b.config.SourceImage,
},
&StepAllocateIp{
FloatingIpPool: b.config.FloatingIpPool,
FloatingIp: b.config.FloatingIp,
},
&common.StepConnectSSH{
SSHAddress: SSHAddress(csp, b.config.SSHPort),
SSHConfig: SSHConfig(b.config.SSHUsername),

View File

@ -16,6 +16,9 @@ type RunConfig struct {
SSHUsername string `mapstructure:"ssh_username"`
SSHPort int `mapstructure:"ssh_port"`
OpenstackProvider string `mapstructure:"openstack_provider"`
UseFloatingIp bool `mapstructure:"use_floating_ip"`
FloatingIpPool string `mapstructure:"floating_ip_pool"`
FloatingIp string `mapstructure:"floating_ip"`
// Unexported fields that are calculated from others
sshTimeout time.Duration
@ -43,6 +46,10 @@ func (c *RunConfig) Prepare(t *packer.ConfigTemplate) []error {
c.RawSSHTimeout = "5m"
}
if c.UseFloatingIp && c.FloatingIpPool == "" {
c.FloatingIpPool = "public"
}
// Validation
var err error
errs := make([]error, 0)

View File

@ -13,24 +13,35 @@ import (
// for determining the SSH address based on the server AccessIPv4 setting..
func SSHAddress(csp gophercloud.CloudServersProvider, port int) func(multistep.StateBag) (string, error) {
return func(state multistep.StateBag) (string, error) {
for j := 0; j < 2; j++ {
s := state.Get("server").(*gophercloud.Server)
if s.AccessIPv4 != "" {
return fmt.Sprintf("%s:%d", s.AccessIPv4, port), nil
}
if s.AccessIPv6 != "" {
return fmt.Sprintf("[%s]:%d", s.AccessIPv6, port), nil
}
serverState, err := csp.ServerById(s.Id)
s := state.Get("server").(*gophercloud.Server)
if err != nil {
return "", err
}
state.Put("server", serverState)
time.Sleep(1 * time.Second)
if ip := state.Get("access_ip").(gophercloud.FloatingIp); ip.Ip != "" {
return fmt.Sprintf("%s:%d", ip.Ip, port), nil
}
ip_pools, err := s.AllAddressPools()
if err != nil {
return "", errors.New("Error parsing SSH addresses")
}
for pool, addresses := range ip_pools {
if pool != "" {
for _, address := range addresses {
if address.Addr != "" {
return fmt.Sprintf("%s:%d", address.Addr, port), nil
}
}
}
}
serverState, err := csp.ServerById(s.Id)
if err != nil {
return "", err
}
state.Put("server", serverState)
time.Sleep(1 * time.Second)
return "", errors.New("couldn't determine IP address for server")
}
}

View File

@ -0,0 +1,66 @@
package openstack
import (
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"github.com/rackspace/gophercloud"
)
type StepAllocateIp struct {
FloatingIpPool string
FloatingIp string
}
func (s *StepAllocateIp) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
csp := state.Get("csp").(gophercloud.CloudServersProvider)
server := state.Get("server").(*gophercloud.Server)
var instanceIp gophercloud.FloatingIp
// This is here in case we error out before putting instanceIp into the
// statebag below, because it is requested by Cleanup()
state.Put("access_ip", instanceIp)
if s.FloatingIp != "" {
instanceIp.Ip = s.FloatingIp
} else if s.FloatingIpPool != "" {
newIp, err := csp.CreateFloatingIp(s.FloatingIpPool)
if err != nil {
err := fmt.Errorf("Error creating floating ip from pool '%s'", s.FloatingIpPool)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
instanceIp = newIp
ui.Say(fmt.Sprintf("Created temporary floating IP %s...", instanceIp.Ip))
}
if instanceIp.Ip != "" {
if err := csp.AssociateFloatingIp(server.Id, instanceIp); err != nil {
err := fmt.Errorf("Error associating floating IP %s with instance.", instanceIp.Ip)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
} else {
ui.Say(fmt.Sprintf("Added floating IP %s to instance...", instanceIp.Ip))
}
}
state.Put("access_ip", instanceIp)
return multistep.ActionContinue
}
func (s *StepAllocateIp) Cleanup(state multistep.StateBag) {
ui := state.Get("ui").(packer.Ui)
csp := state.Get("csp").(gophercloud.CloudServersProvider)
instanceIp := state.Get("access_ip").(gophercloud.FloatingIp)
if s.FloatingIpPool != "" && instanceIp.Id != 0 {
if err := csp.DeleteFloatingIp(instanceIp); err != nil {
ui.Error(fmt.Sprintf("Error deleting temporary floating IP %s", instanceIp.Ip))
return
}
ui.Say(fmt.Sprintf("Deleted temporary floating IP %s", instanceIp.Ip))
}
}