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.
Files changed (34) hide show
  1. {rackfish-1.0.1 → rackfish-1.0.3}/CHANGELOG.md +20 -0
  2. {rackfish-1.0.1 → rackfish-1.0.3}/PKG-INFO +9 -34
  3. {rackfish-1.0.1 → rackfish-1.0.3}/README.md +8 -33
  4. rackfish-1.0.3/docs/ETAG_SUPPORT.md +241 -0
  5. {rackfish-1.0.1 → rackfish-1.0.3}/docs/EXAMPLES.md +17 -1
  6. {rackfish-1.0.1 → rackfish-1.0.3}/docs/INDEX.md +79 -18
  7. rackfish-1.0.3/docs/SINGULAR_COLLECTION_ACCESS.md +354 -0
  8. rackfish-1.0.3/docs/SSL_CONFIGURATION.md +228 -0
  9. {rackfish-1.0.1 → rackfish-1.0.3}/docs/TESTS.md +1 -1
  10. {rackfish-1.0.1 → rackfish-1.0.3}/pyproject.toml +1 -1
  11. {rackfish-1.0.1 → rackfish-1.0.3}/rackfish/__init__.py +1 -1
  12. {rackfish-1.0.1 → rackfish-1.0.3}/rackfish/client.py +56 -12
  13. {rackfish-1.0.1 → rackfish-1.0.3}/rackfish.egg-info/PKG-INFO +9 -34
  14. {rackfish-1.0.1 → rackfish-1.0.3}/rackfish.egg-info/SOURCES.txt +6 -1
  15. {rackfish-1.0.1 → rackfish-1.0.3}/tests/test_common_usage.py +2 -2
  16. rackfish-1.0.3/tests/test_etag_support.py +119 -0
  17. rackfish-1.0.3/tests/test_singular_collection_access.py +167 -0
  18. {rackfish-1.0.1 → rackfish-1.0.3}/CONTRIBUTING.md +0 -0
  19. {rackfish-1.0.1 → rackfish-1.0.3}/LICENSE +0 -0
  20. {rackfish-1.0.1 → rackfish-1.0.3}/MANIFEST.in +0 -0
  21. {rackfish-1.0.1 → rackfish-1.0.3}/docs/COMPLETION_SUMMARY.md +0 -0
  22. {rackfish-1.0.1 → rackfish-1.0.3}/docs/OEM_LINKS_SURFACING.md +0 -0
  23. {rackfish-1.0.1 → rackfish-1.0.3}/docs/USE_CASES.md +0 -0
  24. {rackfish-1.0.1 → rackfish-1.0.3}/examples/demo_surfacing_comprehensive.py +0 -0
  25. {rackfish-1.0.1 → rackfish-1.0.3}/examples/example_oem_links.py +0 -0
  26. {rackfish-1.0.1 → rackfish-1.0.3}/examples/examples_comprehensive.py +0 -0
  27. {rackfish-1.0.1 → rackfish-1.0.3}/rackfish.egg-info/dependency_links.txt +0 -0
  28. {rackfish-1.0.1 → rackfish-1.0.3}/rackfish.egg-info/requires.txt +0 -0
  29. {rackfish-1.0.1 → rackfish-1.0.3}/rackfish.egg-info/top_level.txt +0 -0
  30. {rackfish-1.0.1 → rackfish-1.0.3}/requirements.txt +0 -0
  31. {rackfish-1.0.1 → rackfish-1.0.3}/setup.cfg +0 -0
  32. {rackfish-1.0.1 → rackfish-1.0.3}/tests/__init__.py +0 -0
  33. {rackfish-1.0.1 → rackfish-1.0.3}/tests/test_oem_links_surfacing.py +0 -0
  34. {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.1
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.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.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
- - **[README.md](README.md)** - Main documentation, installation, quick start, and overview
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
- - **[test_common_usage.py](test_common_usage.py)** - Tests for common Redfish operations
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
- - **[test_oem_links_surfacing.py](test_oem_links_surfacing.py)** - Tests for OEM and Links surfacing
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
- - **[test_recursion_fix.py](test_recursion_fix.py)** - Tests for recursion guard
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
- - **[examples_comprehensive.py](examples_comprehensive.py)** - Full working examples
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
- - **[demo_surfacing_comprehensive.py](demo_surfacing_comprehensive.py)** - OEM/Links surfacing demonstration
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
- - **[example_oem_links.py](example_oem_links.py)** - OEM/Links usage examples
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
- - **[rackfish.py](rackfish.py)** - Main library implementation
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
- - **[requirements.txt](requirements.txt)** - Python dependencies (only `requests`)
139
+ - `../requirements.txt` - Python dependencies (only `requests`)
140
+ - `../pyproject.toml` - Project configuration and metadata
101
141
 
102
142
  ## 🤖 AI Agent Instructions
103
143
 
104
- - **[.github/copilot-instructions.md](.github/copilot-instructions.md)** - Guidelines for AI coding agents
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
- ### I want to...
152
+ ### Common Tasks
113
153
 
114
154
  #### Learn the basics
115
- → Start with [README.md](README.md)
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
- → Look at [examples_comprehensive.py](examples_comprehensive.py)
187
+
188
+ → Look at `../examples/examples_comprehensive.py`
131
189
 
132
190
  #### Understand architecture
133
- → Read [.github/copilot-instructions.md](.github/copilot-instructions.md)
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 [.github/copilot-instructions.md](.github/copilot-instructions.md) for:
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
- - `import_certificate.py` - Existing certificate import utility
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.0
243
+ **Version:** 1.0.3
183
244
 
184
245
  **Supported Python:** 3.8+
185
246