Sometimes we don't want to return the result of a nested key-value
decoding as a dictionary but as a list of dictionaries. This happens
when we parse actions where keys can be repeated.

Refactor code that already takes that into account from ofp_act.py to
kv.py and use it for datapath action "clone".

Signed-off-by: Adrian Moreno <amore...@redhat.com>
---
 python/ovs/flow/kv.py        | 21 +++++++++++++++++++-
 python/ovs/flow/odp.py       |  6 ++++--
 python/ovs/flow/ofp.py       | 14 ++++++-------
 python/ovs/flow/ofp_act.py   | 18 +----------------
 python/ovs/tests/test_odp.py | 38 +++++++++++++++++++++++++-----------
 5 files changed, 59 insertions(+), 38 deletions(-)

diff --git a/python/ovs/flow/kv.py b/python/ovs/flow/kv.py
index 32463254b..3138db008 100644
--- a/python/ovs/flow/kv.py
+++ b/python/ovs/flow/kv.py
@@ -320,7 +320,26 @@ def decode_nested_kv(decoders, value):
     return {kv.key: kv.value for kv in parser.kv()}
 
 
-def nested_kv_decoder(decoders=None):
+def decode_nested_kv_list(decoders, value):
+    """A key-value decoder that extracts nested key-value pairs and returns
+    them in a list of dictionary.
+
+    Args:
+        decoders (KVDecoders): The KVDecoders to use.
+        value (str): The value string to decode.
+    """
+    if not value:
+        # Mark as flag
+        return True
+
+    parser = KVParser(value, decoders)
+    parser.parse()
+    return [{kv.key: kv.value} for kv in parser.kv()]
+
+
+def nested_kv_decoder(decoders=None, is_list=False):
     """Helper function that creates a nested kv decoder with given
     KVDecoders."""
+    if is_list:
+        return functools.partial(decode_nested_kv_list, decoders)
     return functools.partial(decode_nested_kv, decoders)
diff --git a/python/ovs/flow/odp.py b/python/ovs/flow/odp.py
index 3bc3aec8e..db63afc8d 100644
--- a/python/ovs/flow/odp.py
+++ b/python/ovs/flow/odp.py
@@ -337,7 +337,8 @@ class ODPFlow(Flow):
         }
 
         _decoders["clone"] = nested_kv_decoder(
-            KVDecoders(decoders=_decoders, default_free=decode_free_output)
+            KVDecoders(decoders=_decoders, default_free=decode_free_output),
+            is_list=True,
         )
 
         return {
@@ -350,7 +351,8 @@ class ODPFlow(Flow):
                             KVDecoders(
                                 decoders=_decoders,
                                 default_free=decode_free_output,
-                            )
+                            ),
+                            is_list=True,
                         ),
                     }
                 )
diff --git a/python/ovs/flow/ofp.py b/python/ovs/flow/ofp.py
index 4ebb97df0..bc9cc5de6 100644
--- a/python/ovs/flow/ofp.py
+++ b/python/ovs/flow/ofp.py
@@ -31,7 +31,6 @@ from ovs.flow.ofp_act import (
     decode_dec_ttl,
     decode_chk_pkt_larger,
     decode_zone,
-    decode_exec,
     decode_learn,
 )
 
@@ -337,8 +336,7 @@ class OFPFlow(Flow):
                         "table": decode_int,
                         "nat": decode_nat,
                         "force": decode_flag,
-                        "exec": functools.partial(
-                            decode_exec,
+                        "exec": nested_kv_decoder(
                             KVDecoders(
                                 {
                                     **OFPFlow._encap_actions_decoders_args(),
@@ -346,6 +344,7 @@ class OFPFlow(Flow):
                                     **OFPFlow._meta_action_decoders_args(),
                                 }
                             ),
+                            is_list=True,
                         ),
                         "alg": decode_default,
                     }
@@ -360,6 +359,7 @@ class OFPFlow(Flow):
                     }
                 )
             ),
+            # learn moved to _clone actions.
         }
 
     @staticmethod
@@ -401,11 +401,11 @@ class OFPFlow(Flow):
         """
         return {
             "learn": decode_learn(action_decoders),
-            "clone": functools.partial(
-                decode_exec, KVDecoders(action_decoders)
+            "clone": nested_kv_decoder(
+                KVDecoders(action_decoders), is_list=True
             ),
-            "write_actions": functools.partial(
-                decode_exec, KVDecoders(action_decoders)
+            "write_actions": nested_kv_decoder(
+                KVDecoders(action_decoders), is_list=True
             ),
         }
 
diff --git a/python/ovs/flow/ofp_act.py b/python/ovs/flow/ofp_act.py
index c481d6fc7..5eaf0b218 100644
--- a/python/ovs/flow/ofp_act.py
+++ b/python/ovs/flow/ofp_act.py
@@ -1,8 +1,5 @@
 """Defines decoders for OpenFlow actions.
 """
-
-import functools
-
 from ovs.flow.decoders import (
     decode_default,
     decode_time,
@@ -258,19 +255,6 @@ def decode_zone(value):
     return decode_field(value)
 
 
-def decode_exec(action_decoders, value):
-    """Decodes the value of the 'exec' keyword (part of the ct action).
-
-    Args:
-        decode_actions (KVDecoders): The decoders to be used to decode the
-            nested exec.
-        value (string): The string to be decoded.
-    """
-    exec_parser = KVParser(value, action_decoders)
-    exec_parser.parse()
-    return [{kv.key: kv.value} for kv in exec_parser.kv()]
-
-
 def decode_learn(action_decoders):
     """Create the decoder to be used to decode the 'learn' action.
 
@@ -338,4 +322,4 @@ def decode_learn(action_decoders):
         default_free=learn_field_decoding_free,
     )
 
-    return functools.partial(decode_exec, learn_decoder)
+    return nested_kv_decoder(learn_decoder, is_list=True)
diff --git a/python/ovs/tests/test_odp.py b/python/ovs/tests/test_odp.py
index 715be3869..f8017ca8a 100644
--- a/python/ovs/tests/test_odp.py
+++ b/python/ovs/tests/test_odp.py
@@ -453,21 +453,37 @@ def test_odp_fields(input_string, expected):
             ],
         ),
         (
-            "actions:clone(1)" ",clone(clone(push_vlan(vid=12,pcp=0),2),1)",
+            "actions:clone(1),clone(clone(push_vlan(vid=12,pcp=0),2),1)",
             [
-                KeyValue("clone", {"output": {"port": 1}}),
+                KeyValue("clone", [{"output": {"port": 1}}]),
                 KeyValue(
                     "clone",
-                    {
-                        "output": {"port": 1},
-                        "clone": {
-                            "push_vlan": {
-                                "vid": 12,
-                                "pcp": 0,
-                            },
-                            "output": {"port": 2},
+                    [
+                        {
+                            "clone": [
+                                {
+                                    "push_vlan": {
+                                        "vid": 12,
+                                        "pcp": 0,
+                                    },
+                                },
+                                {"output": {"port": 2}},
+                            ]
                         },
-                    },
+                        {"output": {"port": 1}},
+                    ],
+                ),
+            ],
+        ),
+        (
+            "actions:clone(recirc(0x1),recirc(0x2))",
+            [
+                KeyValue(
+                    "clone",
+                    [
+                        {"recirc": 1},
+                        {"recirc": 2},
+                    ],
                 ),
             ],
         ),
-- 
2.37.3

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

Reply via email to