github.com/pmcatominey/terraform@v0.7.0-rc2.0.20160708105029-1401a52a5cc5/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 err := setProjectid(p, cs, d); err != nil { 172 return err 173 } 174 175 // Create the new template 176 r, err := cs.Template.RegisterTemplate(p) 177 if err != nil { 178 return fmt.Errorf("Error creating template %s: %s", name, err) 179 } 180 181 d.SetId(r.RegisterTemplate[0].Id) 182 183 // Wait until the template is ready to use, or timeout with an error... 184 currentTime := time.Now().Unix() 185 timeout := int64(d.Get("is_ready_timeout").(int)) 186 for { 187 // Start with the sleep so the register action has a few seconds 188 // to process the registration correctly. Without this wait 189 time.Sleep(10 * time.Second) 190 191 err := resourceCloudStackTemplateRead(d, meta) 192 if err != nil { 193 return err 194 } 195 196 if d.Get("is_ready").(bool) { 197 return nil 198 } 199 200 if time.Now().Unix()-currentTime > timeout { 201 return fmt.Errorf("Timeout while waiting for template to become ready") 202 } 203 } 204 } 205 206 func resourceCloudStackTemplateRead(d *schema.ResourceData, meta interface{}) error { 207 cs := meta.(*cloudstack.CloudStackClient) 208 209 // Get the template details 210 t, count, err := cs.Template.GetTemplateByID( 211 d.Id(), 212 "executable", 213 cloudstack.WithProject(d.Get("project").(string)), 214 ) 215 if err != nil { 216 if count == 0 { 217 log.Printf( 218 "[DEBUG] Template %s no longer exists", d.Get("name").(string)) 219 d.SetId("") 220 return nil 221 } 222 223 return err 224 } 225 226 d.Set("name", t.Name) 227 d.Set("display_text", t.Displaytext) 228 d.Set("format", t.Format) 229 d.Set("hypervisor", t.Hypervisor) 230 d.Set("is_dynamically_scalable", t.Isdynamicallyscalable) 231 d.Set("is_extractable", t.Isextractable) 232 d.Set("is_featured", t.Isfeatured) 233 d.Set("is_public", t.Ispublic) 234 d.Set("password_enabled", t.Passwordenabled) 235 d.Set("is_ready", t.Isready) 236 237 setValueOrID(d, "os_type", t.Ostypename, t.Ostypeid) 238 setValueOrID(d, "project", t.Project, t.Projectid) 239 setValueOrID(d, "zone", t.Zonename, t.Zoneid) 240 241 return nil 242 } 243 244 func resourceCloudStackTemplateUpdate(d *schema.ResourceData, meta interface{}) error { 245 cs := meta.(*cloudstack.CloudStackClient) 246 name := d.Get("name").(string) 247 248 // Create a new parameter struct 249 p := cs.Template.NewUpdateTemplateParams(d.Id()) 250 251 if d.HasChange("name") { 252 p.SetName(name) 253 } 254 255 if d.HasChange("display_text") { 256 p.SetDisplaytext(d.Get("display_text").(string)) 257 } 258 259 if d.HasChange("format") { 260 p.SetFormat(d.Get("format").(string)) 261 } 262 263 if d.HasChange("is_dynamically_scalable") { 264 p.SetIsdynamicallyscalable(d.Get("is_dynamically_scalable").(bool)) 265 } 266 267 if d.HasChange("os_type") { 268 ostypeid, e := retrieveID(cs, "os_type", d.Get("os_type").(string)) 269 if e != nil { 270 return e.Error() 271 } 272 p.SetOstypeid(ostypeid) 273 } 274 275 if d.HasChange("password_enabled") { 276 p.SetPasswordenabled(d.Get("password_enabled").(bool)) 277 } 278 279 _, err := cs.Template.UpdateTemplate(p) 280 if err != nil { 281 return fmt.Errorf("Error updating template %s: %s", name, err) 282 } 283 284 return resourceCloudStackTemplateRead(d, meta) 285 } 286 287 func resourceCloudStackTemplateDelete(d *schema.ResourceData, meta interface{}) error { 288 cs := meta.(*cloudstack.CloudStackClient) 289 290 // Create a new parameter struct 291 p := cs.Template.NewDeleteTemplateParams(d.Id()) 292 293 // Delete the template 294 log.Printf("[INFO] Deleting template: %s", d.Get("name").(string)) 295 _, err := cs.Template.DeleteTemplate(p) 296 if err != nil { 297 // This is a very poor way to be told the ID does no longer exist :( 298 if strings.Contains(err.Error(), fmt.Sprintf( 299 "Invalid parameter id value=%s due to incorrect long value format, "+ 300 "or entity does not exist", d.Id())) { 301 return nil 302 } 303 304 return fmt.Errorf("Error deleting template %s: %s", d.Get("name").(string), err) 305 } 306 return nil 307 } 308 309 func verifyTemplateParams(d *schema.ResourceData) error { 310 format := d.Get("format").(string) 311 if format != "OVA" && format != "QCOW2" && format != "RAW" && format != "VHD" && format != "VMDK" { 312 return fmt.Errorf( 313 "%s is not a valid format. Valid options are 'OVA','QCOW2', 'RAW', 'VHD' and 'VMDK'", format) 314 } 315 316 return nil 317 }