@@ -19,6 +19,7 @@ package cloud
1919import (
2020 "encoding/base64"
2121 "fmt"
22+ "strconv"
2223 "strings"
2324
2425 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -85,34 +86,34 @@ func (c *client) ResolveVMInstanceDetails(csMachine *infrav1.CloudStackMachine)
8586 return errors .New ("no match found" )
8687}
8788
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 ) {
8990 if len (csMachine .Spec .Offering .ID ) > 0 {
9091 csOffering , count , err := c .cs .ServiceOffering .GetServiceOfferingByID (csMachine .Spec .Offering .ID )
9192 if err != nil {
9293 c .customMetrics .EvaluateErrorAndIncrementAcsReconciliationErrorCounter (err )
93- return "" , multierror .Append (retErr , errors .Wrapf (
94+ return * csOffering , multierror .Append (retErr , errors .Wrapf (
9495 err , "could not get Service Offering by ID %s" , csMachine .Spec .Offering .ID ))
9596 } else if count != 1 {
96- return "" , multierror .Append (retErr , errors .Errorf (
97+ return * csOffering , multierror .Append (retErr , errors .Errorf (
9798 "expected 1 Service Offering with UUID %s, but got %d" , csMachine .Spec .Offering .ID , count ))
9899 }
99100
100101 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 (
102103 "offering name %s does not match name %s returned using UUID %s" , csMachine .Spec .Offering .Name , csOffering .Name , csMachine .Spec .Offering .ID ))
103104 }
104- return csMachine . Spec . Offering . ID , nil
105+ return * csOffering , nil
105106 }
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 ))
107108 if err != nil {
108109 c .customMetrics .EvaluateErrorAndIncrementAcsReconciliationErrorCounter (err )
109- return "" , multierror .Append (retErr , errors .Wrapf (
110+ return * csOffering , multierror .Append (retErr , errors .Wrapf (
110111 err , "could not get Service Offering ID from %s in zone %s" , csMachine .Spec .Offering .Name , zoneID ))
111112 } else if count != 1 {
112- return "" , multierror .Append (retErr , errors .Errorf (
113+ return * csOffering , multierror .Append (retErr , errors .Errorf (
113114 "expected 1 Service Offering with name %s in zone %s, but got %d" , csMachine .Spec .Offering .Name , zoneID , count ))
114115 }
115- return offeringID , nil
116+ return * csOffering , nil
116117}
117118
118119func (c * client ) ResolveTemplate (
@@ -206,9 +207,61 @@ func verifyDiskoffering(csMachine *infrav1.CloudStackMachine, c *client, diskOff
206207 return diskOfferingID , nil
207208}
208209
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+
209262// GetOrCreateVMInstance CreateVMInstance will fetch or create a VM instance, and
210263// sets the infrastructure machine spec and status accordingly.
211- func (c * client ) GetOrCreateVMInstance (
264+ func (c * client ) CheckLimitsAndCreateVM (
212265 csMachine * infrav1.CloudStackMachine ,
213266 capiMachine * clusterv1.Machine ,
214267 csCluster * infrav1.CloudStackCluster ,
@@ -217,27 +270,31 @@ func (c *client) GetOrCreateVMInstance(
217270 userData string ,
218271) error {
219272
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 {
223275 return err
224276 }
225277
226- offeringID , err := c .ResolveServiceOffering ( csMachine , fd .Spec .Zone .ID )
278+ templateID , err := c .ResolveTemplate ( csCluster , csMachine , fd .Spec .Zone .ID )
227279 if err != nil {
228280 return err
229281 }
230- templateID , err := c .ResolveTemplate ( csCluster , csMachine , fd .Spec .Zone .ID )
282+ diskOfferingID , err := c .ResolveDiskOffering ( csMachine , fd .Spec .Zone .ID )
231283 if err != nil {
232284 return err
233285 }
234- diskOfferingID , err := c .ResolveDiskOffering (csMachine , fd .Spec .Zone .ID )
286+
287+ err = c .CheckAccountLimits (fd , offering )
235288 if err != nil {
236289 return err
237290 }
238291
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 )
241298 p .SetNetworkids ([]string {fd .Spec .Zone .Network .ID })
242299 setIfNotEmpty (csMachine .Name , p .SetName )
243300 setIfNotEmpty (capiMachine .Name , p .SetDisplayname )
@@ -289,6 +346,31 @@ func (c *client) GetOrCreateVMInstance(
289346 csMachine .Spec .InstanceID = pointer .String (deployVMResp .Id )
290347 csMachine .Status .Status = pointer .String (metav1 .StatusSuccess )
291348 }
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+
292374 // Resolve uses a VM metrics request response to fill cloudstack machine status.
293375 // The deployment response is insufficient.
294376 return c .ResolveVMInstanceDetails (csMachine )
0 commit comments