diff --git a/tools/cap.py b/tools/cap.py
new file mode 100755
index 0000000000..11ecfc64e2
--- /dev/null
+++ b/tools/cap.py
@@ -0,0 +1,138 @@
+#! /usr/bin/env python
+
+# 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.
+
+
+import argparse
+import re
+
+import pkg_resources
+
+overrides = dict()
+# List of overrides needed. Ignore version in pip-freeze and use the one here
+# instead. Example:
+# suds 0.4.1 isn't pip installable but is in distribution packages
+# overrides['suds'] = 'suds==0.4'
+# apt package of libvirt-python is lower then our minimum requirement
+# overrides['libvirt-python'] = None
+
+
+def cap(requirements, frozen):
+    """Cap requirements to version in freeze.
+
+    Go through every package in requirements and try to cap.
+
+    Input: two arrays of lines.
+    Output: Array of new lines.
+    """
+    output = []
+    for line in requirements:
+        try:
+            req = pkg_resources.Requirement.parse(line)
+            specifier = str(req.specifier)
+            if any(op in specifier for op in ['==', '~=', '<']):
+                # if already capped, continue
+                output.append(line)
+                continue
+        except ValueError:
+            # line was a comment, continue
+            output.append(line)
+            continue
+        if req.project_name in overrides:
+            new_line = overrides[req.project_name]
+            if new_line:
+                output.append(overrides[req.project_name])
+            else:
+                output.append(line)
+            continue
+        # add cap
+        new_cap = cap_requirement(req.project_name, frozen)
+        if new_cap:
+            output.append(pin(line, new_cap))
+        else:
+            output.append(line)
+    return output
+
+
+def pin(line, new_cap):
+    """Add new cap into existing line
+
+    Don't use pkg_resources so we can preserve the comments.
+    """
+    end = None
+    use_comma = False
+    parts = line.split(' #')
+    if len(split(parts[0].strip())) > 1:
+        use_comma = True
+    if "#" in line:
+        # if comment
+        end = parts[1]
+    # cap to new max version
+    if end:
+        new_end = "<=%s #%s" % (new_cap, end)
+    else:
+        new_end = "<=%s" % new_cap
+    if use_comma is True:
+        return "%s,%s" % (parts[0].strip(), new_end)
+    else:
+        return "%s%s" % (parts[0].strip(), new_end)
+
+
+def split(line):
+    return re.split('[><=]', line)
+
+
+def cap_requirement(requirement, frozen):
+    # Find current version of requirement in freeze
+    specifier = frozen.get(requirement, None)
+    if specifier:
+        return split(str(specifier))[-1]
+    return None
+
+
+def freeze(lines):
+    """Parse lines from freeze file into a dict.
+
+    Where k:v is project_name:specifier.
+    """
+    freeze = dict()
+
+    for line in lines:
+        try:
+            req = pkg_resources.Requirement.parse(line)
+            freeze[req.project_name] = req.specifier
+        except ValueError:
+            # not a valid requirement, can be a comment, blank line etc
+            continue
+    return freeze
+
+
+def main():
+    parser = argparse.ArgumentParser(
+        description="Take the output of "
+                    "'pip freeze' and use the installed versions to "
+                    "caps requirements.")
+    parser.add_argument('requirements', help='requirements file input')
+    parser.add_argument(
+        'freeze',
+        help='output of pip freeze, taken from a full tempest job')
+    args = parser.parse_args()
+    with open(args.requirements) as f:
+        requirements = [line.strip() for line in f.readlines()]
+    with open(args.freeze) as f:
+        frozen = freeze([line.strip() for line in f.readlines()])
+    for line in cap(requirements, frozen):
+        print(line)
+
+if __name__ == '__main__':
+    main()