github.com/armen/terraform@v0.5.2-0.20150529052519-caa8117a08f1/builtin/providers/cloudstack/resource_cloudstack_instance.go (about) 1 package cloudstack 2 3 import ( 4 "crypto/sha1" 5 "encoding/base64" 6 "encoding/hex" 7 "fmt" 8 "log" 9 "strings" 10 11 "github.com/hashicorp/terraform/helper/schema" 12 "github.com/xanzy/go-cloudstack/cloudstack" 13 ) 14 15 func resourceCloudStackInstance() *schema.Resource { 16 return &schema.Resource{ 17 Create: resourceCloudStackInstanceCreate, 18 Read: resourceCloudStackInstanceRead, 19 Update: resourceCloudStackInstanceUpdate, 20 Delete: resourceCloudStackInstanceDelete, 21 22 Schema: map[string]*schema.Schema{ 23 "name": &schema.Schema{ 24 Type: schema.TypeString, 25 Required: true, 26 ForceNew: true, 27 }, 28 29 "display_name": &schema.Schema{ 30 Type: schema.TypeString, 31 Optional: true, 32 Computed: true, 33 }, 34 35 "service_offering": &schema.Schema{ 36 Type: schema.TypeString, 37 Required: true, 38 }, 39 40 "network": &schema.Schema{ 41 Type: schema.TypeString, 42 Optional: true, 43 ForceNew: true, 44 }, 45 46 "ipaddress": &schema.Schema{ 47 Type: schema.TypeString, 48 Optional: true, 49 Computed: true, 50 ForceNew: true, 51 }, 52 53 "template": &schema.Schema{ 54 Type: schema.TypeString, 55 Required: true, 56 ForceNew: true, 57 }, 58 59 "zone": &schema.Schema{ 60 Type: schema.TypeString, 61 Required: true, 62 ForceNew: true, 63 }, 64 65 "user_data": &schema.Schema{ 66 Type: schema.TypeString, 67 Optional: true, 68 ForceNew: true, 69 StateFunc: func(v interface{}) string { 70 switch v.(type) { 71 case string: 72 hash := sha1.Sum([]byte(v.(string))) 73 return hex.EncodeToString(hash[:]) 74 default: 75 return "" 76 } 77 }, 78 }, 79 80 "expunge": &schema.Schema{ 81 Type: schema.TypeBool, 82 Optional: true, 83 Default: false, 84 }, 85 }, 86 } 87 } 88 89 func resourceCloudStackInstanceCreate(d *schema.ResourceData, meta interface{}) error { 90 cs := meta.(*cloudstack.CloudStackClient) 91 92 // Retrieve the service_offering UUID 93 serviceofferingid, e := retrieveUUID(cs, "service_offering", d.Get("service_offering").(string)) 94 if e != nil { 95 return e.Error() 96 } 97 98 // Retrieve the zone object 99 zone, _, err := cs.Zone.GetZoneByName(d.Get("zone").(string)) 100 if err != nil { 101 return err 102 } 103 104 // Retrieve the template UUID 105 templateid, e := retrieveTemplateUUID(cs, zone.Id, d.Get("template").(string)) 106 if e != nil { 107 return e.Error() 108 } 109 110 // Create a new parameter struct 111 p := cs.VirtualMachine.NewDeployVirtualMachineParams(serviceofferingid, templateid, zone.Id) 112 113 // Set the name 114 name := d.Get("name").(string) 115 p.SetName(name) 116 117 // Set the display name 118 if displayname, ok := d.GetOk("display_name"); ok { 119 p.SetDisplayname(displayname.(string)) 120 } else { 121 p.SetDisplayname(name) 122 } 123 124 if zone.Networktype == "Advanced" { 125 // Retrieve the network UUID 126 networkid, e := retrieveUUID(cs, "network", d.Get("network").(string)) 127 if e != nil { 128 return e.Error() 129 } 130 // Set the default network ID 131 p.SetNetworkids([]string{networkid}) 132 } 133 134 // If there is a ipaddres supplied, add it to the parameter struct 135 if ipaddres, ok := d.GetOk("ipaddress"); ok { 136 p.SetIpaddress(ipaddres.(string)) 137 } 138 139 // If the user data contains any info, it needs to be base64 encoded and 140 // added to the parameter struct 141 if userData, ok := d.GetOk("user_data"); ok { 142 ud := base64.StdEncoding.EncodeToString([]byte(userData.(string))) 143 if len(ud) > 2048 { 144 return fmt.Errorf( 145 "The supplied user_data contains %d bytes after encoding, "+ 146 "this exeeds the limit of 2048 bytes", len(ud)) 147 } 148 p.SetUserdata(ud) 149 } 150 151 // Create the new instance 152 r, err := cs.VirtualMachine.DeployVirtualMachine(p) 153 if err != nil { 154 return fmt.Errorf("Error creating the new instance %s: %s", name, err) 155 } 156 157 d.SetId(r.Id) 158 159 // Set the connection info for any configured provisioners 160 d.SetConnInfo(map[string]string{ 161 "host": r.Nic[0].Ipaddress, 162 "password": r.Password, 163 }) 164 165 return resourceCloudStackInstanceRead(d, meta) 166 } 167 168 func resourceCloudStackInstanceRead(d *schema.ResourceData, meta interface{}) error { 169 cs := meta.(*cloudstack.CloudStackClient) 170 171 // Get the virtual machine details 172 vm, count, err := cs.VirtualMachine.GetVirtualMachineByID(d.Id()) 173 if err != nil { 174 if count == 0 { 175 log.Printf("[DEBUG] Instance %s does no longer exist", d.Get("name").(string)) 176 // Clear out all details so it's obvious the instance is gone 177 d.SetId("") 178 return nil 179 } 180 181 return err 182 } 183 184 // Update the config 185 d.Set("name", vm.Name) 186 d.Set("display_name", vm.Displayname) 187 d.Set("ipaddress", vm.Nic[0].Ipaddress) 188 d.Set("zone", vm.Zonename) 189 190 setValueOrUUID(d, "network", vm.Nic[0].Networkname, vm.Nic[0].Networkid) 191 setValueOrUUID(d, "service_offering", vm.Serviceofferingname, vm.Serviceofferingid) 192 setValueOrUUID(d, "template", vm.Templatename, vm.Templateid) 193 194 return nil 195 } 196 197 func resourceCloudStackInstanceUpdate(d *schema.ResourceData, meta interface{}) error { 198 cs := meta.(*cloudstack.CloudStackClient) 199 d.Partial(true) 200 201 name := d.Get("name").(string) 202 203 // Check if the display name is changed and if so, update the virtual machine 204 if d.HasChange("display_name") { 205 log.Printf("[DEBUG] Display name changed for %s, starting update", name) 206 207 // Create a new parameter struct 208 p := cs.VirtualMachine.NewUpdateVirtualMachineParams(d.Id()) 209 210 // Set the new display name 211 p.SetDisplayname(d.Get("display_name").(string)) 212 213 // Update the display name 214 _, err := cs.VirtualMachine.UpdateVirtualMachine(p) 215 if err != nil { 216 return fmt.Errorf( 217 "Error updating the display name for instance %s: %s", name, err) 218 } 219 220 d.SetPartial("display_name") 221 } 222 223 // Check if the service offering is changed and if so, update the offering 224 if d.HasChange("service_offering") { 225 log.Printf("[DEBUG] Service offering changed for %s, starting update", name) 226 227 // Retrieve the service_offering UUID 228 serviceofferingid, e := retrieveUUID(cs, "service_offering", d.Get("service_offering").(string)) 229 if e != nil { 230 return e.Error() 231 } 232 233 // Create a new parameter struct 234 p := cs.VirtualMachine.NewChangeServiceForVirtualMachineParams(d.Id(), serviceofferingid) 235 236 // Before we can actually change the service offering, the virtual machine must be stopped 237 _, err := cs.VirtualMachine.StopVirtualMachine(cs.VirtualMachine.NewStopVirtualMachineParams(d.Id())) 238 if err != nil { 239 return fmt.Errorf( 240 "Error stopping instance %s before changing service offering: %s", name, err) 241 } 242 // Change the service offering 243 _, err = cs.VirtualMachine.ChangeServiceForVirtualMachine(p) 244 if err != nil { 245 return fmt.Errorf( 246 "Error changing the service offering for instance %s: %s", name, err) 247 } 248 // Start the virtual machine again 249 _, err = cs.VirtualMachine.StartVirtualMachine(cs.VirtualMachine.NewStartVirtualMachineParams(d.Id())) 250 if err != nil { 251 return fmt.Errorf( 252 "Error starting instance %s after changing service offering: %s", name, err) 253 } 254 255 d.SetPartial("service_offering") 256 } 257 258 d.Partial(false) 259 return resourceCloudStackInstanceRead(d, meta) 260 } 261 262 func resourceCloudStackInstanceDelete(d *schema.ResourceData, meta interface{}) error { 263 cs := meta.(*cloudstack.CloudStackClient) 264 265 // Create a new parameter struct 266 p := cs.VirtualMachine.NewDestroyVirtualMachineParams(d.Id()) 267 268 if d.Get("expunge").(bool) { 269 p.SetExpunge(true) 270 } 271 272 log.Printf("[INFO] Destroying instance: %s", d.Get("name").(string)) 273 if _, err := cs.VirtualMachine.DestroyVirtualMachine(p); err != nil { 274 // This is a very poor way to be told the UUID does no longer exist :( 275 if strings.Contains(err.Error(), fmt.Sprintf( 276 "Invalid parameter id value=%s due to incorrect long value format, "+ 277 "or entity does not exist", d.Id())) { 278 return nil 279 } 280 281 return fmt.Errorf("Error destroying instance: %s", err) 282 } 283 284 return nil 285 }