diff --git a/builder/xenserver/common/common_config.go b/builder/xenserver/common/common_config.go index 71ea9b67..dc2639ba 100644 --- a/builder/xenserver/common/common_config.go +++ b/builder/xenserver/common/common_config.go @@ -50,10 +50,11 @@ type CommonConfig struct { 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"` + DiscDrives int `mapstructure:"disc_drives"` + 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 { @@ -183,6 +184,10 @@ func (c *CommonConfig) Prepare(ctx *interpolate.Context, pc *common.PackerConfig errs = append(errs, fmt.Errorf("Failed to parse ssh_wait_timeout: %s", err)) } + if c.DiscDrives < 0 { + errs = append(errs, errors.New("disc_drives greater than or equal to 0.")) + } + switch c.Format { case "xva", "xva_compressed", "vdi_raw", "vdi_vhd", "none": default: diff --git a/builder/xenserver/common/step_configure_disc_drives.go b/builder/xenserver/common/step_configure_disc_drives.go new file mode 100644 index 00000000..1a3350dc --- /dev/null +++ b/builder/xenserver/common/step_configure_disc_drives.go @@ -0,0 +1,116 @@ +package common + +import ( + "fmt" + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/packer" + "github.com/nilshell/xmlrpc" + xsclient "github.com/xenserver/go-xenserver-client" + "strings" +) + +type StepConfigureDiscDrives struct{} + +func (self *StepConfigureDiscDrives) Run(state multistep.StateBag) multistep.StepAction { + ui := state.Get("ui").(packer.Ui) + config := state.Get("commonconfig").(CommonConfig) + client := state.Get("client").(xsclient.XenAPIClient) + ui.Say("Step: Configure disc drives") + + uuid := state.Get("instance_uuid").(string) + instance, err := client.GetVMByUuid(uuid) + if err != nil { + ui.Error(fmt.Sprintf("Unable to get VM from UUID '%s': %s", uuid, err.Error())) + return multistep.ActionHalt + } + + vbds, err := instance.GetVBDs() + if err != nil { + ui.Error(fmt.Sprintf("Error getting VBDs: %s", err.Error())) + return multistep.ActionHalt + } + + var current_number_of_disc_drives int = 0 + for _, vbd := range vbds { + vbd_rec, err := vbd.GetRecord() + if err != nil { + ui.Error(fmt.Sprintf("Error getting VBD record: %s", err.Error())) + return multistep.ActionHalt + } + if vbd_rec["type"].(string) == "CD" { + if current_number_of_disc_drives < config.DiscDrives { + ui.Say("Ejecting disc drive") + err = vbd.Eject() + if err != nil && !strings.Contains(err.Error(), "VBD_IS_EMPTY") { + ui.Error(fmt.Sprintf("Error ejecting VBD: %s", err.Error())) + return multistep.ActionHalt + } + current_number_of_disc_drives++ + } else { + ui.Say("Destroying excess disc drive") + _ = vbd.Eject() + _ = vbd.Unplug() + err = vbd.Destroy() + if err != nil { + ui.Error(fmt.Sprintf("Error destroying VBD: %s", err.Error())) + return multistep.ActionHalt + } + } + } + } + + if current_number_of_disc_drives < config.DiscDrives { + vbd_rec := make(xmlrpc.Struct) + vbd_rec["VM"] = instance.Ref + vbd_rec["VDI"] = "OpaqueRef:NULL" + vbd_rec["userdevice"] = "autodetect" + vbd_rec["empty"] = true + vbd_rec["other_config"] = make(xmlrpc.Struct) + vbd_rec["qos_algorithm_type"] = "" + vbd_rec["qos_algorithm_params"] = make(xmlrpc.Struct) + vbd_rec["mode"] = "RO" + vbd_rec["bootable"] = true + vbd_rec["unpluggable"] = false + vbd_rec["type"] = "CD" + var failures int = 0 + for current_number_of_disc_drives < config.DiscDrives { + ui.Say("Creating disc drive") + + result := xsclient.APIResult{} + err := client.APICall(&result, "VBD.create", vbd_rec) + + if err != nil { + failures++ + if failures < 3 { + ui.Error("Error creating disc drive. Retrying...") + continue + } else { + ui.Error("Failed to create disc drive after 3 tries.") + return multistep.ActionHalt + } + } + + vbd_ref := result.Value.(string) + + result = xsclient.APIResult{} + err = client.APICall(&result, "VBD.get_uuid", vbd_ref) + + if err != nil { + failures++ + if failures < 3 { + ui.Error("Error verifying disc drive. Retrying...") + continue + } else { + ui.Error("Failed to create disc drive after 3 tries.") + return multistep.ActionHalt + } + } + + current_number_of_disc_drives++ + } + } + + return multistep.ActionContinue +} + +func (self *StepConfigureDiscDrives) Cleanup(state multistep.StateBag) {} diff --git a/builder/xenserver/iso/builder.go b/builder/xenserver/iso/builder.go index 03f26cf7..05d84762 100644 --- a/builder/xenserver/iso/builder.go +++ b/builder/xenserver/iso/builder.go @@ -317,6 +317,7 @@ func (self *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (pa &xscommon.StepDetachVdi{ VdiUuidKey: "floppy_vdi_uuid", }, + new(xscommon.StepConfigureDiscDrives), new(xscommon.StepExport), } diff --git a/builder/xenserver/xva/builder.go b/builder/xenserver/xva/builder.go index 22be43c0..bd0bd10b 100644 --- a/builder/xenserver/xva/builder.go +++ b/builder/xenserver/xva/builder.go @@ -180,6 +180,7 @@ func (self *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (pa &xscommon.StepDetachVdi{ VdiUuidKey: "tools_vdi_uuid", }, + new(xscommon.StepConfigureDiscDrives), new(xscommon.StepExport), } diff --git a/docs/builders/xenserver-iso.html.markdown b/docs/builders/xenserver-iso.html.markdown index bc7c00e3..e3dfdf83 100644 --- a/docs/builders/xenserver-iso.html.markdown +++ b/docs/builders/xenserver-iso.html.markdown @@ -100,6 +100,9 @@ each category, the available options are alphabetized and described. run `xe template-list`. Setting the correct value hints to XenServer how to optimize the virtual hardware to work best with that operating system. +* `disc_drives` (integer) - How many DVD drives to keep on the exported VM. + Default is 0. + * `disk_size` (integer) - The size, in megabytes, of the hard disk to create for the VM. By default, this is 40000 (about 40 GB).