Add 'delete_host' command in 'nova-manage cell_v2'

Add 'delete_host' command in 'nova-manage cell_v2'.
Add an optional 'force' option in 'nova-manage cell_v2 delete_cell'.
If specifying the 'force' option, a cell can be deleted
even if the cell has hosts.

The 'features' description is changed to the 'fixes'
in the release note.

Conflicts:
        doc/source/cli/nova-manage.rst

Change-Id: I8cd7826c2c03687c6b85731519778f09d542b236
Closes-Bug: #1721179
(cherry-picked from commit f4f17b364e)
(cherry-picked from commit 868ef98762)
This commit is contained in:
Takashi NATSUME 2017-10-08 00:55:50 +09:00
parent b09157d8c1
commit d34222286b
4 changed files with 203 additions and 11 deletions

View File

@ -160,12 +160,14 @@ Nova Cells v2
uuid are shown. Use the --verbose option to see transport url and
database connection details.
``nova-manage cell_v2 delete_cell --cell_uuid <cell_uuid>``
``nova-manage cell_v2 delete_cell [--force] --cell_uuid <cell_uuid>``
Delete an empty cell by the given uuid. Returns 0 if the empty cell is
found and deleted successfully, 1 if a cell with that uuid could not be
found, 2 if host mappings were found for the cell (cell not empty), and
3 if there are instances mapped to the cell (cell not empty).
Delete a cell by the given uuid. Returns 0 if the empty cell is
found and deleted successfully or the cell that has hosts is found and
the cell and the hosts are deleted successfully with ``--force`` option,
1 if a cell with that uuid could not be found, 2 if host mappings were
found for the cell (cell not empty) without ``--force`` option, and 3
if there are instances mapped to the cell (cell not empty).
``nova-manage cell_v2 update_cell --cell_uuid <cell_uuid> [--name <cell_name>] [--transport-url <transport_url>] [--database_connection <database_connection>]``
@ -182,6 +184,14 @@ Nova Cells v2
a running system will NOT result in all nodes immediately using the
new values. Use caution when changing these values.
``nova-manage cell_v2 delete_host --cell_uuid <cell_uuid> --host <host>``
Delete a host by the given host name and the given cell uuid. Returns 0
if the empty host is found and deleted successfully, 1 if a cell with
that uuid could not be found, 2 if a host with that name could not be
found, 3 if a host with that name is not in a cell with that uuid, 4 if
a host with that name has instances (host not empty).
Nova Logs
~~~~~~~~~

View File

