baremetal: fail-less mode for wait_for_nodes_provision_state
This change adds a new `fail` flag to the call. If set to False, the call will return a namedtuple (success, failure, timeout) with lists of nodes. Timeout and failure exceptions will not be raised. Change-Id: Ie20193ce51fcd5ce3ffd479143225bd1a1e8c94a
This commit is contained in:
parent
74f74f90d0
commit
00647dbb24
@ -18,3 +18,10 @@ The ``ValidationResult`` class represents the result of a validation.
|
|||||||
|
|
||||||
.. autoclass:: openstack.baremetal.v1.node.ValidationResult
|
.. autoclass:: openstack.baremetal.v1.node.ValidationResult
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
The WaitResult Class
|
||||||
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The ``WaitResult`` class represents the result of waiting for several nodes.
|
||||||
|
|
||||||
|
.. autoclass:: openstack.baremetal.v1.node.WaitResult
|
||||||
|
@ -17,6 +17,7 @@ from openstack.baremetal.v1 import driver as _driver
|
|||||||
from openstack.baremetal.v1 import node as _node
|
from openstack.baremetal.v1 import node as _node
|
||||||
from openstack.baremetal.v1 import port as _port
|
from openstack.baremetal.v1 import port as _port
|
||||||
from openstack.baremetal.v1 import port_group as _portgroup
|
from openstack.baremetal.v1 import port_group as _portgroup
|
||||||
|
from openstack import exceptions
|
||||||
from openstack import proxy
|
from openstack import proxy
|
||||||
from openstack import utils
|
from openstack import utils
|
||||||
|
|
||||||
@ -371,7 +372,8 @@ class Proxy(proxy.Proxy):
|
|||||||
|
|
||||||
def wait_for_nodes_provision_state(self, nodes, expected_state,
|
def wait_for_nodes_provision_state(self, nodes, expected_state,
|
||||||
timeout=None,
|
timeout=None,
|
||||||
abort_on_failed_state=True):
|
abort_on_failed_state=True,
|
||||||
|
fail=True):
|
||||||
"""Wait for the nodes to reach the expected state.
|
"""Wait for the nodes to reach the expected state.
|
||||||
|
|
||||||
:param nodes: List of nodes - name, ID or
|
:param nodes: List of nodes - name, ID or
|
||||||
@ -384,9 +386,13 @@ class Proxy(proxy.Proxy):
|
|||||||
if any node reaches a failure state which does not match the
|
if any node reaches a failure state which does not match the
|
||||||
expected one. Note that the failure state for ``enroll`` ->
|
expected one. Note that the failure state for ``enroll`` ->
|
||||||
``manageable`` transition is ``enroll`` again.
|
``manageable`` transition is ``enroll`` again.
|
||||||
|
:param fail: If set to ``False`` this call will not raise on timeouts
|
||||||
|
and provisioning failures.
|
||||||
|
|
||||||
:return: The list of :class:`~openstack.baremetal.v1.node.Node`
|
:return: If `fail` is ``True`` (the default), the list of
|
||||||
instances that reached the requested state.
|
:class:`~openstack.baremetal.v1.node.Node` instances that reached
|
||||||
|
the requested state. If `fail` is ``False``, a
|
||||||
|
:class:`~openstack.baremetal.v1.node.WaitResult` named tuple.
|
||||||
:raises: :class:`~openstack.exceptions.ResourceFailure` if a node
|
:raises: :class:`~openstack.exceptions.ResourceFailure` if a node
|
||||||
reaches an error state and ``abort_on_failed_state`` is ``True``.
|
reaches an error state and ``abort_on_failed_state`` is ``True``.
|
||||||
:raises: :class:`~openstack.exceptions.ResourceTimeout` on timeout.
|
:raises: :class:`~openstack.exceptions.ResourceTimeout` on timeout.
|
||||||
@ -395,29 +401,45 @@ class Proxy(proxy.Proxy):
|
|||||||
for n in nodes)
|
for n in nodes)
|
||||||
|
|
||||||
finished = []
|
finished = []
|
||||||
|
failed = []
|
||||||
remaining = nodes
|
remaining = nodes
|
||||||
for count in utils.iterate_timeout(
|
try:
|
||||||
timeout,
|
for count in utils.iterate_timeout(
|
||||||
"Timeout waiting for nodes %(nodes)s to reach "
|
timeout,
|
||||||
"target state '%(state)s'" % {'nodes': log_nodes,
|
"Timeout waiting for nodes %(nodes)s to reach "
|
||||||
'state': expected_state}):
|
"target state '%(state)s'" % {'nodes': log_nodes,
|
||||||
nodes = [self.get_node(n) for n in remaining]
|
'state': expected_state}):
|
||||||
remaining = []
|
nodes = [self.get_node(n) for n in remaining]
|
||||||
for n in nodes:
|
remaining = []
|
||||||
if n._check_state_reached(self, expected_state,
|
for n in nodes:
|
||||||
abort_on_failed_state):
|
try:
|
||||||
finished.append(n)
|
if n._check_state_reached(self, expected_state,
|
||||||
else:
|
abort_on_failed_state):
|
||||||
remaining.append(n)
|
finished.append(n)
|
||||||
|
else:
|
||||||
|
remaining.append(n)
|
||||||
|
except exceptions.ResourceFailure:
|
||||||
|
if fail:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
failed.append(n)
|
||||||
|
|
||||||
if not remaining:
|
if not remaining:
|
||||||
return finished
|
if fail:
|
||||||
|
return finished
|
||||||
|
else:
|
||||||
|
return _node.WaitResult(finished, failed, [])
|
||||||
|
|
||||||
self.log.debug(
|
self.log.debug(
|
||||||
'Still waiting for nodes %(nodes)s to reach state '
|
'Still waiting for nodes %(nodes)s to reach state '
|
||||||
'"%(target)s"',
|
'"%(target)s"',
|
||||||
{'nodes': ', '.join(n.id for n in remaining),
|
{'nodes': ', '.join(n.id for n in remaining),
|
||||||
'target': expected_state})
|
'target': expected_state})
|
||||||
|
except exceptions.ResourceTimeout:
|
||||||
|
if fail:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
return _node.WaitResult(finished, failed, remaining)
|
||||||
|
|
||||||
def set_node_power_state(self, node, target):
|
def set_node_power_state(self, node, target):
|
||||||
"""Run an action modifying node's power state.
|
"""Run an action modifying node's power state.
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import collections
|
||||||
|
|
||||||
from openstack.baremetal.v1 import _common
|
from openstack.baremetal.v1 import _common
|
||||||
from openstack import exceptions
|
from openstack import exceptions
|
||||||
from openstack import resource
|
from openstack import resource
|
||||||
@ -30,6 +32,23 @@ class ValidationResult(object):
|
|||||||
self.reason = reason
|
self.reason = reason
|
||||||
|
|
||||||
|
|
||||||
|
class WaitResult(collections.namedtuple('WaitResult',
|
||||||
|
['success', 'failure', 'timeout'])):
|
||||||
|
"""A named tuple representing a result of waiting for several nodes.
|
||||||
|
|
||||||
|
Each component is a list of :class:`~openstack.baremetal.v1.node.Node`
|
||||||
|
objects:
|
||||||
|
|
||||||
|
:ivar ~.success: a list of :class:`~openstack.baremetal.v1.node.Node`
|
||||||
|
objects that reached the state.
|
||||||
|
:ivar ~.timeout: a list of :class:`~openstack.baremetal.v1.node.Node`
|
||||||
|
objects that reached timeout.
|
||||||
|
:ivar ~.failure: a list of :class:`~openstack.baremetal.v1.node.Node`
|
||||||
|
objects that hit a failure.
|
||||||
|
"""
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
|
|
||||||
class Node(_common.ListMixin, resource.Resource):
|
class Node(_common.ListMixin, resource.Resource):
|
||||||
|
|
||||||
resources_key = 'nodes'
|
resources_key = 'nodes'
|
||||||
|
@ -235,6 +235,25 @@ class TestWaitForNodesProvisionState(base.TestCase):
|
|||||||
n._check_state_reached.assert_called_once_with(
|
n._check_state_reached.assert_called_once_with(
|
||||||
self.proxy, 'fake state', True)
|
self.proxy, 'fake state', True)
|
||||||
|
|
||||||
|
def test_success_no_fail(self, mock_get):
|
||||||
|
# two attempts, one node succeeds after the 1st
|
||||||
|
nodes = [mock.Mock(spec=node.Node, id=str(i))
|
||||||
|
for i in range(3)]
|
||||||
|
for i, n in enumerate(nodes):
|
||||||
|
# 1st attempt on 1st node, 2nd attempt on 2nd node
|
||||||
|
n._check_state_reached.return_value = not (i % 2)
|
||||||
|
mock_get.side_effect = nodes
|
||||||
|
|
||||||
|
result = self.proxy.wait_for_nodes_provision_state(
|
||||||
|
['abcd', node.Node(id='1234')], 'fake state', fail=False)
|
||||||
|
self.assertEqual([nodes[0], nodes[2]], result.success)
|
||||||
|
self.assertEqual([], result.failure)
|
||||||
|
self.assertEqual([], result.timeout)
|
||||||
|
|
||||||
|
for n in nodes:
|
||||||
|
n._check_state_reached.assert_called_once_with(
|
||||||
|
self.proxy, 'fake state', True)
|
||||||
|
|
||||||
def test_timeout(self, mock_get):
|
def test_timeout(self, mock_get):
|
||||||
mock_get.return_value._check_state_reached.return_value = False
|
mock_get.return_value._check_state_reached.return_value = False
|
||||||
mock_get.return_value.id = '1234'
|
mock_get.return_value.id = '1234'
|
||||||
@ -245,3 +264,38 @@ class TestWaitForNodesProvisionState(base.TestCase):
|
|||||||
timeout=0.001)
|
timeout=0.001)
|
||||||
mock_get.return_value._check_state_reached.assert_called_with(
|
mock_get.return_value._check_state_reached.assert_called_with(
|
||||||
self.proxy, 'fake state', True)
|
self.proxy, 'fake state', True)
|
||||||
|
|
||||||
|
def test_timeout_no_fail(self, mock_get):
|
||||||
|
mock_get.return_value._check_state_reached.return_value = False
|
||||||
|
mock_get.return_value.id = '1234'
|
||||||
|
|
||||||
|
result = self.proxy.wait_for_nodes_provision_state(
|
||||||
|
['abcd'], 'fake state', timeout=0.001, fail=False)
|
||||||
|
mock_get.return_value._check_state_reached.assert_called_with(
|
||||||
|
self.proxy, 'fake state', True)
|
||||||
|
|
||||||
|
self.assertEqual([], result.success)
|
||||||
|
self.assertEqual([mock_get.return_value], result.timeout)
|
||||||
|
self.assertEqual([], result.failure)
|
||||||
|
|
||||||
|
def test_timeout_and_failures_not_fail(self, mock_get):
|
||||||
|
def _fake_get(_self, uuid):
|
||||||
|
result = mock.Mock()
|
||||||
|
result.id = uuid
|
||||||
|
if uuid == '1':
|
||||||
|
result._check_state_reached.return_value = True
|
||||||
|
elif uuid == '2':
|
||||||
|
result._check_state_reached.side_effect = \
|
||||||
|
exceptions.ResourceFailure("boom")
|
||||||
|
else:
|
||||||
|
result._check_state_reached.return_value = False
|
||||||
|
return result
|
||||||
|
|
||||||
|
mock_get.side_effect = _fake_get
|
||||||
|
|
||||||
|
result = self.proxy.wait_for_nodes_provision_state(
|
||||||
|
['1', '2', '3'], 'fake state', timeout=0.001, fail=False)
|
||||||
|
|
||||||
|
self.assertEqual(['1'], [x.id for x in result.success])
|
||||||
|
self.assertEqual(['3'], [x.id for x in result.timeout])
|
||||||
|
self.assertEqual(['2'], [x.id for x in result.failure])
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Adds an ability for the bare metal ``wait_for_nodes_provision_state`` call
|
||||||
|
to return an object with nodes that succeeded, failed or timed out instead
|
||||||
|
of raising an exception.
|
Loading…
Reference in New Issue
Block a user