Skip to content

Commit b36ad92

Browse files
committed
Merge branch 'development' into alex_rocm_fix
2 parents a26c14e + 752247a commit b36ad92

File tree

14 files changed

+586
-42
lines changed

14 files changed

+586
-42
lines changed

dev-setup.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Create venv if not already present
22
if [ ! -d "venv" ]; then
3-
python3 -m pip install virtualenv
4-
python3 -m virtualenv venv
3+
python3 -m pip install venv
4+
python3 -m venv venv
55
fi
66

77
# Activate the desired venv

nodescraper/base/inbandcollectortask.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
from typing import Generic, Optional
2828

2929
from nodescraper.connection.inband import InBandConnection
30-
from nodescraper.connection.inband.inband import CommandArtifact, FileArtifact
30+
from nodescraper.connection.inband.inband import BaseFileArtifact, CommandArtifact
3131
from nodescraper.enums import EventPriority, OSFamily, SystemInteractionLevel
3232
from nodescraper.generictypes import TCollectArg, TDataModel
3333
from nodescraper.interfaces import DataCollector, TaskResultHook
@@ -99,7 +99,7 @@ def _run_sut_cmd(
9999

100100
def _read_sut_file(
101101
self, filename: str, encoding="utf-8", strip: bool = True, log_artifact=True
102-
) -> FileArtifact:
102+
) -> BaseFileArtifact:
103103
"""
104104
Read a file from the SUT and return its content.
105105
@@ -110,7 +110,7 @@ def _read_sut_file(
110110
log_artifact (bool, optional): whether we should log the contents of the file. Defaults to True.
111111
112112
Returns:
113-
FileArtifact: The content of the file read from the SUT, which includes the file name and content
113+
BaseFileArtifact: The content of the file read from the SUT, which includes the file name and content
114114
"""
115115
file_res = self.connection.read_file(filename=filename, encoding=encoding, strip=strip)
116116
if log_artifact:

nodescraper/connection/inband/__init__.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,13 @@
2323
# SOFTWARE.
2424
#
2525
###############################################################################
26-
from .inband import CommandArtifact, FileArtifact, InBandConnection
26+
from .inband import (
27+
BaseFileArtifact,
28+
BinaryFileArtifact,
29+
CommandArtifact,
30+
InBandConnection,
31+
TextFileArtifact,
32+
)
2733
from .inbandlocal import LocalShell
2834
from .inbandmanager import InBandConnectionManager
2935
from .sshparams import SSHConnectionParams
@@ -33,6 +39,8 @@
3339
"LocalShell",
3440
"InBandConnectionManager",
3541
"InBandConnection",
36-
"FileArtifact",
42+
"BaseFileArtifact",
43+
"TextFileArtifact",
44+
"BinaryFileArtifact",
3745
"CommandArtifact",
3846
]

nodescraper/connection/inband/inband.py

Lines changed: 100 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
#
2525
###############################################################################
2626
import abc
27+
import os
28+
from typing import Optional
2729

2830
from pydantic import BaseModel
2931

@@ -37,12 +39,103 @@ class CommandArtifact(BaseModel):
3739
exit_code: int
3840

3941

40-
class FileArtifact(BaseModel):
41-
"""Artifact to contains contents of file read into memory"""
42+
class BaseFileArtifact(BaseModel, abc.ABC):
43+
"""Base class for files"""
4244

