Add node() and ports() to NodeInfo

Will be useful for plugins once we pass NodeInfo in.
Implements: blueprint plugin-interface-v2

Change-Id: If2060095d7f4c11bd25ea0e0e2fd5e8aca1d53a5
This commit is contained in:
Dmitry Tantsur 2015-06-08 16:22:00 +02:00
parent 83fab1031f
commit 3ac546b157
6 changed files with 96 additions and 16 deletions

View File

@ -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'

View File

@ -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():

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)