"Fossies" - the Fresh Open Source Software Archive

Member "ironic-16.0.3/ironic/tests/unit/conductor/test_manager.py" (18 Jan 2021, 393940 Bytes) of package /linux/misc/openstack/ironic-16.0.3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "test_manager.py": 16.0.2_vs_16.0.3.

    1 # coding=utf-8
    2 
    3 # Copyright 2013 Hewlett-Packard Development Company, L.P.
    4 # Copyright 2013 International Business Machines Corporation
    5 # All Rights Reserved.
    6 #
    7 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
    8 #    not use this file except in compliance with the License. You may obtain
    9 #    a copy of the License at
   10 #
   11 #         http://www.apache.org/licenses/LICENSE-2.0
   12 #
   13 #    Unless required by applicable law or agreed to in writing, software
   14 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   15 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   16 #    License for the specific language governing permissions and limitations
   17 #    under the License.
   18 
   19 """Test class for Ironic ManagerService."""
   20 
   21 from collections import namedtuple
   22 import datetime
   23 import queue
   24 import re
   25 from unittest import mock
   26 
   27 import eventlet
   28 from futurist import waiters
   29 from oslo_config import cfg
   30 import oslo_messaging as messaging
   31 from oslo_utils import uuidutils
   32 from oslo_versionedobjects import base as ovo_base
   33 from oslo_versionedobjects import fields
   34 
   35 from ironic.common import boot_devices
   36 from ironic.common import components
   37 from ironic.common import driver_factory
   38 from ironic.common import exception
   39 from ironic.common import faults
   40 from ironic.common import images
   41 from ironic.common import indicator_states
   42 from ironic.common import nova
   43 from ironic.common import states
   44 from ironic.conductor import cleaning
   45 from ironic.conductor import deployments
   46 from ironic.conductor import manager
   47 from ironic.conductor import notification_utils
   48 from ironic.conductor import steps as conductor_steps
   49 from ironic.conductor import task_manager
   50 from ironic.conductor import utils as conductor_utils
   51 from ironic.db import api as dbapi
   52 from ironic.drivers import base as drivers_base
   53 from ironic.drivers.modules import fake
   54 from ironic.drivers.modules.network import flat as n_flat
   55 from ironic import objects
   56 from ironic.objects import base as obj_base
   57 from ironic.objects import fields as obj_fields
   58 from ironic.tests.unit.conductor import mgr_utils
   59 from ironic.tests.unit.db import base as db_base
   60 from ironic.tests.unit.db import utils as db_utils
   61 from ironic.tests.unit.objects import utils as obj_utils
   62 
   63 CONF = cfg.CONF
   64 
   65 
   66 @mgr_utils.mock_record_keepalive
   67 class ChangeNodePowerStateTestCase(mgr_utils.ServiceSetUpMixin,
   68                                    db_base.DbTestCase):
   69 
   70     @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
   71     def test_change_node_power_state_power_on(self, get_power_mock):
   72         # Test change_node_power_state including integration with
   73         # conductor.utils.node_power_action and lower.
   74         get_power_mock.return_value = states.POWER_OFF
   75         node = obj_utils.create_test_node(self.context,
   76                                           driver='fake-hardware',
   77                                           power_state=states.POWER_OFF)
   78         self._start_service()
   79 
   80         self.service.change_node_power_state(self.context,
   81                                              node.uuid,
   82                                              states.POWER_ON)
   83         self._stop_service()
   84 
   85         get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
   86         node.refresh()
   87         self.assertEqual(states.POWER_ON, node.power_state)
   88         self.assertIsNone(node.target_power_state)
   89         self.assertIsNone(node.last_error)
   90         # Verify the reservation has been cleared by
   91         # background task's link callback.
   92         self.assertIsNone(node.reservation)
   93 
   94     @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
   95     def test_change_node_power_state_soft_power_off_timeout(self,
   96                                                             get_power_mock):
   97         # Test change_node_power_state with timeout optional parameter
   98         # including integration with conductor.utils.node_power_action and
   99         # lower.
  100         get_power_mock.return_value = states.POWER_ON
  101         node = obj_utils.create_test_node(self.context,
  102                                           driver='fake-hardware',
  103                                           power_state=states.POWER_ON)
  104         self._start_service()
  105 
  106         self.service.change_node_power_state(self.context,
  107                                              node.uuid,
  108                                              states.SOFT_POWER_OFF,
  109                                              timeout=2)
  110         self._stop_service()
  111 
  112         get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
  113         node.refresh()
  114         self.assertEqual(states.POWER_OFF, node.power_state)
  115         self.assertIsNone(node.target_power_state)
  116         self.assertIsNone(node.last_error)
  117         # Verify the reservation has been cleared by
  118         # background task's link callback.
  119         self.assertIsNone(node.reservation)
  120 
  121     @mock.patch.object(conductor_utils, 'node_power_action', autospec=True)
  122     def test_change_node_power_state_node_already_locked(self,
  123                                                          pwr_act_mock):
  124         # Test change_node_power_state with mocked
  125         # conductor.utils.node_power_action.
  126         fake_reservation = 'fake-reserv'
  127         pwr_state = states.POWER_ON
  128         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
  129                                           power_state=pwr_state,
  130                                           reservation=fake_reservation)
  131         self._start_service()
  132 
  133         exc = self.assertRaises(messaging.rpc.ExpectedException,
  134                                 self.service.change_node_power_state,
  135                                 self.context,
  136                                 node.uuid,
  137                                 states.POWER_ON)
  138         # Compare true exception hidden by @messaging.expected_exceptions
  139         self.assertEqual(exception.NodeLocked, exc.exc_info[0])
  140 
  141         # In this test worker should not be spawned, but waiting to make sure
  142         # the below perform_mock assertion is valid.
  143         self._stop_service()
  144         self.assertFalse(pwr_act_mock.called, 'node_power_action has been '
  145                                               'unexpectedly called.')
  146         # Verify existing reservation wasn't broken.
  147         node.refresh()
  148         self.assertEqual(fake_reservation, node.reservation)
  149 
  150     def test_change_node_power_state_worker_pool_full(self):
  151         # Test change_node_power_state including integration with
  152         # conductor.utils.node_power_action and lower.
  153         initial_state = states.POWER_OFF
  154         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
  155                                           power_state=initial_state)
  156         self._start_service()
  157 
  158         with mock.patch.object(self.service,
  159                                '_spawn_worker', autospec=True) as spawn_mock:
  160             spawn_mock.side_effect = exception.NoFreeConductorWorker()
  161 
  162             exc = self.assertRaises(messaging.rpc.ExpectedException,
  163                                     self.service.change_node_power_state,
  164                                     self.context,
  165                                     node.uuid,
  166                                     states.POWER_ON)
  167             # Compare true exception hidden by @messaging.expected_exceptions
  168             self.assertEqual(exception.NoFreeConductorWorker, exc.exc_info[0])
  169 
  170             spawn_mock.assert_called_once_with(mock.ANY, mock.ANY,
  171                                                mock.ANY, timeout=mock.ANY)
  172             node.refresh()
  173             self.assertEqual(initial_state, node.power_state)
  174             self.assertIsNone(node.target_power_state)
  175             self.assertIsNotNone(node.last_error)
  176             # Verify the picked reservation has been cleared due to full pool.
  177             self.assertIsNone(node.reservation)
  178 
  179     @mock.patch.object(fake.FakePower, 'set_power_state', autospec=True)
  180     @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
  181     def test_change_node_power_state_exception_in_background_task(
  182             self, get_power_mock, set_power_mock):
  183         # Test change_node_power_state including integration with
  184         # conductor.utils.node_power_action and lower.
  185         initial_state = states.POWER_OFF
  186         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
  187                                           power_state=initial_state)
  188         self._start_service()
  189 
  190         get_power_mock.return_value = states.POWER_OFF
  191         new_state = states.POWER_ON
  192         set_power_mock.side_effect = exception.PowerStateFailure(
  193             pstate=new_state
  194         )
  195 
  196         self.service.change_node_power_state(self.context,
  197                                              node.uuid,
  198                                              new_state)
  199         self._stop_service()
  200 
  201         get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
  202         set_power_mock.assert_called_once_with(mock.ANY, mock.ANY,
  203                                                new_state, timeout=None)
  204         node.refresh()
  205         self.assertEqual(initial_state, node.power_state)
  206         self.assertIsNone(node.target_power_state)
  207         self.assertIsNotNone(node.last_error)
  208         # Verify the reservation has been cleared by background task's
  209         # link callback despite exception in background task.
  210         self.assertIsNone(node.reservation)
  211 
  212     @mock.patch.object(fake.FakePower, 'validate', autospec=True)
  213     def test_change_node_power_state_validate_fail(self, validate_mock):
  214         # Test change_node_power_state where task.driver.power.validate
  215         # fails and raises an exception
  216         initial_state = states.POWER_ON
  217         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
  218                                           power_state=initial_state)
  219         self._start_service()
  220 
  221         validate_mock.side_effect = exception.InvalidParameterValue(
  222             'wrong power driver info')
  223 
  224         exc = self.assertRaises(messaging.rpc.ExpectedException,
  225                                 self.service.change_node_power_state,
  226                                 self.context,
  227                                 node.uuid,
  228                                 states.POWER_ON)
  229 
  230         self.assertEqual(exception.InvalidParameterValue, exc.exc_info[0])
  231 
  232         node.refresh()
  233         validate_mock.assert_called_once_with(mock.ANY, mock.ANY)
  234         self.assertEqual(states.POWER_ON, node.power_state)
  235         self.assertIsNone(node.target_power_state)
  236         self.assertIsNone(node.last_error)
  237 
  238     @mock.patch('ironic.objects.node.NodeSetPowerStateNotification',
  239                 autospec=True)
  240     def test_node_set_power_state_notif_success(self, mock_notif):
  241         # Test that successfully changing a node's power state sends the
  242         # correct .start and .end notifications
  243         self.config(notification_level='info')
  244         self.config(host='my-host')
  245         # Required for exception handling
  246         mock_notif.__name__ = 'NodeSetPowerStateNotification'
  247         node = obj_utils.create_test_node(self.context,
  248                                           driver='fake-hardware',
  249                                           power_state=states.POWER_OFF)
  250 
  251         self._start_service()
  252         self.service.change_node_power_state(self.context,
  253                                              node.uuid,
  254                                              states.POWER_ON)
  255         # Give async worker a chance to finish
  256         self._stop_service()
  257 
  258         # 2 notifications should be sent: 1 .start and 1 .end
  259         self.assertEqual(2, mock_notif.call_count)
  260         self.assertEqual(2, mock_notif.return_value.emit.call_count)
  261 
  262         first_notif_args = mock_notif.call_args_list[0][1]
  263         second_notif_args = mock_notif.call_args_list[1][1]
  264 
  265         self.assertNotificationEqual(first_notif_args,
  266                                      'ironic-conductor', CONF.host,
  267                                      'baremetal.node.power_set.start',
  268                                      obj_fields.NotificationLevel.INFO)
  269         self.assertNotificationEqual(second_notif_args,
  270                                      'ironic-conductor', CONF.host,
  271                                      'baremetal.node.power_set.end',
  272                                      obj_fields.NotificationLevel.INFO)
  273 
  274     @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
  275     @mock.patch('ironic.objects.node.NodeSetPowerStateNotification',
  276                 autospec=True)
  277     def test_node_set_power_state_notif_get_power_fail(self, mock_notif,
  278                                                        get_power_mock):
  279         # Test that correct notifications are sent when changing node power
  280         # state and retrieving the node's current power state fails
  281         self.config(notification_level='info')
  282         self.config(host='my-host')
  283         # Required for exception handling
  284         mock_notif.__name__ = 'NodeSetPowerStateNotification'
  285         node = obj_utils.create_test_node(self.context,
  286                                           driver='fake-hardware',
  287                                           power_state=states.POWER_OFF)
  288         self._start_service()
  289 
  290         get_power_mock.side_effect = Exception('I have failed')
  291         self.service.change_node_power_state(self.context,
  292                                              node.uuid,
  293                                              states.POWER_ON)
  294         # Give async worker a chance to finish
  295         self._stop_service()
  296 
  297         get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
  298 
  299         # 2 notifications should be sent: 1 .start and 1 .error
  300         self.assertEqual(2, mock_notif.call_count)
  301         self.assertEqual(2, mock_notif.return_value.emit.call_count)
  302 
  303         first_notif_args = mock_notif.call_args_list[0][1]
  304         second_notif_args = mock_notif.call_args_list[1][1]
  305 
  306         self.assertNotificationEqual(first_notif_args,
  307                                      'ironic-conductor', CONF.host,
  308                                      'baremetal.node.power_set.start',
  309                                      obj_fields.NotificationLevel.INFO)
  310         self.assertNotificationEqual(second_notif_args,
  311                                      'ironic-conductor', CONF.host,
  312                                      'baremetal.node.power_set.error',
  313                                      obj_fields.NotificationLevel.ERROR)
  314 
  315     @mock.patch.object(fake.FakePower, 'set_power_state', autospec=True)
  316     @mock.patch('ironic.objects.node.NodeSetPowerStateNotification',
  317                 autospec=True)
  318     def test_node_set_power_state_notif_set_power_fail(self, mock_notif,
  319                                                        set_power_mock):
  320         # Test that correct notifications are sent when changing node power
  321         # state and setting the node's power state fails
  322         self.config(notification_level='info')
  323         self.config(host='my-host')
  324         # Required for exception handling
  325         mock_notif.__name__ = 'NodeSetPowerStateNotification'
  326         node = obj_utils.create_test_node(self.context,
  327                                           driver='fake-hardware',
  328                                           power_state=states.POWER_OFF)
  329         self._start_service()
  330 
  331         set_power_mock.side_effect = Exception('I have failed')
  332         self.service.change_node_power_state(self.context,
  333                                              node.uuid,
  334                                              states.POWER_ON)
  335         # Give async worker a chance to finish
  336         self._stop_service()
  337 
  338         set_power_mock.assert_called_once_with(mock.ANY, mock.ANY,
  339                                                states.POWER_ON, timeout=None)
  340 
  341         # 2 notifications should be sent: 1 .start and 1 .error
  342         self.assertEqual(2, mock_notif.call_count)
  343         self.assertEqual(2, mock_notif.return_value.emit.call_count)
  344 
  345         first_notif_args = mock_notif.call_args_list[0][1]
  346         second_notif_args = mock_notif.call_args_list[1][1]
  347 
  348         self.assertNotificationEqual(first_notif_args,
  349                                      'ironic-conductor', CONF.host,
  350                                      'baremetal.node.power_set.start',
  351                                      obj_fields.NotificationLevel.INFO)
  352         self.assertNotificationEqual(second_notif_args,
  353                                      'ironic-conductor', CONF.host,
  354                                      'baremetal.node.power_set.error',
  355                                      obj_fields.NotificationLevel.ERROR)
  356 
  357     @mock.patch('ironic.objects.node.NodeSetPowerStateNotification',
  358                 autospec=True)
  359     def test_node_set_power_state_notif_spawn_fail(self, mock_notif):
  360         # Test that failure notification is not sent when spawning the
  361         # background conductor worker fails
  362         self.config(notification_level='info')
  363         self.config(host='my-host')
  364         # Required for exception handling
  365         mock_notif.__name__ = 'NodeSetPowerStateNotification'
  366         node = obj_utils.create_test_node(self.context,
  367                                           driver='fake-hardware',
  368                                           power_state=states.POWER_OFF)
  369 
  370         self._start_service()
  371         with mock.patch.object(self.service,
  372                                '_spawn_worker', autospec=True) as spawn_mock:
  373             spawn_mock.side_effect = exception.NoFreeConductorWorker()
  374             self.assertRaises(messaging.rpc.ExpectedException,
  375                               self.service.change_node_power_state,
  376                               self.context,
  377                               node.uuid,
  378                               states.POWER_ON)
  379 
  380             spawn_mock.assert_called_once_with(
  381                 conductor_utils.node_power_action, mock.ANY, states.POWER_ON,
  382                 timeout=None)
  383             self.assertFalse(mock_notif.called)
  384 
  385     @mock.patch('ironic.objects.node.NodeSetPowerStateNotification',
  386                 autospec=True)
  387     def test_node_set_power_state_notif_no_state_change(self, mock_notif):
  388         # Test that correct notifications are sent when changing node power
  389         # state and no state change is necessary
  390         self.config(notification_level='info')
  391         self.config(host='my-host')
  392         # Required for exception handling
  393         mock_notif.__name__ = 'NodeSetPowerStateNotification'
  394         node = obj_utils.create_test_node(self.context,
  395                                           driver='fake-hardware',
  396                                           power_state=states.POWER_OFF)
  397 
  398         self._start_service()
  399         self.service.change_node_power_state(self.context,
  400                                              node.uuid,
  401                                              states.POWER_OFF)
  402         # Give async worker a chance to finish
  403         self._stop_service()
  404 
  405         # 2 notifications should be sent: 1 .start and 1 .end
  406         self.assertEqual(2, mock_notif.call_count)
  407         self.assertEqual(2, mock_notif.return_value.emit.call_count)
  408 
  409         first_notif_args = mock_notif.call_args_list[0][1]
  410         second_notif_args = mock_notif.call_args_list[1][1]
  411 
  412         self.assertNotificationEqual(first_notif_args,
  413                                      'ironic-conductor', CONF.host,
  414                                      'baremetal.node.power_set.start',
  415                                      obj_fields.NotificationLevel.INFO)
  416         self.assertNotificationEqual(second_notif_args,
  417                                      'ironic-conductor', CONF.host,
  418                                      'baremetal.node.power_set.end',
  419                                      obj_fields.NotificationLevel.INFO)
  420 
  421     @mock.patch.object(fake.FakePower, 'get_supported_power_states',
  422                        autospec=True)
  423     def test_change_node_power_state_unsupported_state(self, supported_mock):
  424         # Test change_node_power_state where unsupported power state raises
  425         # an exception
  426         initial_state = states.POWER_ON
  427         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
  428                                           power_state=initial_state)
  429         self._start_service()
  430 
  431         supported_mock.return_value = [
  432             states.POWER_ON, states.POWER_OFF, states.REBOOT]
  433 
  434         exc = self.assertRaises(messaging.rpc.ExpectedException,
  435                                 self.service.change_node_power_state,
  436                                 self.context,
  437                                 node.uuid,
  438                                 states.SOFT_POWER_OFF)
  439 
  440         self.assertEqual(exception.InvalidParameterValue, exc.exc_info[0])
  441 
  442         node.refresh()
  443         supported_mock.assert_called_once_with(mock.ANY, mock.ANY)
  444         self.assertEqual(states.POWER_ON, node.power_state)
  445         self.assertIsNone(node.target_power_state)
  446         self.assertIsNone(node.last_error)
  447 
  448 
  449 @mgr_utils.mock_record_keepalive
  450 class CreateNodeTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
  451     def test_create_node(self):
  452         node = obj_utils.get_test_node(self.context, driver='fake-hardware',
  453                                        extra={'test': 'one'})
  454 
  455         res = self.service.create_node(self.context, node)
  456 
  457         self.assertEqual({'test': 'one'}, res['extra'])
  458         res = objects.Node.get_by_uuid(self.context, node['uuid'])
  459         self.assertEqual({'test': 'one'}, res['extra'])
  460 
  461     @mock.patch.object(driver_factory, 'check_and_update_node_interfaces',
  462                        autospec=True)
  463     def test_create_node_validation_fails(self, mock_validate):
  464         node = obj_utils.get_test_node(self.context, driver='fake-hardware',
  465                                        extra={'test': 'one'})
  466         mock_validate.side_effect = exception.InterfaceNotFoundInEntrypoint(
  467             'boom')
  468 
  469         exc = self.assertRaises(messaging.rpc.ExpectedException,
  470                                 self.service.create_node,
  471                                 self.context, node)
  472         # Compare true exception hidden by @messaging.expected_exceptions
  473         self.assertEqual(exception.InterfaceNotFoundInEntrypoint,
  474                          exc.exc_info[0])
  475 
  476         self.assertRaises(exception.NotFound,
  477                           objects.Node.get_by_uuid, self.context, node['uuid'])
  478 
  479 
  480 @mgr_utils.mock_record_keepalive
  481 class UpdateNodeTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
  482     def test_update_node(self):
  483         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
  484                                           extra={'test': 'one'})
  485 
  486         # check that ManagerService.update_node actually updates the node
  487         node.extra = {'test': 'two'}
  488         res = self.service.update_node(self.context, node)
  489         self.assertEqual({'test': 'two'}, res['extra'])
  490 
  491     def test_update_node_maintenance_set_false(self):
  492         node = obj_utils.create_test_node(self.context,
  493                                           driver='fake-hardware',
  494                                           maintenance=True,
  495                                           fault='clean failure',
  496                                           maintenance_reason='reason')
  497 
  498         # check that ManagerService.update_node actually updates the node
  499         node.maintenance = False
  500         res = self.service.update_node(self.context, node)
  501         self.assertFalse(res['maintenance'])
  502         self.assertIsNone(res['maintenance_reason'])
  503         self.assertIsNone(res['fault'])
  504 
  505     def test_update_node_protected_set(self):
  506         for state in ('active', 'rescue'):
  507             node = obj_utils.create_test_node(self.context,
  508                                               uuid=uuidutils.generate_uuid(),
  509                                               provision_state=state)
  510 
  511             node.protected = True
  512             res = self.service.update_node(self.context, node)
  513             self.assertTrue(res['protected'])
  514             self.assertIsNone(res['protected_reason'])
  515 
  516     def test_update_node_protected_unset(self):
  517         # NOTE(dtantsur): we allow unsetting protected in any state to make
  518         # sure a node cannot get stuck in it.
  519         for state in ('active', 'rescue', 'rescue failed'):
  520             node = obj_utils.create_test_node(self.context,
  521                                               uuid=uuidutils.generate_uuid(),
  522                                               provision_state=state,
  523                                               protected=True,
  524                                               protected_reason='reason')
  525 
  526             # check that ManagerService.update_node actually updates the node
  527             node.protected = False
  528             res = self.service.update_node(self.context, node)
  529             self.assertFalse(res['protected'])
  530             self.assertIsNone(res['protected_reason'])
  531 
  532     def test_update_node_protected_invalid_state(self):
  533         node = obj_utils.create_test_node(self.context,
  534                                           provision_state='available')
  535 
  536         node.protected = True
  537         exc = self.assertRaises(messaging.rpc.ExpectedException,
  538                                 self.service.update_node,
  539                                 self.context,
  540                                 node)
  541         # Compare true exception hidden by @messaging.expected_exceptions
  542         self.assertEqual(exception.InvalidState, exc.exc_info[0])
  543 
  544         res = objects.Node.get_by_uuid(self.context, node['uuid'])
  545         self.assertFalse(res['protected'])
  546         self.assertIsNone(res['protected_reason'])
  547 
  548     def test_update_node_protected_reason_without_protected(self):
  549         node = obj_utils.create_test_node(self.context,
  550                                           provision_state='active')
  551 
  552         node.protected_reason = 'reason!'
  553         exc = self.assertRaises(messaging.rpc.ExpectedException,
  554                                 self.service.update_node,
  555                                 self.context,
  556                                 node)
  557         # Compare true exception hidden by @messaging.expected_exceptions
  558         self.assertEqual(exception.InvalidParameterValue, exc.exc_info[0])
  559 
  560         res = objects.Node.get_by_uuid(self.context, node['uuid'])
  561         self.assertFalse(res['protected'])
  562         self.assertIsNone(res['protected_reason'])
  563 
  564     def test_update_node_retired_set(self):
  565         for state in ('active', 'rescue', 'manageable'):
  566             node = obj_utils.create_test_node(self.context,
  567                                               uuid=uuidutils.generate_uuid(),
  568                                               provision_state=state)
  569 
  570             node.retired = True
  571             res = self.service.update_node(self.context, node)
  572             self.assertTrue(res['retired'])
  573             self.assertIsNone(res['retired_reason'])
  574 
  575     def test_update_node_retired_invalid_state(self):
  576         # NOTE(arne_wiebalck): nodes in available cannot be 'retired'.
  577         # This is to ensure backwards comaptibility.
  578         node = obj_utils.create_test_node(self.context,
  579                                           provision_state='available')
  580 
  581         node.retired = True
  582         exc = self.assertRaises(messaging.rpc.ExpectedException,
  583                                 self.service.update_node,
  584                                 self.context,
  585                                 node)
  586         # Compare true exception hidden by @messaging.expected_exceptions
  587         self.assertEqual(exception.InvalidState, exc.exc_info[0])
  588 
  589         res = objects.Node.get_by_uuid(self.context, node['uuid'])
  590         self.assertFalse(res['retired'])
  591         self.assertIsNone(res['retired_reason'])
  592 
  593     def test_update_node_retired_unset(self):
  594         for state in ('active', 'manageable', 'rescue', 'rescue failed'):
  595             node = obj_utils.create_test_node(self.context,
  596                                               uuid=uuidutils.generate_uuid(),
  597                                               provision_state=state,
  598                                               retired=True,
  599                                               retired_reason='EOL')
  600 
  601             # check that ManagerService.update_node actually updates the node
  602             node.retired = False
  603             res = self.service.update_node(self.context, node)
  604             self.assertFalse(res['retired'])
  605             self.assertIsNone(res['retired_reason'])
  606 
  607     def test_update_node_retired_reason_without_retired(self):
  608         node = obj_utils.create_test_node(self.context,
  609                                           provision_state='active')
  610 
  611         node.retired_reason = 'warranty expired'
  612         exc = self.assertRaises(messaging.rpc.ExpectedException,
  613                                 self.service.update_node,
  614                                 self.context,
  615                                 node)
  616         # Compare true exception hidden by @messaging.expected_exceptions
  617         self.assertEqual(exception.InvalidParameterValue, exc.exc_info[0])
  618 
  619         res = objects.Node.get_by_uuid(self.context, node['uuid'])
  620         self.assertFalse(res['retired'])
  621         self.assertIsNone(res['retired_reason'])
  622 
  623     def test_update_node_already_locked(self):
  624         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
  625                                           extra={'test': 'one'})
  626 
  627         # check that it fails if something else has locked it already
  628         with task_manager.acquire(self.context, node['id'], shared=False):
  629             node.extra = {'test': 'two'}
  630             exc = self.assertRaises(messaging.rpc.ExpectedException,
  631                                     self.service.update_node,
  632                                     self.context,
  633                                     node)
  634             # Compare true exception hidden by @messaging.expected_exceptions
  635             self.assertEqual(exception.NodeLocked, exc.exc_info[0])
  636 
  637         # verify change did not happen
  638         res = objects.Node.get_by_uuid(self.context, node['uuid'])
  639         self.assertEqual({'test': 'one'}, res['extra'])
  640 
  641     def test_update_node_already_associated(self):
  642         old_instance = uuidutils.generate_uuid()
  643         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
  644                                           instance_uuid=old_instance)
  645         node.instance_uuid = uuidutils.generate_uuid()
  646         exc = self.assertRaises(messaging.rpc.ExpectedException,
  647                                 self.service.update_node,
  648                                 self.context,
  649                                 node)
  650         # Compare true exception hidden by @messaging.expected_exceptions
  651         self.assertEqual(exception.NodeAssociated, exc.exc_info[0])
  652 
  653         # verify change did not happen
  654         res = objects.Node.get_by_uuid(self.context, node['uuid'])
  655         self.assertEqual(old_instance, res['instance_uuid'])
  656 
  657     @mock.patch('ironic.drivers.modules.fake.FakePower.get_power_state',
  658                 autospec=True)
  659     def _test_associate_node(self, power_state, mock_get_power_state):
  660         mock_get_power_state.return_value = power_state
  661         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
  662                                           instance_uuid=None,
  663                                           power_state=states.NOSTATE)
  664         uuid1 = uuidutils.generate_uuid()
  665         uuid2 = uuidutils.generate_uuid()
  666         node.instance_uuid = uuid1
  667         self.service.update_node(self.context, node)
  668 
  669         # Check if the change was applied
  670         node.instance_uuid = uuid2
  671         node.refresh()
  672         self.assertEqual(uuid1, node.instance_uuid)
  673 
  674     def test_associate_node_powered_off(self):
  675         self._test_associate_node(states.POWER_OFF)
  676 
  677     def test_associate_node_powered_on(self):
  678         self._test_associate_node(states.POWER_ON)
  679 
  680     def test_update_node_invalid_driver(self):
  681         existing_driver = 'fake-hardware'
  682         wrong_driver = 'wrong-driver'
  683         node = obj_utils.create_test_node(self.context,
  684                                           driver=existing_driver,
  685                                           extra={'test': 'one'},
  686                                           instance_uuid=None)
  687         # check that it fails because driver not found
  688         node.driver = wrong_driver
  689         node.driver_info = {}
  690         exc = self.assertRaises(messaging.rpc.ExpectedException,
  691                                 self.service.update_node,
  692                                 self.context, node)
  693         self.assertEqual(exception.DriverNotFound, exc.exc_info[0])
  694 
  695         # verify change did not happen
  696         node.refresh()
  697         self.assertEqual(existing_driver, node.driver)
  698 
  699     def test_update_node_from_invalid_driver(self):
  700         existing_driver = 'fake-hardware'
  701         wrong_driver = 'wrong-driver'
  702         node = obj_utils.create_test_node(self.context, driver=wrong_driver)
  703         node.driver = existing_driver
  704         result = self.service.update_node(self.context, node)
  705         self.assertEqual(existing_driver, result.driver)
  706         node.refresh()
  707         self.assertEqual(existing_driver, node.driver)
  708 
  709     UpdateInterfaces = namedtuple('UpdateInterfaces', ('old', 'new'))
  710     # NOTE(dtantsur): "old" interfaces here do not match the defaults, so that
  711     # we can test resetting them.
  712     IFACE_UPDATE_DICT = {
  713         'boot_interface': UpdateInterfaces('pxe', 'fake'),
  714         'console_interface': UpdateInterfaces('no-console', 'fake'),
  715         'deploy_interface': UpdateInterfaces('iscsi', 'fake'),
  716         'inspect_interface': UpdateInterfaces('no-inspect', 'fake'),
  717         'management_interface': UpdateInterfaces(None, 'fake'),
  718         'network_interface': UpdateInterfaces('noop', 'flat'),
  719         'power_interface': UpdateInterfaces(None, 'fake'),
  720         'raid_interface': UpdateInterfaces('no-raid', 'fake'),
  721         'rescue_interface': UpdateInterfaces('no-rescue', 'fake'),
  722         'storage_interface': UpdateInterfaces('fake', 'noop'),
  723     }
  724 
  725     def _create_node_with_interfaces(self, prov_state, maintenance=False):
  726         old_ifaces = {}
  727         for iface_name, ifaces in self.IFACE_UPDATE_DICT.items():
  728             old_ifaces[iface_name] = ifaces.old
  729         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
  730                                           uuid=uuidutils.generate_uuid(),
  731                                           provision_state=prov_state,
  732                                           maintenance=maintenance,
  733                                           **old_ifaces)
  734         return node
  735 
  736     def _test_update_node_interface_allowed(self, node, iface_name, new_iface):
  737         setattr(node, iface_name, new_iface)
  738         self.service.update_node(self.context, node)
  739         node.refresh()
  740         self.assertEqual(new_iface, getattr(node, iface_name))
  741 
  742     def _test_update_node_interface_in_allowed_state(self, prov_state,
  743                                                      maintenance=False):
  744         node = self._create_node_with_interfaces(prov_state,
  745                                                  maintenance=maintenance)
  746         for iface_name, ifaces in self.IFACE_UPDATE_DICT.items():
  747             self._test_update_node_interface_allowed(node, iface_name,
  748                                                      ifaces.new)
  749         node.destroy()
  750 
  751     def test_update_node_interface_in_allowed_state(self):
  752         for state in [states.ENROLL, states.MANAGEABLE, states.INSPECTING,
  753                       states.INSPECTWAIT, states.AVAILABLE]:
  754             self._test_update_node_interface_in_allowed_state(state)
  755 
  756     def test_update_node_interface_in_maintenance(self):
  757         self._test_update_node_interface_in_allowed_state(states.ACTIVE,
  758                                                           maintenance=True)
  759 
  760     def _test_update_node_interface_not_allowed(self, node, iface_name,
  761                                                 new_iface):
  762         old_iface = getattr(node, iface_name)
  763         setattr(node, iface_name, new_iface)
  764         exc = self.assertRaises(messaging.rpc.ExpectedException,
  765                                 self.service.update_node,
  766                                 self.context, node)
  767         self.assertEqual(exception.InvalidState, exc.exc_info[0])
  768         node.refresh()
  769         self.assertEqual(old_iface, getattr(node, iface_name))
  770 
  771     def _test_update_node_interface_in_not_allowed_state(self, prov_state):
  772         node = self._create_node_with_interfaces(prov_state)
  773         for iface_name, ifaces in self.IFACE_UPDATE_DICT.items():
  774             self._test_update_node_interface_not_allowed(node, iface_name,
  775                                                          ifaces.new)
  776         node.destroy()
  777 
  778     def test_update_node_interface_in_not_allowed_state(self):
  779         for state in [states.ACTIVE, states.DELETING]:
  780             self._test_update_node_interface_in_not_allowed_state(state)
  781 
  782     def _test_update_node_interface_invalid(self, node, iface_name):
  783         old_iface = getattr(node, iface_name)
  784         setattr(node, iface_name, 'invalid')
  785         exc = self.assertRaises(messaging.rpc.ExpectedException,
  786                                 self.service.update_node,
  787                                 self.context, node)
  788         self.assertEqual(exception.InterfaceNotFoundInEntrypoint,
  789                          exc.exc_info[0])
  790         node.refresh()
  791         self.assertEqual(old_iface, getattr(node, iface_name))
  792 
  793     def test_update_node_interface_invalid(self):
  794         node = self._create_node_with_interfaces(states.MANAGEABLE)
  795         for iface_name in self.IFACE_UPDATE_DICT:
  796             self._test_update_node_interface_invalid(node, iface_name)
  797 
  798     def test_update_node_with_reset_interfaces(self):
  799         # Modify only one interface at a time
  800         for iface_name, ifaces in self.IFACE_UPDATE_DICT.items():
  801             node = self._create_node_with_interfaces(states.AVAILABLE)
  802             setattr(node, iface_name, ifaces.new)
  803             # Updating a driver is mandatory for reset_interfaces to work
  804             node.driver = 'fake-hardware'
  805             self.service.update_node(self.context, node,
  806                                      reset_interfaces=True)
  807             node.refresh()
  808             self.assertEqual(ifaces.new, getattr(node, iface_name))
  809             # Other interfaces must be reset to their defaults
  810             for other_iface_name, ifaces in self.IFACE_UPDATE_DICT.items():
  811                 if other_iface_name == iface_name:
  812                     continue
  813                 # For this to work, the "old" interfaces in IFACE_UPDATE_DICT
  814                 # must not match the defaults.
  815                 self.assertNotEqual(ifaces.old,
  816                                     getattr(node, other_iface_name),
  817                                     "%s does not match the default after "
  818                                     "reset with setting %s: %s" %
  819                                     (other_iface_name, iface_name,
  820                                      getattr(node, other_iface_name)))
  821 
  822     def _test_update_node_change_resource_class(self, state,
  823                                                 resource_class=None,
  824                                                 new_resource_class='new',
  825                                                 expect_error=False,
  826                                                 maintenance=False):
  827         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
  828                                           uuid=uuidutils.generate_uuid(),
  829                                           provision_state=state,
  830                                           resource_class=resource_class,
  831                                           maintenance=maintenance)
  832         self.addCleanup(node.destroy)
  833 
  834         node.resource_class = new_resource_class
  835         if expect_error:
  836             exc = self.assertRaises(messaging.rpc.ExpectedException,
  837                                     self.service.update_node,
  838                                     self.context,
  839                                     node)
  840             # Compare true exception hidden by @messaging.expected_exceptions
  841             self.assertEqual(exception.InvalidState, exc.exc_info[0])
  842 
  843             expected_msg_regex = \
  844                 (r'^Node {} can not have resource_class updated unless it is '
  845                  r'in one of allowed \(.*\) states.$').format(
  846                     re.escape(node.uuid))
  847             self.assertRegex(str(exc.exc_info[1]), expected_msg_regex)
  848 
  849             # verify change did not happen
  850             res = objects.Node.get_by_uuid(self.context, node['uuid'])
  851             self.assertEqual(resource_class, res['resource_class'])
  852         else:
  853             self.service.update_node(self.context, node)
  854 
  855             res = objects.Node.get_by_uuid(self.context, node['uuid'])
  856             self.assertEqual('new', res['resource_class'])
  857 
  858     def test_update_resource_class_allowed_state(self):
  859         for state in [states.ENROLL, states.MANAGEABLE, states.INSPECTING,
  860                       states.AVAILABLE]:
  861             self._test_update_node_change_resource_class(
  862                 state, resource_class='old', expect_error=False)
  863 
  864     def test_update_resource_class_no_previous_value(self):
  865         for state in [states.ENROLL, states.MANAGEABLE, states.INSPECTING,
  866                       states.AVAILABLE, states.ACTIVE]:
  867             self._test_update_node_change_resource_class(
  868                 state, resource_class=None, expect_error=False)
  869 
  870     def test_update_resource_class_not_allowed(self):
  871         self._test_update_node_change_resource_class(
  872             states.ACTIVE, resource_class='old', new_resource_class='new',
  873             expect_error=True)
  874         self._test_update_node_change_resource_class(
  875             states.ACTIVE, resource_class='old', new_resource_class=None,
  876             expect_error=True)
  877         self._test_update_node_change_resource_class(
  878             states.ACTIVE, resource_class='old', new_resource_class=None,
  879             expect_error=True, maintenance=True)
  880 
  881     def test_update_node_hardware_type(self):
  882         existing_hardware = 'fake-hardware'
  883         existing_interface = 'fake'
  884         new_hardware = 'manual-management'
  885         new_interface = 'pxe'
  886         node = obj_utils.create_test_node(self.context,
  887                                           driver=existing_hardware,
  888                                           boot_interface=existing_interface)
  889         node.driver = new_hardware
  890         node.boot_interface = new_interface
  891         self.service.update_node(self.context, node)
  892         node.refresh()
  893         self.assertEqual(new_hardware, node.driver)
  894         self.assertEqual(new_interface, node.boot_interface)
  895 
  896     def test_update_node_deleting_allocation(self):
  897         node = obj_utils.create_test_node(self.context)
  898         alloc = obj_utils.create_test_allocation(self.context)
  899         # Establish cross-linking between the node and the allocation
  900         alloc.node_id = node.id
  901         alloc.save()
  902         node.refresh()
  903         self.assertEqual(alloc.id, node.allocation_id)
  904         self.assertEqual(alloc.uuid, node.instance_uuid)
  905 
  906         node.instance_uuid = None
  907         res = self.service.update_node(self.context, node)
  908         self.assertRaises(exception.AllocationNotFound,
  909                           objects.Allocation.get_by_id,
  910                           self.context, alloc.id)
  911         self.assertIsNone(res['instance_uuid'])
  912         self.assertIsNone(res['allocation_id'])
  913 
  914         node.refresh()
  915         self.assertIsNone(node.instance_uuid)
  916         self.assertIsNone(node.allocation_id)
  917 
  918     def test_update_node_deleting_allocation_forbidden(self):
  919         node = obj_utils.create_test_node(self.context,
  920                                           provision_state='active',
  921                                           maintenance=False)
  922         alloc = obj_utils.create_test_allocation(self.context)
  923         # Establish cross-linking between the node and the allocation
  924         alloc.node_id = node.id
  925         alloc.save()
  926         node.refresh()
  927         self.assertEqual(alloc.id, node.allocation_id)
  928         self.assertEqual(alloc.uuid, node.instance_uuid)
  929 
  930         node.instance_uuid = None
  931         exc = self.assertRaises(messaging.rpc.ExpectedException,
  932                                 self.service.update_node,
  933                                 self.context, node)
  934         self.assertEqual(exception.InvalidState, exc.exc_info[0])
  935 
  936         node.refresh()
  937         self.assertEqual(alloc.id, node.allocation_id)
  938         self.assertEqual(alloc.uuid, node.instance_uuid)
  939 
  940     def test_update_node_deleting_allocation_in_maintenance(self):
  941         node = obj_utils.create_test_node(self.context,
  942                                           provision_state='active',
  943                                           maintenance=True)
  944         alloc = obj_utils.create_test_allocation(self.context)
  945         # Establish cross-linking between the node and the allocation
  946         alloc.node_id = node.id
  947         alloc.save()
  948         node.refresh()
  949         self.assertEqual(alloc.id, node.allocation_id)
  950         self.assertEqual(alloc.uuid, node.instance_uuid)
  951 
  952         node.instance_uuid = None
  953         res = self.service.update_node(self.context, node)
  954         self.assertRaises(exception.AllocationNotFound,
  955                           objects.Allocation.get_by_id,
  956                           self.context, alloc.id)
  957         self.assertIsNone(res['instance_uuid'])
  958         self.assertIsNone(res['allocation_id'])
  959 
  960         node.refresh()
  961         self.assertIsNone(node.instance_uuid)
  962         self.assertIsNone(node.allocation_id)
  963 
  964     def test_update_node_maintenance_with_broken_interface(self):
  965         # Updates of non-driver fields are possible with a broken driver
  966         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
  967                                           power_interface='foobar',
  968                                           extra={'test': 'one'})
  969 
  970         node.maintenance = True
  971         res = self.service.update_node(self.context, node)
  972         self.assertTrue(res.maintenance)
  973 
  974         node.refresh()
  975         self.assertTrue(node.maintenance)
  976         self.assertEqual('foobar', node.power_interface)
  977 
  978     def test_update_node_interface_field_with_broken_interface(self):
  979         # Updates of driver fields are NOT possible with a broken driver,
  980         # unless they're fixing the breakage.
  981         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
  982                                           power_interface='foobar',
  983                                           deploy_interface='fake',
  984                                           extra={'test': 'one'})
  985 
  986         node.deploy_interface = 'iscsi'
  987         exc = self.assertRaises(messaging.rpc.ExpectedException,
  988                                 self.service.update_node,
  989                                 self.context, node)
  990         self.assertEqual(exception.InterfaceNotFoundInEntrypoint,
  991                          exc.exc_info[0])
  992 
  993         node.refresh()
  994         self.assertEqual('foobar', node.power_interface)
  995         self.assertEqual('fake', node.deploy_interface)
  996 
  997     def test_update_node_fix_broken_interface(self):
  998         # Updates of non-driver fields are possible with a broken driver
  999         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 1000                                           power_interface='foobar',
 1001                                           extra={'test': 'one'})
 1002 
 1003         node.power_interface = 'fake'
 1004         self.service.update_node(self.context, node)
 1005 
 1006         node.refresh()
 1007         self.assertEqual('fake', node.power_interface)
 1008 
 1009 
 1010 @mgr_utils.mock_record_keepalive
 1011 class VendorPassthruTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
 1012 
 1013     @mock.patch.object(task_manager.TaskManager, 'upgrade_lock', autospec=True)
 1014     @mock.patch.object(task_manager.TaskManager, 'spawn_after', autospec=True)
 1015     def test_vendor_passthru_async(self, mock_spawn,
 1016                                    mock_upgrade):
 1017         node = obj_utils.create_test_node(self.context,
 1018                                           vendor_interface='fake')
 1019         info = {'bar': 'baz'}
 1020         self._start_service()
 1021 
 1022         response = self.service.vendor_passthru(self.context, node.uuid,
 1023                                                 'second_method', 'POST',
 1024                                                 info)
 1025         # Waiting to make sure the below assertions are valid.
 1026         self._stop_service()
 1027 
 1028         # Assert spawn_after was called
 1029         self.assertTrue(mock_spawn.called)
 1030         self.assertIsNone(response['return'])
 1031         self.assertTrue(response['async'])
 1032 
 1033         # Assert lock was upgraded to an exclusive one
 1034         self.assertEqual(1, mock_upgrade.call_count)
 1035 
 1036         node.refresh()
 1037         self.assertIsNone(node.last_error)
 1038         # Verify reservation has been cleared.
 1039         self.assertIsNone(node.reservation)
 1040 
 1041     @mock.patch.object(task_manager.TaskManager, 'upgrade_lock', autospec=True)
 1042     @mock.patch.object(task_manager.TaskManager, 'spawn_after', autospec=True)
 1043     def test_vendor_passthru_sync(self, mock_spawn, mock_upgrade):
 1044         node = obj_utils.create_test_node(self.context, driver='fake-hardware')
 1045         info = {'bar': 'meow'}
 1046         self._start_service()
 1047 
 1048         response = self.service.vendor_passthru(self.context, node.uuid,
 1049                                                 'third_method_sync',
 1050                                                 'POST', info)
 1051         # Waiting to make sure the below assertions are valid.
 1052         self._stop_service()
 1053 
 1054         # Assert no workers were used
 1055         self.assertFalse(mock_spawn.called)
 1056         self.assertTrue(response['return'])
 1057         self.assertFalse(response['async'])
 1058 
 1059         # Assert lock was upgraded to an exclusive one
 1060         self.assertEqual(1, mock_upgrade.call_count)
 1061 
 1062         node.refresh()
 1063         self.assertIsNone(node.last_error)
 1064         # Verify reservation has been cleared.
 1065         self.assertIsNone(node.reservation)
 1066 
 1067     @mock.patch.object(task_manager.TaskManager, 'upgrade_lock', autospec=True)
 1068     @mock.patch.object(task_manager.TaskManager, 'spawn_after', autospec=True)
 1069     def test_vendor_passthru_shared_lock(self, mock_spawn, mock_upgrade):
 1070         node = obj_utils.create_test_node(self.context, driver='fake-hardware')
 1071         info = {'bar': 'woof'}
 1072         self._start_service()
 1073 
 1074         response = self.service.vendor_passthru(self.context, node.uuid,
 1075                                                 'fourth_method_shared_lock',
 1076                                                 'POST', info)
 1077         # Waiting to make sure the below assertions are valid.
 1078         self._stop_service()
 1079 
 1080         # Assert spawn_after was called
 1081         self.assertTrue(mock_spawn.called)
 1082         self.assertIsNone(response['return'])
 1083         self.assertTrue(response['async'])
 1084 
 1085         # Assert lock was never upgraded to an exclusive one
 1086         self.assertFalse(mock_upgrade.called)
 1087 
 1088         node.refresh()
 1089         self.assertIsNone(node.last_error)
 1090         # Verify there's no reservation on the node
 1091         self.assertIsNone(node.reservation)
 1092 
 1093     def test_vendor_passthru_http_method_not_supported(self):
 1094         node = obj_utils.create_test_node(self.context, driver='fake-hardware')
 1095         self._start_service()
 1096 
 1097         # GET not supported by first_method
 1098         exc = self.assertRaises(messaging.rpc.ExpectedException,
 1099                                 self.service.vendor_passthru,
 1100                                 self.context, node.uuid,
 1101                                 'second_method', 'GET', {})
 1102         # Compare true exception hidden by @messaging.expected_exceptions
 1103         self.assertEqual(exception.InvalidParameterValue, exc.exc_info[0])
 1104 
 1105         node.refresh()
 1106         self.assertIsNone(node.last_error)
 1107         # Verify reservation has been cleared.
 1108         self.assertIsNone(node.reservation)
 1109 
 1110     def test_vendor_passthru_node_already_locked(self):
 1111         fake_reservation = 'test_reserv'
 1112         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 1113                                           reservation=fake_reservation)
 1114         info = {'bar': 'baz'}
 1115         self._start_service()
 1116 
 1117         exc = self.assertRaises(messaging.rpc.ExpectedException,
 1118                                 self.service.vendor_passthru,
 1119                                 self.context, node.uuid, 'second_method',
 1120                                 'POST', info)
 1121         # Compare true exception hidden by @messaging.expected_exceptions
 1122         self.assertEqual(exception.NodeLocked, exc.exc_info[0])
 1123 
 1124         node.refresh()
 1125         self.assertIsNone(node.last_error)
 1126         # Verify the existing reservation is not broken.
 1127         self.assertEqual(fake_reservation, node.reservation)
 1128 
 1129     def test_vendor_passthru_unsupported_method(self):
 1130         node = obj_utils.create_test_node(self.context, driver='fake-hardware')
 1131         info = {'bar': 'baz'}
 1132         self._start_service()
 1133 
 1134         exc = self.assertRaises(messaging.rpc.ExpectedException,
 1135                                 self.service.vendor_passthru,
 1136                                 self.context, node.uuid,
 1137                                 'unsupported_method', 'POST', info)
 1138         # Compare true exception hidden by @messaging.expected_exceptions
 1139         self.assertEqual(exception.InvalidParameterValue,
 1140                          exc.exc_info[0])
 1141 
 1142         node.refresh()
 1143         self.assertIsNone(node.last_error)
 1144         # Verify reservation has been cleared.
 1145         self.assertIsNone(node.reservation)
 1146 
 1147     def test_vendor_passthru_missing_method_parameters(self):
 1148         node = obj_utils.create_test_node(self.context, driver='fake-hardware')
 1149         info = {'invalid_param': 'whatever'}
 1150         self._start_service()
 1151 
 1152         exc = self.assertRaises(messaging.rpc.ExpectedException,
 1153                                 self.service.vendor_passthru,
 1154                                 self.context, node.uuid,
 1155                                 'second_method', 'POST', info)
 1156         # Compare true exception hidden by @messaging.expected_exceptions
 1157         self.assertEqual(exception.MissingParameterValue, exc.exc_info[0])
 1158 
 1159         node.refresh()
 1160         self.assertIsNone(node.last_error)
 1161         # Verify reservation has been cleared.
 1162         self.assertIsNone(node.reservation)
 1163 
 1164     def test_vendor_passthru_worker_pool_full(self):
 1165         node = obj_utils.create_test_node(self.context, driver='fake-hardware')
 1166         info = {'bar': 'baz'}
 1167         self._start_service()
 1168 
 1169         with mock.patch.object(self.service,
 1170                                '_spawn_worker', autospec=True) as spawn_mock:
 1171             spawn_mock.side_effect = exception.NoFreeConductorWorker()
 1172 
 1173             exc = self.assertRaises(messaging.rpc.ExpectedException,
 1174                                     self.service.vendor_passthru,
 1175                                     self.context, node.uuid,
 1176                                     'second_method', 'POST', info)
 1177             # Compare true exception hidden by @messaging.expected_exceptions
 1178             self.assertEqual(exception.NoFreeConductorWorker, exc.exc_info[0])
 1179 
 1180             # Waiting to make sure the below assertions are valid.
 1181             self._stop_service()
 1182 
 1183             node.refresh()
 1184             self.assertIsNone(node.last_error)
 1185             # Verify reservation has been cleared.
 1186             self.assertIsNone(node.reservation)
 1187 
 1188     @mock.patch.object(driver_factory, 'get_interface', autospec=True)
 1189     def test_get_node_vendor_passthru_methods(self, mock_iface):
 1190         fake_routes = {'test_method': {'async': True,
 1191                                        'description': 'foo',
 1192                                        'http_methods': ['POST'],
 1193                                        'func': None}}
 1194         mock_iface.return_value.vendor_routes = fake_routes
 1195         node = obj_utils.create_test_node(self.context, driver='fake-hardware')
 1196         self._start_service()
 1197 
 1198         data = self.service.get_node_vendor_passthru_methods(self.context,
 1199                                                              node.uuid)
 1200         # The function reference should not be returned
 1201         del fake_routes['test_method']['func']
 1202         self.assertEqual(fake_routes, data)
 1203 
 1204     @mock.patch.object(driver_factory, 'get_interface', autospec=True)
 1205     @mock.patch.object(manager.ConductorManager, '_spawn_worker',
 1206                        autospec=True)
 1207     def test_driver_vendor_passthru_sync(self, mock_spawn, mock_get_if):
 1208         expected = {'foo': 'bar'}
 1209         vendor_mock = mock.Mock(spec=drivers_base.VendorInterface)
 1210         mock_get_if.return_value = vendor_mock
 1211         driver_name = 'fake-hardware'
 1212         test_method = mock.MagicMock(return_value=expected)
 1213         vendor_mock.driver_routes = {
 1214             'test_method': {'func': test_method,
 1215                             'async': False,
 1216                             'attach': False,
 1217                             'http_methods': ['POST']}}
 1218         self.service.init_host()
 1219         # init_host() called _spawn_worker because of the heartbeat
 1220         mock_spawn.reset_mock()
 1221         # init_host() called get_interface during driver loading
 1222         mock_get_if.reset_mock()
 1223 
 1224         vendor_args = {'test': 'arg'}
 1225         response = self.service.driver_vendor_passthru(
 1226             self.context, driver_name, 'test_method', 'POST', vendor_args)
 1227 
 1228         # Assert that the vendor interface has no custom
 1229         # driver_vendor_passthru()
 1230         self.assertFalse(hasattr(vendor_mock, 'driver_vendor_passthru'))
 1231         self.assertEqual(expected, response['return'])
 1232         self.assertFalse(response['async'])
 1233         test_method.assert_called_once_with(self.context, **vendor_args)
 1234         # No worker was spawned
 1235         self.assertFalse(mock_spawn.called)
 1236         mock_get_if.assert_called_once_with(mock.ANY, 'vendor', 'fake')
 1237 
 1238     @mock.patch.object(driver_factory, 'get_interface', autospec=True)
 1239     @mock.patch.object(manager.ConductorManager, '_spawn_worker',
 1240                        autospec=True)
 1241     def test_driver_vendor_passthru_async(self, mock_spawn, mock_iface):
 1242         test_method = mock.MagicMock()
 1243         mock_iface.return_value.driver_routes = {
 1244             'test_sync_method': {'func': test_method,
 1245                                  'async': True,
 1246                                  'attach': False,
 1247                                  'http_methods': ['POST']}}
 1248         self.service.init_host()
 1249         # init_host() called _spawn_worker because of the heartbeat
 1250         mock_spawn.reset_mock()
 1251 
 1252         vendor_args = {'test': 'arg'}
 1253         response = self.service.driver_vendor_passthru(
 1254             self.context, 'fake-hardware', 'test_sync_method', 'POST',
 1255             vendor_args)
 1256 
 1257         self.assertIsNone(response['return'])
 1258         self.assertTrue(response['async'])
 1259         mock_spawn.assert_called_once_with(self.service, test_method,
 1260                                            self.context, **vendor_args)
 1261 
 1262     @mock.patch.object(driver_factory, 'get_interface', autospec=True)
 1263     def test_driver_vendor_passthru_http_method_not_supported(self,
 1264                                                               mock_iface):
 1265         mock_iface.return_value.driver_routes = {
 1266             'test_method': {'func': mock.MagicMock(),
 1267                             'async': True,
 1268                             'http_methods': ['POST']}}
 1269         self.service.init_host()
 1270         # GET not supported by test_method
 1271         exc = self.assertRaises(messaging.ExpectedException,
 1272                                 self.service.driver_vendor_passthru,
 1273                                 self.context, 'fake-hardware', 'test_method',
 1274                                 'GET', {})
 1275         # Compare true exception hidden by @messaging.expected_exceptions
 1276         self.assertEqual(exception.InvalidParameterValue,
 1277                          exc.exc_info[0])
 1278 
 1279     def test_driver_vendor_passthru_method_not_supported(self):
 1280         # Test for when the vendor interface is set, but hasn't passed a
 1281         # driver_passthru_mapping to MixinVendorInterface
 1282         self.service.init_host()
 1283         exc = self.assertRaises(messaging.ExpectedException,
 1284                                 self.service.driver_vendor_passthru,
 1285                                 self.context, 'fake-hardware', 'test_method',
 1286                                 'POST', {})
 1287         # Compare true exception hidden by @messaging.expected_exceptions
 1288         self.assertEqual(exception.InvalidParameterValue,
 1289                          exc.exc_info[0])
 1290 
 1291     def test_driver_vendor_passthru_driver_not_found(self):
 1292         self.service.init_host()
 1293         self.assertRaises(messaging.ExpectedException,
 1294                           self.service.driver_vendor_passthru,
 1295                           self.context, 'does_not_exist', 'test_method',
 1296                           'POST', {})
 1297 
 1298     @mock.patch.object(driver_factory, 'default_interface', autospec=True)
 1299     def test_driver_vendor_passthru_no_default_interface(self,
 1300                                                          mock_def_iface):
 1301         self.service.init_host()
 1302         # NOTE(rloo): service.init_host() will call
 1303         #             driver_factory.default_interface() and we want these to
 1304         #             succeed, so we set the side effect *after* that call.
 1305         mock_def_iface.reset_mock()
 1306         mock_def_iface.side_effect = exception.NoValidDefaultForInterface('no')
 1307         exc = self.assertRaises(messaging.ExpectedException,
 1308                                 self.service.driver_vendor_passthru,
 1309                                 self.context, 'fake-hardware', 'test_method',
 1310                                 'POST', {})
 1311         mock_def_iface.assert_called_once_with(mock.ANY, 'vendor',
 1312                                                driver_name='fake-hardware')
 1313         # Compare true exception hidden by @messaging.expected_exceptions
 1314         self.assertEqual(exception.NoValidDefaultForInterface,
 1315                          exc.exc_info[0])
 1316 
 1317     @mock.patch.object(driver_factory, 'get_interface', autospec=True)
 1318     def test_get_driver_vendor_passthru_methods(self, mock_get_if):
 1319         vendor_mock = mock.Mock(spec=drivers_base.VendorInterface)
 1320         mock_get_if.return_value = vendor_mock
 1321         driver_name = 'fake-hardware'
 1322         fake_routes = {'test_method': {'async': True,
 1323                                        'description': 'foo',
 1324                                        'http_methods': ['POST'],
 1325                                        'func': None}}
 1326         vendor_mock.driver_routes = fake_routes
 1327         self.service.init_host()
 1328 
 1329         # init_host() will call get_interface
 1330         mock_get_if.reset_mock()
 1331 
 1332         data = self.service.get_driver_vendor_passthru_methods(self.context,
 1333                                                                driver_name)
 1334         # The function reference should not be returned
 1335         del fake_routes['test_method']['func']
 1336         self.assertEqual(fake_routes, data)
 1337 
 1338         mock_get_if.assert_called_once_with(mock.ANY, 'vendor', 'fake')
 1339 
 1340     @mock.patch.object(driver_factory, 'default_interface', autospec=True)
 1341     def test_get_driver_vendor_passthru_methods_no_default_interface(
 1342             self, mock_def_iface):
 1343         self.service.init_host()
 1344         # NOTE(rloo): service.init_host() will call
 1345         #             driver_factory.default_interface() and we want these to
 1346         #             succeed, so we set the side effect *after* that call.
 1347         mock_def_iface.reset_mock()
 1348         mock_def_iface.side_effect = exception.NoValidDefaultForInterface('no')
 1349         exc = self.assertRaises(
 1350             messaging.rpc.ExpectedException,
 1351             self.service.get_driver_vendor_passthru_methods,
 1352             self.context, 'fake-hardware')
 1353         mock_def_iface.assert_called_once_with(mock.ANY, 'vendor',
 1354                                                driver_name='fake-hardware')
 1355         # Compare true exception hidden by @messaging.expected_exceptions
 1356         self.assertEqual(exception.NoValidDefaultForInterface,
 1357                          exc.exc_info[0])
 1358 
 1359     @mock.patch.object(driver_factory, 'get_interface', autospec=True)
 1360     def test_driver_vendor_passthru_validation_failed(self, mock_iface):
 1361         mock_iface.return_value.driver_validate.side_effect = (
 1362             exception.MissingParameterValue('error'))
 1363         test_method = mock.Mock()
 1364         mock_iface.return_value.driver_routes = {
 1365             'test_method': {'func': test_method,
 1366                             'async': False,
 1367                             'http_methods': ['POST']}}
 1368         self.service.init_host()
 1369         exc = self.assertRaises(messaging.ExpectedException,
 1370                                 self.service.driver_vendor_passthru,
 1371                                 self.context, 'fake-hardware', 'test_method',
 1372                                 'POST', {})
 1373         self.assertEqual(exception.MissingParameterValue,
 1374                          exc.exc_info[0])
 1375         self.assertFalse(test_method.called)
 1376 
 1377 
 1378 @mgr_utils.mock_record_keepalive
 1379 @mock.patch.object(images, 'is_whole_disk_image', autospec=True)
 1380 class ServiceDoNodeDeployTestCase(mgr_utils.ServiceSetUpMixin,
 1381                                   db_base.DbTestCase):
 1382     def test_do_node_deploy_invalid_state(self, mock_iwdi):
 1383         mock_iwdi.return_value = False
 1384         self._start_service()
 1385         # test that node deploy fails if the node is already provisioned
 1386         node = obj_utils.create_test_node(
 1387             self.context, driver='fake-hardware',
 1388             provision_state=states.ACTIVE,
 1389             target_provision_state=states.NOSTATE)
 1390         exc = self.assertRaises(messaging.rpc.ExpectedException,
 1391                                 self.service.do_node_deploy,
 1392                                 self.context, node['uuid'])
 1393         # Compare true exception hidden by @messaging.expected_exceptions
 1394         self.assertEqual(exception.InvalidStateRequested, exc.exc_info[0])
 1395         # This is a sync operation last_error should be None.
 1396         self.assertIsNone(node.last_error)
 1397         # Verify reservation has been cleared.
 1398         self.assertIsNone(node.reservation)
 1399         self.assertFalse(mock_iwdi.called)
 1400         self.assertNotIn('is_whole_disk_image', node.driver_internal_info)
 1401 
 1402     def test_do_node_deploy_maintenance(self, mock_iwdi):
 1403         mock_iwdi.return_value = False
 1404         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 1405                                           maintenance=True)
 1406         exc = self.assertRaises(messaging.rpc.ExpectedException,
 1407                                 self.service.do_node_deploy,
 1408                                 self.context, node['uuid'])
 1409         # Compare true exception hidden by @messaging.expected_exceptions
 1410         self.assertEqual(exception.NodeInMaintenance, exc.exc_info[0])
 1411         # This is a sync operation last_error should be None.
 1412         self.assertIsNone(node.last_error)
 1413         # Verify reservation has been cleared.
 1414         self.assertIsNone(node.reservation)
 1415         self.assertFalse(mock_iwdi.called)
 1416 
 1417     def _test_do_node_deploy_validate_fail(self, mock_validate, mock_iwdi):
 1418         mock_iwdi.return_value = False
 1419         # InvalidParameterValue should be re-raised as InstanceDeployFailure
 1420         mock_validate.side_effect = exception.InvalidParameterValue('error')
 1421         node = obj_utils.create_test_node(self.context, driver='fake-hardware')
 1422         exc = self.assertRaises(messaging.rpc.ExpectedException,
 1423                                 self.service.do_node_deploy,
 1424                                 self.context, node.uuid)
 1425         # Compare true exception hidden by @messaging.expected_exceptions
 1426         self.assertEqual(exception.InstanceDeployFailure, exc.exc_info[0])
 1427         self.assertEqual(exc.exc_info[1].code, 400)
 1428         # Check the message of InstanceDeployFailure. In a
 1429         # messaging.rpc.ExpectedException sys.exc_info() is stored in exc_info
 1430         # in the exception object. So InstanceDeployFailure will be in
 1431         # exc_info[1]
 1432         self.assertIn(r'node 1be26c0b-03f2-4d2e-ae87-c02d7f33c123',
 1433                       str(exc.exc_info[1]))
 1434         # This is a sync operation last_error should be None.
 1435         self.assertIsNone(node.last_error)
 1436         # Verify reservation has been cleared.
 1437         self.assertIsNone(node.reservation)
 1438         mock_iwdi.assert_called_once_with(self.context, node.instance_info)
 1439         self.assertNotIn('is_whole_disk_image', node.driver_internal_info)
 1440 
 1441     @mock.patch('ironic.drivers.modules.fake.FakeDeploy.validate',
 1442                 autospec=True)
 1443     def test_do_node_deploy_validate_fail(self, mock_validate, mock_iwdi):
 1444         self._test_do_node_deploy_validate_fail(mock_validate, mock_iwdi)
 1445 
 1446     @mock.patch('ironic.drivers.modules.fake.FakePower.validate',
 1447                 autospec=True)
 1448     def test_do_node_deploy_power_validate_fail(self, mock_validate,
 1449                                                 mock_iwdi):
 1450         self._test_do_node_deploy_validate_fail(mock_validate, mock_iwdi)
 1451 
 1452     @mock.patch.object(conductor_utils, 'validate_instance_info_traits',
 1453                        autospec=True)
 1454     def test_do_node_deploy_traits_validate_fail(self, mock_validate,
 1455                                                  mock_iwdi):
 1456         self._test_do_node_deploy_validate_fail(mock_validate, mock_iwdi)
 1457 
 1458     @mock.patch.object(conductor_steps, 'validate_deploy_templates',
 1459                        autospec=True)
 1460     def test_do_node_deploy_validate_template_fail(self, mock_validate,
 1461                                                    mock_iwdi):
 1462         self._test_do_node_deploy_validate_fail(mock_validate, mock_iwdi)
 1463 
 1464     def test_do_node_deploy_partial_ok(self, mock_iwdi):
 1465         mock_iwdi.return_value = False
 1466         self._start_service()
 1467         thread = self.service._spawn_worker(lambda: None)
 1468         with mock.patch.object(self.service, '_spawn_worker',
 1469                                autospec=True) as mock_spawn:
 1470             mock_spawn.return_value = thread
 1471 
 1472             node = obj_utils.create_test_node(
 1473                 self.context,
 1474                 driver='fake-hardware',
 1475                 provision_state=states.AVAILABLE,
 1476                 driver_internal_info={'agent_url': 'url'})
 1477 
 1478             self.service.do_node_deploy(self.context, node.uuid)
 1479             self._stop_service()
 1480             node.refresh()
 1481             self.assertEqual(states.DEPLOYING, node.provision_state)
 1482             self.assertEqual(states.ACTIVE, node.target_provision_state)
 1483             # This is a sync operation last_error should be None.
 1484             self.assertIsNone(node.last_error)
 1485             # Verify reservation has been cleared.
 1486             self.assertIsNone(node.reservation)
 1487             mock_spawn.assert_called_once_with(mock.ANY, mock.ANY,
 1488                                                mock.ANY, None)
 1489             mock_iwdi.assert_called_once_with(self.context, node.instance_info)
 1490             self.assertFalse(node.driver_internal_info['is_whole_disk_image'])
 1491 
 1492     def test_do_node_deploy_rebuild_active_state_error(self, mock_iwdi):
 1493         # Tests manager.do_node_deploy() & deployments.do_next_deploy_step(),
 1494         # when getting an unexpected state returned from a deploy_step.
 1495         mock_iwdi.return_value = True
 1496         self._start_service()
 1497         # NOTE(rloo): We have to mock this here as opposed to using a
 1498         # decorator. With a decorator, when initialization is done, the
 1499         # mocked deploy() method isn't considered a deploy step. So we defer
 1500         # mock'ing until after the init is done.
 1501         with mock.patch.object(fake.FakeDeploy,
 1502                                'deploy', autospec=True) as mock_deploy:
 1503             mock_deploy.return_value = states.DEPLOYING
 1504             node = obj_utils.create_test_node(
 1505                 self.context, driver='fake-hardware',
 1506                 provision_state=states.ACTIVE,
 1507                 target_provision_state=states.NOSTATE,
 1508                 instance_info={'image_source': uuidutils.generate_uuid(),
 1509                                'kernel': 'aaaa', 'ramdisk': 'bbbb'},
 1510                 driver_internal_info={'is_whole_disk_image': False})
 1511 
 1512             self.service.do_node_deploy(self.context, node.uuid, rebuild=True)
 1513             self._stop_service()
 1514             node.refresh()
 1515             self.assertEqual(states.DEPLOYFAIL, node.provision_state)
 1516             self.assertEqual(states.ACTIVE, node.target_provision_state)
 1517             self.assertIsNotNone(node.last_error)
 1518             # Verify reservation has been cleared.
 1519             self.assertIsNone(node.reservation)
 1520             mock_deploy.assert_called_once_with(mock.ANY, mock.ANY)
 1521             # Verify instance_info values have been cleared.
 1522             self.assertNotIn('kernel', node.instance_info)
 1523             self.assertNotIn('ramdisk', node.instance_info)
 1524             mock_iwdi.assert_called_once_with(self.context, node.instance_info)
 1525             # Verify is_whole_disk_image reflects correct value on rebuild.
 1526             self.assertTrue(node.driver_internal_info['is_whole_disk_image'])
 1527             self.assertIsNone(node.driver_internal_info['deploy_steps'])
 1528 
 1529     def test_do_node_deploy_rebuild_active_state_waiting(self, mock_iwdi):
 1530         mock_iwdi.return_value = False
 1531         self._start_service()
 1532         # NOTE(rloo): We have to mock this here as opposed to using a
 1533         # decorator. With a decorator, when initialization is done, the
 1534         # mocked deploy() method isn't considered a deploy step. So we defer
 1535         # mock'ing until after the init is done.
 1536         with mock.patch.object(fake.FakeDeploy,
 1537                                'deploy', autospec=True) as mock_deploy:
 1538             mock_deploy.return_value = states.DEPLOYWAIT
 1539             node = obj_utils.create_test_node(
 1540                 self.context, driver='fake-hardware',
 1541                 provision_state=states.ACTIVE,
 1542                 target_provision_state=states.NOSTATE,
 1543                 instance_info={'image_source': uuidutils.generate_uuid()})
 1544 
 1545             self.service.do_node_deploy(self.context, node.uuid, rebuild=True)
 1546             self._stop_service()
 1547             node.refresh()
 1548             self.assertEqual(states.DEPLOYWAIT, node.provision_state)
 1549             self.assertEqual(states.ACTIVE, node.target_provision_state)
 1550             # last_error should be None.
 1551             self.assertIsNone(node.last_error)
 1552             # Verify reservation has been cleared.
 1553             self.assertIsNone(node.reservation)
 1554             mock_deploy.assert_called_once_with(mock.ANY, mock.ANY)
 1555             mock_iwdi.assert_called_once_with(self.context, node.instance_info)
 1556             self.assertFalse(node.driver_internal_info['is_whole_disk_image'])
 1557             self.assertEqual(1, len(node.driver_internal_info['deploy_steps']))
 1558 
 1559     def test_do_node_deploy_rebuild_active_state_done(self, mock_iwdi):
 1560         mock_iwdi.return_value = False
 1561         self._start_service()
 1562         # NOTE(rloo): We have to mock this here as opposed to using a
 1563         # decorator. With a decorator, when initialization is done, the
 1564         # mocked deploy() method isn't considered a deploy step. So we defer
 1565         # mock'ing until after the init is done.
 1566         with mock.patch.object(fake.FakeDeploy,
 1567                                'deploy', autospec=True) as mock_deploy:
 1568             mock_deploy.return_value = None
 1569             node = obj_utils.create_test_node(
 1570                 self.context, driver='fake-hardware',
 1571                 provision_state=states.ACTIVE,
 1572                 target_provision_state=states.NOSTATE)
 1573 
 1574             self.service.do_node_deploy(self.context, node.uuid, rebuild=True)
 1575             self._stop_service()
 1576             node.refresh()
 1577             self.assertEqual(states.ACTIVE, node.provision_state)
 1578             self.assertEqual(states.NOSTATE, node.target_provision_state)
 1579             # last_error should be None.
 1580             self.assertIsNone(node.last_error)
 1581             # Verify reservation has been cleared.
 1582             self.assertIsNone(node.reservation)
 1583             mock_deploy.assert_called_once_with(mock.ANY, mock.ANY)
 1584             mock_iwdi.assert_called_once_with(self.context, node.instance_info)
 1585             self.assertFalse(node.driver_internal_info['is_whole_disk_image'])
 1586             self.assertIsNone(node.driver_internal_info['deploy_steps'])
 1587 
 1588     def test_do_node_deploy_rebuild_deployfail_state(self, mock_iwdi):
 1589         mock_iwdi.return_value = False
 1590         self._start_service()
 1591         # NOTE(rloo): We have to mock this here as opposed to using a
 1592         # decorator. With a decorator, when initialization is done, the
 1593         # mocked deploy() method isn't considered a deploy step. So we defer
 1594         # mock'ing until after the init is done.
 1595         with mock.patch.object(fake.FakeDeploy,
 1596                                'deploy', autospec=True) as mock_deploy:
 1597             mock_deploy.return_value = None
 1598             node = obj_utils.create_test_node(
 1599                 self.context, driver='fake-hardware',
 1600                 provision_state=states.DEPLOYFAIL,
 1601                 target_provision_state=states.NOSTATE)
 1602 
 1603             self.service.do_node_deploy(self.context, node.uuid, rebuild=True)
 1604             self._stop_service()
 1605             node.refresh()
 1606             self.assertEqual(states.ACTIVE, node.provision_state)
 1607             self.assertEqual(states.NOSTATE, node.target_provision_state)
 1608             # last_error should be None.
 1609             self.assertIsNone(node.last_error)
 1610             # Verify reservation has been cleared.
 1611             self.assertIsNone(node.reservation)
 1612             mock_deploy.assert_called_once_with(mock.ANY, mock.ANY)
 1613             mock_iwdi.assert_called_once_with(self.context, node.instance_info)
 1614             self.assertFalse(node.driver_internal_info['is_whole_disk_image'])
 1615             self.assertIsNone(node.driver_internal_info['deploy_steps'])
 1616 
 1617     def test_do_node_deploy_rebuild_error_state(self, mock_iwdi):
 1618         mock_iwdi.return_value = False
 1619         self._start_service()
 1620         # NOTE(rloo): We have to mock this here as opposed to using a
 1621         # decorator. With a decorator, when initialization is done, the
 1622         # mocked deploy() method isn't considered a deploy step. So we defer
 1623         # mock'ing until after the init is done.
 1624         with mock.patch.object(fake.FakeDeploy,
 1625                                'deploy', autospec=True) as mock_deploy:
 1626             mock_deploy.return_value = None
 1627             node = obj_utils.create_test_node(
 1628                 self.context, driver='fake-hardware',
 1629                 provision_state=states.ERROR,
 1630                 target_provision_state=states.NOSTATE)
 1631 
 1632             self.service.do_node_deploy(self.context, node.uuid, rebuild=True)
 1633             self._stop_service()
 1634             node.refresh()
 1635             self.assertEqual(states.ACTIVE, node.provision_state)
 1636             self.assertEqual(states.NOSTATE, node.target_provision_state)
 1637             # last_error should be None.
 1638             self.assertIsNone(node.last_error)
 1639             # Verify reservation has been cleared.
 1640             self.assertIsNone(node.reservation)
 1641             mock_deploy.assert_called_once_with(mock.ANY, mock.ANY)
 1642             mock_iwdi.assert_called_once_with(self.context, node.instance_info)
 1643             self.assertFalse(node.driver_internal_info['is_whole_disk_image'])
 1644             self.assertIsNone(node.driver_internal_info['deploy_steps'])
 1645 
 1646     def test_do_node_deploy_rebuild_from_available_state(self, mock_iwdi):
 1647         mock_iwdi.return_value = False
 1648         self._start_service()
 1649         # test node will not rebuild if state is AVAILABLE
 1650         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 1651                                           provision_state=states.AVAILABLE)
 1652         exc = self.assertRaises(messaging.rpc.ExpectedException,
 1653                                 self.service.do_node_deploy,
 1654                                 self.context, node['uuid'], rebuild=True)
 1655         # Compare true exception hidden by @messaging.expected_exceptions
 1656         self.assertEqual(exception.InvalidStateRequested, exc.exc_info[0])
 1657         # Last_error should be None.
 1658         self.assertIsNone(node.last_error)
 1659         # Verify reservation has been cleared.
 1660         self.assertIsNone(node.reservation)
 1661         self.assertFalse(mock_iwdi.called)
 1662         self.assertNotIn('is_whole_disk_image', node.driver_internal_info)
 1663 
 1664     def test_do_node_deploy_rebuild_protected(self, mock_iwdi):
 1665         mock_iwdi.return_value = False
 1666         self._start_service()
 1667         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 1668                                           provision_state=states.ACTIVE,
 1669                                           protected=True)
 1670         exc = self.assertRaises(messaging.rpc.ExpectedException,
 1671                                 self.service.do_node_deploy,
 1672                                 self.context, node['uuid'], rebuild=True)
 1673         # Compare true exception hidden by @messaging.expected_exceptions
 1674         self.assertEqual(exception.NodeProtected, exc.exc_info[0])
 1675         # Last_error should be None.
 1676         self.assertIsNone(node.last_error)
 1677         # Verify reservation has been cleared.
 1678         self.assertIsNone(node.reservation)
 1679         self.assertFalse(mock_iwdi.called)
 1680 
 1681     def test_do_node_deploy_worker_pool_full(self, mock_iwdi):
 1682         mock_iwdi.return_value = False
 1683         prv_state = states.AVAILABLE
 1684         tgt_prv_state = states.NOSTATE
 1685         node = obj_utils.create_test_node(self.context,
 1686                                           provision_state=prv_state,
 1687                                           target_provision_state=tgt_prv_state,
 1688                                           last_error=None,
 1689                                           driver='fake-hardware')
 1690         self._start_service()
 1691 
 1692         with mock.patch.object(self.service, '_spawn_worker',
 1693                                autospec=True) as mock_spawn:
 1694             mock_spawn.side_effect = exception.NoFreeConductorWorker()
 1695 
 1696             exc = self.assertRaises(messaging.rpc.ExpectedException,
 1697                                     self.service.do_node_deploy,
 1698                                     self.context, node.uuid)
 1699             # Compare true exception hidden by @messaging.expected_exceptions
 1700             self.assertEqual(exception.NoFreeConductorWorker, exc.exc_info[0])
 1701             self._stop_service()
 1702             node.refresh()
 1703             # Make sure things were rolled back
 1704             self.assertEqual(prv_state, node.provision_state)
 1705             self.assertEqual(tgt_prv_state, node.target_provision_state)
 1706             self.assertIsNotNone(node.last_error)
 1707             # Verify reservation has been cleared.
 1708             self.assertIsNone(node.reservation)
 1709             mock_iwdi.assert_called_once_with(self.context, node.instance_info)
 1710             self.assertFalse(node.driver_internal_info['is_whole_disk_image'])
 1711 
 1712 
 1713 @mgr_utils.mock_record_keepalive
 1714 class ContinueNodeDeployTestCase(mgr_utils.ServiceSetUpMixin,
 1715                                  db_base.DbTestCase):
 1716     def setUp(self):
 1717         super(ContinueNodeDeployTestCase, self).setUp()
 1718         self.deploy_start = {
 1719             'step': 'deploy_start', 'priority': 50, 'interface': 'deploy'}
 1720         self.deploy_end = {
 1721             'step': 'deploy_end', 'priority': 20, 'interface': 'deploy'}
 1722         self.in_band_step = {
 1723             'step': 'deploy_middle', 'priority': 30, 'interface': 'deploy'}
 1724         self.deploy_steps = [self.deploy_start, self.deploy_end]
 1725 
 1726     @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker',
 1727                 autospec=True)
 1728     def test_continue_node_deploy_worker_pool_full(self, mock_spawn):
 1729         # Test the appropriate exception is raised if the worker pool is full
 1730         prv_state = states.DEPLOYWAIT
 1731         tgt_prv_state = states.ACTIVE
 1732         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 1733                                           provision_state=prv_state,
 1734                                           target_provision_state=tgt_prv_state,
 1735                                           last_error=None)
 1736         self._start_service()
 1737 
 1738         mock_spawn.side_effect = exception.NoFreeConductorWorker()
 1739 
 1740         self.assertRaises(exception.NoFreeConductorWorker,
 1741                           self.service.continue_node_deploy,
 1742                           self.context, node.uuid)
 1743 
 1744     @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker',
 1745                 autospec=True)
 1746     def test_continue_node_deploy_wrong_state(self, mock_spawn):
 1747         # Test the appropriate exception is raised if node isn't already
 1748         # in DEPLOYWAIT state
 1749         prv_state = states.DEPLOYFAIL
 1750         tgt_prv_state = states.ACTIVE
 1751         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 1752                                           provision_state=prv_state,
 1753                                           target_provision_state=tgt_prv_state,
 1754                                           last_error=None)
 1755         self._start_service()
 1756 
 1757         self.assertRaises(exception.InvalidStateRequested,
 1758                           self.service.continue_node_deploy,
 1759                           self.context, node.uuid)
 1760 
 1761         self._stop_service()
 1762         node.refresh()
 1763         # Make sure node wasn't modified
 1764         self.assertEqual(prv_state, node.provision_state)
 1765         self.assertEqual(tgt_prv_state, node.target_provision_state)
 1766         # Verify reservation has been cleared.
 1767         self.assertIsNone(node.reservation)
 1768 
 1769     @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker',
 1770                 autospec=True)
 1771     def test_continue_node_deploy(self, mock_spawn):
 1772         # test a node can continue deploying via RPC
 1773         prv_state = states.DEPLOYWAIT
 1774         tgt_prv_state = states.ACTIVE
 1775         driver_info = {'deploy_steps': self.deploy_steps,
 1776                        'deploy_step_index': 0,
 1777                        'steps_validated': True}
 1778         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 1779                                           provision_state=prv_state,
 1780                                           target_provision_state=tgt_prv_state,
 1781                                           last_error=None,
 1782                                           driver_internal_info=driver_info,
 1783                                           deploy_step=self.deploy_steps[0])
 1784         self._start_service()
 1785         self.service.continue_node_deploy(self.context, node.uuid)
 1786         self._stop_service()
 1787         node.refresh()
 1788         self.assertEqual(states.DEPLOYING, node.provision_state)
 1789         self.assertEqual(tgt_prv_state, node.target_provision_state)
 1790         mock_spawn.assert_called_with(mock.ANY,
 1791                                       deployments.do_next_deploy_step,
 1792                                       mock.ANY, 1)
 1793 
 1794     @mock.patch('ironic.drivers.modules.fake.FakeDeploy.get_deploy_steps',
 1795                 autospec=True)
 1796     @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker',
 1797                 autospec=True)
 1798     def test_continue_node_deploy_first_agent_boot(self, mock_spawn,
 1799                                                    mock_get_steps):
 1800         new_steps = [self.deploy_start, self.in_band_step, self.deploy_end]
 1801         mock_get_steps.return_value = new_steps
 1802         prv_state = states.DEPLOYWAIT
 1803         tgt_prv_state = states.ACTIVE
 1804         driver_info = {'deploy_steps': self.deploy_steps,
 1805                        'deploy_step_index': 0}
 1806         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 1807                                           provision_state=prv_state,
 1808                                           target_provision_state=tgt_prv_state,
 1809                                           last_error=None,
 1810                                           driver_internal_info=driver_info,
 1811                                           deploy_step=self.deploy_steps[0])
 1812         self._start_service()
 1813         self.service.continue_node_deploy(self.context, node.uuid)
 1814         self._stop_service()
 1815         node.refresh()
 1816         self.assertEqual(states.DEPLOYING, node.provision_state)
 1817         self.assertEqual(tgt_prv_state, node.target_provision_state)
 1818         self.assertTrue(node.driver_internal_info['steps_validated'])
 1819         self.assertEqual(new_steps, node.driver_internal_info['deploy_steps'])
 1820         mock_spawn.assert_called_with(mock.ANY,
 1821                                       deployments.do_next_deploy_step,
 1822                                       mock.ANY, 1)
 1823 
 1824     @mock.patch.object(task_manager.TaskManager, 'process_event',
 1825                        autospec=True)
 1826     @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker',
 1827                 autospec=True)
 1828     def test_continue_node_deploy_deprecated(self, mock_spawn, mock_event):
 1829         # TODO(rloo): delete this when we remove support for handling
 1830         # deploy steps; node will always be in DEPLOYWAIT then.
 1831 
 1832         # test a node can continue deploying via RPC
 1833         prv_state = states.DEPLOYING
 1834         tgt_prv_state = states.ACTIVE
 1835         driver_info = {'deploy_steps': self.deploy_steps,
 1836                        'deploy_step_index': 0,
 1837                        'steps_validated': True}
 1838         self._start_service()
 1839         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 1840                                           provision_state=prv_state,
 1841                                           target_provision_state=tgt_prv_state,
 1842                                           last_error=None,
 1843                                           driver_internal_info=driver_info,
 1844                                           deploy_step=self.deploy_steps[0])
 1845         self.service.continue_node_deploy(self.context, node.uuid)
 1846         self._stop_service()
 1847         node.refresh()
 1848         self.assertEqual(states.DEPLOYING, node.provision_state)
 1849         self.assertEqual(tgt_prv_state, node.target_provision_state)
 1850         mock_spawn.assert_called_with(mock.ANY,
 1851                                       deployments.do_next_deploy_step,
 1852                                       mock.ANY, 1)
 1853         self.assertFalse(mock_event.called)
 1854 
 1855     @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker',
 1856                 autospec=True)
 1857     def _continue_node_deploy_skip_step(self, mock_spawn, skip=True):
 1858         # test that skipping current step mechanism works
 1859         driver_info = {'deploy_steps': self.deploy_steps,
 1860                        'deploy_step_index': 0,
 1861                        'steps_validated': True}
 1862         if not skip:
 1863             driver_info['skip_current_deploy_step'] = skip
 1864         node = obj_utils.create_test_node(
 1865             self.context, driver='fake-hardware',
 1866             provision_state=states.DEPLOYWAIT,
 1867             target_provision_state=states.MANAGEABLE,
 1868             driver_internal_info=driver_info, deploy_step=self.deploy_steps[0])
 1869         self._start_service()
 1870         self.service.continue_node_deploy(self.context, node.uuid)
 1871         self._stop_service()
 1872         node.refresh()
 1873         if skip:
 1874             expected_step_index = 1
 1875         else:
 1876             self.assertNotIn(
 1877                 'skip_current_deploy_step', node.driver_internal_info)
 1878             expected_step_index = 0
 1879         mock_spawn.assert_called_with(mock.ANY,
 1880                                       deployments.do_next_deploy_step,
 1881                                       mock.ANY, expected_step_index)
 1882 
 1883     def test_continue_node_deploy_skip_step(self):
 1884         self._continue_node_deploy_skip_step()
 1885 
 1886     def test_continue_node_deploy_no_skip_step(self):
 1887         self._continue_node_deploy_skip_step(skip=False)
 1888 
 1889     @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker',
 1890                 autospec=True)
 1891     def test_continue_node_deploy_polling(self, mock_spawn):
 1892         # test that deployment_polling flag is cleared
 1893         driver_info = {'deploy_steps': self.deploy_steps,
 1894                        'deploy_step_index': 0,
 1895                        'deployment_polling': True,
 1896                        'steps_validated': True}
 1897         node = obj_utils.create_test_node(
 1898             self.context, driver='fake-hardware',
 1899             provision_state=states.DEPLOYWAIT,
 1900             target_provision_state=states.MANAGEABLE,
 1901             driver_internal_info=driver_info, deploy_step=self.deploy_steps[0])
 1902         self._start_service()
 1903         self.service.continue_node_deploy(self.context, node.uuid)
 1904         self._stop_service()
 1905         node.refresh()
 1906         self.assertNotIn('deployment_polling', node.driver_internal_info)
 1907         mock_spawn.assert_called_with(mock.ANY,
 1908                                       deployments.do_next_deploy_step,
 1909                                       mock.ANY, 1)
 1910 
 1911     @mock.patch.object(conductor_steps, 'validate_deploy_templates',
 1912                        autospec=True)
 1913     @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker',
 1914                 autospec=True)
 1915     def test_continue_node_steps_validation(self, mock_spawn, mock_validate):
 1916         prv_state = states.DEPLOYWAIT
 1917         tgt_prv_state = states.ACTIVE
 1918         mock_validate.side_effect = exception.InvalidParameterValue('boom')
 1919         driver_info = {'deploy_steps': self.deploy_steps,
 1920                        'deploy_step_index': 0,
 1921                        'steps_validated': False}
 1922         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 1923                                           provision_state=prv_state,
 1924                                           target_provision_state=tgt_prv_state,
 1925                                           last_error=None,
 1926                                           driver_internal_info=driver_info,
 1927                                           deploy_step=self.deploy_steps[0])
 1928         self._start_service()
 1929         mock_spawn.reset_mock()
 1930         self.service.continue_node_deploy(self.context, node.uuid)
 1931         self._stop_service()
 1932         node.refresh()
 1933         self.assertEqual(states.DEPLOYFAIL, node.provision_state)
 1934         self.assertIn('Failed to validate the final deploy steps',
 1935                       node.last_error)
 1936         self.assertIn('boom', node.last_error)
 1937         self.assertEqual(tgt_prv_state, node.target_provision_state)
 1938         self.assertFalse(mock_spawn.called)
 1939 
 1940 
 1941 @mgr_utils.mock_record_keepalive
 1942 class CheckTimeoutsTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
 1943     @mock.patch('ironic.drivers.modules.fake.FakeDeploy.clean_up',
 1944                 autospec=True)
 1945     def test__check_deploy_timeouts(self, mock_cleanup):
 1946         self._start_service()
 1947         CONF.set_override('deploy_callback_timeout', 1, group='conductor')
 1948         node = obj_utils.create_test_node(
 1949             self.context, driver='fake-hardware',
 1950             provision_state=states.DEPLOYWAIT,
 1951             target_provision_state=states.ACTIVE,
 1952             provision_updated_at=datetime.datetime(2000, 1, 1, 0, 0))
 1953 
 1954         self.service._check_deploy_timeouts(self.context)
 1955         self._stop_service()
 1956         node.refresh()
 1957         self.assertEqual(states.DEPLOYFAIL, node.provision_state)
 1958         self.assertEqual(states.ACTIVE, node.target_provision_state)
 1959         self.assertIsNotNone(node.last_error)
 1960         mock_cleanup.assert_called_once_with(mock.ANY, mock.ANY)
 1961 
 1962     def _check_cleanwait_timeouts(self, manual=False, with_step=True):
 1963         self._start_service()
 1964         CONF.set_override('clean_callback_timeout', 1, group='conductor')
 1965         tgt_prov_state = states.MANAGEABLE if manual else states.AVAILABLE
 1966         node = obj_utils.create_test_node(
 1967             self.context, driver='fake-hardware',
 1968             provision_state=states.CLEANWAIT,
 1969             target_provision_state=tgt_prov_state,
 1970             provision_updated_at=datetime.datetime(2000, 1, 1, 0, 0),
 1971             clean_step={
 1972                 'interface': 'deploy',
 1973                 'step': 'erase_devices'} if with_step else {},
 1974             driver_internal_info={
 1975                 'cleaning_reboot': manual,
 1976                 'clean_step_index': 0})
 1977 
 1978         self.service._check_cleanwait_timeouts(self.context)
 1979         self._stop_service()
 1980         node.refresh()
 1981         self.assertEqual(states.CLEANFAIL, node.provision_state)
 1982         self.assertEqual(tgt_prov_state, node.target_provision_state)
 1983         self.assertIsNotNone(node.last_error)
 1984         self.assertEqual(with_step, node.maintenance)
 1985         self.assertEqual(faults.CLEAN_FAILURE if with_step else None,
 1986                          node.fault)
 1987         # Test that cleaning parameters have been purged in order
 1988         # to prevent looping of the cleaning sequence
 1989         self.assertEqual({}, node.clean_step)
 1990         self.assertNotIn('clean_step_index', node.driver_internal_info)
 1991         self.assertNotIn('cleaning_reboot', node.driver_internal_info)
 1992 
 1993     def test__check_cleanwait_timeouts_automated_clean(self):
 1994         self._check_cleanwait_timeouts()
 1995 
 1996     def test__check_cleanwait_timeouts_manual_clean(self):
 1997         self._check_cleanwait_timeouts(manual=True)
 1998 
 1999     def test__check_cleanwait_timeouts_boot_timeout(self):
 2000         self._check_cleanwait_timeouts(with_step=False)
 2001 
 2002     @mock.patch('ironic.drivers.modules.fake.FakeRescue.clean_up',
 2003                 autospec=True)
 2004     @mock.patch.object(conductor_utils, 'node_power_action', autospec=True)
 2005     def test_check_rescuewait_timeouts(self, node_power_mock,
 2006                                        mock_clean_up):
 2007         self._start_service()
 2008         CONF.set_override('rescue_callback_timeout', 1, group='conductor')
 2009         tgt_prov_state = states.RESCUE
 2010         node = obj_utils.create_test_node(
 2011             self.context, driver='fake-hardware',
 2012             rescue_interface='fake',
 2013             network_interface='flat',
 2014             provision_state=states.RESCUEWAIT,
 2015             target_provision_state=tgt_prov_state,
 2016             provision_updated_at=datetime.datetime(2000, 1, 1, 0, 0))
 2017 
 2018         self.service._check_rescuewait_timeouts(self.context)
 2019         self._stop_service()
 2020         node.refresh()
 2021         self.assertEqual(states.RESCUEFAIL, node.provision_state)
 2022         self.assertEqual(tgt_prov_state, node.target_provision_state)
 2023         self.assertIsNotNone(node.last_error)
 2024         self.assertIn('Timeout reached while waiting for rescue ramdisk',
 2025                       node.last_error)
 2026         mock_clean_up.assert_called_once_with(mock.ANY, mock.ANY)
 2027         node_power_mock.assert_called_once_with(mock.ANY, states.POWER_OFF)
 2028 
 2029 
 2030 @mgr_utils.mock_record_keepalive
 2031 class DoNodeTearDownTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
 2032     def test_do_node_tear_down_invalid_state(self):
 2033         self._start_service()
 2034         # test node.provision_state is incorrect for tear_down
 2035         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 2036                                           provision_state=states.AVAILABLE)
 2037         exc = self.assertRaises(messaging.rpc.ExpectedException,
 2038                                 self.service.do_node_tear_down,
 2039                                 self.context, node['uuid'])
 2040         # Compare true exception hidden by @messaging.expected_exceptions
 2041         self.assertEqual(exception.InvalidStateRequested, exc.exc_info[0])
 2042 
 2043     def test_do_node_tear_down_protected(self):
 2044         self._start_service()
 2045         # test node.provision_state is incorrect for tear_down
 2046         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 2047                                           provision_state=states.ACTIVE,
 2048                                           protected=True)
 2049         exc = self.assertRaises(messaging.rpc.ExpectedException,
 2050                                 self.service.do_node_tear_down,
 2051                                 self.context, node['uuid'])
 2052         # Compare true exception hidden by @messaging.expected_exceptions
 2053         self.assertEqual(exception.NodeProtected, exc.exc_info[0])
 2054 
 2055     @mock.patch('ironic.drivers.modules.fake.FakePower.validate',
 2056                 autospec=True)
 2057     def test_do_node_tear_down_validate_fail(self, mock_validate):
 2058         # InvalidParameterValue should be re-raised as InstanceDeployFailure
 2059         mock_validate.side_effect = exception.InvalidParameterValue('error')
 2060         node = obj_utils.create_test_node(
 2061             self.context, driver='fake-hardware',
 2062             provision_state=states.ACTIVE,
 2063             target_provision_state=states.NOSTATE)
 2064         exc = self.assertRaises(messaging.rpc.ExpectedException,
 2065                                 self.service.do_node_tear_down,
 2066                                 self.context, node.uuid)
 2067         # Compare true exception hidden by @messaging.expected_exceptions
 2068         self.assertEqual(exception.InstanceDeployFailure, exc.exc_info[0])
 2069 
 2070     @mock.patch('ironic.drivers.modules.fake.FakeDeploy.tear_down',
 2071                 autospec=True)
 2072     def test_do_node_tear_down_driver_raises_error(self, mock_tear_down):
 2073         # test when driver.deploy.tear_down raises exception
 2074         node = obj_utils.create_test_node(
 2075             self.context, driver='fake-hardware',
 2076             provision_state=states.DELETING,
 2077             target_provision_state=states.AVAILABLE,
 2078             instance_info={'foo': 'bar'},
 2079             driver_internal_info={'is_whole_disk_image': False})
 2080 
 2081         task = task_manager.TaskManager(self.context, node.uuid)
 2082         self._start_service()
 2083         mock_tear_down.side_effect = exception.InstanceDeployFailure('test')
 2084         self.assertRaises(exception.InstanceDeployFailure,
 2085                           self.service._do_node_tear_down, task,
 2086                           node.provision_state)
 2087         node.refresh()
 2088         self.assertEqual(states.ERROR, node.provision_state)
 2089         self.assertEqual(states.NOSTATE, node.target_provision_state)
 2090         self.assertIsNotNone(node.last_error)
 2091         # Assert instance_info was erased
 2092         self.assertEqual({}, node.instance_info)
 2093         mock_tear_down.assert_called_once_with(mock.ANY, task)
 2094 
 2095     @mock.patch('ironic.drivers.modules.fake.FakeConsole.stop_console',
 2096                 autospec=True)
 2097     def test_do_node_tear_down_console_raises_error(self, mock_console):
 2098         # test when _set_console_mode raises exception
 2099         node = obj_utils.create_test_node(
 2100             self.context, driver='fake-hardware',
 2101             provision_state=states.DELETING,
 2102             target_provision_state=states.AVAILABLE,
 2103             instance_info={'foo': 'bar'},
 2104             console_enabled=True,
 2105             driver_internal_info={'is_whole_disk_image': False})
 2106 
 2107         task = task_manager.TaskManager(self.context, node.uuid)
 2108         self._start_service()
 2109         mock_console.side_effect = exception.ConsoleError('test')
 2110         self.assertRaises(exception.ConsoleError,
 2111                           self.service._do_node_tear_down, task,
 2112                           node.provision_state)
 2113         node.refresh()
 2114         self.assertEqual(states.ERROR, node.provision_state)
 2115         self.assertEqual(states.NOSTATE, node.target_provision_state)
 2116         self.assertIsNotNone(node.last_error)
 2117         # Assert instance_info was erased
 2118         self.assertEqual({}, node.instance_info)
 2119         mock_console.assert_called_once_with(mock.ANY, task)
 2120 
 2121     # TODO(TheJulia): Since we're functionally bound to neutron support
 2122     # by default, the fake drivers still invoke neutron.
 2123     @mock.patch('ironic.drivers.modules.fake.FakeConsole.stop_console',
 2124                 autospec=True)
 2125     @mock.patch('ironic.common.neutron.unbind_neutron_port', autospec=True)
 2126     @mock.patch('ironic.conductor.cleaning.do_node_clean', autospec=True)
 2127     @mock.patch('ironic.drivers.modules.fake.FakeDeploy.tear_down',
 2128                 autospec=True)
 2129     def _test__do_node_tear_down_ok(self, mock_tear_down, mock_clean,
 2130                                     mock_unbind, mock_console,
 2131                                     enabled_console=False,
 2132                                     with_allocation=False):
 2133         # test when driver.deploy.tear_down succeeds
 2134         node = obj_utils.create_test_node(
 2135             self.context, driver='fake-hardware',
 2136             provision_state=states.DELETING,
 2137             target_provision_state=states.AVAILABLE,
 2138             instance_uuid=(uuidutils.generate_uuid()
 2139                            if not with_allocation else None),
 2140             instance_info={'foo': 'bar'},
 2141             console_enabled=enabled_console,
 2142             driver_internal_info={'is_whole_disk_image': False,
 2143                                   'deploy_steps': {},
 2144                                   'root_uuid_or_disk_id': 'foo',
 2145                                   'instance': {'ephemeral_gb': 10}})
 2146         port = obj_utils.create_test_port(
 2147             self.context, node_id=node.id,
 2148             internal_info={'tenant_vif_port_id': 'foo'})
 2149         if with_allocation:
 2150             alloc = obj_utils.create_test_allocation(self.context)
 2151             # Establish cross-linking between the node and the allocation
 2152             alloc.node_id = node.id
 2153             alloc.save()
 2154             node.refresh()
 2155 
 2156         task = task_manager.TaskManager(self.context, node.uuid)
 2157         self._start_service()
 2158         self.service._do_node_tear_down(task, node.provision_state)
 2159         node.refresh()
 2160         port.refresh()
 2161         # Node will be moved to AVAILABLE after cleaning, not tested here
 2162         self.assertEqual(states.CLEANING, node.provision_state)
 2163         self.assertEqual(states.AVAILABLE, node.target_provision_state)
 2164         self.assertIsNone(node.last_error)
 2165         self.assertIsNone(node.instance_uuid)
 2166         self.assertIsNone(node.allocation_id)
 2167         self.assertEqual({}, node.instance_info)
 2168         self.assertNotIn('instance', node.driver_internal_info)
 2169         self.assertIsNone(node.driver_internal_info['deploy_steps'])
 2170         self.assertNotIn('root_uuid_or_disk_id', node.driver_internal_info)
 2171         self.assertNotIn('is_whole_disk_image', node.driver_internal_info)
 2172         mock_tear_down.assert_called_once_with(task.driver.deploy, task)
 2173         mock_clean.assert_called_once_with(task)
 2174         self.assertEqual({}, port.internal_info)
 2175         mock_unbind.assert_called_once_with('foo', context=mock.ANY)
 2176         if enabled_console:
 2177             mock_console.assert_called_once_with(task.driver.console, task)
 2178         else:
 2179             self.assertFalse(mock_console.called)
 2180         if with_allocation:
 2181             self.assertRaises(exception.AllocationNotFound,
 2182                               objects.Allocation.get_by_id,
 2183                               self.context, alloc.id)
 2184 
 2185     def test__do_node_tear_down_ok_without_console(self):
 2186         self._test__do_node_tear_down_ok(enabled_console=False)
 2187 
 2188     def test__do_node_tear_down_ok_with_console(self):
 2189         self._test__do_node_tear_down_ok(enabled_console=True)
 2190 
 2191     def test__do_node_tear_down_with_allocation(self):
 2192         self._test__do_node_tear_down_ok(with_allocation=True)
 2193 
 2194     @mock.patch('ironic.drivers.modules.fake.FakeRescue.clean_up',
 2195                 autospec=True)
 2196     @mock.patch('ironic.conductor.cleaning.do_node_clean', autospec=True)
 2197     @mock.patch('ironic.drivers.modules.fake.FakeDeploy.tear_down',
 2198                 autospec=True)
 2199     def _test_do_node_tear_down_from_state(self, init_state, is_rescue_state,
 2200                                            mock_tear_down, mock_clean,
 2201                                            mock_rescue_clean):
 2202         node = obj_utils.create_test_node(
 2203             self.context, driver='fake-hardware',
 2204             uuid=uuidutils.generate_uuid(),
 2205             provision_state=init_state,
 2206             target_provision_state=states.AVAILABLE,
 2207             driver_internal_info={'is_whole_disk_image': False})
 2208 
 2209         self._start_service()
 2210         self.service.do_node_tear_down(self.context, node.uuid)
 2211         self._stop_service()
 2212         node.refresh()
 2213         # Node will be moved to AVAILABLE after cleaning, not tested here
 2214         self.assertEqual(states.CLEANING, node.provision_state)
 2215         self.assertEqual(states.AVAILABLE, node.target_provision_state)
 2216         self.assertIsNone(node.last_error)
 2217         self.assertEqual({}, node.instance_info)
 2218         mock_tear_down.assert_called_once_with(mock.ANY, mock.ANY)
 2219         mock_clean.assert_called_once_with(mock.ANY)
 2220         if is_rescue_state:
 2221             mock_rescue_clean.assert_called_once_with(mock.ANY, mock.ANY)
 2222         else:
 2223             self.assertFalse(mock_rescue_clean.called)
 2224 
 2225     def test__do_node_tear_down_from_valid_states(self):
 2226         valid_states = [states.ACTIVE, states.DEPLOYWAIT, states.DEPLOYFAIL,
 2227                         states.ERROR]
 2228         for state in valid_states:
 2229             self._test_do_node_tear_down_from_state(state, False)
 2230 
 2231         valid_rescue_states = [states.RESCUEWAIT, states.RESCUE,
 2232                                states.UNRESCUEFAIL, states.RESCUEFAIL]
 2233         for state in valid_rescue_states:
 2234             self._test_do_node_tear_down_from_state(state, True)
 2235 
 2236     # NOTE(tenbrae): partial tear-down was broken. A node left in a state of
 2237     #                DELETING could not have tear_down called on it a second
 2238     #                time Thus, I have removed the unit test, which faultily
 2239     #                asserted only that a node could be left in a state of
 2240     #                incomplete deletion -- not that such a node's deletion
 2241     #                could later be completed.
 2242 
 2243     @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker',
 2244                 autospec=True)
 2245     def test_do_node_tear_down_worker_pool_full(self, mock_spawn):
 2246         prv_state = states.ACTIVE
 2247         tgt_prv_state = states.NOSTATE
 2248         fake_instance_info = {'foo': 'bar'}
 2249         driver_internal_info = {'is_whole_disk_image': False}
 2250         node = obj_utils.create_test_node(
 2251             self.context, driver='fake-hardware', provision_state=prv_state,
 2252             target_provision_state=tgt_prv_state,
 2253             instance_info=fake_instance_info,
 2254             driver_internal_info=driver_internal_info, last_error=None)
 2255         self._start_service()
 2256 
 2257         mock_spawn.side_effect = exception.NoFreeConductorWorker()
 2258 
 2259         exc = self.assertRaises(messaging.rpc.ExpectedException,
 2260                                 self.service.do_node_tear_down,
 2261                                 self.context, node.uuid)
 2262         # Compare true exception hidden by @messaging.expected_exceptions
 2263         self.assertEqual(exception.NoFreeConductorWorker, exc.exc_info[0])
 2264         self._stop_service()
 2265         node.refresh()
 2266         # Assert instance_info/driver_internal_info was not touched
 2267         self.assertEqual(fake_instance_info, node.instance_info)
 2268         self.assertEqual(driver_internal_info, node.driver_internal_info)
 2269         # Make sure things were rolled back
 2270         self.assertEqual(prv_state, node.provision_state)
 2271         self.assertEqual(tgt_prv_state, node.target_provision_state)
 2272         self.assertIsNotNone(node.last_error)
 2273         # Verify reservation has been cleared.
 2274         self.assertIsNone(node.reservation)
 2275 
 2276 
 2277 @mgr_utils.mock_record_keepalive
 2278 class DoProvisioningActionTestCase(mgr_utils.ServiceSetUpMixin,
 2279                                    db_base.DbTestCase):
 2280     @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker',
 2281                 autospec=True)
 2282     def test_do_provisioning_action_worker_pool_full(self, mock_spawn):
 2283         prv_state = states.MANAGEABLE
 2284         tgt_prv_state = states.NOSTATE
 2285         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 2286                                           provision_state=prv_state,
 2287                                           target_provision_state=tgt_prv_state,
 2288                                           last_error=None)
 2289         self._start_service()
 2290 
 2291         mock_spawn.side_effect = exception.NoFreeConductorWorker()
 2292 
 2293         exc = self.assertRaises(messaging.rpc.ExpectedException,
 2294                                 self.service.do_provisioning_action,
 2295                                 self.context, node.uuid, 'provide')
 2296         # Compare true exception hidden by @messaging.expected_exceptions
 2297         self.assertEqual(exception.NoFreeConductorWorker, exc.exc_info[0])
 2298         self._stop_service()
 2299         node.refresh()
 2300         # Make sure things were rolled back
 2301         self.assertEqual(prv_state, node.provision_state)
 2302         self.assertEqual(tgt_prv_state, node.target_provision_state)
 2303         self.assertIsNotNone(node.last_error)
 2304         # Verify reservation has been cleared.
 2305         self.assertIsNone(node.reservation)
 2306 
 2307     @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker',
 2308                 autospec=True)
 2309     def test_do_provision_action_provide(self, mock_spawn):
 2310         # test when a node is cleaned going from manageable to available
 2311         node = obj_utils.create_test_node(
 2312             self.context, driver='fake-hardware',
 2313             provision_state=states.MANAGEABLE,
 2314             target_provision_state=states.AVAILABLE)
 2315 
 2316         self._start_service()
 2317         self.service.do_provisioning_action(self.context, node.uuid, 'provide')
 2318         node.refresh()
 2319         # Node will be moved to AVAILABLE after cleaning, not tested here
 2320         self.assertEqual(states.CLEANING, node.provision_state)
 2321         self.assertEqual(states.AVAILABLE, node.target_provision_state)
 2322         self.assertIsNone(node.last_error)
 2323         mock_spawn.assert_called_with(self.service,
 2324                                       cleaning.do_node_clean, mock.ANY)
 2325 
 2326     @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker',
 2327                 autospec=True)
 2328     def test_do_provision_action_provide_in_maintenance(self, mock_spawn):
 2329         CONF.set_override('allow_provisioning_in_maintenance', False,
 2330                           group='conductor')
 2331         # test when a node is cleaned going from manageable to available
 2332         node = obj_utils.create_test_node(
 2333             self.context, driver='fake-hardware',
 2334             provision_state=states.MANAGEABLE,
 2335             target_provision_state=None,
 2336             maintenance=True)
 2337 
 2338         self._start_service()
 2339         mock_spawn.reset_mock()
 2340         exc = self.assertRaises(messaging.rpc.ExpectedException,
 2341                                 self.service.do_provisioning_action,
 2342                                 self.context, node.uuid, 'provide')
 2343         # Compare true exception hidden by @messaging.expected_exceptions
 2344         self.assertEqual(exception.NodeInMaintenance, exc.exc_info[0])
 2345         node.refresh()
 2346         self.assertEqual(states.MANAGEABLE, node.provision_state)
 2347         self.assertIsNone(node.target_provision_state)
 2348         self.assertIsNone(node.last_error)
 2349         self.assertFalse(mock_spawn.called)
 2350 
 2351     @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker',
 2352                 autospec=True)
 2353     def test_do_provision_action_manage(self, mock_spawn):
 2354         # test when a node is verified going from enroll to manageable
 2355         node = obj_utils.create_test_node(
 2356             self.context, driver='fake-hardware',
 2357             provision_state=states.ENROLL,
 2358             target_provision_state=states.MANAGEABLE)
 2359 
 2360         self._start_service()
 2361         self.service.do_provisioning_action(self.context, node.uuid, 'manage')
 2362         node.refresh()
 2363         # Node will be moved to MANAGEABLE after verification, not tested here
 2364         self.assertEqual(states.VERIFYING, node.provision_state)
 2365         self.assertEqual(states.MANAGEABLE, node.target_provision_state)
 2366         self.assertIsNone(node.last_error)
 2367         mock_spawn.assert_called_with(self.service,
 2368                                       self.service._do_node_verify, mock.ANY)
 2369 
 2370     @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker',
 2371                 autospec=True)
 2372     def _do_provision_action_abort(self, mock_spawn, manual=False):
 2373         tgt_prov_state = states.MANAGEABLE if manual else states.AVAILABLE
 2374         node = obj_utils.create_test_node(
 2375             self.context, driver='fake-hardware',
 2376             provision_state=states.CLEANWAIT,
 2377             target_provision_state=tgt_prov_state)
 2378 
 2379         self._start_service()
 2380         self.service.do_provisioning_action(self.context, node.uuid, 'abort')
 2381         node.refresh()
 2382         # Node will be moved to tgt_prov_state after cleaning, not tested here
 2383         self.assertEqual(states.CLEANFAIL, node.provision_state)
 2384         self.assertEqual(tgt_prov_state, node.target_provision_state)
 2385         self.assertIsNone(node.last_error)
 2386         mock_spawn.assert_called_with(
 2387             self.service, cleaning.do_node_clean_abort, mock.ANY)
 2388 
 2389     def test_do_provision_action_abort_automated_clean(self):
 2390         self._do_provision_action_abort()
 2391 
 2392     def test_do_provision_action_abort_manual_clean(self):
 2393         self._do_provision_action_abort(manual=True)
 2394 
 2395     def test_do_provision_action_abort_clean_step_not_abortable(self):
 2396         node = obj_utils.create_test_node(
 2397             self.context, driver='fake-hardware',
 2398             provision_state=states.CLEANWAIT,
 2399             target_provision_state=states.AVAILABLE,
 2400             clean_step={'step': 'foo', 'abortable': False})
 2401 
 2402         self._start_service()
 2403         self.service.do_provisioning_action(self.context, node.uuid, 'abort')
 2404         node.refresh()
 2405         # Assert the current clean step was marked to be aborted later
 2406         self.assertIn('abort_after', node.clean_step)
 2407         self.assertTrue(node.clean_step['abort_after'])
 2408         # Make sure things stays as it was before
 2409         self.assertEqual(states.CLEANWAIT, node.provision_state)
 2410         self.assertEqual(states.AVAILABLE, node.target_provision_state)
 2411 
 2412 
 2413 @mgr_utils.mock_record_keepalive
 2414 class DoNodeCleanTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
 2415     def setUp(self):
 2416         super(DoNodeCleanTestCase, self).setUp()
 2417         self.config(automated_clean=True, group='conductor')
 2418         self.power_update = {
 2419             'step': 'update_firmware', 'priority': 10, 'interface': 'power'}
 2420         self.deploy_update = {
 2421             'step': 'update_firmware', 'priority': 10, 'interface': 'deploy'}
 2422         self.deploy_erase = {
 2423             'step': 'erase_disks', 'priority': 20, 'interface': 'deploy'}
 2424         # Automated cleaning should be executed in this order
 2425         self.clean_steps = [self.deploy_erase, self.power_update,
 2426                             self.deploy_update]
 2427         self.next_clean_step_index = 1
 2428         # Manual clean step
 2429         self.deploy_raid = {
 2430             'step': 'build_raid', 'priority': 0, 'interface': 'deploy'}
 2431 
 2432     @mock.patch('ironic.drivers.modules.fake.FakePower.validate',
 2433                 autospec=True)
 2434     def test_do_node_clean_maintenance(self, mock_validate):
 2435         node = obj_utils.create_test_node(
 2436             self.context, driver='fake-hardware',
 2437             provision_state=states.MANAGEABLE,
 2438             target_provision_state=states.NOSTATE,
 2439             maintenance=True, maintenance_reason='reason')
 2440         self._start_service()
 2441         exc = self.assertRaises(messaging.rpc.ExpectedException,
 2442                                 self.service.do_node_clean,
 2443                                 self.context, node.uuid, [])
 2444         # Compare true exception hidden by @messaging.expected_exceptions
 2445         self.assertEqual(exception.NodeInMaintenance, exc.exc_info[0])
 2446         self.assertFalse(mock_validate.called)
 2447 
 2448     @mock.patch('ironic.conductor.task_manager.TaskManager.process_event',
 2449                 autospec=True)
 2450     def _test_do_node_clean_validate_fail(self, mock_validate, mock_process):
 2451         mock_validate.side_effect = exception.InvalidParameterValue('error')
 2452         node = obj_utils.create_test_node(
 2453             self.context, driver='fake-hardware',
 2454             provision_state=states.MANAGEABLE,
 2455             target_provision_state=states.NOSTATE)
 2456         self._start_service()
 2457         exc = self.assertRaises(messaging.rpc.ExpectedException,
 2458                                 self.service.do_node_clean,
 2459                                 self.context, node.uuid, [])
 2460         # Compare true exception hidden by @messaging.expected_exceptions
 2461         self.assertEqual(exception.InvalidParameterValue, exc.exc_info[0])
 2462         mock_validate.assert_called_once_with(mock.ANY, mock.ANY)
 2463         self.assertFalse(mock_process.called)
 2464 
 2465     @mock.patch('ironic.drivers.modules.fake.FakePower.validate',
 2466                 autospec=True)
 2467     def test_do_node_clean_power_validate_fail(self, mock_validate):
 2468         self._test_do_node_clean_validate_fail(mock_validate)
 2469 
 2470     @mock.patch('ironic.drivers.modules.network.flat.FlatNetwork.validate',
 2471                 autospec=True)
 2472     def test_do_node_clean_network_validate_fail(self, mock_validate):
 2473         self._test_do_node_clean_validate_fail(mock_validate)
 2474 
 2475     @mock.patch('ironic.drivers.modules.network.flat.FlatNetwork.validate',
 2476                 autospec=True)
 2477     @mock.patch('ironic.drivers.modules.fake.FakePower.validate',
 2478                 autospec=True)
 2479     def test_do_node_clean_invalid_state(self, mock_power_valid,
 2480                                          mock_network_valid):
 2481         # test node.provision_state is incorrect for clean
 2482         node = obj_utils.create_test_node(
 2483             self.context, driver='fake-hardware',
 2484             provision_state=states.ENROLL,
 2485             target_provision_state=states.NOSTATE)
 2486         self._start_service()
 2487         exc = self.assertRaises(messaging.rpc.ExpectedException,
 2488                                 self.service.do_node_clean,
 2489                                 self.context, node.uuid, [])
 2490         # Compare true exception hidden by @messaging.expected_exceptions
 2491         self.assertEqual(exception.InvalidStateRequested, exc.exc_info[0])
 2492         mock_power_valid.assert_called_once_with(mock.ANY, mock.ANY)
 2493         mock_network_valid.assert_called_once_with(mock.ANY, mock.ANY)
 2494         node.refresh()
 2495         self.assertNotIn('clean_steps', node.driver_internal_info)
 2496 
 2497     @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker',
 2498                 autospec=True)
 2499     @mock.patch('ironic.drivers.modules.network.flat.FlatNetwork.validate',
 2500                 autospec=True)
 2501     @mock.patch('ironic.drivers.modules.fake.FakePower.validate',
 2502                 autospec=True)
 2503     def test_do_node_clean_ok(self, mock_power_valid, mock_network_valid,
 2504                               mock_spawn):
 2505         node = obj_utils.create_test_node(
 2506             self.context, driver='fake-hardware',
 2507             provision_state=states.MANAGEABLE,
 2508             target_provision_state=states.NOSTATE, last_error='old error')
 2509         self._start_service()
 2510         clean_steps = [self.deploy_raid]
 2511         self.service.do_node_clean(self.context, node.uuid, clean_steps)
 2512         mock_power_valid.assert_called_once_with(mock.ANY, mock.ANY)
 2513         mock_network_valid.assert_called_once_with(mock.ANY, mock.ANY)
 2514         mock_spawn.assert_called_with(
 2515             self.service, cleaning.do_node_clean, mock.ANY, clean_steps)
 2516         node.refresh()
 2517         # Node will be moved to CLEANING
 2518         self.assertEqual(states.CLEANING, node.provision_state)
 2519         self.assertEqual(states.MANAGEABLE, node.target_provision_state)
 2520         self.assertNotIn('clean_steps', node.driver_internal_info)
 2521         self.assertIsNone(node.last_error)
 2522 
 2523     @mock.patch('ironic.conductor.utils.remove_agent_url', autospec=True)
 2524     @mock.patch('ironic.conductor.utils.is_fast_track', autospec=True)
 2525     @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker',
 2526                 autospec=True)
 2527     @mock.patch('ironic.drivers.modules.network.flat.FlatNetwork.validate',
 2528                 autospec=True)
 2529     @mock.patch('ironic.drivers.modules.fake.FakePower.validate',
 2530                 autospec=True)
 2531     def test_do_node_clean_ok_fast_track(
 2532             self, mock_power_valid, mock_network_valid, mock_spawn,
 2533             mock_is_fast_track, mock_remove_agent_url):
 2534         node = obj_utils.create_test_node(
 2535             self.context, driver='fake-hardware',
 2536             provision_state=states.MANAGEABLE,
 2537             driver_internal_info={'agent_url': 'meow'})
 2538         mock_is_fast_track.return_value = True
 2539         self._start_service()
 2540         clean_steps = [self.deploy_raid]
 2541         self.service.do_node_clean(self.context, node.uuid, clean_steps)
 2542         mock_power_valid.assert_called_once_with(mock.ANY, mock.ANY)
 2543         mock_network_valid.assert_called_once_with(mock.ANY, mock.ANY)
 2544         mock_spawn.assert_called_with(
 2545             self.service, cleaning.do_node_clean, mock.ANY, clean_steps)
 2546         node.refresh()
 2547         # Node will be moved to CLEANING
 2548         self.assertEqual(states.CLEANING, node.provision_state)
 2549         self.assertEqual(states.MANAGEABLE, node.target_provision_state)
 2550         self.assertNotIn('clean_steps', node.driver_internal_info)
 2551         mock_is_fast_track.assert_called_once_with(mock.ANY)
 2552         mock_remove_agent_url.assert_not_called()
 2553 
 2554     @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker',
 2555                 autospec=True)
 2556     @mock.patch('ironic.drivers.modules.network.flat.FlatNetwork.validate',
 2557                 autospec=True)
 2558     @mock.patch('ironic.drivers.modules.fake.FakePower.validate',
 2559                 autospec=True)
 2560     def test_do_node_clean_worker_pool_full(self, mock_power_valid,
 2561                                             mock_network_valid, mock_spawn):
 2562         prv_state = states.MANAGEABLE
 2563         tgt_prv_state = states.NOSTATE
 2564         node = obj_utils.create_test_node(
 2565             self.context, driver='fake-hardware', provision_state=prv_state,
 2566             target_provision_state=tgt_prv_state)
 2567         self._start_service()
 2568         clean_steps = [self.deploy_raid]
 2569         mock_spawn.side_effect = exception.NoFreeConductorWorker()
 2570         exc = self.assertRaises(messaging.rpc.ExpectedException,
 2571                                 self.service.do_node_clean,
 2572                                 self.context, node.uuid, clean_steps)
 2573         # Compare true exception hidden by @messaging.expected_exceptions
 2574         self.assertEqual(exception.NoFreeConductorWorker, exc.exc_info[0])
 2575         self._stop_service()
 2576         mock_power_valid.assert_called_once_with(mock.ANY, mock.ANY)
 2577         mock_network_valid.assert_called_once_with(mock.ANY, mock.ANY)
 2578         mock_spawn.assert_called_with(
 2579             self.service, cleaning.do_node_clean, mock.ANY, clean_steps)
 2580         node.refresh()
 2581         # Make sure states were rolled back
 2582         self.assertEqual(prv_state, node.provision_state)
 2583         self.assertEqual(tgt_prv_state, node.target_provision_state)
 2584 
 2585         self.assertIsNotNone(node.last_error)
 2586         self.assertIsNone(node.reservation)
 2587 
 2588     @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker',
 2589                 autospec=True)
 2590     def test_continue_node_clean_worker_pool_full(self, mock_spawn):
 2591         # Test the appropriate exception is raised if the worker pool is full
 2592         prv_state = states.CLEANWAIT
 2593         tgt_prv_state = states.AVAILABLE
 2594         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 2595                                           provision_state=prv_state,
 2596                                           target_provision_state=tgt_prv_state,
 2597                                           last_error=None)
 2598         self._start_service()
 2599 
 2600         mock_spawn.side_effect = exception.NoFreeConductorWorker()
 2601 
 2602         self.assertRaises(exception.NoFreeConductorWorker,
 2603                           self.service.continue_node_clean,
 2604                           self.context, node.uuid)
 2605 
 2606     @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker',
 2607                 autospec=True)
 2608     def test_continue_node_clean_wrong_state(self, mock_spawn):
 2609         # Test the appropriate exception is raised if node isn't already
 2610         # in CLEANWAIT state
 2611         prv_state = states.ACTIVE
 2612         tgt_prv_state = states.AVAILABLE
 2613         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 2614                                           provision_state=prv_state,
 2615                                           target_provision_state=tgt_prv_state,
 2616                                           last_error=None)
 2617         self._start_service()
 2618 
 2619         self.assertRaises(exception.InvalidStateRequested,
 2620                           self.service.continue_node_clean,
 2621                           self.context, node.uuid)
 2622 
 2623         self._stop_service()
 2624         node.refresh()
 2625         # Make sure things were rolled back
 2626         self.assertEqual(prv_state, node.provision_state)
 2627         self.assertEqual(tgt_prv_state, node.target_provision_state)
 2628         # Verify reservation has been cleared.
 2629         self.assertIsNone(node.reservation)
 2630 
 2631     @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker',
 2632                 autospec=True)
 2633     def _continue_node_clean(self, return_state, mock_spawn, manual=False):
 2634         # test a node can continue cleaning via RPC
 2635         prv_state = return_state
 2636         tgt_prv_state = states.MANAGEABLE if manual else states.AVAILABLE
 2637         driver_info = {'clean_steps': self.clean_steps,
 2638                        'clean_step_index': 0}
 2639         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 2640                                           provision_state=prv_state,
 2641                                           target_provision_state=tgt_prv_state,
 2642                                           last_error=None,
 2643                                           driver_internal_info=driver_info,
 2644                                           clean_step=self.clean_steps[0])
 2645         self._start_service()
 2646         self.service.continue_node_clean(self.context, node.uuid)
 2647         self._stop_service()
 2648         node.refresh()
 2649         self.assertEqual(states.CLEANING, node.provision_state)
 2650         self.assertEqual(tgt_prv_state, node.target_provision_state)
 2651         mock_spawn.assert_called_with(self.service,
 2652                                       cleaning.do_next_clean_step,
 2653                                       mock.ANY, self.next_clean_step_index)
 2654 
 2655     def test_continue_node_clean_automated(self):
 2656         self._continue_node_clean(states.CLEANWAIT)
 2657 
 2658     def test_continue_node_clean_manual(self):
 2659         self._continue_node_clean(states.CLEANWAIT, manual=True)
 2660 
 2661     @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker',
 2662                 autospec=True)
 2663     def _continue_node_clean_skip_step(self, mock_spawn, skip=True):
 2664         # test that skipping current step mechanism works
 2665         driver_info = {'clean_steps': self.clean_steps,
 2666                        'clean_step_index': 0}
 2667         if not skip:
 2668             driver_info['skip_current_clean_step'] = skip
 2669         node = obj_utils.create_test_node(
 2670             self.context, driver='fake-hardware',
 2671             provision_state=states.CLEANWAIT,
 2672             target_provision_state=states.MANAGEABLE,
 2673             driver_internal_info=driver_info, clean_step=self.clean_steps[0])
 2674         self._start_service()
 2675         self.service.continue_node_clean(self.context, node.uuid)
 2676         self._stop_service()
 2677         node.refresh()
 2678         if skip:
 2679             expected_step_index = 1
 2680         else:
 2681             self.assertNotIn(
 2682                 'skip_current_clean_step', node.driver_internal_info)
 2683             expected_step_index = 0
 2684         mock_spawn.assert_called_with(self.service,
 2685                                       cleaning.do_next_clean_step,
 2686                                       mock.ANY, expected_step_index)
 2687 
 2688     def test_continue_node_clean_skip_step(self):
 2689         self._continue_node_clean_skip_step()
 2690 
 2691     def test_continue_node_clean_no_skip_step(self):
 2692         self._continue_node_clean_skip_step(skip=False)
 2693 
 2694     @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker',
 2695                 autospec=True)
 2696     def test_continue_node_clean_polling(self, mock_spawn):
 2697         # test that cleaning_polling flag is cleared
 2698         driver_info = {'clean_steps': self.clean_steps,
 2699                        'clean_step_index': 0,
 2700                        'cleaning_polling': True}
 2701         node = obj_utils.create_test_node(
 2702             self.context, driver='fake-hardware',
 2703             provision_state=states.CLEANWAIT,
 2704             target_provision_state=states.MANAGEABLE,
 2705             driver_internal_info=driver_info, clean_step=self.clean_steps[0])
 2706         self._start_service()
 2707         self.service.continue_node_clean(self.context, node.uuid)
 2708         self._stop_service()
 2709         node.refresh()
 2710         self.assertNotIn('cleaning_polling', node.driver_internal_info)
 2711         mock_spawn.assert_called_with(self.service,
 2712                                       cleaning.do_next_clean_step,
 2713                                       mock.ANY, 1)
 2714 
 2715     def _continue_node_clean_abort(self, manual=False):
 2716         last_clean_step = self.clean_steps[0]
 2717         last_clean_step['abortable'] = False
 2718         last_clean_step['abort_after'] = True
 2719         driver_info = {'clean_steps': self.clean_steps,
 2720                        'clean_step_index': 0}
 2721         tgt_prov_state = states.MANAGEABLE if manual else states.AVAILABLE
 2722         node = obj_utils.create_test_node(
 2723             self.context, driver='fake-hardware',
 2724             provision_state=states.CLEANWAIT,
 2725             target_provision_state=tgt_prov_state, last_error=None,
 2726             driver_internal_info=driver_info, clean_step=self.clean_steps[0])
 2727 
 2728         self._start_service()
 2729         self.service.continue_node_clean(self.context, node.uuid)
 2730         self._stop_service()
 2731         node.refresh()
 2732         self.assertEqual(states.CLEANFAIL, node.provision_state)
 2733         self.assertEqual(tgt_prov_state, node.target_provision_state)
 2734         self.assertIsNotNone(node.last_error)
 2735         # assert the clean step name is in the last error message
 2736         self.assertIn(self.clean_steps[0]['step'], node.last_error)
 2737 
 2738     def test_continue_node_clean_automated_abort(self):
 2739         self._continue_node_clean_abort()
 2740 
 2741     def test_continue_node_clean_manual_abort(self):
 2742         self._continue_node_clean_abort(manual=True)
 2743 
 2744     def _continue_node_clean_abort_last_clean_step(self, manual=False):
 2745         last_clean_step = self.clean_steps[0]
 2746         last_clean_step['abortable'] = False
 2747         last_clean_step['abort_after'] = True
 2748         driver_info = {'clean_steps': [self.clean_steps[0]],
 2749                        'clean_step_index': 0}
 2750         tgt_prov_state = states.MANAGEABLE if manual else states.AVAILABLE
 2751         node = obj_utils.create_test_node(
 2752             self.context, driver='fake-hardware',
 2753             provision_state=states.CLEANWAIT,
 2754             target_provision_state=tgt_prov_state, last_error=None,
 2755             driver_internal_info=driver_info, clean_step=self.clean_steps[0])
 2756 
 2757         self._start_service()
 2758         self.service.continue_node_clean(self.context, node.uuid)
 2759         self._stop_service()
 2760         node.refresh()
 2761         self.assertEqual(tgt_prov_state, node.provision_state)
 2762         self.assertIsNone(node.target_provision_state)
 2763         self.assertIsNone(node.last_error)
 2764 
 2765     def test_continue_node_clean_automated_abort_last_clean_step(self):
 2766         self._continue_node_clean_abort_last_clean_step()
 2767 
 2768     def test_continue_node_clean_manual_abort_last_clean_step(self):
 2769         self._continue_node_clean_abort_last_clean_step(manual=True)
 2770 
 2771 
 2772 class DoNodeRescueTestCase(mgr_utils.CommonMixIn, mgr_utils.ServiceSetUpMixin,
 2773                            db_base.DbTestCase):
 2774     @mock.patch('ironic.conductor.task_manager.acquire', autospec=True)
 2775     def test_do_node_rescue(self, mock_acquire):
 2776         self._start_service()
 2777         dii = {'agent_secret_token': 'token',
 2778                'agent_url': 'http://url',
 2779                'other field': 'value'}
 2780         task = self._create_task(
 2781             node_attrs=dict(driver='fake-hardware',
 2782                             provision_state=states.ACTIVE,
 2783                             instance_info={},
 2784                             driver_internal_info=dii))
 2785         mock_acquire.side_effect = self._get_acquire_side_effect(task)
 2786         self.service.do_node_rescue(self.context, task.node.uuid,
 2787                                     "password")
 2788         task.process_event.assert_called_once_with(
 2789             'rescue',
 2790             callback=self.service._spawn_worker,
 2791             call_args=(self.service._do_node_rescue, task),
 2792             err_handler=conductor_utils.spawn_rescue_error_handler)
 2793         self.assertIn('rescue_password', task.node.instance_info)
 2794         self.assertIn('hashed_rescue_password', task.node.instance_info)
 2795         self.assertEqual({'other field': 'value'},
 2796                          task.node.driver_internal_info)
 2797 
 2798     def test_do_node_rescue_invalid_state(self):
 2799         self._start_service()
 2800         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 2801                                           network_interface='noop',
 2802                                           provision_state=states.AVAILABLE,
 2803                                           instance_info={})
 2804         exc = self.assertRaises(messaging.rpc.ExpectedException,
 2805                                 self.service.do_node_rescue,
 2806                                 self.context, node.uuid, "password")
 2807         node.refresh()
 2808         self.assertNotIn('rescue_password', node.instance_info)
 2809         self.assertNotIn('hashed_rescue_password', node.instance_info)
 2810         self.assertEqual(exception.InvalidStateRequested, exc.exc_info[0])
 2811 
 2812     def _test_do_node_rescue_when_validate_fail(self, mock_validate):
 2813         # InvalidParameterValue should be re-raised as InstanceRescueFailure
 2814         mock_validate.side_effect = exception.InvalidParameterValue('error')
 2815         node = obj_utils.create_test_node(
 2816             self.context, driver='fake-hardware',
 2817             provision_state=states.ACTIVE,
 2818             target_provision_state=states.NOSTATE,
 2819             instance_info={})
 2820         exc = self.assertRaises(messaging.rpc.ExpectedException,
 2821                                 self.service.do_node_rescue,
 2822                                 self.context, node.uuid, "password")
 2823         node.refresh()
 2824         self.assertNotIn('hashed_rescue_password', node.instance_info)
 2825         # Compare true exception hidden by @messaging.expected_exceptions
 2826         self.assertEqual(exception.InstanceRescueFailure, exc.exc_info[0])
 2827 
 2828     @mock.patch('ironic.drivers.modules.fake.FakeRescue.validate',
 2829                 autospec=True)
 2830     def test_do_node_rescue_when_rescue_validate_fail(self, mock_validate):
 2831         self._test_do_node_rescue_when_validate_fail(mock_validate)
 2832 
 2833     @mock.patch('ironic.drivers.modules.fake.FakePower.validate',
 2834                 autospec=True)
 2835     def test_do_node_rescue_when_power_validate_fail(self, mock_validate):
 2836         self._test_do_node_rescue_when_validate_fail(mock_validate)
 2837 
 2838     @mock.patch('ironic.drivers.modules.network.flat.FlatNetwork.validate',
 2839                 autospec=True)
 2840     def test_do_node_rescue_when_network_validate_fail(self, mock_validate):
 2841         self._test_do_node_rescue_when_validate_fail(mock_validate)
 2842 
 2843     def test_do_node_rescue_maintenance(self):
 2844         node = obj_utils.create_test_node(
 2845             self.context, driver='fake-hardware',
 2846             network_interface='noop',
 2847             provision_state=states.ACTIVE,
 2848             maintenance=True,
 2849             target_provision_state=states.NOSTATE,
 2850             instance_info={})
 2851         exc = self.assertRaises(messaging.rpc.ExpectedException,
 2852                                 self.service.do_node_rescue,
 2853                                 self.context, node['uuid'], "password")
 2854         # Compare true exception hidden by @messaging.expected_exceptions
 2855         self.assertEqual(exception.NodeInMaintenance, exc.exc_info[0])
 2856         # This is a sync operation last_error should be None.
 2857         self.assertIsNone(node.last_error)
 2858 
 2859     @mock.patch('ironic.drivers.modules.fake.FakeRescue.rescue', autospec=True)
 2860     def test__do_node_rescue_returns_rescuewait(self, mock_rescue):
 2861         self._start_service()
 2862         node = obj_utils.create_test_node(
 2863             self.context, driver='fake-hardware',
 2864             provision_state=states.RESCUING,
 2865             instance_info={'rescue_password': 'password',
 2866                            'hashed_rescue_password': '1234'})
 2867         with task_manager.TaskManager(self.context, node.uuid) as task:
 2868             mock_rescue.return_value = states.RESCUEWAIT
 2869             self.service._do_node_rescue(task)
 2870             node.refresh()
 2871             self.assertEqual(states.RESCUEWAIT, node.provision_state)
 2872             self.assertEqual(states.RESCUE, node.target_provision_state)
 2873             self.assertIn('rescue_password', node.instance_info)
 2874             self.assertIn('hashed_rescue_password', node.instance_info)
 2875 
 2876     @mock.patch('ironic.drivers.modules.fake.FakeRescue.rescue', autospec=True)
 2877     def test__do_node_rescue_returns_rescue(self, mock_rescue):
 2878         self._start_service()
 2879         node = obj_utils.create_test_node(
 2880             self.context, driver='fake-hardware',
 2881             provision_state=states.RESCUING,
 2882             instance_info={
 2883                 'rescue_password': 'password',
 2884                 'hashed_rescue_password': '1234'})
 2885         with task_manager.TaskManager(self.context, node.uuid) as task:
 2886             mock_rescue.return_value = states.RESCUE
 2887             self.service._do_node_rescue(task)
 2888             node.refresh()
 2889             self.assertEqual(states.RESCUE, node.provision_state)
 2890             self.assertEqual(states.NOSTATE, node.target_provision_state)
 2891             self.assertIn('rescue_password', node.instance_info)
 2892             self.assertIn('hashed_rescue_password', node.instance_info)
 2893 
 2894     @mock.patch.object(manager, 'LOG', autospec=True)
 2895     @mock.patch('ironic.drivers.modules.fake.FakeRescue.rescue', autospec=True)
 2896     def test__do_node_rescue_errors(self, mock_rescue, mock_log):
 2897         self._start_service()
 2898         node = obj_utils.create_test_node(
 2899             self.context, driver='fake-hardware',
 2900             provision_state=states.RESCUING,
 2901             instance_info={
 2902                 'rescue_password': 'password',
 2903                 'hashed_rescue_password': '1234'})
 2904         mock_rescue.side_effect = exception.InstanceRescueFailure(
 2905             'failed to rescue')
 2906         with task_manager.TaskManager(self.context, node.uuid) as task:
 2907             self.assertRaises(exception.InstanceRescueFailure,
 2908                               self.service._do_node_rescue, task)
 2909             node.refresh()
 2910             self.assertEqual(states.RESCUEFAIL, node.provision_state)
 2911             self.assertEqual(states.RESCUE, node.target_provision_state)
 2912             self.assertNotIn('rescue_password', node.instance_info)
 2913             self.assertNotIn('hashed_rescue_password', node.instance_info)
 2914             self.assertTrue(node.last_error.startswith('Failed to rescue'))
 2915             self.assertTrue(mock_log.error.called)
 2916 
 2917     @mock.patch.object(manager, 'LOG', autospec=True)
 2918     @mock.patch('ironic.drivers.modules.fake.FakeRescue.rescue', autospec=True)
 2919     def test__do_node_rescue_bad_state(self, mock_rescue, mock_log):
 2920         self._start_service()
 2921         node = obj_utils.create_test_node(
 2922             self.context, driver='fake-hardware',
 2923             provision_state=states.RESCUING,
 2924             instance_info={
 2925                 'rescue_password': 'password',
 2926                 'hashed_rescue_password': '1234'})
 2927         mock_rescue.return_value = states.ACTIVE
 2928         with task_manager.TaskManager(self.context, node.uuid) as task:
 2929             self.service._do_node_rescue(task)
 2930             node.refresh()
 2931             self.assertEqual(states.RESCUEFAIL, node.provision_state)
 2932             self.assertEqual(states.RESCUE, node.target_provision_state)
 2933             self.assertNotIn('rescue_password', node.instance_info)
 2934             self.assertNotIn('hashed_rescue_password', node.instance_info)
 2935             self.assertTrue(node.last_error.startswith('Failed to rescue'))
 2936             self.assertTrue(mock_log.error.called)
 2937 
 2938     @mock.patch('ironic.conductor.task_manager.acquire', autospec=True)
 2939     def test_do_node_unrescue(self, mock_acquire):
 2940         self._start_service()
 2941         task = self._create_task(
 2942             node_attrs=dict(driver='fake-hardware',
 2943                             provision_state=states.RESCUE,
 2944                             driver_internal_info={'agent_url': 'url'}))
 2945         mock_acquire.side_effect = self._get_acquire_side_effect(task)
 2946         self.service.do_node_unrescue(self.context, task.node.uuid)
 2947         task.node.refresh()
 2948         self.assertNotIn('agent_url', task.node.driver_internal_info)
 2949         task.process_event.assert_called_once_with(
 2950             'unrescue',
 2951             callback=self.service._spawn_worker,
 2952             call_args=(self.service._do_node_unrescue, task),
 2953             err_handler=conductor_utils.provisioning_error_handler)
 2954 
 2955     def test_do_node_unrescue_invalid_state(self):
 2956         self._start_service()
 2957         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 2958                                           provision_state=states.AVAILABLE)
 2959         exc = self.assertRaises(messaging.rpc.ExpectedException,
 2960                                 self.service.do_node_unrescue,
 2961                                 self.context, node.uuid)
 2962         self.assertEqual(exception.InvalidStateRequested, exc.exc_info[0])
 2963 
 2964     @mock.patch('ironic.drivers.modules.fake.FakePower.validate',
 2965                 autospec=True)
 2966     def test_do_node_unrescue_validate_fail(self, mock_validate):
 2967         # InvalidParameterValue should be re-raised as InstanceUnrescueFailure
 2968         mock_validate.side_effect = exception.InvalidParameterValue('error')
 2969         node = obj_utils.create_test_node(
 2970             self.context, driver='fake-hardware',
 2971             provision_state=states.RESCUE,
 2972             target_provision_state=states.NOSTATE)
 2973         exc = self.assertRaises(messaging.rpc.ExpectedException,
 2974                                 self.service.do_node_unrescue,
 2975                                 self.context, node.uuid)
 2976         # Compare true exception hidden by @messaging.expected_exceptions
 2977         self.assertEqual(exception.InstanceUnrescueFailure, exc.exc_info[0])
 2978 
 2979     def test_do_node_unrescue_maintenance(self):
 2980         node = obj_utils.create_test_node(
 2981             self.context, driver='fake-hardware',
 2982             provision_state=states.RESCUE,
 2983             maintenance=True,
 2984             target_provision_state=states.NOSTATE,
 2985             instance_info={})
 2986         exc = self.assertRaises(messaging.rpc.ExpectedException,
 2987                                 self.service.do_node_unrescue,
 2988                                 self.context, node.uuid)
 2989         # Compare true exception hidden by @messaging.expected_exceptions
 2990         self.assertEqual(exception.NodeInMaintenance, exc.exc_info[0])
 2991         # This is a sync operation last_error should be None.
 2992         node.refresh()
 2993         self.assertIsNone(node.last_error)
 2994 
 2995     @mock.patch('ironic.drivers.modules.fake.FakeRescue.unrescue',
 2996                 autospec=True)
 2997     def test__do_node_unrescue(self, mock_unrescue):
 2998         self._start_service()
 2999         dii = {'agent_url': 'http://url',
 3000                'agent_secret_token': 'token',
 3001                'other field': 'value'}
 3002         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 3003                                           provision_state=states.UNRESCUING,
 3004                                           target_provision_state=states.ACTIVE,
 3005                                           instance_info={},
 3006                                           driver_internal_info=dii)
 3007         with task_manager.TaskManager(self.context, node.uuid) as task:
 3008             mock_unrescue.return_value = states.ACTIVE
 3009             self.service._do_node_unrescue(task)
 3010             node.refresh()
 3011             self.assertEqual(states.ACTIVE, node.provision_state)
 3012             self.assertEqual(states.NOSTATE, node.target_provision_state)
 3013             self.assertEqual({'other field': 'value'},
 3014                              node.driver_internal_info)
 3015 
 3016     @mock.patch.object(manager, 'LOG', autospec=True)
 3017     @mock.patch('ironic.drivers.modules.fake.FakeRescue.unrescue',
 3018                 autospec=True)
 3019     def test__do_node_unrescue_ironic_error(self, mock_unrescue, mock_log):
 3020         self._start_service()
 3021         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 3022                                           provision_state=states.UNRESCUING,
 3023                                           target_provision_state=states.ACTIVE,
 3024                                           instance_info={})
 3025         mock_unrescue.side_effect = exception.InstanceUnrescueFailure(
 3026             'Unable to unrescue')
 3027         with task_manager.TaskManager(self.context, node.uuid) as task:
 3028             self.assertRaises(exception.InstanceUnrescueFailure,
 3029                               self.service._do_node_unrescue, task)
 3030             node.refresh()
 3031             self.assertEqual(states.UNRESCUEFAIL, node.provision_state)
 3032             self.assertEqual(states.ACTIVE, node.target_provision_state)
 3033             self.assertTrue('Unable to unrescue' in node.last_error)
 3034             self.assertTrue(mock_log.error.called)
 3035 
 3036     @mock.patch.object(manager, 'LOG', autospec=True)
 3037     @mock.patch('ironic.drivers.modules.fake.FakeRescue.unrescue',
 3038                 autospec=True)
 3039     def test__do_node_unrescue_other_error(self, mock_unrescue, mock_log):
 3040         self._start_service()
 3041         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 3042                                           provision_state=states.UNRESCUING,
 3043                                           target_provision_state=states.ACTIVE,
 3044                                           instance_info={})
 3045         mock_unrescue.side_effect = RuntimeError('Some failure')
 3046         with task_manager.TaskManager(self.context, node.uuid) as task:
 3047             self.assertRaises(RuntimeError,
 3048                               self.service._do_node_unrescue, task)
 3049             node.refresh()
 3050             self.assertEqual(states.UNRESCUEFAIL, node.provision_state)
 3051             self.assertEqual(states.ACTIVE, node.target_provision_state)
 3052             self.assertTrue('Some failure' in node.last_error)
 3053             self.assertTrue(mock_log.exception.called)
 3054 
 3055     @mock.patch('ironic.drivers.modules.fake.FakeRescue.unrescue',
 3056                 autospec=True)
 3057     def test__do_node_unrescue_bad_state(self, mock_unrescue):
 3058         self._start_service()
 3059         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 3060                                           provision_state=states.UNRESCUING,
 3061                                           instance_info={})
 3062         mock_unrescue.return_value = states.RESCUEWAIT
 3063         with task_manager.TaskManager(self.context, node.uuid) as task:
 3064             self.service._do_node_unrescue(task)
 3065             node.refresh()
 3066             self.assertEqual(states.UNRESCUEFAIL, node.provision_state)
 3067             self.assertEqual(states.ACTIVE, node.target_provision_state)
 3068             self.assertTrue('Driver returned unexpected state' in
 3069                             node.last_error)
 3070 
 3071     @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker',
 3072                 autospec=True)
 3073     def test_provision_rescue_abort(self, mock_spawn):
 3074         node = obj_utils.create_test_node(
 3075             self.context, driver='fake-hardware',
 3076             provision_state=states.RESCUEWAIT,
 3077             target_provision_state=states.RESCUE,
 3078             instance_info={'rescue_password': 'password'})
 3079         self._start_service()
 3080         self.service.do_provisioning_action(self.context, node.uuid, 'abort')
 3081         node.refresh()
 3082         self.assertEqual(states.RESCUEFAIL, node.provision_state)
 3083         self.assertIsNone(node.last_error)
 3084         self.assertNotIn('rescue_password', node.instance_info)
 3085         mock_spawn.assert_called_with(
 3086             self.service, self.service._do_node_rescue_abort, mock.ANY)
 3087 
 3088     @mock.patch.object(fake.FakeRescue, 'clean_up', autospec=True)
 3089     def test__do_node_rescue_abort(self, clean_up_mock):
 3090         node = obj_utils.create_test_node(
 3091             self.context, driver='fake-hardware',
 3092             provision_state=states.RESCUEFAIL,
 3093             target_provision_state=states.RESCUE,
 3094             driver_internal_info={'agent_url': 'url'})
 3095         with task_manager.acquire(self.context, node.uuid) as task:
 3096             self.service._do_node_rescue_abort(task)
 3097             clean_up_mock.assert_called_once_with(task.driver.rescue, task)
 3098             self.assertIsNotNone(task.node.last_error)
 3099             self.assertFalse(task.node.maintenance)
 3100             self.assertNotIn('agent_url', task.node.driver_internal_info)
 3101 
 3102     @mock.patch.object(fake.FakeRescue, 'clean_up', autospec=True)
 3103     def test__do_node_rescue_abort_clean_up_fail(self, clean_up_mock):
 3104         clean_up_mock.side_effect = Exception('Surprise')
 3105         node = obj_utils.create_test_node(
 3106             self.context, driver='fake-hardware',
 3107             provision_state=states.RESCUEFAIL)
 3108         with task_manager.acquire(self.context, node.uuid) as task:
 3109             self.service._do_node_rescue_abort(task)
 3110             clean_up_mock.assert_called_once_with(task.driver.rescue, task)
 3111             self.assertIsNotNone(task.node.last_error)
 3112             self.assertIsNotNone(task.node.maintenance_reason)
 3113             self.assertTrue(task.node.maintenance)
 3114             self.assertEqual('rescue abort failure',
 3115                              task.node.fault)
 3116 
 3117 
 3118 @mgr_utils.mock_record_keepalive
 3119 class DoNodeVerifyTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
 3120     @mock.patch('ironic.objects.node.NodeCorrectedPowerStateNotification',
 3121                 autospec=True)
 3122     @mock.patch('ironic.drivers.modules.fake.FakePower.get_power_state',
 3123                 autospec=True)
 3124     @mock.patch('ironic.drivers.modules.fake.FakePower.validate',
 3125                 autospec=True)
 3126     def test__do_node_verify(self, mock_validate, mock_get_power_state,
 3127                              mock_notif):
 3128         self._start_service()
 3129         mock_get_power_state.return_value = states.POWER_OFF
 3130         # Required for exception handling
 3131         mock_notif.__name__ = 'NodeCorrectedPowerStateNotification'
 3132         node = obj_utils.create_test_node(
 3133             self.context, driver='fake-hardware',
 3134             provision_state=states.VERIFYING,
 3135             target_provision_state=states.MANAGEABLE,
 3136             last_error=None,
 3137             power_state=states.NOSTATE)
 3138 
 3139         with task_manager.acquire(
 3140                 self.context, node['id'], shared=False) as task:
 3141             self.service._do_node_verify(task)
 3142 
 3143         self._stop_service()
 3144 
 3145         # 1 notification should be sent -
 3146         # baremetal.node.power_state_corrected.success
 3147         mock_notif.assert_called_once_with(publisher=mock.ANY,
 3148                                            event_type=mock.ANY,
 3149                                            level=mock.ANY,
 3150                                            payload=mock.ANY)
 3151         mock_notif.return_value.emit.assert_called_once_with(mock.ANY)
 3152 
 3153         node.refresh()
 3154 
 3155         mock_validate.assert_called_once_with(mock.ANY, task)
 3156         mock_get_power_state.assert_called_once_with(mock.ANY, task)
 3157 
 3158         self.assertEqual(states.MANAGEABLE, node.provision_state)
 3159         self.assertIsNone(node.target_provision_state)
 3160         self.assertIsNone(node.last_error)
 3161         self.assertEqual(states.POWER_OFF, node.power_state)
 3162 
 3163     @mock.patch('ironic.drivers.modules.fake.FakePower.get_power_state',
 3164                 autospec=True)
 3165     @mock.patch('ironic.drivers.modules.fake.FakePower.validate',
 3166                 autospec=True)
 3167     def test__do_node_verify_validation_fails(self, mock_validate,
 3168                                               mock_get_power_state):
 3169         self._start_service()
 3170         node = obj_utils.create_test_node(
 3171             self.context, driver='fake-hardware',
 3172             provision_state=states.VERIFYING,
 3173             target_provision_state=states.MANAGEABLE,
 3174             last_error=None,
 3175             power_state=states.NOSTATE)
 3176 
 3177         mock_validate.side_effect = RuntimeError("boom")
 3178 
 3179         with task_manager.acquire(
 3180                 self.context, node['id'], shared=False) as task:
 3181             self.service._do_node_verify(task)
 3182 
 3183         self._stop_service()
 3184         node.refresh()
 3185 
 3186         mock_validate.assert_called_once_with(mock.ANY, task)
 3187 
 3188         self.assertEqual(states.ENROLL, node.provision_state)
 3189         self.assertIsNone(node.target_provision_state)
 3190         self.assertTrue(node.last_error)
 3191         self.assertFalse(mock_get_power_state.called)
 3192 
 3193     @mock.patch('ironic.drivers.modules.fake.FakePower.get_power_state',
 3194                 autospec=True)
 3195     @mock.patch('ironic.drivers.modules.fake.FakePower.validate',
 3196                 autospec=True)
 3197     def test__do_node_verify_get_state_fails(self, mock_validate,
 3198                                              mock_get_power_state):
 3199         self._start_service()
 3200         node = obj_utils.create_test_node(
 3201             self.context, driver='fake-hardware',
 3202             provision_state=states.VERIFYING,
 3203             target_provision_state=states.MANAGEABLE,
 3204             last_error=None,
 3205             power_state=states.NOSTATE)
 3206 
 3207         mock_get_power_state.side_effect = RuntimeError("boom")
 3208 
 3209         with task_manager.acquire(
 3210                 self.context, node['id'], shared=False) as task:
 3211             self.service._do_node_verify(task)
 3212 
 3213         self._stop_service()
 3214         node.refresh()
 3215 
 3216         mock_get_power_state.assert_called_once_with(mock.ANY, task)
 3217 
 3218         self.assertEqual(states.ENROLL, node.provision_state)
 3219         self.assertIsNone(node.target_provision_state)
 3220         self.assertTrue(node.last_error)
 3221 
 3222 
 3223 @mgr_utils.mock_record_keepalive
 3224 class MiscTestCase(mgr_utils.ServiceSetUpMixin, mgr_utils.CommonMixIn,
 3225                    db_base.DbTestCase):
 3226     def test__mapped_to_this_conductor(self):
 3227         self._start_service()
 3228         n = db_utils.get_test_node()
 3229         self.assertTrue(self.service._mapped_to_this_conductor(
 3230             n['uuid'], 'fake-hardware', ''))
 3231         self.assertFalse(self.service._mapped_to_this_conductor(
 3232             n['uuid'], 'fake-hardware', 'foogroup'))
 3233         self.assertFalse(self.service._mapped_to_this_conductor(n['uuid'],
 3234                                                                 'otherdriver',
 3235                                                                 ''))
 3236 
 3237     @mock.patch.object(images, 'is_whole_disk_image', autospec=True)
 3238     def test_validate_dynamic_driver_interfaces(self, mock_iwdi):
 3239         mock_iwdi.return_value = False
 3240         target_raid_config = {'logical_disks': [{'size_gb': 1,
 3241                                                  'raid_level': '1'}]}
 3242         node = obj_utils.create_test_node(
 3243             self.context, driver='fake-hardware',
 3244             target_raid_config=target_raid_config,
 3245             network_interface='noop')
 3246         ret = self.service.validate_driver_interfaces(self.context,
 3247                                                       node.uuid)
 3248         expected = {'console': {'result': True},
 3249                     'power': {'result': True},
 3250                     'inspect': {'result': True},
 3251                     'management': {'result': True},
 3252                     'boot': {'result': True},
 3253                     'raid': {'result': True},
 3254                     'deploy': {'result': True},
 3255                     'network': {'result': True},
 3256                     'storage': {'result': True},
 3257                     'rescue': {'result': True},
 3258                     'bios': {'result': True}}
 3259         self.assertEqual(expected, ret)
 3260         mock_iwdi.assert_called_once_with(self.context, node.instance_info)
 3261 
 3262     @mock.patch.object(fake.FakeDeploy, 'validate', autospec=True)
 3263     @mock.patch.object(images, 'is_whole_disk_image', autospec=True)
 3264     def test_validate_driver_interfaces_validation_fail(self, mock_iwdi,
 3265                                                         mock_val):
 3266         mock_iwdi.return_value = False
 3267         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 3268                                           network_interface='noop')
 3269         reason = 'fake reason'
 3270         mock_val.side_effect = exception.InvalidParameterValue(reason)
 3271         ret = self.service.validate_driver_interfaces(self.context,
 3272                                                       node.uuid)
 3273         self.assertFalse(ret['deploy']['result'])
 3274         self.assertEqual(reason, ret['deploy']['reason'])
 3275         mock_iwdi.assert_called_once_with(self.context, node.instance_info)
 3276 
 3277     @mock.patch.object(fake.FakeDeploy, 'validate', autospec=True)
 3278     @mock.patch.object(images, 'is_whole_disk_image', autospec=True)
 3279     def test_validate_driver_interfaces_validation_fail_unexpected(
 3280             self, mock_iwdi, mock_val):
 3281         node = obj_utils.create_test_node(self.context, driver='fake-hardware')
 3282         mock_val.side_effect = Exception('boom')
 3283         ret = self.service.validate_driver_interfaces(self.context,
 3284                                                       node.uuid)
 3285         reason = ('Unexpected exception, traceback saved '
 3286                   'into log by ironic conductor service '
 3287                   'that is running on test-host: boom')
 3288         self.assertFalse(ret['deploy']['result'])
 3289         self.assertEqual(reason, ret['deploy']['reason'])
 3290 
 3291         mock_iwdi.assert_called_once_with(self.context, node.instance_info)
 3292 
 3293     @mock.patch.object(images, 'is_whole_disk_image', autospec=True)
 3294     def test_validate_driver_interfaces_validation_fail_instance_traits(
 3295             self, mock_iwdi):
 3296         mock_iwdi.return_value = False
 3297         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 3298                                           network_interface='noop')
 3299         with mock.patch(
 3300                 'ironic.conductor.utils.validate_instance_info_traits',
 3301                 autospec=True) as ii_traits:
 3302             reason = 'fake reason'
 3303             ii_traits.side_effect = exception.InvalidParameterValue(reason)
 3304             ret = self.service.validate_driver_interfaces(self.context,
 3305                                                           node.uuid)
 3306             self.assertFalse(ret['deploy']['result'])
 3307             self.assertEqual(reason, ret['deploy']['reason'])
 3308             mock_iwdi.assert_called_once_with(self.context, node.instance_info)
 3309 
 3310     @mock.patch.object(images, 'is_whole_disk_image', autospec=True)
 3311     def test_validate_driver_interfaces_validation_fail_deploy_templates(
 3312             self, mock_iwdi):
 3313         mock_iwdi.return_value = False
 3314         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 3315                                           network_interface='noop')
 3316         with mock.patch(
 3317                 'ironic.conductor.steps.validate_deploy_templates',
 3318                 autospec=True) as mock_validate:
 3319             reason = 'fake reason'
 3320             mock_validate.side_effect = exception.InvalidParameterValue(reason)
 3321             ret = self.service.validate_driver_interfaces(self.context,
 3322                                                           node.uuid)
 3323             self.assertFalse(ret['deploy']['result'])
 3324             self.assertEqual(reason, ret['deploy']['reason'])
 3325             mock_iwdi.assert_called_once_with(self.context, node.instance_info)
 3326 
 3327     @mock.patch.object(manager.ConductorManager, '_fail_if_in_state',
 3328                        autospec=True)
 3329     @mock.patch.object(manager.ConductorManager, '_mapped_to_this_conductor',
 3330                        autospec=True)
 3331     @mock.patch.object(dbapi.IMPL, 'get_nodeinfo_list', autospec=True)
 3332     def test_iter_nodes(self, mock_nodeinfo_list, mock_mapped,
 3333                         mock_fail_if_state):
 3334         self._start_service()
 3335         self.columns = ['uuid', 'driver', 'conductor_group', 'id']
 3336         nodes = [self._create_node(id=i, driver='fake-hardware',
 3337                                    conductor_group='')
 3338                  for i in range(2)]
 3339         mock_nodeinfo_list.return_value = self._get_nodeinfo_list_response(
 3340             nodes)
 3341         mock_mapped.side_effect = [True, False]
 3342 
 3343         result = list(self.service.iter_nodes(fields=['id'],
 3344                                               filters=mock.sentinel.filters))
 3345         self.assertEqual([(nodes[0].uuid, 'fake-hardware', '', 0)], result)
 3346         mock_nodeinfo_list.assert_called_once_with(
 3347             columns=self.columns, filters=mock.sentinel.filters)
 3348         expected_calls = [mock.call(mock.ANY, mock.ANY,
 3349                                     {'provision_state': 'deploying',
 3350                                      'reserved': False},
 3351                                     'deploying',
 3352                                     'provision_updated_at',
 3353                                     last_error=mock.ANY),
 3354                           mock.call(mock.ANY, mock.ANY,
 3355                                     {'provision_state': 'cleaning',
 3356                                      'reserved': False},
 3357                                     'cleaning',
 3358                                     'provision_updated_at',
 3359                                     last_error=mock.ANY)]
 3360         mock_fail_if_state.assert_has_calls(expected_calls)
 3361 
 3362     @mock.patch.object(dbapi.IMPL, 'get_nodeinfo_list', autospec=True)
 3363     def test_iter_nodes_shutdown(self, mock_nodeinfo_list):
 3364         self._start_service()
 3365         self.columns = ['uuid', 'driver', 'conductor_group', 'id']
 3366         nodes = [self._create_node(driver='fake-hardware')]
 3367         mock_nodeinfo_list.return_value = self._get_nodeinfo_list_response(
 3368             nodes)
 3369         self.service._shutdown = True
 3370 
 3371         result = list(self.service.iter_nodes(fields=['id'],
 3372                                               filters=mock.sentinel.filters))
 3373         self.assertEqual([], result)
 3374 
 3375 
 3376 @mgr_utils.mock_record_keepalive
 3377 class ConsoleTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
 3378     def test_set_console_mode_worker_pool_full(self):
 3379         node = obj_utils.create_test_node(self.context, driver='fake-hardware')
 3380         self._start_service()
 3381         with mock.patch.object(self.service,
 3382                                '_spawn_worker', autospec=True) as spawn_mock:
 3383             spawn_mock.side_effect = exception.NoFreeConductorWorker()
 3384 
 3385             exc = self.assertRaises(messaging.rpc.ExpectedException,
 3386                                     self.service.set_console_mode,
 3387                                     self.context, node.uuid, True)
 3388             # Compare true exception hidden by @messaging.expected_exceptions
 3389             self.assertEqual(exception.NoFreeConductorWorker, exc.exc_info[0])
 3390             self._stop_service()
 3391             spawn_mock.assert_called_once_with(mock.ANY, mock.ANY, mock.ANY)
 3392 
 3393     @mock.patch.object(notification_utils, 'emit_console_notification',
 3394                        autospec=True)
 3395     def test_set_console_mode_enabled(self, mock_notify):
 3396         node = obj_utils.create_test_node(self.context, driver='fake-hardware')
 3397         self._start_service()
 3398         self.service.set_console_mode(self.context, node.uuid, True)
 3399         self._stop_service()
 3400         node.refresh()
 3401         self.assertTrue(node.console_enabled)
 3402         mock_notify.assert_has_calls(
 3403             [mock.call(mock.ANY, 'console_set',
 3404                        obj_fields.NotificationStatus.START),
 3405              mock.call(mock.ANY, 'console_set',
 3406                        obj_fields.NotificationStatus.END)])
 3407 
 3408     @mock.patch.object(notification_utils, 'emit_console_notification',
 3409                        autospec=True)
 3410     def test_set_console_mode_disabled(self, mock_notify):
 3411         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 3412                                           console_enabled=True)
 3413         self._start_service()
 3414         self.service.set_console_mode(self.context, node.uuid, False)
 3415         self._stop_service()
 3416         node.refresh()
 3417         self.assertFalse(node.console_enabled)
 3418         mock_notify.assert_has_calls(
 3419             [mock.call(mock.ANY, 'console_set',
 3420                        obj_fields.NotificationStatus.START),
 3421              mock.call(mock.ANY, 'console_set',
 3422                        obj_fields.NotificationStatus.END)])
 3423 
 3424     @mock.patch.object(fake.FakeConsole, 'validate', autospec=True)
 3425     def test_set_console_mode_validation_fail(self, mock_val):
 3426         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 3427                                           last_error=None)
 3428         self._start_service()
 3429         mock_val.side_effect = exception.InvalidParameterValue('error')
 3430         exc = self.assertRaises(messaging.rpc.ExpectedException,
 3431                                 self.service.set_console_mode,
 3432                                 self.context, node.uuid, True)
 3433         # Compare true exception hidden by @messaging.expected_exceptions
 3434         self.assertEqual(exception.InvalidParameterValue, exc.exc_info[0])
 3435 
 3436     @mock.patch.object(fake.FakeConsole, 'start_console', autospec=True)
 3437     @mock.patch.object(notification_utils, 'emit_console_notification',
 3438                        autospec=True)
 3439     def test_set_console_mode_start_fail(self, mock_notify, mock_sc):
 3440         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 3441                                           last_error=None,
 3442                                           console_enabled=False)
 3443         self._start_service()
 3444         mock_sc.side_effect = exception.IronicException('test-error')
 3445         self.service.set_console_mode(self.context, node.uuid, True)
 3446         self._stop_service()
 3447         mock_sc.assert_called_once_with(mock.ANY, mock.ANY)
 3448         node.refresh()
 3449         self.assertIsNotNone(node.last_error)
 3450         mock_notify.assert_has_calls(
 3451             [mock.call(mock.ANY, 'console_set',
 3452                        obj_fields.NotificationStatus.START),
 3453              mock.call(mock.ANY, 'console_set',
 3454                        obj_fields.NotificationStatus.ERROR)])
 3455 
 3456     @mock.patch.object(fake.FakeConsole, 'stop_console', autospec=True)
 3457     @mock.patch.object(notification_utils, 'emit_console_notification',
 3458                        autospec=True)
 3459     def test_set_console_mode_stop_fail(self, mock_notify, mock_sc):
 3460         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 3461                                           last_error=None,
 3462                                           console_enabled=True)
 3463         self._start_service()
 3464         mock_sc.side_effect = exception.IronicException('test-error')
 3465         self.service.set_console_mode(self.context, node.uuid, False)
 3466         self._stop_service()
 3467         mock_sc.assert_called_once_with(mock.ANY, mock.ANY)
 3468         node.refresh()
 3469         self.assertIsNotNone(node.last_error)
 3470         mock_notify.assert_has_calls(
 3471             [mock.call(mock.ANY, 'console_set',
 3472                        obj_fields.NotificationStatus.START),
 3473              mock.call(mock.ANY, 'console_set',
 3474                        obj_fields.NotificationStatus.ERROR)])
 3475 
 3476     @mock.patch.object(fake.FakeConsole, 'start_console', autospec=True)
 3477     @mock.patch.object(notification_utils, 'emit_console_notification',
 3478                        autospec=True)
 3479     def test_enable_console_already_enabled(self, mock_notify, mock_sc):
 3480         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 3481                                           console_enabled=True)
 3482         self._start_service()
 3483         self.service.set_console_mode(self.context, node.uuid, True)
 3484         self._stop_service()
 3485         self.assertFalse(mock_sc.called)
 3486         self.assertFalse(mock_notify.called)
 3487 
 3488     @mock.patch.object(fake.FakeConsole, 'stop_console', autospec=True)
 3489     @mock.patch.object(notification_utils, 'emit_console_notification',
 3490                        autospec=True)
 3491     def test_disable_console_already_disabled(self, mock_notify, mock_sc):
 3492         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 3493                                           console_enabled=False)
 3494         self._start_service()
 3495         self.service.set_console_mode(self.context, node.uuid, False)
 3496         self._stop_service()
 3497         self.assertFalse(mock_sc.called)
 3498         self.assertFalse(mock_notify.called)
 3499 
 3500     @mock.patch.object(fake.FakeConsole, 'get_console', autospec=True)
 3501     def test_get_console(self, mock_gc):
 3502         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 3503                                           console_enabled=True)
 3504         console_info = {'test': 'test info'}
 3505         mock_gc.return_value = console_info
 3506         data = self.service.get_console_information(self.context,
 3507                                                     node.uuid)
 3508         self.assertEqual(console_info, data)
 3509 
 3510     def test_get_console_disabled(self):
 3511         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 3512                                           console_enabled=False)
 3513         exc = self.assertRaises(messaging.rpc.ExpectedException,
 3514                                 self.service.get_console_information,
 3515                                 self.context, node.uuid)
 3516         # Compare true exception hidden by @messaging.expected_exceptions
 3517         self.assertEqual(exception.NodeConsoleNotEnabled, exc.exc_info[0])
 3518 
 3519     @mock.patch.object(fake.FakeConsole, 'validate', autospec=True)
 3520     def test_get_console_validate_fail(self, mock_val):
 3521         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 3522                                           console_enabled=True)
 3523         mock_val.side_effect = exception.InvalidParameterValue('error')
 3524         exc = self.assertRaises(messaging.rpc.ExpectedException,
 3525                                 self.service.get_console_information,
 3526                                 self.context, node.uuid)
 3527         # Compare true exception hidden by @messaging.expected_exceptions
 3528         self.assertEqual(exception.InvalidParameterValue, exc.exc_info[0])
 3529 
 3530 
 3531 @mgr_utils.mock_record_keepalive
 3532 class DestroyNodeTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
 3533 
 3534     def test_destroy_node(self):
 3535         self._start_service()
 3536         for state in states.DELETE_ALLOWED_STATES:
 3537             node = obj_utils.create_test_node(self.context,
 3538                                               provision_state=state)
 3539             self.service.destroy_node(self.context, node.uuid)
 3540             self.assertRaises(exception.NodeNotFound,
 3541                               self.dbapi.get_node_by_uuid,
 3542                               node.uuid)
 3543 
 3544     def test_destroy_node_reserved(self):
 3545         self._start_service()
 3546         fake_reservation = 'fake-reserv'
 3547         node = obj_utils.create_test_node(self.context,
 3548                                           reservation=fake_reservation)
 3549 
 3550         exc = self.assertRaises(messaging.rpc.ExpectedException,
 3551                                 self.service.destroy_node,
 3552                                 self.context, node.uuid)
 3553         # Compare true exception hidden by @messaging.expected_exceptions
 3554         self.assertEqual(exception.NodeLocked, exc.exc_info[0])
 3555         # Verify existing reservation wasn't broken.
 3556         node.refresh()
 3557         self.assertEqual(fake_reservation, node.reservation)
 3558 
 3559     def test_destroy_node_associated(self):
 3560         self._start_service()
 3561         node = obj_utils.create_test_node(
 3562             self.context, instance_uuid=uuidutils.generate_uuid())
 3563 
 3564         exc = self.assertRaises(messaging.rpc.ExpectedException,
 3565                                 self.service.destroy_node,
 3566                                 self.context, node.uuid)
 3567         # Compare true exception hidden by @messaging.expected_exceptions
 3568         self.assertEqual(exception.NodeAssociated, exc.exc_info[0])
 3569 
 3570         # Verify reservation was released.
 3571         node.refresh()
 3572         self.assertIsNone(node.reservation)
 3573 
 3574     def test_destroy_node_with_allocation(self):
 3575         # Nodes with allocations can be deleted in maintenance
 3576         node = obj_utils.create_test_node(self.context,
 3577                                           provision_state=states.ACTIVE,
 3578                                           maintenance=True)
 3579         alloc = obj_utils.create_test_allocation(self.context)
 3580         # Establish cross-linking between the node and the allocation
 3581         alloc.node_id = node.id
 3582         alloc.save()
 3583         node.refresh()
 3584 
 3585         self.service.destroy_node(self.context, node.uuid)
 3586         self.assertRaises(exception.NodeNotFound,
 3587                           self.dbapi.get_node_by_uuid,
 3588                           node.uuid)
 3589         self.assertRaises(exception.AllocationNotFound,
 3590                           self.dbapi.get_allocation_by_id,
 3591                           alloc.id)
 3592 
 3593     def test_destroy_node_invalid_provision_state(self):
 3594         self._start_service()
 3595         node = obj_utils.create_test_node(self.context,
 3596                                           provision_state=states.ACTIVE)
 3597 
 3598         exc = self.assertRaises(messaging.rpc.ExpectedException,
 3599                                 self.service.destroy_node,
 3600                                 self.context, node.uuid)
 3601         # Compare true exception hidden by @messaging.expected_exceptions
 3602         self.assertEqual(exception.InvalidState, exc.exc_info[0])
 3603         # Verify reservation was released.
 3604         node.refresh()
 3605         self.assertIsNone(node.reservation)
 3606 
 3607     def test_destroy_node_protected_provision_state_available(self):
 3608         CONF.set_override('allow_deleting_available_nodes',
 3609                           False, group='conductor')
 3610         self._start_service()
 3611         node = obj_utils.create_test_node(self.context,
 3612                                           provision_state=states.AVAILABLE)
 3613 
 3614         exc = self.assertRaises(messaging.rpc.ExpectedException,
 3615                                 self.service.destroy_node,
 3616                                 self.context, node.uuid)
 3617         # Compare true exception hidden by @messaging.expected_exceptions
 3618         self.assertEqual(exception.InvalidState, exc.exc_info[0])
 3619         # Verify reservation was released.
 3620         node.refresh()
 3621         self.assertIsNone(node.reservation)
 3622 
 3623     def test_destroy_node_protected(self):
 3624         self._start_service()
 3625         node = obj_utils.create_test_node(self.context,
 3626                                           provision_state=states.ACTIVE,
 3627                                           protected=True,
 3628                                           # Even in maintenance the protected
 3629                                           # nodes are not deleted
 3630                                           maintenance=True)
 3631 
 3632         exc = self.assertRaises(messaging.rpc.ExpectedException,
 3633                                 self.service.destroy_node,
 3634                                 self.context, node.uuid)
 3635         # Compare true exception hidden by @messaging.expected_exceptions
 3636         self.assertEqual(exception.NodeProtected, exc.exc_info[0])
 3637         # Verify reservation was released.
 3638         node.refresh()
 3639         self.assertIsNone(node.reservation)
 3640 
 3641     def test_destroy_node_allowed_in_maintenance(self):
 3642         self._start_service()
 3643         node = obj_utils.create_test_node(
 3644             self.context, instance_uuid=uuidutils.generate_uuid(),
 3645             provision_state=states.ACTIVE, maintenance=True)
 3646         self.service.destroy_node(self.context, node.uuid)
 3647         self.assertRaises(exception.NodeNotFound,
 3648                           self.dbapi.get_node_by_uuid,
 3649                           node.uuid)
 3650 
 3651     def test_destroy_node_power_off(self):
 3652         self._start_service()
 3653         node = obj_utils.create_test_node(self.context,
 3654                                           power_state=states.POWER_OFF)
 3655         self.service.destroy_node(self.context, node.uuid)
 3656 
 3657     @mock.patch.object(fake.FakeConsole, 'stop_console', autospec=True)
 3658     @mock.patch.object(notification_utils, 'emit_console_notification',
 3659                        autospec=True)
 3660     def test_destroy_node_console_enabled(self, mock_notify, mock_sc):
 3661         self._start_service()
 3662         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 3663                                           console_enabled=True)
 3664         self.service.destroy_node(self.context, node.uuid)
 3665         mock_sc.assert_called_once_with(mock.ANY, mock.ANY)
 3666         self.assertRaises(exception.NodeNotFound,
 3667                           self.dbapi.get_node_by_uuid,
 3668                           node.uuid)
 3669         mock_notify.assert_has_calls(
 3670             [mock.call(mock.ANY, 'console_set',
 3671                        obj_fields.NotificationStatus.START),
 3672              mock.call(mock.ANY, 'console_set',
 3673                        obj_fields.NotificationStatus.END)])
 3674 
 3675     @mock.patch.object(fake.FakeConsole, 'stop_console', autospec=True)
 3676     @mock.patch.object(notification_utils, 'emit_console_notification',
 3677                        autospec=True)
 3678     def test_destroy_node_console_disable_fail(self, mock_notify, mock_sc):
 3679         self._start_service()
 3680         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 3681                                           console_enabled=True)
 3682         mock_sc.side_effect = Exception()
 3683         self.service.destroy_node(self.context, node.uuid)
 3684         mock_sc.assert_called_once_with(mock.ANY, mock.ANY)
 3685         self.assertRaises(exception.NodeNotFound,
 3686                           self.dbapi.get_node_by_uuid,
 3687                           node.uuid)
 3688         mock_notify.assert_has_calls(
 3689             [mock.call(mock.ANY, 'console_set',
 3690                        obj_fields.NotificationStatus.START),
 3691              mock.call(mock.ANY, 'console_set',
 3692                        obj_fields.NotificationStatus.ERROR)])
 3693 
 3694     @mock.patch.object(fake.FakePower, 'set_power_state', autospec=True)
 3695     def test_destroy_node_adopt_failed_no_power_change(self, mock_power):
 3696         self._start_service()
 3697         node = obj_utils.create_test_node(self.context,
 3698                                           driver='fake-hardware',
 3699                                           provision_state=states.ADOPTFAIL)
 3700         self.service.destroy_node(self.context, node.uuid)
 3701         self.assertFalse(mock_power.called)
 3702 
 3703     def test_destroy_node_broken_driver(self):
 3704         node = obj_utils.create_test_node(self.context,
 3705                                           power_interface='broken')
 3706         self._start_service()
 3707         self.service.destroy_node(self.context, node.uuid)
 3708         self.assertRaises(exception.NodeNotFound,
 3709                           self.dbapi.get_node_by_uuid,
 3710                           node.uuid)
 3711 
 3712 
 3713 @mgr_utils.mock_record_keepalive
 3714 class CreatePortTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
 3715 
 3716     @mock.patch.object(conductor_utils, 'validate_port_physnet', autospec=True)
 3717     def test_create_port(self, mock_validate):
 3718         node = obj_utils.create_test_node(self.context, driver='fake-hardware')
 3719         port = obj_utils.get_test_port(self.context, node_id=node.id,
 3720                                        extra={'foo': 'bar'})
 3721         res = self.service.create_port(self.context, port)
 3722         self.assertEqual({'foo': 'bar'}, res.extra)
 3723         res = objects.Port.get_by_uuid(self.context, port['uuid'])
 3724         self.assertEqual({'foo': 'bar'}, res.extra)
 3725         mock_validate.assert_called_once_with(mock.ANY, port)
 3726 
 3727     def test_create_port_node_locked(self):
 3728         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 3729                                           reservation='fake-reserv')
 3730         port = obj_utils.get_test_port(self.context, node_id=node.id)
 3731         exc = self.assertRaises(messaging.rpc.ExpectedException,
 3732                                 self.service.create_port,
 3733                                 self.context, port)
 3734         # Compare true exception hidden by @messaging.expected_exceptions
 3735         self.assertEqual(exception.NodeLocked, exc.exc_info[0])
 3736         self.assertRaises(exception.PortNotFound, port.get_by_uuid,
 3737                           self.context, port.uuid)
 3738 
 3739     @mock.patch.object(conductor_utils, 'validate_port_physnet', autospec=True)
 3740     def test_create_port_mac_exists(self, mock_validate):
 3741         node = obj_utils.create_test_node(self.context, driver='fake-hardware')
 3742         port = obj_utils.create_test_port(self.context, node_id=node.id)
 3743         port = obj_utils.get_test_port(self.context, node_id=node.id,
 3744                                        uuid=uuidutils.generate_uuid())
 3745         exc = self.assertRaises(messaging.rpc.ExpectedException,
 3746                                 self.service.create_port,
 3747                                 self.context, port)
 3748         # Compare true exception hidden by @messaging.expected_exceptions
 3749         self.assertEqual(exception.MACAlreadyExists, exc.exc_info[0])
 3750         self.assertRaises(exception.PortNotFound, port.get_by_uuid,
 3751                           self.context, port.uuid)
 3752 
 3753     @mock.patch.object(conductor_utils, 'validate_port_physnet', autospec=True)
 3754     def test_create_port_physnet_validation_failure_conflict(self,
 3755                                                              mock_validate):
 3756         mock_validate.side_effect = exception.Conflict
 3757         node = obj_utils.create_test_node(self.context, driver='fake-hardware')
 3758         port = obj_utils.get_test_port(self.context, node_id=node.id)
 3759         exc = self.assertRaises(messaging.rpc.ExpectedException,
 3760                                 self.service.create_port,
 3761                                 self.context, port)
 3762         # Compare true exception hidden by @messaging.expected_exceptions
 3763         self.assertEqual(exception.Conflict, exc.exc_info[0])
 3764         self.assertRaises(exception.PortNotFound, port.get_by_uuid,
 3765                           self.context, port.uuid)
 3766 
 3767     @mock.patch.object(conductor_utils, 'validate_port_physnet', autospec=True)
 3768     def test_create_port_physnet_validation_failure_inconsistent(
 3769             self, mock_validate):
 3770         mock_validate.side_effect = exception.PortgroupPhysnetInconsistent(
 3771             portgroup='pg1', physical_networks='physnet1, physnet2')
 3772         node = obj_utils.create_test_node(self.context, driver='fake-hardware')
 3773         port = obj_utils.get_test_port(self.context, node_id=node.id)
 3774         exc = self.assertRaises(messaging.rpc.ExpectedException,
 3775                                 self.service.create_port,
 3776                                 self.context, port)
 3777         # Compare true exception hidden by @messaging.expected_exceptions
 3778         self.assertEqual(exception.PortgroupPhysnetInconsistent,
 3779                          exc.exc_info[0])
 3780         self.assertRaises(exception.PortNotFound, port.get_by_uuid,
 3781                           self.context, port.uuid)
 3782 
 3783 
 3784 @mgr_utils.mock_record_keepalive
 3785 class UpdatePortTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
 3786 
 3787     @mock.patch.object(conductor_utils, 'validate_port_physnet', autospec=True)
 3788     @mock.patch.object(n_flat.FlatNetwork, 'port_changed', autospec=True)
 3789     @mock.patch.object(n_flat.FlatNetwork, 'validate', autospec=True)
 3790     def test_update_port(self, mock_val, mock_pc, mock_vpp):
 3791         node = obj_utils.create_test_node(self.context, driver='fake-hardware')
 3792 
 3793         port = obj_utils.create_test_port(self.context,
 3794                                           node_id=node.id,
 3795                                           extra={'foo': 'bar'})
 3796         new_extra = {'foo': 'baz'}
 3797         port.extra = new_extra
 3798         res = self.service.update_port(self.context, port)
 3799         self.assertEqual(new_extra, res.extra)
 3800         mock_val.assert_called_once_with(mock.ANY, mock.ANY)
 3801         mock_pc.assert_called_once_with(mock.ANY, mock.ANY, port)
 3802         mock_vpp.assert_called_once_with(mock.ANY, port)
 3803 
 3804     def test_update_port_node_locked(self):
 3805         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 3806                                           reservation='fake-reserv')
 3807 
 3808         port = obj_utils.create_test_port(self.context, node_id=node.id)
 3809         port.extra = {'foo': 'baz'}
 3810         exc = self.assertRaises(messaging.rpc.ExpectedException,
 3811                                 self.service.update_port,
 3812                                 self.context, port)
 3813         # Compare true exception hidden by @messaging.expected_exceptions
 3814         self.assertEqual(exception.NodeLocked, exc.exc_info[0])
 3815 
 3816     @mock.patch.object(n_flat.FlatNetwork, 'port_changed', autospec=True)
 3817     @mock.patch.object(n_flat.FlatNetwork, 'validate', autospec=True)
 3818     def test_update_port_port_changed_failure(self, mock_val, mock_pc):
 3819         node = obj_utils.create_test_node(self.context, driver='fake-hardware')
 3820 
 3821         port = obj_utils.create_test_port(self.context,
 3822                                           node_id=node.id)
 3823         old_address = port.address
 3824         port.address = '11:22:33:44:55:bb'
 3825         mock_pc.side_effect = (exception.FailedToUpdateMacOnPort('boom'))
 3826         exc = self.assertRaises(messaging.rpc.ExpectedException,
 3827                                 self.service.update_port,
 3828                                 self.context, port)
 3829         mock_pc.assert_called_once_with(mock.ANY, mock.ANY, port)
 3830         mock_val.assert_called_once_with(mock.ANY, mock.ANY)
 3831         self.assertEqual(exception.FailedToUpdateMacOnPort, exc.exc_info[0])
 3832         port.refresh()
 3833         self.assertEqual(old_address, port.address)
 3834 
 3835     @mock.patch.object(n_flat.FlatNetwork, 'port_changed', autospec=True)
 3836     @mock.patch.object(n_flat.FlatNetwork, 'validate', autospec=True)
 3837     def test_update_port_address_active_node(self, mock_val, mock_pc):
 3838         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 3839                                           instance_uuid=None,
 3840                                           provision_state='active')
 3841         port = obj_utils.create_test_port(self.context,
 3842                                           node_id=node.id,
 3843                                           extra={'vif_port_id': 'fake-id'})
 3844         old_address = port.address
 3845         new_address = '11:22:33:44:55:bb'
 3846         port.address = new_address
 3847         exc = self.assertRaises(messaging.rpc.ExpectedException,
 3848                                 self.service.update_port,
 3849                                 self.context, port)
 3850         # Compare true exception hidden by @messaging.expected_exceptions
 3851         self.assertEqual(exception.InvalidState, exc.exc_info[0])
 3852         port.refresh()
 3853         self.assertEqual(old_address, port.address)
 3854         self.assertFalse(mock_pc.called)
 3855         self.assertFalse(mock_val.called)
 3856 
 3857     @mock.patch.object(n_flat.FlatNetwork, 'port_changed', autospec=True)
 3858     @mock.patch.object(n_flat.FlatNetwork, 'validate', autospec=True)
 3859     def test_update_port_address_maintenance(self, mock_val, mock_pc):
 3860         node = obj_utils.create_test_node(
 3861             self.context, driver='fake-hardware', maintenance=True,
 3862             instance_uuid=uuidutils.generate_uuid(), provision_state='active')
 3863         port = obj_utils.create_test_port(self.context,
 3864                                           node_id=node.id,
 3865                                           extra={'vif_port_id': 'fake-id'})
 3866         new_address = '11:22:33:44:55:bb'
 3867         port.address = new_address
 3868         res = self.service.update_port(self.context, port)
 3869         self.assertEqual(new_address, res.address)
 3870         mock_val.assert_called_once_with(mock.ANY, mock.ANY)
 3871         mock_pc.assert_called_once_with(mock.ANY, mock.ANY, port)
 3872 
 3873     @mock.patch.object(n_flat.FlatNetwork, 'port_changed', autospec=True)
 3874     @mock.patch.object(n_flat.FlatNetwork, 'validate', autospec=True)
 3875     def test_update_port_portgroup_active_node(self, mock_val, mock_pc):
 3876         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 3877                                           instance_uuid=None,
 3878                                           provision_state='active')
 3879         pg1 = obj_utils.create_test_portgroup(self.context, node_id=node.id)
 3880         pg2 = obj_utils.create_test_portgroup(
 3881             self.context, node_id=node.id, name='bar',
 3882             address='aa:bb:cc:dd:ee:ff', uuid=uuidutils.generate_uuid())
 3883         port = obj_utils.create_test_port(self.context,
 3884                                           node_id=node.id,
 3885                                           portgroup_id=pg1.id)
 3886         port.portgroup_id = pg2.id
 3887         exc = self.assertRaises(messaging.rpc.ExpectedException,
 3888                                 self.service.update_port,
 3889                                 self.context, port)
 3890         # Compare true exception hidden by @messaging.expected_exceptions
 3891         self.assertEqual(exception.InvalidState, exc.exc_info[0])
 3892         port.refresh()
 3893         self.assertEqual(pg1.id, port.portgroup_id)
 3894         self.assertFalse(mock_pc.called)
 3895         self.assertFalse(mock_val.called)
 3896 
 3897     @mock.patch.object(n_flat.FlatNetwork, 'port_changed', autospec=True)
 3898     @mock.patch.object(n_flat.FlatNetwork, 'validate', autospec=True)
 3899     def test_update_port_portgroup_enroll_node(self, mock_val, mock_pc):
 3900         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 3901                                           instance_uuid=None,
 3902                                           provision_state='enroll')
 3903         pg1 = obj_utils.create_test_portgroup(self.context, node_id=node.id)
 3904         pg2 = obj_utils.create_test_portgroup(
 3905             self.context, node_id=node.id, name='bar',
 3906             address='aa:bb:cc:dd:ee:ff', uuid=uuidutils.generate_uuid())
 3907         port = obj_utils.create_test_port(self.context,
 3908                                           node_id=node.id,
 3909                                           portgroup_id=pg1.id)
 3910         port.portgroup_id = pg2.id
 3911         self.service.update_port(self.context, port)
 3912         port.refresh()
 3913         self.assertEqual(pg2.id, port.portgroup_id)
 3914         mock_pc.assert_called_once_with(mock.ANY, mock.ANY, port)
 3915         mock_val.assert_called_once_with(mock.ANY, mock.ANY)
 3916 
 3917     def test_update_port_node_deleting_state(self):
 3918         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 3919                                           provision_state=states.DELETING)
 3920         port = obj_utils.create_test_port(self.context,
 3921                                           node_id=node.id,
 3922                                           extra={'foo': 'bar'})
 3923         old_pxe = port.pxe_enabled
 3924         port.pxe_enabled = True
 3925         exc = self.assertRaises(messaging.rpc.ExpectedException,
 3926                                 self.service.update_port,
 3927                                 self.context, port)
 3928         self.assertEqual(exception.InvalidState, exc.exc_info[0])
 3929         port.refresh()
 3930         self.assertEqual(old_pxe, port.pxe_enabled)
 3931 
 3932     @mock.patch.object(n_flat.FlatNetwork, 'port_changed', autospec=True)
 3933     @mock.patch.object(n_flat.FlatNetwork, 'validate', autospec=True)
 3934     def test_update_port_node_manageable_state(self, mock_val,
 3935                                                mock_pc):
 3936         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 3937                                           provision_state=states.MANAGEABLE)
 3938         port = obj_utils.create_test_port(self.context,
 3939                                           node_id=node.id,
 3940                                           extra={'foo': 'bar'})
 3941         port.pxe_enabled = True
 3942         self.service.update_port(self.context, port)
 3943         port.refresh()
 3944         self.assertEqual(True, port.pxe_enabled)
 3945         mock_val.assert_called_once_with(mock.ANY, mock.ANY)
 3946         mock_pc.assert_called_once_with(mock.ANY, mock.ANY, port)
 3947 
 3948     @mock.patch.object(n_flat.FlatNetwork, 'port_changed', autospec=True)
 3949     @mock.patch.object(n_flat.FlatNetwork, 'validate', autospec=True)
 3950     def test_update_port_to_node_in_inspect_wait_state(self, mock_val,
 3951                                                        mock_pc):
 3952         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 3953                                           provision_state=states.INSPECTWAIT)
 3954         port = obj_utils.create_test_port(self.context,
 3955                                           node_id=node.id,
 3956                                           extra={'foo': 'bar'})
 3957         port.pxe_enabled = True
 3958         self.service.update_port(self.context, port)
 3959         port.refresh()
 3960         self.assertEqual(True, port.pxe_enabled)
 3961         mock_val.assert_called_once_with(mock.ANY, mock.ANY)
 3962         mock_pc.assert_called_once_with(mock.ANY, mock.ANY, port)
 3963 
 3964     @mock.patch.object(n_flat.FlatNetwork, 'port_changed', autospec=True)
 3965     @mock.patch.object(n_flat.FlatNetwork, 'validate', autospec=True)
 3966     def test_update_port_node_active_state_and_maintenance(self, mock_val,
 3967                                                            mock_pc):
 3968         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 3969                                           provision_state=states.ACTIVE,
 3970                                           maintenance=True)
 3971         port = obj_utils.create_test_port(self.context,
 3972                                           node_id=node.id,
 3973                                           extra={'foo': 'bar'})
 3974         port.pxe_enabled = True
 3975         self.service.update_port(self.context, port)
 3976         port.refresh()
 3977         self.assertEqual(True, port.pxe_enabled)
 3978         mock_val.assert_called_once_with(mock.ANY, mock.ANY)
 3979         mock_pc.assert_called_once_with(mock.ANY, mock.ANY, port)
 3980 
 3981     @mock.patch.object(n_flat.FlatNetwork, 'port_changed', autospec=True)
 3982     @mock.patch.object(n_flat.FlatNetwork, 'validate', autospec=True)
 3983     def test_update_port_physnet_maintenance(self, mock_val, mock_pc):
 3984         node = obj_utils.create_test_node(
 3985             self.context, driver='fake-hardware', maintenance=True,
 3986             instance_uuid=uuidutils.generate_uuid(), provision_state='active')
 3987         port = obj_utils.create_test_port(self.context,
 3988                                           node_id=node.id,
 3989                                           extra={'vif_port_id': 'fake-id'})
 3990         new_physnet = 'physnet1'
 3991         port.physical_network = new_physnet
 3992         res = self.service.update_port(self.context, port)
 3993         self.assertEqual(new_physnet, res.physical_network)
 3994         mock_val.assert_called_once_with(mock.ANY, mock.ANY)
 3995         mock_pc.assert_called_once_with(mock.ANY, mock.ANY, port)
 3996 
 3997     def test_update_port_physnet_node_deleting_state(self):
 3998         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 3999                                           provision_state=states.DELETING)
 4000         port = obj_utils.create_test_port(self.context,
 4001                                           node_id=node.id,
 4002                                           extra={'foo': 'bar'})
 4003         old_physnet = port.physical_network
 4004         port.physical_network = 'physnet1'
 4005         exc = self.assertRaises(messaging.rpc.ExpectedException,
 4006                                 self.service.update_port,
 4007                                 self.context, port)
 4008         self.assertEqual(exception.InvalidState, exc.exc_info[0])
 4009         port.refresh()
 4010         self.assertEqual(old_physnet, port.physical_network)
 4011 
 4012     @mock.patch.object(conductor_utils, 'validate_port_physnet', autospec=True)
 4013     def test_update_port_physnet_validation_failure_conflict(self,
 4014                                                              mock_validate):
 4015         mock_validate.side_effect = exception.Conflict
 4016         node = obj_utils.create_test_node(self.context, driver='fake-hardware')
 4017         port = obj_utils.create_test_port(self.context, node_id=node.id,
 4018                                           uuid=uuidutils.generate_uuid())
 4019         port.extra = {'foo': 'bar'}
 4020         exc = self.assertRaises(messaging.rpc.ExpectedException,
 4021                                 self.service.update_port,
 4022                                 self.context, port)
 4023         # Compare true exception hidden by @messaging.expected_exceptions
 4024         self.assertEqual(exception.Conflict, exc.exc_info[0])
 4025         mock_validate.assert_called_once_with(mock.ANY, port)
 4026 
 4027     @mock.patch.object(conductor_utils, 'validate_port_physnet', autospec=True)
 4028     def test_update_port_physnet_validation_failure_inconsistent(
 4029             self, mock_validate):
 4030         mock_validate.side_effect = exception.PortgroupPhysnetInconsistent(
 4031             portgroup='pg1', physical_networks='physnet1, physnet2')
 4032         node = obj_utils.create_test_node(self.context, driver='fake-hardware')
 4033         port = obj_utils.create_test_port(self.context, node_id=node.id,
 4034                                           uuid=uuidutils.generate_uuid())
 4035         port.extra = {'foo': 'bar'}
 4036         exc = self.assertRaises(messaging.rpc.ExpectedException,
 4037                                 self.service.update_port,
 4038                                 self.context, port)
 4039         # Compare true exception hidden by @messaging.expected_exceptions
 4040         self.assertEqual(exception.PortgroupPhysnetInconsistent,
 4041                          exc.exc_info[0])
 4042         mock_validate.assert_called_once_with(mock.ANY, port)
 4043 
 4044 
 4045 @mgr_utils.mock_record_keepalive
 4046 class SensorsTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
 4047 
 4048     def test__filter_out_unsupported_types_all(self):
 4049         self._start_service()
 4050         CONF.set_override('send_sensor_data_types', ['All'], group='conductor')
 4051         fake_sensors_data = {"t1": {'f1': 'v1'}, "t2": {'f1': 'v1'}}
 4052         actual_result = (
 4053             self.service._filter_out_unsupported_types(fake_sensors_data))
 4054         expected_result = {"t1": {'f1': 'v1'}, "t2": {'f1': 'v1'}}
 4055         self.assertEqual(expected_result, actual_result)
 4056 
 4057     def test__filter_out_unsupported_types_part(self):
 4058         self._start_service()
 4059         CONF.set_override('send_sensor_data_types', ['t1'], group='conductor')
 4060         fake_sensors_data = {"t1": {'f1': 'v1'}, "t2": {'f1': 'v1'}}
 4061         actual_result = (
 4062             self.service._filter_out_unsupported_types(fake_sensors_data))
 4063         expected_result = {"t1": {'f1': 'v1'}}
 4064         self.assertEqual(expected_result, actual_result)
 4065 
 4066     def test__filter_out_unsupported_types_non(self):
 4067         self._start_service()
 4068         CONF.set_override('send_sensor_data_types', ['t3'], group='conductor')
 4069         fake_sensors_data = {"t1": {'f1': 'v1'}, "t2": {'f1': 'v1'}}
 4070         actual_result = (
 4071             self.service._filter_out_unsupported_types(fake_sensors_data))
 4072         expected_result = {}
 4073         self.assertEqual(expected_result, actual_result)
 4074 
 4075     @mock.patch.object(messaging.Notifier, 'info', autospec=True)
 4076     @mock.patch.object(task_manager, 'acquire', autospec=True)
 4077     def test_send_sensor_task(self, acquire_mock, notifier_mock):
 4078         nodes = queue.Queue()
 4079         for i in range(5):
 4080             nodes.put_nowait(('fake_uuid-%d' % i, 'fake-hardware', '', None))
 4081         self._start_service()
 4082         CONF.set_override('send_sensor_data', True, group='conductor')
 4083 
 4084         task = acquire_mock.return_value.__enter__.return_value
 4085         task.node.maintenance = False
 4086         task.node.driver = 'fake'
 4087         task.node.name = 'fake_node'
 4088         get_sensors_data_mock = task.driver.management.get_sensors_data
 4089         validate_mock = task.driver.management.validate
 4090         get_sensors_data_mock.return_value = 'fake-sensor-data'
 4091         self.service._sensors_nodes_task(self.context, nodes)
 4092         self.assertEqual(5, acquire_mock.call_count)
 4093         self.assertEqual(5, validate_mock.call_count)
 4094         self.assertEqual(5, get_sensors_data_mock.call_count)
 4095         self.assertEqual(5, notifier_mock.call_count)
 4096         n_call = mock.call(mock.ANY, mock.ANY, 'hardware.fake.metrics',
 4097                            {'event_type': 'hardware.fake.metrics.update',
 4098                             'node_name': 'fake_node', 'timestamp': mock.ANY,
 4099                             'message_id': mock.ANY,
 4100                             'payload': 'fake-sensor-data',
 4101                             'node_uuid': mock.ANY, 'instance_uuid': None})
 4102         notifier_mock.assert_has_calls([n_call, n_call, n_call,
 4103                                         n_call, n_call])
 4104 
 4105     @mock.patch.object(task_manager, 'acquire', autospec=True)
 4106     def test_send_sensor_task_shutdown(self, acquire_mock):
 4107         nodes = queue.Queue()
 4108         nodes.put_nowait(('fake_uuid', 'fake-hardware', '', None))
 4109         self._start_service()
 4110         self.service._shutdown = True
 4111         CONF.set_override('send_sensor_data', True, group='conductor')
 4112         self.service._sensors_nodes_task(self.context, nodes)
 4113         acquire_mock.return_value.__enter__.assert_not_called()
 4114 
 4115     @mock.patch.object(task_manager, 'acquire', autospec=True)
 4116     def test_send_sensor_task_no_management(self, acquire_mock):
 4117         nodes = queue.Queue()
 4118         nodes.put_nowait(('fake_uuid', 'fake-hardware', '', None))
 4119 
 4120         CONF.set_override('send_sensor_data', True, group='conductor')
 4121 
 4122         self._start_service()
 4123 
 4124         task = acquire_mock.return_value.__enter__.return_value
 4125         task.node.maintenance = False
 4126         task.driver.management = None
 4127 
 4128         self.service._sensors_nodes_task(self.context, nodes)
 4129 
 4130         self.assertTrue(acquire_mock.called)
 4131 
 4132     @mock.patch.object(manager.LOG, 'debug', autospec=True)
 4133     @mock.patch.object(task_manager, 'acquire', autospec=True)
 4134     def test_send_sensor_task_maintenance(self, acquire_mock, debug_log):
 4135         nodes = queue.Queue()
 4136         nodes.put_nowait(('fake_uuid', 'fake-hardware', '', None))
 4137         self._start_service()
 4138         CONF.set_override('send_sensor_data', True, group='conductor')
 4139 
 4140         task = acquire_mock.return_value.__enter__.return_value
 4141         task.node.maintenance = True
 4142         get_sensors_data_mock = task.driver.management.get_sensors_data
 4143         validate_mock = task.driver.management.validate
 4144 
 4145         self.service._sensors_nodes_task(self.context, nodes)
 4146         self.assertTrue(acquire_mock.called)
 4147         self.assertFalse(validate_mock.called)
 4148         self.assertFalse(get_sensors_data_mock.called)
 4149         self.assertTrue(debug_log.called)
 4150 
 4151     @mock.patch.object(manager.ConductorManager, '_spawn_worker',
 4152                        autospec=True)
 4153     @mock.patch.object(manager.ConductorManager, '_mapped_to_this_conductor',
 4154                        autospec=True)
 4155     @mock.patch.object(dbapi.IMPL, 'get_nodeinfo_list', autospec=True)
 4156     def test___send_sensor_data(self, get_nodeinfo_list_mock,
 4157                                 _mapped_to_this_conductor_mock,
 4158                                 mock_spawn):
 4159         self._start_service()
 4160 
 4161         CONF.set_override('send_sensor_data', True, group='conductor')
 4162         # NOTE(galyna): do not wait for threads to be finished in unittests
 4163         CONF.set_override('send_sensor_data_wait_timeout', 0,
 4164                           group='conductor')
 4165         _mapped_to_this_conductor_mock.return_value = True
 4166         get_nodeinfo_list_mock.return_value = [('fake_uuid', 'fake', None)]
 4167         self.service._send_sensor_data(self.context)
 4168         mock_spawn.assert_called_with(self.service,
 4169                                       self.service._sensors_nodes_task,
 4170                                       self.context, mock.ANY)
 4171 
 4172     @mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker',
 4173                 autospec=True)
 4174     @mock.patch.object(manager.ConductorManager, '_mapped_to_this_conductor',
 4175                        autospec=True)
 4176     @mock.patch.object(dbapi.IMPL, 'get_nodeinfo_list', autospec=True)
 4177     def test___send_sensor_data_multiple_workers(
 4178             self, get_nodeinfo_list_mock, _mapped_to_this_conductor_mock,
 4179             mock_spawn):
 4180         self._start_service()
 4181         mock_spawn.reset_mock()
 4182 
 4183         number_of_workers = 8
 4184         CONF.set_override('send_sensor_data', True, group='conductor')
 4185         CONF.set_override('send_sensor_data_workers', number_of_workers,
 4186                           group='conductor')
 4187         # NOTE(galyna): do not wait for threads to be finished in unittests
 4188         CONF.set_override('send_sensor_data_wait_timeout', 0,
 4189                           group='conductor')
 4190 
 4191         _mapped_to_this_conductor_mock.return_value = True
 4192         get_nodeinfo_list_mock.return_value = [('fake_uuid', 'fake',
 4193                                                 None)] * 20
 4194         self.service._send_sensor_data(self.context)
 4195         self.assertEqual(number_of_workers,
 4196                          mock_spawn.call_count)
 4197 
 4198     # TODO(TheJulia): At some point, we should add a test to validate that
 4199     # a modified filter to return all nodes actually works, although
 4200     # the way the sensor tests are written, the list is all mocked.
 4201 
 4202 
 4203 @mgr_utils.mock_record_keepalive
 4204 class BootDeviceTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
 4205 
 4206     @mock.patch.object(fake.FakeManagement, 'set_boot_device', autospec=True)
 4207     @mock.patch.object(fake.FakeManagement, 'validate', autospec=True)
 4208     def test_set_boot_device(self, mock_val, mock_sbd):
 4209         node = obj_utils.create_test_node(self.context, driver='fake-hardware')
 4210         self.service.set_boot_device(self.context, node.uuid,
 4211                                      boot_devices.PXE)
 4212         mock_val.assert_called_once_with(mock.ANY, mock.ANY)
 4213         mock_sbd.assert_called_once_with(mock.ANY, mock.ANY, boot_devices.PXE,
 4214                                          persistent=False)
 4215 
 4216     def test_set_boot_device_node_locked(self):
 4217         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 4218                                           reservation='fake-reserv')
 4219         exc = self.assertRaises(messaging.rpc.ExpectedException,
 4220                                 self.service.set_boot_device,
 4221                                 self.context, node.uuid, boot_devices.DISK)
 4222         # Compare true exception hidden by @messaging.expected_exceptions
 4223         self.assertEqual(exception.NodeLocked, exc.exc_info[0])
 4224 
 4225     @mock.patch.object(fake.FakeManagement, 'validate', autospec=True)
 4226     def test_set_boot_device_validate_fail(self, mock_val):
 4227         node = obj_utils.create_test_node(self.context, driver='fake-hardware')
 4228         mock_val.side_effect = exception.InvalidParameterValue('error')
 4229         exc = self.assertRaises(messaging.rpc.ExpectedException,
 4230                                 self.service.set_boot_device,
 4231                                 self.context, node.uuid, boot_devices.DISK)
 4232         # Compare true exception hidden by @messaging.expected_exceptions
 4233         self.assertEqual(exception.InvalidParameterValue, exc.exc_info[0])
 4234 
 4235     def test_get_boot_device(self):
 4236         node = obj_utils.create_test_node(self.context, driver='fake-hardware')
 4237         bootdev = self.service.get_boot_device(self.context, node.uuid)
 4238         expected = {'boot_device': boot_devices.PXE, 'persistent': False}
 4239         self.assertEqual(expected, bootdev)
 4240 
 4241     def test_get_boot_device_node_locked(self):
 4242         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 4243                                           reservation='fake-reserv')
 4244         exc = self.assertRaises(messaging.rpc.ExpectedException,
 4245                                 self.service.get_boot_device,
 4246                                 self.context, node.uuid)
 4247         # Compare true exception hidden by @messaging.expected_exceptions
 4248         self.assertEqual(exception.NodeLocked, exc.exc_info[0])
 4249 
 4250     @mock.patch.object(fake.FakeManagement, 'validate', autospec=True)
 4251     def test_get_boot_device_validate_fail(self, mock_val):
 4252         node = obj_utils.create_test_node(self.context, driver='fake-hardware')
 4253         mock_val.side_effect = exception.InvalidParameterValue('error')
 4254         exc = self.assertRaises(messaging.rpc.ExpectedException,
 4255                                 self.service.get_boot_device,
 4256                                 self.context, node.uuid)
 4257         # Compare true exception hidden by @messaging.expected_exceptions
 4258         self.assertEqual(exception.InvalidParameterValue, exc.exc_info[0])
 4259 
 4260     def test_get_supported_boot_devices(self):
 4261         node = obj_utils.create_test_node(self.context, driver='fake-hardware')
 4262         bootdevs = self.service.get_supported_boot_devices(self.context,
 4263                                                            node.uuid)
 4264         self.assertEqual([boot_devices.PXE], bootdevs)
 4265 
 4266 
 4267 @mgr_utils.mock_record_keepalive
 4268 class IndicatorsTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
 4269 
 4270     @mock.patch.object(fake.FakeManagement, 'set_indicator_state',
 4271                        autospec=True)
 4272     @mock.patch.object(fake.FakeManagement, 'validate', autospec=True)
 4273     def test_set_indicator_state(self, mock_val, mock_sbd):
 4274         node = obj_utils.create_test_node(self.context, driver='fake-hardware')
 4275         self.service.set_indicator_state(
 4276             self.context, node.uuid, components.CHASSIS,
 4277             'led', indicator_states.ON)
 4278         mock_val.assert_called_once_with(mock.ANY, mock.ANY)
 4279         mock_sbd.assert_called_once_with(
 4280             mock.ANY, mock.ANY, components.CHASSIS, 'led', indicator_states.ON)
 4281 
 4282     def test_get_indicator_state(self):
 4283         node = obj_utils.create_test_node(self.context, driver='fake-hardware')
 4284         state = self.service.get_indicator_state(
 4285             self.context, node.uuid, components.CHASSIS, 'led-0')
 4286         expected = indicator_states.ON
 4287         self.assertEqual(expected, state)
 4288 
 4289     def test_get_supported_indicators(self):
 4290         node = obj_utils.create_test_node(self.context, driver='fake-hardware')
 4291         indicators = self.service.get_supported_indicators(
 4292             self.context, node.uuid)
 4293         expected = {
 4294             'chassis': {
 4295                 'led-0': {
 4296                     'readonly': True,
 4297                     'states': [
 4298                         indicator_states.OFF,
 4299                         indicator_states.ON
 4300                     ]
 4301                 }
 4302             },
 4303             'system': {
 4304                 'led': {
 4305                     'readonly': False,
 4306                     'states': [
 4307                         indicator_states.BLINKING,
 4308                         indicator_states.OFF,
 4309                         indicator_states.ON
 4310                     ]
 4311                 }
 4312             }
 4313         }
 4314         self.assertEqual(expected, indicators)
 4315 
 4316 
 4317 @mgr_utils.mock_record_keepalive
 4318 class NmiTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
 4319 
 4320     @mock.patch.object(fake.FakeManagement, 'inject_nmi', autospec=True)
 4321     @mock.patch.object(fake.FakeManagement, 'validate', autospec=True)
 4322     def test_inject_nmi(self, mock_val, mock_nmi):
 4323         node = obj_utils.create_test_node(self.context, driver='fake-hardware')
 4324         self.service.inject_nmi(self.context, node.uuid)
 4325         mock_val.assert_called_once_with(mock.ANY, mock.ANY)
 4326         mock_nmi.assert_called_once_with(mock.ANY, mock.ANY)
 4327 
 4328     def test_inject_nmi_node_locked(self):
 4329         node = obj_utils.create_test_node(self.context, driver='fake-hardware',
 4330                                           reservation='fake-reserv')
 4331         exc = self.assertRaises(messaging.rpc.ExpectedException,
 4332                                 self.service.inject_nmi,
 4333                                 self.context, node.uuid)
 4334         # Compare true exception hidden by @messaging.expected_exceptions
 4335         self.assertEqual(exception.NodeLocked, exc.exc_info[0])
 4336 
 4337     @mock.patch.object(fake.FakeManagement, 'validate',