diff --git a/builder/xenserver/common/http_upload.go b/builder/xenserver/common/http_upload.go index 57443ac..4932f96 100644 --- a/builder/xenserver/common/http_upload.go +++ b/builder/xenserver/common/http_upload.go @@ -23,25 +23,27 @@ func appendQuery(urlstring, k, v string) (string, error) { return u.String(), err } -func httpUpload(import_url string, fh *os.File, state multistep.StateBag) error { +func HTTPUpload(import_url string, fh *os.File, state multistep.StateBag) (result *XenAPIObject, err error) { ui := state.Get("ui").(packer.Ui) client := state.Get("client").(XenAPIClient) task, err := client.CreateTask() if err != nil { - return fmt.Errorf("Unable to create task: %s", err.Error()) + err = fmt.Errorf("Unable to create task: %s", err.Error()) + return } defer task.Destroy() import_task_url, err := appendQuery(import_url, "task_id", task.Ref) if err != nil { - return err + return } // Get file length fstat, err := fh.Stat() if err != nil { - return fmt.Errorf("Unable to stat '%s': %s", fh.Name(), err.Error()) + err = fmt.Errorf("Unable to stat '%s': %s", fh.Name(), err.Error()) + return } fileLength := fstat.Size() @@ -61,11 +63,12 @@ func httpUpload(import_url string, fh *os.File, state multistep.StateBag) error resp, err := httpClient.Do(request) // Do closes fh for us, according to docs if err != nil { - return err + return } if resp.StatusCode != 200 { - return fmt.Errorf("PUT request got non-200 status code: %s", resp.Status) + err = fmt.Errorf("PUT request got non-200 status code: %s", resp.Status) + return } logIteration := 0 @@ -107,9 +110,16 @@ func httpUpload(import_url string, fh *os.File, state multistep.StateBag) error resp.Body.Close() if err != nil { - return fmt.Errorf("Error uploading: %s", err.Error()) + err = fmt.Errorf("Error uploading: %s", err.Error()) + return + } + + result, err = task.GetResult() + if err != nil { + err = fmt.Errorf("Error getting result: %s", err.Error()) + return } log.Printf("Upload complete") - return nil + return } diff --git a/builder/xenserver/common/step_detach_vdi.go b/builder/xenserver/common/step_detach_vdi.go index c7a992d..8445f9d 100644 --- a/builder/xenserver/common/step_detach_vdi.go +++ b/builder/xenserver/common/step_detach_vdi.go @@ -39,7 +39,8 @@ func (self *StepDetachVdi) Run(state multistep.StateBag) multistep.StepAction { err = instance.DisconnectVdi(vdi) if err != nil { ui.Error(fmt.Sprintf("Unable to detach VDI '%s': %s", vdiUuid, err.Error())) - return multistep.ActionHalt + //return multistep.ActionHalt + return multistep.ActionContinue } log.Printf("Detached VDI '%s'", vdiUuid) diff --git a/builder/xenserver/common/step_upload_vdi.go b/builder/xenserver/common/step_upload_vdi.go index 6de6d92..ce3c8d3 100644 --- a/builder/xenserver/common/step_upload_vdi.go +++ b/builder/xenserver/common/step_upload_vdi.go @@ -35,7 +35,7 @@ func (self *StepUploadVdi) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionHalt } - // Open the file for reading (NB: httpUpload closes the file for us) + // Open the file for reading (NB: HTTPUpload 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())) @@ -64,7 +64,7 @@ func (self *StepUploadVdi) Run(state multistep.StateBag) multistep.StepAction { } state.Put(self.VdiUuidKey, vdiUuid) - err = httpUpload(fmt.Sprintf("https://%s/import_raw_vdi?vdi=%s&session_id=%s", + _, err = HTTPUpload(fmt.Sprintf("https://%s/import_raw_vdi?vdi=%s&session_id=%s", client.Host, vdi.Ref, client.Session.(string), diff --git a/builder/xenserver/xva/builder.go b/builder/xenserver/xva/builder.go index 94f256c..1787c5d 100644 --- a/builder/xenserver/xva/builder.go +++ b/builder/xenserver/xva/builder.go @@ -15,6 +15,7 @@ type config struct { common.PackerConfig `mapstructure:",squash"` xscommon.CommonConfig `mapstructure:",squash"` + SourcePath string `mapstructure:"source_path"` VMMemory uint `mapstructure:"vm_memory"` CloneTemplate string `mapstructure:"clone_template"` @@ -65,6 +66,7 @@ func (self *Builder) Prepare(raws ...interface{}) (params []string, retErr error // Template substitution templates := map[string]*string{ + "source_path": &self.config.SourcePath, "clone_template": &self.config.CloneTemplate, "network_name": &self.config.NetworkName, } @@ -79,6 +81,10 @@ func (self *Builder) Prepare(raws ...interface{}) (params []string, retErr error // Validation + if self.config.SourcePath == "" { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("A source_path must be specified")) + } + if len(errs.Errors) > 0 { retErr = errors.New(errs.Error()) } @@ -104,6 +110,7 @@ func (self *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (pa 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) @@ -131,7 +138,7 @@ func (self *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (pa VdiName: self.config.ToolsIsoName, VdiUuidKey: "tools_vdi_uuid", }, - new(stepCreateInstance), + new(stepImportInstance), &xscommon.StepAttachVdi{ VdiUuidKey: "floppy_vdi_uuid", VdiType: xscommon.Floppy, @@ -140,6 +147,7 @@ func (self *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (pa VdiUuidKey: "tools_vdi_uuid", VdiType: xscommon.CD, }, + new(xscommon.StepRemoveDevices), new(xscommon.StepStartOnHIMN), &xscommon.StepForwardPortOverSSH{ RemotePort: xscommon.HimnSSHPort, diff --git a/builder/xenserver/xva/step_create_instance.go b/builder/xenserver/xva/step_import_instance.go similarity index 57% rename from builder/xenserver/xva/step_create_instance.go rename to builder/xenserver/xva/step_import_instance.go index 986516a..47531cd 100644 --- a/builder/xenserver/xva/step_create_instance.go +++ b/builder/xenserver/xva/step_import_instance.go @@ -1,56 +1,58 @@ package xva import ( - // "fmt" + "fmt" + "os" "github.com/mitchellh/multistep" - // "github.com/mitchellh/packer/packer" + "github.com/mitchellh/packer/packer" xscommon "github.com/rdobson/packer-builder-xenserver/builder/xenserver/common" ) -type stepCreateInstance struct { +type stepImportInstance struct { instance *xscommon.VM vdi *xscommon.VDI } -func (self *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction { +func (self *stepImportInstance) 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: Import Instance") + + // find the SR + 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: httpUpload closes the file for us) + fh, err := os.Open(config.SourcePath) + if err != nil { + ui.Error(fmt.Sprintf("Unable to open XVA '%s': %s", config.SourcePath, err.Error())) + return multistep.ActionHalt + } + + result, err := xscommon.HTTPUpload(fmt.Sprintf("https://%s/import?session_id=%s&sr_id=%s", + client.Host, + client.Session.(string), + sr.Ref, + ), fh, state) + if err != nil { + ui.Error(fmt.Sprintf("Unable to upload VDI: %s", err.Error())) + return multistep.ActionHalt + } + if result == nil { + ui.Error("XAPI did not reply with an instance reference") + return multistep.ActionHalt + } + + instance := xscommon.VM(*result) /* - 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())) @@ -63,27 +65,6 @@ func (self *stepCreateInstance) Run(state multistep.StateBag) multistep.StepActi 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 @@ -151,20 +132,21 @@ func (self *stepCreateInstance) Run(state multistep.StateBag) multistep.StepActi 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)) */ + 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("Imported instance '%s'", instanceId)) + return multistep.ActionContinue } -func (self *stepCreateInstance) Cleanup(state multistep.StateBag) { +func (self *stepImportInstance) Cleanup(state multistep.StateBag) { /* config := state.Get("config").(config) if config.ShouldKeepVM(state) {