Rename ipmi driver to ipmitool
To make naming more consistent with other ipmi drivers that will be introduced later, the module name for ipmi driver that uses the ipmitool is renamed from ipmi to ipmitool. Change-Id: I8ec0d4beb29a06af7b27d3ff0620fab5446579b1
This commit is contained in:
@@ -1,256 +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.
|
||||
|
||||
"""
|
||||
Ironic IPMI power manager.
|
||||
"""
|
||||
|
||||
import contextlib
|
||||
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.conductor import task_manager
|
||||
from ironic.drivers import base
|
||||
from ironic.openstack.common import excutils
|
||||
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']
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _make_password_file(password):
|
||||
try:
|
||||
fd, path = tempfile.mkstemp()
|
||||
os.fchmod(fd, stat.S_IRUSR | stat.S_IWUSR)
|
||||
with os.fdopen(fd, "w") as f:
|
||||
f.write(password)
|
||||
|
||||
yield path
|
||||
utils.delete_if_exists(path)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
utils.delete_if_exists(path)
|
||||
|
||||
|
||||
def _parse_driver_info(node):
|
||||
driver_info = json.loads(node.get('driver_info', ''))
|
||||
ipmi_info = driver_info.get('ipmi')
|
||||
address = ipmi_info.get('address', None)
|
||||
username = ipmi_info.get('username', None)
|
||||
password = ipmi_info.get('password', None)
|
||||
port = ipmi_info.get('terminal_port', None)
|
||||
|
||||
if not address or not username or not password:
|
||||
raise exception.InvalidParameterValue(_(
|
||||
"IPMI credentials not supplied to IPMI driver."))
|
||||
|
||||
return {
|
||||
'address': address,
|
||||
'username': username,
|
||||
'password': password,
|
||||
'port': port,
|
||||
'uuid': node.get('uuid')
|
||||
}
|
||||
|
||||
|
||||
def _exec_ipmitool(driver_info, command):
|
||||
args = ['ipmitool',
|
||||
'-I',
|
||||
'lanplus',
|
||||
'-H',
|
||||
driver_info['address'],
|
||||
'-U',
|
||||
driver_info['username'],
|
||||
'-f']
|
||||
with _make_password_file(driver_info['password']) as pw_file:
|
||||
args.append(pw_file)
|
||||
args.extend(command.split(" "))
|
||||
out, err = utils.execute(*args, attempts=3)
|
||||
LOG.debug(_("ipmitool stdout: '%(out)s', stderr: '%(err)s'"),
|
||||
locals())
|
||||
return out, err
|
||||
|
||||
|
||||
def _power_on(driver_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(driver_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(driver_info, "power on")
|
||||
except Exception:
|
||||
# Log failures but keep trying
|
||||
LOG.warning(_("IPMI power on failed for node %s.")
|
||||
% driver_info['uuid'])
|
||||
|
||||
timer = loopingcall.FixedIntervalLoopingCall(_wait_for_power_on,
|
||||
state, retries)
|
||||
timer.start(interval=1).wait()
|
||||
return state[0]
|
||||
|
||||
|
||||
def _power_off(driver_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(driver_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(driver_info, "power off")
|
||||
except Exception:
|
||||
# Log failures but keep trying
|
||||
LOG.warning(_("IPMI power off failed for node %s.")
|
||||
% driver_info['uuid'])
|
||||
|
||||
timer = loopingcall.FixedIntervalLoopingCall(_wait_for_power_off,
|
||||
state=state, retries=retries)
|
||||
timer.start(interval=1).wait()
|
||||
return state[0]
|
||||
|
||||
|
||||
def _power_status(driver_info):
|
||||
out_err = _exec_ipmitool(driver_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 IPMIPower(base.PowerInterface):
|
||||
|
||||
def validate(self, node):
|
||||
"""Check that node['driver_info'] contains IPMI credentials.
|
||||
|
||||
:param node: Single node object.
|
||||
:raises: InvalidParameterValue
|
||||
"""
|
||||
_parse_driver_info(node)
|
||||
|
||||
def get_power_state(self, task, node):
|
||||
"""Get the current power state."""
|
||||
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."""
|
||||
driver_info = _parse_driver_info(node)
|
||||
|
||||
if pstate == states.POWER_ON:
|
||||
state = _power_on(driver_info)
|
||||
elif pstate == states.POWER_OFF:
|
||||
state = _power_off(driver_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."""
|
||||
driver_info = _parse_driver_info(node)
|
||||
_power_off(driver_info)
|
||||
state = _power_on(driver_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: a TaskManager instance.
|
||||
: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"
|
||||
driver_info = _parse_driver_info(node)
|
||||
try:
|
||||
out, err = _exec_ipmitool(driver_info, cmd)
|
||||
# TODO(deva): validate (out, err) and add unit test for failure
|
||||
except Exception:
|
||||
raise exception.IPMIFailure(cmd=cmd)
|
||||
@@ -35,10 +35,10 @@ console_scripts =
|
||||
|
||||
ironic.drivers =
|
||||
fake = ironic.drivers.fake:FakeDriver
|
||||
fake_ipmi = ironic.drivers.fake:FakeIPMIDriver
|
||||
fake_ipmitool = ironic.drivers.fake:FakeIPMIToolDriver
|
||||
fake_ssh = ironic.drivers.fake:FakeSSHDriver
|
||||
fake_pxe = ironic.drivers.fake:FakePXEDriver
|
||||
pxe_ipmi = ironic.drivers.pxe:PXEAndIPMIDriver
|
||||
pxe_ipmitool = ironic.drivers.pxe:PXEAndIPMIToolDriver
|
||||
pxe_ssh = ironic.drivers.pxe:PXEAndSSHDriver
|
||||
|
||||
[pbr]
|
||||
|
||||
Reference in New Issue
Block a user