github.com/chalford/terraform@v0.3.7-0.20150113080010-a78c69a8c81f/builtin/providers/cloudstack/resource_cloudstack_instance.go (about)

     1  package cloudstack
     2  
     3  import (
     4  	"crypto/sha1"
     5  	"encoding/base64"
     6  	"encoding/hex"
     7  	"fmt"
     8  	"log"
     9  	"strings"
    10  
    11  	"github.com/hashicorp/terraform/helper/schema"
    12  	"github.com/xanzy/go-cloudstack/cloudstack"
    13  )
    14  
    15  func resourceCloudStackInstance() *schema.Resource {
    16  	return &schema.Resource{
    17  		Create: resourceCloudStackInstanceCreate,
    18  		Read:   resourceCloudStackInstanceRead,
    19  		Update: resourceCloudStackInstanceUpdate,
    20  		Delete: resourceCloudStackInstanceDelete,
    21  
    22  		Schema: map[string]*schema.Schema{
    23  			"name": &schema.Schema{
    24  				Type:     schema.TypeString,
    25  				Required: true,
    26  				ForceNew: true,
    27  			},
    28  
    29  			"display_name": &schema.Schema{
    30  				Type:     schema.TypeString,
    31  				Optional: true,
    32  				Computed: true,
    33  			},
    34  
    35  			"service_offering": &schema.Schema{
    36  				Type:     schema.TypeString,
    37  				Required: true,
    38  			},
    39  
    40  			"network": &schema.Schema{
    41  				Type:     schema.TypeString,
    42  				Optional: true,
    43  				ForceNew: true,
    44  			},
    45  
    46  			"ipaddress": &schema.Schema{
    47  				Type:     schema.TypeString,
    48  				Optional: true,
    49  				Computed: true,
    50  				ForceNew: true,
    51  			},
    52  
    53  			"template": &schema.Schema{
    54  				Type:     schema.TypeString,
    55  				Required: true,
    56  				ForceNew: true,
    57  			},
    58  
    59  			"zone": &schema.Schema{
    60  				Type:     schema.TypeString,
    61  				Required: true,
    62  				ForceNew: true,
    63  			},
    64  
    65  			"user_data": &schema.Schema{
    66  				Type:     schema.TypeString,
    67  				Optional: true,
    68  				ForceNew: true,
    69  				StateFunc: func(v interface{}) string {
    70  					switch v.(type) {
    71  					case string:
    72  						hash := sha1.Sum([]byte(v.(string)))
    73  						return hex.EncodeToString(hash[:])
    74  					default:
    75  						return ""
    76  					}
    77  				},
    78  			},
    79  
    80  			"expunge": &schema.Schema{
    81  				Type:     schema.TypeBool,
    82  				Optional: true,
    83  				Default:  false,
    84  			},
    85  		},
    86  	}
    87  }
    88  
    89  func resourceCloudStackInstanceCreate(d *schema.ResourceData, meta interface{}) error {
    90  	cs := meta.(*cloudstack.CloudStackClient)
    91  
    92  	// Retrieve the service_offering UUID
    93  	serviceofferingid, e := retrieveUUID(cs, "service_offering", d.Get("service_offering").(string))
    94  	if e != nil {
    95  		return e.Error()
    96  	}
    97  
    98  	// Retrieve the template UUID
    99  	templateid, e := retrieveUUID(cs, "template", d.Get("template").(string))
   100  	if e != nil {
   101  		return e.Error()
   102  	}
   103  
   104  	// Retrieve the zone object
   105  	zone, _, err := cs.Zone.GetZoneByName(d.Get("zone").(string))
   106  	if err != nil {
   107  		return err
   108  	}
   109  
   110  	// Create a new parameter struct
   111  	p := cs.VirtualMachine.NewDeployVirtualMachineParams(serviceofferingid, templateid, zone.Id)
   112  
   113  	// Set the name
   114  	name := d.Get("name").(string)
   115  	p.SetName(name)
   116  
   117  	// Set the display name
   118  	if displayname, ok := d.GetOk("display_name"); ok {
   119  		p.SetDisplayname(displayname.(string))
   120  	} else {
   121  		p.SetDisplayname(name)
   122  	}
   123  
   124  	if zone.Networktype == "Advanced" {
   125  		// Retrieve the network UUID
   126  		networkid, e := retrieveUUID(cs, "network", d.Get("network").(string))
   127  		if e != nil {
   128  			return e.Error()
   129  		}
   130  		// Set the default network ID
   131  		p.SetNetworkids([]string{networkid})
   132  	}
   133  
   134  	// If there is a ipaddres supplied, add it to the parameter struct
   135  	if ipaddres, ok := d.GetOk("ipaddress"); ok {
   136  		p.SetIpaddress(ipaddres.(string))
   137  	}
   138  
   139  	// If the user data contains any info, it needs to be base64 encoded and
   140  	// added to the parameter struct
   141  	if userData, ok := d.GetOk("user_data"); ok {
   142  		ud := base64.StdEncoding.EncodeToString([]byte(userData.(string)))
   143  		if len(ud) > 2048 {
   144  			return fmt.Errorf(
   145  				"The supplied user_data contains %d bytes after encoding, "+
   146  					"this exeeds the limit of 2048 bytes", len(ud))
   147  		}
   148  		p.SetUserdata(ud)
   149  	}
   150  
   151  	// Create the new instance
   152  	r, err := cs.VirtualMachine.DeployVirtualMachine(p)
   153  	if err != nil {
   154  		return fmt.Errorf("Error creating the new instance %s: %s", name, err)
   155  	}
   156  
   157  	d.SetId(r.Id)
   158  
   159  	return resourceCloudStackInstanceRead(d, meta)
   160  }
   161  
   162  func resourceCloudStackInstanceRead(d *schema.ResourceData, meta interface{}) error {
   163  	cs := meta.(*cloudstack.CloudStackClient)
   164  
   165  	// Get the virtual machine details
   166  	vm, count, err := cs.VirtualMachine.GetVirtualMachineByID(d.Id())
   167  	if err != nil {
   168  		if count == 0 {
   169  			log.Printf("[DEBUG] Instance %s does no longer exist", d.Get("name").(string))
   170  			// Clear out all details so it's obvious the instance is gone
   171  			d.SetId("")
   172  			return nil
   173  		}
   174  
   175  		return err
   176  	}
   177  
   178  	// Update the config
   179  	d.Set("name", vm.Name)
   180  	d.Set("display_name", vm.Displayname)
   181  	d.Set("service_offering", vm.Serviceofferingname)
   182  	d.Set("network", vm.Nic[0].Networkname)
   183  	d.Set("ipaddress", vm.Nic[0].Ipaddress)
   184  	d.Set("template", vm.Templatename)
   185  	d.Set("zone", vm.Zonename)
   186  
   187  	return nil
   188  }
   189  
   190  func resourceCloudStackInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
   191  	cs := meta.(*cloudstack.CloudStackClient)
   192  	d.Partial(true)
   193  
   194  	name := d.Get("name").(string)
   195  
   196  	// Check if the display name is changed and if so, update the virtual machine
   197  	if d.HasChange("display_name") {
   198  		log.Printf("[DEBUG] Display name changed for %s, starting update", name)
   199  
   200  		// Create a new parameter struct
   201  		p := cs.VirtualMachine.NewUpdateVirtualMachineParams(d.Id())
   202  
   203  		// Set the new display name
   204  		p.SetDisplayname(d.Get("display_name").(string))
   205  
   206  		// Update the display name
   207  		_, err := cs.VirtualMachine.UpdateVirtualMachine(p)
   208  		if err != nil {
   209  			return fmt.Errorf(
   210  				"Error updating the display name for instance %s: %s", name, err)
   211  		}
   212  
   213  		d.SetPartial("display_name")
   214  	}
   215  
   216  	// Check if the service offering is changed and if so, update the offering
   217  	if d.HasChange("service_offering") {
   218  		log.Printf("[DEBUG] Service offering changed for %s, starting update", name)
   219  
   220  		// Retrieve the service_offering UUID
   221  		serviceofferingid, e := retrieveUUID(cs, "service_offering", d.Get("service_offering").(string))
   222  		if e != nil {
   223  			return e.Error()
   224  		}
   225  
   226  		// Create a new parameter struct
   227  		p := cs.VirtualMachine.NewChangeServiceForVirtualMachineParams(d.Id(), serviceofferingid)
   228  
   229  		// Before we can actually change the service offering, the virtual machine must be stopped
   230  		_, err := cs.VirtualMachine.StopVirtualMachine(cs.VirtualMachine.NewStopVirtualMachineParams(d.Id()))
   231  		if err != nil {
   232  			return fmt.Errorf(
   233  				"Error stopping instance %s before changing service offering: %s", name, err)
   234  		}
   235  		// Change the service offering
   236  		_, err = cs.VirtualMachine.ChangeServiceForVirtualMachine(p)
   237  		if err != nil {
   238  			return fmt.Errorf(
   239  				"Error changing the service offering for instance %s: %s", name, err)
   240  		}
   241  		// Start the virtual machine again
   242  		_, err = cs.VirtualMachine.StartVirtualMachine(cs.VirtualMachine.NewStartVirtualMachineParams(d.Id()))
   243  		if err != nil {
   244  			return fmt.Errorf(
   245  				"Error starting instance %s after changing service offering: %s", name, err)
   246  		}
   247  
   248  		d.SetPartial("service_offering")
   249  	}
   250  
   251  	d.Partial(false)
   252  	return resourceCloudStackInstanceRead(d, meta)
   253  }
   254  
   255  func resourceCloudStackInstanceDelete(d *schema.ResourceData, meta interface{}) error {
   256  	cs := meta.(*cloudstack.CloudStackClient)
   257  
   258  	// Create a new parameter struct
   259  	p := cs.VirtualMachine.NewDestroyVirtualMachineParams(d.Id())
   260  
   261  	if d.Get("expunge").(bool) {
   262  		p.SetExpunge(true)
   263  	}
   264  
   265  	log.Printf("[INFO] Destroying instance: %s", d.Get("name").(string))
   266  	if _, err := cs.VirtualMachine.DestroyVirtualMachine(p); err != nil {
   267  		// This is a very poor way to be told the UUID does no longer exist :(
   268  		if strings.Contains(err.Error(), fmt.Sprintf(
   269  			"Invalid parameter id value=%s due to incorrect long value format, "+
   270  				"or entity does not exist", d.Id())) {
   271  			return nil
   272  		}
   273  
   274  		return fmt.Errorf("Error destroying instance: %s", err)
   275  	}
   276  
   277  	return nil
   278  }