6b0b4baf2f
This aligns the Xenserver plugin to being a bit more inline with what Packer > 1.6.0 is expecting, since packer now simply ignores the iso_checksum_type (it's supposed to error out but that code path isn't working right now because we don't set PluginType in the configs. The unit tests have been altered to reflect this reality. Note that this isn't a comprehensive change; the config still has the inert ISOChecksumType, and there's probably a laundry list of other things that needs to be looked at, For now though, we have working unit tests again. Documentation from SDK has been aligned for iso_checksum
327 lines
8.4 KiB
Go
327 lines
8.4 KiB
Go
package iso
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"path"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/hashicorp/hcl/v2/hcldec"
|
|
"github.com/hashicorp/packer-plugin-sdk/communicator"
|
|
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
commonsteps "github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
|
|
"github.com/hashicorp/packer-plugin-sdk/packer"
|
|
hconfig "github.com/hashicorp/packer-plugin-sdk/template/config"
|
|
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
|
|
xsclient "github.com/terra-farm/go-xen-api-client"
|
|
xscommon "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common"
|
|
)
|
|
|
|
type Builder struct {
|
|
config xscommon.Config
|
|
runner multistep.Runner
|
|
}
|
|
|
|
func (self *Builder) ConfigSpec() hcldec.ObjectSpec { return self.config.FlatMapstructure().HCL2Spec() }
|
|
|
|
func (self *Builder) Prepare(raws ...interface{}) (params []string, warns []string, retErr error) {
|
|
|
|
var errs *packer.MultiError
|
|
|
|
err := hconfig.Decode(&self.config, &hconfig.DecodeOpts{
|
|
Interpolate: true,
|
|
InterpolateFilter: &interpolate.RenderFilter{
|
|
Exclude: []string{
|
|
"boot_command",
|
|
},
|
|
},
|
|
}, raws...)
|
|
|
|
if err != nil {
|
|
errs = packer.MultiErrorAppend(errs, err)
|
|
}
|
|
|
|
errs = packer.MultiErrorAppend(
|
|
errs, self.config.CommonConfig.Prepare(self.config.GetInterpContext(), &self.config.PackerConfig)...)
|
|
errs = packer.MultiErrorAppend(errs, self.config.SSHConfig.Prepare(self.config.GetInterpContext())...)
|
|
|
|
// Set default values
|
|
|
|
if self.config.RawInstallTimeout == "" {
|
|
self.config.RawInstallTimeout = "200m"
|
|
}
|
|
|
|
if self.config.DiskSize == 0 {
|
|
self.config.DiskSize = 40000
|
|
}
|
|
|
|
if self.config.VCPUsMax == 0 {
|
|
self.config.VCPUsMax = 1
|
|
}
|
|
|
|
if self.config.VCPUsAtStartup == 0 {
|
|
self.config.VCPUsAtStartup = 1
|
|
}
|
|
|
|
if self.config.VCPUsAtStartup > self.config.VCPUsMax {
|
|
self.config.VCPUsAtStartup = self.config.VCPUsMax
|
|
}
|
|
|
|
if self.config.VMMemory == 0 {
|
|
self.config.VMMemory = 1024
|
|
}
|
|
|
|
if self.config.CloneTemplate == "" {
|
|
self.config.CloneTemplate = "Other install media"
|
|
}
|
|
|
|
if self.config.Firmware == "" {
|
|
self.config.Firmware = "bios"
|
|
}
|
|
|
|
if len(self.config.PlatformArgs) == 0 {
|
|
pargs := make(map[string]string)
|
|
pargs["viridian"] = "false"
|
|
pargs["nx"] = "true"
|
|
pargs["pae"] = "true"
|
|
pargs["apic"] = "true"
|
|
pargs["timeoffset"] = "0"
|
|
pargs["acpi"] = "1"
|
|
self.config.PlatformArgs = pargs
|
|
}
|
|
|
|
// Template substitution
|
|
|
|
templates := map[string]*string{
|
|
"clone_template": &self.config.CloneTemplate,
|
|
"iso_checksum": &self.config.ISOChecksum,
|
|
"iso_checksum_type": &self.config.ISOChecksumType,
|
|
"iso_url": &self.config.ISOUrl,
|
|
"iso_name": &self.config.ISOName,
|
|
"install_timeout": &self.config.RawInstallTimeout,
|
|
}
|
|
for i := range self.config.ISOUrls {
|
|
templates[fmt.Sprintf("iso_urls[%d]", i)] = &self.config.ISOUrls[i]
|
|
}
|
|
|
|
// Validation
|
|
|
|
self.config.InstallTimeout, err = time.ParseDuration(self.config.RawInstallTimeout)
|
|
if err != nil {
|
|
errs = packer.MultiErrorAppend(
|
|
errs, fmt.Errorf("Failed to parse install_timeout: %s", err))
|
|
}
|
|
|
|
if self.config.ISOName == "" {
|
|
// If ISO name is not specified, assume a URL and checksum has been provided.
|
|
self.config.ISOChecksum = strings.ToLower(self.config.ISOChecksum)
|
|
|
|
if len(self.config.ISOUrls) == 0 {
|
|
if self.config.ISOUrl == "" {
|
|
errs = packer.MultiErrorAppend(
|
|
errs, errors.New("One of iso_url or iso_urls must be specified."))
|
|
} else {
|
|
self.config.ISOUrls = []string{self.config.ISOUrl}
|
|
}
|
|
|
|
} else if self.config.ISOUrl != "" {
|
|
errs = packer.MultiErrorAppend(
|
|
errs, errors.New("Only one of iso_url or iso_urls may be specified."))
|
|
}
|
|
|
|
//The SDK can validate the ISO checksum and other sanity checks on the url.
|
|
iso_config := commonsteps.ISOConfig {
|
|
ISOChecksum: self.config.ISOChecksum,
|
|
ISOUrls: self.config.ISOUrls,
|
|
}
|
|
|
|
_, iso_errs := iso_config.Prepare(nil)
|
|
if iso_errs != nil {
|
|
for _, this_err := range iso_errs {
|
|
errs = packer.MultiErrorAppend(errs, this_err)
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
// An ISO name has been provided. It should be attached from an available SR.
|
|
|
|
}
|
|
|
|
if len(errs.Errors) > 0 {
|
|
retErr = errors.New(errs.Error())
|
|
}
|
|
|
|
return nil, nil, retErr
|
|
|
|
}
|
|
|
|
func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
|
|
c, err := xscommon.NewXenAPIClient(self.config.HostIp, self.config.Username, self.config.Password)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ui.Say("XAPI client session established")
|
|
|
|
c.GetClient().Host.GetAll(c.GetSessionRef())
|
|
|
|
//Share state between the other steps using a statebag
|
|
state := new(multistep.BasicStateBag)
|
|
state.Put("client", c)
|
|
state.Put("config", self.config)
|
|
state.Put("commonconfig", self.config.CommonConfig)
|
|
state.Put("hook", hook)
|
|
state.Put("ui", ui)
|
|
|
|
httpReqChan := make(chan string, 1)
|
|
|
|
//Build the steps
|
|
download_steps := []multistep.Step{
|
|
&commonsteps.StepDownload{
|
|
Checksum: self.config.ISOChecksum,
|
|
Description: "ISO",
|
|
ResultKey: "iso_path",
|
|
Url: self.config.ISOUrls,
|
|
},
|
|
}
|
|
steps := []multistep.Step{
|
|
&xscommon.StepPrepareOutputDir{
|
|
Force: self.config.PackerForce,
|
|
Path: self.config.OutputDir,
|
|
},
|
|
&commonsteps.StepCreateFloppy{
|
|
Files: self.config.FloppyFiles,
|
|
Label: "cidata",
|
|
},
|
|
&xscommon.StepHTTPServer{
|
|
Chan: httpReqChan,
|
|
},
|
|
&xscommon.StepUploadVdi{
|
|
VdiNameFunc: func() string {
|
|
return "Packer-floppy-disk"
|
|
},
|
|
ImagePathFunc: func() string {
|
|
if floppyPath, ok := state.GetOk("floppy_path"); ok {
|
|
return floppyPath.(string)
|
|
}
|
|
return ""
|
|
},
|
|
VdiUuidKey: "floppy_vdi_uuid",
|
|
},
|
|
&xscommon.StepFindOrUploadVdi{
|
|
xscommon.StepUploadVdi{
|
|
VdiNameFunc: func() string {
|
|
if len(self.config.ISOUrls) > 0 {
|
|
return path.Base(self.config.ISOUrls[0])
|
|
}
|
|
return ""
|
|
},
|
|
ImagePathFunc: func() string {
|
|
if isoPath, ok := state.GetOk("iso_path"); ok {
|
|
return isoPath.(string)
|
|
}
|
|
return ""
|
|
},
|
|
VdiUuidKey: "iso_vdi_uuid",
|
|
},
|
|
},
|
|
&xscommon.StepFindVdi{
|
|
VdiName: self.config.ToolsIsoName,
|
|
VdiUuidKey: "tools_vdi_uuid",
|
|
},
|
|
&xscommon.StepFindVdi{
|
|
VdiName: self.config.ISOName,
|
|
VdiUuidKey: "isoname_vdi_uuid",
|
|
},
|
|
new(stepCreateInstance),
|
|
&xscommon.StepAttachVdi{
|
|
VdiUuidKey: "floppy_vdi_uuid",
|
|
VdiType: xsclient.VbdTypeFloppy,
|
|
},
|
|
&xscommon.StepAttachVdi{
|
|
VdiUuidKey: "iso_vdi_uuid",
|
|
VdiType: xsclient.VbdTypeCD,
|
|
},
|
|
&xscommon.StepAttachVdi{
|
|
VdiUuidKey: "isoname_vdi_uuid",
|
|
VdiType: xsclient.VbdTypeCD,
|
|
},
|
|
&xscommon.StepAttachVdi{
|
|
VdiUuidKey: "tools_vdi_uuid",
|
|
VdiType: xsclient.VbdTypeCD,
|
|
},
|
|
new(xscommon.StepStartVmPaused),
|
|
new(xscommon.StepSetVmHostSshAddress),
|
|
// &xscommon.StepForwardPortOverSSH{
|
|
// RemotePort: xscommon.InstanceVNCPort,
|
|
// RemoteDest: xscommon.InstanceVNCIP,
|
|
// HostPortMin: self.config.HostPortMin,
|
|
// HostPortMax: self.config.HostPortMax,
|
|
// ResultKey: "local_vnc_port",
|
|
// },
|
|
new(xscommon.StepBootWait),
|
|
&xscommon.StepTypeBootCommand{
|
|
Ctx: *self.config.GetInterpContext(),
|
|
},
|
|
&xscommon.StepWaitForIP{
|
|
Chan: httpReqChan,
|
|
Timeout: self.config.InstallTimeout, // @todo change this
|
|
},
|
|
&xscommon.StepForwardPortOverSSH{
|
|
RemotePort: xscommon.InstanceSSHPort,
|
|
RemoteDest: xscommon.InstanceSSHIP,
|
|
HostPortMin: self.config.HostPortMin,
|
|
HostPortMax: self.config.HostPortMax,
|
|
ResultKey: "local_ssh_port",
|
|
},
|
|
&communicator.StepConnect{
|
|
Config: &self.config.SSHConfig.Comm,
|
|
Host: xscommon.InstanceSSHIP,
|
|
SSHConfig: self.config.Comm.SSHConfigFunc(),
|
|
SSHPort: xscommon.InstanceSSHPort,
|
|
},
|
|
new(commonsteps.StepProvision),
|
|
new(xscommon.StepShutdown),
|
|
new(xscommon.StepSetVmToTemplate),
|
|
&xscommon.StepDetachVdi{
|
|
VdiUuidKey: "iso_vdi_uuid",
|
|
},
|
|
&xscommon.StepDetachVdi{
|
|
VdiUuidKey: "isoname_vdi_uuid",
|
|
},
|
|
&xscommon.StepDetachVdi{
|
|
VdiUuidKey: "tools_vdi_uuid",
|
|
},
|
|
&xscommon.StepDetachVdi{
|
|
VdiUuidKey: "floppy_vdi_uuid",
|
|
},
|
|
new(xscommon.StepExport),
|
|
}
|
|
|
|
if self.config.ISOName == "" {
|
|
steps = append(download_steps, steps...)
|
|
}
|
|
|
|
self.runner = &multistep.BasicRunner{Steps: steps}
|
|
self.runner.Run(ctx, state)
|
|
|
|
if rawErr, ok := state.GetOk("error"); ok {
|
|
return nil, rawErr.(error)
|
|
}
|
|
|
|
// If we were interrupted or cancelled, then just exit.
|
|
if _, ok := state.GetOk(multistep.StateCancelled); ok {
|
|
return nil, errors.New("Build was cancelled.")
|
|
}
|
|
if _, ok := state.GetOk(multistep.StateHalted); ok {
|
|
return nil, errors.New("Build was halted.")
|
|
}
|
|
|
|
artifact, _ := xscommon.NewArtifact(self.config.OutputDir)
|
|
|
|
return artifact, nil
|
|
}
|