mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
add salt_monitor beacon
pr suggestions
This commit is contained in:
parent
ac4ecad325
commit
e3cbba35f4
4 changed files with 310 additions and 0 deletions
|
@ -31,6 +31,7 @@ beacon modules
|
|||
pkg
|
||||
proxy_example
|
||||
ps
|
||||
salt_monitor
|
||||
salt_proxy
|
||||
sensehat
|
||||
service
|
||||
|
|
6
doc/ref/beacons/all/salt.beacons.salt_monitor.rst
Normal file
6
doc/ref/beacons/all/salt.beacons.salt_monitor.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
=========================
|
||||
salt.beacons.salt_monitor
|
||||
=========================
|
||||
|
||||
.. automodule:: salt.beacons.salt_monitor
|
||||
:members:
|
118
salt/beacons/salt_monitor.py
Normal file
118
salt/beacons/salt_monitor.py
Normal file
|
@ -0,0 +1,118 @@
|
|||
"""
|
||||
A beacon to execute salt execution module functions. This beacon will fire only if the return data is "truthy".
|
||||
The function return, funtion name and args and/or kwargs, will be passed as data in the event.
|
||||
|
||||
The configuration can accept a list of salt functions to execute every interval.
|
||||
Make sure to allot enough time via 'interval' key to allow all salt functions to execute.
|
||||
The salt functions will be executed sequentially.
|
||||
|
||||
The elements in list of functions can be either a simple string (with no arguments) or a dictionary with a single
|
||||
key being the salt execution module and sub keys indicating args and / or kwargs.
|
||||
|
||||
See example config below.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
beacons:
|
||||
salt_monitor:
|
||||
- salt_fun:
|
||||
- slsutil.renderer:
|
||||
args:
|
||||
- salt://states/apache.sls
|
||||
kwargs:
|
||||
- default_renderer: jinja
|
||||
- test.ping
|
||||
- interval: 3600 # seconds
|
||||
"""
|
||||
|
||||
|
||||
def _parse_args(args_kwargs_dict):
|
||||
args = args_kwargs_dict.get("args", [])
|
||||
kwargs = args_kwargs_dict.get("kwargs", {})
|
||||
if kwargs:
|
||||
_kwargs = {}
|
||||
list(map(_kwargs.update, kwargs))
|
||||
kwargs = _kwargs
|
||||
|
||||
return args, kwargs
|
||||
|
||||
|
||||
def validate(config):
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
if isinstance(_config["salt_fun"], str):
|
||||
# a simple str is taking as the single function with no args / kwargs
|
||||
fun = _config["salt_fun"]
|
||||
if fun not in __salt__:
|
||||
return False, "{} not in __salt__".format(fun)
|
||||
else:
|
||||
for entry in _config["salt_fun"]:
|
||||
if isinstance(entry, dict):
|
||||
# check dict is of correct form
|
||||
fun, args_kwargs_dict = next(iter(entry.items()))
|
||||
for key in args_kwargs_dict:
|
||||
if key == "args":
|
||||
if not isinstance(args_kwargs_dict[key], list):
|
||||
return (
|
||||
False,
|
||||
"args key for fun {} must be list".format(fun),
|
||||
)
|
||||
elif key == "kwargs":
|
||||
if not isinstance(args_kwargs_dict[key], list):
|
||||
return (
|
||||
False,
|
||||
"kwargs key for fun {} must be list of key value pairs".format(
|
||||
fun
|
||||
),
|
||||
)
|
||||
for key_value in args_kwargs_dict[key]:
|
||||
if not isinstance(key_value, dict):
|
||||
return (
|
||||
False,
|
||||
"{} is not a key / value pair".format(key_value),
|
||||
)
|
||||
else:
|
||||
return (
|
||||
False,
|
||||
"key {} not allowed under fun {}".format(key, fun),
|
||||
)
|
||||
else:
|
||||
# entry must be function itself
|
||||
fun = entry
|
||||
|
||||
if fun not in __salt__:
|
||||
return False, "{} not in __salt__".format(fun)
|
||||
|
||||
return True, "valid config"
|
||||
|
||||
|
||||
def beacon(config):
|
||||
events = []
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
if isinstance(_config["salt_fun"], str):
|
||||
# support for single salt_fun with no args / kwargs supplied as str
|
||||
fun = _config["salt_fun"]
|
||||
ret = __salt__[fun]()
|
||||
return [{"salt_fun": fun, "ret": ret}]
|
||||
# else, we should have an iterable
|
||||
for entry in _config["salt_fun"]:
|
||||
if isinstance(entry, dict):
|
||||
fun, args_kwargs_dict = list(entry.items())[0]
|
||||
args, kwargs = _parse_args(args_kwargs_dict)
|
||||
else:
|
||||
fun = entry
|
||||
args = ()
|
||||
kwargs = {}
|
||||
|
||||
ret = __salt__[fun](*args, **kwargs)
|
||||
|
||||
if ret:
|
||||
_ret = {"salt_fun": fun, "ret": ret}
|
||||
if args:
|
||||
_ret["args"] = args
|
||||
if kwargs:
|
||||
_ret["kwargs"] = kwargs
|
||||
events.append(_ret)
|
||||
return events
|
185
tests/unit/beacons/test_salt_monitor.py
Normal file
185
tests/unit/beacons/test_salt_monitor.py
Normal file
|
@ -0,0 +1,185 @@
|
|||
# pylint: disable=E8231
|
||||
# Salt libs
|
||||
import salt.beacons.salt_monitor as salt_monitor
|
||||
from tests.support.mixins import LoaderModuleMockMixin
|
||||
|
||||
# Salt testing libs
|
||||
from tests.support.unit import TestCase
|
||||
|
||||
TEST_CONFIG = [
|
||||
{
|
||||
"config": [{"salt_fun": ["test.ping", "test.version"]}],
|
||||
"expected_validate": (True, "valid config"),
|
||||
"expected_beacon": [
|
||||
{"salt_fun": "test.ping", "ret": True},
|
||||
{"salt_fun": "test.version", "ret": "3000"},
|
||||
],
|
||||
},
|
||||
{
|
||||
"config": [
|
||||
{"salt_fun": [{"cmd.run": {"args": ["echo hello world",]}}, "test.version"]}
|
||||
], # *args behaves weird on list with single string, does it happen in yaml?
|
||||
"expected_validate": (True, "valid config"),
|
||||
"expected_beacon": [
|
||||
{
|
||||
"salt_fun": "cmd.run",
|
||||
"ret": (("echo hello world",), {}),
|
||||
"args": ["echo hello world"],
|
||||
},
|
||||
{"salt_fun": "test.version", "ret": "3000"},
|
||||
],
|
||||
},
|
||||
{
|
||||
"config": [
|
||||
{
|
||||
"salt_fun": [
|
||||
{
|
||||
"cmd.run": {
|
||||
"args": ["echo hello world",],
|
||||
"kwargs": [{"shell": "ps"}],
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"expected_validate": (True, "valid config"),
|
||||
"expected_beacon": [
|
||||
{
|
||||
"salt_fun": "cmd.run",
|
||||
"ret": (("echo hello world",), {"shell": "ps"}),
|
||||
"args": ["echo hello world"],
|
||||
"kwargs": {"shell": "ps"},
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"config": [
|
||||
{"salt_fun": [{"cmd.run": {"args": "echo hello world"}}, "test.version"]}
|
||||
],
|
||||
"expected_validate": (False, "args key for fun cmd.run must be list"),
|
||||
"expected_beacon": None, # None != []
|
||||
},
|
||||
{
|
||||
"config": [
|
||||
{
|
||||
"salt_fun": [
|
||||
{
|
||||
"cmd.run": {
|
||||
"args": ["echo hello world"],
|
||||
"kwargs": {"shell": "ps"},
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"expected_validate": (
|
||||
False,
|
||||
"kwargs key for fun cmd.run must be list of key value pairs",
|
||||
),
|
||||
"expected_beacon": None,
|
||||
},
|
||||
{
|
||||
"config": [
|
||||
{
|
||||
"salt_fun": [
|
||||
{"cmd.run": {"args": ["echo hello world"], "kwargs": ["shell=ps"]}}
|
||||
]
|
||||
}
|
||||
],
|
||||
"expected_validate": (
|
||||
False,
|
||||
"{} is not a key / value pair".format("shell=ps"),
|
||||
),
|
||||
"expected_beacon": None,
|
||||
},
|
||||
{
|
||||
"config": [
|
||||
{
|
||||
"salt_fun": [
|
||||
{
|
||||
"cmd.run": {
|
||||
"args": ["echo hello world"],
|
||||
"kwargs": [{"shell": "ps"}],
|
||||
"bg": True,
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"expected_validate": (False, "key bg not allowed under fun cmd.run"),
|
||||
"expected_beacon": None,
|
||||
},
|
||||
{
|
||||
"config": [
|
||||
{
|
||||
"salt_fun": [
|
||||
{"bogus.fun": {"args": ["echo hello world"]}},
|
||||
"test.version",
|
||||
]
|
||||
}
|
||||
],
|
||||
"expected_validate": (False, "bogus.fun not in __salt__"),
|
||||
"expected_beacon": None,
|
||||
},
|
||||
{
|
||||
"config": [{"salt_fun": ["test.ping", "test.false"]}],
|
||||
"expected_validate": (True, "valid config"),
|
||||
"expected_beacon": [{"salt_fun": "test.ping", "ret": True}],
|
||||
},
|
||||
{
|
||||
"config": [{"salt_fun": ["test.false"]}],
|
||||
"expected_validate": (True, "valid config"),
|
||||
"expected_beacon": [],
|
||||
},
|
||||
{
|
||||
"config": [{"salt_fun": "test.ping"}],
|
||||
"expected_validate": (True, "valid config"),
|
||||
"expected_beacon": [{"salt_fun": "test.ping", "ret": True}],
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def mock_test_ping():
|
||||
return True
|
||||
|
||||
|
||||
def mock_test_version():
|
||||
return "3000"
|
||||
|
||||
|
||||
def mock_test(*args, **kwargs):
|
||||
return args, kwargs
|
||||
|
||||
|
||||
def mock_test_false():
|
||||
return False
|
||||
|
||||
|
||||
class SaltBeaconTestCase(TestCase, LoaderModuleMockMixin):
|
||||
"""
|
||||
Test case for salt.beacons.salt_monitor
|
||||
"""
|
||||
|
||||
def setup_loader_modules(self):
|
||||
return {
|
||||
salt_monitor: {
|
||||
"__salt__": {
|
||||
"test.ping": mock_test_ping,
|
||||
"test.version": mock_test_version,
|
||||
"cmd.run": mock_test,
|
||||
"test.false": mock_test_false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def test_validate(self):
|
||||
for i, config in enumerate(TEST_CONFIG):
|
||||
valid = salt_monitor.validate(config["config"])
|
||||
self.assertEqual(valid, config["expected_validate"])
|
||||
|
||||
def test_beacon(self):
|
||||
for config in TEST_CONFIG:
|
||||
if config["expected_beacon"] is None:
|
||||
continue
|
||||
events = salt_monitor.beacon(config["config"])
|
||||
self.assertEqual(events, config["expected_beacon"])
|
Loading…
Add table
Reference in a new issue