From f0a146dba0f50749a3ba96c111f8e2b7b6edca1b Mon Sep 17 00:00:00 2001 From: Jaspal Singh Date: Tue, 2 Dec 2025 11:37:45 -0600 Subject: [PATCH 1/7] techsupport changes --- .../plugins/inband/package/analyzer_args.py | 3 ++ .../inband/package/package_collector.py | 44 ++++++++++++++++++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/nodescraper/plugins/inband/package/analyzer_args.py b/nodescraper/plugins/inband/package/analyzer_args.py index cbd7ebad..881f965e 100644 --- a/nodescraper/plugins/inband/package/analyzer_args.py +++ b/nodescraper/plugins/inband/package/analyzer_args.py @@ -34,6 +34,9 @@ class PackageAnalyzerArgs(AnalyzerArgs): exp_package_ver: Dict[str, Optional[str]] = Field(default_factory=dict) regex_match: bool = False + rocm_regex: str = ( + r"ocl-icd|kfdtest|llvm-amd|miopen|half|hip|hcc|hsa|rocm|atmi|comgr|composa|amd-smi|aomp|amdgpu|rock|mivision|migraph|rocprofiler|roctracer|rocbl|hipify|rocsol|rocthr|rocff|rocalu|rocprim|rocrand|rccl|rocspar|rdc|rocwmma|rpp|openmp|amdfwflash|ocl|opencl" + ) @classmethod def build_from_model(cls, datamodel: PackageDataModel) -> "PackageAnalyzerArgs": diff --git a/nodescraper/plugins/inband/package/package_collector.py b/nodescraper/plugins/inband/package/package_collector.py index bc3a65b3..ab6975e7 100644 --- a/nodescraper/plugins/inband/package/package_collector.py +++ b/nodescraper/plugins/inband/package/package_collector.py @@ -34,10 +34,11 @@ from nodescraper.models import TaskResult from nodescraper.utils import get_exception_details +from .analyzer_args import PackageAnalyzerArgs from .packagedata import PackageDataModel -class PackageCollector(InBandDataCollector[PackageDataModel, None]): +class PackageCollector(InBandDataCollector[PackageDataModel, PackageAnalyzerArgs]): """Collecting Package information from the system""" DATA_MODEL = PackageDataModel @@ -181,9 +182,34 @@ def _handle_command_failure(self, command_artifact: CommandArtifact): self.result.message = "Failed to run Package Manager command" self.result.status = ExecutionStatus.EXECUTION_FAILURE - def collect_data(self, args=None) -> tuple[TaskResult, Optional[PackageDataModel]]: + def _filter_rocm_packages(self, packages: dict[str, str], rocm_pattern: str) -> dict[str, str]: + """Filter ROCm-related packages from a package dictionary. + + This method searches package names for ROCm-related patterns and returns + only the matching packages. + + Args: + packages (dict[str, str]): Dictionary with package names as keys and versions as values. + rocm_pattern (str): Regex pattern to match ROCm-related package names. + + Returns: + dict[str, str]: Filtered dictionary containing only ROCm-related packages. + """ + rocm_packages = {} + pattern = re.compile(rocm_pattern, re.IGNORECASE) + for package_name, version in packages.items(): + if pattern.search(package_name): + rocm_packages[package_name] = version + return rocm_packages + + def collect_data( + self, args: Optional[PackageAnalyzerArgs] = None + ) -> tuple[TaskResult, Optional[PackageDataModel]]: """Collect package information from the system. + Args: + args (Optional[PackageAnalyzerArgs]): Optional arguments containing ROCm regex pattern. + Returns: tuple[TaskResult, Optional[PackageDataModel]]: tuple containing the task result and a PackageDataModel instance with the collected package information, or None if there was an error. @@ -205,6 +231,20 @@ def collect_data(self, args=None) -> tuple[TaskResult, Optional[PackageDataModel self.result.message = "Unsupported OS" self.result.status = ExecutionStatus.NOT_RAN return self.result, None + + # Filter and log ROCm packages if on Linux + if self.system_info.os_family == OSFamily.LINUX and packages: + # Get ROCm pattern from args or use default + rocm_pattern = args.rocm_regex if args else PackageAnalyzerArgs().rocm_regex + rocm_packages = self._filter_rocm_packages(packages, rocm_pattern) + if rocm_packages: + self._log_event( + category=EventCategory.OS, + description=f"Found {len(rocm_packages)} ROCm-related packages installed", + priority=EventPriority.INFO, + data={"rocm_packages": sorted(rocm_packages.keys())}, + ) + try: package_model = PackageDataModel(version_info=packages) except ValidationError as val_err: From 38dd4a7ef4cdc591a625606c2ff5bb4e2749fcf3 Mon Sep 17 00:00:00 2001 From: Jaspal Singh Date: Wed, 3 Dec 2025 11:33:37 -0600 Subject: [PATCH 2/7] regex changes --- nodescraper/plugins/inband/package/package_collector.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/nodescraper/plugins/inband/package/package_collector.py b/nodescraper/plugins/inband/package/package_collector.py index ab6975e7..9da89f5e 100644 --- a/nodescraper/plugins/inband/package/package_collector.py +++ b/nodescraper/plugins/inband/package/package_collector.py @@ -238,9 +238,11 @@ def collect_data( rocm_pattern = args.rocm_regex if args else PackageAnalyzerArgs().rocm_regex rocm_packages = self._filter_rocm_packages(packages, rocm_pattern) if rocm_packages: + self.result.message = f"Found {len(rocm_packages)} ROCm-related packages installed as per given regex: {rocm_pattern}" + self.result.status = ExecutionStatus.ERROR self._log_event( category=EventCategory.OS, - description=f"Found {len(rocm_packages)} ROCm-related packages installed", + description=f"Found {len(rocm_packages)} ROCm-related packages installed as per given regex: {rocm_pattern}", priority=EventPriority.INFO, data={"rocm_packages": sorted(rocm_packages.keys())}, ) From c60b3b0a8ddb618f698d87a86f9de5abd553e127 Mon Sep 17 00:00:00 2001 From: Jaspal Singh Date: Wed, 3 Dec 2025 11:37:54 -0600 Subject: [PATCH 3/7] status fix --- nodescraper/plugins/inband/package/package_collector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nodescraper/plugins/inband/package/package_collector.py b/nodescraper/plugins/inband/package/package_collector.py index 9da89f5e..9eecd835 100644 --- a/nodescraper/plugins/inband/package/package_collector.py +++ b/nodescraper/plugins/inband/package/package_collector.py @@ -239,7 +239,7 @@ def collect_data( rocm_packages = self._filter_rocm_packages(packages, rocm_pattern) if rocm_packages: self.result.message = f"Found {len(rocm_packages)} ROCm-related packages installed as per given regex: {rocm_pattern}" - self.result.status = ExecutionStatus.ERROR + self.result.status = ExecutionStatus.OK self._log_event( category=EventCategory.OS, description=f"Found {len(rocm_packages)} ROCm-related packages installed as per given regex: {rocm_pattern}", From 3e5540aeb2e162ef72312ffde311fc45b572f85c Mon Sep 17 00:00:00 2001 From: jaspals Date: Thu, 4 Dec 2025 17:52:58 +0000 Subject: [PATCH 4/7] fixed message --- nodescraper/plugins/inband/package/package_collector.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/nodescraper/plugins/inband/package/package_collector.py b/nodescraper/plugins/inband/package/package_collector.py index 9eecd835..7963c634 100644 --- a/nodescraper/plugins/inband/package/package_collector.py +++ b/nodescraper/plugins/inband/package/package_collector.py @@ -238,7 +238,9 @@ def collect_data( rocm_pattern = args.rocm_regex if args else PackageAnalyzerArgs().rocm_regex rocm_packages = self._filter_rocm_packages(packages, rocm_pattern) if rocm_packages: - self.result.message = f"Found {len(rocm_packages)} ROCm-related packages installed as per given regex: {rocm_pattern}" + self.result.message = ( + f"Found {len(rocm_packages)} ROCm-related packages installed as per given regex" + ) self.result.status = ExecutionStatus.OK self._log_event( category=EventCategory.OS, From f43bac37b5be8e5d8423b0b28ab4540ea66de19d Mon Sep 17 00:00:00 2001 From: jaspals Date: Thu, 4 Dec 2025 19:37:31 +0000 Subject: [PATCH 5/7] added unit tests --- .../fixtures/package_plugin_config.json | 3 + test/unit/plugin/test_package_collector.py | 143 ++++++++++++++++++ 2 files changed, 146 insertions(+) diff --git a/test/functional/fixtures/package_plugin_config.json b/test/functional/fixtures/package_plugin_config.json index 45d6e76b..d0ad760c 100644 --- a/test/functional/fixtures/package_plugin_config.json +++ b/test/functional/fixtures/package_plugin_config.json @@ -2,6 +2,9 @@ "global_args": {}, "plugins": { "PackagePlugin": { + "collection_args": { + "rocm_regex": "rocm|hip|hsa|amdgpu" + }, "analysis_args": { "exp_package_ver": { "gcc": "11.4.0" diff --git a/test/unit/plugin/test_package_collector.py b/test/unit/plugin/test_package_collector.py index 8cf6f2fa..00a2694e 100644 --- a/test/unit/plugin/test_package_collector.py +++ b/test/unit/plugin/test_package_collector.py @@ -222,3 +222,146 @@ def test_bad_splits_ubuntu(collector, conn_mock, command_results): ] res, _ = collector.collect_data() assert res.status == ExecutionStatus.OK + + +def test_rocm_package_filtering_default_regex(collector, conn_mock, command_results): + """Test ROCm package filtering with default regex pattern.""" + # Mock Ubuntu system with ROCm packages + ubuntu_packages = """rocm-core 5.7.0 + hip-runtime-amd 5.7.0 + hsa-rocr 1.9.0 + amdgpu-dkms 6.3.6 + gcc 11.4.0 + python3 3.10.12""" + + conn_mock.run_command.side_effect = [ + CommandArtifact( + command="", + exit_code=0, + stdout=command_results["ubuntu_rel"], + stderr="", + ), + CommandArtifact( + command="", + exit_code=0, + stdout=ubuntu_packages, + stderr="", + ), + ] + + res, data = collector.collect_data() + assert res.status == ExecutionStatus.OK + # Check that ROCm packages are found + assert "rocm" in res.message.lower() + assert data is not None + # Verify all packages are collected + assert len(data.version_info) == 6 + + +def test_rocm_package_filtering_custom_regex(collector, conn_mock, command_results): + """Test ROCm package filtering with custom regex pattern.""" + from nodescraper.plugins.inband.package.analyzer_args import PackageAnalyzerArgs + + # Mock Ubuntu system with ROCm packages + ubuntu_packages = """rocm-core 5.7.0 + hip-runtime-amd 5.7.0 + hsa-rocr 1.9.0 + amdgpu-dkms 6.3.6 + gcc 11.4.0 + python3 3.10.12""" + + conn_mock.run_command.side_effect = [ + CommandArtifact( + command="", + exit_code=0, + stdout=command_results["ubuntu_rel"], + stderr="", + ), + CommandArtifact( + command="", + exit_code=0, + stdout=ubuntu_packages, + stderr="", + ), + ] + + # Use custom regex that only matches 'rocm' and 'hip' + args = PackageAnalyzerArgs(rocm_regex="rocm|hip") + res, data = collector.collect_data(args) + assert res.status == ExecutionStatus.OK + # Check that ROCm packages are found + assert "found 2 rocm-related packages" in res.message.lower() + assert data is not None + + +def test_rocm_package_filtering_no_matches(collector, conn_mock, command_results): + """Test ROCm package filtering when no ROCm packages are installed.""" + from nodescraper.plugins.inband.package.analyzer_args import PackageAnalyzerArgs + + # Mock Ubuntu system without ROCm packages + ubuntu_packages = """gcc 11.4.0 + python3 3.10.12 + vim 8.2.3995""" + + conn_mock.run_command.side_effect = [ + CommandArtifact( + command="", + exit_code=0, + stdout=command_results["ubuntu_rel"], + stderr="", + ), + CommandArtifact( + command="", + exit_code=0, + stdout=ubuntu_packages, + stderr="", + ), + ] + + args = PackageAnalyzerArgs(rocm_regex="rocm|hip|hsa") + res, data = collector.collect_data(args) + assert res.status == ExecutionStatus.OK + # No ROCm packages found, so message should not mention them + assert "rocm" not in res.message.lower() or res.message == "" + assert data is not None + assert len(data.version_info) == 3 + + +def test_filter_rocm_packages_method(collector): + """Test _filter_rocm_packages method directly.""" + packages = { + "rocm-core": "5.7.0", + "hip-runtime-amd": "5.7.0", + "hsa-rocr": "1.9.0", + "amdgpu-dkms": "6.3.6", + "gcc": "11.4.0", + "python3": "3.10.12", + } + + # Test with default-like pattern + rocm_pattern = "rocm|hip|hsa|amdgpu" + filtered = collector._filter_rocm_packages(packages, rocm_pattern) + + assert len(filtered) == 4 + assert "rocm-core" in filtered + assert "hip-runtime-amd" in filtered + assert "hsa-rocr" in filtered + assert "amdgpu-dkms" in filtered + assert "gcc" not in filtered + assert "python3" not in filtered + + +def test_filter_rocm_packages_case_insensitive(collector): + """Test that ROCm package filtering is case-insensitive.""" + packages = { + "ROCM-Core": "5.7.0", + "HIP-Runtime-AMD": "5.7.0", + "gcc": "11.4.0", + } + + rocm_pattern = "rocm|hip" + filtered = collector._filter_rocm_packages(packages, rocm_pattern) + + assert len(filtered) == 2 + assert "ROCM-Core" in filtered + assert "HIP-Runtime-AMD" in filtered From f896e7ff7976795a792f9880110feaba9a7b075e Mon Sep 17 00:00:00 2001 From: jaspals Date: Fri, 5 Dec 2025 21:00:39 +0000 Subject: [PATCH 6/7] changes as per review comments --- .../plugins/inband/package/analyzer_args.py | 10 +++++++++- .../plugins/inband/package/package_collector.py | 15 ++++++++++----- nodescraper/plugins/inband/package/packagedata.py | 4 ++++ .../fixtures/package_plugin_config.json | 3 ++- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/nodescraper/plugins/inband/package/analyzer_args.py b/nodescraper/plugins/inband/package/analyzer_args.py index 881f965e..617ae370 100644 --- a/nodescraper/plugins/inband/package/analyzer_args.py +++ b/nodescraper/plugins/inband/package/analyzer_args.py @@ -37,7 +37,15 @@ class PackageAnalyzerArgs(AnalyzerArgs): rocm_regex: str = ( r"ocl-icd|kfdtest|llvm-amd|miopen|half|hip|hcc|hsa|rocm|atmi|comgr|composa|amd-smi|aomp|amdgpu|rock|mivision|migraph|rocprofiler|roctracer|rocbl|hipify|rocsol|rocthr|rocff|rocalu|rocprim|rocrand|rccl|rocspar|rdc|rocwmma|rpp|openmp|amdfwflash|ocl|opencl" ) + enable_rocm_regex: bool = False @classmethod def build_from_model(cls, datamodel: PackageDataModel) -> "PackageAnalyzerArgs": - return cls(exp_package_ver=datamodel.version_info) + # Use custom rocm_regex from collection_args if enable_rocm_regex is true + if datamodel.enable_rocm_regex and datamodel.rocm_regex: + rocm_regex = datamodel.rocm_regex + else: + # Use default rocm_regex + rocm_regex = PackageAnalyzerArgs().rocm_regex + + return cls(exp_package_ver=datamodel.version_info, rocm_regex=rocm_regex) diff --git a/nodescraper/plugins/inband/package/package_collector.py b/nodescraper/plugins/inband/package/package_collector.py index 7963c634..f136c1e6 100644 --- a/nodescraper/plugins/inband/package/package_collector.py +++ b/nodescraper/plugins/inband/package/package_collector.py @@ -236,21 +236,26 @@ def collect_data( if self.system_info.os_family == OSFamily.LINUX and packages: # Get ROCm pattern from args or use default rocm_pattern = args.rocm_regex if args else PackageAnalyzerArgs().rocm_regex + self.logger.info("Using rocm_pattern: %s", rocm_pattern) rocm_packages = self._filter_rocm_packages(packages, rocm_pattern) if rocm_packages: - self.result.message = ( - f"Found {len(rocm_packages)} ROCm-related packages installed as per given regex" - ) + self.result.message = f"Found {len(rocm_packages)} ROCm-related packages installed" self.result.status = ExecutionStatus.OK self._log_event( category=EventCategory.OS, - description=f"Found {len(rocm_packages)} ROCm-related packages installed as per given regex: {rocm_pattern}", + description=f"Found {len(rocm_packages)} ROCm-related packages installed", priority=EventPriority.INFO, data={"rocm_packages": sorted(rocm_packages.keys())}, ) + # Extract rocm_regex and enable_rocm_regex from args if provided + rocm_regex = args.rocm_regex if args else "" + enable_rocm_regex = getattr(args, "enable_rocm_regex", False) if args else False + try: - package_model = PackageDataModel(version_info=packages) + package_model = PackageDataModel( + version_info=packages, rocm_regex=rocm_regex, enable_rocm_regex=enable_rocm_regex + ) except ValidationError as val_err: self._log_event( category=EventCategory.RUNTIME, diff --git a/nodescraper/plugins/inband/package/packagedata.py b/nodescraper/plugins/inband/package/packagedata.py index c1943307..ea95e1c5 100644 --- a/nodescraper/plugins/inband/package/packagedata.py +++ b/nodescraper/plugins/inband/package/packagedata.py @@ -32,6 +32,10 @@ class PackageDataModel(DataModel): Attributes: version_info (dict[str, str]): The version information for the package Key is the package name and value is the version of the package + rocm_regex (str): Regular expression pattern for ROCm package filtering + enable_rocm_regex (bool): Whether to use custom ROCm regex from collection_args """ version_info: dict[str, str] + rocm_regex: str = "" + enable_rocm_regex: bool = False diff --git a/test/functional/fixtures/package_plugin_config.json b/test/functional/fixtures/package_plugin_config.json index d0ad760c..538bd3a9 100644 --- a/test/functional/fixtures/package_plugin_config.json +++ b/test/functional/fixtures/package_plugin_config.json @@ -3,7 +3,8 @@ "plugins": { "PackagePlugin": { "collection_args": { - "rocm_regex": "rocm|hip|hsa|amdgpu" + "rocm_regex": "rocm|hip|hsa|amdgpu", + "enable_rocm_regex": true }, "analysis_args": { "exp_package_ver": { From 868f1bb7da45970110870bc4617bcf484f707c2f Mon Sep 17 00:00:00 2001 From: jaspals Date: Mon, 8 Dec 2025 18:16:37 +0000 Subject: [PATCH 7/7] changed default regex --- .../plugins/inband/package/analyzer_args.py | 9 ++--- .../inband/package/package_collector.py | 35 +++++++++++-------- test/unit/plugin/test_package_collector.py | 34 ------------------ 3 files changed, 23 insertions(+), 55 deletions(-) diff --git a/nodescraper/plugins/inband/package/analyzer_args.py b/nodescraper/plugins/inband/package/analyzer_args.py index 617ae370..62a34c1f 100644 --- a/nodescraper/plugins/inband/package/analyzer_args.py +++ b/nodescraper/plugins/inband/package/analyzer_args.py @@ -34,18 +34,15 @@ class PackageAnalyzerArgs(AnalyzerArgs): exp_package_ver: Dict[str, Optional[str]] = Field(default_factory=dict) regex_match: bool = False - rocm_regex: str = ( - r"ocl-icd|kfdtest|llvm-amd|miopen|half|hip|hcc|hsa|rocm|atmi|comgr|composa|amd-smi|aomp|amdgpu|rock|mivision|migraph|rocprofiler|roctracer|rocbl|hipify|rocsol|rocthr|rocff|rocalu|rocprim|rocrand|rccl|rocspar|rdc|rocwmma|rpp|openmp|amdfwflash|ocl|opencl" - ) + # rocm_regex is optional and should be specified in plugin_config.json if needed + rocm_regex: Optional[str] = None enable_rocm_regex: bool = False @classmethod def build_from_model(cls, datamodel: PackageDataModel) -> "PackageAnalyzerArgs": # Use custom rocm_regex from collection_args if enable_rocm_regex is true + rocm_regex = None if datamodel.enable_rocm_regex and datamodel.rocm_regex: rocm_regex = datamodel.rocm_regex - else: - # Use default rocm_regex - rocm_regex = PackageAnalyzerArgs().rocm_regex return cls(exp_package_ver=datamodel.version_info, rocm_regex=rocm_regex) diff --git a/nodescraper/plugins/inband/package/package_collector.py b/nodescraper/plugins/inband/package/package_collector.py index f136c1e6..a6cf42fe 100644 --- a/nodescraper/plugins/inband/package/package_collector.py +++ b/nodescraper/plugins/inband/package/package_collector.py @@ -232,24 +232,29 @@ def collect_data( self.result.status = ExecutionStatus.NOT_RAN return self.result, None - # Filter and log ROCm packages if on Linux + # Filter and log ROCm packages if on Linux and rocm_regex is provided if self.system_info.os_family == OSFamily.LINUX and packages: - # Get ROCm pattern from args or use default - rocm_pattern = args.rocm_regex if args else PackageAnalyzerArgs().rocm_regex - self.logger.info("Using rocm_pattern: %s", rocm_pattern) - rocm_packages = self._filter_rocm_packages(packages, rocm_pattern) - if rocm_packages: - self.result.message = f"Found {len(rocm_packages)} ROCm-related packages installed" - self.result.status = ExecutionStatus.OK - self._log_event( - category=EventCategory.OS, - description=f"Found {len(rocm_packages)} ROCm-related packages installed", - priority=EventPriority.INFO, - data={"rocm_packages": sorted(rocm_packages.keys())}, - ) + # Get ROCm pattern from args if provided + rocm_pattern = args.rocm_regex if args else None + if rocm_pattern: + self.logger.info("Using rocm_pattern: %s", rocm_pattern) + rocm_packages = self._filter_rocm_packages(packages, rocm_pattern) + if rocm_packages: + self.result.message = ( + f"Found {len(rocm_packages)} ROCm-related packages installed" + ) + self.result.status = ExecutionStatus.OK + self._log_event( + category=EventCategory.OS, + description=f"Found {len(rocm_packages)} ROCm-related packages installed", + priority=EventPriority.INFO, + data={"rocm_packages": sorted(rocm_packages.keys())}, + ) + else: + self.logger.info("No rocm_regex provided, skipping ROCm package filtering") # Extract rocm_regex and enable_rocm_regex from args if provided - rocm_regex = args.rocm_regex if args else "" + rocm_regex = args.rocm_regex if (args and args.rocm_regex) else "" enable_rocm_regex = getattr(args, "enable_rocm_regex", False) if args else False try: diff --git a/test/unit/plugin/test_package_collector.py b/test/unit/plugin/test_package_collector.py index 00a2694e..b83755fb 100644 --- a/test/unit/plugin/test_package_collector.py +++ b/test/unit/plugin/test_package_collector.py @@ -224,40 +224,6 @@ def test_bad_splits_ubuntu(collector, conn_mock, command_results): assert res.status == ExecutionStatus.OK -def test_rocm_package_filtering_default_regex(collector, conn_mock, command_results): - """Test ROCm package filtering with default regex pattern.""" - # Mock Ubuntu system with ROCm packages - ubuntu_packages = """rocm-core 5.7.0 - hip-runtime-amd 5.7.0 - hsa-rocr 1.9.0 - amdgpu-dkms 6.3.6 - gcc 11.4.0 - python3 3.10.12""" - - conn_mock.run_command.side_effect = [ - CommandArtifact( - command="", - exit_code=0, - stdout=command_results["ubuntu_rel"], - stderr="", - ), - CommandArtifact( - command="", - exit_code=0, - stdout=ubuntu_packages, - stderr="", - ), - ] - - res, data = collector.collect_data() - assert res.status == ExecutionStatus.OK - # Check that ROCm packages are found - assert "rocm" in res.message.lower() - assert data is not None - # Verify all packages are collected - assert len(data.version_info) == 6 - - def test_rocm_package_filtering_custom_regex(collector, conn_mock, command_results): """Test ROCm package filtering with custom regex pattern.""" from nodescraper.plugins.inband.package.analyzer_args import PackageAnalyzerArgs