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:
Ben Nemec 2015-04-28 11:26:24 -05:00
parent 72e760de83
commit fd55b43964
3 changed files with 70 additions and 1 deletions

View File

@ -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

View File

@ -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)

View File

@ -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))