Merge pull request #8 from chengsun/master

More bug fixes
This commit is contained in:
Rob Dobson 2014-12-15 21:45:03 +00:00
commit b67b96c82a
8 changed files with 191 additions and 93 deletions

View File

@ -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")
}

View File

@ -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 {

View File

@ -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

View 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) {}

View File

@ -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:

View File

@ -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")

View File

@ -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)
}
}

View File

@ -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
}