This is an automated email from the ASF dual-hosted git repository. pearl11594 pushed a commit to branch add-autoscale-support in repository https://gitbox.apache.org/repos/asf/cloudstack-terraform-provider.git
commit 65495540fa211752c3c76e8bc77302ecff6bb6a4 Author: Pearl Dsilva <pearl1...@gmail.com> AuthorDate: Fri Aug 29 11:59:47 2025 -0400 Add support for Autoscale VM groups --- cloudstack/resource_cloudstack_autoscale_policy.go | 195 ++++++++++++++ .../resource_cloudstack_autoscale_vm_group.go | 286 +++++++++++++++++++++ .../resource_cloudstack_autoscale_vm_profile.go | 230 +++++++++++++++-- cloudstack/resource_cloudstack_condition.go | 172 +++++++++++++ cloudstack/resource_cloudstack_counter.go | 126 +++++++++ 5 files changed, 981 insertions(+), 28 deletions(-) diff --git a/cloudstack/resource_cloudstack_autoscale_policy.go b/cloudstack/resource_cloudstack_autoscale_policy.go new file mode 100644 index 0000000..85c6ed3 --- /dev/null +++ b/cloudstack/resource_cloudstack_autoscale_policy.go @@ -0,0 +1,195 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package cloudstack + +import ( + "fmt" + "log" + + "github.com/apache/cloudstack-go/v2/cloudstack" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceCloudStackAutoScalePolicy() *schema.Resource { + return &schema.Resource{ + Create: resourceCloudStackAutoScalePolicyCreate, + Read: resourceCloudStackAutoScalePolicyRead, + Update: resourceCloudStackAutoScalePolicyUpdate, + Delete: resourceCloudStackAutoScalePolicyDelete, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Optional: true, + Description: "the name of the autoscale policy", + }, + "action": { + Type: schema.TypeString, + Required: true, + Description: "the action to be executed if all the conditions evaluate to true for the specified duration", + ForceNew: true, + }, + "duration": { + Type: schema.TypeInt, + Required: true, + Description: "the duration in which the conditions have to be true before action is taken", + }, + "quiet_time": { + Type: schema.TypeInt, + Optional: true, + Description: "the cool down period in which the policy should not be evaluated after the action has been taken", + }, + "condition_ids": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Required: true, + Description: "the list of IDs of the conditions that are being evaluated on every interval", + }, + }, + } +} + +func resourceCloudStackAutoScalePolicyCreate(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + action := d.Get("action").(string) + duration := d.Get("duration").(int) + + conditionIds := []string{} + if v, ok := d.GetOk("condition_ids"); ok { + conditionSet := v.(*schema.Set) + for _, id := range conditionSet.List() { + conditionIds = append(conditionIds, id.(string)) + } + } + + p := cs.AutoScale.NewCreateAutoScalePolicyParams(action, conditionIds, duration) + + if v, ok := d.GetOk("name"); ok { + p.SetName(v.(string)) + } + if v, ok := d.GetOk("quiet_time"); ok { + p.SetQuiettime(v.(int)) + } + + log.Printf("[DEBUG] Creating autoscale policy") + resp, err := cs.AutoScale.CreateAutoScalePolicy(p) + if err != nil { + return fmt.Errorf("Error creating autoscale policy: %s", err) + } + + d.SetId(resp.Id) + log.Printf("[DEBUG] Autoscale policy created with ID: %s", resp.Id) + + return resourceCloudStackAutoScalePolicyRead(d, meta) +} + +func resourceCloudStackAutoScalePolicyRead(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + p := cs.AutoScale.NewListAutoScalePoliciesParams() + p.SetId(d.Id()) + + resp, err := cs.AutoScale.ListAutoScalePolicies(p) + if err != nil { + return fmt.Errorf("Error retrieving autoscale policy: %s", err) + } + + if resp.Count == 0 { + log.Printf("[DEBUG] Autoscale policy %s no longer exists", d.Id()) + d.SetId("") + return nil + } + + policy := resp.AutoScalePolicies[0] + d.Set("name", policy.Name) + d.Set("action", policy.Action) + d.Set("duration", policy.Duration) + d.Set("quiet_time", policy.Quiettime) + + conditionIds := schema.NewSet(schema.HashString, []interface{}{}) + for _, conditionId := range policy.Conditions { + conditionIds.Add(conditionId) + } + d.Set("condition_ids", conditionIds) + + return nil +} + +func resourceCloudStackAutoScalePolicyUpdate(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + if d.HasChange("name") || d.HasChange("condition_ids") || d.HasChange("duration") || d.HasChange("quiet_time") { + log.Printf("[DEBUG] Updating autoscale policy: %s", d.Id()) + + p := cs.AutoScale.NewUpdateAutoScalePolicyParams(d.Id()) + + if d.HasChange("name") { + if v, ok := d.GetOk("name"); ok { + p.SetName(v.(string)) + } + } + + if d.HasChange("duration") { + duration := d.Get("duration").(int) + p.SetDuration(duration) + } + + if d.HasChange("quiet_time") { + if v, ok := d.GetOk("quiet_time"); ok { + p.SetQuiettime(v.(int)) + } + } + + if d.HasChange("condition_ids") { + conditionIds := []string{} + if v, ok := d.GetOk("condition_ids"); ok { + conditionSet := v.(*schema.Set) + for _, id := range conditionSet.List() { + conditionIds = append(conditionIds, id.(string)) + } + } + p.SetConditionids(conditionIds) + } + + _, err := cs.AutoScale.UpdateAutoScalePolicy(p) + if err != nil { + return fmt.Errorf("Error updating autoscale policy: %s", err) + } + + log.Printf("[DEBUG] Autoscale policy updated successfully: %s", d.Id()) + } + + return resourceCloudStackAutoScalePolicyRead(d, meta) +} + +func resourceCloudStackAutoScalePolicyDelete(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + p := cs.AutoScale.NewDeleteAutoScalePolicyParams(d.Id()) + + log.Printf("[DEBUG] Deleting autoscale policy: %s", d.Id()) + _, err := cs.AutoScale.DeleteAutoScalePolicy(p) + if err != nil { + return fmt.Errorf("Error deleting autoscale policy: %s", err) + } + + return nil +} diff --git a/cloudstack/resource_cloudstack_autoscale_vm_group.go b/cloudstack/resource_cloudstack_autoscale_vm_group.go new file mode 100644 index 0000000..0d50e02 --- /dev/null +++ b/cloudstack/resource_cloudstack_autoscale_vm_group.go @@ -0,0 +1,286 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package cloudstack + +import ( + "fmt" + "log" + "strings" + + "github.com/apache/cloudstack-go/v2/cloudstack" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceCloudStackAutoScaleVMGroup() *schema.Resource { + return &schema.Resource{ + Create: resourceCloudStackAutoScaleVMGroupCreate, + Read: resourceCloudStackAutoScaleVMGroupRead, + Update: resourceCloudStackAutoScaleVMGroupUpdate, + Delete: resourceCloudStackAutoScaleVMGroupDelete, + + Schema: map[string]*schema.Schema{ + "lbrule_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "the ID of the load balancer rule", + }, + + "name": { + Type: schema.TypeString, + Optional: true, + Description: "the name of the autoscale vmgroup", + }, + + "min_members": { + Type: schema.TypeInt, + Required: true, + Description: "the minimum number of members in the vmgroup, the number of instances in the vm group will be equal to or more than this number", + }, + + "max_members": { + Type: schema.TypeInt, + Required: true, + Description: "the maximum number of members in the vmgroup, The number of instances in the vm group will be equal to or less than this number", + }, + + "interval": { + Type: schema.TypeInt, + Optional: true, + Description: "the frequency in which the performance counters to be collected", + }, + + "scaleup_policy_ids": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Required: true, + Description: "list of scaleup autoscale policies", + }, + + "scaledown_policy_ids": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Required: true, + Description: "list of scaledown autoscale policies", + }, + + "vm_profile_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "the autoscale profile that contains information about the vms in the vm group", + }, + + "display": { + Type: schema.TypeBool, + Optional: true, + Description: "an optional field, whether to the display the group to the end user or not", + }, + }, + } +} + +func resourceCloudStackAutoScaleVMGroupCreate(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + lbruleid := d.Get("lbrule_id").(string) + minmembers := d.Get("min_members").(int) + maxmembers := d.Get("max_members").(int) + vmprofileid := d.Get("vm_profile_id").(string) + + scaleUpPolicyIds := []string{} + if v, ok := d.GetOk("scaleup_policy_ids"); ok { + scaleUpSet := v.(*schema.Set) + for _, id := range scaleUpSet.List() { + scaleUpPolicyIds = append(scaleUpPolicyIds, id.(string)) + } + } + + scaleDownPolicyIds := []string{} + if v, ok := d.GetOk("scaledown_policy_ids"); ok { + scaleDownSet := v.(*schema.Set) + for _, id := range scaleDownSet.List() { + scaleDownPolicyIds = append(scaleDownPolicyIds, id.(string)) + } + } + + p := cs.AutoScale.NewCreateAutoScaleVmGroupParams(lbruleid, maxmembers, minmembers, scaleDownPolicyIds, scaleUpPolicyIds, vmprofileid) + + if v, ok := d.GetOk("name"); ok { + p.SetName(v.(string)) + } + + if v, ok := d.GetOk("interval"); ok { + p.SetInterval(v.(int)) + } + + if v, ok := d.GetOk("display"); ok { + p.SetFordisplay(v.(bool)) + } + + log.Printf("[DEBUG] Creating autoscale VM group") + resp, err := cs.AutoScale.CreateAutoScaleVmGroup(p) + if err != nil { + return fmt.Errorf("Error creating autoscale VM group: %s", err) + } + + d.SetId(resp.Id) + log.Printf("[DEBUG] Autoscale VM group created with ID: %s", resp.Id) + + return resourceCloudStackAutoScaleVMGroupRead(d, meta) +} + +func resourceCloudStackAutoScaleVMGroupRead(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + p := cs.AutoScale.NewListAutoScaleVmGroupsParams() + p.SetId(d.Id()) + + resp, err := cs.AutoScale.ListAutoScaleVmGroups(p) + if err != nil { + return fmt.Errorf("Error retrieving autoscale VM group: %s", err) + } + + if resp.Count == 0 { + log.Printf("[DEBUG] Autoscale VM group %s no longer exists", d.Id()) + d.SetId("") + return nil + } + + group := resp.AutoScaleVmGroups[0] + d.Set("name", group.Name) + d.Set("lbrule_id", group.Lbruleid) + d.Set("min_members", group.Minmembers) + d.Set("max_members", group.Maxmembers) + d.Set("interval", group.Interval) + d.Set("vm_profile_id", group.Vmprofileid) + d.Set("display", group.Fordisplay) + + scaleUpPolicyIds := schema.NewSet(schema.HashString, []interface{}{}) + if group.Scaleuppolicies != nil { + for _, policyId := range group.Scaleuppolicies { + scaleUpPolicyIds.Add(policyId) + } + } + d.Set("scaleup_policy_ids", scaleUpPolicyIds) + + scaleDownPolicyIds := schema.NewSet(schema.HashString, []interface{}{}) + if group.Scaledownpolicies != nil { + for _, policyId := range group.Scaledownpolicies { + scaleDownPolicyIds.Add(policyId) + } + } + d.Set("scaledown_policy_ids", scaleDownPolicyIds) + + return nil +} + +func resourceCloudStackAutoScaleVMGroupUpdate(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + if d.HasChange("name") || d.HasChange("min_members") || d.HasChange("max_members") || + d.HasChange("interval") || d.HasChange("scaleup_policy_ids") || + d.HasChange("scaledown_policy_ids") || d.HasChange("display") { + + log.Printf("[DEBUG] Updating autoscale VM group: %s", d.Id()) + + p := cs.AutoScale.NewUpdateAutoScaleVmGroupParams(d.Id()) + + if d.HasChange("name") { + if v, ok := d.GetOk("name"); ok { + p.SetName(v.(string)) + } + } + + if d.HasChange("min_members") { + minmembers := d.Get("min_members").(int) + p.SetMinmembers(minmembers) + } + + if d.HasChange("max_members") { + maxmembers := d.Get("max_members").(int) + p.SetMaxmembers(maxmembers) + } + + if d.HasChange("interval") { + if v, ok := d.GetOk("interval"); ok { + p.SetInterval(v.(int)) + } + } + + if d.HasChange("scaleup_policy_ids") { + scaleUpPolicyIds := []string{} + if v, ok := d.GetOk("scaleup_policy_ids"); ok { + scaleUpSet := v.(*schema.Set) + for _, id := range scaleUpSet.List() { + scaleUpPolicyIds = append(scaleUpPolicyIds, id.(string)) + } + } + p.SetScaleuppolicyids(scaleUpPolicyIds) + } + + if d.HasChange("scaledown_policy_ids") { + scaleDownPolicyIds := []string{} + if v, ok := d.GetOk("scaledown_policy_ids"); ok { + scaleDownSet := v.(*schema.Set) + for _, id := range scaleDownSet.List() { + scaleDownPolicyIds = append(scaleDownPolicyIds, id.(string)) + } + } + p.SetScaledownpolicyids(scaleDownPolicyIds) + } + + if d.HasChange("display") { + if v, ok := d.GetOk("display"); ok { + p.SetFordisplay(v.(bool)) + } + } + + _, err := cs.AutoScale.UpdateAutoScaleVmGroup(p) + if err != nil { + return fmt.Errorf("Error updating autoscale VM group: %s", err) + } + + log.Printf("[DEBUG] Autoscale VM group updated successfully: %s", d.Id()) + } + + return resourceCloudStackAutoScaleVMGroupRead(d, meta) +} + +func resourceCloudStackAutoScaleVMGroupDelete(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + p := cs.AutoScale.NewDeleteAutoScaleVmGroupParams(d.Id()) + + log.Printf("[DEBUG] Deleting autoscale VM group: %s", d.Id()) + _, err := cs.AutoScale.DeleteAutoScaleVmGroup(p) + if err != nil { + // This is a very poor way to be told the ID does no longer exist :( + if strings.Contains(err.Error(), fmt.Sprintf( + "Invalid parameter id value=%s due to incorrect long value format, "+ + "or entity does not exist", d.Id())) { + return nil + } + + return fmt.Errorf("Error deleting autoscale VM group: %s", err) + } + + return nil +} diff --git a/cloudstack/resource_cloudstack_autoscale_vm_profile.go b/cloudstack/resource_cloudstack_autoscale_vm_profile.go index 04fc5d5..5c2cf4c 100644 --- a/cloudstack/resource_cloudstack_autoscale_vm_profile.go +++ b/cloudstack/resource_cloudstack_autoscale_vm_profile.go @@ -38,33 +38,95 @@ func resourceCloudStackAutoScaleVMProfile() *schema.Resource { Schema: map[string]*schema.Schema{ "service_offering": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "the service offering of the auto deployed virtual machine", }, "template": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + Description: "the template of the auto deployed virtual machine", }, "zone": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "availability zone for the auto deployed virtual machine", }, "destroy_vm_grace_period": { - Type: schema.TypeString, - Optional: true, - Computed: true, + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "the time allowed for existing connections to get closed before a vm is expunged", }, "other_deploy_params": { - Type: schema.TypeMap, - Optional: true, - Computed: true, - ForceNew: true, + Type: schema.TypeMap, + Optional: true, + Computed: true, + ForceNew: true, + Description: "parameters other than zoneId/serviceOfferringId/templateId of the auto deployed virtual machine", + }, + + "counter_param_list": { + Type: schema.TypeMap, + Optional: true, + Description: "counterparam list. Example: counterparam[0].name=snmpcommunity&counterparam[0].value=public&counterparam[1].name=snmpport&counterparam[1].value=161", + }, + + "user_data": { + Type: schema.TypeString, + Optional: true, + Description: "an optional binary data that can be sent to the virtual machine upon a successful deployment. This binary data must be base64 encoded before adding it to the request.", + }, + + "user_data_id": { + Type: schema.TypeString, + Optional: true, + Description: "the ID of the Userdata", + }, + + "user_data_details": { + Type: schema.TypeMap, + Optional: true, + Description: "used to specify the parameters values for the variables in userdata", + }, + + "autoscale_user_id": { + Type: schema.TypeString, + Optional: true, + Description: "the ID of the user used to launch and destroy the VMs", + }, + + "display": { + Type: schema.TypeBool, + Optional: true, + Description: "an optional field, whether to the display the profile to the end user or not", + }, + + "account_name": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: "account that will own the autoscale VM profile", + }, + + "project_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: "an optional project for the autoscale VM profile", + }, + + "domain_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: "domain ID of the account owning a autoscale VM profile", }, "metadata": metadataSchema(), @@ -75,19 +137,16 @@ func resourceCloudStackAutoScaleVMProfile() *schema.Resource { func resourceCloudStackAutoScaleVMProfileCreate(d *schema.ResourceData, meta interface{}) error { cs := meta.(*cloudstack.CloudStackClient) - // Retrieve the service_offering ID serviceofferingid, e := retrieveID(cs, "service_offering", d.Get("service_offering").(string)) if e != nil { return e.Error() } - // Retrieve the zone ID zoneid, e := retrieveID(cs, "zone", d.Get("zone").(string)) if e != nil { return e.Error() } - // Retrieve the template ID templateid, e := retrieveTemplateID(cs, zoneid, d.Get("template").(string)) if e != nil { return e.Error() @@ -111,7 +170,50 @@ func resourceCloudStackAutoScaleVMProfileCreate(d *schema.ResourceData, meta int p.SetOtherdeployparams(nv) } - // Create the new vm profile + if v, ok := d.GetOk("counter_param_list"); ok { + nv := make(map[string]string) + for k, v := range v.(map[string]interface{}) { + nv[k] = v.(string) + } + p.SetCounterparam(nv) + } + + if v, ok := d.GetOk("user_data"); ok { + p.SetUserdata(v.(string)) + } + + if v, ok := d.GetOk("user_data_id"); ok { + p.SetUserdataid(v.(string)) + } + + if v, ok := d.GetOk("user_data_details"); ok { + nv := make(map[string]string) + for k, v := range v.(map[string]interface{}) { + nv[k] = v.(string) + } + p.SetUserdatadetails(nv) + } + + if v, ok := d.GetOk("autoscale_user_id"); ok { + p.SetAutoscaleuserid(v.(string)) + } + + if v, ok := d.GetOk("display"); ok { + p.SetFordisplay(v.(bool)) + } + + if v, ok := d.GetOk("account_name"); ok { + p.SetAccount(v.(string)) + } + + if v, ok := d.GetOk("project_id"); ok { + p.SetProjectid(v.(string)) + } + + if v, ok := d.GetOk("domain_id"); ok { + p.SetDomainid(v.(string)) + } + r, err := cs.AutoScale.CreateAutoScaleVmProfile(p) if err != nil { return fmt.Errorf("Error creating AutoScaleVmProfile %s: %s", d.Id(), err) @@ -119,12 +221,11 @@ func resourceCloudStackAutoScaleVMProfileCreate(d *schema.ResourceData, meta int d.SetId(r.Id) - // Set metadata if necessary if err = setMetadata(cs, d, "AutoScaleVmProfile"); err != nil { return fmt.Errorf("Error setting metadata on the AutoScaleVmProfile %s: %s", d.Id(), err) } - return nil + return resourceCloudStackAutoScaleVMProfileRead(d, meta) } func resourceCloudStackAutoScaleVMProfileRead(d *schema.ResourceData, meta interface{}) error { @@ -168,6 +269,36 @@ func resourceCloudStackAutoScaleVMProfileRead(d *schema.ResourceData, meta inter d.Set("other_deploy_params", p.Otherdeployparams) } + if p.Userdata != "" { + d.Set("user_data", p.Userdata) + } + + if p.Userdataid != "" { + d.Set("user_data_id", p.Userdataid) + } + + if p.Userdatadetails != "" { + d.Set("user_data_details", map[string]interface{}{}) + } + + if p.Autoscaleuserid != "" { + d.Set("autoscale_user_id", p.Autoscaleuserid) + } + + d.Set("display", p.Fordisplay) + + if p.Account != "" { + d.Set("account_name", p.Account) + } + + if p.Projectid != "" { + d.Set("project_id", p.Projectid) + } + + if p.Domainid != "" { + d.Set("domain_id", p.Domainid) + } + metadata, err := getMetadata(cs, d, "AutoScaleVmProfile") if err != nil { return err @@ -180,7 +311,6 @@ func resourceCloudStackAutoScaleVMProfileRead(d *schema.ResourceData, meta inter func resourceCloudStackAutoScaleVMProfileUpdate(d *schema.ResourceData, meta interface{}) error { cs := meta.(*cloudstack.CloudStackClient) - // Create a new parameter struct p := cs.AutoScale.NewUpdateAutoScaleVmProfileParams(d.Id()) if d.HasChange("template") { @@ -196,11 +326,57 @@ func resourceCloudStackAutoScaleVMProfileUpdate(d *schema.ResourceData, meta int } if d.HasChange("destroy_vm_grace_period") { - duration, err := time.ParseDuration(d.Get("destroy_vm_grace_period").(string)) - if err != nil { - return err + if v, ok := d.GetOk("destroy_vm_grace_period"); ok { + duration, err := time.ParseDuration(v.(string)) + if err != nil { + return err + } + p.SetExpungevmgraceperiod(int(duration.Seconds())) + } + } + + if d.HasChange("counter_param_list") { + if v, ok := d.GetOk("counter_param_list"); ok { + nv := make(map[string]string) + for k, v := range v.(map[string]interface{}) { + nv[k] = v.(string) + } + p.SetCounterparam(nv) + } + } + + if d.HasChange("user_data") { + if v, ok := d.GetOk("user_data"); ok { + p.SetUserdata(v.(string)) + } + } + + if d.HasChange("user_data_id") { + if v, ok := d.GetOk("user_data_id"); ok { + p.SetUserdataid(v.(string)) + } + } + + if d.HasChange("user_data_details") { + if v, ok := d.GetOk("user_data_details"); ok { + nv := make(map[string]string) + for k, v := range v.(map[string]interface{}) { + nv[k] = v.(string) + } + p.SetUserdatadetails(nv) + } + } + + if d.HasChange("autoscale_user_id") { + if v, ok := d.GetOk("autoscale_user_id"); ok { + p.SetAutoscaleuserid(v.(string)) + } + } + + if d.HasChange("display") { + if v, ok := d.GetOk("display"); ok { + p.SetFordisplay(v.(bool)) } - p.SetExpungevmgraceperiod(int(duration.Seconds())) } _, err := cs.AutoScale.UpdateAutoScaleVmProfile(p) @@ -220,10 +396,8 @@ func resourceCloudStackAutoScaleVMProfileUpdate(d *schema.ResourceData, meta int func resourceCloudStackAutoScaleVMProfileDelete(d *schema.ResourceData, meta interface{}) error { cs := meta.(*cloudstack.CloudStackClient) - // Create a new parameter struct p := cs.AutoScale.NewDeleteAutoScaleVmProfileParams(d.Id()) - // Delete the template log.Printf("[INFO] Deleting AutoScaleVmProfile: %s", d.Id()) _, err := cs.AutoScale.DeleteAutoScaleVmProfile(p) if err != nil { diff --git a/cloudstack/resource_cloudstack_condition.go b/cloudstack/resource_cloudstack_condition.go new file mode 100644 index 0000000..7af09a1 --- /dev/null +++ b/cloudstack/resource_cloudstack_condition.go @@ -0,0 +1,172 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package cloudstack + +import ( + "fmt" + "log" + + "github.com/apache/cloudstack-go/v2/cloudstack" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceCloudStackCondition() *schema.Resource { + return &schema.Resource{ + Create: resourceCloudStackConditionCreate, + Read: resourceCloudStackConditionRead, + Update: resourceCloudStackConditionUpdate, + Delete: resourceCloudStackConditionDelete, + + Schema: map[string]*schema.Schema{ + "counter_id": { + Type: schema.TypeString, + Required: true, + Description: "The ID of the counter to be used in the condition.", + ForceNew: true, + }, + "relational_operator": { + Type: schema.TypeString, + Required: true, + Description: "Relational Operator to be used with threshold. Valid values are EQ, GT, LT, GE, LE.", + }, + "threshold": { + Type: schema.TypeFloat, + Required: true, + Description: "Value for which the Counter will be evaluated with the Operator selected.", + }, + "account_name": { + Type: schema.TypeString, + Required: true, + Description: "the account of the condition. Must be used with the domainId parameter.", + ForceNew: true, + }, + "domain_id": { + Type: schema.TypeString, + Required: true, + Description: "the domain ID of the account.", + ForceNew: true, + }, + "project_id": { + Type: schema.TypeString, + Optional: true, + Description: "optional project for the condition", + ForceNew: true, + }, + }, + } +} + +func resourceCloudStackConditionRead(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + p := cs.AutoScale.NewListConditionsParams() + p.SetId(d.Id()) + + resp, err := cs.AutoScale.ListConditions(p) + if err != nil { + return fmt.Errorf("Error retrieving condition: %s", err) + } + + if resp.Count == 0 { + log.Printf("[DEBUG] Condition %s no longer exists", d.Id()) + d.SetId("") + return nil + } + + condition := resp.Conditions[0] + d.Set("counter_id", condition.Counterid) + d.Set("relational_operator", condition.Relationaloperator) + d.Set("threshold", condition.Threshold) + d.Set("account_name", condition.Account) + d.Set("domain_id", condition.Domainid) + if condition.Projectid != "" { + d.Set("project_id", condition.Projectid) + } + + return nil +} + +func resourceCloudStackConditionUpdate(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + if d.HasChange("relational_operator") || d.HasChange("threshold") { + log.Printf("[DEBUG] Updating condition: %s", d.Id()) + + relationaloperator := d.Get("relational_operator").(string) + threshold := d.Get("threshold").(float64) + + p := cs.AutoScale.NewUpdateConditionParams(d.Id(), relationaloperator, int64(threshold)) + + _, err := cs.AutoScale.UpdateCondition(p) + if err != nil { + return fmt.Errorf("Error updating condition: %s", err) + } + + log.Printf("[DEBUG] Condition updated successfully: %s", d.Id()) + } + + return resourceCloudStackConditionRead(d, meta) +} + +func resourceCloudStackConditionDelete(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + p := cs.AutoScale.NewDeleteConditionParams(d.Id()) + + log.Printf("[DEBUG] Deleting condition: %s", d.Id()) + _, err := cs.AutoScale.DeleteCondition(p) + if err != nil { + return fmt.Errorf("Error deleting condition: %s", err) + } + + return nil +} +func resourceCloudStackConditionCreate(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + counterid := d.Get("counter_id") + relationaloperator := d.Get("relational_operator").(string) + threshold := d.Get("threshold").(float64) + + account, accountOk := d.GetOk("account_name") + domainid, domainOk := d.GetOk("domain_id") + + if !accountOk || !domainOk { + return fmt.Errorf("account_name and domain_id are required fields") + } + + p := cs.AutoScale.NewCreateConditionParams(counterid.(string), relationaloperator, int64(threshold)) + p.SetAccount(account.(string)) + p.SetDomainid(domainid.(string)) + + if v, ok := d.GetOk("project_id"); ok { + p.SetProjectid(v.(string)) + } + + log.Printf("[DEBUG] Creating condition") + resp, err := cs.AutoScale.CreateCondition(p) + if err != nil { + return fmt.Errorf("Error creating condition: %s", err) + } + + d.SetId(resp.Id) + log.Printf("[DEBUG] Condition created with ID: %s", resp.Id) + + return resourceCloudStackConditionRead(d, meta) +} diff --git a/cloudstack/resource_cloudstack_counter.go b/cloudstack/resource_cloudstack_counter.go new file mode 100644 index 0000000..ea3f70f --- /dev/null +++ b/cloudstack/resource_cloudstack_counter.go @@ -0,0 +1,126 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package cloudstack + +import ( + "fmt" + "log" + + "github.com/apache/cloudstack-go/v2/cloudstack" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceCloudStackCounter() *schema.Resource { + return &schema.Resource{ + Create: resourceCloudStackCounterCreate, + Read: resourceCloudStackCounterRead, + Delete: resourceCloudStackCounterDelete, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: "Name of the counter", + ForceNew: true, + }, + + "source": { + Type: schema.TypeString, + Required: true, + Description: "Source of the counter", + ForceNew: true, + }, + "value": { + Type: schema.TypeString, + Required: true, + Description: "Value of the counter e.g. oid in case of snmp", + ForceNew: true, + }, + "provider": { + Type: schema.TypeString, + Required: true, + Description: "Provider of the counter", + ForceNew: true, + }, + }, + } +} + +func resourceCloudStackCounterCreate(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + name := d.Get("name").(string) + source := d.Get("source").(string) + value := d.Get("value").(string) + provider := d.Get("provider").(string) + + p := cs.AutoScale.NewCreateCounterParams(name, provider, source, value) + + log.Printf("[DEBUG] Creating counter: %s", name) + resp, err := cs.AutoScale.CreateCounter(p) + if err != nil { + return fmt.Errorf("Error creating counter: %s", err) + } + + d.SetId(resp.Id) + log.Printf("[DEBUG] Counter created with ID: %s", resp.Id) + + return resourceCloudStackCounterRead(d, meta) +} + +func resourceCloudStackCounterRead(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + p := cs.AutoScale.NewListCountersParams() + p.SetId(d.Id()) + + resp, err := cs.AutoScale.ListCounters(p) + if err != nil { + return fmt.Errorf("Error retrieving counter: %s", err) + } + + if resp.Count == 0 { + log.Printf("[DEBUG] Counter %s no longer exists", d.Id()) + d.SetId("") + return nil + } + + counter := resp.Counters[0] + d.Set("name", counter.Name) + d.Set("source", counter.Source) + d.Set("value", counter.Value) + d.Set("provider", counter.Provider) + + return nil +} + +func resourceCloudStackCounterDelete(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + p := cs.AutoScale.NewDeleteCounterParams(d.Id()) + + log.Printf("[DEBUG] Deleting counter: %s", d.Id()) + _, err := cs.AutoScale.DeleteCounter(p) + if err != nil { + return fmt.Errorf("Error deleting counter: %s", err) + } + + return nil +}