diff --git a/xenserver/xenserver.go b/xenserver/xenserver.go index efccfbb..ff4733a 100644 --- a/xenserver/xenserver.go +++ b/xenserver/xenserver.go @@ -55,6 +55,7 @@ type Driver struct { CaCertPath string PrivateKeyPath string osTemplateLabelName string + CoreosConfigDrive bool xenAPIClient *XenAPIClient } @@ -140,6 +141,11 @@ func (d *Driver) GetCreateFlags() []mcnflag.Flag { Usage: "XenServer Username used to SSH into guest OS", Value: B2D_USER, }, + mcnflag.BoolFlag{ + EnvVar: "XENSERVER_COREOS_CONFIGDRIVE", + Name: "xenserver-coreos-configdrive", + Usage: "XenServer Enable CoreOS specific ConfigDrive (requires xscontainer supplemental pack)", + }, } } @@ -201,6 +207,7 @@ func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error { d.ISO = d.ResolveStorePath(isoFilename) d.TAR = d.ResolveStorePath(tarFilename) d.osTemplateLabelName = flags.String("xenserver-os-template") + d.CoreosConfigDrive = flags.Bool("xenserver-coreos-configdrive") return nil } @@ -337,7 +344,6 @@ func (d *Driver) Create() error { // Only upload ISO image if using Other install media, otherwise use existing VM template var sr *xsclient.SR - var isoVdi,diskVdi *xsclient.VDI var isoVdiUuid,diskVdiUuid string // Get the SR @@ -367,7 +373,7 @@ func (d *Driver) Create() error { } // Create the VDI - isoVdi, err = sr.CreateVdi(isoFilename, isoFileInfo.Size()) + isoVdi, err := sr.CreateVdi(isoFilename, isoFileInfo.Size()) if err != nil { log.Errorf("Unable to create ISO VDI '%s': %v", isoFilename, err) return err @@ -390,7 +396,7 @@ func (d *Driver) Create() error { } // Create the VDI - diskVdi, err = sr.CreateVdi("bootdocker disk", int64(d.DiskSize)*1024*1024) + 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 @@ -404,75 +410,6 @@ func (d *Driver) Create() error { return err } - } else { - log.Infof("Creating Config Drive...") - - pubKey, err := ioutil.ReadFile(d.publicSSHKeyPath()) - if err != nil { - return err - } - userdatatext := `#cloud-config -ssh_authorized_keys: - - ` + string(pubKey) - - fakeUuid, err := pseudoUuid() - if err != nil { - return err - } - - metadatatext := `{ "uuid": "` + fakeUuid + `"}` - - tempDir := d.ResolveStorePath("configdrive") - - //Create the folder structure inside the temp folder - path := filepath.Join(tempDir, "openstack", "latest") - os.MkdirAll(path, os.ModePerm) - f, err := os.OpenFile(filepath.Join(path, "user_data"), os.O_WRONLY|os.O_CREATE, 0666) - if err != nil { - log.Errorf("Unable to open user_data config drive file '%s': %v", path, err) - return err - } - io.WriteString(f, userdatatext) - f.Close() - - fmetadata, err := os.OpenFile(filepath.Join(path, "meta_data.json"), os.O_WRONLY|os.O_CREATE, 0666) - if err != nil { - log.Errorf("Unable to open meta_data.json config drive file '%s': %v", path, err) - return err - } - io.WriteString(fmetadata, metadatatext) - fmetadata.Close() - - configIsoFilename := d.ResolveStorePath("configdrive.iso") - cmd := exec.Command("/usr/bin/mkisofs", "-R", "-V", "config-2", "-o", configIsoFilename, tempDir) - err = cmd.Run() - if err != nil { - log.Errorf("Unable to create ConfigDrive ISO '%s': %v", configIsoFilename, err) - return err - } - - configIsoFileInfo, err := os.Stat(configIsoFilename) - if err != nil { - return err - } - - // Create the VDI - isoVdi, err = sr.CreateVdi(configIsoFilename, configIsoFileInfo.Size()) - 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 - } } log.Infof("Creating VM...") @@ -557,7 +494,7 @@ ssh_authorized_keys: if d.osTemplateLabelName == "Other install media" { log.Infof("Add ISO VDI to VM...") - diskVdi, err = c.GetVdiByUuid(isoVdiUuid) + diskVdi, err := c.GetVdiByUuid(isoVdiUuid) if err != nil { return err } @@ -578,16 +515,41 @@ ssh_authorized_keys: return err } } else { - log.Infof("Add ConfigDrive ISO VDI to VM...") - diskVdi, err = c.GetVdiByUuid(isoVdiUuid) + pubKey, err := ioutil.ReadFile(d.publicSSHKeyPath()) if err != nil { return err } - - err = vm.ConnectVdi(diskVdi, xsclient.Disk, "") + 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...") @@ -1009,3 +971,101 @@ func pseudoUuid() (string, error) { 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 } + +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 +} + +func (d *Driver) createGenericConfigDrive(pubKey []byte, vmUuid string, sr *xsclient.SR) (string, error) { + userdatatext := `#cloud-config +ssh_authorized_keys: + - ` + string(pubKey) + + metadatatext := `{ "uuid": "` + vmUuid + `"}` + + tempDir := d.ResolveStorePath("configdrive") + + //Create the folder structure inside the temp folder + path := filepath.Join(tempDir, "openstack", "latest") + os.MkdirAll(path, os.ModePerm) + f, err := os.OpenFile(filepath.Join(path, "user_data"), os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + log.Errorf("Unable to open user_data config drive file '%s': %v", path, err) + return "", err + } + io.WriteString(f, userdatatext) + f.Close() + + fmetadata, err := os.OpenFile(filepath.Join(path, "meta_data.json"), os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + log.Errorf("Unable to open meta_data.json config drive file '%s': %v", path, err) + return "", err + } + io.WriteString(fmetadata, metadatatext) + fmetadata.Close() + + configIsoFilename := d.ResolveStorePath("configdrive.iso") + cmd := exec.Command("/usr/bin/mkisofs", "-R", "-V", "config-2", "-o", configIsoFilename, tempDir) + err = cmd.Run() + if err != nil { + log.Errorf("Unable to create ConfigDrive ISO '%s': %v", configIsoFilename, err) + return "", err + } + configIsoFileInfo, err := os.Stat(configIsoFilename) + if err != nil { + return "", err + } + + // Create the VDI + isoVdi, err := sr.CreateVdi(configIsoFilename, configIsoFileInfo.Size()) + 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 +}