redfish-python-sdk 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. redfish_python_sdk-1.0.0.dist-info/METADATA +164 -0
  2. redfish_python_sdk-1.0.0.dist-info/RECORD +56 -0
  3. redfish_python_sdk-1.0.0.dist-info/WHEEL +5 -0
  4. redfish_python_sdk-1.0.0.dist-info/licenses/LICENSE +29 -0
  5. redfish_python_sdk-1.0.0.dist-info/top_level.txt +1 -0
  6. redfish_sdk/__init__.py +63 -0
  7. redfish_sdk/client.py +1894 -0
  8. redfish_sdk/exceptions.py +56 -0
  9. redfish_sdk/http_client.py +452 -0
  10. redfish_sdk/managers/__init__.py +21 -0
  11. redfish_sdk/managers/_log_helpers.py +144 -0
  12. redfish_sdk/managers/account.py +96 -0
  13. redfish_sdk/managers/chassis.py +327 -0
  14. redfish_sdk/managers/event.py +264 -0
  15. redfish_sdk/managers/managers.py +120 -0
  16. redfish_sdk/managers/registries.py +48 -0
  17. redfish_sdk/managers/session.py +130 -0
  18. redfish_sdk/managers/systems.py +630 -0
  19. redfish_sdk/managers/task.py +89 -0
  20. redfish_sdk/managers/update.py +99 -0
  21. redfish_sdk/managers/update_strategies/__init__.py +51 -0
  22. redfish_sdk/managers/update_strategies/base.py +101 -0
  23. redfish_sdk/managers/update_strategies/h3c.py +140 -0
  24. redfish_sdk/managers/update_strategies/inspur.py +89 -0
  25. redfish_sdk/managers/update_strategies/lenovo.py +59 -0
  26. redfish_sdk/managers/update_strategies/nettrix.py +56 -0
  27. redfish_sdk/managers/update_strategies/registry.py +72 -0
  28. redfish_sdk/managers/update_strategies/vendor_detect.py +111 -0
  29. redfish_sdk/managers/update_strategies/xfusion.py +60 -0
  30. redfish_sdk/managers/update_strategies/zte.py +68 -0
  31. redfish_sdk/models/__init__.py +55 -0
  32. redfish_sdk/models/account.py +60 -0
  33. redfish_sdk/models/chassis.py +86 -0
  34. redfish_sdk/models/check.py +231 -0
  35. redfish_sdk/models/common.py +102 -0
  36. redfish_sdk/models/drive.py +53 -0
  37. redfish_sdk/models/event.py +64 -0
  38. redfish_sdk/models/fru.py +59 -0
  39. redfish_sdk/models/gpu.py +33 -0
  40. redfish_sdk/models/logs.py +56 -0
  41. redfish_sdk/models/managers.py +153 -0
  42. redfish_sdk/models/memory.py +50 -0
  43. redfish_sdk/models/network_adapter.py +76 -0
  44. redfish_sdk/models/oem.py +173 -0
  45. redfish_sdk/models/pcie_device.py +89 -0
  46. redfish_sdk/models/power.py +92 -0
  47. redfish_sdk/models/processor.py +50 -0
  48. redfish_sdk/models/registry.py +34 -0
  49. redfish_sdk/models/resource_key.py +70 -0
  50. redfish_sdk/models/root.py +50 -0
  51. redfish_sdk/models/session.py +42 -0
  52. redfish_sdk/models/storage.py +77 -0
  53. redfish_sdk/models/systems.py +194 -0
  54. redfish_sdk/models/task.py +54 -0
  55. redfish_sdk/models/thermal.py +111 -0
  56. redfish_sdk/models/update.py +55 -0
