~ubuntu-branches/ubuntu/vivid/ironic/vivid-updates

« back to all changes in this revision

Viewing changes to ironic/tests/conductor/test_manager.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short
  • Date: 2015-03-30 11:14:57 UTC
  • mfrom: (1.2.6)
  • Revision ID: package-import@ubuntu.com-20150330111457-kr4ju3guf22m4vbz
Tags: 2015.1~b3-0ubuntu1
* New upstream release.
  + d/control: 
    - Align with upstream dependencies.
    - Add dh-python to build-dependencies.
    - Add psmisc as a dependency. (LP: #1358820)
  + d/p/fix-requirements.patch: Rediffed.
  + d/ironic-conductor.init.in: Fixed typos in LSB headers,
    thanks to JJ Asghar. (LP: #1429962)

Show diffs side-by-side

added added

removed removed

Lines of Context:
24
24
import mock
25
25
from oslo import messaging
26
26
from oslo_config import cfg
 
27
from oslo_context import context
27
28
from oslo_db import exception as db_exception
 
29
from oslo_utils import strutils
 
30
from oslo_utils import uuidutils
28
31
 
29
32
from ironic.common import boot_devices
30
33
from ironic.common import driver_factory
31
34
from ironic.common import exception
 
35
from ironic.common import images
32
36
from ironic.common import keystone
33
37
from ironic.common import states
34
38
from ironic.common import swift
35
 
from ironic.common import utils as ironic_utils
36
39
from ironic.conductor import manager
37
40
from ironic.conductor import task_manager
38
41
from ironic.conductor import utils as conductor_utils
39
42
from ironic.db import api as dbapi
40
43
from ironic.drivers import base as drivers_base
41
44
from ironic import objects
42
 
from ironic.openstack.common import context
43
45
from ironic.tests import base as tests_base
44
46
from ironic.tests.conductor import utils as mgr_utils
45
47
from ironic.tests.db import base as tests_db_base
53
55
    @staticmethod
54
56
    def _create_node(**kwargs):
55
57
        attrs = {'id': 1,
56
 
                 'uuid': ironic_utils.generate_uuid(),
 
58
                 'uuid': uuidutils.generate_uuid(),
57
59
                 'power_state': states.POWER_OFF,
58
60
                 'maintenance': False,
59
61
                 'reservation': None}
127
129
                # NOTE(comstud): Not ideal to throw this into
128
130
                # a helper, however it's the cleanest way
129
131
                # to verify we're dealing with the correct task/node.
130
 
                if ironic_utils.is_int_like(fa_self.node_id):
 
132
                if strutils.is_int_like(fa_self.node_id):
131
133
                    self.assertEqual(fa_self.node_id, task.node.id)
132
134
                else:
133
135
                    self.assertEqual(fa_self.node_id, task.node.uuid)
198
200
                          objects.Conductor.get_by_hostname,
199
201
                          self.context, self.hostname)
200
202
 
 
203
    def test_stop_doesnt_unregister_conductor(self):
 
204
        self._start_service()
 
205
        res = objects.Conductor.get_by_hostname(self.context, self.hostname)
 
206
        self.assertEqual(self.hostname, res['hostname'])
 
207
        self.service.del_host(deregister=False)
 
208
        res = objects.Conductor.get_by_hostname(self.context, self.hostname)
 
209
        self.assertEqual(self.hostname, res['hostname'])
 
210
 
201
211
    @mock.patch.object(driver_factory.DriverFactory, '__getitem__',
202
212
                       lambda *args: mock.MagicMock())
203
213
    def test_start_registers_driver_names(self):
913
923
 
914
924
 
915
925
@_mock_record_keepalive
916
 
class DoNodeDeployTearDownTestCase(_ServiceSetUpMixin,
 
926
@mock.patch.object(images, 'is_whole_disk_image')
 
927
class ServiceDoNodeDeployTestCase(_ServiceSetUpMixin,
917
928
                                   tests_db_base.DbTestCase):
918
 
    def test_do_node_deploy_invalid_state(self):
 
929
    def test_do_node_deploy_invalid_state(self, mock_iwdi):
 
930
        mock_iwdi.return_value = False
919
931
        self._start_service()
920
932
        # test that node deploy fails if the node is already provisioned
921
933
        node = obj_utils.create_test_node(self.context, driver='fake',
930
942
        self.assertIsNone(node.last_error)
931
943
        # Verify reservation has been cleared.
932
944
        self.assertIsNone(node.reservation)
 
945
        mock_iwdi.assert_called_once_with(self.context, node.instance_info)
 
946
        self.assertNotIn('is_whole_disk_image', node.driver_internal_info)
933
947
 
934
 
    def test_do_node_deploy_maintenance(self):
 
948
    def test_do_node_deploy_maintenance(self, mock_iwdi):
 
949
        mock_iwdi.return_value = False
935
950
        node = obj_utils.create_test_node(self.context, driver='fake',
936
951
                                          maintenance=True)
937
952
        exc = self.assertRaises(messaging.rpc.ExpectedException,
943
958
        self.assertIsNone(node.last_error)
944
959
        # Verify reservation has been cleared.
945
960
        self.assertIsNone(node.reservation)
 
961
        self.assertFalse(mock_iwdi.called)
946
962
 
947
 
    def _test_do_node_deploy_validate_fail(self, mock_validate):
 
963
    def _test_do_node_deploy_validate_fail(self, mock_validate, mock_iwdi):
 
964
        mock_iwdi.return_value = False
948
965
        # InvalidParameterValue should be re-raised as InstanceDeployFailure
949
966
        mock_validate.side_effect = exception.InvalidParameterValue('error')
950
967
        node = obj_utils.create_test_node(self.context, driver='fake')
957
974
        self.assertIsNone(node.last_error)
958
975
        # Verify reservation has been cleared.
959
976
        self.assertIsNone(node.reservation)
 
977
        mock_iwdi.assert_called_once_with(self.context, node.instance_info)
 
978
        self.assertNotIn('is_whole_disk_image', node.driver_internal_info)
960
979
 
961
980
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.validate')
962
 
    def test_do_node_deploy_validate_fail(self, mock_validate):
963
 
        self._test_do_node_deploy_validate_fail(mock_validate)
 
981
    def test_do_node_deploy_validate_fail(self, mock_validate, mock_iwdi):
 
982
        self._test_do_node_deploy_validate_fail(mock_validate, mock_iwdi)
964
983
 
965
984
    @mock.patch('ironic.drivers.modules.fake.FakePower.validate')
966
 
    def test_do_node_deploy_power_validate_fail(self, mock_validate):
967
 
        self._test_do_node_deploy_validate_fail(mock_validate)
968
 
 
969
 
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
970
 
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.prepare')
971
 
    def test__do_node_deploy_driver_raises_prepare_error(self, mock_prepare,
972
 
                                                         mock_deploy):
973
 
        self._start_service()
974
 
        # test when driver.deploy.prepare raises an exception
975
 
        mock_prepare.side_effect = exception.InstanceDeployFailure('test')
976
 
        node = obj_utils.create_test_node(self.context, driver='fake',
977
 
                                          provision_state=states.DEPLOYING,
978
 
                                          target_provision_state=states.ACTIVE)
979
 
        task = task_manager.TaskManager(self.context, node.uuid)
980
 
 
981
 
        self.assertRaises(exception.InstanceDeployFailure,
982
 
                          manager.do_node_deploy, task,
983
 
                          self.service.conductor.id)
984
 
        node.refresh()
985
 
        self.assertEqual(states.DEPLOYFAIL, node.provision_state)
986
 
        # NOTE(deva): failing a deploy does not clear the target state
987
 
        #             any longer. Instead, it is cleared when the instance
988
 
        #             is deleted.
989
 
        self.assertEqual(states.ACTIVE, node.target_provision_state)
990
 
        self.assertIsNotNone(node.last_error)
991
 
        self.assertTrue(mock_prepare.called)
992
 
        self.assertFalse(mock_deploy.called)
993
 
 
994
 
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
995
 
    def test__do_node_deploy_driver_raises_error(self, mock_deploy):
996
 
        self._start_service()
997
 
        # test when driver.deploy.deploy raises an exception
998
 
        mock_deploy.side_effect = exception.InstanceDeployFailure('test')
999
 
        node = obj_utils.create_test_node(self.context, driver='fake',
1000
 
                                          provision_state=states.DEPLOYING,
1001
 
                                          target_provision_state=states.ACTIVE)
1002
 
        task = task_manager.TaskManager(self.context, node.uuid)
1003
 
 
1004
 
        self.assertRaises(exception.InstanceDeployFailure,
1005
 
                          manager.do_node_deploy, task,
1006
 
                          self.service.conductor.id)
1007
 
        node.refresh()
1008
 
        self.assertEqual(states.DEPLOYFAIL, node.provision_state)
1009
 
        # NOTE(deva): failing a deploy does not clear the target state
1010
 
        #             any longer. Instead, it is cleared when the instance
1011
 
        #             is deleted.
1012
 
        self.assertEqual(states.ACTIVE, node.target_provision_state)
1013
 
        self.assertIsNotNone(node.last_error)
1014
 
        mock_deploy.assert_called_once_with(mock.ANY)
1015
 
 
1016
 
    @mock.patch.object(manager, '_store_configdrive')
1017
 
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
1018
 
    def test__do_node_deploy_ok(self, mock_deploy, mock_store):
1019
 
        self._start_service()
1020
 
        # test when driver.deploy.deploy returns DEPLOYDONE
1021
 
        mock_deploy.return_value = states.DEPLOYDONE
1022
 
        node = obj_utils.create_test_node(self.context, driver='fake',
1023
 
                                          provision_state=states.DEPLOYING,
1024
 
                                          target_provision_state=states.ACTIVE)
1025
 
        task = task_manager.TaskManager(self.context, node.uuid)
1026
 
 
1027
 
        manager.do_node_deploy(task, self.service.conductor.id)
1028
 
        node.refresh()
1029
 
        self.assertEqual(states.ACTIVE, node.provision_state)
1030
 
        self.assertEqual(states.NOSTATE, node.target_provision_state)
1031
 
        self.assertIsNone(node.last_error)
1032
 
        mock_deploy.assert_called_once_with(mock.ANY)
1033
 
        # assert _store_configdrive wasn't invoked
1034
 
        self.assertFalse(mock_store.called)
1035
 
 
1036
 
    @mock.patch.object(manager, '_store_configdrive')
1037
 
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
1038
 
    def test__do_node_deploy_ok_configdrive(self, mock_deploy, mock_store):
1039
 
        self._start_service()
1040
 
        # test when driver.deploy.deploy returns DEPLOYDONE
1041
 
        mock_deploy.return_value = states.DEPLOYDONE
1042
 
        node = obj_utils.create_test_node(self.context, driver='fake',
1043
 
                                          provision_state=states.DEPLOYING,
1044
 
                                          target_provision_state=states.ACTIVE)
1045
 
        task = task_manager.TaskManager(self.context, node.uuid)
1046
 
        configdrive = 'foo'
1047
 
 
1048
 
        manager.do_node_deploy(task, self.service.conductor.id,
1049
 
                               configdrive=configdrive)
1050
 
        node.refresh()
1051
 
        self.assertEqual(states.ACTIVE, node.provision_state)
1052
 
        self.assertEqual(states.NOSTATE, node.target_provision_state)
1053
 
        self.assertIsNone(node.last_error)
1054
 
        mock_deploy.assert_called_once_with(mock.ANY)
1055
 
        mock_store.assert_called_once_with(task.node, configdrive)
1056
 
 
1057
 
    @mock.patch.object(swift, 'SwiftAPI')
1058
 
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
1059
 
    def test__do_node_deploy_configdrive_swift_error(self, mock_deploy,
1060
 
                                                     mock_swift):
1061
 
        CONF.set_override('configdrive_use_swift', True, group='conductor')
1062
 
        self._start_service()
1063
 
        # test when driver.deploy.deploy returns DEPLOYDONE
1064
 
        mock_deploy.return_value = states.DEPLOYDONE
1065
 
        node = obj_utils.create_test_node(self.context, driver='fake',
1066
 
                                          provision_state=states.DEPLOYING,
1067
 
                                          target_provision_state=states.ACTIVE)
1068
 
        task = task_manager.TaskManager(self.context, node.uuid)
1069
 
 
1070
 
        mock_swift.side_effect = exception.SwiftOperationError('error')
1071
 
        self.assertRaises(exception.SwiftOperationError,
1072
 
                           manager.do_node_deploy, task,
1073
 
                           self.service.conductor.id,
1074
 
                           configdrive='fake config drive')
1075
 
        node.refresh()
1076
 
        self.assertEqual(states.DEPLOYFAIL, node.provision_state)
1077
 
        self.assertEqual(states.ACTIVE, node.target_provision_state)
1078
 
        self.assertIsNotNone(node.last_error)
1079
 
        self.assertFalse(mock_deploy.called)
1080
 
 
1081
 
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
1082
 
    def test__do_node_deploy_ok_2(self, mock_deploy):
1083
 
        # NOTE(rloo): a different way of testing for the same thing as in
1084
 
        # test__do_node_deploy_ok()
1085
 
        self._start_service()
1086
 
        # test when driver.deploy.deploy returns DEPLOYDONE
1087
 
        mock_deploy.return_value = states.DEPLOYDONE
1088
 
        node = obj_utils.create_test_node(self.context, driver='fake')
1089
 
        task = task_manager.TaskManager(self.context, node.uuid)
1090
 
        task.process_event('deploy')
1091
 
 
1092
 
        manager.do_node_deploy(task, self.service.conductor.id)
1093
 
        node.refresh()
1094
 
        self.assertEqual(states.ACTIVE, node.provision_state)
1095
 
        self.assertEqual(states.NOSTATE, node.target_provision_state)
1096
 
        self.assertIsNone(node.last_error)
1097
 
        mock_deploy.assert_called_once_with(mock.ANY)
 
985
    def test_do_node_deploy_power_validate_fail(self, mock_validate,
 
986
                                                mock_iwdi):
 
987
        self._test_do_node_deploy_validate_fail(mock_validate, mock_iwdi)
1098
988
 
1099
989
    @mock.patch('ironic.conductor.task_manager.TaskManager.process_event')
1100
 
    def test_deploy_with_nostate_converts_to_available(self, mock_pe):
 
990
    def test_deploy_with_nostate_converts_to_available(self, mock_pe,
 
991
                                                       mock_iwdi):
1101
992
        # expressly create a node using the Juno-era NOSTATE state
1102
993
        # and assert that it does not result in an error, and that the state
1103
994
        # is converted to the new AVAILABLE state.
1104
995
        # Mock the process_event call, because the transitions from
1105
996
        # AVAILABLE are tested thoroughly elsewhere
1106
997
        # NOTE(deva): This test can be deleted after Kilo is released
 
998
        mock_iwdi.return_value = False
1107
999
        self._start_service()
1108
1000
        node = obj_utils.create_test_node(self.context, driver='fake',
1109
1001
                                          provision_state=states.NOSTATE)
1112
1004
        self.assertTrue(mock_pe.called)
1113
1005
        node.refresh()
1114
1006
        self.assertEqual(states.AVAILABLE, node.provision_state)
 
1007
        mock_iwdi.assert_called_once_with(self.context, node.instance_info)
 
1008
        self.assertFalse(node.driver_internal_info['is_whole_disk_image'])
1115
1009
 
1116
 
    def test_do_node_deploy_partial_ok(self):
 
1010
    def test_do_node_deploy_partial_ok(self, mock_iwdi):
 
1011
        mock_iwdi.return_value = False
1117
1012
        self._start_service()
1118
1013
        thread = self.service._spawn_worker(lambda: None)
1119
1014
        with mock.patch.object(self.service, '_spawn_worker') as mock_spawn:
1133
1028
            self.assertIsNone(node.reservation)
1134
1029
            mock_spawn.assert_called_once_with(mock.ANY, mock.ANY,
1135
1030
                                               mock.ANY, None)
 
1031
            mock_iwdi.assert_called_once_with(self.context, node.instance_info)
 
1032
            self.assertFalse(node.driver_internal_info['is_whole_disk_image'])
1136
1033
 
1137
1034
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
1138
 
    def test_do_node_deploy_rebuild_active_state(self, mock_deploy):
 
1035
    def test_do_node_deploy_rebuild_active_state(self, mock_deploy, mock_iwdi):
1139
1036
        # This tests manager.do_node_deploy(), the 'else' path of
1140
1037
        # 'if new_state == states.DEPLOYDONE'. The node's states
1141
1038
        # aren't changed in this case.
 
1039
        mock_iwdi.return_value = True
1142
1040
        self._start_service()
1143
1041
        mock_deploy.return_value = states.DEPLOYING
1144
1042
        node = obj_utils.create_test_node(self.context, driver='fake',
1145
 
                                         provision_state=states.ACTIVE,
1146
 
                                         target_provision_state=states.NOSTATE,
1147
 
                                         instance_info={'kernel': 'aaaa',
1148
 
                                         'ramdisk': 'bbbb'})
 
1043
            provision_state=states.ACTIVE,
 
1044
            target_provision_state=states.NOSTATE,
 
1045
            instance_info={'image_source': uuidutils.generate_uuid(),
 
1046
                           'kernel': 'aaaa', 'ramdisk': 'bbbb'},
 
1047
            driver_internal_info={'is_whole_disk_image': False})
1149
1048
 
1150
1049
        self.service.do_node_deploy(self.context, node.uuid, rebuild=True)
1151
1050
        self.service._worker_pool.waitall()
1160
1059
        # Verify instance_info values has been cleared.
1161
1060
        self.assertNotIn('kernel', node.instance_info)
1162
1061
        self.assertNotIn('ramdisk', node.instance_info)
 
1062
        mock_iwdi.assert_called_once_with(self.context, node.instance_info)
 
1063
        # Verify is_whole_disk_image reflects correct value on rebuild.
 
1064
        self.assertTrue(node.driver_internal_info['is_whole_disk_image'])
1163
1065
 
1164
1066
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
1165
 
    def test_do_node_deploy_rebuild_active_state_waiting(self, mock_deploy):
 
1067
    def test_do_node_deploy_rebuild_active_state_waiting(self, mock_deploy,
 
1068
                                                         mock_iwdi):
 
1069
        mock_iwdi.return_value = False
1166
1070
        self._start_service()
1167
1071
        mock_deploy.return_value = states.DEPLOYWAIT
1168
1072
        node = obj_utils.create_test_node(self.context, driver='fake',
1169
 
                                         provision_state=states.ACTIVE,
1170
 
                                         target_provision_state=states.NOSTATE)
 
1073
            provision_state=states.ACTIVE,
 
1074
            target_provision_state=states.NOSTATE,
 
1075
            instance_info={'image_source': uuidutils.generate_uuid()})
1171
1076
 
1172
1077
        self.service.do_node_deploy(self.context, node.uuid, rebuild=True)
1173
1078
        self.service._worker_pool.waitall()
1179
1084
        # Verify reservation has been cleared.
1180
1085
        self.assertIsNone(node.reservation)
1181
1086
        mock_deploy.assert_called_once_with(mock.ANY)
 
1087
        mock_iwdi.assert_called_once_with(self.context, node.instance_info)
 
1088
        self.assertFalse(node.driver_internal_info['is_whole_disk_image'])
1182
1089
 
1183
1090
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
1184
 
    def test_do_node_deploy_rebuild_active_state_done(self, mock_deploy):
 
1091
    def test_do_node_deploy_rebuild_active_state_done(self, mock_deploy,
 
1092
                                                      mock_iwdi):
 
1093
        mock_iwdi.return_value = False
1185
1094
        self._start_service()
1186
1095
        mock_deploy.return_value = states.DEPLOYDONE
1187
1096
        node = obj_utils.create_test_node(self.context, driver='fake',
1198
1107
        # Verify reservation has been cleared.
1199
1108
        self.assertIsNone(node.reservation)
1200
1109
        mock_deploy.assert_called_once_with(mock.ANY)
 
1110
        mock_iwdi.assert_called_once_with(self.context, node.instance_info)
 
1111
        self.assertFalse(node.driver_internal_info['is_whole_disk_image'])
1201
1112
 
1202
1113
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
1203
 
    def test_do_node_deploy_rebuild_deployfail_state(self, mock_deploy):
 
1114
    def test_do_node_deploy_rebuild_deployfail_state(self, mock_deploy,
 
1115
                                                     mock_iwdi):
 
1116
        mock_iwdi.return_value = False
1204
1117
        self._start_service()
1205
1118
        mock_deploy.return_value = states.DEPLOYDONE
1206
1119
        node = obj_utils.create_test_node(self.context, driver='fake',
1217
1130
        # Verify reservation has been cleared.
1218
1131
        self.assertIsNone(node.reservation)
1219
1132
        mock_deploy.assert_called_once_with(mock.ANY)
 
1133
        mock_iwdi.assert_called_once_with(self.context, node.instance_info)
 
1134
        self.assertFalse(node.driver_internal_info['is_whole_disk_image'])
1220
1135
 
1221
1136
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
1222
 
    def test_do_node_deploy_rebuild_error_state(self, mock_deploy):
 
1137
    def test_do_node_deploy_rebuild_error_state(self, mock_deploy, mock_iwdi):
 
1138
        mock_iwdi.return_value = False
1223
1139
        self._start_service()
1224
1140
        mock_deploy.return_value = states.DEPLOYDONE
1225
1141
        node = obj_utils.create_test_node(self.context, driver='fake',
1236
1152
        # Verify reservation has been cleared.
1237
1153
        self.assertIsNone(node.reservation)
1238
1154
        mock_deploy.assert_called_once_with(mock.ANY)
 
1155
        mock_iwdi.assert_called_once_with(self.context, node.instance_info)
 
1156
        self.assertFalse(node.driver_internal_info['is_whole_disk_image'])
1239
1157
 
1240
 
    def test_do_node_deploy_rebuild_from_available_state(self):
 
1158
    def test_do_node_deploy_rebuild_from_available_state(self, mock_iwdi):
 
1159
        mock_iwdi.return_value = False
1241
1160
        self._start_service()
1242
1161
        # test node will not rebuild if state is AVAILABLE
1243
1162
        node = obj_utils.create_test_node(self.context, driver='fake',
1251
1170
        self.assertIsNone(node.last_error)
1252
1171
        # Verify reservation has been cleared.
1253
1172
        self.assertIsNone(node.reservation)
 
1173
        mock_iwdi.assert_called_once_with(self.context, node.instance_info)
 
1174
        self.assertNotIn('is_whole_disk_image', node.driver_internal_info)
 
1175
 
 
1176
    def test_do_node_deploy_worker_pool_full(self, mock_iwdi):
 
1177
        mock_iwdi.return_value = False
 
1178
        prv_state = states.AVAILABLE
 
1179
        tgt_prv_state = states.NOSTATE
 
1180
        node = obj_utils.create_test_node(self.context,
 
1181
                                          provision_state=prv_state,
 
1182
                                          target_provision_state=tgt_prv_state,
 
1183
                                          last_error=None, driver='fake')
 
1184
        self._start_service()
 
1185
 
 
1186
        with mock.patch.object(self.service, '_spawn_worker') as mock_spawn:
 
1187
            mock_spawn.side_effect = exception.NoFreeConductorWorker()
 
1188
 
 
1189
            exc = self.assertRaises(messaging.rpc.ExpectedException,
 
1190
                                    self.service.do_node_deploy,
 
1191
                                    self.context, node.uuid)
 
1192
            # Compare true exception hidden by @messaging.expected_exceptions
 
1193
            self.assertEqual(exception.NoFreeConductorWorker, exc.exc_info[0])
 
1194
            self.service._worker_pool.waitall()
 
1195
            node.refresh()
 
1196
            # Make sure things were rolled back
 
1197
            self.assertEqual(prv_state, node.provision_state)
 
1198
            self.assertEqual(tgt_prv_state, node.target_provision_state)
 
1199
            self.assertIsNotNone(node.last_error)
 
1200
            # Verify reservation has been cleared.
 
1201
            self.assertIsNone(node.reservation)
 
1202
            mock_iwdi.assert_called_once_with(self.context, node.instance_info)
 
1203
            self.assertFalse(node.driver_internal_info['is_whole_disk_image'])
 
1204
 
 
1205
 
 
1206
@_mock_record_keepalive
 
1207
class DoNodeDeployTearDownTestCase(_ServiceSetUpMixin,
 
1208
                                   tests_db_base.DbTestCase):
 
1209
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
 
1210
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.prepare')
 
1211
    def test__do_node_deploy_driver_raises_prepare_error(self, mock_prepare,
 
1212
                                                         mock_deploy):
 
1213
        self._start_service()
 
1214
        # test when driver.deploy.prepare raises an exception
 
1215
        mock_prepare.side_effect = exception.InstanceDeployFailure('test')
 
1216
        node = obj_utils.create_test_node(self.context, driver='fake',
 
1217
                                          provision_state=states.DEPLOYING,
 
1218
                                          target_provision_state=states.ACTIVE)
 
1219
        task = task_manager.TaskManager(self.context, node.uuid)
 
1220
 
 
1221
        self.assertRaises(exception.InstanceDeployFailure,
 
1222
                          manager.do_node_deploy, task,
 
1223
                          self.service.conductor.id)
 
1224
        node.refresh()
 
1225
        self.assertEqual(states.DEPLOYFAIL, node.provision_state)
 
1226
        # NOTE(deva): failing a deploy does not clear the target state
 
1227
        #             any longer. Instead, it is cleared when the instance
 
1228
        #             is deleted.
 
1229
        self.assertEqual(states.ACTIVE, node.target_provision_state)
 
1230
        self.assertIsNotNone(node.last_error)
 
1231
        self.assertTrue(mock_prepare.called)
 
1232
        self.assertFalse(mock_deploy.called)
 
1233
 
 
1234
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
 
1235
    def test__do_node_deploy_driver_raises_error(self, mock_deploy):
 
1236
        self._start_service()
 
1237
        # test when driver.deploy.deploy raises an exception
 
1238
        mock_deploy.side_effect = exception.InstanceDeployFailure('test')
 
1239
        node = obj_utils.create_test_node(self.context, driver='fake',
 
1240
                                          provision_state=states.DEPLOYING,
 
1241
                                          target_provision_state=states.ACTIVE)
 
1242
        task = task_manager.TaskManager(self.context, node.uuid)
 
1243
 
 
1244
        self.assertRaises(exception.InstanceDeployFailure,
 
1245
                          manager.do_node_deploy, task,
 
1246
                          self.service.conductor.id)
 
1247
        node.refresh()
 
1248
        self.assertEqual(states.DEPLOYFAIL, node.provision_state)
 
1249
        # NOTE(deva): failing a deploy does not clear the target state
 
1250
        #             any longer. Instead, it is cleared when the instance
 
1251
        #             is deleted.
 
1252
        self.assertEqual(states.ACTIVE, node.target_provision_state)
 
1253
        self.assertIsNotNone(node.last_error)
 
1254
        mock_deploy.assert_called_once_with(mock.ANY)
 
1255
 
 
1256
    @mock.patch.object(manager, '_store_configdrive')
 
1257
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
 
1258
    def test__do_node_deploy_ok(self, mock_deploy, mock_store):
 
1259
        self._start_service()
 
1260
        # test when driver.deploy.deploy returns DEPLOYDONE
 
1261
        mock_deploy.return_value = states.DEPLOYDONE
 
1262
        node = obj_utils.create_test_node(self.context, driver='fake',
 
1263
                                          provision_state=states.DEPLOYING,
 
1264
                                          target_provision_state=states.ACTIVE)
 
1265
        task = task_manager.TaskManager(self.context, node.uuid)
 
1266
 
 
1267
        manager.do_node_deploy(task, self.service.conductor.id)
 
1268
        node.refresh()
 
1269
        self.assertEqual(states.ACTIVE, node.provision_state)
 
1270
        self.assertEqual(states.NOSTATE, node.target_provision_state)
 
1271
        self.assertIsNone(node.last_error)
 
1272
        mock_deploy.assert_called_once_with(mock.ANY)
 
1273
        # assert _store_configdrive wasn't invoked
 
1274
        self.assertFalse(mock_store.called)
 
1275
 
 
1276
    @mock.patch.object(manager, '_store_configdrive')
 
1277
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
 
1278
    def test__do_node_deploy_ok_configdrive(self, mock_deploy, mock_store):
 
1279
        self._start_service()
 
1280
        # test when driver.deploy.deploy returns DEPLOYDONE
 
1281
        mock_deploy.return_value = states.DEPLOYDONE
 
1282
        node = obj_utils.create_test_node(self.context, driver='fake',
 
1283
                                          provision_state=states.DEPLOYING,
 
1284
                                          target_provision_state=states.ACTIVE)
 
1285
        task = task_manager.TaskManager(self.context, node.uuid)
 
1286
        configdrive = 'foo'
 
1287
 
 
1288
        manager.do_node_deploy(task, self.service.conductor.id,
 
1289
                               configdrive=configdrive)
 
1290
        node.refresh()
 
1291
        self.assertEqual(states.ACTIVE, node.provision_state)
 
1292
        self.assertEqual(states.NOSTATE, node.target_provision_state)
 
1293
        self.assertIsNone(node.last_error)
 
1294
        mock_deploy.assert_called_once_with(mock.ANY)
 
1295
        mock_store.assert_called_once_with(task.node, configdrive)
 
1296
 
 
1297
    @mock.patch.object(swift, 'SwiftAPI')
 
1298
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
 
1299
    def test__do_node_deploy_configdrive_swift_error(self, mock_deploy,
 
1300
                                                     mock_swift):
 
1301
        CONF.set_override('configdrive_use_swift', True, group='conductor')
 
1302
        self._start_service()
 
1303
        # test when driver.deploy.deploy returns DEPLOYDONE
 
1304
        mock_deploy.return_value = states.DEPLOYDONE
 
1305
        node = obj_utils.create_test_node(self.context, driver='fake',
 
1306
                                          provision_state=states.DEPLOYING,
 
1307
                                          target_provision_state=states.ACTIVE)
 
1308
        task = task_manager.TaskManager(self.context, node.uuid)
 
1309
 
 
1310
        mock_swift.side_effect = exception.SwiftOperationError('error')
 
1311
        self.assertRaises(exception.SwiftOperationError,
 
1312
                           manager.do_node_deploy, task,
 
1313
                           self.service.conductor.id,
 
1314
                           configdrive='fake config drive')
 
1315
        node.refresh()
 
1316
        self.assertEqual(states.DEPLOYFAIL, node.provision_state)
 
1317
        self.assertEqual(states.ACTIVE, node.target_provision_state)
 
1318
        self.assertIsNotNone(node.last_error)
 
1319
        self.assertFalse(mock_deploy.called)
 
1320
 
 
1321
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
 
1322
    def test__do_node_deploy_ok_2(self, mock_deploy):
 
1323
        # NOTE(rloo): a different way of testing for the same thing as in
 
1324
        # test__do_node_deploy_ok()
 
1325
        self._start_service()
 
1326
        # test when driver.deploy.deploy returns DEPLOYDONE
 
1327
        mock_deploy.return_value = states.DEPLOYDONE
 
1328
        node = obj_utils.create_test_node(self.context, driver='fake')
 
1329
        task = task_manager.TaskManager(self.context, node.uuid)
 
1330
        task.process_event('deploy')
 
1331
 
 
1332
        manager.do_node_deploy(task, self.service.conductor.id)
 
1333
        node.refresh()
 
1334
        self.assertEqual(states.ACTIVE, node.provision_state)
 
1335
        self.assertEqual(states.NOSTATE, node.target_provision_state)
 
1336
        self.assertIsNone(node.last_error)
 
1337
        mock_deploy.assert_called_once_with(mock.ANY)
1254
1338
 
1255
1339
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.clean_up')
1256
1340
    def test__check_deploy_timeouts(self, mock_cleanup):
1269
1353
        self.assertIsNotNone(node.last_error)
1270
1354
        mock_cleanup.assert_called_once_with(mock.ANY)
1271
1355
 
1272
 
    def test_do_node_deploy_worker_pool_full(self):
1273
 
        prv_state = states.AVAILABLE
1274
 
        tgt_prv_state = states.NOSTATE
1275
 
        node = obj_utils.create_test_node(self.context,
1276
 
                                          provision_state=prv_state,
1277
 
                                          target_provision_state=tgt_prv_state,
1278
 
                                          last_error=None, driver='fake')
1279
 
        self._start_service()
1280
 
 
1281
 
        with mock.patch.object(self.service, '_spawn_worker') as mock_spawn:
1282
 
            mock_spawn.side_effect = exception.NoFreeConductorWorker()
1283
 
 
1284
 
            exc = self.assertRaises(messaging.rpc.ExpectedException,
1285
 
                                    self.service.do_node_deploy,
1286
 
                                    self.context, node.uuid)
1287
 
            # Compare true exception hidden by @messaging.expected_exceptions
1288
 
            self.assertEqual(exception.NoFreeConductorWorker, exc.exc_info[0])
1289
 
            self.service._worker_pool.waitall()
1290
 
            node.refresh()
1291
 
            # Make sure things were rolled back
1292
 
            self.assertEqual(prv_state, node.provision_state)
1293
 
            self.assertEqual(tgt_prv_state, node.target_provision_state)
1294
 
            self.assertIsNotNone(node.last_error)
1295
 
            # Verify reservation has been cleared.
1296
 
            self.assertIsNone(node.reservation)
1297
 
 
1298
1356
    def test_do_node_tear_down_invalid_state(self):
1299
1357
        self._start_service()
1300
1358
        # test node.provision_state is incorrect for tear_down
1322
1380
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.tear_down')
1323
1381
    def test_do_node_tear_down_driver_raises_error(self, mock_tear_down):
1324
1382
        # test when driver.deploy.tear_down raises exception
1325
 
        node = obj_utils.create_test_node(self.context, driver='fake',
1326
 
                                      provision_state=states.DELETING,
1327
 
                                      target_provision_state=states.DELETED,
1328
 
                                      instance_info={'foo': 'bar'})
 
1383
        node = obj_utils.create_test_node(
 
1384
                self.context, driver='fake', provision_state=states.DELETING,
 
1385
                target_provision_state=states.AVAILABLE,
 
1386
                instance_info={'foo': 'bar'},
 
1387
                driver_internal_info={'is_whole_disk_image': False})
1329
1388
 
1330
1389
        task = task_manager.TaskManager(self.context, node.uuid)
1331
1390
        self._start_service()
1332
1391
        mock_tear_down.side_effect = exception.InstanceDeployFailure('test')
1333
1392
        self.assertRaises(exception.InstanceDeployFailure,
1334
 
                          manager.do_node_tear_down, task)
 
1393
                          self.service._do_node_tear_down, task)
1335
1394
        node.refresh()
1336
1395
        self.assertEqual(states.ERROR, node.provision_state)
1337
1396
        self.assertEqual(states.AVAILABLE, node.target_provision_state)
1340
1399
        self.assertEqual({}, node.instance_info)
1341
1400
        mock_tear_down.assert_called_once_with(mock.ANY)
1342
1401
 
 
1402
    @mock.patch('ironic.conductor.manager.ConductorManager._do_node_clean')
1343
1403
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.tear_down')
1344
 
    def test_do_node_tear_down_ok(self, mock_tear_down):
1345
 
        # test when driver.deploy.tear_down returns DELETED
1346
 
        node = obj_utils.create_test_node(self.context, driver='fake',
1347
 
                                      provision_state=states.DELETING,
1348
 
                                      target_provision_state=states.DELETED,
1349
 
                                      instance_info={'foo': 'bar'})
 
1404
    def test__do_node_tear_down_ok(self, mock_tear_down, mock_clean):
 
1405
        # test when driver.deploy.tear_down succeeds
 
1406
        node = obj_utils.create_test_node(
 
1407
                self.context, driver='fake', provision_state=states.DELETING,
 
1408
                target_provision_state=states.AVAILABLE,
 
1409
                instance_info={'foo': 'bar'},
 
1410
                driver_internal_info={'is_whole_disk_image': False})
1350
1411
 
1351
1412
        task = task_manager.TaskManager(self.context, node.uuid)
1352
1413
        self._start_service()
1353
 
        mock_tear_down.return_value = states.DELETED
1354
 
        manager.do_node_tear_down(task)
 
1414
        self.service._do_node_tear_down(task)
1355
1415
        node.refresh()
1356
 
        self.assertEqual(states.AVAILABLE, node.provision_state)
1357
 
        self.assertEqual(states.NOSTATE, node.target_provision_state)
 
1416
        # Node will be moved to AVAILABLE after cleaning, not tested here
 
1417
        self.assertEqual(states.CLEANING, node.provision_state)
 
1418
        self.assertEqual(states.AVAILABLE, node.target_provision_state)
1358
1419
        self.assertIsNone(node.last_error)
1359
1420
        self.assertEqual({}, node.instance_info)
1360
1421
        mock_tear_down.assert_called_once_with(mock.ANY)
 
1422
        mock_clean.assert_called_once_with(mock.ANY)
1361
1423
 
 
1424
    @mock.patch('ironic.conductor.manager.ConductorManager._do_node_clean')
1362
1425
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.tear_down')
1363
 
    def _test_do_node_tear_down_from_state(self, init_state, mock_tear_down):
1364
 
        mock_tear_down.return_value = states.DELETED
1365
 
        node = obj_utils.create_test_node(self.context, driver='fake',
1366
 
                                      uuid=ironic_utils.generate_uuid(),
1367
 
                                      provision_state=init_state,
1368
 
                                      target_provision_state=states.NOSTATE)
 
1426
    def _test_do_node_tear_down_from_state(self, init_state, mock_tear_down,
 
1427
                                           mock_clean):
 
1428
        node = obj_utils.create_test_node(
 
1429
                self.context, driver='fake', uuid=uuidutils.generate_uuid(),
 
1430
                provision_state=init_state,
 
1431
                target_provision_state=states.AVAILABLE,
 
1432
                driver_internal_info={'is_whole_disk_image': False})
1369
1433
 
1370
1434
        self.service.do_node_tear_down(self.context, node.uuid)
1371
1435
        self.service._worker_pool.waitall()
1372
1436
        node.refresh()
1373
 
        self.assertEqual(states.AVAILABLE, node.provision_state)
1374
 
        self.assertEqual(states.NOSTATE, node.target_provision_state)
 
1437
        # Node will be moved to AVAILABLE after cleaning, not tested here
 
1438
        self.assertEqual(states.CLEANING, node.provision_state)
 
1439
        self.assertEqual(states.AVAILABLE, node.target_provision_state)
1375
1440
        self.assertIsNone(node.last_error)
1376
1441
        self.assertEqual({}, node.instance_info)
1377
1442
        mock_tear_down.assert_called_once_with(mock.ANY)
 
1443
        mock_clean.assert_called_once_with(mock.ANY)
1378
1444
 
1379
 
    def test_do_node_tear_down_from_valid_states(self):
 
1445
    def test__do_node_tear_down_from_valid_states(self):
1380
1446
        valid_states = [states.ACTIVE, states.DEPLOYWAIT, states.DEPLOYFAIL,
1381
1447
                        states.ERROR]
1382
1448
        self._start_service()
1395
1461
        prv_state = states.ACTIVE
1396
1462
        tgt_prv_state = states.NOSTATE
1397
1463
        fake_instance_info = {'foo': 'bar'}
1398
 
        node = obj_utils.create_test_node(self.context, driver='fake',
1399
 
                                          provision_state=prv_state,
1400
 
                                          target_provision_state=tgt_prv_state,
1401
 
                                          instance_info=fake_instance_info,
1402
 
                                          last_error=None)
 
1464
        driver_internal_info = {'is_whole_disk_image': False}
 
1465
        node = obj_utils.create_test_node(
 
1466
                self.context, driver='fake', provision_state=prv_state,
 
1467
                target_provision_state=tgt_prv_state,
 
1468
                instance_info=fake_instance_info,
 
1469
                driver_internal_info=driver_internal_info, last_error=None)
1403
1470
        self._start_service()
1404
1471
 
1405
1472
        mock_spawn.side_effect = exception.NoFreeConductorWorker()
1411
1478
        self.assertEqual(exception.NoFreeConductorWorker, exc.exc_info[0])
1412
1479
        self.service._worker_pool.waitall()
1413
1480
        node.refresh()
1414
 
        # Assert instance_info was not touched
 
1481
        # Assert instance_info/driver_internal_info was not touched
1415
1482
        self.assertEqual(fake_instance_info, node.instance_info)
1416
 
        # Make sure things were rolled back
1417
 
        self.assertEqual(prv_state, node.provision_state)
1418
 
        self.assertEqual(tgt_prv_state, node.target_provision_state)
1419
 
        self.assertIsNotNone(node.last_error)
1420
 
        # Verify reservation has been cleared.
1421
 
        self.assertIsNone(node.reservation)
1422
 
 
1423
 
 
1424
 
@_mock_record_keepalive
1425
 
class MiscTestCase(_ServiceSetUpMixin, tests_db_base.DbTestCase):
 
1483
        self.assertEqual(driver_internal_info, node.driver_internal_info)
 
1484
        # Make sure things were rolled back
 
1485
        self.assertEqual(prv_state, node.provision_state)
 
1486
        self.assertEqual(tgt_prv_state, node.target_provision_state)
 
1487
        self.assertIsNotNone(node.last_error)
 
1488
        # Verify reservation has been cleared.
 
1489
        self.assertIsNone(node.reservation)
 
1490
 
 
1491
    @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker')
 
1492
    def test_do_provisioning_action_worker_pool_full(self, mock_spawn):
 
1493
        prv_state = states.MANAGEABLE
 
1494
        tgt_prv_state = states.CLEANING
 
1495
        node = obj_utils.create_test_node(self.context, driver='fake',
 
1496
                                          provision_state=prv_state,
 
1497
                                          target_provision_state=tgt_prv_state,
 
1498
                                          last_error=None)
 
1499
        self._start_service()
 
1500
 
 
1501
        mock_spawn.side_effect = exception.NoFreeConductorWorker()
 
1502
 
 
1503
        exc = self.assertRaises(messaging.rpc.ExpectedException,
 
1504
                                self.service.do_provisioning_action,
 
1505
                                self.context, node.uuid, 'provide')
 
1506
        # Compare true exception hidden by @messaging.expected_exceptions
 
1507
        self.assertEqual(exception.NoFreeConductorWorker, exc.exc_info[0])
 
1508
        self.service._worker_pool.waitall()
 
1509
        node.refresh()
 
1510
        # Make sure things were rolled back
 
1511
        self.assertEqual(prv_state, node.provision_state)
 
1512
        self.assertEqual(tgt_prv_state, node.target_provision_state)
 
1513
        self.assertIsNotNone(node.last_error)
 
1514
        # Verify reservation has been cleared.
 
1515
        self.assertIsNone(node.reservation)
 
1516
 
 
1517
    @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker')
 
1518
    def test_do_provision_action_provide(self, mock_spawn):
 
1519
        # test when a node is cleaned going from manageable to available
 
1520
        node = obj_utils.create_test_node(
 
1521
            self.context, driver='fake',
 
1522
            provision_state=states.MANAGEABLE,
 
1523
            target_provision_state=states.AVAILABLE)
 
1524
 
 
1525
        self._start_service()
 
1526
        self.service.do_provisioning_action(self.context, node.uuid, 'provide')
 
1527
        node.refresh()
 
1528
        # Node will be moved to AVAILABLE after cleaning, not tested here
 
1529
        self.assertEqual(states.CLEANING, node.provision_state)
 
1530
        self.assertEqual(states.AVAILABLE, node.target_provision_state)
 
1531
        self.assertIsNone(node.last_error)
 
1532
        mock_spawn.assert_called_with(self.service._do_node_clean, mock.ANY)
 
1533
 
 
1534
 
 
1535
@_mock_record_keepalive
 
1536
class DoNodeCleanTestCase(_ServiceSetUpMixin, tests_db_base.DbTestCase):
 
1537
    def setUp(self):
 
1538
        super(DoNodeCleanTestCase, self).setUp()
 
1539
        self.config(clean_nodes=True, group='conductor')
 
1540
        self.power_update = {
 
1541
            'step': 'update_firmware', 'priority': 10, 'interface': 'power'}
 
1542
        self.deploy_update = {
 
1543
            'step': 'update_firmware', 'priority': 10, 'interface': 'deploy'}
 
1544
        self.deploy_erase = {
 
1545
            'step': 'erase_disks', 'priority': 20, 'interface': 'deploy'}
 
1546
        # Cleaning should be executed in this order
 
1547
        self.clean_steps = [self.deploy_erase, self.power_update,
 
1548
                            self.deploy_update]
 
1549
        # Zap step
 
1550
        self.deploy_raid = {
 
1551
            'step': 'build_raid', 'priority': 0, 'interface': 'deploy'}
 
1552
 
 
1553
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.get_clean_steps')
 
1554
    @mock.patch('ironic.drivers.modules.fake.FakePower.get_clean_steps')
 
1555
    def test__get_cleaning_steps(self, mock_power_steps, mock_deploy_steps):
 
1556
        # Test getting cleaning steps, with one driver returning None, two
 
1557
        # conflicting priorities, and asserting they are ordered properly.
 
1558
        node = obj_utils.create_test_node(
 
1559
            self.context, driver='fake',
 
1560
            provision_state=states.CLEANING,
 
1561
            target_provision_state=states.AVAILABLE)
 
1562
 
 
1563
        mock_power_steps.return_value = [self.power_update]
 
1564
        mock_deploy_steps.return_value = [self.deploy_erase,
 
1565
                                          self.deploy_update]
 
1566
 
 
1567
        with task_manager.acquire(
 
1568
                self.context, node['id'], shared=False) as task:
 
1569
            steps = manager._get_cleaning_steps(task, enabled=False)
 
1570
 
 
1571
        self.assertEqual(self.clean_steps, steps)
 
1572
 
 
1573
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.get_clean_steps')
 
1574
    @mock.patch('ironic.drivers.modules.fake.FakePower.get_clean_steps')
 
1575
    def test__get_cleaning_steps_only_enabled(self, mock_power_steps,
 
1576
                                              mock_deploy_steps):
 
1577
        # Test getting only cleaning steps, with one driver returning None, two
 
1578
        # conflicting priorities, and asserting they are ordered properly.
 
1579
        # Should discard zap step
 
1580
        node = obj_utils.create_test_node(
 
1581
            self.context, driver='fake',
 
1582
            provision_state=states.CLEANING,
 
1583
            target_provision_state=states.AVAILABLE)
 
1584
 
 
1585
        mock_power_steps.return_value = [self.power_update]
 
1586
        mock_deploy_steps.return_value = [self.deploy_erase,
 
1587
                                          self.deploy_update,
 
1588
                                          self.deploy_raid]
 
1589
 
 
1590
        with task_manager.acquire(
 
1591
                self.context, node['id'], shared=True) as task:
 
1592
            steps = manager._get_cleaning_steps(task, enabled=True)
 
1593
 
 
1594
        self.assertEqual(self.clean_steps, steps)
 
1595
 
 
1596
    @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker')
 
1597
    def test_continue_node_clean_worker_pool_full(self, mock_spawn):
 
1598
        # Test the appropriate exception is raised if the worker pool is full
 
1599
        prv_state = states.CLEANING
 
1600
        tgt_prv_state = states.AVAILABLE
 
1601
        node = obj_utils.create_test_node(self.context, driver='fake',
 
1602
                                          provision_state=prv_state,
 
1603
                                          target_provision_state=tgt_prv_state,
 
1604
                                          last_error=None)
 
1605
        self._start_service()
 
1606
 
 
1607
        mock_spawn.side_effect = exception.NoFreeConductorWorker()
 
1608
 
 
1609
        exc = self.assertRaises(messaging.rpc.ExpectedException,
 
1610
                                self.service.continue_node_clean,
 
1611
                                self.context, node.uuid)
 
1612
        # Compare true exception hidden by @messaging.expected_exceptions
 
1613
        self.assertEqual(exception.NoFreeConductorWorker, exc.exc_info[0])
 
1614
 
 
1615
        self.service._worker_pool.waitall()
 
1616
        node.refresh()
 
1617
        # Make sure things were rolled back
 
1618
        self.assertEqual(prv_state, node.provision_state)
 
1619
        self.assertEqual(tgt_prv_state, node.target_provision_state)
 
1620
 
 
1621
    @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker')
 
1622
    def test_continue_node_clean_wrong_state(self, mock_spawn):
 
1623
        # Test the appropriate exception is raised if node isn't already
 
1624
        # in CLEANING state
 
1625
        prv_state = states.DELETING
 
1626
        tgt_prv_state = states.AVAILABLE
 
1627
        node = obj_utils.create_test_node(self.context, driver='fake',
 
1628
                                          provision_state=prv_state,
 
1629
                                          target_provision_state=tgt_prv_state,
 
1630
                                          last_error=None)
 
1631
        self._start_service()
 
1632
 
 
1633
        exc = self.assertRaises(messaging.rpc.ExpectedException,
 
1634
                                self.service.continue_node_clean,
 
1635
                                self.context, node.uuid)
 
1636
        # Compare true exception hidden by @messaging.expected_exceptions
 
1637
        self.assertEqual(exception.InvalidStateRequested, exc.exc_info[0])
 
1638
 
 
1639
        self.service._worker_pool.waitall()
 
1640
        node.refresh()
 
1641
        # Make sure things were rolled back
 
1642
        self.assertEqual(prv_state, node.provision_state)
 
1643
        self.assertEqual(tgt_prv_state, node.target_provision_state)
 
1644
        # Verify reservation has been cleared.
 
1645
        self.assertIsNone(node.reservation)
 
1646
 
 
1647
    @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker')
 
1648
    def test_continue_node_clean(self, mock_spawn):
 
1649
        # test a node can continue cleaning via RPC
 
1650
        prv_state = states.CLEANING
 
1651
        tgt_prv_state = states.AVAILABLE
 
1652
        driver_info = {'clean_steps': self.clean_steps}
 
1653
        node = obj_utils.create_test_node(self.context, driver='fake',
 
1654
                                          provision_state=prv_state,
 
1655
                                          target_provision_state=tgt_prv_state,
 
1656
                                          last_error=None,
 
1657
                                          driver_internal_info=driver_info,
 
1658
                                          clean_step=self.clean_steps[1])
 
1659
        self._start_service()
 
1660
        self.service.continue_node_clean(self.context, node.uuid)
 
1661
        self.service._worker_pool.waitall()
 
1662
        node.refresh()
 
1663
        mock_spawn.assert_called_with(self.service._do_next_clean_step,
 
1664
                                      mock.ANY, self.clean_steps,
 
1665
                                      self.clean_steps[1])
 
1666
 
 
1667
    @mock.patch('ironic.drivers.modules.fake.FakePower.validate')
 
1668
    def test__do_node_clean_validate_fail(self, mock_validate):
 
1669
        # InvalidParameterValue should be cause node to go to CLEANFAIL
 
1670
        self.config(clean_nodes=True, group='conductor')
 
1671
        mock_validate.side_effect = exception.InvalidParameterValue('error')
 
1672
        node = obj_utils.create_test_node(
 
1673
            self.context, driver='fake',
 
1674
            provision_state=states.CLEANING,
 
1675
            target_provision_state=states.AVAILABLE)
 
1676
        with task_manager.acquire(
 
1677
                self.context, node['id'], shared=False) as task:
 
1678
            self.service._do_node_clean(task)
 
1679
        node.refresh()
 
1680
        self.assertEqual(states.CLEANFAIL, node.provision_state)
 
1681
 
 
1682
    @mock.patch('ironic.drivers.modules.fake.FakePower.validate')
 
1683
    def test__do_node_clean_disabled(self, mock_validate):
 
1684
        self.config(clean_nodes=False, group='conductor')
 
1685
        node = obj_utils.create_test_node(
 
1686
            self.context, driver='fake',
 
1687
            provision_state=states.CLEANING,
 
1688
            target_provision_state=states.AVAILABLE,
 
1689
            last_error=None)
 
1690
 
 
1691
        self._start_service()
 
1692
        with task_manager.acquire(
 
1693
                self.context, node['id'], shared=False) as task:
 
1694
            self.service._do_node_clean(task)
 
1695
        self.service._worker_pool.waitall()
 
1696
        node.refresh()
 
1697
 
 
1698
        # Assert that the node was moved to available without cleaning
 
1699
        mock_validate.assert_not_called()
 
1700
        self.assertEqual(states.AVAILABLE, node.provision_state)
 
1701
        self.assertEqual(states.NOSTATE, node.target_provision_state)
 
1702
        self.assertEqual({}, node.clean_step)
 
1703
        self.assertIsNone(node.driver_internal_info.get('clean_steps'))
 
1704
 
 
1705
    @mock.patch('ironic.conductor.manager.set_node_cleaning_steps')
 
1706
    @mock.patch('ironic.conductor.manager.ConductorManager.'
 
1707
                '_do_next_clean_step')
 
1708
    @mock.patch('ironic.drivers.modules.fake.FakePower.validate')
 
1709
    def test__do_node_clean(self, mock_validate, mock_next_step, mock_steps):
 
1710
        node = obj_utils.create_test_node(
 
1711
            self.context, driver='fake',
 
1712
            provision_state=states.CLEANING,
 
1713
            target_provision_state=states.AVAILABLE,
 
1714
            last_error=None,
 
1715
            power_state=states.POWER_OFF,
 
1716
            driver_internal_info={'clean_steps': []})
 
1717
 
 
1718
        mock_steps.return_value = self.clean_steps
 
1719
 
 
1720
        self._start_service()
 
1721
        with task_manager.acquire(
 
1722
                self.context, node['id'], shared=False) as task:
 
1723
            self.service._do_node_clean(task)
 
1724
 
 
1725
        self.service._worker_pool.waitall()
 
1726
        node.refresh()
 
1727
 
 
1728
        mock_validate.assert_called_once()
 
1729
        mock_next_step.assert_called_once_with(mock.ANY, [], {})
 
1730
        mock_steps.assert_called_once()
 
1731
 
 
1732
        # Check that state didn't change
 
1733
        self.assertEqual(states.CLEANING, node.provision_state)
 
1734
        self.assertEqual(states.AVAILABLE, node.target_provision_state)
 
1735
 
 
1736
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.execute_clean_step')
 
1737
    def test__do_next_clean_step_first_step_async(self, mock_execute):
 
1738
        # Execute the first async clean step on a node
 
1739
        node = obj_utils.create_test_node(
 
1740
            self.context, driver='fake',
 
1741
            provision_state=states.CLEANING,
 
1742
            target_provision_state=states.AVAILABLE,
 
1743
            last_error=None,
 
1744
            clean_step={})
 
1745
        mock_execute.return_value = states.CLEANING
 
1746
 
 
1747
        self._start_service()
 
1748
 
 
1749
        with task_manager.acquire(
 
1750
                self.context, node['id'], shared=False) as task:
 
1751
            self.service._do_next_clean_step(task, self.clean_steps,
 
1752
                                             node.clean_step)
 
1753
 
 
1754
        self.service._worker_pool.waitall()
 
1755
        node.refresh()
 
1756
 
 
1757
        self.assertEqual(states.CLEANING, node.provision_state)
 
1758
        self.assertEqual(self.clean_steps[0], node.clean_step)
 
1759
        mock_execute.assert_called_once_with(mock.ANY, self.clean_steps[0])
 
1760
 
 
1761
    @mock.patch('ironic.drivers.modules.fake.FakePower.execute_clean_step')
 
1762
    def test__do_next_clean_step_continue_from_last_step(self, mock_execute):
 
1763
        # Resume an in-progress cleaning after the first async step
 
1764
        node = obj_utils.create_test_node(
 
1765
            self.context, driver='fake',
 
1766
            provision_state=states.CLEANING,
 
1767
            target_provision_state=states.AVAILABLE,
 
1768
            last_error=None,
 
1769
            clean_step=self.clean_steps[0])
 
1770
        mock_execute.return_value = states.CLEANING
 
1771
 
 
1772
        self._start_service()
 
1773
 
 
1774
        with task_manager.acquire(
 
1775
                self.context, node['id'], shared=False) as task:
 
1776
            self.service._do_next_clean_step(task, self.clean_steps,
 
1777
                                             self.clean_steps[0])
 
1778
 
 
1779
        self.service._worker_pool.waitall()
 
1780
        node.refresh()
 
1781
 
 
1782
        self.assertEqual(states.CLEANING, node.provision_state)
 
1783
        self.assertEqual(self.clean_steps[1], node.clean_step)
 
1784
        mock_execute.assert_called_once_with(mock.ANY, self.clean_steps[1])
 
1785
 
 
1786
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.execute_clean_step')
 
1787
    def test__do_next_clean_step_last_step_noop(self, mock_execute):
 
1788
        # Resume where last_step is the last cleaning step, should be noop
 
1789
        node = obj_utils.create_test_node(
 
1790
            self.context, driver='fake',
 
1791
            provision_state=states.CLEANING,
 
1792
            target_provision_state=states.AVAILABLE,
 
1793
            last_error=None,
 
1794
            clean_step=self.clean_steps[-1])
 
1795
 
 
1796
        self._start_service()
 
1797
 
 
1798
        with task_manager.acquire(
 
1799
                self.context, node['id'], shared=False) as task:
 
1800
            self.service._do_next_clean_step(
 
1801
                task, self.clean_steps, self.clean_steps[-1])
 
1802
 
 
1803
        self.service._worker_pool.waitall()
 
1804
        node.refresh()
 
1805
 
 
1806
        # Cleaning should be complete without calling additional steps
 
1807
        self.assertEqual(states.AVAILABLE, node.provision_state)
 
1808
        self.assertEqual({}, node.clean_step)
 
1809
        mock_execute.assert_not_called()
 
1810
 
 
1811
    @mock.patch('ironic.drivers.modules.fake.FakePower.execute_clean_step')
 
1812
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.execute_clean_step')
 
1813
    def test__do_next_clean_step_all(self, mock_deploy_execute,
 
1814
                                     mock_power_execute):
 
1815
        # Run all steps from start to finish (all synchronous)
 
1816
        node = obj_utils.create_test_node(
 
1817
            self.context, driver='fake',
 
1818
            provision_state=states.CLEANING,
 
1819
            target_provision_state=states.AVAILABLE,
 
1820
            last_error=None,
 
1821
            clean_step={})
 
1822
        mock_deploy_execute.return_value = None
 
1823
        mock_power_execute.return_value = None
 
1824
 
 
1825
        self._start_service()
 
1826
 
 
1827
        with task_manager.acquire(
 
1828
                self.context, node['id'], shared=False) as task:
 
1829
            self.service._do_next_clean_step(
 
1830
                task, self.clean_steps, node.clean_step)
 
1831
 
 
1832
        self.service._worker_pool.waitall()
 
1833
        node.refresh()
 
1834
 
 
1835
        # Cleaning should be complete
 
1836
        self.assertEqual(states.AVAILABLE, node.provision_state)
 
1837
        self.assertEqual({}, node.clean_step)
 
1838
        mock_power_execute.assert_called_once_with(mock.ANY,
 
1839
                                                   self.clean_steps[1])
 
1840
        mock_deploy_execute.assert_has_calls = [
 
1841
            mock.call(self.clean_steps[0]),
 
1842
            mock.call(self.clean_steps[2])
 
1843
        ]
 
1844
 
 
1845
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.execute_clean_step')
 
1846
    def test__do_next_clean_step_bad_last_step(self, mock_execute):
 
1847
        # Make sure cleaning fails if last_step is incorrect
 
1848
        node = obj_utils.create_test_node(
 
1849
            self.context, driver='fake',
 
1850
            provision_state=states.CLEANING,
 
1851
            target_provision_state=states.AVAILABLE,
 
1852
            last_error=None,
 
1853
            clean_step={})
 
1854
 
 
1855
        self._start_service()
 
1856
 
 
1857
        with task_manager.acquire(
 
1858
                self.context, node['id'], shared=False) as task:
 
1859
            self.service._do_next_clean_step(
 
1860
                task, self.clean_steps, {'interface': 'deploy',
 
1861
                                         'step': 'not_a_clean_step',
 
1862
                                         'priority': 100})
 
1863
 
 
1864
        self.service._worker_pool.waitall()
 
1865
        node.refresh()
 
1866
 
 
1867
        # Node should have failed without executing anything
 
1868
        self.assertEqual(states.CLEANFAIL, node.provision_state)
 
1869
        self.assertEqual({}, node.clean_step)
 
1870
        self.assertIsNotNone(node.last_error)
 
1871
        self.assertTrue(node.maintenance)
 
1872
        mock_execute.assert_not_called()
 
1873
 
 
1874
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.execute_clean_step')
 
1875
    def test__do_next_clean_step_fail(self, mock_execute):
 
1876
        # When a clean step fails, go to CLEANFAIL
 
1877
        node = obj_utils.create_test_node(
 
1878
            self.context, driver='fake',
 
1879
            provision_state=states.CLEANING,
 
1880
            target_provision_state=states.AVAILABLE,
 
1881
            last_error=None,
 
1882
            clean_step={})
 
1883
        mock_execute.side_effect = Exception()
 
1884
 
 
1885
        self._start_service()
 
1886
 
 
1887
        with task_manager.acquire(
 
1888
                self.context, node['id'], shared=False) as task:
 
1889
            self.service._do_next_clean_step(
 
1890
                task, self.clean_steps, node.clean_step)
 
1891
 
 
1892
        self.service._worker_pool.waitall()
 
1893
        node.refresh()
 
1894
 
 
1895
        # Make sure we go to CLEANFAIL, clear clean_steps
 
1896
        self.assertEqual(states.CLEANFAIL, node.provision_state)
 
1897
        self.assertEqual({}, node.clean_step)
 
1898
        self.assertIsNotNone(node.last_error)
 
1899
        self.assertTrue(node.maintenance)
 
1900
        mock_execute.assert_not_called()
 
1901
        mock_execute.assert_called_once_with(mock.ANY, self.clean_steps[0])
 
1902
 
 
1903
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.execute_clean_step')
 
1904
    def test__do_next_clean_step_no_steps(self, mock_execute):
 
1905
        # Resume where there are no steps, should be a noop
 
1906
        node = obj_utils.create_test_node(
 
1907
            self.context, driver='fake',
 
1908
            provision_state=states.CLEANING,
 
1909
            target_provision_state=states.AVAILABLE,
 
1910
            last_error=None,
 
1911
            clean_step={})
 
1912
 
 
1913
        self._start_service()
 
1914
 
 
1915
        with task_manager.acquire(
 
1916
                self.context, node['id'], shared=False) as task:
 
1917
            self.service._do_next_clean_step(
 
1918
                task, [], node.clean_step)
 
1919
 
 
1920
        self.service._worker_pool.waitall()
 
1921
        node.refresh()
 
1922
 
 
1923
        # Cleaning should be complete without calling additional steps
 
1924
        self.assertEqual(states.AVAILABLE, node.provision_state)
 
1925
        self.assertEqual({}, node.clean_step)
 
1926
        mock_execute.assert_not_called()
 
1927
 
 
1928
    @mock.patch('ironic.drivers.modules.fake.FakePower.execute_clean_step')
 
1929
    @mock.patch('ironic.drivers.modules.fake.FakeDeploy.execute_clean_step')
 
1930
    def test__do_next_clean_step_bad_step_return_value(
 
1931
            self, deploy_exec_mock, power_exec_mock):
 
1932
        # When a clean step fails, go to CLEANFAIL
 
1933
        node = obj_utils.create_test_node(
 
1934
            self.context, driver='fake',
 
1935
            provision_state=states.CLEANING,
 
1936
            target_provision_state=states.AVAILABLE,
 
1937
            last_error=None,
 
1938
            clean_step={})
 
1939
        deploy_exec_mock.return_value = "foo"
 
1940
 
 
1941
        self._start_service()
 
1942
 
 
1943
        with task_manager.acquire(
 
1944
                self.context, node['id'], shared=False) as task:
 
1945
            self.service._do_next_clean_step(
 
1946
                task, self.clean_steps, node.clean_step)
 
1947
 
 
1948
        self.service._worker_pool.waitall()
 
1949
        node.refresh()
 
1950
 
 
1951
        # Make sure we go to CLEANFAIL, clear clean_steps
 
1952
        self.assertEqual(states.CLEANFAIL, node.provision_state)
 
1953
        self.assertEqual({}, node.clean_step)
 
1954
        self.assertIsNotNone(node.last_error)
 
1955
        self.assertTrue(node.maintenance)
 
1956
        deploy_exec_mock.assert_called_once_with(mock.ANY,
 
1957
                                                 self.clean_steps[0])
 
1958
        # Make sure we don't execute any other step and return
 
1959
        self.assertFalse(power_exec_mock.called)
 
1960
 
 
1961
    @mock.patch('ironic.conductor.manager._get_cleaning_steps')
 
1962
    def test_set_node_cleaning_steps(self, mock_steps):
 
1963
        mock_steps.return_value = self.clean_steps
 
1964
 
 
1965
        node = obj_utils.create_test_node(
 
1966
            self.context, driver='fake',
 
1967
            provision_state=states.CLEANING,
 
1968
            target_provision_state=states.AVAILABLE,
 
1969
            last_error=None,
 
1970
            clean_step=None)
 
1971
 
 
1972
        with task_manager.acquire(
 
1973
                self.context, node['id'], shared=False) as task:
 
1974
            manager.set_node_cleaning_steps(task)
 
1975
            node.refresh()
 
1976
            self.assertEqual(self.clean_steps,
 
1977
                             task.node.driver_internal_info['clean_steps'])
 
1978
            self.assertEqual({}, node.clean_step)
 
1979
 
 
1980
 
 
1981
@_mock_record_keepalive
 
1982
class MiscTestCase(_ServiceSetUpMixin, _CommonMixIn, tests_db_base.DbTestCase):
1426
1983
    def test_get_driver_known(self):
1427
1984
        self._start_service()
1428
1985
        driver = self.service._get_driver('fake')
1439
1996
        self.assertTrue(self.service._mapped_to_this_conductor(n['uuid'],
1440
1997
                                                               'fake'))
1441
1998
        self.assertFalse(self.service._mapped_to_this_conductor(n['uuid'],
 
1999
 
1442
2000
                                                                'otherdriver'))
1443
2001
 
1444
 
    def test_validate_driver_interfaces(self):
 
2002
    @mock.patch.object(images, 'is_whole_disk_image')
 
2003
    def test_validate_driver_interfaces(self, mock_iwdi):
 
2004
        mock_iwdi.return_value = False
1445
2005
        node = obj_utils.create_test_node(self.context, driver='fake')
1446
2006
        ret = self.service.validate_driver_interfaces(self.context,
1447
2007
                                                      node.uuid)
1448
2008
        expected = {'console': {'result': True},
1449
2009
                    'power': {'result': True},
 
2010
                    'inspect': {'result': True},
1450
2011
                    'management': {'result': True},
1451
2012
                    'deploy': {'result': True}}
1452
2013
        self.assertEqual(expected, ret)
 
2014
        mock_iwdi.assert_called_once_with(self.context, node.instance_info)
1453
2015
 
1454
 
    def test_validate_driver_interfaces_validation_fail(self):
 
2016
    @mock.patch.object(images, 'is_whole_disk_image')
 
2017
    def test_validate_driver_interfaces_validation_fail(self, mock_iwdi):
 
2018
        mock_iwdi.return_value = False
1455
2019
        node = obj_utils.create_test_node(self.context, driver='fake')
1456
2020
        with mock.patch(
1457
2021
                 'ironic.drivers.modules.fake.FakeDeploy.validate'
1462
2026
                                                          node.uuid)
1463
2027
            self.assertFalse(ret['deploy']['result'])
1464
2028
            self.assertEqual(reason, ret['deploy']['reason'])
 
2029
            mock_iwdi.assert_called_once_with(self.context, node.instance_info)
 
2030
 
 
2031
    @mock.patch.object(manager.ConductorManager, '_mapped_to_this_conductor')
 
2032
    @mock.patch.object(dbapi.IMPL, 'get_nodeinfo_list')
 
2033
    def test_iter_nodes(self, mock_nodeinfo_list, mock_mapped):
 
2034
        self._start_service()
 
2035
        self.columns = ['uuid', 'driver', 'id']
 
2036
        nodes = [self._create_node(id=i, driver='fake') for i in range(2)]
 
2037
        mock_nodeinfo_list.return_value = self._get_nodeinfo_list_response(
 
2038
            nodes)
 
2039
        mock_mapped.side_effect = [True, False]
 
2040
 
 
2041
        result = list(self.service.iter_nodes(fields=['id'],
 
2042
                                              filters=mock.sentinel.filters))
 
2043
        self.assertEqual([(nodes[0].uuid, 'fake', 0)], result)
 
2044
        mock_nodeinfo_list.assert_called_once_with(
 
2045
            columns=self.columns, filters=mock.sentinel.filters)
1465
2046
 
1466
2047
 
1467
2048
@_mock_record_keepalive
1675
2256
                                          power_state=states.POWER_OFF)
1676
2257
        self.service.destroy_node(self.context, node.uuid)
1677
2258
 
 
2259
    def test_destroy_node_console_enabled(self):
 
2260
        self._start_service()
 
2261
        node = obj_utils.create_test_node(self.context, driver='fake',
 
2262
                                          console_enabled=True)
 
2263
        with mock.patch.object(self.driver.console,
 
2264
                               'stop_console') as mock_sc:
 
2265
            self.service.destroy_node(self.context, node.uuid)
 
2266
            mock_sc.assert_called_once_with(mock.ANY)
 
2267
            self.assertRaises(exception.NodeNotFound,
 
2268
                              self.dbapi.get_node_by_uuid,
 
2269
                              node.uuid)
 
2270
 
1678
2271
 
1679
2272
@_mock_record_keepalive
1680
2273
class UpdatePortTestCase(_ServiceSetUpMixin, tests_db_base.DbTestCase):
1799
2392
    @mock.patch.object(dbapi.IMPL, 'get_nodeinfo_list')
1800
2393
    @mock.patch.object(task_manager, 'acquire')
1801
2394
    def test___send_sensor_data_disabled(self, acquire_mock,
1802
 
        get_nodeinfo_list_mock, _mapped_to_this_conductor_mock):
 
2395
                                         get_nodeinfo_list_mock,
 
2396
                                         _mapped_to_this_conductor_mock):
1803
2397
        node = obj_utils.create_test_node(self.context,
1804
2398
                                          driver='fake')
1805
2399
        self._start_service()
2147
2741
        self.service.dbapi = self.dbapi
2148
2742
        self.node = self._create_node()
2149
2743
        self.filters = {'reserved': False, 'maintenance': False}
2150
 
        self.columns = ['id', 'uuid', 'driver']
 
2744
        self.columns = ['uuid', 'driver', 'id']
2151
2745
 
2152
2746
    def test_node_not_mapped(self, get_nodeinfo_mock, get_node_mock,
2153
2747
                             mapped_mock, acquire_mock, sync_mock):
2349
2943
        mapped_map = {}
2350
2944
        for i in range(1, 12):
2351
2945
            attrs = {'id': i,
2352
 
                     'uuid': ironic_utils.generate_uuid()}
 
2946
                     'uuid': uuidutils.generate_uuid()}
2353
2947
            if i == 3:
2354
2948
                attrs['provision_state'] = states.DEPLOYWAIT
2355
2949
                attrs['target_provision_state'] = states.ACTIVE
2388
2982
 
2389
2983
        with mock.patch.object(eventlet, 'sleep') as sleep_mock:
2390
2984
            self.service._sync_power_states(self.context)
2391
 
            # Ensure we've yielded on every iteration
2392
 
            self.assertEqual(len(nodes), sleep_mock.call_count)
 
2985
            # Ensure we've yielded on every iteration, except for node
 
2986
            # not mapped to this conductor
 
2987
            self.assertEqual(len(nodes) - 1, sleep_mock.call_count)
2393
2988
 
2394
2989
        get_nodeinfo_mock.assert_called_once_with(
2395
2990
                columns=self.columns, filters=self.filters)
2686
3281
        self._check_driver_properties("fake_ssh", expected)
2687
3282
 
2688
3283
    def test_driver_properties_fake_pxe(self):
2689
 
        expected = ['pxe_deploy_kernel', 'pxe_deploy_ramdisk']
 
3284
        expected = ['pxe_deploy_kernel', 'pxe_deploy_ramdisk',
 
3285
                    'deploy_kernel', 'deploy_ramdisk']
2690
3286
        self._check_driver_properties("fake_pxe", expected)
2691
3287
 
2692
3288
    def test_driver_properties_fake_seamicro(self):
2706
3302
                    'ipmi_username', 'ipmi_bridging', 'ipmi_transit_channel',
2707
3303
                    'ipmi_transit_address', 'ipmi_target_channel',
2708
3304
                    'ipmi_target_address', 'ipmi_local_address',
2709
 
                    'pxe_deploy_kernel', 'pxe_deploy_ramdisk'
 
3305
                    'pxe_deploy_kernel', 'pxe_deploy_ramdisk',
 
3306
                    'deploy_kernel', 'deploy_ramdisk',
2710
3307
                    ]
2711
3308
        self._check_driver_properties("pxe_ipmitool", expected)
2712
3309
 
2713
3310
    def test_driver_properties_pxe_ipminative(self):
2714
3311
        expected = ['ipmi_address', 'ipmi_password', 'ipmi_username',
2715
3312
                    'pxe_deploy_kernel', 'pxe_deploy_ramdisk',
 
3313
                    'deploy_kernel', 'deploy_ramdisk',
2716
3314
                    'ipmi_terminal_port']
2717
3315
        self._check_driver_properties("pxe_ipminative", expected)
2718
3316
 
2719
3317
    def test_driver_properties_pxe_ssh(self):
2720
3318
        expected = ['pxe_deploy_kernel', 'pxe_deploy_ramdisk',
 
3319
                    'deploy_kernel', 'deploy_ramdisk',
2721
3320
                    'ssh_address', 'ssh_username', 'ssh_virt_type',
2722
3321
                    'ssh_key_contents', 'ssh_key_filename',
2723
3322
                    'ssh_password', 'ssh_port']
2725
3324
 
2726
3325
    def test_driver_properties_pxe_seamicro(self):
2727
3326
        expected = ['pxe_deploy_kernel', 'pxe_deploy_ramdisk',
2728
 
                   'seamicro_api_endpoint', 'seamicro_password',
2729
 
                   'seamicro_server_id', 'seamicro_username',
2730
 
                   'seamicro_api_version', 'seamicro_terminal_port']
 
3327
                    'deploy_kernel', 'deploy_ramdisk',
 
3328
                    'seamicro_api_endpoint', 'seamicro_password',
 
3329
                    'seamicro_server_id', 'seamicro_username',
 
3330
                    'seamicro_api_version', 'seamicro_terminal_port']
