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