rackfish 1.0.1__tar.gz → 1.0.3__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.
- {rackfish-1.0.1 → rackfish-1.0.3}/CHANGELOG.md +20 -0
- {rackfish-1.0.1 → rackfish-1.0.3}/PKG-INFO +9 -34
- {rackfish-1.0.1 → rackfish-1.0.3}/README.md +8 -33
- rackfish-1.0.3/docs/ETAG_SUPPORT.md +241 -0
- {rackfish-1.0.1 → rackfish-1.0.3}/docs/EXAMPLES.md +17 -1
- {rackfish-1.0.1 → rackfish-1.0.3}/docs/INDEX.md +79 -18
- rackfish-1.0.3/docs/SINGULAR_COLLECTION_ACCESS.md +354 -0
- rackfish-1.0.3/docs/SSL_CONFIGURATION.md +228 -0
- {rackfish-1.0.1 → rackfish-1.0.3}/docs/TESTS.md +1 -1
- {rackfish-1.0.1 → rackfish-1.0.3}/pyproject.toml +1 -1
- {rackfish-1.0.1 → rackfish-1.0.3}/rackfish/__init__.py +1 -1
- {rackfish-1.0.1 → rackfish-1.0.3}/rackfish/client.py +56 -12
- {rackfish-1.0.1 → rackfish-1.0.3}/rackfish.egg-info/PKG-INFO +9 -34
- {rackfish-1.0.1 → rackfish-1.0.3}/rackfish.egg-info/SOURCES.txt +6 -1
- {rackfish-1.0.1 → rackfish-1.0.3}/tests/test_common_usage.py +2 -2
- rackfish-1.0.3/tests/test_etag_support.py +119 -0
- rackfish-1.0.3/tests/test_singular_collection_access.py +167 -0
- {rackfish-1.0.1 → rackfish-1.0.3}/CONTRIBUTING.md +0 -0
- {rackfish-1.0.1 → rackfish-1.0.3}/LICENSE +0 -0
- {rackfish-1.0.1 → rackfish-1.0.3}/MANIFEST.in +0 -0
- {rackfish-1.0.1 → rackfish-1.0.3}/docs/COMPLETION_SUMMARY.md +0 -0
- {rackfish-1.0.1 → rackfish-1.0.3}/docs/OEM_LINKS_SURFACING.md +0 -0
- {rackfish-1.0.1 → rackfish-1.0.3}/docs/USE_CASES.md +0 -0
- {rackfish-1.0.1 → rackfish-1.0.3}/examples/demo_surfacing_comprehensive.py +0 -0
- {rackfish-1.0.1 → rackfish-1.0.3}/examples/example_oem_links.py +0 -0
- {rackfish-1.0.1 → rackfish-1.0.3}/examples/examples_comprehensive.py +0 -0
- {rackfish-1.0.1 → rackfish-1.0.3}/rackfish.egg-info/dependency_links.txt +0 -0
- {rackfish-1.0.1 → rackfish-1.0.3}/rackfish.egg-info/requires.txt +0 -0
- {rackfish-1.0.1 → rackfish-1.0.3}/rackfish.egg-info/top_level.txt +0 -0
- {rackfish-1.0.1 → rackfish-1.0.3}/requirements.txt +0 -0
- {rackfish-1.0.1 → rackfish-1.0.3}/setup.cfg +0 -0
- {rackfish-1.0.1 → rackfish-1.0.3}/tests/__init__.py +0 -0
- {rackfish-1.0.1 → rackfish-1.0.3}/tests/test_oem_links_surfacing.py +0 -0
- {rackfish-1.0.1 → rackfish-1.0.3}/tests/test_recursion_fix.py +0 -0
@@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
|
+
## [Unreleased]
|
9
|
+
|
10
|
+
## [1.0.3] - 2025-10-15
|
11
|
+
|
12
|
+
### Added
|
13
|
+
|
14
|
+
- Singular collection access: Access single-member collections directly (e.g., `client.System` instead of `next(iter(client.Systems))`)
|
15
|
+
- Works at all resource hierarchy levels (client and nested resources)
|
16
|
+
- Automatic fallback to traditional methods when collection doesn't have exactly one member
|
17
|
+
- Comprehensive test suite for singular access (`test_singular_collection_access.py`)
|
18
|
+
- Documentation for singular collection access feature
|
19
|
+
- New test suite for ETag functionality (`test_etag_support.py`)
|
20
|
+
|
21
|
+
### Fixed
|
22
|
+
|
23
|
+
- Added ETag support for PATCH requests via If-Match header to prevent HTTP 412 errors
|
24
|
+
- PATCH operations now automatically include ETags when available in resource metadata
|
25
|
+
- Resources without ETags continue to work normally (backward compatible)
|
26
|
+
- Suppressed urllib3 InsecureRequestWarning when `verify_ssl=False` is used
|
27
|
+
|
8
28
|
## [1.0.1] - 2025-10-15
|
9
29
|
|
10
30
|
### Fixed
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: rackfish
|
3
|
-
Version: 1.0.
|
3
|
+
Version: 1.0.3
|
4
4
|
Summary: A lightweight, dynamic Python client for Redfish BMC APIs
|
5
5
|
Author-email: Dmitrii Frolov <thefrolov@mts.ru>
|
6
6
|
License: MIT
|
@@ -58,17 +58,6 @@ A lightweight, dynamic Python client for interacting with Redfish BMC (Baseboard
|
|
58
58
|
- 📚 **Collection Support** - Iterate Redfish collections naturally
|
59
59
|
- 🔐 **Flexible Auth** - Session tokens or Basic authentication
|
60
60
|
|
61
|
-
## Features
|
62
|
-
|
63
|
-
- **Zero Dependencies** (except `requests`) - Minimal footprint
|
64
|
-
- **Lazy Loading** - Resources fetched on-demand for performance
|
65
|
-
- **Dynamic Attributes** - JSON properties become Python attributes
|
66
|
-
- **OEM Surfacing** - Vendor extensions automatically accessible
|
67
|
-
- **Links Surfacing** - Related resources directly navigable
|
68
|
-
- **Action Validation** - Parameter validation using ActionInfo schemas
|
69
|
-
- **Collection Support** - Iterate Redfish collections naturally
|
70
|
-
- **Session & Basic Auth** - Flexible authentication options
|
71
|
-
|
72
61
|
## Installation
|
73
62
|
|
74
63
|
### From PyPI (recommended)
|
@@ -101,8 +90,11 @@ client = RedfishClient("https://bmc.example.com", "admin", "password",
|
|
101
90
|
use_session=True, verify_ssl=False)
|
102
91
|
root = client.connect()
|
103
92
|
|
104
|
-
# Power control
|
105
|
-
system = next(iter(client.Systems))
|
93
|
+
# Power control - multiple ways to access systems
|
94
|
+
system = next(iter(client.Systems)) # Traditional iteration
|
95
|
+
system = client.Systems.Members[0] # Direct member access
|
96
|
+
system = client.System # Singular form (if only one member!)
|
97
|
+
|
106
98
|
system.Reset(ResetType="GracefulRestart")
|
107
99
|
|
108
100
|
# Access OEM properties (auto-surfaced)
|
@@ -166,7 +158,8 @@ for temp in chassis.Thermal.Temperatures:
|
|
166
158
|
print(f"{temp.Name}: {temp.ReadingCelsius}°C")
|
167
159
|
```
|
168
160
|
|
169
|
-
See [EXAMPLES.md](EXAMPLES.md) for 100+ more examples covering:
|
161
|
+
See [docs/EXAMPLES.md](docs/EXAMPLES.md) for 100+ more examples covering:
|
162
|
+
|
170
163
|
- BIOS configuration
|
171
164
|
- Certificate management
|
172
165
|
- Virtual media (KVM)
|
@@ -175,24 +168,6 @@ See [EXAMPLES.md](EXAMPLES.md) for 100+ more examples covering:
|
|
175
168
|
- SEL/log collection
|
176
169
|
- And much more...
|
177
170
|
|
178
|
-
## Testing
|
179
|
-
|
180
|
-
Run the test suite:
|
181
|
-
|
182
|
-
```bash
|
183
|
-
# Install with dev dependencies
|
184
|
-
pip install -e ".[dev]"
|
185
|
-
|
186
|
-
# Run all tests
|
187
|
-
pytest tests/
|
188
|
-
|
189
|
-
# Run with coverage
|
190
|
-
pytest --cov=rackfish tests/
|
191
|
-
|
192
|
-
# Run specific test file
|
193
|
-
pytest tests/test_common_usage.py
|
194
|
-
```
|
195
|
-
|
196
171
|
## Project Structure
|
197
172
|
|
198
173
|
```
|
@@ -356,7 +331,7 @@ See LICENSE file.
|
|
356
331
|
|
357
332
|
## Version
|
358
333
|
|
359
|
-
Current version: 1.0.
|
334
|
+
Current version: 1.0.3
|
360
335
|
|
361
336
|
## Requirements
|
362
337
|
|
@@ -20,17 +20,6 @@ A lightweight, dynamic Python client for interacting with Redfish BMC (Baseboard
|
|
20
20
|
- 📚 **Collection Support** - Iterate Redfish collections naturally
|
21
21
|
- 🔐 **Flexible Auth** - Session tokens or Basic authentication
|
22
22
|
|
23
|
-
## Features
|
24
|
-
|
25
|
-
- **Zero Dependencies** (except `requests`) - Minimal footprint
|
26
|
-
- **Lazy Loading** - Resources fetched on-demand for performance
|
27
|
-
- **Dynamic Attributes** - JSON properties become Python attributes
|
28
|
-
- **OEM Surfacing** - Vendor extensions automatically accessible
|
29
|
-
- **Links Surfacing** - Related resources directly navigable
|
30
|
-
- **Action Validation** - Parameter validation using ActionInfo schemas
|
31
|
-
- **Collection Support** - Iterate Redfish collections naturally
|
32
|
-
- **Session & Basic Auth** - Flexible authentication options
|
33
|
-
|
34
23
|
## Installation
|
35
24
|
|
36
25
|
### From PyPI (recommended)
|
@@ -63,8 +52,11 @@ client = RedfishClient("https://bmc.example.com", "admin", "password",
|
|
63
52
|
use_session=True, verify_ssl=False)
|
64
53
|
root = client.connect()
|
65
54
|
|
66
|
-
# Power control
|
67
|
-
system = next(iter(client.Systems))
|
55
|
+
# Power control - multiple ways to access systems
|
56
|
+
system = next(iter(client.Systems)) # Traditional iteration
|
57
|
+
system = client.Systems.Members[0] # Direct member access
|
58
|
+
system = client.System # Singular form (if only one member!)
|
59
|
+
|
68
60
|
system.Reset(ResetType="GracefulRestart")
|
69
61
|
|
70
62
|
# Access OEM properties (auto-surfaced)
|
@@ -128,7 +120,8 @@ for temp in chassis.Thermal.Temperatures:
|
|
128
120
|
print(f"{temp.Name}: {temp.ReadingCelsius}°C")
|
129
121
|
```
|
130
122
|
|
131
|
-
See [EXAMPLES.md](EXAMPLES.md) for 100+ more examples covering:
|
123
|
+
See [docs/EXAMPLES.md](docs/EXAMPLES.md) for 100+ more examples covering:
|
124
|
+
|
132
125
|
- BIOS configuration
|
133
126
|
- Certificate management
|
134
127
|
- Virtual media (KVM)
|
@@ -137,24 +130,6 @@ See [EXAMPLES.md](EXAMPLES.md) for 100+ more examples covering:
|
|
137
130
|
- SEL/log collection
|
138
131
|
- And much more...
|
139
132
|
|
140
|
-
## Testing
|
141
|
-
|
142
|
-
Run the test suite:
|
143
|
-
|
144
|
-
```bash
|
145
|
-
# Install with dev dependencies
|
146
|
-
pip install -e ".[dev]"
|
147
|
-
|
148
|
-
# Run all tests
|
149
|
-
pytest tests/
|
150
|
-
|
151
|
-
# Run with coverage
|
152
|
-
pytest --cov=rackfish tests/
|
153
|
-
|
154
|
-
# Run specific test file
|
155
|
-
pytest tests/test_common_usage.py
|
156
|
-
```
|
157
|
-
|
158
133
|
## Project Structure
|
159
134
|
|
160
135
|
```
|
@@ -318,7 +293,7 @@ See LICENSE file.
|
|
318
293
|
|
319
294
|
## Version
|
320
295
|
|
321
|
-
Current version: 1.0.
|
296
|
+
Current version: 1.0.3
|
322
297
|
|
323
298
|
## Requirements
|
324
299
|
|
@@ -0,0 +1,241 @@
|
|
1
|
+
# ETag Support in rackfish
|
2
|
+
|
3
|
+
## Overview
|
4
|
+
|
5
|
+
As of version 1.0.2 (unreleased), rackfish automatically includes ETag support for PATCH requests to prevent HTTP 412 (Precondition Failed) errors from Redfish BMCs that require concurrency control.
|
6
|
+
|
7
|
+
## Background
|
8
|
+
|
9
|
+
Many Redfish BMC implementations require the `If-Match` header with an ETag value when performing PATCH operations. This is a standard HTTP precondition mechanism to prevent lost updates in concurrent environments.
|
10
|
+
|
11
|
+
### The Problem
|
12
|
+
|
13
|
+
When a PATCH request is sent without the `If-Match` header:
|
14
|
+
- BMCs may respond with **HTTP 428 Precondition Required**
|
15
|
+
- BMCs may respond with **HTTP 412 Precondition Failed**
|
16
|
+
- The update operation fails even though the data is valid
|
17
|
+
|
18
|
+
### The Solution
|
19
|
+
|
20
|
+
rackfish now automatically:
|
21
|
+
1. Extracts the `@odata.etag` value from resources when they are fetched
|
22
|
+
2. Includes the ETag in the `If-Match` header for all PATCH requests
|
23
|
+
3. Falls back gracefully when no ETag is present (backward compatible)
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
No changes are needed to your existing code! ETag support works automatically.
|
28
|
+
|
29
|
+
### Example: Attribute Assignment
|
30
|
+
|
31
|
+
```python
|
32
|
+
from rackfish import RedfishClient
|
33
|
+
|
34
|
+
client = RedfishClient("https://bmc.example.com", "admin", "password")
|
35
|
+
client.connect()
|
36
|
+
|
37
|
+
# Get a system resource (ETag is automatically extracted if present)
|
38
|
+
system = client.Systems[0]
|
39
|
+
|
40
|
+
# Modify an attribute - ETag is automatically included in PATCH
|
41
|
+
system.AssetTag = "MyNewServer"
|
42
|
+
# Behind the scenes: PATCH with If-Match header containing the ETag
|
43
|
+
```
|
44
|
+
|
45
|
+
### Example: Using .patch() Method
|
46
|
+
|
47
|
+
```python
|
48
|
+
# Complex updates using .patch()
|
49
|
+
system.patch({
|
50
|
+
"AssetTag": "UpdatedTag",
|
51
|
+
"IndicatorLED": "Lit"
|
52
|
+
})
|
53
|
+
# ETag is automatically included in the PATCH request
|
54
|
+
```
|
55
|
+
|
56
|
+
### Example: Resources Without ETags
|
57
|
+
|
58
|
+
```python
|
59
|
+
# Works seamlessly with BMCs that don't provide ETags
|
60
|
+
system.PowerState = "On"
|
61
|
+
# PATCH is sent without If-Match header (no error)
|
62
|
+
```
|
63
|
+
|
64
|
+
## How It Works
|
65
|
+
|
66
|
+
### 1. ETag Extraction
|
67
|
+
|
68
|
+
When a resource is fetched, rackfish looks for the `@odata.etag` field in the JSON response:
|
69
|
+
|
70
|
+
```json
|
71
|
+
{
|
72
|
+
"@odata.id": "/redfish/v1/Systems/1",
|
73
|
+
"@odata.type": "#ComputerSystem.v1_0_0.ComputerSystem",
|
74
|
+
"@odata.etag": "W/\"12345678\"",
|
75
|
+
"Id": "1",
|
76
|
+
"AssetTag": "MyServer"
|
77
|
+
}
|
78
|
+
```
|
79
|
+
|
80
|
+
The ETag value `W/"12345678"` is stored internally in the resource object.
|
81
|
+
|
82
|
+
### 2. ETag Inclusion in PATCH
|
83
|
+
|
84
|
+
When a PATCH operation is triggered (via attribute assignment or `.patch()` method), rackfish:
|
85
|
+
|
86
|
+
1. Checks if the resource has an `@odata.etag` value
|
87
|
+
2. If present, includes it in the `If-Match` header
|
88
|
+
3. If absent, sends the PATCH without the header
|
89
|
+
|
90
|
+
HTTP request example with ETag:
|
91
|
+
```http
|
92
|
+
PATCH /redfish/v1/Systems/1 HTTP/1.1
|
93
|
+
Host: bmc.example.com
|
94
|
+
Content-Type: application/json
|
95
|
+
If-Match: W/"12345678"
|
96
|
+
|
97
|
+
{"AssetTag": "NewValue"}
|
98
|
+
```
|
99
|
+
|
100
|
+
### 3. Backward Compatibility
|
101
|
+
|
102
|
+
Resources without ETags continue to work normally:
|
103
|
+
- Old BMCs that don't provide ETags work as before
|
104
|
+
- No breaking changes to existing code
|
105
|
+
- Optional feature that activates only when ETags are present
|
106
|
+
|
107
|
+
## Implementation Details
|
108
|
+
|
109
|
+
### Modified Methods
|
110
|
+
|
111
|
+
#### `RedfishClient.patch(path, data, etag=None)`
|
112
|
+
|
113
|
+
The `patch` method now accepts an optional `etag` parameter:
|
114
|
+
|
115
|
+
```python
|
116
|
+
def patch(self, path: str, data: dict[str, Any], etag: str | None = None) -> None:
|
117
|
+
url = _safe_join(self.base_url, path) if not path.startswith("http") else path
|
118
|
+
headers = {}
|
119
|
+
if etag:
|
120
|
+
headers["If-Match"] = etag
|
121
|
+
resp = self._http.patch(url, json=data, headers=headers, timeout=self.timeout)
|
122
|
+
if resp.status_code not in (200, 204):
|
123
|
+
raise RedfishError(f"PATCH {url} -> {resp.status_code} {resp.text}")
|
124
|
+
```
|
125
|
+
|
126
|
+
#### `RedfishResource.patch(updates)`
|
127
|
+
|
128
|
+
The resource `patch` method extracts and passes the ETag:
|
129
|
+
|
130
|
+
```python
|
131
|
+
def patch(self, updates: dict[str, Any]) -> None:
|
132
|
+
if not self._path:
|
133
|
+
raise RedfishError("PATCH requires a resource path")
|
134
|
+
self._ensure_fetched()
|
135
|
+
etag = self._raw.get("@odata.etag")
|
136
|
+
self._client.patch(self._path, updates, etag=etag)
|
137
|
+
self.refresh()
|
138
|
+
```
|
139
|
+
|
140
|
+
#### `RedfishResource.__setattr__(name, value)`
|
141
|
+
|
142
|
+
Attribute assignment also passes the ETag:
|
143
|
+
|
144
|
+
```python
|
145
|
+
if name in self._raw and not isinstance(self._raw[name], (dict, list)):
|
146
|
+
etag = self._raw.get("@odata.etag")
|
147
|
+
self._client.patch(self._path, {name: value}, etag=etag)
|
148
|
+
self._raw[name] = value
|
149
|
+
object.__setattr__(self, name, value)
|
150
|
+
```
|
151
|
+
|
152
|
+
## Testing
|
153
|
+
|
154
|
+
A comprehensive test suite verifies ETag functionality:
|
155
|
+
|
156
|
+
```bash
|
157
|
+
pytest tests/test_etag_support.py -v
|
158
|
+
```
|
159
|
+
|
160
|
+
Tests cover:
|
161
|
+
1. ✅ ETag included in PATCH via attribute assignment
|
162
|
+
2. ✅ ETag included in PATCH via `.patch()` method
|
163
|
+
3. ✅ PATCH works without ETag present (backward compatibility)
|
164
|
+
|
165
|
+
## Benefits
|
166
|
+
|
167
|
+
### 1. Prevents Lost Updates
|
168
|
+
|
169
|
+
ETags ensure that updates are based on the current state of the resource:
|
170
|
+
- Detects when another client has modified the resource
|
171
|
+
- Prevents overwriting changes made by concurrent operations
|
172
|
+
- Provides optimistic concurrency control
|
173
|
+
|
174
|
+
### 2. BMC Compatibility
|
175
|
+
|
176
|
+
Works with BMCs that require precondition headers:
|
177
|
+
- ✅ HPE iLO (when ETag enforcement is enabled)
|
178
|
+
- ✅ Dell iDRAC (some configurations)
|
179
|
+
- ✅ Huawei iBMC
|
180
|
+
- ✅ Lenovo XClarity Controller
|
181
|
+
- ✅ Supermicro BMCs
|
182
|
+
- ✅ Generic Redfish implementations
|
183
|
+
|
184
|
+
### 3. Zero Breaking Changes
|
185
|
+
|
186
|
+
- Existing code works without modification
|
187
|
+
- Optional feature that activates automatically
|
188
|
+
- Graceful fallback for BMCs without ETag support
|
189
|
+
|
190
|
+
## Troubleshooting
|
191
|
+
|
192
|
+
### HTTP 412 Precondition Failed
|
193
|
+
|
194
|
+
If you still get 412 errors after this fix:
|
195
|
+
|
196
|
+
1. **Stale ETag**: The resource was modified by another client
|
197
|
+
```python
|
198
|
+
# Solution: Refresh the resource before updating
|
199
|
+
system.refresh()
|
200
|
+
system.AssetTag = "NewValue"
|
201
|
+
```
|
202
|
+
|
203
|
+
2. **Wrong ETag format**: Some BMCs are picky about ETag format
|
204
|
+
- Check BMC logs for details
|
205
|
+
- Verify the ETag value in the response
|
206
|
+
|
207
|
+
3. **ETag not updated after refresh**: May be a BMC bug
|
208
|
+
```python
|
209
|
+
# Workaround: Fetch a fresh instance
|
210
|
+
system = client.Systems[0] # Get fresh data
|
211
|
+
system.AssetTag = "NewValue"
|
212
|
+
```
|
213
|
+
|
214
|
+
### HTTP 428 Precondition Required
|
215
|
+
|
216
|
+
If you get 428 errors, it means:
|
217
|
+
- The BMC requires an ETag but the resource doesn't have one
|
218
|
+
- The resource hasn't been fetched yet (link stub)
|
219
|
+
|
220
|
+
```python
|
221
|
+
# Solution: Ensure resource is fetched
|
222
|
+
system._ensure_fetched() # Force fetch if needed
|
223
|
+
system.patch({"AssetTag": "NewValue"})
|
224
|
+
```
|
225
|
+
|
226
|
+
## References
|
227
|
+
|
228
|
+
- [RFC 7232: HTTP Conditional Requests](https://tools.ietf.org/html/rfc7232)
|
229
|
+
- [Redfish Specification: ETags](https://www.dmtf.org/sites/default/files/standards/documents/DSP0266_1.15.1.pdf)
|
230
|
+
- [HTTP 412 Precondition Failed](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/412)
|
231
|
+
- [HTTP 428 Precondition Required](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/428)
|
232
|
+
|
233
|
+
## Version History
|
234
|
+
|
235
|
+
- **v1.0.2** (unreleased): ETag support added
|
236
|
+
- **v1.0.1**: Initial release without ETag support
|
237
|
+
- **v1.0.0**: Initial release
|
238
|
+
|
239
|
+
---
|
240
|
+
|
241
|
+
For more information, see the [main documentation](INDEX.md) or [examples](EXAMPLES.md).
|
@@ -18,6 +18,22 @@ for system in client.Systems:
|
|
18
18
|
print(f"System: {system.Id} - {system.Name} - Power: {system.PowerState}")
|
19
19
|
```
|
20
20
|
|
21
|
+
### Alternative: Singular Access for Single-Member Collections
|
22
|
+
|
23
|
+
If you know there's only one system (common in 1U servers), use singular form:
|
24
|
+
|
25
|
+
```python
|
26
|
+
# Only works if Systems collection has exactly 1 member
|
27
|
+
system = client.System
|
28
|
+
print(f"System: {system.Id} - {system.Name} - Power: {system.PowerState}")
|
29
|
+
|
30
|
+
# Traditional methods also work:
|
31
|
+
# system = next(iter(client.Systems))
|
32
|
+
# system = client.Systems.Members[0]
|
33
|
+
```
|
34
|
+
|
35
|
+
See [SINGULAR_COLLECTION_ACCESS.md](SINGULAR_COLLECTION_ACCESS.md) for details.
|
36
|
+
|
21
37
|
## 3. Access OEM and Links Properties (Surfaced)
|
22
38
|
|
23
39
|
```python
|
@@ -792,5 +808,5 @@ client.delete("/redfish/v1/SomeCollection/Item")
|
|
792
808
|
**See also:**
|
793
809
|
|
794
810
|
- [OEM_LINKS_SURFACING.md](OEM_LINKS_SURFACING.md) for advanced surfacing details
|
795
|
-
- [test_common_usage.py](test_common_usage.py) for test examples
|
811
|
+
- [../tests/test_common_usage.py](../tests/test_common_usage.py) for test examples
|
796
812
|
|
@@ -5,13 +5,15 @@ Complete documentation for the rackfish dynamic Redfish client library.
|
|
5
5
|
## 📚 Documentation Files
|
6
6
|
|
7
7
|
### Getting Started
|
8
|
-
|
8
|
+
|
9
|
+
- **[../README.md](../README.md)** - Main documentation, installation, quick start, and overview
|
9
10
|
- Features and benefits
|
10
11
|
- Quick start examples
|
11
12
|
- Architecture overview
|
12
13
|
- Supported vendors
|
13
14
|
|
14
15
|
### Usage Examples
|
16
|
+
|
15
17
|
- **[EXAMPLES.md](EXAMPLES.md)** - Comprehensive code examples for 150+ Redfish operations
|
16
18
|
- User management (create, delete, update)
|
17
19
|
- Storage and logical drives
|
@@ -31,13 +33,32 @@ Complete documentation for the rackfish dynamic Redfish client library.
|
|
31
33
|
- Maps original function names to rackfish patterns
|
32
34
|
|
33
35
|
### Advanced Topics
|
36
|
+
|
34
37
|
- **[OEM_LINKS_SURFACING.md](OEM_LINKS_SURFACING.md)** - Details on automatic OEM and Links surfacing
|
35
38
|
- How vendor extensions are surfaced
|
36
39
|
- Collision avoidance mechanism
|
37
40
|
- Before/after code comparisons
|
38
41
|
- Benefits and backward compatibility
|
39
42
|
|
43
|
+
- **[SINGULAR_COLLECTION_ACCESS.md](SINGULAR_COLLECTION_ACCESS.md)** - Singular access to single-member collections
|
44
|
+
- Convenient `client.System` instead of `next(iter(client.Systems))`
|
45
|
+
- Works for any collection with exactly one member
|
46
|
+
- Best practices and error handling
|
47
|
+
- Usage examples and limitations
|
48
|
+
|
49
|
+
- **[ETAG_SUPPORT.md](ETAG_SUPPORT.md)** - ETag support for PATCH requests
|
50
|
+
- Automatic If-Match header inclusion
|
51
|
+
- Prevents HTTP 412 errors
|
52
|
+
- Usage examples and troubleshooting
|
53
|
+
|
54
|
+
- **[SSL_CONFIGURATION.md](SSL_CONFIGURATION.md)** - SSL/TLS configuration guide
|
55
|
+
- Secure vs insecure connections
|
56
|
+
- Self-signed certificate handling
|
57
|
+
- Custom CA bundles
|
58
|
+
- Best practices and troubleshooting
|
59
|
+
|
40
60
|
### Testing
|
61
|
+
|
41
62
|
- **[TESTS.md](TESTS.md)** - Test suite documentation
|
42
63
|
- How to run tests
|
43
64
|
- Test coverage overview
|
@@ -45,26 +66,42 @@ Complete documentation for the rackfish dynamic Redfish client library.
|
|
45
66
|
|
46
67
|
## 🧪 Test Files
|
47
68
|
|
48
|
-
|
69
|
+
Located in `../tests/`:
|
70
|
+
|
71
|
+
- **test_common_usage.py** - Tests for common Redfish operations
|
49
72
|
- Resource traversal and collection iteration
|
50
73
|
- OEM/Links surfacing
|
51
74
|
- Action invocation
|
52
75
|
- PATCH updates
|
53
76
|
- Create/delete operations
|
54
77
|
|
55
|
-
- **
|
78
|
+
- **test_oem_links_surfacing.py** - Tests for OEM and Links surfacing
|
56
79
|
- OEM property surfacing (Huawei, Dell vendors)
|
57
80
|
- Links resource surfacing (Chassis, ManagedBy)
|
58
81
|
- Collision avoidance
|
59
82
|
- OEM action binding
|
60
83
|
|
61
|
-
- **
|
84
|
+
- **test_recursion_fix.py** - Tests for recursion guard
|
62
85
|
- 50-level deep nested structure handling
|
63
86
|
- Lazy loading verification
|
64
87
|
|
88
|
+
- **test_etag_support.py** - Tests for ETag functionality
|
89
|
+
- ETag extraction and usage
|
90
|
+
- PATCH with If-Match header
|
91
|
+
- Backward compatibility
|
92
|
+
|
93
|
+
- **test_singular_collection_access.py** - Tests for singular collection access
|
94
|
+
- Single member access (success cases)
|
95
|
+
- Multiple members (error handling)
|
96
|
+
- Empty collections (error handling)
|
97
|
+
- Nested collections support
|
98
|
+
- Client-level and resource-level access
|
99
|
+
|
65
100
|
## 💻 Example Files
|
66
101
|
|
67
|
-
|
102
|
+
Located in `../examples/`:
|
103
|
+
|
104
|
+
- **examples_comprehensive.py** - Full working examples
|
68
105
|
- Real-world usage patterns
|
69
106
|
- User management
|
70
107
|
- Power control
|
@@ -77,18 +114,20 @@ Complete documentation for the rackfish dynamic Redfish client library.
|
|
77
114
|
- LDAP configuration
|
78
115
|
- (Most write operations commented out for safety)
|
79
116
|
|
80
|
-
- **
|
117
|
+
- **demo_surfacing_comprehensive.py** - OEM/Links surfacing demonstration
|
81
118
|
- Mock Huawei BMC simulation
|
82
119
|
- Before/after comparison
|
83
120
|
- Backward compatibility verification
|
84
121
|
|
85
|
-
- **
|
122
|
+
- **example_oem_links.py** - OEM/Links usage examples
|
86
123
|
- Benefits demonstration
|
87
124
|
- Navigation pattern improvements
|
88
125
|
|
89
126
|
## 🔧 Source Files
|
90
127
|
|
91
|
-
|
128
|
+
Located in `../rackfish/`:
|
129
|
+
|
130
|
+
- **client.py** - Main library implementation
|
92
131
|
- `RedfishClient` class - HTTP client and session management
|
93
132
|
- `RedfishResource` class - Dynamic resource object graph
|
94
133
|
- Lazy loading mechanism
|
@@ -97,11 +136,12 @@ Complete documentation for the rackfish dynamic Redfish client library.
|
|
97
136
|
|
98
137
|
## 📋 Configuration Files
|
99
138
|
|
100
|
-
-
|
139
|
+
- `../requirements.txt` - Python dependencies (only `requests`)
|
140
|
+
- `../pyproject.toml` - Project configuration and metadata
|
101
141
|
|
102
142
|
## 🤖 AI Agent Instructions
|
103
143
|
|
104
|
-
- **[
|
144
|
+
- **[../.github/copilot-instructions.md](../.github/copilot-instructions.md)** - Guidelines for AI coding agents
|
105
145
|
- Architecture details
|
106
146
|
- Design patterns
|
107
147
|
- Extension guidelines
|
@@ -109,28 +149,47 @@ Complete documentation for the rackfish dynamic Redfish client library.
|
|
109
149
|
|
110
150
|
## 🚀 Quick Navigation by Task
|
111
151
|
|
112
|
-
###
|
152
|
+
### Common Tasks
|
113
153
|
|
114
154
|
#### Learn the basics
|
115
|
-
|
155
|
+
|
156
|
+
→ Start with [../README.md](../README.md)
|
116
157
|
|
117
158
|
#### See code examples
|
159
|
+
|
118
160
|
→ Go to [EXAMPLES.md](EXAMPLES.md)
|
119
161
|
|
120
162
|
#### Find a specific use case
|
163
|
+
|
121
164
|
→ Check [USE_CASES.md](USE_CASES.md) index
|
122
165
|
|
123
166
|
#### Understand OEM surfacing
|
167
|
+
|
124
168
|
→ Read [OEM_LINKS_SURFACING.md](OEM_LINKS_SURFACING.md)
|
125
169
|
|
170
|
+
#### Configure SSL/TLS
|
171
|
+
|
172
|
+
→ See [SSL_CONFIGURATION.md](SSL_CONFIGURATION.md)
|
173
|
+
|
174
|
+
#### Understand ETag support
|
175
|
+
|
176
|
+
→ Read [ETAG_SUPPORT.md](ETAG_SUPPORT.md)
|
177
|
+
|
178
|
+
#### Use singular collection access
|
179
|
+
|
180
|
+
→ Read [SINGULAR_COLLECTION_ACCESS.md](SINGULAR_COLLECTION_ACCESS.md)
|
181
|
+
|
126
182
|
#### Run tests
|
183
|
+
|
127
184
|
→ See [TESTS.md](TESTS.md)
|
128
185
|
|
129
186
|
#### Write real code
|
130
|
-
|
187
|
+
|
188
|
+
→ Look at `../examples/examples_comprehensive.py`
|
131
189
|
|
132
190
|
#### Understand architecture
|
133
|
-
|
191
|
+
|
192
|
+
→ Read [../.github/copilot-instructions.md](../.github/copilot-instructions.md)
|
134
193
|
|
135
194
|
## 📊 Coverage Summary
|
136
195
|
|
@@ -163,7 +222,8 @@ Complete documentation for the rackfish dynamic Redfish client library.
|
|
163
222
|
|
164
223
|
## 📝 Contributing
|
165
224
|
|
166
|
-
See [
|
225
|
+
See [../.github/copilot-instructions.md](../.github/copilot-instructions.md) for:
|
226
|
+
|
167
227
|
- Code architecture
|
168
228
|
- Extension guidelines
|
169
229
|
- Design patterns
|
@@ -171,15 +231,16 @@ See [.github/copilot-instructions.md](.github/copilot-instructions.md) for:
|
|
171
231
|
|
172
232
|
## 🔗 Related Files
|
173
233
|
|
174
|
-
-
|
234
|
+
- `../CHANGELOG.md` - Version history and changes
|
235
|
+
- `../CONTRIBUTING.md` - Contribution guidelines
|
236
|
+
- `../LICENSE` - MIT License
|
175
237
|
- `.python-version` - Python version specification
|
176
|
-
- `.github/` - GitHub-specific files
|
177
238
|
|
178
239
|
---
|
179
240
|
|
180
241
|
**Last Updated:** October 15, 2025
|
181
242
|
|
182
|
-
**Version:** 1.0.
|
243
|
+
**Version:** 1.0.3
|
183
244
|
|
184
245
|
**Supported Python:** 3.8+
|
185
246
|
|