This is an automated email from the ASF dual-hosted git repository. vishesh 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 2d90b19 serviceoffering: add params for custom offering, storage tags, encryptroot (#212) 2d90b19 is described below commit 2d90b197c345f209cbfc73a25ff67d95e3efd5a9 Author: Abhishek Kumar <abhishek.mr...@gmail.com> AuthorDate: Thu Aug 28 17:10:21 2025 +0530 serviceoffering: add params for custom offering, storage tags, encryptroot (#212) --- cloudstack/resource_cloudstack_service_offering.go | 148 ++++++++++++++++++--- .../resource_cloudstack_service_offering_test.go | 39 ++++++ website/docs/r/service_offering.html.markdown | 20 +++ 3 files changed, 192 insertions(+), 15 deletions(-) diff --git a/cloudstack/resource_cloudstack_service_offering.go b/cloudstack/resource_cloudstack_service_offering.go index 170e05e..107938d 100644 --- a/cloudstack/resource_cloudstack_service_offering.go +++ b/cloudstack/resource_cloudstack_service_offering.go @@ -22,6 +22,7 @@ package cloudstack import ( "fmt" "log" + "strconv" "github.com/apache/cloudstack-go/v2/cloudstack" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -97,6 +98,48 @@ func resourceCloudStackServiceOffering() *schema.Resource { return }, }, + "customized": { + Description: "Whether service offering allows custom CPU/memory or not", + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + Computed: true, + }, + "min_cpu_number": { + Description: "Minimum number of CPU cores allowed", + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + "max_cpu_number": { + Description: "Maximum number of CPU cores allowed", + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + "min_memory": { + Description: "Minimum memory allowed (MB)", + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + "max_memory": { + Description: "Maximum memory allowed (MB)", + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + "encrypt_root": { + Description: "Encrypt the root disk for VMs using this service offering", + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + }, + "storage_tags": { + Description: "Storage tags to associate with the service offering", + Type: schema.TypeString, + Optional: true, + }, }, } } @@ -108,12 +151,15 @@ func resourceCloudStackServiceOfferingCreate(d *schema.ResourceData, meta interf // Create a new parameter struct p := cs.ServiceOffering.NewCreateServiceOfferingParams(display_text, name) - if v, ok := d.GetOk("cpu_number"); ok { - p.SetCpunumber(v.(int)) + + cpuNumber, cpuNumberOk := d.GetOk("cpu_number") + if cpuNumberOk { + p.SetCpunumber(cpuNumber.(int)) } - if v, ok := d.GetOk("cpu_speed"); ok { - p.SetCpuspeed(v.(int)) + cpuSpeed, cpuSpeedOk := d.GetOk("cpu_speed") + if cpuSpeedOk { + p.SetCpuspeed(cpuSpeed.(int)) } if v, ok := d.GetOk("host_tags"); ok { @@ -124,8 +170,9 @@ func resourceCloudStackServiceOfferingCreate(d *schema.ResourceData, meta interf p.SetLimitcpuuse(v.(bool)) } - if v, ok := d.GetOk("memory"); ok { - p.SetMemory(v.(int)) + memory, memoryOk := d.GetOk("memory") + if memoryOk { + p.SetMemory(memory.(int)) } if v, ok := d.GetOk("offer_ha"); ok { @@ -136,6 +183,39 @@ func resourceCloudStackServiceOfferingCreate(d *schema.ResourceData, meta interf p.SetStoragetype(v.(string)) } + customized := false + if v, ok := d.GetOk("customized"); ok { + customized = v.(bool) + } + if !cpuNumberOk && !cpuSpeedOk && !memoryOk { + customized = true + } + p.SetCustomized(customized) + + if v, ok := d.GetOk("min_cpu_number"); ok { + p.SetMincpunumber(v.(int)) + } + + if v, ok := d.GetOk("max_cpu_number"); ok { + p.SetMaxcpunumber(v.(int)) + } + + if v, ok := d.GetOk("min_memory"); ok { + p.SetMinmemory(v.(int)) + } + + if v, ok := d.GetOk("max_memory"); ok { + p.SetMaxmemory(v.(int)) + } + + if v, ok := d.GetOk("encrypt_root"); ok { + p.SetEncryptroot(v.(bool)) + } + + if v, ok := d.GetOk("storage_tags"); ok { + p.SetTags(v.(string)) + } + log.Printf("[DEBUG] Creating Service Offering %s", name) s, err := cs.ServiceOffering.CreateServiceOffering(p) @@ -168,15 +248,22 @@ func resourceCloudStackServiceOfferingRead(d *schema.ResourceData, meta interfac d.SetId(s.Id) fields := map[string]interface{}{ - "name": s.Name, - "display_text": s.Displaytext, - "cpu_number": s.Cpunumber, - "cpu_speed": s.Cpuspeed, - "host_tags": s.Hosttags, - "limit_cpu_use": s.Limitcpuuse, - "memory": s.Memory, - "offer_ha": s.Offerha, - "storage_type": s.Storagetype, + "name": s.Name, + "display_text": s.Displaytext, + "cpu_number": s.Cpunumber, + "cpu_speed": s.Cpuspeed, + "host_tags": s.Hosttags, + "limit_cpu_use": s.Limitcpuuse, + "memory": s.Memory, + "offer_ha": s.Offerha, + "storage_type": s.Storagetype, + "customized": s.Iscustomized, + "min_cpu_number": getIntFromDetails(s.Serviceofferingdetails, "mincpunumber"), + "max_cpu_number": getIntFromDetails(s.Serviceofferingdetails, "maxcpunumber"), + "min_memory": getIntFromDetails(s.Serviceofferingdetails, "minmemory"), + "max_memory": getIntFromDetails(s.Serviceofferingdetails, "maxmemory"), + "encrypt_root": s.Encryptroot, + "storage_tags": s.Storagetags, } for k, v := range fields { @@ -247,6 +334,24 @@ func resourceCloudStackServiceOfferingUpdate(d *schema.ResourceData, meta interf } + if d.HasChange("storage_tags") { + log.Printf("[DEBUG] Tags changed for %s, starting update", name) + + // Create a new parameter struct + p := cs.ServiceOffering.NewUpdateServiceOfferingParams(d.Id()) + + // Set the new tags + p.SetStoragetags(d.Get("storage_tags").(string)) + + // Update the host tags + _, err := cs.ServiceOffering.UpdateServiceOffering(p) + if err != nil { + return fmt.Errorf( + "Error updating the storage tags for service offering %s: %s", name, err) + } + + } + return resourceCloudStackServiceOfferingRead(d, meta) } @@ -263,3 +368,16 @@ func resourceCloudStackServiceOfferingDelete(d *schema.ResourceData, meta interf return nil } + +// getIntFromDetails extracts an integer value from the service offering details map. +func getIntFromDetails(details map[string]string, key string) interface{} { + if details == nil { + return nil + } + if val, ok := details[key]; ok { + if i, err := strconv.Atoi(val); err == nil { + return i + } + } + return nil +} diff --git a/cloudstack/resource_cloudstack_service_offering_test.go b/cloudstack/resource_cloudstack_service_offering_test.go index fc29628..d4634c9 100644 --- a/cloudstack/resource_cloudstack_service_offering_test.go +++ b/cloudstack/resource_cloudstack_service_offering_test.go @@ -83,3 +83,42 @@ func testAccCheckCloudStackServiceOfferingExists(n string, so *cloudstack.Servic return nil } } + +func TestAccCloudStackServiceOffering_customized(t *testing.T) { + var so cloudstack.ServiceOffering + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackServiceOffering_customized, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackServiceOfferingExists("cloudstack_service_offering.custom", &so), + resource.TestCheckResourceAttr("cloudstack_service_offering.custom", "customized", "true"), + resource.TestCheckResourceAttr("cloudstack_service_offering.custom", "min_cpu_number", "1"), + resource.TestCheckResourceAttr("cloudstack_service_offering.custom", "max_cpu_number", "8"), + resource.TestCheckResourceAttr("cloudstack_service_offering.custom", "min_memory", "1024"), + resource.TestCheckResourceAttr("cloudstack_service_offering.custom", "max_memory", "16384"), + resource.TestCheckResourceAttr("cloudstack_service_offering.custom", "cpu_speed", "1000"), + resource.TestCheckResourceAttr("cloudstack_service_offering.custom", "encrypt_root", "true"), + resource.TestCheckResourceAttr("cloudstack_service_offering.custom", "storage_tags", "production,ssd"), + ), + }, + }, + }) +} + +const testAccCloudStackServiceOffering_customized = ` +resource "cloudstack_service_offering" "custom" { + name = "custom_service_offering" + display_text = "Custom Test" + customized = true + min_cpu_number = 1 + max_cpu_number = 8 + min_memory = 1024 + max_memory = 16384 + cpu_speed = 1000 + encrypt_root = true + storage_tags = "production,ssd" +} +` diff --git a/website/docs/r/service_offering.html.markdown b/website/docs/r/service_offering.html.markdown index 94735eb..33852cb 100644 --- a/website/docs/r/service_offering.html.markdown +++ b/website/docs/r/service_offering.html.markdown @@ -49,6 +49,26 @@ The following arguments are supported: * `storage_type` - (Optional) The storage type of the service offering. Values are `local` and `shared`. Changing this forces a new resource to be created. +* `customized` - (Optional) Whether the service offering allows custom CPU and memory values. Set to `true` to enable users to specify CPU/memory within the min/max constraints for constrained offerings and any value for unconstrained offerings. + Changing this forces a new resource to be created. + +* `min_cpu_number` - (Optional) Minimum number of CPU cores allowed for customized offerings. + Changing this forces a new resource to be created. + +* `max_cpu_number` - (Optional) Maximum number of CPU cores allowed for customized offerings. + Changing this forces a new resource to be created. + +* `min_memory` - (Optional) Minimum memory (in MB) allowed for customized offerings. + Changing this forces a new resource to be created. + +* `max_memory` - (Optional) Maximum memory (in MB) allowed for customized offerings. + Changing this forces a new resource to be created. + +* `encrypt_root` - (Optional) Whether to encrypt the root disk for VMs using this service offering. + Changing this forces a new resource to be created. + +* `storage_tags` - (Optional) Storage tags to associate with the service offering. + ## Attributes Reference The following attributes are exported: