Skip to content

Commit 57465aa

Browse files
committed
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
1 parent f6667b0 commit 57465aa

File tree

8 files changed

+227
-2
lines changed

8 files changed

+227
-2
lines changed

nova/db/api.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,21 @@ def compute_node_get_by_host_and_nodename(context, host, nodename):
244244
return IMPL.compute_node_get_by_host_and_nodename(context, host, nodename)
245245

246246

247+
def compute_node_get_by_nodename(context, hypervisor_hostname):
248+
"""Get a compute node by hypervisor_hostname.
249+
250+
:param context: The security context (admin)
251+
:param hypervisor_hostname: Name of the node
252+
253+
:returns: Dictionary-like object containing properties of the compute node,
254+
including its statistics
255+
256+
Raises ComputeHostNotFound if hypervisor_hostname with the given name
257+
doesn't exist.
258+
"""
259+
return IMPL.compute_node_get_by_nodename(context, hypervisor_hostname)
260+
261+
247262
def compute_node_get_all(context):
248263
"""Get all computeNodes.
249264

nova/db/sqlalchemy/api.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,15 @@ def compute_node_get_by_host_and_nodename(context, host, nodename):
653653
return results[0]
654654

655655

656+
@pick_context_manager_reader
657+
def compute_node_get_by_nodename(context, hypervisor_hostname):
658+
results = _compute_node_fetchall(context,
659+
{"hypervisor_hostname": hypervisor_hostname})
660+
if not results:
661+
raise exception.ComputeHostNotFound(host=hypervisor_hostname)
662+
return results[0]
663+
664+
656665
@pick_context_manager_reader_allow_async
657666
def compute_node_get_all_by_host(context, host):
658667
results = _compute_node_fetchall(context, {"host": host})

nova/objects/compute_node.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ class ComputeNode(base.NovaPersistentObject, base.NovaObject):
5353
# Version 1.16: Added disk_allocation_ratio
5454
# Version 1.17: Added mapped
5555
# Version 1.18: Added get_by_uuid().
56-
VERSION = '1.18'
56+
# Version 1.19: Added get_by_nodename().
57+
VERSION = '1.19'
5758

5859
fields = {
5960
'id': fields.IntegerField(read_only=True),
@@ -270,6 +271,17 @@ def get_by_host_and_nodename(cls, context, host, nodename):
270271
context, host, nodename)
271272
return cls._from_db_object(context, cls(), db_compute)
272273

274+
@base.remotable_classmethod
275+
def get_by_nodename(cls, context, hypervisor_hostname):
276+
'''Get by node name (i.e. hypervisor hostname).
277+
278+
Raises ComputeHostNotFound if hypervisor_hostname with the given name
279+
doesn't exist.
280+
'''
281+
db_compute = db.compute_node_get_by_nodename(
282+
context, hypervisor_hostname)
283+
return cls._from_db_object(context, cls(), db_compute)
284+
273285
# TODO(pkholkin): Remove this method in the next major version bump
274286
@base.remotable_classmethod
275287
def get_first_node_by_host_for_old_compat(cls, context, host,

nova/scheduler/host_manager.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,69 @@ def targeted_operation(cctxt):
641641
for service in _services})
642642
return compute_nodes, services
643643

644+
def _get_cell_by_host(self, ctxt, host):
645+
'''Get CellMapping object of a cell the given host belongs to.'''
646+
try:
647+
host_mapping = objects.HostMapping.get_by_host(ctxt, host)
648+
return host_mapping.cell_mapping
649+
except exception.HostMappingNotFound:
650+
LOG.warning('No host-to-cell mapping found for selected '
651+
'host %(host)s.', {'host': host})
652+
return
653+
654+
def get_compute_nodes_by_host_or_node(self, ctxt, host, node, cell=None):
655+
'''Get compute nodes from given host or node'''
656+
def return_empty_list_for_not_found(func):
657+
def wrapper(*args, **kwargs):
658+
try:
659+
ret = func(*args, **kwargs)
660+
except exception.NotFound:
661+
ret = objects.ComputeNodeList()
662+
return ret
663+
return wrapper
664+
665+
@return_empty_list_for_not_found
666+
def _get_by_host_and_node(ctxt):
667+
compute_node = objects.ComputeNode.get_by_host_and_nodename(
668+
ctxt, host, node)
669+
return objects.ComputeNodeList(objects=[compute_node])
670+
671+
@return_empty_list_for_not_found
672+
def _get_by_host(ctxt):
673+
return objects.ComputeNodeList.get_all_by_host(ctxt, host)
674+
675+
@return_empty_list_for_not_found
676+
def _get_by_node(ctxt):
677+
compute_node = objects.ComputeNode.get_by_nodename(ctxt, node)
678+
return objects.ComputeNodeList(objects=[compute_node])
679+
680+
if host and node:
681+
target_fnc = _get_by_host_and_node
682+
elif host:
683+
target_fnc = _get_by_host
684+
else:
685+
target_fnc = _get_by_node
686+
687+
if host and not cell:
688+
# optimization not to issue queries to every cell DB
689+
cell = self._get_cell_by_host(ctxt, host)
690+
691+
cells = [cell] if cell else self.enabled_cells
692+
693+
timeout = context_module.CELL_TIMEOUT
694+
nodes_by_cell = context_module.scatter_gather_cells(
695+
ctxt, cells, timeout, target_fnc)
696+
try:
697+
# Only one cell should have a value for the compute nodes
698+
# so we get it here
699+
nodes = next(
700+
nodes for nodes in nodes_by_cell.values() if nodes)
701+
except StopIteration:
702+
# ...or we find no node if none of the cells has a value
703+
nodes = objects.ComputeNodeList()
704+
705+
return nodes
706+
644707
def refresh_cells_caches(self):
645708
# NOTE(tssurya): This function is called from the scheduler manager's
646709
# reset signal handler and also upon startup of the scheduler.

nova/tests/unit/db/test_db_api.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7219,6 +7219,28 @@ def test_compute_node_get_by_host_and_nodename_not_found(self):
72197219
db.compute_node_get_by_host_and_nodename,
72207220
self.ctxt, 'host1', 'wrong')
72217221

7222+
def test_compute_node_get_by_nodename(self):
7223+
# Create another node on top of the same service
7224+
compute_node_same_host = self.compute_node_dict.copy()
7225+
compute_node_same_host['uuid'] = uuidutils.generate_uuid()
7226+
compute_node_same_host['stats'] = jsonutils.dumps(self.stats)
7227+
compute_node_same_host['hypervisor_hostname'] = 'node_2'
7228+
7229+
node = db.compute_node_create(self.ctxt, compute_node_same_host)
7230+
7231+
expected = node
7232+
result = db.compute_node_get_by_nodename(
7233+
self.ctxt, 'node_2')
7234+
7235+
self._assertEqualObjects(expected, result,
7236+
ignored_keys=self._ignored_keys +
7237+
['stats', 'service'])
7238+
7239+
def test_compute_node_get_by_nodename_not_found(self):
7240+
self.assertRaises(exception.ComputeHostNotFound,
7241+
db.compute_node_get_by_nodename,
7242+
self.ctxt, 'wrong')
7243+
72227244
def test_compute_node_get(self):
72237245
compute_node_id = self.item['id']
72247246
node = db.compute_node_get(self.ctxt, compute_node_id)

nova/tests/unit/objects/test_compute_node.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,16 @@ def test_get_by_host_and_nodename(self, cn_get_by_h_and_n):
239239
subs=self.subs(),
240240
comparators=self.comparators())
241241

242+
@mock.patch.object(db, 'compute_node_get_by_nodename')
243+
def test_get_by_nodename(self, cn_get_by_n):
244+
cn_get_by_n.return_value = fake_compute_node
245+
246+
compute = compute_node.ComputeNode.get_by_nodename(
247+
self.context, 'vm.danplanet.com')
248+
self.compare_obj(compute, fake_compute_node,
249+
subs=self.subs(),
250+
comparators=self.comparators())
251+
242252
@mock.patch('nova.db.api.compute_node_get_all_by_host')
243253
def test_get_first_node_by_host_for_old_compat(
244254
self, cn_get_all_by_host):

nova/tests/unit/objects/test_objects.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1043,7 +1043,7 @@ def obj_name(cls):
10431043
'BuildRequestList': '1.0-cd95608eccb89fbc702c8b52f38ec738',
10441044
'CellMapping': '1.1-5d652928000a5bc369d79d5bde7e497d',
10451045
'CellMappingList': '1.1-496ef79bb2ab41041fff8bcb57996352',
1046-
'ComputeNode': '1.18-431fafd8ac4a5f3559bd9b1f1332cc22',
1046+
'ComputeNode': '1.19-af6bd29a6c3b225da436a0d8487096f2',
10471047
'ComputeNodeList': '1.17-52f3b0962b1c86b98590144463ebb192',
10481048
'ConsoleAuthToken': '1.0-a61bf7b54517c4013a12289c5a5268ea',
10491049
'CpuDiagnostics': '1.0-d256f2e442d1b837735fd17dfe8e3d47',

nova/tests/unit/scheduler/test_host_manager.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,6 +1048,100 @@ def test_get_computes_for_cells_failures(self, mock_sg):
10481048
mock.sentinel.c1n2]}, cns)
10491049
self.assertEqual(['a', 'b'], sorted(srv.keys()))
10501050

1051+
@mock.patch('nova.objects.HostMapping.get_by_host')
1052+
@mock.patch('nova.objects.ComputeNode.get_by_nodename')
1053+
@mock.patch('nova.objects.ComputeNode.get_by_host_and_nodename')
1054+
@mock.patch('nova.objects.ComputeNodeList.get_all_by_host')
1055+
def test_get_compute_nodes_by_host_or_node(self,
1056+
mock_get_all, mock_get_host_node, mock_get_node, mock_get_hm):
1057+
def _varify_result(expected, result):
1058+
self.assertEqual(len(expected), len(result))
1059+
for expected_cn, result_cn in zip(expected, result):
1060+
self.assertEqual(expected_cn.host, result_cn.host)
1061+
self.assertEqual(expected_cn.node, result_cn.node)
1062+
1063+
context = nova_context.RequestContext('fake', 'fake')
1064+
1065+
cn1 = objects.ComputeNode(host='fake_multihost', node='fake_node1')
1066+
cn2 = objects.ComputeNode(host='fake_multihost', node='fake_node2')
1067+
cn3 = objects.ComputeNode(host='fake_host1', node='fake_node')
1068+
mock_get_all.return_value = objects.ComputeNodeList(objects=[cn1, cn2])
1069+
mock_get_host_node.return_value = cn1
1070+
mock_get_node.return_value = cn3
1071+
1072+
mock_get_hm.return_value = objects.HostMapping(
1073+
context=context,
1074+
host='fake_multihost',
1075+
cell_mapping=objects.CellMapping(uuid=uuids.cell1,
1076+
db_connection='none://1',
1077+
transport_url='none://'))
1078+
1079+
# Case1: call it with host
1080+
host = 'fake_multihost'
1081+
node = None
1082+
1083+
result = self.host_manager.get_compute_nodes_by_host_or_node(
1084+
context, host, node)
1085+
expected = objects.ComputeNodeList(objects=[cn1, cn2])
1086+
1087+
_varify_result(expected, result)
1088+
mock_get_all.assert_called_once_with(context, 'fake_multihost')
1089+
mock_get_host_node.assert_not_called()
1090+
mock_get_node.assert_not_called()
1091+
mock_get_hm.assert_called_once_with(context, 'fake_multihost')
1092+
1093+
mock_get_all.reset_mock()
1094+
mock_get_hm.reset_mock()
1095+
1096+
# Case2: call it with host and node
1097+
host = 'fake_multihost'
1098+
node = 'fake_node1'
1099+
1100+
result = self.host_manager.get_compute_nodes_by_host_or_node(
1101+
context, host, node)
1102+
expected = objects.ComputeNodeList(objects=[cn1])
1103+
1104+
_varify_result(expected, result)
1105+
mock_get_all.assert_not_called()
1106+
mock_get_host_node.assert_called_once_with(
1107+
context, 'fake_multihost', 'fake_node1')
1108+
mock_get_node.assert_not_called()
1109+
mock_get_hm.assert_called_once_with(context, 'fake_multihost')
1110+
1111+
mock_get_host_node.reset_mock()
1112+
mock_get_hm.reset_mock()
1113+
1114+
# Case3: call it with node
1115+
host = None
1116+
node = 'fake_node'
1117+
1118+
result = self.host_manager.get_compute_nodes_by_host_or_node(
1119+
context, host, node)
1120+
expected = objects.ComputeNodeList(objects=[cn3])
1121+
1122+
_varify_result(expected, result)
1123+
mock_get_all.assert_not_called()
1124+
mock_get_host_node.assert_not_called()
1125+
mock_get_node.assert_called_once_with(context, 'fake_node')
1126+
mock_get_hm.assert_not_called()
1127+
1128+
@mock.patch('nova.objects.HostMapping.get_by_host')
1129+
@mock.patch('nova.objects.ComputeNodeList.get_all_by_host')
1130+
def test_get_compute_nodes_by_host_or_node_empty_list(
1131+
self, mock_get_all, mock_get_hm):
1132+
mock_get_all.side_effect = exception.ComputeHostNotFound(host='fake')
1133+
mock_get_hm.side_effect = exception.HostMappingNotFound(name='fake')
1134+
1135+
context = nova_context.RequestContext('fake', 'fake')
1136+
1137+
host = 'fake'
1138+
node = None
1139+
1140+
result = self.host_manager.get_compute_nodes_by_host_or_node(
1141+
context, host, node)
1142+
1143+
self.assertEqual(0, len(result))
1144+
10511145

10521146
class HostManagerChangedNodesTestCase(test.NoDBTestCase):
10531147
"""Test case for HostManager class."""

0 commit comments

Comments
 (0)