terraform/builtin/providers/clc/resource_clc_server.go

376 lines
9.7 KiB
Go

package clc
import (
"fmt"
"log"
"strings"
clc "github.com/CenturyLinkCloud/clc-sdk"
"github.com/CenturyLinkCloud/clc-sdk/api"
"github.com/CenturyLinkCloud/clc-sdk/server"
"github.com/CenturyLinkCloud/clc-sdk/status"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceCLCServer() *schema.Resource {
return &schema.Resource{
Create: resourceCLCServerCreate,
Read: resourceCLCServerRead,
Update: resourceCLCServerUpdate,
Delete: resourceCLCServerDelete,
Schema: map[string]*schema.Schema{
"name_template": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"name": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"group_id": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"source_server_id": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"cpu": &schema.Schema{
Type: schema.TypeInt,
Required: true,
},
"memory_mb": &schema.Schema{
Type: schema.TypeInt,
Required: true,
},
// optional
"description": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "",
},
"type": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "standard",
ForceNew: true,
},
"network_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"custom_fields": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeMap},
},
"additional_disks": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeMap},
},
"packages": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeMap},
},
// optional: misc state storage. non-CLC field
"metadata": &schema.Schema{
Type: schema.TypeMap,
Optional: true,
},
// optional
"storage_type": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "standard",
},
"aa_policy_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
// optional fields for bareMetal
"configuration_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"os_type": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
// sorta computed
"password": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
Default: nil,
},
"private_ip_address": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
Default: nil,
},
"power_state": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
Default: nil,
},
// computed
"created_date": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"modified_date": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"public_ip_address": &schema.Schema{
// RO: if a public_ip is on this server, populate it
Type: schema.TypeString,
Computed: true,
},
},
}
}
func resourceCLCServerCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clc.Client)
spec := server.Server{
Name: d.Get("name_template").(string),
Password: d.Get("password").(string),
Description: d.Get("description").(string),
GroupID: d.Get("group_id").(string),
CPU: d.Get("cpu").(int),
MemoryGB: d.Get("memory_mb").(int) / 1024,
SourceServerID: d.Get("source_server_id").(string),
Type: d.Get("type").(string),
IPaddress: d.Get("private_ip_address").(string),
NetworkID: d.Get("network_id").(string),
Storagetype: d.Get("storage_type").(string),
AntiAffinityPolicyID: d.Get("aa_policy_id").(string),
}
var err error
disks, err := parseAdditionalDisks(d)
if err != nil {
return fmt.Errorf("Failed parsing disks: %v", err)
}
spec.Additionaldisks = disks
fields, err := parseCustomFields(d)
if err != nil {
return fmt.Errorf("Failed setting customfields: %v", err)
}
spec.Customfields = fields
pkgs, err := parsePackages(d)
if err != nil {
return fmt.Errorf("Failed setting packages: %v", err)
}
spec.Packages = pkgs
if spec.Type == "bareMetal" {
// additional bareMetal fields
if conf_id := d.Get("configuration_id").(string); conf_id != "" {
spec.ConfigurationID = conf_id
}
if os_type := d.Get("os_type").(string); os_type != "" {
spec.OSType = os_type
}
}
resp, err := client.Server.Create(spec)
if err != nil || !resp.IsQueued {
return fmt.Errorf("Failed creating server: %v", err)
}
// server's UUID returned under rel=self link
_, uuid := resp.Links.GetID("self")
ok, st := resp.GetStatusID()
if !ok {
return fmt.Errorf("Failed extracting status to poll on %v: %v", resp, err)
}
err = waitStatus(client, st)
if err != nil {
return err
}
s, err := client.Server.Get(uuid)
d.SetId(strings.ToUpper(s.Name))
log.Printf("[INFO] Server created. id: %v", s.Name)
return resourceCLCServerRead(d, meta)
}
func resourceCLCServerRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clc.Client)
s, err := client.Server.Get(d.Id())
if err != nil {
log.Printf("[INFO] Failed finding server: %v. Marking destroyed", d.Id())
d.SetId("")
return nil
}
if len(s.Details.IPaddresses) > 0 {
d.Set("private_ip_address", s.Details.IPaddresses[0].Internal)
if "" != s.Details.IPaddresses[0].Public {
d.Set("public_ip_address", s.Details.IPaddresses[0].Public)
}
}
d.Set("name", s.Name)
d.Set("groupId", s.GroupID)
d.Set("status", s.Status)
d.Set("power_state", s.Details.Powerstate)
d.Set("cpu", s.Details.CPU)
d.Set("memory_mb", s.Details.MemoryMB)
d.Set("disk_gb", s.Details.Storagegb)
d.Set("status", s.Status)
d.Set("storage_type", s.Storagetype)
d.Set("created_date", s.ChangeInfo.CreatedDate)
d.Set("modified_date", s.ChangeInfo.ModifiedDate)
creds, err := client.Server.GetCredentials(d.Id())
if err != nil {
return err
}
d.Set("password", creds.Password)
return nil
}
func resourceCLCServerUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clc.Client)
id := d.Id()
var err error
var edits []api.Update
var updates []api.Update
var i int
poll := make(chan *status.Response, 1)
d.Partial(true)
s, err := client.Server.Get(id)
if err != nil {
return fmt.Errorf("Failed fetching server: %v - %v", d.Id(), err)
}
// edits happen synchronously
if delta, orig := d.Get("description").(string), s.Description; delta != orig {
d.SetPartial("description")
edits = append(edits, server.UpdateDescription(delta))
}
if delta, orig := d.Get("group_id").(string), s.GroupID; delta != orig {
d.SetPartial("group_id")
edits = append(edits, server.UpdateGroup(delta))
}
if len(edits) > 0 {
err = client.Server.Edit(id, edits...)
if err != nil {
return fmt.Errorf("Failed saving edits: %v", err)
}
}
// updates are queue processed
if d.HasChange("password") {
d.SetPartial("password")
creds, _ := client.Server.GetCredentials(id)
old := creds.Password
pass := d.Get("password").(string)
updates = append(updates, server.UpdateCredentials(old, pass))
}
if i = d.Get("cpu").(int); i != s.Details.CPU {
d.SetPartial("cpu")
updates = append(updates, server.UpdateCPU(i))
}
if i = d.Get("memory_mb").(int); i != s.Details.MemoryMB {
d.SetPartial("memory_mb")
updates = append(updates, server.UpdateMemory(i/1024)) // takes GB
}
if d.HasChange("custom_fields") {
d.SetPartial("custom_fields")
fields, err := parseCustomFields(d)
if err != nil {
return fmt.Errorf("Failed setting customfields: %v", err)
}
updates = append(updates, server.UpdateCustomfields(fields))
}
if d.HasChange("additional_disks") {
d.SetPartial("additional_disks")
disks, err := parseAdditionalDisks(d)
if err != nil {
return fmt.Errorf("Failed parsing disks: %v", err)
}
updates = append(updates, server.UpdateAdditionaldisks(disks))
}
if len(updates) > 0 {
resp, err := client.Server.Update(id, updates...)
if err != nil {
return fmt.Errorf("Failed saving updates: %v", err)
}
err = client.Status.Poll(resp.ID, poll)
if err != nil {
return err
}
status := <-poll
if status.Failed() {
return fmt.Errorf("Update failed")
}
log.Printf("[INFO] Server updated! status: %v", status.Status)
}
if d.HasChange("power_state") {
st := d.Get("power_state").(string)
log.Printf("[DEBUG] POWER: %v => %v", s.Details.Powerstate, st)
newst := stateFromString(st)
servers, err := client.Server.PowerState(newst, s.Name)
if err != nil {
return fmt.Errorf("Failed setting power state to: %v", newst)
}
ok, id := servers[0].GetStatusID()
if !ok {
return fmt.Errorf("Failed extracting power state queue status from: %v", servers[0])
}
err = client.Status.Poll(id, poll)
if err != nil {
return err
}
status := <-poll
if status.Failed() {
return fmt.Errorf("Update failed")
}
log.Printf("[INFO] state updated: %v", status)
}
d.Partial(false)
return resourceCLCServerRead(d, meta)
}
func resourceCLCServerDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clc.Client)
id := d.Id()
resp, err := client.Server.Delete(id)
if err != nil || !resp.IsQueued {
return fmt.Errorf("Failed queueing delete of %v - %v", id, err)
}
ok, st := resp.GetStatusID()
if !ok {
return fmt.Errorf("Failed extracting status to poll on %v: %v", resp, err)
}
err = waitStatus(client, st)
if err != nil {
return err
}
log.Printf("[INFO] Server sucessfully deleted: %v", st)
return nil
}