Merge "Add server to clean up even in case of errors"
diff --git a/.gitignore b/.gitignore
index 8d2b281..28a9b9c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,5 +15,5 @@
 dist
 build
 .testrepository
-.coverage
+.coverage*
 cover/
diff --git a/etc/whitelist.yaml b/etc/whitelist.yaml
deleted file mode 100644
index 2d8b741..0000000
--- a/etc/whitelist.yaml
+++ /dev/null
@@ -1,233 +0,0 @@
-n-cpu:
-    - module: "nova.virt.libvirt.driver"
-      message: "During wait destroy, instance disappeared"
-    - module: "glanceclient.common.http"
-      message: "Request returned failure status"
-    - module: "nova.openstack.common.periodic_task"
-      message: "Error during ComputeManager\\.update_available_resource: \
-        'NoneType' object is not iterable"
-    - module: "nova.compute.manager"
-      message: "Possibly task preempted"
-    - module: "nova.openstack.common.rpc.amqp"
-      message: "Exception during message handling"
-    - module: "nova.network.api"
-      message: "Failed storing info cache"
-    - module: "nova.compute.manager"
-      message: "Error while trying to clean up image"
-    - module: "nova.virt.libvirt.driver"
-      message: "Error injecting data into image.*\\(Unexpected error while \
-        running command"
-    - module: "nova.compute.manager"
-      message: "Instance failed to spawn"
-    - module: "nova.compute.manager"
-      message: "Error: Unexpected error while running command"
-    - module: "nova.virt.libvirt.driver"
-      message: "Error from libvirt during destroy"
-    - module: "nova.virt.libvirt.vif"
-      message: "Failed while unplugging vif"
-    - module: "nova.openstack.common.loopingcal"
-      message: "in fixed duration looping call"
-    - module: "nova.virt.libvirt.driver"
-      message: "Getting disk size of instance"
-    - module: "nova.virt.libvirt.driver"
-      message: "No such file or directory: '/opt/stack/data/nova/instances"
-    - module: "nova.virt.libvirt.driver"
-      message: "Nova requires libvirt version 0\\.9\\.11 or greater"
-    - module: "nova.compute.manager"
-      message: "error during stop\\(\\) in sync_power_state"
-    - module: "nova.compute.manager"
-      message: "Instance failed network setup after 1 attempt"
-    - module: "nova.compute.manager"
-      message: "Periodic sync_power_state task had an error"
-    - module: "nova.virt.driver"
-      message: "Info cache for instance .* could not be found"
-
-g-api:
-    - module: "glance.store.sheepdog"
-      message: "Error in store configuration: Unexpected error while \
-        running command"
-    - module: "swiftclient"
-      message: "Container HEAD failed: .*404 Not Found"
-    - module: "glance.api.middleware.cache"
-      message: "however the registry did not contain metadata for that image"
-    - module: "oslo.messaging.notify._impl_messaging"
-      message: ".*"
-
-ceilometer-acompute:
-    - module: "ceilometer.compute.pollsters.disk"
-      message: "Unable to read from monitor: Connection reset by peer"
-    - module: "ceilometer.compute.pollsters.disk"
-      message: "Requested operation is not valid: domain is not running"
-    - module: "ceilometer.compute.pollsters.net"
-      message: "Requested operation is not valid: domain is not running"
-    - module: "ceilometer.compute.pollsters.disk"
-      message: "Domain not found: no domain with matching uuid"
-    - module: "ceilometer.compute.pollsters.net"
-      message: "Domain not found: no domain with matching uuid"
-    - module: "ceilometer.compute.pollsters.net"
-      message: "No module named libvirt"
-    - module: "ceilometer.compute.pollsters.net"
-      message: "Unable to write to monitor: Broken pipe"
-    - module: "ceilometer.compute.pollsters.cpu"
-      message: "Domain not found: no domain with matching uuid"
-    - module: "ceilometer.compute.pollsters.net"
-      message: ".*"
-    - module: "ceilometer.compute.pollsters.disk"
-      message: ".*"
-
-ceilometer-acentral:
-    - module: "ceilometer.central.manager"
-      message: "403 Forbidden"
-    - module: "ceilometer.central.manager"
-      message: "get_samples\\(\\) got an unexpected keyword argument 'resources'"
-
-ceilometer-alarm-evaluator:
-    - module: "ceilometer.alarm.service"
-      message: "alarm evaluation cycle failed"
-    - module: "ceilometer.alarm.evaluator.threshold"
-      message: ".*"
-
-ceilometer-api:
-    - module: "wsme.api"
-      message: ".*"
-
-h-api:
-    - module: "root"
-      message: "Returning 400 to user: The server could not comply with \
-        the request since it is either malformed or otherwise incorrect"
-    - module: "root"
-      message: "Unexpected error occurred serving API: Request limit \
-        exceeded: Template exceeds maximum allowed size"
-    - module: "root"
-      message: "Unexpected error occurred serving API: The Stack \
-        .*could not be found"
-
-h-eng:
-    - module: "heat.openstack.common.rpc.amqp"
-      message: "Exception during message handling"
-    - module: "heat.openstack.common.rpc.common"
-      message: "The Stack .* could not be found"
-
-n-api:
-    - module: "glanceclient.common.http"
-      message: "Request returned failure status"
-    - module: "nova.api.openstack"
-      message: "Caught error: Quota exceeded for"
-    - module: "nova.compute.api"
-      message: "ServerDiskConfigTest"
-    - module: "nova.compute.api"
-      message: "ServersTest"
-    - module: "nova.compute.api"
-      message: "\\{u'kernel_id'.*u'ramdisk_id':"
-    - module: "nova.api.openstack.wsgi"
-      message: "takes exactly 4 arguments"
-    - module: "nova.api.openstack"
-      message: "Caught error: Instance .* could not be found"
-    - module: "nova.api.metadata.handler"
-      message: "Failed to get metadata for instance id:"
-
-n-cond:
-    - module: "nova.notifications"
-      message: "Failed to send state update notification"
-    - module: "nova.openstack.common.rpc.amqp"
-      message: "Exception during message handling"
-    - module: "nova.openstack.common.rpc.common"
-      message: "but the actual state is deleting to caller"
-    - module: "nova.openstack.common.rpc.common"
-      message: "Traceback \\(most recent call last"
-    - module: "nova.openstack.common.threadgroup"
-      message: "Service with host .* topic conductor exists."
-
-n-sch:
-    - module: "nova.scheduler.filter_scheduler"
-      message: "Error from last host: "
-
-n-net:
-    - module: "nova.openstack.common.rpc.amqp"
-      message: "Exception during message handling"
-    - module: "nova.openstack.common.rpc.common"
-      message: "'NoneType' object has no attribute '__getitem__'"
-    - module: "nova.openstack.common.rpc.common"
-      message: "Instance .* could not be found"
-
-c-api:
-    - module: "cinder.api.middleware.fault"
-      message: "Caught error: Volume .* could not be found"
-    - module: "cinder.api.middleware.fault"
-      message: "Caught error: Snapshot .* could not be found"
-    - module: "cinder.api.openstack.wsgi"
-      message: "argument must be a string or a number, not 'NoneType'"
-    - module: "cinder.volume.api"
-      message: "Volume status must be available to reserve"
-
-c-vol:
-    - module: "cinder.brick.iscsi.iscsi"
-      message: "Failed to create iscsi target for volume id"
-    - module: "cinder.brick.local_dev.lvm"
-      message: "stat failed: No such file or directory"
-    - module: "cinder.brick.local_dev.lvm"
-      message: "LV stack-volumes.*in use: not deactivating"
-    - module: "cinder.brick.local_dev.lvm"
-      message: "Can't remove open logical volume"
-
-ceilometer-collector:
-    - module: "stevedore.extension"
-      message: ".*"
-    - module: "ceilometer.collector.dispatcher.database"
-      message: "duplicate key value violates unique constraint"
-    - module: "ceilometer.collector.dispatcher.database"
-      message: "Failed to record metering data: QueuePool limit"
-    - module: "ceilometer.dispatcher.database"
-      message: "\\(DataError\\) integer out of range"
-    - module: "ceilometer.collector.dispatcher.database"
-      message: "Failed to record metering data: .* integer out of range"
-    - module: "ceilometer.collector.dispatcher.database"
-      message: "Failed to record metering data: .* integer out of range"
-    - module: "ceilometer.openstack.common.db.sqlalchemy.session"
-      message: "DB exception wrapped"
-
-q-agt:
-    - module: "neutron.agent.linux.ovs_lib"
-      message: "Unable to execute.*Exception:"
-
-q-dhcp:
-    - module: "neutron.common.legacy"
-      message: "Skipping unknown group key: firewall_driver"
-    - module: "neutron.agent.dhcp_agent"
-      message: "Unable to enable dhcp"
-    - module: "neutron.agent.dhcp_agent"
-      message: "Network .* RPC info call failed"
-
-q-l3:
-    - module: "neutron.common.legacy"
-      message: "Skipping unknown group key: firewall_driver"
-    - module: "neutron.agent.l3_agent"
-      message: "Failed synchronizing routers"
-
-q-vpn:
-    - module: "neutron.common.legacy"
-      message: "Skipping unknown group key: firewall_driver"
-
-q-lbaas:
-    - module: "neutron.common.legacy"
-      message: "Skipping unknown group key: firewall_driver"
-    - module: "neutron.services.loadbalancer.drivers.haproxy.agent_manager"
-      message: "Error upating stats"
-    - module: "neutron.services.loadbalancer.drivers.haproxy.agent_manager"
-      message: "Unable to destroy device for pool"
-
-q-svc:
-    - module: "neutron.common.legacy"
-      message: "Skipping unknown group key: firewall_driver"
-    - module: "neutron.openstack.common.rpc.amqp"
-      message: "Exception during message handling"
-    - module: "neutron.openstack.common.rpc.common"
-      message: "(Network|Pool|Subnet|Agent|Port) .* could not be found"
-    - module: "neutron.api.v2.resource"
-      message: ".* failed"
-    - module: ".*"
-      message: ".*"
-
-s-proxy:
-    - module: "proxy-server"
-      message: "Timeout talking to memcached"
diff --git a/tempest/api/compute/admin/test_servers.py b/tempest/api/compute/admin/test_servers.py
index 40a4df7..77431bb 100644
--- a/tempest/api/compute/admin/test_servers.py
+++ b/tempest/api/compute/admin/test_servers.py
@@ -27,6 +27,7 @@
     _host_key = 'OS-EXT-SRV-ATTR:host'
 
     @classmethod
