github.phpd.cn/hashicorp/packer@v1.3.2/builder/cloudstack/step_create_template.go (about) 1 package cloudstack 2 3 import ( 4 "context" 5 "fmt" 6 "time" 7 8 "github.com/hashicorp/packer/helper/multistep" 9 "github.com/hashicorp/packer/packer" 10 "github.com/xanzy/go-cloudstack/cloudstack" 11 ) 12 13 type stepCreateTemplate struct{} 14 15 func (s *stepCreateTemplate) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { 16 client := state.Get("client").(*cloudstack.CloudStackClient) 17 config := state.Get("config").(*Config) 18 ui := state.Get("ui").(packer.Ui) 19 20 ui.Say(fmt.Sprintf("Creating template: %s", config.TemplateName)) 21 22 // Retrieve the instance ID from the previously saved state. 23 instanceID, ok := state.Get("instance_id").(string) 24 if !ok || instanceID == "" { 25 err := fmt.Errorf("Could not retrieve instance_id from state!") 26 state.Put("error", err) 27 ui.Error(err.Error()) 28 return multistep.ActionHalt 29 } 30 31 // Create a new parameter struct. 32 p := client.Template.NewCreateTemplateParams( 33 config.TemplateDisplayText, 34 config.TemplateName, 35 config.TemplateOS, 36 ) 37 38 // Configure the template according to the supplied config. 39 p.SetIsfeatured(config.TemplateFeatured) 40 p.SetIspublic(config.TemplatePublic) 41 p.SetIsdynamicallyscalable(config.TemplateScalable) 42 p.SetPasswordenabled(config.TemplatePasswordEnabled) 43 p.SetRequireshvm(config.TemplateRequiresHVM) 44 45 if config.Project != "" { 46 p.SetProjectid(config.Project) 47 } 48 49 if config.TemplateTag != "" { 50 p.SetTemplatetag(config.TemplateTag) 51 } 52 53 ui.Message("Retrieving the ROOT volume ID...") 54 volumeID, err := getRootVolumeID(client, instanceID, config.Project) 55 if err != nil { 56 state.Put("error", err) 57 ui.Error(err.Error()) 58 return multistep.ActionHalt 59 } 60 61 // Set the volume ID from which to create the template. 62 p.SetVolumeid(volumeID) 63 64 ui.Message("Creating the new template...") 65 template, err := client.Template.CreateTemplate(p) 66 if err != nil { 67 err := fmt.Errorf("Error creating the new template %s: %s", config.TemplateName, err) 68 state.Put("error", err) 69 ui.Error(err.Error()) 70 return multistep.ActionHalt 71 } 72 73 // This is kind of nasty, but it appears to be needed to prevent corrupt templates. 74 // When CloudStack says the template creation is done and you then delete the source 75 // volume shortly after, it seems to corrupt the newly created template. Giving it an 76 // additional 30 seconds to really finish up, seem to prevent that from happening. 77 time.Sleep(30 * time.Second) 78 79 ui.Message("Template has been created!") 80 81 // Store the template. 82 state.Put("template", template) 83 84 return multistep.ActionContinue 85 } 86 87 // Cleanup any resources that may have been created during the Run phase. 88 func (s *stepCreateTemplate) Cleanup(state multistep.StateBag) { 89 // Nothing to cleanup for this step. 90 } 91 92 func getRootVolumeID(client *cloudstack.CloudStackClient, instanceID, projectID string) (string, error) { 93 // Retrieve the virtual machine object. 94 p := client.Volume.NewListVolumesParams() 95 96 // Set the type and virtual machine ID 97 p.SetType("ROOT") 98 p.SetVirtualmachineid(instanceID) 99 if projectID != "" { 100 p.SetProjectid(projectID) 101 } 102 103 volumes, err := client.Volume.ListVolumes(p) 104 if err != nil { 105 return "", fmt.Errorf("Failed to retrieve ROOT volume: %s", err) 106 } 107 if volumes.Count != 1 { 108 return "", fmt.Errorf("Could not find ROOT disk of instance %s", instanceID) 109 } 110 111 return volumes.Volumes[0].Id, nil 112 }