From 04bbc1b9f591cec5114d0229d81200a144ab007c Mon Sep 17 00:00:00 2001 From: Dongyu Zheng Date: Wed, 26 Oct 2016 21:45:35 -0700 Subject: [PATCH 1/3] Allow XVA builder to use templates already on XenCenter --- builder/xenserver/xva/builder.go | 18 ++-- .../xva/step_instantiate_template.go | 83 +++++++++++++++++++ 2 files changed, 96 insertions(+), 5 deletions(-) create mode 100644 builder/xenserver/xva/step_instantiate_template.go diff --git a/builder/xenserver/xva/builder.go b/builder/xenserver/xva/builder.go index 22be43c0..59e08380 100644 --- a/builder/xenserver/xva/builder.go +++ b/builder/xenserver/xva/builder.go @@ -20,8 +20,9 @@ type config struct { common.PackerConfig `mapstructure:",squash"` xscommon.CommonConfig `mapstructure:",squash"` - SourcePath string `mapstructure:"source_path"` - VMMemory uint `mapstructure:"vm_memory"` + SourcePath string `mapstructure:"source_path"` + SourceTemplate string `mapstructure:"source_template"` + VMMemory uint `mapstructure:"vm_memory"` PlatformArgs map[string]string `mapstructure:"platform_args"` @@ -72,8 +73,8 @@ 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 self.config.SourcePath == "" && self.config.SourceTemplate == "" { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("Either source_path or source_template must be specified")) } if len(errs.Errors) > 0 { @@ -107,6 +108,13 @@ func (self *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (pa httpReqChan := make(chan string, 1) + var stepImportOrInstantiate multistep.Step + if self.config.SourceTemplate != "" { + stepImportOrInstantiate = new(stepInstantiateTemplate) + } else { + stepImportOrInstantiate = new(stepImportInstance) + } + //Build the steps steps := []multistep.Step{ &xscommon.StepPrepareOutputDir{ @@ -133,7 +141,7 @@ func (self *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (pa VdiName: self.config.ToolsIsoName, VdiUuidKey: "tools_vdi_uuid", }, - new(stepImportInstance), + stepImportOrInstantiate, &xscommon.StepAttachVdi{ VdiUuidKey: "floppy_vdi_uuid", VdiType: xsclient.Floppy, diff --git a/builder/xenserver/xva/step_instantiate_template.go b/builder/xenserver/xva/step_instantiate_template.go new file mode 100644 index 00000000..da14610c --- /dev/null +++ b/builder/xenserver/xva/step_instantiate_template.go @@ -0,0 +1,83 @@ +package xva + +import ( + "fmt" + + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/packer" + xsclient "github.com/xenserver/go-xenserver-client" +) + +type stepInstantiateTemplate struct { + instance *xsclient.VM + vdi *xsclient.VDI +} + +func (self *stepInstantiateTemplate) Run(state multistep.StateBag) multistep.StepAction { + client := state.Get("client").(xsclient.XenAPIClient) + config := state.Get("config").(config) + ui := state.Get("ui").(packer.Ui) + + ui.Say("Step: Instantiate Template") + + var template *xsclient.VM + var err error + + if len(config.SourceTemplate) >= 7 && config.SourceTemplate[:7] == "uuid://" { + templateUuid := config.SourceTemplate[7:] + + template, err = client.GetVMByUuid(templateUuid) + if err != nil { + ui.Error(fmt.Sprintf("Could not get template with UUID '%s': %s", templateUuid, err.Error())) + return multistep.ActionHalt + } + } else { + templates, err := client.GetVMByNameLabel(config.SourceTemplate) + if err != nil { + ui.Error(fmt.Sprintf("Error getting template: %s", err.Error())) + return multistep.ActionHalt + } + + switch { + case len(templates) == 0: + ui.Error(fmt.Sprintf("Couldn't find a template with the name-label '%s'. Aborting.", config.SourceTemplate)) + return multistep.ActionHalt + case len(templates) > 1: + ui.Error(fmt.Sprintf("Found more than one template with the name '%s'. The name must be unique. Aborting.", config.SourceTemplate)) + return multistep.ActionHalt + } + + template = templates[0] + } + + self.instance, err = template.Clone(config.VMName) + if err != nil { + ui.Error(fmt.Sprintf("Error cloning template: %s", err.Error())) + return multistep.ActionHalt + } + + err = self.instance.SetIsATemplate(false) + if err != nil { + ui.Error(fmt.Sprintf("Error converting template to a VM: %s", err.Error())) + return multistep.ActionHalt + } + + instanceId, err := self.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) + + err = self.instance.SetDescription(config.VMDescription) + if err != nil { + ui.Error(fmt.Sprintf("Error setting VM description: %s", err.Error())) + return multistep.ActionHalt + } + + ui.Say(fmt.Sprintf("Instantiated template '%s'", instanceId)) + + return multistep.ActionContinue +} + +func (self *stepInstantiateTemplate) Cleanup(state multistep.StateBag) {} From aa8289a44baa36f98d30cc7a93d8d6a7cdc75df9 Mon Sep 17 00:00:00 2001 From: Dongyu Zheng Date: Thu, 27 Oct 2016 11:55:22 -0700 Subject: [PATCH 2/3] Default to use source_path if source_template fails --- builder/xenserver/xva/builder.go | 10 ++---- builder/xenserver/xva/step_import_instance.go | 13 ++++++-- .../xva/step_instantiate_template.go | 33 ++++++++++++------- 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/builder/xenserver/xva/builder.go b/builder/xenserver/xva/builder.go index 59e08380..4cb2be29 100644 --- a/builder/xenserver/xva/builder.go +++ b/builder/xenserver/xva/builder.go @@ -108,13 +108,6 @@ func (self *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (pa httpReqChan := make(chan string, 1) - var stepImportOrInstantiate multistep.Step - if self.config.SourceTemplate != "" { - stepImportOrInstantiate = new(stepInstantiateTemplate) - } else { - stepImportOrInstantiate = new(stepImportInstance) - } - //Build the steps steps := []multistep.Step{ &xscommon.StepPrepareOutputDir{ @@ -141,7 +134,8 @@ func (self *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (pa VdiName: self.config.ToolsIsoName, VdiUuidKey: "tools_vdi_uuid", }, - stepImportOrInstantiate, + new(stepInstantiateTemplate), + new(stepImportInstance), &xscommon.StepAttachVdi{ VdiUuidKey: "floppy_vdi_uuid", VdiType: xsclient.Floppy, diff --git a/builder/xenserver/xva/step_import_instance.go b/builder/xenserver/xva/step_import_instance.go index 0a734b73..14bf75b9 100644 --- a/builder/xenserver/xva/step_import_instance.go +++ b/builder/xenserver/xva/step_import_instance.go @@ -16,13 +16,22 @@ type stepImportInstance struct { } func (self *stepImportInstance) Run(state multistep.StateBag) multistep.StepAction { - - client := state.Get("client").(xsclient.XenAPIClient) config := state.Get("config").(config) + + if state.Get("instance_uuid") != nil { + return multistep.ActionContinue + } + ui := state.Get("ui").(packer.Ui) + client := state.Get("client").(xsclient.XenAPIClient) ui.Say("Step: Import Instance") + if config.SourcePath == "" { + ui.Error(fmt.Sprintf("Failed to instantiate \"source_template\": \"%s\" and \"source_path\" is empty. Aborting.", config.SourceTemplate)) + return multistep.ActionHalt + } + // find the SR sr, err := config.GetSR(client) if err != nil { diff --git a/builder/xenserver/xva/step_instantiate_template.go b/builder/xenserver/xva/step_instantiate_template.go index da14610c..a9993fa6 100644 --- a/builder/xenserver/xva/step_instantiate_template.go +++ b/builder/xenserver/xva/step_instantiate_template.go @@ -14,8 +14,13 @@ type stepInstantiateTemplate struct { } func (self *stepInstantiateTemplate) Run(state multistep.StateBag) multistep.StepAction { - client := state.Get("client").(xsclient.XenAPIClient) config := state.Get("config").(config) + + if config.SourceTemplate == "" { + return multistep.ActionContinue + } + + client := state.Get("client").(xsclient.XenAPIClient) ui := state.Get("ui").(packer.Ui) ui.Say("Step: Instantiate Template") @@ -29,22 +34,26 @@ func (self *stepInstantiateTemplate) Run(state multistep.StateBag) multistep.Ste template, err = client.GetVMByUuid(templateUuid) if err != nil { ui.Error(fmt.Sprintf("Could not get template with UUID '%s': %s", templateUuid, err.Error())) - return multistep.ActionHalt + ui.Error("Defaulting to use \"source_path\".") + return multistep.ActionContinue } } else { templates, err := client.GetVMByNameLabel(config.SourceTemplate) if err != nil { ui.Error(fmt.Sprintf("Error getting template: %s", err.Error())) - return multistep.ActionHalt + ui.Error("Defaulting to use \"source_path\".") + return multistep.ActionContinue } switch { case len(templates) == 0: - ui.Error(fmt.Sprintf("Couldn't find a template with the name-label '%s'. Aborting.", config.SourceTemplate)) - return multistep.ActionHalt + ui.Error(fmt.Sprintf("Couldn't find a template with the name-label '%s'.", config.SourceTemplate)) + ui.Error("Defaulting to use \"source_path\".") + return multistep.ActionContinue case len(templates) > 1: - ui.Error(fmt.Sprintf("Found more than one template with the name '%s'. The name must be unique. Aborting.", config.SourceTemplate)) - return multistep.ActionHalt + ui.Error(fmt.Sprintf("Found more than one template with the name '%s'. The name must be unique.", config.SourceTemplate)) + ui.Error("Defaulting to use \"source_path\".") + return multistep.ActionContinue } template = templates[0] @@ -56,18 +65,18 @@ func (self *stepInstantiateTemplate) Run(state multistep.StateBag) multistep.Ste return multistep.ActionHalt } - err = self.instance.SetIsATemplate(false) + instanceId, err := self.instance.GetUuid() if err != nil { - ui.Error(fmt.Sprintf("Error converting template to a VM: %s", err.Error())) + ui.Error(fmt.Sprintf("Unable to get VM UUID: %s", err.Error())) return multistep.ActionHalt } + state.Put("instance_uuid", instanceId) - instanceId, err := self.instance.GetUuid() + err = self.instance.SetIsATemplate(false) if err != nil { - ui.Error(fmt.Sprintf("Unable to get VM UUID: %s", err.Error())) + ui.Error(fmt.Sprintf("Error converting template to a VM: %s", err.Error())) return multistep.ActionHalt } - state.Put("instance_uuid", instanceId) err = self.instance.SetDescription(config.VMDescription) if err != nil { From ab68636892f04cd6141e71ef4d0bb5924f1b0d11 Mon Sep 17 00:00:00 2001 From: Dongyu Zheng Date: Fri, 4 Nov 2016 15:43:42 -0700 Subject: [PATCH 3/3] Added cleanup to step_instantiate_template --- .../xva/step_instantiate_template.go | 56 ++++++++++++++++++- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/builder/xenserver/xva/step_instantiate_template.go b/builder/xenserver/xva/step_instantiate_template.go index a9993fa6..edcc436b 100644 --- a/builder/xenserver/xva/step_instantiate_template.go +++ b/builder/xenserver/xva/step_instantiate_template.go @@ -10,7 +10,6 @@ import ( type stepInstantiateTemplate struct { instance *xsclient.VM - vdi *xsclient.VDI } func (self *stepInstantiateTemplate) Run(state multistep.StateBag) multistep.StepAction { @@ -89,4 +88,57 @@ func (self *stepInstantiateTemplate) Run(state multistep.StateBag) multistep.Ste return multistep.ActionContinue } -func (self *stepInstantiateTemplate) Cleanup(state multistep.StateBag) {} +func (self *stepInstantiateTemplate) Cleanup(state multistep.StateBag) { + config := state.Get("config").(config) + if config.ShouldKeepVM(state) { + return + } + + ui := state.Get("ui").(packer.Ui) + + // We want to perform a 'xe vm-uninstall' + // Uninstall a VM. This operation will destroy those VDIs that are marked RW and connected to this VM only. + if self.instance != nil { + vbds, err := self.instance.GetVBDs() + if err != nil { + ui.Error(err.Error()) + } + + // Destroy VDIs before destroying VM, since vm.Destroy() also destroys VBDs, + // in which case we will be unable to find the VDIs + for _, vbd := range vbds { + vbd_rec, err := vbd.GetRecord() + if err != nil { + ui.Error(err.Error()) + continue + } + if vbd_rec["mode"].(string) == "RW" { + vdi, err := vbd.GetVDI() + if err != nil { + ui.Error(err.Error()) + continue + } + vdi_vbds, err := vdi.GetVBDs() + if err != nil { + ui.Error(err.Error()) + continue + } + // If connected to this VM only + if len(vdi_vbds) <= 1 { + ui.Say("Destroying VDI") + err = vdi.Destroy() + if err != nil { + ui.Error(err.Error()) + } + } + } + } + + ui.Say("Destroying VM") + _ = self.instance.HardShutdown() + err = self.instance.Destroy() + if err != nil { + ui.Error(err.Error()) + } + } +}