Add introspection rules actions to add/remove traits on nodes

Otherwise it's not possible to modify them, since they're not updated
via the regular node updating mechanism.

Change-Id: I338015ff9dafe07f4e70a23ddcf6cd488eda9907
Story: #2003788
Task: #26496
This commit is contained in:
Dmitry Tantsur 2018-09-17 15:20:28 +02:00
parent 34b150340c
commit 0a26a6677d
6 changed files with 87 additions and 0 deletions

View File

@ -123,6 +123,12 @@ Default available actions include:
value as a list and appends value to it. If optional ``unique`` parameter is value as a list and appends value to it. If optional ``unique`` parameter is
set to ``True``, nothing will be added if given value is already in a list. set to ``True``, nothing will be added if given value is already in a list.
* ``add-trait`` adds a trait to an Ironic node. Requires a ``name`` field
with the name of the trait to add.
* ``remove-trait`` removes a trait from an Ironic node. Requires a ``name``
field with the name of the trait to remove.
Starting from Mitaka release, ``value`` field in actions supports fetching data Starting from Mitaka release, ``value`` field in actions supports fetching data
from introspection, using `python string formatting notation from introspection, using `python string formatting notation
<https://docs.python.org/2/library/string.html#formatspec>`_:: <https://docs.python.org/2/library/string.html#formatspec>`_::

View File

@ -470,6 +470,28 @@ class NodeInfo(object):
ironic=ironic, ironic=ironic,
capabilities=ir_utils.dict_to_capabilities(existing)) capabilities=ir_utils.dict_to_capabilities(existing))
def add_trait(self, trait, ironic=None):
"""Add a trait to the node.
:param trait: trait to add
:param ironic: Ironic client to use instead of self.ironic
"""
ironic = ironic or self.ironic
ironic.node.add_trait(self.uuid, trait)
def remove_trait(self, trait, ironic=None):
"""Remove a trait from the node.
:param trait: trait to add
:param ironic: Ironic client to use instead of self.ironic
"""
ironic = ironic or self.ironic
try:
ironic.node.remove_trait(self.uuid, trait)
except exceptions.NotFound:
LOG.debug('Trait %s is not set, cannot remove', trait,
node_info=self)
def delete_port(self, port, ironic=None): def delete_port(self, port, ironic=None):
"""Delete port. """Delete port.

View File

@ -151,3 +151,17 @@ class ExtendAttributeAction(base.RuleActionPlugin):
return values return values
node_info.replace_field(params['path'], _replace, default=[]) node_info.replace_field(params['path'], _replace, default=[])
class AddTraitAction(base.RuleActionPlugin):
REQUIRED_PARAMS = {'name'}
def apply(self, node_info, params, **kwargs):
node_info.add_trait(params['name'])
class RemoveTraitAction(base.RuleActionPlugin):
REQUIRED_PARAMS = {'name'}
def apply(self, node_info, params, **kwargs):
node_info.remove_trait(params['name'])

View File

@ -14,6 +14,7 @@
"""Tests for introspection rules plugins.""" """Tests for introspection rules plugins."""
from ironicclient import exceptions
import mock import mock
from ironic_inspector.common import ironic as ir_utils from ironic_inspector.common import ironic as ir_utils
@ -222,3 +223,40 @@ class TestExtendAttributeAction(test_base.NodeTest):
self.node.extra['value'] = [42] self.node.extra['value'] = [42]
self.act.apply(self.node_info, params) self.act.apply(self.node_info, params)
self.assertFalse(mock_patch.called) self.assertFalse(mock_patch.called)
@mock.patch('ironic_inspector.common.ironic.get_client', autospec=True)
class TestAddTraitAction(test_base.NodeTest):
act = rules_plugins.AddTraitAction()
params = {'name': 'CUSTOM_FOO'}
def test_validate(self, mock_cli):
self.act.validate(self.params)
self.assertRaises(ValueError, self.act.validate, {'value': 42})
def test_add(self, mock_cli):
self.act.apply(self.node_info, self.params)
mock_cli.return_value.node.add_trait.assert_called_once_with(
self.uuid, 'CUSTOM_FOO')
@mock.patch('ironic_inspector.common.ironic.get_client', autospec=True)
class TestRemoveTraitAction(test_base.NodeTest):
act = rules_plugins.RemoveTraitAction()
params = {'name': 'CUSTOM_FOO'}
def test_validate(self, mock_cli):
self.act.validate(self.params)
self.assertRaises(ValueError, self.act.validate, {'value': 42})
def test_remove(self, mock_cli):
self.act.apply(self.node_info, self.params)
mock_cli.return_value.node.remove_trait.assert_called_once_with(
self.uuid, 'CUSTOM_FOO')
def test_remove_not_found(self, mock_cli):
mock_cli.return_value.node.remove_trait.side_effect = (
exceptions.NotFound('trait not found'))
self.act.apply(self.node_info, self.params)
mock_cli.return_value.node.remove_trait.assert_called_once_with(
self.uuid, 'CUSTOM_FOO')

View File

@ -0,0 +1,5 @@
---
features:
- |
Adds new introspection rules actions to add or remove traits on nodes:
``add-trait`` and ``remove-trait``.

View File

@ -60,6 +60,8 @@ ironic_inspector.rules.actions =
set-attribute = ironic_inspector.plugins.rules:SetAttributeAction set-attribute = ironic_inspector.plugins.rules:SetAttributeAction
set-capability = ironic_inspector.plugins.rules:SetCapabilityAction set-capability = ironic_inspector.plugins.rules:SetCapabilityAction
extend-attribute = ironic_inspector.plugins.rules:ExtendAttributeAction extend-attribute = ironic_inspector.plugins.rules:ExtendAttributeAction
add-trait = ironic_inspector.plugins.rules:AddTraitAction
remove-trait = ironic_inspector.plugins.rules:RemoveTraitAction
ironic_inspector.pxe_filter = ironic_inspector.pxe_filter =
dnsmasq = ironic_inspector.pxe_filter.dnsmasq:DnsmasqFilter dnsmasq = ironic_inspector.pxe_filter.dnsmasq:DnsmasqFilter
iptables = ironic_inspector.pxe_filter.iptables:IptablesFilter iptables = ironic_inspector.pxe_filter.iptables:IptablesFilter