Merge "Add get_compute_nodes_by_host_or_node()"

This commit is contained in:
Zuul 2019-05-01 21:32:15 +00:00 committed by Gerrit Code Review
commit 5efce384fc
8 changed files with 227 additions and 2 deletions

View File

@ -244,6 +244,21 @@ def compute_node_get_by_host_and_nodename(context, host, nodename):
return IMPL.compute_node_get_by_host_and_nodename(context, host, nodename)
def compute_node_get_by_nodename(context, hypervisor_hostname):
"""Get a compute node by hypervisor_hostname.
:param context: The security context (admin)
:param hypervisor_hostname: Name of the node
:returns: Dictionary-like object containing properties of the compute node,
including its statistics
Raises ComputeHostNotFound if hypervisor_hostname with the given name
doesn't exist.
"""
return IMPL.compute_node_get_by_nodename(context, hypervisor_hostname)
def compute_node_get_all(context):
"""Get all computeNodes.

View File

@ -639,6 +639,15 @@ def compute_node_get_by_host_and_nodename(context, host, nodename):
return results[0]
@pick_context_manager_reader
def compute_node_get_by_nodename(context, hypervisor_hostname):
results = _compute_node_fetchall(context,
{"hypervisor_hostname": hypervisor_hostname})
if not results:
raise exception.ComputeHostNotFound(host=hypervisor_hostname)
return results[0]
@pick_context_manager_reader_allow_async
def compute_node_get_all_by_host(context, host):
results = _compute_node_fetchall(context, {"host": host})

View File

@ -53,7 +53,8 @@ class ComputeNode(base.NovaPersistentObject, base.NovaObject):
# Version 1.16: Added disk_allocation_ratio
# Version 1.17: Added mapped
# Version 1.18: Added get_by_uuid().
VERSION = '1.18'
# Version 1.19: Added get_by_nodename().
VERSION = '1.19'
fields = {
'id': fields.IntegerField(read_only=True),
@ -270,6 +271,17 @@ class ComputeNode(base.NovaPersistentObject, base.NovaObject):
context, host, nodename)
return cls._from_db_object(context, cls(), db_compute)
@base.remotable_classmethod
def get_by_nodename(cls, context, hypervisor_hostname):
'''Get by node name (i.e. hypervisor hostname).
Raises ComputeHostNotFound if hypervisor_hostname with the given name
doesn't exist.
'''
db_compute = db.compute_node_get_by_nodename(
context, hypervisor_hostname)
return cls._from_db_object(context, cls(), db_compute)
# TODO(pkholkin): Remove this method in the next major version bump
@base.remotable_classmethod
def get_first_node_by_host_for_old_compat(cls, context, host,

View File

@ -641,6 +641,69 @@ class HostManager(object):
for service in _services})
return compute_nodes, services
def _get_cell_by_host(self, ctxt, host):
'''Get CellMapping object of a cell the given host belongs to.'''
try:
host_mapping = objects.HostMapping.get_by_host(ctxt, host)
return host_mapping.cell_mapping
except exception.HostMappingNotFound:
LOG.warning('No host-to-cell mapping found for selected '
'host %(host)s.', {'host': host})
return
def get_compute_nodes_by_host_or_node(self, ctxt, host, node, cell=None):
'''Get compute nodes from given host or node'''
def return_empty_list_for_not_found(func):
def wrapper(*args, **kwargs):
try:
ret = func(*args, **kwargs)
except exception.NotFound:
ret = objects.ComputeNodeList()
return ret
return wrapper
@return_empty_list_for_not_found
def _get_by_host_and_node(ctxt):
compute_node = objects.ComputeNode.get_by_host_and_nodename(
ctxt, host, node)
return objects.ComputeNodeList(objects=[compute_node])
@return_empty_list_for_not_found
def _get_by_host(ctxt):
return objects.ComputeNodeList.get_all_by_host(ctxt, host)
@return_empty_list_for_not_found
def _get_by_node(ctxt):
compute_node = objects.ComputeNode.get_by_nodename(ctxt, node)
return objects.ComputeNodeList(objects=[compute_node])
if host and node:
target_fnc = _get_by_host_and_node
elif host:
target_fnc = _get_by_host
else:
target_fnc = _get_by_node
if host and not cell:
# optimization not to issue queries to every cell DB
cell = self._get_cell_by_host(ctxt, host)
cells = [cell] if cell else self.enabled_cells
timeout = context_module.CELL_TIMEOUT
nodes_by_cell = context_module.scatter_gather_cells(
ctxt, cells, timeout, target_fnc)
try:
# Only one cell should have a value for the compute nodes
# so we get it here
nodes = next(
nodes for nodes in nodes_by_cell.values() if nodes)
except StopIteration:
# ...or we find no node if none of the cells has a value
nodes = objects.ComputeNodeList()
return nodes
def refresh_cells_caches(self):
# NOTE(tssurya): This function is called from the scheduler manager's
# reset signal handler and also upon startup of the scheduler.

View File

@ -7359,6 +7359,28 @@ class ComputeNodeTestCase(test.TestCase, ModelsObjectComparatorMixin):
db.compute_node_get_by_host_and_nodename,
self.ctxt, 'host1', 'wrong')
def test_compute_node_get_by_nodename(self):
# Create another node on top of the same service
compute_node_same_host = self.compute_node_dict.copy()
compute_node_same_host['uuid'] = uuidutils.generate_uuid()
compute_node_same_host['stats'] = jsonutils.dumps(self.stats)
compute_node_same_host['hypervisor_hostname'] = 'node_2'
node = db.compute_node_create(self.ctxt, compute_node_same_host)
expected = node
result = db.compute_node_get_by_nodename(
self.ctxt, 'node_2')
self._assertEqualObjects(expected, result,
ignored_keys=self._ignored_keys +
['stats', 'service'])
def test_compute_node_get_by_nodename_not_found(self):
self.assertRaises(exception.ComputeHostNotFound,
db.compute_node_get_by_nodename,
self.ctxt, 'wrong')
def test_compute_node_get(self):
compute_node_id = self.item['id']
node = db.compute_node_get(self.ctxt, compute_node_id)

View File

@ -239,6 +239,16 @@ class _TestComputeNodeObject(object):
subs=self.subs(),
comparators=self.comparators())
@mock.patch.object(db, 'compute_node_get_by_nodename')
def test_get_by_nodename(self, cn_get_by_n):
cn_get_by_n.return_value = fake_compute_node
compute = compute_node.ComputeNode.get_by_nodename(
self.context, 'vm.danplanet.com')
self.compare_obj(compute, fake_compute_node,
subs=self.subs(),
comparators=self.comparators())
@mock.patch('nova.db.api.compute_node_get_all_by_host')
def test_get_first_node_by_host_for_old_compat(
self, cn_get_all_by_host):

View File

@ -1043,7 +1043,7 @@ object_data = {
'BuildRequestList': '1.0-cd95608eccb89fbc702c8b52f38ec738',
'CellMapping': '1.1-5d652928000a5bc369d79d5bde7e497d',
'CellMappingList': '1.1-496ef79bb2ab41041fff8bcb57996352',
'ComputeNode': '1.18-431fafd8ac4a5f3559bd9b1f1332cc22',
'ComputeNode': '1.19-af6bd29a6c3b225da436a0d8487096f2',
'ComputeNodeList': '1.17-52f3b0962b1c86b98590144463ebb192',
'ConsoleAuthToken': '1.0-a61bf7b54517c4013a12289c5a5268ea',
'CpuDiagnostics': '1.0-d256f2e442d1b837735fd17dfe8e3d47',

View File

@ -1048,6 +1048,100 @@ class HostManagerTestCase(test.NoDBTestCase):
mock.sentinel.c1n2]}, cns)
self.assertEqual(['a', 'b'], sorted(srv.keys()))
@mock.patch('nova.objects.HostMapping.get_by_host')
@mock.patch('nova.objects.ComputeNode.get_by_nodename')
@mock.patch('nova.objects.ComputeNode.get_by_host_and_nodename')
@mock.patch('nova.objects.ComputeNodeList.get_all_by_host')
def test_get_compute_nodes_by_host_or_node(self,
mock_get_all, mock_get_host_node, mock_get_node, mock_get_hm):
def _varify_result(expected, result):
self.assertEqual(len(expected), len(result))
for expected_cn, result_cn in zip(expected, result):
self.assertEqual(expected_cn.host, result_cn.host)
self.assertEqual(expected_cn.node, result_cn.node)
context = nova_context.RequestContext('fake', 'fake')
cn1 = objects.ComputeNode(host='fake_multihost', node='fake_node1')
cn2 = objects.ComputeNode(host='fake_multihost', node='fake_node2')
cn3 = objects.ComputeNode(host='fake_host1', node='fake_node')
mock_get_all.return_value = objects.ComputeNodeList(objects=[cn1, cn2])
mock_get_host_node.return_value = cn1
mock_get_node.return_value = cn3
mock_get_hm.return_value = objects.HostMapping(
context=context,
host='fake_multihost',
cell_mapping=objects.CellMapping(uuid=uuids.cell1,
db_connection='none://1',
transport_url='none://'))
# Case1: call it with host
host = 'fake_multihost'
node = None
result = self.host_manager.get_compute_nodes_by_host_or_node(
context, host, node)
expected = objects.ComputeNodeList(objects=[cn1, cn2])
_varify_result(expected, result)
mock_get_all.assert_called_once_with(context, 'fake_multihost')
mock_get_host_node.assert_not_called()
mock_get_node.assert_not_called()
mock_get_hm.assert_called_once_with(context, 'fake_multihost')
mock_get_all.reset_mock()
mock_get_hm.reset_mock()
# Case2: call it with host and node
host = 'fake_multihost'
node = 'fake_node1'
result = self.host_manager.get_compute_nodes_by_host_or_node(
context, host, node)
expected = objects.ComputeNodeList(objects=[cn1])
_varify_result(expected, result)
mock_get_all.assert_not_called()
mock_get_host_node.assert_called_once_with(
context, 'fake_multihost', 'fake_node1')
mock_get_node.assert_not_called()
mock_get_hm.assert_called_once_with(context, 'fake_multihost')
mock_get_host_node.reset_mock()
mock_get_hm.reset_mock()
# Case3: call it with node
host = None
node = 'fake_node'
result = self.host_manager.get_compute_nodes_by_host_or_node(
context, host, node)
expected = objects.ComputeNodeList(objects=[cn3])
_varify_result(expected, result)
mock_get_all.assert_not_called()
mock_get_host_node.assert_not_called()
mock_get_node.assert_called_once_with(context, 'fake_node')
mock_get_hm.assert_not_called()
@mock.patch('nova.objects.HostMapping.get_by_host')
@mock.patch('nova.objects.ComputeNodeList.get_all_by_host')
def test_get_compute_nodes_by_host_or_node_empty_list(
self, mock_get_all, mock_get_hm):
mock_get_all.side_effect = exception.ComputeHostNotFound(host='fake')
mock_get_hm.side_effect = exception.HostMappingNotFound(name='fake')
context = nova_context.RequestContext('fake', 'fake')
host = 'fake'
node = None
result = self.host_manager.get_compute_nodes_by_host_or_node(
context, host, node)
self.assertEqual(0, len(result))
class HostManagerChangedNodesTestCase(test.NoDBTestCase):
"""Test case for HostManager class."""