diff mbox series

[ovs-dev] lflow: Add missing sample flow.

Message ID 20240426105258.1911274-1-xsimonar@redhat.com
State Accepted
Headers show
Series [ovs-dev] lflow: Add missing sample flow. | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success

Commit Message

Xavier Simonart April 26, 2024, 10:52 a.m. UTC
If lflow inode had to be recomputed (e.g. due to non_vif_data change), then there could be
some missing sample actions.

This issue was highlighted by some flaky failures of test "Check default openflow flows".

The test "Check default openflow flows" has been updated to create a race-condition
in which ovn-controller handles both ovsdb Flow_Sample_Collector_Set changes and ofport
change for a geneve interface at the same time.

Finally, the test has also been updated as
- It used to print many "printf %s\n: command not found" due to IFS changes.
- It was using a non-existing check_debug function.

Fixes: 5b1476709d7c ("controller: only sample flow if Collector Set exists")

Signed-off-by: Xavier Simonart <xsimonar@redhat.com>
---
 controller/ovn-controller.c | 13 +++++++++
 tests/ovn.at                | 58 ++++++++++++++++++++++++++++++++-----
 2 files changed, 64 insertions(+), 7 deletions(-)

Comments

Mark Michelson April 26, 2024, 8 p.m. UTC | #1
Thank you for this Xavier,

I have one minor suggestion below. It can be fixed when this is merged.

Acked-by: Mark Michelson <mmichels@redhat.com>

