boto: instance teardown wait until instance is gone

Instance teardown will wait until the instance not listed or
the absence reported as an error.

Bug: #1098112

Change-Id: Ib863995fcac50e6849ce6633b5aad828675e0504
diff --git a/tempest/testboto.py b/tempest/testboto.py
index c38bf99..09ac950 100644
--- a/tempest/testboto.py
+++ b/tempest/testboto.py
@@ -22,6 +22,7 @@
 
 import boto
 from boto.exception import BotoServerError
+from boto.exception import EC2ResponseError
 from boto.s3.bucket import Bucket
 from boto.s3.key import Key
 import nose
@@ -203,26 +204,32 @@
                                'deleting', 'deleted', 'error'))
     valid_snapshot_status = set(('pending', 'completed', 'error'))
 
-    #TODO(afazekas): object base version for resurces supports update
-    def waitImageState(self, lfunction, wait_for):
-        state = state_wait(lfunction, wait_for, self.valid_image_state)
-        self.assertIn(state, self.valid_image_state)
+    gone_set = set(('_GONE',))
+
+    def state_wait_gone(self, lfunction, final_set, valid_set):
+        if not isinstance(final_set, set):
+            final_set = set((final_set,))
+        final_set |= self.gone_set
+        state = state_wait(lfunction, final_set, valid_set)
+        self.assertIn(state, valid_set | self.gone_set)
         return state
 
+    #TODO(afazekas): object based versions for resurces which supports update
+    def waitImageState(self, lfunction, wait_for):
+        return self.state_wait_gone(lfunction, wait_for,
+                                    self.valid_image_state)
+
     def waitInstanceState(self, lfunction, wait_for):
-        state = state_wait(lfunction, wait_for, self.valid_instance_state)
-        self.assertIn(state, self.valid_instance_state)
-        return state
+        return self.state_wait_gone(lfunction, wait_for,
+                                    self.valid_instance_state)
 
     def waitVolumeStatus(self, lfunction, wait_for):
-        state = state_wait(lfunction, wait_for, self.valid_volume_status)
-        self.assertIn(state, self.valid_volume_status)
-        return state
+        return self.state_wait_gone(lfunction, wait_for,
+                                    self.valid_volume_status)
 
     def waitSnapshotStatus(self, lfunction, wait_for):
-        state = state_wait(lfunction, wait_for, self.valid_snapshot_status)
-        self.assertIn(state, self.valid_snapshot_status)
-        return state
+        return self.state_wait_gone(lfunction, wait_for,
+                                    self.valid_snapshot_status)
 
     def assertImageStateWait(self, lfunction, wait_for):
         state = self.waitImageState(lfunction, wait_for)
@@ -323,13 +330,22 @@
             try:
                 instance.update(validate=True)
             except ValueError:
-                return "terminated"
+                return "_GONE"
+            except EC2ResponseError as exc:
+                if cls.ec2_error_code.\
+                client.InvalidInstanceID.NotFound.match(exc):
+                    return "_GONE"
+                #NOTE(afazekas): incorrect code,
+                # but the resource must be destoreyd
+                if exc.error_code == "InstanceNotFound":
+                    return "_GONE"
+
             return instance.state
 
         for instance in reservation.instances:
             try:
                 instance.terminate()
-                re_search_wait(_instance_state, "terminated")
+                re_search_wait(_instance_state, "_GONE")
             except BaseException as exc:
                 LOG.exception(exc)
                 exc_num += 1
@@ -345,8 +361,6 @@
            Use just for teardown!
         """
         #NOTE(afazekas): should wait/try until all related instance terminates
-        #2.   looks like it is locked even if the instance not listed
-        time.sleep(1)
         group.delete()
 
     @classmethod
diff --git a/tempest/tests/boto/test_ec2_instance_run.py b/tempest/tests/boto/test_ec2_instance_run.py
index 331e54c..6a8778a 100644
--- a/tempest/tests/boto/test_ec2_instance_run.py
+++ b/tempest/tests/boto/test_ec2_instance_run.py
@@ -18,6 +18,7 @@
 from contextlib import closing
 import logging
 
+from boto.exception import EC2ResponseError
 from boto.s3.key import Key
 import nose
 from nose.plugins.attrib import attr
@@ -121,7 +122,7 @@
         self.cancelResourceCleanUp(rcuk)
 
     @attr(type='smoke')
-    @unittest.skip("Skipped until the Bug #1098112 is resolved")
+    @unittest.skip("Skipped until the Bug #1098891 is resolved")
     def test_run_terminate_instance(self):
         # EC2 run, terminate immediately
         image_ami = self.ec2_client.get_image(self.images["ami"]
@@ -132,9 +133,18 @@
 
         for instance in reservation.instances:
             instance.terminate()
-
-        instance.update(validate=True)
-        self.assertNotEqual(instance.state, "running")
+        try:
+            instance.update(validate=True)
+        except ValueError:
+            pass
+        except EC2ResponseError as exc:
+            if self.ec2_error_code.\
+                client.InvalidInstanceID.NotFound.match(exc):
+                pass
+            else:
+                raise
+        else:
+            self.assertNotEqual(instance.state, "running")
 
     #NOTE(afazekas): doctored test case,
     # with normal validation it would fail