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;
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. */
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);
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;
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;
}
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,
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;
--- /dev/null
+// 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.";
+ }
+ }
+ }
+ }
+}