On 10/16/25 6:51 PM, Lorenzo Bianconi wrote:
> Collect vif and router ports IPs/MAC bindings in SB Advertised_Mac_Binding
> table in
> order to announce them as EVPN Type2 Route messages.
>
> Signed-off-by: Lorenzo Bianconi <[email protected]>
> ---
Hi Lorenzo,
Thanks for the patch!
> NEWS | 3 +
> northd/en-advertised-route-sync.c | 250 ++++++++++++++++++++++++++++++
> northd/en-advertised-route-sync.h | 7 +
> northd/inc-proc-northd.c | 10 +-
> ovn-nb.xml | 6 +
> ovn-sb.ovsschema | 22 ++-
> ovn-sb.xml | 29 ++++
> tests/ovn-northd.at | 48 +++++-
> 8 files changed, 366 insertions(+), 9 deletions(-)
>
> diff --git a/NEWS b/NEWS
> index fb4d63194..77a329669 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -14,6 +14,9 @@ Post v25.09.0
> Switches. If set to "true" ovn-controller will give preference to SB
> (Static_)MAC_Bindings of adjacent logical routers over ARPs learned
> through EVPN on the switch.
> + * Add the "other_config:dynamic-routing-redistribute=ip" to Logical
> + Switches to announce local IPs/MAC bindings belonging to the same
> + EVPN domain.
> - Add support for Network Function insertion in OVN with stateful traffic
> redirection capability in Logical Switch datapath. The feature
> introduces
> three new NB database tables:
> diff --git a/northd/en-advertised-route-sync.c
> b/northd/en-advertised-route-sync.c
> index 423baaacf..1bcf1ebf9 100644
> --- a/northd/en-advertised-route-sync.c
> +++ b/northd/en-advertised-route-sync.c
> @@ -834,3 +834,253 @@ advertised_route_table_sync(
> hmap_destroy(&sync_routes);
> }
>
> +struct evpn_type2 {
In line with my other comments, I think I'd call this "struct
advertised_mac_binding". Or, similar to "struct ar_entry" just "struct
amb_entry". And I'd also update all the static function names similarly.
> + struct hmap_node hmap_node;
> +
> + const struct sbrec_port_binding *sb;
> + char *ip;
> + char *mac;
> +};
> +
> +static bool
> +evpn_is_ip_redistribute_running(const struct ovn_datapath *od)
Nit: I'd call this "evpn_ip_redistribution_enabled()".
> +{
> + int64_t vni = ovn_smap_get_llong(&od->nbs->other_config,
> + "dynamic-routing-vni", -1);
> + if (!ovn_is_valid_vni(vni)) {
> + return false;
> + }
We already have od->has_evpn_vni. Let's use that.
> +
> + const char *redistribute = smap_get(&od->nbs->other_config,
> + "dynamic-routing-redistribute");
> + return redistribute && !strcmp(redistribute, "ip");
> +}
> +
> +static struct evpn_type2 *
> +evpn_type2_entry_find(struct hmap *map,
> + const char *ip, const char *mac)
We should hash and compare datapath and logical port too. What if you
have different logical switches connected to different logical routers
that happen to have the same IPs?
> +{
> + uint32_t hash = hash_string(ip, 0);
> + hash = hash_string(mac, hash);
> +
> + struct evpn_type2 *e;
> + HMAP_FOR_EACH_WITH_HASH (e, hmap_node, hash, map) {
> + if (!strcmp(e->ip, ip) && !strcmp(e->mac, mac)) {
> + return e;
> + }
> + }
> +
> + return NULL;
> +}
> +
> +static void
> +evpn_type2_entry_add(struct hmap *map,
> + const struct sbrec_port_binding *sb,
> + const char *ip, const char *mac)
> +{
> + struct evpn_type2 *e = xmalloc(sizeof *e);
> + e->ip = xstrdup(ip);
> + e->mac = xstrdup(mac);
> + e->sb = sb;
> +
> + uint32_t hash = hash_string(ip, 0);
> + hash = hash_string(mac, hash);
> + hmap_insert(map, &e->hmap_node, hash);
> +}
> +
> +static void
> +evpn_type2_entry_destroy(struct evpn_type2 *e)
> +{
> + free(e->ip);
> + free(e->mac);
> + free(e);
> +}
> +
> +static void
> +evpn_add_type2(struct hmap *map, const struct sbrec_port_binding *sb,
> + struct lport_addresses *addr)
> +{
> + struct ds ip = DS_EMPTY_INITIALIZER;
> +
> + if (!addr) {
> + return;
> + }
> +
> + for (size_t i = 0; i < addr->n_ipv4_addrs; i++) {
> + ds_clear(&ip);
> + ds_put_format(&ip, "%s/32", addr->ipv4_addrs[i].addr_s);
> + if (!evpn_type2_entry_find(map, ds_cstr(&ip), addr->ea_s)) {
> + evpn_type2_entry_add(map, sb, ds_cstr(&ip), addr->ea_s);
The SB.Advertised_MAC_Binding.IP is just an IP, we don't need prefix
lenght. We don't need to build this intermediate string, we can just
pass 'addr->ipv4_addrs[i].addr_s'.
> + }
> + }
> +
> + for (size_t i = 0; i < addr->n_ipv6_addrs; i++) {
> + if (prefix_is_link_local(&addr->ipv6_addrs[i].addr, 128)) {
> + continue;
> + }
> +
> + ds_clear(&ip);
> + ds_put_format(&ip, "%s/128", addr->ipv6_addrs[i].addr_s);
> + if (!evpn_type2_entry_find(map, ds_cstr(&ip), addr->ea_s)) {
> + evpn_type2_entry_add(map, sb, ds_cstr(&ip), addr->ea_s);
Here too, just pass 'addr->ipv6_addrs[i].addr_s'.
> + }
> + }
> +
> + ds_destroy(&ip);
> +}
> +
> +static void
> +build_evpn_type2_for_ls(const struct ovn_datapath *od, struct hmap *map)
> +{
> + ovs_assert(od->nbs);
> +
> + if (!evpn_is_ip_redistribute_running(od)) {
> + return;
> + }
> +
> + struct ovn_port *op;
> + HMAP_FOR_EACH (op, dp_node, &od->ports) {
> + if (!op->sb) {
> + continue;
> + }
> +
> + if (lsp_is_router(op->nbsp) && op->peer) {
> + evpn_add_type2(map, op->sb, &op->peer->lrp_networks);
> + }
> +
> + if (!strcmp(op->nbsp->type, "")) { /* LSP */
> + evpn_add_type2(map, op->sb, op->lsp_addrs);
> + }
> + }
> +}
> +
> +static void
> +build_evpn_type2_for_lr(const struct ovn_datapath *od, struct hmap *map)
> +{
> + ovs_assert(od->nbr);
> +
> + struct ovn_port *op;
> + HMAP_FOR_EACH (op, dp_node, &od->ports) {
> + struct ovn_port *peer = op->peer;
> + if (!peer || !peer->od || !peer->od->nbs || !peer->sb) {
> + continue;
> + }
> +
> + struct ovn_datapath *peer_od = peer->od;
> + if (evpn_is_ip_redistribute_running(peer_od)) {
> + evpn_add_type2(map, peer->sb, &op->lrp_networks);
> + }
This whole function seems a bit redundant because
build_evpn_type2_for_ls() already handles router ports.
> + }
> +}
> +
> +void *
> +en_evpn_type2_sync_init(struct engine_node *node OVS_UNUSED,
> + struct engine_arg *arg OVS_UNUSED)
> +{
> + return NULL;
> +}
> +
> +enum engine_node_state
> +en_evpn_type2_sync_run(struct engine_node *node, void *data OVS_UNUSED)
> +{
> + struct northd_data *northd_data = engine_get_input_data("northd", node);
> + const struct sbrec_advertised_mac_binding_table *sbrec_adv_mb_table =
> + EN_OVSDB_GET(engine_get_input("SB_advertised_mac_binding", node));
> + const struct engine_context *eng_ctx = engine_get_context();
> +
> + struct hmap evpn_type2_map = HMAP_INITIALIZER(&evpn_type2_map);
> +
> + struct ovn_datapath *od;
> + HMAP_FOR_EACH (od, key_node, &northd_data->lr_datapaths.datapaths) {
> + build_evpn_type2_for_lr(od, &evpn_type2_map);
> + }
> +
> + HMAP_FOR_EACH (od, key_node, &northd_data->ls_datapaths.datapaths) {
> + build_evpn_type2_for_ls(od, &evpn_type2_map);
> + }
> +
> + struct evpn_type2 *e;
> + const struct sbrec_advertised_mac_binding *sb_adv_mb;
> + SBREC_ADVERTISED_MAC_BINDING_TABLE_FOR_EACH_SAFE (sb_adv_mb,
> + sbrec_adv_mb_table) {
> + e = evpn_type2_entry_find(&evpn_type2_map, sb_adv_mb->ip,
> + sb_adv_mb->mac);
> + if (!e) {
> + sbrec_advertised_mac_binding_delete(sb_adv_mb);
> + } else {
> + hmap_remove(&evpn_type2_map, &e->hmap_node);
> + evpn_type2_entry_destroy(e);
> + }
> + }
> +
> + HMAP_FOR_EACH_POP (e, hmap_node, &evpn_type2_map) {
> + sb_adv_mb =
> + sbrec_advertised_mac_binding_insert(eng_ctx->ovnsb_idl_txn);
> + sbrec_advertised_mac_binding_set_datapath(sb_adv_mb,
> e->sb->datapath);
> + sbrec_advertised_mac_binding_set_logical_port(sb_adv_mb, e->sb);
> + sbrec_advertised_mac_binding_set_ip(sb_adv_mb, e->ip);
> + sbrec_advertised_mac_binding_set_mac(sb_adv_mb, e->mac);
> + evpn_type2_entry_destroy(e);
> + }
> +
> + hmap_destroy(&evpn_type2_map);
> +
> + return EN_UPDATED;
> +}
> +
> +void
> +en_evpn_type2_sync_cleanup(void *data OVS_UNUSED)
> +{
> +}
> +
> +enum engine_input_handler_result
> +evpn_type2_sync_northd_change_handler(struct engine_node *node,
> + void *data OVS_UNUSED)
> +{
> + struct northd_data *northd_data = engine_get_input_data("northd", node);
> + if (!northd_has_tracked_data(&northd_data->trk_data)) {
> + return EN_UNHANDLED;
> + }
> +
> + struct northd_tracked_data *nd_changes = &northd_data->trk_data;
> +
> + struct hmapx_node *n;
> + HMAPX_FOR_EACH (n, &nd_changes->trk_switches.crupdated) {
> + const struct ovn_datapath *od = n->data;
> + if (evpn_is_ip_redistribute_running(od)) {
> + return EN_UNHANDLED;
> + }
> + }
> + HMAPX_FOR_EACH (n, &nd_changes->trk_switches.deleted) {
> + const struct ovn_datapath *od = n->data;
> + if (evpn_is_ip_redistribute_running(od)) {
> + return EN_UNHANDLED;
> + }
> + }
> +
> + HMAPX_FOR_EACH (n, &nd_changes->trk_routers.crupdated) {
> + const struct ovn_datapath *od = n->data;
> + struct ovn_port *op;
> + HMAP_FOR_EACH (op, dp_node, &od->ports) {
> + struct ovn_port *peer = op->peer;
> + if (peer && peer->od && peer->od->nbs &&
> + evpn_is_ip_redistribute_running(peer->od)) {
> + return EN_UNHANDLED;
> + }
> + }
> + }
> + HMAPX_FOR_EACH (n, &nd_changes->trk_routers.deleted) {
> + const struct ovn_datapath *od = n->data;
> + struct ovn_port *op;
> + HMAP_FOR_EACH (op, dp_node, &od->ports) {
> + struct ovn_port *peer = op->peer;
> + if (peer && peer->od && peer->od->nbs &&
> + evpn_is_ip_redistribute_running(peer->od)) {
> + return EN_UNHANDLED;
> + }
> + }
> + }
> +
> + return EN_HANDLED_UNCHANGED;
> +}
> +
> diff --git a/northd/en-advertised-route-sync.h
> b/northd/en-advertised-route-sync.h
> index 7a2927000..2557a6c7f 100644
> --- a/northd/en-advertised-route-sync.h
> +++ b/northd/en-advertised-route-sync.h
> @@ -42,4 +42,11 @@ enum engine_node_state en_advertised_route_sync_run(struct
> engine_node *,
> void *en_dynamic_routes_init(struct engine_node *, struct engine_arg *);
> void en_dynamic_routes_cleanup(void *data);
> enum engine_node_state en_dynamic_routes_run(struct engine_node *, void
> *data);
> +
> +void *en_evpn_type2_sync_init(struct engine_node *, struct engine_arg *);
> +void en_evpn_type2_sync_cleanup(void *data);
> +enum engine_node_state en_evpn_type2_sync_run(struct engine_node *,
> + void *data);
I think OVN developers might not always be very familiar with the
details of BGP+EVPN. "type2" might be too specific. In the end we just
populate the "Advertised_Mac_Binding" table. Why not call this
"en_advertised_mac_binding_run/init/cleanup()"?
> +enum engine_input_handler_result
> +evpn_type2_sync_northd_change_handler(struct engine_node *, void *data);
> #endif /* EN_ADVERTISED_ROUTE_SYNC_H */
> diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c
> index fdea550d7..762c72f2d 100644
> --- a/northd/inc-proc-northd.c
> +++ b/northd/inc-proc-northd.c
> @@ -118,7 +118,8 @@ static unixctl_cb_func chassis_features_list;
> SB_NODE(ecmp_nexthop) \
> SB_NODE(acl_id) \
> SB_NODE(advertised_route) \
> - SB_NODE(learned_route)
> + SB_NODE(learned_route) \
> + SB_NODE(advertised_mac_binding)
>
> enum sb_engine_node {
> #define SB_NODE(NAME) SB_##NAME,
> @@ -181,6 +182,7 @@ static ENGINE_NODE(ecmp_nexthop);
> static ENGINE_NODE(multicast_igmp);
> static ENGINE_NODE(acl_id);
> static ENGINE_NODE(advertised_route_sync);
> +static ENGINE_NODE(evpn_type2_sync);
I think I'd call this "advertised_mac_binding_sync".
> static ENGINE_NODE(learned_route_sync, CLEAR_TRACKED_DATA);
> static ENGINE_NODE(dynamic_routes);
> static ENGINE_NODE(group_ecmp_route, CLEAR_TRACKED_DATA);
> @@ -355,6 +357,11 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
> engine_add_input(&en_advertised_route_sync, &en_northd,
> advertised_route_sync_northd_change_handler);
>
> + engine_add_input(&en_evpn_type2_sync, &en_sb_advertised_mac_binding,
> NULL);
We need to add "SB.Advertised_MAC_Binding" to the set of ignored tables
in ovn-northd.c, see the part after the "/* Disable alerting for pure
write-only columns. */" comment there. Otherwise we trigger a recompute
every time we add a new entry.
> + engine_add_input(&en_evpn_type2_sync, &en_sb_port_binding, NULL);
This feels a bit extreme though. Should we add a handler for
SB.Port_Binding changes? E.g., only recompute when "addresses" change
or when ports are added/deleted and only if the switch has EVPN enabled?
> + engine_add_input(&en_evpn_type2_sync, &en_northd,
> + evpn_type2_sync_northd_change_handler);
> +
> engine_add_input(&en_learned_route_sync, &en_sb_learned_route,
> learned_route_sync_sb_learned_route_change_handler);
> engine_add_input(&en_learned_route_sync, &en_northd,
> @@ -452,6 +459,7 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
> engine_add_input(&en_sync_from_sb, &en_sb_ha_chassis_group, NULL);
>
> engine_add_input(&en_northd_output, &en_acl_id, NULL);
> + engine_add_input(&en_northd_output, &en_evpn_type2_sync, NULL);
> engine_add_input(&en_northd_output, &en_sync_from_sb, NULL);
> engine_add_input(&en_northd_output, &en_sync_to_sb,
> northd_output_sync_to_sb_handler);
> diff --git a/ovn-nb.xml b/ovn-nb.xml
> index 73b5f213f..c2d16f5eb 100644
> --- a/ovn-nb.xml
> +++ b/ovn-nb.xml
> @@ -977,6 +977,12 @@
> routers.
> </p>
>
> + <p>
> + If <code>ip</code> is specified then ovn-controller will advertise
> + all IPs/MACs bindings that are local to the chassis. The applies to
> + VIFs and router ports.
> + </p>
> +
> <p>
> NOTE: this feature is experimental and may be subject to
> removal/change in the future.
> diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema
> index f22141489..9fddf042d 100644
> --- a/ovn-sb.ovsschema
> +++ b/ovn-sb.ovsschema
> @@ -1,7 +1,7 @@
> {
> "name": "OVN_Southbound",
> - "version": "21.6.0",
> - "cksum": "1200327755 35814",
> + "version": "21.7.0",
> + "cksum": "1565005821 36671",
> "tables": {
> "SB_Global": {
> "columns": {
> @@ -695,6 +695,22 @@
> "id": {"type": {"key": {"type": "integer",
> "minInteger": 0,
> "maxInteger": 32767}}}},
> - "isRoot": true}
> + "isRoot": true},
> + "Advertised_Mac_Binding": {
Nit: all other mac binding tables capitalize MAC, i.e.: "MAC_Binding"
and "Static_MAC_Binding".
> + "columns": {
> + "datapath": {"type": {"key": {"type": "uuid",
> + "refTable": "Datapath_Binding",
> + "refType": "strong"}}},
> + "logical_port": {"type": {"key": {"type": "uuid",
> + "refTable": "Port_Binding",
> + "refType": "strong"}}},
> + "ip": {"type": "string"},
> + "mac": {"type": "string"},
> + "external_ids": {
> + "type": {"key": "string", "value": "string",
> + "min": 0, "max": "unlimited"}}},
> + "indexes": [["datapath", "logical_port",
> + "ip", "mac"]],
> + "isRoot": true}
> }
> }
> diff --git a/ovn-sb.xml b/ovn-sb.xml
> index a781129b7..3eab5f998 100644
> --- a/ovn-sb.xml
> +++ b/ovn-sb.xml
> @@ -5457,4 +5457,33 @@ tcp.flags = RST;
> <code>allow-established</code> ACL.
> </column>
> </table>
> +
> + <table name="Advertised_Mac_Binding">
> + <p>
> + Each record represents an ip/mac binding the
> + <code>OVN_Northbound.Logical_Switch</code> is announcing outside
> + the network fabric if <code>EVPN</code> is enabled on the datapath.
> + </p>
> +
> + <column name="datapath">
> + The datapath belonging to the
> + <code>OVN_Northbound.Logical_Switch</code> this type2 route belongs to.
I think users might not be very familiar with the details of BGP+EVPN.
"Type2 route" might be too specific. Maybe let's just call this "ip/mac
binding" as you did in the description of the table above?
> + </column>
> +
> + <column name="logical_port">
> + This is the <ref table="Port_Binding"/> this type2 route belongs to.
Same here.
> + </column>
> +
> + <column name="ip">
> + Announced host route (e.g. 192.168.100.0/32).
It's an IP, we don't need the mask. It could just be "Announced IP".
> + </column>
> +
> + <column name="mac">
> + Announced mac address.
> + </column>
> +
> + <column name="external_ids">
> + See <em>External IDs</em> at the beginning of this document.
> + </column>
> + </table>
> </database>
> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> index 07fb57bd6..66b36ae10 100644
> --- a/tests/ovn-northd.at
> +++ b/tests/ovn-northd.at
> @@ -4077,7 +4077,7 @@ check_engine_stats northd recompute nocompute
> check_engine_stats bfd recompute nocompute
> check_engine_stats routes recompute nocompute
> check_engine_stats lflow recompute nocompute
> -check_engine_stats northd_output norecompute compute
> +check_engine_stats northd_output recompute nocompute
This is because we with this patch we trigger a recompute of the new
en_evpn_type2_sync I-P node each and every time _any_ port binding
changes. That's a performance regression in a way. Please see my
comment about adding a change handler above.
> CHECK_NO_CHANGE_AFTER_RECOMPUTE
> check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
>
> @@ -4093,7 +4093,7 @@ check_engine_stats northd recompute nocompute
> check_engine_stats bfd recompute nocompute
> check_engine_stats routes recompute nocompute
> check_engine_stats lflow recompute nocompute
> -check_engine_stats northd_output norecompute compute
> +check_engine_stats northd_output recompute nocompute
Same here I guess.
> CHECK_NO_CHANGE_AFTER_RECOMPUTE
> check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
>
> @@ -4105,7 +4105,7 @@ check_engine_stats northd recompute nocompute
> check_engine_stats bfd recompute nocompute
> check_engine_stats route_policies recompute nocompute
> check_engine_stats lflow recompute nocompute
> -check_engine_stats northd_output norecompute compute
> +check_engine_stats northd_output recompute nocompute
Here too.
> CHECK_NO_CHANGE_AFTER_RECOMPUTE
> check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
>
> @@ -4140,7 +4140,7 @@ check_engine_stats northd recompute nocompute
> check_engine_stats bfd recompute nocompute
> check_engine_stats routes recompute nocompute
> check_engine_stats lflow recompute nocompute
> -check_engine_stats northd_output norecompute compute
> +check_engine_stats northd_output recompute compute
> CHECK_NO_CHANGE_AFTER_RECOMPUTE
> check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
>
> @@ -4155,7 +4155,7 @@ check_engine_stats northd recompute nocompute
> check_engine_stats bfd recompute nocompute
> check_engine_stats route_policies recompute nocompute
> check_engine_stats lflow recompute nocompute
> -check_engine_stats northd_output norecompute compute
> +check_engine_stats northd_output recompute compute
Here too.
> CHECK_NO_CHANGE_AFTER_RECOMPUTE
> check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
>
> @@ -18716,3 +18716,41 @@ AT_CHECK(
>
> AT_CLEANUP
> ])
> +
> +OVN_FOR_EACH_NORTHD_NO_HV([
> +AT_SETUP([LS EVPN Route Type2 sync])
> +ovn_start ovn-northd
> +
> +AS_BOX([Create logical switch.])
> +check ovn-nbctl ls-add ls-evpn \
> + -- set logical_switch ls-evpn other_config:dynamic-routing-vni=10 \
> + -- lsp-add ls-evpn vm1 \
> + -- lsp-set-addresses vm1 "00:00:00:00:01:00 10.0.0.2"
> +check ovn-nbctl --wait=sb sync
> +check_row_count Advertised_Mac_Binding 0
> +ls_evpn_uuid=$(fetch_column Datapath_Binding _uuid external_ids:name=ls-evpn)
> +
> +check ovn-nbctl --wait=sb set logical_switch ls-evpn
> other_config:dynamic-routing-redistribute=ip
> +wait_row_count Advertised_Mac_Binding 1 ip=10.0.0.2/32
> datapath=$ls_evpn_uuid mac='00\:00\:00\:00\:01\:00'
> +
> +check ovn-nbctl lr-add lr \
> + -- lrp-add lr lrp 00:00:00:00:01:01 10.0.0.1/24 \
> + -- lsp-add ls-evpn ls-evpn-lrp \
> + -- lsp-set-type ls-evpn-lrp router \
> + -- lsp-set-addresses ls-evpn-lrp 00:00:00:00:01:01 \
> + -- lsp-set-options ls-evpn-lrp router-port=lrp
> +check ovn-nbctl --wait=sb sync
> +
> +wait_row_count Advertised_Mac_Binding 1 ip=10.0.0.2/32
> datapath=$ls_evpn_uuid mac='00\:00\:00\:00\:01\:00'
> +wait_row_count Advertised_Mac_Binding 1 ip=10.0.0.1/32
> datapath=$ls_evpn_uuid mac='00\:00\:00\:00\:01\:01'
This should be "check_row_count" because we used "--wait=sb sync" above.
> +check_row_count Advertised_Mac_Binding 2
> +
> +check ovn-nbctl --wait=sb lsp-del vm1
> +wait_row_count Advertised_Mac_Binding 1 ip=10.0.0.1/32
> datapath=$ls_evpn_uuid mac='00\:00\:00\:00\:01\:01'
Here too.
> +check_row_count Advertised_Mac_Binding 1
> +
> +check ovn-nbctl --wait=sb clear logical_switch ls-evpn other_config
> +wait_row_count Advertised_Mac_Binding 0
This one as well.
> +
> +AT_CLEANUP
> +])
Regards,
Dumitru
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev