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  }