Don't try to update a node in power transition
If a node is registered while it is turned on, it may take some time for Ironic to assert the power state, and while it is doing so it won't allow us to update the node. This can be a problem for the "configure boot" command because it would typically be run right after registration. To avoid this, make sure the node's power state has been updated before trying to do the update. Note that I did not just leave this to the Ironic client retry logic because it results in a large number of warnings logged without any explanation why. Explicitly checking the state allows us to log a more descriptive message. Change-Id: I8c349752aed98645615b06957cbea90b8d63039f
This commit is contained in:
parent
72e760de83
commit
fd55b43964
|
@ -19,3 +19,8 @@
|
|||
class UnsupportedVersion(Exception):
|
||||
"""The user is trying to use an unsupported version of the API"""
|
||||
pass
|
||||
|
||||
|
||||
class Timeout(Exception):
|
||||
"""An operation timed out"""
|
||||
pass
|
||||
|
|
|
@ -19,6 +19,7 @@ import json
|
|||
import mock
|
||||
import os
|
||||
|
||||
from rdomanager_oscplugin import exceptions
|
||||
from rdomanager_oscplugin.tests.v1.baremetal import fakes
|
||||
from rdomanager_oscplugin.v1 import baremetal
|
||||
|
||||
|
@ -351,3 +352,44 @@ class TestConfigureBoot(fakes.TestBaremetal):
|
|||
'path': '/driver_info/deploy_kernel'
|
||||
}])
|
||||
])
|
||||
|
||||
@mock.patch('openstackclient.common.utils.find_resource')
|
||||
@mock.patch.object(baremetal.ConfigureBootPlugin, 'sleep_time',
|
||||
new_callable=mock.PropertyMock,
|
||||
return_value=0)
|
||||
def test_configure_boot_in_transition(self, _, find_resource_mock):
|
||||
find_resource_mock.return_value = mock.Mock(id="IDIDID")
|
||||
|
||||
bm_client = self.app.client_manager.rdomanager_oscplugin.baremetal()
|
||||
bm_client.node.list.return_value = [mock.Mock(uuid="ABCDEFGH",
|
||||
power_state=None),
|
||||
]
|
||||
bm_client.node.get.side_effect = [mock.Mock(uuid="ABCDEFGH",
|
||||
power_state=None),
|
||||
mock.Mock(uuid="ABCDEFGH",
|
||||
power_state='available'),
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, [], [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.assertEqual(1, bm_client.node.list.call_count)
|
||||
self.assertEqual(2, bm_client.node.get.call_count)
|
||||
self.assertEqual(1, bm_client.node.update.call_count)
|
||||
|
||||
@mock.patch('openstackclient.common.utils.find_resource')
|
||||
@mock.patch.object(baremetal.ConfigureBootPlugin, 'sleep_time',
|
||||
new_callable=mock.PropertyMock,
|
||||
return_value=0)
|
||||
def test_configure_boot_timeout(self, _, find_resource_mock):
|
||||
find_resource_mock.return_value = mock.Mock(id="IDIDID")
|
||||
|
||||
bm_client = self.app.client_manager.rdomanager_oscplugin.baremetal()
|
||||
bm_client.node.list.return_value = [mock.Mock(uuid="ABCDEFGH",
|
||||
power_state=None),
|
||||
]
|
||||
bm_client.node.get.return_value = mock.Mock(uuid="ABCDEFGH",
|
||||
power_state=None)
|
||||
parsed_args = self.check_parser(self.cmd, [], [])
|
||||
self.assertRaises(exceptions.Timeout,
|
||||
self.cmd.take_action,
|
||||
parsed_args)
|
||||
|
|
|
@ -20,13 +20,15 @@ import csv
|
|||
import json
|
||||
import logging
|
||||
import sys
|
||||
import time
|
||||
|
||||
from cliff import command
|
||||
from cliff import lister
|
||||
from ironic_discoverd import client as discoverd_client
|
||||
from openstackclient.common import utils
|
||||
from os_cloud_config import nodes
|
||||
|
||||
from cliff import command
|
||||
from rdomanager_oscplugin import exceptions
|
||||
|
||||
|
||||
def _csv_to_nodes_dict(nodes_csv):
|
||||
|
@ -166,6 +168,8 @@ class ConfigureBootPlugin(command.Command):
|
|||
"""Baremetal configure boot plugin"""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ConfigureBootPlugin")
|
||||
loops = 12
|
||||
sleep_time = 10
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
|
||||
|
@ -182,6 +186,24 @@ class ConfigureBootPlugin(command.Command):
|
|||
kernel_id, ramdisk_id))
|
||||
|
||||
for node in bm_client.node.list():
|
||||
# NOTE(bnemec): Ironic won't let us update the node while the
|
||||
# power_state is transitioning.
|
||||
if node.power_state is None:
|
||||
self.log.warning('Node %s power state is in transition. '
|
||||
'Waiting up to %d seconds for it to '
|
||||
'complete.',
|
||||
node.uuid,
|
||||
self.loops * self.sleep_time)
|
||||
for _ in range(self.loops):
|
||||
time.sleep(self.sleep_time)
|
||||
node = bm_client.node.get(node.uuid)
|
||||
if node.power_state is not None:
|
||||
break
|
||||
else:
|
||||
msg = ('Timed out waiting for node %s power state.' %
|
||||
node.uuid)
|
||||
raise exceptions.Timeout(msg)
|
||||
|
||||
self.log.debug("Configuring boot for Node {0}".format(
|
||||
node.uuid))
|
||||
|
||||
|
|
Loading…
Reference in New Issue