Skip to content

Commit

Permalink
UT - CP-47302: VM start with anti-affinity
Browse files Browse the repository at this point in the history
Signed-off-by: Bengang Yuan <[email protected]>
  • Loading branch information
BengangY committed Apr 23, 2024
1 parent eb98609 commit 9909216
Show file tree
Hide file tree
Showing 2 changed files with 309 additions and 7 deletions.
314 changes: 308 additions & 6 deletions ocaml/tests/test_vm_helpers.ml
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ let rec assert_equivalent expected_grouping actual_grouping =
assert_host_groups_equal e g ;
assert_equivalent es gs

let assert_host_groups_equal_for_vgpu g g' =
let assert_host_groups_equal g g' =
match g' with
| [] ->
()
Expand All @@ -170,7 +170,7 @@ let assert_host_groups_equal_for_vgpu g g' =
Alcotest.(check (slist string String.compare))
"check host strings" (extract_host_strings g) (extract_host_strings g')

let rec assert_equivalent_for_vgpu expected_grouping actual_grouping =
let rec assert_equivalent_for_grouping expected_grouping actual_grouping =
match (expected_grouping, actual_grouping) with
| [], [] ->
()
Expand All @@ -181,19 +181,23 @@ let rec assert_equivalent_for_vgpu expected_grouping actual_grouping =
Alcotest.fail
(Printf.sprintf "%d fewer groups than expected." (List.length xx))
| e :: es, g :: gs ->
assert_host_groups_equal_for_vgpu e g ;
assert_equivalent_for_vgpu es gs
assert_host_groups_equal e g ;
assert_equivalent_for_grouping es gs

let assert_grouping ~__context gpu_group ~visible_hosts vgpu_type g =
let vgpu = VGPU_T.make_vgpu ~__context ~gPU_group:gpu_group vgpu_type in
let host_lists = rank_hosts_by_best_vgpu ~__context vgpu visible_hosts in
assert_equivalent_for_vgpu g host_lists
assert_equivalent_for_grouping g host_lists

let check_expectations ~__context gpu_group visible_hosts vgpu_type
expected_grouping =
assert_grouping ~__context gpu_group ~visible_hosts vgpu_type
expected_grouping

let check_anti_affinity_grouping ~__context ~vm ~group expected_grouping =
let host_lists = rank_hosts_by_placement ~__context ~vm ~group in
assert_equivalent_for_grouping expected_grouping host_lists

let test_group_hosts_bf () =
on_pool_of_k1s
VGPU_T.(
Expand Down Expand Up @@ -524,6 +528,38 @@ let test_group_hosts_netsriov_with_allocated () =
"Test-failure: Unexpected number of sriov network in test"
)

(*--- Helper functions ---*)
let on_pool_of_anti_affinity
(f : Context.t -> API.ref_host -> API.ref_host -> API.ref_host -> 'a) =
let __context = T.make_test_database () in
let h1 = List.hd (Db.Host.get_all ~__context) in
(* Make two more hosts *)
let h2 = T.make_host ~__context () in
let h3 = T.make_host ~__context () in
f __context h1 h2 h3

let test_get_group_key_anti_affinity () =
on_pool_of_anti_affinity (fun __context _ _ _ ->
let vm = T.make_vm ~__context () in
let group = T.make_vm_group ~__context ~placement:`anti_affinity () in
Db.VM.set_groups ~__context ~self:vm ~value:[group] ;
match Xapi_vm_helpers.get_group_key ~__context ~vm with
| `AntiAffinity _ ->
()
| _ ->
Alcotest.fail "Test-failure: Unexpected Group Key in test"
) ;
on_pool_of_anti_affinity (fun __context _ _ _ ->
let vm = T.make_vm ~__context () in
let normal_group = T.make_vm_group ~__context ~placement:`normal () in
Db.VM.set_groups ~__context ~self:vm ~value:[normal_group] ;
match Xapi_vm_helpers.get_group_key ~__context ~vm with
| `Other ->
()
| _ ->
Alcotest.fail "Test-failure: Unexpected Group Key in test"
)

let test_get_group_key_vgpu () =
on_pool_of_intel_i350 (fun __context _ _ _ ->
let group = List.hd (Db.GPU_group.get_all ~__context) in
Expand Down Expand Up @@ -573,6 +609,263 @@ let test_get_group_key_vgpu_and_netsriov () =
Alcotest.fail "Test-failure: Unexpected Group Key in test"
)

let test_get_group_key_anti_affinity_and_vgpu_and_netsriov () =
on_pool_of_intel_i350 (fun __context _ _ _ ->
let group = List.hd (Db.GPU_group.get_all ~__context) in
let vm = make_vm_with_vgpu_in_group ~__context VGPU_T.k100 group in
let sriov_network =
List.find
(fun network ->
Xapi_network_sriov_helpers.is_sriov_network ~__context ~self:network
)
(Db.Network.get_all ~__context)
in
let (_ : API.ref_VIF) =
T.make_vif ~__context ~vM:vm ~network:sriov_network ()
in
let anti_affinity_group =
T.make_vm_group ~__context ~placement:`anti_affinity ()
in
Db.VM.set_groups ~__context ~self:vm ~value:[anti_affinity_group] ;
match Xapi_vm_helpers.get_group_key ~__context ~vm with
| `AntiAffinity _ ->
()
| _ ->
Alcotest.fail "Test-failure: Unexpected Group Key in test"
)

type test_case = {
description: string
; state: string
; vms: string list
; resident_on: (string * string) list
; placement_policy: string
; affinity_host: string option
; expected: string list list
}

let test_cases =
[
{
description= "No VM in the group"
; state= "halted"
; vms= []
; resident_on= []
; placement_policy= "anti_affinity"
; affinity_host= None
; expected= [["h1"; "h2"; "h3"]]
}
; {
description= "Not anti affinity group"
; state= "halted"
; vms= ["vm1"; "vm2"; "vm3"]
; resident_on= [("vm1", "h2"); ("vm2", "h3"); ("vm3", "h3")]
; placement_policy= "normal"
; affinity_host= None
; expected= [["h1"; "h2"; "h3"]]
}
; {
description= "3 stopped VMs"
; state= "halted"
; vms= ["vm1"; "vm2"; "vm3"]
; resident_on= []
; placement_policy= "anti_affinity"
; affinity_host= None
; expected= [["h1"; "h2"; "h3"]]
}
; {
description= "3 suspended VMs"
; state= "suspended"
; vms= ["vm1"; "vm2"; "vm3"]
; resident_on= []
; placement_policy= "anti_affinity"
; affinity_host= None
; expected= [["h1"; "h2"; "h3"]]
}
; {
description= "3 paused VMs (h1(0) h2(1) h3(2))"
; state= "paused"
; vms= ["vm1"; "vm2"; "vm3"]
; resident_on= [("vm1", "h2"); ("vm2", "h3"); ("vm3", "h3")]
; placement_policy= "anti_affinity"
; affinity_host= None
; expected= [["h1"; "h2"; "h3"]]
}
; {
description= "3 running VMs (h1(1) h2(1) h3(1))"
; state= "running"
; vms= ["vm1"; "vm2"; "vm3"]
; resident_on= [("vm1", "h1"); ("vm2", "h2"); ("vm3", "h3")]
; placement_policy= "anti_affinity"
; affinity_host= None
; expected= [["h1"; "h2"; "h3"]]
}
; {
description= "3 running VMs (h1(0) h2(1) h3(2))"
; state= "running"
; vms= ["vm1"; "vm2"; "vm3"]
; resident_on= [("vm1", "h2"); ("vm2", "h3"); ("vm3", "h3")]
; placement_policy= "anti_affinity"
; affinity_host= None
; expected= [["h1"]; ["h2"]; ["h3"]]
}
; {
description= "3 running VMs (h1(0) h2(0) h3(3))"
; state= "running"
; vms= ["vm1"; "vm2"; "vm3"]
; resident_on= [("vm1", "h3"); ("vm2", "h3"); ("vm3", "h3")]
; placement_policy= "anti_affinity"
; affinity_host= None
; expected= [["h1"; "h2"]; ["h3"]]
}
; {
description= "3 starting VMs (h1(1) h2(1) h3(1))"
; state= "starting"
; vms= ["vm1"; "vm2"; "vm3"]
; resident_on= [("vm1", "h1"); ("vm2", "h2"); ("vm3", "h3")]
; placement_policy= "anti_affinity"
; affinity_host= None
; expected= [["h1"; "h2"; "h3"]]
}
; {
description= "3 starting VMs (h1(0) h2(1) h3(2))"
; state= "starting"
; vms= ["vm1"; "vm2"; "vm3"]
; resident_on= [("vm1", "h2"); ("vm2", "h3"); ("vm3", "h3")]
; placement_policy= "anti_affinity"
; affinity_host= None
; expected= [["h1"]; ["h2"]; ["h3"]]
}
; {
description= "3 starting VMs (h1(0) h2(0) h3(3))"
; state= "starting"
; vms= ["vm1"; "vm2"; "vm3"]
; resident_on= [("vm1", "h3"); ("vm2", "h3"); ("vm3", "h3")]
; placement_policy= "anti_affinity"
; affinity_host= None
; expected= [["h1"; "h2"]; ["h3"]]
}
; {
description= "3 running VMs with affinity host"
; state= "running"
; vms= ["vm1"; "vm2"; "vm3"]
; resident_on= [("vm1", "h1"); ("vm2", "h2"); ("vm3", "h3")]
; placement_policy= "anti_affinity"
; affinity_host= Some "h3"
; expected= [["h3"]; ["h1"; "h2"]]
}
; {
description= "6 running VMs (h1(2) h2(2) h3(2))"
; state= "starting"
; vms= ["vm1"; "vm2"; "vm3"; "vm4"; "vm5"; "vm6"]
; resident_on=
[
("vm1", "h1")
; ("vm2", "h1")
; ("vm3", "h2")
; ("vm4", "h2")
; ("vm5", "h3")
; ("vm6", "h3")
]
; placement_policy= "anti_affinity"
; affinity_host= None
; expected= [["h1"; "h2"; "h3"]]
}
; {
description= "6 running VMs (h1(1) h2(2) h3(3))"
; state= "starting"
; vms= ["vm1"; "vm2"; "vm3"; "vm4"; "vm5"; "vm6"]
; resident_on=
[
("vm1", "h1")
; ("vm2", "h2")
; ("vm3", "h2")
; ("vm4", "h3")
; ("vm5", "h3")
; ("vm6", "h3")
]
; placement_policy= "anti_affinity"
; affinity_host= None
; expected= [["h1"]; ["h2"]; ["h3"]]
}
]

let make_other_vms_for_aa_test ~__context ~host1 ~host2 ~host3 =
let no_group_vm1 = T.make_vm ~__context ~power_state:`Running () in
Db.VM.set_resident_on ~__context ~self:no_group_vm1 ~value:host1 ;
let no_group_vm2 = T.make_vm ~__context ~power_state:`Running () in
Db.VM.set_resident_on ~__context ~self:no_group_vm2 ~value:host2 ;
let group = T.make_vm_group ~__context ~placement:`anti_affinity () in
let other_group_vm1 = T.make_vm ~__context ~power_state:`Halted () in
let other_group_vm2 = T.make_vm ~__context ~power_state:`Running () in
Db.VM.set_resident_on ~__context ~self:other_group_vm2 ~value:host3 ;
Db.VM.set_groups ~__context ~self:other_group_vm1 ~value:[group] ;
Db.VM.set_groups ~__context ~self:other_group_vm2 ~value:[group]

let test_anti_affinity
{state; vms; resident_on; placement_policy; affinity_host; expected; _} () =
let __context = T.make_test_database () in
let host1 = List.hd (Db.Host.get_all ~__context) in
let host2 = T.make_host ~__context () in
let host3 = T.make_host ~__context () in
let host_map = [("h1", host1); ("h2", host2); ("h3", host3)] in
let vm = T.make_vm ~__context () in
let placement =
match placement_policy with
| "anti_affinity" ->
`anti_affinity
| _ ->
`normal
in
let group = T.make_vm_group ~__context ~placement () in
Db.VM.set_groups ~__context ~self:vm ~value:[group] ;
vms
|> List.map (fun key ->
let vm = T.make_vm ~__context () in
(key, vm)
)
|> List.iter (fun (key, vm) ->
Db.VM.set_groups ~__context ~self:vm ~value:[group] ;
match state with
| "running" ->
Db.VM.set_power_state ~__context ~self:vm ~value:`Running ;
Db.VM.set_resident_on ~__context ~self:vm
~value:(List.assoc (List.assoc key resident_on) host_map)
| "starting" ->
Db.VM.set_power_state ~__context ~self:vm ~value:`Halted ;
Db.VM.set_scheduled_to_be_resident_on ~__context ~self:vm
~value:(List.assoc (List.assoc key resident_on) host_map)
| "suspended" ->
Db.VM.set_power_state ~__context ~self:vm ~value:`Suspended
| "paused" ->
Db.VM.set_power_state ~__context ~self:vm ~value:`Paused ;
Db.VM.set_resident_on ~__context ~self:vm
~value:(List.assoc (List.assoc key resident_on) host_map)
| "halted" ->
Db.VM.set_power_state ~__context ~self:vm ~value:`Halted
| _ ->
()
) ;
Db.VM.set_affinity ~__context ~self:vm
~value:
( match affinity_host with
| None ->
Ref.null
| Some h ->
List.assoc h host_map
) ;
make_other_vms_for_aa_test ~__context ~host1 ~host2 ~host3 ;
check_anti_affinity_grouping ~__context ~vm ~group
(List.map
(fun list -> List.map (fun key -> List.assoc key host_map) list)
expected
)

let generate_anti_affinity_tests case =
(case.description, `Quick, test_anti_affinity case)

let anti_affinity_tests = List.map generate_anti_affinity_tests test_cases

let test =
[
("test_gpus_available_succeeds", `Quick, test_gpus_available_succeeds)
Expand Down Expand Up @@ -612,14 +905,23 @@ let test =
, `Quick
, test_group_hosts_netsriov_with_allocated
)
; ( "test_get_group_key_anti_affinity"
, `Quick
, test_get_group_key_anti_affinity
)
; ("test_get_group_key_vgpu", `Quick, test_get_group_key_vgpu)
; ("test_get_group_key_netsriov", `Quick, test_get_group_key_netsriov)
; ( "test_get_group_key_vgpu_and_netsriov"
, `Quick
, test_get_group_key_vgpu_and_netsriov
)
; ( "test_get_group_key_anti_affinity_and_vgpu_and_netsriov"
, `Quick
, test_get_group_key_anti_affinity_and_vgpu_and_netsriov
)
]

let () =
Suite_init.harness_init () ;
Alcotest.run "Test VM Helpers suite" [("Test_vm_helpers", test)]
Alcotest.run "Test VM Helpers suite"
[("Test_vm_helpers", test @ anti_affinity_tests)]
2 changes: 1 addition & 1 deletion quality-gate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
set -e

list-hd () {
N=318
N=321
LIST_HD=$(git grep -r --count 'List.hd' -- **/*.ml | cut -d ':' -f 2 | paste -sd+ - | bc)
if [ "$LIST_HD" -eq "$N" ]; then
echo "OK counted $LIST_HD List.hd usages"
Expand Down

0 comments on commit 9909216

Please sign in to comment.