summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlinggao <linggao@us.ibm.com>2013-09-17 19:45:35 +0000
committerlinggao <linggao@us.ibm.com>2013-09-25 20:43:44 +0000
commit2089d3c01b4168e07d303e14da9a05608e921f28 (patch)
tree397536d13fd9c3a4da6cf48cf52d47e08e067d57
parent98670162c74c245cb671ca53c934f0533b3d570a (diff)
downloadironic-2089d3c01b4168e07d303e14da9a05608e921f28.tar.gz
Add native ipmi driver
Implemented a power driver for baremetal node that uses the native python ipmi driver called pyghmi. Change-Id: I41954ebba7c8fa2873a7f1a1f73a4511b0afa301 Implements: blueprint native-ipmi
-rw-r--r--ironic/common/exception.py2
-rw-r--r--ironic/drivers/fake.py10
-rw-r--r--ironic/drivers/modules/ipminative.py294
-rw-r--r--ironic/drivers/pxe.py19
-rw-r--r--ironic/tests/drivers/test_ipminative.py274
-rw-r--r--requirements.txt1
-rw-r--r--setup.cfg2
7 files changed, 601 insertions, 1 deletions
diff --git a/ironic/common/exception.py b/ironic/common/exception.py
index 10b74fe33..1ea8d327d 100644
--- a/ironic/common/exception.py
+++ b/ironic/common/exception.py
@@ -307,7 +307,7 @@ class ChassisNotEmpty(IronicException):
class IPMIFailure(IronicException):
- message = _("IPMI command failed: %(cmd)s.")
+ message = _("IPMI call failed: %(cmd)s.")
class SSHConnectFailed(IronicException):
diff --git a/ironic/drivers/fake.py b/ironic/drivers/fake.py
index 3479d2eb7..73b0c260c 100644
--- a/ironic/drivers/fake.py
+++ b/ironic/drivers/fake.py
@@ -20,6 +20,7 @@ Fake drivers used in testing.
from ironic.drivers import base
from ironic.drivers.modules import fake
+from ironic.drivers.modules import ipminative
from ironic.drivers.modules import ipmitool
from ironic.drivers.modules import pxe
from ironic.drivers.modules import ssh
@@ -58,3 +59,12 @@ class FakeSSHDriver(base.BaseDriver):
def __init__(self):
self.power = ssh.SSHPower()
self.deploy = fake.FakeDeploy()
+
+
+class FakeIPMINativeDriver(base.BaseDriver):
+ """Example implementation of a Driver."""
+
+ def __init__(self):
+ self.power = ipminative.NativeIPMIPower()
+ self.deploy = fake.FakeDeploy()
+ self.vendor = self.power
diff --git a/ironic/drivers/modules/ipminative.py b/ironic/drivers/modules/ipminative.py
new file mode 100644
index 000000000..ec1170e39
--- /dev/null
+++ b/ironic/drivers/modules/ipminative.py
@@ -0,0 +1,294 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# coding=utf-8
+
+# Copyright 2013 International Business Machines Corporation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""
+Ironic Native IPMI power manager.
+"""
+
+from oslo.config import cfg
+
+from ironic.common import exception
+from ironic.common import states
+from ironic.conductor import task_manager
+from ironic.drivers import base
+from ironic.openstack.common import log as logging
+from pyghmi import exceptions as pyghmi_exception
+from pyghmi.ipmi import command as ipmi_command
+
+opts = [
+ cfg.IntOpt('native_ipmi_waiting_time',
+ default=300,
+ help='Waiting time for a native ipmi command in seconds'),
+ ]
+
+CONF = cfg.CONF
+CONF.register_opts(opts)
+
+LOG = logging.getLogger(__name__)
+
+
+def _parse_driver_info(node):
+ """Gets the bmc access info for the given node.
+ :raises: InvalidParameterValue when required ipmi credentials
+ are missing.
+ """
+
+ info = node.get('driver_info', '')
+ ipmi_info = info.get('ipmi')
+ bmc_info = {}
+ bmc_info['address'] = ipmi_info.get('address')
+ bmc_info['username'] = ipmi_info.get('username')
+ bmc_info['password'] = ipmi_info.get('password')
+
+ # address, username and password must be present
+ missing_info = [key for key in bmc_info if not bmc_info[key]]
+ if missing_info:
+ raise exception.InvalidParameterValue(_(
+ "The following IPMI credentials are not supplied"
+ " to IPMI driver: %s."
+ ) % missing_info)
+
+ # get additonal info
+ bmc_info['uuid'] = node.get('uuid')
+
+ return bmc_info
+
+
+def _power_on(driver_info):
+ """Turn the power on for this node.
+
+ :param driver_info: the bmc access info for a node.
+ :returns: power state POWER_ON, one of :class:`ironic.common.states`.
+ :raises: IPMIFailure when the native ipmi call fails.
+ :raises: PowerStateFailure when invalid power state is returned
+ from ipmi.
+ """
+
+ msg = _("IPMI power on failed for node %(node_id)s with the "
+ "following error: %(error)s")
+ try:
+ ipmicmd = ipmi_command.Command(bmc=driver_info['address'],
+ userid=driver_info['username'],
+ password=driver_info['password'])
+ wait = CONF.native_ipmi_waiting_time
+ ret = ipmicmd.set_power('on', wait)
+ except pyghmi_exception.IpmiException as e:
+ LOG.warning(msg % {'node_id': driver_info['uuid'], 'error': str(e)})
+ raise exception.IPMIFailure(cmd=str(e))
+
+ state = ret.get('powerstate')
+ if state == 'on':
+ return states.POWER_ON
+ else:
+ LOG.warning(msg % {'node_id': driver_info['uuid'], 'error': ret})
+ raise exception.PowerStateFailure(pstate=state)
+
+
+def _power_off(driver_info):
+ """Turn the power off for this node.
+
+ :param driver_info: the bmc access info for a node.
+ :returns: power state POWER_OFF, one of :class:`ironic.common.states`.
+ :raises: IPMIFailure when the native ipmi call fails.
+ :raises: PowerStateFailure when invalid power state is returned
+ from ipmi.
+ """
+
+ msg = _("IPMI power off failed for node %(node_id)s with the "
+ "following error: %(error)s")
+ try:
+ ipmicmd = ipmi_command.Command(bmc=driver_info['address'],
+ userid=driver_info['username'],
+ password=driver_info['password'])
+ wait = CONF.native_ipmi_waiting_time
+ ret = ipmicmd.set_power('off', wait)
+ except pyghmi_exception.IpmiException as e:
+ LOG.warning(msg % {'node_id': driver_info['uuid'], 'error': str(e)})
+ raise exception.IPMIFailure(cmd=str(e))
+
+ state = ret.get('powerstate')
+ if state == 'off':
+ return states.POWER_OFF
+ else:
+ LOG.warning(msg % {'node_id': driver_info['uuid'], 'error': ret})
+ raise exception.PowerStateFailure(pstate=state)
+
+
+def _reboot(driver_info):
+ """Reboot this node.
+
+ If the power is off, turn it on. If the power is on, reset it.
+
+ :param driver_info: the bmc access info for a node.
+ :returns: power state POWER_ON, one of :class:`ironic.common.states`.
+ :raises: IPMIFailure when the native ipmi call fails.
+ :raises: PowerStateFailure when invalid power state is returned
+ from ipmi.
+ """
+
+ msg = _("IPMI power reboot failed for node %(node_id)s with the "
+ "following error: %(error)s")
+ try:
+ ipmicmd = ipmi_command.Command(bmc=driver_info['address'],
+ userid=driver_info['username'],
+ password=driver_info['password'])
+ wait = CONF.native_ipmi_waiting_time
+ ret = ipmicmd.set_power('boot', wait)
+ except pyghmi_exception.IpmiException as e:
+ LOG.warning(msg % {'node_id': driver_info['uuid'], 'error': str(e)})
+ raise exception.IPMIFailure(cmd=str(e))
+
+ state = ret.get('powerstate')
+ if state == 'on':
+ return states.POWER_ON
+ else:
+ LOG.warning(msg % {'node_id': driver_info['uuid'], 'error': ret})
+ raise exception.PowerStateFailure(pstate=state)
+
+
+def _power_status(driver_info):
+ """Get the power status for this node.
+
+ :param driver_info: the bmc access info for a node.
+ :returns: power state POWER_ON, POWER_OFF or ERROR defined in
+ :class:`ironic.common.states`.
+ :raises: IPMIFailure when the native ipmi call fails.
+ """
+
+ try:
+ ipmicmd = ipmi_command.Command(bmc=driver_info['address'],
+ userid=driver_info['username'],
+ password=driver_info['password'])
+ ret = ipmicmd.get_power()
+ except pyghmi_exception.IpmiException as e:
+ LOG.warning(_("IPMI get power state 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))
+
+ state = ret.get('powerstate')
+ if state == 'on':
+ return states.POWER_ON
+ elif state == 'off':
+ return states.POWER_OFF
+ else:
+ # NOTE(linggao): Do not throw an exception here because it might
+ # return other valid values. It is up to the caller to decide
+ # what to do.
+ LOG.warning(_("IPMI get power state for node %(node_id)s returns the "
+ "following details: %(detail)s")
+ % {'node_id': driver_info['uuid'], 'detail': ret})
+ return states.ERROR
+
+
+class NativeIPMIPower(base.PowerInterface):
+ """The power driver using native python-ipmi library."""
+
+ def validate(self, node):
+ """Check that node['driver_info'] contains IPMI credentials.
+
+ :param node: a single node to validate.
+ :raises: InvalidParameterValue when required ipmi credentials
+ are missing.
+ """
+ _parse_driver_info(node)
+
+ def get_power_state(self, task, node):
+ """Get the current power state.
+
+ :param task: a TaskManager instance.
+ :param node: the node info.
+ :returns: power state POWER_ON, POWER_OFF or ERROR defined in
+ :class:`ironic.common.states`.
+ :raises: InvalidParameterValue when required ipmi credentials
+ are missing.
+ :raises: IPMIFailure when the native ipmi call fails.
+ """
+ driver_info = _parse_driver_info(node)
+ return _power_status(driver_info)
+
+ @task_manager.require_exclusive_lock
+ def set_power_state(self, task, node, pstate):
+ """Turn the power on or off.
+
+ :param task: a TaskManager instance.
+ :param node: the node info.
+ :param pstate: a power state that will be set on the given node.
+ :raises: IPMIFailure when the native ipmi call fails.
+ :raises: InvalidParameterValue when an invalid power state
+ is specified or required ipmi credentials are missing.
+ :raises: PowerStateFailure when invalid power state is returned
+ from ipmi.
+ """
+
+ driver_info = _parse_driver_info(node)
+
+ if pstate == states.POWER_ON:
+ _power_on(driver_info)
+ elif pstate == states.POWER_OFF:
+ _power_off(driver_info)
+ else:
+ raise exception.InvalidParameterValue(_(
+ "set_power_state called with an invalid power state: %s."
+ ) % pstate)
+
+ @task_manager.require_exclusive_lock
+ def reboot(self, task, node):
+ """Cycles the power to a node.
+
+ :param task: a TaskManager instance.
+ :param node: the node info.
+ :raises: IPMIFailure when the native ipmi call fails.
+ :raises: InvalidParameterValue when required ipmi credentials
+ are missing.
+ :raises: PowerStateFailure when invalid power state is returned
+ from ipmi.
+ """
+
+ driver_info = _parse_driver_info(node)
+ _reboot(driver_info)
+
+ @task_manager.require_exclusive_lock
+ def _set_boot_device(self, task, node, device, persistent=False):
+ """Set the boot device for a node.
+
+ :param task: a TaskManager instance.
+ :param node: The Node.
+ :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.
+ """
+
+ if device not in ipmi_command.boot_devices:
+ raise exception.InvalidParameterValue(_(
+ "Invalid boot device %s specified.") % device)
+ driver_info = _parse_driver_info(node)
+ try:
+ ipmicmd = ipmi_command.Command(bmc=driver_info['address'],
+ userid=driver_info['username'],
+ password=driver_info['password'])
+ ipmicmd.set_bootdev(device)
+ 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))
diff --git a/ironic/drivers/pxe.py b/ironic/drivers/pxe.py
index 5b2b45a2d..262d085bf 100644
--- a/ironic/drivers/pxe.py
+++ b/ironic/drivers/pxe.py
@@ -19,6 +19,7 @@ PXE Driver and supporting meta-classes.
"""
from ironic.drivers import base
+from ironic.drivers.modules import ipminative
from ironic.drivers.modules import ipmitool
from ironic.drivers.modules import pxe
from ironic.drivers.modules import ssh
@@ -57,3 +58,21 @@ class PXEAndSSHDriver(base.BaseDriver):
self.deploy = pxe.PXEDeploy()
self.rescue = self.deploy
self.vendor = None
+
+
+class PXEAndIPMINativeDriver(base.BaseDriver):
+ """PXE + Native IPMI driver.
+
+ This driver implements the `core` functionality, combining
+ :class:ironic.drivers.modules.ipminative.NativeIPMIPower for power
+ on/off and reboot with
+ :class:ironic.driver.modules.pxe.PXE for image deployment.
+ Implementations are in those respective classes;
+ this class is merely the glue between them.
+ """
+
+ def __init__(self):
+ self.power = ipminative.NativeIPMIPower()
+ self.deploy = pxe.PXEDeploy()
+ self.rescue = self.deploy
+ self.vendor = pxe.IPMIVendorPassthru()
diff --git a/ironic/tests/drivers/test_ipminative.py b/ironic/tests/drivers/test_ipminative.py
new file mode 100644
index 000000000..31388ed7b
--- /dev/null
+++ b/ironic/tests/drivers/test_ipminative.py
@@ -0,0 +1,274 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# coding=utf-8
+
+# Copyright 2013 International Business Machines Corporation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""
+Test class for Native IPMI power driver module.
+"""
+
+from ironic.common import exception
+from ironic.common import states
+from ironic.conductor import task_manager
+from ironic.db import api as db_api
+from ironic.drivers.modules import ipminative
+from ironic.tests import base
+from ironic.tests.conductor import utils as mgr_utils
+from ironic.tests.db import base as db_base
+from ironic.tests.db import utils as db_utils
+from oslo.config import cfg
+from pyghmi.ipmi import command as ipmi_command
+
+
+CONF = cfg.CONF
+
+
+class IPMINativePrivateMethodTestCase(base.TestCase):
+ """Test cases for ipminative private methods."""
+
+ def setUp(self):
+ super(IPMINativePrivateMethodTestCase, self).setUp()
+ n = db_utils.get_test_node(
+ driver='fake_ipminative',
+ driver_info=db_utils.ipmi_info)
+ self.dbapi = db_api.get_instance()
+ self.node = self.dbapi.create_node(n)
+ self.info = ipminative._parse_driver_info(self.node)
+ self.mox.StubOutWithMock(ipmi_command.Command, '__init__')
+ ipmi_command.Command.__init__(bmc=self.info.get('address'),
+ userid=self.info.get('username'),
+ password=self.info.get('password')).AndReturn(None)
+
+ def test__parse_driver_info(self):
+ # make sure we get back the expected things
+ self.assertIsNotNone(self.info.get('address'))
+ self.assertIsNotNone(self.info.get('username'))
+ self.assertIsNotNone(self.info.get('password'))
+ self.assertIsNotNone(self.info.get('uuid'))
+
+ self.mox.ReplayAll()
+ ipmi_command.Command(bmc=self.info.get('address'),
+ userid=self.info.get('username'),
+ password=self.info.get('password'))
+ self.mox.VerifyAll()
+
+ # make sure error is raised when info, eg. username, is missing
+ _driver_info = {
+ 'ipmi': {
+ "address": "2.2.3.4",
+ "password": "fake",
+ }
+ }
+ node = db_utils.get_test_node(driver_info=_driver_info)
+ self.assertRaises(exception.InvalidParameterValue,
+ ipminative._parse_driver_info,
+ node)
+
+ def test__power_status_on(self):
+ self.mox.StubOutWithMock(ipmi_command.Command, 'get_power')
+ ipmi_command.Command.get_power().AndReturn({'powerstate': 'on'})
+ self.mox.ReplayAll()
+
+ state = ipminative._power_status(self.info)
+ self.mox.VerifyAll()
+ self.assertEqual(state, states.POWER_ON)
+
+ def test__power_status_off(self):
+ self.mox.StubOutWithMock(ipmi_command.Command, 'get_power')
+ ipmi_command.Command.get_power().AndReturn({'powerstate': 'off'})
+ self.mox.ReplayAll()
+
+ state = ipminative._power_status(self.info)
+ self.mox.VerifyAll()
+ self.assertEqual(state, states.POWER_OFF)
+
+ def test__power_status_error(self):
+ self.mox.StubOutWithMock(ipmi_command.Command, 'get_power')
+ ipmi_command.Command.get_power().AndReturn({'powerstate': 'Error'})
+ self.mox.ReplayAll()
+
+ state = ipminative._power_status(self.info)
+ self.mox.VerifyAll()
+ self.assertEqual(state, states.ERROR)
+
+ def test__power_on(self):
+ self.mox.StubOutWithMock(ipmi_command.Command, 'set_power')
+ ipmi_command.Command.set_power('on', 300).AndReturn(
+ {'powerstate': 'on'})
+ self.mox.ReplayAll()
+
+ state = ipminative._power_on(self.info)
+ self.mox.VerifyAll()
+ self.assertEqual(state, states.POWER_ON)
+
+ def test__power_off(self):
+ self.mox.StubOutWithMock(ipmi_command.Command, 'set_power')
+ ipmi_command.Command.set_power('off', 300).AndReturn(
+ {'powerstate': 'off'})
+ self.mox.ReplayAll()
+
+ state = ipminative._power_off(self.info)
+ self.mox.VerifyAll()
+ self.assertEqual(state, states.POWER_OFF)
+
+ def test__reboot(self):
+ self.mox.StubOutWithMock(ipmi_command.Command, 'set_power')
+ ipmi_command.Command.set_power('boot', 300).AndReturn(
+ {'powerstate': 'on'})
+ self.mox.ReplayAll()
+
+ state = ipminative._reboot(self.info)
+ self.mox.VerifyAll()
+ self.assertEqual(state, states.POWER_ON)
+
+
+class IPMINativeDriverTestCase(db_base.DbTestCase):
+ """Test cases for ipminative.NativeIPMIPower class functions.
+ """
+
+ def setUp(self):
+ super(IPMINativeDriverTestCase, self).setUp()
+ self.dbapi = db_api.get_instance()
+ self.driver = mgr_utils.get_mocked_node_manager(
+ driver='fake_ipminative')
+
+ n = db_utils.get_test_node(
+ driver='fake_ipminative',
+ driver_info=db_utils.ipmi_info)
+ self.dbapi = db_api.get_instance()
+ self.node = self.dbapi.create_node(n)
+ self.info = ipminative._parse_driver_info(self.node)
+
+ def test_get_power_state(self):
+
+ self.mox.StubOutWithMock(ipmi_command.Command, 'get_power')
+ self.mox.StubOutWithMock(ipmi_command.Command, '__init__')
+ ipmi_command.Command.__init__(bmc=self.info.get('address'),
+ userid=self.info.get('username'),
+ password=self.info.get('password')).AndReturn(None)
+ ipmi_command.Command.get_power().AndReturn({'powerstate': 'off'})
+ ipmi_command.Command.__init__(bmc=self.info.get('address'),
+ userid=self.info.get('username'),
+ password=self.info.get('password')).AndReturn(None)
+ ipmi_command.Command.get_power().AndReturn({'powerstate': 'on'})
+ ipmi_command.Command.__init__(bmc=self.info.get('address'),
+ userid=self.info.get('username'),
+ password=self.info.get('password')).AndReturn(None)
+ ipmi_command.Command.get_power().AndReturn({'powerstate': 'error'})
+ self.mox.ReplayAll()
+
+ pstate = self.driver.power.get_power_state(None, self.node)
+ self.assertEqual(pstate, states.POWER_OFF)
+
+ pstate = self.driver.power.get_power_state(None, self.node)
+ self.assertEqual(pstate, states.POWER_ON)
+
+ pstate = self.driver.power.get_power_state(None, self.node)
+ self.assertEqual(pstate, states.ERROR)
+
+ self.mox.VerifyAll()
+
+ def test_set_power_on_ok(self):
+ self.mox.StubOutWithMock(ipminative, '_power_on')
+ self.mox.StubOutWithMock(ipminative, '_power_off')
+
+ ipminative._power_on(self.info).AndReturn(states.POWER_ON)
+ self.mox.ReplayAll()
+
+ with task_manager.acquire([self.node['uuid']]) as task:
+ self.driver.power.set_power_state(
+ task, self.node, states.POWER_ON)
+ self.mox.VerifyAll()
+
+ def test_set_power_off_ok(self):
+ self.mox.StubOutWithMock(ipminative, '_power_on')
+ self.mox.StubOutWithMock(ipminative, '_power_off')
+
+ ipminative._power_off(self.info).AndReturn(states.POWER_OFF)
+ self.mox.ReplayAll()
+
+ with task_manager.acquire([self.node['uuid']]) as task:
+ self.driver.power.set_power_state(
+ task, self.node, states.POWER_OFF)
+ self.mox.VerifyAll()
+
+ def test_set_power_on_fail(self):
+ self.mox.StubOutWithMock(ipmi_command.Command, 'set_power')
+ self.mox.StubOutWithMock(ipmi_command.Command, '__init__')
+ ipmi_command.Command.__init__(bmc=self.info.get('address'),
+ userid=self.info.get('username'),
+ password=self.info.get('password')).AndReturn(None)
+ ipmi_command.Command.set_power('on', 300).AndReturn(
+ {'powerstate': 'error'})
+ self.mox.ReplayAll()
+
+ with task_manager.acquire([self.node['uuid']]) as task:
+ self.assertRaises(exception.PowerStateFailure,
+ self.driver.power.set_power_state,
+ task,
+ self.node,
+ states.POWER_ON)
+ self.mox.VerifyAll()
+
+ def test_set_boot_device_ok(self):
+ self.mox.StubOutWithMock(ipmi_command.Command, 'set_bootdev')
+ self.mox.StubOutWithMock(ipmi_command.Command, '__init__')
+ ipmi_command.Command.__init__(bmc=self.info.get('address'),
+ userid=self.info.get('username'),
+ password=self.info.get('password')).AndReturn(None)
+ ipmi_command.Command.set_bootdev('pxe').AndReturn(None)
+ self.mox.ReplayAll()
+
+ with task_manager.acquire([self.node['uuid']]) as task:
+ self.driver.power._set_boot_device(task, self.node, 'pxe')
+ self.mox.VerifyAll()
+
+ def test_set_boot_device_bad_device(self):
+ with task_manager.acquire([self.node['uuid']]) as task:
+ self.assertRaises(exception.InvalidParameterValue,
+ self.driver.power._set_boot_device,
+ task,
+ self.node,
+ 'fake-device')
+
+ def test_reboot_ok(self):
+ self.mox.StubOutWithMock(ipminative, '_reboot')
+
+ ipminative._reboot(self.info).AndReturn(None)
+ self.mox.ReplayAll()
+
+ with task_manager.acquire([self.node['uuid']]) as task:
+ self.driver.power.reboot(task, self.node)
+
+ self.mox.VerifyAll()
+
+ def test_reboot_fail(self):
+ self.mox.StubOutWithMock(ipmi_command.Command, 'set_power')
+ self.mox.StubOutWithMock(ipmi_command.Command, '__init__')
+ ipmi_command.Command.__init__(bmc=self.info.get('address'),
+ userid=self.info.get('username'),
+ password=self.info.get('password')).AndReturn(None)
+ ipmi_command.Command.set_power('boot', 300).AndReturn(
+ {'powerstate': 'error'})
+ self.mox.ReplayAll()
+
+ with task_manager.acquire([self.node['uuid']]) as task:
+ self.assertRaises(exception.PowerStateFailure,
+ self.driver.power.reboot,
+ task,
+ self.node)
+
+ self.mox.VerifyAll()
diff --git a/requirements.txt b/requirements.txt
index d72aaa035..2c4c1bed6 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -24,3 +24,4 @@ six<1.4.0
jsonpatch>=1.1
WSME>=0.5b2
Cheetah>=2.4.4
+pyghmi
diff --git a/setup.cfg b/setup.cfg
index 268b7e5fd..2d9905015 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -36,9 +36,11 @@ console_scripts =
ironic.drivers =
fake = ironic.drivers.fake:FakeDriver
fake_ipmitool = ironic.drivers.fake:FakeIPMIToolDriver
+ fake_ipminative = ironic.drivers.fake:FakeIPMINativeDriver
fake_ssh = ironic.drivers.fake:FakeSSHDriver
fake_pxe = ironic.drivers.fake:FakePXEDriver
pxe_ipmitool = ironic.drivers.pxe:PXEAndIPMIToolDriver
+ pxe_ipminative = ironic.drivers.pxe:PXEAndIPMINativeDriver
pxe_ssh = ironic.drivers.pxe:PXEAndSSHDriver
[pbr]