Merge "Add vendor interface to ipminative driver"

This commit is contained in:
Jenkins 2015-09-08 09:51:00 +00:00 committed by Gerrit Code Review
commit cabb692669
3 changed files with 185 additions and 0 deletions

View File

@ -112,6 +112,7 @@ class FakeIPMINativeDriver(base.BaseDriver):
self.power = ipminative.NativeIPMIPower()
self.console = ipminative.NativeIPMIShellinaboxConsole()
self.deploy = fake.FakeDeploy()
self.vendor = ipminative.VendorPassthru()
self.management = ipminative.NativeIPMIManagement()

View File

@ -297,6 +297,44 @@ def _get_sensors_data(driver_info):
return sensors_data
def _parse_raw_bytes(raw_bytes):
"""Parse raw bytes string.
:param raw_bytes: a string of hexadecimal raw bytes, e.g. '0x00 0x01'.
:returns: a tuple containing the arguments for pyghmi call as integers,
(IPMI net function, IPMI command, list of command's data).
:raises: InvalidParameterValue when an invalid value is specified.
"""
try:
bytes_list = [int(x, base=16) for x in raw_bytes.split()]
return bytes_list[0], bytes_list[1], bytes_list[2:]
except ValueError:
raise exception.InvalidParameterValue(_(
"Invalid raw bytes string: '%s'") % raw_bytes)
except IndexError:
raise exception.InvalidParameterValue(_(
"Raw bytes string requires two bytes at least."))
def _send_raw(driver_info, raw_bytes):
"""Send raw bytes to the BMC."""
netfn, command, data = _parse_raw_bytes(raw_bytes)
LOG.debug("Sending raw bytes %(bytes)s to node %(node_id)s",
{'bytes': raw_bytes, 'node_id': driver_info['uuid']})
try:
ipmicmd = ipmi_command.Command(bmc=driver_info['address'],
userid=driver_info['username'],
password=driver_info['password'])
ipmicmd.xraw_command(netfn, command, data=data)
except pyghmi_exception.IpmiException as e:
msg = (_("IPMI send raw bytes '%(bytes)s' failed for node %(node_id)s"
" with the following error: %(error)s") %
{'bytes': raw_bytes, 'node_id': driver_info['uuid'],
'error': e})
LOG.error(msg)
raise exception.IPMIFailure(msg)
class NativeIPMIPower(base.PowerInterface):
"""The power driver using native python-ipmi library."""
@ -569,3 +607,65 @@ class NativeIPMIShellinaboxConsole(base.ConsoleInterface):
driver_info = _parse_driver_info(task.node)
url = console_utils.get_shellinabox_console_url(driver_info['port'])
return {'type': 'shellinabox', 'url': url}
class VendorPassthru(base.VendorInterface):
def get_properties(self):
return COMMON_PROPERTIES
def validate(self, task, method, **kwargs):
"""Validate vendor-specific actions.
:param task: a task from TaskManager.
:param method: method to be validated
:param kwargs: info for action.
:raises: InvalidParameterValue when an invalid parameter value is
specified.
:raises: MissingParameterValue if a required parameter is missing.
"""
if method == 'send_raw':
raw_bytes = kwargs.get('raw_bytes')
if not raw_bytes:
raise exception.MissingParameterValue(_(
'Parameter raw_bytes (string of bytes) was not '
'specified.'))
_parse_raw_bytes(raw_bytes)
_parse_driver_info(task.node)
@base.passthru(['POST'])
@task_manager.require_exclusive_lock
def send_raw(self, task, http_method, raw_bytes):
"""Send raw bytes to the BMC. Bytes should be a string of bytes.
:param task: a TaskManager instance.
:param http_method: the HTTP method used on the request.
:param raw_bytes: a string of raw bytes to send, e.g. '0x00 0x01'
:raises: IPMIFailure on an error from native IPMI call.
:raises: MissingParameterValue if a required parameter is missing.
:raises: InvalidParameterValue when an invalid value is specified.
"""
driver_info = _parse_driver_info(task.node)
_send_raw(driver_info, raw_bytes)
@base.passthru(['POST'])
@task_manager.require_exclusive_lock
def bmc_reset(self, task, http_method, warm=True):
"""Reset BMC via IPMI command.
:param task: a TaskManager instance.
:param http_method: the HTTP method used on the request.
:param warm: boolean parameter to decide on warm or cold reset.
:raises: IPMIFailure on an error from native IPMI call.
:raises: MissingParameterValue if a required parameter is missing.
:raises: InvalidParameterValue when an invalid value is specified
"""
driver_info = _parse_driver_info(task.node)
# NOTE(yuriyz): pyghmi 0.8.0 does not have a method for BMC reset
command = '0x03' if warm else '0x02'
raw_command = '0x06 ' + command
_send_raw(driver_info, raw_command)

