python-ipmi 0.5.8__py3-none-any.whl → 0.6.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyipmi/__init__.py +90 -72
- pyipmi/bmc.py +192 -23
- pyipmi/chassis.py +204 -60
- pyipmi/constants.py +35 -0
- pyipmi/dcmi.py +351 -14
- pyipmi/errors.py +34 -22
- pyipmi/event.py +62 -3
- pyipmi/fields.py +33 -28
- pyipmi/fru.py +1099 -114
- pyipmi/helper.py +50 -16
- pyipmi/hpm.py +908 -187
- pyipmi/interfaces/__init__.py +14 -4
- pyipmi/interfaces/aardvark.py +119 -177
- pyipmi/interfaces/base.py +87 -0
- pyipmi/interfaces/ipmb.py +248 -42
- pyipmi/interfaces/ipmbdev.py +46 -162
- pyipmi/interfaces/ipmidev.py +244 -0
- pyipmi/interfaces/ipmitool.py +157 -89
- pyipmi/interfaces/mock.py +14 -13
- pyipmi/interfaces/openipmblink.py +312 -0
- pyipmi/interfaces/rmcp.py +219 -195
- pyipmi/interfaces/rmcpplus.py +570 -0
- pyipmi/interfaces/router.py +242 -0
- pyipmi/ipmitool.py +1113 -428
- pyipmi/lan.py +251 -76
- pyipmi/logger.py +4 -4
- pyipmi/messaging.py +207 -18
- pyipmi/mixin.py +52 -0
- pyipmi/msgs/__init__.py +0 -1
- pyipmi/msgs/constants.py +98 -0
- pyipmi/msgs/dcmi.py +177 -19
- pyipmi/msgs/device_messaging.py +34 -0
- pyipmi/msgs/fru.py +3 -1
- pyipmi/msgs/hpm.py +7 -7
- pyipmi/msgs/message.py +119 -83
- pyipmi/msgs/picmg.py +7 -5
- pyipmi/msgs/registry.py +18 -9
- pyipmi/msgs/sel.py +4 -4
- pyipmi/msgs/vita.py +0 -1
- pyipmi/picmg.py +490 -52
- pyipmi/sdr.py +583 -94
- pyipmi/sel.py +185 -20
- pyipmi/sensor.py +163 -38
- pyipmi/session.py +58 -34
- pyipmi/state.py +13 -5
- pyipmi/utils.py +60 -55
- pyipmi/version.py +1 -1
- pyipmi/vita.py +281 -0
- python_ipmi-0.6.0.dist-info/METADATA +312 -0
- python_ipmi-0.6.0.dist-info/RECORD +62 -0
- {python_ipmi-0.5.8.dist-info → python_ipmi-0.6.0.dist-info}/WHEEL +1 -1
- pyipmi/emulation.py +0 -395
- python_ipmi-0.5.8.dist-info/METADATA +0 -207
- python_ipmi-0.5.8.dist-info/RECORD +0 -56
- {python_ipmi-0.5.8.dist-info → python_ipmi-0.6.0.dist-info}/entry_points.txt +0 -0
- {python_ipmi-0.5.8.dist-info → python_ipmi-0.6.0.dist-info}/licenses/AUTHORS +0 -0
- {python_ipmi-0.5.8.dist-info → python_ipmi-0.6.0.dist-info}/licenses/COPYING +0 -0
- {python_ipmi-0.5.8.dist-info → python_ipmi-0.6.0.dist-info}/top_level.txt +0 -0
pyipmi/__init__.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
1
|
# Copyright (c) 2014 Kontron Europe GmbH
|
|
3
2
|
#
|
|
4
3
|
# This library is free software; you can redistribute it and/or
|
|
@@ -15,10 +14,11 @@
|
|
|
15
14
|
# License along with this library; if not, write to the Free Software
|
|
16
15
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
17
16
|
|
|
18
|
-
from __future__ import
|
|
17
|
+
from __future__ import annotations
|
|
19
18
|
|
|
20
19
|
import time
|
|
21
20
|
import ast
|
|
21
|
+
from typing import Any, Literal
|
|
22
22
|
|
|
23
23
|
from . import bmc
|
|
24
24
|
from . import chassis
|
|
@@ -27,42 +27,45 @@ from . import event
|
|
|
27
27
|
from . import fru
|
|
28
28
|
from . import hpm
|
|
29
29
|
from . import lan
|
|
30
|
+
from . import logger # noqa: F401 - installs the NullHandler
|
|
30
31
|
from . import messaging
|
|
31
32
|
from . import picmg
|
|
32
33
|
from . import sdr
|
|
33
34
|
from . import sel
|
|
34
35
|
from . import sensor
|
|
36
|
+
from . import vita
|
|
35
37
|
from . import msgs
|
|
36
38
|
|
|
37
39
|
from .errors import IpmiTimeoutError, CompletionCodeError, RetryError
|
|
40
|
+
from .msgs import Message
|
|
38
41
|
from .msgs.registry import create_request_by_name
|
|
39
42
|
from .session import Session
|
|
40
43
|
from .utils import check_rsp_completion_code, is_string
|
|
41
44
|
|
|
42
45
|
try:
|
|
43
|
-
from version import __version__
|
|
46
|
+
from .version import __version__
|
|
44
47
|
except ImportError:
|
|
45
48
|
__version__ = 'dev'
|
|
46
49
|
|
|
47
50
|
|
|
48
|
-
def create_connection(interface):
|
|
51
|
+
def create_connection(interface: Any) -> Ipmi:
|
|
49
52
|
session = Session()
|
|
50
53
|
session.interface = interface
|
|
51
54
|
return Ipmi(interface=interface, session=session)
|
|
52
55
|
|
|
53
56
|
|
|
54
|
-
class Requester
|
|
57
|
+
class Requester:
|
|
55
58
|
"""The Requester class.
|
|
56
59
|
|
|
57
60
|
This represents an IPMI device which initiates a request/response
|
|
58
61
|
message exchange.
|
|
59
62
|
"""
|
|
60
63
|
|
|
61
|
-
def __init__(self, ipmb_address):
|
|
64
|
+
def __init__(self, ipmb_address: int) -> None:
|
|
62
65
|
self.ipmb_address = ipmb_address
|
|
63
66
|
|
|
64
67
|
|
|
65
|
-
class NullRequester
|
|
68
|
+
class NullRequester:
|
|
66
69
|
"""The NullRequester class.
|
|
67
70
|
|
|
68
71
|
This requester is used for interfaces which doesn't require a valid
|
|
@@ -70,31 +73,32 @@ class NullRequester(object):
|
|
|
70
73
|
"""
|
|
71
74
|
|
|
72
75
|
@property
|
|
73
|
-
def ipmb_address(self):
|
|
76
|
+
def ipmb_address(self) -> int:
|
|
74
77
|
raise AssertionError('NullRequester does not provide an IPMB address')
|
|
75
78
|
|
|
76
79
|
|
|
77
|
-
class Routing
|
|
80
|
+
class Routing:
|
|
78
81
|
"""The Target class represents an IPMI target."""
|
|
79
82
|
|
|
80
|
-
def __init__(self, rq_sa, rs_sa, channel):
|
|
83
|
+
def __init__(self, rq_sa: int, rs_sa: int, channel: int | None) -> None:
|
|
81
84
|
self.rq_sa = rq_sa
|
|
82
85
|
self.rs_sa = rs_sa
|
|
83
86
|
self.channel = channel
|
|
84
87
|
|
|
85
|
-
def __str__(self):
|
|
88
|
+
def __str__(self) -> str:
|
|
86
89
|
s = 'Routing: Rq: %s Rs: %s Ch: %s' \
|
|
87
90
|
% (self.rq_sa, self.rs_sa, self.channel)
|
|
88
91
|
return s
|
|
89
92
|
|
|
90
93
|
|
|
91
|
-
class Target
|
|
94
|
+
class Target:
|
|
92
95
|
"""The Target class represents an IPMI target."""
|
|
93
96
|
|
|
94
|
-
routing = None
|
|
95
|
-
ipmb_address = None
|
|
97
|
+
routing: list[Routing] | None = None
|
|
98
|
+
ipmb_address: int | None = None
|
|
96
99
|
|
|
97
|
-
def __init__(self, ipmb_address
|
|
100
|
+
def __init__(self, ipmb_address: int | None = None,
|
|
101
|
+
routing: str | list[tuple] | None = None) -> None:
|
|
98
102
|
"""Initializer for the Target class.
|
|
99
103
|
|
|
100
104
|
`ipmb_address` is the IPMB target address
|
|
@@ -107,49 +111,57 @@ class Target(object):
|
|
|
107
111
|
if routing:
|
|
108
112
|
self.set_routing(routing)
|
|
109
113
|
|
|
110
|
-
def set_routing_information(self, routing):
|
|
114
|
+
def set_routing_information(self, routing: str | list[tuple]) -> None:
|
|
111
115
|
self.set_routing(routing)
|
|
112
116
|
|
|
113
|
-
def set_routing(self, routing):
|
|
117
|
+
def set_routing(self, routing: str | list[tuple]) -> None:
|
|
114
118
|
"""Set the path over which a target is reachable.
|
|
115
119
|
|
|
116
|
-
|
|
117
|
-
|
|
120
|
+
Each hop of the path is a tuple ``(rq_sa, rs_sa, channel)``: the
|
|
121
|
+
requester address, the responder address and the channel of the
|
|
122
|
+
bridge to the next hop. The channel of the last hop is None.
|
|
118
123
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
124
|
+
Args:
|
|
125
|
+
routing: The list of hops, or its string representation as
|
|
126
|
+
given on the command line.
|
|
122
127
|
|
|
123
|
-
Example #
|
|
124
|
-
|
|
125
|
-
routing = [(0x81,0x20,0),(0x20,0x82,7),(0x20,0x72,None)]
|
|
128
|
+
Example #1, access to an ATCA blade in a chassis (slave 0x81,
|
|
129
|
+
target 0x82)::
|
|
126
130
|
|
|
131
|
+
routing = [(0x81, 0x20, 0), (0x20, 0x82, None)]
|
|
127
132
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
| .-----------| | |
|
|
131
|
-
| ShMC | CM | | MMC |
|
|
132
|
-
channel=0 | | | channel=7 | |
|
|
133
|
-
81 ------------| 0x20 |0x82 0x20 |-------------| 0x72 |
|
|
134
|
-
| | | | |
|
|
135
|
-
| | | | |
|
|
136
|
-
| `-----------| | |
|
|
137
|
-
`-------------------´ `--------´
|
|
138
|
-
`------------´ `---´ `---------------´
|
|
133
|
+
Example #2, access to an AMC in a uTCA chassis (slave 0x81,
|
|
134
|
+
target 0x72)::
|
|
139
135
|
|
|
140
|
-
|
|
136
|
+
routing = [(0x81, 0x20, 0), (0x20, 0x82, 7), (0x20, 0x72, None)]
|
|
141
137
|
|
|
142
|
-
|
|
143
|
-
|
|
138
|
+
uTCA - MCH AMC
|
|
139
|
+
.-------------------. .--------.
|
|
140
|
+
| .-----------| | |
|
|
141
|
+
| ShMC | CM | | MMC |
|
|
142
|
+
channel=0 | | | channel=7 | |
|
|
143
|
+
81 ------------| 0x20 |0x82 0x20 |-------------| 0x72 |
|
|
144
|
+
| | | | |
|
|
145
|
+
| | | | |
|
|
146
|
+
| `-----------| | |
|
|
147
|
+
`-------------------´ `--------´
|
|
148
|
+
`------------´ `---´ `---------------´
|
|
144
149
|
|
|
150
|
+
Example #3, access to an AMC in an ATCA AMC carrier (slave 0x81,
|
|
151
|
+
target 0x72)::
|
|
152
|
+
|
|
153
|
+
routing = [(0x81, 0x20, 0), (0x20, 0x8e, 7), (0x20, 0x80, None)]
|
|
145
154
|
"""
|
|
146
155
|
if is_string(routing):
|
|
147
156
|
# if type(routing) in [unicode, str]:
|
|
148
157
|
routing = ast.literal_eval(routing)
|
|
149
158
|
self.routing = [Routing(*route) for route in routing]
|
|
150
159
|
|
|
151
|
-
def __str__(self):
|
|
152
|
-
|
|
160
|
+
def __str__(self) -> str:
|
|
161
|
+
if self.ipmb_address is None:
|
|
162
|
+
string = 'Target: IPMB: none\n'
|
|
163
|
+
else:
|
|
164
|
+
string = 'Target: IPMB: 0x%02x\n' % self.ipmb_address
|
|
153
165
|
if self.routing:
|
|
154
166
|
for route in self.routing:
|
|
155
167
|
string += ' %s\n' % route
|
|
@@ -158,10 +170,11 @@ class Target(object):
|
|
|
158
170
|
|
|
159
171
|
class Ipmi(bmc.Bmc, chassis.Chassis, dcmi.Dcmi, fru.Fru, picmg.Picmg, hpm.Hpm,
|
|
160
172
|
sdr.Sdr, sensor.Sensor, event.Event, sel.Sel, lan.Lan,
|
|
161
|
-
messaging.Messaging):
|
|
173
|
+
messaging.Messaging, vita.Vita):
|
|
162
174
|
|
|
163
|
-
def __init__(self, interface=None, target
|
|
164
|
-
|
|
175
|
+
def __init__(self, interface: Any = None, target: Target | None = None,
|
|
176
|
+
session: Session | None = None,
|
|
177
|
+
requester: Any = None) -> None:
|
|
165
178
|
self._interface = interface
|
|
166
179
|
|
|
167
180
|
# we need a session, set if not passed
|
|
@@ -172,33 +185,35 @@ class Ipmi(bmc.Bmc, chassis.Chassis, dcmi.Dcmi, fru.Fru, picmg.Picmg, hpm.Hpm,
|
|
|
172
185
|
self._session.interface = interface
|
|
173
186
|
|
|
174
187
|
self._target = target
|
|
175
|
-
self.requester = requester
|
|
188
|
+
self.requester = requester if requester is not None else NullRequester()
|
|
176
189
|
|
|
177
190
|
for base in Ipmi.__bases__:
|
|
178
|
-
base.__init__(self)
|
|
191
|
+
base.__init__(self) # type: ignore[misc]
|
|
179
192
|
|
|
180
|
-
def __enter__(self):
|
|
193
|
+
def __enter__(self) -> Ipmi:
|
|
181
194
|
self.open()
|
|
182
195
|
return self
|
|
183
196
|
|
|
184
|
-
def __exit__(self, exception_type, exception_value,
|
|
197
|
+
def __exit__(self, exception_type: Any, exception_value: Any,
|
|
198
|
+
traceback: Any) -> Literal[False]:
|
|
185
199
|
self.close()
|
|
186
200
|
return False
|
|
187
201
|
|
|
188
|
-
def open(self):
|
|
202
|
+
def open(self) -> None:
|
|
189
203
|
self.interface.open()
|
|
190
204
|
if self.session is not None:
|
|
191
205
|
self.session.establish()
|
|
192
206
|
|
|
193
|
-
def close(self):
|
|
207
|
+
def close(self) -> None:
|
|
194
208
|
if self.session is not None:
|
|
195
209
|
self.session.close()
|
|
196
210
|
self.interface.close()
|
|
197
211
|
|
|
198
|
-
def is_ipmc_accessible(self):
|
|
212
|
+
def is_ipmc_accessible(self) -> bool:
|
|
199
213
|
return self.interface.is_ipmc_accessible(self.target)
|
|
200
214
|
|
|
201
|
-
def wait_until_ipmb_is_accessible(self, timeout,
|
|
215
|
+
def wait_until_ipmb_is_accessible(self, timeout: float,
|
|
216
|
+
interval: float = 0.25) -> None:
|
|
202
217
|
start_time = time.time()
|
|
203
218
|
while time.time() < start_time + (timeout):
|
|
204
219
|
try:
|
|
@@ -208,7 +223,7 @@ class Ipmi(bmc.Bmc, chassis.Chassis, dcmi.Dcmi, fru.Fru, picmg.Picmg, hpm.Hpm,
|
|
|
208
223
|
|
|
209
224
|
self.is_ipmc_accessible()
|
|
210
225
|
|
|
211
|
-
def send_message(self, req, retry=3):
|
|
226
|
+
def send_message(self, req: Message, retry: int = 3) -> Message:
|
|
212
227
|
req.target = self.target
|
|
213
228
|
req.requester = self.requester
|
|
214
229
|
rsp = None
|
|
@@ -226,7 +241,8 @@ class Ipmi(bmc.Bmc, chassis.Chassis, dcmi.Dcmi, fru.Fru, picmg.Picmg, hpm.Hpm,
|
|
|
226
241
|
|
|
227
242
|
return rsp
|
|
228
243
|
|
|
229
|
-
def send_message_with_name(self, name, *args,
|
|
244
|
+
def send_message_with_name(self, name: str, *args: Any,
|
|
245
|
+
**kwargs: Any) -> Message:
|
|
230
246
|
req = create_request_by_name(name)
|
|
231
247
|
|
|
232
248
|
for key, value in kwargs.items():
|
|
@@ -236,7 +252,7 @@ class Ipmi(bmc.Bmc, chassis.Chassis, dcmi.Dcmi, fru.Fru, picmg.Picmg, hpm.Hpm,
|
|
|
236
252
|
check_rsp_completion_code(rsp)
|
|
237
253
|
return rsp
|
|
238
254
|
|
|
239
|
-
def raw_command(self, lun, netfn, raw_bytes):
|
|
255
|
+
def raw_command(self, lun: int, netfn: int, raw_bytes: bytes) -> bytes:
|
|
240
256
|
"""Send the raw command data and return the raw response.
|
|
241
257
|
|
|
242
258
|
lun: the logical unit number
|
|
@@ -248,33 +264,35 @@ class Ipmi(bmc.Bmc, chassis.Chassis, dcmi.Dcmi, fru.Fru, picmg.Picmg, hpm.Hpm,
|
|
|
248
264
|
return self.interface.send_and_receive_raw(self.target, lun, netfn,
|
|
249
265
|
raw_bytes)
|
|
250
266
|
|
|
251
|
-
|
|
267
|
+
@property
|
|
268
|
+
def interface(self) -> Any:
|
|
252
269
|
try:
|
|
253
270
|
return self._interface
|
|
254
271
|
except AttributeError:
|
|
255
|
-
raise RuntimeError('No interface has been set')
|
|
272
|
+
raise RuntimeError('No interface has been set') from None
|
|
256
273
|
|
|
257
|
-
|
|
274
|
+
@interface.setter
|
|
275
|
+
def interface(self, interface: Any) -> None:
|
|
276
|
+
self._interface = interface
|
|
277
|
+
|
|
278
|
+
@property
|
|
279
|
+
def session(self) -> Session:
|
|
258
280
|
try:
|
|
259
281
|
return self._session
|
|
260
282
|
except AttributeError:
|
|
261
|
-
raise RuntimeError('No IPMI session has been set')
|
|
283
|
+
raise RuntimeError('No IPMI session has been set') from None
|
|
284
|
+
|
|
285
|
+
@session.setter
|
|
286
|
+
def session(self, session: Session) -> None:
|
|
287
|
+
self._session = session
|
|
262
288
|
|
|
263
|
-
|
|
289
|
+
@property
|
|
290
|
+
def target(self) -> Target | None:
|
|
264
291
|
try:
|
|
265
292
|
return self._target
|
|
266
293
|
except AttributeError:
|
|
267
|
-
raise RuntimeError('No IPMI target has been set')
|
|
268
|
-
|
|
269
|
-
def _set_interface(self, interface):
|
|
270
|
-
self._interface = interface
|
|
294
|
+
raise RuntimeError('No IPMI target has been set') from None
|
|
271
295
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
def _set_target(self, target):
|
|
296
|
+
@target.setter
|
|
297
|
+
def target(self, target: Target | None) -> None:
|
|
276
298
|
self._target = target
|
|
277
|
-
|
|
278
|
-
target = property(_get_target, _set_target)
|
|
279
|
-
interface = property(_get_interface, _set_interface)
|
|
280
|
-
session = property(_get_session, _set_session)
|
pyipmi/bmc.py
CHANGED
|
@@ -14,27 +14,80 @@
|
|
|
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
|
-
|
|
17
|
+
"""BMC device commands: device ID, resets, I2C access and watchdog timer.
|
|
18
|
+
|
|
19
|
+
The commands are the methods of :class:`Bmc`, which are available on
|
|
20
|
+
:class:`pyipmi.Ipmi`.
|
|
21
|
+
|
|
22
|
+
Example:
|
|
23
|
+
Print the device ID and the firmware version of the BMC::
|
|
24
|
+
|
|
25
|
+
device_id = ipmi.get_device_id()
|
|
26
|
+
print(device_id.device_id, device_id.fw_revision)
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
from array import array
|
|
32
|
+
|
|
33
|
+
from .msgs import create_request_by_name, Message
|
|
18
34
|
from .utils import check_completion_code
|
|
19
35
|
from .state import State
|
|
20
36
|
from .fields import VersionField
|
|
37
|
+
from .constants import manufacturer_name
|
|
38
|
+
from .mixin import IpmiMixin
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Bmc(IpmiMixin):
|
|
42
|
+
"""BMC device commands, available on :class:`pyipmi.Ipmi`."""
|
|
21
43
|
|
|
44
|
+
def get_device_id(self) -> DeviceId:
|
|
45
|
+
"""Get the device ID of the controller.
|
|
22
46
|
|
|
23
|
-
|
|
24
|
-
|
|
47
|
+
Returns:
|
|
48
|
+
The device ID, firmware version, IPMI version and supported
|
|
49
|
+
functions of the controller.
|
|
50
|
+
"""
|
|
25
51
|
return DeviceId(self.send_message_with_name('GetDeviceId'))
|
|
26
52
|
|
|
27
|
-
def get_device_guid(self):
|
|
53
|
+
def get_device_guid(self) -> DeviceGuid:
|
|
54
|
+
"""Get the GUID of the controller.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
The device GUID.
|
|
58
|
+
"""
|
|
28
59
|
return DeviceGuid(self.send_message_with_name('GetDeviceGuid'))
|
|
29
60
|
|
|
30
|
-
def cold_reset(self):
|
|
61
|
+
def cold_reset(self) -> None:
|
|
62
|
+
"""Cold reset the controller, it is reinitialized."""
|
|
31
63
|
self.send_message_with_name('ColdReset')
|
|
32
64
|
|
|
33
|
-
def warm_reset(self):
|
|
65
|
+
def warm_reset(self) -> None:
|
|
66
|
+
"""Warm reset the controller, its state is kept."""
|
|
34
67
|
self.send_message_with_name('WarmReset')
|
|
35
68
|
|
|
36
|
-
def i2c_write_read(self, bus_type, bus_id, channel
|
|
37
|
-
|
|
69
|
+
def i2c_write_read(self, bus_type: int, bus_id: int, channel: int,
|
|
70
|
+
address: int, count: int,
|
|
71
|
+
data: bytes | None = None) -> array:
|
|
72
|
+
"""Write to and read from a device on an I2C bus of the controller.
|
|
73
|
+
|
|
74
|
+
The data is written first, then ``count`` bytes are read.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
bus_type: 0 for a public bus (IPMB), 1 for a private bus.
|
|
78
|
+
bus_id: The bus ID.
|
|
79
|
+
channel: The channel number of a public bus.
|
|
80
|
+
address: The 7-bit I2C address of the device.
|
|
81
|
+
count: The number of bytes to read.
|
|
82
|
+
data: The data to write, nothing is written if None or empty.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
The data read.
|
|
86
|
+
|
|
87
|
+
Raises:
|
|
88
|
+
CompletionCodeError: The controller rejected the request, e.g.
|
|
89
|
+
because the device did not acknowledge.
|
|
90
|
+
"""
|
|
38
91
|
req = create_request_by_name('MasterWriteRead')
|
|
39
92
|
req.bus_id.type = bus_type
|
|
40
93
|
req.bus_id.id = bus_id
|
|
@@ -47,14 +100,55 @@ class Bmc(object):
|
|
|
47
100
|
check_completion_code(rsp.completion_code)
|
|
48
101
|
return rsp.data
|
|
49
102
|
|
|
50
|
-
def i2c_write(self, bus_type, bus_id, channel
|
|
103
|
+
def i2c_write(self, bus_type: int, bus_id: int, channel: int,
|
|
104
|
+
address: int, data: bytes) -> None:
|
|
105
|
+
"""Write to a device on an I2C bus of the controller.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
bus_type: 0 for a public bus (IPMB), 1 for a private bus.
|
|
109
|
+
bus_id: The bus ID.
|
|
110
|
+
channel: The channel number of a public bus.
|
|
111
|
+
address: The 7-bit I2C address of the device.
|
|
112
|
+
data: The data to write.
|
|
113
|
+
|
|
114
|
+
Raises:
|
|
115
|
+
CompletionCodeError: The controller rejected the request.
|
|
116
|
+
"""
|
|
51
117
|
self.i2c_write_read(bus_type, bus_id, channel, address, 0, data)
|
|
52
118
|
|
|
53
|
-
def i2c_read(self, bus_type, bus_id, channel
|
|
119
|
+
def i2c_read(self, bus_type: int, bus_id: int, channel: int,
|
|
120
|
+
address: int, count: int) -> array:
|
|
121
|
+
"""Read from a device on an I2C bus of the controller.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
bus_type: 0 for a public bus (IPMB), 1 for a private bus.
|
|
125
|
+
bus_id: The bus ID.
|
|
126
|
+
channel: The channel number of a public bus.
|
|
127
|
+
address: The 7-bit I2C address of the device.
|
|
128
|
+
count: The number of bytes to read.
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
The data read.
|
|
132
|
+
|
|
133
|
+
Raises:
|
|
134
|
+
CompletionCodeError: The controller rejected the request.
|
|
135
|
+
"""
|
|
54
136
|
return self.i2c_write_read(bus_type, bus_id, channel,
|
|
55
137
|
address, count, None)
|
|
56
138
|
|
|
57
|
-
def set_watchdog_timer(self, config):
|
|
139
|
+
def set_watchdog_timer(self, config: Watchdog) -> None:
|
|
140
|
+
"""Set the watchdog timer.
|
|
141
|
+
|
|
142
|
+
The timer is started with :meth:`reset_watchdog_timer`.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
config: The settings of the watchdog timer. All attributes
|
|
146
|
+
except ``is_running`` and ``present_countdown`` have to be
|
|
147
|
+
set.
|
|
148
|
+
|
|
149
|
+
Raises:
|
|
150
|
+
CompletionCodeError: The controller rejected the settings.
|
|
151
|
+
"""
|
|
58
152
|
req = create_request_by_name('SetWatchdogTimer')
|
|
59
153
|
req.timer_use.timer_use = config.timer_use
|
|
60
154
|
req.timer_use.dont_stop = config.dont_stop and 1 or 0
|
|
@@ -69,14 +163,49 @@ class Bmc(object):
|
|
|
69
163
|
rsp = self.send_message(req)
|
|
70
164
|
check_completion_code(rsp.completion_code)
|
|
71
165
|
|
|
72
|
-
def get_watchdog_timer(self):
|
|
166
|
+
def get_watchdog_timer(self) -> Watchdog:
|
|
167
|
+
"""Get the settings and the present countdown of the watchdog timer.
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
The watchdog timer, ``dont_stop`` is not set.
|
|
171
|
+
"""
|
|
73
172
|
return Watchdog(self.send_message_with_name('GetWatchdogTimer'))
|
|
74
173
|
|
|
75
|
-
def reset_watchdog_timer(self):
|
|
174
|
+
def reset_watchdog_timer(self) -> None:
|
|
175
|
+
"""Start or restart the watchdog timer with its initial countdown.
|
|
176
|
+
|
|
177
|
+
Raises:
|
|
178
|
+
CompletionCodeError: The timer was not set before (0x80).
|
|
179
|
+
"""
|
|
76
180
|
self.send_message_with_name('ResetWatchdogTimer')
|
|
77
181
|
|
|
78
182
|
|
|
79
183
|
class Watchdog(State):
|
|
184
|
+
"""The settings of the watchdog timer.
|
|
185
|
+
|
|
186
|
+
The ``TIMER_USE_*`` and ``TIMEOUT_ACTION_*`` constants are the timer
|
|
187
|
+
uses and the timeout actions.
|
|
188
|
+
|
|
189
|
+
Attributes:
|
|
190
|
+
timer_use (int): The use of the timer, one of the ``TIMER_USE_*``
|
|
191
|
+
constants.
|
|
192
|
+
is_running (bool): Only returned: the timer is running.
|
|
193
|
+
dont_log (bool): Don't log the timer expiration in the SEL.
|
|
194
|
+
pre_timeout_interrupt (int): The interrupt before the timeout
|
|
195
|
+
action: 0 none, 1 SMI, 2 NMI / diagnostic interrupt, 3 messaging
|
|
196
|
+
interrupt.
|
|
197
|
+
timeout_action (int): The action on the timer expiration, one of
|
|
198
|
+
the ``TIMEOUT_ACTION_*`` constants.
|
|
199
|
+
pre_timeout_interval (int): The time in seconds before the timeout
|
|
200
|
+
action at which the pre-timeout interrupt is generated.
|
|
201
|
+
timer_use_expiration_flags (int): A bit mask with one bit per timer
|
|
202
|
+
use, bit 1 for BIOS FRB2 up to bit 5 for OEM: the expired timer
|
|
203
|
+
uses when returned, the flags to clear when set.
|
|
204
|
+
initial_countdown (int): The countdown value in 100 ms units.
|
|
205
|
+
present_countdown (int): Only returned: the present countdown value
|
|
206
|
+
in 100 ms units.
|
|
207
|
+
"""
|
|
208
|
+
|
|
80
209
|
TIMER_USE_OEM = 5
|
|
81
210
|
TIMER_USE_SMS_OS = 4
|
|
82
211
|
TIMER_USE_OS_LOAD = 3
|
|
@@ -88,6 +217,10 @@ class Watchdog(State):
|
|
|
88
217
|
TIMEOUT_ACTION_POWER_DOWN = 2
|
|
89
218
|
TIMEOUT_ACTION_POWER_CYCLE = 3
|
|
90
219
|
|
|
220
|
+
#: Only used to set the timer: keep a running timer running. It is not
|
|
221
|
+
#: returned by :meth:`Bmc.get_watchdog_timer`.
|
|
222
|
+
dont_stop: bool | None
|
|
223
|
+
|
|
91
224
|
__properties__ = [
|
|
92
225
|
# (property, description)
|
|
93
226
|
('timer_use', ''),
|
|
@@ -102,7 +235,7 @@ class Watchdog(State):
|
|
|
102
235
|
('present_countdown', ''),
|
|
103
236
|
]
|
|
104
237
|
|
|
105
|
-
def _from_response(self, rsp):
|
|
238
|
+
def _from_response(self, rsp: Message) -> None:
|
|
106
239
|
self.timer_use = rsp.timer_use.timer_use
|
|
107
240
|
self.is_running = bool(rsp.timer_use.is_running)
|
|
108
241
|
self.dont_log = bool(rsp.timer_use.dont_log)
|
|
@@ -115,8 +248,30 @@ class Watchdog(State):
|
|
|
115
248
|
|
|
116
249
|
|
|
117
250
|
class DeviceId(State):
|
|
118
|
-
|
|
119
|
-
|
|
251
|
+
"""The device ID of a controller.
|
|
252
|
+
|
|
253
|
+
Attributes:
|
|
254
|
+
device_id (int): The device ID.
|
|
255
|
+
revision (int): The device revision.
|
|
256
|
+
provides_sdrs (bool): The device provides device SDRs.
|
|
257
|
+
available (bool): The device firmware, an SDR update or the
|
|
258
|
+
self-initialization is in progress. Despite its name, the
|
|
259
|
+
device is in normal operation if this is False.
|
|
260
|
+
fw_revision (VersionField): The firmware revision.
|
|
261
|
+
ipmi_version (VersionField): The IPMI version, e.g. 2.0.
|
|
262
|
+
manufacturer_id (int): The IANA manufacturer ID.
|
|
263
|
+
manufacturer_name (str | None): The name of a well known
|
|
264
|
+
manufacturer, see :func:`pyipmi.constants.manufacturer_name`.
|
|
265
|
+
None for other manufacturers.
|
|
266
|
+
product_id (int): The product ID.
|
|
267
|
+
supported_functions (list[str]): The supported device functions,
|
|
268
|
+
see :meth:`supports_function`.
|
|
269
|
+
aux (list[int] | None): The auxiliary firmware revision, None if
|
|
270
|
+
the controller does not report it.
|
|
271
|
+
"""
|
|
272
|
+
|
|
273
|
+
def __str__(self) -> str:
|
|
274
|
+
"""Return the device ID fields as one line."""
|
|
120
275
|
string = 'Device ID: %d' % self.device_id
|
|
121
276
|
string += ' revision: %d' % self.revision
|
|
122
277
|
string += ' available: %d' % self.available
|
|
@@ -127,15 +282,20 @@ class DeviceId(State):
|
|
|
127
282
|
string += ' functions: %s' % ','.join(self.supported_functions)
|
|
128
283
|
return string
|
|
129
284
|
|
|
130
|
-
def supports_function(self, name):
|
|
131
|
-
"""Return
|
|
285
|
+
def supports_function(self, name: str) -> bool:
|
|
286
|
+
"""Return whether the device supports a function.
|
|
287
|
+
|
|
288
|
+
Args:
|
|
289
|
+
name: The function, one of 'SENSOR', 'SDR_REPOSITORY', 'SEL',
|
|
290
|
+
'FRU_INVENTORY', 'IPMB_EVENT_RECEIVER',
|
|
291
|
+
'IPMB_EVENT_GENERATOR', 'BRIDGE' or 'CHASSIS', in any case.
|
|
132
292
|
|
|
133
|
-
|
|
134
|
-
|
|
293
|
+
Returns:
|
|
294
|
+
True if the function is supported.
|
|
135
295
|
"""
|
|
136
296
|
return name.lower() in self.supported_functions
|
|
137
297
|
|
|
138
|
-
def _from_response(self, rsp):
|
|
298
|
+
def _from_response(self, rsp: Message) -> None:
|
|
139
299
|
self.device_id = rsp.device_id
|
|
140
300
|
self.revision = rsp.device_revision.device_revision
|
|
141
301
|
self.provides_sdrs = bool(rsp.device_revision.provides_device_sdrs)
|
|
@@ -148,6 +308,7 @@ class DeviceId(State):
|
|
|
148
308
|
(rsp.ipmi_version & 0xf, (rsp.ipmi_version >> 4) & 0xf))
|
|
149
309
|
|
|
150
310
|
self.manufacturer_id = rsp.manufacturer_id
|
|
311
|
+
self.manufacturer_name = manufacturer_name(self.manufacturer_id)
|
|
151
312
|
self.product_id = rsp.product_id
|
|
152
313
|
|
|
153
314
|
self.supported_functions = []
|
|
@@ -171,10 +332,18 @@ class DeviceId(State):
|
|
|
171
332
|
|
|
172
333
|
|
|
173
334
|
class DeviceGuid(State):
|
|
174
|
-
|
|
335
|
+
"""The GUID of a controller.
|
|
336
|
+
|
|
337
|
+
Attributes:
|
|
338
|
+
device_guid (Sequence[int]): The 16 bytes of the GUID as returned.
|
|
339
|
+
device_guid_string (str): The GUID in the usual string format.
|
|
340
|
+
"""
|
|
341
|
+
|
|
342
|
+
def __str__(self) -> str:
|
|
343
|
+
"""Return the GUID string."""
|
|
175
344
|
return 'Device GUID: %s' % self.device_guid_string
|
|
176
345
|
|
|
177
|
-
def _from_response(self, rsp):
|
|
346
|
+
def _from_response(self, rsp: Message) -> None:
|
|
178
347
|
self.device_guid = rsp.device_guid
|
|
179
348
|
self.device_guid_string = \
|
|
180
349
|
'%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-' \
|