+    @test.safe_setup
     def setUpClass(cls):
         super(ServersAdminTestJSON, cls).setUpClass()
         cls.client = cls.os_adm.servers_client
diff --git a/tempest/api/compute/servers/test_list_servers_negative.py b/tempest/api/compute/servers/test_list_servers_negative.py
index c825fb9..768cc11 100644
--- a/tempest/api/compute/servers/test_list_servers_negative.py
+++ b/tempest/api/compute/servers/test_list_servers_negative.py
@@ -26,6 +26,7 @@
     force_tenant_isolation = True
 
     @classmethod
+    @test.safe_setup
     def setUpClass(cls):
         super(ListServersNegativeTestJSON, cls).setUpClass()
         cls.client = cls.servers_client
diff --git a/tempest/api/compute/servers/test_server_rescue.py b/tempest/api/compute/servers/test_server_rescue.py
index 48f2e14..093e9e2 100644
--- a/tempest/api/compute/servers/test_server_rescue.py
+++ b/tempest/api/compute/servers/test_server_rescue.py
@@ -21,6 +21,7 @@
 class ServerRescueTestJSON(base.BaseV2ComputeTest):
 
     @classmethod
+    @test.safe_setup
     def setUpClass(cls):
         cls.set_network_resources(network=True, subnet=True, router=True)
         super(ServerRescueTestJSON, cls).setUpClass()
