Initial work on splitting ISO and XVA builders
Compiles -- but xva create_instance is incomplete Several changes were rolled into this commit, including: - "export_format" is now "format", and "keep_instance" is "keep_vm" - gox is used for building (with the intention of allowing cross-compilation in the future)
This commit is contained in:
parent
4c4ba3026b
commit
152bdee7f2
9
build.sh
9
build.sh
@ -1 +1,8 @@
|
|||||||
go build -o $PACKERPATH/packer-builder-xenserver main.go
|
XC_OS=${XC_OS:-$(go env GOOS)}
|
||||||
|
XC_ARCH=${XC_ARCH:-$(go env GOARCH)}
|
||||||
|
|
||||||
|
gox \
|
||||||
|
-os="${XC_OS}" \
|
||||||
|
-arch="${XC_ARCH}" \
|
||||||
|
-output "build/{{.OS}}_{{.Arch}}/packer-{{.Dir}}" \
|
||||||
|
./...
|
||||||
|
@ -1,528 +0,0 @@
|
|||||||
package xenserver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"github.com/mitchellh/multistep"
|
|
||||||
"github.com/mitchellh/packer/common"
|
|
||||||
commonssh "github.com/mitchellh/packer/common/ssh"
|
|
||||||
"github.com/mitchellh/packer/packer"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Set the unique ID for this builder
|
|
||||||
const BuilderId = "packer.xenserver"
|
|
||||||
|
|
||||||
type config struct {
|
|
||||||
common.PackerConfig `mapstructure:",squash"`
|
|
||||||
|
|
||||||
Username string `mapstructure:"username"`
|
|
||||||
Password string `mapstructure:"password"`
|
|
||||||
HostIp string `mapstructure:"host_ip"`
|
|
||||||
|
|
||||||
VMName string `mapstructure:"vm_name"`
|
|
||||||
VMMemory uint `mapstructure:"vm_memory"`
|
|
||||||
DiskSize uint `mapstructure:"disk_size"`
|
|
||||||
CloneTemplate string `mapstructure:"clone_template"`
|
|
||||||
SrName string `mapstructure:"sr_name"`
|
|
||||||
FloppyFiles []string `mapstructure:"floppy_files"`
|
|
||||||
NetworkName string `mapstructure:"network_name"`
|
|
||||||
|
|
||||||
HostPortMin uint `mapstructure:"host_port_min"`
|
|
||||||
HostPortMax uint `mapstructure:"host_port_max"`
|
|
||||||
|
|
||||||
BootCommand []string `mapstructure:"boot_command"`
|
|
||||||
ShutdownCommand string `mapstructure:"shutdown_command"`
|
|
||||||
|
|
||||||
RawBootWait string `mapstructure:"boot_wait"`
|
|
||||||
BootWait time.Duration ``
|
|
||||||
|
|
||||||
ISOChecksum string `mapstructure:"iso_checksum"`
|
|
||||||
ISOChecksumType string `mapstructure:"iso_checksum_type"`
|
|
||||||
ISOUrls []string `mapstructure:"iso_urls"`
|
|
||||||
ISOUrl string `mapstructure:"iso_url"`
|
|
||||||
|
|
||||||
ToolsIsoName string `mapstructure:"tools_iso_name"`
|
|
||||||
|
|
||||||
HTTPDir string `mapstructure:"http_directory"`
|
|
||||||
HTTPPortMin uint `mapstructure:"http_port_min"`
|
|
||||||
HTTPPortMax uint `mapstructure:"http_port_max"`
|
|
||||||
|
|
||||||
LocalIp string `mapstructure:"local_ip"`
|
|
||||||
PlatformArgs map[string]string `mapstructure:"platform_args"`
|
|
||||||
|
|
||||||
RawInstallTimeout string `mapstructure:"install_timeout"`
|
|
||||||
InstallTimeout time.Duration ``
|
|
||||||
|
|
||||||
RawSSHWaitTimeout string `mapstructure:"ssh_wait_timeout"`
|
|
||||||
SSHWaitTimeout time.Duration ``
|
|
||||||
|
|
||||||
SSHPassword string `mapstructure:"ssh_password"`
|
|
||||||
SSHUser string `mapstructure:"ssh_username"`
|
|
||||||
SSHKeyPath string `mapstructure:"ssh_key_path"`
|
|
||||||
|
|
||||||
OutputDir string `mapstructure:"output_directory"`
|
|
||||||
ExportFormat string `mapstructure:"export_format"`
|
|
||||||
|
|
||||||
KeepInstance string `mapstructure:"keep_instance"`
|
|
||||||
|
|
||||||
tpl *packer.ConfigTemplate
|
|
||||||
}
|
|
||||||
|
|
||||||
type Builder struct {
|
|
||||||
config config
|
|
||||||
runner multistep.Runner
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Builder) Prepare(raws ...interface{}) (params []string, retErr error) {
|
|
||||||
|
|
||||||
md, err := common.DecodeConfig(&self.config, raws...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
errs := common.CheckUnusedConfig(md)
|
|
||||||
if errs == nil {
|
|
||||||
errs = &packer.MultiError{}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.config.tpl, err = packer.NewConfigTemplate()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
self.config.tpl.UserVars = self.config.PackerUserVars
|
|
||||||
|
|
||||||
// Set default values
|
|
||||||
|
|
||||||
if self.config.HostPortMin == 0 {
|
|
||||||
self.config.HostPortMin = 5900
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.config.HostPortMax == 0 {
|
|
||||||
self.config.HostPortMax = 6000
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.config.RawBootWait == "" {
|
|
||||||
self.config.RawBootWait = "5s"
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.config.RawInstallTimeout == "" {
|
|
||||||
self.config.RawInstallTimeout = "200m"
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.config.ToolsIsoName == "" {
|
|
||||||
self.config.ToolsIsoName = "xs-tools.iso"
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.config.HTTPPortMin == 0 {
|
|
||||||
self.config.HTTPPortMin = 8000
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.config.HTTPPortMax == 0 {
|
|
||||||
self.config.HTTPPortMax = 9000
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.config.RawSSHWaitTimeout == "" {
|
|
||||||
self.config.RawSSHWaitTimeout = "200m"
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.config.DiskSize == 0 {
|
|
||||||
self.config.DiskSize = 40000
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.config.VMMemory == 0 {
|
|
||||||
self.config.VMMemory = 1024
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.config.CloneTemplate == "" {
|
|
||||||
self.config.CloneTemplate = "Other install media"
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.config.FloppyFiles == nil {
|
|
||||||
self.config.FloppyFiles = make([]string, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.config.OutputDir == "" {
|
|
||||||
self.config.OutputDir = fmt.Sprintf("output-%s", self.config.PackerBuildName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.config.ExportFormat == "" {
|
|
||||||
self.config.ExportFormat = "xva"
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.config.KeepInstance == "" {
|
|
||||||
self.config.KeepInstance = "never"
|
|
||||||
}
|
|
||||||
|
|
||||||
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{
|
|
||||||
"username": &self.config.Username,
|
|
||||||
"password": &self.config.Password,
|
|
||||||
"host_ip": &self.config.HostIp,
|
|
||||||
"vm_name": &self.config.VMName,
|
|
||||||
"clone_template": &self.config.CloneTemplate,
|
|
||||||
"sr_name": &self.config.SrName,
|
|
||||||
"network_name": &self.config.NetworkName,
|
|
||||||
"shutdown_command": &self.config.ShutdownCommand,
|
|
||||||
"boot_wait": &self.config.RawBootWait,
|
|
||||||
"iso_checksum": &self.config.ISOChecksum,
|
|
||||||
"iso_checksum_type": &self.config.ISOChecksumType,
|
|
||||||
"iso_url": &self.config.ISOUrl,
|
|
||||||
"tools_iso_name": &self.config.ToolsIsoName,
|
|
||||||
"http_directory": &self.config.HTTPDir,
|
|
||||||
"local_ip": &self.config.LocalIp,
|
|
||||||
"install_timeout": &self.config.RawInstallTimeout,
|
|
||||||
"ssh_wait_timeout": &self.config.RawSSHWaitTimeout,
|
|
||||||
"ssh_username": &self.config.SSHUser,
|
|
||||||
"ssh_password": &self.config.SSHPassword,
|
|
||||||
"ssh_key_path": &self.config.SSHKeyPath,
|
|
||||||
"output_directory": &self.config.OutputDir,
|
|
||||||
"export_format": &self.config.ExportFormat,
|
|
||||||
"keep_instance": &self.config.KeepInstance,
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range self.config.FloppyFiles {
|
|
||||||
templates[fmt.Sprintf("floppy_files[%d]", i)] = &self.config.FloppyFiles[i]
|
|
||||||
}
|
|
||||||
for i := range self.config.ISOUrls {
|
|
||||||
templates[fmt.Sprintf("iso_urls[%d]", i)] = &self.config.ISOUrls[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
for n, ptr := range templates {
|
|
||||||
var err error
|
|
||||||
*ptr, err = self.config.tpl.Process(*ptr, nil)
|
|
||||||
if err != nil {
|
|
||||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Error processing %s: %s", n, err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validation
|
|
||||||
|
|
||||||
self.config.BootWait, err = time.ParseDuration(self.config.RawBootWait)
|
|
||||||
if err != nil {
|
|
||||||
errs = packer.MultiErrorAppend(
|
|
||||||
errs, fmt.Errorf("Failed to parse boot_wait: %s", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
self.config.SSHWaitTimeout, err = time.ParseDuration(self.config.RawSSHWaitTimeout)
|
|
||||||
if err != nil {
|
|
||||||
errs = packer.MultiErrorAppend(
|
|
||||||
errs, fmt.Errorf("Failed to parse ssh_wait_timeout: %s", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, command := range self.config.BootCommand {
|
|
||||||
if err := self.config.tpl.Validate(command); err != nil {
|
|
||||||
errs = packer.MultiErrorAppend(errs,
|
|
||||||
fmt.Errorf("Error processing boot_command[%d]: %s", i, err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.config.SSHUser == "" {
|
|
||||||
errs = packer.MultiErrorAppend(
|
|
||||||
errs, errors.New("An ssh_username must be specified."))
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.config.SSHKeyPath != "" {
|
|
||||||
if _, err := os.Stat(self.config.SSHKeyPath); err != nil {
|
|
||||||
errs = packer.MultiErrorAppend(
|
|
||||||
errs, fmt.Errorf("ssh_key_path is invalid: %s", err))
|
|
||||||
} else if _, err := commonssh.FileSigner(self.config.SSHKeyPath); err != nil {
|
|
||||||
errs = packer.MultiErrorAppend(
|
|
||||||
errs, fmt.Errorf("ssh_key_path is invalid: %s", err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.config.Username == "" {
|
|
||||||
errs = packer.MultiErrorAppend(
|
|
||||||
errs, errors.New("A username for the xenserver host must be specified."))
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.config.Password == "" {
|
|
||||||
errs = packer.MultiErrorAppend(
|
|
||||||
errs, errors.New("A password for the xenserver host must be specified."))
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.config.HostIp == "" {
|
|
||||||
errs = packer.MultiErrorAppend(
|
|
||||||
errs, errors.New("An ip for the xenserver host must be specified."))
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.config.VMName == "" {
|
|
||||||
errs = packer.MultiErrorAppend(
|
|
||||||
errs, errors.New("vm_name must be specified."))
|
|
||||||
}
|
|
||||||
|
|
||||||
switch self.config.ExportFormat {
|
|
||||||
case "xva", "vdi_raw":
|
|
||||||
default:
|
|
||||||
errs = packer.MultiErrorAppend(
|
|
||||||
errs, errors.New("export_format must be one of 'xva', 'vdi_raw'"))
|
|
||||||
}
|
|
||||||
|
|
||||||
switch self.config.KeepInstance {
|
|
||||||
case "always", "never", "on_success":
|
|
||||||
default:
|
|
||||||
errs = packer.MultiErrorAppend(
|
|
||||||
errs, errors.New("keep_instance must be one of 'always', 'never', 'on_success'"))
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
if self.config.LocalIp == "" {
|
|
||||||
errs = packer.MultiErrorAppend(
|
|
||||||
errs, errors.New("A local IP visible to XenServer's mangement interface is required to serve files."))
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
if self.config.HTTPPortMin > self.config.HTTPPortMax {
|
|
||||||
errs = packer.MultiErrorAppend(
|
|
||||||
errs, errors.New("the HTTP min port must be less than the max"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.config.HostPortMin > self.config.HostPortMax {
|
|
||||||
errs = packer.MultiErrorAppend(
|
|
||||||
errs, errors.New("the host min port must be less than the max"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.config.ISOChecksumType == "" {
|
|
||||||
errs = packer.MultiErrorAppend(
|
|
||||||
errs, errors.New("The iso_checksum_type must be specified."))
|
|
||||||
} else {
|
|
||||||
self.config.ISOChecksumType = strings.ToLower(self.config.ISOChecksumType)
|
|
||||||
if self.config.ISOChecksumType != "none" {
|
|
||||||
if self.config.ISOChecksum == "" {
|
|
||||||
errs = packer.MultiErrorAppend(
|
|
||||||
errs, errors.New("Due to the file size being large, an iso_checksum is required."))
|
|
||||||
} else {
|
|
||||||
self.config.ISOChecksum = strings.ToLower(self.config.ISOChecksum)
|
|
||||||
}
|
|
||||||
|
|
||||||
if hash := common.HashForType(self.config.ISOChecksumType); hash == nil {
|
|
||||||
errs = packer.MultiErrorAppend(
|
|
||||||
errs, fmt.Errorf("Unsupported checksum type: %s", self.config.ISOChecksumType))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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."))
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, url := range self.config.ISOUrls {
|
|
||||||
self.config.ISOUrls[i], err = common.DownloadableURL(url)
|
|
||||||
if err != nil {
|
|
||||||
errs = packer.MultiErrorAppend(
|
|
||||||
errs, fmt.Errorf("Failed to parse iso_urls[%d]: %s", i, err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
client := NewXenAPIClient(self.config.HostIp, self.config.Username, self.config.Password)
|
|
||||||
|
|
||||||
err := client.Login()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err.(error)
|
|
||||||
}
|
|
||||||
ui.Say("XAPI client session established")
|
|
||||||
|
|
||||||
client.GetHosts()
|
|
||||||
|
|
||||||
//Share state between the other steps using a statebag
|
|
||||||
state := new(multistep.BasicStateBag)
|
|
||||||
state.Put("cache", cache)
|
|
||||||
state.Put("client", client)
|
|
||||||
state.Put("config", self.config)
|
|
||||||
state.Put("hook", hook)
|
|
||||||
state.Put("ui", ui)
|
|
||||||
|
|
||||||
//Build the steps
|
|
||||||
steps := []multistep.Step{
|
|
||||||
&common.StepDownload{
|
|
||||||
Checksum: self.config.ISOChecksum,
|
|
||||||
ChecksumType: self.config.ISOChecksumType,
|
|
||||||
Description: "ISO",
|
|
||||||
ResultKey: "iso_path",
|
|
||||||
Url: self.config.ISOUrls,
|
|
||||||
},
|
|
||||||
new(stepPrepareOutputDir),
|
|
||||||
&common.StepCreateFloppy{
|
|
||||||
Files: self.config.FloppyFiles,
|
|
||||||
},
|
|
||||||
new(stepHTTPServer),
|
|
||||||
&stepUploadVdi{
|
|
||||||
VdiName: "Packer-floppy-disk",
|
|
||||||
ImagePathFunc: func() string {
|
|
||||||
if floppyPath, ok := state.GetOk("floppy_path"); ok {
|
|
||||||
return floppyPath.(string)
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
},
|
|
||||||
VdiUuidKey: "floppy_vdi_uuid",
|
|
||||||
},
|
|
||||||
&stepUploadVdi{
|
|
||||||
VdiName: path.Base(self.config.ISOUrls[0]),
|
|
||||||
ImagePathFunc: func() string {
|
|
||||||
return state.Get("iso_path").(string)
|
|
||||||
},
|
|
||||||
VdiUuidKey: "iso_vdi_uuid",
|
|
||||||
},
|
|
||||||
&stepFindVdi{
|
|
||||||
VdiName: self.config.ToolsIsoName,
|
|
||||||
VdiUuidKey: "tools_vdi_uuid",
|
|
||||||
},
|
|
||||||
new(stepCreateInstance),
|
|
||||||
&stepAttachVdi{
|
|
||||||
VdiUuidKey: "floppy_vdi_uuid",
|
|
||||||
VdiType: Floppy,
|
|
||||||
},
|
|
||||||
&stepAttachVdi{
|
|
||||||
VdiUuidKey: "iso_vdi_uuid",
|
|
||||||
VdiType: CD,
|
|
||||||
},
|
|
||||||
&stepAttachVdi{
|
|
||||||
VdiUuidKey: "tools_vdi_uuid",
|
|
||||||
VdiType: CD,
|
|
||||||
},
|
|
||||||
new(stepStartVmPaused),
|
|
||||||
new(stepGetVNCPort),
|
|
||||||
&stepForwardPortOverSSH{
|
|
||||||
RemotePort: instanceVNCPort,
|
|
||||||
RemoteDest: instanceVNCIP,
|
|
||||||
HostPortMin: self.config.HostPortMin,
|
|
||||||
HostPortMax: self.config.HostPortMax,
|
|
||||||
ResultKey: "local_vnc_port",
|
|
||||||
},
|
|
||||||
new(stepBootWait),
|
|
||||||
new(stepTypeBootCommand),
|
|
||||||
new(stepWait),
|
|
||||||
&stepDetachVdi{
|
|
||||||
VdiUuidKey: "floppy_vdi_uuid",
|
|
||||||
},
|
|
||||||
&stepDetachVdi{
|
|
||||||
VdiUuidKey: "iso_vdi_uuid",
|
|
||||||
},
|
|
||||||
new(stepRemoveDevices),
|
|
||||||
new(stepStartOnHIMN),
|
|
||||||
&stepForwardPortOverSSH{
|
|
||||||
RemotePort: himnSSHPort,
|
|
||||||
RemoteDest: himnSSHIP,
|
|
||||||
HostPortMin: self.config.HostPortMin,
|
|
||||||
HostPortMax: self.config.HostPortMax,
|
|
||||||
ResultKey: "local_ssh_port",
|
|
||||||
},
|
|
||||||
&common.StepConnectSSH{
|
|
||||||
SSHAddress: sshLocalAddress,
|
|
||||||
SSHConfig: sshConfig,
|
|
||||||
SSHWaitTimeout: self.config.SSHWaitTimeout,
|
|
||||||
},
|
|
||||||
new(common.StepProvision),
|
|
||||||
new(stepShutdownAndExport),
|
|
||||||
}
|
|
||||||
|
|
||||||
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, _ := 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")
|
|
||||||
}
|
|
||||||
|
|
||||||
// all steps should check config.ShouldKeepInstance first before cleaning up
|
|
||||||
func (cfg config) ShouldKeepInstance(state multistep.StateBag) bool {
|
|
||||||
switch cfg.KeepInstance {
|
|
||||||
case "always":
|
|
||||||
return true
|
|
||||||
case "never":
|
|
||||||
return false
|
|
||||||
case "on_success":
|
|
||||||
// only keep instance if build was successful
|
|
||||||
_, cancelled := state.GetOk(multistep.StateCancelled)
|
|
||||||
_, halted := state.GetOk(multistep.StateHalted)
|
|
||||||
return !(cancelled || halted)
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("Unknown keep_instance value '%s'", cfg.KeepInstance))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (config config) GetSR(client XenAPIClient) (*SR, error) {
|
|
||||||
if config.SrName == "" {
|
|
||||||
// Find the default SR
|
|
||||||
return client.GetDefaultSR()
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Use the provided name label to find the SR to use
|
|
||||||
srs, err := client.GetSRByNameLabel(config.SrName)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case len(srs) == 0:
|
|
||||||
return nil, fmt.Errorf("Couldn't find a SR with the specified name-label '%s'", config.SrName)
|
|
||||||
case len(srs) > 1:
|
|
||||||
return nil, fmt.Errorf("Found more than one SR with the name '%s'. The name must be unique", config.SrName)
|
|
||||||
}
|
|
||||||
|
|
||||||
return srs[0], nil
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
package xenserver
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -7,6 +7,9 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// This is the common builder ID to all of these artifacts.
|
||||||
|
const BuilderId = "packer.xenserver"
|
||||||
|
|
||||||
type LocalArtifact struct {
|
type LocalArtifact struct {
|
||||||
dir string
|
dir string
|
||||||
f []string
|
f []string
|
@ -1,4 +1,4 @@
|
|||||||
package xenserver
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
278
builder/xenserver/common/common_config.go
Normal file
278
builder/xenserver/common/common_config.go
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/mitchellh/multistep"
|
||||||
|
"github.com/mitchellh/packer/common"
|
||||||
|
commonssh "github.com/mitchellh/packer/common/ssh"
|
||||||
|
"github.com/mitchellh/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CommonConfig struct {
|
||||||
|
Username string `mapstructure:"username"`
|
||||||
|
Password string `mapstructure:"password"`
|
||||||
|
HostIp string `mapstructure:"host_ip"`
|
||||||
|
|
||||||
|
VMName string `mapstructure:"vm_name"`
|
||||||
|
SrName string `mapstructure:"sr_name"`
|
||||||
|
FloppyFiles []string `mapstructure:"floppy_files"`
|
||||||
|
NetworkName string `mapstructure:"network_name"`
|
||||||
|
|
||||||
|
HostPortMin uint `mapstructure:"host_port_min"`
|
||||||
|
HostPortMax uint `mapstructure:"host_port_max"`
|
||||||
|
|
||||||
|
BootCommand []string `mapstructure:"boot_command"`
|
||||||
|
ShutdownCommand string `mapstructure:"shutdown_command"`
|
||||||
|
|
||||||
|
RawBootWait string `mapstructure:"boot_wait"`
|
||||||
|
BootWait time.Duration
|
||||||
|
|
||||||
|
ToolsIsoName string `mapstructure:"tools_iso_name"`
|
||||||
|
|
||||||
|
HTTPDir string `mapstructure:"http_directory"`
|
||||||
|
HTTPPortMin uint `mapstructure:"http_port_min"`
|
||||||
|
HTTPPortMax uint `mapstructure:"http_port_max"`
|
||||||
|
|
||||||
|
LocalIp string `mapstructure:"local_ip"`
|
||||||
|
|
||||||
|
// SSHHostPortMin uint `mapstructure:"ssh_host_port_min"`
|
||||||
|
// SSHHostPortMax uint `mapstructure:"ssh_host_port_max"`
|
||||||
|
SSHKeyPath string `mapstructure:"ssh_key_path"`
|
||||||
|
SSHPassword string `mapstructure:"ssh_password"`
|
||||||
|
// SSHPort uint `mapstructure:"ssh_port"`
|
||||||
|
SSHUser string `mapstructure:"ssh_username"`
|
||||||
|
RawSSHWaitTimeout string `mapstructure:"ssh_wait_timeout"`
|
||||||
|
SSHWaitTimeout time.Duration
|
||||||
|
|
||||||
|
OutputDir string `mapstructure:"output_directory"`
|
||||||
|
Format string `mapstructure:"format"`
|
||||||
|
KeepVM string `mapstructure:"keep_vm"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommonConfig) Prepare(t *packer.ConfigTemplate, pc *common.PackerConfig) []error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Set default values
|
||||||
|
|
||||||
|
if c.HostPortMin == 0 {
|
||||||
|
c.HostPortMin = 5900
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.HostPortMax == 0 {
|
||||||
|
c.HostPortMax = 6000
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.RawBootWait == "" {
|
||||||
|
c.RawBootWait = "5s"
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.ToolsIsoName == "" {
|
||||||
|
c.ToolsIsoName = "xs-tools.iso"
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.HTTPPortMin == 0 {
|
||||||
|
c.HTTPPortMin = 8000
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.HTTPPortMax == 0 {
|
||||||
|
c.HTTPPortMax = 9000
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.RawSSHWaitTimeout == "" {
|
||||||
|
c.RawSSHWaitTimeout = "200m"
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.FloppyFiles == nil {
|
||||||
|
c.FloppyFiles = make([]string, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
if c.SSHHostPortMin == 0 {
|
||||||
|
c.SSHHostPortMin = 2222
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.SSHHostPortMax == 0 {
|
||||||
|
c.SSHHostPortMax = 4444
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.SSHPort == 0 {
|
||||||
|
c.SSHPort = 22
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
if c.RawSSHWaitTimeout == "" {
|
||||||
|
c.RawSSHWaitTimeout = "20m"
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.OutputDir == "" {
|
||||||
|
c.OutputDir = fmt.Sprintf("output-%s", pc.PackerBuildName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Format == "" {
|
||||||
|
c.Format = "xva"
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.KeepVM == "" {
|
||||||
|
c.KeepVM = "never"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Template substitution
|
||||||
|
|
||||||
|
templates := map[string]*string{
|
||||||
|
"username": &c.Username,
|
||||||
|
"password": &c.Password,
|
||||||
|
"host_ip": &c.HostIp,
|
||||||
|
"vm_name": &c.VMName,
|
||||||
|
"sr_name": &c.SrName,
|
||||||
|
"shutdown_command": &c.ShutdownCommand,
|
||||||
|
"boot_wait": &c.RawBootWait,
|
||||||
|
"tools_iso_name": &c.ToolsIsoName,
|
||||||
|
"http_directory": &c.HTTPDir,
|
||||||
|
"local_ip": &c.LocalIp,
|
||||||
|
"ssh_key_path": &c.SSHKeyPath,
|
||||||
|
"ssh_password": &c.SSHPassword,
|
||||||
|
"ssh_username": &c.SSHUser,
|
||||||
|
"ssh_wait_timeout": &c.RawSSHWaitTimeout,
|
||||||
|
"output_directory": &c.OutputDir,
|
||||||
|
"format": &c.Format,
|
||||||
|
"keep_vm": &c.KeepVM,
|
||||||
|
}
|
||||||
|
for i := range c.FloppyFiles {
|
||||||
|
templates[fmt.Sprintf("floppy_files[%d]", i)] = &c.FloppyFiles[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
errs := make([]error, 0)
|
||||||
|
for n, ptr := range templates {
|
||||||
|
*ptr, err = t.Process(*ptr, nil)
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, fmt.Errorf("Error processing %s: %s", n, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validation
|
||||||
|
|
||||||
|
if c.Username == "" {
|
||||||
|
errs = append(errs, errors.New("A username for the xenserver host must be specified."))
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Password == "" {
|
||||||
|
errs = append(errs, errors.New("A password for the xenserver host must be specified."))
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.HostIp == "" {
|
||||||
|
errs = append(errs, errors.New("An ip for the xenserver host must be specified."))
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.VMName == "" {
|
||||||
|
errs = append(errs, errors.New("vm_name must be specified."))
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.HostPortMin > c.HostPortMax {
|
||||||
|
errs = append(errs, errors.New("the host min port must be less than the max"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.HTTPPortMin > c.HTTPPortMax {
|
||||||
|
errs = append(errs, errors.New("the HTTP min port must be less than the max"))
|
||||||
|
}
|
||||||
|
|
||||||
|
c.BootWait, err = time.ParseDuration(c.RawBootWait)
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, fmt.Errorf("Failed to parse boot_wait: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, command := range c.BootCommand {
|
||||||
|
if err := t.Validate(command); err != nil {
|
||||||
|
errs = append(errs,
|
||||||
|
fmt.Errorf("Error processing boot_command[%d]: %s", i, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.SSHKeyPath != "" {
|
||||||
|
if _, err := os.Stat(c.SSHKeyPath); err != nil {
|
||||||
|
errs = append(errs, fmt.Errorf("ssh_key_path is invalid: %s", err))
|
||||||
|
} else if _, err := commonssh.FileSigner(c.SSHKeyPath); err != nil {
|
||||||
|
errs = append(errs, fmt.Errorf("ssh_key_path is invalid: %s", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
if c.SSHHostPortMin > c.SSHHostPortMax {
|
||||||
|
errs = append(errs,
|
||||||
|
errors.New("ssh_host_port_min must be less than ssh_host_port_max"))
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
if c.SSHUser == "" {
|
||||||
|
errs = append(errs, errors.New("An ssh_username must be specified."))
|
||||||
|
}
|
||||||
|
|
||||||
|
c.SSHWaitTimeout, err = time.ParseDuration(c.RawSSHWaitTimeout)
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, fmt.Errorf("Failed to parse ssh_wait_timeout: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
switch c.Format {
|
||||||
|
case "xva", "vdi_raw":
|
||||||
|
default:
|
||||||
|
errs = append(errs, errors.New("format must be one of 'xva', 'vdi_raw'"))
|
||||||
|
}
|
||||||
|
|
||||||
|
switch c.KeepVM {
|
||||||
|
case "always", "never", "on_success":
|
||||||
|
default:
|
||||||
|
errs = append(errs, errors.New("keep_vm must be one of 'always', 'never', 'on_success'"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
if c.LocalIp == "" {
|
||||||
|
errs = append(errs, errors.New("A local IP visible to XenServer's mangement interface is required to serve files."))
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
// steps should check config.ShouldKeepVM first before cleaning up the VM
|
||||||
|
func (c CommonConfig) ShouldKeepVM(state multistep.StateBag) bool {
|
||||||
|
switch c.KeepVM {
|
||||||
|
case "always":
|
||||||
|
return true
|
||||||
|
case "never":
|
||||||
|
return false
|
||||||
|
case "on_success":
|
||||||
|
// only keep instance if build was successful
|
||||||
|
_, cancelled := state.GetOk(multistep.StateCancelled)
|
||||||
|
_, halted := state.GetOk(multistep.StateHalted)
|
||||||
|
return !(cancelled || halted)
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Unknown keep_vm value '%s'", c.KeepVM))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (config CommonConfig) GetSR(client XenAPIClient) (*SR, error) {
|
||||||
|
if config.SrName == "" {
|
||||||
|
// Find the default SR
|
||||||
|
return client.GetDefaultSR()
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Use the provided name label to find the SR to use
|
||||||
|
srs, err := client.GetSRByNameLabel(config.SrName)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case len(srs) == 0:
|
||||||
|
return nil, fmt.Errorf("Couldn't find a SR with the specified name-label '%s'", config.SrName)
|
||||||
|
case len(srs) > 1:
|
||||||
|
return nil, fmt.Errorf("Found more than one SR with the name '%s'. The name must be unique", config.SrName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return srs[0], nil
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package xenserver
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
@ -1,4 +1,4 @@
|
|||||||
package xenserver
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/mitchellh/multistep"
|
"github.com/mitchellh/multistep"
|
@ -1,4 +1,4 @@
|
|||||||
package xenserver
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@ -13,22 +13,21 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func sshAddress(state multistep.StateBag) (string, error) {
|
func SSHAddress(state multistep.StateBag) (string, error) {
|
||||||
sshIP := state.Get("ssh_address").(string)
|
sshIP := state.Get("ssh_address").(string)
|
||||||
sshHostPort := 22
|
sshHostPort := 22
|
||||||
return fmt.Sprintf("%s:%d", sshIP, sshHostPort), nil
|
return fmt.Sprintf("%s:%d", sshIP, sshHostPort), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func sshLocalAddress(state multistep.StateBag) (string, error) {
|
func SSHLocalAddress(state multistep.StateBag) (string, error) {
|
||||||
sshLocalPort := state.Get("local_ssh_port").(uint)
|
sshLocalPort := state.Get("local_ssh_port").(uint)
|
||||||
conn_str := fmt.Sprintf("%s:%d", "127.0.0.1", sshLocalPort)
|
conn_str := fmt.Sprintf("%s:%d", "127.0.0.1", sshLocalPort)
|
||||||
log.Printf("sshLocalAddress: %s", conn_str)
|
log.Printf("sshLocalAddress: %s", conn_str)
|
||||||
return conn_str, nil
|
return conn_str, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func sshConfig(state multistep.StateBag) (*gossh.ClientConfig, error) {
|
func SSHConfig(state multistep.StateBag) (*gossh.ClientConfig, error) {
|
||||||
config := state.Get("config").(config)
|
config := state.Get("commonconfig").(CommonConfig)
|
||||||
|
|
||||||
auth := []gossh.AuthMethod{
|
auth := []gossh.AuthMethod{
|
||||||
gossh.Password(config.SSHPassword),
|
gossh.Password(config.SSHPassword),
|
||||||
gossh.KeyboardInteractive(
|
gossh.KeyboardInteractive(
|
@ -1,4 +1,4 @@
|
|||||||
package xenserver
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -7,14 +7,14 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
)
|
)
|
||||||
|
|
||||||
type stepAttachVdi struct {
|
type StepAttachVdi struct {
|
||||||
VdiUuidKey string
|
VdiUuidKey string
|
||||||
VdiType VDIType
|
VdiType VDIType
|
||||||
|
|
||||||
vdi *VDI
|
vdi *VDI
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *stepAttachVdi) Run(state multistep.StateBag) multistep.StepAction {
|
func (self *StepAttachVdi) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
client := state.Get("client").(XenAPIClient)
|
client := state.Get("client").(XenAPIClient)
|
||||||
|
|
||||||
@ -51,10 +51,10 @@ func (self *stepAttachVdi) Run(state multistep.StateBag) multistep.StepAction {
|
|||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *stepAttachVdi) Cleanup(state multistep.StateBag) {
|
func (self *StepAttachVdi) Cleanup(state multistep.StateBag) {
|
||||||
config := state.Get("config").(config)
|
config := state.Get("commonconfig").(CommonConfig)
|
||||||
client := state.Get("client").(XenAPIClient)
|
client := state.Get("client").(XenAPIClient)
|
||||||
if config.ShouldKeepInstance(state) {
|
if config.ShouldKeepVM(state) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package xenserver
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -6,11 +6,11 @@ import (
|
|||||||
"github.com/mitchellh/packer/packer"
|
"github.com/mitchellh/packer/packer"
|
||||||
)
|
)
|
||||||
|
|
||||||
type stepBootWait struct{}
|
type StepBootWait struct{}
|
||||||
|
|
||||||
func (self *stepBootWait) Run(state multistep.StateBag) multistep.StepAction {
|
func (self *StepBootWait) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
client := state.Get("client").(XenAPIClient)
|
client := state.Get("client").(XenAPIClient)
|
||||||
config := state.Get("config").(config)
|
config := state.Get("commonconfig").(CommonConfig)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
instance, _ := client.GetVMByUuid(state.Get("instance_uuid").(string))
|
instance, _ := client.GetVMByUuid(state.Get("instance_uuid").(string))
|
||||||
@ -28,4 +28,4 @@ func (self *stepBootWait) Run(state multistep.StateBag) multistep.StepAction {
|
|||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *stepBootWait) Cleanup(state multistep.StateBag) {}
|
func (self *StepBootWait) Cleanup(state multistep.StateBag) {}
|
@ -1,4 +1,4 @@
|
|||||||
package xenserver
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -7,11 +7,11 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
)
|
)
|
||||||
|
|
||||||
type stepDetachVdi struct {
|
type StepDetachVdi struct {
|
||||||
VdiUuidKey string
|
VdiUuidKey string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *stepDetachVdi) Run(state multistep.StateBag) multistep.StepAction {
|
func (self *StepDetachVdi) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
client := state.Get("client").(XenAPIClient)
|
client := state.Get("client").(XenAPIClient)
|
||||||
|
|
||||||
@ -47,4 +47,4 @@ func (self *stepDetachVdi) Run(state multistep.StateBag) multistep.StepAction {
|
|||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *stepDetachVdi) Cleanup(state multistep.StateBag) {}
|
func (self *StepDetachVdi) Cleanup(state multistep.StateBag) {}
|
@ -1,4 +1,4 @@
|
|||||||
package xenserver
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -6,13 +6,13 @@ import (
|
|||||||
"github.com/mitchellh/packer/packer"
|
"github.com/mitchellh/packer/packer"
|
||||||
)
|
)
|
||||||
|
|
||||||
type stepFindVdi struct {
|
type StepFindVdi struct {
|
||||||
VdiName string
|
VdiName string
|
||||||
ImagePathFunc func() string
|
ImagePathFunc func() string
|
||||||
VdiUuidKey string
|
VdiUuidKey string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *stepFindVdi) Run(state multistep.StateBag) multistep.StepAction {
|
func (self *StepFindVdi) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
client := state.Get("client").(XenAPIClient)
|
client := state.Get("client").(XenAPIClient)
|
||||||
|
|
||||||
@ -39,4 +39,4 @@ func (self *stepFindVdi) Run(state multistep.StateBag) multistep.StepAction {
|
|||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *stepFindVdi) Cleanup(state multistep.StateBag) {}
|
func (self *StepFindVdi) Cleanup(state multistep.StateBag) {}
|
@ -1,4 +1,4 @@
|
|||||||
package xenserver
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/mitchellh/packer/packer"
|
"github.com/mitchellh/packer/packer"
|
||||||
)
|
)
|
||||||
|
|
||||||
type stepForwardPortOverSSH struct {
|
type StepForwardPortOverSSH struct {
|
||||||
RemotePort func(state multistep.StateBag) (uint, error)
|
RemotePort func(state multistep.StateBag) (uint, error)
|
||||||
RemoteDest func(state multistep.StateBag) (string, error)
|
RemoteDest func(state multistep.StateBag) (string, error)
|
||||||
|
|
||||||
@ -16,9 +16,9 @@ type stepForwardPortOverSSH struct {
|
|||||||
ResultKey string
|
ResultKey string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *stepForwardPortOverSSH) Run(state multistep.StateBag) multistep.StepAction {
|
func (self *StepForwardPortOverSSH) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
|
||||||
config := state.Get("config").(config)
|
config := state.Get("commonconfig").(CommonConfig)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
// Find a free local port:
|
// Find a free local port:
|
||||||
@ -46,4 +46,4 @@ func (self *stepForwardPortOverSSH) Run(state multistep.StateBag) multistep.Step
|
|||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *stepForwardPortOverSSH) Cleanup(state multistep.StateBag) {}
|
func (self *StepForwardPortOverSSH) Cleanup(state multistep.StateBag) {}
|
@ -1,4 +1,4 @@
|
|||||||
package xenserver
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -7,11 +7,11 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
type stepGetVNCPort struct{}
|
type StepGetVNCPort struct{}
|
||||||
|
|
||||||
func (self *stepGetVNCPort) Run(state multistep.StateBag) multistep.StepAction {
|
func (self *StepGetVNCPort) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
|
||||||
config := state.Get("config").(config)
|
config := state.Get("commonconfig").(CommonConfig)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
ui.Say("Step: forward the instances VNC port over SSH")
|
ui.Say("Step: forward the instances VNC port over SSH")
|
||||||
@ -38,15 +38,15 @@ func (self *stepGetVNCPort) Run(state multistep.StateBag) multistep.StepAction {
|
|||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *stepGetVNCPort) Cleanup(state multistep.StateBag) {
|
func (self *StepGetVNCPort) Cleanup(state multistep.StateBag) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func instanceVNCPort(state multistep.StateBag) (uint, error) {
|
func InstanceVNCPort(state multistep.StateBag) (uint, error) {
|
||||||
vncPort := state.Get("instance_vnc_port").(uint)
|
vncPort := state.Get("instance_vnc_port").(uint)
|
||||||
return vncPort, nil
|
return vncPort, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func instanceVNCIP(state multistep.StateBag) (string, error) {
|
func InstanceVNCIP(state multistep.StateBag) (string, error) {
|
||||||
// The port is in Dom0, so we want to forward from localhost
|
// The port is in Dom0, so we want to forward from localhost
|
||||||
return "127.0.0.1", nil
|
return "127.0.0.1", nil
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package xenserver
|
package common
|
||||||
|
|
||||||
// Taken from mitchellh/packer/builder/qemu/step_http_server.go
|
// Taken from mitchellh/packer/builder/qemu/step_http_server.go
|
||||||
|
|
||||||
@ -20,12 +20,12 @@ import (
|
|||||||
//
|
//
|
||||||
// Produces:
|
// Produces:
|
||||||
// http_port int - The port the HTTP server started on.
|
// http_port int - The port the HTTP server started on.
|
||||||
type stepHTTPServer struct {
|
type StepHTTPServer struct {
|
||||||
l net.Listener
|
l net.Listener
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stepHTTPServer) Run(state multistep.StateBag) multistep.StepAction {
|
func (s *StepHTTPServer) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
config := state.Get("config").(config)
|
config := state.Get("commonconfig").(CommonConfig)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
var httpPort uint = 0
|
var httpPort uint = 0
|
||||||
@ -54,7 +54,7 @@ func (s *stepHTTPServer) Run(state multistep.StateBag) multistep.StepAction {
|
|||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stepHTTPServer) Cleanup(multistep.StateBag) {
|
func (s *StepHTTPServer) Cleanup(multistep.StateBag) {
|
||||||
if s.l != nil {
|
if s.l != nil {
|
||||||
// Close the listener so that the HTTP server stops
|
// Close the listener so that the HTTP server stops
|
||||||
s.l.Close()
|
s.l.Close()
|
@ -1,4 +1,4 @@
|
|||||||
package xenserver
|
package common
|
||||||
|
|
||||||
/* Taken from https://raw.githubusercontent.com/mitchellh/packer/master/builder/qemu/step_prepare_output_dir.go */
|
/* Taken from https://raw.githubusercontent.com/mitchellh/packer/master/builder/qemu/step_prepare_output_dir.go */
|
||||||
|
|
||||||
@ -10,18 +10,20 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type stepPrepareOutputDir struct{}
|
type StepPrepareOutputDir struct {
|
||||||
|
Force bool
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
func (stepPrepareOutputDir) Run(state multistep.StateBag) multistep.StepAction {
|
func (self *StepPrepareOutputDir) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
config := state.Get("config").(config)
|
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
if _, err := os.Stat(config.OutputDir); err == nil && config.PackerForce {
|
if _, err := os.Stat(self.Path); err == nil && self.Force {
|
||||||
ui.Say("Deleting previous output directory...")
|
ui.Say("Deleting previous output directory...")
|
||||||
os.RemoveAll(config.OutputDir)
|
os.RemoveAll(self.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.MkdirAll(config.OutputDir, 0755); err != nil {
|
if err := os.MkdirAll(self.Path, 0755); err != nil {
|
||||||
state.Put("error", err)
|
state.Put("error", err)
|
||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
@ -29,17 +31,16 @@ func (stepPrepareOutputDir) Run(state multistep.StateBag) multistep.StepAction {
|
|||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
func (stepPrepareOutputDir) Cleanup(state multistep.StateBag) {
|
func (self *StepPrepareOutputDir) Cleanup(state multistep.StateBag) {
|
||||||
_, cancelled := state.GetOk(multistep.StateCancelled)
|
_, cancelled := state.GetOk(multistep.StateCancelled)
|
||||||
_, halted := state.GetOk(multistep.StateHalted)
|
_, halted := state.GetOk(multistep.StateHalted)
|
||||||
|
|
||||||
if cancelled || halted {
|
if cancelled || halted {
|
||||||
config := state.Get("config").(config)
|
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
ui.Say("Deleting output directory...")
|
ui.Say("Deleting output directory...")
|
||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
err := os.RemoveAll(config.OutputDir)
|
err := os.RemoveAll(self.Path)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package xenserver
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -6,9 +6,9 @@ import (
|
|||||||
"github.com/mitchellh/packer/packer"
|
"github.com/mitchellh/packer/packer"
|
||||||
)
|
)
|
||||||
|
|
||||||
type stepRemoveDevices struct{}
|
type StepRemoveDevices struct{}
|
||||||
|
|
||||||
func (self *stepRemoveDevices) Run(state multistep.StateBag) multistep.StepAction {
|
func (self *StepRemoveDevices) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
client := state.Get("client").(XenAPIClient)
|
client := state.Get("client").(XenAPIClient)
|
||||||
|
|
||||||
@ -37,4 +37,4 @@ func (self *stepRemoveDevices) Run(state multistep.StateBag) multistep.StepActio
|
|||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *stepRemoveDevices) Cleanup(state multistep.StateBag) {}
|
func (self *StepRemoveDevices) Cleanup(state multistep.StateBag) {}
|
@ -1,4 +1,4 @@
|
|||||||
package xenserver
|
package common
|
||||||
|
|
||||||
/* Taken from https://raw.githubusercontent.com/mitchellh/packer/master/builder/qemu/step_prepare_output_dir.go */
|
/* Taken from https://raw.githubusercontent.com/mitchellh/packer/master/builder/qemu/step_prepare_output_dir.go */
|
||||||
|
|
||||||
@ -13,7 +13,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type stepShutdownAndExport struct{}
|
type StepShutdownAndExport struct{}
|
||||||
|
|
||||||
func downloadFile(url, filename string) (err error) {
|
func downloadFile(url, filename string) (err error) {
|
||||||
|
|
||||||
@ -44,8 +44,8 @@ func downloadFile(url, filename string) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (stepShutdownAndExport) Run(state multistep.StateBag) multistep.StepAction {
|
func (StepShutdownAndExport) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
config := state.Get("config").(config)
|
config := state.Get("commonconfig").(CommonConfig)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
client := state.Get("client").(XenAPIClient)
|
client := state.Get("client").(XenAPIClient)
|
||||||
instance_uuid := state.Get("instance_uuid").(string)
|
instance_uuid := state.Get("instance_uuid").(string)
|
||||||
@ -56,7 +56,7 @@ func (stepShutdownAndExport) Run(state multistep.StateBag) multistep.StepAction
|
|||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.Say("Step: Shutdown and export VPX")
|
ui.Say("Step: Shutdown and export")
|
||||||
|
|
||||||
// Shutdown the VM
|
// Shutdown the VM
|
||||||
success := func() bool {
|
success := func() bool {
|
||||||
@ -109,7 +109,7 @@ func (stepShutdownAndExport) Run(state multistep.StateBag) multistep.StepAction
|
|||||||
|
|
||||||
ui.Say("Successfully shut down VM")
|
ui.Say("Successfully shut down VM")
|
||||||
|
|
||||||
switch config.ExportFormat {
|
switch config.Format {
|
||||||
case "xva":
|
case "xva":
|
||||||
// export the VM
|
// export the VM
|
||||||
|
|
||||||
@ -164,7 +164,7 @@ func (stepShutdownAndExport) Run(state multistep.StateBag) multistep.StepAction
|
|||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("Unknown export_format '%s'", config.ExportFormat))
|
panic(fmt.Sprintf("Unknown export format '%s'", config.Format))
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.Say("Download completed: " + config.OutputDir)
|
ui.Say("Download completed: " + config.OutputDir)
|
||||||
@ -172,5 +172,4 @@ func (stepShutdownAndExport) Run(state multistep.StateBag) multistep.StepAction
|
|||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
func (stepShutdownAndExport) Cleanup(state multistep.StateBag) {
|
func (StepShutdownAndExport) Cleanup(state multistep.StateBag) {}
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
package xenserver
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
gossh "code.google.com/p/go.crypto/ssh"
|
gossh "code.google.com/p/go.crypto/ssh"
|
||||||
@ -9,7 +9,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type stepStartOnHIMN struct{}
|
type StepStartOnHIMN struct{}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This step starts the installed guest on the Host Internal Management Network
|
* This step starts the installed guest on the Host Internal Management Network
|
||||||
@ -19,11 +19,11 @@ type stepStartOnHIMN struct{}
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func (self *stepStartOnHIMN) Run(state multistep.StateBag) multistep.StepAction {
|
func (self *StepStartOnHIMN) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
client := state.Get("client").(XenAPIClient)
|
client := state.Get("client").(XenAPIClient)
|
||||||
config := state.Get("config").(config)
|
config := state.Get("commonconfig").(CommonConfig)
|
||||||
|
|
||||||
ui.Say("Step: Start VM on the Host Internal Mangement Network")
|
ui.Say("Step: Start VM on the Host Internal Mangement Network")
|
||||||
|
|
||||||
@ -124,13 +124,13 @@ func (self *stepStartOnHIMN) Run(state multistep.StateBag) multistep.StepAction
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *stepStartOnHIMN) Cleanup(state multistep.StateBag) {}
|
func (self *StepStartOnHIMN) Cleanup(state multistep.StateBag) {}
|
||||||
|
|
||||||
func himnSSHIP(state multistep.StateBag) (string, error) {
|
func HimnSSHIP(state multistep.StateBag) (string, error) {
|
||||||
ip := state.Get("himn_ssh_address").(string)
|
ip := state.Get("himn_ssh_address").(string)
|
||||||
return ip, nil
|
return ip, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func himnSSHPort(state multistep.StateBag) (uint, error) {
|
func HimnSSHPort(state multistep.StateBag) (uint, error) {
|
||||||
return 22, nil
|
return 22, nil
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package xenserver
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -7,9 +7,9 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
)
|
)
|
||||||
|
|
||||||
type stepStartVmPaused struct{}
|
type StepStartVmPaused struct{}
|
||||||
|
|
||||||
func (self *stepStartVmPaused) Run(state multistep.StateBag) multistep.StepAction {
|
func (self *StepStartVmPaused) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
|
||||||
client := state.Get("client").(XenAPIClient)
|
client := state.Get("client").(XenAPIClient)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
@ -39,11 +39,11 @@ func (self *stepStartVmPaused) Run(state multistep.StateBag) multistep.StepActio
|
|||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *stepStartVmPaused) Cleanup(state multistep.StateBag) {
|
func (self *StepStartVmPaused) Cleanup(state multistep.StateBag) {
|
||||||
config := state.Get("config").(config)
|
config := state.Get("commonconfig").(CommonConfig)
|
||||||
client := state.Get("client").(XenAPIClient)
|
client := state.Get("client").(XenAPIClient)
|
||||||
|
|
||||||
if config.ShouldKeepInstance(state) {
|
if config.ShouldKeepVM(state) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package xenserver
|
package common
|
||||||
|
|
||||||
/* Heavily borrowed from builder/quemu/step_type_boot_command.go */
|
/* Heavily borrowed from builder/quemu/step_type_boot_command.go */
|
||||||
|
|
||||||
@ -23,10 +23,12 @@ type bootCommandTemplateData struct {
|
|||||||
HTTPPort uint
|
HTTPPort uint
|
||||||
}
|
}
|
||||||
|
|
||||||
type stepTypeBootCommand struct{}
|
type StepTypeBootCommand struct {
|
||||||
|
Tpl *packer.ConfigTemplate
|
||||||
|
}
|
||||||
|
|
||||||
func (self *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction {
|
func (self *StepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
config := state.Get("config").(config)
|
config := state.Get("commonconfig").(CommonConfig)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
vnc_port := state.Get("local_vnc_port").(uint)
|
vnc_port := state.Get("local_vnc_port").(uint)
|
||||||
http_port := state.Get("http_port").(uint)
|
http_port := state.Get("http_port").(uint)
|
||||||
@ -68,7 +70,7 @@ func (self *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAct
|
|||||||
ui.Say("About to type boot commands over VNC...")
|
ui.Say("About to type boot commands over VNC...")
|
||||||
for _, command := range config.BootCommand {
|
for _, command := range config.BootCommand {
|
||||||
|
|
||||||
command, err := config.tpl.Process(command, tplData)
|
command, err := self.Tpl.Process(command, tplData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("Error preparing boot command: %s", err)
|
err := fmt.Errorf("Error preparing boot command: %s", err)
|
||||||
state.Put("error", err)
|
state.Put("error", err)
|
||||||
@ -89,7 +91,7 @@ func (self *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAct
|
|||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *stepTypeBootCommand) Cleanup(multistep.StateBag) {}
|
func (self *StepTypeBootCommand) Cleanup(multistep.StateBag) {}
|
||||||
|
|
||||||
// Taken from qemu's builder plugin - not an exported function.
|
// Taken from qemu's builder plugin - not an exported function.
|
||||||
func vncSendString(c *vnc.ClientConn, original string) {
|
func vncSendString(c *vnc.ClientConn, original string) {
|
@ -1,4 +1,4 @@
|
|||||||
package xenserver
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
@ -11,14 +11,14 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type stepUploadVdi struct {
|
type StepUploadVdi struct {
|
||||||
VdiName string
|
VdiName string
|
||||||
ImagePathFunc func() string
|
ImagePathFunc func() string
|
||||||
VdiUuidKey string
|
VdiUuidKey string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *stepUploadVdi) Run(state multistep.StateBag) multistep.StepAction {
|
func (self *StepUploadVdi) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
config := state.Get("config").(config)
|
config := state.Get("commonconfig").(CommonConfig)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
client := state.Get("client").(XenAPIClient)
|
client := state.Get("client").(XenAPIClient)
|
||||||
|
|
||||||
@ -154,12 +154,12 @@ func (self *stepUploadVdi) Run(state multistep.StateBag) multistep.StepAction {
|
|||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *stepUploadVdi) Cleanup(state multistep.StateBag) {
|
func (self *StepUploadVdi) Cleanup(state multistep.StateBag) {
|
||||||
config := state.Get("config").(config)
|
config := state.Get("commonconfig").(CommonConfig)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
client := state.Get("client").(XenAPIClient)
|
client := state.Get("client").(XenAPIClient)
|
||||||
|
|
||||||
if config.ShouldKeepInstance(state) {
|
if config.ShouldKeepVM(state) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
304
builder/xenserver/iso/builder.go
Normal file
304
builder/xenserver/iso/builder.go
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
package iso
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/mitchellh/multistep"
|
||||||
|
"github.com/mitchellh/packer/common"
|
||||||
|
"github.com/mitchellh/packer/packer"
|
||||||
|
xscommon "github.com/rdobson/packer-builder-xenserver/builder/xenserver/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
type config struct {
|
||||||
|
common.PackerConfig `mapstructure:",squash"`
|
||||||
|
xscommon.CommonConfig `mapstructure:",squash"`
|
||||||
|
|
||||||
|
VMMemory uint `mapstructure:"vm_memory"`
|
||||||
|
DiskSize uint `mapstructure:"disk_size"`
|
||||||
|
CloneTemplate string `mapstructure:"clone_template"`
|
||||||
|
|
||||||
|
ISOChecksum string `mapstructure:"iso_checksum"`
|
||||||
|
ISOChecksumType string `mapstructure:"iso_checksum_type"`
|
||||||
|
ISOUrls []string `mapstructure:"iso_urls"`
|
||||||
|
ISOUrl string `mapstructure:"iso_url"`
|
||||||
|
|
||||||
|
PlatformArgs map[string]string `mapstructure:"platform_args"`
|
||||||
|
|
||||||
|
RawInstallTimeout string `mapstructure:"install_timeout"`
|
||||||
|
InstallTimeout time.Duration ``
|
||||||
|
|
||||||
|
tpl *packer.ConfigTemplate
|
||||||
|
}
|
||||||
|
|
||||||
|
type Builder struct {
|
||||||
|
config config
|
||||||
|
runner multistep.Runner
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Builder) Prepare(raws ...interface{}) (params []string, retErr error) {
|
||||||
|
|
||||||
|
md, err := common.DecodeConfig(&self.config, raws...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
self.config.tpl, err = packer.NewConfigTemplate()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
self.config.tpl.UserVars = self.config.PackerUserVars
|
||||||
|
|
||||||
|
errs := common.CheckUnusedConfig(md)
|
||||||
|
errs = packer.MultiErrorAppend(
|
||||||
|
errs, self.config.CommonConfig.Prepare(self.config.tpl, &self.config.PackerConfig)...)
|
||||||
|
|
||||||
|
// Set default values
|
||||||
|
|
||||||
|
if self.config.RawInstallTimeout == "" {
|
||||||
|
self.config.RawInstallTimeout = "200m"
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.config.DiskSize == 0 {
|
||||||
|
self.config.DiskSize = 40000
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.config.VMMemory == 0 {
|
||||||
|
self.config.VMMemory = 1024
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.config.CloneTemplate == "" {
|
||||||
|
self.config.CloneTemplate = "Other install media"
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
"network_name": &self.config.NetworkName,
|
||||||
|
"iso_checksum": &self.config.ISOChecksum,
|
||||||
|
"iso_checksum_type": &self.config.ISOChecksumType,
|
||||||
|
"iso_url": &self.config.ISOUrl,
|
||||||
|
"install_timeout": &self.config.RawInstallTimeout,
|
||||||
|
}
|
||||||
|
for i := range self.config.ISOUrls {
|
||||||
|
templates[fmt.Sprintf("iso_urls[%d]", i)] = &self.config.ISOUrls[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
for n, ptr := range templates {
|
||||||
|
var err error
|
||||||
|
*ptr, err = self.config.tpl.Process(*ptr, nil)
|
||||||
|
if err != nil {
|
||||||
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Error processing %s: %s", n, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.ISOChecksumType == "" {
|
||||||
|
errs = packer.MultiErrorAppend(
|
||||||
|
errs, errors.New("The iso_checksum_type must be specified."))
|
||||||
|
} else {
|
||||||
|
self.config.ISOChecksumType = strings.ToLower(self.config.ISOChecksumType)
|
||||||
|
if self.config.ISOChecksumType != "none" {
|
||||||
|
if self.config.ISOChecksum == "" {
|
||||||
|
errs = packer.MultiErrorAppend(
|
||||||
|
errs, errors.New("Due to the file size being large, an iso_checksum is required."))
|
||||||
|
} else {
|
||||||
|
self.config.ISOChecksum = strings.ToLower(self.config.ISOChecksum)
|
||||||
|
}
|
||||||
|
|
||||||
|
if hash := common.HashForType(self.config.ISOChecksumType); hash == nil {
|
||||||
|
errs = packer.MultiErrorAppend(
|
||||||
|
errs, fmt.Errorf("Unsupported checksum type: %s", self.config.ISOChecksumType))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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."))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, url := range self.config.ISOUrls {
|
||||||
|
self.config.ISOUrls[i], err = common.DownloadableURL(url)
|
||||||
|
if err != nil {
|
||||||
|
errs = packer.MultiErrorAppend(
|
||||||
|
errs, fmt.Errorf("Failed to parse iso_urls[%d]: %s", i, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
client := xscommon.NewXenAPIClient(self.config.HostIp, self.config.Username, self.config.Password)
|
||||||
|
|
||||||
|
err := client.Login()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err.(error)
|
||||||
|
}
|
||||||
|
ui.Say("XAPI client session established")
|
||||||
|
|
||||||
|
client.GetHosts()
|
||||||
|
|
||||||
|
//Share state between the other steps using a statebag
|
||||||
|
state := new(multistep.BasicStateBag)
|
||||||
|
state.Put("cache", cache)
|
||||||
|
state.Put("client", client)
|
||||||
|
state.Put("config", self.config)
|
||||||
|
state.Put("commonconfig", self.config.CommonConfig)
|
||||||
|
state.Put("hook", hook)
|
||||||
|
state.Put("ui", ui)
|
||||||
|
|
||||||
|
//Build the steps
|
||||||
|
steps := []multistep.Step{
|
||||||
|
&common.StepDownload{
|
||||||
|
Checksum: self.config.ISOChecksum,
|
||||||
|
ChecksumType: self.config.ISOChecksumType,
|
||||||
|
Description: "ISO",
|
||||||
|
ResultKey: "iso_path",
|
||||||
|
Url: self.config.ISOUrls,
|
||||||
|
},
|
||||||
|
&xscommon.StepPrepareOutputDir{
|
||||||
|
Force: self.config.PackerForce,
|
||||||
|
Path: self.config.OutputDir,
|
||||||
|
},
|
||||||
|
&common.StepCreateFloppy{
|
||||||
|
Files: self.config.FloppyFiles,
|
||||||
|
},
|
||||||
|
new(xscommon.StepHTTPServer),
|
||||||
|
&xscommon.StepUploadVdi{
|
||||||
|
VdiName: "Packer-floppy-disk",
|
||||||
|
ImagePathFunc: func() string {
|
||||||
|
if floppyPath, ok := state.GetOk("floppy_path"); ok {
|
||||||
|
return floppyPath.(string)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
},
|
||||||
|
VdiUuidKey: "floppy_vdi_uuid",
|
||||||
|
},
|
||||||
|
&xscommon.StepUploadVdi{
|
||||||
|
VdiName: path.Base(self.config.ISOUrls[0]),
|
||||||
|
ImagePathFunc: func() string {
|
||||||
|
return state.Get("iso_path").(string)
|
||||||
|
},
|
||||||
|
VdiUuidKey: "iso_vdi_uuid",
|
||||||
|
},
|
||||||
|
&xscommon.StepFindVdi{
|
||||||
|
VdiName: self.config.ToolsIsoName,
|
||||||
|
VdiUuidKey: "tools_vdi_uuid",
|
||||||
|
},
|
||||||
|
new(stepCreateInstance),
|
||||||
|
&xscommon.StepAttachVdi{
|
||||||
|
VdiUuidKey: "floppy_vdi_uuid",
|
||||||
|
VdiType: xscommon.Floppy,
|
||||||
|
},
|
||||||
|
&xscommon.StepAttachVdi{
|
||||||
|
VdiUuidKey: "iso_vdi_uuid",
|
||||||
|
VdiType: xscommon.CD,
|
||||||
|
},
|
||||||
|
&xscommon.StepAttachVdi{
|
||||||
|
VdiUuidKey: "tools_vdi_uuid",
|
||||||
|
VdiType: xscommon.CD,
|
||||||
|
},
|
||||||
|
new(xscommon.StepStartVmPaused),
|
||||||
|
new(xscommon.StepGetVNCPort),
|
||||||
|
&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{
|
||||||
|
Tpl: self.config.tpl,
|
||||||
|
},
|
||||||
|
new(stepWait),
|
||||||
|
&xscommon.StepDetachVdi{
|
||||||
|
VdiUuidKey: "floppy_vdi_uuid",
|
||||||
|
},
|
||||||
|
&xscommon.StepDetachVdi{
|
||||||
|
VdiUuidKey: "iso_vdi_uuid",
|
||||||
|
},
|
||||||
|
new(xscommon.StepRemoveDevices),
|
||||||
|
new(xscommon.StepStartOnHIMN),
|
||||||
|
&xscommon.StepForwardPortOverSSH{
|
||||||
|
RemotePort: xscommon.HimnSSHPort,
|
||||||
|
RemoteDest: xscommon.HimnSSHIP,
|
||||||
|
HostPortMin: self.config.HostPortMin,
|
||||||
|
HostPortMax: self.config.HostPortMax,
|
||||||
|
ResultKey: "local_ssh_port",
|
||||||
|
},
|
||||||
|
&common.StepConnectSSH{
|
||||||
|
SSHAddress: xscommon.SSHLocalAddress,
|
||||||
|
SSHConfig: xscommon.SSHConfig,
|
||||||
|
SSHWaitTimeout: self.config.SSHWaitTimeout,
|
||||||
|
},
|
||||||
|
new(common.StepProvision),
|
||||||
|
new(xscommon.StepShutdownAndExport),
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package xenserver
|
package iso
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/mitchellh/packer/packer"
|
"github.com/mitchellh/packer/packer"
|
@ -1,19 +1,21 @@
|
|||||||
package xenserver
|
package iso
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/mitchellh/multistep"
|
"github.com/mitchellh/multistep"
|
||||||
"github.com/mitchellh/packer/packer"
|
"github.com/mitchellh/packer/packer"
|
||||||
|
xscommon "github.com/rdobson/packer-builder-xenserver/builder/xenserver/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
type stepCreateInstance struct {
|
type stepCreateInstance struct {
|
||||||
instance *VM
|
instance *xscommon.VM
|
||||||
vdi *VDI
|
vdi *xscommon.VDI
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction {
|
func (self *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
|
||||||
client := state.Get("client").(XenAPIClient)
|
client := state.Get("client").(xscommon.XenAPIClient)
|
||||||
config := state.Get("config").(config)
|
config := state.Get("config").(config)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
@ -75,7 +77,7 @@ func (self *stepCreateInstance) Run(state multistep.StateBag) multistep.StepActi
|
|||||||
}
|
}
|
||||||
self.vdi = vdi
|
self.vdi = vdi
|
||||||
|
|
||||||
err = instance.ConnectVdi(vdi, Disk)
|
err = instance.ConnectVdi(vdi, xscommon.Disk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ui.Error(fmt.Sprintf("Unable to connect packer disk VDI: %s", err.Error()))
|
ui.Error(fmt.Sprintf("Unable to connect packer disk VDI: %s", err.Error()))
|
||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
@ -83,11 +85,11 @@ func (self *stepCreateInstance) Run(state multistep.StateBag) multistep.StepActi
|
|||||||
|
|
||||||
// Connect Network
|
// Connect Network
|
||||||
|
|
||||||
var network *Network
|
var network *xscommon.Network
|
||||||
|
|
||||||
if config.NetworkName == "" {
|
if config.NetworkName == "" {
|
||||||
// No network has be specified. Use the management interface
|
// No network has be specified. Use the management interface
|
||||||
network = new(Network)
|
network = new(xscommon.Network)
|
||||||
network.Ref = ""
|
network.Ref = ""
|
||||||
network.Client = &client
|
network.Client = &client
|
||||||
|
|
||||||
@ -162,7 +164,7 @@ func (self *stepCreateInstance) Run(state multistep.StateBag) multistep.StepActi
|
|||||||
|
|
||||||
func (self *stepCreateInstance) Cleanup(state multistep.StateBag) {
|
func (self *stepCreateInstance) Cleanup(state multistep.StateBag) {
|
||||||
config := state.Get("config").(config)
|
config := state.Get("config").(config)
|
||||||
if config.ShouldKeepInstance(state) {
|
if config.ShouldKeepVM(state) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -1,11 +1,13 @@
|
|||||||
package xenserver
|
package iso
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/mitchellh/multistep"
|
|
||||||
"github.com/mitchellh/packer/packer"
|
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/mitchellh/multistep"
|
||||||
|
"github.com/mitchellh/packer/packer"
|
||||||
|
xscommon "github.com/rdobson/packer-builder-xenserver/builder/xenserver/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
type stepWait struct{}
|
type stepWait struct{}
|
||||||
@ -13,7 +15,7 @@ type stepWait struct{}
|
|||||||
func (self *stepWait) Run(state multistep.StateBag) multistep.StepAction {
|
func (self *stepWait) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
config := state.Get("config").(config)
|
config := state.Get("config").(config)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
client := state.Get("client").(XenAPIClient)
|
client := state.Get("client").(xscommon.XenAPIClient)
|
||||||
|
|
||||||
ui.Say("Step: Wait for install to complete.")
|
ui.Say("Step: Wait for install to complete.")
|
||||||
|
|
||||||
@ -26,7 +28,7 @@ func (self *stepWait) Run(state multistep.StateBag) multistep.StepAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Expect install to be configured to shutdown on completion
|
//Expect install to be configured to shutdown on completion
|
||||||
err = InterruptibleWait{
|
err = xscommon.InterruptibleWait{
|
||||||
Predicate: func() (bool, error) {
|
Predicate: func() (bool, error) {
|
||||||
log.Printf("Waiting for install to complete.")
|
log.Printf("Waiting for install to complete.")
|
||||||
power_state, err := instance.GetPowerState()
|
power_state, err := instance.GetPowerState()
|
192
builder/xenserver/xva/builder.go
Normal file
192
builder/xenserver/xva/builder.go
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
package xva
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/mitchellh/multistep"
|
||||||
|
"github.com/mitchellh/packer/common"
|
||||||
|
"github.com/mitchellh/packer/packer"
|
||||||
|
xscommon "github.com/rdobson/packer-builder-xenserver/builder/xenserver/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
type config struct {
|
||||||
|
common.PackerConfig `mapstructure:",squash"`
|
||||||
|
xscommon.CommonConfig `mapstructure:",squash"`
|
||||||
|
|
||||||
|
VMMemory uint `mapstructure:"vm_memory"`
|
||||||
|
CloneTemplate string `mapstructure:"clone_template"`
|
||||||
|
|
||||||
|
PlatformArgs map[string]string `mapstructure:"platform_args"`
|
||||||
|
|
||||||
|
tpl *packer.ConfigTemplate
|
||||||
|
}
|
||||||
|
|
||||||
|
type Builder struct {
|
||||||
|
config config
|
||||||
|
runner multistep.Runner
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Builder) Prepare(raws ...interface{}) (params []string, retErr error) {
|
||||||
|
|
||||||
|
md, err := common.DecodeConfig(&self.config, raws...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
self.config.tpl, err = packer.NewConfigTemplate()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
self.config.tpl.UserVars = self.config.PackerUserVars
|
||||||
|
|
||||||
|
errs := common.CheckUnusedConfig(md)
|
||||||
|
errs = packer.MultiErrorAppend(
|
||||||
|
errs, self.config.CommonConfig.Prepare(self.config.tpl, &self.config.PackerConfig)...)
|
||||||
|
|
||||||
|
// Set default values
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Template substitution
|
||||||
|
|
||||||
|
templates := map[string]*string{
|
||||||
|
"clone_template": &self.config.CloneTemplate,
|
||||||
|
"network_name": &self.config.NetworkName,
|
||||||
|
}
|
||||||
|
|
||||||
|
for n, ptr := range templates {
|
||||||
|
var err error
|
||||||
|
*ptr, err = self.config.tpl.Process(*ptr, nil)
|
||||||
|
if err != nil {
|
||||||
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Error processing %s: %s", n, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validation
|
||||||
|
|
||||||
|
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
|
||||||
|
client := xscommon.NewXenAPIClient(self.config.HostIp, self.config.Username, self.config.Password)
|
||||||
|
|
||||||
|
err := client.Login()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err.(error)
|
||||||
|
}
|
||||||
|
ui.Say("XAPI client session established")
|
||||||
|
|
||||||
|
client.GetHosts()
|
||||||
|
|
||||||
|
//Share state between the other steps using a statebag
|
||||||
|
state := new(multistep.BasicStateBag)
|
||||||
|
state.Put("cache", cache)
|
||||||
|
state.Put("client", client)
|
||||||
|
state.Put("config", self.config)
|
||||||
|
state.Put("hook", hook)
|
||||||
|
state.Put("ui", ui)
|
||||||
|
|
||||||
|
//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{
|
||||||
|
VdiName: "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(stepCreateInstance),
|
||||||
|
&xscommon.StepAttachVdi{
|
||||||
|
VdiUuidKey: "floppy_vdi_uuid",
|
||||||
|
VdiType: xscommon.Floppy,
|
||||||
|
},
|
||||||
|
&xscommon.StepAttachVdi{
|
||||||
|
VdiUuidKey: "tools_vdi_uuid",
|
||||||
|
VdiType: xscommon.CD,
|
||||||
|
},
|
||||||
|
new(xscommon.StepStartOnHIMN),
|
||||||
|
&xscommon.StepForwardPortOverSSH{
|
||||||
|
RemotePort: xscommon.HimnSSHPort,
|
||||||
|
RemoteDest: xscommon.HimnSSHIP,
|
||||||
|
HostPortMin: self.config.HostPortMin,
|
||||||
|
HostPortMax: self.config.HostPortMax,
|
||||||
|
ResultKey: "local_ssh_port",
|
||||||
|
},
|
||||||
|
&common.StepConnectSSH{
|
||||||
|
SSHAddress: xscommon.SSHLocalAddress,
|
||||||
|
SSHConfig: xscommon.SSHConfig,
|
||||||
|
SSHWaitTimeout: self.config.SSHWaitTimeout,
|
||||||
|
},
|
||||||
|
new(common.StepProvision),
|
||||||
|
&xscommon.StepDetachVdi{
|
||||||
|
VdiUuidKey: "floppy_vdi_uuid",
|
||||||
|
},
|
||||||
|
&xscommon.StepDetachVdi{
|
||||||
|
VdiUuidKey: "iso_vdi_uuid",
|
||||||
|
},
|
||||||
|
new(xscommon.StepShutdownAndExport),
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
360
builder/xenserver/xva/builder_test.go
Normal file
360
builder/xenserver/xva/builder_test.go
Normal file
@ -0,0 +1,360 @@
|
|||||||
|
package xva
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mitchellh/packer/packer"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func testConfig() map[string]interface{} {
|
||||||
|
return map[string]interface{}{
|
||||||
|
"host_ip": "localhost",
|
||||||
|
"username": "admin",
|
||||||
|
"password": "admin",
|
||||||
|
"vm_name": "foo",
|
||||||
|
"iso_checksum": "foo",
|
||||||
|
"iso_checksum_type": "md5",
|
||||||
|
"iso_url": "http://www.google.com/",
|
||||||
|
"shutdown_command": "yes",
|
||||||
|
"ssh_username": "foo",
|
||||||
|
|
||||||
|
packer.BuildNameConfigKey: "foo",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuilder_ImplementsBuilder(t *testing.T) {
|
||||||
|
var raw interface{}
|
||||||
|
raw = &Builder{}
|
||||||
|
if _, ok := raw.(packer.Builder); !ok {
|
||||||
|
t.Error("Builder must implement builder.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuilderPrepare_Defaults(t *testing.T) {
|
||||||
|
var b Builder
|
||||||
|
config := testConfig()
|
||||||
|
warns, err := b.Prepare(config)
|
||||||
|
if len(warns) > 0 {
|
||||||
|
t.Fatalf("bad: %#v", warns)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("should not have error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.config.ToolsIsoName != "xs-tools.iso" {
|
||||||
|
t.Errorf("bad tools ISO name: %s", b.config.ToolsIsoName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.config.CloneTemplate != "Other install media" {
|
||||||
|
t.Errorf("bad clone template: %s", b.config.CloneTemplate)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.config.VMName == "" {
|
||||||
|
t.Errorf("bad vm name: %s", b.config.VMName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.config.ExportFormat != "xva" {
|
||||||
|
t.Errorf("bad format: %s", b.config.ExportFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.config.KeepInstance != "never" {
|
||||||
|
t.Errorf("bad keep instance: %s", b.config.KeepInstance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuilderPrepare_DiskSize(t *testing.T) {
|
||||||
|
var b Builder
|
||||||
|
config := testConfig()
|
||||||
|
|
||||||
|
delete(config, "disk_size")
|
||||||
|
warns, err := b.Prepare(config)
|
||||||
|
if len(warns) > 0 {
|
||||||
|
t.Fatalf("bad: %#v", warns)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("bad err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.config.DiskSize != 40000 {
|
||||||
|
t.Fatalf("bad size: %d", b.config.DiskSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
config["disk_size"] = 60000
|
||||||
|
b = Builder{}
|
||||||
|
warns, err = b.Prepare(config)
|
||||||
|
if len(warns) > 0 {
|
||||||
|
t.Fatalf("bad: %#v", warns)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("should not have error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.config.DiskSize != 60000 {
|
||||||
|
t.Fatalf("bad size: %s", b.config.DiskSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuilderPrepare_Format(t *testing.T) {
|
||||||
|
var b Builder
|
||||||
|
config := testConfig()
|
||||||
|
|
||||||
|
// Bad
|
||||||
|
config["export_format"] = "foo"
|
||||||
|
warns, err := b.Prepare(config)
|
||||||
|
if len(warns) > 0 {
|
||||||
|
t.Fatalf("bad: %#v", warns)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("should have error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Good
|
||||||
|
config["export_format"] = "vdi_raw"
|
||||||
|
b = Builder{}
|
||||||
|
warns, err = b.Prepare(config)
|
||||||
|
if len(warns) > 0 {
|
||||||
|
t.Fatalf("bad: %#v", warns)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("should not have error: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuilderPrepare_HTTPPort(t *testing.T) {
|
||||||
|
var b Builder
|
||||||
|
config := testConfig()
|
||||||
|
|
||||||
|
// Bad
|
||||||
|
config["http_port_min"] = 1000
|
||||||
|
config["http_port_max"] = 500
|
||||||
|
warns, err := b.Prepare(config)
|
||||||
|
if len(warns) > 0 {
|
||||||
|
t.Fatalf("bad: %#v", warns)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("should have error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bad
|
||||||
|
config["http_port_min"] = -500
|
||||||
|
b = Builder{}
|
||||||
|
warns, err = b.Prepare(config)
|
||||||
|
if len(warns) > 0 {
|
||||||
|
t.Fatalf("bad: %#v", warns)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("should have error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Good
|
||||||
|
config["http_port_min"] = 500
|
||||||
|
config["http_port_max"] = 1000
|
||||||
|
b = Builder{}
|
||||||
|
warns, err = b.Prepare(config)
|
||||||
|
if len(warns) > 0 {
|
||||||
|
t.Fatalf("bad: %#v", warns)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("should not have error: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuilderPrepare_InvalidKey(t *testing.T) {
|
||||||
|
var b Builder
|
||||||
|
config := testConfig()
|
||||||
|
|
||||||
|
// Add a random key
|
||||||
|
config["i_should_not_be_valid"] = true
|
||||||
|
warns, err := b.Prepare(config)
|
||||||
|
if len(warns) > 0 {
|
||||||
|
t.Fatalf("bad: %#v", warns)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("should have error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuilderPrepare_ISOChecksum(t *testing.T) {
|
||||||
|
var b Builder
|
||||||
|
config := testConfig()
|
||||||
|
|
||||||
|
// Test bad
|
||||||
|
config["iso_checksum"] = ""
|
||||||
|
warns, err := b.Prepare(config)
|
||||||
|
if len(warns) > 0 {
|
||||||
|
t.Fatalf("bad: %#v", warns)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("should have error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test good
|
||||||
|
config["iso_checksum"] = "FOo"
|
||||||
|
b = Builder{}
|
||||||
|
warns, err = b.Prepare(config)
|
||||||
|
if len(warns) > 0 {
|
||||||
|
t.Fatalf("bad: %#v", warns)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("should not have error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.config.ISOChecksum != "foo" {
|
||||||
|
t.Fatalf("should've lowercased: %s", b.config.ISOChecksum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuilderPrepare_ISOChecksumType(t *testing.T) {
|
||||||
|
var b Builder
|
||||||
|
config := testConfig()
|
||||||
|
|
||||||
|
// Test bad
|
||||||
|
config["iso_checksum_type"] = ""
|
||||||
|
warns, err := b.Prepare(config)
|
||||||
|
if len(warns) > 0 {
|
||||||
|
t.Fatalf("bad: %#v", warns)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("should have error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test good
|
||||||
|
config["iso_checksum_type"] = "mD5"
|
||||||
|
b = Builder{}
|
||||||
|
warns, err = b.Prepare(config)
|
||||||
|
if len(warns) > 0 {
|
||||||
|
t.Fatalf("bad: %#v", warns)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("should not have error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.config.ISOChecksumType != "md5" {
|
||||||
|
t.Fatalf("should've lowercased: %s", b.config.ISOChecksumType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test unknown
|
||||||
|
config["iso_checksum_type"] = "fake"
|
||||||
|
b = Builder{}
|
||||||
|
warns, err = b.Prepare(config)
|
||||||
|
if len(warns) > 0 {
|
||||||
|
t.Fatalf("bad: %#v", warns)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("should have error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test none
|
||||||
|
config["iso_checksum_type"] = "none"
|
||||||
|
b = Builder{}
|
||||||
|
warns, err = b.Prepare(config)
|
||||||
|
// @todo: give warning in this case?
|
||||||
|
/*
|
||||||
|
if len(warns) == 0 {
|
||||||
|
t.Fatalf("bad: %#v", warns)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("should not have error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.config.ISOChecksumType != "none" {
|
||||||
|
t.Fatalf("should've lowercased: %s", b.config.ISOChecksumType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuilderPrepare_ISOUrl(t *testing.T) {
|
||||||
|
var b Builder
|
||||||
|
config := testConfig()
|
||||||
|
delete(config, "iso_url")
|
||||||
|
delete(config, "iso_urls")
|
||||||
|
|
||||||
|
// Test both epty
|
||||||
|
config["iso_url"] = ""
|
||||||
|
b = Builder{}
|
||||||
|
warns, err := b.Prepare(config)
|
||||||
|
if len(warns) > 0 {
|
||||||
|
t.Fatalf("bad: %#v", warns)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("should have error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test iso_url set
|
||||||
|
config["iso_url"] = "http://www.packer.io"
|
||||||
|
b = Builder{}
|
||||||
|
warns, err = b.Prepare(config)
|
||||||
|
if len(warns) > 0 {
|
||||||
|
t.Fatalf("bad: %#v", warns)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("should not have error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := []string{"http://www.packer.io"}
|
||||||
|
if !reflect.DeepEqual(b.config.ISOUrls, expected) {
|
||||||
|
t.Fatalf("bad: %#v", b.config.ISOUrls)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test both set
|
||||||
|
config["iso_url"] = "http://www.packer.io"
|
||||||
|
config["iso_urls"] = []string{"http://www.packer.io"}
|
||||||
|
b = Builder{}
|
||||||
|
warns, err = b.Prepare(config)
|
||||||
|
if len(warns) > 0 {
|
||||||
|
t.Fatalf("bad: %#v", warns)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("should have error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test just iso_urls set
|
||||||
|
delete(config, "iso_url")
|
||||||
|
config["iso_urls"] = []string{
|
||||||
|
"http://www.packer.io",
|
||||||
|
"http://www.hashicorp.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
b = Builder{}
|
||||||
|
warns, err = b.Prepare(config)
|
||||||
|
if len(warns) > 0 {
|
||||||
|
t.Fatalf("bad: %#v", warns)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("should not have error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected = []string{
|
||||||
|
"http://www.packer.io",
|
||||||
|
"http://www.hashicorp.com",
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(b.config.ISOUrls, expected) {
|
||||||
|
t.Fatalf("bad: %#v", b.config.ISOUrls)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuilderPrepare_KeepInstance(t *testing.T) {
|
||||||
|
var b Builder
|
||||||
|
config := testConfig()
|
||||||
|
|
||||||
|
// Bad
|
||||||
|
config["keep_instance"] = "foo"
|
||||||
|
warns, err := b.Prepare(config)
|
||||||
|
if len(warns) > 0 {
|
||||||
|
t.Fatalf("bad: %#v", warns)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("should have error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Good
|
||||||
|
config["keep_instance"] = "always"
|
||||||
|
b = Builder{}
|
||||||
|
warns, err = b.Prepare(config)
|
||||||
|
if len(warns) > 0 {
|
||||||
|
t.Fatalf("bad: %#v", warns)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("should not have error: %s", err)
|
||||||
|
}
|
||||||
|
}
|
193
builder/xenserver/xva/step_create_instance.go
Normal file
193
builder/xenserver/xva/step_create_instance.go
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
package xva
|
||||||
|
|
||||||
|
import (
|
||||||
|
// "fmt"
|
||||||
|
|
||||||
|
"github.com/mitchellh/multistep"
|
||||||
|
// "github.com/mitchellh/packer/packer"
|
||||||
|
xscommon "github.com/rdobson/packer-builder-xenserver/builder/xenserver/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
type stepCreateInstance struct {
|
||||||
|
instance *xscommon.VM
|
||||||
|
vdi *xscommon.VDI
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
|
||||||
|
/*
|
||||||
|
client := state.Get("client").(xscommon.XenAPIClient)
|
||||||
|
config := state.Get("config").(config)
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
|
ui.Say("Step: Create Instance")
|
||||||
|
|
||||||
|
// Get the template to clone from
|
||||||
|
|
||||||
|
vms, err := client.GetVMByNameLabel(config.CloneTemplate)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case len(vms) == 0:
|
||||||
|
ui.Error(fmt.Sprintf("Couldn't find a template with the name-label '%s'. Aborting.", config.CloneTemplate))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
case len(vms) > 1:
|
||||||
|
ui.Error(fmt.Sprintf("Found more than one template with the name '%s'. The name must be unique. Aborting.", config.CloneTemplate))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
template := vms[0]
|
||||||
|
|
||||||
|
// Clone that VM template
|
||||||
|
instance, err := template.Clone(config.VMName)
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(fmt.Sprintf("Error cloning VM: %s", err.Error()))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
self.instance = instance
|
||||||
|
|
||||||
|
err = instance.SetIsATemplate(false)
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(fmt.Sprintf("Error setting is_a_template=false: %s", err.Error()))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
err = instance.SetStaticMemoryRange(config.VMMemory*1024*1024, config.VMMemory*1024*1024)
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(fmt.Sprintf("Error setting VM memory=%d: %s", config.VMMemory*1024*1024, err.Error()))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.SetPlatform(config.PlatformArgs)
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(fmt.Sprintf("Error setting VM platform: %s", err.Error()))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create VDI for the instance
|
||||||
|
|
||||||
|
sr, err := config.GetSR(client)
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(fmt.Sprintf("Unable to get SR: %s", err.Error()))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
vdi, err := sr.CreateVdi("Packer-disk", int64(config.DiskSize*1024*1024))
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(fmt.Sprintf("Unable to create packer disk VDI: %s", err.Error()))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
self.vdi = vdi
|
||||||
|
|
||||||
|
err = instance.ConnectVdi(vdi, xscommon.Disk)
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(fmt.Sprintf("Unable to connect packer disk VDI: %s", err.Error()))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect Network
|
||||||
|
|
||||||
|
var network *xscommon.Network
|
||||||
|
|
||||||
|
if config.NetworkName == "" {
|
||||||
|
// No network has be specified. Use the management interface
|
||||||
|
network = new(xscommon.Network)
|
||||||
|
network.Ref = ""
|
||||||
|
network.Client = &client
|
||||||
|
|
||||||
|
pifs, err := client.GetPIFs()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(fmt.Sprintf("Error getting PIFs: %s", err.Error()))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, pif := range pifs {
|
||||||
|
pif_rec, err := pif.GetRecord()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(fmt.Sprintf("Error getting PIF record: %s", err.Error()))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
if pif_rec["management"].(bool) {
|
||||||
|
network.Ref = pif_rec["network"].(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if network.Ref == "" {
|
||||||
|
ui.Error("Error: couldn't find management network. Aborting.")
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Look up the network by it's name label
|
||||||
|
|
||||||
|
networks, err := client.GetNetworkByNameLabel(config.NetworkName)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(fmt.Sprintf("Error occured getting Network by name-label: %s", err.Error()))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case len(networks) == 0:
|
||||||
|
ui.Error(fmt.Sprintf("Couldn't find a network with the specified name-label '%s'. Aborting.", config.NetworkName))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
case len(networks) > 1:
|
||||||
|
ui.Error(fmt.Sprintf("Found more than one network with the name '%s'. The name must be unique. Aborting.", config.NetworkName))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
network = networks[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
ui.Say(err.Error())
|
||||||
|
}
|
||||||
|
_, err = instance.ConnectNetwork(network, "0")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
ui.Say(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
instanceId, err := instance.GetUuid()
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(fmt.Sprintf("Unable to get VM UUID: %s", err.Error()))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
state.Put("instance_uuid", instanceId)
|
||||||
|
ui.Say(fmt.Sprintf("Created instance '%s'", instanceId))
|
||||||
|
*/
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *stepCreateInstance) Cleanup(state multistep.StateBag) {
|
||||||
|
/*
|
||||||
|
config := state.Get("config").(config)
|
||||||
|
if config.ShouldKeepVM(state) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
|
if self.instance != nil {
|
||||||
|
ui.Say("Destroying VM")
|
||||||
|
_ = self.instance.HardShutdown() // redundant, just in case
|
||||||
|
err := self.instance.Destroy()
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.vdi != nil {
|
||||||
|
ui.Say("Destroying VDI")
|
||||||
|
err := self.vdi.Destroy()
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
@ -2,7 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/mitchellh/packer/packer/plugin"
|
"github.com/mitchellh/packer/packer/plugin"
|
||||||
"github.com/rdobson/packer-builder-xenserver/builder/xenserver"
|
"github.com/rdobson/packer-builder-xenserver/builder/xenserver/iso"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -10,6 +10,6 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
server.RegisterBuilder(new(xenserver.Builder))
|
server.RegisterBuilder(new(iso.Builder))
|
||||||
server.Serve()
|
server.Serve()
|
||||||
}
|
}
|
15
plugin/builder-xenserver-xva/main.go
Normal file
15
plugin/builder-xenserver-xva/main.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mitchellh/packer/packer/plugin"
|
||||||
|
"github.com/rdobson/packer-builder-xenserver/builder/xenserver/xva"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
server, err := plugin.Server()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
server.RegisterBuilder(new(xva.Builder))
|
||||||
|
server.Serve()
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user