@@ -0,0 +1,231 @@
1
+ """
2
+ 模型层注解式校验引擎。
3
+
4
+ 提供自定义 Field() 包装和 validate_model() 通用校验函数,
5
+ 实现 Go playground/validator 风格的 struct tag 声明式校验。
6
+
7
+ 用法:
8
+ # 模型定义(替换 from pydantic import Field)
9
+ from redfish_sdk.models.check import Field
10
+
11
+ class Processor(Entity):
12
+ manufacturer: Optional[str] = Field(None, alias="Manufacturer", validate="required,type=str")
13
+ total_cores: Optional[int] = Field(None, alias="TotalCores", validate="required,type=int,gt=0")
14
+
15
+ # 校验(测试代码中调用)
16
+ from redfish_sdk.models.check import validate_model
17
+ validate_model(processor_instance)
18
+ """
19
+ from __future__ import annotations
20
+
21
+ import warnings
22
+ from typing import Any
23
+
24
+ from pydantic import BaseModel
25
+ from pydantic import Field as _PydanticField
26
+
27
+ # ---------------------------------------------------------------------------
28
+ # 自定义 Field() 包装
29
+ # ---------------------------------------------------------------------------
30
+
31
+ def Field(default=None, *, alias=None, validate=None, **kwargs):
32
+ """
33
+ Pydantic Field 的增强包装,新增 validate 参数。
34
+
35
+ 用法与原生 Field 完全一致,额外支持 validate 标签:
36
+ Field(None, alias="Manufacturer", validate="required,type=str")
37
+
38
+ 内部实现: 将 validate 字符串存入 json_schema_extra,
39
+ 供 validate_model() 在运行时读取。
40
+ """
41
+ if validate is not None:
42
+ extra = kwargs.get("json_schema_extra") or {}
43
+ extra["validate"] = validate
44
+ kwargs["json_schema_extra"] = extra
45
+ return _PydanticField(default, alias=alias, **kwargs)
46
+
47
+
48
+ # ---------------------------------------------------------------------------
49
+ # 标签解析器
50
+ # ---------------------------------------------------------------------------
51
+
52
+ _TYPE_MAP = {
53
+ "str": str,
54
+ "int": int,
55
+ "float": float,
56
+ "bool": bool,
57
+ }
58
+
59
+
60
+ def _parse_validate_tag(tag: str) -> dict:
61
+ """
62
+ 解析 validate 标签字符串为规则字典。
63
+
64
+ 示例:
65
+ "required,type=str" → {"required": True, "type": str}
66
+ "required,type=int,gt=0" → {"required": True, "type": int, "gt": 0}
67
+ "oneof=On Off PoweringOn" → {"oneof": ("On", "Off", "PoweringOn")}
68
+ "status" → {"status": True}
69
+ "gte_field=total_cores" → {"gte_field": "total_cores"}
70
+ """
71
+ rules = {}
72
+ for part in tag.split(","):
73
+ part = part.strip()
74
+ if not part:
75
+ continue
76
+ if "=" in part:
77
+ key, val = part.split("=", 1)
78
+ key = key.strip()
79
+ val = val.strip()
80
+ if key == "type":
81
+ rules["type"] = _TYPE_MAP[val]
82
+ elif key in ("gt", "ge", "lt", "le"):
83
+ rules[key] = float(val) if "." in val else int(val)
84
+ elif key == "oneof":
85
+ rules["oneof"] = tuple(val.split())
86
+ elif key.endswith("_field"):
87
+ rules[key] = val # 跨字段引用,存字段名
88
+ else:
89
+ rules[key] = val
90
+ else:
91
+ # 无值标签:required, status, list
92
+ rules[part] = True
93
+ return rules
94
+
95
+
96
+ # ---------------------------------------------------------------------------
97
+ # 通用校验引擎
98
+ # ---------------------------------------------------------------------------
99
+
100
+ def validate_model(obj: BaseModel) -> None:
101
+ """
102
+ 根据模型字段上的 validate 标签自动执行校验。
103
+
104
+ 行为:
105
+ - required 且值为 None/空字符串 → warnings.warn(WARN,不失败)
106
+ - 类型不匹配 → AssertionError(FAIL)
107
+ - 值域不满足 → AssertionError(FAIL)
108
+ - 枚举不匹配 → AssertionError(FAIL)
109
+
110
+ 用法:
111
+ validate_model(processor_instance)
112
+ """
113
+ resource_id = getattr(obj, "odata_id", "unknown")
114
+ cls_name = obj.__class__.__name__
115
+
116
+ for field_name, field_info in obj.__class__.model_fields.items():
117
+ extra = field_info.json_schema_extra
118
+ if not extra or "validate" not in extra:
119
+ continue
120
+
121
+ tag = extra["validate"]
122
+ rules = _parse_validate_tag(tag)
123
+ value = getattr(obj, field_name, None)
124
+
125
+ # --- status 特殊处理 ---
126
+ if rules.get("status"):
127
+ if value is not None:
128
+ _check_status(value, cls_name, resource_id)
129
+ continue
130
+
131
+ required = rules.get("required", False)
132
+
133
+ # --- None 检查 ---
134
+ if value is None:
135
+ if required:
136
+ warnings.warn(
137
+ f"{cls_name}.{field_name} 为 None (resource={resource_id})",
138
+ stacklevel=2,
139
+ )
140
+ continue
141
+
142
+ # --- 空字符串检查 ---
143
+ if isinstance(value, str) and value == "":
144
+ if required:
145
+ warnings.warn(
146
+ f"{cls_name}.{field_name} 为空字符串 (resource={resource_id})",
147
+ stacklevel=2,
148
+ )
149
+ continue
150
+
151
+ # --- list 标签 ---
152
+ if rules.get("list"):
153
+ assert isinstance(value, list), (
154
+ f"{cls_name}.{field_name} 类型错误: 期望 list, "
155
+ f"实际 {type(value).__name__} (resource={resource_id})"
156
+ )
157
+ continue
158
+
159
+ # --- type 类型检查 ---
160
+ expected = rules.get("type")
161
+ if expected is not None:
162
+ if expected is float:
163
+ assert isinstance(value, (int, float)), (
164
+ f"{cls_name}.{field_name} 类型错误: 期望 数字, "
165
+ f"实际 {type(value).__name__} (值={value!r}, resource={resource_id})"
166
+ )
167
+ else:
168
+ assert isinstance(value, expected), (
169
+ f"{cls_name}.{field_name} 类型错误: 期望 {expected.__name__}, "
170
+ f"实际 {type(value).__name__} (值={value!r}, resource={resource_id})"
171
+ )
172
+
173
+ # --- gt / ge / lt / le 值域检查 ---
174
+ if isinstance(value, (int, float)):
175
+ if "gt" in rules:
176
+ assert value > rules["gt"], (
177
+ f"{cls_name}.{field_name} 应 > {rules['gt']}, "
178
+ f"实际={value} (resource={resource_id})"
179
+ )
180
+ if "ge" in rules:
181
+ assert value >= rules["ge"], (
182
+ f"{cls_name}.{field_name} 应 >= {rules['ge']}, "
183
+ f"实际={value} (resource={resource_id})"
184
+ )
185
+ if "lt" in rules:
186
+ assert value < rules["lt"], (
187
+ f"{cls_name}.{field_name} 应 < {rules['lt']}, "
188
+ f"实际={value} (resource={resource_id})"
189
+ )
190
+ if "le" in rules:
191
+ assert value <= rules["le"], (
192
+ f"{cls_name}.{field_name} 应 <= {rules['le']}, "
193
+ f"实际={value} (resource={resource_id})"
194
+ )
195
+
196
+ # --- oneof 枚举检查 ---
197
+ if "oneof" in rules and isinstance(value, str):
198
+ assert value in rules["oneof"], (
199
+ f"{cls_name}.{field_name} 值不在允许范围: "
200
+ f"实际={value!r}, 允许={rules['oneof']} (resource={resource_id})"
201
+ )
202
+
203
+ # --- gte_field 跨字段检查 ---
204
+ if "gte_field" in rules and isinstance(value, (int, float)):
205
+ other_name = rules["gte_field"]
206
+ other_val = getattr(obj, other_name, None)
207
+ if other_val is not None and isinstance(other_val, (int, float)):
208
+ assert value >= other_val, (
209
+ f"{cls_name}.{field_name} ({value}) 应 >= "
210
+ f"{other_name} ({other_val}) (resource={resource_id})"
211
+ )
212
+
213
+
214
+ def _check_status(status: Any, parent_cls: str, resource_id: str) -> None:
215
+ """校验 Status 子对象。"""
216
+ from redfish_sdk.models.common import Status
217
+
218
+ assert isinstance(status, Status), (
219
+ f"{parent_cls}.status 类型错误: 期望 Status, "
220
+ f"实际 {type(status).__name__} (resource={resource_id})"
221
+ )
222
+ if status.state is not None:
223
+ assert isinstance(status.state, str), (
224
+ f"{parent_cls}.status.state 类型错误: "
225
+ f"期望 str, 实际 {type(status.state).__name__} (resource={resource_id})"
226
+ )
227
+ if status.health is not None:
228
+ assert isinstance(status.health, str), (
229
+ f"{parent_cls}.status.health 类型错误: "
230
+ f"期望 str, 实际 {type(status.health).__name__} (resource={resource_id})"
231
+ )
@@ -0,0 +1,102 @@
1
+ """
2
+ Common base models for Redfish resources.
3
+ """
4
+ from __future__ import annotations
5
+
6
+ from typing import Generic, List, Optional, TypeVar
7
+
8
+ from pydantic import BaseModel, ConfigDict, Field
9
+
10
+
11
+ class Link(BaseModel):
12
+ """
13
+ Base class for all Redfish resource links.
14
+ Contains only the @odata.id field, which uniquely identifies the resource URL.
15
+ """
16
+ model_config = ConfigDict(populate_by_name=True, extra="allow")
17
+
18
+ odata_id: Optional[str] = Field(None, alias="@odata.id")
19
+
20
+ def __repr__(self) -> str:
21
+ return f"{self.__class__.__name__}(odata_id={self.odata_id!r})"
22
+
23
+
24
+ class Entity(Link):
25
+ """
26
+ Entity is the base class for all Redfish resources.
27
+ Inherits from Link and adds common fields like Id, Name, Description, and ETag.
28
+
29
+ """
30
+ odata_context: Optional[str] = Field(None, alias="@odata.context")
31
+ odata_type: Optional[str] = Field(None, alias="@odata.type")
32
+ odata_etag: Optional[str] = Field(None, alias="@odata.etag")
33
+ id: Optional[str] = Field(None, alias="Id")
34
+ name: Optional[str] = Field(None, alias="Name")
35
+ description: Optional[str] = Field(None, alias="Description")
36
+
37
+ def __repr__(self) -> str:
38
+ return f"{self.__class__.__name__}(id={self.id!r}, name={self.name!r})"
39
+
40
+
41
+ T = TypeVar("T", bound=Link)
42
+
43
+
44
+ class Collection(Entity, Generic[T]):
45
+ """
46
+ Generic Redfish collection container.
47
+
48
+ Example:
49
+ /redfish/v1/Systems -> Collection[System]
50
+ /redfish/v1/Chassis -> Collection[Chassis]
51
+
52
+ """
53
+ members_count: Optional[int] = Field(None, alias="Members@odata.count")
54
+ members: Optional[List[T]] = Field(default_factory=list, alias="Members")
55
+
56
+
57
+ class Status(BaseModel):
58
+ """
59
+ Standard Redfish status object, used across most resource types.
60
+ """
61
+ model_config = ConfigDict(populate_by_name=True, extra="allow")
62
+
63
+ state: Optional[str] = Field(None, alias="State")
64
+ health: Optional[str] = Field(None, alias="Health")
65
+ health_rollup: Optional[str] = Field(None, alias="HealthRollup")
66
+
67
+
68
+ class ExtendedInfo(BaseModel):
69
+ """Extended error information."""
70
+ model_config = ConfigDict(populate_by_name=True, extra="allow")
71
+
72
+ message_id: Optional[str] = Field(None, alias="MessageId")
73
+ message: Optional[str] = Field(None, alias="Message")
74
+ message_args: Optional[List[str]] = Field(None, alias="MessageArgs")
75
+ severity: Optional[str] = Field(None, alias="Severity")
76
+ resolution: Optional[str] = Field(None, alias="Resolution")
77
+
78
+
79
+ class RedfishError(BaseModel):
80
+ """
81
+ Standard Redfish error response body.
82
+ """
83
+ model_config = ConfigDict(populate_by_name=True, extra="allow")
84
+
85
+ code: Optional[str] = Field(None, alias="code")
86
+ message: Optional[str] = Field(None, alias="message")
87
+ extended_info: Optional[List[ExtendedInfo]] = Field(None, alias="@Message.ExtendedInfo")
88
+
89
+
90
+ class RedfishResponse(BaseModel):
91
+ """
92
+ Generic Redfish operation response.
93
+ Used for reset, patch and other mutation operations that return minimal data.
94
+ """
95
+ model_config = ConfigDict(populate_by_name=True, extra="allow")
96
+
97
+ error: Optional[RedfishError] = Field(None, alias="error")
98
+ odata_id: Optional[str] = Field(None, alias="@odata.id")
99
+ odata_type: Optional[str] = Field(None, alias="@odata.type")
100
+ message: Optional[str] = Field(None, alias="Message")
101
+ task_id: Optional[str] = Field(None, alias="TaskId")
102
+ task_state: Optional[str] = Field(None, alias="TaskState")
@@ -0,0 +1,53 @@
1
+ """
2
+ Drive (HDD/SSD/NVMe) component models.
3
+ """
4
+ from __future__ import annotations
5
+
6
+ from typing import Any, Dict, List, Optional
7
+
8
+ from pydantic import BaseModel, ConfigDict
9
+
10
+ from .check import Field
11
+ from .common import Entity, Status
12
+
13
+
14
+ class Location(BaseModel):
15
+ """Physical location info."""
16
+ model_config = ConfigDict(populate_by_name=True, extra="allow")
17
+
18
+ info: Optional[str] = Field(None, alias="Info")
19
+ info_format: Optional[str] = Field(None, alias="InfoFormat")
20
+ placement: Optional[Any] = Field(None, alias="Placement")
21
+ postal_address: Optional[Any] = Field(None, alias="PostalAddress")
22
+
23
+
24
+ class Drive(Entity):
25
+ """
26
+ Represents a physical storage drive (HDD/SSD/NVMe).
27
+ Endpoint: /redfish/v1/Chassis/{chassisId}/Drives/{driveId}
28
+
29
+ """
30
+ indicator_led: Optional[str] = Field(None, alias="IndicatorLED")
31
+ model: Optional[str] = Field(None, alias="Model", validate="type=str")
32
+ revision: Optional[str] = Field(None, alias="Revision")
33
+ capacity_bytes: Optional[int] = Field(None, alias="CapacityBytes", validate="required,type=int,gt=0")
34
+ protocol: Optional[str] = Field(None, alias="Protocol", validate="type=str")
35
+ serial_number: Optional[str] = Field(None, alias="SerialNumber", validate="type=str")
36
+ media_type: Optional[str] = Field(None, alias="MediaType", validate="type=str")
37
+ manufacturer: Optional[str] = Field(None, alias="Manufacturer", validate="type=str")
38
+ capable_speed_gbs: Optional[Any] = Field(None, alias="CapableSpeedGbs")
39
+ negotiated_speed_gbs: Optional[Any] = Field(None, alias="NegotiatedSpeedGbs")
40
+ failure_predicted: Optional[bool] = Field(None, alias="FailurePredicted")
41
+ predicted_media_life_left_percent: Optional[int] = Field(
42
+ None, alias="PredictedMediaLifeLeftPercent"
43
+ )
44
+ hotspare_type: Optional[str] = Field(None, alias="HotspareType")
45
+ status_indicator: Optional[str] = Field(None, alias="StatusIndicator")
46
+ status: Optional[Status] = Field(None, alias="Status", validate="status")
47
+ location: Optional[List[Location]] = Field(None, alias="Location")
48
+ # Drive power state + Redfish Actions (e.g. #Drive.Reset).
49
+ # `actions` is intentionally weakly typed (dict) because vendors vary widely
50
+ # in how they expose action targets/AllowableValues. Helper methods on
51
+ # SystemsManager parse the well-known sub-keys.
52
+ power_state: Optional[str] = Field(None, alias="PowerState")
53
+ actions: Optional[Dict[str, Any]] = Field(None, alias="Actions")
@@ -0,0 +1,64 @@
1
+ """
2
+ Event service models.
3
+ """
4
+ from __future__ import annotations
5
+
6
+ from typing import Any, Dict, List, Optional
7
+
8
+ from pydantic import Field
9
+
10
+ from .common import Entity, Link, Status # noqa: F401 (Status kept for downstream imports)
11
+
12
+
13
+ class EventService(Entity):
14
+ """
15
+ The Event service provides event subscription management.
16
+ Endpoint: /redfish/v1/EventService
17
+ """
18
+ subscriptions: Optional[Link] = Field(None, alias="Subscriptions")
19
+ delivery_retry_attempts: Optional[int] = Field(None, alias="DeliveryRetryAttempts")
20
+ delivery_retry_interval_seconds: Optional[int] = Field(
21
+ None, alias="DeliveryRetryIntervalSeconds"
22
+ )
23
+ event_types_for_subscription: Optional[List[str]] = Field(
24
+ None, alias="EventTypesForSubscription"
25
+ )
26
+ service_enabled: Optional[bool] = Field(None, alias="ServiceEnabled")
27
+ status: Optional[Status] = Field(None, alias="Status")
28
+ # Redfish Actions block (e.g. #EventService.SubmitTestEvent).
29
+ actions: Optional[Dict[str, Any]] = Field(None, alias="Actions")
30
+
31
+
32
+ class Subscription(Entity):
33
+ """
34
+ Represents an event subscription (webhook).
35
+ Endpoint: /redfish/v1/EventService/Subscriptions/{subscriptionId}
36
+ """
37
+ context: Optional[str] = Field(None, alias="Context")
38
+ destination: Optional[str] = Field(None, alias="Destination")
39
+ event_types: Optional[List[str]] = Field(None, alias="EventTypes")
40
+ # Type widened to Any: different BMC vendors return either a dict
41
+ # (``{"X-Auth-Token": "..."}``) or a list of dicts
42
+ # (``[{"X-Auth-Token": "..."}]``). Mirrors the lenient style used by
43
+ # ``EventService.actions`` above.
44
+ http_headers: Optional[Any] = Field(None, alias="HttpHeaders")
45
+ oem_type: Optional[str] = Field(None, alias="OemSubscriptionType")
46
+ protocol: Optional[str] = Field(None, alias="Protocol")
47
+ registry_prefixes: Optional[List[str]] = Field(None, alias="RegistryPrefixes")
48
+ resource_types: Optional[List[str]] = Field(None, alias="ResourceTypes")
49
+ # Type widened to Any: per Redfish spec ``Status`` is a complex object
50
+ # (``{"State": "Enabled", "Health": "OK"}``), but multiple BMC vendors
51
+ # return a bare string (e.g. ``"Enabled"``) for EventDestination.
52
+ # Mirrors the lenient style of ``http_headers`` above.
53
+ status: Optional[Any] = Field(None, alias="Status")
54
+ subscription_type: Optional[str] = Field(None, alias="SubscriptionType")
55
+ # —— Additional fields observed across BMC vendors ——
56
+ origin_resources: Optional[List[Dict[str, Any]]] = Field(
57
+ None, alias="OriginResources"
58
+ )
59
+ delivery_retry_policy: Optional[str] = Field(
60
+ None, alias="DeliveryRetryPolicy"
61
+ )
62
+ message_ids: Optional[List[str]] = Field(None, alias="MessageIds")
63
+ event_format_type: Optional[str] = Field(None, alias="EventFormatType")
64
+ severities: Optional[List[str]] = Field(None, alias="Severities")
@@ -0,0 +1,59 @@
1
+ """
2
+ FRU (Field Replaceable Unit) models.
3
+
4
+ """
5
+ from __future__ import annotations
6
+
7
+ from typing import Optional
8
+
9
+ from pydantic import BaseModel, ConfigDict, Field
10
+
11
+ from .common import Entity, Status
12
+ from .oem import MainBoard
13
+
14
+
15
+ class FruChassis(BaseModel):
16
+ """FRU chassis component info."""
17
+ model_config = ConfigDict(populate_by_name=True, extra="allow")
18
+
19
+ chassis_part_number: Optional[str] = Field(None, alias="ChassisPartNumber")
20
+ chassis_serial_number: Optional[str] = Field(None, alias="ChassisSerialNumber")
21
+ chassis_type: Optional[str] = Field(None, alias="ChassisType")
22
+
23
+
24
+ class FruProduct(BaseModel):
25
+ """FRU product info."""
26
+ model_config = ConfigDict(populate_by_name=True, extra="allow")
27
+
28
+ manufacturer: Optional[str] = Field(None, alias="Manufacturer")
29
+ name: Optional[str] = Field(None, alias="Name")
30
+ part_number: Optional[str] = Field(None, alias="PartNumber")
31
+ serial_number: Optional[str] = Field(None, alias="SerialNumber")
32
+ version: Optional[str] = Field(None, alias="Version")
33
+ asset_tag: Optional[str] = Field(None, alias="AssetTag")
34
+
35
+
36
+ class FruDevice(BaseModel):
37
+ """FRU device info (card, module, etc.)."""
38
+ model_config = ConfigDict(populate_by_name=True, extra="allow")
39
+
40
+ name: Optional[str] = Field(None, alias="Name")
41
+ manufacturer: Optional[str] = Field(None, alias="Manufacturer")
42
+ part_number: Optional[str] = Field(None, alias="PartNumber")
43
+ serial_number: Optional[str] = Field(None, alias="SerialNumber")
44
+
45
+
46
+ class Fru(Entity):
47
+ """
48
+ FRU (Field Replaceable Unit) information resource.
49
+ Contains hardware inventory info like board, chassis, and product details.
50
+
51
+ Retrieved via the OEM FRU link in the System or Chassis resource:
52
+ - system.oem.bmc.fru.odata_id
53
+
54
+ """
55
+ chassis: Optional[FruChassis] = Field(None, alias="Chassis")
56
+ board: Optional[MainBoard] = Field(None, alias="MainBoard")
57
+ product: Optional[FruProduct] = Field(None, alias="Product")
58
+ device: Optional[FruDevice] = Field(None, alias="device")
59
+ status: Optional[Status] = Field(None, alias="Status")
@@ -0,0 +1,33 @@
1
+ """
2
+ GPU (Graphics Processing Unit) component models.
3
+
4
+ """
5
+ from __future__ import annotations
6
+
7
+ from typing import Optional
8
+
9
+ from pydantic import BaseModel, ConfigDict
10
+
11
+ from .check import Field
12
+ from .common import Entity
13
+
14
+
15
+ class GpuOEM(BaseModel):
16
+ """OEM-specific GPU fields."""
17
+ model_config = ConfigDict(populate_by_name=True, extra="allow")
18
+
19
+ serial_number: Optional[str] = Field(None, alias="SerialNumber")
20
+
21
+
22
+ class Gpu(Entity):
23
+ """
24
+ Represents a GPU (Graphics Processing Unit).
25
+ Can come from either /redfish/v1/Systems/{systemId}/GraphicsControllers
26
+ or derived from /redfish/v1/Chassis/{chassisId}/PCIeDevices.
27
+
28
+ """
29
+ manufacturer: Optional[str] = Field(None, alias="Manufacturer", validate="type=str")
30
+ model: Optional[str] = Field(None, alias="Model", validate="type=str")
31
+ version: Optional[str] = Field(None, alias="Version", validate="type=str")
32
+ power_watts: Optional[str] = Field(None, alias="PowerWatts", validate="type=str")
33
+ oem: Optional[GpuOEM] = Field(None, alias="Oem")
@@ -0,0 +1,56 @@
1
+ """
2
+ Log service models.
3
+
4
+ """
5
+ from __future__ import annotations
6
+
7
+ from typing import Any, Dict, Optional
8
+
9
+ from pydantic import Field
10
+
11
+ from .common import Entity, Link, Status
12
+
13
+
14
+ class Log(Entity):
15
+ """
16
+ Represents a log service on a system or manager.
17
+ Endpoint: /redfish/v1/Systems/{id}/LogServices/{logId}
18
+ /redfish/v1/Managers/{id}/LogServices/{logId}
19
+
20
+ """
21
+ date_time: Optional[str] = Field(None, alias="DateTime")
22
+ date_time_local_offset: Optional[str] = Field(None, alias="DateTimeLocalOffset")
23
+ entries: Optional[Link] = Field(None, alias="Entries")
24
+ max_number_of_records: Optional[int] = Field(None, alias="MaxNumberOfRecords")
25
+ overwrite_policy: Optional[str] = Field(None, alias="OverWritePolicy")
26
+ service_enabled: Optional[bool] = Field(None, alias="ServiceEnabled")
27
+ status: Optional[Status] = Field(None, alias="Status")
28
+ # Redfish Actions block (e.g. #LogService.ClearLog).
29
+ actions: Optional[Dict[str, Any]] = Field(None, alias="Actions")
30
+
31
+
32
+ class LogEntry(Entity):
33
+ """
34
+ A single log entry within a log service.
35
+ Endpoint: /redfish/v1/Systems/{id}/LogServices/{logId}/Entries/{entryId}
36
+
37
+ """
38
+ created: Optional[str] = Field(None, alias="Created")
39
+ entry_code: Optional[str] = Field(None, alias="EntryCode")
40
+ entry_type: Optional[str] = Field(None, alias="EntryType")
41
+ message: Optional[str] = Field(None, alias="Message")
42
+ message_args: Optional[list] = Field(None, alias="MessageArgs")
43
+ message_id: Optional[str] = Field(None, alias="MessageId")
44
+ oem_log_entry_code: Optional[str] = Field(None, alias="OemLogEntryCode")
45
+ oem_record_format: Optional[str] = Field(None, alias="OemRecordFormat")
46
+ sensor_number: Optional[int] = Field(None, alias="SensorNumber")
47
+ sensor_type: Optional[str] = Field(None, alias="SensorType")
48
+ severity: Optional[str] = Field(None, alias="Severity")
49
+ status: Optional[Status] = Field(None, alias="Status")
50
+ # DMTF v1.4 optional fields commonly needed for
51
+ # compliance checks (e.g. bmc_autotest managers_004*_*_log_check.py).
52
+ # ``odata_id`` / ``odata_type`` are already inherited from Entity.
53
+ event_timestamp: Optional[str] = Field(None, alias="EventTimestamp")
54
+ diagnostic_data_size_bytes: Optional[int] = Field(
55
+ None, alias="DiagnosticDataSizeBytes"
56
+ )