Added filter tests to list images tests, addresses lp bug 900088

* Added tests for all filters except by id and changes-since
* Also improved exception handling to include server uuid for troubleshooting

Change-Id: I9a69390e178bb6bb9244f7000a7e01dd3e530e73
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index bcdc758..18f86a7 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -107,6 +107,13 @@
 
         if resp.status in (500, 501):
             body = json.loads(body)
-            raise exceptions.ComputeFault(body['computeFault']['message'])
+            #I'm seeing both computeFault and cloudServersFault come back.
+            #Will file a bug to fix, but leave as is for now.
+
+            if 'cloudServersFault' in body:
+                message = body['cloudServersFault']['message']
+            else:
+                message = body['computeFault']['message']
+            raise exceptions.ComputeFault(message)
 
         return resp, body
diff --git a/tempest/exceptions.py b/tempest/exceptions.py
index 180f13a..16d6af3 100644
--- a/tempest/exceptions.py
+++ b/tempest/exceptions.py
@@ -1,13 +1,19 @@
 class TimeoutException(Exception):
     """Exception on timeout"""
-    def __repr__(self):
-        return "Request timed out"
+    def __init__(self, message):
+        self.message = message
+
+    def __str__(self):
+        return repr(self.message)
 
 
 class BuildErrorException(Exception):
-    """Exception on server build"""
-    def __repr__(self):
-        return "Server failed into error status"
+    """Server entered ERROR status unintentionally"""
+    def __init__(self, message):
+        self.message = message
+
+    def __str__(self):
+        return repr(self.message)
 
 
 class BadRequest(Exception):
diff --git a/tempest/services/nova/json/servers_client.py b/tempest/services/nova/json/servers_client.py
index b3d2ed6..5fac08e 100644
--- a/tempest/services/nova/json/servers_client.py
+++ b/tempest/services/nova/json/servers_client.py
@@ -141,10 +141,13 @@
             server_status = body['status']
 
             if(server_status == 'ERROR'):
-                raise exceptions.BuildErrorException
+                message = 'Server %s entered ERROR status.' % server_id
+                raise exceptions.BuildErrorException(message)
 
             if (int(time.time()) - start >= self.build_timeout):
-                raise exceptions.TimeoutException
+                message = 'Server %s failed to reach ACTIVE status within the \
+                required time (%s s).' % (server_id, self.build_timeout)
+                raise exceptions.TimeoutException(message)
 
     def list_addresses(self, server_id):
         """Lists all addresses for a server"""
diff --git a/tempest/tests/test_list_images.py b/tempest/tests/test_list_images.py
index 0f1f966..d78cf02 100644
--- a/tempest/tests/test_list_images.py
+++ b/tempest/tests/test_list_images.py
@@ -1,8 +1,14 @@
 from nose.plugins.attrib import attr
 from tempest import openstack
+from tempest.common.utils.data_utils import rand_name
 import unittest2 as unittest
 
 
+def _parse_image_id(image_ref):
+    temp = image_ref.rsplit('/')
+    return temp[len(temp) - 1]
+
+
 class ListImagesTest(unittest.TestCase):
 
     @classmethod
@@ -14,6 +20,40 @@
         cls.image_ref = cls.config.env.image_ref
         cls.flavor_ref = cls.config.env.flavor_ref
 
