Skip to content

Commit 63f866b

Browse files
poddmCodeBleu
andauthored
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 <400979+CodeBleu@users.noreply.github.com>
1 parent 3021ce2 commit 63f866b

15 files changed

+2469
-25
lines changed

cloudstack/provider_v6.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,11 @@ func (p *CloudstackProvider) ConfigValidators(ctx context.Context) []provider.Co
164164
}
165165

166166
func (p *CloudstackProvider) Resources(ctx context.Context) []func() resource.Resource {
167-
return []func() resource.Resource{}
167+
return []func() resource.Resource{
168+
NewserviceOfferingUnconstrainedResource,
169+
NewserviceOfferingConstrainedResource,
170+
NewserviceOfferingFixedResource,
171+
}
168172
}
169173

170174
func (p *CloudstackProvider) DataSources(ctx context.Context) []func() datasource.DataSource {
Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
//
2+
// Licensed to the Apache Software Foundation (ASF) under one
3+
// or more contributor license agreements. See the NOTICE file
4+
// distributed with this work for additional information
5+
// regarding copyright ownership. The ASF licenses this file
6+
// to you under the Apache License, Version 2.0 (the
7+
// "License"); you may not use this file except in compliance
8+
// with the License. You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing,
13+
// software distributed under the License is distributed on an
14+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
// KIND, either express or implied. See the License for the
16+
// specific language governing permissions and limitations
17+
// under the License.
18+
//
19+
20+
package cloudstack
21+
22+
import (
23+
"context"
24+
"fmt"
25+
"strconv"
26+
27+
"github.com/apache/cloudstack-go/v2/cloudstack"
28+
"github.com/hashicorp/terraform-plugin-framework/resource"
29+
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
30+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int32planmodifier"
31+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
32+
"github.com/hashicorp/terraform-plugin-framework/types"
33+
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
34+
)
35+
36+
var (
37+
_ resource.Resource = &serviceOfferingConstrainedResource{}
38+
_ resource.ResourceWithConfigure = &serviceOfferingConstrainedResource{}
39+
)
40+
41+
func NewserviceOfferingConstrainedResource() resource.Resource {
42+
return &serviceOfferingConstrainedResource{}
43+
}
44+
45+
type serviceOfferingConstrainedResource struct {
46+
client *cloudstack.CloudStackClient
47+
}
48+
49+
func (r *serviceOfferingConstrainedResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
50+
resp.Schema = schema.Schema{
51+
Attributes: serviceOfferingMergeCommonSchema(map[string]schema.Attribute{
52+
"cpu_speed": schema.Int32Attribute{
53+
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 https://libvirt.org/formatdomain.html#cpu-tuning.",
54+
Required: true,
55+
PlanModifiers: []planmodifier.Int32{
56+
int32planmodifier.RequiresReplace(),
57+
},
58+
},
59+
"max_cpu_number": schema.Int32Attribute{
60+
Description: "The maximum number of CPUs to be set with Custom Compute Offering",
61+
Required: true,
62+
PlanModifiers: []planmodifier.Int32{
63+
int32planmodifier.RequiresReplace(),
64+
},
65+
},
66+
"max_memory": schema.Int32Attribute{
67+
Description: "The maximum memory size of the custom service offering in MB",
68+
Required: true,
69+
PlanModifiers: []planmodifier.Int32{
70+
int32planmodifier.RequiresReplace(),
71+
},
72+
},
73+
"min_cpu_number": schema.Int32Attribute{
74+
Description: "The minimum number of CPUs to be set with Custom Compute Offering",
75+
Required: true,
76+
PlanModifiers: []planmodifier.Int32{
77+
int32planmodifier.RequiresReplace(),
78+
},
79+
},
80+
"min_memory": schema.Int32Attribute{
81+
Description: "The minimum memory size of the custom service offering in MB",
82+
Required: true,
83+
PlanModifiers: []planmodifier.Int32{
84+
int32planmodifier.RequiresReplace(),
85+
},
86+
},
87+
}),
88+
}
89+
}
90+
91+
func (r *serviceOfferingConstrainedResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
92+
var plan serviceOfferingConstrainedResourceModel
93+
var planDiskQosHypervisor ServiceOfferingDiskQosHypervisor
94+
var planDiskOffering ServiceOfferingDiskOffering
95+
var planDiskQosStorage ServiceOfferingDiskQosStorage
96+
97+
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
98+
if !plan.ServiceOfferingDiskQosHypervisor.IsNull() {
99+
resp.Diagnostics.Append(plan.ServiceOfferingDiskQosHypervisor.As(ctx, &planDiskQosHypervisor, basetypes.ObjectAsOptions{})...)
100+
}
101+
if !plan.ServiceOfferingDiskOffering.IsNull() {
102+
resp.Diagnostics.Append(plan.ServiceOfferingDiskOffering.As(ctx, &planDiskOffering, basetypes.ObjectAsOptions{})...)
103+
}
104+
if !plan.ServiceOfferingDiskQosStorage.IsNull() {
105+
resp.Diagnostics.Append(plan.ServiceOfferingDiskQosStorage.As(ctx, &planDiskQosStorage, basetypes.ObjectAsOptions{})...)
106+
}
107+
if resp.Diagnostics.HasError() {
108+
return
109+
}
110+
111+
// common params
112+
params := r.client.ServiceOffering.NewCreateServiceOfferingParams(plan.DisplayText.ValueString(), plan.Name.ValueString())
113+
plan.commonCreateParams(ctx, params)
114+
planDiskQosHypervisor.commonCreateParams(ctx, params)
115+
planDiskOffering.commonCreateParams(ctx, params)
116+
planDiskQosStorage.commonCreateParams(ctx, params)
117+
118+
// resource specific params
119+
if !plan.CpuSpeed.IsNull() {
120+
params.SetCpuspeed(int(plan.CpuSpeed.ValueInt32()))
121+
}
122+
if !plan.MaxCpuNumber.IsNull() {
123+
params.SetMaxcpunumber(int(plan.MaxCpuNumber.ValueInt32()))
124+
}
125+
if !plan.MaxMemory.IsNull() {
126+
params.SetMaxmemory(int(plan.MaxMemory.ValueInt32()))
127+
}
128+
if !plan.MinCpuNumber.IsNull() {
129+
params.SetMincpunumber(int(plan.MinCpuNumber.ValueInt32()))
130+
}
131+
if !plan.MinMemory.IsNull() {
132+
params.SetMinmemory(int(plan.MinMemory.ValueInt32()))
133+
}
134+
135+
// create offering
136+
cs, err := r.client.ServiceOffering.CreateServiceOffering(params)
137+
if err != nil {
138+
resp.Diagnostics.AddError(
139+
"Error creating service offering",
140+
"Could not create constrained offering, unexpected error: "+err.Error(),
141+
)
142+
return
143+
}
144+
145+
//
146+
plan.Id = types.StringValue(cs.Id)
147+
148+
//
149+
resp.Diagnostics.Append(resp.State.Set(ctx, plan)...)
150+
}
151+
152+
func (r *serviceOfferingConstrainedResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
153+
var state serviceOfferingConstrainedResourceModel
154+
var stateDiskQosHypervisor ServiceOfferingDiskQosHypervisor
155+
var stateDiskOffering ServiceOfferingDiskOffering
156+
var stateDiskQosStorage ServiceOfferingDiskQosStorage
157+
158+
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
159+
if !state.ServiceOfferingDiskQosHypervisor.IsNull() {
160+
resp.Diagnostics.Append(state.ServiceOfferingDiskQosHypervisor.As(ctx, &stateDiskQosHypervisor, basetypes.ObjectAsOptions{})...)
161+
}
162+
if !state.ServiceOfferingDiskOffering.IsNull() {
163+
resp.Diagnostics.Append(state.ServiceOfferingDiskOffering.As(ctx, &stateDiskOffering, basetypes.ObjectAsOptions{})...)
164+
}
165+
if !state.ServiceOfferingDiskQosStorage.IsNull() {
166+
resp.Diagnostics.Append(state.ServiceOfferingDiskQosStorage.As(ctx, &stateDiskQosStorage, basetypes.ObjectAsOptions{})...)
167+
}
168+
if resp.Diagnostics.HasError() {
169+
return
170+
}
171+
172+
cs, _, err := r.client.ServiceOffering.GetServiceOfferingByID(state.Id.ValueString())
173+
if err != nil {
174+
resp.Diagnostics.AddError(
175+
"Error reading service offering",
176+
"Could not read constrained service offering, unexpected error: "+err.Error(),
177+
)
178+
return
179+
}
180+
181+
// resource specific
182+
if cs.Cpuspeed > 0 {
183+
state.CpuSpeed = types.Int32Value(int32(cs.Cpuspeed))
184+
}
185+
if v, found := cs.Serviceofferingdetails["maxcpunumber"]; found {
186+
i, err := strconv.Atoi(v)
187+
if err != nil {
188+
resp.Diagnostics.AddError(
189+
"Error reading service offering",
190+
"Could not read constrained service offering max cpu, unexpected error: "+err.Error(),
191+
)
192+
return
193+
}
194+
state.MaxCpuNumber = types.Int32Value(int32(i))
195+
}
196+
if v, found := cs.Serviceofferingdetails["mincpunumber"]; found {
197+
i, err := strconv.Atoi(v)
198+
if err != nil {
199+
resp.Diagnostics.AddError(
200+
"Error reading service offering",
201+
"Could not read constrained service offering min cpu number, unexpected error: "+err.Error(),
202+
)
203+
return
204+
}
205+
state.MinCpuNumber = types.Int32Value(int32(i))
206+
}
207+
if v, found := cs.Serviceofferingdetails["maxmemory"]; found {
208+
i, err := strconv.Atoi(v)
209+
if err != nil {
210+
resp.Diagnostics.AddError(
211+
"Error reading service offering",
212+
"Could not read constrained service offering max memory, unexpected error: "+err.Error(),
213+
)
214+
return
215+
}
216+
state.MaxMemory = types.Int32Value(int32(i))
217+
}
218+
if v, found := cs.Serviceofferingdetails["minmemory"]; found {
219+
i, err := strconv.Atoi(v)
220+
if err != nil {
221+
resp.Diagnostics.AddError(
222+
"Error reading service offering",
223+
"Could not read constrained service offering min memory, unexpected error: "+err.Error(),
224+
)
225+
return
226+
}
227+
state.MinMemory = types.Int32Value(int32(i))
228+
}
229+
230+
state.commonRead(ctx, cs)
231+
stateDiskQosHypervisor.commonRead(ctx, cs)
232+
stateDiskOffering.commonRead(ctx, cs)
233+
stateDiskQosStorage.commonRead(ctx, cs)
234+
if resp.Diagnostics.HasError() {
235+
return
236+
}
237+
238+
resp.Diagnostics.Append(resp.State.Set(ctx, state)...)
239+
240+
}
241+
242+
// Update updates the resource and sets the updated Terraform state on success.
243+
func (r *serviceOfferingConstrainedResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
244+
var state serviceOfferingConstrainedResourceModel
245+
246+
resp.Diagnostics.Append(req.Plan.Get(ctx, &state)...)
247+
if resp.Diagnostics.HasError() {
248+
return
249+
}
250+
251+
params := r.client.ServiceOffering.NewUpdateServiceOfferingParams(state.Id.ValueString())
252+
state.commonUpdateParams(ctx, params)
253+
254+
cs, err := r.client.ServiceOffering.UpdateServiceOffering(params)
255+
if err != nil {
256+
resp.Diagnostics.AddError(
257+
"Error updating service offering",
258+
"Could not update constrained service offering, unexpected error: "+err.Error(),
259+
)
260+
return
261+
}
262+
263+
state.commonUpdate(ctx, cs)
264+
if resp.Diagnostics.HasError() {
265+
return
266+
}
267+
resp.Diagnostics.Append(resp.State.Set(ctx, state)...)
268+
}
269+
270+
func (r *serviceOfferingConstrainedResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
271+
var state serviceOfferingConstrainedResourceModel
272+
273+
diags := req.State.Get(ctx, &state)
274+
resp.Diagnostics.Append(diags...)
275+
if resp.Diagnostics.HasError() {
276+
return
277+
}
278+
279+
// Delete the service offering
280+
_, err := r.client.ServiceOffering.DeleteServiceOffering(r.client.ServiceOffering.NewDeleteServiceOfferingParams(state.Id.ValueString()))
281+
if err != nil {
282+
resp.Diagnostics.AddError(
283+
"Error deleting service offering",
284+
"Could not delete constrained offering, unexpected error: "+err.Error(),
285+
)
286+
return
287+
}
288+
}
289+
290+
func (r *serviceOfferingConstrainedResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
291+
// Add a nil check when handling ProviderData because Terraform
292+
// sets that data after it calls the ConfigureProvider RPC.
293+
if req.ProviderData == nil {
294+
return
295+
}
296+
297+
client, ok := req.ProviderData.(*cloudstack.CloudStackClient)
298+
299+
if !ok {
300+
resp.Diagnostics.AddError(
301+
"Unexpected Data Source Configure Type",
302+
fmt.Sprintf("Expected *cloudstack.CloudStackClient, got: %T. Please report this issue to the provider developers.", req.ProviderData),
303+
)
304+
return
305+
}
306+
307+
r.client = client
308+
}
309+
310+
// Metadata returns the resource type name.
311+
func (r *serviceOfferingConstrainedResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
312+
resp.TypeName = req.ProviderTypeName + "_service_offering_constrained"
313+
}

0 commit comments

Comments
 (0)