@@ -3816,6 +3816,335 @@ def test_get_guest_config_numa_host_instance_topo_cpu_pinning(self):
38163816 self.assertEqual([instance_cell.id], memnode.nodeset)
38173817 self.assertEqual("strict", memnode.mode)
38183818
3819+ def test_get_guest_config_numa_host_instance_cpu_mixed(self):
3820+ """Test to create mixed instance libvirt configuration which has a
3821+ default emulator thread policy and verify the NUMA topology related
3822+ settings.
3823+ """
3824+ self.flags(cpu_shared_set='2-5,8-29',
3825+ cpu_dedicated_set='6,7,30,31',
3826+ group='compute')
3827+
3828+ instance_topology = objects.InstanceNUMATopology(cells=[
3829+ objects.InstanceNUMACell(
3830+ id=3, cpuset=set([0, 1]), pcpuset=set([2, 3]), memory=1024,
3831+ cpu_pinning={2: 30, 3: 31}
3832+ ),
3833+ objects.InstanceNUMACell(
3834+ id=0, cpuset=set([4, 5, 6]), pcpuset=set([7]), memory=1024,
3835+ cpu_pinning={7: 6}
3836+ ),
3837+ ])
3838+ instance_ref = objects.Instance(**self.test_instance)
3839+ instance_ref.numa_topology = instance_topology
3840+ image_meta = objects.ImageMeta.from_dict(self.test_image_meta)
3841+ flavor = objects.Flavor(memory_mb=2048, vcpus=8, root_gb=496,
3842+ ephemeral_gb=8128, swap=33550336, name='fake',
3843+ extra_specs={})
3844+ instance_ref.flavor = flavor
3845+
3846+ caps = vconfig.LibvirtConfigCaps()
3847+ caps.host = vconfig.LibvirtConfigCapsHost()
3848+ caps.host.cpu = vconfig.LibvirtConfigCPU()
3849+ caps.host.cpu.arch = fields.Architecture.X86_64
3850+ caps.host.topology = fakelibvirt.NUMATopology(
3851+ cpu_nodes=4, cpu_sockets=1, cpu_cores=4, cpu_threads=2)
3852+ conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
3853+ disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type,
3854+ instance_ref,
3855+ image_meta)
3856+
3857+ with test.nested(
3858+ mock.patch.object(
3859+ objects.InstanceNUMATopology, "get_by_instance_uuid",
3860+ return_value=instance_topology),
3861+ mock.patch.object(host.Host, 'has_min_version', return_value=True),
3862+ mock.patch.object(host.Host, "get_capabilities",
3863+ return_value=caps),
3864+ mock.patch.object(host.Host, 'get_online_cpus',
3865+ return_value=set(range(32)))
3866+ ):
3867+ cfg = conn._get_guest_config(instance_ref, [],
3868+ image_meta, disk_info)
3869+ self.assertIsNone(cfg.cpuset)
3870+
3871+ # NOTE(huaqiang): Within a mixed instance, it is expected that the
3872+ # pinned and unpinned CPUs, which belong to the same instance NUMA
3873+ # cell, are scheduled on a same host NUMA cell, the instance pinned
3874+ # CPU is 1:1 scheduled to a dedicated host CPU, each unpinned CPU
3875+ # floats over the shared CPU list of the same host NUMA cell.
3876+ # The host NUMA cell's dedicated CPU list and shared CPU list are
3877+ # calculated from a combination of '[compute]cpu_dedicated_set',
3878+ # '[compute]cpu_shared_set', the host NUMA topology and the online
3879+ # CPUs.
3880+ #
3881+ # The first instance NUMA cell is fit into the fourth host NUMA
3882+ # cell due to having the same 'id' 3. Instance CPU 0 and 1 are
3883+ # unpinned CPUs, check each of them floats on the host NUMA cell's
3884+ # sharing CPUs, which are CPU 24-29.
3885+ self.assertEqual(0, cfg.cputune.vcpupin[0].id)
3886+ self.assertEqual(set(range(24, 30)), cfg.cputune.vcpupin[0].cpuset)
3887+ self.assertEqual(1, cfg.cputune.vcpupin[1].id)
3888+ self.assertEqual(set(range(24, 30)), cfg.cputune.vcpupin[1].cpuset)
3889+ # Check each of the instance NUMA cell's pinned CPUs is pinned to a
3890+ # dedicated CPU from the fourth host NUMA cell.
3891+ self.assertEqual(2, cfg.cputune.vcpupin[2].id)
3892+ self.assertEqual(set([30]), cfg.cputune.vcpupin[2].cpuset)
3893+ self.assertEqual(3, cfg.cputune.vcpupin[3].id)
3894+ self.assertEqual(set([31]), cfg.cputune.vcpupin[3].cpuset)
3895+
3896+ # Instance CPU 4-7 belong to the second instance NUMA cell, which
3897+ # is fit into host NUMA cell 0. CPU 4-6 are unpinned CPUs, each of
3898+ # them floats on the host NUMA cell's sharing CPU set, CPU 2-5.
3899+ self.assertEqual(4, cfg.cputune.vcpupin[4].id)
3900+ self.assertEqual(set(range(2, 6)), cfg.cputune.vcpupin[4].cpuset)
3901+ self.assertEqual(5, cfg.cputune.vcpupin[5].id)
3902+ self.assertEqual(set(range(2, 6)), cfg.cputune.vcpupin[5].cpuset)
3903+ self.assertEqual(6, cfg.cputune.vcpupin[6].id)
3904+ self.assertEqual(set(range(2, 6)), cfg.cputune.vcpupin[6].cpuset)
3905+ # Instance CPU 7 is pinned to the host NUMA cell's dedicated CPU 6.
3906+ self.assertEqual(set([6]), cfg.cputune.vcpupin[7].cpuset)
3907+ self.assertIsNotNone(cfg.cpu.numa)
3908+
3909+ # Check emulator thread is pinned to union of
3910+ # cfg.cputune.vcpupin[*].cpuset
3911+ self.assertIsInstance(cfg.cputune.emulatorpin,
3912+ vconfig.LibvirtConfigGuestCPUTuneEmulatorPin)
3913+ self.assertEqual(
3914+ set([2, 3, 4, 5, 6, 24, 25, 26, 27, 28, 29, 30, 31]),
3915+ cfg.cputune.emulatorpin.cpuset)
3916+
3917+ for i, (instance_cell, numa_cfg_cell) in enumerate(
3918+ zip(instance_topology.cells, cfg.cpu.numa.cells)
3919+ ):
3920+ self.assertEqual(i, numa_cfg_cell.id)
3921+ self.assertEqual(instance_cell.total_cpus, numa_cfg_cell.cpus)
3922+ self.assertEqual(instance_cell.memory * units.Ki,
3923+ numa_cfg_cell.memory)
3924+ self.assertIsNone(numa_cfg_cell.memAccess)
3925+
3926+ allnodes = set([cell.id for cell in instance_topology.cells])
3927+ self.assertEqual(allnodes, set(cfg.numatune.memory.nodeset))
3928+ self.assertEqual("strict", cfg.numatune.memory.mode)
3929+
3930+ for i, (instance_cell, memnode) in enumerate(
3931+ zip(instance_topology.cells, cfg.numatune.memnodes)
3932+ ):
3933+ self.assertEqual(i, memnode.cellid)
3934+ self.assertEqual([instance_cell.id], memnode.nodeset)
3935+
3936+ def test_get_guest_config_numa_host_instance_cpu_mixed_isolated_emu(self):
3937+ """Test to create mixed instance libvirt configuration which has an
3938+ ISOLATED emulator thread policy and verify the NUMA topology related
3939+ settings.
3940+ """
3941+ self.flags(cpu_shared_set='2-5,8-29',
3942+ cpu_dedicated_set='6,7,30,31',
3943+ group='compute')
3944+ instance_topology = objects.InstanceNUMATopology(
3945+ emulator_threads_policy=fields.CPUEmulatorThreadsPolicy.ISOLATE,
3946+ cells=[objects.InstanceNUMACell(
3947+ id=0, cpuset=set([0, 1]), pcpuset=set([2]), memory=1024,
3948+ cpu_pinning={2: 6},
3949+ cpuset_reserved=set([7]))])
3950+ instance_ref = objects.Instance(**self.test_instance)
3951+ instance_ref.numa_topology = instance_topology
3952+ image_meta = objects.ImageMeta.from_dict(self.test_image_meta)
3953+ flavor = objects.Flavor(memory_mb=2048, vcpus=8, root_gb=496,
3954+ ephemeral_gb=8128, swap=33550336, name='fake',
3955+ extra_specs={})
3956+ instance_ref.flavor = flavor
3957+
3958+ caps = vconfig.LibvirtConfigCaps()
3959+ caps.host = vconfig.LibvirtConfigCapsHost()
3960+ caps.host.cpu = vconfig.LibvirtConfigCPU()
3961+ caps.host.cpu.arch = fields.Architecture.X86_64
3962+ caps.host.topology = fakelibvirt.NUMATopology(
3963+ cpu_nodes=4, cpu_sockets=1, cpu_cores=4, cpu_threads=2)
3964+
3965+ conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
3966+ disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type,
3967+ instance_ref,
3968+ image_meta)
3969+
3970+ with test.nested(
3971+ mock.patch.object(
3972+ objects.InstanceNUMATopology, "get_by_instance_uuid",
3973+ return_value=instance_topology),
3974+ mock.patch.object(host.Host, 'has_min_version',
3975+ return_value=True),
3976+ mock.patch.object(host.Host, "get_capabilities",
3977+ return_value=caps),
3978+ mock.patch.object(host.Host, 'get_online_cpus',
3979+ return_value=set(range(32))),
3980+ ):
3981+ cfg = conn._get_guest_config(instance_ref, [],
3982+ image_meta, disk_info)
3983+ self.assertIsNone(cfg.cpuset)
3984+ # NOTE(huaqiang): The instance NUMA cell is fit into the first host
3985+ # NUMA cell, which is matched by the 'id' fields of two objects.
3986+ # CPU 2-5 are the first host NUMA cell's floating CPU set.
3987+ # Check any instance unpinned CPU is floating on this CPU set.
3988+ self.assertEqual(0, cfg.cputune.vcpupin[0].id)
3989+ self.assertEqual(set([2, 3, 4, 5]), cfg.cputune.vcpupin[0].cpuset)
3990+ self.assertEqual(1, cfg.cputune.vcpupin[1].id)
3991+ self.assertEqual(set([2, 3, 4, 5]), cfg.cputune.vcpupin[1].cpuset)
3992+ # Check instance CPU 2, a pinned CPU, is pinned to a dedicated CPU
3993+ # of host's first NUMA cell.
3994+ self.assertEqual(2, cfg.cputune.vcpupin[2].id)
3995+ self.assertEqual(set([6]), cfg.cputune.vcpupin[2].cpuset)
3996+ self.assertIsNotNone(cfg.cpu.numa)
3997+
3998+ # With an ISOLATE policy, emulator thread will be pinned to the
3999+ # reserved host CPU.
4000+ self.assertIsInstance(cfg.cputune.emulatorpin,
4001+ vconfig.LibvirtConfigGuestCPUTuneEmulatorPin)
4002+ self.assertEqual(set([7]), cfg.cputune.emulatorpin.cpuset)
4003+
4004+ for i, (instance_cell, numa_cfg_cell) in enumerate(
4005+ zip(instance_topology.cells, cfg.cpu.numa.cells)
4006+ ):
4007+ self.assertEqual(i, numa_cfg_cell.id)
4008+ self.assertEqual(instance_cell.total_cpus, numa_cfg_cell.cpus)
4009+ self.assertEqual(instance_cell.memory * units.Ki,
4010+ numa_cfg_cell.memory)
4011+ self.assertIsNone(numa_cfg_cell.memAccess)
4012+
4013+ allnodes = set([cell.id for cell in instance_topology.cells])
4014+ self.assertEqual(allnodes, set(cfg.numatune.memory.nodeset))
4015+ self.assertEqual("strict", cfg.numatune.memory.mode)
4016+
4017+ for i, (instance_cell, memnode) in enumerate(
4018+ zip(instance_topology.cells, cfg.numatune.memnodes)
4019+ ):
4020+ self.assertEqual(i, memnode.cellid)
4021+ self.assertEqual([instance_cell.id], memnode.nodeset)
4022+
4023+ def test_get_guest_config_numa_host_instance_cpu_mixed_realtime(self):
4024+ """Test of creating mixed instance libvirt configuration. which is
4025+ created through 'hw:cpu_realtime_mask' and 'hw:cpu_realtime' extra
4026+ specs, verifying the NUMA topology and real-time related settings.
4027+ """
4028+ self.flags(cpu_shared_set='2-5,8-29',
4029+ cpu_dedicated_set='6,7,30,31',
4030+ group='compute')
4031+
4032+ instance_topology = objects.InstanceNUMATopology(
4033+ cells=[
4034+ objects.InstanceNUMACell(
4035+ id=0, cpuset=set([2]), pcpuset=set([0, 1]),
4036+ cpu_pinning={0: 6, 1: 7},
4037+ memory=1024, pagesize=2048),
4038+ objects.InstanceNUMACell(
4039+ id=3, cpuset=set([3]), pcpuset=set([4, 5]),
4040+ cpu_pinning={4: 30, 5: 31},
4041+ memory=1024, pagesize=2048)])
4042+ instance_ref = objects.Instance(**self.test_instance)
4043+ instance_ref.numa_topology = instance_topology
4044+ image_meta = objects.ImageMeta.from_dict(self.test_image_meta)
4045+ # NOTE(huaqiang): libvirt driver takes the real-time CPU list from the
4046+ # flavor extra spec 'hw:cpu_realtime_mask'. For a mixed instance with
4047+ # real-time CPUs, the dedicated CPU and the real-time CPU are the
4048+ # same CPU set, this is checked in API layer.
4049+ flavor = objects.Flavor(
4050+ vcpus=6, memory_mb=2048, root_gb=496,
4051+ ephemeral_gb=8128, swap=33550336, name='fake',
4052+ extra_specs={
4053+ "hw:numa_nodes": "2",
4054+ "hw:cpu_realtime": "yes",
4055+ "hw:cpu_policy": "mixed",
4056+ "hw:cpu_realtime_mask": "^2-3"
4057+ })
4058+ instance_ref.flavor = flavor
4059+
4060+ caps = vconfig.LibvirtConfigCaps()
4061+ caps.host = vconfig.LibvirtConfigCapsHost()
4062+ caps.host.cpu = vconfig.LibvirtConfigCPU()
4063+ caps.host.cpu.arch = fields.Architecture.X86_64
4064+ caps.host.topology = fakelibvirt.NUMATopology(
4065+ cpu_nodes=4, cpu_sockets=1, cpu_cores=4, cpu_threads=2)
4066+ for i, cell in enumerate(caps.host.topology.cells):
4067+ cell.mempages = fakelibvirt.create_mempages(
4068+ [(4, 1024 * i), (2048, i)])
4069+
4070+ drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
4071+ disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type,
4072+ instance_ref,
4073+ image_meta)
4074+
4075+ with test.nested(
4076+ mock.patch.object(
4077+ objects.InstanceNUMATopology, "get_by_instance_uuid",
4078+ return_value=instance_topology),
4079+ mock.patch.object(host.Host, 'has_min_version', return_value=True),
4080+ mock.patch.object(host.Host, "get_capabilities",
4081+ return_value=caps),
4082+ mock.patch.object(host.Host, 'get_online_cpus',
4083+ return_value=set(range(32))),
4084+ ):
4085+ cfg = drvr._get_guest_config(instance_ref, [],
4086+ image_meta, disk_info)
4087+
4088+ for instance_cell, numa_cfg_cell, index in zip(
4089+ instance_topology.cells,
4090+ cfg.cpu.numa.cells,
4091+ range(len(instance_topology.cells))
4092+ ):
4093+ self.assertEqual(index, numa_cfg_cell.id)
4094+ self.assertEqual(instance_cell.total_cpus, numa_cfg_cell.cpus)
4095+ self.assertEqual(instance_cell.memory * units.Ki,
4096+ numa_cfg_cell.memory)
4097+ self.assertEqual("shared", numa_cfg_cell.memAccess)
4098+
4099+ allnodes = [cell.id for cell in instance_topology.cells]
4100+ self.assertEqual(allnodes, cfg.numatune.memory.nodeset)
4101+ self.assertEqual("strict", cfg.numatune.memory.mode)
4102+
4103+ for instance_cell, memnode, index in zip(
4104+ instance_topology.cells,
4105+ cfg.numatune.memnodes,
4106+ range(len(instance_topology.cells))
4107+ ):
4108+ self.assertEqual(index, memnode.cellid)
4109+ self.assertEqual([instance_cell.id], memnode.nodeset)
4110+ self.assertEqual("strict", memnode.mode)
4111+
4112+ # NOTE(huaqiang): Instance first NUMA cell is fit to the first host
4113+ # NUMA cell. In this host NUMA cell, CPU 2-5 are the sharing CPU
4114+ # set, CPU 6, 7 are dedicated CPUs.
4115+ #
4116+ # Check instance CPU 0, 1 are 1:1 pinned on host NUMA cell's
4117+ # dedicated CPUs.
4118+ self.assertEqual(set([6]), cfg.cputune.vcpupin[0].cpuset)
4119+ self.assertEqual(set([7]), cfg.cputune.vcpupin[1].cpuset)
4120+ # Check CPU 2, an unpinned CPU, is floating on this host NUMA
4121+ # cell's sharing CPU set.
4122+ self.assertEqual(set([2, 3, 4, 5]), cfg.cputune.vcpupin[2].cpuset)
4123+
4124+ # The second instance NUMA cell is fit to the fourth host NUMA
4125+ # cell due to a same 'id'. Host CPU 24-29 are sharing CPU set, host
4126+ # CPU 30, 31 are dedicated CPU to be pinning.
4127+ #
4128+ # Check CPU 3 is floating on the sharing CPU set.
4129+ self.assertEqual(set([24, 25, 26, 27, 28, 29]),
4130+ cfg.cputune.vcpupin[3].cpuset)
4131+ # Check CPU 4, 5 are pinned on host dedicated CPUs.
4132+ self.assertEqual(set([30]), cfg.cputune.vcpupin[4].cpuset)
4133+ self.assertEqual(set([31]), cfg.cputune.vcpupin[5].cpuset)
4134+
4135+ # Check the real-time host CPUs are excluded from the host CPU
4136+ # list the emulator is floating on.
4137+ self.assertEqual(set([2, 3, 4, 5, 24, 25, 26, 27, 28, 29]),
4138+ cfg.cputune.emulatorpin.cpuset)
4139+
4140+ # Check the real-time scheduler is set, and all real-time CPUs are
4141+ # in the vcpusched[0].vcpus list. In nova, the real-time scheduler
4142+ # is always set to 'fifo', and there is always only one element in
4143+ # cfg.cputune.vcpusched.
4144+ self.assertEqual(1, len(cfg.cputune.vcpusched))
4145+ self.assertEqual("fifo", cfg.cputune.vcpusched[0].scheduler)
4146+ self.assertEqual(set([0, 1, 4, 5]), cfg.cputune.vcpusched[0].vcpus)
4147+
38194148 def test_get_guest_config_numa_host_mempages_shared(self):
38204149 self.flags(cpu_shared_set='2-5', cpu_dedicated_set=None,
38214150 group='compute')
0 commit comments