2731
3331
        self._check_driver_properties("pxe_seamicro", expected)
2732
3332
 
2733
3333
    def test_driver_properties_pxe_snmp(self):
2734
3334
        expected = ['pxe_deploy_kernel', 'pxe_deploy_ramdisk',
 
3335
                    'deploy_kernel', 'deploy_ramdisk',
2735
3336
                    'snmp_driver', 'snmp_address', 'snmp_port', 'snmp_version',
2736
3337
                    'snmp_community', 'snmp_security', 'snmp_outlet']
2737
3338
        self._check_driver_properties("pxe_snmp", expected)
2738
3339
 
2739
3340
    def test_driver_properties_fake_ilo(self):
2740
3341
        expected = ['ilo_address', 'ilo_username', 'ilo_password',
2741
 
                   'client_port', 'client_timeout']
 
3342
                    'client_port', 'client_timeout', 'inspect_ports',
 
3343
                    'ilo_change_password']
2742
3344
        self._check_driver_properties("fake_ilo", expected)
2743
3345
 
2744
3346
    def test_driver_properties_ilo_iscsi(self):
2745
3347
        expected = ['ilo_address', 'ilo_username', 'ilo_password',
2746
3348
                   'client_port', 'client_timeout', 'ilo_deploy_iso',
2747
 
                   'console_port']
 
