This is needed so that we can actually update the given topology with the updated data after a successful claim. Deleting it will also be needed when we actually make the resize work properly for instances with NUMA topology, so we add it here as well. We do not expose the new InstanceNUMATopology methods as @remotable to avoid having to bump the object version thus making this an easier backport target. This is OK since they are only called from Instance.save() which is @remotable, and can be trivially made remotable should this be needed later (causing a version bump that need not be backported). Change-Id: I64ff2d00ca20bd065bb17ebaa9c40b64b8cbb817 Partial-bug: #1386236
131 lines
5.1 KiB
Python
131 lines
5.1 KiB
Python
# Copyright 2014 Red Hat Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
from nova import db
|
|
from nova import exception
|
|
from nova.objects import base
|
|
from nova.objects import fields
|
|
from nova.virt import hardware
|
|
|
|
|
|
class InstanceNUMACell(base.NovaObject):
|
|
# Version 1.0: Initial version
|
|
# Version 1.1: Add pagesize field
|
|
VERSION = '1.1'
|
|
|
|
fields = {
|
|
'id': fields.IntegerField(read_only=True),
|
|
'cpuset': fields.SetOfIntegersField(),
|
|
'memory': fields.IntegerField(),
|
|
'pagesize': fields.IntegerField(nullable=True),
|
|
}
|
|
|
|
|
|
class InstanceNUMATopology(base.NovaObject):
|
|
# Version 1.0: Initial version
|
|
# Version 1.1: Takes into account pagesize
|
|
VERSION = '1.1'
|
|
|
|
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'),
|
|
}
|
|
|
|
@classmethod
|
|
def obj_from_db_obj(cls, instance_uuid, db_obj):
|
|
topo = hardware.VirtNUMAInstanceTopology.from_json(db_obj)
|
|
obj_topology = cls.obj_from_topology(topo)
|
|
obj_topology.instance_uuid = instance_uuid
|
|
# NOTE (ndipanov) not really needed as we never save, but left for
|
|
# consistency
|
|
obj_topology.id = 0
|
|
obj_topology.obj_reset_changes()
|
|
return obj_topology
|
|
|
|
@classmethod
|
|
def obj_from_topology(cls, topology):
|
|
if not isinstance(topology, hardware.VirtNUMAInstanceTopology):
|
|
raise exception.ObjectActionError(action='obj_from_topology',
|
|
reason='invalid topology class')
|
|
if topology:
|
|
cells = []
|
|
for topocell in topology.cells:
|
|
pagesize = (topocell.pagesize
|
|
and topocell.pagesize.size_kb or None)
|
|
cell = InstanceNUMACell(id=topocell.id, cpuset=topocell.cpuset,
|
|
memory=topocell.memory,
|
|
pagesize=pagesize)
|
|
cells.append(cell)
|
|
return cls(cells=cells)
|
|
|
|
def topology_from_obj(self):
|
|
cells = []
|
|
for objcell in self.cells:
|
|
pagesize = (
|
|
objcell.pagesize and
|
|
hardware.VirtPageSize(objcell.pagesize) or None)
|
|
cell = hardware.VirtNUMATopologyCellInstance(objcell.id,
|
|
objcell.cpuset,
|
|
objcell.memory,
|
|
pagesize=pagesize)
|
|
cells.append(cell)
|
|
return hardware.VirtNUMAInstanceTopology(cells=cells)
|
|
|
|
# TODO(ndipanov) Remove this method on the major version bump to 2.0
|
|
@base.remotable
|
|
def create(self, context):
|
|
topology = self.topology_from_obj()
|
|
if not topology:
|
|
return
|
|
values = {'numa_topology': topology.to_json()}
|
|
db.instance_extra_update_by_uuid(context, self.instance_uuid,
|
|
values)
|
|
self.obj_reset_changes()
|
|
|
|
# NOTE(ndipanov): We can't rename create and want to avoid version bump
|
|
# as this needs to be backported to stable so this is not a @remotable
|
|
# That's OK since we only call it from inside Instance.save() which is.
|
|
def _save(self, context):
|
|
topology = self.topology_from_obj()
|
|
if not topology:
|
|
return
|
|
values = {'numa_topology': topology.to_json()}
|
|
db.instance_extra_update_by_uuid(context, self.instance_uuid,
|
|
values)
|
|
self.obj_reset_changes()
|
|
|
|
# NOTE(ndipanov): We want to avoid version bump
|
|
# as this needs to be backported to stable so this is not a @remotable
|
|
# That's OK since we only call it from inside Instance.save() which is.
|
|
@classmethod
|
|
def delete_by_instance_uuid(cls, context, instance_uuid):
|
|
values = {'numa_topology': None}
|
|
db.instance_extra_update_by_uuid(context, instance_uuid,
|
|
values)
|
|
|
|
@base.remotable_classmethod
|
|
def get_by_instance_uuid(cls, context, instance_uuid):
|
|
db_extra = db.instance_extra_get_by_instance_uuid(
|
|
context, instance_uuid, columns=['numa_topology'])
|
|
if not db_extra:
|
|
raise exception.NumaTopologyNotFound(instance_uuid=instance_uuid)
|
|
|
|
if db_extra['numa_topology'] is None:
|
|
return None
|
|
|
|
return cls.obj_from_db_obj(instance_uuid, db_extra['numa_topology'])
|