@ -1481,15 +1481,26 @@ class CellV2Commands(object):
print(t)
return 0
@args('--force', action='store_true', default=False,
help=_('Delete hosts that belong to the cell as well.'))
@args('--cell_uuid', metavar='<cell_uuid>', dest='cell_uuid',
required=True, help=_('The uuid of the cell to delete.'))
def delete_cell(self, cell_uuid):
def delete_cell(self, cell_uuid, force=False):
"""Delete an empty cell by the given uuid.
If the cell is not found by uuid or it is not empty (it has host or
instance mappings) this command will return a non-zero exit code.
This command will return a non-zero exit code in the following cases.
Returns 0 if the empty cell is found and deleted successfully.
* The cell is not found by uuid.
* It has hosts and force is False.
* It has instance mappings.
If force is True and the cell has host, hosts are deleted as well.
Returns 0 in the following cases.
* The empty cell is found and deleted successfully.
* The cell has hosts and force is True and the cell and the hosts are
deleted successfully.
"""
ctxt = context.get_admin_context()
# Find the CellMapping given the uuid.
@ -1502,7 +1513,7 @@ 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:
if host_mappings and not force:
print(_('There are existing hosts mapped to cell with uuid %s.') %
cell_uuid)
return 2
@ -1515,6 +1526,10 @@ class CellV2Commands(object):
'uuid %s.') % cell_uuid)
return 3
# Delete hosts mapped to the cell.
for host_mapping in host_mappings:
host_mapping.destroy()
# There are no hosts or instances mapped to the cell so delete it.
cell_mapping.destroy()
return 0
@ -1567,6 +1582,52 @@ class CellV2Commands(object):
return 0
@args('--cell_uuid', metavar='<cell_uuid>', dest='cell_uuid',
required=True, help=_('The uuid of the cell.'))
@args('--host', metavar='<host>', dest='host',
required=True, help=_('The host to delete.'))
def delete_host(self, cell_uuid, host):
"""Delete a host in a cell (host mappings) by the given host name
This command will return a non-zero exit code in the following cases.
* The cell is not found by uuid.
* The host is not found by host name.
* The host is not in the cell.
* The host has instances.
Returns 0 if the host is deleted successfully.
"""
ctxt = context.get_admin_context()
# Find the CellMapping given the uuid.
try:
cell_mapping = objects.CellMapping.get_by_uuid(ctxt, cell_uuid)
except exception.CellMappingNotFound:
print(_('Cell with uuid %s was not found.') % cell_uuid)
return 1
try:
host_mapping = objects.HostMapping.get_by_host(ctxt, host)
except exception.HostMappingNotFound:
print(_('The host %s was not found.') % host)
return 2
if host_mapping.cell_mapping.uuid != cell_mapping.uuid:
print(_('The host %(host)s was not found '
'in the cell %(cell_uuid)s.') % {'host': host,
'cell_uuid': cell_uuid})
return 3
with context.target_cell(ctxt, cell_mapping) as cctxt:
instances = objects.InstanceList.get_by_host(cctxt, host)
if instances:
print(_('There are instances on the host %s.') % host)
return 4
host_mapping.destroy()
return 0
CATEGORIES = {
'account': AccountCommands,

View File

@ -1579,7 +1579,7 @@ class CellV2CommandsTestCase(test.NoDBTestCase):
output = self.output.getvalue().strip()
self.assertIn('There are existing instances mapped to cell', output)
def test_delete_cell_success(self):
def test_delete_cell_success_without_host_mappings(self):
"""Tests trying to delete an empty cell."""
cell_uuid = uuidutils.generate_uuid()
ctxt = context.get_admin_context()
@ -1592,6 +1592,28 @@ class CellV2CommandsTestCase(test.NoDBTestCase):
output = self.output.getvalue().strip()
self.assertEqual('', output)
@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):
"""Tests trying to delete a cell with host."""
ctxt = context.get_admin_context()
# create the cell mapping
cm = objects.CellMapping(
context=ctxt, uuid=uuidsentinel.cell1,
database_connection='fake:///db', transport_url='fake:///mq')
cm.create()
# create a host mapping in this cell
hm = objects.HostMapping(
context=ctxt, host='fake-host', cell_mapping=cm)
hm.create()
self.assertEqual(0, self.commands.delete_cell(uuidsentinel.cell1,
force=True))
output = self.output.getvalue().strip()
self.assertEqual('', output)
mock_hm_destroy.assert_called_once_with()
mock_cell_destroy.assert_called_once_with()
def test_update_cell_not_found(self):
self.assertEqual(1, self.commands.update_cell(
uuidsentinel.cell1, 'foo', 'fake://new', 'fake:///new'))
@ -1640,6 +1662,97 @@ class CellV2CommandsTestCase(test.NoDBTestCase):
output = self.output.getvalue().strip()
self.assertEqual('', output)
def test_delete_host_cell_not_found(self):
"""Tests trying to delete a host but a specified cell is not found."""
self.assertEqual(1, self.commands.delete_host(uuidsentinel.cell1,
'fake-host'))
output = self.output.getvalue().strip()
self.assertEqual(
'Cell with uuid %s was not found.' % uuidsentinel.cell1, output)
def test_delete_host_host_not_found(self):
"""Tests trying to delete a host but the host is not found."""
ctxt = context.get_admin_context()
# create the cell mapping
cm = objects.CellMapping(
context=ctxt, uuid=uuidsentinel.cell1,
database_connection='fake:///db', transport_url='fake:///mq')
cm.create()
self.assertEqual(2, self.commands.delete_host(uuidsentinel.cell1,
'fake-host'))
output = self.output.getvalue().strip()
self.assertEqual('The host fake-host was not found.', output)
def test_delete_host_host_not_in_cell(self):
"""Tests trying to delete a host
but the host does not belongs to a specified cell.
"""
ctxt = context.get_admin_context()
# create the cell mapping
cm1 = objects.CellMapping(
context=ctxt, uuid=uuidsentinel.cell1,
database_connection='fake:///db', transport_url='fake:///mq')
cm1.create()
cm2 = objects.CellMapping(
context=ctxt, uuid=uuidsentinel.cell2,
database_connection='fake:///db', transport_url='fake:///mq')
cm2.create()
# create a host mapping in another cell
hm = objects.HostMapping(
context=ctxt, host='fake-host', cell_mapping=cm2)
hm.create()
self.assertEqual(3, self.commands.delete_host(uuidsentinel.cell1,
'fake-host'))
output = self.output.getvalue().strip()
self.assertEqual(('The host fake-host was not found in the cell %s.' %
uuidsentinel.cell1), output)
@mock.patch.object(objects.InstanceList, 'get_by_host')
def test_delete_host_instances_exist(self, mock_get_by_host):
"""Tests trying to delete a host but the host has instances."""
ctxt = context.get_admin_context()
# create the cell mapping
cm1 = objects.CellMapping(
context=ctxt, uuid=uuidsentinel.cell1,
database_connection='fake:///db', transport_url='fake:///mq')
cm1.create()
# create a host mapping in the cell
hm = objects.HostMapping(
context=ctxt, host='fake-host', cell_mapping=cm1)
hm.create()
mock_get_by_host.return_value = [objects.Instance(
ctxt, uuid=uuidsentinel.instance)]
self.assertEqual(4, self.commands.delete_host(uuidsentinel.cell1,
'fake-host'))
output = self.output.getvalue().strip()
self.assertEqual('There are instances on the host fake-host.', output)
mock_get_by_host.assert_called_once_with(
test.MatchType(context.RequestContext), 'fake-host')
@mock.patch.object(objects.InstanceList, 'get_by_host',
return_value=[])
@mock.patch.object(objects.HostMapping, 'destroy')
def test_delete_host_success(self, mock_destroy, mock_get_by_host):
"""Tests trying to delete a host that has not instances."""
ctxt = context.get_admin_context()
# create the cell mapping
cm1 = objects.CellMapping(
context=ctxt, uuid=uuidsentinel.cell1,
database_connection='fake:///db', transport_url='fake:///mq')
cm1.create()
# create a host mapping in the cell
hm = objects.HostMapping(
context=ctxt, host='fake-host', cell_mapping=cm1)
hm.create()
self.assertEqual(0, self.commands.delete_host(uuidsentinel.cell1,
'fake-host'))
output = self.output.getvalue().strip()
self.assertEqual('', output)
mock_get_by_host.assert_called_once_with(
test.MatchType(context.RequestContext), 'fake-host')
mock_destroy.assert_called_once_with()
class TestNovaManageMain(test.NoDBTestCase):
"""Tests the nova-manage:main() setup code."""

View File

@ -0,0 +1,8 @@
---
fixes:
- |
The ``delete_host`` command has been added in ``nova-manage cell_v2``
to delete a host from a cell (host mappings).
The ``force`` option has been added in ``nova-manage cell_v2 delete_cell``.
If the ``force`` option is specified, a cell can be deleted
even if the cell has hosts.