diff --git a/controller/binding.c b/controller/binding.c index ea5bf5a9fd..88c9640c3a 100644 --- a/controller/binding.c +++ b/controller/binding.c @@ -825,6 +825,16 @@ static bool binding_lport_update_port_sec( static bool ovs_iface_matches_lport_iface_id_ver( const struct ovsrec_interface *, const struct sbrec_port_binding *); +static bool cleanup_patch_port_local_dps( + const struct sbrec_port_binding *, const struct sbrec_port_binding *cr_pb, + const struct sbrec_port_binding *peer, struct local_datapath *ld, + struct binding_ctx_in *b_ctx_in, + struct binding_ctx_out *b_ctx_out, + bool *cleanup); +static bool local_datapath_is_relevant( + struct local_datapath *, struct local_datapath *ignore_peer_ld, + struct hmap *local_datapaths, int *depth, const struct sbrec_chassis *, + struct ovsdb_idl_index *); void related_lports_init(struct related_lports *rp) @@ -1062,6 +1072,13 @@ binding_dump_related_lports(struct related_lports *related_lports, } } +struct dp_binding { + struct hmap_node key_node; + + uint32_t dp_key; + struct hmapx binding_lports; +}; + void binding_dump_local_bindings(struct local_binding_data *lbinding_data, struct ds *out_data) @@ -1130,6 +1147,19 @@ binding_dump_local_bindings(struct local_binding_data *lbinding_data, free(nodes); } +void +binding_dump_local_datapaths(struct hmap *local_datapaths, + struct ds *out_data) +{ + ds_put_cstr(out_data, "Local datapaths:\n"); + struct local_datapath *ld; + HMAP_FOR_EACH (ld, hmap_node, local_datapaths) { + ds_put_format(out_data, "Datapath: %s, type: %s\n", + smap_get(&ld->datapath->external_ids, "name"), + ld->is_switch ? "switch" : "router"); + } +} + void set_pb_chassis_in_sbrec(const struct sbrec_port_binding *pb, const struct sbrec_chassis *chassis_rec, @@ -2137,7 +2167,9 @@ build_local_bindings(struct binding_ctx_in *b_ctx_in, static bool consider_patch_port_for_local_datapaths( const struct sbrec_port_binding *, - struct binding_ctx_in *, struct binding_ctx_out *); + const struct sbrec_port_binding *cr_pb, + struct binding_ctx_in *, struct binding_ctx_out *, + bool check_and_remove_localdps); void binding_run(struct binding_ctx_in *b_ctx_in, struct binding_ctx_out *b_ctx_out) @@ -2182,7 +2214,8 @@ binding_run(struct binding_ctx_in *b_ctx_in, struct binding_ctx_out *b_ctx_out) switch (lport_type) { case LP_PATCH: update_related_lport(pb, b_ctx_out); - consider_patch_port_for_local_datapaths(pb, b_ctx_in, b_ctx_out); + consider_patch_port_for_local_datapaths(pb, NULL, b_ctx_in, + b_ctx_out, false); break; case LP_VTEP: @@ -2238,6 +2271,9 @@ binding_run(struct binding_ctx_in *b_ctx_in, struct binding_ctx_out *b_ctx_out) struct lport *lnet_lport = xmalloc(sizeof *lnet_lport); lnet_lport->pb = pb; ovs_list_push_back(&localnet_lports, &lnet_lport->list_node); + if (pb->chassis == b_ctx_in->chassis_rec) { + sbrec_port_binding_set_chassis(pb, NULL); + } break; } @@ -2566,7 +2602,6 @@ consider_iface_release(const struct ovsrec_interface *iface_rec, lbinding->iface->name, &lbinding->iface->header_.uuid); } - } else if (b_lport && b_lport->type == LP_LOCALPORT) { /* lbinding is associated with a localport. Remove it from the * related lports. */ @@ -2956,12 +2991,27 @@ handle_updated_vif_lport(const struct sbrec_port_binding *pb, static bool consider_patch_port_for_local_datapaths(const struct sbrec_port_binding *pb, + const struct sbrec_port_binding *cr_pb, struct binding_ctx_in *b_ctx_in, - struct binding_ctx_out *b_ctx_out) + struct binding_ctx_out *b_ctx_out, + bool check_and_remove_localdps) { - struct local_datapath *ld = - get_local_datapath(b_ctx_out->local_datapaths, - pb->datapath->tunnel_key); + const struct sbrec_port_binding *peer; + struct local_datapath *peer_ld = NULL; + struct local_datapath *ld = NULL; + + ld = get_local_datapath(b_ctx_out->local_datapaths, + pb->datapath->tunnel_key); + if (ld && ld->has_only_dgp_peer_ports) { + /* Nothing much to do. */ + return true; + } + + peer = lport_get_peer(pb, b_ctx_in->sbrec_port_binding_by_name); + if (peer) { + peer_ld = get_local_datapath(b_ctx_out->local_datapaths, + peer->datapath->tunnel_key); + } if (!ld) { /* If 'ld' for this lport is not present, then check if @@ -2969,17 +3019,9 @@ consider_patch_port_for_local_datapaths(const struct sbrec_port_binding *pb, * and peer's datapath is already in the local datapaths, * then add this lport's datapath to the local_datapaths. * */ - const struct sbrec_port_binding *peer; - struct local_datapath *peer_ld = NULL; - peer = lport_get_peer(pb, b_ctx_in->sbrec_port_binding_by_name); - if (peer) { - peer_ld = - get_local_datapath(b_ctx_out->local_datapaths, - peer->datapath->tunnel_key); - } - if (peer_ld && need_add_peer_to_local( - b_ctx_in->sbrec_port_binding_by_name, peer, - b_ctx_in->chassis_rec)) { + if (peer_ld && !peer_ld->has_only_dgp_peer_ports && + need_add_peer_to_local(b_ctx_in->sbrec_port_binding_by_name, peer, + b_ctx_in->chassis_rec)) { ld = add_local_datapath( b_ctx_in->sbrec_datapath_binding_by_key, b_ctx_in->sbrec_port_binding_by_datapath, @@ -2992,7 +3034,7 @@ consider_patch_port_for_local_datapaths(const struct sbrec_port_binding *pb, /* Add the peer datapath to the local datapaths if it's * not present yet. */ - if (need_add_peer_to_local( + if (peer && need_add_peer_to_local( b_ctx_in->sbrec_port_binding_by_name, pb, b_ctx_in->chassis_rec)) { add_local_datapath_peer_port( @@ -3003,6 +3045,18 @@ consider_patch_port_for_local_datapaths(const struct sbrec_port_binding *pb, ld, b_ctx_out->local_datapaths, b_ctx_out->tracked_dp_bindings); } + + if (check_and_remove_localdps) { + bool cleanedup = false; + if (!cleanup_patch_port_local_dps(pb, cr_pb, peer, ld, b_ctx_in, + b_ctx_out, &cleanedup)) { + return false; + } + + if (cleanedup) { + ld = NULL; + } + } } /* If this chassis is requested - try to claim. */ @@ -3021,12 +3075,10 @@ consider_patch_port_for_local_datapaths(const struct sbrec_port_binding *pb, || if_status_is_port_claimed(b_ctx_out->if_mgr, pb->logical_port)) { remove_local_lports(pb->logical_port, b_ctx_out); - if (!release_lport(pb, ld, b_ctx_in->chassis_rec, - !b_ctx_in->ovnsb_idl_txn, - b_ctx_out->tracked_dp_bindings, - b_ctx_out->if_mgr)) { - return false; - } + return release_lport(pb, ld, b_ctx_in->chassis_rec, + !b_ctx_in->ovnsb_idl_txn, + b_ctx_out->tracked_dp_bindings, + b_ctx_out->if_mgr); } return true; } @@ -3086,8 +3138,8 @@ handle_updated_port(struct binding_ctx_in *b_ctx_in, case LP_PATCH: update_related_lport(pb, b_ctx_out); - handled = consider_patch_port_for_local_datapaths(pb, b_ctx_in, - b_ctx_out); + handled = consider_patch_port_for_local_datapaths(pb, NULL, b_ctx_in, + b_ctx_out, true); break; case LP_VTEP: @@ -3130,8 +3182,8 @@ handle_updated_port(struct binding_ctx_in *b_ctx_in, break; } handled = consider_patch_port_for_local_datapaths(distributed_pb, - b_ctx_in, - b_ctx_out); + pb, b_ctx_in, + b_ctx_out, true); break; case LP_EXTERNAL: @@ -3901,3 +3953,142 @@ binding_destroy(void) shash_destroy_free_data(&_qos_ports); sset_clear(&_postponed_ports); } + +static bool +is_patch_pb_chassis_relevant( + const struct sbrec_port_binding *pb, + const struct sbrec_chassis *chassis, + struct ovsdb_idl_index *sbrec_port_binding_by_name) +{ + if (ha_chassis_group_contains(pb->ha_chassis_group, chassis)) { + return true; + } + + const struct sbrec_port_binding *pb_crp = + lport_get_cr_port(sbrec_port_binding_by_name, pb); + if (pb_crp) { + return ha_chassis_group_contains(pb_crp->ha_chassis_group, chassis); + } + + return false; +} + +static bool +cleanup_patch_port_local_dps(const struct sbrec_port_binding *pb, + const struct sbrec_port_binding *cr_pb, + const struct sbrec_port_binding *peer, + struct local_datapath *ld, + struct binding_ctx_in *b_ctx_in, + struct binding_ctx_out *b_ctx_out, + bool *cleanedup) +{ + *cleanedup = false; + if (!peer) { + /* Remove 'pb' from the ld's peer ports as it has no peer. */ + remove_local_datapath_peer_port(pb, ld, + b_ctx_out->local_datapaths); + } + + /* We can consider removing the 'ld' of the patch port 'pb' from the + * local datapaths, if all the below conditions are met + * - 'pb' doesn't have a peer or ld' is a router datapath + * - if 'pb' is a distributed gateway port (dgp), then + * its chassisredirect port's ha chassis group doesn't + * contain our 'chassis rec' + * - and finally 'ld' is not relevant any more. See + * local_datapath_is_relevant() for more details. + * + * Note: If 'ld' can be removed, then all its connected local datapaths + * can also be removed. + * + * For example, if we had sw1-port1 -> sw1 -> lr1 -> sw2 and if + * sw1-port1 resides on this chassis, and if the link between sw1 and + * lr1 is broken, then we can remove lr1 and sw2 from the + * local_datapaths. + * */ + + bool consider_ld_for_removal = !peer || !ld->is_switch; + if (consider_ld_for_removal && cr_pb) { + consider_ld_for_removal = !ha_chassis_group_contains( + cr_pb->ha_chassis_group, b_ctx_in->chassis_rec); + } + + if (!consider_ld_for_removal) { + return true; + } + + int depth = 0; + + bool is_relevant = local_datapath_is_relevant( + ld, NULL, b_ctx_out->local_datapaths, + &depth, b_ctx_in->chassis_rec, + b_ctx_in->sbrec_port_binding_by_name); + + if (depth >= 100) { + /* datapaths are too deeply nested. Fall back to recompute. */ + return false; + } + + if (!is_relevant) { + /* This 'ld' can be removed from the local datapaths as + * - its a router datapath and + * - it has no peers locally. */ + local_datapath_remove_and_destroy(ld, b_ctx_out->local_datapaths, + b_ctx_out->tracked_dp_bindings); + *cleanedup = true; + } + + return true; +} + +static bool +local_datapath_is_relevant(struct local_datapath *ld, + struct local_datapath *ignore_peer_ld, + struct hmap *local_datapaths, int *depth, + const struct sbrec_chassis *chassis, + struct ovsdb_idl_index *sbrec_pb_by_name) +{ + if (!sset_is_empty(&ld->claimed_lports) || + !shash_is_empty(&ld->external_ports) || + !shash_is_empty(&ld->multichassis_ports) || + ld->vtep_port) { + return true; + } + + bool relevant = false; + + if (*depth >= 100) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); + VLOG_WARN_RL(&rl, "datapaths nested too deep"); + return true; + } + + for (size_t i = 0; i < ld->n_peer_ports && !relevant; i++) { + const struct sbrec_port_binding *remote = ld->peer_ports[i].remote; + const struct sbrec_port_binding *local = ld->peer_ports[i].local; + + if (is_patch_pb_chassis_relevant(local, chassis, + sbrec_pb_by_name)) { + return true; + } + + if (is_patch_pb_chassis_relevant(remote, chassis, + sbrec_pb_by_name)) { + return true; + } + + struct local_datapath *peer_ld; + uint32_t remote_peer_ld_key; + remote_peer_ld_key = ld->peer_ports[i].remote->datapath->tunnel_key; + peer_ld = get_local_datapath(local_datapaths, remote_peer_ld_key); + if (peer_ld && !peer_ld->has_only_dgp_peer_ports && + peer_ld != ignore_peer_ld) { + *depth = *depth + 1; + relevant = local_datapath_is_relevant(peer_ld, ld, + local_datapaths, depth, + chassis, sbrec_pb_by_name); + } + } + + return relevant; +} diff --git a/controller/binding.h b/controller/binding.h index d13ae36c79..a4346c3e10 100644 --- a/controller/binding.h +++ b/controller/binding.h @@ -201,6 +201,8 @@ bool binding_handle_port_binding_changes(struct binding_ctx_in *, void binding_tracked_dp_destroy(struct hmap *tracked_datapaths); void binding_dump_local_bindings(struct local_binding_data *, struct ds *); +void binding_dump_local_datapaths(struct hmap *local_datapaths, + struct ds *out_data); void binding_dump_related_lports(struct related_lports *related_lports, struct ds *); diff --git a/controller/local_data.c b/controller/local_data.c index e19b2bf865..bb5740f1d6 100644 --- a/controller/local_data.c +++ b/controller/local_data.c @@ -53,6 +53,13 @@ static struct tracked_datapath *tracked_datapath_create( static bool datapath_is_switch(const struct sbrec_datapath_binding *); static bool datapath_is_transit_switch(const struct sbrec_datapath_binding *); +static bool datapath_has_only_dgp_peer_ports( + const struct sbrec_datapath_binding *); +static void local_datapath_remove_and_destroy__( + struct local_datapath *ld, + const struct sbrec_port_binding *ignore_peer_port, + struct hmap *local_datapaths, + struct hmap *tracked_datapaths); static uint64_t local_datapath_usage; @@ -86,6 +93,7 @@ local_datapath_alloc(const struct sbrec_datapath_binding *dp) ld->datapath = dp; ld->is_switch = datapath_is_switch(dp); ld->is_transit_switch = datapath_is_transit_switch(dp); + ld->has_only_dgp_peer_ports = datapath_has_only_dgp_peer_ports(dp); shash_init(&ld->external_ports); shash_init(&ld->multichassis_ports); sset_init(&ld->claimed_lports); @@ -132,6 +140,14 @@ local_datapath_destroy(struct local_datapath *ld) free(ld); } +void local_datapath_remove_and_destroy(struct local_datapath *ld, + struct hmap *local_datapaths, + struct hmap *tracked_datapaths) +{ + local_datapath_remove_and_destroy__(ld, NULL, local_datapaths, + tracked_datapaths); +} + /* Checks if pb is running on local gw router or pb is a patch port * and the peer datapath should be added to local datapaths. */ bool @@ -226,12 +242,12 @@ add_local_datapath_peer_port( get_local_datapath(local_datapaths, peer->datapath->tunnel_key); if (!peer_ld) { - add_local_datapath__(sbrec_datapath_binding_by_key, - sbrec_port_binding_by_datapath, - sbrec_port_binding_by_name, 1, - peer->datapath, chassis, local_datapaths, - tracked_datapaths); - return; + peer_ld = add_local_datapath__(sbrec_datapath_binding_by_key, + sbrec_port_binding_by_datapath, + sbrec_port_binding_by_name, 1, + peer->datapath, chassis, + local_datapaths, + tracked_datapaths); } local_datapath_peer_port_add(peer_ld, peer, pb); @@ -613,6 +629,17 @@ add_local_datapath__(struct ovsdb_idl_index *sbrec_datapath_binding_by_key, tracked_datapaths); } + if (ld->has_only_dgp_peer_ports) { + /* If this flag is set, it means this 'switch' datapath has + * - one ore many localnet ports. + * - all the router ports it is connected to are + * distributed gateway ports (DGPs). + * There is no need to add the routers of the dgps to + * the local datapaths. + * */ + return ld; + } + if (depth >= 100) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); VLOG_WARN_RL(&rl, "datapaths nested too deep"); @@ -705,6 +732,13 @@ datapath_is_transit_switch(const struct sbrec_datapath_binding *ldp) return smap_get(&ldp->external_ids, "interconn-ts") != NULL; } +static bool +datapath_has_only_dgp_peer_ports(const struct sbrec_datapath_binding *ldp) +{ + return datapath_is_switch(ldp) && + smap_get_bool(&ldp->external_ids, "only_dgp_peer_ports", false); +} + bool lb_is_local(const struct sbrec_load_balancer *sbrec_lb, const struct hmap *local_datapaths) @@ -745,3 +779,41 @@ lb_is_local(const struct sbrec_load_balancer *sbrec_lb, return false; } + +static void +local_datapath_remove_and_destroy__(struct local_datapath *ld, + const struct sbrec_port_binding *ignore_pb, + struct hmap *local_datapaths, + struct hmap *tracked_datapaths) +{ + for (size_t i = 0; i < ld->n_peer_ports; i++) { + const struct sbrec_port_binding *remote = ld->peer_ports[i].remote; + const struct sbrec_port_binding *local = ld->peer_ports[i].local; + + if (local == ignore_pb) { + continue; + } + + struct local_datapath *peer_ld; + uint32_t remote_peer_ld_key; + + remote_peer_ld_key = ld->peer_ports[i].remote->datapath->tunnel_key; + peer_ld = get_local_datapath(local_datapaths, remote_peer_ld_key); + if (peer_ld && !peer_ld->has_only_dgp_peer_ports) { + local_datapath_remove_and_destroy__(peer_ld, remote, + local_datapaths, + tracked_datapaths); + } else if (peer_ld && peer_ld->has_only_dgp_peer_ports) { + remove_local_datapath_peer_port(ld->peer_ports[i].remote, + peer_ld, local_datapaths); + } + } + + hmap_remove(local_datapaths, &ld->hmap_node); + if (tracked_datapaths) { + tracked_datapath_add(ld->datapath, TRACKED_RESOURCE_REMOVED, + tracked_datapaths); + } + + local_datapath_destroy(ld); +} diff --git a/controller/local_data.h b/controller/local_data.h index 857d63a51d..ef01f4259e 100644 --- a/controller/local_data.h +++ b/controller/local_data.h @@ -46,6 +46,8 @@ struct local_datapath { const struct sbrec_datapath_binding *datapath; bool is_switch; bool is_transit_switch; + /* Valid only for 'is_switch' local datapath. */ + bool has_only_dgp_peer_ports; /* The localnet port in this datapath, if any (at most one is allowed). */ const struct sbrec_port_binding *localnet_port; @@ -91,6 +93,10 @@ struct local_datapath * add_local_datapath( void local_datapaths_destroy(struct hmap *local_datapaths); void local_datapath_destroy(struct local_datapath *ld); +void local_datapath_remove_and_destroy(struct local_datapath *, + struct hmap *local_datapaths, + struct hmap *tracked_datapaths); + void add_local_datapath_peer_port( const struct sbrec_port_binding *, const struct sbrec_chassis *, diff --git a/controller/lport.c b/controller/lport.c index 8bc230e896..f164803528 100644 --- a/controller/lport.c +++ b/controller/lport.c @@ -102,6 +102,18 @@ lport_get_l3gw_peer(const struct sbrec_port_binding *pb, return get_peer_lport(pb, sbrec_port_binding_by_name); } +const struct sbrec_port_binding * +lport_get_cr_port(struct ovsdb_idl_index *sbrec_port_binding_by_name, + const struct sbrec_port_binding *pb) +{ + const char *crp = smap_get(&pb->options, "chassis-redirect-port"); + if (crp) { + return lport_lookup_by_name(sbrec_port_binding_by_name, crp); + } + + return NULL; +} + enum can_bind lport_can_bind_on_this_chassis(const struct sbrec_chassis *chassis_rec, const struct sbrec_port_binding *pb) diff --git a/controller/lport.h b/controller/lport.h index 3a58ebe39e..069bd59096 100644 --- a/controller/lport.h +++ b/controller/lport.h @@ -73,4 +73,8 @@ const struct sbrec_port_binding *lport_get_l3gw_peer( bool lport_is_activated_by_activation_strategy(const struct sbrec_port_binding *pb, const struct sbrec_chassis *chassis); +const struct sbrec_port_binding *lport_get_cr_port( + struct ovsdb_idl_index *sbrec_port_binding_by_name, + const struct sbrec_port_binding *); + #endif /* controller/lport.h */ diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c index da942abaa9..dc8ceec076 100644 --- a/controller/ovn-controller.c +++ b/controller/ovn-controller.c @@ -101,6 +101,7 @@ static unixctl_cb_func debug_pause_execution; static unixctl_cb_func debug_resume_execution; static unixctl_cb_func debug_status_execution; static unixctl_cb_func debug_dump_local_bindings; +static unixctl_cb_func debug_dump_local_datapaths; static unixctl_cb_func debug_dump_related_lports; static unixctl_cb_func debug_dump_local_template_vars; static unixctl_cb_func debug_dump_local_mac_bindings; @@ -1688,6 +1689,22 @@ runtime_data_sb_datapath_binding_handler(struct engine_node *node OVS_UNUSED, return false; } } + + if (sbrec_datapath_binding_is_updated( + dp, SBREC_DATAPATH_BINDING_COL_EXTERNAL_IDS) && + !sbrec_datapath_binding_is_new(dp)) { + struct local_datapath *ld = + get_local_datapath(&rt_data->local_datapaths, + dp->tunnel_key); + if (ld && ld->is_switch) { + bool only_dgp_peer_ports = + smap_get_bool(&dp->external_ids, "only_dgp_peer_ports", + false); + if (ld->has_only_dgp_peer_ports != only_dgp_peer_ports) { + return false; + } + } + } } return true; @@ -4357,6 +4374,12 @@ lflow_output_runtime_data_handler(struct engine_node *node, init_lflow_ctx(node, fo, &l_ctx_in, &l_ctx_out); struct tracked_datapath *tdp; + HMAP_FOR_EACH (tdp, node, tracked_dp_bindings) { + if (tdp->tracked_type == TRACKED_RESOURCE_REMOVED) { + return false; + } + } + HMAP_FOR_EACH (tdp, node, tracked_dp_bindings) { if (tdp->tracked_type == TRACKED_RESOURCE_NEW) { if (!lflow_add_flows_for_datapath(tdp->dp, &l_ctx_in, @@ -5552,6 +5575,10 @@ main(int argc, char *argv[]) debug_dump_local_bindings, &runtime_data->lbinding_data); + unixctl_command_register("debug/dump-local-datapaths", "", 0, 0, + debug_dump_local_datapaths, + &runtime_data->local_datapaths); + unixctl_command_register("debug/dump-related-ports", "", 0, 0, debug_dump_related_lports, &runtime_data->related_lports); @@ -6517,6 +6544,17 @@ debug_dump_local_bindings(struct unixctl_conn *conn, int argc OVS_UNUSED, ds_destroy(&binding_data); } +static void +debug_dump_local_datapaths(struct unixctl_conn *conn, int argc OVS_UNUSED, + const char *argv[] OVS_UNUSED, + void *local_datapaths) +{ + struct ds local_dps_data = DS_EMPTY_INITIALIZER; + binding_dump_local_datapaths(local_datapaths, &local_dps_data); + unixctl_command_reply(conn, ds_cstr(&local_dps_data)); + ds_destroy(&local_dps_data); +} + static void debug_dump_related_lports(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *related_lports) diff --git a/tests/multinode.at b/tests/multinode.at index c1bd3123ac..b828f16135 100644 --- a/tests/multinode.at +++ b/tests/multinode.at @@ -2776,3 +2776,181 @@ for i in 1 2; do done AT_CLEANUP + +AT_SETUP([ovn multinode - only_dgp_peer_ports provider switch functionality]) + +# Check that ovn-fake-multinode setup is up and running +check_fake_multinode_setup + +# Delete the multinode NB and OVS resources before starting the test. +cleanup_multinode_resources + +check multinode_nbctl ls-add sw0 +check multinode_nbctl lsp-add sw0 sw0-port1 +check multinode_nbctl lsp-set-addresses sw0-port1 "50:54:00:00:00:03 10.0.0.3 1000::3" +check multinode_nbctl lsp-add sw0 sw0-port2 +check multinode_nbctl lsp-set-addresses sw0-port2 "50:54:00:00:00:04 10.0.0.4 1000::4" + +check multinode_nbctl lr-add lr0 +check multinode_nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 1000::1/64 +check multinode_nbctl lsp-add sw0 sw0-lr0 +check multinode_nbctl lsp-set-type sw0-lr0 router +check multinode_nbctl lsp-set-addresses sw0-lr0 router +check multinode_nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0 + +check multinode_nbctl ls-add public +check multinode_nbctl lrp-add lr0 lr0-public 00:00:20:20:12:13 172.16.1.100/24 2000::1/64 +check multinode_nbctl lsp-add public public-lr0 +check multinode_nbctl lsp-set-type public-lr0 router +check multinode_nbctl lsp-set-addresses public-lr0 router +check multinode_nbctl lsp-set-options public-lr0 router-port=lr0-public + +# localnet port +check multinode_nbctl lsp-add public ln-public +check multinode_nbctl lsp-set-type ln-public localnet +check multinode_nbctl lsp-set-addresses ln-public unknown +check multinode_nbctl lsp-set-options ln-public network_name=public + +check multinode_nbctl lrp-set-gateway-chassis lr0-public ovn-gw-1 20 +check multinode_nbctl lr-nat-add lr0 snat 172.16.1.100 10.0.0.0/24 +check multinode_nbctl lr-nat-add lr0 dnat_and_snat 172.16.1.110 10.0.0.3 sw0-port1 50:54:00:00:00:03 +check multinode_nbctl lr-nat-add lr0 snat 2000::1 1000::/64 +check multinode_nbctl lr-nat-add lr0 dnat_and_snat 2000::2 1000::3 sw0-port1 50:54:00:00:00:03 + +check multinode_nbctl --wait=hv sync + +check multinode_nbctl ls-add sw1 +check multinode_nbctl lsp-add sw1 sw1-port1 +check multinode_nbctl lsp-set-addresses sw1-port1 "40:54:00:00:00:03 20.0.0.3 2000::3" + +check multinode_nbctl lr-add lr1 +check multinode_nbctl lrp-add lr1 lr1-sw1 00:00:01:00:ef:01 20.0.0.1/24 2000::a/64 +check multinode_nbctl lsp-add sw1 sw1-lr1 +check multinode_nbctl lsp-set-type sw1-lr1 router +check multinode_nbctl lsp-set-addresses sw1-lr1 router +check multinode_nbctl lsp-set-options sw1-lr1 router-port=lr1-sw1 + +check multinode_nbctl lrp-add lr1 lr1-public 00:00:20:30:22:13 172.16.1.101/24 +check multinode_nbctl lsp-add public public-lr1 +check multinode_nbctl lsp-set-type public-lr1 router +check multinode_nbctl lsp-set-addresses public-lr1 router +check multinode_nbctl lsp-set-options public-lr1 router-port=lr1-public + +check multinode_nbctl lr-nat-add lr1 snat 172.16.1.101 20.0.0.0/24 +check multinode_nbctl lr-nat-add lr1 dnat_and_snat 172.16.1.120 20.0.0.3 +check multinode_nbctl lrp-set-gateway-chassis lr1-public ovn-gw-1 20 + +check multinode_nbctl --wait=hv sync + +m_as ovn-chassis-1 /data/create_fake_vm.sh sw0-port1 sw0p1 50:54:00:00:00:03 1342 10.0.0.3 24 10.0.0.1 1000::3/64 1000::a +m_as ovn-chassis-2 /data/create_fake_vm.sh sw1-port1 sw1p1 40:54:00:00:00:03 1342 20.0.0.3 24 20.0.0.1 2000::4/64 1000::a + +m_wait_for_ports_up sw0-port1 +m_wait_for_ports_up sw1-port1 + +m_as ovn-central-az1-1 ovn-sbctl show + +m_as ovn-chassis-1 ovn-appctl debug/dump-local-datapaths | sort +m_as ovn-chassis-2 ovn-appctl debug/dump-local-datapaths | sort + +AT_CHECK([m_as ovn-chassis-1 ovn-appctl debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +AT_CHECK([m_as ovn-chassis-2 ovn-appctl debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr1, type: router +Datapath: sw1, type: switch +Local datapaths: +]) + +AT_CHECK([m_as ovn-gw-1 ovn-appctl debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: lr1, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Datapath: sw1, type: switch +Local datapaths: +]) + +# ping lr0-public IP - 172.168.0.100 +M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.100 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +# ping lr1-public IP - 172.168.0.101 from sw0p1 +M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.101 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +# ping public ip of sw1-port1 - 172.16.1.120 from sw0p1 +M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.120 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +# ping public ip of sw0-port1 - 172.16.1.110 from sw1p1 +M_NS_CHECK_EXEC([ovn-chassis-2], [sw1p1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.110 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +# Bind sw0-port2 on chassis-2 +m_as ovn-chassis-2 /data/create_fake_vm.sh sw0-port2 sw0p2 50:54:00:00:00:04 1342 10.0.0.4 24 10.0.0.1 1000::4/64 1000::a +m_wait_for_ports_up sw0-port2 + +AT_CHECK([m_as ovn-chassis-2 ovn-appctl debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: lr1, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Datapath: sw1, type: switch +Local datapaths: +]) + +# ping public ip of sw0-port1 - 172.16.1.110 from sw0p2 +M_NS_CHECK_EXEC([ovn-chassis-2], [sw0p2], [ping -q -c 3 -i 0.3 -w 2 172.16.1.110 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +# ping public ip of sw1-port1 - 172.16.1.120 from sw0p2 +M_NS_CHECK_EXEC([ovn-chassis-2], [sw0p2], [ping -q -c 3 -i 0.3 -w 2 172.16.1.120 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +# Create a normal router port in public with its peer as a normal distributed router port. +check multinode_nbctl lsp-add public public-lr2 +check multinode_nbctl lsp-set-type public-lr2 router +check multinode_nbctl lsp-set-addresses public-lr2 router +check multinode_nbctl lsp-set-options public-lr2 router-port=lr2-public +check multinode_nbctl lr-add lr2 +check multinode_nbctl lrp-add lr2 lr2-public 00:00:41:00:1f:61 172.16.1.102/24 3000::a/64 + +check multinode_nbctl --wait=hv sync +AT_CHECK([m_as ovn-chassis-1 ovn-appctl debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: lr1, type: router +Datapath: lr2, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Datapath: sw1, type: switch +Local datapaths: +]) + +AT_CHECK([m_as ovn-chassis-2 ovn-appctl debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: lr1, type: router +Datapath: lr2, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Datapath: sw1, type: switch +Local datapaths: +]) + +AT_CLEANUP diff --git a/tests/ovn-performance.at b/tests/ovn-performance.at index 7d480c20c8..a003fc36bc 100644 --- a/tests/ovn-performance.at +++ b/tests/ovn-performance.at @@ -479,7 +479,7 @@ OVN_CONTROLLER_EXPECT_NO_HIT( ) OVN_CONTROLLER_EXPECT_HIT_COND( - [hv1 hv2 hv3 hv4 hv5], [lflow_run], [=0 =0 >0 =0 =0], + [hv1 hv2 hv3 hv4 hv5], [lflow_run], [>0 >0 >0 =0 =0], [ovn-nbctl --wait=hv lrp-set-gateway-chassis lr1-public hv3 30 && ovn-nbctl --wait=hv sync] ) @@ -552,8 +552,8 @@ hv5_ch=$(ovn-sbctl --bare --columns _uuid list chassis hv5) OVS_WAIT_UNTIL([ovn-sbctl find port_binding logical_port=cr-lr1-public chassis=$hv5_ch]) check ovn-nbctl --wait=hv sync # Delete hv5 from gateway chassis. There should be no lflow_run. -OVN_CONTROLLER_EXPECT_NO_HIT( - [hv1 hv2 hv3 hv4 hv5], [lflow_run], +OVN_CONTROLLER_EXPECT_HIT_COND( + [hv1 hv2 hv3 hv4 hv5], [lflow_run], [=0 =0 =0 =0 =0] [ovn-nbctl --wait=hv lrp-del-gateway-chassis lr1-public hv5] ) diff --git a/tests/ovn.at b/tests/ovn.at index d105ed2535..d4e87212a5 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -41481,6 +41481,859 @@ OVN_CLEANUP([hv1]) AT_CLEANUP ]) +OVN_FOR_EACH_NORTHD_NO_HV([ +AT_SETUP([ovn-controller -- only_dgp_peer_ports flag in SB datapath_binding]) +AT_KEYWORDS([multiple-l3dgw-ports]) +ovn_start +net_add n1 +sim_add hv1 +as hv1 +check ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.1 + +sim_add hv2 +as hv2 +check ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.2 + +sim_add gw1 +as gw1 +check ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.3 + +sim_add gw2 +as gw2 +check ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.4 + +check ovn-nbctl ls-add sw0 +check ovn-nbctl lsp-add sw0 sw0-port1 +check ovn-nbctl lsp-set-addresses sw0-port1 "50:54:00:00:00:01 10.0.0.3 1000::3" +check ovn-nbctl lsp-add sw0 sw0-port2 +check ovn-nbctl lsp-set-addresses sw0-port2 "50:54:00:00:00:02 10.0.0.4 1000::4" + +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 1000::1/64 +check ovn-nbctl lsp-add sw0 sw0-lr0 +check ovn-nbctl lsp-set-type sw0-lr0 router +check ovn-nbctl lsp-set-addresses sw0-lr0 router +check ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0 + +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 2000::1/64 +check ovn-nbctl lsp-add public public-lr0 +check ovn-nbctl lsp-set-type public-lr0 router +check ovn-nbctl lsp-set-addresses public-lr0 router +check ovn-nbctl lsp-set-options public-lr0 router-port=lr0-public + +# localnet port +check ovn-nbctl lsp-add public ln-public +check ovn-nbctl lsp-set-type ln-public localnet +check ovn-nbctl lsp-set-addresses ln-public unknown +check ovn-nbctl lsp-set-options ln-public network_name=phys + +check ovn-nbctl lrp-set-gateway-chassis lr0-public gw1 20 +check ovn-nbctl lr-nat-add lr0 snat 172.168.0.100 10.0.0.0/24 +check ovn-nbctl lr-nat-add lr0 dnat_and_snat 172.168.0.110 10.0.0.4 sw0-port2 f0:00:00:01:02:04 +check ovn-nbctl lr-nat-add lr0 snat 2000::1 1000::/64 +check ovn-nbctl lr-nat-add lr0 dnat_and_snat 2000::2 1000::4 sw0-port2 f0:00:00:01:02:04 + +check ovn-nbctl --wait=hv sync + +sw0_dp_key=$(printf "%x" $(fetch_column Datapath_Binding tunnel_key external_ids:name=sw0)) +lr0_dp_key=$(printf "%x" $(fetch_column Datapath_Binding tunnel_key external_ids:name=lr0)) +public_dp_key=$(printf "%x" $(fetch_column Datapath_Binding tunnel_key external_ids:name=public)) + +check_offlows_for_datapath() { + hv=$1 + dp_key=$2 + should_be_present=$3 + + if [[ "$should_be_present" == "yes" ]]; then + echo "Flows should be present for hv - $hv : datapath - $dp_key" + OVS_WAIT_UNTIL( + [test $(as $hv ovs-ofctl dump-flows br-int | grep -c metadata=0x$dp_key) -gt 0] + ) + else + echo "Flows should NOT be present for hv - $hv : datapath - $dp_key" + OVS_WAIT_UNTIL( + [test $(as $hv ovs-ofctl dump-flows br-int | grep -c metadata=0x$dp_key) -eq 0] + ) + fi +} + +AT_CHECK([ovn-sbctl get datapath_binding public external_ids:only_dgp_peer_ports], [0], [dnl +"true" +]) + +check_offlows_for_datapath hv1 $sw0_dp_key no +check_offlows_for_datapath hv1 $lr0_dp_key no +check_offlows_for_datapath hv1 $public_dp_key no + +check_offlows_for_datapath hv2 $sw0_dp_key no +check_offlows_for_datapath hv2 $lr0_dp_key no +check_offlows_for_datapath hv2 $public_dp_key no + +check_offlows_for_datapath gw1 $sw0_dp_key yes +check_offlows_for_datapath gw1 $lr0_dp_key yes +check_offlows_for_datapath gw1 $public_dp_key yes + +check_offlows_for_datapath gw2 $sw0_dp_key no +check_offlows_for_datapath gw2 $lr0_dp_key no +check_offlows_for_datapath gw2 $public_dp_key no + +AT_CHECK([as hv1 ovn-appctl -t ovn-controller debug/dump-local-datapaths], [0], [dnl +Local datapaths: +]) + +AT_CHECK([as hv2 ovn-appctl -t ovn-controller debug/dump-local-datapaths], [0], [dnl +Local datapaths: +]) + +AT_CHECK([as gw1 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +AT_CHECK([as gw2 ovn-appctl -t ovn-controller debug/dump-local-datapaths], [0], [dnl +Local datapaths: +]) + +# Create a VIF on hv1 for sw0-port1 +AS_BOX([create a VIF on hv1 for sw0-port1]) + +as hv1 +ovs-vsctl -- add-port br-int hv1-vif1 -- \ + set interface hv1-vif1 external-ids:iface-id=sw0-port1 \ + options:tx_pcap=hv1/vif1-tx.pcap \ + options:rxq_pcap=hv1/vif1-rx.pcap \ + ofport-request=1 + +wait_for_ports_up sw0-port1 + +AS_BOX([Create a VIF on hv1 for sw0-port1 - hv1 should have flows for sw0, lr0 and public]) + +check_offlows_for_datapath hv1 $sw0_dp_key yes +check_offlows_for_datapath hv1 $lr0_dp_key yes +check_offlows_for_datapath hv1 $public_dp_key yes + +AS_BOX([hv2 should NOT have flows for sw0, lr0 and public]) +check_offlows_for_datapath hv2 $sw0_dp_key no +check_offlows_for_datapath hv2 $lr0_dp_key no +check_offlows_for_datapath hv2 $public_dp_key no + +AT_CHECK([as hv1 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +AT_CHECK([as hv2 ovn-appctl -t ovn-controller debug/dump-local-datapaths], [0], [dnl +Local datapaths: +]) + +AT_CHECK([as gw1 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +AT_CHECK([as gw2 ovn-appctl -t ovn-controller debug/dump-local-datapaths], [0], [dnl +Local datapaths: +]) + +AS_BOX([create a switch sw1 and router lr1, attach both and attach lr1 to public]) + +check ovn-nbctl ls-add sw1 +check ovn-nbctl lsp-add sw1 sw1-port1 +check ovn-nbctl lsp-set-addresses sw1-port1 "60:54:00:00:00:01 20.0.0.3" + +check ovn-nbctl lr-add lr1 +check ovn-nbctl lrp-add lr1 lr1-sw1 00:00:01:00:ef:01 20.0.0.1/24 +check ovn-nbctl lsp-add sw1 sw1-lr1 +check ovn-nbctl lsp-set-type sw1-lr1 router +check ovn-nbctl lsp-set-addresses sw1-lr1 router +check ovn-nbctl lsp-set-options sw1-lr1 router-port=lr1-sw1 + +check ovn-nbctl lrp-add lr1 lr1-public 00:00:20:30:22:13 172.168.0.101/24 +check ovn-nbctl lsp-add public public-lr1 +check ovn-nbctl lsp-set-type public-lr1 router +check ovn-nbctl lsp-set-addresses public-lr1 router +check ovn-nbctl lsp-set-options public-lr1 router-port=lr1-public + +sw1_dp_key=$(printf "%x" $(fetch_column Datapath_Binding tunnel_key external_ids:name=sw1)) +lr1_dp_key=$(printf "%x" $(fetch_column Datapath_Binding tunnel_key external_ids:name=lr1)) + +check ovn-nbctl lr-nat-add lr1 snat 172.168.0.101 20.0.0.0/24 +check ovn-nbctl lr-nat-add lr1 dnat_and_snat 172.168.0.140 20.0.0.3 + +AS_BOX([create a switch sw2 and router lr2, attach both and attach lr2 to public]) + +check ovn-nbctl ls-add sw2 +check ovn-nbctl lsp-add sw2 sw2-port1 +check ovn-nbctl lsp-set-addresses sw2-port1 "70:54:00:00:00:01 30.0.0.3" + +check ovn-nbctl lr-add lr2 +check ovn-nbctl lrp-add lr2 lr2-sw2 00:00:02:00:ef:01 30.0.0.1/24 +check ovn-nbctl lsp-add sw2 sw2-lr2 +check ovn-nbctl lsp-set-type sw2-lr2 router +check ovn-nbctl lsp-set-addresses sw2-lr2 router +check ovn-nbctl lsp-set-options sw2-lr2 router-port=lr2-sw2 + +check ovn-nbctl lrp-add lr2 lr2-public 00:00:20:40:22:53 172.168.0.102/24 +check ovn-nbctl lsp-add public public-lr2 +check ovn-nbctl lsp-set-type public-lr2 router +check ovn-nbctl lsp-set-addresses public-lr2 router +check ovn-nbctl lsp-set-options public-lr2 router-port=lr2-public + +sw2_dp_key=$(printf "%x" $(fetch_column Datapath_Binding tunnel_key external_ids:name=sw2)) +lr2_dp_key=$(printf "%x" $(fetch_column Datapath_Binding tunnel_key external_ids:name=lr2)) + +check ovn-nbctl lr-nat-add lr2 snat 172.168.0.102 30.0.0.0/24 +check ovn-nbctl lr-nat-add lr2 dnat_and_snat 172.168.0.150 30.0.0.3 + +check ovn-nbctl --wait=hv sync + +# Since lr1-public is not a DGP, public is not a "only_dgp_peer_ports". +AT_CHECK([ovn-sbctl get datapath_binding public external_ids:only_dgp_peer_ports], [1], [ignore], [ignore]) + +check_offlows_for_datapath hv1 $sw1_dp_key yes +check_offlows_for_datapath hv1 $lr1_dp_key yes +check_offlows_for_datapath hv1 $sw2_dp_key yes +check_offlows_for_datapath hv1 $lr2_dp_key yes + +check_offlows_for_datapath hv2 $sw1_dp_key no +check_offlows_for_datapath hv2 $lr1_dp_key no +check_offlows_for_datapath hv2 $sw2_dp_key no +check_offlows_for_datapath hv2 $lr2_dp_key no + +check_offlows_for_datapath gw1 $sw1_dp_key yes +check_offlows_for_datapath gw1 $lr1_dp_key yes +check_offlows_for_datapath gw1 $sw2_dp_key yes +check_offlows_for_datapath gw1 $lr2_dp_key yes + +check_offlows_for_datapath gw2 $sw1_dp_key no +check_offlows_for_datapath gw2 $lr1_dp_key no +check_offlows_for_datapath gw2 $sw2_dp_key no +check_offlows_for_datapath gw2 $lr2_dp_key no + +AT_CHECK([as hv1 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: lr1, type: router +Datapath: lr2, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Datapath: sw1, type: switch +Datapath: sw2, type: switch +Local datapaths: +]) + +AT_CHECK([as hv2 ovn-appctl -t ovn-controller debug/dump-local-datapaths], [0], [dnl +Local datapaths: +]) + +AT_CHECK([as gw1 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: lr1, type: router +Datapath: lr2, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Datapath: sw1, type: switch +Datapath: sw2, type: switch +Local datapaths: +]) + +AT_CHECK([as gw2 ovn-appctl -t ovn-controller debug/dump-local-datapaths], [0], [dnl +Local datapaths: +]) + +AS_BOX([Set gw2 as gateway chassis for lr1-public and lr2-public]) +check ovn-nbctl --wait=hv lrp-set-gateway-chassis lr1-public gw2 20 +check ovn-nbctl --wait=hv lrp-set-gateway-chassis lr2-public gw2 30 +wait_row_count Port_Binding 1 logical_port=cr-lr1-public +wait_row_count Port_Binding 1 logical_port=cr-lr2-public + +AT_CHECK([ovn-sbctl get datapath_binding public external_ids:only_dgp_peer_ports], [0], [dnl +"true" +]) + +check ovn-nbctl --wait=hv sync + +check_offlows_for_datapath hv1 $sw0_dp_key yes +check_offlows_for_datapath hv1 $lr0_dp_key yes +check_offlows_for_datapath hv1 $public_dp_key yes +check_offlows_for_datapath hv1 $sw1_dp_key no +check_offlows_for_datapath hv1 $lr1_dp_key no +check_offlows_for_datapath hv1 $sw2_dp_key no +check_offlows_for_datapath hv1 $lr2_dp_key no + +check_offlows_for_datapath hv2 $sw0_dp_key no +check_offlows_for_datapath hv2 $lr0_dp_key no +check_offlows_for_datapath hv2 $public_dp_key no +check_offlows_for_datapath hv2 $sw1_dp_key no +check_offlows_for_datapath hv2 $lr1_dp_key no +check_offlows_for_datapath hv2 $sw2_dp_key no +check_offlows_for_datapath hv2 $lr2_dp_key no + +check_offlows_for_datapath gw1 $sw0_dp_key yes +check_offlows_for_datapath gw1 $lr0_dp_key yes +check_offlows_for_datapath gw1 $public_dp_key yes +check_offlows_for_datapath gw1 $sw1_dp_key no +check_offlows_for_datapath gw1 $lr1_dp_key no +check_offlows_for_datapath gw1 $sw2_dp_key no +check_offlows_for_datapath gw1 $lr2_dp_key no + +check_offlows_for_datapath gw2 $sw0_dp_key no +check_offlows_for_datapath gw2 $lr0_dp_key no +check_offlows_for_datapath hv1 $public_dp_key yes +check_offlows_for_datapath gw2 $sw1_dp_key yes +check_offlows_for_datapath gw2 $lr1_dp_key yes +check_offlows_for_datapath gw2 $sw2_dp_key yes +check_offlows_for_datapath gw2 $lr2_dp_key yes + +AT_CHECK([as hv1 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +AT_CHECK([as hv2 ovn-appctl -t ovn-controller debug/dump-local-datapaths], [0], [dnl +Local datapaths: +]) + +AT_CHECK([as gw1 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +AT_CHECK([as gw2 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr1, type: router +Datapath: lr2, type: router +Datapath: public, type: switch +Datapath: sw1, type: switch +Datapath: sw2, type: switch +Local datapaths: +]) + +AS_BOX([Create a VIF on hv2 for sw1-port1]) + +as hv2 +ovs-vsctl -- add-port br-int hv2-vif1 -- \ + set interface hv2-vif1 external-ids:iface-id=sw1-port1 \ + options:tx_pcap=hv2/vif1-tx.pcap \ + options:rxq_pcap=hv2/vif1-rx.pcap \ + ofport-request=1 + +wait_for_ports_up sw1-port1 + +check_offlows_for_datapath hv1 $sw0_dp_key yes +check_offlows_for_datapath hv1 $lr0_dp_key yes +check_offlows_for_datapath hv1 $public_dp_key yes +check_offlows_for_datapath hv1 $sw1_dp_key no +check_offlows_for_datapath hv1 $lr1_dp_key no +check_offlows_for_datapath hv1 $sw2_dp_key no +check_offlows_for_datapath hv1 $lr2_dp_key no + +check_offlows_for_datapath hv2 $sw0_dp_key no +check_offlows_for_datapath hv2 $lr0_dp_key no + +# Since there are no distributed dnat_and_snat entries +# in lr1, hv2 will not have "public" in its +# local datapaths. +check_offlows_for_datapath hv2 $public_dp_key no +check_offlows_for_datapath hv2 $sw1_dp_key yes +check_offlows_for_datapath hv2 $lr1_dp_key yes +check_offlows_for_datapath hv2 $sw2_dp_key no +check_offlows_for_datapath hv2 $lr2_dp_key no + +check_offlows_for_datapath gw1 $sw0_dp_key yes +check_offlows_for_datapath gw1 $lr0_dp_key yes +check_offlows_for_datapath gw1 $public_dp_key yes +check_offlows_for_datapath gw1 $sw1_dp_key no +check_offlows_for_datapath gw1 $lr1_dp_key no +check_offlows_for_datapath gw1 $sw2_dp_key no +check_offlows_for_datapath gw1 $lr2_dp_key no + +# gw2 should have sw1, lr1, sw2 and lr2 and public in its local datapaths. +check_offlows_for_datapath gw2 $sw0_dp_key no +check_offlows_for_datapath gw2 $lr0_dp_key no +check_offlows_for_datapath gw2 $public_dp_key yes +check_offlows_for_datapath gw2 $sw1_dp_key yes +check_offlows_for_datapath gw2 $lr1_dp_key yes +check_offlows_for_datapath gw2 $sw2_dp_key yes +check_offlows_for_datapath gw2 $lr2_dp_key yes + +AT_CHECK([as hv1 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +AT_CHECK([as hv2 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr1, type: router +Datapath: sw1, type: switch +Local datapaths: +]) + +AT_CHECK([as gw1 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +AT_CHECK([as gw2 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr1, type: router +Datapath: lr2, type: router +Datapath: public, type: switch +Datapath: sw1, type: switch +Datapath: sw2, type: switch +Local datapaths: +]) + +# Add distributed dnat_and_snat in lr1. hv2 should have +# public in its local datapaths. +AS_BOX([ Add distributed dnat_and_snat in lr1]) + +check ovn-nbctl lr-nat-del lr1 dnat_and_snat +check ovn-nbctl --wait=hv lr-nat-add lr1 dnat_and_snat 172.168.0.140 20.0.0.3 sw1-port1 10:00:00:01:02:14 + +check_offlows_for_datapath hv2 $sw0_dp_key no +check_offlows_for_datapath hv2 $lr0_dp_key no +check_offlows_for_datapath hv2 $public_dp_key yes +check_offlows_for_datapath hv2 $sw1_dp_key yes +check_offlows_for_datapath hv2 $lr1_dp_key yes +check_offlows_for_datapath hv2 $sw2_dp_key no +check_offlows_for_datapath hv2 $lr2_dp_key no + +AT_CHECK([as hv1 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +AT_CHECK([as hv2 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr1, type: router +Datapath: public, type: switch +Datapath: sw1, type: switch +Local datapaths: +]) + +AT_CHECK([as gw1 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +AT_CHECK([as gw2 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr1, type: router +Datapath: lr2, type: router +Datapath: public, type: switch +Datapath: sw1, type: switch +Datapath: sw2, type: switch +Local datapaths: +]) + +AS_BOX([Create a VIF on hv2 for sw0-port2]) + +as hv2 +ovs-vsctl -- add-port br-int hv2-vif2 -- \ + set interface hv2-vif2 external-ids:iface-id=sw0-port2 \ + options:tx_pcap=hv2/vif2-tx.pcap \ + options:rxq_pcap=hv2/vif2-rx.pcap \ + ofport-request=2 + +wait_for_ports_up sw0-port2 + +check_offlows_for_datapath hv2 $sw0_dp_key yes +check_offlows_for_datapath hv2 $lr0_dp_key yes +check_offlows_for_datapath hv2 $public_dp_key yes +check_offlows_for_datapath hv2 $sw1_dp_key yes +check_offlows_for_datapath hv2 $lr1_dp_key yes +check_offlows_for_datapath hv2 $sw2_dp_key no +check_offlows_for_datapath hv2 $lr2_dp_key no + +AT_CHECK([as hv1 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +AT_CHECK([as hv2 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: lr1, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Datapath: sw1, type: switch +Local datapaths: +]) + +AT_CHECK([as gw1 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +AT_CHECK([as gw2 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr1, type: router +Datapath: lr2, type: router +Datapath: public, type: switch +Datapath: sw1, type: switch +Datapath: sw2, type: switch +Local datapaths: +]) + +AS_BOX([Delete the VIF for sw1-port1 in hv2]) + +as hv2 ovs-vsctl del-port hv2-vif1 +check ovn-nbctl --wait=hv sync +check_column "false" Port_Binding up logical_port=sw1-port1 + +check_offlows_for_datapath hv2 $sw0_dp_key yes +check_offlows_for_datapath hv2 $lr0_dp_key yes +check_offlows_for_datapath hv2 $public_dp_key yes +check_offlows_for_datapath hv2 $sw1_dp_key no +check_offlows_for_datapath hv2 $lr1_dp_key no +check_offlows_for_datapath hv2 $sw2_dp_key no +check_offlows_for_datapath hv2 $lr2_dp_key no + +AT_CHECK([as hv1 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +AT_CHECK([as hv2 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +AT_CHECK([as gw1 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +AT_CHECK([as gw2 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr1, type: router +Datapath: lr2, type: router +Datapath: public, type: switch +Datapath: sw1, type: switch +Datapath: sw2, type: switch +Local datapaths: +]) + +AS_BOX([Delete the VIF for sw0-port2 in hv2]) + +# Presently when a port binding is released we are not +# deleting its datapath from the local_datapaths if it +# is not relevant anymore. + +as hv2 ovs-vsctl del-port hv2-vif2 +check ovn-nbctl --wait=hv sync +check_column "false" Port_Binding up logical_port=sw0-port2 + +AT_CHECK([as hv1 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +# hv2 would still have public, sw0 and lr0 in its local datapaths. +# Next recompute should delete these datapaths. +AT_CHECK([as hv2 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +AT_CHECK([as gw1 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +AT_CHECK([as gw2 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr1, type: router +Datapath: lr2, type: router +Datapath: public, type: switch +Datapath: sw1, type: switch +Datapath: sw2, type: switch +Local datapaths: +]) + +# Trigger a recompute +AS_BOX([Trigger a recompute in hv2]) +check as hv2 ovn-appctl inc-engine/recompute + +check_offlows_for_datapath hv2 $sw0_dp_key no +check_offlows_for_datapath hv2 $lr0_dp_key no +check_offlows_for_datapath hv2 $public_dp_key no +check_offlows_for_datapath hv2 $sw1_dp_key no +check_offlows_for_datapath hv2 $lr1_dp_key no +check_offlows_for_datapath hv2 $sw2_dp_key no +check_offlows_for_datapath hv2 $lr2_dp_key no + +AT_CHECK([as hv1 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +AT_CHECK([as hv2 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Local datapaths: +]) + +AT_CHECK([as gw1 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +AT_CHECK([as gw2 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr1, type: router +Datapath: lr2, type: router +Datapath: public, type: switch +Datapath: sw1, type: switch +Datapath: sw2, type: switch +Local datapaths: +]) + +AS_BOX([Disconnect sw2 from lr2]) + +check ovn-nbctl --wait=hv lsp-set-options sw2-lr2 router-port=lr2-sw2xxx +check_offlows_for_datapath hv1 $sw0_dp_key yes +check_offlows_for_datapath hv1 $lr0_dp_key yes +check_offlows_for_datapath hv1 $public_dp_key yes +check_offlows_for_datapath hv1 $sw1_dp_key no +check_offlows_for_datapath hv1 $lr1_dp_key no +check_offlows_for_datapath hv1 $sw2_dp_key no +check_offlows_for_datapath hv1 $lr2_dp_key no + +AT_CHECK([as hv1 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +AT_CHECK([as hv2 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Local datapaths: +]) + +AT_CHECK([as gw1 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +AT_CHECK([as gw2 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr1, type: router +Datapath: lr2, type: router +Datapath: public, type: switch +Datapath: sw1, type: switch +Local datapaths: +]) + +AS_BOX([Reconnect sw2 to lr2 again]) + +check ovn-nbctl --wait=hv lsp-set-options sw2-lr2 router-port=lr2-sw2 +check_offlows_for_datapath hv1 $sw0_dp_key yes +check_offlows_for_datapath hv1 $lr0_dp_key yes +check_offlows_for_datapath hv1 $public_dp_key yes +check_offlows_for_datapath hv1 $sw1_dp_key no +check_offlows_for_datapath hv1 $lr1_dp_key no +check_offlows_for_datapath hv1 $sw2_dp_key no +check_offlows_for_datapath hv1 $lr2_dp_key no + +AT_CHECK([as hv1 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +AT_CHECK([as hv2 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Local datapaths: +]) + +AT_CHECK([as gw1 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +AT_CHECK([as gw2 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr1, type: router +Datapath: lr2, type: router +Datapath: public, type: switch +Datapath: sw1, type: switch +Datapath: sw2, type: switch +Local datapaths: +]) + +AS_BOX([Create a VIF on gw2 for sw1-port1]) + +as gw2 +ovs-vsctl -- add-port br-int gw2-vif2 -- \ + set interface gw2-vif2 external-ids:iface-id=sw1-port1 \ + options:tx_pcap=gw2/vif2-tx.pcap \ + options:rxq_pcap=gw2/vif2-rx.pcap \ + ofport-request=2 + +wait_for_ports_up sw1-port1 + +check_offlows_for_datapath gw2 $sw0_dp_key no +check_offlows_for_datapath gw2 $lr0_dp_key no +check_offlows_for_datapath gw2 $public_dp_key yes +check_offlows_for_datapath gw2 $sw1_dp_key yes +check_offlows_for_datapath gw2 $lr1_dp_key yes +check_offlows_for_datapath gw2 $sw2_dp_key yes +check_offlows_for_datapath gw2 $lr2_dp_key yes + +AT_CHECK([as hv1 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +AT_CHECK([as hv2 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Local datapaths: +]) + +AT_CHECK([as gw1 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +AT_CHECK([as gw2 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr1, type: router +Datapath: lr2, type: router +Datapath: public, type: switch +Datapath: sw1, type: switch +Datapath: sw2, type: switch +Local datapaths: +]) + +AS_BOX([Delete the VIF for sw1-port1 in gw2]) + +as gw2 ovs-vsctl del-port gw2-vif2 +check ovn-nbctl --wait=hv sync +check_column "false" Port_Binding up logical_port=sw1-port1 + +AT_CHECK([as hv1 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +# hv2 would still have public in its local datapaths. Next recompute should +# delete this datapath from the local datapaths. +AT_CHECK([as hv2 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Local datapaths: +]) + +AT_CHECK([as gw1 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +AT_CHECK([as gw2 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr1, type: router +Datapath: lr2, type: router +Datapath: public, type: switch +Datapath: sw1, type: switch +Datapath: sw2, type: switch +Local datapaths: +]) + +AS_BOX([Create a logical port for public and bind it on hv2]) +# hv2 will only have public in its local datapaths. +check ovn-nbctl lsp-add public public-p1 + +as hv2 +ovs-vsctl -- add-port br-int hv2-vif3 -- \ + set interface hv2-vif3 external-ids:iface-id=public-p1 \ + options:tx_pcap=hv2/vif3-tx.pcap \ + options:rxq_pcap=hv2/vif3-rx.pcap \ + ofport-request=2 + +wait_for_ports_up public-p1 + +# as hv2 ovn-appctl -t ovn-controller inc-engine/recompute +# check ovn-nbctl --wait=hv sync + +as hv2 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort + +check_offlows_for_datapath hv2 $sw0_dp_key no +check_offlows_for_datapath hv2 $lr0_dp_key no +check_offlows_for_datapath hv2 $public_dp_key yes +check_offlows_for_datapath hv2 $sw1_dp_key no +check_offlows_for_datapath hv2 $lr1_dp_key no +check_offlows_for_datapath hv2 $sw2_dp_key no +check_offlows_for_datapath hv2 $lr2_dp_key no + +AT_CHECK([as hv1 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +AT_CHECK([as hv2 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: public, type: switch +Local datapaths: +]) + +AT_CHECK([as gw1 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr0, type: router +Datapath: public, type: switch +Datapath: sw0, type: switch +Local datapaths: +]) + +AT_CHECK([as gw2 ovn-appctl -t ovn-controller debug/dump-local-datapaths | sort], [0], [dnl +Datapath: lr1, type: router +Datapath: lr2, type: router +Datapath: public, type: switch +Datapath: sw1, type: switch +Datapath: sw2, type: switch +Local datapaths: +]) + +OVN_CLEANUP([hv1], [hv2], [gw1], [gw2]) +AT_CLEANUP +]) + OVN_FOR_EACH_NORTHD([ AT_SETUP([requested-tnl-key-recompute]) AT_KEYWORDS([requested-tnl-key-recompute])