github.com/amanya/packer@v0.12.1-0.20161117214323-902ac5ab2eb6/builder/cloudstack/step_create_instance.go (about) 1 package cloudstack 2 3 import ( 4 "encoding/base64" 5 "fmt" 6 "strings" 7 8 "github.com/mitchellh/multistep" 9 "github.com/mitchellh/packer/packer" 10 "github.com/xanzy/go-cloudstack/cloudstack" 11 ) 12 13 // stepCreateInstance represents a Packer build step that creates CloudStack instances. 14 type stepCreateInstance struct{} 15 16 // Run executes the Packer build step that creates a CloudStack instance. 17 func (s *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction { 18 client := state.Get("client").(*cloudstack.CloudStackClient) 19 config := state.Get("config").(*Config) 20 ui := state.Get("ui").(packer.Ui) 21 22 ui.Say("Creating instance...") 23 24 // Create a new parameter struct. 25 p := client.VirtualMachine.NewDeployVirtualMachineParams( 26 config.ServiceOffering, 27 config.instanceSource, 28 config.Zone, 29 ) 30 31 // Configure the instance. 32 p.SetName(config.InstanceName) 33 p.SetDisplayname("Created by Packer") 34 35 if config.Keypair != "" { 36 p.SetKeypair(config.Keypair) 37 } 38 39 // If we use an ISO, configure the disk offering. 40 if config.SourceISO != "" { 41 p.SetDiskofferingid(config.DiskOffering) 42 p.SetHypervisor(config.Hypervisor) 43 } 44 45 // If we use a template, set the root disk size. 46 if config.SourceTemplate != "" && config.DiskSize > 0 { 47 p.SetRootdisksize(config.DiskSize) 48 } 49 50 // Retrieve the zone object. 51 zone, _, err := client.Zone.GetZoneByID(config.Zone) 52 if err != nil { 53 ui.Error(err.Error()) 54 return multistep.ActionHalt 55 } 56 57 if zone.Networktype == "Advanced" { 58 // Set the network ID's. 59 p.SetNetworkids([]string{config.Network}) 60 } 61 62 // If there is a project supplied, set the project id. 63 if config.Project != "" { 64 p.SetProjectid(config.Project) 65 } 66 67 if config.UserData != "" { 68 ud, err := getUserData(config.UserData, config.HTTPGetOnly) 69 if err != nil { 70 ui.Error(err.Error()) 71 return multistep.ActionHalt 72 } 73 74 p.SetUserdata(ud) 75 } 76 77 // Create the new instance. 78 instance, err := client.VirtualMachine.DeployVirtualMachine(p) 79 if err != nil { 80 ui.Error(fmt.Sprintf("Error creating new instance %s: %s", config.InstanceName, err)) 81 return multistep.ActionHalt 82 } 83 84 ui.Message("Instance has been created!") 85 86 // Set the auto generated password if a password was not explicitly configured. 87 switch config.Comm.Type { 88 case "ssh": 89 if config.Comm.SSHPassword == "" { 90 config.Comm.SSHPassword = instance.Password 91 } 92 case "winrm": 93 if config.Comm.WinRMPassword == "" { 94 config.Comm.WinRMPassword = instance.Password 95 } 96 } 97 98 // Set the host address when using the local IP address to connect. 99 if config.UseLocalIPAddress { 100 config.hostAddress = instance.Nic[0].Ipaddress 101 } 102 103 // Store the instance ID so we can remove it later. 104 state.Put("instance_id", instance.Id) 105 106 return multistep.ActionContinue 107 } 108 109 // Cleanup any resources that may have been created during the Run phase. 110 func (s *stepCreateInstance) Cleanup(state multistep.StateBag) { 111 client := state.Get("client").(*cloudstack.CloudStackClient) 112 ui := state.Get("ui").(packer.Ui) 113 114 instanceID, ok := state.Get("instance_id").(string) 115 if !ok || instanceID == "" { 116 return 117 } 118 119 // Create a new parameter struct. 120 p := client.VirtualMachine.NewDestroyVirtualMachineParams(instanceID) 121 122 // Set expunge so the instance is completely removed 123 p.SetExpunge(true) 124 125 ui.Say("Deleting instance...") 126 if _, err := client.VirtualMachine.DestroyVirtualMachine(p); err != nil { 127 // This is a very poor way to be told the ID does no longer exist :( 128 if strings.Contains(err.Error(), fmt.Sprintf( 129 "Invalid parameter id value=%s due to incorrect long value format, "+ 130 "or entity does not exist", instanceID)) { 131 return 132 } 133 134 ui.Error(fmt.Sprintf("Error destroying instance: %s", err)) 135 } 136 137 ui.Message("Instance has been deleted!") 138 139 return 140 } 141 142 // getUserData returns the user data as a base64 encoded string. 143 func getUserData(userData string, httpGETOnly bool) (string, error) { 144 ud := base64.StdEncoding.EncodeToString([]byte(userData)) 145 146 // deployVirtualMachine uses POST by default, so max userdata is 32K 147 maxUD := 32768 148 149 if httpGETOnly { 150 // deployVirtualMachine using GET instead, so max userdata is 2K 151 maxUD = 2048 152 } 153 154 if len(ud) > maxUD { 155 return "", fmt.Errorf( 156 "The supplied user_data contains %d bytes after encoding, "+ 157 "this exeeds the limit of %d bytes", len(ud), maxUD) 158 } 159 160 return ud, nil 161 }