On 5/21/24 23:56, Ilya Maximets wrote: > On 5/21/24 20:56, [email protected] wrote: >> On Tue, May 21, 2024 at 08:03:49PM GMT, Ilya Maximets wrote: >>> On 5/21/24 18:13, [email protected] wrote: >>>> On Tue, May 21, 2024 at 03:49:03PM GMT, [email protected] wrote: >>>>> On Tue, May 21, 2024 at 01:51:33PM GMT, Ilya Maximets wrote: >>>>>> On 5/7/24 16:30, Adrian Moreno wrote: >>>>>>> The goal of this utility is to read both datapath and Openflow flows >>>>>>> (using the flow library already available) and print them in different >>>>>>> formats and styles to make it easier to understand them and troubleshoot >>>>>>> issues. >>>>>>> >>>>>>> The formats are quite opinionated and so are the colors chosen so I'm >>>>>>> eager to hear what is the impression caused to the community. >>>>>>> >>>>>>> Here is a summary of the formats and features supported: >>>>>>> >>>>>>> - Openflow >>>>>>> - Console: Prints flows back to the console allowing filtering and >>>>>>> extensive formatting. >>>>>>> - Cookie: Arranges flows based on cookie (instead of table) to ease >>>>>>> visualization of OVN-generated flows. >>>>>>> - HTML: Prints an HTML file that shows the flows arranged by tables. >>>>>>> resubmit actions have a hyperlinke to the target table to ease >>>>>>> navegation. >>>>>>> - Logic: Many times OVN generates many "logically-equivalent" flows. >>>>>>> These are flows that have the same structure: match on the same >>>>>>> values and have the same actions. The only thing that changes is >>>>>>> the actual value they match against and maybe the arguments of the >>>>>>> actions. This format collapses these flows so you can visualize the >>>>>>> logical pipeline easier. >>>>>>> - JSON: JSON format. >>>>>>> >>>>>>> More OpenFlow features: >>>>>>> - OVN metadata: Both the "cookie" and the "logic" format allow to >>>>>>> connect to a running OVN NB/SB databases and enrich the flows with >>>>>>> OVN context based on the flow's cookie. >>>>>>> >>>>>>> - Datapath: >>>>>>> - Console: Prints flows back to the console allowing filtering and >>>>>>> extensive formatting. >>>>>>> - Tree: Datapath flows use recirculation to further execute >>>>>>> additional actions. This format builds a tree of flows following >>>>>>> the recirculation identifiers and prints it in the console. >>>>>>> - HTML: Prints the datapath flow tree in HTML. It includes some >>>>>>> minimal JS to support expanding and collapsing of entire branches. >>>>>>> - Graph: Following the "tree" format, this one prints the tree in >>>>>>> graphviz format. >>>>>>> - JSON: JSON format. >>>>>>> >>>>>>> Additional datapath features: >>>>>>> - Many datapath formats are based on the tree flow hierarchy. An >>>>>>> interesting feature of this structure is that filtering is done at >>>>>>> the branch level. This means that when a flow satisfies the filter, >>>>>>> the entire sub-tree leading to that flow is shown. >>>>>>> >>>>>>> Additional common features: >>>>>>> - Styling: Styles for both console and HTML formats can be defined >>>>>>> using a configuration file. >>>>>>> - Heat maps: Some formats support heat-maps. A color pallete ranging >>>>>>> from blue (cold) to red (hot) is used to print the number of >>>>>>> packets and bytes of the flows. That way, the flows that are >>>>>>> handling more traffic are much easier to spot >>>>>>> >>>>>>> -- >>>>>>> V3 -> V4: >>>>>>> - Add manpage to rpm package >>>>>>> - Fix Eelco's comments >>>>>>> V2 -> V3: >>>>>>> - Fix grammar thanks to Eelco's review >>>>>>> V1 -> V2: >>>>>>> - Fix typos and nits on documentation >>>>>>> - Split documentation in two: ovs-flowviz.8 man page and a topic >>>>>>> article with more detailed examples. >>>>>>> RFC -> V1: >>>>>>> - Addressed Eelco's comments >>>>>>> - Added a documentation page >>>>>>> - Added support for dark style HTML pages >>>>>>> - Patch 3. Small fix in the way a style is looked up. Use the key in >>>>>>> the KV instead of the metadata string. This helps with "free" values >>>>>>> such as "output". >>>>>>> >>>>>>> Adrian Moreno (12): >>>>>>> python: ovs: Add flowviz scheleton. >>>>>>> python: ovs: flowviz: Add file processing infra. >>>>>>> python: ovs: flowviz: Add console formatting. >>>>>>> python: ovs: flowviz: Add default config file. >>>>>>> python: ovs: flowviz: Add html formatting. >>>>>>> python: ovs: flowviz: Add datapath tree format. >>>>>>> python: ovs: flowviz: Add OpenFlow logical view. >>>>>>> python: ovs: flowviz: Add Openflow cookie format. >>>>>>> python: ovs: flowviz: Add datapath html format. >>>>>>> python: ovs: flowviz: Add datapath graph format. >>>>>>> python: ovs: flowviz: Support html dark style. >>>>>>> documentation: Document ovs-flowviz. >>>>>>> >>>>>>> Documentation/automake.mk | 4 +- >>>>>>> Documentation/conf.py | 2 + >>>>>>> Documentation/ref/index.rst | 1 + >>>>>>> Documentation/ref/ovs-flowviz.8.rst | 531 ++++++++++++++++++++ >>>>>>> Documentation/topics/flow-visualization.rst | 271 ++++++++++ >>>>>>> Documentation/topics/index.rst | 1 + >>>>>>> python/automake.mk | 32 +- >>>>>>> python/ovs/flowviz/__init__.py | 2 + >>>>>>> python/ovs/flowviz/console.py | 196 ++++++++ >>>>>>> python/ovs/flowviz/format.py | 371 ++++++++++++++ >>>>>>> python/ovs/flowviz/html_format.py | 138 +++++ >>>>>>> python/ovs/flowviz/main.py | 196 ++++++++ >>>>>>> python/ovs/flowviz/odp/__init__.py | 0 >>>>>>> python/ovs/flowviz/odp/cli.py | 116 +++++ >>>>>>> python/ovs/flowviz/odp/graph.py | 418 +++++++++++++++ >>>>>>> python/ovs/flowviz/odp/html.py | 279 ++++++++++ >>>>>>> python/ovs/flowviz/odp/tree.py | 303 +++++++++++ >>>>>>> python/ovs/flowviz/ofp/__init__.py | 0 >>>>>>> python/ovs/flowviz/ofp/cli.py | 246 +++++++++ >>>>>>> python/ovs/flowviz/ofp/html.py | 98 ++++ >>>>>>> python/ovs/flowviz/ofp/logic.py | 364 ++++++++++++++ >>>>>>> python/ovs/flowviz/ovs-flowviz | 20 + >>>>>>> python/ovs/flowviz/ovs-flowviz.conf | 128 +++++ >>>>>>> python/ovs/flowviz/process.py | 263 ++++++++++ >>>>>>> python/setup.py | 14 +- >>>>>>> rhel/openvswitch-fedora.spec.in | 1 + >>>>>>> rhel/openvswitch.spec.in | 1 + >>>>>>> 27 files changed, 3986 insertions(+), 10 deletions(-) >>>>>>> create mode 100644 Documentation/ref/ovs-flowviz.8.rst >>>>>>> create mode 100644 Documentation/topics/flow-visualization.rst >>>>>>> create mode 100644 python/ovs/flowviz/__init__.py >>>>>>> create mode 100644 python/ovs/flowviz/console.py >>>>>>> create mode 100644 python/ovs/flowviz/format.py >>>>>>> create mode 100644 python/ovs/flowviz/html_format.py >>>>>>> create mode 100644 python/ovs/flowviz/main.py >>>>>>> create mode 100644 python/ovs/flowviz/odp/__init__.py >>>>>>> create mode 100644 python/ovs/flowviz/odp/cli.py >>>>>>> create mode 100644 python/ovs/flowviz/odp/graph.py >>>>>>> create mode 100644 python/ovs/flowviz/odp/html.py >>>>>>> create mode 100644 python/ovs/flowviz/odp/tree.py >>>>>>> create mode 100644 python/ovs/flowviz/ofp/__init__.py >>>>>>> create mode 100644 python/ovs/flowviz/ofp/cli.py >>>>>>> create mode 100644 python/ovs/flowviz/ofp/html.py >>>>>>> create mode 100644 python/ovs/flowviz/ofp/logic.py >>>>>>> create mode 100755 python/ovs/flowviz/ovs-flowviz >>>>>>> create mode 100644 python/ovs/flowviz/ovs-flowviz.conf >>>>>>> create mode 100644 python/ovs/flowviz/process.py >>>>>>> >>>>>> >>>>>> Hi, Adrian. Thanks for v4! >>>>>> >>>>>> Not a full review, but I was trying to use this version of flowviz today >>>>>> on a real flow dump from an ovn-kubernetes cluster and it crashes unable >>>>>> to parse check_pkt_len datapath actions: >>>>>> >>>>>> # ovs-flowviz -i snippet1.txt datapath tree >>>>>> >>>>>> Traceback (most recent call last): >>>>>> File "/python/ovs/flow/kv.py", line 294, in parse >>>>>> key, val = self._decoders.decode(keyword, value_str) >>>>>> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >>>>>> File "/python/ovs/flow/kv.py", line 150, in decode >>>>>> raise ParseError( >>>>>> ovs.flow.kv.ParseError: Cannot parse key check_pkt_len: No decoder found >>> >>> <snip> >>> >>>>>> >>>>>> Not the exact same line, but a similar crash happens while parsing: >>>>>> >>>>>> recirc_id(0x204ae73),in_port(20),ct_state(+new-est-rel-rpl-inv+trk),ct_mark(0/0xffff000f),eth(src=1a:1b:1c:1d:18:18,dst=2a:2b:2c:2d:2e:2f),eth_type(0x0800),ipv4(src=192.168.10.1/255.255.255.254,dst=10.64.0.0/255.192.0.0,proto=6,ttl=64,frag=no), >>>>>> packets:7, bytes:518, used:9.077s, flags:S, >>>>>> actions:ct(commit,zone=269,mark=0/0x1,nat(src)),set(eth(src=3a:3b:3c:3d:3e:3f,dst=4a:4b:4c:4d:4e:4f)),set(ipv4(ttl=63)),check_pkt_len(size=8514,gt(sample(sample=100.0%,actions(meter(4),userspace(pid=4294967295,controller(reason=1,dont_send=0,continuation=0,recirc_id=29551991,rule_cookie=0x769b5340,controller_id=0,max_len=65535))))),le(set(eth(src=0a:0b:0c:0d:08:08,dst=aa:bb:cc:dd:ee:ff)),set(ipv4(ttl=62)),check_pkt_len(size=8514,gt(sample(sample=100.0%,actions(meter(4),userspace(pid=4294967295,controller(reason=1,dont_send=0,continuation=0,recirc_id=33861236,rule_cookie=0x9ac6aa33,controller_id=0,max_len=65535))))),le(ct(zone=12,nat),recirc(0x204ae75))))) >>>>>> >>>>>> My guess is that it has some trouble with nested check_pkt_len. >>>>>> >>>>> >>>>> Thanks. I'll take a look. >>>>> >>>> >>>> If you still have the original flowdump handy, could you please try this >>>> patch? >>>> >>>> --- >>>> diff --git a/python/ovs/flow/odp.py b/python/ovs/flow/odp.py >>>> index 7d9b165d4..a8f8c067a 100644 >>>> --- a/python/ovs/flow/odp.py >>>> +++ b/python/ovs/flow/odp.py >>>> @@ -365,29 +365,30 @@ class ODPFlow(Flow): >>> >>> <snip> >>> >>> Yeah, that worked. I'm not sure if the result is more readbale than it was >>> in a plain >>> text, but it worked. :) >>> >> >> Sorry it didn't help. > > It's about 500 flows and they are not pretty as seen above. So, it's hard to > make them look good. > >> Do you have any suggestion (apart from the one >> below)? >> >>> The 'tree' view is a bit confusing because it only takes into account >>> recirculation id >>> and doesn't care about other fields being completely different. So, it >>> draws a tree >>> with a lot of impossible branches... Do you think there is a way to fix >>> that? >>> >> >> Without implementing a matching engine, getting 100% accuracy is tough. >> I added filtering and highlighting to assist but at the end it's true you >> have >> to mentally do the matching. > > Yeah. In general case for a 100% accuracy we would have to implement parts > of the datapath, e.g. execute the actions. > > However, some things are immutable and do not support masked matches. Some > of these are also always present. For example, in_port() is always there > and it cannot be changed by actions, so we can avoid trees like this: > > │ ├── recirc_id(0x1c43aff),in_port(18)... > │ │ actions:...recirc(0x204b096) > │ │ └── recirc_id(0x204b096),in_port(18), > │ │ actions:set(ipv4(ttl=62)),hash(l4(0)),recirc(0x204b097) > │ │ ├── recirc_id(0x204b097),dp_hash(0x8/0xf),in_port(18)... > │ │ │ actions:...recirc(0x1c2f203) > │ │ │ ├── recirc_id(0x1c2f203),in_port(14)... > │ │ │ ├── recirc_id(0x1c2f203),in_port(19)... > │ │ │ ├── recirc_id(0x1c2f203),in_port(23)... > │ │ │ ├── recirc_id(0x1c2f203),in_port(18)... > │ │ │ ├── recirc_id(0x1c2f203),in_port(19)... > │ │ │ ├── recirc_id(0x1c2f203),in_port(14)... > │ │ │ ├── recirc_id(0x1c2f203),in_port(18)... > │ │ │ ├── recirc_id(0x1c2f203),in_port(14)... > │ │ │ └── recirc_id(0x1c2f203),in_port(18)... > │ │ ├── recirc_id(0x204b097),dp_hash(0x4/0xf),in_port(18) > │ │ │ ├── recirc_id(0x1c2f203),in_port(14)... > │ │ │ ├── recirc_id(0x1c2f203),in_port(19)... > │ │ │ ├── recirc_id(0x1c2f203),in_port(23)... > │ │ │ ├── recirc_id(0x1c2f203),in_port(18)... > │ │ │ ├── recirc_id(0x1c2f203),in_port(19)... > │ │ │ ├── recirc_id(0x1c2f203),in_port(14)... > │ │ │ ├── recirc_id(0x1c2f203),in_port(18)... > │ │ │ ├── recirc_id(0x1c2f203),in_port(14)... > │ │ │ └── recirc_id(0x1c2f203),in_port(18)... > > > Above is heavily abridged, but yo can see that only 4 out of 18 flows > at the last level match on the same input port. This may be optimized. > > In general it just blows out the total output from 500 flows into 18000 > lines of a tree output in my case.
Correction: this is related to repetitions in general, removing flows based on in_port alone will reduce the 18K number, but it will sill be way far from 500. > > There are trees where on some level many flows have exact same actions > with slightly different matches. I wonder if these can be groupped to > only list the actions once. This is beneficial especially if those > actions have reirculation in them, so it will also not be repeated. > >> >> I also thought of having some interactivity (I added some very basic one >> in the html format) so you could manually remove the branches you're not >> interested in. But that's the only thin > > Yeah, I'd really like interactive filtering... maybe even not interactive for > the starters. Maybe just a string match or regex instead of packet-aware > filtering. The way it should work is to go through the tree and print out > only paths that contain a match, i.e. all parents of the matching flow > and children. Basically, cut out the siblings. For exmaple, something like > --filter="in_port(18)" should only print trees that match on port 18. > This is an easy one, since input port is always in the flow. > > A big problem I'm also facing is that grep doesn't work due to coloring. > And if I remove the colors for grep I will not be able to add them back, > so grep filtering kind of has to be supported natively in ovs-flowviz. > > All in all, what we all want is just have retis print out datapath flows as > packets match them. :D So, we can pcap-filter a TCP stream and see all > the datapath flows it matched and actions executed. > > Best regards, Ilya Maximets. > >> >> BTW. Thanks for trying it out and giving feedback. >> >> -- >> Adrián >> > _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
