Activity log for bug #1961482

Date Who What changed Old value New value Message
2022-02-19 16:04:48 Balazs Gibizer bug added bug
2022-02-19 16:05:22 Balazs Gibizer description In Nova we have the following relationship (reduced the size of the objects for clarity): class Architecture(BaseNovaEnum): X86_64 = arch.X86_64 RISCV32 = arch.RISCV32 class ArchitectureField(BaseEnumField): AUTO_TYPE = Architecture() class HVSpec(VersionedObject): # bumped to 1.3 when RISCV32 introduced VERSION = '1.3' fields = { 'arch': fields.ArchitectureField(), } class ComputeNode(VersionedObject): # bumped to 1.20 when RISCV32 introduced VERSION = '1.20' fields = { 'supported_hv_specs': fields.ListOfObjectsField('HVSpec'), } Now assume having a ComputeNode object with version 1.20 using RISCV32 in one of the HVSpecs in the supported_hv_specs list and you want to backport that object to ComputeNode 1.19 (HVSpec 1.2). The HVSpec object cannot backport itself as there is no valid old version of the object. So only the parent object, ComputeNode, can do a meaningful backport. It can remove the supported_hv_specs list those HVSpec objects that has too new arch value. So the impl for that is something like: def obj_make_compatible(self, primitive, target_version): str_target_version = target_version target_version = versionutils.convert_version_to_tuple(target_version) if target_version < (1, 20) and 'supported_hv_specs' in primitive: primitive['supported_hv_specs'] = [ hvspec for hvspec in primitive['supported_hv_specs'] if hvspec['nova_object.data']['arch'] not in ('riscv32', 'riscv64') ] # [removed the older backports] super(ComputeNode, self).obj_make_compatible( primitive, str_target_version) However this fail on the OVO side when doing the HVSpec backport at [1]: File "/home/gibizer/upstream/git/nova/nova/tests/unit/objects/test_compute_node.py", line 517, in test_obj_make_compatible_new_archs primitive = compute.obj_to_primitive( File "/home/gibizer/upstream/git/nova/.tox/py39/lib/python3.9/site-packages/fixtures/_fixtures/monkeypatch.py", line 89, in avoid_get return captured_method(*args, **kwargs) File "/home/gibizer/upstream/git/nova/.tox/py39/lib/python3.9/site-packages/oslo_versionedobjects/fixture.py", line 464, in _doit result = self._original_otp(obj, *args, **kwargs) File "/home/gibizer/upstream/git/nova/.tox/py39/lib/python3.9/site-packages/oslo_versionedobjects/base.py", line 562, in obj_to_primitive self.obj_make_compatible_from_manifest(primitive, File "/home/gibizer/upstream/git/nova/.tox/py39/lib/python3.9/site-packages/oslo_versionedobjects/base.py", line 536, in obj_make_compatible_from_manifest return self.obj_make_compatible(primitive, target_version) File "/home/gibizer/upstream/git/nova/nova/objects/compute_node.py", line 158, in obj_make_compatible super(ComputeNode, self).obj_make_compatible( File "/home/gibizer/upstream/git/nova/.tox/py39/lib/python3.9/site-packages/oslo_versionedobjects/base.py", line 523, in obj_make_compatible self._obj_make_obj_compatible(primitive, target_version, key) File "/home/gibizer/upstream/git/nova/.tox/py39/lib/python3.9/site-packages/oslo_versionedobjects/base.py", line 483, in _obj_make_obj_compatible _get_subobject_version(target_version, File "/home/gibizer/upstream/git/nova/.tox/py39/lib/python3.9/site-packages/oslo_versionedobjects/base.py", line 1192, in _get_subobject_version backport_func(child) File "/home/gibizer/upstream/git/nova/.tox/py39/lib/python3.9/site-packages/oslo_versionedobjects/base.py", line 485, in <lambda> lambda ver: _do_subobject_backport( File "/home/gibizer/upstream/git/nova/.tox/py39/lib/python3.9/site-packages/oslo_versionedobjects/base.py", line 1209, in _do_subobject_backport element._obj_primitive_field(primitive[field][i], 'data'), IndexError: list index out of range What happens is that in [1] the primitive is the reduced list but the supported_hv_specs field on the parent obj still having all the original HVSpec OVOs. Basically the implementation assumes that a list in primitives has the same lenght as a list field value. I think the code should iterate the primitive value instead of iterating the obj.field value. [1] https://github.com/openstack/oslo.versionedobjects/blob/25d34d68dafd12fcc4fc843975b8b97994f2bd58/oslo_versionedobjects/base.py#L1207-L1212 In Nova we have the following relationship (reduced the size of the objects for clarity): class Architecture(BaseNovaEnum):     X86_64 = arch.X86_64     RISCV32 = arch.RISCV32 class ArchitectureField(BaseEnumField):     AUTO_TYPE = Architecture() class HVSpec(VersionedObject):     # bumped to 1.3 when RISCV32 introduced     VERSION = '1.3'     fields = {         'arch': fields.ArchitectureField(),         } class ComputeNode(VersionedObject):     # bumped to 1.20 when RISCV32 introduced     VERSION = '1.20'     fields = {         'supported_hv_specs': fields.ListOfObjectsField('HVSpec'),     } Now assume having a ComputeNode object with version 1.20 using RISCV32 in one of the HVSpecs in the supported_hv_specs list and you want to backport that object to ComputeNode 1.19 (HVSpec 1.2). The HVSpec object cannot backport itself as there is no valid old version of the object. So only the parent object, ComputeNode, can do a meaningful backport. It can remove the supported_hv_specs list those HVSpec objects that has too new arch value. So the impl for that is something like:     def obj_make_compatible(self, primitive, target_version):         str_target_version = target_version         target_version = versionutils.convert_version_to_tuple(target_version)         if target_version < (1, 20) and 'supported_hv_specs' in primitive:             primitive['supported_hv_specs'] = [                 hvspec for hvspec in primitive['supported_hv_specs']                 if hvspec['nova_object.data']['arch'] not in                 ('riscv32', 'riscv64')                 ]         # [removed the older backports]         super(ComputeNode, self).obj_make_compatible(             primitive, str_target_version) However this fail on the OVO side when doing the HVSpec backport at [1]:       File "/home/gibizer/upstream/git/nova/nova/tests/unit/objects/test_compute_node.py", line 517, in test_obj_make_compatible_new_archs     primitive = compute.obj_to_primitive(       File "/home/gibizer/upstream/git/nova/.tox/py39/lib/python3.9/site-packages/fixtures/_fixtures/monkeypatch.py", line 89, in avoid_get     return captured_method(*args, **kwargs)       File "/home/gibizer/upstream/git/nova/.tox/py39/lib/python3.9/site-packages/oslo_versionedobjects/fixture.py", line 464, in _doit     result = self._original_otp(obj, *args, **kwargs)       File "/home/gibizer/upstream/git/nova/.tox/py39/lib/python3.9/site-packages/oslo_versionedobjects/base.py", line 562, in obj_to_primitive     self.obj_make_compatible_from_manifest(primitive,       File "/home/gibizer/upstream/git/nova/.tox/py39/lib/python3.9/site-packages/oslo_versionedobjects/base.py", line 536, in obj_make_compatible_from_manifest     return self.obj_make_compatible(primitive, target_version)       File "/home/gibizer/upstream/git/nova/nova/objects/compute_node.py", line 158, in obj_make_compatible     super(ComputeNode, self).obj_make_compatible(       File "/home/gibizer/upstream/git/nova/.tox/py39/lib/python3.9/site-packages/oslo_versionedobjects/base.py", line 523, in obj_make_compatible     self._obj_make_obj_compatible(primitive, target_version, key)       File "/home/gibizer/upstream/git/nova/.tox/py39/lib/python3.9/site-packages/oslo_versionedobjects/base.py", line 483, in _obj_make_obj_compatible     _get_subobject_version(target_version,       File "/home/gibizer/upstream/git/nova/.tox/py39/lib/python3.9/site-packages/oslo_versionedobjects/base.py", line 1192, in _get_subobject_version     backport_func(child)       File "/home/gibizer/upstream/git/nova/.tox/py39/lib/python3.9/site-packages/oslo_versionedobjects/base.py", line 485, in <lambda>     lambda ver: _do_subobject_backport(       File "/home/gibizer/upstream/git/nova/.tox/py39/lib/python3.9/site-packages/oslo_versionedobjects/base.py", line 1209, in _do_subobject_backport     element._obj_primitive_field(primitive[field][i], 'data'),     IndexError: list index out of range What happens is that in [1] the primitive is the reduced list but the supported_hv_specs field on the parent obj still having all the original HVSpec OVOs. Basically the implementation assumes that a list in primitives has the same lenght as a list field value. [1] https://github.com/openstack/oslo.versionedobjects/blob/25d34d68dafd12fcc4fc843975b8b97994f2bd58/oslo_versionedobjects/base.py#L1207-L1212