diff --git a/tempest/api/compute/servers/test_server_rescue_negative.py b/tempest/api/compute/servers/test_server_rescue_negative.py
index e027567..ef45585 100644
--- a/tempest/api/compute/servers/test_server_rescue_negative.py
+++ b/tempest/api/compute/servers/test_server_rescue_negative.py
@@ -22,6 +22,7 @@
 class ServerRescueNegativeTestJSON(base.BaseV2ComputeTest):
 
     @classmethod
+    @test.safe_setup
     def setUpClass(cls):
         cls.set_network_resources(network=True, subnet=True, router=True)
         super(ServerRescueNegativeTestJSON, cls).setUpClass()
diff --git a/tempest/api/compute/v3/admin/test_servers.py b/tempest/api/compute/v3/admin/test_servers.py
index fb8afe4..579a535 100644
--- a/tempest/api/compute/v3/admin/test_servers.py
+++ b/tempest/api/compute/v3/admin/test_servers.py
@@ -27,6 +27,7 @@
     _host_key = 'os-extended-server-attributes:host'
 
     @classmethod
+    @test.safe_setup
     def setUpClass(cls):
         super(ServersAdminV3Test, cls).setUpClass()
         cls.client = cls.servers_admin_client
diff --git a/tempest/api/compute/v3/servers/test_list_servers_negative.py b/tempest/api/compute/v3/servers/test_list_servers_negative.py
index 92f44fe..9cbc4e0 100644
--- a/tempest/api/compute/v3/servers/test_list_servers_negative.py
+++ b/tempest/api/compute/v3/servers/test_list_servers_negative.py
@@ -19,13 +19,14 @@
 
 from tempest.api.compute import base
 from tempest import exceptions