3349
                   'console_port', 'inspect_ports', 'ilo_change_password']
2748
3350
        self._check_driver_properties("iscsi_ilo", expected)
2749
3351
 
2750
3352
    def test_driver_properties_agent_ilo(self):
2751
3353
        expected = ['ilo_address', 'ilo_username', 'ilo_password',
2752
3354
                   'client_port', 'client_timeout', 'ilo_deploy_iso',
2753
 
                   'console_port']
 
3355
                   'console_port', 'inspect_ports', 'ilo_change_password']
2754
3356
        self._check_driver_properties("agent_ilo", expected)
2755
3357
 
2756
3358
    def test_driver_properties_fail(self):
2786
3388
        self.filters = {'reserved': False,
2787
3389
                        'maintenance': False,
2788
3390
                        'provision_state': states.ACTIVE}
2789
 
        self.columns = ['id', 'uuid', 'driver', 'conductor_affinity']
 
3391
        self.columns = ['uuid', 'driver', 'id', 'conductor_affinity']
2790
3392
 
2791
3393
    def _assert_get_nodeinfo_args(self, get_nodeinfo_mock):
2792
3394
        get_nodeinfo_mock.assert_called_once_with(
2986
3588
        mock_swift.return_value.get_temp_url.assert_called_once_with(
2987
3589
            container_name, expected_obj_name, timeout)
2988
3590
        self.assertEqual(expected_instance_info, self.node.instance_info)
 
3591
 
 
3592
 
 
3593
@_mock_record_keepalive
 
3594
class NodeInspectHardware(_ServiceSetUpMixin,
 
3595
                                   tests_db_base.DbTestCase):
 
3596
 
 
3597
    @mock.patch('ironic.drivers.modules.fake.FakeInspect.inspect_hardware')
 
3598
    def test_inspect_hardware_ok(self, mock_inspect):
 
3599
        self._start_service()
 
3600
        node = obj_utils.create_test_node(self.context, driver='fake',
 
3601
                                          provision_state=states.INSPECTING)
 
3602
        task = task_manager.TaskManager(self.context, node.uuid)
 
3603
        mock_inspect.return_value = states.MANAGEABLE
 
3604
        manager._do_inspect_hardware(task)
 
3605
        node.refresh()
 
3606
        self.assertEqual(states.MANAGEABLE, node.provision_state)
 
3607
        self.assertEqual(states.NOSTATE, node.target_provision_state)
 
3608
        self.assertIsNone(node.last_error)
 
3609
        mock_inspect.assert_called_once_with(mock.ANY)
 
3610
 
 
3611
    @mock.patch('ironic.drivers.modules.fake.FakeInspect.inspect_hardware')
 
3612
    def test_inspect_hardware_return_inspecting(self, mock_inspect):
 
3613
        self._start_service()
 
3614
        node = obj_utils.create_test_node(self.context, driver='fake',
 
3615
                                          provision_state=states.INSPECTING)
 
3616
        task = task_manager.TaskManager(self.context, node.uuid)
 
3617
        mock_inspect.return_value = states.INSPECTING
 
3618
        manager._do_inspect_hardware(task)
 
3619
        node.refresh()
 
3620
        self.assertEqual(states.INSPECTING, node.provision_state)
 
3621
        self.assertEqual(states.NOSTATE, node.target_provision_state)
 
3622
        self.assertIsNone(node.last_error)
 
3623
        mock_inspect.assert_called_once_with(mock.ANY)
 
3624
 
 
3625
    @mock.patch.object(manager, 'LOG')
 
3626
    @mock.patch('ironic.drivers.modules.fake.FakeInspect.inspect_hardware')
 
3627
    def test_inspect_hardware_return_other_state(self, mock_inspect, log_mock):
 
3628
        self._start_service()
 
3629
        node = obj_utils.create_test_node(self.context, driver='fake',
 
3630
                                          provision_state=states.INSPECTING)
 
3631
        task = task_manager.TaskManager(self.context, node.uuid)
 
3632
        mock_inspect.return_value = None
 
3633
        self.assertRaises(exception.HardwareInspectionFailure,
 
3634
                          manager._do_inspect_hardware, task)
 
3635
        node.refresh()
 
3636
        self.assertEqual(states.INSPECTFAIL, node.provision_state)
 
3637
        self.assertEqual(states.MANAGEABLE, node.target_provision_state)
 
3638
        self.assertIsNotNone(node.last_error)
 
3639
        mock_inspect.assert_called_once_with(mock.ANY)
 
3640
        self.assertTrue(log_mock.error.called)
 
3641
 
 
3642
    def test__check_inspect_timeouts(self):
 
3643
        self._start_service()
 
3644
        CONF.set_override('inspect_timeout', 1, group='conductor')
 
3645
        node = obj_utils.create_test_node(self.context, driver='fake',
 
3646
                provision_state=states.INSPECTING,
 
3647
                target_provision_state=states.MANAGEABLE,
 
3648
                provision_updated_at=datetime.datetime(2000, 1, 1, 0, 0),
 
3649
                inspection_started_at=datetime.datetime(2000, 1, 1, 0, 0))
 
3650
 
 
3651
        self.service._check_inspect_timeouts(self.context)
 
3652
        self.service._worker_pool.waitall()
 
3653
        node.refresh()
 
3654
        self.assertEqual(states.INSPECTFAIL, node.provision_state)
 
3655
        self.assertEqual(states.MANAGEABLE, node.target_provision_state)
 
3656
        self.assertIsNotNone(node.last_error)
 
3657
 
 
3658
    @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker')
 
3659
    def test_inspect_hardware_worker_pool_full(self, mock_spawn):
 
3660
        prv_state = states.MANAGEABLE
 
3661
        tgt_prv_state = states.NOSTATE
 
3662
        node = obj_utils.create_test_node(self.context,
 
3663
                                          provision_state=prv_state,
 
3664
                                          target_provision_state=tgt_prv_state,
 
3665
                                          last_error=None, driver='fake')
 
3666
        self._start_service()
 
3667
 
 
3668
        mock_spawn.side_effect = exception.NoFreeConductorWorker()
 
3669
 
 
3670
        exc = self.assertRaises(messaging.rpc.ExpectedException,
 
3671
                                self.service.inspect_hardware,
 
3672
                                self.context, node.uuid)
 
3673
        # Compare true exception hidden by @messaging.expected_exceptions
 
3674
        self.assertEqual(exception.NoFreeConductorWorker, exc.exc_info[0])
 
3675
        self.service._worker_pool.waitall()
 
3676
        node.refresh()
 
3677
        # Make sure things were rolled back
 
3678
        self.assertEqual(prv_state, node.provision_state)
 
3679
        self.assertEqual(tgt_prv_state, node.target_provision_state)
 
3680
        self.assertIsNotNone(node.last_error)
 
3681
        # Verify reservation has been cleared.
 
3682
        self.assertIsNone(node.reservation)
 
3683
 
 
3684
    def _test_inspect_hardware_validate_fail(self, mock_validate):
 
3685
        mock_validate.side_effect = exception.InvalidParameterValue('error')
 
3686
        node = obj_utils.create_test_node(self.context, driver='fake')
 
3687
        exc = self.assertRaises(messaging.rpc.ExpectedException,
 
3688
                                self.service.inspect_hardware,
 
3689
                                self.context, node.uuid)
 
3690
        # Compare true exception hidden by @messaging.expected_exceptions
 
3691
        self.assertEqual(exception.HardwareInspectionFailure, exc.exc_info[0])
 
3692
        # This is a sync operation last_error should be None.
 
3693
        self.assertIsNone(node.last_error)
 
3694
        # Verify reservation has been cleared.
 
3695
        self.assertIsNone(node.reservation)
 
3696
 
 
3697
    @mock.patch('ironic.drivers.modules.fake.FakeInspect.validate')
 
3698
    def test_inspect_hardware_validate_fail(self, mock_validate):
 
3699
        self._test_inspect_hardware_validate_fail(mock_validate)
 
3700
 
 
3701
    @mock.patch('ironic.drivers.modules.fake.FakePower.validate')
 
3702
    def test_inspect_hardware_power_validate_fail(self, mock_validate):
 
3703
        self._test_inspect_hardware_validate_fail(mock_validate)
 
3704
 
 
3705
    @mock.patch('ironic.drivers.modules.fake.FakeInspect.inspect_hardware')
 
3706
    def test_inspect_hardware_raises_error(self, mock_inspect):
 
3707
        self._start_service()
 
3708
        mock_inspect.side_effect = exception.HardwareInspectionFailure('test')
 
3709
        state = states.MANAGEABLE
 
3710
        node = obj_utils.create_test_node(self.context, driver='fake',
 
3711
                                          provision_state=states.INSPECTING,
 
3712
                                          target_provision_state=state)
 
3713
        task = task_manager.TaskManager(self.context, node.uuid)
 
3714
 
 
3715
        self.assertRaises(exception.HardwareInspectionFailure,
 
3716
                          manager._do_inspect_hardware, task)
 
3717
        node.refresh()
 
3718
        self.assertEqual(states.INSPECTFAIL, node.provision_state)
 
3719
        self.assertEqual(states.MANAGEABLE, node.target_provision_state)
 
3720
        self.assertIsNotNone(node.last_error)
 
3721
        self.assertTrue(mock_inspect.called)
 
3722
 
 
3723
 
 
3724
@mock.patch.object(task_manager, 'acquire')
 
3725
@mock.patch.object(manager.ConductorManager, '_mapped_to_this_conductor')
 
3726
@mock.patch.object(dbapi.IMPL, 'get_nodeinfo_list')
 
3727
class ManagerCheckInspectTimeoutsTestCase(_CommonMixIn,
 
3728
                                         tests_db_base.DbTestCase):
 
3729
    def setUp(self):
 
3730
        super(ManagerCheckInspectTimeoutsTestCase, self).setUp()
 
3731
        self.config(inspect_timeout=300, group='conductor')
 
3732
        self.service = manager.ConductorManager('hostname', 'test-topic')
 
3733
        self.service.dbapi = self.dbapi
 
3734
 
 
3735
        self.node = self._create_node(provision_state=states.INSPECTING,
 
3736
                                      target_provision_state=states.MANAGEABLE)
 
3737
        self.task = self._create_task(node=self.node)
 
3738
 
 
3739
        self.node2 = self._create_node(provision_state=states.INSPECTING,
 
3740
                                      target_provision_state=states.MANAGEABLE)
 
3741
        self.task2 = self._create_task(node=self.node2)
 
3742
 
 
3743
        self.filters = {'reserved': False,
 
3744
                        'inspection_started_before': 300,
 
3745
                        'provision_state': states.INSPECTING}
 
3746
        self.columns = ['uuid', 'driver']
 
3747
 
 
3748
    def _assert_get_nodeinfo_args(self, get_nodeinfo_mock):
 
3749
        get_nodeinfo_mock.assert_called_once_with(sort_dir='asc',
 
3750
                columns=self.columns, filters=self.filters,
 
3751
                sort_key='inspection_started_at')
 
3752
 
 
3753
    def test__check_inspect_timeouts_disabled(self, get_nodeinfo_mock,
 
3754
                                              mapped_mock, acquire_mock):
 
3755
        self.config(inspect_timeout=0, group='conductor')
 
3756
 
 
3757
        self.service._check_inspect_timeouts(self.context)
 
3758
 
 
3759
        self.assertFalse(get_nodeinfo_mock.called)
 
3760
        self.assertFalse(mapped_mock.called)
 
3761
        self.assertFalse(acquire_mock.called)
 
3762
 
 
3763
    def test__check_inspect_timeouts_not_mapped(self, get_nodeinfo_mock,
 
3764
                                                mapped_mock, acquire_mock):
 
3765
        get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response()
 
3766
        mapped_mock.return_value = False
 
3767
 
 
3768
        self.service._check_inspect_timeouts(self.context)
 
3769
 
 
3770
        self._assert_get_nodeinfo_args(get_nodeinfo_mock)
 
3771
        mapped_mock.assert_called_once_with(self.node.uuid, self.node.driver)
 
3772
        self.assertFalse(acquire_mock.called)
 
3773
 
 
3774
    def test__check_inspect_timeout(self, get_nodeinfo_mock,
 
3775
                                    mapped_mock, acquire_mock):
 
3776
        get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response()
 
3777
        mapped_mock.return_value = True
 
3778
        acquire_mock.side_effect = self._get_acquire_side_effect(self.task)
 
3779
 
 
3780
        self.service._check_inspect_timeouts(self.context)
 
3781
 
 
3782
        self._assert_get_nodeinfo_args(get_nodeinfo_mock)
 
3783
        mapped_mock.assert_called_once_with(self.node.uuid, self.node.driver)
 
3784
        acquire_mock.assert_called_once_with(self.context, self.node.uuid)
 
3785
        self.task.process_event.assert_called_with('fail')
 
3786
 
 
3787
    def test__check_inspect_timeouts_acquire_node_disappears(self,
 
3788
                                                             get_nodeinfo_mock,
 
3789
                                                             mapped_mock,
 
3790
                                                             acquire_mock):
 
3791
        get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response()
 
3792
        mapped_mock.return_value = True
 
3793
        acquire_mock.side_effect = exception.NodeNotFound(node='fake')
 
3794
 
 
3795
        # Exception eaten
 
3796
        self.service._check_inspect_timeouts(self.context)
 
3797
 
 
3798
        self._assert_get_nodeinfo_args(get_nodeinfo_mock)
 
3799
        mapped_mock.assert_called_once_with(
 
3800
                self.node.uuid, self.node.driver)
 
3801
        acquire_mock.assert_called_once_with(self.context,
 
3802
                                             self.node.uuid)
 
3803
        self.assertFalse(self.task.process_event.called)
 
3804
 
 
3805
    def test__check_inspect_timeouts_acquire_node_locked(self,
 
3806
                                                         get_nodeinfo_mock,
 
3807
                                                         mapped_mock,
 
3808
                                                         acquire_mock):
 
3809
        get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response()
 
3810
        mapped_mock.return_value = True
 
3811
        acquire_mock.side_effect = exception.NodeLocked(node='fake',
 
3812
                                                        host='fake')
 
3813
 
 
3814
        # Exception eaten
 
3815
        self.service._check_inspect_timeouts(self.context)
 
3816
 
 
3817
        self._assert_get_nodeinfo_args(get_nodeinfo_mock)
 
3818
        mapped_mock.assert_called_once_with(
 
3819
                self.node.uuid, self.node.driver)
 
3820
        acquire_mock.assert_called_once_with(self.context,
 
3821
                                             self.node.uuid)
 
3822
        self.assertFalse(self.task.process_event.called)
 
3823
 
 
3824
    def test__check_inspect_timeouts_no_acquire_after_lock(self,
 
3825
                                                           get_nodeinfo_mock,
 
3826
                                                           mapped_mock,
 
3827
                                                           acquire_mock):
 
3828
        task = self._create_task(
 
3829
                node_attrs=dict(provision_state=states.AVAILABLE,
 
3830
                                uuid=self.node.uuid))
 
3831
        get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response()
 
3832
        mapped_mock.return_value = True
 
3833
        acquire_mock.side_effect = self._get_acquire_side_effect(task)
 
3834
 
 
3835
        self.service._check_inspect_timeouts(self.context)
 
3836
 
 
3837
        self._assert_get_nodeinfo_args(get_nodeinfo_mock)
 
3838
        mapped_mock.assert_called_once_with(
 
3839
                self.node.uuid, self.node.driver)
 
3840
        acquire_mock.assert_called_once_with(self.context,
 
3841
                                             self.node.uuid)
 
3842
        self.assertFalse(task.process_event.called)
 
3843
 
 
3844
    def test__check_inspect_timeouts_to_maintenance_after_lock(self,
 
3845
                                                get_nodeinfo_mock,
 
3846
                                                mapped_mock,
 
3847
                                                acquire_mock):
 
3848
        task = self._create_task(
 
3849
                node_attrs=dict(provision_state=states.INSPECTING,
 
3850
                                target_provision_state=states.MANAGEABLE,
 
3851
                                maintenance=True,
 
3852
                                uuid=self.node.uuid))
 
3853
        get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response(
 
3854
                [task.node, self.node2])
 
3855
        mapped_mock.return_value = True
 
3856
        acquire_mock.side_effect = self._get_acquire_side_effect(
 
3857
                [task, self.task2])
 
3858
 
 
3859
        self.service._check_inspect_timeouts(self.context)
 
3860
 
 
3861
        self._assert_get_nodeinfo_args(get_nodeinfo_mock)
 
3862
        self.assertEqual([mock.call(self.node.uuid, task.node.driver),
 
3863
                          mock.call(self.node2.uuid, self.node2.driver)],
 
3864
                         mapped_mock.call_args_list)
 
3865
        self.assertEqual([mock.call(self.context, self.node.uuid),
 
3866
                          mock.call(self.context, self.node2.uuid)],
 
3867
                         acquire_mock.call_args_list)
 
3868
        # First node skipped
 
3869
        self.assertFalse(task.process_event.called)
 
3870
        # Second node spawned
 
3871
        self.task2.process_event.assert_called_with('fail')
 
3872
 
 
3873
    def test__check_inspect_timeouts_exiting_no_worker_avail(self,
 
3874
                                                       get_nodeinfo_mock,
 
3875
                                                       mapped_mock,
 
3876
                                                       acquire_mock):
 
3877
        get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response(
 
3878
                [self.node, self.node2])
 
3879
        mapped_mock.return_value = True
 
3880
        acquire_mock.side_effect = self._get_acquire_side_effect(
 
3881
                [(self.task, exception.NoFreeConductorWorker()), self.task2])
 
3882
 
 
3883
        # Exception should be nuked
 
3884
        self.service._check_inspect_timeouts(self.context)
 
3885
 
 
3886
        self._assert_get_nodeinfo_args(get_nodeinfo_mock)
 
3887
        # mapped should be only called for the first node as we should
 
3888
        # have exited the loop early due to NoFreeConductorWorker
 
3889
        mapped_mock.assert_called_once_with(
 
3890
                self.node.uuid, self.node.driver)
 
3891
        acquire_mock.assert_called_once_with(self.context,
 
3892
                                             self.node.uuid)
 
3893
        self.task.process_event.assert_called_with('fail')
 
3894
 
 
3895
    def test__check_inspect_timeouts_exit_with_other_exception(self,
 
3896
                                                  get_nodeinfo_mock,
 
3897
                                                  mapped_mock,
 
3898
                                                  acquire_mock):
 
3899
        get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response(
 
3900
                [self.node, self.node2])
 
3901
        mapped_mock.return_value = True
 
3902
        acquire_mock.side_effect = self._get_acquire_side_effect(
 
3903
                [(self.task, exception.IronicException('foo')), self.task2])
 
3904
 
 
3905
        # Should re-raise
 
3906
        self.assertRaises(exception.IronicException,
 
3907
                          self.service._check_inspect_timeouts,
 
3908
                          self.context)
 
3909
 
 
3910
        self._assert_get_nodeinfo_args(get_nodeinfo_mock)
 
3911
        # mapped should be only called for the first node as we should
 
3912
        # have exited the loop early due to unknown exception
 
3913
        mapped_mock.assert_called_once_with(
 
3914
                self.node.uuid, self.node.driver)
 
3915
        acquire_mock.assert_called_once_with(self.context,
 
3916
                                             self.node.uuid)
 
3917
        self.task.process_event.assert_called_with('fail')
 
3918
 
 
3919
    def test__check_inspect_timeouts_worker_limit(self, get_nodeinfo_mock,
 
3920
                                                  mapped_mock, acquire_mock):
 
3921
        self.config(periodic_max_workers=2, group='conductor')
 
3922
 
 
3923
        # Use the same nodes/tasks to make life easier in the tests
 
3924
        # here
 
3925
 
 
3926
        get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response(
 
3927
                [self.node] * 3)
 
3928
        mapped_mock.return_value = True
 
3929
        acquire_mock.side_effect = self._get_acquire_side_effect(
 
3930
                [self.task] * 3)
 
3931
 
 
3932
        self.service._check_inspect_timeouts(self.context)
 
3933
 
 
3934
        # Should only have ran 2.
 
3935
        self.assertEqual([mock.call(self.node.uuid, self.node.driver)] * 2,
 
3936
                         mapped_mock.call_args_list)
 
3937
        self.assertEqual([mock.call(self.context, self.node.uuid)] * 2,
 
3938
                         acquire_mock.call_args_list)
 
3939
        process_event_call = mock.call('fail')
 
3940
        self.assertEqual([process_event_call] * 2,
 
3941
                         self.task.process_event.call_args_list)
 
3942
 
 
3943
 
 
3944
@_mock_record_keepalive
 
3945
class DestroyPortTestCase(_ServiceSetUpMixin, tests_db_base.DbTestCase):
 
3946
    def test_destroy_port(self):
 
3947
        node = obj_utils.create_test_node(self.context, driver='fake')
 
3948
 
 
3949
        port = obj_utils.create_test_port(self.context,
 
3950
                                          node_id=node.id)
 
3951
        self.service.destroy_port(self.context, port)
 
3952
        self.assertRaises(exception.PortNotFound, port.refresh)
 
3953
 
 
3954
    def test_destroy_port_node_locked(self):
 
3955
        node = obj_utils.create_test_node(self.context, driver='fake',
 
3956
                                   reservation='fake-reserv')
 
3957
 
 
3958
        port = obj_utils.create_test_port(self.context, node_id=node.id)
 
3959
        exc = self.assertRaises(messaging.rpc.ExpectedException,
 
3960
                                self.service.destroy_port,
 
3961
                                self.context, port)
 
3962
        # Compare true exception hidden by @messaging.expected_exceptions
 
3963
        self.assertEqual(exception.NodeLocked, exc.exc_info[0])