Ilya Etingof 9efb9e313d Kill misbehaving ipmitool process
We can't trust ipmitool to terminate in time. We may have to kill
the process if it's running for longer than we asked it to.

On the other hand, abrupt IPMI exchange termination is said to be
dangerous to the state of the BMC being managed. Therefore this patch
only kills timed out IPMI "power status" call.

For the purpose of killing hung `ipmitool` we inject the time-capped
`popen.wait` call before the uncapped `popen.communicate` is called
internally. Then just kill stuck `ipmitool` process and go on.

Story: 2004449
Task: 28127
Change-Id: I7e1eafb334fe3a3337926aca27c14fe559ce0e39
2018-12-05 09:19:59 +01:00

2730 lines
120 KiB
Python

# coding=utf-8
# Copyright 2012 Hewlett-Packard Development Company, L.P.
# Copyright (c) 2012 NTT DOCOMO, INC.
# Copyright 2014 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 IPMITool driver module."""
import contextlib
import os
import random
import stat
import subprocess
import tempfile
import time
import types
import fixtures
from ironic_lib import utils as ironic_utils
import mock
from oslo_concurrency import processutils
from oslo_utils import uuidutils
import six
from ironic.common import boot_devices
from ironic.common import exception
from ironic.common import states
from ironic.common import utils
from ironic.conductor import task_manager
import ironic.conf
from ironic.drivers.modules import boot_mode_utils
from ironic.drivers.modules import console_utils
from ironic.drivers.modules import ipmitool as ipmi
from ironic.drivers import utils as driver_utils
from ironic.tests import base
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
CONF = ironic.conf.CONF
INFO_DICT = db_utils.get_test_ipmi_info()
# BRIDGE_INFO_DICT will have all the bridging parameters appended
BRIDGE_INFO_DICT = INFO_DICT.copy()
BRIDGE_INFO_DICT.update(db_utils.get_test_ipmi_bridging_parameters())
class IPMIToolCheckInitTestCase(base.TestCase):
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(utils, 'check_dir', autospec=True)
def test_power_init_calls(self, mock_check_dir, mock_support):
mock_support.return_value = True
ipmi.TMP_DIR_CHECKED = None
ipmi.IPMIPower()
mock_support.assert_called_with(mock.ANY)
mock_check_dir.assert_called_once_with()
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(utils, 'check_dir', autospec=True)
def test_power_init_calls_raises_1(self, mock_check_dir, mock_support):
mock_support.return_value = True
ipmi.TMP_DIR_CHECKED = None
mock_check_dir.side_effect = exception.PathNotFound(dir="foo_dir")
self.assertRaises(exception.PathNotFound, ipmi.IPMIPower)
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(utils, 'check_dir', autospec=True)
def test_power_init_calls_raises_2(self, mock_check_dir, mock_support):
mock_support.return_value = True
ipmi.TMP_DIR_CHECKED = None
mock_check_dir.side_effect = exception.DirectoryNotWritable(
dir="foo_dir")
self.assertRaises(exception.DirectoryNotWritable, ipmi.IPMIPower)
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(utils, 'check_dir', autospec=True)
def test_power_init_calls_raises_3(self, mock_check_dir, mock_support):
mock_support.return_value = True
ipmi.TMP_DIR_CHECKED = None
mock_check_dir.side_effect = exception.InsufficientDiskSpace(
path="foo_dir", required=1, actual=0)
self.assertRaises(exception.InsufficientDiskSpace, ipmi.IPMIPower)
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(utils, 'check_dir', autospec=True)
def test_power_init_calls_already_checked(self,
mock_check_dir,
mock_support):
mock_support.return_value = True
ipmi.TMP_DIR_CHECKED = True
ipmi.IPMIPower()
mock_support.assert_called_with(mock.ANY)
self.assertFalse(mock_check_dir.called)
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(utils, 'check_dir', autospec=True)
def test_management_init_calls(self, mock_check_dir, mock_support):
mock_support.return_value = True
ipmi.TMP_DIR_CHECKED = None
ipmi.IPMIManagement()
mock_support.assert_called_with(mock.ANY)
mock_check_dir.assert_called_once_with()
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(utils, 'check_dir', autospec=True)
def test_management_init_calls_already_checked(self,
mock_check_dir,
mock_support):
mock_support.return_value = True
ipmi.TMP_DIR_CHECKED = False
ipmi.IPMIManagement()
mock_support.assert_called_with(mock.ANY)
self.assertFalse(mock_check_dir.called)
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(utils, 'check_dir', autospec=True)
def test_vendor_passthru_init_calls(self, mock_check_dir, mock_support):
mock_support.return_value = True
ipmi.TMP_DIR_CHECKED = None
ipmi.VendorPassthru()
mock_support.assert_called_with(mock.ANY)
mock_check_dir.assert_called_once_with()
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(utils, 'check_dir', autospec=True)
def test_vendor_passthru_init_calls_already_checked(self,
mock_check_dir,
mock_support):
mock_support.return_value = True
ipmi.TMP_DIR_CHECKED = True
ipmi.VendorPassthru()
mock_support.assert_called_with(mock.ANY)
self.assertFalse(mock_check_dir.called)
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(utils, 'check_dir', autospec=True)
def test_console_init_calls(self, mock_check_dir, mock_support):
mock_support.return_value = True
ipmi.TMP_DIR_CHECKED = None
ipmi.IPMIShellinaboxConsole()
mock_support.assert_called_with(mock.ANY)
mock_check_dir.assert_called_once_with()
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(utils, 'check_dir', autospec=True)
def test_console_init_calls_already_checked(self,
mock_check_dir,
mock_support):
mock_support.return_value = True
ipmi.TMP_DIR_CHECKED = True
ipmi.IPMIShellinaboxConsole()
mock_support.assert_called_with(mock.ANY)
self.assertFalse(mock_check_dir.called)
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(utils, 'check_dir', autospec=True)
def test_console_init_calls_for_socat(self, mock_check_dir, mock_support):
with mock.patch.object(ipmi, 'TMP_DIR_CHECKED'):
mock_support.return_value = True
ipmi.TMP_DIR_CHECKED = None
ipmi.IPMISocatConsole()
mock_support.assert_called_with(mock.ANY)
mock_check_dir.assert_called_once_with()
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(utils, 'check_dir', autospec=True)
def test_console_init_calls_for_socat_already_checked(self,
mock_check_dir,
mock_support):
with mock.patch.object(ipmi, 'TMP_DIR_CHECKED'):
mock_support.return_value = True
ipmi.TMP_DIR_CHECKED = True
ipmi.IPMISocatConsole()
mock_support.assert_called_with(mock.ANY)
self.assertFalse(mock_check_dir.called)
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(subprocess, 'check_call', autospec=True)
class IPMIToolCheckOptionSupportedTestCase(base.TestCase):
def test_check_timing_pass(self, mock_chkcall, mock_support):
mock_chkcall.return_value = (None, None)
mock_support.return_value = None
expected = [mock.call('timing'),
mock.call('timing', True)]
ipmi._check_option_support(['timing'])
self.assertTrue(mock_chkcall.called)
self.assertEqual(expected, mock_support.call_args_list)
def test_check_timing_fail(self, mock_chkcall, mock_support):
mock_chkcall.side_effect = subprocess.CalledProcessError(1, 'ipmitool')
mock_support.return_value = None
expected = [mock.call('timing'),
mock.call('timing', False)]
ipmi._check_option_support(['timing'])
self.assertTrue(mock_chkcall.called)
self.assertEqual(expected, mock_support.call_args_list)
def test_check_timing_no_ipmitool(self, mock_chkcall, mock_support):
mock_chkcall.side_effect = OSError()
mock_support.return_value = None
expected = [mock.call('timing')]
self.assertRaises(OSError, ipmi._check_option_support, ['timing'])
self.assertTrue(mock_chkcall.called)
self.assertEqual(expected, mock_support.call_args_list)
def test_check_single_bridge_pass(self, mock_chkcall, mock_support):
mock_chkcall.return_value = (None, None)
mock_support.return_value = None
expected = [mock.call('single_bridge'),
mock.call('single_bridge', True)]
ipmi._check_option_support(['single_bridge'])
self.assertTrue(mock_chkcall.called)
self.assertEqual(expected, mock_support.call_args_list)
def test_check_single_bridge_fail(self, mock_chkcall, mock_support):
mock_chkcall.side_effect = subprocess.CalledProcessError(1, 'ipmitool')
mock_support.return_value = None
expected = [mock.call('single_bridge'),
mock.call('single_bridge', False)]
ipmi._check_option_support(['single_bridge'])
self.assertTrue(mock_chkcall.called)
self.assertEqual(expected, mock_support.call_args_list)
def test_check_single_bridge_no_ipmitool(self, mock_chkcall,
mock_support):
mock_chkcall.side_effect = OSError()
mock_support.return_value = None
expected = [mock.call('single_bridge')]
self.assertRaises(OSError, ipmi._check_option_support,
['single_bridge'])
self.assertTrue(mock_chkcall.called)
self.assertEqual(expected, mock_support.call_args_list)
def test_check_dual_bridge_pass(self, mock_chkcall, mock_support):
mock_chkcall.return_value = (None, None)
mock_support.return_value = None
expected = [mock.call('dual_bridge'),
mock.call('dual_bridge', True)]
ipmi._check_option_support(['dual_bridge'])
self.assertTrue(mock_chkcall.called)
self.assertEqual(expected, mock_support.call_args_list)
def test_check_dual_bridge_fail(self, mock_chkcall, mock_support):
mock_chkcall.side_effect = subprocess.CalledProcessError(1, 'ipmitool')
mock_support.return_value = None
expected = [mock.call('dual_bridge'),
mock.call('dual_bridge', False)]
ipmi._check_option_support(['dual_bridge'])
self.assertTrue(mock_chkcall.called)
self.assertEqual(expected, mock_support.call_args_list)
def test_check_dual_bridge_no_ipmitool(self, mock_chkcall, mock_support):
mock_chkcall.side_effect = OSError()
mock_support.return_value = None
expected = [mock.call('dual_bridge')]
self.assertRaises(OSError, ipmi._check_option_support,
['dual_bridge'])
self.assertTrue(mock_chkcall.called)
self.assertEqual(expected, mock_support.call_args_list)
def test_check_all_options_pass(self, mock_chkcall, mock_support):
mock_chkcall.return_value = (None, None)
mock_support.return_value = None
expected = [
mock.call('timing'), mock.call('timing', True),
mock.call('single_bridge'),
mock.call('single_bridge', True),
mock.call('dual_bridge'), mock.call('dual_bridge', True)]
ipmi._check_option_support(['timing', 'single_bridge', 'dual_bridge'])
self.assertTrue(mock_chkcall.called)
self.assertEqual(expected, mock_support.call_args_list)
def test_check_all_options_fail(self, mock_chkcall, mock_support):
options = ['timing', 'single_bridge', 'dual_bridge']
mock_chkcall.side_effect = [subprocess.CalledProcessError(
1, 'ipmitool')] * len(options)
mock_support.return_value = None
expected = [
mock.call('timing'), mock.call('timing', False),
mock.call('single_bridge'),
mock.call('single_bridge', False),
mock.call('dual_bridge'),
mock.call('dual_bridge', False)]
ipmi._check_option_support(options)
self.assertTrue(mock_chkcall.called)
self.assertEqual(expected, mock_support.call_args_list)
def test_check_all_options_no_ipmitool(self, mock_chkcall, mock_support):
mock_chkcall.side_effect = OSError()
mock_support.return_value = None
# exception is raised once ipmitool was not found for an command
expected = [mock.call('timing')]
self.assertRaises(OSError, ipmi._check_option_support,
['timing', 'single_bridge', 'dual_bridge'])
self.assertTrue(mock_chkcall.called)
self.assertEqual(expected, mock_support.call_args_list)
awesome_password_filename = 'awesome_password_filename'
@contextlib.contextmanager
def _make_password_file_stub(password):
yield awesome_password_filename
class IPMIToolPrivateMethodTestCaseMeta(type):
"""Generate and inject parametrized test cases"""
ipmitool_errors = [
'insufficient resources for session',
'Node busy',
'Timeout',
'Out of space',
'BMC initialization in progress'
]
def __new__(mcs, name, bases, attrs):
def gen_test_methods(message):
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(utils, 'execute', autospec=True)
def exec_ipmitool_exception_retry(
self, mock_exec, mock_support):
ipmi.LAST_CMD_TIME = {}
mock_support.return_value = False
mock_exec.side_effect = [
processutils.ProcessExecutionError(
stderr=message
),
(None, None)
]
# Directly set the configuration values such that
# the logic will cause _exec_ipmitool to retry twice.
self.config(min_command_interval=1, group='ipmi')
self.config(command_retry_timeout=2, group='ipmi')
ipmi._exec_ipmitool(self.info, 'A B C')
mock_support.assert_called_once_with('timing')
self.assertEqual(2, mock_exec.call_count)
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(utils, 'execute', autospec=True)
def exec_ipmitool_exception_retries_exceeded(
self, mock_exec, mock_support):
ipmi.LAST_CMD_TIME = {}
mock_support.return_value = False
mock_exec.side_effect = [processutils.ProcessExecutionError(
stderr=message
)]
# Directly set the configuration values such that
# the logic will cause _exec_ipmitool to timeout.
self.config(min_command_interval=1, group='ipmi')
self.config(command_retry_timeout=1, group='ipmi')
self.assertRaises(processutils.ProcessExecutionError,
ipmi._exec_ipmitool,
self.info, 'A B C')
mock_support.assert_called_once_with('timing')
self.assertEqual(1, mock_exec.call_count)
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(utils, 'execute', autospec=True)
def exec_ipmitool_exception_non_retryable_failure(
self, mock_exec, mock_support):
ipmi.LAST_CMD_TIME = {}
mock_support.return_value = False
# Return a retryable error, then an error that cannot
# be retried thus resulting in a single retry
# attempt by _exec_ipmitool.
mock_exec.side_effect = [
processutils.ProcessExecutionError(
stderr=message
),
processutils.ProcessExecutionError(
stderr="Unknown"
),
]
# Directly set the configuration values such that
# the logic will cause _exec_ipmitool to retry up
# to 3 times.
self.config(min_command_interval=1, group='ipmi')
self.config(command_retry_timeout=3, group='ipmi')
self.assertRaises(processutils.ProcessExecutionError,
ipmi._exec_ipmitool,
self.info, 'A B C')
mock_support.assert_called_once_with('timing')
self.assertEqual(2, mock_exec.call_count)
return (exec_ipmitool_exception_retry,
exec_ipmitool_exception_retries_exceeded,
exec_ipmitool_exception_non_retryable_failure)
# NOTE(etingof): this loop will inject some methods into the
# class being built to be then picked up by unittest to test
# ironic's handling of specific `ipmitool` errors
for ipmi_message in mcs.ipmitool_errors:
for fun in gen_test_methods(ipmi_message):
suffix = ipmi_message.lower().replace(' ', '_')
test_name = "test_%s_%s" % (fun.__name__, suffix)
attrs[test_name] = fun
return type.__new__(mcs, name, bases, attrs)
class Base(db_base.DbTestCase):
def setUp(self):
super(Base, self).setUp()
self.config(enabled_power_interfaces=['fake', 'ipmitool'],
enabled_management_interfaces=['fake', 'ipmitool'],
enabled_vendor_interfaces=['fake', 'ipmitool',
'no-vendor'],
enabled_console_interfaces=['fake', 'ipmitool-socat',
'ipmitool-shellinabox',
'no-console'])
self.node = obj_utils.create_test_node(
self.context,
console_interface='ipmitool-socat',
management_interface='ipmitool',
power_interface='ipmitool',
vendor_interface='ipmitool',
driver_info=INFO_DICT)
self.info = ipmi._parse_driver_info(self.node)
self.console = ipmi.IPMISocatConsole()
self.management = ipmi.IPMIManagement()
self.power = ipmi.IPMIPower()
self.vendor = ipmi.VendorPassthru()
@six.add_metaclass(IPMIToolPrivateMethodTestCaseMeta)
class IPMIToolPrivateMethodTestCase(Base):
def setUp(self):
super(IPMIToolPrivateMethodTestCase, self).setUp()
# power actions use oslo_service.BackoffLoopingCall,
# mock random.SystemRandom gauss distribution
self._mock_system_random_distribution()
mock_sleep_fixture = self.useFixture(
fixtures.MockPatchObject(time, 'sleep', autospec=True))
self.mock_sleep = mock_sleep_fixture.mock
# NOTE(etingof): besides the conventional unittest methods that follow,
# the metaclass will inject some more `test_` methods aimed at testing
# the handling of specific errors potentially returned by the `ipmitool`
def _mock_system_random_distribution(self):
# random.SystemRandom with gauss distribution is used by oslo_service's
# BackoffLoopingCall, it multiplies default interval (equals to 1) by
# 2 * return_value, so if you want BackoffLoopingCall to "sleep" for
# 1 second, return_value should be 0.5.
m = mock.patch.object(random.SystemRandom, 'gauss', return_value=0.5)
m.start()
self.addCleanup(m.stop)
def _test__make_password_file(self, input_password,
exception_to_raise=None):
pw_file = None
try:
with ipmi._make_password_file(input_password) as pw_file:
if exception_to_raise is not None:
raise exception_to_raise
self.assertTrue(os.path.isfile(pw_file))
self.assertEqual(0o600, os.stat(pw_file)[stat.ST_MODE] & 0o777)
with open(pw_file, "r") as f:
password = f.read()
self.assertEqual(str(input_password), password)
finally:
if pw_file is not None:
self.assertFalse(os.path.isfile(pw_file))
def test__make_password_file_str_password(self):
self._test__make_password_file(self.info['password'])
def test__make_password_file_with_numeric_password(self):
self._test__make_password_file(12345)
def test__make_password_file_caller_exception(self):
# Test caller raising exception
result = self.assertRaises(
ValueError,
self._test__make_password_file,
12345, ValueError('we should fail'))
self.assertEqual('we should fail', six.text_type(result))
@mock.patch.object(tempfile, 'NamedTemporaryFile',
new=mock.MagicMock(side_effect=OSError('Test Error')))
def test__make_password_file_tempfile_known_exception(self):
# Test OSError exception in _make_password_file for
# tempfile.NamedTemporaryFile
self.assertRaises(
exception.PasswordFileFailedToCreate,
self._test__make_password_file, 12345)
@mock.patch.object(
tempfile, 'NamedTemporaryFile',
new=mock.MagicMock(side_effect=OverflowError('Test Error')))
def test__make_password_file_tempfile_unknown_exception(self):
# Test exception in _make_password_file for tempfile.NamedTemporaryFile
result = self.assertRaises(
OverflowError,
self._test__make_password_file, 12345)
self.assertEqual('Test Error', six.text_type(result))
def test__make_password_file_write_exception(self):
# Test exception in _make_password_file for write()
mock_namedtemp = mock.mock_open(mock.MagicMock(name='JLV'))
with mock.patch('tempfile.NamedTemporaryFile', mock_namedtemp):
mock_filehandle = mock_namedtemp.return_value
mock_write = mock_filehandle.write
mock_write.side_effect = OSError('Test 2 Error')
self.assertRaises(
exception.PasswordFileFailedToCreate,
self._test__make_password_file, 12345)
def test__parse_driver_info(self):
# make sure we get back the expected things
_OPTIONS = ['address', 'username', 'password', 'uuid']
for option in _OPTIONS:
self.assertIsNotNone(self.info[option])
info = dict(INFO_DICT)
# test the default value for 'priv_level'
node = obj_utils.get_test_node(self.context, driver_info=info)
ret = ipmi._parse_driver_info(node)
self.assertEqual('ADMINISTRATOR', ret['priv_level'])
# ipmi_username / ipmi_password are not mandatory
del info['ipmi_username']
node = obj_utils.get_test_node(self.context, driver_info=info)
ipmi._parse_driver_info(node)
del info['ipmi_password']
node = obj_utils.get_test_node(self.context, driver_info=info)
ipmi._parse_driver_info(node)
# make sure error is raised when ipmi_address is missing
info = dict(INFO_DICT)
del info['ipmi_address']
node = obj_utils.get_test_node(self.context, driver_info=info)
self.assertRaises(exception.MissingParameterValue,
ipmi._parse_driver_info,
node)
# test the invalid priv_level value
info = dict(INFO_DICT)
info['ipmi_priv_level'] = 'ABCD'
node = obj_utils.get_test_node(self.context, driver_info=info)
self.assertRaises(exception.InvalidParameterValue,
ipmi._parse_driver_info,
node)
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
def test__parse_driver_info_with_invalid_bridging_type(
self, mock_support):
info = BRIDGE_INFO_DICT.copy()
# make sure error is raised when ipmi_bridging has unexpected value
info['ipmi_bridging'] = 'junk'
node = obj_utils.get_test_node(self.context, driver_info=info)
self.assertRaises(exception.InvalidParameterValue,
ipmi._parse_driver_info,
node)
self.assertFalse(mock_support.called)
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
def test__parse_driver_info_with_no_bridging(
self, mock_support):
_OPTIONS = ['address', 'username', 'password', 'uuid']
_BRIDGING_OPTIONS = ['local_address', 'transit_channel',
'transit_address',
'target_channel', 'target_address']
info = BRIDGE_INFO_DICT.copy()
info['ipmi_bridging'] = 'no'
node = obj_utils.get_test_node(self.context, driver_info=info)
ret = ipmi._parse_driver_info(node)
# ensure that _is_option_supported was not called
self.assertFalse(mock_support.called)
# check if we got all the required options
for option in _OPTIONS:
self.assertIsNotNone(ret[option])
# test the default value for 'priv_level'
self.assertEqual('ADMINISTRATOR', ret['priv_level'])
# check if bridging parameters were set to None
for option in _BRIDGING_OPTIONS:
self.assertIsNone(ret[option])
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
def test__parse_driver_info_with_dual_bridging_pass(
self, mock_support):
_OPTIONS = ['address', 'username', 'password', 'uuid',
'local_address', 'transit_channel', 'transit_address',
'target_channel', 'target_address']
node = obj_utils.get_test_node(self.context,
driver_info=BRIDGE_INFO_DICT)
expected = [mock.call('dual_bridge')]
# test double bridging and make sure we get back expected result
mock_support.return_value = True
ret = ipmi._parse_driver_info(node)
self.assertEqual(expected, mock_support.call_args_list)
for option in _OPTIONS:
self.assertIsNotNone(ret[option])
# test the default value for 'priv_level'
self.assertEqual('ADMINISTRATOR', ret['priv_level'])
info = BRIDGE_INFO_DICT.copy()
# ipmi_local_address / ipmi_username / ipmi_password are not mandatory
for optional_arg in ['ipmi_local_address', 'ipmi_username',
'ipmi_password']:
del info[optional_arg]
node = obj_utils.get_test_node(self.context, driver_info=info)
ipmi._parse_driver_info(node)
self.assertEqual(mock.call('dual_bridge'), mock_support.call_args)
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
def test__parse_driver_info_with_dual_bridging_not_supported(
self, mock_support):
node = obj_utils.get_test_node(self.context,
driver_info=BRIDGE_INFO_DICT)
# if dual bridge is not supported then check if error is raised
mock_support.return_value = False
self.assertRaises(exception.InvalidParameterValue,
ipmi._parse_driver_info, node)
mock_support.assert_called_once_with('dual_bridge')
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
def test__parse_driver_info_with_dual_bridging_missing_parameters(
self, mock_support):
info = BRIDGE_INFO_DICT.copy()
mock_support.return_value = True
# make sure error is raised when dual bridging is selected and the
# required parameters for dual bridging are not provided
for param in ['ipmi_transit_channel', 'ipmi_target_address',
'ipmi_transit_address', 'ipmi_target_channel']:
del info[param]
node = obj_utils.get_test_node(self.context, driver_info=info)
self.assertRaises(exception.MissingParameterValue,
ipmi._parse_driver_info, node)
self.assertEqual(mock.call('dual_bridge'),
mock_support.call_args)
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
def test__parse_driver_info_with_single_bridging_pass(
self, mock_support):
_OPTIONS = ['address', 'username', 'password', 'uuid',
'local_address', 'target_channel', 'target_address']
info = BRIDGE_INFO_DICT.copy()
info['ipmi_bridging'] = 'single'
node = obj_utils.get_test_node(self.context, driver_info=info)
expected = [mock.call('single_bridge')]
# test single bridging and make sure we get back expected things
mock_support.return_value = True
ret = ipmi._parse_driver_info(node)
self.assertEqual(expected, mock_support.call_args_list)
for option in _OPTIONS:
self.assertIsNotNone(ret[option])
# test the default value for 'priv_level'
self.assertEqual('ADMINISTRATOR', ret['priv_level'])
# check if dual bridge params are set to None
self.assertIsNone(ret['transit_channel'])
self.assertIsNone(ret['transit_address'])
# ipmi_local_address / ipmi_username / ipmi_password are not mandatory
for optional_arg in ['ipmi_local_address', 'ipmi_username',
'ipmi_password']:
del info[optional_arg]
node = obj_utils.get_test_node(self.context, driver_info=info)
ipmi._parse_driver_info(node)
self.assertEqual(mock.call('single_bridge'),
mock_support.call_args)
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
def test__parse_driver_info_with_single_bridging_not_supported(
self, mock_support):
info = BRIDGE_INFO_DICT.copy()
info['ipmi_bridging'] = 'single'
node = obj_utils.get_test_node(self.context, driver_info=info)
# if single bridge is not supported then check if error is raised
mock_support.return_value = False
self.assertRaises(exception.InvalidParameterValue,
ipmi._parse_driver_info, node)
mock_support.assert_called_once_with('single_bridge')
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
def test__parse_driver_info_with_single_bridging_missing_parameters(
self, mock_support):
info = dict(BRIDGE_INFO_DICT)
info['ipmi_bridging'] = 'single'
mock_support.return_value = True
# make sure error is raised when single bridging is selected and the
# required parameters for single bridging are not provided
for param in ['ipmi_target_channel', 'ipmi_target_address']:
del info[param]
node = obj_utils.get_test_node(self.context, driver_info=info)
self.assertRaises(exception.MissingParameterValue,
ipmi._parse_driver_info,
node)
self.assertEqual(mock.call('single_bridge'),
mock_support.call_args)
def test__parse_driver_info_numeric_password(self):
# ipmi_password must not be converted to int / float
# even if it includes just numbers.
info = dict(INFO_DICT)
info['ipmi_password'] = 12345678
node = obj_utils.get_test_node(self.context, driver_info=info)
ret = ipmi._parse_driver_info(node)
self.assertEqual(six.u('12345678'), ret['password'])
self.assertIsInstance(ret['password'], six.text_type)
def test__parse_driver_info_ipmi_prot_version_1_5(self):
info = dict(INFO_DICT)
info['ipmi_protocol_version'] = '1.5'
node = obj_utils.get_test_node(self.context, driver_info=info)
ret = ipmi._parse_driver_info(node)
self.assertEqual('1.5', ret['protocol_version'])
def test__parse_driver_info_invalid_ipmi_prot_version(self):
info = dict(INFO_DICT)
info['ipmi_protocol_version'] = '9000'
node = obj_utils.get_test_node(self.context, driver_info=info)
self.assertRaises(exception.InvalidParameterValue,
ipmi._parse_driver_info, node)
def test__parse_driver_info_invalid_ipmi_port(self):
info = dict(INFO_DICT)
info['ipmi_port'] = '700000'
node = obj_utils.get_test_node(self.context, driver_info=info)
self.assertRaises(exception.InvalidParameterValue,
ipmi._parse_driver_info, node)
def test__parse_driver_info_ipmi_port_valid(self):
info = dict(INFO_DICT)
info['ipmi_port'] = '623'
node = obj_utils.get_test_node(self.context, driver_info=info)
ret = ipmi._parse_driver_info(node)
self.assertEqual(623, ret['dest_port'])
@mock.patch.object(ipmi.LOG, 'warning', spec_set=True, autospec=True)
def test__parse_driver_info_undefined_credentials(self, mock_log):
info = dict(INFO_DICT)
del info['ipmi_username']
del info['ipmi_password']
node = obj_utils.get_test_node(self.context, driver_info=info)
ipmi._parse_driver_info(node)
calls = [
mock.call(u'ipmi_username is not defined or empty for node '
u'%s: NULL user will be utilized.', self.node.uuid),
mock.call(u'ipmi_password is not defined or empty for node '
u'%s: NULL password will be utilized.', self.node.uuid),
]
mock_log.assert_has_calls(calls)
@mock.patch.object(ipmi.LOG, 'warning', spec_set=True, autospec=True)
def test__parse_driver_info_have_credentials(
self, mock_log):
"""Ensure no warnings generated if have credentials"""
info = dict(INFO_DICT)
node = obj_utils.get_test_node(self.context, driver_info=info)
ipmi._parse_driver_info(node)
self.assertFalse(mock_log.called)
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(ipmi, '_make_password_file', _make_password_file_stub)
@mock.patch.object(utils, 'execute', autospec=True)
def test__exec_ipmitool_first_call_to_address(self, mock_exec,
mock_support):
ipmi.LAST_CMD_TIME = {}
args = [
'ipmitool',
'-I', 'lanplus',
'-H', self.info['address'],
'-L', self.info['priv_level'],
'-U', self.info['username'],
'-f', awesome_password_filename,
'A', 'B', 'C',
]
mock_support.return_value = False
mock_exec.return_value = (None, None)
ipmi._exec_ipmitool(self.info, 'A B C')
mock_support.assert_called_once_with('timing')
mock_exec.assert_called_once_with(*args)
self.assertFalse(self.mock_sleep.called)
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(ipmi, '_make_password_file', _make_password_file_stub)
@mock.patch.object(utils, 'execute', autospec=True)
def test__exec_ipmitool_second_call_to_address_sleep(
self, mock_exec, mock_support):
ipmi.LAST_CMD_TIME = {}
args = [[
'ipmitool',
'-I', 'lanplus',
'-H', self.info['address'],
'-L', self.info['priv_level'],
'-U', self.info['username'],
'-f', awesome_password_filename,
'A', 'B', 'C',
], [
'ipmitool',
'-I', 'lanplus',
'-H', self.info['address'],
'-L', self.info['priv_level'],
'-U', self.info['username'],
'-f', awesome_password_filename,
'D', 'E', 'F',
]]
expected = [mock.call('timing'),
mock.call('timing')]
mock_support.return_value = False
mock_exec.side_effect = [(None, None), (None, None)]
ipmi._exec_ipmitool(self.info, 'A B C')
mock_exec.assert_called_with(*args[0])
ipmi._exec_ipmitool(self.info, 'D E F')
self.assertTrue(self.mock_sleep.called)
self.assertEqual(expected, mock_support.call_args_list)
mock_exec.assert_called_with(*args[1])
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(ipmi, '_make_password_file', _make_password_file_stub)
@mock.patch.object(utils, 'execute', autospec=True)
def test__exec_ipmitool_second_call_to_address_no_sleep(
self, mock_exec, mock_support):
ipmi.LAST_CMD_TIME = {}
args = [[
'ipmitool',
'-I', 'lanplus',
'-H', self.info['address'],
'-L', self.info['priv_level'],
'-U', self.info['username'],
'-f', awesome_password_filename,
'A', 'B', 'C',
], [
'ipmitool',
'-I', 'lanplus',
'-H', self.info['address'],
'-L', self.info['priv_level'],
'-U', self.info['username'],
'-f', awesome_password_filename,
'D', 'E', 'F',
]]
expected = [mock.call('timing'),
mock.call('timing')]
mock_support.return_value = False
mock_exec.side_effect = [(None, None), (None, None)]
ipmi._exec_ipmitool(self.info, 'A B C')
mock_exec.assert_called_with(*args[0])
# act like enough time has passed
ipmi.LAST_CMD_TIME[self.info['address']] = (
time.time() - CONF.ipmi.min_command_interval)
ipmi._exec_ipmitool(self.info, 'D E F')
self.assertFalse(self.mock_sleep.called)
self.assertEqual(expected, mock_support.call_args_list)
mock_exec.assert_called_with(*args[1])
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(ipmi, '_make_password_file', _make_password_file_stub)
@mock.patch.object(utils, 'execute', autospec=True)
def test__exec_ipmitool_two_calls_to_diff_address(
self, mock_exec, mock_support):
ipmi.LAST_CMD_TIME = {}
args = [[
'ipmitool',
'-I', 'lanplus',
'-H', self.info['address'],
'-L', self.info['priv_level'],
'-U', self.info['username'],
'-f', awesome_password_filename,
'A', 'B', 'C',
], [
'ipmitool',
'-I', 'lanplus',
'-H', '127.127.127.127',
'-L', self.info['priv_level'],
'-U', self.info['username'],
'-f', awesome_password_filename,
'D', 'E', 'F',
]]
expected = [mock.call('timing'),
mock.call('timing')]
mock_support.return_value = False
mock_exec.side_effect = [(None, None), (None, None)]
ipmi._exec_ipmitool(self.info, 'A B C')
mock_exec.assert_called_with(*args[0])
self.info['address'] = '127.127.127.127'
ipmi._exec_ipmitool(self.info, 'D E F')
self.assertFalse(self.mock_sleep.called)
self.assertEqual(expected, mock_support.call_args_list)
mock_exec.assert_called_with(*args[1])
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(ipmi, '_make_password_file', _make_password_file_stub)
@mock.patch.object(utils, 'execute', autospec=True)
def test__exec_ipmitool_without_timing(
self, mock_exec, mock_support):
args = [
'ipmitool',
'-I', 'lanplus',
'-H', self.info['address'],
'-L', self.info['priv_level'],
'-U', self.info['username'],
'-f', awesome_password_filename,
'A', 'B', 'C',
]
mock_support.return_value = False
mock_exec.return_value = (None, None)
ipmi._exec_ipmitool(self.info, 'A B C')
mock_support.assert_called_once_with('timing')
mock_exec.assert_called_once_with(*args)
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(ipmi, '_make_password_file', _make_password_file_stub)
@mock.patch.object(utils, 'execute', autospec=True)
def test__exec_ipmitool_with_timing(
self, mock_exec, mock_support):
args = [
'ipmitool',
'-I', 'lanplus',
'-H', self.info['address'],
'-L', self.info['priv_level'],
'-U', self.info['username'],
'-R', '12',
'-N', '5',
'-f', awesome_password_filename,
'A', 'B', 'C',
]
mock_support.return_value = True
mock_exec.return_value = (None, None)
ipmi._exec_ipmitool(self.info, 'A B C')
mock_support.assert_called_once_with('timing')
mock_exec.assert_called_once_with(*args)
def test__exec_ipmitool_wait(self):
mock_popen = mock.MagicMock()
mock_popen.poll.side_effect = [1, 1, 1, 1, 1]
ipmi._exec_ipmitool_wait(1, {'uuid': ''}, mock_popen)
self.assertTrue(mock_popen.terminate.called)
self.assertTrue(mock_popen.kill.called)
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(ipmi, '_make_password_file', _make_password_file_stub)
@mock.patch.object(utils, 'execute', autospec=True)
def test__exec_ipmitool_without_username(
self, mock_exec, mock_support):
# An undefined username is treated the same as an empty username and
# will cause no user (-U) to be specified.
self.info['username'] = None
args = [
'ipmitool',
'-I', 'lanplus',
'-H', self.info['address'],
'-L', self.info['priv_level'],
'-f', awesome_password_filename,
'A', 'B', 'C',
]
mock_support.return_value = False
mock_exec.return_value = (None, None)
ipmi._exec_ipmitool(self.info, 'A B C')
mock_support.assert_called_once_with('timing')
mock_exec.assert_called_once_with(*args)
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(ipmi, '_make_password_file', _make_password_file_stub)
@mock.patch.object(utils, 'execute', autospec=True)
def test__exec_ipmitool_with_empty_username(
self, mock_exec, mock_support):
# An empty username is treated the same as an undefined username and
# will cause no user (-U) to be specified.
self.info['username'] = ""
args = [
'ipmitool',
'-I', 'lanplus',
'-H', self.info['address'],
'-L', self.info['priv_level'],
'-f', awesome_password_filename,
'A', 'B', 'C',
]
mock_support.return_value = False
mock_exec.return_value = (None, None)
ipmi._exec_ipmitool(self.info, 'A B C')
mock_support.assert_called_once_with('timing')
mock_exec.assert_called_once_with(*args)
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(
ipmi, '_make_password_file', wraps=_make_password_file_stub)
@mock.patch.object(utils, 'execute', autospec=True)
def test__exec_ipmitool_without_password(self, mock_exec,
_make_password_file_mock,
mock_support):
# An undefined password is treated the same as an empty password and
# will cause a NULL (\0) password to be used"""
self.info['password'] = None
args = [
'ipmitool',
'-I', 'lanplus',
'-H', self.info['address'],
'-L', self.info['priv_level'],
'-U', self.info['username'],
'-f', awesome_password_filename,
'A', 'B', 'C',
]
mock_support.return_value = False
mock_exec.return_value = (None, None)
ipmi._exec_ipmitool(self.info, 'A B C')
mock_support.assert_called_once_with('timing')
mock_exec.assert_called_once_with(*args)
_make_password_file_mock.assert_called_once_with('\0')
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(
ipmi, '_make_password_file', wraps=_make_password_file_stub)
@mock.patch.object(utils, 'execute', autospec=True)
def test__exec_ipmitool_with_empty_password(self, mock_exec,
_make_password_file_mock,
mock_support):
# An empty password is treated the same as an undefined password and
# will cause a NULL (\0) password to be used"""
self.info['password'] = ""
args = [
'ipmitool',
'-I', 'lanplus',
'-H', self.info['address'],
'-L', self.info['priv_level'],
'-U', self.info['username'],
'-f', awesome_password_filename,
'A', 'B', 'C',
]
mock_support.return_value = False
mock_exec.return_value = (None, None)
ipmi._exec_ipmitool(self.info, 'A B C')
mock_support.assert_called_once_with('timing')
mock_exec.assert_called_once_with(*args)
_make_password_file_mock.assert_called_once_with('\0')
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(ipmi, '_make_password_file', _make_password_file_stub)
@mock.patch.object(utils, 'execute', autospec=True)
def test__exec_ipmitool_with_dual_bridging(self,
mock_exec,
mock_support):
node = obj_utils.get_test_node(self.context,
driver_info=BRIDGE_INFO_DICT)
# when support for dual bridge command is called returns True
mock_support.return_value = True
info = ipmi._parse_driver_info(node)
args = [
'ipmitool',
'-I', 'lanplus',
'-H', info['address'],
'-L', info['priv_level'],
'-U', info['username'],
'-m', info['local_address'],
'-B', info['transit_channel'],
'-T', info['transit_address'],
'-b', info['target_channel'],
'-t', info['target_address'],
'-f', awesome_password_filename,
'A', 'B', 'C',
]
expected = [mock.call('dual_bridge'),
mock.call('timing')]
# When support for timing command is called returns False
mock_support.return_value = False
mock_exec.return_value = (None, None)
ipmi._exec_ipmitool(info, 'A B C')
self.assertEqual(expected, mock_support.call_args_list)
mock_exec.assert_called_once_with(*args)
@mock.patch.object(ipmi, '_make_password_file', _make_password_file_stub)
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(utils, 'execute', autospec=True)
def test__exec_ipmitool_with_single_bridging(self,
mock_exec,
mock_support):
single_bridge_info = dict(BRIDGE_INFO_DICT)
single_bridge_info['ipmi_bridging'] = 'single'
node = obj_utils.get_test_node(self.context,
driver_info=single_bridge_info)
# when support for single bridge command is called returns True
mock_support.return_value = True
info = ipmi._parse_driver_info(node)
info['transit_channel'] = info['transit_address'] = None
args = [
'ipmitool',
'-I', 'lanplus',
'-H', info['address'],
'-L', info['priv_level'],
'-U', info['username'],
'-m', info['local_address'],
'-b', info['target_channel'],
'-t', info['target_address'],
'-f', awesome_password_filename,
'A', 'B', 'C',
]
expected = [mock.call('single_bridge'),
mock.call('timing')]
# When support for timing command is called returns False
mock_support.return_value = False
mock_exec.return_value = (None, None)
ipmi._exec_ipmitool(info, 'A B C')
self.assertEqual(expected, mock_support.call_args_list)
mock_exec.assert_called_once_with(*args)
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(ipmi, '_make_password_file', _make_password_file_stub)
@mock.patch.object(utils, 'execute', autospec=True)
def test__exec_ipmitool_exception(self, mock_exec, mock_support):
args = [
'ipmitool',
'-I', 'lanplus',
'-H', self.info['address'],
'-L', self.info['priv_level'],
'-U', self.info['username'],
'-f', awesome_password_filename,
'A', 'B', 'C',
]
mock_support.return_value = False
mock_exec.side_effect = processutils.ProcessExecutionError("x")
self.assertRaises(processutils.ProcessExecutionError,
ipmi._exec_ipmitool,
self.info, 'A B C')
mock_support.assert_called_once_with('timing')
mock_exec.assert_called_once_with(*args)
self.assertEqual(1, mock_exec.call_count)
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(ipmi, '_make_password_file', _make_password_file_stub)
@mock.patch.object(utils, 'execute', autospec=True)
def test__exec_ipmitool_IPMI_version_1_5(
self, mock_exec, mock_support):
self.info['protocol_version'] = '1.5'
# Assert it uses "-I lan" (1.5) instead of "-I lanplus" (2.0)
args = [
'ipmitool',
'-I', 'lan',
'-H', self.info['address'],
'-L', self.info['priv_level'],
'-U', self.info['username'],
'-f', awesome_password_filename,
'A', 'B', 'C',
]
mock_support.return_value = False
mock_exec.return_value = (None, None)
ipmi._exec_ipmitool(self.info, 'A B C')
mock_support.assert_called_once_with('timing')
mock_exec.assert_called_once_with(*args)
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(ipmi, '_make_password_file', _make_password_file_stub)
@mock.patch.object(utils, 'execute', autospec=True)
def test__exec_ipmitool_with_port(self, mock_exec, mock_support):
self.info['dest_port'] = '1623'
ipmi.LAST_CMD_TIME = {}
args = [
'ipmitool',
'-I', 'lanplus',
'-H', self.info['address'],
'-L', self.info['priv_level'],
'-p', '1623',
'-U', self.info['username'],
'-f', awesome_password_filename,
'A', 'B', 'C',
]
mock_support.return_value = False
mock_exec.return_value = (None, None)
ipmi._exec_ipmitool(self.info, 'A B C')
mock_support.assert_called_once_with('timing')
mock_exec.assert_called_once_with(*args)
self.assertFalse(self.mock_sleep.called)
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(ipmi, '_make_password_file', _make_password_file_stub)
@mock.patch.object(utils, 'execute', autospec=True)
def test__exec_ipmitool_with_check_exit_code(self, mock_exec,
mock_support):
args = [
'ipmitool',
'-I', 'lanplus',
'-H', self.info['address'],
'-L', self.info['priv_level'],
'-U', self.info['username'],
'-f', awesome_password_filename,
'A', 'B', 'C',
]
mock_support.return_value = False
mock_exec.return_value = (None, None)
ipmi._exec_ipmitool(self.info, 'A B C', check_exit_code=[0, 1])
mock_support.assert_called_once_with('timing')
mock_exec.assert_called_once_with(*args, check_exit_code=[0, 1])
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def test__power_status_on(self, mock_exec):
mock_exec.return_value = ["Chassis Power is on\n", None]
state = ipmi._power_status(self.info)
mock_exec.assert_called_once_with(self.info, "power status",
kill_on_timeout=True)
self.assertEqual(states.POWER_ON, state)
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def test__power_status_off(self, mock_exec):
mock_exec.return_value = ["Chassis Power is off\n", None]
state = ipmi._power_status(self.info)
mock_exec.assert_called_once_with(self.info, "power status",
kill_on_timeout=True)
self.assertEqual(states.POWER_OFF, state)
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def test__power_status_error(self, mock_exec):
mock_exec.return_value = ["Chassis Power is badstate\n", None]
state = ipmi._power_status(self.info)
mock_exec.assert_called_once_with(self.info, "power status",
kill_on_timeout=True)
self.assertEqual(states.ERROR, state)
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def test__power_status_exception(self, mock_exec):
mock_exec.side_effect = processutils.ProcessExecutionError("error")
self.assertRaises(exception.IPMIFailure,
ipmi._power_status,
self.info)
mock_exec.assert_called_once_with(self.info, "power status",
kill_on_timeout=True)
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
@mock.patch('eventlet.greenthread.sleep', autospec=True)
def test__power_on_max_retries(self, sleep_mock, mock_exec):
self.config(command_retry_timeout=2, group='ipmi')
def side_effect(driver_info, command, **kwargs):
resp_dict = {"power status": ["Chassis Power is off\n", None],
"power on": [None, None]}
return resp_dict.get(command, ["Bad\n", None])
mock_exec.side_effect = side_effect
expected = [mock.call(self.info, "power on"),
mock.call(self.info, "power status", kill_on_timeout=True),
mock.call(self.info, "power status", kill_on_timeout=True)]
with task_manager.acquire(self.context, self.node.uuid) as task:
self.assertRaises(exception.PowerStateFailure,
ipmi._power_on, task, self.info, timeout=2)
self.assertEqual(expected, mock_exec.call_args_list)
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
@mock.patch('eventlet.greenthread.sleep', autospec=True)
def test__soft_power_off(self, sleep_mock, mock_exec):
def side_effect(driver_info, command, **kwargs):
resp_dict = {"power status": ["Chassis Power is off\n", None],
"power soft": [None, None]}
return resp_dict.get(command, ["Bad\n", None])
mock_exec.side_effect = side_effect
expected = [mock.call(self.info, "power soft"),
mock.call(self.info, "power status", kill_on_timeout=True)]
with task_manager.acquire(self.context, self.node.uuid) as task:
state = ipmi._soft_power_off(task, self.info)
self.assertEqual(expected, mock_exec.call_args_list)
self.assertEqual(states.POWER_OFF, state)
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
@mock.patch('eventlet.greenthread.sleep', autospec=True)
def test__soft_power_off_max_retries(self, sleep_mock, mock_exec):
def side_effect(driver_info, command, **kwargs):
resp_dict = {"power status": ["Chassis Power is on\n", None],
"power soft": [None, None]}
return resp_dict.get(command, ["Bad\n", None])
mock_exec.side_effect = side_effect
expected = [mock.call(self.info, "power soft"),
mock.call(self.info, "power status", kill_on_timeout=True),
mock.call(self.info, "power status", kill_on_timeout=True)]
with task_manager.acquire(self.context, self.node.uuid) as task:
self.assertRaises(exception.PowerStateFailure,
ipmi._soft_power_off, task, self.info, timeout=2)
self.assertEqual(expected, mock_exec.call_args_list)
@mock.patch.object(ipmi, '_power_status', autospec=True)
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
@mock.patch('eventlet.greenthread.sleep', autospec=True)
def test___set_and_wait_no_needless_status_polling(
self, sleep_mock, mock_exec, mock_status):
# Check that if the call to power state change fails, it doesn't
# call power_status().
self.config(command_retry_timeout=2, group='ipmi')
mock_exec.side_effect = exception.IPMIFailure(cmd='power on')
with task_manager.acquire(self.context, self.node.uuid) as task:
self.assertRaises(exception.IPMIFailure, ipmi._power_on, task,
self.info)
self.assertFalse(mock_status.called)
class IPMIToolDriverTestCase(Base):
@mock.patch.object(ipmi, "_parse_driver_info", autospec=True)
def test_power_validate(self, mock_parse):
mock_parse.return_value = {}
with task_manager.acquire(self.context, self.node.uuid) as task:
task.driver.power.validate(task)
mock_parse.assert_called_once_with(mock.ANY)
def test_get_properties(self):
expected = ipmi.COMMON_PROPERTIES
self.assertEqual(expected, self.power.get_properties())
expected = list(ipmi.COMMON_PROPERTIES) + list(ipmi.CONSOLE_PROPERTIES)
self.assertEqual(sorted(expected),
sorted(self.console.get_properties()))
with task_manager.acquire(self.context, self.node.uuid) as task:
self.assertEqual(sorted(expected),
sorted(task.driver.get_properties()))
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def test_get_power_state(self, mock_exec):
returns = iter([["Chassis Power is off\n", None],
["Chassis Power is on\n", None],
["\n", None]])
expected = [mock.call(self.info, "power status", kill_on_timeout=True),
mock.call(self.info, "power status", kill_on_timeout=True),
mock.call(self.info, "power status", kill_on_timeout=True)]
mock_exec.side_effect = returns
with task_manager.acquire(self.context, self.node.uuid) as task:
pstate = self.power.get_power_state(task)
self.assertEqual(states.POWER_OFF, pstate)
pstate = self.power.get_power_state(task)
self.assertEqual(states.POWER_ON, pstate)
pstate = self.power.get_power_state(task)
self.assertEqual(states.ERROR, pstate)
self.assertEqual(mock_exec.call_args_list, expected)
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def test_get_power_state_exception(self, mock_exec):
mock_exec.side_effect = processutils.ProcessExecutionError("error")
with task_manager.acquire(self.context, self.node.uuid) as task:
self.assertRaises(exception.IPMIFailure,
self.power.get_power_state,
task)
mock_exec.assert_called_once_with(self.info, "power status",
kill_on_timeout=True)
@mock.patch.object(ipmi, '_power_on', autospec=True)
@mock.patch.object(ipmi, '_power_off', autospec=True)
def test_set_power_on_ok(self, mock_off, mock_on):
self.config(command_retry_timeout=0, group='ipmi')
mock_on.return_value = states.POWER_ON
with task_manager.acquire(self.context,
self.node.uuid) as task:
self.power.set_power_state(task, states.POWER_ON)
mock_on.assert_called_once_with(task, self.info, timeout=None)
self.assertFalse(mock_off.called)
@mock.patch.object(ipmi, '_power_on', autospec=True)
@mock.patch.object(ipmi, '_power_off', autospec=True)
def test_set_power_on_timeout_ok(self, mock_off, mock_on):
self.config(command_retry_timeout=0, group='ipmi')
mock_on.return_value = states.POWER_ON
with task_manager.acquire(self.context,
self.node.uuid) as task:
self.power.set_power_state(task, states.POWER_ON, timeout=2)
mock_on.assert_called_once_with(task, self.info, timeout=2)
self.assertFalse(mock_off.called)
@mock.patch.object(driver_utils, 'ensure_next_boot_device', autospec=True)
@mock.patch.object(ipmi, '_power_on', autospec=True)
@mock.patch.object(ipmi, '_power_off', autospec=True)
def test_set_power_on_with_next_boot(self, mock_off, mock_on,
mock_next_boot):
self.config(command_retry_timeout=0, group='ipmi')
mock_on.return_value = states.POWER_ON
with task_manager.acquire(self.context,
self.node.uuid) as task:
self.power.set_power_state(task, states.POWER_ON)
mock_next_boot.assert_called_once_with(task, self.info)
mock_on.assert_called_once_with(task, self.info, timeout=None)
self.assertFalse(mock_off.called)
@mock.patch.object(driver_utils, 'ensure_next_boot_device', autospec=True)
@mock.patch.object(ipmi, '_power_on', autospec=True)
@mock.patch.object(ipmi, '_power_off', autospec=True)
def test_set_power_on_with_next_boot_timeout(self, mock_off, mock_on,
mock_next_boot):
self.config(command_retry_timeout=0, group='ipmi')
mock_on.return_value = states.POWER_ON
with task_manager.acquire(self.context,
self.node.uuid) as task:
self.power.set_power_state(task, states.POWER_ON, timeout=2)
mock_next_boot.assert_called_once_with(task, self.info)
mock_on.assert_called_once_with(task, self.info, timeout=2)
self.assertFalse(mock_off.called)
@mock.patch.object(ipmi, '_power_on', autospec=True)
@mock.patch.object(ipmi, '_power_off', autospec=True)
def test_set_power_off_ok(self, mock_off, mock_on):
self.config(command_retry_timeout=0, group='ipmi')
mock_off.return_value = states.POWER_OFF
with task_manager.acquire(self.context,
self.node.uuid) as task:
self.power.set_power_state(task, states.POWER_OFF)
mock_off.assert_called_once_with(task, self.info, timeout=None)
self.assertFalse(mock_on.called)
@mock.patch.object(ipmi, '_power_on', autospec=True)
@mock.patch.object(ipmi, '_power_off', autospec=True)
def test_set_power_off_timeout_ok(self, mock_off, mock_on):
self.config(command_retry_timeout=0, group='ipmi')
mock_off.return_value = states.POWER_OFF
with task_manager.acquire(self.context,
self.node.uuid) as task:
self.power.set_power_state(task, states.POWER_OFF, timeout=2)
mock_off.assert_called_once_with(task, self.info, timeout=2)
self.assertFalse(mock_on.called)
@mock.patch.object(ipmi, '_power_on', autospec=True)
@mock.patch.object(ipmi, '_soft_power_off', autospec=True)
def test_set_soft_power_off_ok(self, mock_off, mock_on):
self.config(command_retry_timeout=0, group='ipmi')
mock_off.return_value = states.POWER_OFF
with task_manager.acquire(self.context,
self.node['uuid']) as task:
self.power.set_power_state(task, states.SOFT_POWER_OFF)
mock_off.assert_called_once_with(task, self.info, timeout=None)
self.assertFalse(mock_on.called)
@mock.patch.object(ipmi, '_power_on', autospec=True)
@mock.patch.object(ipmi, '_soft_power_off', autospec=True)
def test_set_soft_power_off_timeout_ok(self, mock_off, mock_on):
self.config(command_retry_timeout=0, group='ipmi')
mock_off.return_value = states.POWER_OFF
with task_manager.acquire(self.context,
self.node['uuid']) as task:
self.power.set_power_state(task, states.SOFT_POWER_OFF, timeout=2)
mock_off.assert_called_once_with(task, self.info, timeout=2)
self.assertFalse(mock_on.called)
@mock.patch.object(driver_utils, 'ensure_next_boot_device', autospec=True)
@mock.patch.object(ipmi, '_power_on', autospec=True)
@mock.patch.object(ipmi, '_soft_power_off', autospec=True)
def test_set_soft_reboot_ok(self, mock_off, mock_on, mock_next_boot):
self.config(command_retry_timeout=0, group='ipmi')
mock_off.return_value = states.POWER_OFF
mock_on.return_value = states.POWER_ON
with task_manager.acquire(self.context,
self.node['uuid']) as task:
self.power.set_power_state(task, states.SOFT_REBOOT)
mock_next_boot.assert_called_once_with(task, self.info)
mock_off.assert_called_once_with(task, self.info, timeout=None)
mock_on.assert_called_once_with(task, self.info, timeout=None)
@mock.patch.object(driver_utils, 'ensure_next_boot_device', autospec=True)
@mock.patch.object(ipmi, '_power_on', autospec=True)
@mock.patch.object(ipmi, '_soft_power_off', autospec=True)
def test_set_soft_reboot_timeout_ok(self, mock_off, mock_on,
mock_next_boot):
self.config(command_retry_timeout=0, group='ipmi')
mock_off.return_value = states.POWER_OFF
mock_on.return_value = states.POWER_ON
with task_manager.acquire(self.context,
self.node['uuid']) as task:
self.power.set_power_state(task, states.SOFT_REBOOT, timeout=2)
mock_next_boot.assert_called_once_with(task, self.info)
mock_off.assert_called_once_with(task, self.info, timeout=2)
mock_on.assert_called_once_with(task, self.info, timeout=2)
@mock.patch.object(driver_utils, 'ensure_next_boot_device', autospec=True)
@mock.patch.object(ipmi, '_power_on', autospec=True)
@mock.patch.object(ipmi, '_soft_power_off', autospec=True)
def test_set_soft_reboot_timeout_fail(self, mock_off, mock_on,
mock_next_boot):
self.config(command_retry_timeout=0, group='ipmi')
mock_off.side_effect = exception.PowerStateFailure(
pstate=states.POWER_ON)
with task_manager.acquire(self.context,
self.node['uuid']) as task:
self.assertRaises(exception.PowerStateFailure,
self.power.set_power_state,
task,
states.SOFT_REBOOT,
timeout=2)
mock_off.assert_called_once_with(task, self.info, timeout=2)
self.assertFalse(mock_next_boot.called)
self.assertFalse(mock_on.called)
@mock.patch.object(ipmi, '_power_on', autospec=True)
@mock.patch.object(ipmi, '_power_off', autospec=True)
def test_set_power_on_fail(self, mock_off, mock_on):
self.config(command_retry_timeout=0, group='ipmi')
mock_on.side_effect = exception.PowerStateFailure(
pstate=states.POWER_ON)
with task_manager.acquire(self.context,
self.node.uuid) as task:
self.assertRaises(exception.PowerStateFailure,
self.power.set_power_state,
task,
states.POWER_ON)
mock_on.assert_called_once_with(task, self.info, timeout=None)
self.assertFalse(mock_off.called)
@mock.patch.object(ipmi, '_power_on', autospec=True)
@mock.patch.object(ipmi, '_power_off', autospec=True)
def test_set_power_on_timeout_fail(self, mock_off, mock_on):
self.config(command_retry_timeout=0, group='ipmi')
mock_on.side_effect = exception.PowerStateFailure(pstate=states.ERROR)
with task_manager.acquire(self.context,
self.node.uuid) as task:
self.assertRaises(exception.PowerStateFailure,
self.power.set_power_state,
task,
states.POWER_ON,
timeout=2)
mock_on.assert_called_once_with(task, self.info, timeout=2)
self.assertFalse(mock_off.called)
def test_set_power_invalid_state(self):
with task_manager.acquire(self.context, self.node.uuid) as task:
self.assertRaises(exception.InvalidParameterValue,
self.power.set_power_state,
task,
"fake state")
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def test_send_raw_bytes_ok(self, mock_exec):
mock_exec.return_value = [None, None]
with task_manager.acquire(self.context,
self.node.uuid) as task:
self.vendor.send_raw(task, http_method='POST',
raw_bytes='0x00 0x01')
mock_exec.assert_called_once_with(self.info, 'raw 0x00 0x01')
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def test_send_raw_bytes_fail(self, mock_exec):
mock_exec.side_effect = exception.PasswordFileFailedToCreate('error')
with task_manager.acquire(self.context,
self.node.uuid) as task:
self.assertRaises(exception.IPMIFailure,
self.vendor.send_raw,
task,
http_method='POST',
raw_bytes='0x00 0x01')
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def test__bmc_reset_ok(self, mock_exec):
mock_exec.return_value = [None, None]
with task_manager.acquire(self.context,
self.node.uuid) as task:
self.vendor.bmc_reset(task, 'POST')
mock_exec.assert_called_once_with(self.info, 'bmc reset warm')
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def test__bmc_reset_cold(self, mock_exec):
mock_exec.return_value = [None, None]
with task_manager.acquire(self.context,
self.node.uuid) as task:
self.vendor.bmc_reset(task, 'POST', warm=False)
mock_exec.assert_called_once_with(self.info, 'bmc reset cold')
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def test__bmc_reset_fail(self, mock_exec):
mock_exec.side_effect = processutils.ProcessExecutionError()
with task_manager.acquire(self.context,
self.node.uuid) as task:
self.assertRaises(exception.IPMIFailure,
self.vendor.bmc_reset,
task, 'POST')
@mock.patch.object(driver_utils, 'ensure_next_boot_device', autospec=True)
@mock.patch.object(ipmi, '_power_off', spec_set=types.FunctionType)
@mock.patch.object(ipmi, '_power_on', spec_set=types.FunctionType)
@mock.patch.object(ipmi, '_power_status',
lambda driver_info: states.POWER_ON)
def test_reboot_ok(self, mock_on, mock_off, mock_next_boot):
manager = mock.MagicMock()
# NOTE(rloo): if autospec is True, then manager.mock_calls is empty
mock_off.return_value = states.POWER_OFF
mock_on.return_value = states.POWER_ON
manager.attach_mock(mock_off, 'power_off')
manager.attach_mock(mock_on, 'power_on')
with task_manager.acquire(self.context,
self.node.uuid) as task:
expected = [mock.call.power_off(task, self.info, timeout=None),
mock.call.power_on(task, self.info, timeout=None)]
self.power.reboot(task)
mock_next_boot.assert_called_once_with(task, self.info)
self.assertEqual(expected, manager.mock_calls)
@mock.patch.object(driver_utils, 'ensure_next_boot_device', autospec=True)
@mock.patch.object(ipmi, '_power_off', spec_set=types.FunctionType)
@mock.patch.object(ipmi, '_power_on', spec_set=types.FunctionType)
@mock.patch.object(ipmi, '_power_status',
lambda driver_info: states.POWER_OFF)
def test_reboot_already_off(self, mock_on, mock_off, mock_next_boot):
manager = mock.MagicMock()
# NOTE(rloo): if autospec is True, then manager.mock_calls is empty
mock_off.return_value = states.POWER_OFF
mock_on.return_value = states.POWER_ON
manager.attach_mock(mock_off, 'power_off')
manager.attach_mock(mock_on, 'power_on')
with task_manager.acquire(self.context,
self.node.uuid) as task:
expected = [mock.call.power_on(task, self.info, timeout=None)]
self.power.reboot(task)
mock_next_boot.assert_called_once_with(task, self.info)
self.assertEqual(expected, manager.mock_calls)
@mock.patch.object(driver_utils, 'ensure_next_boot_device', autospec=True)
@mock.patch.object(ipmi, '_power_off', spec_set=types.FunctionType)
@mock.patch.object(ipmi, '_power_on', spec_set=types.FunctionType)
@mock.patch.object(ipmi, '_power_status',
lambda driver_info: states.POWER_ON)
def test_reboot_timeout_ok(self, mock_on, mock_off, mock_next_boot):
manager = mock.MagicMock()
# NOTE(rloo): if autospec is True, then manager.mock_calls is empty
manager.attach_mock(mock_off, 'power_off')
manager.attach_mock(mock_on, 'power_on')
with task_manager.acquire(self.context,
self.node.uuid) as task:
expected = [mock.call.power_off(task, self.info, timeout=2),
mock.call.power_on(task, self.info, timeout=2)]
self.power.reboot(task, timeout=2)
mock_next_boot.assert_called_once_with(task, self.info)
self.assertEqual(expected, manager.mock_calls)
@mock.patch.object(ipmi, '_power_off', spec_set=types.FunctionType)
@mock.patch.object(ipmi, '_power_on', spec_set=types.FunctionType)
@mock.patch.object(ipmi, '_power_status',
lambda driver_info: states.POWER_ON)
def test_reboot_fail_power_off(self, mock_on, mock_off):
manager = mock.MagicMock()
# NOTE(rloo): if autospec is True, then manager.mock_calls is empty
mock_off.side_effect = exception.PowerStateFailure(
pstate=states.POWER_OFF)
manager.attach_mock(mock_off, 'power_off')
manager.attach_mock(mock_on, 'power_on')
with task_manager.acquire(self.context,
self.node.uuid) as task:
expected = [mock.call.power_off(task, self.info, timeout=None)]
self.assertRaises(exception.PowerStateFailure,
self.power.reboot,
task)
self.assertEqual(expected, manager.mock_calls)
@mock.patch.object(ipmi, '_power_off', spec_set=types.FunctionType)
@mock.patch.object(ipmi, '_power_on', spec_set=types.FunctionType)
@mock.patch.object(ipmi, '_power_status',
lambda driver_info: states.POWER_ON)
def test_reboot_fail_power_on(self, mock_on, mock_off):
manager = mock.MagicMock()
# NOTE(rloo): if autospec is True, then manager.mock_calls is empty
mock_off.return_value = states.POWER_OFF
mock_on.side_effect = exception.PowerStateFailure(
pstate=states.POWER_ON)
manager.attach_mock(mock_off, 'power_off')
manager.attach_mock(mock_on, 'power_on')
with task_manager.acquire(self.context,
self.node.uuid) as task:
expected = [mock.call.power_off(task, self.info, timeout=None),
mock.call.power_on(task, self.info, timeout=None)]
self.assertRaises(exception.PowerStateFailure,
self.power.reboot,
task)
self.assertEqual(expected, manager.mock_calls)
@mock.patch.object(ipmi, '_power_off', spec_set=types.FunctionType)
@mock.patch.object(ipmi, '_power_on', spec_set=types.FunctionType)
@mock.patch.object(ipmi, '_power_status',
lambda driver_info: states.POWER_ON)
def test_reboot_timeout_fail(self, mock_on, mock_off):
manager = mock.MagicMock()
# NOTE(rloo): if autospec is True, then manager.mock_calls is empty
mock_on.side_effect = exception.PowerStateFailure(
pstate=states.POWER_ON)
manager.attach_mock(mock_off, 'power_off')
manager.attach_mock(mock_on, 'power_on')
with task_manager.acquire(self.context,
self.node.uuid) as task:
expected = [mock.call.power_off(task, self.info, timeout=2),
mock.call.power_on(task, self.info, timeout=2)]
self.assertRaises(exception.PowerStateFailure,
self.power.reboot,
task, timeout=2)
self.assertEqual(expected, manager.mock_calls)
@mock.patch.object(ipmi, '_parse_driver_info', autospec=True)
def test_vendor_passthru_validate__parse_driver_info_fail(self, info_mock):
info_mock.side_effect = exception.InvalidParameterValue("bad")
with task_manager.acquire(self.context, self.node.uuid) as task:
self.assertRaises(exception.InvalidParameterValue,
self.vendor.validate,
task, method='send_raw', raw_bytes='0x00 0x01')
info_mock.assert_called_once_with(task.node)
def test_vendor_passthru_validate__send_raw_bytes_good(self):
with task_manager.acquire(self.context, self.node.uuid) as task:
self.vendor.validate(task,
method='send_raw',
http_method='POST',
raw_bytes='0x00 0x01')
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.vendor.validate,
task, method='send_raw')
@mock.patch.object(ipmi.VendorPassthru, 'send_raw', autospec=True)
def test_vendor_passthru_call_send_raw_bytes(self, raw_bytes_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
self.vendor.send_raw(task, http_method='POST',
raw_bytes='0x00 0x01')
raw_bytes_mock.assert_called_once_with(
self.vendor, task, http_method='POST',
raw_bytes='0x00 0x01')
def test_vendor_passthru_validate__bmc_reset_good(self):
with task_manager.acquire(self.context, self.node.uuid) as task:
self.vendor.validate(task, method='bmc_reset')
def test_vendor_passthru_validate__bmc_reset_warm_good(self):
with task_manager.acquire(self.context, self.node.uuid) as task:
self.vendor.validate(task, method='bmc_reset', warm=True)
def test_vendor_passthru_validate__bmc_reset_cold_good(self):
with task_manager.acquire(self.context, self.node.uuid) as task:
self.vendor.validate(task, method='bmc_reset', warm=False)
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def _vendor_passthru_call_bmc_reset(self, warm, expected,
mock_exec):
mock_exec.return_value = [None, None]
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
self.vendor.bmc_reset(task, 'POST', warm=warm)
mock_exec.assert_called_once_with(
mock.ANY, 'bmc reset %s' % expected)
def test_vendor_passthru_call_bmc_reset_warm(self):
for param in (True, 'true', 'on', 'y', 'yes'):
self._vendor_passthru_call_bmc_reset(param, 'warm')
def test_vendor_passthru_call_bmc_reset_cold(self):
for param in (False, 'false', 'off', 'n', 'no'):
self._vendor_passthru_call_bmc_reset(param, 'cold')
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))
def test_vendor_passthru_driver_routes(self):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
driver_routes = task.driver.vendor.driver_routes
self.assertIsInstance(driver_routes, dict)
self.assertEqual({}, driver_routes)
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def test_management_interface_set_boot_device_ok(self, mock_exec):
mock_exec.return_value = [None, None]
with task_manager.acquire(self.context, self.node.uuid) as task:
self.management.set_boot_device(task, boot_devices.PXE)
mock_calls = [mock.call(self.info, "raw 0x00 0x08 0x03 0x08"),
mock.call(self.info, "chassis bootdev pxe")]
mock_exec.assert_has_calls(mock_calls)
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def test_management_interface_force_set_boot_device_ok(self, mock_exec):
mock_exec.return_value = [None, None]
with task_manager.acquire(self.context, self.node.uuid) as task:
driver_info = task.node.driver_info
driver_info['ipmi_force_boot_device'] = True
task.node.driver_info = driver_info
self.info['force_boot_device'] = True
self.management.set_boot_device(task, boot_devices.PXE)
task.node.refresh()
self.assertIs(
False,
task.node.driver_internal_info['is_next_boot_persistent']
)
mock_calls = [mock.call(self.info, "raw 0x00 0x08 0x03 0x08"),
mock.call(self.info, "chassis bootdev pxe")]
mock_exec.assert_has_calls(mock_calls)
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def test_management_interface_set_boot_device_persistent(self, mock_exec):
mock_exec.return_value = [None, None]
with task_manager.acquire(self.context, self.node.uuid) as task:
driver_info = task.node.driver_info
driver_info['ipmi_force_boot_device'] = True
task.node.driver_info = driver_info
self.info['force_boot_device'] = True
self.management.set_boot_device(task, boot_devices.PXE, True)
self.assertEqual(
boot_devices.PXE,
task.node.driver_internal_info['persistent_boot_device'])
mock_calls = [mock.call(self.info, "raw 0x00 0x08 0x03 0x08"),
mock.call(self.info, "chassis bootdev pxe")]
mock_exec.assert_has_calls(mock_calls)
def test_management_interface_set_boot_device_bad_device(self):
with task_manager.acquire(self.context, self.node.uuid) as task:
self.assertRaises(exception.InvalidParameterValue,
self.management.set_boot_device,
task, 'fake-device')
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def test_management_interface_set_boot_device_exec_failed(self, mock_exec):
mock_exec.side_effect = processutils.ProcessExecutionError()
with task_manager.acquire(self.context, self.node.uuid) as task:
self.assertRaises(exception.IPMIFailure,
self.management.set_boot_device,
task, boot_devices.PXE)
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def test_management_interface_set_boot_device_unknown_exception(self,
mock_exec):
class FakeException(Exception):
pass
mock_exec.side_effect = FakeException('boom')
with task_manager.acquire(self.context, self.node.uuid) as task:
self.assertRaises(FakeException,
self.management.set_boot_device,
task, boot_devices.PXE)
@mock.patch.object(boot_mode_utils, 'get_boot_mode_for_deploy')
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def test_management_interface_set_boot_device_uefi(self, mock_exec,
mock_boot_mode):
mock_boot_mode.return_value = 'uefi'
mock_exec.return_value = [None, None]
with task_manager.acquire(self.context, self.node.uuid) as task:
self.management.set_boot_device(task, boot_devices.PXE)
mock_calls = [
mock.call(self.info, "raw 0x00 0x08 0x03 0x08"),
mock.call(self.info, "chassis bootdev pxe options=efiboot")
]
mock_exec.assert_has_calls(mock_calls)
@mock.patch.object(boot_mode_utils, 'get_boot_mode_for_deploy')
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def test_management_interface_set_boot_device_uefi_and_persistent(
self, mock_exec, mock_boot_mode):
mock_boot_mode.return_value = 'uefi'
mock_exec.return_value = [None, None]
with task_manager.acquire(self.context, self.node.uuid) as task:
self.management.set_boot_device(task, boot_devices.PXE,
persistent=True)
mock_calls = [
mock.call(self.info, "raw 0x00 0x08 0x03 0x08"),
mock.call(self.info, "raw 0x00 0x08 0x05 0xe0 0x04 0x00 0x00 0x00")
]
mock_exec.assert_has_calls(mock_calls)
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,
boot_devices.SAFE]
self.assertEqual(sorted(expected), sorted(task.driver.management.
get_supported_boot_devices(task)))
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def test_management_interface_get_boot_device(self, mock_exec):
# output, expected boot device
bootdevs = [('Boot Device Selector : '
'Force Boot from default Hard-Drive\n',
boot_devices.DISK),
('Boot Device Selector : '
'Force Boot from default Hard-Drive, request Safe-Mode\n',
boot_devices.SAFE),
('Boot Device Selector : '
'Force Boot into BIOS Setup\n',
boot_devices.BIOS),
('Boot Device Selector : '
'Force PXE\n',
boot_devices.PXE),
('Boot Device Selector : '
'Force Boot from CD/DVD\n',
boot_devices.CDROM)]
with task_manager.acquire(self.context, self.node.uuid) as task:
for out, expected_device in bootdevs:
mock_exec.return_value = (out, '')
expected_response = {'boot_device': expected_device,
'persistent': False}
self.assertEqual(expected_response,
task.driver.management.get_boot_device(task))
mock_exec.assert_called_with(mock.ANY,
"chassis bootparam get 5")
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def test_management_interface_get_boot_device_unknown_dev(self, mock_exec):
with task_manager.acquire(self.context, self.node.uuid) as task:
mock_exec.return_value = ('Boot Device Selector : Fake\n', '')
response = task.driver.management.get_boot_device(task)
self.assertIsNone(response['boot_device'])
mock_exec.assert_called_with(mock.ANY, "chassis bootparam get 5")
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def test_management_interface_get_boot_device_fail(self, mock_exec):
with task_manager.acquire(self.context, self.node.uuid) as task:
mock_exec.side_effect = processutils.ProcessExecutionError()
self.assertRaises(exception.IPMIFailure,
task.driver.management.get_boot_device, task)
mock_exec.assert_called_with(mock.ANY, "chassis bootparam get 5")
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def test_management_interface_get_boot_device_persistent(self, mock_exec):
outputs = [('Options apply to only next boot\n'
'Boot Device Selector : Force PXE\n',
False),
('Options apply to all future boots\n'
'Boot Device Selector : Force PXE\n',
True)]
with task_manager.acquire(self.context, self.node.uuid) as task:
for out, expected_persistent in outputs:
mock_exec.return_value = (out, '')
expected_response = {'boot_device': boot_devices.PXE,
'persistent': expected_persistent}
self.assertEqual(expected_response,
task.driver.management.get_boot_device(task))
mock_exec.assert_called_with(mock.ANY,
"chassis bootparam get 5")
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.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(),
management_interface='ipmitool')
with task_manager.acquire(self.context, node.uuid) as task:
self.assertRaises(exception.MissingParameterValue,
task.driver.management.validate, task)
@mock.patch.object(ipmi.LOG, 'error', spec_set=True, autospec=True)
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def test_management_interface_inject_nmi_ok(self, mock_exec, mock_log):
with task_manager.acquire(self.context, self.node.uuid) as task:
driver_info = ipmi._parse_driver_info(task.node)
self.management.inject_nmi(task)
mock_exec.assert_called_once_with(driver_info, "power diag")
self.assertFalse(mock_log.called)
@mock.patch.object(ipmi.LOG, 'error', spec_set=True, autospec=True)
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def test_management_interface_inject_nmi_fail(self, mock_exec, mock_log):
mock_exec.side_effect = exception.PasswordFileFailedToCreate('error')
with task_manager.acquire(self.context, self.node.uuid) as task:
driver_info = ipmi._parse_driver_info(task.node)
self.assertRaises(exception.IPMIFailure,
self.management.inject_nmi,
task)
mock_exec.assert_called_once_with(driver_info, "power diag")
self.assertTrue(mock_log.called)
def test__parse_ipmi_sensor_data_ok(self):
fake_sensors_data = """
Sensor ID : Temp (0x1)
Entity ID : 3.1 (Processor)
Sensor Type (Analog) : Temperature
Sensor Reading : -58 (+/- 1) degrees C
Status : ok
Nominal Reading : 50.000
Normal Minimum : 11.000
Normal Maximum : 69.000
Upper critical : 90.000
Upper non-critical : 85.000
Positive Hysteresis : 1.000
Negative Hysteresis : 1.000
Sensor ID : Temp (0x2)
Entity ID : 3.2 (Processor)
Sensor Type (Analog) : Temperature
Sensor Reading : 50 (+/- 1) degrees C
Status : ok
Nominal Reading : 50.000
Normal Minimum : 11.000
Normal Maximum : 69.000
Upper critical : 90.000
Upper non-critical : 85.000
Positive Hysteresis : 1.000
Negative Hysteresis : 1.000
Sensor ID : FAN MOD 1A RPM (0x30)
Entity ID : 7.1 (System Board)
Sensor Type (Analog) : Fan
Sensor Reading : 8400 (+/- 75) RPM
Status : ok
Nominal Reading : 5325.000
Normal Minimum : 10425.000
Normal Maximum : 14775.000
Lower critical : 4275.000
Positive Hysteresis : 375.000
Negative Hysteresis : 375.000
Sensor ID : FAN MOD 1B RPM (0x31)
Entity ID : 7.1 (System Board)
Sensor Type (Analog) : Fan
Sensor Reading : 8550 (+/- 75) RPM
Status : ok
Nominal Reading : 7800.000
Normal Minimum : 10425.000
Normal Maximum : 14775.000
Lower critical : 4275.000
Positive Hysteresis : 375.000
Negative Hysteresis : 375.000
"""
expected_return = {
'Fan': {
'FAN MOD 1A RPM (0x30)': {
'Status': 'ok',
'Sensor Reading': '8400 (+/- 75) RPM',
'Entity ID': '7.1 (System Board)',
'Normal Minimum': '10425.000',
'Positive Hysteresis': '375.000',
'Normal Maximum': '14775.000',
'Sensor Type (Analog)': 'Fan',
'Lower critical': '4275.000',
'Negative Hysteresis': '375.000',
'Sensor ID': 'FAN MOD 1A RPM (0x30)',
'Nominal Reading': '5325.000'
},
'FAN MOD 1B RPM (0x31)': {
'Status': 'ok',
'Sensor Reading': '8550 (+/- 75) RPM',
'Entity ID': '7.1 (System Board)',
'Normal Minimum': '10425.000',
'Positive Hysteresis': '375.000',
'Normal Maximum': '14775.000',
'Sensor Type (Analog)': 'Fan',
'Lower critical': '4275.000',
'Negative Hysteresis': '375.000',
'Sensor ID': 'FAN MOD 1B RPM (0x31)',
'Nominal Reading': '7800.000'
}
},
'Temperature': {
'Temp (0x1)': {
'Status': 'ok',
'Sensor Reading': '-58 (+/- 1) degrees C',
'Entity ID': '3.1 (Processor)',
'Normal Minimum': '11.000',
'Positive Hysteresis': '1.000',
'Upper non-critical': '85.000',
'Normal Maximum': '69.000',
'Sensor Type (Analog)': 'Temperature',
'Negative Hysteresis': '1.000',
'Upper critical': '90.000',
'Sensor ID': 'Temp (0x1)',
'Nominal Reading': '50.000'
},
'Temp (0x2)': {
'Status': 'ok',
'Sensor Reading': '50 (+/- 1) degrees C',
'Entity ID': '3.2 (Processor)',
'Normal Minimum': '11.000',
'Positive Hysteresis': '1.000',
'Upper non-critical': '85.000',
'Normal Maximum': '69.000',
'Sensor Type (Analog)': 'Temperature',
'Negative Hysteresis': '1.000',
'Upper critical': '90.000',
'Sensor ID': 'Temp (0x2)',
'Nominal Reading': '50.000'
}
}
}
ret = ipmi._parse_ipmi_sensors_data(self.node, fake_sensors_data)
self.assertEqual(expected_return, ret)
def test__parse_ipmi_sensor_data_missing_sensor_reading(self):
fake_sensors_data = """
Sensor ID : Temp (0x1)
Entity ID : 3.1 (Processor)
Sensor Type (Analog) : Temperature
Status : ok
Nominal Reading : 50.000
Normal Minimum : 11.000
Normal Maximum : 69.000
Upper critical : 90.000
Upper non-critical : 85.000
Positive Hysteresis : 1.000
Negative Hysteresis : 1.000
Sensor ID : Temp (0x2)
Entity ID : 3.2 (Processor)
Sensor Type (Analog) : Temperature
Sensor Reading : 50 (+/- 1) degrees C
Status : ok
Nominal Reading : 50.000
Normal Minimum : 11.000
Normal Maximum : 69.000
Upper critical : 90.000
Upper non-critical : 85.000
Positive Hysteresis : 1.000
Negative Hysteresis : 1.000
Sensor ID : FAN MOD 1A RPM (0x30)
Entity ID : 7.1 (System Board)
Sensor Type (Analog) : Fan
Sensor Reading : 8400 (+/- 75) RPM
Status : ok
Nominal Reading : 5325.000
Normal Minimum : 10425.000
Normal Maximum : 14775.000
Lower critical : 4275.000
Positive Hysteresis : 375.000
Negative Hysteresis : 375.000
"""
expected_return = {
'Fan': {
'FAN MOD 1A RPM (0x30)': {
'Status': 'ok',
'Sensor Reading': '8400 (+/- 75) RPM',
'Entity ID': '7.1 (System Board)',
'Normal Minimum': '10425.000',
'Positive Hysteresis': '375.000',
'Normal Maximum': '14775.000',
'Sensor Type (Analog)': 'Fan',
'Lower critical': '4275.000',
'Negative Hysteresis': '375.000',
'Sensor ID': 'FAN MOD 1A RPM (0x30)',
'Nominal Reading': '5325.000'
}
},
'Temperature': {
'Temp (0x2)': {
'Status': 'ok',
'Sensor Reading': '50 (+/- 1) degrees C',
'Entity ID': '3.2 (Processor)',
'Normal Minimum': '11.000',
'Positive Hysteresis': '1.000',
'Upper non-critical': '85.000',
'Normal Maximum': '69.000',
'Sensor Type (Analog)': 'Temperature',
'Negative Hysteresis': '1.000',
'Upper critical': '90.000',
'Sensor ID': 'Temp (0x2)',
'Nominal Reading': '50.000'
}
}
}
ret = ipmi._parse_ipmi_sensors_data(self.node, fake_sensors_data)
self.assertEqual(expected_return, ret)
def test__parse_ipmi_sensor_data_failed(self):
fake_sensors_data = "abcdef"
self.assertRaises(exception.FailedToParseSensorData,
ipmi._parse_ipmi_sensors_data,
self.node,
fake_sensors_data)
fake_sensors_data = "abc:def:ghi"
self.assertRaises(exception.FailedToParseSensorData,
ipmi._parse_ipmi_sensors_data,
self.node,
fake_sensors_data)
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def test_dump_sdr_ok(self, mock_exec):
mock_exec.return_value = (None, None)
with task_manager.acquire(self.context, self.node.uuid) as task:
ipmi.dump_sdr(task, 'foo_file')
mock_exec.assert_called_once_with(self.info, 'sdr dump foo_file')
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def test_dump_sdr_fail(self, mock_exec):
with task_manager.acquire(self.context, self.node.uuid) as task:
mock_exec.side_effect = processutils.ProcessExecutionError()
self.assertRaises(exception.IPMIFailure, ipmi.dump_sdr, task,
'foo_file')
mock_exec.assert_called_once_with(self.info, 'sdr dump foo_file')
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def test_send_raw_bytes_returns(self, mock_exec):
fake_ret = ('foo', 'bar')
mock_exec.return_value = fake_ret
with task_manager.acquire(self.context, self.node.uuid) as task:
ret = ipmi.send_raw(task, 'fake raw')
self.assertEqual(fake_ret, ret)
class IPMIToolShellinaboxTestCase(db_base.DbTestCase):
console_interface = 'ipmitool-shellinabox'
console_class = ipmi.IPMIShellinaboxConsole
def setUp(self):
super(IPMIToolShellinaboxTestCase, self).setUp()
self.config(enabled_console_interfaces=[self.console_interface,
'no-console'])
self.node = obj_utils.create_test_node(
self.context,
console_interface=self.console_interface,
driver_info=INFO_DICT)
self.info = ipmi._parse_driver_info(self.node)
self.console = self.console_class()
def test_console_validate(self):
with task_manager.acquire(
self.context, self.node.uuid, shared=True) as task:
task.node.driver_info['ipmi_terminal_port'] = 123
task.driver.console.validate(task)
def test_console_validate_missing_port(self):
with task_manager.acquire(
self.context, self.node.uuid, shared=True) as task:
task.node.driver_info.pop('ipmi_terminal_port', None)
self.assertRaises(exception.MissingParameterValue,
task.driver.console.validate, task)
def test_console_validate_invalid_port(self):
with task_manager.acquire(
self.context, self.node.uuid, shared=True) as task:
task.node.driver_info['ipmi_terminal_port'] = ''
self.assertRaises(exception.InvalidParameterValue,
task.driver.console.validate, task)
def test_console_validate_wrong_ipmi_protocol_version(self):
with task_manager.acquire(
self.context, self.node.uuid, shared=True) as task:
task.node.driver_info['ipmi_terminal_port'] = 123
task.node.driver_info['ipmi_protocol_version'] = '1.5'
self.assertRaises(exception.InvalidParameterValue,
task.driver.console.validate, task)
def test__get_ipmi_cmd(self):
with task_manager.acquire(self.context,
self.node.uuid) as task:
driver_info = ipmi._parse_driver_info(task.node)
ipmi_cmd = self.console._get_ipmi_cmd(driver_info, 'pw_file')
expected_ipmi_cmd = ("/:%(uid)s:%(gid)s:HOME:ipmitool "
"-H %(address)s -I lanplus -U %(user)s "
"-f pw_file" %
{'uid': os.getuid(), 'gid': os.getgid(),
'address': driver_info['address'],
'user': driver_info['username']})
self.assertEqual(expected_ipmi_cmd, ipmi_cmd)
def test__get_ipmi_cmd_without_user(self):
with task_manager.acquire(self.context,
self.node.uuid) as task:
driver_info = ipmi._parse_driver_info(task.node)
driver_info['username'] = None
ipmi_cmd = self.console._get_ipmi_cmd(driver_info, 'pw_file')
expected_ipmi_cmd = ("/:%(uid)s:%(gid)s:HOME:ipmitool "
"-H %(address)s -I lanplus "
"-f pw_file" %
{'uid': os.getuid(), 'gid': os.getgid(),
'address': driver_info['address']})
self.assertEqual(expected_ipmi_cmd, ipmi_cmd)
@mock.patch.object(ipmi.IPMIConsole, '_start_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.console.start_console(task)
driver_info = ipmi._parse_driver_info(task.node)
mock_start.assert_called_once_with(
self.console, driver_info,
console_utils.start_shellinabox_console)
@mock.patch.object(ipmi.IPMIConsole, '_get_ipmi_cmd', autospec=True)
@mock.patch.object(console_utils, 'start_shellinabox_console',
autospec=True)
def test__start_console(self, mock_start, mock_ipmi_cmd):
mock_start.return_value = None
with task_manager.acquire(self.context,
self.node.uuid) as task:
driver_info = ipmi._parse_driver_info(task.node)
self.console._start_console(
driver_info, console_utils.start_shellinabox_console)
mock_start.assert_called_once_with(self.info['uuid'],
self.info['port'],
mock.ANY)
mock_ipmi_cmd.assert_called_once_with(self.console,
driver_info, mock.ANY)
@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:
driver_info = ipmi._parse_driver_info(task.node)
self.assertRaises(exception.ConsoleSubprocessFailed,
self.console._start_console,
driver_info,
console_utils.start_shellinabox_console)
@mock.patch.object(console_utils, 'start_shellinabox_console',
autospec=True)
def test__start_console_fail_nodir(self, mock_start):
mock_start.side_effect = exception.ConsoleError()
with task_manager.acquire(self.context,
self.node.uuid) as task:
driver_info = ipmi._parse_driver_info(task.node)
self.assertRaises(exception.ConsoleError,
self.console._start_console,
driver_info,
console_utils.start_shellinabox_console)
mock_start.assert_called_once_with(self.node.uuid, mock.ANY, mock.ANY)
@mock.patch.object(console_utils, 'make_persistent_password_file',
autospec=True)
@mock.patch.object(console_utils, 'start_shellinabox_console',
autospec=True)
def test__start_console_empty_password(self, mock_start, mock_pass):
driver_info = self.node.driver_info
del driver_info['ipmi_password']
self.node.driver_info = driver_info
self.node.save()
with task_manager.acquire(self.context,
self.node.uuid) as task:
driver_info = ipmi._parse_driver_info(task.node)
self.console._start_console(
driver_info, console_utils.start_shellinabox_console)
mock_pass.assert_called_once_with(mock.ANY, '\0')
mock_start.assert_called_once_with(self.info['uuid'],
self.info['port'],
mock.ANY)
@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.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.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 = 'http://localhost:4201'
mock_get.return_value = url
expected = {'type': 'shellinabox', 'url': url}
with task_manager.acquire(self.context,
self.node.uuid) as task:
console_info = self.console.get_console(task)
self.assertEqual(expected, console_info)
mock_get.assert_called_once_with(self.info['port'])
class IPMIToolSocatDriverTestCase(IPMIToolShellinaboxTestCase):
console_interface = 'ipmitool-shellinabox'
console_class = ipmi.IPMISocatConsole
def test__get_ipmi_cmd(self):
with task_manager.acquire(self.context,
self.node.uuid) as task:
driver_info = ipmi._parse_driver_info(task.node)
ipmi_cmd = self.console._get_ipmi_cmd(driver_info, 'pw_file')
expected_ipmi_cmd = ("ipmitool -H %(address)s -I lanplus "
"-U %(user)s -f pw_file" %
{'address': driver_info['address'],
'user': driver_info['username']})
self.assertEqual(expected_ipmi_cmd, ipmi_cmd)
def test__get_ipmi_cmd_without_user(self):
with task_manager.acquire(self.context,
self.node.uuid) as task:
driver_info = ipmi._parse_driver_info(task.node)
driver_info['username'] = None
ipmi_cmd = self.console._get_ipmi_cmd(driver_info, 'pw_file')
expected_ipmi_cmd = ("ipmitool -H %(address)s -I lanplus "
"-f pw_file" %
{'address': driver_info['address']})
self.assertEqual(expected_ipmi_cmd, ipmi_cmd)
@mock.patch.object(ipmi.IPMIConsole, '_start_console', autospec=True)
@mock.patch.object(ipmi.IPMISocatConsole, '_exec_stop_console',
autospec=True)
def test_start_console(self, mock_stop, mock_start):
mock_start.return_value = None
mock_stop.return_value = None
with task_manager.acquire(self.context,
self.node.uuid) as task:
self.console.start_console(task)
driver_info = ipmi._parse_driver_info(task.node)
mock_stop.assert_called_once_with(self.console, driver_info)
mock_start.assert_called_once_with(
self.console, driver_info,
console_utils.start_socat_console)
@mock.patch.object(ipmi.IPMISocatConsole, '_get_ipmi_cmd', autospec=True)
@mock.patch.object(console_utils, 'start_socat_console',
autospec=True)
def test__start_console(self, mock_start, mock_ipmi_cmd):
mock_start.return_value = None
with task_manager.acquire(self.context,
self.node.uuid) as task:
driver_info = ipmi._parse_driver_info(task.node)
self.console._start_console(
driver_info, console_utils.start_socat_console)
mock_start.assert_called_once_with(self.info['uuid'],
self.info['port'],
mock.ANY)
mock_ipmi_cmd.assert_called_once_with(self.console,
driver_info, mock.ANY)
@mock.patch.object(console_utils, 'start_socat_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:
driver_info = ipmi._parse_driver_info(task.node)
self.assertRaises(exception.ConsoleSubprocessFailed,
self.console._start_console,
driver_info,
console_utils.start_socat_console)
mock_start.assert_called_once_with(self.info['uuid'],
self.info['port'],
mock.ANY)
@mock.patch.object(console_utils, 'start_socat_console',
autospec=True)
def test__start_console_fail_nodir(self, mock_start):
mock_start.side_effect = exception.ConsoleError()
with task_manager.acquire(self.context,
self.node.uuid) as task:
driver_info = ipmi._parse_driver_info(task.node)
self.assertRaises(exception.ConsoleError,
self.console._start_console,
driver_info,
console_utils.start_socat_console)
mock_start.assert_called_once_with(self.node.uuid, mock.ANY, mock.ANY)
@mock.patch.object(console_utils, 'make_persistent_password_file',
autospec=True)
@mock.patch.object(console_utils, 'start_socat_console',
autospec=True)
def test__start_console_empty_password(self, mock_start, mock_pass):
driver_info = self.node.driver_info
del driver_info['ipmi_password']
self.node.driver_info = driver_info
self.node.save()
with task_manager.acquire(self.context,
self.node.uuid) as task:
driver_info = ipmi._parse_driver_info(task.node)
self.console._start_console(
driver_info, console_utils.start_socat_console)
mock_pass.assert_called_once_with(mock.ANY, '\0')
mock_start.assert_called_once_with(self.info['uuid'],
self.info['port'],
mock.ANY)
@mock.patch.object(ipmi.IPMISocatConsole, '_exec_stop_console',
autospec=True)
@mock.patch.object(console_utils, 'stop_socat_console',
autospec=True)
def test_stop_console(self, mock_stop, mock_exec_stop):
mock_stop.return_value = None
with task_manager.acquire(self.context,
self.node.uuid) as task:
driver_info = ipmi._parse_driver_info(task.node)
self.console.stop_console(task)
mock_stop.assert_called_once_with(self.info['uuid'])
mock_exec_stop.assert_called_once_with(self.console,
driver_info)
@mock.patch.object(ipmi.IPMISocatConsole, '_exec_stop_console',
autospec=True)
@mock.patch.object(ironic_utils, 'unlink_without_raise',
autospec=True)
@mock.patch.object(console_utils, 'stop_socat_console',
autospec=True)
def test_stop_console_fail(self, mock_stop, mock_unlink, mock_exec_stop):
mock_stop.side_effect = exception.ConsoleError()
with task_manager.acquire(self.context,
self.node.uuid) as task:
self.assertRaises(exception.ConsoleError,
self.console.stop_console,
task)
mock_stop.assert_called_once_with(self.node.uuid)
mock_unlink.assert_called_once_with(
ipmi._console_pwfile_path(self.node.uuid))
self.assertFalse(mock_exec_stop.called)
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def test__exec_stop_console(self, mock_exec):
with task_manager.acquire(self.context,
self.node.uuid) as task:
driver_info = ipmi._parse_driver_info(task.node)
self.console._exec_stop_console(driver_info)
mock_exec.assert_called_once_with(
driver_info, 'sol deactivate', check_exit_code=[0, 1])
@mock.patch.object(console_utils, 'get_socat_console_url',
autospec=True)
def test_get_console(self, mock_get_url):
url = 'tcp://localhost:4201'
mock_get_url.return_value = url
expected = {'type': 'socat', 'url': url}
with task_manager.acquire(self.context,
self.node.uuid) as task:
console_info = self.console.get_console(task)
self.assertEqual(expected, console_info)
mock_get_url.assert_called_once_with(self.info['port'])