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 <[email protected]>
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 <[email protected]>
---
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>
+```