On Mon, Nov 21, 2022 at 4:40 AM Xavier Simonart <[email protected]> wrote: > > In the following scenario: > - interface "old" is created and external_ids:iface-id is set (to lp) > - interface "new" is created and external_ids:iface-id is set (to same lp) > - interface "old" is deleted > flows related to lp were deleted. > > Note that after "new" interface is created, flows use "new" ofport. > The state where old and new interfaces have both external_ids:iface-id set at > the same time is "invalid", and all flows are not installed for lpold. > > Fixes: 3ae8470edc64 ("I-P: Handle runtime data changes for pflow_output engine.") > Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=2129866 > > Signed-off-by: Xavier Simonart <[email protected]> > --- > controller/binding.c | 43 +++++++---- > controller/binding.h | 1 + > tests/ovn.at | 168 +++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 200 insertions(+), 12 deletions(-) > > diff --git a/controller/binding.c b/controller/binding.c > index db1eb7a40..5df62baef 100644 > --- a/controller/binding.c > +++ b/controller/binding.c > @@ -1873,6 +1873,7 @@ build_local_bindings(struct binding_ctx_in *b_ctx_in, > lbinding = local_binding_create(iface_id, iface_rec); > local_binding_add(local_bindings, lbinding); > } else { > + lbinding->multiple_bindings = true; > static struct vlog_rate_limit rl = > VLOG_RATE_LIMIT_INIT(1, 5); > VLOG_WARN_RL( > @@ -2163,6 +2164,10 @@ consider_iface_claim(const struct ovsrec_interface *iface_rec, > lbinding = local_binding_create(iface_id, iface_rec); > local_binding_add(local_bindings, lbinding); > } else { > + if (lbinding->iface && lbinding->iface != iface_rec) { > + lbinding->multiple_bindings = true; > + b_ctx_out->local_lports_changed = true; > + } > lbinding->iface = iface_rec; > } > > @@ -2181,6 +2186,13 @@ consider_iface_claim(const struct ovsrec_interface *iface_rec, > return true; > } > > + /* If multiple bindings to the same port, remove the "old" binding. > + * This ensures that change tracking is correct. > + */ > + if (lbinding->multiple_bindings) { > + remove_related_lport(pb, b_ctx_out); > + } > + > enum en_lport_type lport_type = get_lport_type(pb); > if (lport_type == LP_LOCALPORT) { > return consider_localport(pb, b_ctx_in, b_ctx_out); > @@ -2235,18 +2247,24 @@ consider_iface_release(const struct ovsrec_interface *iface_rec, > lbinding = local_binding_find(local_bindings, iface_id); > > if (lbinding) { > - int64_t ofport = iface_rec->n_ofport ? *iface_rec->ofport : 0; > - if (lbinding->iface != iface_rec && !ofport) { > - /* If external_ids:iface-id is set within the same transaction > - * as adding an interface to a bridge, ovn-controller is > - * usually initially notified of ovs interface changes with > - * ofport == 0. If the lport was bound to a different interface > - * we do not want to release it. > - */ > - VLOG_DBG("Not releasing lport %s as %s was claimed " > - "and %s was never bound)", iface_id, lbinding->iface ? > - lbinding->iface->name : "", iface_rec->name); > - return true; > + if (lbinding->multiple_bindings) { > + VLOG_INFO("Multiple bindings for %s: force recompute to clean up", > + iface_id); > + return false; > + } else { > + int64_t ofport = iface_rec->n_ofport ? *iface_rec->ofport : 0; > + if (lbinding->iface != iface_rec && !ofport) { > + /* If external_ids:iface-id is set within the same transaction > + * as adding an interface to a bridge, ovn-controller is > + * usually initially notified of ovs interface changes with > + * ofport == 0. If the lport was bound to a different interface > + * we do not want to release it. > + */ > + VLOG_DBG("Not releasing lport %s as %s was claimed " > + "and %s was never bound)", iface_id, lbinding->iface ? > + lbinding->iface->name : "", iface_rec->name); > + return true; > + } > } > } > > @@ -3058,6 +3076,7 @@ local_binding_create(const char *name, const struct ovsrec_interface *iface) > struct local_binding *lbinding = xzalloc(sizeof *lbinding); > lbinding->name = xstrdup(name); > lbinding->iface = iface; > + lbinding->multiple_bindings = false; > ovs_list_init(&lbinding->binding_lports); > > return lbinding; > diff --git a/controller/binding.h b/controller/binding.h > index ad959a9e6..6c3a98b02 100644 > --- a/controller/binding.h > +++ b/controller/binding.h > @@ -135,6 +135,7 @@ struct local_binding { > char *name; > const struct ovsrec_interface *iface; > struct ovs_list binding_lports; > + bool multiple_bindings; > }; > > struct local_binding_data { > diff --git a/tests/ovn.at b/tests/ovn.at > index 6552681bd..7a918c639 100644 > --- a/tests/ovn.at > +++ b/tests/ovn.at > @@ -33053,3 +33053,171 @@ check ovn-nbctl --wait=hv sync > OVN_CLEANUP([hv1]) > AT_CLEANUP > ]) > + > +m4_define([MULTIPLE_OVS_INT], > + [OVN_FOR_EACH_NORTHD([ > + AT_SETUP([ovn-controller: Multiple OVS interfaces bound to same logical port ($1)]) > + ovn_start > + net_add n1 > + > + sim_add hv1 > + as hv1 > + ovs-vsctl add-br br-phys > + ovn_attach n1 br-phys 192.168.0.1 > + > + get_flows() > + { > + cookie=${1} > + ovs-ofctl dump-flows br-int | grep $cookie | > + sed -e 's/duration=[[0-9.]]*s, //g' | > + sed -e 's/idle_age=[[0-9]]*, //g' | > + sed -e 's/n_packets=[[0-9]]*, //g' | > + sed -e 's/n_bytes=[[0-9]]*, //g' > + } > + > + check ovn-nbctl ls-add ls > + check ovn-nbctl lsp-add ls lp > + if test X$1 != X; then > + check ovn-nbctl lsp-set-type lp $1 > + fi > + check ovn-nbctl lsp-set-addresses lp "00:00:00:01:01:02 192.168.1.2" > + > + check ovn-nbctl lsp-add ls vm1 > + check ovn-nbctl lsp-set-addresses vm1 "00:00:00:01:01:11 192.168.1.11" > + check ovs-vsctl add-port br-int vm1 -- set interface vm1 type=internal external_ids:iface-id=vm1 > + > + check ovn-nbctl --wait=hv sync > + > + check ovs-vsctl add-port br-int lpold -- set interface lpold type=internal > + check ovs-vsctl set interface lpold external_ids:iface-id=lp > + > + OVS_WAIT_UNTIL([test x$(ovn-sbctl --bare --columns _uuid find port_binding logical_port=lp) != x]) > + echo ====================================================== > + echo === Flows after iface-id set for the old interface === > + echo ====================================================== > + COOKIE=$(ovn-sbctl find port_binding logical_port=lp|grep uuid|cut -d: -f2| cut -c1-8 | sed 's/^\s*0\{0,8\}//') > + > + OVS_WAIT_UNTIL([ > + ofport=$(ovs-vsctl --bare --columns ofport find Interface name=lpold) > + ovs-ofctl dump-flows br-int | grep $COOKIE | grep "actions=output:$ofport" > + ]) > + nb_flows=`ovs-ofctl dump-flows br-int | grep $COOKIE | wc -l` > + echo $nb_flows "flows after iface-id set for old interface" > + > + echo ====================================================== > + echo === Flows after iface-id set for the new interface === > + echo ====================================================== > + # Set external_ids:iface-id within same transaction as adding the port. > + # This will generally cause ovn-controller to get initially notified of ovs interface changes with ofport == 0. > + check ovs-vsctl add-port br-int lpnew -- set interface lpnew type=internal -- set interface lpnew external_ids:iface-id=lp > + OVS_WAIT_UNTIL([ > + ofport=$(ovs-vsctl --bare --columns ofport find Interface name=lpnew) > + ovs-ofctl dump-flows br-int | grep $COOKIE | grep "actions=output:$ofport" > + ]) > + check test "$nb_flows" = $(ovs-ofctl dump-flows br-int | grep $COOKIE | wc -l) > + flows_lpnew=$(get_flows $COOKIE) > + > + echo ====================================================== > + echo ======= Flows after old interface is deleted ========= > + echo ====================================================== > + check ovs-vsctl del-port br-int lpold > + # We do not expect changes, so let's wait for controller to get time to process any update > + check ovn-nbctl --wait=hv sync > + check test "$nb_flows" = $(ovs-ofctl dump-flows br-int | grep $COOKIE | wc -l) > + flows_after_deletion=$(get_flows $COOKIE) > + check test "$flows_lpnew" = "$flows_after_deletion" > + > + echo ====================================================== > + echo ======= Flows after lptemp interface is created ==== > + echo ====================================================== > + # Set external_ids:iface-id in a different transaction as adding the port. > + # This will generally cause ovn-controller to get notified of ovs interface changes with a proper ofport. > + check ovs-vsctl add-port br-int lptemp -- set Interface lptemp type=internal > + check ovs-vsctl set Interface lptemp external_ids:iface-id=lp > + OVS_WAIT_UNTIL([ > + ofport=$(ovs-vsctl --bare --columns ofport find Interface name=lptemp) > + ovs-ofctl dump-flows br-int | grep $COOKIE | grep "actions=output:$ofport" > + ]) > + check test "$nb_flows" = $(ovs-ofctl dump-flows br-int | grep $COOKIE | wc -l) > + > + echo ====================================================== > + echo ======= Flows after lptemp interface is deleted ====== > + echo ====================================================== > + check ovs-vsctl del-port br-int lptemp > + OVS_WAIT_UNTIL([ > + ofport=$(ovs-vsctl --bare --columns ofport find Interface name=lpnew) > + echo $ofport > + ovs-ofctl dump-flows br-int | grep $COOKIE > + ovs-ofctl dump-flows br-int | grep $COOKIE | grep "actions=output:$ofport" > + ]) > + check test "$nb_flows" = $(ovs-ofctl dump-flows br-int | grep $COOKIE | wc -l) > + flows_after_deletion=$(get_flows $COOKIE) > + check test "$flows_lpnew" = "$flows_after_deletion" > + > + echo ====================================================== > + echo ======= Flows after new interface is deleted ========= > + echo ====================================================== > + check ovs-vsctl del-port br-int lpnew > + OVS_WAIT_UNTIL([ > + nb_flows=`ovs-ofctl dump-flows br-int | grep $COOKIE | wc -l` > + test "${nb_flows}" = 0 > + ]) > + > + echo ====================================================== > + echo ======= Three interfaces bound to the same port ====== > + echo ====================================================== > + check ovs-vsctl add-port br-int lpold -- set interface lpold type=internal > + check ovs-vsctl set interface lpold external_ids:iface-id=lp > + check ovs-vsctl add-port br-int lpnew -- set interface lpnew type=internal > + check ovs-vsctl set interface lpnew external_ids:iface-id=lp > + > + # Wait for lpnew flows to be installed > + OVS_WAIT_UNTIL([ > + ofport=$(ovs-vsctl --bare --columns ofport find Interface name=lpnew) > + ovs-ofctl dump-flows br-int | grep $COOKIE | grep "actions=output:$ofport" > + ]) > + flows_lpnew=$(get_flows $COOKIE) > + nb_flows=`ovs-ofctl dump-flows br-int | grep $COOKIE | wc -l` > + > + check ovs-vsctl add-port br-int lptemp -- set Interface lptemp type=internal > + check ovs-vsctl set Interface lptemp external_ids:iface-id=lp > + > + # Wait for lptemp flows to be installed > + OVS_WAIT_UNTIL([ > + ofport=$(ovs-vsctl --bare --columns ofport find Interface name=lptemp) > + ovs-ofctl dump-flows br-int | grep $COOKIE | grep "actions=output:$ofport" > + ]) > + > + # Delete both lpold and lptemp to go to a stable situation > + check ovs-vsctl del-port br-int lptemp > + check ovs-vsctl del-port br-int lpold > + > + OVS_WAIT_UNTIL([ > + test 0 = $(ovs-vsctl show | grep "Port lpold" | wc -l) > + ]) > + > + # Wait for correct/lpnew flows to be installed > + OVS_WAIT_UNTIL([ > + ofport=$(ovs-vsctl --bare --columns ofport find Interface name=lpnew) > + ovs-ofctl dump-flows br-int | grep $COOKIE | grep "actions=output:$ofport" > + ]) > + check test "$nb_flows" = $(ovs-ofctl dump-flows br-int | grep $COOKIE | wc -l) > + flows_after_deletion=$(get_flows $COOKIE) > + check test "$flows_lpnew" = "$flows_after_deletion" > + > + # Check that recompute still works > + check ovn-appctl -t ovn-controller recompute > + OVS_WAIT_UNTIL([ > + ofport=$(ovs-vsctl --bare --columns ofport find Interface name=lpnew) > + ovs-ofctl dump-flows br-int | grep $COOKIE | grep "actions=output:$ofport" > + ]) > + check test "$nb_flows" = $(ovs-ofctl dump-flows br-int | grep $COOKIE | wc -l) > + flows_after_deletion=$(get_flows $COOKIE) > + check test "$flows_lpnew" = "$flows_after_deletion" > + > + OVN_CLEANUP([hv1]) > + AT_CLEANUP > + ])]) > + > +MULTIPLE_OVS_INT([localport]) > +MULTIPLE_OVS_INT([]) > -- > 2.31.1 >
Acked-by: Han Zhou <[email protected]> _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
