python-ipmi 0.6.0__py3-none-any.whl → 0.6.1__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.
- pyipmi/__init__.py +184 -19
- pyipmi/bmc.py +6 -6
- pyipmi/chassis.py +1 -1
- pyipmi/dcmi.py +32 -32
- pyipmi/fields.py +8 -2
- pyipmi/fru.py +8 -8
- pyipmi/hpm.py +24 -16
- pyipmi/interfaces/__init__.py +63 -0
- pyipmi/interfaces/aardvark.py +61 -0
- pyipmi/interfaces/base.py +109 -17
- pyipmi/interfaces/ipmb.py +204 -58
- pyipmi/interfaces/ipmbdev.py +30 -1
- pyipmi/interfaces/ipmidev.py +81 -1
- pyipmi/interfaces/ipmitool.py +110 -10
- pyipmi/interfaces/mock.py +27 -6
- pyipmi/interfaces/openipmblink.py +116 -5
- pyipmi/interfaces/rmcp.py +285 -23
- pyipmi/interfaces/rmcpplus.py +173 -8
- pyipmi/interfaces/router.py +87 -10
- pyipmi/ipmitool.py +17 -11
- pyipmi/mixin.py +2 -2
- pyipmi/msgs/message.py +5 -1
- pyipmi/msgs/sensor.py +19 -0
- pyipmi/picmg.py +32 -32
- pyipmi/sdr.py +17 -13
- pyipmi/sel.py +8 -8
- pyipmi/sensor.py +23 -10
- pyipmi/utils.py +4 -1
- pyipmi/version.py +1 -1
- pyipmi/vita.py +20 -20
- {python_ipmi-0.6.0.dist-info → python_ipmi-0.6.1.dist-info}/METADATA +66 -6
- python_ipmi-0.6.1.dist-info/RECORD +62 -0
- python_ipmi-0.6.0.dist-info/RECORD +0 -62
- {python_ipmi-0.6.0.dist-info → python_ipmi-0.6.1.dist-info}/WHEEL +0 -0
- {python_ipmi-0.6.0.dist-info → python_ipmi-0.6.1.dist-info}/entry_points.txt +0 -0
- {python_ipmi-0.6.0.dist-info → python_ipmi-0.6.1.dist-info}/licenses/AUTHORS +0 -0
- {python_ipmi-0.6.0.dist-info → python_ipmi-0.6.1.dist-info}/licenses/COPYING +0 -0
- {python_ipmi-0.6.0.dist-info → python_ipmi-0.6.1.dist-info}/top_level.txt +0 -0
pyipmi/__init__.py
CHANGED
|
@@ -14,10 +14,38 @@
|
|
|
14
14
|
# License along with this library; if not, write to the Free Software
|
|
15
15
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
16
16
|
|
|
17
|
+
"""A pure Python IPMI library.
|
|
18
|
+
|
|
19
|
+
A connection to an IPMI device is an :class:`Ipmi` object, created by
|
|
20
|
+
:func:`create_connection` for an interface (see
|
|
21
|
+
:func:`pyipmi.interfaces.create_interface`).
|
|
22
|
+
The connection sends the requests to its :class:`Target`, the BMC or a
|
|
23
|
+
controller behind it, which is reached over the :class:`Routing` hops of
|
|
24
|
+
the target. The IPMI commands are the methods of :class:`Ipmi`, which
|
|
25
|
+
inherits them from the command groups of the modules, e.g.
|
|
26
|
+
:mod:`pyipmi.bmc` and :mod:`pyipmi.sdr`.
|
|
27
|
+
|
|
28
|
+
Example:
|
|
29
|
+
Print the device ID of a BMC over RMCP+::
|
|
30
|
+
|
|
31
|
+
import pyipmi
|
|
32
|
+
import pyipmi.interfaces
|
|
33
|
+
|
|
34
|
+
interface = pyipmi.interfaces.create_interface('rmcpplus')
|
|
35
|
+
ipmi = pyipmi.create_connection(interface)
|
|
36
|
+
ipmi.session.set_session_type_rmcp('10.0.0.1', port=623)
|
|
37
|
+
ipmi.session.set_auth_type_user('admin', 'admin')
|
|
38
|
+
ipmi.target = pyipmi.Target(ipmb_address=0x20)
|
|
39
|
+
|
|
40
|
+
with ipmi:
|
|
41
|
+
print(ipmi.get_device_id())
|
|
42
|
+
"""
|
|
43
|
+
|
|
17
44
|
from __future__ import annotations
|
|
18
45
|
|
|
19
46
|
import time
|
|
20
47
|
import ast
|
|
48
|
+
import warnings
|
|
21
49
|
from typing import Any, Literal
|
|
22
50
|
|
|
23
51
|
from . import bmc
|
|
@@ -49,6 +77,19 @@ except ImportError:
|
|
|
49
77
|
|
|
50
78
|
|
|
51
79
|
def create_connection(interface: Any) -> Ipmi:
|
|
80
|
+
"""Create a connection for an interface.
|
|
81
|
+
|
|
82
|
+
The connection gets a new :class:`pyipmi.session.Session` for the
|
|
83
|
+
interface. The target is not set, assign it to :attr:`Ipmi.target`
|
|
84
|
+
before sending requests.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
interface: The interface, e.g. created by
|
|
88
|
+
:func:`pyipmi.interfaces.create_interface`.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
The connection.
|
|
92
|
+
"""
|
|
52
93
|
session = Session()
|
|
53
94
|
session.interface = interface
|
|
54
95
|
return Ipmi(interface=interface, session=session)
|
|
@@ -78,9 +119,17 @@ class NullRequester:
|
|
|
78
119
|
|
|
79
120
|
|
|
80
121
|
class Routing:
|
|
81
|
-
"""
|
|
122
|
+
"""One hop of the path to a target, see :meth:`Target.set_routing`."""
|
|
82
123
|
|
|
83
124
|
def __init__(self, rq_sa: int, rs_sa: int, channel: int | None) -> None:
|
|
125
|
+
"""Initialize the hop.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
rq_sa: The requester slave address.
|
|
129
|
+
rs_sa: The responder slave address.
|
|
130
|
+
channel: The channel of the bridge to the next hop, None for
|
|
131
|
+
the last hop.
|
|
132
|
+
"""
|
|
84
133
|
self.rq_sa = rq_sa
|
|
85
134
|
self.rs_sa = rs_sa
|
|
86
135
|
self.channel = channel
|
|
@@ -99,11 +148,14 @@ class Target:
|
|
|
99
148
|
|
|
100
149
|
def __init__(self, ipmb_address: int | None = None,
|
|
101
150
|
routing: str | list[tuple] | None = None) -> None:
|
|
102
|
-
"""
|
|
151
|
+
"""Initialize the target.
|
|
103
152
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
153
|
+
Args:
|
|
154
|
+
ipmb_address: The IPMB address of the target, e.g. 0x20 for
|
|
155
|
+
the BMC.
|
|
156
|
+
routing: The path over which the target is reachable, used to
|
|
157
|
+
build the bridged Send Message requests, see
|
|
158
|
+
:meth:`set_routing`.
|
|
107
159
|
"""
|
|
108
160
|
if ipmb_address:
|
|
109
161
|
self.ipmb_address = ipmb_address
|
|
@@ -112,6 +164,10 @@ class Target:
|
|
|
112
164
|
self.set_routing(routing)
|
|
113
165
|
|
|
114
166
|
def set_routing_information(self, routing: str | list[tuple]) -> None:
|
|
167
|
+
"""Set the path over which a target is reachable.
|
|
168
|
+
|
|
169
|
+
An alias of :meth:`set_routing`.
|
|
170
|
+
"""
|
|
115
171
|
self.set_routing(routing)
|
|
116
172
|
|
|
117
173
|
def set_routing(self, routing: str | list[tuple]) -> None:
|
|
@@ -171,10 +227,32 @@ class Target:
|
|
|
171
227
|
class Ipmi(bmc.Bmc, chassis.Chassis, dcmi.Dcmi, fru.Fru, picmg.Picmg, hpm.Hpm,
|
|
172
228
|
sdr.Sdr, sensor.Sensor, event.Event, sel.Sel, lan.Lan,
|
|
173
229
|
messaging.Messaging, vita.Vita):
|
|
230
|
+
"""A connection to an IPMI device.
|
|
231
|
+
|
|
232
|
+
The IPMI commands are the methods of this class, which it inherits
|
|
233
|
+
from the command groups, e.g. :meth:`get_device_id` from the one of
|
|
234
|
+
:mod:`pyipmi.bmc`. The requests are sent over the interface to
|
|
235
|
+
the target.
|
|
236
|
+
|
|
237
|
+
The connection is a context manager, which opens the interface and
|
|
238
|
+
establishes the session on entry and closes them on exit. Set up the
|
|
239
|
+
session before, see the example of :mod:`pyipmi`.
|
|
240
|
+
"""
|
|
174
241
|
|
|
175
242
|
def __init__(self, interface: Any = None, target: Target | None = None,
|
|
176
243
|
session: Session | None = None,
|
|
177
244
|
requester: Any = None) -> None:
|
|
245
|
+
"""Initialize the connection.
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
interface: The interface the requests are sent over.
|
|
249
|
+
target: The target of the requests.
|
|
250
|
+
session: The session, a new one if not given. The interface
|
|
251
|
+
of the session is set to ``interface``.
|
|
252
|
+
requester: The requester of the requests, needed by interfaces
|
|
253
|
+
that send the requests on the IPMB, a
|
|
254
|
+
:class:`NullRequester` if not given.
|
|
255
|
+
"""
|
|
178
256
|
self._interface = interface
|
|
179
257
|
|
|
180
258
|
# we need a session, set if not passed
|
|
@@ -200,30 +278,83 @@ class Ipmi(bmc.Bmc, chassis.Chassis, dcmi.Dcmi, fru.Fru, picmg.Picmg, hpm.Hpm,
|
|
|
200
278
|
return False
|
|
201
279
|
|
|
202
280
|
def open(self) -> None:
|
|
281
|
+
"""Open the interface and establish the session."""
|
|
203
282
|
self.interface.open()
|
|
204
283
|
if self.session is not None:
|
|
205
284
|
self.session.establish()
|
|
206
285
|
|
|
207
286
|
def close(self) -> None:
|
|
287
|
+
"""Close the session and the interface."""
|
|
208
288
|
if self.session is not None:
|
|
209
289
|
self.session.close()
|
|
210
290
|
self.interface.close()
|
|
211
291
|
|
|
292
|
+
def is_target_accessible(self) -> bool:
|
|
293
|
+
"""Check if the target answers.
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
True if the target answers. Depending on the interface, False
|
|
297
|
+
is returned or an exception is raised if it does not.
|
|
298
|
+
"""
|
|
299
|
+
return self.interface.is_target_accessible(self.target)
|
|
300
|
+
|
|
212
301
|
def is_ipmc_accessible(self) -> bool:
|
|
213
|
-
|
|
302
|
+
"""Deprecated, the old name of :meth:`is_target_accessible`."""
|
|
303
|
+
warnings.warn('is_ipmc_accessible is deprecated, use '
|
|
304
|
+
'is_target_accessible', DeprecationWarning,
|
|
305
|
+
stacklevel=2)
|
|
306
|
+
return self.is_target_accessible()
|
|
214
307
|
|
|
215
|
-
def
|
|
216
|
-
|
|
308
|
+
def wait_until_target_is_accessible(self, timeout: float,
|
|
309
|
+
interval: float = 0.25) -> None:
|
|
310
|
+
"""Wait until the target is accessible.
|
|
311
|
+
|
|
312
|
+
Args:
|
|
313
|
+
timeout: The time to wait in seconds.
|
|
314
|
+
interval: The time between the checks in seconds.
|
|
315
|
+
|
|
316
|
+
Raises:
|
|
317
|
+
IpmiTimeoutError: The target is not accessible after the
|
|
318
|
+
timeout, if the interface raises it, see
|
|
319
|
+
:meth:`is_target_accessible`.
|
|
320
|
+
"""
|
|
217
321
|
start_time = time.time()
|
|
218
322
|
while time.time() < start_time + (timeout):
|
|
219
323
|
try:
|
|
220
|
-
self.
|
|
324
|
+
if self.is_target_accessible():
|
|
325
|
+
return
|
|
221
326
|
except IpmiTimeoutError:
|
|
222
|
-
|
|
327
|
+
pass
|
|
328
|
+
time.sleep(interval)
|
|
329
|
+
|
|
330
|
+
self.is_target_accessible()
|
|
223
331
|
|
|
224
|
-
|
|
332
|
+
def wait_until_ipmb_is_accessible(self, timeout: float,
|
|
333
|
+
interval: float = 0.25) -> None:
|
|
334
|
+
"""Deprecated, use :meth:`wait_until_target_is_accessible`."""
|
|
335
|
+
warnings.warn('wait_until_ipmb_is_accessible is deprecated, use '
|
|
336
|
+
'wait_until_target_is_accessible', DeprecationWarning,
|
|
337
|
+
stacklevel=2)
|
|
338
|
+
self.wait_until_target_is_accessible(timeout, interval)
|
|
225
339
|
|
|
226
340
|
def send_message(self, req: Message, retry: int = 3) -> Message:
|
|
341
|
+
"""Send a request to the target and return the response.
|
|
342
|
+
|
|
343
|
+
The request is sent again if the target is busy. The completion
|
|
344
|
+
code of the response is not checked.
|
|
345
|
+
|
|
346
|
+
Args:
|
|
347
|
+
req: The request message.
|
|
348
|
+
retry: The number of tries.
|
|
349
|
+
|
|
350
|
+
Returns:
|
|
351
|
+
The response message.
|
|
352
|
+
|
|
353
|
+
Raises:
|
|
354
|
+
RetryError: The target is still busy after ``retry`` tries.
|
|
355
|
+
CompletionCodeError: The interface failed with another
|
|
356
|
+
completion code, e.g. of a bridged Send Message request.
|
|
357
|
+
"""
|
|
227
358
|
req.target = self.target
|
|
228
359
|
req.requester = self.requester
|
|
229
360
|
rsp = None
|
|
@@ -236,13 +367,28 @@ class Ipmi(bmc.Bmc, chassis.Chassis, dcmi.Dcmi, fru.Fru, picmg.Picmg, hpm.Hpm,
|
|
|
236
367
|
except CompletionCodeError as e:
|
|
237
368
|
if e.cc == msgs.constants.CC_NODE_BUSY:
|
|
238
369
|
continue
|
|
370
|
+
raise
|
|
239
371
|
else:
|
|
240
372
|
raise RetryError()
|
|
241
373
|
|
|
242
374
|
return rsp
|
|
243
375
|
|
|
244
|
-
def
|
|
245
|
-
|
|
376
|
+
def send_message_by_name(self, name: str, *args: Any,
|
|
377
|
+
**kwargs: Any) -> Message:
|
|
378
|
+
"""Send a request by its name and return the response.
|
|
379
|
+
|
|
380
|
+
Args:
|
|
381
|
+
name: The name of the request, e.g. ``'GetDeviceId'``.
|
|
382
|
+
*args: Not used.
|
|
383
|
+
**kwargs: The fields of the request, set as attributes.
|
|
384
|
+
|
|
385
|
+
Returns:
|
|
386
|
+
The response message.
|
|
387
|
+
|
|
388
|
+
Raises:
|
|
389
|
+
CompletionCodeError: The completion code of the response is
|
|
390
|
+
not successful.
|
|
391
|
+
"""
|
|
246
392
|
req = create_request_by_name(name)
|
|
247
393
|
|
|
248
394
|
for key, value in kwargs.items():
|
|
@@ -252,20 +398,37 @@ class Ipmi(bmc.Bmc, chassis.Chassis, dcmi.Dcmi, fru.Fru, picmg.Picmg, hpm.Hpm,
|
|
|
252
398
|
check_rsp_completion_code(rsp)
|
|
253
399
|
return rsp
|
|
254
400
|
|
|
255
|
-
def
|
|
256
|
-
|
|
401
|
+
def send_message_with_name(self, name: str, *args: Any,
|
|
402
|
+
**kwargs: Any) -> Message:
|
|
403
|
+
"""Deprecated, the old name of :meth:`send_message_by_name`."""
|
|
404
|
+
warnings.warn('send_message_with_name is deprecated, use '
|
|
405
|
+
'send_message_by_name', DeprecationWarning,
|
|
406
|
+
stacklevel=2)
|
|
407
|
+
return self.send_message_by_name(name, *args, **kwargs)
|
|
257
408
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
raw_bytes: the raw message as bytestring
|
|
409
|
+
def send_raw(self, lun: int, netfn: int, raw_bytes: bytes) -> bytes:
|
|
410
|
+
"""Send a raw request to the target and return the raw response.
|
|
261
411
|
|
|
262
|
-
|
|
412
|
+
Args:
|
|
413
|
+
lun: The logical unit number.
|
|
414
|
+
netfn: The network function.
|
|
415
|
+
raw_bytes: The request, starting with the command ID.
|
|
416
|
+
|
|
417
|
+
Returns:
|
|
418
|
+
The response, starting with the completion code.
|
|
263
419
|
"""
|
|
264
420
|
return self.interface.send_and_receive_raw(self.target, lun, netfn,
|
|
265
421
|
raw_bytes)
|
|
266
422
|
|
|
423
|
+
def raw_command(self, lun: int, netfn: int, raw_bytes: bytes) -> bytes:
|
|
424
|
+
"""Deprecated, the old name of :meth:`send_raw`."""
|
|
425
|
+
warnings.warn('raw_command is deprecated, use send_raw',
|
|
426
|
+
DeprecationWarning, stacklevel=2)
|
|
427
|
+
return self.send_raw(lun, netfn, raw_bytes)
|
|
428
|
+
|
|
267
429
|
@property
|
|
268
430
|
def interface(self) -> Any:
|
|
431
|
+
"""The interface the requests are sent over."""
|
|
269
432
|
try:
|
|
270
433
|
return self._interface
|
|
271
434
|
except AttributeError:
|
|
@@ -277,6 +440,7 @@ class Ipmi(bmc.Bmc, chassis.Chassis, dcmi.Dcmi, fru.Fru, picmg.Picmg, hpm.Hpm,
|
|
|
277
440
|
|
|
278
441
|
@property
|
|
279
442
|
def session(self) -> Session:
|
|
443
|
+
"""The session of the connection."""
|
|
280
444
|
try:
|
|
281
445
|
return self._session
|
|
282
446
|
except AttributeError:
|
|
@@ -288,6 +452,7 @@ class Ipmi(bmc.Bmc, chassis.Chassis, dcmi.Dcmi, fru.Fru, picmg.Picmg, hpm.Hpm,
|
|
|
288
452
|
|
|
289
453
|
@property
|
|
290
454
|
def target(self) -> Target | None:
|
|
455
|
+
"""The target of the requests."""
|
|
291
456
|
try:
|
|
292
457
|
return self._target
|
|
293
458
|
except AttributeError:
|
pyipmi/bmc.py
CHANGED
|
@@ -48,7 +48,7 @@ class Bmc(IpmiMixin):
|
|
|
48
48
|
The device ID, firmware version, IPMI version and supported
|
|
49
49
|
functions of the controller.
|
|
50
50
|
"""
|
|
51
|
-
return DeviceId(self.
|
|
51
|
+
return DeviceId(self.send_message_by_name('GetDeviceId'))
|
|
52
52
|
|
|
53
53
|
def get_device_guid(self) -> DeviceGuid:
|
|
54
54
|
"""Get the GUID of the controller.
|
|
@@ -56,15 +56,15 @@ class Bmc(IpmiMixin):
|
|
|
56
56
|
Returns:
|
|
57
57
|
The device GUID.
|
|
58
58
|
"""
|
|
59
|
-
return DeviceGuid(self.
|
|
59
|
+
return DeviceGuid(self.send_message_by_name('GetDeviceGuid'))
|
|
60
60
|
|
|
61
61
|
def cold_reset(self) -> None:
|
|
62
62
|
"""Cold reset the controller, it is reinitialized."""
|
|
63
|
-
self.
|
|
63
|
+
self.send_message_by_name('ColdReset')
|
|
64
64
|
|
|
65
65
|
def warm_reset(self) -> None:
|
|
66
66
|
"""Warm reset the controller, its state is kept."""
|
|
67
|
-
self.
|
|
67
|
+
self.send_message_by_name('WarmReset')
|
|
68
68
|
|
|
69
69
|
def i2c_write_read(self, bus_type: int, bus_id: int, channel: int,
|
|
70
70
|
address: int, count: int,
|
|
@@ -169,7 +169,7 @@ class Bmc(IpmiMixin):
|
|
|
169
169
|
Returns:
|
|
170
170
|
The watchdog timer, ``dont_stop`` is not set.
|
|
171
171
|
"""
|
|
172
|
-
return Watchdog(self.
|
|
172
|
+
return Watchdog(self.send_message_by_name('GetWatchdogTimer'))
|
|
173
173
|
|
|
174
174
|
def reset_watchdog_timer(self) -> None:
|
|
175
175
|
"""Start or restart the watchdog timer with its initial countdown.
|
|
@@ -177,7 +177,7 @@ class Bmc(IpmiMixin):
|
|
|
177
177
|
Raises:
|
|
178
178
|
CompletionCodeError: The timer was not set before (0x80).
|
|
179
179
|
"""
|
|
180
|
-
self.
|
|
180
|
+
self.send_message_by_name('ResetWatchdogTimer')
|
|
181
181
|
|
|
182
182
|
|
|
183
183
|
class Watchdog(State):
|
pyipmi/chassis.py
CHANGED
|
@@ -207,7 +207,7 @@ class Chassis(IpmiMixin):
|
|
|
207
207
|
Returns:
|
|
208
208
|
The chassis status.
|
|
209
209
|
"""
|
|
210
|
-
return ChassisStatus(self.
|
|
210
|
+
return ChassisStatus(self.send_message_by_name('GetChassisStatus'))
|
|
211
211
|
|
|
212
212
|
def chassis_control(self, option: int) -> None:
|
|
213
213
|
"""Control the chassis power.
|
pyipmi/dcmi.py
CHANGED
|
@@ -92,8 +92,8 @@ class Dcmi(IpmiMixin):
|
|
|
92
92
|
(bits ``major`` and ``minor``, the DCMI version),
|
|
93
93
|
``parameter_revision`` and ``parameter_data``.
|
|
94
94
|
"""
|
|
95
|
-
rsp = self.
|
|
96
|
-
|
|
95
|
+
rsp = self.send_message_by_name('GetDcmiCapabilities',
|
|
96
|
+
parameter_selector=selector)
|
|
97
97
|
return rsp
|
|
98
98
|
|
|
99
99
|
def get_power_reading(self, mode: int, attributes: int = 0) -> Message:
|
|
@@ -113,8 +113,8 @@ class Dcmi(IpmiMixin):
|
|
|
113
113
|
``reading_state`` (bit 6 is set if the power measurement is
|
|
114
114
|
active).
|
|
115
115
|
"""
|
|
116
|
-
rsp = self.
|
|
117
|
-
|
|
116
|
+
rsp = self.send_message_by_name('GetPowerReading',
|
|
117
|
+
mode=mode, attributes=attributes)
|
|
118
118
|
return rsp
|
|
119
119
|
|
|
120
120
|
def _get_all_instances(self, name: str, entity_id: int,
|
|
@@ -131,11 +131,11 @@ class Dcmi(IpmiMixin):
|
|
|
131
131
|
items: list[Any] = []
|
|
132
132
|
start = 0
|
|
133
133
|
while True:
|
|
134
|
-
rsp = self.
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
134
|
+
rsp = self.send_message_by_name(name,
|
|
135
|
+
sensor_type=1,
|
|
136
|
+
entity_id=entity_id,
|
|
137
|
+
entity_instance=0,
|
|
138
|
+
entity_instance_start=start)
|
|
139
139
|
received = decode(rsp)
|
|
140
140
|
new = [(k, i) for (k, i) in received if k not in keys]
|
|
141
141
|
for (k, i) in new:
|
|
@@ -204,7 +204,7 @@ class Dcmi(IpmiMixin):
|
|
|
204
204
|
CompletionCodeError: The BMC rejected the request, e.g. if no
|
|
205
205
|
power limit is set (0x80).
|
|
206
206
|
"""
|
|
207
|
-
return self.
|
|
207
|
+
return self.send_message_by_name('GetPowerLimit')
|
|
208
208
|
|
|
209
209
|
def set_power_limit(self, power_limit: int, correction_time_limit: int,
|
|
210
210
|
statistics_sampling_period: int,
|
|
@@ -223,7 +223,7 @@ class Dcmi(IpmiMixin):
|
|
|
223
223
|
exception_actions: The action if the limit is exceeded, one of
|
|
224
224
|
the ``POWER_LIMIT_EXCEPTION_*`` constants.
|
|
225
225
|
"""
|
|
226
|
-
self.
|
|
226
|
+
self.send_message_by_name(
|
|
227
227
|
'SetPowerLimit',
|
|
228
228
|
exception_actions=exception_actions,
|
|
229
229
|
power_limit=power_limit,
|
|
@@ -232,13 +232,13 @@ class Dcmi(IpmiMixin):
|
|
|
232
232
|
|
|
233
233
|
def activate_power_limit(self) -> None:
|
|
234
234
|
"""Activate the power limit."""
|
|
235
|
-
self.
|
|
236
|
-
|
|
235
|
+
self.send_message_by_name('ActivateDeactivatePowerLimit',
|
|
236
|
+
activation=1)
|
|
237
237
|
|
|
238
238
|
def deactivate_power_limit(self) -> None:
|
|
239
239
|
"""Deactivate the power limit."""
|
|
240
|
-
self.
|
|
241
|
-
|
|
240
|
+
self.send_message_by_name('ActivateDeactivatePowerLimit',
|
|
241
|
+
activation=0)
|
|
242
242
|
|
|
243
243
|
def get_thermal_limit(self, entity_id: int,
|
|
244
244
|
entity_instance: int) -> Message:
|
|
@@ -254,9 +254,9 @@ class Dcmi(IpmiMixin):
|
|
|
254
254
|
``temperature_limit`` in degree Celsius and ``exception_time``
|
|
255
255
|
in seconds.
|
|
256
256
|
"""
|
|
257
|
-
return self.
|
|
258
|
-
|
|
259
|
-
|
|
257
|
+
return self.send_message_by_name('GetThermalLimit',
|
|
258
|
+
entity_id=entity_id,
|
|
259
|
+
entity_instance=entity_instance)
|
|
260
260
|
|
|
261
261
|
def set_thermal_limit(self, entity_id: int, entity_instance: int,
|
|
262
262
|
temperature_limit: int, exception_time: int,
|
|
@@ -301,9 +301,9 @@ class Dcmi(IpmiMixin):
|
|
|
301
301
|
(bits ``major`` and ``minor``), ``parameter_revision`` and
|
|
302
302
|
``parameter_data``.
|
|
303
303
|
"""
|
|
304
|
-
return self.
|
|
305
|
-
|
|
306
|
-
|
|
304
|
+
return self.send_message_by_name('GetDcmiConfigurationParameters',
|
|
305
|
+
parameter_selector=selector,
|
|
306
|
+
set_selector=set_selector)
|
|
307
307
|
|
|
308
308
|
def set_dcmi_configuration_parameters(self, selector: int, data: bytes,
|
|
309
309
|
set_selector: int = 0) -> None:
|
|
@@ -314,21 +314,21 @@ class Dcmi(IpmiMixin):
|
|
|
314
314
|
data: The parameter data.
|
|
315
315
|
set_selector: The set selector of the parameter.
|
|
316
316
|
"""
|
|
317
|
-
self.
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
317
|
+
self.send_message_by_name('SetDcmiConfigurationParameters',
|
|
318
|
+
parameter_selector=selector,
|
|
319
|
+
set_selector=set_selector,
|
|
320
|
+
parameter_data=data)
|
|
321
321
|
|
|
322
322
|
def _get_dcmi_string(self, name: str, length_request_size: int) -> bytes:
|
|
323
323
|
# the first request is only used to get the total length
|
|
324
|
-
rsp = self.
|
|
325
|
-
|
|
324
|
+
rsp = self.send_message_by_name(name, offset=0,
|
|
325
|
+
number_of_bytes=length_request_size)
|
|
326
326
|
total_length = rsp.total_length
|
|
327
327
|
data = b''
|
|
328
328
|
while len(data) < total_length:
|
|
329
329
|
count = min(MAX_STRING_CHUNK_SIZE, total_length - len(data))
|
|
330
|
-
rsp = self.
|
|
331
|
-
|
|
330
|
+
rsp = self.send_message_by_name(name, offset=len(data),
|
|
331
|
+
number_of_bytes=count)
|
|
332
332
|
chunk = rsp.data.tobytes()
|
|
333
333
|
if not chunk:
|
|
334
334
|
break
|
|
@@ -338,9 +338,9 @@ class Dcmi(IpmiMixin):
|
|
|
338
338
|
def _set_dcmi_string(self, name: str, data: bytes) -> None:
|
|
339
339
|
for offset in range(0, len(data), MAX_STRING_CHUNK_SIZE):
|
|
340
340
|
chunk = data[offset:offset + MAX_STRING_CHUNK_SIZE]
|
|
341
|
-
self.
|
|
342
|
-
|
|
343
|
-
|
|
341
|
+
self.send_message_by_name(name, offset=offset,
|
|
342
|
+
number_of_bytes=len(chunk),
|
|
343
|
+
data=chunk)
|
|
344
344
|
|
|
345
345
|
def get_asset_tag(self) -> str:
|
|
346
346
|
"""Return the asset tag of the system.
|
pyipmi/fields.py
CHANGED
|
@@ -53,7 +53,13 @@ class VersionField:
|
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
def _unpack6bitascii(data: Sequence[int]) -> str:
|
|
56
|
-
"""Unpack the 6bit ascii encoded string.
|
|
56
|
+
"""Unpack the 6bit ascii encoded string.
|
|
57
|
+
|
|
58
|
+
The characters are packed in groups of 3 bytes, the last group can be
|
|
59
|
+
shorter, e.g. 5 characters take 4 bytes.
|
|
60
|
+
"""
|
|
61
|
+
count = len(data) * 8 // 6
|
|
62
|
+
data = list(data) + [0] * (-len(data) % 3)
|
|
57
63
|
string = ''
|
|
58
64
|
for i in range(0, len(data), 3):
|
|
59
65
|
d = data[i:i+3]
|
|
@@ -61,7 +67,7 @@ def _unpack6bitascii(data: Sequence[int]) -> str:
|
|
|
61
67
|
string += chr(0x20 + (((d[0] & 0xc0) >> 6) | ((d[1] & 0xf) << 2)))
|
|
62
68
|
string += chr(0x20 + (((d[1] & 0xf0) >> 4) | ((d[2] & 0x3) << 4)))
|
|
63
69
|
string += chr(0x20 + ((d[2] & 0xfc) >> 2))
|
|
64
|
-
return string
|
|
70
|
+
return string[:count]
|
|
65
71
|
|
|
66
72
|
|
|
67
73
|
class TypeLengthString:
|
pyipmi/fru.py
CHANGED
|
@@ -82,8 +82,8 @@ class Fru(IpmiMixin):
|
|
|
82
82
|
Returns:
|
|
83
83
|
The size of the FRU inventory area in bytes.
|
|
84
84
|
"""
|
|
85
|
-
rsp = self.
|
|
86
|
-
|
|
85
|
+
rsp = self.send_message_by_name('GetFruInventoryAreaInfo',
|
|
86
|
+
fru_id=fru_id)
|
|
87
87
|
return rsp.area_size
|
|
88
88
|
|
|
89
89
|
def write_fru_data(self, data: bytes, offset: int = 0,
|
|
@@ -101,10 +101,10 @@ class Fru(IpmiMixin):
|
|
|
101
101
|
Exception: The device wrote fewer bytes than were sent.
|
|
102
102
|
"""
|
|
103
103
|
for chunk in chunks(data, self.write_length):
|
|
104
|
-
write_rsp = self.
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
104
|
+
write_rsp = self.send_message_by_name('WriteFruData',
|
|
105
|
+
fru_id=fru_id,
|
|
106
|
+
offset=offset,
|
|
107
|
+
data=chunk)
|
|
108
108
|
|
|
109
109
|
# check if device wrote the same number of bytes sent
|
|
110
110
|
if write_rsp.count_written != len(chunk):
|
|
@@ -147,8 +147,8 @@ class Fru(IpmiMixin):
|
|
|
147
147
|
req_size = min(read_length.length, area_size - off)
|
|
148
148
|
|
|
149
149
|
try:
|
|
150
|
-
rsp = self.
|
|
151
|
-
|
|
150
|
+
rsp = self.send_message_by_name('ReadFruData', fru_id=fru_id,
|
|
151
|
+
offset=off, count=req_size)
|
|
152
152
|
except CompletionCodeError as ex:
|
|
153
153
|
if ex.cc in (constants.CC_CANT_RET_NUM_REQ_BYTES,
|
|
154
154
|
constants.CC_REQ_DATA_FIELD_EXCEED,
|