Datapath flows can be arranged into a "tree"-like structure based on
recirculation ids, e.g:
recirc(0),eth(...),ipv4(...) actions=ct,recirc(0x42)
\-> recirc(42),ct_state(0/0),eth(...),ipv4(...) actions=1
\-> recirc(42),ct_state(1/0),eth(...),ipv4(...) actions=userspace(...)
This patch adds support for building such logical datapath trees in a
format-agnostic way and adds support for console-based formatting
supporting:
- head-maps formatting of statistics
- hash-based pallete of recirculation ids: each recirculation id is
assigned a unique color to easily follow the sequence of related
actions.
- full-tree filtering: if a user specifies a filter, an entire subtree
is filtered out if none of its branches satisfy it.
Acked-by: Eelco Chaudron
Signed-off-by: Adrian Moreno
---
python/automake.mk | 1 +
python/ovs/flowviz/console.py | 21 +++
python/ovs/flowviz/odp/cli.py | 21 ++-
python/ovs/flowviz/odp/tree.py | 291 +
4 files changed, 332 insertions(+), 2 deletions(-)
create mode 100644 python/ovs/flowviz/odp/tree.py
diff --git a/python/automake.mk b/python/automake.mk
index 0487494d0..b3fef9bed 100644
--- a/python/automake.mk
+++ b/python/automake.mk
@@ -71,6 +71,7 @@ ovs_flowviz = \
python/ovs/flowviz/main.py \
python/ovs/flowviz/odp/__init__.py \
python/ovs/flowviz/odp/cli.py \
+ python/ovs/flowviz/odp/tree.py \
python/ovs/flowviz/ofp/__init__.py \
python/ovs/flowviz/ofp/cli.py \
python/ovs/flowviz/ofp/html.py \
diff --git a/python/ovs/flowviz/console.py b/python/ovs/flowviz/console.py
index 4a3443360..93cd9b0b1 100644
--- a/python/ovs/flowviz/console.py
+++ b/python/ovs/flowviz/console.py
@@ -13,6 +13,8 @@
# limitations under the License.
import colorsys
+import itertools
+import zlib
from rich.console import Console
from rich.color import Color
@@ -170,6 +172,25 @@ def heat_pallete(min_value, max_value):
return heat
+def hash_pallete(hue, saturation, value):
+"""Generates a color pallete with the cartesian product
+of the hsv values provided and returns a callable that assigns a color for
+each value hash
+"""
+HSV_tuples = itertools.product(hue, saturation, value)
+RGB_tuples = map(lambda x: colorsys.hsv_to_rgb(*x), HSV_tuples)
+styles = [
+Style(color=Color.from_rgb(r * 255, g * 255, b * 255))
+for r, g, b in RGB_tuples
+]
+
+def get_style(string):
+hash_val = zlib.crc32(bytes(str(string), "utf-8"))
+return styles[hash_val % len(styles)]
+
+return get_style
+
+
def default_highlight():
"""Generates a default style for highlights."""
return Style(underline=True)
diff --git a/python/ovs/flowviz/odp/cli.py b/python/ovs/flowviz/odp/cli.py
index 78f5cfff4..4740e753e 100644
--- a/python/ovs/flowviz/odp/cli.py
+++ b/python/ovs/flowviz/odp/cli.py
@@ -13,12 +13,12 @@
# limitations under the License.
import click
-
from ovs.flowviz.main import maincli
+from ovs.flowviz.odp.tree import ConsoleTreeProcessor
from ovs.flowviz.process import (
DatapathFactory,
-JSONProcessor,
ConsoleProcessor,
+JSONProcessor,
)
@@ -65,3 +65,20 @@ def console(opts, heat_map):
)
proc.process()
proc.print()
+
+
+@datapath.command()
+@click.option(
+"-h",
+"--heat-map",
+is_flag=True,
+default=False,
+show_default=True,
+help="Create heat-map with packet and byte counters",
+)
+@click.pass_obj
+def tree(opts, heat_map):
+"""Print the flows in a tree based on the 'recirc_id'."""
+processor = ConsoleTreeProcessor(opts)
+processor.process()
+processor.print(heat_map)
diff --git a/python/ovs/flowviz/odp/tree.py b/python/ovs/flowviz/odp/tree.py
new file mode 100644
index 0..d249e7d6d
--- /dev/null
+++ b/python/ovs/flowviz/odp/tree.py
@@ -0,0 +1,291 @@
+# Copyright (c) 2023 Red Hat, 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.
+
+from rich.style import Style
+from rich.text import Text
+from rich.tree import Tree
+
+from ovs.flowviz.console import (
+ConsoleFormatter,
+ConsoleBuffer,
+hash_pallete,
+heat_pallete,
+file_header,
+)
+from ovs.flowviz.process import (
+DatapathFactory,
+FileProcessor,
+)
+
+
+class TreeElem:
+"""Element in the tree.
+Args:
+children (list[TreeElem]): Optional, list of children
+is_root (bool): Optional; whether this is the root elemen
+