This is an automated email from the ASF dual-hosted git repository.
sureshanaparti 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 9159ca8 Add GPU support: gpu_card / vgpu_profile data sources and GPU
on service offerings (#315)
9159ca8 is described below
commit 9159ca8294bc58ff92d997c687c3c517e958d35e
Author: poddm <[email protected]>
AuthorDate: Tue Sep 15 05:22:17 2026 -0700
Add GPU support: gpu_card / vgpu_profile data sources and GPU on service
offerings (#315)
---
.github/actions/setup-cloudstack/action.yml | 28 +++
.gitignore | 3 +
cloudstack/data_source_cloudstack_gpu_card.go | 153 ++++++++++++++++
cloudstack/data_source_cloudstack_gpu_card_test.go | 51 ++++++
cloudstack/data_source_cloudstack_vgpu_profile.go | 197 +++++++++++++++++++++
.../data_source_cloudstack_vgpu_profile_test.go | 50 ++++++
cloudstack/provider.go | 2 +
cloudstack/provider_test.go | 31 ++++
.../service_offering_constrained_resource.go | 22 +--
.../service_offering_constrained_resource_test.go | 58 ++++++
cloudstack/service_offering_fixed_resource.go | 22 +--
cloudstack/service_offering_fixed_resource_test.go | 53 ++++++
cloudstack/service_offering_models.go | 7 +
cloudstack/service_offering_schema.go | 44 +++++
.../service_offering_unconstrained_resource.go | 22 +--
...service_offering_unconstrained_resource_test.go | 47 +++++
cloudstack/service_offering_util.go | 65 ++++++-
website/docs/d/gpu_card.html.markdown | 51 ++++++
website/docs/d/vgpu_profile.html.markdown | 58 ++++++
.../r/service_offering_constrained.html.markdown | 12 ++
.../docs/r/service_offering_fixed.html.markdown | 11 ++
.../r/service_offering_unconstrained.html.markdown | 11 ++
22 files changed, 949 insertions(+), 49 deletions(-)
diff --git a/.github/actions/setup-cloudstack/action.yml
b/.github/actions/setup-cloudstack/action.yml
index 7d53ab8..f20bd52 100644
--- a/.github/actions/setup-cloudstack/action.yml
+++ b/.github/actions/setup-cloudstack/action.yml
@@ -96,6 +96,34 @@ runs:
echo "api_key=$CLOUDSTACK_API_KEY" >> $GITHUB_OUTPUT
echo "secret_key=$CLOUDSTACK_SECRET_KEY" >> $GITHUB_OUTPUT
+ - name: Discover GPU devices
+ shell: bash
+ run: |
+
+ set -euo pipefail
+
+ # GPU management APIs require CloudStack 4.22.0.0 or newer.
+ CURRENT_VERSION="${{ inputs.cloudstack-version }}"
+ if [ "$(printf '%s\n4.22.0.0\n' "$CURRENT_VERSION" | sort -V | head
-n1)" != "4.22.0.0" ]; then
+ echo "CloudStack $CURRENT_VERSION predates GPU support (4.22.0.0+);
skipping GPU discovery."
+ exit 0
+ fi
+
+ CONTAINER_ID=$(docker container ls --format=json -l | jq -r .ID)
+
+ # Refresh the API cache so cmk knows the 4.22+ GPU APIs (the bundled
+ # cache predates them); "discover gpudevices" maps to
discoverGpuDevices.
+ docker exec $CONTAINER_ID cmk -p localcloud sync
+
+ # Discover GPU devices on each routing host so the simulator exposes
GPU
+ # cards and vGPU profiles for the GPU acceptance tests.
discoverGpuDevices
+ # takes a required "id" argument that is the host's UUID.
+ HOST_IDS=$(docker exec $CONTAINER_ID cmk -p localcloud list hosts
type=Routing --output json | jq -r '.host[].id')
+ for HOST_ID in $HOST_IDS; do
+ echo "Discovering GPU devices on host $HOST_ID"
+ docker exec $CONTAINER_ID cmk -p localcloud discover gpudevices
id=$HOST_ID
+ done
+
- name: Install CMK
shell: bash
run: |
diff --git a/.gitignore b/.gitignore
index 2c88f78..0980d2a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,6 +23,9 @@ website/node_modules
*.iml
*.test
*.iml
+dist/
+.gocache/
+.gomodcache/
website/vendor
diff --git a/cloudstack/data_source_cloudstack_gpu_card.go
b/cloudstack/data_source_cloudstack_gpu_card.go
new file mode 100644
index 0000000..b7224bb
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_gpu_card.go
@@ -0,0 +1,153 @@
+//
+// 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"
+ "log"
+ "strconv"
+
+ "github.com/apache/cloudstack-go/v2/cloudstack"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+)
+
+func dataSourceCloudstackGpuCard() *schema.Resource {
+ return &schema.Resource{
+ Read: datasourceCloudStackGpuCardRead,
+ Schema: map[string]*schema.Schema{
+ "filter": dataSourceFiltersSchema(),
+
+ //Computed values
+ "id": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "name": {
+ Description: "the name of the GPU card",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "device_id": {
+ Description: "the device id of the GPU card",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "device_name": {
+ Description: "the device name of the GPU card",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "vendor_id": {
+ Description: "the vendor id of the GPU card",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "vendor_name": {
+ Description: "the vendor name of the GPU card",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ }
+}
+
+func datasourceCloudStackGpuCardRead(d *schema.ResourceData, meta interface{})
error {
+ cs := meta.(*cloudstack.CloudStackClient)
+ p := cs.GPU.NewListGpuCardsParams()
+
+ if err := applyGpuCardFilters(p, d.Get("filter").(*schema.Set)); err !=
nil {
+ return err
+ }
+
+ csGpuCards, err := cs.GPU.ListGpuCards(p)
+ if err != nil {
+ return fmt.Errorf("failed to list GPU cards: %s", err)
+ }
+
+ switch len(csGpuCards.GpuCards) {
+ case 0:
+ return fmt.Errorf("no GPU cards found")
+ case 1:
+ return gpuCardDescriptionAttributes(d, csGpuCards.GpuCards[0])
+ default:
+ return fmt.Errorf("%d GPU cards matched the given filters; "+
+ "refine the filters (e.g. add device_id or vendor_id)
to match exactly one card", len(csGpuCards.GpuCards))
+ }
+}
+
+func gpuCardDescriptionAttributes(d *schema.ResourceData, card
*cloudstack.GpuCard) error {
+ d.SetId(card.Id)
+
+ fields := map[string]interface{}{
+ "id": card.Id,
+ "name": card.Name,
+ "device_id": card.Deviceid,
+ "device_name": card.Devicename,
+ "vendor_id": card.Vendorid,
+ "vendor_name": card.Vendorname,
+ }
+
+ for k, v := range fields {
+ if err := d.Set(k, v); err != nil {
+ log.Printf("[WARN] Error setting %s: %s", k, err)
+ }
+ }
+
+ return nil
+}
+
+func applyGpuCardFilters(p *cloudstack.ListGpuCardsParams, filters
*schema.Set) error {
+ seen := make(map[string]bool)
+ for _, f := range filters.List() {
+ filter := f.(map[string]interface{})
+ name := filter["name"].(string)
+ value := filter["value"].(string)
+
+ if seen[name] {
+ return fmt.Errorf("duplicate filter %q; each filter
name may only be specified once", name)
+ }
+ seen[name] = true
+
+ switch name {
+ case "id":
+ p.SetId(value)
+ case "device_id":
+ p.SetDeviceid(value)
+ case "device_name":
+ p.SetDevicename(value)
+ case "vendor_id":
+ p.SetVendorid(value)
+ case "vendor_name":
+ p.SetVendorname(value)
+ case "keyword":
+ p.SetKeyword(value)
+ case "active_only":
+ b, err := strconv.ParseBool(value)
+ if err != nil {
+ return fmt.Errorf("invalid boolean value %q for
filter %q: %s", value, name, err)
+ }
+ p.SetActiveonly(b)
+ default:
+ return fmt.Errorf("unsupported filter %q; supported
filters: id, device_id, device_name, vendor_id, vendor_name, keyword,
active_only", name)
+ }
+ }
+
+ return nil
+}
diff --git a/cloudstack/data_source_cloudstack_gpu_card_test.go
b/cloudstack/data_source_cloudstack_gpu_card_test.go
new file mode 100644
index 0000000..9d2a43d
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_gpu_card_test.go
@@ -0,0 +1,51 @@
+//
+// 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 TestAccGpuCardDataSource_basic(t *testing.T) {
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheckGPU(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ {
+ Config: testGpuCardDataSourceConfig_basic,
+ Check: resource.ComposeTestCheckFunc(
+
resource.TestCheckResourceAttrSet("data.cloudstack_gpu_card.test", "id"),
+
resource.TestCheckResourceAttr("data.cloudstack_gpu_card.test", "name",
"Simulator Graphics Card Pro"),
+ ),
+ },
+ },
+ })
+}
+
+const testGpuCardDataSourceConfig_basic = `
+data "cloudstack_gpu_card" "test" {
+ filter {
+ name = "device_name"
+ value = "Graphics Card Pro"
+ }
+}
+`
diff --git a/cloudstack/data_source_cloudstack_vgpu_profile.go
b/cloudstack/data_source_cloudstack_vgpu_profile.go
new file mode 100644
index 0000000..453af4f
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_vgpu_profile.go
@@ -0,0 +1,197 @@
+//
+// 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"
+ "log"
+ "strconv"
+
+ "github.com/apache/cloudstack-go/v2/cloudstack"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+)
+
+func dataSourceCloudstackVgpuProfile() *schema.Resource {
+ return &schema.Resource{
+ Read: datasourceCloudStackVgpuProfileRead,
+ Schema: map[string]*schema.Schema{
+ "filter": dataSourceFiltersSchema(),
+
+ //Computed values
+ "id": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "name": {
+ Description: "the name of the vGPU profile",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "description": {
+ Description: "the description of the vGPU
profile",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "device_id": {
+ Description: "the device id of the GPU card",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "device_name": {
+ Description: "the device name of the GPU card",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "gpu_card_id": {
+ Description: "the GPU card id of the vGPU
profile",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "gpu_card_name": {
+ Description: "the GPU card name of the vGPU
profile",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "max_heads": {
+ Description: "the maximum displays per vGPU
instance",
+ Type: schema.TypeInt,
+ Computed: true,
+ },
+ "max_resolution_x": {
+ Description: "the maximum X resolution per
display",
+ Type: schema.TypeInt,
+ Computed: true,
+ },
+ "max_resolution_y": {
+ Description: "the maximum Y resolution per
display",
+ Type: schema.TypeInt,
+ Computed: true,
+ },
+ "max_vgpu_per_physical_gpu": {
+ Description: "the maximum number of vGPU
instances per physical GPU",
+ Type: schema.TypeInt,
+ Computed: true,
+ },
+ "vendor_id": {
+ Description: "the vendor id of the GPU card",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "vendor_name": {
+ Description: "the vendor name of the GPU card",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "video_ram": {
+ Description: "the video RAM size in MB for the
vGPU profile",
+ Type: schema.TypeInt,
+ Computed: true,
+ },
+ },
+ }
+}
+
+func datasourceCloudStackVgpuProfileRead(d *schema.ResourceData, meta
interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+ p := cs.GPU.NewListVgpuProfilesParams()
+
+ if err := applyVgpuProfileFilters(p, d.Get("filter").(*schema.Set));
err != nil {
+ return err
+ }
+
+ csVgpuProfiles, err := cs.GPU.ListVgpuProfiles(p)
+ if err != nil {
+ return fmt.Errorf("failed to list vGPU profiles: %s", err)
+ }
+
+ switch len(csVgpuProfiles.VgpuProfiles) {
+ case 0:
+ return fmt.Errorf("no vGPU profiles found")
+ case 1:
+ return vgpuProfileDescriptionAttributes(d,
csVgpuProfiles.VgpuProfiles[0])
+ default:
+ return fmt.Errorf("%d vGPU profiles matched the given filters;
"+
+ "refine the filters (e.g. add gpu_card_id) to match
exactly one profile", len(csVgpuProfiles.VgpuProfiles))
+ }
+}
+
+func vgpuProfileDescriptionAttributes(d *schema.ResourceData, profile
*cloudstack.VgpuProfile) error {
+ d.SetId(profile.Id)
+
+ fields := map[string]interface{}{
+ "id": profile.Id,
+ "name": profile.Name,
+ "description": profile.Description,
+ "device_id": profile.Deviceid,
+ "device_name": profile.Devicename,
+ "gpu_card_id": profile.Gpucardid,
+ "gpu_card_name": profile.Gpucardname,
+ "max_heads": profile.Maxheads,
+ "max_resolution_x": profile.Maxresolutionx,
+ "max_resolution_y": profile.Maxresolutiony,
+ "max_vgpu_per_physical_gpu": profile.Maxvgpuperphysicalgpu,
+ "vendor_id": profile.Vendorid,
+ "vendor_name": profile.Vendorname,
+ "video_ram": profile.Videoram,
+ }
+
+ for k, v := range fields {
+ if err := d.Set(k, v); err != nil {
+ log.Printf("[WARN] Error setting %s: %s", k, err)
+ }
+ }
+
+ return nil
+}
+
+func applyVgpuProfileFilters(p *cloudstack.ListVgpuProfilesParams, filters
*schema.Set) error {
+ seen := make(map[string]bool)
+ for _, f := range filters.List() {
+ filter := f.(map[string]interface{})
+ name := filter["name"].(string)
+ value := filter["value"].(string)
+
+ if seen[name] {
+ return fmt.Errorf("duplicate filter %q; each filter
name may only be specified once", name)
+ }
+ seen[name] = true
+
+ switch name {
+ case "id":
+ p.SetId(value)
+ case "name":
+ p.SetName(value)
+ case "gpu_card_id":
+ p.SetGpucardid(value)
+ case "keyword":
+ p.SetKeyword(value)
+ case "active_only":
+ b, err := strconv.ParseBool(value)
+ if err != nil {
+ return fmt.Errorf("invalid boolean value %q for
filter %q: %s", value, name, err)
+ }
+ p.SetActiveonly(b)
+ default:
+ return fmt.Errorf("unsupported filter %q; supported
filters: id, name, gpu_card_id, keyword, active_only", name)
+ }
+ }
+
+ return nil
+}
diff --git a/cloudstack/data_source_cloudstack_vgpu_profile_test.go
b/cloudstack/data_source_cloudstack_vgpu_profile_test.go
new file mode 100644
index 0000000..284f9fe
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_vgpu_profile_test.go
@@ -0,0 +1,50 @@
+//
+// 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 TestAccVgpuProfileDataSource_basic(t *testing.T) {
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheckGPU(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ {
+ Config: testVgpuProfileDataSourceConfig_basic,
+ Check: resource.ComposeTestCheckFunc(
+
resource.TestCheckResourceAttr("data.cloudstack_vgpu_profile.test", "name",
"sim-8q"),
+ ),
+ },
+ },
+ })
+}
+
+const testVgpuProfileDataSourceConfig_basic = `
+data "cloudstack_vgpu_profile" "test" {
+ filter {
+ name = "name"
+ value = "sim-8q"
+ }
+}
+`
diff --git a/cloudstack/provider.go b/cloudstack/provider.go
index f4d3a4a..81bd20b 100644
--- a/cloudstack/provider.go
+++ b/cloudstack/provider.go
@@ -109,6 +109,8 @@ func Provider() *schema.Provider {
"cloudstack_user_data":
dataSourceCloudstackUserData(),
"cloudstack_kubernetes_cluster_config":
dataSourceCloudstackKubernetesClusterConfig(),
"cloudstack_security_group":
dataSourceCloudstackSecurityGroup(),
+ "cloudstack_vgpu_profile":
dataSourceCloudstackVgpuProfile(),
+ "cloudstack_gpu_card":
dataSourceCloudstackGpuCard(),
},
ResourcesMap: map[string]*schema.Resource{
diff --git a/cloudstack/provider_test.go b/cloudstack/provider_test.go
index ca47cbf..190dc3b 100644
--- a/cloudstack/provider_test.go
+++ b/cloudstack/provider_test.go
@@ -206,6 +206,37 @@ func testAccPreCheckStaticRouteNexthop(t *testing.T) {
requireMinimumCloudStackVersion(t, minVersionNum, "Static route nexthop
parameter")
}
+// testAccPreCheckGPU checks if the CloudStack version supports GPU features
(requires 4.22.0+)
+// and that GPU devices have been discovered on the hosts. vGPU profiles only
exist once GPU
+// devices are discovered (via the discoverGpuDevices API); when none are
present the GPU tests
+// are skipped so environments without GPU hardware (e.g. the CI simulator) do
not fail.
+func testAccPreCheckGPU(t *testing.T) {
+ testAccPreCheck(t)
+
+ const minVersionNum = 4022 // 4.22.0
+ requireMinimumCloudStackVersion(t, minVersionNum, "GPU card and vGPU
profile support")
+
+ requireVgpuProfiles(t)
+}
+
+// requireVgpuProfiles skips the test when no vGPU profiles are available.
vGPU profiles are only
+// created once GPU devices have been discovered on the hosts (via the
discoverGpuDevices API),
+// which is not the case in environments without GPU hardware.
+func requireVgpuProfiles(t *testing.T) {
+ t.Helper()
+ cs := newTestClient(t)
+
+ p := cs.GPU.NewListVgpuProfilesParams()
+ profiles, err := cs.GPU.ListVgpuProfiles(p)
+ if err != nil {
+ t.Fatalf("Failed to list vGPU profiles: %v", err)
+ }
+
+ if profiles.Count == 0 {
+ t.Skip("No vGPU profiles found; discover GPU devices on the
hosts (discoverGpuDevices) to run GPU acceptance tests")
+ }
+}
+
// newTestClient creates a CloudStack client from environment variables for
use in test PreCheck functions.
// This is needed because PreCheck functions run before the test framework
configures the provider,
// so testAccProvider.Meta() is nil at that point.
diff --git a/cloudstack/service_offering_constrained_resource.go
b/cloudstack/service_offering_constrained_resource.go
index 92c8077..4ada5b6 100644
--- a/cloudstack/service_offering_constrained_resource.go
+++ b/cloudstack/service_offering_constrained_resource.go
@@ -93,6 +93,7 @@ func (r *serviceOfferingConstrainedResource) Create(ctx
context.Context, req res
var planDiskQosHypervisor ServiceOfferingDiskQosHypervisor
var planDiskOffering ServiceOfferingDiskOffering
var planDiskQosStorage ServiceOfferingDiskQosStorage
+ var planGpu ServiceOfferingGpu
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
if !plan.ServiceOfferingDiskQosHypervisor.IsNull() {
@@ -104,6 +105,9 @@ func (r *serviceOfferingConstrainedResource) Create(ctx
context.Context, req res
if !plan.ServiceOfferingDiskQosStorage.IsNull() {
resp.Diagnostics.Append(plan.ServiceOfferingDiskQosStorage.As(ctx,
&planDiskQosStorage, basetypes.ObjectAsOptions{})...)
}
+ if !plan.ServiceOfferingGpu.IsNull() {
+ resp.Diagnostics.Append(plan.ServiceOfferingGpu.As(ctx,
&planGpu, basetypes.ObjectAsOptions{})...)
+ }
if resp.Diagnostics.HasError() {
return
}
@@ -114,6 +118,7 @@ func (r *serviceOfferingConstrainedResource) Create(ctx
context.Context, req res
planDiskQosHypervisor.commonCreateParams(ctx, params)
planDiskOffering.commonCreateParams(ctx, params)
planDiskQosStorage.commonCreateParams(ctx, params)
+ planGpu.commonCreateParams(ctx, params)
// resource specific params
if !plan.CpuSpeed.IsNull() {
@@ -151,20 +156,8 @@ func (r *serviceOfferingConstrainedResource) Create(ctx
context.Context, req res
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
}
@@ -227,10 +220,7 @@ func (r *serviceOfferingConstrainedResource) Read(ctx
context.Context, req resou
state.MinMemory = types.Int32Value(int32(i))
}
- state.commonRead(ctx, cs)
- stateDiskQosHypervisor.commonRead(ctx, cs)
- stateDiskOffering.commonRead(ctx, cs)
- stateDiskQosStorage.commonRead(ctx, cs)
+ resp.Diagnostics.Append(state.commonRead(ctx, cs)...)
if resp.Diagnostics.HasError() {
return
}
diff --git a/cloudstack/service_offering_constrained_resource_test.go
b/cloudstack/service_offering_constrained_resource_test.go
index 5368db7..6cb90d3 100644
--- a/cloudstack/service_offering_constrained_resource_test.go
+++ b/cloudstack/service_offering_constrained_resource_test.go
@@ -76,6 +76,24 @@ func TestAccServiceOfferingConstrained(t *testing.T) {
})
}
+func TestAccServiceOfferingConstrained_GPU(t *testing.T) {
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheckGPU(t) },
+ ProtoV6ProviderFactories: testAccMuxProvider,
+ Steps: []resource.TestStep{
+ {
+ Config:
testAccServiceOfferingCustomConstrained_gpu,
+ Check: resource.ComposeTestCheckFunc(
+
resource.TestCheckResourceAttr("cloudstack_service_offering_constrained.gpu",
"name", "gpu"),
+
resource.TestCheckResourceAttrPair("cloudstack_service_offering_constrained.gpu",
"gpu.vgpu_profile_id", "data.cloudstack_vgpu_profile.test", "id"),
+
resource.TestCheckResourceAttr("cloudstack_service_offering_constrained.gpu",
"gpu.count", "1"),
+
resource.TestCheckResourceAttr("cloudstack_service_offering_constrained.gpu",
"gpu.display", "true"),
+ ),
+ },
+ },
+ })
+}
+
const testAccServiceOfferingCustomConstrained1 = `
resource "cloudstack_zone" "test" {
name = "acctest"
@@ -274,6 +292,46 @@ resource "cloudstack_service_offering_constrained"
"disk_hypervisor" {
}
`
+const testAccServiceOfferingCustomConstrained_gpu = `
+data "cloudstack_vgpu_profile" "test" {
+ filter {
+ name = "name"
+ value = "sim-8q"
+ }
+}
+
+resource "cloudstack_service_offering_constrained" "gpu" {
+ display_text = "gpu"
+ name = "gpu"
+
+ // 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
+
+ gpu = {
+ vgpu_profile_id = data.cloudstack_vgpu_profile.test.id
+ count = 1
+ display = true
+ }
+}
+`
+
const testAccServiceOfferingCustomConstrained_disk_storage = `
resource "cloudstack_service_offering_constrained" "disk_storage" {
display_text = "disk_storage"
diff --git a/cloudstack/service_offering_fixed_resource.go
b/cloudstack/service_offering_fixed_resource.go
index 6b500fd..3bbfd28 100644
--- a/cloudstack/service_offering_fixed_resource.go
+++ b/cloudstack/service_offering_fixed_resource.go
@@ -78,6 +78,7 @@ func (r *serviceOfferingFixedResource) Create(ctx
context.Context, req resource.
var planDiskQosHypervisor ServiceOfferingDiskQosHypervisor
var planDiskOffering ServiceOfferingDiskOffering
var planDiskQosStorage ServiceOfferingDiskQosStorage
+ var planGpu ServiceOfferingGpu
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
if !plan.ServiceOfferingDiskQosHypervisor.IsNull() {
@@ -89,6 +90,9 @@ func (r *serviceOfferingFixedResource) Create(ctx
context.Context, req resource.
if !plan.ServiceOfferingDiskQosStorage.IsNull() {
resp.Diagnostics.Append(plan.ServiceOfferingDiskQosStorage.As(ctx,
&planDiskQosStorage, basetypes.ObjectAsOptions{})...)
}
+ if !plan.ServiceOfferingGpu.IsNull() {
+ resp.Diagnostics.Append(plan.ServiceOfferingGpu.As(ctx,
&planGpu, basetypes.ObjectAsOptions{})...)
+ }
if resp.Diagnostics.HasError() {
return
}
@@ -99,6 +103,7 @@ func (r *serviceOfferingFixedResource) Create(ctx
context.Context, req resource.
planDiskQosHypervisor.commonCreateParams(ctx, params)
planDiskOffering.commonCreateParams(ctx, params)
planDiskQosStorage.commonCreateParams(ctx, params)
+ planGpu.commonCreateParams(ctx, params)
// resource specific params
if !plan.CpuNumber.IsNull() {
@@ -128,20 +133,8 @@ func (r *serviceOfferingFixedResource) Create(ctx
context.Context, req resource.
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
}
@@ -166,10 +159,7 @@ func (r *serviceOfferingFixedResource) Read(ctx
context.Context, req resource.Re
state.Memory = types.Int32Value(int32(cs.Memory))
}
- state.commonRead(ctx, cs)
- stateDiskQosHypervisor.commonRead(ctx, cs)
- stateDiskOffering.commonRead(ctx, cs)
- stateDiskQosStorage.commonRead(ctx, cs)
+ resp.Diagnostics.Append(state.commonRead(ctx, cs)...)
if resp.Diagnostics.HasError() {
return
}
diff --git a/cloudstack/service_offering_fixed_resource_test.go
b/cloudstack/service_offering_fixed_resource_test.go
index 438740e..12de429 100644
--- a/cloudstack/service_offering_fixed_resource_test.go
+++ b/cloudstack/service_offering_fixed_resource_test.go
@@ -70,6 +70,24 @@ func TestAccServiceOfferingFixed(t *testing.T) {
})
}
+func TestAccServiceOfferingFixed_GPU(t *testing.T) {
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheckGPU(t) },
+ ProtoV6ProviderFactories: testAccMuxProvider,
+ Steps: []resource.TestStep{
+ {
+ Config: testAccServiceOfferingFixed_gpu,
+ Check: resource.ComposeTestCheckFunc(
+
resource.TestCheckResourceAttr("cloudstack_service_offering_fixed.gpu", "name",
"gpu"),
+
resource.TestCheckResourceAttrPair("cloudstack_service_offering_fixed.gpu",
"gpu.vgpu_profile_id", "data.cloudstack_vgpu_profile.test", "id"),
+
resource.TestCheckResourceAttr("cloudstack_service_offering_fixed.gpu",
"gpu.count", "1"),
+
resource.TestCheckResourceAttr("cloudstack_service_offering_fixed.gpu",
"gpu.display", "true"),
+ ),
+ },
+ },
+ })
+}
+
const testAccServiceOfferingFixed1 = `
resource "cloudstack_service_offering_fixed" "fixed1" {
display_text = "fixed1"
@@ -237,3 +255,38 @@ resource "cloudstack_service_offering_fixed"
"disk_storage" {
}
}
`
+
+const testAccServiceOfferingFixed_gpu = `
+data "cloudstack_vgpu_profile" "test" {
+ filter {
+ name = "name"
+ value = "sim-8q"
+ }
+}
+
+resource "cloudstack_service_offering_fixed" "gpu" {
+ display_text = "gpu"
+ name = "gpu"
+
+ // 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
+
+ gpu = {
+ vgpu_profile_id = data.cloudstack_vgpu_profile.test.id
+ count = 1
+ display = true
+ }
+}
+`
diff --git a/cloudstack/service_offering_models.go
b/cloudstack/service_offering_models.go
index a93ffa4..35c6dd3 100644
--- a/cloudstack/service_offering_models.go
+++ b/cloudstack/service_offering_models.go
@@ -58,6 +58,7 @@ type serviceOfferingCommonResourceModel struct {
ServiceOfferingDiskQosHypervisor types.Object `tfsdk:"disk_hypervisor"`
ServiceOfferingDiskOffering types.Object `tfsdk:"disk_offering"`
ServiceOfferingDiskQosStorage types.Object `tfsdk:"disk_storage"`
+ ServiceOfferingGpu types.Object `tfsdk:"gpu"`
}
type ServiceOfferingDiskQosHypervisor struct {
@@ -84,3 +85,9 @@ type ServiceOfferingDiskQosStorage struct {
MaxIops types.Int64 `tfsdk:"max_iops"`
MinIops types.Int64 `tfsdk:"min_iops"`
}
+
+type ServiceOfferingGpu struct {
+ VgpuProfileId types.String `tfsdk:"vgpu_profile_id"`
+ Count types.Int32 `tfsdk:"count"`
+ Display types.Bool `tfsdk:"display"`
+}
diff --git a/cloudstack/service_offering_schema.go
b/cloudstack/service_offering_schema.go
index 9586d51..368bd5e 100644
--- a/cloudstack/service_offering_schema.go
+++ b/cloudstack/service_offering_schema.go
@@ -27,8 +27,10 @@ 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/int32default"
"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/objectplanmodifier"
"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"
@@ -115,6 +117,9 @@ func serviceOfferingMergeCommonSchema(s1
map[string]schema.Attribute) map[string
},
"disk_offering": schema.SingleNestedAttribute{
Optional: true,
+ PlanModifiers: []planmodifier.Object{
+ objectplanmodifier.RequiresReplace(),
+ },
Attributes: map[string]schema.Attribute{
"cache_mode": schema.StringAttribute{
Description: "the cache mode to use for
this disk offering. none, writeback or writethrough",
@@ -159,6 +164,9 @@ func serviceOfferingMergeCommonSchema(s1
map[string]schema.Attribute) map[string
},
"disk_hypervisor": schema.SingleNestedAttribute{
Optional: true,
+ PlanModifiers: []planmodifier.Object{
+ objectplanmodifier.RequiresReplace(),
+ },
Attributes: map[string]schema.Attribute{
"bytes_read_rate": schema.Int64Attribute{
Description: "io requests read rate of
the disk offering",
@@ -206,6 +214,9 @@ func serviceOfferingMergeCommonSchema(s1
map[string]schema.Attribute) map[string
},
"disk_storage": schema.SingleNestedAttribute{
Optional: true,
+ PlanModifiers: []planmodifier.Object{
+ objectplanmodifier.RequiresReplace(),
+ },
Attributes: map[string]schema.Attribute{
"customized_iops": schema.BoolAttribute{
Description: "true if disk offering
uses custom iops, false otherwise",
@@ -237,6 +248,39 @@ func serviceOfferingMergeCommonSchema(s1
map[string]schema.Attribute) map[string
},
},
},
+ "gpu": schema.SingleNestedAttribute{
+ Optional: true,
+ PlanModifiers: []planmodifier.Object{
+ objectplanmodifier.RequiresReplace(),
+ },
+ Attributes: map[string]schema.Attribute{
+ "vgpu_profile_id": schema.StringAttribute{
+ Description: "the ID of the vGPU
profile to associate with the service offering",
+ Required: true,
+ PlanModifiers: []planmodifier.String{
+
stringplanmodifier.RequiresReplace(),
+ },
+ },
+ "count": schema.Int32Attribute{
+ Description: "the number of GPUs to
assign to the guest VM",
+ Optional: true,
+ Computed: true,
+ PlanModifiers: []planmodifier.Int32{
+
int32planmodifier.RequiresReplace(),
+ },
+ Default: int32default.StaticInt32(1),
+ },
+ "display": schema.BoolAttribute{
+ Description: "whether the GPU is
presented as a display device to the guest VM",
+ Optional: true,
+ Computed: true,
+ PlanModifiers: []planmodifier.Bool{
+
boolplanmodifier.RequiresReplace(),
+ },
+ Default: booldefault.StaticBool(false),
+ },
+ },
+ },
}
for key, value := range s1 {
diff --git a/cloudstack/service_offering_unconstrained_resource.go
b/cloudstack/service_offering_unconstrained_resource.go
index 98b937c..fc8a319 100644
--- a/cloudstack/service_offering_unconstrained_resource.go
+++ b/cloudstack/service_offering_unconstrained_resource.go
@@ -55,6 +55,7 @@ func (r *serviceOfferingUnconstrainedResource) Create(ctx
context.Context, req r
var planDiskQosHypervisor ServiceOfferingDiskQosHypervisor
var planDiskOffering ServiceOfferingDiskOffering
var planDiskQosStorage ServiceOfferingDiskQosStorage
+ var planGpu ServiceOfferingGpu
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
if !plan.ServiceOfferingDiskQosHypervisor.IsNull() {
@@ -66,6 +67,9 @@ func (r *serviceOfferingUnconstrainedResource) Create(ctx
context.Context, req r
if !plan.ServiceOfferingDiskQosStorage.IsNull() {
resp.Diagnostics.Append(plan.ServiceOfferingDiskQosStorage.As(ctx,
&planDiskQosStorage, basetypes.ObjectAsOptions{})...)
}
+ if !plan.ServiceOfferingGpu.IsNull() {
+ resp.Diagnostics.Append(plan.ServiceOfferingGpu.As(ctx,
&planGpu, basetypes.ObjectAsOptions{})...)
+ }
if resp.Diagnostics.HasError() {
return
}
@@ -76,6 +80,7 @@ func (r *serviceOfferingUnconstrainedResource) Create(ctx
context.Context, req r
planDiskQosHypervisor.commonCreateParams(ctx, params)
planDiskOffering.commonCreateParams(ctx, params)
planDiskQosStorage.commonCreateParams(ctx, params)
+ planGpu.commonCreateParams(ctx, params)
// create offering
cs, err := r.client.ServiceOffering.CreateServiceOffering(params)
@@ -94,20 +99,8 @@ func (r *serviceOfferingUnconstrainedResource) Create(ctx
context.Context, req r
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
}
@@ -121,10 +114,7 @@ func (r *serviceOfferingUnconstrainedResource) Read(ctx
context.Context, req res
return
}
- state.commonRead(ctx, cs)
- stateDiskQosHypervisor.commonRead(ctx, cs)
- stateDiskOffering.commonRead(ctx, cs)
- stateDiskQosStorage.commonRead(ctx, cs)
+ resp.Diagnostics.Append(state.commonRead(ctx, cs)...)
if resp.Diagnostics.HasError() {
return
}
diff --git a/cloudstack/service_offering_unconstrained_resource_test.go
b/cloudstack/service_offering_unconstrained_resource_test.go
index 5aba779..d592dc7 100644
--- a/cloudstack/service_offering_unconstrained_resource_test.go
+++ b/cloudstack/service_offering_unconstrained_resource_test.go
@@ -70,6 +70,24 @@ func TestAccServiceOfferingUnconstrained(t *testing.T) {
})
}
+func TestAccServiceOfferingUnconstrained_GPU(t *testing.T) {
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheckGPU(t) },
+ ProtoV6ProviderFactories: testAccMuxProvider,
+ Steps: []resource.TestStep{
+ {
+ Config: testAccServiceOfferingUnconstrained_gpu,
+ Check: resource.ComposeTestCheckFunc(
+
resource.TestCheckResourceAttr("cloudstack_service_offering_unconstrained.gpu",
"name", "gpu"),
+
resource.TestCheckResourceAttrPair("cloudstack_service_offering_unconstrained.gpu",
"gpu.vgpu_profile_id", "data.cloudstack_vgpu_profile.test", "id"),
+
resource.TestCheckResourceAttr("cloudstack_service_offering_unconstrained.gpu",
"gpu.count", "1"),
+
resource.TestCheckResourceAttr("cloudstack_service_offering_unconstrained.gpu",
"gpu.display", "true"),
+ ),
+ },
+ },
+ })
+}
+
const testAccServiceOfferingUnconstrained1 = `
resource "cloudstack_service_offering_unconstrained" "unconstrained1" {
display_text = "unconstrained1"
@@ -205,3 +223,32 @@ resource "cloudstack_service_offering_unconstrained"
"disk_storage" {
}
}
`
+
+const testAccServiceOfferingUnconstrained_gpu = `
+data "cloudstack_vgpu_profile" "test" {
+ filter {
+ name = "name"
+ value = "sim-8q"
+ }
+}
+
+resource "cloudstack_service_offering_unconstrained" "gpu" {
+ display_text = "gpu"
+ name = "gpu"
+
+ host_tags = "test0101,test0202"
+ network_rate = 1024
+ deployment_planner = "UserDispersingPlanner"
+
+ dynamic_scaling_enabled = true
+ is_volatile = true
+ limit_cpu_use = true
+ offer_ha = true
+
+ gpu = {
+ vgpu_profile_id = data.cloudstack_vgpu_profile.test.id
+ count = 1
+ display = true
+ }
+}
+`
diff --git a/cloudstack/service_offering_util.go
b/cloudstack/service_offering_util.go
index 666e0fc..6b5de5a 100644
--- a/cloudstack/service_offering_util.go
+++ b/cloudstack/service_offering_util.go
@@ -22,7 +22,9 @@ import (
"strings"
"github.com/apache/cloudstack-go/v2/cloudstack"
+ "github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/hashicorp/terraform-plugin-framework/types/basetypes"
)
//
------------------------------------------------------------------------------------------------------------------------------
@@ -72,7 +74,9 @@ func (plan *serviceOfferingCommonResourceModel)
commonUpdateParams(ctx context.C
//
------------------------------------------------------------------------------------------------------------------------------
// common Read methods
// -
-func (state *serviceOfferingCommonResourceModel) commonRead(ctx
context.Context, cs *cloudstack.ServiceOffering) {
+func (state *serviceOfferingCommonResourceModel) commonRead(ctx
context.Context, cs *cloudstack.ServiceOffering) diag.Diagnostics {
+ var diags diag.Diagnostics
+
state.Id = types.StringValue(cs.Id)
if cs.Deploymentplanner != "" {
@@ -108,6 +112,41 @@ func (state *serviceOfferingCommonResourceModel)
commonRead(ctx context.Context,
state.LimitCpuUse = types.BoolValue(cs.Limitcpuuse)
state.OfferHa = types.BoolValue(cs.Offerha)
+ // Refresh the nested blocks and encode them back into state so drift
is detected
+ if !state.ServiceOfferingDiskQosHypervisor.IsNull() {
+ var v ServiceOfferingDiskQosHypervisor
+ diags.Append(state.ServiceOfferingDiskQosHypervisor.As(ctx, &v,
basetypes.ObjectAsOptions{})...)
+ v.commonRead(ctx, cs)
+ obj, d := types.ObjectValueFrom(ctx,
state.ServiceOfferingDiskQosHypervisor.AttributeTypes(ctx), v)
+ diags.Append(d...)
+ state.ServiceOfferingDiskQosHypervisor = obj
+ }
+ if !state.ServiceOfferingDiskOffering.IsNull() {
+ var v ServiceOfferingDiskOffering
+ diags.Append(state.ServiceOfferingDiskOffering.As(ctx, &v,
basetypes.ObjectAsOptions{})...)
+ v.commonRead(ctx, cs)
+ obj, d := types.ObjectValueFrom(ctx,
state.ServiceOfferingDiskOffering.AttributeTypes(ctx), v)
+ diags.Append(d...)
+ state.ServiceOfferingDiskOffering = obj
+ }
+ if !state.ServiceOfferingDiskQosStorage.IsNull() {
+ var v ServiceOfferingDiskQosStorage
+ diags.Append(state.ServiceOfferingDiskQosStorage.As(ctx, &v,
basetypes.ObjectAsOptions{})...)
+ v.commonRead(ctx, cs)
+ obj, d := types.ObjectValueFrom(ctx,
state.ServiceOfferingDiskQosStorage.AttributeTypes(ctx), v)
+ diags.Append(d...)
+ state.ServiceOfferingDiskQosStorage = obj
+ }
+ if !state.ServiceOfferingGpu.IsNull() {
+ var v ServiceOfferingGpu
+ diags.Append(state.ServiceOfferingGpu.As(ctx, &v,
basetypes.ObjectAsOptions{})...)
+ v.commonRead(ctx, cs)
+ obj, d := types.ObjectValueFrom(ctx,
state.ServiceOfferingGpu.AttributeTypes(ctx), v)
+ diags.Append(d...)
+ state.ServiceOfferingGpu = obj
+ }
+
+ return diags
}
func (state *ServiceOfferingDiskQosHypervisor) commonRead(ctx context.Context,
cs *cloudstack.ServiceOffering) {
@@ -170,6 +209,16 @@ func (state *ServiceOfferingDiskQosStorage) commonRead(ctx
context.Context, cs *
}
+func (state *ServiceOfferingGpu) commonRead(ctx context.Context, cs
*cloudstack.ServiceOffering) {
+ if cs.Vgpuprofileid != "" {
+ state.VgpuProfileId = types.StringValue(cs.Vgpuprofileid)
+ } else {
+ state.VgpuProfileId = types.StringNull()
+ }
+ state.Count = types.Int32Value(int32(cs.Gpucount))
+ state.Display = types.BoolValue(cs.Gpudisplay)
+}
+
//
------------------------------------------------------------------------------------------------------------------------------
// common Create methods
// -
@@ -278,3 +327,17 @@ func (plan *ServiceOfferingDiskQosStorage)
commonCreateParams(ctx context.Contex
return p
}
+
+func (plan *ServiceOfferingGpu) commonCreateParams(ctx context.Context, p
*cloudstack.CreateServiceOfferingParams)
*cloudstack.CreateServiceOfferingParams {
+ if !plan.VgpuProfileId.IsNull() {
+ p.SetVgpuprofileid(plan.VgpuProfileId.ValueString())
+ }
+ if !plan.Count.IsNull() {
+ p.SetGpucount(int(plan.Count.ValueInt32()))
+ }
+ if !plan.Display.IsNull() {
+ p.SetGpudisplay(plan.Display.ValueBool())
+ }
+
+ return p
+}
diff --git a/website/docs/d/gpu_card.html.markdown
b/website/docs/d/gpu_card.html.markdown
new file mode 100644
index 0000000..087a5d8
--- /dev/null
+++ b/website/docs/d/gpu_card.html.markdown
@@ -0,0 +1,51 @@
+---
+layout: "cloudstack"
+page_title: "CloudStack: cloudstack_gpu_card"
+description: |-
+ Gets information about a GPU card.
+---
+
+# cloudstack_gpu_card
+
+Use this data source to get information about a GPU card for use in other
resources.
+
+## Example Usage
+
+```hcl
+data "cloudstack_gpu_card" "card" {
+ filter {
+ name = "keyword"
+ value = "NVIDIA"
+ }
+}
+
+output "gpu_card_id" {
+ value = data.cloudstack_gpu_card.card.id
+}
+```
+
+## Argument Reference
+
+The following arguments are supported:
+
+* `filter` - (Required) One or more name/value pairs to filter off of. See
detailed documentation below.
+
+### Filter Arguments
+
+* `name` - (Required) The name of the field to filter on. Filtering is
performed server-side by the
+ CloudStack API. Supported values are `id`, `device_id`, `device_name`,
`vendor_id`, `vendor_name`,
+ `keyword`, and `active_only`.
+* `value` - (Required) The value to filter on. This is passed directly to the
CloudStack API and
+ matched exactly (not as a regular expression). The filters must narrow the
result to a single GPU
+ card; if more than one card matches, an error is returned.
+
+## Attributes Reference
+
+The following attributes are exported:
+
+* `id` - The ID of the GPU card.
+* `name` - The name of the GPU card.
+* `device_id` - The device id of the GPU card.
+* `device_name` - The device name of the GPU card.
+* `vendor_id` - The vendor id of the GPU card.
+* `vendor_name` - The vendor name of the GPU card.
diff --git a/website/docs/d/vgpu_profile.html.markdown
b/website/docs/d/vgpu_profile.html.markdown
new file mode 100644
index 0000000..c266892
--- /dev/null
+++ b/website/docs/d/vgpu_profile.html.markdown
@@ -0,0 +1,58 @@
+---
+layout: "cloudstack"
+page_title: "CloudStack: cloudstack_vgpu_profile"
+description: |-
+ Gets information about a vGPU profile.
+---
+
+# cloudstack_vgpu_profile
+
+Use this data source to get information about a vGPU profile for use in other
resources.
+
+## Example Usage
+
+```hcl
+data "cloudstack_vgpu_profile" "profile" {
+ filter {
+ name = "name"
+ value = "passthrough"
+ }
+}
+
+output "vgpu_profile_id" {
+ value = data.cloudstack_vgpu_profile.profile.id
+}
+```
+
+## Argument Reference
+
+The following arguments are supported:
+
+* `filter` - (Required) One or more name/value pairs to filter off of. See
detailed documentation below.
+
+### Filter Arguments
+
+* `name` - (Required) The name of the field to filter on. Filtering is
performed server-side by the
+ CloudStack API. Supported values are `id`, `name`, `gpu_card_id`, `keyword`,
and `active_only`.
+* `value` - (Required) The value to filter on. This is passed directly to the
CloudStack API and
+ matched exactly (not as a regular expression). The filters must narrow the
result to a single
+ vGPU profile; if more than one profile matches, an error is returned.
+
+## Attributes Reference
+
+The following attributes are exported:
+
+* `id` - The ID of the vGPU profile.
+* `name` - The name of the vGPU profile.
+* `description` - The description of the vGPU profile.
+* `device_id` - The device id of the GPU card.
+* `device_name` - The device name of the GPU card.
+* `gpu_card_id` - The GPU card id of the vGPU profile.
+* `gpu_card_name` - The GPU card name of the vGPU profile.
+* `max_heads` - The maximum displays per vGPU instance.
+* `max_resolution_x` - The maximum X resolution per display.
+* `max_resolution_y` - The maximum Y resolution per display.
+* `max_vgpu_per_physical_gpu` - The maximum number of vGPU instances per
physical GPU.
+* `vendor_id` - The vendor id of the GPU card.
+* `vendor_name` - The vendor name of the GPU card.
+* `video_ram` - The video RAM size in MB for the vGPU profile.
diff --git a/website/docs/r/service_offering_constrained.html.markdown
b/website/docs/r/service_offering_constrained.html.markdown
index 20a33b5..8ff5bc7 100644
--- a/website/docs/r/service_offering_constrained.html.markdown
+++ b/website/docs/r/service_offering_constrained.html.markdown
@@ -37,6 +37,12 @@ resource "cloudstack_service_offering_constrained" "example"
{
provisioning_type = "thin"
storage_type = "local"
}
+
+ gpu {
+ vgpu_profile_id = "gpu-profile-uuid"
+ count = 1
+ display = false
+ }
}
```
@@ -89,6 +95,12 @@ The following arguments are supported:
- `max_iops` (Int, Optional) - Max IOPS of the compute offering.
- `min_iops` (Int, Optional) - Min IOPS of the compute offering.
+#### `gpu` (Block, Optional)
+
+- `vgpu_profile_id` (String, Required) - The ID of the vGPU profile to
associate with the service offering.
+- `count` (Int, Optional, Computed) - The number of GPUs to assign to the
guest VM.
+- `display` (Bool, Optional, Computed, Default: false) - Whether the GPU is
presented as a display device to the guest VM.
+
## Attributes Reference
In addition to the arguments above, the following attributes are exported:
diff --git a/website/docs/r/service_offering_fixed.html.markdown
b/website/docs/r/service_offering_fixed.html.markdown
index 448baa4..1c86805 100644
--- a/website/docs/r/service_offering_fixed.html.markdown
+++ b/website/docs/r/service_offering_fixed.html.markdown
@@ -32,6 +32,11 @@ resource "cloudstack_service_offering_fixed" "fixed1" {
# disk_offering { ... }
# disk_hypervisor { ... }
# disk_storage { ... }
+ # gpu {
+ # vgpu_profile_id = "..."
+ # count = 1
+ # display = false
+ # }
}
```
@@ -86,6 +91,12 @@ The following arguments are supported:
- `max_iops` (Optional) - Max IOPS of the compute offering.
- `min_iops` (Optional) - Min IOPS of the compute offering.
+#### `gpu` (Optional)
+
+- `vgpu_profile_id` (Required) - The ID of the vGPU profile to associate with
the service offering.
+- `count` (Optional, Computed) - The number of GPUs to assign to the guest VM.
+- `display` (Optional, Computed) - Whether the GPU is presented as a display
device to the guest VM. Defaults to `false`.
+
## Attributes Reference
In addition to the arguments above, the following attributes are exported:
diff --git a/website/docs/r/service_offering_unconstrained.html.markdown
b/website/docs/r/service_offering_unconstrained.html.markdown
index 4a71140..5d4bb02 100644
--- a/website/docs/r/service_offering_unconstrained.html.markdown
+++ b/website/docs/r/service_offering_unconstrained.html.markdown
@@ -29,6 +29,11 @@ resource "cloudstack_service_offering_unconstrained"
"unconstrained1" {
# disk_offering { ... }
# disk_hypervisor { ... }
# disk_storage { ... }
+ # gpu {
+ # vgpu_profile_id = "..."
+ # count = 1
+ # display = false
+ # }
}
```
@@ -80,6 +85,12 @@ The following arguments are supported:
- `max_iops` (Optional) - Max IOPS of the compute offering.
- `min_iops` (Optional) - Min IOPS of the compute offering.
+#### `gpu` (Optional)
+
+- `vgpu_profile_id` (Required) - The ID of the vGPU profile to associate with
the service offering.
+- `count` (Optional, Computed) - The number of GPUs to assign to the guest VM.
+- `display` (Optional, Computed) - Whether the GPU is presented as a display
device to the guest VM. Defaults to `false`.
+
## Attributes Reference
In addition to the arguments above, the following attributes are exported: