Merge "Remove deprecated, untested ipminative driver"
This commit is contained in:
commit
73c29a4c66
@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
# These are available on pypi
|
# These are available on pypi
|
||||||
proliantutils>=2.2.1
|
proliantutils>=2.2.1
|
||||||
pyghmi>=0.8.0
|
|
||||||
pysnmp
|
pysnmp
|
||||||
python-ironic-inspector-client>=1.5.0
|
python-ironic-inspector-client>=1.5.0
|
||||||
python-oneviewclient<3.0.0,>=2.5.2
|
python-oneviewclient<3.0.0,>=2.5.2
|
||||||
|
@ -475,11 +475,11 @@ controller in your bare metal server by using ``ipmitool``::
|
|||||||
default is fairly conservative, as setting this timeout too low can cause
|
default is fairly conservative, as setting this timeout too low can cause
|
||||||
older BMCs to crash and require a hard-reset.
|
older BMCs to crash and require a hard-reset.
|
||||||
|
|
||||||
Bare Metal service supports sending IPMI sensor data to Telemetry with pxe_ipmitool,
|
Bare Metal service supports sending IPMI sensor data to Telemetry with
|
||||||
pxe_ipminative, agent_ipmitool, agent_pyghmi, agent_ilo, iscsi_ilo, pxe_ilo,
|
pxe_ipmitool, agent_ipmitool, agent_ilo, iscsi_ilo, pxe_ilo, and with pxe_irmc
|
||||||
and with pxe_irmc driver starting from Kilo release. By default, support for
|
driver. By default, support for sending IPMI sensor data to Telemetry is
|
||||||
sending IPMI sensor data to Telemetry is disabled. If you want to enable it,
|
disabled. If you want to enable it, you should make the following two changes
|
||||||
you should make the following two changes in ``ironic.conf``:
|
in ``ironic.conf``:
|
||||||
|
|
||||||
* ``notification_driver = messaging`` in the ``DEFAULT`` section
|
* ``notification_driver = messaging`` in the ``DEFAULT`` section
|
||||||
* ``send_sensor_data = true`` in the ``conductor`` section
|
* ``send_sensor_data = true`` in the ``conductor`` section
|
||||||
|
@ -43,7 +43,7 @@ service via hrefs.
|
|||||||
There are however some limitations for different drivers:
|
There are however some limitations for different drivers:
|
||||||
|
|
||||||
* If you're using one of the drivers that use agent deploy method (namely,
|
* If you're using one of the drivers that use agent deploy method (namely,
|
||||||
``agent_ilo``, ``agent_ipmitool``, ``agent_pyghmi`` or ``agent_ssh``)
|
``agent_ilo``, ``agent_ipmitool``, or ``agent_ssh``)
|
||||||
you have to know MD5 checksum for your instance image. To
|
you have to know MD5 checksum for your instance image. To
|
||||||
compute it, you can use the following command::
|
compute it, you can use the following command::
|
||||||
|
|
||||||
|
@ -22,7 +22,6 @@ from ironic.drivers.modules import agent
|
|||||||
from ironic.drivers.modules.cimc import management as cimc_mgmt
|
from ironic.drivers.modules.cimc import management as cimc_mgmt
|
||||||
from ironic.drivers.modules.cimc import power as cimc_power
|
from ironic.drivers.modules.cimc import power as cimc_power
|
||||||
from ironic.drivers.modules import inspector
|
from ironic.drivers.modules import inspector
|
||||||
from ironic.drivers.modules import ipminative
|
|
||||||
from ironic.drivers.modules import pxe
|
from ironic.drivers.modules import pxe
|
||||||
from ironic.drivers.modules import ssh
|
from ironic.drivers.modules import ssh
|
||||||
from ironic.drivers.modules.ucs import management as ucs_mgmt
|
from ironic.drivers.modules.ucs import management as ucs_mgmt
|
||||||
@ -34,32 +33,6 @@ AgentAndIPMIToolDriver = ipmi.AgentAndIPMIToolDriver
|
|||||||
AgentAndIPMIToolAndSocatDriver = ipmi.AgentAndIPMIToolAndSocatDriver
|
AgentAndIPMIToolAndSocatDriver = ipmi.AgentAndIPMIToolAndSocatDriver
|
||||||
|
|
||||||
|
|
||||||
class AgentAndIPMINativeDriver(base.BaseDriver):
|
|
||||||
"""Agent + IPMINative driver.
|
|
||||||
|
|
||||||
This driver implements the `core` functionality, combining
|
|
||||||
:class:`ironic.drivers.modules.ipminative.NativeIPMIPower` (for power
|
|
||||||
on/off and reboot) with
|
|
||||||
:class:`ironic.drivers.modules.agent.AgentDeploy` (for image
|
|
||||||
deployment).
|
|
||||||
Implementations are in those respective classes; this class is merely the
|
|
||||||
glue between them.
|
|
||||||
"""
|
|
||||||
|
|
||||||
supported = False
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.power = ipminative.NativeIPMIPower()
|
|
||||||
self.boot = pxe.PXEBoot()
|
|
||||||
self.deploy = agent.AgentDeploy()
|
|
||||||
self.management = ipminative.NativeIPMIManagement()
|
|
||||||
self.console = ipminative.NativeIPMIShellinaboxConsole()
|
|
||||||
self.vendor = ipminative.VendorPassthru()
|
|
||||||
self.raid = agent.AgentRAID()
|
|
||||||
self.inspect = inspector.Inspector.create_if_enabled(
|
|
||||||
'AgentAndIPMINativeDriver')
|
|
||||||
|
|
||||||
|
|
||||||
class AgentAndSSHDriver(base.BaseDriver):
|
class AgentAndSSHDriver(base.BaseDriver):
|
||||||
"""Agent + SSH driver.
|
"""Agent + SSH driver.
|
||||||
|
|
||||||
|
@ -36,7 +36,6 @@ from ironic.drivers.modules.ilo import inspect as ilo_inspect
|
|||||||
from ironic.drivers.modules.ilo import management as ilo_management
|
from ironic.drivers.modules.ilo import management as ilo_management
|
||||||
from ironic.drivers.modules.ilo import power as ilo_power
|
from ironic.drivers.modules.ilo import power as ilo_power
|
||||||
from ironic.drivers.modules import inspector
|
from ironic.drivers.modules import inspector
|
||||||
from ironic.drivers.modules import ipminative
|
|
||||||
from ironic.drivers.modules import ipmitool
|
from ironic.drivers.modules import ipmitool
|
||||||
from ironic.drivers.modules.irmc import inspect as irmc_inspect
|
from ironic.drivers.modules.irmc import inspect as irmc_inspect
|
||||||
from ironic.drivers.modules.irmc import management as irmc_management
|
from ironic.drivers.modules.irmc import management as irmc_management
|
||||||
@ -125,23 +124,6 @@ class FakeSSHDriver(base.BaseDriver):
|
|||||||
self.console = ssh.ShellinaboxConsole()
|
self.console = ssh.ShellinaboxConsole()
|
||||||
|
|
||||||
|
|
||||||
class FakeIPMINativeDriver(base.BaseDriver):
|
|
||||||
"""Fake IPMINative driver."""
|
|
||||||
|
|
||||||
supported = False
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
if not importutils.try_import('pyghmi'):
|
|
||||||
raise exception.DriverLoadError(
|
|
||||||
driver=self.__class__.__name__,
|
|
||||||
reason=_("Unable to import pyghmi IPMI library"))
|
|
||||||
self.power = ipminative.NativeIPMIPower()
|
|
||||||
self.console = ipminative.NativeIPMIShellinaboxConsole()
|
|
||||||
self.deploy = fake.FakeDeploy()
|
|
||||||
self.vendor = ipminative.VendorPassthru()
|
|
||||||
self.management = ipminative.NativeIPMIManagement()
|
|
||||||
|
|
||||||
|
|
||||||
class FakeAgentDriver(base.BaseDriver):
|
class FakeAgentDriver(base.BaseDriver):
|
||||||
"""Example implementation of an AgentDriver."""
|
"""Example implementation of an AgentDriver."""
|
||||||
|
|
||||||
|
@ -1,702 +0,0 @@
|
|||||||
# 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.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
from ironic_lib import metrics_utils
|
|
||||||
from ironic_lib import utils as ironic_utils
|
|
||||||
from oslo_log import log as logging
|
|
||||||
from oslo_utils import excutils
|
|
||||||
from oslo_utils import importutils
|
|
||||||
from oslo_utils import strutils
|
|
||||||
|
|
||||||
from ironic.common import boot_devices
|
|
||||||
from ironic.common import exception
|
|
||||||
from ironic.common.i18n import _, _LE, _LW
|
|
||||||
from ironic.common import states
|
|
||||||
from ironic.common import utils
|
|
||||||
from ironic.conductor import task_manager
|
|
||||||
from ironic.conf import CONF
|
|
||||||
from ironic.drivers import base
|
|
||||||
from ironic.drivers.modules import console_utils
|
|
||||||
from ironic.drivers.modules import deploy_utils
|
|
||||||
from ironic.drivers import utils as driver_utils
|
|
||||||
|
|
||||||
pyghmi = importutils.try_import('pyghmi')
|
|
||||||
if pyghmi:
|
|
||||||
from pyghmi import exceptions as pyghmi_exception
|
|
||||||
from pyghmi.ipmi import command as ipmi_command
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
METRICS = metrics_utils.get_metrics_logger(__name__)
|
|
||||||
|
|
||||||
REQUIRED_PROPERTIES = {'ipmi_address': _("IP of the node's BMC. Required."),
|
|
||||||
'ipmi_password': _("IPMI password. Required."),
|
|
||||||
'ipmi_username': _("IPMI username. Required.")}
|
|
||||||
OPTIONAL_PROPERTIES = {
|
|
||||||
'ipmi_force_boot_device': _("Whether Ironic should specify the boot "
|
|
||||||
"device to the BMC each time the server "
|
|
||||||
"is turned on, eg. because the BMC is not "
|
|
||||||
"capable of remembering the selected boot "
|
|
||||||
"device across power cycles; default value "
|
|
||||||
"is False. Optional.")
|
|
||||||
}
|
|
||||||
COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy()
|
|
||||||
COMMON_PROPERTIES.update(OPTIONAL_PROPERTIES)
|
|
||||||
CONSOLE_PROPERTIES = {
|
|
||||||
'ipmi_terminal_port': _("node's UDP port to connect to. Only required for "
|
|
||||||
"console access.")
|
|
||||||
}
|
|
||||||
|
|
||||||
_BOOT_DEVICES_MAP = {
|
|
||||||
boot_devices.DISK: 'hd',
|
|
||||||
boot_devices.PXE: 'network',
|
|
||||||
boot_devices.CDROM: 'cdrom',
|
|
||||||
boot_devices.BIOS: 'setup',
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def _parse_driver_info(node):
|
|
||||||
"""Gets the bmc access info for the given node.
|
|
||||||
|
|
||||||
:raises: MissingParameterValue when required ipmi credentials
|
|
||||||
are missing.
|
|
||||||
:raises: InvalidParameterValue when the IPMI terminal port is not an
|
|
||||||
integer.
|
|
||||||
"""
|
|
||||||
|
|
||||||
info = node.driver_info or {}
|
|
||||||
missing_info = [key for key in REQUIRED_PROPERTIES if not info.get(key)]
|
|
||||||
if missing_info:
|
|
||||||
raise exception.MissingParameterValue(_(
|
|
||||||
"Missing the following IPMI credentials in node's"
|
|
||||||
" driver_info: %s.") % missing_info)
|
|
||||||
|
|
||||||
bmc_info = {}
|
|
||||||
bmc_info['address'] = info.get('ipmi_address')
|
|
||||||
bmc_info['username'] = info.get('ipmi_username')
|
|
||||||
bmc_info['password'] = info.get('ipmi_password')
|
|
||||||
bmc_info['force_boot_device'] = info.get('ipmi_force_boot_device', False)
|
|
||||||
|
|
||||||
# get additional info
|
|
||||||
bmc_info['uuid'] = node.uuid
|
|
||||||
|
|
||||||
# terminal port must be an integer
|
|
||||||
port = info.get('ipmi_terminal_port')
|
|
||||||
if port is not None:
|
|
||||||
port = utils.validate_network_port(port, 'ipmi_terminal_port')
|
|
||||||
bmc_info['port'] = port
|
|
||||||
|
|
||||||
return bmc_info
|
|
||||||
|
|
||||||
|
|
||||||
def _console_pwfile_path(uuid):
|
|
||||||
"""Return the file path for storing the ipmi password."""
|
|
||||||
file_name = "%(uuid)s.pw" % {'uuid': uuid}
|
|
||||||
return os.path.join(CONF.tempdir, file_name)
|
|
||||||
|
|
||||||
|
|
||||||
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.ipmi.retry_timeout
|
|
||||||
ret = ipmicmd.set_power('on', wait)
|
|
||||||
except pyghmi_exception.IpmiException as e:
|
|
||||||
error = msg % {'node_id': driver_info['uuid'], 'error': e}
|
|
||||||
LOG.error(error)
|
|
||||||
raise exception.IPMIFailure(error)
|
|
||||||
|
|
||||||
state = ret.get('powerstate')
|
|
||||||
if state == 'on':
|
|
||||||
return states.POWER_ON
|
|
||||||
else:
|
|
||||||
error = _("bad response: %s") % ret
|
|
||||||
LOG.error(msg, {'node_id': driver_info['uuid'], 'error': error})
|
|
||||||
raise exception.PowerStateFailure(pstate=states.POWER_ON)
|
|
||||||
|
|
||||||
|
|
||||||
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.ipmi.retry_timeout
|
|
||||||
ret = ipmicmd.set_power('off', wait)
|
|
||||||
except pyghmi_exception.IpmiException as e:
|
|
||||||
error = msg % {'node_id': driver_info['uuid'], 'error': e}
|
|
||||||
LOG.error(error)
|
|
||||||
raise exception.IPMIFailure(error)
|
|
||||||
|
|
||||||
state = ret.get('powerstate')
|
|
||||||
if state == 'off':
|
|
||||||
return states.POWER_OFF
|
|
||||||
else:
|
|
||||||
error = _("bad response: %s") % ret
|
|
||||||
LOG.error(msg, {'node_id': driver_info['uuid'], 'error': error})
|
|
||||||
raise exception.PowerStateFailure(pstate=states.POWER_OFF)
|
|
||||||
|
|
||||||
|
|
||||||
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.ipmi.retry_timeout
|
|
||||||
ret = ipmicmd.set_power('boot', wait)
|
|
||||||
except pyghmi_exception.IpmiException as e:
|
|
||||||
error = msg % {'node_id': driver_info['uuid'], 'error': e}
|
|
||||||
LOG.error(error)
|
|
||||||
raise exception.IPMIFailure(error)
|
|
||||||
|
|
||||||
if 'error' in ret:
|
|
||||||
error = _("bad response: %s") % ret
|
|
||||||
LOG.error(msg, {'node_id': driver_info['uuid'], 'error': error})
|
|
||||||
raise exception.PowerStateFailure(pstate=states.REBOOT)
|
|
||||||
|
|
||||||
return states.POWER_ON
|
|
||||||
|
|
||||||
|
|
||||||
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:
|
|
||||||
msg = (_("IPMI get power state failed for node %(node_id)s "
|
|
||||||
"with the following error: %(error)s") %
|
|
||||||
{'node_id': driver_info['uuid'], 'error': e})
|
|
||||||
LOG.error(msg)
|
|
||||||
raise exception.IPMIFailure(msg)
|
|
||||||
|
|
||||||
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(_LW("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
|
|
||||||
|
|
||||||
|
|
||||||
def _get_sensors_data(driver_info):
|
|
||||||
"""Get sensors data.
|
|
||||||
|
|
||||||
:param driver_info: node's driver info
|
|
||||||
:raises: FailedToGetSensorData when getting the sensor data fails.
|
|
||||||
:returns: returns a dict of sensor data group by sensor type.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
ipmicmd = ipmi_command.Command(bmc=driver_info['address'],
|
|
||||||
userid=driver_info['username'],
|
|
||||||
password=driver_info['password'])
|
|
||||||
ret = ipmicmd.get_sensor_data()
|
|
||||||
except Exception as e:
|
|
||||||
LOG.error(_LE("IPMI get sensor data failed for node %(node_id)s "
|
|
||||||
"with the following error: %(error)s"),
|
|
||||||
{'node_id': driver_info['uuid'], 'error': e})
|
|
||||||
raise exception.FailedToGetSensorData(
|
|
||||||
node=driver_info['uuid'], error=e)
|
|
||||||
|
|
||||||
if not ret:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
sensors_data = {}
|
|
||||||
for reading in ret:
|
|
||||||
# ignore the sensor data which has no sensor reading value
|
|
||||||
if not reading.value:
|
|
||||||
continue
|
|
||||||
sensors_data.setdefault(
|
|
||||||
reading.type,
|
|
||||||
{})[reading.name] = {
|
|
||||||
'Sensor Reading': '%s %s' % (reading.value, reading.units),
|
|
||||||
'Sensor ID': reading.name,
|
|
||||||
'States': str(reading.states),
|
|
||||||
'Units': reading.units,
|
|
||||||
'Health': str(reading.health)}
|
|
||||||
|
|
||||||
return sensors_data
|
|
||||||
|
|
||||||
|
|
||||||
def _parse_raw_bytes(raw_bytes):
|
|
||||||
"""Parse raw bytes string.
|
|
||||||
|
|
||||||
:param raw_bytes: a string of hexadecimal raw bytes, e.g. '0x00 0x01'.
|
|
||||||
:returns: a tuple containing the arguments for pyghmi call as integers,
|
|
||||||
(IPMI net function, IPMI command, list of command's data).
|
|
||||||
:raises: InvalidParameterValue when an invalid value is specified.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
bytes_list = [int(x, base=16) for x in raw_bytes.split()]
|
|
||||||
return bytes_list[0], bytes_list[1], bytes_list[2:]
|
|
||||||
except ValueError:
|
|
||||||
raise exception.InvalidParameterValue(_(
|
|
||||||
"Invalid raw bytes string: '%s'") % raw_bytes)
|
|
||||||
except IndexError:
|
|
||||||
raise exception.InvalidParameterValue(_(
|
|
||||||
"Raw bytes string requires two bytes at least."))
|
|
||||||
|
|
||||||
|
|
||||||
def _send_raw(driver_info, raw_bytes):
|
|
||||||
"""Send raw bytes to the BMC."""
|
|
||||||
netfn, command, data = _parse_raw_bytes(raw_bytes)
|
|
||||||
LOG.debug("Sending raw bytes %(bytes)s to node %(node_id)s",
|
|
||||||
{'bytes': raw_bytes, 'node_id': driver_info['uuid']})
|
|
||||||
try:
|
|
||||||
ipmicmd = ipmi_command.Command(bmc=driver_info['address'],
|
|
||||||
userid=driver_info['username'],
|
|
||||||
password=driver_info['password'])
|
|
||||||
ipmicmd.xraw_command(netfn, command, data=data)
|
|
||||||
except pyghmi_exception.IpmiException as e:
|
|
||||||
msg = (_("IPMI send raw bytes '%(bytes)s' failed for node %(node_id)s"
|
|
||||||
" with the following error: %(error)s") %
|
|
||||||
{'bytes': raw_bytes, 'node_id': driver_info['uuid'],
|
|
||||||
'error': e})
|
|
||||||
LOG.error(msg)
|
|
||||||
raise exception.IPMIFailure(msg)
|
|
||||||
|
|
||||||
|
|
||||||
class NativeIPMIPower(base.PowerInterface):
|
|
||||||
"""The power driver using native python-ipmi library."""
|
|
||||||
|
|
||||||
def get_properties(self):
|
|
||||||
return COMMON_PROPERTIES
|
|
||||||
|
|
||||||
@METRICS.timer('NativeIPMIPower.validate')
|
|
||||||
def validate(self, task):
|
|
||||||
"""Check that node['driver_info'] contains IPMI credentials.
|
|
||||||
|
|
||||||
:param task: a TaskManager instance containing the node to act on.
|
|
||||||
:raises: MissingParameterValue when required ipmi credentials
|
|
||||||
are missing.
|
|
||||||
"""
|
|
||||||
_parse_driver_info(task.node)
|
|
||||||
|
|
||||||
@METRICS.timer('NativeIPMIPower.get_power_state')
|
|
||||||
def get_power_state(self, task):
|
|
||||||
"""Get the current power state of the task's node.
|
|
||||||
|
|
||||||
:param task: a TaskManager instance containing the node to act on.
|
|
||||||
:returns: power state POWER_ON, POWER_OFF or ERROR defined in
|
|
||||||
:class:`ironic.common.states`.
|
|
||||||
:raises: MissingParameterValue when required ipmi credentials
|
|
||||||
are missing.
|
|
||||||
:raises: IPMIFailure when the native ipmi call fails.
|
|
||||||
"""
|
|
||||||
driver_info = _parse_driver_info(task.node)
|
|
||||||
return _power_status(driver_info)
|
|
||||||
|
|
||||||
@METRICS.timer('NativeIPMIPower.set_power_state')
|
|
||||||
@task_manager.require_exclusive_lock
|
|
||||||
def set_power_state(self, task, pstate):
|
|
||||||
"""Turn the power on or off.
|
|
||||||
|
|
||||||
:param task: a TaskManager instance containing the node to act on.
|
|
||||||
:param pstate: a power state that will be set on the task's node.
|
|
||||||
:raises: IPMIFailure when the native ipmi call fails.
|
|
||||||
:raises: MissingParameterValue when required ipmi credentials
|
|
||||||
are missing.
|
|
||||||
:raises: InvalidParameterValue when an invalid power state
|
|
||||||
is specified
|
|
||||||
:raises: PowerStateFailure when invalid power state is returned
|
|
||||||
from ipmi.
|
|
||||||
"""
|
|
||||||
|
|
||||||
driver_info = _parse_driver_info(task.node)
|
|
||||||
|
|
||||||
if pstate == states.POWER_ON:
|
|
||||||
driver_utils.ensure_next_boot_device(task, driver_info)
|
|
||||||
_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)
|
|
||||||
|
|
||||||
@METRICS.timer('NativeIPMIPower.reboot')
|
|
||||||
@task_manager.require_exclusive_lock
|
|
||||||
def reboot(self, task):
|
|
||||||
"""Cycles the power to the task's node.
|
|
||||||
|
|
||||||
:param task: a TaskManager instance containing the node to act on.
|
|
||||||
:raises: IPMIFailure when the native ipmi call fails.
|
|
||||||
:raises: MissingParameterValue when required ipmi credentials
|
|
||||||
are missing.
|
|
||||||
:raises: PowerStateFailure when invalid power state is returned
|
|
||||||
from ipmi.
|
|
||||||
"""
|
|
||||||
|
|
||||||
driver_info = _parse_driver_info(task.node)
|
|
||||||
driver_utils.ensure_next_boot_device(task, driver_info)
|
|
||||||
_reboot(driver_info)
|
|
||||||
|
|
||||||
|
|
||||||
class NativeIPMIManagement(base.ManagementInterface):
|
|
||||||
|
|
||||||
def get_properties(self):
|
|
||||||
return COMMON_PROPERTIES
|
|
||||||
|
|
||||||
@METRICS.timer('NativeIPMIManagement.validate')
|
|
||||||
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: MissingParameterValue when required ipmi credentials
|
|
||||||
are missing.
|
|
||||||
|
|
||||||
"""
|
|
||||||
_parse_driver_info(task.node)
|
|
||||||
|
|
||||||
def get_supported_boot_devices(self, task):
|
|
||||||
"""Get a list of the supported boot devices.
|
|
||||||
|
|
||||||
:param task: a task from TaskManager.
|
|
||||||
:returns: A list with the supported boot devices defined
|
|
||||||
in :mod:`ironic.common.boot_devices`.
|
|
||||||
|
|
||||||
"""
|
|
||||||
return list(_BOOT_DEVICES_MAP.keys())
|
|
||||||
|
|
||||||
@METRICS.timer('NativeIPMIManagement.set_boot_device')
|
|
||||||
@task_manager.require_exclusive_lock
|
|
||||||
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 required ipmi credentials are missing.
|
|
||||||
:raises: MissingParameterValue when required ipmi credentials
|
|
||||||
are missing.
|
|
||||||
:raises: IPMIFailure on an error from pyghmi.
|
|
||||||
"""
|
|
||||||
if device not in self.get_supported_boot_devices(task):
|
|
||||||
raise exception.InvalidParameterValue(_(
|
|
||||||
"Invalid boot device %s specified.") % device)
|
|
||||||
|
|
||||||
if task.node.driver_info.get('ipmi_force_boot_device', False):
|
|
||||||
driver_utils.force_persistent_boot(task,
|
|
||||||
device,
|
|
||||||
persistent)
|
|
||||||
# Reset persistent to False, in case of BMC does not support
|
|
||||||
# persistent or we do not have admin rights.
|
|
||||||
persistent = False
|
|
||||||
|
|
||||||
boot_mode = deploy_utils.get_boot_mode_for_deploy(task.node)
|
|
||||||
driver_info = _parse_driver_info(task.node)
|
|
||||||
try:
|
|
||||||
ipmicmd = ipmi_command.Command(bmc=driver_info['address'],
|
|
||||||
userid=driver_info['username'],
|
|
||||||
password=driver_info['password'])
|
|
||||||
bootdev = _BOOT_DEVICES_MAP[device]
|
|
||||||
uefiboot = boot_mode == 'uefi'
|
|
||||||
ipmicmd.set_bootdev(bootdev, persist=persistent, uefiboot=uefiboot)
|
|
||||||
except pyghmi_exception.IpmiException as 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)
|
|
||||||
|
|
||||||
@METRICS.timer('NativeIPMIManagement.get_boot_device')
|
|
||||||
def get_boot_device(self, task):
|
|
||||||
"""Get the current boot device for the task's node.
|
|
||||||
|
|
||||||
Returns the current boot device of the node.
|
|
||||||
|
|
||||||
:param task: a task from TaskManager.
|
|
||||||
:raises: MissingParameterValue 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.
|
|
||||||
|
|
||||||
"""
|
|
||||||
driver_info = task.node.driver_info
|
|
||||||
driver_internal_info = task.node.driver_internal_info
|
|
||||||
if (driver_info.get('ipmi_force_boot_device', False) and
|
|
||||||
driver_internal_info.get('persistent_boot_device') and
|
|
||||||
driver_internal_info.get('is_next_boot_persistent', True)):
|
|
||||||
return {
|
|
||||||
'boot_device': driver_internal_info['persistent_boot_device'],
|
|
||||||
'persistent': True
|
|
||||||
}
|
|
||||||
|
|
||||||
driver_info = _parse_driver_info(task.node)
|
|
||||||
response = {'boot_device': 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)
|
|
||||||
|
|
||||||
response['persistent'] = ret.get('persistent')
|
|
||||||
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
|
|
||||||
|
|
||||||
@METRICS.timer('NativeIPMIManagement.get_sensors_data')
|
|
||||||
def get_sensors_data(self, task):
|
|
||||||
"""Get sensors data.
|
|
||||||
|
|
||||||
:param task: a TaskManager instance.
|
|
||||||
:raises: FailedToGetSensorData when getting the sensor data fails.
|
|
||||||
:raises: MissingParameterValue if required ipmi parameters are missing
|
|
||||||
:returns: returns a dict of sensor data group by sensor type.
|
|
||||||
|
|
||||||
"""
|
|
||||||
driver_info = _parse_driver_info(task.node)
|
|
||||||
return _get_sensors_data(driver_info)
|
|
||||||
|
|
||||||
|
|
||||||
class NativeIPMIShellinaboxConsole(base.ConsoleInterface):
|
|
||||||
"""A ConsoleInterface that uses pyghmi and shellinabox."""
|
|
||||||
|
|
||||||
def get_properties(self):
|
|
||||||
d = COMMON_PROPERTIES.copy()
|
|
||||||
d.update(CONSOLE_PROPERTIES)
|
|
||||||
return d
|
|
||||||
|
|
||||||
@METRICS.timer('NativeIPMIShellinaboxConsole.validate')
|
|
||||||
def validate(self, task):
|
|
||||||
"""Validate the Node console info.
|
|
||||||
|
|
||||||
:param task: a TaskManager instance containing the node to act on.
|
|
||||||
:raises: MissingParameterValue when required IPMI credentials or
|
|
||||||
the IPMI terminal port are missing
|
|
||||||
:raises: InvalidParameterValue when the IPMI terminal port is not
|
|
||||||
an integer.
|
|
||||||
"""
|
|
||||||
driver_info = _parse_driver_info(task.node)
|
|
||||||
if not driver_info['port']:
|
|
||||||
raise exception.MissingParameterValue(_(
|
|
||||||
"Missing 'ipmi_terminal_port' parameter in node's"
|
|
||||||
" driver_info."))
|
|
||||||
|
|
||||||
@METRICS.timer('NativeIPMIShellinaboxConsole.start_console')
|
|
||||||
def start_console(self, task):
|
|
||||||
"""Start a remote console for the node.
|
|
||||||
|
|
||||||
:param task: a TaskManager instance containing the node to act on.
|
|
||||||
:raises: MissingParameterValue when required ipmi credentials
|
|
||||||
are missing.
|
|
||||||
:raises: InvalidParameterValue when the IPMI terminal port is not an
|
|
||||||
integer.
|
|
||||||
:raises: ConsoleError if unable to start the console process.
|
|
||||||
"""
|
|
||||||
driver_info = _parse_driver_info(task.node)
|
|
||||||
|
|
||||||
path = _console_pwfile_path(driver_info['uuid'])
|
|
||||||
pw_file = console_utils.make_persistent_password_file(
|
|
||||||
path, driver_info['password'])
|
|
||||||
|
|
||||||
console_cmd = ("/:%(uid)s:%(gid)s:HOME:pyghmicons %(bmc)s"
|
|
||||||
" %(user)s"
|
|
||||||
" %(passwd_file)s"
|
|
||||||
% {'uid': os.getuid(),
|
|
||||||
'gid': os.getgid(),
|
|
||||||
'bmc': driver_info['address'],
|
|
||||||
'user': driver_info['username'],
|
|
||||||
'passwd_file': pw_file})
|
|
||||||
try:
|
|
||||||
console_utils.start_shellinabox_console(driver_info['uuid'],
|
|
||||||
driver_info['port'],
|
|
||||||
console_cmd)
|
|
||||||
except exception.ConsoleError:
|
|
||||||
with excutils.save_and_reraise_exception():
|
|
||||||
ironic_utils.unlink_without_raise(path)
|
|
||||||
|
|
||||||
@METRICS.timer('NativeIPMIShellinaboxConsole.stop_console')
|
|
||||||
def stop_console(self, task):
|
|
||||||
"""Stop the remote console session for the node.
|
|
||||||
|
|
||||||
:param task: a TaskManager instance containing the node to act on.
|
|
||||||
:raises: ConsoleError if unable to stop the console process.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
console_utils.stop_shellinabox_console(task.node.uuid)
|
|
||||||
finally:
|
|
||||||
password_file = _console_pwfile_path(task.node.uuid)
|
|
||||||
ironic_utils.unlink_without_raise(password_file)
|
|
||||||
|
|
||||||
@METRICS.timer('NativeIPMIShellinaboxConsole.get_console')
|
|
||||||
def get_console(self, task):
|
|
||||||
"""Get the type and connection information about the console.
|
|
||||||
|
|
||||||
:param task: a TaskManager instance containing the node to act on.
|
|
||||||
:raises: MissingParameterValue when required IPMI credentials or
|
|
||||||
the IPMI terminal port are missing
|
|
||||||
:raises: InvalidParameterValue when the IPMI terminal port is not
|
|
||||||
an integer.
|
|
||||||
"""
|
|
||||||
driver_info = _parse_driver_info(task.node)
|
|
||||||
url = console_utils.get_shellinabox_console_url(driver_info['port'])
|
|
||||||
return {'type': 'shellinabox', 'url': url}
|
|
||||||
|
|
||||||
|
|
||||||
class VendorPassthru(base.VendorInterface):
|
|
||||||
|
|
||||||
def get_properties(self):
|
|
||||||
return COMMON_PROPERTIES
|
|
||||||
|
|
||||||
@METRICS.timer('VendorPassthru.validate')
|
|
||||||
def validate(self, task, method, **kwargs):
|
|
||||||
"""Validate vendor-specific actions.
|
|
||||||
|
|
||||||
:param task: a task from TaskManager.
|
|
||||||
:param method: method to be validated
|
|
||||||
:param kwargs: info for action.
|
|
||||||
:raises: InvalidParameterValue when an invalid parameter value is
|
|
||||||
specified.
|
|
||||||
:raises: MissingParameterValue if a required parameter is missing.
|
|
||||||
|
|
||||||
"""
|
|
||||||
if method == 'send_raw':
|
|
||||||
raw_bytes = kwargs.get('raw_bytes')
|
|
||||||
if not raw_bytes:
|
|
||||||
raise exception.MissingParameterValue(_(
|
|
||||||
'Parameter raw_bytes (string of bytes) was not '
|
|
||||||
'specified.'))
|
|
||||||
_parse_raw_bytes(raw_bytes)
|
|
||||||
|
|
||||||
_parse_driver_info(task.node)
|
|
||||||
|
|
||||||
@METRICS.timer('VendorPassthru.send_raw')
|
|
||||||
@base.passthru(['POST'],
|
|
||||||
description=_("Send raw bytes to the BMC. Required "
|
|
||||||
"argument: 'raw_bytes' - a string of raw "
|
|
||||||
"bytes (e.g. '0x00 0x01')."))
|
|
||||||
@task_manager.require_exclusive_lock
|
|
||||||
def send_raw(self, task, http_method, raw_bytes):
|
|
||||||
"""Send raw bytes to the BMC. Bytes should be a string of bytes.
|
|
||||||
|
|
||||||
:param task: a TaskManager instance.
|
|
||||||
:param http_method: the HTTP method used on the request.
|
|
||||||
:param raw_bytes: a string of raw bytes to send, e.g. '0x00 0x01'
|
|
||||||
:raises: IPMIFailure on an error from native IPMI call.
|
|
||||||
:raises: MissingParameterValue if a required parameter is missing.
|
|
||||||
:raises: InvalidParameterValue when an invalid value is specified.
|
|
||||||
|
|
||||||
"""
|
|
||||||
driver_info = _parse_driver_info(task.node)
|
|
||||||
_send_raw(driver_info, raw_bytes)
|
|
||||||
|
|
||||||
@METRICS.timer('VendorPassthru.bmc_reset')
|
|
||||||
@base.passthru(['POST'],
|
|
||||||
description=_("Reset the BMC. Required argument: 'warm' "
|
|
||||||
"(Boolean) - for warm (True) or cold (False) "
|
|
||||||
"reset."))
|
|
||||||
@task_manager.require_exclusive_lock
|
|
||||||
def bmc_reset(self, task, http_method, warm=True):
|
|
||||||
"""Reset BMC via IPMI command.
|
|
||||||
|
|
||||||
:param task: a TaskManager instance.
|
|
||||||
:param http_method: the HTTP method used on the request.
|
|
||||||
:param warm: boolean parameter to decide on warm or cold reset.
|
|
||||||
:raises: IPMIFailure on an error from native IPMI call.
|
|
||||||
:raises: MissingParameterValue if a required parameter is missing.
|
|
||||||
:raises: InvalidParameterValue when an invalid value is specified
|
|
||||||
|
|
||||||
"""
|
|
||||||
driver_info = _parse_driver_info(task.node)
|
|
||||||
warm = strutils.bool_from_string(warm)
|
|
||||||
# NOTE(yuriyz): pyghmi 0.8.0 does not have a method for BMC reset
|
|
||||||
command = '0x03' if warm else '0x02'
|
|
||||||
raw_command = '0x06 ' + command
|
|
||||||
_send_raw(driver_info, raw_command)
|
|
@ -33,7 +33,6 @@ from ironic.drivers.modules.ilo import management as ilo_management
|
|||||||
from ironic.drivers.modules.ilo import power as ilo_power
|
from ironic.drivers.modules.ilo import power as ilo_power
|
||||||
from ironic.drivers.modules.ilo import vendor as ilo_vendor
|
from ironic.drivers.modules.ilo import vendor as ilo_vendor
|
||||||
from ironic.drivers.modules import inspector
|
from ironic.drivers.modules import inspector
|
||||||
from ironic.drivers.modules import ipminative
|
|
||||||
from ironic.drivers.modules import ipmitool
|
from ironic.drivers.modules import ipmitool
|
||||||
from ironic.drivers.modules.irmc import inspect as irmc_inspect
|
from ironic.drivers.modules.irmc import inspect as irmc_inspect
|
||||||
from ironic.drivers.modules.irmc import management as irmc_management
|
from ironic.drivers.modules.irmc import management as irmc_management
|
||||||
@ -77,35 +76,6 @@ class PXEAndSSHDriver(base.BaseDriver):
|
|||||||
self.console = ssh.ShellinaboxConsole()
|
self.console = ssh.ShellinaboxConsole()
|
||||||
|
|
||||||
|
|
||||||
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.drivers.modules.iscsi_deploy.ISCSIDeploy`
|
|
||||||
for image deployment. Implementations are in those respective
|
|
||||||
classes; this class is merely the glue between them.
|
|
||||||
"""
|
|
||||||
|
|
||||||
supported = False
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
if not importutils.try_import('pyghmi'):
|
|
||||||
raise exception.DriverLoadError(
|
|
||||||
driver=self.__class__.__name__,
|
|
||||||
reason=_("Unable to import pyghmi library"))
|
|
||||||
self.power = ipminative.NativeIPMIPower()
|
|
||||||
self.console = ipminative.NativeIPMIShellinaboxConsole()
|
|
||||||
self.boot = pxe.PXEBoot()
|
|
||||||
self.deploy = iscsi_deploy.ISCSIDeploy()
|
|
||||||
self.management = ipminative.NativeIPMIManagement()
|
|
||||||
self.vendor = ipminative.VendorPassthru()
|
|
||||||
self.inspect = inspector.Inspector.create_if_enabled(
|
|
||||||
'PXEAndIPMINativeDriver')
|
|
||||||
self.raid = agent.AgentRAID()
|
|
||||||
|
|
||||||
|
|
||||||
class PXEAndIloDriver(base.BaseDriver):
|
class PXEAndIloDriver(base.BaseDriver):
|
||||||
"""PXE + Ilo Driver using IloClient interface.
|
"""PXE + Ilo Driver using IloClient interface.
|
||||||
|
|
||||||
|
@ -4758,11 +4758,6 @@ class ManagerTestProperties(mgr_utils.ServiceSetUpMixin,
|
|||||||
]
|
]
|
||||||
self._check_driver_properties("fake_ipmitool", expected)
|
self._check_driver_properties("fake_ipmitool", expected)
|
||||||
|
|
||||||
def test_driver_properties_fake_ipminative(self):
|
|
||||||
expected = ['ipmi_address', 'ipmi_password', 'ipmi_username',
|
|
||||||
'ipmi_terminal_port', 'ipmi_force_boot_device']
|
|
||||||
self._check_driver_properties("fake_ipminative", expected)
|
|
||||||
|
|
||||||
def test_driver_properties_fake_ssh(self):
|
def test_driver_properties_fake_ssh(self):
|
||||||
expected = ['ssh_address', 'ssh_username',
|
expected = ['ssh_address', 'ssh_username',
|
||||||
'vbox_use_headless', 'ssh_virt_type',
|
'vbox_use_headless', 'ssh_virt_type',
|
||||||
@ -4790,13 +4785,6 @@ class ManagerTestProperties(mgr_utils.ServiceSetUpMixin,
|
|||||||
'ipmi_force_boot_device', 'deploy_forces_oob_reboot']
|
'ipmi_force_boot_device', 'deploy_forces_oob_reboot']
|
||||||
self._check_driver_properties("pxe_ipmitool", expected)
|
self._check_driver_properties("pxe_ipmitool", expected)
|
||||||
|
|
||||||
def test_driver_properties_pxe_ipminative(self):
|
|
||||||
expected = ['ipmi_address', 'ipmi_password', 'ipmi_username',
|
|
||||||
'deploy_kernel', 'deploy_ramdisk',
|
|
||||||
'ipmi_terminal_port', 'ipmi_force_boot_device',
|
|
||||||
'deploy_forces_oob_reboot']
|
|
||||||
self._check_driver_properties("pxe_ipminative", expected)
|
|
||||||
|
|
||||||
def test_driver_properties_pxe_ssh(self):
|
def test_driver_properties_pxe_ssh(self):
|
||||||
expected = ['deploy_kernel', 'deploy_ramdisk',
|
expected = ['deploy_kernel', 'deploy_ramdisk',
|
||||||
'ssh_address', 'ssh_username',
|
'ssh_address', 'ssh_username',
|
||||||
|
@ -1,642 +0,0 @@
|
|||||||
# 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.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import mock
|
|
||||||
from oslo_utils import uuidutils
|
|
||||||
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.conductor import task_manager
|
|
||||||
from ironic.drivers.modules import console_utils
|
|
||||||
from ironic.drivers.modules import deploy_utils
|
|
||||||
from ironic.drivers.modules import ipminative
|
|
||||||
from ironic.drivers import utils as driver_utils
|
|
||||||
from ironic.tests.unit.conductor import mgr_utils
|
|
||||||
from ironic.tests.unit.db import base as db_base
|
|
||||||
from ironic.tests.unit.db import utils as db_utils
|
|
||||||
from ironic.tests.unit.objects import utils as obj_utils
|
|
||||||
|
|
||||||
INFO_DICT = db_utils.get_test_ipmi_info()
|
|
||||||
|
|
||||||
|
|
||||||
class IPMINativePrivateMethodTestCase(db_base.DbTestCase):
|
|
||||||
"""Test cases for ipminative private methods."""
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(IPMINativePrivateMethodTestCase, self).setUp()
|
|
||||||
self.node = obj_utils.create_test_node(self.context,
|
|
||||||
driver='fake_ipminative',
|
|
||||||
driver_info=INFO_DICT)
|
|
||||||
self.info = ipminative._parse_driver_info(self.node)
|
|
||||||
|
|
||||||
def test__parse_driver_info(self):
|
|
||||||
# make sure we get back the expected things
|
|
||||||
self.assertEqual('1.2.3.4', self.info['address'])
|
|
||||||
self.assertEqual('admin', self.info['username'])
|
|
||||||
self.assertEqual('fake', self.info['password'])
|
|
||||||
self.assertEqual('1be26c0b-03f2-4d2e-ae87-c02d7f33c123',
|
|
||||||
self.info['uuid'])
|
|
||||||
self.assertEqual(False, self.info['force_boot_device'])
|
|
||||||
|
|
||||||
# make sure error is raised when info, eg. username, is missing
|
|
||||||
info = dict(INFO_DICT)
|
|
||||||
del info['ipmi_username']
|
|
||||||
|
|
||||||
node = obj_utils.get_test_node(self.context, driver_info=info)
|
|
||||||
self.assertRaises(exception.MissingParameterValue,
|
|
||||||
ipminative._parse_driver_info,
|
|
||||||
node)
|
|
||||||
|
|
||||||
@mock.patch('pyghmi.ipmi.command.Command', autospec=True)
|
|
||||||
def test__power_status_on(self, ipmi_mock):
|
|
||||||
ipmicmd = ipmi_mock.return_value
|
|
||||||
ipmicmd.get_power.return_value = {'powerstate': 'on'}
|
|
||||||
|
|
||||||
state = ipminative._power_status(self.info)
|
|
||||||
ipmicmd.get_power.assert_called_once_with()
|
|
||||||
self.assertEqual(states.POWER_ON, state)
|
|
||||||
|
|
||||||
@mock.patch('pyghmi.ipmi.command.Command', autospec=True)
|
|
||||||
def test__power_status_off(self, ipmi_mock):
|
|
||||||
ipmicmd = ipmi_mock.return_value
|
|
||||||
ipmicmd.get_power.return_value = {'powerstate': 'off'}
|
|
||||||
|
|
||||||
state = ipminative._power_status(self.info)
|
|
||||||
ipmicmd.get_power.assert_called_once_with()
|
|
||||||
self.assertEqual(states.POWER_OFF, state)
|
|
||||||
|
|
||||||
@mock.patch('pyghmi.ipmi.command.Command', autospec=True)
|
|
||||||
def test__power_status_error(self, ipmi_mock):
|
|
||||||
ipmicmd = ipmi_mock.return_value
|
|
||||||
ipmicmd.get_power.return_value = {'powerstate': 'Error'}
|
|
||||||
|
|
||||||
state = ipminative._power_status(self.info)
|
|
||||||
ipmicmd.get_power.assert_called_once_with()
|
|
||||||
self.assertEqual(states.ERROR, state)
|
|
||||||
|
|
||||||
@mock.patch('pyghmi.ipmi.command.Command', autospec=True)
|
|
||||||
def test__power_on(self, ipmi_mock):
|
|
||||||
ipmicmd = ipmi_mock.return_value
|
|
||||||
ipmicmd.set_power.return_value = {'powerstate': 'on'}
|
|
||||||
|
|
||||||
self.config(retry_timeout=400, group='ipmi')
|
|
||||||
state = ipminative._power_on(self.info)
|
|
||||||
ipmicmd.set_power.assert_called_once_with('on', 400)
|
|
||||||
self.assertEqual(states.POWER_ON, state)
|
|
||||||
|
|
||||||
@mock.patch('pyghmi.ipmi.command.Command', autospec=True)
|
|
||||||
def test__power_off(self, ipmi_mock):
|
|
||||||
ipmicmd = ipmi_mock.return_value
|
|
||||||
ipmicmd.set_power.return_value = {'powerstate': 'off'}
|
|
||||||
|
|
||||||
self.config(retry_timeout=500, group='ipmi')
|
|
||||||
state = ipminative._power_off(self.info)
|
|
||||||
ipmicmd.set_power.assert_called_once_with('off', 500)
|
|
||||||
self.assertEqual(states.POWER_OFF, state)
|
|
||||||
|
|
||||||
@mock.patch('pyghmi.ipmi.command.Command', autospec=True)
|
|
||||||
def test__reboot(self, ipmi_mock):
|
|
||||||
ipmicmd = ipmi_mock.return_value
|
|
||||||
ipmicmd.set_power.return_value = {'powerstate': 'on'}
|
|
||||||
|
|
||||||
self.config(retry_timeout=600, group='ipmi')
|
|
||||||
state = ipminative._reboot(self.info)
|
|
||||||
ipmicmd.set_power.assert_called_once_with('boot', 600)
|
|
||||||
self.assertEqual(states.POWER_ON, state)
|
|
||||||
|
|
||||||
def _create_sensor_object(self, value, type_, name, states=None,
|
|
||||||
units='fake_units', health=0):
|
|
||||||
if states is None:
|
|
||||||
states = []
|
|
||||||
return type('Reading', (object, ), {
|
|
||||||
'value': value, 'type': type_, 'name': name,
|
|
||||||
'states': states, 'units': units, 'health': health})()
|
|
||||||
|
|
||||||
@mock.patch('pyghmi.ipmi.command.Command', autospec=True)
|
|
||||||
def test__get_sensors_data(self, ipmi_mock):
|
|
||||||
reading_1 = self._create_sensor_object('fake_value1',
|
|
||||||
'fake_type_A',
|
|
||||||
'fake_name1')
|
|
||||||
reading_2 = self._create_sensor_object('fake_value2',
|
|
||||||
'fake_type_A',
|
|
||||||
'fake_name2')
|
|
||||||
reading_3 = self._create_sensor_object('fake_value3',
|
|
||||||
'fake_type_B',
|
|
||||||
'fake_name3')
|
|
||||||
readings = [reading_1, reading_2, reading_3]
|
|
||||||
ipmicmd = ipmi_mock.return_value
|
|
||||||
ipmicmd.get_sensor_data.return_value = readings
|
|
||||||
expected = {
|
|
||||||
'fake_type_A': {
|
|
||||||
'fake_name1': {
|
|
||||||
'Health': '0',
|
|
||||||
'Sensor ID': 'fake_name1',
|
|
||||||
'Sensor Reading': 'fake_value1 fake_units',
|
|
||||||
'States': '[]',
|
|
||||||
'Units': 'fake_units'
|
|
||||||
},
|
|
||||||
'fake_name2': {
|
|
||||||
'Health': '0',
|
|
||||||
'Sensor ID': 'fake_name2',
|
|
||||||
'Sensor Reading': 'fake_value2 fake_units',
|
|
||||||
'States': '[]',
|
|
||||||
'Units': 'fake_units'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'fake_type_B': {
|
|
||||||
'fake_name3': {
|
|
||||||
'Health': '0',
|
|
||||||
'Sensor ID': 'fake_name3',
|
|
||||||
'Sensor Reading': 'fake_value3 fake_units',
|
|
||||||
'States': '[]', 'Units': 'fake_units'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret = ipminative._get_sensors_data(self.info)
|
|
||||||
self.assertEqual(expected, ret)
|
|
||||||
|
|
||||||
@mock.patch('pyghmi.ipmi.command.Command', autospec=True)
|
|
||||||
def test__get_sensors_data_missing_values(self, ipmi_mock):
|
|
||||||
reading_1 = self._create_sensor_object('fake_value1',
|
|
||||||
'fake_type_A',
|
|
||||||
'fake_name1')
|
|
||||||
reading_2 = self._create_sensor_object(None,
|
|
||||||
'fake_type_A',
|
|
||||||
'fake_name2')
|
|
||||||
reading_3 = self._create_sensor_object(None,
|
|
||||||
'fake_type_B',
|
|
||||||
'fake_name3')
|
|
||||||
readings = [reading_1, reading_2, reading_3]
|
|
||||||
ipmicmd = ipmi_mock.return_value
|
|
||||||
ipmicmd.get_sensor_data.return_value = readings
|
|
||||||
|
|
||||||
expected = {
|
|
||||||
'fake_type_A': {
|
|
||||||
'fake_name1': {
|
|
||||||
'Health': '0',
|
|
||||||
'Sensor ID': 'fake_name1',
|
|
||||||
'Sensor Reading': 'fake_value1 fake_units',
|
|
||||||
'States': '[]',
|
|
||||||
'Units': 'fake_units'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret = ipminative._get_sensors_data(self.info)
|
|
||||||
self.assertEqual(expected, ret)
|
|
||||||
|
|
||||||
def test__parse_raw_bytes_ok(self):
|
|
||||||
bytes_string = '0x11 0x12 0x25 0xFF'
|
|
||||||
netfn, cmd, data = ipminative._parse_raw_bytes(bytes_string)
|
|
||||||
self.assertEqual(0x11, netfn)
|
|
||||||
self.assertEqual(0x12, cmd)
|
|
||||||
self.assertEqual([0x25, 0xFF], data)
|
|
||||||
|
|
||||||
def test__parse_raw_bytes_invalid_value(self):
|
|
||||||
bytes_string = '0x11 oops'
|
|
||||||
self.assertRaises(exception.InvalidParameterValue,
|
|
||||||
ipminative._parse_raw_bytes,
|
|
||||||
bytes_string)
|
|
||||||
|
|
||||||
def test__parse_raw_bytes_missing_byte(self):
|
|
||||||
bytes_string = '0x11'
|
|
||||||
self.assertRaises(exception.InvalidParameterValue,
|
|
||||||
ipminative._parse_raw_bytes,
|
|
||||||
bytes_string)
|
|
||||||
|
|
||||||
@mock.patch('pyghmi.ipmi.command.Command', autospec=True)
|
|
||||||
def test__send_raw(self, ipmi_mock):
|
|
||||||
ipmicmd = ipmi_mock.return_value
|
|
||||||
ipminative._send_raw(self.info, '0x01 0x02 0x03 0x04')
|
|
||||||
ipmicmd.xraw_command.assert_called_once_with(1, 2, data=[3, 4])
|
|
||||||
|
|
||||||
@mock.patch('pyghmi.ipmi.command.Command', autospec=True)
|
|
||||||
def test__send_raw_fail(self, ipmi_mock):
|
|
||||||
ipmicmd = ipmi_mock.return_value
|
|
||||||
ipmicmd.xraw_command.side_effect = pyghmi_exception.IpmiException()
|
|
||||||
self.assertRaises(exception.IPMIFailure, ipminative._send_raw,
|
|
||||||
self.info, '0x01 0x02')
|
|
||||||
|
|
||||||
|
|
||||||
class IPMINativeDriverTestCase(db_base.DbTestCase):
|
|
||||||
"""Test cases for ipminative.NativeIPMIPower class functions."""
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(IPMINativeDriverTestCase, self).setUp()
|
|
||||||
mgr_utils.mock_the_extension_manager(driver="fake_ipminative")
|
|
||||||
self.driver = driver_factory.get_driver("fake_ipminative")
|
|
||||||
|
|
||||||
self.node = obj_utils.create_test_node(self.context,
|
|
||||||
driver='fake_ipminative',
|
|
||||||
driver_info=INFO_DICT)
|
|
||||||
self.info = ipminative._parse_driver_info(self.node)
|
|
||||||
|
|
||||||
def test_get_properties(self):
|
|
||||||
expected = ipminative.COMMON_PROPERTIES
|
|
||||||
self.assertEqual(expected, self.driver.power.get_properties())
|
|
||||||
self.assertEqual(expected, self.driver.management.get_properties())
|
|
||||||
self.assertEqual(expected, self.driver.vendor.get_properties())
|
|
||||||
|
|
||||||
expected = list(ipminative.COMMON_PROPERTIES)
|
|
||||||
expected += list(ipminative.CONSOLE_PROPERTIES)
|
|
||||||
self.assertEqual(sorted(expected),
|
|
||||||
sorted(self.driver.console.get_properties().keys()))
|
|
||||||
self.assertEqual(sorted(expected),
|
|
||||||
sorted(self.driver.get_properties().keys()))
|
|
||||||
|
|
||||||
@mock.patch('pyghmi.ipmi.command.Command', autospec=True)
|
|
||||||
def test_get_power_state(self, ipmi_mock):
|
|
||||||
# Getting the mocked command.
|
|
||||||
cmd_mock = ipmi_mock.return_value
|
|
||||||
# Getting the get power mock.
|
|
||||||
get_power_mock = cmd_mock.get_power
|
|
||||||
|
|
||||||
return_values = [{'powerstate': 'error'},
|
|
||||||
{'powerstate': 'on'},
|
|
||||||
{'powerstate': 'off'}]
|
|
||||||
|
|
||||||
get_power_mock.side_effect = lambda: return_values.pop()
|
|
||||||
|
|
||||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
|
||||||
pstate = self.driver.power.get_power_state(task)
|
|
||||||
self.assertEqual(states.POWER_OFF, pstate)
|
|
||||||
|
|
||||||
pstate = self.driver.power.get_power_state(task)
|
|
||||||
self.assertEqual(states.POWER_ON, pstate)
|
|
||||||
|
|
||||||
pstate = self.driver.power.get_power_state(task)
|
|
||||||
self.assertEqual(states.ERROR, pstate)
|
|
||||||
self.assertEqual(3, get_power_mock.call_count,
|
|
||||||
"pyghmi.ipmi.command.Command.get_power was not"
|
|
||||||
" called 3 times.")
|
|
||||||
|
|
||||||
@mock.patch.object(ipminative, '_power_on', autospec=True)
|
|
||||||
def test_set_power_on_ok(self, power_on_mock):
|
|
||||||
power_on_mock.return_value = states.POWER_ON
|
|
||||||
|
|
||||||
with task_manager.acquire(self.context,
|
|
||||||
self.node.uuid) as task:
|
|
||||||
self.driver.power.set_power_state(
|
|
||||||
task, states.POWER_ON)
|
|
||||||
power_on_mock.assert_called_once_with(self.info)
|
|
||||||
|
|
||||||
@mock.patch.object(driver_utils, 'ensure_next_boot_device', autospec=True)
|
|
||||||
@mock.patch.object(ipminative, '_power_on', autospec=True)
|
|
||||||
def test_set_power_on_with_next_boot(self, power_on_mock, mock_next_boot):
|
|
||||||
power_on_mock.return_value = states.POWER_ON
|
|
||||||
|
|
||||||
with task_manager.acquire(self.context,
|
|
||||||
self.node.uuid) as task:
|
|
||||||
self.driver.power.set_power_state(
|
|
||||||
task, states.POWER_ON)
|
|
||||||
mock_next_boot.assert_called_once_with(task, self.info)
|
|
||||||
power_on_mock.assert_called_once_with(self.info)
|
|
||||||
|
|
||||||
@mock.patch.object(ipminative, '_power_off', autospec=True)
|
|
||||||
def test_set_power_off_ok(self, power_off_mock):
|
|
||||||
power_off_mock.return_value = states.POWER_OFF
|
|
||||||
|
|
||||||
with task_manager.acquire(self.context,
|
|
||||||
self.node.uuid) as task:
|
|
||||||
self.driver.power.set_power_state(
|
|
||||||
task, states.POWER_OFF)
|
|
||||||
power_off_mock.assert_called_once_with(self.info)
|
|
||||||
|
|
||||||
@mock.patch('pyghmi.ipmi.command.Command', autospec=True)
|
|
||||||
def test_set_power_on_fail(self, ipmi_mock):
|
|
||||||
ipmicmd = ipmi_mock.return_value
|
|
||||||
ipmicmd.set_power.return_value = {'powerstate': 'error'}
|
|
||||||
|
|
||||||
self.config(retry_timeout=500, group='ipmi')
|
|
||||||
with task_manager.acquire(self.context,
|
|
||||||
self.node.uuid) as task:
|
|
||||||
self.assertRaises(exception.PowerStateFailure,
|
|
||||||
self.driver.power.set_power_state,
|
|
||||||
task,
|
|
||||||
states.POWER_ON)
|
|
||||||
ipmicmd.set_power.assert_called_once_with('on', 500)
|
|
||||||
|
|
||||||
@mock.patch('pyghmi.ipmi.command.Command', autospec=True)
|
|
||||||
def test_set_boot_device_ok(self, ipmi_mock):
|
|
||||||
ipmicmd = ipmi_mock.return_value
|
|
||||||
ipmicmd.set_bootdev.return_value = None
|
|
||||||
|
|
||||||
with task_manager.acquire(self.context,
|
|
||||||
self.node.uuid) as task:
|
|
||||||
self.driver.management.set_boot_device(task, boot_devices.PXE)
|
|
||||||
# PXE is converted to 'network' internally by ipminative
|
|
||||||
ipmicmd.set_bootdev.assert_called_once_with('network', persist=False,
|
|
||||||
uefiboot=False)
|
|
||||||
|
|
||||||
@mock.patch('pyghmi.ipmi.command.Command', autospec=True)
|
|
||||||
def test_force_set_boot_device_ok(self, ipmi_mock):
|
|
||||||
ipmicmd = ipmi_mock.return_value
|
|
||||||
ipmicmd.set_bootdev.return_value = None
|
|
||||||
|
|
||||||
with task_manager.acquire(self.context,
|
|
||||||
self.node.uuid) as task:
|
|
||||||
task.node.driver_info['ipmi_force_boot_device'] = True
|
|
||||||
self.driver.management.set_boot_device(task, boot_devices.PXE)
|
|
||||||
task.node.refresh()
|
|
||||||
self.assertEqual(
|
|
||||||
False,
|
|
||||||
task.node.driver_internal_info['is_next_boot_persistent']
|
|
||||||
)
|
|
||||||
# PXE is converted to 'network' internally by ipminative
|
|
||||||
ipmicmd.set_bootdev.assert_called_once_with('network', persist=False,
|
|
||||||
uefiboot=False)
|
|
||||||
|
|
||||||
@mock.patch('pyghmi.ipmi.command.Command', autospec=True)
|
|
||||||
def test_set_boot_device_with_persistent(self, ipmi_mock):
|
|
||||||
ipmicmd = ipmi_mock.return_value
|
|
||||||
ipmicmd.set_bootdev.return_value = None
|
|
||||||
|
|
||||||
with task_manager.acquire(self.context,
|
|
||||||
self.node.uuid) as task:
|
|
||||||
task.node.driver_info['ipmi_force_boot_device'] = True
|
|
||||||
self.driver.management.set_boot_device(task,
|
|
||||||
boot_devices.PXE,
|
|
||||||
True)
|
|
||||||
self.assertEqual(
|
|
||||||
boot_devices.PXE,
|
|
||||||
task.node.driver_internal_info['persistent_boot_device'])
|
|
||||||
# PXE is converted to 'network' internally by ipminative
|
|
||||||
ipmicmd.set_bootdev.assert_called_once_with('network', persist=False,
|
|
||||||
uefiboot=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.management.set_boot_device,
|
|
||||||
task,
|
|
||||||
'fake-device')
|
|
||||||
|
|
||||||
@mock.patch.object(deploy_utils, 'get_boot_mode_for_deploy')
|
|
||||||
@mock.patch('pyghmi.ipmi.command.Command', autospec=True)
|
|
||||||
def test_set_boot_device_uefi(self, ipmi_mock, boot_mode_mock):
|
|
||||||
ipmicmd = ipmi_mock.return_value
|
|
||||||
boot_mode_mock.return_value = 'uefi'
|
|
||||||
|
|
||||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
|
||||||
self.driver.management.set_boot_device(task, boot_devices.PXE)
|
|
||||||
# PXE is converted to 'network' internally by ipminative
|
|
||||||
ipmicmd.set_bootdev.assert_called_once_with('network', persist=False,
|
|
||||||
uefiboot=True)
|
|
||||||
|
|
||||||
@mock.patch.object(deploy_utils, 'get_boot_mode_for_deploy')
|
|
||||||
@mock.patch('pyghmi.ipmi.command.Command', autospec=True)
|
|
||||||
def test_set_boot_device_uefi_and_persistent(
|
|
||||||
self, ipmi_mock, boot_mode_mock):
|
|
||||||
ipmicmd = ipmi_mock.return_value
|
|
||||||
boot_mode_mock.return_value = 'uefi'
|
|
||||||
|
|
||||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
|
||||||
self.driver.management.set_boot_device(task, boot_devices.PXE,
|
|
||||||
persistent=True)
|
|
||||||
# PXE is converted to 'network' internally by ipminative
|
|
||||||
ipmicmd.set_bootdev.assert_called_once_with('network', persist=True,
|
|
||||||
uefiboot=True)
|
|
||||||
|
|
||||||
@mock.patch.object(driver_utils, 'ensure_next_boot_device', autospec=True)
|
|
||||||
@mock.patch.object(ipminative, '_reboot', autospec=True)
|
|
||||||
def test_reboot_ok(self, reboot_mock, mock_next_boot):
|
|
||||||
reboot_mock.return_value = None
|
|
||||||
|
|
||||||
with task_manager.acquire(self.context,
|
|
||||||
self.node.uuid) as task:
|
|
||||||
self.driver.power.reboot(task)
|
|
||||||
mock_next_boot.assert_called_once_with(task, self.info)
|
|
||||||
reboot_mock.assert_called_once_with(self.info)
|
|
||||||
|
|
||||||
@mock.patch('pyghmi.ipmi.command.Command', autospec=True)
|
|
||||||
def test_reboot_fail(self, ipmi_mock):
|
|
||||||
ipmicmd = ipmi_mock.return_value
|
|
||||||
ipmicmd.set_power.return_value = {'error': 'Some IPMI error'}
|
|
||||||
|
|
||||||
self.config(retry_timeout=500, group='ipmi')
|
|
||||||
with task_manager.acquire(self.context,
|
|
||||||
self.node.uuid) as task:
|
|
||||||
self.assertRaises(exception.PowerStateFailure,
|
|
||||||
self.driver.power.reboot,
|
|
||||||
task)
|
|
||||||
ipmicmd.set_power.assert_called_once_with('boot', 500)
|
|
||||||
|
|
||||||
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(task)))
|
|
||||||
|
|
||||||
@mock.patch('pyghmi.ipmi.command.Command', autospec=True)
|
|
||||||
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'])
|
|
||||||
self.assertIsNone(bootdev['persistent'])
|
|
||||||
|
|
||||||
@mock.patch('pyghmi.ipmi.command.Command', autospec=True)
|
|
||||||
def test_management_interface_get_boot_device_persistent(self, ipmi_mock):
|
|
||||||
ipmicmd = ipmi_mock.return_value
|
|
||||||
ipmicmd.get_bootdev.return_value = {'bootdev': 'hd',
|
|
||||||
'persistent': True}
|
|
||||||
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'])
|
|
||||||
self.assertTrue(bootdev['persistent'])
|
|
||||||
|
|
||||||
@mock.patch('pyghmi.ipmi.command.Command', autospec=True)
|
|
||||||
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)
|
|
||||||
|
|
||||||
@mock.patch('pyghmi.ipmi.command.Command', autospec=True)
|
|
||||||
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', autospec=True)
|
|
||||||
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_get_force_boot_device_persistent(self):
|
|
||||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
|
||||||
task.node.driver_info['ipmi_force_boot_device'] = True
|
|
||||||
task.node.driver_internal_info['persistent_boot_device'] = 'pxe'
|
|
||||||
bootdev = self.driver.management.get_boot_device(task)
|
|
||||||
self.assertEqual('pxe', bootdev['boot_device'])
|
|
||||||
self.assertTrue(bootdev['persistent'])
|
|
||||||
|
|
||||||
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,
|
|
||||||
uuid=uuidutils.generate_uuid(),
|
|
||||||
driver='fake_ipminative')
|
|
||||||
with task_manager.acquire(self.context, node.uuid) as task:
|
|
||||||
self.assertRaises(exception.MissingParameterValue,
|
|
||||||
task.driver.management.validate, task)
|
|
||||||
|
|
||||||
@mock.patch('pyghmi.ipmi.command.Command', autospec=True)
|
|
||||||
def test_get_sensors_data(self, ipmi_mock):
|
|
||||||
ipmicmd = ipmi_mock.return_value
|
|
||||||
ipmicmd.get_sensor_data.return_value = None
|
|
||||||
|
|
||||||
with task_manager.acquire(self.context,
|
|
||||||
self.node.uuid) as task:
|
|
||||||
self.driver.management.get_sensors_data(task)
|
|
||||||
ipmicmd.get_sensor_data.assert_called_once_with()
|
|
||||||
|
|
||||||
@mock.patch.object(console_utils, 'start_shellinabox_console',
|
|
||||||
autospec=True)
|
|
||||||
def test_start_console(self, mock_start):
|
|
||||||
mock_start.return_value = None
|
|
||||||
|
|
||||||
with task_manager.acquire(self.context,
|
|
||||||
self.node.uuid) as task:
|
|
||||||
self.driver.console.start_console(task)
|
|
||||||
|
|
||||||
mock_start.assert_called_once_with(self.info['uuid'],
|
|
||||||
self.info['port'],
|
|
||||||
mock.ANY)
|
|
||||||
self.assertTrue(mock_start.called)
|
|
||||||
|
|
||||||
@mock.patch.object(console_utils, 'start_shellinabox_console',
|
|
||||||
autospec=True)
|
|
||||||
def test_start_console_fail(self, mock_start):
|
|
||||||
mock_start.side_effect = exception.ConsoleSubprocessFailed(
|
|
||||||
error='error')
|
|
||||||
|
|
||||||
with task_manager.acquire(self.context,
|
|
||||||
self.node.uuid) as task:
|
|
||||||
self.assertRaises(exception.ConsoleSubprocessFailed,
|
|
||||||
self.driver.console.start_console,
|
|
||||||
task)
|
|
||||||
|
|
||||||
self.assertTrue(mock_start.called)
|
|
||||||
|
|
||||||
@mock.patch.object(console_utils, 'stop_shellinabox_console',
|
|
||||||
autospec=True)
|
|
||||||
def test_stop_console(self, mock_stop):
|
|
||||||
mock_stop.return_value = None
|
|
||||||
|
|
||||||
with task_manager.acquire(self.context,
|
|
||||||
self.node['uuid']) as task:
|
|
||||||
self.driver.console.stop_console(task)
|
|
||||||
|
|
||||||
mock_stop.assert_called_once_with(self.info['uuid'])
|
|
||||||
|
|
||||||
@mock.patch.object(console_utils, 'stop_shellinabox_console',
|
|
||||||
autospec=True)
|
|
||||||
def test_stop_console_fail(self, mock_stop):
|
|
||||||
mock_stop.side_effect = exception.ConsoleError()
|
|
||||||
|
|
||||||
with task_manager.acquire(self.context,
|
|
||||||
self.node.uuid) as task:
|
|
||||||
self.assertRaises(exception.ConsoleError,
|
|
||||||
self.driver.console.stop_console,
|
|
||||||
task)
|
|
||||||
|
|
||||||
mock_stop.assert_called_once_with(self.node.uuid)
|
|
||||||
|
|
||||||
@mock.patch.object(console_utils, 'get_shellinabox_console_url',
|
|
||||||
autospec=True)
|
|
||||||
def test_get_console(self, mock_get_url):
|
|
||||||
url = 'http://localhost:4201'
|
|
||||||
mock_get_url.return_value = url
|
|
||||||
expected = {'type': 'shellinabox', 'url': url}
|
|
||||||
|
|
||||||
with task_manager.acquire(self.context,
|
|
||||||
self.node.uuid) as task:
|
|
||||||
console_info = self.driver.console.get_console(task)
|
|
||||||
|
|
||||||
self.assertEqual(expected, console_info)
|
|
||||||
mock_get_url.assert_called_once_with(self.info['port'])
|
|
||||||
|
|
||||||
@mock.patch.object(ipminative, '_parse_driver_info', autospec=True)
|
|
||||||
@mock.patch.object(ipminative, '_parse_raw_bytes', autospec=True)
|
|
||||||
def test_vendor_passthru_validate__send_raw_bytes_good(self, mock_raw,
|
|
||||||
mock_driver):
|
|
||||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
|
||||||
self.driver.vendor.validate(task,
|
|
||||||
method='send_raw',
|
|
||||||
http_method='POST',
|
|
||||||
raw_bytes='0x00 0x01')
|
|
||||||
mock_raw.assert_called_once_with('0x00 0x01')
|
|
||||||
mock_driver.assert_called_once_with(task.node)
|
|
||||||
|
|
||||||
def test_vendor_passthru_validate__send_raw_bytes_fail(self):
|
|
||||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
|
||||||
self.assertRaises(exception.MissingParameterValue,
|
|
||||||
self.driver.vendor.validate,
|
|
||||||
task, method='send_raw')
|
|
||||||
|
|
||||||
def test_vendor_passthru_vendor_routes(self):
|
|
||||||
expected = ['send_raw', 'bmc_reset']
|
|
||||||
with task_manager.acquire(self.context, self.node.uuid,
|
|
||||||
shared=True) as task:
|
|
||||||
vendor_routes = task.driver.vendor.vendor_routes
|
|
||||||
self.assertIsInstance(vendor_routes, dict)
|
|
||||||
self.assertEqual(sorted(expected), sorted(vendor_routes))
|
|
||||||
|
|
||||||
@mock.patch.object(ipminative, '_send_raw', autospec=True)
|
|
||||||
def test_send_raw(self, send_raw_mock):
|
|
||||||
bytes = '0x00 0x01'
|
|
||||||
with task_manager.acquire(self.context,
|
|
||||||
self.node.uuid) as task:
|
|
||||||
self.driver.vendor.send_raw(task, http_method='POST',
|
|
||||||
raw_bytes=bytes)
|
|
||||||
|
|
||||||
send_raw_mock.assert_called_once_with(self.info, bytes)
|
|
||||||
|
|
||||||
@mock.patch.object(ipminative, '_send_raw', autospec=True)
|
|
||||||
def _test_bmc_reset(self, warm, expected_bytes, send_raw_mock):
|
|
||||||
with task_manager.acquire(self.context,
|
|
||||||
self.node.uuid) as task:
|
|
||||||
self.driver.vendor.bmc_reset(task, http_method='POST', warm=warm)
|
|
||||||
|
|
||||||
send_raw_mock.assert_called_once_with(self.info, expected_bytes)
|
|
||||||
|
|
||||||
def test_bmc_reset_cold(self):
|
|
||||||
for param in (False, 'false', 'off', 'n', 'no'):
|
|
||||||
self._test_bmc_reset(param, '0x06 0x02')
|
|
||||||
|
|
||||||
def test_bmc_reset_warm(self):
|
|
||||||
for param in (True, 'true', 'on', 'y', 'yes'):
|
|
||||||
self._test_bmc_reset(param, '0x06 0x03')
|
|
@ -28,7 +28,6 @@ from ironic.drivers.modules.ilo import inspect as ilo_inspect
|
|||||||
from ironic.drivers.modules.ilo import management as ilo_management
|
from ironic.drivers.modules.ilo import management as ilo_management
|
||||||
from ironic.drivers.modules.ilo import power as ilo_power
|
from ironic.drivers.modules.ilo import power as ilo_power
|
||||||
from ironic.drivers.modules.ilo import vendor as ilo_vendor
|
from ironic.drivers.modules.ilo import vendor as ilo_vendor
|
||||||
from ironic.drivers.modules import ipminative
|
|
||||||
from ironic.drivers.modules import ipmitool
|
from ironic.drivers.modules import ipmitool
|
||||||
from ironic.drivers.modules.irmc import management as irmc_management
|
from ironic.drivers.modules.irmc import management as irmc_management
|
||||||
from ironic.drivers.modules.irmc import power as irmc_power
|
from ironic.drivers.modules.irmc import power as irmc_power
|
||||||
@ -53,32 +52,6 @@ class PXEDriversTestCase(testtools.TestCase):
|
|||||||
self.assertIsNone(driver.inspect)
|
self.assertIsNone(driver.inspect)
|
||||||
self.assertIsInstance(driver.raid, agent.AgentRAID)
|
self.assertIsInstance(driver.raid, agent.AgentRAID)
|
||||||
|
|
||||||
@mock.patch.object(pxe.importutils, 'try_import', spec_set=True,
|
|
||||||
autospec=True)
|
|
||||||
def test_pxe_ipminative_driver(self, try_import_mock):
|
|
||||||
try_import_mock.return_value = True
|
|
||||||
|
|
||||||
driver = pxe.PXEAndIPMINativeDriver()
|
|
||||||
|
|
||||||
self.assertIsInstance(driver.power, ipminative.NativeIPMIPower)
|
|
||||||
self.assertIsInstance(driver.console,
|
|
||||||
ipminative.NativeIPMIShellinaboxConsole)
|
|
||||||
self.assertIsInstance(driver.boot, pxe_module.PXEBoot)
|
|
||||||
self.assertIsInstance(driver.deploy, iscsi_deploy.ISCSIDeploy)
|
|
||||||
self.assertIsInstance(driver.management,
|
|
||||||
ipminative.NativeIPMIManagement)
|
|
||||||
self.assertIsInstance(driver.vendor, ipminative.VendorPassthru)
|
|
||||||
self.assertIsNone(driver.inspect)
|
|
||||||
self.assertIsInstance(driver.raid, agent.AgentRAID)
|
|
||||||
|
|
||||||
@mock.patch.object(pxe.importutils, 'try_import', spec_set=True,
|
|
||||||
autospec=True)
|
|
||||||
def test_pxe_ipminative_driver_import_error(self, try_import_mock):
|
|
||||||
try_import_mock.return_value = False
|
|
||||||
|
|
||||||
self.assertRaises(exception.DriverLoadError,
|
|
||||||
pxe.PXEAndIPMINativeDriver)
|
|
||||||
|
|
||||||
@mock.patch.object(pxe.importutils, 'try_import', spec_set=True,
|
@mock.patch.object(pxe.importutils, 'try_import', spec_set=True,
|
||||||
autospec=True)
|
autospec=True)
|
||||||
def test_pxe_ilo_driver(self, try_import_mock):
|
def test_pxe_ilo_driver(self, try_import_mock):
|
||||||
|
@ -57,22 +57,6 @@ PROLIANTUTILS_SPEC = (
|
|||||||
'utils',
|
'utils',
|
||||||
)
|
)
|
||||||
|
|
||||||
# pyghmi
|
|
||||||
PYGHMI_SPEC = (
|
|
||||||
'exceptions',
|
|
||||||
'ipmi',
|
|
||||||
)
|
|
||||||
PYGHMI_EXC_SPEC = (
|
|
||||||
'IpmiException',
|
|
||||||
)
|
|
||||||
PYGHMI_IPMI_SPEC = (
|
|
||||||
'command',
|
|
||||||
)
|
|
||||||
PYGHMI_IPMICMD_SPEC = (
|
|
||||||
'boot_devices',
|
|
||||||
'Command',
|
|
||||||
)
|
|
||||||
|
|
||||||
# pywsman
|
# pywsman
|
||||||
PYWSMAN_SPEC = (
|
PYWSMAN_SPEC = (
|
||||||
'Client',
|
'Client',
|
||||||
|
@ -22,7 +22,6 @@ respective external libraries' actually being present.
|
|||||||
Any external library required by a third-party driver should be mocked here.
|
Any external library required by a third-party driver should be mocked here.
|
||||||
Current list of mocked libraries:
|
Current list of mocked libraries:
|
||||||
|
|
||||||
- ipminative
|
|
||||||
- proliantutils
|
- proliantutils
|
||||||
- pysnmp
|
- pysnmp
|
||||||
- scciclient
|
- scciclient
|
||||||
@ -49,26 +48,6 @@ ipmitool.TIMING_SUPPORT = False
|
|||||||
ipmitool.DUAL_BRIDGE_SUPPORT = False
|
ipmitool.DUAL_BRIDGE_SUPPORT = False
|
||||||
ipmitool.SINGLE_BRIDGE_SUPPORT = False
|
ipmitool.SINGLE_BRIDGE_SUPPORT = False
|
||||||
|
|
||||||
pyghmi = importutils.try_import("pyghmi")
|
|
||||||
if not pyghmi:
|
|
||||||
p = mock.MagicMock(spec_set=mock_specs.PYGHMI_SPEC)
|
|
||||||
p.exceptions = mock.MagicMock(spec_set=mock_specs.PYGHMI_EXC_SPEC)
|
|
||||||
p.exceptions.IpmiException = Exception
|
|
||||||
p.ipmi = mock.MagicMock(spec_set=mock_specs.PYGHMI_IPMI_SPEC)
|
|
||||||
p.ipmi.command = mock.MagicMock(spec_set=mock_specs.PYGHMI_IPMICMD_SPEC)
|
|
||||||
p.ipmi.command.Command = mock.MagicMock(spec_set=[])
|
|
||||||
sys.modules['pyghmi'] = p
|
|
||||||
sys.modules['pyghmi.exceptions'] = p.exceptions
|
|
||||||
sys.modules['pyghmi.ipmi'] = p.ipmi
|
|
||||||
sys.modules['pyghmi.ipmi.command'] = p.ipmi.command
|
|
||||||
# FIXME(deva): the next line is a hack, because several unit tests
|
|
||||||
# actually depend on this particular string being present
|
|
||||||
# in pyghmi.ipmi.command.boot_devices
|
|
||||||
p.ipmi.command.boot_devices = {'pxe': 4}
|
|
||||||
|
|
||||||
if 'ironic.drivers.modules.ipminative' in sys.modules:
|
|
||||||
six.moves.reload_module(sys.modules['ironic.drivers.modules.ipminative'])
|
|
||||||
|
|
||||||
proliantutils = importutils.try_import('proliantutils')
|
proliantutils = importutils.try_import('proliantutils')
|
||||||
if not proliantutils:
|
if not proliantutils:
|
||||||
proliantutils = mock.MagicMock(spec_set=mock_specs.PROLIANTUTILS_SPEC)
|
proliantutils = mock.MagicMock(spec_set=mock_specs.PROLIANTUTILS_SPEC)
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
upgrade:
|
||||||
|
- |
|
||||||
|
The agent_pyghmi, pxe_ipminative, and fake_ipminative drivers have all
|
||||||
|
been removed from ironic due to lack of testing. Nodes using these
|
||||||
|
drivers should be changed to the agent_ipmitool or pxe_ipmitool driver.
|
@ -47,7 +47,6 @@ ironic.drivers =
|
|||||||
agent_ipmitool_socat = ironic.drivers.ipmi:AgentAndIPMIToolAndSocatDriver
|
agent_ipmitool_socat = ironic.drivers.ipmi:AgentAndIPMIToolAndSocatDriver
|
||||||
agent_irmc = ironic.drivers.irmc:IRMCVirtualMediaAgentDriver
|
agent_irmc = ironic.drivers.irmc:IRMCVirtualMediaAgentDriver
|
||||||
agent_pxe_oneview = ironic.drivers.oneview:AgentPXEOneViewDriver
|
agent_pxe_oneview = ironic.drivers.oneview:AgentPXEOneViewDriver
|
||||||
agent_pyghmi = ironic.drivers.agent:AgentAndIPMINativeDriver
|
|
||||||
agent_ssh = ironic.drivers.agent:AgentAndSSHDriver
|
agent_ssh = ironic.drivers.agent:AgentAndSSHDriver
|
||||||
agent_ucs = ironic.drivers.agent:AgentAndUcsDriver
|
agent_ucs = ironic.drivers.agent:AgentAndUcsDriver
|
||||||
fake = ironic.drivers.fake:FakeDriver
|
fake = ironic.drivers.fake:FakeDriver
|
||||||
@ -56,7 +55,6 @@ ironic.drivers =
|
|||||||
fake_inspector = ironic.drivers.fake:FakeIPMIToolInspectorDriver
|
fake_inspector = ironic.drivers.fake:FakeIPMIToolInspectorDriver
|
||||||
fake_ipmitool = ironic.drivers.fake:FakeIPMIToolDriver
|
fake_ipmitool = ironic.drivers.fake:FakeIPMIToolDriver
|
||||||
fake_ipmitool_socat = ironic.drivers.fake:FakeIPMIToolSocatDriver
|
fake_ipmitool_socat = ironic.drivers.fake:FakeIPMIToolSocatDriver
|
||||||
fake_ipminative = ironic.drivers.fake:FakeIPMINativeDriver
|
|
||||||
fake_ssh = ironic.drivers.fake:FakeSSHDriver
|
fake_ssh = ironic.drivers.fake:FakeSSHDriver
|
||||||
fake_pxe = ironic.drivers.fake:FakePXEDriver
|
fake_pxe = ironic.drivers.fake:FakePXEDriver
|
||||||
fake_ilo = ironic.drivers.fake:FakeIloDriver
|
fake_ilo = ironic.drivers.fake:FakeIloDriver
|
||||||
@ -71,7 +69,6 @@ ironic.drivers =
|
|||||||
iscsi_pxe_oneview = ironic.drivers.oneview:ISCSIPXEOneViewDriver
|
iscsi_pxe_oneview = ironic.drivers.oneview:ISCSIPXEOneViewDriver
|
||||||
pxe_ipmitool = ironic.drivers.ipmi:PXEAndIPMIToolDriver
|
pxe_ipmitool = ironic.drivers.ipmi:PXEAndIPMIToolDriver
|
||||||
pxe_ipmitool_socat = ironic.drivers.ipmi:PXEAndIPMIToolAndSocatDriver
|
pxe_ipmitool_socat = ironic.drivers.ipmi:PXEAndIPMIToolAndSocatDriver
|
||||||
pxe_ipminative = ironic.drivers.pxe:PXEAndIPMINativeDriver
|
|
||||||
pxe_ssh = ironic.drivers.pxe:PXEAndSSHDriver
|
pxe_ssh = ironic.drivers.pxe:PXEAndSSHDriver
|
||||||
pxe_ilo = ironic.drivers.pxe:PXEAndIloDriver
|
pxe_ilo = ironic.drivers.pxe:PXEAndIloDriver
|
||||||
pxe_drac = ironic.drivers.drac:PXEDracDriver
|
pxe_drac = ironic.drivers.drac:PXEDracDriver
|
||||||
|
Loading…
Reference in New Issue
Block a user