When anaylizing OVN issues, it might be useful to see what OpenFlow
flows were generated from each logical flow. In order to make it simpler
to visualize this, add a cookie format that simply sorts the flows first
by cookie, then by table.
Example:
$ export OVN_NB_DB=...
$ export OVN_SB_DB=...
$ ovs-vsctl dump-flows br-int | ovs-flowviz openflow cookie
--ovn-filter="acl.*icmp4"
$ ovs-vsctl dump-flows br-int | ovs-flowviz openflow cookie
--ovn-detrace
Acked-by: Eelco Chaudron
Signed-off-by: Adrian Moreno
---
python/ovs/flowviz/ofp/cli.py | 57 +-
python/ovs/flowviz/ofp/logic.py | 61 +
2 files changed, 117 insertions(+), 1 deletion(-)
diff --git a/python/ovs/flowviz/ofp/cli.py b/python/ovs/flowviz/ofp/cli.py
index 6b1435ea1..9658d00d3 100644
--- a/python/ovs/flowviz/ofp/cli.py
+++ b/python/ovs/flowviz/ofp/cli.py
@@ -18,7 +18,7 @@ import click
from ovs.flowviz.main import maincli
from ovs.flowviz.ofp.html import HTMLProcessor
-from ovs.flowviz.ofp.logic import LogicFlowProcessor
+from ovs.flowviz.ofp.logic import CookieProcessor, LogicFlowProcessor
from ovs.flowviz.process import (
OpenFlowFactory,
JSONProcessor,
@@ -182,6 +182,61 @@ def logic(
processor.print(show_flows)
+@openflow.command()
+@click.option(
+"-d",
+"--ovn-detrace",
+"ovn_detrace_flag",
+is_flag=True,
+show_default=True,
+help="Use ovn-detrace to extract cookie information",
+)
+@click.option(
+"--ovn-detrace-path",
+default="/usr/bin",
+type=click.Path(),
+help="Use an alternative path to where ovn_detrace.py is located. "
+"Instead of using this option you can just set PYTHONPATH accordingly",
+show_default=True,
+callback=ovn_detrace_callback,
+)
+@click.option(
+"--ovnnb-db",
+default=os.getenv("OVN_NB_DB") or "unix:/var/run/ovn/ovnnb_db.sock",
+help="Specify the OVN NB database string (implies -d). "
+"If the OVN_NB_DB environment variable is set, it's used as default. "
+"Otherwise, the default is unix:/var/run/ovn/ovnnb_db.sock",
+callback=ovn_detrace_callback,
+)
+@click.option(
+"--ovnsb-db",
+default=os.getenv("OVN_SB_DB") or "unix:/var/run/ovn/ovnsb_db.sock",
+help="Specify the OVN NB database string (implies -d). "
+"If the OVN_NB_DB environment variable is set, it's used as default. "
+"Otherwise, the default is unix:/var/run/ovn/ovnnb_db.sock",
+callback=ovn_detrace_callback,
+)
+@click.option(
+"-o",
+"--ovn-filter",
+help="Specify a filter to be run on ovn-detrace information (implied -d). "
+"Format: python regular expression "
+"(see https://docs.python.org/3/library/re.html)",
+callback=ovn_detrace_callback,
+)
+@click.pass_obj
+def cookie(
+opts, ovn_detrace_flag, ovn_detrace_path, ovnnb_db, ovnsb_db, ovn_filter
+):
+"""Print the flow tables sorted by cookie."""
+if ovn_detrace_flag:
+opts["ovn_detrace_flag"] = True
+
+processor = CookieProcessor(opts)
+processor.process()
+processor.print()
+
+
@openflow.command()
@click.pass_obj
def html(opts):
diff --git a/python/ovs/flowviz/ofp/logic.py b/python/ovs/flowviz/ofp/logic.py
index db2124374..9d244d137 100644
--- a/python/ovs/flowviz/ofp/logic.py
+++ b/python/ovs/flowviz/ofp/logic.py
@@ -301,3 +301,64 @@ cookie_style_gen = hash_pallete(
saturation=[0.5],
value=[0.5 + x / 10 * (0.85 - 0.5) for x in range(0, 10)],
)
+
+
+class CookieProcessor(OpenFlowFactory, FileProcessor):
+"""Processor that sorts flows into cookies and tables."""
+
+def __init__(self, opts):
+super().__init__(opts)
+self.data = dict()
+self.ovn_detrace = (
+OVNDetrace(opts) if opts.get("ovn_detrace_flag") else None
+)
+
+def start_file(self, name, filename):
+self.cookies = dict()
+
+def stop_file(self, name, filename):
+self.data[name] = self.cookies
+
+def process_flow(self, flow, name):
+"""Sort the flows by table and logical flow."""
+cookie = flow.info.get("cookie") or 0
+if not self.cookies.get(cookie):
+self.cookies[cookie] = dict()
+
+table = flow.info.get("table") or 0
+if not self.cookies[cookie].get(table):
+self.cookies[cookie][table] = list()
+self.cookies[cookie][table].append(flow)
+
+def print(self):
+ofconsole = ConsoleFormatter(opts=self.opts)
+console = ofconsole.console
+for name, cookies in self.data.items():
+console.print("\n")
+console.print(file_header(name))
+tree = Tree("Ofproto Cookie Tree")
+
+for cookie, tables in cookies.items():
+ovn_info = None
+if self.ovn_detrace:
+ovn_info = self.ovn_detrace.get_ovn_info(cookie)
+if self.opts.get("ovn_filter"):
+ovn_regexp =