On 25 Apr 2022, at 17:03, Adrian Moreno wrote:
> On 3/18/22 11:19, Eelco Chaudron wrote:
>>
>>
>> On 11 Mar 2022, at 16:21, Adrian Moreno wrote:
>>
>>> Some openflow or dpif flows encode their arguments in lists, eg:
>>> "some_action(arg1,arg2,arg3)". In order to decode this in a way that can
>>> be then stored and queried, add ListParser and ListDecoders classes
>>> that parse lists into KeyValue instances.
>>>
>>> The ListParser / ListDecoders mechanism is quite similar to KVParser and
>>> KVDecoders. Since the "key" of the different KeyValue objects is now
>>> ommited, it has to be provided by ListDecoders.
>>>
>>> For example, take the openflow action "resubmit" that can be written as:
>>>
>>> resubmit([port],[table][,ct])
>>>
>>> Can be decoded by creating a ListDecoders instance such as:
>>>
>>> ListDecoders([
>>> ("port", decode_default),
>>> ("table", decode_int),
>>> ("ct", decode_flag),
>>> ])
>>>
>>> Naturally, the order of the decoders must be kept.
>>>
>>> Acked-by: Eelco Chaudron <[email protected]>
>>> Signed-off-by: Adrian Moreno <[email protected]>
>>> ---
>>> python/automake.mk | 1 +
>>> python/ovs/flows/list.py | 121 +++++++++++++++++++++++++++++++++++++++
>>> 2 files changed, 122 insertions(+)
>>> create mode 100644 python/ovs/flows/list.py
>>>
>>> diff --git a/python/automake.mk b/python/automake.mk
>>> index 7ce842d66..73438d615 100644
>>> --- a/python/automake.mk
>>> +++ b/python/automake.mk
>>> @@ -29,6 +29,7 @@ ovs_pyfiles = \
>>> python/ovs/flows/__init__.py \
>>> python/ovs/flows/decoders.py \
>>> python/ovs/flows/kv.py \
>>> + python/ovs/flows/list.py \
>>> python/ovs/json.py \
>>> python/ovs/jsonrpc.py \
>>> python/ovs/ovsuuid.py \
>>> diff --git a/python/ovs/flows/list.py b/python/ovs/flows/list.py
>>> new file mode 100644
>>> index 000000000..57e7c5908
>>> --- /dev/null
>>> +++ b/python/ovs/flows/list.py
>>> @@ -0,0 +1,121 @@
>>> +import re
>>> +
>>> +from ovs.flows.kv import KeyValue, KeyMetadata, ParseError
>>> +from ovs.flows.decoders import decode_default
>>> +
>>> +
>>> +class ListDecoders(object):
>>> + """ListDecoders is used by ListParser to decode the elements in the
>>> list.
>>> +
>>> + A decoder is a function that accepts a value and returns its decoded
>>> + object.
>>> +
>>> + ListDecoders is initialized with a list of tuples that contains the
>>> + keyword and the decoding function associated with each position in the
>>> + list. The order is, therefore, important.
>>> +
>>> + Args:
>>> + decoders (list of tuples): Optional; a list of tuples.
>>> + The first element in the tuple is the keyword associated with
>>> the
>>> + value. The second element in the tuple is the decoder function.
>>> + """
>>> +
>>> + def __init__(self, decoders=None):
>>> + self._decoders = decoders or list()
>>> +
>>> + def decode(self, index, value_str):
>>> + """Decode the index'th element of the list.
>>> +
>>> + Args:
>>> + index (int): The position in the list of the element to decode.
>>> + value_str (str): The value string to decode.
>>> + """
>>> + if index < 0 or index >= len(self._decoders):
>>> + return self._default_decoder(index, value_str)
>>> +
>>> + try:
>>> + key = self._decoders[index][0]
>>> + value = self._decoders[index][1](value_str)
>>> + return key, value
>>> + except Exception as e:
>>> + raise ParseError(
>>> + "Failed to decode value_str {}: {}".format(value_str,
>>> str(e))
>>> + )
>>> +
>>> + @staticmethod
>>> + def _default_decoder(index, value):
>>> + key = "elem_{}".format(index)
>>> + return key, decode_default(value)
>>> +
>>> +
>>> +class ListParser(object):
>>> + """ListParser parses a list of values and stores them as key-value
>>> pairs.
>>> +
>>> + It uses a ListDecoders instance to decode each element in the list.
>>> +
>>> + Args:
>>> + string (str): The string to parse.
>>> + decoders (ListDecoders): Optional, the decoders to use.
>>> + delims (list): Optional, list of delimiters of the list. Defaults
>>> to
>>> + [','].
>>> + """
>>> + def __init__(self, string, decoders=None, delims=[","]):
>>> + self._string = string
>>> + self._decoders = decoders or ListDecoders()
>>> + self._keyval = list()
>>> + self._regexp = r"({})".format("|".join(delims))
>>> +
>>> + def kv(self):
>>> + return self._keyval
>>> +
>>> + def __iter__(self):
>>> + return iter(self._keyval)
>>> +
>>> + def parse(self):
>>> + """Parse the list in string.
>>> +
>>> + Raises:
>>> + ParseError if any parsing error occurs.
>>> + """
>>> + kpos = 0
>>> + index = 0
>>> + while kpos < len(self._string) and self._string[kpos] != "\n":
>>> + split_parts = re.split(self._regexp, self._string[kpos:], 1)
>>
>> Was there a specific reason why the below code was removed? Are we accepting
>> the exception in this case?
>>
>> -+ if len(split_parts) == 0:
>> -+ break
>>
>
> Sorry I missed this comment.
> The reason for dropping this is, as Mark Michelson pointed out [1], because
> re.split() does not return a zero length list. If there is no match, it
> returns a list of 1 element with the remainder string.
>
> [1]
> https://patchwork.ozlabs.org/project/openvswitch/patch/[email protected]/#2834381
ACK this makes sense!
Acked-by: Eelco Chaudron <[email protected]>
>>> + value_str = split_parts[0]
>>> +
>>> + key, value = self._decoders.decode(index, value_str)
>>> +
>>> + meta = KeyMetadata(
>>> + kpos=kpos,
>>> + vpos=kpos,
>>> + kstring=value_str,
>>> + vstring=value_str,
>>> + )
>>> + self._keyval.append(KeyValue(key, value, meta))
>>> +
>>> + kpos += len(value_str) + 1
>>> + index += 1
>>> +
>>> +
>>> +def decode_nested_list(decoders, value, delims=[","]):
>>> + """Decodes a string value that contains a list of elements and returns
>>> + them in a dictionary.
>>> +
>>> + Args:
>>> + decoders (ListDecoders): The ListDecoders to use.
>>> + value (str): The value string to decode.
>>> + delims (list(str)): Optional, the list of delimiters to use.
>>> + """
>>> + parser = ListParser(value, decoders, delims)
>>> + parser.parse()
>>> + return {kv.key: kv.value for kv in parser.kv()}
>>> +
>>> +
>>> +def nested_list_decoder(decoders=None, delims=[","]):
>>> + """Helper function that creates a nested list decoder with given
>>> + ListDecoders and delimiters.
>>> + """
>>> + def decoder(value):
>>> + return decode_nested_list(decoders, value, delims)
>>> +
>>> + return decoder
>>> --
>>> 2.34.1
>>
>
> --
> Adrián Moreno
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev