@@ -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 (
ConsoleProcessor,
JSONOpenFlowProcessor,
@@ -172,6 +172,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):
@@ -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(FileProcessor):
+ """Processor that sorts flows into cookies and tables."""
+
+ def __init__(self, opts):
+ super().__init__(opts, "ofp")
+ 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 = re.compile(self.opts.get("ovn_filter"))
+ if not ovn_regexp.search(ovn_info):
+ continue
+
+ cookie_tree = tree.add("** Cookie {} **".format(hex(cookie)))
+ if ovn_info:
+ ovn = cookie_tree.add("OVN Info")
+ for part in ovn_info.split("\n"):
+ if part.strip():
+ ovn.add(part.strip())
+
+ tables_tree = cookie_tree.add("Tables")
+ for table, flows in tables.items():
+ table_tree = tables_tree.add("* Table {} * ".format(table))
+ for flow in flows:
+ buf = ConsoleBuffer(Text())
+ ofconsole.format_flow(buf, flow)
+ table_tree.add(buf.text)
+ console.print(tree)