From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from dpdk.org (dpdk.org [92.243.14.124]) by inbox.dpdk.org (Postfix) with ESMTP id 9665FA04B0; Sat, 24 Oct 2020 01:31:45 +0200 (CEST) Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 85FCD677D; Sat, 24 Oct 2020 01:31:44 +0200 (CEST) Received: from mail-oi1-f196.google.com (mail-oi1-f196.google.com [209.85.167.196]) by dpdk.org (Postfix) with ESMTP id 46168B62 for ; Sat, 24 Oct 2020 01:31:42 +0200 (CEST) Received: by mail-oi1-f196.google.com with SMTP id f8so3654923oij.10 for ; Fri, 23 Oct 2020 16:31:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=iol.unh.edu; s=unh-iol; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=wlqFoVwDalMpaAF+jj1oqINPm5KnBgZ0ZLUk3wYgbAI=; b=Xv+U+zkGynXAeCt7BkWxwX1k4akLOB6r/4wqQ6zLZ/C3lKeJRE6zlP/iU3kb/U77KJ K641WhUvReCwXqNm/RNeoAzmO41JdZkKiMofLnUPnOE8HGInOsh/RzArKGYWb04UCHoM t3ehpxs7U1HwBMkK/XHyx01oKPZC47SMqtfUw= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=wlqFoVwDalMpaAF+jj1oqINPm5KnBgZ0ZLUk3wYgbAI=; b=NBb21Cwv1Z7u7A4VmtRtXfLV3QyIr2YPfVi3aqd2w9S6N7ZhKTL8xHJe//pye44KZn JlbqoGossyip56KVQxDyDZ2c1pr17YRY6hqLZFqmxLqp+vUIgIvOPkdv76G7jAE9HKUa 8goLfmDnjcTwniHFEjvfZgZi+Da6X+j4nc70Sl5rZrfOUwbhO+Hkh4cuwWI3t4zmvEnh bNDJwH7B2t6vxXaPaRIMJrrQJf8qqew9HsX6q5fRN6LiwMVDXNDqrhYzCABJxHLhf8pN QTgnvDeQe3MTGI8NhPt6qTMcUZyKLI7oNJyYYkE62teYSkFTE99h0rDcJ2OH10HFzTGb w/+w== X-Gm-Message-State: AOAM532UzMobCGIkH6uIP8DPBrNG+amInEKddk8ZDXLaAmIyhgSU18Mh wFAgm3thXvNWOgO7j7INZcCnu7VlKPXK0huLVCg1Hg== X-Google-Smtp-Source: ABdhPJzvf+2P2nU268l6VTowWfHb46C1HC41IavQMwUUrhXVx2R7Vl72JCCZZXRb9OXPgUIOxHqSmtDzEGRuFZFhgWY= X-Received: by 2002:aca:54c3:: with SMTP id i186mr3376961oib.35.1603495900550; Fri, 23 Oct 2020 16:31:40 -0700 (PDT) MIME-Version: 1.0 References: <20201023232903.37387-1-ohilyard@iol.unh.edu> <20201023232903.37387-2-ohilyard@iol.unh.edu> <20201023232903.37387-3-ohilyard@iol.unh.edu> <20201023232903.37387-4-ohilyard@iol.unh.edu> <20201023232903.37387-5-ohilyard@iol.unh.edu> <20201023232903.37387-6-ohilyard@iol.unh.edu> <20201023232903.37387-7-ohilyard@iol.unh.edu> <20201023232903.37387-8-ohilyard@iol.unh.edu> In-Reply-To: <20201023232903.37387-8-ohilyard@iol.unh.edu> From: Owen Hilyard Date: Fri, 23 Oct 2020 19:30:54 -0400 Message-ID: To: Owen Hilyard , dts@dpdk.org, "Tu, Lijuan" Cc: Sarah Hall , "Ma, LihongX" , Lincoln Lavoie , "Chen, Zhaoyan" , "Peng, Yuan" Content-Type: multipart/alternative; boundary="0000000000008d311305b25efb7a" Subject: Re: [dts] [PATCH v2] rte flow: add flow test generator X-BeenThere: dts@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: test suite reviews and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dts-bounces@dpdk.org Sender: "dts" --0000000000008d311305b25efb7a Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable I'm not sure why there's a duplicate of patch 7 attached here, please ignore it. On Fri, Oct 23, 2020 at 7:29 PM Owen Hilyard wrote: > This file contains the actual generation logic for tests. > > The actual implimentation of the generation logic makes heavy use of the > functional programming features in python due to the need to allow delaye= d > execution through the entire chain of items. This is accomplished using a > mixture of generator functions and the aformentioned functional programmi= ng > capabilities. This delayed execution allows a for a much greater number o= f > flow rules to be generated without causing issues in memory, since at mos= t > the program is storing a few dozen stack frames, instead of potentially a > few thousand flow rules. This allows the generation logic to, with the > proper parameters, generate all possible flow rules (ignoring properties)= . > This would normally result in 2.5 million flow rules needing to be reside= nt > in memory before any properties could be changed. The approach of fully > fuzzing the parser was not taken because, by my estimate, it would result > in more than 11,479,792,543,757,705,936,896 flow rules. > > Signed-off-by: Owen Hilyard > --- > framework/flow/generator.py | 165 ++++++++++++++++++++++++++++++++++++ > 1 file changed, 165 insertions(+) > create mode 100644 framework/flow/generator.py > > diff --git a/framework/flow/generator.py b/framework/flow/generator.py > new file mode 100644 > index 0000000..0fe52b2 > --- /dev/null > +++ b/framework/flow/generator.py > @@ -0,0 +1,165 @@ > +# BSD LICENSE > +# > +# Copyright(c) 2020 Intel Corporation. All rights reserved. > +# Copyright =C2=A9 2018[, 2019] The University of New Hampshire. All rig= hts > reserved. > +# All rights reserved. > +# > +# Redistribution and use in source and binary forms, with or without > +# modification, are permitted provided that the following conditions > +# are met: > +# > +# * Redistributions of source code must retain the above copyright > +# notice, this list of conditions and the following disclaimer. > +# * Redistributions in binary form must reproduce the above copyright > +# notice, this list of conditions and the following disclaimer in > +# the documentation and/or other materials provided with the > +# distribution. > +# * Neither the name of Intel Corporation nor the names of its > +# contributors may be used to endorse or promote products derived > +# from this software without specific prior written permission. > +# > +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS > +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT > +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR > +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT > +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, > +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT > +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, > +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY > +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT > +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE > +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. > +from __future__ import annotations > + > +import copy > +import itertools > +import os > +import sys > +import time > +from typing import List, Set, Generator, Iterable, FrozenSet, Tuple > + > +import numpy as np > + > +from flow.flow import Flow > +from flow.flow_pattern_items import PATTERN_ITEMS_TYPE_CLASS_MAPPING, > PatternFlowItem, \ > + PATTERN_OPERATION_TYPES, TUNNELING_PROTOCOL_TYPES, > ALWAYS_ALLOWED_ITEMS, FlowItemEnd, FlowItemVxlan, FlowItemIpv4, \ > + FlowItemEth, FlowItemGre, L3_FLOW_TYPES, FlowItemVlan, FlowItemUdp > +from flow.flow_rule import FlowItemType > + > + > +def get_valid_next_protocols(current_protocol, protocol_stack, > type_denylist): > + return list(filter( > + lambda patent_item: patent_item not in type_denylist and > patent_item not in {p.type for p in protocol_stack}, > + current_protocol.valid_parent_items)) > + > + > +def _generate(type_denylist=3DNone) -> List[List[PatternFlowItem]]: > + if type_denylist is None: > + type_denylist =3D set() > + UNUSED_PATTERN_ITEMS =3D {PATTERN_ITEMS_TYPE_CLASS_MAPPING[i] for i = in > type_denylist} > + > + patterns: List[List[PatternFlowItem]] =3D [] > + for pattern_item in [clazz for clazz in > PATTERN_ITEMS_TYPE_CLASS_MAPPING.values() if > + clazz not in UNUSED_PATTERN_ITEMS]: > + protocol_stack =3D [] > + if protocol_stack.count(pattern_item) >=3D 2: > + continue > + > + current_protocol =3D pattern_item() > + valid_next_protocols =3D get_valid_next_protocols(current_protoc= ol, > protocol_stack, type_denylist) > + while len(valid_next_protocols) > 0: > + protocol_stack.append(current_protocol) > + current_protocol =3D > PATTERN_ITEMS_TYPE_CLASS_MAPPING[list(valid_next_protocols)[0]]() > + valid_next_protocols =3D > get_valid_next_protocols(current_protocol, protocol_stack, type_denylist) > + > + protocol_stack.append(current_protocol) > + > + patterns.append(list(reversed(protocol_stack))) # This will > place the lowest level protocols first > + return patterns > + > + > +def convert_protocol_stack_to_flow_pattern(protocol_stack): > + return Flow(pattern_items=3Dprotocol_stack) > + > + > +def _get_patterns_with_type_denylist(type_denylist: Set): > + return [convert_protocol_stack_to_flow_pattern(protocol_stack) for > protocol_stack in (_generate( > + type_denylist=3Dtype_denylist > + ))] > + > + > +def _get_normal_protocol_patterns() -> List[Flow]: > + return _get_patterns_with_type_denylist( > + PATTERN_OPERATION_TYPES | ALWAYS_ALLOWED_ITEMS | > {FlowItemType.ANY, > + > FlowItemType.END}) > + > + > +def _get_tunnelled_protocol_patterns(patterns: List[Flow]) -> > Generator[Flow]: > + VXLAN_FLOW =3D Flow(pattern_items=3D[FlowItemEth(), FlowItemIpv4(), > FlowItemUdp(), FlowItemVxlan()]) > + for pattern in patterns: > + yield VXLAN_FLOW / pattern > + > + GRE_FLOW =3D Flow(pattern_items=3D[FlowItemEth(), FlowItemIpv4(), > FlowItemGre()]) > + for pattern in patterns: > + if len(pattern.pattern_items) >=3D 2: > + if pattern.pattern_items[1].type in L3_FLOW_TYPES: > + yield GRE_FLOW / pattern > + > + > +def get_patterns() -> Iterable[Iterable[Flow]]: > + patterns: List[Flow] =3D _get_normal_protocol_patterns() > + > + # The flow with only an ethernet header was a consequence of the > + # generation algorithm, but isn't that useful to test since we can't > + # create a failing case without getting each NIC to write arbitrary > + # bytes over the link. > + eth_only_flow =3D Flow(pattern_items=3D[FlowItemEth()]) > + patterns.remove(eth_only_flow) > + > + # tunnelled_patterns =3D _get_tunnelled_protocol_patterns(patterns) > + > + return patterns > + > + > +def add_properties_to_patterns(patterns: Iterable[Flow]) -> > Iterable[Tuple[Flow, FrozenSet[str], FrozenSet[str], str]]: > + test_property_flow_iters =3D map(lambda f: f.get_test_property_flows= (), > patterns) > + for iterator in test_property_flow_iters: > + yield from iterator > + > + > +def get_patterns_with_properties() -> Iterable[Tuple[Flow, > FrozenSet[str], FrozenSet[str], str]]: > + base_patterns =3D get_patterns() > + return add_properties_to_patterns(base_patterns) > + > + > +def create_test_function_strings(test_configurations: > Iterable[Tuple[Flow, FrozenSet[str], FrozenSet[str], str]]) -> \ > + Iterable[str]: > + """ > + This will break if the __str__ methods of frozenset ever changes or > if % formatting syntax is removed. > + > + @param test_configurations: An iterable with test configurations to > convert into test case strings. > + @return: An iterable containing strings that are function parameters= . > + """ > + function_template =3D \ > + """ > +def test_%s(self): > + self.do_test_with_queue_action("%s", %s, %s) > + """ > + return map(lambda test_configuration: function_template % ( > + test_configuration[-1], test_configuration[0], > test_configuration[1], test_configuration[2],), > + test_configurations) > + > + > +def main(): > + """ > + Run this file (python3 generator.py) from the flow directory to prin= t > + out the pattern functions which are normally automatically generated > + and added to the RTE Flow test suite at runtime. > + """ > + pattern_tests =3D list(get_patterns_with_properties()) > + pattern_functions =3D create_test_function_strings(pattern_tests) > + print("\n".join(pattern_functions)) > + > + > +if __name__ =3D=3D "__main__": > + main() > -- > 2.25.1 > > --0000000000008d311305b25efb7a Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
I'm not sure why there's a duplicate of patch 7 at= tached here, please ignore it.=C2=A0

