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