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.
- {python_ipmi-0.5.7/python_ipmi.egg-info → python_ipmi-0.5.8}/PKG-INFO +13 -3
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/__init__.py +32 -9
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/bmc.py +1 -1
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/errors.py +19 -1
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/fields.py +1 -1
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/fru.py +73 -49
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/helper.py +1 -1
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/hpm.py +1 -1
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/interfaces/aardvark.py +18 -10
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/interfaces/ipmb.py +2 -2
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/interfaces/ipmbdev.py +8 -4
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/interfaces/ipmitool.py +36 -6
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/interfaces/mock.py +6 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/interfaces/rmcp.py +8 -3
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/ipmitool.py +3 -5
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/messaging.py +1 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/message.py +2 -2
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/version.py +1 -1
- {python_ipmi-0.5.7 → python_ipmi-0.5.8/python_ipmi.egg-info}/PKG-INFO +13 -3
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/python_ipmi.egg-info/SOURCES.txt +1 -0
- python_ipmi-0.5.8/tests/test_event.py +38 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_fru.py +31 -9
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_ipmi.py +36 -1
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/AUTHORS +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/COPYING +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/README.rst +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/chassis.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/constants.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/dcmi.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/emulation.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/event.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/interfaces/__init__.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/lan.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/logger.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/__init__.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/bmc.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/chassis.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/constants.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/dcmi.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/device_messaging.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/event.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/fru.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/hpm.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/lan.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/picmg.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/registry.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/sdr.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/sel.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/sensor.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/session.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/msgs/vita.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/picmg.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/sdr.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/sel.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/sensor.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/session.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/state.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/pyipmi/utils.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/python_ipmi.egg-info/dependency_links.txt +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/python_ipmi.egg-info/entry_points.txt +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/python_ipmi.egg-info/top_level.txt +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/setup.cfg +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/setup.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_bmc.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_chassis.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_device_messaging.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_errors.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_fields.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_helper.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_hpm.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_ipmitool.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_lan.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_picmg.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_sdr.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_sel.py +0 -0
- {python_ipmi-0.5.7 → python_ipmi-0.5.8}/tests/test_sensor.py +0 -0
- {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
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: python-ipmi
|
|
3
|
-
Version: 0.5.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
169
|
-
self.
|
|
170
|
-
|
|
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
|
|
|
@@ -69,7 +69,7 @@ class DescriptionError(Exception):
|
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
class RetryError(Exception):
|
|
72
|
-
"""
|
|
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
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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)
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
55
|
-
self.enable_pullups(
|
|
56
|
-
if
|
|
57
|
-
self.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
|
|
60
|
-
self.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
|
-
|
|
81
|
-
self._session = session
|
|
89
|
+
pass
|
|
82
90
|
|
|
83
91
|
def close_session(self):
|
|
84
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
28
|
-
self._session = session
|
|
32
|
+
pass
|
|
29
33
|
|
|
30
34
|
def close_session(self):
|
|
31
|
-
|
|
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
|
|
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
|
-
|
|
135
|
-
|
|
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
|
|
|
@@ -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
|
-
"""
|
|
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
|
|
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
|
-
|
|
668
|
-
ipmi.session.close()
|
|
666
|
+
ipmi.close() # this will close interface and session
|
|
669
667
|
|
|
670
668
|
|
|
671
669
|
COMMANDS = (
|
|
@@ -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
|
|
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
|
|
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.
|
|
2
|
+
__version__ = '0.5.8'
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: python-ipmi
|
|
3
|
-
Version: 0.5.
|
|
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.
|
|
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
|
========================
|
|
@@ -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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|