From f101aa85115881bfc0169fae8048965df3b95193 Mon Sep 17 00:00:00 2001 From: Cheng Sun Date: Wed, 17 Dec 2014 16:11:22 +0000 Subject: [PATCH 1/9] step_start_vm_paused: cleanup forces VM shutdown This allows steps between create_instance and start_vm_paused to assume that the VM is shut down when cleaning up --- builder/xenserver/step_create_instance.go | 2 +- builder/xenserver/step_start_vm_paused.go | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/builder/xenserver/step_create_instance.go b/builder/xenserver/step_create_instance.go index fcb800c..9a9cc52 100644 --- a/builder/xenserver/step_create_instance.go +++ b/builder/xenserver/step_create_instance.go @@ -196,7 +196,7 @@ func (self *stepCreateInstance) Cleanup(state multistep.StateBag) { if self.instance != nil { ui.Say("Destroying VM") - _ = self.instance.HardShutdown() + _ = self.instance.HardShutdown() // redundant, just in case err := self.instance.Destroy() if err != nil { ui.Error(err.Error()) diff --git a/builder/xenserver/step_start_vm_paused.go b/builder/xenserver/step_start_vm_paused.go index 8a0e246..31b15d5 100644 --- a/builder/xenserver/step_start_vm_paused.go +++ b/builder/xenserver/step_start_vm_paused.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" + "log" ) type stepStartVmPaused struct{} @@ -39,4 +40,22 @@ func (self *stepStartVmPaused) Run(state multistep.StateBag) multistep.StepActio } func (self *stepStartVmPaused) Cleanup(state multistep.StateBag) { + config := state.Get("config").(config) + client := state.Get("client").(XenAPIClient) + + if config.ShouldKeepInstance(state) { + return + } + + uuid := state.Get("instance_uuid").(string) + instance, err := client.GetVMByUuid(uuid) + if err != nil { + log.Printf(fmt.Sprintf("Unable to get VM from UUID '%s': %s", uuid, err.Error())) + return + } + + err = instance.HardShutdown() + if err != nil { + log.Printf(fmt.Sprintf("Unable to force shutdown VM '%s': %s", uuid, err.Error())) + } } From 31acbe65089713451251161abd48daeea0092ed3 Mon Sep 17 00:00:00 2001 From: Cheng Sun Date: Wed, 17 Dec 2014 17:59:26 +0000 Subject: [PATCH 2/9] Fix error message typo --- builder/xenserver/step_create_instance.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/xenserver/step_create_instance.go b/builder/xenserver/step_create_instance.go index 9a9cc52..3d4a50d 100644 --- a/builder/xenserver/step_create_instance.go +++ b/builder/xenserver/step_create_instance.go @@ -132,7 +132,7 @@ func (self *stepCreateInstance) Run(state multistep.StateBag) multistep.StepActi 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 SR with the name '%s'. The name must be unique. Aborting.", config.NetworkName)) + ui.Error(fmt.Sprintf("Found more than one network with the name '%s'. The name must be unique. Aborting.", config.NetworkName)) return multistep.ActionHalt } From 5cb0a0955bbd5923889433d881cb380664dc093f Mon Sep 17 00:00:00 2001 From: Cheng Sun Date: Mon, 15 Dec 2014 15:27:22 +0000 Subject: [PATCH 3/9] Add Floppy ConnectVdi type --- builder/xenserver/client.go | 24 +++++++++++++++++++---- builder/xenserver/step_create_instance.go | 4 ++-- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/builder/xenserver/client.go b/builder/xenserver/client.go index 96dea6f..7107a7b 100644 --- a/builder/xenserver/client.go +++ b/builder/xenserver/client.go @@ -36,6 +36,15 @@ type VDI struct { Client *XenAPIClient } +type VDIType int + +const ( + _ = iota + Disk + CD + Floppy +) + type Network struct { Ref string Client *XenAPIClient @@ -506,7 +515,7 @@ func (self *VM) SetStaticMemoryRange(min, max string) (err error) { return } -func (self *VM) ConnectVdi(vdi *VDI, iso bool) (err error) { +func (self *VM) ConnectVdi(vdi *VDI, vdiType VDIType) (err error) { // 1. Create a VBD @@ -514,20 +523,27 @@ func (self *VM) ConnectVdi(vdi *VDI, iso bool) (err error) { vbd_rec["VM"] = self.Ref vbd_rec["VDI"] = vdi.Ref vbd_rec["userdevice"] = "autodetect" - vbd_rec["unpluggable"] = false vbd_rec["empty"] = false vbd_rec["other_config"] = make(xmlrpc.Struct) vbd_rec["qos_algorithm_type"] = "" vbd_rec["qos_algorithm_params"] = make(xmlrpc.Struct) - if iso { + switch vdiType { + case CD: vbd_rec["mode"] = "RO" vbd_rec["bootable"] = true + vbd_rec["unpluggable"] = false vbd_rec["type"] = "CD" - } else { + case Disk: vbd_rec["mode"] = "RW" vbd_rec["bootable"] = false + vbd_rec["unpluggable"] = false vbd_rec["type"] = "Disk" + case Floppy: + vbd_rec["mode"] = "RW" + vbd_rec["bootable"] = false + vbd_rec["unpluggable"] = true + vbd_rec["type"] = "Floppy" } result := APIResult{} diff --git a/builder/xenserver/step_create_instance.go b/builder/xenserver/step_create_instance.go index 3d4a50d..119665d 100644 --- a/builder/xenserver/step_create_instance.go +++ b/builder/xenserver/step_create_instance.go @@ -75,7 +75,7 @@ func (self *stepCreateInstance) Run(state multistep.StateBag) multistep.StepActi } self.vdi = vdi - err = instance.ConnectVdi(vdi, false) + err = instance.ConnectVdi(vdi, Disk) if err != nil { ui.Error(fmt.Sprintf("Unable to connect packer disk VDI: %s", err.Error())) return multistep.ActionHalt @@ -168,7 +168,7 @@ func (self *stepCreateInstance) Run(state multistep.StateBag) multistep.StepActi //ui.Say("Using VDI: " + iso_vdi_uuid) //iso, _ := client.GetVdiByUuid(iso_vdi_uuid) - err = instance.ConnectVdi(iso, true) + err = instance.ConnectVdi(iso, CD) if err != nil { ui.Error(fmt.Sprintf("Unable to connect ISO VDI: %s", err.Error())) return multistep.ActionHalt From 313f5a735475d7a49a71c9f722ff37d9152c1a0d Mon Sep 17 00:00:00 2001 From: Cheng Sun Date: Thu, 18 Dec 2014 16:04:44 +0000 Subject: [PATCH 4/9] Add Task class to xapi client --- builder/xenserver/client.go | 82 +++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/builder/xenserver/client.go b/builder/xenserver/client.go index 7107a7b..d2380b2 100644 --- a/builder/xenserver/client.go +++ b/builder/xenserver/client.go @@ -70,6 +70,22 @@ type Pool struct { Client *XenAPIClient } +type Task struct { + Ref string + Client *XenAPIClient +} + +type TaskStatusType int + +const ( + _ = iota + Pending + Success + Failure + Cancelling + Cancelled +) + func (c *XenAPIClient) RPCCall(result interface{}, method string, params []interface{}) (err error) { fmt.Println(params) p := new(xmlrpc.Params) @@ -314,6 +330,20 @@ func (client *XenAPIClient) GetPIFs() (pifs []*PIF, err error) { return pifs, nil } +func (client *XenAPIClient) CreateTask() (task *Task, err error) { + result := APIResult{} + err = client.APICall(&result, "task.create", "packer-task", "Packer task") + + if err != nil { + return + } + + task = new(Task) + task.Ref = result.Value.(string) + task.Client = client + return +} + // VM associated functions func (self *VM) Clone(label string) (new_instance *VM, err error) { @@ -768,6 +798,58 @@ func (self *VDI) Destroy() (err error) { return } +// Task associated functions + +func (self *Task) GetStatus() (status TaskStatusType, err error) { + result := APIResult{} + err = self.Client.APICall(&result, "task.get_status", self.Ref) + if err != nil { + return + } + rawStatus := result.Value.(string) + switch rawStatus { + case "pending": + status = Pending + case "success": + status = Success + case "failure": + status = Failure + case "cancelling": + status = Cancelling + case "cancelled": + status = Cancelled + default: + panic(fmt.Sprintf("Task.get_status: Unknown status '%s'", rawStatus)) + } + return +} + +func (self *Task) GetProgress() (progress float64, err error) { + result := APIResult{} + err = self.Client.APICall(&result, "task.get_progress", self.Ref) + if err != nil { + return + } + progress = result.Value.(float64) + return +} + +func (self *Task) GetErrorInfo() (errorInfo string, err error) { + result := APIResult{} + err = self.Client.APICall(&result, "task.get_error_info", self.Ref) + if err != nil { + return + } + errorInfo = result.Value.(string) + return +} + +func (self *Task) Destroy() (err error) { + result := APIResult{} + err = self.Client.APICall(&result, "task.destroy", self.Ref) + return +} + // Client Initiator func NewXenAPIClient(host, username, password string) (client XenAPIClient) { From e308c0759f2f63516da9ada49e28eacf0533c052 Mon Sep 17 00:00:00 2001 From: Cheng Sun Date: Wed, 17 Dec 2014 18:45:25 +0000 Subject: [PATCH 5/9] VDI upload, floppy and tools support This commit add new VDI steps named step_{attach,detach,find,upload}_vdi - attach/detach are self-explanatory; can specify CD or Floppy - find finds a VDI by name-label and stores the uuid - upload uploads a VDI from a local disk image and stores the uuid In order to demonstrate the new VDI code, this commit trivially adds: - support for uploading ISOs again (dependent on unmerged XAPI patch) - initial floppy upload/attach support (dependent on currently-broken XAPI patch) - initial XenServer Tools support --- builder/xenserver/builder.go | 180 ++++++++++++------- builder/xenserver/client.go | 73 +++++++- builder/xenserver/step_attach_vdi.go | 80 +++++++++ builder/xenserver/step_create_instance.go | 26 --- builder/xenserver/step_detach_vdi.go | 50 ++++++ builder/xenserver/step_find_vdi.go | 42 +++++ builder/xenserver/step_upload_iso.go | 78 --------- builder/xenserver/step_upload_vdi.go | 201 ++++++++++++++++++++++ 8 files changed, 559 insertions(+), 171 deletions(-) create mode 100644 builder/xenserver/step_attach_vdi.go create mode 100644 builder/xenserver/step_detach_vdi.go create mode 100644 builder/xenserver/step_find_vdi.go delete mode 100644 builder/xenserver/step_upload_iso.go create mode 100644 builder/xenserver/step_upload_vdi.go diff --git a/builder/xenserver/builder.go b/builder/xenserver/builder.go index 0b51192..c3ba993 100644 --- a/builder/xenserver/builder.go +++ b/builder/xenserver/builder.go @@ -9,6 +9,8 @@ import ( "github.com/mitchellh/packer/packer" "log" "os" + "path" + "strings" "time" ) @@ -21,15 +23,14 @@ type config struct { Username string `mapstructure:"username"` Password string `mapstructure:"password"` HostIp string `mapstructure:"host_ip"` - IsoUrl string `mapstructure:"iso_url"` - InstanceName string `mapstructure:"instance_name"` - InstanceMemory string `mapstructure:"instance_memory"` - RootDiskSize string `mapstructure:"root_disk_size"` - CloneTemplate string `mapstructure:"clone_template"` - IsoName string `mapstructure:"iso_name"` - SrName string `mapstructure:"sr_name"` - NetworkName string `mapstructure:"network_name"` + InstanceName string `mapstructure:"instance_name"` + InstanceMemory string `mapstructure:"instance_memory"` + RootDiskSize string `mapstructure:"root_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"` @@ -45,6 +46,8 @@ type config struct { 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"` @@ -111,6 +114,10 @@ func (self *Builder) Prepare(raws ...interface{}) (params []string, retErr error self.config.RawInstallTimeout = "200m" } + if self.config.ToolsIsoName == "" { + self.config.ToolsIsoName = "xs-tools.iso" + } + if self.config.HTTPPortMin == 0 { self.config.HTTPPortMin = 8000 } @@ -131,6 +138,10 @@ func (self *Builder) Prepare(raws ...interface{}) (params []string, retErr error 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) } @@ -160,18 +171,18 @@ func (self *Builder) Prepare(raws ...interface{}) (params []string, retErr error "username": &self.config.Username, "password": &self.config.Password, "host_ip": &self.config.HostIp, - "iso_url": &self.config.IsoUrl, "instance_name": &self.config.InstanceName, "instance_memory": &self.config.InstanceMemory, "root_disk_size": &self.config.RootDiskSize, "clone_template": &self.config.CloneTemplate, - "iso_name": &self.config.IsoName, "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, @@ -184,6 +195,13 @@ func (self *Builder) Prepare(raws ...interface{}) (params []string, retErr error "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) @@ -194,18 +212,6 @@ func (self *Builder) Prepare(raws ...interface{}) (params []string, retErr error // Validation - /* - if self.config.IsoUrl == "" { - errs = packer.MultiErrorAppend( - errs, errors.New("a iso url must be specified")) - } - */ - - if self.config.IsoName == "" { - errs = packer.MultiErrorAppend( - errs, errors.New("an iso_name must be specified")) - } - self.config.BootWait, err = time.ParseDuration(self.config.RawBootWait) if err != nil { errs = packer.MultiErrorAppend( @@ -301,43 +307,48 @@ func (self *Builder) Prepare(raws ...interface{}) (params []string, retErr error 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 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 self.config.ISOUrl == "" { - errs = packer.MultiErrorAppend( - errs, errors.New("A ISO URL must be specfied.")) - } else { - self.config.ISOUrls = []string{self.config.ISOUrl} - } + } + } + + 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)) + } + } - 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 the iso_url (%d): %s", i, err)) - } - } - */ if len(errs.Errors) > 0 { retErr = errors.New(errs.Error()) } @@ -368,19 +379,52 @@ func (self *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (pa //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, - }, - */ + &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), - //new(stepUploadIso), + &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{ @@ -393,6 +437,12 @@ func (self *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (pa new(stepBootWait), new(stepTypeBootCommand), new(stepWait), + &stepDetachVdi{ + VdiUuidKey: "floppy_vdi_uuid", + }, + &stepDetachVdi{ + VdiUuidKey: "iso_vdi_uuid", + }, new(stepRemoveDevices), new(stepStartOnHIMN), &stepForwardPortOverSSH{ diff --git a/builder/xenserver/client.go b/builder/xenserver/client.go index d2380b2..dcf8a1a 100644 --- a/builder/xenserver/client.go +++ b/builder/xenserver/client.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "github.com/nilshell/xmlrpc" + "log" ) type XenAPIClient struct { @@ -603,6 +604,36 @@ func (self *VM) ConnectVdi(vdi *VDI, vdiType VDIType) (err error) { return } +func (self *VM) DisconnectVdi(vdi *VDI) error { + vbds, err := self.GetVBDs() + if err != nil { + return fmt.Errorf("Unable to get VM VBDs: %s", err.Error()) + } + + for _, vbd := range vbds { + rec, err := vbd.GetRecord() + if err != nil { + return fmt.Errorf("Could not get record for VBD '%s': %s", vbd.Ref, err.Error()) + } + + if recVdi, ok := rec["VDI"].(string); ok { + if recVdi == vdi.Ref { + _ = vbd.Unplug() + err = vbd.Destroy() + if err != nil { + return fmt.Errorf("Could not destroy VBD '%s': %s", vbd.Ref, err.Error()) + } + + return nil + } + } else { + log.Printf("Could not find VDI record in VBD '%s'", vbd.Ref) + } + } + + return fmt.Errorf("Could not find VBD for VDI '%s'", vdi.Ref) +} + func (self *VM) SetPlatform(params map[string]string) (err error) { result := APIResult{} platform_rec := make(xmlrpc.Struct) @@ -766,6 +797,24 @@ func (self *VBD) Eject() (err error) { return nil } +func (self *VBD) Unplug() (err error) { + result := APIResult{} + err = self.Client.APICall(&result, "VBD.unplug", self.Ref) + if err != nil { + return err + } + return nil +} + +func (self *VBD) Destroy() (err error) { + result := APIResult{} + err = self.Client.APICall(&result, "VBD.destroy", self.Ref) + if err != nil { + return err + } + return nil +} + // VIF associated functions func (self *VIF) Destroy() (err error) { @@ -789,6 +838,23 @@ func (self *VDI) GetUuid() (vdi_uuid string, err error) { return vdi_uuid, nil } +func (self *VDI) GetVBDs() (vbds []VBD, err error) { + vbds = make([]VBD, 0) + result := APIResult{} + err = self.Client.APICall(&result, "VDI.get_VBDs", self.Ref) + if err != nil { + return vbds, err + } + for _, elem := range result.Value.([]interface{}) { + vbd := VBD{} + vbd.Ref = elem.(string) + vbd.Client = self.Client + vbds = append(vbds, vbd) + } + + return vbds, nil +} + func (self *VDI) Destroy() (err error) { result := APIResult{} err = self.Client.APICall(&result, "VDI.destroy", self.Ref) @@ -834,13 +900,16 @@ func (self *Task) GetProgress() (progress float64, err error) { return } -func (self *Task) GetErrorInfo() (errorInfo string, err error) { +func (self *Task) GetErrorInfo() (errorInfo []string, err error) { result := APIResult{} err = self.Client.APICall(&result, "task.get_error_info", self.Ref) if err != nil { return } - errorInfo = result.Value.(string) + errorInfo = make([]string, 0) + for _, infoRaw := range result.Value.([]interface{}) { + errorInfo = append(errorInfo, infoRaw.(string)) + } return } diff --git a/builder/xenserver/step_attach_vdi.go b/builder/xenserver/step_attach_vdi.go new file mode 100644 index 0000000..a43e8a7 --- /dev/null +++ b/builder/xenserver/step_attach_vdi.go @@ -0,0 +1,80 @@ +package xenserver + +import ( + "fmt" + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/packer" + "log" +) + +type stepAttachVdi struct { + VdiUuidKey string + VdiType VDIType + + vdi *VDI +} + +func (self *stepAttachVdi) Run(state multistep.StateBag) multistep.StepAction { + ui := state.Get("ui").(packer.Ui) + client := state.Get("client").(XenAPIClient) + + var vdiUuid string + if vdiUuidRaw, ok := state.GetOk(self.VdiUuidKey); ok { + vdiUuid = vdiUuidRaw.(string) + } else { + log.Printf("Skipping attach of '%s'", self.VdiUuidKey) + return multistep.ActionContinue + } + + var err error + self.vdi, err = client.GetVdiByUuid(vdiUuid) + if err != nil { + ui.Error(fmt.Sprintf("Unable to get VDI from UUID '%s': %s", vdiUuid, err.Error())) + return multistep.ActionHalt + } + + uuid := state.Get("instance_uuid").(string) + instance, err := client.GetVMByUuid(uuid) + if err != nil { + ui.Error(fmt.Sprintf("Unable to get VM from UUID '%s': %s", uuid, err.Error())) + return multistep.ActionHalt + } + + err = instance.ConnectVdi(self.vdi, self.VdiType) + if err != nil { + ui.Error(fmt.Sprintf("Error attaching VDI '%s': '%s'", vdiUuid, err.Error())) + return multistep.ActionHalt + } + + log.Printf("Attached VDI '%s'", vdiUuid) + + return multistep.ActionContinue +} + +func (self *stepAttachVdi) Cleanup(state multistep.StateBag) { + config := state.Get("config").(config) + client := state.Get("client").(XenAPIClient) + if config.ShouldKeepInstance(state) { + return + } + + if self.vdi == nil { + return + } + + uuid := state.Get("instance_uuid").(string) + instance, err := client.GetVMByUuid(uuid) + if err != nil { + log.Printf("Unable to get VM from UUID '%s': %s", uuid, err.Error()) + return + } + + vdiUuid := state.Get(self.VdiUuidKey).(string) + + err = instance.DisconnectVdi(self.vdi) + if err != nil { + log.Printf("Unable to disconnect VDI '%s': %s", vdiUuid, err.Error()) + return + } + log.Printf("Detached VDI '%s'", vdiUuid) +} diff --git a/builder/xenserver/step_create_instance.go b/builder/xenserver/step_create_instance.go index 119665d..e39e7cb 100644 --- a/builder/xenserver/step_create_instance.go +++ b/builder/xenserver/step_create_instance.go @@ -148,32 +148,6 @@ func (self *stepCreateInstance) Run(state multistep.StateBag) multistep.StepActi ui.Say(err.Error()) } - // Connect the ISO - //iso_vdi_uuid := state.Get("iso_vdi_uuid").(string) - - isos, err := client.GetVdiByNameLabel(config.IsoName) - - switch { - case len(isos) == 0: - ui.Error(fmt.Sprintf("Couldn't find an ISO named '%s'. Aborting", config.IsoName)) - return multistep.ActionHalt - case len(isos) > 1: - ui.Error(fmt.Sprintf("Found more than one VDI with name '%s'. Name must be unique. Aborting.", config.IsoName)) - return multistep.ActionHalt - } - - iso := isos[0] - - //iso, _ := client.GetVdiByUuid(config.IsoUuid) - //ui.Say("Using VDI: " + iso_vdi_uuid) - //iso, _ := client.GetVdiByUuid(iso_vdi_uuid) - - err = instance.ConnectVdi(iso, CD) - if err != nil { - ui.Error(fmt.Sprintf("Unable to connect ISO VDI: %s", err.Error())) - return multistep.ActionHalt - } - instanceId, err := instance.GetUuid() if err != nil { ui.Error(fmt.Sprintf("Unable to get VM UUID: %s", err.Error())) diff --git a/builder/xenserver/step_detach_vdi.go b/builder/xenserver/step_detach_vdi.go new file mode 100644 index 0000000..63f9477 --- /dev/null +++ b/builder/xenserver/step_detach_vdi.go @@ -0,0 +1,50 @@ +package xenserver + +import ( + "fmt" + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/packer" + "log" +) + +type stepDetachVdi struct { + VdiUuidKey string +} + +func (self *stepDetachVdi) Run(state multistep.StateBag) multistep.StepAction { + ui := state.Get("ui").(packer.Ui) + client := state.Get("client").(XenAPIClient) + + var vdiUuid string + if vdiUuidRaw, ok := state.GetOk(self.VdiUuidKey); ok { + vdiUuid = vdiUuidRaw.(string) + } else { + log.Printf("Skipping detach of '%s'", self.VdiUuidKey) + return multistep.ActionContinue + } + + vdi, err := client.GetVdiByUuid(vdiUuid) + if err != nil { + ui.Error(fmt.Sprintf("Unable to get VDI from UUID '%s': %s", vdiUuid, err.Error())) + return multistep.ActionHalt + } + + uuid := state.Get("instance_uuid").(string) + instance, err := client.GetVMByUuid(uuid) + if err != nil { + ui.Error(fmt.Sprintf("Unable to get VM from UUID '%s': %s", uuid, err.Error())) + return multistep.ActionHalt + } + + err = instance.DisconnectVdi(vdi) + if err != nil { + ui.Error(fmt.Sprintf("Unable to detach VDI '%s': %s", vdiUuid, err.Error())) + return multistep.ActionHalt + } + + log.Printf("Detached VDI '%s'", vdiUuid) + + return multistep.ActionContinue +} + +func (self *stepDetachVdi) Cleanup(state multistep.StateBag) {} diff --git a/builder/xenserver/step_find_vdi.go b/builder/xenserver/step_find_vdi.go new file mode 100644 index 0000000..7799887 --- /dev/null +++ b/builder/xenserver/step_find_vdi.go @@ -0,0 +1,42 @@ +package xenserver + +import ( + "fmt" + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/packer" +) + +type stepFindVdi struct { + VdiName string + ImagePathFunc func() string + VdiUuidKey string +} + +func (self *stepFindVdi) Run(state multistep.StateBag) multistep.StepAction { + ui := state.Get("ui").(packer.Ui) + client := state.Get("client").(XenAPIClient) + + vdis, err := client.GetVdiByNameLabel(self.VdiName) + + switch { + case len(vdis) == 0: + ui.Error(fmt.Sprintf("Couldn't find a VDI named '%s'", self.VdiName)) + return multistep.ActionHalt + case len(vdis) > 1: + ui.Error(fmt.Sprintf("Found more than one VDI with name '%s'. Name must be unique", self.VdiName)) + return multistep.ActionHalt + } + + vdi := vdis[0] + + vdiUuid, err := vdi.GetUuid() + if err != nil { + ui.Error(fmt.Sprintf("Unable to get UUID of VDI '%s': %s", self.VdiName, err.Error())) + return multistep.ActionHalt + } + state.Put(self.VdiUuidKey, vdiUuid) + + return multistep.ActionContinue +} + +func (self *stepFindVdi) Cleanup(state multistep.StateBag) {} diff --git a/builder/xenserver/step_upload_iso.go b/builder/xenserver/step_upload_iso.go deleted file mode 100644 index b4dcaa5..0000000 --- a/builder/xenserver/step_upload_iso.go +++ /dev/null @@ -1,78 +0,0 @@ -package xenserver - -import ( - "github.com/mitchellh/multistep" - "github.com/mitchellh/packer/packer" - "log" - "os" - "os/exec" - "strconv" -) - -type stepUploadIso struct{} - -func (self *stepUploadIso) Run(state multistep.StateBag) multistep.StepAction { - - client := state.Get("client").(XenAPIClient) - config := state.Get("config").(config) - ui := state.Get("ui").(packer.Ui) - - ui.Say("Step: Upload ISO to server") - iso_path := state.Get("iso_path").(string) - - // Determine the ISO's filesize - file, err := os.Open(iso_path) - if err != nil { - ui.Error(err.Error()) - return multistep.ActionHalt - } - stat, err := file.Stat() - - if err != nil { - ui.Error(err.Error()) - return multistep.ActionHalt - } - - iso_filesize := stat.Size() - - // Create a VDI with the write size - srs, err := client.GetSRByNameLabel(config.SrName) - - sr := srs[0] - - if err != nil { - ui.Error(err.Error()) - return multistep.ActionHalt - } - - filesize_str := strconv.FormatInt(iso_filesize, 10) - log.Printf("Filesize of the ISO is %d", filesize_str) - vdi, err := sr.CreateVdi("Packer Gen "+stat.Name(), filesize_str) - - if err != nil { - ui.Error(err.Error()) - return multistep.ActionHalt - } - - // Upload the ISO to this VDI - vdi_uuid, _ := vdi.GetUuid() - host_url := "https://" + client.Host - log.Printf("Host URL: %s", host_url) - ui.Say("Uploading ISO to " + vdi_uuid) - out, err := exec.Command("/usr/bin/importdisk.py", host_url, client.Username, client.Password, vdi_uuid, iso_path).CombinedOutput() - log.Printf("Output: %s", out) - - if err != nil { - log.Printf("%s", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - // Stash the vdi uuid to be used in preference - state.Put("iso_vdi_uuid", vdi_uuid) - - return multistep.ActionContinue -} - -func (self *stepUploadIso) Cleanup(state multistep.StateBag) { -} diff --git a/builder/xenserver/step_upload_vdi.go b/builder/xenserver/step_upload_vdi.go new file mode 100644 index 0000000..254feb0 --- /dev/null +++ b/builder/xenserver/step_upload_vdi.go @@ -0,0 +1,201 @@ +package xenserver + +import ( + "crypto/tls" + "fmt" + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/packer" + "log" + "net/http" + "os" + "time" +) + +type stepUploadVdi struct { + VdiName string + ImagePathFunc func() string + VdiUuidKey string +} + +func (self *stepUploadVdi) Run(state multistep.StateBag) multistep.StepAction { + config := state.Get("config").(config) + ui := state.Get("ui").(packer.Ui) + client := state.Get("client").(XenAPIClient) + + imagePath := self.ImagePathFunc() + if imagePath == "" { + // skip if no disk image to attach + return multistep.ActionContinue + } + + ui.Say(fmt.Sprintf("Step: Upload VDI '%s'", self.VdiName)) + + // Create VDI for the image + sr, err := config.GetSR(client) + if err != nil { + ui.Error(fmt.Sprintf("Unable to get SR: %s", err.Error())) + return multistep.ActionHalt + } + + // Open the file for reading (NB: putFile closes the file for us) + fh, err := os.Open(imagePath) + if err != nil { + ui.Error(fmt.Sprintf("Unable to open disk image '%s': %s", imagePath, err.Error())) + return multistep.ActionHalt + } + + // Get file length + fstat, err := fh.Stat() + if err != nil { + ui.Error(fmt.Sprintf("Unable to stat disk image '%s': %s", imagePath, err.Error())) + return multistep.ActionHalt + } + fileLength := fstat.Size() + + // Create the VDI + vdi, err := sr.CreateVdi(self.VdiName, fmt.Sprintf("%d", fileLength)) + if err != nil { + ui.Error(fmt.Sprintf("Unable to create VDI '%s': %s", self.VdiName, err.Error())) + return multistep.ActionHalt + } + + vdiUuid, err := vdi.GetUuid() + if err != nil { + ui.Error(fmt.Sprintf("Unable to get UUID of VDI '%s': %s", self.VdiName, err.Error())) + return multistep.ActionHalt + } + state.Put(self.VdiUuidKey, vdiUuid) + + task, err := client.CreateTask() + if err != nil { + ui.Error(fmt.Sprintf("Unable to create task: %s", err.Error())) + return multistep.ActionHalt + } + defer task.Destroy() + + import_url := fmt.Sprintf("https://%s/import_raw_vdi?vdi=%s&session_id=%s&task_id=%s", + client.Host, + vdi.Ref, + client.Session.(string), + task.Ref, + ) + + // Define a new transport which allows self-signed certs + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + + // Create a client + httpClient := &http.Client{Transport: tr} + + // Create request and download file + request, err := http.NewRequest("PUT", import_url, fh) + request.ContentLength = fileLength + + ui.Say(fmt.Sprintf("PUT disk image '%s'", import_url)) + + resp, err := httpClient.Do(request) // Do closes fh for us, according to docs + if err != nil { + ui.Error(fmt.Sprintf("Unable to upload disk image: %s", err.Error())) + return multistep.ActionHalt + } + + if resp.StatusCode != 200 { + ui.Error(fmt.Sprintf("Unable to upload disk image: PUT request got non-200 status code: %s", resp.Status)) + return multistep.ActionHalt + } + + logInterval := 0 + err = InterruptibleWait{ + Predicate: func() (bool, error) { + status, err := task.GetStatus() + if err != nil { + return false, fmt.Errorf("Failed to get task status: %s", err.Error()) + } + switch status { + case Pending: + progress, err := task.GetProgress() + if err != nil { + return false, fmt.Errorf("Failed to get progress: %s", err.Error()) + } + logInterval = logInterval + 1 + if logInterval%5 == 0 { + log.Printf("Upload %.0f%% complete", progress*100) + } + return false, nil + case Success: + return true, nil + case Failure: + errorInfo, err := task.GetErrorInfo() + if err != nil { + errorInfo = []string{fmt.Sprintf("furthermore, failed to get error info: %s", err.Error())} + } + return false, fmt.Errorf("Task failed: %s", errorInfo) + case Cancelling, Cancelled: + return false, fmt.Errorf("Task cancelled") + default: + return false, fmt.Errorf("Unknown task status %v", status) + } + }, + PredicateInterval: 1 * time.Second, + Timeout: 24 * time.Hour, + }.Wait(state) + + resp.Body.Close() + + if err != nil { + ui.Error(fmt.Sprintf("Error uploading: %s", err.Error())) + + return multistep.ActionHalt + } + + log.Printf("Upload complete") + + return multistep.ActionContinue +} + +func (self *stepUploadVdi) Cleanup(state multistep.StateBag) { + config := state.Get("config").(config) + ui := state.Get("ui").(packer.Ui) + client := state.Get("client").(XenAPIClient) + + if config.ShouldKeepInstance(state) { + return + } + + vdiUuidRaw, ok := state.GetOk(self.VdiUuidKey) + if !ok { + // VDI doesn't exist + return + } + + vdiUuid := vdiUuidRaw.(string) + if vdiUuid == "" { + // VDI already cleaned up + return + } + + vdi, err := client.GetVdiByUuid(vdiUuid) + if err != nil { + ui.Error(fmt.Sprintf("Can't get VDI '%s': %s", vdiUuid, err.Error())) + return + } + + // an interrupted import_raw_vdi takes a while to release the VDI + // so try several times + for i := 0; i < 3; i++ { + log.Printf("Trying to destroy VDI...") + err = vdi.Destroy() + if err == nil { + break + } + time.Sleep(1 * time.Second) + } + if err != nil { + ui.Error(fmt.Sprintf("Can't destroy VDI '%s': %s", vdiUuid, err.Error())) + return + } + ui.Say(fmt.Sprintf("Destroyed VDI '%s'", self.VdiName)) + + state.Put(self.VdiUuidKey, "") +} From 987a5ac55e08763976dcdb6167a6ef8566426dc4 Mon Sep 17 00:00:00 2001 From: Cheng Sun Date: Thu, 18 Dec 2014 17:10:17 +0000 Subject: [PATCH 6/9] Rename root_disk_size to disk_size CreateVdi now takes an integer size Note that disk_size is now in MB; root_disk_size was in bytes Fixed example --- builder/xenserver/builder.go | 12 +++++------- builder/xenserver/client.go | 4 ++-- builder/xenserver/step_create_instance.go | 2 +- builder/xenserver/step_upload_vdi.go | 2 +- examples/centos-6.4.conf | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) diff --git a/builder/xenserver/builder.go b/builder/xenserver/builder.go index c3ba993..79a4ca1 100644 --- a/builder/xenserver/builder.go +++ b/builder/xenserver/builder.go @@ -26,7 +26,7 @@ type config struct { InstanceName string `mapstructure:"instance_name"` InstanceMemory string `mapstructure:"instance_memory"` - RootDiskSize string `mapstructure:"root_disk_size"` + DiskSize uint `mapstructure:"disk_size"` CloneTemplate string `mapstructure:"clone_template"` SrName string `mapstructure:"sr_name"` FloppyFiles []string `mapstructure:"floppy_files"` @@ -130,6 +130,10 @@ func (self *Builder) Prepare(raws ...interface{}) (params []string, retErr error self.config.RawSSHWaitTimeout = "200m" } + if self.config.DiskSize == 0 { + self.config.DiskSize = 40000 + } + if self.config.InstanceMemory == "" { self.config.InstanceMemory = "1024000000" } @@ -173,7 +177,6 @@ func (self *Builder) Prepare(raws ...interface{}) (params []string, retErr error "host_ip": &self.config.HostIp, "instance_name": &self.config.InstanceName, "instance_memory": &self.config.InstanceMemory, - "root_disk_size": &self.config.RootDiskSize, "clone_template": &self.config.CloneTemplate, "sr_name": &self.config.SrName, "network_name": &self.config.NetworkName, @@ -272,11 +275,6 @@ func (self *Builder) Prepare(raws ...interface{}) (params []string, retErr error errs, errors.New("An instance name must be specified.")) } - if self.config.RootDiskSize == "" { - errs = packer.MultiErrorAppend( - errs, errors.New("A root disk size must be specified.")) - } - switch self.config.ExportFormat { case "xva", "vdi_raw": default: diff --git a/builder/xenserver/client.go b/builder/xenserver/client.go index dcf8a1a..c5bfe11 100644 --- a/builder/xenserver/client.go +++ b/builder/xenserver/client.go @@ -689,13 +689,13 @@ func (self *VM) SetIsATemplate(is_a_template bool) (err error) { // SR associated functions -func (self *SR) CreateVdi(name_label, size string) (vdi *VDI, err error) { +func (self *SR) CreateVdi(name_label string, size int64) (vdi *VDI, err error) { vdi = new(VDI) vdi_rec := make(xmlrpc.Struct) vdi_rec["name_label"] = name_label vdi_rec["SR"] = self.Ref - vdi_rec["virtual_size"] = size + vdi_rec["virtual_size"] = fmt.Sprintf("%d", size) vdi_rec["type"] = "user" vdi_rec["sharable"] = false vdi_rec["read_only"] = false diff --git a/builder/xenserver/step_create_instance.go b/builder/xenserver/step_create_instance.go index e39e7cb..2aed3a1 100644 --- a/builder/xenserver/step_create_instance.go +++ b/builder/xenserver/step_create_instance.go @@ -68,7 +68,7 @@ func (self *stepCreateInstance) Run(state multistep.StateBag) multistep.StepActi return multistep.ActionHalt } - vdi, err := sr.CreateVdi("Packer-disk", config.RootDiskSize) + 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 diff --git a/builder/xenserver/step_upload_vdi.go b/builder/xenserver/step_upload_vdi.go index 254feb0..fe3e57f 100644 --- a/builder/xenserver/step_upload_vdi.go +++ b/builder/xenserver/step_upload_vdi.go @@ -53,7 +53,7 @@ func (self *stepUploadVdi) Run(state multistep.StateBag) multistep.StepAction { fileLength := fstat.Size() // Create the VDI - vdi, err := sr.CreateVdi(self.VdiName, fmt.Sprintf("%d", fileLength)) + vdi, err := sr.CreateVdi(self.VdiName, fileLength) if err != nil { ui.Error(fmt.Sprintf("Unable to create VDI '%s': %s", self.VdiName, err.Error())) return multistep.ActionHalt diff --git a/examples/centos-6.4.conf b/examples/centos-6.4.conf index 18af4cb..893c07c 100644 --- a/examples/centos-6.4.conf +++ b/examples/centos-6.4.conf @@ -6,7 +6,7 @@ "host_ip": "10.81.2.105", "instance_name": "packer-centos-6-4", "instance_memory": "2048000000", - "root_disk_size": "40000000000", + "disk_size": 40000, "iso_name": "CentOS-6.4-x86_64-minimal.iso", "http_directory": "http", "local_ip": "10.80.3.223", From 2e1f94e831edac7bc96eb5aff12da77334702bd1 Mon Sep 17 00:00:00 2001 From: Cheng Sun Date: Thu, 18 Dec 2014 17:20:51 +0000 Subject: [PATCH 7/9] Rename instance_name to vm_name Fixed example --- builder/xenserver/builder.go | 8 ++++---- builder/xenserver/step_create_instance.go | 2 +- builder/xenserver/step_shutdown_and_export.go | 2 +- builder/xenserver/step_type_boot_command.go | 2 +- examples/centos-6.4.conf | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/builder/xenserver/builder.go b/builder/xenserver/builder.go index 79a4ca1..1abf8d7 100644 --- a/builder/xenserver/builder.go +++ b/builder/xenserver/builder.go @@ -24,7 +24,7 @@ type config struct { Password string `mapstructure:"password"` HostIp string `mapstructure:"host_ip"` - InstanceName string `mapstructure:"instance_name"` + VMName string `mapstructure:"vm_name"` InstanceMemory string `mapstructure:"instance_memory"` DiskSize uint `mapstructure:"disk_size"` CloneTemplate string `mapstructure:"clone_template"` @@ -175,7 +175,7 @@ func (self *Builder) Prepare(raws ...interface{}) (params []string, retErr error "username": &self.config.Username, "password": &self.config.Password, "host_ip": &self.config.HostIp, - "instance_name": &self.config.InstanceName, + "vm_name": &self.config.VMName, "instance_memory": &self.config.InstanceMemory, "clone_template": &self.config.CloneTemplate, "sr_name": &self.config.SrName, @@ -270,9 +270,9 @@ func (self *Builder) Prepare(raws ...interface{}) (params []string, retErr error errs, errors.New("An ip for the xenserver host must be specified.")) } - if self.config.InstanceName == "" { + if self.config.VMName == "" { errs = packer.MultiErrorAppend( - errs, errors.New("An instance name must be specified.")) + errs, errors.New("vm_name must be specified.")) } switch self.config.ExportFormat { diff --git a/builder/xenserver/step_create_instance.go b/builder/xenserver/step_create_instance.go index 2aed3a1..668f695 100644 --- a/builder/xenserver/step_create_instance.go +++ b/builder/xenserver/step_create_instance.go @@ -35,7 +35,7 @@ func (self *stepCreateInstance) Run(state multistep.StateBag) multistep.StepActi template := vms[0] // Clone that VM template - instance, err := template.Clone(config.InstanceName) + instance, err := template.Clone(config.VMName) if err != nil { ui.Error(fmt.Sprintf("Error cloning VM: %s", err.Error())) return multistep.ActionHalt diff --git a/builder/xenserver/step_shutdown_and_export.go b/builder/xenserver/step_shutdown_and_export.go index 79f1448..7dcd89f 100644 --- a/builder/xenserver/step_shutdown_and_export.go +++ b/builder/xenserver/step_shutdown_and_export.go @@ -119,7 +119,7 @@ func (stepShutdownAndExport) Run(state multistep.StateBag) multistep.StepAction client.Session.(string), ) - export_filename := fmt.Sprintf("%s/%s.xva", config.OutputDir, config.InstanceName) + export_filename := fmt.Sprintf("%s/%s.xva", config.OutputDir, config.VMName) ui.Say("Getting XVA " + export_url) err = downloadFile(export_url, export_filename) diff --git a/builder/xenserver/step_type_boot_command.go b/builder/xenserver/step_type_boot_command.go index 1211e50..28b5f63 100644 --- a/builder/xenserver/step_type_boot_command.go +++ b/builder/xenserver/step_type_boot_command.go @@ -60,7 +60,7 @@ func (self *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAct // @todo - include http port/ip so kickstarter files can be grabbed tplData := &bootCommandTemplateData{ - config.InstanceName, + config.VMName, config.LocalIp, http_port, } diff --git a/examples/centos-6.4.conf b/examples/centos-6.4.conf index 893c07c..3de670a 100644 --- a/examples/centos-6.4.conf +++ b/examples/centos-6.4.conf @@ -4,7 +4,7 @@ "username": "root", "password": "hostpassword", "host_ip": "10.81.2.105", - "instance_name": "packer-centos-6-4", + "vm_name": "packer-centos-6-4", "instance_memory": "2048000000", "disk_size": 40000, "iso_name": "CentOS-6.4-x86_64-minimal.iso", From b9a7feb225ad08ca620810b65301bafa5044cfeb Mon Sep 17 00:00:00 2001 From: Cheng Sun Date: Thu, 18 Dec 2014 17:26:10 +0000 Subject: [PATCH 8/9] Rename instance_memory to vm_memory Note that vm_memory is now in MB; instance_memory was in bytes SetStaticMemoryRange now takes int64 Fixed example --- builder/xenserver/builder.go | 19 +++++++++---------- builder/xenserver/client.go | 6 ++++-- builder/xenserver/step_create_instance.go | 4 ++-- examples/centos-6.4.conf | 2 +- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/builder/xenserver/builder.go b/builder/xenserver/builder.go index 1abf8d7..9574caa 100644 --- a/builder/xenserver/builder.go +++ b/builder/xenserver/builder.go @@ -24,13 +24,13 @@ type config struct { Password string `mapstructure:"password"` HostIp string `mapstructure:"host_ip"` - VMName string `mapstructure:"vm_name"` - InstanceMemory string `mapstructure:"instance_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"` + 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"` @@ -134,8 +134,8 @@ func (self *Builder) Prepare(raws ...interface{}) (params []string, retErr error self.config.DiskSize = 40000 } - if self.config.InstanceMemory == "" { - self.config.InstanceMemory = "1024000000" + if self.config.VMMemory == 0 { + self.config.VMMemory = 1024 } if self.config.CloneTemplate == "" { @@ -176,7 +176,6 @@ func (self *Builder) Prepare(raws ...interface{}) (params []string, retErr error "password": &self.config.Password, "host_ip": &self.config.HostIp, "vm_name": &self.config.VMName, - "instance_memory": &self.config.InstanceMemory, "clone_template": &self.config.CloneTemplate, "sr_name": &self.config.SrName, "network_name": &self.config.NetworkName, diff --git a/builder/xenserver/client.go b/builder/xenserver/client.go index c5bfe11..9fdffcd 100644 --- a/builder/xenserver/client.go +++ b/builder/xenserver/client.go @@ -537,9 +537,11 @@ func (self *VM) GetGuestMetrics() (metrics map[string]interface{}, err error) { return metrics, nil } -func (self *VM) SetStaticMemoryRange(min, max string) (err error) { +func (self *VM) SetStaticMemoryRange(min, max uint) (err error) { result := APIResult{} - err = self.Client.APICall(&result, "VM.set_memory_limits", self.Ref, min, max, min, max) + strMin := fmt.Sprintf("%d", min) + strMax := fmt.Sprintf("%d", max) + err = self.Client.APICall(&result, "VM.set_memory_limits", self.Ref, strMin, strMax, strMin, strMax) if err != nil { return err } diff --git a/builder/xenserver/step_create_instance.go b/builder/xenserver/step_create_instance.go index 668f695..927bca5 100644 --- a/builder/xenserver/step_create_instance.go +++ b/builder/xenserver/step_create_instance.go @@ -48,9 +48,9 @@ func (self *stepCreateInstance) Run(state multistep.StateBag) multistep.StepActi return multistep.ActionHalt } - err = instance.SetStaticMemoryRange(config.InstanceMemory, config.InstanceMemory) + err = instance.SetStaticMemoryRange(config.VMMemory*1024*1024, config.VMMemory*1024*1024) if err != nil { - ui.Error(fmt.Sprintf("Error setting VM memory=%s: %s", config.InstanceMemory, err.Error())) + ui.Error(fmt.Sprintf("Error setting VM memory=%d: %s", config.VMMemory*1024*1024, err.Error())) return multistep.ActionHalt } diff --git a/examples/centos-6.4.conf b/examples/centos-6.4.conf index 3de670a..2adfc77 100644 --- a/examples/centos-6.4.conf +++ b/examples/centos-6.4.conf @@ -5,7 +5,7 @@ "password": "hostpassword", "host_ip": "10.81.2.105", "vm_name": "packer-centos-6-4", - "instance_memory": "2048000000", + "vm_memory": 2048, "disk_size": 40000, "iso_name": "CentOS-6.4-x86_64-minimal.iso", "http_directory": "http", From 30c4c8001fa017a2faff45f310b7752bb4f8d817 Mon Sep 17 00:00:00 2001 From: Cheng Sun Date: Fri, 19 Dec 2014 14:10:29 +0000 Subject: [PATCH 9/9] Don't explicitly remove ISOs in step_remove_devices --- builder/xenserver/step_remove_devices.go | 28 ------------------------ 1 file changed, 28 deletions(-) diff --git a/builder/xenserver/step_remove_devices.go b/builder/xenserver/step_remove_devices.go index dd97e3c..cec045e 100644 --- a/builder/xenserver/step_remove_devices.go +++ b/builder/xenserver/step_remove_devices.go @@ -22,34 +22,6 @@ func (self *stepRemoveDevices) Run(state multistep.StateBag) multistep.StepActio return multistep.ActionHalt } - // Eject ISO from drive - vbds, err := instance.GetVBDs() - if err != nil { - ui.Error(fmt.Sprintf("Could not get VBDs")) - ui.Error(err.Error()) - return multistep.ActionHalt - } - for _, vbd := range vbds { - rec, err := vbd.GetRecord() - if err != nil { - ui.Error(fmt.Sprintf("Could not get record for VBD")) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - // Hack - should encapsulate this in the client really - // This is needed because we can't guarentee the type - // returned by the xmlrpc lib will be string - if recType, ok := rec["type"].(string); ok { - if recType == "CD" { - ui.Say("Ejecting CD...") - vbd.Eject() - } - } else { - break - } - } - // Destroy all connected VIFs vifs, err := instance.GetVIFs() if err != nil {