ironic/ironic/tests/conductor/test_manager.py

2281 lines
107 KiB
Python

# coding=utf-8
# Copyright 2013 Hewlett-Packard Development Company, L.P.
# Copyright 2013 International Business Machines Corporation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Test class for Ironic ManagerService."""
import eventlet
import mock
from oslo.config import cfg
from oslo import messaging
from ironic.common import boot_devices
from ironic.common import driver_factory
from ironic.common import exception
from ironic.common import states
from ironic.common import utils as ironic_utils
from ironic.conductor import manager
from ironic.conductor import task_manager
from ironic.conductor import utils as conductor_utils
from ironic.db import api as dbapi
from ironic.drivers import base as drivers_base
from ironic import objects
from ironic.openstack.common import context
from ironic.tests import base as tests_base
from ironic.tests.conductor import utils as mgr_utils
from ironic.tests.db import base as tests_db_base
from ironic.tests.db import utils
from ironic.tests.objects import utils as obj_utils
CONF = cfg.CONF
class _CommonMixIn(object):
@staticmethod
def _create_node(**kwargs):
attrs = {'id': 1,
'uuid': ironic_utils.generate_uuid(),
'provision_state': states.POWER_OFF,
'maintenance': False,
'reservation': None}
attrs.update(kwargs)
node = mock.Mock(spec_set=objects.Node)
for attr in attrs:
setattr(node, attr, attrs[attr])
return node
def _create_task(self, node=None, node_attrs=None):
if node_attrs is None:
node_attrs = {}
if node is None:
node = self._create_node(**node_attrs)
task = mock.Mock(spec_set=['node', 'release_resources',
'spawn_after'])
task.node = node
return task
def _get_nodeinfo_list_response(self, nodes=None):
if nodes is None:
nodes = [self.node]
elif not isinstance(nodes, (list, tuple)):
nodes = [nodes]
return [tuple(getattr(n, c) for c in self.columns) for n in nodes]
def _get_acquire_side_effect(self, task_infos):
"""Helper method to generate a task_manager.acquire() side effect.
This accepts a list of information about task mocks to return.
task_infos can be a single entity or a list.
Each task_info can be a single entity, the task to return, or it
can be a tuple of (task, exception_to_raise_on_exit). 'task' can
be an exception to raise on __enter__.
Examples: _get_acquire_side_effect(self, task): Yield task
_get_acquire_side_effect(self, [task, enter_exception(),
(task2, exit_exception())])
Yield task on first call to acquire()
raise enter_exception() in __enter__ on 2nd call to
acquire()
Yield task2 on 3rd call to acquire(), but raise
exit_exception() on __exit__()
"""
tasks = []
exit_exceptions = []
if not isinstance(task_infos, list):
task_infos = [task_infos]
for task_info in task_infos:
if isinstance(task_info, tuple):
task, exc = task_info
else:
task = task_info
exc = None
tasks.append(task)
exit_exceptions.append(exc)
class FakeAcquire(object):
def __init__(fa_self, context, node_id, *args, **kwargs):
# We actually verify these arguments via
# acquire_mock.call_args_list(). However, this stores the
# node_id so we can assert we're returning the correct node
# in __enter__().
fa_self.node_id = node_id
def __enter__(fa_self):
task = tasks.pop(0)
if isinstance(task, Exception):
raise task
# NOTE(comstud): Not ideal to throw this into
# a helper, however it's the cleanest way
# to verify we're dealing with the correct task/node.
if ironic_utils.is_int_like(fa_self.node_id):
self.assertEqual(fa_self.node_id, task.node.id)
else:
self.assertEqual(fa_self.node_id, task.node.uuid)
return task
def __exit__(fa_self, exc_typ, exc_val, exc_tb):
exc = exit_exceptions.pop(0)
if exc_typ is None and exc is not None:
raise exc
return FakeAcquire
class _ServiceSetUpMixin(object):
def setUp(self):
super(_ServiceSetUpMixin, self).setUp()
self.hostname = 'test-host'
self.config(enabled_drivers=['fake'])
self.config(node_locked_retry_attempts=1, group='conductor')
self.config(node_locked_retry_interval=0, group='conductor')
self.service = manager.ConductorManager(self.hostname, 'test-topic')
self.dbapi = dbapi.get_instance()
mgr_utils.mock_the_extension_manager()
self.driver = driver_factory.get_driver("fake")
def _stop_service(self):
try:
objects.Conductor.get_by_hostname(self.context, self.hostname)
except exception.ConductorNotFound:
return
self.service.del_host()
def _start_service(self):
self.service.init_host()
self.addCleanup(self._stop_service)
def _mock_record_keepalive(func_or_class):
return mock.patch.object(
manager.ConductorManager,
'_conductor_service_record_keepalive',
lambda: None)(func_or_class)
@_mock_record_keepalive
class StartStopTestCase(_ServiceSetUpMixin, tests_db_base.DbTestCase):
def test_start_registers_conductor(self):
self.assertRaises(exception.ConductorNotFound,
objects.Conductor.get_by_hostname,
self.context, self.hostname)
self._start_service()
res = objects.Conductor.get_by_hostname(self.context, self.hostname)
self.assertEqual(self.hostname, res['hostname'])
def test_stop_unregisters_conductor(self):
self._start_service()
res = objects.Conductor.get_by_hostname(self.context, self.hostname)
self.assertEqual(self.hostname, res['hostname'])
self.service.del_host()
self.assertRaises(exception.ConductorNotFound,
objects.Conductor.get_by_hostname,
self.context, self.hostname)
def test_start_registers_driver_names(self):
init_names = ['fake1', 'fake2']
restart_names = ['fake3', 'fake4']
df = driver_factory.DriverFactory()
with mock.patch.object(df._extension_manager, 'names') as mock_names:
# verify driver names are registered
self.config(enabled_drivers=init_names)
mock_names.return_value = init_names
self._start_service()
res = objects.Conductor.get_by_hostname(self.context,
self.hostname)
self.assertEqual(init_names, res['drivers'])
# verify that restart registers new driver names
self.config(enabled_drivers=restart_names)
mock_names.return_value = restart_names
self._start_service()
res = objects.Conductor.get_by_hostname(self.context,
self.hostname)
self.assertEqual(restart_names, res['drivers'])
@mock.patch.object(driver_factory.DriverFactory, '__init__')
def test_start_fails_on_missing_driver(self, mock_df):
mock_df.side_effect = exception.DriverNotFound('test')
with mock.patch.object(self.dbapi, 'register_conductor') as mock_reg:
self.assertRaises(exception.DriverNotFound,
self.service.init_host)
self.assertTrue(mock_df.called)
self.assertFalse(mock_reg.called)
class KeepAliveTestCase(_ServiceSetUpMixin, tests_db_base.DbTestCase):
def test__conductor_service_record_keepalive(self):
self._start_service()
# avoid wasting time at the event.wait()
CONF.set_override('heartbeat_interval', 0, 'conductor')
with mock.patch.object(self.dbapi, 'touch_conductor') as mock_touch:
with mock.patch.object(self.service._keepalive_evt, 'is_set') as \
mock_is_set:
mock_is_set.side_effect = [False, True]
self.service._conductor_service_record_keepalive()
mock_touch.assert_called_once_with(self.hostname)
@_mock_record_keepalive
class ChangeNodePowerStateTestCase(_ServiceSetUpMixin,
tests_db_base.DbTestCase):
def test_change_node_power_state_power_on(self):
# Test change_node_power_state including integration with
# conductor.utils.node_power_action and lower.
node = obj_utils.create_test_node(self.context,
driver='fake',
power_state=states.POWER_OFF)
self._start_service()
with mock.patch.object(self.driver.power, 'get_power_state') \
as get_power_mock:
get_power_mock.return_value = states.POWER_OFF
self.service.change_node_power_state(self.context,
node.uuid,
states.POWER_ON)
self.service._worker_pool.waitall()
get_power_mock.assert_called_once_with(mock.ANY)
node.refresh()
self.assertEqual(states.POWER_ON, node.power_state)
self.assertIsNone(node.target_power_state)
self.assertIsNone(node.last_error)
# Verify the reservation has been cleared by
# background task's link callback.
self.assertIsNone(node.reservation)
@mock.patch.object(conductor_utils, 'node_power_action')
def test_change_node_power_state_node_already_locked(self,
pwr_act_mock):
# Test change_node_power_state with mocked
# conductor.utils.node_power_action.
fake_reservation = 'fake-reserv'
pwr_state = states.POWER_ON
node = obj_utils.create_test_node(self.context, driver='fake',
power_state=pwr_state,
reservation=fake_reservation)
self._start_service()
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.change_node_power_state,
self.context,
node.uuid,
states.POWER_ON)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.NodeLocked, exc.exc_info[0])
# In this test worker should not be spawned, but waiting to make sure
# the below perform_mock assertion is valid.
self.service._worker_pool.waitall()
self.assertFalse(pwr_act_mock.called, 'node_power_action has been '
'unexpectedly called.')
# Verify existing reservation wasn't broken.
node.refresh()
self.assertEqual(fake_reservation, node.reservation)
def test_change_node_power_state_worker_pool_full(self):
# Test change_node_power_state including integration with
# conductor.utils.node_power_action and lower.
initial_state = states.POWER_OFF
node = obj_utils.create_test_node(self.context, driver='fake',
power_state=initial_state)
self._start_service()
with mock.patch.object(self.service, '_spawn_worker') \
as spawn_mock:
spawn_mock.side_effect = exception.NoFreeConductorWorker()
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.change_node_power_state,
self.context,
node.uuid,
states.POWER_ON)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.NoFreeConductorWorker, exc.exc_info[0])
spawn_mock.assert_called_once_with(mock.ANY, mock.ANY,
mock.ANY)
node.refresh()
self.assertEqual(initial_state, node.power_state)
self.assertIsNone(node.target_power_state)
self.assertIsNone(node.last_error)
# Verify the picked reservation has been cleared due to full pool.
self.assertIsNone(node.reservation)
def test_change_node_power_state_exception_in_background_task(
self):
# Test change_node_power_state including integration with
# conductor.utils.node_power_action and lower.
initial_state = states.POWER_OFF
node = obj_utils.create_test_node(self.context, driver='fake',
power_state=initial_state)
self._start_service()
with mock.patch.object(self.driver.power, 'get_power_state') \
as get_power_mock:
get_power_mock.return_value = states.POWER_OFF
with mock.patch.object(self.driver.power, 'set_power_state') \
as set_power_mock:
new_state = states.POWER_ON
set_power_mock.side_effect = exception.PowerStateFailure(
pstate=new_state
)
self.service.change_node_power_state(self.context,
node.uuid,
new_state)
self.service._worker_pool.waitall()
get_power_mock.assert_called_once_with(mock.ANY)
set_power_mock.assert_called_once_with(mock.ANY, new_state)
node.refresh()
self.assertEqual(initial_state, node.power_state)
self.assertIsNone(node.target_power_state)
self.assertIsNotNone(node.last_error)
# Verify the reservation has been cleared by background task's
# link callback despite exception in background task.
self.assertIsNone(node.reservation)
def test_change_node_power_state_validate_fail(self):
# Test change_node_power_state where task.driver.power.validate
# fails and raises an exception
initial_state = states.POWER_ON
node = obj_utils.create_test_node(self.context, driver='fake',
power_state=initial_state)
self._start_service()
with mock.patch.object(self.driver.power, 'validate') \
as validate_mock:
validate_mock.side_effect = exception.InvalidParameterValue(
'wrong power driver info')
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.change_node_power_state,
self.context,
node.uuid,
states.POWER_ON)
self.assertEqual(exception.InvalidParameterValue, exc.exc_info[0])
node.refresh()
validate_mock.assert_called_once_with(mock.ANY)
self.assertEqual(states.POWER_ON, node.power_state)
self.assertIsNone(node.target_power_state)
self.assertIsNone(node.last_error)
@_mock_record_keepalive
class UpdateNodeTestCase(_ServiceSetUpMixin, tests_db_base.DbTestCase):
def test_update_node(self):
node = obj_utils.create_test_node(self.context, driver='fake',
extra={'test': 'one'})
# check that ManagerService.update_node actually updates the node
node.extra = {'test': 'two'}
res = self.service.update_node(self.context, node)
self.assertEqual({'test': 'two'}, res['extra'])
def test_update_node_already_locked(self):
node = obj_utils.create_test_node(self.context, driver='fake',
extra={'test': 'one'})
# check that it fails if something else has locked it already
with task_manager.acquire(self.context, node['id'], shared=False):
node.extra = {'test': 'two'}
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.update_node,
self.context,
node)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.NodeLocked, exc.exc_info[0])
# verify change did not happen
res = objects.Node.get_by_uuid(self.context, node['uuid'])
self.assertEqual({'test': 'one'}, res['extra'])
def test_associate_node_invalid_state(self):
node = obj_utils.create_test_node(self.context, driver='fake',
extra={'test': 'one'},
instance_uuid=None,
power_state=states.POWER_ON)
# check that it fails because state is POWER_ON
node.instance_uuid = 'fake-uuid'
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.update_node,
self.context,
node)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.NodeInWrongPowerState, exc.exc_info[0])
# verify change did not happen
node.refresh()
self.assertIsNone(node.instance_uuid)
def test_associate_node_valid_state(self):
node = obj_utils.create_test_node(self.context, driver='fake',
instance_uuid=None,
power_state=states.NOSTATE)
with mock.patch('ironic.drivers.modules.fake.FakePower.'
'get_power_state') as mock_get_power_state:
mock_get_power_state.return_value = states.POWER_OFF
node.instance_uuid = 'fake-uuid'
self.service.update_node(self.context, node)
# Check if the change was applied
node.instance_uuid = 'meow'
node.refresh()
self.assertEqual('fake-uuid', node.instance_uuid)
def test_update_node_invalid_driver(self):
existing_driver = 'fake'
wrong_driver = 'wrong-driver'
node = obj_utils.create_test_node(self.context,
driver=existing_driver,
extra={'test': 'one'},
instance_uuid=None,
task_state=states.POWER_ON)
# check that it fails because driver not found
node.driver = wrong_driver
node.driver_info = {}
self.assertRaises(exception.DriverNotFound,
self.service.update_node,
self.context,
node)
# verify change did not happen
node.refresh()
self.assertEqual(existing_driver, node.driver)
@_mock_record_keepalive
class VendorPassthruTestCase(_ServiceSetUpMixin, tests_db_base.DbTestCase):
def test_vendor_passthru_success(self):
node = obj_utils.create_test_node(self.context, driver='fake')
info = {'bar': 'baz'}
self._start_service()
self.service.vendor_passthru(
self.context, node.uuid, 'first_method', info)
# Waiting to make sure the below assertions are valid.
self.service._worker_pool.waitall()
node.refresh()
self.assertIsNone(node.last_error)
# Verify reservation has been cleared.
self.assertIsNone(node.reservation)
def test_vendor_passthru_node_already_locked(self):
fake_reservation = 'test_reserv'
node = obj_utils.create_test_node(self.context, driver='fake',
reservation=fake_reservation)
info = {'bar': 'baz'}
self._start_service()
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.vendor_passthru,
self.context, node.uuid, 'first_method', info)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.NodeLocked, exc.exc_info[0])
node.refresh()
self.assertIsNone(node.last_error)
# Verify the existing reservation is not broken.
self.assertEqual(fake_reservation, node.reservation)
def test_vendor_passthru_unsupported_method(self):
node = obj_utils.create_test_node(self.context, driver='fake')
info = {'bar': 'baz'}
self._start_service()
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.vendor_passthru,
self.context,
node.uuid, 'unsupported_method', info)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.UnsupportedDriverExtension,
exc.exc_info[0])
node.refresh()
self.assertIsNone(node.last_error)
# Verify reservation has been cleared.
self.assertIsNone(node.reservation)
def test_vendor_passthru_missing_method_parameters(self):
node = obj_utils.create_test_node(self.context, driver='fake')
info = {'invalid_param': 'whatever'}
self._start_service()
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.vendor_passthru,
self.context, node.uuid, 'first_method', info)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.MissingParameterValue, exc.exc_info[0])
node.refresh()
self.assertIsNone(node.last_error)
# Verify reservation has been cleared.
self.assertIsNone(node.reservation)
def test_vendor_passthru_vendor_interface_not_supported(self):
node = obj_utils.create_test_node(self.context, driver='fake')
info = {'bar': 'baz'}
self.driver.vendor = None
self._start_service()
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.vendor_passthru,
self.context,
node.uuid, 'whatever_method', info)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.UnsupportedDriverExtension,
exc.exc_info[0])
node.refresh()
# Verify reservation has been cleared.
self.assertIsNone(node.reservation)
def test_vendor_passthru_worker_pool_full(self):
node = obj_utils.create_test_node(self.context, driver='fake')
info = {'bar': 'baz'}
self._start_service()
with mock.patch.object(self.service, '_spawn_worker') \
as spawn_mock:
spawn_mock.side_effect = exception.NoFreeConductorWorker()
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.vendor_passthru,
self.context,
node.uuid, 'first_method', info)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.NoFreeConductorWorker, exc.exc_info[0])
# Waiting to make sure the below assertions are valid.
self.service._worker_pool.waitall()
node.refresh()
self.assertIsNone(node.last_error)
# Verify reservation has been cleared.
self.assertIsNone(node.reservation)
def test_driver_vendor_passthru_success(self):
expected = {'foo': 'bar'}
self.driver.vendor = vendor = mock.Mock()
vendor.driver_vendor_passthru.return_value = expected
self.service.init_host()
got = self.service.driver_vendor_passthru(self.context,
'fake',
'test_method',
{'test': 'arg'})
self.assertEqual(expected, got)
vendor.driver_vendor_passthru.assert_called_once_with(
mock.ANY,
method='test_method',
test='arg')
def test_driver_vendor_passthru_vendor_interface_not_supported(self):
# Test for when no vendor interface is set at all
self.driver.vendor = None
self.service.init_host()
exc = self.assertRaises(messaging.ExpectedException,
self.service.driver_vendor_passthru,
self.context,
'fake',
'test_method',
{})
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.UnsupportedDriverExtension,
exc.exc_info[0])
def test_driver_vendor_passthru_not_supported(self):
# Test for when the vendor interface is set, but hasn't passed a
# driver_passthru_mapping to MixinVendorInterface
self.service.init_host()
exc = self.assertRaises(messaging.ExpectedException,
self.service.driver_vendor_passthru,
self.context,
'fake',
'test_method',
{})
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.UnsupportedDriverExtension,
exc.exc_info[0])
def test_driver_vendor_passthru_driver_not_found(self):
self.service.init_host()
self.assertRaises(messaging.ExpectedException,
self.service.driver_vendor_passthru,
self.context,
'does_not_exist',
'test_method',
{})
@_mock_record_keepalive
class DoNodeDeployTearDownTestCase(_ServiceSetUpMixin,
tests_db_base.DbTestCase):
def test_do_node_deploy_invalid_state(self):
# test node['provision_state'] is not NOSTATE
node = obj_utils.create_test_node(self.context, driver='fake',
provision_state=states.ACTIVE)
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.do_node_deploy,
self.context, node['uuid'])
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.InstanceDeployFailure, exc.exc_info[0])
# This is a sync operation last_error should be None.
self.assertIsNone(node.last_error)
# Verify reservation has been cleared.
self.assertIsNone(node.reservation)
def test_do_node_deploy_maintenance(self):
node = obj_utils.create_test_node(self.context, driver='fake',
maintenance=True)
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.do_node_deploy,
self.context, node['uuid'])
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.NodeInMaintenance, exc.exc_info[0])
# This is a sync operation last_error should be None.
self.assertIsNone(node.last_error)
# Verify reservation has been cleared.
self.assertIsNone(node.reservation)
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.validate')
def test_do_node_deploy_validate_fail(self, mock_validate):
# InvalidParameterValue should be re-raised as InstanceDeployFailure
mock_validate.side_effect = exception.InvalidParameterValue('error')
node = obj_utils.create_test_node(self.context, driver='fake')
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.do_node_deploy,
self.context, node.uuid)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.InstanceDeployFailure, exc.exc_info[0])
# This is a sync operation last_error should be None.
self.assertIsNone(node.last_error)
# Verify reservation has been cleared.
self.assertIsNone(node.reservation)
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
def test__do_node_deploy_driver_raises_error(self, mock_deploy):
# test when driver.deploy.deploy raises an exception
mock_deploy.side_effect = exception.InstanceDeployFailure('test')
node = obj_utils.create_test_node(self.context, driver='fake',
provision_state=states.NOSTATE)
task = task_manager.TaskManager(self.context, node.uuid)
self.assertRaises(exception.InstanceDeployFailure,
self.service._do_node_deploy,
self.context, task)
node.refresh()
self.assertEqual(states.DEPLOYFAIL, node.provision_state)
self.assertEqual(states.NOSTATE, node.target_provision_state)
self.assertIsNotNone(node.last_error)
mock_deploy.assert_called_once_with(mock.ANY)
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
def test__do_node_deploy_ok(self, mock_deploy):
# test when driver.deploy.deploy returns DEPLOYDONE
mock_deploy.return_value = states.DEPLOYDONE
node = obj_utils.create_test_node(self.context, driver='fake',
provision_state=states.NOSTATE)
task = task_manager.TaskManager(self.context, node.uuid)
self.service._do_node_deploy(self.context, task)
node.refresh()
self.assertEqual(states.ACTIVE, node.provision_state)
self.assertEqual(states.NOSTATE, node.target_provision_state)
self.assertIsNone(node.last_error)
mock_deploy.assert_called_once_with(mock.ANY)
def test_do_node_deploy_partial_ok(self):
self._start_service()
thread = self.service._spawn_worker(lambda: None)
with mock.patch.object(self.service, '_spawn_worker') as mock_spawn:
mock_spawn.return_value = thread
node = obj_utils.create_test_node(self.context, driver='fake',
provision_state=states.NOSTATE)
self.service.do_node_deploy(self.context, node.uuid)
self.service._worker_pool.waitall()
node.refresh()
self.assertEqual(states.DEPLOYING, node.provision_state)
self.assertEqual(states.DEPLOYDONE, node.target_provision_state)
# This is a sync operation last_error should be None.
self.assertIsNone(node.last_error)
# Verify reservation has been cleared.
self.assertIsNone(node.reservation)
mock_spawn.assert_called_once_with(mock.ANY, mock.ANY, mock.ANY)
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
def test_do_node_deploy_rebuild_active_state(self, mock_deploy):
self._start_service()
mock_deploy.return_value = states.DEPLOYING
node = obj_utils.create_test_node(self.context, driver='fake',
provision_state=states.ACTIVE)
self.service.do_node_deploy(self.context, node.uuid, rebuild=True)
self.service._worker_pool.waitall()
node.refresh()
self.assertEqual(states.DEPLOYING, node.provision_state)
self.assertEqual(states.DEPLOYDONE, node.target_provision_state)
# last_error should be None.
self.assertIsNone(node.last_error)
# Verify reservation has been cleared.
self.assertIsNone(node.reservation)
mock_deploy.assert_called_once()
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
def test_do_node_deploy_rebuild_deployfail_state(self, mock_deploy):
self._start_service()
mock_deploy.return_value = states.DEPLOYING
node = obj_utils.create_test_node(self.context, driver='fake',
provision_state=states.DEPLOYFAIL)
self.service.do_node_deploy(self.context, node.uuid, rebuild=True)
self.service._worker_pool.waitall()
node.refresh()
self.assertEqual(states.DEPLOYING, node.provision_state)
self.assertEqual(states.DEPLOYDONE, node.target_provision_state)
# last_error should be None.
self.assertIsNone(node.last_error)
# Verify reservation has been cleared.
self.assertIsNone(node.reservation)
mock_deploy.assert_called_once()
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
def test_do_node_deploy_rebuild_error_state(self, mock_deploy):
self._start_service()
mock_deploy.return_value = states.DEPLOYING
node = obj_utils.create_test_node(self.context, driver='fake',
provision_state=states.ERROR)
self.service.do_node_deploy(self.context, node.uuid, rebuild=True)
self.service._worker_pool.waitall()
node.refresh()
self.assertEqual(states.DEPLOYING, node.provision_state)
self.assertEqual(states.DEPLOYDONE, node.target_provision_state)
# last_error should be None.
self.assertIsNone(node.last_error)
# Verify reservation has been cleared.
self.assertIsNone(node.reservation)
mock_deploy.assert_called_once()
def test_do_node_deploy_rebuild_nostate_state(self):
# test node will not rebuild if state is NOSTATE
node = obj_utils.create_test_node(self.context, driver='fake',
provision_state=states.NOSTATE)
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.do_node_deploy,
self.context, node['uuid'], rebuild=True)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.InstanceDeployFailure, exc.exc_info[0])
# Last_error should be None.
self.assertIsNone(node.last_error)
# Verify reservation has been cleared.
self.assertIsNone(node.reservation)
def test_do_node_deploy_worker_pool_full(self):
prv_state = states.NOSTATE
tgt_prv_state = states.NOSTATE
node = obj_utils.create_test_node(self.context,
provision_state=prv_state,
target_provision_state=tgt_prv_state,
last_error=None, driver='fake')
self._start_service()
with mock.patch.object(self.service, '_spawn_worker') as mock_spawn:
mock_spawn.side_effect = exception.NoFreeConductorWorker()
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.do_node_deploy,
self.context, node.uuid)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.NoFreeConductorWorker, exc.exc_info[0])
self.service._worker_pool.waitall()
node.refresh()
# Make sure things were rolled back
self.assertEqual(prv_state, node.provision_state)
self.assertEqual(tgt_prv_state, node.target_provision_state)
self.assertIsNotNone(node.last_error)
# Verify reservation has been cleared.
self.assertIsNone(node.reservation)
def test_do_node_tear_down_invalid_state(self):
# test node.provision_state is incorrect for tear_down
node = obj_utils.create_test_node(self.context, driver='fake',
provision_state=states.NOSTATE)
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.do_node_tear_down,
self.context, node['uuid'])
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.InstanceDeployFailure, exc.exc_info[0])
@mock.patch('ironic.drivers.modules.fake.FakePower.validate')
def test_do_node_tear_down_validate_fail(self, mock_validate):
# InvalidParameterValue should be re-raised as InstanceDeployFailure
mock_validate.side_effect = exception.InvalidParameterValue('error')
node = obj_utils.create_test_node(self.context, driver='fake',
provision_state=states.ACTIVE)
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.do_node_tear_down,
self.context, node.uuid)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.InstanceDeployFailure, exc.exc_info[0])
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.tear_down')
def test_do_node_tear_down_driver_raises_error(self, mock_tear_down):
# test when driver.deploy.tear_down raises exception
node = obj_utils.create_test_node(self.context, driver='fake',
provision_state=states.ACTIVE,
instance_info={'foo': 'bar'})
task = task_manager.TaskManager(self.context, node.uuid)
self._start_service()
mock_tear_down.side_effect = exception.InstanceDeployFailure('test')
self.assertRaises(exception.InstanceDeployFailure,
self.service._do_node_tear_down,
self.context, task)
node.refresh()
self.assertEqual(states.ERROR, node.provision_state)
self.assertEqual(states.NOSTATE, node.target_provision_state)
self.assertIsNotNone(node.last_error)
# Assert instance_info was erased
self.assertEqual({}, node.instance_info)
mock_tear_down.assert_called_once_with(mock.ANY)
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.tear_down')
def test_do_node_tear_down_ok(self, mock_tear_down):
# test when driver.deploy.tear_down returns DELETED
node = obj_utils.create_test_node(self.context, driver='fake',
provision_state=states.ACTIVE,
instance_info={'foo': 'bar'})
task = task_manager.TaskManager(self.context, node.uuid)
self._start_service()
mock_tear_down.return_value = states.DELETED
self.service._do_node_tear_down(self.context, task)
node.refresh()
self.assertEqual(states.NOSTATE, node.provision_state)
self.assertEqual(states.NOSTATE, node.target_provision_state)
self.assertIsNone(node.last_error)
self.assertEqual({}, node.instance_info)
mock_tear_down.assert_called_once_with(mock.ANY)
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.tear_down')
def test_do_node_tear_down_partial_ok(self, mock_tear_down):
# test when driver.deploy.tear_down doesn't return DELETED
node = obj_utils.create_test_node(self.context, driver='fake',
provision_state=states.ACTIVE,
instance_info={'foo': 'bar'})
self._start_service()
task = task_manager.TaskManager(self.context, node.uuid)
mock_tear_down.return_value = states.DELETING
self.service._do_node_tear_down(self.context, task)
node.refresh()
self.assertEqual(states.DELETING, node.provision_state)
self.assertIsNone(node.last_error)
self.assertEqual({}, node.instance_info)
mock_tear_down.assert_called_once_with(mock.ANY)
@mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker')
def test_do_node_tear_down_worker_pool_full(self, mock_spawn):
prv_state = states.ACTIVE
tgt_prv_state = states.NOSTATE
fake_instance_info = {'foo': 'bar'}
node = obj_utils.create_test_node(self.context, driver='fake',
provision_state=prv_state,
target_provision_state=tgt_prv_state,
instance_info=fake_instance_info,
last_error=None)
self._start_service()
mock_spawn.side_effect = exception.NoFreeConductorWorker()
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.do_node_tear_down,
self.context, node.uuid)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.NoFreeConductorWorker, exc.exc_info[0])
self.service._worker_pool.waitall()
node.refresh()
# Assert instance_info was not touched
self.assertEqual(fake_instance_info, node.instance_info)
# Make sure things were rolled back
self.assertEqual(prv_state, node.provision_state)
self.assertEqual(tgt_prv_state, node.target_provision_state)
self.assertIsNotNone(node.last_error)
# Verify reservation has been cleared.
self.assertIsNone(node.reservation)
@_mock_record_keepalive
class MiscTestCase(_ServiceSetUpMixin, tests_db_base.DbTestCase):
def test_get_driver_known(self):
self._start_service()
driver = self.service._get_driver('fake')
self.assertTrue(isinstance(driver, drivers_base.BaseDriver))
def test_get_driver_unknown(self):
self._start_service()
self.assertRaises(exception.DriverNotFound,
self.service._get_driver, 'unknown_driver')
def test__mapped_to_this_conductor(self):
self._start_service()
n = utils.get_test_node()
self.assertTrue(self.service._mapped_to_this_conductor(n['uuid'],
'fake'))
self.assertFalse(self.service._mapped_to_this_conductor(n['uuid'],
'otherdriver'))
def test_validate_driver_interfaces(self):
node = obj_utils.create_test_node(self.context, driver='fake')
ret = self.service.validate_driver_interfaces(self.context,
node.uuid)
expected = {'console': {'result': True},
'power': {'result': True},
'management': {'result': True},
'deploy': {'result': True}}
self.assertEqual(expected, ret)
def test_validate_driver_interfaces_validation_fail(self):
node = obj_utils.create_test_node(self.context, driver='fake')
with mock.patch('ironic.drivers.modules.fake.FakeDeploy.validate') \
as deploy:
reason = 'fake reason'
deploy.side_effect = exception.InvalidParameterValue(reason)
ret = self.service.validate_driver_interfaces(self.context,
node.uuid)
self.assertFalse(ret['deploy']['result'])
self.assertEqual(reason, ret['deploy']['reason'])
def test_maintenance_mode_on(self):
node = obj_utils.create_test_node(self.context, driver='fake')
self.service.change_node_maintenance_mode(self.context, node.uuid,
True)
node.refresh()
self.assertTrue(node.maintenance)
def test_maintenance_mode_off(self):
node = obj_utils.create_test_node(self.context, driver='fake',
maintenance=True)
self.service.change_node_maintenance_mode(self.context, node.uuid,
False)
node.refresh()
self.assertFalse(node.maintenance)
def test_maintenance_mode_on_failed(self):
node = obj_utils.create_test_node(self.context, driver='fake',
maintenance=True)
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.change_node_maintenance_mode,
self.context, node.uuid, True)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.NodeMaintenanceFailure, exc.exc_info[0])
node.refresh(self.context)
self.assertTrue(node.maintenance)
def test_maintenance_mode_off_failed(self):
node = obj_utils.create_test_node(self.context, driver='fake')
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.change_node_maintenance_mode,
self.context, node.uuid, False)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.NodeMaintenanceFailure, exc.exc_info[0])
node.refresh(self.context)
self.assertFalse(node.maintenance)
@_mock_record_keepalive
class ConsoleTestCase(_ServiceSetUpMixin, tests_db_base.DbTestCase):
def test_set_console_mode_worker_pool_full(self):
node = obj_utils.create_test_node(self.context, driver='fake')
self._start_service()
with mock.patch.object(self.service, '_spawn_worker') \
as spawn_mock:
spawn_mock.side_effect = exception.NoFreeConductorWorker()
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.set_console_mode,
self.context, node.uuid, True)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.NoFreeConductorWorker, exc.exc_info[0])
self.service._worker_pool.waitall()
spawn_mock.assert_called_once_with(mock.ANY, mock.ANY, mock.ANY)
def test_set_console_mode_enabled(self):
node = obj_utils.create_test_node(self.context, driver='fake')
self._start_service()
self.service.set_console_mode(self.context, node.uuid, True)
self.service._worker_pool.waitall()
node.refresh()
self.assertTrue(node.console_enabled)
def test_set_console_mode_disabled(self):
node = obj_utils.create_test_node(self.context, driver='fake')
self._start_service()
self.service.set_console_mode(self.context, node.uuid, False)
self.service._worker_pool.waitall()
node.refresh()
self.assertFalse(node.console_enabled)
def test_set_console_mode_not_supported(self):
node = obj_utils.create_test_node(self.context, driver='fake',
last_error=None)
self._start_service()
# null the console interface
self.driver.console = None
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.set_console_mode, self.context,
node.uuid, True)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.UnsupportedDriverExtension,
exc.exc_info[0])
self.service._worker_pool.waitall()
node.refresh()
def test_set_console_mode_validation_fail(self):
node = obj_utils.create_test_node(self.context, driver='fake',
last_error=None)
self._start_service()
with mock.patch.object(self.driver.console, 'validate') as mock_val:
mock_val.side_effect = exception.InvalidParameterValue('error')
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.set_console_mode,
self.context, node.uuid, True)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.InvalidParameterValue, exc.exc_info[0])
def test_set_console_mode_start_fail(self):
node = obj_utils.create_test_node(self.context, driver='fake',
last_error=None,
console_enabled=False)
self._start_service()
with mock.patch.object(self.driver.console, 'start_console') \
as mock_sc:
mock_sc.side_effect = exception.IronicException('test-error')
self.service.set_console_mode(self.context, node.uuid, True)
self.service._worker_pool.waitall()
mock_sc.assert_called_once_with(mock.ANY)
node.refresh()
self.assertIsNotNone(node.last_error)
def test_set_console_mode_stop_fail(self):
node = obj_utils.create_test_node(self.context, driver='fake',
last_error=None,
console_enabled=True)
self._start_service()
with mock.patch.object(self.driver.console, 'stop_console') \
as mock_sc:
mock_sc.side_effect = exception.IronicException('test-error')
self.service.set_console_mode(self.context, node.uuid, False)
self.service._worker_pool.waitall()
mock_sc.assert_called_once_with(mock.ANY)
node.refresh()
self.assertIsNotNone(node.last_error)
def test_enable_console_already_enabled(self):
node = obj_utils.create_test_node(self.context, driver='fake',
console_enabled=True)
self._start_service()
with mock.patch.object(self.driver.console, 'start_console') \
as mock_sc:
self.service.set_console_mode(self.context, node.uuid, True)
self.service._worker_pool.waitall()
self.assertFalse(mock_sc.called)
def test_disable_console_already_disabled(self):
node = obj_utils.create_test_node(self.context, driver='fake',
console_enabled=False)
self._start_service()
with mock.patch.object(self.driver.console, 'stop_console') \
as mock_sc:
self.service.set_console_mode(self.context, node.uuid, False)
self.service._worker_pool.waitall()
self.assertFalse(mock_sc.called)
def test_get_console(self):
node = obj_utils.create_test_node(self.context, driver='fake',
console_enabled=True)
console_info = {'test': 'test info'}
with mock.patch.object(self.driver.console, 'get_console') as mock_gc:
mock_gc.return_value = console_info
data = self.service.get_console_information(self.context,
node.uuid)
self.assertEqual(console_info, data)
def test_get_console_not_supported(self):
node = obj_utils.create_test_node(self.context, driver='fake',
console_enabled=True)
# null the console interface
self.driver.console = None
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.get_console_information,
self.context, node.uuid)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.UnsupportedDriverExtension,
exc.exc_info[0])
def test_get_console_disabled(self):
node = obj_utils.create_test_node(self.context, driver='fake',
console_enabled=False)
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.get_console_information,
self.context, node.uuid)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.NodeConsoleNotEnabled, exc.exc_info[0])
def test_get_console_validate_fail(self):
node = obj_utils.create_test_node(self.context, driver='fake',
console_enabled=True)
with mock.patch.object(self.driver.console, 'validate') as mock_gc:
mock_gc.side_effect = exception.InvalidParameterValue('error')
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.get_console_information,
self.context, node.uuid)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.InvalidParameterValue, exc.exc_info[0])
@_mock_record_keepalive
class DestroyNodeTestCase(_ServiceSetUpMixin, tests_db_base.DbTestCase):
def test_destroy_node(self):
self._start_service()
node = obj_utils.create_test_node(self.context, driver='fake')
self.service.destroy_node(self.context, node.uuid)
self.assertRaises(exception.NodeNotFound,
self.dbapi.get_node_by_uuid,
node.uuid)
def test_destroy_node_reserved(self):
self._start_service()
fake_reservation = 'fake-reserv'
node = obj_utils.create_test_node(self.context,
reservation=fake_reservation)
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.destroy_node,
self.context, node.uuid)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.NodeLocked, exc.exc_info[0])
# Verify existing reservation wasn't broken.
node.refresh()
self.assertEqual(fake_reservation, node.reservation)
def test_destroy_node_associated(self):
self._start_service()
node = obj_utils.create_test_node(self.context,
instance_uuid='fake-uuid')
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.destroy_node,
self.context, node.uuid)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.NodeAssociated, exc.exc_info[0])
# Verify reservation was released.
node.refresh()
self.assertIsNone(node.reservation)
def test_destroy_node_power_on(self):
self._start_service()
node = obj_utils.create_test_node(self.context,
power_state=states.POWER_ON)
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.destroy_node,
self.context, node.uuid)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.NodeInWrongPowerState, exc.exc_info[0])
# Verify reservation was released.
node.refresh()
self.assertIsNone(node.reservation)
def test_destroy_node_power_off(self):
self._start_service()
node = obj_utils.create_test_node(self.context,
power_state=states.POWER_OFF)
self.service.destroy_node(self.context, node.uuid)
@_mock_record_keepalive
class UpdatePortTestCase(_ServiceSetUpMixin, tests_db_base.DbTestCase):
def test_update_port(self):
obj_utils.create_test_node(self.context, driver='fake')
port = obj_utils.create_test_port(self.context, extra={'foo': 'bar'})
new_extra = {'foo': 'baz'}
port.extra = new_extra
res = self.service.update_port(self.context, port)
self.assertEqual(new_extra, res.extra)
def test_update_port_node_locked(self):
obj_utils.create_test_node(self.context, driver='fake',
reservation='fake-reserv')
port = obj_utils.create_test_port(self.context)
port.extra = {'foo': 'baz'}
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.update_port,
self.context, port)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.NodeLocked, exc.exc_info[0])
@mock.patch('ironic.dhcp.neutron.NeutronDHCPApi.update_port_address')
def test_update_port_address(self, mac_update_mock):
obj_utils.create_test_node(self.context, driver='fake')
port = obj_utils.create_test_port(self.context,
extra={'vif_port_id': 'fake-id'})
new_address = '11:22:33:44:55:bb'
port.address = new_address
res = self.service.update_port(self.context, port)
self.assertEqual(new_address, res.address)
mac_update_mock.assert_called_once_with('fake-id', new_address)
@mock.patch('ironic.dhcp.neutron.NeutronDHCPApi.update_port_address')
def test_update_port_address_fail(self, mac_update_mock):
obj_utils.create_test_node(self.context, driver='fake')
port = obj_utils.create_test_port(self.context,
extra={'vif_port_id': 'fake-id'})
old_address = port.address
port.address = '11:22:33:44:55:bb'
mac_update_mock.side_effect = exception.FailedToUpdateMacOnPort(
port_id=port.uuid)
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.update_port,
self.context, port)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.FailedToUpdateMacOnPort, exc.exc_info[0])
port.refresh(self.context)
self.assertEqual(old_address, port.address)
@mock.patch('ironic.dhcp.neutron.NeutronDHCPApi.update_port_address')
def test_update_port_address_no_vif_id(self, mac_update_mock):
obj_utils.create_test_node(self.context, driver='fake')
port = obj_utils.create_test_port(self.context)
new_address = '11:22:33:44:55:bb'
port.address = new_address
res = self.service.update_port(self.context, port)
self.assertEqual(new_address, res.address)
self.assertFalse(mac_update_mock.called)
def test__filter_out_unsupported_types_all(self):
self._start_service()
CONF.set_override('send_sensor_data_types', ['All'], group='conductor')
fake_sensors_data = {"t1": {'f1': 'v1'}, "t2": {'f1': 'v1'}}
actual_result = self.service._filter_out_unsupported_types(
fake_sensors_data)
expected_result = {"t1": {'f1': 'v1'}, "t2": {'f1': 'v1'}}
self.assertEqual(expected_result, actual_result)
def test__filter_out_unsupported_types_part(self):
self._start_service()
CONF.set_override('send_sensor_data_types', ['t1'], group='conductor')
fake_sensors_data = {"t1": {'f1': 'v1'}, "t2": {'f1': 'v1'}}
actual_result = self.service._filter_out_unsupported_types(
fake_sensors_data)
expected_result = {"t1": {'f1': 'v1'}}
self.assertEqual(expected_result, actual_result)
def test__filter_out_unsupported_types_non(self):
self._start_service()
CONF.set_override('send_sensor_data_types', ['t3'], group='conductor')
fake_sensors_data = {"t1": {'f1': 'v1'}, "t2": {'f1': 'v1'}}
actual_result = self.service._filter_out_unsupported_types(
fake_sensors_data)
expected_result = {}
self.assertEqual(expected_result, actual_result)
@mock.patch.object(manager.ConductorManager, '_mapped_to_this_conductor')
@mock.patch.object(dbapi.IMPL, 'get_nodeinfo_list')
@mock.patch.object(task_manager, 'acquire')
def test___send_sensor_data(self, acquire_mock, get_nodeinfo_list_mock,
_mapped_to_this_conductor_mock):
node = obj_utils.create_test_node(self.context,
driver='fake')
self._start_service()
CONF.set_override('send_sensor_data', True, group='conductor')
acquire_mock.return_value.__enter__.return_value.driver = self.driver
with mock.patch.object(self.driver.management,
'get_sensors_data') as get_sensors_data_mock:
get_sensors_data_mock.return_value = 'fake-sensor-data'
_mapped_to_this_conductor_mock.return_value = True
get_nodeinfo_list_mock.return_value = [(node.uuid, node.driver,
node.instance_uuid)]
self.service._send_sensor_data(self.context)
self.assertTrue(get_nodeinfo_list_mock.called)
self.assertTrue(_mapped_to_this_conductor_mock.called)
self.assertTrue(acquire_mock.called)
self.assertTrue(get_sensors_data_mock.called)
@mock.patch.object(manager.ConductorManager, '_mapped_to_this_conductor')
@mock.patch.object(dbapi.IMPL, 'get_nodeinfo_list')
@mock.patch.object(task_manager, 'acquire')
def test___send_sensor_data_disabled(self, acquire_mock,
get_nodeinfo_list_mock, _mapped_to_this_conductor_mock):
node = obj_utils.create_test_node(self.context,
driver='fake')
self._start_service()
acquire_mock.return_value.__enter__.return_value.driver = self.driver
with mock.patch.object(self.driver.management,
'get_sensors_data') as get_sensors_data_mock:
get_sensors_data_mock.return_value = 'fake-sensor-data'
_mapped_to_this_conductor_mock.return_value = True
get_nodeinfo_list_mock.return_value = [(node.uuid, node.driver,
node.instance_uuid)]
self.service._send_sensor_data(self.context)
self.assertFalse(get_nodeinfo_list_mock.called)
self.assertFalse(_mapped_to_this_conductor_mock.called)
self.assertFalse(acquire_mock.called)
self.assertFalse(get_sensors_data_mock.called)
def test_set_boot_device(self):
node = obj_utils.create_test_node(self.context, driver='fake')
with mock.patch.object(self.driver.management, 'validate') as mock_val:
with mock.patch.object(self.driver.management, 'set_boot_device') \
as mock_sbd:
self.service.set_boot_device(self.context, node.uuid,
boot_devices.PXE)
mock_val.assert_called_once_with(mock.ANY)
mock_sbd.assert_called_once_with(mock.ANY, boot_devices.PXE,
persistent=False)
def test_set_boot_device_node_locked(self):
node = obj_utils.create_test_node(self.context, driver='fake',
reservation='fake-reserv')
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.set_boot_device,
self.context, node.uuid, boot_devices.DISK)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.NodeLocked, exc.exc_info[0])
def test_set_boot_device_not_supported(self):
node = obj_utils.create_test_node(self.context, driver='fake')
# null the console interface
self.driver.management = None
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.set_boot_device,
self.context, node.uuid, boot_devices.DISK)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.UnsupportedDriverExtension,
exc.exc_info[0])
def test_set_boot_device_validate_fail(self):
node = obj_utils.create_test_node(self.context, driver='fake')
with mock.patch.object(self.driver.management, 'validate') as mock_val:
mock_val.side_effect = exception.InvalidParameterValue('error')
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.set_boot_device,
self.context, node.uuid, boot_devices.DISK)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.InvalidParameterValue, exc.exc_info[0])
def test_get_boot_device(self):
node = obj_utils.create_test_node(self.context, driver='fake')
bootdev = self.service.get_boot_device(self.context, node.uuid)
expected = {'boot_device': boot_devices.PXE, 'persistent': False}
self.assertEqual(expected, bootdev)
def test_get_boot_device_node_locked(self):
node = obj_utils.create_test_node(self.context, driver='fake',
reservation='fake-reserv')
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.get_boot_device,
self.context, node.uuid)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.NodeLocked, exc.exc_info[0])
def test_get_boot_device_not_supported(self):
node = obj_utils.create_test_node(self.context, driver='fake')
# null the management interface
self.driver.management = None
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.get_boot_device,
self.context, node.uuid)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.UnsupportedDriverExtension,
exc.exc_info[0])
def test_get_boot_device_validate_fail(self):
node = obj_utils.create_test_node(self.context, driver='fake')
with mock.patch.object(self.driver.management, 'validate') as mock_val:
mock_val.side_effect = exception.InvalidParameterValue('error')
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.get_boot_device,
self.context, node.uuid)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.InvalidParameterValue, exc.exc_info[0])
def test_get_supported_boot_devices(self):
node = obj_utils.create_test_node(self.context, driver='fake')
bootdevs = self.service.get_supported_boot_devices(self.context,
node.uuid)
self.assertEqual([boot_devices.PXE], bootdevs)
def test_get_supported_boot_devices_iface_not_supported(self):
node = obj_utils.create_test_node(self.context, driver='fake')
# null the management interface
self.driver.management = None
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.get_supported_boot_devices,
self.context, node.uuid)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.UnsupportedDriverExtension,
exc.exc_info[0])
class ManagerSpawnWorkerTestCase(tests_base.TestCase):
def setUp(self):
super(ManagerSpawnWorkerTestCase, self).setUp()
self.service = manager.ConductorManager('hostname', 'test-topic')
def test__spawn_worker(self):
worker_pool = mock.Mock(spec_set=['free', 'spawn'])
worker_pool.free.return_value = True
self.service._worker_pool = worker_pool
self.service._spawn_worker('fake', 1, 2, foo='bar', cat='meow')
worker_pool.spawn.assert_called_once_with(
'fake', 1, 2, foo='bar', cat='meow')
def test__spawn_worker_none_free(self):
worker_pool = mock.Mock(spec_set=['free', 'spawn'])
worker_pool.free.return_value = False
self.service._worker_pool = worker_pool
self.assertRaises(exception.NoFreeConductorWorker,
self.service._spawn_worker, 'fake')
self.assertFalse(worker_pool.spawn.called)
@mock.patch.object(conductor_utils, 'node_power_action')
class ManagerDoSyncPowerStateTestCase(tests_base.TestCase):
def setUp(self):
super(ManagerDoSyncPowerStateTestCase, self).setUp()
self.service = manager.ConductorManager('hostname', 'test-topic')
self.context = context.get_admin_context()
self.driver = mock.Mock(spec_set=drivers_base.BaseDriver)
self.power = self.driver.power
self.node = mock.Mock(spec_set=objects.Node)
self.task = mock.Mock(spec_set=['context', 'driver', 'node'])
self.task.context = self.context
self.task.driver = self.driver
self.task.node = self.node
self.config(force_power_state_during_sync=False, group='conductor')
def _do_sync_power_state(self, old_power_state, new_power_states,
fail_validate=False, fail_change=False):
if not isinstance(new_power_states, (list, tuple)):
new_power_states = [new_power_states]
if fail_validate:
exc = exception.InvalidParameterValue('error')
self.power.validate.side_effect = exc
if fail_change:
exc = exception.IronicException('test')
self.power.node_power_action.side_effect = exc
for new_power_state in new_power_states:
self.node.power_state = old_power_state
if isinstance(new_power_state, Exception):
self.power.get_power_state.side_effect = new_power_state
else:
self.power.get_power_state.return_value = new_power_state
self.service._do_sync_power_state(self.task)
def test_state_unchanged(self, node_power_action):
self._do_sync_power_state('fake-power', 'fake-power')
self.assertFalse(self.power.validate.called)
self.power.get_power_state.assert_called_once_with(self.task)
self.assertEqual('fake-power', self.node.power_state)
self.assertFalse(self.node.save.called)
self.assertFalse(node_power_action.called)
def test_state_not_set(self, node_power_action):
self._do_sync_power_state(None, states.POWER_ON)
self.power.validate.assert_called_once_with(self.task)
self.power.get_power_state.assert_called_once_with(self.task)
self.node.save.assert_called_once_with(self.context)
self.assertFalse(node_power_action.called)
self.assertEqual(states.POWER_ON, self.node.power_state)
def test_validate_fail(self, node_power_action):
self._do_sync_power_state(None, states.POWER_ON,
fail_validate=True)
self.power.validate.assert_called_once_with(self.task)
self.assertFalse(self.power.get_power_state.called)
self.assertFalse(self.node.save.called)
self.assertFalse(node_power_action.called)
self.assertEqual(None, self.node.power_state)
def test_get_power_state_fail(self, node_power_action):
self._do_sync_power_state('fake',
exception.IronicException('foo'))
self.assertFalse(self.power.validate.called)
self.power.get_power_state.assert_called_once_with(self.task)
self.assertFalse(self.node.save.called)
self.assertFalse(node_power_action.called)
self.assertEqual('fake', self.node.power_state)
self.assertEqual(1,
self.service.power_state_sync_count[self.node.uuid])
def test_get_power_state_error(self, node_power_action):
self._do_sync_power_state('fake', states.ERROR)
self.assertFalse(self.power.validate.called)
self.power.get_power_state.assert_called_once_with(self.task)
self.assertFalse(self.node.save.called)
self.assertFalse(node_power_action.called)
self.assertEqual('fake', self.node.power_state)
self.assertEqual(1,
self.service.power_state_sync_count[self.node.uuid])
def test_state_changed_no_sync(self, node_power_action):
self._do_sync_power_state(states.POWER_ON, states.POWER_OFF)
self.assertFalse(self.power.validate.called)
self.power.get_power_state.assert_called_once_with(self.task)
self.node.save.assert_called_once_with(self.context)
self.assertFalse(node_power_action.called)
self.assertEqual(states.POWER_OFF, self.node.power_state)
def test_state_changed_sync(self, node_power_action):
self.config(force_power_state_during_sync=True, group='conductor')
self.config(power_state_sync_max_retries=1, group='conductor')
self._do_sync_power_state(states.POWER_ON, states.POWER_OFF)
self.assertFalse(self.power.validate.called)
self.power.get_power_state.assert_called_once_with(self.task)
self.assertFalse(self.node.save.called)
node_power_action.assert_called_once_with(self.task, states.POWER_ON)
self.assertEqual(states.POWER_ON, self.node.power_state)
def test_state_changed_sync_failed(self, node_power_action):
self.config(force_power_state_during_sync=True, group='conductor')
self._do_sync_power_state(states.POWER_ON, states.POWER_OFF,
fail_change=True)
# Just testing that this test doesn't raise.
self.assertFalse(self.power.validate.called)
self.power.get_power_state.assert_called_once_with(self.task)
self.assertFalse(self.node.save.called)
node_power_action.assert_called_once_with(self.task, states.POWER_ON)
self.assertEqual(states.POWER_ON, self.node.power_state)
self.assertEqual(1,
self.service.power_state_sync_count[self.node.uuid])
def test_max_retries_exceeded(self, node_power_action):
self.config(force_power_state_during_sync=True, group='conductor')
self.config(power_state_sync_max_retries=1, group='conductor')
self._do_sync_power_state(states.POWER_ON, [states.POWER_OFF,
states.POWER_OFF])
self.assertFalse(self.power.validate.called)
power_exp_calls = [mock.call(self.task)] * 2
self.assertEqual(power_exp_calls,
self.power.get_power_state.call_args_list)
self.node.save.assert_called_once_with(self.context)
node_power_action.assert_called_once_with(self.task, states.POWER_ON)
self.assertEqual(states.POWER_OFF, self.node.power_state)
self.assertEqual(1,
self.service.power_state_sync_count[self.node.uuid])
self.assertTrue(self.node.maintenance)
def test_max_retries_exceeded2(self, node_power_action):
self.config(force_power_state_during_sync=True, group='conductor')
self.config(power_state_sync_max_retries=2, group='conductor')
self._do_sync_power_state(states.POWER_ON, [states.POWER_OFF,
states.POWER_OFF,
states.POWER_OFF])
self.assertFalse(self.power.validate.called)
power_exp_calls = [mock.call(self.task)] * 3
self.assertEqual(power_exp_calls,
self.power.get_power_state.call_args_list)
self.node.save.assert_called_once_with(self.context)
npa_exp_calls = [mock.call(self.task, states.POWER_ON)] * 2
self.assertEqual(npa_exp_calls, node_power_action.call_args_list)
self.assertEqual(states.POWER_OFF, self.node.power_state)
self.assertEqual(2,
self.service.power_state_sync_count[self.node.uuid])
self.assertTrue(self.node.maintenance)
def test_retry_then_success(self, node_power_action):
self.config(force_power_state_during_sync=True, group='conductor')
self.config(power_state_sync_max_retries=2, group='conductor')
self._do_sync_power_state(states.POWER_ON, [states.POWER_OFF,
states.POWER_OFF,
states.POWER_ON])
self.assertFalse(self.power.validate.called)
power_exp_calls = [mock.call(self.task)] * 3
self.assertEqual(power_exp_calls,
self.power.get_power_state.call_args_list)
self.assertFalse(self.node.save.called)
npa_exp_calls = [mock.call(self.task, states.POWER_ON)] * 2
self.assertEqual(npa_exp_calls, node_power_action.call_args_list)
self.assertEqual(states.POWER_ON, self.node.power_state)
self.assertNotIn(self.node.uuid, self.service.power_state_sync_count)
def test_power_state_sync_max_retries_gps_exception(self,
node_power_action):
self.config(power_state_sync_max_retries=2, group='conductor')
self.service.power_state_sync_count[self.node.uuid] = 2
self._do_sync_power_state('fake',
exception.IronicException('foo'),
fail_change=True)
self.assertFalse(self.power.validate.called)
self.power.get_power_state.assert_called_once_with(self.task)
self.assertEqual(None, self.node.power_state)
self.assertTrue(self.node.maintenance)
self.assertTrue(self.node.save.called)
self.assertFalse(node_power_action.called)
@mock.patch.object(manager.ConductorManager, '_do_sync_power_state')
@mock.patch.object(task_manager, 'acquire')
@mock.patch.object(manager.ConductorManager, '_mapped_to_this_conductor')
@mock.patch.object(objects.Node, 'get_by_id')
@mock.patch.object(dbapi.IMPL, 'get_nodeinfo_list')
class ManagerSyncPowerStatesTestCase(_CommonMixIn, tests_base.TestCase):
def setUp(self):
super(ManagerSyncPowerStatesTestCase, self).setUp()
self.service = manager.ConductorManager('hostname', 'test-topic')
self.dbapi = dbapi.get_instance()
self.service.dbapi = self.dbapi
self.context = context.get_admin_context()
self.node = self._create_node()
self.filters = {'reserved': False, 'maintenance': False}
self.columns = ['id', 'uuid', 'driver']
def test_node_not_mapped(self, get_nodeinfo_mock, get_node_mock,
mapped_mock, acquire_mock, sync_mock):
get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response()
get_node_mock.return_value = self.node
mapped_mock.return_value = False
self.service._sync_power_states(self.context)
get_nodeinfo_mock.assert_called_once_with(
columns=self.columns, filters=self.filters)
mapped_mock.assert_called_once_with(self.node.uuid,
self.node.driver)
self.assertFalse(get_node_mock.called)
self.assertFalse(acquire_mock.called)
self.assertFalse(sync_mock.called)
def test_node_disappeared(self, get_nodeinfo_mock, get_node_mock,
mapped_mock, acquire_mock, sync_mock):
get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response()
get_node_mock.return_value = self.node
mapped_mock.return_value = True
get_node_mock.side_effect = exception.NodeNotFound(node=self.node.uuid)
self.service._sync_power_states(self.context)
get_nodeinfo_mock.assert_called_once_with(
columns=self.columns, filters=self.filters)
mapped_mock.assert_called_once_with(self.node.uuid,
self.node.driver)
get_node_mock.assert_called_once_with(self.context, self.node.id)
self.assertFalse(acquire_mock.called)
self.assertFalse(sync_mock.called)
def test_node_in_deploywait(self, get_nodeinfo_mock, get_node_mock,
mapped_mock, acquire_mock, sync_mock):
get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response()
get_node_mock.return_value = self.node
self.node.provision_state = states.DEPLOYWAIT
self.service._sync_power_states(self.context)
get_nodeinfo_mock.assert_called_once_with(
columns=self.columns, filters=self.filters)
mapped_mock.assert_called_once_with(self.node.uuid,
self.node.driver)
get_node_mock.assert_called_once_with(self.context, self.node.id)
self.assertFalse(acquire_mock.called)
self.assertFalse(sync_mock.called)
def test_node_in_maintenance(self, get_nodeinfo_mock, get_node_mock,
mapped_mock, acquire_mock, sync_mock):
get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response()
get_node_mock.return_value = self.node
self.node.maintenance = True
self.service._sync_power_states(self.context)
get_nodeinfo_mock.assert_called_once_with(
columns=self.columns, filters=self.filters)
mapped_mock.assert_called_once_with(self.node.uuid,
self.node.driver)
get_node_mock.assert_called_once_with(self.context, self.node.id)
self.assertFalse(acquire_mock.called)
self.assertFalse(sync_mock.called)
def test_node_has_reservation(self, get_nodeinfo_mock, get_node_mock,
mapped_mock, acquire_mock, sync_mock):
get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response()
get_node_mock.return_value = self.node
self.node.reservation = 'fake'
self.service._sync_power_states(self.context)
get_nodeinfo_mock.assert_called_once_with(
columns=self.columns, filters=self.filters)
mapped_mock.assert_called_once_with(self.node.uuid,
self.node.driver)
get_node_mock.assert_called_once_with(self.context, self.node.id)
self.assertFalse(acquire_mock.called)
self.assertFalse(sync_mock.called)
def test_node_locked_on_acquire(self, get_nodeinfo_mock, get_node_mock,
mapped_mock, acquire_mock, sync_mock):
get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response()
get_node_mock.return_value = self.node
mapped_mock.return_value = True
acquire_mock.side_effect = exception.NodeLocked(node=self.node.uuid,
host='fake')
self.service._sync_power_states(self.context)
get_nodeinfo_mock.assert_called_once_with(
columns=self.columns, filters=self.filters)
mapped_mock.assert_called_once_with(self.node.uuid,
self.node.driver)
get_node_mock.assert_called_once_with(self.context, self.node.id)
acquire_mock.assert_called_once_with(self.context, self.node.id)
self.assertFalse(sync_mock.called)
def test_node_in_deploywait_on_acquire(self, get_nodeinfo_mock,
get_node_mock, mapped_mock,
acquire_mock, sync_mock):
get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response()
get_node_mock.return_value = self.node
mapped_mock.return_value = True
task = self._create_task(
node_attrs=dict(provision_state=states.DEPLOYWAIT,
id=self.node.id))
acquire_mock.side_effect = self._get_acquire_side_effect(task)
self.service._sync_power_states(self.context)
get_nodeinfo_mock.assert_called_once_with(
columns=self.columns, filters=self.filters)
mapped_mock.assert_called_once_with(self.node.uuid,
self.node.driver)
get_node_mock.assert_called_once_with(self.context, self.node.id)
acquire_mock.assert_called_once_with(self.context, self.node.id)
self.assertFalse(sync_mock.called)
def test_node_in_maintenance_on_acquire(self, get_nodeinfo_mock,
get_node_mock, mapped_mock,
acquire_mock, sync_mock):
get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response()
get_node_mock.return_value = self.node
mapped_mock.return_value = True
task = self._create_task(
node_attrs=dict(maintenance=True, id=self.node.id))
acquire_mock.side_effect = self._get_acquire_side_effect(task)
self.service._sync_power_states(self.context)
get_nodeinfo_mock.assert_called_once_with(
columns=self.columns, filters=self.filters)
mapped_mock.assert_called_once_with(self.node.uuid,
self.node.driver)
get_node_mock.assert_called_once_with(self.context, self.node.id)
acquire_mock.assert_called_once_with(self.context, self.node.id)
self.assertFalse(sync_mock.called)
def test_node_disappears_on_acquire(self, get_nodeinfo_mock,
get_node_mock, mapped_mock,
acquire_mock, sync_mock):
get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response()
get_node_mock.return_value = self.node
mapped_mock.return_value = True
acquire_mock.side_effect = exception.NodeNotFound(node=self.node.uuid,
host='fake')
self.service._sync_power_states(self.context)
get_nodeinfo_mock.assert_called_once_with(
columns=self.columns, filters=self.filters)
mapped_mock.assert_called_once_with(self.node.uuid,
self.node.driver)
get_node_mock.assert_called_once_with(self.context, self.node.id)
acquire_mock.assert_called_once_with(self.context, self.node.id)
self.assertFalse(sync_mock.called)
def test_single_node(self, get_nodeinfo_mock, get_node_mock,
mapped_mock, acquire_mock, sync_mock):
get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response()
get_node_mock.return_value = self.node
mapped_mock.return_value = True
task = self._create_task(node_attrs=dict(id=self.node.id))
acquire_mock.side_effect = self._get_acquire_side_effect(task)
self.service._sync_power_states(self.context)
get_nodeinfo_mock.assert_called_once_with(
columns=self.columns, filters=self.filters)
mapped_mock.assert_called_once_with(self.node.uuid,
self.node.driver)
get_node_mock.assert_called_once_with(self.context, self.node.id)
acquire_mock.assert_called_once_with(self.context, self.node.id)
sync_mock.assert_called_once_with(task)
def test__sync_power_state_multiple_nodes(self, get_nodeinfo_mock,
get_node_mock, mapped_mock,
acquire_mock, sync_mock):
# Create 11 nodes:
# 1st node: Should acquire and try to sync
# 2nd node: Not mapped to this conductor
# 3rd node: In DEPLOYWAIT provision_state
# 4th node: In maintenance mode
# 5th node: Has a reservation
# 6th node: Disappears after getting nodeinfo list.
# 7th node: task_manger.acquire() fails due to lock
# 8th node: task_manger.acquire() fails due to node disappearing
# 9th node: In DEPLOYWAIT provision_state acquire()
# 10th node: In maintenance mode on acquire()
# 11th node: Should acquire and try to sync
nodes = []
get_node_map = {}
mapped_map = {}
for i in range(1, 12):
attrs = {'id': i,
'uuid': ironic_utils.generate_uuid()}
if i == 3:
attrs['provision_state'] = states.DEPLOYWAIT
elif i == 4:
attrs['maintenance'] = True
elif i == 5:
attrs['reservation'] = 'fake'
n = self._create_node(**attrs)
nodes.append(n)
mapped_map[n.uuid] = False if i == 2 else True
get_node_map[n.uuid] = n
tasks = [self._create_task(node_attrs=dict(id=1)),
exception.NodeLocked(node=7, host='fake'),
exception.NodeNotFound(node=8, host='fake'),
self._create_task(
node_attrs=dict(id=9,
provision_state=states.DEPLOYWAIT)),
self._create_task(
node_attrs=dict(id=10, maintenance=True)),
self._create_task(node_attrs=dict(id=11))]
def _get_node_side_effect(ctxt, node_id):
if node_id == 6:
# Make this node disappear
raise exception.NodeNotFound(node=node_id)
return nodes[node_id - 1]
get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response(
nodes)
mapped_mock.side_effect = lambda x, y: mapped_map[x]
get_node_mock.side_effect = _get_node_side_effect
acquire_mock.side_effect = self._get_acquire_side_effect(tasks)
with mock.patch.object(eventlet, 'sleep') as sleep_mock:
self.service._sync_power_states(self.context)
# Ensure we've yielded on every iteration
self.assertEqual(len(nodes), sleep_mock.call_count)
get_nodeinfo_mock.assert_called_once_with(
columns=self.columns, filters=self.filters)
mapped_calls = [mock.call(n.uuid, n.driver) for n in nodes]
self.assertEqual(mapped_calls, mapped_mock.call_args_list)
get_node_calls = [mock.call(self.context, n.id)
for n in nodes[:1] + nodes[2:]]
self.assertEqual(get_node_calls,
get_node_mock.call_args_list)
acquire_calls = [mock.call(self.context, n.id)
for n in nodes[:1] + nodes[6:]]
self.assertEqual(acquire_calls, acquire_mock.call_args_list)
sync_calls = [mock.call(tasks[0]), mock.call(tasks[5])]
self.assertEqual(sync_calls, sync_mock.call_args_list)
@mock.patch.object(task_manager, 'acquire')
@mock.patch.object(manager.ConductorManager, '_mapped_to_this_conductor')
@mock.patch.object(dbapi.IMPL, 'get_nodeinfo_list')
class ManagerCheckDeployTimeoutsTestCase(_CommonMixIn, tests_base.TestCase):
def setUp(self):
super(ManagerCheckDeployTimeoutsTestCase, self).setUp()
self.config(deploy_callback_timeout=300, group='conductor')
self.context = context.get_admin_context()
self.service = manager.ConductorManager('hostname', 'test-topic')
self.dbapi = dbapi.get_instance()
self.service.dbapi = self.dbapi
self.node = self._create_node(provision_state=states.DEPLOYWAIT)
self.task = self._create_task(node=self.node)
self.node2 = self._create_node(provision_state=states.DEPLOYWAIT)
self.task2 = self._create_task(node=self.node2)
self.filters = {'reserved': False, 'maintenance': False,
'provisioned_before': 300,
'provision_state': states.DEPLOYWAIT}
self.columns = ['uuid', 'driver']
def _assert_get_nodeinfo_args(self, get_nodeinfo_mock):
get_nodeinfo_mock.assert_called_once_with(
columns=self.columns, filters=self.filters,
sort_key='provision_updated_at', sort_dir='asc')
def test_disabled(self, get_nodeinfo_mock, mapped_mock,
acquire_mock):
self.config(deploy_callback_timeout=0, group='conductor')
self.service._check_deploy_timeouts(self.context)
self.assertFalse(get_nodeinfo_mock.called)
self.assertFalse(mapped_mock.called)
self.assertFalse(acquire_mock.called)
def test_not_mapped(self, get_nodeinfo_mock, mapped_mock, acquire_mock):
get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response()
mapped_mock.return_value = False
self.service._check_deploy_timeouts(self.context)
self._assert_get_nodeinfo_args(get_nodeinfo_mock)
mapped_mock.assert_called_once_with(self.node.uuid, self.node.driver)
self.assertFalse(acquire_mock.called)
def test_timeout(self, get_nodeinfo_mock, mapped_mock, acquire_mock):
get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response()
mapped_mock.return_value = True
acquire_mock.side_effect = self._get_acquire_side_effect(self.task)
self.service._check_deploy_timeouts(self.context)
self._assert_get_nodeinfo_args(get_nodeinfo_mock)
mapped_mock.assert_called_once_with(self.node.uuid, self.node.driver)
acquire_mock.assert_called_once_with(self.context, self.node.uuid)
self.task.spawn_after.assert_called_with(
self.service._spawn_worker,
conductor_utils.cleanup_after_timeout, self.task)
def test_acquire_node_disappears(self, get_nodeinfo_mock, mapped_mock,
acquire_mock):
get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response()
mapped_mock.return_value = True
acquire_mock.side_effect = exception.NodeNotFound(node='fake')
# Exception eaten
self.service._check_deploy_timeouts(self.context)
self._assert_get_nodeinfo_args(get_nodeinfo_mock)
mapped_mock.assert_called_once_with(
self.node.uuid, self.node.driver)
acquire_mock.assert_called_once_with(self.context,
self.node.uuid)
self.assertFalse(self.task.spawn_after.called)
def test_acquire_node_locked(self, get_nodeinfo_mock, mapped_mock,
acquire_mock):
get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response()
mapped_mock.return_value = True
acquire_mock.side_effect = exception.NodeLocked(node='fake',
host='fake')
# Exception eaten
self.service._check_deploy_timeouts(self.context)
self._assert_get_nodeinfo_args(get_nodeinfo_mock)
mapped_mock.assert_called_once_with(
self.node.uuid, self.node.driver)
acquire_mock.assert_called_once_with(self.context,
self.node.uuid)
self.assertFalse(self.task.spawn_after.called)
def test_no_deploywait_after_lock(self, get_nodeinfo_mock, mapped_mock,
acquire_mock):
task = self._create_task(
node_attrs=dict(provision_state=states.NOSTATE,
uuid=self.node.uuid))
get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response()
mapped_mock.return_value = True
acquire_mock.side_effect = self._get_acquire_side_effect(task)
self.service._check_deploy_timeouts(self.context)
self._assert_get_nodeinfo_args(get_nodeinfo_mock)
mapped_mock.assert_called_once_with(
self.node.uuid, self.node.driver)
acquire_mock.assert_called_once_with(self.context,
self.node.uuid)
self.assertFalse(task.spawn_after.called)
def test_maintenance_after_lock(self, get_nodeinfo_mock, mapped_mock,
acquire_mock):
task = self._create_task(
node_attrs=dict(provision_state=states.DEPLOYWAIT,
maintenance=True,
uuid=self.node.uuid))
get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response(
[task.node, self.node2])
mapped_mock.return_value = True
acquire_mock.side_effect = self._get_acquire_side_effect(
[task, self.task2])
self.service._check_deploy_timeouts(self.context)
self._assert_get_nodeinfo_args(get_nodeinfo_mock)
self.assertEqual([mock.call(self.node.uuid, task.node.driver),
mock.call(self.node2.uuid, self.node2.driver)],
mapped_mock.call_args_list)
self.assertEqual([mock.call(self.context, self.node.uuid),
mock.call(self.context, self.node2.uuid)],
acquire_mock.call_args_list)
# First node skipped
self.assertFalse(task.spawn_after.called)
# Second node spawned
self.task2.spawn_after.assert_called_with(
self.service._spawn_worker,
conductor_utils.cleanup_after_timeout, self.task2)
def test_exiting_no_worker_avail(self, get_nodeinfo_mock, mapped_mock,
acquire_mock):
get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response(
[self.node, self.node2])
mapped_mock.return_value = True
acquire_mock.side_effect = self._get_acquire_side_effect(
[(self.task, exception.NoFreeConductorWorker()), self.task2])
# Exception should be nuked
self.service._check_deploy_timeouts(self.context)
self._assert_get_nodeinfo_args(get_nodeinfo_mock)
# mapped should be only called for the first node as we should
# have exited the loop early due to NoFreeConductorWorker
mapped_mock.assert_called_once_with(
self.node.uuid, self.node.driver)
acquire_mock.assert_called_once_with(self.context,
self.node.uuid)
self.task.spawn_after.assert_called_with(
self.service._spawn_worker,
conductor_utils.cleanup_after_timeout, self.task)
def test_exiting_with_other_exception(self, get_nodeinfo_mock,
mapped_mock, acquire_mock):
get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response(
[self.node, self.node2])
mapped_mock.return_value = True
acquire_mock.side_effect = self._get_acquire_side_effect(
[(self.task, exception.IronicException('foo')), self.task2])
# Should re-raise
self.assertRaises(exception.IronicException,
self.service._check_deploy_timeouts,
self.context)
self._assert_get_nodeinfo_args(get_nodeinfo_mock)
# mapped should be only called for the first node as we should
# have exited the loop early due to unknown exception
mapped_mock.assert_called_once_with(
self.node.uuid, self.node.driver)
acquire_mock.assert_called_once_with(self.context,
self.node.uuid)
self.task.spawn_after.assert_called_with(
self.service._spawn_worker,
conductor_utils.cleanup_after_timeout, self.task)
def test_worker_limit(self, get_nodeinfo_mock, mapped_mock, acquire_mock):
self.config(periodic_max_workers=2, group='conductor')
# Use the same nodes/tasks to make life easier in the tests
# here
get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response(
[self.node] * 3)
mapped_mock.return_value = True
acquire_mock.side_effect = self._get_acquire_side_effect(
[self.task] * 3)
self.service._check_deploy_timeouts(self.context)
# Should only have ran 2.
self.assertEqual([mock.call(self.node.uuid, self.node.driver)] * 2,
mapped_mock.call_args_list)
self.assertEqual([mock.call(self.context, self.node.uuid)] * 2,
acquire_mock.call_args_list)
spawn_after_call = mock.call(self.service._spawn_worker,
conductor_utils.cleanup_after_timeout,
self.task)
self.assertEqual([spawn_after_call] * 2,
self.task.spawn_after.call_args_list)
@mock.patch.object(dbapi.IMPL, 'update_port')
@mock.patch('ironic.dhcp.neutron.NeutronDHCPApi.update_port_address')
def test_update_port_duplicate_mac(self, get_nodeinfo_mock, mapped_mock,
acquire_mock, mac_update_mock, mock_up):
ndict = utils.get_test_node(driver='fake')
self.dbapi.create_node(ndict)
port = obj_utils.create_test_port(self.context)
mock_up.side_effect = exception.MACAlreadyExists(mac=port.address)
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.update_port,
self.context, port)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.MACAlreadyExists, exc.exc_info[0])
# ensure Neutron wasn't updated
self.assertFalse(mac_update_mock.called)
class ManagerTestProperties(tests_db_base.DbTestCase):
def setUp(self):
super(ManagerTestProperties, self).setUp()
self.service = manager.ConductorManager('test-host', 'test-topic')
self.context = context.get_admin_context()
def _check_driver_properties(self, driver, expected):
mgr_utils.mock_the_extension_manager(driver=driver)
self.driver = driver_factory.get_driver(driver)
self.service.init_host()
properties = self.service.get_driver_properties(self.context, driver)
self.assertEqual(sorted(expected), sorted(properties.keys()))
def test_driver_properties_fake(self):
expected = ['A1', 'A2', 'B1', 'B2']
self._check_driver_properties("fake", expected)
def test_driver_properties_fake_ipmitool(self):
expected = ['ipmi_address', 'ipmi_terminal_port',
'ipmi_password', 'ipmi_priv_level',
'ipmi_username', 'ipmi_bridging',
'ipmi_transit_channel', 'ipmi_transit_address',
'ipmi_target_channel', 'ipmi_target_address',
'ipmi_local_address'
]
self._check_driver_properties("fake_ipmitool", expected)
def test_driver_properties_fake_ipminative(self):
expected = ['ipmi_address', 'ipmi_password', 'ipmi_username']
self._check_driver_properties("fake_ipminative", expected)
def test_driver_properties_fake_ssh(self):
expected = ['ssh_address', 'ssh_username', 'ssh_virt_type',
'ssh_key_contents', 'ssh_key_filename',
'ssh_password', 'ssh_port']
self._check_driver_properties("fake_ssh", expected)
def test_driver_properties_fake_pxe(self):
expected = ['pxe_deploy_kernel', 'pxe_deploy_ramdisk']
self._check_driver_properties("fake_pxe", expected)
def test_driver_properties_fake_seamicro(self):
expected = ['seamicro_api_endpoint', 'seamicro_password',
'seamicro_server_id', 'seamicro_username',
'seamicro_api_version']
self._check_driver_properties("fake_seamicro", expected)
def test_driver_properties_fake_snmp(self):
expected = ['snmp_driver', 'snmp_address', 'snmp_port', 'snmp_version',
'snmp_community', 'snmp_security', 'snmp_outlet']
self._check_driver_properties("fake_snmp", expected)
def test_driver_properties_pxe_ipmitool(self):
expected = ['ipmi_address', 'ipmi_terminal_port',
'ipmi_password', 'ipmi_priv_level',
'ipmi_username', 'ipmi_bridging', 'ipmi_transit_channel',
'ipmi_transit_address', 'ipmi_target_channel',
'ipmi_target_address', 'ipmi_local_address',
'pxe_deploy_kernel', 'pxe_deploy_ramdisk'
]
self._check_driver_properties("pxe_ipmitool", expected)
def test_driver_properties_pxe_ipminative(self):
expected = ['ipmi_address', 'ipmi_password', 'ipmi_username',
'pxe_deploy_kernel', 'pxe_deploy_ramdisk']
self._check_driver_properties("pxe_ipminative", expected)
def test_driver_properties_pxe_ssh(self):
expected = ['pxe_deploy_kernel', 'pxe_deploy_ramdisk',
'ssh_address', 'ssh_username', 'ssh_virt_type',
'ssh_key_contents', 'ssh_key_filename',
'ssh_password', 'ssh_port']
self._check_driver_properties("pxe_ssh", expected)
def test_driver_properties_pxe_seamicro(self):
expected = ['pxe_deploy_kernel', 'pxe_deploy_ramdisk',
'seamicro_api_endpoint', 'seamicro_password',
'seamicro_server_id', 'seamicro_username',
'seamicro_api_version']
self._check_driver_properties("pxe_seamicro", expected)
def test_driver_properties_pxe_snmp(self):
expected = ['pxe_deploy_kernel', 'pxe_deploy_ramdisk',
'snmp_driver', 'snmp_address', 'snmp_port', 'snmp_version',
'snmp_community', 'snmp_security', 'snmp_outlet']
self._check_driver_properties("pxe_snmp", expected)
def test_driver_properties_fake_ilo(self):
expected = ['ilo_address', 'ilo_username', 'ilo_password',
'client_port', 'client_timeout']
self._check_driver_properties("fake_ilo", expected)
def test_driver_properties_ilo_iscsi(self):
expected = ['ilo_address', 'ilo_username', 'ilo_password',
'client_port', 'client_timeout', 'ilo_deploy_iso',
'ipmi_address', 'ipmi_terminal_port',
'ipmi_password', 'ipmi_priv_level',
'ipmi_username', 'ipmi_bridging', 'ipmi_transit_channel',
'ipmi_transit_address', 'ipmi_target_channel',
'ipmi_target_address', 'ipmi_local_address']
self._check_driver_properties("iscsi_ilo", expected)
def test_driver_properties_fail(self):
mgr_utils.mock_the_extension_manager()
self.driver = driver_factory.get_driver("fake")
self.service.init_host()
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.get_driver_properties,
self.context, "bad-driver")
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.DriverNotFound, exc.exc_info[0])