diff --git a/builder/xenserver/builder.go b/builder/xenserver/builder.go index 3d63661..5f999d9 100644 --- a/builder/xenserver/builder.go +++ b/builder/xenserver/builder.go @@ -34,7 +34,8 @@ type config struct { HostPortMin uint `mapstructure:"host_port_min"` HostPortMax uint `mapstructure:"host_port_max"` - BootCommand []string `mapstructure:"boot_command"` + BootCommand []string `mapstructure:"boot_command"` + ShutdownCommand string `mapstructure:"shutdown_command"` RawBootWait string `mapstructure:"boot_wait"` BootWait time.Duration `` @@ -167,6 +168,7 @@ func (self *Builder) Prepare(raws ...interface{}) (params []string, retErr error "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, diff --git a/builder/xenserver/step_shutdown_and_export.go b/builder/xenserver/step_shutdown_and_export.go index 96d53d2..5cade20 100644 --- a/builder/xenserver/step_shutdown_and_export.go +++ b/builder/xenserver/step_shutdown_and_export.go @@ -10,6 +10,7 @@ import ( "io" "net/http" "os" + "time" ) type stepShutdownAndExport struct{} @@ -58,13 +59,56 @@ func (stepShutdownAndExport) Run(state multistep.StateBag) multistep.StepAction ui.Say("Step: Shutdown and export VPX") // Shutdown the VM - ui.Say("Shutting down the VM...") - err = instance.CleanShutdown() - if err != nil { - ui.Error(fmt.Sprintf("Could not shut down VM: %s", err.Error())) - return multistep.ActionHalt + success := func() bool { + if config.ShutdownCommand != "" { + ui.Say("Executing shutdown command...") + + _, err := execute_ssh_cmd(config.ShutdownCommand, config.HostIp, "22", config.Username, config.Password) + if err != nil { + ui.Error(fmt.Sprintf("Shutdown command failed: %s", err.Error())) + return false + } + + ui.Say("Waiting for VM to enter Halted state...") + + err = InterruptibleWait{ + Predicate: func() (bool, error) { + power_state, err := instance.GetPowerState() + return power_state == "Halted", err + }, + PredicateInterval: 5 * time.Second, + Timeout: 300 * time.Second, + }.Wait(state) + + if err != nil { + ui.Error(fmt.Sprintf("Error waiting for VM to halt: %s", err.Error())) + return false + } + + } else { + ui.Say("Attempting to cleanly shutdown the VM...") + + err = instance.CleanShutdown() + if err != nil { + ui.Error(fmt.Sprintf("Could not shut down VM: %s", err.Error())) + return false + } + + } + return true + }() + + if !success { + ui.Say("Forcing hard shutdown of the VM...") + err = instance.HardShutdown() + if err != nil { + ui.Error(fmt.Sprintf("Could not hard shut down VM -- giving up: %s", err.Error())) + return multistep.ActionHalt + } } + ui.Say("Successfully shut down VM") + switch config.ExportFormat { case "xva": // export the VM