python-ipmi 0.5.7__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 +115 -74
- pyipmi/bmc.py +193 -24
- pyipmi/chassis.py +204 -60
- pyipmi/constants.py +35 -0
- pyipmi/dcmi.py +351 -14
- pyipmi/errors.py +47 -17
- pyipmi/event.py +62 -3
- pyipmi/fields.py +33 -28
- pyipmi/fru.py +1140 -131
- pyipmi/helper.py +50 -16
- pyipmi/hpm.py +909 -188
- pyipmi/interfaces/__init__.py +14 -4
- pyipmi/interfaces/aardvark.py +130 -180
- pyipmi/interfaces/base.py +87 -0
- pyipmi/interfaces/ipmb.py +250 -44
- pyipmi/interfaces/ipmbdev.py +48 -160
- pyipmi/interfaces/ipmidev.py +244 -0
- pyipmi/interfaces/ipmitool.py +182 -84
- pyipmi/interfaces/mock.py +15 -8
- pyipmi/interfaces/openipmblink.py +312 -0
- pyipmi/interfaces/rmcp.py +225 -196
- pyipmi/interfaces/rmcpplus.py +570 -0
- pyipmi/interfaces/router.py +242 -0
- pyipmi/ipmitool.py +1112 -429
- pyipmi/lan.py +251 -76
- pyipmi/logger.py +4 -4
- pyipmi/messaging.py +208 -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 +120 -84
- 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.7.dist-info → python_ipmi-0.6.0.dist-info}/WHEEL +1 -1
- pyipmi/emulation.py +0 -395
- python_ipmi-0.5.7.dist-info/METADATA +0 -197
- python_ipmi-0.5.7.dist-info/RECORD +0 -56
- {python_ipmi-0.5.7.dist-info → python_ipmi-0.6.0.dist-info}/entry_points.txt +0 -0
- {python_ipmi-0.5.7.dist-info → python_ipmi-0.6.0.dist-info/licenses}/AUTHORS +0 -0
- {python_ipmi-0.5.7.dist-info → python_ipmi-0.6.0.dist-info/licenses}/COPYING +0 -0
- {python_ipmi-0.5.7.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,46 +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
|
-
|
|
52
|
-
ipmi.interface = interface
|
|
53
|
-
ipmi.session = session
|
|
54
|
-
ipmi.requester = NullRequester()
|
|
55
|
-
return ipmi
|
|
54
|
+
return Ipmi(interface=interface, session=session)
|
|
56
55
|
|
|
57
56
|
|
|
58
|
-
class Requester
|
|
57
|
+
class Requester:
|
|
59
58
|
"""The Requester class.
|
|
60
59
|
|
|
61
60
|
This represents an IPMI device which initiates a request/response
|
|
62
61
|
message exchange.
|
|
63
62
|
"""
|
|
64
63
|
|
|
65
|
-
def __init__(self, ipmb_address):
|
|
64
|
+
def __init__(self, ipmb_address: int) -> None:
|
|
66
65
|
self.ipmb_address = ipmb_address
|
|
67
66
|
|
|
68
67
|
|
|
69
|
-
class NullRequester
|
|
68
|
+
class NullRequester:
|
|
70
69
|
"""The NullRequester class.
|
|
71
70
|
|
|
72
71
|
This requester is used for interfaces which doesn't require a valid
|
|
@@ -74,31 +73,32 @@ class NullRequester(object):
|
|
|
74
73
|
"""
|
|
75
74
|
|
|
76
75
|
@property
|
|
77
|
-
def ipmb_address(self):
|
|
76
|
+
def ipmb_address(self) -> int:
|
|
78
77
|
raise AssertionError('NullRequester does not provide an IPMB address')
|
|
79
78
|
|
|
80
79
|
|
|
81
|
-
class Routing
|
|
80
|
+
class Routing:
|
|
82
81
|
"""The Target class represents an IPMI target."""
|
|
83
82
|
|
|
84
|
-
def __init__(self, rq_sa, rs_sa, channel):
|
|
83
|
+
def __init__(self, rq_sa: int, rs_sa: int, channel: int | None) -> None:
|
|
85
84
|
self.rq_sa = rq_sa
|
|
86
85
|
self.rs_sa = rs_sa
|
|
87
86
|
self.channel = channel
|
|
88
87
|
|
|
89
|
-
def __str__(self):
|
|
88
|
+
def __str__(self) -> str:
|
|
90
89
|
s = 'Routing: Rq: %s Rs: %s Ch: %s' \
|
|
91
90
|
% (self.rq_sa, self.rs_sa, self.channel)
|
|
92
91
|
return s
|
|
93
92
|
|
|
94
93
|
|
|
95
|
-
class Target
|
|
94
|
+
class Target:
|
|
96
95
|
"""The Target class represents an IPMI target."""
|
|
97
96
|
|
|
98
|
-
routing = None
|
|
99
|
-
ipmb_address = None
|
|
97
|
+
routing: list[Routing] | None = None
|
|
98
|
+
ipmb_address: int | None = None
|
|
100
99
|
|
|
101
|
-
def __init__(self, ipmb_address
|
|
100
|
+
def __init__(self, ipmb_address: int | None = None,
|
|
101
|
+
routing: str | list[tuple] | None = None) -> None:
|
|
102
102
|
"""Initializer for the Target class.
|
|
103
103
|
|
|
104
104
|
`ipmb_address` is the IPMB target address
|
|
@@ -111,49 +111,57 @@ class Target(object):
|
|
|
111
111
|
if routing:
|
|
112
112
|
self.set_routing(routing)
|
|
113
113
|
|
|
114
|
-
def set_routing_information(self, routing):
|
|
114
|
+
def set_routing_information(self, routing: str | list[tuple]) -> None:
|
|
115
115
|
self.set_routing(routing)
|
|
116
116
|
|
|
117
|
-
def set_routing(self, routing):
|
|
117
|
+
def set_routing(self, routing: str | list[tuple]) -> None:
|
|
118
118
|
"""Set the path over which a target is reachable.
|
|
119
119
|
|
|
120
|
-
|
|
121
|
-
|
|
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.
|
|
122
123
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
Args:
|
|
125
|
+
routing: The list of hops, or its string representation as
|
|
126
|
+
given on the command line.
|
|
126
127
|
|
|
127
|
-
Example #
|
|
128
|
-
|
|
129
|
-
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)::
|
|
130
130
|
|
|
131
|
+
routing = [(0x81, 0x20, 0), (0x20, 0x82, None)]
|
|
131
132
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
| .-----------| | |
|
|
135
|
-
| ShMC | CM | | MMC |
|
|
136
|
-
channel=0 | | | channel=7 | |
|
|
137
|
-
81 ------------| 0x20 |0x82 0x20 |-------------| 0x72 |
|
|
138
|
-
| | | | |
|
|
139
|
-
| | | | |
|
|
140
|
-
| `-----------| | |
|
|
141
|
-
`-------------------´ `--------´
|
|
142
|
-
`------------´ `---´ `---------------´
|
|
133
|
+
Example #2, access to an AMC in a uTCA chassis (slave 0x81,
|
|
134
|
+
target 0x72)::
|
|
143
135
|
|
|
144
|
-
|
|
136
|
+
routing = [(0x81, 0x20, 0), (0x20, 0x82, 7), (0x20, 0x72, None)]
|
|
145
137
|
|
|
146
|
-
|
|
147
|
-
|
|
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
|
+
`------------´ `---´ `---------------´
|
|
148
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)]
|
|
149
154
|
"""
|
|
150
155
|
if is_string(routing):
|
|
151
156
|
# if type(routing) in [unicode, str]:
|
|
152
157
|
routing = ast.literal_eval(routing)
|
|
153
158
|
self.routing = [Routing(*route) for route in routing]
|
|
154
159
|
|
|
155
|
-
def __str__(self):
|
|
156
|
-
|
|
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
|
|
157
165
|
if self.routing:
|
|
158
166
|
for route in self.routing:
|
|
159
167
|
string += ' %s\n' % route
|
|
@@ -162,20 +170,50 @@ class Target(object):
|
|
|
162
170
|
|
|
163
171
|
class Ipmi(bmc.Bmc, chassis.Chassis, dcmi.Dcmi, fru.Fru, picmg.Picmg, hpm.Hpm,
|
|
164
172
|
sdr.Sdr, sensor.Sensor, event.Event, sel.Sel, lan.Lan,
|
|
165
|
-
messaging.Messaging):
|
|
173
|
+
messaging.Messaging, vita.Vita):
|
|
174
|
+
|
|
175
|
+
def __init__(self, interface: Any = None, target: Target | None = None,
|
|
176
|
+
session: Session | None = None,
|
|
177
|
+
requester: Any = None) -> None:
|
|
178
|
+
self._interface = interface
|
|
179
|
+
|
|
180
|
+
# we need a session, set if not passed
|
|
181
|
+
if session is None:
|
|
182
|
+
session = Session()
|
|
183
|
+
self._session = session
|
|
184
|
+
# session needs an interface
|
|
185
|
+
self._session.interface = interface
|
|
166
186
|
|
|
167
|
-
|
|
168
|
-
self.
|
|
169
|
-
self._session = None
|
|
170
|
-
self._target = None
|
|
187
|
+
self._target = target
|
|
188
|
+
self.requester = requester if requester is not None else NullRequester()
|
|
171
189
|
|
|
172
190
|
for base in Ipmi.__bases__:
|
|
173
|
-
base.__init__(self)
|
|
191
|
+
base.__init__(self) # type: ignore[misc]
|
|
192
|
+
|
|
193
|
+
def __enter__(self) -> Ipmi:
|
|
194
|
+
self.open()
|
|
195
|
+
return self
|
|
196
|
+
|
|
197
|
+
def __exit__(self, exception_type: Any, exception_value: Any,
|
|
198
|
+
traceback: Any) -> Literal[False]:
|
|
199
|
+
self.close()
|
|
200
|
+
return False
|
|
174
201
|
|
|
175
|
-
def
|
|
202
|
+
def open(self) -> None:
|
|
203
|
+
self.interface.open()
|
|
204
|
+
if self.session is not None:
|
|
205
|
+
self.session.establish()
|
|
206
|
+
|
|
207
|
+
def close(self) -> None:
|
|
208
|
+
if self.session is not None:
|
|
209
|
+
self.session.close()
|
|
210
|
+
self.interface.close()
|
|
211
|
+
|
|
212
|
+
def is_ipmc_accessible(self) -> bool:
|
|
176
213
|
return self.interface.is_ipmc_accessible(self.target)
|
|
177
214
|
|
|
178
|
-
def wait_until_ipmb_is_accessible(self, timeout,
|
|
215
|
+
def wait_until_ipmb_is_accessible(self, timeout: float,
|
|
216
|
+
interval: float = 0.25) -> None:
|
|
179
217
|
start_time = time.time()
|
|
180
218
|
while time.time() < start_time + (timeout):
|
|
181
219
|
try:
|
|
@@ -185,7 +223,7 @@ class Ipmi(bmc.Bmc, chassis.Chassis, dcmi.Dcmi, fru.Fru, picmg.Picmg, hpm.Hpm,
|
|
|
185
223
|
|
|
186
224
|
self.is_ipmc_accessible()
|
|
187
225
|
|
|
188
|
-
def send_message(self, req, retry=3):
|
|
226
|
+
def send_message(self, req: Message, retry: int = 3) -> Message:
|
|
189
227
|
req.target = self.target
|
|
190
228
|
req.requester = self.requester
|
|
191
229
|
rsp = None
|
|
@@ -203,7 +241,8 @@ class Ipmi(bmc.Bmc, chassis.Chassis, dcmi.Dcmi, fru.Fru, picmg.Picmg, hpm.Hpm,
|
|
|
203
241
|
|
|
204
242
|
return rsp
|
|
205
243
|
|
|
206
|
-
def send_message_with_name(self, name, *args,
|
|
244
|
+
def send_message_with_name(self, name: str, *args: Any,
|
|
245
|
+
**kwargs: Any) -> Message:
|
|
207
246
|
req = create_request_by_name(name)
|
|
208
247
|
|
|
209
248
|
for key, value in kwargs.items():
|
|
@@ -213,7 +252,7 @@ class Ipmi(bmc.Bmc, chassis.Chassis, dcmi.Dcmi, fru.Fru, picmg.Picmg, hpm.Hpm,
|
|
|
213
252
|
check_rsp_completion_code(rsp)
|
|
214
253
|
return rsp
|
|
215
254
|
|
|
216
|
-
def raw_command(self, lun, netfn, raw_bytes):
|
|
255
|
+
def raw_command(self, lun: int, netfn: int, raw_bytes: bytes) -> bytes:
|
|
217
256
|
"""Send the raw command data and return the raw response.
|
|
218
257
|
|
|
219
258
|
lun: the logical unit number
|
|
@@ -225,33 +264,35 @@ class Ipmi(bmc.Bmc, chassis.Chassis, dcmi.Dcmi, fru.Fru, picmg.Picmg, hpm.Hpm,
|
|
|
225
264
|
return self.interface.send_and_receive_raw(self.target, lun, netfn,
|
|
226
265
|
raw_bytes)
|
|
227
266
|
|
|
228
|
-
|
|
267
|
+
@property
|
|
268
|
+
def interface(self) -> Any:
|
|
229
269
|
try:
|
|
230
270
|
return self._interface
|
|
231
271
|
except AttributeError:
|
|
232
|
-
raise RuntimeError('No interface has been set')
|
|
272
|
+
raise RuntimeError('No interface has been set') from None
|
|
233
273
|
|
|
234
|
-
|
|
274
|
+
@interface.setter
|
|
275
|
+
def interface(self, interface: Any) -> None:
|
|
276
|
+
self._interface = interface
|
|
277
|
+
|
|
278
|
+
@property
|
|
279
|
+
def session(self) -> Session:
|
|
235
280
|
try:
|
|
236
281
|
return self._session
|
|
237
282
|
except AttributeError:
|
|
238
|
-
raise RuntimeError('No IPMI session has been set')
|
|
283
|
+
raise RuntimeError('No IPMI session has been set') from None
|
|
239
284
|
|
|
240
|
-
|
|
285
|
+
@session.setter
|
|
286
|
+
def session(self, session: Session) -> None:
|
|
287
|
+
self._session = session
|
|
288
|
+
|
|
289
|
+
@property
|
|
290
|
+
def target(self) -> Target | None:
|
|
241
291
|
try:
|
|
242
292
|
return self._target
|
|
243
293
|
except AttributeError:
|
|
244
|
-
raise RuntimeError('No IPMI target has been set')
|
|
294
|
+
raise RuntimeError('No IPMI target has been set') from None
|
|
245
295
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
def _set_session(self, session):
|
|
250
|
-
self._session = session
|
|
251
|
-
|
|
252
|
-
def _set_target(self, target):
|
|
296
|
+
@target.setter
|
|
297
|
+
def target(self, target: Target | None) -> None:
|
|
253
298
|
self._target = target
|
|
254
|
-
|
|
255
|
-
target = property(_get_target, _set_target)
|
|
256
|
-
interface = property(_get_interface, _set_interface)
|
|
257
|
-
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,8 +217,12 @@ 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', ''),
|
|
94
227
|
('dont_stop', ''),
|
|
95
228
|
('is_running', ''),
|
|
@@ -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-' \
|