@@ -278,7 +278,9 @@ func resourceCloudStackAutoScaleVMProfileRead(d *schema.ResourceData, meta inter
278278 }
279279
280280 if p .Userdatadetails != "" {
281- d .Set ("user_data_details" , map [string ]interface {}{})
281+ if _ , ok := d .GetOk ("user_data_details" ); ! ok {
282+ d .Set ("user_data_details" , map [string ]interface {}{})
283+ }
282284 }
283285
284286 if p .Autoscaleuserid != "" {
@@ -308,85 +310,185 @@ func resourceCloudStackAutoScaleVMProfileRead(d *schema.ResourceData, meta inter
308310 return nil
309311}
310312
311- func resourceCloudStackAutoScaleVMProfileUpdate (d * schema.ResourceData , meta interface {}) error {
312- cs := meta .(* cloudstack.CloudStackClient )
313+ // waitForVMGroupsState waits for the specified VM groups to reach the desired state
314+ func waitForVMGroupsState (cs * cloudstack.CloudStackClient , groupIDs []string , desiredState string ) error {
315+ maxRetries := 30 // 30 * 2 seconds = 60 seconds max wait
316+ for i := 0 ; i < maxRetries ; i ++ {
317+ allInDesiredState := true
318+
319+ for _ , groupID := range groupIDs {
320+ group , _ , err := cs .AutoScale .GetAutoScaleVmGroupByID (groupID )
321+ if err != nil {
322+ return fmt .Errorf ("Error checking state of VM group %s: %s" , groupID , err )
323+ }
324+
325+ groupInDesiredState := false
326+ if desiredState == "disabled" {
327+ groupInDesiredState = (group .State == "disabled" )
328+ } else if desiredState == "enabled" {
329+ groupInDesiredState = (group .State == "enabled" )
330+ } else {
331+ groupInDesiredState = (group .State == desiredState )
332+ }
313333
314- p := cs .AutoScale .NewUpdateAutoScaleVmProfileParams (d .Id ())
334+ if ! groupInDesiredState {
335+ allInDesiredState = false
336+ log .Printf ("[DEBUG] VM group %s is in state '%s', waiting for '%s'" , groupID , group .State , desiredState )
337+ break
338+ }
339+ }
315340
316- if d .HasChange ("template" ) {
317- zoneid , e := retrieveID (cs , "zone" , d .Get ("zone" ).(string ))
318- if e != nil {
319- return e .Error ()
341+ if allInDesiredState {
342+ log .Printf ("[INFO] All VM groups have reached desired state: %s" , desiredState )
343+ return nil
320344 }
321- templateid , e := retrieveTemplateID (cs , zoneid , d .Get ("template" ).(string ))
322- if e != nil {
323- return e .Error ()
345+
346+ if i < maxRetries - 1 {
347+ log .Printf ("[INFO] Waiting for VM groups to reach state '%s' (attempt %d/%d)" , desiredState , i + 1 , maxRetries )
348+ time .Sleep (2 * time .Second )
324349 }
325- p .SetTemplateid (templateid )
326350 }
327351
328- if d .HasChange ("destroy_vm_grace_period" ) {
329- if v , ok := d .GetOk ("destroy_vm_grace_period" ); ok {
330- duration , err := time .ParseDuration (v .(string ))
331- if err != nil {
332- return err
333- }
334- p .SetExpungevmgraceperiod (int (duration .Seconds ()))
352+ return fmt .Errorf ("Timeout waiting for VM groups to reach state '%s' after %d seconds" , desiredState , maxRetries * 2 )
353+ }
354+
355+ func waitForVMGroupsToBeDisabled (cs * cloudstack.CloudStackClient , profileID string ) error {
356+ log .Printf ("[DEBUG] Waiting for VM groups using profile %s to be disabled" , profileID )
357+ listParams := cs .AutoScale .NewListAutoScaleVmGroupsParams ()
358+ listParams .SetVmprofileid (profileID )
359+
360+ groups , err := cs .AutoScale .ListAutoScaleVmGroups (listParams )
361+ if err != nil {
362+ log .Printf ("[ERROR] Failed to list VM groups for profile %s: %s" , profileID , err )
363+ return fmt .Errorf ("Error listing autoscale VM groups: %s" , err )
364+ }
365+
366+ log .Printf ("[DEBUG] Found %d VM groups using profile %s" , len (groups .AutoScaleVmGroups ), profileID )
367+
368+ var groupIDs []string
369+ for _ , group := range groups .AutoScaleVmGroups {
370+ log .Printf ("[DEBUG] VM group %s (%s) current state: %s" , group .Name , group .Id , group .State )
371+ groupIDs = append (groupIDs , group .Id )
372+ }
373+
374+ if len (groupIDs ) == 0 {
375+ log .Printf ("[DEBUG] No VM groups found using profile %s" , profileID )
376+ return nil
377+ }
378+
379+ log .Printf ("[INFO] Waiting for %d VM groups to be disabled for profile update" , len (groupIDs ))
380+ if err := waitForVMGroupsState (cs , groupIDs , "disabled" ); err != nil {
381+ return fmt .Errorf ("Autoscale VM groups must be disabled before updating profile: %s" , err )
382+ }
383+
384+ log .Printf ("[DEBUG] All VM groups are now disabled for profile %s" , profileID )
385+ return nil
386+ }
387+
388+ func resourceCloudStackAutoScaleVMProfileUpdate (d * schema.ResourceData , meta interface {}) error {
389+ cs := meta .(* cloudstack.CloudStackClient )
390+ log .Printf ("[DEBUG] Profile update requested for ID: %s" , d .Id ())
391+ for _ , key := range []string {"template" , "destroy_vm_grace_period" , "counter_param_list" , "user_data" , "user_data_id" , "user_data_details" , "autoscale_user_id" , "display" , "metadata" } {
392+ if d .HasChange (key ) {
393+ old , new := d .GetChange (key )
394+ log .Printf ("[DEBUG] Field '%s' changed from %v to %v" , key , old , new )
335395 }
336396 }
337397
338- if d .HasChange ("counter_param_list" ) {
339- if v , ok := d .GetOk ("counter_param_list" ); ok {
340- nv := make (map [string ]string )
341- for k , v := range v .(map [string ]interface {}) {
342- nv [k ] = v .(string )
398+ // Check if we only have metadata changes (which don't require CloudStack API update)
399+ onlyMetadataChanges := d .HasChange ("metadata" ) &&
400+ ! d .HasChange ("template" ) &&
401+ ! d .HasChange ("destroy_vm_grace_period" ) &&
402+ ! d .HasChange ("counter_param_list" ) &&
403+ ! d .HasChange ("user_data" ) &&
404+ ! d .HasChange ("user_data_id" ) &&
405+ ! d .HasChange ("user_data_details" ) &&
406+ ! d .HasChange ("autoscale_user_id" ) &&
407+ ! d .HasChange ("display" )
408+
409+ if ! onlyMetadataChanges {
410+ if err := waitForVMGroupsToBeDisabled (cs , d .Id ()); err != nil {
411+ return fmt .Errorf ("Autoscale VM groups must be disabled before updating profile: %s" , err )
412+ }
413+
414+ p := cs .AutoScale .NewUpdateAutoScaleVmProfileParams (d .Id ())
415+
416+ if d .HasChange ("template" ) {
417+ zoneid , e := retrieveID (cs , "zone" , d .Get ("zone" ).(string ))
418+ if e != nil {
419+ return e .Error ()
420+ }
421+ templateid , e := retrieveTemplateID (cs , zoneid , d .Get ("template" ).(string ))
422+ if e != nil {
423+ return e .Error ()
343424 }
344- p .SetCounterparam ( nv )
425+ p .SetTemplateid ( templateid )
345426 }
346- }
347427
348- if d .HasChange ("user_data" ) {
349- if v , ok := d .GetOk ("user_data" ); ok {
350- p .SetUserdata (v .(string ))
428+ if d .HasChange ("destroy_vm_grace_period" ) {
429+ if v , ok := d .GetOk ("destroy_vm_grace_period" ); ok {
430+ duration , err := time .ParseDuration (v .(string ))
431+ if err != nil {
432+ return err
433+ }
434+ p .SetExpungevmgraceperiod (int (duration .Seconds ()))
435+ }
351436 }
352- }
353437
354- if d .HasChange ("user_data_id" ) {
355- if v , ok := d .GetOk ("user_data_id" ); ok {
356- p .SetUserdataid (v .(string ))
438+ if d .HasChange ("counter_param_list" ) {
439+ if v , ok := d .GetOk ("counter_param_list" ); ok {
440+ nv := make (map [string ]string )
441+ for k , v := range v .(map [string ]interface {}) {
442+ nv [k ] = v .(string )
443+ }
444+ p .SetCounterparam (nv )
445+ }
357446 }
358- }
359447
360- if d .HasChange ("user_data_details" ) {
361- if v , ok := d .GetOk ("user_data_details" ); ok {
362- nv := make (map [string ]string )
363- for k , v := range v .(map [string ]interface {}) {
364- nv [k ] = v .(string )
448+ if d .HasChange ("user_data" ) {
449+ if v , ok := d .GetOk ("user_data" ); ok {
450+ p .SetUserdata (v .(string ))
365451 }
366- p .SetUserdatadetails (nv )
367452 }
368- }
369453
370- if d .HasChange ("autoscale_user_id" ) {
371- if v , ok := d .GetOk ("autoscale_user_id" ); ok {
372- p .SetAutoscaleuserid (v .(string ))
454+ if d .HasChange ("user_data_id" ) {
455+ if v , ok := d .GetOk ("user_data_id" ); ok {
456+ p .SetUserdataid (v .(string ))
457+ }
373458 }
374- }
375459
376- if d .HasChange ("display" ) {
377- if v , ok := d .GetOk ("display" ); ok {
378- p .SetFordisplay (v .(bool ))
460+ if d .HasChange ("user_data_details" ) {
461+ if v , ok := d .GetOk ("user_data_details" ); ok {
462+ nv := make (map [string ]string )
463+ for k , v := range v .(map [string ]interface {}) {
464+ nv [k ] = v .(string )
465+ }
466+ p .SetUserdatadetails (nv )
467+ }
379468 }
380- }
381469
382- _ , err := cs .AutoScale .UpdateAutoScaleVmProfile (p )
383- if err != nil {
384- return fmt .Errorf ("Error updating AutoScaleVmProfile %s: %s" , d .Id (), err )
470+ if d .HasChange ("autoscale_user_id" ) {
471+ if v , ok := d .GetOk ("autoscale_user_id" ); ok {
472+ p .SetAutoscaleuserid (v .(string ))
473+ }
474+ }
475+
476+ if d .HasChange ("display" ) {
477+ if v , ok := d .GetOk ("display" ); ok {
478+ p .SetFordisplay (v .(bool ))
479+ }
480+ }
481+
482+ log .Printf ("[DEBUG] Performing CloudStack API update for profile %s" , d .Id ())
483+ _ , updateErr := cs .AutoScale .UpdateAutoScaleVmProfile (p )
484+ if updateErr != nil {
485+ return fmt .Errorf ("Error updating AutoScaleVmProfile %s: %s" , d .Id (), updateErr )
486+ }
385487 }
386488
387489 if d .HasChange ("metadata" ) {
388- if err := updateMetadata (cs , d , "AutoScaleVmProfile" ); err != nil {
389- return fmt .Errorf ("Error updating tags on AutoScaleVmProfile %s: %s" , d .Id (), err )
490+ if metadataErr := updateMetadata (cs , d , "AutoScaleVmProfile" ); metadataErr != nil {
491+ return fmt .Errorf ("Error updating tags on AutoScaleVmProfile %s: %s" , d .Id (), metadataErr )
390492 }
391493 }
392494
0 commit comments