package cloudstack import ( "context" "fmt" "time" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" "github.com/xanzy/go-cloudstack/cloudstack" ) type stepCreateTemplate struct{} func (s *stepCreateTemplate) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { client := state.Get("client").(*cloudstack.CloudStackClient) config := state.Get("config").(*Config) ui := state.Get("ui").(packer.Ui) ui.Say(fmt.Sprintf("Creating template: %s", config.TemplateName)) // Retrieve the instance ID from the previously saved state. instanceID, ok := state.Get("instance_id").(string) if !ok || instanceID == "" { err := fmt.Errorf("Could not retrieve instance_id from state!") state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } // Create a new parameter struct. p := client.Template.NewCreateTemplateParams( config.TemplateDisplayText, config.TemplateName, config.TemplateOS, ) // Configure the template according to the supplied config. p.SetIsfeatured(config.TemplateFeatured) p.SetIspublic(config.TemplatePublic) p.SetIsdynamicallyscalable(config.TemplateScalable) p.SetPasswordenabled(config.TemplatePasswordEnabled) p.SetRequireshvm(config.TemplateRequiresHVM) if config.Project != "" { p.SetProjectid(config.Project) } if config.TemplateTag != "" { p.SetTemplatetag(config.TemplateTag) } ui.Message("Retrieving the ROOT volume ID...") volumeID, err := getRootVolumeID(client, instanceID) if err != nil { state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } // Set the volume ID from which to create the template. p.SetVolumeid(volumeID) ui.Message("Creating the new template...") template, err := client.Template.CreateTemplate(p) if err != nil { err := fmt.Errorf("Error creating the new template %s: %s", config.TemplateName, err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } // This is kind of nasty, but it appears to be needed to prevent corrupt templates. // When CloudStack says the template creation is done and you then delete the source // volume shortly after, it seems to corrupt the newly created template. Giving it an // additional 30 seconds to really finish up, seem to prevent that from happening. time.Sleep(30 * time.Second) ui.Message("Template has been created!") // Store the template. state.Put("template", template) return multistep.ActionContinue } // Cleanup any resources that may have been created during the Run phase. func (s *stepCreateTemplate) Cleanup(state multistep.StateBag) { // Nothing to cleanup for this step. } func getRootVolumeID(client *cloudstack.CloudStackClient, instanceID string) (string, error) { // Retrieve the virtual machine object. p := client.Volume.NewListVolumesParams() // Set the type and virtual machine ID p.SetType("ROOT") p.SetVirtualmachineid(instanceID) volumes, err := client.Volume.ListVolumes(p) if err != nil { return "", fmt.Errorf("Failed to retrieve ROOT volume: %s", err) } if volumes.Count != 1 { return "", fmt.Errorf("Could not find ROOT disk of instance %s", instanceID) } return volumes.Volumes[0].Id, nil }