summaryrefslogtreecommitdiff
path: root/ironic/drivers/ipmi.py
diff options
context:
space:
mode:
authorDevananda van der Veen <devananda.vdv@gmail.com>2013-06-04 12:19:18 -0700
committerDevananda van der Veen <devananda.vdv@gmail.com>2013-06-11 17:12:21 -0700
commit59d5bea14ac752e4d3e70f17f926584dc1ab9257 (patch)
tree912e55ba32d3a82b8dc40bfe6db0226766ced3fb /ironic/drivers/ipmi.py
parent9d80a7171b5da680421da5b2e73f20bc86201b57 (diff)
downloadironic-59d5bea14ac752e4d3e70f17f926584dc1ab9257.tar.gz
Restructuring driver API and inheritance.
Based on discussions during and after the Ironic team meeting on June 03, regarding support for substantially different driver work flows, this is a re-working of the internal driver API. tl;dr: The strict separation of "control" and "deploy" driver was an artefact of the ipmi + pxe implementation used in nova-baremetal, and does not map on to all drivers. Furthermore, the prior implementation did not accurately represent the separation of "core", "standard", and "vendor-specific" driver functionality. These changes impact the v1 API structure, but since that is largely not implemented yet, this change does not attempt to affect the public API itself. Highlights: - No more deploy + control driver; nodes have one and only one driver. This drops the deploy_driver and deploy_info parameters, and renames control_driver -> driver, and control_info -> driver_info. - Interfaces for core, standard, and vendor functionality now clearly defined in the driver API. - Improve Fake driver to demonstrate use of interfaces. - Convert IPMI and SSH driver classes into interfaces, and move to drivers/modules/ directory. - Stub for the pxe interfaces. - Stub implementations of pxe+ipmi and pxe+ssh drivers. - driver_info field uses more standard names, but requires driver-specific data to be in a nested object. Examples in tests/db/utils.py as before. A separate doc change will follow this to update the API v1 spec. Also includes some cosmetic cleanup of test_ssh.py and test_ipmi.py. Change-Id: I057ede8e07b1b57010e81ef58415debe0ba8b934
Diffstat (limited to 'ironic/drivers/ipmi.py')
-rw-r--r--ironic/drivers/ipmi.py271
1 files changed, 0 insertions, 271 deletions
diff --git a/ironic/drivers/ipmi.py b/ironic/drivers/ipmi.py
deleted file mode 100644
index c797df2a9..000000000
--- a/ironic/drivers/ipmi.py
+++ /dev/null
@@ -1,271 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-# coding=utf-8
-
-# Copyright 2012 Hewlett-Packard Development Company, L.P.
-# Copyright (c) 2012 NTT DOCOMO, INC.
-# 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.
-
-"""
-Baremetal IPMI power manager.
-"""
-
-import os
-import stat
-import tempfile
-
-from oslo.config import cfg
-
-from ironic.common import exception
-from ironic.common import paths
-from ironic.common import states
-from ironic.common import utils
-from ironic.drivers import base
-from ironic.manager import task_manager
-from ironic.openstack.common import jsonutils as json
-from ironic.openstack.common import log as logging
-from ironic.openstack.common import loopingcall
-
-opts = [
- cfg.StrOpt('terminal',
- default='shellinaboxd',
- help='path to baremetal terminal program'),
- cfg.StrOpt('terminal_cert_dir',
- default=None,
- help='path to baremetal terminal SSL cert(PEM)'),
- cfg.StrOpt('terminal_pid_dir',
- default=paths.state_path_def('baremetal/console'),
- help='path to directory stores pidfiles of baremetal_terminal'),
- cfg.IntOpt('ipmi_power_retry',
- default=5,
- help='Maximum seconds to retry IPMI operations'),
- ]
-
-CONF = cfg.CONF
-CONF.register_opts(opts)
-
-LOG = logging.getLogger(__name__)
-
-VALID_BOOT_DEVICES = ['pxe', 'disk', 'safe', 'cdrom', 'bios']
-
-
-# TODO(deva): use a contextmanager for this, and port it to nova.
-def _make_password_file(password):
- fd, path = tempfile.mkstemp()
- os.fchmod(fd, stat.S_IRUSR | stat.S_IWUSR)
- with os.fdopen(fd, "w") as f:
- f.write(password)
- return path
-
-
-def _parse_control_info(node):
- info = json.loads(node.get('control_info', ''))
- address = info.get('ipmi_address', None)
- user = info.get('ipmi_username', None)
- password = info.get('ipmi_password', None)
- port = info.get('ipmi_terminal_port', None)
-
- if not address or not user or not password:
- raise exception.InvalidParameterValue(_(
- "IPMI credentials not supplied to IPMI driver."))
-
- return {
- 'address': address,
- 'user': user,
- 'password': password,
- 'port': port,
- 'uuid': node.get('uuid')
- }
-
-
-def _exec_ipmitool(c_info, command):
- args = ['ipmitool',
- '-I',
- 'lanplus',
- '-H',
- c_info['address'],
- '-U',
- c_info['user'],
- '-f']
- try:
- pwfile = _make_password_file(c_info['password'])
- args.append(pwfile)
- args.extend(command.split(" "))
- out, err = utils.execute(*args, attempts=3)
- LOG.debug(_("ipmitool stdout: '%(out)s', stderr: '%(err)s'"),
- locals())
- return out, err
- finally:
- utils.delete_if_exists(pwfile)
-
-
-def _power_on(c_info):
- """Turn the power to this node ON."""
-
- # use mutable objects so the looped method can change them
- state = [None]
- retries = [0]
-
- def _wait_for_power_on(state, retries):
- """Called at an interval until the node's power is on."""
-
- state[0] = _power_status(c_info)
- if state[0] == states.POWER_ON:
- raise loopingcall.LoopingCallDone()
-
- if retries[0] > CONF.ipmi_power_retry:
- state[0] = states.ERROR
- raise loopingcall.LoopingCallDone()
- try:
- retries[0] += 1
- _exec_ipmitool(c_info, "power on")
- except Exception:
- # Log failures but keep trying
- LOG.warning(_("IPMI power on failed for node %s.")
- % c_info['uuid'])
-
- timer = loopingcall.FixedIntervalLoopingCall(_wait_for_power_on,
- state, retries)
- timer.start(interval=1).wait()
- return state[0]
-
-
-def _power_off(c_info):
- """Turn the power to this node OFF."""
-
- # use mutable objects so the looped method can change them
- state = [None]
- retries = [0]
-
- def _wait_for_power_off(state, retries):
- """Called at an interval until the node's power is off."""
-
- state[0] = _power_status(c_info)
- if state[0] == states.POWER_OFF:
- raise loopingcall.LoopingCallDone()
-
- if retries[0] > CONF.ipmi_power_retry:
- state[0] = states.ERROR
- raise loopingcall.LoopingCallDone()
- try:
- retries[0] += 1
- _exec_ipmitool(c_info, "power off")
- except Exception:
- # Log failures but keep trying
- LOG.warning(_("IPMI power off failed for node %s.")
- % c_info['uuid'])
-
- timer = loopingcall.FixedIntervalLoopingCall(_wait_for_power_off,
- state=state, retries=retries)
- timer.start(interval=1).wait()
- return state[0]
-
-
-def _power_status(c_info):
- out_err = _exec_ipmitool(c_info, "power status")
- if out_err[0] == "Chassis Power is on\n":
- return states.POWER_ON
- elif out_err[0] == "Chassis Power is off\n":
- return states.POWER_OFF
- else:
- return states.ERROR
-
-
-class IPMIPowerDriver(base.ControlDriver):
- """Generic IPMI Power Driver
-
- This ControlDriver class provides mechanism for controlling the power state
- of physical hardware via IPMI calls. It also provides console access for
- some supported hardware.
-
- NOTE: This driver does not currently support multi-node operations.
- """
-
- def __init__(self):
- pass
-
- def validate_driver_info(self, node):
- """Check that node['control_info'] contains the requisite fields."""
- try:
- _parse_control_info(node)
- except exception.InvalidParameterValue:
- return False
- return True
-
- def get_power_state(self, task, node):
- """Get the current power state."""
- c_info = _parse_control_info(node)
- return _power_status(c_info)
-
- @task_manager.require_exclusive_lock
- def set_power_state(self, task, node, pstate):
- """Turn the power on or off."""
- c_info = _parse_control_info(node)
-
- if pstate == states.POWER_ON:
- state = _power_on(c_info)
- elif pstate == states.POWER_OFF:
- state = _power_off(c_info)
- else:
- raise exception.IronicException(_(
- "set_power_state called with invalid power state."))
-
- if state != pstate:
- raise exception.PowerStateFailure(pstate=pstate)
-
- @task_manager.require_exclusive_lock
- def reboot(self, task, node):
- """Cycles the power to a node."""
- c_info = _parse_control_info(node)
- _power_off(c_info)
- state = _power_on(c_info)
-
- if state != states.POWER_ON:
- raise exception.PowerStateFailure(pstate=states.POWER_ON)
-
- @task_manager.require_exclusive_lock
- def set_boot_device(self, task, node, device, persistent=False):
- """Set the boot device for a node.
-
- :param task: TaskManager context.
- :param node: The Node.
- :param device: Boot device. One of [pxe, disk, cdrom, safe, bios].
- :param persistent: Whether to set next-boot, or make the change
- permanent. Default: False.
- :raises: InvalidParameterValue if an invalid boot device is specified.
- :raises: IPMIFailure on an error from ipmitool.
-
- """
- if device not in VALID_BOOT_DEVICES:
- raise exception.InvalidParameterValue(_(
- "Invalid boot device %s specified.") % device)
- cmd = "chassis bootdev %s" % device
- if persistent:
- cmd = cmd + " options=persistent"
- c_info = _parse_control_info(node)
- try:
- out, err = _exec_ipmitool(c_info, cmd)
- # TODO(deva): validate (out, err) and add unit test for failure
- except Exception:
- raise exception.IPMIFailure(cmd=cmd)
-
- # TODO(deva): port start_console
- def start_console(self, task, node):
- raise exception.IronicException(_(
- "start_console is not supported by IPMIPowerDriver."))
-
- # TODO(deva): port stop_console
- def stop_console(self, task, node):
- raise exception.IronicException(_(
- "stop_console is not supported by IPMIPowerDriver."))