diff --git a/ironic/common/states.py b/ironic/common/states.py index e5251036c8..f14ce5ec89 100644 --- a/ironic/common/states.py +++ b/ironic/common/states.py @@ -189,8 +189,7 @@ UPDATE_ALLOWED_STATES = (DEPLOYFAIL, INSPECTING, INSPECTFAIL, CLEANFAIL, ERROR, VERIFYING, ADOPTFAIL) """Transitional states in which we allow updating a node.""" -DELETE_ALLOWED_STATES = (AVAILABLE, NOSTATE, MANAGEABLE, ENROLL, - ADOPTFAIL) +DELETE_ALLOWED_STATES = (AVAILABLE, MANAGEABLE, ENROLL, ADOPTFAIL) """States in which node deletion is allowed.""" STABLE_STATES = (ENROLL, MANAGEABLE, AVAILABLE, ACTIVE, ERROR) diff --git a/ironic/conductor/task_manager.py b/ironic/conductor/task_manager.py index 5aa549c168..a709e488d0 100644 --- a/ironic/conductor/task_manager.py +++ b/ironic/conductor/task_manager.py @@ -192,7 +192,7 @@ class TaskManager(object): self._on_error_method = None self.context = context - self.node = None + self._node = None self.node_id = node_id self.shared = shared @@ -218,19 +218,21 @@ class TaskManager(object): self.driver = driver_factory.build_driver_for_task( self, driver_name=driver_name) - # NOTE(deva): this handles the Juno-era NOSTATE state - # and should be deleted after Kilo is released - if self.node.provision_state is states.NOSTATE: - self.node.provision_state = states.AVAILABLE - self.node.save() - - self.fsm.initialize(start_state=self.node.provision_state, - target_state=self.node.target_provision_state) - except Exception: with excutils.save_and_reraise_exception(): self.release_resources() + @property + def node(self): + return self._node + + @node.setter + def node(self, node): + self._node = node + if node is not None: + self.fsm.initialize(start_state=self.node.provision_state, + target_state=self.node.target_provision_state) + def _lock(self): self._debug_timer.restart() diff --git a/ironic/tests/unit/conductor/test_manager.py b/ironic/tests/unit/conductor/test_manager.py index 19f05f2d38..835a41f749 100644 --- a/ironic/tests/unit/conductor/test_manager.py +++ b/ironic/tests/unit/conductor/test_manager.py @@ -763,27 +763,6 @@ class ServiceDoNodeDeployTestCase(mgr_utils.ServiceSetUpMixin, mock_iwdi): self._test_do_node_deploy_validate_fail(mock_validate, mock_iwdi) - @mock.patch('ironic.conductor.task_manager.TaskManager.process_event') - def test_deploy_with_nostate_converts_to_available(self, mock_pe, - mock_iwdi): - # expressly create a node using the Juno-era NOSTATE state - # and assert that it does not result in an error, and that the state - # is converted to the new AVAILABLE state. - # Mock the process_event call, because the transitions from - # AVAILABLE are tested thoroughly elsewhere - # NOTE(deva): This test can be deleted after Kilo is released - mock_iwdi.return_value = False - self._start_service() - node = obj_utils.create_test_node(self.context, driver='fake', - provision_state=states.NOSTATE) - self.assertEqual(states.NOSTATE, node.provision_state) - self.service.do_node_deploy(self.context, node.uuid) - self.assertTrue(mock_pe.called) - node.refresh() - self.assertEqual(states.AVAILABLE, node.provision_state) - mock_iwdi.assert_called_once_with(self.context, node.instance_info) - self.assertFalse(node.driver_internal_info['is_whole_disk_image']) - def test_do_node_deploy_partial_ok(self, mock_iwdi): mock_iwdi.return_value = False self._start_service() diff --git a/ironic/tests/unit/conductor/test_task_manager.py b/ironic/tests/unit/conductor/test_task_manager.py index ca652d2986..8246b71a27 100644 --- a/ironic/tests/unit/conductor/test_task_manager.py +++ b/ironic/tests/unit/conductor/test_task_manager.py @@ -396,6 +396,28 @@ class TaskManagerTestCase(tests_db_base.DbTestCase): get_ports_mock.assert_called_once_with(self.context, self.node.id) get_portgroups_mock.assert_called_once_with(self.context, self.node.id) + def test_upgrade_lock_refreshes_fsm(self, get_portgroups_mock, + get_ports_mock, build_driver_mock, + reserve_mock, release_mock, + node_get_mock): + reserve_mock.return_value = self.node + node_get_mock.return_value = self.node + with task_manager.acquire(self.context, 'fake-node-id', + shared=True) as task1: + self.assertEqual(states.AVAILABLE, task1.node.provision_state) + + with task_manager.acquire(self.context, 'fake-node-id', + shared=False) as task2: + # move the node to manageable + task2.process_event('manage') + self.assertEqual(states.MANAGEABLE, task1.node.provision_state) + + # now upgrade our shared task and try to go to cleaning + # this will explode if task1's FSM doesn't get refreshed + task1.upgrade_lock() + task1.process_event('provide') + self.assertEqual(states.CLEANING, task1.node.provision_state) + def test_spawn_after( self, get_portgroups_mock, get_ports_mock, build_driver_mock, reserve_mock, release_mock, node_get_mock): diff --git a/ironic/tests/unit/db/utils.py b/ironic/tests/unit/db/utils.py index 6d2b300fdf..85154a2c22 100644 --- a/ironic/tests/unit/db/utils.py +++ b/ironic/tests/unit/db/utils.py @@ -216,7 +216,7 @@ def get_test_node(**kw): 'conductor_affinity': kw.get('conductor_affinity', None), 'power_state': kw.get('power_state', states.NOSTATE), 'target_power_state': kw.get('target_power_state', states.NOSTATE), - 'provision_state': kw.get('provision_state', states.NOSTATE), + 'provision_state': kw.get('provision_state', states.AVAILABLE), 'target_provision_state': kw.get('target_provision_state', states.NOSTATE), 'provision_updated_at': kw.get('provision_updated_at'),