On Fri, Jul 24, 2026 at 12:41 PM Lucas Vargas Dias <[email protected]> wrote:
> Until now any change to a logical switch port of type "router" fell back > to a full northd recompute, because lsp_can_be_inc_processed() only > allowed plain VIF and remote ports. Enable incremental processing for > creation and deletion of router ports as well. > > Unlike VIF/remote ports, a router port is not self-contained: a full > recompute wires a peer relationship to the logical router port (LRP) in > join_logical_ports() and populates aggregate datapath state > (od->router_ports, peer->od->ls_peers, the "router" address, ...). The > incremental path now wires (and tears down) this peer relationship > itself, mirroring the router branch of join_logical_ports(). The wiring > runs before the SB port binding is synced, as ovn_port_update_sbrec() > consults op->peer. > > Some flows that toggle with the presence of a router port are owned by > lflow_refs other than the port's own and are handled explicitly: > > - Sibling switch ports' ARP-resolve flows depend on od->router_ports, > so the existing ports are re-tracked to the lflow engine. > - The peer LRP's SB Port_Binding options:peer is (re)synced. > - The per-switch ls_stateful lflow_ref (skip-conntrack flows generated > for each router port) is regenerated in the lflow port-change handler. > - The router port's stateful_lflow_ref is cleared on deletion (only > router ports populate it via build_lbnat_lflows_iterate_by_lsp()). > > Dependencies that live outside the router port's lflow_ref and that this > path does not keep in sync trigger a fall back to a full recompute > (router_lsp_needs_recompute()): distributed gateway ports, gateway > routers, NAT, load balancers, static or dynamic routing, IPv6 RA, > arp_proxy, ACLs, vtep ports, mcast relay, or more than one router port on > the switch. > > An update to a router port other than its "up" column also falls back to > recompute, as re-wiring the peer relationship on reinit is not supported. > > Add tests covering incremental create/delete of a router port (including > regeneration of a sibling VIF's ARP-resolve flow) and the recompute > fallbacks for the distributed-gateway and dynamic-routing cases. > > Assisted-by: Claude Opus 4.8, ClaudeCode > Signed-off-by: Lucas Vargas Dias <[email protected]> > --- > northd/en-ls-stateful.c | 7 + > northd/northd.c | 340 +++++++++++++++++++++++++++++++++++++++- > tests/ovn-northd.at | 139 +++++++++++++++- > 3 files changed, 471 insertions(+), 15 deletions(-) > > diff --git a/northd/en-ls-stateful.c b/northd/en-ls-stateful.c > index 1127b7d50..19c44a05e 100644 > --- a/northd/en-ls-stateful.c > +++ b/northd/en-ls-stateful.c > @@ -336,6 +336,13 @@ ls_stateful_table_find_(const struct > ls_stateful_table *table, > return NULL; > } > > +const struct ls_stateful_record * > +ls_stateful_table_find(const struct ls_stateful_table *table, > + const struct nbrec_logical_switch *nbs) > +{ > + return ls_stateful_table_find_(table, nbs); > +} > + > static struct ls_stateful_record * > ls_stateful_record_create(struct ls_stateful_table *table, > const struct ovn_datapath *od, > diff --git a/northd/northd.c b/northd/northd.c > index f4bb5096e..404ab50a9 100644 > --- a/northd/northd.c > +++ b/northd/northd.c > @@ -4331,13 +4331,35 @@ sync_pbs_for_northd_changed_ovn_ports( > const struct lr_stateful_table *lr_stateful_table) > { > struct hmapx_node *hmapx_node; > + struct ovn_port *op; > > HMAPX_FOR_EACH (hmapx_node, &trk_ovn_ports->created) { > - sync_pb_for_lsp(hmapx_node->data, lr_stateful_table); > + op = hmapx_node->data; > + sync_pb_for_lsp(op, lr_stateful_table); > + /* A newly created router port must set options:peer on its peer > LRP's > + * port binding. */ > + if (lsp_is_router(op->nbsp) && op->peer && op->peer->nbrp) { > + sync_pb_for_lrp(op->peer, lr_stateful_table); > + } > } > > HMAPX_FOR_EACH (hmapx_node, &trk_ovn_ports->updated) { > - sync_pb_for_lsp(hmapx_node->data, lr_stateful_table); > + op = hmapx_node->data; > + sync_pb_for_lsp(op, lr_stateful_table); > + if (lsp_is_router(op->nbsp) && op->peer && op->peer->nbrp) { > + sync_pb_for_lrp(op->peer, lr_stateful_table); > + } > + } > + > + /* A deleted router port must clear options:peer on its (still > existing) > + * peer LRP's port binding. ls_router_port_unwire_peer() already > reset > + * op->peer->peer to NULL, so sync_pb_for_lrp() will omit the peer > + * option. */ > + HMAPX_FOR_EACH (hmapx_node, &trk_ovn_ports->deleted) { > + op = hmapx_node->data; > + if (lsp_is_router(op->nbsp) && op->peer && op->peer->nbrp) { > + sync_pb_for_lrp(op->peer, lr_stateful_table); > + } > } > } > > @@ -4689,10 +4711,10 @@ destroy_northd_tracked_data(struct northd_data *nd) > static bool > lsp_can_be_inc_processed(const struct nbrec_logical_switch_port *nbsp) > { > - /* Support only normal VIF, remote, localport, and virtual ports for > - * now. */ > + /* Support only normal VIF, remote, localport, virtual, and router > + * ports for now. */ > if (nbsp->type[0] && !lsp_is_remote(nbsp) && !lsp_is_localport(nbsp) > && > - !lsp_is_virtual(nbsp)) { > + !lsp_is_virtual(nbsp) && !lsp_is_router(nbsp)) { > return false; > } > > @@ -4779,6 +4801,177 @@ virtual_lsp_needs_recompute(struct ovn_datapath > *od, const char *lport) > return false; > } > > + > +/* A logical switch port of type "router" is not self-contained: in a full > + * recompute join_logical_ports() wires a peer relationship to the logical > + * router port (LRP) and populates aggregate datapath state (see > + * ls_router_port_wire_peer()). Several flows that toggle with the > presence of > + * such a port are owned by lflow_refs other than the port's own (the peer > + * LRP's ref, the ls_stateful ref, the switch datapath ref, ...), which > the > + * incremental LSP path does not keep in sync. Return true when any such > + * dependency is present so the caller falls back to a full recompute. > + * > + * 'is_delete' is true when 'nbsp' is being removed (the port is still > counted > + * in od->router_ports at this point). */ > +static bool > +router_lsp_needs_recompute(struct ovn_datapath *od, > + const struct nbrec_logical_switch_port *nbsp, > + const struct hmap *lr_ports, bool is_delete) > +{ > + /* arp_proxy adds proxy-arp admission flows owned by the peer LRP's > + * lflow_ref and sets od->has_arp_proxy_port. */ > + if (smap_get( ->options, "arp_proxy")) { > + return true; > + } > + > + /* Handle a single router port per switch for now. With more than one > + * router port the router ports generate inter-router-port > + * ARP-resolve/routable flows for each other (owned by sibling refs) > that > + * this path does not regenerate. */ > + if (is_delete ? vector_len(&od->router_ports) > 1 > + : !vector_is_empty(&od->router_ports)) { > + return true; > + } > + > + /* Switch-level stateful/aggregate dependencies that live outside the > + * port's own lflow_ref: ls_stateful skip-conntrack flows over > + * od->router_ports, LB install set (od->ls_peers), and vtep hairpin > flows > + * owned by od->datapath_lflows. */ > + if (od->nbs->n_acls || od->nbs->n_load_balancer || > + od->nbs->n_load_balancer_group || od->has_vtep_lports) { > + return true; > + } > why do you fall back on n_acls here? doesn't that get regenerated in lflow_handle_northd_port_changes(), specifically by the hmapx ls_stateful_regen? so the skip-conntrack flows should be rebuilt correctly even with ACLs present. Is there another dependancy that I am not seeing? > + > + const char *peer_name = smap_get( ->options, "router-port"); > + if (!peer_name) { > + /* No peer to wire; the port is inert. */ > + return false; > + } > + > + struct ovn_port *peer = ovn_port_find(lr_ports, peer_name); > + if (!peer || !peer->nbrp) { > + /* Peer LRP not present yet. This matches join_logical_ports(), > which > + * leaves op->peer NULL and does not add the port to > od->router_ports; > + * the port is inert and can be processed incrementally. */ > + return false; > + } > + > + /* Bad LRP-to-LRP peering or a disabled LRP; let recompute deal with > it. */ > + if (peer->nbrp->peer || !lrport_is_enabled(peer->nbrp)) { > + return true; > + } > + > + /* Distributed gateway / gateway-router complexity: l3gateway and > + * chassisredirect SB port types, GARP nat_addresses, cr_port. */ > + if (lrp_is_l3dgw(peer) || peer->cr_port || > + !vector_is_empty(&peer->od->l3dgw_ports) || > + peer->od->is_gw_router || > + smap_get(&peer->od->nbr->options, "chassis")) { > + return true; > + } > + > + /* NAT, static routes, LBs and dynamic routing on the peer router > pull in > + * stateful/routable/advertised-route dependencies not tracked here. > */ > + const struct nbrec_logical_router *nbr = peer->od->nbr; > + if (nbr->n_nat || nbr->n_static_routes || nbr->n_load_balancer || > + nbr->n_load_balancer_group) { > + return true; > + } > + if (peer->od->dynamic_routing || > + peer->od->dynamic_routing_redistribute != DRRM_NONE) { > + return true; > + } > + > + /* IPv6 RA flows are owned by the peer LRP's lflow_ref and toggle > with the > + * peer's presence. */ > + if (!smap_is_empty(&peer->nbrp->ipv6_ra_configs)) { > + return true; > + } > + > + /* mcast relay would flip od->mcast_info.sw.flood_relay, changing > flows > + * owned by od->datapath_lflows. */ > + if (peer->od->mcast_info.rtr.relay) { > + return true; > + } > + > + return false; > +} > + > +/* Wire the peer relationship of a logical switch port 'op' of type > "router", > + * mirroring the router branch of join_logical_ports(). 'op->od' must be > set. > + * Must run before the SB port binding is synced, as > ovn_port_update_sbrec() > + * consults op->peer. */ > +static void > +ls_router_port_wire_peer(struct ovn_port *op, const struct hmap *lr_ports) > +{ > + const char *peer_name = smap_get(&op->nbsp->options, "router-port"); > + if (!peer_name) { > + return; > + } > + > + struct ovn_port *peer = ovn_port_find(lr_ports, peer_name); > + if (!peer || !peer->nbrp || peer->nbrp->peer) { > + return; > + } > + > + vector_push(&op->od->router_ports, &op); > + vector_push(&peer->od->ls_peers, &op->od); > + peer->peer = op; > + op->peer = peer; > +} > + > +/* Fill op->lsp_addrs for the "router" address of a router-type LSP from > its > + * peer LRP networks (skipped by parse_lsp_addrs()). Must run after > + * ls_port_init() and with op->peer set. */ > +static void > +ls_router_port_add_peer_networks(struct ovn_port *op) > +{ > + for (size_t j = 0; j < op->nbsp->n_addresses; j++) { > + if (!strcmp(op->nbsp->addresses[j], "router")) { > + if (extract_lrp_networks(op->peer->nbrp, > + &op->lsp_addrs[op->n_lsp_addrs])) { > + op->n_lsp_addrs++; > + } > + break; > + } > + } > +} > + > +/* Tear down the peer relationship wired by ls_router_port_wire_peer() > when a > + * router-type LSP is deleted. Keeps op->peer set so the SB port-binding > sync > + * node can still reach the peer LRP to clear its options:peer (deleted > tracked > + * ports are freed only at the end of the engine run). */ > +static void > +ls_router_port_unwire_peer(struct ovn_port *op) > +{ > + struct ovn_port *peer = op->peer; > + if (!peer) { > + return; > + } > + > + struct ovn_port *rp; > + size_t i = 0; > + VECTOR_FOR_EACH (&op->od->router_ports, rp) { > + if (rp == op) { > + vector_remove(&op->od->router_ports, i, NULL); > + break; > + } > + i++; > + } > + > + struct ovn_datapath *ls_od; > + i = 0; > + VECTOR_FOR_EACH (&peer->od->ls_peers, ls_od) { > + if (ls_od == op->od) { > + vector_remove(&peer->od->ls_peers, i, NULL); > + break; > + } > + i++; > + } > + > + peer->peer = NULL; > +} > + > static bool > ls_port_has_changed(const struct nbrec_logical_switch_port *new) > { > @@ -4846,7 +5039,7 @@ ls_port_init(struct ovn_port *op, struct > ovsdb_idl_txn *ovnsb_txn, > static struct ovn_port * > ls_port_create(struct ovsdb_idl_txn *ovnsb_txn, struct hmap *ls_ports, > const char *key, const struct nbrec_logical_switch_port > *nbsp, > - struct ovn_datapath *od, > + struct ovn_datapath *od, const struct hmap *lr_ports, > const struct sbrec_mirror_table *sbrec_mirror_table, > struct ovsdb_idl_index *sbrec_chassis_by_name, > struct ovsdb_idl_index *sbrec_chassis_by_hostname, > @@ -4855,6 +5048,16 @@ ls_port_create(struct ovsdb_idl_txn *ovnsb_txn, > struct hmap *ls_ports, > struct ovn_port *op = ovn_port_create(ls_ports, key, nbsp, NULL, > NULL); > hmap_insert(&od->ports, &op->dp_node, hmap_node_hash(&op->key_node)); > + > + /* A router-type LSP must have its peer LRP wired before the SB port > + * binding is synced by ls_port_init() (ovn_port_update_sbrec() > consults > + * op->peer). op->od is normally set inside ls_port_init(); set it > early > + * so the peer wiring can use op->od->router_ports. */ > + if (lsp_is_router(nbsp)) { > + op->od = od; > + ls_router_port_wire_peer(op, lr_ports); > + } > + > if (!ls_port_init(op, ovnsb_txn, od, NULL, sbrec_mirror_table, > sbrec_chassis_by_name, sbrec_chassis_by_hostname, > sbrec_encap_by_ip)) { > if ls_port_init() fails after ls_router_port_wire_peer() pushes op into the datapaths router_ports vector and pushes the router_ports datapath into the peers ls_peers vector that leaves a stale entry in peer->od->ls_peers and a reference to the freed op in od->router_ports. in practice this is not a big deal because this failure triggers a database recalculation but it would be cleaner to call ls_router_port_unwire_peer() before ovn_port_destroy() in the failure branch. @@ -4862,6 +5065,10 @@ ls_port_create(struct ovsdb_idl_txn *ovnsb_txn, > struct hmap *ls_ports, > return NULL; > } > > + if (lsp_is_router(nbsp) && op->peer) { > + ls_router_port_add_peer_networks(op); > + } > + > return op; > } > > @@ -5056,6 +5263,7 @@ ls_handle_lsp_changes(struct ovsdb_idl_txn > *ovnsb_idl_txn, > > bool ls_had_only_router_ports = (!vector_is_empty(&od->router_ports) > && (vector_len(&od->router_ports) == hmap_count(&od->ports))); > + bool router_ports_changed = false; > > struct ovs_list existing_virtual_ports; > struct ovn_port *op; > @@ -5083,9 +5291,17 @@ ls_handle_lsp_changes(struct ovsdb_idl_txn > *ovnsb_idl_txn, > * to recompute. */ > goto fail; > } > + if (lsp_is_router(new_nbsp) && > + router_lsp_needs_recompute(od, new_nbsp, > &nd->lr_ports, > + false)) { > + /* This router port has a dependency on a connected > router > + * that can't be handled incrementally. Fall back to > + * recompute. */ > + goto fail; > + } > op = ls_port_create(ovnsb_idl_txn, &nd->ls_ports, > new_nbsp->name, new_nbsp, od, > - ni->sbrec_mirror_table, > + &nd->lr_ports, ni->sbrec_mirror_table, > ni->sbrec_chassis_by_name, > ni->sbrec_chassis_by_hostname, > ni->sbrec_encap_by_ip); > @@ -5093,9 +5309,30 @@ ls_handle_lsp_changes(struct ovsdb_idl_txn > *ovnsb_idl_txn, > goto fail; > } > add_op_to_northd_tracked_ports(&trk_lsps->created, op); > + if (lsp_is_router(new_nbsp) && op->peer) { > + /* A router port was added to od->router_ports; > sibling > + * ports' ARP-resolve flows must be regenerated. */ > + router_ports_changed = true; > + } > } else if (ls_port_has_changed(new_nbsp)) { > /* Existing port updated */ > bool temp = false; > + if (lsp_is_router(new_nbsp)) { > + /* The SB port binding type of a router port ("patch", > + * "l3gateway", ...) never matches its NB type > ("router"), > + * so lsp_is_type_changed() can't be used here. > Re-wiring > + * the peer relationship on reinit is not supported, > so > + * fall back to recompute on any change other than > the "up" > + * column; an "up"-only change does not affect > router-port > + * flows, so ignore it. */ > + if (!op->lsp_can_be_inc_processed || > + !lsp_can_be_inc_processed(new_nbsp) || > + check_lsp_changes_other_than_up(new_nbsp)) { > + goto fail; > + } > + op->visited = true; > + continue; > + } > if (lsp_is_type_changed(op->sb, new_nbsp, &temp) || > !op->lsp_can_be_inc_processed || > !lsp_can_be_inc_processed(new_nbsp)) { > @@ -5174,6 +5411,14 @@ ls_handle_lsp_changes(struct ovsdb_idl_txn > *ovnsb_idl_txn, > * recompute. */ > goto fail; > } > + if (lsp_is_router(op->nbsp) && > + router_lsp_needs_recompute(od, op->nbsp, &nd->lr_ports, > + true)) { > + /* This router port has a dependency on a connected > router that > + * can't be regenerated incrementally. Fall back to > + * recompute. */ > + goto fail; > + } > if (sset_contains(&nd->svc_monitor_lsps, op->key)) { > /* This port was used for svc monitor, which may be > * impacted by this deletion. Fallback to recompute. */ > @@ -5187,6 +5432,14 @@ ls_handle_lsp_changes(struct ovsdb_idl_txn > *ovnsb_idl_txn, > * resolved; fall back to recompute. */ > goto fail; > } > + if (lsp_is_router(op->nbsp) && op->peer) { > + /* Tear down the peer wiring and flag that sibling ports' > + * ARP-resolve flows must be regenerated. op->peer is > kept so > + * the SB port-binding sync node can clear the peer LRP's > + * options:peer. */ > + ls_router_port_unwire_peer(op); > + router_ports_changed = true; > + } > add_op_to_northd_tracked_ports(&trk_lsps->deleted, op); > hmap_remove(&nd->ls_ports, &op->key_node); > hmap_remove(&od->ports, &op->dp_node); > @@ -5218,6 +5471,19 @@ ls_handle_lsp_changes(struct ovsdb_idl_txn > *ovnsb_idl_txn, > } > } > > + /* Adding or removing a router port changes od->router_ports, on > which the > + * ARP-resolve flows of the sibling switch ports depend (see > + * build_arp_resolve_flows_for_lsp()). Re-track the existing sibling > ports > + * so the lflow engine regenerates their flows. Newly created ports > (inD > + * trk_lsps->created) already get their flows generated. */ > nit: could you mention that deleted ports are already removed from the hmap dp_node above so we don't have to worry about them in this loop > + if (router_ports_changed) { > + HMAP_FOR_EACH (op, dp_node, &od->ports) { > + if (!hmapx_contains(&trk_lsps->created, op)) { > + add_op_to_northd_tracked_ports(&trk_lsps->updated, op); > + } > + } > + } > + > /* Update old virtual ports that have newly created or newly deleted > * VIF as parent port. This code handles cases where the virtual port > was > * created before the parent port or when the parent port was > recreated. > @@ -20857,16 +21123,35 @@ lflow_handle_northd_port_changes(struct > ovsdb_idl_txn *ovnsb_txn, > struct hmapx_node *hmapx_node; > struct ovn_port *op; > > + /* Logical switches whose set of router ports changed. The per-switch > + * ls_stateful lflow_ref contains skip-conntrack flows generated for > each > + * router port (see build_ls_stateful_rec_pre_lb()/_pre_acls()), so > it must > + * be regenerated when a router port is created or deleted. */ > + struct hmapx ls_stateful_regen = > HMAPX_INITIALIZER(&ls_stateful_regen); > + > HMAPX_FOR_EACH (hmapx_node, &trk_lsps->deleted) { > op = hmapx_node->data; > /* Make sure 'op' is an lsp and not lrp. */ > ovs_assert(op->nbsp); > + if (lsp_is_router(op->nbsp) && op->peer) { > + hmapx_add(&ls_stateful_regen, op->od); > + } > bool handled = lflow_ref_resync_flows( > op->lflow_ref, lflows, ovnsb_txn, lflow_input->dps, > lflow_input->ovn_internal_version_changed, > lflow_input->sbrec_logical_flow_table, > lflow_input->sbrec_logical_dp_group_table); > + if (handled) { > + /* Router ports also own flows on their stateful_lflow_ref > (see > + * build_lbnat_lflows_iterate_by_lsp()); clear those too. */ > + handled = lflow_ref_resync_flows( > + op->stateful_lflow_ref, lflows, ovnsb_txn, > lflow_input->dps, > + lflow_input->ovn_internal_version_changed, > + lflow_input->sbrec_logical_flow_table, > + lflow_input->sbrec_logical_dp_group_table); > + } > if (!handled) { > + hmapx_destroy(&ls_stateful_regen); > return false; > } > /* No need to update SB multicast groups, thanks to weak > @@ -20914,6 +21199,7 @@ lflow_handle_northd_port_changes(struct > ovsdb_idl_txn *ovnsb_txn, > ds_destroy(&actions); > > if (!handled) { > + hmapx_destroy(&ls_stateful_regen); > return false; > } > } > @@ -20922,6 +21208,9 @@ lflow_handle_northd_port_changes(struct > ovsdb_idl_txn *ovnsb_txn, > op = hmapx_node->data; > /* Make sure 'op' is an lsp and not lrp. */ > ovs_assert(op->nbsp); > + if (lsp_is_router(op->nbsp) && op->peer) { > + hmapx_add(&ls_stateful_regen, op->od); > + } > > struct ds match = DS_EMPTY_INITIALIZER; > struct ds actions = DS_EMPTY_INITIALIZER; > @@ -20955,11 +21244,46 @@ lflow_handle_northd_port_changes(struct > ovsdb_idl_txn *ovnsb_txn, > ds_destroy(&actions); > > if (!handled) { > + hmapx_destroy(&ls_stateful_regen); > return false; > } > } > > - return true; > + /* Regenerate the ls_stateful lflows of switches whose set of router > ports > + * changed (the skip-conntrack flows for router ports are owned by the > + * per-switch ls_stateful lflow_ref, not by the port). */ > + bool handled = true; > + HMAPX_FOR_EACH (hmapx_node, &ls_stateful_regen) { > + struct ovn_datapath *od = hmapx_node->data; > + const struct ls_stateful_record *ls_stateful_rec = > + ls_stateful_table_find(lflow_input->ls_stateful_table, > od->nbs); > + if (!ls_stateful_rec) { > + continue; > + } > + > + lflow_ref_unlink_lflows(ls_stateful_rec->lflow_ref); > + build_ls_stateful_flows(ls_stateful_rec, od, > + lflow_input->ls_port_groups, > + lflow_input->meter_groups, > + lflow_input->sampling_apps, > + lflow_input->features, > + lflows, > + lflow_input->sbrec_acl_id_table); > + build_network_function(od, lflows, lflow_input->ls_port_groups, > + ls_stateful_rec->lflow_ref); > + handled = lflow_ref_sync_lflows( > + ls_stateful_rec->lflow_ref, lflows, ovnsb_txn, > + lflow_input->dps, > + lflow_input->ovn_internal_version_changed, > + lflow_input->sbrec_logical_flow_table, > + lflow_input->sbrec_logical_dp_group_table); > + if (!handled) { > + break; > + } > + } > + > + hmapx_destroy(&ls_stateful_regen); > + return handled; > } > > bool > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at > index 558ce10d8..13df2b712 100644 > --- a/tests/ovn-northd.at > +++ b/tests/ovn-northd.at > @@ -12285,6 +12285,132 @@ CHECK_NO_CHANGE_AFTER_RECOMPUTE > OVN_CLEANUP_NORTHD > AT_CLEANUP > > +AT_SETUP([Router port incremental processing]) > +AT_KEYWORDS([incremental processing]) > +ovn_start > + > +check ovn-nbctl ls-add sw0 > +check ovn-nbctl lr-add lr0 > +check ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 > +check ovn-nbctl --wait=sb lsp-add sw0 vif0 \ > + -- lsp-set-addresses vif0 "00:00:00:00:00:01 10.0.0.4" > + > +# Connecting the switch to the pre-existing LRP should be incrementally > +# processed (the Logical_Router row is untouched, so it is only a new > LSP). > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats > +check ovn-nbctl --wait=sb lsp-add-router-port sw0 sw0-lr0 lr0-sw0 > +check_engine_compute northd incremental > +check_engine_compute lflow incremental > + > +# The peer relationship is reflected on both SB port bindings. > +AT_CHECK([ovn-sbctl get port_binding sw0-lr0 type], [0], [dnl > +patch > +]) > +AT_CHECK([ovn-sbctl get port_binding sw0-lr0 options:peer], [0], [dnl > +lr0-sw0 > +]) > +AT_CHECK([ovn-sbctl get port_binding lr0-sw0 options:peer], [0], [dnl > +sw0-lr0 > +]) > + > +# The router port L2 lookup flow is present on the switch. > +AT_CHECK([ovn-sbctl dump-flows sw0 | grep ls_in_l2_lkup | grep sw0-lr0 \ > + | grep -c 'eth.dst == 00:00:00:00:ff:01'], [0], [1 > +]) > + > +# The sibling VIF's ARP-resolve flow was regenerated on the router > pipeline > +# (it depends on sw0's set of router ports). > +AT_CHECK([ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve \ > + | grep 'reg0 == 10.0.0.4' | grep -c 'eth.dst = 00:00:00:00:00:01'], > [0], [1 > +]) > + > +CHECK_NO_CHANGE_AFTER_RECOMPUTE > + > +# Disconnecting the switch from the router should be incrementally > processed. > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats > +check ovn-nbctl --wait=sb lsp-del sw0-lr0 > +check_engine_compute northd incremental > +check_engine_compute lflow incremental > + > +# The peer LRP port binding no longer references the deleted switch port. > +AT_CHECK([ovn-sbctl get port_binding lr0-sw0 options:peer], [1], [], [dnl > +ovn-sbctl: no key "peer" in Port_Binding record "lr0-sw0" column options > +]) > + > +# The router port L2 lookup flow and the VIF's ARP-resolve flow are gone. > +AT_CHECK([ovn-sbctl dump-flows sw0 | grep ls_in_l2_lkup | grep -c > sw0-lr0], > + [1], [0 > +]) > +AT_CHECK([ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve \ > + | grep -c 'reg0 == 10.0.0.4'], [1], [0 > +]) > + > +CHECK_NO_CHANGE_AFTER_RECOMPUTE > + > +OVN_CLEANUP_NORTHD > +AT_CLEANUP > + > +AT_SETUP([Router port incremental processing fallback with distributed > gateway]) > +AT_KEYWORDS([incremental processing]) > +ovn_start > + > +check ovn-sbctl chassis-add gw1 geneve 127.0.0.1 > + > +check ovn-nbctl ls-add sw0 > +check ovn-nbctl lr-add lr0 > + > +# Distributed gateway port on lr0. > +check ovn-nbctl ls-add public > +check ovn-nbctl lrp-add lr0 lr0-public 00:00:20:20:12:13 172.168.0.100/24 > +check ovn-nbctl --wait=sb lsp-add-router-port public public-lr0 lr0-public > +check ovn-nbctl lsp-add-localnet-port public ln-public public > +check ovn-nbctl --wait=sb lrp-set-gateway-chassis lr0-public gw1 20 > + > +check ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 > + > +# Connecting a switch to a router that has a distributed gateway port > must fall > +# back to recompute (l3gateway/chassisredirect SB types and GARP > nat_addresses > +# depend on state outside the switch port's lflow_ref). > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats > +check ovn-nbctl --wait=sb lsp-add-router-port sw0 sw0-lr0 lr0-sw0 > +check_engine_compute northd recompute > + > +# Deleting it must also fall back to recompute. > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats > +check ovn-nbctl --wait=sb lsp-del sw0-lr0 > +check_engine_compute northd recompute > + > +CHECK_NO_CHANGE_AFTER_RECOMPUTE > + > +OVN_CLEANUP_NORTHD > +AT_CLEANUP > + > +AT_SETUP([Router port incremental processing fallback with dynamic > routing]) > +AT_KEYWORDS([incremental processing]) > +ovn_start > + > +check ovn-nbctl ls-add sw0 > +check ovn-nbctl lr-add lr0 \ > + -- set Logical_Router lr0 options:dynamic-routing=true > +check ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 > + > +# Connecting a switch to a dynamic-routing router must fall back to > recompute: > +# advertised/routable flows depend on state outside the switch port's > +# lflow_ref. > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats > +check ovn-nbctl --wait=sb lsp-add-router-port sw0 sw0-lr0 lr0-sw0 > +check_engine_compute northd recompute > + > +# Deleting it must also fall back to recompute. > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats > +check ovn-nbctl --wait=sb lsp-del sw0-lr0 > +check_engine_compute northd recompute > + > +CHECK_NO_CHANGE_AFTER_RECOMPUTE > + > +OVN_CLEANUP_NORTHD > +AT_CLEANUP > + > OVN_FOR_EACH_NORTHD_NO_HV([ > AT_SETUP([SB Port binding incremental processing]) > ovn_start > @@ -12327,17 +12453,16 @@ check ovn-nbctl --wait=sb sync > check_recompute_counter 0 0 > CHECK_NO_CHANGE_AFTER_RECOMPUTE > > -# Test lsp of type router > +# Test lsp of type router. This port has no "router-port" option, so it > has no > +# peer LRP and is inert; both the NB logical switch port change and the > +# subsequent "up" change set by ovn-northd are now incrementally > processed. > check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats > check ovn-nbctl --wait=sb lsp-add ls0 rp -- lsp-set-type rp router > - > -# northd engine recomputes twice. Both the times for handling NB logical > switch port > -# changes and not because of SB port binding changes. This is because > ovn-northd > -# sets the "up" to true. > -check_recompute_counter 2 2 > +check_recompute_counter 0 0 > CHECK_NO_CHANGE_AFTER_RECOMPUTE > > -# Set some options to 'rp'. northd should only recompute once. > +# Set some options to 'rp'. Updating a router port other than its "up" > column > +# falls back to recompute (re-wiring the peer on reinit is not supported). > check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats > check ovn-nbctl --wait=sb lsp-set-options rp foo=bar > check_recompute_counter 1 1 > -- > 2.43.0 > > > -- > > > > > _'Esta mensagem é direcionada apenas para os endereços constantes no > cabeçalho inicial. Se você não está listado nos endereços constantes no > cabeçalho, pedimos-lhe que desconsidere completamente o conteúdo dessa > mensagem e cuja cópia, encaminhamento e/ou execução das ações citadas > estão > imediatamente anuladas e proibidas'._ > > > * **'Apesar do Magazine Luiza tomar > todas as precauções razoáveis para assegurar que nenhum vírus esteja > presente nesse e-mail, a empresa não poderá aceitar a responsabilidade por > quaisquer perdas ou danos causados por esse e-mail ou por seus anexos'.* > > > > _______________________________________________ > dev mailing list > [email protected] > https://mail.openvswitch.org/mailman/listinfo/ovs-dev > > _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
