diff --git a/pyipmi/errors.py b/pyipmi/errors.py index 9d10cc1..2c1dbfe 100644 --- a/pyipmi/errors.py +++ b/pyipmi/errors.py @@ -99,3 +99,12 @@ def __init__(self, msg=None): def __str__(self): return "{}".format(self.msg) + + +class AuthenticationError(Exception): + """Authentication error.""" + def __init__(self, msg=None): + self.msg = msg + + def __str__(self): + return "{}".format(self.msg) diff --git a/pyipmi/interfaces/ipmitool.py b/pyipmi/interfaces/ipmitool.py index d0adb2e..8b92146 100644 --- a/pyipmi/interfaces/ipmitool.py +++ b/pyipmi/interfaces/ipmitool.py @@ -21,7 +21,12 @@ from array import array from ..session import Session -from ..errors import IpmiTimeoutError, IpmiConnectionError, IpmiLongPasswordError +from ..errors import ( + IpmiTimeoutError, + IpmiConnectionError, + IpmiLongPasswordError, + AuthenticationError, +) from ..logger import log from ..msgs import encode_message, decode_message, create_message from ..msgs.constants import CC_OK @@ -64,6 +69,8 @@ def __init__(self, interface_type='lan', cipher=None): r".*Could not open device.*") self.re_long_password = re.compile( r".*password is longer than.*") + self.re_authentication_error = re.compile( + r".*RAKP [0-9]+ HMAC.*") self._session = None def open(self): @@ -89,6 +96,7 @@ def rmcp_ping(self): cmd += (' -I %s' % self._interface_type) cmd += (' -H %s' % self._session.rmcp_host) cmd += (' -p %s' % self._session.rmcp_port) + cmd += (' -v') if self._session.auth_type == Session.AUTH_TYPE_NONE: cmd += (' -A NONE') elif self._session.auth_type == Session.AUTH_TYPE_PASSWORD: @@ -142,13 +150,19 @@ def _parse_output(self, output): if self.re_long_password.match(line): raise IpmiLongPasswordError(line) + if self.re_authentication_error.match(line): + raise AuthenticationError('Authentication error') + hexstr += line.replace('\r', '').strip() + ' ' hexstr = hexstr.strip() if len(hexstr): - rsp = array('B', [ - int(value, 16) for value in hexstr.split(' ') - ]) + try: + rsp = array('B', [ + int(value, 16) for value in hexstr.split(' ') + ]) + except ValueError: + pass return cc, rsp @@ -250,6 +264,7 @@ def _build_ipmitool_cmd(self, target, lun, netfn, raw_bytes): cmd += (' -I %s' % self._interface_type) cmd += (' -H %s' % self._session.rmcp_host) cmd += (' -p %s' % self._session.rmcp_port) + cmd += (' -v') cmd += self._build_ipmitool_priv_level(self._session.priv_level) diff --git a/tests/interfaces/test_ipmitool.py b/tests/interfaces/test_ipmitool.py index 4ae59bd..de8c0bf 100644 --- a/tests/interfaces/test_ipmitool.py +++ b/tests/interfaces/test_ipmitool.py @@ -5,7 +5,7 @@ import pytest -from pyipmi.errors import IpmiTimeoutError, IpmiConnectionError, IpmiLongPasswordError +from pyipmi.errors import IpmiTimeoutError, IpmiConnectionError, IpmiLongPasswordError, AuthenticationError from pyipmi.interfaces import Ipmitool from pyipmi import Session, Target from pyipmi.utils import py3_array_tobytes @@ -53,7 +53,7 @@ def test_rmcp_ping(self): self._interface.rmcp_ping() mock.assert_called_once_with('ipmitool -I lan -H 10.0.1.1 -p 623 ' - '-U "admin" -P "secret" ' + '-v -U "admin" -P "secret" ' 'session info all') def test_send_and_receive_raw_valid(self): @@ -65,7 +65,7 @@ def test_send_and_receive_raw_valid(self): self._interface.send_and_receive_raw(target, 0, 0x6, b'\x01') mock.assert_called_once_with('ipmitool -I lan -H 10.0.1.1 -p 623 ' - '-L ADMINISTRATOR -U "admin" -P "secret" ' + '-v -L ADMINISTRATOR -U "admin" -P "secret" ' '-t 0x20 -l 0 raw 0x06 0x01 2>&1') def test_send_and_receive_raw_lanplus(self): @@ -80,7 +80,7 @@ def test_send_and_receive_raw_lanplus(self): interface.send_and_receive_raw(target, 0, 0x6, b'\x01') mock.assert_called_once_with('ipmitool -I lanplus -H 10.0.1.1 -p 623 ' - '-L ADMINISTRATOR -U "admin" -P "secret" ' + '-v -L ADMINISTRATOR -U "admin" -P "secret" ' '-t 0x20 -l 0 raw 0x06 0x01 2>&1') def test_send_and_receive_raw_cipher(self): @@ -95,7 +95,7 @@ def test_send_and_receive_raw_cipher(self): interface.send_and_receive_raw(target, 0, 0x6, b'\x01') mock.assert_called_once_with('ipmitool -I lan -H 10.0.1.1 -p 623 ' - '-L ADMINISTRATOR -C 7 ' + '-v -L ADMINISTRATOR -C 7 ' '-U "admin" -P "secret" ' '-t 0x20 -l 0 raw 0x06 0x01 2>&1') @@ -110,7 +110,7 @@ def test_send_and_receive_raw_no_auth(self): self._interface.send_and_receive_raw(target, 0, 0x6, b'\x01') mock.assert_called_once_with('ipmitool -I lan -H 10.0.1.1 -p 623 ' - '-L ADMINISTRATOR -P "" ' + '-v -L ADMINISTRATOR -P "" ' '-t 0x20 -l 0 raw 0x06 0x01 2>&1') def test_send_and_receive_raw_return_value(self): @@ -236,3 +236,9 @@ def test_parse_long_password_error(self): with pytest.raises(IpmiLongPasswordError): cc, rsp = self._interface._parse_output(test_str) assert rsp is None + + def test_parse_output_authentication_error(self): + test_str = b'Loading ...\nUsing ...\n> RAKP 2 HMAC is invalid\nError:' + with pytest.raises(AuthenticationError): + cc, rsp = self._interface._parse_output(test_str) + assert rsp is None