diff --git a/cloudstack/data_source_cloudstack_autoscale_policy.go b/cloudstack/data_source_cloudstack_autoscale_policy.go new file mode 100644 index 00000000..c8bea3c9 --- /dev/null +++ b/cloudstack/data_source_cloudstack_autoscale_policy.go @@ -0,0 +1,152 @@ +// +// 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 dataSourceCloudstackAutoscalePolicy() *schema.Resource { + return &schema.Resource{ + Read: dataSourceCloudstackAutoscalePolicyRead, + + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "action": { + Type: schema.TypeString, + Computed: true, + }, + + "duration": { + Type: schema.TypeInt, + Computed: true, + }, + + "quiet_time": { + Type: schema.TypeInt, + Computed: true, + }, + + "condition_ids": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "account_name": { + Type: schema.TypeString, + Computed: true, + }, + + "domain_id": { + Type: schema.TypeString, + Computed: true, + }, + + "project_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceCloudstackAutoscalePolicyRead(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + id, idOk := d.GetOk("id") + name, nameOk := d.GetOk("name") + + if !idOk && !nameOk { + return fmt.Errorf("either 'id' or 'name' must be specified") + } + + var policy *cloudstack.AutoScalePolicy + + if idOk { + p := cs.AutoScale.NewListAutoScalePoliciesParams() + p.SetId(id.(string)) + + resp, err := cs.AutoScale.ListAutoScalePolicies(p) + if err != nil { + return fmt.Errorf("failed to list autoscale policies: %s", err) + } + + if resp.Count == 0 { + return fmt.Errorf("autoscale policy with ID %s not found", id.(string)) + } + + policy = resp.AutoScalePolicies[0] + } else { + p := cs.AutoScale.NewListAutoScalePoliciesParams() + + resp, err := cs.AutoScale.ListAutoScalePolicies(p) + if err != nil { + return fmt.Errorf("failed to list autoscale policies: %s", err) + } + + for _, pol := range resp.AutoScalePolicies { + if pol.Name == name.(string) { + policy = pol + break + } + } + + if policy == nil { + return fmt.Errorf("autoscale policy with name %s not found", name.(string)) + } + } + + log.Printf("[DEBUG] Found autoscale policy: %s", policy.Name) + + d.SetId(policy.Id) + d.Set("name", policy.Name) + d.Set("action", policy.Action) + d.Set("duration", policy.Duration) + d.Set("quiet_time", policy.Quiettime) + d.Set("account_name", policy.Account) + d.Set("domain_id", policy.Domainid) + if policy.Projectid != "" { + d.Set("project_id", policy.Projectid) + } + + conditionIds := make([]string, len(policy.Conditions)) + for i, condition := range policy.Conditions { + conditionIds[i] = condition.Id + } + d.Set("condition_ids", conditionIds) + + return nil +} diff --git a/cloudstack/data_source_cloudstack_autoscale_vm_group.go b/cloudstack/data_source_cloudstack_autoscale_vm_group.go new file mode 100644 index 00000000..135cd4c3 --- /dev/null +++ b/cloudstack/data_source_cloudstack_autoscale_vm_group.go @@ -0,0 +1,188 @@ +// +// 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 dataSourceCloudstackAutoscaleVMGroup() *schema.Resource { + return &schema.Resource{ + Read: dataSourceCloudstackAutoscaleVMGroupRead, + + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "lbrule_id": { + Type: schema.TypeString, + Computed: true, + }, + + "min_members": { + Type: schema.TypeInt, + Computed: true, + }, + + "max_members": { + Type: schema.TypeInt, + Computed: true, + }, + + "vm_profile_id": { + Type: schema.TypeString, + Computed: true, + }, + + "scaleup_policy_ids": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "scaledown_policy_ids": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "state": { + Type: schema.TypeString, + Computed: true, + }, + + "interval": { + Type: schema.TypeInt, + Computed: true, + }, + + "available_virtual_machine_count": { + Type: schema.TypeInt, + Computed: true, + }, + + "account_name": { + Type: schema.TypeString, + Computed: true, + }, + + "domain_id": { + Type: schema.TypeString, + Computed: true, + }, + + "project_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceCloudstackAutoscaleVMGroupRead(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + id, idOk := d.GetOk("id") + name, nameOk := d.GetOk("name") + + if !idOk && !nameOk { + return fmt.Errorf("either 'id' or 'name' must be specified") + } + + var group *cloudstack.AutoScaleVmGroup + + if idOk { + p := cs.AutoScale.NewListAutoScaleVmGroupsParams() + p.SetId(id.(string)) + + resp, err := cs.AutoScale.ListAutoScaleVmGroups(p) + if err != nil { + return fmt.Errorf("failed to list autoscale VM groups: %s", err) + } + + if resp.Count == 0 { + return fmt.Errorf("autoscale VM group with ID %s not found", id.(string)) + } + + group = resp.AutoScaleVmGroups[0] + } else { + p := cs.AutoScale.NewListAutoScaleVmGroupsParams() + + resp, err := cs.AutoScale.ListAutoScaleVmGroups(p) + if err != nil { + return fmt.Errorf("failed to list autoscale VM groups: %s", err) + } + + for _, grp := range resp.AutoScaleVmGroups { + if grp.Name == name.(string) { + group = grp + break + } + } + + if group == nil { + return fmt.Errorf("autoscale VM group with name %s not found", name.(string)) + } + } + + log.Printf("[DEBUG] Found autoscale VM group: %s", group.Name) + + d.SetId(group.Id) + 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("vm_profile_id", group.Vmprofileid) + d.Set("state", group.State) + d.Set("interval", group.Interval) + d.Set("available_virtual_machine_count", group.Availablevirtualmachinecount) + d.Set("account_name", group.Account) + d.Set("domain_id", group.Domainid) + if group.Projectid != "" { + d.Set("project_id", group.Projectid) + } + + scaleupPolicyIds := make([]string, len(group.Scaleuppolicies)) + for i, policy := range group.Scaleuppolicies { + scaleupPolicyIds[i] = policy.Id + } + d.Set("scaleup_policy_ids", scaleupPolicyIds) + + scaledownPolicyIds := make([]string, len(group.Scaledownpolicies)) + for i, policy := range group.Scaledownpolicies { + scaledownPolicyIds[i] = policy.Id + } + d.Set("scaledown_policy_ids", scaledownPolicyIds) + + return nil +} diff --git a/cloudstack/data_source_cloudstack_autoscale_vm_profile.go b/cloudstack/data_source_cloudstack_autoscale_vm_profile.go new file mode 100644 index 00000000..47d38c03 --- /dev/null +++ b/cloudstack/data_source_cloudstack_autoscale_vm_profile.go @@ -0,0 +1,148 @@ +// +// 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 dataSourceCloudstackAutoscaleVMProfile() *schema.Resource { + return &schema.Resource{ + Read: dataSourceCloudstackAutoscaleVMProfileRead, + + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "service_offering": { + Type: schema.TypeString, + Computed: true, + }, + + "template": { + Type: schema.TypeString, + Computed: true, + }, + + "zone": { + Type: schema.TypeString, + Computed: true, + }, + + "destroy_vm_grace_period": { + Type: schema.TypeString, + Computed: true, + }, + + "counter_param_list": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "user_data": { + Type: schema.TypeString, + Computed: true, + }, + + "user_data_details": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "account_name": { + Type: schema.TypeString, + Computed: true, + }, + + "domain_id": { + Type: schema.TypeString, + Computed: true, + }, + + "project_id": { + Type: schema.TypeString, + Computed: true, + }, + + "display": { + Type: schema.TypeBool, + Computed: true, + }, + + "other_deploy_params": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + } +} + +func dataSourceCloudstackAutoscaleVMProfileRead(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + id, idOk := d.GetOk("id") + + if !idOk { + return fmt.Errorf("'id' must be specified") + } + + p := cs.AutoScale.NewListAutoScaleVmProfilesParams() + p.SetId(id.(string)) + + resp, err := cs.AutoScale.ListAutoScaleVmProfiles(p) + if err != nil { + return fmt.Errorf("failed to list autoscale VM profiles: %s", err) + } + + if resp.Count == 0 { + return fmt.Errorf("autoscale VM profile with ID %s not found", id.(string)) + } + + profile := resp.AutoScaleVmProfiles[0] + + log.Printf("[DEBUG] Found autoscale VM profile: %s", profile.Id) + + d.SetId(profile.Id) + d.Set("service_offering", profile.Serviceofferingid) + d.Set("template", profile.Templateid) + d.Set("zone", profile.Zoneid) + d.Set("account_name", profile.Account) + d.Set("domain_id", profile.Domainid) + if profile.Projectid != "" { + d.Set("project_id", profile.Projectid) + } + d.Set("display", profile.Fordisplay) + + if profile.Userdata != "" { + d.Set("user_data", profile.Userdata) + } + + return nil +} diff --git a/cloudstack/data_source_cloudstack_condition.go b/cloudstack/data_source_cloudstack_condition.go new file mode 100644 index 00000000..d901f286 --- /dev/null +++ b/cloudstack/data_source_cloudstack_condition.go @@ -0,0 +1,110 @@ +// +// 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 dataSourceCloudstackCondition() *schema.Resource { + return &schema.Resource{ + Read: dataSourceCloudstackConditionRead, + + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "counter_id": { + Type: schema.TypeString, + Computed: true, + }, + + "relational_operator": { + Type: schema.TypeString, + Computed: true, + }, + + "threshold": { + Type: schema.TypeFloat, + Computed: true, + }, + + "account_name": { + Type: schema.TypeString, + Computed: true, + }, + + "domain_id": { + Type: schema.TypeString, + Computed: true, + }, + + "project_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceCloudstackConditionRead(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + id, idOk := d.GetOk("id") + + if !idOk { + return fmt.Errorf("'id' must be specified") + } + + p := cs.AutoScale.NewListConditionsParams() + p.SetId(id.(string)) + + resp, err := cs.AutoScale.ListConditions(p) + if err != nil { + return fmt.Errorf("failed to list conditions: %s", err) + } + + if resp.Count == 0 { + return fmt.Errorf("condition with ID %s not found", id.(string)) + } + + condition := resp.Conditions[0] + + log.Printf("[DEBUG] Found condition: %s", condition.Id) + + d.SetId(condition.Id) + 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 +} diff --git a/cloudstack/data_source_cloudstack_counter.go b/cloudstack/data_source_cloudstack_counter.go new file mode 100644 index 00000000..dab1e44a --- /dev/null +++ b/cloudstack/data_source_cloudstack_counter.go @@ -0,0 +1,122 @@ +// +// 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 dataSourceCloudstackCounter() *schema.Resource { + return &schema.Resource{ + Read: dataSourceCloudstackCounterRead, + + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "source": { + Type: schema.TypeString, + Computed: true, + }, + + "value": { + Type: schema.TypeString, + Computed: true, + }, + + "counter_provider": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceCloudstackCounterRead(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + id, idOk := d.GetOk("id") + name, nameOk := d.GetOk("name") + + if !idOk && !nameOk { + return fmt.Errorf("either 'id' or 'name' must be specified") + } + + var counter *cloudstack.Counter + + if idOk { + // Get counter by ID + p := cs.AutoScale.NewListCountersParams() + p.SetId(id.(string)) + + resp, err := cs.AutoScale.ListCounters(p) + if err != nil { + return fmt.Errorf("failed to list counters: %s", err) + } + + if resp.Count == 0 { + return fmt.Errorf("counter with ID %s not found", id.(string)) + } + + counter = resp.Counters[0] + } else { + // Get counter by name + p := cs.AutoScale.NewListCountersParams() + + resp, err := cs.AutoScale.ListCounters(p) + if err != nil { + return fmt.Errorf("failed to list counters: %s", err) + } + + for _, c := range resp.Counters { + if c.Name == name.(string) { + counter = c + break + } + } + + if counter == nil { + return fmt.Errorf("counter with name %s not found", name.(string)) + } + } + + log.Printf("[DEBUG] Found counter: %s", counter.Name) + + d.SetId(counter.Id) + d.Set("name", counter.Name) + d.Set("source", counter.Source) + d.Set("value", counter.Value) + d.Set("counter_provider", counter.Provider) + + return nil +} diff --git a/cloudstack/provider.go b/cloudstack/provider.go index 17e0c226..e353a205 100644 --- a/cloudstack/provider.go +++ b/cloudstack/provider.go @@ -78,28 +78,37 @@ func Provider() *schema.Provider { }, DataSourcesMap: map[string]*schema.Resource{ - "cloudstack_template": dataSourceCloudstackTemplate(), - "cloudstack_ssh_keypair": dataSourceCloudstackSSHKeyPair(), - "cloudstack_instance": dataSourceCloudstackInstance(), - "cloudstack_network_offering": dataSourceCloudstackNetworkOffering(), - "cloudstack_zone": dataSourceCloudStackZone(), - "cloudstack_service_offering": dataSourceCloudstackServiceOffering(), - "cloudstack_volume": dataSourceCloudstackVolume(), - "cloudstack_vpc": dataSourceCloudstackVPC(), - "cloudstack_ipaddress": dataSourceCloudstackIPAddress(), - "cloudstack_user": dataSourceCloudstackUser(), - "cloudstack_vpn_connection": dataSourceCloudstackVPNConnection(), - "cloudstack_pod": dataSourceCloudstackPod(), - "cloudstack_domain": dataSourceCloudstackDomain(), - "cloudstack_physicalnetwork": dataSourceCloudStackPhysicalNetwork(), - "cloudstack_role": dataSourceCloudstackRole(), + "cloudstack_autoscale_policy": dataSourceCloudstackAutoscalePolicy(), + "cloudstack_autoscale_vm_group": dataSourceCloudstackAutoscaleVMGroup(), + "cloudstack_autoscale_vm_profile": dataSourceCloudstackAutoscaleVMProfile(), + "cloudstack_condition": dataSourceCloudstackCondition(), + "cloudstack_counter": dataSourceCloudstackCounter(), + "cloudstack_template": dataSourceCloudstackTemplate(), + "cloudstack_ssh_keypair": dataSourceCloudstackSSHKeyPair(), + "cloudstack_instance": dataSourceCloudstackInstance(), + "cloudstack_network_offering": dataSourceCloudstackNetworkOffering(), + "cloudstack_zone": dataSourceCloudStackZone(), + "cloudstack_service_offering": dataSourceCloudstackServiceOffering(), + "cloudstack_volume": dataSourceCloudstackVolume(), + "cloudstack_vpc": dataSourceCloudstackVPC(), + "cloudstack_ipaddress": dataSourceCloudstackIPAddress(), + "cloudstack_user": dataSourceCloudstackUser(), + "cloudstack_vpn_connection": dataSourceCloudstackVPNConnection(), + "cloudstack_pod": dataSourceCloudstackPod(), + "cloudstack_physicalnetwork": dataSourceCloudStackPhysicalNetwork(), + "cloudstack_domain": dataSourceCloudstackDomain(), + "cloudstack_role": dataSourceCloudstackRole(), }, ResourcesMap: map[string]*schema.Resource{ "cloudstack_affinity_group": resourceCloudStackAffinityGroup(), "cloudstack_attach_volume": resourceCloudStackAttachVolume(), + "cloudstack_autoscale_policy": resourceCloudStackAutoScalePolicy(), + "cloudstack_autoscale_vm_group": resourceCloudStackAutoScaleVMGroup(), "cloudstack_autoscale_vm_profile": resourceCloudStackAutoScaleVMProfile(), + "cloudstack_condition": resourceCloudStackCondition(), "cloudstack_configuration": resourceCloudStackConfiguration(), + "cloudstack_counter": resourceCloudStackCounter(), "cloudstack_disk": resourceCloudStackDisk(), "cloudstack_egress_firewall": resourceCloudStackEgressFirewall(), "cloudstack_firewall": resourceCloudStackFirewall(), diff --git a/cloudstack/resource_cloudstack_autoscale_policy.go b/cloudstack/resource_cloudstack_autoscale_policy.go new file mode 100644 index 00000000..3f3fd16a --- /dev/null +++ b/cloudstack/resource_cloudstack_autoscale_policy.go @@ -0,0 +1,206 @@ +// +// 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 _, condition := range policy.Conditions { + var conditionInterface interface{} = condition + switch v := conditionInterface.(type) { + case string: + conditionIds.Add(v) + case map[string]interface{}: + if id, ok := v["id"].(string); ok { + conditionIds.Add(id) + } + default: + log.Printf("[DEBUG] Unexpected condition type: %T, value: %+v", condition, condition) + conditionIds.Add(fmt.Sprintf("%v", condition)) + } + } + 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 00000000..ddbb6757 --- /dev/null +++ b/cloudstack/resource_cloudstack_autoscale_vm_group.go @@ -0,0 +1,312 @@ +// +// 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) + + if v, ok := d.GetOk("name"); ok { + d.Set("name", v.(string)) + } + d.Set("lbrule_id", lbruleid) + d.Set("min_members", minmembers) + d.Set("max_members", maxmembers) + d.Set("vm_profile_id", vmprofileid) + if v, ok := d.GetOk("interval"); ok { + d.Set("interval", v.(int)) + } + if v, ok := d.GetOk("display"); ok { + d.Set("display", v.(bool)) + } + + scaleUpSet := schema.NewSet(schema.HashString, []interface{}{}) + for _, id := range scaleUpPolicyIds { + scaleUpSet.Add(id) + } + d.Set("scaleup_policy_ids", scaleUpSet) + + scaleDownSet := schema.NewSet(schema.HashString, []interface{}{}) + for _, id := range scaleDownPolicyIds { + scaleDownSet.Add(id) + } + d.Set("scaledown_policy_ids", scaleDownSet) + + return nil +} + +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 04fc5d5f..5c2cf4ce 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 00000000..cd74e5cb --- /dev/null +++ b/cloudstack/resource_cloudstack_condition.go @@ -0,0 +1,182 @@ +// +// 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) + + // Set the values directly instead of calling read to avoid JSON unmarshaling issues + d.Set("counter_id", counterid.(string)) + d.Set("relational_operator", relationaloperator) + d.Set("threshold", threshold) + d.Set("account_name", account.(string)) + d.Set("domain_id", domainid.(string)) + if v, ok := d.GetOk("project_id"); ok { + d.Set("project_id", v.(string)) + } + + return nil +} diff --git a/cloudstack/resource_cloudstack_counter.go b/cloudstack/resource_cloudstack_counter.go new file mode 100644 index 00000000..ddd7e9e4 --- /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, + }, + "counter_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("counter_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("counter_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 +}