4345
filename: str
46+
47+
@abc.abstractmethod
48+
def log_model(self, log_path: str) -> None:
49+
"""Write file to path
50+
51+
Args:
52+
log_path (str): Path for file
53+
"""
54+
pass
55+
56+
@abc.abstractmethod
57+
def contents_str(self) -> str:
58+
pass
59+
60+
@classmethod
61+
def from_bytes(
62+
cls,
63+
filename: str,
64+
raw_contents: bytes,
65+
encoding: Optional[str] = "utf-8",
66+
strip: bool = True,
67+
) -> "BaseFileArtifact":
68+
"""factory method
69+
70+
Args:
71+
filename (str): name of file to be read
72+
raw_contents (bytes): Raw file content
73+
encoding (Optional[str], optional): Optional encoding. Defaults to "utf-8".
74+
strip (bool, optional): Remove padding. Defaults to True.
75+
76+
Returns:
77+
BaseFileArtifact: _Returns instance of Artifact file
78+
"""
79+
if encoding is None:
80+
return BinaryFileArtifact(filename=filename, contents=raw_contents)
81+
82+
try:
83+
text = raw_contents.decode(encoding)
84+
return TextFileArtifact(filename=filename, contents=text.strip() if strip else text)
85+
except UnicodeDecodeError:
86+
return BinaryFileArtifact(filename=filename, contents=raw_contents)
87+
88+
89+
class TextFileArtifact(BaseFileArtifact):
90+
"""Class for text file artifacts"""
91+
4492
contents: str
4593

94+
def log_model(self, log_path: str) -> None:
95+
"""Write file to disk
96+
97+
Args:
98+
log_path (str): Path for file
99+
"""
100+
path = os.path.join(log_path, self.filename)
101+
with open(path, "w", encoding="utf-8") as f:
102+
f.write(self.contents)
103+
104+
def contents_str(self) -> str:
105+
"""Get content as str
106+
107+
Returns:
108+
str: Str instance of file content
109+
"""
110+
return self.contents
111+
112+
113+
class BinaryFileArtifact(BaseFileArtifact):
114+
"""Class for binary file artifacts"""
115+
116+
contents: bytes
117+
118+
def log_model(self, log_path: str) -> None:
119+
"""Write file to disk
120+
121+
Args:
122+
log_path (str): Path for file
123+
"""
124+
log_name = os.path.join(log_path, self.filename)
125+
with open(log_name, "wb") as f:
126+
f.write(self.contents)
127+
128+
def contents_str(self) -> str:
129+
"""File content
130+
131+
Returns:
132+
str: Str instance of file content
133+
"""
134+
try:
135+
return self.contents.decode("utf-8")
136+
except UnicodeDecodeError:
137+
return f"<binary data: {len(self.contents)} bytes>"
138+
46139

47140
class InBandConnection(abc.ABC):
48141

