Skip to content
29 changes: 29 additions & 0 deletions nodescraper/plugins/inband/sysctl/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
###############################################################################
#
# MIT License
#
# Copyright (c) 2025 Advanced Micro Devices, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
###############################################################################
from .analyzer_args import SysctlAnalyzerArgs
from .sysctl_plugin import SysctlPlugin

__all__ = ["SysctlPlugin", "SysctlAnalyzerArgs"]
67 changes: 67 additions & 0 deletions nodescraper/plugins/inband/sysctl/analyzer_args.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
###############################################################################
#
# MIT License
#
# Copyright (c) 2025 Advanced Micro Devices, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
###############################################################################
from typing import Optional

from nodescraper.models import AnalyzerArgs
from nodescraper.plugins.inband.sysctl.sysctldata import SysctlDataModel


class SysctlAnalyzerArgs(AnalyzerArgs):
exp_vm_swappiness: Optional[int] = None
exp_vm_numa_balancing: Optional[int] = None
exp_vm_oom_kill_allocating_task: Optional[int] = None
exp_vm_compaction_proactiveness: Optional[int] = None
exp_vm_compact_unevictable_allowed: Optional[int] = None
exp_vm_extfrag_threshold: Optional[int] = None
exp_vm_zone_reclaim_mode: Optional[int] = None
exp_vm_dirty_background_ratio: Optional[int] = None
exp_vm_dirty_ratio: Optional[int] = None
exp_vm_dirty_writeback_centisecs: Optional[int] = None
exp_kernel_numa_balancing: Optional[int] = None

@classmethod
def build_from_model(cls, datamodel: SysctlDataModel) -> "SysctlAnalyzerArgs":
"""build analyzer args from data model

Args:
datamodel (SysctlDataModel): data model for plugin

Returns:
SysctlAnalyzerArgs: instance of analyzer args class
"""
return cls(
exp_vm_swappiness=datamodel.vm_swappiness,
exp_vm_numa_balancing=datamodel.vm_numa_balancing,
exp_vm_oom_kill_allocating_task=datamodel.vm_oom_kill_allocating_task,
exp_vm_compaction_proactiveness=datamodel.vm_compaction_proactiveness,
exp_vm_compact_unevictable_allowed=datamodel.vm_compact_unevictable_allowed,
exp_vm_extfrag_threshold=datamodel.vm_extfrag_threshold,
exp_vm_zone_reclaim_mode=datamodel.vm_zone_reclaim_mode,
exp_vm_dirty_background_ratio=datamodel.vm_dirty_background_ratio,
exp_vm_dirty_ratio=datamodel.vm_dirty_ratio,
exp_vm_dirty_writeback_centisecs=datamodel.vm_dirty_writeback_centisecs,
exp_kernel_numa_balancing=datamodel.kernel_numa_balancing,
)
82 changes: 82 additions & 0 deletions nodescraper/plugins/inband/sysctl/sysctl_analyzer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
###############################################################################
#
# MIT License
#
# Copyright (c) 2025 Advanced Micro Devices, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
###############################################################################

from typing import Optional

from nodescraper.enums import EventCategory, EventPriority, ExecutionStatus
from nodescraper.interfaces import DataAnalyzer
from nodescraper.models import TaskResult

from .analyzer_args import SysctlAnalyzerArgs
from .sysctldata import SysctlDataModel


class SysctlAnalyzer(DataAnalyzer[SysctlDataModel, SysctlAnalyzerArgs]):
"""Check sysctl matches expected sysctl details"""

DATA_MODEL = SysctlDataModel

def analyze_data(
self, data: SysctlDataModel, args: Optional[SysctlAnalyzerArgs] = None
) -> TaskResult:
"""Analyze the Sysctl data against expected Sysctl values."""
mismatches = {}

if not args:
args = SysctlAnalyzerArgs()

for exp_field_name, expected_value in args.model_dump(exclude_unset=True).items():

data_field_name = exp_field_name.removeprefix("exp_")
actual_value = getattr(data, data_field_name, None)

if actual_value is None:
mismatches[data_field_name] = {"expected": expected_value, "actual": "missing"}
elif actual_value != expected_value:
mismatches[data_field_name] = {"expected": expected_value, "actual": actual_value}

