A ODPFlow is a Flow with the following sections:
ufid
info (e.g: bytes, packets, dp, etc)
match
actions

Only three datapath actions require special handling:
gre: because it has double parenthesis
geneve: because it supports many concatenated lists of options
nat: we reuse the decoder used for openflow actions

Acked-by: Eelco Chaudron <echau...@redhat.com>
Signed-off-by: Adrian Moreno <amore...@redhat.com>
---
 python/automake.mk      |   1 +
 python/ovs/flows/odp.py | 783 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 784 insertions(+)
 create mode 100644 python/ovs/flows/odp.py

diff --git a/python/automake.mk b/python/automake.mk
index 5b0c0d63f..aabbf7db2 100644
--- a/python/automake.mk
+++ b/python/automake.mk
@@ -31,6 +31,7 @@ ovs_pyfiles = \
        python/ovs/flows/flow.py \
        python/ovs/flows/kv.py \
        python/ovs/flows/list.py \
+       python/ovs/flows/odp.py \
        python/ovs/flows/ofp.py \
        python/ovs/flows/ofp_act.py \
        python/ovs/json.py \
diff --git a/python/ovs/flows/odp.py b/python/ovs/flows/odp.py
new file mode 100644
index 000000000..e894f4b96
--- /dev/null
+++ b/python/ovs/flows/odp.py
@@ -0,0 +1,783 @@
+""" Defines an Open vSwitch Datapath Flow.
+"""
+import re
+from functools import partial
+
+from ovs.flows.flow import Flow, Section
+
+from ovs.flows.kv import (
+    KVParser,
+    KVDecoders,
+    nested_kv_decoder,
+    decode_nested_kv,
+)
+from ovs.flows.decoders import (
+    decode_default,
+    decode_time,
+    decode_int,
+    decode_mask,
+    Mask8,
+    Mask16,
+    Mask32,
+    Mask64,
+    Mask128,
+    IPMask,
+    EthMask,
+    decode_free_output,
+    decode_flag,
+    decode_nat,
+)
+
+
+class ODPFlow(Flow):
+    """ODPFLow represents a Open vSwitch Datapath flow.
+
+    Attributes:
+        ufid: The UFID section with only one key-value, with keyword "ufid".
+        info: The info section.
+        match: The match section.
+        actions: The actions section.
+        id: The id object given at construction time.
+
+    """
+
+    """
+    These class variables are used to cache the KVDecoders instances. This
+    will speed up subsequent flow parsings.
+    """
+    _info_decoders = None
+    _match_decoders = None
+    _action_decoders = None
+
+    @staticmethod
+    def info_decoders():
+        """Return the KVDecoders instance to parse the info section.
+
+        Uses the cached version if available.
+        """
+        if not ODPFlow._info_decoders:
+            ODPFlow._info_decoders = ODPFlow._gen_info_decoders()
+        return ODPFlow._info_decoders
+
+    @staticmethod
+    def match_decoders():
+        """Return the KVDecoders instance to parse the match section.
+
+        Uses the cached version if available.
+        """
+        if not ODPFlow._match_decoders:
+            ODPFlow._match_decoders = ODPFlow._gen_match_decoders()
+        return ODPFlow._match_decoders
+
+    @staticmethod
+    def action_decoders():
+        """Return the KVDecoders instance to parse the actions section.
+
+        Uses the cached version if available.
+        """
+        if not ODPFlow._action_decoders:
+            ODPFlow._action_decoders = ODPFlow._gen_action_decoders()
+        return ODPFlow._action_decoders
+
+    def __init__(self, odp_string, id=None):
+        """Parse a odp flow string.
+
+        The string is expected to have the following format:
+             [ufid], [match] [flow data] actions:[actions]
+
+        Args:
+            odp_string (str): A datapath flow string.
+
+        Returns:
+            A ODPFlow instance.
+        """
+        sections = []
+
+        # If UFID present, parse it and add it to it's own section.
+        ufid_pos = odp_string.find("ufid:")
+        if ufid_pos >= 0:
+            ufid_string = odp_string[
+                ufid_pos : (odp_string[ufid_pos:].find(",") + 1)
+            ]
+            ufid_parser = KVParser(
+                ufid_string, KVDecoders({"ufid": decode_default})
+            )
+            ufid_parser.parse()
+            if len(ufid_parser.kv()) != 1:
+                raise ValueError("malformed odp flow: %s" % odp_string)
+            sections.append(
+                Section("ufid", ufid_pos, ufid_string, ufid_parser.kv())
+            )
+
+        action_pos = odp_string.find("actions:")
+        if action_pos < 0:
+            raise ValueError("malformed odp flow: %s" % odp_string)
+
+        # rest of the string is between ufid and actions
+        rest = odp_string[
+            (ufid_pos + len(ufid_string) if ufid_pos >= 0 else 0) : action_pos
+        ]
+
+        action_pos += 8  # len("actions:")
+        actions = odp_string[action_pos:]
+
+        field_parts = rest.lstrip(" ").partition(" ")
+
+        if len(field_parts) != 3:
+            raise ValueError("malformed odp flow: %s" % odp_string)
+
+        match = field_parts[0]
+        info = field_parts[2]
+
+        iparser = KVParser(info, ODPFlow.info_decoders())
+        iparser.parse()
+        isection = Section(
+            name="info",
+            pos=odp_string.find(info),
+            string=info,
+            data=iparser.kv(),
+        )
+        sections.append(isection)
+
+        mparser = KVParser(match, ODPFlow.match_decoders())
+        mparser.parse()
+        msection = Section(
+            name="match",
+            pos=odp_string.find(match),
+            string=match,
+            data=mparser.kv(),
+        )
+        sections.append(msection)
+
+        aparser = KVParser(actions, ODPFlow.action_decoders())
+        aparser.parse()
+        asection = Section(
+            name="actions",
+            pos=action_pos,
+            string=actions,
+            data=aparser.kv(),
+            is_list=True,
+        )
+        sections.append(asection)
+
+        super(ODPFlow, self).__init__(sections, odp_string, id)
+
+    def __str__(self):
+        if self._orig:
+            return self._orig
+        else:
+            return self.to_string()
+
+    def to_string(self):
+        """Return a text representation of the flow."""
+        string = "ufid: {}".format(self.ufid) if self.ufid else ""
+        string += "Info: {} | ".format(self.info)
+        string += "Match : {} | ".format(self.match)
+        string += "Actions: {}".format(self.actions)
+        return string
+
+    @staticmethod
+    def _gen_info_decoders():
+        """Generate the info KVDecoders."""
+        return KVDecoders(ODPFlow._info_decoders_args())
+
+    @staticmethod
+    def _info_decoders_args():
+        """Generate the decoder args for the info KVDecoders."""
+        return {
+            "packets": decode_int,
+            "bytes": decode_int,
+            "used": decode_time,
+            "flags": decode_default,
+            "dp": decode_default,
+        }
+
+    @staticmethod
+    def _gen_action_decoders():
+        """Generate the action KVDecoders."""
+        return KVDecoders(
+            ODPFlow._action_decoders_args(), default_free=decode_free_output
+        )
+
+    @staticmethod
+    def _action_decoders_args():
+        """Generate the arguments for the action KVDecoders."""
+        _decoders = {
+            "drop": decode_flag,
+            "lb_output": decode_int,
+            "trunc": decode_int,
+            "recirc": decode_int,
+            "userspace": nested_kv_decoder(
+                KVDecoders(
+                    {
+                        "pid": decode_int,
+                        "sFlow": nested_kv_decoder(
+                            KVDecoders(
+                                {
+                                    "vid": decode_int,
+                                    "pcp": decode_int,
+                                    "output": decode_int,
+                                }
+                            )
+                        ),
+                        "slow_path": decode_default,
+                        "flow_sample": nested_kv_decoder(
+                            KVDecoders(
+                                {
+                                    "probability": decode_int,
+                                    "collector_sed_id": decode_int,
+                                    "obs_domain_id": decode_int,
+                                    "obs_point_id": decode_int,
+                                    "output_port": decode_default,
+                                    "ingress": decode_flag,
+                                    "egress": decode_flag,
+                                }
+                            )
+                        ),
+                        "ipfix": nested_kv_decoder(
+                            KVDecoders(
+                                {
+                                    "output_port": decode_default,
+                                }
+                            )
+                        ),
+                        "controller": nested_kv_decoder(
+                            KVDecoders(
+                                {
+                                    "reason": decode_int,
+                                    "dont_send": decode_int,
+                                    "continuation": decode_int,
+                                    "recirc_id": decode_int,
+                                    "rule_cookie": decode_int,
+                                    "controller_id": decode_int,
+                                    "max_len": decode_int,
+                                }
+                            )
+                        ),
+                        "userdata": decode_default,
+                        "actions": decode_flag,
+                        "tunnel_out_port": decode_default,
+                        "push_eth": nested_kv_decoder(
+                            KVDecoders(
+                                {
+                                    "src": EthMask,
+                                    "dst": EthMask,
+                                    "type": decode_int,
+                                }
+                            )
+                        ),
+                        "pop_eth": decode_flag,
+                    }
+                )
+            ),
+            "set": nested_kv_decoder(
+                KVDecoders(ODPFlow._field_decoders_args())
+            ),
+            "push_vlan": nested_kv_decoder(
+                KVDecoders(
+                    {
+                        "vid": decode_int,
+                        "pcp": decode_int,
+                        "cfi": decode_int,
+                        "tpid": decode_int,
+                    }
+                )
+            ),
+            "pop_vlan": decode_flag,
+            "push_nsh": nested_kv_decoder(
+                KVDecoders(
+                    {
+                        "flags": decode_int,
+                        "ttl": decode_int,
+                        "mdtype": decode_int,
+                        "np": decode_int,
+                        "spi": decode_int,
+                        "si": decode_int,
+                        "c1": decode_int,
+                        "c2": decode_int,
+                        "c3": decode_int,
+                        "c4": decode_int,
+                        "md2": decode_int,
+                    }
+                )
+            ),
+            "pop_nsh": decode_flag,
+            "tnl_pop": decode_int,
+            "ct_clear": decode_flag,
+            "ct": nested_kv_decoder(
+                KVDecoders(
+                    {
+                        "commit": decode_flag,
+                        "force_commit": decode_flag,
+                        "zone": decode_int,
+                        "mark": Mask32,
+                        "label": Mask128,
+                        "helper": decode_default,
+                        "timeout": decode_default,
+                        "nat": decode_nat,
+                    }
+                )
+            ),
+            **ODPFlow._tnl_action_decoder_args(),
+        }
+
+        _decoders["clone"] = nested_kv_decoder(
+            KVDecoders(decoders=_decoders, default_free=decode_free_output)
+        )
+
+        return {
+            **_decoders,
+            "sample": nested_kv_decoder(
+                KVDecoders(
+                    {
+                        "sample": (lambda x: float(x.strip("%"))),
+                        "actions": nested_kv_decoder(
+                            KVDecoders(
+                                decoders=_decoders,
+                                default_free=decode_free_output,
+                            )
+                        ),
+                    }
+                )
+            ),
+            "check_pkt_len": nested_kv_decoder(
+                KVDecoders(
+                    {
+                        "size": decode_int,
+                        "gt": nested_kv_decoder(
+                            KVDecoders(
+                                decoders=_decoders,
+                                default_free=decode_free_output,
+                            )
+                        ),
+                        "le": nested_kv_decoder(
+                            KVDecoders(
+                                decoders=_decoders,
+                                default_free=decode_free_output,
+                            )
+                        ),
+                    }
+                )
+            ),
+        }
+
+    @staticmethod
+    def _tnl_action_decoder_args():
+        """Generate the decoder arguments for the tunnel actions."""
+        return {
+            "tnl_push": nested_kv_decoder(
+                KVDecoders(
+                    {
+                        "tnl_port": decode_default,
+                        "header": nested_kv_decoder(
+                            KVDecoders(
+                                {
+                                    "size": decode_int,
+                                    "type": decode_int,
+                                    "eth": nested_kv_decoder(
+                                        KVDecoders(
+                                            {
+                                                "src": EthMask,
+                                                "dst": EthMask,
+                                                "dl_type": decode_int,
+                                            }
+                                        )
+                                    ),
+                                    "ipv4": nested_kv_decoder(
+                                        KVDecoders(
+                                            {
+                                                "src": IPMask,
+                                                "dst": IPMask,
+                                                "proto": decode_int,
+                                                "tos": decode_int,
+                                                "ttl": decode_int,
+                                                "frag": decode_int,
+                                            }
+                                        )
+                                    ),
+                                    "ipv6": nested_kv_decoder(
+                                        KVDecoders(
+                                            {
+                                                "src": IPMask,
+                                                "dst": IPMask,
+                                                "label": decode_int,
+                                                "proto": decode_int,
+                                                "tclass": decode_int,
+                                                "hlimit": decode_int,
+                                            }
+                                        )
+                                    ),
+                                    "udp": nested_kv_decoder(
+                                        KVDecoders(
+                                            {
+                                                "src": decode_int,
+                                                "dst": decode_int,
+                                                "dsum": Mask16,
+                                            }
+                                        )
+                                    ),
+                                    "vxlan": nested_kv_decoder(
+                                        KVDecoders(
+                                            {
+                                                "flags": decode_int,
+                                                "vni": decode_int,
+                                            }
+                                        )
+                                    ),
+                                    "geneve": nested_kv_decoder(
+                                        KVDecoders(
+                                            {
+                                                "oam": decode_flag,
+                                                "crit": decode_flag,
+                                                "vni": decode_int,
+                                                "options": partial(
+                                                    decode_geneve, False
+                                                ),
+                                            }
+                                        )
+                                    ),
+                                    "gre": decode_tnl_gre,
+                                    "erspan": nested_kv_decoder(
+                                        KVDecoders(
+                                            {
+                                                "ver": decode_int,
+                                                "sid": decode_int,
+                                                "idx": decode_int,
+                                                "dir": decode_int,
+                                                "hwid": decode_int,
+                                            }
+                                        )
+                                    ),
+                                    "gtpu": nested_kv_decoder(
+                                        KVDecoders(
+                                            {
+                                                "flags": decode_int,
+                                                "msgtype": decode_int,
+                                                "teid": decode_int,
+                                            }
+                                        )
+                                    ),
+                                }
+                            )
+                        ),
+                        "out_port": decode_default,
+                    }
+                )
+            )
+        }
+
+    @staticmethod
+    def _gen_match_decoders():
+        """Generate the match KVDecoders."""
+        return KVDecoders(ODPFlow._match_decoders_args())
+
+    @staticmethod
+    def _match_decoders_args():
+        """Generate the arguments for the match KVDecoders."""
+        return {
+            **ODPFlow._field_decoders_args(),
+            "encap": nested_kv_decoder(
+                KVDecoders(ODPFlow._field_decoders_args())
+            ),
+        }
+
+    @staticmethod
+    def _field_decoders_args():
+        """Generate the decoder arguments for the match fields."""
+        return {
+            "skb_priority": Mask32,
+            "skb_mark": Mask32,
+            "recirc_id": decode_int,
+            "dp_hash": Mask32,
+            "ct_state": decode_default,
+            "ct_zone": Mask16,
+            "ct_mark": Mask32,
+            "ct_label": Mask128,
+            "ct_tuple4": nested_kv_decoder(
+                KVDecoders(
+                    {
+                        "src": IPMask,
+                        "dst": IPMask,
+                        "proto": Mask8,
+                        "tcp_src": Mask16,
+                        "tcp_dst": Mask16,
+                    }
+                )
+            ),
+            "ct_tuple6": nested_kv_decoder(
+                KVDecoders(
+                    {
+                        "src": IPMask,
+                        "dst": IPMask,
+                        "proto": Mask8,
+                        "tcp_src": Mask16,
+                        "tcp_dst": Mask16,
+                    }
+                )
+            ),
+            "tunnel": nested_kv_decoder(
+                KVDecoders(
+                    {
+                        "tun_id": Mask64,
+                        "src": IPMask,
+                        "dst": IPMask,
+                        "ipv6_src": IPMask,
+                        "ipv6_dst": IPMask,
+                        "tos": Mask8,
+                        "ttl": Mask8,
+                        "tp_src": Mask16,
+                        "tp_dst": Mask16,
+                        "erspan": nested_kv_decoder(
+                            KVDecoders(
+                                {
+                                    "ver": Mask8,
+                                    "idx": Mask32,
+                                    "sid": decode_int,
+                                    "dir": Mask8,
+                                    "hwid": Mask8,
+                                }
+                            )
+                        ),
+                        "vxlan": nested_kv_decoder(
+                            KVDecoders(
+                                {
+                                    "gbp": nested_kv_decoder(
+                                        KVDecoders(
+                                            {
+                                                "id": Mask16,
+                                                "flags": Mask8,
+                                            }
+                                        )
+                                    )
+                                }
+                            )
+                        ),
+                        "geneve": partial(decode_geneve, True),
+                        "gtpu": nested_kv_decoder(
+                            KVDecoders(
+                                {
+                                    "flags": Mask8,
+                                    "msgtype": Mask8,
+                                }
+                            )
+                        ),
+                        "flags": decode_default,
+                    }
+                )
+            ),
+            "in_port": decode_default,
+            "eth": nested_kv_decoder(
+                KVDecoders(
+                    {
+                        "src": EthMask,
+                        "dst": EthMask,
+                    }
+                )
+            ),
+            "vlan": nested_kv_decoder(
+                KVDecoders(
+                    {
+                        "vid": Mask16,
+                        "pcp": Mask16,
+                        "cfi": Mask16,
+                    }
+                )
+            ),
+            "eth_type": Mask16,
+            "mpls": nested_kv_decoder(
+                KVDecoders(
+                    {
+                        "label": Mask32,
+                        "tc": Mask32,
+                        "ttl": Mask32,
+                        "bos": Mask32,
+                    }
+                )
+            ),
+            "ipv4": nested_kv_decoder(
+                KVDecoders(
+                    {
+                        "src": IPMask,
+                        "dst": IPMask,
+                        "proto": Mask8,
+                        "tos": Mask8,
+                        "ttl": Mask8,
+                        "frag": decode_default,
+                    }
+                )
+            ),
+            "ipv6": nested_kv_decoder(
+                KVDecoders(
+                    {
+                        "src": IPMask,
+                        "dst": IPMask,
+                        "label": decode_mask(20),
+                        "proto": Mask8,
+                        "tclass": Mask8,
+                        "hlimit": Mask8,
+                        "frag": decode_default,
+                    }
+                )
+            ),
+            "tcp": nested_kv_decoder(
+                KVDecoders(
+                    {
+                        "src": Mask16,
+                        "dst": Mask16,
+                    }
+                )
+            ),
+            "tcp_flags": decode_default,
+            "udp": nested_kv_decoder(
+                KVDecoders(
+                    {
+                        "src": Mask16,
+                        "dst": Mask16,
+                    }
+                )
+            ),
+            "sctp": nested_kv_decoder(
+                KVDecoders(
+                    {
+                        "src": Mask16,
+                        "dst": Mask16,
+                    }
+                )
+            ),
+            "icmp": nested_kv_decoder(
+                KVDecoders(
+                    {
+                        "type": Mask8,
+                        "code": Mask8,
+                    }
+                )
+            ),
+            "icmpv6": nested_kv_decoder(
+                KVDecoders(
+                    {
+                        "type": Mask8,
+                        "code": Mask8,
+                    }
+                )
+            ),
+            "arp": nested_kv_decoder(
+                KVDecoders(
+                    {
+                        "sip": IPMask,
+                        "tip": IPMask,
+                        "op": Mask16,
+                        "sha": EthMask,
+                        "tha": EthMask,
+                    }
+                )
+            ),
+            "nd": nested_kv_decoder(
+                KVDecoders(
+                    {
+                        "target": IPMask,
+                        "sll": EthMask,
+                        "tll": EthMask,
+                    }
+                )
+            ),
+            "nd_ext": nested_kv_decoder(
+                KVDecoders(
+                    {
+                        "nd_reserved": Mask32,
+                        "nd_options_type": Mask8,
+                    }
+                )
+            ),
+            "packet_type": nested_kv_decoder(
+                KVDecoders(
+                    {
+                        "ns": Mask16,
+                        "id": Mask16,
+                    }
+                )
+            ),
+            "nsh": nested_kv_decoder(
+                KVDecoders(
+                    {
+                        "flags": Mask8,
+                        "mdtype": Mask8,
+                        "np": Mask8,
+                        "spi": Mask32,
+                        "si": Mask8,
+                        "c1": Mask32,
+                        "c2": Mask32,
+                        "c3": Mask32,
+                        "c4": Mask32,
+                    }
+                )
+            ),
+        }
+
+
+def decode_geneve(mask, value):
+    """Decode geneve options.
+    Used for both tnl_push(header(geneve(options()))) action and
+    tunnel(geneve()) match.
+
+    It has the following format:
+
+    {class=0xffff,type=0x80,len=4,0xa}
+
+    Args:
+        mask (bool): Whether masking is supported.
+        value (str): The value to decode.
+    """
+    if mask:
+        decoders = {
+            "class": Mask16,
+            "type": Mask8,
+            "len": Mask8,
+        }
+
+        def free_decoder(value):
+            return "data", Mask128(value)
+
+    else:
+        decoders = {
+            "class": decode_int,
+            "type": decode_int,
+            "len": decode_int,
+        }
+
+        def free_decoder(value):
+            return "data", decode_int(value)
+
+    result = []
+    for opts in re.findall(r"{.*?}", value):
+        result.append(
+            decode_nested_kv(
+                KVDecoders(decoders=decoders, default_free=free_decoder),
+                opts.strip("{}"),
+            )
+        )
+    return result
+
+
+def decode_tnl_gre(value):
+    """
+    Decode tnl_push(header(gre())) action.
+
+    It has the following format:
+
+    gre((flags=0x2000,proto=0x6558),key=0x1e241))
+
+    Args:
+        value (str): The value to decode.
+    """
+    return decode_nested_kv(
+        KVDecoders(
+            {
+                "flags": decode_int,
+                "proto": decode_int,
+                "key": decode_int,
+                "csum": decode_int,
+                "seq": decode_int,
+            }
+        ),
+        value.replace("(", "").replace(")", ""),
+    )
-- 
2.34.1

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to