Add statsd.

Reports statistics on job runtime, number/rate of jobs run, and
number/rate of individual gerrit events to statsd.

Change-Id: I0db9352610b4c268c95da98fd282d7f463945f90
Reviewed-on: https://review.openstack.org/18628
Reviewed-by: Monty Taylor <mordred@inaugust.com>
Reviewed-by: Jeremy Stanley <fungi@yuggoth.org>
Approved: James E. Blair <corvus@inaugust.com>
Tested-by: Jenkins
diff --git a/tools/pip-requires b/tools/pip-requires
index e681a0c..265f663 100644
--- a/tools/pip-requires
+++ b/tools/pip-requires
@@ -7,3 +7,4 @@
 lockfile
 python-daemon
 extras
+statsd==1.0.0
diff --git a/zuul/model.py b/zuul/model.py
index 2b5f5b3..f6b16bf 100644
--- a/zuul/model.py
+++ b/zuul/model.py
@@ -436,6 +436,8 @@
         self.result = None
         self.build_set = None
         self.launch_time = time.time()
+        self.start_time = None
+        self.end_time = None
 
     def __repr__(self):
         return '<Build %s of %s>' % (self.uuid, self.job.name)
diff --git a/zuul/scheduler.py b/zuul/scheduler.py
index aa3f8ab..83487de 100644
--- a/zuul/scheduler.py
+++ b/zuul/scheduler.py
@@ -12,6 +12,7 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
+import extras
 import json
 import logging
 import os
@@ -19,12 +20,15 @@
 import Queue
 import re
 import threading
+import time
 import yaml
 
 import model
 from model import Pipeline, Job, Project, ChangeQueue, EventFilter
 import merger
 
+statsd = extras.try_import('statsd.statsd')
+
 
 class Scheduler(threading.Thread):
     log = logging.getLogger("zuul.Scheduler")
@@ -214,6 +218,8 @@
 
     def addEvent(self, event):
         self.log.debug("Adding trigger event: %s" % event)
+        if statsd:
+            statsd.incr('gerrit.event.%s' % event.type)
         self.queue_lock.acquire()
         self.trigger_event_queue.put(event)
         self.queue_lock.release()
@@ -221,6 +227,7 @@
 
     def onBuildStarted(self, build):
         self.log.debug("Adding start event for build: %s" % build)
+        build.start_time = time.time()
         self.queue_lock.acquire()
         self.result_event_queue.put(('started', build))
         self.queue_lock.release()
@@ -228,6 +235,13 @@
 
     def onBuildCompleted(self, build):
         self.log.debug("Adding complete event for build: %s" % build)
+        build.end_time = time.time()
+        if statsd:
+            dt = int((build.end_time - build.start_time) * 1000)
+            key = 'zuul.job.%s' % build.job.name
+            if build.result in ['SUCCESS', 'FAILURE']:
+                statsd.timing(key, dt)
+            statsd.incr(key)
         self.queue_lock.acquire()
         self.result_event_queue.put(('completed', build))
         self.queue_lock.release()
@@ -325,6 +339,11 @@
         return False
 
     def run(self):
+        if statsd:
+            self.log.debug("Statsd enabled")
+        else:
+            self.log.debug("Statsd disabled because python statsd "
+                           "package not found")
         while True:
             self.log.debug("Run handler sleeping")
             self.wake_event.wait()