@@ -19,6 +19,7 @@ package cloud
19
19
import (
20
20
"encoding/base64"
21
21
"fmt"
22
+ "strconv"
22
23
"strings"
23
24
24
25
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -85,34 +86,34 @@ func (c *client) ResolveVMInstanceDetails(csMachine *infrav1.CloudStackMachine)
85
86
return errors .New ("no match found" )
86
87
}
87
88
88
- func (c * client ) ResolveServiceOffering (csMachine * infrav1.CloudStackMachine , zoneID string ) (offeringID string , retErr error ) {
89
+ func (c * client ) ResolveServiceOffering (csMachine * infrav1.CloudStackMachine , zoneID string ) (offering cloudstack. ServiceOffering , retErr error ) {
89
90
if len (csMachine .Spec .Offering .ID ) > 0 {
90
91
csOffering , count , err := c .cs .ServiceOffering .GetServiceOfferingByID (csMachine .Spec .Offering .ID )
91
92
if err != nil {
92
93
c .customMetrics .EvaluateErrorAndIncrementAcsReconciliationErrorCounter (err )
93
- return "" , multierror .Append (retErr , errors .Wrapf (
94
+ return * csOffering , multierror .Append (retErr , errors .Wrapf (
94
95
err , "could not get Service Offering by ID %s" , csMachine .Spec .Offering .ID ))
95
96
} else if count != 1 {
96
- return "" , multierror .Append (retErr , errors .Errorf (
97
+ return * csOffering , multierror .Append (retErr , errors .Errorf (
97
98
"expected 1 Service Offering with UUID %s, but got %d" , csMachine .Spec .Offering .ID , count ))
98
99
}
99
100
100
101
if len (csMachine .Spec .Offering .Name ) > 0 && csMachine .Spec .Offering .Name != csOffering .Name {
101
- return "" , multierror .Append (retErr , errors .Errorf (
102
+ return * csOffering , multierror .Append (retErr , errors .Errorf (
102
103
"offering name %s does not match name %s returned using UUID %s" , csMachine .Spec .Offering .Name , csOffering .Name , csMachine .Spec .Offering .ID ))
103
104
}
104
- return csMachine . Spec . Offering . ID , nil
105
+ return * csOffering , nil
105
106
}
106
- offeringID , count , err := c .cs .ServiceOffering .GetServiceOfferingID (csMachine .Spec .Offering .Name , cloudstack .WithZone (zoneID ))
107
+ csOffering , count , err := c .cs .ServiceOffering .GetServiceOfferingByName (csMachine .Spec .Offering .Name , cloudstack .WithZone (zoneID ))
107
108
if err != nil {
108
109
c .customMetrics .EvaluateErrorAndIncrementAcsReconciliationErrorCounter (err )
109
- return "" , multierror .Append (retErr , errors .Wrapf (
110
+ return * csOffering , multierror .Append (retErr , errors .Wrapf (
110
111
err , "could not get Service Offering ID from %s in zone %s" , csMachine .Spec .Offering .Name , zoneID ))
111
112
} else if count != 1 {
112
- return "" , multierror .Append (retErr , errors .Errorf (
113
+ return * csOffering , multierror .Append (retErr , errors .Errorf (
113
114
"expected 1 Service Offering with name %s in zone %s, but got %d" , csMachine .Spec .Offering .Name , zoneID , count ))
114
115
}
115
- return offeringID , nil
116
+ return * csOffering , nil
116
117
}
117
118
118
119
func (c * client ) ResolveTemplate (
@@ -206,9 +207,61 @@ func verifyDiskoffering(csMachine *infrav1.CloudStackMachine, c *client, diskOff
206
207
return diskOfferingID , nil
207
208
}
208
209
210
+ // CheckAccountLimits Checks the account's limit of VM, CPU & Memory
211
+ // before deploying a VM.
212
+ func (c * client ) CheckAccountLimits (fd * infrav1.CloudStackFailureDomain , offering cloudstack.ServiceOffering ) error {
213
+ if c .user .Account .CPUAvailable != "Unlimited" {
214
+ cpuAvailable , err := strconv .ParseInt (c .user .Account .CPUAvailable , 10 , 0 )
215
+ if err == nil && int64 (offering .Cpunumber ) > cpuAvailable {
216
+ return fmt .Errorf ("CPU available (%d) in account can't fulfil the requirement: %d" , cpuAvailable , offering .Cpunumber )
217
+ }
218
+ }
219
+
220
+ if c .user .Account .MemoryAvailable != "Unlimited" {
221
+ memoryAvailable , err := strconv .ParseInt (c .user .Account .MemoryAvailable , 10 , 0 )
222
+ if err == nil && int64 (offering .Memory ) > memoryAvailable {
223
+ return fmt .Errorf ("memory available (%d) in account can't fulfil the requirement: %d" , memoryAvailable , offering .Memory )
224
+ }
225
+ }
226
+
227
+ if c .user .Account .VMAvailable != "Unlimited" {
228
+ vmAvailable , err := strconv .ParseInt (c .user .Account .VMAvailable , 10 , 0 )
229
+ if err == nil && vmAvailable <= 0 {
230
+ return fmt .Errorf ("VM Limit in account has reached it's maximum value" )
231
+ }
232
+ }
233
+ return nil
234
+ }
235
+
236
+ // CheckDomainLimits Checks the domain's limit of VM, CPU & Memory
237
+ // before deploying a VM.
238
+ func (c * client ) CheckDomainLimits (fd * infrav1.CloudStackFailureDomain , offering cloudstack.ServiceOffering ) error {
239
+ if c .user .Account .Domain .CPUAvailable != "Unlimited" {
240
+ cpuAvailable , err := strconv .ParseInt (c .user .Account .Domain .CPUAvailable , 10 , 0 )
241
+ if err == nil && int64 (offering .Cpunumber ) > cpuAvailable {
242
+ return fmt .Errorf ("CPU available (%d) in domain can't fulfil the requirement: %d" , cpuAvailable , offering .Cpunumber )
243
+ }
244
+ }
245
+
246
+ if c .user .Account .Domain .MemoryAvailable != "Unlimited" {
247
+ memoryAvailable , err := strconv .ParseInt (c .user .Account .Domain .MemoryAvailable , 10 , 0 )
248
+ if err == nil && int64 (offering .Memory ) > memoryAvailable {
249
+ return fmt .Errorf ("memory available (%d) in domain can't fulfil the requirement: %d" , memoryAvailable , offering .Memory )
250
+ }
251
+ }
252
+
253
+ if c .user .Account .Domain .VMAvailable != "Unlimited" {
254
+ vmAvailable , err := strconv .ParseInt (c .user .Account .Domain .VMAvailable , 10 , 0 )
255
+ if err == nil && vmAvailable > 0 {
256
+ return fmt .Errorf ("VM Limit in domain has reached it's maximum value" )
257
+ }
258
+ }
259
+ return nil
260
+ }
261
+
209
262
// GetOrCreateVMInstance CreateVMInstance will fetch or create a VM instance, and
210
263
// sets the infrastructure machine spec and status accordingly.
211
- func (c * client ) GetOrCreateVMInstance (
264
+ func (c * client ) CheckLimitsAndCreateVM (
212
265
csMachine * infrav1.CloudStackMachine ,
213
266
capiMachine * clusterv1.Machine ,
214
267
csCluster * infrav1.CloudStackCluster ,
@@ -217,27 +270,31 @@ func (c *client) GetOrCreateVMInstance(
217
270
userData string ,
218
271
) error {
219
272
220
- // Check if VM instance already exists.
221
- if err := c .ResolveVMInstanceDetails (csMachine ); err == nil ||
222
- ! strings .Contains (strings .ToLower (err .Error ()), "no match" ) {
273
+ offering , err := c .ResolveServiceOffering (csMachine , fd .Spec .Zone .ID )
274
+ if err != nil {
223
275
return err
224
276
}
225
277
226
- offeringID , err := c .ResolveServiceOffering ( csMachine , fd .Spec .Zone .ID )
278
+ templateID , err := c .ResolveTemplate ( csCluster , csMachine , fd .Spec .Zone .ID )
227
279
if err != nil {
228
280
return err
229
281
}
230
- templateID , err := c .ResolveTemplate ( csCluster , csMachine , fd .Spec .Zone .ID )
282
+ diskOfferingID , err := c .ResolveDiskOffering ( csMachine , fd .Spec .Zone .ID )
231
283
if err != nil {
232
284
return err
233
285
}
234
- diskOfferingID , err := c .ResolveDiskOffering (csMachine , fd .Spec .Zone .ID )
286
+
287
+ err = c .CheckAccountLimits (fd , offering )
235
288
if err != nil {
236
289
return err
237
290
}
238
291
239
- // Create VM instance.
240
- p := c .cs .VirtualMachine .NewDeployVirtualMachineParams (offeringID , templateID , fd .Spec .Zone .ID )
292
+ err = c .CheckDomainLimits (fd , offering )
293
+ if err != nil {
294
+ return err
295
+ }
296
+
297
+ p := c .cs .VirtualMachine .NewDeployVirtualMachineParams (offering .Id , templateID , fd .Spec .Zone .ID )
241
298
p .SetNetworkids ([]string {fd .Spec .Zone .Network .ID })
242
299
setIfNotEmpty (csMachine .Name , p .SetName )
243
300
setIfNotEmpty (capiMachine .Name , p .SetDisplayname )
@@ -289,6 +346,31 @@ func (c *client) GetOrCreateVMInstance(
289
346
csMachine .Spec .InstanceID = pointer .String (deployVMResp .Id )
290
347
csMachine .Status .Status = pointer .String (metav1 .StatusSuccess )
291
348
}
349
+ return nil
350
+ }
351
+
352
+ // GetOrCreateVMInstance CreateVMInstance will fetch or create a VM instance, and
353
+ // sets the infrastructure machine spec and status accordingly.
354
+ func (c * client ) GetOrCreateVMInstance (
355
+ csMachine * infrav1.CloudStackMachine ,
356
+ capiMachine * clusterv1.Machine ,
357
+ csCluster * infrav1.CloudStackCluster ,
358
+ fd * infrav1.CloudStackFailureDomain ,
359
+ affinity * infrav1.CloudStackAffinityGroup ,
360
+ userData string ,
361
+ ) error {
362
+
363
+ // Check if VM instance already exists.
364
+ if err := c .ResolveVMInstanceDetails (csMachine ); err == nil ||
365
+ ! strings .Contains (strings .ToLower (err .Error ()), "no match" ) {
366
+ return err
367
+ }
368
+
369
+ // Create VM instance.
370
+ if err := c .CheckLimitsAndCreateVM (csMachine , capiMachine , csCluster , fd , affinity , userData ); err != nil {
371
+ return err
372
+ }
373
+
292
374
// Resolve uses a VM metrics request response to fill cloudstack machine status.
293
375
// The deployment response is insufficient.
294
376
return c .ResolveVMInstanceDetails (csMachine )
0 commit comments