Merge "Add vendor interface to ipminative driver"
This commit is contained in:
commit
cabb692669
@ -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()
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user