Please see my comments below. On Wed, May 20, 2020 at 12:41 PM <num...@ovn.org> wrote: > > From: Numan Siddique <num...@ovn.org> > > In order to handle runtime data changes incrementally, the flow outut > runtime data handle should know the changed runtime data. > Runtime data now tracks the changed data for any OVS interface > and SB port binding changes. The tracked data contains a hmap > of tracked datapaths (which changed during runtime data processing. > > The flow outout runtime_data handler in this patch doesn't do much > with the tracked data. It returns false if there is tracked data available > so that flow_output run is called. If no tracked data is available > then there is no need for flow computation and the handler returns true. > > Next patch in the series processes the tracked data incrementally. > > Acked-by: Mark Michelson <mmich...@redhat.com> > Co-Authored-by: Venkata Anil <anilvenk...@redhat.com> > Signed-off-by: Venkata Anil <anilvenk...@redhat.com> > Signed-off-by: Numan Siddique <num...@ovn.org> > --- > controller/binding.c | 159 ++++++++++++++++++++++++++++++++---- > controller/binding.h | 21 +++++ > controller/ovn-controller.c | 62 +++++++++++++- > tests/ovn-performance.at | 28 +++---- > 4 files changed, 240 insertions(+), 30 deletions(-) > > diff --git a/controller/binding.c b/controller/binding.c > index f00c975ce..9b3d46b23 100644 > --- a/controller/binding.c > +++ b/controller/binding.c > @@ -69,13 +69,20 @@ binding_register_ovs_idl(struct ovsdb_idl *ovs_idl) > ovsdb_idl_add_column(ovs_idl, &ovsrec_qos_col_type); > } > > +static struct tracked_binding_datapath *tracked_binding_datapath_create( > + const struct sbrec_datapath_binding *, > + bool is_new, struct hmap *tracked_dps); > +static struct tracked_binding_datapath *tracked_binding_datapath_find( > + struct hmap *, const struct sbrec_datapath_binding *); > + > static void > add_local_datapath__(struct ovsdb_idl_index *sbrec_datapath_binding_by_key, > struct ovsdb_idl_index *sbrec_port_binding_by_datapath, > struct ovsdb_idl_index *sbrec_port_binding_by_name, > const struct sbrec_datapath_binding *datapath, > bool has_local_l3gateway, int depth, > - struct hmap *local_datapaths) > + struct hmap *local_datapaths, > + struct hmap *updated_dp_bindings) > { > uint32_t dp_key = datapath->tunnel_key; > struct local_datapath *ld = get_local_datapath(local_datapaths, dp_key); > @@ -92,6 +99,11 @@ add_local_datapath__(struct ovsdb_idl_index *sbrec_datapath_binding_by_key, > ld->localnet_port = NULL; > ld->has_local_l3gateway = has_local_l3gateway; > > + if (updated_dp_bindings && > + !tracked_binding_datapath_find(updated_dp_bindings, datapath)) { > + tracked_binding_datapath_create(datapath, true, updated_dp_bindings); > + } > + > if (depth >= 100) { > static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); > VLOG_WARN_RL(&rl, "datapaths nested too deep"); > @@ -124,7 +136,8 @@ add_local_datapath__(struct ovsdb_idl_index *sbrec_datapath_binding_by_key, > sbrec_port_binding_by_datapath, > sbrec_port_binding_by_name, > peer->datapath, false, > - depth + 1, local_datapaths); > + depth + 1, local_datapaths, > + updated_dp_bindings); > } > ld->n_peer_ports++; > if (ld->n_peer_ports > ld->n_allocated_peer_ports) { > @@ -147,12 +160,14 @@ add_local_datapath(struct ovsdb_idl_index *sbrec_datapath_binding_by_key, > struct ovsdb_idl_index *sbrec_port_binding_by_datapath, > struct ovsdb_idl_index *sbrec_port_binding_by_name, > const struct sbrec_datapath_binding *datapath, > - bool has_local_l3gateway, struct hmap *local_datapaths) > + bool has_local_l3gateway, struct hmap *local_datapaths, > + struct hmap *updated_dp_bindings) > { > add_local_datapath__(sbrec_datapath_binding_by_key, > sbrec_port_binding_by_datapath, > sbrec_port_binding_by_name, > - datapath, has_local_l3gateway, 0, local_datapaths); > + datapath, has_local_l3gateway, 0, local_datapaths, > + updated_dp_bindings); > } > > static void > @@ -623,6 +638,71 @@ is_lport_container(const struct sbrec_port_binding *pb) > return !pb->type[0] && pb->parent_port && pb->parent_port[0]; > } > > +static struct tracked_binding_datapath * > +tracked_binding_datapath_create(const struct sbrec_datapath_binding *dp, > + bool is_new, > + struct hmap *tracked_datapaths) > +{ > + struct tracked_binding_datapath *t_dp = xzalloc(sizeof *t_dp); > + t_dp->dp = dp; > + t_dp->is_new = is_new; > + ovs_list_init(&t_dp->lports_head); > + hmap_insert(tracked_datapaths, &t_dp->node, uuid_hash(&dp->header_.uuid)); > + return t_dp; > +} > + > +static struct tracked_binding_datapath * > +tracked_binding_datapath_find(struct hmap *tracked_datapaths, > + const struct sbrec_datapath_binding *dp) > +{ > + struct tracked_binding_datapath *t_dp; > + size_t hash = uuid_hash(&dp->header_.uuid); > + HMAP_FOR_EACH_WITH_HASH (t_dp, node, hash, tracked_datapaths) { > + if (uuid_equals(&t_dp->dp->header_.uuid, &dp->header_.uuid)) { > + return t_dp; > + } > + } > + > + return NULL; > +} > + > +static void > +tracked_binding_datapath_lport_add(const struct sbrec_port_binding *pb, > + bool deleted, > + struct hmap *tracked_datapaths) > +{ > + if (!tracked_datapaths) { > + return; > + } > + > + struct tracked_binding_datapath *tracked_dp = > + tracked_binding_datapath_find(tracked_datapaths, pb->datapath); > + if (!tracked_dp) { > + tracked_dp = tracked_binding_datapath_create(pb->datapath, false, > + tracked_datapaths); > + } > + struct tracked_binding_lport *lport = xmalloc(sizeof *lport); > + lport->pb = pb; > + lport->deleted = deleted; > + ovs_list_push_back(&tracked_dp->lports_head, &lport->list_node); > +} > + > +void > +binding_tracked_dp_destroy(struct hmap *tracked_datapaths) > +{ > + struct tracked_binding_datapath *t_dp; > + HMAP_FOR_EACH_POP (t_dp, node, tracked_datapaths) { > + struct tracked_binding_lport *lport, *next; > + LIST_FOR_EACH_SAFE (lport, next, list_node, &t_dp->lports_head) { > + ovs_list_remove(&lport->list_node); > + free(lport); > + } > + free(t_dp); > + } > + > + hmap_destroy(tracked_datapaths); > +} > + > /* Corresponds to each Port_Binding.type. */ > enum en_lport_type { > LP_UNKNOWN, > @@ -770,7 +850,8 @@ can_bind_on_this_chassis(const struct sbrec_chassis *chassis_rec, > static bool > release_local_binding_children(const struct sbrec_chassis *chassis_rec, > struct local_binding *lbinding, > - bool sb_readonly) > + bool sb_readonly, > + struct hmap *tracked_dp_bindings) > { > struct shash_node *node; > SHASH_FOR_EACH (node, &lbinding->children) { > @@ -780,6 +861,10 @@ release_local_binding_children(const struct sbrec_chassis *chassis_rec, > return false; > } > } > + if (tracked_dp_bindings) { > + tracked_binding_datapath_lport_add(l->pb, true, > + tracked_dp_bindings); > + } > } > > return true; > @@ -787,10 +872,11 @@ release_local_binding_children(const struct sbrec_chassis *chassis_rec, > > static bool > release_local_binding(const struct sbrec_chassis *chassis_rec, > - struct local_binding *lbinding, bool sb_readonly) > + struct local_binding *lbinding, bool sb_readonly, > + struct hmap *tracked_dp_bindings) > { > if (!release_local_binding_children(chassis_rec, lbinding, > - sb_readonly)) { > + sb_readonly, tracked_dp_bindings)) { > return false; > } > > @@ -798,6 +884,10 @@ release_local_binding(const struct sbrec_chassis *chassis_rec, > return release_lport(lbinding->pb, sb_readonly); > } > > + if (tracked_dp_bindings) { > + tracked_binding_datapath_lport_add(lbinding->pb, true, > + tracked_dp_bindings); > + } > return true; > } > > @@ -821,7 +911,8 @@ consider_vif_lport_(const struct sbrec_port_binding *pb, > add_local_datapath(b_ctx_in->sbrec_datapath_binding_by_key, > b_ctx_in->sbrec_port_binding_by_datapath, > b_ctx_in->sbrec_port_binding_by_name, > - pb->datapath, false, b_ctx_out->local_datapaths); > + pb->datapath, false, b_ctx_out->local_datapaths, > + b_ctx_out->tracked_dp_bindings); > update_local_lport_ids(b_ctx_out->local_lport_ids, pb); > if (lbinding->iface && qos_map && b_ctx_in->ovs_idl_txn) { > get_qos_params(pb, qos_map); > @@ -1003,7 +1094,8 @@ consider_nonvif_lport_(const struct sbrec_port_binding *pb, > b_ctx_in->sbrec_port_binding_by_datapath, > b_ctx_in->sbrec_port_binding_by_name, > pb->datapath, has_local_l3gateway, > - b_ctx_out->local_datapaths); > + b_ctx_out->local_datapaths, > + b_ctx_out->tracked_dp_bindings); > > update_local_lport_ids(b_ctx_out->local_lport_ids, pb); > return claim_lport(pb, b_ctx_in->chassis_rec, NULL, > @@ -1080,7 +1172,8 @@ consider_ha_lport(const struct sbrec_port_binding *pb, > b_ctx_in->sbrec_port_binding_by_datapath, > b_ctx_in->sbrec_port_binding_by_name, > pb->datapath, false, > - b_ctx_out->local_datapaths); > + b_ctx_out->local_datapaths, > + b_ctx_out->tracked_dp_bindings); > } > > return consider_nonvif_lport_(pb, our_chassis, false, b_ctx_in, b_ctx_out); > @@ -1367,7 +1460,8 @@ add_local_datapath_peer_port(const struct sbrec_port_binding *pb, > b_ctx_in->sbrec_port_binding_by_datapath, > b_ctx_in->sbrec_port_binding_by_name, > peer->datapath, false, > - 1, b_ctx_out->local_datapaths); > + 1, b_ctx_out->local_datapaths, > + b_ctx_out->tracked_dp_bindings); > return; > } > > @@ -1444,6 +1538,18 @@ remove_pb_from_local_datapath(const struct sbrec_port_binding *pb, > } > } > > +static void > +update_lport_tracking(const struct sbrec_port_binding *pb, > + bool old_claim, bool new_claim, > + struct hmap *tracked_dp_bindings) > +{ > + if (!tracked_dp_bindings || !pb || (old_claim == new_claim)) { > + return; > + } > + > + tracked_binding_datapath_lport_add(pb, old_claim, tracked_dp_bindings); > +} > + > static bool > handle_iface_add(const struct ovsrec_interface *iface_rec, > const char *iface_id, > @@ -1472,6 +1578,7 @@ handle_iface_add(const struct ovsrec_interface *iface_rec, > } > } > > + bool claimed = is_lbinding_this_chassis(lbinding, b_ctx_in->chassis_rec); > if (lbinding->pb) { > if (!consider_vif_lport(lbinding->pb, b_ctx_in, b_ctx_out, > lbinding, qos_map)) { > @@ -1479,6 +1586,10 @@ handle_iface_add(const struct ovsrec_interface *iface_rec, > } > } > > + bool now_claimed = > + is_lbinding_this_chassis(lbinding, b_ctx_in->chassis_rec); > + update_lport_tracking(lbinding->pb, claimed, now_claimed, > + b_ctx_out->tracked_dp_bindings); > /* Update the child local_binding's iface (if any children) and try to > * claim the container lbindings. */ > struct shash_node *node; > @@ -1486,10 +1597,15 @@ handle_iface_add(const struct ovsrec_interface *iface_rec, > struct local_binding *child = node->data; > child->iface = iface_rec; > if (child->type == BT_CONTAINER) { > + claimed = is_lbinding_this_chassis(child, b_ctx_in->chassis_rec); > if (!consider_container_lport(child->pb, b_ctx_in, b_ctx_out, > qos_map)) { > return false; > } > + now_claimed = > + is_lbinding_this_chassis(child, b_ctx_in->chassis_rec); > + update_lport_tracking(child->pb, claimed, now_claimed, > + b_ctx_out->tracked_dp_bindings); > } > } > > @@ -1508,7 +1624,8 @@ handle_iface_delete(const char *lport_name, const char *iface_name, > lbinding->pb->chassis == b_ctx_in->chassis_rec) { > > if (!release_local_binding(b_ctx_in->chassis_rec, lbinding, > - !b_ctx_in->ovnsb_idl_txn)) { > + !b_ctx_in->ovnsb_idl_txn, > + b_ctx_out->tracked_dp_bindings)) { > return false; > } > > @@ -1556,6 +1673,8 @@ binding_handle_ovs_interface_changes(struct binding_ctx_in *b_ctx_in, > bool handled = true; > *changed = false; > > + *b_ctx_out->local_lports_changed = false; > + > /* Run the tracked interfaces loop twice. One to handle deleted > * changes. And another to handle add/update changes. > * This will ensure correctness. > @@ -1698,9 +1817,10 @@ handle_deleted_vif_lport(const struct sbrec_port_binding *pb, > * clear the 'chassis' column of 'pb'. But we need to do > * for the local_binding's children. */ > if (lbinding->type == BT_VIF && > - !release_local_binding_children(b_ctx_in->chassis_rec, > - lbinding, > - !b_ctx_in->ovnsb_idl_txn)) { > + !release_local_binding_children( > + b_ctx_in->chassis_rec, lbinding, > + !b_ctx_in->ovnsb_idl_txn, > + b_ctx_out->tracked_dp_bindings)) { > return false; > } > > @@ -1746,6 +1866,9 @@ handle_updated_vif_lport(const struct sbrec_port_binding *pb, > return true; > } > > + update_lport_tracking(pb, claimed, now_claimed, > + b_ctx_out->tracked_dp_bindings); > + > struct local_binding *lbinding = > local_binding_find(b_ctx_out->local_bindings, pb->logical_port); > > @@ -1755,11 +1878,17 @@ handle_updated_vif_lport(const struct sbrec_port_binding *pb, > SHASH_FOR_EACH (node, &lbinding->children) { > struct local_binding *child = node->data; > if (child->type == BT_CONTAINER) { > + claimed = is_lbinding_this_chassis(child, b_ctx_in->chassis_rec); > handled = consider_container_lport(child->pb, b_ctx_in, b_ctx_out, > qos_map); > if (!handled) { > return false; > } > + > + now_claimed = is_lbinding_this_chassis(child, > + b_ctx_in->chassis_rec); > + update_lport_tracking(child->pb, claimed, now_claimed, > + b_ctx_out->tracked_dp_bindings); > } > } > > diff --git a/controller/binding.h b/controller/binding.h > index 21118ecd4..fc2a673e5 100644 > --- a/controller/binding.h > +++ b/controller/binding.h > @@ -19,6 +19,9 @@ > > #include <stdbool.h> > #include "openvswitch/shash.h" > +#include "openvswitch/hmap.h" > +#include "openvswitch/uuid.h" > +#include "openvswitch/list.h" > > struct hmap; > struct ovsdb_idl; > @@ -58,6 +61,8 @@ struct binding_ctx_out { > struct sset *local_lport_ids; > struct sset *egress_ifaces; > struct smap *local_iface_ids; > + struct hmap *tracked_dp_bindings; > + bool *local_lports_changed; > }; > > enum local_binding_type { > @@ -82,6 +87,21 @@ local_binding_find(struct shash *local_bindings, const char *name) > return shash_find_data(local_bindings, name); > } > > +/* Represents a tracked binding logical port. */ > +struct tracked_binding_lport { > + const struct sbrec_port_binding *pb; > + struct ovs_list list_node; > + bool deleted; > +}; > + > +/* Represent a tracked binding datapath. */ > +struct tracked_binding_datapath { > + struct hmap_node node; > + const struct sbrec_datapath_binding *dp; > + bool is_new; > + struct ovs_list lports_head; /* List of struct tracked_binding_lport. */ > +}; > + > void binding_register_ovs_idl(struct ovsdb_idl *); > void binding_run(struct binding_ctx_in *, struct binding_ctx_out *); > bool binding_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn, > @@ -96,4 +116,5 @@ bool binding_handle_ovs_interface_changes(struct binding_ctx_in *, > bool binding_handle_port_binding_changes(struct binding_ctx_in *, > struct binding_ctx_out *, > bool *changed); > +void binding_tracked_dp_destroy(struct hmap *tracked_datapaths); > #endif /* controller/binding.h */ > diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c > index 8f95fff1f..dc790f0f7 100644 > --- a/controller/ovn-controller.c > +++ b/controller/ovn-controller.c > @@ -978,6 +978,23 @@ struct ed_type_runtime_data { > struct smap local_iface_ids; > }; > > +struct ed_type_runtime_tracked_data { > + struct hmap tracked_dp_bindings; > + bool local_lports_changed; > + bool tracked; > +}; > + > +static void > +en_runtime_clear_tracked_data(void *tracked_data) > +{ > + struct ed_type_runtime_tracked_data *data = tracked_data; > + > + binding_tracked_dp_destroy(&data->tracked_dp_bindings); > + hmap_init(&data->tracked_dp_bindings); > + data->local_lports_changed = false; > + data->tracked = false; > +} > + > static void * > en_runtime_data_init(struct engine_node *node OVS_UNUSED, > struct engine_arg *arg OVS_UNUSED) > @@ -991,6 +1008,13 @@ en_runtime_data_init(struct engine_node *node OVS_UNUSED, > sset_init(&data->egress_ifaces); > smap_init(&data->local_iface_ids); > local_bindings_init(&data->local_bindings); > + > + struct ed_type_runtime_tracked_data *tracked_data = > + xzalloc(sizeof *tracked_data); > + hmap_init(&tracked_data->tracked_dp_bindings); > + node->tracked_data = tracked_data; > + node->clear_tracked_data = en_runtime_clear_tracked_data; > + > return data; > } > > @@ -1093,6 +1117,8 @@ init_binding_ctx(struct engine_node *node, > b_ctx_out->egress_ifaces = &rt_data->egress_ifaces; > b_ctx_out->local_bindings = &rt_data->local_bindings; > b_ctx_out->local_iface_ids = &rt_data->local_iface_ids; > + b_ctx_out->tracked_dp_bindings = NULL; > + b_ctx_out->local_lports_changed = NULL; > } > > static void > @@ -1104,6 +1130,8 @@ en_runtime_data_run(struct engine_node *node, void *data) > struct sset *local_lport_ids = &rt_data->local_lport_ids; > struct sset *active_tunnels = &rt_data->active_tunnels; > > + en_runtime_clear_tracked_data(node->tracked_data); > + > static bool first_run = true; > if (first_run) { > /* don't cleanup since there is no data yet */ > @@ -1156,9 +1184,13 @@ static bool > runtime_data_ovs_interface_handler(struct engine_node *node, void *data) > { > struct ed_type_runtime_data *rt_data = data; > + struct ed_type_runtime_tracked_data *tracked_data = node->tracked_data; > struct binding_ctx_in b_ctx_in; > struct binding_ctx_out b_ctx_out; > init_binding_ctx(node, rt_data, &b_ctx_in, &b_ctx_out); > + tracked_data->tracked = true; > + b_ctx_out.tracked_dp_bindings = &tracked_data->tracked_dp_bindings; > + b_ctx_out.local_lports_changed = &tracked_data->local_lports_changed; > > bool changed = false; > if (!binding_handle_ovs_interface_changes(&b_ctx_in, &b_ctx_out, > @@ -1191,6 +1223,10 @@ runtime_data_sb_port_binding_handler(struct engine_node *node, void *data) > return false; > } > > + struct ed_type_runtime_tracked_data *tracked_data = node->tracked_data; > + tracked_data->tracked = true; > + b_ctx_out.tracked_dp_bindings = &tracked_data->tracked_dp_bindings; > + > bool changed = false; > if (!binding_handle_port_binding_changes(&b_ctx_in, &b_ctx_out, > &changed)) { > @@ -1862,6 +1898,29 @@ physical_flow_changes_ovs_iface_handler(struct engine_node *node OVS_UNUSED, > return true; > } > > +static bool > +flow_output_runtime_data_handler(struct engine_node *node, > + void *data OVS_UNUSED) > +{ > + struct ed_type_runtime_tracked_data *tracked_data = > + engine_get_input_tracked_data("runtime_data", node); > + > + if (!tracked_data || !tracked_data->tracked) { > + return false; > + } > + > + if (!hmap_is_empty(&tracked_data->tracked_dp_bindings)) { > + /* We are not yet handling the tracked datapath binding > + * changes. Return false to trigger full recompute. */ > + return false; > + } > + > + if (tracked_data->local_lports_changed) { > + engine_set_node_state(node, EN_UPDATED); > + } > + return true;
Once the change handler is added and when it returns true, it means it has handled the changes incrementally with full confidence. However, I didn't see handling of the changes in runtime_data for local_bindings, local_lport_ids, active_tunnels, egress_ifaces, local_iface_ids, etc. (they are not involved in the tracked data, if I read the code correctly). Even for local_lports, why do we return true even if there are changes tracked for it? So how could we be confident that those changes can be ignored by flow_output node? If they can be ignored, why would they be needed in flow_output_run(), which passes them to flow_run()/physical_run()? Is there something missing? > +} > + > struct ovn_controller_exit_args { > bool *exiting; > bool *restart; > @@ -2014,7 +2073,8 @@ main(int argc, char *argv[]) > flow_output_addr_sets_handler); > engine_add_input(&en_flow_output, &en_port_groups, > flow_output_port_groups_handler); > - engine_add_input(&en_flow_output, &en_runtime_data, NULL); > + engine_add_input(&en_flow_output, &en_runtime_data, > + flow_output_runtime_data_handler); > engine_add_input(&en_flow_output, &en_mff_ovn_geneve, NULL); > engine_add_input(&en_flow_output, &en_physical_flow_changes, > flow_output_physical_flow_changes_handler); > diff --git a/tests/ovn-performance.at b/tests/ovn-performance.at > index 5cc1960b6..6e064e64f 100644 > --- a/tests/ovn-performance.at > +++ b/tests/ovn-performance.at > @@ -274,7 +274,7 @@ for i in 1 2; do > ) > > # Add router port to $ls > - OVN_CONTROLLER_EXPECT_HIT( > + OVN_CONTROLLER_EXPECT_NO_HIT( > [hv1 hv2], [lflow_run], > [ovn-nbctl --wait=hv lrp-add lr1 $lrp 02:00:00:00:0$i:01 10.0.$i.1/24] > ) > @@ -282,15 +282,15 @@ for i in 1 2; do > [hv1 hv2], [lflow_run], > [ovn-nbctl --wait=hv lsp-add $ls $lsp] > ) > - OVN_CONTROLLER_EXPECT_HIT( > + OVN_CONTROLLER_EXPECT_NO_HIT( > [hv1 hv2], [lflow_run], > [ovn-nbctl --wait=hv lsp-set-type $lsp router] > ) > - OVN_CONTROLLER_EXPECT_HIT( > + OVN_CONTROLLER_EXPECT_NO_HIT( > [hv1 hv2], [lflow_run], > [ovn-nbctl --wait=hv lsp-set-options $lsp router-port=$lrp] > ) > - OVN_CONTROLLER_EXPECT_HIT( > + OVN_CONTROLLER_EXPECT_NO_HIT( > [hv1 hv2], [lflow_run], > [ovn-nbctl --wait=hv lsp-set-addresses $lsp router] > ) > @@ -404,8 +404,8 @@ for i in 1 2; do > lp=lp$i > > # Delete port $lp > - OVN_CONTROLLER_EXPECT_HIT_COND( > - [hv$i hv$j], [lflow_run], [>0 =0], > + OVN_CONTROLLER_EXPECT_NO_HIT( > + [hv$i hv$j], [lflow_run], > [ovn-nbctl --wait=hv lsp-del $lp] > ) > done > @@ -416,15 +416,15 @@ OVN_CONTROLLER_EXPECT_NO_HIT( > [ovn-nbctl --wait=hv destroy Port_Group pg1] > ) > > -for i in 1 2; do > - ls=ls$i > +OVN_CONTROLLER_EXPECT_HIT( > + [hv1 hv2], [lflow_run], > + [ovn-nbctl --wait=hv ls-del ls1] > +) > > - # Delete switch $ls > - OVN_CONTROLLER_EXPECT_HIT( > - [hv1 hv2], [lflow_run], > - [ovn-nbctl --wait=hv ls-del $ls] > - ) > -done > +OVN_CONTROLLER_EXPECT_NO_HIT( > + [hv1 hv2], [lflow_run], > + [ovn-nbctl --wait=hv ls-del ls2] > +) > > # Delete router lr1 > OVN_CONTROLLER_EXPECT_NO_HIT( > -- > 2.26.2 > > _______________________________________________ > dev mailing list > d...@openvswitch.org > https://mail.openvswitch.org/mailman/listinfo/ovs-dev _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev