github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/builtin/providers/cloudstack/resource_cloudstack_template.go (about)

     1  package cloudstack
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/hashicorp/terraform/helper/schema"
    10  	"github.com/xanzy/go-cloudstack/cloudstack"
    11  )
    12  
    13  func resourceCloudStackTemplate() *schema.Resource {
    14  	return &schema.Resource{
    15  		Create: resourceCloudStackTemplateCreate,
    16  		Read:   resourceCloudStackTemplateRead,
    17  		Update: resourceCloudStackTemplateUpdate,
    18  		Delete: resourceCloudStackTemplateDelete,
    19  
    20  		Schema: map[string]*schema.Schema{
    21  			"name": &schema.Schema{
    22  				Type:     schema.TypeString,
    23  				Required: true,
    24  			},
    25  
    26  			"display_text": &schema.Schema{
    27  				Type:     schema.TypeString,
    28  				Optional: true,
    29  				Computed: true,
    30  			},
    31  
    32  			"format": &schema.Schema{
    33  				Type:     schema.TypeString,
    34  				Required: true,
    35  			},
    36  
    37  			"hypervisor": &schema.Schema{
    38  				Type:     schema.TypeString,
    39  				Required: true,
    40  				ForceNew: true,
    41  			},
    42  
    43  			"os_type": &schema.Schema{
    44  				Type:     schema.TypeString,
    45  				Required: true,
    46  			},
    47  
    48  			"url": &schema.Schema{
    49  				Type:     schema.TypeString,
    50  				Required: true,
    51  				ForceNew: true,
    52  			},
    53  
    54  			"project": &schema.Schema{
    55  				Type:     schema.TypeString,
    56  				Optional: true,
    57  				ForceNew: true,
    58  			},
    59  
    60  			"zone": &schema.Schema{
    61  				Type:     schema.TypeString,
    62  				Required: true,
    63  				ForceNew: true,
    64  			},
    65  
    66  			"is_dynamically_scalable": &schema.Schema{
    67  				Type:     schema.TypeBool,
    68  				Optional: true,
    69  				Computed: true,
    70  			},
    71  
    72  			"is_extractable": &schema.Schema{
    73  				Type:     schema.TypeBool,
    74  				Optional: true,
    75  				Computed: true,
    76  				ForceNew: true,
    77  			},
    78  
    79  			"is_featured": &schema.Schema{
    80  				Type:     schema.TypeBool,
    81  				Optional: true,
    82  				Computed: true,
    83  				ForceNew: true,
    84  			},
    85  
    86  			"is_public": &schema.Schema{
    87  				Type:     schema.TypeBool,
    88  				Optional: true,
    89  				Computed: true,
    90  			},
    91  
    92  			"password_enabled": &schema.Schema{
    93  				Type:     schema.TypeBool,
    94  				Optional: true,
    95  				Computed: true,
    96  			},
    97  
    98  			"is_ready": &schema.Schema{
    99  				Type:     schema.TypeBool,
   100  				Computed: true,
   101  			},
   102  
   103  			"is_ready_timeout": &schema.Schema{
   104  				Type:     schema.TypeInt,
   105  				Optional: true,
   106  				Default:  300,
   107  			},
   108  		},
   109  	}
   110  }
   111  
   112  func resourceCloudStackTemplateCreate(d *schema.ResourceData, meta interface{}) error {
   113  	cs := meta.(*cloudstack.CloudStackClient)
   114  
   115  	if err := verifyTemplateParams(d); err != nil {
   116  		return err
   117  	}
   118  
   119  	name := d.Get("name").(string)
   120  
   121  	// Compute/set the display text
   122  	displaytext := d.Get("display_text").(string)
   123  	if displaytext == "" {
   124  		displaytext = name
   125  	}
   126  
   127  	// Retrieve the os_type ID
   128  	ostypeid, e := retrieveID(cs, "os_type", d.Get("os_type").(string))
   129  	if e != nil {
   130  		return e.Error()
   131  	}
   132  
   133  	// Retrieve the zone ID
   134  	zoneid, e := retrieveID(cs, "zone", d.Get("zone").(string))
   135  	if e != nil {
   136  		return e.Error()
   137  	}
   138  
   139  	// Create a new parameter struct
   140  	p := cs.Template.NewRegisterTemplateParams(
   141  		displaytext,
   142  		d.Get("format").(string),
   143  		d.Get("hypervisor").(string),
   144  		name,
   145  		ostypeid,
   146  		d.Get("url").(string),
   147  		zoneid)
   148  
   149  	// Set optional parameters
   150  	if v, ok := d.GetOk("is_dynamically_scalable"); ok {
   151  		p.SetIsdynamicallyscalable(v.(bool))
   152  	}
   153  
   154  	if v, ok := d.GetOk("is_extractable"); ok {
   155  		p.SetIsextractable(v.(bool))
   156  	}
   157  
   158  	if v, ok := d.GetOk("is_featured"); ok {
   159  		p.SetIsfeatured(v.(bool))
   160  	}
   161  
   162  	if v, ok := d.GetOk("is_public"); ok {
   163  		p.SetIspublic(v.(bool))
   164  	}
   165  
   166  	if v, ok := d.GetOk("password_enabled"); ok {
   167  		p.SetPasswordenabled(v.(bool))
   168  	}
   169  
   170  	// If there is a project supplied, we retrieve and set the project id
   171  	if project, ok := d.GetOk("project"); ok {
   172  		// Retrieve the project ID
   173  		projectid, e := retrieveID(cs, "project", project.(string))
   174  		if e != nil {
   175  			return e.Error()
   176  		}
   177  		// Set the default project ID
   178  		p.SetProjectid(projectid)
   179  	}
   180  
   181  	// Create the new template
   182  	r, err := cs.Template.RegisterTemplate(p)
   183  	if err != nil {
   184  		return fmt.Errorf("Error creating template %s: %s", name, err)
   185  	}
   186  
   187  	d.SetId(r.RegisterTemplate[0].Id)
   188  
   189  	// Wait until the template is ready to use, or timeout with an error...
   190  	currentTime := time.Now().Unix()
   191  	timeout := int64(d.Get("is_ready_timeout").(int))
   192  	for {
   193  		// Start with the sleep so the register action has a few seconds
   194  		// to process the registration correctly. Without this wait
   195  		time.Sleep(10 * time.Second)
   196  
   197  		err := resourceCloudStackTemplateRead(d, meta)
   198  		if err != nil {
   199  			return err
   200  		}
   201  
   202  		if d.Get("is_ready").(bool) {
   203  			return nil
   204  		}
   205  
   206  		if time.Now().Unix()-currentTime > timeout {
   207  			return fmt.Errorf("Timeout while waiting for template to become ready")
   208  		}
   209  	}
   210  }
   211  
   212  func resourceCloudStackTemplateRead(d *schema.ResourceData, meta interface{}) error {
   213  	cs := meta.(*cloudstack.CloudStackClient)
   214  
   215  	// Get the template details
   216  	t, count, err := cs.Template.GetTemplateByID(d.Id(), "executable")
   217  	if err != nil {
   218  		if count == 0 {
   219  			log.Printf(
   220  				"[DEBUG] Template %s no longer exists", d.Get("name").(string))
   221  			d.SetId("")
   222  			return nil
   223  		}
   224  
   225  		return err
   226  	}
   227  
   228  	d.Set("name", t.Name)
   229  	d.Set("display_text", t.Displaytext)
   230  	d.Set("format", t.Format)
   231  	d.Set("hypervisor", t.Hypervisor)
   232  	d.Set("is_dynamically_scalable", t.Isdynamicallyscalable)
   233  	d.Set("is_extractable", t.Isextractable)
   234  	d.Set("is_featured", t.Isfeatured)
   235  	d.Set("is_public", t.Ispublic)
   236  	d.Set("password_enabled", t.Passwordenabled)
   237  	d.Set("is_ready", t.Isready)
   238  
   239  	setValueOrID(d, "os_type", t.Ostypename, t.Ostypeid)
   240  	setValueOrID(d, "project", t.Project, t.Projectid)
   241  	setValueOrID(d, "zone", t.Zonename, t.Zoneid)
   242  
   243  	return nil
   244  }
   245  
   246  func resourceCloudStackTemplateUpdate(d *schema.ResourceData, meta interface{}) error {
   247  	cs := meta.(*cloudstack.CloudStackClient)
   248  	name := d.Get("name").(string)
   249  
   250  	// Create a new parameter struct
   251  	p := cs.Template.NewUpdateTemplateParams(d.Id())
   252  
   253  	if d.HasChange("name") {
   254  		p.SetName(name)
   255  	}
   256  
   257  	if d.HasChange("display_text") {
   258  		p.SetDisplaytext(d.Get("display_text").(string))
   259  	}
   260  
   261  	if d.HasChange("format") {
   262  		p.SetFormat(d.Get("format").(string))
   263  	}
   264  
   265  	if d.HasChange("is_dynamically_scalable") {
   266  		p.SetIsdynamicallyscalable(d.Get("is_dynamically_scalable").(bool))
   267  	}
   268  
   269  	if d.HasChange("os_type") {
   270  		ostypeid, e := retrieveID(cs, "os_type", d.Get("os_type").(string))
   271  		if e != nil {
   272  			return e.Error()
   273  		}
   274  		p.SetOstypeid(ostypeid)
   275  	}
   276  
   277  	if d.HasChange("password_enabled") {
   278  		p.SetPasswordenabled(d.Get("password_enabled").(bool))
   279  	}
   280  
   281  	_, err := cs.Template.UpdateTemplate(p)
   282  	if err != nil {
   283  		return fmt.Errorf("Error updating template %s: %s", name, err)
   284  	}
   285  
   286  	return resourceCloudStackTemplateRead(d, meta)
   287  }
   288  
   289  func resourceCloudStackTemplateDelete(d *schema.ResourceData, meta interface{}) error {
   290  	cs := meta.(*cloudstack.CloudStackClient)
   291  
   292  	// Create a new parameter struct
   293  	p := cs.Template.NewDeleteTemplateParams(d.Id())
   294  
   295  	// Delete the template
   296  	log.Printf("[INFO] Deleting template: %s", d.Get("name").(string))
   297  	_, err := cs.Template.DeleteTemplate(p)
   298  	if err != nil {
   299  		// This is a very poor way to be told the ID does no longer exist :(
   300  		if strings.Contains(err.Error(), fmt.Sprintf(
   301  			"Invalid parameter id value=%s due to incorrect long value format, "+
   302  				"or entity does not exist", d.Id())) {
   303  			return nil
   304  		}
   305  
   306  		return fmt.Errorf("Error deleting template %s: %s", d.Get("name").(string), err)
   307  	}
   308  	return nil
   309  }
   310  
   311  func verifyTemplateParams(d *schema.ResourceData) error {
   312  	format := d.Get("format").(string)
   313  	if format != "OVA" && format != "QCOW2" && format != "RAW" && format != "VHD" && format != "VMDK" {
   314  		return fmt.Errorf(
   315  			"%s is not a valid format. Valid options are 'OVA','QCOW2', 'RAW', 'VHD' and 'VMDK'", format)
   316  	}
   317  
   318  	return nil
   319  }