Merge pull request #1 from rdobson/himn

Use the host internal management interface to obtain VM IP
This commit is contained in:
Rob Dobson 2014-11-20 18:57:59 +00:00
commit 4746a397be
14 changed files with 554 additions and 199 deletions

View File

@ -1 +1 @@
go build -o $GOPATH/packer-builder-xenserver main.go go build -o $PACKERPATH/packer-builder-xenserver main.go

View File

@ -8,7 +8,6 @@ import (
"log" "log"
"errors" "errors"
"time" "time"
"strings"
"os" "os"
commonssh "github.com/mitchellh/packer/common/ssh" commonssh "github.com/mitchellh/packer/common/ssh"
) )
@ -33,8 +32,8 @@ type config struct {
SrUuid string `mapstructure:"sr_uuid"` SrUuid string `mapstructure:"sr_uuid"`
NetworkUuid string `mapstructure:"network_uuid"` NetworkUuid string `mapstructure:"network_uuid"`
VncPortMin uint `mapstructure:"vnc_port_min"` HostPortMin uint `mapstructure:"host_port_min"`
VncPortMax uint `mapstructure:"vnc_port_max"` HostPortMax uint `mapstructure:"host_port_max"`
BootCommand []string `mapstructure:"boot_command"` BootCommand []string `mapstructure:"boot_command"`
RawBootWait string `mapstructure:"boot_wait"` RawBootWait string `mapstructure:"boot_wait"`
@ -92,12 +91,12 @@ func (self *Builder) Prepare (raws ...interface{}) (params []string, retErr erro
// Set default vaules // Set default vaules
if self.config.VncPortMin == 0 { if self.config.HostPortMin == 0 {
self.config.VncPortMin = 5900 self.config.HostPortMin = 5900
} }
if self.config.VncPortMax == 0 { if self.config.HostPortMax == 0 {
self.config.VncPortMax = 6000 self.config.HostPortMax = 6000
} }
if self.config.RawBootWait == "" { if self.config.RawBootWait == "" {
@ -259,11 +258,11 @@ func (self *Builder) Prepare (raws ...interface{}) (params []string, retErr erro
errs, errors.New("the HTTP min port must be less than the max")) errs, errors.New("the HTTP min port must be less than the max"))
} }
if self.config.VncPortMin > self.config.VncPortMax { if self.config.HostPortMin > self.config.HostPortMax {
errs = packer.MultiErrorAppend( errs = packer.MultiErrorAppend(
errs, errors.New("the VNC min port must be less than the max")) errs, errors.New("the host min port must be less than the max"))
} }
/*
if self.config.ISOChecksumType == "" { if self.config.ISOChecksumType == "" {
errs = packer.MultiErrorAppend( errs = packer.MultiErrorAppend(
errs, errors.New("The iso_checksum_type must be specified.")) errs, errors.New("The iso_checksum_type must be specified."))
@ -299,7 +298,7 @@ func (self *Builder) Prepare (raws ...interface{}) (params []string, retErr erro
errs, fmt.Errorf("Failed to parse the iso_url (%d): %s", i, err)) errs, fmt.Errorf("Failed to parse the iso_url (%d): %s", i, err))
} }
} }
*/
if len(errs.Errors) > 0 { if len(errs.Errors) > 0 {
retErr = errors.New(errs.Error()) retErr = errors.New(errs.Error())
} }
@ -331,6 +330,7 @@ func (self *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (pa
//Build the steps //Build the steps
steps := []multistep.Step{ steps := []multistep.Step{
/*
&common.StepDownload{ &common.StepDownload{
Checksum: self.config.ISOChecksum, Checksum: self.config.ISOChecksum,
ChecksumType: self.config.ISOChecksumType, ChecksumType: self.config.ISOChecksumType,
@ -338,17 +338,33 @@ func (self *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (pa
ResultKey: "iso_path", ResultKey: "iso_path",
Url: self.config.ISOUrls, Url: self.config.ISOUrls,
}, },
*/
new(stepPrepareOutputDir), new(stepPrepareOutputDir),
new(stepHTTPServer), new(stepHTTPServer),
new(stepUploadIso), //new(stepUploadIso),
new(stepCreateInstance), new(stepCreateInstance),
new(stepStartVmPaused), new(stepStartVmPaused),
new(stepForwardVncPortOverSsh), new(stepGetVNCPort),
&stepForwardPortOverSSH{
RemotePort: instanceVNCPort,
RemoteDest: instanceVNCIP,
HostPortMin: self.config.HostPortMin,
HostPortMax: self.config.HostPortMax,
ResultKey: "local_vnc_port",
},
new(stepBootWait), new(stepBootWait),
new(stepTypeBootCommand), new(stepTypeBootCommand),
new(stepWait), new(stepWait),
new(stepStartOnHIMN),
&stepForwardPortOverSSH{
RemotePort: himnSSHPort,
RemoteDest: himnSSHIP,
HostPortMin: self.config.HostPortMin,
HostPortMax: self.config.HostPortMax,
ResultKey: "local_ssh_port",
},
&common.StepConnectSSH{ &common.StepConnectSSH{
SSHAddress: sshAddress, SSHAddress: sshLocalAddress,
SSHConfig: sshConfig, SSHConfig: sshConfig,
SSHWaitTimeout: self.config.sshWaitTimeout, SSHWaitTimeout: self.config.sshWaitTimeout,
}, },

View File

@ -55,6 +55,11 @@ type VBD struct {
Client *XenAPIClient Client *XenAPIClient
} }
type VIF struct {
Ref string
Client *XenAPIClient
}
func (c *XenAPIClient) RPCCall (result interface{}, method string, params []interface{}) (err error) { func (c *XenAPIClient) RPCCall (result interface{}, method string, params []interface{}) (err error) {
fmt.Println(params) fmt.Println(params)
p := new(xmlrpc.Params) p := new(xmlrpc.Params)
@ -106,6 +111,7 @@ func (client *XenAPIClient) APICall (result *APIResult, method string, params ..
if result.Status != "Success" { if result.Status != "Success" {
fmt.Println("Encountered an API error: ", result.Status) fmt.Println("Encountered an API error: ", result.Status)
fmt.Println(res["ErrorDescription"]) fmt.Println(res["ErrorDescription"])
log.Fatal(res["ErrorDescription"])
return errors.New("API Error occurred") return errors.New("API Error occurred")
} else { } else {
result.Value = res["Value"] result.Value = res["Value"]
@ -147,6 +153,26 @@ func (client *XenAPIClient) GetNetworkByUuid (network_uuid string) (network *Net
return return
} }
func (client *XenAPIClient) GetNetworkByNameLabel (name_label string) (networks []*Network, err error) {
networks = make([]*Network, 0)
result := APIResult{}
err = client.APICall(&result, "network.get_by_name_label", name_label)
if err != nil {
return networks, err
}
for _, elem := range result.Value.([]interface{}) {
network := new(Network)
network.Ref = elem.(string)
network.Client = client
networks = append(networks, network)
}
return networks, nil
}
func (client *XenAPIClient) GetSRByUuid (sr_uuid string) (sr *SR, err error) { func (client *XenAPIClient) GetSRByUuid (sr_uuid string) (sr *SR, err error) {
sr = new(SR) sr = new(SR)
result := APIResult{} result := APIResult{}
@ -275,6 +301,24 @@ func (self *VM) GetVBDs() (vbds []VBD, err error) {
return vbds, nil return vbds, nil
} }
func (self *VM) GetVIFs() (vifs []VIF, err error) {
vifs = make([]VIF, 0)
result := APIResult{}
err = self.Client.APICall(&result, "VM.get_VIFs", self.Ref)
if err != nil {
return vifs, err
}
for _, elem := range result.Value.([]interface{}) {
vif := VIF{}
vif.Ref = elem.(string)
vif.Client = self.Client
vifs = append(vifs, vif)
}
return vifs, nil
}
func (self *VM) GetDisks() (vdis []*VDI, err error) { func (self *VM) GetDisks() (vdis []*VDI, err error) {
// Return just data disks (non-isos) // Return just data disks (non-isos)
vdis = make([]*VDI, 0) vdis = make([]*VDI, 0)
@ -405,7 +449,7 @@ func (self *VM) SetPlatform(params map[string]string) (err error) {
} }
func (self *VM) ConnectNetwork (network *Network, device string) (err error) { func (self *VM) ConnectNetwork (network *Network, device string) (vif *VIF, err error) {
// Create the VIF // Create the VIF
vif_rec := make(xmlrpc.Struct) vif_rec := make(xmlrpc.Struct)
@ -422,10 +466,14 @@ func (self *VM) ConnectNetwork (network *Network, device string) (err error) {
err = self.Client.APICall(&result, "VIF.create", vif_rec) err = self.Client.APICall(&result, "VIF.create", vif_rec)
if err != nil { if err != nil {
return err return nil, err
} }
return nil vif = new(VIF)
vif.Ref = result.Value.(string)
vif.Client = self.Client
return vif, nil
} }
// Setters // Setters
@ -469,6 +517,21 @@ func (self *SR) CreateVdi (name_label, size string) (vdi *VDI, err error) {
return return
} }
// Network associated functions
func (self *Network) GetAssignedIPs () (ip_map map[string]string, err error) {
ip_map = make(map[string]string, 0)
result := APIResult{}
err = self.Client.APICall(&result, "network.get_assigned_ips", self.Ref)
if err != nil {
return ip_map, err
}
for k, v := range result.Value.(xmlrpc.Struct) {
ip_map[k] = v.(string)
}
return ip_map, nil
}
// VBD associated functions // VBD associated functions
func (self *VBD) GetRecord () (record map[string]interface{}, err error) { func (self *VBD) GetRecord () (record map[string]interface{}, err error) {
record = make(map[string]interface{}) record = make(map[string]interface{})
@ -505,6 +568,17 @@ func (self *VBD) Eject () (err error) {
return nil return nil
} }
// VIF associated functions
func (self *VIF) Destroy () (err error) {
result := APIResult{}
err = self.Client.APICall(&result, "VIF.destroy", self.Ref)
if err != nil {
return err
}
return nil
}
// VDI associated functions // VDI associated functions
func (self *VDI) GetUuid () (vdi_uuid string, err error) { func (self *VDI) GetUuid () (vdi_uuid string, err error) {

View File

@ -6,6 +6,11 @@ import (
"github.com/mitchellh/multistep" "github.com/mitchellh/multistep"
commonssh "github.com/mitchellh/packer/common/ssh" commonssh "github.com/mitchellh/packer/common/ssh"
"github.com/mitchellh/packer/communicator/ssh" "github.com/mitchellh/packer/communicator/ssh"
"strings"
"log"
"bytes"
"net"
"io"
) )
func sshAddress(state multistep.StateBag) (string, error) { func sshAddress(state multistep.StateBag) (string, error) {
@ -14,6 +19,13 @@ func sshAddress(state multistep.StateBag) (string, error) {
return fmt.Sprintf("%s:%d", sshIP, sshHostPort), nil return fmt.Sprintf("%s:%d", sshIP, sshHostPort), nil
} }
func sshLocalAddress(state multistep.StateBag) (string, error) {
sshLocalPort := state.Get("local_ssh_port").(uint)
conn_str := fmt.Sprintf("%s:%d", "127.0.0.1", sshLocalPort)
log.Printf("sshLocalAddress: %s", conn_str)
return conn_str, nil
}
func sshConfig(state multistep.StateBag) (*gossh.ClientConfig, error) { func sshConfig(state multistep.StateBag) (*gossh.ClientConfig, error) {
config := state.Get("config").(config) config := state.Get("config").(config)
@ -37,3 +49,104 @@ func sshConfig(state multistep.StateBag) (*gossh.ClientConfig, error) {
Auth: auth, Auth: auth,
}, nil }, nil
} }
func execute_ssh_cmd (cmd, host, port, username, password string) (stdout string, err error) {
// Setup connection config
config := &gossh.ClientConfig {
User: username,
Auth: []gossh.AuthMethod {
gossh.Password(password),
},
}
client, err := gossh.Dial("tcp", host + ":" + port, config)
if err != nil {
return "", err
}
//Create session
session, err := client.NewSession()
if err != nil {
return "", err
}
defer session.Close()
var b bytes.Buffer
session.Stdout = &b
if err := session.Run(cmd); err != nil {
return "", err
}
session.Close()
return strings.Trim(b.String(), "\n"), nil
}
func forward(local_conn net.Conn, config *gossh.ClientConfig, server, remote_dest string, remote_port uint) {
ssh_client_conn, err := gossh.Dial("tcp", server + ":22", config)
if err != nil {
log.Fatalf("local ssh.Dial error: %s", err)
}
remote_loc := fmt.Sprintf("%s:%d", remote_dest, remote_port)
ssh_conn, err := ssh_client_conn.Dial("tcp", remote_loc)
if err != nil {
log.Fatalf("ssh.Dial error: %s", err)
}
go func() {
_, err = io.Copy(ssh_conn, local_conn)
if err != nil {
log.Fatalf("io.copy failed: %v", err)
}
}()
go func() {
_, err = io.Copy(local_conn, ssh_conn)
if err != nil {
log.Fatalf("io.copy failed: %v", err)
}
}()
}
func ssh_port_forward(local_port uint, remote_port uint, remote_dest, host, username, password string) (err error) {
config := &gossh.ClientConfig {
User: username,
Auth: []gossh.AuthMethod{
gossh.Password(password),
},
}
// Listen on a local port
local_listener, err := net.Listen("tcp",
fmt.Sprintf("%s:%d",
"127.0.0.1",
local_port))
if err != nil {
log.Fatalf("Local listen failed: %s", err)
return err
}
for {
local_connection, err := local_listener.Accept()
if err != nil {
log.Fatalf("Local accept failed: %s", err)
return err
}
// Forward to a remote port
go forward(local_connection, config, host, remote_dest, remote_port)
}
return nil
}

View File

@ -40,7 +40,7 @@ func (self *stepCreateInstance) Run(state multistep.StateBag) multistep.StepActi
if err != nil { if err != nil {
ui.Say(err.Error()) ui.Say(err.Error())
} }
err = instance.ConnectNetwork(network, "0") _, err = instance.ConnectNetwork(network, "0")
if err != nil { if err != nil {
ui.Say(err.Error()) ui.Say(err.Error())
@ -57,6 +57,7 @@ func (self *stepCreateInstance) Run(state multistep.StateBag) multistep.StepActi
// Stash the VM reference // Stash the VM reference
self.InstanceId, _ = instance.GetUuid() self.InstanceId, _ = instance.GetUuid()
state.Put("instance_uuid", self.InstanceId) state.Put("instance_uuid", self.InstanceId)
state.Put("instance", instance)
ui.Say(fmt.Sprintf("Created instance '%s'", self.InstanceId)) ui.Say(fmt.Sprintf("Created instance '%s'", self.InstanceId))
return multistep.ActionContinue return multistep.ActionContinue

View File

@ -0,0 +1,73 @@
package xenserver
import (
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
"net"
"fmt"
)
type stepForwardPortOverSSH struct {
RemotePort func (state multistep.StateBag) (uint, error)
RemoteDest func (state multistep.StateBag) (string, error)
HostPortMin uint
HostPortMax uint
ResultKey string
}
func (self *stepForwardPortOverSSH) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(config)
ui := state.Get("ui").(packer.Ui)
// Find a free local port:
log.Printf("Looking for an available port between %d and %d",
self.HostPortMin,
self.HostPortMax)
var sshHostPort uint
var foundPort bool
foundPort = false
for i := self.HostPortMin; i < self.HostPortMax; i++ {
sshHostPort = i
log.Printf("Trying port: %d", sshHostPort)
l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", sshHostPort))
if err == nil {
l.Close()
foundPort = true
break
}
}
if !foundPort {
log.Fatal("Error: unable to find free host port. Try providing a larger range")
return multistep.ActionHalt
}
ui.Say(fmt.Sprintf("Creating a local port forward over SSH on local port %d", sshHostPort))
remotePort, _ := self.RemotePort(state)
remoteDest, _ := self.RemoteDest(state)
go ssh_port_forward(sshHostPort, remotePort, remoteDest, config.HostIp, config.Username, config.Password)
ui.Say(fmt.Sprintf("Port forward setup. %d ---> %s:%d on %s", sshHostPort, remoteDest, remotePort, config.HostIp))
// Provide the local port to future steps.
state.Put(self.ResultKey, sshHostPort)
return multistep.ActionContinue
}
func (self *stepForwardPortOverSSH) Cleanup(state multistep.StateBag) {}

View File

@ -1,143 +0,0 @@
package xenserver
import (
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"fmt"
"code.google.com/p/go.crypto/ssh"
"bytes"
"net"
"io"
"strconv"
"log"
"strings"
)
type stepForwardVncPortOverSsh struct {}
func execute_ssh_cmd (cmd, host, port, username, password string) (stdout string, err error) {
// Setup connection config
config := &ssh.ClientConfig {
User: username,
Auth: []ssh.AuthMethod {
ssh.Password(password),
},
}
client, err := ssh.Dial("tcp", host + ":" + port, config)
if err != nil {
return "", err
}
//Create session
session, err := client.NewSession()
if err != nil {
return "", err
}
defer session.Close()
var b bytes.Buffer
session.Stdout = &b
if err := session.Run(cmd); err != nil {
return "", err
}
session.Close()
return strings.Trim(b.String(), "\n"), nil
}
func forward(local_conn net.Conn, config *ssh.ClientConfig, server, remote_port string) {
ssh_client_conn, err := ssh.Dial("tcp", server + ":22", config)
if err != nil {
log.Fatalf("local ssh.Dial error: %s", err)
}
ssh_conn, err := ssh_client_conn.Dial("tcp", "127.0.0.1:" + remote_port)
if err != nil {
log.Fatalf("ssh.Dial error: %s", err)
}
go func() {
_, err = io.Copy(ssh_conn, local_conn)
if err != nil {
log.Fatalf("io.copy failed: %v", err)
}
}()
go func() {
_, err = io.Copy(local_conn, ssh_conn)
if err != nil {
log.Fatalf("io.copy failed: %v", err)
}
}()
}
func ssh_port_forward(local_port, remote_port, host, username, password string) (err error) {
config := &ssh.ClientConfig {
User: username,
Auth: []ssh.AuthMethod{
ssh.Password(password),
},
}
// Listen on a local port
local_listener, err := net.Listen("tcp", "127.0.0.1:" + local_port)
if err != nil {
log.Fatalf("Local listen failed: %s", err)
return err
}
for {
local_connection, err := local_listener.Accept()
if err != nil {
log.Fatalf("Local accept failed: %s", err)
return err
}
// Forward to a remote port
go forward(local_connection, config, host, remote_port)
}
return nil
}
func (self *stepForwardVncPortOverSsh) Run(state multistep.StateBag) multistep.StepAction {
// client := state.Get("client").(XenAPIClient)
config := state.Get("config").(config)
ui := state.Get("ui").(packer.Ui)
ui.Say("Step: forward the instances VNC port over SSH")
domid := state.Get("domid").(string)
cmd := fmt.Sprintf("xenstore-read /local/domain/%s/console/vnc-port", domid)
remote_vncport, _ := execute_ssh_cmd(cmd, config.HostIp, "22", config.Username, config.Password)
ui.Say("The VNC port is " + remote_vncport)
// Just take the min port for the moment
state.Put("local_vnc_port", config.VncPortMin)
local_port := strconv.Itoa(int(config.VncPortMin))
ui.Say("About to setup SSH Port forward setup on local port " + local_port)
go ssh_port_forward(local_port, remote_vncport, config.HostIp, config.Username, config.Password)
ui.Say("Port forward setup.")
return multistep.ActionContinue
}
func (self *stepForwardVncPortOverSsh) Cleanup(state multistep.StateBag) {
}

View File

@ -0,0 +1,52 @@
package xenserver
import (
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"fmt"
"strconv"
"log"
)
type stepGetVNCPort struct {}
func (self *stepGetVNCPort) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(config)
ui := state.Get("ui").(packer.Ui)
ui.Say("Step: forward the instances VNC port over SSH")
domid := state.Get("domid").(string)
cmd := fmt.Sprintf("xenstore-read /local/domain/%s/console/vnc-port", domid)
remote_vncport, _ := execute_ssh_cmd(cmd, config.HostIp, "22", config.Username, config.Password)
remote_port, err := strconv.ParseUint(remote_vncport, 10, 16)
if err != nil {
log.Fatal(err.Error())
log.Fatal(fmt.Sprintf("Unable to convert '%s' to an int", remote_vncport))
return multistep.ActionHalt
}
state.Put("instance_vnc_port", uint(remote_port))
return multistep.ActionContinue
}
func (self *stepGetVNCPort) Cleanup(state multistep.StateBag) {
}
func instanceVNCPort (state multistep.StateBag) (uint, error) {
vncPort := state.Get("instance_vnc_port").(uint)
return vncPort, nil
}
func instanceVNCIP (state multistep.StateBag) (string, error) {
// The port is in Dom0, so we want to forward from localhost
return "127.0.0.1", nil
}

View File

@ -34,7 +34,7 @@ func (stepPrepareOutputDir) Cleanup(state multistep.StateBag) {
_, halted := state.GetOk(multistep.StateHalted) _, halted := state.GetOk(multistep.StateHalted)
if cancelled || halted { if cancelled || halted {
config := state.Get("config").(*config) config := state.Get("config").(config)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
ui.Say("Deleting output directory...") ui.Say("Deleting output directory...")

View File

@ -0,0 +1,123 @@
package xenserver
import (
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"time"
"log"
"fmt"
)
type stepStartOnHIMN struct{}
/*
* This step starts the installed guest on the Host Internal Management Network
* as there exists an API to obtain the IP allocated to the VM by XAPI.
* This in turn will allow Packer to SSH into the VM, provided NATing has been
* enabled on the host.
*
*/
func (self *stepStartOnHIMN) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
client := state.Get("client").(XenAPIClient)
config := state.Get("config").(config)
ui.Say("Step: Start VM on the Host Internal Mangement Network")
instance := state.Get("instance").(*VM)
// Find the HIMN Ref
networks, err := client.GetNetworkByNameLabel("Host internal management network")
if err != nil || len(networks) == 0 {
log.Fatal("Unable to find a host internal management network")
log.Fatal(err.Error())
return multistep.ActionHalt
}
himn := networks[0]
// Create a VIF for the HIMN
himn_vif, err := instance.ConnectNetwork(himn, "0")
if err != nil {
log.Fatal("Error creating VIF")
log.Fatal(err.Error())
return multistep.ActionHalt
}
// Start the VM
instance.Start(false, false)
var himn_iface_ip string = ""
// Obtain the allocated IP
for i:=0; i < 10; i++ {
ips, _ := himn.GetAssignedIPs()
log.Printf("IPs: %s", ips)
log.Printf("Ref: %s", instance.Ref)
//Check for instance.Ref in map
if vm_ip, ok := ips[himn_vif.Ref]; ok {
ui.Say("Found the VM's IP " + vm_ip)
himn_iface_ip = vm_ip
break
}
ui.Say("Wait for IP address...")
time.Sleep(10*time.Second)
}
if himn_iface_ip != "" {
state.Put("himn_ssh_address", himn_iface_ip)
ui.Say("Stored VM's IP " + himn_iface_ip)
} else {
log.Fatal("Unable to find an IP on the Host-internal management interface")
return multistep.ActionHalt
}
// Wait for the VM to boot, and check we can ping this interface
ping_cmd := fmt.Sprintf("ping -c 1 %s", himn_iface_ip)
err = nil
for i:=0; i < 30; i++ {
ui.Message(fmt.Sprintf("Attempting to ping interface: %s", ping_cmd))
_, err := execute_ssh_cmd(ping_cmd, config.HostIp, "22", config.Username, config.Password)
if err == nil {
ui.Message("Ping success! Continuing...")
break
}
time.Sleep(10 * time.Second)
}
if err != nil {
log.Fatal("Unable to ping interface. Something is wrong. Has the VM not booted?")
log.Fatal(err.Error())
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (self *stepStartOnHIMN) Cleanup(state multistep.StateBag) {}
func himnSSHIP (state multistep.StateBag) (string, error) {
ip := state.Get("himn_ssh_address").(string)
return ip, nil
}
func himnSSHPort (state multistep.StateBag) (uint, error) {
return 22, nil
}

View File

@ -33,6 +33,7 @@ func (self *stepTypeBootCommand) Run (state multistep.StateBag) multistep.StepAc
// Connect to the local VNC port as we have set up a SSH port forward // Connect to the local VNC port as we have set up a SSH port forward
ui.Say("Connecting to the VM over VNC") ui.Say("Connecting to the VM over VNC")
ui.Message(fmt.Sprintf("Using local port: %d", vnc_port))
net_conn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", vnc_port)) net_conn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", vnc_port))
if err != nil { if err != nil {

View File

@ -5,7 +5,6 @@ import (
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"time" "time"
"reflect" "reflect"
"github.com/nilshell/xmlrpc"
) )
@ -34,7 +33,7 @@ func (self *stepWait) Run(state multistep.StateBag) multistep.StepAction {
} }
} }
//Eject ISO from drive and start VM // Eject ISO from drive
vbds, _ := instance.GetVBDs() vbds, _ := instance.GetVBDs()
for _, vbd := range vbds { for _, vbd := range vbds {
rec, _ := vbd.GetRecord() rec, _ := vbd.GetRecord()
@ -53,42 +52,13 @@ func (self *stepWait) Run(state multistep.StateBag) multistep.StepAction {
} }
} }
ui.Say("Starting VM...") // Destroy all connected VIFs
instance.Start(false, false) vifs, _ := instance.GetVIFs()
for _, vif := range vifs {
ui.Message("Destroying VIF " + vif.Ref)
vm_ip := "" vif.Destroy()
for i:=0; i < 10; i++ {
ref, _ := instance.GetGuestMetricsRef()
if ref != "OpaqueRef:NULL" {
metrics, _ := instance.GetGuestMetrics()
// todo: xmlrpc shouldn't be needed here
networks := metrics["networks"].(xmlrpc.Struct)
for k, v := range networks {
if k == "0/ip" && v.(string) != "" {
vm_ip = v.(string)
break
}
}
}
// Check if an IP has been returned yet
if vm_ip != "" {
break
}
ui.Say("Wait for IP address...")
time.Sleep(10*time.Second)
} }
// Pass on the VM's IP
state.Put("ssh_address", vm_ip)
ui.Say("Found the VM's IP " + vm_ip)
return multistep.ActionContinue return multistep.ActionContinue
} }

26
examples/centos-6.4.conf Normal file
View File

@ -0,0 +1,26 @@
{
"builders": [{
"type": "xenserver",
"username": "root",
"password": "hostpassword",
"host_ip": "10.81.2.105",
"instance_name": "packer-centos-6-4",
"root_disk_size": "5000000000",
"clone_template": "b31a4a3a-a44f-68df-bb53-33529a9db3b7",
"iso_uuid": "3079a3f4-a49b-4f65-8afd-c2351b2c3399",
"sr_uuid": "88328079-d58d-2b43-1f28-982e0e60ec23",
"network_uuid": "02021c25-34b8-2a32-3f55-1d6338e869e2",
"iso_url": "http://mirrors.usc.edu/pub/linux/distributions/centos/6.4/isos/x86_64/CentOS-6.4-x86_64-minimal.iso",
"iso_checksum_type": "md5",
"iso_checksum": "4a5fa01c81cc300f4729136e28ebe600",
"http_directory": "http",
"local_ip": "10.80.3.223",
"ssh_username": "root",
"ssh_password": "vmpassword",
"boot_command":
[
"<tab><wait>",
" ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/centos6-ks.cfg<enter>"
]
}]
}

View File

@ -0,0 +1,49 @@
install
cdrom
lang en_US.UTF-8
keyboard us
unsupported_hardware
network --bootproto=dhcp
rootpw --iscrypted $1$wwD0QIub$WNz1RShXh1UapQzmpF4/c1
firewall --disabled
authconfig --enableshadow --passalgo=sha512
selinux --permissive
timezone UTC
bootloader --location=mbr
text
skipx
zerombr
clearpart --all --initlabel
autopart
auth --useshadow --enablemd5
firstboot --disabled
#reboot
poweroff
%packages --ignoremissing
@Base
@Core
@Development Tools
openssl-devel
readline-devel
zlib-devel
kernel-devel
vim
wget
%end
%post
yum -y update
# update root certs
wget -O/etc/pki/tls/certs/ca-bundle.crt http://curl.haxx.se/ca/cacert.pem
# vagrant
groupadd vagrant -g 999
useradd vagrant -g vagrant -G wheel -u 900 -s /bin/bash
echo "vagrant" | passwd --stdin vagrant
# sudo
echo "vagrant ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
sed -i "s/^.*requiretty/#Defaults requiretty/" /etc/sudoers
%end