Skip to content

Commit ba71dc5

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "[ironic] Partition & use cache for list_instance*" into unmaintained/zed
2 parents 20e17e0 + bae6152 commit ba71dc5

File tree

3 files changed

+54
-49
lines changed

3 files changed

+54
-49
lines changed

nova/tests/unit/virt/ironic/test_driver.py

Lines changed: 26 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -582,71 +582,58 @@ def test__get_node_list_fail(self):
582582

583583
@mock.patch.object(objects.Instance, 'get_by_uuid')
584584
def test_list_instances(self, mock_inst_by_uuid):
585-
nodes = []
585+
nodes = {}
586586
instances = []
587587
for i in range(2):
588588
uuid = uuidutils.generate_uuid()
589+
node_uuid = uuidutils.generate_uuid()
589590
instances.append(fake_instance.fake_instance_obj(self.ctx,
590591
id=i,
591592
uuid=uuid))
592-
nodes.append(ironic_utils.get_test_node(instance_id=uuid,
593-
fields=['instance_id']))
593+
nodes[node_uuid] = ironic_utils.get_test_node(
594+
id=node_uuid, instance_id=uuid, fields=('instance_id',))
594595
mock_inst_by_uuid.side_effect = instances
595-
self.mock_conn.nodes.return_value = iter(nodes)
596+
self.driver.node_cache = nodes
596597

597598
response = self.driver.list_instances()
598599

599-
self.mock_conn.nodes.assert_called_with(associated=True,
600-
fields=['instance_uuid'])
601600
expected_calls = [mock.call(mock.ANY, instances[0].uuid),
602601
mock.call(mock.ANY, instances[1].uuid)]
603602
mock_inst_by_uuid.assert_has_calls(expected_calls)
604603
self.assertEqual(['instance-00000000', 'instance-00000001'],
605604
sorted(response))
606605

607-
# NOTE(dustinc) This test ensures we use instance_uuid not instance_id in
608-
# 'fields' when calling ironic.
606+
@mock.patch.object(ironic_driver.IronicDriver, '_refresh_cache')
609607
@mock.patch.object(objects.Instance, 'get_by_uuid')
610-
def test_list_instances_uses_instance_uuid(self, mock_inst_by_uuid):
611-
self.driver.list_instances()
612-
613-
self.mock_conn.nodes.assert_called_with(associated=True,
614-
fields=['instance_uuid'])
615-
616-
@mock.patch.object(objects.Instance, 'get_by_uuid')
617-
def test_list_instances_fail(self, mock_inst_by_uuid):
618-
self.mock_conn.nodes.side_effect = exception.NovaException
608+
def test_list_instances_fail(self, mock_inst_by_uuid, mock_cache):
609+
mock_cache.side_effect = exception.VirtDriverNotReady
619610

620611
self.assertRaises(exception.VirtDriverNotReady,
621612
self.driver.list_instances)
622-
self.mock_conn.nodes.assert_called_with(associated=True,
623-
fields=['instance_uuid'])
624613
self.assertFalse(mock_inst_by_uuid.called)
625614

626615
def test_list_instance_uuids(self):
627616
num_nodes = 2
628-
nodes = []
617+
nodes = {}
629618
for n in range(num_nodes):
630-
nodes.append(ironic_utils.get_test_node(
631-
instance_id=uuidutils.generate_uuid(),
632-
fields=['instance_id']))
633-
self.mock_conn.nodes.return_value = iter(nodes)
619+
node_uuid = uuidutils.generate_uuid()
620+
instance_uuid = uuidutils.generate_uuid()
621+
nodes[instance_uuid] = ironic_utils.get_test_node(
622+
id=node_uuid,
623+
instance_id=instance_uuid,
624+
fields=('instance_id',))
625+
self.driver.node_cache = nodes
626+
instance_uuids = self.driver.list_instance_uuids()
627+
expected = nodes.keys()
628+
629+
self.assertEqual(sorted(expected), sorted(instance_uuids))
630+
631+
@mock.patch.object(ironic_driver.IronicDriver, '_refresh_cache')
632+
def test_list_instance_uuids_fail(self, mock_cache):
633+
mock_cache.side_effect = exception.VirtDriverNotReady
634634

