1 Star 0 Fork 0

powerpaas/machine

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
driver.go 13.04 KB
一键复制 编辑 原始数据 按行查看 历史
package softlayer
import (
"fmt"
"io/ioutil"
"net"
"os"
"regexp"
"time"
"github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/libmachine/log"
"github.com/docker/machine/libmachine/mcnflag"
"github.com/docker/machine/libmachine/ssh"
"github.com/docker/machine/libmachine/state"
)
const (
APIEndpoint = "https://api.softlayer.com/rest/v3"
)
type Driver struct {
*drivers.BaseDriver
deviceConfig *deviceConfig
Id int
Client *Client
SSHKeyID int
}
type deviceConfig struct {
DiskSize int
Cpu int
Hostname string
Domain string
Region string
Memory int
Image string
HourlyBilling bool
LocalDisk bool
PrivateNet bool
PublicVLAN int
PrivateVLAN int
NetworkMaxSpeed int
}
const (
defaultMemory = 1024
defaultDiskSize = 0
defaultRegion = "dal01"
defaultCpus = 1
defaultImage = "UBUNTU_LATEST"
defaultPublicVLANIP = 0
defaultPrivateVLANIP = 0
defaultNetworkMaxSpeed = 100
)
func NewDriver(hostName, storePath string) drivers.Driver {
return &Driver{
Client: &Client{
Endpoint: APIEndpoint,
},
deviceConfig: &deviceConfig{
HourlyBilling: true,
DiskSize: defaultDiskSize,
Image: defaultImage,
Memory: defaultMemory,
Cpu: defaultCpus,
Region: defaultRegion,
PrivateVLAN: defaultPrivateVLANIP,
PublicVLAN: defaultPublicVLANIP,
NetworkMaxSpeed: defaultNetworkMaxSpeed,
},
BaseDriver: &drivers.BaseDriver{
MachineName: hostName,
StorePath: storePath,
},
}
}
func (d *Driver) GetSSHHostname() (string, error) {
return d.GetIP()
}
func (d *Driver) GetCreateFlags() []mcnflag.Flag {
// Set hourly billing to true by default since codegangsta cli doesn't take default bool values
if os.Getenv("SOFTLAYER_HOURLY_BILLING") == "" {
os.Setenv("SOFTLAYER_HOURLY_BILLING", "true")
}
return []mcnflag.Flag{
mcnflag.IntFlag{
EnvVar: "SOFTLAYER_MEMORY",
Name: "softlayer-memory",
Usage: "Memory in MB for machine",
Value: defaultMemory,
},
mcnflag.IntFlag{
EnvVar: "SOFTLAYER_DISK_SIZE",
Name: "softlayer-disk-size",
Usage: "Disk size for machine, a value of 0 uses the default size on softlayer",
Value: defaultDiskSize,
},
mcnflag.StringFlag{
EnvVar: "SOFTLAYER_USER",
Name: "softlayer-user",
Usage: "softlayer user account name",
},
mcnflag.StringFlag{
EnvVar: "SOFTLAYER_API_KEY",
Name: "softlayer-api-key",
Usage: "softlayer user API key",
},
mcnflag.StringFlag{
EnvVar: "SOFTLAYER_REGION",
Name: "softlayer-region",
Usage: "softlayer region for machine",
Value: defaultRegion,
},
mcnflag.IntFlag{
EnvVar: "SOFTLAYER_CPU",
Name: "softlayer-cpu",
Usage: "number of CPU's for the machine",
Value: defaultCpus,
},
mcnflag.StringFlag{
EnvVar: "SOFTLAYER_HOSTNAME",
Name: "softlayer-hostname",
Usage: "hostname for the machine - defaults to machine name",
},
mcnflag.StringFlag{
EnvVar: "SOFTLAYER_DOMAIN",
Name: "softlayer-domain",
Usage: "domain name for machine",
},
mcnflag.StringFlag{
EnvVar: "SOFTLAYER_API_ENDPOINT",
Name: "softlayer-api-endpoint",
Usage: "softlayer api endpoint to use",
Value: APIEndpoint,
},
mcnflag.BoolFlag{
EnvVar: "SOFTLAYER_HOURLY_BILLING",
Name: "softlayer-hourly-billing",
Usage: "set hourly billing for machine - on by default",
},
mcnflag.BoolFlag{
EnvVar: "SOFTLAYER_LOCAL_DISK",
Name: "softlayer-local-disk",
Usage: "use machine local disk instead of softlayer SAN",
},
mcnflag.BoolFlag{
EnvVar: "SOFTLAYER_PRIVATE_NET",
Name: "softlayer-private-net-only",
Usage: "Use only private networking",
},
mcnflag.StringFlag{
EnvVar: "SOFTLAYER_IMAGE",
Name: "softlayer-image",
Usage: "OS image for machine",
Value: defaultImage,
},
mcnflag.IntFlag{
EnvVar: "SOFTLAYER_PUBLIC_VLAN_ID",
Name: "softlayer-public-vlan-id",
Usage: "",
},
mcnflag.IntFlag{
EnvVar: "SOFTLAYER_PRIVATE_VLAN_ID",
Name: "softlayer-private-vlan-id",
Usage: "",
},
mcnflag.IntFlag{
EnvVar: "SOFTLAYER_NETWORK_MAX_SPEED",
Name: "softlayer-network-max-speed",
Usage: "Max speed of public and private network",
Value: defaultNetworkMaxSpeed,
},
}
}
func validateDeviceConfig(c *deviceConfig) error {
if c.Domain == "" {
return fmt.Errorf("Missing required setting - --softlayer-domain")
}
if c.Region == "" {
return fmt.Errorf("Missing required setting - --softlayer-region")
}
if c.Cpu < 1 {
return fmt.Errorf("Missing required setting - --softlayer-cpu")
}
if c.PrivateNet && c.PublicVLAN > 0 {
return fmt.Errorf("Can not specify both --softlayer-private-net-only and --softlayer-public-vlan-id")
}
if c.PublicVLAN > 0 && c.PrivateVLAN == 0 {
return fmt.Errorf("Missing required setting - --softlayer-private-vlan-id (because --softlayer-public-vlan-id is specified)")
}
if c.PrivateVLAN > 0 && !c.PrivateNet && c.PublicVLAN == 0 {
return fmt.Errorf("Missing required setting - --softlayer-public-vlan-id (because --softlayer-private-vlan-id is specified)")
}
return nil
}
func validateClientConfig(c *Client) error {
if c.ApiKey == "" {
return fmt.Errorf("Missing required setting - --softlayer-api-key")
}
if c.User == "" {
return fmt.Errorf("Missing required setting - --softlayer-user")
}
if c.Endpoint == "" {
return fmt.Errorf("Missing required setting - --softlayer-api-endpoint")
}
return nil
}
func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
d.Client = &Client{
Endpoint: flags.String("softlayer-api-endpoint"),
User: flags.String("softlayer-user"),
ApiKey: flags.String("softlayer-api-key"),
}
d.SetSwarmConfigFromFlags(flags)
d.SSHUser = "root"
d.SSHPort = 22
if err := validateClientConfig(d.Client); err != nil {
return err
}
d.deviceConfig = &deviceConfig{
Hostname: flags.String("softlayer-hostname"),
DiskSize: flags.Int("softlayer-disk-size"),
Cpu: flags.Int("softlayer-cpu"),
Domain: flags.String("softlayer-domain"),
Memory: flags.Int("softlayer-memory"),
PrivateNet: flags.Bool("softlayer-private-net-only"),
LocalDisk: flags.Bool("softlayer-local-disk"),
HourlyBilling: flags.Bool("softlayer-hourly-billing"),
Image: flags.String("softlayer-image"),
Region: flags.String("softlayer-region"),
PublicVLAN: flags.Int("softlayer-public-vlan-id"),
PrivateVLAN: flags.Int("softlayer-private-vlan-id"),
NetworkMaxSpeed: flags.Int("softlayer-network-max-speed"),
}
if d.deviceConfig.Hostname == "" {
d.deviceConfig.Hostname = d.GetMachineName()
}
return validateDeviceConfig(d.deviceConfig)
}
func (d *Driver) getClient() *Client {
return d.Client
}
// DriverName returns the name of the driver
func (d *Driver) DriverName() string {
return "softlayer"
}
func (d *Driver) GetURL() (string, error) {
if err := drivers.MustBeRunning(d); err != nil {
return "", err
}
ip, err := d.GetIP()
if err != nil {
return "", err
}
if ip == "" {
return "", nil
}
return "tcp://" + net.JoinHostPort(ip, "2376"), nil
}
func (d *Driver) GetIP() (string, error) {
if d.IPAddress != "" {
return d.IPAddress, nil
}
if d.deviceConfig != nil && d.deviceConfig.PrivateNet == true {
return d.getClient().VirtualGuest().GetPrivateIP(d.Id)
}
if os.Getenv("SOFTLAYER_DOCKER_ON_PRIVATE_IP") == "" {
os.Setenv("SOFTLAYER_DOCKER_ON_PRIVATE_IP", "false")
}
if os.Getenv("SOFTLAYER_DOCKER_ON_PRIVATE_IP") == "true" {
return d.getClient().VirtualGuest().GetPrivateIP(d.Id)
} else {
return d.getClient().VirtualGuest().GetPublicIP(d.Id)
}
}
func (d *Driver) GetState() (state.State, error) {
s, err := d.getClient().VirtualGuest().PowerState(d.Id)
if err != nil {
return state.None, err
}
var vmState state.State
switch s {
case "Running":
vmState = state.Running
case "Halted":
vmState = state.Stopped
default:
vmState = state.None
}
return vmState, nil
}
func (d *Driver) GetActiveTransaction() (string, error) {
t, err := d.getClient().VirtualGuest().ActiveTransaction(d.Id)
if err != nil {
return "", err
}
return t, nil
}
func (d *Driver) waitForStart() {
log.Infof("Waiting for host to become available")
for {
s, err := d.GetState()
if err != nil {
log.Debugf("Failed to GetState - %+v", err)
continue
}
if s == state.Running {
break
} else {
log.Debugf("Still waiting - state is %s...", s)
}
time.Sleep(2 * time.Second)
}
}
func (d *Driver) getIP() (string, error) {
log.Infof("Getting Host IP")
for {
var (
ip string
err error
)
if d.deviceConfig.PrivateNet {
ip, err = d.getClient().VirtualGuest().GetPrivateIP(d.Id)
} else {
ip, err = d.getClient().VirtualGuest().GetPublicIP(d.Id)
}
if err != nil {
time.Sleep(2 * time.Second)
continue
}
// if docker daemon is expected to run on private IP
// it will overwrite settings from PrivateNet
if os.Getenv("SOFTLAYER_DOCKER_ON_PRIVATE_IP") == "" {
os.Setenv("SOFTLAYER_DOCKER_ON_PRIVATE_IP", "false")
}
if os.Getenv("SOFTLAYER_DOCKER_ON_PRIVATE_IP") == "true" {
ip, err = d.getClient().VirtualGuest().GetPrivateIP(d.Id)
} else {
ip, err = d.getClient().VirtualGuest().GetPublicIP(d.Id)
}
// not a perfect regex, but should be just fine for our needs
exp := regexp.MustCompile(`\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}`)
if exp.MatchString(ip) {
d.IPAddress = ip
return ip, nil
}
time.Sleep(2 * time.Second)
}
}
func (d *Driver) waitForSetupTransactions() {
log.Infof("Waiting for host setup transactions to complete")
// sometimes we'll hit a case where there's no active transaction, but if
// we check again in a few seconds, it moves to the next transaction. We
// don't want to get false-positives, so we check a few times in a row to make sure!
noActiveCount, maxNoActiveCount := 0, 3
for {
t, err := d.GetActiveTransaction()
if err != nil {
noActiveCount = 0
log.Debugf("Failed to GetActiveTransaction - %+v", err)
continue
}
if t == "" {
if noActiveCount == maxNoActiveCount {
break
}
noActiveCount++
} else {
noActiveCount = 0
log.Debugf("Still waiting - active transaction is %s...", t)
}
time.Sleep(2 * time.Second)
}
}
func (d *Driver) Create() error {
spec := d.buildHostSpec()
log.Infof("Creating SSH key...")
key, err := d.createSSHKey()
if err != nil {
return err
}
log.Infof("SSH key %s (%d) created in SoftLayer", key.Label, key.Id)
d.SSHKeyID = key.Id
spec.SshKeys = []*SSHKey{key}
id, err := d.getClient().VirtualGuest().Create(spec)
if err != nil {
return fmt.Errorf("Error creating host: %q", err)
}
d.Id = id
d.getIP()
d.waitForStart()
d.waitForSetupTransactions()
return nil
}
func (d *Driver) buildHostSpec() *HostSpec {
spec := &HostSpec{
Hostname: d.deviceConfig.Hostname,
Domain: d.deviceConfig.Domain,
Cpu: d.deviceConfig.Cpu,
Memory: d.deviceConfig.Memory,
Datacenter: Datacenter{Name: d.deviceConfig.Region},
Os: d.deviceConfig.Image,
HourlyBilling: d.deviceConfig.HourlyBilling,
PrivateNetOnly: d.deviceConfig.PrivateNet,
LocalDisk: d.deviceConfig.LocalDisk,
}
if d.deviceConfig.NetworkMaxSpeed > 0 {
spec.NetworkMaxSpeeds = []NetworkMaxSpeed{{MaxSpeed: d.deviceConfig.NetworkMaxSpeed}}
}
if d.deviceConfig.DiskSize > 0 {
spec.BlockDevices = []BlockDevice{{Device: "0", DiskImage: DiskImage{Capacity: d.deviceConfig.DiskSize}}}
}
if d.deviceConfig.PublicVLAN > 0 {
spec.PrimaryNetworkComponent = &NetworkComponent{
NetworkVLAN: &NetworkVLAN{
Id: d.deviceConfig.PublicVLAN,
},
}
}
if d.deviceConfig.PrivateVLAN > 0 {
spec.PrimaryBackendNetworkComponent = &NetworkComponent{
NetworkVLAN: &NetworkVLAN{
Id: d.deviceConfig.PrivateVLAN,
},
}
}
log.Debugf("Built host spec %#v", spec)
return spec
}
func (d *Driver) createSSHKey() (*SSHKey, error) {
if err := ssh.GenerateSSHKey(d.GetSSHKeyPath()); err != nil {
return nil, err
}
publicKey, err := ioutil.ReadFile(d.publicSSHKeyPath())
if err != nil {
return nil, err
}
key, err := d.getClient().SSHKey().Create(d.deviceConfig.Hostname, string(publicKey))
if err != nil {
return nil, err
}
return key, nil
}
func (d *Driver) publicSSHKeyPath() string {
return d.GetSSHKeyPath() + ".pub"
}
func (d *Driver) Remove() error {
log.Infof("Canceling SoftLayer instance %d...", d.Id)
var err error
for i := 0; i < 5; i++ {
if err = d.getClient().VirtualGuest().Cancel(d.Id); err != nil {
time.Sleep(2 * time.Second)
continue
}
break
}
if err != nil {
return err
}
log.Infof("Removing SSH Key %d...", d.SSHKeyID)
if err = d.getClient().SSHKey().Delete(d.SSHKeyID); err != nil {
return err
}
return nil
}
func (d *Driver) Start() error {
return d.getClient().VirtualGuest().PowerOn(d.Id)
}
func (d *Driver) Stop() error {
return d.getClient().VirtualGuest().PowerOff(d.Id)
}
func (d *Driver) Restart() error {
return d.getClient().VirtualGuest().Reboot(d.Id)
}
func (d *Driver) Kill() error {
return d.Stop()
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/powerpaas/machine.git
git@gitee.com:powerpaas/machine.git
powerpaas
machine
machine
v0.15.0-rancher16

搜索帮助