github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/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 }