Support floating IP allocation and assignation

Signed-off-by: Guillaume Giamarchi <guillaume.giamarchi@gmail.com>
This commit is contained in:
Guillaume Giamarchi 2014-12-14 22:38:25 +01:00
parent edec8cd024
commit a2d64fe5c0
2 changed files with 231 additions and 89 deletions

View File

@ -30,9 +30,13 @@ type Client interface {
GetInstanceIpAddresses(d *Driver) ([]IpAddress, error) GetInstanceIpAddresses(d *Driver) ([]IpAddress, error)
CreateKeyPair(d *Driver, name string, publicKey string) error CreateKeyPair(d *Driver, name string, publicKey string) error
DeleteKeyPair(d *Driver, name string) error DeleteKeyPair(d *Driver, name string) error
GetNetworkId(d *Driver, name string) (string, error) GetNetworkId(d *Driver) (string, error)
GetFlavorId(d *Driver, name string) (string, error) GetFlavorId(d *Driver) (string, error)
GetImageId(d *Driver, name string) (string, error) GetImageId(d *Driver) (string, error)
AssignFloatingIP(d *Driver, floatingIp *FloatingIp, portId string) error
GetFloatingIPs(d *Driver) ([]FloatingIp, error)
GetFloatingIpPoolId(d *Driver) (string, error)
GetInstancePortId(d *Driver) (string, error)
} }
type GenericClient struct { type GenericClient struct {
@ -77,16 +81,18 @@ type IpAddress struct {
Mac string Mac string
} }
type FloatingIp struct {
Id string
Ip string
NetworkId string
PortId string
}
func (c *GenericClient) GetInstanceState(d *Driver) (string, error) { func (c *GenericClient) GetInstanceState(d *Driver) (string, error) {
server, err := c.GetServerDetail(d) server, err := c.GetServerDetail(d)
if err != nil { if err != nil {
return "", err return "", err
} }
c.getFloatingIPs(d)
c.getPorts(d)
return server.Status, nil return server.Status, nil
} }
@ -153,8 +159,16 @@ func (c *GenericClient) GetInstanceIpAddresses(d *Driver) ([]IpAddress, error) {
return addresses, nil return addresses, nil
} }
func (c *GenericClient) GetNetworkId(d *Driver, networkName string) (string, error) { func (c *GenericClient) GetNetworkId(d *Driver) (string, error) {
opts := networks.ListOpts{Name: d.NetworkName} return c.getNetworkId(d, d.NetworkName)
}
func (c *GenericClient) GetFloatingIpPoolId(d *Driver) (string, error) {
return c.getNetworkId(d, d.FloatingIpPool)
}
func (c *GenericClient) getNetworkId(d *Driver, networkName string) (string, error) {
opts := networks.ListOpts{Name: networkName}
pager := networks.List(c.Network, opts) pager := networks.List(c.Network, opts)
networkId := "" networkId := ""
@ -165,7 +179,7 @@ func (c *GenericClient) GetNetworkId(d *Driver, networkName string) (string, err
} }
for _, n := range networkList { for _, n := range networkList {
if n.Name == d.NetworkName { if n.Name == networkName {
networkId = n.ID networkId = n.ID
return false, nil return false, nil
} }
@ -177,7 +191,7 @@ func (c *GenericClient) GetNetworkId(d *Driver, networkName string) (string, err
return networkId, err return networkId, err
} }
func (c *GenericClient) GetFlavorId(d *Driver, flavorName string) (string, error) { func (c *GenericClient) GetFlavorId(d *Driver) (string, error) {
pager := flavors.ListDetail(c.Compute, nil) pager := flavors.ListDetail(c.Compute, nil)
flavorId := "" flavorId := ""
@ -200,8 +214,8 @@ func (c *GenericClient) GetFlavorId(d *Driver, flavorName string) (string, error
return flavorId, err return flavorId, err
} }
func (c *GenericClient) GetImageId(d *Driver, imageName string) (string, error) { func (c *GenericClient) GetImageId(d *Driver) (string, error) {
opts := images.ListOpts{Name: imageName} opts := images.ListOpts{Name: d.ImageName}
pager := images.ListDetail(c.Compute, opts) pager := images.ListDetail(c.Compute, opts)
imageId := "" imageId := ""
@ -250,16 +264,48 @@ func (c *GenericClient) GetServerDetail(d *Driver) (*servers.Server, error) {
return server, nil return server, nil
} }
func (c *GenericClient) getFloatingIPs(d *Driver) ([]string, error) { func (c *GenericClient) AssignFloatingIP(d *Driver, floatingIp *FloatingIp, portId string) error {
pager := floatingips.List(c.Network, floatingips.ListOpts{}) if floatingIp.Id == "" {
f, err := floatingips.Create(c.Network, floatingips.CreateOpts{
FloatingNetworkID: d.FloatingIpPoolId,
PortID: portId,
}).Extract()
if err != nil {
return err
}
floatingIp.Id = f.ID
floatingIp.Ip = f.FloatingIP
floatingIp.NetworkId = f.FloatingNetworkID
floatingIp.PortId = f.PortID
return nil
}
_, err := floatingips.Update(c.Network, floatingIp.Id, floatingips.UpdateOpts{
PortID: portId,
}).Extract()
if err != nil {
return err
}
return nil
}
func (c *GenericClient) GetFloatingIPs(d *Driver) ([]FloatingIp, error) {
pager := floatingips.List(c.Network, floatingips.ListOpts{
FloatingNetworkID: d.FloatingIpPoolId,
})
ips := []FloatingIp{}
err := pager.EachPage(func(page pagination.Page) (bool, error) { err := pager.EachPage(func(page pagination.Page) (bool, error) {
floatingipList, err := floatingips.ExtractFloatingIPs(page) floatingipList, err := floatingips.ExtractFloatingIPs(page)
if err != nil { if err != nil {
return false, err return false, err
} }
for _, f := range floatingipList { for _, f := range floatingipList {
log.Debug("### FloatingIP => %s", f) ips = append(ips, FloatingIp{
Id: f.ID,
Ip: f.FloatingIP,
NetworkId: f.FloatingNetworkID,
PortId: f.PortID,
})
} }
return true, nil return true, nil
}) })
@ -267,29 +313,32 @@ func (c *GenericClient) getFloatingIPs(d *Driver) ([]string, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return nil, nil return ips, nil
} }
func (c *GenericClient) getPorts(d *Driver) ([]string, error) { func (c *GenericClient) GetInstancePortId(d *Driver) (string, error) {
pager := ports.List(c.Network, ports.ListOpts{ pager := ports.List(c.Network, ports.ListOpts{
DeviceID: d.MachineId, DeviceID: d.MachineId,
NetworkID: d.NetworkId,
}) })
var portId string
err := pager.EachPage(func(page pagination.Page) (bool, error) { err := pager.EachPage(func(page pagination.Page) (bool, error) {
portList, err := ports.ExtractPorts(page) portList, err := ports.ExtractPorts(page)
if err != nil { if err != nil {
return false, err return false, err
} }
for _, port := range portList { for _, port := range portList {
log.Debug("### Port => %s", port) portId = port.ID
return false, nil
} }
return true, nil return true, nil
}) })
if err != nil { if err != nil {
return nil, err return "", err
} }
return nil, nil return portId, nil
} }
func (c *GenericClient) InitComputeClient(d *Driver) error { func (c *GenericClient) InitComputeClient(d *Driver) error {

View File

@ -6,6 +6,7 @@ import (
"os/exec" "os/exec"
"path" "path"
"strings" "strings"
"time"
log "github.com/Sirupsen/logrus" log "github.com/Sirupsen/logrus"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
@ -16,28 +17,30 @@ import (
) )
type Driver struct { type Driver struct {
AuthUrl string AuthUrl string
Username string Username string
Password string Password string
TenantName string TenantName string
TenantId string TenantId string
Region string Region string
EndpointType string EndpointType string
MachineName string MachineName string
MachineId string MachineId string
FlavorName string FlavorName string
FlavorId string FlavorId string
ImageName string ImageName string
ImageId string ImageId string
KeyPairName string KeyPairName string
NetworkName string NetworkName string
NetworkId string NetworkId string
SecurityGroups []string SecurityGroups []string
FloatingIpPool string FloatingIpPool string
SSHUser string FloatingIpPoolId string
SSHPort int SSHUser string
storePath string SSHPort int
client Client Ip string
storePath string
client Client
} }
type CreateFlags struct { type CreateFlags struct {
@ -218,44 +221,34 @@ func (d *Driver) GetURL() (string, error) {
} }
func (d *Driver) GetIP() (string, error) { func (d *Driver) GetIP() (string, error) {
if d.Ip != "" {
return d.Ip, nil
}
log.WithField("MachineId", d.MachineId).Debug("Looking for the IP address...")
if err := d.initCompute(); err != nil { if err := d.initCompute(); err != nil {
return "", err return "", err
} }
addresses, err := d.client.GetInstanceIpAddresses(d) addressType := Fixed
if err != nil { if d.FloatingIpPool != "" {
return "", err addressType = Floating
} }
floating := []string{} // Looking for the IP address in a retry loop to deal with OpenStack latency
fixed := []string{} for retryCount := 0; retryCount < 200; retryCount++ {
addresses, err := d.client.GetInstanceIpAddresses(d)
for _, address := range addresses { if err != nil {
if address.AddressType == Floating { return "", err
floating = append(floating, address.Address)
continue
} }
if address.AddressType == Fixed { for _, a := range addresses {
fixed = append(fixed, address.Address) if a.AddressType == addressType {
continue return a.Address, nil
}
} }
log.Warnf("Unknown IP address type : %s", address) time.Sleep(2 * time.Second)
} }
if len(floating) == 1 {
return d.foundIP(floating[0]), nil
} else if len(floating) > 1 {
log.Warnf("Multiple floating IP found. Take the first one of %s", floating)
return d.foundIP(floating[0]), nil
}
if len(fixed) == 1 {
return d.foundIP(fixed[0]), nil
} else if len(fixed) > 1 {
log.Warnf("Multiple fixed IP found. Take the first one of %s", floating)
return d.foundIP(fixed[0]), nil
}
return "", fmt.Errorf("No IP found for the machine") return "", fmt.Errorf("No IP found for the machine")
} }
@ -308,7 +301,18 @@ func (d *Driver) Create() error {
if err := d.createMachine(); err != nil { if err := d.createMachine(); err != nil {
return err return err
} }
if err := d.waitForInstanceToStart(); err != nil { if err := d.waitForInstanceActive(); err != nil {
return err
}
if d.FloatingIpPool != "" {
if err := d.assignFloatingIp(); err != nil {
return err
}
}
if err := d.lookForIpAddress(); err != nil {
return err
}
if err := d.waitForSSHServer(); err != nil {
return err return err
} }
if err := d.installDocker(); err != nil { if err := d.installDocker(); err != nil {
@ -389,7 +393,7 @@ func (d *Driver) GetSSHCommand(args ...string) (*exec.Cmd, error) {
args = []string{"sudo", "sh", "-c", fmt.Sprintf("'%s'", cmd)} args = []string{"sudo", "sh", "-c", fmt.Sprintf("'%s'", cmd)}
} }
log.Debug("Command: %s", args) log.WithField("MachineId", d.MachineId).Debug("Command: %s", args)
return ssh.GetSSHCommand(ip, d.SSHPort, d.SSHUser, d.sshKeyPath(), args...), nil return ssh.GetSSHCommand(ip, d.SSHPort, d.SSHUser, d.sshKeyPath(), args...), nil
} }
@ -441,21 +445,13 @@ func (d *Driver) checkConfig() error {
return nil return nil
} }
func (d *Driver) foundIP(ip string) string {
log.WithFields(log.Fields{
"IP": ip,
"MachineId": d.MachineId,
}).Debug("IP address found")
return ip
}
func (d *Driver) resolveIds() error { func (d *Driver) resolveIds() error {
if d.NetworkName != "" { if d.NetworkName != "" {
if err := d.initNetwork(); err != nil { if err := d.initNetwork(); err != nil {
return err return err
} }
networkId, err := d.client.GetNetworkId(d, d.NetworkName) networkId, err := d.client.GetNetworkId(d)
if err != nil { if err != nil {
return err return err
@ -476,7 +472,7 @@ func (d *Driver) resolveIds() error {
if err := d.initCompute(); err != nil { if err := d.initCompute(); err != nil {
return err return err
} }
flavorId, err := d.client.GetFlavorId(d, d.FlavorName) flavorId, err := d.client.GetFlavorId(d)
if err != nil { if err != nil {
return err return err
@ -497,7 +493,7 @@ func (d *Driver) resolveIds() error {
if err := d.initCompute(); err != nil { if err := d.initCompute(); err != nil {
return err return err
} }
imageId, err := d.client.GetImageId(d, d.ImageName) imageId, err := d.client.GetImageId(d)
if err != nil { if err != nil {
return err return err
@ -514,6 +510,27 @@ func (d *Driver) resolveIds() error {
}).Debug("Found image id using its name") }).Debug("Found image id using its name")
} }
if d.FloatingIpPool != "" {
if err := d.initNetwork(); err != nil {
return err
}
f, err := d.client.GetFloatingIpPoolId(d)
if err != nil {
return err
}
if f == "" {
return fmt.Errorf(errorUnknownNetworkName, d.FloatingIpPool)
}
d.FloatingIpPoolId = f
log.WithFields(log.Fields{
"Name": d.FloatingIpPool,
"ID": d.FloatingIpPoolId,
}).Debug("Found floating IP pool id using its name")
}
return nil return nil
} }
@ -573,18 +590,94 @@ func (d *Driver) createMachine() error {
return nil return nil
} }
func (d *Driver) waitForInstanceToStart() error { func (d *Driver) assignFloatingIp() error {
log.WithField("MachineId", d.MachineId).Debug("Waiting for the OpenStack instance to start...")
if err := d.initNetwork(); err != nil {
return err
}
portId, err := d.client.GetInstancePortId(d)
if err != nil {
return err
}
ips, err := d.client.GetFloatingIPs(d)
if err != nil {
return err
}
var floatingIp *FloatingIp
log.WithFields(log.Fields{
"MachineId": d.MachineId,
"Pool": d.FloatingIpPool,
}).Debugf("Looking for an available floating IP")
for _, ip := range ips {
if ip.PortId == "" {
log.WithFields(log.Fields{
"MachineId": d.MachineId,
"IP": ip.Ip,
}).Debugf("Available floating IP found")
floatingIp = &ip
break
}
}
if floatingIp == nil {
floatingIp = &FloatingIp{}
log.WithField("MachineId", d.MachineId).Debugf("No available floating IP found. Allocating a new one...")
} else {
log.WithField("MachineId", d.MachineId).Debugf("Assigning floating IP to the instance")
}
if err := d.client.AssignFloatingIP(d, floatingIp, portId); err != nil {
return err
}
d.Ip = floatingIp.Ip
return nil
}
func (d *Driver) waitForInstanceActive() error {
log.WithField("MachineId", d.MachineId).Debug("Waiting for the OpenStack instance to be ACTIVE...")
if err := d.client.WaitForInstanceStatus(d, "ACTIVE", 200); err != nil { if err := d.client.WaitForInstanceStatus(d, "ACTIVE", 200); err != nil {
return err return err
} }
return nil
}
func (d *Driver) lookForIpAddress() error {
ip, err := d.GetIP() ip, err := d.GetIP()
if err != nil { if err != nil {
return err return err
} }
d.Ip = ip
log.WithFields(log.Fields{
"IP": ip,
"MachineId": d.MachineId,
}).Debug("IP address found")
return nil
}
func (d *Driver) waitForSSHServer() error {
ip, err := d.GetIP()
if err != nil {
return err
}
log.WithFields(log.Fields{
"MachineId": d.MachineId,
"IP": ip,
}).Debug("Waiting for the SSH server to be started...")
return ssh.WaitForTCP(fmt.Sprintf("%s:%d", ip, d.SSHPort)) return ssh.WaitForTCP(fmt.Sprintf("%s:%d", ip, d.SSHPort))
} }
func (d *Driver) waitForInstanceToStart() error {
if err := d.waitForInstanceActive(); err != nil {
return err
}
return d.waitForSSHServer()
}
func (d *Driver) installDocker() error { func (d *Driver) installDocker() error {
log.WithField("MachineId", d.MachineId).Debug("Adding key to authorized-keys.d...") log.WithField("MachineId", d.MachineId).Debug("Adding key to authorized-keys.d...")