diff --git a/nova/cmd/manage.py b/nova/cmd/manage.py index 2cb7aed90d7d..1cdc81f4d031 100644 --- a/nova/cmd/manage.py +++ b/nova/cmd/manage.py @@ -1687,10 +1687,16 @@ class CellV2Commands(object): # Check to see if there are any HostMappings for this cell. host_mappings = objects.HostMappingList.get_by_cell_id( ctxt, cell_mapping.id) - if host_mappings and not force: - print(_('There are existing hosts mapped to cell with uuid %s.') % - cell_uuid) - return 2 + nodes = [] + if host_mappings: + if not force: + print(_('There are existing hosts mapped to cell with uuid ' + '%s.') % cell_uuid) + return 2 + # We query for the compute nodes in the cell, + # so that they can be unmapped. + with context.target_cell(ctxt, cell_mapping) as cctxt: + nodes = objects.ComputeNodeList.get_all(cctxt) # Check to see if there are any InstanceMappings for this cell. instance_mappings = objects.InstanceMappingList.get_by_cell_id( @@ -1700,6 +1706,12 @@ class CellV2Commands(object): 'uuid %s.') % cell_uuid) return 3 + # Unmap the compute nodes so that they can be discovered + # again in future, if needed. + for node in nodes: + node.mapped = 0 + node.save() + # Delete hosts mapped to the cell. for host_mapping in host_mappings: host_mapping.destroy() diff --git a/nova/tests/functional/test_nova_manage.py b/nova/tests/functional/test_nova_manage.py index 08cde5fbeae7..4bb21bb3bc22 100644 --- a/nova/tests/functional/test_nova_manage.py +++ b/nova/tests/functional/test_nova_manage.py @@ -12,6 +12,7 @@ from nova.cmd import manage from nova import context +from nova import exception from nova import objects from nova import test @@ -317,3 +318,33 @@ class NovaManageCellV2Test(test.TestCase): cns = objects.ComputeNodeList.get_all(self.context) self.assertEqual(1, len(cns)) self.assertEqual(0, cns[0].mapped) + + def test_delete_cell_force_unmaps_computes(self): + cells = objects.CellMappingList.get_all(self.context) + + self.commands.discover_hosts() + + # We should have one host mapping + objects.HostMapping.get_by_host(self.context, 'fake-host1') + + # We should have one mapped node + cns = objects.ComputeNodeList.get_all(self.context) + self.assertEqual(1, len(cns)) + self.assertEqual(1, cns[0].mapped) + + for cell in cells: + res = self.commands.delete_cell(cell.uuid, force=True) + self.assertEqual(0, res) + + # The host mapping should be deleted since the force option is used + self.assertRaises(exception.HostMappingNotFound, + objects.HostMapping.get_by_host, self.context, 'fake-host1') + + # All our cells should be deleted + cells = objects.CellMappingList.get_all(self.context) + self.assertEqual(0, len(cells)) + + # Our node should now be unmapped + cns = objects.ComputeNodeList.get_all(self.context) + self.assertEqual(1, len(cns)) + self.assertEqual(0, cns[0].mapped) diff --git a/nova/tests/unit/test_nova_manage.py b/nova/tests/unit/test_nova_manage.py index cf2933891408..d145c19226ce 100644 --- a/nova/tests/unit/test_nova_manage.py +++ b/nova/tests/unit/test_nova_manage.py @@ -1574,7 +1574,8 @@ class CellV2CommandsTestCase(test.NoDBTestCase): self.assertEqual('Cell with uuid %s was not found.' % cell_uuid, output) - def test_delete_cell_host_mappings_exist(self): + @mock.patch.object(objects.ComputeNodeList, 'get_all') + def test_delete_cell_host_mappings_exist(self, mock_get_cn): """Tests trying to delete a cell which has host mappings.""" cell_uuid = uuidutils.generate_uuid() ctxt = context.get_admin_context() @@ -1587,6 +1588,7 @@ class CellV2CommandsTestCase(test.NoDBTestCase): hm = objects.HostMapping( context=ctxt, host='fake-host', cell_mapping=cm) hm.create() + mock_get_cn.return_value = [] self.assertEqual(2, self.commands.delete_cell(cell_uuid)) output = self.output.getvalue().strip() self.assertIn('There are existing hosts mapped to cell', output) @@ -1622,10 +1624,11 @@ class CellV2CommandsTestCase(test.NoDBTestCase): output = self.output.getvalue().strip() self.assertEqual('', output) + @mock.patch.object(objects.ComputeNodeList, 'get_all') @mock.patch.object(objects.HostMapping, 'destroy') @mock.patch.object(objects.CellMapping, 'destroy') def test_delete_cell_success_with_host_mappings(self, mock_cell_destroy, - mock_hm_destroy): + mock_hm_destroy, mock_get_cn): """Tests trying to delete a cell with host.""" ctxt = context.get_admin_context() # create the cell mapping @@ -1637,6 +1640,7 @@ class CellV2CommandsTestCase(test.NoDBTestCase): hm = objects.HostMapping( context=ctxt, host='fake-host', cell_mapping=cm) hm.create() + mock_get_cn.return_value = [] self.assertEqual(0, self.commands.delete_cell(uuidsentinel.cell1, force=True)) output = self.output.getvalue().strip()