All changes to northbound logical routers can be incrementally processed
by en-datapath-logical-router. This change ensures that it is done and
the changes can then be propagated to the next nodes. A new test in
ovn-northd.at ensures the behavior.

Signed-off-by: Mark Michelson <mmich...@redhat.com>
---
 northd/datapath-sync.c              |  53 +++++++
 northd/datapath-sync.h              |  13 ++
 northd/en-datapath-logical-router.c | 217 +++++++++++++++++++++-------
 northd/en-datapath-logical-router.h |   5 +
 northd/inc-proc-northd.c            |   5 +-
 tests/ovn-northd.at                 |  45 ++++++
 6 files changed, 283 insertions(+), 55 deletions(-)

diff --git a/northd/datapath-sync.c b/northd/datapath-sync.c
index 4916ed647..983f39224 100644
--- a/northd/datapath-sync.c
+++ b/northd/datapath-sync.c
@@ -16,6 +16,8 @@
 #include <config.h>
 
 #include "datapath-sync.h"
+#include "ovsdb-idl-provider.h"
+#include "uuid.h"
 
 static const char *ovn_datapath_strings[] = {
     [DP_SWITCH] = "logical-switch",
@@ -74,6 +76,9 @@ ovn_unsynced_datapath_map_init(struct 
ovn_unsynced_datapath_map *map,
 {
     *map = (struct ovn_unsynced_datapath_map) {
         .dps = HMAP_INITIALIZER(&map->dps),
+        .new = HMAPX_INITIALIZER(&map->new),
+        .deleted = HMAPX_INITIALIZER(&map->deleted),
+        .updated = HMAPX_INITIALIZER(&map->updated),
         .dp_type = dp_type,
     };
 }
@@ -82,9 +87,57 @@ void
 ovn_unsynced_datapath_map_destroy(struct ovn_unsynced_datapath_map *map)
 {
     struct ovn_unsynced_datapath *dp;
+    struct hmapx_node *node;
+
+    hmapx_destroy(&map->new);
+    hmapx_destroy(&map->updated);
+    HMAPX_FOR_EACH_SAFE (node, &map->deleted) {
+       /* Items in the deleted hmapx need to be freed individually since
+        * they are not in map->dps.
+        */
+        dp = node->data;
+        ovn_unsynced_datapath_destroy(dp);
+        free(dp);
+    }
+    hmapx_destroy(&map->deleted);
+
     HMAP_FOR_EACH_POP (dp, hmap_node, &map->dps) {
         ovn_unsynced_datapath_destroy(dp);
         free(dp);
     }
     hmap_destroy(&map->dps);
 }
+
+struct ovn_unsynced_datapath *
+ovn_unsynced_datapath_find(const struct ovn_unsynced_datapath_map *map,
+                           const struct uuid *datapath_uuid)
+{
+    uint32_t hash = uuid_hash(datapath_uuid);
+
+    struct ovn_unsynced_datapath *udp;
+    HMAP_FOR_EACH_WITH_HASH (udp, hmap_node, hash, &map->dps) {
+        if (uuid_equals(&udp->nb_row->uuid, datapath_uuid)) {
+            return udp;
+        }
+    }
+    return NULL;
+}
+
+void
+ovn_unsynced_datapath_map_clear_tracked_data(
+    struct ovn_unsynced_datapath_map *map)
+{
+    hmapx_clear(&map->new);
+    hmapx_clear(&map->updated);
+
+    /* Deleted entries need to be freed since they don't
+     * exist in map->dps.
+     */
+    struct hmapx_node *node;
+    HMAPX_FOR_EACH_SAFE (node, &map->deleted) {
+        struct ovn_unsynced_datapath *udp = node->data;
+        ovn_unsynced_datapath_destroy(udp);
+        free(udp);
+        hmapx_delete(&map->deleted, node);
+    }
+}
diff --git a/northd/datapath-sync.h b/northd/datapath-sync.h
index 246500c7b..081d0af93 100644
--- a/northd/datapath-sync.h
+++ b/northd/datapath-sync.h
@@ -18,6 +18,7 @@
 
 #include "openvswitch/hmap.h"
 #include "smap.h"
+#include "hmapx.h"
 
 /* Datapath syncing API. This file consists of utility functions
  * that can be used when syncing northbound datapath types (e.g.
@@ -62,6 +63,11 @@ struct ovn_unsynced_datapath_map {
     /* ovn_unsynced_datapath */
     struct hmap dps;
     enum ovn_datapath_type dp_type;
+
+    /* Data for incremental case */
+    struct hmapx new;
+    struct hmapx updated;
+    struct hmapx deleted;
 };
 
 struct ovn_synced_datapath {
@@ -83,4 +89,11 @@ void ovn_unsynced_datapath_map_init(struct 
ovn_unsynced_datapath_map *,
                                     enum ovn_datapath_type);
 void ovn_unsynced_datapath_map_destroy(struct ovn_unsynced_datapath_map *);
 
+struct ovn_unsynced_datapath *
+ovn_unsynced_datapath_find(const struct ovn_unsynced_datapath_map *,
+                           const struct uuid *);
+
+void ovn_unsynced_datapath_map_clear_tracked_data(
+    struct ovn_unsynced_datapath_map *);
+
 #endif /* DATAPATH_SYNC_H */
diff --git a/northd/en-datapath-logical-router.c 
b/northd/en-datapath-logical-router.c
index 5160c9c70..8a9adbc4f 100644
--- a/northd/en-datapath-logical-router.c
+++ b/northd/en-datapath-logical-router.c
@@ -34,6 +34,80 @@ en_datapath_logical_router_init(struct engine_node *node 
OVS_UNUSED,
     return map;
 }
 
+static void
+gather_external_ids(const struct nbrec_logical_router *nbr,
+                    struct smap *external_ids)
+{
+    smap_add(external_ids, "name", nbr->name);
+    const char *neutron_router = smap_get(&nbr->options,
+                                           "neutron:router_name");
+    if (neutron_router && neutron_router[0]) {
+        smap_add(external_ids, "name2", neutron_router);
+    }
+
+    int64_t ct_zone_limit = ovn_smap_get_llong(&nbr->options,
+                                               "ct-zone-limit", -1);
+    if (ct_zone_limit > 0) {
+        smap_add_format(external_ids, "ct-zone-limit", "%"PRId64,
+                        ct_zone_limit);
+    }
+
+    int nat_default_ct = smap_get_int(&nbr->options,
+                                      "snat-ct-zone", -1);
+    if (nat_default_ct >= 0) {
+        smap_add_format(external_ids, "snat-ct-zone", "%d",
+                        nat_default_ct);
+    }
+
+    bool learn_from_arp_request =
+        smap_get_bool(&nbr->options, "always_learn_from_arp_request",
+                      true);
+    if (!learn_from_arp_request) {
+        smap_add(external_ids, "always_learn_from_arp_request",
+                 "false");
+    }
+
+    /* For timestamp refreshing, the smallest threshold of the option is
+     * set to SB to make sure all entries are refreshed in time.
+     * This approach simplifies processing in ovn-controller, but it
+     * may be enhanced, if necessary, to parse the complete CIDR-based
+     * threshold configurations to SB to reduce unnecessary refreshes. */
+    uint32_t age_threshold = min_mac_binding_age_threshold(
+                                   smap_get(&nbr->options,
+                                           "mac_binding_age_threshold"));
+    if (age_threshold) {
+        smap_add_format(external_ids, "mac_binding_age_threshold",
+                        "%u", age_threshold);
+    }
+
+    /* For backwards-compatibility, also store the NB UUID in
+     * external-ids:logical-router. This is useful if ovn-controller
+     * has not updated and expects this to be where to find the
+     * UUID.
+     */
+    smap_add_format(external_ids, "logical-router", UUID_FMT,
+                    UUID_ARGS(&nbr->header_.uuid));
+}
+
+static struct ovn_unsynced_datapath *
+allocate_unsynced_router(const struct nbrec_logical_router *nbr)
+{
+    struct ovn_unsynced_datapath *dp =
+        ovn_unsynced_datapath_alloc(nbr->name, DP_ROUTER,
+                                    smap_get_int(&nbr->options,
+                                                 "requested-tnl-key", 0),
+                                    &nbr->header_);
+
+    gather_external_ids(nbr, &dp->external_ids);
+    return dp;
+}
+
+static bool
+logical_router_is_enabled(const struct nbrec_logical_router *nbr)
+{
+    return !nbr->enabled || *nbr->enabled;
+}
+
 enum engine_node_state
 en_datapath_logical_router_run(struct engine_node *node , void *data)
 {
@@ -47,69 +121,106 @@ en_datapath_logical_router_run(struct engine_node *node , 
void *data)
 
     const struct nbrec_logical_router *nbr;
     NBREC_LOGICAL_ROUTER_TABLE_FOR_EACH (nbr, nb_lr_table) {
-        if (nbr->enabled && !(*nbr->enabled)) {
+        if (!logical_router_is_enabled(nbr)) {
             continue;
         }
-        struct ovn_unsynced_datapath *dp =
-            ovn_unsynced_datapath_alloc(nbr->name, DP_ROUTER,
-                                        smap_get_int(&nbr->options,
-                                                     "requested-tnl-key", 0),
-                                        &nbr->header_);
-
-        smap_add(&dp->external_ids, "name", dp->name);
-        const char *neutron_router = smap_get(&nbr->options,
-                                               "neutron:router_name");
-        if (neutron_router && neutron_router[0]) {
-            smap_add(&dp->external_ids, "name2", neutron_router);
-        }
+        struct ovn_unsynced_datapath *dp = allocate_unsynced_router(nbr);
+        hmap_insert(&map->dps, &dp->hmap_node, uuid_hash(&nbr->header_.uuid));
+    }
 
-        int64_t ct_zone_limit = ovn_smap_get_llong(&nbr->options,
-                                                   "ct-zone-limit", -1);
-        if (ct_zone_limit > 0) {
-            smap_add_format(&dp->external_ids, "ct-zone-limit", "%"PRId64,
-                            ct_zone_limit);
-        }
+    return EN_UPDATED;
+}
 
-        int nat_default_ct = smap_get_int(&nbr->options,
-                                          "snat-ct-zone", -1);
-        if (nat_default_ct >= 0) {
-            smap_add_format(&dp->external_ids, "snat-ct-zone", "%d",
-                            nat_default_ct);
-        }
+void
+en_datapath_logical_router_clear_tracked_data(void *data)
+{
+    struct ovn_unsynced_datapath_map *map = data;
+    ovn_unsynced_datapath_map_clear_tracked_data(map);
+}
 
-        bool learn_from_arp_request =
-            smap_get_bool(&nbr->options, "always_learn_from_arp_request",
-                          true);
-        if (!learn_from_arp_request) {
-            smap_add(&dp->external_ids, "always_learn_from_arp_request",
-                     "false");
-        }
+enum engine_input_handler_result
+en_datapath_logical_router_logical_router_handler(struct engine_node *node,
+                                                  void *data)
+{
+    const struct nbrec_logical_router_table *nb_lr_table =
+        EN_OVSDB_GET(engine_get_input("NB_logical_router", node));
 
-        /* For timestamp refreshing, the smallest threshold of the option is
-         * set to SB to make sure all entries are refreshed in time.
-         * This approach simplifies processing in ovn-controller, but it
-         * may be enhanced, if necessary, to parse the complete CIDR-based
-         * threshold configurations to SB to reduce unnecessary refreshes. */
-        uint32_t age_threshold = min_mac_binding_age_threshold(
-                                       smap_get(&nbr->options,
-                                               "mac_binding_age_threshold"));
-        if (age_threshold) {
-            smap_add_format(&dp->external_ids, "mac_binding_age_threshold",
-                            "%u", age_threshold);
-        }
+    struct ovn_unsynced_datapath_map *map = data;
+    enum engine_input_handler_result result = EN_HANDLED_UNCHANGED;
 
-        /* For backwards-compatibility, also store the NB UUID in
-         * external-ids:logical-router. This is useful if ovn-controller
-         * has not updated and expects this to be where to find the
-         * UUID.
-         */
-        smap_add_format(&dp->external_ids, "logical-router", UUID_FMT,
-                        UUID_ARGS(&nbr->header_.uuid));
+    const struct nbrec_logical_router *nbr;
+    bool is_deleted;
+    bool is_new;
+    bool is_updated;
+    struct ovn_unsynced_datapath *udp;
+    NBREC_LOGICAL_ROUTER_TABLE_FOR_EACH_TRACKED (nbr, nb_lr_table) {
+        udp = ovn_unsynced_datapath_find(map, &nbr->header_.uuid);
+        is_deleted = false;
+        is_new = false;
+        is_updated = false;
+        if (nbrec_logical_router_is_deleted(nbr)) {
+            if (udp) {
+                is_deleted = true;
+            } else {
+                return EN_UNHANDLED;
+            }
+        } else if (nbrec_logical_router_is_new(nbr)) {
+            if (udp) {
+                return EN_UNHANDLED;
+            }
+            if (logical_router_is_enabled(nbr)) {
+                is_new = true;
+            }
+        } else {
+            /* Updated logical routers may need to be treated as "new" or
+             * "deleted" if the "enabled" setting changed on the router.
+             */
+            if (!udp) {
+                if (logical_router_is_enabled(nbr)) {
+                    /* The router was previously not enabled but now
+                     * is. Treat it as new.
+                     */
+                    is_new = true;
+                }
+            } else if (!logical_router_is_enabled(nbr)) {
+                /* The router was previously enabled but now is not
+                 * enabled. Treat it as if it were deleted.
+                 */
+                is_deleted = true;
+            } else {
+                is_updated = true;
+            }
+        }
 
-        hmap_insert(&map->dps, &dp->hmap_node, uuid_hash(&nbr->header_.uuid));
+        if (is_new) {
+            ovs_assert(!udp);
+            udp = allocate_unsynced_router(nbr);
+            hmap_insert(&map->dps, &udp->hmap_node,
+                        uuid_hash(&nbr->header_.uuid));
+            hmapx_add(&map->new, udp);
+            result = EN_HANDLED_UPDATED;
+        } else if (is_deleted) {
+            ovs_assert(udp);
+            hmap_remove(&map->dps, &udp->hmap_node);
+            hmapx_add(&map->deleted, udp);
+            result = EN_HANDLED_UPDATED;
+        } else if (is_updated) {
+            /* We could try to modify the unsynced datapath in place
+             * based on the new logical router, but it's easier to
+             * just allocate a new one.
+             */
+            ovs_assert(udp);
+            udp->requested_tunnel_key = smap_get_int(&nbr->options,
+                                                     "requested-tnl-key", 0);
+            smap_destroy(&udp->external_ids);
+            smap_init(&udp->external_ids);
+            gather_external_ids(nbr, &udp->external_ids);
+            hmapx_add(&map->updated, udp);
+            result = EN_HANDLED_UPDATED;
+        }
     }
 
-    return EN_UPDATED;
+    return result;
 }
 
 void
diff --git a/northd/en-datapath-logical-router.h 
b/northd/en-datapath-logical-router.h
index 31e70fbf5..fa2bf798f 100644
--- a/northd/en-datapath-logical-router.h
+++ b/northd/en-datapath-logical-router.h
@@ -25,6 +25,7 @@ void *en_datapath_logical_router_init(struct engine_node *,
 
 enum engine_node_state en_datapath_logical_router_run(struct engine_node *,
                                                       void *data);
+void en_datapath_logical_router_clear_tracked_data(void *data);
 void en_datapath_logical_router_cleanup(void *data);
 
 struct ovn_synced_logical_router {
@@ -45,4 +46,8 @@ enum engine_node_state en_datapath_synced_logical_router_run(
 
 void en_datapath_synced_logical_router_cleanup(void *data);
 
+enum engine_input_handler_result
+en_datapath_logical_router_logical_router_handler(struct engine_node *,
+                                                  void *);
+
 #endif /* EN_DATAPATH_LOGICAL_ROUTER_H */
diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c
index 854e7add3..bf1a09c08 100644
--- a/northd/inc-proc-northd.c
+++ b/northd/inc-proc-northd.c
@@ -182,7 +182,7 @@ static ENGINE_NODE(advertised_route_sync);
 static ENGINE_NODE(learned_route_sync, CLEAR_TRACKED_DATA);
 static ENGINE_NODE(dynamic_routes);
 static ENGINE_NODE(group_ecmp_route, CLEAR_TRACKED_DATA);
-static ENGINE_NODE(datapath_logical_router);
+static ENGINE_NODE(datapath_logical_router, CLEAR_TRACKED_DATA);
 static ENGINE_NODE(datapath_logical_switch);
 static ENGINE_NODE(datapath_sync);
 static ENGINE_NODE(datapath_synced_logical_router);
@@ -220,7 +220,8 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
     engine_add_input(&en_datapath_logical_switch, &en_nb_logical_switch, NULL);
     engine_add_input(&en_datapath_logical_switch, &en_global_config, NULL);
 
-    engine_add_input(&en_datapath_logical_router, &en_nb_logical_router, NULL);
+    engine_add_input(&en_datapath_logical_router, &en_nb_logical_router,
+                     en_datapath_logical_router_logical_router_handler);
 
     engine_add_input(&en_datapath_sync, &en_datapath_logical_switch, NULL);
     engine_add_input(&en_datapath_sync, &en_datapath_logical_router, NULL);
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index 58d8e66e9..64506e79e 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -17733,3 +17733,48 @@ check as northd ovn-appctl -t ovn-northd 
inc-engine/clear-stats
 
 AT_CLEANUP
 ])
+
+OVN_FOR_EACH_NORTHD_NO_HV([
+AT_SETUP([Unsynced Logical Router Incremental Processing])
+ovn_start
+
+# Start by issuing a recompute just so we know that the engine
+# has run at least once.
+check as northd ovn-appctl -t ovn-northd inc-engine/recompute
+
+# Now all of our router changes should be handled incrementally.
+# There is no circumstance under which en-datapath-logical-router
+# should have to recompute.
+
+# Add a router
+check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
+check ovn-nbctl --wait=sb lr-add lr1
+check_engine_stats datapath_logical_router norecompute compute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+# Update router options
+check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
+check ovn-nbctl --wait=sb set Logical_Router lr1 options:ct-zone-limit=10
+check_engine_stats datapath_logical_router norecompute compute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+# Disable logical router.
+check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
+check ovn-nbctl --wait=sb set Logical_Router lr1 enabled=false
+check_engine_stats datapath_logical_router norecompute compute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+# Re-enable logical router.
+check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
+check ovn-nbctl --wait=sb set Logical_Router lr1 enabled=true
+check_engine_stats datapath_logical_router norecompute compute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+# Delete logical router
+check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
+check ovn-nbctl --wait=sb lr-del lr1
+check_engine_stats datapath_logical_router norecompute compute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+AT_CLEANUP
+])
-- 
2.49.0

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to