2016-01-28 01:48:44 +01:00
|
|
|
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,
|
|
|
|
},
|
|
|
|
"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",
|
|
|
|
},
|
|
|
|
"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},
|
|
|
|
},
|
2016-05-04 19:06:17 +02:00
|
|
|
"packages": &schema.Schema{
|
|
|
|
Type: schema.TypeList,
|
|
|
|
Optional: true,
|
|
|
|
Elem: &schema.Schema{Type: schema.TypeMap},
|
|
|
|
},
|
2016-01-28 01:48:44 +01:00
|
|
|
|
|
|
|
// 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",
|
|
|
|
},
|
2016-05-06 19:52:37 +02:00
|
|
|
"aa_policy_id": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
|
|
|
|
// optional fields for bareMetal
|
|
|
|
"configuration_id": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
"os_type": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
},
|
2016-01-28 01:48:44 +01:00
|
|
|
|
|
|
|
// sorta computed
|
2016-04-29 18:58:25 +02:00
|
|
|
"password": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
Default: nil,
|
|
|
|
},
|
2016-01-28 01:48:44 +01:00
|
|
|
"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{
|
2016-05-06 19:52:37 +02:00
|
|
|
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),
|
2016-01-28 01:48:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
|
2016-05-04 19:06:17 +02:00
|
|
|
pkgs, err := parsePackages(d)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Failed setting packages: %v", err)
|
|
|
|
}
|
|
|
|
spec.Packages = pkgs
|
|
|
|
|
2016-05-06 19:52:37 +02:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-28 01:48:44 +01:00
|
|
|
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)
|
2016-04-29 18:58:25 +02:00
|
|
|
|
|
|
|
creds, err := client.Server.GetCredentials(d.Id())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
d.Set("password", creds.Password)
|
2016-01-28 01:48:44 +01:00
|
|
|
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")
|
2016-04-29 18:58:25 +02:00
|
|
|
creds, _ := client.Server.GetCredentials(id)
|
|
|
|
old := creds.Password
|
2016-01-28 01:48:44 +01:00
|
|
|
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)
|
2016-04-29 18:58:25 +02:00
|
|
|
return resourceCLCServerRead(d, meta)
|
2016-01-28 01:48:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|