summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDonald Sharp <donaldsharp72@gmail.com>2025-01-15 16:22:11 +0100
committerGitHub <noreply@github.com>2025-01-15 16:22:11 +0100
commit953d5fd526a4ec184d8a9a1fca01fc9528b58e71 (patch)
treeac972ad56a273eba12ff181c9c7794cad5a8ee1f
parentMerge pull request #17776 from nabahr/group-rpf-mode (diff)
parentlib: northbound/mgmtd: add backend model support (diff)
downloadfrr-953d5fd526a4ec184d8a9a1fca01fc9528b58e71.tar.xz
frr-953d5fd526a4ec184d8a9a1fca01fc9528b58e71.zip
Merge pull request #17799 from LabNConsulting/chopps/backend-yang-model
mgmtd backend yang model (depends on #17796)
-rw-r--r--lib/mgmt_be_client.c211
-rw-r--r--lib/mgmt_be_client.h2
-rw-r--r--lib/northbound.h10
-rw-r--r--lib/northbound_oper.c81
-rw-r--r--lib/subdir.am1
-rw-r--r--mgmtd/mgmt_be_adapter.c11
-rw-r--r--mgmtd/mgmt_main.c7
-rw-r--r--ripd/rip_main.c1
-rw-r--r--ripngd/ripng_main.c1
-rw-r--r--staticd/static_main.c1
-rw-r--r--yang/frr-backend.yang102
-rw-r--r--yang/subdir.am1
-rw-r--r--zebra/main.c1
13 files changed, 416 insertions, 14 deletions
diff --git a/lib/mgmt_be_client.c b/lib/mgmt_be_client.c
index efd5d70a7..3a07a1d2d 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 a3e3896d5..5e78f0f43 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 bd802e088..ce59bfd01 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 a296b147a..6336db502 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 984e92ff1..a975eb2fc 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 e159d68ec..1c32f936b 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 1880d9441..7d909446c 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 67469f5fe..cfe4a7e43 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 ada9ad4e7..b3584b9c3 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 9468a98b8..5e74326e3 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 000000000..7149cbb99
--- /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 786bd0bca..9d4bc8e78 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 47b4a4408..d189d1e0a 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,