Docker-machine-driver-xenserver derivado do docker-machine-driver-xcp, apenas para compatibilidade de clusters Rancher criados com o driver antigo
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

1104 lines
26 KiB

//This file was modified from the original work, https://github.com/xenserver/docker-machine-driver-xenserver
package xcp
import (
"archive/tar"
"bytes"
"crypto/rand"
"crypto/tls"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"strings"
"time"
"path"
"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"
"github.com/nilshell/xmlrpc"
xsclient "github.com/xenserver/go-xenserver-client"
"golang.org/x/net/context"
diskfs "github.com/diskfs/go-diskfs"
"github.com/diskfs/go-diskfs/disk"
"github.com/diskfs/go-diskfs/filesystem"
"github.com/diskfs/go-diskfs/filesystem/iso9660"
)
const (
isoFilename = "boot2docker.iso"
tarFilename = "boot2docker.tar"
B2D_USER = "docker"
B2D_PASS = "tcuser"
)
type Driver struct {
*drivers.BaseDriver
Server string
Username string
Password string
Boot2DockerURL string
CPU uint
Memory uint
DiskSize uint
SR string
Network string
Host string
ISO string
TAR string
UploadTimeout uint
WaitTimeout uint
CaCertPath string
PrivateKeyPath string
osTemplateLabelName string
CoreosConfigDrive bool
xenAPIClient *XenAPIClient
}
// GetCreateFlags registers the flags this driver adds to
// "docker hosts create"
func (d *Driver) GetCreateFlags() []mcnflag.Flag {
return []mcnflag.Flag{
mcnflag.StringFlag{
EnvVar: "XCP_SERVER",
Name: "xcp-server",
Usage: "XCP server hostname/IP for docker VM",
},
mcnflag.StringFlag{
EnvVar: "XCP_USERNAME",
Name: "xcp-username",
Usage: "XCP username",
},
mcnflag.StringFlag{
EnvVar: "XCP_PASSWORD",
Name: "xcp-password",
Usage: "XCP password",
},
mcnflag.StringFlag{
EnvVar: "XCP_BOOT2DOCKER_URL",
Name: "xcp-boot2docker-url",
Usage: "XCP URL for boot2docker image",
},
mcnflag.IntFlag{
EnvVar: "XCP_VCPU_COUNT",
Name: "xcp-vcpu-count",
Usage: "XCP vCPU number for docker VM",
Value: 1,
},
mcnflag.IntFlag{
EnvVar: "XCP_MEMORY_SIZE",
Name: "xcp-memory-size",
Usage: "XCP size of memory for docker VM (in MB)",
Value: 1024,
},
mcnflag.IntFlag{
EnvVar: "XCP_DISK_SIZE",
Name: "xcp-disk-size",
Usage: "XCP size of disk for docker VM (in MB)",
Value: 5120,
},
mcnflag.StringFlag{
EnvVar: "XCP_SR_LABEL",
Name: "xcp-sr-label",
Usage: "XCP SR label where the docker VM will be attached",
},
mcnflag.StringFlag{
EnvVar: "XCP_NETWORK_LABEL",
Name: "xcp-network-label",
Usage: "XCP network label where the docker VM will be attached",
},
mcnflag.StringFlag{
EnvVar: "XCP_HOST_LABEL",
Name: "xcp-host-label",
Usage: "XCP host label where the docker VM will be run",
},
mcnflag.IntFlag{
EnvVar: "XCP_UPLOAD_TIMEOUT",
Name: "xcp-upload-timeout",
Usage: "XCP upload VDI timeout(seconds)",
Value: 5 * 60,
},
mcnflag.IntFlag{
EnvVar: "XCP_WAIT_TIMEOUT",
Name: "xcp-wait-timeout",
Usage: "XCP wait VM start timeout(seconds)",
Value: 30 * 60,
},
mcnflag.StringFlag{
EnvVar: "XCP_OS_TEMPLATE",
Name: "xcp-os-template",
Usage: "XCP OS Template Label Name",
Value: "Other install media",
},
mcnflag.StringFlag{
EnvVar: "XCP_OS_USERNAME",
Name: "xcp-os-username",
Usage: "XCP Username used to SSH into guest OS",
Value: B2D_USER,
},
mcnflag.BoolFlag{
EnvVar: "XCP_COREOS_CONFIGDRIVE",
Name: "xcp-coreos-configdrive",
Usage: "XCP Enable CoreOS specific ConfigDrive (requires xscontainer supplemental pack)",
},
}
}
func NewDriver() *Driver {
return &Driver{}
}
func (d *Driver) GetMachineName() string {
return d.MachineName
}
func (d *Driver) GetSSHHostname() (string, error) {
return d.GetIP()
}
func (d *Driver) GetSSHPort() (int, error) {
if d.SSHPort == 0 {
d.SSHPort = 22
}
return d.SSHPort, nil
}
func (d *Driver) GetSSHUsername() string {
if d.SSHUser == "" {
d.SSHUser = B2D_USER
}
return d.SSHUser
}
func (d *Driver) DriverName() string {
return "xcp"
}
func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
d.SSHUser = flags.String("xcp-os-username")
d.SSHPort = 22
d.Server = flags.String("xcp-server")
d.Username = flags.String("xcp-username")
d.Password = flags.String("xcp-password")
d.Boot2DockerURL = flags.String("xcp-boot2docker-url")
d.CPU = uint(flags.Int("xcp-vcpu-count"))
d.Memory = uint(flags.Int("xcp-memory-size"))
d.DiskSize = uint(flags.Int("xcp-disk-size"))
d.SR = flags.String("xcp-sr-label")
d.Network = flags.String("xcp-network-label")
d.Host = flags.String("xcp-host-label")
d.UploadTimeout = uint(flags.Int("xcp-upload-timeout"))
d.WaitTimeout = uint(flags.Int("xcp-wait-timeout"))
d.SwarmMaster = flags.Bool("swarm-master")
d.SwarmHost = flags.String("swarm-host")
d.SwarmDiscovery = flags.String("swarm-discovery")
d.ISO = d.ResolveStorePath(isoFilename)
d.TAR = d.ResolveStorePath(tarFilename)
d.osTemplateLabelName = flags.String("xcp-os-template")
d.CoreosConfigDrive = flags.Bool("xcp-coreos-configdrive")
return nil
}
func (d *Driver) GetURL() (string, error) {
ip, _ := d.GetIP()
if ip == "" {
return "", nil
}
return fmt.Sprintf("tcp://%s:2376", ip), nil
}
func (d *Driver) GetIP() (string, error) {
status, err := d.GetState()
if err != nil {
return "", err
}
if status != state.Running {
return "", fmt.Errorf("Docker VM(%v) is not running", d.MachineName)
}
// Need Login first if it is a fresh session
c, err := d.GetXenAPIClient()
if err != nil {
return "", err
}
// Get doker machine by label name
vm, err := c.GetUniqueVMByNameLabel(d.MachineName)
if err != nil {
return "", err
}
// Get docker machine VM metrics
metrics, err := vm.GetGuestMetrics()
if err != nil {
return "", err
}
// Get networks metrics
networks, ok := metrics["networks"]
if !ok {
return "", fmt.Errorf("Docker VM(%v) get network metrics error: \"networks\" not presented", d.MachineName)
}
net, ok := networks.(xmlrpc.Struct)
if !ok {
return "", fmt.Errorf("Docker VM(%v) get network metrics error: \"networks\" is not a xmlrpc.Struct instance", d.MachineName)
}
for i := 0; i < len(net); i++ {
ip, ok := net[fmt.Sprintf("%d/ip", i)]
if ok && ip != "" {
return ip.(string), nil
}
}
return "", fmt.Errorf("Docker VM(%v) get ipaddr error", d.MachineName)
}
func (d *Driver) GetState() (state.State, error) {
var err error
// Need Login first if it is a fresh session
c, err := d.GetXenAPIClient()
if err != nil {
return state.None, err
}
// Get docker machine VM by label name
vm, err := c.GetUniqueVMByNameLabel(d.MachineName)
if err != nil {
return state.None, err
}
// Get doker machine VM power state
powerState, err := vm.GetPowerState()
if err != nil {
return state.None, err
}
// https://github.com/xapi-project/xen-api/blob/cacbb2d3c5996efbf4cf117ab862439271a8cecd/ocaml/client_records/record_util.ml#L20
switch strings.ToLower(powerState) {
case "halted":
return state.Stopped, nil
case "paused":
return state.Paused, nil
case "running":
return state.Running, nil
case "suspended":
return state.Paused, nil
case "shutting down":
return state.Stopping, nil
case "migrating":
return state.Running, nil
}
return state.None, nil
}
func (d *Driver) PreCreateCheck() error {
if d.osTemplateLabelName == "Other install media" {
// Downloading boot2docker to cache should be done here to make sure
// that a download failure will not leave a machine half created.
b2dutils := mcnutils.NewB2dUtils(d.StorePath)
if err := b2dutils.UpdateISOCache(d.Boot2DockerURL); err != nil {
return err
}
}
return nil
}
func (d *Driver) Create() error {
var err error
d.setMachineNameIfNotSet()
if d.osTemplateLabelName == "Other install media" {
b2dutils := mcnutils.NewB2dUtils(d.StorePath)
if err := b2dutils.CopyIsoToMachineDir(d.Boot2DockerURL, d.MachineName); err != nil {
return err
}
}
log.Infof("Logging into XenServer %s...", d.Server)
c, err := d.GetXenAPIClient()
if err != nil {
return err
}
// Generate SSH Keys
log.Infof("Creating SSH key...")
if err := ssh.GenerateSSHKey(d.sshKeyPath()); err != nil {
return err
}
// Only upload ISO image if using Other install media, otherwise use existing VM template
var sr *xsclient.SR
var isoVdiUuid, diskVdiUuid string
// Get the SR
if d.SR == "" {
sr, err = c.GetDefaultSR()
if sr.Ref == "OpaqueRef:NULL" {
err := errors.New("No default SR found. Please configure a " +
"default or specify the SR explicitly using " +
"--xcp-sr-label.")
log.Errorf("%v", err)
return err
}
} else {
sr, err = c.GetUniqueSRByNameLabel(d.SR)
}
if err != nil {
return err
}
if d.osTemplateLabelName == "Other install media" {
log.Infof("Creating ISO VDI...")
isoFileInfo, err := os.Stat(d.ISO)
if err != nil {
return err
}
// Create the VDI
isoVdi, err := sr.CreateVdi(isoFilename, isoFileInfo.Size())
if err != nil {
log.Errorf("Unable to create ISO VDI '%s': %v", isoFilename, err)
return err
}
// Import the VDI
if err = d.importVdi(isoVdi, d.ISO, time.Duration(d.UploadTimeout)*time.Second); err != nil {
return err
}
isoVdiUuid, err = isoVdi.GetUuid()
if err != nil {
return err
}
log.Infof("Creating Disk VDI...")
err = d.generateDiskImage()
if err != nil {
return err
}
// Create the VDI
diskVdi, err := sr.CreateVdi("bootdocker disk", int64(d.DiskSize)*1024*1024)
if err != nil {
log.Errorf("Unable to create ISO VDI '%s': %v", "bootdocker disk", err)
return err
}
if err = d.importVdi(diskVdi, d.TAR, time.Duration(d.UploadTimeout)*time.Second); err != nil {
return err
}
diskVdiUuid, err = diskVdi.GetUuid()
if err != nil {
return err
}
}
log.Infof("Creating VM...")
vm0, err := c.GetUniqueVMByNameLabel(d.osTemplateLabelName)
if err != nil {
return err
}
// Clone VM from VM template
vm, err := vm0.Clone(fmt.Sprintf("__gui__%s", d.MachineName))
if err != nil {
return err
}
vmMacSeed, err := pseudoUuid()
if err != nil {
return err
}
hostname, err := os.Hostname()
if err != nil {
log.Errorf("Unable get local hostname")
}
otherConfig := map[string]string{
"base_template_name": d.osTemplateLabelName,
"install-methods": "cdrom,nfs,http,ftp",
"linux_template": "true",
"mac_seed": vmMacSeed,
"docker-machine-creator": hostname,
}
err = vm.SetOtherConfig(otherConfig)
if err != nil {
return err
}
log.Infof("Provision VM...")
err = vm.Provision()
if err != nil {
return err
}
// Set machine name
err = vm.SetNameLabel(d.MachineName)
if err != nil {
return err
}
// Set vCPU number
err = vm.SetVCPUsMax(d.CPU)
if err != nil {
return err
}
err = vm.SetVCPUsAtStartup(d.CPU)
if err != nil {
return err
}
platform_params := map[string]string{
"acpi": "1",
"apic": "true",
"cores-per-socket": "1",
"device_id": "0001",
"nx": "true",
"pae": "true",
"vga": "std",
"videoram": "8",
"viridian": "false",
}
err = vm.SetPlatform(platform_params)
if err != nil {
return err
}
// Set machine memory size
err = vm.SetStaticMemoryRange(uint64(d.Memory)*1024*1024, uint64(d.Memory)*1024*1024)
if err != nil {
return err
}
if d.osTemplateLabelName == "Other install media" {
log.Infof("Add ISO VDI to VM...")
diskVdi, err := c.GetVdiByUuid(isoVdiUuid)
if err != nil {
return err
}
err = vm.ConnectVdi(diskVdi, xsclient.Disk, "0")
if err != nil {
return err
}
log.Infof("Add Disk VDI to VM...")
diskVdi, err = c.GetVdiByUuid(diskVdiUuid)
if err != nil {
return err
}
err = vm.ConnectVdi(diskVdi, xsclient.Disk, "1")
if err != nil {
return err
}
} else {
pubKey, err := ioutil.ReadFile(d.publicSSHKeyPath())
if err != nil {
return err
}
vmUuid, err := vm.GetUuid()
if err != nil {
return err
}
srUuid, err := sr.GetUuid()
if err != nil {
return err
}
if d.CoreosConfigDrive {
err := d.createCoreOSConfigDrive(pubKey, vmUuid, srUuid)
if err != nil {
return err
}
} else {
log.Infof("Creating Config Drive...")
isoVdiUuid, err := d.createGenericConfigDrive(pubKey, vmUuid, sr)
if err != nil {
return err
}
log.Infof("Add ConfigDrive ISO VDI to VM...")
diskVdi, err := c.GetVdiByUuid(isoVdiUuid)
if err != nil {
return err
}
err = vm.ConnectVdi(diskVdi, xsclient.Disk, "")
if err != nil {
return err
}
}
}
log.Infof("Add Network to VM...")
var networks []*xsclient.Network
if d.Network == "" {
networks1, err := c.GetNetworks()
if err != nil {
return err
}
for _, network := range networks1 {
otherConfig, err := network.GetOtherConfig()
if err != nil {
return err
}
isInternal, ok := otherConfig["is_host_internal_management_network"]
if ok && isInternal == "true" {
continue
}
automaitc, ok := otherConfig["automatic"]
if ok && automaitc == "false" {
continue
}
networks = append(networks, network)
}
} else {
network, err := c.GetUniqueNetworkByNameLabel(d.Network)
if err != nil {
return err
}
networks = append(networks, network)
}
if len(networks) == 0 {
return fmt.Errorf("Unable get available networks for %v", d.MachineName)
}
vifDevices, err := vm.GetAllowedVIFDevices()
if err != nil {
return err
}
if len(vifDevices) < len(networks) {
log.Warnf("VM(%s) networks number is limited to %d.", d.MachineName, len(vifDevices))
networks = networks[:len(vifDevices)]
}
for i, network := range networks {
_, err = vm.ConnectNetwork(network, vifDevices[i])
if err != nil {
return err
}
}
log.Infof("Starting VM...")
if d.Host == "" {
if err = vm.Start(false, false); err != nil {
return err
}
} else {
host, err := c.GetUniqueHostByNameLabel(d.Host)
if err != nil {
return err
}
if err = vm.StartOn(host, false, false); err != nil {
return err
}
}
if err := d.wait(time.Duration(d.WaitTimeout) * time.Second); err != nil {
return err
}
log.Infof("VM Created.")
return nil
}
func (d *Driver) wait(timeout time.Duration) (err error) {
log.Infof("Waiting for VM to start...")
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
out := make(chan error, 1)
go func(ctx context.Context, out chan<- error) {
var ip string
for {
ip, _ = d.GetIP()
if ip != "" {
break
}
if t, ok := ctx.Deadline(); ok && time.Now().After(t) {
out <- fmt.Errorf("Wait GetIP timed out")
return
}
time.Sleep(1 * time.Second)
}
port, err := d.GetSSHPort()
if err != nil {
out <- err
return
}
addr := fmt.Sprintf("%s:%d", ip, port)
d.IPAddress = ip
log.Infof("Got VM address(%v), Now waiting for SSH", addr)
out <- drivers.WaitForSSH(d)
}(ctx, out)
select {
case err := <-out:
return err
case <-ctx.Done():
return fmt.Errorf("Wait for VM to start timed out: %v", ctx.Err())
}
return nil
}
func (d *Driver) Start() error {
var err error
c, err := d.GetXenAPIClient()
if err != nil {
return err
}
vm, err := c.GetUniqueVMByNameLabel(d.MachineName)
if err != nil {
return err
}
if err = vm.Start(false, true); err != nil {
return err
}
if err = d.wait(time.Duration(d.WaitTimeout) * time.Second); err != nil {
return err
}
return nil
}
func (d *Driver) Stop() error {
var err error
c, err := d.GetXenAPIClient()
if err != nil {
return err
}
vm, err := c.GetUniqueVMByNameLabel(d.MachineName)
if err != nil {
return err
}
if err = vm.CleanShutdown(); err != nil {
return err
}
return nil
}
func (d *Driver) Remove() error {
var err error
c, err := d.GetXenAPIClient()
if err != nil {
return err
}
vm, err := c.GetUniqueVMByNameLabel(d.MachineName)
if err != nil {
return err
}
vmState, err := d.GetState()
if err != nil {
return err
}
switch vmState {
case state.None:
return fmt.Errorf("Unable get VM(%v) state.", d.MachineName)
case state.Stopping:
return fmt.Errorf("VM(%v) state is stopping, please wait.", d.MachineName)
case state.Stopped:
break
case state.Paused:
if err = vm.HardShutdown(); err != nil {
return err
}
default:
if err = vm.CleanShutdown(); err != nil {
if err = vm.HardShutdown(); err != nil {
return err
}
}
}
disks, err := vm.GetDisks()
if err != nil {
return err
}
for _, disk := range disks {
if err = disk.Destroy(); err != nil {
return err
}
}
if err = vm.Destroy(); err != nil {
return err
}
return nil
}
func (d *Driver) Restart() error {
if err := d.Stop(); err != nil {
return err
}
return d.Start()
}
func (d *Driver) Kill() error {
var err error
c, err := d.GetXenAPIClient()
if err != nil {
return err
}
vm, err := c.GetUniqueVMByNameLabel(d.MachineName)
if err != nil {
return err
}
if err = vm.HardShutdown(); err != nil {
return err
}
return nil
}
func (d *Driver) setMachineNameIfNotSet() {
if d.MachineName == "" {
d.MachineName = fmt.Sprintf("docker-machine-unknown")
}
}
func (d *Driver) sshKeyPath() string {
return d.GetSSHKeyPath()
}
func (d *Driver) publicSSHKeyPath() string {
return d.sshKeyPath() + ".pub"
}
// Make a boot2docker VM disk image.
func (d *Driver) generateDiskImage() error {
magicString := "boot2docker, please format-me"
buf := new(bytes.Buffer)
tw := tar.NewWriter(buf)
log.Infof("Generate Disk Image...")
// magicString first so the automount script knows to format the disk
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
}
if err := tw.Close(); err != nil {
return err
}
if err := ioutil.WriteFile(d.TAR, buf.Bytes(), 0644); err != nil {
return err
}
return nil
}
func (d *Driver) GetXenAPIClient() (*XenAPIClient, error) {
if d.xenAPIClient == nil {
c := NewXenAPIClient(d.Server, d.Username, d.Password)
if err := c.Login(); err != nil {
return nil, err
}
d.xenAPIClient = &c
}
return d.xenAPIClient, nil
}
func (d *Driver) importVdi(vdi *xsclient.VDI, filename string, timeout time.Duration) error {
f, err := os.Open(filename)
if err != nil {
log.Errorf("Unable to open disk image '%s': %v", filename, err)
return err
}
// Get file length
fi, err := f.Stat()
if err != nil {
log.Errorf("Unable to stat disk image '%s': %v", filename, err)
return err
}
task, err := vdi.Client.CreateTask()
if err != nil {
return fmt.Errorf("Unable to create task: %v", err)
}
urlStr := fmt.Sprintf("https://%s/import_raw_vdi?vdi=%s&session_id=%s&task_id=%s",
vdi.Client.Host, vdi.Ref, vdi.Client.Session.(string), task.Ref)
// Define a new http Transport which allows self-signed certs
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
req, err := http.NewRequest("PUT", urlStr, f)
if err != nil {
return err
}
req.ContentLength = fi.Size()
resp, err := tr.RoundTrip(req)
if err != nil {
log.Errorf("Unable to upload VDI: %v", err)
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
msg, _ := ioutil.ReadAll(resp.Body)
err = fmt.Errorf("xcp reply %s: %v", resp.Status, string(msg))
log.Errorf("Unable to upload VDI: %v", err)
return err
}
log.Infof("Waiting Upload VDI task(%v) to complete...", task.Ref)
if err = d.waitTask(task, timeout); err != nil {
return err
}
return nil
}
func (d *Driver) waitTask(task *xsclient.Task, timeout time.Duration) error {
timeout_at := time.Now().Add(timeout)
for {
status, err := task.GetStatus()
if err != nil {
return fmt.Errorf("Failed to get task status: %s", err.Error())
}
if status == xsclient.Success {
log.Infof("Upload VDI task(%s) completed", task.Ref)
break
}
switch status {
case xsclient.Pending:
progress, err := task.GetProgress()
if err != nil {
return fmt.Errorf("Failed to get progress: %s", err.Error())
}
log.Debugf("Upload %.0f%% complete", progress*100)
log.Infof("Uploading...")
case xsclient.Failure:
errorInfo, err := task.GetErrorInfo()
if err != nil {
errorInfo = []string{fmt.Sprintf("furthermore, failed to get error info: %s", err.Error())}
}
return fmt.Errorf("Task failed: %s", errorInfo)
case xsclient.Cancelling, xsclient.Cancelled:
return fmt.Errorf("Task Cancelled")
default:
return fmt.Errorf("Unknown task status %v", status)
}
if time.Now().After(timeout_at) {
return fmt.Errorf("Upload VDI task(%s) timed out", task.Ref)
}
time.Sleep(5 * time.Second)
}
return nil
}
func pseudoUuid() (string, error) {
b := make([]byte, 16)
_, err := io.ReadFull(rand.Reader, b)
if err != nil {
return "", err
}
uuid := fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
return uuid, nil
}
// CoreOS only Config Drive
func (d *Driver) createCoreOSConfigDrive(pubKey []byte, vmuuid string, sruuid string) error {
userdatatext := `#cloud-config
hostname: %XSVMNAMETOHOSTNAME%
coreos:
units:
# XenServer Linux Guest Agent
- name: xe-linux-distribution.service
command: start
content: |
[Unit]
Description=XenServer Linux Guest Agent
After=docker.service
[Service]
ExecStartPre=/media/configdrive/agent/xe-linux-distribution /var/cache/xe-linux-distribution
Environment="XE_UPDATE_GUEST_ATTRS=/media/configdrive/agent/xe-update-guest-attrs"
ExecStart=/media/configdrive/agent/xe-daemon
ssh_authorized_keys:
# The following entry will automatically be replaced with a public key
# generated by XenServer's container management. The key-entry must exist,
# in order to enable container management for this VM.
- ssh-rsa %XSCONTAINERRSAPUB%
- ` + string(pubKey)
hosts, err := d.xenAPIClient.GetHosts()
if err != nil {
log.Errorf("Could not retrieve hosts in the pool: %s", err)
return err
}
ahost := hosts[0]
args := make(map[string]string)
args["vmuuid"] = vmuuid
args["sruuid"] = sruuid
args["configuration"] = userdatatext
ahost.CallPlugin("xscontainer", "create_config_drive", args)
return nil
}
// Generic Config Drive for cloud-init enabled OS templates
func (d *Driver) createGenericConfigDrive(pubKey []byte, vmUuid string, sr *xsclient.SR) (string, error) {
userdatatext := `#cloud-config
ssh_authorized_keys:
- ` + string(pubKey)
metadatatext := `{ "uuid": "` + vmUuid + `"}`
//create ISO file
var diskSize int64
diskSize = 10 * 1024 * 1024 // 10 MB
var configIsoFilename = d.ResolveStorePath("configdrive.iso")
cdDisk, err := diskfs.Create(configIsoFilename, diskSize, diskfs.Raw)
if err != nil {
log.Errorf("Unable to create configdrive.iso: %v", err)
}
// the following line is required for an ISO, which may have logical block sizes
// only of 2048, 4096, 8192
cdDisk.LogicalBlocksize = 2048
fspec := disk.FilesystemSpec{Partition: 0, FSType: filesystem.TypeISO9660, VolumeLabel: "config-2"}
cdFS, err := cdDisk.CreateFilesystem(fspec)
if err != nil {
log.Errorf("Unable to create ConfigDrive filesystem: %v", err)
}
cloudInitPrefix := path.Join("/", "openstack", "latest")
// place down cloud-init info
err = cdFS.Mkdir(cloudInitPrefix)
if err != nil {
log.Errorf("Error creating cloud init directory structure: %v", err)
}
metadataPath := path.Join(cloudInitPrefix, "meta_data.json")
log.Infof("Opening %s", metadataPath)
metadataFile, err := cdFS.OpenFile(metadataPath, os.O_CREATE|os.O_RDWR)
if err != nil {
log.Errorf("Error opening meta data: %v", err)
}
log.Infof("Writing metadata contents")
_, err = metadataFile.Write([]byte(metadatatext))
if err != nil {
log.Errorf("Error writting meta data: %v", err)
}
userdataPath := path.Join(cloudInitPrefix, "user_data")
log.Infof("Opening %s", userdataPath)
userdataFile, err := cdFS.OpenFile(userdataPath, os.O_CREATE|os.O_RDWR)
if err != nil {
log.Errorf("Error opening user data: %v", err)
}
_, err = userdataFile.Write([]byte(userdatatext))
if err != nil {
log.Errorf("Error writting user data: %v", err)
}
//finalize ISO
iso, ok := cdFS.(*iso9660.FileSystem)
if !ok {
log.Errorf("not an iso9660 filesystem")
}
err = iso.Finalize(iso9660.FinalizeOptions{})
if err != nil {
log.Errorf("Error finalizing configdrive.iso: %v", err)
}
// Create the VDI
isoVdi, err := sr.CreateVdi(configIsoFilename, diskSize)
if err != nil {
log.Errorf("Unable to create ConfigDrive ISO VDI '%s': %v", configIsoFilename, err)
return "", err
}
// Import the VDI
log.Infof("Starting import of ConfigDrive VDI '%s'...", configIsoFilename)
if err = d.importVdi(isoVdi, configIsoFilename, time.Duration(d.UploadTimeout)*time.Second); err != nil {
return "", err
}
isoVdiUuid, err := isoVdi.GetUuid()
if err != nil {
return "", err
}
return isoVdiUuid, nil
}