Add @services decorator

This commit adds a new decorator used for specifying service tags for
a test. It just wraps the attr decorator but before adding the attr it
checks to make sure a valid service name was used.

Part of bp add-service-tags

Change-Id: I0cdf3f5c703a5e4573b8e9b26fb8ede9b6d77690
diff --git a/tempest/exceptions.py b/tempest/exceptions.py
index 62bd8cf..924ebc9 100644
--- a/tempest/exceptions.py
+++ b/tempest/exceptions.py
@@ -65,6 +65,10 @@
     message = 'Unauthorized'
 
 
+class InvalidServiceTag(RestClientException):
+    message = "Invalid service tag"
+
+
 class TimeoutException(TempestException):
     message = "Request timed out"
 
diff --git a/tempest/test.py b/tempest/test.py
index decae94..24c4489 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -26,6 +26,7 @@
 
 from tempest import clients
 from tempest import config
+from tempest import exceptions
 from tempest.openstack.common import log as logging
 
 LOG = logging.getLogger(__name__)
@@ -57,6 +58,25 @@
     return decorator
 
 
+def services(*args, **kwargs):
+    """A decorator used to set an attr for each service used in a test case
+
+    This decorator applies a testtools attr for each service that gets
+    exercised by a test case.
+    """
+    valid_service_list = ['compute', 'image', 'volume', 'orchestration',
+                          'network', 'identity', 'object', 'dashboard']
+
+    def decorator(f):
+        for service in args:
+            if service not in valid_service_list:
+                raise exceptions.InvalidServiceTag('%s is not a valid service'
+                                                   % service)
+        attr(type=list(args))(f)
+        return f
+    return decorator
+
+
 def stresstest(*args, **kwargs):
     """Add stress test decorator