Merge pull request #73 from ggiamarchi/openstack-driver
OpenStack & Rackspace drivers
This commit is contained in:
419
drivers/openstack/client.go
Normal file
419
drivers/openstack/client.go
Normal file
@@ -0,0 +1,419 @@
|
||||
package openstack
|
||||
|
||||
import (
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/openstack"
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs"
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/startstop"
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/flavors"
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/images"
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/ports"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
type Client interface {
|
||||
Authenticate(d *Driver) error
|
||||
InitComputeClient(d *Driver) error
|
||||
InitNetworkClient(d *Driver) error
|
||||
|
||||
CreateInstance(d *Driver) (string, error)
|
||||
GetInstanceState(d *Driver) (string, error)
|
||||
StartInstance(d *Driver) error
|
||||
StopInstance(d *Driver) error
|
||||
RestartInstance(d *Driver) error
|
||||
DeleteInstance(d *Driver) error
|
||||
WaitForInstanceStatus(d *Driver, status string, timeout int) error
|
||||
GetInstanceIpAddresses(d *Driver) ([]IpAddress, error)
|
||||
CreateKeyPair(d *Driver, name string, publicKey string) error
|
||||
DeleteKeyPair(d *Driver, name 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 {
|
||||
Provider *gophercloud.ProviderClient
|
||||
Compute *gophercloud.ServiceClient
|
||||
Network *gophercloud.ServiceClient
|
||||
}
|
||||
|
||||
func (c *GenericClient) CreateInstance(d *Driver) (string, error) {
|
||||
serverOpts := servers.CreateOpts{
|
||||
Name: d.MachineName,
|
||||
FlavorRef: d.FlavorId,
|
||||
ImageRef: d.ImageId,
|
||||
SecurityGroups: d.SecurityGroups,
|
||||
}
|
||||
if d.NetworkId != "" {
|
||||
serverOpts.Networks = []servers.Network{
|
||||
{
|
||||
UUID: d.NetworkId,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"Name": d.MachineName,
|
||||
}).Info("Creating server...")
|
||||
|
||||
server, err := servers.Create(c.Compute, keypairs.CreateOptsExt{
|
||||
serverOpts,
|
||||
d.KeyPairName,
|
||||
}).Extract()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return server.ID, nil
|
||||
}
|
||||
|
||||
const (
|
||||
Floating string = "floating"
|
||||
Fixed string = "fixed"
|
||||
)
|
||||
|
||||
type IpAddress struct {
|
||||
Network string
|
||||
AddressType string
|
||||
Address string
|
||||
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
|
||||
}
|
||||
return server.Status, nil
|
||||
}
|
||||
|
||||
func (c *GenericClient) StartInstance(d *Driver) error {
|
||||
if result := startstop.Start(c.Compute, d.MachineId); result.Err != nil {
|
||||
return result.Err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *GenericClient) StopInstance(d *Driver) error {
|
||||
if result := startstop.Stop(c.Compute, d.MachineId); result.Err != nil {
|
||||
return result.Err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *GenericClient) RestartInstance(d *Driver) error {
|
||||
if result := servers.Reboot(c.Compute, d.MachineId, servers.SoftReboot); result.Err != nil {
|
||||
return result.Err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *GenericClient) DeleteInstance(d *Driver) error {
|
||||
if result := servers.Delete(c.Compute, d.MachineId); result.Err != nil {
|
||||
return result.Err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *GenericClient) WaitForInstanceStatus(d *Driver, status string, timeout int) error {
|
||||
if err := servers.WaitForStatus(c.Compute, d.MachineId, status, timeout); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *GenericClient) GetInstanceIpAddresses(d *Driver) ([]IpAddress, error) {
|
||||
server, err := c.GetServerDetail(d)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addresses := []IpAddress{}
|
||||
for network, networkAddresses := range server.Addresses {
|
||||
for _, element := range networkAddresses.([]interface{}) {
|
||||
address := element.(map[string]interface{})
|
||||
|
||||
addr := IpAddress{
|
||||
Network: network,
|
||||
Address: address["addr"].(string),
|
||||
}
|
||||
|
||||
if tp, ok := address["OS-EXT-IPS:type"]; ok {
|
||||
addr.AddressType = tp.(string)
|
||||
}
|
||||
if mac, ok := address["OS-EXT-IPS-MAC:mac_addr"]; ok {
|
||||
addr.Mac = mac.(string)
|
||||
}
|
||||
|
||||
addresses = append(addresses, addr)
|
||||
}
|
||||
}
|
||||
return addresses, nil
|
||||
}
|
||||
|
||||
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 := ""
|
||||
|
||||
err := pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
networkList, err := networks.ExtractNetworks(page)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, n := range networkList {
|
||||
if n.Name == networkName {
|
||||
networkId = n.ID
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
return networkId, err
|
||||
}
|
||||
|
||||
func (c *GenericClient) GetFlavorId(d *Driver) (string, error) {
|
||||
pager := flavors.ListDetail(c.Compute, nil)
|
||||
flavorId := ""
|
||||
|
||||
err := pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
flavorList, err := flavors.ExtractFlavors(page)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, f := range flavorList {
|
||||
if f.Name == d.FlavorName {
|
||||
flavorId = f.ID
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
return flavorId, err
|
||||
}
|
||||
|
||||
func (c *GenericClient) GetImageId(d *Driver) (string, error) {
|
||||
opts := images.ListOpts{Name: d.ImageName}
|
||||
pager := images.ListDetail(c.Compute, opts)
|
||||
imageId := ""
|
||||
|
||||
err := pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
imageList, err := images.ExtractImages(page)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, i := range imageList {
|
||||
if i.Name == d.ImageName {
|
||||
imageId = i.ID
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
return imageId, err
|
||||
}
|
||||
|
||||
func (c *GenericClient) CreateKeyPair(d *Driver, name string, publicKey string) error {
|
||||
opts := keypairs.CreateOpts{
|
||||
Name: name,
|
||||
PublicKey: publicKey,
|
||||
}
|
||||
if result := keypairs.Create(c.Compute, opts); result.Err != nil {
|
||||
return result.Err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *GenericClient) DeleteKeyPair(d *Driver, name string) error {
|
||||
if result := keypairs.Delete(c.Compute, name); result.Err != nil {
|
||||
return result.Err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *GenericClient) GetServerDetail(d *Driver) (*servers.Server, error) {
|
||||
server, err := servers.Get(c.Compute, d.MachineId).Extract()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return server, nil
|
||||
}
|
||||
|
||||
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 {
|
||||
ips = append(ips, FloatingIp{
|
||||
Id: f.ID,
|
||||
Ip: f.FloatingIP,
|
||||
NetworkId: f.FloatingNetworkID,
|
||||
PortId: f.PortID,
|
||||
})
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ips, nil
|
||||
}
|
||||
|
||||
func (c *GenericClient) GetInstancePortId(d *Driver) (string, error) {
|
||||
pager := ports.List(c.Network, ports.ListOpts{
|
||||
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 {
|
||||
portId = port.ID
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return portId, nil
|
||||
}
|
||||
|
||||
func (c *GenericClient) InitComputeClient(d *Driver) error {
|
||||
if c.Compute != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
compute, err := openstack.NewComputeV2(c.Provider, gophercloud.EndpointOpts{
|
||||
Region: d.Region,
|
||||
Availability: c.getEndpointType(d),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Compute = compute
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *GenericClient) InitNetworkClient(d *Driver) error {
|
||||
if c.Network != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
network, err := openstack.NewNetworkV2(c.Provider, gophercloud.EndpointOpts{
|
||||
Region: d.Region,
|
||||
Availability: c.getEndpointType(d),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Network = network
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *GenericClient) getEndpointType(d *Driver) gophercloud.Availability {
|
||||
switch d.EndpointType {
|
||||
case "internalURL":
|
||||
return gophercloud.AvailabilityInternal
|
||||
case "adminURL":
|
||||
return gophercloud.AvailabilityAdmin
|
||||
}
|
||||
return gophercloud.AvailabilityPublic
|
||||
}
|
||||
|
||||
func (c *GenericClient) Authenticate(d *Driver) error {
|
||||
if c.Provider != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"AuthUrl": d.AuthUrl,
|
||||
"Username": d.Username,
|
||||
"TenantName": d.TenantName,
|
||||
"TenantID": d.TenantId,
|
||||
}).Debug("Authenticating...")
|
||||
|
||||
opts := gophercloud.AuthOptions{
|
||||
IdentityEndpoint: d.AuthUrl,
|
||||
Username: d.Username,
|
||||
Password: d.Password,
|
||||
TenantName: d.TenantName,
|
||||
TenantID: d.TenantId,
|
||||
AllowReauth: true,
|
||||
}
|
||||
|
||||
provider, err := openstack.AuthenticatedClient(opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Provider = provider
|
||||
|
||||
return nil
|
||||
}
|
||||
748
drivers/openstack/openstack.go
Normal file
748
drivers/openstack/openstack.go
Normal file
@@ -0,0 +1,748 @@
|
||||
package openstack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/docker/docker/utils"
|
||||
"github.com/docker/machine/drivers"
|
||||
"github.com/docker/machine/ssh"
|
||||
"github.com/docker/machine/state"
|
||||
)
|
||||
|
||||
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
|
||||
FloatingIpPoolId string
|
||||
SSHUser string
|
||||
SSHPort int
|
||||
Ip string
|
||||
EnableDockerInstall bool
|
||||
storePath string
|
||||
client Client
|
||||
}
|
||||
|
||||
type CreateFlags struct {
|
||||
AuthUrl *string
|
||||
Username *string
|
||||
Password *string
|
||||
TenantName *string
|
||||
TenantId *string
|
||||
Region *string
|
||||
EndpointType *string
|
||||
FlavorName *string
|
||||
FlavorId *string
|
||||
ImageName *string
|
||||
ImageId *string
|
||||
NetworkName *string
|
||||
NetworkId *string
|
||||
SecurityGroups *string
|
||||
FloatingIpPool *string
|
||||
SSHUser *string
|
||||
SSHPort *int
|
||||
}
|
||||
|
||||
func init() {
|
||||
drivers.Register("openstack", &drivers.RegisteredDriver{
|
||||
New: NewDriver,
|
||||
GetCreateFlags: GetCreateFlags,
|
||||
})
|
||||
}
|
||||
|
||||
func GetCreateFlags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
cli.StringFlag{
|
||||
EnvVar: "OS_AUTH_URL",
|
||||
Name: "openstack-auth-url",
|
||||
Usage: "OpenStack authentication URL",
|
||||
Value: "",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "OS_USERNAME",
|
||||
Name: "openstack-username",
|
||||
Usage: "OpenStack username",
|
||||
Value: "",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "OS_PASSWORD",
|
||||
Name: "openstack-password",
|
||||
Usage: "OpenStack password",
|
||||
Value: "",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "OS_TENANT_NAME",
|
||||
Name: "openstack-tenant-name",
|
||||
Usage: "OpenStack tenant name",
|
||||
Value: "",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "OS_TENANT_ID",
|
||||
Name: "openstack-tenant-id",
|
||||
Usage: "OpenStack tenant id",
|
||||
Value: "",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "OS_REGION_NAME",
|
||||
Name: "openstack-region",
|
||||
Usage: "OpenStack region name",
|
||||
Value: "",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "OS_ENDPOINT_TYPE",
|
||||
Name: "openstack-endpoint-type",
|
||||
Usage: "OpenStack endpoint type (adminURL, internalURL or publicURL)",
|
||||
Value: "",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "openstack-flavor-id",
|
||||
Usage: "OpenStack flavor id to use for the instance",
|
||||
Value: "",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "openstack-flavor-name",
|
||||
Usage: "OpenStack flavor name to use for the instance",
|
||||
Value: "",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "openstack-image-id",
|
||||
Usage: "OpenStack image id to use for the instance",
|
||||
Value: "",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "openstack-image-name",
|
||||
Usage: "OpenStack image name to use for the instance",
|
||||
Value: "",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "openstack-net-id",
|
||||
Usage: "OpenStack image name to use for the instance",
|
||||
Value: "",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "openstack-net-name",
|
||||
Usage: "OpenStack network name the machine will be connected on",
|
||||
Value: "",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "openstack-sec-groups",
|
||||
Usage: "OpenStack comma separated security groups for the machine",
|
||||
Value: "",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "openstack-floatingip-pool",
|
||||
Usage: "OpenStack floating IP pool to get an IP from to assign to the instance",
|
||||
Value: "",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "openstack-ssh-user",
|
||||
Usage: "OpenStack SSH user",
|
||||
Value: "root",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "openstack-ssh-port",
|
||||
Usage: "OpenStack SSH port",
|
||||
Value: 22,
|
||||
},
|
||||
// Using a StringFlag rather than a BoolFlag because
|
||||
// the BoolFlag default value is always false
|
||||
cli.StringFlag{
|
||||
Name: "openstack-docker-install",
|
||||
Usage: "Set if docker have to be installed on the machine",
|
||||
Value: "true",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func NewDriver(machineName string, storePath string) (drivers.Driver, error) {
|
||||
log.WithFields(log.Fields{
|
||||
"machineName": machineName,
|
||||
"storePath": storePath,
|
||||
}).Debug("Instantiating OpenStack driver...")
|
||||
|
||||
return NewDerivedDriver(machineName, storePath, &GenericClient{})
|
||||
}
|
||||
|
||||
func NewDerivedDriver(machineName string, storePath string, client Client) (*Driver, error) {
|
||||
return &Driver{
|
||||
MachineName: machineName,
|
||||
storePath: storePath,
|
||||
client: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *Driver) DriverName() string {
|
||||
return "openstack"
|
||||
}
|
||||
|
||||
func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
|
||||
d.AuthUrl = flags.String("openstack-auth-url")
|
||||
d.Username = flags.String("openstack-username")
|
||||
d.Password = flags.String("openstack-password")
|
||||
d.TenantName = flags.String("openstack-tenant-name")
|
||||
d.TenantId = flags.String("openstack-tenant-id")
|
||||
d.Region = flags.String("openstack-region")
|
||||
d.EndpointType = flags.String("openstack-endpoint-type")
|
||||
d.FlavorId = flags.String("openstack-flavor-id")
|
||||
d.FlavorName = flags.String("openstack-flavor-name")
|
||||
d.ImageId = flags.String("openstack-image-id")
|
||||
d.ImageName = flags.String("openstack-image-name")
|
||||
d.NetworkId = flags.String("openstack-net-id")
|
||||
d.NetworkName = flags.String("openstack-net-name")
|
||||
if flags.String("openstack-sec-groups") != "" {
|
||||
d.SecurityGroups = strings.Split(flags.String("openstack-sec-groups"), ",")
|
||||
}
|
||||
d.FloatingIpPool = flags.String("openstack-floatingip-pool")
|
||||
d.SSHUser = flags.String("openstack-ssh-user")
|
||||
d.SSHPort = flags.Int("openstack-ssh-port")
|
||||
|
||||
installDocker, err := strconv.ParseBool(flags.String("openstack-docker-install"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.EnableDockerInstall = installDocker
|
||||
return d.checkConfig()
|
||||
}
|
||||
|
||||
func (d *Driver) GetURL() (string, error) {
|
||||
ip, err := d.GetIP()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if ip == "" {
|
||||
return "", nil
|
||||
}
|
||||
return fmt.Sprintf("tcp://%s:2376", ip), nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
addressType := Fixed
|
||||
if d.FloatingIpPool != "" {
|
||||
addressType = Floating
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
for _, a := range addresses {
|
||||
if a.AddressType == addressType {
|
||||
return a.Address, nil
|
||||
}
|
||||
}
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
return "", fmt.Errorf("No IP found for the machine")
|
||||
}
|
||||
|
||||
func (d *Driver) GetState() (state.State, error) {
|
||||
log.WithField("MachineId", d.MachineId).Debug("Get status for OpenStack instance...")
|
||||
if err := d.initCompute(); err != nil {
|
||||
return state.None, err
|
||||
}
|
||||
|
||||
s, err := d.client.GetInstanceState(d)
|
||||
if err != nil {
|
||||
return state.None, err
|
||||
}
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"MachineId": d.MachineId,
|
||||
"State": s,
|
||||
}).Debug("State for OpenStack instance")
|
||||
|
||||
switch s {
|
||||
case "ACTIVE":
|
||||
return state.Running, nil
|
||||
case "PAUSED":
|
||||
return state.Paused, nil
|
||||
case "SUSPENDED":
|
||||
return state.Saved, nil
|
||||
case "SHUTOFF":
|
||||
return state.Stopped, nil
|
||||
case "BUILDING":
|
||||
return state.Starting, nil
|
||||
case "ERROR":
|
||||
return state.Error, nil
|
||||
}
|
||||
return state.None, nil
|
||||
}
|
||||
|
||||
func (d *Driver) Create() error {
|
||||
d.KeyPairName = fmt.Sprintf("%s-%s", d.MachineName, utils.GenerateRandomID())
|
||||
|
||||
if err := d.resolveIds(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.createSSHKey(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.createMachine(); err != nil {
|
||||
return err
|
||||
}
|
||||
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 d.EnableDockerInstall {
|
||||
if err := d.installDocker(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Driver) Start() error {
|
||||
log.WithField("MachineId", d.MachineId).Info("Starting OpenStack instance...")
|
||||
if err := d.initCompute(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.client.StartInstance(d); err != nil {
|
||||
return err
|
||||
}
|
||||
return d.waitForInstanceToStart()
|
||||
}
|
||||
|
||||
func (d *Driver) Stop() error {
|
||||
log.WithField("MachineId", d.MachineId).Info("Stopping OpenStack instance...")
|
||||
if err := d.initCompute(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.client.StopInstance(d); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.WithField("MachineId", d.MachineId).Info("Waiting for the OpenStack instance to stop...")
|
||||
if err := d.client.WaitForInstanceStatus(d, "SHUTOFF", 200); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Driver) Remove() error {
|
||||
log.WithField("MachineId", d.MachineId).Info("Deleting OpenStack instance...")
|
||||
if err := d.initCompute(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.client.DeleteInstance(d); err != nil {
|
||||
return err
|
||||
}
|
||||
log.WithField("Name", d.KeyPairName).Info("Deleting Key Pair...")
|
||||
if err := d.client.DeleteKeyPair(d, d.KeyPairName); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Driver) Restart() error {
|
||||
log.WithField("MachineId", d.MachineId).Info("Restarting OpenStack instance...")
|
||||
if err := d.initCompute(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.client.RestartInstance(d); err != nil {
|
||||
return err
|
||||
}
|
||||
return d.waitForInstanceToStart()
|
||||
}
|
||||
|
||||
func (d *Driver) Kill() error {
|
||||
return d.Stop()
|
||||
}
|
||||
|
||||
func (d *Driver) Upgrade() error {
|
||||
return fmt.Errorf("unable to upgrade as we are using the custom docker binary with identity auth")
|
||||
}
|
||||
|
||||
func (d *Driver) GetSSHCommand(args ...string) (*exec.Cmd, error) {
|
||||
ip, err := d.GetIP()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(args) != 0 && d.SSHUser != "root" {
|
||||
cmd := strings.Replace(strings.Join(args, " "), "'", "\\'", -1)
|
||||
args = []string{"sudo", "sh", "-c", fmt.Sprintf("'%s'", cmd)}
|
||||
}
|
||||
|
||||
log.WithField("MachineId", d.MachineId).Debug("Command: %s", args)
|
||||
return ssh.GetSSHCommand(ip, d.SSHPort, d.SSHUser, d.sshKeyPath(), args...), nil
|
||||
}
|
||||
|
||||
const (
|
||||
errorMandatoryEnvOrOption string = "%s must be specified either using the environment variable %s or the CLI option %s"
|
||||
errorMandatoryOption string = "%s must be specified using the CLI option %s"
|
||||
errorExclusiveOptions string = "Either %s or %s must be specified, not both"
|
||||
errorMandatoryTenantNameOrId string = "Tenant id or name must be provided either using one of the environment variables OS_TENANT_ID and OS_TENANT_NAME or one of the CLI options --openstack-tenant-id and --openstack-tenant-name"
|
||||
errorWrongEndpointType string = "Endpoint type must be 'publicURL', 'adminURL' or 'internalURL'"
|
||||
errorUnknownFlavorName string = "Unable to find flavor named %s"
|
||||
errorUnknownImageName string = "Unable to find image named %s"
|
||||
errorUnknownNetworkName string = "Unable to find network named %s"
|
||||
)
|
||||
|
||||
func (d *Driver) checkConfig() error {
|
||||
if d.AuthUrl == "" {
|
||||
return fmt.Errorf(errorMandatoryEnvOrOption, "Autentication URL", "OS_AUTH_URL", "--openstack-auth-url")
|
||||
}
|
||||
if d.Username == "" {
|
||||
return fmt.Errorf(errorMandatoryEnvOrOption, "Username", "OS_USERNAME", "--openstack-username")
|
||||
}
|
||||
if d.Password == "" {
|
||||
return fmt.Errorf(errorMandatoryEnvOrOption, "Password", "OS_PASSWORD", "--openstack-password")
|
||||
}
|
||||
if d.TenantName == "" && d.TenantId == "" {
|
||||
return fmt.Errorf(errorMandatoryTenantNameOrId)
|
||||
}
|
||||
|
||||
if d.FlavorName == "" && d.FlavorId == "" {
|
||||
return fmt.Errorf(errorMandatoryOption, "Flavor name or Flavor id", "--openstack-flavor-name or --openstack-flavor-id")
|
||||
}
|
||||
if d.FlavorName != "" && d.FlavorId != "" {
|
||||
return fmt.Errorf(errorExclusiveOptions, "Flavor name", "Flavor id")
|
||||
}
|
||||
|
||||
if d.ImageName == "" && d.ImageId == "" {
|
||||
return fmt.Errorf(errorMandatoryOption, "Image name or Image id", "--openstack-image-name or --openstack-image-id")
|
||||
}
|
||||
if d.ImageName != "" && d.ImageId != "" {
|
||||
return fmt.Errorf(errorExclusiveOptions, "Image name", "Image id")
|
||||
}
|
||||
|
||||
if d.NetworkName != "" && d.NetworkId != "" {
|
||||
return fmt.Errorf(errorExclusiveOptions, "Network name", "Network id")
|
||||
}
|
||||
if d.EndpointType != "" && (d.EndpointType != "publicURL" && d.EndpointType != "adminURL" && d.EndpointType != "internalURL") {
|
||||
return fmt.Errorf(errorWrongEndpointType)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Driver) resolveIds() error {
|
||||
if d.NetworkName != "" {
|
||||
if err := d.initNetwork(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
networkId, err := d.client.GetNetworkId(d)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if networkId == "" {
|
||||
return fmt.Errorf(errorUnknownNetworkName, d.NetworkName)
|
||||
}
|
||||
|
||||
d.NetworkId = networkId
|
||||
log.WithFields(log.Fields{
|
||||
"Name": d.NetworkName,
|
||||
"ID": d.NetworkId,
|
||||
}).Debug("Found network id using its name")
|
||||
}
|
||||
|
||||
if d.FlavorName != "" {
|
||||
if err := d.initCompute(); err != nil {
|
||||
return err
|
||||
}
|
||||
flavorId, err := d.client.GetFlavorId(d)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if flavorId == "" {
|
||||
return fmt.Errorf(errorUnknownFlavorName, d.FlavorName)
|
||||
}
|
||||
|
||||
d.FlavorId = flavorId
|
||||
log.WithFields(log.Fields{
|
||||
"Name": d.FlavorName,
|
||||
"ID": d.FlavorId,
|
||||
}).Debug("Found flavor id using its name")
|
||||
}
|
||||
|
||||
if d.ImageName != "" {
|
||||
if err := d.initCompute(); err != nil {
|
||||
return err
|
||||
}
|
||||
imageId, err := d.client.GetImageId(d)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if imageId == "" {
|
||||
return fmt.Errorf(errorUnknownImageName, d.ImageName)
|
||||
}
|
||||
|
||||
d.ImageId = imageId
|
||||
log.WithFields(log.Fields{
|
||||
"Name": d.ImageName,
|
||||
"ID": d.ImageId,
|
||||
}).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
|
||||
}
|
||||
|
||||
func (d *Driver) initCompute() error {
|
||||
if err := d.client.Authenticate(d); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.client.InitComputeClient(d); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Driver) initNetwork() error {
|
||||
if err := d.client.Authenticate(d); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.client.InitNetworkClient(d); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Driver) createSSHKey() error {
|
||||
log.WithField("Name", d.KeyPairName).Debug("Creating Key Pair...")
|
||||
if err := ssh.GenerateSSHKey(d.sshKeyPath()); err != nil {
|
||||
return err
|
||||
}
|
||||
publicKey, err := ioutil.ReadFile(d.publicSSHKeyPath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := d.initCompute(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.client.CreateKeyPair(d, d.KeyPairName, string(publicKey)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Driver) createMachine() error {
|
||||
log.WithFields(log.Fields{
|
||||
"FlavorId": d.FlavorId,
|
||||
"ImageId": d.ImageId,
|
||||
}).Debug("Creating OpenStack instance...")
|
||||
|
||||
if err := d.initCompute(); err != nil {
|
||||
return err
|
||||
}
|
||||
instanceId, err := d.client.CreateInstance(d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.MachineId = instanceId
|
||||
return nil
|
||||
}
|
||||
|
||||
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...")
|
||||
|
||||
if err := drivers.AddPublicKeyToAuthorizedHosts(d, "/.docker/authorized-keys.d"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.WithField("MachineId", d.MachineId).Debug("Installing docker daemon on the machine")
|
||||
|
||||
if err := d.sshExec([]string{
|
||||
`apt-get install -y curl`,
|
||||
`curl -sSL https://get.docker.com | /bin/sh >/var/log/docker-install.log 2>&1`,
|
||||
`service docker stop`,
|
||||
`curl -sSL https://ehazlett.s3.amazonaws.com/public/docker/linux/docker-1.4.1-136b351e-identity -o /usr/bin/docker`,
|
||||
`echo "export DOCKER_OPTS=\"--auth=identity --host=tcp://0.0.0.0:2376\"" >> /etc/default/docker`,
|
||||
`service docker start`,
|
||||
}); err != nil {
|
||||
log.Error("The docker installation failed.")
|
||||
log.Error(
|
||||
"The driver assumes that your instance is running Ubuntu. If this is not the case, you should ",
|
||||
"use the option --openstack-docker-install=false (or --{provider}-docker-install=false) when ",
|
||||
"creating a machine, and then install and configure docker manually.",
|
||||
)
|
||||
log.Error(
|
||||
`Also, you can use "machine ssh" to manually configure docker on this host.`,
|
||||
)
|
||||
|
||||
// Don't return this ssh error so that host creation succeeds and "machine ssh" and "machine rm"
|
||||
// are usable.
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Driver) sshExec(commands []string) error {
|
||||
for _, command := range commands {
|
||||
sshCmd, err := d.GetSSHCommand(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := sshCmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Driver) sshKeyPath() string {
|
||||
return path.Join(d.storePath, "id_rsa")
|
||||
}
|
||||
|
||||
func (d *Driver) publicSSHKeyPath() string {
|
||||
return d.sshKeyPath() + ".pub"
|
||||
}
|
||||
Reference in New Issue
Block a user