diff --git a/doc/source/cli/nova-manage.rst b/doc/source/cli/nova-manage.rst index ccca7f9b7ef7..40bb0503e7d0 100644 --- a/doc/source/cli/nova-manage.rst +++ b/doc/source/cli/nova-manage.rst @@ -153,14 +153,16 @@ Nova Cells v2 error before they have been scheduled. Returns 0 if cell0 is created successfully or already setup. -``nova-manage cell_v2 map_instances --cell_uuid [--max-count ]`` +``nova-manage cell_v2 map_instances --cell_uuid [--max-count ] [--reset]`` Map instances to the provided cell. Instances in the nova database will be queried from oldest to newest and mapped to the provided cell. A max_count can be set on the number of instance to map in a single run. Repeated runs of the command will start from where the last run finished - so it is not necessary to increase max-count to finish. Returns 0 if all - instances have been mapped, and 1 if there are still instances to be - mapped. + so it is not necessary to increase max-count to finish. A reset option + can be passed which will reset the marker, thus making the command start + from the beginning as opposed to the default behavior of starting from + where the last run finished. Returns 0 if all instances have been mapped, + and 1 if there are still instances to be mapped. If ``--max-count`` is not specified, all instances in the cell will be mapped in batches of 50. If you have a large number of instances, consider diff --git a/nova/cmd/manage.py b/nova/cmd/manage.py index 9a5975541d17..28211c671f08 100644 --- a/nova/cmd/manage.py +++ b/nova/cmd/manage.py @@ -1130,7 +1130,11 @@ class CellV2Commands(object): 'in the cell will be mapped in batches of 50. If you have a ' 'large number of instances, consider specifying a custom value ' 'and run the command until it exits with 0.') - def map_instances(self, cell_uuid, max_count=None): + @args('--reset', action='store_true', dest='reset_marker', + help='The command will start from the beginning as opposed to the ' + 'default behavior of starting from where the last run ' + 'finished') + def map_instances(self, cell_uuid, max_count=None, reset_marker=None): """Map instances into the provided cell. Instances in the nova database of the provided cell (nova database @@ -1138,8 +1142,11 @@ class CellV2Commands(object): oldest to newest and if unmapped, will be mapped to the provided cell. A max-count can be set on the number of instance to map in a single run. Repeated runs of the command will start from where the last run - finished so it is not necessary to increase max-count to finish. An - exit code of 0 indicates that all instances have been mapped. + finished so it is not necessary to increase max-count to finish. A + reset option can be passed which will reset the marker, thus making the + command start from the beginning as opposed to the default behavior of + starting from where the last run finished. An exit code of 0 indicates + that all instances have been mapped. """ if max_count is not None: @@ -1169,6 +1176,8 @@ class CellV2Commands(object): else: # There should be only one here marker = marker_mapping[0].instance_uuid.replace(' ', '-') + if reset_marker: + marker = None marker_mapping[0].destroy() next_marker = True diff --git a/nova/tests/unit/test_nova_manage.py b/nova/tests/unit/test_nova_manage.py index e4e14c7cf4b1..e668d1abce71 100644 --- a/nova/tests/unit/test_nova_manage.py +++ b/nova/tests/unit/test_nova_manage.py @@ -1272,6 +1272,73 @@ class CellV2CommandsTestCase(test.NoDBTestCase): test.MatchType(context.RequestContext), test.MatchObjPrims(cell_mapping)) + @mock.patch.object(context, 'target_cell') + def test_map_instances_marker_reset(self, mock_target_cell): + ctxt = context.RequestContext('fake-user', 'fake_project') + cell_uuid = uuidutils.generate_uuid() + cell_mapping = objects.CellMapping( + ctxt, uuid=cell_uuid, name='fake', + transport_url='fake://', database_connection='fake://') + cell_mapping.create() + mock_target_cell.return_value.__enter__.return_value = ctxt + instance_uuids = [] + for i in range(5): + uuid = uuidutils.generate_uuid() + instance_uuids.append(uuid) + objects.Instance(ctxt, project_id=ctxt.project_id, + uuid=uuid).create() + + # Maps first three instances. + ret = self.commands.map_instances(cell_uuid, max_count=3) + self.assertEqual(1, ret) + + # Verifying that the marker is now based on third instance + # i.e the position of the marker. + inst_mappings = objects.InstanceMappingList.get_by_project_id(ctxt, + 'INSTANCE_MIGRATION_MARKER') + marker = inst_mappings[0].instance_uuid.replace(' ', '-') + self.assertEqual(instance_uuids[2], marker) + + # Now calling reset with map_instances max_count=2 would reset + # the marker as expected and start map_instances from the beginning. + # This implies we end up finding the marker based on second instance. + ret = self.commands.map_instances(cell_uuid, max_count=2, + reset_marker=True) + self.assertEqual(1, ret) + + inst_mappings = objects.InstanceMappingList.get_by_project_id(ctxt, + 'INSTANCE_MIGRATION_MARKER') + marker = inst_mappings[0].instance_uuid.replace(' ', '-') + self.assertEqual(instance_uuids[1], marker) + + # Maps 4th instance using the marker (3rd is already mapped). + ret = self.commands.map_instances(cell_uuid, max_count=2) + self.assertEqual(1, ret) + + # Verifying that the marker is now based on fourth instance + # i.e the position of the marker. + inst_mappings = objects.InstanceMappingList.get_by_project_id(ctxt, + 'INSTANCE_MIGRATION_MARKER') + marker = inst_mappings[0].instance_uuid.replace(' ', '-') + self.assertEqual(instance_uuids[3], marker) + + # Maps first four instances (all four duplicate entries which + # are already present from previous calls) + ret = self.commands.map_instances(cell_uuid, max_count=4, + reset_marker=True) + self.assertEqual(1, ret) + + # Verifying that the marker is still based on fourth instance + # i.e the position of the marker. + inst_mappings = objects.InstanceMappingList.get_by_project_id(ctxt, + 'INSTANCE_MIGRATION_MARKER') + marker = inst_mappings[0].instance_uuid.replace(' ', '-') + self.assertEqual(instance_uuids[3], marker) + + # Maps the 5th instance. + ret = self.commands.map_instances(cell_uuid) + self.assertEqual(0, ret) + def test_map_instances_validate_cell_uuid(self): # create a random cell_uuid which is invalid cell_uuid = uuidutils.generate_uuid() diff --git a/releasenotes/notes/reset-marker-for-map_instances-0c841ef45e3adc7b.yaml b/releasenotes/notes/reset-marker-for-map_instances-0c841ef45e3adc7b.yaml new file mode 100644 index 000000000000..6fa433e0b552 --- /dev/null +++ b/releasenotes/notes/reset-marker-for-map_instances-0c841ef45e3adc7b.yaml @@ -0,0 +1,8 @@ +--- +features: + - | + Currently the ``nova-manage cell_v2 map_instances`` command uses a marker + setup by which repeated runs of the command will start from where the last + run finished, by default. A ``--reset`` option has been added to this command + by which the marker can be reset and users can start the process from the + beginning if needed, instead of the default behavior.