contrib/summarize_loadtest: Simplify filtering logic

BUG=None
TEST=None

Change-Id: If694be70b69b95a2f8fba6526397ca09147b843c
Reviewed-on: https://chromium-review.googlesource.com/868373
Commit-Ready: David Riley <davidriley@chromium.org>
Tested-by: David Riley <davidriley@chromium.org>
Reviewed-by: Xixuan Wu <xixuan@chromium.org>
diff --git a/contrib/summarize_loadtest.py b/contrib/summarize_loadtest.py
index eac9392..6dd8505 100755
--- a/contrib/summarize_loadtest.py
+++ b/contrib/summarize_loadtest.py
@@ -7,10 +7,8 @@
 """Load generator for devserver."""
 
 import argparse
-import ast
 import itertools
 import json
-import operator
 import pprint
 import re
 import sys
@@ -20,16 +18,6 @@
 from chromite.lib import cros_logging as logging
 
 
-# Map ast to operator.
-OPERATORS = {
-    ast.Add: operator.add, ast.Sub: operator.sub, ast.Mult: operator.mul,
-    ast.Div: operator.truediv, ast.USub: operator.neg,
-    ast.Not: operator.not_,
-    ast.Eq: operator.eq, ast.NotEq: operator.ne,
-    ast.Lt: operator.lt, ast.Gt: operator.gt,
-    ast.LtE: operator.le, ast.GtE: operator.ge,
-}
-
 # Default keys to skip displaying.
 DEFAULT_SKIP = [
     'build_name',
@@ -71,67 +59,11 @@
         parser.add_argument('--%s' % arg, type=str, action='store',
                             help='Comma-separated list of %s to filter by.' %
                             arg)
+    parser.add_argument('--no-summary', action='store_false', dest='summary',
+                        help='Disable summary.')
 
     return parser
 
-def eval_entry(expr, entry):
-    """Perform evaluation of an expression.
-
-    Named variables are interpreted as key-values from entry.
-    """
-    return eval_node(ast.parse(expr, mode='eval').body, entry)
-
-def eval_node(node, entry):
-    """Perform evaluation of a node."""
-    if isinstance(node, ast.Num):
-        return node.n
-    elif isinstance(node, ast.Str):
-        return node.s
-    elif isinstance(node, ast.Name):
-        if node.id == 'True':
-            return True
-        elif node.id == 'False':
-            return False
-        else:
-            return entry[node.id]
-    elif isinstance(node, ast.BinOp):
-        return OPERATORS[type(node.op)](eval_node(node.left, entry),
-                                        eval_node(node.right, entry))
-    elif isinstance(node, ast.UnaryOp): # <operator> <operand> e.g., -1
-        return OPERATORS[type(node.op)](eval_node(node.operand, entry))
-    elif isinstance(node, ast.BoolOp): # <operator> <operand> e.g., -1
-        if isinstance(node.op, ast.And):
-            for value in node.values:
-                if not eval_node(value, entry):
-                    return False
-            return True
-        elif isinstance(node.op, ast.Or):
-            for value in node.values:
-                if eval_node(value, entry):
-                    return True
-            return False
-        else:
-            raise TypeError(node)
-    elif isinstance(node, ast.Compare): # <operator> <operand> e.g., -1
-        left = node.left
-        for op, comparator in zip(node.ops, node.comparators):
-            if not OPERATORS[type(op)](eval_node(left, entry),
-                                       eval_node(comparator, entry)):
-                return False
-            left = comparator
-        return True
-    elif isinstance(node, ast.Call):
-        if isinstance(node.func, ast.Name) and node.func.id == 'match':
-            return re.match(eval_node(node.args[0], entry),
-                            eval_node(node.args[1], entry))
-        elif isinstance(node.func, ast.Name) and node.func.id == 'search':
-            return re.search(eval_node(node.args[0], entry),
-                             eval_node(node.args[1], entry))
-        else:
-            raise TypeError(node)
-    else:
-        raise TypeError(node)
-
 def summarize_entries(entries, skip=set()):
     """Summarize a list of entries."""
     TAG_KEYS = [
@@ -219,7 +151,7 @@
                                        options.__dict__[arg].split(','),
                              entries)
     if options.filter:
-        entries = filter(lambda x: eval_entry(options.filter, x), entries)
+        entries = filter(lambda x: eval(options.filter, {'re': re}, x), entries)
 
     # Group the entries based on specified keys.
     groups = group_entries(options.group.split(',') if options.group else None,
@@ -232,7 +164,7 @@
         parents = []
         for entry in dump_entries:
             print(json.dumps(entry))
-            if entry['parent'] not in parents:
+            if 'parent' in entry and entry['parent'] not in parents:
                 parents.append(entry['parent'])
         # Dump all parents.
         for entry in all_entries:
@@ -240,9 +172,10 @@
                 print(json.dumps(entry))
 
     # Summarize the entries, group by group.
-    skip = options.skip.split(',') if options.skip else set()
-    summaries = [summarize_entries(group, skip) for group in groups]
-    print(json.dumps(summaries, indent=2))
+    if options.summary:
+        skip = options.skip.split(',') if options.skip else set()
+        summaries = [summarize_entries(group, skip) for group in groups]
+        print(json.dumps(summaries, indent=2))
 
 if __name__ == '__main__':
     sys.exit(main(sys.argv[1:]))