From 3ac546b1576c32051f74db1ae0facea3bf80d93a Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Mon, 8 Jun 2015 16:22:00 +0200 Subject: [PATCH] Add node() and ports() to NodeInfo Will be useful for plugins once we pass NodeInfo in. Implements: blueprint plugin-interface-v2 Change-Id: If2060095d7f4c11bd25ea0e0e2fd5e8aca1d53a5 --- ironic_inspector/introspect.py | 3 +- ironic_inspector/node_cache.py | 21 +++++++- ironic_inspector/process.py | 2 +- ironic_inspector/test/test_introspect.py | 21 ++++---- ironic_inspector/test/test_node_cache.py | 61 ++++++++++++++++++++++++ ironic_inspector/test/test_process.py | 4 +- 6 files changed, 96 insertions(+), 16 deletions(-) diff --git a/ironic_inspector/introspect.py b/ironic_inspector/introspect.py index b1942c6b2..b28b56fd0 100644 --- a/ironic_inspector/introspect.py +++ b/ironic_inspector/introspect.py @@ -113,8 +113,7 @@ def introspect(uuid, new_ipmi_credentials=None): def _background_introspect(ironic, cached_node): # TODO(dtantsur): pagination - macs = [p.address for p in ironic.node.list_ports(cached_node.uuid, - limit=0)] + macs = [p.address for p in cached_node.ports(ironic)] if macs: cached_node.add_attribute(node_cache.MACS_ATTRIBUTE, macs) LOG.info(_LI('Whitelisting MAC\'s %(macs)s for node %(node)s on the' diff --git a/ironic_inspector/node_cache.py b/ironic_inspector/node_cache.py index ca3926503..069910f6a 100644 --- a/ironic_inspector/node_cache.py +++ b/ironic_inspector/node_cache.py @@ -53,12 +53,15 @@ MACS_ATTRIBUTE = 'mac' class NodeInfo(object): """Record about a node in the cache.""" - def __init__(self, uuid, started_at, finished_at=None, error=None): + def __init__(self, uuid, started_at, finished_at=None, error=None, + node=None, ports=None): self.uuid = uuid self.started_at = started_at self.finished_at = finished_at self.error = error self.invalidate_cache() + self._node = node + self._ports = ports @property def options(self): @@ -129,6 +132,22 @@ class NodeInfo(object): def invalidate_cache(self): """Clear all cached info, so that it's reloaded next time.""" self._options = None + self._node = None + self._ports = None + + def node(self, ironic=None): + """Get Ironic node object associated with the cached node record.""" + if self._node is None: + ironic = utils.get_client() if ironic is None else ironic + self._node = ironic.node.get(self.uuid) + return self._node + + def ports(self, ironic=None): + """Get Ironic port objects associated with the cached node record.""" + if self._ports is None: + ironic = utils.get_client() if ironic is None else ironic + self._ports = ironic.node.list_ports(self.uuid, limit=0) + return self._ports def init(): diff --git a/ironic_inspector/process.py b/ironic_inspector/process.py index e2ff068aa..9f9e93141 100644 --- a/ironic_inspector/process.py +++ b/ironic_inspector/process.py @@ -84,7 +84,7 @@ def process(node_info): ironic = utils.get_client() try: - node = ironic.node.get(cached_node.uuid) + node = cached_node.node(ironic) except exceptions.NotFound: msg = (_('Node UUID %s was found in cache, but is not found in Ironic') % cached_node.uuid) diff --git a/ironic_inspector/test/test_introspect.py b/ironic_inspector/test/test_introspect.py index e62f98612..f635fb7f6 100644 --- a/ironic_inspector/test/test_introspect.py +++ b/ironic_inspector/test/test_introspect.py @@ -38,12 +38,12 @@ class BaseTest(test_base.NodeTest): provision_state='foobar') self.ports = [mock.Mock(address=m) for m in self.macs] self.cached_node = mock.Mock(uuid=self.uuid, options={}) + self.cached_node.ports.return_value = self.ports def _prepare(self, client_mock): cli = client_mock.return_value cli.node.get.return_value = self.node cli.node.validate.return_value = mock.Mock(power={'result': True}) - cli.node.list_ports.return_value = self.ports return cli @@ -62,10 +62,10 @@ class TestIntrospect(BaseTest): cli.node.get.assert_called_once_with(self.uuid) cli.node.validate.assert_called_once_with(self.uuid) - cli.node.list_ports.assert_called_once_with(self.uuid, limit=0) add_mock.assert_called_once_with(self.uuid, bmc_address=self.bmc_address) + self.cached_node.ports.assert_called_once_with(cli) self.cached_node.add_attribute.assert_called_once_with('mac', self.macs) filters_mock.assert_called_with(cli) @@ -102,7 +102,7 @@ class TestIntrospect(BaseTest): cli.node.get.assert_called_once_with(self.uuid) cli.node.validate.assert_called_with(self.uuid) - cli.node.list_ports.assert_called_once_with(self.uuid, limit=0) + self.cached_node.ports.assert_called_once_with(cli) add_mock.assert_called_once_with(self.uuid, bmc_address=self.bmc_address) @@ -152,16 +152,15 @@ class TestIntrospect(BaseTest): cli = client_mock.return_value cli.node.get.return_value = self.node_compat cli.node.validate.return_value = mock.Mock(power={'result': True}) - cli.node.list_ports.return_value = self.ports add_mock.return_value = mock.Mock(uuid=self.node_compat.uuid, options={}) + add_mock.return_value.ports.return_value = self.ports introspect.introspect(self.node_compat.uuid) cli.node.get.assert_called_once_with(self.node_compat.uuid) cli.node.validate.assert_called_once_with(self.node_compat.uuid) - cli.node.list_ports.assert_called_once_with(self.node_compat.uuid, - limit=0) + add_mock.return_value.ports.assert_called_once_with(cli) add_mock.assert_called_once_with(self.node_compat.uuid, bmc_address=None) @@ -176,12 +175,12 @@ class TestIntrospect(BaseTest): def test_no_macs(self, client_mock, add_mock, filters_mock): cli = self._prepare(client_mock) - cli.node.list_ports.return_value = [] + self.ports[:] = [] add_mock.return_value = self.cached_node introspect.introspect(self.node.uuid) - cli.node.list_ports.assert_called_once_with(self.uuid, limit=0) + self.cached_node.ports.assert_called_once_with(cli) add_mock.assert_called_once_with(self.uuid, bmc_address=self.bmc_address) @@ -205,7 +204,7 @@ class TestIntrospect(BaseTest): 'Cannot get node', introspect.introspect, self.uuid) - self.assertEqual(0, cli.node.list_ports.call_count) + self.assertEqual(0, self.cached_node.ports.call_count) self.assertEqual(0, filters_mock.call_count) self.assertEqual(0, cli.node.set_power_state.call_count) self.assertFalse(add_mock.called) @@ -223,7 +222,7 @@ class TestIntrospect(BaseTest): introspect.introspect, self.uuid) cli.node.validate.assert_called_once_with(self.uuid) - self.assertEqual(0, cli.node.list_ports.call_count) + self.assertEqual(0, self.cached_node.ports.call_count) self.assertEqual(0, filters_mock.call_count) self.assertEqual(0, cli.node.set_power_state.call_count) self.assertFalse(add_mock.called) @@ -238,7 +237,7 @@ class TestIntrospect(BaseTest): 'node %s with provision state "active"' % self.uuid, introspect.introspect, self.uuid) - self.assertEqual(0, cli.node.list_ports.call_count) + self.assertEqual(0, self.cached_node.ports.call_count) self.assertEqual(0, filters_mock.call_count) self.assertEqual(0, cli.node.set_power_state.call_count) self.assertFalse(add_mock.called) diff --git a/ironic_inspector/test/test_node_cache.py b/ironic_inspector/test/test_node_cache.py index 6e4f8f933..34f9c2700 100644 --- a/ironic_inspector/test/test_node_cache.py +++ b/ironic_inspector/test/test_node_cache.py @@ -321,3 +321,64 @@ class TestNodeInfoOptions(test_base.NodeTest): new = node_cache.NodeInfo(uuid=self.uuid, started_at=3.14) self.assertEqual(data, new.options['name']) + + +@mock.patch.object(utils, 'get_client') +class TestNodeCacheIronicObjects(unittest.TestCase): + def test_node_provided(self, mock_ironic): + node_info = node_cache.NodeInfo(uuid='uuid', started_at=0, + node=mock.sentinel.node) + self.assertIs(mock.sentinel.node, node_info.node()) + self.assertIs(mock.sentinel.node, node_info.node(ironic='ironic')) + self.assertFalse(mock_ironic.called) + + def test_node_not_provided(self, mock_ironic): + mock_ironic.return_value.node.get.return_value = mock.sentinel.node + node_info = node_cache.NodeInfo(uuid='uuid', started_at=0) + + self.assertIs(mock.sentinel.node, node_info.node()) + self.assertIs(node_info.node(), node_info.node()) + + mock_ironic.assert_called_once_with() + mock_ironic.return_value.node.get.assert_called_once_with('uuid') + + def test_node_ironic_arg(self, mock_ironic): + ironic2 = mock.Mock() + ironic2.node.get.return_value = mock.sentinel.node + node_info = node_cache.NodeInfo(uuid='uuid', started_at=0) + + self.assertIs(mock.sentinel.node, node_info.node(ironic=ironic2)) + self.assertIs(node_info.node(), node_info.node(ironic=ironic2)) + + self.assertFalse(mock_ironic.called) + ironic2.node.get.assert_called_once_with('uuid') + + def test_ports_provided(self, mock_ironic): + node_info = node_cache.NodeInfo(uuid='uuid', started_at=0, + ports=mock.sentinel.ports) + self.assertIs(mock.sentinel.ports, node_info.ports()) + self.assertIs(mock.sentinel.ports, node_info.ports(ironic='ironic')) + self.assertFalse(mock_ironic.called) + + def test_ports_not_provided(self, mock_ironic): + mock_ironic.return_value.node.list_ports.return_value = ( + mock.sentinel.ports) + node_info = node_cache.NodeInfo(uuid='uuid', started_at=0) + + self.assertIs(mock.sentinel.ports, node_info.ports()) + self.assertIs(node_info.ports(), node_info.ports()) + + mock_ironic.assert_called_once_with() + mock_ironic.return_value.node.list_ports.assert_called_once_with( + 'uuid', limit=0) + + def test_ports_ironic_arg(self, mock_ironic): + ironic2 = mock.Mock() + ironic2.node.list_ports.return_value = mock.sentinel.ports + node_info = node_cache.NodeInfo(uuid='uuid', started_at=0) + + self.assertIs(mock.sentinel.ports, node_info.ports(ironic=ironic2)) + self.assertIs(node_info.ports(), node_info.ports(ironic=ironic2)) + + self.assertFalse(mock_ironic.called) + ironic2.node.list_ports.assert_called_once_with('uuid', limit=0) diff --git a/ironic_inspector/test/test_process.py b/ironic_inspector/test/test_process.py index ef04767f6..addd28bf6 100644 --- a/ironic_inspector/test/test_process.py +++ b/ironic_inspector/test/test_process.py @@ -343,7 +343,9 @@ class TestProcessNode(BaseTest): self.cli.port.create.side_effect = self.ports self.cli.node.update.return_value = self.node - def call(self): + @mock.patch.object(utils, 'get_client') + def call(self, mock_cli): + mock_cli.return_value = self.cli return process._process_node(self.cli, self.node, self.data, self.cached_node)