Using the existing FlowTree and HTMLFormatter, create an HTML tree
visualization that also supports collapsing and expanding entire flow
trees and subtrees.
Examples:
$ ovs-appcl dpctl/dump-flows | ovs-flowviz --highlight drop datapath
html > /tmp/flows.html
$ ovs-appcl dpctl/dump-flows | ovs-flowviz -f "output.port=3"
datapath html > /tmp/flows.html
Acked-by: Eelco Chaudron
Signed-off-by: Adrian Moreno
---
python/automake.mk | 1 +
python/ovs/flowviz/odp/cli.py | 10 ++
python/ovs/flowviz/odp/html.py | 259 +
3 files changed, 270 insertions(+)
create mode 100644 python/ovs/flowviz/odp/html.py
diff --git a/python/automake.mk b/python/automake.mk
index 449daf023..44e9e08ab 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/html.py \
python/ovs/flowviz/odp/tree.py \
python/ovs/flowviz/ofp/__init__.py \
python/ovs/flowviz/ofp/cli.py \
diff --git a/python/ovs/flowviz/odp/cli.py b/python/ovs/flowviz/odp/cli.py
index 4740e753e..059dd708e 100644
--- a/python/ovs/flowviz/odp/cli.py
+++ b/python/ovs/flowviz/odp/cli.py
@@ -14,6 +14,7 @@
import click
from ovs.flowviz.main import maincli
+from ovs.flowviz.odp.html import HTMLTreeProcessor
from ovs.flowviz.odp.tree import ConsoleTreeProcessor
from ovs.flowviz.process import (
DatapathFactory,
@@ -82,3 +83,12 @@ def tree(opts, heat_map):
processor = ConsoleTreeProcessor(opts)
processor.process()
processor.print(heat_map)
+
+
+@datapath.command()
+@click.pass_obj
+def html(opts):
+"""Print the flows in an HTML list sorted by recirc_id."""
+processor = HTMLTreeProcessor(opts)
+processor.process()
+processor.print()
diff --git a/python/ovs/flowviz/odp/html.py b/python/ovs/flowviz/odp/html.py
new file mode 100644
index 0..4aa08dc70
--- /dev/null
+++ b/python/ovs/flowviz/odp/html.py
@@ -0,0 +1,259 @@
+# 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 ovs.flowviz.html_format import HTMLBuffer, HTMLFormatter
+from ovs.flowviz.odp.tree import FlowElem, FlowTree
+from ovs.flowviz.process import DatapathFactory, FileProcessor
+
+
+class HTMLTreeProcessor(DatapathFactory, FileProcessor):
+def __init__(self, opts):
+super().__init__(opts)
+self.data = dict()
+
+def start_file(self, name, filename):
+self.tree = HTMLTree(name, self.opts)
+
+def process_flow(self, flow, name):
+self.tree.add(flow)
+
+def process(self):
+super().process(False)
+
+def stop_file(self, name, filename):
+self.data[name] = self.tree
+
+def print(self):
+html_obj = ""
+for name, tree in self.data.items():
+html_obj += ""
+html_obj += "{}".format(name)
+tree.build()
+if self.opts.get("filter"):
+tree.filter(self.opts.get("filter"))
+html_obj += tree.render()
+html_obj += ""
+print(html_obj)
+
+
+class HTMLTree(FlowTree):
+"""HTMLTree is a Flowtree that prints the tree in html format.
+
+Args:
+opts(dict): Options dictionary
+flows(dict[int, list[DPFlow]): Optional; initial flows
+"""
+
+html_header = """
+
+.flow{
+background-color:white;
+display: inline-block;
+text-align: left;
+font-family: monospace;
+}
+.active{
+border: 2px solid #0008ff;
+}
+input[type='checkbox'] { display: none; }
+.wrap-collabsible {
+margin: 1.2rem 0;
+}
+.lbl-toggle-main {
+font-weight: bold;
+font-family: monospace;
+font-size: 1.5rem;
+text-transform: uppercase;
+text-align: center;
+padding: 1rem;
+#cursor: pointer;
+border-radius: 7px;
+transition: all 0.25s ease-out;
+}
+.lbl-toggle-flow {
+font-family: monospace;
+font-size: 1.0rem;
+text-transform: uppercase;
+text-align: center;
+padding: 1rem;
+#cursor: pointer;
+border-radius: 7px;
+transition: all 0.25s ease-out;
+}
+.lbl-toggle:hover {
+color: #0008ff;
+}
+.lbl-toggle::before {
+content: ' ';
+display: inline-block;
+