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