On Fri, Oct 23, 2020 at 7:29 PM Owen Hi= lyard <ohilyard@iol.unh.edu&= gt; wrote:
This = file contains the actual generation logic for tests.

The actual implimentation of the generation logic makes heavy use of the fu= nctional programming features in python due to the need to allow delayed ex= ecution through the entire chain of items. This is accomplished using a mix= ture of generator functions and the aformentioned functional programming ca= pabilities. This delayed execution allows a for a much greater number of fl= ow rules to be generated without causing issues in memory, since at most th= e program is storing a few dozen stack frames, instead of potentially a few= thousand flow rules. This allows the generation logic to, with the proper = parameters, generate all possible flow rules (ignoring properties). This wo= uld normally result in 2.5 million flow rules needing to be resident in mem= ory before any properties could be changed. The approach of fully fuzzing t= he parser was not taken because, by my estimate, it would result in more th= an 11,479,792,543,757,705,936,896 flow rules.

Signed-off-by: Owen Hilyard <ohilyard@iol.unh.edu>
---
=C2=A0framework/flow/generator.py | 165 +++++++++++++++++++++++++++++++++++= +
=C2=A01 file changed, 165 insertions(+)
=C2=A0create mode 100644 framework/flow/generator.py

diff --git a/framework/flow/generator.py b/framework/flow/generator.py
new file mode 100644
index 0000000..0fe52b2
--- /dev/null
+++ b/framework/flow/generator.py
@@ -0,0 +1,165 @@
+# BSD LICENSE
+#
+# Copyright(c) 2020 Intel Corporation. All rights reserved.
+# Copyright =C2=A9 2018[, 2019] The University of New Hampshire. All right= s reserved.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+#=C2=A0 =C2=A0* Redistributions of source code must retain the above copyr= ight
+#=C2=A0 =C2=A0 =C2=A0notice, this list of conditions and the following dis= claimer.
+#=C2=A0 =C2=A0* Redistributions in binary form must reproduce the above co= pyright
+#=C2=A0 =C2=A0 =C2=A0notice, this list of conditions and the following dis= claimer in
+#=C2=A0 =C2=A0 =C2=A0the documentation and/or other materials provided wit= h the
+#=C2=A0 =C2=A0 =C2=A0distribution.
+#=C2=A0 =C2=A0* Neither the name of Intel Corporation nor the names of its=
+#=C2=A0 =C2=A0 =C2=A0contributors may be used to endorse or promote produc= ts derived
+#=C2=A0 =C2=A0 =C2=A0from this software without specific prior written per= mission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT = NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+from __future__ import annotations
+
+import copy
+import itertools
+import os
+import sys
+import time
+from typing import List, Set, Generator, Iterable, FrozenSet, Tuple
+
+import numpy as np
+
+from flow.flow import Flow
+from flow.flow_pattern_items import PATTERN_ITEMS_TYPE_CLASS_MAPPING, Patt= ernFlowItem, \
+=C2=A0 =C2=A0 PATTERN_OPERATION_TYPES, TUNNELING_PROTOCOL_TYPES, ALWAYS_AL= LOWED_ITEMS, FlowItemEnd, FlowItemVxlan, FlowItemIpv4, \
+=C2=A0 =C2=A0 FlowItemEth, FlowItemGre, L3_FLOW_TYPES, FlowItemVlan, FlowI= temUdp
+from flow.flow_rule import FlowItemType
+
+
+def get_valid_next_protocols(current_protocol, protocol_stack, type_denyli= st):
+=C2=A0 =C2=A0 return list(filter(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 lambda patent_item: patent_item not in type_de= nylist and patent_item not in {p.type for p in protocol_stack},
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 current_protocol.valid_parent_items))
+
+
+def _generate(type_denylist=3DNone) -> List[List[PatternFlowItem]]:
+=C2=A0 =C2=A0 if type_denylist is None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 type_denylist =3D set()
+=C2=A0 =C2=A0 UNUSED_PATTERN_ITEMS =3D {PATTERN_ITEMS_TYPE_CLASS_MAPPING[i= ] for i in type_denylist}
+
+=C2=A0 =C2=A0 patterns: List[List[PatternFlowItem]] =3D []
+=C2=A0 =C2=A0 for pattern_item in [clazz for clazz in PATTERN_ITEMS_TYPE_C= LASS_MAPPING.values() if
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0clazz not in UNUSED_PATTERN_ITEMS]:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 protocol_stack =3D []
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if protocol_stack.count(pattern_item) >=3D = 2:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 continue
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 current_protocol =3D pattern_item()
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 valid_next_protocols =3D get_valid_next_protoc= ols(current_protocol, protocol_stack, type_denylist)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 while len(valid_next_protocols) > 0:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 protocol_stack.append(current_pr= otocol)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 current_protocol =3D PATTERN_ITE= MS_TYPE_CLASS_MAPPING[list(valid_next_protocols)[0]]()
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 valid_next_protocols =3D get_val= id_next_protocols(current_protocol, protocol_stack, type_denylist)
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 protocol_stack.append(current_protocol)
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 patterns.append(list(reversed(protocol_stack))= )=C2=A0 # This will place the lowest level protocols first
+=C2=A0 =C2=A0 return patterns
+
+
+def convert_protocol_stack_to_flow_pattern(protocol_stack):
+=C2=A0 =C2=A0 return Flow(pattern_items=3Dprotocol_stack)
+
+
+def _get_patterns_with_type_denylist(type_denylist: Set):
+=C2=A0 =C2=A0 return [convert_protocol_stack_to_flow_pattern(protocol_stac= k) for protocol_stack in (_generate(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 type_denylist=3Dtype_denylist
+=C2=A0 =C2=A0 ))]
+
+
+def _get_normal_protocol_patterns() -> List[Flow]:
+=C2=A0 =C2=A0 return _get_patterns_with_type_denylist(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 PATTERN_OPERATION_TYPES | ALWAYS_ALLOWED_ITEMS= | {FlowItemType.ANY,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 FlowItemType.END})<= br> +
+
+def _get_tunnelled_protocol_patterns(patterns: List[Flow]) -> Generator= [Flow]:
+=C2=A0 =C2=A0 VXLAN_FLOW =3D Flow(pattern_items=3D[FlowItemEth(), FlowItem= Ipv4(), FlowItemUdp(), FlowItemVxlan()])
+=C2=A0 =C2=A0 for pattern in patterns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 yield VXLAN_FLOW / pattern
+
+=C2=A0 =C2=A0 GRE_FLOW =3D Flow(pattern_items=3D[FlowItemEth(), FlowItemIp= v4(), FlowItemGre()])
+=C2=A0 =C2=A0 for pattern in patterns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if len(pattern.pattern_items) >=3D 2:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if pattern.pattern_items[1].type= in L3_FLOW_TYPES:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 yield GRE_FLOW / p= attern
+
+
+def get_patterns() -> Iterable[Iterable[Flow]]:
+=C2=A0 =C2=A0 patterns: List[Flow] =3D _get_normal_protocol_patterns()
+
+=C2=A0 =C2=A0 # The flow with only an ethernet header was a consequence of= the
+=C2=A0 =C2=A0 # generation algorithm, but isn't that useful to test si= nce we can't
+=C2=A0 =C2=A0 # create a failing case without getting each NIC to write ar= bitrary
+=C2=A0 =C2=A0 # bytes over the link.
+=C2=A0 =C2=A0 eth_only_flow =3D Flow(pattern_items=3D[FlowItemEth()])
+=C2=A0 =C2=A0 patterns.remove(eth_only_flow)
+
+=C2=A0 =C2=A0 # tunnelled_patterns =3D _get_tunnelled_protocol_patterns(pa= tterns)
+
+=C2=A0 =C2=A0 return patterns
+
+
+def add_properties_to_patterns(patterns: Iterable[Flow]) -> Iterable[Tu= ple[Flow, FrozenSet[str], FrozenSet[str], str]]:
+=C2=A0 =C2=A0 test_property_flow_iters =3D map(lambda f: f.get_test_proper= ty_flows(), patterns)
+=C2=A0 =C2=A0 for iterator in test_property_flow_iters:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 yield from iterator
+
+
+def get_patterns_with_properties() -> Iterable[Tuple[Flow, FrozenSet[st= r], FrozenSet[str], str]]:
+=C2=A0 =C2=A0 base_patterns =3D get_patterns()
+=C2=A0 =C2=A0 return add_properties_to_patterns(base_patterns)
+
+
+def create_test_function_strings(test_configurations: Iterable[Tuple[Flow,= FrozenSet[str], FrozenSet[str], str]]) -> \
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Iterable[str]:
+=C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 This will break if the __str__ methods of frozenset ever cha= nges or if % formatting syntax is removed.
+
+=C2=A0 =C2=A0 @param test_configurations: An iterable with test configurat= ions to convert into test case strings.
+=C2=A0 =C2=A0 @return: An iterable containing strings that are function pa= rameters.
+=C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 function_template =3D \
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+def test_%s(self):
+=C2=A0 =C2=A0 self.do_test_with_queue_action("%s", %s, %s)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 return map(lambda test_configuration: function_template % (<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 test_configuration[-1], test_configuration[0],= test_configuration[1], test_configuration[2],),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0test_configurations= )
+
+
+def main():
+=C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 Run this file (python3 generator.py) from the flow directory= to print
+=C2=A0 =C2=A0 out the pattern functions which are normally automatically g= enerated
+=C2=A0 =C2=A0 and added to the RTE Flow test suite at runtime.
+=C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 pattern_tests =3D list(get_patterns_with_properties())
+=C2=A0 =C2=A0 pattern_functions =3D create_test_function_strings(pattern_t= ests)
+=C2=A0 =C2=A0 print("\n".join(pattern_functions))
+
+
+if __name__ =3D=3D "__main__":
+=C2=A0 =C2=A0 main()
--
2.25.1

--0000000000008d311305b25efb7a--