1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
|
# 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.
import colorsys
import itertools
import zlib
from rich.console import Console
from rich.color import Color
from rich.text import Text
from rich.style import Style
from ovs.flowviz.format import FlowFormatter, FlowBuffer
def file_header(name):
return Text(f"### {name} ###")
class ConsoleBuffer(FlowBuffer):
"""ConsoleBuffer implements FlowBuffer to provide console-based text
formatting based on rich.Text.
Append functions accept a rich.Style.
Args:
rtext(rich.Text): Optional; text instance to reuse
"""
def __init__(self, rtext):
self._text = rtext or Text()
@property
def text(self):
return self._text
def _append(self, string, style):
"""Append to internal text."""
return self._text.append(string, style)
def append_key(self, kv, style):
"""Append a key.
Args:
kv (KeyValue): the KeyValue instance to append
style (rich.Style): the style to use
"""
return self._append(kv.meta.kstring, style)
def append_delim(self, kv, style):
"""Append a delimiter.
Args:
kv (KeyValue): the KeyValue instance to append
style (rich.Style): the style to use
"""
return self._append(kv.meta.delim, style)
def append_end_delim(self, kv, style):
"""Append an end delimiter.
Args:
kv (KeyValue): the KeyValue instance to append
style (rich.Style): the style to use
"""
return self._append(kv.meta.end_delim, style)
def append_value(self, kv, style):
"""Append a value.
Args:
kv (KeyValue): the KeyValue instance to append
style (rich.Style): the style to use
"""
return self._append(kv.meta.vstring, style)
def append_value_omitted(self, kv):
"""Append an omitted value.
Args:
kv (KeyValue): the KeyValue instance to append
"""
dots = "." * len(kv.meta.vstring)
return self._append(dots, None)
def append_extra(self, extra, style):
"""Append extra string.
Args:
kv (KeyValue): the KeyValue instance to append
style (rich.Style): the style to use
"""
return self._append(extra, style)
class ConsoleFormatter(FlowFormatter):
"""ConsoleFormatter is a FlowFormatter that formats flows into the console
using rich.Console.
Args:
console (rich.Console): Optional, an existing console to use
max_value_len (int): Optional; max length of the printed values
kwargs (dict): Optional; Extra arguments to be passed down to
rich.console.Console()
"""
def __init__(self, opts=None, console=None, **kwargs):
super(ConsoleFormatter, self).__init__()
self.style = self.style_from_opts(opts)
self.console = console or Console(color_system="256", **kwargs)
def style_from_opts(self, opts):
return self._style_from_opts(opts, "console", Style)
def print_flow(self, flow, highlighted=None, omitted=None):
"""Prints a flow to the console.
Args:
flow (ovs_dbg.OFPFlow): the flow to print
style (dict): Optional; style dictionary to use
highlighted (list): Optional; list of KeyValues to highlight
omitted (list): Optional; list of KeyValues to omit
"""
buf = ConsoleBuffer(Text())
self.format_flow(buf, flow, highlighted, omitted)
self.console.print(buf.text, soft_wrap=True)
def format_flow(self, buf, flow, highlighted=None, omitted=None):
"""Formats the flow into the provided buffer as a rich.Text.
Args:
buf (FlowBuffer): the flow buffer to append to
flow (ovs_dbg.OFPFlow): the flow to format
style (FlowStyle): Optional; style object to use
highlighted (list): Optional; list of KeyValues to highlight
omitted (list): Optional; list of KeyValues to omit
"""
return super(ConsoleFormatter, self).format_flow(
buf, flow, self.style, highlighted, omitted
)
def heat_pallete(min_value, max_value):
"""Generates a color pallete based on the 5-color heat pallete so that
for each value between min and max a color is returned that represents it's
relative size.
Args:
min_value (int): minimum value
max_value (int) maximum value
"""
h_min = 0 # red
h_max = 220 / 360 # blue
def heat(value):
if max_value == min_value:
r, g, b = colorsys.hsv_to_rgb(h_max / 2, 1.0, 1.0)
else:
normalized = (int(value) - min_value) / (max_value - min_value)
hue = ((1 - normalized) + h_min) * (h_max - h_min)
r, g, b = colorsys.hsv_to_rgb(hue, 1.0, 1.0)
return Style(color=Color.from_rgb(r * 255, g * 255, b * 255))
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)
|