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,89 @@
1
+ """
2
+ Task service manager — manages asynchronous tasks.
3
+
4
+ """
5
+ from __future__ import annotations
6
+
7
+ import logging
8
+ import time
9
+ from typing import TYPE_CHECKING, List
10
+
11
+ from ..models.task import Task
12
+
13
+ if TYPE_CHECKING:
14
+ from ..client import RedfishClient
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class TaskServiceManager:
20
+ """
21
+ Manages Redfish Task resources.
22
+
23
+
24
+ """
25
+
26
+ def __init__(self, client: RedfishClient):
27
+ self._client = client
28
+ self._http = client._http_client
29
+
30
+ def tasks(self) -> List[Task]:
31
+ """
32
+ Get the list of all tasks.
33
+ """
34
+ task_service = self._client._get_task_service()
35
+ return self._client._get_collection(task_service.tasks.odata_id, Task)
36
+
37
+ def get(self, task_id: str) -> Task:
38
+ """
39
+ Get a specific task by ID.
40
+
41
+ Args:
42
+ task_id: Task ID
43
+
44
+ Returns:
45
+ Task resource
46
+ """
47
+ task_service = self._client._get_task_service()
48
+ return self._http.get(f"{task_service.tasks.odata_id}/{task_id}", Task)
49
+
50
+ def wait_for_task(
51
+ self,
52
+ task_id: str,
53
+ poll_interval: int = 5,
54
+ timeout: int = 600,
55
+ ) -> Task:
56
+ """
57
+ Poll a task until it completes or times out.
58
+
59
+ Useful for monitoring long-running firmware update tasks.
60
+
61
+ Args:
62
+ task_id: Task ID to monitor
63
+ poll_interval: Seconds between polls (default 5)
64
+ timeout: Maximum wait time in seconds (default 600)
65
+
66
+ Returns:
67
+ Completed Task resource
68
+
69
+ Raises:
70
+ TimeoutError: If task does not complete within timeout
71
+ """
72
+ elapsed = 0
73
+ while elapsed < timeout:
74
+ task = self.get(task_id)
75
+ state = task.task_state or ""
76
+ logger.info(
77
+ "Task %s: state=%s, percent=%s%%",
78
+ task_id, state, task.percent_complete
79
+ )
80
+
81
+ if state in ("Completed", "Exception", "Killed", "Cancelled"):
82
+ return task
83
+
84
+ time.sleep(poll_interval)
85
+ elapsed += poll_interval
86
+
87
+ raise TimeoutError(
88
+ f"Task {task_id} did not complete within {timeout} seconds"
89
+ )
@@ -0,0 +1,99 @@
1
+ """
2
+ Update service manager — manages firmware updates.
3
+
4
+ Provides:
5
+ - Firmware inventory listing
6
+ - Client certificate listing
7
+ - Firmware update (SimpleUpdate) with multi-vendor strategy support
8
+
9
+ """
10
+ from __future__ import annotations
11
+
12
+ import logging
13
+ from typing import TYPE_CHECKING, Any, List, Optional
14
+
15
+ from ..models.common import RedfishResponse
16
+ from ..models.update import ClientCertificate, FirmwareInventory
17
+ from .update_strategies import UpdateStrategyRegistry, VendorDetector
18
+
19
+ if TYPE_CHECKING:
20
+ from ..client import RedfishClient
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ class UpdateServiceManager:
26
+ """
27
+ Manages Redfish Update service resources.
28
+
29
+ SimpleUpdate automatically detects the server vendor and applies
30
+ the appropriate request body format. Users can override the vendor
31
+ detection by passing the ``vendor`` parameter.
32
+ """
33
+
34
+ def __init__(self, client: RedfishClient):
35
+ self._client = client
36
+ self._http = client._http_client
37
+
38
+ def firmware_inventory(self) -> List[FirmwareInventory]:
39
+ """
40
+ Get the list of firmware inventory entries.
41
+
42
+ """
43
+ update_service = self._client._get_update_service()
44
+ return self._client._get_collection(
45
+ update_service.firmware_inventory.odata_id, FirmwareInventory
46
+ )
47
+
48
+ def client_certificates(self) -> List[ClientCertificate]:
49
+ """
50
+ Get the list of client certificates for firmware update authentication.
51
+
52
+ """
53
+ update_service = self._client._get_update_service()
54
+ return self._client._get_collection(
55
+ update_service.client_certificates.odata_id, ClientCertificate
56
+ )
57
+
58
+ def simple_update(
59
+ self,
60
+ image_uri: str,
61
+ transfer_protocol: str = "HTTP",
62
+ targets: Optional[list] = None,
63
+ vendor: Optional[str] = None,
64
+ **kwargs: Any,
65
+ ) -> RedfishResponse:
66
+ """
67
+ Trigger a firmware update via a remote image URI (e.g., NFS, HTTP).
68
+
69
+ Automatically detects the server vendor and uses the appropriate
70
+ request body format. The vendor can be manually overridden.
71
+
72
+ Args:
73
+ image_uri: URI of the firmware image (e.g., "http://nas/fw/bmc.bin")
74
+ transfer_protocol: Transfer protocol (e.g., "HTTP", "NFS", "TFTP")
75
+ targets: Optional list of firmware target paths
76
+ (e.g., ["/redfish/v1/Managers/1"] or ["ActiveBMC"])
77
+ vendor: Optional vendor override (e.g., "inspur", "lenovo").
78
+ If not set, the vendor is auto-detected.
79
+ **kwargs: Vendor-specific parameters. Common ones include:
80
+ - username (str): File server username (Inspur, Lenovo, H3C)
81
+ - password (str): File server password (Inspur, Lenovo, H3C)
82
+ - preserve_config (bool): Preserve configuration during update
83
+
84
+ Returns:
85
+ RedfishResponse (may contain a task reference for async update)
86
+ """
87
+ detected_vendor = vendor or VendorDetector.detect(self._client)
88
+ strategy = UpdateStrategyRegistry.get(detected_vendor)
89
+
90
+ logger.info(
91
+ "SimpleUpdate: vendor=%s, strategy=%s, ImageURI=%s",
92
+ detected_vendor,
93
+ type(strategy).__name__,
94
+ image_uri,
95
+ )
96
+
97
+ return strategy.execute(
98
+ self._client, image_uri, transfer_protocol, targets, **kwargs
99
+ )
@@ -0,0 +1,51 @@
1
+ """
2
+ Update strategies for multi-vendor firmware update support.
3
+
4
+ This package implements the Strategy Pattern for SimpleUpdate, allowing
5
+ each server vendor to have its own body-construction logic while keeping
6
+ a single entry point in UpdateServiceManager.simple_update().
7
+
8
+ Architecture:
9
+ BaseUpdateStrategy (ABC)
10
+ ├── GenericUpdateStrategy — standard Redfish fallback
11
+ ├── InspurUpdateStrategy — Inspur (浪潮)
12
+ ├── ZteUpdateStrategy — ZTE (中兴)
13
+ ├── H3cUpdateStrategy — H3C (新华三)
14
+ ├── NettrixUpdateStrategy — Nettrix (宁畅)
15
+ ├── XFusionUpdateStrategy — xFusion (超聚变)
16
+ └── LenovoUpdateStrategy — Lenovo (联想)
17
+
18
+ All strategies are auto-registered when this package is imported.
19
+ """
20
+
21
+ from .base import BaseUpdateStrategy, GenericUpdateStrategy
22
+ from .h3c import H3cUpdateStrategy
23
+ from .inspur import InspurUpdateStrategy
24
+ from .lenovo import LenovoUpdateStrategy
25
+ from .nettrix import NettrixUpdateStrategy
26
+ from .registry import UpdateStrategyRegistry
27
+ from .vendor_detect import VendorDetector
28
+ from .xfusion import XFusionUpdateStrategy
29
+ from .zte import ZteUpdateStrategy
30
+
31
+ # --- Auto-register all vendor strategies ---
32
+ UpdateStrategyRegistry.register("generic", GenericUpdateStrategy())
33
+ UpdateStrategyRegistry.register("inspur", InspurUpdateStrategy())
34
+ UpdateStrategyRegistry.register("zte", ZteUpdateStrategy())
35
+ UpdateStrategyRegistry.register("h3c", H3cUpdateStrategy())
36
+ UpdateStrategyRegistry.register("nettrix", NettrixUpdateStrategy())
37
+ UpdateStrategyRegistry.register("xfusion", XFusionUpdateStrategy())
38
+ UpdateStrategyRegistry.register("lenovo", LenovoUpdateStrategy())
39
+
40
+ __all__ = [
41
+ "BaseUpdateStrategy",
42
+ "GenericUpdateStrategy",
43
+ "InspurUpdateStrategy",
44
+ "ZteUpdateStrategy",
45
+ "H3cUpdateStrategy",
46
+ "NettrixUpdateStrategy",
47
+ "XFusionUpdateStrategy",
48
+ "LenovoUpdateStrategy",
49
+ "UpdateStrategyRegistry",
50
+ "VendorDetector",
51
+ ]
@@ -0,0 +1,101 @@
1
+ """
2
+ Base update strategy and generic (standard Redfish) fallback implementation.
3
+
4
+ All vendor-specific strategies inherit from BaseUpdateStrategy and override
5
+ the execute() method to build the vendor-appropriate request body.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import logging
10
+ from abc import ABC, abstractmethod
11
+ from typing import TYPE_CHECKING, Any, Dict, Optional
12
+
13
+ from ...models.common import RedfishResponse
14
+
15
+ if TYPE_CHECKING:
16
+ from ...client import RedfishClient
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ class BaseUpdateStrategy(ABC):
22
+ """
23
+ Abstract base class for firmware update strategies.
24
+
25
+ Subclasses must implement execute() to construct the vendor-specific
26
+ request body and POST it to the SimpleUpdate action target.
27
+ """
28
+
29
+ def _discover_action_target(self, client: RedfishClient) -> str:
30
+ """
31
+ Discover the SimpleUpdate action target URL from the UpdateService.
32
+
33
+ Falls back to the standard path if the target is not found in Actions.
34
+ """
35
+ update_service = client._get_update_service()
36
+ raw = client._http_client.get_raw(update_service.odata_id)
37
+ actions = raw.get("Actions", {})
38
+ simple_update_action = actions.get("#UpdateService.SimpleUpdate", {})
39
+ target = simple_update_action.get("target")
40
+
41
+ if not target:
42
+ target = f"{update_service.odata_id}/Actions/UpdateService.SimpleUpdate"
43
+
44
+ return target
45
+
46
+ @abstractmethod
47
+ def execute(
48
+ self,
49
+ client: RedfishClient,
50
+ image_uri: str,
51
+ transfer_protocol: str = "HTTP",
52
+ targets: Optional[list] = None,
53
+ **kwargs: Any,
54
+ ) -> RedfishResponse:
55
+ """
56
+ Execute the firmware update.
57
+
58
+ Args:
59
+ client: RedfishClient instance (provides HTTP access)
60
+ image_uri: URI of the firmware image
61
+ transfer_protocol: Transfer protocol (HTTP, NFS, TFTP, etc.)
62
+ targets: Optional list of firmware target paths
63
+ **kwargs: Vendor-specific parameters
64
+
65
+ Returns:
66
+ RedfishResponse (may contain a task reference for async update)
67
+ """
68
+ ...
69
+
70
+
71
+ class GenericUpdateStrategy(BaseUpdateStrategy):
72
+ """
73
+ Standard Redfish SimpleUpdate strategy (fallback).
74
+
75
+ Sends the standard body with ImageURI, TransferProtocol, and optional
76
+ Targets. This is equivalent to the original simple_update implementation
77
+ and is used when the vendor is not recognized.
78
+ """
79
+
80
+ def execute(
81
+ self,
82
+ client: RedfishClient,
83
+ image_uri: str,
84
+ transfer_protocol: str = "HTTP",
85
+ targets: Optional[list] = None,
86
+ **kwargs: Any,
87
+ ) -> RedfishResponse:
88
+ target = self._discover_action_target(client)
89
+
90
+ body: Dict[str, Any] = {
91
+ "ImageURI": image_uri,
92
+ "TransferProtocol": transfer_protocol,
93
+ }
94
+ if targets:
95
+ body["Targets"] = targets
96
+
97
+ logger.info(
98
+ "GenericUpdateStrategy: triggering SimpleUpdate with ImageURI=%s",
99
+ image_uri,
100
+ )
101
+ return client._http_client.post(target, RedfishResponse, raw_body=body)
@@ -0,0 +1,140 @@
1
+ """
2
+ H3C (新华三) firmware update strategy.
3
+
4
+ H3C BMCs do NOT send a separate TransferProtocol field; the protocol is
5
+ embedded in the ImageURI. For SFTP/CIFS, credentials are also embedded
6
+ in the URI (e.g., sftp://user:pwd@host/path).
7
+
8
+ Reference: UpdateService固件刷新接口 — 新华三
9
+ """
10
+ from __future__ import annotations
11
+
12
+ import logging
13
+ from typing import TYPE_CHECKING, Any, Dict, Optional
14
+ from urllib.parse import urlparse, urlunparse
15
+
16
+ from ...models.common import RedfishResponse
17
+ from .base import BaseUpdateStrategy
18
+
19
+ if TYPE_CHECKING:
20
+ from ...client import RedfishClient
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ class H3cUpdateStrategy(BaseUpdateStrategy):
26
+ """
27
+ Firmware update strategy for H3C (新华三) servers.
28
+
29
+ Supported kwargs:
30
+ username (str): File server username -> embedded in ImageURI
31
+ password (str): File server password -> embedded in ImageURI
32
+ preserve_config (bool): True -> "Retain", False -> "Restore"
33
+ Maps to Oem.Public.Preserve
34
+ restore_mode (str): Retain/Restore/ForceRestore -> Oem.Public.Preserve
35
+ (overrides preserve_config if both are set)
36
+ reboot_mode (str): Auto/Manual -> Oem.Public.RebootMode
37
+ backup (str): Backup option -> Oem.Public.Backup
38
+ bios_flash (str): Flash1/Flash2/Both -> Oem.Public.BiosFlash
39
+ image_md5_uri (str): MD5 checksum URI -> Oem.Public.ImageMd5URI
40
+ delay (str): Delay duration -> Oem.Public.Date
41
+ upgrade_type (str): all/bios/me/microcode -> Oem.Public.UpgradeType
42
+ """
43
+
44
+ def execute(
45
+ self,
46
+ client: RedfishClient,
47
+ image_uri: str,
48
+ transfer_protocol: str = "HTTP",
49
+ targets: Optional[list] = None,
50
+ **kwargs: Any,
51
+ ) -> RedfishResponse:
52
+ target = self._discover_action_target(client)
53
+
54
+ # H3C embeds credentials in the URI for SFTP/CIFS
55
+ final_uri = self._build_uri_with_credentials(
56
+ image_uri,
57
+ transfer_protocol,
58
+ kwargs.get("username"),
59
+ kwargs.get("password"),
60
+ )
61
+
62
+ # H3C does NOT send TransferProtocol as a separate field
63
+ body: Dict[str, Any] = {
64
+ "ImageURI": final_uri,
65
+ }
66
+
67
+ # Build OEM extension
68
+ oem_public: Dict[str, Any] = {}
69
+
70
+ # Preserve config: restore_mode takes precedence
71
+ if kwargs.get("restore_mode"):
72
+ oem_public["Preserve"] = kwargs["restore_mode"]
73
+ elif "preserve_config" in kwargs:
74
+ oem_public["Preserve"] = "Retain" if kwargs["preserve_config"] else "Restore"
75
+
76
+ if kwargs.get("reboot_mode"):
77
+ oem_public["RebootMode"] = kwargs["reboot_mode"]
78
+ if kwargs.get("backup"):
79
+ oem_public["Backup"] = kwargs["backup"]
80
+ if kwargs.get("bios_flash"):
81
+ oem_public["BiosFlash"] = kwargs["bios_flash"]
82
+ if kwargs.get("image_md5_uri"):
83
+ oem_public["ImageMd5URI"] = kwargs["image_md5_uri"]
84
+ if kwargs.get("delay"):
85
+ oem_public["Date"] = kwargs["delay"]
86
+ if kwargs.get("upgrade_type"):
87
+ oem_public["UpgradeType"] = kwargs["upgrade_type"]
88
+
89
+ if oem_public:
90
+ body["Oem"] = {"Public": oem_public}
91
+
92
+ logger.info(
93
+ "H3cUpdateStrategy: triggering SimpleUpdate with ImageURI=%s",
94
+ final_uri,
95
+ )
96
+ return client._http_client.post(target, RedfishResponse, raw_body=body)
97
+
98
+ @staticmethod
99
+ def _build_uri_with_credentials(
100
+ image_uri: str,
101
+ transfer_protocol: str,
102
+ username: Optional[str],
103
+ password: Optional[str],
104
+ ) -> str:
105
+ """
106
+ Embed credentials into the URI for protocols that require it.
107
+
108
+ For SFTP/CIFS, H3C expects: sftp://user:pwd@host/path
109
+ For HTTP/TFTP/NFS, credentials are not embedded.
110
+ """
111
+ if not username:
112
+ return image_uri
113
+
114
+ proto_lower = transfer_protocol.lower()
115
+ if proto_lower not in ("sftp", "cifs"):
116
+ return image_uri
117
+
118
+ parsed = urlparse(image_uri)
119
+
120
+ # If credentials are already in the URI, don't override
121
+ if parsed.username:
122
+ return image_uri
123
+
124
+ # Build netloc with credentials
125
+ credentials = username
126
+ if password:
127
+ credentials = f"{username}:{password}"
128
+
129
+ new_netloc = f"{credentials}@{parsed.hostname}"
130
+ if parsed.port:
131
+ new_netloc = f"{new_netloc}:{parsed.port}"
132
+
133
+ return urlunparse((
134
+ parsed.scheme or proto_lower,
135
+ new_netloc,
136
+ parsed.path,
137
+ parsed.params,
138
+ parsed.query,
139
+ parsed.fragment,
140
+ ))
@@ -0,0 +1,89 @@
1
+ """
2
+ Inspur (浪潮) firmware update strategy.
3
+
4
+ Inspur BMCs use heavy OEM extensions under Oem.Public, including
5
+ FlashItem selection, BIOS update type, PFR options, and file server
6
+ credentials in the request body.
7
+
8
+ Reference: UpdateService固件刷新接口 — 浪潮
9
+ """
10
+ from __future__ import annotations
11
+
12
+ import logging
13
+ from typing import TYPE_CHECKING, Any, Dict, Optional
14
+
15
+ from ...models.common import RedfishResponse
16
+ from .base import BaseUpdateStrategy
17
+
18
+ if TYPE_CHECKING:
19
+ from ...client import RedfishClient
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+ class InspurUpdateStrategy(BaseUpdateStrategy):
25
+ """
26
+ Firmware update strategy for Inspur (浪潮) servers.
27
+
28
+ Supported kwargs:
29
+ username (str): File server username -> body.Username
30
+ password (str): File server password -> body.Password
31
+ preserve_config (bool): Preserve config -> Oem.Public.PreserveConf
32
+ flash_item (str): BMC/BIOS/CPLD -> Oem.Public.FlashItem
33
+ bios_update_type (str): FullBIOS/SeamlessBIOS -> Oem.Public.BIOSUpdateType
34
+ bios_flash (str): Flash1/Flash2/Both -> Oem.Public.BiosFlash
35
+ pfr_type (bool): PFR update toggle -> Oem.Public.PFRType
36
+ pfr_region (str): PFR region -> Oem.Public.PFRRegion
37
+ pfr_update_dynamic (bool): PFR dynamic update -> Oem.Public.PFRUpdateDynamic
38
+ seamless_module (str): BIOS_ONLY/ME/MICROCODE -> Oem.Public.SeamlessModule
39
+ """
40
+
41
+ def execute(
42
+ self,
43
+ client: RedfishClient,
44
+ image_uri: str,
45
+ transfer_protocol: str = "HTTPS",
46
+ targets: Optional[list] = None,
47
+ **kwargs: Any,
48
+ ) -> RedfishResponse:
49
+ target = self._discover_action_target(client)
50
+
51
+ body: Dict[str, Any] = {
52
+ "ImageURI": image_uri,
53
+ "TransferProtocol": transfer_protocol,
54
+ }
55
+
56
+ # File server credentials
57
+ if kwargs.get("username"):
58
+ body["Username"] = kwargs["username"]
59
+ if kwargs.get("password"):
60
+ body["Password"] = kwargs["password"]
61
+
62
+ # Build OEM extension
63
+ oem_public: Dict[str, Any] = {}
64
+
65
+ if kwargs.get("flash_item"):
66
+ oem_public["FlashItem"] = kwargs["flash_item"]
67
+ if "preserve_config" in kwargs:
68
+ oem_public["PreserveConf"] = bool(kwargs["preserve_config"])
69
+ if kwargs.get("bios_update_type"):
70
+ oem_public["BIOSUpdateType"] = kwargs["bios_update_type"]
71
+ if kwargs.get("bios_flash"):
72
+ oem_public["BiosFlash"] = kwargs["bios_flash"]
73
+ if "pfr_type" in kwargs:
74
+ oem_public["PFRType"] = bool(kwargs["pfr_type"])
75
+ if kwargs.get("pfr_region"):
76
+ oem_public["PFRRegion"] = kwargs["pfr_region"]
77
+ if "pfr_update_dynamic" in kwargs:
78
+ oem_public["PFRUpdateDynamic"] = bool(kwargs["pfr_update_dynamic"])
79
+ if kwargs.get("seamless_module"):
80
+ oem_public["SeamlessModule"] = kwargs["seamless_module"]
81
+
82
+ if oem_public:
83
+ body["Oem"] = {"Public": oem_public}
84
+
85
+ logger.info(
86
+ "InspurUpdateStrategy: triggering SimpleUpdate with ImageURI=%s",
87
+ image_uri,
88
+ )
89
+ return client._http_client.post(target, RedfishResponse, raw_body=body)
@@ -0,0 +1,59 @@
1
+ """
2
+ Lenovo (联想) firmware update strategy.
3
+
4
+ Lenovo BMCs are closest to standard Redfish with no OEM extensions.
5
+ The only addition is optional Username/Password for file server auth.
6
+
7
+ Reference: UpdateService固件刷新接口 — 联想
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import logging
12
+ from typing import TYPE_CHECKING, Any, Dict, Optional
13
+
14
+ from ...models.common import RedfishResponse
15
+ from .base import BaseUpdateStrategy
16
+
17
+ if TYPE_CHECKING:
18
+ from ...client import RedfishClient
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ class LenovoUpdateStrategy(BaseUpdateStrategy):
24
+ """
25
+ Firmware update strategy for Lenovo (联想) servers.
26
+
27
+ Supported kwargs:
28
+ username (str): File server username -> body.Username
29
+ password (str): File server password -> body.Password
30
+ """
31
+
32
+ def execute(
33
+ self,
34
+ client: RedfishClient,
35
+ image_uri: str,
36
+ transfer_protocol: str = "HTTPS",
37
+ targets: Optional[list] = None,
38
+ **kwargs: Any,
39
+ ) -> RedfishResponse:
40
+ target = self._discover_action_target(client)
41
+
42
+ body: Dict[str, Any] = {
43
+ "ImageURI": image_uri,
44
+ "TransferProtocol": transfer_protocol,
45
+ }
46
+ if targets:
47
+ body["Targets"] = targets
48
+
49
+ # Lenovo supports file server credentials
50
+ if kwargs.get("username"):
51
+ body["Username"] = kwargs["username"]
52
+ if kwargs.get("password"):
53
+ body["Password"] = kwargs["password"]
54
+
55
+ logger.info(
56
+ "LenovoUpdateStrategy: triggering SimpleUpdate with ImageURI=%s",
57
+ image_uri,
58
+ )
59
+ return client._http_client.post(target, RedfishResponse, raw_body=body)
@@ -0,0 +1,56 @@
1
+ """
2
+ Nettrix (宁畅) firmware update strategy.
3
+
4
+ Nettrix BMCs are closest to standard Redfish. The only extension is
5
+ SaveConfig at the body top level (not under Oem).
6
+
7
+ Reference: UpdateService固件刷新接口 — 宁畅
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import logging
12
+ from typing import TYPE_CHECKING, Any, Dict, Optional
13
+
14
+ from ...models.common import RedfishResponse
15
+ from .base import BaseUpdateStrategy
16
+
17
+ if TYPE_CHECKING:
18
+ from ...client import RedfishClient
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ class NettrixUpdateStrategy(BaseUpdateStrategy):
24
+ """
25
+ Firmware update strategy for Nettrix (宁畅) servers.
26
+
27
+ Supported kwargs:
28
+ preserve_config (bool): Preserve config -> body.SaveConfig
29
+ """
30
+
31
+ def execute(
32
+ self,
33
+ client: RedfishClient,
34
+ image_uri: str,
35
+ transfer_protocol: str = "HTTPS",
36
+ targets: Optional[list] = None,
37
+ **kwargs: Any,
38
+ ) -> RedfishResponse:
39
+ target = self._discover_action_target(client)
40
+
41
+ body: Dict[str, Any] = {
42
+ "ImageURI": image_uri,
43
+ "TransferProtocol": transfer_protocol,
44
+ }
45
+ if targets:
46
+ body["Targets"] = targets
47
+
48
+ # Nettrix puts SaveConfig at the body top level
49
+ if "preserve_config" in kwargs:
50
+ body["SaveConfig"] = bool(kwargs["preserve_config"])
51
+
52
+ logger.info(
53
+ "NettrixUpdateStrategy: triggering SimpleUpdate with ImageURI=%s",
54
+ image_uri,
55
+ )
56
+ return client._http_client.post(target, RedfishResponse, raw_body=body)