@@ -67,15 +67,16 @@ ovs_flowviz = \
python/ovs/flowviz/__init__.py \
python/ovs/flowviz/console.py \
python/ovs/flowviz/format.py \
+ python/ovs/flowviz/html_format.py \
python/ovs/flowviz/main.py \
python/ovs/flowviz/odp/__init__.py \
python/ovs/flowviz/odp/cli.py \
python/ovs/flowviz/ofp/__init__.py \
python/ovs/flowviz/ofp/cli.py \
+ python/ovs/flowviz/ofp/html.py \
python/ovs/flowviz/ovs-flowviz \
python/ovs/flowviz/process.py
-
# These python files are used at build time but not runtime,
# so they are not installed.
EXTRA_DIST += \
new file mode 100644
@@ -0,0 +1,136 @@
+# 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.format import FlowFormatter, FlowBuffer, FlowStyle
+
+
+class HTMLStyle:
+ """HTMLStyle defines a style for html-formatted flows.
+
+ Args:
+ color(str): Optional; a string representing the CSS color to use
+ anchor_gen(callable): Optional; a callable to be used to generate the
+ href
+ """
+
+ def __init__(self, color=None, anchor_gen=None):
+ self.color = color
+ self.anchor_gen = anchor_gen
+
+
+class HTMLBuffer(FlowBuffer):
+ """HTMLBuffer implementes FlowBuffer to provide html-based flow formatting.
+
+ Each flow gets formatted as:
+ <div><span>...</span></div>
+ """
+
+ def __init__(self):
+ self._text = ""
+
+ @property
+ def text(self):
+ return self._text
+
+ def _append(self, string, color, href):
+ """Append a key a string"""
+ style = ' style="color:{}"'.format(color) if color else ""
+ self._text += "<span{}>".format(style)
+ if href:
+ self._text += "<a href={}>".format(href)
+ self._text += string
+ if href:
+ self._text += "</a>"
+ self._text += "</span>"
+
+ def append_key(self, kv, style):
+ """Append a key.
+ Args:
+ kv (KeyValue): the KeyValue instance to append
+ style (HTMLStyle): the style to use
+ """
+ href = style.anchor_gen(kv) if (style and style.anchor_gen) else ""
+ return self._append(
+ kv.meta.kstring, style.color if style else "", href
+ )
+
+ def append_delim(self, kv, style):
+ """Append a delimiter.
+ Args:
+ kv (KeyValue): the KeyValue instance to append
+ style (HTMLStyle): the style to use
+ """
+ href = style.anchor_gen(kv) if (style and style.anchor_gen) else ""
+ return self._append(kv.meta.delim, style.color if style else "", href)
+
+ def append_end_delim(self, kv, style):
+ """Append an end delimiter.
+ Args:
+ kv (KeyValue): the KeyValue instance to append
+ style (HTMLStyle): the style to use
+ """
+ href = style.anchor_gen(kv) if (style and style.anchor_gen) else ""
+ return self._append(
+ kv.meta.end_delim, style.color if style else "", href
+ )
+
+ def append_value(self, kv, style):
+ """Append a value.
+ Args:
+ kv (KeyValue): the KeyValue instance to append
+ style (HTMLStyle): the style to use
+ """
+ href = style.anchor_gen(kv) if (style and style.anchor_gen) else ""
+ return self._append(
+ kv.meta.vstring, style.color if style else "", href
+ )
+
+ def append_extra(self, extra, style):
+ """Append extra string.
+ Args:
+ kv (KeyValue): the KeyValue instance to append
+ style (HTMLStyle): the style to use
+ """
+ return self._append(extra, style.color if style else "", "")
+
+
+class HTMLFormatter(FlowFormatter):
+ """Formts a flow in HTML Format."""
+
+ default_style_obj = FlowStyle(
+ {
+ "value.resubmit": HTMLStyle(
+ anchor_gen=lambda x: "#table_{}".format(x.value["table"])
+ ),
+ "default": HTMLStyle(),
+ }
+ )
+
+ def __init__(self, opts=None):
+ super(HTMLFormatter, self).__init__()
+ self.style = (
+ self._style_from_opts(opts, "html", HTMLStyle) or FlowStyle()
+ )
+
+ def format_flow(self, buf, flow, highlighted=None):
+ """Formats the flow into the provided buffer as a html object.
+
+ Args:
+ buf (FlowBuffer): the flow buffer to append to
+ flow (ovs_dbg.OFPFlow): the flow to format
+ highlighted (list): Optional; list of KeyValues to highlight
+ """
+ return super(HTMLFormatter, self).format_flow(
+ buf, flow, self.style, highlighted
+ )
@@ -15,6 +15,7 @@
import click
from ovs.flowviz.main import maincli
+from ovs.flowviz.ofp.html import HTMLProcessor
from ovs.flowviz.process import (
ConsoleProcessor,
OpenFlowFactory,
@@ -66,3 +67,12 @@ def console(opts, heat_map):
)
proc.process()
proc.print()
+
+
+@openflow.command()
+@click.pass_obj
+def html(opts):
+ """Print the flows in an linked HTML list arranged by tables."""
+ processor = HTMLProcessor(opts)
+ processor.process()
+ print(processor.html())
new file mode 100644
@@ -0,0 +1,80 @@
+# 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, HTMLStyle
+from ovs.flowviz.process import (
+ OpenFlowFactory,
+ FileProcessor,
+)
+
+
+class HTMLProcessor(OpenFlowFactory, FileProcessor):
+ """File processor that prints Openflow tables in HTML."""
+
+ def __init__(self, opts):
+ super().__init__(opts)
+ self.data = dict()
+
+ def start_file(self, name, filename):
+ self.tables = dict()
+
+ def stop_file(self, name, filename):
+ self.data[name] = self.tables
+
+ def process_flow(self, flow, name):
+ table = flow.info.get("table") or 0
+ if not self.tables.get(table):
+ self.tables[table] = list()
+ self.tables[table].append(flow)
+
+ def html(self):
+ html_obj = ""
+ for name, tables in self.data.items():
+ name = name.replace(" ", "_")
+ html_obj += "<h1>{}</h1>".format(name)
+ html_obj += "<div id=flow_list>"
+ for table, flows in tables.items():
+ formatter = HTMLFormatter(self.opts)
+
+ def anchor(x):
+ return "#table_%s_%s" % (name, x.value["table"])
+
+ formatter.style.set_value_style(
+ "resubmit",
+ HTMLStyle(
+ formatter.style.get("value.resubmit"),
+ anchor_gen=anchor,
+ ),
+ )
+ html_obj += (
+ "<h2 id=table_{name}_{table}> Table {table}</h2>".format(
+ name=name, table=table
+ )
+ )
+ html_obj += "<ul id=table_{}_flow_list>".format(table)
+ for flow in flows:
+ html_obj += "<li id=flow_{}>".format(flow.id)
+ highlighted = None
+ if self.opts.get("highlight"):
+ result = self.opts.get("highlight").evaluate(flow)
+ if result:
+ highlighted = result.kv
+ buf = HTMLBuffer()
+ formatter.format_flow(buf, flow, highlighted)
+ html_obj += buf.text
+ html_obj += "</li>"
+ html_obj += "</ul>"
+ html_obj += "</div>"
+
+ return html_obj
@@ -4,7 +4,7 @@
#
# [FORMAT].[PORTION].[SELECTOR].[ELEMENT] = [VALUE]
#
-# * FORMAT: console
+# * FORMAT: console or html
# * PORTION: The portion of the flow that the style applies to
# - key: Selects how to print the key of a KeyValue pair
# - key: Selects how to print the value of a KeyValue pair
@@ -25,6 +25,11 @@
# - underline: if set to "true", the selected portion will be underlined
#
#[1] https://rich.readthedocs.io/en/stable/appendix/colors.html#standard-colors
+#
+# HTML Styles
+# ==============
+# * ELEMENT:
+# - color: defines the color in hex format
[styles.dark]
@@ -92,3 +97,12 @@ console.value.highlighted.color = #f20905
console.key.highlighted.underline = true
console.value.highlighted.underline = true
console.delim.highlighted.underline = true
+
+# html
+html.key.color = #00005f
+html.value.color = #870000
+html.key.resubmit.color = #00d700
+html.key.output.color = #005f00
+html.value.output.color = #00d700
+html.key.highlighted.color = #FF00FF
+html.value.highlighted.color = #FF00FF