Added sysfs state module to manage kernel objects

This commit is contained in:
piterpunk 2021-05-06 23:47:40 -03:00 committed by Twangboy
parent 4206994aad
commit d1932fa3c5
No known key found for this signature in database
GPG key ID: ED267D5C0DE6F8A6
5 changed files with 197 additions and 0 deletions

1
changelog/60154.added Normal file
View file

@ -0,0 +1 @@
State module to manage SysFS attributes

View file

@ -303,6 +303,7 @@ state modules
supervisord
svn
sysctl
sysfs
syslog_ng
sysrc
telemetry_alert

View file

@ -0,0 +1,5 @@
salt.states.sysfs
==================
.. automodule:: salt.states.sysfs
:members:

73
salt/states/sysfs.py Normal file
View 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

View 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