mirror of
https://github.com/saltstack/salt.git
synced 2025-04-16 09:40:20 +00:00
Added sysfs state module to manage kernel objects
This commit is contained in:
parent
4206994aad
commit
d1932fa3c5
5 changed files with 197 additions and 0 deletions
1
changelog/60154.added
Normal file
1
changelog/60154.added
Normal file
|
@ -0,0 +1 @@
|
|||
State module to manage SysFS attributes
|
|
@ -303,6 +303,7 @@ state modules
|
|||
supervisord
|
||||
svn
|
||||
sysctl
|
||||
sysfs
|
||||
syslog_ng
|
||||
sysrc
|
||||
telemetry_alert
|
||||
|
|
5
doc/ref/states/all/salt.states.sysfs.rst
Normal file
5
doc/ref/states/all/salt.states.sysfs.rst
Normal file
|
@ -0,0 +1,5 @@
|
|||
salt.states.sysfs
|
||||
==================
|
||||
|
||||
.. automodule:: salt.states.sysfs
|
||||
:members:
|
73
salt/states/sysfs.py
Normal file
73
salt/states/sysfs.py
Normal file
|
@ -0,0 +1,73 @@
|
|||
"""
|
||||
Configuration of the kernel using sysfs
|
||||
=======================================
|
||||
|
||||
Control the kernel object attributes exported by sysfs
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
kernel/mm/transparent_hugepage/enabled
|
||||
sysfs.present:
|
||||
- value: never
|
||||
|
||||
.. versionadded: TBD
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
|
||||
def __virtual__():
|
||||
"""
|
||||
This state is only available on Minions which support sysctl
|
||||
"""
|
||||
if "sysfs.attr" in __salt__:
|
||||
return True
|
||||
return (False, "sysfs module could not be loaded")
|
||||
|
||||
|
||||
def present(name, value, config=None):
|
||||
"""
|
||||
Ensure that the named sysfs attribute is set with the defined value
|
||||
|
||||
name
|
||||
The name of the sysfs attribute to edit
|
||||
|
||||
value
|
||||
The sysfs value to apply
|
||||
|
||||
"""
|
||||
ret = {"name": name, "result": True, "changes": {}, "comment": ""}
|
||||
|
||||
current = __salt__["sysfs.read"](name)
|
||||
if current is False:
|
||||
ret["result"] = False
|
||||
ret["comment"] = "SysFS attribute {} doesn't exist.".format(name)
|
||||
else:
|
||||
# if the return is a dict, the "name" is an object not an attribute
|
||||
if isinstance(current, dict):
|
||||
ret["result"] = False
|
||||
ret["comment"] = "{} is not a SysFS attribute.".format(name)
|
||||
else:
|
||||
# some attribute files lists all available options and the selected one between []
|
||||
if isinstance(current, str):
|
||||
current = re.sub(r"(.*\[|\].*)", "", current)
|
||||
if value == current:
|
||||
ret["result"] = True
|
||||
ret["comment"] = "SysFS attribute {} is already set.".format(name)
|
||||
else:
|
||||
ret["result"] = None
|
||||
|
||||
if ret["result"] is None:
|
||||
if __opts__["test"]:
|
||||
ret["comment"] = "SysFS attribute {} set to be changed.".format(name)
|
||||
else:
|
||||
update = __salt__["sysfs.write"](name, value)
|
||||
if not update:
|
||||
ret["result"] = False
|
||||
ret["comment"] = "Failed to set {} to {}".format(name, value)
|
||||
else:
|
||||
ret["result"] = True
|
||||
ret["changes"] = {name: value}
|
||||
ret["comment"] = "Updated SysFS attribute {} to {}".format(name, value)
|
||||
|
||||
return ret
|
117
tests/pytests/unit/states/test_sysfs.py
Normal file
117
tests/pytests/unit/states/test_sysfs.py
Normal file
|
@ -0,0 +1,117 @@
|
|||
"""
|
||||
:codeauthor: Piter Punk <piterpunk@slackware.com>
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import salt.states.sysfs as sysfs
|
||||
from tests.support.mock import MagicMock, patch
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def configure_loader_modules():
|
||||
return {sysfs: {}}
|
||||
|
||||
|
||||
def test_if_the_sysfs_attribute_exists():
|
||||
"""
|
||||
Test sysfs.present for a non-existent attribute
|
||||
"""
|
||||
name = "block/sda/queue/this_does_not_exist"
|
||||
value = "none"
|
||||
comment = "SysFS attribute {} doesn't exist.".format(name)
|
||||
ret = {"name": name, "result": False, "changes": {}, "comment": comment}
|
||||
|
||||
mock_read = MagicMock(return_value=False)
|
||||
with patch.dict(sysfs.__salt__, {"sysfs.read": mock_read}):
|
||||
assert sysfs.present(name, value) == ret
|
||||
|
||||
|
||||
def test_name_is_an_object_and_not_an_attribute():
|
||||
"""
|
||||
Test sysfs.present targeting an object and not one of its attributes
|
||||
"""
|
||||
name = "block/sda/queue"
|
||||
value = "none"
|
||||
comment = "{} is not a SysFS attribute.".format(name)
|
||||
ret = {"name": name, "result": False, "changes": {}, "comment": comment}
|
||||
|
||||
read_from_sysfs = {
|
||||
"rotational": 1,
|
||||
"rq_affinity": 1,
|
||||
"scheduler": "[none] mq-deadline",
|
||||
}
|
||||
|
||||
mock_read = MagicMock(return_value=read_from_sysfs)
|
||||
with patch.dict(sysfs.__salt__, {"sysfs.read": mock_read}):
|
||||
assert sysfs.present(name, value) == ret
|
||||
|
||||
|
||||
def test_already_set():
|
||||
"""
|
||||
Test sysfs.present with equal old and new values
|
||||
"""
|
||||
name = "block/sda/queue"
|
||||
value = "none"
|
||||
comment = "SysFS attribute {} is already set.".format(name)
|
||||
ret = {"name": name, "result": True, "changes": {}, "comment": comment}
|
||||
|
||||
read_from_sysfs = "[none] mq-deadline"
|
||||
|
||||
mock_read = MagicMock(return_value=read_from_sysfs)
|
||||
with patch.dict(sysfs.__salt__, {"sysfs.read": mock_read}):
|
||||
assert sysfs.present(name, value) == ret
|
||||
|
||||
|
||||
def test_set_new_value_with_test_equals_true():
|
||||
"""
|
||||
Test sysfs.present setting a new value
|
||||
"""
|
||||
name = "devices/system/cpu/cpufreq/policy0"
|
||||
value = "powersave"
|
||||
comment = "SysFS attribute {} set to be changed.".format(name)
|
||||
ret = {"name": name, "result": None, "changes": {}, "comment": comment}
|
||||
|
||||
read_from_sysfs = "performance"
|
||||
|
||||
mock_read = MagicMock(return_value=read_from_sysfs)
|
||||
with patch.dict(sysfs.__opts__, {"test": True}):
|
||||
with patch.dict(sysfs.__salt__, {"sysfs.read": mock_read}):
|
||||
assert sysfs.present(name, value) == ret
|
||||
|
||||
|
||||
def test_set_new_value_with_success():
|
||||
"""
|
||||
Test sysfs.present setting a new value
|
||||
"""
|
||||
name = "block/sda/queue/scheduler"
|
||||
value = "mq-deadline"
|
||||
comment = "Updated SysFS attribute {} to {}".format(name, value)
|
||||
ret = {"name": name, "result": True, "changes": {name: value}, "comment": comment}
|
||||
|
||||
read_from_sysfs = "[none] mq-deadline"
|
||||
|
||||
mock_read = MagicMock(return_value=read_from_sysfs)
|
||||
with patch.dict(sysfs.__opts__, {"test": False}):
|
||||
with patch.dict(sysfs.__salt__, {"sysfs.read": mock_read}):
|
||||
mock_write = MagicMock(return_value=True)
|
||||
with patch.dict(sysfs.__salt__, {"sysfs.write": mock_write}):
|
||||
assert sysfs.present(name, value) == ret
|
||||
|
||||
|
||||
def test_set_new_value_with_failure():
|
||||
"""
|
||||
Test sysfs.present failure writing the value
|
||||
"""
|
||||
name = "block/sda/queue/scheduler"
|
||||
value = "imaginary_scheduler"
|
||||
comment = "Failed to set {} to {}".format(name, value)
|
||||
ret = {"name": name, "result": False, "changes": {}, "comment": comment}
|
||||
|
||||
read_from_sysfs = "[none] mq-deadline"
|
||||
|
||||
mock_read = MagicMock(return_value=read_from_sysfs)
|
||||
with patch.dict(sysfs.__opts__, {"test": False}):
|
||||
with patch.dict(sysfs.__salt__, {"sysfs.read": mock_read}):
|
||||
mock_write = MagicMock(return_value=False)
|
||||
with patch.dict(sysfs.__salt__, {"sysfs.write": mock_write}):
|
||||
assert sysfs.present(name, value) == ret
|
Loading…
Add table
Reference in a new issue