diff options
Diffstat (limited to 'ironic/drivers/modules/ipminative.py')
-rw-r--r-- | ironic/drivers/modules/ipminative.py | 294 |
1 files changed, 294 insertions, 0 deletions
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)) |