diff --git a/configure.ac b/configure.ac index 9213bf010512..f11b345cf6ef 100644 --- a/configure.ac +++ b/configure.ac @@ -751,8 +751,6 @@ AC_ARG_ENABLE([snmp], AS_HELP_STRING([--enable-snmp], [enable SNMP support for agentx])) AC_ARG_ENABLE([config_rollbacks], AS_HELP_STRING([--enable-config-rollbacks], [enable configuration rollbacks (requires sqlite3)])) -AC_ARG_ENABLE([confd], - AS_HELP_STRING([--enable-confd=ARG], [enable confd integration])) AC_ARG_ENABLE([sysrepo], AS_HELP_STRING([--enable-sysrepo], [enable sysrepo integration])) AC_ARG_ENABLE([grpc], @@ -2081,22 +2079,6 @@ if test "$enable_config_rollbacks" = "yes"; then ]) fi -dnl --------------- -dnl confd -dnl --------------- -if test "$enable_confd" != "" -a "$enable_confd" != "no"; then - AC_CHECK_PROG([CONFD], [confd], [confd], [/bin/false], "${enable_confd}/bin") - if test "$CONFD" = "/bin/false"; then - AC_MSG_ERROR([confd was not found on your system.])] - fi - AC_CHECK_PROG([CONFDC], [confdc], [confdc], [/bin/false], "${enable_confd}/bin") - CONFD_CFLAGS="-I${enable_confd}/include -L${enable_confd}/lib" - AC_SUBST([CONFD_CFLAGS]) - CONFD_LIBS="-lconfd" - AC_SUBST([CONFD_LIBS]) - AC_DEFINE([HAVE_CONFD], [1], [Enable confd integration]) -fi - dnl --------------- dnl sysrepo dnl --------------- @@ -2766,7 +2748,6 @@ AM_CONDITIONAL([ENABLE_BGP_VNC], [test "$enable_bgp_vnc" != "no"]) AM_CONDITIONAL([BGP_BMP], [$bgpd_bmp]) dnl northbound AM_CONDITIONAL([SQLITE3], [$SQLITE3]) -AM_CONDITIONAL([CONFD], [test "$enable_confd" != ""]) AM_CONDITIONAL([SYSREPO], [test "$enable_sysrepo" = "yes"]) AM_CONDITIONAL([GRPC], [test "$enable_grpc" = "yes"]) AM_CONDITIONAL([ZEROMQ], [test "$ZEROMQ" = "true"]) diff --git a/doc/developer/northbound/advanced-topics.rst b/doc/developer/northbound/advanced-topics.rst index edfc10bcbd32..eb756026e08a 100644 --- a/doc/developer/northbound/advanced-topics.rst +++ b/doc/developer/northbound/advanced-topics.rst @@ -12,8 +12,7 @@ Auto-generated CLI commands In order to have less code to maintain, it should be possible to write a tool that auto-generates CLI commands based on the FRR YANG models. As a matter of fact, there are already a number of NETCONF-based CLIs that do -exactly that (e.g. `Clixon `__, -ConfD’s CLI). +exactly that (e.g. `Clixon `__). The problem however is that there isn’t an exact one-to-one mapping between the existing CLI commands and the corresponding YANG nodes from @@ -27,11 +26,6 @@ command for each YANG leaf, (leaf-)list and presence-container. The ripd’s ``timers basic`` command, for instance, would become three different commands, which would be undesirable. - This Tail-f’s® - `document `__ - shows how to customize ConfD auto-generated CLI commands using YANG - annotations. - The good news is that *libyang* allows users to create plugins to implement their own YANG extensions, which can be used to implement CLI annotations. If done properly, a CLI generator can save FRR developers @@ -76,8 +70,8 @@ Example of how this feature could be provided in the CLI: ``commit confirmed [minutes <1-60>]``. The ability to do confirmed commits should also be exposed in the northbound API so that the northbound plugins can also take advantage of it (in the case of the -Sysrepo and ConfD plugins, confirmed commits are implemented externally -in the *netopeer2-server* and *confd* daemons, respectively). +Sysrepo plugin, confirmed commit is implemented externally in the +*netopeer2-server* daemon). Proposed feature: enable/disable configuration commands/sections ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/developer/northbound/architecture.rst b/doc/developer/northbound/architecture.rst index 5fd89c32f190..4e84f1d6a49d 100644 --- a/doc/developer/northbound/architecture.rst +++ b/doc/developer/northbound/architecture.rst @@ -116,7 +116,7 @@ multitude of different management APIs, all of them connected to the northbound layer of the FRR daemons. By default, only the CLI interface is compiled built-in in the FRR daemons. The other management interfaces are provided as optional plugins and need to be loaded during the daemon -initialization (e.g. *zebra -M confd*). This design makes it possible to +initialization (e.g. *zebra -M grpc*). This design makes it possible to integrate FRR with different NETCONF solutions without introducing vendor lock-in. The [[Plugins - Writing Your Own]] page explains how to write custom northbound plugins that can be tailored to all needs diff --git a/doc/developer/northbound/demos.rst b/doc/developer/northbound/demos.rst index 8a0f6ad6b252..7c5ae0c229ad 100644 --- a/doc/developer/northbound/demos.rst +++ b/doc/developer/northbound/demos.rst @@ -8,23 +8,3 @@ This short demo shows some of the capabilities of the new transactional CLI: |asciicast1| - -ConfD + NETCONF + Cisco YDK ---------------------------- - -This is a very simple demo of *ripd* being configured by a python -script. The script uses NETCONF to communicate with *ripd*, which has -the ConfD plugin loaded. The most interesting part, however, is the fact -that the python script is not using handcrafted XML payloads to -configure *ripd*. Instead, the script is using python bindings generated -using Cisco’s YANG Development Kit (YDK). - -- Script used in the demo: - https://gist.github.com/rwestphal/defa9bd1ccf216ab082d4711ae402f95 - -|asciicast2| - -.. |asciicast1| image:: https://asciinema.org/a/jL0BS5HfP2kS6N1HfgsZvfZk1.png - :target: https://asciinema.org/a/jL0BS5HfP2kS6N1HfgsZvfZk1 -.. |asciicast2| image:: https://asciinema.org/a/VfMElNxsjLcdvV7484E6ChxWv.png - :target: https://asciinema.org/a/VfMElNxsjLcdvV7484E6ChxWv diff --git a/doc/developer/northbound/links.rst b/doc/developer/northbound/links.rst index 6cec176f8b6d..e8fb3272381a 100644 --- a/doc/developer/northbound/links.rst +++ b/doc/developer/northbound/links.rst @@ -195,14 +195,6 @@ pyangbind - GitHub page: https://github.com/robshakir/pyangbind - Documentation: http://pynms.io/pyangbind/ -ConfD -^^^^^ - -- Official webpage (for ConfD Basic): - http://www.tail-f.com/confd-basic/ -- Training Videos: http://www.tail-f.com/confd-training-videos/ -- Forum: http://discuss.tail-f.com/ - Sysrepo ^^^^^^^ diff --git a/doc/developer/northbound/operational-data-rpcs-and-notifications.rst b/doc/developer/northbound/operational-data-rpcs-and-notifications.rst index 5cb70ca6f169..07f92c2ca0d8 100644 --- a/doc/developer/northbound/operational-data-rpcs-and-notifications.rst +++ b/doc/developer/northbound/operational-data-rpcs-and-notifications.rst @@ -10,10 +10,10 @@ Operational data ~~~~~~~~~~~~~~~~ Writing API-agnostic code for YANG-modeled operational data is -challenging. ConfD and Sysrepo, for instance, have completely different -APIs to fetch operational data. So how can we write API-agnostic -callbacks that can be used by both the ConfD and Sysrepo plugins, and -any other northbound client that might be written in the future? +challenging. Sysrepo, for instance, has completely different API to +fetch operational data. So how can we write API-agnostic callbacks +that can be used by both the Sysrepo plugin, and any other northbound +client that might be written in the future? As an additional requirement, the callbacks must be designed in a way that makes in-place XPath filtering possible. As an example, a @@ -94,27 +94,18 @@ in the northbound architecture: */ void *(*lookup_entry)(struct yang_list_keys *keys); -These callbacks were designed to provide maximum flexibility, and borrow -a lot of ideas from the ConfD API. Each callback does one and only one -task, they are indivisible primitives that can be combined in several -different ways to iterate over operational data. The extra flexibility -certainly has a performance cost, but it’s the price to pay if we want -to expose FRR operational data using several different management -interfaces (e.g. NETCONF via either ConfD or Sysrepo+Netopeer2). In the +These callbacks were designed to provide maximum flexibility. Each +callback does one and only one task, they are indivisible primitives +that can be combined in several different ways to iterate over operational +data. The extra flexibility certainly has a performance cost, but it’s the +price to pay if we want to expose FRR operational data using several +different management interfaces (e.g. Sysrepo+Netopeer2). In the future it might be possible to introduce optional callbacks that do things like returning multiple objects at once. They would provide enhanced performance when iterating over large lists, but their use would be limited by the northbound plugins that can be integrated with them. - NOTE: using the northbound callbacks as a base, the ConfD plugin can - provide up to 100 objects between each round trip between FRR and the - *confd* daemon. Preliminary tests showed FRR taking ~7 seconds - (asynchronously, without blocking the main pthread) to return a RIP - table containing 100k routes to a NETCONF client connected to *confd* - (JSON was used as the encoding format). Work needs to be done to find - the bottlenecks and optimize this operation. - The [[Plugins - Writing Your Own]] page explains how the northbound plugins can fetch operational data using the aforementioned northbound callbacks, and how in-place XPath filtering can be implemented. @@ -351,10 +342,10 @@ are being iterated over. If that is not done, the list entry returned by this callback can become a dangling pointer when used in another callback. -Currently the ConfD and Sysrepo plugins run only in the main pthread. -The plan in the short-term is to introduce a separate pthread only for -handling operational data, and use the main pthread only for handling -configuration changes, RPCs and notifications. +Currently the Sysrepo plugin runs only in the main pthread. The plan in the +short-term is to introduce a separate pthread only for handling operational +data, and use the main pthread only for handling configuration changes, +RPCs and notifications. RPCs and Actions ~~~~~~~~~~~~~~~~ @@ -396,8 +387,8 @@ some EXEC-level commands using YANG so that their functionality is exposed to other management interfaces other than the CLI. As an example, if the ``clear bgp`` command is modeled using a YANG RPC, and a corresponding ``rpc`` callback is written, then it should be possible to -clear BGP neighbors using NETCONF and RESTCONF with that RPC (the ConfD -and Sysrepo plugins have full support for YANG RPCs and actions). +clear BGP neighbors using NETCONF and RESTCONF with that RPC (the Sysrepo +plugin has full support for YANG RPCs and actions). Here’s an example of a very simple RPC modeled using YANG: @@ -568,8 +559,7 @@ Now sending the *authentication-failure* YANG notification should be as simple as calling the above function and provide the appropriate interface name. The notification will be processed by all northbound plugins that subscribed a callback to the ``nb_notification_send`` hook. -The ConfD and Sysrepo plugins, for instance, use this hook to relay the -notifications to the *confd*/*sysrepod* daemons, which can generate -NETCONF notifications to subscribed clients. When no northbound plugin -is loaded, ``nb_notification_send()`` doesn’t do anything and the -notifications are ignored. +The Sysrepo plugin, for instance, uses this hook to relay the notifications +to the *sysrepod* daemon, which can generate NETCONF notifications to subscribed +clients. When no northbound plugin is loaded, ``nb_notification_send()`` doesn’t +do anything and the notifications are ignored. diff --git a/doc/user/installation.rst b/doc/user/installation.rst index f07bade52c7c..d17112d8aa18 100644 --- a/doc/user/installation.rst +++ b/doc/user/installation.rst @@ -326,11 +326,6 @@ options from the list below. Build with configuration rollback support. Requires SQLite3. -.. option:: --enable-confd= - - Build the ConfD northbound plugin. Look for the libconfd libs and headers - in `dir`. - .. option:: --enable-sysrepo Build the Sysrepo northbound plugin. diff --git a/lib/lib_errors.c b/lib/lib_errors.c index a96fac9cd461..9d6c04325cc3 100644 --- a/lib/lib_errors.c +++ b/lib/lib_errors.c @@ -307,24 +307,6 @@ static struct log_ref ferr_lib_err[] = { .description = "The northbound subsystem, during initialization, has detected that a libyang plugin failed to be loaded", .suggestion = "Check if the FRR libyang plugins were installed correctly in the system", }, - { - .code = EC_LIB_CONFD_INIT, - .title = "ConfD initialization error", - .description = "Upon startup FRR failed to properly initialize and startup the ConfD northbound plugin", - .suggestion = "Check if ConfD is installed correctly in the system. Also, check if the confd daemon is running.", - }, - { - .code = EC_LIB_CONFD_DATA_CONVERT, - .title = "ConfD data conversion error", - .description = "An error has occurred while converting a ConfD data value (binary) to a string", - .suggestion = "Open an Issue with all relevant log files and restart FRR" - }, - { - .code = EC_LIB_LIBCONFD, - .title = "libconfd error", - .description = "The northbound subsystem has detected that the libconfd library returned an error", - .suggestion = "Open an Issue with all relevant log files and restart FRR" - }, { .code = EC_LIB_SYSREPO_INIT, .title = "Sysrepo initialization error", diff --git a/lib/lib_errors.h b/lib/lib_errors.h index 8cdfb166c706..9e0d539599ec 100644 --- a/lib/lib_errors.h +++ b/lib/lib_errors.h @@ -65,9 +65,6 @@ enum lib_log_refs { EC_LIB_NB_TRANSACTION_RECORD_FAILED, EC_LIB_LIBYANG, EC_LIB_LIBYANG_PLUGIN_LOAD, - EC_LIB_CONFD_INIT, - EC_LIB_CONFD_DATA_CONVERT, - EC_LIB_LIBCONFD, EC_LIB_SYSREPO_INIT, EC_LIB_SYSREPO_DATA_CONVERT, EC_LIB_LIBSYSREPO, diff --git a/lib/northbound.c b/lib/northbound.c index 487f225913cf..11dca732ba4a 100644 --- a/lib/northbound.c +++ b/lib/northbound.c @@ -2403,8 +2403,6 @@ const char *nb_client_name(enum nb_client client) switch (client) { case NB_CLIENT_CLI: return "CLI"; - case NB_CLIENT_CONFD: - return "ConfD"; case NB_CLIENT_SYSREPO: return "Sysrepo"; case NB_CLIENT_GRPC: diff --git a/lib/northbound.h b/lib/northbound.h index 5be111cf0a12..01e23c816009 100644 --- a/lib/northbound.h +++ b/lib/northbound.h @@ -621,11 +621,6 @@ struct nb_node { /* Flags. */ uint8_t flags; - -#ifdef HAVE_CONFD - /* ConfD hash value corresponding to this YANG path. */ - int confd_hash; -#endif }; /* The YANG container or list contains only config data. */ #define F_NB_NODE_CONFIG_ONLY 0x01 @@ -700,7 +695,6 @@ enum nb_error { enum nb_client { NB_CLIENT_NONE = 0, NB_CLIENT_CLI, - NB_CLIENT_CONFD, NB_CLIENT_SYSREPO, NB_CLIENT_GRPC, NB_CLIENT_PCEP, diff --git a/lib/northbound_confd.c b/lib/northbound_confd.c deleted file mode 100644 index 8503d18002c7..000000000000 --- a/lib/northbound_confd.c +++ /dev/null @@ -1,1494 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2018 NetDEF, Inc. - * Renato Westphal - */ - -#include - -#include "log.h" -#include "lib_errors.h" -#include "command.h" -#include "debug.h" -#include "libfrr.h" -#include "lib/version.h" -#include "northbound.h" - -#include -#include -#include -#include - -DEFINE_MTYPE_STATIC(LIB, CONFD, "ConfD module"); - -static struct debug nb_dbg_client_confd = {0, "Northbound client: ConfD"}; - -static struct event_loop *master; -static struct sockaddr confd_addr; -static int cdb_sub_sock, dp_ctl_sock, dp_worker_sock; -static struct event *t_cdb_sub, *t_dp_ctl, *t_dp_worker; -static struct confd_daemon_ctx *dctx; -static struct confd_notification_ctx *live_ctx; -static bool confd_connected; -static struct list *confd_spoints; -static struct nb_transaction *transaction; - -static void frr_confd_finish_cdb(void); -static void frr_confd_finish_dp(void); -static int frr_confd_finish(void); - -#define flog_err_confd(funcname) \ - flog_err(EC_LIB_LIBCONFD, "%s: %s() failed: %s (%d): %s", __func__, \ - (funcname), confd_strerror(confd_errno), confd_errno, \ - confd_lasterr()) - - -/* ------------ Utils ------------ */ - -/* Get XPath string from ConfD hashed keypath. */ -static void frr_confd_get_xpath(const confd_hkeypath_t *kp, char *xpath, - size_t len) -{ - char *p; - - confd_xpath_pp_kpath(xpath, len, 0, kp); - - /* - * Replace double quotes by single quotes (the format accepted by the - * northbound API). - */ - p = xpath; - while ((p = strchr(p, '"')) != NULL) - *p++ = '\''; -} - -/* Convert ConfD binary value to a string. */ -static int frr_confd_val2str(const char *xpath, const confd_value_t *value, - char *string, size_t string_size) -{ - struct confd_cs_node *csp; - - csp = confd_cs_node_cd(NULL, xpath); - if (!csp) { - flog_err_confd("confd_cs_node_cd"); - return -1; - } - if (confd_val2str(csp->info.type, value, string, string_size) - == CONFD_ERR) { - flog_err_confd("confd_val2str"); - return -1; - } - - return 0; -} - -/* Obtain list entry from ConfD hashed keypath. */ -static int frr_confd_hkeypath_get_list_entry(const confd_hkeypath_t *kp, - struct nb_node *nb_node, - const void **list_entry) -{ - struct nb_node *nb_node_list; - int parent_lists = 0; - int curr_list = 0; - - *list_entry = NULL; - - /* - * Count the number of YANG lists in the path, disconsidering the - * last element. - */ - nb_node_list = nb_node; - while (nb_node_list->parent_list) { - nb_node_list = nb_node_list->parent_list; - parent_lists++; - } - if (nb_node->snode->nodetype != LYS_LIST && parent_lists == 0) - return 0; - - /* Start from the beginning and move down the tree. */ - for (int i = kp->len; i >= 0; i--) { - struct yang_list_keys keys; - - /* Not a YANG list. */ - if (kp->v[i][0].type != C_BUF) - continue; - - /* Obtain list keys. */ - memset(&keys, 0, sizeof(keys)); - for (int j = 0; kp->v[i][j].type != C_NOEXISTS; j++) { - strlcpy(keys.key[keys.num], - (char *)kp->v[i][j].val.buf.ptr, - sizeof(keys.key[keys.num])); - keys.num++; - } - - /* Obtain northbound node associated to the YANG list. */ - nb_node_list = nb_node; - for (int j = curr_list; j < parent_lists; j++) - nb_node_list = nb_node_list->parent_list; - - /* Obtain list entry. */ - if (!CHECK_FLAG(nb_node_list->flags, F_NB_NODE_KEYLESS_LIST)) { - *list_entry = nb_callback_lookup_entry( - nb_node, *list_entry, &keys); - if (*list_entry == NULL) - return -1; - } else { - unsigned long ptr_ulong; - - /* Retrieve list entry from pseudo-key (string). */ - if (sscanf(keys.key[0], "%lu", &ptr_ulong) != 1) - return -1; - *list_entry = (const void *)ptr_ulong; - } - - curr_list++; - } - - return 0; -} - -/* Fill the current date and time into a confd_datetime structure. */ -static void getdatetime(struct confd_datetime *datetime) -{ - struct tm tm; - struct timeval tv; - - gettimeofday(&tv, NULL); - gmtime_r(&tv.tv_sec, &tm); - - memset(datetime, 0, sizeof(*datetime)); - datetime->year = 1900 + tm.tm_year; - datetime->month = tm.tm_mon + 1; - datetime->day = tm.tm_mday; - datetime->sec = tm.tm_sec; - datetime->micro = tv.tv_usec; - datetime->timezone = 0; - datetime->timezone_minutes = 0; - datetime->hour = tm.tm_hour; - datetime->min = tm.tm_min; -} - -/* ------------ CDB code ------------ */ - -struct cdb_iter_args { - struct nb_config *candidate; - bool error; -}; - -static enum cdb_iter_ret -frr_confd_cdb_diff_iter(confd_hkeypath_t *kp, enum cdb_iter_op cdb_op, - confd_value_t *oldv, confd_value_t *newv, void *args) -{ - char xpath[XPATH_MAXLEN]; - struct nb_node *nb_node; - enum nb_operation nb_op; - struct cdb_iter_args *iter_args = args; - char value_str[YANG_VALUE_MAXLEN]; - struct yang_data *data; - char *sb1, *sb2; - int ret; - - frr_confd_get_xpath(kp, xpath, sizeof(xpath)); - - /* - * HACK: obtain value of leaf-list elements from the XPath due to - * a bug in the ConfD API. - */ - value_str[0] = '\0'; - sb1 = strrchr(xpath, '['); - sb2 = strrchr(xpath, ']'); - if (sb1 && sb2 && !strchr(sb1, '=')) { - *sb2 = '\0'; - strlcpy(value_str, sb1 + 1, sizeof(value_str)); - *sb1 = '\0'; - } - - nb_node = nb_node_find(xpath); - if (!nb_node) { - flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, - "%s: unknown data path: %s", __func__, xpath); - iter_args->error = true; - return ITER_STOP; - } - - /* Map operation values. */ - switch (cdb_op) { - case MOP_CREATED: - nb_op = NB_OP_CREATE; - break; - case MOP_DELETED: - nb_op = NB_OP_DESTROY; - break; - case MOP_VALUE_SET: - if (nb_is_operation_allowed(nb_node, NB_OP_MODIFY)) - nb_op = NB_OP_MODIFY; - else - /* Ignore list keys modifications. */ - return ITER_RECURSE; - break; - case MOP_MOVED_AFTER: - nb_op = NB_OP_MOVE; - break; - case MOP_MODIFIED: - /* We're not interested on this. */ - return ITER_RECURSE; - default: - flog_err(EC_LIB_DEVELOPMENT, - "%s: unexpected operation %u [xpath %s]", __func__, - cdb_op, xpath); - iter_args->error = true; - return ITER_STOP; - } - - /* Convert ConfD value to a string. */ - if (nb_node->snode->nodetype != LYS_LEAFLIST && newv - && frr_confd_val2str(nb_node->xpath, newv, value_str, - sizeof(value_str)) - != 0) { - flog_err(EC_LIB_CONFD_DATA_CONVERT, - "%s: failed to convert ConfD value to a string", - __func__); - iter_args->error = true; - return ITER_STOP; - } - - /* Edit the candidate configuration. */ - data = yang_data_new(xpath, value_str); - ret = nb_candidate_edit(iter_args->candidate, nb_node, nb_op, xpath, - NULL, data); - yang_data_free(data); - if (ret != NB_OK) { - flog_warn( - EC_LIB_NB_CANDIDATE_EDIT_ERROR, - "%s: failed to edit candidate configuration: operation [%s] xpath [%s]", - __func__, nb_operation_name(nb_op), xpath); - iter_args->error = true; - return ITER_STOP; - } - - return ITER_RECURSE; -} - -static void frr_confd_cdb_read_cb_prepare(int fd, int *subp, int reslen) -{ - struct nb_context context = {}; - struct nb_config *candidate; - struct cdb_iter_args iter_args; - char errmsg[BUFSIZ] = {0}; - int ret; - - candidate = nb_config_dup(running_config); - - /* Iterate over all configuration changes. */ - iter_args.candidate = candidate; - iter_args.error = false; - for (int i = 0; i < reslen; i++) { - if (cdb_diff_iterate(fd, subp[i], frr_confd_cdb_diff_iter, - ITER_WANT_PREV, &iter_args) - != CONFD_OK) { - flog_err_confd("cdb_diff_iterate"); - } - } - free(subp); - - if (iter_args.error) { - nb_config_free(candidate); - - if (cdb_sub_abort_trans( - cdb_sub_sock, CONFD_ERRCODE_APPLICATION_INTERNAL, 0, - 0, "Couldn't apply configuration changes") - != CONFD_OK) { - flog_err_confd("cdb_sub_abort_trans"); - return; - } - return; - } - - /* - * Validate the configuration changes and allocate all resources - * required to apply them. - */ - transaction = NULL; - context.client = NB_CLIENT_CONFD; - ret = nb_candidate_commit_prepare(context, candidate, NULL, - &transaction, false, false, errmsg, - sizeof(errmsg)); - if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) { - enum confd_errcode errcode; - - switch (ret) { - case NB_ERR_LOCKED: - errcode = CONFD_ERRCODE_IN_USE; - break; - case NB_ERR_RESOURCE: - errcode = CONFD_ERRCODE_RESOURCE_DENIED; - break; - default: - errcode = CONFD_ERRCODE_APPLICATION; - break; - } - - /* Reject the configuration changes. */ - if (cdb_sub_abort_trans(cdb_sub_sock, errcode, 0, 0, "%s", - errmsg) - != CONFD_OK) { - flog_err_confd("cdb_sub_abort_trans"); - return; - } - } else { - /* Acknowledge the notification. */ - if (cdb_sync_subscription_socket(fd, CDB_DONE_PRIORITY) - != CONFD_OK) { - flog_err_confd("cdb_sync_subscription_socket"); - return; - } - - /* No configuration changes. */ - if (!transaction) - nb_config_free(candidate); - } -} - -static void frr_confd_cdb_read_cb_commit(int fd, int *subp, int reslen) -{ - /* - * No need to process the configuration changes again as we're already - * keeping track of them in the "transaction" variable. - */ - free(subp); - - /* Apply the transaction. */ - if (transaction) { - struct nb_config *candidate = transaction->config; - char errmsg[BUFSIZ] = {0}; - - nb_candidate_commit_apply(transaction, true, NULL, errmsg, - sizeof(errmsg)); - nb_config_free(candidate); - } - - /* Acknowledge the notification. */ - if (cdb_sync_subscription_socket(fd, CDB_DONE_PRIORITY) != CONFD_OK) { - flog_err_confd("cdb_sync_subscription_socket"); - return; - } -} - -static int frr_confd_cdb_read_cb_abort(int fd, int *subp, int reslen) -{ - /* - * No need to process the configuration changes again as we're already - * keeping track of them in the "transaction" variable. - */ - free(subp); - - /* Abort the transaction. */ - if (transaction) { - struct nb_config *candidate = transaction->config; - char errmsg[BUFSIZ] = {0}; - - nb_candidate_commit_abort(transaction, errmsg, sizeof(errmsg)); - nb_config_free(candidate); - } - - /* Acknowledge the notification. */ - if (cdb_sync_subscription_socket(fd, CDB_DONE_PRIORITY) != CONFD_OK) { - flog_err_confd("cdb_sync_subscription_socket"); - return -1; - } - - return 0; -} - -static void frr_confd_cdb_read_cb(struct event *thread) -{ - int fd = EVENT_FD(thread); - enum cdb_sub_notification cdb_ev; - int flags; - int *subp = NULL; - int reslen = 0; - - event_add_read(master, frr_confd_cdb_read_cb, NULL, fd, &t_cdb_sub); - - if (cdb_read_subscription_socket2(fd, &cdb_ev, &flags, &subp, &reslen) - != CONFD_OK) { - flog_err_confd("cdb_read_subscription_socket2"); - return; - } - - switch (cdb_ev) { - case CDB_SUB_PREPARE: - frr_confd_cdb_read_cb_prepare(fd, subp, reslen); - break; - case CDB_SUB_COMMIT: - frr_confd_cdb_read_cb_commit(fd, subp, reslen); - break; - case CDB_SUB_ABORT: - frr_confd_cdb_read_cb_abort(fd, subp, reslen); - break; - default: - flog_err_confd("unknown CDB event"); - break; - } -} - -/* Trigger CDB subscriptions to read the startup configuration. */ -static void *thread_cdb_trigger_subscriptions(void *data) -{ - int sock; - int *sub_points = NULL, len = 0; - struct listnode *node; - int *spoint; - int i = 0; - - /* Create CDB data socket. */ - sock = socket(PF_INET, SOCK_STREAM, 0); - if (sock < 0) { - flog_err(EC_LIB_SOCKET, "%s: failed to create socket: %s", - __func__, safe_strerror(errno)); - return NULL; - } - - if (cdb_connect(sock, CDB_DATA_SOCKET, &confd_addr, - sizeof(struct sockaddr_in)) - != CONFD_OK) { - flog_err_confd("cdb_connect"); - return NULL; - } - - /* - * Fill array containing the subscription point of all loaded YANG - * modules. - */ - len = listcount(confd_spoints); - sub_points = XCALLOC(MTYPE_CONFD, len * sizeof(int)); - for (ALL_LIST_ELEMENTS_RO(confd_spoints, node, spoint)) - sub_points[i++] = *spoint; - - if (cdb_trigger_subscriptions(sock, sub_points, len) != CONFD_OK) { - flog_err_confd("cdb_trigger_subscriptions"); - return NULL; - } - - /* Cleanup and exit thread. */ - XFREE(MTYPE_CONFD, sub_points); - cdb_close(sock); - - return NULL; -} - -static int frr_confd_subscribe(const struct lysc_node *snode, void *arg) -{ - struct yang_module *module = arg; - struct nb_node *nb_node; - int *spoint; - int ret; - - switch (snode->nodetype) { - case LYS_CONTAINER: - case LYS_LEAF: - case LYS_LEAFLIST: - case LYS_LIST: - break; - default: - return YANG_ITER_CONTINUE; - } - - if (CHECK_FLAG(snode->flags, LYS_CONFIG_R)) - return YANG_ITER_CONTINUE; - - nb_node = snode->priv; - if (!nb_node) - return YANG_ITER_CONTINUE; - - DEBUGD(&nb_dbg_client_confd, "%s: subscribing to '%s'", __func__, - nb_node->xpath); - - spoint = XMALLOC(MTYPE_CONFD, sizeof(*spoint)); - ret = cdb_subscribe2(cdb_sub_sock, CDB_SUB_RUNNING_TWOPHASE, - CDB_SUB_WANT_ABORT_ON_ABORT, 3, spoint, - module->confd_hash, nb_node->xpath); - if (ret != CONFD_OK) { - flog_err_confd("cdb_subscribe2"); - XFREE(MTYPE_CONFD, spoint); - return YANG_ITER_CONTINUE; - } - - listnode_add(confd_spoints, spoint); - return YANG_ITER_CONTINUE; -} - -static int frr_confd_init_cdb(void) -{ - struct yang_module *module; - pthread_t cdb_trigger_thread; - - /* Create CDB subscription socket. */ - cdb_sub_sock = socket(PF_INET, SOCK_STREAM, 0); - if (cdb_sub_sock < 0) { - flog_err(EC_LIB_SOCKET, "%s: failed to create socket: %s", - __func__, safe_strerror(errno)); - return -1; - } - - if (cdb_connect(cdb_sub_sock, CDB_SUBSCRIPTION_SOCKET, &confd_addr, - sizeof(struct sockaddr_in)) - != CONFD_OK) { - flog_err_confd("cdb_connect"); - goto error; - } - - /* Subscribe to all loaded YANG data modules. */ - confd_spoints = list_new(); - RB_FOREACH (module, yang_modules, &yang_modules) { - module->confd_hash = confd_str2hash(module->info->ns); - if (module->confd_hash == 0) { - flog_err( - EC_LIB_LIBCONFD, - "%s: failed to find hash value for namespace %s", - __func__, module->info->ns); - goto error; - } - - /* - * The CDB API doesn't provide a mechanism to subscribe to an - * entire YANG module. So we have to find the top level - * nodes ourselves and subscribe to their paths. - */ - yang_snodes_iterate(module->info, frr_confd_subscribe, 0, - module); - } - - if (cdb_subscribe_done(cdb_sub_sock) != CONFD_OK) { - flog_err_confd("cdb_subscribe_done"); - goto error; - } - - /* Create short lived pthread to trigger the CDB subscriptions. */ - if (pthread_create(&cdb_trigger_thread, NULL, - thread_cdb_trigger_subscriptions, NULL)) { - flog_err(EC_LIB_SYSTEM_CALL, "%s: error creating pthread: %s", - __func__, safe_strerror(errno)); - goto error; - } - pthread_detach(cdb_trigger_thread); - - event_add_read(master, frr_confd_cdb_read_cb, NULL, cdb_sub_sock, - &t_cdb_sub); - - return 0; - -error: - frr_confd_finish_cdb(); - - return -1; -} - -static void frr_confd_finish_cdb(void) -{ - if (cdb_sub_sock > 0) { - EVENT_OFF(t_cdb_sub); - cdb_close(cdb_sub_sock); - } -} - -/* ------------ DP code ------------ */ - -static int frr_confd_transaction_init(struct confd_trans_ctx *tctx) -{ - confd_trans_set_fd(tctx, dp_worker_sock); - - return CONFD_OK; -} - -#define CONFD_MAX_CHILD_NODES 32 - -static int frr_confd_data_get_elem(struct confd_trans_ctx *tctx, - confd_hkeypath_t *kp) -{ - struct nb_node *nb_node; - char xpath[XPATH_MAXLEN]; - struct yang_data *data; - confd_value_t v; - const void *list_entry = NULL; - - frr_confd_get_xpath(kp, xpath, sizeof(xpath)); - - nb_node = nb_node_find(xpath); - if (!nb_node) { - flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, - "%s: unknown data path: %s", __func__, xpath); - confd_data_reply_not_found(tctx); - return CONFD_OK; - } - - if (frr_confd_hkeypath_get_list_entry(kp, nb_node, &list_entry) != 0) { - confd_data_reply_not_found(tctx); - return CONFD_OK; - } - - data = nb_callback_get_elem(nb_node, xpath, list_entry); - if (data) { - if (data->value) { - CONFD_SET_STR(&v, data->value); - confd_data_reply_value(tctx, &v); - } else - confd_data_reply_found(tctx); - yang_data_free(data); - } else - confd_data_reply_not_found(tctx); - - return CONFD_OK; -} - -static int frr_confd_data_get_next(struct confd_trans_ctx *tctx, - confd_hkeypath_t *kp, long next) -{ - struct nb_node *nb_node; - char xpath[XPATH_MAXLEN]; - struct yang_data *data; - const void *parent_list_entry, *nb_next; - confd_value_t v[LIST_MAXKEYS]; - - frr_confd_get_xpath(kp, xpath, sizeof(xpath)); - - nb_node = nb_node_find(xpath); - if (!nb_node) { - flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, - "%s: unknown data path: %s", __func__, xpath); - confd_data_reply_next_key(tctx, NULL, -1, -1); - return CONFD_OK; - } - - if (frr_confd_hkeypath_get_list_entry(kp, nb_node, &parent_list_entry) - != 0) { - /* List entry doesn't exist anymore. */ - confd_data_reply_next_key(tctx, NULL, -1, -1); - return CONFD_OK; - } - - nb_next = nb_callback_get_next(nb_node, parent_list_entry, - (next == -1) ? NULL : (void *)next); - if (!nb_next) { - /* End of the list or leaf-list. */ - confd_data_reply_next_key(tctx, NULL, -1, -1); - return CONFD_OK; - } - - switch (nb_node->snode->nodetype) { - case LYS_LIST: - if (!CHECK_FLAG(nb_node->flags, F_NB_NODE_KEYLESS_LIST)) { - struct yang_list_keys keys; - - memset(&keys, 0, sizeof(keys)); - if (nb_callback_get_keys(nb_node, nb_next, &keys) - != NB_OK) { - flog_warn(EC_LIB_NB_CB_STATE, - "%s: failed to get list keys", - __func__); - confd_data_reply_next_key(tctx, NULL, -1, -1); - return CONFD_OK; - } - - /* Feed keys to ConfD. */ - for (size_t i = 0; i < keys.num; i++) - CONFD_SET_STR(&v[i], keys.key[i]); - confd_data_reply_next_key(tctx, v, keys.num, - (long)nb_next); - } else { - char pointer_str[32]; - - /* - * ConfD 6.6 user guide, chapter 6.11 (Operational data - * lists without keys): - * "To support this without having completely separate - * APIs, we use a "pseudo" key in the ConfD APIs for - * this type of list. This key is not part of the data - * model, and completely hidden in the northbound agent - * interfaces, but is used with e.g. the get_next() and - * get_elem() callbacks as if it were a normal key. This - * "pseudo" key is always a single signed 64-bit - * integer, i.e. the confd_value_t type is C_INT64. The - * values can be chosen arbitrarily by the application, - * as long as a key value returned by get_next() can be - * used to get the data for the corresponding list entry - * with get_elem() or get_object() as usual. It could - * e.g. be an index into an array that holds the data, - * or even a memory address in integer form". - * - * Since we're using the CONFD_DAEMON_FLAG_STRINGSONLY - * option, we must convert our pseudo-key (a void - * pointer) to a string before sending it to confd. - */ - snprintf(pointer_str, sizeof(pointer_str), "%lu", - (unsigned long)nb_next); - CONFD_SET_STR(&v[0], pointer_str); - confd_data_reply_next_key(tctx, v, 1, (long)nb_next); - } - break; - case LYS_LEAFLIST: - data = nb_callback_get_elem(nb_node, xpath, nb_next); - if (data) { - if (data->value) { - CONFD_SET_STR(&v[0], data->value); - confd_data_reply_next_key(tctx, v, 1, - (long)nb_next); - } - yang_data_free(data); - } else - confd_data_reply_next_key(tctx, NULL, -1, -1); - break; - default: - break; - } - - return CONFD_OK; -} - -/* - * Optional callback - implemented for performance reasons. - */ -static int frr_confd_data_get_object(struct confd_trans_ctx *tctx, - confd_hkeypath_t *kp) -{ - struct nb_node *nb_node; - const struct lysc_node *child; - char xpath[XPATH_MAXLEN]; - char xpath_child[XPATH_MAXLEN * 2]; - struct list *elements; - struct yang_data *data; - const void *list_entry; - confd_value_t values[CONFD_MAX_CHILD_NODES]; - size_t nvalues = 0; - - frr_confd_get_xpath(kp, xpath, sizeof(xpath)); - - nb_node = nb_node_find(xpath); - if (!nb_node) { - flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, - "%s: unknown data path: %s", __func__, xpath); - confd_data_reply_not_found(tctx); - return CONFD_ERR; - } - - if (frr_confd_hkeypath_get_list_entry(kp, nb_node, &list_entry) != 0) { - confd_data_reply_not_found(tctx); - return CONFD_OK; - } - - elements = yang_data_list_new(); - - /* Loop through list child nodes. */ - LY_LIST_FOR (lysc_node_child(nb_node->snode), child) { - struct nb_node *nb_node_child = child->priv; - confd_value_t *v; - - if (nvalues > CONFD_MAX_CHILD_NODES) - break; - - v = &values[nvalues++]; - - /* Non-presence containers, lists and leaf-lists. */ - if (!nb_node_child->cbs.get_elem) { - CONFD_SET_NOEXISTS(v); - continue; - } - - snprintf(xpath_child, sizeof(xpath_child), "%s/%s", xpath, - child->name); - data = nb_callback_get_elem(nb_node_child, xpath_child, - list_entry); - if (data) { - if (data->value) - CONFD_SET_STR(v, data->value); - else { - /* Presence containers and empty leafs. */ - CONFD_SET_XMLTAG( - v, nb_node_child->confd_hash, - confd_str2hash(nb_node_child->snode - ->module->ns)); - } - listnode_add(elements, data); - } else - CONFD_SET_NOEXISTS(v); - } - - confd_data_reply_value_array(tctx, values, nvalues); - - /* Release memory. */ - list_delete(&elements); - - return CONFD_OK; -} - -/* - * Optional callback - implemented for performance reasons. - */ -static int frr_confd_data_get_next_object(struct confd_trans_ctx *tctx, - confd_hkeypath_t *kp, long next) -{ - char xpath[XPATH_MAXLEN]; - struct nb_node *nb_node; - struct list *elements; - const void *parent_list_entry; - const void *nb_next; -#define CONFD_OBJECTS_PER_TIME 100 - struct confd_next_object objects[CONFD_OBJECTS_PER_TIME + 1]; - char pseudo_keys[CONFD_OBJECTS_PER_TIME][32]; - int nobjects = 0; - - frr_confd_get_xpath(kp, xpath, sizeof(xpath)); - - nb_node = nb_node_find(xpath); - if (!nb_node) { - flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, - "%s: unknown data path: %s", __func__, xpath); - confd_data_reply_next_object_array(tctx, NULL, 0, 0); - return CONFD_OK; - } - - if (frr_confd_hkeypath_get_list_entry(kp, nb_node, &parent_list_entry) - != 0) { - confd_data_reply_next_object_array(tctx, NULL, 0, 0); - return CONFD_OK; - } - - elements = yang_data_list_new(); - nb_next = (next == -1) ? NULL : (void *)next; - - memset(objects, 0, sizeof(objects)); - for (int j = 0; j < CONFD_OBJECTS_PER_TIME; j++) { - struct confd_next_object *object; - const struct lysc_node *child; - struct yang_data *data; - size_t nvalues = 0; - - object = &objects[j]; - - nb_next = nb_callback_get_next(nb_node, parent_list_entry, - nb_next); - if (!nb_next) - /* End of the list. */ - break; - - object->next = (long)nb_next; - - /* Leaf-lists require special handling. */ - if (nb_node->snode->nodetype == LYS_LEAFLIST) { - object->v = XMALLOC(MTYPE_CONFD, sizeof(confd_value_t)); - data = nb_callback_get_elem(nb_node, xpath, nb_next); - assert(data && data->value); - CONFD_SET_STR(object->v, data->value); - nvalues++; - listnode_add(elements, data); - goto next; - } - - object->v = - XMALLOC(MTYPE_CONFD, - CONFD_MAX_CHILD_NODES * sizeof(confd_value_t)); - - /* - * ConfD 6.6 user guide, chapter 6.11 (Operational data lists - * without keys): - * "In the response to the get_next_object() callback, the data - * provider is expected to provide the key values along with the - * other leafs in an array that is populated according to the - * data model. This must be done also for this type of list, - * even though the key isn't actually in the data model. The - * "pseudo" key must always be the first element in the array". - */ - if (CHECK_FLAG(nb_node->flags, F_NB_NODE_KEYLESS_LIST)) { - confd_value_t *v; - - snprintf(pseudo_keys[j], sizeof(pseudo_keys[j]), "%lu", - (unsigned long)nb_next); - - v = &object->v[nvalues++]; - CONFD_SET_STR(v, pseudo_keys[j]); - } - - /* Loop through list child nodes. */ - LY_LIST_FOR (lysc_node_child(nb_node->snode), child) { - struct nb_node *nb_node_child = child->priv; - char xpath_child[XPATH_MAXLEN * 2]; - confd_value_t *v; - - if (nvalues > CONFD_MAX_CHILD_NODES) - break; - - v = &object->v[nvalues++]; - - /* Non-presence containers, lists and leaf-lists. */ - if (!nb_node_child->cbs.get_elem) { - CONFD_SET_NOEXISTS(v); - continue; - } - - snprintf(xpath_child, sizeof(xpath_child), "%s/%s", - xpath, child->name); - data = nb_callback_get_elem(nb_node_child, xpath_child, - nb_next); - if (data) { - if (data->value) - CONFD_SET_STR(v, data->value); - else { - /* - * Presence containers and empty leafs. - */ - CONFD_SET_XMLTAG( - v, nb_node_child->confd_hash, - confd_str2hash( - nb_node_child->snode - ->module->ns)); - } - listnode_add(elements, data); - } else - CONFD_SET_NOEXISTS(v); - } - next: - object->n = nvalues; - nobjects++; - } - - if (nobjects == 0) { - confd_data_reply_next_object_array(tctx, NULL, 0, 0); - list_delete(&elements); - return CONFD_OK; - } - - /* Detect end of the list. */ - if (!nb_next) { - nobjects++; - objects[nobjects].v = NULL; - } - - /* Reply to ConfD. */ - confd_data_reply_next_object_arrays(tctx, objects, nobjects, 0); - if (!nb_next) - nobjects--; - - /* Release memory. */ - list_delete(&elements); - for (int j = 0; j < nobjects; j++) { - struct confd_next_object *object; - - object = &objects[j]; - XFREE(MTYPE_CONFD, object->v); - } - - return CONFD_OK; -} - -static int frr_confd_notification_send(const char *xpath, - struct list *arguments) -{ - struct nb_node *nb_node; - struct yang_module *module; - struct confd_datetime now; - confd_tag_value_t *values; - int nvalues; - int i = 0; - struct yang_data *data; - struct listnode *node; - int ret; - - nb_node = nb_node_find(xpath); - if (!nb_node) { - flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, - "%s: unknown data path: %s", __func__, xpath); - return -1; - } - module = yang_module_find(nb_node->snode->module->name); - assert(module); - - nvalues = 2; - if (arguments) - nvalues += listcount(arguments); - - values = XMALLOC(MTYPE_CONFD, nvalues * sizeof(*values)); - - CONFD_SET_TAG_XMLBEGIN(&values[i++], nb_node->confd_hash, - module->confd_hash); - for (ALL_LIST_ELEMENTS_RO(arguments, node, data)) { - struct nb_node *nb_node_arg; - - nb_node_arg = nb_node_find(data->xpath); - if (!nb_node_arg) { - flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, - "%s: unknown data path: %s", __func__, - data->xpath); - XFREE(MTYPE_CONFD, values); - return NB_ERR; - } - - CONFD_SET_TAG_STR(&values[i++], nb_node_arg->confd_hash, - data->value); - } - CONFD_SET_TAG_XMLEND(&values[i++], nb_node->confd_hash, - module->confd_hash); - - getdatetime(&now); - ret = confd_notification_send(live_ctx, &now, values, nvalues); - - /* Release memory. */ - XFREE(MTYPE_CONFD, values); - - /* Map ConfD return code to northbound return code. */ - switch (ret) { - case CONFD_OK: - return NB_OK; - default: - return NB_ERR; - } -} - -static int frr_confd_action_init(struct confd_user_info *uinfo) -{ - confd_action_set_fd(uinfo, dp_worker_sock); - - return CONFD_OK; -} - -static int frr_confd_action_execute(struct confd_user_info *uinfo, - struct xml_tag *name, confd_hkeypath_t *kp, - confd_tag_value_t *params, int nparams) -{ - char xpath[XPATH_MAXLEN]; - struct nb_node *nb_node; - struct list *input; - struct list *output; - struct yang_data *data; - confd_tag_value_t *reply; - int ret = CONFD_OK; - char errmsg[BUFSIZ] = {0}; - - /* Getting the XPath is tricky. */ - if (kp) { - /* This is a YANG RPC. */ - frr_confd_get_xpath(kp, xpath, sizeof(xpath)); - strlcat(xpath, "/", sizeof(xpath)); - strlcat(xpath, confd_hash2str(name->tag), sizeof(xpath)); - } else { - /* This is a YANG action. */ - snprintf(xpath, sizeof(xpath), "/%s:%s", - confd_ns2prefix(name->ns), confd_hash2str(name->tag)); - } - - nb_node = nb_node_find(xpath); - if (!nb_node) { - flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, - "%s: unknown data path: %s", __func__, xpath); - return CONFD_ERR; - } - - input = yang_data_list_new(); - output = yang_data_list_new(); - - /* Process input nodes. */ - for (int i = 0; i < nparams; i++) { - char xpath_input[XPATH_MAXLEN * 2]; - char value_str[YANG_VALUE_MAXLEN]; - - snprintf(xpath_input, sizeof(xpath_input), "%s/%s", xpath, - confd_hash2str(params[i].tag.tag)); - - if (frr_confd_val2str(xpath_input, ¶ms[i].v, value_str, - sizeof(value_str)) - != 0) { - flog_err( - EC_LIB_CONFD_DATA_CONVERT, - "%s: failed to convert ConfD value to a string", - __func__); - ret = CONFD_ERR; - goto exit; - } - - data = yang_data_new(xpath_input, value_str); - listnode_add(input, data); - } - - /* Execute callback registered for this XPath. */ - if (nb_callback_rpc(nb_node, xpath, input, output, errmsg, - sizeof(errmsg)) - != NB_OK) { - flog_warn(EC_LIB_NB_CB_RPC, "%s: rpc callback failed: %s", - __func__, xpath); - ret = CONFD_ERR; - goto exit; - } - - /* Process output nodes. */ - if (listcount(output) > 0) { - struct listnode *node; - int i = 0; - - reply = XMALLOC(MTYPE_CONFD, - listcount(output) * sizeof(*reply)); - - for (ALL_LIST_ELEMENTS_RO(output, node, data)) { - struct nb_node *nb_node_output; - int hash; - - nb_node_output = nb_node_find(data->xpath); - if (!nb_node_output) { - flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, - "%s: unknown data path: %s", __func__, - data->xpath); - goto exit; - } - - hash = confd_str2hash(nb_node_output->snode->name); - CONFD_SET_TAG_STR(&reply[i++], hash, data->value); - } - confd_action_reply_values(uinfo, reply, listcount(output)); - XFREE(MTYPE_CONFD, reply); - } - -exit: - /* Release memory. */ - list_delete(&input); - list_delete(&output); - - return ret; -} - - -static int frr_confd_dp_read(struct confd_daemon_ctx *dctx, int fd) -{ - int ret; - - ret = confd_fd_ready(dctx, fd); - if (ret == CONFD_EOF) { - flog_err_confd("confd_fd_ready"); - frr_confd_finish(); - return -1; - } else if (ret == CONFD_ERR && confd_errno != CONFD_ERR_EXTERNAL) { - flog_err_confd("confd_fd_ready"); - frr_confd_finish(); - return -1; - } - - return 0; -} - -static void frr_confd_dp_ctl_read(struct event *thread) -{ - struct confd_daemon_ctx *dctx = EVENT_ARG(thread); - int fd = EVENT_FD(thread); - - event_add_read(master, frr_confd_dp_ctl_read, dctx, fd, &t_dp_ctl); - - frr_confd_dp_read(dctx, fd); -} - -static void frr_confd_dp_worker_read(struct event *thread) -{ - struct confd_daemon_ctx *dctx = EVENT_ARG(thread); - int fd = EVENT_FD(thread); - - event_add_read(master, frr_confd_dp_worker_read, dctx, fd, - &t_dp_worker); - - frr_confd_dp_read(dctx, fd); -} - -static int frr_confd_subscribe_state(const struct lysc_node *snode, void *arg) -{ - struct nb_node *nb_node = snode->priv; - struct confd_data_cbs *data_cbs = arg; - - if (!nb_node || !CHECK_FLAG(snode->flags, LYS_CONFIG_R)) - return YANG_ITER_CONTINUE; - /* We only need to subscribe to the root of the state subtrees. */ - if (snode->parent && CHECK_FLAG(snode->parent->flags, LYS_CONFIG_R)) - return YANG_ITER_CONTINUE; - - DEBUGD(&nb_dbg_client_confd, - "%s: providing data to '%s' (callpoint %s)", __func__, - nb_node->xpath, snode->name); - - strlcpy(data_cbs->callpoint, snode->name, sizeof(data_cbs->callpoint)); - if (confd_register_data_cb(dctx, data_cbs) != CONFD_OK) - flog_err_confd("confd_register_data_cb"); - - return YANG_ITER_CONTINUE; -} - -static int frr_confd_init_dp(const char *program_name) -{ - struct confd_trans_cbs trans_cbs; - struct confd_data_cbs data_cbs; - struct confd_notification_stream_cbs ncbs; - struct confd_action_cbs acbs; - - /* Initialize daemon context. */ - dctx = confd_init_daemon(program_name); - if (!dctx) { - flog_err_confd("confd_init_daemon"); - goto error; - } - - /* - * Inform we want to receive YANG values as raw strings, and that we - * want to provide only strings in the reply functions, regardless of - * the YANG type. - */ - confd_set_daemon_flags(dctx, CONFD_DAEMON_FLAG_STRINGSONLY); - - /* Establish a control socket. */ - dp_ctl_sock = socket(PF_INET, SOCK_STREAM, 0); - if (dp_ctl_sock < 0) { - flog_err(EC_LIB_SOCKET, "%s: failed to create socket: %s", - __func__, safe_strerror(errno)); - goto error; - } - - if (confd_connect(dctx, dp_ctl_sock, CONTROL_SOCKET, &confd_addr, - sizeof(struct sockaddr_in)) - != CONFD_OK) { - flog_err_confd("confd_connect"); - goto error; - } - - /* - * Establish a worker socket (only one since this plugin runs on a - * single thread). - */ - dp_worker_sock = socket(PF_INET, SOCK_STREAM, 0); - if (dp_worker_sock < 0) { - flog_err(EC_LIB_SOCKET, "%s: failed to create socket: %s", - __func__, safe_strerror(errno)); - goto error; - } - if (confd_connect(dctx, dp_worker_sock, WORKER_SOCKET, &confd_addr, - sizeof(struct sockaddr_in)) - != CONFD_OK) { - flog_err_confd("confd_connect"); - goto error; - } - - /* Register transaction callback functions. */ - memset(&trans_cbs, 0, sizeof(trans_cbs)); - trans_cbs.init = frr_confd_transaction_init; - confd_register_trans_cb(dctx, &trans_cbs); - - /* Register our read/write callbacks. */ - memset(&data_cbs, 0, sizeof(data_cbs)); - data_cbs.get_elem = frr_confd_data_get_elem; - data_cbs.exists_optional = frr_confd_data_get_elem; - data_cbs.get_next = frr_confd_data_get_next; - data_cbs.get_object = frr_confd_data_get_object; - data_cbs.get_next_object = frr_confd_data_get_next_object; - - /* - * Iterate over all loaded YANG modules and subscribe to the paths - * referent to state data. - */ - yang_snodes_iterate(NULL, frr_confd_subscribe_state, 0, &data_cbs); - - /* Register notification stream. */ - memset(&ncbs, 0, sizeof(ncbs)); - ncbs.fd = dp_worker_sock; - /* - * RFC 5277 - Section 3.2.3: - * A NETCONF server implementation supporting the notification - * capability MUST support the "NETCONF" notification event - * stream. This stream contains all NETCONF XML event notifications - * supported by the NETCONF server. - */ - strlcpy(ncbs.streamname, "NETCONF", sizeof(ncbs.streamname)); - if (confd_register_notification_stream(dctx, &ncbs, &live_ctx) - != CONFD_OK) { - flog_err_confd("confd_register_notification_stream"); - goto error; - } - - /* Register the action handler callback. */ - memset(&acbs, 0, sizeof(acbs)); - strlcpy(acbs.actionpoint, "actionpoint", sizeof(acbs.actionpoint)); - acbs.init = frr_confd_action_init; - acbs.action = frr_confd_action_execute; - if (confd_register_action_cbs(dctx, &acbs) != CONFD_OK) { - flog_err_confd("confd_register_action_cbs"); - goto error; - } - - /* Notify we registered all callbacks we wanted. */ - if (confd_register_done(dctx) != CONFD_OK) { - flog_err_confd("confd_register_done"); - goto error; - } - - event_add_read(master, frr_confd_dp_ctl_read, dctx, dp_ctl_sock, - &t_dp_ctl); - event_add_read(master, frr_confd_dp_worker_read, dctx, dp_worker_sock, - &t_dp_worker); - - return 0; - -error: - frr_confd_finish_dp(); - - return -1; -} - -static void frr_confd_finish_dp(void) -{ - if (dp_worker_sock > 0) { - EVENT_OFF(t_dp_worker); - close(dp_worker_sock); - } - if (dp_ctl_sock > 0) { - EVENT_OFF(t_dp_ctl); - close(dp_ctl_sock); - } - if (dctx != NULL) - confd_release_daemon(dctx); -} - -/* ------------ CLI ------------ */ - -DEFUN (debug_nb_confd, - debug_nb_confd_cmd, - "[no] debug northbound client confd", - NO_STR - DEBUG_STR - "Northbound debugging\n" - "Client\n" - "ConfD\n") -{ - uint32_t mode = DEBUG_NODE2MODE(vty->node); - bool no = strmatch(argv[0]->text, "no"); - - DEBUG_MODE_SET(&nb_dbg_client_confd, mode, !no); - - return CMD_SUCCESS; -} - -static int frr_confd_debug_config_write(struct vty *vty) -{ - if (DEBUG_MODE_CHECK(&nb_dbg_client_confd, DEBUG_MODE_CONF)) - vty_out(vty, "debug northbound client confd\n"); - - return 0; -} - -static int frr_confd_debug_set_all(uint32_t flags, bool set) -{ - DEBUG_FLAGS_SET(&nb_dbg_client_confd, flags, set); - - /* If all modes have been turned off, don't preserve options. */ - if (!DEBUG_MODE_CHECK(&nb_dbg_client_confd, DEBUG_MODE_ALL)) - DEBUG_CLEAR(&nb_dbg_client_confd); - - return 0; -} - -static void frr_confd_cli_init(void) -{ - hook_register(nb_client_debug_config_write, - frr_confd_debug_config_write); - hook_register(nb_client_debug_set_all, frr_confd_debug_set_all); - - install_element(ENABLE_NODE, &debug_nb_confd_cmd); - install_element(CONFIG_NODE, &debug_nb_confd_cmd); -} - -/* ------------ Main ------------ */ - -static int frr_confd_calculate_snode_hash(const struct lysc_node *snode, - void *arg) -{ - struct nb_node *nb_node = snode->priv; - - if (nb_node) - nb_node->confd_hash = confd_str2hash(snode->name); - - return YANG_ITER_CONTINUE; -} - -static int frr_confd_init(const char *program_name) -{ - struct sockaddr_in *confd_addr4 = (struct sockaddr_in *)&confd_addr; - int debuglevel = CONFD_SILENT; - int ret = -1; - - /* Initialize ConfD library. */ - confd_init(program_name, stderr, debuglevel); - - confd_addr4->sin_family = AF_INET; - confd_addr4->sin_addr.s_addr = inet_addr("127.0.0.1"); - confd_addr4->sin_port = htons(CONFD_PORT); - if (confd_load_schemas(&confd_addr, sizeof(struct sockaddr_in)) - != CONFD_OK) { - flog_err_confd("confd_load_schemas"); - return -1; - } - - ret = frr_confd_init_cdb(); - if (ret != 0) - goto error; - - ret = frr_confd_init_dp(program_name); - if (ret != 0) { - frr_confd_finish_cdb(); - goto error; - } - - yang_snodes_iterate(NULL, frr_confd_calculate_snode_hash, 0, NULL); - - hook_register(nb_notification_send, frr_confd_notification_send); - - confd_connected = true; - return 0; - -error: - confd_free_schemas(); - - return ret; -} - -static int frr_confd_finish(void) -{ - if (!confd_connected) - return 0; - - frr_confd_finish_cdb(); - frr_confd_finish_dp(); - - confd_free_schemas(); - - confd_connected = false; - - return 0; -} - -static int frr_confd_module_late_init(struct event_loop *tm) -{ - master = tm; - - if (frr_confd_init(frr_get_progname()) < 0) { - flog_err(EC_LIB_CONFD_INIT, - "failed to initialize the ConfD module"); - return -1; - } - - hook_register(frr_fini, frr_confd_finish); - frr_confd_cli_init(); - - return 0; -} - -static int frr_confd_module_init(void) -{ - hook_register(frr_late_init, frr_confd_module_late_init); - - return 0; -} - -FRR_MODULE_SETUP(.name = "frr_confd", .version = FRR_VERSION, - .description = "FRR ConfD integration module", - .init = frr_confd_module_init, -); diff --git a/lib/subdir.am b/lib/subdir.am index c621ad06584e..5ec6adf4c0a0 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -409,18 +409,6 @@ lib_libfrrzmq_la_SOURCES = \ lib/frr_zmq.c \ #end -# -# Tail-f's ConfD support -# -if CONFD -module_LTLIBRARIES += lib/confd.la -endif - -lib_confd_la_CFLAGS = $(AM_CFLAGS) $(CONFD_CFLAGS) -lib_confd_la_LDFLAGS = $(MODULE_LDFLAGS) -lib_confd_la_LIBADD = lib/libfrr.la $(CONFD_LIBS) -lib_confd_la_SOURCES = lib/northbound_confd.c - # # Sysrepo support # diff --git a/lib/yang.h b/lib/yang.h index 65f6a73e0b50..85be38bf81ba 100644 --- a/lib/yang.h +++ b/lib/yang.h @@ -45,9 +45,6 @@ struct yang_module { RB_ENTRY(yang_module) entry; const char *name; const struct lys_module *info; -#ifdef HAVE_CONFD - int confd_hash; -#endif #ifdef HAVE_SYSREPO sr_subscription_ctx_t *sr_subscription; struct event *sr_thread; diff --git a/yang/confd/confd.frr-ripd.yang b/yang/confd/confd.frr-ripd.yang deleted file mode 100644 index 7bbe54cca9db..000000000000 --- a/yang/confd/confd.frr-ripd.yang +++ /dev/null @@ -1,24 +0,0 @@ -module confd.frr-ripd { - namespace "urn:dummy"; - prefix "dummy"; - - import tailf-common { - prefix tailf; - } - import frr-ripd { - prefix frr-ripd; - } - - tailf:annotate-module "frr-ripd" { - tailf:annotate-statement "container[name='ripd']" { - tailf:annotate-statement "list[name='instance']" { - tailf:annotate-statement "container[name='state']" { - tailf:callpoint "state"; - } - } - } - tailf:annotate-statement "rpc[name='clear-rip-route']" { - tailf:actionpoint "actionpoint"; - } - } -} diff --git a/yang/confd/confd.frr-ripngd.yang b/yang/confd/confd.frr-ripngd.yang deleted file mode 100644 index 83383fb4544b..000000000000 --- a/yang/confd/confd.frr-ripngd.yang +++ /dev/null @@ -1,24 +0,0 @@ -module confd.frr-ripngd { - namespace "urn:dummy"; - prefix "dummy"; - - import tailf-common { - prefix tailf; - } - import frr-ripngd { - prefix frr-ripngd; - } - - tailf:annotate-module "frr-ripngd" { - tailf:annotate-statement "container[name='ripngd']" { - tailf:annotate-statement "list[name='instance']" { - tailf:annotate-statement "container[name='state']" { - tailf:callpoint "state"; - } - } - } - tailf:annotate-statement "rpc[name='clear-ripng-route']" { - tailf:actionpoint "actionpoint"; - } - } -} diff --git a/yang/subdir.am b/yang/subdir.am index 6745d537125a..71aa04087858 100644 --- a/yang/subdir.am +++ b/yang/subdir.am @@ -106,31 +106,4 @@ endif CLEANFILES += \ yang/*.c \ yang/ietf/*.c \ - yang/confd/*.c \ # - -if CONFD - -SUBMODULES = $(shell cd $(top_srcdir); grep -l belongs-to $(dist_yangmodels_DATA)) -EXCLUDED_MODULES = $(SUBMODULES) yang/frr-module-translator.yang -YANG_MODULES = $(filter-out $(EXCLUDED_MODULES),$(dist_yangmodels_DATA)) - -fxsdir = $(sysconfdir)/confd -fxs_DATA = $(YANG_MODULES:.yang=.fxs) - -SUFFIXES += .fxs -CLEANFILES += $(fxs_DATA) - -AM_V_CONFDC = $(AM_V_CONFDC_@AM_V@) -AM_V_CONFDC_ = $(AM_V_CONFDC_@AM_DEFAULT_V@) -AM_V_CONFDC_0 = @echo " CONFDC " $@; - -CONFDC_FLAGS = --yangpath $(srcdir)/yang --yangpath $(srcdir)/yang/ietf - -yang/%.fxs: yang/%.yang yang/confd/confd.%.yang - $(AM_V_CONFDC)$(CONFDC) $(CONFDC_FLAGS) -c -o $@ -a $(srcdir)/yang/confd/confd.$*.yang -- $< - -yang/%.fxs: yang/%.yang - $(AM_V_CONFDC)$(CONFDC) $(CONFDC_FLAGS) -c -o $@ -- $< - -endif