diff --git a/nova/objects/instance_numa_topology.py b/nova/objects/instance_numa_topology.py index 6bd32d508f6f..00aaf9bc3773 100644 --- a/nova/objects/instance_numa_topology.py +++ b/nova/objects/instance_numa_topology.py @@ -17,22 +17,42 @@ from oslo.serialization import jsonutils from nova import db from nova import exception from nova.objects import base -from nova.objects import fields +from nova.objects import fields as obj_fields from nova.virt import hardware class InstanceNUMACell(base.NovaObject): # Version 1.0: Initial version # Version 1.1: Add pagesize field - VERSION = '1.1' + # Version 1.2: Add cpu_pinning_raw and topology fields + VERSION = '1.2' fields = { - 'id': fields.IntegerField(read_only=True), - 'cpuset': fields.SetOfIntegersField(), - 'memory': fields.IntegerField(), - 'pagesize': fields.IntegerField(nullable=True), + 'id': obj_fields.IntegerField(read_only=True), + 'cpuset': obj_fields.SetOfIntegersField(), + 'memory': obj_fields.IntegerField(), + 'pagesize': obj_fields.IntegerField(nullable=True), + 'cpu_topology': obj_fields.ObjectField('VirtCPUTopology', + nullable=True), + 'cpu_pinning_raw': obj_fields.DictOfIntegersField(nullable=True) } + obj_relationships = { + 'cpu_topology': [('1.2', '1.0')] + } + + cpu_pinning = obj_fields.DictProxyField('cpu_pinning_raw') + + def __init__(self, **kwargs): + super(InstanceNUMACell, self).__init__(**kwargs) + if 'cpu_topology' not in kwargs: + self.cpu_topology = None + if 'cpu_pinning' not in kwargs: + self.cpu_pinning = None + + def __len__(self): + return len(self.cpuset) + def _to_dict(self): # NOTE(sahid): Used as legacy, could be renamed in # _legacy_to_dict_ to the future to avoid confusing. @@ -53,6 +73,29 @@ class InstanceNUMACell(base.NovaObject): return cls(id=cell_id, cpuset=cpuset, memory=memory, pagesize=pagesize) + @property + def siblings(self): + cpu_list = sorted(list(self.cpuset)) + + threads = 0 + if self.cpu_topology: + threads = self.cpu_topology.threads + if threads == 1: + threads = 0 + + return map(set, zip(*[iter(cpu_list)] * threads)) + + def pin(self, vcpu, pcpu): + if vcpu not in self.cpuset: + return + pinning_dict = self.cpu_pinning or {} + pinning_dict[vcpu] = pcpu + self.cpu_pinning = pinning_dict + + def pin_vcpus(self, *cpu_pairs): + for vcpu, pcpu in cpu_pairs: + self.pin(vcpu, pcpu) + class InstanceNUMATopology(base.NovaObject): # Version 1.0: Initial version @@ -62,9 +105,9 @@ class InstanceNUMATopology(base.NovaObject): fields = { # NOTE(danms): The 'id' field is no longer used and should be # removed in the future when convenient - 'id': fields.IntegerField(), - 'instance_uuid': fields.UUIDField(), - 'cells': fields.ListOfObjectsField('InstanceNUMACell'), + 'id': obj_fields.IntegerField(), + 'instance_uuid': obj_fields.UUIDField(), + 'cells': obj_fields.ListOfObjectsField('InstanceNUMACell'), } @classmethod diff --git a/nova/tests/unit/objects/test_instance_numa_topology.py b/nova/tests/unit/objects/test_instance_numa_topology.py index 2d80fa644dfa..5054a772cd67 100644 --- a/nova/tests/unit/objects/test_instance_numa_topology.py +++ b/nova/tests/unit/objects/test_instance_numa_topology.py @@ -92,6 +92,44 @@ class _TestInstanceNUMATopology(object): objects.InstanceNUMATopology.get_by_instance_uuid, self.context, 'fake_uuid') + def test_siblings(self): + topo = objects.VirtCPUTopology(sockets=1, cores=3, threads=0) + inst_cell = objects.InstanceNUMACell( + cpuset=set([0, 1, 2]), topology=topo) + self.assertEqual([], inst_cell.siblings) + + # One thread actually means no threads + topo = objects.VirtCPUTopology(sockets=1, cores=3, threads=1) + inst_cell = objects.InstanceNUMACell( + cpuset=set([0, 1, 2]), cpu_topology=topo) + self.assertEqual([], inst_cell.siblings) + + topo = objects.VirtCPUTopology(sockets=1, cores=2, threads=2) + inst_cell = objects.InstanceNUMACell( + cpuset=set([0, 1, 2, 3]), cpu_topology=topo) + self.assertEqual([set([0, 1]), set([2, 3])], inst_cell.siblings) + + topo = objects.VirtCPUTopology(sockets=1, cores=1, threads=4) + inst_cell = objects.InstanceNUMACell( + cpuset=set([0, 1, 2, 3]), cpu_topology=topo) + self.assertEqual([set([0, 1, 2, 3])], inst_cell.siblings) + + def test_pin(self): + inst_cell = objects.InstanceNUMACell(cpuset=set([0, 1, 2, 3]), + cpu_pinning=None) + inst_cell.pin(0, 14) + self.assertEqual({0: 14}, inst_cell.cpu_pinning) + inst_cell.pin(12, 14) + self.assertEqual({0: 14}, inst_cell.cpu_pinning) + inst_cell.pin(1, 16) + self.assertEqual({0: 14, 1: 16}, inst_cell.cpu_pinning) + + def test_pin_vcpus(self): + inst_cell = objects.InstanceNUMACell(cpuset=set([0, 1, 2, 3]), + cpu_pinning=None) + inst_cell.pin_vcpus((0, 14), (1, 15), (2, 16), (3, 17)) + self.assertEqual({0: 14, 1: 15, 2: 16, 3: 17}, inst_cell.cpu_pinning) + class TestInstanceNUMATopology(test_objects._LocalTest, _TestInstanceNUMATopology): diff --git a/nova/tests/unit/objects/test_objects.py b/nova/tests/unit/objects/test_objects.py index f3d12ad45282..c4be9ea5de36 100644 --- a/nova/tests/unit/objects/test_objects.py +++ b/nova/tests/unit/objects/test_objects.py @@ -1103,7 +1103,7 @@ object_data = { 'InstanceGroupList': '1.6-c6b78f3c9d9080d33c08667e80589817', 'InstanceInfoCache': '1.5-ef64b604498bfa505a8c93747a9d8b2f', 'InstanceList': '1.13-179093360c48747a41694cc2f326d75d', - 'InstanceNUMACell': '1.1-8d2a13c8360cc9ea1b68c9c6c4476857', + 'InstanceNUMACell': '1.2-5d2dfa36e9ecca9b63f24bf3bc958ea4', 'InstanceNUMATopology': '1.1-86b95d263c4c68411d44c6741b8d2bb0', 'InstancePCIRequest': '1.1-e082d174f4643e5756ba098c47c1510f', 'InstancePCIRequests': '1.1-bc7c6684d8579ee49d6a3b8aef756918', @@ -1152,6 +1152,7 @@ object_relationships = { 'TagList': '1.0', 'SecurityGroupList': '1.0', 'InstancePCIRequests': '1.1'}, + 'InstanceNUMACell': {'VirtCPUTopology': '1.0'}, 'MyObj': {'MyOwnedObject': '1.0'}, 'SecurityGroupRule': {'SecurityGroup': '1.1'}, 'Service': {'ComputeNode': '1.6'},