list_instances - cache allocations

When listing instances fetch all allocations to a cache,
fetch each nodes allocation from the cache. This is
significantly faster than calling get_allocation for each
node.

time metealsmith list # 250 nodes:
  real 0m54.970s vs real 1m45.048s without "cached".

Story: 2010571
Task: 47318
Change-Id: Ic4cef61e338fb57bea5de4f96eb3584386cc5152
This commit is contained in:
Harald Jensås 2023-02-08 01:34:10 +01:00
parent a71776b8a4
commit 364fa23916
2 changed files with 25 additions and 7 deletions

View File

@ -50,6 +50,8 @@ class Provisioner(object):
OpenStack API during provisioning.
"""
allocations_cache = dict()
def __init__(self, session=None, cloud_region=None, dry_run=False):
if cloud_region is None:
if session is None:
@ -630,6 +632,8 @@ class Provisioner(object):
:return: list of :py:class:`metalsmith.Instance` objects.
"""
nodes = self.connection.baremetal.nodes(associated=True, details=True)
Provisioner.allocations_cache = {
a.id: a for a in self.connection.baremetal.allocations()}
instances = [i for i in map(self._get_instance, nodes)
if i.state != _instance.InstanceState.UNKNOWN]
return instances
@ -678,13 +682,24 @@ class Provisioner(object):
'with a node' % node)
def _get_instance(self, ident):
node, allocation = self._find_node_and_allocation(ident)
if allocation is None and node.allocation_id:
if hasattr(ident, 'allocation_id'):
node = ident
try:
allocation = self.connection.baremetal.get_allocation(
node.allocation_id)
try:
allocation = Provisioner.allocations_cache[
node.instance_id]
except KeyError:
allocation = self.connection.baremetal.get_allocation(
node.allocation_id)
except os_exc.ResourceNotFound as exc:
raise exceptions.InstanceNotFound(str(exc))
else:
node, allocation = self._find_node_and_allocation(ident)
if allocation is None and node.allocation_id:
try:
allocation = self.connection.baremetal.get_allocation(
node.allocation_id)
except os_exc.ResourceNotFound as exc:
raise exceptions.InstanceNotFound(str(exc))
return _instance.Instance(self.connection, node,
allocation=allocation)

View File

@ -32,6 +32,7 @@ NODE_FIELDS = ['name', 'id', 'instance_info', 'instance_id', 'is_maintenance',
'maintenance_reason', 'properties', 'provision_state', 'extra',
'last_error', 'traits', 'resource_class', 'conductor_group',
'allocation_id']
ALLOCATION_FIELDS = ['id', 'name', 'node_id']
class TestInit(unittest.TestCase):
@ -2073,14 +2074,16 @@ class TestListInstances(Base):
self.nodes[0].allocation_id = 'id2'
self.nodes[6].instance_id = None
self.api.baremetal.nodes.return_value = self.nodes
self.allocations = [mock.Mock(id='id2')]
self.api.baremetal.allocations.return_value = self.allocations
def test_list(self):
instances = self.pr.list_instances()
self.assertTrue(all(isinstance(i, _instance.Instance)
for i in instances))
self.assertEqual(self.nodes[:6], [i.node for i in instances])
self.assertEqual([self.api.baremetal.get_allocation.return_value]
+ [None] * 5,
self.assertEqual([self.api.baremetal.get_allocation.return_value] * 6,
[i.allocation for i in instances])
self.api.baremetal.nodes.assert_called_once_with(associated=True,
details=True)
self.api.baremetal.allocations.assert_called_once()