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

View File

@ -6,6 +6,7 @@ import (
"os/exec"
"path"
"strings"
"time"
log "github.com/Sirupsen/logrus"
"github.com/codegangsta/cli"
@ -16,28 +17,30 @@ import (
)
type Driver struct {
AuthUrl string
Username string
Password string
TenantName string
TenantId string
Region string
EndpointType string
MachineName string
MachineId string
FlavorName string
FlavorId string
ImageName string
ImageId string
KeyPairName string
NetworkName string
NetworkId string
SecurityGroups []string
FloatingIpPool string
SSHUser string
SSHPort int
storePath string
client Client
AuthUrl string
Username string
Password string
TenantName string
TenantId string
Region string
EndpointType string
MachineName string
MachineId string
FlavorName string
FlavorId string
ImageName string
ImageId string
KeyPairName string
NetworkName string
NetworkId string
SecurityGroups []string
FloatingIpPool string
FloatingIpPoolId string
SSHUser string
SSHPort int
Ip string
storePath string
client Client
}
type CreateFlags struct {
@ -218,44 +221,34 @@ func (d *Driver) GetURL() (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 {
return "", err
}
addresses, err := d.client.GetInstanceIpAddresses(d)
if err != nil {
return "", err
addressType := Fixed
if d.FloatingIpPool != "" {
addressType = Floating
}
floating := []string{}
fixed := []string{}
for _, address := range addresses {
if address.AddressType == Floating {
floating = append(floating, address.Address)
continue
// Looking for the IP address in a retry loop to deal with OpenStack latency
for retryCount := 0; retryCount < 200; retryCount++ {
addresses, err := d.client.GetInstanceIpAddresses(d)
if err != nil {
return "", err
}
if address.AddressType == Fixed {
fixed = append(fixed, address.Address)
continue
for _, a := range addresses {
if a.AddressType == addressType {
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")
}
@ -308,7 +301,18 @@ func (d *Driver) Create() error {
if err := d.createMachine(); err != nil {
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
}
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)}
}
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
}
@ -441,21 +445,13 @@ func (d *Driver) checkConfig() error {
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 {
if d.NetworkName != "" {
if err := d.initNetwork(); err != nil {
return err
}
networkId, err := d.client.GetNetworkId(d, d.NetworkName)
networkId, err := d.client.GetNetworkId(d)
if err != nil {
return err
@ -476,7 +472,7 @@ func (d *Driver) resolveIds() error {
if err := d.initCompute(); err != nil {
return err
}
flavorId, err := d.client.GetFlavorId(d, d.FlavorName)
flavorId, err := d.client.GetFlavorId(d)
if err != nil {
return err
@ -497,7 +493,7 @@ func (d *Driver) resolveIds() error {
if err := d.initCompute(); err != nil {
return err
}
imageId, err := d.client.GetImageId(d, d.ImageName)
imageId, err := d.client.GetImageId(d)
if err != nil {
return err
@ -514,6 +510,27 @@ func (d *Driver) resolveIds() error {
}).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
}
@ -573,18 +590,94 @@ func (d *Driver) createMachine() error {
return nil
}
func (d *Driver) waitForInstanceToStart() error {
log.WithField("MachineId", d.MachineId).Debug("Waiting for the OpenStack instance to start...")
func (d *Driver) assignFloatingIp() error {
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 {
return err
}
return nil
}
func (d *Driver) lookForIpAddress() error {
ip, err := d.GetIP()
if err != nil {
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))
}
func (d *Driver) waitForInstanceToStart() error {
if err := d.waitForInstanceActive(); err != nil {
return err
}
return d.waitForSSHServer()
}
func (d *Driver) installDocker() error {
log.WithField("MachineId", d.MachineId).Debug("Adding key to authorized-keys.d...")