Skip to content

Commit

Permalink
F #322: add templates datasource
Browse files Browse the repository at this point in the history
  • Loading branch information
treywelsh authored and frousselet committed Jan 20, 2023
1 parent f6a6ec6 commit 40838b8
Show file tree
Hide file tree
Showing 2 changed files with 353 additions and 0 deletions.
352 changes: 352 additions & 0 deletions opennebula/data_opennebula_templates.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,352 @@
package opennebula

import (
"context"
"crypto/sha512"
"errors"
"fmt"
"sort"
"strconv"

templateSc "github.com/OpenNebula/one/src/oca/go/src/goca/schemas/template"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func dataOpennebulaTemplates() *schema.Resource {
return &schema.Resource{
ReadContext: datasourceOpennebulaTemplatesRead,

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Optional: true,
Description: "Name of the Templates",
},
"has_cpu": {
Type: schema.TypeBool,
Optional: true,
Description: "Indicate if templates have CPU defined",
},
"has_vcpu": {
Type: schema.TypeBool,
Optional: true,
Description: "Indicate if templates have VCPU defined",
},
"has_memory": {
Type: schema.TypeBool,
Optional: true,
Description: "Indicate if templates have memory defined",
},
"cpu": func() *schema.Schema {
s := cpuSchema()

s.ValidateFunc = func(v interface{}, k string) (ws []string, errs []error) {
value := v.(float64)

if value == 0 {
errs = append(errs, errors.New("cpu should be strictly greater than 0"))
}

return
}
return s
}(),
"vcpu": func() *schema.Schema {
s := vcpuSchema()

s.ValidateFunc = func(v interface{}, k string) (ws []string, errs []error) {
value := v.(int)

if value == 0 {
errs = append(errs, errors.New("vcpu should be strictly greater than 0"))
}

return
}
return s
}(),
"memory": func() *schema.Schema {
s := memorySchema()

s.ValidateFunc = func(v interface{}, k string) (ws []string, errs []error) {
value := v.(int)

if value == 0 {
errs = append(errs, errors.New("memory should be strictly greater than 0"))
}

return
}
return s
}(),
"tags": tagsSchema(),
"sort_on": {
Type: schema.TypeString,
Optional: true,
Description: "Attribute used to sort the templates list. Only attribute with integer type will work.",
},
"order": {
Type: schema.TypeString,
Optional: true,
Description: "Ordering of the sort: asc or desc",
},
"templates": {
Type: schema.TypeList,
Optional: false,
Computed: true,
Description: "List of matching templates",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"id": {
Type: schema.TypeInt,
Optional: false,
Computed: true,
Description: "Name of the Template",
},
"name": {
Type: schema.TypeString,
Optional: false,
Computed: true,
Description: "Name of the Template",
},
"cpu": func() *schema.Schema {
s := cpuSchema()
s.Optional = false
s.Computed = true
return s
}(),
"vcpu": func() *schema.Schema {
s := vcpuSchema()
s.Optional = false
s.Computed = true
return s
}(),
"memory": func() *schema.Schema {
s := memorySchema()
s.Optional = false
s.Computed = true
return s
}(),
"disk": func() *schema.Schema {
s := diskSchema()
s.Computed = true
s.Optional = false
return s
}(),
"nic": func() *schema.Schema {
s := nicSchema()
s.Computed = true
s.Optional = false
return s
}(),
"vmgroup": func() *schema.Schema {
s := vmGroupSchema()
s.Computed = true
s.Optional = false
s.MaxItems = 0
s.Description = "Virtual Machine Group to associate with during VM creation only."
return s
}(),
"register_date": {
Type: schema.TypeInt,
Optional: false,
Computed: true,
Description: "Creation date of the template.",
},
"tags": func() *schema.Schema {
s := tagsSchema()
s.Computed = true
s.Optional = false
return s
}(),
},
},
},
},
}
}