-from tempest.test import attr
+from tempest import test
 
 
 class ListServersNegativeV3Test(base.BaseV3ComputeTest):
     force_tenant_isolation = True
 
     @classmethod
+    @test.safe_setup
     def setUpClass(cls):
         super(ListServersNegativeV3Test, cls).setUpClass()
         cls.client = cls.servers_client
@@ -51,7 +52,7 @@
                                                ignore_error=True)
         cls.deleted_fixtures.append(srv)
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_list_servers_with_a_deleted_server(self):
         # Verify deleted servers do not show by default in list servers
         # List servers and verify server not returned
@@ -63,7 +64,7 @@
         self.assertEqual('200', resp['status'])
         self.assertEqual([], actual)
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_list_servers_by_non_existing_image(self):
         # Listing servers for a non existing image returns empty list
         non_existing_image = '1234abcd-zzz0-aaa9-ppp3-0987654abcde'
@@ -72,7 +73,7 @@
         self.assertEqual('200', resp['status'])
         self.assertEqual([], servers)
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_list_servers_by_non_existing_flavor(self):
         # Listing servers by non existing flavor returns empty list
         non_existing_flavor = 1234
@@ -81,7 +82,7 @@
         self.assertEqual('200', resp['status'])
         self.assertEqual([], servers)
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_list_servers_by_non_existing_server_name(self):
         # Listing servers for a non existent server name returns empty list
         non_existing_name = 'junk_server_1234'
@@ -90,7 +91,7 @@
         self.assertEqual('200', resp['status'])
         self.assertEqual([], servers)
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_list_servers_status_non_existing(self):
         # Return an empty list when invalid status is specified
         non_existing_status = 'BALONEY'
@@ -99,33 +100,33 @@
         self.assertEqual('200', resp['status'])
         self.assertEqual([], servers)
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_list_servers_by_limits(self):
         # List servers by specifying limits
         resp, body = self.client.list_servers({'limit': 1})
         self.assertEqual('200', resp['status'])
         self.assertEqual(1, len([x for x in body['servers'] if 'id' in x]))
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_list_servers_by_limits_greater_than_actual_count(self):
         # List servers by specifying a greater value for limit
         resp, body = self.client.list_servers({'limit': 100})
         self.assertEqual('200', resp['status'])
         self.assertEqual(len(self.existing_fixtures), len(body['servers']))
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     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'})
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     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})
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_list_servers_by_changes_since(self):
         # Servers are listed by specifying changes-since date
         changes_since = {'changes_since': self.start_time.isoformat()}