if mismatches:
self.result.status = ExecutionStatus.ERROR
self.result.message = f"{len(mismatches)} sysctl parameter(s) mismatched."
self.result.message = "Sysctl parameters mismatch detected."
self._log_event(
category=EventCategory.OS,
description="Sysctl mismatch detected",
data=mismatches,
priority=EventPriority.ERROR,
console_log=True,
)
else:
self._log_event(
category=EventCategory.OS,
description="All expected sysctl parameters matched",
priority=EventPriority.INFO,
console_log=True,
)
self.result.status = ExecutionStatus.OK
self.result.message = "All expected sysctl parameters match."

return self.result
99 changes: 99 additions & 0 deletions nodescraper/plugins/inband/sysctl/sysctl_collector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
###############################################################################
#
# MIT License
#
# Copyright (c) 2025 Advanced Micro Devices, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
###############################################################################

from nodescraper.base import InBandDataCollector
from nodescraper.enums import EventCategory, EventPriority, ExecutionStatus, OSFamily
from nodescraper.models import TaskResult

from .sysctldata import SysctlDataModel


class SysctlCollector(InBandDataCollector[SysctlDataModel, None]):
"""Collect sysctl kernel VM settings."""

DATA_MODEL = SysctlDataModel

def collect_data(
self,
args=None,
) -> tuple[TaskResult, SysctlDataModel | None]:
"""Collect sysctl VM tuning values from the system."""
values = {}

if self.system_info.os_family == OSFamily.WINDOWS:
self._log_event(
category=EventCategory.OS,
description="Windows is not supported for sysctl collection.",
priority=EventPriority.WARNING,
console_log=True,
)
return self.result, None

for field_name in SysctlDataModel.model_fields:
sysctl_key = field_name.replace("_", ".", 1)
res = self._run_sut_cmd(f"sysctl -n {sysctl_key}")

if res.exit_code == 0:
try:
values[field_name] = int(res.stdout.strip())
except ValueError:
self._log_event(
category=EventCategory.OS,
description=f"Invalid integer value for {sysctl_key}",
data={"stdout": res.stdout},
priority=EventPriority.ERROR,
console_log=True,
)
else:
self._log_event(
category=EventCategory.OS,
description=f"Error checking Linux system setting : {sysctl_key}",
data={"system_setting": sysctl_key, "exit_code": res.exit_code},
priority=EventPriority.WARNING,
console_log=True,
)

if values:
sysctl_data = SysctlDataModel(**values)
self._log_event(
category="OS",
description="Sysctl settings read",
data=sysctl_data.model_dump(),
priority=EventPriority.INFO,
)
self.result.message = "SYSCTL data collected"
self.result.status = ExecutionStatus.OK
else:
sysctl_data = None
self._log_event(
category=EventCategory.OS,
description="Sysctl settings not read",
priority=EventPriority.CRITICAL,
)
self.result.message = "Sysctl settings not read"
self.result.status = ExecutionStatus.ERROR

return self.result, sysctl_data
43 changes: 43 additions & 0 deletions nodescraper/plugins/inband/sysctl/sysctl_plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
###############################################################################
#
# MIT License
#
# Copyright (c) 2025 Advanced Micro Devices, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
###############################################################################
from nodescraper.base import InBandDataPlugin

from .analyzer_args import SysctlAnalyzerArgs
from .sysctl_analyzer import SysctlAnalyzer
from .sysctl_collector import SysctlCollector
from .sysctldata import SysctlDataModel


class SysctlPlugin(InBandDataPlugin[SysctlDataModel, None, SysctlAnalyzerArgs]):
"""Plugin for collection and analysis of BIOS data"""

DATA_MODEL = SysctlDataModel

COLLECTOR = SysctlCollector

ANALYZER = SysctlAnalyzer

ANALYZER_ARGS = SysctlAnalyzerArgs
42 changes: 42 additions & 0 deletions nodescraper/plugins/inband/sysctl/sysctldata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
###############################################################################
#
# MIT License
#
# Copyright (c) 2025 Advanced Micro Devices, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
###############################################################################
from typing import Optional

from nodescraper.models import DataModel


class SysctlDataModel(DataModel):
vm_swappiness: Optional[int] = None
vm_numa_balancing: Optional[int] = None
vm_oom_kill_allocating_task: Optional[int] = None
vm_compaction_proactiveness: Optional[int] = None
vm_compact_unevictable_allowed: Optional[int] = None
vm_extfrag_threshold: Optional[int] = None
vm_zone_reclaim_mode: Optional[int] = None
vm_dirty_background_ratio: Optional[int] = None
vm_dirty_ratio: Optional[int] = None
vm_dirty_writeback_centisecs: Optional[int] = None
kernel_numa_balancing: Optional[int] = None
Loading