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.
Files changed (58) hide show
  1. pyipmi/__init__.py +115 -74
  2. pyipmi/bmc.py +193 -24
  3. pyipmi/chassis.py +204 -60
  4. pyipmi/constants.py +35 -0
  5. pyipmi/dcmi.py +351 -14
  6. pyipmi/errors.py +47 -17
  7. pyipmi/event.py +62 -3
  8. pyipmi/fields.py +33 -28
  9. pyipmi/fru.py +1140 -131
  10. pyipmi/helper.py +50 -16
  11. pyipmi/hpm.py +909 -188
  12. pyipmi/interfaces/__init__.py +14 -4
  13. pyipmi/interfaces/aardvark.py +130 -180
  14. pyipmi/interfaces/base.py +87 -0
  15. pyipmi/interfaces/ipmb.py +250 -44
  16. pyipmi/interfaces/ipmbdev.py +48 -160
  17. pyipmi/interfaces/ipmidev.py +244 -0
  18. pyipmi/interfaces/ipmitool.py +182 -84
  19. pyipmi/interfaces/mock.py +15 -8
  20. pyipmi/interfaces/openipmblink.py +312 -0
  21. pyipmi/interfaces/rmcp.py +225 -196
  22. pyipmi/interfaces/rmcpplus.py +570 -0
  23. pyipmi/interfaces/router.py +242 -0
  24. pyipmi/ipmitool.py +1112 -429
  25. pyipmi/lan.py +251 -76
  26. pyipmi/logger.py +4 -4
  27. pyipmi/messaging.py +208 -18
  28. pyipmi/mixin.py +52 -0
  29. pyipmi/msgs/__init__.py +0 -1
  30. pyipmi/msgs/constants.py +98 -0
  31. pyipmi/msgs/dcmi.py +177 -19
  32. pyipmi/msgs/device_messaging.py +34 -0
  33. pyipmi/msgs/fru.py +3 -1
  34. pyipmi/msgs/hpm.py +7 -7
  35. pyipmi/msgs/message.py +120 -84
  36. pyipmi/msgs/picmg.py +7 -5
  37. pyipmi/msgs/registry.py +18 -9
  38. pyipmi/msgs/sel.py +4 -4
  39. pyipmi/msgs/vita.py +0 -1
  40. pyipmi/picmg.py +490 -52
  41. pyipmi/sdr.py +583 -94
  42. pyipmi/sel.py +185 -20
  43. pyipmi/sensor.py +163 -38
  44. pyipmi/session.py +58 -34
  45. pyipmi/state.py +13 -5
  46. pyipmi/utils.py +60 -55
  47. pyipmi/version.py +1 -1
  48. pyipmi/vita.py +281 -0
  49. python_ipmi-0.6.0.dist-info/METADATA +312 -0
  50. python_ipmi-0.6.0.dist-info/RECORD +62 -0
  51. {python_ipmi-0.5.7.dist-info → python_ipmi-0.6.0.dist-info}/WHEEL +1 -1
  52. pyipmi/emulation.py +0 -395
  53. python_ipmi-0.5.7.dist-info/METADATA +0 -197
  54. python_ipmi-0.5.7.dist-info/RECORD +0 -56
  55. {python_ipmi-0.5.7.dist-info → python_ipmi-0.6.0.dist-info}/entry_points.txt +0 -0
  56. {python_ipmi-0.5.7.dist-info → python_ipmi-0.6.0.dist-info/licenses}/AUTHORS +0 -0
  57. {python_ipmi-0.5.7.dist-info → python_ipmi-0.6.0.dist-info/licenses}/COPYING +0 -0
  58. {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 absolute_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
- ipmi = Ipmi()
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(object):
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(object):
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(object):
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(object):
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=None, routing=None):
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
- The path is given as a list of tuples in the form (address,
121
- bridge_channel).
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
- Example #1: access to an ATCA blade in a chassis
124
- slave = 0x81, target = 0x82
125
- routing = [(0x81,0x20,0),(0x20,0x82,None)]
124
+ Args:
125
+ routing: The list of hops, or its string representation as
126
+ given on the command line.
126
127
 
127
- Example #2: access to an AMC in a uTCA chassis
128
- slave = 0x81, target = 0x72
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
- uTCA - MCH AMC
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
- Example #3: access to an AMC in a ATCA AMC carrier
136
+ routing = [(0x81, 0x20, 0), (0x20, 0x82, 7), (0x20, 0x72, None)]
145
137
 
146
- slave = 0x81, target = 0x72
147
- routing = [(0x81,0x20,0),(0x20,0x8e,7),(0x20,0x80,None)]
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
- string = 'Target: IPMB: 0x%02x\n' % self.ipmb_address
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
- def __init__(self):
168
- self._interface = None
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 is_ipmc_accessible(self):
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, interval=0.25):
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, **kwargs):
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
- def _get_interface(self):
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
- def _get_session(self):
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
- def _get_target(self):
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
- def _set_interface(self, interface):
247
- self._interface = interface
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
- from .msgs import create_request_by_name
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
- class Bmc(object):
24
- def get_device_id(self):
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, address, count,
37
- data=None):
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, address, data):
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, address, count):
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
- # (propery, description)
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
- def __str__(self):
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 if a function is supported.
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
- `name` is one of 'SENSOR', 'SDR_REPOSITORY', 'SEL', 'FRU_INVENTORY',
134
- 'IPMB_EVENT_RECEIVER', 'IPMB_EVENT_GENERATOR', 'BRIDGE', 'CHASSIS'.
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
- def __str__(self):
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-' \