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 (
|
||||
"fmt"
|
||||
@ -7,6 +7,9 @@ import (
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// This is the common builder ID to all of these artifacts.
|
||||
const BuilderId = "packer.xenserver"
|
||||
|
||||
type LocalArtifact struct {
|
||||
dir string
|
||||
f []string
|
@ -1,4 +1,4 @@
|
||||
package xenserver
|
||||
package common
|
||||
|
||||
import (
|
||||
"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 (
|
||||
"fmt"
|
@ -1,4 +1,4 @@
|
||||
package xenserver
|
||||
package common
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/multistep"
|
@ -1,4 +1,4 @@
|
||||
package xenserver
|
||||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -13,22 +13,21 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func sshAddress(state multistep.StateBag) (string, error) {
|
||||
func SSHAddress(state multistep.StateBag) (string, error) {
|
||||
sshIP := state.Get("ssh_address").(string)
|
||||
sshHostPort := 22
|
||||
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)
|
||||
conn_str := fmt.Sprintf("%s:%d", "127.0.0.1", sshLocalPort)
|
||||
log.Printf("sshLocalAddress: %s", conn_str)
|
||||
return conn_str, nil
|
||||
}
|
||||
|
||||
func sshConfig(state multistep.StateBag) (*gossh.ClientConfig, error) {
|
||||
config := state.Get("config").(config)
|
||||
|
||||
func SSHConfig(state multistep.StateBag) (*gossh.ClientConfig, error) {
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
auth := []gossh.AuthMethod{
|
||||
gossh.Password(config.SSHPassword),
|
||||
gossh.KeyboardInteractive(
|
@ -1,4 +1,4 @@
|
||||
package xenserver
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@ -7,14 +7,14 @@ import (
|
||||
"log"
|
||||
)
|
||||
|
||||
type stepAttachVdi struct {
|
||||
type StepAttachVdi struct {
|
||||
VdiUuidKey string
|
||||
VdiType VDIType
|
||||
|
||||
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)
|
||||
client := state.Get("client").(XenAPIClient)
|
||||
|
||||
@ -51,10 +51,10 @@ func (self *stepAttachVdi) Run(state multistep.StateBag) multistep.StepAction {
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (self *stepAttachVdi) Cleanup(state multistep.StateBag) {
|
||||
config := state.Get("config").(config)
|
||||
func (self *StepAttachVdi) Cleanup(state multistep.StateBag) {
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
client := state.Get("client").(XenAPIClient)
|
||||
if config.ShouldKeepInstance(state) {
|
||||
if config.ShouldKeepVM(state) {
|
||||
return
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package xenserver
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@ -6,11 +6,11 @@ import (
|
||||
"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)
|
||||
config := state.Get("config").(config)
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
instance, _ := client.GetVMByUuid(state.Get("instance_uuid").(string))
|
||||
@ -28,4 +28,4 @@ func (self *stepBootWait) Run(state multistep.StateBag) multistep.StepAction {
|
||||
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 (
|
||||
"fmt"
|
||||
@ -7,11 +7,11 @@ import (
|
||||
"log"
|
||||
)
|
||||
|
||||
type stepDetachVdi struct {
|
||||
type StepDetachVdi struct {
|
||||
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)
|
||||
client := state.Get("client").(XenAPIClient)
|
||||
|
||||
@ -47,4 +47,4 @@ func (self *stepDetachVdi) Run(state multistep.StateBag) multistep.StepAction {
|
||||
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 (
|
||||
"fmt"
|
||||
@ -6,13 +6,13 @@ import (
|
||||
"github.com/mitchellh/packer/packer"
|
||||
)
|
||||
|
||||
type stepFindVdi struct {
|
||||
type StepFindVdi struct {
|
||||
VdiName string
|
||||
ImagePathFunc func() 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)
|
||||
client := state.Get("client").(XenAPIClient)
|
||||
|
||||
@ -39,4 +39,4 @@ func (self *stepFindVdi) Run(state multistep.StateBag) multistep.StepAction {
|
||||
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 (
|
||||
"fmt"
|
||||
@ -6,7 +6,7 @@ import (
|
||||
"github.com/mitchellh/packer/packer"
|
||||
)
|
||||
|
||||
type stepForwardPortOverSSH struct {
|
||||
type StepForwardPortOverSSH struct {
|
||||
RemotePort func(state multistep.StateBag) (uint, error)
|
||||
RemoteDest func(state multistep.StateBag) (string, error)
|
||||
|
||||
@ -16,9 +16,9 @@ type stepForwardPortOverSSH struct {
|
||||
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)
|
||||
|
||||
// Find a free local port:
|
||||
@ -46,4 +46,4 @@ func (self *stepForwardPortOverSSH) Run(state multistep.StateBag) multistep.Step
|
||||
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 (
|
||||
"fmt"
|
||||
@ -7,11 +7,11 @@ import (
|
||||
"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.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
|
||||
}
|
||||
|
||||
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)
|
||||
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
|
||||
return "127.0.0.1", nil
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package xenserver
|
||||
package common
|
||||
|
||||
// Taken from mitchellh/packer/builder/qemu/step_http_server.go
|
||||
|
||||
@ -20,12 +20,12 @@ import (
|
||||
//
|
||||
// Produces:
|
||||
// http_port int - The port the HTTP server started on.
|
||||
type stepHTTPServer struct {
|
||||
type StepHTTPServer struct {
|
||||
l net.Listener
|
||||
}
|
||||
|
||||
func (s *stepHTTPServer) Run(state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(config)
|
||||
func (s *StepHTTPServer) Run(state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
var httpPort uint = 0
|
||||
@ -54,7 +54,7 @@ func (s *stepHTTPServer) Run(state multistep.StateBag) multistep.StepAction {
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepHTTPServer) Cleanup(multistep.StateBag) {
|
||||
func (s *StepHTTPServer) Cleanup(multistep.StateBag) {
|
||||
if s.l != nil {
|
||||
// Close the listener so that the HTTP server stops
|
||||
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 */
|
||||
|
||||
@ -10,18 +10,20 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type stepPrepareOutputDir struct{}
|
||||
type StepPrepareOutputDir struct {
|
||||
Force bool
|
||||
Path string
|
||||
}
|
||||
|
||||
func (stepPrepareOutputDir) Run(state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(config)
|
||||
func (self *StepPrepareOutputDir) Run(state multistep.StateBag) multistep.StepAction {
|
||||
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...")
|
||||
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)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
@ -29,17 +31,16 @@ func (stepPrepareOutputDir) Run(state multistep.StateBag) multistep.StepAction {
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (stepPrepareOutputDir) Cleanup(state multistep.StateBag) {
|
||||
func (self *StepPrepareOutputDir) Cleanup(state multistep.StateBag) {
|
||||
_, cancelled := state.GetOk(multistep.StateCancelled)
|
||||
_, halted := state.GetOk(multistep.StateHalted)
|
||||
|
||||
if cancelled || halted {
|
||||
config := state.Get("config").(config)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
ui.Say("Deleting output directory...")
|
||||
for i := 0; i < 5; i++ {
|
||||
err := os.RemoveAll(config.OutputDir)
|
||||
err := os.RemoveAll(self.Path)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package xenserver
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@ -6,9 +6,9 @@ import (
|
||||
"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)
|
||||
client := state.Get("client").(XenAPIClient)
|
||||
|
||||
@ -37,4 +37,4 @@ func (self *stepRemoveDevices) Run(state multistep.StateBag) multistep.StepActio
|
||||
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 */
|
||||
|
||||
@ -13,7 +13,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type stepShutdownAndExport struct{}
|
||||
type StepShutdownAndExport struct{}
|
||||
|
||||
func downloadFile(url, filename string) (err error) {
|
||||
|
||||
@ -44,8 +44,8 @@ func downloadFile(url, filename string) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (stepShutdownAndExport) Run(state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(config)
|
||||
func (StepShutdownAndExport) Run(state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
client := state.Get("client").(XenAPIClient)
|
||||
instance_uuid := state.Get("instance_uuid").(string)
|
||||
@ -56,7 +56,7 @@ func (stepShutdownAndExport) Run(state multistep.StateBag) multistep.StepAction
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say("Step: Shutdown and export VPX")
|
||||
ui.Say("Step: Shutdown and export")
|
||||
|
||||
// Shutdown the VM
|
||||
success := func() bool {
|
||||
@ -109,7 +109,7 @@ func (stepShutdownAndExport) Run(state multistep.StateBag) multistep.StepAction
|
||||
|
||||
ui.Say("Successfully shut down VM")
|
||||
|
||||
switch config.ExportFormat {
|
||||
switch config.Format {
|
||||
case "xva":
|
||||
// export the VM
|
||||
|
||||
@ -164,7 +164,7 @@ func (stepShutdownAndExport) Run(state multistep.StateBag) multistep.StepAction
|
||||
}
|
||||
|
||||
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)
|
||||
@ -172,5 +172,4 @@ func (stepShutdownAndExport) Run(state multistep.StateBag) multistep.StepAction
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (stepShutdownAndExport) Cleanup(state multistep.StateBag) {
|
||||
}
|
||||
func (StepShutdownAndExport) Cleanup(state multistep.StateBag) {}
|
@ -1,4 +1,4 @@
|
||||
package xenserver
|
||||
package common
|
||||
|
||||
import (
|
||||
gossh "code.google.com/p/go.crypto/ssh"
|
||||
@ -9,7 +9,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type stepStartOnHIMN struct{}
|
||||
type StepStartOnHIMN struct{}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
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")
|
||||
|
||||
@ -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)
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
func himnSSHPort(state multistep.StateBag) (uint, error) {
|
||||
func HimnSSHPort(state multistep.StateBag) (uint, error) {
|
||||
return 22, nil
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package xenserver
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@ -7,9 +7,9 @@ import (
|
||||
"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)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
@ -39,11 +39,11 @@ func (self *stepStartVmPaused) Run(state multistep.StateBag) multistep.StepActio
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (self *stepStartVmPaused) Cleanup(state multistep.StateBag) {
|
||||
config := state.Get("config").(config)
|
||||
func (self *StepStartVmPaused) Cleanup(state multistep.StateBag) {
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
client := state.Get("client").(XenAPIClient)
|
||||
|
||||
if config.ShouldKeepInstance(state) {
|
||||
if config.ShouldKeepVM(state) {
|
||||
return
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package xenserver
|
||||
package common
|
||||
|
||||
/* Heavily borrowed from builder/quemu/step_type_boot_command.go */
|
||||
|
||||
@ -23,10 +23,12 @@ type bootCommandTemplateData struct {
|
||||
HTTPPort uint
|
||||
}
|
||||
|
||||
type stepTypeBootCommand struct{}
|
||||
type StepTypeBootCommand struct {
|
||||
Tpl *packer.ConfigTemplate
|
||||
}
|
||||
|
||||
func (self *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(config)
|
||||
func (self *StepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
vnc_port := state.Get("local_vnc_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...")
|
||||
for _, command := range config.BootCommand {
|
||||
|
||||
command, err := config.tpl.Process(command, tplData)
|
||||
command, err := self.Tpl.Process(command, tplData)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error preparing boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
@ -89,7 +91,7 @@ func (self *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAct
|
||||
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.
|
||||
func vncSendString(c *vnc.ClientConn, original string) {
|
@ -1,4 +1,4 @@
|
||||
package xenserver
|
||||
package common
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
@ -11,14 +11,14 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type stepUploadVdi struct {
|
||||
type StepUploadVdi struct {
|
||||
VdiName string
|
||||
ImagePathFunc func() string
|
||||
VdiUuidKey string
|
||||
}
|
||||
|
||||
func (self *stepUploadVdi) Run(state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(config)
|
||||
func (self *StepUploadVdi) Run(state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
client := state.Get("client").(XenAPIClient)
|
||||
|
||||
@ -154,12 +154,12 @@ func (self *stepUploadVdi) Run(state multistep.StateBag) multistep.StepAction {
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (self *stepUploadVdi) Cleanup(state multistep.StateBag) {
|
||||
config := state.Get("config").(config)
|
||||
func (self *StepUploadVdi) Cleanup(state multistep.StateBag) {
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
client := state.Get("client").(XenAPIClient)
|
||||
|
||||
if config.ShouldKeepInstance(state) {
|
||||
if config.ShouldKeepVM(state) {
|
||||
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 (
|
||||
"github.com/mitchellh/packer/packer"
|
@ -1,19 +1,21 @@
|
||||
package xenserver
|
||||
package iso
|
||||
|
||||
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 *VM
|
||||
vdi *VDI
|
||||
instance *xscommon.VM
|
||||
vdi *xscommon.VDI
|
||||
}
|
||||
|
||||
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)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
@ -75,7 +77,7 @@ func (self *stepCreateInstance) Run(state multistep.StateBag) multistep.StepActi
|
||||
}
|
||||
self.vdi = vdi
|
||||
|
||||
err = instance.ConnectVdi(vdi, Disk)
|
||||
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
|
||||
@ -83,11 +85,11 @@ func (self *stepCreateInstance) Run(state multistep.StateBag) multistep.StepActi
|
||||
|
||||
// Connect Network
|
||||
|
||||
var network *Network
|
||||
var network *xscommon.Network
|
||||
|
||||
if config.NetworkName == "" {
|
||||
// No network has be specified. Use the management interface
|
||||
network = new(Network)
|
||||
network = new(xscommon.Network)
|
||||
network.Ref = ""
|
||||
network.Client = &client
|
||||
|
||||
@ -162,7 +164,7 @@ func (self *stepCreateInstance) Run(state multistep.StateBag) multistep.StepActi
|
||||
|
||||
func (self *stepCreateInstance) Cleanup(state multistep.StateBag) {
|
||||
config := state.Get("config").(config)
|
||||
if config.ShouldKeepInstance(state) {
|
||||
if config.ShouldKeepVM(state) {
|
||||
return
|
||||
}
|
||||
|
@ -1,11 +1,13 @@
|
||||
package xenserver
|
||||
package iso
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
xscommon "github.com/rdobson/packer-builder-xenserver/builder/xenserver/common"
|
||||
)
|
||||
|
||||
type stepWait struct{}
|
||||
@ -13,7 +15,7 @@ type stepWait struct{}
|
||||
func (self *stepWait) Run(state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(config)
|
||||
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.")
|
||||
|
||||
@ -26,7 +28,7 @@ func (self *stepWait) Run(state multistep.StateBag) multistep.StepAction {
|
||||
}
|
||||
|
||||
//Expect install to be configured to shutdown on completion
|
||||
err = InterruptibleWait{
|
||||
err = xscommon.InterruptibleWait{
|
||||
Predicate: func() (bool, error) {
|
||||
log.Printf("Waiting for install to complete.")
|
||||
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 (
|
||||
"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() {
|
||||
@ -10,6 +10,6 @@ func main() {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
server.RegisterBuilder(new(xenserver.Builder))
|
||||
server.RegisterBuilder(new(iso.Builder))
|
||||
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