Merge "New discover command to add new hosts to a cell"
This commit is contained in:
@@ -1478,6 +1478,39 @@ class CellV2Commands(object):
|
||||
mapping.cell_mapping.uuid))
|
||||
return 0
|
||||
|
||||
@args('--cell_uuid', metavar='<cell_uuid>', dest='cell_uuid',
|
||||
help='If provided only this cell will be searched for new hosts to '
|
||||
'map.')
|
||||
def discover_hosts(self, cell_uuid=None):
|
||||
"""Searches cells, or a single cell, and maps found hosts.
|
||||
|
||||
When a new host is added to a deployment it will add a service entry
|
||||
to the db it's configured to use. This command will check the db for
|
||||
each cell, or a single one if passed in, and map any hosts which are
|
||||
not currently mapped. If a host is already mapped nothing will be done.
|
||||
"""
|
||||
ctxt = context.RequestContext()
|
||||
|
||||
if cell_uuid:
|
||||
cell_mappings = [objects.CellMapping.get_by_uuid(ctxt, cell_uuid)]
|
||||
else:
|
||||
cell_mappings = objects.CellMappingList.get_all(context)
|
||||
|
||||
for cell_mapping in cell_mappings:
|
||||
# TODO(alaski): Factor this into helper method on CellMapping
|
||||
if cell_mapping.uuid == cell_mapping.CELL0_UUID:
|
||||
continue
|
||||
with context.target_cell(ctxt, cell_mapping):
|
||||
compute_nodes = objects.ComputeNodeList.get_all(ctxt)
|
||||
for compute in compute_nodes:
|
||||
try:
|
||||
objects.HostMapping.get_by_host(ctxt, compute.host)
|
||||
except exception.HostMappingNotFound:
|
||||
host_mapping = objects.HostMapping(
|
||||
ctxt, host=compute.host,
|
||||
cell_mapping=cell_mapping)
|
||||
host_mapping.create()
|
||||
|
||||
|
||||
CATEGORIES = {
|
||||
'account': AccountCommands,
|
||||
|
@@ -1210,3 +1210,129 @@ class CellV2CommandsTestCase(test.TestCase):
|
||||
# and reasonably verify that path
|
||||
self.assertEqual(1, self.commands.verify_instance(uuidsentinel.foo,
|
||||
quiet=True))
|
||||
|
||||
def _return_compute_nodes(self, ctxt, num=1):
|
||||
nodes = []
|
||||
for i in range(num):
|
||||
nodes.append(objects.ComputeNode(ctxt,
|
||||
uuid=uuidutils.generate_uuid(),
|
||||
host='fake',
|
||||
vcpus=1,
|
||||
memory_mb=1,
|
||||
local_gb=1,
|
||||
vcpus_used=0,
|
||||
memory_mb_used=0,
|
||||
local_gb_used=0,
|
||||
hypervisor_type='',
|
||||
hypervisor_version=1,
|
||||
cpu_info=''))
|
||||
return nodes
|
||||
|
||||
@mock.patch.object(context, 'target_cell')
|
||||
@mock.patch.object(objects, 'HostMapping')
|
||||
@mock.patch.object(objects.ComputeNodeList, 'get_all')
|
||||
@mock.patch.object(objects.CellMappingList, 'get_all')
|
||||
@mock.patch.object(objects.CellMapping, 'get_by_uuid')
|
||||
def test_discover_hosts_single_cell(self, mock_cell_mapping_get_by_uuid,
|
||||
mock_cell_mapping_get_all,
|
||||
mock_compute_get_all,
|
||||
mock_host_mapping, mock_target_cell):
|
||||
host_mock = mock.MagicMock()
|
||||
mock_host_mapping.return_value = host_mock
|
||||
exc = exception.HostMappingNotFound(name='fake')
|
||||
mock_host_mapping.get_by_host.side_effect = exc
|
||||
|
||||
ctxt = context.RequestContext()
|
||||
|
||||
compute_nodes = self._return_compute_nodes(ctxt)
|
||||
mock_compute_get_all.return_value = objects.ComputeNodeList(
|
||||
objects=compute_nodes)
|
||||
|
||||
cell_mapping = objects.CellMapping(uuid=uuidutils.generate_uuid())
|
||||
mock_cell_mapping_get_by_uuid.return_value = cell_mapping
|
||||
|
||||
self.commands.discover_hosts(cell_uuid=cell_mapping.uuid)
|
||||
|
||||
mock_target_cell.assert_called_once_with(
|
||||
test.MatchType(context.RequestContext), cell_mapping)
|
||||
host_mock.create.assert_called_once()
|
||||
mock_host_mapping.assert_called_once_with(
|
||||
test.MatchType(context.RequestContext), host='fake',
|
||||
cell_mapping=cell_mapping)
|
||||
mock_cell_mapping_get_all.assert_not_called()
|
||||
|
||||
@mock.patch.object(context, 'target_cell')
|
||||
@mock.patch.object(objects, 'HostMapping')
|
||||
@mock.patch.object(objects.ComputeNodeList, 'get_all')
|
||||
@mock.patch.object(objects.CellMappingList, 'get_all')
|
||||
@mock.patch.object(objects.CellMapping, 'get_by_uuid')
|
||||
def test_discover_hosts_single_cell_no_new_hosts(
|
||||
self, mock_cell_mapping_get_by_uuid, mock_cell_mapping_get_all,
|
||||
mock_compute_get_all, mock_host_mapping, mock_target_cell):
|
||||
|
||||
host_mock = mock.MagicMock()
|
||||
mock_host_mapping.return_value = host_mock
|
||||
|
||||
ctxt = context.RequestContext()
|
||||
|
||||
compute_nodes = self._return_compute_nodes(ctxt)
|
||||
mock_compute_get_all.return_value = objects.ComputeNodeList(
|
||||
objects=compute_nodes)
|
||||
|
||||
cell_mapping = objects.CellMapping(uuid=uuidutils.generate_uuid())
|
||||
mock_cell_mapping_get_by_uuid.return_value = cell_mapping
|
||||
|
||||
self.commands.discover_hosts(cell_uuid=cell_mapping.uuid)
|
||||
|
||||
mock_target_cell.assert_called_once_with(
|
||||
test.MatchType(context.RequestContext), cell_mapping)
|
||||
mock_host_mapping.assert_not_called()
|
||||
mock_cell_mapping_get_all.assert_not_called()
|
||||
|
||||
@mock.patch.object(context, 'target_cell')
|
||||
@mock.patch.object(objects, 'HostMapping')
|
||||
@mock.patch.object(objects.ComputeNodeList, 'get_all')
|
||||
@mock.patch.object(objects.CellMappingList, 'get_all')
|
||||
@mock.patch.object(objects.CellMapping, 'get_by_uuid')
|
||||
def test_discover_hosts_multiple_cells(self, mock_cell_mapping_get_by_uuid,
|
||||
mock_cell_mapping_get_all,
|
||||
mock_compute_get_all,
|
||||
mock_host_mapping,
|
||||
mock_target_cell):
|
||||
host_mock = mock.MagicMock()
|
||||
mock_host_mapping.return_value = host_mock
|
||||
exc = exception.HostMappingNotFound(name='fake')
|
||||
mock_host_mapping.get_by_host.side_effect = exc
|
||||
|
||||
ctxt = context.RequestContext()
|
||||
|
||||
compute_nodes = self._return_compute_nodes(ctxt, num=2)
|
||||
mock_compute_get_all.side_effect = (
|
||||
objects.ComputeNodeList(objects=compute_nodes[1:]),
|
||||
objects.ComputeNodeList(objects=compute_nodes[:1]))
|
||||
|
||||
cell_mapping1 = objects.CellMapping(uuid=uuidutils.generate_uuid())
|
||||
cell_mapping2 = objects.CellMapping(uuid=uuidutils.generate_uuid())
|
||||
mock_cell_mapping_get_all.return_value = objects.CellMappingList(
|
||||
objects=[cell_mapping1, cell_mapping2])
|
||||
|
||||
self.commands.discover_hosts()
|
||||
|
||||
self.assertEqual(2, mock_target_cell.call_count)
|
||||
target_calls = [mock.call(test.MatchType(context.RequestContext),
|
||||
cell_mapping1),
|
||||
mock.call(test.MatchType(context.RequestContext),
|
||||
cell_mapping2)]
|
||||
self.assertEqual(target_calls, mock_target_cell.call_args_list)
|
||||
|
||||
self.assertEqual(2, host_mock.create.call_count)
|
||||
self.assertEqual(2, mock_host_mapping.call_count)
|
||||
host_mapping_calls = [mock.call(test.MatchType(context.RequestContext),
|
||||
host=compute_nodes[0].host,
|
||||
cell_mapping=cell_mapping1),
|
||||
mock.call(test.MatchType(context.RequestContext),
|
||||
host=compute_nodes[1].host,
|
||||
cell_mapping=cell_mapping2)]
|
||||
self.assertEqual(host_mapping_calls, mock_host_mapping.call_args_list)
|
||||
|
||||
mock_cell_mapping_get_by_uuid.assert_not_called()
|
||||
|
@@ -0,0 +1,25 @@
|
||||
---
|
||||
features:
|
||||
- A new nova-manage command has been added to discover any new hosts that are
|
||||
added to a cell. If a deployment has migrated to cellsv2 using either the
|
||||
simple_cell_setup or the map_cell0/map_cell_and_hosts/map_instances combo
|
||||
then anytime a new host is added to a cell this new
|
||||
"nova-manage cell_v2 discover_hosts" needs to be run before instances can
|
||||
be booted on that host. If multiple hosts are added at one time the command
|
||||
only needs to be run one time to discover all of them.
|
||||
|
||||
Please note that adding a host to a cell and not running this command could
|
||||
lead to build failures/reschedules if that host is selected by the
|
||||
scheduler. The discover_hosts command is necessary to route requests to the
|
||||
host but is not necessary in order for the scheduler to be aware of the
|
||||
host. In order to avoid that it is advised that new compute hosts are
|
||||
disabled until the discover command has been run.
|
||||
issues:
|
||||
- If a deployer has updated their deployment to using cellsv2 using either
|
||||
the simple_cell_setup or the map_cell0/map_cell_and_hosts/map_instances
|
||||
combo and they add a new host into the cell it may cause build failures
|
||||
or reschedules until they run the "nova-manage cell_v2 discover_hosts"
|
||||
command. This is because the scheduler will quickly become aware of the
|
||||
host but nova-api will not know how to route the request to that host until
|
||||
it has been "discovered". In order to avoid that it is advised that
|
||||
new computes are disabled until the discover command has been run.
|
Reference in New Issue
Block a user