commit
50403a3ed6
@ -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",
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
@ -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
|
||||
}
|
||||
|
@ -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) {}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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) {}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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) {
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
}
|
||||
|
@ -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
16
main.go
@ -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()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user