719 lines
31 KiB
Python
719 lines
31 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 time
|
|
|
|
import mock
|
|
from oslo.config import cfg
|
|
from testtools.matchers import HasLength
|
|
|
|
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 import objects
|
|
from ironic.openstack.common import context
|
|
from ironic.tests.conductor import utils as mgr_utils
|
|
from ironic.tests.db import base
|
|
from ironic.tests.db import utils
|
|
|
|
CONF = cfg.CONF
|
|
|
|
|
|
class ManagerTestCase(base.DbTestCase):
|
|
|
|
def setUp(self):
|
|
super(ManagerTestCase, self).setUp()
|
|
self.service = manager.ConductorManager('test-host', 'test-topic')
|
|
self.context = context.get_admin_context()
|
|
self.dbapi = dbapi.get_instance()
|
|
mgr_utils.mock_the_extension_manager()
|
|
self.driver = driver_factory.get_driver("fake")
|
|
|
|
def test_start_registers_conductor(self):
|
|
self.assertRaises(exception.ConductorNotFound,
|
|
self.dbapi.get_conductor,
|
|
'test-host')
|
|
self.service.start()
|
|
res = self.dbapi.get_conductor('test-host')
|
|
self.assertEqual(res['hostname'], 'test-host')
|
|
|
|
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
|
|
mock_names.return_value = init_names
|
|
self.service.start()
|
|
res = self.dbapi.get_conductor('test-host')
|
|
self.assertEqual(res['drivers'], init_names)
|
|
|
|
# verify that restart registers new driver names
|
|
mock_names.return_value = restart_names
|
|
self.service.start()
|
|
res = self.dbapi.get_conductor('test-host')
|
|
self.assertEqual(res['drivers'], restart_names)
|
|
|
|
def test__conductor_service_record_keepalive(self):
|
|
self.service.start()
|
|
with mock.patch.object(self.dbapi, 'touch_conductor') as mock_touch:
|
|
self.service._conductor_service_record_keepalive(self.context)
|
|
mock_touch.assert_called_once_with('test-host')
|
|
|
|
def test__sync_power_state_no_sync(self):
|
|
self.service.start()
|
|
n = utils.get_test_node(driver='fake', power_state='fake-power')
|
|
self.dbapi.create_node(n)
|
|
with mock.patch.object(self.driver.power,
|
|
'get_power_state') as get_power_mock:
|
|
get_power_mock.return_value = 'fake-power'
|
|
self.service._sync_power_states(self.context)
|
|
get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
|
|
node = self.dbapi.get_node(n['id'])
|
|
self.assertEqual(node['power_state'], 'fake-power')
|
|
|
|
def test__sync_power_state_do_sync(self):
|
|
self.service.start()
|
|
n = utils.get_test_node(driver='fake', power_state='fake-power')
|
|
self.dbapi.create_node(n)
|
|
with mock.patch.object(self.driver.power,
|
|
'get_power_state') as get_power_mock:
|
|
get_power_mock.return_value = states.POWER_ON
|
|
self.service._sync_power_states(self.context)
|
|
get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
|
|
node = self.dbapi.get_node(n['id'])
|
|
self.assertEqual(node['power_state'], states.POWER_ON)
|
|
|
|
def test__sync_power_state_node_locked(self):
|
|
self.service.start()
|
|
n = utils.get_test_node(driver='fake', power_state='fake-power')
|
|
self.dbapi.create_node(n)
|
|
self.dbapi.reserve_nodes('fake-reserve', [n['id']])
|
|
with mock.patch.object(self.driver.power,
|
|
'get_power_state') as get_power_mock:
|
|
self.service._sync_power_states(self.context)
|
|
self.assertFalse(get_power_mock.called)
|
|
node = self.dbapi.get_node(n['id'])
|
|
self.assertEqual('fake-power', node['power_state'])
|
|
|
|
def test__sync_power_state_multiple_nodes(self):
|
|
self.service.start()
|
|
|
|
# create three nodes
|
|
nodes = []
|
|
nodeinfo = []
|
|
for i in range(0, 3):
|
|
n = utils.get_test_node(id=i, uuid=ironic_utils.generate_uuid(),
|
|
driver='fake', power_state=states.POWER_OFF)
|
|
self.dbapi.create_node(n)
|
|
nodes.append(n['uuid'])
|
|
nodeinfo.append([i, n['uuid'], 'fake'])
|
|
|
|
# lock the first node
|
|
self.dbapi.reserve_nodes('fake-reserve', [nodes[0]])
|
|
|
|
with mock.patch.object(self.driver.power,
|
|
'get_power_state') as get_power_mock:
|
|
get_power_mock.return_value = states.POWER_ON
|
|
with mock.patch.object(self.dbapi,
|
|
'get_nodeinfo_list') as get_fnl_mock:
|
|
# delete the second node
|
|
self.dbapi.destroy_node(nodes[1])
|
|
get_fnl_mock.return_value = nodeinfo
|
|
self.service._sync_power_states(self.context)
|
|
# check that get_power only called once, which updated third node
|
|
get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
|
|
n1 = self.dbapi.get_node(nodes[0])
|
|
n3 = self.dbapi.get_node(nodes[2])
|
|
self.assertEqual(n1['power_state'], states.POWER_OFF)
|
|
self.assertEqual(n3['power_state'], states.POWER_ON)
|
|
|
|
def test__sync_power_state_node_no_power_state(self):
|
|
self.service.start()
|
|
|
|
# create three nodes
|
|
nodes = []
|
|
for i in range(0, 3):
|
|
n = utils.get_test_node(id=i, uuid=ironic_utils.generate_uuid(),
|
|
driver='fake', power_state=states.POWER_OFF)
|
|
self.dbapi.create_node(n)
|
|
nodes.append(n['uuid'])
|
|
|
|
# cannot get power state of node 2; only nodes 1 & 3 have
|
|
# their power states changed.
|
|
with mock.patch.object(self.driver.power,
|
|
'get_power_state') as get_power_mock:
|
|
returns = [states.POWER_ON,
|
|
exception.InvalidParameterValue("invalid"),
|
|
states.POWER_ON]
|
|
|
|
def side_effect(*args):
|
|
result = returns.pop(0)
|
|
if isinstance(result, Exception):
|
|
raise result
|
|
return result
|
|
|
|
get_power_mock.side_effect = side_effect
|
|
self.service._sync_power_states(self.context)
|
|
self.assertThat(returns, HasLength(0))
|
|
|
|
final = [states.POWER_ON, states.POWER_OFF, states.POWER_ON]
|
|
for i in range(0, 3):
|
|
n = self.dbapi.get_node(nodes[i])
|
|
self.assertEqual(n.power_state, final[i])
|
|
|
|
def test__sync_power_state_node_deploywait(self):
|
|
self.service.start()
|
|
n = utils.get_test_node(provision_state=states.DEPLOYWAIT)
|
|
self.dbapi.create_node(n)
|
|
|
|
with mock.patch.object(self.driver.power,
|
|
'get_power_state') as get_power_mock:
|
|
self.service._sync_power_states(self.context)
|
|
self.assertFalse(get_power_mock.called)
|
|
|
|
def test_get_power_state(self):
|
|
n = utils.get_test_node(driver='fake')
|
|
self.dbapi.create_node(n)
|
|
|
|
# FakeControlDriver.get_power_state will "pass"
|
|
# and states.NOSTATE is None, so this test should pass.
|
|
state = self.service.get_node_power_state(self.context, n['uuid'])
|
|
self.assertEqual(state, states.NOSTATE)
|
|
|
|
def test_get_power_state_with_mock(self):
|
|
n = utils.get_test_node(driver='fake')
|
|
self.dbapi.create_node(n)
|
|
|
|
with mock.patch.object(self.driver.power, 'get_power_state') \
|
|
as get_power_mock:
|
|
get_power_mock.side_effect = [states.POWER_OFF, states.POWER_ON]
|
|
expected = [mock.call(mock.ANY, mock.ANY),
|
|
mock.call(mock.ANY, mock.ANY)]
|
|
|
|
state = self.service.get_node_power_state(self.context, n['uuid'])
|
|
self.assertEqual(state, states.POWER_OFF)
|
|
state = self.service.get_node_power_state(self.context, n['uuid'])
|
|
self.assertEqual(state, states.POWER_ON)
|
|
self.assertEqual(get_power_mock.call_args_list, expected)
|
|
|
|
def test_change_node_power_state_power_on(self):
|
|
# Test change_node_power_state including integration with
|
|
# conductor.utils.node_power_action and lower.
|
|
n = utils.get_test_node(driver='fake',
|
|
power_state=states.POWER_OFF)
|
|
db_node = self.dbapi.create_node(n)
|
|
self.service.start()
|
|
|
|
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,
|
|
db_node.uuid,
|
|
states.POWER_ON)
|
|
self.service._worker_pool.waitall()
|
|
|
|
get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
|
|
db_node.refresh(self.context)
|
|
self.assertEqual(states.POWER_ON, db_node.power_state)
|
|
self.assertIsNone(db_node.target_power_state)
|
|
self.assertIsNone(db_node.last_error)
|
|
# Verify the reservation has been cleared by
|
|
# background task's link callback.
|
|
self.assertIsNone(db_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
|
|
n = utils.get_test_node(driver='fake',
|
|
power_state=pwr_state,
|
|
reservation=fake_reservation)
|
|
db_node = self.dbapi.create_node(n)
|
|
self.service.start()
|
|
|
|
self.assertRaises(exception.NodeLocked,
|
|
self.service.change_node_power_state,
|
|
self.context,
|
|
db_node.uuid,
|
|
states.POWER_ON)
|
|
# 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.
|
|
db_node.refresh(self.context)
|
|
self.assertEqual(fake_reservation, db_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
|
|
n = utils.get_test_node(driver='fake',
|
|
power_state=initial_state)
|
|
db_node = self.dbapi.create_node(n)
|
|
self.service.start()
|
|
|
|
with mock.patch.object(self.service, '_spawn_worker') \
|
|
as spawn_mock:
|
|
spawn_mock.side_effect = exception.NoFreeConductorWorker()
|
|
|
|
self.assertRaises(exception.NoFreeConductorWorker,
|
|
self.service.change_node_power_state,
|
|
self.context,
|
|
db_node.uuid,
|
|
states.POWER_ON)
|
|
|
|
spawn_mock.assert_called_once_with(mock.ANY, mock.ANY,
|
|
mock.ANY, mock.ANY)
|
|
db_node.refresh(self.context)
|
|
self.assertEqual(initial_state, db_node.power_state)
|
|
self.assertIsNone(db_node.target_power_state)
|
|
self.assertIsNone(db_node.last_error)
|
|
# Verify the picked reservation has been cleared due to full pool.
|
|
self.assertIsNone(db_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
|
|
n = utils.get_test_node(driver='fake',
|
|
power_state=initial_state)
|
|
db_node = self.dbapi.create_node(n)
|
|
self.service.start()
|
|
|
|
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,
|
|
db_node.uuid,
|
|
new_state)
|
|
self.service._worker_pool.waitall()
|
|
|
|
get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
|
|
set_power_mock.assert_called_once_with(mock.ANY, mock.ANY,
|
|
new_state)
|
|
db_node.refresh(self.context)
|
|
self.assertEqual(initial_state, db_node.power_state)
|
|
self.assertIsNone(db_node.target_power_state)
|
|
self.assertIsNotNone(db_node.last_error)
|
|
# Verify the reservation has been cleared by background task's
|
|
# link callback despite exception in background task.
|
|
self.assertIsNone(db_node.reservation)
|
|
|
|
def test_update_node(self):
|
|
ndict = utils.get_test_node(driver='fake', extra={'test': 'one'})
|
|
node = self.dbapi.create_node(ndict)
|
|
|
|
# check that ManagerService.update_node actually updates the node
|
|
node['extra'] = {'test': 'two'}
|
|
res = self.service.update_node(self.context, node)
|
|
self.assertEqual(res['extra'], {'test': 'two'})
|
|
|
|
def test_update_node_already_locked(self):
|
|
ndict = utils.get_test_node(driver='fake', extra={'test': 'one'})
|
|
node = self.dbapi.create_node(ndict)
|
|
|
|
# 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'}
|
|
self.assertRaises(exception.NodeLocked,
|
|
self.service.update_node,
|
|
self.context,
|
|
node)
|
|
|
|
# verify change did not happen
|
|
res = objects.Node.get_by_uuid(self.context, node['uuid'])
|
|
self.assertEqual(res['extra'], {'test': 'one'})
|
|
|
|
def test_associate_node_invalid_state(self):
|
|
ndict = utils.get_test_node(driver='fake',
|
|
extra={'test': 'one'},
|
|
instance_uuid=None,
|
|
power_state=states.POWER_ON)
|
|
node = self.dbapi.create_node(ndict)
|
|
|
|
# check that it fails because state is POWER_ON
|
|
node['instance_uuid'] = 'fake-uuid'
|
|
self.assertRaises(exception.NodeInWrongPowerState,
|
|
self.service.update_node,
|
|
self.context,
|
|
node)
|
|
|
|
# verify change did not happen
|
|
res = objects.Node.get_by_uuid(self.context, node['uuid'])
|
|
self.assertIsNone(res['instance_uuid'])
|
|
|
|
def test_associate_node_valid_state(self):
|
|
ndict = utils.get_test_node(driver='fake',
|
|
instance_uuid=None,
|
|
power_state=states.NOSTATE)
|
|
node = self.dbapi.create_node(ndict)
|
|
|
|
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
|
|
res = objects.Node.get_by_uuid(self.context, node['uuid'])
|
|
self.assertEqual(res['instance_uuid'], 'fake-uuid')
|
|
|
|
def test_update_node_invalid_driver(self):
|
|
existing_driver = 'fake'
|
|
wrong_driver = 'wrong-driver'
|
|
ndict = utils.get_test_node(driver=existing_driver,
|
|
extra={'test': 'one'},
|
|
instance_uuid=None,
|
|
task_state=states.POWER_ON)
|
|
node = self.dbapi.create_node(ndict)
|
|
# 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
|
|
res = objects.Node.get_by_uuid(self.context, node['uuid'])
|
|
self.assertEqual(res['driver'], existing_driver)
|
|
|
|
def test_vendor_action(self):
|
|
n = utils.get_test_node(driver='fake')
|
|
self.dbapi.create_node(n)
|
|
info = {'bar': 'baz'}
|
|
self.service.do_vendor_action(
|
|
self.context, n['uuid'], 'first_method', info)
|
|
|
|
def test_validate_vendor_action(self):
|
|
n = utils.get_test_node(driver='fake')
|
|
node = self.dbapi.create_node(n)
|
|
info = {'bar': 'baz'}
|
|
self.service.validate_vendor_action(
|
|
self.context, n['uuid'], 'first_method', info)
|
|
node.refresh(self.context)
|
|
self.assertIsNone(node.last_error)
|
|
|
|
def test_validate_vendor_action_unsupported_method(self):
|
|
n = utils.get_test_node(driver='fake')
|
|
node = self.dbapi.create_node(n)
|
|
info = {'bar': 'baz'}
|
|
self.assertRaises(exception.InvalidParameterValue,
|
|
self.service.validate_vendor_action,
|
|
self.context, n['uuid'], 'abc', info)
|
|
node.refresh(self.context)
|
|
self.assertIsNotNone(node.last_error)
|
|
|
|
def test_validate_vendor_action_no_parameter(self):
|
|
n = utils.get_test_node(driver='fake')
|
|
node = self.dbapi.create_node(n)
|
|
info = {'fake': 'baz'}
|
|
self.assertRaises(exception.InvalidParameterValue,
|
|
self.service.validate_vendor_action,
|
|
self.context, n['uuid'], 'first_method', info)
|
|
node.refresh(self.context)
|
|
self.assertIsNotNone(node.last_error)
|
|
|
|
def test_validate_vendor_action_unsupported(self):
|
|
n = utils.get_test_node(driver='fake')
|
|
node = self.dbapi.create_node(n)
|
|
info = {'bar': 'baz'}
|
|
self.driver.vendor = None
|
|
self.assertRaises(exception.UnsupportedDriverExtension,
|
|
self.service.validate_vendor_action,
|
|
self.context, n['uuid'], 'foo', info)
|
|
node.refresh(self.context)
|
|
self.assertIsNotNone(node.last_error)
|
|
|
|
def test_do_node_deploy_invalid_state(self):
|
|
# test node['provision_state'] is not NOSTATE
|
|
ndict = utils.get_test_node(driver='fake',
|
|
provision_state=states.ACTIVE)
|
|
node = self.dbapi.create_node(ndict)
|
|
self.assertRaises(exception.InstanceDeployFailure,
|
|
self.service.do_node_deploy,
|
|
self.context, node['uuid'])
|
|
|
|
def test_do_node_deploy_maintenance(self):
|
|
ndict = utils.get_test_node(driver='fake', maintenance=True)
|
|
node = self.dbapi.create_node(ndict)
|
|
self.assertRaises(exception.InstanceDeployFailure,
|
|
self.service.do_node_deploy,
|
|
self.context, node['uuid'])
|
|
|
|
def test_do_node_deploy_driver_raises_error(self):
|
|
# test when driver.deploy.deploy raises an exception
|
|
ndict = utils.get_test_node(driver='fake',
|
|
provision_state=states.NOSTATE)
|
|
node = self.dbapi.create_node(ndict)
|
|
|
|
with mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy') \
|
|
as deploy:
|
|
deploy.side_effect = exception.InstanceDeployFailure('test')
|
|
self.assertRaises(exception.InstanceDeployFailure,
|
|
self.service.do_node_deploy,
|
|
self.context, node['uuid'])
|
|
node.refresh(self.context)
|
|
self.assertEqual(node['provision_state'], states.DEPLOYFAIL)
|
|
self.assertEqual(node['target_provision_state'], states.NOSTATE)
|
|
self.assertIsNotNone(node['last_error'])
|
|
deploy.assert_called_once_with(mock.ANY, mock.ANY)
|
|
|
|
def test_do_node_deploy_ok(self):
|
|
# test when driver.deploy.deploy returns DEPLOYDONE
|
|
ndict = utils.get_test_node(driver='fake',
|
|
provision_state=states.NOSTATE)
|
|
node = self.dbapi.create_node(ndict)
|
|
|
|
with mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy') \
|
|
as deploy:
|
|
deploy.return_value = states.DEPLOYDONE
|
|
self.service.do_node_deploy(self.context, node['uuid'])
|
|
node.refresh(self.context)
|
|
self.assertEqual(node['provision_state'], states.ACTIVE)
|
|
self.assertEqual(node['target_provision_state'], states.NOSTATE)
|
|
self.assertIsNone(node['last_error'])
|
|
deploy.assert_called_once_with(mock.ANY, mock.ANY)
|
|
|
|
def test_do_node_deploy_partial_ok(self):
|
|
# test when driver.deploy.deploy doesn't return DEPLOYDONE
|
|
ndict = utils.get_test_node(driver='fake',
|
|
provision_state=states.NOSTATE)
|
|
node = self.dbapi.create_node(ndict)
|
|
|
|
with mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy') \
|
|
as deploy:
|
|
deploy.return_value = states.DEPLOYING
|
|
self.service.do_node_deploy(self.context, node['uuid'])
|
|
node.refresh(self.context)
|
|
self.assertEqual(node['provision_state'], states.DEPLOYING)
|
|
self.assertEqual(node['target_provision_state'], states.DEPLOYDONE)
|
|
self.assertIsNone(node['last_error'])
|
|
deploy.assert_called_once_with(mock.ANY, mock.ANY)
|
|
|
|
def test_do_node_tear_down_invalid_state(self):
|
|
# test node['provision_state'] is incorrect for tear_down
|
|
ndict = utils.get_test_node(driver='fake',
|
|
provision_state=states.NOSTATE)
|
|
node = self.dbapi.create_node(ndict)
|
|
self.assertRaises(exception.InstanceDeployFailure,
|
|
self.service.do_node_tear_down,
|
|
self.context, node['uuid'])
|
|
|
|
def test_do_node_tear_down_driver_raises_error(self):
|
|
# test when driver.deploy.tear_down raises exception
|
|
ndict = utils.get_test_node(driver='fake',
|
|
provision_state=states.ACTIVE)
|
|
node = self.dbapi.create_node(ndict)
|
|
|
|
with mock.patch('ironic.drivers.modules.fake.FakeDeploy.tear_down') \
|
|
as deploy:
|
|
deploy.side_effect = exception.InstanceDeployFailure('test')
|
|
self.assertRaises(exception.InstanceDeployFailure,
|
|
self.service.do_node_tear_down,
|
|
self.context, node['uuid'])
|
|
node.refresh(self.context)
|
|
self.assertEqual(node['provision_state'], states.ERROR)
|
|
self.assertEqual(node['target_provision_state'], states.NOSTATE)
|
|
self.assertIsNotNone(node['last_error'])
|
|
deploy.assert_called_once_with(mock.ANY, mock.ANY)
|
|
|
|
def test_do_node_tear_down_ok(self):
|
|
# test when driver.deploy.tear_down returns DELETED
|
|
ndict = utils.get_test_node(driver='fake',
|
|
provision_state=states.ACTIVE)
|
|
node = self.dbapi.create_node(ndict)
|
|
|
|
with mock.patch('ironic.drivers.modules.fake.FakeDeploy.tear_down') \
|
|
as deploy:
|
|
deploy.return_value = states.DELETED
|
|
self.service.do_node_tear_down(self.context, node['uuid'])
|
|
node.refresh(self.context)
|
|
self.assertEqual(node['provision_state'], states.NOSTATE)
|
|
self.assertEqual(node['target_provision_state'], states.NOSTATE)
|
|
self.assertIsNone(node['last_error'])
|
|
deploy.assert_called_once_with(mock.ANY, mock.ANY)
|
|
|
|
def test_do_node_tear_down_partial_ok(self):
|
|
# test when driver.deploy.tear_down doesn't return DELETED
|
|
ndict = utils.get_test_node(driver='fake',
|
|
provision_state=states.ACTIVE)
|
|
node = self.dbapi.create_node(ndict)
|
|
|
|
with mock.patch('ironic.drivers.modules.fake.FakeDeploy.tear_down') \
|
|
as deploy:
|
|
deploy.return_value = states.DELETING
|
|
self.service.do_node_tear_down(self.context, node['uuid'])
|
|
node.refresh(self.context)
|
|
self.assertEqual(node['provision_state'], states.DELETING)
|
|
self.assertEqual(node['target_provision_state'], states.DELETED)
|
|
self.assertIsNone(node['last_error'])
|
|
deploy.assert_called_once_with(mock.ANY, mock.ANY)
|
|
|
|
def test_validate_driver_interfaces(self):
|
|
ndict = utils.get_test_node(driver='fake')
|
|
node = self.dbapi.create_node(ndict)
|
|
ret = self.service.validate_driver_interfaces(self.context,
|
|
node['uuid'])
|
|
expected = {'console': {'result': None, 'reason': 'not supported'},
|
|
'rescue': {'result': None, 'reason': 'not supported'},
|
|
'power': {'result': True},
|
|
'deploy': {'result': True}}
|
|
self.assertEqual(expected, ret)
|
|
|
|
def test_validate_driver_interfaces_validation_fail(self):
|
|
ndict = utils.get_test_node(driver='fake')
|
|
node = self.dbapi.create_node(ndict)
|
|
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):
|
|
ndict = utils.get_test_node(driver='fake')
|
|
node = self.dbapi.create_node(ndict)
|
|
self.service.change_node_maintenance_mode(self.context, node.uuid,
|
|
True)
|
|
node.refresh(self.context)
|
|
self.assertTrue(node.maintenance)
|
|
|
|
def test_maintenance_mode_off(self):
|
|
ndict = utils.get_test_node(driver='fake',
|
|
maintenance=True)
|
|
node = self.dbapi.create_node(ndict)
|
|
self.service.change_node_maintenance_mode(self.context, node.uuid,
|
|
False)
|
|
node.refresh(self.context)
|
|
self.assertFalse(node.maintenance)
|
|
|
|
def test_maintenance_mode_on_failed(self):
|
|
ndict = utils.get_test_node(driver='fake',
|
|
maintenance=True)
|
|
node = self.dbapi.create_node(ndict)
|
|
self.assertRaises(exception.NodeMaintenanceFailure,
|
|
self.service.change_node_maintenance_mode,
|
|
self.context, node.uuid, True)
|
|
node.refresh(self.context)
|
|
self.assertTrue(node.maintenance)
|
|
|
|
def test_maintenance_mode_off_failed(self):
|
|
ndict = utils.get_test_node(driver='fake')
|
|
node = self.dbapi.create_node(ndict)
|
|
self.assertRaises(exception.NodeMaintenanceFailure,
|
|
self.service.change_node_maintenance_mode,
|
|
self.context, node.uuid, False)
|
|
node.refresh(self.context)
|
|
self.assertFalse(node.maintenance)
|
|
|
|
def test__spawn_worker(self):
|
|
func_mock = mock.Mock()
|
|
args = (1, 2, "test")
|
|
kwargs = dict(kw1='test1', kw2='test2')
|
|
self.service.start()
|
|
|
|
thread = self.service._spawn_worker(func_mock, *args, **kwargs)
|
|
self.service._worker_pool.waitall()
|
|
|
|
self.assertIsNotNone(thread)
|
|
func_mock.assert_called_once_with(*args, **kwargs)
|
|
|
|
# The tests below related to greenthread. We have they to assert our
|
|
# assumptions about greenthread behavior.
|
|
|
|
def test__spawn_link_callback_added_during_execution(self):
|
|
def func():
|
|
time.sleep(1)
|
|
link_callback = mock.Mock()
|
|
self.service.start()
|
|
|
|
thread = self.service._spawn_worker(func)
|
|
# func_mock executing at this moment
|
|
thread.link(link_callback)
|
|
self.service._worker_pool.waitall()
|
|
|
|
link_callback.assert_called_once_with(thread)
|
|
|
|
def test__spawn_link_callback_added_after_execution(self):
|
|
def func():
|
|
pass
|
|
link_callback = mock.Mock()
|
|
self.service.start()
|
|
|
|
thread = self.service._spawn_worker(func)
|
|
self.service._worker_pool.waitall()
|
|
# func_mock finished at this moment
|
|
thread.link(link_callback)
|
|
|
|
link_callback.assert_called_once_with(thread)
|
|
|
|
def test__spawn_link_callback_exception_inside_thread(self):
|
|
def func():
|
|
time.sleep(1)
|
|
raise Exception()
|
|
link_callback = mock.Mock()
|
|
self.service.start()
|
|
|
|
thread = self.service._spawn_worker(func)
|
|
# func_mock executing at this moment
|
|
thread.link(link_callback)
|
|
self.service._worker_pool.waitall()
|
|
|
|
link_callback.assert_called_once_with(thread)
|
|
|
|
def test__spawn_link_callback_added_after_exception_inside_thread(self):
|
|
def func():
|
|
raise Exception()
|
|
link_callback = mock.Mock()
|
|
self.service.start()
|
|
|
|
thread = self.service._spawn_worker(func)
|
|
self.service._worker_pool.waitall()
|
|
# func_mock finished at this moment
|
|
thread.link(link_callback)
|
|
|
|
link_callback.assert_called_once_with(thread)
|