python-ipmi 0.5.7__tar.gz → 0.5.8__tar.gz

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 (77) hide show
  1. {python_ipmi-0.5.7/python_ipmi.egg-info → python_ipmi-0.5.8}/PKG-INFO +13 -3
  2. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/__init__.py +32 -9
  3. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/bmc.py +1 -1
  4. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/errors.py +19 -1
  5. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/fields.py +1 -1
  6. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/fru.py +73 -49
  7. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/helper.py +1 -1
  8. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/hpm.py +1 -1
  9. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/interfaces/aardvark.py +18 -10
  10. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/interfaces/ipmb.py +2 -2
  11. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/interfaces/ipmbdev.py +8 -4
  12. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/interfaces/ipmitool.py +36 -6
  13. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/interfaces/mock.py +6 -0
  14. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/interfaces/rmcp.py +8 -3
  15. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/ipmitool.py +3 -5
  16. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/messaging.py +1 -0
  17. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/message.py +2 -2
  18. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/version.py +1 -1
  19. {python_ipmi-0.5.7 → python_ipmi-0.5.8/python_ipmi.egg-info}/PKG-INFO +13 -3
  20. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/python_ipmi.egg-info/SOURCES.txt +1 -0
  21. python_ipmi-0.5.8/tests/test_event.py +38 -0
  22. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_fru.py +31 -9
  23. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_ipmi.py +36 -1
  24. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/AUTHORS +0 -0
  25. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/COPYING +0 -0
  26. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/README.rst +0 -0
  27. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/chassis.py +0 -0
  28. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/constants.py +0 -0
  29. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/dcmi.py +0 -0
  30. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/emulation.py +0 -0
  31. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/event.py +0 -0
  32. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/interfaces/__init__.py +0 -0
  33. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/lan.py +0 -0
  34. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/logger.py +0 -0
  35. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/__init__.py +0 -0
  36. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/bmc.py +0 -0
  37. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/chassis.py +0 -0
  38. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/constants.py +0 -0
  39. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/dcmi.py +0 -0
  40. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/device_messaging.py +0 -0
  41. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/event.py +0 -0
  42. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/fru.py +0 -0
  43. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/hpm.py +0 -0
  44. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/lan.py +0 -0
  45. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/picmg.py +0 -0
  46. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/registry.py +0 -0
  47. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/sdr.py +0 -0
  48. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/sel.py +0 -0
  49. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/sensor.py +0 -0
  50. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/session.py +0 -0
  51. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/vita.py +0 -0
  52. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/picmg.py +0 -0
  53. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/sdr.py +0 -0
  54. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/sel.py +0 -0
  55. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/sensor.py +0 -0
  56. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/session.py +0 -0
  57. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/state.py +0 -0
  58. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/utils.py +0 -0
  59. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/python_ipmi.egg-info/dependency_links.txt +0 -0
  60. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/python_ipmi.egg-info/entry_points.txt +0 -0
  61. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/python_ipmi.egg-info/top_level.txt +0 -0
  62. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/setup.cfg +0 -0
  63. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/setup.py +0 -0
  64. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_bmc.py +0 -0
  65. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_chassis.py +0 -0
  66. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_device_messaging.py +0 -0
  67. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_errors.py +0 -0
  68. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_fields.py +0 -0
  69. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_helper.py +0 -0
  70. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_hpm.py +0 -0
  71. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_ipmitool.py +0 -0
  72. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_lan.py +0 -0
  73. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_picmg.py +0 -0
  74. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_sdr.py +0 -0
  75. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_sel.py +0 -0
  76. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_sensor.py +0 -0
  77. {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_utils.py +0 -0
@@ -1,9 +1,9 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: python-ipmi
3
- Version: 0.5.7
3
+ Version: 0.5.8
4
4
  Summary: Pure python IPMI library
5
5
  Home-page: https://github.com/kontron/python-ipmi
6
- Download-URL: https://github.com/kontron/python-ipmi/tarball/0.5.7
6
+ Download-URL: https://github.com/kontron/python-ipmi/tarball/0.5.8
7
7
  Author: Michael Walle, Heiko Thiery
8
8
  Author-email: michael.walle@kontron.com, heiko.thiery@kontron.com
9
9
  License: LGPLv2+
@@ -24,6 +24,16 @@ Classifier: Programming Language :: Python :: 3.13
24
24
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
25
  License-File: COPYING
26
26
  License-File: AUTHORS
27
+ Dynamic: author
28
+ Dynamic: author-email
29
+ Dynamic: classifier
30
+ Dynamic: description
31
+ Dynamic: download-url
32
+ Dynamic: home-page
33
+ Dynamic: license
34
+ Dynamic: license-file
35
+ Dynamic: platform
36
+ Dynamic: summary
27
37
 
28
38
  Pure Python IPMI Library
29
39
  ========================
@@ -48,11 +48,7 @@ except ImportError:
48
48
  def create_connection(interface):
49
49
  session = Session()
50
50
  session.interface = interface
51
- ipmi = Ipmi()
52
- ipmi.interface = interface
53
- ipmi.session = session
54
- ipmi.requester = NullRequester()
55
- return ipmi
51
+ return Ipmi(interface=interface, session=session)
56
52
 
57
53
 
58
54
  class Requester(object):
@@ -164,14 +160,41 @@ class Ipmi(bmc.Bmc, chassis.Chassis, dcmi.Dcmi, fru.Fru, picmg.Picmg, hpm.Hpm,
164
160
  sdr.Sdr, sensor.Sensor, event.Event, sel.Sel, lan.Lan,
165
161
  messaging.Messaging):
166
162
 
167
- def __init__(self):
168
- self._interface = None
169
- self._session = None
170
- self._target = None
163
+ def __init__(self, interface=None, target=None, session=Session(),
164
+ requester=NullRequester()):
165
+ self._interface = interface
166
+
167
+ # we need a session, set if not passed
168
+ if session is None:
169
+ session = Session()
170
+ self._session = session
171
+ # session needs an interface
172
+ self._session.interface = interface
173
+
174
+ self._target = target
175
+ self.requester = requester
171
176
 
172
177
  for base in Ipmi.__bases__:
173
178
  base.__init__(self)
174
179
 
180
+ def __enter__(self):
181
+ self.open()
182
+ return self
183
+
184
+ def __exit__(self, exception_type, exception_value, traceback):
185
+ self.close()
186
+ return False
187
+
188
+ def open(self):
189
+ self.interface.open()
190
+ if self.session is not None:
191
+ self.session.establish()
192
+
193
+ def close(self):
194
+ if self.session is not None:
195
+ self.session.close()
196
+ self.interface.close()
197
+
175
198
  def is_ipmc_accessible(self):
176
199
  return self.interface.is_ipmc_accessible(self.target)
177
200
 
@@ -89,7 +89,7 @@ class Watchdog(State):
89
89
  TIMEOUT_ACTION_POWER_CYCLE = 3
90
90
 
91
91
  __properties__ = [
92
- # (propery, description)
92
+ # (property, description)
93
93
  ('timer_use', ''),
94
94
  ('dont_stop', ''),
95
95
  ('is_running', ''),
@@ -69,7 +69,7 @@ class DescriptionError(Exception):
69
69
 
70
70
 
71
71
  class RetryError(Exception):
72
- """Maxium number of retries exceeded."""
72
+ """Maximum number of retries exceeded."""
73
73
  pass
74
74
 
75
75
 
@@ -90,3 +90,21 @@ class IpmiConnectionError(Exception):
90
90
 
91
91
  def __str__(self):
92
92
  return "{}".format(self.msg)
93
+
94
+
95
+ class IpmiLongPasswordError(Exception):
96
+ """Password longer than 20 bytes."""
97
+ def __init__(self, msg=None):
98
+ self.msg = msg
99
+
100
+ def __str__(self):
101
+ return "{}".format(self.msg)
102
+
103
+
104
+ class AuthenticationError(Exception):
105
+ """Authentication error."""
106
+ def __init__(self, msg=None):
107
+ self.msg = msg
108
+
109
+ def __str__(self):
110
+ return "{}".format(self.msg)
@@ -64,7 +64,7 @@ def _unpack6bitascii(data):
64
64
 
65
65
  class TypeLengthString(object):
66
66
  """
67
- This is the TYPE/LENGTH BYTE FORMAT field represenation according the
67
+ This is the TYPE/LENGTH BYTE FORMAT field representation according the
68
68
  Platform Management FRU Information Storage Definition v1.0.
69
69
 
70
70
  In addition the difference to the 'FRU Information Storage Definition' to
@@ -88,9 +88,9 @@ class Fru(object):
88
88
  def read_fru_data_full(self, fru_id=0):
89
89
  return self.read_fru_data(fru_id=fru_id)
90
90
 
91
- def get_fru_inventory_header(self, fru_id=0):
91
+ def get_fru_inventory_header(self, fru_id=0, ignore_checksum=False):
92
92
  data = self.read_fru_data(offset=0, count=8, fru_id=fru_id)
93
- return InventoryCommonHeader(data)
93
+ return InventoryCommonHeader(data, ignore_checksum=ignore_checksum)
94
94
 
95
95
  def _read_fru_area(self, offset, fru_id=0):
96
96
  # read the area header
@@ -99,26 +99,30 @@ class Fru(object):
99
99
  count = data[1] * 8
100
100
  return self.read_fru_data(offset=offset, count=count, fru_id=fru_id)
101
101
 
102
- def get_fru_chassis_area(self, fru_id=0):
103
- header = self.get_fru_inventory_header(fru_id=fru_id)
102
+ def get_fru_chassis_area(self, fru_id=0, ignore_checksum=False):
103
+ header = self.get_fru_inventory_header(fru_id=fru_id,
104
+ ignore_checksum=ignore_checksum)
104
105
  data = self._read_fru_area(offset=header.chassis_info_area_offset,
105
106
  fru_id=fru_id)
106
- return InventoryChassisInfoArea(data)
107
+ return InventoryChassisInfoArea(data, ignore_checksum=ignore_checksum)
107
108
 
108
- def get_fru_board_area(self, fru_id=0):
109
- header = self.get_fru_inventory_header(fru_id=fru_id)
109
+ def get_fru_board_area(self, fru_id=0, ignore_checksum=False):
110
+ header = self.get_fru_inventory_header(fru_id=fru_id,
111
+ ignore_checksum=ignore_checksum)
110
112
  data = self._read_fru_area(offset=header.board_info_area_offset,
111
113
  fru_id=fru_id)
112
- return InventoryBoardInfoArea(data)
114
+ return InventoryBoardInfoArea(data, ignore_checksum=ignore_checksum)
113
115
 
114
- def get_fru_product_area(self, fru_id=0):
115
- header = self.get_fru_inventory_header(fru_id=fru_id)
116
+ def get_fru_product_area(self, fru_id=0, ignore_checksum=False):
117
+ header = self.get_fru_inventory_header(fru_id=fru_id,
118
+ ignore_checksum=ignore_checksum)
116
119
  data = self._read_fru_area(offset=header.product_info_area_offset,
117
120
  fru_id=fru_id)
118
- return InventoryProductInfoArea(data)
121
+ return InventoryProductInfoArea(data, ignore_checksum=ignore_checksum)
119
122
 
120
- def get_fru_multirecord_area(self, fru_id=0):
121
- header = self.get_fru_inventory_header(fru_id=fru_id)
123
+ def get_fru_multirecord_area(self, fru_id=0, ignore_checksum=False):
124
+ header = self.get_fru_inventory_header(fru_id=fru_id,
125
+ ignore_checksum=ignore_checksum)
122
126
 
123
127
  # we have to determine the length of the area first
124
128
  offset = header.multirecord_area_offset
@@ -137,31 +141,47 @@ class Fru(object):
137
141
  # now read the full area
138
142
  offset = header.multirecord_area_offset
139
143
  data = self.read_fru_data(offset=offset, count=count)
140
- return InventoryMultiRecordArea(data)
144
+ return InventoryMultiRecordArea(data, ignore_checksum=ignore_checksum)
141
145
 
142
- def get_fru_inventory(self, fru_id=0):
146
+ def get_fru_inventory(self, fru_id=0, ignore_checksum=False):
143
147
  """
144
148
  Get the full parsed FRU inventory data.
145
149
  """
146
150
  fru = FruInventory()
147
- header = self.get_fru_inventory_header(fru_id=fru_id)
151
+
152
+ header = self.get_fru_inventory_header(
153
+ fru_id=fru_id,
154
+ ignore_checksum=ignore_checksum
155
+ )
148
156
 
149
157
  if header.chassis_info_area_offset:
150
- fru.chassis_info_area = self.get_fru_chassis_area(fru_id=fru_id)
158
+ fru.chassis_info_area = self.get_fru_chassis_area(
159
+ fru_id=fru_id,
160
+ ignore_checksum=ignore_checksum
161
+ )
151
162
 
152
163
  if header.board_info_area_offset:
153
- fru.board_info_area = self.get_fru_board_area(fru_id=fru_id)
164
+ fru.board_info_area = self.get_fru_board_area(
165
+ fru_id=fru_id,
166
+ ignore_checksum=ignore_checksum
167
+ )
154
168
 
155
169
  if header.product_info_area_offset:
156
- fru.product_info_area = self.get_fru_product_area(fru_id=fru_id)
170
+ fru.product_info_area = self.get_fru_product_area(
171
+ fru_id=fru_id,
172
+ ignore_checksum=ignore_checksum
173
+ )
157
174
 
158
175
  if header.multirecord_area_offset:
159
- fru.multirecord_area = self.get_fru_multirecord_area(fru_id=fru_id)
176
+ fru.multirecord_area = self.get_fru_multirecord_area(
177
+ fru_id=fru_id,
178
+ ignore_checksum=ignore_checksum
179
+ )
160
180
 
161
181
  return fru
162
182
 
163
183
 
164
- def get_fru_inventory_from_file(filename):
184
+ def get_fru_inventory_from_file(filename, ignore_checksum=False):
165
185
  try:
166
186
  file = open(filename, "rb")
167
187
  except IOError:
@@ -173,7 +193,7 @@ def get_fru_inventory_from_file(filename):
173
193
  file_data = file.read(file_size)
174
194
  data = array.array('B', file_data)
175
195
  file.close()
176
- return FruInventory(data)
196
+ return FruInventory(data, ignore_checksum=ignore_checksum)
177
197
 
178
198
 
179
199
  CUSTOM_FIELD_END = 0xc1
@@ -190,18 +210,18 @@ def _decode_custom_fields(data):
190
210
 
191
211
 
192
212
  class FruData(object):
193
- def __init__(self, data=None):
213
+ def __init__(self, data=None, ignore_checksum=False):
194
214
  if data:
195
215
  if isinstance(data, str):
196
216
  data = [ord(c) for c in data]
197
217
  self.data = data
198
218
  if hasattr(self, '_from_data'):
199
- self._from_data(data)
219
+ self._from_data(data, ignore_checksum=ignore_checksum)
200
220
 
201
221
 
202
222
  class InventoryCommonHeader(FruData):
203
- def _from_data(self, data):
204
- if len(data) != 8:
223
+ def _from_data(self, data, ignore_checksum=False):
224
+ if len(data) < 8:
205
225
  raise DecodingError('InventoryCommonHeader length != 8')
206
226
  self.format_version = data[0] & 0x0f
207
227
  self.internal_use_area_offset = data[1] * 8 or None
@@ -209,18 +229,18 @@ class InventoryCommonHeader(FruData):
209
229
  self.board_info_area_offset = data[3] * 8 or None
210
230
  self.product_info_area_offset = data[4] * 8 or None
211
231
  self.multirecord_area_offset = data[5] * 8 or None
212
- if sum(data) % 256 != 0:
213
- raise DecodingError('InventoryCommonHeader checksum failed')
232
+ if sum(data[:8]) % 256 != 0 and ignore_checksum is False:
233
+ raise DecodingError(f'InventoryCommonHeader checksum failed {sum(data) % 0x10}')
214
234
 
215
235
 
216
236
  class CommonInfoArea(FruData):
217
- def _from_data(self, data):
237
+ def _from_data(self, data, ignore_checksum=False):
218
238
  self.format_version = data[0] & 0x0f
219
239
  if self.format_version != 1:
220
240
  raise DecodingError('unsupported format version (%d)' %
221
241
  self.format_version)
222
242
  self.length = data[1] * 8
223
- if sum(data[:self.length]) % 256 != 0:
243
+ if sum(data[:self.length]) % 256 != 0 and ignore_checksum is False:
224
244
  raise DecodingError('checksum failed')
225
245
 
226
246
 
@@ -249,7 +269,7 @@ class InventoryChassisInfoArea(CommonInfoArea):
249
269
  TYPE_RAID_CHASSIS = 22
250
270
  TYPE_RACK_MOUNT_CHASSIS = 23
251
271
 
252
- def _from_data(self, data):
272
+ def _from_data(self, data, ignore_checksum=False):
253
273
  CommonInfoArea._from_data(self, data)
254
274
  self.type = data[2]
255
275
  offset = 3
@@ -261,8 +281,8 @@ class InventoryChassisInfoArea(CommonInfoArea):
261
281
 
262
282
 
263
283
  class InventoryBoardInfoArea(CommonInfoArea):
264
- def _from_data(self, data):
265
- CommonInfoArea._from_data(self, data)
284
+ def _from_data(self, data, ignore_checksum=False):
285
+ CommonInfoArea._from_data(self, data, ignore_checksum=ignore_checksum)
266
286
  self.language_code = data[2]
267
287
  minutes = data[5] << 16 | data[4] << 8 | data[3]
268
288
  self.mfg_date = (datetime.datetime(1996, 1, 1)
@@ -282,7 +302,7 @@ class InventoryBoardInfoArea(CommonInfoArea):
282
302
 
283
303
 
284
304
  class InventoryProductInfoArea(CommonInfoArea):
285
- def _from_data(self, data):
305
+ def _from_data(self, data, ignore_checksum=False):
286
306
  CommonInfoArea._from_data(self, data)
287
307
  self.language_code = data[2]
288
308
  offset = 3
@@ -318,17 +338,17 @@ class FruDataMultiRecord(FruData):
318
338
  return '%02x: %s' % (self.record_type_id,
319
339
  ' '.join('%02x' % b for b in self.raw))
320
340
 
321
- def _from_data(self, data):
341
+ def _from_data(self, data, ignore_checksum=False):
322
342
  if len(data) < 5:
323
343
  raise DecodingError('data too short')
324
344
  self.record_type_id = data[0]
325
345
  self.format_version = data[1] & 0x0f
326
346
  self.end_of_list = bool(data[1] & 0x80)
327
347
  self.length = data[2]
328
- if sum(data[:5]) % 256 != 0:
348
+ if sum(data[:5]) % 256 != 0 and ignore_checksum is False:
329
349
  raise DecodingError('FruDataMultiRecord header checksum failed')
330
350
  self.raw = data[5:5+self.length]
331
- if (sum(self.raw) + data[3]) % 256 != 0:
351
+ if (sum(self.raw) + data[3]) % 256 != 0 and ignore_checksum is False:
332
352
  raise DecodingError('FruDataMultiRecord record checksum failed')
333
353
 
334
354
  @staticmethod
@@ -386,11 +406,11 @@ class FruPicmgRecord(FruDataMultiRecord):
386
406
 
387
407
  return FruPicmgRecord(data)
388
408
 
389
- def _from_data(self, data):
409
+ def _from_data(self, data, ignore_checksum=False):
390
410
  if len(data) < 10:
391
411
  raise DecodingError('data too short')
392
412
  data = array.array('B', data)
393
- FruDataMultiRecord._from_data(self, data)
413
+ FruDataMultiRecord._from_data(self, data, ignore_checksum=ignore_checksum)
394
414
  self.manufacturer_id = \
395
415
  data[5] | data[6] << 8 | data[7] << 16
396
416
  self.picmg_record_type_id = data[8]
@@ -398,7 +418,7 @@ class FruPicmgRecord(FruDataMultiRecord):
398
418
 
399
419
 
400
420
  class FruPicmgPowerModuleCapabilityRecord(FruPicmgRecord):
401
- def _from_data(self, data):
421
+ def _from_data(self, data, ignore_checksum=False):
402
422
  if len(data) < 12:
403
423
  raise DecodingError('data too short')
404
424
  FruPicmgRecord._from_data(self, data)
@@ -407,11 +427,11 @@ class FruPicmgPowerModuleCapabilityRecord(FruPicmgRecord):
407
427
 
408
428
 
409
429
  class InventoryMultiRecordArea(object):
410
- def __init__(self, data):
430
+ def __init__(self, data, ignore_checksum=False):
411
431
  if data:
412
432
  self._from_data(data)
413
433
 
414
- def _from_data(self, data):
434
+ def _from_data(self, data, ignore_checksum=False):
415
435
  self.records = list()
416
436
  offset = 0
417
437
  while True:
@@ -423,31 +443,35 @@ class InventoryMultiRecordArea(object):
423
443
 
424
444
 
425
445
  class FruInventory(object):
426
- def __init__(self, data=None):
446
+ def __init__(self, data=None, ignore_checksum=False):
427
447
  self.chassis_info_area = None
428
448
  self.board_info_area = None
429
449
  self.product_info_area = None
430
450
  self.multirecord_area = None
431
451
 
432
452
  if data:
433
- self._from_data(data)
453
+ self._from_data(data, ignore_checksum=ignore_checksum)
434
454
 
435
- def _from_data(self, data):
455
+ def _from_data(self, data, ignore_checksum=False):
436
456
  self.raw = data
437
457
  self.common_header = InventoryCommonHeader(data[:8])
438
458
 
439
459
  if self.common_header.chassis_info_area_offset:
440
460
  self.chassis_info_area = InventoryChassisInfoArea(
441
- data[self.common_header.chassis_info_area_offset:])
461
+ data[self.common_header.chassis_info_area_offset:],
462
+ ignore_checksum=ignore_checksum)
442
463
 
443
464
  if self.common_header.board_info_area_offset:
444
465
  self.board_info_area = InventoryBoardInfoArea(
445
- data[self.common_header.board_info_area_offset:])
466
+ data[self.common_header.board_info_area_offset:],
467
+ ignore_checksum=ignore_checksum)
446
468
 
447
469
  if self.common_header.product_info_area_offset:
448
470
  self.product_info_area = InventoryProductInfoArea(
449
- data[self.common_header.product_info_area_offset:])
471
+ data[self.common_header.product_info_area_offset:],
472
+ ignore_checksum=ignore_checksum)
450
473
 
451
474
  if self.common_header.multirecord_area_offset:
452
475
  self.multirecord_area = InventoryMultiRecordArea(
453
- data[self.common_header.multirecord_area_offset:])
476
+ data[self.common_header.multirecord_area_offset:],
477
+ ignore_checksum=ignore_checksum)
@@ -85,7 +85,7 @@ def get_sdr_data_helper(reserve_fn, get_fn, record_id, reservation_id=None):
85
85
  (next_id, data) = get_fn(reservation_id, record_id, offset, length)
86
86
  except CompletionCodeError as e:
87
87
  if e.cc == constants.CC_CANT_RET_NUM_REQ_BYTES:
88
- # reduce max lenght
88
+ # reduce max length
89
89
  max_req_len -= 4
90
90
  if max_req_len <= 0:
91
91
  retry = 0
@@ -314,7 +314,7 @@ class Hpm(object):
314
314
  # tbd check version
315
315
 
316
316
  ####################################################
317
- # compare current revision with upgrade image earlist comp rev
317
+ # compare current revision with upgrade image earliest comp rev
318
318
  targetCap = self.get_target_upgrade_capabilities()
319
319
  # tbd check version
320
320
 
@@ -44,23 +44,32 @@ class Aardvark(object):
44
44
  'use this interface.')
45
45
 
46
46
  self.slave_address = slave_address
47
+ self.port = port
48
+ self.serial_number = serial_number
49
+ self.i2c_pullups = enable_i2c_pullups
50
+ self.target_power = enable_target_power
51
+ self.fastmode = enable_fastmode
47
52
  self.timeout = 0.25
48
53
  self.max_retries = 3
49
54
  self.next_sequence_number = 0
50
55
 
51
- self._dev = pyaardvark.open(port, serial_number)
56
+ def open(self):
57
+ self._dev = pyaardvark.open(self.port, self.serial_number)
52
58
  self._dev.enable_i2c_slave(self.slave_address >> 1)
53
59
 
54
- if enable_i2c_pullups:
55
- self.enable_pullups(enable_i2c_pullups)
56
- if enable_target_power:
57
- self.enable_target_power(enable_target_power)
60
+ if self.i2c_pullups:
61
+ self.enable_pullups(self.i2c_pullups)
62
+ if self.target_power:
63
+ self.enable_target_power(self.target_power)
58
64
 
59
- if enable_fastmode is not None:
60
- self.enable_fastmode(enable_fastmode)
65
+ if self.fastmode is not None:
66
+ self.enable_fastmode(self.fastmode)
61
67
  else:
62
68
  self.enable_fastmode(False)
63
69
 
70
+ def close(self):
71
+ self._dev.close()
72
+
64
73
  def enable_pullups(self, enabled):
65
74
  self._dev.i2c_pullups = enabled
66
75
 
@@ -77,11 +86,10 @@ class Aardvark(object):
77
86
  self._dev.i2c_master_write(address, data)
78
87
 
79
88
  def establish_session(self, session):
80
- # just remember session parameters here
81
- self._session = session
89
+ pass
82
90
 
83
91
  def close_session(self):
84
- self._dev.close()
92
+ pass
85
93
 
86
94
  def is_ipmc_accessible(self, target):
87
95
  header = IpmbHeaderReq()
@@ -149,7 +149,7 @@ def encode_ipmb_msg(header, data):
149
149
 
150
150
 
151
151
  def encode_send_message(payload, rq_sa, rs_sa, channel, seq, tracking=1):
152
- """Encode a send message command and embedd the message to be send.
152
+ """Encode a send message command and embed the message to be send.
153
153
 
154
154
  payload: the message to be send as bytestring
155
155
  rq_sa: the requester source address
@@ -178,7 +178,7 @@ def encode_send_message(payload, rq_sa, rs_sa, channel, seq, tracking=1):
178
178
 
179
179
 
180
180
  def encode_bridged_message(routing, header, payload, seq):
181
- """Encode a (multi-)bridged command and embedd the message to be send.
181
+ """Encode a (multi-)bridged command and embed the message to be send.
182
182
 
183
183
  routing:
184
184
  payload: the message to be send as bytestring
@@ -17,18 +17,22 @@ class IpmbDev(object):
17
17
  def __init__(self, slave_address=0x20, port='/dev/ipmb-0'):
18
18
  # TODO: slave address is currently not defined here
19
19
  self.slave_address = slave_address
20
+ self.port = port
20
21
  self.timeout = 0.25
21
22
  self.max_retries = 3
22
23
  self.next_sequence_number = 0
23
24
 
24
- self._dev = os.open(port, os.O_RDWR)
25
+ def open(self):
26
+ self._dev = os.open(self.port, os.O_RDWR)
27
+
28
+ def close(self):
29
+ os.close(self._dev)
25
30
 
26
31
  def establish_session(self, session):
27
- # just remember session parameters here
28
- self._session = session
32
+ pass
29
33
 
30
34
  def close_session(self):
31
- os.close(self._dev)
35
+ pass
32
36
 
33
37
  def is_ipmc_accessible(self, target):
34
38
  header = IpmbHeaderReq()
@@ -21,7 +21,12 @@ from subprocess import Popen, PIPE
21
21
  from array import array
22
22
 
23
23
  from ..session import Session
24
- from ..errors import IpmiTimeoutError, IpmiConnectionError
24
+ from ..errors import (
25
+ IpmiTimeoutError,
26
+ IpmiConnectionError,
27
+ IpmiLongPasswordError,
28
+ AuthenticationError,
29
+ )
25
30
  from ..logger import log
26
31
  from ..msgs import encode_message, decode_message, create_message
27
32
  from ..msgs.constants import CC_OK
@@ -62,13 +67,24 @@ class Ipmitool(object):
62
67
  r".*Unable to establish.*")
63
68
  self.re_could_not_open = re.compile(
64
69
  r".*Could not open device.*")
65
-
70
+ self.re_long_password = re.compile(
71
+ r".*password is longer than.*")
72
+ self.re_authentication_error = re.compile(
73
+ r".*RAKP [0-9]+ HMAC.*")
66
74
  self._session = None
67
75
 
76
+ def open(self):
77
+ pass
78
+
79
+ def close(self):
80
+ pass
81
+
68
82
  def establish_session(self, session):
69
- # just remember session parameters here
70
83
  self._session = session
71
84
 
85
+ def close_session(self):
86
+ pass
87
+
72
88
  def rmcp_ping(self):
73
89
 
74
90
  if self._interface_type == 'serial-terminal':
@@ -80,6 +96,7 @@ class Ipmitool(object):
80
96
  cmd += (' -I %s' % self._interface_type)
81
97
  cmd += (' -H %s' % self._session.rmcp_host)
82
98
  cmd += (' -p %s' % self._session.rmcp_port)
99
+ cmd += (' -v')
83
100
  if self._session.auth_type == Session.AUTH_TYPE_NONE:
84
101
  cmd += (' -A NONE')
85
102
  elif self._session.auth_type == Session.AUTH_TYPE_PASSWORD:
@@ -108,6 +125,9 @@ class Ipmitool(object):
108
125
  # Don't try to parse ipmitool error messages
109
126
  if 'failed' in line:
110
127
  continue
128
+ # Don't try to parse spurious ipmitool output
129
+ if 'Received a response with unexpected ID' in line:
130
+ continue
111
131
 
112
132
  # Check for timeout
113
133
  if self.re_timeout.match(line):
@@ -127,13 +147,22 @@ class Ipmitool(object):
127
147
  if self.re_could_not_open.match(line):
128
148
  raise RuntimeError('ipmitool failed: {}'.format(output))
129
149
 
150
+ if self.re_long_password.match(line):
151
+ raise IpmiLongPasswordError(line)
152
+
153
+ if self.re_authentication_error.match(line):
154
+ raise AuthenticationError('Authentication error')
155
+
130
156
  hexstr += line.replace('\r', '').strip() + ' '
131
157
 
132
158
  hexstr = hexstr.strip()
133
159
  if len(hexstr):
134
- rsp = array('B', [
135
- int(value, 16) for value in hexstr.split(' ')
136
- ])
160
+ try:
161
+ rsp = array('B', [
162
+ int(value, 16) for value in hexstr.split(' ')
163
+ ])
164
+ except ValueError:
165
+ pass
137
166
 
138
167
  return cc, rsp
139
168
 
@@ -235,6 +264,7 @@ class Ipmitool(object):
235
264
  cmd += (' -I %s' % self._interface_type)
236
265
  cmd += (' -H %s' % self._session.rmcp_host)
237
266
  cmd += (' -p %s' % self._session.rmcp_port)
267
+ cmd += (' -v')
238
268
 
239
269
  cmd += self._build_ipmitool_priv_level(self._session.priv_level)
240
270
 
@@ -6,6 +6,12 @@ class Mock(object):
6
6
  def __init__(self):
7
7
  pass
8
8
 
9
+ def open(self):
10
+ pass
11
+
12
+ def close(self):
13
+ pass
14
+
9
15
  def establish_session(self, session):
10
16
  pass
11
17
 
@@ -248,7 +248,7 @@ class IpmiMsg(object):
248
248
  return struct.unpack("<I", struct.pack(">I", seq))[0]
249
249
 
250
250
  def _padd_password(self):
251
- """Padd the password.
251
+ """Pad the password.
252
252
 
253
253
  The password/key is 0 padded to 16-bytes for all specified
254
254
  authentication types.
@@ -393,12 +393,10 @@ class Rmcp(object):
393
393
  """
394
394
  self.host = None
395
395
  self.port = None
396
- self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
397
396
  self.seq_number = 0xff
398
397
  self.slave_address = slave_address
399
398
  self.host_target = Target(host_target_address)
400
399
  self.max_retries = max_retries
401
- self.set_timeout(2.0)
402
400
  self.next_sequence_number = 0
403
401
  self.keep_alive_interval = keep_alive_interval
404
402
  self._stop_keep_alive = None
@@ -408,6 +406,13 @@ class Rmcp(object):
408
406
  self.ignore_sdu_length = quirks_cfg.get('rmcp_ignore_sdu_length', False)
409
407
  self.ignore_rq_seq = quirks_cfg.get('rmcp_ignore_rq_seq', False)
410
408
 
409
+ def open(self):
410
+ self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
411
+ self.set_timeout(2.0)
412
+
413
+ def close(self):
414
+ pass
415
+
411
416
  def _send_rmcp_msg(self, sdu, class_of_msg):
412
417
  rmcp = RmcpMsg(class_of_msg)
413
418
  pdu = rmcp.pack(sdu, self.seq_number)
@@ -474,7 +474,7 @@ Options:
474
474
  -I <interface> Set interface (available: rmcp, aardvark, ipmitool, ipmbdev)
475
475
  -H <host> Set RMCP host
476
476
  -U <user> Set RMCP user
477
- -L <level> Set RMCP priviledge level
477
+ -L <level> Set RMCP privilege level
478
478
  -P <password> Set RMCP password
479
479
  -o <options> Set interface specific options (name=value, separated
480
480
  by commas, see below for available options).
@@ -644,9 +644,8 @@ def main():
644
644
  if rmcp_priv_level is not None:
645
645
  ipmi.session.set_priv_level(rmcp_priv_level)
646
646
 
647
- ipmi.session.establish()
648
-
649
647
  try:
648
+ ipmi.open() # this will open interface and session
650
649
  cmd(ipmi, args)
651
650
  except pyipmi.errors.CompletionCodeError as e:
652
651
  print('Command returned with completion code 0x%02x' % e.cc)
@@ -664,8 +663,7 @@ def main():
664
663
  sys.exit(1)
665
664
 
666
665
  finally:
667
- if rmcp_host is not None:
668
- ipmi.session.close()
666
+ ipmi.close() # this will close interface and session
669
667
 
670
668
 
671
669
  COMMANDS = (
@@ -196,3 +196,4 @@ class UserAccess(State):
196
196
  s += ' IPMI messaging: %s\n' % self.ipmi_messaging
197
197
  s += ' Link Auth.: %s\n' % self.link_auth
198
198
  s += ' Callback only: %s' % self.callback_only
199
+ return s
@@ -54,7 +54,7 @@ class ByteArray(BaseField):
54
54
  def encode(self, obj, data):
55
55
  a = getattr(obj, self.name)
56
56
  if len(a) != self._length(obj):
57
- raise EncodingError('Array must be exaclty %d bytes long '
57
+ raise EncodingError('Array must be exactly %d bytes long '
58
58
  '(but is %d long)' %
59
59
  (self._length(obj), len(a)))
60
60
  for i in range(self._length(obj)):
@@ -320,7 +320,7 @@ class Message(object):
320
320
 
321
321
  buf -- option message buffer to decode
322
322
 
323
- Optional keyword arguments corresponts to members to set (matching
323
+ Optional keyword arguments corresponds to members to set (matching
324
324
  fields in self.__fields__, or 'data').
325
325
  """
326
326
  # create message fields
@@ -1,2 +1,2 @@
1
1
  # This file was autogenerated by setup.py
2
- __version__ = '0.5.7'
2
+ __version__ = '0.5.8'
@@ -1,9 +1,9 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: python-ipmi
3
- Version: 0.5.7
3
+ Version: 0.5.8
4
4
  Summary: Pure python IPMI library
5
5
  Home-page: https://github.com/kontron/python-ipmi
6
- Download-URL: https://github.com/kontron/python-ipmi/tarball/0.5.7
6
+ Download-URL: https://github.com/kontron/python-ipmi/tarball/0.5.8
7
7
  Author: Michael Walle, Heiko Thiery
8
8
  Author-email: michael.walle@kontron.com, heiko.thiery@kontron.com
9
9
  License: LGPLv2+
@@ -24,6 +24,16 @@ Classifier: Programming Language :: Python :: 3.13
24
24
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
25
  License-File: COPYING
26
26
  License-File: AUTHORS
27
+ Dynamic: author
28
+ Dynamic: author-email
29
+ Dynamic: classifier
30
+ Dynamic: description
31
+ Dynamic: download-url
32
+ Dynamic: home-page
33
+ Dynamic: license
34
+ Dynamic: license-file
35
+ Dynamic: platform
36
+ Dynamic: summary
27
37
 
28
38
  Pure Python IPMI Library
29
39
  ========================
@@ -61,6 +61,7 @@ tests/test_bmc.py
61
61
  tests/test_chassis.py
62
62
  tests/test_device_messaging.py
63
63
  tests/test_errors.py
64
+ tests/test_event.py
64
65
  tests/test_fields.py
65
66
  tests/test_fru.py
66
67
  tests/test_helper.py
@@ -0,0 +1,38 @@
1
+ from unittest.mock import MagicMock
2
+
3
+ from pyipmi import interfaces, create_connection
4
+ from pyipmi.msgs.event import (SetEventReceiverRsp, GetEventReceiverRsp)
5
+
6
+
7
+ class TestEvent(object):
8
+
9
+ def setup_method(self):
10
+ self.mock_send_recv = MagicMock()
11
+
12
+ interface = interfaces.create_interface('mock')
13
+ self.ipmi = create_connection(interface)
14
+ self.ipmi.send_message = self.mock_send_recv
15
+
16
+ def test_set_event_receiver(self):
17
+
18
+ rsp = SetEventReceiverRsp()
19
+ rsp.completion_code = 0
20
+ self.mock_send_recv.return_value = rsp
21
+
22
+ self.ipmi.set_event_receiver(ipmb_address=0xb0, lun=1)
23
+ args, _ = self.mock_send_recv.call_args
24
+ req = args[0]
25
+ assert req.event_receiver.ipmb_i2c_slave_address == 0xb0
26
+ assert req.event_receiver.lun == 1
27
+
28
+ def test_get_event_receiver(self):
29
+
30
+ rsp = GetEventReceiverRsp()
31
+ rsp.completion_code = 0
32
+ rsp.event_receiver.ipmb_i2c_slave_address = 0xc0
33
+ rsp.event_receiver.lun = 2
34
+ self.mock_send_recv.return_value = rsp
35
+
36
+ (addr, lun) = self.ipmi.get_event_receiver()
37
+ assert addr == 0xc0
38
+ assert lun == 2
@@ -1,12 +1,19 @@
1
1
  #!/usr/bin/env python
2
2
  # -*- coding: utf-8 -*-
3
3
  import os
4
+ import pytest
4
5
 
5
- from pyipmi.fru import (FruData, FruPicmgPowerModuleCapabilityRecord,
6
+ from pyipmi.errors import (DecodingError)
7
+
8
+ from pyipmi.fru import (FruData, FruInventory,
9
+ FruPicmgPowerModuleCapabilityRecord,
6
10
  InventoryCommonHeader, InventoryBoardInfoArea,
7
11
  get_fru_inventory_from_file)
8
12
 
9
13
 
14
+ this_file_path = os.path.dirname(os.path.abspath(__file__))
15
+
16
+
10
17
  def test_frudata_object():
11
18
  fru_field = FruData((0, 1, 2, 3))
12
19
  assert fru_field.data[0] == 0
@@ -25,16 +32,21 @@ def test_commonheader_object():
25
32
  InventoryCommonHeader((0, 1, 2, 3, 4, 5, 6, 235))
26
33
 
27
34
 
35
+ def test_commonheader_object_invalid_checksum():
36
+ with pytest.raises(DecodingError):
37
+ InventoryCommonHeader((0, 1, 2, 3, 4, 5, 6, 0))
38
+
39
+ InventoryCommonHeader((0, 1, 2, 3, 4, 5, 6, 0), ignore_checksum=True)
40
+
41
+
28
42
  def test_fru_inventory_from_file():
29
- path = os.path.dirname(os.path.abspath(__file__))
30
- fru_file = os.path.join(path, 'fru_bin/kontron_am4010.bin')
43
+ fru_file = os.path.join(this_file_path, 'fru_bin/kontron_am4010.bin')
31
44
  fru = get_fru_inventory_from_file(fru_file)
32
45
  assert fru.chassis_info_area is None
33
46
 
34
47
 
35
48
  def test_board_area():
36
- path = os.path.dirname(os.path.abspath(__file__))
37
- fru_file = os.path.join(path, 'fru_bin/kontron_am4010.bin')
49
+ fru_file = os.path.join(this_file_path, 'fru_bin/kontron_am4010.bin')
38
50
  fru = get_fru_inventory_from_file(fru_file)
39
51
 
40
52
  board_area = fru.board_info_area
@@ -45,8 +57,7 @@ def test_board_area():
45
57
 
46
58
 
47
59
  def test_product_area():
48
- path = os.path.dirname(os.path.abspath(__file__))
49
- fru_file = os.path.join(path, 'fru_bin/kontron_am4010.bin')
60
+ fru_file = os.path.join(this_file_path, 'fru_bin/kontron_am4010.bin')
50
61
  fru = get_fru_inventory_from_file(fru_file)
51
62
 
52
63
  product_area = fru.product_info_area
@@ -57,8 +68,7 @@ def test_product_area():
57
68
 
58
69
 
59
70
  def test_multirecord_with_power_module_capability_record():
60
- path = os.path.dirname(os.path.abspath(__file__))
61
- fru_file = os.path.join(path, 'fru_bin/vadatech_utc017.bin')
71
+ fru_file = os.path.join(this_file_path, 'fru_bin/vadatech_utc017.bin')
62
72
  fru = get_fru_inventory_from_file(fru_file)
63
73
  assert len(fru.multirecord_area.records) == 1
64
74
  record = fru.multirecord_area.records[0]
@@ -72,3 +82,15 @@ def test_BoardInfoArea():
72
82
  assert area.product_name.string == 'PowerEdge R515 '
73
83
  assert area.serial_number.string == 'CN717033AI0058'
74
84
  assert area.part_number.string == '0RMRF7A05'
85
+
86
+
87
+ def test_FruInventory_ignore_checksum_error():
88
+ data = b'\x01\x00\x00\x01\x04\x00\x00\xfa\x01\x03\x00vq\xb4\xcaASRockRack\xc0\xc0\xc0\xc0\xc1\x00\x1b\x01\x03\x00\xcaASRockRack\xc0\xc0\xc0\xc0\xc0\xc0\xc1\x00\x00M\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
89
+
90
+ with pytest.raises(DecodingError):
91
+ FruInventory(data, ignore_checksum=False)
92
+
93
+ inv = FruInventory(data, ignore_checksum=True)
94
+
95
+ assert inv.board_info_area.manufacturer.string == 'ASRockRack'
96
+ assert inv.product_info_area.manufacturer.string == 'ASRockRack'
@@ -5,7 +5,8 @@ from unittest.mock import MagicMock
5
5
 
6
6
  import pytest
7
7
 
8
- from pyipmi import interfaces, create_connection, Target, Routing
8
+ from pyipmi import (Ipmi, interfaces, create_connection, NullRequester, Routing,
9
+ Session, Target)
9
10
  from pyipmi.errors import CompletionCodeError, RetryError
10
11
  from pyipmi.msgs.bmc import GetDeviceIdReq, GetDeviceIdRsp
11
12
  from pyipmi.msgs.sensor import GetSensorReadingReq, GetSensorReadingRsp
@@ -88,6 +89,40 @@ def test_target_set_routing_from_string():
88
89
  assert target.routing[1].channel == 0x23
89
90
 
90
91
 
92
+ def test_create_connection():
93
+ interface = interfaces.create_interface('mock')
94
+
95
+ ipmi = create_connection(interface)
96
+ assert ipmi.interface is not None
97
+ assert isinstance(ipmi.interface, interfaces.mock.Mock)
98
+ assert isinstance(ipmi.session, Session)
99
+ assert isinstance(ipmi.session.interface, interfaces.mock.Mock)
100
+ assert isinstance(ipmi.requester, NullRequester)
101
+
102
+
103
+ def test_ipmi():
104
+ ipmi = Ipmi()
105
+ assert ipmi.interface is None
106
+ assert isinstance(ipmi.session, Session)
107
+ assert isinstance(ipmi.requester, NullRequester)
108
+
109
+ interface = interfaces.create_interface('mock')
110
+ ipmi = Ipmi(interface=interface)
111
+ assert isinstance(ipmi.interface, interfaces.mock.Mock)
112
+ assert isinstance(ipmi.session, Session)
113
+ assert isinstance(ipmi.session.interface, interfaces.mock.Mock)
114
+ assert isinstance(ipmi.requester, NullRequester)
115
+
116
+
117
+ def test_ipmi_with_statemetn():
118
+ interface = interfaces.create_interface('mock')
119
+
120
+ with Ipmi(interface=interface) as ipmi:
121
+ assert ipmi.interface is not None
122
+ assert isinstance(ipmi.session, Session)
123
+ assert isinstance(ipmi.requester, NullRequester)
124
+
125
+
91
126
  def test_ipmi_send_message_retry():
92
127
  req = GetDeviceIdReq()
93
128
  rsp = GetDeviceIdRsp()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes