On Tue, Nov 22, 2022 at 6:14 AM Dumitru Ceara <[email protected]> wrote: > > Expand SB.Template_Var records in two stages: > 1. first expand them to local values in match/action strings > 2. then reparse the expanded strings > > For the case when a lflow references a Chassis_Template_Var > also track references (similar to the ones maintained for > multicast groups, address sets, port_groups, port bindings). > > Signed-off-by: Dumitru Ceara <[email protected]> > --- > V3: > - Addressed Mark's comments: > - Added missing calls to store_lflow_template_refs() in > consider_lflow_for_added_as_ips__(). > - Reverted unrelated changes. > - Added TODO item for ovn-trace. > - Changed lexer_parse_template_string() to make it return an > encapsulated parsed string. That's to try to avoid user errors. > V2: > - Fix GCC build due to missing newline. > - Handle SB table rename Template_Var -> Chassis_Template_Var. > - Address Han's comments: > - Add new function to parse lflow actions. > - Move xstrdup inside lexer_parse_template_string() and execute it only > if needed. > - Match template vars only by chassis name. > - Change local_templates table to a plain smap. > - Some indentation updates. > - Use NULL template variable change handlers for chassis/Open_vSwitch > changes. > - Added more tests. > - Fix tracking of template references in lflow actions. > --- > TODO.rst | 4 + > controller/lflow.c | 127 +++++++++++++------ > controller/lflow.h | 1 > controller/ofctrl.c | 13 +- > controller/ofctrl.h | 3 > controller/ovn-controller.c | 289 +++++++++++++++++++++++++++++++++++++++++++ > include/ovn/expr.h | 4 - > include/ovn/lex.h | 54 +++++++- > lib/actions.c | 9 + > lib/expr.c | 18 ++- > lib/lex.c | 54 ++++++++ > lib/objdep.c | 1 > lib/objdep.h | 1 > tests/ovn-controller.at | 50 +++++++ > tests/ovn.at | 80 ++++++++++++ > tests/test-ovn.c | 16 ++ > utilities/ovn-trace.c | 36 ++++- > 17 files changed, 688 insertions(+), 72 deletions(-) > > diff --git a/TODO.rst b/TODO.rst > index 12738280ba..fe5f9a2f30 100644 > --- a/TODO.rst > +++ b/TODO.rst > @@ -179,3 +179,7 @@ OVN To-do List > OVS' daemonize_start() call and initializes the additional things, like > the unixctl commands. Or, we should move the APIs such as > daemon_started_recently() to OVS's lib/daemon. > + > +* Chassis_Template_Var > + > + * Support template variables when tracing packets with ovn-trace. > diff --git a/controller/lflow.c b/controller/lflow.c > index d4434bdee8..84625fb3f1 100644 > --- a/controller/lflow.c > +++ b/controller/lflow.c > @@ -81,6 +81,8 @@ convert_match_to_expr(const struct sbrec_logical_flow *, > const struct local_datapath *ldp, > struct expr **prereqs, const struct shash *addr_sets, > const struct shash *port_groups, > + const struct smap *template_vars, > + struct sset *template_vars_ref, > struct objdep_mgr *, bool *pg_addr_set_ref); > static void > add_matches_to_flow_table(const struct sbrec_logical_flow *, > @@ -297,6 +299,54 @@ as_info_from_expr_const(const char *as_name, const union expr_constant *c, > return true; > } > > +static void > +store_lflow_template_refs(struct objdep_mgr *lflow_deps_mgr, > + const struct sset *template_vars_ref, > + const struct sbrec_logical_flow *lflow) > +{ > + const char *tv_name; > + SSET_FOR_EACH (tv_name, template_vars_ref) { > + objdep_mgr_add(lflow_deps_mgr, OBJDEP_TYPE_TEMPLATE, tv_name, > + &lflow->header_.uuid); > + } > +} > + > +static bool > +lflow_parse_actions(const struct sbrec_logical_flow *lflow, > + const struct lflow_ctx_in *l_ctx_in, > + struct sset *template_vars_ref, > + struct ofpbuf *ovnacts_out, > + struct expr **prereqs_out) > +{ > + bool ingress = !strcmp(lflow->pipeline, "ingress"); > + struct ovnact_parse_params pp = { > + .symtab = &symtab, > + .dhcp_opts = l_ctx_in->dhcp_opts, > + .dhcpv6_opts = l_ctx_in->dhcpv6_opts, > + .nd_ra_opts = l_ctx_in->nd_ra_opts, > + .controller_event_opts = l_ctx_in->controller_event_opts, > + > + .pipeline = ingress ? OVNACT_P_INGRESS : OVNACT_P_EGRESS, > + .n_tables = LOG_PIPELINE_LEN, > + .cur_ltable = lflow->table_id, > + }; > + > + struct lex_str actions_s = > + lexer_parse_template_string(lflow->actions, l_ctx_in->template_vars, > + template_vars_ref); > + char *error = ovnacts_parse_string(lex_str_get(&actions_s), &pp, > + ovnacts_out, prereqs_out); > + lex_str_free(&actions_s); > + if (error) { > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); > + VLOG_WARN_RL(&rl, "error parsing actions \"%s\": %s", > + lflow->actions, error); > + free(error); > + return false; > + } > + return true; > +} > + > /* Parses the lflow regarding the changed address set 'as_name', and generates > * ovs flows for the newly added addresses in 'as_diff_added' only. It is > * similar to consider_logical_flow__, with the below differences: > @@ -347,27 +397,16 @@ consider_lflow_for_added_as_ips__( > > uint64_t ovnacts_stub[1024 / 8]; > struct ofpbuf ovnacts = OFPBUF_STUB_INITIALIZER(ovnacts_stub); > - struct ovnact_parse_params pp = { > - .symtab = &symtab, > - .dhcp_opts = l_ctx_in->dhcp_opts, > - .dhcpv6_opts = l_ctx_in->dhcpv6_opts, > - .nd_ra_opts = l_ctx_in->nd_ra_opts, > - .controller_event_opts = l_ctx_in->controller_event_opts, > - .pipeline = ingress ? OVNACT_P_INGRESS : OVNACT_P_EGRESS, > - .n_tables = LOG_PIPELINE_LEN, > - .cur_ltable = lflow->table_id, > - }; > + struct sset template_vars_ref = SSET_INITIALIZER(&template_vars_ref); > struct expr *prereqs = NULL; > - char *error; > > - error = ovnacts_parse_string(lflow->actions, &pp, &ovnacts, &prereqs); > - if (error) { > - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); > - VLOG_WARN_RL(&rl, "error parsing actions \"%s\": %s", > - lflow->actions, error); > - free(error); > + if (!lflow_parse_actions(lflow, l_ctx_in, &template_vars_ref, > + &ovnacts, &prereqs)) { > ovnacts_free(ovnacts.data, ovnacts.size); > ofpbuf_uninit(&ovnacts); > + store_lflow_template_refs(l_ctx_out->lflow_deps_mgr, > + &template_vars_ref, lflow); > + sset_destroy(&template_vars_ref); > return true; > } > > @@ -431,6 +470,8 @@ consider_lflow_for_added_as_ips__( > struct expr *expr = convert_match_to_expr(lflow, ldp, &prereqs, > l_ctx_in->addr_sets, > l_ctx_in->port_groups, > + l_ctx_in->template_vars, > + &template_vars_ref, > l_ctx_out->lflow_deps_mgr, NULL); > shash_replace((struct shash *)l_ctx_in->addr_sets, as_name, real_as); > if (new_fake_as) { > @@ -502,6 +543,11 @@ done: > ofpbuf_uninit(&ovnacts); > expr_destroy(expr); > expr_matches_destroy(&matches); > + > + store_lflow_template_refs(l_ctx_out->lflow_deps_mgr, > + &template_vars_ref, lflow); > + sset_destroy(&template_vars_ref); > + > return handled; > } > > @@ -913,6 +959,8 @@ convert_match_to_expr(const struct sbrec_logical_flow *lflow, > struct expr **prereqs, > const struct shash *addr_sets, > const struct shash *port_groups, > + const struct smap *template_vars, > + struct sset *template_vars_ref, > struct objdep_mgr *mgr, > bool *pg_addr_set_ref) > { > @@ -920,11 +968,16 @@ convert_match_to_expr(const struct sbrec_logical_flow *lflow, > struct sset port_groups_ref = SSET_INITIALIZER(&port_groups_ref); > char *error = NULL; > > - struct expr *e = expr_parse_string(lflow->match, &symtab, addr_sets, > - port_groups, &addr_sets_ref, > + struct lex_str match_s = lexer_parse_template_string(lflow->match, > + template_vars, > + template_vars_ref); > + struct expr *e = expr_parse_string(lex_str_get(&match_s), &symtab, > + addr_sets, port_groups, &addr_sets_ref, > &port_groups_ref, > ldp->datapath->tunnel_key, > &error); > + lex_str_free(&match_s); > + > struct shash_node *addr_sets_ref_node; > SHASH_FOR_EACH (addr_sets_ref_node, &addr_sets_ref) { > objdep_mgr_add_with_refcount(mgr, OBJDEP_TYPE_ADDRSET, > @@ -1015,28 +1068,16 @@ consider_logical_flow__(const struct sbrec_logical_flow *lflow, > * XXX Deny changes to 'outport' in egress pipeline. */ > uint64_t ovnacts_stub[1024 / 8]; > struct ofpbuf ovnacts = OFPBUF_STUB_INITIALIZER(ovnacts_stub); > - struct ovnact_parse_params pp = { > - .symtab = &symtab, > - .dhcp_opts = l_ctx_in->dhcp_opts, > - .dhcpv6_opts = l_ctx_in->dhcpv6_opts, > - .nd_ra_opts = l_ctx_in->nd_ra_opts, > - .controller_event_opts = l_ctx_in->controller_event_opts, > - > - .pipeline = ingress ? OVNACT_P_INGRESS : OVNACT_P_EGRESS, > - .n_tables = LOG_PIPELINE_LEN, > - .cur_ltable = lflow->table_id, > - }; > + struct sset template_vars_ref = SSET_INITIALIZER(&template_vars_ref); > struct expr *prereqs = NULL; > - char *error; > > - error = ovnacts_parse_string(lflow->actions, &pp, &ovnacts, &prereqs); > - if (error) { > - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); > - VLOG_WARN_RL(&rl, "error parsing actions \"%s\": %s", > - lflow->actions, error); > - free(error); > + if (!lflow_parse_actions(lflow, l_ctx_in, &template_vars_ref, > + &ovnacts, &prereqs)) { > ovnacts_free(ovnacts.data, ovnacts.size); > ofpbuf_uninit(&ovnacts); > + store_lflow_template_refs(l_ctx_out->lflow_deps_mgr, > + &template_vars_ref, lflow); > + sset_destroy(&template_vars_ref); > return; > } > > @@ -1087,6 +1128,8 @@ consider_logical_flow__(const struct sbrec_logical_flow *lflow, > case LCACHE_T_NONE: > expr = convert_match_to_expr(lflow, ldp, &prereqs, l_ctx_in->addr_sets, > l_ctx_in->port_groups, > + l_ctx_in->template_vars, > + &template_vars_ref, > l_ctx_out->lflow_deps_mgr, > &pg_addr_set_ref); > if (!expr) { > @@ -1101,11 +1144,13 @@ consider_logical_flow__(const struct sbrec_logical_flow *lflow, > } > > /* If caching is enabled and this is a not cached expr that doesn't refer > - * to address sets or port groups, save it to potentially cache it later. > + * to address sets, port groups, or template variables, save it to > + * potentially cache it later. > */ > if (lcv_type == LCACHE_T_NONE > && lflow_cache_is_enabled(l_ctx_out->lflow_cache) > - && !pg_addr_set_ref) { > + && !pg_addr_set_ref > + && sset_is_empty(&template_vars_ref)) { > cached_expr = expr_clone(expr); > } > > @@ -1190,6 +1235,10 @@ done: > expr_destroy(cached_expr); > expr_matches_destroy(matches); > free(matches); > + > + store_lflow_template_refs(l_ctx_out->lflow_deps_mgr, > + &template_vars_ref, lflow); > + sset_destroy(&template_vars_ref); > } > > static void > diff --git a/controller/lflow.h b/controller/lflow.h > index a7025eb23c..d95fd41142 100644 > --- a/controller/lflow.h > +++ b/controller/lflow.h > @@ -113,6 +113,7 @@ struct lflow_ctx_in { > const struct hmap *dhcp_opts; > const struct hmap *dhcpv6_opts; > const struct controller_event_options *controller_event_opts; > + const struct smap *template_vars; > bool lb_hairpin_use_ct_mark; > }; > > diff --git a/controller/ofctrl.c b/controller/ofctrl.c > index c779912583..6c631ea41e 100644 > --- a/controller/ofctrl.c > +++ b/controller/ofctrl.c > @@ -2873,7 +2873,8 @@ ofctrl_lookup_port(const void *br_int_, const char *port_name, > char * > ofctrl_inject_pkt(const struct ovsrec_bridge *br_int, const char *flow_s, > const struct shash *addr_sets, > - const struct shash *port_groups) > + const struct shash *port_groups, > + const struct smap *template_vars) > { > int version = rconn_get_version(swconn); > if (version < 0) { > @@ -2881,9 +2882,13 @@ ofctrl_inject_pkt(const struct ovsrec_bridge *br_int, const char *flow_s, > } > > struct flow uflow; > - char *error = expr_parse_microflow(flow_s, &symtab, addr_sets, > - port_groups, ofctrl_lookup_port, > - br_int, &uflow); > + struct lex_str flow_exp_s = lexer_parse_template_string(flow_s, > + template_vars, > + NULL); > + char *error = expr_parse_microflow(lex_str_get(&flow_exp_s), &symtab, > + addr_sets, port_groups, > + ofctrl_lookup_port, br_int, &uflow); > + lex_str_free(&flow_exp_s); > if (error) { > return error; > } > diff --git a/controller/ofctrl.h b/controller/ofctrl.h > index 71d3f5838a..3968245126 100644 > --- a/controller/ofctrl.h > +++ b/controller/ofctrl.h > @@ -71,7 +71,8 @@ void ofctrl_ct_flush_zone(uint16_t zone_id); > > char *ofctrl_inject_pkt(const struct ovsrec_bridge *br_int, > const char *flow_s, const struct shash *addr_sets, > - const struct shash *port_groups); > + const struct shash *port_groups, > + const struct smap *template_vars); > > /* Flow table interfaces to the rest of ovn-controller. */ > > diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c > index 09028d1452..f9ed0e3855 100644 > --- a/controller/ovn-controller.c > +++ b/controller/ovn-controller.c > @@ -91,6 +91,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_template_vars; > static unixctl_cb_func debug_dump_lflow_conj_ids; > static unixctl_cb_func lflow_cache_flush_cmd; > static unixctl_cb_func lflow_cache_show_stats_cmd; > @@ -170,6 +171,8 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl, > * > * Monitor IGMP_Groups for local chassis. > * > + * Monitor Template_Var for local chassis. > + * > * We always monitor patch ports because they allow us to see the linkages > * between related logical datapaths. That way, when we know that we have > * a VIF on a particular logical switch, we immediately know to monitor all > @@ -184,6 +187,7 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl, > struct ovsdb_idl_condition ip_mcast = OVSDB_IDL_CONDITION_INIT(&ip_mcast); > struct ovsdb_idl_condition igmp = OVSDB_IDL_CONDITION_INIT(&igmp); > struct ovsdb_idl_condition chprv = OVSDB_IDL_CONDITION_INIT(&chprv); > + struct ovsdb_idl_condition tv = OVSDB_IDL_CONDITION_INIT(&tv); > > /* Always monitor all logical datapath groups. Otherwise, DPG updates may > * be received *after* the lflows using it are seen by ovn-controller. > @@ -201,6 +205,7 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl, > ovsdb_idl_condition_add_clause_true(&ip_mcast); > ovsdb_idl_condition_add_clause_true(&igmp); > ovsdb_idl_condition_add_clause_true(&chprv); > + ovsdb_idl_condition_add_clause_true(&tv); > goto out; > } > > @@ -239,6 +244,9 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl, > /* Monitors Chassis_Private record for current chassis only. */ > sbrec_chassis_private_add_clause_name(&chprv, OVSDB_F_EQ, > chassis->name); > + > + sbrec_chassis_template_var_add_clause_chassis(&tv, OVSDB_F_EQ, > + chassis->name); > } else { > /* During initialization, we monitor all records in Chassis_Private so > * that we don't try to recreate existing ones. */ > @@ -290,6 +298,7 @@ out:; > sbrec_ip_multicast_set_condition(ovnsb_idl, &ip_mcast), > sbrec_igmp_group_set_condition(ovnsb_idl, &igmp), > sbrec_chassis_private_set_condition(ovnsb_idl, &chprv), > + sbrec_chassis_template_var_set_condition(ovnsb_idl, &tv), > }; > > unsigned int expected_cond_seqno = 0; > @@ -307,6 +316,7 @@ out:; > ovsdb_idl_condition_destroy(&ip_mcast); > ovsdb_idl_condition_destroy(&igmp); > ovsdb_idl_condition_destroy(&chprv); > + ovsdb_idl_condition_destroy(&tv); > return expected_cond_seqno; > } > > @@ -1014,7 +1024,8 @@ ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl) > SB_NODE(load_balancer, "load_balancer") \ > SB_NODE(fdb, "fdb") \ > SB_NODE(meter, "meter") \ > - SB_NODE(static_mac_binding, "static_mac_binding") > + SB_NODE(static_mac_binding, "static_mac_binding") \ > + SB_NODE(chassis_template_var, "chassis_template_var") > > enum sb_engine_node { > #define SB_NODE(NAME, NAME_STR) SB_##NAME, > @@ -1676,6 +1687,184 @@ runtime_data_sb_datapath_binding_handler(struct engine_node *node OVS_UNUSED, > return true; > } > > +static void > +local_templates_to_string(const struct smap *local_templates, > + struct ds *out_data) > +{ > + const struct smap_node **nodes = smap_sort(local_templates); > + > + for (size_t i = 0; i < smap_count(local_templates); i++) { > + const struct smap_node *node = nodes[i]; > + > + ds_put_format(out_data, "name: '%s' value: '%s'\n", > + node->key, node->value); > + } > + free(nodes); > +} > + > +struct ed_type_template_vars { > + struct smap local_templates; > + > + bool change_tracked; > + struct sset new; > + struct sset deleted; > + struct sset updated; > +}; > + > +static void > +template_vars_init(struct ovsdb_idl_index *tv_index_by_chassis, > + const struct sbrec_chassis *chassis, > + struct smap *local_templates) > +{ > + const struct sbrec_chassis_template_var *tv; > + struct sbrec_chassis_template_var *tv_key = > + sbrec_chassis_template_var_index_init_row(tv_index_by_chassis); > + sbrec_chassis_template_var_index_set_chassis(tv_key, chassis->name); > + > + tv = sbrec_chassis_template_var_index_find(tv_index_by_chassis, tv_key); > + if (tv) { > + smap_destroy(local_templates); > + smap_clone(local_templates, &tv->variables); > + } > + > + sbrec_chassis_template_var_index_destroy_row(tv_key); > +} > + > +static void > +template_vars_update(const struct sbrec_chassis_template_var_table *tv_table, > + const struct sbrec_chassis *chassis, > + struct smap *local_templates, struct sset *new, > + struct sset *deleted, struct sset *updated) > +{ > + const struct sbrec_chassis_template_var *tv; > + struct smap_node *node; > + SBREC_CHASSIS_TEMPLATE_VAR_TABLE_FOR_EACH_TRACKED (tv, tv_table) { > + if (strcmp(tv->chassis, chassis->name)) { > + continue; > + } > + > + if (sbrec_chassis_template_var_is_deleted(tv)) { > + SMAP_FOR_EACH (node, local_templates) { > + sset_add(deleted, node->key); > + } > + } else if (sbrec_chassis_template_var_is_new(tv)) { > + ovs_assert(smap_count(local_templates) == 0); > + SMAP_FOR_EACH (node, &tv->variables) { > + sset_add(new, node->key); > + } > + } else { > + SMAP_FOR_EACH (node, &tv->variables) { > + struct smap_node *old = smap_get_node(local_templates, > + node->key); > + if (old) { > + if (strcmp(old->value, node->value)) { > + sset_add(updated, node->key); > + } > + } else { > + sset_add(new, node->key); > + } > + } > + SMAP_FOR_EACH (node, local_templates) { > + sset_add (deleted, node->key); > + } > + } > + > + smap_destroy(local_templates); > + smap_clone(local_templates, &tv->variables); > + return; > + } > +} > + > +static void * > +en_template_vars_init(struct engine_node *node OVS_UNUSED, > + struct engine_arg *arg OVS_UNUSED) > +{ > + struct ed_type_template_vars *tv_data = xzalloc(sizeof *tv_data); > + smap_init(&tv_data->local_templates); > + tv_data->change_tracked = false; > + sset_init(&tv_data->new); > + sset_init(&tv_data->deleted); > + sset_init(&tv_data->updated); > + return tv_data; > +} > + > +static void > +en_template_vars_run(struct engine_node *node, void *data) > +{ > + struct ed_type_template_vars *tv_data = data; > + > + const struct ovsrec_open_vswitch_table *ovs_table = > + EN_OVSDB_GET(engine_get_input("OVS_open_vswitch", node)); > + struct ovsdb_idl_index *sbrec_chassis_by_name = > + engine_ovsdb_node_get_index(engine_get_input("SB_chassis", node), > + "name"); > + const struct sbrec_chassis *chassis = > + chassis_lookup_by_name(sbrec_chassis_by_name, > + get_ovs_chassis_id(ovs_table)); > + struct ovsdb_idl_index *sbrec_chassis_template_var_index_by_chassis = > + engine_ovsdb_node_get_index( > + engine_get_input("SB_chassis_template_var", node), > + "chassis"); > + > + smap_clear(&tv_data->local_templates); > + template_vars_init(sbrec_chassis_template_var_index_by_chassis, > + chassis, &tv_data->local_templates); > + engine_set_node_state(node, EN_UPDATED); > +} > + > +static bool > +template_vars_sb_chassis_template_var_handler(struct engine_node *node, > + void *data) > +{ > + struct ed_type_template_vars *tv_data = data; > + > + const struct sbrec_chassis_template_var_table *tv_table = > + EN_OVSDB_GET(engine_get_input("SB_chassis_template_var", node)); > + const struct ovsrec_open_vswitch_table *ovs_table = > + EN_OVSDB_GET(engine_get_input("OVS_open_vswitch", node)); > + struct ovsdb_idl_index *sbrec_chassis_by_name = > + engine_ovsdb_node_get_index(engine_get_input("SB_chassis", node), > + "name"); > + const struct sbrec_chassis *chassis = > + chassis_lookup_by_name(sbrec_chassis_by_name, > + get_ovs_chassis_id(ovs_table)); > + > + template_vars_update(tv_table, chassis, &tv_data->local_templates, > + &tv_data->new, &tv_data->deleted, &tv_data->updated); > + > + if (!sset_is_empty(&tv_data->new) || !sset_is_empty(&tv_data->deleted) || > + !sset_is_empty(&tv_data->updated)) { > + engine_set_node_state(node, EN_UPDATED); > + } else { > + engine_set_node_state(node, EN_UNCHANGED); > + } > + > + tv_data->change_tracked = true; > + return true; > +} > + > +static void > +en_template_vars_clear_tracked_data(void *data) > +{ > + struct ed_type_template_vars *tv_data = data; > + > + sset_clear(&tv_data->new); > + sset_clear(&tv_data->deleted); > + sset_clear(&tv_data->updated); > + tv_data->change_tracked = false; > +} > + > +static void > +en_template_vars_cleanup(void *data) > +{ > + struct ed_type_template_vars *tv_data = data; > + > + smap_destroy(&tv_data->local_templates); > + sset_destroy(&tv_data->new); > + sset_destroy(&tv_data->deleted); > + sset_destroy(&tv_data->updated); > +} > + > struct ed_type_addr_sets { > struct shash addr_sets; > bool change_tracked; > @@ -2724,6 +2913,9 @@ init_lflow_ctx(struct engine_node *node, > struct ed_type_dhcp_options *dhcp_opts = > engine_get_input_data("dhcp_options", node); > > + struct ed_type_template_vars *template_vars = > + engine_get_input_data("template_vars", node); > + > l_ctx_in->sbrec_multicast_group_by_name_datapath = > sbrec_mc_group_by_name_dp; > l_ctx_in->sbrec_logical_flow_by_logical_datapath = > @@ -2756,6 +2948,7 @@ init_lflow_ctx(struct engine_node *node, > l_ctx_in->dhcp_opts = &dhcp_opts->v4_opts; > l_ctx_in->dhcpv6_opts = &dhcp_opts->v6_opts; > l_ctx_in->controller_event_opts = &fo->controller_event_opts; > + l_ctx_in->template_vars = &template_vars->local_templates; > > l_ctx_out->flow_table = &fo->flow_table; > l_ctx_out->group_table = &fo->group_table; > @@ -3082,6 +3275,64 @@ lflow_output_port_groups_handler(struct engine_node *node, void *data) > return true; > } > > +static bool > +lflow_output_template_vars_handler(struct engine_node *node, void *data) > +{ > + struct ed_type_template_vars *tv_data = > + engine_get_input_data("template_vars", node); > + > + struct ed_type_lflow_output *fo = data; > + struct lflow_ctx_out l_ctx_out; > + struct lflow_ctx_in l_ctx_in; > + init_lflow_ctx(node, fo, &l_ctx_in, &l_ctx_out); > + > + const char *res_name; > + bool changed; > + > + if (!tv_data->change_tracked) { > + return false; > + } > + > + SSET_FOR_EACH (res_name, &tv_data->deleted) { > + if (!objdep_mgr_handle_change(l_ctx_out.lflow_deps_mgr, > + OBJDEP_TYPE_TEMPLATE, > + res_name, lflow_handle_changed_ref, > + l_ctx_out.lflows_processed, > + &l_ctx_in, &l_ctx_out, &changed)) { > + return false; > + } > + if (changed) { > + engine_set_node_state(node, EN_UPDATED); > + } > + } > + SSET_FOR_EACH (res_name, &tv_data->updated) { > + if (!objdep_mgr_handle_change(l_ctx_out.lflow_deps_mgr, > + OBJDEP_TYPE_TEMPLATE, > + res_name, lflow_handle_changed_ref, > + l_ctx_out.lflows_processed, > + &l_ctx_in, &l_ctx_out, &changed)) { > + return false; > + } > + if (changed) { > + engine_set_node_state(node, EN_UPDATED); > + } > + } > + SSET_FOR_EACH (res_name, &tv_data->new) { > + if (!objdep_mgr_handle_change(l_ctx_out.lflow_deps_mgr, > + OBJDEP_TYPE_TEMPLATE, > + res_name, lflow_handle_changed_ref, > + l_ctx_out.lflows_processed, > + &l_ctx_in, &l_ctx_out, &changed)) { > + return false; > + } > + if (changed) { > + engine_set_node_state(node, EN_UPDATED); > + } > + } > + > + return true; > +} > + > static bool > lflow_output_runtime_data_handler(struct engine_node *node, > void *data OVS_UNUSED) > @@ -3668,6 +3919,9 @@ main(int argc, char *argv[]) > struct ovsdb_idl_index *sbrec_static_mac_binding_by_datapath > = ovsdb_idl_index_create1(ovnsb_idl_loop.idl, > &sbrec_static_mac_binding_col_datapath); > + struct ovsdb_idl_index *sbrec_chassis_template_var_index_by_chassis > + = ovsdb_idl_index_create1(ovnsb_idl_loop.idl, > + &sbrec_chassis_template_var_col_chassis); > > ovsdb_idl_track_add_all(ovnsb_idl_loop.idl); > ovsdb_idl_omit_alert(ovnsb_idl_loop.idl, > @@ -3729,6 +3983,7 @@ main(int argc, char *argv[]) > > /* Define inc-proc-engine nodes. */ > ENGINE_NODE(sb_ro, "sb_ro"); > + ENGINE_NODE_WITH_CLEAR_TRACK_DATA(template_vars, "template_vars"); > ENGINE_NODE_WITH_CLEAR_TRACK_DATA_IS_VALID(ct_zones, "ct_zones"); > ENGINE_NODE_WITH_CLEAR_TRACK_DATA(ovs_interface_shadow, > "ovs_interface_shadow"); > @@ -3755,6 +4010,10 @@ main(int argc, char *argv[]) > #undef OVS_NODE > > /* Add dependencies between inc-proc-engine nodes. */ > + engine_add_input(&en_template_vars, &en_ovs_open_vswitch, NULL); > + engine_add_input(&en_template_vars, &en_sb_chassis, NULL); > + engine_add_input(&en_template_vars, &en_sb_chassis_template_var, > + template_vars_sb_chassis_template_var_handler); > > engine_add_input(&en_addr_sets, &en_sb_address_set, > addr_sets_sb_address_set_handler); > @@ -3822,6 +4081,8 @@ main(int argc, char *argv[]) > lflow_output_addr_sets_handler); > engine_add_input(&en_lflow_output, &en_port_groups, > lflow_output_port_groups_handler); > + engine_add_input(&en_lflow_output, &en_template_vars, > + lflow_output_template_vars_handler); > engine_add_input(&en_lflow_output, &en_runtime_data, > lflow_output_runtime_data_handler); > engine_add_input(&en_lflow_output, &en_non_vif_data, > @@ -3930,6 +4191,8 @@ main(int argc, char *argv[]) > sbrec_mac_binding_by_datapath); > engine_ovsdb_node_add_index(&en_sb_static_mac_binding, "datapath", > sbrec_static_mac_binding_by_datapath); > + engine_ovsdb_node_add_index(&en_sb_chassis_template_var, "chassis", > + sbrec_chassis_template_var_index_by_chassis); > > struct ed_type_lflow_output *lflow_output_data = > engine_get_internal_data(&en_lflow_output); > @@ -3939,6 +4202,8 @@ main(int argc, char *argv[]) > engine_get_internal_data(&en_ct_zones); > struct ed_type_runtime_data *runtime_data = > engine_get_internal_data(&en_runtime_data); > + struct ed_type_template_vars *template_vars_data = > + engine_get_internal_data(&en_template_vars); > > ofctrl_init(&lflow_output_data->group_table, > &lflow_output_data->meter_table, > @@ -4000,6 +4265,10 @@ main(int argc, char *argv[]) > debug_dump_lflow_conj_ids, > &lflow_output_data->conj_ids); > > + unixctl_command_register("debug/dump-local-template-vars", "", 0, 0, > + debug_dump_local_template_vars, > + &template_vars_data->local_templates); > + > unixctl_command_register("debug/ignore-startup-delay", "", 0, 0, > debug_ignore_startup_delay, NULL); > > @@ -4376,9 +4645,12 @@ main(int argc, char *argv[]) > engine_get_data(&en_addr_sets); > struct ed_type_port_groups *pg_data = > engine_get_data(&en_port_groups); > - if (br_int && chassis && as_data && pg_data) { > + struct ed_type_template_vars *tv_data = > + engine_get_data(&en_template_vars); > + if (br_int && chassis && as_data && pg_data && tv_data) { > char *error = ofctrl_inject_pkt(br_int, pending_pkt.flow_s, > - &as_data->addr_sets, &pg_data->port_groups_cs_local); > + &as_data->addr_sets, &pg_data->port_groups_cs_local, > + &tv_data->local_templates); > if (error) { > unixctl_command_reply_error(pending_pkt.conn, error); > free(error); > @@ -4847,6 +5119,17 @@ debug_dump_lflow_conj_ids(struct unixctl_conn *conn, int argc OVS_UNUSED, > ds_destroy(&conj_ids_dump); > } > > +static void > +debug_dump_local_template_vars(struct unixctl_conn *conn, int argc OVS_UNUSED, > + const char *argv[] OVS_UNUSED, void *local_vars) > +{ > + struct ds tv_str = DS_EMPTY_INITIALIZER; > + ds_put_cstr(&tv_str, "Local template vars:\n"); > + local_templates_to_string(local_vars, &tv_str); > + unixctl_command_reply(conn, ds_cstr(&tv_str)); > + ds_destroy(&tv_str); > +} > + > static void > debug_ignore_startup_delay(struct unixctl_conn *conn, int argc OVS_UNUSED, > const char *argv[] OVS_UNUSED, void *arg OVS_UNUSED) > diff --git a/include/ovn/expr.h b/include/ovn/expr.h > index 3b141b034c..80d95ff675 100644 > --- a/include/ovn/expr.h > +++ b/include/ovn/expr.h > @@ -59,6 +59,7 @@ > #include "openvswitch/match.h" > #include "openvswitch/meta-flow.h" > #include "logical-fields.h" > +#include "smap.h" > > struct ds; > struct expr; > @@ -521,7 +522,8 @@ union expr_constant { > char *string; > }; > > -bool expr_constant_parse(struct lexer *, const struct expr_field *, > +bool expr_constant_parse(struct lexer *, > + const struct expr_field *, > union expr_constant *); > void expr_constant_format(const union expr_constant *, > enum expr_constant_type, struct ds *); > diff --git a/include/ovn/lex.h b/include/ovn/lex.h > index ecb7ace243..9159b7a263 100644 > --- a/include/ovn/lex.h > +++ b/include/ovn/lex.h > @@ -23,6 +23,8 @@ > * This is a simple lexical analyzer (or tokenizer) for OVN match expressions > * and ACLs. */ > > +#include "smap.h" > +#include "sset.h" > #include "openvswitch/meta-flow.h" > > struct ds; > @@ -37,7 +39,8 @@ enum lex_type { > LEX_T_INTEGER, /* 12345 or 1.2.3.4 or ::1 or 01:02:03:04:05 */ > LEX_T_MASKED_INTEGER, /* 12345/10 or 1.2.0.0/16 or ::2/127 or... */ > LEX_T_MACRO, /* $NAME */ > - LEX_T_PORT_GROUP, /* @NAME */ > + LEX_T_PORT_GROUP, /* @NAME */ > + LEX_T_TEMPLATE, /* ^NAME */ > LEX_T_ERROR, /* invalid input */ > > /* Bare tokens. */ > @@ -86,9 +89,9 @@ struct lex_token { > /* One of LEX_*. */ > enum lex_type type; > > - /* Meaningful for LEX_T_ID, LEX_T_STRING, LEX_T_ERROR, LEX_T_MACRO only. > - * For these token types, 's' may point to 'buffer'; otherwise, it points > - * to malloc()ed memory owned by the token. > + /* Meaningful for LEX_T_ID, LEX_T_STRING, LEX_T_ERROR, LEX_T_MACRO, > + * LEX_T_TEMPLATE only. For these token types, 's' may point to 'buffer'; > + * otherwise, it points to malloc()ed memory owned by the token. > * > * Must be NULL for other token types. > * > @@ -151,4 +154,47 @@ void lexer_syntax_error(struct lexer *, const char *message, ...) > > char *lexer_steal_error(struct lexer *); > > +struct lex_str { > + union { > + const char *cs; > + char *s; > + }; > + bool owned; > +}; > + > +static inline struct lex_str > +lex_str_use(const char *s) > +{ > + return (struct lex_str) { > + .cs = s, > + .owned = false, > + }; > +} > + > +static inline struct lex_str > +lex_str_steal(char *s) > +{ > + return (struct lex_str) { > + .s = s, > + .owned = true, > + }; > +} > + > +static inline const char * > +lex_str_get(const struct lex_str *ls) > +{ > + return ls->owned ? ls->s : ls->cs; > +} > + > +static inline void > +lex_str_free(struct lex_str *ls) > +{ > + if (ls->owned) { > + free(ls->s); > + } > +} > + > +struct lex_str lexer_parse_template_string(const char *s, > + const struct smap *template_vars, > + struct sset *template_vars_ref); > #endif /* ovn/lex.h */ > diff --git a/lib/actions.c b/lib/actions.c > index b59f364bf5..87c7e45439 100644 > --- a/lib/actions.c > +++ b/lib/actions.c > @@ -258,8 +258,8 @@ add_prerequisite(struct action_context *ctx, const char *prerequisite) > struct expr *expr; > char *error; > > - expr = expr_parse_string(prerequisite, ctx->pp->symtab, NULL, NULL, > - NULL, NULL, 0, &error); > + expr = expr_parse_string(prerequisite, ctx->pp->symtab, NULL, NULL, NULL, > + NULL, 0, &error); > ovs_assert(!error); > ctx->prereqs = expr_combine(EXPR_T_AND, ctx->prereqs, expr); > } > @@ -5106,6 +5106,11 @@ parse_set_action(struct action_context *ctx) > static bool > parse_action(struct action_context *ctx) > { > + if (ctx->lexer->token.type == LEX_T_TEMPLATE) { > + lexer_error(ctx->lexer, "Unexpanded template."); > + return false; > + } > + > if (ctx->lexer->token.type != LEX_T_ID) { > lexer_syntax_error(ctx->lexer, NULL); > return false; > diff --git a/lib/expr.c b/lib/expr.c > index d1f9d28ca6..43f44771cf 100644 > --- a/lib/expr.c > +++ b/lib/expr.c > @@ -695,7 +695,9 @@ parse_field(struct expr_context *ctx, struct expr_field *f) > return false; > } > > - symbol = shash_find_data(ctx->symtab, ctx->lexer->token.s); > + symbol = ctx->symtab > + ? shash_find_data(ctx->symtab, ctx->lexer->token.s) > + : NULL; > if (!symbol) { > lexer_syntax_error(ctx->lexer, "expecting field name"); > return false; > @@ -894,7 +896,10 @@ parse_constant(struct expr_context *ctx, struct expr_constant_set *cs, > cs->as_name = NULL; > } > > - if (ctx->lexer->token.type == LEX_T_STRING) { > + if (ctx->lexer->token.type == LEX_T_TEMPLATE) { > + lexer_error(ctx->lexer, "Unexpanded template."); > + return false; > + } else if (ctx->lexer->token.type == LEX_T_STRING) { > if (!assign_constant_set_type(ctx, cs, EXPR_C_STRING)) { > return false; > } > @@ -978,7 +983,9 @@ expr_constant_parse(struct lexer *lexer, const struct expr_field *f, > return false; > } > > - struct expr_context ctx = { .lexer = lexer }; > + struct expr_context ctx = { > + .lexer = lexer, > + }; > > struct expr_constant_set cs; > memset(&cs, 0, sizeof cs); > @@ -1332,7 +1339,10 @@ expr_parse_primary(struct expr_context *ctx, bool *atomic) > return e; > } > > - if (ctx->lexer->token.type == LEX_T_ID) { > + if (ctx->lexer->token.type == LEX_T_TEMPLATE) { > + lexer_error(ctx->lexer, "Unexpanded template."); > + return NULL; > + } else if (ctx->lexer->token.type == LEX_T_ID) { > struct expr_field f; > enum expr_relop r; > struct expr_constant_set c; > diff --git a/lib/lex.c b/lib/lex.c > index c84d52aa8d..5251868b5a 100644 > --- a/lib/lex.c > +++ b/lib/lex.c > @@ -235,6 +235,10 @@ lex_token_format(const struct lex_token *token, struct ds *s) > ds_put_format(s, "@%s", token->s); > break; > > + case LEX_T_TEMPLATE: > + ds_put_format(s, "^%s", token->s); > + break; > + > case LEX_T_LPAREN: > ds_put_cstr(s, "("); > break; > @@ -588,6 +592,18 @@ lex_parse_port_group(const char *p, struct lex_token *token) > return lex_parse_id(p, LEX_T_PORT_GROUP, token); > } > > +static const char * > +lex_parse_template(const char *p, struct lex_token *token) > +{ > + p++; > + if (!lex_is_id1(*p)) { > + lex_error(token, "`^' must be followed by a valid identifier."); > + return p; > + } > + > + return lex_parse_id(p, LEX_T_TEMPLATE, token); > +} > + > /* Initializes 'token' and parses the first token from the beginning of > * null-terminated string 'p' into 'token'. Stores a pointer to the start of > * the token (after skipping white space and comments, if any) into '*startp'. > @@ -766,6 +782,10 @@ next: > p = lex_parse_port_group(p, token); > break; > > + case '^': > + p = lex_parse_template(p, token); > + break; > + > case ':': > if (p[1] != ':') { > token->type = LEX_T_COLON; > @@ -1031,3 +1051,37 @@ lexer_steal_error(struct lexer *lexer) > lexer->error = NULL; > return error; > } > + > +/* Takes ownership of 's' and expands all templates that are encountered > + * in the contents of 's', if possible. Adds the encountered template names > + * to 'template_vars_ref'. > + */ > +struct lex_str > +lexer_parse_template_string(const char *s, const struct smap *template_vars, > + struct sset *template_vars_ref) > +{ > + /* No '^' means no templates. */ > + if (!strchr(s, '^')) { > + return lex_str_use(s); > + } > + > + struct ds expanded = DS_EMPTY_INITIALIZER; > + > + struct lexer lexer; > + lexer_init(&lexer, s); > + > + while (lexer_get(&lexer) != LEX_T_END) { > + if (lexer.token.type == LEX_T_TEMPLATE) { > + ds_put_cstr(&expanded, smap_get_def(template_vars, lexer.token.s, > + lexer.token.s)); > + if (template_vars_ref) { > + sset_add(template_vars_ref, lexer.token.s); > + } > + } else { > + lex_token_format(&lexer.token, &expanded); > + } > + } > + > + lexer_destroy(&lexer); > + return lex_str_steal(ds_steal_cstr(&expanded)); > +} > diff --git a/lib/objdep.c b/lib/objdep.c > index 092d4af261..06cf126f12 100644 > --- a/lib/objdep.c > +++ b/lib/objdep.c > @@ -245,6 +245,7 @@ objdep_type_name(enum objdep_type type) > [OBJDEP_TYPE_PORTGROUP] = "Port_Group", > [OBJDEP_TYPE_PORTBINDING] = "Port_Binding", > [OBJDEP_TYPE_MC_GROUP] = "Multicast_Group", > + [OBJDEP_TYPE_TEMPLATE] = "Template", > }; > > ovs_assert(type < OBJDEP_TYPE_MAX); > diff --git a/lib/objdep.h b/lib/objdep.h > index 50c7b01ef1..1ea781947c 100644 > --- a/lib/objdep.h > +++ b/lib/objdep.h > @@ -26,6 +26,7 @@ enum objdep_type { > OBJDEP_TYPE_PORTGROUP, > OBJDEP_TYPE_PORTBINDING, > OBJDEP_TYPE_MC_GROUP, > + OBJDEP_TYPE_TEMPLATE, > OBJDEP_TYPE_MAX, > }; > > diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at > index 6a0e83c338..6bc9ba75d3 100644 > --- a/tests/ovn-controller.at > +++ b/tests/ovn-controller.at > @@ -2449,3 +2449,53 @@ OVS_WAIT_UNTIL([check_ovsdb_zone ${lr_uuid}_snat $snat_zone]) > > OVN_CLEANUP([hv1]) > AT_CLEANUP > + > +OVN_FOR_EACH_NORTHD([ > +AT_SETUP([ovn-controller - Local Chassis_Template_Var updates]) > +AT_KEYWORDS([templates]) > +ovn_start > + > +net_add n1 > +sim_add hv > +as hv > +ovs-vsctl \ > + -- add-br br-phys \ > + -- add-br br-eth0 \ > + -- add-br br-eth1 \ > + -- add-br br-eth2 > +ovn_attach n1 br-phys 192.168.0.1 > + > +m4_define([GET_LOCAL_TEMPLATE_VARS], > + [ovn-appctl debug/dump-local-template-vars | grep -v 'Local template vars']) > + > +dnl Expect no local vars initially. > +AT_CHECK([GET_LOCAL_TEMPLATE_VARS], [1], []) > + > +AT_CHECK([ovn-nbctl --wait=hv create Chassis_Template_Var chassis="hv"], [0], [ignore]) > +AT_CHECK([GET_LOCAL_TEMPLATE_VARS], [1], []) > + > +dnl Expect new vars to be handled properly. > +check ovn-nbctl --wait=hv set Chassis_Template_Var hv variables:foo=foo-hv variables:bar=bar-hv > +AT_CHECK([GET_LOCAL_TEMPLATE_VARS], [0], [dnl > +name: 'bar' value: 'bar-hv' > +name: 'foo' value: 'foo-hv' > +]) > + > +dnl Expect var updates to be handled properly. > +check ovn-nbctl --wait=hv set Chassis_Template_Var hv variables:bar=bar-new-hv > +AT_CHECK([GET_LOCAL_TEMPLATE_VARS], [0], [dnl > +name: 'bar' value: 'bar-new-hv' > +name: 'foo' value: 'foo-hv' > +]) > + > +dnl Expect var deletions to be handled properly. > +check ovn-nbctl --wait=hv remove Chassis_Template_Var hv variables bar > +AT_CHECK([GET_LOCAL_TEMPLATE_VARS], [0], [dnl > +name: 'foo' value: 'foo-hv' > +]) > + > +check ovn-nbctl --wait=hv remove Chassis_Template_Var hv variables foo > +AT_CHECK([GET_LOCAL_TEMPLATE_VARS], [1], []) > + > +AT_CLEANUP > +]) > diff --git a/tests/ovn.at b/tests/ovn.at > index dde0f582b5..68e788e78f 100644 > --- a/tests/ovn.at > +++ b/tests/ovn.at > @@ -259,7 +259,7 @@ fe:x => error("Invalid numeric constant.") > & => error("`&' is only valid as part of `&&'.") > | => error("`|' is only valid as part of `||'.") > > -^ => error("Invalid character `^' in input.") > +^ => error("`^' must be followed by a valid identifier.") > ]) > AT_CAPTURE_FILE([input.txt]) > sed 's/ =>.*//' test-cases.txt > input.txt > @@ -33067,3 +33067,81 @@ check ovn-nbctl --wait=hv sync > OVN_CLEANUP([hv1]) > AT_CLEANUP > ]) > + > +OVN_FOR_EACH_NORTHD([ > +AT_SETUP([Logical flows with Chassis_Template_Var references]) > +AT_KEYWORDS([templates]) > +ovn_start > +net_add n1 > + > +sim_add hv1 > +as hv1 > +ovs-vsctl add-br br-phys > +ovn_attach n1 br-phys 192.168.0.1 > + > +check ovn-nbctl ls-add sw > + > +dnl Use --wait=sb to ensure lsp1 getting a tunnel_key before lsp2. > +check ovn-nbctl --wait=sb lsp-add sw lsp1 > +check ovn-nbctl --wait=sb lsp-add sw lsp2 > + > +AT_CHECK([ovn-nbctl create Chassis_Template_Var chassis=hv1], [0], [ignore]) > + > +check ovn-nbctl pg-add pg1 lsp1 lsp2 > +AT_CHECK([ovn-nbctl create address_set name=as1 addresses=\"1.1.1.1\",\"1.1.1.2\"], [0], [ignore]) > + > +dnl Create an ACL that uses an "uninstantiated" template. > +check ovn-nbctl acl-add sw from-lport 1 "ip4.src == 42.42.42.42 && ^CONDITION" allow > + > +check ovs-vsctl add-port br-int p1 -- set interface p1 external_ids:iface-id=lsp1 > +check ovs-vsctl add-port br-int p2 -- set interface p2 external_ids:iface-id=lsp2 > + > +wait_for_ports_up > +ovn-nbctl --wait=hv sync > + > +dnl Ensure the ACL is not translated to OpenFlow. > +as hv1 > +AT_CHECK([ovs-ofctl dump-flows br-int | grep '42\.42\.42\.42'], [1], []) > + > +dnl Create a Chassis_Template_Var mapping for CONDITION. > +check ovn-nbctl --wait=hv set Chassis_Template_Var hv1 variables:CONDITION='inport == @pg1' > + > +lsp1=0x$(fetch_column Port_Binding tunnel_key logical_port=lsp1) > +lsp2=0x$(fetch_column Port_Binding tunnel_key logical_port=lsp2) > + > +dnl Ensure the ACL is translated to OpenFlows expanding pg1. > +as hv1 > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int | grep '42\.42\.42\.42' | ofctl_strip_all], [0], [dnl > + table=16, priority=1001,ip,reg14=$lsp1,metadata=0x1,nw_src=42.42.42.42 actions=resubmit(,17) > + table=16, priority=1001,ip,reg14=$lsp2,metadata=0x1,nw_src=42.42.42.42 actions=resubmit(,17) > +]) > + > +dnl Remove a port from pg1 and expect OpenFlows to be correctly updated. > +check ovn-nbctl --wait=hv pg-set-ports pg1 lsp2 > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int | grep '42\.42\.42\.42' | ofctl_strip_all], [0], [dnl > + table=16, priority=1001,ip,reg14=$lsp2,metadata=0x1,nw_src=42.42.42.42 actions=resubmit(,17) > +]) > + > +dnl Change the Chassis_Template_Var mapping to use the address set. > +check ovn-nbctl --wait=hv set Chassis_Template_Var hv1 variables:CONDITION='ip4.dst == $as1' > + > +dnl Ensure the ACL is translated to OpenFlows expanding as1. > +as hv1 > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int | grep '42\.42\.42\.42' | ofctl_strip_all], [0], [dnl > + table=16, priority=1001,ip,metadata=0x1,nw_src=42.42.42.42,nw_dst=1.1.1.1 actions=resubmit(,17) > + table=16, priority=1001,ip,metadata=0x1,nw_src=42.42.42.42,nw_dst=1.1.1.2 actions=resubmit(,17) > +]) > + > +dnl Remove an IP from AS1 and expect OpenFlows to be correctly updated. > +check ovn-nbctl set address_set as1 addresses=\"1.1.1.1\" > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int | grep '42\.42\.42\.42' | ofctl_strip_all], [0], [dnl > + table=16, priority=1001,ip,metadata=0x1,nw_src=42.42.42.42,nw_dst=1.1.1.1 actions=resubmit(,17) > +]) > + > +dnl Remove the mapping and expect OpenFlows to be removed. > +check ovn-nbctl --wait=hv clear Chassis_Template_Var hv1 variables > +AT_CHECK([ovs-ofctl dump-flows br-int | grep '42\.42\.42\.42'], [1], []) > + > +OVN_CLEANUP([hv1]) > +AT_CLEANUP > +]) > diff --git a/tests/test-ovn.c b/tests/test-ovn.c > index a241f150d1..54747a52ec 100644 > --- a/tests/test-ovn.c > +++ b/tests/test-ovn.c > @@ -291,12 +291,14 @@ test_parse_expr__(int steps) > struct shash symtab; > struct shash addr_sets; > struct shash port_groups; > + struct smap template_vars; > struct simap ports; > struct ds input; > > create_symtab(&symtab); > create_addr_sets(&addr_sets); > create_port_groups(&port_groups); > + smap_init(&template_vars); > > simap_init(&ports); > simap_put(&ports, "eth0", 5); > @@ -355,6 +357,7 @@ test_parse_expr__(int steps) > shash_destroy(&addr_sets); > expr_const_sets_destroy(&port_groups); > shash_destroy(&port_groups); > + smap_destroy(&template_vars); > } > > static void > @@ -913,8 +916,8 @@ test_tree_shape_exhaustively(struct expr *expr, struct shash *symtab, > expr_format(expr, &s); > > char *error; > - modified = expr_parse_string(ds_cstr(&s), symtab, NULL, > - NULL, NULL, NULL, 0, &error); > + modified = expr_parse_string(ds_cstr(&s), symtab, NULL, NULL, NULL, > + NULL, 0, &error); > if (error) { > fprintf(stderr, "%s fails to parse (%s)\n", > ds_cstr(&s), error); > @@ -1286,6 +1289,8 @@ test_parse_actions(struct ovs_cmdl_context *ctx OVS_UNUSED) > struct ds input; > bool ok = true; > > + struct smap template_vars = SMAP_INITIALIZER(&template_vars); > + > create_symtab(&symtab); > create_gen_opts(&dhcp_opts, &dhcpv6_opts, &nd_ra_opts, &event_opts); > > @@ -1322,7 +1327,10 @@ test_parse_actions(struct ovs_cmdl_context *ctx OVS_UNUSED) > .n_tables = 24, > .cur_ltable = 10, > }; > - error = ovnacts_parse_string(ds_cstr(&input), &pp, &ovnacts, &prereqs); > + struct lex_str exp_input = > + lexer_parse_template_string(ds_cstr(&input), &template_vars, NULL); > + error = ovnacts_parse_string(lex_str_get(&exp_input), &pp, &ovnacts, > + &prereqs); > if (!error) { > /* Convert the parsed representation back to a string and print it, > * if it's different from the input. */ > @@ -1409,6 +1417,7 @@ test_parse_actions(struct ovs_cmdl_context *ctx OVS_UNUSED) > expr_destroy(prereqs); > ovnacts_free(ovnacts.data, ovnacts.size); > ofpbuf_uninit(&ovnacts); > + lex_str_free(&exp_input); > } > ds_destroy(&input); > > @@ -1419,6 +1428,7 @@ test_parse_actions(struct ovs_cmdl_context *ctx OVS_UNUSED) > dhcp_opts_destroy(&dhcpv6_opts); > nd_ra_opts_destroy(&nd_ra_opts); > controller_event_opts_destroy(&event_opts); > + smap_destroy(&template_vars); > ovn_extend_table_destroy(&group_table); > ovn_extend_table_destroy(&meter_table); > exit(ok ? EXIT_SUCCESS : EXIT_FAILURE); > diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c > index 8e3a8d9ca9..f4c3de61fe 100644 > --- a/utilities/ovn-trace.c > +++ b/utilities/ovn-trace.c > @@ -505,6 +505,7 @@ static struct hmap dhcp_opts; /* Contains "struct gen_opts_map"s. */ > static struct hmap dhcpv6_opts; /* Contains "struct gen_opts_map"s. */ > static struct hmap nd_ra_opts; /* Contains "struct gen_opts_map"s. */ > static struct controller_event_options event_opts; > +static struct smap template_vars; > > static struct ovntrace_datapath * > ovntrace_datapath_find_by_sb_uuid(const struct uuid *sb_uuid) > @@ -955,9 +956,13 @@ parse_lflow_for_datapath(const struct sbrec_logical_flow *sblf, > > char *error; > struct expr *match; > - match = expr_parse_string(sblf->match, &symtab, &address_sets, > - &port_groups, NULL, NULL, dp->tunnel_key, > - &error); > + struct lex_str match_s = lexer_parse_template_string(sblf->match, > + &template_vars, > + NULL); > + match = expr_parse_string(lex_str_get(&match_s), &symtab, > + &address_sets, &port_groups, NULL, NULL, > + dp->tunnel_key, &error); > + lex_str_free(&match_s); > if (error) { > VLOG_WARN("%s: parsing expression failed (%s)", > sblf->match, error); > @@ -980,7 +985,11 @@ parse_lflow_for_datapath(const struct sbrec_logical_flow *sblf, > uint64_t stub[1024 / 8]; > struct ofpbuf ovnacts = OFPBUF_STUB_INITIALIZER(stub); > struct expr *prereqs; > - error = ovnacts_parse_string(sblf->actions, &pp, &ovnacts, &prereqs); > + struct lex_str actions_s = > + lexer_parse_template_string(sblf->actions, &template_vars, NULL); > + error = ovnacts_parse_string(lex_str_get(&actions_s), &pp, &ovnacts, > + &prereqs); > + lex_str_free(&actions_s); > if (error) { > VLOG_WARN("%s: parsing actions failed (%s)", sblf->actions, error); > free(error); > @@ -1078,6 +1087,7 @@ read_gen_opts(void) > nd_ra_opts_init(&nd_ra_opts); > > controller_event_opts_init(&event_opts); > + smap_init(&template_vars); > } > > static void > @@ -3424,9 +3434,12 @@ trace_parse(const char *dp_s, const char *flow_s, > * > * First make sure that the expression parses. */ > char *error; > - struct expr *e = expr_parse_string(flow_s, &symtab, &address_sets, > - &port_groups, NULL, NULL, 0, > - &error); > + struct lex_str flow_exp_s = > + lexer_parse_template_string(flow_s, &template_vars, NULL); > + struct expr *e = expr_parse_string(lex_str_get(&flow_exp_s), &symtab, > + &address_sets, &port_groups, NULL, > + NULL, 0, &error); > + lex_str_free(&flow_exp_s); > if (!e) { > return trace_parse_error(error); > } > @@ -3451,9 +3464,12 @@ trace_parse(const char *dp_s, const char *flow_s, > free(port_name); > } > > - char *error = expr_parse_microflow(flow_s, &symtab, &address_sets, > - &port_groups, ovntrace_lookup_port, > - *dpp, uflow); > + struct lex_str flow_exp_s = > + lexer_parse_template_string(flow_s, &template_vars, NULL); > + char *error = expr_parse_microflow(lex_str_get(&flow_exp_s), &symtab, > + &address_sets, &port_groups, > + ovntrace_lookup_port, *dpp, uflow); > + lex_str_free(&flow_exp_s); > if (error) { > return trace_parse_error(error); > } >
Acked-by: Han Zhou <[email protected]> _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
