diff options
| -rw-r--r-- | lib/mgmt_be_client.c | 211 | ||||
| -rw-r--r-- | lib/mgmt_be_client.h | 2 | ||||
| -rw-r--r-- | lib/northbound.h | 10 | ||||
| -rw-r--r-- | lib/northbound_oper.c | 81 | ||||
| -rw-r--r-- | lib/subdir.am | 1 | ||||
| -rw-r--r-- | mgmtd/mgmt_be_adapter.c | 11 | ||||
| -rw-r--r-- | mgmtd/mgmt_main.c | 7 | ||||
| -rw-r--r-- | ripd/rip_main.c | 1 | ||||
| -rw-r--r-- | ripngd/ripng_main.c | 1 | ||||
| -rw-r--r-- | staticd/static_main.c | 1 | ||||
| -rw-r--r-- | yang/frr-backend.yang | 102 | ||||
| -rw-r--r-- | yang/subdir.am | 1 | ||||
| -rw-r--r-- | zebra/main.c | 1 | 
13 files changed, 416 insertions, 14 deletions
diff --git a/lib/mgmt_be_client.c b/lib/mgmt_be_client.c index efd5d70a74..3a07a1d2d9 100644 --- a/lib/mgmt_be_client.c +++ b/lib/mgmt_be_client.c @@ -99,12 +99,12 @@ struct mgmt_be_client {  	struct nb_config *candidate_config;  	struct nb_config *running_config; -	unsigned long num_edit_nb_cfg; -	unsigned long avg_edit_nb_cfg_tm; -	unsigned long num_prep_nb_cfg; -	unsigned long avg_prep_nb_cfg_tm; -	unsigned long num_apply_nb_cfg; -	unsigned long avg_apply_nb_cfg_tm; +	uint64_t num_edit_nb_cfg; +	uint64_t avg_edit_nb_cfg_tm; +	uint64_t num_prep_nb_cfg; +	uint64_t avg_prep_nb_cfg_tm; +	uint64_t num_apply_nb_cfg; +	uint64_t avg_apply_nb_cfg_tm;  	struct mgmt_be_txns_head txn_head; @@ -117,7 +117,7 @@ struct mgmt_be_client {  struct debug mgmt_dbg_be_client = {  	.conf = "debug mgmt client backend", -	.desc = "Management backend client operations" +	.desc = "Management backend client operations",  };  /* NOTE: only one client per proc for now. */ @@ -621,7 +621,7 @@ static int mgmt_be_txn_cfg_prepare(struct mgmt_be_txn_ctx *txn)  	mgmt_be_send_cfgdata_create_reply(client_ctx, txn->txn_id,  		error ? false : true, error ? err_buf : NULL); -	debug_be_client("Avg-nb-edit-duration %lu uSec, nb-prep-duration %lu (avg: %lu) uSec, batch size %u", +	debug_be_client("Avg-nb-edit-duration %Lu uSec, nb-prep-duration %lu (avg: %Lu) uSec, batch size %u",  			client_ctx->avg_edit_nb_cfg_tm, prep_nb_cfg_tm,  			client_ctx->avg_prep_nb_cfg_tm, (uint32_t)num_processed); @@ -770,10 +770,9 @@ static int mgmt_be_txn_proc_cfgapply(struct mgmt_be_txn_ctx *txn)  	gettimeofday(&apply_nb_cfg_end, NULL);  	apply_nb_cfg_tm = timeval_elapsed(apply_nb_cfg_end, apply_nb_cfg_start); -	client_ctx->avg_apply_nb_cfg_tm = ((client_ctx->avg_apply_nb_cfg_tm * -					    client_ctx->num_apply_nb_cfg) + -					   apply_nb_cfg_tm) / -					  (client_ctx->num_apply_nb_cfg + 1); +	client_ctx->avg_apply_nb_cfg_tm = +		((client_ctx->avg_apply_nb_cfg_tm * client_ctx->num_apply_nb_cfg) + apply_nb_cfg_tm) / +		(client_ctx->num_apply_nb_cfg + 1);  	client_ctx->num_apply_nb_cfg++;  	txn->nb_txn = NULL; @@ -789,8 +788,8 @@ static int mgmt_be_txn_proc_cfgapply(struct mgmt_be_txn_ctx *txn)  	mgmt_be_send_apply_reply(client_ctx, txn->txn_id, true, NULL); -	debug_be_client("Nb-apply-duration %lu (avg: %lu) uSec", -			apply_nb_cfg_tm, client_ctx->avg_apply_nb_cfg_tm); +	debug_be_client("Nb-apply-duration %lu (avg: %Lu) uSec", apply_nb_cfg_tm, +			client_ctx->avg_apply_nb_cfg_tm);  	return 0;  } @@ -1333,6 +1332,190 @@ DEFPY(debug_mgmt_client_be, debug_mgmt_client_be_cmd,  	return CMD_SUCCESS;  } +/* + * XPath: /frr-backend:clients/client + * + * We only implement a list of one entry (for the this backend client) the + * results will be merged inside mgmtd. + */ +static const void *clients_client_get_next(struct nb_cb_get_next_args *args) +{ +	if (args->list_entry == NULL) +		return __be_client; +	return NULL; +} + +static int clients_client_get_keys(struct nb_cb_get_keys_args *args) +{ +	args->keys->num = 1; +	strlcpy(args->keys->key[0], __be_client->name, sizeof(args->keys->key[0])); + +	return NB_OK; +} + +static const void *clients_client_lookup_entry(struct nb_cb_lookup_entry_args *args) +{ +	const char *name = args->keys->key[0]; + +	if (!strcmp(name, __be_client->name)) +		return __be_client; + +	return NULL; +} + +/* + * XPath: /frr-backend:clients/client/name + */ +static enum nb_error clients_client_name_get(const struct nb_node *nb_node, +					     const void *parent_list_entry, struct lyd_node *parent) +{ +	const struct lysc_node *snode = nb_node->snode; +	LY_ERR err; + +	err = lyd_new_term(parent, snode->module, snode->name, __be_client->name, false, NULL); +	if (err != LY_SUCCESS) +		return NB_ERR_RESOURCE; + +	return NB_OK; +} + +/* + * XPath: /frr-backend:clients/client/state/candidate-config-version + */ +static enum nb_error clients_client_state_candidate_config_version_get( +	const struct nb_node *nb_node, const void *parent_list_entry, struct lyd_node *parent) +{ +	const struct lysc_node *snode = nb_node->snode; +	uint64_t value = __be_client->candidate_config->version; + +	if (lyd_new_term_bin(parent, snode->module, snode->name, &value, sizeof(value), +			     LYD_NEW_PATH_UPDATE, NULL)) +		return NB_ERR_RESOURCE; + +	return NB_OK; +} + +/* + * XPath: /frr-backend:clients/client/state/running-config-version + */ +static enum nb_error clients_client_state_running_config_version_get(const struct nb_node *nb_node, +								     const void *parent_list_entry, +								     struct lyd_node *parent) +{ +	const struct lysc_node *snode = nb_node->snode; +	uint64_t value = __be_client->running_config->version; + +	if (lyd_new_term_bin(parent, snode->module, snode->name, &value, sizeof(value), +			     LYD_NEW_PATH_UPDATE, NULL)) +		return NB_ERR_RESOURCE; + +	return NB_OK; +} + +/* + * XPath: /frr-backend:clients/client/state/notify-selectors + * + * Is this better in northbound_notif.c? Let's decide when we add more to this module. + */ + +static enum nb_error clients_client_state_notify_selectors_get(const struct nb_node *nb_node, +							       const void *parent_list_entry, +							       struct lyd_node *parent) +{ +	const struct lysc_node *snode = nb_node->snode; +	const char **p; +	LY_ERR err; + +	darr_foreach_p (nb_notif_filters, p) { +		err = lyd_new_term(parent, snode->module, snode->name, *p, false, NULL); +		if (err != LY_SUCCESS) +			return NB_ERR_RESOURCE; +	} + +	return NB_OK; +} + +/* clang-format off */ +const struct frr_yang_module_info frr_backend_info = { +	.name = "frr-backend", +	.nodes = { +		{ +			.xpath = "/frr-backend:clients/client", +			.cbs = { +				.get_next = clients_client_get_next, +				.get_keys = clients_client_get_keys, +				.lookup_entry = clients_client_lookup_entry, +			} +		}, +		{ +			.xpath = "/frr-backend:clients/client/name", +			.cbs.get = clients_client_name_get, +		}, +		{ +			.xpath = "/frr-backend:clients/client/state/candidate-config-version", +			.cbs = { +				.get = clients_client_state_candidate_config_version_get, +			} +		}, +		{ +			.xpath = "/frr-backend:clients/client/state/running-config-version", +			.cbs = { +				.get = clients_client_state_running_config_version_get, +			} +		}, +		{ +			.xpath = "/frr-backend:clients/client/state/edit-count", +			.cbs = { +				.get = nb_oper_uint64_get, +				.get_elem = (void *)(intptr_t)offsetof(struct mgmt_be_client, num_edit_nb_cfg), +			} +		}, +		{ +			.xpath = "/frr-backend:clients/client/state/avg-edit-time", +			.cbs = { +				.get = nb_oper_uint64_get, +				.get_elem = (void *)(intptr_t)offsetof(struct mgmt_be_client, avg_edit_nb_cfg_tm), +			} +		}, +		{ +			.xpath = "/frr-backend:clients/client/state/prep-count", +			.cbs = { +				.get = nb_oper_uint64_get, +				.get_elem = (void *)(intptr_t)offsetof(struct mgmt_be_client, num_prep_nb_cfg), +			} +		}, +		{ +			.xpath = "/frr-backend:clients/client/state/avg-prep-time", +			.cbs = { +				.get = nb_oper_uint64_get, +				.get_elem = (void *)(intptr_t)offsetof(struct mgmt_be_client, avg_prep_nb_cfg_tm), +			} +		}, +		{ +			.xpath = "/frr-backend:clients/client/state/apply-count", +			.cbs = { +				.get = nb_oper_uint64_get, +				.get_elem = (void *)(intptr_t)offsetof(struct mgmt_be_client, num_apply_nb_cfg), +			} +		}, +		{ +			.xpath = "/frr-backend:clients/client/state/avg-apply-time", +			.cbs = { +				.get = nb_oper_uint64_get, +				.get_elem = (void *)(intptr_t)offsetof(struct mgmt_be_client, avg_apply_nb_cfg_tm), +			} +		}, +		{ +			.xpath = "/frr-backend:clients/client/state/notify-selectors", +			.cbs.get = clients_client_state_notify_selectors_get, +		}, +		{ +			.xpath = NULL, +		}, +	} +}; +/* clang-format on */ +  struct mgmt_be_client *mgmt_be_client_create(const char *client_name,  					     struct mgmt_be_client_cbs *cbs,  					     uintptr_t user_data, diff --git a/lib/mgmt_be_client.h b/lib/mgmt_be_client.h index a3e3896d52..5e78f0f433 100644 --- a/lib/mgmt_be_client.h +++ b/lib/mgmt_be_client.h @@ -85,6 +85,8 @@ struct mgmt_be_client_cbs {  extern struct debug mgmt_dbg_be_client; +extern const struct frr_yang_module_info frr_backend_info; +  /***************************************************************   * API prototypes   ***************************************************************/ diff --git a/lib/northbound.h b/lib/northbound.h index bd802e0888..ce59bfd01a 100644 --- a/lib/northbound.h +++ b/lib/northbound.h @@ -836,6 +836,9 @@ extern struct debug nb_dbg_libyang;  /* Global running configuration. */  extern struct nb_config *running_config; +/* Global notification filters */ +extern const char **nb_notif_filters; +  /* Wrappers for the northbound callbacks. */  extern struct yang_data *nb_callback_has_new_get_elem(const struct nb_node *nb_node); @@ -1521,6 +1524,13 @@ extern void *nb_oper_walk_finish_arg(void *walk);   */  extern void *nb_oper_walk_cb_arg(void *walk); +/* Generic getter functions */ +extern enum nb_error nb_oper_uint32_get(const struct nb_node *nb_node, +					const void *parent_list_entry, struct lyd_node *parent); + +extern enum nb_error nb_oper_uint64_get(const struct nb_node *nb_node, +					const void *parent_list_entry, struct lyd_node *parent); +  /*   * Validate if the northbound callback operation is valid for the given node.   * diff --git a/lib/northbound_oper.c b/lib/northbound_oper.c index a296b147ac..6336db502a 100644 --- a/lib/northbound_oper.c +++ b/lib/northbound_oper.c @@ -1918,6 +1918,87 @@ enum nb_error nb_oper_iterate_legacy(const char *xpath,  	return ret;  } +static const char *__adjust_ptr(struct lysc_node_leaf *lsnode, const char *valuep, size_t *size) +{ +	switch (lsnode->type->basetype) { +	case LY_TYPE_INT8: +	case LY_TYPE_UINT8: +#ifdef BIG_ENDIAN +		valuep += 7; +#endif +		*size = 1; +		break; +	case LY_TYPE_INT16: +	case LY_TYPE_UINT16: +#ifdef BIG_ENDIAN +		valuep += 6; +#endif +		*size = 2; +		break; +	case LY_TYPE_INT32: +	case LY_TYPE_UINT32: +#ifdef BIG_ENDIAN +		valuep += 4; +#endif +		*size = 4; +		break; +	case LY_TYPE_INT64: +	case LY_TYPE_UINT64: +		*size = 8; +		break; +	case LY_TYPE_UNKNOWN: +	case LY_TYPE_BINARY: +	case LY_TYPE_STRING: +	case LY_TYPE_BITS: +	case LY_TYPE_BOOL: +	case LY_TYPE_DEC64: +	case LY_TYPE_EMPTY: +	case LY_TYPE_ENUM: +	case LY_TYPE_IDENT: +	case LY_TYPE_INST: +	case LY_TYPE_LEAFREF: +	case LY_TYPE_UNION: +	default: +		assert(0); +	} +	return valuep; +} + +enum nb_error nb_oper_uint64_get(const struct nb_node *nb_node, const void *parent_list_entry, +				 struct lyd_node *parent) +{ +	struct lysc_node_leaf *lsnode = (struct lysc_node_leaf *)nb_node->snode; +	struct lysc_node *snode = &lsnode->node; +	ssize_t offset = (ssize_t)nb_node->cbs.get_elem; +	uint64_t ubigval = *(uint64_t *)((char *)parent_list_entry + offset); +	const char *valuep; +	size_t size; + +	valuep = __adjust_ptr(lsnode, (const char *)&ubigval, &size); +	if (lyd_new_term_bin(parent, snode->module, snode->name, valuep, size, LYD_NEW_PATH_UPDATE, +			     NULL)) +		return NB_ERR_RESOURCE; +	return NB_OK; +} + + +enum nb_error nb_oper_uint32_get(const struct nb_node *nb_node, const void *parent_list_entry, +				 struct lyd_node *parent) +{ +	struct lysc_node_leaf *lsnode = (struct lysc_node_leaf *)nb_node->snode; +	struct lysc_node *snode = &lsnode->node; +	ssize_t offset = (ssize_t)nb_node->cbs.get_elem; +	uint64_t ubigval = *(uint64_t *)((char *)parent_list_entry + offset); +	const char *valuep; +	size_t size; + +	valuep = __adjust_ptr(lsnode, (const char *)&ubigval, &size); +	if (lyd_new_term_bin(parent, snode->module, snode->name, valuep, size, LYD_NEW_PATH_UPDATE, +			     NULL)) +		return NB_ERR_RESOURCE; +	return NB_OK; +} +  void nb_oper_init(struct event_loop *loop)  {  	event_loop = loop; diff --git a/lib/subdir.am b/lib/subdir.am index 984e92ff17..a975eb2fc4 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -145,6 +145,7 @@ lib_libfrr_la_SOURCES = \  nodist_lib_libfrr_la_SOURCES = \  	yang/frr-affinity-map.yang.c \ +	yang/frr-backend.yang.c \  	yang/frr-filter.yang.c \  	yang/frr-if-rmap.yang.c \  	yang/frr-interface.yang.c \ diff --git a/mgmtd/mgmt_be_adapter.c b/mgmtd/mgmt_be_adapter.c index e159d68ec0..1c32f936b0 100644 --- a/mgmtd/mgmt_be_adapter.c +++ b/mgmtd/mgmt_be_adapter.c @@ -77,6 +77,7 @@ static const char *const zebra_config_xpaths[] = {  };  static const char *const zebra_oper_xpaths[] = { +	"/frr-backend:clients",  	"/frr-interface:lib/interface",  	"/frr-vrf:lib/vrf/frr-zebra:zebra",  	"/frr-zebra:zebra", @@ -94,6 +95,7 @@ static const char *const ripd_config_xpaths[] = {  	NULL,  };  static const char *const ripd_oper_xpaths[] = { +	"/frr-backend:clients",  	"/frr-ripd:ripd",  	"/ietf-key-chain:key-chains",  	NULL, @@ -114,6 +116,7 @@ static const char *const ripngd_config_xpaths[] = {  	NULL,  };  static const char *const ripngd_oper_xpaths[] = { +	"/frr-backend:clients",  	"/frr-ripngd:ripngd",  	NULL,  }; @@ -130,6 +133,11 @@ static const char *const staticd_config_xpaths[] = {  	"/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd",  	NULL,  }; + +static const char *const staticd_oper_xpaths[] = { +	"/frr-backend:clients", +	NULL, +};  #endif  static const char *const *be_client_config_xpaths[MGMTD_BE_CLIENT_ID_MAX] = { @@ -152,6 +160,9 @@ static const char *const *be_client_oper_xpaths[MGMTD_BE_CLIENT_ID_MAX] = {  #ifdef HAVE_RIPNGD  	[MGMTD_BE_CLIENT_ID_RIPNGD] = ripngd_oper_xpaths,  #endif +#ifdef HAVE_STATICD +	[MGMTD_BE_CLIENT_ID_STATICD] = staticd_oper_xpaths, +#endif  	[MGMTD_BE_CLIENT_ID_ZEBRA] = zebra_oper_xpaths,  }; diff --git a/mgmtd/mgmt_main.c b/mgmtd/mgmt_main.c index 1880d94415..7d909446c3 100644 --- a/mgmtd/mgmt_main.c +++ b/mgmtd/mgmt_main.c @@ -159,6 +159,12 @@ const struct frr_yang_module_info ietf_netconf_with_defaults_info = {   * clients into mgmtd. The modules are used by libyang in order to support   * parsing binary data returns from the backend.   */ +const struct frr_yang_module_info frr_backend_client_info = { +	.name = "frr-backend", +	.ignore_cfg_cbs = true, +	.nodes = { { .xpath = NULL } }, +}; +  const struct frr_yang_module_info zebra_route_map_info = {  	.name = "frr-zebra-route-map",  	.ignore_cfg_cbs = true, @@ -183,6 +189,7 @@ static const struct frr_yang_module_info *const mgmt_yang_modules[] = {  	/*  	 * YANG module info used by backend clients get added here.  	 */ +	&frr_backend_client_info,  	&frr_zebra_cli_info,  	&zebra_route_map_info, diff --git a/ripd/rip_main.c b/ripd/rip_main.c index 67469f5fe5..cfe4a7e437 100644 --- a/ripd/rip_main.c +++ b/ripd/rip_main.c @@ -127,6 +127,7 @@ static struct frr_signal_t ripd_signals[] = {  };  static const struct frr_yang_module_info *const ripd_yang_modules[] = { +	&frr_backend_info,  	&frr_filter_info,  	&frr_interface_info,  	&frr_ripd_info, diff --git a/ripngd/ripng_main.c b/ripngd/ripng_main.c index ada9ad4e78..b3584b9c3a 100644 --- a/ripngd/ripng_main.c +++ b/ripngd/ripng_main.c @@ -120,6 +120,7 @@ struct frr_signal_t ripng_signals[] = {  };  static const struct frr_yang_module_info *const ripngd_yang_modules[] = { +	&frr_backend_info,  	&frr_filter_info,  	&frr_interface_info,  	&frr_ripngd_info, diff --git a/staticd/static_main.c b/staticd/static_main.c index 9468a98b83..5e74326e38 100644 --- a/staticd/static_main.c +++ b/staticd/static_main.c @@ -107,6 +107,7 @@ struct frr_signal_t static_signals[] = {  };  static const struct frr_yang_module_info *const staticd_yang_modules[] = { +	&frr_backend_info,  	&frr_interface_info,  	&frr_vrf_info,  	&frr_routing_info, diff --git a/yang/frr-backend.yang b/yang/frr-backend.yang new file mode 100644 index 0000000000..7149cbb991 --- /dev/null +++ b/yang/frr-backend.yang @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: BSD-2-Clause +module frr-backend { +  yang-version 1.1; +  namespace "http://frrouting.org/yang/oper"; +  prefix frr-backend; + +  organization +    "FRRouting"; +  contact +    "FRR Users List:       <mailto:frog@lists.frrouting.org> +     FRR Development List: <mailto:dev@lists.frrouting.org>"; +  description +    "This module defines a model for FRR backend management. + +     Copyright (c) 2024, LabN Consulting, L.L.C. + +     Redistribution and use in source and binary forms, with or without +     modification, are permitted provided that the following conditions +     are met: + +     1. Redistributions of source code must retain the above copyright notice, +     this list of conditions and the following disclaimer. + +     2. Redistributions in binary form must reproduce the above copyright +     notice, this list of conditions and the following disclaimer in the +     documentation and/or other materials provided with the distribution. + +     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +     \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +     LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +     A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +     HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +     LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +     DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +     OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + +  revision 2024-12-29 { +    description "Initial revision"; +    reference "FRR source code"; +  } + +  container clients { +    config false; +    description "The backend clients"; + +    list client { +      key name; +      description "A backend client"; + +      leaf name { +        type string; +        description "Name of the backend client"; +      } + +      container state { +        description "FRR backend operational state"; + +        leaf candidate-config-version { +          type uint64; +          description "Local candidate config version."; +        } +        leaf running-config-version { +          type uint64; +          description "Local running config version."; +        } +        leaf edit-count { +          type uint64; +          description "Number of config edits handled."; +        } +        leaf avg-edit-time { +          type uint64; +          description "Average edit time in microseconds."; +        } +        leaf prep-count { +          type uint64; +          description "Number of config preps handled."; +        } +        leaf avg-prep-time { +          type uint64; +          description "Average prep time in microseconds."; +        } +        leaf apply-count { +          type uint64; +          description "Number of config applies handled."; +        } +        leaf avg-apply-time { +          type uint64; +          description "Average apply time in microseconds."; +        } +        leaf-list notify-selectors { +          type string; +          description +            "List of paths identifying which state to send change +               notifications for."; +        } +      } +    } +  } +} diff --git a/yang/subdir.am b/yang/subdir.am index 786bd0bca6..9d4bc8e78d 100644 --- a/yang/subdir.am +++ b/yang/subdir.am @@ -20,6 +20,7 @@ EXTRA_DIST += yang/embedmodel.py  # without problems, as seen in libfrr.  dist_yangmodels_DATA += yang/frr-affinity-map.yang +dist_yangmodels_DATA += yang/frr-backend.yang  dist_yangmodels_DATA += yang/frr-filter.yang  dist_yangmodels_DATA += yang/frr-module-translator.yang  dist_yangmodels_DATA += yang/frr-nexthop.yang diff --git a/zebra/main.c b/zebra/main.c index 47b4a44082..d189d1e0a0 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -287,6 +287,7 @@ struct frr_signal_t zebra_signals[] = {  /* clang-format off */  static const struct frr_yang_module_info *const zebra_yang_modules[] = { +	&frr_backend_info,  	&frr_filter_info,  	&frr_interface_info,  	&frr_route_map_info,  | 
