"Fossies" - the Fresh Open Source Software Archive

Member "ironic-16.0.3/ironic/tests/unit/conductor/test_utils.py" (18 Jan 2021, 105533 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_utils.py": 16.0.2_vs_16.0.3.

    1 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
    2 #    not use this file except in compliance with the License. You may obtain
    3 #    a copy of the License at
    4 #
    5 #         http://www.apache.org/licenses/LICENSE-2.0
    6 #
    7 #    Unless required by applicable law or agreed to in writing, software
    8 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    9 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   10 #    License for the specific language governing permissions and limitations
   11 #    under the License.
   12 
   13 import os
   14 import tempfile
   15 import time
   16 from unittest import mock
   17 
   18 from oslo_config import cfg
   19 from oslo_utils import timeutils
   20 from oslo_utils import uuidutils
   21 
   22 from ironic.common import boot_devices
   23 from ironic.common import boot_modes
   24 from ironic.common import exception
   25 from ironic.common import network
   26 from ironic.common import neutron
   27 from ironic.common import nova
   28 from ironic.common import states
   29 from ironic.conductor import rpcapi
   30 from ironic.conductor import task_manager
   31 from ironic.conductor import utils as conductor_utils
   32 from ironic.drivers import base as drivers_base
   33 from ironic.drivers.modules import fake
   34 from ironic import objects
   35 from ironic.objects import fields as obj_fields
   36 from ironic.tests import base as tests_base
   37 from ironic.tests.unit.db import base as db_base
   38 from ironic.tests.unit.db import utils as db_utils
   39 from ironic.tests.unit.objects import utils as obj_utils
   40 
   41 CONF = cfg.CONF
   42 
   43 
   44 class TestPowerNoTimeout(drivers_base.PowerInterface):
   45     """Missing 'timeout' parameter for get_power_state & reboot"""
   46 
   47     def get_properties(self):
   48         return {}
   49 
   50     def validate(self, task):
   51         pass
   52 
   53     def get_power_state(self, task):
   54         return task.node.power_state
   55 
   56     def set_power_state(self, task, power_state, timeout=None):
   57         task.node.power_state = power_state
   58 
   59     def reboot(self, task):
   60         pass
   61 
   62 
   63 class NodeSetBootDeviceTestCase(db_base.DbTestCase):
   64 
   65     def setUp(self):
   66         super(NodeSetBootDeviceTestCase, self).setUp()
   67         self.node = obj_utils.create_test_node(self.context,
   68                                                uuid=uuidutils.generate_uuid())
   69         self.task = task_manager.TaskManager(self.context, self.node.uuid)
   70 
   71     def test_node_set_boot_device_non_existent_device(self):
   72         self.assertRaises(exception.InvalidParameterValue,
   73                           conductor_utils.node_set_boot_device,
   74                           self.task,
   75                           device='fake')
   76 
   77     @mock.patch.object(fake.FakeManagement, 'set_boot_device', autospec=True)
   78     def test_node_set_boot_device_valid(self, mock_sbd):
   79         conductor_utils.node_set_boot_device(self.task, device='pxe')
   80         mock_sbd.assert_called_once_with(mock.ANY, self.task,
   81                                          device='pxe', persistent=False)
   82 
   83     @mock.patch.object(fake.FakeManagement, 'set_boot_device', autospec=True)
   84     def test_node_set_boot_device_adopting(self, mock_sbd):
   85         self.task.node.provision_state = states.ADOPTING
   86         conductor_utils.node_set_boot_device(self.task, device='pxe')
   87         self.assertFalse(mock_sbd.called)
   88 
   89 
   90 class NodeGetBootModeTestCase(db_base.DbTestCase):
   91 
   92     def setUp(self):
   93         super(NodeGetBootModeTestCase, self).setUp()
   94         self.node = obj_utils.create_test_node(self.context,
   95                                                uuid=uuidutils.generate_uuid())
   96         self.task = task_manager.TaskManager(self.context, self.node.uuid)
   97 
   98     @mock.patch.object(fake.FakeManagement, 'get_boot_mode', autospec=True)
   99     def test_node_get_boot_mode_valid(self, mock_gbm):
  100         mock_gbm.return_value = 'bios'
  101         boot_mode = conductor_utils.node_get_boot_mode(self.task)
  102         self.assertEqual(boot_mode, 'bios')
  103         mock_gbm.assert_called_once_with(mock.ANY, self.task)
  104 
  105     @mock.patch.object(fake.FakeManagement, 'get_boot_mode', autospec=True)
  106     def test_node_get_boot_mode_unsupported(self, mock_gbm):
  107         mock_gbm.side_effect = exception.UnsupportedDriverExtension(
  108             driver=self.task.node.driver, extension='get_boot_mode')
  109         self.assertRaises(exception.UnsupportedDriverExtension,
  110                           conductor_utils.node_get_boot_mode, self.task)
  111 
  112 
  113 class NodeSetBootModeTestCase(db_base.DbTestCase):
  114 
  115     def setUp(self):
  116         super(NodeSetBootModeTestCase, self).setUp()
  117         self.node = obj_utils.create_test_node(self.context,
  118                                                uuid=uuidutils.generate_uuid())
  119         self.task = task_manager.TaskManager(self.context, self.node.uuid)
  120 
  121     @mock.patch.object(fake.FakeManagement, 'get_supported_boot_modes',
  122                        autospec=True)
  123     def test_node_set_boot_mode_non_existent_mode(self, mock_gsbm):
  124 
  125         mock_gsbm.return_value = [boot_modes.LEGACY_BIOS]
  126 
  127         self.assertRaises(exception.InvalidParameterValue,
  128                           conductor_utils.node_set_boot_mode,
  129                           self.task,
  130                           mode='non-existing')
  131 
  132     @mock.patch.object(fake.FakeManagement, 'set_boot_mode', autospec=True)
  133     @mock.patch.object(fake.FakeManagement, 'get_supported_boot_modes',
  134                        autospec=True)
  135     def test_node_set_boot_mode_valid(self, mock_gsbm, mock_sbm):
  136         mock_gsbm.return_value = [boot_modes.LEGACY_BIOS]
  137 
  138         conductor_utils.node_set_boot_mode(self.task,
  139                                            mode=boot_modes.LEGACY_BIOS)
  140         mock_sbm.assert_called_once_with(mock.ANY, self.task,
  141                                          mode=boot_modes.LEGACY_BIOS)
  142 
  143     @mock.patch.object(fake.FakeManagement, 'set_boot_mode', autospec=True)
  144     @mock.patch.object(fake.FakeManagement, 'get_supported_boot_modes',
  145                        autospec=True)
  146     def test_node_set_boot_mode_adopting(self, mock_gsbm, mock_sbm):
  147         mock_gsbm.return_value = [boot_modes.LEGACY_BIOS]
  148 
  149         old_provision_state = self.task.node.provision_state
  150         self.task.node.provision_state = states.ADOPTING
  151         try:
  152             conductor_utils.node_set_boot_mode(self.task,
  153                                                mode=boot_modes.LEGACY_BIOS)
  154 
  155         finally:
  156             self.task.node.provision_state = old_provision_state
  157 
  158         self.assertFalse(mock_sbm.called)
  159 
  160 
  161 class NodePowerActionTestCase(db_base.DbTestCase):
  162     @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
  163     def test_node_power_action_power_on(self, get_power_mock):
  164         """Test node_power_action to turn node power on."""
  165         node = obj_utils.create_test_node(self.context,
  166                                           uuid=uuidutils.generate_uuid(),
  167                                           driver='fake-hardware',
  168                                           power_state=states.POWER_OFF)
  169         task = task_manager.TaskManager(self.context, node.uuid)
  170 
  171         get_power_mock.return_value = states.POWER_OFF
  172 
  173         conductor_utils.node_power_action(task, states.POWER_ON)
  174 
  175         node.refresh()
  176         get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
  177         self.assertEqual(states.POWER_ON, node['power_state'])
  178         self.assertIsNone(node['target_power_state'])
  179         self.assertIsNone(node['last_error'])
  180 
  181     @mock.patch('ironic.objects.node.NodeSetPowerStateNotification',
  182                 autospec=True)
  183     @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
  184     @mock.patch.object(nova, 'power_update', autospec=True)
  185     def test_node_power_action_power_on_notify(self, mock_power_update,
  186                                                get_power_mock,
  187                                                mock_notif):
  188         """Test node_power_action to power on node and send notification."""
  189         self.config(notification_level='info')
  190         self.config(host='my-host')
  191         # Required for exception handling
  192         mock_notif.__name__ = 'NodeSetPowerStateNotification'
  193         node = obj_utils.create_test_node(self.context,
  194                                           uuid=uuidutils.generate_uuid(),
  195                                           driver='fake-hardware',
  196                                           instance_uuid=uuidutils.uuid,
  197                                           power_state=states.POWER_OFF)
  198         task = task_manager.TaskManager(self.context, node.uuid)
  199 
  200         get_power_mock.return_value = states.POWER_OFF
  201 
  202         conductor_utils.node_power_action(task, states.POWER_ON)
  203 
  204         node.refresh()
  205         get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
  206         self.assertEqual(states.POWER_ON, node.power_state)
  207         self.assertIsNone(node.target_power_state)
  208         self.assertIsNone(node.last_error)
  209 
  210         # 2 notifications should be sent: 1 .start and 1 .end
  211         self.assertEqual(2, mock_notif.call_count)
  212         self.assertEqual(2, mock_notif.return_value.emit.call_count)
  213 
  214         first_notif_args = mock_notif.call_args_list[0][1]
  215         second_notif_args = mock_notif.call_args_list[1][1]
  216 
  217         self.assertNotificationEqual(first_notif_args,
  218                                      'ironic-conductor', CONF.host,
  219                                      'baremetal.node.power_set.start',
  220                                      obj_fields.NotificationLevel.INFO)
  221         self.assertNotificationEqual(second_notif_args,
  222                                      'ironic-conductor', CONF.host,
  223                                      'baremetal.node.power_set.end',
  224                                      obj_fields.NotificationLevel.INFO)
  225         mock_power_update.assert_called_once_with(
  226             task.context, node.instance_uuid, states.POWER_ON)
  227 
  228     @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
  229     def test_node_power_action_power_off(self, get_power_mock):
  230         """Test node_power_action to turn node power off."""
  231         dii = {'agent_secret_token': 'token',
  232                'agent_cached_deploy_steps': ['steps']}
  233         node = obj_utils.create_test_node(self.context,
  234                                           uuid=uuidutils.generate_uuid(),
  235                                           driver='fake-hardware',
  236                                           power_state=states.POWER_ON,
  237                                           driver_internal_info=dii)
  238         task = task_manager.TaskManager(self.context, node.uuid)
  239 
  240         get_power_mock.return_value = states.POWER_ON
  241 
  242         conductor_utils.node_power_action(task, states.POWER_OFF)
  243 
  244         node.refresh()
  245         get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
  246         self.assertEqual(states.POWER_OFF, node['power_state'])
  247         self.assertIsNone(node['target_power_state'])
  248         self.assertIsNone(node['last_error'])
  249         self.assertNotIn('agent_secret_token', node['driver_internal_info'])
  250         self.assertNotIn('agent_cached_deploy_steps',
  251                          node['driver_internal_info'])
  252 
  253     @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
  254     def test_node_power_action_power_off_pregenerated_token(self,
  255                                                             get_power_mock):
  256         dii = {'agent_secret_token': 'token',
  257                'agent_secret_token_pregenerated': True}
  258         node = obj_utils.create_test_node(self.context,
  259                                           uuid=uuidutils.generate_uuid(),
  260                                           driver='fake-hardware',
  261                                           power_state=states.POWER_ON,
  262                                           driver_internal_info=dii)
  263         task = task_manager.TaskManager(self.context, node.uuid)
  264 
  265         get_power_mock.return_value = states.POWER_ON
  266 
  267         conductor_utils.node_power_action(task, states.POWER_OFF)
  268 
  269         node.refresh()
  270         get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
  271         self.assertEqual(states.POWER_OFF, node['power_state'])
  272         self.assertIsNone(node['target_power_state'])
  273         self.assertIsNone(node['last_error'])
  274         self.assertEqual('token',
  275                          node['driver_internal_info']['agent_secret_token'])
  276 
  277     @mock.patch.object(fake.FakePower, 'reboot', autospec=True)
  278     @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
  279     def test_node_power_action_power_reboot(self, get_power_mock, reboot_mock):
  280         """Test for reboot a node."""
  281         dii = {'agent_secret_token': 'token',
  282                'agent_cached_deploy_steps': ['steps']}
  283         node = obj_utils.create_test_node(self.context,
  284                                           uuid=uuidutils.generate_uuid(),
  285                                           driver='fake-hardware',
  286                                           power_state=states.POWER_ON,
  287                                           driver_internal_info=dii)
  288         task = task_manager.TaskManager(self.context, node.uuid)
  289 
  290         conductor_utils.node_power_action(task, states.REBOOT)
  291         self.assertFalse(get_power_mock.called)
  292 
  293         node.refresh()
  294         reboot_mock.assert_called_once_with(mock.ANY, mock.ANY, timeout=None)
  295         self.assertEqual(states.POWER_ON, node['power_state'])
  296         self.assertIsNone(node['target_power_state'])
  297         self.assertIsNone(node['last_error'])
  298         self.assertNotIn('agent_secret_token', node['driver_internal_info'])
  299 
  300     @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
  301     def test_node_power_action_invalid_state(self, get_power_mock):
  302         """Test for exception when changing to an invalid power state."""
  303         node = obj_utils.create_test_node(self.context,
  304                                           uuid=uuidutils.generate_uuid(),
  305                                           driver='fake-hardware',
  306                                           power_state=states.POWER_ON)
  307         task = task_manager.TaskManager(self.context, node.uuid)
  308 
  309         get_power_mock.return_value = states.POWER_ON
  310 
  311         self.assertRaises(exception.InvalidParameterValue,
  312                           conductor_utils.node_power_action,
  313                           task,
  314                           "INVALID_POWER_STATE")
  315 
  316         node.refresh()
  317         self.assertFalse(get_power_mock.called)
  318         self.assertEqual(states.POWER_ON, node['power_state'])
  319         self.assertIsNone(node['target_power_state'])
  320         self.assertIsNotNone(node['last_error'])
  321 
  322         # last_error is cleared when a new transaction happens
  323         conductor_utils.node_power_action(task, states.POWER_OFF)
  324         node.refresh()
  325         self.assertEqual(states.POWER_OFF, node['power_state'])
  326         self.assertIsNone(node['target_power_state'])
  327         self.assertIsNone(node['last_error'])
  328 
  329     @mock.patch('ironic.objects.node.NodeSetPowerStateNotification',
  330                 autospec=True)
  331     @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
  332     def test_node_power_action_invalid_state_notify(self, get_power_mock,
  333                                                     mock_notif):
  334         """Test for notification when changing to an invalid power state."""
  335         self.config(notification_level='info')
  336         self.config(host='my-host')
  337         # Required for exception handling
  338         mock_notif.__name__ = 'NodeSetPowerStateNotification'
  339         node = obj_utils.create_test_node(self.context,
  340                                           uuid=uuidutils.generate_uuid(),
  341                                           driver='fake-hardware',
  342                                           power_state=states.POWER_ON)
  343         task = task_manager.TaskManager(self.context, node.uuid)
  344 
  345         get_power_mock.return_value = states.POWER_ON
  346 
  347         self.assertRaises(exception.InvalidParameterValue,
  348                           conductor_utils.node_power_action,
  349                           task,
  350                           "INVALID_POWER_STATE")
  351 
  352         node.refresh()
  353         self.assertFalse(get_power_mock.called)
  354         self.assertEqual(states.POWER_ON, node.power_state)
  355         self.assertIsNone(node.target_power_state)
  356         self.assertIsNotNone(node.last_error)
  357 
  358         # 2 notifications should be sent: 1 .start and 1 .error
  359         self.assertEqual(2, mock_notif.call_count)
  360         self.assertEqual(2, mock_notif.return_value.emit.call_count)
  361 
  362         first_notif_args = mock_notif.call_args_list[0][1]
  363         second_notif_args = mock_notif.call_args_list[1][1]
  364 
  365         self.assertNotificationEqual(first_notif_args,
  366                                      'ironic-conductor', CONF.host,
  367                                      'baremetal.node.power_set.start',
  368                                      obj_fields.NotificationLevel.INFO)
  369         self.assertNotificationEqual(second_notif_args,
  370                                      'ironic-conductor', CONF.host,
  371                                      'baremetal.node.power_set.error',
  372                                      obj_fields.NotificationLevel.ERROR)
  373 
  374     def test_node_power_action_already_being_processed(self):
  375         """Test node power action after aborted power action.
  376 
  377         The target_power_state is expected to be None so it isn't
  378         checked in the code. This is what happens if it is not None.
  379         (Eg, if a conductor had died during a previous power-off
  380         attempt and left the target_power_state set to states.POWER_OFF,
  381         and the user is attempting to power-off again.)
  382         """
  383         node = obj_utils.create_test_node(self.context,
  384                                           uuid=uuidutils.generate_uuid(),
  385                                           driver='fake-hardware',
  386                                           power_state=states.POWER_ON,
  387                                           target_power_state=states.POWER_OFF)
  388         task = task_manager.TaskManager(self.context, node.uuid)
  389 
  390         conductor_utils.node_power_action(task, states.POWER_OFF)
  391 
  392         node.refresh()
  393         self.assertEqual(states.POWER_OFF, node['power_state'])
  394         self.assertEqual(states.NOSTATE, node['target_power_state'])
  395         self.assertIsNone(node['last_error'])
  396         self.assertNotIn('agent_secret_token', node['driver_internal_info'])
  397 
  398     @mock.patch.object(conductor_utils, 'LOG', autospec=True)
  399     @mock.patch.object(fake.FakePower, 'set_power_state', autospec=True)
  400     @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
  401     def test_node_power_action_in_same_state(self, get_power_mock,
  402                                              set_power_mock, log_mock):
  403         """Test setting node state to its present state.
  404 
  405         Test that we don't try to set the power state if the requested
  406         state is the same as the current state.
  407         """
  408         node = obj_utils.create_test_node(self.context,
  409                                           uuid=uuidutils.generate_uuid(),
  410                                           driver='fake-hardware',
  411                                           last_error='anything but None',
  412                                           power_state=states.POWER_ON)
  413         task = task_manager.TaskManager(self.context, node.uuid)
  414 
  415         get_power_mock.return_value = states.POWER_ON
  416 
  417         conductor_utils.node_power_action(task, states.POWER_ON)
  418 
  419         node.refresh()
  420         get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
  421         self.assertFalse(set_power_mock.called,
  422                          "set_power_state unexpectedly called")
  423         self.assertEqual(states.POWER_ON, node['power_state'])
  424         self.assertIsNone(node['target_power_state'])
  425         self.assertIsNone(node['last_error'])
  426         log_mock.warning.assert_called_once_with(
  427             u"Not going to change node %(node)s power state because "
  428             u"current state = requested state = '%(state)s'.",
  429             {'state': states.POWER_ON, 'node': node.uuid})
  430 
  431     @mock.patch.object(fake.FakePower, 'set_power_state', autospec=True)
  432     @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
  433     def test_node_power_action_in_same_state_db_not_in_sync(self,
  434                                                             get_power_mock,
  435                                                             set_power_mock):
  436         """Test setting node state to its present state if DB is out of sync.
  437 
  438         Under rare conditions (see bug #1403106) database might contain stale
  439         information, make sure we fix it.
  440         """
  441         node = obj_utils.create_test_node(self.context,
  442                                           uuid=uuidutils.generate_uuid(),
  443                                           driver='fake-hardware',
  444                                           last_error='anything but None',
  445                                           power_state=states.POWER_ON)
  446         task = task_manager.TaskManager(self.context, node.uuid)
  447 
  448         get_power_mock.return_value = states.POWER_OFF
  449 
  450         conductor_utils.node_power_action(task, states.POWER_OFF)
  451 
  452         node.refresh()
  453         get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
  454         self.assertFalse(set_power_mock.called,
  455                          "set_power_state unexpectedly called")
  456         self.assertEqual(states.POWER_OFF, node['power_state'])
  457         self.assertIsNone(node['target_power_state'])
  458         self.assertIsNone(node['last_error'])
  459 
  460     @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
  461     def test_node_power_action_failed_getting_state(self, get_power_mock):
  462         """Test for exception when we can't get the current power state."""
  463         node = obj_utils.create_test_node(self.context,
  464                                           uuid=uuidutils.generate_uuid(),
  465                                           driver='fake-hardware',
  466                                           power_state=states.POWER_ON)
  467         task = task_manager.TaskManager(self.context, node.uuid)
  468 
  469         get_power_mock.side_effect = (
  470             exception.InvalidParameterValue('failed getting power state'))
  471 
  472         self.assertRaises(exception.InvalidParameterValue,
  473                           conductor_utils.node_power_action,
  474                           task,
  475                           states.POWER_ON)
  476 
  477         node.refresh()
  478         get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
  479         self.assertEqual(states.POWER_ON, node['power_state'])
  480         self.assertIsNone(node['target_power_state'])
  481         self.assertIsNotNone(node['last_error'])
  482 
  483     @mock.patch('ironic.objects.node.NodeSetPowerStateNotification',
  484                 autospec=True)
  485     @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
  486     def test_node_power_action_failed_getting_state_notify(self,
  487                                                            get_power_mock,
  488                                                            mock_notif):
  489         """Test for notification when we can't get the current power state."""
  490         self.config(notification_level='info')
  491         self.config(host='my-host')
  492         # Required for exception handling
  493         mock_notif.__name__ = 'NodeSetPowerStateNotification'
  494         node = obj_utils.create_test_node(self.context,
  495                                           uuid=uuidutils.generate_uuid(),
  496                                           driver='fake-hardware',
  497                                           power_state=states.POWER_ON)
  498         task = task_manager.TaskManager(self.context, node.uuid)
  499 
  500         get_power_mock.side_effect = (
  501             exception.InvalidParameterValue('failed getting power state'))
  502 
  503         self.assertRaises(exception.InvalidParameterValue,
  504                           conductor_utils.node_power_action,
  505                           task,
  506                           states.POWER_ON)
  507 
  508         node.refresh()
  509         get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
  510         self.assertEqual(states.POWER_ON, node.power_state)
  511         self.assertIsNone(node.target_power_state)
  512         self.assertIsNotNone(node.last_error)
  513 
  514         # 2 notifications should be sent: 1 .start and 1 .error
  515         self.assertEqual(2, mock_notif.call_count)
  516         self.assertEqual(2, mock_notif.return_value.emit.call_count)
  517 
  518         first_notif_args = mock_notif.call_args_list[0][1]
  519         second_notif_args = mock_notif.call_args_list[1][1]
  520 
  521         self.assertNotificationEqual(first_notif_args,
  522                                      'ironic-conductor', CONF.host,
  523                                      'baremetal.node.power_set.start',
  524                                      obj_fields.NotificationLevel.INFO)
  525         self.assertNotificationEqual(second_notif_args,
  526                                      'ironic-conductor', CONF.host,
  527                                      'baremetal.node.power_set.error',
  528                                      obj_fields.NotificationLevel.ERROR)
  529 
  530     @mock.patch.object(fake.FakePower, 'set_power_state', autospec=True)
  531     @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
  532     def test_node_power_action_set_power_failure(self, get_power_mock,
  533                                                  set_power_mock):
  534         """Test if an exception is thrown when the set_power call fails."""
  535         node = obj_utils.create_test_node(self.context,
  536                                           uuid=uuidutils.generate_uuid(),
  537                                           driver='fake-hardware',
  538                                           power_state=states.POWER_OFF)
  539         task = task_manager.TaskManager(self.context, node.uuid)
  540 
  541         get_power_mock.return_value = states.POWER_OFF
  542         set_power_mock.side_effect = exception.IronicException()
  543 
  544         self.assertRaises(
  545             exception.IronicException,
  546             conductor_utils.node_power_action,
  547             task,
  548             states.POWER_ON)
  549 
  550         node.refresh()
  551         get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
  552         set_power_mock.assert_called_once_with(
  553             mock.ANY, mock.ANY, states.POWER_ON, timeout=None)
  554         self.assertEqual(states.POWER_OFF, node['power_state'])
  555         self.assertIsNone(node['target_power_state'])
  556         self.assertIsNotNone(node['last_error'])
  557 
  558     @mock.patch('ironic.objects.node.NodeSetPowerStateNotification',
  559                 autospec=True)
  560     @mock.patch.object(fake.FakePower, 'set_power_state', autospec=True)
  561     @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
  562     def test_node_power_action_set_power_failure_notify(self, get_power_mock,
  563                                                         set_power_mock,
  564                                                         mock_notif):
  565         """Test if a notification is sent when the set_power call fails."""
  566         self.config(notification_level='info')
  567         self.config(host='my-host')
  568         # Required for exception handling
  569         mock_notif.__name__ = 'NodeSetPowerStateNotification'
  570         node = obj_utils.create_test_node(self.context,
  571                                           uuid=uuidutils.generate_uuid(),
  572                                           driver='fake-hardware',
  573                                           power_state=states.POWER_OFF)
  574         task = task_manager.TaskManager(self.context, node.uuid)
  575 
  576         get_power_mock.return_value = states.POWER_OFF
  577         set_power_mock.side_effect = exception.IronicException()
  578 
  579         self.assertRaises(
  580             exception.IronicException,
  581             conductor_utils.node_power_action,
  582             task,
  583             states.POWER_ON)
  584 
  585         node.refresh()
  586         get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
  587         set_power_mock.assert_called_once_with(
  588             mock.ANY, mock.ANY, states.POWER_ON, timeout=None)
  589         self.assertEqual(states.POWER_OFF, node.power_state)
  590         self.assertIsNone(node.target_power_state)
  591         self.assertIsNotNone(node.last_error)
  592 
  593         # 2 notifications should be sent: 1 .start and 1 .error
  594         self.assertEqual(2, mock_notif.call_count)
  595         self.assertEqual(2, mock_notif.return_value.emit.call_count)
  596 
  597         first_notif_args = mock_notif.call_args_list[0][1]
  598         second_notif_args = mock_notif.call_args_list[1][1]
  599 
  600         self.assertNotificationEqual(first_notif_args,
  601                                      'ironic-conductor', CONF.host,
  602                                      'baremetal.node.power_set.start',
  603                                      obj_fields.NotificationLevel.INFO)
  604         self.assertNotificationEqual(
  605             second_notif_args, 'ironic-conductor', CONF.host,
  606             'baremetal.node.power_set.error',
  607             obj_fields.NotificationLevel.ERROR)
  608 
  609     @mock.patch.object(fake.FakeStorage, 'attach_volumes', autospec=True)
  610     def test_node_power_action_power_on_storage_attach(self, attach_mock):
  611         """Test node_power_action to turn node power on and attach storage."""
  612         node = obj_utils.create_test_node(self.context,
  613                                           uuid=uuidutils.generate_uuid(),
  614                                           driver='fake-hardware',
  615                                           power_state=states.POWER_OFF,
  616                                           storage_interface="fake",
  617                                           provision_state=states.ACTIVE)
  618         task = task_manager.TaskManager(self.context, node.uuid)
  619 
  620         conductor_utils.node_power_action(task, states.POWER_ON)
  621 
  622         node.refresh()
  623         attach_mock.assert_called_once_with(mock.ANY, task)
  624         self.assertEqual(states.POWER_ON, node['power_state'])
  625         self.assertIsNone(node['target_power_state'])
  626         self.assertIsNone(node['last_error'])
  627 
  628     @mock.patch.object(fake.FakeStorage, 'attach_volumes', autospec=True)
  629     def test_node_power_action_reboot_storage_attach(self, attach_mock):
  630         """Test node_power_action to reboot the node and attach storage."""
  631         node = obj_utils.create_test_node(self.context,
  632                                           uuid=uuidutils.generate_uuid(),
  633                                           driver='fake-hardware',
  634                                           power_state=states.POWER_ON,
  635                                           storage_interface="fake",
  636                                           provision_state=states.ACTIVE)
  637         task = task_manager.TaskManager(self.context, node.uuid)
  638 
  639         conductor_utils.node_power_action(task, states.REBOOT)
  640 
  641         node.refresh()
  642         attach_mock.assert_called_once_with(mock.ANY, task)
  643         self.assertEqual(states.POWER_ON, node['power_state'])
  644         self.assertIsNone(node['target_power_state'])
  645         self.assertIsNone(node['last_error'])
  646 
  647     @mock.patch.object(fake.FakeStorage, 'detach_volumes', autospec=True)
  648     def test_node_power_action_power_off_storage_detach(self, detach_mock):
  649         """Test node_power_action to turn node power off and detach storage."""
  650         node = obj_utils.create_test_node(self.context,
  651                                           uuid=uuidutils.generate_uuid(),
  652                                           driver='fake-hardware',
  653                                           power_state=states.POWER_ON,
  654                                           storage_interface="fake",
  655                                           provision_state=states.ACTIVE)
  656         task = task_manager.TaskManager(self.context, node.uuid)
  657 
  658         conductor_utils.node_power_action(task, states.POWER_OFF)
  659 
  660         node.refresh()
  661         detach_mock.assert_called_once_with(mock.ANY, task)
  662         self.assertEqual(states.POWER_OFF, node['power_state'])
  663         self.assertIsNone(node['target_power_state'])
  664         self.assertIsNone(node['last_error'])
  665 
  666     def test__calculate_target_state(self):
  667         for new_state in (states.POWER_ON, states.REBOOT, states.SOFT_REBOOT):
  668             self.assertEqual(
  669                 states.POWER_ON,
  670                 conductor_utils._calculate_target_state(new_state))
  671         for new_state in (states.POWER_OFF, states.SOFT_POWER_OFF):
  672             self.assertEqual(
  673                 states.POWER_OFF,
  674                 conductor_utils._calculate_target_state(new_state))
  675         self.assertIsNone(conductor_utils._calculate_target_state('bad_state'))
  676 
  677     @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
  678     def test__can_skip_state_change_different_state(self, get_power_mock):
  679         """Test setting node state to different state.
  680 
  681         Test that we should change state if requested state is different from
  682         current state.
  683         """
  684         node = obj_utils.create_test_node(self.context,
  685                                           uuid=uuidutils.generate_uuid(),
  686                                           driver='fake-hardware',
  687                                           last_error='anything but None',
  688                                           power_state=states.POWER_ON)
  689         task = task_manager.TaskManager(self.context, node.uuid)
  690 
  691         get_power_mock.return_value = states.POWER_ON
  692 
  693         result = conductor_utils._can_skip_state_change(
  694             task, states.POWER_OFF)
  695 
  696         self.assertFalse(result)
  697         get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
  698 
  699     @mock.patch.object(conductor_utils, 'LOG', autospec=True)
  700     @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
  701     def test__can_skip_state_change_same_state(self, get_power_mock, mock_log):
  702         """Test setting node state to its present state.
  703 
  704         Test that we don't try to set the power state if the requested
  705         state is the same as the current state.
  706         """
  707         node = obj_utils.create_test_node(self.context,
  708                                           uuid=uuidutils.generate_uuid(),
  709                                           driver='fake-hardware',
  710                                           last_error='anything but None',
  711                                           power_state=states.POWER_ON)
  712         task = task_manager.TaskManager(self.context, node.uuid)
  713 
  714         get_power_mock.return_value = states.POWER_ON
  715 
  716         result = conductor_utils._can_skip_state_change(
  717             task, states.POWER_ON)
  718 
  719         self.assertTrue(result)
  720         node.refresh()
  721         get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
  722         self.assertEqual(states.POWER_ON, node['power_state'])
  723         self.assertEqual(states.NOSTATE, node['target_power_state'])
  724         self.assertIsNone(node['last_error'])
  725         mock_log.warning.assert_called_once_with(
  726             u"Not going to change node %(node)s power state because "
  727             u"current state = requested state = '%(state)s'.",
  728             {'state': states.POWER_ON, 'node': node.uuid})
  729 
  730     @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
  731     def test__can_skip_state_change_db_not_in_sync(self, get_power_mock):
  732         """Test setting node state to its present state if DB is out of sync.
  733 
  734         Under rare conditions (see bug #1403106) database might contain stale
  735         information, make sure we fix it.
  736         """
  737         node = obj_utils.create_test_node(self.context,
  738                                           uuid=uuidutils.generate_uuid(),
  739                                           driver='fake-hardware',
  740                                           last_error='anything but None',
  741                                           power_state=states.POWER_ON)
  742         task = task_manager.TaskManager(self.context, node.uuid)
  743 
  744         get_power_mock.return_value = states.POWER_OFF
  745 
  746         result = conductor_utils._can_skip_state_change(task, states.POWER_OFF)
  747 
  748         self.assertTrue(result)
  749 
  750         node.refresh()
  751         get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
  752         self.assertEqual(states.POWER_OFF, node['power_state'])
  753         self.assertEqual(states.NOSTATE, node['target_power_state'])
  754         self.assertIsNone(node['last_error'])
  755 
  756     @mock.patch('ironic.objects.node.NodeSetPowerStateNotification',
  757                 autospec=True)
  758     @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
  759     def test__can_skip_state_change_failed_getting_state_notify(
  760             self, get_power_mock, mock_notif):
  761         """Test for notification & exception when can't get power state.
  762 
  763         Test to make sure we generate a notification and also that an exception
  764         is raised when we can't get the current power state.
  765         """
  766         self.config(notification_level='info')
  767         self.config(host='my-host')
  768         # Required for exception handling
  769         mock_notif.__name__ = 'NodeSetPowerStateNotification'
  770         node = obj_utils.create_test_node(self.context,
  771                                           uuid=uuidutils.generate_uuid(),
  772                                           driver='fake-hardware',
  773                                           power_state=states.POWER_ON)
  774         task = task_manager.TaskManager(self.context, node.uuid)
  775 
  776         get_power_mock.side_effect = (
  777             exception.InvalidParameterValue('failed getting power state'))
  778 
  779         self.assertRaises(exception.InvalidParameterValue,
  780                           conductor_utils._can_skip_state_change,
  781                           task,
  782                           states.POWER_ON)
  783 
  784         node.refresh()
  785         get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
  786         self.assertEqual(states.POWER_ON, node.power_state)
  787         self.assertEqual(states.NOSTATE, node['target_power_state'])
  788         self.assertIsNotNone(node.last_error)
  789 
  790         # 1 notification should be sent for the error
  791         self.assertEqual(1, mock_notif.call_count)
  792         self.assertEqual(1, mock_notif.return_value.emit.call_count)
  793 
  794         notif_args = mock_notif.call_args_list[0][1]
  795 
  796         self.assertNotificationEqual(notif_args,
  797                                      'ironic-conductor', CONF.host,
  798                                      'baremetal.node.power_set.error',
  799                                      obj_fields.NotificationLevel.ERROR)
  800 
  801     def test_node_power_action_reboot_no_timeout(self):
  802         """Test node reboot using Power Interface with no timeout arg."""
  803         node = obj_utils.create_test_node(self.context,
  804                                           uuid=uuidutils.generate_uuid(),
  805                                           driver='fake-hardware',
  806                                           console_interface='no-console',
  807                                           inspect_interface='no-inspect',
  808                                           raid_interface='no-raid',
  809                                           rescue_interface='no-rescue',
  810                                           vendor_interface='no-vendor',
  811                                           bios_interface='no-bios',
  812                                           power_state=states.POWER_ON)
  813         self.config(enabled_boot_interfaces=['fake'])
  814         self.config(enabled_deploy_interfaces=['fake'])
  815         self.config(enabled_management_interfaces=['fake'])
  816         self.config(enabled_power_interfaces=['fake'])
  817 
  818         task = task_manager.TaskManager(self.context, node.uuid)
  819         task.driver.power = TestPowerNoTimeout()
  820         self.assertRaisesRegex(TypeError,
  821                                'unexpected keyword argument',
  822                                conductor_utils.node_power_action,
  823                                task, states.REBOOT)
  824         node.refresh()
  825         self.assertEqual(states.POWER_ON, node['power_state'])
  826         self.assertIsNone(node['target_power_state'])
  827         self.assertTrue('unexpected keyword argument' in node['last_error'])
  828 
  829 
  830 class NodeSoftPowerActionTestCase(db_base.DbTestCase):
  831 
  832     @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
  833     def test_node_power_action_power_soft_reboot(self, get_power_mock):
  834         """Test for soft reboot a node."""
  835         node = obj_utils.create_test_node(self.context,
  836                                           uuid=uuidutils.generate_uuid(),
  837                                           driver='fake-hardware',
  838                                           power_state=states.POWER_ON)
  839         task = task_manager.TaskManager(self.context, node.uuid)
  840 
  841         get_power_mock.return_value = states.POWER_ON
  842 
  843         conductor_utils.node_power_action(task, states.SOFT_REBOOT)
  844 
  845         node.refresh()
  846         self.assertFalse(get_power_mock.called)
  847         self.assertEqual(states.POWER_ON, node['power_state'])
  848         self.assertIsNone(node['target_power_state'])
  849         self.assertIsNone(node['last_error'])
  850 
  851     @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
  852     def test_node_power_action_power_soft_reboot_timeout(self, get_power_mock):
  853         """Test for soft reboot a node."""
  854         node = obj_utils.create_test_node(self.context,
  855                                           uuid=uuidutils.generate_uuid(),
  856                                           driver='fake-hardware',
  857                                           power_state=states.POWER_ON)
  858         task = task_manager.TaskManager(self.context, node.uuid)
  859 
  860         get_power_mock.return_value = states.POWER_ON
  861 
  862         conductor_utils.node_power_action(task, states.SOFT_REBOOT,
  863                                           timeout=2)
  864 
  865         node.refresh()
  866         self.assertFalse(get_power_mock.called)
  867         self.assertEqual(states.POWER_ON, node['power_state'])
  868         self.assertIsNone(node['target_power_state'])
  869         self.assertIsNone(node['last_error'])
  870 
  871     @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
  872     def test_node_power_action_soft_power_off(self, get_power_mock):
  873         """Test node_power_action to turn node soft power off."""
  874         node = obj_utils.create_test_node(self.context,
  875                                           uuid=uuidutils.generate_uuid(),
  876                                           driver='fake-hardware',
  877                                           power_state=states.POWER_ON)
  878         task = task_manager.TaskManager(self.context, node.uuid)
  879 
  880         get_power_mock.return_value = states.POWER_ON
  881 
  882         conductor_utils.node_power_action(task, states.SOFT_POWER_OFF)
  883 
  884         node.refresh()
  885         get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
  886         self.assertEqual(states.POWER_OFF, node['power_state'])
  887         self.assertIsNone(node['target_power_state'])
  888         self.assertIsNone(node['last_error'])
  889 
  890     @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
  891     def test_node_power_action_soft_power_off_timeout(self, get_power_mock):
  892         """Test node_power_action to turn node soft power off."""
  893         node = obj_utils.create_test_node(self.context,
  894                                           uuid=uuidutils.generate_uuid(),
  895                                           driver='fake-hardware',
  896                                           power_state=states.POWER_ON)
  897         task = task_manager.TaskManager(self.context, node.uuid)
  898 
  899         get_power_mock.return_value = states.POWER_ON
  900 
  901         conductor_utils.node_power_action(task, states.SOFT_POWER_OFF,
  902                                           timeout=2)
  903 
  904         node.refresh()
  905         get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
  906         self.assertEqual(states.POWER_OFF, node['power_state'])
  907         self.assertIsNone(node['target_power_state'])
  908         self.assertIsNone(node['last_error'])
  909 
  910     @mock.patch.object(fake.FakeStorage, 'detach_volumes', autospec=True)
  911     def test_node_power_action_soft_power_off_storage_detach(self,
  912                                                              detach_mock):
  913         """Test node_power_action to soft power off node and detach storage."""
  914         node = obj_utils.create_test_node(self.context,
  915                                           uuid=uuidutils.generate_uuid(),
  916                                           driver='fake-hardware',
  917                                           power_state=states.POWER_ON,
  918                                           storage_interface="fake",
  919                                           provision_state=states.ACTIVE)
  920         task = task_manager.TaskManager(self.context, node.uuid)
  921 
  922         conductor_utils.node_power_action(task, states.SOFT_POWER_OFF)
  923 
  924         node.refresh()
  925         detach_mock.assert_called_once_with(mock.ANY, task)
  926         self.assertEqual(states.POWER_OFF, node['power_state'])
  927         self.assertIsNone(node['target_power_state'])
  928         self.assertIsNone(node['last_error'])
  929 
  930 
  931 class DeployingErrorHandlerTestCase(tests_base.TestCase):
  932     def setUp(self):
  933         super(DeployingErrorHandlerTestCase, self).setUp()
  934         self.task = mock.Mock(spec=task_manager.TaskManager)
  935         self.task.context = self.context
  936         self.task.driver = mock.Mock(spec_set=['deploy'])
  937         self.task.shared = False
  938         self.task.node = mock.Mock(spec_set=objects.Node)
  939         self.node = self.task.node
  940         self.node.provision_state = states.DEPLOYING
  941         self.node.last_error = None
  942         self.node.deploy_step = None
  943         self.node.driver_internal_info = {}
  944         self.logmsg = "log message"
  945         self.errmsg = "err message"
  946 
  947     @mock.patch.object(conductor_utils, 'deploying_error_handler',
  948                        autospec=True)
  949     def test_cleanup_after_timeout(self, mock_handler):
  950         conductor_utils.cleanup_after_timeout(self.task)
  951         mock_handler.assert_called_once_with(self.task, mock.ANY, mock.ANY)
  952 
  953     def test_cleanup_after_timeout_shared_lock(self):
  954         self.task.shared = True
  955 
  956         self.assertRaises(exception.ExclusiveLockRequired,
  957                           conductor_utils.cleanup_after_timeout,
  958                           self.task)
  959 
  960     def test_deploying_error_handler(self):
  961         info = self.node.driver_internal_info
  962         info['deploy_step_index'] = 2
  963         info['deployment_reboot'] = True
  964         info['deployment_polling'] = True
  965         info['skip_current_deploy_step'] = True
  966         info['agent_url'] = 'url'
  967         conductor_utils.deploying_error_handler(self.task, self.logmsg,
  968                                                 self.errmsg)
  969 
  970         self.assertEqual([mock.call()] * 2, self.node.save.call_args_list)
  971         self.task.driver.deploy.clean_up.assert_called_once_with(self.task)
  972         self.assertEqual(self.errmsg, self.node.last_error)
  973         self.assertEqual({}, self.node.deploy_step)
  974         self.assertNotIn('deploy_step_index', self.node.driver_internal_info)
  975         self.assertNotIn('deployment_reboot', self.node.driver_internal_info)
  976         self.assertNotIn('deployment_polling', self.node.driver_internal_info)
  977         self.assertNotIn('skip_current_deploy_step',
  978                          self.node.driver_internal_info)
  979         self.assertNotIn('agent_url', self.node.driver_internal_info)
  980         self.task.process_event.assert_called_once_with('fail')
  981 
  982     def _test_deploying_error_handler_cleanup(self, exc, expected_str):
  983         clean_up_mock = self.task.driver.deploy.clean_up
  984         clean_up_mock.side_effect = exc
  985 
  986         conductor_utils.deploying_error_handler(self.task, self.logmsg,
  987                                                 self.errmsg)
  988 
  989         self.task.driver.deploy.clean_up.assert_called_once_with(self.task)
  990         self.assertEqual([mock.call()] * 2, self.node.save.call_args_list)
  991         self.assertIn(expected_str, self.node.last_error)
  992         self.assertEqual({}, self.node.deploy_step)
  993         self.assertNotIn('deploy_step_index', self.node.driver_internal_info)
  994         self.task.process_event.assert_called_once_with('fail')
  995 
  996     def test_deploying_error_handler_cleanup_ironic_exception(self):
  997         self._test_deploying_error_handler_cleanup(
  998             exception.IronicException('moocow'), 'moocow')
  999 
 1000     def test_deploying_error_handler_cleanup_random_exception(self):
 1001         self._test_deploying_error_handler_cleanup(
 1002             Exception('moocow'), 'unhandled exception')
 1003 
 1004     def test_deploying_error_handler_no_cleanup(self):
 1005         conductor_utils.deploying_error_handler(
 1006             self.task, self.logmsg, self.errmsg, clean_up=False)
 1007 
 1008         self.assertFalse(self.task.driver.deploy.clean_up.called)
 1009         self.assertEqual([mock.call()] * 2, self.node.save.call_args_list)
 1010         self.assertEqual(self.errmsg, self.node.last_error)
 1011         self.assertEqual({}, self.node.deploy_step)
 1012         self.assertNotIn('deploy_step_index', self.node.driver_internal_info)
 1013         self.task.process_event.assert_called_once_with('fail')
 1014 
 1015     def test_deploying_error_handler_not_deploy(self):
 1016         # Not in a deploy state
 1017         self.node.provision_state = states.AVAILABLE
 1018         self.node.driver_internal_info['deploy_step_index'] = 2
 1019 
 1020         conductor_utils.deploying_error_handler(
 1021             self.task, self.logmsg, self.errmsg, clean_up=False)
 1022 
 1023         self.assertEqual([mock.call()] * 2, self.node.save.call_args_list)
 1024         self.assertEqual(self.errmsg, self.node.last_error)
 1025         self.assertIsNone(self.node.deploy_step)
 1026         self.assertIn('deploy_step_index', self.node.driver_internal_info)
 1027         self.task.process_event.assert_called_once_with('fail')
 1028 
 1029 
 1030 class ErrorHandlersTestCase(tests_base.TestCase):
 1031     def setUp(self):
 1032         super(ErrorHandlersTestCase, self).setUp()
 1033         self.task = mock.Mock(spec=task_manager.TaskManager)
 1034         self.task.driver = mock.Mock(spec_set=['deploy', 'network', 'rescue'])
 1035         self.task.node = mock.Mock(spec_set=objects.Node)
 1036         self.task.shared = False
 1037         self.node = self.task.node
 1038         # NOTE(mariojv) Some of the test cases that use the task below require
 1039         # strict typing of the node power state fields and would fail if passed
 1040         # a Mock object in constructors. A task context is also required for
 1041         # notifications.
 1042         self.node.configure_mock(power_state=states.POWER_OFF,
 1043                                  target_power_state=states.POWER_ON,
 1044                                  maintenance=False, maintenance_reason=None)
 1045         self.task.context = self.context
 1046 
 1047     @mock.patch.object(conductor_utils, 'LOG', autospec=True)
 1048     def test_provision_error_handler_no_worker(self, log_mock):
 1049         exc = exception.NoFreeConductorWorker()
 1050         conductor_utils.provisioning_error_handler(exc, self.node, 'state-one',
 1051                                                    'state-two')
 1052         self.node.save.assert_called_once_with()
 1053         self.assertEqual('state-one', self.node.provision_state)
 1054         self.assertEqual('state-two', self.node.target_provision_state)
 1055         self.assertIn('No free conductor workers', self.node.last_error)
 1056         self.assertTrue(log_mock.warning.called)
 1057 
 1058     @mock.patch.object(conductor_utils, 'LOG', autospec=True)
 1059     def test_provision_error_handler_other_error(self, log_mock):
 1060         exc = Exception('foo')
 1061         conductor_utils.provisioning_error_handler(exc, self.node, 'state-one',
 1062                                                    'state-two')
 1063         self.assertFalse(self.node.save.called)
 1064         self.assertFalse(log_mock.warning.called)
 1065 
 1066     @mock.patch.object(conductor_utils, 'cleaning_error_handler',
 1067                        autospec=True)
 1068     def test_cleanup_cleanwait_timeout_handler_call(self, mock_error_handler):
 1069         self.task.node.uuid = '18c95393-b775-4887-a274-c45be47509d5'
 1070         self.node.clean_step = {}
 1071         conductor_utils.cleanup_cleanwait_timeout(self.task)
 1072 
 1073         mock_error_handler.assert_called_once_with(
 1074             self.task,
 1075             logmsg="Cleaning for node 18c95393-b775-4887-a274-c45be47509d5 "
 1076                    "failed. Timeout reached while cleaning the node. Please "
 1077                    "check if the ramdisk responsible for the cleaning is "
 1078                    "running on the node. Failed on step {}.",
 1079             errmsg="Timeout reached while cleaning the node. Please "
 1080                    "check if the ramdisk responsible for the cleaning is "
 1081                    "running on the node. Failed on step {}.",
 1082             set_fail_state=False)
 1083 
 1084     def test_cleanup_cleanwait_timeout(self):
 1085         self.node.provision_state = states.CLEANFAIL
 1086         target = 'baz'
 1087         self.node.target_provision_state = target
 1088         self.node.driver_internal_info = {}
 1089         self.node.clean_step = {'key': 'val'}
 1090         clean_error = ("Timeout reached while cleaning the node. Please "
 1091                        "check if the ramdisk responsible for the cleaning is "
 1092                        "running on the node. Failed on step {'key': 'val'}.")
 1093         self.node.driver_internal_info = {
 1094             'cleaning_reboot': True,
 1095             'clean_step_index': 0}
 1096         conductor_utils.cleanup_cleanwait_timeout(self.task)
 1097         self.assertEqual({}, self.node.clean_step)
 1098         self.assertNotIn('clean_step_index', self.node.driver_internal_info)
 1099         self.assertFalse(self.task.process_event.called)
 1100         self.assertTrue(self.node.maintenance)
 1101         self.assertEqual(clean_error, self.node.maintenance_reason)
 1102         self.assertEqual('clean failure', self.node.fault)
 1103 
 1104     @mock.patch.object(conductor_utils.LOG, 'error', autospec=True)
 1105     def _test_cleaning_error_handler(self, mock_log_error,
 1106                                      prov_state=states.CLEANING):
 1107         self.node.provision_state = prov_state
 1108         target = 'baz'
 1109         self.node.target_provision_state = target
 1110         self.node.clean_step = {'key': 'val'}
 1111         self.node.driver_internal_info = {
 1112             'cleaning_reboot': True,
 1113             'cleaning_polling': True,
 1114             'skip_current_clean_step': True,
 1115             'clean_step_index': 0,
 1116             'agent_url': 'url'}
 1117         msg = 'error bar'
 1118         last_error = "last error"
 1119         conductor_utils.cleaning_error_handler(self.task, msg,
 1120                                                errmsg=last_error)
 1121         self.node.save.assert_called_once_with()
 1122         self.assertEqual({}, self.node.clean_step)
 1123         self.assertNotIn('clean_step_index', self.node.driver_internal_info)
 1124         self.assertNotIn('cleaning_reboot', self.node.driver_internal_info)
 1125         self.assertNotIn('cleaning_polling', self.node.driver_internal_info)
 1126         self.assertNotIn('skip_current_clean_step',
 1127                          self.node.driver_internal_info)
 1128         self.assertEqual(last_error, self.node.last_error)
 1129         self.assertTrue(self.node.maintenance)
 1130         self.assertEqual(last_error, self.node.maintenance_reason)
 1131         self.assertEqual('clean failure', self.node.fault)
 1132         driver = self.task.driver.deploy
 1133         driver.tear_down_cleaning.assert_called_once_with(self.task)
 1134         if prov_state == states.CLEANFAIL:
 1135             self.assertFalse(self.task.process_event.called)
 1136         else:
 1137             self.task.process_event.assert_called_once_with('fail',
 1138                                                             target_state=None)
 1139         self.assertNotIn('agent_url', self.node.driver_internal_info)
 1140         mock_log_error.assert_called_once_with(msg, exc_info=False)
 1141 
 1142     def test_cleaning_error_handler(self):
 1143         self._test_cleaning_error_handler()
 1144 
 1145     def test_cleaning_error_handler_cleanwait(self):
 1146         self._test_cleaning_error_handler(prov_state=states.CLEANWAIT)
 1147 
 1148     def test_cleaning_error_handler_cleanfail(self):
 1149         self._test_cleaning_error_handler(prov_state=states.CLEANFAIL)
 1150 
 1151     def test_cleaning_error_handler_manual(self):
 1152         target = states.MANAGEABLE
 1153         self.node.target_provision_state = target
 1154         conductor_utils.cleaning_error_handler(self.task, 'foo')
 1155         self.task.process_event.assert_called_once_with('fail',
 1156                                                         target_state=target)
 1157 
 1158     def test_cleaning_error_handler_no_teardown(self):
 1159         target = states.MANAGEABLE
 1160         self.node.target_provision_state = target
 1161         conductor_utils.cleaning_error_handler(self.task, 'foo',
 1162                                                tear_down_cleaning=False)
 1163         self.assertFalse(self.task.driver.deploy.tear_down_cleaning.called)
 1164         self.task.process_event.assert_called_once_with('fail',
 1165                                                         target_state=target)
 1166 
 1167     def test_cleaning_error_handler_no_fail(self):
 1168         conductor_utils.cleaning_error_handler(self.task, 'foo',
 1169                                                set_fail_state=False)
 1170         driver = self.task.driver.deploy
 1171         driver.tear_down_cleaning.assert_called_once_with(self.task)
 1172         self.assertFalse(self.task.process_event.called)
 1173 
 1174     @mock.patch.object(conductor_utils, 'LOG', autospec=True)
 1175     def test_cleaning_error_handler_tear_down_error(self, log_mock):
 1176         def _side_effect(task):
 1177             # simulate overwriting last error by another operation (e.g. power)
 1178             task.node.last_error = None
 1179             raise Exception('bar')
 1180 
 1181         driver = self.task.driver.deploy
 1182         msg = 'foo'
 1183         driver.tear_down_cleaning.side_effect = _side_effect
 1184         conductor_utils.cleaning_error_handler(self.task, msg)
 1185         log_mock.error.assert_called_once_with(msg, exc_info=False)
 1186         self.assertTrue(log_mock.exception.called)
 1187         self.assertIn(msg, self.node.last_error)
 1188         self.assertIn(msg, self.node.maintenance_reason)
 1189         self.assertEqual('clean failure', self.node.fault)
 1190 
 1191     def test_abort_on_conductor_take_over_cleaning(self):
 1192         self.node.provision_state = states.CLEANFAIL
 1193         conductor_utils.abort_on_conductor_take_over(self.task)
 1194         self.assertTrue(self.node.maintenance)
 1195         self.assertIn('take over', self.node.maintenance_reason)
 1196         self.assertIn('take over', self.node.last_error)
 1197         self.assertEqual('clean failure', self.node.fault)
 1198         self.task.driver.deploy.tear_down_cleaning.assert_called_once_with(
 1199             self.task)
 1200         self.node.save.assert_called_once_with()
 1201 
 1202     def test_abort_on_conductor_take_over_deploying(self):
 1203         self.node.provision_state = states.DEPLOYFAIL
 1204         conductor_utils.abort_on_conductor_take_over(self.task)
 1205         self.assertFalse(self.node.maintenance)
 1206         self.assertIn('take over', self.node.last_error)
 1207         self.node.save.assert_called_once_with()
 1208 
 1209     @mock.patch.object(conductor_utils, 'LOG', autospec=True)
 1210     def test_spawn_cleaning_error_handler_no_worker(self, log_mock):
 1211         exc = exception.NoFreeConductorWorker()
 1212         conductor_utils.spawn_cleaning_error_handler(exc, self.node)
 1213         self.node.save.assert_called_once_with()
 1214         self.assertIn('No free conductor workers', self.node.last_error)
 1215         self.assertTrue(log_mock.warning.called)
 1216 
 1217     @mock.patch.object(conductor_utils, 'LOG', autospec=True)
 1218     def test_spawn_cleaning_error_handler_other_error(self, log_mock):
 1219         exc = Exception('foo')
 1220         conductor_utils.spawn_cleaning_error_handler(exc, self.node)
 1221         self.assertFalse(self.node.save.called)
 1222         self.assertFalse(log_mock.warning.called)
 1223 
 1224     @mock.patch.object(conductor_utils, 'LOG', autospec=True)
 1225     def test_spawn_deploying_error_handler_no_worker(self, log_mock):
 1226         exc = exception.NoFreeConductorWorker()
 1227         conductor_utils.spawn_deploying_error_handler(exc, self.node)
 1228         self.node.save.assert_called_once_with()
 1229         self.assertIn('No free conductor workers', self.node.last_error)
 1230         self.assertTrue(log_mock.warning.called)
 1231 
 1232     @mock.patch.object(conductor_utils, 'LOG', autospec=True)
 1233     def test_spawn_deploying_error_handler_other_error(self, log_mock):
 1234         exc = Exception('foo')
 1235         conductor_utils.spawn_deploying_error_handler(exc, self.node)
 1236         self.assertFalse(self.node.save.called)
 1237         self.assertFalse(log_mock.warning.called)
 1238 
 1239     @mock.patch.object(conductor_utils, 'LOG', autospec=True)
 1240     def test_spawn_rescue_error_handler_no_worker(self, log_mock):
 1241         exc = exception.NoFreeConductorWorker()
 1242         self.node.instance_info = {'rescue_password': 'pass',
 1243                                    'hashed_rescue_password': '12'}
 1244         conductor_utils.spawn_rescue_error_handler(exc, self.node)
 1245         self.node.save.assert_called_once_with()
 1246         self.assertIn('No free conductor workers', self.node.last_error)
 1247         self.assertTrue(log_mock.warning.called)
 1248         self.assertNotIn('rescue_password', self.node.instance_info)
 1249         self.assertNotIn('hashed_rescue_password', self.node.instance_info)
 1250 
 1251     @mock.patch.object(conductor_utils, 'LOG', autospec=True)
 1252     def test_spawn_rescue_error_handler_other_error(self, log_mock):
 1253         exc = Exception('foo')
 1254         self.node.instance_info = {'rescue_password': 'pass',
 1255                                    'hashed_rescue_password': '12'}
 1256         conductor_utils.spawn_rescue_error_handler(exc, self.node)
 1257         self.assertFalse(self.node.save.called)
 1258         self.assertFalse(log_mock.warning.called)
 1259         self.assertIn('rescue_password', self.node.instance_info)
 1260 
 1261     @mock.patch.object(conductor_utils, 'LOG', autospec=True)
 1262     def test_power_state_error_handler_no_worker(self, log_mock):
 1263         exc = exception.NoFreeConductorWorker()
 1264         conductor_utils.power_state_error_handler(exc, self.node, 'newstate')
 1265         self.node.save.assert_called_once_with()
 1266         self.assertEqual('newstate', self.node.power_state)
 1267         self.assertEqual(states.NOSTATE, self.node.target_power_state)
 1268         self.assertIn('No free conductor workers', self.node.last_error)
 1269         self.assertTrue(log_mock.warning.called)
 1270 
 1271     @mock.patch.object(conductor_utils, 'LOG', autospec=True)
 1272     def test_power_state_error_handler_other_error(self, log_mock):
 1273         exc = Exception('foo')
 1274         conductor_utils.power_state_error_handler(exc, self.node, 'foo')
 1275         self.assertFalse(self.node.save.called)
 1276         self.assertFalse(log_mock.warning.called)
 1277 
 1278     @mock.patch.object(conductor_utils, 'LOG', autospec=True)
 1279     @mock.patch.object(conductor_utils, 'node_power_action', autospec=True)
 1280     def test_cleanup_rescuewait_timeout(self, node_power_mock, log_mock):
 1281         conductor_utils.cleanup_rescuewait_timeout(self.task)
 1282         self.assertTrue(log_mock.error.called)
 1283         node_power_mock.assert_called_once_with(mock.ANY, states.POWER_OFF)
 1284         self.task.driver.rescue.clean_up.assert_called_once_with(self.task)
 1285         self.assertIn('Timeout reached', self.node.last_error)
 1286         self.node.save.assert_called_once_with()
 1287 
 1288     @mock.patch.object(conductor_utils, 'LOG', autospec=True)
 1289     @mock.patch.object(conductor_utils, 'node_power_action', autospec=True)
 1290     def test_cleanup_rescuewait_timeout_known_exc(
 1291             self, node_power_mock, log_mock):
 1292         clean_up_mock = self.task.driver.rescue.clean_up
 1293         clean_up_mock.side_effect = exception.IronicException('moocow')
 1294         conductor_utils.cleanup_rescuewait_timeout(self.task)
 1295         self.assertEqual(2, log_mock.error.call_count)
 1296         node_power_mock.assert_called_once_with(mock.ANY, states.POWER_OFF)
 1297         self.task.driver.rescue.clean_up.assert_called_once_with(self.task)
 1298         self.assertIn('moocow', self.node.last_error)
 1299         self.node.save.assert_called_once_with()
 1300 
 1301     @mock.patch.object(conductor_utils, 'LOG', autospec=True)
 1302     @mock.patch.object(conductor_utils, 'node_power_action', autospec=True)
 1303     def test_cleanup_rescuewait_timeout_unknown_exc(
 1304             self, node_power_mock, log_mock):
 1305         clean_up_mock = self.task.driver.rescue.clean_up
 1306         clean_up_mock.side_effect = Exception('moocow')
 1307         conductor_utils.cleanup_rescuewait_timeout(self.task)
 1308         self.assertTrue(log_mock.error.called)
 1309         node_power_mock.assert_called_once_with(mock.ANY, states.POWER_OFF)
 1310         self.task.driver.rescue.clean_up.assert_called_once_with(self.task)
 1311         self.assertIn('Rescue failed', self.node.last_error)
 1312         self.node.save.assert_called_once_with()
 1313         self.assertTrue(log_mock.exception.called)
 1314 
 1315     @mock.patch.object(conductor_utils, 'node_power_action', autospec=True)
 1316     def _test_rescuing_error_handler(self, node_power_mock,
 1317                                      set_state=True):
 1318         self.node.provision_state = states.RESCUEWAIT
 1319         self.node.driver_internal_info.update({'agent_url': 'url'})
 1320         conductor_utils.rescuing_error_handler(self.task,
 1321                                                'some exception for node',
 1322                                                set_fail_state=set_state)
 1323         node_power_mock.assert_called_once_with(mock.ANY, states.POWER_OFF)
 1324         self.task.driver.rescue.clean_up.assert_called_once_with(self.task)
 1325         self.node.save.assert_called_once_with()
 1326         self.assertNotIn('agent_url', self.node.driver_internal_info)
 1327         if set_state:
 1328             self.assertTrue(self.task.process_event.called)
 1329         else:
 1330             self.assertFalse(self.task.process_event.called)
 1331 
 1332     def test_rescuing_error_handler(self):
 1333         self._test_rescuing_error_handler()
 1334 
 1335     def test_rescuing_error_handler_set_failed_state_false(self):
 1336         self._test_rescuing_error_handler(set_state=False)
 1337 
 1338     @mock.patch.object(conductor_utils.LOG, 'error', autospec=True)
 1339     @mock.patch.object(conductor_utils, 'node_power_action', autospec=True)
 1340     def test_rescuing_error_handler_ironic_exc(self, node_power_mock,
 1341                                                log_mock):
 1342         self.node.provision_state = states.RESCUEWAIT
 1343         expected_exc = exception.IronicException('moocow')
 1344         clean_up_mock = self.task.driver.rescue.clean_up
 1345         clean_up_mock.side_effect = expected_exc
 1346         conductor_utils.rescuing_error_handler(self.task,
 1347                                                'some exception for node')
 1348         node_power_mock.assert_called_once_with(mock.ANY, states.POWER_OFF)
 1349         self.task.driver.rescue.clean_up.assert_called_once_with(self.task)
 1350         log_mock.assert_called_once_with('Rescue operation was unsuccessful, '
 1351                                          'clean up failed for node %(node)s: '
 1352                                          '%(error)s',
 1353                                          {'node': self.node.uuid,
 1354                                           'error': expected_exc})
 1355         self.node.save.assert_called_once_with()
 1356 
 1357     @mock.patch.object(conductor_utils.LOG, 'exception', autospec=True)
 1358     @mock.patch.object(conductor_utils, 'node_power_action', autospec=True)
 1359     def test_rescuing_error_handler_other_exc(self, node_power_mock,
 1360                                               log_mock):
 1361         self.node.provision_state = states.RESCUEWAIT
 1362         expected_exc = RuntimeError()
 1363         clean_up_mock = self.task.driver.rescue.clean_up
 1364         clean_up_mock.side_effect = expected_exc
 1365         conductor_utils.rescuing_error_handler(self.task,
 1366                                                'some exception for node')
 1367         node_power_mock.assert_called_once_with(mock.ANY, states.POWER_OFF)
 1368         self.task.driver.rescue.clean_up.assert_called_once_with(self.task)
 1369         log_mock.assert_called_once_with('Rescue failed for node '
 1370                                          '%(node)s, an exception was '
 1371                                          'encountered while aborting.',
 1372                                          {'node': self.node.uuid})
 1373         self.node.save.assert_called_once_with()
 1374 
 1375     @mock.patch.object(conductor_utils.LOG, 'error', autospec=True)
 1376     @mock.patch.object(conductor_utils, 'node_power_action', autospec=True)
 1377     def test_rescuing_error_handler_bad_state(self, node_power_mock,
 1378                                               log_mock):
 1379         self.node.provision_state = states.RESCUE
 1380         self.task.process_event.side_effect = exception.InvalidState
 1381         expected_exc = exception.IronicException('moocow')
 1382         clean_up_mock = self.task.driver.rescue.clean_up
 1383         clean_up_mock.side_effect = expected_exc
 1384         conductor_utils.rescuing_error_handler(self.task,
 1385                                                'some exception for node')
 1386         node_power_mock.assert_called_once_with(mock.ANY, states.POWER_OFF)
 1387         self.task.driver.rescue.clean_up.assert_called_once_with(self.task)
 1388         self.task.process_event.assert_called_once_with('fail')
 1389         log_calls = [mock.call('Rescue operation was unsuccessful, clean up '
 1390                                'failed for node %(node)s: %(error)s',
 1391                                {'node': self.node.uuid,
 1392                                 'error': expected_exc}),
 1393                      mock.call('Internal error. Node %(node)s in provision '
 1394                                'state "%(state)s" could not transition to a '
 1395                                'failed state.',
 1396                                {'node': self.node.uuid,
 1397                                 'state': self.node.provision_state})]
 1398         log_mock.assert_has_calls(log_calls)
 1399         self.node.save.assert_called_once_with()
 1400 
 1401 
 1402 class ValidatePortPhysnetTestCase(db_base.DbTestCase):
 1403 
 1404     def setUp(self):
 1405         super(ValidatePortPhysnetTestCase, self).setUp()
 1406         self.node = obj_utils.create_test_node(self.context,
 1407                                                driver='fake-hardware')
 1408 
 1409     @mock.patch.object(objects.Port, 'obj_what_changed', autospec=True)
 1410     def test_validate_port_physnet_no_portgroup_create(self, mock_owc):
 1411         port = obj_utils.get_test_port(self.context, node_id=self.node.id)
 1412         # NOTE(mgoddard): The port object passed to the conductor will not have
 1413         # a portgroup_id attribute in this case.
 1414         del port.portgroup_id
 1415         with task_manager.acquire(self.context, self.node.uuid) as task:
 1416             conductor_utils.validate_port_physnet(task, port)
 1417         # Verify the early return in the non-portgroup case.
 1418         self.assertFalse(mock_owc.called)
 1419 
 1420     @mock.patch.object(network, 'get_ports_by_portgroup_id', autospec=True)
 1421     def test_validate_port_physnet_no_portgroup_update(self, mock_gpbpi):
 1422         port = obj_utils.create_test_port(self.context, node_id=self.node.id)
 1423         port.extra = {'foo': 'bar'}
 1424         with task_manager.acquire(self.context, self.node.uuid) as task:
 1425             conductor_utils.validate_port_physnet(task, port)
 1426         # Verify the early return in the no portgroup update case.
 1427         self.assertFalse(mock_gpbpi.called)
 1428 
 1429     def test_validate_port_physnet_inconsistent_physnets(self):
 1430         # NOTE(mgoddard): This *shouldn't* happen, but let's make sure we can
 1431         # handle it.
 1432         portgroup = obj_utils.create_test_portgroup(self.context,
 1433                                                     node_id=self.node.id)
 1434         obj_utils.create_test_port(self.context, node_id=self.node.id,
 1435                                    portgroup_id=portgroup.id,
 1436                                    address='00:11:22:33:44:55',
 1437                                    physical_network='physnet1',
 1438                                    uuid=uuidutils.generate_uuid())
 1439         obj_utils.create_test_port(self.context, node_id=self.node.id,
 1440                                    portgroup_id=portgroup.id,
 1441                                    address='00:11:22:33:44:56',
 1442                                    physical_network='physnet2',
 1443                                    uuid=uuidutils.generate_uuid())
 1444         port = obj_utils.get_test_port(self.context, node_id=self.node.id,
 1445                                        portgroup_id=portgroup.id,
 1446                                        address='00:11:22:33:44:57',
 1447                                        physical_network='physnet2',
 1448                                        uuid=uuidutils.generate_uuid())
 1449 
 1450         with task_manager.acquire(self.context, self.node.uuid) as task:
 1451             self.assertRaises(exception.PortgroupPhysnetInconsistent,
 1452                               conductor_utils.validate_port_physnet,
 1453                               task, port)
 1454 
 1455     def test_validate_port_physnet_inconsistent_physnets_fix(self):
 1456         # NOTE(mgoddard): This *shouldn't* happen, but let's make sure that if
 1457         # we do get into this state that it is possible to resolve by setting
 1458         # the physical_network correctly.
 1459         portgroup = obj_utils.create_test_portgroup(self.context,
 1460                                                     node_id=self.node.id)
 1461         obj_utils.create_test_port(self.context, node_id=self.node.id,
 1462                                    portgroup_id=portgroup.id,
 1463                                    address='00:11:22:33:44:55',
 1464                                    physical_network='physnet1',
 1465                                    uuid=uuidutils.generate_uuid())
 1466         port = obj_utils.create_test_port(self.context, node_id=self.node.id,
 1467                                           portgroup_id=portgroup.id,
 1468                                           address='00:11:22:33:44:56',
 1469                                           physical_network='physnet2',
 1470                                           uuid=uuidutils.generate_uuid())
 1471         port.physical_network = 'physnet1'
 1472 
 1473         with task_manager.acquire(self.context, self.node.uuid) as task:
 1474             conductor_utils.validate_port_physnet(task, port)
 1475 
 1476     def _test_validate_port_physnet(self,
 1477                                     num_current_ports,
 1478                                     current_physnet,
 1479                                     new_physnet,
 1480                                     operation,
 1481                                     valid=True):
 1482         """Helper method for testing validate_port_physnet.
 1483 
 1484         :param num_current_ports: Number of existing ports in the portgroup.
 1485         :param current_physnet: Physical network of existing ports in the
 1486                                 portgroup.
 1487         :param new_physnet: Physical network to set on the port that is being
 1488                             created or updated.
 1489         :param operation: The operation to perform. One of 'create', 'update',
 1490                           or 'update_add'. 'create' creates a new port and adds
 1491                           it to the portgroup. 'update' updates one of the
 1492                           existing ports. 'update_add' updates a port and adds
 1493                           it to the portgroup.
 1494         :param valid: Whether the operation is expected to succeed.
 1495         """
 1496         # Prepare existing resources - a node, and a portgroup with optional
 1497         # existing ports.
 1498         port = None
 1499         portgroup = obj_utils.create_test_portgroup(self.context,
 1500                                                     node_id=self.node.id)
 1501         macs = ("00:11:22:33:44:%02x" % index
 1502                 for index in range(num_current_ports + 1))
 1503         for _ in range(num_current_ports):
 1504             # NOTE: When operation == 'update' we update the last port in the
 1505             # portgroup.
 1506             port = obj_utils.create_test_port(
 1507                 self.context, node_id=self.node.id, portgroup_id=portgroup.id,
 1508                 address=next(macs), physical_network=current_physnet,
 1509                 uuid=uuidutils.generate_uuid())
 1510 
 1511         # Prepare the port on which we are performing the operation.
 1512         if operation == 'create':
 1513             # NOTE(mgoddard): We use db_utils here rather than obj_utils as it
 1514             # allows us to create a Port without a physical_network field, more
 1515             # closely matching what happens during creation of a port when a
 1516             # physical_network is not specified.
 1517             port = db_utils.get_test_port(
 1518                 node_id=self.node.id, portgroup_id=portgroup.id,
 1519                 address=next(macs), uuid=uuidutils.generate_uuid(),
 1520                 physical_network=new_physnet)
 1521             if new_physnet is None:
 1522                 del port["physical_network"]
 1523             port = objects.Port(self.context, **port)
 1524         elif operation == 'update_add':
 1525             port = obj_utils.create_test_port(
 1526                 self.context, node_id=self.node.id, portgroup_id=None,
 1527                 address=next(macs), physical_network=current_physnet,
 1528                 uuid=uuidutils.generate_uuid())
 1529             port.portgroup_id = portgroup.id
 1530 
 1531         if operation != 'create' and new_physnet != current_physnet:
 1532             port.physical_network = new_physnet
 1533 
 1534         # Perform the validation.
 1535         with task_manager.acquire(self.context, self.node.uuid) as task:
 1536             if valid:
 1537                 conductor_utils.validate_port_physnet(task, port)
 1538             else:
 1539                 self.assertRaises(exception.Conflict,
 1540                                   conductor_utils.validate_port_physnet,
 1541                                   task, port)
 1542 
 1543     def _test_validate_port_physnet_create(self, **kwargs):
 1544         self._test_validate_port_physnet(operation='create', **kwargs)
 1545 
 1546     def _test_validate_port_physnet_update(self, **kwargs):
 1547         self._test_validate_port_physnet(operation='update', **kwargs)
 1548 
 1549     def _test_validate_port_physnet_update_add(self, **kwargs):
 1550         self._test_validate_port_physnet(operation='update_add', **kwargs)
 1551 
 1552     # Empty portgroup
 1553 
 1554     def test_validate_port_physnet_empty_portgroup_create_1(self):
 1555         self._test_validate_port_physnet_create(
 1556             num_current_ports=0,
 1557             current_physnet=None,
 1558             new_physnet=None)
 1559 
 1560     def test_validate_port_physnet_empty_portgroup_create_2(self):
 1561         self._test_validate_port_physnet_create(
 1562             num_current_ports=0,
 1563             current_physnet=None,
 1564             new_physnet='physnet1')
 1565 
 1566     def test_validate_port_physnet_empty_portgroup_update_1(self):
 1567         self._test_validate_port_physnet_update_add(
 1568             num_current_ports=0,
 1569             current_physnet=None,
 1570             new_physnet=None)
 1571 
 1572     def test_validate_port_physnet_empty_portgroup_update_2(self):
 1573         self._test_validate_port_physnet_update_add(
 1574             num_current_ports=0,
 1575             current_physnet=None,
 1576             new_physnet='physnet1')
 1577 
 1578     # 1-port portgroup, no physnet.
 1579 
 1580     def test_validate_port_physnet_1_port_portgroup_no_physnet_create_1(self):
 1581         self._test_validate_port_physnet_create(
 1582             num_current_ports=1,
 1583             current_physnet=None,
 1584             new_physnet=None)
 1585 
 1586     def test_validate_port_physnet_1_port_portgroup_no_physnet_create_2(self):
 1587         self._test_validate_port_physnet_create(
 1588             num_current_ports=1,
 1589             current_physnet=None,
 1590             new_physnet='physnet1',
 1591             valid=False)
 1592 
 1593     def test_validate_port_physnet_1_port_portgroup_no_physnet_update_1(self):
 1594         self._test_validate_port_physnet_update(
 1595             num_current_ports=1,
 1596             current_physnet=None,
 1597             new_physnet=None)
 1598 
 1599     def test_validate_port_physnet_1_port_portgroup_no_physnet_update_2(self):
 1600         self._test_validate_port_physnet_update(
 1601             num_current_ports=1,
 1602             current_physnet=None,
 1603             new_physnet='physnet1')
 1604 
 1605     def test_validate_port_physnet_1_port_portgroup_no_physnet_update_add_1(
 1606             self):
 1607         self._test_validate_port_physnet_update_add(
 1608             num_current_ports=1,
 1609             current_physnet=None,
 1610             new_physnet=None)
 1611 
 1612     def test_validate_port_physnet_1_port_portgroup_no_physnet_update_add_2(
 1613             self):
 1614         self._test_validate_port_physnet_update_add(
 1615             num_current_ports=1,
 1616             current_physnet=None,
 1617             new_physnet='physnet1',
 1618             valid=False)
 1619 
 1620     # 1-port portgroup, with physnet 'physnet1'.
 1621 
 1622     def test_validate_port_physnet_1_port_portgroup_w_physnet_create_1(self):
 1623         self._test_validate_port_physnet_create(
 1624             num_current_ports=1,
 1625             current_physnet='physnet1',
 1626             new_physnet='physnet1')
 1627 
 1628     def test_validate_port_physnet_1_port_portgroup_w_physnet_create_2(self):
 1629         self._test_validate_port_physnet_create(
 1630             num_current_ports=1,
 1631             current_physnet='physnet1',
 1632             new_physnet='physnet2',
 1633             valid=False)
 1634 
 1635     def test_validate_port_physnet_1_port_portgroup_w_physnet_create_3(self):
 1636         self._test_validate_port_physnet_create(
 1637             num_current_ports=1,
 1638             current_physnet='physnet1',
 1639             new_physnet=None,
 1640             valid=False)
 1641 
 1642     def test_validate_port_physnet_1_port_portgroup_w_physnet_update_1(self):
 1643         self._test_validate_port_physnet_update(
 1644             num_current_ports=1,
 1645             current_physnet='physnet1',
 1646             new_physnet='physnet1')
 1647 
 1648     def test_validate_port_physnet_1_port_portgroup_w_physnet_update_2(self):
 1649         self._test_validate_port_physnet_update(
 1650             num_current_ports=1,
 1651             current_physnet='physnet1',
 1652             new_physnet='physnet2')
 1653 
 1654     def test_validate_port_physnet_1_port_portgroup_w_physnet_update_3(self):
 1655         self._test_validate_port_physnet_update(
 1656             num_current_ports=1,
 1657             current_physnet='physnet1',
 1658             new_physnet=None)
 1659 
 1660     def test_validate_port_physnet_1_port_portgroup_w_physnet_update_add_1(
 1661             self):
 1662         self._test_validate_port_physnet_update_add(
 1663             num_current_ports=1,
 1664             current_physnet='physnet1',
 1665             new_physnet='physnet1')
 1666 
 1667     def test_validate_port_physnet_1_port_portgroup_w_physnet_update_add_2(
 1668             self):
 1669         self._test_validate_port_physnet_update_add(
 1670             num_current_ports=1,
 1671             current_physnet='physnet1',
 1672             new_physnet='physnet2',
 1673             valid=False)
 1674 
 1675     def test_validate_port_physnet_1_port_portgroup_w_physnet_update_add_3(
 1676             self):
 1677         self._test_validate_port_physnet_update_add(
 1678             num_current_ports=1,
 1679             current_physnet='physnet1',
 1680             new_physnet=None,
 1681             valid=False)
 1682 
 1683     # 2-port portgroup, no physnet
 1684 
 1685     def test_validate_port_physnet_2_port_portgroup_no_physnet_update_1(self):
 1686         self._test_validate_port_physnet_update(
 1687             num_current_ports=2,
 1688             current_physnet=None,
 1689             new_physnet=None)
 1690 
 1691     def test_validate_port_physnet_2_port_portgroup_no_physnet_update_2(self):
 1692         self._test_validate_port_physnet_update(
 1693             num_current_ports=2,
 1694             current_physnet=None,
 1695             new_physnet='physnet1',
 1696             valid=False)
 1697 
 1698     # 2-port portgroup, with physnet 'physnet1'
 1699 
 1700     def test_validate_port_physnet_2_port_portgroup_w_physnet_update_1(self):
 1701         self._test_validate_port_physnet_update(
 1702             num_current_ports=2,
 1703             current_physnet='physnet1',
 1704             new_physnet='physnet1')
 1705 
 1706     def test_validate_port_physnet_2_port_portgroup_w_physnet_update_2(self):
 1707         self._test_validate_port_physnet_update(
 1708             num_current_ports=2,
 1709             current_physnet='physnet1',
 1710             new_physnet='physnet2',
 1711             valid=False)
 1712 
 1713     def test_validate_port_physnet_2_port_portgroup_w_physnet_update_3(self):
 1714         self._test_validate_port_physnet_update(
 1715             num_current_ports=2,
 1716             current_physnet='physnet1',
 1717             new_physnet=None,
 1718             valid=False)
 1719 
 1720 
 1721 class MiscTestCase(db_base.DbTestCase):
 1722     def setUp(self):
 1723         super(MiscTestCase, self).setUp()
 1724         self.node = obj_utils.create_test_node(
 1725             self.context,
 1726             driver='fake-hardware',
 1727             instance_info={'rescue_password': 'pass'})
 1728 
 1729     def _test_remove_node_rescue_password(self, save=True):
 1730         conductor_utils.remove_node_rescue_password(self.node, save=save)
 1731         self.assertNotIn('rescue_password', self.node.instance_info)
 1732         self.node.refresh()
 1733         if save:
 1734             self.assertNotIn('rescue_password', self.node.instance_info)
 1735         else:
 1736             self.assertIn('rescue_password', self.node.instance_info)
 1737 
 1738     def test_remove_node_rescue_password_save_true(self):
 1739         self._test_remove_node_rescue_password(save=True)
 1740 
 1741     def test_remove_node_rescue_password_save_false(self):
 1742         self._test_remove_node_rescue_password(save=False)
 1743 
 1744     @mock.patch.object(rpcapi.ConductorAPI, 'continue_node_deploy',
 1745                        autospec=True)
 1746     def test_notify_conductor_resume_operation(self, mock_rpc_call):
 1747         self.config(host='fake-host')
 1748         with task_manager.acquire(
 1749                 self.context, self.node.uuid, shared=False) as task:
 1750             conductor_utils.notify_conductor_resume_operation(task, 'deploy')
 1751             mock_rpc_call.assert_called_once_with(
 1752                 mock.ANY, task.context, self.node.uuid,
 1753                 topic='ironic.conductor_manager.fake-host')
 1754 
 1755     @mock.patch.object(conductor_utils, 'notify_conductor_resume_operation',
 1756                        autospec=True)
 1757     def test_notify_conductor_resume_clean(self, mock_resume):
 1758         with task_manager.acquire(
 1759                 self.context, self.node.uuid, shared=False) as task:
 1760             conductor_utils.notify_conductor_resume_clean(task)
 1761             mock_resume.assert_called_once_with(task, 'clean')
 1762 
 1763     @mock.patch.object(conductor_utils, 'notify_conductor_resume_operation',
 1764                        autospec=True)
 1765     def test_notify_conductor_resume_deploy(self, mock_resume):
 1766         with task_manager.acquire(
 1767                 self.context, self.node.uuid, shared=False) as task:
 1768             conductor_utils.notify_conductor_resume_deploy(task)
 1769             mock_resume.assert_called_once_with(task, 'deploy')
 1770 
 1771     @mock.patch.object(time, 'sleep', autospec=True)
 1772     @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
 1773     @mock.patch.object(drivers_base.NetworkInterface, 'need_power_on',
 1774                        autospec=True)
 1775     @mock.patch.object(conductor_utils, 'node_set_boot_device',
 1776                        autospec=True)
 1777     @mock.patch.object(conductor_utils, 'node_power_action',
 1778                        autospec=True)
 1779     def test_power_on_node_if_needed_true(
 1780             self, power_action_mock, boot_device_mock,
 1781             need_power_on_mock, get_power_state_mock, time_mock):
 1782         with task_manager.acquire(
 1783                 self.context, self.node.uuid, shared=False) as task:
 1784             need_power_on_mock.return_value = True
 1785             get_power_state_mock.return_value = states.POWER_OFF
 1786             power_state = conductor_utils.power_on_node_if_needed(task)
 1787             self.assertEqual(power_state, states.POWER_OFF)
 1788             boot_device_mock.assert_called_once_with(
 1789                 task, boot_devices.BIOS, persistent=False)
 1790             power_action_mock.assert_called_once_with(task, states.POWER_ON)
 1791 
 1792     @mock.patch.object(time, 'sleep', autospec=True)
 1793     @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
 1794     @mock.patch.object(drivers_base.NetworkInterface, 'need_power_on',
 1795                        autospec=True)
 1796     @mock.patch.object(conductor_utils, 'node_set_boot_device',
 1797                        autospec=True)
 1798     @mock.patch.object(conductor_utils, 'node_power_action',
 1799                        autospec=True)
 1800     def test_power_on_node_if_needed_false_power_on(
 1801             self, power_action_mock, boot_device_mock,
 1802             need_power_on_mock, get_power_state_mock, time_mock):
 1803         with task_manager.acquire(
 1804                 self.context, self.node.uuid, shared=False) as task:
 1805             need_power_on_mock.return_value = True
 1806             get_power_state_mock.return_value = states.POWER_ON
 1807             power_state = conductor_utils.power_on_node_if_needed(task)
 1808             self.assertIsNone(power_state)
 1809             self.assertEqual(0, boot_device_mock.call_count)
 1810             self.assertEqual(0, power_action_mock.call_count)
 1811 
 1812     @mock.patch.object(time, 'sleep', autospec=True)
 1813     @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
 1814     @mock.patch.object(drivers_base.NetworkInterface, 'need_power_on',
 1815                        autospec=True)
 1816     @mock.patch.object(conductor_utils, 'node_set_boot_device',
 1817                        autospec=True)
 1818     @mock.patch.object(conductor_utils, 'node_power_action',
 1819                        autospec=True)
 1820     def test_power_on_node_if_needed_false_no_need(
 1821             self, power_action_mock, boot_device_mock,
 1822             need_power_on_mock, get_power_state_mock, time_mock):
 1823         with task_manager.acquire(
 1824                 self.context, self.node.uuid, shared=False) as task:
 1825             need_power_on_mock.return_value = False
 1826             get_power_state_mock.return_value = states.POWER_OFF
 1827             power_state = conductor_utils.power_on_node_if_needed(task)
 1828             self.assertIsNone(power_state)
 1829             self.assertEqual(0, boot_device_mock.call_count)
 1830             self.assertEqual(0, power_action_mock.call_count)
 1831 
 1832     @mock.patch.object(neutron, 'get_client', autospec=True)
 1833     @mock.patch.object(neutron, 'wait_for_host_agent', autospec=True)
 1834     @mock.patch.object(time, 'sleep', autospec=True)
 1835     @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
 1836     @mock.patch.object(drivers_base.NetworkInterface, 'need_power_on',
 1837                        autospec=True)
 1838     @mock.patch.object(conductor_utils, 'node_set_boot_device',
 1839                        autospec=True)
 1840     @mock.patch.object(conductor_utils, 'node_power_action',
 1841                        autospec=True)
 1842     def test_power_on_node_if_needed_with_smart_nic_port(
 1843             self, power_action_mock, boot_device_mock,
 1844             need_power_on_mock, get_power_state_mock, time_mock,
 1845             wait_agent_mock, get_client_mock):
 1846         llc = {'port_id': 'rep0-0', 'hostname': 'host1'}
 1847         port = obj_utils.get_test_port(self.context, node_id=self.node.id,
 1848                                        is_smartnic=True,
 1849                                        local_link_connection=llc)
 1850         with task_manager.acquire(
 1851                 self.context, self.node.uuid, shared=False) as task:
 1852             task.ports = [port]
 1853             need_power_on_mock.return_value = True
 1854             get_power_state_mock.return_value = states.POWER_OFF
 1855             power_state = conductor_utils.power_on_node_if_needed(task)
 1856             self.assertEqual(power_state, states.POWER_OFF)
 1857             boot_device_mock.assert_called_once_with(
 1858                 task, boot_devices.BIOS, persistent=False)
 1859             power_action_mock.assert_called_once_with(task, states.POWER_ON)
 1860             get_client_mock.assert_called_once_with(context=self.context)
 1861             wait_agent_mock.assert_called_once_with(mock.ANY, 'host1',
 1862                                                     target_state='down')
 1863 
 1864     @mock.patch.object(time, 'sleep', autospec=True)
 1865     @mock.patch.object(conductor_utils, 'node_power_action',
 1866                        autospec=True)
 1867     def test_restore_power_state_if_needed_true(
 1868             self, power_action_mock, time_mock):
 1869         with task_manager.acquire(
 1870                 self.context, self.node.uuid, shared=False) as task:
 1871             power_state = states.POWER_OFF
 1872             conductor_utils.restore_power_state_if_needed(task, power_state)
 1873             power_action_mock.assert_called_once_with(task, power_state)
 1874 
 1875     @mock.patch.object(time, 'sleep', autospec=True)
 1876     @mock.patch.object(conductor_utils, 'node_power_action',
 1877                        autospec=True)
 1878     def test_restore_power_state_if_needed_false(
 1879             self, power_action_mock, time_mock):
 1880         with task_manager.acquire(
 1881                 self.context, self.node.uuid, shared=False) as task:
 1882             power_state = None
 1883             conductor_utils.restore_power_state_if_needed(task, power_state)
 1884             self.assertEqual(0, power_action_mock.call_count)
 1885 
 1886 
 1887 class ValidateInstanceInfoTraitsTestCase(tests_base.TestCase):
 1888 
 1889     def setUp(self):
 1890         super(ValidateInstanceInfoTraitsTestCase, self).setUp()
 1891         self.node = obj_utils.get_test_node(self.context,
 1892                                             driver='fake-hardware',
 1893                                             traits=['trait1', 'trait2'])
 1894 
 1895     def test_validate_instance_info_traits_no_instance_traits(self):
 1896         conductor_utils.validate_instance_info_traits(self.node)
 1897 
 1898     def test_validate_instance_info_traits_empty_instance_traits(self):
 1899         self.node.instance_info['traits'] = []
 1900         conductor_utils.validate_instance_info_traits(self.node)
 1901 
 1902     def test_validate_instance_info_traits_invalid_type(self):
 1903         self.node.instance_info['traits'] = 'not-a-list'
 1904         self.assertRaisesRegex(exception.InvalidParameterValue,
 1905                                'Error parsing traits from Node',
 1906                                conductor_utils.validate_instance_info_traits,
 1907                                self.node)
 1908 
 1909     def test_validate_instance_info_traits_invalid_trait_type(self):
 1910         self.node.instance_info['traits'] = ['trait1', {}]
 1911         self.assertRaisesRegex(exception.InvalidParameterValue,
 1912                                'Error parsing traits from Node',
 1913                                conductor_utils.validate_instance_info_traits,
 1914                                self.node)
 1915 
 1916     def test_validate_instance_info_traits(self):
 1917         self.node.instance_info['traits'] = ['trait1', 'trait2']
 1918         conductor_utils.validate_instance_info_traits(self.node)
 1919 
 1920     def test_validate_instance_info_traits_missing(self):
 1921         self.node.instance_info['traits'] = ['trait1', 'trait3']
 1922         self.assertRaisesRegex(exception.InvalidParameterValue,
 1923                                'Cannot specify instance traits that are not',
 1924                                conductor_utils.validate_instance_info_traits,
 1925                                self.node)
 1926 
 1927 
 1928 @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
 1929 class FastTrackTestCase(db_base.DbTestCase):
 1930 
 1931     def setUp(self):
 1932         super(FastTrackTestCase, self).setUp()
 1933         self.node = obj_utils.create_test_node(
 1934             self.context, driver='fake-hardware',
 1935             uuid=uuidutils.generate_uuid(),
 1936             driver_internal_info={
 1937                 'agent_last_heartbeat': str(timeutils.utcnow().isoformat())})
 1938         self.config(fast_track=True, group='deploy')
 1939 
 1940     def test_is_fast_track(self, mock_get_power):
 1941         mock_get_power.return_value = states.POWER_ON
 1942         with task_manager.acquire(
 1943                 self.context, self.node.uuid, shared=False) as task:
 1944             self.assertTrue(conductor_utils.is_fast_track(task))
 1945 
 1946     def test_is_fast_track_config_false(self, mock_get_power):
 1947         self.config(fast_track=False, group='deploy')
 1948         mock_get_power.return_value = states.POWER_ON
 1949         with task_manager.acquire(
 1950                 self.context, self.node.uuid, shared=False) as task:
 1951             self.assertFalse(conductor_utils.is_fast_track(task))
 1952 
 1953     def test_is_fast_track_power_off_false(self, mock_get_power):
 1954         mock_get_power.return_value = states.POWER_OFF
 1955         with task_manager.acquire(
 1956                 self.context, self.node.uuid, shared=False) as task:
 1957             self.assertFalse(conductor_utils.is_fast_track(task))
 1958 
 1959     def test_is_fast_track_no_heartbeat(self, mock_get_power):
 1960         mock_get_power.return_value = states.POWER_ON
 1961         i_info = self.node.driver_internal_info
 1962         i_info.pop('agent_last_heartbeat')
 1963         self.node.driver_internal_info = i_info
 1964         self.node.save()
 1965         with task_manager.acquire(
 1966                 self.context, self.node.uuid, shared=False) as task:
 1967             self.assertFalse(conductor_utils.is_fast_track(task))
 1968 
 1969     def test_is_fast_track_error_blocks(self, mock_get_power):
 1970         mock_get_power.return_value = states.POWER_ON
 1971         self.node.last_error = "bad things happened"
 1972         self.node.save()
 1973         with task_manager.acquire(
 1974                 self.context, self.node.uuid, shared=False) as task:
 1975             self.assertFalse(conductor_utils.is_fast_track(task))
 1976 
 1977 
 1978 class GetNodeNextStepsTestCase(db_base.DbTestCase):
 1979     def setUp(self):
 1980         super(GetNodeNextStepsTestCase, self).setUp()
 1981         self.power_update = {
 1982             'step': 'update_firmware', 'priority': 10, 'interface': 'power'}
 1983         self.deploy_update = {
 1984             'step': 'update_firmware', 'priority': 10, 'interface': 'deploy'}
 1985         self.deploy_erase = {
 1986             'step': 'erase_disks', 'priority': 20, 'interface': 'deploy'}
 1987         # Automated cleaning should be executed in this order
 1988         self.clean_steps = [self.deploy_erase, self.power_update,
 1989                             self.deploy_update]
 1990         self.deploy_start = {
 1991             'step': 'deploy_start', 'priority': 50, 'interface': 'deploy'}
 1992         self.deploy_end = {
 1993             'step': 'deploy_end', 'priority': 20, 'interface': 'deploy'}
 1994         self.deploy_steps = [self.deploy_start, self.deploy_end]
 1995 
 1996     def _test_get_node_next_deploy_steps(self, skip=True):
 1997         driver_internal_info = {'deploy_steps': self.deploy_steps,
 1998                                 'deploy_step_index': 0}
 1999         node = obj_utils.create_test_node(
 2000             self.context, driver='fake-hardware',
 2001             provision_state=states.DEPLOYWAIT,
 2002             target_provision_state=states.ACTIVE,
 2003             driver_internal_info=driver_internal_info,
 2004             last_error=None,
 2005             deploy_step=self.deploy_steps[0])
 2006 
 2007         with task_manager.acquire(self.context, node.uuid) as task:
 2008             step_index = conductor_utils.get_node_next_deploy_steps(
 2009                 task, skip_current_step=skip)
 2010             expected_index = 1 if skip else 0
 2011             self.assertEqual(expected_index, step_index)
 2012 
 2013     def test_get_node_next_deploy_steps(self):
 2014         self._test_get_node_next_deploy_steps()
 2015 
 2016     def test_get_node_next_deploy_steps_no_skip(self):
 2017         self._test_get_node_next_deploy_steps(skip=False)
 2018 
 2019     def test_get_node_next_deploy_steps_unset_deploy_step(self):
 2020         driver_internal_info = {'deploy_steps': self.deploy_steps,
 2021                                 'deploy_step_index': None}
 2022         node = obj_utils.create_test_node(
 2023             self.context, driver='fake-hardware',
 2024             provision_state=states.DEPLOYWAIT,
 2025             target_provision_state=states.ACTIVE,
 2026             driver_internal_info=driver_internal_info,
 2027             last_error=None,
 2028             deploy_step=None)
 2029 
 2030         with task_manager.acquire(self.context, node.uuid) as task:
 2031             step_index = conductor_utils.get_node_next_deploy_steps(task)
 2032             self.assertEqual(0, step_index)
 2033 
 2034     def test_get_node_next_steps_exception(self):
 2035         node = obj_utils.create_test_node(self.context)
 2036 
 2037         with task_manager.acquire(self.context, node.uuid) as task:
 2038             self.assertRaises(exception.Invalid,
 2039                               conductor_utils._get_node_next_steps,
 2040                               task, 'foo')
 2041 
 2042     def _test_get_node_next_clean_steps(self, skip=True):
 2043         driver_internal_info = {'clean_steps': self.clean_steps,
 2044                                 'clean_step_index': 0}
 2045         node = obj_utils.create_test_node(
 2046             self.context, driver='fake-hardware',
 2047             provision_state=states.CLEANWAIT,
 2048             target_provision_state=states.AVAILABLE,
 2049             driver_internal_info=driver_internal_info,
 2050             last_error=None,
 2051             clean_step=self.clean_steps[0])
 2052 
 2053         with task_manager.acquire(self.context, node.uuid) as task:
 2054             step_index = conductor_utils.get_node_next_clean_steps(
 2055                 task, skip_current_step=skip)
 2056             expected_index = 1 if skip else 0
 2057             self.assertEqual(expected_index, step_index)
 2058 
 2059     def test_get_node_next_clean_steps(self):
 2060         self._test_get_node_next_clean_steps()
 2061 
 2062     def test_get_node_next_clean_steps_no_skip(self):
 2063         self._test_get_node_next_clean_steps(skip=False)
 2064 
 2065     def test_get_node_next_clean_steps_unset_clean_step(self):
 2066         driver_internal_info = {'clean_steps': self.clean_steps,
 2067                                 'clean_step_index': None}
 2068         node = obj_utils.create_test_node(
 2069             self.context, driver='fake-hardware',
 2070             provision_state=states.CLEANWAIT,
 2071             target_provision_state=states.AVAILABLE,
 2072             driver_internal_info=driver_internal_info,
 2073             last_error=None,
 2074             clean_step=None)
 2075 
 2076         with task_manager.acquire(self.context, node.uuid) as task:
 2077             step_index = conductor_utils.get_node_next_clean_steps(task)
 2078             self.assertEqual(0, step_index)
 2079 
 2080 
 2081 class AgentTokenUtilsTestCase(tests_base.TestCase):
 2082 
 2083     def setUp(self):
 2084         super(AgentTokenUtilsTestCase, self).setUp()
 2085         self.node = obj_utils.get_test_node(self.context,
 2086                                             driver='fake-hardware')
 2087 
 2088     def test_add_secret_token(self):
 2089         self.assertNotIn('agent_secret_token', self.node.driver_internal_info)
 2090         conductor_utils.add_secret_token(self.node)
 2091         self.assertIn('agent_secret_token', self.node.driver_internal_info)
 2092 
 2093     def test_wipe_deploy_internal_info(self):
 2094         conductor_utils.add_secret_token(self.node)
 2095         self.assertIn('agent_secret_token', self.node.driver_internal_info)
 2096         conductor_utils.wipe_deploy_internal_info(mock.Mock(node=self.node))
 2097         self.assertNotIn('agent_secret_token', self.node.driver_internal_info)
 2098 
 2099     def test_is_agent_token_present(self):
 2100         # This should always be False as the token has not been added yet.
 2101         self.assertFalse(conductor_utils.is_agent_token_present(self.node))
 2102         conductor_utils.add_secret_token(self.node)
 2103         self.assertTrue(conductor_utils.is_agent_token_present(self.node))
 2104 
 2105 
 2106 class GetAttachedVifTestCase(db_base.DbTestCase):
 2107 
 2108     def setUp(self):
 2109         super(GetAttachedVifTestCase, self).setUp()
 2110         self.node = obj_utils.create_test_node(self.context,
 2111                                                driver='fake-hardware')
 2112         self.port = obj_utils.get_test_port(self.context,
 2113                                             node_id=self.node.id)
 2114 
 2115     def test_get_attached_vif_none(self):
 2116         vif, use = conductor_utils.get_attached_vif(self.port)
 2117         self.assertIsNone(vif)
 2118         self.assertIsNone(use)
 2119 
 2120     def test_get_attached_vif_tenant(self):
 2121         self.port.internal_info = {'tenant_vif_port_id': '1'}
 2122         vif, use = conductor_utils.get_attached_vif(self.port)
 2123         self.assertEqual('1', vif)
 2124         self.assertEqual('tenant', use)
 2125 
 2126     def test_get_attached_vif_provisioning(self):
 2127         self.port.internal_info = {'provisioning_vif_port_id': '1'}
 2128         vif, use = conductor_utils.get_attached_vif(self.port)
 2129         self.assertEqual('1', vif)
 2130         self.assertEqual('provisioning', use)
 2131 
 2132     def test_get_attached_vif_cleaning(self):
 2133         self.port.internal_info = {'cleaning_vif_port_id': '1'}
 2134         vif, use = conductor_utils.get_attached_vif(self.port)
 2135         self.assertEqual('1', vif)
 2136         self.assertEqual('cleaning', use)
 2137 
 2138     def test_get_attached_vif_rescuing(self):
 2139         self.port.internal_info = {'rescuing_vif_port_id': '1'}
 2140         vif, use = conductor_utils.get_attached_vif(self.port)
 2141         self.assertEqual('1', vif)
 2142         self.assertEqual('rescuing', use)
 2143 
 2144     def test_get_attached_vif_inspecting(self):
 2145         self.port.internal_info = {'inspection_vif_port_id': '1'}
 2146         vif, use = conductor_utils.get_attached_vif(self.port)
 2147         self.assertEqual('1', vif)
 2148         self.assertEqual('inspecting', use)
 2149 
 2150 
 2151 class StoreAgentCertificateTestCase(db_base.DbTestCase):
 2152 
 2153     def setUp(self):
 2154         super(StoreAgentCertificateTestCase, self).setUp()
 2155         self.node = obj_utils.create_test_node(self.context,
 2156                                                driver='fake-hardware')
 2157         self.tempdir = tempfile.mkdtemp()
 2158         CONF.set_override('certificates_path', self.tempdir, group='agent')
 2159         self.fname = os.path.join(self.tempdir, '%s.crt' % self.node.uuid)
 2160 
 2161     def test_store_new(self):
 2162         result = conductor_utils.store_agent_certificate(self.node,
 2163                                                          'cert text')
 2164         self.assertEqual(self.fname, result)
 2165         with open(self.fname, 'rt') as fp:
 2166             self.assertEqual('cert text', fp.read())
 2167 
 2168     def test_store_existing(self):
 2169         old_fname = os.path.join(self.tempdir, 'old.crt')
 2170         with open(old_fname, 'wt') as fp:
 2171             fp.write('cert text')
 2172 
 2173         self.node.driver_internal_info['agent_verify_ca'] = old_fname
 2174         result = conductor_utils.store_agent_certificate(self.node,
 2175                                                          'cert text')
 2176         self.assertEqual(old_fname, result)
 2177         self.assertFalse(os.path.exists(self.fname))
 2178 
 2179     def test_no_change(self):
 2180         old_fname = os.path.join(self.tempdir, 'old.crt')
 2181         with open(old_fname, 'wt') as fp:
 2182             fp.write('cert text')
 2183 
 2184         self.node.driver_internal_info['agent_verify_ca'] = old_fname
 2185         self.assertRaises(exception.InvalidParameterValue,
 2186                           conductor_utils.store_agent_certificate,
 2187                           self.node, 'new cert text')
 2188         self.assertFalse(os.path.exists(self.fname))
 2189 
 2190     def test_take_over(self):
 2191         old_fname = os.path.join(self.tempdir, 'old.crt')
 2192         self.node.driver_internal_info['agent_verify_ca'] = old_fname
 2193         result = conductor_utils.store_agent_certificate(self.node,
 2194                                                          'cert text')
 2195         self.assertEqual(self.fname, result)
 2196         with open(self.fname, 'rt') as fp:
 2197             self.assertEqual('cert text', fp.read())