Fixes bug/999647. Adds negative tests for LIST and GET servers API

Change-Id: I888d277ebb6ccf1e95fec9f82063237939c01cff
diff --git a/tempest/services/nova/json/servers_client.py b/tempest/services/nova/json/servers_client.py
index 53b2d6f..b732b41 100644
--- a/tempest/services/nova/json/servers_client.py
+++ b/tempest/services/nova/json/servers_client.py
@@ -155,6 +155,24 @@
                 message += ' Current status: %s.' % server_status
                 raise exceptions.TimeoutException(message)
 
+    def wait_for_server_termination(self, server_id):
+        """Waits for server to reach termination"""
+        start_time = int(time.time())
+        while True:
+            try:
+                resp, body = self.get_server(server_id)
+            except exceptions.NotFound:
+                return
+
+            server_status = body['status']
+            if server_status == 'ERROR':
+                raise exceptions.BuildErrorException
+
+            if int(time.time()) - start_time >= self.build_timeout:
+                raise exceptions.TimeoutException
+
+            time.sleep(self.build_interval)
+
     def list_addresses(self, server_id):
         """Lists all addresses for a server"""
         resp, body = self.get("servers/%s/ips" % str(server_id))
diff --git a/tempest/tests/test_list_servers_negative.py b/tempest/tests/test_list_servers_negative.py
new file mode 100644
index 0000000..18030b7
--- /dev/null
+++ b/tempest/tests/test_list_servers_negative.py
@@ -0,0 +1,409 @@
+import re
+import sys
+import unittest2 as unittest
+from tempest import exceptions
+from base_compute_test import BaseComputeTest
+from tempest import openstack
+from tempest.common.utils.data_utils import rand_name
+
+
+class ServerDetailsNegativeTest(BaseComputeTest):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.client = cls.servers_client
+        cls.servers = []
+
+        # Verify the alternate user is configured and not the same as the first
+        cls.user1 = cls.config.compute.username
+        cls.user2 = cls.config.compute.alt_username
+        cls.user2_password = cls.config.compute.alt_password
+        cls.user2_tenant_name = cls.config.compute.alt_tenant_name
+        cls.multi_user = False
+
+        if (not None in (cls.user2, cls.user2_password, cls.user2_tenant_name)
+            and cls.user1 != cls.user2):
+
+            try:
+                cls.alt_manager = openstack.AltManager()
+                cls.alt_client = cls.alt_manager.servers_client
+            except exceptions.AuthenticationFailure:
+                # multi_user is already set to false, just fall through
+                pass
+            else:
+                cls.multi_user = True
+
+    @classmethod
+    def tearDownClass(cls):
+        """Terminate all running instances in nova"""
+        try:
+            resp, body = cls.client.list_servers()
+            for server in body['servers']:
+                resp, body = cls.client.delete_server(server['id'])
+        except exceptions.NotFound:
+            pass
+
+    def tearDown(self):
+        """Terminate instances created by tests"""
+        try:
+            for server in self.servers:
+                resp, body = self.client.delete_server(server)
+                if resp['status'] == '204':
+                    self.client.wait_for_server_termination(server)
+        except exceptions.NotFound:
+            pass
+
+    def get_active_servers(self, server_count):
+        """Returns active test instances to calling test methods"""
+        resp, body = self.client.list_servers_with_detail()
+        servers = body['servers']
+        active_servers = [server for server in servers if server['status'] ==
+                        'ACTIVE']
+        num_of_active_servers = len(active_servers)
+
+        # Check if we already have enough active servers
+        if active_servers and num_of_active_servers >= server_count:
+            return active_servers[0:server_count]
+
+        # Otherwise create the remaining shortfall of servers
+        servers_needed = server_count - num_of_active_servers
+
+        for i in range(0, servers_needed):
+            instance = self.create_instance()
+            active_servers.append(instance)
+
+        return active_servers
+
+    def create_instance(self, image_id=None):
+        name = rand_name('test-vm-')
+        flavor = self.flavor_ref
+
+        if not image_id:
+            image_id = self.image_ref
+
+        body, server = self.client.create_server(name, image_id, flavor)
+        self.client.wait_for_server_status(server['id'], 'ACTIVE')
+        self.servers.append(server['id'])
+        return server
+
+    def test_list_servers_when_no_servers_running(self):
+        """Return an empty list when there are no active servers"""
+        # Delete Active servers
+        try:
+            resp, body = self.client.list_servers()
+            for server in body['servers']:
+                resp, body = self.client.delete_server(server['id'])
+                self.client.wait_for_server_termination(server['id'])
+        except exceptions.NotFound:
+            pass
+        # Verify empty list
+        resp, body = self.client.list_servers()
+        servers = body['servers']
+        self.assertEqual('200', resp['status'])
+        self.assertEqual([], servers)
+
+    def test_list_servers_with_a_deleted_server(self):
+        """Create and delete a server and verify empty list"""
+        server = self.get_active_servers(1)[0]
+
+        # Delete the server
+        self.client.delete_server(server['id'])
+        self.client.wait_for_server_termination(server['id'])
+
+        # List servers and verify server not returned
+        resp, body = self.client.list_servers()
+        servers = body['servers']
+        actual = [srv for srv in servers if srv['id'] == server['id']]
+        self.assertEqual('200', resp['status'])
+        self.assertEqual([], actual)
+
+    def test_list_servers_by_existing_image(self):
+        """Server list is returned for a specific image and snapshot images"""
+        try:
+            base_server = self.get_active_servers(1)[0]
+
+            # Create a snapshot of the server
+            snapshot_name = "%s_snapshot" % base_server['id']
+            resp, body = self.client.create_image(base_server['id'],
+                                                 snapshot_name)
+            snapshot_url = resp['location']
+            match = re.search('/images/(?P<image_id>.+)', snapshot_url)
+            self.assertIsNotNone(match)
+            snapshot_id = match.groupdict()['image_id']
+
+            self.images_client.wait_for_image_status(snapshot_id, 'ACTIVE')
+
+            # Create a server from the snapshot
+            snap_server = self.create_instance(image_id=snapshot_id)
+            self.servers.append(snap_server['id'])
+
+            # List base servers by image
+            resp, body = self.client.list_servers({'image': self.image_ref})
+            servers = body['servers']
+            self.assertEqual('200', resp['status'])
+            self.assertIn(base_server['id'], [server['id'] for server in
+                          servers])
+            self.assertTrue(len(body['servers']) > 0)
+
+            # List snapshotted server by image
+            resp, body = self.client.list_servers({'image': snapshot_id})
+            servers = body['servers']
+            self.assertEqual('200', resp['status'])
+            self.assertIn(snap_server['id'], [server['id'] for server in
+                         servers])
+            self.assertEqual(1, len(body['servers']))
+
+        finally:
+            self.images_client.delete_image(snapshot_id)
+
+    @unittest.skip("Until Bug 1002911 is fixed")
+    def test_list_servers_by_non_existing_image(self):
+        """Listing servers for a non existing image raises error"""
+        self.assertRaises(exceptions.NotFound, self.client.list_servers,
+                         {'image': '1234abcd-zzz0-aaa9-ppp3-0987654abcde'})
+
+    @unittest.skip("Until Bug 1002911 is fixed")
+    def test_list_servers_by_image_pass_overlimit_image(self):
+        """Return an error while listing server with too large image id"""
+        self.assertRaises(exceptions.OverLimit, self.client.list_servers,
+                         {'image': sys.maxint + 1})
+
+    @unittest.skip("Until Bug 1002911 is fixed")
+    def test_list_servers_by_image_pass_negative_id(self):
+        """Return an error while listing server with negative image id"""
+        self.assertRaises(exceptions.BadRequest, self.client.list_servers,
+                         {'image': -1})
+
+    def test_list_servers_by_existing_flavor(self):
+        """List servers by flavor"""
+        self.get_active_servers(1)
+
+        resp, body = self.client.list_servers({'flavor': self.flavor_ref})
+        self.assertEqual('200', resp['status'])
+        self.assertTrue(len(body['servers']) > 0)
+
+    @unittest.skip("Until Bug 1002918 is fixed")
+    def test_list_servers_by_non_existing_flavor(self):
+        """Return an error while listing server by non existing flavor"""
+        self.assertRaises(exceptions.NotFound, self.client.list_servers,
+                        {'flavor': 1234})
+
+    @unittest.skip("Until Bug 1002918 is fixed")
+    def test_list_servers_by_flavor_pass_overlimit_flavor(self):
+        """Return an error while listing server with too large flavor value"""
+        self.assertRaises(exceptions.OverLimit, self.client.list_servers,
+                        {'flavor': sys.maxint + 1})
+
+    @unittest.skip("Until Bug 1002918 is fixed")
+    def test_list_servers_by_flavor_pass_negative_value(self):
+        """Return an error while listing server with negative flavor value"""
+        self.assertRaises(exceptions.BadRequest, self.client.list_servers,
+                         {'flavor': -1})
+
+    def test_list_servers_by_server_name(self):
+        """Returns a list of servers containing an existing server name"""
+        server_name = rand_name('test-vm-')
+        resp, server = self.client.create_server(server_name, self.image_ref,
+                                                self.flavor_ref)
+        self.servers.append(server['id'])
+
+        resp, body = self.client.list_servers({'name': server_name})
+        self.assertEqual('200', resp['status'])
+        self.assertEqual(server_name, body['servers'][0]['name'])
+
+    @unittest.skip("Until Bug 1002892 is fixed")
+    def test_list_servers_by_non_existing_server_name(self):
+        """Return an error while listing for a non existent server"""
+        resp, body = self.client.list_servers({'name': 'junk_serv_100'})
+        self.assertRaises(exceptions.NotFound, self.client.list_servers,
+                          {'name': 'junk_serv_100'})
+
+    @unittest.skip("Until Bug 1002892 is fixed")
+    def test_list_servers_by_server_name_empty(self):
+        """Return an error when an empty server name is passed"""
+        self.assertRaises(exceptions.BadRequest, self.client.list_servers,
+                        {'name': ''})
+
+    @unittest.skip("Until Bug 1002892 is fixed")
+    def test_list_servers_by_server_name_too_large(self):
+        """Return an error for a very large value for server name listing"""
+        self.assertRaises(exceptions.OverLimit, self.client.list_servers,
+                        {'name': 'a' * 65})
+
+    @unittest.skip("Until Bug 1002892 is fixed")
+    def test_list_servers_by_name_pass_numeric_name(self):
+        """Return an error for a numeric server name listing"""
+        self.assertRaises(exceptions.BadRequest, self.client.list_servers,
+                        {'name': 99})
+
+    def test_list_servers_by_active_status(self):
+        """Return a listing of servers by active status"""
+        self.create_instance()
+        resp, body = self.client.list_servers({'status': 'ACTIVE'})
+        self.assertEqual('200', resp['status'])
+        self.assertTrue(len(body['servers']) > 0)
+
+    def test_list_servers_by_building_status(self):
+        """Return a listing of servers in build state"""
+        server_name = rand_name('test-vm-')
+        resp, server = self.client.create_server(server_name, self.image_ref,
+                                                self.flavor_ref)
+        self.servers.append(server['id'])
+        resp, body = self.client.list_servers({'status': 'BUILD'})
+        self.assertEqual('200', resp['status'])
+        self.assertEqual(1, len(body['servers']))
+        self.assertEqual(server['id'], body['servers'][0]['id'])
+
+        self.client.wait_for_server_status(server['id'], 'ACTIVE')
+
+    def test_list_servers_status_is_invalid(self):
+        """Return an error when invalid status is specified"""
+        self.assertRaises(exceptions.BadRequest, self.client.list_servers,
+                          {'status': 'DEAD'})
+
+    def test_list_servers_pass_numeric_status(self):
+        """Return an error when a numeric value for status is specified"""
+        self.assertRaises(exceptions.BadRequest, self.client.list_servers,
+                         {'status': 1})
+
+    def test_list_servers_by_limits(self):
+        """List servers by specifying limits"""
+        self.get_active_servers(2)
+        resp, body = self.client.list_servers({'limit': 1})
+        self.assertEqual('200', resp['status'])
+        self.assertEqual(1, len(body['servers']))
+
+    def test_list_servers_by_limits_greater_than_actual_count(self):
+        """List servers by specifying a greater value for limit"""
+        self.get_active_servers(2)
+        resp, body = self.client.list_servers({'limit': 100})
+        self.assertEqual('200', resp['status'])
+        self.assertTrue(len(body['servers']) >= 2)
+
+    def test_list_servers_by_limits_pass_string(self):
+        """Return an error if a string value is passed for limit"""
+        self.assertRaises(exceptions.BadRequest, self.client.list_servers,
+                         {'limit': 'testing'})
+
+    def test_list_servers_by_limits_pass_negative_value(self):
+        """Return an error if a negative value for limit is passed"""
+        self.assertRaises(exceptions.BadRequest, self.client.list_servers,
+                         {'limit': -1})
+
+    @unittest.skip("Until Bug 1002924 is fixed")
+    def test_list_servers_by_limits_pass_overlimit_value(self):
+        """Return an error if too large value for limit is passed"""
+        self.assertRaises(exceptions.OverLimit, self.client.list_servers,
+                         {'limit': sys.maxint + 1})
+
+    def test_list_servers_by_changes_since(self):
+        """Servers are listed by specifying changes-since date"""
+        self.get_active_servers(2)
+        resp, body = self.client.list_servers(
+                         {'changes-since': '2011-01-01T12:34:00Z'})
+        self.assertEqual('200', resp['status'])
+        self.assertTrue(len(body['servers']) >= 2)
+
+    def test_list_servers_by_changes_since_invalid_date(self):
+        """Return an error when invalid date format is passed"""
+        self.assertRaises(exceptions.BadRequest, self.client.list_servers,
+                          {'changes-since': '2011/01/01'})
+
+    @unittest.skip("Until Bug 1002926 is fixed")
+    def test_list_servers_by_changes_since_future_date(self):
+        """Return an error when a date in the future is passed"""
+        self.assertRaises(exceptions.BadRequest, self.client.list_servers,
+                          {'changes-since': '2051-01-01T12:34:00Z'})
+
+    @unittest.skip("Until Bug 1002935 is fixed")
+    def test_list_servers_list_another_tenant_servers(self):
+        """Return an error when a user lists servers in another tenant"""
+        # Create a server by a user in it's tenant
+        server_name = rand_name('test-vm-')
+        resp, server = self.client.create_server(server_name, self.image_ref,
+                                                 self.flavor_ref)
+        self.servers.append(server['id'])
+
+        # List the servers by alternate user in the base user's tenant
+        self.assertRaises(exceptions.NotFound, self.alt_client.list_servers,
+                         {'name': server_name})
+
+    def test_list_servers_detail_when_no_servers_running(self):
+        """Return an empty list for servers detail when no active servers"""
+        # Delete active servers
+        try:
+            resp, body = self.client.list_servers()
+            for server in body['servers']:
+                resp, body = self.client.delete_server(server['id'])
+                self.client.wait_for_server_termination(server['id'])
+        except exceptions.NotFound:
+            pass
+        # Verify empty list
+        resp, body = self.client.list_servers_with_detail()
+        self.assertEqual('200', resp['status'])
+        servers = body['servers']
+        actual = [srv for srv in servers if srv['status'] == 'ACTIVE']
+        self.assertEqual([], actual)
+
+    def test_list_servers_detail_server_is_building(self):
+        """Server in Build state is listed"""
+        server_name = rand_name('test-vm-')
+        resp, server = self.client.create_server(server_name, self.image_ref,
+                                                 self.flavor_ref)
+
+        self.servers.append(server['id'])
+        resp, body = self.client.list_servers_with_detail()
+        self.assertEqual('200', resp['status'])
+        self.assertEqual('BUILD', body['servers'][0]['status'])
+
+    def test_list_servers_detail_server_is_deleted(self):
+        """Server details are not listed for a deleted server"""
+        server = self.get_active_servers(1)[0]
+
+        self.client.delete_server(server['id'])
+        self.client.wait_for_server_termination(server['id'])
+        resp, body = self.client.list_servers_with_detail()
+        servers = body['servers']
+        actual = [srv for srv in servers if srv['id'] == server['id']]
+        self.assertEqual('200', resp['status'])
+        self.assertEqual([], actual)
+
+    def test_get_server_details_non_existent_id(self):
+        """Return an error during get server details using non-existent id"""
+        self.assertRaises(exceptions.NotFound, self.client.get_server,
+                         'junk-123ab-45cd')
+
+    def test_get_server_details_another_tenant_server(self):
+        """Return an error when querying details of server in another tenant"""
+        server_name = rand_name('test-vm-')
+        resp, server = self.client.create_server(server_name, self.image_ref,
+                                                 self.flavor_ref)
+        self.servers.append(server['id'])
+        self.assertRaises(exceptions.NotFound, self.alt_client.get_server,
+                          server['id'])
+
+    def test_get_server_details_pass_string_uuid(self):
+        """Return an error when a string value is passed for uuid"""
+        try:
+            self.assertRaises(exceptions.NotFound, self.client.get_server,
+                             'junk-server-uuid')
+        except Exception as e:
+            self.fail("Incorrect Exception raised: %s" % e)
+
+    @unittest.skip("Until Bug 1002901 is fixed")
+    def test_get_server_details_pass_negative_uuid(self):
+        """Return an error when a negative value is passed for uuid"""
+        try:
+            self.assertRaises(exceptions.BadRequest, self.client.get_server,
+                             -1)
+        except Exception as e:
+            self.fail("Incorrect Exception raised: %s" % e)
+
+    @unittest.skip("Until Bug 1002901 is fixed")
+    def test_get_server_details_pass_overlimit_length_uuid(self):
+        """Return an error when a very large value is passed for uuid"""
+        try:
+            self.assertRaises(exceptions.OverLimit, self.client.get_server,
+                              'a' * 37)
+        except Exception as e:
+            self.fail("Incorrect Exception raised: %s" % e)