commit
b67b96c82a
@ -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,
|
||||
@ -199,6 +201,11 @@ func (self *Builder) Prepare(raws ...interface{}) (params []string, retErr error
|
||||
}
|
||||
*/
|
||||
|
||||
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(
|
||||
@ -386,6 +393,7 @@ func (self *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (pa
|
||||
new(stepBootWait),
|
||||
new(stepTypeBootCommand),
|
||||
new(stepWait),
|
||||
new(stepRemoveDevices),
|
||||
new(stepStartOnHIMN),
|
||||
&stepForwardPortOverSSH{
|
||||
RemotePort: himnSSHPort,
|
||||
@ -423,6 +431,14 @@ func (self *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (pa
|
||||
return artifact, nil
|
||||
}
|
||||
|
||||
func (self *Builder) Cancel() {
|
||||
if self.runner != nil {
|
||||
log.Println("Cancelling the step runner...")
|
||||
self.runner.Cancel()
|
||||
}
|
||||
fmt.Println("Cancelling the builder")
|
||||
}
|
||||
|
||||
// all steps should check config.ShouldKeepInstance first before cleaning up
|
||||
func (cfg config) ShouldKeepInstance(state multistep.StateBag) bool {
|
||||
switch cfg.KeepInstance {
|
||||
@ -440,10 +456,26 @@ func (cfg config) ShouldKeepInstance(state multistep.StateBag) bool {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Builder) Cancel() {
|
||||
if self.runner != nil {
|
||||
log.Println("Cancelling the step runner...")
|
||||
self.runner.Cancel()
|
||||
func (config config) GetSR(client XenAPIClient) (*SR, error) {
|
||||
if config.SrName == "" {
|
||||
// Find the default SR
|
||||
return client.GetDefaultSR()
|
||||
|
||||
} else {
|
||||
// Use the provided name label to find the SR to use
|
||||
srs, err := client.GetSRByNameLabel(config.SrName)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(srs) == 0:
|
||||
return nil, fmt.Errorf("Couldn't find a SR with the specified name-label '%s'", config.SrName)
|
||||
case len(srs) > 1:
|
||||
return nil, fmt.Errorf("Found more than one SR with the name '%s'. The name must be unique", config.SrName)
|
||||
}
|
||||
|
||||
return srs[0], nil
|
||||
}
|
||||
fmt.Println("Cancelling the builder")
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ func (wait InterruptibleWait) Wait(state multistep.StateBag) error {
|
||||
|
||||
select {
|
||||
case <-time.After(wait.PredicateInterval):
|
||||
// do nothing; loop again
|
||||
continue
|
||||
case <-stopWaiting:
|
||||
return
|
||||
}
|
||||
@ -63,14 +63,17 @@ func (wait InterruptibleWait) Wait(state multistep.StateBag) error {
|
||||
for {
|
||||
// wait for either install to complete/error,
|
||||
// an interrupt to come through, or a timeout to occur
|
||||
|
||||
if _, ok := state.GetOk(multistep.StateCancelled); ok {
|
||||
return InterruptedError{}
|
||||
}
|
||||
|
||||
select {
|
||||
case result := <-predicateResult:
|
||||
return result.err
|
||||
|
||||
case <-time.After(1 * time.Second):
|
||||
if _, ok := state.GetOk(multistep.StateCancelled); ok {
|
||||
return InterruptedError{}
|
||||
}
|
||||
continue
|
||||
|
||||
case <-timeout:
|
||||
if wait.Predicate != nil {
|
||||
|
@ -61,37 +61,11 @@ func (self *stepCreateInstance) Run(state multistep.StateBag) multistep.StepActi
|
||||
}
|
||||
|
||||
// Create VDI for the instance
|
||||
var sr *SR
|
||||
|
||||
if config.SrName == "" {
|
||||
// Find the default SR
|
||||
default_sr, err := client.GetDefaultSR()
|
||||
sr = default_sr
|
||||
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error getting default SR: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
} else {
|
||||
// Use the provided name label to find the SR to use
|
||||
srs, err := client.GetSRByNameLabel(config.SrName)
|
||||
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error getting default SR: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(srs) == 0:
|
||||
ui.Error(fmt.Sprintf("Couldn't find a SR with the specified name-label '%s'. Aborting.", config.SrName))
|
||||
return multistep.ActionHalt
|
||||
case len(srs) > 1:
|
||||
ui.Error(fmt.Sprintf("Found more than one SR with the name '%s'. The name must be unique. Aborting.", config.SrName))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
sr = srs[0]
|
||||
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", config.RootDiskSize)
|
||||
@ -207,7 +181,6 @@ func (self *stepCreateInstance) Run(state multistep.StateBag) multistep.StepActi
|
||||
}
|
||||
|
||||
state.Put("instance_uuid", instanceId)
|
||||
state.Put("instance", instance)
|
||||
ui.Say(fmt.Sprintf("Created instance '%s'", instanceId))
|
||||
|
||||
return multistep.ActionContinue
|
||||
|
68
builder/xenserver/step_remove_devices.go
Normal file
68
builder/xenserver/step_remove_devices.go
Normal file
@ -0,0 +1,68 @@
|
||||
package xenserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
)
|
||||
|
||||
type stepRemoveDevices struct{}
|
||||
|
||||
func (self *stepRemoveDevices) Run(state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
client := state.Get("client").(XenAPIClient)
|
||||
|
||||
ui.Say("Step: Remove devices from VM")
|
||||
|
||||
instance_id := state.Get("instance_uuid").(string)
|
||||
instance, err := client.GetVMByUuid(instance_id)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Could not get VM from UUID %s", instance_id))
|
||||
ui.Error(err.Error())
|
||||
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 {
|
||||
ui.Error(fmt.Sprintf("Could not get VIFs"))
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
for _, vif := range vifs {
|
||||
ui.Message("Destroying VIF " + vif.Ref)
|
||||
vif.Destroy()
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (self *stepRemoveDevices) Cleanup(state multistep.StateBag) {}
|
@ -10,6 +10,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
type stepShutdownAndExport struct{}
|
||||
@ -58,26 +59,74 @@ 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
|
||||
|
||||
export_url := fmt.Sprintf("https://%s/export?vm=%s&session_id=%s",
|
||||
export_url := fmt.Sprintf("https://%s/export?uuid=%s&session_id=%s",
|
||||
client.Host,
|
||||
instance_uuid,
|
||||
client.Session.(string),
|
||||
)
|
||||
|
||||
export_filename := fmt.Sprintf("%s/%s.xva", config.OutputDir, config.InstanceName)
|
||||
|
||||
ui.Say("Getting XVA " + export_url)
|
||||
downloadFile(export_url, export_filename)
|
||||
err = downloadFile(export_url, export_filename)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Could not download XVA: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
case "vdi_raw":
|
||||
// export the disks
|
||||
@ -105,8 +154,13 @@ func (stepShutdownAndExport) Run(state multistep.StateBag) multistep.StepAction
|
||||
)
|
||||
|
||||
disk_export_filename := fmt.Sprintf("%s/%s.raw", config.OutputDir, disk_uuid)
|
||||
|
||||
ui.Say("Getting VDI " + disk_export_url)
|
||||
downloadFile(disk_export_url, disk_export_filename)
|
||||
err = downloadFile(disk_export_url, disk_export_filename)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Could not download VDI: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
|
@ -27,7 +27,12 @@ func (self *stepStartOnHIMN) Run(state multistep.StateBag) multistep.StepAction
|
||||
|
||||
ui.Say("Step: Start VM on the Host Internal Mangement Network")
|
||||
|
||||
instance := state.Get("instance").(*VM)
|
||||
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
|
||||
}
|
||||
|
||||
// Find the HIMN Ref
|
||||
networks, err := client.GetNetworkByNameLabel("Host internal management network")
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
const KeyLeftShift uint = 0xFFE1
|
||||
|
||||
type bootCommandTemplateData struct {
|
||||
Name string
|
||||
HTTPIP string
|
||||
HTTPPort uint
|
||||
}
|
||||
@ -59,6 +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.LocalIp,
|
||||
http_port,
|
||||
}
|
||||
@ -179,7 +181,7 @@ func vncSendString(c *vnc.ClientConn, original string) {
|
||||
c.KeyEvent(uint32(KeyLeftShift), false)
|
||||
}
|
||||
|
||||
// qemu is picky, so no matter what, wait a small period
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
// no matter what, wait a small period
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -27,7 +28,7 @@ func (self *stepWait) Run(state multistep.StateBag) multistep.StepAction {
|
||||
//Expect install to be configured to shutdown on completion
|
||||
err = InterruptibleWait{
|
||||
Predicate: func() (bool, error) {
|
||||
ui.Say("Waiting for install to complete.")
|
||||
log.Printf("Waiting for install to complete.")
|
||||
power_state, err := instance.GetPowerState()
|
||||
return power_state == "Halted", err
|
||||
},
|
||||
@ -43,46 +44,6 @@ func (self *stepWait) Run(state multistep.StateBag) multistep.StepAction {
|
||||
|
||||
ui.Say("Install has completed. Moving on.")
|
||||
|
||||
// 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 {
|
||||
ui.Error(fmt.Sprintf("Could not get VIFs"))
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
for _, vif := range vifs {
|
||||
ui.Message("Destroying VIF " + vif.Ref)
|
||||
vif.Destroy()
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user