diff --git a/CHANGELOG.md b/CHANGELOG.md index e34fd8822..30a527270 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ FEATURES: * **New Resource**: `opennebula_user_quotas` (#448) * **New Resource**: `opennebula_group_quotas` (#447) +* resources/opennebula_virtual_machine: add `raw` section and share its code with `opennebula_template` resource. (#464) DEPRECATION: diff --git a/opennebula/resource_opennebula_template.go b/opennebula/resource_opennebula_template.go index d2db4bbff..db2889fba 100644 --- a/opennebula/resource_opennebula_template.go +++ b/opennebula/resource_opennebula_template.go @@ -58,36 +58,6 @@ func commonTemplateSchemas() map[string]*schema.Schema { }, }, "disk": diskSchema(), - "raw": { - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - Description: "Low-level hypervisor tuning", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "type": { - Type: schema.TypeString, - Required: true, - ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { - validtypes := []string{"kvm", "lxd", "vmware"} - value := v.(string) - - if inArray(value, validtypes) < 0 { - errors = append(errors, fmt.Errorf("Type %q must be one of: %s", k, strings.Join(validtypes, ","))) - } - - return - }, - Description: "Name of the hypervisor: kvm, lxd, vmware", - }, - "data": { - Type: schema.TypeString, - Required: true, - Description: "Low-level data to pass to the hypervisor", - }, - }, - }, - }, "reg_time": { Type: schema.TypeInt, Computed: true, @@ -475,32 +445,6 @@ func resourceOpennebulaTemplateReadCustom(ctx context.Context, d *schema.Resourc diags = append(diags, diag) } - rawVec, _ := tpl.Template.GetVector("RAW") - if rawVec != nil { - - rawMap := make([]map[string]interface{}, 0, 1) - - hypType, _ := rawVec.GetStr("TYPE") - data, _ := rawVec.GetStr("DATA") - - rawMap = append(rawMap, map[string]interface{}{ - "type": hypType, - "data": data, - }) - - if _, ok := d.GetOk("raw"); ok { - err = d.Set("raw", rawMap) - if err != nil { - diags = append(diags, diag.Diagnostic{ - Severity: diag.Error, - Summary: "Failed to set attribute", - Detail: fmt.Sprintf("template (ID: %s): %s", d.Id(), err), - }) - return diags - } - } - } - if tpl.LockInfos != nil { d.Set("lock", LockLevelToString(tpl.LockInfos.Locked)) } @@ -714,50 +658,11 @@ func resourceOpennebulaTemplateUpdateCustom(ctx context.Context, d *schema.Resou } if d.HasChange("raw") { - newTpl.Del("RAW") - - raw := d.Get("raw").([]interface{}) - if len(raw) > 0 { - for i := 0; i < len(raw); i++ { - rawConfig := raw[i].(map[string]interface{}) - rawVec := newTpl.AddVector("RAW") - rawVec.AddPair("TYPE", rawConfig["type"].(string)) - rawVec.AddPair("DATA", rawConfig["data"].(string)) - } - } - } - - if d.HasChange("sched_requirements") { - schedRequirements := d.Get("sched_requirements").(string) - - if len(schedRequirements) > 0 { - newTpl.Placement(vmk.SchedRequirements, schedRequirements) - } else { - newTpl.Del(string(vmk.SchedRequirements)) - } - update = true - } - - if d.HasChange("sched_ds_requirements") { - schedDSRequirements := d.Get("sched_ds_requirements").(string) - - if len(schedDSRequirements) > 0 { - newTpl.Placement(vmk.SchedDSRequirements, schedDSRequirements) - } else { - newTpl.Del(string(vmk.SchedDSRequirements)) - } + updateRaw(d, &newTpl.Template) update = true } - if d.HasChange("description") { - newTpl.Del(string(vmk.Description)) - - description := d.Get("description").(string) - - if len(description) > 0 { - newTpl.Add(vmk.Description, description) - } - + if updateTemplate(d, &newTpl) { update = true } @@ -813,13 +718,6 @@ func resourceOpennebulaTemplateUpdateCustom(ctx context.Context, d *schema.Resou update = true } - if d.HasChange("template_section") { - - updateTemplateSection(d, &newTpl.Template) - - update = true - } - if d.HasChange("tags") { oldTagsIf, newTagsIf := d.GetChange("tags") @@ -1002,15 +900,6 @@ func generateTemplate(d *schema.ResourceData, meta interface{}) (*vm.Template, e return nil, err } - //Generate RAW definition - raw := d.Get("raw").([]interface{}) - for i := 0; i < len(raw); i++ { - rawConfig := raw[i].(map[string]interface{}) - rawVec := tpl.AddVector("RAW") - rawVec.AddPair("TYPE", rawConfig["type"].(string)) - rawVec.AddPair("DATA", rawConfig["data"].(string)) - } - // add default tags if they aren't overriden config := meta.(*Configuration) diff --git a/opennebula/resource_opennebula_virtual_machine.go b/opennebula/resource_opennebula_virtual_machine.go index c71ba4fd7..b48173109 100644 --- a/opennebula/resource_opennebula_virtual_machine.go +++ b/opennebula/resource_opennebula_virtual_machine.go @@ -1225,45 +1225,7 @@ func resourceOpennebulaVirtualMachineUpdateCustom(ctx context.Context, d *schema }, } - if d.HasChange("sched_requirements") { - schedRequirements := d.Get("sched_requirements").(string) - - if len(schedRequirements) > 0 { - tpl.Placement(vmk.SchedRequirements, schedRequirements) - } else { - tpl.Del(string(vmk.SchedRequirements)) - } - update = true - } - - if d.HasChange("sched_ds_requirements") { - schedDSRequirements := d.Get("sched_ds_requirements").(string) - - if len(schedDSRequirements) > 0 { - tpl.Placement(vmk.SchedDSRequirements, schedDSRequirements) - } else { - tpl.Del(string(vmk.SchedDSRequirements)) - } - update = true - } - - if d.HasChange("description") { - - tpl.Del(string(vmk.Description)) - - description := d.Get("description").(string) - - if len(description) > 0 { - tpl.Add(vmk.Description, description) - } - - update = true - } - - if d.HasChange("template_section") { - - updateTemplateSection(d, &tpl.Template) - + if updateTemplate(d, tpl) { update = true } @@ -1503,6 +1465,11 @@ func resourceOpennebulaVirtualMachineUpdateCustom(ctx context.Context, d *schema } } + if d.HasChange("raw") { + updateRaw(d, &tpl.Template) + updateConf = true + } + if d.HasChange("cpu") || d.HasChange("vcpu") || d.HasChange("memory") { timeout := time.Duration(d.Get("timeout").(int)) * time.Minute diff --git a/opennebula/shared_schemas.go b/opennebula/shared_schemas.go index dd8e5de3b..df279d516 100644 --- a/opennebula/shared_schemas.go +++ b/opennebula/shared_schemas.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/OpenNebula/one/src/oca/go/src/goca/dynamic" + dyn "github.com/OpenNebula/one/src/oca/go/src/goca/dynamic" "github.com/OpenNebula/one/src/oca/go/src/goca/schemas/shared" "github.com/OpenNebula/one/src/oca/go/src/goca/schemas/vm" vmk "github.com/OpenNebula/one/src/oca/go/src/goca/schemas/vm/keys" @@ -85,14 +86,44 @@ func commonVMSchemas() map[string]*schema.Schema { func commonInstanceSchema() map[string]*schema.Schema { return map[string]*schema.Schema{ - "cpu": cpuSchema(), - "vcpu": vcpuSchema(), - "memory": memorySchema(), - "context": contextSchema(), - "cpumodel": cpumodelSchema(), - "graphics": graphicsSchema(), - "os": osSchema(), - "vmgroup": vmGroupSchema(), + "cpu": cpuSchema(), + "vcpu": vcpuSchema(), + "memory": memorySchema(), + "context": contextSchema(), + "cpumodel": cpumodelSchema(), + "graphics": graphicsSchema(), + "os": osSchema(), + "vmgroup": vmGroupSchema(), + "raw": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Description: "Low-level hypervisor tuning", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + validtypes := []string{"kvm", "lxd", "vmware"} + value := v.(string) + + if !contains(value, validtypes) { + errors = append(errors, fmt.Errorf("Type %q must be one of: %s", k, strings.Join(validtypes, ","))) + } + + return + }, + Description: "Name of the hypervisor: kvm, lxd, vmware", + }, + "data": { + Type: schema.TypeString, + Required: true, + Description: "Low-level data to pass to the hypervisor", + }, + }, + }, + }, "tags": tagsSchema(), "default_tags": defaultTagsSchemaComputed(), "tags_all": tagsSchemaComputed(), @@ -654,6 +685,15 @@ func generateVMTemplate(d *schema.ResourceData, tpl *vm.Template) error { } + //Generate RAW definition + raw := d.Get("raw").([]interface{}) + for i := 0; i < len(raw); i++ { + rawConfig := raw[i].(map[string]interface{}) + rawVec := tpl.AddVector("RAW") + rawVec.AddPair("TYPE", rawConfig["type"].(string)) + rawVec.AddPair("DATA", rawConfig["data"].(string)) + } + descr, ok := d.GetOk("description") if ok { tpl.Add(vmk.Description, descr.(string)) @@ -662,6 +702,67 @@ func generateVMTemplate(d *schema.ResourceData, tpl *vm.Template) error { return nil } +func updateTemplate(d *schema.ResourceData, tpl *vm.Template) bool { + + update := false + + if d.HasChange("sched_requirements") { + schedRequirements := d.Get("sched_requirements").(string) + + if len(schedRequirements) > 0 { + tpl.Placement(vmk.SchedRequirements, schedRequirements) + } else { + tpl.Del(string(vmk.SchedRequirements)) + } + update = true + } + + if d.HasChange("sched_ds_requirements") { + schedDSRequirements := d.Get("sched_ds_requirements").(string) + + if len(schedDSRequirements) > 0 { + tpl.Placement(vmk.SchedDSRequirements, schedDSRequirements) + } else { + tpl.Del(string(vmk.SchedDSRequirements)) + } + update = true + } + + if d.HasChange("description") { + + tpl.Del(string(vmk.Description)) + + description := d.Get("description").(string) + + if len(description) > 0 { + tpl.Add(vmk.Description, description) + } + + update = true + } + + if d.HasChange("template_section") { + updateTemplateSection(d, &tpl.Template) + update = true + } + + return update +} + +func updateRaw(d *schema.ResourceData, tpl *dyn.Template) { + tpl.Del("RAW") + + raw := d.Get("raw").([]interface{}) + if len(raw) > 0 { + for i := 0; i < len(raw); i++ { + rawConfig := raw[i].(map[string]interface{}) + rawVec := tpl.AddVector("RAW") + rawVec.AddPair("TYPE", rawConfig["type"].(string)) + rawVec.AddPair("DATA", rawConfig["data"].(string)) + } + } +} + func flattenNIC(nic shared.NIC) map[string]interface{} { sg := make([]int, 0) @@ -768,6 +869,8 @@ func flattenTemplate(d *schema.ResourceData, inheritedVectors map[string]interfa port, _ := vmTemplate.GetIOGraphic(vmk.Port) t, _ := vmTemplate.GetIOGraphic(vmk.GraphicType) keymap, _ := vmTemplate.GetIOGraphic(vmk.Keymap) + // Raw + rawVec, _ := vmTemplate.GetVector("RAW") // VM size cpu, _ := vmTemplate.GetCPU() @@ -841,6 +944,26 @@ func flattenTemplate(d *schema.ResourceData, inheritedVectors map[string]interfa } } + if rawVec != nil { + + rawMap := make([]map[string]interface{}, 0, 1) + + hypType, _ := rawVec.GetStr("TYPE") + data, _ := rawVec.GetStr("DATA") + + rawMap = append(rawMap, map[string]interface{}{ + "type": hypType, + "data": data, + }) + + if _, ok := d.GetOk("raw"); ok { + err = d.Set("raw", rawMap) + if err != nil { + return err + } + } + } + return nil } diff --git a/website/docs/r/virtual_machine.html.markdown b/website/docs/r/virtual_machine.html.markdown index c81cc8a37..611ea4038 100644 --- a/website/docs/r/virtual_machine.html.markdown +++ b/website/docs/r/virtual_machine.html.markdown @@ -99,6 +99,7 @@ The following arguments are supported: * `keep_nic_order` - (Optional) Indicates if the provider should keep NIC list ordering at update. * `vmgroup` - (Optional) See [VM group parameters](#vm-group-parameters) below for details. Changing this argument triggers a new resource. * `group` - (Optional) Name of the group which owns the virtual machine. Defaults to the caller primary group. +* `raw` - (Optional) Allow to pass hypervisor level tuning content. See [Raw parameters](#raw-parameters) below for details. * `sched_requirements` - (Optional) Scheduling requirements to deploy the resource following specific rule. * `sched_ds_requirements` - (Optional) Storage placement requirements to deploy the resource following specific rule. * `tags` - (Optional) Virtual Machine tags (Key = Value). @@ -162,6 +163,13 @@ A NIC update will be triggered in adding or removing a `nic` section, or by a mo * `vmgroup_id` - (Required) ID of the VM group to use. * `role` - (Required) role of the VM group to use. +### Raw parameters + +`raw` supports the following arguments: + +* `type` - (Required) - Hypervisor. Supported values: `kvm`, `lxd`, `vmware`. +* `data` - (Required) - Raw data to pass to the hypervisor. + ### Template section parameters `template_section` supports the following arguments: