From 17c58e8d243fbac79c28260a0174fbf2187bd9d0 Mon Sep 17 00:00:00 2001 From: Heinrich Kruger Date: Thu, 26 May 2022 16:21:49 +0100 Subject: [PATCH 1/5] Enable xva builder to create VM from existing template --- builder/xenserver/xva/builder.go | 10 +- .../xva/step_create_from_template.go | 191 ++++++++++++++++++ builder/xenserver/xva/step_import_instance.go | 6 + 3 files changed, 204 insertions(+), 3 deletions(-) create mode 100644 builder/xenserver/xva/step_create_from_template.go diff --git a/builder/xenserver/xva/builder.go b/builder/xenserver/xva/builder.go index 4eee1c4..f64cafc 100644 --- a/builder/xenserver/xva/builder.go +++ b/builder/xenserver/xva/builder.go @@ -3,7 +3,6 @@ package xva import ( "context" "errors" - "fmt" "time" "github.com/hashicorp/hcl/v2/hcldec" @@ -74,8 +73,12 @@ func (self *Builder) Prepare(raws ...interface{}) (params []string, warns []stri // Validation - if self.config.SourcePath == "" { - errs = packer.MultiErrorAppend(errs, fmt.Errorf("A source_path must be specified")) + if self.config.SourcePath == "" && self.config.CloneTemplate == "" { + errs = packer.MultiErrorAppend( + errs, errors.New("Either source_path or clone_template must be specified")) + } else if self.config.SourcePath != "" && self.config.CloneTemplate != "" { + errs = packer.MultiErrorAppend( + errs, errors.New("Only one of source_path and clone_template must be specified")) } if len(errs.Errors) > 0 { @@ -134,6 +137,7 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p VdiName: self.config.ToolsIsoName, VdiUuidKey: "tools_vdi_uuid", }, + new(stepCreateFromTemplate), new(stepImportInstance), &xscommon.StepAttachVdi{ VdiUuidKey: "floppy_vdi_uuid", diff --git a/builder/xenserver/xva/step_create_from_template.go b/builder/xenserver/xva/step_create_from_template.go new file mode 100644 index 0000000..a4bd436 --- /dev/null +++ b/builder/xenserver/xva/step_create_from_template.go @@ -0,0 +1,191 @@ +package xva + +import ( + "context" + "fmt" + "log" + + "github.com/hashicorp/packer-plugin-sdk/multistep" + "github.com/hashicorp/packer-plugin-sdk/packer" + xsclient "github.com/terra-farm/go-xen-api-client" + xscommon "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common" +) + +type stepCreateFromTemplate struct { + instance *xsclient.VMRef +} + +func (self *stepCreateFromTemplate) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { + + c := state.Get("client").(*xscommon.Connection) + config := state.Get("config").(xscommon.Config) + ui := state.Get("ui").(packer.Ui) + + ui.Say("Step: Create Instance from Template") + + if config.CloneTemplate == "" { + log.Println("Skipping creation from template - no `clone_template` configured.") + return multistep.ActionContinue + } + + // Get the template to clone from + + vms, err := c.GetClient().VM.GetByNameLabel(c.GetSessionRef(), 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 := c.GetClient().VM.Clone(c.GetSessionRef(), template, config.VMName) + if err != nil { + ui.Error(fmt.Sprintf("Error cloning VM: %s", err.Error())) + return multistep.ActionHalt + } + self.instance = &instance + + err = c.GetClient().VM.SetIsATemplate(c.GetSessionRef(), instance, false) + if err != nil { + ui.Error(fmt.Sprintf("Error setting is_a_template=false: %s", err.Error())) + return multistep.ActionHalt + } + + err = c.GetClient().VM.SetVCPUsMax(c.GetSessionRef(), instance, int(config.VCPUsMax)) + if err != nil { + ui.Error(fmt.Sprintf("Error setting VM VCPUs Max=%d: %s", config.VCPUsMax, err.Error())) + return multistep.ActionHalt + } + + err = c.GetClient().VM.SetVCPUsAtStartup(c.GetSessionRef(), instance, int(config.VCPUsAtStartup)) + if err != nil { + ui.Error(fmt.Sprintf("Error setting VM VCPUs At Startup=%d: %s", config.VCPUsAtStartup, err.Error())) + return multistep.ActionHalt + } + + memory := int(config.VMMemory * 1024 * 1024) + err = c.GetClient().VM.SetMemoryLimits(c.GetSessionRef(), instance, memory, memory, memory, memory) + if err != nil { + ui.Error(fmt.Sprintf("Error setting VM memory=%d: %s", memory, err.Error())) + return multistep.ActionHalt + } + + err = c.GetClient().VM.SetPlatform(c.GetSessionRef(), instance, config.PlatformArgs) + if err != nil { + ui.Error(fmt.Sprintf("Error setting VM platform: %s", err.Error())) + return multistep.ActionHalt + } + + err = c.GetClient().VM.SetNameDescription(c.GetSessionRef(), instance, config.VMDescription) + if err != nil { + ui.Error(fmt.Sprintf("Error setting VM description: %s", err.Error())) + return multistep.ActionHalt + } + + // Connect Network + + var network xsclient.NetworkRef + + if len(config.NetworkNames) == 0 { + // No network has be specified. Use the management interface + log.Println("No network name given, attempting to use management interface") + pifs, err := c.GetClient().PIF.GetAll(c.GetSessionRef()) + + if err != nil { + ui.Error(fmt.Sprintf("Error getting PIFs: %s", err.Error())) + return multistep.ActionHalt + } + + for _, pif := range pifs { + pif_rec, err := c.GetClient().PIF.GetRecord(c.GetSessionRef(), pif) + + if err != nil { + ui.Error(fmt.Sprintf("Error getting PIF record: %s", err.Error())) + return multistep.ActionHalt + } + + if pif_rec.Management { + network = pif_rec.Network + } + + } + + if string(network) == "" { + ui.Error("Error: couldn't find management network. Aborting.") + return multistep.ActionHalt + } + + log.Printf("Creating VIF on network '%s' on VM '%s'\n", network, instance) + _, err = xscommon.ConnectNetwork(c, network, instance, "0") + + if err != nil { + ui.Error(fmt.Sprintf("Failed to create VIF with error: %v", err)) + return multistep.ActionHalt + } + + } else { + log.Printf("Using provided network names: %v\n", config.NetworkNames) + // Look up each network by it's name label + for i, networkNameLabel := range config.NetworkNames { + networks, err := c.GetClient().Network.GetByNameLabel(c.GetSessionRef(), networkNameLabel) + + 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.", networkNameLabel)) + 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.", networkNameLabel)) + return multistep.ActionHalt + } + + //we need the VIF index string + vifIndexString := fmt.Sprintf("%d", i) + _, err = xscommon.ConnectNetwork(c, networks[0], instance, vifIndexString) + + if err != nil { + ui.Say(fmt.Sprintf("Failed to connect VIF with error: %v", err.Error())) + } + } + } + + instanceId, err := c.GetClient().VM.GetUUID(c.GetSessionRef(), instance) + 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 *stepCreateFromTemplate) Cleanup(state multistep.StateBag) { + config := state.Get("config").(xscommon.Config) + if config.ShouldKeepVM(state) { + return + } + + ui := state.Get("ui").(packer.Ui) + c := state.Get("client").(*xscommon.Connection) + + if self.instance != nil { + ui.Say("Destroying VM") + _ = c.GetClient().VM.HardShutdown(c.GetSessionRef(), *self.instance) // redundant, just in case + err := c.GetClient().VM.Destroy(c.GetSessionRef(), *self.instance) + if err != nil { + ui.Error(err.Error()) + } + } +} diff --git a/builder/xenserver/xva/step_import_instance.go b/builder/xenserver/xva/step_import_instance.go index 54215fa..ecc53a8 100644 --- a/builder/xenserver/xva/step_import_instance.go +++ b/builder/xenserver/xva/step_import_instance.go @@ -3,6 +3,7 @@ package xva import ( "context" "fmt" + "log" "os" "github.com/hashicorp/packer-plugin-sdk/multistep" @@ -24,6 +25,11 @@ func (self *stepImportInstance) Run(ctx context.Context, state multistep.StateBa ui.Say("Step: Import Instance") + if config.SourcePath == "" { + log.Println("Skipping imporing instance - no `source_path` configured.") + return multistep.ActionContinue + } + // find the SR srs, err := c.GetClient().SR.GetAll(c.GetSessionRef()) sr := srs[0] From 0bb70e89572fe3ebf6b69bd700e15abd0d4b0173 Mon Sep 17 00:00:00 2001 From: Heinrich Kruger Date: Thu, 26 May 2022 16:25:17 +0100 Subject: [PATCH 2/5] Update XVA builder to match ISO builder --- builder/xenserver/xva/builder.go | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/builder/xenserver/xva/builder.go b/builder/xenserver/xva/builder.go index f64cafc..0477172 100644 --- a/builder/xenserver/xva/builder.go +++ b/builder/xenserver/xva/builder.go @@ -42,6 +42,7 @@ func (self *Builder) Prepare(raws ...interface{}) (params []string, warns []stri errs = packer.MultiErrorAppend( errs, self.config.CommonConfig.Prepare(self.config.GetInterpContext(), &self.config.PackerConfig)...) + errs = packer.MultiErrorAppend(errs, self.config.SSHConfig.Prepare(self.config.GetInterpContext())...) // Set default values if self.config.VCPUsMax == 0 { @@ -104,7 +105,7 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p //Share state between the other steps using a statebag state := new(multistep.BasicStateBag) state.Put("client", c) - // state.Put("config", self.config) + state.Put("config", self.config) state.Put("commonconfig", self.config.CommonConfig) state.Put("hook", hook) state.Put("ui", ui) @@ -119,8 +120,11 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p }, &commonsteps.StepCreateFloppy{ Files: self.config.FloppyFiles, + Label: "cidata", + }, + &xscommon.StepHTTPServer{ + Chan: httpReqChan, }, - new(xscommon.StepHTTPServer), &xscommon.StepUploadVdi{ VdiNameFunc: func() string { return "Packer-floppy-disk" @@ -157,20 +161,28 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p Chan: httpReqChan, Timeout: 300 * time.Minute, /*self.config.InstallTimeout*/ // @todo change this }, + &xscommon.StepForwardPortOverSSH{ + RemotePort: xscommon.InstanceSSHPort, + RemoteDest: xscommon.InstanceSSHIP, + HostPortMin: self.config.HostPortMin, + HostPortMax: self.config.HostPortMax, + ResultKey: "local_ssh_port", + }, &communicator.StepConnect{ Config: &self.config.SSHConfig.Comm, - Host: xscommon.CommHost, - SSHConfig: xscommon.SSHConfigFunc(self.config.CommonConfig.SSHConfig), - SSHPort: xscommon.SSHPort, + Host: xscommon.InstanceSSHIP, + SSHConfig: self.config.Comm.SSHConfigFunc(), + SSHPort: xscommon.InstanceSSHPort, }, new(commonsteps.StepProvision), new(xscommon.StepShutdown), - &xscommon.StepDetachVdi{ - VdiUuidKey: "floppy_vdi_uuid", - }, + new(xscommon.StepSetVmToTemplate), &xscommon.StepDetachVdi{ VdiUuidKey: "tools_vdi_uuid", }, + &xscommon.StepDetachVdi{ + VdiUuidKey: "floppy_vdi_uuid", + }, new(xscommon.StepExport), } From 461367ad1cbaa7ac7d97432f599ff51073d3bb1b Mon Sep 17 00:00:00 2001 From: Dom Del Nano Date: Sat, 15 Apr 2023 14:49:11 -0700 Subject: [PATCH 3/5] Move create instance step to common directory --- .../{iso => common}/step_create_instance.go | 23 +++++++++---------- builder/xenserver/iso/builder.go | 2 +- 2 files changed, 12 insertions(+), 13 deletions(-) rename builder/xenserver/{iso => common}/step_create_instance.go (91%) diff --git a/builder/xenserver/iso/step_create_instance.go b/builder/xenserver/common/step_create_instance.go similarity index 91% rename from builder/xenserver/iso/step_create_instance.go rename to builder/xenserver/common/step_create_instance.go index df6702a..8f3611a 100644 --- a/builder/xenserver/iso/step_create_instance.go +++ b/builder/xenserver/common/step_create_instance.go @@ -1,4 +1,4 @@ -package iso +package common import ( "context" @@ -9,18 +9,17 @@ import ( "github.com/hashicorp/packer-plugin-sdk/packer" xenapi "github.com/terra-farm/go-xen-api-client" xsclient "github.com/terra-farm/go-xen-api-client" - xscommon "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common" ) -type stepCreateInstance struct { +type StepCreateInstance struct { instance *xsclient.VMRef vdi *xsclient.VDIRef } -func (self *stepCreateInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { +func (self *StepCreateInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - c := state.Get("client").(*xscommon.Connection) - config := state.Get("config").(xscommon.Config) + c := state.Get("client").(*Connection) + config := state.Get("config").(Config) ui := state.Get("ui").(packer.Ui) ui.Say("Step: Create Instance") @@ -134,7 +133,7 @@ func (self *stepCreateInstance) Run(ctx context.Context, state multistep.StateBa } self.vdi = &vdi - err = xscommon.ConnectVdi(c, instance, vdi, xsclient.VbdTypeDisk) + err = ConnectVdi(c, instance, vdi, xsclient.VbdTypeDisk) if err != nil { ui.Error(fmt.Sprintf("Unable to connect packer disk VDI: %s", err.Error())) return multistep.ActionHalt @@ -174,7 +173,7 @@ func (self *stepCreateInstance) Run(ctx context.Context, state multistep.StateBa } log.Printf("Creating VIF on network '%s' on VM '%s'\n", network, instance) - _, err = xscommon.ConnectNetwork(c, network, instance, "0") + _, err = ConnectNetwork(c, network, instance, "0") if err != nil { ui.Error(fmt.Sprintf("Failed to create VIF with error: %v", err)) @@ -203,7 +202,7 @@ func (self *stepCreateInstance) Run(ctx context.Context, state multistep.StateBa //we need the VIF index string vifIndexString := fmt.Sprintf("%d", i) - _, err = xscommon.ConnectNetwork(c, networks[0], instance, vifIndexString) + _, err = ConnectNetwork(c, networks[0], instance, vifIndexString) if err != nil { ui.Say(fmt.Sprintf("Failed to connect VIF with error: %v", err.Error())) @@ -223,14 +222,14 @@ func (self *stepCreateInstance) Run(ctx context.Context, state multistep.StateBa return multistep.ActionContinue } -func (self *stepCreateInstance) Cleanup(state multistep.StateBag) { - config := state.Get("config").(xscommon.Config) +func (self *StepCreateInstance) Cleanup(state multistep.StateBag) { + config := state.Get("config").(Config) if config.ShouldKeepVM(state) { return } ui := state.Get("ui").(packer.Ui) - c := state.Get("client").(*xscommon.Connection) + c := state.Get("client").(*Connection) if self.instance != nil { ui.Say("Destroying VM") diff --git a/builder/xenserver/iso/builder.go b/builder/xenserver/iso/builder.go index 9a0d1d1..055d001 100644 --- a/builder/xenserver/iso/builder.go +++ b/builder/xenserver/iso/builder.go @@ -235,7 +235,7 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p VdiName: self.config.ISOName, VdiUuidKey: "isoname_vdi_uuid", }, - new(stepCreateInstance), + new(xscommon.StepCreateInstance), &xscommon.StepAttachVdi{ VdiUuidKey: "floppy_vdi_uuid", VdiType: xsclient.VbdTypeFloppy, From 7c4b652c17fbef51d61624d9fe5cea153011fed4 Mon Sep 17 00:00:00 2001 From: Dom Del Nano Date: Sat, 15 Apr 2023 14:58:13 -0700 Subject: [PATCH 4/5] Update StepCreateInstance to accommodate both ISO and XVA builders --- .../xenserver/common/step_create_instance.go | 70 ++++++++++--------- builder/xenserver/iso/builder.go | 4 +- 2 files changed, 41 insertions(+), 33 deletions(-) diff --git a/builder/xenserver/common/step_create_instance.go b/builder/xenserver/common/step_create_instance.go index 8f3611a..7ceba97 100644 --- a/builder/xenserver/common/step_create_instance.go +++ b/builder/xenserver/common/step_create_instance.go @@ -12,6 +12,10 @@ import ( ) type StepCreateInstance struct { + // The XVA builder assumes it will boot an instance with an OS installed on its disks + // while the ISO builder needs packer to create a disk for an OS to be installed on. + AssumePreInstalledOS bool + instance *xsclient.VMRef vdi *xsclient.VDIRef } @@ -100,43 +104,45 @@ func (self *StepCreateInstance) Run(ctx context.Context, state multistep.StateBa } } - err = c.GetClient().VM.RemoveFromOtherConfig(c.GetSessionRef(), instance, "disks") - if err != nil { - ui.Error(fmt.Sprintf("Error removing disks from VM other-config: %s", err.Error())) - return multistep.ActionHalt - } + if !self.AssumePreInstalledOS { + err = c.GetClient().VM.RemoveFromOtherConfig(c.GetSessionRef(), instance, "disks") + if err != nil { + ui.Error(fmt.Sprintf("Error removing disks from VM other-config: %s", err.Error())) + return multistep.ActionHalt + } - // Create VDI for the instance - sr, err := config.GetSR(c) + // Create VDI for the instance + sr, err := config.GetSR(c) - if err != nil { - ui.Error(fmt.Sprintf("Unable to get SR: %s", err.Error())) - return multistep.ActionHalt - } + if err != nil { + ui.Error(fmt.Sprintf("Unable to get SR: %s", err.Error())) + return multistep.ActionHalt + } - ui.Say(fmt.Sprintf("Using the following SR for the VM: %s", sr)) + ui.Say(fmt.Sprintf("Using the following SR for the VM: %s", sr)) - vdi, err := c.GetClient().VDI.Create(c.GetSessionRef(), xenapi.VDIRecord{ - NameLabel: "Packer-disk", - VirtualSize: int(config.DiskSize * 1024 * 1024), - Type: "user", - Sharable: false, - ReadOnly: false, - SR: sr, - OtherConfig: map[string]string{ - "temp": "temp", - }, - }) - if err != nil { - ui.Error(fmt.Sprintf("Unable to create packer disk VDI: %s", err.Error())) - return multistep.ActionHalt - } - self.vdi = &vdi + vdi, err := c.GetClient().VDI.Create(c.GetSessionRef(), xenapi.VDIRecord{ + NameLabel: "Packer-disk", + VirtualSize: int(config.DiskSize * 1024 * 1024), + Type: "user", + Sharable: false, + ReadOnly: false, + SR: sr, + OtherConfig: map[string]string{ + "temp": "temp", + }, + }) + if err != nil { + ui.Error(fmt.Sprintf("Unable to create packer disk VDI: %s", err.Error())) + return multistep.ActionHalt + } + self.vdi = &vdi - err = ConnectVdi(c, instance, vdi, xsclient.VbdTypeDisk) - if err != nil { - ui.Error(fmt.Sprintf("Unable to connect packer disk VDI: %s", err.Error())) - return multistep.ActionHalt + err = ConnectVdi(c, instance, vdi, xsclient.VbdTypeDisk) + if err != nil { + ui.Error(fmt.Sprintf("Unable to connect packer disk VDI: %s", err.Error())) + return multistep.ActionHalt + } } // Connect Network diff --git a/builder/xenserver/iso/builder.go b/builder/xenserver/iso/builder.go index 055d001..7a3c5cc 100644 --- a/builder/xenserver/iso/builder.go +++ b/builder/xenserver/iso/builder.go @@ -235,7 +235,9 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p VdiName: self.config.ISOName, VdiUuidKey: "isoname_vdi_uuid", }, - new(xscommon.StepCreateInstance), + &xscommon.StepCreateInstance{ + AssumePreInstalledOS: false, + }, &xscommon.StepAttachVdi{ VdiUuidKey: "floppy_vdi_uuid", VdiType: xsclient.VbdTypeFloppy, From 2fe14d14ac8ff4c09f4fe9031cef080bf02dbf18 Mon Sep 17 00:00:00 2001 From: Dom Del Nano Date: Sat, 15 Apr 2023 15:01:00 -0700 Subject: [PATCH 5/5] Update XVA builder to use shared StepCreateInstance step --- builder/xenserver/xva/builder.go | 4 +- .../xva/step_create_from_template.go | 191 ------------------ 2 files changed, 3 insertions(+), 192 deletions(-) delete mode 100644 builder/xenserver/xva/step_create_from_template.go diff --git a/builder/xenserver/xva/builder.go b/builder/xenserver/xva/builder.go index 0477172..e948413 100644 --- a/builder/xenserver/xva/builder.go +++ b/builder/xenserver/xva/builder.go @@ -141,7 +141,9 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p VdiName: self.config.ToolsIsoName, VdiUuidKey: "tools_vdi_uuid", }, - new(stepCreateFromTemplate), + &xscommon.StepCreateInstance{ + AssumePreInstalledOS: true, + }, new(stepImportInstance), &xscommon.StepAttachVdi{ VdiUuidKey: "floppy_vdi_uuid", diff --git a/builder/xenserver/xva/step_create_from_template.go b/builder/xenserver/xva/step_create_from_template.go deleted file mode 100644 index a4bd436..0000000 --- a/builder/xenserver/xva/step_create_from_template.go +++ /dev/null @@ -1,191 +0,0 @@ -package xva - -import ( - "context" - "fmt" - "log" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/hashicorp/packer-plugin-sdk/packer" - xsclient "github.com/terra-farm/go-xen-api-client" - xscommon "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common" -) - -type stepCreateFromTemplate struct { - instance *xsclient.VMRef -} - -func (self *stepCreateFromTemplate) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - - c := state.Get("client").(*xscommon.Connection) - config := state.Get("config").(xscommon.Config) - ui := state.Get("ui").(packer.Ui) - - ui.Say("Step: Create Instance from Template") - - if config.CloneTemplate == "" { - log.Println("Skipping creation from template - no `clone_template` configured.") - return multistep.ActionContinue - } - - // Get the template to clone from - - vms, err := c.GetClient().VM.GetByNameLabel(c.GetSessionRef(), 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 := c.GetClient().VM.Clone(c.GetSessionRef(), template, config.VMName) - if err != nil { - ui.Error(fmt.Sprintf("Error cloning VM: %s", err.Error())) - return multistep.ActionHalt - } - self.instance = &instance - - err = c.GetClient().VM.SetIsATemplate(c.GetSessionRef(), instance, false) - if err != nil { - ui.Error(fmt.Sprintf("Error setting is_a_template=false: %s", err.Error())) - return multistep.ActionHalt - } - - err = c.GetClient().VM.SetVCPUsMax(c.GetSessionRef(), instance, int(config.VCPUsMax)) - if err != nil { - ui.Error(fmt.Sprintf("Error setting VM VCPUs Max=%d: %s", config.VCPUsMax, err.Error())) - return multistep.ActionHalt - } - - err = c.GetClient().VM.SetVCPUsAtStartup(c.GetSessionRef(), instance, int(config.VCPUsAtStartup)) - if err != nil { - ui.Error(fmt.Sprintf("Error setting VM VCPUs At Startup=%d: %s", config.VCPUsAtStartup, err.Error())) - return multistep.ActionHalt - } - - memory := int(config.VMMemory * 1024 * 1024) - err = c.GetClient().VM.SetMemoryLimits(c.GetSessionRef(), instance, memory, memory, memory, memory) - if err != nil { - ui.Error(fmt.Sprintf("Error setting VM memory=%d: %s", memory, err.Error())) - return multistep.ActionHalt - } - - err = c.GetClient().VM.SetPlatform(c.GetSessionRef(), instance, config.PlatformArgs) - if err != nil { - ui.Error(fmt.Sprintf("Error setting VM platform: %s", err.Error())) - return multistep.ActionHalt - } - - err = c.GetClient().VM.SetNameDescription(c.GetSessionRef(), instance, config.VMDescription) - if err != nil { - ui.Error(fmt.Sprintf("Error setting VM description: %s", err.Error())) - return multistep.ActionHalt - } - - // Connect Network - - var network xsclient.NetworkRef - - if len(config.NetworkNames) == 0 { - // No network has be specified. Use the management interface - log.Println("No network name given, attempting to use management interface") - pifs, err := c.GetClient().PIF.GetAll(c.GetSessionRef()) - - if err != nil { - ui.Error(fmt.Sprintf("Error getting PIFs: %s", err.Error())) - return multistep.ActionHalt - } - - for _, pif := range pifs { - pif_rec, err := c.GetClient().PIF.GetRecord(c.GetSessionRef(), pif) - - if err != nil { - ui.Error(fmt.Sprintf("Error getting PIF record: %s", err.Error())) - return multistep.ActionHalt - } - - if pif_rec.Management { - network = pif_rec.Network - } - - } - - if string(network) == "" { - ui.Error("Error: couldn't find management network. Aborting.") - return multistep.ActionHalt - } - - log.Printf("Creating VIF on network '%s' on VM '%s'\n", network, instance) - _, err = xscommon.ConnectNetwork(c, network, instance, "0") - - if err != nil { - ui.Error(fmt.Sprintf("Failed to create VIF with error: %v", err)) - return multistep.ActionHalt - } - - } else { - log.Printf("Using provided network names: %v\n", config.NetworkNames) - // Look up each network by it's name label - for i, networkNameLabel := range config.NetworkNames { - networks, err := c.GetClient().Network.GetByNameLabel(c.GetSessionRef(), networkNameLabel) - - 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.", networkNameLabel)) - 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.", networkNameLabel)) - return multistep.ActionHalt - } - - //we need the VIF index string - vifIndexString := fmt.Sprintf("%d", i) - _, err = xscommon.ConnectNetwork(c, networks[0], instance, vifIndexString) - - if err != nil { - ui.Say(fmt.Sprintf("Failed to connect VIF with error: %v", err.Error())) - } - } - } - - instanceId, err := c.GetClient().VM.GetUUID(c.GetSessionRef(), instance) - 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 *stepCreateFromTemplate) Cleanup(state multistep.StateBag) { - config := state.Get("config").(xscommon.Config) - if config.ShouldKeepVM(state) { - return - } - - ui := state.Get("ui").(packer.Ui) - c := state.Get("client").(*xscommon.Connection) - - if self.instance != nil { - ui.Say("Destroying VM") - _ = c.GetClient().VM.HardShutdown(c.GetSessionRef(), *self.instance) // redundant, just in case - err := c.GetClient().VM.Destroy(c.GetSessionRef(), *self.instance) - if err != nil { - ui.Error(err.Error()) - } - } -}