Message ID | 20231201191449.2386134-9-amorenoz@redhat.com |
---|---|
State | RFC |
Delegated to: | Simon Horman |
Headers | show |
Series | Add flow visualization utility | expand |
Context | Check | Description |
---|---|---|
ovsrobot/apply-robot | success | apply and check: success |
ovsrobot/intel-ovs-compilation | fail | test: fail |
ovsrobot/github-robot-_Build_and_Test | fail | github build: failed |
On 1 Dec 2023, at 20:14, Adrian Moreno wrote: > When anaylizing OVN issues, it might be useful to see what OpenFlow > flows were generated from each logical flow. In order to make it simpler > to visualize this, add a cookie format that simply sorts the flows first > by cookie, then by table. The code looks good to me, however, did not try with ovs-detrace. One comment on code that needs to move to the previous patch. Maybe add an example in the commit message. Acked-by: Eelco Chaudron <echaudro@redhat.com> > Signed-off-by: Adrian Moreno <amorenoz@redhat.com> > --- > python/ovs/flowviz/ofp/cli.py | 57 ++++++++++++++++++++++++++++- > python/ovs/flowviz/ofp/logic.py | 63 ++++++++++++++++++++++++++++++++- > 2 files changed, 118 insertions(+), 2 deletions(-) > > diff --git a/python/ovs/flowviz/ofp/cli.py b/python/ovs/flowviz/ofp/cli.py > index 6b1435ea1..9658d00d3 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 ( > OpenFlowFactory, > JSONProcessor, > @@ -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 cb4568cf1..9d244d137 100644 > --- a/python/ovs/flowviz/ofp/logic.py > +++ b/python/ovs/flowviz/ofp/logic.py > @@ -200,7 +200,7 @@ class LogicFlowProcessor(OpenFlowFactory, FileProcessor): > if len(self.heat_map) > 0 and len(table.values()) > 0: > for i, field in enumerate(self.heat_map): > (min_val, max_val) = self.min_max[name][i] > - self.console.style.set_value_style( > + formatter.style.set_value_style( This probably needs to move to patch 7. > field, heat_pallete(min_val, max_val) > ) > > @@ -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) > -- > 2.43.0 > > _______________________________________________ > dev mailing list > dev@openvswitch.org > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
On 1/30/24 16:55, Eelco Chaudron wrote: > On 1 Dec 2023, at 20:14, Adrian Moreno wrote: > >> When anaylizing OVN issues, it might be useful to see what OpenFlow >> flows were generated from each logical flow. In order to make it simpler >> to visualize this, add a cookie format that simply sorts the flows first >> by cookie, then by table. > > The code looks good to me, however, did not try with ovs-detrace. One comment on code that needs to move to the previous patch. > > Maybe add an example in the commit message. Sure. > > Acked-by: Eelco Chaudron <echaudro@redhat.com> > >> Signed-off-by: Adrian Moreno <amorenoz@redhat.com> >> --- >> python/ovs/flowviz/ofp/cli.py | 57 ++++++++++++++++++++++++++++- >> python/ovs/flowviz/ofp/logic.py | 63 ++++++++++++++++++++++++++++++++- >> 2 files changed, 118 insertions(+), 2 deletions(-) >> >> diff --git a/python/ovs/flowviz/ofp/cli.py b/python/ovs/flowviz/ofp/cli.py >> index 6b1435ea1..9658d00d3 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 ( >> OpenFlowFactory, >> JSONProcessor, >> @@ -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 cb4568cf1..9d244d137 100644 >> --- a/python/ovs/flowviz/ofp/logic.py >> +++ b/python/ovs/flowviz/ofp/logic.py >> @@ -200,7 +200,7 @@ class LogicFlowProcessor(OpenFlowFactory, FileProcessor): >> if len(self.heat_map) > 0 and len(table.values()) > 0: >> for i, field in enumerate(self.heat_map): >> (min_val, max_val) = self.min_max[name][i] >> - self.console.style.set_value_style( >> + formatter.style.set_value_style( > > This probably needs to move to patch 7. > Yep! >> field, heat_pallete(min_val, max_val) >> ) >> >> @@ -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) >> -- >> 2.43.0 >> >> _______________________________________________ >> dev mailing list >> dev@openvswitch.org >> https://mail.openvswitch.org/mailman/listinfo/ovs-dev >
diff --git a/python/ovs/flowviz/ofp/cli.py b/python/ovs/flowviz/ofp/cli.py index 6b1435ea1..9658d00d3 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 ( OpenFlowFactory, JSONProcessor, @@ -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 cb4568cf1..9d244d137 100644 --- a/python/ovs/flowviz/ofp/logic.py +++ b/python/ovs/flowviz/ofp/logic.py @@ -200,7 +200,7 @@ class LogicFlowProcessor(OpenFlowFactory, FileProcessor): if len(self.heat_map) > 0 and len(table.values()) > 0: for i, field in enumerate(self.heat_map): (min_val, max_val) = self.min_max[name][i] - self.console.style.set_value_style( + formatter.style.set_value_style( field, heat_pallete(min_val, max_val) ) @@ -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)
When anaylizing OVN issues, it might be useful to see what OpenFlow flows were generated from each logical flow. In order to make it simpler to visualize this, add a cookie format that simply sorts the flows first by cookie, then by table. Signed-off-by: Adrian Moreno <amorenoz@redhat.com> --- python/ovs/flowviz/ofp/cli.py | 57 ++++++++++++++++++++++++++++- python/ovs/flowviz/ofp/logic.py | 63 ++++++++++++++++++++++++++++++++- 2 files changed, 118 insertions(+), 2 deletions(-)