From 5674d83cfb517dcd14ccc5b49a2cef0d3ac54272 Mon Sep 17 00:00:00 2001 From: Stijn Simons Date: Thu, 9 Jul 2026 08:58:43 +0000 Subject: [PATCH] Enhance cloudstack_disk_offering resource and datasource --- .../data_source_cloudstack_disk_offering.go | 168 +++++++++++++++++ ...ta_source_cloudstack_disk_offering_test.go | 62 +++++++ cloudstack/provider.go | 1 + .../resource_cloudstack_disk_offering.go | 168 +++++++++++++++-- .../resource_cloudstack_disk_offering_test.go | 174 ++++++++++++++++++ website/docs/d/disk_offering.html.markdown | 39 ++++ website/docs/r/disk_offering.html.markdown | 20 +- 7 files changed, 617 insertions(+), 15 deletions(-) create mode 100644 cloudstack/data_source_cloudstack_disk_offering.go create mode 100644 cloudstack/data_source_cloudstack_disk_offering_test.go create mode 100644 cloudstack/resource_cloudstack_disk_offering_test.go create mode 100644 website/docs/d/disk_offering.html.markdown diff --git a/cloudstack/data_source_cloudstack_disk_offering.go b/cloudstack/data_source_cloudstack_disk_offering.go new file mode 100644 index 00000000..d99daa85 --- /dev/null +++ b/cloudstack/data_source_cloudstack_disk_offering.go @@ -0,0 +1,168 @@ +// +// 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 ( + "encoding/json" + "fmt" + "log" + "regexp" + "strings" + "time" + + "github.com/apache/cloudstack-go/v2/cloudstack" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceCloudstackDiskOffering() *schema.Resource { + return &schema.Resource{ + Read: datasourceCloudStackDiskOfferingRead, + Schema: map[string]*schema.Schema{ + "filter": dataSourceFiltersSchema(), + + // Computed values + "name": { + Type: schema.TypeString, + Computed: true, + }, + "display_text": { + Type: schema.TypeString, + Computed: true, + }, + "disk_size": { + Type: schema.TypeInt, + Computed: true, + }, + "customized": { + Type: schema.TypeBool, + Computed: true, + }, + "storage_type": { + Type: schema.TypeString, + Computed: true, + }, + "provisioning_type": { + Type: schema.TypeString, + Computed: true, + }, + "tags": { + Type: schema.TypeString, + Computed: true, + }, + "display_offering": { + Type: schema.TypeBool, + Computed: true, + }, + }, + } +} + +func datasourceCloudStackDiskOfferingRead(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + p := cs.DiskOffering.NewListDiskOfferingsParams() + csDiskOfferings, err := cs.DiskOffering.ListDiskOfferings(p) + if err != nil { + return fmt.Errorf("Failed to list disk offerings: %s", err) + } + + filters := d.Get("filter") + var diskOfferings []*cloudstack.DiskOffering + + for _, o := range csDiskOfferings.DiskOfferings { + match, err := applyDiskOfferingFilters(o, filters.(*schema.Set)) + if err != nil { + return err + } + if match { + diskOfferings = append(diskOfferings, o) + } + } + + if len(diskOfferings) == 0 { + return fmt.Errorf("No disk offering is matching with the specified regex") + } + + diskOffering, err := latestDiskOffering(diskOfferings) + if err != nil { + return err + } + log.Printf("[DEBUG] Selected disk offering: %s\n", diskOffering.Displaytext) + + return diskOfferingDescriptionAttributes(d, diskOffering) +} + +func diskOfferingDescriptionAttributes(d *schema.ResourceData, diskOffering *cloudstack.DiskOffering) error { + d.SetId(diskOffering.Id) + d.Set("name", diskOffering.Name) + d.Set("display_text", diskOffering.Displaytext) + d.Set("disk_size", int(diskOffering.Disksize)) + d.Set("customized", diskOffering.Iscustomized) + d.Set("storage_type", diskOffering.Storagetype) + d.Set("provisioning_type", diskOffering.Provisioningtype) + d.Set("tags", diskOffering.Tags) + d.Set("display_offering", diskOffering.Displayoffering) + + return nil +} + +func latestDiskOffering(diskOfferings []*cloudstack.DiskOffering) (*cloudstack.DiskOffering, error) { + var latest time.Time + var diskOffering *cloudstack.DiskOffering + + for _, o := range diskOfferings { + created, err := time.Parse("2006-01-02T15:04:05-0700", o.Created) + if err != nil { + return nil, fmt.Errorf("Failed to parse creation date of a disk offering: %s", err) + } + + if created.After(latest) { + latest = created + diskOffering = o + } + } + + return diskOffering, nil +} + +func applyDiskOfferingFilters(diskOffering *cloudstack.DiskOffering, filters *schema.Set) (bool, error) { + var diskOfferingJSON map[string]interface{} + k, _ := json.Marshal(diskOffering) + err := json.Unmarshal(k, &diskOfferingJSON) + if err != nil { + return false, err + } + + for _, f := range filters.List() { + m := f.(map[string]interface{}) + r, err := regexp.Compile(m["value"].(string)) + if err != nil { + return false, fmt.Errorf("Invalid regex: %s", err) + } + updatedName := strings.ReplaceAll(m["name"].(string), "_", "") + diskOfferingField, ok := diskOfferingJSON[updatedName].(string) + if !ok { + return false, fmt.Errorf("Field %s is not a string and cannot be filtered", m["name"].(string)) + } + if !r.MatchString(diskOfferingField) { + return false, nil + } + } + return true, nil +} diff --git a/cloudstack/data_source_cloudstack_disk_offering_test.go b/cloudstack/data_source_cloudstack_disk_offering_test.go new file mode 100644 index 00000000..f5e60e67 --- /dev/null +++ b/cloudstack/data_source_cloudstack_disk_offering_test.go @@ -0,0 +1,62 @@ +// +// 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 TestAccDiskOfferingDataSource_basic(t *testing.T) { + resourceName := "cloudstack_disk_offering.disk-offering-resource" + datasourceName := "data.cloudstack_disk_offering.disk-offering-data-source" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testDiskOfferingDataSourceConfig_basic, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(datasourceName, "name", resourceName, "name"), + resource.TestCheckResourceAttrPair(datasourceName, "display_text", resourceName, "display_text"), + resource.TestCheckResourceAttrPair(datasourceName, "disk_size", resourceName, "disk_size"), + ), + }, + }, + }) +} + +const testDiskOfferingDataSourceConfig_basic = ` +resource "cloudstack_disk_offering" "disk-offering-resource" { + name = "TestDiskOfferingDataSource" + display_text = "DisplayDiskOffering" + disk_size = 20 +} + +data "cloudstack_disk_offering" "disk-offering-data-source" { + filter { + name = "name" + value = "TestDiskOfferingDataSource" + } + depends_on = [cloudstack_disk_offering.disk-offering-resource] +} +` diff --git a/cloudstack/provider.go b/cloudstack/provider.go index 72090147..f2c8ed6a 100644 --- a/cloudstack/provider.go +++ b/cloudstack/provider.go @@ -89,6 +89,7 @@ func Provider() *schema.Provider { "cloudstack_network_offering": dataSourceCloudstackNetworkOffering(), "cloudstack_zone": dataSourceCloudStackZone(), "cloudstack_service_offering": dataSourceCloudstackServiceOffering(), + "cloudstack_disk_offering": dataSourceCloudstackDiskOffering(), "cloudstack_volume": dataSourceCloudstackVolume(), "cloudstack_vpc": dataSourceCloudstackVPC(), "cloudstack_ipaddress": dataSourceCloudstackIPAddress(), diff --git a/cloudstack/resource_cloudstack_disk_offering.go b/cloudstack/resource_cloudstack_disk_offering.go index 197eaf4b..731095ee 100644 --- a/cloudstack/resource_cloudstack_disk_offering.go +++ b/cloudstack/resource_cloudstack_disk_offering.go @@ -20,6 +20,7 @@ package cloudstack import ( + "fmt" "log" "github.com/apache/cloudstack-go/v2/cloudstack" @@ -32,18 +33,75 @@ func resourceCloudStackDiskOffering() *schema.Resource { Read: resourceCloudStackDiskOfferingRead, Update: resourceCloudStackDiskOfferingUpdate, Delete: resourceCloudStackDiskOfferingDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, Schema: map[string]*schema.Schema{ "name": { - Type: schema.TypeString, - Required: true, + Description: "The name of the disk offering", + Type: schema.TypeString, + Required: true, }, "display_text": { - Type: schema.TypeString, - Required: true, + Description: "The display text of the disk offering", + Type: schema.TypeString, + Required: true, }, "disk_size": { - Type: schema.TypeInt, - Required: true, + Description: "The size of the disk offering in GB", + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"customized"}, + }, + "customized": { + Description: "Whether the disk offering allows a custom disk size at deployment time", + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + Default: false, + ConflictsWith: []string{"disk_size"}, + }, + "storage_type": { + Description: "The storage type of the disk offering. Values are local and shared", + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: "shared", + ValidateFunc: func(val interface{}, key string) (warns []string, errs []error) { + v := val.(string) + if v == "local" || v == "shared" { + return + } + errs = append(errs, fmt.Errorf("storage type should be either local or shared, got %s", v)) + return + }, + }, + "provisioning_type": { + Description: "Provisioning type used to create volumes. Values are thin, sparse and fat", + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: "thin", + ValidateFunc: func(val interface{}, key string) (warns []string, errs []error) { + v := val.(string) + if v == "thin" || v == "sparse" || v == "fat" { + return + } + errs = append(errs, fmt.Errorf("provisioning type should be one of thin, sparse or fat, got %s", v)) + return + }, + }, + "tags": { + Description: "The storage tags for the disk offering", + Type: schema.TypeString, + Optional: true, + }, + "display_offering": { + Description: "Whether the disk offering is displayed to the end user", + Type: schema.TypeBool, + Optional: true, + Default: true, }, }, } @@ -52,16 +110,36 @@ func resourceCloudStackDiskOffering() *schema.Resource { func resourceCloudStackDiskOfferingCreate(d *schema.ResourceData, meta interface{}) error { cs := meta.(*cloudstack.CloudStackClient) name := d.Get("name").(string) - display_text := d.Get("display_text").(string) - disk_size := d.Get("disk_size").(int) + displayText := d.Get("display_text").(string) // Create a new parameter struct - p := cs.DiskOffering.NewCreateDiskOfferingParams(name, display_text) - p.SetDisksize(int64(disk_size)) + p := cs.DiskOffering.NewCreateDiskOfferingParams(displayText, name) + + if v, ok := d.GetOk("disk_size"); ok { + p.SetDisksize(int64(v.(int))) + } + + if v, ok := d.GetOk("customized"); ok { + p.SetCustomized(v.(bool)) + } + + if v, ok := d.GetOk("storage_type"); ok { + p.SetStoragetype(v.(string)) + } + + if v, ok := d.GetOk("provisioning_type"); ok { + p.SetProvisioningtype(v.(string)) + } + + if v, ok := d.GetOk("tags"); ok { + p.SetTags(v.(string)) + } + + // display_offering defaults to true, so read it directly rather than via GetOk + p.SetDisplayoffering(d.Get("display_offering").(bool)) log.Printf("[DEBUG] Creating Disk Offering %s", name) diskOff, err := cs.DiskOffering.CreateDiskOffering(p) - if err != nil { return err } @@ -72,8 +150,70 @@ func resourceCloudStackDiskOfferingCreate(d *schema.ResourceData, meta interface return resourceCloudStackDiskOfferingRead(d, meta) } -func resourceCloudStackDiskOfferingRead(d *schema.ResourceData, meta interface{}) error { return nil } +func resourceCloudStackDiskOfferingRead(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + log.Printf("[DEBUG] Retrieving Disk Offering %s", d.Id()) -func resourceCloudStackDiskOfferingUpdate(d *schema.ResourceData, meta interface{}) error { return nil } + // Get the Disk Offering details + diskOff, count, err := cs.DiskOffering.GetDiskOfferingByID(d.Id()) + if err != nil { + if count == 0 { + log.Printf("[DEBUG] Disk Offering %s does no longer exist", d.Get("name").(string)) + d.SetId("") + return nil + } + return err + } -func resourceCloudStackDiskOfferingDelete(d *schema.ResourceData, meta interface{}) error { return nil } + d.Set("name", diskOff.Name) + d.Set("display_text", diskOff.Displaytext) + d.Set("disk_size", int(diskOff.Disksize)) + d.Set("customized", diskOff.Iscustomized) + d.Set("storage_type", diskOff.Storagetype) + d.Set("provisioning_type", diskOff.Provisioningtype) + d.Set("tags", diskOff.Tags) + d.Set("display_offering", diskOff.Displayoffering) + + return nil +} + +func resourceCloudStackDiskOfferingUpdate(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + name := d.Get("name").(string) + + if d.HasChange("name") || d.HasChange("display_text") || + d.HasChange("tags") || d.HasChange("display_offering") { + + // Create a new parameter struct + p := cs.DiskOffering.NewUpdateDiskOfferingParams(d.Id()) + + p.SetName(d.Get("name").(string)) + p.SetDisplaytext(d.Get("display_text").(string)) + p.SetTags(d.Get("tags").(string)) + p.SetDisplayoffering(d.Get("display_offering").(bool)) + + log.Printf("[DEBUG] Updating Disk Offering %s", name) + _, err := cs.DiskOffering.UpdateDiskOffering(p) + if err != nil { + return fmt.Errorf("Error updating Disk Offering %s: %s", name, err) + } + } + + return resourceCloudStackDiskOfferingRead(d, meta) +} + +func resourceCloudStackDiskOfferingDelete(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + // Create a new parameter struct + p := cs.DiskOffering.NewDeleteDiskOfferingParams(d.Id()) + + log.Printf("[DEBUG] Deleting Disk Offering %s", d.Get("name").(string)) + _, err := cs.DiskOffering.DeleteDiskOffering(p) + if err != nil { + return fmt.Errorf("Error deleting Disk Offering: %s", err) + } + + return nil +} diff --git a/cloudstack/resource_cloudstack_disk_offering_test.go b/cloudstack/resource_cloudstack_disk_offering_test.go new file mode 100644 index 00000000..49ac6972 --- /dev/null +++ b/cloudstack/resource_cloudstack_disk_offering_test.go @@ -0,0 +1,174 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package cloudstack + +import ( + "fmt" + "testing" + + "github.com/apache/cloudstack-go/v2/cloudstack" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" +) + +func TestAccCloudStackDiskOffering_basic(t *testing.T) { + var do cloudstack.DiskOffering + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackDiskOfferingDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackDiskOffering_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackDiskOfferingExists("cloudstack_disk_offering.test1", &do), + resource.TestCheckResourceAttr("cloudstack_disk_offering.test1", "name", "disk_offering_1"), + resource.TestCheckResourceAttr("cloudstack_disk_offering.test1", "display_text", "Test"), + resource.TestCheckResourceAttr("cloudstack_disk_offering.test1", "disk_size", "10"), + resource.TestCheckResourceAttr("cloudstack_disk_offering.test1", "storage_type", "shared"), + ), + }, + }, + }) +} + +const testAccCloudStackDiskOffering_basic = ` +resource "cloudstack_disk_offering" "test1" { + name = "disk_offering_1" + display_text = "Test" + disk_size = 10 +} +` + +func TestAccCloudStackDiskOffering_customized(t *testing.T) { + var do cloudstack.DiskOffering + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackDiskOfferingDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackDiskOffering_customized, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackDiskOfferingExists("cloudstack_disk_offering.custom", &do), + resource.TestCheckResourceAttr("cloudstack_disk_offering.custom", "customized", "true"), + resource.TestCheckResourceAttr("cloudstack_disk_offering.custom", "storage_type", "local"), + resource.TestCheckResourceAttr("cloudstack_disk_offering.custom", "provisioning_type", "thin"), + resource.TestCheckResourceAttr("cloudstack_disk_offering.custom", "tags", "ssd"), + ), + }, + }, + }) +} + +const testAccCloudStackDiskOffering_customized = ` +resource "cloudstack_disk_offering" "custom" { + name = "custom_disk_offering" + display_text = "Custom Test" + customized = true + storage_type = "local" + provisioning_type = "thin" + tags = "ssd" +} +` + +func TestAccCloudStackDiskOffering_update(t *testing.T) { + var do cloudstack.DiskOffering + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackDiskOfferingDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackDiskOffering_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackDiskOfferingExists("cloudstack_disk_offering.test1", &do), + resource.TestCheckResourceAttr("cloudstack_disk_offering.test1", "display_text", "Test"), + ), + }, + { + Config: testAccCloudStackDiskOffering_update, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackDiskOfferingExists("cloudstack_disk_offering.test1", &do), + resource.TestCheckResourceAttr("cloudstack_disk_offering.test1", "name", "disk_offering_1_updated"), + resource.TestCheckResourceAttr("cloudstack_disk_offering.test1", "display_text", "Test Updated"), + resource.TestCheckResourceAttr("cloudstack_disk_offering.test1", "tags", "gold"), + ), + }, + }, + }) +} + +const testAccCloudStackDiskOffering_update = ` +resource "cloudstack_disk_offering" "test1" { + name = "disk_offering_1_updated" + display_text = "Test Updated" + disk_size = 10 + tags = "gold" +} +` + +func testAccCheckCloudStackDiskOfferingExists(n string, do *cloudstack.DiskOffering) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No disk offering ID is set") + } + + cs := testAccProvider.Meta().(*cloudstack.CloudStackClient) + resp, _, err := cs.DiskOffering.GetDiskOfferingByID(rs.Primary.ID) + if err != nil { + return err + } + + if resp.Id != rs.Primary.ID { + return fmt.Errorf("Disk offering not found") + } + + *do = *resp + + return nil + } +} + +func testAccCheckCloudStackDiskOfferingDestroy(s *terraform.State) error { + cs := testAccProvider.Meta().(*cloudstack.CloudStackClient) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "cloudstack_disk_offering" { + continue + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No disk offering ID is set") + } + + _, _, err := cs.DiskOffering.GetDiskOfferingByID(rs.Primary.ID) + if err == nil { + return fmt.Errorf("Disk offering %s still exists", rs.Primary.ID) + } + } + + return nil +} diff --git a/website/docs/d/disk_offering.html.markdown b/website/docs/d/disk_offering.html.markdown new file mode 100644 index 00000000..9ed35ab7 --- /dev/null +++ b/website/docs/d/disk_offering.html.markdown @@ -0,0 +1,39 @@ +--- +layout: "cloudstack" +page_title: "Cloudstack: cloudstack_disk_offering" +sidebar_current: "docs-cloudstack-cloudstack_disk_offering" +description: |- + Gets information about cloudstack disk offering. +--- + +# cloudstack_disk_offering + +Use this datasource to get information about a disk offering for use in other resources. + +### Example Usage + +```hcl +data "cloudstack_disk_offering" "disk-offering-data-source" { + filter { + name = "name" + value = "custom" + } +} +``` + +### Argument Reference + +* `filter` - (Required) One or more name/value pairs to filter off of. You can apply filters on any exported attributes. + +## Attributes Reference + +The following attributes are exported: + +* `name` - The name of the disk offering. +* `display_text` - The display text of the disk offering. +* `disk_size` - The size of the disk offering in GB. +* `customized` - Whether the disk offering allows a custom disk size. +* `storage_type` - The storage type of the disk offering. +* `provisioning_type` - The provisioning type of the disk offering. +* `tags` - The storage tags for the disk offering. +* `display_offering` - Whether the disk offering is displayed to the end user. diff --git a/website/docs/r/disk_offering.html.markdown b/website/docs/r/disk_offering.html.markdown index cbccf164..26145ba7 100644 --- a/website/docs/r/disk_offering.html.markdown +++ b/website/docs/r/disk_offering.html.markdown @@ -27,7 +27,20 @@ The following arguments are supported: * `name` - (Required) The name of the disk offering. * `display_text` - (Required) The display text of the disk offering. -* `disk_size` - (Required) The size of the disk offering in GB. +* `disk_size` - (Optional) The size of the disk offering in GB. Conflicts with + `customized`. Changing this forces a new resource to be created. +* `customized` - (Optional) Whether the disk offering allows a custom disk size + to be specified at deployment time. Conflicts with `disk_size`. Defaults to + `false`. Changing this forces a new resource to be created. +* `storage_type` - (Optional) The storage type of the disk offering. Values are + `local` and `shared`. Defaults to `shared`. Changing this forces a new + resource to be created. +* `provisioning_type` - (Optional) The provisioning type used to create volumes. + Values are `thin`, `sparse` and `fat`. Defaults to `thin`. Changing this + forces a new resource to be created. +* `tags` - (Optional) The storage tags for the disk offering. +* `display_offering` - (Optional) Whether the disk offering is displayed to the + end user. Defaults to `true`. ## Attributes Reference @@ -37,6 +50,11 @@ The following attributes are exported: * `name` - The name of the disk offering. * `display_text` - The display text of the disk offering. * `disk_size` - The size of the disk offering in GB. +* `customized` - Whether the disk offering allows a custom disk size. +* `storage_type` - The storage type of the disk offering. +* `provisioning_type` - The provisioning type of the disk offering. +* `tags` - The storage tags for the disk offering. +* `display_offering` - Whether the disk offering is displayed to the end user. ## Import