Add get_compute_nodes_by_host_or_node()
This patch adds a function, get_compute_nodes_by_host_or_node() to the host manager to get ComputeNode objects by the given host name and/or by the given node name. Change-Id: Ic492766691741e3a8e4938ad90307cb2fe484cc5 Blueprint: use-placement-in-tree
This commit is contained in:
parent
f6667b05d2
commit
57465aae77
|
@ -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.
|
||||
|
||||
|
|
|
@ -653,6 +653,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})
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -7219,6 +7219,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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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."""
|
||||
|
|
Loading…
Reference in New Issue