@@ -138,13 +139,13 @@
                          "Number of servers %d is wrong in %s" %
                          (num_expected, body['servers']))
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     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'})
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_list_servers_by_changes_since_future_date(self):
         # Return an empty list when a date in the future is passed
         changes_since = {'changes_since': '2051-01-01T12:34:00Z'}
@@ -152,7 +153,7 @@
         self.assertEqual('200', resp['status'])
         self.assertEqual(0, len(body['servers']))
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_list_servers_detail_server_is_deleted(self):
         # Server details are not listed for a deleted server
         deleted_ids = [s['id'] for s in self.deleted_fixtures]
diff --git a/tempest/api/compute/v3/servers/test_server_rescue_negative.py b/tempest/api/compute/v3/servers/test_server_rescue_negative.py
index 6e09376..6bb441c 100644
--- a/tempest/api/compute/v3/servers/test_server_rescue_negative.py
+++ b/tempest/api/compute/v3/servers/test_server_rescue_negative.py
@@ -22,6 +22,7 @@
 class ServerRescueNegativeV3Test(base.BaseV3ComputeTest):
 
     @classmethod
+    @test.safe_setup
     def setUpClass(cls):
         super(ServerRescueNegativeV3Test, cls).setUpClass()
         cls.device = 'vdf'
diff --git a/tempest/api_schema/compute/v2/limits.py b/tempest/api_schema/compute/v2/limits.py
new file mode 100644
index 0000000..b9857f1
--- /dev/null
+++ b/tempest/api_schema/compute/v2/limits.py
@@ -0,0 +1,94 @@
+# Copyright 2014 NEC Corporation.  All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+get_limit = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'limits': {
+                'type': 'object',
+                'properties': {
+                    'absolute': {
+                        'type': 'object',
+                        'properties': {
+                            'maxTotalRAMSize': {'type': 'integer'},
+                            'totalCoresUsed': {'type': 'integer'},
+                            'maxTotalInstances': {'type': 'integer'},
+                            'maxTotalFloatingIps': {'type': 'integer'},
+                            'totalSecurityGroupsUsed': {'type': 'integer'},
+                            'maxTotalCores': {'type': 'integer'},
+                            'totalFloatingIpsUsed': {'type': 'integer'},
+                            'maxSecurityGroups': {'type': 'integer'},
+                            'maxServerMeta': {'type': 'integer'},
+                            'maxPersonality': {'type': 'integer'},
+                            'maxImageMeta': {'type': 'integer'},
+                            'maxPersonalitySize': {'type': 'integer'},
+                            'maxSecurityGroupRules': {'type': 'integer'},
+                            'maxTotalKeypairs': {'type': 'integer'},
+                            'totalRAMUsed': {'type': 'integer'},
+                            'totalInstancesUsed': {'type': 'integer'}
+                        },
+                        'required': ['maxImageMeta',
+                                     'maxPersonality',
+                                     'maxPersonalitySize',
+                                     'maxSecurityGroupRules',
+                                     'maxSecurityGroups',
+                                     'maxServerMeta',
+                                     'maxTotalCores',
+                                     'maxTotalFloatingIps',
+                                     'maxTotalInstances',
+                                     'maxTotalKeypairs',
+                                     'maxTotalRAMSize',
+                                     'totalCoresUsed',
+                                     'totalFloatingIpsUsed',
+                                     'totalInstancesUsed',
+                                     'totalRAMUsed',
+                                     'totalSecurityGroupsUsed']
+                    },
+                    'rate': {
+                        'type': 'array',
+                        'items': {
+                            'type': 'object',
+                            'properties': {
+                                'limit': {
+                                    'type': 'array',
+                                    'items': {
+                                        'type': 'object',
+                                        'properties': {
+                                            'next-available':
+                                                {'type': 'string'},
+                                            'remaining':
+                                                {'type': 'integer'},
+                                            'unit':
+                                                {'type': 'string'},
+                                            'value':
+                                                {'type': 'integer'},
+                                            'verb':
+                                                {'type': 'string'}
+                                        }
+                                    }
+                                },
+                                'regex': {'type': 'string'},
+                                'uri': {'type': 'string'}
+                            }
+                        }
+                    }
+                },
+                'required': ['absolute', 'rate']
+            }
+        },
+        'required': ['limits']
+    }
+}
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index 55be60a..7f39905 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -15,7 +15,9 @@
 import re
 
 
