@@ -818,6 +818,88 @@ def test_resize_revert_bug_1944759(self):
818818 self ._assert_pinned_cpus (src_host , 2 )
819819 self ._assert_pinned_cpus (dst_host , 0 )
820820
821+ def test_resize_dedicated_policy_race_on_dest_bug_1953359 (self ):
822+
823+ self .flags (cpu_dedicated_set = '0-2' , cpu_shared_set = None ,
824+ group = 'compute' )
825+ self .flags (vcpu_pin_set = None )
826+
827+ host_info = fakelibvirt .HostInfo (cpu_nodes = 1 , cpu_sockets = 1 ,
828+ cpu_cores = 2 , cpu_threads = 1 ,
829+ kB_mem = 15740000 )
830+ self .start_compute (host_info = host_info , hostname = 'compute1' )
831+
832+ extra_spec = {
833+ 'hw:cpu_policy' : 'dedicated' ,
834+ }
835+ flavor_id = self ._create_flavor (vcpu = 1 , extra_spec = extra_spec )
836+ expected_usage = {'DISK_GB' : 20 , 'MEMORY_MB' : 2048 , 'PCPU' : 1 }
837+
838+ server = self ._run_build_test (flavor_id , expected_usage = expected_usage )
839+
840+ inst = objects .Instance .get_by_uuid (self .ctxt , server ['id' ])
841+ self .assertEqual (1 , len (inst .numa_topology .cells ))
842+ # assert that the pcpu 0 is used on compute1
843+ self .assertEqual ({'0' : 0 }, inst .numa_topology .cells [0 ].cpu_pinning_raw )
844+
845+ # start another compute with the same config
846+ self .start_compute (host_info = host_info , hostname = 'compute2' )
847+
848+ # boot another instance but now on compute2 so that it occupies the
849+ # pcpu 0 on compute2
850+ # NOTE(gibi): _run_build_test cannot be used here as it assumes only
851+ # compute1 exists
852+ server2 = self ._create_server (
853+ flavor_id = flavor_id ,
854+ host = 'compute2' ,
855+ )
856+ inst2 = objects .Instance .get_by_uuid (self .ctxt , server2 ['id' ])
857+ self .assertEqual (1 , len (inst2 .numa_topology .cells ))
858+ # assert that the pcpu 0 is used
859+ self .assertEqual (
860+ {'0' : 0 }, inst2 .numa_topology .cells [0 ].cpu_pinning_raw )
861+
862+ # migrate the first instance from compute1 to compute2 but stop
863+ # migrating at the start of finish_resize. Then start a racing periodic
864+ # update_available_resources.
865+
866+ def fake_finish_resize (* args , ** kwargs ):
867+ # start a racing update_available_resource periodic
868+ self ._run_periodics ()
869+ # we expect it that CPU pinning fails on the destination node
870+ # as the resource_tracker will use the source node numa_topology
871+ # and that does not fit to the dest node as pcpu 0 in the dest
872+ # is already occupied.
873+
874+ # TODO(stephenfin): The mock of 'migrate_disk_and_power_off' should
875+ # probably be less...dumb
876+ with mock .patch ('nova.virt.libvirt.driver.LibvirtDriver'
877+ '.migrate_disk_and_power_off' , return_value = '{}' ):
878+ with mock .patch (
879+ 'nova.compute.manager.ComputeManager.finish_resize'
880+ ) as mock_finish_resize :
881+ mock_finish_resize .side_effect = fake_finish_resize
882+ post = {'migrate' : None }
883+ self .admin_api .post_server_action (server ['id' ], post )
884+
885+ log = self .stdlog .logger .output
886+ # The resize_claim correctly calculates that the inst1 should be pinned
887+ # to pcpu id 1 instead of 0
888+ self .assertIn (
889+ 'Computed NUMA topology CPU pinning: usable pCPUs: [[1]], '
890+ 'vCPUs mapping: [(0, 1)]' ,
891+ log ,
892+ )
893+ # But the periodic fails as it tries to apply the source topology on
894+ # the dest. This is bug 1953359.
895+ log = self .stdlog .logger .output
896+ self .assertIn ('Error updating resources for node compute2' , log )
897+ self .assertIn (
898+ 'nova.exception.CPUPinningInvalid: CPU set to pin [0] must be '
899+ 'a subset of free CPU set [1]' ,
900+ log ,
901+ )
902+
821903
822904class NUMAServerTestWithCountingQuotaFromPlacement (NUMAServersTest ):
823905
0 commit comments