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;
+ }
+
+ 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)) {
@@ -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 (in
+ * trk_lsps->created) already get their flows generated. */
+ 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