RedfishResource¶
RedfishResource
¶
Redfish API for DMTF BMC compatibility.
Redfish does not use the standard PiKVM response format,
so it calls PiKVM.request() directly.
Source code in src/aiopikvm/resources/redfish.py
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 | |
get_root()
async
¶
Get the Redfish service root.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Service root document. |
Raises:
| Type | Description |
|---|---|
ResponseError
|
If the body is not a JSON object. |
PiKVMError
|
If PiKVM refuses the request or is unreachable. |
Source code in src/aiopikvm/resources/redfish.py
get_systems()
async
¶
Get the systems collection.
Members is a list of {"@odata.id": "/redfish/v1/Systems/<id>"}
links, one for "0" when the ATX subsystem is enabled and one per
port of an attached PiKVM Switch. The ids are the tail of those paths,
not the members themselves. On a device with ATX disabled and no
switch the collection is empty while Systems/0 still resolves —
the collection lists what can be powered, not what can be read.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Systems collection document. |
Raises:
| Type | Description |
|---|---|
ResponseError
|
If the body is not a JSON object. |
PiKVMError
|
If PiKVM refuses the request or is unreachable. |
Source code in src/aiopikvm/resources/redfish.py
get_system(system_id='0')
async
¶
Get details for a specific system.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
system_id
|
str
|
Redfish id of the system. kvmd accepts the literal
|
'0'
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
System resource document, including |
Raises:
| Type | Description |
|---|---|
ResponseError
|
If the body is not a JSON object. |
APIError
|
If the id is not one kvmd knows (HTTP 400), or the switch has no such port. |
PiKVMError
|
If PiKVM is unreachable. |
Source code in src/aiopikvm/resources/redfish.py
update_system(system_id='0', **attrs)
async
¶
Send a Redfish system update.
kvmd accepts this and does nothing: the handler is a stub that answers HTTP 204, ignores the body and does not even look at system_id. It exists so that BMC tooling which PATCHes a system as part of its normal flow does not fail (pikvm/pikvm#1525). Nothing here changes the device, and a later read returns exactly what it returned before.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
system_id
|
str
|
Redfish id of the system, ignored by kvmd. |
'0'
|
**attrs
|
Any
|
Redfish attributes, ignored by kvmd. |
{}
|
Raises:
| Type | Description |
|---|---|
PiKVMError
|
If PiKVM refuses the request or is unreachable. |
Source code in src/aiopikvm/resources/redfish.py
reset(reset_type='ForceRestart', system_id='0')
async
¶
Send a Redfish ComputerSystem.Reset action.
This is the Redfish spelling of the ATX calls, and it acts on real
hardware. Each ResetType presses one front-panel switch:
"On"and"ForceOn": a short power click, only if the host is off. The two are the same call."ForceOff": the power switch held down — 5.5 s by default, and configurable — only if the host is on."GracefulShutdown": a short power click, only if the host is on — the OS decides what to do with it."ForceRestart": a click on the reset switch, only if the host is on. It does not cut the power."PushPowerButton": a short power click, with no power-state condition. On a switch port it is still dropped while that port is busy with an earlier click.
The default "ForceRestart" gives the host no chance to shut down
cleanly. Everything but "PushPowerButton" is conditional on the
power state kvmd reads from the host's power LED — the same value it
reports as PowerState, on both the "0" and the switch-port
branch — so a "ForceRestart" does nothing at all against a host
kvmd believes to be off, and still answers 204. Where that LED is
miswired or unread, the conditional types are unpredictable; compare
PowerState against reality before relying on them.
Returns nothing — kvmd answers HTTP 204 with an empty body, and the
action is asynchronous besides. Read the outcome from
get_system()'s
PowerState, or from
ATXResource.get_state().
With the ATX subsystem disabled in the kvmd config, system_id="0"
still answers 204 and does nothing at all, so there is no error to
catch — check ATXState.enabled first where that matters. A switch
port is not covered by that: kvmd checks enabled only on the
"0" branch, and a "SwitchPort<N>" reset acts on the port
whatever the ATX plugin is set to.
Unlike
get_system(),
this does not bounds-check a switch port: kvmd validates the form of
the id and then drops a command for a port that does not exist, so
"SwitchPort9" on a four-port switch answers 204 and does nothing.
Read the port back with
get_system()
— that one does answer 400 — if the id came from somewhere you do not
control.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
reset_type
|
ResetType
|
One of
|
'ForceRestart'
|
system_id
|
str
|
Redfish id of the system to act on: |
'0'
|
Raises:
| Type | Description |
|---|---|
APIError
|
If the reset type is not one kvmd accepts, or the id is not of a form it knows (HTTP 400). An id whose form is valid but whose port does not exist is not refused. |
BusyError
|
If a click on the same line of |
PiKVMError
|
If PiKVM is unreachable. |
Source code in src/aiopikvm/resources/redfish.py
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 | |
get_managers()
async
¶
Get the managers collection.
kvmd serves exactly one, BMC, and its path is a literal in the
route table rather than a parameter — which is why
get_manager()
takes no id. The collection is here for a Redfish client that walks
the tree rather than guessing at paths.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Manager collection document. |
Raises:
| Type | Description |
|---|---|
ResponseError
|
If the body is not a JSON object. |
PiKVMError
|
If PiKVM refuses the request or is unreachable. |
Source code in src/aiopikvm/resources/redfish.py
get_manager()
async
¶
Get the BMC manager.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Manager document. |
Raises:
| Type | Description |
|---|---|
ResponseError
|
If the body is not a JSON object. |
PiKVMError
|
If PiKVM refuses the request or is unreachable. |
Source code in src/aiopikvm/resources/redfish.py
get_virtual_media_collection()
async
¶
Get the virtual media collection.
One member, MSD, at a path kvmd hardcodes.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Virtual media collection document. |
Raises:
| Type | Description |
|---|---|
ResponseError
|
If the body is not a JSON object. |
PiKVMError
|
If PiKVM refuses the request or is unreachable. |
Source code in src/aiopikvm/resources/redfish.py
get_virtual_media()
async
¶
Get the mass storage drive as a Redfish virtual media device.
This is the Redfish view of
MSDResource.get_state(),
and a narrower one: Image and ImageName, Inserted,
WriteProtected, and kvmd's own Oem.PiKVM block with
MsdEnabled, MsdOnline, MsdBusy and DriveOptical.
Every drive field is null while the drive is offline — kvmd only
reads them when online is true — so Inserted: null means "not
known", not "no". Oem.PiKVM.MsdOnline is what tells the two apart.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Virtual media document. |
Raises:
| Type | Description |
|---|---|
ResponseError
|
If the body is not a JSON object. |
PiKVMError
|
If PiKVM refuses the request or is unreachable. |
Source code in src/aiopikvm/resources/redfish.py
insert_media(image, *, inserted=True, write_protected=True)
async
¶
Put a stored image into the drive.
The Redfish spelling of selecting an image and connecting the drive. kvmd ejects whatever is connected first, then selects image and — unless inserted is false — connects the drive again.
Despite the Image@Redfish.AllowableValues: ["URI"] the document
advertises, kvmd reads this as a stored image name, the same one
MSDResource.set_params()
takes, and hands it straight to that call. A URL is not refused for
being one: kvmd's validator splits the argument on / and checks
each part as a filename, so a URL passes as a multi-part path. What
an online MSD then makes of it is not recorded — the device this was
captured from had its MSD offline, where a URL dies in the same bare
HTTP 500 as any other name (below). Upload the image first, or use
MSDResource.upload_remote()
for a remote one.
kvmd decides whether to present the drive as an optical one with
name.lower().startswith(".iso") — startswith, not
endswith. No ordinary name begins with a file extension, so this
path always mounts a flash drive, and ubuntu.iso inserted here is
not a CD-ROM. Use
MSDResource.set_params()
with cdrom=True where that matters. Verified against kvmd 4.206.
On a device whose MSD is offline this answers HTTP 500 with an
empty error block, which is a defect rather than a refusal: kvmd
reads state.get("drive", {}).get("connected") before it checks
online, and an offline MSD reports drive as null — the
key is there, so the default never applies and the attribute lookup
raises. Nothing says which subsystem failed, so check
Oem.PiKVM.MsdOnline from
get_virtual_media()
first where the drive may not be set up. Recorded against kvmd 4.206.
Returns nothing: kvmd answers HTTP 204 with an empty body. Read the
result back from
get_virtual_media().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
image
|
str
|
Name of an image already in MSD storage. |
required |
inserted
|
bool
|
Connect the drive to the host afterwards. |
True
|
write_protected
|
bool
|
Present the drive read-only. This is Redfish's
spelling of the inverse of kvmd's |
True
|
Raises:
| Type | Description |
|---|---|
APIError
|
HTTP 400 if the name is not one kvmd's validator accepts, and HTTP 500 — carrying no error name or message — if the MSD is offline, per the defect above. |
BusyError
|
If the drive is busy with another operation (HTTP 409). |
PiKVMError
|
If PiKVM is unreachable. |
Source code in src/aiopikvm/resources/redfish.py
eject_media()
async
¶
Disconnect the drive and clear the selected image.
Both halves, in that order — the same pair
MSDResource.set_connected()
and MSDResource.set_params()
do. An offline drive is where this differs from
insert_media():
the eject reaches kvmd's own MSD plugin and comes back as a proper
HTTP 400 MsdOfflineError rather than a bare 500. What an online
drive with nothing in it answers is not recorded.
Returns nothing: kvmd answers HTTP 204 with an empty body.
Raises:
| Type | Description |
|---|---|
APIError
|
HTTP 400 |
BusyError
|
If the drive is busy with another operation (HTTP 409). |
PiKVMError
|
If PiKVM is unreachable. |
Source code in src/aiopikvm/resources/redfish.py
ResetType = Literal['On', 'ForceOn', 'ForceOff', 'GracefulShutdown', 'ForceRestart', 'PushPowerButton']
¶
Every ResetType kvmd accepts, matched case-sensitively.
The DMTF schema defines more — GracefulRestart, Nmi, PowerCycle —
and kvmd refuses all of them with HTTP 400. Unlike the output, button and
compression names elsewhere in this client, these are not lowercased on the
way in: kvmd looks the name up as given, so "forceoff" is refused as
surely as a type it has never heard of. Key names are matched the same way.
RESET_TYPES is the same list to
check against at runtime.
RESET_TYPES = get_args(ResetType.__value__)
module-attribute
¶
The values of ResetType, in a
tuple, for checking at runtime.
Read off the type rather than written out again, so the two cannot drift
apart. The live list is also in each system document under
Actions["#ComputerSystem.Reset"]["ResetType@Redfish.AllowableValues"],
which is what pins this one to a real device.