From 71e1adeaf07ec1bf7f2e9bdd0c93ec2c90da85f7 Mon Sep 17 00:00:00 2001
From: Mark Vanderwiel <vanderwl@us.ibm.com>
Date: Wed, 18 Nov 2015 16:21:33 -0600
Subject: [PATCH] Allow error status to be specified

For some apis, heat, the error status is "failed". This patch
changes the wait_for_status method to allow for the error
status to be passed in the same way as the success status.

Change-Id: I20db4051d3f5611a4b13fe23ea8798b82a40da81
---
 openstackclient/common/utils.py            |  3 +-
 openstackclient/tests/common/test_utils.py | 40 ++++++++++++++++++++++
 2 files changed, 42 insertions(+), 1 deletion(-)

diff --git a/openstackclient/common/utils.py b/openstackclient/common/utils.py
index 8db4f35bc8..0a1f8e0e79 100644
--- a/openstackclient/common/utils.py
+++ b/openstackclient/common/utils.py
@@ -298,6 +298,7 @@ def wait_for_status(status_f,
                     res_id,
                     status_field='status',
                     success_status=['active'],
+                    error_status=['error'],
                     sleep_time=5,
                     callback=None):
     """Wait for status change on a resource during a long-running operation
@@ -316,7 +317,7 @@ def wait_for_status(status_f,
         if status in success_status:
             retval = True
             break
-        elif status == 'error':
+        elif status in error_status:
             retval = False
             break
         if callback:
diff --git a/openstackclient/tests/common/test_utils.py b/openstackclient/tests/common/test_utils.py
index 373c0de4d8..b564ffab48 100644
--- a/openstackclient/tests/common/test_utils.py
+++ b/openstackclient/tests/common/test_utils.py
@@ -136,6 +136,46 @@ class TestUtils(test_utils.TestCase):
                           utils.sort_items,
                           items, sort_str)
 
+    @mock.patch.object(time, 'sleep')
+    def test_wait_for_status_ok(self, mock_sleep):
+        # Tests the normal flow that the resource is status=active
+        resource = mock.MagicMock(status='ACTIVE')
+        status_f = mock.Mock(return_value=resource)
+        res_id = str(uuid.uuid4())
+        self.assertTrue(utils.wait_for_status(status_f, res_id,))
+        self.assertFalse(mock_sleep.called)
+
+    @mock.patch.object(time, 'sleep')
+    def test_wait_for_status_ok__with_overrides(self, mock_sleep):
+        # Tests the normal flow that the resource is status=complete
+        resource = mock.MagicMock(my_status='COMPLETE')
+        status_f = mock.Mock(return_value=resource)
+        res_id = str(uuid.uuid4())
+        self.assertTrue(utils.wait_for_status(status_f, res_id,
+                                              status_field='my_status',
+                                              success_status=['complete']))
+        self.assertFalse(mock_sleep.called)
+
+    @mock.patch.object(time, 'sleep')
+    def test_wait_for_status_error(self, mock_sleep):
+        # Tests that we fail if the resource is status=error
+        resource = mock.MagicMock(status='ERROR')
+        status_f = mock.Mock(return_value=resource)
+        res_id = str(uuid.uuid4())
+        self.assertFalse(utils.wait_for_status(status_f, res_id))
+        self.assertFalse(mock_sleep.called)
+
+    @mock.patch.object(time, 'sleep')
+    def test_wait_for_status_error_with_overrides(self, mock_sleep):
+        # Tests that we fail if the resource is my_status=failed
+        resource = mock.MagicMock(my_status='FAILED')
+        status_f = mock.Mock(return_value=resource)
+        res_id = str(uuid.uuid4())
+        self.assertFalse(utils.wait_for_status(status_f, res_id,
+                                               status_field='my_status',
+                                               error_status=['failed']))
+        self.assertFalse(mock_sleep.called)
+
     @mock.patch.object(time, 'sleep')
     def test_wait_for_delete_ok(self, mock_sleep):
         # Tests the normal flow that the resource is deleted with a 404 coming