On 4/26/24 06:52, Xavier Simonart wrote:
> If lflow inode had to be recomputed (e.g. due to non_vif_data change), then there could be
> some missing sample actions.
> 
> This issue was highlighted by some flaky failures of test "Check default openflow flows".
> 
> The test "Check default openflow flows" has been updated to create a race-condition
> in which ovn-controller handles both ovsdb Flow_Sample_Collector_Set changes and ofport
> change for a geneve interface at the same time.
> 
> Finally, the test has also been updated as
> - It used to print many "printf %s\n: command not found" due to IFS changes.
> - It was using a non-existing check_debug function.
> 
> Fixes: 5b1476709d7c ("controller: only sample flow if Collector Set exists")
> 
> Signed-off-by: Xavier Simonart <xsimonar@redhat.com>
> ---
>   controller/ovn-controller.c | 13 +++++++++
>   tests/ovn.at                | 58 ++++++++++++++++++++++++++++++++-----
>   2 files changed, 64 insertions(+), 7 deletions(-)
> 
> diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
> index 23269af83..a7dff53eb 100644
> --- a/controller/ovn-controller.c
> +++ b/controller/ovn-controller.c
> @@ -4036,6 +4036,8 @@ en_lflow_output_run(struct engine_node *node, void *data)
>           EN_OVSDB_GET(engine_get_input("OVS_bridge", node));
>       const struct ovsrec_bridge *br_int = get_br_int(bridge_table, ovs_table);
>       const char *chassis_id = get_ovs_chassis_id(ovs_table);
> +    const struct ovsrec_flow_sample_collector_set_table *flow_collector_table =
> +        EN_OVSDB_GET(engine_get_input("OVS_flow_sample_collector_set", node));
>   
>       struct ovsdb_idl_index *sbrec_chassis_by_name =
>           engine_ovsdb_node_get_index(
> @@ -4049,6 +4051,17 @@ en_lflow_output_run(struct engine_node *node, void *data)
>   
>       ovs_assert(br_int && chassis);
>   
> +    const struct ovsrec_flow_sample_collector_set *set;
> +    OVSREC_FLOW_SAMPLE_COLLECTOR_SET_TABLE_FOR_EACH (set,
> +                                                    flow_collector_table) {
> +        if (set->bridge == br_int) {
> +            struct ed_type_lflow_output *lfo = data;
> +            flow_collector_ids_clear(&lfo->collector_ids);
> +            flow_collector_ids_init_from_table(&lfo->collector_ids,
> +                                               flow_collector_table);
> +        }
> +    }
> +
>       struct ed_type_lflow_output *fo = data;
>       struct ovn_desired_flow_table *lflow_table = &fo->flow_table;
>       struct ovn_extend_table *group_table = &fo->group_table;
> diff --git a/tests/ovn.at b/tests/ovn.at
> index 79c9524c6..2c8d15134 100644
> --- a/tests/ovn.at
> +++ b/tests/ovn.at
> @@ -35118,7 +35118,10 @@ check_default_flows() {
>   
>   # Check that every drop flow gets sampled.
>   check_sample_drops() {
> -
> +    hv=hv$1
> +    remote_hv=hv$((${1}%2 + 1))
> +    race_condition=$2
> +    ovs-vsctl destroy Flow_Sample_Collector_Set 123
>       check ovn-nbctl -- remove NB_Global . options debug_drop_collector_set \
>                       -- remove NB_Global . options debug_drop_domain_id
>       check ovn-nbctl --wait=hv sync
> @@ -35128,14 +35131,52 @@ check_sample_drops() {
>       # Take match part of flows that contain "drop".
>       drop_matches="$(grep 'drop' oflows_nosample | grep -oP 'table=\d*, priority=.* ')"
>   
> +    if [[ x$race_condition = x"true" ]]; then
> +        sleep_controller $hv
> +        # Get ofport used by the geneve interface
> +        OVS_WAIT_UNTIL([
> +            ofport=$(as $hv ovs-vsctl --bare --columns ofport find Interface name=ovn-${remote_hv}-0)
> +            test 1 -le $ofport
> +        ])
> +
> +        # Add a vif while ovn-controller sleeps, and make it request the ofport used by the geneve interface.
> +        # This used to cause the geneve interface to change ofport.

Suggestion: s/This used to/This is used to/

"This used to" sounds like something was true in the past but is no 
longer true now. I got confused when I looked below and saw that the 
geneve ofport is changing, since the comment makes it sound like that 
shouldn't be happening any more.

Saying "This is used to..." makes it more clear that you are purposely 
invoking a current behavior, rather than documenting a past behavior 
that you are trying to ensure is no longer present.

> +        ovs-vsctl -- add-port br-int vif3 -- set interface vif3\
> +            options:tx_pcap=${hv}/vif3-tx.pcap \
> +            options:rxq_pcap=${hv}/vif3-rx.pcap \
> +            ofport-request=$ofport
> +        OVS_WAIT_UNTIL([
> +            vif_ofport=$(as $hv ovs-vsctl --bare --columns ofport find Interface name=vif3)
> +            test 1 -le $vif_ofport
> +        ])
> +        # For the geneve interface ofport change to happen...
> +        ovs-vsctl -- add-port br-int vif4 -- set interface vif4\
> +            options:tx_pcap=${hv}/vif4-tx.pcap \
> +            options:rxq_pcap=${hv}/vif4-rx.pcap
> +        OVS_WAIT_UNTIL([
> +            new_ofport=$(as $hv ovs-vsctl --bare --columns ofport find Interface name=ovn-${remote_hv}-0)
> +            test $ofport -ne $new_ofport
> +        ])
> +    fi
> +
>       ovs-vsctl --id=@br get Bridge br-int --  \
>           --id=@i create IPFIX targets=\"192.168.1.1\"  -- \
>           create Flow_Sample_Collector_Set bridge=@br id=123 ipfix=@i
>   
>       check ovn-nbctl -- set NB_Global . options:debug_drop_collector_set="123" \
>                       -- set NB_Global . options:debug_drop_domain_id="1"
> -    check ovn-nbctl --wait=hv sync
>   
> +    if [[ x$race_condition = x"true" ]]; then
> +        # Wait sb as ovn-controller sleeps.
> +        check ovn-nbctl --wait=sb sync
> +        # Wake up ovn-controller. It should most of the times receive non-vif ofport change and sb changes at the same time.
> +        wake_up_controller $hv
> +        # Check twice ovn-controller is running to guarantee if run a full loop.
> +        OVS_WAIT_UNTIL([test x$(as $hv ovn-appctl -t ovn-controller debug/status) = "xrunning"])
> +        OVS_WAIT_UNTIL([test x$(as $hv ovn-appctl -t ovn-controller debug/status) = "xrunning"])
> +    else
> +        check ovn-nbctl --wait=hv sync
> +    fi
>       ovs-ofctl dump-flows --no-stats br-int > oflows_sample
>       AT_CAPTURE_FILE([oflows_sample])
>   
> @@ -35143,6 +35184,8 @@ check_sample_drops() {
>       save_IFS=$IFS
>       IFS=$'\n'
>       for flow in $drop_matches; do
> +        # Restore IFS to avoid "printf %s\n: command not found" errors.
> +        IFS=$save_IFS
>           AT_CHECK([grep "${flow}actions=.*sample.*" oflows_sample], [0], [ignore], [ignore], [echo "Flow $flow has a drop and did not get sampled"])
>       done
>       IFS=$save_IFS
> @@ -35155,9 +35198,9 @@ check_drops() {
>       check_default_flows
>   
>       as hv1
> -    check_sample_drops
> +    check_sample_drops 1 $1
>       as hv2
> -    check_sample_drops
> +    check_sample_drops 2 $1
>   }
>   
>   # Logical network:
> @@ -35225,7 +35268,8 @@ ovs-vsctl -- add-port br-int hv2-vif1 -- \
>   wait_for_ports_up
>   check ovn-nbctl --wait=hv sync
>   
> -check_debug
> +check_drops
> +check_drops true
>   
>   # Add stateless ACL
>   check ovn-nbctl --wait=sb \
> @@ -35233,7 +35277,7 @@ check ovn-nbctl --wait=sb \
>   check ovn-nbctl --wait=sb \
>                   -- acl-add ls2 from-lport 100 'ip4' allow-stateless
>   
> -check_debug
> +check_drops
>   
>   check ovn-nbctl --wait=sb acl-del ls1
>   check ovn-nbctl --wait=sb acl-del ls2
> @@ -35244,7 +35288,7 @@ check ovn-nbctl --wait=sb \
>   check ovn-nbctl --wait=sb \
>                   -- acl-add ls2 from-lport 100 "udp" allow-related
>   
> -check_debug
> +check_drops
>   
>   check ovn-nbctl --wait=sb acl-del ls1
>   check ovn-nbctl --wait=sb acl-del ls2
diff mbox series

Patch

diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
index 23269af83..a7dff53eb 100644
--- a/controller/ovn-controller.c
+++ b/controller/ovn-controller.c
@@ -4036,6 +4036,8 @@  en_lflow_output_run(struct engine_node *node, void *data)
         EN_OVSDB_GET(engine_get_input("OVS_bridge", node));
     const struct ovsrec_bridge *br_int = get_br_int(bridge_table, ovs_table);
     const char *chassis_id = get_ovs_chassis_id(ovs_table);
+    const struct ovsrec_flow_sample_collector_set_table *flow_collector_table =
+        EN_OVSDB_GET(engine_get_input("OVS_flow_sample_collector_set", node));
 
     struct ovsdb_idl_index *sbrec_chassis_by_name =
         engine_ovsdb_node_get_index(
@@ -4049,6 +4051,17 @@  en_lflow_output_run(struct engine_node *node, void *data)
 
     ovs_assert(br_int && chassis);
 
+    const struct ovsrec_flow_sample_collector_set *set;
+    OVSREC_FLOW_SAMPLE_COLLECTOR_SET_TABLE_FOR_EACH (set,
+                                                    flow_collector_table) {
+        if (set->bridge == br_int) {
+            struct ed_type_lflow_output *lfo = data;
+            flow_collector_ids_clear(&lfo->collector_ids);
+            flow_collector_ids_init_from_table(&lfo->collector_ids,
+                                               flow_collector_table);
+        }
+    }
+
     struct ed_type_lflow_output *fo = data;
     struct ovn_desired_flow_table *lflow_table = &fo->flow_table;
     struct ovn_extend_table *group_table = &fo->group_table;
diff --git a/tests/ovn.at b/tests/ovn.at
index 79c9524c6..2c8d15134 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -35118,7 +35118,10 @@  check_default_flows() {
 
 # Check that every drop flow gets sampled.
 check_sample_drops() {
-
+    hv=hv$1
+    remote_hv=hv$((${1}%2 + 1))
+    race_condition=$2
+    ovs-vsctl destroy Flow_Sample_Collector_Set 123
     check ovn-nbctl -- remove NB_Global . options debug_drop_collector_set \
                     -- remove NB_Global . options debug_drop_domain_id
     check ovn-nbctl --wait=hv sync
@@ -35128,14 +35131,52 @@  check_sample_drops() {
     # Take match part of flows that contain "drop".
     drop_matches="$(grep 'drop' oflows_nosample | grep -oP 'table=\d*, priority=.* ')"
 
+    if [[ x$race_condition = x"true" ]]; then
+        sleep_controller $hv
+        # Get ofport used by the geneve interface
+        OVS_WAIT_UNTIL([
+            ofport=$(as $hv ovs-vsctl --bare --columns ofport find Interface name=ovn-${remote_hv}-0)
+            test 1 -le $ofport
+        ])
+
+        # Add a vif while ovn-controller sleeps, and make it request the ofport used by the geneve interface.
+        # This used to cause the geneve interface to change ofport.
+        ovs-vsctl -- add-port br-int vif3 -- set interface vif3\
+            options:tx_pcap=${hv}/vif3-tx.pcap \
+            options:rxq_pcap=${hv}/vif3-rx.pcap \
+            ofport-request=$ofport
+        OVS_WAIT_UNTIL([
+            vif_ofport=$(as $hv ovs-vsctl --bare --columns ofport find Interface name=vif3)
+            test 1 -le $vif_ofport
+        ])
+        # For the geneve interface ofport change to happen...
+        ovs-vsctl -- add-port br-int vif4 -- set interface vif4\
+            options:tx_pcap=${hv}/vif4-tx.pcap \
+            options:rxq_pcap=${hv}/vif4-rx.pcap
+        OVS_WAIT_UNTIL([
+            new_ofport=$(as $hv ovs-vsctl --bare --columns ofport find Interface name=ovn-${remote_hv}-0)
+            test $ofport -ne $new_ofport
+        ])
+    fi
+
     ovs-vsctl --id=@br get Bridge br-int --  \
         --id=@i create IPFIX targets=\"192.168.1.1\"  -- \
         create Flow_Sample_Collector_Set bridge=@br id=123 ipfix=@i
 
     check ovn-nbctl -- set NB_Global . options:debug_drop_collector_set="123" \
                     -- set NB_Global . options:debug_drop_domain_id="1"
-    check ovn-nbctl --wait=hv sync
 
+    if [[ x$race_condition = x"true" ]]; then
+        # Wait sb as ovn-controller sleeps.
+        check ovn-nbctl --wait=sb sync
+        # Wake up ovn-controller. It should most of the times receive non-vif ofport change and sb changes at the same time.
+        wake_up_controller $hv
+        # Check twice ovn-controller is running to guarantee if run a full loop.
+        OVS_WAIT_UNTIL([test x$(as $hv ovn-appctl -t ovn-controller debug/status) = "xrunning"])
+        OVS_WAIT_UNTIL([test x$(as $hv ovn-appctl -t ovn-controller debug/status) = "xrunning"])
+    else
+        check ovn-nbctl --wait=hv sync
+    fi
     ovs-ofctl dump-flows --no-stats br-int > oflows_sample
     AT_CAPTURE_FILE([oflows_sample])
 
@@ -35143,6 +35184,8 @@  check_sample_drops() {
     save_IFS=$IFS
     IFS=$'\n'
     for flow in $drop_matches; do
+        # Restore IFS to avoid "printf %s\n: command not found" errors.
+        IFS=$save_IFS
         AT_CHECK([grep "${flow}actions=.*sample.*" oflows_sample], [0], [ignore], [ignore], [echo "Flow $flow has a drop and did not get sampled"])
     done
     IFS=$save_IFS
@@ -35155,9 +35198,9 @@  check_drops() {
     check_default_flows
 
     as hv1
-    check_sample_drops
+    check_sample_drops 1 $1
     as hv2
-    check_sample_drops
+    check_sample_drops 2 $1
 }
 
 # Logical network:
@@ -35225,7 +35268,8 @@  ovs-vsctl -- add-port br-int hv2-vif1 -- \
 wait_for_ports_up
 check ovn-nbctl --wait=hv sync
 
-check_debug
+check_drops
+check_drops true
 
 # Add stateless ACL
 check ovn-nbctl --wait=sb \
@@ -35233,7 +35277,7 @@  check ovn-nbctl --wait=sb \
 check ovn-nbctl --wait=sb \
                 -- acl-add ls2 from-lport 100 'ip4' allow-stateless
 
-check_debug
+check_drops
 
 check ovn-nbctl --wait=sb acl-del ls1
 check ovn-nbctl --wait=sb acl-del ls2
@@ -35244,7 +35288,7 @@  check ovn-nbctl --wait=sb \
 check ovn-nbctl --wait=sb \
                 -- acl-add ls2 from-lport 100 "udp" allow-related
 
-check_debug
+check_drops
 
 check ovn-nbctl --wait=sb acl-del ls1
 check ovn-nbctl --wait=sb acl-del ls2