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.
- redfish_python_sdk-1.0.0.dist-info/METADATA +164 -0
- redfish_python_sdk-1.0.0.dist-info/RECORD +56 -0
- redfish_python_sdk-1.0.0.dist-info/WHEEL +5 -0
- redfish_python_sdk-1.0.0.dist-info/licenses/LICENSE +29 -0
- redfish_python_sdk-1.0.0.dist-info/top_level.txt +1 -0
- redfish_sdk/__init__.py +63 -0
- redfish_sdk/client.py +1894 -0
- redfish_sdk/exceptions.py +56 -0
- redfish_sdk/http_client.py +452 -0
- redfish_sdk/managers/__init__.py +21 -0
- redfish_sdk/managers/_log_helpers.py +144 -0
- redfish_sdk/managers/account.py +96 -0
- redfish_sdk/managers/chassis.py +327 -0
- redfish_sdk/managers/event.py +264 -0
- redfish_sdk/managers/managers.py +120 -0
- redfish_sdk/managers/registries.py +48 -0
- redfish_sdk/managers/session.py +130 -0
- redfish_sdk/managers/systems.py +630 -0
- redfish_sdk/managers/task.py +89 -0
- redfish_sdk/managers/update.py +99 -0
- redfish_sdk/managers/update_strategies/__init__.py +51 -0
- redfish_sdk/managers/update_strategies/base.py +101 -0
- redfish_sdk/managers/update_strategies/h3c.py +140 -0
- redfish_sdk/managers/update_strategies/inspur.py +89 -0
- redfish_sdk/managers/update_strategies/lenovo.py +59 -0
- redfish_sdk/managers/update_strategies/nettrix.py +56 -0
- redfish_sdk/managers/update_strategies/registry.py +72 -0
- redfish_sdk/managers/update_strategies/vendor_detect.py +111 -0
- redfish_sdk/managers/update_strategies/xfusion.py +60 -0
- redfish_sdk/managers/update_strategies/zte.py +68 -0
- redfish_sdk/models/__init__.py +55 -0
- redfish_sdk/models/account.py +60 -0
- redfish_sdk/models/chassis.py +86 -0
- redfish_sdk/models/check.py +231 -0
- redfish_sdk/models/common.py +102 -0
- redfish_sdk/models/drive.py +53 -0
- redfish_sdk/models/event.py +64 -0
- redfish_sdk/models/fru.py +59 -0
- redfish_sdk/models/gpu.py +33 -0
- redfish_sdk/models/logs.py +56 -0
- redfish_sdk/models/managers.py +153 -0
- redfish_sdk/models/memory.py +50 -0
- redfish_sdk/models/network_adapter.py +76 -0
- redfish_sdk/models/oem.py +173 -0
- redfish_sdk/models/pcie_device.py +89 -0
- redfish_sdk/models/power.py +92 -0
- redfish_sdk/models/processor.py +50 -0
- redfish_sdk/models/registry.py +34 -0
- redfish_sdk/models/resource_key.py +70 -0
- redfish_sdk/models/root.py +50 -0
- redfish_sdk/models/session.py +42 -0
- redfish_sdk/models/storage.py +77 -0
- redfish_sdk/models/systems.py +194 -0
- redfish_sdk/models/task.py +54 -0
- redfish_sdk/models/thermal.py +111 -0
- 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
|
+
)
|