github.com/chalford/terraform@v0.3.7-0.20150113080010-a78c69a8c81f/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 template UUID 99 templateid, e := retrieveUUID(cs, "template", d.Get("template").(string)) 100 if e != nil { 101 return e.Error() 102 } 103 104 // Retrieve the zone object 105 zone, _, err := cs.Zone.GetZoneByName(d.Get("zone").(string)) 106 if err != nil { 107 return err 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 return resourceCloudStackInstanceRead(d, meta) 160 } 161 162 func resourceCloudStackInstanceRead(d *schema.ResourceData, meta interface{}) error { 163 cs := meta.(*cloudstack.CloudStackClient) 164 165 // Get the virtual machine details 166 vm, count, err := cs.VirtualMachine.GetVirtualMachineByID(d.Id()) 167 if err != nil { 168 if count == 0 { 169 log.Printf("[DEBUG] Instance %s does no longer exist", d.Get("name").(string)) 170 // Clear out all details so it's obvious the instance is gone 171 d.SetId("") 172 return nil 173 } 174 175 return err 176 } 177 178 // Update the config 179 d.Set("name", vm.Name) 180 d.Set("display_name", vm.Displayname) 181 d.Set("service_offering", vm.Serviceofferingname) 182 d.Set("network", vm.Nic[0].Networkname) 183 d.Set("ipaddress", vm.Nic[0].Ipaddress) 184 d.Set("template", vm.Templatename) 185 d.Set("zone", vm.Zonename) 186 187 return nil 188 } 189 190 func resourceCloudStackInstanceUpdate(d *schema.ResourceData, meta interface{}) error { 191 cs := meta.(*cloudstack.CloudStackClient) 192 d.Partial(true) 193 194 name := d.Get("name").(string) 195 196 // Check if the display name is changed and if so, update the virtual machine 197 if d.HasChange("display_name") { 198 log.Printf("[DEBUG] Display name changed for %s, starting update", name) 199 200 // Create a new parameter struct 201 p := cs.VirtualMachine.NewUpdateVirtualMachineParams(d.Id()) 202 203 // Set the new display name 204 p.SetDisplayname(d.Get("display_name").(string)) 205 206 // Update the display name 207 _, err := cs.VirtualMachine.UpdateVirtualMachine(p) 208 if err != nil { 209 return fmt.Errorf( 210 "Error updating the display name for instance %s: %s", name, err) 211 } 212 213 d.SetPartial("display_name") 214 } 215 216 // Check if the service offering is changed and if so, update the offering 217 if d.HasChange("service_offering") { 218 log.Printf("[DEBUG] Service offering changed for %s, starting update", name) 219 220 // Retrieve the service_offering UUID 221 serviceofferingid, e := retrieveUUID(cs, "service_offering", d.Get("service_offering").(string)) 222 if e != nil { 223 return e.Error() 224 } 225 226 // Create a new parameter struct 227 p := cs.VirtualMachine.NewChangeServiceForVirtualMachineParams(d.Id(), serviceofferingid) 228 229 // Before we can actually change the service offering, the virtual machine must be stopped 230 _, err := cs.VirtualMachine.StopVirtualMachine(cs.VirtualMachine.NewStopVirtualMachineParams(d.Id())) 231 if err != nil { 232 return fmt.Errorf( 233 "Error stopping instance %s before changing service offering: %s", name, err) 234 } 235 // Change the service offering 236 _, err = cs.VirtualMachine.ChangeServiceForVirtualMachine(p) 237 if err != nil { 238 return fmt.Errorf( 239 "Error changing the service offering for instance %s: %s", name, err) 240 } 241 // Start the virtual machine again 242 _, err = cs.VirtualMachine.StartVirtualMachine(cs.VirtualMachine.NewStartVirtualMachineParams(d.Id())) 243 if err != nil { 244 return fmt.Errorf( 245 "Error starting instance %s after changing service offering: %s", name, err) 246 } 247 248 d.SetPartial("service_offering") 249 } 250 251 d.Partial(false) 252 return resourceCloudStackInstanceRead(d, meta) 253 } 254 255 func resourceCloudStackInstanceDelete(d *schema.ResourceData, meta interface{}) error { 256 cs := meta.(*cloudstack.CloudStackClient) 257 258 // Create a new parameter struct 259 p := cs.VirtualMachine.NewDestroyVirtualMachineParams(d.Id()) 260 261 if d.Get("expunge").(bool) { 262 p.SetExpunge(true) 263 } 264 265 log.Printf("[INFO] Destroying instance: %s", d.Get("name").(string)) 266 if _, err := cs.VirtualMachine.DestroyVirtualMachine(p); err != nil { 267 // This is a very poor way to be told the UUID does no longer exist :( 268 if strings.Contains(err.Error(), fmt.Sprintf( 269 "Invalid parameter id value=%s due to incorrect long value format, "+ 270 "or entity does not exist", d.Id())) { 271 return nil 272 } 273 274 return fmt.Errorf("Error destroying instance: %s", err) 275 } 276 277 return nil 278 }