635-
uuids = self.driver.list_instance_uuids()
636-
637-
self.mock_conn.nodes.assert_called_with(associated=True,
638-
fields=['instance_uuid'])
639-
expected = [n.instance_id for n in nodes]
640-
self.assertEqual(sorted(expected), sorted(uuids))
641-
642-
# NOTE(dustinc) This test ensures we use instance_uuid not instance_id in
643-
# 'fields' when calling ironic.
644-
@mock.patch.object(objects.Instance, 'get_by_uuid')
645-
def test_list_instance_uuids_uses_instance_uuid(self, mock_inst_by_uuid):
646-
self.driver.list_instance_uuids()
647-
648-
self.mock_conn.nodes.assert_called_with(associated=True,
649-
fields=['instance_uuid'])
635+
self.assertRaises(exception.VirtDriverNotReady,
636+
self.driver.list_instance_uuids)
650637

651638
@mock.patch.object(objects.InstanceList, 'get_uuids_by_host')
652639
@mock.patch.object(objects.ServiceList, 'get_all_computes_by_hv_type')

nova/virt/ironic/driver.py

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -647,13 +647,21 @@ def list_instances(self):
647647
:raises: VirtDriverNotReady
648648
649649
"""
650-
# NOTE(dustinc): The SDK returns an object with instance_id,
651-
# but the Ironic API expects instance_uuid in query.
650+
# NOTE(JayF): As of this writing, November 2023, this is only called
651+
# one place; in compute/manager.py, and only if
652+
# list_instance_uuids is not implemented. This means that
653+
# this is effectively dead code in the Ironic driver.
654+
if not self.node_cache:
655+
# Empty cache, try to populate it. If we cannot populate it, this
656+
# is OK. This information is only used to cleanup deleted nodes;
657+
# if Ironic has no deleted nodes; we're good.
658+
self._refresh_cache()
659+
652660
context = nova_context.get_admin_context()
653-
return [objects.Instance.get_by_uuid(context, i.instance_id).name
654-
for i in self._get_node_list(return_generator=True,
655-
associated=True,
656-
fields=['instance_uuid'])]
661+
662+
return [objects.Instance.get_by_uuid(context, node.instance_id).name
663+
for node in self.node_cache.values()
664+
if node.instance_id is not None]
657665

658666
def list_instance_uuids(self):
659667
"""Return the IDs of all the instances provisioned.
@@ -662,10 +670,15 @@ def list_instance_uuids(self):
662670
:raises: VirtDriverNotReady
663671
664672
"""
665-
# NOTE(dustinc): The SDK returns an object with instance_id,
666-
# but the Ironic API expects instance_uuid in query.
667-
return [node.instance_id for node in self._get_node_list(
668-
return_generator=True, associated=True, fields=['instance_uuid'])]
673+
if not self.node_cache:
674+
# Empty cache, try to populate it. If we cannot populate it, this
675+
# is OK. This information is only used to cleanup deleted nodes;
676+
# if Ironic has no deleted nodes; we're good.
677+
self._refresh_cache()
678+
679+
return [node.instance_id
680+
for node in self.node_cache.values()
681+
if node.instance_id is not None]
669682

670683
def node_is_available(self, nodename):
671684
"""Confirms a Nova hypervisor node exists in the Ironic inventory.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
fixes:
2+
- Ironic virt driver now uses the node cache and respects partition keys,
3+
such as conductor group, for list_instances and list_instance_uuids calls.
4+
This fix will improve performance of the periodic queries which use these
5+
driver methods and reduce API and DB load on the backing Ironic service.

0 commit comments

Comments
 (0)