@@ -295,6 +295,8 @@ def __init__(
295
295
# self._did_start_work = False
296
296
# Now, VM was rebooted when it was running at start, then stopped and powered on:
297
297
# .power_state in [] and any(_did_nice_shutdown_work, _did_force_shutdown_work) and _was_start_tried
298
+ self ._was_reboot_tried = False
299
+ self ._was_reset_tried = False
298
300
299
301
@property
300
302
def nic_list (self ):
@@ -853,40 +855,87 @@ def get_vm_and_boot_devices(cls, ansible_dict, rest_client):
853
855
]
854
856
return vm , boot_devices_uuid , boot_devices_ansible
855
857
856
- def update_vm_power_state (self , module , rest_client , desired_power_action ):
858
+ def update_vm_power_state (
859
+ self , module , rest_client , desired_power_action , ignore_repeated_request : bool
860
+ ):
857
861
"""Sets the power state to what is stored in self.power_state"""
862
+
858
863
# desired_power_action must be present in FROM_ANSIBLE_TO_HYPERCORE_POWER_ACTION's keys
864
+ def assert_or_ignore_repeated_request (msg ):
865
+ if ignore_repeated_request :
866
+ # Do not start/stop/shutdown VM twice.
867
+ # Normally we want to assert if a second start/stop/shutdown is tried.
868
+ # But as very last module change it makes sense to push VM into requested power_state,
869
+ # without knowing what did module already do with VM power_state.
870
+ # So in this special case, allow a second call, and just silently ignore
871
+ # it if VM power_state was already set to desired state.
872
+ return
873
+ else :
874
+ raise AssertionError (msg )
875
+
859
876
if not self ._power_state :
860
877
raise errors .ScaleComputingError ("No information about VM's power state." )
861
878
862
879
# keep a record what was done
863
880
if desired_power_action == "start" :
864
881
if self ._was_start_tried :
865
- raise AssertionError ("VM _was_start_tried already set" )
882
+ return assert_or_ignore_repeated_request (
883
+ "VM _was_start_tried already set"
884
+ )
866
885
self ._was_start_tried = True
867
886
if desired_power_action == "shutdown" :
868
887
if self ._was_nice_shutdown_tried :
869
- raise AssertionError ("VM _was_nice_shutdown_tried already set" )
888
+ return assert_or_ignore_repeated_request (
889
+ "VM _was_nice_shutdown_tried already set"
890
+ )
870
891
self ._was_nice_shutdown_tried = True
871
892
if desired_power_action == "stop" :
872
893
if self ._was_force_shutdown_tried :
873
- raise AssertionError ("VM _was_force_shutdown_tried already set" )
894
+ return assert_or_ignore_repeated_request (
895
+ "VM _was_force_shutdown_tried already set"
896
+ )
874
897
self ._was_force_shutdown_tried = True
875
- # reboot, reset are not included
876
-
877
- task_tag = rest_client .create_record (
878
- "/rest/v1/VirDomain/action" ,
879
- [
880
- dict (
881
- virDomainUUID = self .uuid ,
882
- actionType = FROM_ANSIBLE_TO_HYPERCORE_POWER_ACTION [
883
- desired_power_action
884
- ],
885
- cause = "INTERNAL" ,
898
+ if desired_power_action == "reboot" :
899
+ if self ._was_reboot_tried :
900
+ return assert_or_ignore_repeated_request (
901
+ "VM _was_reboot_tried already set"
886
902
)
887
- ],
888
- module .check_mode ,
889
- )
903
+ self ._was_reboot_tried = True
904
+ if desired_power_action == "reset" :
905
+ if self ._was_reset_tried :
906
+ return assert_or_ignore_repeated_request (
907
+ "VM _was_reset_tried already set"
908
+ )
909
+ self ._was_reset_tried = True
910
+
911
+ try :
912
+ task_tag = rest_client .create_record (
913
+ "/rest/v1/VirDomain/action" ,
914
+ [
915
+ dict (
916
+ virDomainUUID = self .uuid ,
917
+ actionType = FROM_ANSIBLE_TO_HYPERCORE_POWER_ACTION [
918
+ desired_power_action
919
+ ],
920
+ cause = "INTERNAL" ,
921
+ )
922
+ ],
923
+ module .check_mode ,
924
+ )
925
+ except errors .UnexpectedAPIResponse as ex :
926
+ if desired_power_action != "reset" :
927
+ raise
928
+ # We are allowed to send reset only if VM is in
929
+ # RUNNING or SHUTDOWN (as in middle of shutting down, but not yet fully shutdown).
930
+ # If VM is already shutoff, the request fails.
931
+ # Ignore this special case.
932
+ # The whole RESET is not even exposed on HyperCore UI,
933
+ # maybe we should remove it from ansible.
934
+ if ex .response_status != 500 :
935
+ # the '500 b'{"error":"An internal error occurred"}'' is the one to ignore
936
+ raise
937
+ module .warn ("Ignoring failed VM RESET" )
938
+ return
890
939
TaskTag .wait_task (rest_client , task_tag )
891
940
892
941
@classmethod
@@ -936,7 +985,7 @@ def vm_shutdown_forced(self, module, rest_client, reboot=False):
936
985
if vm_fresh_data ["state" ] in ["SHUTOFF" , "SHUTDOWN" ]:
937
986
return True
938
987
if module .params ["force_reboot" ] and self ._was_nice_shutdown_tried :
939
- self .update_vm_power_state (module , rest_client , "stop" )
988
+ self .update_vm_power_state (module , rest_client , "stop" , False )
940
989
# force shutdown should always work. If not, we need to pool for state change.
941
990
# Maybe we need to pool for state change anyway -
942
991
# TaskTag might be finished before VM is really off.
@@ -959,7 +1008,7 @@ def wait_shutdown(self, module, rest_client):
959
1008
# and module.params["shutdown_timeout"] # default is 300 anyway
960
1009
and not self ._was_nice_shutdown_tried
961
1010
):
962
- self .update_vm_power_state (module , rest_client , "shutdown" )
1011
+ self .update_vm_power_state (module , rest_client , "shutdown" , False )
963
1012
shutdown_timeout = module .params ["shutdown_timeout" ]
964
1013
start = time ()
965
1014
while 1 :
@@ -983,14 +1032,14 @@ def vm_power_up(self, module, rest_client):
983
1032
# - VM was initially stopped and
984
1033
# - module param power_state is omitted or contains "stop".
985
1034
if self .was_vm_shutdown () and self ._initially_running :
986
- self .update_vm_power_state (module , rest_client , "start" )
1035
+ self .update_vm_power_state (module , rest_client , "start" , False )
987
1036
return
988
1037
# Also start VM if module power_state requires a power on.
989
1038
# Field _power_action is set only if VM instance was created with from_ansible();
990
1039
# it is None if VM instance was created with from_hypercore().
991
1040
requested_power_action = module .params .get ("power_state" )
992
1041
if requested_power_action == "start" :
993
- self .update_vm_power_state (module , rest_client , "start" )
1042
+ self .update_vm_power_state (module , rest_client , "start" , False )
994
1043
995
1044
def was_vm_shutdown (self ) -> bool :
996
1045
"""
@@ -1236,21 +1285,26 @@ def set_vm_params(cls, module, rest_client, vm, param_subset: List[str]):
1236
1285
endpoint = "{0}/{1}" .format ("/rest/v1/VirDomain" , vm .uuid )
1237
1286
task_tag = rest_client .update_record (endpoint , payload , module .check_mode )
1238
1287
TaskTag .wait_task (rest_client , task_tag )
1288
+
1289
+ # shutdown VM if it needs to be rebooted to apply NIC/disk changes
1239
1290
if ManageVMParams ._needs_reboot (
1240
1291
module , changed_parameters
1241
1292
) and vm ._power_action not in ["stop" , "stopped" , "shutdown" ]:
1242
1293
vm .do_shutdown_steps (module , rest_client )
1294
+
1243
1295
return (
1244
1296
True ,
1245
1297
dict (
1246
1298
before = ManageVMParams ._build_before_diff (vm , module ),
1247
1299
after = ManageVMParams ._build_after_diff (module , rest_client ),
1248
1300
),
1301
+ changed_parameters ,
1249
1302
)
1250
1303
else :
1251
1304
return (
1252
1305
False ,
1253
1306
dict (before = None , after = None ),
1307
+ changed_parameters ,
1254
1308
)
1255
1309
1256
1310
@classmethod
0 commit comments