summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ironic/conductor/utils.py20
-rw-r--r--ironic/drivers/fake.py2
-rw-r--r--ironic/drivers/modules/ipminative.py145
-rw-r--r--ironic/drivers/pxe.py7
-rw-r--r--ironic/tests/drivers/test_ipminative.py92
5 files changed, 159 insertions, 107 deletions
diff --git a/ironic/conductor/utils.py b/ironic/conductor/utils.py
index e8189e4ea..fe771189d 100644
--- a/ironic/conductor/utils.py
+++ b/ironic/conductor/utils.py
@@ -34,23 +34,11 @@ def node_set_boot_device(task, device, persistent=False):
ManagementInterface fails.
"""
- try:
- # TODO(lucasagomes): Remove this conditional once all drivers
- # are ported to use the management interface
- if getattr(task.driver, 'management', None):
- task.driver.management.validate(task)
- task.driver.management.set_boot_device(task,
- device=device,
- persistent=persistent)
- else:
- task.driver.vendor.vendor_passthru(task,
+ if getattr(task.driver, 'management', None):
+ task.driver.management.validate(task)
+ task.driver.management.set_boot_device(task,
device=device,
- persistent=persistent,
- method='set_boot_device')
- except exception.UnsupportedDriverExtension:
- # NOTE(deva): Some drivers, like SSH, do not support set_boot_device.
- # This is not a fatal exception.
- pass
+ persistent=persistent)
@task_manager.require_exclusive_lock
diff --git a/ironic/drivers/fake.py b/ironic/drivers/fake.py
index eeed764dc..936dabc4d 100644
--- a/ironic/drivers/fake.py
+++ b/ironic/drivers/fake.py
@@ -79,7 +79,7 @@ class FakeIPMINativeDriver(base.BaseDriver):
def __init__(self):
self.power = ipminative.NativeIPMIPower()
self.deploy = fake.FakeDeploy()
- self.vendor = ipminative.VendorPassthru()
+ self.management = ipminative.NativeIPMIManagement()
class FakeSeaMicroDriver(base.BaseDriver):
diff --git a/ironic/drivers/modules/ipminative.py b/ironic/drivers/modules/ipminative.py
index 4be2b943b..f2a05d7b8 100644
--- a/ironic/drivers/modules/ipminative.py
+++ b/ironic/drivers/modules/ipminative.py
@@ -21,7 +21,9 @@ Ironic Native IPMI power manager.
from oslo.config import cfg
+from ironic.common import boot_devices
from ironic.common import exception
+from ironic.common import i18n
from ironic.common import states
from ironic.conductor import task_manager
from ironic.drivers import base
@@ -45,6 +47,8 @@ opts = [
'Recommended setting is 5 seconds.'),
]
+_LE = i18n._LE
+
CONF = cfg.CONF
CONF.register_opts(opts, group='ipmi')
@@ -55,6 +59,13 @@ REQUIRED_PROPERTIES = {'ipmi_address': _("IP of the node's BMC. Required."),
'ipmi_username': _("IPMI username. Required.")}
COMMON_PROPERTIES = REQUIRED_PROPERTIES
+_BOOT_DEVICES_MAP = {
+ boot_devices.DISK: 'hd',
+ boot_devices.PXE: 'net',
+ boot_devices.CDROM: 'cdrom',
+ boot_devices.BIOS: 'setup',
+}
+
def _parse_driver_info(node):
"""Gets the bmc access info for the given node.
@@ -276,22 +287,51 @@ class NativeIPMIPower(base.PowerInterface):
_reboot(driver_info)
-class VendorPassthru(base.VendorInterface):
+class NativeIPMIManagement(base.ManagementInterface):
+
+ def get_properties(self):
+ return COMMON_PROPERTIES
+
+ def validate(self, task):
+ """Check that 'driver_info' contains IPMI credentials.
+
+ Validates whether the 'driver_info' property of the supplied
+ task's node contains the required credentials information.
+
+ :param task: a task from TaskManager.
+ :raises: InvalidParameterValue when required ipmi credentials
+ are missing.
+
+ """
+ _parse_driver_info(task.node)
+
+ def get_supported_boot_devices(self):
+ """Get a list of the supported boot devices.
+
+ :returns: A list with the supported boot devices defined
+ in :mod:`ironic.common.boot_devices`.
+
+ """
+ return list(_BOOT_DEVICES_MAP.keys())
@task_manager.require_exclusive_lock
- def _set_boot_device(self, task, device, persistent=False):
- """Set the boot device for a node.
-
- :param task: a TaskManager instance.
- :param device: Boot device. One of [net, network, pxe, hd, cd,
- cdrom, dvd, floppy, default, setup, f1]
- :param persistent: Whether to set next-boot, or make the change
- permanent. Default: False.
- :raises: InvalidParameterValue if an invalid boot device is specified
- or required ipmi credentials are missing.
- :raises: IPMIFailure when the native ipmi call fails.
+ def set_boot_device(self, task, device, persistent=False):
+ """Set the boot device for the task's node.
+
+ Set the boot device to use on next reboot of the node.
+
+ :param task: a task from TaskManager.
+ :param device: the boot device, one of
+ :mod:`ironic.common.boot_devices`.
+ :param persistent: Boolean value. True if the boot device will
+ persist to all future boots, False if not.
+ Default: False.
+ :raises: InvalidParameterValue if an invalid boot device is
+ specified or if required ipmi parameters are missing.
+ :raises: IPMIFailure on an error from pyghmi.
+
"""
- if device not in ipmi_command.boot_devices:
+ if device not in self.get_supported_boot_devices():
raise exception.InvalidParameterValue(_(
"Invalid boot device %s specified.") % device)
driver_info = _parse_driver_info(task.node)
@@ -299,45 +339,52 @@ class VendorPassthru(base.VendorInterface):
ipmicmd = ipmi_command.Command(bmc=driver_info['address'],
userid=driver_info['username'],
password=driver_info['password'])
- ipmicmd.set_bootdev(device)
+ bootdev = _BOOT_DEVICES_MAP[device]
+ ipmicmd.set_bootdev(bootdev, persist=persistent)
except pyghmi_exception.IpmiException as e:
- LOG.warning(_("IPMI set boot device failed for node %(node_id)s "
- "with the following error: %(error)s")
- % {'node_id': driver_info['uuid'], 'error': str(e)})
- raise exception.IPMIFailure(cmd=str(e))
+ LOG.error(_LE("IPMI set boot device failed for node %(node_id)s "
+ "with the following error: %(error)s"),
+ {'node_id': driver_info['uuid'], 'error': e})
+ raise exception.IPMIFailure(cmd=e)
- def get_properties(self):
- return COMMON_PROPERTIES
+ def get_boot_device(self, task):
+ """Get the current boot device for the task's node.
- def validate(self, task, **kwargs):
- """Validate vendor-specific actions.
- :param task: a TaskManager instance.
- :param kwargs: the keyword arguments supplied
+ Returns the current boot device of the node.
- :raises: InvalidParameterValue if an invalid boot device is specified,
- required ipmi credentials are missing or an invalid method
- is requested to the driver.
- """
- method = kwargs['method']
- if method == 'set_boot_device':
- device = kwargs.get('device')
- if device not in ipmi_command.boot_devices:
- raise exception.InvalidParameterValue(_(
- "Invalid boot device %s specified.") % device)
- else:
- raise exception.InvalidParameterValue(_(
- "Unsupported method (%s) passed to IPMINative driver.")
- % method)
- _parse_driver_info(task.node)
+ :param task: a task from TaskManager.
+ :raises: InvalidParameterValue if required IPMI parameters
+ are missing.
+ :raises: IPMIFailure on an error from pyghmi.
+ :returns: a dictionary containing:
+
+ :boot_device: the boot device, one of
+ :mod:`ironic.common.boot_devices` or None if it is unknown.
+ :persistent: Whether the boot device will persist to all
+ future boots or not, None if it is unknown.
- def vendor_passthru(self, task, **kwargs):
- """Receive requests for vendor-specific actions.
- :param task: a TaskManager instance.
- :param kwargs: the keyword arguments supplied
"""
- method = kwargs['method']
- if method == 'set_boot_device':
- return self._set_boot_device(
- task,
- kwargs.get('device'),
- kwargs.get('persistent', False))
+ driver_info = _parse_driver_info(task.node)
+ response = {'boot_device': None, 'persistent': None}
+ try:
+ ipmicmd = ipmi_command.Command(bmc=driver_info['address'],
+ userid=driver_info['username'],
+ password=driver_info['password'])
+ ret = ipmicmd.get_bootdev()
+ # FIXME(lucasagomes): pyghmi doesn't seem to handle errors
+ # consistently, for some errors it raises an exception
+ # others it just returns a dictionary with the error.
+ if 'error' in ret:
+ raise pyghmi_exception.IpmiException(ret['error'])
+ except pyghmi_exception.IpmiException as e:
+ LOG.error(_LE("IPMI get boot device failed for node %(node_id)s "
+ "with the following error: %(error)s"),
+ {'node_id': driver_info['uuid'], 'error': e})
+ raise exception.IPMIFailure(cmd=e)
+
+ bootdev = ret.get('bootdev')
+ if bootdev:
+ response['boot_device'] = next((dev for dev, hdev in
+ _BOOT_DEVICES_MAP.items()
+ if hdev == bootdev), None)
+ return response
diff --git a/ironic/drivers/pxe.py b/ironic/drivers/pxe.py
index 5c7ed3ec3..f0c95d878 100644
--- a/ironic/drivers/pxe.py
+++ b/ironic/drivers/pxe.py
@@ -81,11 +81,8 @@ class PXEAndIPMINativeDriver(base.BaseDriver):
reason=_("Unable to import pyghmi library"))
self.power = ipminative.NativeIPMIPower()
self.deploy = pxe.PXEDeploy()
- self.pxe_vendor = pxe.VendorPassthru()
- self.ipmi_vendor = ipminative.VendorPassthru()
- self.mapping = {'pass_deploy_info': self.pxe_vendor,
- 'set_boot_device': self.ipmi_vendor}
- self.vendor = utils.MixinVendorInterface(self.mapping)
+ self.management = ipminative.NativeIPMIManagement()
+ self.vendor = pxe.VendorPassthru()
class PXEAndSeaMicroDriver(base.BaseDriver):
diff --git a/ironic/tests/drivers/test_ipminative.py b/ironic/tests/drivers/test_ipminative.py
index d0649513c..03b60093e 100644
--- a/ironic/tests/drivers/test_ipminative.py
+++ b/ironic/tests/drivers/test_ipminative.py
@@ -22,10 +22,13 @@ Test class for Native IPMI power driver module.
import mock
from oslo.config import cfg
+from pyghmi import exceptions as pyghmi_exception
+from ironic.common import boot_devices
from ironic.common import driver_factory
from ironic.common import exception
from ironic.common import states
+from ironic.common import utils
from ironic.conductor import task_manager
from ironic.db import api as db_api
from ironic.drivers.modules import ipminative
@@ -146,6 +149,7 @@ class IPMINativeDriverTestCase(db_base.DbTestCase):
def test_get_properties(self):
expected = ipminative.COMMON_PROPERTIES
self.assertEqual(expected, self.driver.get_properties())
+ self.assertEqual(expected, self.driver.management.get_properties())
@mock.patch('pyghmi.ipmi.command.Command')
def test_get_power_state(self, ipmi_mock):
@@ -214,13 +218,14 @@ class IPMINativeDriverTestCase(db_base.DbTestCase):
with task_manager.acquire(self.context,
self.node.uuid) as task:
- self.driver.vendor._set_boot_device(task, 'pxe')
- ipmicmd.set_bootdev.assert_called_once_with('pxe')
+ self.driver.management.set_boot_device(task, boot_devices.PXE)
+ # PXE is converted to 'net' internally by ipminative
+ ipmicmd.set_bootdev.assert_called_once_with('net', persist=False)
def test_set_boot_device_bad_device(self):
with task_manager.acquire(self.context, self.node.uuid) as task:
self.assertRaises(exception.InvalidParameterValue,
- self.driver.vendor._set_boot_device,
+ self.driver.management.set_boot_device,
task,
'fake-device')
@@ -246,40 +251,55 @@ class IPMINativeDriverTestCase(db_base.DbTestCase):
task)
ipmicmd.set_power.assert_called_once_with('boot', 500)
- def test_vendor_passthru_validate__set_boot_device_good(self):
- with task_manager.acquire(self.context,
- self.node['uuid']) as task:
- self.driver.vendor.validate(task,
- method='set_boot_device',
- device='pxe')
+ def test_management_interface_get_supported_boot_devices(self):
+ with task_manager.acquire(self.context, self.node.uuid) as task:
+ expected = [boot_devices.PXE, boot_devices.DISK,
+ boot_devices.CDROM, boot_devices.BIOS]
+ self.assertEqual(sorted(expected), sorted(task.driver.management.
+ get_supported_boot_devices()))
- def test_vendor_passthru_val__set_boot_device_fail_unknown_device(self):
- with task_manager.acquire(self.context,
- self.node['uuid']) as task:
- self.assertRaises(exception.InvalidParameterValue,
- self.driver.vendor.validate,
- task, method='set_boot_device',
- device='non-existent')
+ @mock.patch('pyghmi.ipmi.command.Command')
+ def test_management_interface_get_boot_device_good(self, ipmi_mock):
+ ipmicmd = ipmi_mock.return_value
+ ipmicmd.get_bootdev.return_value = {'bootdev': 'hd'}
+ with task_manager.acquire(self.context, self.node.uuid) as task:
+ bootdev = self.driver.management.get_boot_device(task)
+ self.assertEqual(boot_devices.DISK, bootdev['boot_device'])
- def test_vendor_passthru_val__set_boot_device_fail_missed_device_arg(self):
- with task_manager.acquire(self.context,
- self.node['uuid']) as task:
- self.assertRaises(exception.InvalidParameterValue,
- self.driver.vendor.validate,
- task, method='set_boot_device')
+ @mock.patch('pyghmi.ipmi.command.Command')
+ def test_management_interface_get_boot_device_fail(self, ipmi_mock):
+ ipmicmd = ipmi_mock.return_value
+ ipmicmd.get_bootdev.side_effect = pyghmi_exception.IpmiException
+ with task_manager.acquire(self.context, self.node.uuid) as task:
+ self.assertRaises(exception.IPMIFailure,
+ self.driver.management.get_boot_device, task)
- def test_vendor_passthru_validate_method_notmatch(self):
- with task_manager.acquire(self.context,
- self.node['uuid']) as task:
+ @mock.patch('pyghmi.ipmi.command.Command')
+ def test_management_interface_get_boot_device_fail_dict(self, ipmi_mock):
+ ipmicmd = ipmi_mock.return_value
+ ipmicmd.get_bootdev.return_value = {'error': 'boooom'}
+ with task_manager.acquire(self.context, self.node.uuid) as task:
+ self.assertRaises(exception.IPMIFailure,
+ self.driver.management.get_boot_device, task)
+
+ @mock.patch('pyghmi.ipmi.command.Command')
+ def test_management_interface_get_boot_device_unknown(self, ipmi_mock):
+ ipmicmd = ipmi_mock.return_value
+ ipmicmd.get_bootdev.return_value = {'bootdev': 'unknown'}
+ with task_manager.acquire(self.context, self.node.uuid) as task:
+ expected = {'boot_device': None, 'persistent': None}
+ self.assertEqual(expected,
+ self.driver.management.get_boot_device(task))
+
+ def test_management_interface_validate_good(self):
+ with task_manager.acquire(self.context, self.node.uuid) as task:
+ task.driver.management.validate(task)
+
+ def test_management_interface_validate_fail(self):
+ # Missing IPMI driver_info information
+ node = obj_utils.create_test_node(self.context, id=2,
+ uuid=utils.generate_uuid(),
+ driver='fake_ipminative')
+ with task_manager.acquire(self.context, node.uuid) as task:
self.assertRaises(exception.InvalidParameterValue,
- self.driver.vendor.validate,
- task, method='non-existent-method')
-
- @mock.patch.object(ipminative.VendorPassthru, '_set_boot_device')
- def test_vendor_passthru_call__set_boot_device(self, boot_mock):
- with task_manager.acquire(self.context, self.node.uuid,
- shared=False) as task:
- self.driver.vendor.vendor_passthru(task,
- method='set_boot_device',
- device='pxe')
- boot_mock.assert_called_once_with(task, 'pxe', False)
+ task.driver.management.validate, task)