Move update functions to NodeInfo object

Accept ironic object in NodeInfo __init__ instead of every method
to reduce number of times we need to pass it around.

Add new methods to NodeInfo: patch, update_properties,
update_capabilities, patch_port and delete_port.

Change-Id: Ida03ac70e743f53f887bb194f61b0011950d4dfd
Partial-Bug: #1492946
This commit is contained in:
Dmitry Tantsur 2015-09-07 10:31:29 +02:00
parent 18b3cccbaa
commit 5b02639716
7 changed files with 224 additions and 80 deletions

View File

@ -98,7 +98,8 @@ def introspect(uuid, new_ipmi_credentials=None, token=None):
'reason': validation.power['reason']})
node_info = node_cache.add_node(node.uuid,
bmc_address=utils.get_ipmi_address(node))
bmc_address=utils.get_ipmi_address(node),
ironic=ironic)
node_info.set_option('new_ipmi_credentials', new_ipmi_credentials)
def _handle_exceptions():
@ -118,7 +119,7 @@ def _background_introspect(ironic, node_info):
global _LAST_INTROSPECTION_TIME
# TODO(dtantsur): pagination
macs = list(node_info.ports(ironic))
macs = list(node_info.ports())
if macs:
node_info.add_attribute(node_cache.MACS_ATTRIBUTE, macs)
LOG.info(_LI('Whitelisting MAC\'s %(macs)s for node %(node)s on the'

View File

@ -39,7 +39,7 @@ class NodeInfo(object):
"""Record about a node in the cache."""
def __init__(self, uuid, started_at, finished_at=None, error=None,
node=None, ports=None):
node=None, ports=None, ironic=None):
self.uuid = uuid
self.started_at = started_at
self.finished_at = finished_at
@ -50,6 +50,7 @@ class NodeInfo(object):
ports = {p.address: p for p in ports}
self._ports = ports
self._attributes = None
self._ironic = ironic
@property
def options(self):
@ -72,6 +73,13 @@ class NodeInfo(object):
self._attributes.setdefault(row.name, []).append(row.value)
return self._attributes
@property
def ironic(self):
"""Ironic client instance."""
if self._ironic is None:
self._ironic = utils.get_client()
return self._ironic
def set_option(self, name, value):
"""Set an option for a node."""
encoded = json.dumps(value)
@ -127,11 +135,11 @@ class NodeInfo(object):
self._attributes = None
@classmethod
def from_row(cls, row):
def from_row(cls, row, ironic=None):
"""Construct NodeInfo from a database row."""
fields = {key: row[key]
for key in ('uuid', 'started_at', 'finished_at', 'error')}
return cls(**fields)
return cls(ironic=ironic, **fields)
def invalidate_cache(self):
"""Clear all cached info, so that it's reloaded next time."""
@ -139,28 +147,27 @@ class NodeInfo(object):
self._node = None
self._ports = None
self._attributes = None
self._ironic = None
def node(self, ironic=None):
def node(self):
"""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)
self._node = self.ironic.node.get(self.uuid)
return self._node
def create_ports(self, macs, ironic=None):
def create_ports(self, macs):
"""Create one or several ports for this node.
A warning is issued if port already exists on a node.
"""
ironic = utils.get_client() if ironic is None else ironic
for mac in macs:
if mac not in self.ports():
self._create_port(mac, ironic)
self._create_port(mac)
else:
LOG.warn(_LW('Port %(mac)s already exists for node %(uuid)s, '
'skipping'), {'mac': mac, 'uuid': self.uuid})
def ports(self, ironic=None):
def ports(self):
"""Get Ironic port objects associated with the cached node record.
This value is cached as well, use invalidate_cache() to clean.
@ -168,14 +175,13 @@ class NodeInfo(object):
:return: dict MAC -> port object
"""
if self._ports is None:
ironic = utils.get_client() if ironic is None else ironic
self._ports = {p.address: p
for p in ironic.node.list_ports(self.uuid, limit=0)}
self._ports = {p.address: p for p in
self.ironic.node.list_ports(self.uuid, limit=0)}
return self._ports
def _create_port(self, mac, ironic):
def _create_port(self, mac):
try:
port = ironic.port.create(node_uuid=self.uuid, address=mac)
port = self.ironic.port.create(node_uuid=self.uuid, address=mac)
except exceptions.Conflict:
LOG.warn(_LW('Port %(mac)s already exists for node %(uuid)s, '
'skipping'), {'mac': mac, 'uuid': self.uuid})
@ -185,6 +191,66 @@ class NodeInfo(object):
else:
self._ports[mac] = port
def patch(self, patches):
"""Apply JSON patches to a node.
Refreshes cached node instance.
:param patches: JSON patches to apply
:raises: ironicclient exceptions
"""
LOG.debug('Updating node %(uuid)s with patches %(patches)s',
{'uuid': self.uuid, 'patches': patches})
self._node = self.ironic.node.update(self.uuid, patches)
def patch_port(self, port, patches):
"""Apply JSON patches to a port.
:param port: port object or its MAC
:param patches: JSON patches to apply
"""
ports = self.ports()
if isinstance(port, str):
port = ports[port]
LOG.debug('Updating port %(mac)s of node %(uuid)s with patches '
'%(patches)s',
{'mac': port.address, 'uuid': self.uuid, 'patches': patches})
new_port = self.ironic.port.update(port.uuid, patches)
ports[port.address] = new_port
def update_properties(self, **props):
"""Update properties on a node.
:param props: properties to update
"""
patches = [{'op': 'add', 'path': '/properties/%s' % k, 'value': v}
for k, v in props.items()]
self.patch(patches)
def update_capabilities(self, **caps):
"""Update capabilities on a node.
:param props: capabilities to update
"""
existing = utils.capabilities_to_dict(
self.node().properties.get('capabilities'))
existing.update(caps)
self.update_properties(
capabilities=utils.dict_to_capabilities(existing))
def delete_port(self, port):
"""Delete port.
:param port: port object or its MAC
"""
ports = self.ports()
if isinstance(port, str):
port = ports[port]
self.ironic.port.delete(port.uuid)
del ports[port.address]
def add_node(uuid, **attributes):
"""Store information about a node under introspection.
@ -193,7 +259,8 @@ def add_node(uuid, **attributes):
Empty values are skipped.
:param uuid: Ironic node UUID
:param attributes: attributes known about this node (like macs, BMC etc)
:param attributes: attributes known about this node (like macs, BMC etc);
also ironic client instance may be passed under 'ironic'
:returns: NodeInfo
"""
started_at = time.time()
@ -201,7 +268,8 @@ def add_node(uuid, **attributes):
_delete_node(uuid)
db.Node(uuid=uuid, started_at=started_at).save(session)
node_info = NodeInfo(uuid=uuid, started_at=started_at)
node_info = NodeInfo(uuid=uuid, started_at=started_at,
ironic=attributes.pop('ironic', None))
for (name, value) in attributes.items():
if not value:
continue
@ -250,26 +318,29 @@ def _list_node_uuids():
return {x.uuid for x in db.model_query(db.Node.uuid)}
def get_node(uuid):
def get_node(uuid, ironic=None):
"""Get node from cache by it's UUID.
:param uuid: node UUID.
:param ironic: optional ironic client instance
:returns: structure NodeInfo.
"""
row = db.model_query(db.Node).filter_by(uuid=uuid).first()
if row is None:
raise utils.Error(_('Could not find node %s in cache') % uuid,
code=404)
return NodeInfo.from_row(row)
return NodeInfo.from_row(row, ironic=ironic)
def find_node(**attributes):
"""Find node in cache.
:param attributes: attributes known about this node (like macs, BMC etc)
also ironic client instance may be passed under 'ironic'
:returns: structure NodeInfo with attributes ``uuid`` and ``created_at``
:raises: Error if node is not found
"""
ironic = attributes.pop('ironic', None)
# NOTE(dtantsur): sorting is not required, but gives us predictability
found = set()
@ -315,7 +386,7 @@ def find_node(**attributes):
'Introspection for node %(node)s already finished on '
'%(finish)s') % {'node': uuid, 'finish': row.finished_at})
return NodeInfo(uuid=uuid, started_at=row.started_at)
return NodeInfo(uuid=uuid, started_at=row.started_at, ironic=ironic)
def clean_up():

View File

@ -142,8 +142,8 @@ class ValidateInterfacesHook(base.ProcessingHook):
else:
return
ironic = utils.get_client()
for port in node_info.ports(ironic).values():
# list is required as we modify underlying dict
for port in list(node_info.ports().values()):
if port.address not in expected_macs:
LOG.info(_LI("Deleting port %(port)s as its MAC %(mac)s is "
"not in expected MAC list %(expected)s for node "
@ -152,7 +152,7 @@ class ValidateInterfacesHook(base.ProcessingHook):
'mac': port.address,
'expected': list(sorted(expected_macs)),
'node': node_info.uuid})
ironic.port.delete(port.uuid)
node_info.delete_port(port)
class RamdiskErrorHook(base.ProcessingHook):

View File

@ -100,9 +100,8 @@ def process(introspection_data):
}
raise utils.Error(msg)
ironic = utils.get_client()
try:
node = node_info.node(ironic)
node = node_info.node()
except exceptions.NotFound:
msg = (_('Node UUID %s was found in cache, but is not found in Ironic')
% node_info.uuid)
@ -110,7 +109,7 @@ def process(introspection_data):
raise utils.Error(msg, code=404)
try:
return _process_node(ironic, node, introspection_data, node_info)
return _process_node(node, introspection_data, node_info)
except utils.Error as exc:
node_info.finished(error=str(exc))
raise
@ -136,11 +135,11 @@ def _run_post_hooks(node_info, introspection_data):
return node_patches, port_patches
def _process_node(ironic, node, introspection_data, node_info):
def _process_node(node, introspection_data, node_info):
# NOTE(dtantsur): repeat the check in case something changed
utils.check_provision_state(node)
node_info.create_ports(introspection_data.get('macs') or (), ironic=ironic)
node_info.create_ports(introspection_data.get('macs') or ())
node_patches, port_patches = _run_post_hooks(node_info,
introspection_data)
@ -159,15 +158,12 @@ def _process_node(ironic, node, introspection_data, node_info):
else:
LOG.debug('Swift support is disabled, introspection data for node %s '
'won\'t be stored', node_info.uuid)
node = ironic.node.update(node.uuid, node_patches)
node_info.patch(node_patches)
for mac, patches in port_patches.items():
port = node_info.ports(ironic)[mac]
ironic.port.update(port.uuid, patches)
LOG.debug('Node %s was updated with data from introspection process, '
'patches %s, port patches %s',
node.uuid, node_patches, port_patches)
node_info.patch_port(mac, patches)
ironic = utils.get_client()
firewall.update_filters(ironic)
resp = {'uuid': node.uuid}

View File

@ -71,8 +71,9 @@ class TestIntrospect(BaseTest):
cli.node.validate.assert_called_once_with(self.uuid)
add_mock.assert_called_once_with(self.uuid,
bmc_address=self.bmc_address)
self.node_info.ports.assert_called_once_with(cli)
bmc_address=self.bmc_address,
ironic=cli)
self.node_info.ports.assert_called_once_with()
self.node_info.add_attribute.assert_called_once_with('mac',
self.macs)
filters_mock.assert_called_with(cli)
@ -85,7 +86,7 @@ class TestIntrospect(BaseTest):
'new_ipmi_credentials', None)
def test_ok_ilo_and_drac(self, client_mock, add_mock, filters_mock):
self._prepare(client_mock)
cli = self._prepare(client_mock)
add_mock.return_value = self.node_info
for name in ('ilo_address', 'drac_host'):
@ -93,7 +94,8 @@ class TestIntrospect(BaseTest):
introspect.introspect(self.node.uuid)
add_mock.assert_called_with(self.uuid,
bmc_address=self.bmc_address)
bmc_address=self.bmc_address,
ironic=cli)
def test_power_failure(self, client_mock, add_mock, filters_mock):
cli = self._prepare(client_mock)
@ -106,7 +108,8 @@ class TestIntrospect(BaseTest):
cli.node.get.assert_called_once_with(self.uuid)
add_mock.assert_called_once_with(self.uuid,
bmc_address=self.bmc_address)
bmc_address=self.bmc_address,
ironic=cli)
cli.node.set_boot_device.assert_called_once_with(self.uuid,
'pxe',
persistent=False)
@ -125,7 +128,8 @@ class TestIntrospect(BaseTest):
cli.node.get.assert_called_once_with(self.uuid)
add_mock.assert_called_once_with(self.uuid,
bmc_address=self.bmc_address)
bmc_address=self.bmc_address,
ironic=cli)
self.assertFalse(cli.node.set_boot_device.called)
add_mock.return_value.finished.assert_called_once_with(
error=mock.ANY)
@ -144,10 +148,11 @@ class TestIntrospect(BaseTest):
cli.node.get.assert_called_once_with(self.node_compat.uuid)
cli.node.validate.assert_called_once_with(self.node_compat.uuid)
add_mock.return_value.ports.assert_called_once_with(cli)
add_mock.return_value.ports.assert_called_once_with()
add_mock.assert_called_once_with(self.node_compat.uuid,
bmc_address=None)
bmc_address=None,
ironic=cli)
add_mock.return_value.add_attribute.assert_called_once_with('mac',
self.macs)
filters_mock.assert_called_with(cli)
@ -164,10 +169,11 @@ class TestIntrospect(BaseTest):
introspect.introspect(self.node.uuid)
self.node_info.ports.assert_called_once_with(cli)
self.node_info.ports.assert_called_once_with()
add_mock.assert_called_once_with(self.uuid,
bmc_address=self.bmc_address)
bmc_address=self.bmc_address,
ironic=cli)
self.assertFalse(self.node_info.add_attribute.called)
self.assertFalse(filters_mock.called)
cli.node.set_boot_device.assert_called_once_with(self.uuid,
@ -184,7 +190,7 @@ class TestIntrospect(BaseTest):
introspect.introspect(self.uuid)
self.node_info.ports.assert_called_once_with(cli)
self.node_info.ports.assert_called_once_with()
self.node_info.finished.assert_called_once_with(error=mock.ANY)
self.assertEqual(0, filters_mock.call_count)
self.assertEqual(0, cli.node.set_power_state.call_count)
@ -330,7 +336,8 @@ class TestSetIpmiCredentials(BaseTest):
introspect.introspect(self.uuid, new_ipmi_credentials=self.new_creds)
add_mock.assert_called_once_with(self.uuid,
bmc_address=self.bmc_address)
bmc_address=self.bmc_address,
ironic=cli)
filters_mock.assert_called_with(cli)
self.assertFalse(cli.node.validate.called)
self.assertFalse(cli.node.set_boot_device.called)
@ -348,7 +355,8 @@ class TestSetIpmiCredentials(BaseTest):
introspect.introspect(self.uuid, new_ipmi_credentials=self.new_creds)
add_mock.assert_called_once_with(self.uuid,
bmc_address=self.bmc_address)
bmc_address=self.bmc_address,
ironic=cli)
filters_mock.assert_called_with(cli)
self.assertFalse(cli.node.validate.called)
self.assertFalse(cli.node.set_boot_device.called)
@ -380,7 +388,8 @@ class TestSetIpmiCredentials(BaseTest):
new_ipmi_credentials=(None, self.new_creds[1]))
add_mock.assert_called_once_with(self.uuid,
bmc_address=self.bmc_address)
bmc_address=self.bmc_address,
ironic=cli)
filters_mock.assert_called_with(cli)
self.assertFalse(cli.node.validate.called)
self.assertFalse(cli.node.set_boot_device.called)

View File

@ -385,7 +385,7 @@ class TestNodeInfoOptions(test_base.NodeTest):
self.assertEqual(data, new.options['name'])
@mock.patch.object(utils, 'get_client')
@mock.patch.object(utils, 'get_client', autospec=True)
class TestNodeCacheIronicObjects(unittest.TestCase):
def setUp(self):
super(TestNodeCacheIronicObjects, self).setUp()
@ -396,7 +396,6 @@ class TestNodeCacheIronicObjects(unittest.TestCase):
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):
@ -409,29 +408,26 @@ class TestNodeCacheIronicObjects(unittest.TestCase):
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))
def test_node_ironic_preset(self, mock_ironic):
mock_ironic2 = mock.Mock()
mock_ironic2.node.get.return_value = mock.sentinel.node
node_info = node_cache.NodeInfo(uuid='uuid', started_at=0,
ironic=mock_ironic2)
self.assertIs(mock.sentinel.node, node_info.node())
self.assertFalse(mock_ironic.called)
ironic2.node.get.assert_called_once_with('uuid')
mock_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=self.ports)
self.assertIs(self.ports, node_info.ports())
self.assertIs(self.ports, node_info.ports(ironic='ironic'))
self.assertFalse(mock_ironic.called)
def test_ports_provided_list(self, mock_ironic):
node_info = node_cache.NodeInfo(uuid='uuid', started_at=0,
ports=list(self.ports.values()))
self.assertEqual(self.ports, node_info.ports())
self.assertEqual(self.ports, node_info.ports(ironic='ironic'))
self.assertFalse(mock_ironic.called)
def test_ports_not_provided(self, mock_ironic):
@ -446,13 +442,85 @@ class TestNodeCacheIronicObjects(unittest.TestCase):
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 = list(self.ports.values())
node_info = node_cache.NodeInfo(uuid='uuid', started_at=0)
self.assertEqual(self.ports, node_info.ports(ironic=ironic2))
self.assertIs(node_info.ports(), node_info.ports(ironic=ironic2))
def test_ports_ironic_preset(self, mock_ironic):
mock_ironic2 = mock.Mock()
mock_ironic2.node.list_ports.return_value = list(
self.ports.values())
node_info = node_cache.NodeInfo(uuid='uuid', started_at=0,
ironic=mock_ironic2)
self.assertEqual(self.ports, node_info.ports())
self.assertFalse(mock_ironic.called)
ironic2.node.list_ports.assert_called_once_with('uuid', limit=0)
mock_ironic2.node.list_ports.assert_called_once_with(
'uuid', limit=0)
class TestUpdate(test_base.NodeTest):
def setUp(self):
super(TestUpdate, self).setUp()
self.ironic = mock.Mock()
self.ports = {'mac%d' % i: mock.Mock(address='mac%d' % i, uuid=str(i))
for i in range(2)}
self.node_info = node_cache.NodeInfo(uuid=self.uuid,
started_at=0,
node=self.node,
ports=self.ports,
ironic=self.ironic)
def test_patch(self):
self.ironic.node.update.return_value = mock.sentinel.node
self.node_info.patch(['patch'])
self.ironic.node.update.assert_called_once_with(self.uuid, ['patch'])
self.assertIs(mock.sentinel.node, self.node_info.node())
def test_update_properties(self):
self.ironic.node.update.return_value = mock.sentinel.node
self.node_info.update_properties(prop=42)
patch = [{'op': 'add', 'path': '/properties/prop', 'value': 42}]
self.ironic.node.update.assert_called_once_with(self.uuid, patch)
self.assertIs(mock.sentinel.node, self.node_info.node())
def test_update_capabilities(self):
self.ironic.node.update.return_value = mock.sentinel.node
self.node.properties['capabilities'] = 'foo:bar,x:y'
self.node_info.update_capabilities(x=1, y=2)
self.ironic.node.update.assert_called_once_with(self.uuid, mock.ANY)
patch = self.ironic.node.update.call_args[0][1]
new_caps = utils.capabilities_to_dict(patch[0]['value'])
self.assertEqual({'foo': 'bar', 'x': '1', 'y': '2'}, new_caps)
def test_patch_port(self):
self.ironic.port.update.return_value = mock.sentinel.port
self.node_info.patch_port(self.ports['mac0'], ['patch'])
self.ironic.port.update.assert_called_once_with('0', ['patch'])
self.assertIs(mock.sentinel.port,
self.node_info.ports()['mac0'])
def test_patch_port_by_mac(self):
self.ironic.port.update.return_value = mock.sentinel.port
self.node_info.patch_port('mac0', ['patch'])
self.ironic.port.update.assert_called_once_with('0', ['patch'])
self.assertIs(mock.sentinel.port,
self.node_info.ports()['mac0'])
def test_delete_port(self):
self.node_info.delete_port(self.ports['mac0'])
self.ironic.port.delete.assert_called_once_with('0')
self.assertEqual(['mac1'], list(self.node_info.ports()))
def test_delete_port_by_mac(self):
self.node_info.delete_port('mac0')
self.ironic.port.delete.assert_called_once_with('0')
self.assertEqual(['mac1'], list(self.node_info.ports()))

View File

@ -96,7 +96,7 @@ class TestProcess(BaseTest):
pop_mock.assert_called_once_with(bmc_address=self.bmc_address,
mac=self.data['macs'])
cli.node.get.assert_called_once_with(self.uuid)
process_mock.assert_called_once_with(cli, cli.node.get.return_value,
process_mock.assert_called_once_with(cli.node.get.return_value,
self.data, pop_mock.return_value)
@prepare_mocks
@ -116,7 +116,7 @@ class TestProcess(BaseTest):
pop_mock.assert_called_once_with(bmc_address=self.bmc_address,
mac=self.data['macs'])
cli.node.get.assert_called_once_with(self.uuid)
process_mock.assert_called_once_with(cli, cli.node.get.return_value,
process_mock.assert_called_once_with(cli.node.get.return_value,
self.data, pop_mock.return_value)
@prepare_mocks
@ -136,7 +136,7 @@ class TestProcess(BaseTest):
pop_mock.assert_called_once_with(bmc_address=self.bmc_address,
mac=self.data['macs'])
cli.node.get.assert_called_once_with(self.uuid)
process_mock.assert_called_once_with(cli, cli.node.get.return_value,
process_mock.assert_called_once_with(cli.node.get.return_value,
self.data, pop_mock.return_value)
@prepare_mocks
@ -156,7 +156,7 @@ class TestProcess(BaseTest):
pop_mock.assert_called_once_with(bmc_address=self.bmc_address,
mac=self.data['macs'])
cli.node.get.assert_called_once_with(self.uuid)
process_mock.assert_called_once_with(cli, cli.node.get.return_value,
process_mock.assert_called_once_with(cli.node.get.return_value,
self.data, pop_mock.return_value)
@prepare_mocks
@ -177,7 +177,7 @@ class TestProcess(BaseTest):
pop_mock.assert_called_once_with(bmc_address=self.bmc_address,
mac=self.data['macs'])
cli.node.get.assert_called_once_with(self.uuid)
process_mock.assert_called_once_with(cli, cli.node.get.return_value,
process_mock.assert_called_once_with(cli.node.get.return_value,
self.data, pop_mock.return_value)
@prepare_mocks
@ -188,7 +188,7 @@ class TestProcess(BaseTest):
pop_mock.assert_called_once_with(bmc_address=None,
mac=self.data['macs'])
cli.node.get.assert_called_once_with(self.uuid)
process_mock.assert_called_once_with(cli, cli.node.get.return_value,
process_mock.assert_called_once_with(cli.node.get.return_value,
self.data, pop_mock.return_value)
@prepare_mocks
@ -223,7 +223,7 @@ class TestProcess(BaseTest):
pop_mock.assert_called_once_with(bmc_address=self.bmc_address,
mac=[self.macs[1]])
cli.node.get.assert_called_once_with(self.uuid)
process_mock.assert_called_once_with(cli, cli.node.get.return_value,
process_mock.assert_called_once_with(cli.node.get.return_value,
self.data, pop_mock.return_value)
@prepare_mocks
@ -397,8 +397,7 @@ class TestProcessNode(BaseTest):
@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.node_info)
return process._process_node(self.node, self.data, self.node_info)
def test_return_includes_uuid(self, filters_mock, post_hook_mock):
ret_val = self.call()