@@ -363,6 +363,7 @@ def test_obj_from_db_obj(self):
363363 fake_topo_obj = copy .deepcopy (fake_topo_obj_w_cell_v1_4 )
364364 for cell in fake_topo_obj .cells :
365365 cell .cpu_policy = objects .fields .CPUAllocationPolicy .DEDICATED
366+ cell .VERSION = '1.4'
366367
367368 numa_topology = objects .InstanceNUMATopology .obj_from_db_obj (
368369 self .context , fake_instance_uuid , fake_topo_obj ._to_json ())
@@ -402,6 +403,7 @@ def test_obj_from_db_obj_no_pinning(self):
402403 fake_topo_obj = copy .deepcopy (fake_topo_obj_w_cell_v1_4 )
403404 for cell in fake_topo_obj .cells :
404405 cell .cpu_policy = objects .fields .CPUAllocationPolicy .SHARED
406+ cell .VERSION = '1.4'
405407
406408 numa_topology = objects .InstanceNUMATopology .obj_from_db_obj (
407409 self .context , fake_instance_uuid , fake_topo_obj ._to_json ())
@@ -412,7 +414,7 @@ def test_obj_from_db_obj_no_pinning(self):
412414 self .assertEqual (topo_cell .cpuset , obj_cell .cpuset )
413415 self .assertEqual (set (), obj_cell .pcpuset )
414416
415- def test__migrate_legacy_dedicated_instance_cpuset (self ):
417+ def test__migrate_legacy_dedicated_instance_cpuset_dedicate_1_4 (self ):
416418 # Create a topology with a cell on latest version. Would be nice
417419 # to create one the old 1.4 cell version directly but that is only
418420 # possible indirectly as done below.
@@ -446,7 +448,118 @@ def test__migrate_legacy_dedicated_instance_cpuset(self):
446448 # pcpuset
447449 self .assertEqual (set (), topo_loaded .cells [0 ].cpuset )
448450 self .assertEqual ({0 , 1 }, topo_loaded .cells [0 ].pcpuset )
449- # and the version is bumped to 1.6
451+ # and the version is updated to 1.6
452+ self .assertEqual ('1.6' , topo_loaded .cells [0 ].VERSION )
453+
454+ def test__migrate_legacy_dedicated_instance_cpuset_shared_1_4 (self ):
455+ # Create a topology with a cell on latest version. Would be nice
456+ # to create one the old 1.4 cell version directly but that is only
457+ # possible indirectly as done below.
458+ topo = objects .InstanceNUMATopology (
459+ instance_uuid = fake_instance_uuid ,
460+ cells = [
461+ objects .InstanceNUMACell (id = 0 , cpuset = {0 , 1 }, pcpuset = set ()),
462+ ])
463+ topo .cells [0 ].cpu_policy = objects .fields .CPUAllocationPolicy .SHARED
464+
465+ # Use the builtin backlevelling logic to pull it back to old cell
466+ # version
467+ topo_with_cell_1_4 = topo .obj_to_primitive (
468+ target_version = '1.3' , version_manifest = {'InstanceNUMACell' : '1.4' })
469+
470+ # Just check that the backlevelling works, and we have a cell with
471+ # version and data on 1.4 level
472+ cell_1_4_primitive = topo_with_cell_1_4 ['nova_object.data' ]['cells' ][0 ]
473+ self .assertEqual ('1.4' , cell_1_4_primitive ['nova_object.version' ])
474+ self .assertEqual (
475+ (0 , 1 ), cell_1_4_primitive ['nova_object.data' ]['cpuset' ])
476+ self .assertNotIn ('pcpuset' , cell_1_4_primitive ['nova_object.data' ])
477+
478+ # Now simulate that such old data is loaded from the DB and migrated
479+ # from 1.4 to 1.6 by the data migration
480+ topo_loaded = objects .InstanceNUMATopology .obj_from_db_obj (
481+ self .context , fake_instance_uuid ,
482+ jsonutils .dumps (topo_with_cell_1_4 ))
483+
484+ # In place data migration did not move the data as the cpu_policy is
485+ # shared
486+ self .assertEqual ({0 , 1 }, topo_loaded .cells [0 ].cpuset )
487+ self .assertEqual (set (), topo_loaded .cells [0 ].pcpuset )
488+ # but the version is updated to 1.6
489+ self .assertEqual ('1.6' , topo_loaded .cells [0 ].VERSION )
490+
491+ def test__migrate_legacy_dedicated_instance_cpuset_dedicated_half_migrated (
492+ self
493+ ):
494+ # Before the fix for https://bugs.launchpad.net/nova/+bug/2097360
495+ # landed Nova only half migrated the InstanceNUMACell from 1.4 to 1.6
496+ # by doing the data move but not updating the version string in the DB.
497+ # This test case ensures that if such migration happened before the fix
498+ # was deployed then Nova fixed the DB content on the next load as well.
499+
500+ # Create a topology with a cell on latest version. Would be nice
501+ # to create one the old 1.4 cell version directly but that is only
502+ # possible indirectly as done below.
503+ topo = objects .InstanceNUMATopology (
504+ instance_uuid = fake_instance_uuid ,
505+ cells = [
506+ objects .InstanceNUMACell (id = 0 , cpuset = set (), pcpuset = {0 , 1 }),
507+ ])
508+ topo .cells [0 ].cpu_policy = objects .fields .CPUAllocationPolicy .DEDICATED
509+
510+ # simulate the half done migration by pulling back the version string
511+ # in the primitive form to 1.4 while keeping the data on 1.6 format.
512+ topo_primitive = topo .obj_to_primitive ()
513+ cell_primitive = topo_primitive ['nova_object.data' ]['cells' ][0 ]
514+ self .assertEqual ('1.6' , cell_primitive ['nova_object.version' ])
515+ cell_primitive ['nova_object.version' ] = '1.4'
516+
517+ topo_loaded = objects .InstanceNUMATopology .obj_from_db_obj (
518+ self .context , fake_instance_uuid , jsonutils .dumps (topo_primitive ))
519+
520+ # the data did not change
521+ self .assertEqual (set (), topo_loaded .cells [0 ].cpuset )
522+ self .assertEqual ({0 , 1 }, topo_loaded .cells [0 ].pcpuset )
523+ # but the version is updated to 1.6
524+ self .assertEqual ('1.6' , topo_loaded .cells [0 ].VERSION )
525+
526+ def test__migrate_legacy_dedicated_instance_cpuset_mixed_1_4 (self ):
527+ # Before the fix for https://bugs.launchpad.net/nova/+bug/2097360
528+ # landed Nova only half migrated the InstanceNUMACell from 1.4 to 1.6
529+ # by doing the data move but not updating the version string in the DB.
530+ # After this the instance is resized to flavor with mixed cpu_policy
531+ # then there is a good chance that the DB has version string 1.4 but
532+ # the data is on 1.6 format with the cpu_policy set to mixed.
533+ # This test case ensures that if such situation happened before the fix
534+ # was deployed then Nova fixed the DB content on the next load as well.
535+
536+ # Create a topology with a cell on latest version. Would be nice
537+ # to create one the old 1.4 cell version directly but that is only
538+ # possible indirectly as done below.
539+ topo = objects .InstanceNUMATopology (
540+ instance_uuid = fake_instance_uuid ,
541+ cells = [
542+ objects .InstanceNUMACell (id = 0 , cpuset = {0 , 1 }, pcpuset = {2 , 3 }),
543+ ])
544+ topo .cells [0 ].cpu_policy = objects .fields .CPUAllocationPolicy .MIXED
545+
546+ # simulate the half done migration by pulling back the version string
547+ # in the primitive form to 1.4 while keeping the data on 1.6 format.
548+ topo_primitive = topo .obj_to_primitive ()
549+ cell_primitive = topo_primitive ['nova_object.data' ]['cells' ][0 ]
550+ self .assertEqual ('1.6' , cell_primitive ['nova_object.version' ])
551+ cell_primitive ['nova_object.version' ] = '1.4'
552+
553+ topo_loaded = objects .InstanceNUMATopology .obj_from_db_obj (
554+ self .context , fake_instance_uuid , jsonutils .dumps (topo_primitive ))
555+
556+ # the data did not change
557+ self .assertEqual ({0 , 1 }, topo_loaded .cells [0 ].cpuset )
558+ self .assertEqual ({2 , 3 }, topo_loaded .cells [0 ].pcpuset )
559+ self .assertEqual (
560+ objects .fields .CPUAllocationPolicy .MIXED ,
561+ topo_loaded .cells [0 ].cpu_policy )
562+ # but the version is updated to 1.6
450563 self .assertEqual ('1.6' , topo_loaded .cells [0 ].VERSION )
451564
452565
0 commit comments