Abort waiting early if reached a failure state

Currently on failure we loop until timeout. This change will allow a run
to fail faster in such case also exposing the error message.

Change-Id: Ibf17d6c02bf835b86167b703ff1fb67fe932477b
diff --git a/ironic_tempest_plugin/common/waiters.py b/ironic_tempest_plugin/common/waiters.py
index 08e0941..554e5f8 100644
--- a/ironic_tempest_plugin/common/waiters.py
+++ b/ironic_tempest_plugin/common/waiters.py
@@ -45,7 +45,7 @@
 
 
 def wait_for_bm_node_status(client, node_id, attr, status, timeout=None,
-                            interval=None):
+                            interval=None, abort_on_error_state=False):
     """Waits for a baremetal node attribute to reach given status.
 
     :param client: an instance of tempest plugin BaremetalClient.
@@ -56,6 +56,8 @@
         Defaults to client.build_timeout.
     :param interval: an interval between show_node calls for status check.
         Defaults to client.build_interval.
+    :param abort_on_error_state: whether to abort waiting if the node reaches
+        an error state.
 
     The client should have a show_node(node_id) method to get the node.
     """
@@ -69,6 +71,14 @@
         node = utils.get_node(client, node_id=node_id)
         if node[attr] in status:
             return True
+        elif (abort_on_error_state
+              and node['provision_state'].endswith(' failed')):
+            raise lib_exc.TempestException(
+                'Node %(node)s reached failure state %(state)s while waiting '
+                'for %(attr)s=%(expected)s. Error: %(error)s' %
+                {'node': node_id, 'state': node['provision_state'],
+                 'attr': attr, 'expected': status,
+                 'error': node.get('last_error')})
         return False
 
     if not test_utils.call_until_true(is_attr_in_status, timeout,
diff --git a/ironic_tempest_plugin/tests/scenario/baremetal_manager.py b/ironic_tempest_plugin/tests/scenario/baremetal_manager.py
index 6d7d55b..1788463 100644
--- a/ironic_tempest_plugin/tests/scenario/baremetal_manager.py
+++ b/ironic_tempest_plugin/tests/scenario/baremetal_manager.py
@@ -104,10 +104,12 @@
         cls.baremetal_client.list_nodes()
 
     @classmethod
-    def wait_provisioning_state(cls, node_id, state, timeout=10, interval=1):
+    def wait_provisioning_state(cls, node_id, state, timeout=10, interval=1,
+                                abort_on_error_state=True):
         ironic_waiters.wait_for_bm_node_status(
             cls.baremetal_client, node_id=node_id, attr='provision_state',
-            status=state, timeout=timeout, interval=interval)
+            status=state, timeout=timeout, interval=interval,
+            abort_on_error_state=abort_on_error_state)
 
     @classmethod
     def wait_power_state(cls, node_id, state):