1 Star 0 Fork 0

powerpaas/machine

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
vsphere.go 26.78 KB
一键复制 编辑 原始数据 按行查看 历史
Chris Kim 提交于 2019-02-22 12:11 . Updating vmware/govmomi to v0.20.0
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131
/*
* Copyright 2014 VMware, Inc. All rights reserved. Licensed under the Apache v2 License.
*/
package vmwarevsphere
import (
"archive/tar"
"encoding/base64"
"fmt"
"io/ioutil"
"net"
"net/url"
"os"
"strings"
"time"
"github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/libmachine/log"
"github.com/docker/machine/libmachine/mcnflag"
"github.com/docker/machine/libmachine/mcnutils"
"github.com/docker/machine/libmachine/ssh"
"github.com/docker/machine/libmachine/state"
"errors"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/guest"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
"golang.org/x/net/context"
)
const (
// dockerBridgeIP is the default IP address of the docker0 bridge.
dockerBridgeIP = "172.17.0.1"
isoFilename = "boot2docker.iso"
// B2DUser is the guest User for tools login
B2DUser = "docker"
// B2DPass is the guest Pass for tools login
B2DPass = "tcuser"
)
type Driver struct {
*drivers.BaseDriver
Memory int
DiskSize int
CPU int
ISO string
Boot2DockerURL string
CPUS int
IP string
Port int
Username string
Password string
Network string
Networks []string
Datastore string
Datacenter string
Folder string
Pool string
HostSystem string
CfgParams []string
CloudInit string
VAppIpProtocol string
VAppIpAllocationPolicy string
VAppTransport string
VAppProperties []string
SSHPassword string
}
const (
defaultSSHUser = B2DUser
defaultSSHPass = B2DPass
defaultCpus = 2
defaultMemory = 2048
defaultDiskSize = 20480
defaultSDKPort = 443
)
// GetCreateFlags registers the flags this driver adds to
// "docker-machine create"
func (d *Driver) GetCreateFlags() []mcnflag.Flag {
return []mcnflag.Flag{
mcnflag.IntFlag{
EnvVar: "VSPHERE_CPU_COUNT",
Name: "vmwarevsphere-cpu-count",
Usage: "vSphere CPU number for docker VM",
Value: defaultCpus,
},
mcnflag.IntFlag{
EnvVar: "VSPHERE_MEMORY_SIZE",
Name: "vmwarevsphere-memory-size",
Usage: "vSphere size of memory for docker VM (in MB)",
Value: defaultMemory,
},
mcnflag.IntFlag{
EnvVar: "VSPHERE_DISK_SIZE",
Name: "vmwarevsphere-disk-size",
Usage: "vSphere size of disk for docker VM (in MB)",
Value: defaultDiskSize,
},
mcnflag.StringFlag{
EnvVar: "VSPHERE_BOOT2DOCKER_URL",
Name: "vmwarevsphere-boot2docker-url",
Usage: "vSphere URL for boot2docker image",
},
mcnflag.StringFlag{
EnvVar: "VSPHERE_VCENTER",
Name: "vmwarevsphere-vcenter",
Usage: "vSphere IP/hostname for vCenter",
},
mcnflag.IntFlag{
EnvVar: "VSPHERE_VCENTER_PORT",
Name: "vmwarevsphere-vcenter-port",
Usage: "vSphere Port for vCenter",
Value: defaultSDKPort,
},
mcnflag.StringFlag{
EnvVar: "VSPHERE_USERNAME",
Name: "vmwarevsphere-username",
Usage: "vSphere username",
},
mcnflag.StringFlag{
EnvVar: "VSPHERE_PASSWORD",
Name: "vmwarevsphere-password",
Usage: "vSphere password",
},
mcnflag.StringSliceFlag{
EnvVar: "VSPHERE_NETWORK",
Name: "vmwarevsphere-network",
Usage: "vSphere network where the docker VM will be attached",
},
mcnflag.StringFlag{
EnvVar: "VSPHERE_DATASTORE",
Name: "vmwarevsphere-datastore",
Usage: "vSphere datastore for docker VM",
},
mcnflag.StringFlag{
EnvVar: "VSPHERE_DATACENTER",
Name: "vmwarevsphere-datacenter",
Usage: "vSphere datacenter for docker VM",
},
mcnflag.StringFlag{
EnvVar: "VSPHERE_FOLDER",
Name: "vmwarevsphere-folder",
Usage: "vSphere folder for the docker VM. This folder must already exist in the datacenter.",
},
mcnflag.StringFlag{
EnvVar: "VSPHERE_POOL",
Name: "vmwarevsphere-pool",
Usage: "vSphere resource pool for docker VM",
},
mcnflag.StringFlag{
EnvVar: "VSPHERE_HOSTSYSTEM",
Name: "vmwarevsphere-hostsystem",
Usage: "vSphere compute resource where the docker VM will be instantiated. This can be omitted if using a cluster with DRS.",
},
mcnflag.StringSliceFlag{
EnvVar: "VSPHERE_CFGPARAM",
Name: "vmwarevsphere-cfgparam",
Usage: "vSphere vm configuration parameters (used for guestinfo)",
},
mcnflag.StringFlag{
EnvVar: "VSPHERE_CLOUDINIT",
Name: "vmwarevsphere-cloudinit",
Usage: "vSphere cloud-init file or url to set in the guestinfo",
},
mcnflag.StringFlag{
EnvVar: "VSPHERE_VAPP_IPPROTOCOL",
Name: "vmwarevsphere-vapp-ipprotocol",
Usage: "vSphere vApp IP protocol for this deployment. Supported values are: IPv4 and IPv6",
},
mcnflag.StringFlag{
EnvVar: "VSPHERE_VAPP_IPALLOCATIONPOLICY",
Name: "vmwarevsphere-vapp-ipallocationpolicy",
Usage: "vSphere vApp IP allocation policy. Supported values are: dhcp, fixed, transient and fixedAllocated",
},
mcnflag.StringFlag{
EnvVar: "VSPHERE_VAPP_TRANSPORT",
Name: "vmwarevsphere-vapp-transport",
Usage: "vSphere OVF environment transports to use for properties. Supported values are: iso and com.vmware.guestInfo",
},
mcnflag.StringSliceFlag{
EnvVar: "VSPHERE_VAPP_PROPERTY",
Name: "vmwarevsphere-vapp-property",
Usage: "vSphere vApp properties",
},
}
}
func NewDriver(hostName, storePath string) drivers.Driver {
return &Driver{
CPUS: defaultCpus,
Memory: defaultMemory,
DiskSize: defaultDiskSize,
SSHPassword: defaultSSHPass,
Port: defaultSDKPort,
BaseDriver: &drivers.BaseDriver{
SSHUser: defaultSSHUser,
MachineName: hostName,
StorePath: storePath,
},
}
}
func (d *Driver) GetSSHHostname() (string, error) {
return d.GetIP()
}
func (d *Driver) GetSSHUsername() string {
if d.SSHUser == "" {
d.SSHUser = "docker"
}
return d.SSHUser
}
// DriverName returns the name of the driver
func (d *Driver) DriverName() string {
return "vmwarevsphere"
}
func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
d.SSHUser = "docker"
d.SSHPort = 22
d.CPU = flags.Int("vmwarevsphere-cpu-count")
d.Memory = flags.Int("vmwarevsphere-memory-size")
d.DiskSize = flags.Int("vmwarevsphere-disk-size")
d.Boot2DockerURL = flags.String("vmwarevsphere-boot2docker-url")
d.IP = flags.String("vmwarevsphere-vcenter")
d.Port = flags.Int("vmwarevsphere-vcenter-port")
d.Username = flags.String("vmwarevsphere-username")
d.Password = flags.String("vmwarevsphere-password")
d.Networks = flags.StringSlice("vmwarevsphere-network")
d.Datastore = flags.String("vmwarevsphere-datastore")
d.Datacenter = flags.String("vmwarevsphere-datacenter")
// Sanitize input on ingress.
d.Folder = strings.Trim(flags.String("vmwarevsphere-folder"), "/")
d.Pool = flags.String("vmwarevsphere-pool")
d.HostSystem = flags.String("vmwarevsphere-hostsystem")
d.CfgParams = flags.StringSlice("vmwarevsphere-cfgparam")
d.CloudInit = flags.String("vmwarevsphere-cloudinit")
d.VAppIpProtocol = flags.String("vmwarevsphere-vapp-ipprotocol")
d.VAppIpAllocationPolicy = flags.String("vmwarevsphere-vapp-ipallocationpolicy")
d.VAppTransport = flags.String("vmwarevsphere-vapp-transport")
d.VAppProperties = flags.StringSlice("vmwarevsphere-vapp-property")
d.SetSwarmConfigFromFlags(flags)
d.ISO = d.ResolveStorePath(isoFilename)
return nil
}
func (d *Driver) GetURL() (string, error) {
ip, err := d.GetIP()
if err != nil {
return "", err
}
if ip == "" {
return "", nil
}
return fmt.Sprintf("tcp://%s", net.JoinHostPort(ip, "2376")), nil
}
func (d *Driver) GetIP() (string, error) {
status, err := d.GetState()
if status != state.Running {
return "", drivers.ErrHostIsNotRunning
}
// Create context
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c, err := d.vsphereLogin(ctx)
if err != nil {
return "", err
}
defer c.Logout(ctx)
vm, err := d.fetchVM(ctx, c, d.MachineName)
if err != nil {
return "", err
}
configuredMacIPs, err := vm.WaitForNetIP(ctx, false)
if err != nil {
return "", err
}
for _, ips := range configuredMacIPs {
if len(ips) >= 0 {
// Prefer IPv4 address, but fall back to first/IPv6
preferredIP := ips[0]
for _, ip := range ips {
// In addition to non IPv4 addresses, try to filter
// out link local addresses and the default address of
// the Docker0 bridge
netIP := net.ParseIP(ip)
if netIP.To4() != nil && netIP.IsGlobalUnicast() && !netIP.Equal(net.ParseIP(dockerBridgeIP)) {
preferredIP = ip
break
}
}
return preferredIP, nil
}
}
return "", errors.New("No IP despite waiting for one - check DHCP status")
}
func (d *Driver) GetState() (state.State, error) {
// Create context
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c, err := d.vsphereLogin(ctx)
if err != nil {
return state.None, err
}
defer c.Logout(ctx)
vm, err := d.fetchVM(ctx, c, d.MachineName)
if err != nil {
return state.None, err
}
var mvm mo.VirtualMachine
err = c.RetrieveOne(ctx, vm.Reference(), nil, &mvm)
if err != nil {
return state.None, nil
}
s := mvm.Summary
if strings.Contains(string(s.Runtime.PowerState), "poweredOn") {
return state.Running, nil
} else if strings.Contains(string(s.Runtime.PowerState), "poweredOff") {
return state.Stopped, nil
}
return state.None, nil
}
// PreCreateCheck checks that the machine creation process can be started safely.
func (d *Driver) PreCreateCheck() error {
log.Debug("Connecting to vSphere for pre-create checks...")
// Create context
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c, err := d.vsphereLogin(ctx)
if err != nil {
return err
}
defer c.Logout(ctx)
// Create a new finder
f := find.NewFinder(c.Client, true)
dc, err := f.DatacenterOrDefault(ctx, d.Datacenter)
if err != nil {
return err
}
f.SetDatacenter(dc)
// Folder
if d.Folder != "" {
// Find the specified Folder to create the VM in.
folders, err := dc.Folders(ctx)
if err != nil {
return err
}
folder, err := f.Folder(ctx, fmt.Sprintf("%s/%s", folders.VmFolder.InventoryPath, d.Folder))
// It's an error to not find the folder, or for the search itself to fail.
if err != nil {
// The search itself failed.
return err
}
if folder == nil {
return fmt.Errorf("failed to find VM Folder '%s'", d.Folder)
}
}
if _, err := f.DatastoreOrDefault(ctx, d.Datastore); err != nil {
return err
}
// TODO: if the user has both the VSPHERE_NETWORK defined and adds --vmwarevsphere-network
// both are used at the same time - probably should detect that and remove the one from ENV
if len(d.Networks) == 0 {
// machine assumes there will be a network
d.Networks = append(d.Networks, "VM Network")
}
for _, netName := range d.Networks {
if _, err := f.NetworkOrDefault(ctx, netName); err != nil {
return err
}
}
// d.Network needs to remain a string to cope with existing machines :/
d.Network = d.Networks[0]
var hs *object.HostSystem
if d.HostSystem != "" {
var err error
hs, err = f.HostSystemOrDefault(ctx, d.HostSystem)
if err != nil {
return err
}
}
// ResourcePool
if d.Pool != "" {
// Find specified Resource Pool
if _, err := f.ResourcePool(ctx, d.Pool); err != nil {
return err
}
} else if hs != nil {
// Pick default Resource Pool for Host System
if _, err := hs.ResourcePool(ctx); err != nil {
return err
}
} else {
// Pick the default Resource Pool for the Datacenter.
if _, err := f.DefaultResourcePool(ctx); err != nil {
return err
}
}
return nil
}
// Create has the following implementation:
// 1. check whether the docker directory contains the boot2docker ISO
// 2. generate an SSH keypair and bundle it in a tar.
// 3. create a virtual machine with the boot2docker ISO mounted;
// 4. reconfigure the virtual machine network and disk size;
func (d *Driver) Create() error {
b2dutils := mcnutils.NewB2dUtils(d.StorePath)
if err := b2dutils.CopyIsoToMachineDir(d.Boot2DockerURL, d.MachineName); err != nil {
return err
}
log.Infof("Generating SSH Keypair...")
if err := ssh.GenerateSSHKey(d.GetSSHKeyPath()); err != nil {
return err
}
// Create context
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c, err := d.vsphereLogin(ctx)
if err != nil {
return err
}
defer c.Logout(ctx)
// Create a new finder
f := find.NewFinder(c.Client, true)
dc, err := f.DatacenterOrDefault(ctx, d.Datacenter)
if err != nil {
return err
}
f.SetDatacenter(dc)
dss, err := f.DatastoreOrDefault(ctx, d.Datastore)
if err != nil {
return err
}
networks := make(map[string]object.NetworkReference)
for _, netName := range d.Networks {
net, err := f.NetworkOrDefault(ctx, netName)
if err != nil {
return err
}
networks[netName] = net
}
var hs *object.HostSystem
if d.HostSystem != "" {
var err error
hs, err = f.HostSystemOrDefault(ctx, d.HostSystem)
if err != nil {
return err
}
}
var rp *object.ResourcePool
if d.Pool != "" {
// Find specified Resource Pool
rp, err = f.ResourcePool(ctx, d.Pool)
if err != nil {
return err
}
} else if d.HostSystem != "" {
// Pick default Resource Pool for Host System
rp, err = hs.ResourcePool(ctx)
if err != nil {
return err
}
} else {
// Pick the default Resource Pool for the Datacenter.
rp, err = f.DefaultResourcePool(ctx)
if err != nil {
return err
}
}
spec := types.VirtualMachineConfigSpec{
Name: d.MachineName,
GuestId: "otherLinux64Guest",
Files: &types.VirtualMachineFileInfo{VmPathName: fmt.Sprintf("[%s]", dss.Name())},
NumCPUs: int32(d.CPU),
MemoryMB: int64(d.Memory),
}
scsi, err := object.SCSIControllerTypes().CreateSCSIController("pvscsi")
if err != nil {
return err
}
spec.DeviceChange = append(spec.DeviceChange, &types.VirtualDeviceConfigSpec{
Operation: types.VirtualDeviceConfigSpecOperationAdd,
Device: scsi,
})
if d.VAppTransport == "com.vmware.guestInfo" ||
d.VAppTransport == "iso" {
vApp := types.VmConfigSpec{
OvfEnvironmentTransport: []string{d.VAppTransport},
}
if d.VAppIpAllocationPolicy == "dhcp" ||
d.VAppIpAllocationPolicy == "fixed" ||
d.VAppIpAllocationPolicy == "transient" ||
d.VAppIpAllocationPolicy == "fixedAllocated" {
if d.VAppIpProtocol != "IPv4" &&
d.VAppIpProtocol != "IPv6" {
d.VAppIpProtocol = "IPv4"
}
supportedAllocationScheme := "ovfenv"
if d.VAppIpAllocationPolicy == "dhcp" {
supportedAllocationScheme = "dhcp"
}
vApp.IpAssignment = &types.VAppIPAssignmentInfo{
SupportedIpProtocol: []string{d.VAppIpProtocol},
SupportedAllocationScheme: []string{supportedAllocationScheme},
IpProtocol: d.VAppIpProtocol,
IpAllocationPolicy: d.VAppIpAllocationPolicy + "Policy",
}
}
for i, prop := range d.VAppProperties {
v := strings.SplitN(prop, "=", 2)
key := v[0]
typ := "string"
value := ""
if len(v) > 1 {
value = v[1]
}
if strings.HasPrefix(value, "ip:") {
typ = value
value = ""
} else if strings.HasPrefix(value, "${") &&
strings.HasSuffix(value, "}") {
typ = "expression"
}
vApp.Property = append(vApp.Property, types.VAppPropertySpec{
ArrayUpdateSpec: types.ArrayUpdateSpec{
Operation: types.ArrayUpdateOperationAdd,
},
Info: &types.VAppPropertyInfo{
Key: int32(i),
Id: key,
Type: typ,
DefaultValue: value,
},
})
}
spec.VAppConfig = &vApp
}
log.Infof("Creating VM...")
folders, err := dc.Folders(ctx)
folder := folders.VmFolder
if d.Folder != "" {
folder, err = f.Folder(ctx, fmt.Sprintf("%s/%s", folders.VmFolder.InventoryPath, d.Folder))
if err != nil {
return err
}
}
task, err := folder.CreateVM(ctx, spec, rp, hs)
if err != nil {
return err
}
info, err := task.WaitForResult(ctx, nil)
if err != nil {
return err
}
log.Infof("Uploading Boot2docker ISO ...")
dsurl, err := dss.URL(ctx, dc, fmt.Sprintf("%s/%s", d.MachineName, isoFilename))
if err != nil {
return err
}
p := soap.DefaultUpload
if err = c.Client.UploadFile(ctx, d.ISO, dsurl, &p); err != nil {
return err
}
// Retrieve the new VM
vm := object.NewVirtualMachine(c.Client, info.Result.(types.ManagedObjectReference))
devices, err := vm.Device(ctx)
if err != nil {
return err
}
var add []types.BaseVirtualDevice
controller, err := devices.FindDiskController("scsi")
if err != nil {
return err
}
disk := devices.CreateDisk(controller, dss.Reference(),
dss.Path(fmt.Sprintf("%s/%s.vmdk", d.MachineName, d.MachineName)))
// Convert MB to KB
disk.CapacityInKB = int64(d.DiskSize) * 1024
add = append(add, disk)
ide, err := devices.FindIDEController("")
if err != nil {
return err
}
cdrom, err := devices.CreateCdrom(ide)
if err != nil {
return err
}
add = append(add, devices.InsertIso(cdrom, dss.Path(fmt.Sprintf("%s/%s", d.MachineName, isoFilename))))
for _, netName := range d.Networks {
backing, err := networks[netName].EthernetCardBackingInfo(ctx)
if err != nil {
return err
}
netdev, err := object.EthernetCardTypes().CreateEthernetCard("vmxnet3", backing)
if err != nil {
return err
}
log.Infof("adding network: %s", netName)
add = append(add, netdev)
}
log.Infof("Reconfiguring VM")
if vm.AddDevice(ctx, add...); err != nil {
return err
}
// Adding some guestinfo data
var opts []types.BaseOptionValue
for _, param := range d.CfgParams {
v := strings.SplitN(param, "=", 2)
key := v[0]
value := ""
if len(v) > 1 {
value = v[1]
}
fmt.Printf("Setting %s to %s\n", key, value)
opts = append(opts, &types.OptionValue{
Key: key,
Value: value,
})
}
if d.CloudInit != "" {
if _, err := url.ParseRequestURI(d.CloudInit); err == nil {
log.Infof("setting guestinfo.cloud-init.data.url to %s\n", d.CloudInit)
opts = append(opts, &types.OptionValue{
Key: "guestinfo.cloud-init.config.url",
Value: d.CloudInit,
})
} else {
if _, err := os.Stat(d.CloudInit); err == nil {
if value, err := ioutil.ReadFile(d.CloudInit); err == nil {
log.Infof("setting guestinfo.cloud-init.data to encoded content of %s\n", d.CloudInit)
encoded := base64.StdEncoding.EncodeToString(value)
opts = append(opts, &types.OptionValue{
Key: "guestinfo.cloud-init.config.data",
Value: encoded,
})
opts = append(opts, &types.OptionValue{
Key: "guestinfo.cloud-init.data.encoding",
Value: "base64",
})
}
}
}
}
task, err = vm.Reconfigure(ctx, types.VirtualMachineConfigSpec{
ExtraConfig: opts,
})
if err != nil {
return err
}
task.Wait(ctx)
if err := d.Start(); err != nil {
return err
}
log.Infof("Provisioning certs and ssh keys...")
// Generate a tar keys bundle
if err := d.generateKeyBundle(); err != nil {
return err
}
opman := guest.NewOperationsManager(c.Client, vm.Reference())
fileman, err := opman.FileManager(ctx)
if err != nil {
return err
}
src := d.ResolveStorePath("userdata.tar")
s, err := os.Stat(src)
if err != nil {
return err
}
auth := AuthFlag{}
flag := FileAttrFlag{}
auth.auth.Username = B2DUser
auth.auth.Password = B2DPass
flag.SetPerms(0, 0, 660)
url, err := fileman.InitiateFileTransferToGuest(ctx, auth.Auth(), "/home/docker/userdata.tar", flag.Attr(), s.Size(), true)
if err != nil {
return err
}
u, err := c.Client.ParseURL(url)
if err != nil {
return err
}
if err = c.Client.UploadFile(ctx, src, u, nil); err != nil {
return err
}
procman, err := opman.ProcessManager(ctx)
if err != nil {
return err
}
// first, untar - only boot2docker has /var/lib/boot2docker
// TODO: don't hard-code to docker & staff - they are also just b2d
var env []string
guestspec := types.GuestProgramSpec{
ProgramPath: "/usr/bin/sudo",
Arguments: "/usr/bin/sudo /bin/sh -c \"tar xvf /home/docker/userdata.tar -C /home/docker > /var/log/userdata.log 2>&1 && chown -R docker:staff /home/docker\"",
WorkingDirectory: "",
EnvVariables: env,
}
_, err = procman.StartProgram(ctx, auth.Auth(), &guestspec)
if err != nil {
return err
}
// now move to /var/lib/boot2docker if its there
guestspec = types.GuestProgramSpec{
ProgramPath: "/usr/bin/sudo",
Arguments: "/bin/mv /home/docker/userdata.tar /var/lib/boot2docker/userdata.tar",
WorkingDirectory: "",
EnvVariables: env,
}
_, err = procman.StartProgram(ctx, auth.Auth(), &guestspec)
if err != nil {
return err
}
return nil
}
func (d *Driver) Start() error {
machineState, err := d.GetState()
if err != nil {
return err
}
switch machineState {
case state.Running:
log.Infof("VM %s has already been started", d.MachineName)
return nil
case state.Stopped:
// TODO add transactional or error handling in the following steps
// Create context
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c, err := d.vsphereLogin(ctx)
if err != nil {
return err
}
defer c.Logout(ctx)
vm, err := d.fetchVM(ctx, c, d.MachineName)
if err != nil {
return err
}
task, err := vm.PowerOn(ctx)
if err != nil {
return err
}
_, err = task.WaitForResult(ctx, nil)
if err != nil {
return err
}
log.Infof("Waiting for VMware Tools to come online...")
if d.IPAddress, err = d.GetIP(); err != nil {
return err
}
}
return nil
}
func (d *Driver) Stop() error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c, err := d.vsphereLogin(ctx)
if err != nil {
return err
}
defer c.Logout(ctx)
vm, err := d.fetchVM(ctx, c, d.MachineName)
if err != nil {
return err
}
if err := vm.ShutdownGuest(ctx); err != nil {
return err
}
d.IPAddress = ""
return nil
}
func (d *Driver) Restart() error {
if err := d.Stop(); err != nil {
return err
}
// Check for 120 seconds for the machine to stop
for i := 1; i <= 60; i++ {
machineState, err := d.GetState()
if err != nil {
return err
}
if machineState == state.Running {
log.Debugf("Not there yet %d/%d", i, 60)
time.Sleep(2 * time.Second)
continue
}
if machineState == state.Stopped {
break
}
}
machineState, err := d.GetState()
// If the VM is still running after 120 seconds just kill it.
if machineState == state.Running {
if err = d.Kill(); err != nil {
return fmt.Errorf("can't stop VM: %s", err)
}
}
return d.Start()
}
func (d *Driver) Kill() error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c, err := d.vsphereLogin(ctx)
if err != nil {
return err
}
defer c.Logout(ctx)
vm, err := d.fetchVM(ctx, c, d.MachineName)
if err != nil {
return err
}
task, err := vm.PowerOff(ctx)
if err != nil {
return err
}
_, err = task.WaitForResult(ctx, nil)
if err != nil {
return err
}
d.IPAddress = ""
return nil
}
func (d *Driver) Remove() error {
machineState, err := d.GetState()
if err != nil {
return err
}
if machineState == state.Running {
if err = d.Kill(); err != nil {
return fmt.Errorf("can't stop VM: %s", err)
}
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c, err := d.vsphereLogin(ctx)
if err != nil {
return err
}
defer c.Logout(ctx)
// Create a new finder
f := find.NewFinder(c.Client, true)
dc, err := f.DatacenterOrDefault(ctx, d.Datacenter)
if err != nil {
return err
}
f.SetDatacenter(dc)
dss, err := f.DatastoreOrDefault(ctx, d.Datastore)
if err != nil {
return err
}
// Remove B2D Iso from VM folder
m := object.NewFileManager(c.Client)
task, err := m.DeleteDatastoreFile(ctx, dss.Path(fmt.Sprintf("%s/%s", d.MachineName, isoFilename)), dc)
if err != nil {
return err
}
err = task.Wait(ctx)
if err != nil {
if types.IsFileNotFound(err) {
// Ignore error
return nil
}
}
vm, err := d.fetchVM(ctx, c, d.MachineName)
if err != nil {
return err
}
task, err = vm.Destroy(ctx)
if err != nil {
return err
}
_, err = task.WaitForResult(ctx, nil)
if err != nil {
return err
}
return nil
}
func (d *Driver) Upgrade() error {
return fmt.Errorf("upgrade is not supported for vsphere driver at this moment")
}
func (d *Driver) publicSSHKeyPath() string {
return d.GetSSHKeyPath() + ".pub"
}
// Make a boot2docker userdata.tar key bundle
func (d *Driver) generateKeyBundle() error {
log.Debugf("Creating Tar key bundle...")
magicString := "boot2docker, this is vmware speaking"
tf, err := os.Create(d.ResolveStorePath("userdata.tar"))
if err != nil {
return err
}
defer tf.Close()
var fileWriter = tf
tw := tar.NewWriter(fileWriter)
defer tw.Close()
// magicString first so we can figure out who originally wrote the tar.
file := &tar.Header{Name: magicString, Size: int64(len(magicString))}
if err := tw.WriteHeader(file); err != nil {
return err
}
if _, err := tw.Write([]byte(magicString)); err != nil {
return err
}
// .ssh/key.pub => authorized_keys
file = &tar.Header{Name: ".ssh", Typeflag: tar.TypeDir, Mode: 0700}
if err := tw.WriteHeader(file); err != nil {
return err
}
pubKey, err := ioutil.ReadFile(d.publicSSHKeyPath())
if err != nil {
return err
}
file = &tar.Header{Name: ".ssh/authorized_keys", Size: int64(len(pubKey)), Mode: 0644}
if err := tw.WriteHeader(file); err != nil {
return err
}
if _, err := tw.Write([]byte(pubKey)); err != nil {
return err
}
file = &tar.Header{Name: ".ssh/authorized_keys2", Size: int64(len(pubKey)), Mode: 0644}
if err := tw.WriteHeader(file); err != nil {
return err
}
if _, err := tw.Write([]byte(pubKey)); err != nil {
return err
}
err = tw.Close()
return err
}
func (d *Driver) vsphereLogin(ctx context.Context) (*govmomi.Client, error) {
// Parse URL from string
u, err := url.Parse(fmt.Sprintf("https://%s:%d/sdk", d.IP, d.Port))
if err != nil {
return nil, err
}
// set username and password for the URL
u.User = url.UserPassword(d.Username, d.Password)
// Connect and log in to ESX or vCenter
c, err := govmomi.NewClient(ctx, u, true)
if err != nil {
return nil, err
}
return c, nil
}
func (d *Driver) fetchVM(ctx context.Context, c *govmomi.Client, vmname string) (*object.VirtualMachine, error) {
// Create a new finder
f := find.NewFinder(c.Client, true)
var vm *object.VirtualMachine
var err error
dc, err := f.DatacenterOrDefault(ctx, d.Datacenter)
if err != nil {
return vm, err
}
f.SetDatacenter(dc)
vmPath := vmname
if d.Folder != "" {
vmPath = fmt.Sprintf("%s/%s", d.Folder, vmname)
}
vm, err = f.VirtualMachine(ctx, vmPath)
if err != nil {
return vm, err
}
return vm, nil
}
type AuthFlag struct {
auth types.NamePasswordAuthentication
}
func (f *AuthFlag) Auth() types.BaseGuestAuthentication {
return &f.auth
}
type FileAttrFlag struct {
types.GuestPosixFileAttributes
}
func (f *FileAttrFlag) SetPerms(owner, group, perms int) {
owner32 := int32(owner)
group32 := int32(group)
f.OwnerId = &owner32
f.GroupId = &group32
f.Permissions = int64(perms)
}
func (f *FileAttrFlag) Attr() types.BaseGuestFileAttributes {
return &f.GuestPosixFileAttributes
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/powerpaas/machine.git
git@gitee.com:powerpaas/machine.git
powerpaas
machine
machine
v0.15.0-rancher5

搜索帮助