redfish-python-sdk 1.3.0__tar.gz → 1.3.1__tar.gz
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.3.0/redfish_python_sdk.egg-info → redfish_python_sdk-1.3.1}/PKG-INFO +1 -1
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/pyproject.toml +1 -1
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1/redfish_python_sdk.egg-info}/PKG-INFO +1 -1
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_python_sdk.egg-info/SOURCES.txt +2 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/__init__.py +1 -1
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/client.py +2 -2
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/http_client.py +11 -2
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/chassis.py +14 -8
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/event.py +5 -2
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/log_collect_strategies/__init__.py +12 -3
- redfish_python_sdk-1.3.1/redfish_sdk/managers/log_collect_strategies/h3c.py +212 -0
- redfish_python_sdk-1.3.1/redfish_sdk/managers/log_collect_strategies/inspur.py +279 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/log_collect_strategies/lenovo.py +12 -7
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/log_collect_strategies/zte.py +91 -51
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/systems.py +105 -1
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/update_strategies/vendor_detect.py +1 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/drive.py +11 -1
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/fru.py +4 -1
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/log_collect.py +25 -19
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/oem.py +37 -4
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/registry.py +6 -2
- redfish_python_sdk-1.3.1/tests/test_event_headers_mock.py +52 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/tests/test_log_collect_mock.py +539 -33
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/tests/test_models.py +10 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/tests/test_update_strategies.py +5 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/tests/test_v1_1_apis_mock.py +2 -2
- redfish_python_sdk-1.3.0/redfish_sdk/managers/log_collect_strategies/inspur.py +0 -148
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/LICENSE +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/README.md +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_python_sdk.egg-info/dependency_links.txt +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_python_sdk.egg-info/requires.txt +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_python_sdk.egg-info/top_level.txt +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/exceptions.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/__init__.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/_log_helpers.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/account.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/composition.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/log_collect_strategies/base.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/log_collect_strategies/enginetech.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/log_collect_strategies/registry.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/log_collect_strategies/smoothcompute.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/log_collect_strategies/xfusion.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/managers.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/oem_extractors/__init__.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/oem_extractors/base.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/oem_extractors/h3c.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/oem_extractors/inspur.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/oem_extractors/lenovo.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/oem_extractors/nettrix.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/oem_extractors/registry.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/oem_extractors/xfusion.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/oem_extractors/zte.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/registries.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/session.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/system_resources.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/task.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/telemetry.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/update.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/update_strategies/__init__.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/update_strategies/base.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/update_strategies/enginetech.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/update_strategies/h3c.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/update_strategies/inspur.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/update_strategies/lenovo.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/update_strategies/nettrix.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/update_strategies/registry.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/update_strategies/xfusion.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/managers/update_strategies/zte.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/__init__.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/account.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/actions.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/chassis.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/check.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/common.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/composition.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/event.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/gpu.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/logs.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/managers.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/memory.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/network_adapter.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/pcie_device.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/power.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/processor.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/resource_key.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/root.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/session.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/storage.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/system_resources.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/systems.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/task.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/telemetry.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/thermal.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_sdk/models/update.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/setup.cfg +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/tests/test_action_models.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/tests/test_chassis_discovery.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/tests/test_client_mock.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/tests/test_composition_mock.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/tests/test_exception_hints_mock.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/tests/test_firmware_upload_mock.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/tests/test_http_retry_mock.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/tests/test_log_entries_mock.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/tests/test_manager_actions_mock.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/tests/test_manager_cache_lifecycle.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/tests/test_manager_discovery.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/tests/test_manager_network_protocol.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/tests/test_models_mock.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/tests/test_nested_discovery.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/tests/test_oem_extractors.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/tests/test_offline_json.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/tests/test_real_bmc.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/tests/test_sdk_mock.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/tests/test_serial_interfaces_mock.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/tests/test_system_resources_mock.py +0 -0
- {redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/tests/test_telemetry_mock.py +0 -0
{redfish_python_sdk-1.3.0 → redfish_python_sdk-1.3.1}/redfish_python_sdk.egg-info/SOURCES.txt
RENAMED
|
@@ -27,6 +27,7 @@ redfish_sdk/managers/update.py
|
|
|
27
27
|
redfish_sdk/managers/log_collect_strategies/__init__.py
|
|
28
28
|
redfish_sdk/managers/log_collect_strategies/base.py
|
|
29
29
|
redfish_sdk/managers/log_collect_strategies/enginetech.py
|
|
30
|
+
redfish_sdk/managers/log_collect_strategies/h3c.py
|
|
30
31
|
redfish_sdk/managers/log_collect_strategies/inspur.py
|
|
31
32
|
redfish_sdk/managers/log_collect_strategies/lenovo.py
|
|
32
33
|
redfish_sdk/managers/log_collect_strategies/registry.py
|
|
@@ -88,6 +89,7 @@ tests/test_action_models.py
|
|
|
88
89
|
tests/test_chassis_discovery.py
|
|
89
90
|
tests/test_client_mock.py
|
|
90
91
|
tests/test_composition_mock.py
|
|
92
|
+
tests/test_event_headers_mock.py
|
|
91
93
|
tests/test_exception_hints_mock.py
|
|
92
94
|
tests/test_firmware_upload_mock.py
|
|
93
95
|
tests/test_http_retry_mock.py
|
|
@@ -68,7 +68,7 @@ from .models.systems import BootOption
|
|
|
68
68
|
from .models.telemetry import MetricDefinition, MetricReport, MetricReportDefinition, TelemetryService, Triggers
|
|
69
69
|
from .models.update import FirmwareUpdateResponse
|
|
70
70
|
|
|
71
|
-
__version__ = "1.3.
|
|
71
|
+
__version__ = "1.3.1"
|
|
72
72
|
__author__ = "RedNote Infrastructure"
|
|
73
73
|
|
|
74
74
|
__all__ = [
|
|
@@ -1653,8 +1653,8 @@ class RedfishClient:
|
|
|
1653
1653
|
context: Optional context string identifying the subscription.
|
|
1654
1654
|
protocol: Wire protocol; defaults to ``"Redfish"``.
|
|
1655
1655
|
http_headers: Optional headers to send on the callback POST.
|
|
1656
|
-
Pass
|
|
1657
|
-
|
|
1656
|
+
Pass a ``dict`` or a ``list[dict]``. Dictionaries are converted
|
|
1657
|
+
to the standard array form; ``raw_body`` preserves vendor forms.
|
|
1658
1658
|
origin_resources: Optional list of ``{"@odata.id": "..."}``.
|
|
1659
1659
|
subscription_type: Optional ``SubscriptionType`` value.
|
|
1660
1660
|
registry_prefixes: Optional message-registry filter list.
|
|
@@ -307,8 +307,17 @@ class RedfishHttpClient:
|
|
|
307
307
|
except Exception:
|
|
308
308
|
pass
|
|
309
309
|
|
|
310
|
-
|
|
311
|
-
|
|
310
|
+
# 404 ("not found") is routinely an expected outcome handled by callers
|
|
311
|
+
# via fallback (e.g. Chassis/1 -> Chassis/Self) or treated as "absent"
|
|
312
|
+
# (e.g. optional sub-resources). Logging it at ERROR produces alarming
|
|
313
|
+
# noise for what is normal control flow, so it is recorded at DEBUG only.
|
|
314
|
+
# All other non-2xx responses remain at ERROR.
|
|
315
|
+
if code == 404:
|
|
316
|
+
logger.debug("Request returned 404 (not found): %s %s",
|
|
317
|
+
response.request.method, path)
|
|
318
|
+
else:
|
|
319
|
+
logger.error("Request failed: %s %s -> HTTP %d, body: %s",
|
|
320
|
+
response.request.method, path, code, body[:500])
|
|
312
321
|
|
|
313
322
|
if code in (401, 403):
|
|
314
323
|
raise RedfishAuthError(code)
|
|
@@ -197,7 +197,20 @@ class ChassisManager:
|
|
|
197
197
|
"""
|
|
198
198
|
chassis = self.get(chassis_id)
|
|
199
199
|
|
|
200
|
-
# Strategy 1:
|
|
200
|
+
# Strategy 1: Chassis.Drives (canonical chassis-owned drive collection).
|
|
201
|
+
# Preferred because some BMCs expose the authoritative Drive resources
|
|
202
|
+
# (with fields such as IndicatorLED) only here, while Chassis.Links.Drives
|
|
203
|
+
# may resolve to Storage-side Drive references that lack those fields.
|
|
204
|
+
if chassis.drives:
|
|
205
|
+
try:
|
|
206
|
+
drives = self._client._get_collection(chassis.drives.odata_id, Drive)
|
|
207
|
+
if drives:
|
|
208
|
+
return drives
|
|
209
|
+
except RedfishException as exc:
|
|
210
|
+
logger.warning("Chassis.Drives collection failed: %s", exc)
|
|
211
|
+
|
|
212
|
+
# Strategy 2: Chassis.Links.Drives (links may resolve to Storage-side
|
|
213
|
+
# Drive resources). Kept as fallback for BMCs that only populate links.
|
|
201
214
|
if chassis.links and chassis.links.drives:
|
|
202
215
|
drive_links = chassis.links.drives
|
|
203
216
|
if drive_links:
|
|
@@ -223,13 +236,6 @@ class ChassisManager:
|
|
|
223
236
|
if result:
|
|
224
237
|
return result
|
|
225
238
|
|
|
226
|
-
# Strategy 2: Use Chassis.Drives collection endpoint
|
|
227
|
-
if chassis.drives:
|
|
228
|
-
try:
|
|
229
|
-
return self._client._get_collection(chassis.drives.odata_id, Drive)
|
|
230
|
-
except RedfishException as exc:
|
|
231
|
-
logger.warning("Chassis.Drives collection failed: %s", exc)
|
|
232
|
-
|
|
233
239
|
return []
|
|
234
240
|
|
|
235
241
|
def network_adapters(self, chassis_id: Optional[str] = None) -> List[NetworkAdapter]:
|
|
@@ -135,7 +135,8 @@ class EventServiceManager:
|
|
|
135
135
|
protocol: Wire protocol; defaults to ``"Redfish"``.
|
|
136
136
|
http_headers: Optional headers to be sent on the callback POST.
|
|
137
137
|
Pass a ``dict`` (``{"X-Auth-Token": "..."}``) or a
|
|
138
|
-
``list[dict]
|
|
138
|
+
``list[dict]``. Dictionaries are serialized as the standard
|
|
139
|
+
array of header objects. Use ``raw_body`` for vendor shapes.
|
|
139
140
|
origin_resources: Optional list of ``{"@odata.id": "..."}``
|
|
140
141
|
filtering the resources of interest.
|
|
141
142
|
subscription_type: Optional ``SubscriptionType`` value
|
|
@@ -167,7 +168,9 @@ class EventServiceManager:
|
|
|
167
168
|
if context is not None:
|
|
168
169
|
body["Context"] = context
|
|
169
170
|
if http_headers is not None:
|
|
170
|
-
body["HttpHeaders"] =
|
|
171
|
+
body["HttpHeaders"] = (
|
|
172
|
+
[http_headers] if isinstance(http_headers, dict) else http_headers
|
|
173
|
+
)
|
|
171
174
|
if origin_resources is not None:
|
|
172
175
|
body["OriginResources"] = origin_resources
|
|
173
176
|
if subscription_type is not None:
|
|
@@ -10,13 +10,17 @@ Architecture:
|
|
|
10
10
|
BaseLogCollectStrategy (ABC)
|
|
11
11
|
├── GenericLogCollectStrategy — standard Redfish (DMTF) default
|
|
12
12
|
├── XFusionLogCollectStrategy — xFusion (超聚变), OEM diagnostic body
|
|
13
|
+
├── H3cLogCollectStrategy — H3C (新华三), Manager.Dump +
|
|
14
|
+
Manager.GeneralDownload SDS flow
|
|
13
15
|
├── InspurLogCollectStrategy — Inspur (浪潮), collection-level
|
|
14
16
|
CollectAllLog / DownloadAllLog pair
|
|
17
|
+
│ └── IeitLogCollectStrategy — IEIT SYSTEMS, member actions +
|
|
18
|
+
in-flight Task reuse
|
|
15
19
|
├── LenovoLogCollectStrategy — Lenovo (联想, AMI-based),
|
|
16
20
|
CollectAllLog + private
|
|
17
21
|
CollectProgress + DownloadAllLog
|
|
18
22
|
├── ZteLogCollectStrategy — ZTE (中兴), Dump + Dump/Progress +
|
|
19
|
-
|
|
23
|
+
ExportLogs
|
|
20
24
|
└── SmoothcomputeLogCollectStrategy — smoothcompute (顺算), OEM
|
|
21
25
|
DiagnosticService.CollectBlackBox /
|
|
22
26
|
ExportBlackBox (trigger returns a
|
|
@@ -25,7 +29,7 @@ Architecture:
|
|
|
25
29
|
|
|
26
30
|
Only vendors that genuinely differ from the standard DMTF behaviour get a
|
|
27
31
|
dedicated strategy. Vendors whose ``CollectDiagnosticData`` matches the
|
|
28
|
-
standard body (e.g.
|
|
32
|
+
standard body (e.g. Nettrix) intentionally have no file and fall back
|
|
29
33
|
to :class:`GenericLogCollectStrategy` via the registry.
|
|
30
34
|
|
|
31
35
|
Vendor detection is shared with the firmware ``update_strategies`` package
|
|
@@ -37,7 +41,8 @@ All strategies are auto-registered when this package is imported.
|
|
|
37
41
|
from ..update_strategies.vendor_detect import VendorDetector
|
|
38
42
|
from .base import BaseLogCollectStrategy, GenericLogCollectStrategy
|
|
39
43
|
from .enginetech import EnginetechLogCollectStrategy
|
|
40
|
-
from .
|
|
44
|
+
from .h3c import H3cLogCollectStrategy
|
|
45
|
+
from .inspur import IeitLogCollectStrategy, InspurLogCollectStrategy
|
|
41
46
|
from .lenovo import LenovoLogCollectStrategy
|
|
42
47
|
from .registry import LogCollectStrategyRegistry
|
|
43
48
|
from .smoothcompute import SmoothcomputeLogCollectStrategy
|
|
@@ -48,7 +53,9 @@ from .zte import ZteLogCollectStrategy
|
|
|
48
53
|
LogCollectStrategyRegistry.register("generic", GenericLogCollectStrategy())
|
|
49
54
|
LogCollectStrategyRegistry.register("enginetech", EnginetechLogCollectStrategy())
|
|
50
55
|
LogCollectStrategyRegistry.register("xfusion", XFusionLogCollectStrategy())
|
|
56
|
+
LogCollectStrategyRegistry.register("h3c", H3cLogCollectStrategy())
|
|
51
57
|
LogCollectStrategyRegistry.register("inspur", InspurLogCollectStrategy())
|
|
58
|
+
LogCollectStrategyRegistry.register("ieit", IeitLogCollectStrategy())
|
|
52
59
|
LogCollectStrategyRegistry.register("lenovo", LenovoLogCollectStrategy())
|
|
53
60
|
LogCollectStrategyRegistry.register("zte", ZteLogCollectStrategy())
|
|
54
61
|
LogCollectStrategyRegistry.register(
|
|
@@ -60,6 +67,8 @@ __all__ = [
|
|
|
60
67
|
"GenericLogCollectStrategy",
|
|
61
68
|
"EnginetechLogCollectStrategy",
|
|
62
69
|
"XFusionLogCollectStrategy",
|
|
70
|
+
"H3cLogCollectStrategy",
|
|
71
|
+
"IeitLogCollectStrategy",
|
|
63
72
|
"InspurLogCollectStrategy",
|
|
64
73
|
"LenovoLogCollectStrategy",
|
|
65
74
|
"ZteLogCollectStrategy",
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"""H3C (新华三) SDS diagnostic-log collection strategy.
|
|
2
|
+
|
|
3
|
+
H3C HDM does not expose the standard per-LogService
|
|
4
|
+
``#LogService.CollectDiagnosticData`` action. The Manager instead publishes
|
|
5
|
+
OEM ``Manager.Dump`` and ``Manager.GeneralDownload`` actions. Collection
|
|
6
|
+
creates an asynchronous Redfish Task; after it completes, the SDS archive is
|
|
7
|
+
downloaded with a second POST.
|
|
8
|
+
|
|
9
|
+
Observed on H3C HDM firmware 2.09, ``#Manager.GeneralDownload`` is advertised
|
|
10
|
+
as an empty object without a ``target``. For that specific malformed action
|
|
11
|
+
descriptor, this strategy derives the conventional action URI from the
|
|
12
|
+
advertised Manager resource rather than treating an unadvertised action as
|
|
13
|
+
available.
|
|
14
|
+
"""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
from typing import TYPE_CHECKING, Any, Dict, Optional
|
|
19
|
+
|
|
20
|
+
from ...models.common import Collection, Link
|
|
21
|
+
from ...models.managers import Manager
|
|
22
|
+
from ...models.task import Task
|
|
23
|
+
from .base import BaseLogCollectStrategy
|
|
24
|
+
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from ...client import RedfishClient
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
_DUMP_ACTION = "#Manager.Dump"
|
|
31
|
+
_DOWNLOAD_ACTION = "#Manager.GeneralDownload"
|
|
32
|
+
_SDS_PATH = "/mnt/sdmmc0p1/sds/bmc/dump.tar.gz"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class H3cLogCollectStrategy(BaseLogCollectStrategy):
|
|
36
|
+
"""Collect and download H3C SDS bundles through Manager OEM actions."""
|
|
37
|
+
|
|
38
|
+
def find_existing_task(
|
|
39
|
+
self,
|
|
40
|
+
client: "RedfishClient",
|
|
41
|
+
log_services_odata_id: str,
|
|
42
|
+
manager_id: Optional[str] = None,
|
|
43
|
+
) -> Optional[Task]:
|
|
44
|
+
"""Always create a fresh SDS bundle at H3C's fixed artifact path."""
|
|
45
|
+
return None
|
|
46
|
+
|
|
47
|
+
def discover_collect_target(
|
|
48
|
+
self,
|
|
49
|
+
client: "RedfishClient",
|
|
50
|
+
log_services_odata_id: str,
|
|
51
|
+
log_id: Optional[str],
|
|
52
|
+
) -> Optional[str]:
|
|
53
|
+
"""Return the Manager-level ``Manager.Dump`` action target."""
|
|
54
|
+
manager = self._manager_for_log_services(client, log_services_odata_id)
|
|
55
|
+
advertised, target = self._manager_oem_action(manager, _DUMP_ACTION)
|
|
56
|
+
return target if advertised else None
|
|
57
|
+
|
|
58
|
+
def build_collect_body(
|
|
59
|
+
self,
|
|
60
|
+
diagnostic_data_type: Optional[str] = None,
|
|
61
|
+
oem_params: Optional[Dict[str, Any]] = None,
|
|
62
|
+
) -> Dict[str, Any]:
|
|
63
|
+
"""Build H3C's request for generating an SDS archive on the BMC."""
|
|
64
|
+
body: Dict[str, Any] = {
|
|
65
|
+
"Type": "URI",
|
|
66
|
+
"Content": _SDS_PATH,
|
|
67
|
+
}
|
|
68
|
+
if diagnostic_data_type:
|
|
69
|
+
body["LogType"] = diagnostic_data_type
|
|
70
|
+
if oem_params:
|
|
71
|
+
body.update(oem_params)
|
|
72
|
+
return body
|
|
73
|
+
|
|
74
|
+
def trigger(
|
|
75
|
+
self,
|
|
76
|
+
client: "RedfishClient",
|
|
77
|
+
log_services_odata_id: str,
|
|
78
|
+
*,
|
|
79
|
+
log_id: Optional[str] = None,
|
|
80
|
+
diagnostic_data_type: Optional[str] = None,
|
|
81
|
+
oem_params: Optional[Dict[str, Any]] = None,
|
|
82
|
+
manager_id: Optional[str] = None,
|
|
83
|
+
) -> Task:
|
|
84
|
+
"""POST ``Manager.Dump`` and convert its task collection response."""
|
|
85
|
+
from ...exceptions import RedfishValidationError
|
|
86
|
+
|
|
87
|
+
manager = self._manager_for_log_services(client, log_services_odata_id)
|
|
88
|
+
advertised, target = self._manager_oem_action(manager, _DUMP_ACTION)
|
|
89
|
+
if not advertised or not target:
|
|
90
|
+
raise RedfishValidationError(
|
|
91
|
+
f"H3C Manager {manager.odata_id!r} does not expose "
|
|
92
|
+
f"{_DUMP_ACTION} with an action target"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
body = self.build_collect_body(diagnostic_data_type, oem_params)
|
|
96
|
+
logger.info("H3C Manager.Dump POST %s body=%s", target, body)
|
|
97
|
+
response = client._http_client.post(
|
|
98
|
+
target, Collection[Link], raw_body=body
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
task_url = next(
|
|
102
|
+
(
|
|
103
|
+
member.odata_id
|
|
104
|
+
for member in response.members or []
|
|
105
|
+
if member.odata_id
|
|
106
|
+
),
|
|
107
|
+
None,
|
|
108
|
+
)
|
|
109
|
+
if not task_url and response.odata_id and "/Tasks/" in response.odata_id:
|
|
110
|
+
task_url = response.odata_id
|
|
111
|
+
if not task_url:
|
|
112
|
+
raise RedfishValidationError(
|
|
113
|
+
"H3C Manager.Dump did not return a Redfish Task reference"
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
return Task.model_construct(
|
|
117
|
+
id=task_url.rstrip("/").rsplit("/", 1)[-1],
|
|
118
|
+
odata_id=task_url,
|
|
119
|
+
_h3c_manager_id=self._manager_id(manager),
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
def wait_until_ready(
|
|
123
|
+
self,
|
|
124
|
+
client: "RedfishClient",
|
|
125
|
+
task: Task,
|
|
126
|
+
poll_interval: int = 5,
|
|
127
|
+
timeout: int = 1800,
|
|
128
|
+
) -> Task:
|
|
129
|
+
"""Wait through TaskService while retaining the owning Manager id."""
|
|
130
|
+
finished = super().wait_until_ready(
|
|
131
|
+
client, task, poll_interval=poll_interval, timeout=timeout
|
|
132
|
+
)
|
|
133
|
+
manager_id = getattr(task, "_h3c_manager_id", None)
|
|
134
|
+
if manager_id:
|
|
135
|
+
finished = finished.model_copy(
|
|
136
|
+
update={"_h3c_manager_id": manager_id}
|
|
137
|
+
)
|
|
138
|
+
return finished
|
|
139
|
+
|
|
140
|
+
def download_artifact(
|
|
141
|
+
self,
|
|
142
|
+
client: "RedfishClient",
|
|
143
|
+
task: Task,
|
|
144
|
+
output_path: Optional[str] = None,
|
|
145
|
+
) -> "bytes | str":
|
|
146
|
+
"""POST ``Manager.GeneralDownload`` and save the returned archive."""
|
|
147
|
+
from ...exceptions import RedfishValidationError
|
|
148
|
+
|
|
149
|
+
manager_id = getattr(task, "_h3c_manager_id", None)
|
|
150
|
+
manager = client.get_manager(manager_id)
|
|
151
|
+
advertised, target = self._manager_oem_action(
|
|
152
|
+
manager, _DOWNLOAD_ACTION
|
|
153
|
+
)
|
|
154
|
+
if not advertised:
|
|
155
|
+
raise RedfishValidationError(
|
|
156
|
+
f"H3C Manager {manager.odata_id!r} does not advertise "
|
|
157
|
+
f"{_DOWNLOAD_ACTION}"
|
|
158
|
+
)
|
|
159
|
+
if not target and manager.odata_id:
|
|
160
|
+
target = (
|
|
161
|
+
f"{manager.odata_id.rstrip('/')}"
|
|
162
|
+
"/Actions/Oem/Public/Manager.GeneralDownload"
|
|
163
|
+
)
|
|
164
|
+
if not target:
|
|
165
|
+
raise RedfishValidationError(
|
|
166
|
+
f"Could not resolve {_DOWNLOAD_ACTION} for H3C Manager"
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
body = {
|
|
170
|
+
"TransferProtocol": "HTTPS",
|
|
171
|
+
"Content": _SDS_PATH,
|
|
172
|
+
}
|
|
173
|
+
logger.info("H3C Manager.GeneralDownload POST %s", target)
|
|
174
|
+
return client._http_client.download_via_post(
|
|
175
|
+
target, output_path, raw_body=body
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
@staticmethod
|
|
179
|
+
def _manager_for_log_services(
|
|
180
|
+
client: "RedfishClient", log_services_odata_id: str
|
|
181
|
+
) -> Manager:
|
|
182
|
+
"""Resolve the Manager owning an advertised LogServices collection."""
|
|
183
|
+
suffix = "/LogServices"
|
|
184
|
+
manager_url = log_services_odata_id.rstrip("/")
|
|
185
|
+
if manager_url.endswith(suffix):
|
|
186
|
+
manager_url = manager_url[: -len(suffix)]
|
|
187
|
+
manager_id = manager_url.rstrip("/").rsplit("/", 1)[-1]
|
|
188
|
+
return client.get_manager(manager_id)
|
|
189
|
+
|
|
190
|
+
@staticmethod
|
|
191
|
+
def _manager_id(manager: Manager) -> Optional[str]:
|
|
192
|
+
if manager.odata_id:
|
|
193
|
+
return manager.odata_id.rstrip("/").rsplit("/", 1)[-1]
|
|
194
|
+
return str(manager.id) if manager.id is not None else None
|
|
195
|
+
|
|
196
|
+
@staticmethod
|
|
197
|
+
def _manager_oem_action(
|
|
198
|
+
manager: Manager, action_name: str
|
|
199
|
+
) -> tuple[bool, Optional[str]]:
|
|
200
|
+
"""Return whether an H3C OEM action is advertised and its target."""
|
|
201
|
+
if manager.actions is None:
|
|
202
|
+
return False, None
|
|
203
|
+
actions = manager.actions.model_dump(by_alias=True, exclude_none=True)
|
|
204
|
+
oem = actions.get("Oem")
|
|
205
|
+
public = oem.get("Public") if isinstance(oem, dict) else None
|
|
206
|
+
if not isinstance(public, dict) or action_name not in public:
|
|
207
|
+
return False, None
|
|
208
|
+
descriptor = public[action_name]
|
|
209
|
+
if not isinstance(descriptor, dict):
|
|
210
|
+
return True, None
|
|
211
|
+
target = descriptor.get("target")
|
|
212
|
+
return True, target if isinstance(target, str) and target else None
|