@@ -63,14 +156,16 @@ def run_command(
63156
"""
64157

65158
@abc.abstractmethod
66-
def read_file(self, filename: str, encoding: str = "utf-8", strip: bool = True) -> FileArtifact:
67-
"""Read a file into a FileArtifact
159+
def read_file(
160+
self, filename: str, encoding: str = "utf-8", strip: bool = True
161+
) -> BaseFileArtifact:
162+
"""Read a file into a BaseFileArtifact
68163
69164
Args:
70165
filename (str): filename
71166
encoding (str, optional): encoding to use when opening file. Defaults to "utf-8".
72167
strip (bool): automatically strip file contents
73168
74169
Returns:
75-
FileArtifact: file artifact
170+
BaseFileArtifact: file artifact
76171
"""

nodescraper/connection/inband/inbandlocal.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@
2626
import os
2727
import subprocess
2828

29-
from .inband import CommandArtifact, FileArtifact, InBandConnection
29+
from .inband import (
30+
BaseFileArtifact,
31+
CommandArtifact,
32+
InBandConnection,
33+
)
3034

3135

3236
class LocalShell(InBandConnection):
@@ -64,22 +68,25 @@ def run_command(
6468
exit_code=res.returncode,
6569
)
6670

67-
def read_file(self, filename: str, encoding: str = "utf-8", strip: bool = True) -> FileArtifact:
68-
"""Read a local file into a FileArtifact
71+
def read_file(
72+
self, filename: str, encoding: str = "utf-8", strip: bool = True
73+
) -> BaseFileArtifact:
74+
"""Read a local file into a BaseFileArtifact
6975
7076
Args:
7177
filename (str): filename
7278
encoding (str, optional): encoding to use when opening file. Defaults to "utf-8".
7379
strip (bool): automatically strip file contents
7480
7581
Returns:
76-
FileArtifact: file artifact
82+
BaseFileArtifact: file artifact
7783
"""
78-
contents = ""
79-
with open(filename, "r", encoding=encoding) as local_file:
80-
contents = local_file.read().strip()
84+
with open(filename, "rb") as f:
85+
raw_contents = f.read()
8186

82-
return FileArtifact(
87+
return BaseFileArtifact.from_bytes(
8388
filename=os.path.basename(filename),
84-
contents=contents.strip() if strip else contents,
89+
raw_contents=raw_contents,
90+
encoding=encoding,
91+
strip=strip,
8592
)

nodescraper/connection/inband/inbandremote.py

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@
3434
SSHException,
3535
)
3636

37-
from .inband import CommandArtifact, FileArtifact, InBandConnection
37+
from .inband import (
38+
BaseFileArtifact,
39+
CommandArtifact,
40+
InBandConnection,
41+
)
3842
from .sshparams import SSHConnectionParams
3943

4044

@@ -94,27 +98,26 @@ def connect_ssh(self):
9498
def read_file(
9599
self,
96100
filename: str,
97-
encoding="utf-8",
101+
encoding: str | None = "utf-8",
98102
strip: bool = True,
99-
) -> FileArtifact:
100-
"""Read a remote file into a file artifact
103+
) -> BaseFileArtifact:
104+
"""Read a remote file into a BaseFileArtifact.
101105
102106
Args:
103-
filename (str): filename
104-
encoding (str, optional): remote file encoding. Defaults to "utf-8".
105-
strip (bool): automatically strip file contents
107+
filename (str): Path to file on remote host
108+
encoding (str | None, optional): If None, file is read as binary. If str, decode using that encoding. Defaults to "utf-8".
109+
strip (bool): Strip whitespace for text files. Ignored for binary.
106110
107111
Returns:
108-
FileArtifact: file artifact
112+
BaseFileArtifact: Object representing file contents
109113
"""
110-
contents = ""
111-
112-
with self.client.open_sftp().open(filename) as remote_file:
113-
contents = remote_file.read().decode(encoding=encoding, errors="ignore")
114-
115-
return FileArtifact(
114+
with self.client.open_sftp().open(filename, "rb") as remote_file:
115+
raw_contents = remote_file.read()
116+
return BaseFileArtifact.from_bytes(
116117
filename=os.path.basename(filename),
117-
contents=contents.strip() if strip else contents,
118+
raw_contents=raw_contents,
119+
encoding=encoding,
120+
strip=strip,
118121
)
119122

120123
def run_command(

nodescraper/plugins/inband/dmesg/dmesg_analyzer.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from typing import Optional
2929

3030
from nodescraper.base.regexanalyzer import ErrorRegex, RegexAnalyzer
31-
from nodescraper.connection.inband import FileArtifact
31+
from nodescraper.connection.inband import TextFileArtifact
3232
from nodescraper.enums import EventCategory, EventPriority
3333
from nodescraper.models import Event, TaskResult
3434

@@ -386,7 +386,7 @@ def analyze_data(
386386
args.analysis_range_end,
387387
)
388388
self.result.artifacts.append(
389-
FileArtifact(filename="filtered_dmesg.log", contents=dmesg_content)
389+
TextFileArtifact(filename="filtered_dmesg.log", contents=dmesg_content)
390390
)
391391
else:
392392
dmesg_content = data.dmesg_content
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
###############################################################################
2+
#
3+
# MIT License
4+
#
5+
# Copyright (c) 2025 Advanced Micro Devices, Inc.
6+
#
7+
# Permission is hereby granted, free of charge, to any person obtaining a copy
8+
# of this software and associated documentation files (the "Software"), to deal
9+
# in the Software without restriction, including without limitation the rights
10+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
# copies of the Software, and to permit persons to whom the Software is
12+
# furnished to do so, subject to the following conditions:
13+
#
14+
# The above copyright notice and this permission notice shall be included in all
15+
# copies or substantial portions of the Software.
16+
#
17+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+
# SOFTWARE.
24+
#
25+
###############################################################################
26+
from .nvme_plugin import NvmePlugin
27+
28+
__all__ = ["NvmePlugin"]

0 commit comments

Comments
 (0)