diff --git a/builder/xenserver/common/step_http_server.go b/builder/xenserver/common/step_http_server.go index 26aa84d..069030c 100644 --- a/builder/xenserver/common/step_http_server.go +++ b/builder/xenserver/common/step_http_server.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" + "log" "net" "net/http" ) @@ -21,9 +22,30 @@ import ( // Produces: // http_port int - The port the HTTP server started on. type StepHTTPServer struct { + Chan chan<- string + l net.Listener } +type IPSnooper struct { + ch chan<- string + handler http.Handler +} + +func (snooper IPSnooper) ServeHTTP(resp http.ResponseWriter, req *http.Request) { + log.Printf("HTTP: %s %s %s", req.RemoteAddr, req.Method, req.URL) + ip, _, err := net.SplitHostPort(req.RemoteAddr) + if err == nil && ip != "" { + select { + case snooper.ch <- ip: + log.Printf("Remembering remote address '%s'", ip) + default: + // if ch is already full, don't block waiting to send the address, just drop it + } + } + snooper.handler.ServeHTTP(resp, req) +} + func (s *StepHTTPServer) Run(state multistep.StateBag) multistep.StepAction { config := state.Get("commonconfig").(CommonConfig) ui := state.Get("ui").(packer.Ui) @@ -45,7 +67,13 @@ func (s *StepHTTPServer) Run(state multistep.StateBag) multistep.StepAction { // Start the HTTP server and run it in the background fileServer := http.FileServer(http.Dir(config.HTTPDir)) - server := &http.Server{Addr: fmt.Sprintf(":%d", httpPort), Handler: fileServer} + server := &http.Server{ + Addr: fmt.Sprintf(":%d", httpPort), + Handler: IPSnooper{ + ch: s.Chan, + handler: fileServer, + }, + } go server.Serve(s.l) // Save the address into the state so it can be accessed in the future diff --git a/builder/xenserver/common/step_remove_devices.go b/builder/xenserver/common/step_remove_devices.go deleted file mode 100644 index 3213a3b..0000000 --- a/builder/xenserver/common/step_remove_devices.go +++ /dev/null @@ -1,40 +0,0 @@ -package common - -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 - } - - // 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) {} diff --git a/builder/xenserver/common/step_wait_for_http_request.go b/builder/xenserver/common/step_wait_for_http_request.go new file mode 100644 index 0000000..6e9cd7a --- /dev/null +++ b/builder/xenserver/common/step_wait_for_http_request.go @@ -0,0 +1,45 @@ +package common + +import ( + "fmt" + "time" + + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/packer" +) + +type StepWaitForHTTPRequest struct { + Chan <-chan string + Timeout time.Duration +} + +func (self *StepWaitForHTTPRequest) Run(state multistep.StateBag) multistep.StepAction { + ui := state.Get("ui").(packer.Ui) + + ui.Say("Step: Wait for install to complete.") + + timeout := time.After(self.Timeout) + var ip string + select { + case ip = <-self.Chan: + case <-timeout: + ui.Error("Timed out. Giving up waiting for installation to complete.") + return multistep.ActionHalt + } + + ui.Say(fmt.Sprintf("Got IP address '%s'", ip)) + state.Put("instance_ssh_address", ip) + + return multistep.ActionContinue +} + +func (self *StepWaitForHTTPRequest) Cleanup(state multistep.StateBag) {} + +func InstanceSSHIP(state multistep.StateBag) (string, error) { + ip := state.Get("instance_ssh_address").(string) + return ip, nil +} + +func InstanceSSHPort(state multistep.StateBag) (uint, error) { + return 22, nil +} diff --git a/builder/xenserver/iso/builder.go b/builder/xenserver/iso/builder.go index 42e7a15..0b1da28 100644 --- a/builder/xenserver/iso/builder.go +++ b/builder/xenserver/iso/builder.go @@ -186,6 +186,8 @@ func (self *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (pa state.Put("hook", hook) state.Put("ui", ui) + httpReqChan := make(chan string, 1) + //Build the steps steps := []multistep.Step{ &common.StepDownload{ @@ -202,7 +204,9 @@ func (self *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (pa &common.StepCreateFloppy{ Files: self.config.FloppyFiles, }, - new(xscommon.StepHTTPServer), + &xscommon.StepHTTPServer{ + Chan: httpReqChan, + }, &xscommon.StepUploadVdi{ VdiName: "Packer-floppy-disk", ImagePathFunc: func() string { @@ -250,18 +254,13 @@ func (self *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (pa &xscommon.StepTypeBootCommand{ Tpl: self.config.tpl, }, - new(stepWait), - &xscommon.StepDetachVdi{ - VdiUuidKey: "floppy_vdi_uuid", + &xscommon.StepWaitForHTTPRequest{ + Chan: httpReqChan, + Timeout: self.config.InstallTimeout, // @todo change this }, - &xscommon.StepDetachVdi{ - VdiUuidKey: "iso_vdi_uuid", - }, - new(xscommon.StepRemoveDevices), - new(xscommon.StepStartOnHIMN), &xscommon.StepForwardPortOverSSH{ - RemotePort: xscommon.HimnSSHPort, - RemoteDest: xscommon.HimnSSHIP, + RemotePort: xscommon.InstanceSSHPort, + RemoteDest: xscommon.InstanceSSHIP, HostPortMin: self.config.HostPortMin, HostPortMax: self.config.HostPortMax, ResultKey: "local_ssh_port", diff --git a/builder/xenserver/iso/step_wait.go b/builder/xenserver/iso/step_wait.go deleted file mode 100644 index 831985c..0000000 --- a/builder/xenserver/iso/step_wait.go +++ /dev/null @@ -1,52 +0,0 @@ -package iso - -import ( - "fmt" - "log" - "time" - - "github.com/mitchellh/multistep" - "github.com/mitchellh/packer/packer" - xscommon "github.com/rdobson/packer-builder-xenserver/builder/xenserver/common" -) - -type stepWait struct{} - -func (self *stepWait) Run(state multistep.StateBag) multistep.StepAction { - config := state.Get("config").(config) - ui := state.Get("ui").(packer.Ui) - client := state.Get("client").(xscommon.XenAPIClient) - - ui.Say("Step: Wait for install to complete.") - - 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 - } - - //Expect install to be configured to shutdown on completion - err = xscommon.InterruptibleWait{ - Predicate: func() (bool, error) { - log.Printf("Waiting for install to complete.") - power_state, err := instance.GetPowerState() - return power_state == "Halted", err - }, - PredicateInterval: 30 * time.Second, - Timeout: config.InstallTimeout, - }.Wait(state) - - if err != nil { - ui.Error(err.Error()) - ui.Error("Giving up waiting for installation to complete.") - return multistep.ActionHalt - } - - ui.Say("Install has completed. Moving on.") - - return multistep.ActionContinue -} - -func (self *stepWait) Cleanup(state multistep.StateBag) {} diff --git a/builder/xenserver/xva/builder.go b/builder/xenserver/xva/builder.go index 5c90536..7828f5b 100644 --- a/builder/xenserver/xva/builder.go +++ b/builder/xenserver/xva/builder.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "log" + "time" "github.com/mitchellh/multistep" "github.com/mitchellh/packer/common" @@ -112,6 +113,8 @@ func (self *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (pa state.Put("hook", hook) state.Put("ui", ui) + httpReqChan := make(chan string, 1) + //Build the steps steps := []multistep.Step{ &xscommon.StepPrepareOutputDir{ @@ -145,11 +148,26 @@ func (self *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (pa VdiUuidKey: "tools_vdi_uuid", VdiType: xscommon.CD, }, - new(xscommon.StepRemoveDevices), - new(xscommon.StepStartOnHIMN), + new(xscommon.StepStartVmPaused), + new(xscommon.StepGetVNCPort), &xscommon.StepForwardPortOverSSH{ - RemotePort: xscommon.HimnSSHPort, - RemoteDest: xscommon.HimnSSHIP, + RemotePort: xscommon.InstanceVNCPort, + RemoteDest: xscommon.InstanceVNCIP, + HostPortMin: self.config.HostPortMin, + HostPortMax: self.config.HostPortMax, + ResultKey: "local_vnc_port", + }, + new(xscommon.StepBootWait), + &xscommon.StepTypeBootCommand{ + Tpl: self.config.tpl, + }, + &xscommon.StepWaitForIP{ + 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",