github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/clc/resource_clc_server.go (about) 1 package clc 2 3 import ( 4 "fmt" 5 "log" 6 "strings" 7 8 clc "github.com/CenturyLinkCloud/clc-sdk" 9 "github.com/CenturyLinkCloud/clc-sdk/api" 10 "github.com/CenturyLinkCloud/clc-sdk/server" 11 "github.com/CenturyLinkCloud/clc-sdk/status" 12 13 "github.com/hashicorp/terraform/helper/schema" 14 ) 15 16 func resourceCLCServer() *schema.Resource { 17 return &schema.Resource{ 18 Create: resourceCLCServerCreate, 19 Read: resourceCLCServerRead, 20 Update: resourceCLCServerUpdate, 21 Delete: resourceCLCServerDelete, 22 Schema: map[string]*schema.Schema{ 23 "name_template": &schema.Schema{ 24 Type: schema.TypeString, 25 Required: true, 26 }, 27 "name": &schema.Schema{ 28 Type: schema.TypeString, 29 Computed: true, 30 }, 31 "group_id": &schema.Schema{ 32 Type: schema.TypeString, 33 Required: true, 34 }, 35 "source_server_id": &schema.Schema{ 36 Type: schema.TypeString, 37 Required: true, 38 ForceNew: true, 39 }, 40 "cpu": &schema.Schema{ 41 Type: schema.TypeInt, 42 Required: true, 43 }, 44 "memory_mb": &schema.Schema{ 45 Type: schema.TypeInt, 46 Required: true, 47 }, 48 // optional 49 "description": &schema.Schema{ 50 Type: schema.TypeString, 51 Optional: true, 52 Default: "", 53 }, 54 "type": &schema.Schema{ 55 Type: schema.TypeString, 56 Optional: true, 57 Default: "standard", 58 ForceNew: true, 59 }, 60 "network_id": &schema.Schema{ 61 Type: schema.TypeString, 62 Optional: true, 63 }, 64 "custom_fields": &schema.Schema{ 65 Type: schema.TypeList, 66 Optional: true, 67 Elem: &schema.Schema{Type: schema.TypeMap}, 68 }, 69 "additional_disks": &schema.Schema{ 70 Type: schema.TypeList, 71 Optional: true, 72 Elem: &schema.Schema{Type: schema.TypeMap}, 73 }, 74 "packages": &schema.Schema{ 75 Type: schema.TypeList, 76 Optional: true, 77 Elem: &schema.Schema{Type: schema.TypeMap}, 78 }, 79 80 // optional: misc state storage. non-CLC field 81 "metadata": &schema.Schema{ 82 Type: schema.TypeMap, 83 Optional: true, 84 }, 85 86 // optional 87 "storage_type": &schema.Schema{ 88 Type: schema.TypeString, 89 Optional: true, 90 Default: "standard", 91 }, 92 "aa_policy_id": &schema.Schema{ 93 Type: schema.TypeString, 94 Optional: true, 95 }, 96 97 // optional fields for bareMetal 98 "configuration_id": &schema.Schema{ 99 Type: schema.TypeString, 100 Optional: true, 101 ForceNew: true, 102 }, 103 "os_type": &schema.Schema{ 104 Type: schema.TypeString, 105 Optional: true, 106 ForceNew: true, 107 }, 108 109 // sorta computed 110 "password": &schema.Schema{ 111 Type: schema.TypeString, 112 Optional: true, 113 Computed: true, 114 Default: nil, 115 }, 116 "private_ip_address": &schema.Schema{ 117 Type: schema.TypeString, 118 Optional: true, 119 Computed: true, 120 Default: nil, 121 }, 122 "power_state": &schema.Schema{ 123 Type: schema.TypeString, 124 Optional: true, 125 Computed: true, 126 Default: nil, 127 }, 128 129 // computed 130 "created_date": &schema.Schema{ 131 Type: schema.TypeString, 132 Computed: true, 133 }, 134 "modified_date": &schema.Schema{ 135 Type: schema.TypeString, 136 Computed: true, 137 }, 138 "public_ip_address": &schema.Schema{ 139 // RO: if a public_ip is on this server, populate it 140 Type: schema.TypeString, 141 Computed: true, 142 }, 143 }, 144 } 145 } 146 147 func resourceCLCServerCreate(d *schema.ResourceData, meta interface{}) error { 148 client := meta.(*clc.Client) 149 spec := server.Server{ 150 Name: d.Get("name_template").(string), 151 Password: d.Get("password").(string), 152 Description: d.Get("description").(string), 153 GroupID: d.Get("group_id").(string), 154 CPU: d.Get("cpu").(int), 155 MemoryGB: d.Get("memory_mb").(int) / 1024, 156 SourceServerID: d.Get("source_server_id").(string), 157 Type: d.Get("type").(string), 158 IPaddress: d.Get("private_ip_address").(string), 159 NetworkID: d.Get("network_id").(string), 160 Storagetype: d.Get("storage_type").(string), 161 AntiAffinityPolicyID: d.Get("aa_policy_id").(string), 162 } 163 164 var err error 165 disks, err := parseAdditionalDisks(d) 166 if err != nil { 167 return fmt.Errorf("Failed parsing disks: %v", err) 168 } 169 spec.Additionaldisks = disks 170 fields, err := parseCustomFields(d) 171 if err != nil { 172 return fmt.Errorf("Failed setting customfields: %v", err) 173 } 174 spec.Customfields = fields 175 176 pkgs, err := parsePackages(d) 177 if err != nil { 178 return fmt.Errorf("Failed setting packages: %v", err) 179 } 180 spec.Packages = pkgs 181 182 if spec.Type == "bareMetal" { 183 // additional bareMetal fields 184 if conf_id := d.Get("configuration_id").(string); conf_id != "" { 185 spec.ConfigurationID = conf_id 186 } 187 if os_type := d.Get("os_type").(string); os_type != "" { 188 spec.OSType = os_type 189 } 190 } 191 192 resp, err := client.Server.Create(spec) 193 if err != nil || !resp.IsQueued { 194 return fmt.Errorf("Failed creating server: %v", err) 195 } 196 // server's UUID returned under rel=self link 197 _, uuid := resp.Links.GetID("self") 198 199 ok, st := resp.GetStatusID() 200 if !ok { 201 return fmt.Errorf("Failed extracting status to poll on %v: %v", resp, err) 202 } 203 err = waitStatus(client, st) 204 if err != nil { 205 return err 206 } 207 208 s, err := client.Server.Get(uuid) 209 d.SetId(strings.ToUpper(s.Name)) 210 log.Printf("[INFO] Server created. id: %v", s.Name) 211 return resourceCLCServerRead(d, meta) 212 } 213 214 func resourceCLCServerRead(d *schema.ResourceData, meta interface{}) error { 215 client := meta.(*clc.Client) 216 s, err := client.Server.Get(d.Id()) 217 if err != nil { 218 log.Printf("[INFO] Failed finding server: %v. Marking destroyed", d.Id()) 219 d.SetId("") 220 return nil 221 } 222 if len(s.Details.IPaddresses) > 0 { 223 d.Set("private_ip_address", s.Details.IPaddresses[0].Internal) 224 if "" != s.Details.IPaddresses[0].Public { 225 d.Set("public_ip_address", s.Details.IPaddresses[0].Public) 226 } 227 } 228 229 d.Set("name", s.Name) 230 d.Set("groupId", s.GroupID) 231 d.Set("status", s.Status) 232 d.Set("power_state", s.Details.Powerstate) 233 d.Set("cpu", s.Details.CPU) 234 d.Set("memory_mb", s.Details.MemoryMB) 235 d.Set("disk_gb", s.Details.Storagegb) 236 d.Set("status", s.Status) 237 d.Set("storage_type", s.Storagetype) 238 d.Set("created_date", s.ChangeInfo.CreatedDate) 239 d.Set("modified_date", s.ChangeInfo.ModifiedDate) 240 241 creds, err := client.Server.GetCredentials(d.Id()) 242 if err != nil { 243 return err 244 } 245 d.Set("password", creds.Password) 246 return nil 247 } 248 249 func resourceCLCServerUpdate(d *schema.ResourceData, meta interface{}) error { 250 client := meta.(*clc.Client) 251 id := d.Id() 252 253 var err error 254 var edits []api.Update 255 var updates []api.Update 256 var i int 257 258 poll := make(chan *status.Response, 1) 259 d.Partial(true) 260 s, err := client.Server.Get(id) 261 if err != nil { 262 return fmt.Errorf("Failed fetching server: %v - %v", d.Id(), err) 263 } 264 // edits happen synchronously 265 if delta, orig := d.Get("description").(string), s.Description; delta != orig { 266 d.SetPartial("description") 267 edits = append(edits, server.UpdateDescription(delta)) 268 } 269 if delta, orig := d.Get("group_id").(string), s.GroupID; delta != orig { 270 d.SetPartial("group_id") 271 edits = append(edits, server.UpdateGroup(delta)) 272 } 273 if len(edits) > 0 { 274 err = client.Server.Edit(id, edits...) 275 if err != nil { 276 return fmt.Errorf("Failed saving edits: %v", err) 277 } 278 } 279 // updates are queue processed 280 if d.HasChange("password") { 281 d.SetPartial("password") 282 creds, _ := client.Server.GetCredentials(id) 283 old := creds.Password 284 pass := d.Get("password").(string) 285 updates = append(updates, server.UpdateCredentials(old, pass)) 286 } 287 if i = d.Get("cpu").(int); i != s.Details.CPU { 288 d.SetPartial("cpu") 289 updates = append(updates, server.UpdateCPU(i)) 290 } 291 if i = d.Get("memory_mb").(int); i != s.Details.MemoryMB { 292 d.SetPartial("memory_mb") 293 updates = append(updates, server.UpdateMemory(i/1024)) // takes GB 294 } 295 296 if d.HasChange("custom_fields") { 297 d.SetPartial("custom_fields") 298 fields, err := parseCustomFields(d) 299 if err != nil { 300 return fmt.Errorf("Failed setting customfields: %v", err) 301 } 302 updates = append(updates, server.UpdateCustomfields(fields)) 303 } 304 if d.HasChange("additional_disks") { 305 d.SetPartial("additional_disks") 306 disks, err := parseAdditionalDisks(d) 307 if err != nil { 308 return fmt.Errorf("Failed parsing disks: %v", err) 309 } 310 updates = append(updates, server.UpdateAdditionaldisks(disks)) 311 } 312 313 if len(updates) > 0 { 314 resp, err := client.Server.Update(id, updates...) 315 if err != nil { 316 return fmt.Errorf("Failed saving updates: %v", err) 317 } 318 319 err = client.Status.Poll(resp.ID, poll) 320 if err != nil { 321 return err 322 } 323 status := <-poll 324 if status.Failed() { 325 return fmt.Errorf("Update failed") 326 } 327 log.Printf("[INFO] Server updated! status: %v", status.Status) 328 } 329 330 if d.HasChange("power_state") { 331 st := d.Get("power_state").(string) 332 log.Printf("[DEBUG] POWER: %v => %v", s.Details.Powerstate, st) 333 newst := stateFromString(st) 334 servers, err := client.Server.PowerState(newst, s.Name) 335 if err != nil { 336 return fmt.Errorf("Failed setting power state to: %v", newst) 337 } 338 ok, id := servers[0].GetStatusID() 339 if !ok { 340 return fmt.Errorf("Failed extracting power state queue status from: %v", servers[0]) 341 } 342 err = client.Status.Poll(id, poll) 343 if err != nil { 344 return err 345 } 346 status := <-poll 347 if status.Failed() { 348 return fmt.Errorf("Update failed") 349 } 350 log.Printf("[INFO] state updated: %v", status) 351 } 352 353 d.Partial(false) 354 return resourceCLCServerRead(d, meta) 355 } 356 357 func resourceCLCServerDelete(d *schema.ResourceData, meta interface{}) error { 358 client := meta.(*clc.Client) 359 id := d.Id() 360 resp, err := client.Server.Delete(id) 361 if err != nil || !resp.IsQueued { 362 return fmt.Errorf("Failed queueing delete of %v - %v", id, err) 363 } 364 365 ok, st := resp.GetStatusID() 366 if !ok { 367 return fmt.Errorf("Failed extracting status to poll on %v: %v", resp, err) 368 } 369 err = waitStatus(client, st) 370 if err != nil { 371 return err 372 } 373 log.Printf("[INFO] Server sucessfully deleted: %v", st) 374 return nil 375 }