Initial commit
This commit is contained in:
		
							
								
								
									
										58
									
								
								builder/xenserver/common/artifact.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								builder/xenserver/common/artifact.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/packer"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// This is the common builder ID to all of these artifacts.
 | 
			
		||||
const BuilderId = "packer.xenserver"
 | 
			
		||||
 | 
			
		||||
type LocalArtifact struct {
 | 
			
		||||
	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
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &LocalArtifact{
 | 
			
		||||
		dir: dir,
 | 
			
		||||
		f:   files,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (*LocalArtifact) BuilderId() string {
 | 
			
		||||
	return BuilderId
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *LocalArtifact) Files() []string {
 | 
			
		||||
	return a.f
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (*LocalArtifact) Id() string {
 | 
			
		||||
	return "VM"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *LocalArtifact) String() string {
 | 
			
		||||
	return fmt.Sprintf("VM files in directory: %s", a.dir)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *LocalArtifact) State(name string) interface{} {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *LocalArtifact) Destroy() error {
 | 
			
		||||
	return os.RemoveAll(a.dir)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										974
									
								
								builder/xenserver/common/client.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										974
									
								
								builder/xenserver/common/client.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,974 @@
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/xml"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
 | 
			
		||||
	xmlrpc "github.com/amfranz/go-xmlrpc-client"
 | 
			
		||||
	xenapi "github.com/terra-farm/go-xen-api-client"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type XenAPIClient struct {
 | 
			
		||||
	Session  interface{}
 | 
			
		||||
	Host     string
 | 
			
		||||
	Url      string
 | 
			
		||||
	Username string
 | 
			
		||||
	Password string
 | 
			
		||||
	RPC      *xmlrpc.Client
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type APIResult struct {
 | 
			
		||||
	Status           string
 | 
			
		||||
	Value            interface{}
 | 
			
		||||
	ErrorDescription string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type XenAPIObject struct {
 | 
			
		||||
	Ref    string
 | 
			
		||||
	Client *XenAPIClient
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Host XenAPIObject
 | 
			
		||||
type VM XenAPIObject
 | 
			
		||||
type SR XenAPIObject
 | 
			
		||||
type VDI XenAPIObject
 | 
			
		||||
type Network XenAPIObject
 | 
			
		||||
type VBD XenAPIObject
 | 
			
		||||
type VIF XenAPIObject
 | 
			
		||||
type PIF XenAPIObject
 | 
			
		||||
type Pool XenAPIObject
 | 
			
		||||
type Task XenAPIObject
 | 
			
		||||
 | 
			
		||||
type VDIType int
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	_ VDIType = iota
 | 
			
		||||
	Disk
 | 
			
		||||
	CD
 | 
			
		||||
	Floppy
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type TaskStatusType int
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	_ TaskStatusType = iota
 | 
			
		||||
	Pending
 | 
			
		||||
	Success
 | 
			
		||||
	Failure
 | 
			
		||||
	Cancelling
 | 
			
		||||
	Cancelled
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (c *XenAPIClient) RPCCall(result interface{}, method string, params []interface{}) (err error) {
 | 
			
		||||
	fmt.Println(params)
 | 
			
		||||
	p := xmlrpc.Params{Params: params}
 | 
			
		||||
	err = c.RPC.Call(method, p, result)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (client *XenAPIClient) Login() (err error) {
 | 
			
		||||
	//Do loging call
 | 
			
		||||
	result := xmlrpc.Struct{}
 | 
			
		||||
 | 
			
		||||
	params := make([]interface{}, 2)
 | 
			
		||||
	params[0] = client.Username
 | 
			
		||||
	params[1] = client.Password
 | 
			
		||||
 | 
			
		||||
	err = client.RPCCall(&result, "session.login_with_password", params)
 | 
			
		||||
	client.Session = result["Value"]
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (client *XenAPIClient) APICall(result *APIResult, method string, params ...interface{}) (err error) {
 | 
			
		||||
	if client.Session == nil {
 | 
			
		||||
		fmt.Println("Error: no session")
 | 
			
		||||
		return fmt.Errorf("No session. Unable to make call")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//Make a params slice which will include the session
 | 
			
		||||
	p := make([]interface{}, len(params)+1)
 | 
			
		||||
	p[0] = client.Session
 | 
			
		||||
 | 
			
		||||
	if params != nil {
 | 
			
		||||
		for idx, element := range params {
 | 
			
		||||
			p[idx+1] = element
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	res := xmlrpc.Struct{}
 | 
			
		||||
 | 
			
		||||
	err = client.RPCCall(&res, method, p)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result.Status = res["Status"].(string)
 | 
			
		||||
 | 
			
		||||
	if result.Status != "Success" {
 | 
			
		||||
		fmt.Println("Encountered an API error: ", result.Status)
 | 
			
		||||
		fmt.Println(res["ErrorDescription"])
 | 
			
		||||
		return fmt.Errorf("API Error: %s", res["ErrorDescription"])
 | 
			
		||||
	} else {
 | 
			
		||||
		result.Value = res["Value"]
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (client *XenAPIClient) GetHosts() (hosts []*Host, err error) {
 | 
			
		||||
	hosts = make([]*Host, 0)
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	_ = client.APICall(&result, "host.get_all")
 | 
			
		||||
	for _, elem := range result.Value.([]interface{}) {
 | 
			
		||||
		host := new(Host)
 | 
			
		||||
		host.Ref = elem.(string)
 | 
			
		||||
		host.Client = client
 | 
			
		||||
		hosts = append(hosts, host)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (client *XenAPIClient) GetPools() (pools []*Pool, err error) {
 | 
			
		||||
	pools = make([]*Pool, 0)
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = client.APICall(&result, "pool.get_all")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return pools, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, elem := range result.Value.([]interface{}) {
 | 
			
		||||
		pool := new(Pool)
 | 
			
		||||
		pool.Ref = elem.(string)
 | 
			
		||||
		pool.Client = client
 | 
			
		||||
		pools = append(pools, pool)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return pools, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (client *XenAPIClient) GetDefaultSR() (sr *SR, err error) {
 | 
			
		||||
	pools, err := client.GetPools()
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pool_rec, err := pools[0].GetRecord()
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if pool_rec["default_SR"] == "" {
 | 
			
		||||
		return nil, errors.New("No default_SR specified for the pool.")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sr = new(SR)
 | 
			
		||||
	sr.Ref = pool_rec["default_SR"].(string)
 | 
			
		||||
	sr.Client = client
 | 
			
		||||
 | 
			
		||||
	return sr, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (client *XenAPIClient) GetVMByUuid(vm_uuid string) (vm *VM, err error) {
 | 
			
		||||
	vm = new(VM)
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = client.APICall(&result, "VM.get_by_uuid", vm_uuid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	vm.Ref = result.Value.(string)
 | 
			
		||||
	vm.Client = client
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (client *XenAPIClient) GetVMByNameLabel(name_label string) (vms []*VM, err error) {
 | 
			
		||||
	vms = make([]*VM, 0)
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = client.APICall(&result, "VM.get_by_name_label", name_label)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return vms, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, elem := range result.Value.([]interface{}) {
 | 
			
		||||
		vm := new(VM)
 | 
			
		||||
		vm.Ref = elem.(string)
 | 
			
		||||
		vm.Client = client
 | 
			
		||||
		vms = append(vms, vm)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return vms, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (client *XenAPIClient) GetNetworkByUuid(network_uuid string) (network *Network, err error) {
 | 
			
		||||
	network = new(Network)
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = client.APICall(&result, "network.get_by_uuid", network_uuid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	network.Ref = result.Value.(string)
 | 
			
		||||
	network.Client = client
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (client *XenAPIClient) GetNetworkByNameLabel(name_label string) (networks []*Network, err error) {
 | 
			
		||||
	networks = make([]*Network, 0)
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = client.APICall(&result, "network.get_by_name_label", name_label)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return networks, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, elem := range result.Value.([]interface{}) {
 | 
			
		||||
		network := new(Network)
 | 
			
		||||
		network.Ref = elem.(string)
 | 
			
		||||
		network.Client = client
 | 
			
		||||
		networks = append(networks, network)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return networks, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (client *XenAPIClient) GetVdiByNameLabel(name_label string) (vdis []*VDI, err error) {
 | 
			
		||||
	vdis = make([]*VDI, 0)
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = client.APICall(&result, "VDI.get_by_name_label", name_label)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return vdis, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, elem := range result.Value.([]interface{}) {
 | 
			
		||||
		vdi := new(VDI)
 | 
			
		||||
		vdi.Ref = elem.(string)
 | 
			
		||||
		vdi.Client = client
 | 
			
		||||
		vdis = append(vdis, vdi)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return vdis, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (client *XenAPIClient) GetVdiByUuid(vdi_uuid string) (vdi *VDI, err error) {
 | 
			
		||||
	vdi = new(VDI)
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = client.APICall(&result, "VDI.get_by_uuid", vdi_uuid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	vdi.Ref = result.Value.(string)
 | 
			
		||||
	vdi.Client = client
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (client *XenAPIClient) GetPIFs() (pifs []*PIF, err error) {
 | 
			
		||||
	pifs = make([]*PIF, 0)
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = client.APICall(&result, "PIF.get_all")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return pifs, err
 | 
			
		||||
	}
 | 
			
		||||
	for _, elem := range result.Value.([]interface{}) {
 | 
			
		||||
		pif := new(PIF)
 | 
			
		||||
		pif.Ref = elem.(string)
 | 
			
		||||
		pif.Client = client
 | 
			
		||||
		pifs = append(pifs, pif)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return pifs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Host associated functions
 | 
			
		||||
 | 
			
		||||
func (self *Host) GetSoftwareVersion() (versions map[string]interface{}, err error) {
 | 
			
		||||
	versions = make(map[string]interface{})
 | 
			
		||||
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = self.Client.APICall(&result, "host.get_software_version", self.Ref)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for k, v := range result.Value.(xmlrpc.Struct) {
 | 
			
		||||
		versions[k] = v.(string)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *Host) CallPlugin(plugin, function string, args map[string]string) (res string, err error) {
 | 
			
		||||
 | 
			
		||||
	args_rec := make(xmlrpc.Struct)
 | 
			
		||||
	for key, value := range args {
 | 
			
		||||
		args_rec[key] = value
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = self.Client.APICall(&result, "host.call_plugin", self.Ref, plugin, function, args_rec)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// The plugin should return a string value
 | 
			
		||||
	res = result.Value.(string)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// VM associated functions
 | 
			
		||||
 | 
			
		||||
func (self *VM) Clone(label string) (new_instance *VM, err error) {
 | 
			
		||||
	new_instance = new(VM)
 | 
			
		||||
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = self.Client.APICall(&result, "VM.clone", self.Ref, label)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	new_instance.Ref = result.Value.(string)
 | 
			
		||||
	new_instance.Client = self.Client
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *VM) Destroy() (err error) {
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = self.Client.APICall(&result, "VM.destroy", self.Ref)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *VM) Start(paused, force bool) (err error) {
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = self.Client.APICall(&result, "VM.start", self.Ref, paused, force)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *VM) CleanShutdown() (err error) {
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = self.Client.APICall(&result, "VM.clean_shutdown", self.Ref)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Unpause(c *Connection, vmRef xenapi.VMRef) (err error) {
 | 
			
		||||
	err = c.client.VM.Unpause(c.session, vmRef)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *VM) SetHVMBoot(policy, bootOrder string) (err error) {
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = self.Client.APICall(&result, "VM.set_HVM_boot_policy", self.Ref, policy)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	result = APIResult{}
 | 
			
		||||
	params := make(xmlrpc.Struct)
 | 
			
		||||
	params["order"] = bootOrder
 | 
			
		||||
	err = self.Client.APICall(&result, "VM.set_HVM_boot_params", self.Ref, params)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *VM) SetPVBootloader(pv_bootloader, pv_args string) (err error) {
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = self.Client.APICall(&result, "VM.set_PV_bootloader", self.Ref, pv_bootloader)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	result = APIResult{}
 | 
			
		||||
	err = self.Client.APICall(&result, "VM.set_PV_bootloader_args", self.Ref, pv_args)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *VM) GetDomainId() (domid string, err error) {
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = self.Client.APICall(&result, "VM.get_domid", self.Ref)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	domid = result.Value.(string)
 | 
			
		||||
	return domid, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *VM) GetPowerState() (state string, err error) {
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = self.Client.APICall(&result, "VM.get_power_state", self.Ref)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	state = result.Value.(string)
 | 
			
		||||
	return state, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *VM) GetUuid() (uuid string, err error) {
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = self.Client.APICall(&result, "VM.get_uuid", self.Ref)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	uuid = result.Value.(string)
 | 
			
		||||
	return uuid, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *VM) GetVBDs() (vbds []VBD, err error) {
 | 
			
		||||
	vbds = make([]VBD, 0)
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = self.Client.APICall(&result, "VM.get_VBDs", self.Ref)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return vbds, err
 | 
			
		||||
	}
 | 
			
		||||
	for _, elem := range result.Value.([]interface{}) {
 | 
			
		||||
		vbd := VBD{}
 | 
			
		||||
		vbd.Ref = elem.(string)
 | 
			
		||||
		vbd.Client = self.Client
 | 
			
		||||
		vbds = append(vbds, vbd)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return vbds, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetDisks(c *Connection, vmRef xenapi.VMRef) (vdis []xenapi.VDIRef, err error) {
 | 
			
		||||
	// Return just data disks (non-isos)
 | 
			
		||||
	vdis = make([]xenapi.VDIRef, 0)
 | 
			
		||||
	vbds, err := c.client.VM.GetVBDs(c.session, vmRef)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, vbd := range vbds {
 | 
			
		||||
		rec, err := c.client.VBD.GetRecord(c.session, vbd)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		if rec.Type == "Disk" {
 | 
			
		||||
 | 
			
		||||
			vdi, err := c.client.VBD.GetVDI(c.session, vbd)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			vdis = append(vdis, vdi)
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return vdis, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *VM) GetGuestMetricsRef() (ref string, err error) {
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = self.Client.APICall(&result, "VM.get_guest_metrics", self.Ref)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
	ref = result.Value.(string)
 | 
			
		||||
	return ref, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *VM) GetGuestMetrics() (metrics map[string]interface{}, err error) {
 | 
			
		||||
	metrics_ref, err := self.GetGuestMetricsRef()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if metrics_ref == "OpaqueRef:NULL" {
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = self.Client.APICall(&result, "VM_guest_metrics.get_record", metrics_ref)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return result.Value.(xmlrpc.Struct), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *VM) SetStaticMemoryRange(min, max uint) (err error) {
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	strMin := fmt.Sprintf("%d", min)
 | 
			
		||||
	strMax := fmt.Sprintf("%d", max)
 | 
			
		||||
	err = self.Client.APICall(&result, "VM.set_memory_limits", self.Ref, strMin, strMax, strMin, strMax)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ConnectVdi(c *Connection, vmRef xenapi.VMRef, vdiRef xenapi.VDIRef, vbdType xenapi.VbdType) (err error) {
 | 
			
		||||
 | 
			
		||||
	var mode xenapi.VbdMode
 | 
			
		||||
	var unpluggable bool
 | 
			
		||||
	var bootable bool
 | 
			
		||||
	var t xenapi.VbdType
 | 
			
		||||
	switch vbdType {
 | 
			
		||||
	case xenapi.VbdTypeCD:
 | 
			
		||||
		mode = xenapi.VbdModeRO
 | 
			
		||||
		bootable = true
 | 
			
		||||
		unpluggable = false
 | 
			
		||||
		t = xenapi.VbdTypeCD
 | 
			
		||||
	case xenapi.VbdTypeDisk:
 | 
			
		||||
		mode = xenapi.VbdModeRW
 | 
			
		||||
		bootable = false
 | 
			
		||||
		unpluggable = false
 | 
			
		||||
		t = xenapi.VbdTypeDisk
 | 
			
		||||
	case xenapi.VbdTypeFloppy:
 | 
			
		||||
		mode = xenapi.VbdModeRW
 | 
			
		||||
		bootable = false
 | 
			
		||||
		unpluggable = true
 | 
			
		||||
		t = xenapi.VbdTypeFloppy
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vbd_ref, err := c.client.VBD.Create(c.session, xenapi.VBDRecord{
 | 
			
		||||
		VM:         xenapi.VMRef(vmRef),
 | 
			
		||||
		VDI:        xenapi.VDIRef(vdiRef),
 | 
			
		||||
		Userdevice: "autodetect",
 | 
			
		||||
		Empty:      false,
 | 
			
		||||
		// OtherConfig: map[string]interface{{}},
 | 
			
		||||
		QosAlgorithmType: "",
 | 
			
		||||
		// QosAlgorithmParams: map[string]interface{{}},
 | 
			
		||||
		Mode:        mode,
 | 
			
		||||
		Unpluggable: unpluggable,
 | 
			
		||||
		Bootable:    bootable,
 | 
			
		||||
		Type:        t,
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fmt.Println("VBD Ref:", vbd_ref)
 | 
			
		||||
 | 
			
		||||
	uuid, err := c.client.VBD.GetUUID(c.session, vbd_ref)
 | 
			
		||||
 | 
			
		||||
	fmt.Println("VBD UUID: ", uuid)
 | 
			
		||||
	/*
 | 
			
		||||
	   // 2. Plug VBD (Non need - the VM hasn't booted.
 | 
			
		||||
	   // @todo - check VM state
 | 
			
		||||
	   result = APIResult{}
 | 
			
		||||
	   err = self.Client.APICall(&result, "VBD.plug", vbd_ref)
 | 
			
		||||
 | 
			
		||||
	   if err != nil {
 | 
			
		||||
	       return err
 | 
			
		||||
	   }
 | 
			
		||||
	*/
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func DisconnectVdi(c *Connection, vmRef xenapi.VMRef, vdi xenapi.VDIRef) error {
 | 
			
		||||
	vbds, err := c.client.VM.GetVBDs(c.session, vmRef)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Unable to get VM VBDs: %s", err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, vbd := range vbds {
 | 
			
		||||
		rec, err := c.client.VBD.GetRecord(c.session, vbd)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("Could not get record for VBD '%s': %s", vbd, err.Error())
 | 
			
		||||
		}
 | 
			
		||||
		recVdi := rec.VDI
 | 
			
		||||
		if recVdi == vdi {
 | 
			
		||||
			_ = c.client.VBD.Unplug(c.session, vbd)
 | 
			
		||||
			err = c.client.VBD.Destroy(c.session, vbd)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return fmt.Errorf("Could not destroy VBD '%s': %s", vbd, err.Error())
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return nil
 | 
			
		||||
		} else {
 | 
			
		||||
			log.Printf("Could not find VDI record in VBD '%s'", vbd)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return fmt.Errorf("Could not find VBD for VDI '%s'", vdi)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *VM) SetPlatform(params map[string]string) (err error) {
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	platform_rec := make(xmlrpc.Struct)
 | 
			
		||||
	for key, value := range params {
 | 
			
		||||
		platform_rec[key] = value
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = self.Client.APICall(&result, "VM.set_platform", self.Ref, platform_rec)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ConnectNetwork(c *Connection, networkRef xenapi.NetworkRef, vmRef xenapi.VMRef, device string) (*xenapi.VIFRef, error) {
 | 
			
		||||
	vif, err := c.client.VIF.Create(c.session, xenapi.VIFRecord{
 | 
			
		||||
		Network:     networkRef,
 | 
			
		||||
		VM:          vmRef,
 | 
			
		||||
		Device:      device,
 | 
			
		||||
		LockingMode: xenapi.VifLockingModeNetworkDefault,
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	log.Printf("Created the following VIF: %s", vif)
 | 
			
		||||
 | 
			
		||||
	return &vif, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//      Setters
 | 
			
		||||
 | 
			
		||||
func (self *VM) SetIsATemplate(is_a_template bool) (err error) {
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = self.Client.APICall(&result, "VM.set_is_a_template", self.Ref, is_a_template)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SR associated functions
 | 
			
		||||
 | 
			
		||||
func (self *SR) CreateVdi(name_label string, size int64) (vdi *VDI, err error) {
 | 
			
		||||
	vdi = new(VDI)
 | 
			
		||||
 | 
			
		||||
	vdi_rec := make(xmlrpc.Struct)
 | 
			
		||||
	vdi_rec["name_label"] = name_label
 | 
			
		||||
	vdi_rec["SR"] = self.Ref
 | 
			
		||||
	vdi_rec["virtual_size"] = fmt.Sprintf("%d", size)
 | 
			
		||||
	vdi_rec["type"] = "user"
 | 
			
		||||
	vdi_rec["sharable"] = false
 | 
			
		||||
	vdi_rec["read_only"] = false
 | 
			
		||||
 | 
			
		||||
	oc := make(xmlrpc.Struct)
 | 
			
		||||
	oc["temp"] = "temp"
 | 
			
		||||
	vdi_rec["other_config"] = oc
 | 
			
		||||
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = self.Client.APICall(&result, "VDI.create", vdi_rec)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vdi.Ref = result.Value.(string)
 | 
			
		||||
	vdi.Client = self.Client
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Network associated functions
 | 
			
		||||
 | 
			
		||||
func (self *Network) GetAssignedIPs() (ip_map map[string]string, err error) {
 | 
			
		||||
	ip_map = make(map[string]string, 0)
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = self.Client.APICall(&result, "network.get_assigned_ips", self.Ref)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return ip_map, err
 | 
			
		||||
	}
 | 
			
		||||
	for k, v := range result.Value.(xmlrpc.Struct) {
 | 
			
		||||
		ip_map[k] = v.(string)
 | 
			
		||||
	}
 | 
			
		||||
	return ip_map, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PIF associated functions
 | 
			
		||||
 | 
			
		||||
func (self *PIF) GetRecord() (record map[string]interface{}, err error) {
 | 
			
		||||
	record = make(map[string]interface{})
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = self.Client.APICall(&result, "PIF.get_record", self.Ref)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return record, err
 | 
			
		||||
	}
 | 
			
		||||
	for k, v := range result.Value.(xmlrpc.Struct) {
 | 
			
		||||
		record[k] = v
 | 
			
		||||
	}
 | 
			
		||||
	return record, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Pool associated functions
 | 
			
		||||
 | 
			
		||||
func (self *Pool) GetRecord() (record map[string]interface{}, err error) {
 | 
			
		||||
	record = make(map[string]interface{})
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = self.Client.APICall(&result, "pool.get_record", self.Ref)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return record, err
 | 
			
		||||
	}
 | 
			
		||||
	for k, v := range result.Value.(xmlrpc.Struct) {
 | 
			
		||||
		record[k] = v
 | 
			
		||||
	}
 | 
			
		||||
	return record, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// VBD associated functions
 | 
			
		||||
func (self *VBD) GetRecord() (record map[string]interface{}, err error) {
 | 
			
		||||
	record = make(map[string]interface{})
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = self.Client.APICall(&result, "VBD.get_record", self.Ref)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return record, err
 | 
			
		||||
	}
 | 
			
		||||
	for k, v := range result.Value.(xmlrpc.Struct) {
 | 
			
		||||
		record[k] = v
 | 
			
		||||
	}
 | 
			
		||||
	return record, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *VBD) GetVDI() (vdi *VDI, err error) {
 | 
			
		||||
	vbd_rec, err := self.GetRecord()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vdi = new(VDI)
 | 
			
		||||
	vdi.Ref = vbd_rec["VDI"].(string)
 | 
			
		||||
	vdi.Client = self.Client
 | 
			
		||||
 | 
			
		||||
	return vdi, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *VBD) Eject() (err error) {
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = self.Client.APICall(&result, "VBD.eject", self.Ref)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *VBD) Unplug() (err error) {
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = self.Client.APICall(&result, "VBD.unplug", self.Ref)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *VBD) Destroy() (err error) {
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = self.Client.APICall(&result, "VBD.destroy", self.Ref)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// VIF associated functions
 | 
			
		||||
 | 
			
		||||
func (self *VIF) Destroy() (err error) {
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = self.Client.APICall(&result, "VIF.destroy", self.Ref)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// VDI associated functions
 | 
			
		||||
 | 
			
		||||
func (self *VDI) GetUuid() (vdi_uuid string, err error) {
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = self.Client.APICall(&result, "VDI.get_uuid", self.Ref)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	vdi_uuid = result.Value.(string)
 | 
			
		||||
	return vdi_uuid, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *VDI) GetVBDs() (vbds []VBD, err error) {
 | 
			
		||||
	vbds = make([]VBD, 0)
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = self.Client.APICall(&result, "VDI.get_VBDs", self.Ref)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return vbds, err
 | 
			
		||||
	}
 | 
			
		||||
	for _, elem := range result.Value.([]interface{}) {
 | 
			
		||||
		vbd := VBD{}
 | 
			
		||||
		vbd.Ref = elem.(string)
 | 
			
		||||
		vbd.Client = self.Client
 | 
			
		||||
		vbds = append(vbds, vbd)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return vbds, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *VDI) Destroy() (err error) {
 | 
			
		||||
	result := APIResult{}
 | 
			
		||||
	err = self.Client.APICall(&result, "VDI.destroy", self.Ref)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Expose a VDI using the Transfer VM
 | 
			
		||||
// (Legacy VHD export)
 | 
			
		||||
 | 
			
		||||
type TransferRecord struct {
 | 
			
		||||
	UrlFull string `xml:"url_full,attr"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Expose(c *Connection, vdiRef xenapi.VDIRef, format string) (url string, err error) {
 | 
			
		||||
 | 
			
		||||
	hosts, err := c.client.Host.GetAll(c.session)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		err = errors.New(fmt.Sprintf("Could not retrieve hosts in the pool: %s", err.Error()))
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	host := hosts[0]
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		err = errors.New(fmt.Sprintf("Failed to get VDI uuid for %s: %s", vdiRef, err.Error()))
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	args := make(map[string]string)
 | 
			
		||||
	args["transfer_mode"] = "http"
 | 
			
		||||
	args["vdi_uuid"] = string(vdiRef)
 | 
			
		||||
	args["expose_vhd"] = "true"
 | 
			
		||||
	args["network_uuid"] = "management"
 | 
			
		||||
	args["timeout_minutes"] = "5"
 | 
			
		||||
 | 
			
		||||
	handle, err := c.client.Host.CallPlugin(c.session, host, "transfer", "expose", args)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		err = errors.New(fmt.Sprintf("Error whilst exposing VDI %s: %s", vdiRef, err.Error()))
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	args = make(map[string]string)
 | 
			
		||||
	args["record_handle"] = handle
 | 
			
		||||
	record_xml, err := c.client.Host.CallPlugin(c.session, host, "transfer", "get_record", args)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		err = errors.New(fmt.Sprintf("Unable to retrieve transfer record for VDI %s: %s", vdiRef, err.Error()))
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var record TransferRecord
 | 
			
		||||
	xml.Unmarshal([]byte(record_xml), &record)
 | 
			
		||||
 | 
			
		||||
	if record.UrlFull == "" {
 | 
			
		||||
		return "", errors.New(fmt.Sprintf("Error: did not parse XML properly: '%s'", record_xml))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Handles either raw or VHD formats
 | 
			
		||||
 | 
			
		||||
	switch format {
 | 
			
		||||
	case "vhd":
 | 
			
		||||
		url = fmt.Sprintf("%s.vhd", record.UrlFull)
 | 
			
		||||
 | 
			
		||||
	case "raw":
 | 
			
		||||
		url = record.UrlFull
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Unexpose(c *Connection, vdiRef xenapi.VDIRef) (err error) {
 | 
			
		||||
 | 
			
		||||
	disk_uuid, err := c.client.VDI.GetUUID(c.session, vdiRef)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hosts, err := c.client.Host.GetAll(c.session)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		err = errors.New(fmt.Sprintf("Could not retrieve hosts in the pool: %s", err.Error()))
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	host := hosts[0]
 | 
			
		||||
 | 
			
		||||
	args := make(map[string]string)
 | 
			
		||||
	args["vdi_uuid"] = disk_uuid
 | 
			
		||||
 | 
			
		||||
	result, err := c.client.Host.CallPlugin(c.session, host, "transfer", "unexpose", args)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Println(fmt.Sprintf("Unexpose result: %s", result))
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Task associated functions
 | 
			
		||||
 | 
			
		||||
// func (self *Task) GetResult() (object *XenAPIObject, err error) {
 | 
			
		||||
// 	result := APIResult{}
 | 
			
		||||
// 	err = self.Client.APICall(&result, "task.get_result", self.Ref)
 | 
			
		||||
// 	if err != nil {
 | 
			
		||||
// 		return
 | 
			
		||||
// 	}
 | 
			
		||||
// 	switch ref := result.Value.(type) {
 | 
			
		||||
// 	case string:
 | 
			
		||||
// 		// @fixme: xapi currently sends us an xmlrpc-encoded string via xmlrpc.
 | 
			
		||||
// 		// This seems to be a bug in xapi. Remove this workaround when it's fixed
 | 
			
		||||
// 		re := regexp.MustCompile("^<value><array><data><value>([^<]*)</value>.*</data></array></value>$")
 | 
			
		||||
// 		match := re.FindStringSubmatch(ref)
 | 
			
		||||
// 		if match == nil {
 | 
			
		||||
// 			object = nil
 | 
			
		||||
// 		} else {
 | 
			
		||||
// 			object = &XenAPIObject{
 | 
			
		||||
// 				Ref:    match[1],
 | 
			
		||||
// 				Client: self.Client,
 | 
			
		||||
// 			}
 | 
			
		||||
// 		}
 | 
			
		||||
// 	case nil:
 | 
			
		||||
// 		object = nil
 | 
			
		||||
// 	default:
 | 
			
		||||
// 		err = fmt.Errorf("task.get_result: unknown value type %T (expected string or nil)", ref)
 | 
			
		||||
// 	}
 | 
			
		||||
// 	return
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
// Client Initiator
 | 
			
		||||
type Connection struct {
 | 
			
		||||
	client   *xenapi.Client
 | 
			
		||||
	session  xenapi.SessionRef
 | 
			
		||||
	Host     string
 | 
			
		||||
	Username string
 | 
			
		||||
	Password string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c Connection) GetSession() string {
 | 
			
		||||
	return string(c.session)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewXenAPIClient(host, username, password string) (*Connection, error) {
 | 
			
		||||
	client, err := xenapi.NewClient("https://"+host, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	session, err := client.Session.LoginWithPassword(username, password, "1.0", "packer")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &Connection{client, session, host, username, password}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Connection) GetClient() *xenapi.Client {
 | 
			
		||||
	return c.client
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Connection) GetSessionRef() xenapi.SessionRef {
 | 
			
		||||
	return c.session
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										289
									
								
								builder/xenserver/common/common_config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										289
									
								
								builder/xenserver/common/common_config.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,289 @@
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/common"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/multistep"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
 | 
			
		||||
	xenapi "github.com/terra-farm/go-xen-api-client"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type CommonConfig struct {
 | 
			
		||||
	Username string `mapstructure:"remote_username"`
 | 
			
		||||
	Password string `mapstructure:"remote_password"`
 | 
			
		||||
	HostIp   string `mapstructure:"remote_host"`
 | 
			
		||||
 | 
			
		||||
	VMName             string   `mapstructure:"vm_name"`
 | 
			
		||||
	VMDescription      string   `mapstructure:"vm_description"`
 | 
			
		||||
	SrName             string   `mapstructure:"sr_name"`
 | 
			
		||||
	SrISOName          string   `mapstructure:"sr_iso_name"`
 | 
			
		||||
	FloppyFiles        []string `mapstructure:"floppy_files"`
 | 
			
		||||
	NetworkNames       []string `mapstructure:"network_names"`
 | 
			
		||||
	ExportNetworkNames []string `mapstructure:"export_network_names"`
 | 
			
		||||
 | 
			
		||||
	HostPortMin uint `mapstructure:"host_port_min"`
 | 
			
		||||
	HostPortMax uint `mapstructure:"host_port_max"`
 | 
			
		||||
 | 
			
		||||
	BootCommand     []string `mapstructure:"boot_command"`
 | 
			
		||||
	ShutdownCommand string   `mapstructure:"shutdown_command"`
 | 
			
		||||
 | 
			
		||||
	RawBootWait string `mapstructure:"boot_wait"`
 | 
			
		||||
	BootWait    time.Duration
 | 
			
		||||
 | 
			
		||||
	ToolsIsoName string `mapstructure:"tools_iso_name"`
 | 
			
		||||
 | 
			
		||||
	HTTPDir     string `mapstructure:"http_directory"`
 | 
			
		||||
	HTTPPortMin uint   `mapstructure:"http_port_min"`
 | 
			
		||||
	HTTPPortMax uint   `mapstructure:"http_port_max"`
 | 
			
		||||
 | 
			
		||||
	//	SSHHostPortMin    uint   `mapstructure:"ssh_host_port_min"`
 | 
			
		||||
	//	SSHHostPortMax    uint   `mapstructure:"ssh_host_port_max"`
 | 
			
		||||
	SSHKeyPath  string `mapstructure:"ssh_key_path"`
 | 
			
		||||
	SSHPassword string `mapstructure:"ssh_password"`
 | 
			
		||||
	SSHPort     uint   `mapstructure:"ssh_port"`
 | 
			
		||||
	SSHUser     string `mapstructure:"ssh_username"`
 | 
			
		||||
	SSHConfig   `mapstructure:",squash"`
 | 
			
		||||
 | 
			
		||||
	RawSSHWaitTimeout string `mapstructure:"ssh_wait_timeout"`
 | 
			
		||||
	SSHWaitTimeout    time.Duration
 | 
			
		||||
 | 
			
		||||
	OutputDir string `mapstructure:"output_directory"`
 | 
			
		||||
	Format    string `mapstructure:"format"`
 | 
			
		||||
	KeepVM    string `mapstructure:"keep_vm"`
 | 
			
		||||
	IPGetter  string `mapstructure:"ip_getter"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *CommonConfig) Prepare(ctx *interpolate.Context, pc *common.PackerConfig) []error {
 | 
			
		||||
	var err error
 | 
			
		||||
	var errs []error
 | 
			
		||||
 | 
			
		||||
	// Set default values
 | 
			
		||||
 | 
			
		||||
	if c.HostPortMin == 0 {
 | 
			
		||||
		c.HostPortMin = 5900
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.HostPortMax == 0 {
 | 
			
		||||
		c.HostPortMax = 6000
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.RawBootWait == "" {
 | 
			
		||||
		c.RawBootWait = "5s"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.ToolsIsoName == "" {
 | 
			
		||||
		c.ToolsIsoName = "xs-tools.iso"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.HTTPPortMin == 0 {
 | 
			
		||||
		c.HTTPPortMin = 8000
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.HTTPPortMax == 0 {
 | 
			
		||||
		c.HTTPPortMax = 9000
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.RawSSHWaitTimeout == "" {
 | 
			
		||||
		c.RawSSHWaitTimeout = "200m"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.FloppyFiles == nil {
 | 
			
		||||
		c.FloppyFiles = make([]string, 0)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
		if c.SSHHostPortMin == 0 {
 | 
			
		||||
			c.SSHHostPortMin = 2222
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if c.SSHHostPortMax == 0 {
 | 
			
		||||
			c.SSHHostPortMax = 4444
 | 
			
		||||
		}
 | 
			
		||||
	*/
 | 
			
		||||
 | 
			
		||||
	if c.SSHPort == 0 {
 | 
			
		||||
		c.SSHPort = 22
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.RawSSHWaitTimeout == "" {
 | 
			
		||||
		c.RawSSHWaitTimeout = "20m"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.OutputDir == "" {
 | 
			
		||||
		c.OutputDir = fmt.Sprintf("output-%s", pc.PackerBuildName)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.VMName == "" {
 | 
			
		||||
		c.VMName = fmt.Sprintf("packer-%s-{{timestamp}}", pc.PackerBuildName)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Format == "" {
 | 
			
		||||
		c.Format = "xva"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.KeepVM == "" {
 | 
			
		||||
		c.KeepVM = "never"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.IPGetter == "" {
 | 
			
		||||
		c.IPGetter = "auto"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Validation
 | 
			
		||||
 | 
			
		||||
	if c.Username == "" {
 | 
			
		||||
		errs = append(errs, errors.New("remote_username must be specified."))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Password == "" {
 | 
			
		||||
		errs = append(errs, errors.New("remote_password must be specified."))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.HostIp == "" {
 | 
			
		||||
		errs = append(errs, errors.New("remote_host must be specified."))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.HostPortMin > c.HostPortMax {
 | 
			
		||||
		errs = append(errs, errors.New("the host min port must be less than the max"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.HTTPPortMin > c.HTTPPortMax {
 | 
			
		||||
		errs = append(errs, errors.New("the HTTP min port must be less than the max"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.BootWait, err = time.ParseDuration(c.RawBootWait)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		errs = append(errs, fmt.Errorf("Failed to parse boot_wait: %s", err))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.SSHKeyPath != "" {
 | 
			
		||||
		if _, err := os.Stat(c.SSHKeyPath); err != nil {
 | 
			
		||||
			errs = append(errs, fmt.Errorf("ssh_key_path is invalid: %s", err))
 | 
			
		||||
		} else if _, err := FileSigner(c.SSHKeyPath); err != nil {
 | 
			
		||||
			errs = append(errs, fmt.Errorf("ssh_key_path is invalid: %s", err))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
		if c.SSHHostPortMin > c.SSHHostPortMax {
 | 
			
		||||
			errs = append(errs,
 | 
			
		||||
				errors.New("ssh_host_port_min must be less than ssh_host_port_max"))
 | 
			
		||||
		}
 | 
			
		||||
	*/
 | 
			
		||||
 | 
			
		||||
	if c.SSHUser == "" {
 | 
			
		||||
		errs = append(errs, errors.New("An ssh_username must be specified."))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.SSHWaitTimeout, err = time.ParseDuration(c.RawSSHWaitTimeout)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		errs = append(errs, fmt.Errorf("Failed to parse ssh_wait_timeout: %s", err))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch c.Format {
 | 
			
		||||
	case "xva", "xva_compressed", "vdi_raw", "vdi_vhd", "none":
 | 
			
		||||
	default:
 | 
			
		||||
		errs = append(errs, errors.New("format must be one of 'xva', 'vdi_raw', 'vdi_vhd', 'none'"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch c.KeepVM {
 | 
			
		||||
	case "always", "never", "on_success":
 | 
			
		||||
	default:
 | 
			
		||||
		errs = append(errs, errors.New("keep_vm must be one of 'always', 'never', 'on_success'"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch c.IPGetter {
 | 
			
		||||
	case "auto", "tools", "http":
 | 
			
		||||
	default:
 | 
			
		||||
		errs = append(errs, errors.New("ip_getter must be one of 'auto', 'tools', 'http'"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return errs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// steps should check config.ShouldKeepVM first before cleaning up the VM
 | 
			
		||||
func (c CommonConfig) ShouldKeepVM(state multistep.StateBag) bool {
 | 
			
		||||
	switch c.KeepVM {
 | 
			
		||||
	case "always":
 | 
			
		||||
		return true
 | 
			
		||||
	case "never":
 | 
			
		||||
		return false
 | 
			
		||||
	case "on_success":
 | 
			
		||||
		// only keep instance if build was successful
 | 
			
		||||
		_, cancelled := state.GetOk(multistep.StateCancelled)
 | 
			
		||||
		_, halted := state.GetOk(multistep.StateHalted)
 | 
			
		||||
		return !(cancelled || halted)
 | 
			
		||||
	default:
 | 
			
		||||
		panic(fmt.Sprintf("Unknown keep_vm value '%s'", c.KeepVM))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (config CommonConfig) GetSR(c *Connection) (xenapi.SRRef, error) {
 | 
			
		||||
	var srRef xenapi.SRRef
 | 
			
		||||
	if config.SrName == "" {
 | 
			
		||||
		hostRef, err := c.GetClient().Session.GetThisHost(c.session, c.session)
 | 
			
		||||
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return srRef, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		pools, err := c.GetClient().Pool.GetAllRecords(c.session)
 | 
			
		||||
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return srRef, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, pool := range pools {
 | 
			
		||||
			if pool.Master == hostRef {
 | 
			
		||||
				return pool.DefaultSR, nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return srRef, errors.New(fmt.Sprintf("failed to find default SR on host '%s'", hostRef))
 | 
			
		||||
 | 
			
		||||
	} else {
 | 
			
		||||
		// Use the provided name label to find the SR to use
 | 
			
		||||
		srs, err := c.GetClient().SR.GetByNameLabel(c.session, config.SrName)
 | 
			
		||||
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return srRef, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		switch {
 | 
			
		||||
		case len(srs) == 0:
 | 
			
		||||
			return srRef, fmt.Errorf("Couldn't find a SR with the specified name-label '%s'", config.SrName)
 | 
			
		||||
		case len(srs) > 1:
 | 
			
		||||
			return srRef, fmt.Errorf("Found more than one SR with the name '%s'. The name must be unique", config.SrName)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return srs[0], nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (config CommonConfig) GetISOSR(c *Connection) (xenapi.SRRef, error) {
 | 
			
		||||
	var srRef xenapi.SRRef
 | 
			
		||||
	if config.SrISOName == "" {
 | 
			
		||||
		return srRef, errors.New("sr_iso_name must be specified in the packer configuration")
 | 
			
		||||
 | 
			
		||||
	} else {
 | 
			
		||||
		// Use the provided name label to find the SR to use
 | 
			
		||||
		srs, err := c.GetClient().SR.GetByNameLabel(c.session, config.SrName)
 | 
			
		||||
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return srRef, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		switch {
 | 
			
		||||
		case len(srs) == 0:
 | 
			
		||||
			return srRef, fmt.Errorf("Couldn't find a SR with the specified name-label '%s'", config.SrName)
 | 
			
		||||
		case len(srs) > 1:
 | 
			
		||||
			return srRef, fmt.Errorf("Found more than one SR with the name '%s'. The name must be unique", config.SrName)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return srs[0], nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										43
									
								
								builder/xenserver/common/config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								builder/xenserver/common/config.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
//go:generate mapstructure-to-hcl2 -type Config
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/common"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/communicator"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Config struct {
 | 
			
		||||
	common.PackerConfig `mapstructure:",squash"`
 | 
			
		||||
	CommonConfig        `mapstructure:",squash"`
 | 
			
		||||
	Comm                communicator.Config `mapstructure:",squash"`
 | 
			
		||||
 | 
			
		||||
	VCPUsMax       uint              `mapstructure:"vcpus_max"`
 | 
			
		||||
	VCPUsAtStartup uint              `mapstructure:"vcpus_atstartup"`
 | 
			
		||||
	VMMemory       uint              `mapstructure:"vm_memory"`
 | 
			
		||||
	DiskSize       uint              `mapstructure:"disk_size"`
 | 
			
		||||
	CloneTemplate  string            `mapstructure:"clone_template"`
 | 
			
		||||
	VMOtherConfig  map[string]string `mapstructure:"vm_other_config"`
 | 
			
		||||
 | 
			
		||||
	ISOChecksum     string   `mapstructure:"iso_checksum"`
 | 
			
		||||
	ISOChecksumType string   `mapstructure:"iso_checksum_type"`
 | 
			
		||||
	ISOUrls         []string `mapstructure:"iso_urls"`
 | 
			
		||||
	ISOUrl          string   `mapstructure:"iso_url"`
 | 
			
		||||
	ISOName         string   `mapstructure:"iso_name"`
 | 
			
		||||
 | 
			
		||||
	PlatformArgs map[string]string `mapstructure:"platform_args"`
 | 
			
		||||
 | 
			
		||||
	RawInstallTimeout string        `mapstructure:"install_timeout"`
 | 
			
		||||
	InstallTimeout    time.Duration ``
 | 
			
		||||
	SourcePath        string        `mapstructure:"source_path"`
 | 
			
		||||
 | 
			
		||||
	Firmware string `mapstructure:"firmware"`
 | 
			
		||||
 | 
			
		||||
	ctx interpolate.Context
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c Config) GetInterpContext() *interpolate.Context {
 | 
			
		||||
	return &c.ctx
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										225
									
								
								builder/xenserver/common/config.hcl2spec.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										225
									
								
								builder/xenserver/common/config.hcl2spec.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,225 @@
 | 
			
		||||
// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT.
 | 
			
		||||
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/hashicorp/hcl/v2/hcldec"
 | 
			
		||||
	"github.com/zclconf/go-cty/cty"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// FlatConfig is an auto-generated flat version of Config.
 | 
			
		||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
 | 
			
		||||
type FlatConfig struct {
 | 
			
		||||
	PackerBuildName           *string           `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
 | 
			
		||||
	PackerBuilderType         *string           `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
 | 
			
		||||
	PackerDebug               *bool             `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
 | 
			
		||||
	PackerForce               *bool             `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
 | 
			
		||||
	PackerOnError             *string           `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
 | 
			
		||||
	PackerUserVars            map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
 | 
			
		||||
	PackerSensitiveVars       []string          `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
 | 
			
		||||
	Username                  *string           `mapstructure:"remote_username" cty:"remote_username" hcl:"remote_username"`
 | 
			
		||||
	Password                  *string           `mapstructure:"remote_password" cty:"remote_password" hcl:"remote_password"`
 | 
			
		||||
	HostIp                    *string           `mapstructure:"remote_host" cty:"remote_host" hcl:"remote_host"`
 | 
			
		||||
	VMName                    *string           `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
 | 
			
		||||
	VMDescription             *string           `mapstructure:"vm_description" cty:"vm_description" hcl:"vm_description"`
 | 
			
		||||
	SrName                    *string           `mapstructure:"sr_name" cty:"sr_name" hcl:"sr_name"`
 | 
			
		||||
	SrISOName                 *string           `mapstructure:"sr_iso_name" cty:"sr_iso_name" hcl:"sr_iso_name"`
 | 
			
		||||
	FloppyFiles               []string          `mapstructure:"floppy_files" cty:"floppy_files" hcl:"floppy_files"`
 | 
			
		||||
	NetworkNames              []string          `mapstructure:"network_names" cty:"network_names" hcl:"network_names"`
 | 
			
		||||
	ExportNetworkNames        []string          `mapstructure:"export_network_names" cty:"export_network_names" hcl:"export_network_names"`
 | 
			
		||||
	HostPortMin               *uint             `mapstructure:"host_port_min" cty:"host_port_min" hcl:"host_port_min"`
 | 
			
		||||
	HostPortMax               *uint             `mapstructure:"host_port_max" cty:"host_port_max" hcl:"host_port_max"`
 | 
			
		||||
	BootCommand               []string          `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
 | 
			
		||||
	ShutdownCommand           *string           `mapstructure:"shutdown_command" cty:"shutdown_command" hcl:"shutdown_command"`
 | 
			
		||||
	RawBootWait               *string           `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
 | 
			
		||||
	ToolsIsoName              *string           `mapstructure:"tools_iso_name" cty:"tools_iso_name" hcl:"tools_iso_name"`
 | 
			
		||||
	HTTPDir                   *string           `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
 | 
			
		||||
	HTTPPortMin               *uint             `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
 | 
			
		||||
	HTTPPortMax               *uint             `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
 | 
			
		||||
	SSHKeyPath                *string           `mapstructure:"ssh_key_path" cty:"ssh_key_path" hcl:"ssh_key_path"`
 | 
			
		||||
	SSHPassword               *string           `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
 | 
			
		||||
	SSHPort                   *uint             `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
 | 
			
		||||
	SSHUser                   *string           `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
 | 
			
		||||
	Type                      *string           `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
 | 
			
		||||
	PauseBeforeConnect        *string           `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
 | 
			
		||||
	SSHHost                   *string           `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
 | 
			
		||||
	SSHKeyPairName            *string           `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
 | 
			
		||||
	SSHTemporaryKeyPairName   *string           `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
 | 
			
		||||
	SSHTemporaryKeyPairType   *string           `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
 | 
			
		||||
	SSHTemporaryKeyPairBits   *int              `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
 | 
			
		||||
	SSHCiphers                []string          `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
 | 
			
		||||
	SSHClearAuthorizedKeys    *bool             `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
 | 
			
		||||
	SSHKEXAlgos               []string          `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
 | 
			
		||||
	SSHPrivateKeyFile         *string           `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
 | 
			
		||||
	SSHCertificateFile        *string           `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
 | 
			
		||||
	SSHPty                    *bool             `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
 | 
			
		||||
	SSHTimeout                *string           `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
 | 
			
		||||
	SSHWaitTimeout            *string           `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
 | 
			
		||||
	SSHAgentAuth              *bool             `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
 | 
			
		||||
	SSHDisableAgentForwarding *bool             `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
 | 
			
		||||
	SSHHandshakeAttempts      *int              `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
 | 
			
		||||
	SSHBastionHost            *string           `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
 | 
			
		||||
	SSHBastionPort            *int              `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"`
 | 
			
		||||
	SSHBastionAgentAuth       *bool             `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"`
 | 
			
		||||
	SSHBastionUsername        *string           `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"`
 | 
			
		||||
	SSHBastionPassword        *string           `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
 | 
			
		||||
	SSHBastionInteractive     *bool             `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
 | 
			
		||||
	SSHBastionPrivateKeyFile  *string           `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
 | 
			
		||||
	SSHBastionCertificateFile *string           `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
 | 
			
		||||
	SSHFileTransferMethod     *string           `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
 | 
			
		||||
	SSHProxyHost              *string           `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
 | 
			
		||||
	SSHProxyPort              *int              `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
 | 
			
		||||
	SSHProxyUsername          *string           `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"`
 | 
			
		||||
	SSHProxyPassword          *string           `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"`
 | 
			
		||||
	SSHKeepAliveInterval      *string           `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"`
 | 
			
		||||
	SSHReadWriteTimeout       *string           `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
 | 
			
		||||
	SSHRemoteTunnels          []string          `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
 | 
			
		||||
	SSHLocalTunnels           []string          `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
 | 
			
		||||
	SSHPublicKey              []byte            `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
 | 
			
		||||
	SSHPrivateKey             []byte            `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
 | 
			
		||||
	WinRMUser                 *string           `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
 | 
			
		||||
	WinRMPassword             *string           `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
 | 
			
		||||
	WinRMHost                 *string           `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
 | 
			
		||||
	WinRMNoProxy              *bool             `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"`
 | 
			
		||||
	WinRMPort                 *int              `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"`
 | 
			
		||||
	WinRMTimeout              *string           `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"`
 | 
			
		||||
	WinRMUseSSL               *bool             `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"`
 | 
			
		||||
	WinRMInsecure             *bool             `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
 | 
			
		||||
	WinRMUseNTLM              *bool             `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
 | 
			
		||||
	SSHHostPortMin            *uint             `mapstructure:"ssh_host_port_min" cty:"ssh_host_port_min" hcl:"ssh_host_port_min"`
 | 
			
		||||
	SSHHostPortMax            *uint             `mapstructure:"ssh_host_port_max" cty:"ssh_host_port_max" hcl:"ssh_host_port_max"`
 | 
			
		||||
	SSHSkipNatMapping         *bool             `mapstructure:"ssh_skip_nat_mapping" cty:"ssh_skip_nat_mapping" hcl:"ssh_skip_nat_mapping"`
 | 
			
		||||
	OutputDir                 *string           `mapstructure:"output_directory" cty:"output_directory" hcl:"output_directory"`
 | 
			
		||||
	Format                    *string           `mapstructure:"format" cty:"format" hcl:"format"`
 | 
			
		||||
	KeepVM                    *string           `mapstructure:"keep_vm" cty:"keep_vm" hcl:"keep_vm"`
 | 
			
		||||
	IPGetter                  *string           `mapstructure:"ip_getter" cty:"ip_getter" hcl:"ip_getter"`
 | 
			
		||||
	VCPUsMax                  *uint             `mapstructure:"vcpus_max" cty:"vcpus_max" hcl:"vcpus_max"`
 | 
			
		||||
	VCPUsAtStartup            *uint             `mapstructure:"vcpus_atstartup" cty:"vcpus_atstartup" hcl:"vcpus_atstartup"`
 | 
			
		||||
	VMMemory                  *uint             `mapstructure:"vm_memory" cty:"vm_memory" hcl:"vm_memory"`
 | 
			
		||||
	DiskSize                  *uint             `mapstructure:"disk_size" cty:"disk_size" hcl:"disk_size"`
 | 
			
		||||
	CloneTemplate             *string           `mapstructure:"clone_template" cty:"clone_template" hcl:"clone_template"`
 | 
			
		||||
	VMOtherConfig             map[string]string `mapstructure:"vm_other_config" cty:"vm_other_config" hcl:"vm_other_config"`
 | 
			
		||||
	ISOChecksum               *string           `mapstructure:"iso_checksum" cty:"iso_checksum" hcl:"iso_checksum"`
 | 
			
		||||
	ISOChecksumType           *string           `mapstructure:"iso_checksum_type" cty:"iso_checksum_type" hcl:"iso_checksum_type"`
 | 
			
		||||
	ISOUrls                   []string          `mapstructure:"iso_urls" cty:"iso_urls" hcl:"iso_urls"`
 | 
			
		||||
	ISOUrl                    *string           `mapstructure:"iso_url" cty:"iso_url" hcl:"iso_url"`
 | 
			
		||||
	ISOName                   *string           `mapstructure:"iso_name" cty:"iso_name" hcl:"iso_name"`
 | 
			
		||||
	PlatformArgs              map[string]string `mapstructure:"platform_args" cty:"platform_args" hcl:"platform_args"`
 | 
			
		||||
	RawInstallTimeout         *string           `mapstructure:"install_timeout" cty:"install_timeout" hcl:"install_timeout"`
 | 
			
		||||
	SourcePath                *string           `mapstructure:"source_path" cty:"source_path" hcl:"source_path"`
 | 
			
		||||
	Firmware                  *string           `mapstructure:"firmware" cty:"firmware" hcl:"firmware"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FlatMapstructure returns a new FlatConfig.
 | 
			
		||||
// FlatConfig is an auto-generated flat version of Config.
 | 
			
		||||
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
 | 
			
		||||
func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
 | 
			
		||||
	return new(FlatConfig)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HCL2Spec returns the hcl spec of a Config.
 | 
			
		||||
// This spec is used by HCL to read the fields of Config.
 | 
			
		||||
// The decoded values from this spec will then be applied to a FlatConfig.
 | 
			
		||||
func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
 | 
			
		||||
	s := map[string]hcldec.Spec{
 | 
			
		||||
		"packer_build_name":            &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false},
 | 
			
		||||
		"packer_builder_type":          &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false},
 | 
			
		||||
		"packer_debug":                 &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false},
 | 
			
		||||
		"packer_force":                 &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false},
 | 
			
		||||
		"packer_on_error":              &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false},
 | 
			
		||||
		"packer_user_variables":        &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
 | 
			
		||||
		"packer_sensitive_variables":   &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
 | 
			
		||||
		"remote_username":              &hcldec.AttrSpec{Name: "remote_username", Type: cty.String, Required: false},
 | 
			
		||||
		"remote_password":              &hcldec.AttrSpec{Name: "remote_password", Type: cty.String, Required: false},
 | 
			
		||||
		"remote_host":                  &hcldec.AttrSpec{Name: "remote_host", Type: cty.String, Required: false},
 | 
			
		||||
		"vm_name":                      &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false},
 | 
			
		||||
		"vm_description":               &hcldec.AttrSpec{Name: "vm_description", Type: cty.String, Required: false},
 | 
			
		||||
		"sr_name":                      &hcldec.AttrSpec{Name: "sr_name", Type: cty.String, Required: false},
 | 
			
		||||
		"sr_iso_name":                  &hcldec.AttrSpec{Name: "sr_iso_name", Type: cty.String, Required: false},
 | 
			
		||||
		"floppy_files":                 &hcldec.AttrSpec{Name: "floppy_files", Type: cty.List(cty.String), Required: false},
 | 
			
		||||
		"network_names":                &hcldec.AttrSpec{Name: "network_names", Type: cty.List(cty.String), Required: false},
 | 
			
		||||
		"export_network_names":         &hcldec.AttrSpec{Name: "export_network_names", Type: cty.List(cty.String), Required: false},
 | 
			
		||||
		"host_port_min":                &hcldec.AttrSpec{Name: "host_port_min", Type: cty.Number, Required: false},
 | 
			
		||||
		"host_port_max":                &hcldec.AttrSpec{Name: "host_port_max", Type: cty.Number, Required: false},
 | 
			
		||||
		"boot_command":                 &hcldec.AttrSpec{Name: "boot_command", Type: cty.List(cty.String), Required: false},
 | 
			
		||||
		"shutdown_command":             &hcldec.AttrSpec{Name: "shutdown_command", Type: cty.String, Required: false},
 | 
			
		||||
		"boot_wait":                    &hcldec.AttrSpec{Name: "boot_wait", Type: cty.String, Required: false},
 | 
			
		||||
		"tools_iso_name":               &hcldec.AttrSpec{Name: "tools_iso_name", Type: cty.String, Required: false},
 | 
			
		||||
		"http_directory":               &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
 | 
			
		||||
		"http_port_min":                &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
 | 
			
		||||
		"http_port_max":                &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
 | 
			
		||||
		"ssh_key_path":                 &hcldec.AttrSpec{Name: "ssh_key_path", Type: cty.String, Required: false},
 | 
			
		||||
		"ssh_password":                 &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
 | 
			
		||||
		"ssh_port":                     &hcldec.AttrSpec{Name: "ssh_port", Type: cty.Number, Required: false},
 | 
			
		||||
		"ssh_username":                 &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false},
 | 
			
		||||
		"communicator":                 &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false},
 | 
			
		||||
		"pause_before_connecting":      &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false},
 | 
			
		||||
		"ssh_host":                     &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false},
 | 
			
		||||
		"ssh_keypair_name":             &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
 | 
			
		||||
		"temporary_key_pair_name":      &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
 | 
			
		||||
		"temporary_key_pair_type":      &hcldec.AttrSpec{Name: "temporary_key_pair_type", Type: cty.String, Required: false},
 | 
			
		||||
		"temporary_key_pair_bits":      &hcldec.AttrSpec{Name: "temporary_key_pair_bits", Type: cty.Number, Required: false},
 | 
			
		||||
		"ssh_ciphers":                  &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
 | 
			
		||||
		"ssh_clear_authorized_keys":    &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
 | 
			
		||||
		"ssh_key_exchange_algorithms":  &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
 | 
			
		||||
		"ssh_private_key_file":         &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false},
 | 
			
		||||
		"ssh_certificate_file":         &hcldec.AttrSpec{Name: "ssh_certificate_file", Type: cty.String, Required: false},
 | 
			
		||||
		"ssh_pty":                      &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false},
 | 
			
		||||
		"ssh_timeout":                  &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false},
 | 
			
		||||
		"ssh_wait_timeout":             &hcldec.AttrSpec{Name: "ssh_wait_timeout", Type: cty.String, Required: false},
 | 
			
		||||
		"ssh_agent_auth":               &hcldec.AttrSpec{Name: "ssh_agent_auth", Type: cty.Bool, Required: false},
 | 
			
		||||
		"ssh_disable_agent_forwarding": &hcldec.AttrSpec{Name: "ssh_disable_agent_forwarding", Type: cty.Bool, Required: false},
 | 
			
		||||
		"ssh_handshake_attempts":       &hcldec.AttrSpec{Name: "ssh_handshake_attempts", Type: cty.Number, Required: false},
 | 
			
		||||
		"ssh_bastion_host":             &hcldec.AttrSpec{Name: "ssh_bastion_host", Type: cty.String, Required: false},
 | 
			
		||||
		"ssh_bastion_port":             &hcldec.AttrSpec{Name: "ssh_bastion_port", Type: cty.Number, Required: false},
 | 
			
		||||
		"ssh_bastion_agent_auth":       &hcldec.AttrSpec{Name: "ssh_bastion_agent_auth", Type: cty.Bool, Required: false},
 | 
			
		||||
		"ssh_bastion_username":         &hcldec.AttrSpec{Name: "ssh_bastion_username", Type: cty.String, Required: false},
 | 
			
		||||
		"ssh_bastion_password":         &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false},
 | 
			
		||||
		"ssh_bastion_interactive":      &hcldec.AttrSpec{Name: "ssh_bastion_interactive", Type: cty.Bool, Required: false},
 | 
			
		||||
		"ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false},
 | 
			
		||||
		"ssh_bastion_certificate_file": &hcldec.AttrSpec{Name: "ssh_bastion_certificate_file", Type: cty.String, Required: false},
 | 
			
		||||
		"ssh_file_transfer_method":     &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false},
 | 
			
		||||
		"ssh_proxy_host":               &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false},
 | 
			
		||||
		"ssh_proxy_port":               &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false},
 | 
			
		||||
		"ssh_proxy_username":           &hcldec.AttrSpec{Name: "ssh_proxy_username", Type: cty.String, Required: false},
 | 
			
		||||
		"ssh_proxy_password":           &hcldec.AttrSpec{Name: "ssh_proxy_password", Type: cty.String, Required: false},
 | 
			
		||||
		"ssh_keep_alive_interval":      &hcldec.AttrSpec{Name: "ssh_keep_alive_interval", Type: cty.String, Required: false},
 | 
			
		||||
		"ssh_read_write_timeout":       &hcldec.AttrSpec{Name: "ssh_read_write_timeout", Type: cty.String, Required: false},
 | 
			
		||||
		"ssh_remote_tunnels":           &hcldec.AttrSpec{Name: "ssh_remote_tunnels", Type: cty.List(cty.String), Required: false},
 | 
			
		||||
		"ssh_local_tunnels":            &hcldec.AttrSpec{Name: "ssh_local_tunnels", Type: cty.List(cty.String), Required: false},
 | 
			
		||||
		"ssh_public_key":               &hcldec.AttrSpec{Name: "ssh_public_key", Type: cty.List(cty.Number), Required: false},
 | 
			
		||||
		"ssh_private_key":              &hcldec.AttrSpec{Name: "ssh_private_key", Type: cty.List(cty.Number), Required: false},
 | 
			
		||||
		"winrm_username":               &hcldec.AttrSpec{Name: "winrm_username", Type: cty.String, Required: false},
 | 
			
		||||
		"winrm_password":               &hcldec.AttrSpec{Name: "winrm_password", Type: cty.String, Required: false},
 | 
			
		||||
		"winrm_host":                   &hcldec.AttrSpec{Name: "winrm_host", Type: cty.String, Required: false},
 | 
			
		||||
		"winrm_no_proxy":               &hcldec.AttrSpec{Name: "winrm_no_proxy", Type: cty.Bool, Required: false},
 | 
			
		||||
		"winrm_port":                   &hcldec.AttrSpec{Name: "winrm_port", Type: cty.Number, Required: false},
 | 
			
		||||
		"winrm_timeout":                &hcldec.AttrSpec{Name: "winrm_timeout", Type: cty.String, Required: false},
 | 
			
		||||
		"winrm_use_ssl":                &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false},
 | 
			
		||||
		"winrm_insecure":               &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false},
 | 
			
		||||
		"winrm_use_ntlm":               &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false},
 | 
			
		||||
		"ssh_host_port_min":            &hcldec.AttrSpec{Name: "ssh_host_port_min", Type: cty.Number, Required: false},
 | 
			
		||||
		"ssh_host_port_max":            &hcldec.AttrSpec{Name: "ssh_host_port_max", Type: cty.Number, Required: false},
 | 
			
		||||
		"ssh_skip_nat_mapping":         &hcldec.AttrSpec{Name: "ssh_skip_nat_mapping", Type: cty.Bool, Required: false},
 | 
			
		||||
		"output_directory":             &hcldec.AttrSpec{Name: "output_directory", Type: cty.String, Required: false},
 | 
			
		||||
		"format":                       &hcldec.AttrSpec{Name: "format", Type: cty.String, Required: false},
 | 
			
		||||
		"keep_vm":                      &hcldec.AttrSpec{Name: "keep_vm", Type: cty.String, Required: false},
 | 
			
		||||
		"ip_getter":                    &hcldec.AttrSpec{Name: "ip_getter", Type: cty.String, Required: false},
 | 
			
		||||
		"vcpus_max":                    &hcldec.AttrSpec{Name: "vcpus_max", Type: cty.Number, Required: false},
 | 
			
		||||
		"vcpus_atstartup":              &hcldec.AttrSpec{Name: "vcpus_atstartup", Type: cty.Number, Required: false},
 | 
			
		||||
		"vm_memory":                    &hcldec.AttrSpec{Name: "vm_memory", Type: cty.Number, Required: false},
 | 
			
		||||
		"disk_size":                    &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
 | 
			
		||||
		"clone_template":               &hcldec.AttrSpec{Name: "clone_template", Type: cty.String, Required: false},
 | 
			
		||||
		"vm_other_config":              &hcldec.AttrSpec{Name: "vm_other_config", Type: cty.Map(cty.String), Required: false},
 | 
			
		||||
		"iso_checksum":                 &hcldec.AttrSpec{Name: "iso_checksum", Type: cty.String, Required: false},
 | 
			
		||||
		"iso_checksum_type":            &hcldec.AttrSpec{Name: "iso_checksum_type", Type: cty.String, Required: false},
 | 
			
		||||
		"iso_urls":                     &hcldec.AttrSpec{Name: "iso_urls", Type: cty.List(cty.String), Required: false},
 | 
			
		||||
		"iso_url":                      &hcldec.AttrSpec{Name: "iso_url", Type: cty.String, Required: false},
 | 
			
		||||
		"iso_name":                     &hcldec.AttrSpec{Name: "iso_name", Type: cty.String, Required: false},
 | 
			
		||||
		"platform_args":                &hcldec.AttrSpec{Name: "platform_args", Type: cty.Map(cty.String), Required: false},
 | 
			
		||||
		"install_timeout":              &hcldec.AttrSpec{Name: "install_timeout", Type: cty.String, Required: false},
 | 
			
		||||
		"source_path":                  &hcldec.AttrSpec{Name: "source_path", Type: cty.String, Required: false},
 | 
			
		||||
		"firmware":                     &hcldec.AttrSpec{Name: "firmware", Type: cty.String, Required: false},
 | 
			
		||||
	}
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								builder/xenserver/common/find_port.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								builder/xenserver/common/find_port.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// FindPort finds and starts listening on a port in the range [portMin, portMax]
 | 
			
		||||
// returns the listener and the port number on success, or nil, 0 on failure
 | 
			
		||||
func FindPort(portMin uint, portMax uint) (net.Listener, uint) {
 | 
			
		||||
	log.Printf("Looking for an available port between %d and %d", portMin, portMax)
 | 
			
		||||
 | 
			
		||||
	for port := portMin; port <= portMax; port++ {
 | 
			
		||||
		log.Printf("Trying port: %d", port)
 | 
			
		||||
		l, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			return l, port
 | 
			
		||||
		} else {
 | 
			
		||||
			log.Printf("Port %d unavailable: %s", port, err.Error())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, 0
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										127
									
								
								builder/xenserver/common/http_upload.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								builder/xenserver/common/http_upload.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,127 @@
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"os"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/multistep"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/packer"
 | 
			
		||||
	xsclient "github.com/terra-farm/go-xen-api-client"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func appendQuery(urlstring, k, v string) (string, error) {
 | 
			
		||||
	u, err := url.Parse(urlstring)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", fmt.Errorf("Unable to parse URL '%s': %s", urlstring, err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	m := u.Query()
 | 
			
		||||
	m.Add(k, v)
 | 
			
		||||
	u.RawQuery = m.Encode()
 | 
			
		||||
	return u.String(), err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func HTTPUpload(import_url string, fh *os.File, state multistep.StateBag) (result string, err error) {
 | 
			
		||||
	ui := state.Get("ui").(packer.Ui)
 | 
			
		||||
	c := state.Get("client").(*Connection)
 | 
			
		||||
 | 
			
		||||
	task, err := c.client.Task.Create(c.session, "packer-task", "Packer task")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		err = fmt.Errorf("Unable to create task: %s", err.Error())
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer c.client.Task.Destroy(c.session, task)
 | 
			
		||||
 | 
			
		||||
	import_task_url, err := appendQuery(import_url, "task_id", string(task))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get file length
 | 
			
		||||
	fstat, err := fh.Stat()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		err = fmt.Errorf("Unable to stat '%s': %s", fh.Name(), err.Error())
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fileLength := fstat.Size()
 | 
			
		||||
 | 
			
		||||
	// Define a new transport which allows self-signed certs
 | 
			
		||||
	tr := &http.Transport{
 | 
			
		||||
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Create a client
 | 
			
		||||
	httpClient := &http.Client{Transport: tr}
 | 
			
		||||
 | 
			
		||||
	// Create request and download file
 | 
			
		||||
	request, err := http.NewRequest("PUT", import_task_url, fh)
 | 
			
		||||
	request.ContentLength = fileLength
 | 
			
		||||
 | 
			
		||||
	ui.Say(fmt.Sprintf("PUT '%s'", import_task_url))
 | 
			
		||||
 | 
			
		||||
	resp, err := httpClient.Do(request) // Do closes fh for us, according to docs
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if resp.StatusCode != 200 {
 | 
			
		||||
		err = fmt.Errorf("PUT request got non-200 status code: %s", resp.Status)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logIteration := 0
 | 
			
		||||
	err = InterruptibleWait{
 | 
			
		||||
		Predicate: func() (bool, error) {
 | 
			
		||||
			status, err := c.client.Task.GetStatus(c.session, task)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return false, fmt.Errorf("Failed to get task status: %s", err.Error())
 | 
			
		||||
			}
 | 
			
		||||
			switch status {
 | 
			
		||||
			case xsclient.TaskStatusTypePending:
 | 
			
		||||
				progress, err := c.client.Task.GetProgress(c.session, task)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return false, fmt.Errorf("Failed to get progress: %s", err.Error())
 | 
			
		||||
				}
 | 
			
		||||
				logIteration = logIteration + 1
 | 
			
		||||
				if logIteration%5 == 0 {
 | 
			
		||||
					log.Printf("Upload %.0f%% complete", progress*100)
 | 
			
		||||
				}
 | 
			
		||||
				return false, nil
 | 
			
		||||
			case xsclient.TaskStatusTypeSuccess:
 | 
			
		||||
				return true, nil
 | 
			
		||||
			case xsclient.TaskStatusTypeFailure:
 | 
			
		||||
				errorInfo, err := c.client.Task.GetErrorInfo(c.session, task)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					errorInfo = []string{fmt.Sprintf("furthermore, failed to get error info: %s", err.Error())}
 | 
			
		||||
				}
 | 
			
		||||
				return false, fmt.Errorf("Task failed: %s", errorInfo)
 | 
			
		||||
			case xsclient.TaskStatusTypeCancelling, xsclient.TaskStatusTypeCancelled:
 | 
			
		||||
				return false, fmt.Errorf("Task cancelled")
 | 
			
		||||
			default:
 | 
			
		||||
				return false, fmt.Errorf("Unknown task status %v", status)
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		PredicateInterval: 1 * time.Second,
 | 
			
		||||
		Timeout:           24 * time.Hour,
 | 
			
		||||
	}.Wait(state)
 | 
			
		||||
 | 
			
		||||
	resp.Body.Close()
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		err = fmt.Errorf("Error uploading: %s", err.Error())
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result, err = c.client.Task.GetResult(c.session, task)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		err = fmt.Errorf("Error getting result: %s", err.Error())
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Printf("Upload complete")
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										86
									
								
								builder/xenserver/common/interruptible_wait.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								builder/xenserver/common/interruptible_wait.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/multistep"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type InterruptibleWait struct {
 | 
			
		||||
	Timeout time.Duration
 | 
			
		||||
 | 
			
		||||
	// optional:
 | 
			
		||||
	Predicate         func() (result bool, err error)
 | 
			
		||||
	PredicateInterval time.Duration
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type TimeoutError struct{}
 | 
			
		||||
 | 
			
		||||
func (err TimeoutError) Error() string {
 | 
			
		||||
	return "Timed out"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type InterruptedError struct{}
 | 
			
		||||
 | 
			
		||||
func (err InterruptedError) Error() string {
 | 
			
		||||
	return "Interrupted"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type PredicateResult struct {
 | 
			
		||||
	complete bool
 | 
			
		||||
	err      error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Wait waits for up to Timeout duration, checking an optional Predicate every PredicateInterval duration.
 | 
			
		||||
   The first run of Predicate is immediately after Wait is called.
 | 
			
		||||
   If the command is interrupted by the user, then an InterruptedError is returned.
 | 
			
		||||
   If Predicate is not nil, a timeout leads to TimeoutError being returned, and a successful Predicate run leads to nil being returned.
 | 
			
		||||
   If Predicate is nil, a timeout is not an error, and nil is returned.
 | 
			
		||||
*/
 | 
			
		||||
func (wait InterruptibleWait) Wait(state multistep.StateBag) error {
 | 
			
		||||
	predicateResult := make(chan PredicateResult, 1)
 | 
			
		||||
	stopWaiting := make(chan struct{})
 | 
			
		||||
	defer close(stopWaiting)
 | 
			
		||||
 | 
			
		||||
	if wait.Predicate != nil {
 | 
			
		||||
		go func() {
 | 
			
		||||
			for {
 | 
			
		||||
				if complete, err := wait.Predicate(); err != nil || complete {
 | 
			
		||||
					predicateResult <- PredicateResult{complete, err}
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				select {
 | 
			
		||||
				case <-time.After(wait.PredicateInterval):
 | 
			
		||||
					continue
 | 
			
		||||
				case <-stopWaiting:
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	timeout := time.After(wait.Timeout)
 | 
			
		||||
	for {
 | 
			
		||||
		// wait for either install to complete/error,
 | 
			
		||||
		// an interrupt to come through, or a timeout to occur
 | 
			
		||||
 | 
			
		||||
		if _, ok := state.GetOk(multistep.StateCancelled); ok {
 | 
			
		||||
			return InterruptedError{}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		select {
 | 
			
		||||
		case result := <-predicateResult:
 | 
			
		||||
			return result.err
 | 
			
		||||
 | 
			
		||||
		case <-time.After(1 * time.Second):
 | 
			
		||||
			continue
 | 
			
		||||
 | 
			
		||||
		case <-timeout:
 | 
			
		||||
			if wait.Predicate != nil {
 | 
			
		||||
				return TimeoutError{}
 | 
			
		||||
			} else {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										217
									
								
								builder/xenserver/common/ssh.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								builder/xenserver/common/ssh.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,217 @@
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/pem"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/multistep"
 | 
			
		||||
	gossh "golang.org/x/crypto/ssh"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func SSHAddress(state multistep.StateBag) (string, error) {
 | 
			
		||||
	sshIP := state.Get("ssh_address").(string)
 | 
			
		||||
	sshHostPort := 22
 | 
			
		||||
	return fmt.Sprintf("%s:%d", sshIP, sshHostPort), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func SSHLocalAddress(state multistep.StateBag) (string, error) {
 | 
			
		||||
	sshLocalPort, ok := state.Get("local_ssh_port").(uint)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return "", fmt.Errorf("SSH port forwarding hasn't been set up yet")
 | 
			
		||||
	}
 | 
			
		||||
	conn_str := fmt.Sprintf("%s:%d", "127.0.0.1", sshLocalPort)
 | 
			
		||||
	return conn_str, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func SSHPort(state multistep.StateBag) (int, error) {
 | 
			
		||||
	sshHostPort := state.Get("local_ssh_port").(uint)
 | 
			
		||||
	return int(sshHostPort), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func CommHost(state multistep.StateBag) (string, error) {
 | 
			
		||||
	return "127.0.0.1", nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func SSHConfigFunc(config SSHConfig) func(multistep.StateBag) (*gossh.ClientConfig, error) {
 | 
			
		||||
	return func(state multistep.StateBag) (*gossh.ClientConfig, error) {
 | 
			
		||||
		config := state.Get("commonconfig").(CommonConfig)
 | 
			
		||||
		auth := []gossh.AuthMethod{
 | 
			
		||||
			gossh.Password(config.SSHPassword),
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if config.SSHKeyPath != "" {
 | 
			
		||||
			signer, err := FileSigner(config.SSHKeyPath)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			auth = append(auth, gossh.PublicKeys(signer))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return &gossh.ClientConfig{
 | 
			
		||||
			User:            config.SSHUser,
 | 
			
		||||
			Auth:            auth,
 | 
			
		||||
			HostKeyCallback: gossh.InsecureIgnoreHostKey(),
 | 
			
		||||
		}, nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func doExecuteSSHCmd(cmd, target string, config *gossh.ClientConfig) (stdout string, err error) {
 | 
			
		||||
	client, err := gossh.Dial("tcp", target, config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//Create session
 | 
			
		||||
	session, err := client.NewSession()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer session.Close()
 | 
			
		||||
 | 
			
		||||
	var b bytes.Buffer
 | 
			
		||||
	session.Stdout = &b
 | 
			
		||||
	if err := session.Run(cmd); err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return strings.Trim(b.String(), "\n"), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ExecuteHostSSHCmd(state multistep.StateBag, cmd string) (stdout string, err error) {
 | 
			
		||||
	config := state.Get("commonconfig").(CommonConfig)
 | 
			
		||||
	sshAddress, _ := SSHAddress(state)
 | 
			
		||||
	// Setup connection config
 | 
			
		||||
	sshConfig := &gossh.ClientConfig{
 | 
			
		||||
		User: config.Username,
 | 
			
		||||
		Auth: []gossh.AuthMethod{
 | 
			
		||||
			gossh.Password(config.Password),
 | 
			
		||||
		},
 | 
			
		||||
		HostKeyCallback: gossh.InsecureIgnoreHostKey(),
 | 
			
		||||
	}
 | 
			
		||||
	return doExecuteSSHCmd(cmd, sshAddress, sshConfig)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ExecuteGuestSSHCmd(state multistep.StateBag, cmd string) (stdout string, err error) {
 | 
			
		||||
	config := state.Get("commonconfig").(CommonConfig)
 | 
			
		||||
	localAddress, err := SSHLocalAddress(state)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	sshConfig, err := SSHConfigFunc(config.SSHConfig)(state)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return doExecuteSSHCmd(cmd, localAddress, sshConfig)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func forward(local_conn net.Conn, config *gossh.ClientConfig, server, remote_dest string, remote_port uint) error {
 | 
			
		||||
	defer local_conn.Close()
 | 
			
		||||
 | 
			
		||||
	ssh_client_conn, err := gossh.Dial("tcp", server+":22", config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Printf("local ssh.Dial error: %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer ssh_client_conn.Close()
 | 
			
		||||
 | 
			
		||||
	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
 | 
			
		||||
	}
 | 
			
		||||
	defer ssh_conn.Close()
 | 
			
		||||
 | 
			
		||||
	txDone := make(chan struct{})
 | 
			
		||||
	rxDone := make(chan struct{})
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		_, err = io.Copy(ssh_conn, local_conn)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Printf("io.copy failed: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		close(txDone)
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		_, err = io.Copy(local_conn, ssh_conn)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Printf("io.copy failed: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		close(rxDone)
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	<-txDone
 | 
			
		||||
	<-rxDone
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ssh_port_forward(local_listener net.Listener, remote_port int, remote_dest, host, username, password string) error {
 | 
			
		||||
 | 
			
		||||
	config := &gossh.ClientConfig{
 | 
			
		||||
		User: username,
 | 
			
		||||
		Auth: []gossh.AuthMethod{
 | 
			
		||||
			gossh.Password(password),
 | 
			
		||||
		},
 | 
			
		||||
		HostKeyCallback: gossh.InsecureIgnoreHostKey(),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		local_connection, err := local_listener.Accept()
 | 
			
		||||
 | 
			
		||||
		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, uint(remote_port))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FileSigner returns an gossh.Signer for a key file.
 | 
			
		||||
func FileSigner(path string) (gossh.Signer, error) {
 | 
			
		||||
	f, err := os.Open(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	defer f.Close()
 | 
			
		||||
 | 
			
		||||
	keyBytes, err := ioutil.ReadAll(f)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We parse the private key on our own first so that we can
 | 
			
		||||
	// show a nicer error if the private key has a password.
 | 
			
		||||
	block, _ := pem.Decode(keyBytes)
 | 
			
		||||
	if block == nil {
 | 
			
		||||
		return nil, fmt.Errorf(
 | 
			
		||||
			"Failed to read key '%s': no key found", path)
 | 
			
		||||
	}
 | 
			
		||||
	if block.Headers["Proc-Type"] == "4,ENCRYPTED" {
 | 
			
		||||
		return nil, fmt.Errorf(
 | 
			
		||||
			"Failed to read key '%s': password protected keys are\n"+
 | 
			
		||||
				"not supported. Please decrypt the key prior to use.", path)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	signer, err := gossh.ParsePrivateKey(keyBytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("Error setting up SSH config: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return signer, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										48
									
								
								builder/xenserver/common/ssh_config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								builder/xenserver/common/ssh_config.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/communicator"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type SSHConfig struct {
 | 
			
		||||
	Comm communicator.Config `mapstructure:",squash"`
 | 
			
		||||
 | 
			
		||||
	SSHHostPortMin    uint `mapstructure:"ssh_host_port_min"`
 | 
			
		||||
	SSHHostPortMax    uint `mapstructure:"ssh_host_port_max"`
 | 
			
		||||
	SSHSkipNatMapping bool `mapstructure:"ssh_skip_nat_mapping"`
 | 
			
		||||
 | 
			
		||||
	// These are deprecated, but we keep them around for BC
 | 
			
		||||
	// TODO(@mitchellh): remove
 | 
			
		||||
	SSHKeyPath     string        `mapstructure:"ssh_key_path"`
 | 
			
		||||
	SSHWaitTimeout time.Duration `mapstructure:"ssh_wait_timeout"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *SSHConfig) Prepare(ctx *interpolate.Context) []error {
 | 
			
		||||
	if c.SSHHostPortMin == 0 {
 | 
			
		||||
		c.SSHHostPortMin = 2222
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.SSHHostPortMax == 0 {
 | 
			
		||||
		c.SSHHostPortMax = 4444
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// TODO: backwards compatibility, write fixer instead
 | 
			
		||||
	if c.SSHKeyPath != "" {
 | 
			
		||||
		c.Comm.SSHPrivateKeyFile = c.SSHKeyPath
 | 
			
		||||
	}
 | 
			
		||||
	if c.SSHWaitTimeout != 0 {
 | 
			
		||||
		c.Comm.SSHTimeout = c.SSHWaitTimeout
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	errs := c.Comm.Prepare(ctx)
 | 
			
		||||
	if c.SSHHostPortMin > c.SSHHostPortMax {
 | 
			
		||||
		errs = append(errs,
 | 
			
		||||
			errors.New("ssh_host_port_min must be less than ssh_host_port_max"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return errs
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										84
									
								
								builder/xenserver/common/step_attach_vdi.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								builder/xenserver/common/step_attach_vdi.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,84 @@
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/multistep"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/packer"
 | 
			
		||||
	xsclient "github.com/terra-farm/go-xen-api-client"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type StepAttachVdi struct {
 | 
			
		||||
	VdiUuidKey string
 | 
			
		||||
	VdiType    xsclient.VbdType
 | 
			
		||||
 | 
			
		||||
	vdi xsclient.VDIRef
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *StepAttachVdi) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
 | 
			
		||||
	ui := state.Get("ui").(packer.Ui)
 | 
			
		||||
	c := state.Get("client").(*Connection)
 | 
			
		||||
 | 
			
		||||
	log.Printf("Running attach vdi for key %s\n", self.VdiUuidKey)
 | 
			
		||||
	var vdiUuid string
 | 
			
		||||
	if vdiUuidRaw, ok := state.GetOk(self.VdiUuidKey); ok {
 | 
			
		||||
		vdiUuid = vdiUuidRaw.(string)
 | 
			
		||||
	} else {
 | 
			
		||||
		log.Printf("Skipping attach of '%s'", self.VdiUuidKey)
 | 
			
		||||
		return multistep.ActionContinue
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	self.vdi, err = c.client.VDI.GetByUUID(c.session, vdiUuid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to get VDI from UUID '%s': %s", vdiUuid, err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	uuid := state.Get("instance_uuid").(string)
 | 
			
		||||
	instance, err := c.client.VM.GetByUUID(c.session, uuid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to get VM from UUID '%s': %s", uuid, err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = ConnectVdi(c, instance, self.vdi, self.VdiType)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Error attaching VDI '%s': '%s'", vdiUuid, err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Printf("Attached VDI '%s'", vdiUuid)
 | 
			
		||||
 | 
			
		||||
	return multistep.ActionContinue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *StepAttachVdi) Cleanup(state multistep.StateBag) {
 | 
			
		||||
	config := state.Get("commonconfig").(CommonConfig)
 | 
			
		||||
	c := state.Get("client").(*Connection)
 | 
			
		||||
	if config.ShouldKeepVM(state) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if self.vdi == "" {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	uuid := state.Get("instance_uuid").(string)
 | 
			
		||||
	vmRef, err := c.client.VM.GetByUUID(c.session, uuid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Printf("Unable to get VM from UUID '%s': %s", uuid, err.Error())
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vdiUuid := state.Get(self.VdiUuidKey).(string)
 | 
			
		||||
 | 
			
		||||
	err = DisconnectVdi(c, vmRef, self.vdi)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Printf("Unable to disconnect VDI '%s': %s", vdiUuid, err.Error())
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	log.Printf("Detached VDI '%s'", vdiUuid)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								builder/xenserver/common/step_boot_wait.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								builder/xenserver/common/step_boot_wait.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/multistep"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/packer"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type StepBootWait struct{}
 | 
			
		||||
 | 
			
		||||
func (self *StepBootWait) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
 | 
			
		||||
	c := state.Get("client").(*Connection)
 | 
			
		||||
	config := state.Get("commonconfig").(CommonConfig)
 | 
			
		||||
	ui := state.Get("ui").(packer.Ui)
 | 
			
		||||
 | 
			
		||||
	instance, _ := c.client.VM.GetByUUID(c.session, state.Get("instance_uuid").(string))
 | 
			
		||||
	ui.Say("Unpausing VM " + state.Get("instance_uuid").(string))
 | 
			
		||||
	Unpause(c, instance)
 | 
			
		||||
 | 
			
		||||
	if int64(config.BootWait) > 0 {
 | 
			
		||||
		ui.Say(fmt.Sprintf("Waiting %s for boot...", config.BootWait))
 | 
			
		||||
		err := InterruptibleWait{Timeout: config.BootWait}.Wait(state)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			ui.Error(err.Error())
 | 
			
		||||
			return multistep.ActionHalt
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return multistep.ActionContinue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *StepBootWait) Cleanup(state multistep.StateBag) {}
 | 
			
		||||
							
								
								
									
										53
									
								
								builder/xenserver/common/step_detach_vdi.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								builder/xenserver/common/step_detach_vdi.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/multistep"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/packer"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type StepDetachVdi struct {
 | 
			
		||||
	VdiUuidKey string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *StepDetachVdi) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
 | 
			
		||||
	ui := state.Get("ui").(packer.Ui)
 | 
			
		||||
	c := state.Get("client").(*Connection)
 | 
			
		||||
 | 
			
		||||
	var vdiUuid string
 | 
			
		||||
	if vdiUuidRaw, ok := state.GetOk(self.VdiUuidKey); ok {
 | 
			
		||||
		vdiUuid = vdiUuidRaw.(string)
 | 
			
		||||
	} else {
 | 
			
		||||
		log.Printf("Skipping detach of '%s'", self.VdiUuidKey)
 | 
			
		||||
		return multistep.ActionContinue
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vdi, err := c.client.VDI.GetByUUID(c.session, vdiUuid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to get VDI from UUID '%s': %s", vdiUuid, err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	uuid := state.Get("instance_uuid").(string)
 | 
			
		||||
	instance, err := c.client.VM.GetByUUID(c.session, uuid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to get VM from UUID '%s': %s", uuid, err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = DisconnectVdi(c, instance, vdi)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to detach VDI '%s': %s", vdiUuid, err.Error()))
 | 
			
		||||
		//return multistep.ActionHalt
 | 
			
		||||
		return multistep.ActionContinue
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Printf("Detached VDI '%s'", vdiUuid)
 | 
			
		||||
 | 
			
		||||
	return multistep.ActionContinue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *StepDetachVdi) Cleanup(state multistep.StateBag) {}
 | 
			
		||||
							
								
								
									
										278
									
								
								builder/xenserver/common/step_export.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										278
									
								
								builder/xenserver/common/step_export.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,278 @@
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/multistep"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/packer"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type StepExport struct{}
 | 
			
		||||
 | 
			
		||||
func downloadFile(url, filename string, ui packer.Ui) (err error) {
 | 
			
		||||
 | 
			
		||||
	// Create the file
 | 
			
		||||
	fh, err := os.Create(filename)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer fh.Close()
 | 
			
		||||
 | 
			
		||||
	// 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 request and download file
 | 
			
		||||
 | 
			
		||||
	resp, err := client.Get(url)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
 | 
			
		||||
	var progress uint
 | 
			
		||||
	var total uint
 | 
			
		||||
	var percentage uint
 | 
			
		||||
	var marker_len uint
 | 
			
		||||
 | 
			
		||||
	progress = uint(0)
 | 
			
		||||
	total = uint(resp.ContentLength)
 | 
			
		||||
	percentage = uint(0)
 | 
			
		||||
	marker_len = uint(5)
 | 
			
		||||
 | 
			
		||||
	var buffer [4096]byte
 | 
			
		||||
	for {
 | 
			
		||||
		n, err := resp.Body.Read(buffer[:])
 | 
			
		||||
		if err != nil && err != io.EOF {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		progress += uint(n)
 | 
			
		||||
 | 
			
		||||
		if _, write_err := fh.Write(buffer[:n]); write_err != nil {
 | 
			
		||||
			return write_err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err == io.EOF {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Increment percentage in multiples of marker_len
 | 
			
		||||
		cur_percentage := ((progress * 100 / total) / marker_len) * marker_len
 | 
			
		||||
		if cur_percentage > percentage {
 | 
			
		||||
			percentage = cur_percentage
 | 
			
		||||
			ui.Message(fmt.Sprintf("Downloading... %d%%", percentage))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (StepExport) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
 | 
			
		||||
	config := state.Get("commonconfig").(CommonConfig)
 | 
			
		||||
	ui := state.Get("ui").(packer.Ui)
 | 
			
		||||
	c := state.Get("client").(*Connection)
 | 
			
		||||
	instance_uuid := state.Get("instance_uuid").(string)
 | 
			
		||||
	suffix := ".vhd"
 | 
			
		||||
	extrauri := "&format=vhd"
 | 
			
		||||
 | 
			
		||||
	instance, err := c.client.VM.GetByUUID(c.session, instance_uuid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Could not get VM with UUID '%s': %s", instance_uuid, err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(config.ExportNetworkNames) > 0 {
 | 
			
		||||
		vifs, err := c.client.VM.GetVIFs(c.session, instance)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			ui.Error(fmt.Sprintf("Error occured getting VIFs: %s", err.Error()))
 | 
			
		||||
			return multistep.ActionHalt
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, vif := range vifs {
 | 
			
		||||
			err := c.client.VIF.Destroy(c.session, vif)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				ui.Error(fmt.Sprintf("Destroy vif fail: '%s': %s", vif, err.Error()))
 | 
			
		||||
				return multistep.ActionHalt
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		for i, networkNameLabel := range config.ExportNetworkNames {
 | 
			
		||||
			networks, err := c.client.Network.GetByNameLabel(c.session, networkNameLabel)
 | 
			
		||||
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				ui.Error(fmt.Sprintf("Error occured getting Network by name-label: %s", err.Error()))
 | 
			
		||||
				return multistep.ActionHalt
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			switch {
 | 
			
		||||
			case len(networks) == 0:
 | 
			
		||||
				ui.Error(fmt.Sprintf("Couldn't find a network with the specified name-label '%s'. Aborting.", networkNameLabel))
 | 
			
		||||
				return multistep.ActionHalt
 | 
			
		||||
			case len(networks) > 1:
 | 
			
		||||
				ui.Error(fmt.Sprintf("Found more than one network with the name '%s'. The name must be unique. Aborting.", networkNameLabel))
 | 
			
		||||
				return multistep.ActionHalt
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			//we need the VIF index string
 | 
			
		||||
			vifIndexString := fmt.Sprintf("%d", i)
 | 
			
		||||
			_, err = ConnectNetwork(c, networks[0], instance, vifIndexString)
 | 
			
		||||
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				ui.Say(err.Error())
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ui.Say("Step: export artifact")
 | 
			
		||||
 | 
			
		||||
	compress_option_xe := "compress=false"
 | 
			
		||||
	compress_option_url := ""
 | 
			
		||||
 | 
			
		||||
	switch config.Format {
 | 
			
		||||
	case "none":
 | 
			
		||||
		ui.Say("Skipping export")
 | 
			
		||||
		return multistep.ActionContinue
 | 
			
		||||
 | 
			
		||||
	case "xva_compressed":
 | 
			
		||||
		compress_option_xe = "compress=true"
 | 
			
		||||
		compress_option_url = "use_compression=true&"
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case "xva":
 | 
			
		||||
		// export the VM
 | 
			
		||||
 | 
			
		||||
		export_filename := fmt.Sprintf("%s/%s.xva", config.OutputDir, config.VMName)
 | 
			
		||||
 | 
			
		||||
		use_xe := os.Getenv("USE_XE") == "1"
 | 
			
		||||
		if xe, e := exec.LookPath("xe"); e == nil && use_xe {
 | 
			
		||||
			cmd := exec.Command(
 | 
			
		||||
				xe,
 | 
			
		||||
				"-s", c.Host,
 | 
			
		||||
				"-p", "443",
 | 
			
		||||
				"-u", c.Username,
 | 
			
		||||
				"-pw", c.Password,
 | 
			
		||||
				"vm-export",
 | 
			
		||||
				"vm="+instance_uuid,
 | 
			
		||||
				compress_option_xe,
 | 
			
		||||
				"filename="+export_filename,
 | 
			
		||||
			)
 | 
			
		||||
 | 
			
		||||
			ui.Say(fmt.Sprintf("Getting XVA %+v %+v", cmd.Path, cmd.Args))
 | 
			
		||||
 | 
			
		||||
			err = cmd.Run()
 | 
			
		||||
		} else {
 | 
			
		||||
			export_url := fmt.Sprintf("https://%s/export?%suuid=%s&session_id=%s",
 | 
			
		||||
				c.Host,
 | 
			
		||||
				compress_option_url,
 | 
			
		||||
				instance_uuid,
 | 
			
		||||
				c.GetSession(),
 | 
			
		||||
			)
 | 
			
		||||
 | 
			
		||||
			ui.Say("Getting XVA " + export_url)
 | 
			
		||||
			err = downloadFile(export_url, export_filename, ui)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			ui.Error(fmt.Sprintf("Could not download XVA: %s", err.Error()))
 | 
			
		||||
			return multistep.ActionHalt
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case "vdi_raw":
 | 
			
		||||
		suffix = ".raw"
 | 
			
		||||
		extrauri = ""
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case "vdi_vhd":
 | 
			
		||||
		// export the disks
 | 
			
		||||
 | 
			
		||||
		disks, err := GetDisks(c, instance)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			ui.Error(fmt.Sprintf("Could not get VM disks: %s", err.Error()))
 | 
			
		||||
			return multistep.ActionHalt
 | 
			
		||||
		}
 | 
			
		||||
		for _, disk := range disks {
 | 
			
		||||
			disk_uuid, err := c.client.VDI.GetUUID(c.session, disk)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				ui.Error(fmt.Sprintf("Could not get disk with UUID '%s': %s", disk_uuid, err.Error()))
 | 
			
		||||
				return multistep.ActionHalt
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Work out XenServer version
 | 
			
		||||
			hosts, err := c.client.Host.GetAll(c.session)
 | 
			
		||||
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				ui.Error(fmt.Sprintf("Could not retrieve hosts in the pool: %s", err.Error()))
 | 
			
		||||
				return multistep.ActionHalt
 | 
			
		||||
			}
 | 
			
		||||
			host := hosts[0]
 | 
			
		||||
			host_software_versions, err := c.client.Host.GetSoftwareVersion(c.session, host)
 | 
			
		||||
			xs_version := host_software_versions["product_version"]
 | 
			
		||||
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				ui.Error(fmt.Sprintf("Could not get the software version: %s", err.Error()))
 | 
			
		||||
				return multistep.ActionHalt
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var disk_export_url string
 | 
			
		||||
 | 
			
		||||
			// @todo: check for 6.5 SP1
 | 
			
		||||
			if xs_version <= "6.5.0" && config.Format == "vdi_vhd" {
 | 
			
		||||
				// Export the VHD using a Transfer VM
 | 
			
		||||
 | 
			
		||||
				disk_export_url, err = Expose(c, disk, "vhd")
 | 
			
		||||
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					ui.Error(fmt.Sprintf("Failed to expose disk %s: %s", disk_uuid, err.Error()))
 | 
			
		||||
					return multistep.ActionHalt
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			} else {
 | 
			
		||||
 | 
			
		||||
				// Use the preferred direct export from XAPI
 | 
			
		||||
				// 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%s",
 | 
			
		||||
					c.Username,
 | 
			
		||||
					c.Password,
 | 
			
		||||
					c.Host,
 | 
			
		||||
					disk_uuid,
 | 
			
		||||
					extrauri)
 | 
			
		||||
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			disk_export_filename := fmt.Sprintf("%s/%s%s", config.OutputDir, disk_uuid, suffix)
 | 
			
		||||
 | 
			
		||||
			ui.Say("Getting VDI " + disk_export_url)
 | 
			
		||||
			err = downloadFile(disk_export_url, disk_export_filename, ui)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				ui.Error(fmt.Sprintf("Could not download VDI: %s", err.Error()))
 | 
			
		||||
				return multistep.ActionHalt
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Call unexpose in case a TVM was used. The call is harmless
 | 
			
		||||
			// if that is not the case.
 | 
			
		||||
			Unexpose(c, disk)
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		panic(fmt.Sprintf("Unknown export format '%s'", config.Format))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ui.Say("Download completed: " + config.OutputDir)
 | 
			
		||||
 | 
			
		||||
	return multistep.ActionContinue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (StepExport) Cleanup(state multistep.StateBag) {}
 | 
			
		||||
							
								
								
									
										49
									
								
								builder/xenserver/common/step_find_vdi.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								builder/xenserver/common/step_find_vdi.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/multistep"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/packer"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type StepFindVdi struct {
 | 
			
		||||
	VdiName       string
 | 
			
		||||
	ImagePathFunc func() string
 | 
			
		||||
	VdiUuidKey    string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *StepFindVdi) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
 | 
			
		||||
	ui := state.Get("ui").(packer.Ui)
 | 
			
		||||
	c := state.Get("client").(*Connection)
 | 
			
		||||
 | 
			
		||||
	// Ignore if VdiName is not specified
 | 
			
		||||
	if self.VdiName == "" {
 | 
			
		||||
		return multistep.ActionContinue
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vdis, err := c.client.VDI.GetByNameLabel(c.session, self.VdiName)
 | 
			
		||||
 | 
			
		||||
	switch {
 | 
			
		||||
	case len(vdis) == 0:
 | 
			
		||||
		ui.Error(fmt.Sprintf("Couldn't find a VDI named '%s'", self.VdiName))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	case len(vdis) > 1:
 | 
			
		||||
		ui.Error(fmt.Sprintf("Found more than one VDI with name '%s'. Name must be unique", self.VdiName))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vdi := vdis[0]
 | 
			
		||||
 | 
			
		||||
	vdiUuid, err := c.client.VDI.GetUUID(c.session, vdi)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to get UUID of VDI '%s': %s", self.VdiName, err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
	state.Put(self.VdiUuidKey, vdiUuid)
 | 
			
		||||
 | 
			
		||||
	return multistep.ActionContinue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *StepFindVdi) Cleanup(state multistep.StateBag) {}
 | 
			
		||||
							
								
								
									
										50
									
								
								builder/xenserver/common/step_forward_port_over_ssh.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								builder/xenserver/common/step_forward_port_over_ssh.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/multistep"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/packer"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type StepForwardPortOverSSH struct {
 | 
			
		||||
	RemotePort func(state multistep.StateBag) (int, error)
 | 
			
		||||
	RemoteDest func(state multistep.StateBag) (string, error)
 | 
			
		||||
 | 
			
		||||
	HostPortMin uint
 | 
			
		||||
	HostPortMax uint
 | 
			
		||||
 | 
			
		||||
	ResultKey string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *StepForwardPortOverSSH) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
 | 
			
		||||
 | 
			
		||||
	config := state.Get("commonconfig").(CommonConfig)
 | 
			
		||||
	ui := state.Get("ui").(packer.Ui)
 | 
			
		||||
 | 
			
		||||
	// Find a free local port:
 | 
			
		||||
 | 
			
		||||
	l, sshHostPort := FindPort(self.HostPortMin, self.HostPortMax)
 | 
			
		||||
 | 
			
		||||
	if l == nil || sshHostPort == 0 {
 | 
			
		||||
		ui.Error("Error: unable to find free host port. Try providing a larger range [host_port_min, host_port_max]")
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ui.Say(fmt.Sprintf("Creating a local port forward over SSH on local port %d", sshHostPort))
 | 
			
		||||
 | 
			
		||||
	hostAddress, _ := state.Get("ssh_address").(string)
 | 
			
		||||
	remotePort, _ := self.RemotePort(state)
 | 
			
		||||
	remoteDest, _ := self.RemoteDest(state)
 | 
			
		||||
 | 
			
		||||
	go ssh_port_forward(l, remotePort, remoteDest, hostAddress, config.Username, config.Password)
 | 
			
		||||
	ui.Say(fmt.Sprintf("Port forward setup. %d ---> %s:%d on %s", sshHostPort, remoteDest, remotePort, hostAddress))
 | 
			
		||||
 | 
			
		||||
	// Provide the local port to future steps.
 | 
			
		||||
	state.Put(self.ResultKey, sshHostPort)
 | 
			
		||||
 | 
			
		||||
	return multistep.ActionContinue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *StepForwardPortOverSSH) Cleanup(state multistep.StateBag) {}
 | 
			
		||||
							
								
								
									
										51
									
								
								builder/xenserver/common/step_get_vnc_port.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								builder/xenserver/common/step_get_vnc_port.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/multistep"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/packer"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type StepGetVNCPort struct{}
 | 
			
		||||
 | 
			
		||||
func (self *StepGetVNCPort) Run(state multistep.StateBag) multistep.StepAction {
 | 
			
		||||
	ui := state.Get("ui").(packer.Ui)
 | 
			
		||||
 | 
			
		||||
	ui.Say("Step: forward the instances VNC port over SSH")
 | 
			
		||||
 | 
			
		||||
	domid := state.Get("domid").(int)
 | 
			
		||||
	cmd := fmt.Sprintf("xenstore-read /local/domain/%d/console/vnc-port", domid)
 | 
			
		||||
 | 
			
		||||
	remote_vncport, err := ExecuteHostSSHCmd(state, cmd)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to get VNC port (is the VM running?): %s", err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	remote_port, err := strconv.ParseUint(remote_vncport, 10, 16)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to convert '%s' to an int", remote_vncport))
 | 
			
		||||
		ui.Error(err.Error())
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	state.Put("instance_vnc_port", uint(remote_port))
 | 
			
		||||
 | 
			
		||||
	return multistep.ActionContinue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *StepGetVNCPort) Cleanup(state multistep.StateBag) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func InstanceVNCPort(state multistep.StateBag) (uint, error) {
 | 
			
		||||
	vncPort := state.Get("instance_vnc_port").(uint)
 | 
			
		||||
	return vncPort, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func InstanceVNCIP(state multistep.StateBag) (string, error) {
 | 
			
		||||
	// The port is in Dom0, so we want to forward from localhost
 | 
			
		||||
	return "127.0.0.1", nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										96
									
								
								builder/xenserver/common/step_http_server.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								builder/xenserver/common/step_http_server.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,96 @@
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
// Taken from hashicorp/packer/builder/qemu/step_http_server.go
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/multistep"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/packer"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// This step creates and runs the HTTP server that is serving files from the
 | 
			
		||||
// directory specified by the 'http_directory` configuration parameter in the
 | 
			
		||||
// template.
 | 
			
		||||
//
 | 
			
		||||
// Uses:
 | 
			
		||||
//   config *config
 | 
			
		||||
//   ui     packer.Ui
 | 
			
		||||
//
 | 
			
		||||
// Produces:
 | 
			
		||||
//   http_port int - The port the HTTP server started on.
 | 
			
		||||
type StepHTTPServer struct {
 | 
			
		||||
	Chan chan<- string
 | 
			
		||||
 | 
			
		||||
	l net.Listener
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type IPSnooper struct {
 | 
			
		||||
	ch      chan<- string
 | 
			
		||||
	handler http.Handler
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (snooper IPSnooper) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
 | 
			
		||||
	log.Printf("HTTP: %s %s %s", req.RemoteAddr, req.Method, req.URL)
 | 
			
		||||
	ip, _, err := net.SplitHostPort(req.RemoteAddr)
 | 
			
		||||
	if err == nil && ip != "" {
 | 
			
		||||
		select {
 | 
			
		||||
		case snooper.ch <- ip:
 | 
			
		||||
			log.Printf("Remembering remote address '%s'", ip)
 | 
			
		||||
		default:
 | 
			
		||||
			// if ch is already full, don't block waiting to send the address, just drop it
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	snooper.handler.ServeHTTP(resp, req)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *StepHTTPServer) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
 | 
			
		||||
	config := state.Get("commonconfig").(CommonConfig)
 | 
			
		||||
	ui := state.Get("ui").(packer.Ui)
 | 
			
		||||
 | 
			
		||||
	var httpPort uint = 0
 | 
			
		||||
	if config.HTTPDir == "" {
 | 
			
		||||
		// the packer provision steps assert this type is an int
 | 
			
		||||
		// so this cannot be a uint like the rest of the code
 | 
			
		||||
		state.Put("http_port", int(httpPort))
 | 
			
		||||
		return multistep.ActionContinue
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.l, httpPort = FindPort(config.HTTPPortMin, config.HTTPPortMax)
 | 
			
		||||
 | 
			
		||||
	if s.l == nil || httpPort == 0 {
 | 
			
		||||
		ui.Error("Error: unable to find free HTTP server port. Try providing a larger range [http_port_min, http_port_max]")
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ui.Say(fmt.Sprintf("Starting HTTP server on port %d", httpPort))
 | 
			
		||||
 | 
			
		||||
	// Start the HTTP server and run it in the background
 | 
			
		||||
	fileServer := http.FileServer(http.Dir(config.HTTPDir))
 | 
			
		||||
	server := &http.Server{
 | 
			
		||||
		Addr: fmt.Sprintf(":%d", httpPort),
 | 
			
		||||
		Handler: IPSnooper{
 | 
			
		||||
			ch:      s.Chan,
 | 
			
		||||
			handler: fileServer,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	go server.Serve(s.l)
 | 
			
		||||
 | 
			
		||||
	// Save the address into the state so it can be accessed in the future
 | 
			
		||||
	// the packer provision steps assert this type is an int
 | 
			
		||||
	// so this cannot be a uint like the rest of the code
 | 
			
		||||
	state.Put("http_port", int(httpPort))
 | 
			
		||||
 | 
			
		||||
	return multistep.ActionContinue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *StepHTTPServer) Cleanup(multistep.StateBag) {
 | 
			
		||||
	if s.l != nil {
 | 
			
		||||
		// Close the listener so that the HTTP server stops
 | 
			
		||||
		s.l.Close()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										54
									
								
								builder/xenserver/common/step_prepare_output_dir.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								builder/xenserver/common/step_prepare_output_dir.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
/* Taken from https://raw.githubusercontent.com/hashicorp/packer/master/builder/qemu/step_prepare_output_dir.go */
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/multistep"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/packer"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type StepPrepareOutputDir struct {
 | 
			
		||||
	Force bool
 | 
			
		||||
	Path  string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *StepPrepareOutputDir) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
 | 
			
		||||
	ui := state.Get("ui").(packer.Ui)
 | 
			
		||||
 | 
			
		||||
	if _, err := os.Stat(self.Path); err == nil && self.Force {
 | 
			
		||||
		ui.Say("Deleting previous output directory...")
 | 
			
		||||
		os.RemoveAll(self.Path)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := os.MkdirAll(self.Path, 0755); err != nil {
 | 
			
		||||
		state.Put("error", err)
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return multistep.ActionContinue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *StepPrepareOutputDir) Cleanup(state multistep.StateBag) {
 | 
			
		||||
	_, cancelled := state.GetOk(multistep.StateCancelled)
 | 
			
		||||
	_, halted := state.GetOk(multistep.StateHalted)
 | 
			
		||||
 | 
			
		||||
	if cancelled || halted {
 | 
			
		||||
		ui := state.Get("ui").(packer.Ui)
 | 
			
		||||
 | 
			
		||||
		ui.Say("Deleting output directory...")
 | 
			
		||||
		for i := 0; i < 5; i++ {
 | 
			
		||||
			err := os.RemoveAll(self.Path)
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			log.Printf("Error removing output dir: %s", err)
 | 
			
		||||
			time.Sleep(2 * time.Second)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										43
									
								
								builder/xenserver/common/step_set_vm_host_ssh_address.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								builder/xenserver/common/step_set_vm_host_ssh_address.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/multistep"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/packer"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type StepSetVmHostSshAddress struct{}
 | 
			
		||||
 | 
			
		||||
func (self *StepSetVmHostSshAddress) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
 | 
			
		||||
 | 
			
		||||
	c := state.Get("client").(*Connection)
 | 
			
		||||
	ui := state.Get("ui").(packer.Ui)
 | 
			
		||||
 | 
			
		||||
	ui.Say("Step: Set SSH address to VM host IP")
 | 
			
		||||
 | 
			
		||||
	uuid := state.Get("instance_uuid").(string)
 | 
			
		||||
	instance, err := c.client.VM.GetByUUID(c.session, uuid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to get VM from UUID '%s': %s", uuid, err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	host, err := c.client.VM.GetResidentOn(c.session, instance)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to get VM Host for VM '%s': %s", uuid, err.Error()))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	address, err := c.client.Host.GetAddress(c.session, host)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to get address from VM Host: %s", err.Error()))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	state.Put("ssh_address", address)
 | 
			
		||||
	ui.Say(fmt.Sprintf("Set host SSH address to '%s'.", address))
 | 
			
		||||
 | 
			
		||||
	return multistep.ActionContinue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *StepSetVmHostSshAddress) Cleanup(state multistep.StateBag) {}
 | 
			
		||||
							
								
								
									
										35
									
								
								builder/xenserver/common/step_set_vm_to_template.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								builder/xenserver/common/step_set_vm_to_template.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/multistep"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/packer"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type StepSetVmToTemplate struct{}
 | 
			
		||||
 | 
			
		||||
func (StepSetVmToTemplate) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
 | 
			
		||||
	ui := state.Get("ui").(packer.Ui)
 | 
			
		||||
	c := state.Get("client").(*Connection)
 | 
			
		||||
	instance_uuid := state.Get("instance_uuid").(string)
 | 
			
		||||
 | 
			
		||||
	instance, err := c.client.VM.GetByUUID(c.session, instance_uuid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Could not get VM with UUID '%s': %s", instance_uuid, err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = c.client.VM.SetIsATemplate(c.session, instance, true)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("failed to set VM '%s' as a template with error: %v", instance_uuid, err))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ui.Message("Successfully set VM as a template")
 | 
			
		||||
	return multistep.ActionContinue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (StepSetVmToTemplate) Cleanup(state multistep.StateBag) {}
 | 
			
		||||
							
								
								
									
										82
									
								
								builder/xenserver/common/step_shutdown.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								builder/xenserver/common/step_shutdown.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,82 @@
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/multistep"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/packer"
 | 
			
		||||
	xenapi "github.com/terra-farm/go-xen-api-client"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type StepShutdown struct{}
 | 
			
		||||
 | 
			
		||||
func (StepShutdown) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
 | 
			
		||||
	config := state.Get("commonconfig").(CommonConfig)
 | 
			
		||||
	ui := state.Get("ui").(packer.Ui)
 | 
			
		||||
	c := state.Get("client").(*Connection)
 | 
			
		||||
	instance_uuid := state.Get("instance_uuid").(string)
 | 
			
		||||
 | 
			
		||||
	instance, err := c.client.VM.GetByUUID(c.session, instance_uuid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Could not get VM with UUID '%s': %s", instance_uuid, err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ui.Say("Step: Shutting down VM")
 | 
			
		||||
 | 
			
		||||
	// Shutdown the VM
 | 
			
		||||
	success := func() bool {
 | 
			
		||||
		if config.ShutdownCommand != "" {
 | 
			
		||||
			ui.Message("Executing shutdown command...")
 | 
			
		||||
 | 
			
		||||
			_, err := ExecuteGuestSSHCmd(state, config.ShutdownCommand)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				ui.Error(fmt.Sprintf("Shutdown command failed: %s", err.Error()))
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			ui.Message("Waiting for VM to enter Halted state...")
 | 
			
		||||
 | 
			
		||||
			err = InterruptibleWait{
 | 
			
		||||
				Predicate: func() (bool, error) {
 | 
			
		||||
					power_state, err := c.client.VM.GetPowerState(c.session, instance)
 | 
			
		||||
					return power_state == xenapi.VMPowerStateHalted, err
 | 
			
		||||
				},
 | 
			
		||||
				PredicateInterval: 5 * time.Second,
 | 
			
		||||
				Timeout:           300 * time.Second,
 | 
			
		||||
			}.Wait(state)
 | 
			
		||||
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				ui.Error(fmt.Sprintf("Error waiting for VM to halt: %s", err.Error()))
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		} else {
 | 
			
		||||
			ui.Message("Attempting to cleanly shutdown the VM...")
 | 
			
		||||
 | 
			
		||||
			err = c.client.VM.CleanShutdown(c.session, instance)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				ui.Error(fmt.Sprintf("Could not shut down VM: %s", err.Error()))
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
		return true
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	if !success {
 | 
			
		||||
		ui.Say("WARNING: Forcing hard shutdown of the VM...")
 | 
			
		||||
		err = c.client.VM.HardShutdown(c.session, instance)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			ui.Error(fmt.Sprintf("Could not hard shut down VM -- giving up: %s", err.Error()))
 | 
			
		||||
			return multistep.ActionHalt
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ui.Message("Successfully shut down VM")
 | 
			
		||||
	return multistep.ActionContinue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (StepShutdown) Cleanup(state multistep.StateBag) {}
 | 
			
		||||
							
								
								
									
										137
									
								
								builder/xenserver/common/step_start_on_himn.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								builder/xenserver/common/step_start_on_himn.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,137 @@
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/multistep"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/packer"
 | 
			
		||||
	gossh "golang.org/x/crypto/ssh"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type StepStartOnHIMN struct{}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This step starts the installed guest on the Host Internal Management Network
 | 
			
		||||
 * as there exists an API to obtain the IP allocated to the VM by XAPI.
 | 
			
		||||
 * This in turn will allow Packer to SSH into the VM, provided NATing has been
 | 
			
		||||
 * enabled on the host.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
func (self *StepStartOnHIMN) Run(state multistep.StateBag) multistep.StepAction {
 | 
			
		||||
 | 
			
		||||
	ui := state.Get("ui").(packer.Ui)
 | 
			
		||||
	c := state.Get("client").(*Connection)
 | 
			
		||||
 | 
			
		||||
	ui.Say("Step: Start VM on the Host Internal Mangement Network")
 | 
			
		||||
 | 
			
		||||
	uuid := state.Get("instance_uuid").(string)
 | 
			
		||||
	instance, err := c.client.VM.GetByUUID(c.session, uuid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to get VM from UUID '%s': %s", uuid, err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Find the HIMN Ref
 | 
			
		||||
	networks, err := c.client.Network.GetByNameLabel(c.session, "Host internal management network")
 | 
			
		||||
	if err != nil || len(networks) == 0 {
 | 
			
		||||
		ui.Error("Unable to find a host internal management network")
 | 
			
		||||
		ui.Error(err.Error())
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	himn := networks[0]
 | 
			
		||||
 | 
			
		||||
	// Create a VIF for the HIMN
 | 
			
		||||
	himn_vif, err := ConnectNetwork(c, himn, instance, "0")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error("Error creating VIF")
 | 
			
		||||
		ui.Error(err.Error())
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Start the VM
 | 
			
		||||
	c.client.VM.Start(c.session, instance, false, false)
 | 
			
		||||
 | 
			
		||||
	var himn_iface_ip string = ""
 | 
			
		||||
 | 
			
		||||
	// Obtain the allocated IP
 | 
			
		||||
	err = InterruptibleWait{
 | 
			
		||||
		Predicate: func() (found bool, err error) {
 | 
			
		||||
			ips, err := c.client.Network.GetAssignedIps(c.session, himn)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return false, fmt.Errorf("Can't get assigned IPs: %s", err.Error())
 | 
			
		||||
			}
 | 
			
		||||
			log.Printf("IPs: %s", ips)
 | 
			
		||||
			log.Printf("Ref: %s", instance)
 | 
			
		||||
 | 
			
		||||
			//Check for instance.Ref in map
 | 
			
		||||
			if vm_ip, ok := ips[*himn_vif]; ok && vm_ip != "" {
 | 
			
		||||
				ui.Say("Found the VM's IP: " + vm_ip)
 | 
			
		||||
				himn_iface_ip = vm_ip
 | 
			
		||||
				return true, nil
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			ui.Say("Wait for IP address...")
 | 
			
		||||
			return false, nil
 | 
			
		||||
		},
 | 
			
		||||
		PredicateInterval: 10 * time.Second,
 | 
			
		||||
		Timeout:           100 * time.Second,
 | 
			
		||||
	}.Wait(state)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to find an IP on the Host-internal management interface: %s", err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	state.Put("himn_ssh_address", himn_iface_ip)
 | 
			
		||||
	ui.Say("Stored VM's IP " + himn_iface_ip)
 | 
			
		||||
 | 
			
		||||
	// Wait for the VM to boot, and check we can ping this interface
 | 
			
		||||
 | 
			
		||||
	ping_cmd := fmt.Sprintf("ping -c 1 %s", himn_iface_ip)
 | 
			
		||||
 | 
			
		||||
	err = InterruptibleWait{
 | 
			
		||||
		Predicate: func() (success bool, err error) {
 | 
			
		||||
			ui.Message(fmt.Sprintf("Attempting to ping interface: %s", ping_cmd))
 | 
			
		||||
			_, err = ExecuteHostSSHCmd(state, ping_cmd)
 | 
			
		||||
 | 
			
		||||
			switch err.(type) {
 | 
			
		||||
			case nil:
 | 
			
		||||
				// ping succeeded
 | 
			
		||||
				return true, nil
 | 
			
		||||
			case *gossh.ExitError:
 | 
			
		||||
				// ping failed, try again
 | 
			
		||||
				return false, nil
 | 
			
		||||
			default:
 | 
			
		||||
				// unknown error
 | 
			
		||||
				return false, err
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		PredicateInterval: 10 * time.Second,
 | 
			
		||||
		Timeout:           300 * time.Second,
 | 
			
		||||
	}.Wait(state)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to ping interface. (Has the VM not booted?): %s", err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ui.Message("Ping success! Continuing...")
 | 
			
		||||
	return multistep.ActionContinue
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *StepStartOnHIMN) Cleanup(state multistep.StateBag) {}
 | 
			
		||||
 | 
			
		||||
func HimnSSHIP(state multistep.StateBag) (string, error) {
 | 
			
		||||
	ip := state.Get("himn_ssh_address").(string)
 | 
			
		||||
	return ip, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func HimnSSHPort(state multistep.StateBag) (uint, error) {
 | 
			
		||||
	config := state.Get("commonconfig").(CommonConfig)
 | 
			
		||||
	return config.SSHPort, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										58
									
								
								builder/xenserver/common/step_start_vm_paused.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								builder/xenserver/common/step_start_vm_paused.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/multistep"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/packer"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type StepStartVmPaused struct {
 | 
			
		||||
	VmCleanup
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *StepStartVmPaused) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
 | 
			
		||||
 | 
			
		||||
	c := state.Get("client").(*Connection)
 | 
			
		||||
	ui := state.Get("ui").(packer.Ui)
 | 
			
		||||
	config := state.Get("config").(Config)
 | 
			
		||||
 | 
			
		||||
	ui.Say("Step: Start VM Paused")
 | 
			
		||||
 | 
			
		||||
	uuid := state.Get("instance_uuid").(string)
 | 
			
		||||
	instance, err := c.client.VM.GetByUUID(c.session, uuid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to get VM from UUID '%s': %s", uuid, err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// note that here "cd" means boot from hard drive ('c') first, then CDROM ('d')
 | 
			
		||||
	err = c.client.VM.SetHVMBootPolicy(c.session, instance, "BIOS order")
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to set HVM boot params: %s", err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = c.client.VM.SetHVMBootParams(c.session, instance, map[string]string{"order": "cd", "firmware": config.Firmware})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to set HVM boot params: %s", err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = c.client.VM.Start(c.session, instance, true, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to start VM with UUID '%s': %s", uuid, err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	domid, err := c.client.VM.GetDomid(c.session, instance)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to get domid of VM with UUID '%s': %s", uuid, err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
	state.Put("domid", domid)
 | 
			
		||||
 | 
			
		||||
	return multistep.ActionContinue
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										272
									
								
								builder/xenserver/common/step_type_boot_command.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										272
									
								
								builder/xenserver/common/step_type_boot_command.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,272 @@
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
/* Heavily borrowed from builder/quemu/step_type_boot_command.go */
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
	"unicode"
 | 
			
		||||
	"unicode/utf8"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/multistep"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/packer"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
 | 
			
		||||
	"github.com/mitchellh/go-vnc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const KeyLeftShift uint = 0xFFE1
 | 
			
		||||
 | 
			
		||||
type bootCommandTemplateData struct {
 | 
			
		||||
	Name     string
 | 
			
		||||
	HTTPIP   string
 | 
			
		||||
	HTTPPort uint
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type StepTypeBootCommand struct {
 | 
			
		||||
	Ctx interpolate.Context
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
 | 
			
		||||
	config := state.Get("commonconfig").(CommonConfig)
 | 
			
		||||
	ui := state.Get("ui").(packer.Ui)
 | 
			
		||||
	c := state.Get("client").(*Connection)
 | 
			
		||||
	httpPort := state.Get("http_port").(int)
 | 
			
		||||
 | 
			
		||||
	// skip this step if we have nothing to type
 | 
			
		||||
	if len(config.BootCommand) == 0 {
 | 
			
		||||
		return multistep.ActionContinue
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vmRef, err := c.client.VM.GetByNameLabel(c.session, config.VMName)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		state.Put("error", err)
 | 
			
		||||
		ui.Error(err.Error())
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(vmRef) != 1 {
 | 
			
		||||
		ui.Error(fmt.Sprintf("expected to find a single VM, instead found '%d'. Ensure the VM name is unique", len(vmRef)))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	consoles, err := c.client.VM.GetConsoles(c.session, vmRef[0])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		state.Put("error", err)
 | 
			
		||||
		ui.Error(err.Error())
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(consoles) != 1 {
 | 
			
		||||
		ui.Error(fmt.Sprintf("expected to find a VM console, instead found '%d'. Ensure there is only one console", len(consoles)))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	location, err := c.client.Console.GetLocation(c.session, consoles[0])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(err.Error())
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
	locationPieces := strings.SplitAfter(location, "/")
 | 
			
		||||
	consoleHost := strings.TrimSuffix(locationPieces[2], "/")
 | 
			
		||||
	ui.Say(fmt.Sprintf("Connecting to the VM console VNC over xapi via %s", consoleHost))
 | 
			
		||||
	conn, err := net.Dial("tcp", fmt.Sprintf("%s:443", consoleHost))
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		err := fmt.Errorf("Error connecting to VNC: %s", err)
 | 
			
		||||
		state.Put("error", err)
 | 
			
		||||
		ui.Error(err.Error())
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer conn.Close()
 | 
			
		||||
 | 
			
		||||
	tlsConfig := &tls.Config{
 | 
			
		||||
		InsecureSkipVerify: true,
 | 
			
		||||
	}
 | 
			
		||||
	tlsConn := tls.Client(conn, tlsConfig)
 | 
			
		||||
 | 
			
		||||
	consoleLocation := strings.TrimSpace(fmt.Sprintf("/%s", locationPieces[len(locationPieces)-1]))
 | 
			
		||||
	httpReq := fmt.Sprintf("CONNECT %s HTTP/1.0\r\nHost: %s\r\nCookie: session_id=%s\r\n\r\n", consoleLocation, consoleHost, c.session)
 | 
			
		||||
	fmt.Printf("Sending the follow http req: %v", httpReq)
 | 
			
		||||
 | 
			
		||||
	ui.Say(fmt.Sprintf("Making HTTP request to initiate VNC connection: %s", httpReq))
 | 
			
		||||
	_, err = io.WriteString(tlsConn, httpReq)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		err := fmt.Errorf("failed to start vnc session: %v", err)
 | 
			
		||||
		state.Put("error", err)
 | 
			
		||||
		ui.Error(err.Error())
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buffer := make([]byte, 10000)
 | 
			
		||||
	_, err = tlsConn.Read(buffer)
 | 
			
		||||
	if err != nil && err != io.EOF {
 | 
			
		||||
		err := fmt.Errorf("failed to read vnc session response: %v", err)
 | 
			
		||||
		state.Put("error", err)
 | 
			
		||||
		ui.Error(err.Error())
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ui.Say(fmt.Sprintf("Received response: %s", string(buffer)))
 | 
			
		||||
 | 
			
		||||
	vncClient, err := vnc.Client(tlsConn, &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
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer vncClient.Close()
 | 
			
		||||
 | 
			
		||||
	log.Printf("Connected to the VNC console: %s", vncClient.DesktopName)
 | 
			
		||||
 | 
			
		||||
	// find local ip
 | 
			
		||||
	envVar, err := ExecuteHostSSHCmd(state, "echo $SSH_CLIENT")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Error detecting local IP: %s", err))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
	if envVar == "" {
 | 
			
		||||
		ui.Error("Error detecting local IP: $SSH_CLIENT was empty")
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
	localIp := strings.Split(envVar, " ")[0]
 | 
			
		||||
	ui.Message(fmt.Sprintf("Found local IP: %s", localIp))
 | 
			
		||||
 | 
			
		||||
	self.Ctx.Data = &bootCommandTemplateData{
 | 
			
		||||
		config.VMName,
 | 
			
		||||
		localIp,
 | 
			
		||||
		uint(httpPort),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ui.Say("Typing boot commands over VNC...")
 | 
			
		||||
	for _, command := range config.BootCommand {
 | 
			
		||||
 | 
			
		||||
		command, err := interpolate.Render(command, &self.Ctx)
 | 
			
		||||
		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
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		vncSendString(vncClient, command)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ui.Say("Finished typing.")
 | 
			
		||||
 | 
			
		||||
	return multistep.ActionContinue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *StepTypeBootCommand) Cleanup(multistep.StateBag) {}
 | 
			
		||||
 | 
			
		||||
// Taken from qemu's builder plugin - not an exported function.
 | 
			
		||||
func vncSendString(c *vnc.ClientConn, original string) {
 | 
			
		||||
	// Scancodes reference: https://github.com/qemu/qemu/blob/master/ui/vnc_keysym.h
 | 
			
		||||
	special := make(map[string]uint32)
 | 
			
		||||
	special["<bs>"] = 0xFF08
 | 
			
		||||
	special["<del>"] = 0xFFFF
 | 
			
		||||
	special["<enter>"] = 0xFF0D
 | 
			
		||||
	special["<esc>"] = 0xFF1B
 | 
			
		||||
	special["<f1>"] = 0xFFBE
 | 
			
		||||
	special["<f2>"] = 0xFFBF
 | 
			
		||||
	special["<f3>"] = 0xFFC0
 | 
			
		||||
	special["<f4>"] = 0xFFC1
 | 
			
		||||
	special["<f5>"] = 0xFFC2
 | 
			
		||||
	special["<f6>"] = 0xFFC3
 | 
			
		||||
	special["<f7>"] = 0xFFC4
 | 
			
		||||
	special["<f8>"] = 0xFFC5
 | 
			
		||||
	special["<f9>"] = 0xFFC6
 | 
			
		||||
	special["<f10>"] = 0xFFC7
 | 
			
		||||
	special["<f11>"] = 0xFFC8
 | 
			
		||||
	special["<f12>"] = 0xFFC9
 | 
			
		||||
	special["<return>"] = 0xFF0D
 | 
			
		||||
	special["<tab>"] = 0xFF09
 | 
			
		||||
	special["<up>"] = 0xFF52
 | 
			
		||||
	special["<down>"] = 0xFF54
 | 
			
		||||
	special["<left>"] = 0xFF51
 | 
			
		||||
	special["<right>"] = 0xFF53
 | 
			
		||||
	special["<spacebar>"] = 0x020
 | 
			
		||||
	special["<insert>"] = 0xFF63
 | 
			
		||||
	special["<home>"] = 0xFF50
 | 
			
		||||
	special["<end>"] = 0xFF57
 | 
			
		||||
	special["<pageUp>"] = 0xFF55
 | 
			
		||||
	special["<pageDown>"] = 0xFF56
 | 
			
		||||
 | 
			
		||||
	shiftedChars := "~!@#$%^&*()_+{}|:\"<>?"
 | 
			
		||||
 | 
			
		||||
	// TODO(mitchellh): Ripe for optimizations of some point, perhaps.
 | 
			
		||||
	for len(original) > 0 {
 | 
			
		||||
		var keyCode uint32
 | 
			
		||||
		keyShift := false
 | 
			
		||||
 | 
			
		||||
		if strings.HasPrefix(original, "<wait>") {
 | 
			
		||||
			log.Printf("Special code '<wait>' found, sleeping one second")
 | 
			
		||||
			time.Sleep(1 * time.Second)
 | 
			
		||||
			original = original[len("<wait>"):]
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if strings.HasPrefix(original, "<wait5>") {
 | 
			
		||||
			log.Printf("Special code '<wait5>' found, sleeping 5 seconds")
 | 
			
		||||
			time.Sleep(5 * time.Second)
 | 
			
		||||
			original = original[len("<wait5>"):]
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if strings.HasPrefix(original, "<wait10>") {
 | 
			
		||||
			log.Printf("Special code '<wait10>' found, sleeping 10 seconds")
 | 
			
		||||
			time.Sleep(10 * time.Second)
 | 
			
		||||
			original = original[len("<wait10>"):]
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for specialCode, specialValue := range special {
 | 
			
		||||
			if strings.HasPrefix(original, specialCode) {
 | 
			
		||||
				log.Printf("Special code '%s' found, replacing with: %d", specialCode, specialValue)
 | 
			
		||||
				keyCode = specialValue
 | 
			
		||||
				original = original[len(specialCode):]
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if keyCode == 0 {
 | 
			
		||||
			r, size := utf8.DecodeRuneInString(original)
 | 
			
		||||
			original = original[size:]
 | 
			
		||||
			keyCode = uint32(r)
 | 
			
		||||
			keyShift = unicode.IsUpper(r) || strings.ContainsRune(shiftedChars, r)
 | 
			
		||||
 | 
			
		||||
			log.Printf("Sending char '%c', code %d, shift %v", r, keyCode, keyShift)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if keyShift {
 | 
			
		||||
			c.KeyEvent(uint32(KeyLeftShift), true)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		c.KeyEvent(keyCode, true)
 | 
			
		||||
		time.Sleep(time.Second / 10)
 | 
			
		||||
		c.KeyEvent(keyCode, false)
 | 
			
		||||
		time.Sleep(time.Second / 10)
 | 
			
		||||
 | 
			
		||||
		if keyShift {
 | 
			
		||||
			c.KeyEvent(uint32(KeyLeftShift), false)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// no matter what, wait a small period
 | 
			
		||||
		time.Sleep(50 * time.Millisecond)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										145
									
								
								builder/xenserver/common/step_upload_vdi.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								builder/xenserver/common/step_upload_vdi.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,145 @@
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/multistep"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/packer"
 | 
			
		||||
	xenapi "github.com/terra-farm/go-xen-api-client"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type StepUploadVdi struct {
 | 
			
		||||
	VdiNameFunc   func() string
 | 
			
		||||
	ImagePathFunc func() string
 | 
			
		||||
	VdiUuidKey    string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *StepUploadVdi) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
 | 
			
		||||
	config := state.Get("commonconfig").(CommonConfig)
 | 
			
		||||
	ui := state.Get("ui").(packer.Ui)
 | 
			
		||||
	c := state.Get("client").(*Connection)
 | 
			
		||||
 | 
			
		||||
	imagePath := self.ImagePathFunc()
 | 
			
		||||
	vdiName := self.VdiNameFunc()
 | 
			
		||||
	if imagePath == "" {
 | 
			
		||||
		// skip if no disk image to attach
 | 
			
		||||
		return multistep.ActionContinue
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ui.Say(fmt.Sprintf("Step: Upload VDI '%s'", vdiName))
 | 
			
		||||
 | 
			
		||||
	// Create VDI for the image
 | 
			
		||||
	srs, err := c.client.SR.GetAll(c.session)
 | 
			
		||||
	ui.Say(fmt.Sprintf("Step: Found SRs '%v'", srs))
 | 
			
		||||
 | 
			
		||||
	sr, err := config.GetISOSR(c)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to get SR: %v", err))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Open the file for reading (NB: HTTPUpload closes the file for us)
 | 
			
		||||
	fh, err := os.Open(imagePath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to open disk image '%s': %s", imagePath, err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get file length
 | 
			
		||||
	fstat, err := fh.Stat()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to stat disk image '%s': %s", imagePath, err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
	fileLength := fstat.Size()
 | 
			
		||||
 | 
			
		||||
	// Create the VDI
 | 
			
		||||
	// vdi, err := sr.CreateVdi(vdiName, fileLength)
 | 
			
		||||
	vdi, err := c.client.VDI.Create(c.session, xenapi.VDIRecord{
 | 
			
		||||
		NameLabel:   vdiName,
 | 
			
		||||
		VirtualSize: int(fileLength),
 | 
			
		||||
		Type:        "user",
 | 
			
		||||
		Sharable:    false,
 | 
			
		||||
		ReadOnly:    false,
 | 
			
		||||
		SR:          sr,
 | 
			
		||||
		OtherConfig: map[string]string{
 | 
			
		||||
			"temp": "temp",
 | 
			
		||||
		},
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to create VDI '%s': %s", vdiName, err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vdiUuid, err := c.client.VDI.GetUUID(c.session, vdi)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to get UUID of VDI '%s': %s", vdiName, err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
	state.Put(self.VdiUuidKey, vdiUuid)
 | 
			
		||||
 | 
			
		||||
	_, err = HTTPUpload(fmt.Sprintf("https://%s/import_raw_vdi?vdi=%s&session_id=%s",
 | 
			
		||||
		c.Host,
 | 
			
		||||
		vdi,
 | 
			
		||||
		c.GetSession(),
 | 
			
		||||
	), fh, state)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to upload VDI: %s", err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return multistep.ActionContinue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *StepUploadVdi) Cleanup(state multistep.StateBag) {
 | 
			
		||||
	config := state.Get("commonconfig").(CommonConfig)
 | 
			
		||||
	ui := state.Get("ui").(packer.Ui)
 | 
			
		||||
	c := state.Get("client").(*Connection)
 | 
			
		||||
 | 
			
		||||
	vdiName := self.VdiNameFunc()
 | 
			
		||||
 | 
			
		||||
	if config.ShouldKeepVM(state) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vdiUuidRaw, ok := state.GetOk(self.VdiUuidKey)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		// VDI doesn't exist
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vdiUuid := vdiUuidRaw.(string)
 | 
			
		||||
	if vdiUuid == "" {
 | 
			
		||||
		// VDI already cleaned up
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vdi, err := c.client.VDI.GetByUUID(c.session, vdiUuid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Can't get VDI '%s': %s", vdiUuid, err.Error()))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// an interrupted import_raw_vdi takes a while to release the VDI
 | 
			
		||||
	// so try several times
 | 
			
		||||
	for i := 0; i < 3; i++ {
 | 
			
		||||
		log.Printf("Trying to destroy VDI...")
 | 
			
		||||
		err = c.client.VDI.Destroy(c.session, vdi)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		time.Sleep(1 * time.Second)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Can't destroy VDI '%s': %s", vdiUuid, err.Error()))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	ui.Say(fmt.Sprintf("Destroyed VDI '%s'", vdiName))
 | 
			
		||||
 | 
			
		||||
	state.Put(self.VdiUuidKey, "")
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										96
									
								
								builder/xenserver/common/step_wait_for_ip.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								builder/xenserver/common/step_wait_for_ip.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,96 @@
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/multistep"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/packer"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type StepWaitForIP struct {
 | 
			
		||||
	VmCleanup
 | 
			
		||||
	Chan    <-chan string
 | 
			
		||||
	Timeout time.Duration
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *StepWaitForIP) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
 | 
			
		||||
	ui := state.Get("ui").(packer.Ui)
 | 
			
		||||
	c := state.Get("client").(*Connection)
 | 
			
		||||
	config := state.Get("commonconfig").(CommonConfig)
 | 
			
		||||
 | 
			
		||||
	ui.Say("Step: Wait for VM's IP to become known to us.")
 | 
			
		||||
 | 
			
		||||
	uuid := state.Get("instance_uuid").(string)
 | 
			
		||||
	instance, err := c.client.VM.GetByUUID(c.session, uuid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to get VM from UUID '%s': %s", uuid, err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var ip string
 | 
			
		||||
	err = InterruptibleWait{
 | 
			
		||||
		Timeout:           self.Timeout,
 | 
			
		||||
		PredicateInterval: 5 * time.Second,
 | 
			
		||||
		Predicate: func() (result bool, err error) {
 | 
			
		||||
 | 
			
		||||
			if config.IPGetter == "auto" || config.IPGetter == "http" {
 | 
			
		||||
 | 
			
		||||
				// Snoop IP from HTTP fetch
 | 
			
		||||
				select {
 | 
			
		||||
				case ip = <-self.Chan:
 | 
			
		||||
					ui.Message(fmt.Sprintf("Got IP '%s' from HTTP request", ip))
 | 
			
		||||
					return true, nil
 | 
			
		||||
				default:
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if config.IPGetter == "auto" || config.IPGetter == "tools" {
 | 
			
		||||
 | 
			
		||||
				// Look for PV IP
 | 
			
		||||
				m, err := c.client.VM.GetGuestMetrics(c.session, instance)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return false, err
 | 
			
		||||
				}
 | 
			
		||||
				if m != "" {
 | 
			
		||||
					metrics, err := c.client.VMGuestMetrics.GetRecord(c.session, m)
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						return false, err
 | 
			
		||||
					}
 | 
			
		||||
					networks := metrics.Networks
 | 
			
		||||
					var ok bool
 | 
			
		||||
					if ip, ok = networks["0/ip"]; ok {
 | 
			
		||||
						if ip != "" {
 | 
			
		||||
							ui.Message(fmt.Sprintf("Got IP '%s' from XenServer tools", ip))
 | 
			
		||||
							return true, nil
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return false, nil
 | 
			
		||||
		},
 | 
			
		||||
	}.Wait(state)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Could not get IP address of VM: %s", err.Error()))
 | 
			
		||||
		// @todo: give advice on what went wrong (no HTTP server? no PV drivers?)
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ui.Say(fmt.Sprintf("Got IP address '%s'", ip))
 | 
			
		||||
	state.Put("instance_ssh_address", ip)
 | 
			
		||||
 | 
			
		||||
	return multistep.ActionContinue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func InstanceSSHIP(state multistep.StateBag) (string, error) {
 | 
			
		||||
	ip := state.Get("instance_ssh_address").(string)
 | 
			
		||||
	return ip, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func InstanceSSHPort(state multistep.StateBag) (int, error) {
 | 
			
		||||
	return 22, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										31
									
								
								builder/xenserver/common/vm_cleanup.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								builder/xenserver/common/vm_cleanup.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
package common
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/multistep"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type VmCleanup struct{}
 | 
			
		||||
 | 
			
		||||
func (self *VmCleanup) Cleanup(state multistep.StateBag) {
 | 
			
		||||
	config := state.Get("commonconfig").(CommonConfig)
 | 
			
		||||
	c := state.Get("client").(*Connection)
 | 
			
		||||
 | 
			
		||||
	if config.ShouldKeepVM(state) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	uuid := state.Get("instance_uuid").(string)
 | 
			
		||||
	instance, err := c.client.VM.GetByUUID(c.session, uuid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Printf(fmt.Sprintf("Unable to get VM from UUID '%s': %s", uuid, err.Error()))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = c.client.VM.HardShutdown(c.session, instance)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Printf(fmt.Sprintf("Unable to force shutdown VM '%s': %s", uuid, err.Error()))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										325
									
								
								builder/xenserver/iso/builder.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										325
									
								
								builder/xenserver/iso/builder.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,325 @@
 | 
			
		||||
package iso
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"path"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/hcl/v2/hcldec"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/communicator"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/multistep"
 | 
			
		||||
	commonsteps "github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/packer"
 | 
			
		||||
	hconfig "github.com/hashicorp/packer-plugin-sdk/template/config"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
 | 
			
		||||
	xsclient "github.com/terra-farm/go-xen-api-client"
 | 
			
		||||
	xscommon "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Builder struct {
 | 
			
		||||
	config xscommon.Config
 | 
			
		||||
	runner multistep.Runner
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *Builder) ConfigSpec() hcldec.ObjectSpec { return self.config.FlatMapstructure().HCL2Spec() }
 | 
			
		||||
 | 
			
		||||
func (self *Builder) Prepare(raws ...interface{}) (params []string, warns []string, retErr error) {
 | 
			
		||||
 | 
			
		||||
	var errs *packer.MultiError
 | 
			
		||||
 | 
			
		||||
	err := hconfig.Decode(&self.config, &hconfig.DecodeOpts{
 | 
			
		||||
		Interpolate: true,
 | 
			
		||||
		InterpolateFilter: &interpolate.RenderFilter{
 | 
			
		||||
			Exclude: []string{
 | 
			
		||||
				"boot_command",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}, raws...)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		packer.MultiErrorAppend(errs, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	errs = packer.MultiErrorAppend(
 | 
			
		||||
		errs, self.config.CommonConfig.Prepare(self.config.GetInterpContext(), &self.config.PackerConfig)...)
 | 
			
		||||
	errs = packer.MultiErrorAppend(errs, self.config.SSHConfig.Prepare(self.config.GetInterpContext())...)
 | 
			
		||||
 | 
			
		||||
	// Set default values
 | 
			
		||||
 | 
			
		||||
	if self.config.RawInstallTimeout == "" {
 | 
			
		||||
		self.config.RawInstallTimeout = "200m"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if self.config.DiskSize == 0 {
 | 
			
		||||
		self.config.DiskSize = 40000
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if self.config.VCPUsMax == 0 {
 | 
			
		||||
		self.config.VCPUsMax = 1
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if self.config.VCPUsAtStartup == 0 {
 | 
			
		||||
		self.config.VCPUsAtStartup = 1
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if self.config.VCPUsAtStartup > self.config.VCPUsMax {
 | 
			
		||||
		self.config.VCPUsAtStartup = self.config.VCPUsMax
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if self.config.VMMemory == 0 {
 | 
			
		||||
		self.config.VMMemory = 1024
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if self.config.CloneTemplate == "" {
 | 
			
		||||
		self.config.CloneTemplate = "Other install media"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if self.config.Firmware == "" {
 | 
			
		||||
		self.config.Firmware = "bios"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Template substitution
 | 
			
		||||
 | 
			
		||||
	templates := map[string]*string{
 | 
			
		||||
		"clone_template":    &self.config.CloneTemplate,
 | 
			
		||||
		"iso_checksum":      &self.config.ISOChecksum,
 | 
			
		||||
		"iso_checksum_type": &self.config.ISOChecksumType,
 | 
			
		||||
		"iso_url":           &self.config.ISOUrl,
 | 
			
		||||
		"iso_name":          &self.config.ISOName,
 | 
			
		||||
		"install_timeout":   &self.config.RawInstallTimeout,
 | 
			
		||||
	}
 | 
			
		||||
	for i := range self.config.ISOUrls {
 | 
			
		||||
		templates[fmt.Sprintf("iso_urls[%d]", i)] = &self.config.ISOUrls[i]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Validation
 | 
			
		||||
 | 
			
		||||
	self.config.InstallTimeout, err = time.ParseDuration(self.config.RawInstallTimeout)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		errs = packer.MultiErrorAppend(
 | 
			
		||||
			errs, fmt.Errorf("Failed to parse install_timeout: %s", err))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if self.config.ISOName == "" {
 | 
			
		||||
 | 
			
		||||
		// If ISO name is not specified, assume a URL and checksum has been provided.
 | 
			
		||||
 | 
			
		||||
		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 len(self.config.ISOUrls) == 0 {
 | 
			
		||||
			if self.config.ISOUrl == "" {
 | 
			
		||||
				errs = packer.MultiErrorAppend(
 | 
			
		||||
					errs, errors.New("One of iso_url or iso_urls must be specified."))
 | 
			
		||||
			} else {
 | 
			
		||||
				self.config.ISOUrls = []string{self.config.ISOUrl}
 | 
			
		||||
			}
 | 
			
		||||
		} else if self.config.ISOUrl != "" {
 | 
			
		||||
			errs = packer.MultiErrorAppend(
 | 
			
		||||
				errs, errors.New("Only one of iso_url or iso_urls may be specified."))
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
 | 
			
		||||
		// An ISO name has been provided. It should be attached from an available SR.
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(errs.Errors) > 0 {
 | 
			
		||||
		retErr = errors.New(errs.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, nil, retErr
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
 | 
			
		||||
	c, err := xscommon.NewXenAPIClient(self.config.HostIp, self.config.Username, self.config.Password)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	ui.Say("XAPI client session established")
 | 
			
		||||
 | 
			
		||||
	c.GetClient().Host.GetAll(c.GetSessionRef())
 | 
			
		||||
 | 
			
		||||
	//Share state between the other steps using a statebag
 | 
			
		||||
	state := new(multistep.BasicStateBag)
 | 
			
		||||
	state.Put("client", c)
 | 
			
		||||
	state.Put("config", self.config)
 | 
			
		||||
	state.Put("commonconfig", self.config.CommonConfig)
 | 
			
		||||
	state.Put("hook", hook)
 | 
			
		||||
	state.Put("ui", ui)
 | 
			
		||||
 | 
			
		||||
	httpReqChan := make(chan string, 1)
 | 
			
		||||
 | 
			
		||||
	//Build the steps
 | 
			
		||||
	download_steps := []multistep.Step{
 | 
			
		||||
		&commonsteps.StepDownload{
 | 
			
		||||
			Checksum:    self.config.ISOChecksum,
 | 
			
		||||
			Description: "ISO",
 | 
			
		||||
			ResultKey:   "iso_path",
 | 
			
		||||
			Url:         self.config.ISOUrls,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	steps := []multistep.Step{
 | 
			
		||||
		&xscommon.StepPrepareOutputDir{
 | 
			
		||||
			Force: self.config.PackerForce,
 | 
			
		||||
			Path:  self.config.OutputDir,
 | 
			
		||||
		},
 | 
			
		||||
		&commonsteps.StepCreateFloppy{
 | 
			
		||||
			Files: self.config.FloppyFiles,
 | 
			
		||||
			Label: "cidata",
 | 
			
		||||
		},
 | 
			
		||||
		&xscommon.StepHTTPServer{
 | 
			
		||||
			Chan: httpReqChan,
 | 
			
		||||
		},
 | 
			
		||||
		&xscommon.StepUploadVdi{
 | 
			
		||||
			VdiNameFunc: func() string {
 | 
			
		||||
				return "Packer-floppy-disk"
 | 
			
		||||
			},
 | 
			
		||||
			ImagePathFunc: func() string {
 | 
			
		||||
				if floppyPath, ok := state.GetOk("floppy_path"); ok {
 | 
			
		||||
					return floppyPath.(string)
 | 
			
		||||
				}
 | 
			
		||||
				return ""
 | 
			
		||||
			},
 | 
			
		||||
			VdiUuidKey: "floppy_vdi_uuid",
 | 
			
		||||
		},
 | 
			
		||||
		&xscommon.StepUploadVdi{
 | 
			
		||||
			VdiNameFunc: func() string {
 | 
			
		||||
				if len(self.config.ISOUrls) > 0 {
 | 
			
		||||
					return path.Base(self.config.ISOUrls[0])
 | 
			
		||||
				}
 | 
			
		||||
				return ""
 | 
			
		||||
			},
 | 
			
		||||
			ImagePathFunc: func() string {
 | 
			
		||||
				if isoPath, ok := state.GetOk("iso_path"); ok {
 | 
			
		||||
					return isoPath.(string)
 | 
			
		||||
				}
 | 
			
		||||
				return ""
 | 
			
		||||
			},
 | 
			
		||||
			VdiUuidKey: "iso_vdi_uuid",
 | 
			
		||||
		},
 | 
			
		||||
		&xscommon.StepFindVdi{
 | 
			
		||||
			VdiName:    self.config.ToolsIsoName,
 | 
			
		||||
			VdiUuidKey: "tools_vdi_uuid",
 | 
			
		||||
		},
 | 
			
		||||
		&xscommon.StepFindVdi{
 | 
			
		||||
			VdiName:    self.config.ISOName,
 | 
			
		||||
			VdiUuidKey: "isoname_vdi_uuid",
 | 
			
		||||
		},
 | 
			
		||||
		new(stepCreateInstance),
 | 
			
		||||
		&xscommon.StepAttachVdi{
 | 
			
		||||
			VdiUuidKey: "floppy_vdi_uuid",
 | 
			
		||||
			VdiType:    xsclient.VbdTypeFloppy,
 | 
			
		||||
		},
 | 
			
		||||
		&xscommon.StepAttachVdi{
 | 
			
		||||
			VdiUuidKey: "iso_vdi_uuid",
 | 
			
		||||
			VdiType:    xsclient.VbdTypeCD,
 | 
			
		||||
		},
 | 
			
		||||
		&xscommon.StepAttachVdi{
 | 
			
		||||
			VdiUuidKey: "isoname_vdi_uuid",
 | 
			
		||||
			VdiType:    xsclient.VbdTypeCD,
 | 
			
		||||
		},
 | 
			
		||||
		&xscommon.StepAttachVdi{
 | 
			
		||||
			VdiUuidKey: "tools_vdi_uuid",
 | 
			
		||||
			VdiType:    xsclient.VbdTypeCD,
 | 
			
		||||
		},
 | 
			
		||||
		new(xscommon.StepStartVmPaused),
 | 
			
		||||
		new(xscommon.StepSetVmHostSshAddress),
 | 
			
		||||
		// &xscommon.StepForwardPortOverSSH{
 | 
			
		||||
		// 	RemotePort:  xscommon.InstanceVNCPort,
 | 
			
		||||
		// 	RemoteDest:  xscommon.InstanceVNCIP,
 | 
			
		||||
		// 	HostPortMin: self.config.HostPortMin,
 | 
			
		||||
		// 	HostPortMax: self.config.HostPortMax,
 | 
			
		||||
		// 	ResultKey:   "local_vnc_port",
 | 
			
		||||
		// },
 | 
			
		||||
		new(xscommon.StepBootWait),
 | 
			
		||||
		&xscommon.StepTypeBootCommand{
 | 
			
		||||
			Ctx: *self.config.GetInterpContext(),
 | 
			
		||||
		},
 | 
			
		||||
		&xscommon.StepWaitForIP{
 | 
			
		||||
			Chan:    httpReqChan,
 | 
			
		||||
			Timeout: self.config.InstallTimeout, // @todo change this
 | 
			
		||||
		},
 | 
			
		||||
		&xscommon.StepForwardPortOverSSH{
 | 
			
		||||
			RemotePort:  xscommon.InstanceSSHPort,
 | 
			
		||||
			RemoteDest:  xscommon.InstanceSSHIP,
 | 
			
		||||
			HostPortMin: self.config.HostPortMin,
 | 
			
		||||
			HostPortMax: self.config.HostPortMax,
 | 
			
		||||
			ResultKey:   "local_ssh_port",
 | 
			
		||||
		},
 | 
			
		||||
		&communicator.StepConnect{
 | 
			
		||||
			Config:    &self.config.SSHConfig.Comm,
 | 
			
		||||
			Host:      xscommon.InstanceSSHIP,
 | 
			
		||||
			SSHConfig: self.config.Comm.SSHConfigFunc(),
 | 
			
		||||
			SSHPort:   xscommon.InstanceSSHPort,
 | 
			
		||||
		},
 | 
			
		||||
		new(commonsteps.StepProvision),
 | 
			
		||||
		new(xscommon.StepShutdown),
 | 
			
		||||
		new(xscommon.StepSetVmToTemplate),
 | 
			
		||||
		&xscommon.StepDetachVdi{
 | 
			
		||||
			VdiUuidKey: "iso_vdi_uuid",
 | 
			
		||||
		},
 | 
			
		||||
		&xscommon.StepDetachVdi{
 | 
			
		||||
			VdiUuidKey: "isoname_vdi_uuid",
 | 
			
		||||
		},
 | 
			
		||||
		&xscommon.StepDetachVdi{
 | 
			
		||||
			VdiUuidKey: "tools_vdi_uuid",
 | 
			
		||||
		},
 | 
			
		||||
		&xscommon.StepDetachVdi{
 | 
			
		||||
			VdiUuidKey: "floppy_vdi_uuid",
 | 
			
		||||
		},
 | 
			
		||||
		new(xscommon.StepExport),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if self.config.ISOName == "" {
 | 
			
		||||
		steps = append(download_steps, steps...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	self.runner = &multistep.BasicRunner{Steps: steps}
 | 
			
		||||
	self.runner.Run(ctx, state)
 | 
			
		||||
 | 
			
		||||
	if rawErr, ok := state.GetOk("error"); ok {
 | 
			
		||||
		return nil, rawErr.(error)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If we were interrupted or cancelled, then just exit.
 | 
			
		||||
	if _, ok := state.GetOk(multistep.StateCancelled); ok {
 | 
			
		||||
		return nil, errors.New("Build was cancelled.")
 | 
			
		||||
	}
 | 
			
		||||
	if _, ok := state.GetOk(multistep.StateHalted); ok {
 | 
			
		||||
		return nil, errors.New("Build was halted.")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	artifact, _ := xscommon.NewArtifact(self.config.OutputDir)
 | 
			
		||||
 | 
			
		||||
	return artifact, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										361
									
								
								builder/xenserver/iso/builder_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										361
									
								
								builder/xenserver/iso/builder_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,361 @@
 | 
			
		||||
package iso
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/packer"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func testConfig() map[string]interface{} {
 | 
			
		||||
	return map[string]interface{}{
 | 
			
		||||
		"remote_host":       "localhost",
 | 
			
		||||
		"remote_username":   "admin",
 | 
			
		||||
		"remote_password":   "admin",
 | 
			
		||||
		"vm_name":           "foo",
 | 
			
		||||
		"iso_checksum":      "foo",
 | 
			
		||||
		"iso_checksum_type": "md5",
 | 
			
		||||
		"iso_url":           "http://www.google.com/",
 | 
			
		||||
		"shutdown_command":  "yes",
 | 
			
		||||
		"ssh_username":      "foo",
 | 
			
		||||
 | 
			
		||||
		packer.BuildNameConfigKey: "foo",
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestBuilder_ImplementsBuilder(t *testing.T) {
 | 
			
		||||
	var raw interface{}
 | 
			
		||||
	raw = &Builder{}
 | 
			
		||||
	if _, ok := raw.(packer.Builder); !ok {
 | 
			
		||||
		t.Error("Builder must implement builder.")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestBuilderPrepare_Defaults(t *testing.T) {
 | 
			
		||||
	var b Builder
 | 
			
		||||
	config := testConfig()
 | 
			
		||||
	_, warns, err := b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("should not have error: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if b.config.ToolsIsoName != "xs-tools.iso" {
 | 
			
		||||
		t.Errorf("bad tools ISO name: %s", b.config.ToolsIsoName)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if b.config.CloneTemplate != "Other install media" {
 | 
			
		||||
		t.Errorf("bad clone template: %s", b.config.CloneTemplate)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if b.config.VMName == "" {
 | 
			
		||||
		t.Errorf("bad vm name: %s", b.config.VMName)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if b.config.Format != "xva" {
 | 
			
		||||
		t.Errorf("bad format: %s", b.config.Format)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if b.config.KeepVM != "never" {
 | 
			
		||||
		t.Errorf("bad keep instance: %s", b.config.KeepVM)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestBuilderPrepare_DiskSize(t *testing.T) {
 | 
			
		||||
	var b Builder
 | 
			
		||||
	config := testConfig()
 | 
			
		||||
 | 
			
		||||
	delete(config, "disk_size")
 | 
			
		||||
	_, warns, err := b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("bad err: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if b.config.DiskSize != 40000 {
 | 
			
		||||
		t.Fatalf("bad size: %d", b.config.DiskSize)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	config["disk_size"] = 60000
 | 
			
		||||
	b = Builder{}
 | 
			
		||||
	_, warns, err = b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("should not have error: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if b.config.DiskSize != 60000 {
 | 
			
		||||
		t.Fatalf("bad size: %d", b.config.DiskSize)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestBuilderPrepare_Format(t *testing.T) {
 | 
			
		||||
	var b Builder
 | 
			
		||||
	config := testConfig()
 | 
			
		||||
 | 
			
		||||
	// Bad
 | 
			
		||||
	config["format"] = "foo"
 | 
			
		||||
	_, warns, err := b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		t.Fatal("should have error")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Good
 | 
			
		||||
	config["format"] = "vdi_raw"
 | 
			
		||||
	b = Builder{}
 | 
			
		||||
	_, warns, err = b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("should not have error: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestBuilderPrepare_HTTPPort(t *testing.T) {
 | 
			
		||||
	var b Builder
 | 
			
		||||
	config := testConfig()
 | 
			
		||||
 | 
			
		||||
	// Bad
 | 
			
		||||
	config["http_port_min"] = 1000
 | 
			
		||||
	config["http_port_max"] = 500
 | 
			
		||||
	_, warns, err := b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		t.Fatal("should have error")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Bad
 | 
			
		||||
	config["http_port_min"] = -500
 | 
			
		||||
	b = Builder{}
 | 
			
		||||
	_, warns, err = b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		t.Fatal("should have error")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Good
 | 
			
		||||
	config["http_port_min"] = 500
 | 
			
		||||
	config["http_port_max"] = 1000
 | 
			
		||||
	b = Builder{}
 | 
			
		||||
	_, warns, err = b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("should not have error: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestBuilderPrepare_InvalidKey(t *testing.T) {
 | 
			
		||||
	var b Builder
 | 
			
		||||
	config := testConfig()
 | 
			
		||||
 | 
			
		||||
	// Add a random key
 | 
			
		||||
	config["i_should_not_be_valid"] = true
 | 
			
		||||
	_, warns, err := b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		t.Fatal("should have error")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestBuilderPrepare_ISOChecksum(t *testing.T) {
 | 
			
		||||
	var b Builder
 | 
			
		||||
	config := testConfig()
 | 
			
		||||
 | 
			
		||||
	// Test bad
 | 
			
		||||
	config["iso_checksum"] = ""
 | 
			
		||||
	_, warns, err := b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		t.Fatal("should have error")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Test good
 | 
			
		||||
	config["iso_checksum"] = "FOo"
 | 
			
		||||
	b = Builder{}
 | 
			
		||||
	_, warns, err = b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("should not have error: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if b.config.ISOChecksum != "foo" {
 | 
			
		||||
		t.Fatalf("should've lowercased: %s", b.config.ISOChecksum)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestBuilderPrepare_ISOChecksumType(t *testing.T) {
 | 
			
		||||
	var b Builder
 | 
			
		||||
	config := testConfig()
 | 
			
		||||
 | 
			
		||||
	// Test bad
 | 
			
		||||
	config["iso_checksum_type"] = ""
 | 
			
		||||
	_, warns, err := b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		t.Fatal("should have error")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Test good
 | 
			
		||||
	config["iso_checksum_type"] = "mD5"
 | 
			
		||||
	b = Builder{}
 | 
			
		||||
	_, warns, err = b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("should not have error: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if b.config.ISOChecksumType != "md5" {
 | 
			
		||||
		t.Fatalf("should've lowercased: %s", b.config.ISOChecksumType)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Test unknown
 | 
			
		||||
	config["iso_checksum_type"] = "fake"
 | 
			
		||||
	b = Builder{}
 | 
			
		||||
	_, warns, err = b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		t.Fatal("should have error")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Test none
 | 
			
		||||
	config["iso_checksum_type"] = "none"
 | 
			
		||||
	b = Builder{}
 | 
			
		||||
	_, warns, err = b.Prepare(config)
 | 
			
		||||
	// @todo: give warning in this case?
 | 
			
		||||
	/*
 | 
			
		||||
		if len(warns) == 0 {
 | 
			
		||||
			t.Fatalf("bad: %#v", warns)
 | 
			
		||||
		}
 | 
			
		||||
	*/
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("should not have error: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if b.config.ISOChecksumType != "none" {
 | 
			
		||||
		t.Fatalf("should've lowercased: %s", b.config.ISOChecksumType)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestBuilderPrepare_ISOUrl(t *testing.T) {
 | 
			
		||||
	var b Builder
 | 
			
		||||
	config := testConfig()
 | 
			
		||||
	delete(config, "iso_url")
 | 
			
		||||
	delete(config, "iso_urls")
 | 
			
		||||
 | 
			
		||||
	// Test both epty
 | 
			
		||||
	config["iso_url"] = ""
 | 
			
		||||
	b = Builder{}
 | 
			
		||||
	_, warns, err := b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		t.Fatal("should have error")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Test iso_url set
 | 
			
		||||
	config["iso_url"] = "http://www.packer.io"
 | 
			
		||||
	b = Builder{}
 | 
			
		||||
	_, warns, err = b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("should not have error: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expected := []string{"http://www.packer.io"}
 | 
			
		||||
	if !reflect.DeepEqual(b.config.ISOUrls, expected) {
 | 
			
		||||
		t.Fatalf("bad: %#v", b.config.ISOUrls)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Test both set
 | 
			
		||||
	config["iso_url"] = "http://www.packer.io"
 | 
			
		||||
	config["iso_urls"] = []string{"http://www.packer.io"}
 | 
			
		||||
	b = Builder{}
 | 
			
		||||
	_, warns, err = b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		t.Fatal("should have error")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Test just iso_urls set
 | 
			
		||||
	delete(config, "iso_url")
 | 
			
		||||
	config["iso_urls"] = []string{
 | 
			
		||||
		"http://www.packer.io",
 | 
			
		||||
		"http://www.hashicorp.com",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	b = Builder{}
 | 
			
		||||
	_, warns, err = b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("should not have error: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expected = []string{
 | 
			
		||||
		"http://www.packer.io",
 | 
			
		||||
		"http://www.hashicorp.com",
 | 
			
		||||
	}
 | 
			
		||||
	if !reflect.DeepEqual(b.config.ISOUrls, expected) {
 | 
			
		||||
		t.Fatalf("bad: %#v", b.config.ISOUrls)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestBuilderPrepare_KeepVM(t *testing.T) {
 | 
			
		||||
	var b Builder
 | 
			
		||||
	config := testConfig()
 | 
			
		||||
 | 
			
		||||
	// Bad
 | 
			
		||||
	config["keep_vm"] = "foo"
 | 
			
		||||
	_, warns, err := b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		t.Fatal("should have error")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Good
 | 
			
		||||
	config["keep_vm"] = "always"
 | 
			
		||||
	b = Builder{}
 | 
			
		||||
	_, warns, err = b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("should not have error: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										251
									
								
								builder/xenserver/iso/step_create_instance.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								builder/xenserver/iso/step_create_instance.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,251 @@
 | 
			
		||||
package iso
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/multistep"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/packer"
 | 
			
		||||
	xenapi "github.com/terra-farm/go-xen-api-client"
 | 
			
		||||
	xsclient "github.com/terra-farm/go-xen-api-client"
 | 
			
		||||
	xscommon "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type stepCreateInstance struct {
 | 
			
		||||
	instance *xsclient.VMRef
 | 
			
		||||
	vdi      *xsclient.VDIRef
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *stepCreateInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
 | 
			
		||||
 | 
			
		||||
	c := state.Get("client").(*xscommon.Connection)
 | 
			
		||||
	config := state.Get("config").(xscommon.Config)
 | 
			
		||||
	ui := state.Get("ui").(packer.Ui)
 | 
			
		||||
 | 
			
		||||
	ui.Say("Step: Create Instance")
 | 
			
		||||
 | 
			
		||||
	// Get the template to clone from
 | 
			
		||||
 | 
			
		||||
	vms, err := c.GetClient().VM.GetByNameLabel(c.GetSessionRef(), config.CloneTemplate)
 | 
			
		||||
 | 
			
		||||
	switch {
 | 
			
		||||
	case len(vms) == 0:
 | 
			
		||||
		ui.Error(fmt.Sprintf("Couldn't find a template with the name-label '%s'. Aborting.", config.CloneTemplate))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	case len(vms) > 1:
 | 
			
		||||
		ui.Error(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]
 | 
			
		||||
 | 
			
		||||
	// Clone that VM template
 | 
			
		||||
	instance, err := c.GetClient().VM.Clone(c.GetSessionRef(), template, config.VMName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Error cloning VM: %s", err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
	self.instance = &instance
 | 
			
		||||
 | 
			
		||||
	err = c.GetClient().VM.SetIsATemplate(c.GetSessionRef(), instance, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Error setting is_a_template=false: %s", err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = c.GetClient().VM.SetVCPUsMax(c.GetSessionRef(), instance, int(config.VCPUsMax))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Error setting VM VCPUs Max=%d: %s", config.VCPUsMax, err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = c.GetClient().VM.SetVCPUsAtStartup(c.GetSessionRef(), instance, int(config.VCPUsAtStartup))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Error setting VM VCPUs At Startup=%d: %s", config.VCPUsAtStartup, err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memory := int(config.VMMemory * 1024 * 1024)
 | 
			
		||||
	err = c.GetClient().VM.SetMemoryLimits(c.GetSessionRef(), instance, memory, memory, memory, memory)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Error setting VM memory=%d: %s", memory, err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = c.GetClient().VM.SetPlatform(c.GetSessionRef(), instance, config.PlatformArgs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Error setting VM platform: %s", err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = c.GetClient().VM.SetNameDescription(c.GetSessionRef(), instance, config.VMDescription)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Error setting VM description: %s", err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(config.VMOtherConfig) != 0 {
 | 
			
		||||
		vm_other_config, err := c.GetClient().VM.GetOtherConfig(c.GetSessionRef(), instance)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			ui.Error(fmt.Sprintf("Error getting VM other-config: %s", err.Error()))
 | 
			
		||||
			return multistep.ActionHalt
 | 
			
		||||
		}
 | 
			
		||||
		for key, value := range config.VMOtherConfig {
 | 
			
		||||
			vm_other_config[key] = value
 | 
			
		||||
		}
 | 
			
		||||
		err = c.GetClient().VM.SetOtherConfig(c.GetSessionRef(), instance, vm_other_config)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			ui.Error(fmt.Sprintf("Error setting VM other-config: %s", err.Error()))
 | 
			
		||||
			return multistep.ActionHalt
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = c.GetClient().VM.RemoveFromOtherConfig(c.GetSessionRef(), instance, "disks")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Error removing disks from VM other-config: %s", err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Create VDI for the instance
 | 
			
		||||
	sr, err := config.GetSR(c)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to get SR: %s", err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ui.Say(fmt.Sprintf("Using the following SR for the VM: %s", sr))
 | 
			
		||||
 | 
			
		||||
	vdi, err := c.GetClient().VDI.Create(c.GetSessionRef(), xenapi.VDIRecord{
 | 
			
		||||
		NameLabel:   "Packer-disk",
 | 
			
		||||
		VirtualSize: int(config.DiskSize * 1024 * 1024),
 | 
			
		||||
		Type:        "user",
 | 
			
		||||
		Sharable:    false,
 | 
			
		||||
		ReadOnly:    false,
 | 
			
		||||
		SR:          sr,
 | 
			
		||||
		OtherConfig: map[string]string{
 | 
			
		||||
			"temp": "temp",
 | 
			
		||||
		},
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to create packer disk VDI: %s", err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
	self.vdi = &vdi
 | 
			
		||||
 | 
			
		||||
	err = xscommon.ConnectVdi(c, instance, vdi, xsclient.VbdTypeDisk)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to connect packer disk VDI: %s", err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Connect Network
 | 
			
		||||
 | 
			
		||||
	var network xsclient.NetworkRef
 | 
			
		||||
 | 
			
		||||
	if len(config.NetworkNames) == 0 {
 | 
			
		||||
		// No network has be specified. Use the management interface
 | 
			
		||||
		log.Println("No network name given, attempting to use management interface")
 | 
			
		||||
		pifs, err := c.GetClient().PIF.GetAll(c.GetSessionRef())
 | 
			
		||||
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			ui.Error(fmt.Sprintf("Error getting PIFs: %s", err.Error()))
 | 
			
		||||
			return multistep.ActionHalt
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, pif := range pifs {
 | 
			
		||||
			pif_rec, err := c.GetClient().PIF.GetRecord(c.GetSessionRef(), pif)
 | 
			
		||||
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				ui.Error(fmt.Sprintf("Error getting PIF record: %s", err.Error()))
 | 
			
		||||
				return multistep.ActionHalt
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if pif_rec.Management {
 | 
			
		||||
				network = pif_rec.Network
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if string(network) == "" {
 | 
			
		||||
			ui.Error("Error: couldn't find management network. Aborting.")
 | 
			
		||||
			return multistep.ActionHalt
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		log.Printf("Creating VIF on network '%s' on VM '%s'\n", network, instance)
 | 
			
		||||
		_, err = xscommon.ConnectNetwork(c, network, instance, "0")
 | 
			
		||||
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			ui.Error(fmt.Sprintf("Failed to create VIF with error: %v", err))
 | 
			
		||||
			return multistep.ActionHalt
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	} else {
 | 
			
		||||
		log.Printf("Using provided network names: %v\n", config.NetworkNames)
 | 
			
		||||
		// Look up each network by it's name label
 | 
			
		||||
		for i, networkNameLabel := range config.NetworkNames {
 | 
			
		||||
			networks, err := c.GetClient().Network.GetByNameLabel(c.GetSessionRef(), networkNameLabel)
 | 
			
		||||
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				ui.Error(fmt.Sprintf("Error occured getting Network by name-label: %s", err.Error()))
 | 
			
		||||
				return multistep.ActionHalt
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			switch {
 | 
			
		||||
			case len(networks) == 0:
 | 
			
		||||
				ui.Error(fmt.Sprintf("Couldn't find a network with the specified name-label '%s'. Aborting.", networkNameLabel))
 | 
			
		||||
				return multistep.ActionHalt
 | 
			
		||||
			case len(networks) > 1:
 | 
			
		||||
				ui.Error(fmt.Sprintf("Found more than one network with the name '%s'. The name must be unique. Aborting.", networkNameLabel))
 | 
			
		||||
				return multistep.ActionHalt
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			//we need the VIF index string
 | 
			
		||||
			vifIndexString := fmt.Sprintf("%d", i)
 | 
			
		||||
			_, err = xscommon.ConnectNetwork(c, networks[0], instance, vifIndexString)
 | 
			
		||||
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				ui.Say(fmt.Sprintf("Failed to connect VIF with error: %v", err.Error()))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	instanceId, err := c.GetClient().VM.GetUUID(c.GetSessionRef(), instance)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to get VM UUID: %s", err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	state.Put("instance_uuid", instanceId)
 | 
			
		||||
	ui.Say(fmt.Sprintf("Created instance '%s'", instanceId))
 | 
			
		||||
 | 
			
		||||
	return multistep.ActionContinue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *stepCreateInstance) Cleanup(state multistep.StateBag) {
 | 
			
		||||
	config := state.Get("config").(xscommon.Config)
 | 
			
		||||
	if config.ShouldKeepVM(state) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ui := state.Get("ui").(packer.Ui)
 | 
			
		||||
	c := state.Get("client").(*xscommon.Connection)
 | 
			
		||||
 | 
			
		||||
	if self.instance != nil {
 | 
			
		||||
		ui.Say("Destroying VM")
 | 
			
		||||
		_ = c.GetClient().VM.HardShutdown(c.GetSessionRef(), *self.instance) // redundant, just in case
 | 
			
		||||
		err := c.GetClient().VM.Destroy(c.GetSessionRef(), *self.instance)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			ui.Error(err.Error())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if self.vdi != nil {
 | 
			
		||||
		ui.Say("Destroying VDI")
 | 
			
		||||
		err := c.GetClient().VDI.Destroy(c.GetSessionRef(), *self.vdi)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			ui.Error(err.Error())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										191
									
								
								builder/xenserver/xva/builder.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								builder/xenserver/xva/builder.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,191 @@
 | 
			
		||||
package xva
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/hcl/v2/hcldec"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/communicator"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/multistep"
 | 
			
		||||
	commonsteps "github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/packer"
 | 
			
		||||
	hconfig "github.com/hashicorp/packer-plugin-sdk/template/config"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
 | 
			
		||||
	xsclient "github.com/terra-farm/go-xen-api-client"
 | 
			
		||||
	xscommon "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Builder struct {
 | 
			
		||||
	config xscommon.Config
 | 
			
		||||
	runner multistep.Runner
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *Builder) ConfigSpec() hcldec.ObjectSpec { return self.config.FlatMapstructure().HCL2Spec() }
 | 
			
		||||
 | 
			
		||||
func (self *Builder) Prepare(raws ...interface{}) (params []string, warns []string, retErr error) {
 | 
			
		||||
 | 
			
		||||
	var errs *packer.MultiError
 | 
			
		||||
 | 
			
		||||
	err := hconfig.Decode(&self.config, &hconfig.DecodeOpts{
 | 
			
		||||
		Interpolate: true,
 | 
			
		||||
		InterpolateFilter: &interpolate.RenderFilter{
 | 
			
		||||
			Exclude: []string{
 | 
			
		||||
				"boot_command",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}, raws...)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		packer.MultiErrorAppend(errs, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	errs = packer.MultiErrorAppend(
 | 
			
		||||
		errs, self.config.CommonConfig.Prepare(self.config.GetInterpContext(), &self.config.PackerConfig)...)
 | 
			
		||||
 | 
			
		||||
	// Set default values
 | 
			
		||||
	if self.config.VCPUsMax == 0 {
 | 
			
		||||
		self.config.VCPUsMax = 1
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if self.config.VCPUsAtStartup == 0 {
 | 
			
		||||
		self.config.VCPUsAtStartup = 1
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if self.config.VCPUsAtStartup > self.config.VCPUsMax {
 | 
			
		||||
		self.config.VCPUsAtStartup = self.config.VCPUsMax
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if self.config.VMMemory == 0 {
 | 
			
		||||
		self.config.VMMemory = 1024
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Validation
 | 
			
		||||
 | 
			
		||||
	if self.config.SourcePath == "" {
 | 
			
		||||
		errs = packer.MultiErrorAppend(errs, fmt.Errorf("A source_path must be specified"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(errs.Errors) > 0 {
 | 
			
		||||
		retErr = errors.New(errs.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, nil, retErr
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
 | 
			
		||||
	//Setup XAPI client
 | 
			
		||||
	c, err := xscommon.NewXenAPIClient(self.config.HostIp, self.config.Username, self.config.Password)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ui.Say("XAPI client session established")
 | 
			
		||||
 | 
			
		||||
	c.GetClient().Host.GetAll(c.GetSessionRef())
 | 
			
		||||
 | 
			
		||||
	//Share state between the other steps using a statebag
 | 
			
		||||
	state := new(multistep.BasicStateBag)
 | 
			
		||||
	state.Put("client", c)
 | 
			
		||||
	// state.Put("config", self.config)
 | 
			
		||||
	state.Put("commonconfig", self.config.CommonConfig)
 | 
			
		||||
	state.Put("hook", hook)
 | 
			
		||||
	state.Put("ui", ui)
 | 
			
		||||
 | 
			
		||||
	httpReqChan := make(chan string, 1)
 | 
			
		||||
 | 
			
		||||
	//Build the steps
 | 
			
		||||
	steps := []multistep.Step{
 | 
			
		||||
		&xscommon.StepPrepareOutputDir{
 | 
			
		||||
			Force: self.config.PackerForce,
 | 
			
		||||
			Path:  self.config.OutputDir,
 | 
			
		||||
		},
 | 
			
		||||
		&commonsteps.StepCreateFloppy{
 | 
			
		||||
			Files: self.config.FloppyFiles,
 | 
			
		||||
		},
 | 
			
		||||
		new(xscommon.StepHTTPServer),
 | 
			
		||||
		&xscommon.StepUploadVdi{
 | 
			
		||||
			VdiNameFunc: func() string {
 | 
			
		||||
				return "Packer-floppy-disk"
 | 
			
		||||
			},
 | 
			
		||||
			ImagePathFunc: func() string {
 | 
			
		||||
				if floppyPath, ok := state.GetOk("floppy_path"); ok {
 | 
			
		||||
					return floppyPath.(string)
 | 
			
		||||
				}
 | 
			
		||||
				return ""
 | 
			
		||||
			},
 | 
			
		||||
			VdiUuidKey: "floppy_vdi_uuid",
 | 
			
		||||
		},
 | 
			
		||||
		&xscommon.StepFindVdi{
 | 
			
		||||
			VdiName:    self.config.ToolsIsoName,
 | 
			
		||||
			VdiUuidKey: "tools_vdi_uuid",
 | 
			
		||||
		},
 | 
			
		||||
		new(stepImportInstance),
 | 
			
		||||
		&xscommon.StepAttachVdi{
 | 
			
		||||
			VdiUuidKey: "floppy_vdi_uuid",
 | 
			
		||||
			VdiType:    xsclient.VbdTypeFloppy,
 | 
			
		||||
		},
 | 
			
		||||
		&xscommon.StepAttachVdi{
 | 
			
		||||
			VdiUuidKey: "tools_vdi_uuid",
 | 
			
		||||
			VdiType:    xsclient.VbdTypeCD,
 | 
			
		||||
		},
 | 
			
		||||
		new(xscommon.StepStartVmPaused),
 | 
			
		||||
		new(xscommon.StepSetVmHostSshAddress),
 | 
			
		||||
		new(xscommon.StepBootWait),
 | 
			
		||||
		&xscommon.StepTypeBootCommand{
 | 
			
		||||
			Ctx: *self.config.GetInterpContext(),
 | 
			
		||||
		},
 | 
			
		||||
		&xscommon.StepWaitForIP{
 | 
			
		||||
			Chan:    httpReqChan,
 | 
			
		||||
			Timeout: 300 * time.Minute, /*self.config.InstallTimeout*/ // @todo change this
 | 
			
		||||
		},
 | 
			
		||||
		&communicator.StepConnect{
 | 
			
		||||
			Config:    &self.config.SSHConfig.Comm,
 | 
			
		||||
			Host:      xscommon.CommHost,
 | 
			
		||||
			SSHConfig: xscommon.SSHConfigFunc(self.config.CommonConfig.SSHConfig),
 | 
			
		||||
			SSHPort:   xscommon.SSHPort,
 | 
			
		||||
		},
 | 
			
		||||
		new(commonsteps.StepProvision),
 | 
			
		||||
		new(xscommon.StepShutdown),
 | 
			
		||||
		&xscommon.StepDetachVdi{
 | 
			
		||||
			VdiUuidKey: "floppy_vdi_uuid",
 | 
			
		||||
		},
 | 
			
		||||
		&xscommon.StepDetachVdi{
 | 
			
		||||
			VdiUuidKey: "tools_vdi_uuid",
 | 
			
		||||
		},
 | 
			
		||||
		new(xscommon.StepExport),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	self.runner = &multistep.BasicRunner{Steps: steps}
 | 
			
		||||
	self.runner.Run(ctx, state)
 | 
			
		||||
 | 
			
		||||
	if rawErr, ok := state.GetOk("error"); ok {
 | 
			
		||||
		return nil, rawErr.(error)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If we were interrupted or cancelled, then just exit.
 | 
			
		||||
	if _, ok := state.GetOk(multistep.StateCancelled); ok {
 | 
			
		||||
		return nil, errors.New("Build was cancelled.")
 | 
			
		||||
	}
 | 
			
		||||
	if _, ok := state.GetOk(multistep.StateHalted); ok {
 | 
			
		||||
		return nil, errors.New("Build was halted.")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	artifact, _ := xscommon.NewArtifact(self.config.OutputDir)
 | 
			
		||||
 | 
			
		||||
	return artifact, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										189
									
								
								builder/xenserver/xva/builder_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								builder/xenserver/xva/builder_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,189 @@
 | 
			
		||||
package xva
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/packer"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func testConfig() map[string]interface{} {
 | 
			
		||||
	return map[string]interface{}{
 | 
			
		||||
		"remote_host":      "localhost",
 | 
			
		||||
		"remote_username":  "admin",
 | 
			
		||||
		"remote_password":  "admin",
 | 
			
		||||
		"vm_name":          "foo",
 | 
			
		||||
		"shutdown_command": "yes",
 | 
			
		||||
		"ssh_username":     "foo",
 | 
			
		||||
		"source_path":      ".",
 | 
			
		||||
 | 
			
		||||
		packer.BuildNameConfigKey: "foo",
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestBuilder_ImplementsBuilder(t *testing.T) {
 | 
			
		||||
	var raw interface{}
 | 
			
		||||
	raw = &Builder{}
 | 
			
		||||
	if _, ok := raw.(packer.Builder); !ok {
 | 
			
		||||
		t.Error("Builder must implement builder.")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestBuilderPrepare_Defaults(t *testing.T) {
 | 
			
		||||
	var b Builder
 | 
			
		||||
	config := testConfig()
 | 
			
		||||
	_, warns, err := b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("should not have error: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if b.config.ToolsIsoName != "xs-tools.iso" {
 | 
			
		||||
		t.Errorf("bad tools ISO name: %s", b.config.ToolsIsoName)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if b.config.VMName == "" {
 | 
			
		||||
		t.Errorf("bad vm name: %s", b.config.VMName)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if b.config.Format != "xva" {
 | 
			
		||||
		t.Errorf("bad format: %s", b.config.Format)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if b.config.KeepVM != "never" {
 | 
			
		||||
		t.Errorf("bad keep instance: %s", b.config.KeepVM)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestBuilderPrepare_Format(t *testing.T) {
 | 
			
		||||
	var b Builder
 | 
			
		||||
	config := testConfig()
 | 
			
		||||
 | 
			
		||||
	// Bad
 | 
			
		||||
	config["format"] = "foo"
 | 
			
		||||
	_, warns, err := b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		t.Fatal("should have error")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Good
 | 
			
		||||
	config["format"] = "vdi_raw"
 | 
			
		||||
	b = Builder{}
 | 
			
		||||
	_, warns, err = b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("should not have error: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestBuilderPrepare_HTTPPort(t *testing.T) {
 | 
			
		||||
	var b Builder
 | 
			
		||||
	config := testConfig()
 | 
			
		||||
 | 
			
		||||
	// Bad
 | 
			
		||||
	config["http_port_min"] = 1000
 | 
			
		||||
	config["http_port_max"] = 500
 | 
			
		||||
	_, warns, err := b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		t.Fatal("should have error")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Bad
 | 
			
		||||
	config["http_port_min"] = -500
 | 
			
		||||
	b = Builder{}
 | 
			
		||||
	_, warns, err = b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		t.Fatal("should have error")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Good
 | 
			
		||||
	config["http_port_min"] = 500
 | 
			
		||||
	config["http_port_max"] = 1000
 | 
			
		||||
	b = Builder{}
 | 
			
		||||
	_, warns, err = b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("should not have error: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestBuilderPrepare_InvalidKey(t *testing.T) {
 | 
			
		||||
	var b Builder
 | 
			
		||||
	config := testConfig()
 | 
			
		||||
 | 
			
		||||
	// Add a random key
 | 
			
		||||
	config["i_should_not_be_valid"] = true
 | 
			
		||||
	_, warns, err := b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		t.Fatal("should have error")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestBuilderPrepare_KeepVM(t *testing.T) {
 | 
			
		||||
	var b Builder
 | 
			
		||||
	config := testConfig()
 | 
			
		||||
 | 
			
		||||
	// Bad
 | 
			
		||||
	config["keep_vm"] = "foo"
 | 
			
		||||
	_, warns, err := b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		t.Fatal("should have error")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Good
 | 
			
		||||
	config["keep_vm"] = "always"
 | 
			
		||||
	b = Builder{}
 | 
			
		||||
	_, warns, err = b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("should not have error: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestBuilderPrepare_SourcePath(t *testing.T) {
 | 
			
		||||
	var b Builder
 | 
			
		||||
	config := testConfig()
 | 
			
		||||
 | 
			
		||||
	// Bad
 | 
			
		||||
	config["source_path"] = ""
 | 
			
		||||
	_, warns, err := b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		t.Fatal("should have error")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Good
 | 
			
		||||
	config["source_path"] = "."
 | 
			
		||||
	b = Builder{}
 | 
			
		||||
	_, warns, err = b.Prepare(config)
 | 
			
		||||
	if len(warns) > 0 {
 | 
			
		||||
		t.Fatalf("bad: %#v", warns)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("should not have error: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										114
									
								
								builder/xenserver/xva/step_import_instance.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								builder/xenserver/xva/step_import_instance.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,114 @@
 | 
			
		||||
package xva
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/multistep"
 | 
			
		||||
	"github.com/hashicorp/packer-plugin-sdk/packer"
 | 
			
		||||
	xsclient "github.com/terra-farm/go-xen-api-client"
 | 
			
		||||
	xscommon "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type stepImportInstance struct {
 | 
			
		||||
	instance xsclient.VMRef
 | 
			
		||||
	vdi      xsclient.VDIRef
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *stepImportInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
 | 
			
		||||
 | 
			
		||||
	c := state.Get("client").(*xscommon.Connection)
 | 
			
		||||
	config := state.Get("config").(xscommon.Config)
 | 
			
		||||
	ui := state.Get("ui").(packer.Ui)
 | 
			
		||||
 | 
			
		||||
	ui.Say("Step: Import Instance")
 | 
			
		||||
 | 
			
		||||
	// find the SR
 | 
			
		||||
	srs, err := c.GetClient().SR.GetAll(c.GetSessionRef())
 | 
			
		||||
	sr := srs[0]
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to get SR: %s", err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Open the file for reading (NB: httpUpload closes the file for us)
 | 
			
		||||
	fh, err := os.Open(config.SourcePath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to open XVA '%s': %s", config.SourcePath, err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result, err := xscommon.HTTPUpload(fmt.Sprintf("https://%s/import?session_id=%s&sr_id=%s",
 | 
			
		||||
		c.Host,
 | 
			
		||||
		c.GetSession(),
 | 
			
		||||
		sr,
 | 
			
		||||
	), fh, state)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to upload VDI: %s", err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
	if result == "" {
 | 
			
		||||
		ui.Error("XAPI did not reply with an instance reference")
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	instance := xsclient.VMRef(result)
 | 
			
		||||
 | 
			
		||||
	instanceId, err := c.GetClient().VM.GetUUID(c.GetSessionRef(), instance)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Unable to get VM UUID: %s", err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
	state.Put("instance_uuid", instanceId)
 | 
			
		||||
 | 
			
		||||
	err = c.GetClient().VM.SetVCPUsMax(c.GetSessionRef(), instance, int(config.VCPUsMax))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Error setting VM VCPUs Max=%d: %s", config.VCPUsMax, err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = c.GetClient().VM.SetVCPUsAtStartup(c.GetSessionRef(), instance, int(config.VCPUsAtStartup))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Error setting VM VCPUs At Startup=%d: %s", config.VCPUsAtStartup, err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = c.GetClient().VM.SetNameDescription(c.GetSessionRef(), instance, config.VMDescription)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ui.Error(fmt.Sprintf("Error setting VM description: %s", err.Error()))
 | 
			
		||||
		return multistep.ActionHalt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ui.Say(fmt.Sprintf("Imported instance '%s'", instanceId))
 | 
			
		||||
 | 
			
		||||
	return multistep.ActionContinue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *stepImportInstance) Cleanup(state multistep.StateBag) {
 | 
			
		||||
	/*
 | 
			
		||||
		config := state.Get("config").(config)
 | 
			
		||||
		if config.ShouldKeepVM(state) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ui := state.Get("ui").(packer.Ui)
 | 
			
		||||
 | 
			
		||||
		if self.instance != nil {
 | 
			
		||||
			ui.Say("Destroying VM")
 | 
			
		||||
			_ = self.instance.HardShutdown() // redundant, just in case
 | 
			
		||||
			err := self.instance.Destroy()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				ui.Error(err.Error())
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if self.vdi != nil {
 | 
			
		||||
			ui.Say("Destroying VDI")
 | 
			
		||||
			err := self.vdi.Destroy()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				ui.Error(err.Error())
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	*/
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user