This is an automated email from the ASF dual-hosted git repository. kiranchavala pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/cloudstack-terraform-provider.git
The following commit(s) were added to refs/heads/main by this push: new 63f866b service offering. sdk framework rewrite (#138) 63f866b is described below commit 63f866bc0f694d8239f69336bd8fb264dd9d683b Author: poddm <8801231+po...@users.noreply.github.com> AuthorDate: Mon Sep 15 23:45:23 2025 -0700 service offering. sdk framework rewrite (#138) * sdk framework rewrite * adding description to schema fields * fixing domainids value error * Adding better cpu_speed description for vmware, xen and kvm * cleaned up comments * Adding service offering details * Adding license header to files. Fixed an issue with setting zone ids to null, moved storage_tags field to nested block disk_offering * add missing set blocks * Update cloudstack/service_offering_schema.go * add documentation --------- Co-authored-by: CodeBleu <400979+codeb...@users.noreply.github.com> --- cloudstack/provider_v6.go | 6 +- .../service_offering_constrained_resource.go | 313 ++++++++++++++++++++ .../service_offering_constrained_resource_test.go | 319 +++++++++++++++++++++ cloudstack/service_offering_fixed_resource.go | 251 ++++++++++++++++ cloudstack/service_offering_fixed_resource_test.go | 239 +++++++++++++++ cloudstack/service_offering_models.go | 86 ++++++ cloudstack/service_offering_schema.go | 248 ++++++++++++++++ .../service_offering_unconstrained_resource.go | 204 +++++++++++++ ...service_offering_unconstrained_resource_test.go | 207 +++++++++++++ cloudstack/service_offering_util.go | 277 ++++++++++++++++++ go.mod | 19 +- go.sum | 33 +-- .../r/service_offering_constrained.html.markdown | 96 +++++++ .../docs/r/service_offering_fixed.html.markdown | 101 +++++++ .../r/service_offering_unconstrained.html.markdown | 95 ++++++ 15 files changed, 2469 insertions(+), 25 deletions(-) diff --git a/cloudstack/provider_v6.go b/cloudstack/provider_v6.go index 23637f5..82c06c0 100644 --- a/cloudstack/provider_v6.go +++ b/cloudstack/provider_v6.go @@ -164,7 +164,11 @@ func (p *CloudstackProvider) ConfigValidators(ctx context.Context) []provider.Co } func (p *CloudstackProvider) Resources(ctx context.Context) []func() resource.Resource { - return []func() resource.Resource{} + return []func() resource.Resource{ + NewserviceOfferingUnconstrainedResource, + NewserviceOfferingConstrainedResource, + NewserviceOfferingFixedResource, + } } func (p *CloudstackProvider) DataSources(ctx context.Context) []func() datasource.DataSource { diff --git a/cloudstack/service_offering_constrained_resource.go b/cloudstack/service_offering_constrained_resource.go new file mode 100644 index 0000000..92c8077 --- /dev/null +++ b/cloudstack/service_offering_constrained_resource.go @@ -0,0 +1,313 @@ +// +// 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 ( + "context" + "fmt" + "strconv" + + "github.com/apache/cloudstack-go/v2/cloudstack" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int32planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +var ( + _ resource.Resource = &serviceOfferingConstrainedResource{} + _ resource.ResourceWithConfigure = &serviceOfferingConstrainedResource{} +) + +func NewserviceOfferingConstrainedResource() resource.Resource { + return &serviceOfferingConstrainedResource{} +} + +type serviceOfferingConstrainedResource struct { + client *cloudstack.CloudStackClient +} + +func (r *serviceOfferingConstrainedResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: serviceOfferingMergeCommonSchema(map[string]schema.Attribute{ + "cpu_speed": schema.Int32Attribute{ + Description: "VMware and Xen based hypervisors this is the CPU speed of the service offering in MHz. For the KVM hypervisor the values of the parameters cpuSpeed and cpuNumber will be used to calculate the `shares` value. This value is used by the KVM hypervisor to calculate how much time the VM will have access to the host's CPU. The `shares` value does not have a unit, and its purpose is being a weight value for the host to compare between its guest VMs. For more information, see h [...] + Required: true, + PlanModifiers: []planmodifier.Int32{ + int32planmodifier.RequiresReplace(), + }, + }, + "max_cpu_number": schema.Int32Attribute{ + Description: "The maximum number of CPUs to be set with Custom Compute Offering", + Required: true, + PlanModifiers: []planmodifier.Int32{ + int32planmodifier.RequiresReplace(), + }, + }, + "max_memory": schema.Int32Attribute{ + Description: "The maximum memory size of the custom service offering in MB", + Required: true, + PlanModifiers: []planmodifier.Int32{ + int32planmodifier.RequiresReplace(), + }, + }, + "min_cpu_number": schema.Int32Attribute{ + Description: "The minimum number of CPUs to be set with Custom Compute Offering", + Required: true, + PlanModifiers: []planmodifier.Int32{ + int32planmodifier.RequiresReplace(), + }, + }, + "min_memory": schema.Int32Attribute{ + Description: "The minimum memory size of the custom service offering in MB", + Required: true, + PlanModifiers: []planmodifier.Int32{ + int32planmodifier.RequiresReplace(), + }, + }, + }), + } +} + +func (r *serviceOfferingConstrainedResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan serviceOfferingConstrainedResourceModel + var planDiskQosHypervisor ServiceOfferingDiskQosHypervisor + var planDiskOffering ServiceOfferingDiskOffering + var planDiskQosStorage ServiceOfferingDiskQosStorage + + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if !plan.ServiceOfferingDiskQosHypervisor.IsNull() { + resp.Diagnostics.Append(plan.ServiceOfferingDiskQosHypervisor.As(ctx, &planDiskQosHypervisor, basetypes.ObjectAsOptions{})...) + } + if !plan.ServiceOfferingDiskOffering.IsNull() { + resp.Diagnostics.Append(plan.ServiceOfferingDiskOffering.As(ctx, &planDiskOffering, basetypes.ObjectAsOptions{})...) + } + if !plan.ServiceOfferingDiskQosStorage.IsNull() { + resp.Diagnostics.Append(plan.ServiceOfferingDiskQosStorage.As(ctx, &planDiskQosStorage, basetypes.ObjectAsOptions{})...) + } + if resp.Diagnostics.HasError() { + return + } + + // common params + params := r.client.ServiceOffering.NewCreateServiceOfferingParams(plan.DisplayText.ValueString(), plan.Name.ValueString()) + plan.commonCreateParams(ctx, params) + planDiskQosHypervisor.commonCreateParams(ctx, params) + planDiskOffering.commonCreateParams(ctx, params) + planDiskQosStorage.commonCreateParams(ctx, params) + + // resource specific params + if !plan.CpuSpeed.IsNull() { + params.SetCpuspeed(int(plan.CpuSpeed.ValueInt32())) + } + if !plan.MaxCpuNumber.IsNull() { + params.SetMaxcpunumber(int(plan.MaxCpuNumber.ValueInt32())) + } + if !plan.MaxMemory.IsNull() { + params.SetMaxmemory(int(plan.MaxMemory.ValueInt32())) + } + if !plan.MinCpuNumber.IsNull() { + params.SetMincpunumber(int(plan.MinCpuNumber.ValueInt32())) + } + if !plan.MinMemory.IsNull() { + params.SetMinmemory(int(plan.MinMemory.ValueInt32())) + } + + // create offering + cs, err := r.client.ServiceOffering.CreateServiceOffering(params) + if err != nil { + resp.Diagnostics.AddError( + "Error creating service offering", + "Could not create constrained offering, unexpected error: "+err.Error(), + ) + return + } + + // + plan.Id = types.StringValue(cs.Id) + + // + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *serviceOfferingConstrainedResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state serviceOfferingConstrainedResourceModel + var stateDiskQosHypervisor ServiceOfferingDiskQosHypervisor + var stateDiskOffering ServiceOfferingDiskOffering + var stateDiskQosStorage ServiceOfferingDiskQosStorage + + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if !state.ServiceOfferingDiskQosHypervisor.IsNull() { + resp.Diagnostics.Append(state.ServiceOfferingDiskQosHypervisor.As(ctx, &stateDiskQosHypervisor, basetypes.ObjectAsOptions{})...) + } + if !state.ServiceOfferingDiskOffering.IsNull() { + resp.Diagnostics.Append(state.ServiceOfferingDiskOffering.As(ctx, &stateDiskOffering, basetypes.ObjectAsOptions{})...) + } + if !state.ServiceOfferingDiskQosStorage.IsNull() { + resp.Diagnostics.Append(state.ServiceOfferingDiskQosStorage.As(ctx, &stateDiskQosStorage, basetypes.ObjectAsOptions{})...) + } + if resp.Diagnostics.HasError() { + return + } + + cs, _, err := r.client.ServiceOffering.GetServiceOfferingByID(state.Id.ValueString()) + if err != nil { + resp.Diagnostics.AddError( + "Error reading service offering", + "Could not read constrained service offering, unexpected error: "+err.Error(), + ) + return + } + + // resource specific + if cs.Cpuspeed > 0 { + state.CpuSpeed = types.Int32Value(int32(cs.Cpuspeed)) + } + if v, found := cs.Serviceofferingdetails["maxcpunumber"]; found { + i, err := strconv.Atoi(v) + if err != nil { + resp.Diagnostics.AddError( + "Error reading service offering", + "Could not read constrained service offering max cpu, unexpected error: "+err.Error(), + ) + return + } + state.MaxCpuNumber = types.Int32Value(int32(i)) + } + if v, found := cs.Serviceofferingdetails["mincpunumber"]; found { + i, err := strconv.Atoi(v) + if err != nil { + resp.Diagnostics.AddError( + "Error reading service offering", + "Could not read constrained service offering min cpu number, unexpected error: "+err.Error(), + ) + return + } + state.MinCpuNumber = types.Int32Value(int32(i)) + } + if v, found := cs.Serviceofferingdetails["maxmemory"]; found { + i, err := strconv.Atoi(v) + if err != nil { + resp.Diagnostics.AddError( + "Error reading service offering", + "Could not read constrained service offering max memory, unexpected error: "+err.Error(), + ) + return + } + state.MaxMemory = types.Int32Value(int32(i)) + } + if v, found := cs.Serviceofferingdetails["minmemory"]; found { + i, err := strconv.Atoi(v) + if err != nil { + resp.Diagnostics.AddError( + "Error reading service offering", + "Could not read constrained service offering min memory, unexpected error: "+err.Error(), + ) + return + } + state.MinMemory = types.Int32Value(int32(i)) + } + + state.commonRead(ctx, cs) + stateDiskQosHypervisor.commonRead(ctx, cs) + stateDiskOffering.commonRead(ctx, cs) + stateDiskQosStorage.commonRead(ctx, cs) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) + +} + +// Update updates the resource and sets the updated Terraform state on success. +func (r *serviceOfferingConstrainedResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var state serviceOfferingConstrainedResourceModel + + resp.Diagnostics.Append(req.Plan.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + params := r.client.ServiceOffering.NewUpdateServiceOfferingParams(state.Id.ValueString()) + state.commonUpdateParams(ctx, params) + + cs, err := r.client.ServiceOffering.UpdateServiceOffering(params) + if err != nil { + resp.Diagnostics.AddError( + "Error updating service offering", + "Could not update constrained service offering, unexpected error: "+err.Error(), + ) + return + } + + state.commonUpdate(ctx, cs) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) +} + +func (r *serviceOfferingConstrainedResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state serviceOfferingConstrainedResourceModel + + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // Delete the service offering + _, err := r.client.ServiceOffering.DeleteServiceOffering(r.client.ServiceOffering.NewDeleteServiceOfferingParams(state.Id.ValueString())) + if err != nil { + resp.Diagnostics.AddError( + "Error deleting service offering", + "Could not delete constrained offering, unexpected error: "+err.Error(), + ) + return + } +} + +func (r *serviceOfferingConstrainedResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + // Add a nil check when handling ProviderData because Terraform + // sets that data after it calls the ConfigureProvider RPC. + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*cloudstack.CloudStackClient) + + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *cloudstack.CloudStackClient, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + return + } + + r.client = client +} + +// Metadata returns the resource type name. +func (r *serviceOfferingConstrainedResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_service_offering_constrained" +} diff --git a/cloudstack/service_offering_constrained_resource_test.go b/cloudstack/service_offering_constrained_resource_test.go new file mode 100644 index 0000000..5368db7 --- /dev/null +++ b/cloudstack/service_offering_constrained_resource_test.go @@ -0,0 +1,319 @@ +// +// 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 ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccServiceOfferingConstrained(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccMuxProvider, + Steps: []resource.TestStep{ + { + Config: testAccServiceOfferingCustomConstrained1, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_constrained.constrained1", "name", "constrained1"), + ), + }, + { + Config: testAccServiceOfferingCustomConstrained1ZoneAll, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_constrained.constrained1", "name", "constrained1"), + ), + }, + { + Config: testAccServiceOfferingCustomConstrained2, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_constrained.constrained2", "name", "constrained2"), + ), + }, + { + Config: testAccServiceOfferingCustomConstrained2_update, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_constrained.constrained2", "name", "constrained2update"), + ), + }, + { + Config: testAccServiceOfferingCustomConstrained_disk, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_constrained.constrained1", "name", "constrained1"), + ), + }, + { + Config: testAccServiceOfferingCustomConstrained_disk_hypervisor, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_constrained.disk_hypervisor", "name", "disk_hypervisor"), + ), + }, + { + Config: testAccServiceOfferingCustomConstrained_disk_storage, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_constrained.disk_storage", "name", "disk_storage"), + ), + }, + }, + }) +} + +const testAccServiceOfferingCustomConstrained1 = ` +resource "cloudstack_zone" "test" { + name = "acctest" + dns1 = "8.8.8.8" + internal_dns1 = "8.8.4.4" + network_type = "Advanced" +} + +resource "cloudstack_service_offering_constrained" "constrained1" { + display_text = "constrained1" + name = "constrained1" + + // compute + cpu_speed = 2500 + max_cpu_number = 10 + min_cpu_number = 2 + + // memory + max_memory = 4096 + min_memory = 1024 + + // other + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + // Feature flags + dynamic_scaling_enabled = false + is_volatile = false + limit_cpu_use = false + offer_ha = false + zone_ids = [cloudstack_zone.test.id] + +} +` + +const testAccServiceOfferingCustomConstrained1ZoneAll = ` +resource "cloudstack_service_offering_constrained" "constrained1" { + display_text = "constrained11" + name = "constrained1" + + // compute + cpu_speed = 2500 + max_cpu_number = 10 + min_cpu_number = 2 + + // memory + max_memory = 4096 + min_memory = 1024 + + // other + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + // Feature flags + dynamic_scaling_enabled = false + is_volatile = false + limit_cpu_use = false + offer_ha = false + zone_ids = [] +} +` + +const testAccServiceOfferingCustomConstrained2 = ` +resource "cloudstack_service_offering_constrained" "constrained2" { + display_text = "constrained2" + name = "constrained2" + + // compute + cpu_speed = 2500 + max_cpu_number = 10 + min_cpu_number = 2 + + // memory + max_memory = 4096 + min_memory = 1024 + + // other + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + // Feature flags + dynamic_scaling_enabled = true + is_volatile = true + limit_cpu_use = true + offer_ha = true +} +` +const testAccServiceOfferingCustomConstrained2_update = ` +resource "cloudstack_service_offering_constrained" "constrained2" { + display_text = "constrained2update" + name = "constrained2update" + + // compute + cpu_speed = 2500 + max_cpu_number = 10 + min_cpu_number = 2 + + // memory + max_memory = 4096 + min_memory = 1024 + + // other + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + // Feature flags + dynamic_scaling_enabled = true + is_volatile = true + limit_cpu_use = true + offer_ha = true +} +` + +const testAccServiceOfferingCustomConstrained_disk = ` +resource "cloudstack_service_offering_constrained" "constrained1" { + display_text = "constrained1" + name = "constrained1" + + // compute + cpu_speed = 2500 + max_cpu_number = 10 + min_cpu_number = 2 + + // memory + max_memory = 4096 + min_memory = 1024 + + // other + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + // Feature flags + dynamic_scaling_enabled = false + is_volatile = false + limit_cpu_use = false + offer_ha = false + + disk_offering = { + storage_type = "local" + sdfjklsdf = "sdfjks" + provisioning_type = "thin" + cache_mode = "none" + root_disk_size = "5" + storage_tags = "FOO" + disk_offering_strictness = false + } +} +` + +const testAccServiceOfferingCustomConstrained_disk_hypervisor = ` +resource "cloudstack_service_offering_constrained" "disk_hypervisor" { + display_text = "disk_hypervisor" + name = "disk_hypervisor" + + // compute + cpu_speed = 2500 + max_cpu_number = 10 + min_cpu_number = 2 + + // memory + max_memory = 4096 + min_memory = 1024 + + // other + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + // Feature flags + dynamic_scaling_enabled = false + is_volatile = false + limit_cpu_use = false + offer_ha = false + + disk_offering = { + storage_type = "local" + provisioning_type = "thin" + cache_mode = "none" + root_disk_size = "5" + storage_tags = "FOO" + disk_offering_strictness = false + } + disk_hypervisor = { + bytes_read_rate = 1024 + bytes_read_rate_max = 1024 + bytes_read_rate_max_length = 1024 + bytes_write_rate = 1024 + bytes_write_rate_max = 1024 + bytes_write_rate_max_length = 1024 + } +} +` + +const testAccServiceOfferingCustomConstrained_disk_storage = ` +resource "cloudstack_service_offering_constrained" "disk_storage" { + display_text = "disk_storage" + name = "disk_storage" + + // compute + cpu_speed = 2500 + max_cpu_number = 10 + min_cpu_number = 2 + + // memory + max_memory = 4096 + min_memory = 1024 + + // other + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + // Feature flags + dynamic_scaling_enabled = false + is_volatile = false + limit_cpu_use = false + offer_ha = false + + disk_offering = { + storage_type = "local" + provisioning_type = "thin" + cache_mode = "none" + root_disk_size = "5" + storage_tags = "FOO" + disk_offering_strictness = false + } + disk_hypervisor = { + bytes_read_rate = 1024 + bytes_read_rate_max = 1024 + bytes_read_rate_max_length = 1024 + bytes_write_rate = 1024 + bytes_write_rate_max = 1024 + bytes_write_rate_max_length = 1024 + } +} +` diff --git a/cloudstack/service_offering_fixed_resource.go b/cloudstack/service_offering_fixed_resource.go new file mode 100644 index 0000000..6b500fd --- /dev/null +++ b/cloudstack/service_offering_fixed_resource.go @@ -0,0 +1,251 @@ +// +// 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 ( + "context" + "fmt" + + "github.com/apache/cloudstack-go/v2/cloudstack" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int32planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +var ( + _ resource.Resource = &serviceOfferingFixedResource{} + _ resource.ResourceWithConfigure = &serviceOfferingFixedResource{} +) + +func NewserviceOfferingFixedResource() resource.Resource { + return &serviceOfferingFixedResource{} +} + +type serviceOfferingFixedResource struct { + client *cloudstack.CloudStackClient +} + +func (r *serviceOfferingFixedResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: serviceOfferingMergeCommonSchema(map[string]schema.Attribute{ + "cpu_number": schema.Int32Attribute{ + Description: "Number of CPU cores", + Required: true, + PlanModifiers: []planmodifier.Int32{ + int32planmodifier.RequiresReplace(), + }, + }, + "cpu_speed": schema.Int32Attribute{ + Description: "the CPU speed of the service offering in MHz. This does not apply to KVM.", + Required: true, + PlanModifiers: []planmodifier.Int32{ + int32planmodifier.RequiresReplace(), + }, + }, + "memory": schema.Int32Attribute{ + Description: "the total memory of the service offering in MB", + Required: true, + PlanModifiers: []planmodifier.Int32{ + int32planmodifier.RequiresReplace(), + }, + }, + }), + } +} + +func (r *serviceOfferingFixedResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan serviceOfferingFixedResourceModel + var planDiskQosHypervisor ServiceOfferingDiskQosHypervisor + var planDiskOffering ServiceOfferingDiskOffering + var planDiskQosStorage ServiceOfferingDiskQosStorage + + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if !plan.ServiceOfferingDiskQosHypervisor.IsNull() { + resp.Diagnostics.Append(plan.ServiceOfferingDiskQosHypervisor.As(ctx, &planDiskQosHypervisor, basetypes.ObjectAsOptions{})...) + } + if !plan.ServiceOfferingDiskOffering.IsNull() { + resp.Diagnostics.Append(plan.ServiceOfferingDiskOffering.As(ctx, &planDiskOffering, basetypes.ObjectAsOptions{})...) + } + if !plan.ServiceOfferingDiskQosStorage.IsNull() { + resp.Diagnostics.Append(plan.ServiceOfferingDiskQosStorage.As(ctx, &planDiskQosStorage, basetypes.ObjectAsOptions{})...) + } + if resp.Diagnostics.HasError() { + return + } + + // cloudstack params + params := r.client.ServiceOffering.NewCreateServiceOfferingParams(plan.DisplayText.ValueString(), plan.Name.ValueString()) + plan.commonCreateParams(ctx, params) + planDiskQosHypervisor.commonCreateParams(ctx, params) + planDiskOffering.commonCreateParams(ctx, params) + planDiskQosStorage.commonCreateParams(ctx, params) + + // resource specific params + if !plan.CpuNumber.IsNull() { + params.SetCpunumber(int(plan.CpuNumber.ValueInt32())) + } + if !plan.CpuSpeed.IsNull() { + params.SetCpuspeed(int(plan.CpuSpeed.ValueInt32())) + } + if !plan.Memory.IsNull() { + params.SetMemory(int(plan.Memory.ValueInt32())) + } + + // create offering + cs, err := r.client.ServiceOffering.CreateServiceOffering(params) + if err != nil { + resp.Diagnostics.AddError( + "Error creating service offering", + "Could not create fixed offering, unexpected error: "+err.Error(), + ) + return + } + + plan.Id = types.StringValue(cs.Id) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *serviceOfferingFixedResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + + var state serviceOfferingFixedResourceModel + var stateDiskQosHypervisor ServiceOfferingDiskQosHypervisor + var stateDiskOffering ServiceOfferingDiskOffering + var stateDiskQosStorage ServiceOfferingDiskQosStorage + + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if !state.ServiceOfferingDiskQosHypervisor.IsNull() { + resp.Diagnostics.Append(state.ServiceOfferingDiskQosHypervisor.As(ctx, &stateDiskQosHypervisor, basetypes.ObjectAsOptions{})...) + } + if !state.ServiceOfferingDiskOffering.IsNull() { + resp.Diagnostics.Append(state.ServiceOfferingDiskOffering.As(ctx, &stateDiskOffering, basetypes.ObjectAsOptions{})...) + } + if !state.ServiceOfferingDiskQosStorage.IsNull() { + resp.Diagnostics.Append(state.ServiceOfferingDiskQosStorage.As(ctx, &stateDiskQosStorage, basetypes.ObjectAsOptions{})...) + } + if resp.Diagnostics.HasError() { + return + } + + cs, _, err := r.client.ServiceOffering.GetServiceOfferingByID(state.Id.ValueString()) + if err != nil { + resp.Diagnostics.AddError( + "Error reading service offering", + "Could not read fixed service offering, unexpected error: "+err.Error(), + ) + return + } + + // resource specific + if cs.Cpunumber > 0 { + state.CpuNumber = types.Int32Value(int32(cs.Cpunumber)) + } + if cs.Cpuspeed > 0 { + state.CpuSpeed = types.Int32Value(int32(cs.Cpuspeed)) + } + if cs.Memory > 0 { + state.Memory = types.Int32Value(int32(cs.Memory)) + } + + state.commonRead(ctx, cs) + stateDiskQosHypervisor.commonRead(ctx, cs) + stateDiskOffering.commonRead(ctx, cs) + stateDiskQosStorage.commonRead(ctx, cs) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) + +} + +// Update updates the resource and sets the updated Terraform state on success. +func (r *serviceOfferingFixedResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var state serviceOfferingFixedResourceModel + + resp.Diagnostics.Append(req.Plan.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + params := r.client.ServiceOffering.NewUpdateServiceOfferingParams(state.Id.ValueString()) + state.commonUpdateParams(ctx, params) + + cs, err := r.client.ServiceOffering.UpdateServiceOffering(params) + if err != nil { + resp.Diagnostics.AddError( + "Error updating fixed service offering", + "Could not update fixed service offering, unexpected error: "+err.Error(), + ) + return + } + + state.commonUpdate(ctx, cs) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) +} + +// Delete deletes the resource and removes the Terraform state on success. +func (r *serviceOfferingFixedResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state serviceOfferingFixedResourceModel + + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // Delete the service offering + _, err := r.client.ServiceOffering.DeleteServiceOffering(r.client.ServiceOffering.NewDeleteServiceOfferingParams(state.Id.ValueString())) + if err != nil { + resp.Diagnostics.AddError( + "Error deleting service offering", + "Could not delete fixed offering, unexpected error: "+err.Error(), + ) + return + } +} + +func (r *serviceOfferingFixedResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + // Add a nil check when handling ProviderData because Terraform + // sets that data after it calls the ConfigureProvider RPC. + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*cloudstack.CloudStackClient) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *cloudstack.CloudStackClient, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + return + } + r.client = client +} + +// Metadata returns the resource type name. +func (r *serviceOfferingFixedResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_service_offering_fixed" +} diff --git a/cloudstack/service_offering_fixed_resource_test.go b/cloudstack/service_offering_fixed_resource_test.go new file mode 100644 index 0000000..438740e --- /dev/null +++ b/cloudstack/service_offering_fixed_resource_test.go @@ -0,0 +1,239 @@ +// +// 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 ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccServiceOfferingFixed(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccMuxProvider, + Steps: []resource.TestStep{ + { + Config: testAccServiceOfferingFixed1, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_fixed.fixed1", "name", "fixed1"), + ), + }, + { + Config: testAccServiceOfferingFixed2, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_fixed.fixed2", "name", "fixed2"), + ), + }, + { + Config: testAccServiceOfferingFixed2_update, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_fixed.fixed2", "name", "fixed2update"), + ), + }, + { + Config: testAccServiceOfferingFixed_disk, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_fixed.disk", "name", "disk"), + ), + }, + { + Config: testAccServiceOfferingFixed_disk_hypervisor, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_fixed.disk_hypervisor", "name", "disk_hypervisor"), + ), + }, + { + Config: testAccServiceOfferingFixed_disk_storage, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_fixed.disk_storage", "name", "disk_storage"), + ), + }, + }, + }) +} + +const testAccServiceOfferingFixed1 = ` +resource "cloudstack_service_offering_fixed" "fixed1" { + display_text = "fixed1" + name = "fixed1" + + // compute + cpu_number = 2 + cpu_speed = 2500 + memory = 2048 + + // other + host_tags = "test0101, test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + dynamic_scaling_enabled = false + is_volatile = false + limit_cpu_use = false + offer_ha = false + +} +` + +const testAccServiceOfferingFixed2 = ` +resource "cloudstack_service_offering_fixed" "fixed2" { + display_text = "fixed2" + name = "fixed2" + + cpu_number = 2 + cpu_speed = 2500 + memory = 2048 + + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + dynamic_scaling_enabled = true + is_volatile = true + limit_cpu_use = true + offer_ha = true +} +` + +const testAccServiceOfferingFixed2_update = ` +resource "cloudstack_service_offering_fixed" "fixed2" { + display_text = "fixed2update" + name = "fixed2update" + + cpu_number = 2 + cpu_speed = 2500 + memory = 2048 + + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + dynamic_scaling_enabled = true + is_volatile = true + limit_cpu_use = true + offer_ha = true +} +` + +const testAccServiceOfferingFixed_disk = ` +resource "cloudstack_service_offering_fixed" "disk" { + display_text = "disk" + name = "disk" + + // compute + cpu_number = 2 + cpu_speed = 2500 + memory = 2048 + + // other + host_tags = "test0101, test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + dynamic_scaling_enabled = false + is_volatile = false + limit_cpu_use = false + offer_ha = false + + disk_offering = { + storage_type = "local" + provisioning_type = "thin" + cache_mode = "none" + root_disk_size = "5" + storage_tags = "test0101,test0202" + disk_offering_strictness = false + } +} +` + +const testAccServiceOfferingFixed_disk_hypervisor = ` +resource "cloudstack_service_offering_fixed" "disk_hypervisor" { + display_text = "disk_hypervisor" + name = "disk_hypervisor" + + // compute + cpu_number = 2 + cpu_speed = 2500 + memory = 2048 + + // other + host_tags = "test0101, test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + dynamic_scaling_enabled = false + is_volatile = false + limit_cpu_use = false + offer_ha = false + + disk_offering = { + storage_type = "local" + provisioning_type = "thin" + cache_mode = "none" + root_disk_size = "5" + storage_tags = "test0101,test0202" + disk_offering_strictness = false + } + disk_hypervisor = { + bytes_read_rate = 1024 + bytes_read_rate_max = 1024 + bytes_read_rate_max_length = 1024 + bytes_write_rate = 1024 + bytes_write_rate_max = 1024 + bytes_write_rate_max_length = 1024 + } +} +` + +const testAccServiceOfferingFixed_disk_storage = ` +resource "cloudstack_service_offering_fixed" "disk_storage" { + display_text = "disk_storage" + name = "disk_storage" + + // compute + cpu_number = 2 + cpu_speed = 2500 + memory = 2048 + + // other + host_tags = "test0101, test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + dynamic_scaling_enabled = false + is_volatile = false + limit_cpu_use = false + offer_ha = false + + disk_offering = { + storage_type = "local" + provisioning_type = "thin" + cache_mode = "none" + root_disk_size = "5" + storage_tags = "test0101,test0202" + disk_offering_strictness = false + } + disk_storage = { + min_iops = 100 + max_iops = 100 + } +} +` diff --git a/cloudstack/service_offering_models.go b/cloudstack/service_offering_models.go new file mode 100644 index 0000000..a93ffa4 --- /dev/null +++ b/cloudstack/service_offering_models.go @@ -0,0 +1,86 @@ +// +// 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 "github.com/hashicorp/terraform-plugin-framework/types" + +type serviceOfferingConstrainedResourceModel struct { + CpuSpeed types.Int32 `tfsdk:"cpu_speed"` + MaxCpuNumber types.Int32 `tfsdk:"max_cpu_number"` + MaxMemory types.Int32 `tfsdk:"max_memory"` + MinCpuNumber types.Int32 `tfsdk:"min_cpu_number"` + MinMemory types.Int32 `tfsdk:"min_memory"` + serviceOfferingCommonResourceModel +} + +type serviceOfferingUnconstrainedResourceModel struct { + serviceOfferingCommonResourceModel +} + +type serviceOfferingFixedResourceModel struct { + CpuNumber types.Int32 `tfsdk:"cpu_number"` + CpuSpeed types.Int32 `tfsdk:"cpu_speed"` + Memory types.Int32 `tfsdk:"memory"` + serviceOfferingCommonResourceModel +} + +type serviceOfferingCommonResourceModel struct { + DeploymentPlanner types.String `tfsdk:"deployment_planner"` + DiskOfferingId types.String `tfsdk:"disk_offering_id"` + DisplayText types.String `tfsdk:"display_text"` + DomainIds types.Set `tfsdk:"domain_ids"` + DynamicScalingEnabled types.Bool `tfsdk:"dynamic_scaling_enabled"` + HostTags types.String `tfsdk:"host_tags"` + Id types.String `tfsdk:"id"` + IsVolatile types.Bool `tfsdk:"is_volatile"` + LimitCpuUse types.Bool `tfsdk:"limit_cpu_use"` + Name types.String `tfsdk:"name"` + NetworkRate types.Int32 `tfsdk:"network_rate"` + OfferHa types.Bool `tfsdk:"offer_ha"` + ZoneIds types.Set `tfsdk:"zone_ids"` + ServiceOfferingDiskQosHypervisor types.Object `tfsdk:"disk_hypervisor"` + ServiceOfferingDiskOffering types.Object `tfsdk:"disk_offering"` + ServiceOfferingDiskQosStorage types.Object `tfsdk:"disk_storage"` +} + +type ServiceOfferingDiskQosHypervisor struct { + DiskBytesReadRate types.Int64 `tfsdk:"bytes_read_rate"` + DiskBytesReadRateMax types.Int64 `tfsdk:"bytes_read_rate_max"` + DiskBytesReadRateMaxLength types.Int64 `tfsdk:"bytes_read_rate_max_length"` + DiskBytesWriteRate types.Int64 `tfsdk:"bytes_write_rate"` + DiskBytesWriteRateMax types.Int64 `tfsdk:"bytes_write_rate_max"` + DiskBytesWriteRateMaxLength types.Int64 `tfsdk:"bytes_write_rate_max_length"` +} + +type ServiceOfferingDiskOffering struct { + CacheMode types.String `tfsdk:"cache_mode"` + DiskOfferingStrictness types.Bool `tfsdk:"disk_offering_strictness"` + ProvisionType types.String `tfsdk:"provisioning_type"` + RootDiskSize types.Int64 `tfsdk:"root_disk_size"` + StorageType types.String `tfsdk:"storage_type"` + StorageTags types.String `tfsdk:"storage_tags"` +} + +type ServiceOfferingDiskQosStorage struct { + CustomizedIops types.Bool `tfsdk:"customized_iops"` + HypervisorSnapshotReserve types.Int32 `tfsdk:"hypervisor_snapshot_reserve"` + MaxIops types.Int64 `tfsdk:"max_iops"` + MinIops types.Int64 `tfsdk:"min_iops"` +} diff --git a/cloudstack/service_offering_schema.go b/cloudstack/service_offering_schema.go new file mode 100644 index 0000000..9586d51 --- /dev/null +++ b/cloudstack/service_offering_schema.go @@ -0,0 +1,248 @@ +// +// 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. +// + +// Nested schema attributes arent validated +// disk_offering, disk_hypervisor, disk_storage +// ref: https://github.com/hashicorp/terraform-plugin-framework/issues/805 + +package cloudstack + +import ( + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int32planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func serviceOfferingMergeCommonSchema(s1 map[string]schema.Attribute) map[string]schema.Attribute { + common := map[string]schema.Attribute{ + "deployment_planner": schema.StringAttribute{ + Description: "The deployment planner for the service offering", + Optional: true, + }, + "disk_offering_id": schema.StringAttribute{ + Description: "The ID of the disk offering", + Optional: true, + }, + "display_text": schema.StringAttribute{ + Description: "The display text of the service offering", + Required: true, + }, + "domain_ids": schema.SetAttribute{ + Description: "the ID of the containing domain(s), null for public offerings", + Optional: true, + ElementType: types.StringType, + }, + "dynamic_scaling_enabled": schema.BoolAttribute{ + Description: "Enable dynamic scaling of the service offering", + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.RequiresReplace(), + }, + Default: booldefault.StaticBool(false), + }, + "host_tags": schema.StringAttribute{ + Description: "The host tag for this service offering", + Optional: true, + }, + "id": schema.StringAttribute{ + Description: "uuid of service offering", + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "is_volatile": schema.BoolAttribute{ + Description: "Service offering is volatile", + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.RequiresReplace(), + }, + Default: booldefault.StaticBool(false), + }, + "limit_cpu_use": schema.BoolAttribute{ + Description: "Restrict the CPU usage to committed service offering", + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.RequiresReplace(), + }, + Default: booldefault.StaticBool(false), + }, + "name": schema.StringAttribute{ + Description: "The name of the service offering", + Required: true, + }, + "network_rate": schema.Int32Attribute{ + Description: "Data transfer rate in megabits per second", + Optional: true, + }, + "offer_ha": schema.BoolAttribute{ + Description: "The HA for the service offering", + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.RequiresReplace(), + }, + Default: booldefault.StaticBool(false), + }, + "zone_ids": schema.SetAttribute{ + Description: "The ID of the zone(s)", + Optional: true, + ElementType: types.StringType, + }, + "disk_offering": schema.SingleNestedAttribute{ + Optional: true, + Attributes: map[string]schema.Attribute{ + "cache_mode": schema.StringAttribute{ + Description: "the cache mode to use for this disk offering. none, writeback or writethrough", + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "disk_offering_strictness": schema.BoolAttribute{ + Description: "True/False to indicate the strictness of the disk offering association with the compute offering. When set to true, override of disk offering is not allowed when VM is deployed and change disk offering is not allowed for the ROOT disk after the VM is deployed", + Required: true, + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.RequiresReplace(), + }, + }, + "provisioning_type": schema.StringAttribute{ + Description: "provisioning type used to create volumes. Valid values are thin, sparse, fat.", + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "root_disk_size": schema.Int64Attribute{ + Description: "the Root disk size in GB.", + Optional: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.RequiresReplace(), + }, + }, + "storage_type": schema.StringAttribute{ + Description: "the storage type of the service offering. Values are local and shared.", + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "storage_tags": schema.StringAttribute{ + Description: "the tags for the service offering", + Optional: true, + }, + }, + }, + "disk_hypervisor": schema.SingleNestedAttribute{ + Optional: true, + Attributes: map[string]schema.Attribute{ + "bytes_read_rate": schema.Int64Attribute{ + Description: "io requests read rate of the disk offering", + Required: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.RequiresReplace(), + }, + }, + "bytes_read_rate_max": schema.Int64Attribute{ + Description: "burst requests read rate of the disk offering", + Required: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.RequiresReplace(), + }, + }, + "bytes_read_rate_max_length": schema.Int64Attribute{ + Description: "length (in seconds) of the burst", + Required: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.RequiresReplace(), + }, + }, + "bytes_write_rate": schema.Int64Attribute{ + Description: "io requests write rate of the disk offering", + Required: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.RequiresReplace(), + }, + }, + "bytes_write_rate_max": schema.Int64Attribute{ + Description: "burst io requests write rate of the disk offering", + Required: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.RequiresReplace(), + }, + }, + "bytes_write_rate_max_length": schema.Int64Attribute{ + Description: "length (in seconds) of the burst", + Required: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.RequiresReplace(), + }, + }, + }, + }, + "disk_storage": schema.SingleNestedAttribute{ + Optional: true, + Attributes: map[string]schema.Attribute{ + "customized_iops": schema.BoolAttribute{ + Description: "true if disk offering uses custom iops, false otherwise", + Optional: true, + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.RequiresReplace(), + }, + }, + "hypervisor_snapshot_reserve": schema.Int32Attribute{ + Description: "Hypervisor snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware)", + Optional: true, + PlanModifiers: []planmodifier.Int32{ + int32planmodifier.RequiresReplace(), + }, + }, + "max_iops": schema.Int64Attribute{ + Description: "max iops of the compute offering", + Optional: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.RequiresReplace(), + }, + }, + "min_iops": schema.Int64Attribute{ + Description: "min iops of the compute offering", + Optional: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.RequiresReplace(), + }, + }, + }, + }, + } + + for key, value := range s1 { + common[key] = value + } + + return common + +} diff --git a/cloudstack/service_offering_unconstrained_resource.go b/cloudstack/service_offering_unconstrained_resource.go new file mode 100644 index 0000000..98b937c --- /dev/null +++ b/cloudstack/service_offering_unconstrained_resource.go @@ -0,0 +1,204 @@ +// +// 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 ( + "context" + "fmt" + + "github.com/apache/cloudstack-go/v2/cloudstack" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +var ( + _ resource.Resource = &serviceOfferingUnconstrainedResource{} + _ resource.ResourceWithConfigure = &serviceOfferingUnconstrainedResource{} +) + +func NewserviceOfferingUnconstrainedResource() resource.Resource { + return &serviceOfferingUnconstrainedResource{} +} + +type serviceOfferingUnconstrainedResource struct { + client *cloudstack.CloudStackClient +} + +// Schema defines the schema for the resource. +func (r *serviceOfferingUnconstrainedResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: serviceOfferingMergeCommonSchema(map[string]schema.Attribute{}), + } +} + +func (r *serviceOfferingUnconstrainedResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan serviceOfferingUnconstrainedResourceModel + var planDiskQosHypervisor ServiceOfferingDiskQosHypervisor + var planDiskOffering ServiceOfferingDiskOffering + var planDiskQosStorage ServiceOfferingDiskQosStorage + + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if !plan.ServiceOfferingDiskQosHypervisor.IsNull() { + resp.Diagnostics.Append(plan.ServiceOfferingDiskQosHypervisor.As(ctx, &planDiskQosHypervisor, basetypes.ObjectAsOptions{})...) + } + if !plan.ServiceOfferingDiskOffering.IsNull() { + resp.Diagnostics.Append(plan.ServiceOfferingDiskOffering.As(ctx, &planDiskOffering, basetypes.ObjectAsOptions{})...) + } + if !plan.ServiceOfferingDiskQosStorage.IsNull() { + resp.Diagnostics.Append(plan.ServiceOfferingDiskQosStorage.As(ctx, &planDiskQosStorage, basetypes.ObjectAsOptions{})...) + } + if resp.Diagnostics.HasError() { + return + } + + // cloudstack params + params := r.client.ServiceOffering.NewCreateServiceOfferingParams(plan.DisplayText.ValueString(), plan.Name.ValueString()) + plan.commonCreateParams(ctx, params) + planDiskQosHypervisor.commonCreateParams(ctx, params) + planDiskOffering.commonCreateParams(ctx, params) + planDiskQosStorage.commonCreateParams(ctx, params) + + // create offering + cs, err := r.client.ServiceOffering.CreateServiceOffering(params) + if err != nil { + resp.Diagnostics.AddError( + "Error creating service offering", + "Could not create unconstrained offering, unexpected error: "+err.Error(), + ) + return + } + + plan.Id = types.StringValue(cs.Id) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) + +} + +func (r *serviceOfferingUnconstrainedResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state serviceOfferingUnconstrainedResourceModel + var stateDiskQosHypervisor ServiceOfferingDiskQosHypervisor + var stateDiskOffering ServiceOfferingDiskOffering + var stateDiskQosStorage ServiceOfferingDiskQosStorage + + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if !state.ServiceOfferingDiskQosHypervisor.IsNull() { + resp.Diagnostics.Append(state.ServiceOfferingDiskQosHypervisor.As(ctx, &stateDiskQosHypervisor, basetypes.ObjectAsOptions{})...) + } + if !state.ServiceOfferingDiskOffering.IsNull() { + resp.Diagnostics.Append(state.ServiceOfferingDiskOffering.As(ctx, &stateDiskOffering, basetypes.ObjectAsOptions{})...) + } + if !state.ServiceOfferingDiskQosStorage.IsNull() { + resp.Diagnostics.Append(state.ServiceOfferingDiskQosStorage.As(ctx, &stateDiskQosStorage, basetypes.ObjectAsOptions{})...) + } + if resp.Diagnostics.HasError() { + return + } + + cs, _, err := r.client.ServiceOffering.GetServiceOfferingByID(state.Id.ValueString()) + if err != nil { + resp.Diagnostics.AddError( + "Error creating service offering", + "Could not read unconstrained service offering, unexpected error: "+err.Error(), + ) + return + } + + state.commonRead(ctx, cs) + stateDiskQosHypervisor.commonRead(ctx, cs) + stateDiskOffering.commonRead(ctx, cs) + stateDiskQosStorage.commonRead(ctx, cs) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) + +} + +func (r *serviceOfferingUnconstrainedResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var state serviceOfferingUnconstrainedResourceModel + + resp.Diagnostics.Append(req.Plan.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + params := r.client.ServiceOffering.NewUpdateServiceOfferingParams(state.Id.ValueString()) + state.commonUpdateParams(ctx, params) + + cs, err := r.client.ServiceOffering.UpdateServiceOffering(params) + if err != nil { + resp.Diagnostics.AddError( + "Error updating service offering", + "Could not update unconstrained service offering, unexpected error: "+err.Error(), + ) + return + } + + state.commonUpdate(ctx, cs) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) + +} + +func (r *serviceOfferingUnconstrainedResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state serviceOfferingUnconstrainedResourceModel + + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // Delete the service offering + _, err := r.client.ServiceOffering.DeleteServiceOffering(r.client.ServiceOffering.NewDeleteServiceOfferingParams(state.Id.ValueString())) + if err != nil { + resp.Diagnostics.AddError( + "Error deleting service offering", + "Could not delete unconstrained offering, unexpected error: "+err.Error(), + ) + return + } +} + +func (r *serviceOfferingUnconstrainedResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*cloudstack.CloudStackClient) + + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *cloudstack.CloudStackClient, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + return + } + + r.client = client +} + +func (r *serviceOfferingUnconstrainedResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_service_offering_unconstrained" +} diff --git a/cloudstack/service_offering_unconstrained_resource_test.go b/cloudstack/service_offering_unconstrained_resource_test.go new file mode 100644 index 0000000..5aba779 --- /dev/null +++ b/cloudstack/service_offering_unconstrained_resource_test.go @@ -0,0 +1,207 @@ +// // +// // 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 ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccServiceOfferingUnconstrained(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccMuxProvider, + Steps: []resource.TestStep{ + { + Config: testAccServiceOfferingUnconstrained1, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_unconstrained.unconstrained1", "name", "unconstrained1"), + ), + }, + { + Config: testAccServiceOfferingUnconstrained2, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_unconstrained.unconstrained2", "name", "unconstrained2"), + ), + }, + { + Config: testAccServiceOfferingUnconstrained2_update, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_unconstrained.unconstrained2", "name", "unconstrained2update"), + ), + }, + { + Config: testAccServiceOfferingUnconstrained_disk, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_unconstrained.disk", "name", "disk"), + ), + }, + { + Config: testAccServiceOfferingUnconstrained_disk_hypervisor, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_unconstrained.disk_hypervisor", "name", "disk_hypervisor"), + ), + }, + { + Config: testAccServiceOfferingUnconstrained_disk_storage, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_unconstrained.disk_storage", "name", "disk_storage"), + ), + }, + }, + }) +} + +const testAccServiceOfferingUnconstrained1 = ` +resource "cloudstack_service_offering_unconstrained" "unconstrained1" { + display_text = "unconstrained1" + name = "unconstrained1" + + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + dynamic_scaling_enabled = false + is_volatile = false + limit_cpu_use = false + offer_ha = false +} +` + +const testAccServiceOfferingUnconstrained2 = ` +resource "cloudstack_service_offering_unconstrained" "unconstrained2" { + display_text = "unconstrained2" + name = "unconstrained2" + + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + dynamic_scaling_enabled = true + is_volatile = true + limit_cpu_use = true + offer_ha = true +} +` + +const testAccServiceOfferingUnconstrained2_update = ` +resource "cloudstack_service_offering_unconstrained" "unconstrained2" { + display_text = "unconstrained2update" + name = "unconstrained2update" + + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + dynamic_scaling_enabled = true + is_volatile = true + limit_cpu_use = true + offer_ha = true +} +` + +const testAccServiceOfferingUnconstrained_disk = ` +resource "cloudstack_service_offering_unconstrained" "disk" { + display_text = "disk" + name = "disk" + + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + dynamic_scaling_enabled = true + is_volatile = true + limit_cpu_use = true + offer_ha = true + + disk_offering = { + storage_type = "shared" + provisioning_type = "thin" + cache_mode = "none" + root_disk_size = "5" + storage_tags = "test0101,test0202" + disk_offering_strictness = false + } +} +` + +const testAccServiceOfferingUnconstrained_disk_hypervisor = ` +resource "cloudstack_service_offering_unconstrained" "disk_hypervisor" { + display_text = "disk_hypervisor" + name = "disk_hypervisor" + + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + dynamic_scaling_enabled = true + is_volatile = true + limit_cpu_use = true + offer_ha = true + + disk_offering = { + storage_type = "shared" + provisioning_type = "thin" + cache_mode = "none" + root_disk_size = "5" + storage_tags = "test0101,test0202" + disk_offering_strictness = false + } + disk_hypervisor = { + bytes_read_rate = 1024 + bytes_read_rate_max = 1024 + bytes_read_rate_max_length = 1024 + bytes_write_rate = 1024 + bytes_write_rate_max = 1024 + bytes_write_rate_max_length = 1024 + } + +} +` + +const testAccServiceOfferingUnconstrained_disk_storage = ` +resource "cloudstack_service_offering_unconstrained" "disk_storage" { + display_text = "disk_storage" + name = "disk_storage" + + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + dynamic_scaling_enabled = true + is_volatile = true + limit_cpu_use = true + offer_ha = true + + disk_offering = { + storage_type = "shared" + provisioning_type = "thin" + cache_mode = "none" + root_disk_size = "5" + storage_tags = "test0101,test0202" + disk_offering_strictness = false + } + disk_storage = { + min_iops = 100 + max_iops = 100 + } +} +` diff --git a/cloudstack/service_offering_util.go b/cloudstack/service_offering_util.go new file mode 100644 index 0000000..ae52aa1 --- /dev/null +++ b/cloudstack/service_offering_util.go @@ -0,0 +1,277 @@ +// 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 ( + "context" + "strings" + + "github.com/apache/cloudstack-go/v2/cloudstack" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// ------------------------------------------------------------------------------------------------------------------------------ +// Common update methods +// - +func (state *serviceOfferingCommonResourceModel) commonUpdate(ctx context.Context, cs *cloudstack.UpdateServiceOfferingResponse) { + if cs.Displaytext != "" { + state.DisplayText = types.StringValue(cs.Displaytext) + } + if cs.Domainid != "" { + state.DomainIds, _ = types.SetValueFrom(ctx, types.StringType, strings.Split(cs.Domainid, ",")) + } + if cs.Hosttags != "" { + state.HostTags = types.StringValue(cs.Hosttags) + } + if cs.Name != "" { + state.Name = types.StringValue(cs.Name) + } + if cs.Zoneid != "" { + state.ZoneIds, _ = types.SetValueFrom(ctx, types.StringType, strings.Split(cs.Zoneid, ",")) + } +} + +func (plan *serviceOfferingCommonResourceModel) commonUpdateParams(ctx context.Context, p *cloudstack.UpdateServiceOfferingParams) *cloudstack.UpdateServiceOfferingParams { + if !plan.DisplayText.IsNull() { + p.SetDisplaytext(plan.DisplayText.ValueString()) + } + if !plan.DomainIds.IsNull() { + p.SetDomainid(plan.DomainIds.String()) + } + if !plan.HostTags.IsNull() { + p.SetHosttags(plan.HostTags.ValueString()) + } + if !plan.Name.IsNull() { + p.SetName(plan.Name.ValueString()) + } + if !plan.ZoneIds.IsNull() && len(plan.ZoneIds.Elements()) > 0 { + p.SetZoneid(plan.ZoneIds.String()) + } else { + p.SetZoneid("all") + } + + return p + +} + +// ------------------------------------------------------------------------------------------------------------------------------ +// common Read methods +// - +func (state *serviceOfferingCommonResourceModel) commonRead(ctx context.Context, cs *cloudstack.ServiceOffering) { + state.Id = types.StringValue(cs.Id) + + if cs.Deploymentplanner != "" { + state.DeploymentPlanner = types.StringValue(cs.Deploymentplanner) + } + if cs.Diskofferingid != "" { + state.DiskOfferingId = types.StringValue(cs.Diskofferingid) + } + if cs.Displaytext != "" { + state.DisplayText = types.StringValue(cs.Displaytext) + } + if cs.Domainid != "" { + state.DomainIds, _ = types.SetValueFrom(ctx, types.StringType, strings.Split(cs.Domainid, ",")) + } + if cs.Hosttags != "" { + state.HostTags = types.StringValue(cs.Hosttags) + } + if cs.Name != "" { + state.Name = types.StringValue(cs.Name) + } + if cs.Networkrate > 0 { + state.NetworkRate = types.Int32Value(int32(cs.Networkrate)) + } + if cs.Zoneid != "" { + state.ZoneIds, _ = types.SetValueFrom(ctx, types.StringType, strings.Split(cs.Zoneid, ",")) + } + + state.DynamicScalingEnabled = types.BoolValue(cs.Dynamicscalingenabled) + state.IsVolatile = types.BoolValue(cs.Isvolatile) + state.LimitCpuUse = types.BoolValue(cs.Limitcpuuse) + state.OfferHa = types.BoolValue(cs.Offerha) + +} + +func (state *ServiceOfferingDiskQosHypervisor) commonRead(ctx context.Context, cs *cloudstack.ServiceOffering) { + if cs.DiskBytesReadRate > 0 { + state.DiskBytesReadRate = types.Int64Value(cs.DiskBytesReadRate) + } + if cs.DiskBytesReadRateMax > 0 { + state.DiskBytesReadRateMax = types.Int64Value(cs.DiskBytesReadRateMax) + } + if cs.DiskBytesReadRateMaxLength > 0 { + state.DiskBytesReadRateMaxLength = types.Int64Value(cs.DiskBytesReadRateMaxLength) + } + if cs.DiskBytesWriteRate > 0 { + state.DiskBytesWriteRate = types.Int64Value(cs.DiskBytesWriteRate) + } + if cs.DiskBytesWriteRateMax > 0 { + state.DiskBytesWriteRateMax = types.Int64Value(cs.DiskBytesWriteRateMax) + } + if cs.DiskBytesWriteRateMaxLength > 0 { + state.DiskBytesWriteRateMaxLength = types.Int64Value(cs.DiskBytesWriteRateMaxLength) + } + +} + +func (state *ServiceOfferingDiskOffering) commonRead(ctx context.Context, cs *cloudstack.ServiceOffering) { + + if cs.CacheMode != "" { + state.CacheMode = types.StringValue(cs.CacheMode) + } + if cs.Diskofferingstrictness { + state.DiskOfferingStrictness = types.BoolValue(cs.Diskofferingstrictness) + } + if cs.Provisioningtype != "" { + state.ProvisionType = types.StringValue(cs.Provisioningtype) + } + if cs.Rootdisksize > 0 { + state.RootDiskSize = types.Int64Value(cs.Rootdisksize) + } + if cs.Storagetype != "" { + state.StorageType = types.StringValue(cs.Storagetype) + } + if cs.Storagetags != "" { + state.StorageTags = types.StringValue(cs.Storagetags) + } +} + +func (state *ServiceOfferingDiskQosStorage) commonRead(ctx context.Context, cs *cloudstack.ServiceOffering) { + if cs.Iscustomizediops { + state.CustomizedIops = types.BoolValue(cs.Iscustomizediops) + } + if cs.Hypervisorsnapshotreserve > 0 { + state.HypervisorSnapshotReserve = types.Int32Value(int32(cs.Hypervisorsnapshotreserve)) + } + if cs.Maxiops > 0 { + state.MaxIops = types.Int64Value(cs.Maxiops) + } + if cs.Miniops > 0 { + state.MinIops = types.Int64Value(cs.Miniops) + } + +} + +// ------------------------------------------------------------------------------------------------------------------------------ +// common Create methods +// - +func (plan *serviceOfferingCommonResourceModel) commonCreateParams(ctx context.Context, p *cloudstack.CreateServiceOfferingParams) *cloudstack.CreateServiceOfferingParams { + if !plan.DeploymentPlanner.IsNull() && !plan.DeploymentPlanner.IsUnknown() { + p.SetDeploymentplanner(plan.DeploymentPlanner.ValueString()) + } else { + plan.DeploymentPlanner = types.StringNull() + } + if !plan.DiskOfferingId.IsNull() { + p.SetDiskofferingid(plan.DiskOfferingId.ValueString()) + } + if !plan.DomainIds.IsNull() { + domainids := make([]string, len(plan.DomainIds.Elements())) + plan.DomainIds.ElementsAs(ctx, &domainids, false) + p.SetDomainid(domainids) + } + if !plan.DynamicScalingEnabled.IsNull() { + p.SetDynamicscalingenabled(plan.DynamicScalingEnabled.ValueBool()) + } + if !plan.HostTags.IsNull() { + p.SetHosttags(plan.HostTags.ValueString()) + } + if !plan.IsVolatile.IsNull() { + p.SetIsvolatile(plan.IsVolatile.ValueBool()) + } + if !plan.LimitCpuUse.IsNull() { + p.SetLimitcpuuse(plan.LimitCpuUse.ValueBool()) + } + if !plan.NetworkRate.IsNull() { + p.SetNetworkrate(int(plan.NetworkRate.ValueInt32())) + } + if !plan.OfferHa.IsNull() { + p.SetOfferha(plan.OfferHa.ValueBool()) + } + if !plan.ZoneIds.IsNull() { + zoneIds := make([]string, len(plan.ZoneIds.Elements())) + plan.ZoneIds.ElementsAs(ctx, &zoneIds, false) + p.SetZoneid(zoneIds) + } + + return p + +} +func (plan *ServiceOfferingDiskQosHypervisor) commonCreateParams(ctx context.Context, p *cloudstack.CreateServiceOfferingParams) *cloudstack.CreateServiceOfferingParams { + if !plan.DiskBytesReadRate.IsNull() { + p.SetBytesreadrate(plan.DiskBytesReadRate.ValueInt64()) + } + if !plan.DiskBytesReadRateMax.IsNull() { + p.SetBytesreadratemax(plan.DiskBytesReadRateMax.ValueInt64()) + } + if !plan.DiskBytesReadRateMaxLength.IsNull() { + p.SetBytesreadratemaxlength(plan.DiskBytesReadRateMaxLength.ValueInt64()) + } + if !plan.DiskBytesWriteRate.IsNull() { + p.SetByteswriterate(plan.DiskBytesWriteRate.ValueInt64()) + } + if !plan.DiskBytesWriteRateMax.IsNull() { + p.SetByteswriteratemax(plan.DiskBytesWriteRateMax.ValueInt64()) + } + if !plan.DiskBytesWriteRateMaxLength.IsNull() { + p.SetByteswriteratemaxlength(plan.DiskBytesWriteRateMaxLength.ValueInt64()) + } + + return p +} + +func (plan *ServiceOfferingDiskOffering) commonCreateParams(ctx context.Context, p *cloudstack.CreateServiceOfferingParams) *cloudstack.CreateServiceOfferingParams { + + if !plan.CacheMode.IsNull() { + p.SetCachemode(plan.CacheMode.ValueString()) + } + if !plan.DiskOfferingStrictness.IsNull() { + p.SetDiskofferingstrictness(plan.DiskOfferingStrictness.ValueBool()) + } + if !plan.ProvisionType.IsNull() { + p.SetProvisioningtype(plan.ProvisionType.ValueString()) + } + if !plan.RootDiskSize.IsNull() { + p.SetRootdisksize(plan.RootDiskSize.ValueInt64()) + } + if !plan.StorageType.IsNull() { + p.SetStoragetype(plan.StorageType.ValueString()) + } + if !plan.StorageTags.IsNull() { + p.SetTags(plan.StorageTags.ValueString()) + } + + return p + +} + +func (plan *ServiceOfferingDiskQosStorage) commonCreateParams(ctx context.Context, p *cloudstack.CreateServiceOfferingParams) *cloudstack.CreateServiceOfferingParams { + if !plan.CustomizedIops.IsNull() { + p.SetCustomizediops(plan.CustomizedIops.ValueBool()) + } + if !plan.HypervisorSnapshotReserve.IsNull() { + p.SetHypervisorsnapshotreserve(int(plan.HypervisorSnapshotReserve.ValueInt32())) + } + if !plan.MaxIops.IsNull() { + p.SetMaxiops(int64(plan.MaxIops.ValueInt64())) + } + if !plan.MinIops.IsNull() { + p.SetMiniops((plan.MinIops.ValueInt64())) + } + + return p +} diff --git a/go.mod b/go.mod index c09a776..de1b205 100644 --- a/go.mod +++ b/go.mod @@ -21,10 +21,10 @@ require ( github.com/apache/cloudstack-go/v2 v2.17.1 github.com/go-ini/ini v1.67.0 github.com/hashicorp/go-multierror v1.1.1 - github.com/hashicorp/terraform-plugin-framework v1.7.0 + github.com/hashicorp/terraform-plugin-framework v1.12.0 github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 - github.com/hashicorp/terraform-plugin-go v0.22.1 - github.com/hashicorp/terraform-plugin-mux v0.15.0 + github.com/hashicorp/terraform-plugin-go v0.24.0 + github.com/hashicorp/terraform-plugin-mux v0.16.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0 github.com/hashicorp/terraform-plugin-testing v1.7.0 ) @@ -35,14 +35,15 @@ require ( github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/cloudflare/circl v1.6.1 // indirect github.com/fatih/color v1.16.0 // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/mock v1.6.0 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-checkpoint v0.5.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect github.com/hashicorp/go-hclog v1.6.2 // indirect - github.com/hashicorp/go-plugin v1.6.0 // indirect + github.com/hashicorp/go-plugin v1.6.1 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/hc-install v0.6.3 // indirect @@ -77,10 +78,12 @@ require ( golang.org/x/text v0.23.0 // indirect golang.org/x/tools v0.22.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect - google.golang.org/grpc v1.62.1 // indirect - google.golang.org/protobuf v1.33.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect + google.golang.org/grpc v1.66.2 // indirect + google.golang.org/protobuf v1.34.2 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) go 1.23.0 + +toolchain go1.22.4 diff --git a/go.sum b/go.sum index 3c302c0..d71638e 100644 --- a/go.sum +++ b/go.sum @@ -41,8 +41,8 @@ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4er github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= @@ -60,8 +60,8 @@ github.com/hashicorp/go-hclog v1.6.2 h1:NOtoftovWkDheyUM/8JW3QMiXyxJK3uHRK7wV04n github.com/hashicorp/go-hclog v1.6.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-plugin v1.6.0 h1:wgd4KxHJTVGGqWBq4QPB1i5BZNEx9BR8+OFmHDmTk8A= -github.com/hashicorp/go-plugin v1.6.0/go.mod h1:lBS5MtSSBZk0SHc66KACcjjlU6WzEVP/8pwz68aMkCI= +github.com/hashicorp/go-plugin v1.6.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOsVdwI= +github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= @@ -77,16 +77,16 @@ github.com/hashicorp/terraform-exec v0.20.0 h1:DIZnPsqzPGuUnq6cH8jWcPunBfY+C+M8J github.com/hashicorp/terraform-exec v0.20.0/go.mod h1:ckKGkJWbsNqFKV1itgMnE0hY9IYf1HoiekpuN0eWoDw= github.com/hashicorp/terraform-json v0.21.0 h1:9NQxbLNqPbEMze+S6+YluEdXgJmhQykRyRNd+zTI05U= github.com/hashicorp/terraform-json v0.21.0/go.mod h1:qdeBs11ovMzo5puhrRibdD6d2Dq6TyE/28JiU4tIQxk= -github.com/hashicorp/terraform-plugin-framework v1.7.0 h1:wOULbVmfONnJo9iq7/q+iBOBJul5vRovaYJIu2cY/Pw= -github.com/hashicorp/terraform-plugin-framework v1.7.0/go.mod h1:jY9Id+3KbZ17OMpulgnWLSfwxNVYSoYBQFTgsx044CI= +github.com/hashicorp/terraform-plugin-framework v1.12.0 h1:7HKaueHPaikX5/7cbC1r9d1m12iYHY+FlNZEGxQ42CQ= +github.com/hashicorp/terraform-plugin-framework v1.12.0/go.mod h1:N/IOQ2uYjW60Jp39Cp3mw7I/OpC/GfZ0385R0YibmkE= github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 h1:HOjBuMbOEzl7snOdOoUfE2Jgeto6JOjLVQ39Ls2nksc= github.com/hashicorp/terraform-plugin-framework-validators v0.12.0/go.mod h1:jfHGE/gzjxYz6XoUwi/aYiiKrJDeutQNUtGQXkaHklg= -github.com/hashicorp/terraform-plugin-go v0.22.1 h1:iTS7WHNVrn7uhe3cojtvWWn83cm2Z6ryIUDTRO0EV7w= -github.com/hashicorp/terraform-plugin-go v0.22.1/go.mod h1:qrjnqRghvQ6KnDbB12XeZ4FluclYwptntoWCr9QaXTI= +github.com/hashicorp/terraform-plugin-go v0.24.0 h1:2WpHhginCdVhFIrWHxDEg6RBn3YaWzR2o6qUeIEat2U= +github.com/hashicorp/terraform-plugin-go v0.24.0/go.mod h1:tUQ53lAsOyYSckFGEefGC5C8BAaO0ENqzFd3bQeuYQg= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= -github.com/hashicorp/terraform-plugin-mux v0.15.0 h1:+/+lDx0WUsIOpkAmdwBIoFU8UP9o2eZASoOnLsWbKME= -github.com/hashicorp/terraform-plugin-mux v0.15.0/go.mod h1:9ezplb1Dyq394zQ+ldB0nvy/qbNAz3mMoHHseMTMaKo= +github.com/hashicorp/terraform-plugin-mux v0.16.0 h1:RCzXHGDYwUwwqfYYWJKBFaS3fQsWn/ZECEiW7p2023I= +github.com/hashicorp/terraform-plugin-mux v0.16.0/go.mod h1:PF79mAsPc8CpusXPfEVa4X8PtkB+ngWoiUClMrNZlYo= github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0 h1:qHprzXy/As0rxedphECBEQAh3R4yp6pKksKHcqZx5G8= github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0/go.mod h1:H+8tjs9TjV2w57QFVSMBQacf8k/E1XwLXGCARgViC6A= github.com/hashicorp/terraform-plugin-testing v1.7.0 h1:I6aeCyZ30z4NiI3tzyDoO6fS7YxP5xSL1ceOon3gTe8= @@ -168,6 +168,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= @@ -215,14 +216,14 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= -google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= -google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo= +google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= diff --git a/website/docs/r/service_offering_constrained.html.markdown b/website/docs/r/service_offering_constrained.html.markdown new file mode 100644 index 0000000..20a33b5 --- /dev/null +++ b/website/docs/r/service_offering_constrained.html.markdown @@ -0,0 +1,96 @@ +--- +page_title: "cloudstack_service_offering_constrained Resource" +description: |- + Provides a CloudStack Constrained Service Offering resource. This allows you to create and manage constrained compute offerings in CloudStack. +--- + +# cloudstack_service_offering_constrained + +Provides a CloudStack Constrained Service Offering resource. This resource allows you to create and manage service offerings with constrained CPU and memory parameters. + +## Example Usage + +```hcl +resource "cloudstack_service_offering_constrained" "example" { + display_text = "Example Constrained Offering" + name = "example_constrained" + + cpu_speed = 2500 + max_cpu_number = 10 + min_cpu_number = 2 + max_memory = 4096 + min_memory = 1024 + + host_tags = "tag1,tag2" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + dynamic_scaling_enabled = false + is_volatile = false + limit_cpu_use = false + offer_ha = false + zone_ids = ["zone-uuid"] + + disk_offering { + cache_mode = "none" + disk_offering_strictness = true + provisioning_type = "thin" + storage_type = "local" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +- `display_text` (String, Required) - The display text of the service offering. +- `name` (String, Required) - The name of the service offering. +- `cpu_speed` (Int, Required) - CPU speed in MHz. +- `max_cpu_number` (Int, Required) - Maximum number of CPUs. +- `min_cpu_number` (Int, Required) - Minimum number of CPUs. +- `max_memory` (Int, Required) - Maximum memory in MB. +- `min_memory` (Int, Required) - Minimum memory in MB. +- `deployment_planner` (String, Optional) - The deployment planner for the service offering. +- `disk_offering_id` (String, Optional) - The ID of the disk offering. +- `domain_ids` (Set of String, Optional) - The ID(s) of the containing domain(s), null for public offerings. +- `dynamic_scaling_enabled` (Bool, Optional, Default: false) - Enable dynamic scaling of the service offering. +- `host_tags` (String, Optional) - The host tag for this service offering. +- `is_volatile` (Bool, Optional, Default: false) - Service offering is volatile. +- `limit_cpu_use` (Bool, Optional, Default: false) - Restrict the CPU usage to committed service offering. +- `network_rate` (Int, Optional) - Data transfer rate in megabits per second. +- `offer_ha` (Bool, Optional, Default: false) - Enable HA for the service offering. +- `zone_ids` (Set of String, Optional) - The ID(s) of the zone(s). + +### Nested Blocks + +#### `disk_offering` (Block, Optional) + +- `cache_mode` (String, Required) - The cache mode to use for this disk offering. One of `none`, `writeback`, or `writethrough`. +- `disk_offering_strictness` (Bool, Required) - True/False to indicate the strictness of the disk offering association with the compute offering. +- `provisioning_type` (String, Required) - Provisioning type used to create volumes. Valid values are `thin`, `sparse`, `fat`. +- `root_disk_size` (Int, Optional) - The root disk size in GB. +- `storage_type` (String, Required) - The storage type of the service offering. Values are `local` and `shared`. +- `storage_tags` (String, Optional) - The tags for the service offering. + +#### `disk_hypervisor` (Block, Optional) + +- `bytes_read_rate` (Int, Required) - IO requests read rate of the disk offering. +- `bytes_read_rate_max` (Int, Required) - Burst requests read rate of the disk offering. +- `bytes_read_rate_max_length` (Int, Required) - Length (in seconds) of the burst. +- `bytes_write_rate` (Int, Required) - IO requests write rate of the disk offering. +- `bytes_write_rate_max` (Int, Required) - Burst IO requests write rate of the disk offering. +- `bytes_write_rate_max_length` (Int, Required) - Length (in seconds) of the burst. + +#### `disk_storage` (Block, Optional) + +- `customized_iops` (Bool, Optional) - True if disk offering uses custom IOPS, false otherwise. +- `hypervisor_snapshot_reserve` (Int, Optional) - Hypervisor snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware). +- `max_iops` (Int, Optional) - Max IOPS of the compute offering. +- `min_iops` (Int, Optional) - Min IOPS of the compute offering. + +## Attributes Reference + +In addition to the arguments above, the following attributes are exported: + +- `id` - The UUID of the service offering. diff --git a/website/docs/r/service_offering_fixed.html.markdown b/website/docs/r/service_offering_fixed.html.markdown new file mode 100644 index 0000000..448baa4 --- /dev/null +++ b/website/docs/r/service_offering_fixed.html.markdown @@ -0,0 +1,101 @@ +--- +page_title: "cloudstack_service_offering_fixed Resource" +sidebar_current: terraform-resource-service_offering_fixed +description: |- + Provides a CloudStack Service Offering (Fixed) resource. This resource allows you to create and manage fixed compute service offerings in CloudStack. +--- + +# cloudstack_service_offering_fixed + +Provides a CloudStack Service Offering (Fixed) resource. This resource allows you to create and manage fixed compute service offerings in CloudStack. + +## Example Usage + +```hcl +resource "cloudstack_service_offering_fixed" "fixed1" { + name = "fixed1" + display_text = "fixed1" + cpu_number = 2 + cpu_speed = 2000 + memory = 4096 + # Optional common attributes: + # deployment_planner = "FirstFit" + # disk_offering_id = "..." + # domain_ids = ["...", "..."] + # dynamic_scaling_enabled = false + # host_tags = "..." + # is_volatile = false + # limit_cpu_use = false + # network_rate = 1000 + # offer_ha = false + # zone_ids = ["..."] + # disk_offering { ... } + # disk_hypervisor { ... } + # disk_storage { ... } +} +``` + +## Argument Reference + +The following arguments are supported: + +- `name` (Required) - The name of the service offering. +- `display_text` (Required) - The display text of the service offering. +- `cpu_number` (Required) - Number of CPU cores. +- `cpu_speed` (Required) - The CPU speed in MHz (not applicable to KVM). +- `memory` (Required) - The total memory in MB. + +### Common Attributes + +- `deployment_planner` (Optional) - The deployment planner for the service offering. +- `disk_offering_id` (Optional) - The ID of the disk offering. +- `domain_ids` (Optional) - The ID(s) of the containing domain(s), null for public offerings. +- `dynamic_scaling_enabled` (Optional, Computed) - Enable dynamic scaling of the service offering. Defaults to `false`. +- `host_tags` (Optional) - The host tag for this service offering. +- `id` (Computed) - The UUID of the service offering. +- `is_volatile` (Optional, Computed) - Service offering is volatile. Defaults to `false`. +- `limit_cpu_use` (Optional, Computed) - Restrict the CPU usage to committed service offering. Defaults to `false`. +- `network_rate` (Optional) - Data transfer rate in megabits per second. +- `offer_ha` (Optional, Computed) - The HA for the service offering. Defaults to `false`. +- `zone_ids` (Optional) - The ID(s) of the zone(s). + +### Nested Blocks + +#### `disk_offering` (Optional) + +- `cache_mode` (Required) - The cache mode to use for this disk offering. One of `none`, `writeback`, or `writethrough`. +- `disk_offering_strictness` (Required) - True/False to indicate the strictness of the disk offering association. +- `provisioning_type` (Required) - Provisioning type used to create volumes. Valid values: `thin`, `sparse`, `fat`. +- `root_disk_size` (Optional) - The root disk size in GB. +- `storage_type` (Required) - The storage type. Values: `local`, `shared`. +- `storage_tags` (Optional) - The tags for the service offering. + +#### `disk_hypervisor` (Optional) + +- `bytes_read_rate` (Required) - IO requests read rate. +- `bytes_read_rate_max` (Required) - Burst requests read rate. +- `bytes_read_rate_max_length` (Required) - Length (in seconds) of the burst. +- `bytes_write_rate` (Required) - IO requests write rate. +- `bytes_write_rate_max` (Required) - Burst IO requests write rate. +- `bytes_write_rate_max_length` (Required) - Length (in seconds) of the burst. + +#### `disk_storage` (Optional) + +- `customized_iops` (Optional) - True if disk offering uses custom IOPS. +- `hypervisor_snapshot_reserve` (Optional) - Hypervisor snapshot reserve space as a percent of a volume. +- `max_iops` (Optional) - Max IOPS of the compute offering. +- `min_iops` (Optional) - Min IOPS of the compute offering. + +## Attributes Reference + +In addition to the arguments above, the following attributes are exported: + +- `id` - The ID of the service offering. + +## Import + +Service offerings can be imported using the ID: + +```sh +terraform import cloudstack_service_offering_fixed.example <id> +``` diff --git a/website/docs/r/service_offering_unconstrained.html.markdown b/website/docs/r/service_offering_unconstrained.html.markdown new file mode 100644 index 0000000..4a71140 --- /dev/null +++ b/website/docs/r/service_offering_unconstrained.html.markdown @@ -0,0 +1,95 @@ +--- +page_title: "cloudstack_service_offering_unconstrained Resource" +sidebar_current: terraform-resource-service_offering_unconstrained +description: |- + Provides a CloudStack Service Offering (Unconstrained) resource. This resource allows you to create and manage unconstrained compute service offerings in CloudStack. +--- + +# cloudstack_service_offering_unconstrained + +Provides a CloudStack Service Offering (Unconstrained) resource. This resource allows you to create and manage unconstrained compute service offerings in CloudStack. + +## Example Usage + +```hcl +resource "cloudstack_service_offering_unconstrained" "unconstrained1" { + name = "unconstrained1" + display_text = "unconstrained1" + # Optional common attributes: + # deployment_planner = "FirstFit" + # disk_offering_id = "..." + # domain_ids = ["...", "..."] + # dynamic_scaling_enabled = false + # host_tags = "..." + # is_volatile = false + # limit_cpu_use = false + # network_rate = 1000 + # offer_ha = false + # zone_ids = ["..."] + # disk_offering { ... } + # disk_hypervisor { ... } + # disk_storage { ... } +} +``` + +## Argument Reference + +The following arguments are supported: + +- `name` (Required) - The name of the service offering. +- `display_text` (Required) - The display text of the service offering. + +### Common Attributes + +- `deployment_planner` (Optional) - The deployment planner for the service offering. +- `disk_offering_id` (Optional) - The ID of the disk offering. +- `domain_ids` (Optional) - The ID(s) of the containing domain(s), null for public offerings. +- `dynamic_scaling_enabled` (Optional, Computed) - Enable dynamic scaling of the service offering. Defaults to `false`. +- `host_tags` (Optional) - The host tag for this service offering. +- `id` (Computed) - The UUID of the service offering. +- `is_volatile` (Optional, Computed) - Service offering is volatile. Defaults to `false`. +- `limit_cpu_use` (Optional, Computed) - Restrict the CPU usage to committed service offering. Defaults to `false`. +- `network_rate` (Optional) - Data transfer rate in megabits per second. +- `offer_ha` (Optional, Computed) - The HA for the service offering. Defaults to `false`. +- `zone_ids` (Optional) - The ID(s) of the zone(s). + +### Nested Blocks + +#### `disk_offering` (Optional) + +- `cache_mode` (Required) - The cache mode to use for this disk offering. One of `none`, `writeback`, or `writethrough`. +- `disk_offering_strictness` (Required) - True/False to indicate the strictness of the disk offering association. +- `provisioning_type` (Required) - Provisioning type used to create volumes. Valid values: `thin`, `sparse`, `fat`. +- `root_disk_size` (Optional) - The root disk size in GB. +- `storage_type` (Required) - The storage type. Values: `local`, `shared`. +- `storage_tags` (Optional) - The tags for the service offering. + +#### `disk_hypervisor` (Optional) + +- `bytes_read_rate` (Required) - IO requests read rate. +- `bytes_read_rate_max` (Required) - Burst requests read rate. +- `bytes_read_rate_max_length` (Required) - Length (in seconds) of the burst. +- `bytes_write_rate` (Required) - IO requests write rate. +- `bytes_write_rate_max` (Required) - Burst IO requests write rate. +- `bytes_write_rate_max_length` (Required) - Length (in seconds) of the burst. + +#### `disk_storage` (Optional) + +- `customized_iops` (Optional) - True if disk offering uses custom IOPS. +- `hypervisor_snapshot_reserve` (Optional) - Hypervisor snapshot reserve space as a percent of a volume. +- `max_iops` (Optional) - Max IOPS of the compute offering. +- `min_iops` (Optional) - Min IOPS of the compute offering. + +## Attributes Reference + +In addition to the arguments above, the following attributes are exported: + +- `id` - The ID of the service offering. + +## Import + +Service offerings can be imported using the ID: + +```sh +terraform import cloudstack_service_offering_unconstrained.example <id> +```