func templatesFilter(d *schema.ResourceData, meta interface{}) ([]*templateSc.Template, error) {

config := meta.(*Configuration)
controller := config.Controller

templates, err := controller.Templates().Info()
if err != nil {
return nil, err
}

// filter templates with user defined criterias
name, nameOk := d.GetOk("name")
hasCPU := d.Get("has_cpu").(bool)
hasVCPU := d.Get("has_vcpu").(bool)
hasMemory := d.Get("has_memory").(bool)
cpu, cpuOk := d.GetOk("cpu")
vcpu, vcpuOk := d.GetOk("vcpu")
memory, memoryOk := d.GetOk("memory")
tagsInterface, tagsOk := d.GetOk("tags")
tags := tagsInterface.(map[string]interface{})

match := make([]*templateSc.Template, 0, 1)
for i, template := range templates.Templates {

if nameOk && template.Name != name {
continue
}
tplCPU, err := template.Template.GetCPU()
if hasCPU && err != nil {
continue
}
if cpuOk && tplCPU != cpu.(float64) {
continue
}

tplVCPU, err := template.Template.GetVCPU()
if hasVCPU && err != nil {
continue
}
if vcpuOk && tplVCPU != vcpu.(int) {
continue
}

tplMemory, err := template.Template.GetMemory()
if hasMemory && err != nil {
continue
}
if memoryOk && tplMemory != memory.(int) {
continue
}

if tagsOk && !matchTags(template.Template.Template, tags) {
continue
}

match = append(match, &templates.Templates[i])
}

// check filtering results
if len(match) == 0 {
return nil, fmt.Errorf("no templates match the constraints")
}

return match, nil
}

func datasourceOpennebulaTemplatesRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {

var diags diag.Diagnostics

templates, err := templatesFilter(d, meta)
if err != nil {
diags = append(diags, diag.Diagnostic{
Severity: diag.Error,
Summary: "templates filtering failed",
Detail: err.Error(),
})
return diags
}

templatesMaps := make([]map[string]interface{}, 0, len(templates))
for _, template := range templates {

cpu, err := template.Template.GetCPU()
if err == nil {
d.Set("cpu", cpu)
}

vcpu, err := template.Template.GetVCPU()
if err == nil {
d.Set("vcpu", vcpu)
}

memory, err := template.Template.GetMemory()
if err == nil {
d.Set("memory", memory)
}

// builds disks list
disks := template.Template.GetDisks()
diskList := make([]interface{}, 0, len(disks))

for _, disk := range disks {
diskList = append(diskList, flattenDisk(disk))
}

// builds nics list
nics := template.Template.GetNICs()
nicList := make([]interface{}, 0, len(nics))

for _, nic := range nics {
nicList = append(nicList, flattenNIC(nic))
}

// builds VM Groups list
dynTemplate := template.Template.Template
vmgMap := make([]map[string]interface{}, 0, 1)
vmgIdStr, _ := dynTemplate.GetStrFromVec("VMGROUP", "VMGROUP_ID")
vmgid, _ := strconv.ParseInt(vmgIdStr, 10, 32)
vmgRole, _ := dynTemplate.GetStrFromVec("VMGROUP", "ROLE")

vmgMap = append(vmgMap, map[string]interface{}{
"vmgroup_id": vmgid,
"role": vmgRole,
})

// tags
tplPairs := pairsToMap(template.Template.Template)

templateMap := map[string]interface{}{
"name": template.Name,
"id": template.ID,
"cpu": cpu,
"vcpu": vcpu,
"memory": memory,
"disk": diskList,
"nic": nicList,
"vmgroup": vmgMap,
"register_date": template.RegTime,
}

if len(tplPairs) > 0 {
templateMap["tags"] = tplPairs
}

templatesMaps = append(templatesMaps, templateMap)
}

sortOnAttr := d.Get("sort_on").(string)
ordering := d.Get("order").(string)
var orderingFn func(int, int) bool
switch ordering {
case "ASC":
orderingFn = func(i, j int) bool {
return templatesMaps[i][sortOnAttr].(int) > templatesMaps[j][sortOnAttr].(int)
}
case "DESC":
orderingFn = func(i, j int) bool {
return templatesMaps[i][sortOnAttr].(int) < templatesMaps[j][sortOnAttr].(int)
}
}

// will crash if sortOnAttr is the name of an attributes with another type than integer
sort.Slice(templatesMaps, func(i, j int) bool {
return orderingFn(i, j)
})

d.SetId(fmt.Sprintf("%x", sha512.Sum512([]byte(ordering+sortOnAttr))))

err = d.Set("templates", templatesMaps)
if err != nil {
if err != nil {
diags = append(diags, diag.Diagnostic{
Severity: diag.Error,
Summary: "failed to set templates",
Detail: err.Error(),
})
return diags
}
}

return nil
}
1 change: 1 addition & 0 deletions opennebula/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ func Provider() *schema.Provider {
"opennebula_image": dataOpennebulaImage(),
"opennebula_security_group": dataOpennebulaSecurityGroup(),
"opennebula_template": dataOpennebulaTemplate(),
"opennebula_templates": dataOpennebulaTemplates(),
"opennebula_user": dataOpennebulaUser(),
"opennebula_virtual_data_center": dataOpennebulaVirtualDataCenter(),
"opennebula_virtual_network": dataOpennebulaVirtualNetwork(),
Expand Down

0 comments on commit 40838b8

Please sign in to comment.