#include "log.h"
#include "lib_errors.h"
#include "command.h"
+#include "debug.h"
#include "libfrr.h"
#include "version.h"
#include "northbound.h"
DEFINE_MTYPE_STATIC(LIB, CONFD, "ConfD module")
+static struct debug nb_dbg_client_confd = {0, "Northbound client: ConfD"};
+
static struct thread_master *master;
static struct sockaddr confd_addr;
static int cdb_sub_sock, dp_ctl_sock, dp_worker_sock;
nb_node_list = nb_node_list->parent_list;
/* Obtain list entry. */
- *list_entry =
- nb_node_list->cbs.lookup_entry(*list_entry, &keys);
- if (*list_entry == NULL)
- return -1;
+ 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++;
}
nb_op = NB_OP_CREATE;
break;
case MOP_DELETED:
- nb_op = NB_OP_DELETE;
+ nb_op = NB_OP_DESTROY;
break;
case MOP_VALUE_SET:
if (nb_operation_is_valid(NB_OP_MODIFY, nb_node->snode))
*/
transaction = NULL;
ret = nb_candidate_commit_prepare(candidate, NB_CLIENT_CONFD, NULL,
- &transaction);
+ NULL, &transaction);
if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) {
enum confd_errcode errcode;
const char *errmsg;
continue;
nb_node = snode->priv;
- if (debug_northbound)
- zlog_debug("%s: subscribing to '%s'", __func__,
- nb_node->xpath);
+ DEBUGD(&nb_dbg_client_confd, "%s: subscribing to '%s'",
+ __func__, nb_node->xpath);
spoint = XMALLOC(MTYPE_CONFD, sizeof(*spoint));
ret = cdb_subscribe2(
return CONFD_OK;
}
- data = nb_node->cbs.get_elem(xpath, list_entry);
+ data = nb_callback_get_elem(nb_node, xpath, list_entry);
if (data) {
if (data->value) {
CONFD_SET_STR(&v, data->value);
{
struct nb_node *nb_node;
char xpath[BUFSIZ];
- struct yang_list_keys keys;
struct yang_data *data;
const void *parent_list_entry, *nb_next;
confd_value_t v[LIST_MAXKEYS];
return CONFD_OK;
}
- nb_next = nb_node->cbs.get_next(parent_list_entry,
- (next == -1) ? NULL : (void *)next);
+ 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);
switch (nb_node->snode->nodetype) {
case LYS_LIST:
- memset(&keys, 0, sizeof(keys));
- if (nb_node->cbs.get_keys(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;
- }
+ 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);
+ /* 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[16];
+
+ /*
+ * 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_node->cbs.get_elem(xpath, nb_next);
+ data = nb_callback_get_elem(nb_node, xpath, nb_next);
if (data) {
if (data->value) {
CONFD_SET_STR(&v[0], data->value);
snprintf(xpath_child, sizeof(xpath_child), "%s/%s", xpath,
child->name);
- data = nb_node_child->cbs.get_elem(xpath_child, list_entry);
+ data = nb_callback_get_elem(nb_node_child, xpath_child,
+ list_entry);
if (data) {
if (data->value)
CONFD_SET_STR(v, data->value);
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][16];
int nobjects = 0;
frr_confd_get_xpath(kp, xpath, sizeof(xpath));
object = &objects[j];
- nb_next = nb_node->cbs.get_next(parent_list_entry, nb_next);
+ nb_next = nb_callback_get_next(nb_node, parent_list_entry,
+ nb_next);
if (!nb_next)
/* End of the list. */
break;
/* Leaf-lists require special handling. */
if (nb_node->snode->nodetype == LYS_LEAFLIST) {
object->v = XMALLOC(MTYPE_CONFD, sizeof(confd_value_t));
- data = nb_node->cbs.get_elem(xpath, nb_next);
+ data = nb_callback_get_elem(nb_node, xpath, nb_next);
assert(data && data->value);
CONFD_SET_STR(object->v, data->value);
nvalues++;
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_TREE_FOR (nb_node->snode->child, child) {
struct nb_node *nb_node_child = child->priv;
snprintf(xpath_child, sizeof(xpath_child), "%s/%s",
xpath, child->name);
- data = nb_node_child->cbs.get_elem(xpath_child,
- nb_next);
+ data = nb_callback_get_elem(nb_node_child, xpath_child,
+ nb_next);
if (data) {
if (data->value)
CONFD_SET_STR(v, data->value);
}
/* Execute callback registered for this XPath. */
- if (nb_node->cbs.rpc(xpath, input, output) != NB_OK) {
+ if (nb_callback_rpc(nb_node, xpath, input, output) != NB_OK) {
flog_warn(EC_LIB_NB_CB_RPC, "%s: rpc callback failed: %s",
__func__, xpath);
ret = CONFD_ERR;
if (snode->parent && CHECK_FLAG(snode->parent->flags, LYS_CONFIG_R))
return YANG_ITER_CONTINUE;
- if (debug_northbound)
- zlog_debug("%s: providing data to '%s' (callpoint %s)",
- __func__, nb_node->xpath, snode->name);
+ 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)
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 lys_node *snode,
static int frr_confd_finish(void)
{
- if (confd_connected == false)
+ if (!confd_connected)
return 0;
frr_confd_finish_cdb();
}
hook_register(frr_fini, frr_confd_finish);
+ frr_confd_cli_init();
return 0;
}