diff --git a/builder/xenserver/builder.go b/builder/xenserver/builder.go index f2ff42e..8b59a70 100644 --- a/builder/xenserver/builder.go +++ b/builder/xenserver/builder.go @@ -28,9 +28,9 @@ type config struct { InstanceName string `mapstructure:"instance_name"` RootDiskSize string `mapstructure:"root_disk_size"` CloneTemplate string `mapstructure:"clone_template"` - IsoUuid string `mapstructure:"iso_uuid"` - SrUuid string `mapstructure:"sr_uuid"` - NetworkUuid string `mapstructure:"network_uuid"` + IsoName string `mapstructure:"iso_name"` + SrName string `mapstructure:"sr_name"` + NetworkName string `mapstructure:"network_name"` HostPortMin uint `mapstructure:"host_port_min"` HostPortMax uint `mapstructure:"host_port_max"` @@ -127,9 +127,9 @@ func (self *Builder) Prepare (raws ...interface{}) (params []string, retErr erro "instance_name": &self.config.InstanceName, "root_disk_size": &self.config.RootDiskSize, "clone_template": &self.config.CloneTemplate, - "iso_uuid": &self.config.IsoUuid, - "sr_uuid": &self.config.SrUuid, - "network_uuid": &self.config.NetworkUuid, + "iso_name": &self.config.IsoName, + "sr_name": &self.config.SrName, + "network_name": &self.config.NetworkName, "boot_wait": &self.config.RawBootWait, "iso_checksum": &self.config.ISOChecksum, "iso_checksum_type": &self.config.ISOChecksumType, @@ -218,29 +218,15 @@ func (self *Builder) Prepare (raws ...interface{}) (params []string, retErr erro } if self.config.CloneTemplate == "" { - errs = packer.MultiErrorAppend( - errs, errors.New("A template to clone from must be specified.")) - } - - if self.config.IsoUuid == "" { - errs = packer.MultiErrorAppend( - errs, errors.New("a uuid for the installation iso must be specified.")) - } - - if self.config.SrUuid == "" { - errs = packer.MultiErrorAppend( - errs, errors.New("a uuid for the sr used for the instance must be specified.")) - } - - if self.config.NetworkUuid == "" { - errs = packer.MultiErrorAppend( - errs, errors.New("a uuid for the network used for the instance must be specified.")) + self.config.CloneTemplate = "Other install media" } +/* if self.config.LocalIp == "" { errs = packer.MultiErrorAppend( errs, errors.New("A local IP visible to XenServer's mangement interface is required to serve files.")) } +*/ if len(self.config.PlatformArgs) == 0 { pargs := make(map[string]string) diff --git a/builder/xenserver/client.go b/builder/xenserver/client.go index 7e40b4a..64eec8e 100644 --- a/builder/xenserver/client.go +++ b/builder/xenserver/client.go @@ -60,6 +60,16 @@ type VIF struct { Client *XenAPIClient } +type PIF struct { + Ref string + Client *XenAPIClient +} + +type Pool struct { + Ref string + Client *XenAPIClient +} + func (c *XenAPIClient) RPCCall (result interface{}, method string, params []interface{}) (err error) { fmt.Println(params) p := new(xmlrpc.Params) @@ -129,6 +139,50 @@ func (client *XenAPIClient) GetHosts () (err error) { } +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{} @@ -141,6 +195,44 @@ func (client *XenAPIClient) GetVMByUuid (vm_uuid string) (vm *VM, err error) { 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) GetSRByNameLabel (name_label string) (srs []*SR, err error) { + srs = make([]*SR, 0) + result := APIResult{} + err = client.APICall(&result, "SR.get_by_name_label", name_label) + if err != nil { + return srs, err + } + + for _, elem := range result.Value.([]interface{}) { + sr := new(SR) + sr.Ref = elem.(string) + sr.Client = client + srs = append(srs, sr) + } + + return srs, nil +} + func (client *XenAPIClient) GetNetworkByUuid (network_uuid string) (network *Network, err error) { network = new(Network) result := APIResult{} @@ -172,6 +264,24 @@ func (client *XenAPIClient) GetNetworkByNameLabel (name_label string) (networks 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) GetSRByUuid (sr_uuid string) (sr *SR, err error) { sr = new(SR) @@ -197,6 +307,22 @@ func (client *XenAPIClient) GetVdiByUuid (vdi_uuid string) (vdi *VDI, err error) 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 +} // VM associated functions @@ -532,6 +658,37 @@ func (self *Network) GetAssignedIPs () (ip_map map[string]string, err error) { 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{}) diff --git a/builder/xenserver/step_create_instance.go b/builder/xenserver/step_create_instance.go index ed7993d..a07bc5c 100644 --- a/builder/xenserver/step_create_instance.go +++ b/builder/xenserver/step_create_instance.go @@ -5,6 +5,7 @@ import ( "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" "fmt" + "log" ) type stepCreateInstance struct { @@ -21,7 +22,19 @@ func (self *stepCreateInstance) Run(state multistep.StateBag) multistep.StepActi // Get the template to clone from - template, _ := client.GetVMByUuid(config.CloneTemplate) + + vms, err := client.GetVMByNameLabel(config.CloneTemplate) + + switch { + case len(vms) == 0: + log.Fatal(fmt.Sprintf("Couldn't find a template with the name-label '%s'. Aborting.", config.CloneTemplate)) + return multistep.ActionHalt + case len(vms) > 1: + log.Fatal(fmt.Sprintf("Found more than one template with the name '%s'. The name must be unique. Aborting.", config.CloneTemplate)) + return multistep.ActionHalt + } + + template := vms[0] // Clone that VM template instance, _ := template.Clone(config.InstanceName) @@ -30,13 +43,102 @@ func (self *stepCreateInstance) Run(state multistep.StateBag) multistep.StepActi instance.SetPlatform(config.PlatformArgs) // Create VDI for the instance - sr, _ := client.GetSRByUuid(config.SrUuid) + var sr *SR + + if config.SrName == "" { + // Find the default SR + default_sr, err := client.GetDefaultSR() + sr = default_sr + + if err != nil { + log.Fatal(fmt.Sprintf("Error getting default SR: %s", err.Error())) + return multistep.ActionHalt + } + + + } else { + // Use the provided name label to find the SR to use + srs, err := client.GetSRByNameLabel(config.SrName) + + if err != nil { + log.Fatal(fmt.Sprintf("Error getting default SR: %s", err.Error())) + return multistep.ActionHalt + } + + switch { + case len(srs) == 0: + log.Fatal(fmt.Sprintf("Couldn't find a SR with the specified name-label '%s'. Aborting.", config.SrName)) + return multistep.ActionHalt + case len(srs) > 1: + log.Fatal(fmt.Sprintf("Found more than one SR with the name '%s'. The name must be unique. Aborting.", config.SrName)) + return multistep.ActionHalt + } + + sr = srs[0] + } + vdi, _ := sr.CreateVdi("Packer-disk", config.RootDiskSize) instance.ConnectVdi(vdi, false) // Connect Network - network, err := client.GetNetworkByUuid(config.NetworkUuid) + + var network *Network + + if config.NetworkName == "" { + // No network has be specified. Use the management interface + network = new(Network) + network.Ref = "" + network.Client = &client + + pifs, err := client.GetPIFs() + + if err != nil { + log.Fatal(fmt.Sprintf("Error getting PIFs %s", err.Error())) + return multistep.ActionHalt + } + + for _, pif := range pifs { + pif_rec, err := pif.GetRecord() + + if err != nil { + log.Fatal(fmt.Sprintf("Error getting PIF record: %s", err.Error())) + return multistep.ActionHalt + } + + if pif_rec["management"].(bool) { + network.Ref = pif_rec["network"].(string) + } + + } + + if network.Ref == "" { + log.Fatal("Error: couldn't find management network. Aborting.") + return multistep.ActionHalt + } + + } else { + // Look up the network by it's name label + + networks, err := client.GetNetworkByNameLabel(config.NetworkName) + + if err != nil { + log.Fatal(fmt.Sprintf("Error occured getting Network by name-label: %s", err.Error())) + return multistep.ActionHalt + } + + switch { + case len(networks) == 0: + log.Fatal(fmt.Sprintf("Couldn't find a network with the specified name-label '%s'. Aborting.", config.NetworkName)) + return multistep.ActionHalt + case len(networks) > 1: + log.Fatal(fmt.Sprintf("Found more than one SR with the name '%s'. The name must be unique. Aborting.", config.NetworkName)) + return multistep.ActionHalt + } + + network = networks[0] + } + if err != nil { ui.Say(err.Error()) } @@ -49,7 +151,22 @@ func (self *stepCreateInstance) Run(state multistep.StateBag) multistep.StepActi // Connect the ISO //iso_vdi_uuid := state.Get("iso_vdi_uuid").(string) - iso, _ := client.GetVdiByUuid(config.IsoUuid) + + isos, err := client.GetVdiByNameLabel(config.IsoName) + + + switch { + case len(isos) == 0: + log.Fatal(fmt.Sprintf("Couldn't find an ISO named '%s'. Aborting", config.IsoName)) + return multistep.ActionHalt + case len(isos) > 1: + log.Fatal(fmt.Sprintf("Found more than one VDI with name '%s'. Name must be unique. Aborting.", config.IsoName)) + return multistep.ActionHalt + } + + iso := isos[0] + + //iso, _ := client.GetVdiByUuid(config.IsoUuid) //ui.Say("Using VDI: " + iso_vdi_uuid) //iso, _ := client.GetVdiByUuid(iso_vdi_uuid) instance.ConnectVdi(iso, true) diff --git a/builder/xenserver/step_upload_iso.go b/builder/xenserver/step_upload_iso.go index ff74302..27e76e0 100644 --- a/builder/xenserver/step_upload_iso.go +++ b/builder/xenserver/step_upload_iso.go @@ -37,7 +37,9 @@ func (self *stepUploadIso) Run(state multistep.StateBag) multistep.StepAction { iso_filesize := stat.Size() // Create a VDI with the write size - sr, err := client.GetSRByUuid(config.SrUuid) + srs, err := client.GetSRByNameLabel(config.SrName) + + sr := srs[0] if err != nil { ui.Error(err.Error())