+        name = rand_name('server')
+        resp, cls.server1 = cls.servers_client.create_server(name,
+                                                              cls.image_ref,
+                                                              cls.flavor_ref)
+        cls.servers_client.wait_for_server_status(cls.server1['id'], 'ACTIVE')
+
+        name = rand_name('server')
+        resp, cls.server2 = cls.servers_client.create_server(name,
+                                                              cls.image_ref,
+                                                              cls.flavor_ref)
+        cls.servers_client.wait_for_server_status(cls.server2['id'], 'ACTIVE')
+
+        #Create images to be used in the filter tests
+        image1_name = rand_name('image')
+        resp, body = cls.client.create_image(cls.server1['id'], image1_name)
+        cls.image1_id = _parse_image_id(resp['location'])
+        cls.client.wait_for_image_resp_code(cls.image1_id, 200)
+        cls.client.wait_for_image_status(cls.image1_id, 'ACTIVE')
+        resp, cls.image1 = cls.client.get_image(cls.image1_id)
+
+        image2_name = rand_name('image')
+        resp, body = cls.client.create_image(cls.server1['id'], image2_name)
+        cls.image2_id = _parse_image_id(resp['location'])
+        cls.client.wait_for_image_resp_code(cls.image2_id, 200)
+        cls.client.wait_for_image_status(cls.image2_id, 'ACTIVE')
+        resp, cls.image2 = cls.client.get_image(cls.image2_id)
+
+        image3_name = rand_name('image')
+        resp, body = cls.client.create_image(cls.server2['id'], image3_name)
+        cls.image3_id = _parse_image_id(resp['location'])
+        cls.client.wait_for_image_resp_code(cls.image3_id, 200)
+        cls.client.wait_for_image_status(cls.image3_id, 'ACTIVE')
+        resp, cls.image3 = cls.client.get_image(cls.image3_id)
+
     @attr(type='smoke')
     def test_get_image(self):
         """Returns the correct details for a single image"""
@@ -27,9 +67,159 @@
         found = any([i for i in images if i['id'] == self.image_ref])
         self.assertTrue(found)
 
+    @attr(type='positive')
+    def test_list_images_filter_by_status(self):
+        """
+        The list of images should contain only images with the provided status
+        """
+        params = {'status': 'ACTIVE'}
+        resp, images = self.client.list_images(params)
+
+        self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
+        self.assertTrue(any([i for i in images if i['id'] == self.image2_id]))
+        self.assertTrue(any([i for i in images if i['id'] == self.image3_id]))
+
+    @attr(type='positive')
+    def test_list_images_filter_by_name(self):
+        """
+        List of all images should contain the expected images filtered by name
+        """
+        params = {'name': self.image1['name']}
+        resp, images = self.client.list_images(params)
+
+        self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
+        self.assertFalse(any([i for i in images if i['id'] == self.image2_id]))
+        self.assertFalse(any([i for i in images if i['id'] == self.image3_id]))
+
+    @attr(type='positive')
+    def test_list_images_filter_by_server_id(self):
+        """The images should contain images filtered by server id"""
+        params = {'server': self.server1['id']}
+        resp, images = self.client.list_images(params)
+
+        self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
+        self.assertTrue(any([i for i in images if i['id'] == self.image2_id]))
+        self.assertFalse(any([i for i in images if i['id'] == self.image3_id]))
+
+    @attr(type='positive')
+    def test_list_images_filter_by_server_ref(self):
+        """The list of servers should be filtered by server ref"""
+        params = {'server': self.image3['metadata']['instance_ref']}
+        resp, images = self.client.list_images(params)
+
+        self.assertFalse(any([i for i in images if i['id'] == self.image1_id]))
+        self.assertFalse(any([i for i in images if i['id'] == self.image2_id]))
+        self.assertTrue(any([i for i in images if i['id'] == self.image3_id]))
+
+    @attr(type='positive')
+    def test_list_images_filter_by_type(self):
+        """The list of servers should be filtered by image type"""
+        params = {'type': 'snapshot'}
+        resp, images = self.client.list_images(params)
+
+        self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
+        self.assertTrue(any([i for i in images if i['id'] == self.image2_id]))
+        self.assertTrue(any([i for i in images if i['id'] == self.image3_id]))
+        self.assertFalse(any([i for i in images if i['id'] == self.image_ref]))
+
+    @attr(type='positive')
+    def test_list_images_limit_results(self):
+        """Verify only the expected number of results are returned"""
+        params = {'limit': '1'}
+        resp, images = self.client.list_images(params)
+        self.assertEqual(1, len(images))
+
+    @attr(type='positive')
+    def test_list_images_filter_by_changes_since(self):
+        """Verify only updated images are returned in the detailed list"""
+
+        #Becoming ACTIVE will modify the updated time
+        #Filter by the image's created time
+        params = {'changes-since': self.image3['created']}
+        resp, images = self.client.list_images(params)
+        found = any([i for i in images if i['id'] == self.image3_id])
+        self.assertTrue(found)
+
     @attr(type='smoke')
     def test_list_images_with_detail(self):
         """Detailed list of all images should contain the expected image"""
         resp, images = self.client.list_images_with_detail()
         found = any([i for i in images if i['id'] == self.image_ref])
         self.assertTrue(found)
