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