View File

@ -201,6 +201,38 @@ class IPMINativePrivateMethodTestCase(db_base.DbTestCase):
ret = ipminative._get_sensors_data(self.info)
self.assertEqual(expected, ret)
def test__parse_raw_bytes_ok(self):
bytes_string = '0x11 0x12 0x25 0xFF'
netfn, cmd, data = ipminative._parse_raw_bytes(bytes_string)
self.assertEqual(0x11, netfn)
self.assertEqual(0x12, cmd)
self.assertEqual([0x25, 0xFF], data)
def test__parse_raw_bytes_invalid_value(self):
bytes_string = '0x11 oops'
self.assertRaises(exception.InvalidParameterValue,
ipminative._parse_raw_bytes,
bytes_string)
def test__parse_raw_bytes_missing_byte(self):
bytes_string = '0x11'
self.assertRaises(exception.InvalidParameterValue,
ipminative._parse_raw_bytes,
bytes_string)
@mock.patch('pyghmi.ipmi.command.Command', autospec=True)
def test__send_raw(self, ipmi_mock):
ipmicmd = ipmi_mock.return_value
ipminative._send_raw(self.info, '0x01 0x02 0x03 0x04')
ipmicmd.xraw_command.assert_called_once_with(1, 2, data=[3, 4])
@mock.patch('pyghmi.ipmi.command.Command', autospec=True)
def test__send_raw_fail(self, ipmi_mock):
ipmicmd = ipmi_mock.return_value
ipmicmd.xraw_command.side_effect = pyghmi_exception.IpmiException()
self.assertRaises(exception.IPMIFailure, ipminative._send_raw,
self.info, '0x01 0x02')
class IPMINativeDriverTestCase(db_base.DbTestCase):
"""Test cases for ipminative.NativeIPMIPower class functions."""
@ -219,6 +251,7 @@ class IPMINativeDriverTestCase(db_base.DbTestCase):
expected = ipminative.COMMON_PROPERTIES
self.assertEqual(expected, self.driver.power.get_properties())
self.assertEqual(expected, self.driver.management.get_properties())
self.assertEqual(expected, self.driver.vendor.get_properties())
expected = list(ipminative.COMMON_PROPERTIES)
expected += list(ipminative.CONSOLE_PROPERTIES)
@ -466,3 +499,54 @@ class IPMINativeDriverTestCase(db_base.DbTestCase):
self.assertEqual(expected, console_info)
mock_exec.assert_called_once_with(self.info['port'])
self.assertTrue(mock_exec.called)
@mock.patch.object(ipminative, '_parse_driver_info', autospec=True)
@mock.patch.object(ipminative, '_parse_raw_bytes', autospec=True)
def test_vendor_passthru_validate__send_raw_bytes_good(self, mock_raw,
mock_driver):
with task_manager.acquire(self.context, self.node.uuid) as task:
self.driver.vendor.validate(task,
method='send_raw',
http_method='POST',
raw_bytes='0x00 0x01')
mock_raw.assert_called_once_with('0x00 0x01')
mock_driver.assert_called_once_with(task.node)
def test_vendor_passthru_validate__send_raw_bytes_fail(self):
with task_manager.acquire(self.context, self.node.uuid) as task:
self.assertRaises(exception.MissingParameterValue,
self.driver.vendor.validate,
task, method='send_raw')
def test_vendor_passthru_vendor_routes(self):
expected = ['send_raw', 'bmc_reset']
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
vendor_routes = task.driver.vendor.vendor_routes
self.assertIsInstance(vendor_routes, dict)
self.assertEqual(sorted(expected), sorted(vendor_routes))
@mock.patch.object(ipminative, '_send_raw', autospec=True)
def test_send_raw(self, send_raw_mock):
bytes = '0x00 0x01'
with task_manager.acquire(self.context,
self.node.uuid) as task:
self.driver.vendor.send_raw(task, http_method='POST',
raw_bytes=bytes)
send_raw_mock.assert_called_once_with(self.info, bytes)
@mock.patch.object(ipminative, '_send_raw', autospec=True)
def _test_bmc_reset(self, warm, send_raw_mock):
expected_bytes = '0x06 0x03' if warm else '0x06 0x02'
with task_manager.acquire(self.context,
self.node.uuid) as task:
self.driver.vendor.bmc_reset(task, http_method='POST', warm=warm)
send_raw_mock.assert_called_once_with(self.info, expected_bytes)
def test_bmc_reset_cold(self):
self._test_bmc_reset(False)
def test_bmc_reset_warm(self):
self._test_bmc_reset(True)