diff --git a/python/ovs/flowviz/ofp/cli.py b/python/ovs/flowviz/ofp/cli.py
index 51428ede0..690e7618e 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 (
     ConsoleProcessor,
     OpenFlowFactory,
@@ -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 = 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)
