From 3be8ba454af9226a126d5e2b69a52948d6f631f3 Mon Sep 17 00:00:00 2001 From: Vrinda Date: Tue, 23 Jun 2026 06:03:38 +0000 Subject: [PATCH] refactor:move AmdMobileApuPlugin to platforms/mobile_apu --- nodescraper/plugins/__init__.py | 5 ++ .../plugins/platforms/mobile_apu/__init__.py | 0 .../mobile_apu/amd_mobile_apu_analyzer.py | 72 +++++++++++++++++++ .../amd_mobile_apu_analyzer_args.py | 36 ++++++++++ .../mobile_apu/amd_mobile_apu_collector.py | 62 ++++++++++++++++ .../mobile_apu/amd_mobile_apu_data_model.py | 33 +++++++++ .../mobile_apu/amd_mobile_apu_plugin.py | 38 ++++++++++ 7 files changed, 246 insertions(+) create mode 100644 nodescraper/plugins/platforms/mobile_apu/__init__.py create mode 100644 nodescraper/plugins/platforms/mobile_apu/amd_mobile_apu_analyzer.py create mode 100644 nodescraper/plugins/platforms/mobile_apu/amd_mobile_apu_analyzer_args.py create mode 100644 nodescraper/plugins/platforms/mobile_apu/amd_mobile_apu_collector.py create mode 100644 nodescraper/plugins/platforms/mobile_apu/amd_mobile_apu_data_model.py create mode 100644 nodescraper/plugins/platforms/mobile_apu/amd_mobile_apu_plugin.py diff --git a/nodescraper/plugins/__init__.py b/nodescraper/plugins/__init__.py index fdb6d9c8..d64d1591 100644 --- a/nodescraper/plugins/__init__.py +++ b/nodescraper/plugins/__init__.py @@ -23,3 +23,8 @@ # SOFTWARE. # ############################################################################### +from .platforms.mobile_apu.amd_mobile_apu_plugin import AmdMobileApuPlugin + +__all__ = [ + "AmdMobileApuPlugin", +] diff --git a/nodescraper/plugins/platforms/mobile_apu/__init__.py b/nodescraper/plugins/platforms/mobile_apu/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/nodescraper/plugins/platforms/mobile_apu/amd_mobile_apu_analyzer.py b/nodescraper/plugins/platforms/mobile_apu/amd_mobile_apu_analyzer.py new file mode 100644 index 00000000..eaefcdd2 --- /dev/null +++ b/nodescraper/plugins/platforms/mobile_apu/amd_mobile_apu_analyzer.py @@ -0,0 +1,72 @@ +############################################################################### +# +# 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 ExecutionStatus +from nodescraper.interfaces import DataAnalyzer +from nodescraper.models import TaskResult + +from .amd_mobile_apu_analyzer_args import AmdMobileApuAnalyzerArgs +from .amd_mobile_apu_data_model import AmdMobileApuDataModel + + +class AmdMobileApuAnalyzer(DataAnalyzer[AmdMobileApuDataModel, AmdMobileApuAnalyzerArgs]): + DATA_MODEL = AmdMobileApuDataModel + + def analyze( + self, + data_model: AmdMobileApuDataModel, + analyzer_args: AmdMobileApuAnalyzerArgs | None = None, + ): + if data_model.pl1_power_limit_mw is None: + return { + "status": "WARN", + "messages": ["Powercap sensor unavailable (likely WSL or missing kernel module)."], + } + + actual = data_model.pl1_power_limit_mw + if analyzer_args and analyzer_args.exp_pl1_power_limit_mw is not None: + expected = analyzer_args.exp_pl1_power_limit_mw + if abs(actual - expected) > 1000: + return { + "status": "FAIL", + "messages": [f"PL1 mismatch: expected {expected} mW, got {actual} mW"], + } + + return {"status": "PASS", "messages": []} + + def analyze_data( + self, + data: AmdMobileApuDataModel, + args: Optional[AmdMobileApuAnalyzerArgs] = None, + ) -> TaskResult: + result = self.analyze(data, args) + status_map = { + "PASS": ExecutionStatus.OK, + "WARN": ExecutionStatus.WARNING, + "FAIL": ExecutionStatus.ERROR, + } + return TaskResult(status=status_map[result["status"]], message="; ".join(result["messages"])) \ No newline at end of file diff --git a/nodescraper/plugins/platforms/mobile_apu/amd_mobile_apu_analyzer_args.py b/nodescraper/plugins/platforms/mobile_apu/amd_mobile_apu_analyzer_args.py new file mode 100644 index 00000000..ea63affb --- /dev/null +++ b/nodescraper/plugins/platforms/mobile_apu/amd_mobile_apu_analyzer_args.py @@ -0,0 +1,36 @@ + + +############################################################################### +# +# 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 pydantic import Field + +from nodescraper.models import AnalyzerArgs + + +class AmdMobileApuAnalyzerArgs(AnalyzerArgs): + exp_pl1_power_limit_mw: int | None = Field( + None, description="Expected sustained power limit in milliwatts" + ) diff --git a/nodescraper/plugins/platforms/mobile_apu/amd_mobile_apu_collector.py b/nodescraper/plugins/platforms/mobile_apu/amd_mobile_apu_collector.py new file mode 100644 index 00000000..27fa289f --- /dev/null +++ b/nodescraper/plugins/platforms/mobile_apu/amd_mobile_apu_collector.py @@ -0,0 +1,62 @@ +############################################################################### +# +# 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 pathlib import Path +from typing import Optional + +from nodescraper.base import InBandDataCollector +from nodescraper.enums import ExecutionStatus +from nodescraper.models import TaskResult + +from .amd_mobile_apu_data_model import AmdMobileApuDataModel + + +class AmdMobileApuCollector(InBandDataCollector[AmdMobileApuDataModel, None]): + DATA_MODEL = AmdMobileApuDataModel + + def _read_int(self, path: str) -> int | None: + try: + return int(Path(path).read_text().strip()) + except (OSError, IOError, ValueError): + return None + + def collect(self) -> AmdMobileApuDataModel: + pl1_power_limit_mw = self._read_int( + "/sys/class/powercap/intel-rapl:0/constraint_0_power_limit_uw" + ) + if pl1_power_limit_mw is not None: + pl1_power_limit_mw //= 1000 + + cpu_temp_millidegree = self._read_int("/sys/class/thermal/thermal_zone0/temp") + + return AmdMobileApuDataModel( + pl1_power_limit_mw=pl1_power_limit_mw, + cpu_temp_millidegree=cpu_temp_millidegree, + ) + + def collect_data( + self, args: Optional[None] = None + ) -> tuple[TaskResult, Optional[AmdMobileApuDataModel]]: + return TaskResult(status=ExecutionStatus.OK), self.collect() \ No newline at end of file diff --git a/nodescraper/plugins/platforms/mobile_apu/amd_mobile_apu_data_model.py b/nodescraper/plugins/platforms/mobile_apu/amd_mobile_apu_data_model.py new file mode 100644 index 00000000..a4c4dabc --- /dev/null +++ b/nodescraper/plugins/platforms/mobile_apu/amd_mobile_apu_data_model.py @@ -0,0 +1,33 @@ +############################################################################### +# +# 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 pydantic import BaseModel, Field + +from nodescraper.models import DataModel + + +class AmdMobileApuDataModel(DataModel): + pl1_power_limit_mw: int | None = Field(None, description="Sustained power limit in milliwatts") + cpu_temp_millidegree: int | None = Field(None, description="CPU temperature in millidegrees Celsius") \ No newline at end of file diff --git a/nodescraper/plugins/platforms/mobile_apu/amd_mobile_apu_plugin.py b/nodescraper/plugins/platforms/mobile_apu/amd_mobile_apu_plugin.py new file mode 100644 index 00000000..a0af63e0 --- /dev/null +++ b/nodescraper/plugins/platforms/mobile_apu/amd_mobile_apu_plugin.py @@ -0,0 +1,38 @@ +############################################################################### +# +# 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 .amd_mobile_apu_analyzer import AmdMobileApuAnalyzer +from .amd_mobile_apu_analyzer_args import AmdMobileApuAnalyzerArgs +from .amd_mobile_apu_collector import AmdMobileApuCollector +from .amd_mobile_apu_data_model import AmdMobileApuDataModel + + +class AmdMobileApuPlugin(InBandDataPlugin[AmdMobileApuDataModel, None, AmdMobileApuAnalyzerArgs]): + DATA_MODEL = AmdMobileApuDataModel + COLLECTOR = AmdMobileApuCollector + ANALYZER = AmdMobileApuAnalyzer + ANALYZER_ARGS = AmdMobileApuAnalyzerArgs \ No newline at end of file