diff --git a/CHANGELOG.md b/CHANGELOG.md index 9db225434..2a7c76a4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 1.5.0 (Unreleased) + +FEATURES: + +* **New Data Source**: `opennebula_virtual_machines` (#485) + # 1.4.1 (October 22nd, 2024) FEATURES: diff --git a/opennebula/data_opennebula_templates.go b/opennebula/data_opennebula_templates.go index 7f2b436c3..54752f80b 100644 --- a/opennebula/data_opennebula_templates.go +++ b/opennebula/data_opennebula_templates.go @@ -7,12 +7,16 @@ import ( "regexp" "sort" "strconv" + "strings" 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" ) +var orderTypes = []string{"ASC", "DESC"} +var sortOnTemplatesValues = []string{"id", "name", "cpu", "vcpu", "memory", "register_date"} + func dataOpennebulaTemplates() *schema.Resource { return &schema.Resource{ ReadContext: datasourceOpennebulaTemplatesRead, @@ -28,12 +32,30 @@ func dataOpennebulaTemplates() *schema.Resource { "sort_on": { Type: schema.TypeString, Optional: true, - Description: "Attribute used to sort the templates list, it has to be an ", + Description: "Attribute used to sort the templates list, only works on integer attributes.", + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + value := strings.ToUpper(v.(string)) + + if !contains(value, sortOnTemplatesValues) { + errors = append(errors, fmt.Errorf("type %q must be one of: %s", k, strings.Join(sortOnTemplatesValues, ","))) + } + + return + }, }, "order": { Type: schema.TypeString, Optional: true, Description: "Ordering of the sort: ASC or DESC", + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + value := strings.ToUpper(v.(string)) + + if !contains(value, orderTypes) { + errors = append(errors, fmt.Errorf("type %q must be one of: %s", k, strings.Join(orderTypes, ","))) + } + + return + }, }, "templates": { Type: schema.TypeList, @@ -46,7 +68,7 @@ func dataOpennebulaTemplates() *schema.Resource { Type: schema.TypeInt, Optional: false, Computed: true, - Description: "Name of the Template", + Description: "ID of the Template", }, "name": { Type: schema.TypeString, @@ -209,38 +231,76 @@ func datasourceOpennebulaTemplatesRead(ctx context.Context, d *schema.ResourceDa 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) + var sortOnAttr, ordering string + nameRegStr := d.Get("name_regex").(string) + sortOnAttr = d.Get("sort_on").(string) + + if len(sortOnAttr) > 0 && len(templatesMaps) > 1 { + ordering = d.Get("order").(string) + var orderingFn func(int, int) bool + switch ordering { + case "ASC": + switch templatesMaps[0][sortOnAttr].(type) { + case int: + orderingFn = func(i, j int) bool { + return templatesMaps[i][sortOnAttr].(int) > templatesMaps[j][sortOnAttr].(int) + } + case string: + orderingFn = func(i, j int) bool { + return templatesMaps[i][sortOnAttr].(string) > templatesMaps[j][sortOnAttr].(string) + } + case float64: + orderingFn = func(i, j int) bool { + return templatesMaps[i][sortOnAttr].(float64) > templatesMaps[j][sortOnAttr].(float64) + } + default: + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "can't sort templates", + Detail: fmt.Sprintf("%s attribute type comparison is not handled", sortOnAttr), + }) + return diags + } + case "DESC": + switch templatesMaps[0][sortOnAttr].(type) { + case int: + orderingFn = func(i, j int) bool { + return templatesMaps[i][sortOnAttr].(int) < templatesMaps[j][sortOnAttr].(int) + } + case string: + orderingFn = func(i, j int) bool { + return templatesMaps[i][sortOnAttr].(string) < templatesMaps[j][sortOnAttr].(string) + } + case float64: + orderingFn = func(i, j int) bool { + return templatesMaps[i][sortOnAttr].(float64) < templatesMaps[j][sortOnAttr].(float64) + } + default: + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "can't sort templates", + Detail: fmt.Sprintf("%s attribute type comparison is not handled", sortOnAttr), + }) + return diags + } } - } - // 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) - }) - nameRegStr := d.Get("name_regex").(string) + // 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+nameRegStr)))) 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 - } + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "failed to set templates", + Detail: err.Error(), + }) + return diags } return nil diff --git a/opennebula/data_opennebula_virtual_machines.go b/opennebula/data_opennebula_virtual_machines.go new file mode 100644 index 000000000..5918f0e25 --- /dev/null +++ b/opennebula/data_opennebula_virtual_machines.go @@ -0,0 +1,383 @@ +package opennebula + +import ( + "context" + "crypto/sha512" + "errors" + "fmt" + "regexp" + "sort" + "strconv" + "strings" + + vmSc "github.com/OpenNebula/one/src/oca/go/src/goca/schemas/vm" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +var sortOnVMsValues = []string{"id", "name", "cpu", "vcpu", "memory"} + +func dataOpennebulaVirtualMachines() *schema.Resource { + return &schema.Resource{ + ReadContext: datasourceOpennebulaVirtualMachinesRead, + + Schema: map[string]*schema.Schema{ + "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(), + "name_regex": { + Type: schema.TypeString, + Optional: true, + Description: "Filter VMs by name with a RE2 a regular expression", + }, + "sort_on": { + Type: schema.TypeString, + Optional: true, + Description: "Attribute used to sort the VMs list, only works on integer attributes.", + + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + value := strings.ToLower(v.(string)) + + if !contains(value, sortOnVMsValues) { + errors = append(errors, fmt.Errorf("type %q must be one of: %s", k, strings.Join(sortOnVMsValues, ","))) + } + + return + }, + }, + "order": { + Type: schema.TypeString, + Optional: true, + Description: "Ordering of the sort: ASC or DESC", + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + value := strings.ToUpper(v.(string)) + + if !contains(value, orderTypes) { + errors = append(errors, fmt.Errorf("type %q must be one of: %s", k, strings.Join(orderTypes, ","))) + } + + return + }, + }, + "virtual_machines": { + Type: schema.TypeList, + Optional: false, + Computed: true, + Description: "List of matching vms", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeInt, + Optional: false, + Computed: true, + Description: "ID of the VM", + }, + "name": { + Type: schema.TypeString, + Optional: false, + Computed: true, + Description: "Name of the VM", + }, + "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 + }(), + "tags": func() *schema.Schema { + s := tagsSchema() + s.Computed = true + s.Optional = false + return s + }(), + }, + }, + }, + }, + } +} + +func virtualMachinesFilter(d *schema.ResourceData, meta interface{}) ([]*vmSc.VM, error) { + + config := meta.(*Configuration) + controller := config.Controller + + vms, err := controller.VMs().InfoExtended() + if err != nil { + return nil, err + } + + // filter VMs with user defined criterias + 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{}) + nameRegStr := d.Get("name_regex").(string) + var nameReg *regexp.Regexp + if len(nameRegStr) > 0 { + nameReg = regexp.MustCompile(nameRegStr) + } + + matched := make([]*vmSc.VM, 0, 1) + for i, vm := range vms.VMs { + + if nameReg != nil && !nameReg.MatchString(vm.Name) { + continue + } + tplCPU, err := vm.Template.GetCPU() + if err != nil { + continue + } + if cpuOk && tplCPU != cpu.(float64) { + continue + } + + tplVCPU, err := vm.Template.GetVCPU() + if err != nil { + continue + } + if vcpuOk && tplVCPU != vcpu.(int) { + continue + } + + tplMemory, err := vm.Template.GetMemory() + if err != nil { + continue + } + if memoryOk && tplMemory != memory.(int) { + continue + } + + if tagsOk && !matchTags(vm.UserTemplate.Template, tags) { + continue + } + + matched = append(matched, &vms.VMs[i]) + } + + if len(matched) == 0 { + return nil, fmt.Errorf("no VMs match the constraints") + } + + return matched, nil +} + +func datasourceOpennebulaVirtualMachinesRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + + var diags diag.Diagnostics + + vms, err := virtualMachinesFilter(d, meta) + if err != nil { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "VMs filtering failed", + Detail: err.Error(), + }) + return diags + } + + vmsMaps := make([]map[string]interface{}, 0, len(vms)) + for _, vm := range vms { + + cpu, _ := vm.Template.GetCPU() + vcpu, _ := vm.Template.GetVCPU() + memory, _ := vm.Template.GetMemory() + + // builds disks list + disks := vm.Template.GetDisks() + diskList := make([]interface{}, 0, len(disks)) + + for _, disk := range disks { + diskList = append(diskList, flattenDisk(disk)) + } + + // builds nics list + nics := vm.Template.GetNICs() + nicList := make([]interface{}, 0, len(nics)) + + for _, nic := range nics { + nicList = append(nicList, flattenNIC(nic)) + } + + // builds VM Groups list + dynTemplate := vm.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(vm.UserTemplate.Template) + + vmMap := map[string]interface{}{ + "name": vm.Name, + "id": vm.ID, + "cpu": cpu, + "vcpu": vcpu, + "memory": memory, + "disk": diskList, + "nic": nicList, + "vmgroup": vmgMap, + } + + if len(tplPairs) > 0 { + vmMap["tags"] = tplPairs + } + + vmsMaps = append(vmsMaps, vmMap) + } + + var sortOnAttr, ordering string + nameRegStr := d.Get("name_regex").(string) + sortOnAttr = d.Get("sort_on").(string) + + if len(sortOnAttr) > 0 && len(vmsMaps) > 1 { + + ordering = d.Get("order").(string) + var orderingFn func(int, int) bool + switch ordering { + case "DESC": + switch vmsMaps[0][sortOnAttr].(type) { + case int: + orderingFn = func(i, j int) bool { + return vmsMaps[i][sortOnAttr].(int) > vmsMaps[j][sortOnAttr].(int) + } + case string: + orderingFn = func(i, j int) bool { + return vmsMaps[i][sortOnAttr].(string) > vmsMaps[j][sortOnAttr].(string) + } + case float64: + orderingFn = func(i, j int) bool { + return vmsMaps[i][sortOnAttr].(float64) > vmsMaps[j][sortOnAttr].(float64) + } + default: + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "can't sort VMs", + Detail: fmt.Sprintf("%s attribute type comparison is not handled", sortOnAttr), + }) + return diags + } + case "ASC": + switch vmsMaps[0][sortOnAttr].(type) { + case int: + orderingFn = func(i, j int) bool { + return vmsMaps[i][sortOnAttr].(int) < vmsMaps[j][sortOnAttr].(int) + } + case string: + orderingFn = func(i, j int) bool { + return vmsMaps[i][sortOnAttr].(string) < vmsMaps[j][sortOnAttr].(string) + } + case float64: + orderingFn = func(i, j int) bool { + return vmsMaps[i][sortOnAttr].(float64) < vmsMaps[j][sortOnAttr].(float64) + } + default: + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "can't sort VMs", + Detail: fmt.Sprintf("%s attribute type comparison is not handled", sortOnAttr), + }) + return diags + } + } + + // will crash if sortOnAttr is the name of an attributes with another type than integer + sort.Slice(vmsMaps, func(i, j int) bool { + return orderingFn(i, j) + }) + } + + d.SetId(fmt.Sprintf("%x", sha512.Sum512([]byte(ordering+sortOnAttr+nameRegStr)))) + + err = d.Set("virtual_machines", vmsMaps) + if err != nil { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "failed to set virtual_machines", + Detail: err.Error(), + }) + return diags + + } + + return nil +} diff --git a/opennebula/data_opennebula_virtual_machines_test.go b/opennebula/data_opennebula_virtual_machines_test.go new file mode 100644 index 000000000..4969bdfd7 --- /dev/null +++ b/opennebula/data_opennebula_virtual_machines_test.go @@ -0,0 +1,370 @@ +package opennebula + +import ( + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccVirtualMachineDataSource(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckVirtualMachineDestroy, + Steps: []resource.TestStep{ + { + Config: testAccVirtualMachinesDataSourceInvalidCPU, + ExpectError: regexp.MustCompile("cpu should be strictly greater than 0"), + }, + { + Config: testAccVirtualMachinesDataSourceInvalidVCPU, + ExpectError: regexp.MustCompile("vcpu should be strictly greater than 0"), + }, + { + Config: testAccVirtualMachinesDataSourceInvalidMemory, + ExpectError: regexp.MustCompile("memory should be strictly greater than 0"), + }, + { + Config: testAccVirtualMachinesDataSourceInvalidSort, + ExpectError: regexp.MustCompile("type \"sort_on\" must be one of: id,name,cpu,vcpu,memory"), + }, + { + Config: testAccVirtualMachinesDataSourceInvalidOrder, + ExpectError: regexp.MustCompile("type \"order\" must be one of: ASC,DESC"), + }, + { + Config: testAccVirtualMachinesDataSourceNoMatchingVMs, + ExpectError: regexp.MustCompile("no VMs match the constraints"), + }, + { + Config: testAccVirtualMachinesDataSourceBasic, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "data.opennebula_virtual_machines.basic", + "virtual_machines.0.name", + "vm-0", + ), + resource.TestCheckResourceAttr( + "data.opennebula_virtual_machines.basic", + "virtual_machines.0.cpu", + "0.1", + ), + resource.TestCheckResourceAttr( + "data.opennebula_virtual_machines.basic", + "virtual_machines.0.vcpu", + "2", + ), + resource.TestCheckResourceAttr( + "data.opennebula_virtual_machines.basic", + "virtual_machines.0.memory", + "128", + ), + resource.TestCheckResourceAttr( + "data.opennebula_virtual_machines.basic", + "virtual_machines.0.disk.0.volatile_type", + "swap", + ), + resource.TestCheckResourceAttr( + "data.opennebula_virtual_machines.basic", + "virtual_machines.0.nic.0.ip", + "172.20.0.1", + ), + resource.TestCheckResourceAttr( + "data.opennebula_virtual_machines.basic", + "virtual_machines.0.vmgroup.0.vmgroup_id", + "0", + ), + resource.TestCheckResourceAttr( + "data.opennebula_virtual_machines.basic", + "virtual_machines.0.tags.SCHED_REQUIREMENTS", + "CLUSTER_ID!=\"123\"", + ), + ), + }, + { + Config: testAccVirtualMachinesDataSourceBasicSort, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "data.opennebula_virtual_machines.id_asc", + "virtual_machines.0.name", + "vm-0", + ), + resource.TestCheckResourceAttr( + "data.opennebula_virtual_machines.id_asc", + "virtual_machines.1.name", + "vm-1", + ), + resource.TestCheckResourceAttr( + "data.opennebula_virtual_machines.cpu_desc", + "virtual_machines.0.name", + "vm-1", + ), + resource.TestCheckResourceAttr( + "data.opennebula_virtual_machines.cpu_desc", + "virtual_machines.1.name", + "vm-0", + ), + resource.TestCheckResourceAttr( + "data.opennebula_virtual_machines.mem_asc", + "virtual_machines.0.name", + "vm-1", + ), + resource.TestCheckResourceAttr( + "data.opennebula_virtual_machines.mem_asc", + "virtual_machines.1.name", + "vm-0", + ), + resource.TestCheckResourceAttr( + "data.opennebula_virtual_machines.vcpu_desc", + "virtual_machines.0.name", + "vm-0", + ), + resource.TestCheckResourceAttr( + "data.opennebula_virtual_machines.vcpu_desc", + "virtual_machines.1.name", + "vm-1", + ), + resource.TestCheckResourceAttr( + "data.opennebula_virtual_machines.name_desc", + "virtual_machines.0.name", + "vm-1", + ), + resource.TestCheckResourceAttr( + "data.opennebula_virtual_machines.name_desc", + "virtual_machines.1.name", + "vm-0", + ), + resource.TestCheckResourceAttr( + "data.opennebula_virtual_machines.id_desc_cpu", + "virtual_machines.0.name", + "vm-0", + ), + resource.TestCheckResourceAttr( + "data.opennebula_virtual_machines.id_desc_cpu", + "virtual_machines.0.cpu", + "0.1", + ), + resource.TestCheckResourceAttr( + "data.opennebula_virtual_machines.mem_asc_vcpu", + "virtual_machines.0.name", + "vm-1", + ), + resource.TestCheckResourceAttr( + "data.opennebula_virtual_machines.mem_asc_vcpu", + "virtual_machines.0.vcpu", + "1", + ), + resource.TestCheckResourceAttr( + "data.opennebula_virtual_machines.static_name", + "virtual_machines.0.name", + "vm-0", + ), + resource.TestCheckResourceAttr( + "data.opennebula_virtual_machines.static_mem", + "virtual_machines.0.name", + "vm-1", + ), + resource.TestCheckResourceAttr( + "data.opennebula_virtual_machines.static_tags", + "virtual_machines.0.name", + "vm-0", + ), + ), + }, + }, + }) +} + +var testAccVirtualMachinesDataSourceInvalidCPU = ` +data "opennebula_virtual_machines" "test_invalid_id" { + name_regex = "test.*" + sort_on = "id" + order = "ASC" + cpu = 0 +} +` +var testAccVirtualMachinesDataSourceInvalidVCPU = ` +data "opennebula_virtual_machines" "test_invalid_vcpu" { + name_regex = "test.*" + sort_on = "id" + order = "ASC" + vcpu = 0 +} +` +var testAccVirtualMachinesDataSourceInvalidMemory = ` +data "opennebula_virtual_machines" "test_invalid_memory" { + name_regex = "test.*" + sort_on = "id" + order = "ASC" + memory = 0 +} +` +var testAccVirtualMachinesDataSourceInvalidSort = ` +data "opennebula_virtual_machines" "test_invalid_sort" { + name_regex = "test.*" + sort_on = "unsupported_field" + order = "ASC" +} +` +var testAccVirtualMachinesDataSourceInvalidOrder = ` +data "opennebula_virtual_machines" "test_invalid_order" { + name_regex = "test.*" + sort_on = "id" + order = "unsupported_order" +} +` +var testAccVirtualMachinesDataSourceNoMatchingVMs = ` +data "opennebula_virtual_machines" "test_no_matching" { + name_regex = "noMatchingVM.*" +} +` +var testAccFirstVirtualMachine = ` +resource "opennebula_virtual_machine" "first_vm" { + name = "vm-0" + group = "oneadmin" + permissions = "642" + memory = 128 + cpu = 0.1 + vcpu = 2 + sched_requirements = "CLUSTER_ID!=\"123\"" + + disk { + volatile_type = "swap" + size = 16 + target = "vda" + } + + nic { + network_mode_auto = false + ip = "172.20.0.1" + } +} +` +var testAccSecondVirtualMachine = ` +resource "opennebula_virtual_machine" "second_vm" { + name = "vm-1" + group = "oneadmin" + permissions = "642" + memory = 64 + cpu = 0.2 + vcpu = 1 +} +` + +var testAccVirtualMachinesDataSourceBasic = testAccFirstVirtualMachine + ` +data "opennebula_virtual_machines" "basic" { + name_regex = "vm.*" + + depends_on = [opennebula_virtual_machine.first_vm] +} +` +var testAccVirtualMachinesDataSourceBasicSort = testAccFirstVirtualMachine + testAccSecondVirtualMachine + ` +data "opennebula_virtual_machines" "id_asc" { + name_regex = "vm.*" + sort_on = "id" + order = "ASC" + + depends_on = [ + opennebula_virtual_machine.first_vm, + opennebula_virtual_machine.second_vm + ] +} + +data "opennebula_virtual_machines" "cpu_desc" { + name_regex = "vm.*" + sort_on = "cpu" + order = "DESC" + + depends_on = [ + opennebula_virtual_machine.first_vm, + opennebula_virtual_machine.second_vm + ] +} + +data "opennebula_virtual_machines" "mem_asc" { + name_regex = "vm.*" + sort_on = "memory" + order = "ASC" + + depends_on = [ + opennebula_virtual_machine.first_vm, + opennebula_virtual_machine.second_vm + ] +} + +data "opennebula_virtual_machines" "vcpu_desc" { + name_regex = "vm.*" + sort_on = "vcpu" + order = "DESC" + + depends_on = [ + opennebula_virtual_machine.first_vm, + opennebula_virtual_machine.second_vm + ] +} + +data "opennebula_virtual_machines" "name_desc" { + name_regex = "vm.*" + sort_on = "name" + order = "DESC" + + depends_on = [ + opennebula_virtual_machine.first_vm, + opennebula_virtual_machine.second_vm + ] +} + +data "opennebula_virtual_machines" "id_desc_cpu" { + name_regex = "vm.*" + sort_on = "id" + cpu = 0.1 + order = "DESC" + + depends_on = [ + opennebula_virtual_machine.first_vm, + opennebula_virtual_machine.second_vm + ] +} + +data "opennebula_virtual_machines" "mem_asc_vcpu" { + name_regex = "vm-*" + sort_on = "memory" + vcpu = 1 + order = "ASC" + + depends_on = [ + opennebula_virtual_machine.first_vm, + opennebula_virtual_machine.second_vm + ] +} + +data "opennebula_virtual_machines" "static_name" { + name_regex = "vm-0" + + depends_on = [ + opennebula_virtual_machine.first_vm, + opennebula_virtual_machine.second_vm + ] +} + +data "opennebula_virtual_machines" "static_mem" { + memory = 64 + + depends_on = [ + opennebula_virtual_machine.first_vm, + opennebula_virtual_machine.second_vm + ] +} + +data "opennebula_virtual_machines" "static_tags" { + tags = { + sched_requirements = "CLUSTER_ID!=\"123\"" + } + + depends_on = [ + opennebula_virtual_machine.first_vm, + opennebula_virtual_machine.second_vm + ] +} +` diff --git a/opennebula/provider.go b/opennebula/provider.go index 6403aa6a4..c68454876 100644 --- a/opennebula/provider.go +++ b/opennebula/provider.go @@ -80,6 +80,7 @@ func Provider() *schema.Provider { "opennebula_datastore": dataOpennebulaDatastore(), "opennebula_zone": dataOpennebulaZone(), "opennebula_marketplace": dataOpennebulaMarketplace(), + "opennebula_virtual_machines": dataOpennebulaVirtualMachines(), }, ResourcesMap: map[string]*schema.Resource{ diff --git a/website/docs/d/templates.html.markdown b/website/docs/d/templates.html.markdown index ae46a00ad..3c265e214 100644 --- a/website/docs/d/templates.html.markdown +++ b/website/docs/d/templates.html.markdown @@ -25,6 +25,7 @@ data "opennebula_templates" "example" { ## Argument Reference * `name_regex` - (Optional) Filter templates by name with a RE2 regular expression. +* `sort_on` - (Optional) Attribute used to sort the template list among: `id`, `name`, `cpu`, `vcpu`, `memory`, `register_date`. * `has_cpu` - (Optional) Indicate if a CPU value has been defined. * `cpu` - (Optional) Amount of CPU shares assigned to the VM. * `has_vcpu` - (Optional) Indicate if a VCPU value has been defined. diff --git a/website/docs/d/virtual_machines.markdown b/website/docs/d/virtual_machines.markdown new file mode 100644 index 000000000..0c34ac1f9 --- /dev/null +++ b/website/docs/d/virtual_machines.markdown @@ -0,0 +1,50 @@ +--- +layout: "opennebula" +page_title: "OpenNebula: opennebula_virtual_machines" +sidebar_current: "docs-opennebula-datasource-virtual-machines" +description: |- + Get the virtual machine information for a given name. +--- + +# opennebula_virtual_machines + +Use this data source to retrieve virtual machines information. + +## Example Usage + +```hcl +data "opennebula_virtual_machines" "example" { + name_regex = "test.*" + sort_on = "id" + order = "ASC" +} +``` + + +## Argument Reference + +* `name_regex` - (Optional) Filter virtual machines by name with a RE2 regular expression. +* `sort_on` - (Optional) Attribute used to sort the VMs list among: `id`, `name`, `cpu`, `vcpu`, `memory`. +* `cpu` - (Optional) Amount of CPU shares assigned to the VM. +* `vcpu` - (Optional) Number of CPU cores presented to the VM. +* `memory` - (Optional) Amount of RAM assigned to the VM in MB. +* `tags` - (Optional) virtual machine tags (Key = Value). +* `order` - (Optional) Ordering of the sort: ASC or DESC. + +## Attribute Reference + +The following attributes are exported: + +* `virtual_machines` - For each filtered virtual machine, this section collect a list of attributes. See [virtual-machines-attributes](#virtual-machines-attributes) + +## Virtual machines attributes + +* `id` - ID of the virtual machine. +* `name` - Name of the virtual machine. +* `cpu` - Amount of CPU shares assigned to the VM. +* `vcpu` - Number of CPU cores presented to the VM. +* `memory` - Amount of RAM assigned to the VM in MB. +* `disk` - Disk parameters +* `nic` - NIC parameters +* `vmgroup` - VM group parameters +* `tags` - Tags of the virtual machine (Key = Value).