-PYTHON_CLIENTS = ['cinder', 'glance', 'keystone', 'nova', 'swift', 'neutron']
+PYTHON_CLIENTS = ['cinder', 'glance', 'keystone', 'nova', 'swift', 'neutron',
+                  'trove', 'ironic', 'savanna', 'heat', 'ceilometer',
+                  'marconi']
 
 PYTHON_CLIENT_RE = re.compile('import (%s)client' % '|'.join(PYTHON_CLIENTS))
 TEST_DEFINITION = re.compile(r'^\s*def test.*')
diff --git a/tempest/services/compute/json/limits_client.py b/tempest/services/compute/json/limits_client.py
index 1493718..e503bef 100644
--- a/tempest/services/compute/json/limits_client.py
+++ b/tempest/services/compute/json/limits_client.py
@@ -15,6 +15,7 @@
 
 import json
 
+from tempest.api_schema.compute.v2 import limits as schema
 from tempest.common import rest_client
 from tempest import config
 
@@ -30,11 +31,13 @@
     def get_absolute_limits(self):
         resp, body = self.get("limits")
         body = json.loads(body)
+        self.validate_response(schema.get_limit, resp, body)
         return resp, body['limits']['absolute']
 
     def get_specific_absolute_limit(self, absolute_limit):
         resp, body = self.get("limits")
         body = json.loads(body)
+        self.validate_response(schema.get_limit, resp, body)
         if absolute_limit not in body['limits']['absolute']:
             return None
         else:
diff --git a/tools/check_logs.py b/tools/check_logs.py
index edf95a1..e28c230 100755
--- a/tools/check_logs.py
+++ b/tools/check_logs.py
@@ -30,9 +30,30 @@
 dump_all_errors = True
 
 # As logs are made clean, add to this set
-must_be_clean = set(['c-sch', 'g-reg', 'ceilometer-alarm-notifier',
-                     'ceilometer-collector', 'horizon', 'n-crt', 'n-obj',
-                     'q-vpn'])
+allowed_dirty = set([
+    'c-api',
+    'ceilometer-acentral',
+    'ceilometer-acompute',
+    'ceilometer-alarm-evaluator',
+    'ceilometer-anotification',
+    'ceilometer-api',
+    'c-vol',
+    'g-api',
+    'h-api',
+    'h-eng',
+    'ir-cond',
+    'n-api',
+    'n-cpu',
+    'n-net',
+    'n-sch',
+    'q-agt',
+    'q-dhcp',
+    'q-lbaas',
+    'q-meta',
+    'q-metering',
+    'q-svc',
+    'q-vpn',
+    's-proxy'])
 
 
 def process_files(file_specs, url_specs, whitelists):
@@ -69,12 +90,12 @@
                     break
             if not whitelisted or dump_all_errors:
                 if print_log_name:
-                    print("Log File: %s" % name)
+                    print("Log File Has Errors: %s" % name)
                     print_log_name = False
                 if not whitelisted:
                     had_errors = True
                     print("*** Not Whitelisted ***"),
-                print(line)
+                print(line.rstrip())
     return had_errors
 
 
@@ -135,8 +156,8 @@
         return 0
     failed = False
     for log in logs_with_errors:
-        if log in must_be_clean:
-            print("FAILED: %s" % log)
+        if log not in allowed_dirty:
+            print("Log: %s not allowed to have ERRORS or TRACES" % log)
             failed = True
     if failed:
         return 1