packer-plugin-xenserver/builder/xenserver/xva/builder.go

212 lines
5.1 KiB
Go

package xva
import (
"errors"
"fmt"
"log"
"time"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/helper/communicator"
hconfig "github.com/mitchellh/packer/helper/config"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/template/interpolate"
xsclient "github.com/terra-farm/go-xen-api-client"
xscommon "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common"
)
type config struct {
common.PackerConfig `mapstructure:",squash"`
xscommon.CommonConfig `mapstructure:",squash"`
SourcePath string `mapstructure:"source_path"`
VCPUsMax uint `mapstructure:"vcpus_max"`
VCPUsAtStartup uint `mapstructure:"vcpus_atstartup"`
VMMemory uint `mapstructure:"vm_memory"`
PlatformArgs map[string]string `mapstructure:"platform_args"`
ctx interpolate.Context
}
type Builder struct {
config config
runner multistep.Runner
}
func (self *Builder) Prepare(raws ...interface{}) (params []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 {
packer.MultiErrorAppend(errs, err)
}
errs = packer.MultiErrorAppend(
errs, self.config.CommonConfig.Prepare(&self.config.ctx, &self.config.PackerConfig)...)
// Set default values
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 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
}
// Validation
if self.config.SourcePath == "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("A source_path must be specified"))
}
if len(errs.Errors) > 0 {
retErr = errors.New(errs.Error())
}
return nil, retErr
}
func (self *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
//Setup XAPI client
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("cache", cache)
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
steps := []multistep.Step{
&xscommon.StepPrepareOutputDir{
Force: self.config.PackerForce,
Path: self.config.OutputDir,
},
&common.StepCreateFloppy{
Files: self.config.FloppyFiles,
},
new(xscommon.StepHTTPServer),
&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.StepFindVdi{
VdiName: self.config.ToolsIsoName,
VdiUuidKey: "tools_vdi_uuid",
},
new(stepImportInstance),
&xscommon.StepAttachVdi{
VdiUuidKey: "floppy_vdi_uuid",
VdiType: xsclient.VbdTypeFloppy,
},
&xscommon.StepAttachVdi{
VdiUuidKey: "tools_vdi_uuid",
VdiType: xsclient.VbdTypeCD,
},
new(xscommon.StepStartVmPaused),
new(xscommon.StepSetVmHostSshAddress),
new(xscommon.StepBootWait),
&xscommon.StepTypeBootCommand{
Ctx: self.config.ctx,
},
&xscommon.StepWaitForIP{
Chan: httpReqChan,
Timeout: 300 * time.Minute, /*self.config.InstallTimeout*/ // @todo change this
},
&communicator.StepConnect{
Config: &self.config.SSHConfig.Comm,
Host: xscommon.CommHost,
SSHConfig: xscommon.SSHConfigFunc(self.config.CommonConfig.SSHConfig),
SSHPort: xscommon.SSHPort,
},
new(common.StepProvision),
new(xscommon.StepShutdown),
&xscommon.StepDetachVdi{
VdiUuidKey: "floppy_vdi_uuid",
},
&xscommon.StepDetachVdi{
VdiUuidKey: "tools_vdi_uuid",
},
new(xscommon.StepExport),
}
self.runner = &multistep.BasicRunner{Steps: steps}
self.runner.Run(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
}
func (self *Builder) Cancel() {
if self.runner != nil {
log.Println("Cancelling the step runner...")
self.runner.Cancel()
}
fmt.Println("Cancelling the builder")
}