@@ -1259,9 +1259,26 @@ let restart_failed : (API.ref_VM, unit) Hashtbl.t = Hashtbl.create 10
1259
1259
(* We also limit the rate we attempt to retry starting the VM. *)
1260
1260
let last_start_attempt : (API.ref_VM, float) Hashtbl.t = Hashtbl. create 10
1261
1261
1262
+ module VMRefOrd = struct
1263
+ type t = [`VM ] Ref .t
1264
+
1265
+ let compare = Ref. compare
1266
+ end
1267
+
1268
+ module VMMap = Map. Make (VMRefOrd )
1269
+
1270
+ (* When a host is up, it will be added in the HA live set. But it may be still
1271
+ in disabled state so that starting best-effort VMs on it would fail.
1272
+ Meanwhile we don't want to retry on starting them forever.
1273
+ This data is to remember the best-effort VMs which failed to start due to
1274
+ this and the key of the map is the VM ref. And its value is the count of the
1275
+ attempts of starting. This is to avoid retrying for ever and can be adjusted
1276
+ according to how hong the host becomes enabled since it is in HA live set. *)
1277
+ let tried_best_eff_vms = ref VMMap. empty
1278
+
1262
1279
(* Takes the current live_set and number of hosts we're planning to handle, updates the host records in the database
1263
1280
and restarts any offline protected VMs *)
1264
- let restart_auto_run_vms ~__context live_set n =
1281
+ let restart_auto_run_vms ~__context ~ last_live_set ~ live_set n =
1265
1282
(* ensure we have live=false on the host_metrics for those hosts not in the live_set; and force state to Halted for
1266
1283
all VMs that are "running" or "paused" with resident_on set to one of the hosts that is now dead
1267
1284
*)
@@ -1566,32 +1583,90 @@ let restart_auto_run_vms ~__context live_set n =
1566
1583
ok since this is 'best-effort'). NOTE we do not use the restart_vm function above as this will mark the
1567
1584
pool as overcommitted if an HA_OPERATION_WOULD_BREAK_FAILOVER_PLAN is received (although this should never
1568
1585
happen it's better safe than sorry) *)
1569
- map_parallel
1570
- ~order_f: (fun vm -> order_f (vm, Db.VM. get_record ~__context ~self: vm))
1571
- (fun vm ->
1586
+ let is_best_effort r =
1587
+ r.API. vM_ha_restart_priority = Constants. ha_restart_best_effort
1588
+ && r.API. vM_power_state = `Halted
1589
+ in
1590
+ let resets =
1591
+ ! reset_vms
1592
+ |> List. map (fun self -> (self, Db.VM. get_record ~__context ~self ))
1593
+ in
1594
+ let revalidate_tried m =
1595
+ let valid, invalid =
1596
+ VMMap. bindings m
1597
+ |> List. partition_map (fun (self , _ ) ->
1598
+ match Db.VM. get_record ~__context ~self with
1599
+ | r ->
1600
+ Left (self, r)
1601
+ | exception _ ->
1602
+ Right self
1603
+ )
1604
+ in
1605
+ let to_retry, to_remove =
1606
+ List. partition (fun (_ , r ) -> is_best_effort r) valid
1607
+ in
1608
+ let m' =
1609
+ List. map fst to_remove
1610
+ |> List. rev_append invalid
1611
+ |> List. fold_left (fun acc vm -> VMMap. remove vm acc) m
1612
+ in
1613
+ (to_retry, m')
1614
+ in
1615
+ let best_effort_vms =
1616
+ (* Carefully decide which best-effort VMs should attempt to start. *)
1617
+ let all_prot_is_ok = List. for_all (fun (_ , r ) -> r = Ok () ) started in
1618
+ let is_better = List. compare_lengths live_set last_live_set > 0 in
1619
+ ( match (all_prot_is_ok, is_better, last_live_set = live_set) with
1620
+ | true , true , _ ->
1621
+ (* Try to start all the best-effort halted VMs when HA is being
1622
+ enabled or some hosts are transiting to HA live.
1623
+ The DB has been updated by Xapi_vm_lifecycle.force_state_reset.
1624
+ Read again. *)
1625
+ tried_best_eff_vms := VMMap. empty ;
1626
+ Db.VM. get_all_records ~__context
1627
+ | true , false , true ->
1628
+ (* Retry for best-effort VMs which attepmted but failed last time. *)
1629
+ let to_retry, m = revalidate_tried ! tried_best_eff_vms in
1630
+ tried_best_eff_vms := m ;
1631
+ List. rev_append to_retry resets
1632
+ | true , false , false | false , _ , _ ->
1633
+ (* Try to start only the reset VMs. They were observed as residing
1634
+ on the non-live hosts in this run.
1635
+ Give up starting tried VMs as the HA situation changes. *)
1636
+ tried_best_eff_vms := VMMap. empty ;
1637
+ resets
1638
+ )
1639
+ |> List. filter (fun (_ , r ) -> is_best_effort r)
1640
+ in
1641
+ map_parallel ~order_f
1642
+ (fun (vm , _ ) ->
1572
1643
( vm
1573
- , if
1574
- Db.VM. get_power_state ~__context ~self: vm = `Halted
1575
- && Db.VM. get_ha_restart_priority ~__context ~self: vm
1576
- = Constants. ha_restart_best_effort
1577
- then
1578
- TaskChains. task (fun () ->
1579
- Client.Client.Async.VM. start ~rpc ~session_id ~vm
1580
- ~start_paused: false ~force: true
1581
- )
1582
- else
1583
- TaskChains. ok Rpc. Null
1644
+ , TaskChains. task (fun () ->
1645
+ Client.Client.Async.VM. start ~rpc ~session_id ~vm
1646
+ ~start_paused: false ~force: true
1647
+ )
1584
1648
)
1585
1649
)
1586
- ! reset_vms
1650
+ best_effort_vms
1587
1651
|> List. iter (fun (vm , result ) ->
1588
1652
match result with
1589
1653
| Error e ->
1654
+ tried_best_eff_vms :=
1655
+ VMMap. update vm
1656
+ (Option. fold ~none: (Some 1 ) ~some: (fun n ->
1657
+ if n < ! Xapi_globs. ha_best_effort_max_retries then
1658
+ Some (n + 1 )
1659
+ else
1660
+ None
1661
+ )
1662
+ )
1663
+ ! tried_best_eff_vms ;
1590
1664
error " Failed to restart best-effort VM %s (%s): %s"
1591
1665
(Db.VM. get_uuid ~__context ~self: vm)
1592
1666
(Db.VM. get_name_label ~__context ~self: vm)
1593
1667
(ExnHelper. string_of_exn e)
1594
1668
| Ok _ ->
1669
+ tried_best_eff_vms := VMMap. remove vm ! tried_best_eff_vms ;
1595
1670
()
1596
1671
)
1597
1672
)
0 commit comments