Merge pull request #1 from rdobson/himn
Use the host internal management interface to obtain VM IP
This commit is contained in:
commit
4746a397be
2
build.sh
2
build.sh
@ -1 +1 @@
|
|||||||
go build -o $GOPATH/packer-builder-xenserver main.go
|
go build -o $PACKERPATH/packer-builder-xenserver main.go
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
73
builder/xenserver/step_forward_port_over_ssh.go
Normal file
73
builder/xenserver/step_forward_port_over_ssh.go
Normal 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) {}
|
@ -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) {
|
|
||||||
}
|
|
52
builder/xenserver/step_get_vnc_port.go
Normal file
52
builder/xenserver/step_get_vnc_port.go
Normal 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
|
||||||
|
}
|
@ -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...")
|
||||||
|
123
builder/xenserver/step_start_on_himn.go
Normal file
123
builder/xenserver/step_start_on_himn.go
Normal 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
|
||||||
|
}
|
@ -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 {
|
||||||
|
@ -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,41 +52,12 @@ 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
26
examples/centos-6.4.conf
Normal 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>"
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}
|
49
examples/http/centos6-ks.cfg
Normal file
49
examples/http/centos6-ks.cfg
Normal 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
|
Loading…
Reference in New Issue
Block a user