+
+    @attr(type='smoke')
+    def test_list_images_with_detail(self):
+        """Detailed list of all images should contain the expected images"""
+        resp, images = self.client.list_images_with_detail()
+
+        self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
+        self.assertTrue(any([i for i in images if i['id'] == self.image2_id]))
+        self.assertTrue(any([i for i in images if i['id'] == self.image3_id]))
+
+    @attr(type='positive')
+    def test_list_images_with_detail_filter_by_status(self):
+        """
+        Detailed list of all images should only contain images
+        with the provided status
+        """
+        params = {'status': 'ACTIVE'}
+        resp, images = self.client.list_images_with_detail(params)
+
+        self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
+        self.assertTrue(any([i for i in images if i['id'] == self.image2_id]))
+        self.assertTrue(any([i for i in images if i['id'] == self.image3_id]))
+
+    @attr(type='positive')
+    def test_list_images_with_detail_filter_by_name(self):
+        """
+        Detailed list of all images should contain the expected
+        images filtered by name
+        """
+        params = {'name': self.image1['name']}
+        resp, images = self.client.list_images_with_detail(params)
+
+        self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
+        self.assertFalse(any([i for i in images if i['id'] == self.image2_id]))
+        self.assertFalse(any([i for i in images if i['id'] == self.image3_id]))
+
+    @attr(type='positive')
+    def test_list_images_with_detail_limit_results(self):
+        """
+        Verify only the expected number of results (with full details)
+        are returned
+        """
+        params = {'limit': '1'}
+        resp, images = self.client.list_images_with_detail(params)
+        self.assertEqual(1, len(images))
+
+    @attr(type='positive')
+    def test_list_images_with_detail_filter_by_server_ref(self):
+        """Detailed list of servers should be filtered by server ref"""
+        params = {'server': self.image3['metadata']['instance_ref']}
+        resp, images = self.client.list_images_with_detail(params)
+
+        self.assertFalse(any([i for i in images if i['id'] == self.image1_id]))
+        self.assertFalse(any([i for i in images if i['id'] == self.image2_id]))
+        self.assertTrue(any([i for i in images if i['id'] == self.image3_id]))
+
+    @attr(type='positive')
+    def test_list_images_with_detail_filter_by_type(self):
+        """The detailed list of servers should be filtered by image type"""
+        params = {'type': 'snapshot'}
+        resp, images = self.client.list_images_with_detail(params)
+        resp, image4 = self.client.get_image(self.image_ref)
+
+        self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
+        self.assertTrue(any([i for i in images if i['id'] == self.image2_id]))
+        self.assertTrue(any([i for i in images if i['id'] == self.image3_id]))
+        self.assertFalse(any([i for i in images if i['id'] == self.image_ref]))
+
+    @attr(type='positive')
+    def test_list_images_with_detail_filter_by_changes_since(self):
+        """Verify an update image is returned"""
+
+        #Becoming ACTIVE will modify the updated time
+        #Filter by the image's created time
+        params = {'changes-since': self.image1['created']}
+        resp, images = self.client.list_images_with_detail(params)
+        self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))