utilities: Add new pipeline generator script.
authorEthan Jackson <ethan@nicira.com>
Thu, 26 Mar 2015 19:52:42 +0000 (12:52 -0700)
committerEthan Jackson <ethan@nicira.com>
Fri, 8 May 2015 22:26:26 +0000 (15:26 -0700)
When doing OVS performance testing, it's important to have both
realistic traffic traces and OpenFlow pipelines on which to evaluate
prospective changes.  As a first step in this direction, this patch
adds a python script which generates an OpenFlow pipeline intended to
simulate typical network virtualization workloads.

Signed-off-by: Ethan Jackson <ethan@nicira.com>
Acked-by: Daniele Di Proietto <diproiettod@vmware.com>
utilities/automake.mk
utilities/ovs-pipegen.py [new file with mode: 0755]

index a06630f..6083b4b 100644 (file)
@@ -34,9 +34,8 @@ utilities/ovs-lib: $(top_builddir)/config.status
 
 docs += utilities/ovs-command-bashcomp.INSTALL.md
 EXTRA_DIST += \
-       utilities/ovs-check-dead-ifs.in \
        utilities/ovs-appctl-bashcomp.bash \
-       utilities/ovs-vsctl-bashcomp.bash \
+       utilities/ovs-check-dead-ifs.in \
        utilities/ovs-command-bashcomp.INSTALL.md \
        utilities/ovs-ctl.in \
        utilities/ovs-dev.py \
@@ -46,11 +45,13 @@ EXTRA_DIST += \
        utilities/ovs-lib.in \
        utilities/ovs-parse-backtrace.in \
        utilities/ovs-pcap.in \
+       utilities/ovs-pipegen.py \
        utilities/ovs-pki.in \
        utilities/ovs-save \
        utilities/ovs-tcpundump.in \
        utilities/ovs-test.in \
        utilities/ovs-vlan-test.in \
+       utilities/ovs-vsctl-bashcomp.bash \
        utilities/qemu-wrap.py
 MAN_ROOTS += \
        utilities/ovs-appctl.8.in \
diff --git a/utilities/ovs-pipegen.py b/utilities/ovs-pipegen.py
new file mode 100755 (executable)
index 0000000..95647d1
--- /dev/null
@@ -0,0 +1,122 @@
+#!/usr/bin/python
+# Copyright (c) 2013, 2014, 2015 Nicira, Inc.
+#
+# 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 random
+import sys
+import textwrap
+
+def flow_str(stage, match, action, priority=32768):
+    mtd_match = "metadata=%d" % stage
+    if match:
+        mtd_match += "," + match
+
+    return "priority=%d %s,actions=%s" % (priority, mtd_match, action)
+
+
+def resubmit(nxt):
+    return "load:%d->OXM_OF_METADATA[],resubmit(,0)" % nxt
+
+
+def rand_ip_mask():
+    return ("%d.%d.%d.%d" % (random.randint(0, 255), random.randint(0, 255),
+                             random.randint(0, 255), random.randint(0, 255)),
+            random.choice([8, 16, 24, 32]))
+
+
+def rand_bool():
+    return bool(random.randint(0, 1))
+
+
+def l2(stage, action):
+    mac = ["%x" % random.randint(0, 2 ** 8 - 1) for x in range(6)]
+    mac = [x.zfill(2) for x in mac]
+    mac = ":".join(mac)
+    return flow_str(stage, "dl_dst=%s" % mac, action)
+
+
+def l3(stage, action):
+    ip, mask = rand_ip_mask()
+    return flow_str(stage, "ip,ip_dst=%s/%d" % (ip, mask), action,
+                    priority=mask)
+
+
+def l4(stage, action):
+    match = "tcp"
+
+    if rand_bool():
+        match += ",ip_src=%s/%d" % rand_ip_mask()
+
+    if rand_bool():
+        match += ",ip_dst=%s/%d" % rand_ip_mask()
+
+    src_dst = "tp_src" if rand_bool() else "tp_dst"
+    match += ",%s=%d" % (src_dst, random.randint(1024, 2**16 - 1))
+    return flow_str(stage, match, action)
+
+
+def pipeline(size):
+    pipeline = [l2, l3, l4, l2]
+
+    flows = []
+    for stage in xrange(len(pipeline)):
+        action = resubmit(stage + 1)
+        flows += [pipeline[stage](stage, action) for _ in xrange(size)]
+        flows.append(flow_str(stage, "", action, priority=1))
+
+    flows.append(flow_str(len(pipeline), "", "in_port"))
+
+    for f in flows:
+        print f
+
+
+def main():
+    description = textwrap.dedent(
+        """
+        Generate a test OpenFlow pipeline.
+
+        Open vSwitch relies heavily on flow caching to get good performance for
+        packet processing.  While on average, this produces good results,
+        performance is heavily depedent on the slow path OpenFlow tables, and
+        how they're translated into datapath megaflows.  For this reason, when
+        doing performance testing it's important to run with "realistic"
+        OpenFlow tables to ensure results will stand up in the real world.
+
+        This script generates a simple OpenFlow pipeline intended to simulate
+        realistic network virtualization workloads.  All traffic received is
+        run through a series of OpenFlow tables designed to simulate a logical
+        switch, router, and firewall, before forwarded back on the in_port.
+        """)
+
+    epilog = textwrap.dedent(
+        """
+        typical usage:
+          ovs-ofctl del-flows bridge \\
+          && %s | ovs-ofctl add-flows bridge - \\
+          && ovs-ofctl dump-flows bridge
+        """ % sys.argv[0])
+
+    parser = argparse.ArgumentParser(description=description, epilog=epilog,
+                                     formatter_class=\
+                                     argparse.RawDescriptionHelpFormatter)
+    parser.add_argument("--size", dest="size", default=1000,
+                        help="Size (rules) of each OpenFlow table.")
+    args=parser.parse_args()
+
+    pipeline(int(args.size))
+
+
+if __name__ == "__main__":
+    main()