diff --git a/.zuul.yaml b/.zuul.yaml
index 5e75d94defe4c597c61c07f2a7a1f7ac60150196..387b94444a4a4fcc2ad5baafed5cbef4beac00a5 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -2,3 +2,4 @@
     templates:
       - python-charm-jobs
       - openstack-python35-jobs
+      - openstack-cover-jobs
diff --git a/tox.ini b/tox.ini
index 511072283d415d1a8ce1106d6a1767ec209bab6e..d3bc831554a094bf7fcf390a19d146efb7aeb1d6 100644
--- a/tox.ini
+++ b/tox.ini
@@ -69,6 +69,34 @@ basepython = python3
 deps = -r{toxinidir}/test-requirements.txt
 commands = flake8 {posargs} src unit_tests
 
+[testenv:cover]
+# Technique based heavily upon
+# https://github.com/openstack/nova/blob/master/tox.ini
+basepython = python3
+deps = -r{toxinidir}/requirements.txt
+       -r{toxinidir}/test-requirements.txt
+setenv =
+    {[testenv]setenv}
+    PYTHON=coverage run
+commands =
+    coverage erase
+    ostestr {posargs}
+    coverage combine
+    coverage html -d cover
+    coverage xml -o cover/coverage.xml
+    coverage report
+
+[coverage:run]
+branch = True
+concurrency = multiprocessing
+parallel = True
+source =
+    .
+omit =
+    .tox/*
+    */charmhelpers/*
+    unit_tests/*
+
 [testenv:venv]
 basepython = python3
 commands = {posargs}