Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
## 0.4.0 (Unreleased)

FEATURES:

* **New Resource Attribute**: `cloudstack_service_offering` now supports `service_offering_details` for GPU configuration and other advanced settings [GH-246]

IMPROVEMENTS:

* Restore support for managing resource tags as CloudStack 4.11.3+ and 4.12+ support tags again [GH-65]
Expand Down
75 changes: 59 additions & 16 deletions cloudstack/resource_cloudstack_service_offering.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,15 @@ func resourceCloudStackServiceOffering() *schema.Resource {
Type: schema.TypeString,
Optional: true,
},
"service_offering_details": {
Description: "Service offering details for GPU configuration and other advanced settings",
Type: schema.TypeMap,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
}
}
Expand Down Expand Up @@ -216,6 +225,14 @@ func resourceCloudStackServiceOfferingCreate(d *schema.ResourceData, meta interf
p.SetTags(v.(string))
}

if details, ok := d.GetOk("service_offering_details"); ok {
serviceOfferingDetails := make(map[string]string)
for k, v := range details.(map[string]interface{}) {
serviceOfferingDetails[k] = v.(string)
}
p.SetServiceofferingdetails(serviceOfferingDetails)
}

log.Printf("[DEBUG] Creating Service Offering %s", name)
s, err := cs.ServiceOffering.CreateServiceOffering(p)

Expand Down Expand Up @@ -248,22 +265,23 @@ 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,
"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,
"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,
"service_offering_details": getServiceOfferingDetails(s.Serviceofferingdetails),
}

for k, v := range fields {
Expand Down Expand Up @@ -381,3 +399,28 @@ func getIntFromDetails(details map[string]string, key string) interface{} {
}
return nil
}

// getServiceOfferingDetails extracts custom service offering details while excluding
// built-in details that are handled as separate schema fields
func getServiceOfferingDetails(details map[string]string) map[string]interface{} {
if details == nil {
return make(map[string]interface{})
}

// List of built-in details that are handled as separate schema fields
builtInKeys := map[string]bool{
"mincpunumber": true,
"maxcpunumber": true,
"minmemory": true,
"maxmemory": true,
}

result := make(map[string]interface{})
for k, v := range details {
if !builtInKeys[k] {
result[k] = v
}
}

return result
}
37 changes: 37 additions & 0 deletions cloudstack/resource_cloudstack_service_offering_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,40 @@ resource "cloudstack_service_offering" "custom" {
storage_tags = "production,ssd"
}
`

func TestAccCloudStackServiceOffering_gpu(t *testing.T) {
var so cloudstack.ServiceOffering
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccCloudStackServiceOffering_gpu,
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudStackServiceOfferingExists("cloudstack_service_offering.gpu", &so),
resource.TestCheckResourceAttr("cloudstack_service_offering.gpu", "name", "gpu_service_offering"),
resource.TestCheckResourceAttr("cloudstack_service_offering.gpu", "display_text", "GPU Test"),
resource.TestCheckResourceAttr("cloudstack_service_offering.gpu", "cpu_number", "4"),
resource.TestCheckResourceAttr("cloudstack_service_offering.gpu", "memory", "16384"),
resource.TestCheckResourceAttr("cloudstack_service_offering.gpu", "service_offering_details.pciDevice", "Group of NVIDIA A6000 GPUs"),
resource.TestCheckResourceAttr("cloudstack_service_offering.gpu", "service_offering_details.vgpuType", "A6000-8A"),
),
},
},
})
}

const testAccCloudStackServiceOffering_gpu = `
resource "cloudstack_service_offering" "gpu" {
name = "gpu_service_offering"
display_text = "GPU Test"
cpu_number = 4
memory = 16384
cpu_speed = 1000

service_offering_details = {
pciDevice = "Group of NVIDIA A6000 GPUs"
vgpuType = "A6000-8A"
}
}
`
23 changes: 23 additions & 0 deletions website/docs/r/service_offering.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,30 @@ A `cloudstack_service_offering` resource manages a service offering within Cloud

## Example Usage

### Basic Service Offering

```hcl
resource "cloudstack_service_offering" "example" {
name = "example-service-offering"
display_text = "Example Service Offering"
cpu_number = 2
memory = 4096
}
```

### GPU Service Offering

```hcl
resource "cloudstack_service_offering" "gpu_offering" {
name = "gpu-a6000"
display_text = "GPU A6000 Instance"
cpu_number = 4
memory = 16384

service_offering_details = {
pciDevice = "Group of NVIDIA A6000 GPUs"
vgpuType = "A6000-8A"
}
}
```

Expand Down Expand Up @@ -69,6 +89,9 @@ The following arguments are supported:

* `storage_tags` - (Optional) Storage tags to associate with the service offering.

* `service_offering_details` - (Optional) A map of service offering details for GPU configuration and other advanced settings. Common keys include `pciDevice` and `vgpuType` for GPU offerings.
Changing this forces a new resource to be created.

## Attributes Reference

The following attributes are exported:
Expand Down