Merge pull request #5 from chengsun/master

Minor cosmetic changes
This commit is contained in:
Rob Dobson 2014-12-08 18:34:55 +00:00
commit 50403a3ed6
16 changed files with 1469 additions and 1532 deletions

View File

@ -79,7 +79,7 @@ To get you started, there is an example config file which you can use `examples/
"host_ip": "10.81.2.105",
"instance_name": "packer-centos-6-4",
"instance_memory": "2048000000",
"root_disk_size": "5000000000",
"root_disk_size": "40000000000",
"iso_name": "CentOS-6.4-x86_64-minimal.iso",
"http_directory": "http",
"local_ip": "10.80.3.223",

View File

@ -1,57 +1,55 @@
package xenserver
import (
"fmt"
"os"
"github.com/mitchellh/packer/packer"
"path/filepath"
"fmt"
"github.com/mitchellh/packer/packer"
"os"
"path/filepath"
)
type LocalArtifact struct {
dir string
f []string
dir string
f []string
}
func NewArtifact(dir string) (packer.Artifact, error) {
files := make([]string, 0, 1)
visit := func(path string, info os.FileInfo, err error) error {
if !info.IsDir() {
files = append(files, path)
}
return err
}
if err := filepath.Walk(dir, visit); err != nil {
return nil, err
}
files := make([]string, 0, 1)
visit := func(path string, info os.FileInfo, err error) error {
if !info.IsDir() {
files = append(files, path)
}
return err
}
if err := filepath.Walk(dir, visit); err != nil {
return nil, err
}
return &LocalArtifact{
dir: dir,
f: files,
}, nil
return &LocalArtifact{
dir: dir,
f: files,
}, nil
}
func (*LocalArtifact) BuilderId() string {
return BuilderId
return BuilderId
}
func (a *LocalArtifact) Files() []string {
return a.f
return a.f
}
func (*LocalArtifact) Id() string {
return "VM"
return "VM"
}
func (a *LocalArtifact) String() string {
return fmt.Sprintf("VM files in directory: %s", a.dir)
return fmt.Sprintf("VM files in directory: %s", a.dir)
}
func (a *LocalArtifact) State(name string) interface{} {
return nil
return nil
}
func (a *LocalArtifact) Destroy() error {
return os.RemoveAll(a.dir)
return os.RemoveAll(a.dir)
}

View File

@ -1,387 +1,379 @@
package xenserver
import (
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/common"
"fmt"
"log"
"errors"
"time"
"os"
commonssh "github.com/mitchellh/packer/common/ssh"
"errors"
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/common"
commonssh "github.com/mitchellh/packer/common/ssh"
"github.com/mitchellh/packer/packer"
"log"
"os"
"time"
)
// Set the unique ID for this builder
const BuilderId = "packer.xenserver"
type config struct {
common.PackerConfig `mapstructure:",squash"`
common.PackerConfig `mapstructure:",squash"`
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
HostIp string `mapstructure:"host_ip"`
IsoUrl string `mapstructure:"iso_url"`
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
HostIp string `mapstructure:"host_ip"`
IsoUrl string `mapstructure:"iso_url"`
InstanceName string `mapstructure:"instance_name"`
InstanceMemory string `mapstructure:"instance_memory"`
RootDiskSize string `mapstructure:"root_disk_size"`
CloneTemplate string `mapstructure:"clone_template"`
IsoName string `mapstructure:"iso_name"`
SrName string `mapstructure:"sr_name"`
NetworkName string `mapstructure:"network_name"`
InstanceName string `mapstructure:"instance_name"`
InstanceMemory string `mapstructure:"instance_memory"`
RootDiskSize string `mapstructure:"root_disk_size"`
CloneTemplate string `mapstructure:"clone_template"`
IsoName string `mapstructure:"iso_name"`
SrName string `mapstructure:"sr_name"`
NetworkName string `mapstructure:"network_name"`
HostPortMin uint `mapstructure:"host_port_min"`
HostPortMax uint `mapstructure:"host_port_max"`
HostPortMin uint `mapstructure:"host_port_min"`
HostPortMax uint `mapstructure:"host_port_max"`
BootCommand []string `mapstructure:"boot_command"`
RawBootWait string `mapstructure:"boot_wait"`
BootCommand []string `mapstructure:"boot_command"`
RawBootWait string `mapstructure:"boot_wait"`
BootWait time.Duration ``
sshWaitTimeout time.Duration ``
BootWait time.Duration ``
SSHWaitTimeout time.Duration ``
ISOChecksum string `mapstructure:"iso_checksum"`
ISOChecksumType string `mapstructure:"iso_checksum_type"`
ISOUrls []string `mapstructure:"iso_urls"`
ISOUrl string `mapstructure:"iso_url"`
ISOChecksum string `mapstructure:"iso_checksum"`
ISOChecksumType string `mapstructure:"iso_checksum_type"`
ISOUrls []string `mapstructure:"iso_urls"`
ISOUrl string `mapstructure:"iso_url"`
HTTPDir string `mapstructure:"http_directory"`
HTTPPortMin uint `mapstructure:"http_port_min"`
HTTPPortMax uint `mapstructure:"http_port_max"`
HTTPDir string `mapstructure:"http_directory"`
HTTPPortMin uint `mapstructure:"http_port_min"`
HTTPPortMax uint `mapstructure:"http_port_max"`
LocalIp string `mapstructure:"local_ip"`
PlatformArgs map[string]string `mapstructure:"platform_args"`
LocalIp string `mapstructure:"local_ip"`
PlatformArgs map[string]string `mapstructure:"platform_args"`
RawSSHWaitTimeout string `mapstructure:"ssh_wait_timeout"`
RawSSHWaitTimeout string `mapstructure:"ssh_wait_timeout"`
SSHPassword string `mapstructure:"ssh_password"`
SSHUser string `mapstructure:"ssh_username"`
SSHKeyPath string `mapstructure:"ssh_key_path"`
SSHPassword string `mapstructure:"ssh_password"`
SSHUser string `mapstructure:"ssh_username"`
SSHKeyPath string `mapstructure:"ssh_key_path"`
OutputDir string `mapstructure:"output_directory"`
OutputDir string `mapstructure:"output_directory"`
tpl *packer.ConfigTemplate
tpl *packer.ConfigTemplate
}
type Builder struct {
config config
runner multistep.Runner
config config
runner multistep.Runner
}
func (self *Builder) Prepare(raws ...interface{}) (params []string, retErr error) {
func (self *Builder) Prepare (raws ...interface{}) (params []string, retErr error) {
md, err := common.DecodeConfig(&self.config, raws...)
if err != nil {
return nil, err
}
md, err := common.DecodeConfig(&self.config, raws...)
if err != nil {
return nil, err
}
errs := common.CheckUnusedConfig(md)
if errs == nil {
errs = &packer.MultiError{}
}
errs := common.CheckUnusedConfig(md)
if errs == nil {
errs = &packer.MultiError{}
}
self.config.tpl, err = packer.NewConfigTemplate()
self.config.tpl, err = packer.NewConfigTemplate()
if err != nil {
return nil, err
}
if err != nil {
return nil, err
}
// Set default vaules
// Set default vaules
if self.config.HostPortMin == 0 {
self.config.HostPortMin = 5900
}
if self.config.HostPortMin == 0 {
self.config.HostPortMin = 5900
}
if self.config.HostPortMax == 0 {
self.config.HostPortMax = 6000
}
if self.config.HostPortMax == 0 {
self.config.HostPortMax = 6000
}
if self.config.RawBootWait == "" {
self.config.RawBootWait = "5s"
}
if self.config.RawBootWait == "" {
self.config.RawBootWait = "5s"
}
if self.config.HTTPPortMin == 0 {
self.config.HTTPPortMin = 8000
}
if self.config.HTTPPortMin == 0 {
self.config.HTTPPortMin = 8000
}
if self.config.HTTPPortMax == 0 {
self.config.HTTPPortMax = 9000
}
if self.config.HTTPPortMax == 0 {
self.config.HTTPPortMax = 9000
}
if self.config.RawSSHWaitTimeout == "" {
self.config.RawSSHWaitTimeout = "200m"
}
if self.config.RawSSHWaitTimeout == "" {
self.config.RawSSHWaitTimeout = "200m"
}
if self.config.OutputDir == "" {
self.config.OutputDir = fmt.Sprintf("output-%s", self.config.PackerBuildName)
}
if self.config.OutputDir == "" {
self.config.OutputDir = fmt.Sprintf("output-%s", self.config.PackerBuildName)
}
templates := map[string]*string{
"username": &self.config.Username,
"password": &self.config.Password,
"host_ip": &self.config.HostIp,
"iso_url": &self.config.IsoUrl,
"instance_name": &self.config.InstanceName,
"instance_memory": &self.config.InstanceMemory,
"root_disk_size": &self.config.RootDiskSize,
"clone_template": &self.config.CloneTemplate,
"iso_name": &self.config.IsoName,
"sr_name": &self.config.SrName,
"network_name": &self.config.NetworkName,
"boot_wait": &self.config.RawBootWait,
"iso_checksum": &self.config.ISOChecksum,
"iso_checksum_type": &self.config.ISOChecksumType,
"http_directory": &self.config.HTTPDir,
"local_ip": &self.config.LocalIp,
"ssh_wait_timeout": &self.config.RawSSHWaitTimeout,
"ssh_username": &self.config.SSHUser,
"ssh_password": &self.config.SSHPassword,
"ssh_key_path": &self.config.SSHKeyPath,
"output_directory": &self.config.OutputDir,
}
templates := map[string]*string {
"username": &self.config.Username,
"password": &self.config.Password,
"host_ip": &self.config.HostIp,
"iso_url": &self.config.IsoUrl,
"instance_name": &self.config.InstanceName,
"instance_memory": &self.config.InstanceMemory,
"root_disk_size": &self.config.RootDiskSize,
"clone_template": &self.config.CloneTemplate,
"iso_name": &self.config.IsoName,
"sr_name": &self.config.SrName,
"network_name": &self.config.NetworkName,
"boot_wait": &self.config.RawBootWait,
"iso_checksum": &self.config.ISOChecksum,
"iso_checksum_type": &self.config.ISOChecksumType,
"http_directory": &self.config.HTTPDir,
"local_ip": &self.config.LocalIp,
"ssh_wait_timeout": &self.config.RawSSHWaitTimeout,
"ssh_username": &self.config.SSHUser,
"ssh_password": &self.config.SSHPassword,
"ssh_key_path": &self.config.SSHKeyPath,
"output_directory": &self.config.OutputDir,
}
for n, ptr := range templates {
var err error
*ptr, err = self.config.tpl.Process(*ptr, nil)
if err != nil {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Error processing %s: %s", n, err))
}
}
/*
if self.config.IsoUrl == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("a iso url must be specified"))
}
*/
for n, ptr := range templates {
var err error
*ptr, err = self.config.tpl.Process(*ptr, nil)
if err != nil {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Error processing %s: %s", n, err))
}
}
self.config.BootWait, err = time.ParseDuration(self.config.RawBootWait)
if err != nil {
errs = packer.MultiErrorAppend(
errs, errors.New("Failed to parse boot_wait."))
}
/*
if self.config.IsoUrl == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("a iso url must be specified"))
}
*/
self.config.SSHWaitTimeout, err = time.ParseDuration(self.config.RawSSHWaitTimeout)
if err != nil {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("Failed to parse ssh_wait_timeout: %s", err))
}
self.config.BootWait, err = time.ParseDuration(self.config.RawBootWait)
if err != nil {
errs = packer.MultiErrorAppend(
errs, errors.New("Failed to parse boot_wait."))
}
for i, command := range self.config.BootCommand {
if err := self.config.tpl.Validate(command); err != nil {
errs = packer.MultiErrorAppend(errs,
fmt.Errorf("Error processing boot_command[%d]: %s", i, err))
}
}
self.config.sshWaitTimeout, err = time.ParseDuration(self.config.RawSSHWaitTimeout)
if err != nil {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("Failed to parse ssh_wait_timeout: %s", err))
}
if self.config.SSHUser == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("An ssh_username must be specified."))
}
for i, command := range self.config.BootCommand {
if err := self.config.tpl.Validate(command); err != nil {
errs = packer.MultiErrorAppend(errs,
fmt.Errorf("Error processing boot_command[%d]: %s", i, err))
}
}
if self.config.SSHKeyPath != "" {
if _, err := os.Stat(self.config.SSHKeyPath); err != nil {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("ssh_key_path is invalid: %s", err))
} else if _, err := commonssh.FileSigner(self.config.SSHKeyPath); err != nil {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("ssh_key_path is invalid: %s", err))
}
}
if self.config.SSHUser == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("An ssh_username must be specified."))
}
if self.config.Username == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("A username for the xenserver host must be specified."))
}
if self.config.SSHKeyPath != "" {
if _, err := os.Stat(self.config.SSHKeyPath); err != nil {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("ssh_key_path is invalid: %s", err))
} else if _, err := commonssh.FileSigner(self.config.SSHKeyPath); err != nil {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("ssh_key_path is invalid: %s", err))
}
}
if self.config.Password == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("A password for the xenserver host must be specified."))
}
if self.config.Username == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("A username for the xenserver host must be specified."))
}
if self.config.HostIp == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("An ip for the xenserver host must be specified."))
}
if self.config.Password == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("A password for the xenserver host must be specified."))
}
if self.config.InstanceName == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("An insatnce name must be specified."))
}
if self.config.HostIp == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("An ip for the xenserver host must be specified."))
}
if self.config.InstanceMemory == "" {
self.config.InstanceMemory = "1024000000"
}
if self.config.InstanceName == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("An insatnce name must be specified."))
}
if self.config.RootDiskSize == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("A root disk size must be specified."))
}
if self.config.InstanceMemory == "" {
self.config.InstanceMemory = "1024000000"
}
if self.config.CloneTemplate == "" {
self.config.CloneTemplate = "Other install media"
}
if self.config.RootDiskSize == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("A root disk size must be specified."))
}
/*
if self.config.LocalIp == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("A local IP visible to XenServer's mangement interface is required to serve files."))
}
*/
if self.config.CloneTemplate == "" {
self.config.CloneTemplate = "Other install media"
}
if len(self.config.PlatformArgs) == 0 {
pargs := make(map[string]string)
pargs["viridian"] = "false"
pargs["nx"] = "true"
pargs["pae"] = "true"
pargs["apic"] = "true"
pargs["timeoffset"] = "0"
pargs["acpi"] = "1"
self.config.PlatformArgs = pargs
}
/*
if self.config.LocalIp == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("A local IP visible to XenServer's mangement interface is required to serve files."))
}
*/
if self.config.HTTPPortMin > self.config.HTTPPortMax {
errs = packer.MultiErrorAppend(
errs, errors.New("the HTTP min port must be less than the max"))
}
if len(self.config.PlatformArgs) == 0 {
pargs := make(map[string]string)
pargs["viridian"] = "false"
pargs["nx"] = "true"
pargs["pae"] = "true"
pargs["apic"] = "true"
pargs["timeoffset"] = "0"
pargs["acpi"] = "1"
self.config.PlatformArgs = pargs
}
if self.config.HostPortMin > self.config.HostPortMax {
errs = packer.MultiErrorAppend(
errs, errors.New("the host min port must be less than the max"))
}
/*
if self.config.ISOChecksumType == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("The iso_checksum_type must be specified."))
} else {
self.config.ISOChecksumType = strings.ToLower(self.config.ISOChecksumType)
if self.config.ISOChecksumType != "none" {
if self.config.ISOChecksum == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("Due to the file size being large, an iso_checksum is required."))
} else {
self.config.ISOChecksum = strings.ToLower(self.config.ISOChecksum)
}
if self.config.HTTPPortMin > self.config.HTTPPortMax {
errs = packer.MultiErrorAppend(
errs, errors.New("the HTTP min port must be less than the max"))
}
if hash := common.HashForType(self.config.ISOChecksumType); hash == nil {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("Unsupported checksum type: %s", self.config.ISOChecksumType))
}
if self.config.HostPortMin > self.config.HostPortMax {
errs = packer.MultiErrorAppend(
errs, errors.New("the host min port must be less than the max"))
}
/*
if self.config.ISOChecksumType == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("The iso_checksum_type must be specified."))
} else {
self.config.ISOChecksumType = strings.ToLower(self.config.ISOChecksumType)
if self.config.ISOChecksumType != "none" {
if self.config.ISOChecksum == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("Due to the file size being large, an iso_checksum is required."))
} else {
self.config.ISOChecksum = strings.ToLower(self.config.ISOChecksum)
}
}
}
if hash := common.HashForType(self.config.ISOChecksumType); hash == nil {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("Unsupported checksum type: %s", self.config.ISOChecksumType))
}
if self.config.ISOUrl == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("A ISO URL must be specfied."))
} else {
self.config.ISOUrls = []string{self.config.ISOUrl}
}
}
}
for i, url := range self.config.ISOUrls {
self.config.ISOUrls[i], err = common.DownloadableURL(url)
if err != nil {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("Failed to parse the iso_url (%d): %s", i, err))
}
}
*/
if len(errs.Errors) > 0 {
retErr = errors.New(errs.Error())
}
if self.config.ISOUrl == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("A ISO URL must be specfied."))
} else {
self.config.ISOUrls = []string{self.config.ISOUrl}
}
for i, url := range self.config.ISOUrls {
self.config.ISOUrls[i], err = common.DownloadableURL(url)
if err != nil {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("Failed to parse the iso_url (%d): %s", i, err))
}
}
*/
if len(errs.Errors) > 0 {
retErr = errors.New(errs.Error())
}
return nil, retErr
return nil, retErr
}
func (self *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
//Setup XAPI client
client := NewXenAPIClient(self.config.HostIp, self.config.Username, self.config.Password)
//Setup XAPI client
client := NewXenAPIClient(self.config.HostIp, self.config.Username, self.config.Password)
err := client.Login()
if err != nil {
return nil, err.(error)
}
ui.Say("XAPI client session established")
err := client.Login()
if err != nil {
return nil, err.(error)
}
ui.Say("XAPI client session established")
client.GetHosts()
client.GetHosts()
//Share state between the other steps using a statebag
state := new(multistep.BasicStateBag)
state.Put("cache", cache)
state.Put("client", client)
state.Put("config", self.config)
state.Put("hook", hook)
state.Put("ui", ui)
//Share state between the other steps using a statebag
state := new(multistep.BasicStateBag)
state.Put("cache", cache)
state.Put("client", client)
state.Put("config", self.config)
state.Put("hook", hook)
state.Put("ui", ui)
//Build the steps
steps := []multistep.Step{
/*
&common.StepDownload{
Checksum: self.config.ISOChecksum,
ChecksumType: self.config.ISOChecksumType,
Description: "ISO",
ResultKey: "iso_path",
Url: self.config.ISOUrls,
},
*/
new(stepPrepareOutputDir),
new(stepHTTPServer),
//new(stepUploadIso),
new(stepCreateInstance),
new(stepStartVmPaused),
new(stepGetVNCPort),
&stepForwardPortOverSSH{
RemotePort: instanceVNCPort,
RemoteDest: instanceVNCIP,
HostPortMin: self.config.HostPortMin,
HostPortMax: self.config.HostPortMax,
ResultKey: "local_vnc_port",
},
new(stepBootWait),
new(stepTypeBootCommand),
new(stepWait),
new(stepStartOnHIMN),
&stepForwardPortOverSSH{
RemotePort: himnSSHPort,
RemoteDest: himnSSHIP,
HostPortMin: self.config.HostPortMin,
HostPortMax: self.config.HostPortMax,
ResultKey: "local_ssh_port",
},
&common.StepConnectSSH{
SSHAddress: sshLocalAddress,
SSHConfig: sshConfig,
SSHWaitTimeout: self.config.SSHWaitTimeout,
},
new(common.StepProvision),
new(stepShutdownAndExport),
}
//Build the steps
steps := []multistep.Step{
/*
&common.StepDownload{
Checksum: self.config.ISOChecksum,
ChecksumType: self.config.ISOChecksumType,
Description: "ISO",
ResultKey: "iso_path",
Url: self.config.ISOUrls,
},
*/
new(stepPrepareOutputDir),
new(stepHTTPServer),
//new(stepUploadIso),
new(stepCreateInstance),
new(stepStartVmPaused),
new(stepGetVNCPort),
&stepForwardPortOverSSH{
RemotePort: instanceVNCPort,
RemoteDest: instanceVNCIP,
HostPortMin: self.config.HostPortMin,
HostPortMax: self.config.HostPortMax,
ResultKey: "local_vnc_port",
},
new(stepBootWait),
new(stepTypeBootCommand),
new(stepWait),
new(stepStartOnHIMN),
&stepForwardPortOverSSH{
RemotePort: himnSSHPort,
RemoteDest: himnSSHIP,
HostPortMin: self.config.HostPortMin,
HostPortMax: self.config.HostPortMax,
ResultKey: "local_ssh_port",
},
&common.StepConnectSSH{
SSHAddress: sshLocalAddress,
SSHConfig: sshConfig,
SSHWaitTimeout: self.config.sshWaitTimeout,
},
new(common.StepProvision),
new(stepShutdownAndExport),
}
self.runner = &multistep.BasicRunner{Steps: steps}
self.runner.Run(state)
self.runner = &multistep.BasicRunner{Steps: steps}
self.runner.Run(state)
artifact, _ := NewArtifact(self.config.OutputDir)
artifact, _ := NewArtifact(self.config.OutputDir)
if rawErr, ok := state.GetOk("error"); ok {
return nil, rawErr.(error)
}
if rawErr, ok := state.GetOk("error"); ok {
return nil, rawErr.(error)
}
return artifact, nil
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")
if self.runner != nil {
log.Println("Cancelling the step runner...")
self.runner.Cancel()
}
fmt.Println("Cancelling the builder")
}

File diff suppressed because it is too large Load Diff

View File

@ -1,29 +1,29 @@
package xenserver
import (
"fmt"
"bytes"
gossh "code.google.com/p/go.crypto/ssh"
"fmt"
"github.com/mitchellh/multistep"
commonssh "github.com/mitchellh/packer/common/ssh"
"github.com/mitchellh/packer/communicator/ssh"
"strings"
"log"
"bytes"
"net"
"io"
"io"
"log"
"net"
"strings"
)
func sshAddress(state multistep.StateBag) (string, error) {
sshIP := state.Get("ssh_address").(string)
sshIP := state.Get("ssh_address").(string)
sshHostPort := 22
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
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) {
@ -50,103 +50,101 @@ func sshConfig(state multistep.StateBag) (*gossh.ClientConfig, error) {
}, 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
}
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),
},
}
//Create session
session, err := client.NewSession()
client, err := gossh.Dial("tcp", host + ":" + port, config)
if err != nil {
return "", err
}
if err != nil {
return "", err
}
defer session.Close()
//Create session
session, err := client.NewSession()
var b bytes.Buffer
session.Stdout = &b
if err := session.Run(cmd); err != nil {
return "", err
}
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
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)
}
func forward(local_conn net.Conn, config *gossh.ClientConfig, server, remote_dest string, remote_port uint) error {
ssh_client_conn, err := gossh.Dial("tcp", server+":22", config)
if err != nil {
log.Printf("local ssh.Dial error: %s", err)
return 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)
}
remote_loc := fmt.Sprintf("%s:%d", remote_dest, remote_port)
ssh_conn, err := ssh_client_conn.Dial("tcp", remote_loc)
if err != nil {
log.Printf("ssh.Dial error: %s", err)
return 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(ssh_conn, local_conn)
if err != nil {
log.Printf("io.copy failed: %v", err)
}
}()
go func() {
_, err = io.Copy(local_conn, ssh_conn)
if err != nil {
log.Fatalf("io.copy failed: %v", err)
}
}()
go func() {
_, err = io.Copy(local_conn, ssh_conn)
if err != nil {
log.Printf("io.copy failed: %v", err)
}
}()
return nil
}
func ssh_port_forward(local_port uint, remote_port uint, remote_dest, host, username, password string) (err error) {
func ssh_port_forward(local_port uint, remote_port uint, remote_dest, host, username, password string) error {
config := &gossh.ClientConfig {
User: username,
Auth: []gossh.AuthMethod{
gossh.Password(password),
},
}
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
}
// 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.Printf("Local listen failed: %s", err)
return err
}
for {
local_connection, err := local_listener.Accept()
for {
local_connection, err := local_listener.Accept()
if err != nil {
log.Fatalf("Local accept failed: %s", err)
return err
}
if err != nil {
log.Printf("Local accept failed: %s", err)
return err
}
// Forward to a remote port
go forward(local_connection, config, host, remote_dest, remote_port)
}
// Forward to a remote port
go forward(local_connection, config, host, remote_dest, remote_port)
}
return nil
return nil
}

View File

@ -1,30 +1,28 @@
package xenserver
import (
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"time"
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"time"
)
type stepBootWait struct{}
func (self *stepBootWait) Run(state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(XenAPIClient)
config := state.Get("config").(config)
ui := state.Get("ui").(packer.Ui)
client := state.Get("client").(XenAPIClient)
config := state.Get("config").(config)
ui := state.Get("ui").(packer.Ui)
instance, _ := client.GetVMByUuid(state.Get("instance_uuid").(string))
ui.Say("Unpausing VM " + state.Get("instance_uuid").(string))
instance.Unpause()
instance, _ := client.GetVMByUuid(state.Get("instance_uuid").(string))
ui.Say("Unpausing VM " + state.Get("instance_uuid").(string))
instance.Unpause()
if int64(config.BootWait) > 0 {
ui.Say(fmt.Sprintf("Waiting %s for boot...", config.BootWait))
time.Sleep(config.BootWait)
}
return multistep.ActionContinue
if int64(config.BootWait) > 0 {
ui.Say(fmt.Sprintf("Waiting %s for boot...", config.BootWait))
time.Sleep(config.BootWait)
}
return multistep.ActionContinue
}
func (self *stepBootWait) Cleanup(state multistep.StateBag) {}

View File

@ -1,198 +1,192 @@
package xenserver
import (
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"fmt"
"log"
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
)
type stepCreateInstance struct {
InstanceId string
InstanceId string
}
func (self *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(XenAPIClient)
config := state.Get("config").(config)
ui := state.Get("ui").(packer.Ui)
client := state.Get("client").(XenAPIClient)
config := state.Get("config").(config)
ui := state.Get("ui").(packer.Ui)
ui.Say("Step: Create Instance")
ui.Say("Step: Create Instance")
// Get the template to clone from
// Get the template to clone from
vms, err := client.GetVMByNameLabel(config.CloneTemplate)
vms, err := client.GetVMByNameLabel(config.CloneTemplate)
switch {
case len(vms) == 0:
log.Fatal(fmt.Sprintf("Couldn't find a template with the name-label '%s'. Aborting.", config.CloneTemplate))
return multistep.ActionHalt
case len(vms) > 1:
log.Fatal(fmt.Sprintf("Found more than one template with the name '%s'. The name must be unique. Aborting.", config.CloneTemplate))
return multistep.ActionHalt
}
switch {
case len(vms) == 0:
log.Fatal(fmt.Sprintf("Couldn't find a template with the name-label '%s'. Aborting.", config.CloneTemplate))
return multistep.ActionHalt
case len(vms) > 1:
log.Fatal(fmt.Sprintf("Found more than one template with the name '%s'. The name must be unique. Aborting.", config.CloneTemplate))
return multistep.ActionHalt
}
template := vms[0]
template := vms[0]
// Clone that VM template
instance, _ := template.Clone(config.InstanceName)
instance.SetIsATemplate(false)
instance.SetStaticMemoryRange(config.InstanceMemory, config.InstanceMemory)
instance.SetPlatform(config.PlatformArgs)
// Clone that VM template
instance, _ := template.Clone(config.InstanceName)
instance.SetIsATemplate(false)
instance.SetStaticMemoryRange(config.InstanceMemory, config.InstanceMemory)
instance.SetPlatform(config.PlatformArgs)
// Create VDI for the instance
var sr *SR
// Create VDI for the instance
var sr *SR
if config.SrName == "" {
// Find the default SR
default_sr, err := client.GetDefaultSR()
sr = default_sr
if config.SrName == "" {
// Find the default SR
default_sr, err := client.GetDefaultSR()
sr = default_sr
if err != nil {
log.Fatal(fmt.Sprintf("Error getting default SR: %s", err.Error()))
return multistep.ActionHalt
}
if err != nil {
log.Fatal(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 {
log.Fatal(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)
switch {
case len(srs) == 0:
log.Fatal(fmt.Sprintf("Couldn't find a SR with the specified name-label '%s'. Aborting.", config.SrName))
return multistep.ActionHalt
case len(srs) > 1:
log.Fatal(fmt.Sprintf("Found more than one SR with the name '%s'. The name must be unique. Aborting.", config.SrName))
return multistep.ActionHalt
}
if err != nil {
log.Fatal(fmt.Sprintf("Error getting default SR: %s", err.Error()))
return multistep.ActionHalt
}
sr = srs[0]
}
switch {
case len(srs) == 0:
log.Fatal(fmt.Sprintf("Couldn't find a SR with the specified name-label '%s'. Aborting.", config.SrName))
return multistep.ActionHalt
case len(srs) > 1:
log.Fatal(fmt.Sprintf("Found more than one SR with the name '%s'. The name must be unique. Aborting.", config.SrName))
return multistep.ActionHalt
}
vdi, _ := sr.CreateVdi("Packer-disk", config.RootDiskSize)
sr = srs[0]
}
instance.ConnectVdi(vdi, false)
vdi, _ := sr.CreateVdi("Packer-disk", config.RootDiskSize)
// Connect Network
instance.ConnectVdi(vdi, false)
var network *Network
// Connect Network
if config.NetworkName == "" {
// No network has be specified. Use the management interface
network = new(Network)
network.Ref = ""
network.Client = &client
var network *Network
pifs, err := client.GetPIFs()
if config.NetworkName == "" {
// No network has be specified. Use the management interface
network = new(Network)
network.Ref = ""
network.Client = &client
if err != nil {
log.Fatal(fmt.Sprintf("Error getting PIFs %s", err.Error()))
return multistep.ActionHalt
}
pifs, err := client.GetPIFs()
for _, pif := range pifs {
pif_rec, err := pif.GetRecord()
if err != nil {
log.Fatal(fmt.Sprintf("Error getting PIFs %s", err.Error()))
return multistep.ActionHalt
}
if err != nil {
log.Fatal(fmt.Sprintf("Error getting PIF record: %s", err.Error()))
return multistep.ActionHalt
}
for _, pif := range pifs {
pif_rec, err := pif.GetRecord()
if pif_rec["management"].(bool) {
network.Ref = pif_rec["network"].(string)
}
if err != nil {
log.Fatal(fmt.Sprintf("Error getting PIF record: %s", err.Error()))
return multistep.ActionHalt
}
}
if pif_rec["management"].(bool) {
network.Ref = pif_rec["network"].(string)
}
if network.Ref == "" {
log.Fatal("Error: couldn't find management network. Aborting.")
return multistep.ActionHalt
}
}
} else {
// Look up the network by it's name label
if network.Ref == "" {
log.Fatal("Error: couldn't find management network. Aborting.")
return multistep.ActionHalt
}
networks, err := client.GetNetworkByNameLabel(config.NetworkName)
} else {
// Look up the network by it's name label
if err != nil {
log.Fatal(fmt.Sprintf("Error occured getting Network by name-label: %s", err.Error()))
return multistep.ActionHalt
}
networks, err := client.GetNetworkByNameLabel(config.NetworkName)
switch {
case len(networks) == 0:
log.Fatal(fmt.Sprintf("Couldn't find a network with the specified name-label '%s'. Aborting.", config.NetworkName))
return multistep.ActionHalt
case len(networks) > 1:
log.Fatal(fmt.Sprintf("Found more than one SR with the name '%s'. The name must be unique. Aborting.", config.NetworkName))
return multistep.ActionHalt
}
if err != nil {
log.Fatal(fmt.Sprintf("Error occured getting Network by name-label: %s", err.Error()))
return multistep.ActionHalt
}
network = networks[0]
}
switch {
case len(networks) == 0:
log.Fatal(fmt.Sprintf("Couldn't find a network with the specified name-label '%s'. Aborting.", config.NetworkName))
return multistep.ActionHalt
case len(networks) > 1:
log.Fatal(fmt.Sprintf("Found more than one SR with the name '%s'. The name must be unique. Aborting.", config.NetworkName))
return multistep.ActionHalt
}
if err != nil {
ui.Say(err.Error())
}
_, err = instance.ConnectNetwork(network, "0")
network = networks[0]
}
if err != nil {
ui.Say(err.Error())
}
if err != nil {
ui.Say(err.Error())
}
_, err = instance.ConnectNetwork(network, "0")
// Connect the ISO
//iso_vdi_uuid := state.Get("iso_vdi_uuid").(string)
if err != nil {
ui.Say(err.Error())
}
isos, err := client.GetVdiByNameLabel(config.IsoName)
// Connect the ISO
//iso_vdi_uuid := state.Get("iso_vdi_uuid").(string)
switch {
case len(isos) == 0:
log.Fatal(fmt.Sprintf("Couldn't find an ISO named '%s'. Aborting", config.IsoName))
return multistep.ActionHalt
case len(isos) > 1:
log.Fatal(fmt.Sprintf("Found more than one VDI with name '%s'. Name must be unique. Aborting.", config.IsoName))
return multistep.ActionHalt
}
iso := isos[0]
isos, err := client.GetVdiByNameLabel(config.IsoName)
//iso, _ := client.GetVdiByUuid(config.IsoUuid)
//ui.Say("Using VDI: " + iso_vdi_uuid)
//iso, _ := client.GetVdiByUuid(iso_vdi_uuid)
instance.ConnectVdi(iso, true)
// Stash the VM reference
self.InstanceId, _ = instance.GetUuid()
state.Put("instance_uuid", self.InstanceId)
state.Put("instance", instance)
ui.Say(fmt.Sprintf("Created instance '%s'", self.InstanceId))
switch {
case len(isos) == 0:
log.Fatal(fmt.Sprintf("Couldn't find an ISO named '%s'. Aborting", config.IsoName))
return multistep.ActionHalt
case len(isos) > 1:
log.Fatal(fmt.Sprintf("Found more than one VDI with name '%s'. Name must be unique. Aborting.", config.IsoName))
return multistep.ActionHalt
}
iso := isos[0]
//iso, _ := client.GetVdiByUuid(config.IsoUuid)
//ui.Say("Using VDI: " + iso_vdi_uuid)
//iso, _ := client.GetVdiByUuid(iso_vdi_uuid)
instance.ConnectVdi(iso, true)
// Stash the VM reference
self.InstanceId, _ = instance.GetUuid()
state.Put("instance_uuid", self.InstanceId)
state.Put("instance", instance)
ui.Say(fmt.Sprintf("Created instance '%s'", self.InstanceId))
return multistep.ActionContinue
return multistep.ActionContinue
}
func (self *stepCreateInstance) Cleanup(state multistep.StateBag) {
// client := state.Get("client").(*XenAPIClient)
// config := state.Get("config").(config)
// ui := state.Get("ui").(packer.Ui)
// client := state.Get("client").(*XenAPIClient)
// config := state.Get("config").(config)
// ui := state.Get("ui").(packer.Ui)
// If instance hasn't been created, we have nothing to do.
if self.InstanceId == "" {
return
}
// If instance hasn't been created, we have nothing to do.
if self.InstanceId == "" {
return
}
// @todo: destroy the created instance.
// @todo: destroy the created instance.
return
return
}

View File

@ -1,73 +1,68 @@
package xenserver
import (
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
"net"
"fmt"
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
"net"
)
type stepForwardPortOverSSH struct {
RemotePort func(state multistep.StateBag) (uint, error)
RemoteDest func(state multistep.StateBag) (string, error)
RemotePort func (state multistep.StateBag) (uint, error)
RemoteDest func (state multistep.StateBag) (string, error)
HostPortMin uint
HostPortMax uint
HostPortMin uint
HostPortMax uint
ResultKey string
ResultKey string
}
func (self *stepForwardPortOverSSH) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(config)
ui := state.Get("ui").(packer.Ui)
config := state.Get("config").(config)
ui := state.Get("ui").(packer.Ui)
// Find a free local port:
// Find a free local port:
log.Printf("Looking for an available port between %d and %d",
self.HostPortMin,
self.HostPortMax)
log.Printf("Looking for an available port between %d and %d",
self.HostPortMin,
self.HostPortMax)
var sshHostPort uint
var foundPort bool
var sshHostPort uint
var foundPort bool
foundPort = false
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
}
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
}
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)
ui.Say(fmt.Sprintf("Creating a local port forward over SSH on local port %d", sshHostPort))
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))
remotePort, _ := self.RemotePort(state)
remoteDest, _ := self.RemoteDest(state)
// Provide the local port to future steps.
state.Put(self.ResultKey, sshHostPort)
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
return multistep.ActionContinue
}
func (self *stepForwardPortOverSSH) Cleanup(state multistep.StateBag) {}

View File

@ -1,52 +1,49 @@
package xenserver
import (
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"fmt"
"strconv"
"log"
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
"strconv"
)
type stepGetVNCPort struct {}
type stepGetVNCPort struct{}
func (self *stepGetVNCPort) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(config)
ui := state.Get("ui").(packer.Ui)
config := state.Get("config").(config)
ui := state.Get("ui").(packer.Ui)
ui.Say("Step: forward the instances VNC port over SSH")
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)
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_vncport, _ := execute_ssh_cmd(cmd, config.HostIp, "22", config.Username, config.Password)
remote_port, err := strconv.ParseUint(remote_vncport, 10, 16)
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
}
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))
state.Put("instance_vnc_port", uint(remote_port))
return multistep.ActionContinue
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 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
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

@ -3,95 +3,93 @@ package xenserver
/* Taken from https://raw.githubusercontent.com/mitchellh/packer/master/builder/qemu/step_prepare_output_dir.go */
import (
"crypto/tls"
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"io"
"net/http"
"os"
"fmt"
"net/http"
"crypto/tls"
"io"
)
type stepShutdownAndExport struct{}
func downloadFile(url, filename string) (err error) {
// Create the file
fh, err := os.Create(filename)
if err != nil {
return err
}
// Create the file
fh, err := os.Create(filename)
if err != nil {
return err
}
// Define a new transport which allows self-signed certs
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
// Define a new transport which allows self-signed certs
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
// Create a client
client := &http.Client{Transport: tr}
// Create a client
client := &http.Client{Transport: tr}
// Create request and download file
// Create request and download file
resp, err := client.Get(url)
if err != nil {
return err
}
resp, err := client.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
io.Copy(fh, resp.Body)
defer resp.Body.Close()
io.Copy(fh, resp.Body)
return nil
return nil
}
func (stepShutdownAndExport) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(config)
ui := state.Get("ui").(packer.Ui)
client := state.Get("client").(XenAPIClient)
instance_uuid := state.Get("instance_uuid").(string)
client := state.Get("client").(XenAPIClient)
instance_uuid := state.Get("instance_uuid").(string)
instance, _ := client.GetVMByUuid(instance_uuid)
instance, _ := client.GetVMByUuid(instance_uuid)
ui.Say("Step: Shutdown and export VPX")
ui.Say("Step: Shutdown and export VPX")
// Shutdown the VM
ui.Say("Shutting down the VM...")
instance.CleanShutdown()
// Shutdown the VM
ui.Say("Shutting down the VM...")
instance.CleanShutdown()
//Export the VM
//Export the VM
export_url := fmt.Sprintf("https://%s/export?vm=%s&session_id=%s",
client.Host,
instance.Ref,
client.Session.(string),
)
export_url := fmt.Sprintf("https://%s/export?vm=%s&session_id=%s",
client.Host,
instance.Ref,
client.Session.(string),
)
export_filename := fmt.Sprintf("%s/%s.xva", config.OutputDir, config.InstanceName)
ui.Say("Getting metadata " + export_url)
downloadFile(export_url, export_filename)
export_filename := fmt.Sprintf("%s/%s.xva", config.OutputDir, config.InstanceName)
ui.Say("Getting metadata " + export_url)
downloadFile(export_url, export_filename)
disks, _ := instance.GetDisks()
for _, disk := range disks {
disk_uuid, _ := disk.GetUuid()
disks, _ := instance.GetDisks()
for _, disk := range disks {
disk_uuid, _ := disk.GetUuid()
// Basic auth in URL request is required as session token is not
// accepted for some reason.
// @todo: raise with XAPI team.
disk_export_url := fmt.Sprintf("https://%s:%s@%s/export_raw_vdi?vdi=%s",
client.Username,
client.Password,
client.Host,
disk_uuid,
)
// Basic auth in URL request is required as session token is not
// accepted for some reason.
// @todo: raise with XAPI team.
disk_export_url := fmt.Sprintf("https://%s:%s@%s/export_raw_vdi?vdi=%s",
client.Username,
client.Password,
client.Host,
disk_uuid,
)
ui.Say("Getting " + disk_export_url)
disk_export_filename := fmt.Sprintf("%s/%s.raw", config.OutputDir, disk_uuid)
ui.Say("Downloading " + disk_uuid)
downloadFile(disk_export_url, disk_export_filename)
}
ui.Say("Getting " + disk_export_url)
disk_export_filename := fmt.Sprintf("%s/%s.raw", config.OutputDir, disk_uuid)
ui.Say("Downloading " + disk_uuid)
downloadFile(disk_export_url, disk_export_filename)
}
ui.Say("Download complteded: " + config.OutputDir)
ui.Say("Download completed: " + config.OutputDir)
return multistep.ActionContinue
}

View File

@ -1,16 +1,15 @@
package xenserver
import (
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"time"
"log"
"fmt"
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
"time"
)
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.
@ -21,104 +20,99 @@ type stepStartOnHIMN struct{}
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 := 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")
ui.Say("Step: Start VM on the Host Internal Mangement Network")
instance := state.Get("instance").(*VM)
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
}
// 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]
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
}
// 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)
// 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)
var himn_iface_ip string = ""
//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
}
// Obtain the allocated IP
for i:=0; i < 10; i++ {
ips, _ := himn.GetAssignedIPs()
log.Printf("IPs: %s", ips)
log.Printf("Ref: %s", instance.Ref)
ui.Say("Wait for IP address...")
time.Sleep(10 * time.Second)
//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
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
}
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)
// Wait for the VM to boot, and check we can ping this interface
if err == nil {
ui.Message("Ping success! Continuing...")
break
}
ping_cmd := fmt.Sprintf("ping -c 1 %s", himn_iface_ip)
time.Sleep(10 * time.Second)
}
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 {
log.Fatal("Unable to ping interface. Something is wrong. Has the VM not booted?")
log.Fatal(err.Error())
return multistep.ActionHalt
}
if err == nil {
ui.Message("Ping success! Continuing...")
break
}
time.Sleep(10 * time.Second)
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
}
time.Sleep(10 * time.Second)
return multistep.ActionContinue
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 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
func himnSSHPort(state multistep.StateBag) (uint, error) {
return 22, nil
}

View File

@ -1,30 +1,28 @@
package xenserver
import (
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
)
type stepStartVmPaused struct {}
type stepStartVmPaused struct{}
func (self *stepStartVmPaused) Run(state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(XenAPIClient)
ui := state.Get("ui").(packer.Ui)
client := state.Get("client").(XenAPIClient)
ui := state.Get("ui").(packer.Ui)
ui.Say("Step: Start VM Paused")
ui.Say("Step: Start VM Paused")
instance, _ := client.GetVMByUuid(state.Get("instance_uuid").(string))
instance, _ := client.GetVMByUuid(state.Get("instance_uuid").(string))
instance.Start(true, false)
instance.Start(true, false)
domid, _ := instance.GetDomainId()
state.Put("domid", domid)
domid, _ := instance.GetDomainId()
state.Put("domid", domid)
return multistep.ActionContinue
return multistep.ActionContinue
}
func (self *stepStartVmPaused) Cleanup(state multistep.StateBag) {
}

View File

@ -3,92 +3,91 @@ package xenserver
/* Heavily borrowed from builder/quemu/step_type_boot_command.go */
import (
"fmt"
"github.com/mitchellh/go-vnc"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
"net"
"strings"
"time"
"unicode"
"unicode/utf8"
"fmt"
"github.com/mitchellh/go-vnc"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
"net"
"strings"
"time"
"unicode"
"unicode/utf8"
)
const KeyLeftShift uint = 0xFFE1
type bootCommandTemplateData struct {
HTTPIP string
HTTPPort uint
HTTPIP string
HTTPPort uint
}
type stepTypeBootCommand struct{}
func (self *stepTypeBootCommand) Run (state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(config)
ui := state.Get("ui").(packer.Ui)
vnc_port := state.Get("local_vnc_port").(uint)
http_port := state.Get("http_port").(uint)
func (self *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(config)
ui := state.Get("ui").(packer.Ui)
vnc_port := state.Get("local_vnc_port").(uint)
http_port := state.Get("http_port").(uint)
// Connect to the local VNC port as we have set up a SSH port forward
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))
// Connect to the local VNC port as we have set up a SSH port forward
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))
if err != nil {
err := fmt.Errorf("Error connecting to VNC: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if err != nil {
err := fmt.Errorf("Error connecting to VNC: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
defer net_conn.Close()
defer net_conn.Close()
c, err := vnc.Client(net_conn, &vnc.ClientConfig{Exclusive: true})
c, err := vnc.Client(net_conn, &vnc.ClientConfig{Exclusive: true})
if err != nil {
err := fmt.Errorf("Error establishing VNC session: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if err != nil {
err := fmt.Errorf("Error establishing VNC session: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
defer c.Close()
defer c.Close()
log.Printf("Connected to the VNC console: %s", c.DesktopName)
log.Printf("Connected to the VNC console: %s", c.DesktopName)
// @todo - include http port/ip so kickstarter files can be grabbed
tplData := &bootCommandTemplateData {
config.LocalIp,
http_port,
}
// @todo - include http port/ip so kickstarter files can be grabbed
tplData := &bootCommandTemplateData{
config.LocalIp,
http_port,
}
ui.Say("About to type boot commands over VNC...")
for _, command := range config.BootCommand {
ui.Say("About to type boot commands over VNC...")
for _, command := range config.BootCommand {
command, err := config.tpl.Process(command, tplData)
if err != nil {
err := fmt.Errorf("Error preparing boot command: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
command, err := config.tpl.Process(command, tplData)
if err != nil {
err := fmt.Errorf("Error preparing boot command: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
// Check for interrupts
if _, ok := state.GetOk(multistep.StateCancelled); ok {
return multistep.ActionHalt
}
// Check for interrupts
if _, ok := state.GetOk(multistep.StateCancelled); ok {
return multistep.ActionHalt
}
vncSendString(c, command)
}
vncSendString(c, command)
}
ui.Say("Finished typing.")
ui.Say("Finished typing.")
return multistep.ActionContinue
return multistep.ActionContinue
}
func (self *stepTypeBootCommand) Cleanup (multistep.StateBag) {}
func (self *stepTypeBootCommand) Cleanup(multistep.StateBag) {}
// Taken from qemu's builder plugin - not an exported function.
func vncSendString(c *vnc.ClientConn, original string) {

View File

@ -1,81 +1,78 @@
package xenserver
import (
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"os"
"strconv"
"os/exec"
"log"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
"os"
"os/exec"
"strconv"
)
type stepUploadIso struct {}
type stepUploadIso struct{}
func (self *stepUploadIso) Run(state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(XenAPIClient)
config := state.Get("config").(config)
ui := state.Get("ui").(packer.Ui)
client := state.Get("client").(XenAPIClient)
config := state.Get("config").(config)
ui := state.Get("ui").(packer.Ui)
ui.Say("Step: Upload ISO to server")
iso_path := state.Get("iso_path").(string)
ui.Say("Step: Upload ISO to server")
iso_path := state.Get("iso_path").(string)
// Determine the ISO's filesize
file, err := os.Open(iso_path)
if err != nil {
ui.Error(err.Error())
return multistep.ActionHalt
}
stat, err := file.Stat()
// Determine the ISO's filesize
file, err := os.Open(iso_path)
if err != nil {
ui.Error(err.Error())
return multistep.ActionHalt
}
stat, err := file.Stat()
if err != nil {
ui.Error(err.Error())
return multistep.ActionHalt
}
if err != nil {
ui.Error(err.Error())
return multistep.ActionHalt
}
iso_filesize := stat.Size()
iso_filesize := stat.Size()
// Create a VDI with the write size
srs, err := client.GetSRByNameLabel(config.SrName)
// Create a VDI with the write size
srs, err := client.GetSRByNameLabel(config.SrName)
sr := srs[0]
sr := srs[0]
if err != nil {
ui.Error(err.Error())
return multistep.ActionHalt
}
if err != nil {
ui.Error(err.Error())
return multistep.ActionHalt
}
filesize_str := strconv.FormatInt(iso_filesize, 10)
log.Printf("Filesize of the ISO is %d", filesize_str)
vdi, err := sr.CreateVdi("Packer Gen " + stat.Name(), filesize_str)
filesize_str := strconv.FormatInt(iso_filesize, 10)
log.Printf("Filesize of the ISO is %d", filesize_str)
vdi, err := sr.CreateVdi("Packer Gen "+stat.Name(), filesize_str)
if err != nil {
ui.Error(err.Error())
return multistep.ActionHalt
}
if err != nil {
ui.Error(err.Error())
return multistep.ActionHalt
}
// Upload the ISO to this VDI
vdi_uuid, _ := vdi.GetUuid()
host_url := "https://" + client.Host
log.Printf("Host URL: %s", host_url)
ui.Say("Uploading ISO to " + vdi_uuid)
out, err := exec.Command("/usr/bin/importdisk.py", host_url, client.Username, client.Password, vdi_uuid, iso_path).CombinedOutput()
log.Printf("Output: %s", out)
// Upload the ISO to this VDI
vdi_uuid, _ := vdi.GetUuid()
host_url := "https://" + client.Host
log.Printf("Host URL: %s", host_url)
ui.Say("Uploading ISO to " + vdi_uuid)
out, err := exec.Command("/usr/bin/importdisk.py", host_url, client.Username, client.Password, vdi_uuid, iso_path).CombinedOutput()
log.Printf("Output: %s", out)
if err != nil {
log.Printf("%s", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if err != nil {
log.Printf("%s", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
// Stash the vdi uuid to be used in preference
state.Put("iso_vdi_uuid", vdi_uuid)
// Stash the vdi uuid to be used in preference
state.Put("iso_vdi_uuid", vdi_uuid)
return multistep.ActionContinue
return multistep.ActionContinue
}
func (self *stepUploadIso) Cleanup(state multistep.StateBag) {
}

View File

@ -1,66 +1,61 @@
package xenserver
import (
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"time"
"reflect"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"time"
)
type stepWait struct{}
func (self *stepWait) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
client := state.Get("client").(XenAPIClient)
ui := state.Get("ui").(packer.Ui)
client := state.Get("client").(XenAPIClient)
ui.Say("Step: Wait for install to complete.")
ui.Say("Step: Wait for install to complete.")
//Expect install to be configured to shutdown on completion
//Expect install to be configured to shutdown on completion
instance_id := state.Get("instance_uuid").(string)
instance, _ := client.GetVMByUuid(instance_id)
instance_id := state.Get("instance_uuid").(string)
instance, _ := client.GetVMByUuid(instance_id)
for {
time.Sleep(30 * time.Second)
ui.Say("Waiting for VM install...")
for {
time.Sleep(30 * time.Second)
ui.Say("Waiting for VM install...")
power_state, _ := instance.GetPowerState()
if power_state == "Halted" {
ui.Say("Install has completed. Moving on.")
break
}
}
power_state, _ := instance.GetPowerState()
if power_state == "Halted" {
ui.Say("Install has completed. Moving on.")
break
}
}
// Eject ISO from drive
vbds, _ := instance.GetVBDs()
for _, vbd := range vbds {
rec, _ := vbd.GetRecord()
// Eject ISO from drive
vbds, _ := instance.GetVBDs()
for _, vbd := range vbds {
rec, _ := vbd.GetRecord()
// 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
}
}
// 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
switch reflect.TypeOf(rec["type"]).Kind() {
case reflect.String:
if rec["type"].(string) == "CD" {
ui.Say("Ejecting CD...")
vbd.Eject()
}
default:
break
}
}
// Destroy all connected VIFs
vifs, _ := instance.GetVIFs()
for _, vif := range vifs {
ui.Message("Destroying VIF " + vif.Ref)
vif.Destroy()
}
// Destroy all connected VIFs
vifs, _ := instance.GetVIFs()
for _, vif := range vifs {
ui.Message("Destroying VIF " + vif.Ref)
vif.Destroy()
}
return multistep.ActionContinue
return multistep.ActionContinue
}
func (self *stepWait) Cleanup(state multistep.StateBag) {}

16
main.go
View File

@ -1,15 +1,15 @@
package main
import (
"github.com/rdobson/packer-builder-xenserver/builder/xenserver"
"github.com/mitchellh/packer/packer/plugin"
"github.com/mitchellh/packer/packer/plugin"
"github.com/rdobson/packer-builder-xenserver/builder/xenserver"
)
func main() {
server, err := plugin.Server()
if err != nil {
panic(err)
}
server.RegisterBuilder(new(xenserver.Builder))
server.Serve()
server, err := plugin.Server()
if err != nil {
panic(err)
}
server.RegisterBuilder(new(xenserver.Builder))
server.Serve()
}