@@ -930,6 +930,87 @@ def test_resize_revert_bug_1944759(self):
930930 self ._assert_pinned_cpus (src_host , 2 )
931931 self ._assert_pinned_cpus (dst_host , 0 )
932932
933+ def test_resize_dedicated_policy_race_on_dest_bug_1953359 (self ):
934+
935+ self .flags (cpu_dedicated_set = '0-2' , cpu_shared_set = None ,
936+ group = 'compute' )
937+ self .flags (vcpu_pin_set = None )
938+
939+ host_info = fakelibvirt .HostInfo (cpu_nodes = 1 , cpu_sockets = 1 ,
940+ cpu_cores = 2 , cpu_threads = 1 )
941+ self .start_compute (host_info = host_info , hostname = 'compute1' )
942+
943+ extra_spec = {
944+ 'hw:cpu_policy' : 'dedicated' ,
945+ }
946+ flavor_id = self ._create_flavor (vcpu = 1 , extra_spec = extra_spec )
947+ expected_usage = {'DISK_GB' : 20 , 'MEMORY_MB' : 2048 , 'PCPU' : 1 }
948+
949+ server = self ._run_build_test (flavor_id , expected_usage = expected_usage )
950+
951+ inst = objects .Instance .get_by_uuid (self .ctxt , server ['id' ])
952+ self .assertEqual (1 , len (inst .numa_topology .cells ))
953+ # assert that the pcpu 0 is used on compute1
954+ self .assertEqual ({'0' : 0 }, inst .numa_topology .cells [0 ].cpu_pinning_raw )
955+
956+ # start another compute with the same config
957+ self .start_compute (host_info = host_info , hostname = 'compute2' )
958+
959+ # boot another instance but now on compute2 so that it occupies the
960+ # pcpu 0 on compute2
961+ # NOTE(gibi): _run_build_test cannot be used here as it assumes only
962+ # compute1 exists
963+ server2 = self ._create_server (
964+ flavor_id = flavor_id ,
965+ host = 'compute2' ,
966+ )
967+ inst2 = objects .Instance .get_by_uuid (self .ctxt , server2 ['id' ])
968+ self .assertEqual (1 , len (inst2 .numa_topology .cells ))
969+ # assert that the pcpu 0 is used
970+ self .assertEqual (
971+ {'0' : 0 }, inst2 .numa_topology .cells [0 ].cpu_pinning_raw )
972+
973+ # migrate the first instance from compute1 to compute2 but stop
974+ # migrating at the start of finish_resize. Then start a racing periodic
975+ # update_available_resources.
976+
977+ def fake_finish_resize (* args , ** kwargs ):
978+ # start a racing update_available_resource periodic
979+ self ._run_periodics ()
980+ # we expect it that CPU pinning fails on the destination node
981+ # as the resource_tracker will use the source node numa_topology
982+ # and that does not fit to the dest node as pcpu 0 in the dest
983+ # is already occupied.
984+
985+ # TODO(stephenfin): The mock of 'migrate_disk_and_power_off' should
986+ # probably be less...dumb
987+ with mock .patch ('nova.virt.libvirt.driver.LibvirtDriver'
988+ '.migrate_disk_and_power_off' , return_value = '{}' ):
989+ with mock .patch (
990+ 'nova.compute.manager.ComputeManager.finish_resize'
991+ ) as mock_finish_resize :
992+ mock_finish_resize .side_effect = fake_finish_resize
993+ post = {'migrate' : None }
994+ self .admin_api .post_server_action (server ['id' ], post )
995+
996+ log = self .stdlog .logger .output
997+ # The resize_claim correctly calculates that the inst1 should be pinned
998+ # to pcpu id 1 instead of 0
999+ self .assertIn (
1000+ 'Computed NUMA topology CPU pinning: usable pCPUs: [[1]], '
1001+ 'vCPUs mapping: [(0, 1)]' ,
1002+ log ,
1003+ )
1004+ # But the periodic fails as it tries to apply the source topology on
1005+ # the dest. This is bug 1953359.
1006+ log = self .stdlog .logger .output
1007+ self .assertIn ('Error updating resources for node compute2' , log )
1008+ self .assertIn (
1009+ 'nova.exception.CPUPinningInvalid: CPU set to pin [0] must be '
1010+ 'a subset of free CPU set [1]' ,
1011+ log ,
1012+ )
1013+
9331014
9341015class NUMAServerTestWithCountingQuotaFromPlacement (NUMAServersTest ):
9351016
0 commit comments