]> git.proxmox.com Git - mirror_frr.git/blame - lib/northbound.c
lib: don't initialize the northbound database in the unit tests
[mirror_frr.git] / lib / northbound.c
CommitLineData
1c2facd1
RW
1/*
2 * Copyright (C) 2018 NetDEF, Inc.
3 * Renato Westphal
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include <zebra.h>
21
22#include "libfrr.h"
23#include "log.h"
24#include "lib_errors.h"
25#include "command.h"
9eb2c0a1 26#include "debug.h"
1c2facd1
RW
27#include "db.h"
28#include "northbound.h"
29#include "northbound_cli.h"
30#include "northbound_db.h"
31
32DEFINE_MTYPE_STATIC(LIB, NB_NODE, "Northbound Node")
33DEFINE_MTYPE_STATIC(LIB, NB_CONFIG, "Northbound Configuration")
34
35/* Running configuration - shouldn't be modified directly. */
36struct nb_config *running_config;
37
38/*
39 * Global lock used to prevent multiple configuration transactions from
40 * happening concurrently.
41 */
42static bool transaction_in_progress;
43
9eb2c0a1 44static int nb_callback_configuration(const enum nb_event event,
1c2facd1
RW
45 struct nb_config_change *change);
46static struct nb_transaction *nb_transaction_new(struct nb_config *config,
47 struct nb_config_cbs *changes,
48 enum nb_client client,
49 const char *comment);
50static void nb_transaction_free(struct nb_transaction *transaction);
51static int nb_transaction_process(enum nb_event event,
52 struct nb_transaction *transaction);
53static void nb_transaction_apply_finish(struct nb_transaction *transaction);
1a4bc045
RW
54static int nb_oper_data_iter_node(const struct lys_node *snode,
55 const char *xpath, const void *list_entry,
56 const struct yang_list_keys *list_keys,
57 struct yang_translator *translator,
58 bool first, uint32_t flags,
59 nb_oper_data_cb cb, void *arg);
1c2facd1 60
544ca69a
RW
61static int nb_node_check_config_only(const struct lys_node *snode, void *arg)
62{
63 bool *config_only = arg;
64
65 if (CHECK_FLAG(snode->flags, LYS_CONFIG_R)) {
66 *config_only = false;
67 return YANG_ITER_STOP;
68 }
69
70 return YANG_ITER_CONTINUE;
71}
72
e0ccfad2 73static int nb_node_new_cb(const struct lys_node *snode, void *arg)
1c2facd1
RW
74{
75 struct nb_node *nb_node;
76 struct lys_node *sparent, *sparent_list;
77
78 nb_node = XCALLOC(MTYPE_NB_NODE, sizeof(*nb_node));
79 yang_snode_get_path(snode, YANG_PATH_DATA, nb_node->xpath,
80 sizeof(nb_node->xpath));
81 nb_node->priority = NB_DFLT_PRIORITY;
82 sparent = yang_snode_real_parent(snode);
83 if (sparent)
84 nb_node->parent = sparent->priv;
85 sparent_list = yang_snode_parent_list(snode);
86 if (sparent_list)
87 nb_node->parent_list = sparent_list->priv;
88
544ca69a
RW
89 /* Set flags. */
90 if (CHECK_FLAG(snode->nodetype, LYS_CONTAINER | LYS_LIST)) {
91 bool config_only = true;
92
93 yang_snodes_iterate_subtree(snode, nb_node_check_config_only,
94 YANG_ITER_ALLOW_AUGMENTATIONS,
95 &config_only);
96 if (config_only)
97 SET_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY);
98 }
99fb518f
RW
99 if (CHECK_FLAG(snode->nodetype, LYS_LIST)) {
100 struct lys_node_list *slist;
101
102 slist = (struct lys_node_list *)snode;
103 if (slist->keys_size == 0)
104 SET_FLAG(nb_node->flags, F_NB_NODE_KEYLESS_LIST);
105 }
544ca69a 106
1c2facd1
RW
107 /*
108 * Link the northbound node and the libyang schema node with one
109 * another.
110 */
111 nb_node->snode = snode;
112 lys_set_private(snode, nb_node);
e0ccfad2
RW
113
114 return YANG_ITER_CONTINUE;
1c2facd1
RW
115}
116
e0ccfad2 117static int nb_node_del_cb(const struct lys_node *snode, void *arg)
1c2facd1
RW
118{
119 struct nb_node *nb_node;
120
121 nb_node = snode->priv;
122 lys_set_private(snode, NULL);
123 XFREE(MTYPE_NB_NODE, nb_node);
e0ccfad2
RW
124
125 return YANG_ITER_CONTINUE;
1c2facd1
RW
126}
127
544ca69a
RW
128void nb_nodes_create(void)
129{
130 yang_snodes_iterate_all(nb_node_new_cb, 0, NULL);
131}
132
133void nb_nodes_delete(void)
134{
135 yang_snodes_iterate_all(nb_node_del_cb, 0, NULL);
136}
137
1c2facd1
RW
138struct nb_node *nb_node_find(const char *xpath)
139{
140 const struct lys_node *snode;
141
142 /*
143 * Use libyang to find the schema node associated to the xpath and get
144 * the northbound node from there (snode private pointer).
145 */
146 snode = ly_ctx_get_node(ly_native_ctx, NULL, xpath, 0);
147 if (!snode)
148 return NULL;
149
150 return snode->priv;
151}
152
153static int nb_node_validate_cb(const struct nb_node *nb_node,
154 enum nb_operation operation,
155 int callback_implemented, bool optional)
156{
157 bool valid;
158
159 valid = nb_operation_is_valid(operation, nb_node->snode);
160
161 if (!valid && callback_implemented)
162 flog_warn(EC_LIB_NB_CB_UNNEEDED,
163 "unneeded '%s' callback for '%s'",
164 nb_operation_name(operation), nb_node->xpath);
165
166 if (!optional && valid && !callback_implemented) {
167 flog_err(EC_LIB_NB_CB_MISSING, "missing '%s' callback for '%s'",
168 nb_operation_name(operation), nb_node->xpath);
169 return 1;
170 }
171
172 return 0;
173}
174
175/*
176 * Check if the required callbacks were implemented for the given northbound
177 * node.
178 */
179static unsigned int nb_node_validate_cbs(const struct nb_node *nb_node)
180
181{
182 unsigned int error = 0;
183
184 error += nb_node_validate_cb(nb_node, NB_OP_CREATE,
185 !!nb_node->cbs.create, false);
186 error += nb_node_validate_cb(nb_node, NB_OP_MODIFY,
187 !!nb_node->cbs.modify, false);
95ce849b 188 error += nb_node_validate_cb(nb_node, NB_OP_DESTROY,
d01b92fd 189 !!nb_node->cbs.destroy, false);
1c2facd1
RW
190 error += nb_node_validate_cb(nb_node, NB_OP_MOVE, !!nb_node->cbs.move,
191 false);
192 error += nb_node_validate_cb(nb_node, NB_OP_APPLY_FINISH,
193 !!nb_node->cbs.apply_finish, true);
194 error += nb_node_validate_cb(nb_node, NB_OP_GET_ELEM,
195 !!nb_node->cbs.get_elem, false);
196 error += nb_node_validate_cb(nb_node, NB_OP_GET_NEXT,
197 !!nb_node->cbs.get_next, false);
198 error += nb_node_validate_cb(nb_node, NB_OP_GET_KEYS,
199 !!nb_node->cbs.get_keys, false);
200 error += nb_node_validate_cb(nb_node, NB_OP_LOOKUP_ENTRY,
201 !!nb_node->cbs.lookup_entry, false);
202 error += nb_node_validate_cb(nb_node, NB_OP_RPC, !!nb_node->cbs.rpc,
203 false);
204
205 return error;
206}
207
208static unsigned int nb_node_validate_priority(const struct nb_node *nb_node)
209{
210 /* Top-level nodes can have any priority. */
211 if (!nb_node->parent)
212 return 0;
213
214 if (nb_node->priority < nb_node->parent->priority) {
215 flog_err(EC_LIB_NB_CB_INVALID_PRIO,
216 "node has higher priority than its parent [xpath %s]",
217 nb_node->xpath);
218 return 1;
219 }
220
221 return 0;
222}
223
e0ccfad2 224static int nb_node_validate(const struct lys_node *snode, void *arg)
1c2facd1
RW
225{
226 struct nb_node *nb_node = snode->priv;
e0ccfad2 227 unsigned int *errors = arg;
1c2facd1
RW
228
229 /* Validate callbacks and priority. */
230 *errors += nb_node_validate_cbs(nb_node);
231 *errors += nb_node_validate_priority(nb_node);
e0ccfad2
RW
232
233 return YANG_ITER_CONTINUE;
1c2facd1
RW
234}
235
236struct nb_config *nb_config_new(struct lyd_node *dnode)
237{
238 struct nb_config *config;
239
240 config = XCALLOC(MTYPE_NB_CONFIG, sizeof(*config));
241 if (dnode)
242 config->dnode = dnode;
243 else
5e02643a 244 config->dnode = yang_dnode_new(ly_native_ctx, true);
1c2facd1
RW
245 config->version = 0;
246
247 return config;
248}
249
250void nb_config_free(struct nb_config *config)
251{
252 if (config->dnode)
253 yang_dnode_free(config->dnode);
254 XFREE(MTYPE_NB_CONFIG, config);
255}
256
257struct nb_config *nb_config_dup(const struct nb_config *config)
258{
259 struct nb_config *dup;
260
261 dup = XCALLOC(MTYPE_NB_CONFIG, sizeof(*dup));
262 dup->dnode = yang_dnode_dup(config->dnode);
263 dup->version = config->version;
264
265 return dup;
266}
267
268int nb_config_merge(struct nb_config *config_dst, struct nb_config *config_src,
269 bool preserve_source)
270{
271 int ret;
272
273 ret = lyd_merge(config_dst->dnode, config_src->dnode, LYD_OPT_EXPLICIT);
274 if (ret != 0)
275 flog_warn(EC_LIB_LIBYANG, "%s: lyd_merge() failed", __func__);
276
277 if (!preserve_source)
278 nb_config_free(config_src);
279
280 return (ret == 0) ? NB_OK : NB_ERR;
281}
282
283void nb_config_replace(struct nb_config *config_dst,
284 struct nb_config *config_src, bool preserve_source)
285{
286 /* Update version. */
287 if (config_src->version != 0)
288 config_dst->version = config_src->version;
289
290 /* Update dnode. */
e5dc8a44
RW
291 if (config_dst->dnode)
292 yang_dnode_free(config_dst->dnode);
1c2facd1
RW
293 if (preserve_source) {
294 config_dst->dnode = yang_dnode_dup(config_src->dnode);
295 } else {
296 config_dst->dnode = config_src->dnode;
297 config_src->dnode = NULL;
298 nb_config_free(config_src);
299 }
300}
301
302/* Generate the nb_config_cbs tree. */
303static inline int nb_config_cb_compare(const struct nb_config_cb *a,
304 const struct nb_config_cb *b)
305{
306 /* Sort by priority first. */
307 if (a->nb_node->priority < b->nb_node->priority)
308 return -1;
309 if (a->nb_node->priority > b->nb_node->priority)
310 return 1;
311
312 /*
313 * Use XPath as a tie-breaker. This will naturally sort parent nodes
314 * before their children.
315 */
316 return strcmp(a->xpath, b->xpath);
317}
318RB_GENERATE(nb_config_cbs, nb_config_cb, entry, nb_config_cb_compare);
319
320static void nb_config_diff_add_change(struct nb_config_cbs *changes,
321 enum nb_operation operation,
322 const struct lyd_node *dnode)
323{
324 struct nb_config_change *change;
325
326 change = XCALLOC(MTYPE_TMP, sizeof(*change));
327 change->cb.operation = operation;
328 change->cb.nb_node = dnode->schema->priv;
329 yang_dnode_get_path(dnode, change->cb.xpath, sizeof(change->cb.xpath));
330 change->cb.dnode = dnode;
331
332 RB_INSERT(nb_config_cbs, changes, &change->cb);
333}
334
335static void nb_config_diff_del_changes(struct nb_config_cbs *changes)
336{
337 while (!RB_EMPTY(nb_config_cbs, changes)) {
338 struct nb_config_change *change;
339
340 change = (struct nb_config_change *)RB_ROOT(nb_config_cbs,
341 changes);
342 RB_REMOVE(nb_config_cbs, changes, &change->cb);
343 XFREE(MTYPE_TMP, change);
344 }
345}
346
347/*
348 * Helper function used when calculating the delta between two different
349 * configurations. Given a new subtree, calculate all new YANG data nodes,
350 * excluding default leafs and leaf-lists. This is a recursive function.
351 */
cacbffaf
RW
352static void nb_config_diff_created(const struct lyd_node *dnode,
353 struct nb_config_cbs *changes)
1c2facd1 354{
cacbffaf 355 enum nb_operation operation;
1c2facd1
RW
356 struct lyd_node *child;
357
cacbffaf
RW
358 switch (dnode->schema->nodetype) {
359 case LYS_LEAF:
360 case LYS_LEAFLIST:
361 if (lyd_wd_default((struct lyd_node_leaf_list *)dnode))
362 break;
1c2facd1 363
cacbffaf
RW
364 if (nb_operation_is_valid(NB_OP_CREATE, dnode->schema))
365 operation = NB_OP_CREATE;
366 else if (nb_operation_is_valid(NB_OP_MODIFY, dnode->schema))
367 operation = NB_OP_MODIFY;
368 else
369 return;
1c2facd1 370
cacbffaf
RW
371 nb_config_diff_add_change(changes, operation, dnode);
372 break;
373 case LYS_CONTAINER:
374 case LYS_LIST:
375 if (nb_operation_is_valid(NB_OP_CREATE, dnode->schema))
376 nb_config_diff_add_change(changes, NB_OP_CREATE, dnode);
377
378 /* Process child nodes recursively. */
379 LY_TREE_FOR (dnode->child, child) {
380 nb_config_diff_created(child, changes);
1c2facd1 381 }
cacbffaf
RW
382 break;
383 default:
384 break;
1c2facd1
RW
385 }
386}
387
1912caa2
RW
388static void nb_config_diff_deleted(const struct lyd_node *dnode,
389 struct nb_config_cbs *changes)
390{
391 if (nb_operation_is_valid(NB_OP_DESTROY, dnode->schema))
392 nb_config_diff_add_change(changes, NB_OP_DESTROY, dnode);
393 else if (CHECK_FLAG(dnode->schema->nodetype, LYS_CONTAINER)) {
394 struct lyd_node *child;
395
396 /*
397 * Non-presence containers need special handling since they
398 * don't have "destroy" callbacks. In this case, what we need to
399 * do is to call the "destroy" callbacks of their child nodes
400 * when applicable (i.e. optional nodes).
401 */
402 LY_TREE_FOR (dnode->child, child) {
403 nb_config_diff_deleted(child, changes);
404 }
405 }
406}
407
1c2facd1
RW
408/* Calculate the delta between two different configurations. */
409static void nb_config_diff(const struct nb_config *config1,
410 const struct nb_config *config2,
411 struct nb_config_cbs *changes)
412{
413 struct lyd_difflist *diff;
414
415 diff = lyd_diff(config1->dnode, config2->dnode,
416 LYD_DIFFOPT_WITHDEFAULTS);
417 assert(diff);
418
419 for (int i = 0; diff->type[i] != LYD_DIFF_END; i++) {
420 LYD_DIFFTYPE type;
421 struct lyd_node *dnode;
1c2facd1
RW
422
423 type = diff->type[i];
424
425 switch (type) {
426 case LYD_DIFF_CREATED:
427 dnode = diff->second[i];
cacbffaf 428 nb_config_diff_created(dnode, changes);
1c2facd1
RW
429 break;
430 case LYD_DIFF_DELETED:
431 dnode = diff->first[i];
1912caa2 432 nb_config_diff_deleted(dnode, changes);
1c2facd1
RW
433 break;
434 case LYD_DIFF_CHANGED:
435 dnode = diff->second[i];
cacbffaf 436 nb_config_diff_add_change(changes, NB_OP_MODIFY, dnode);
1c2facd1
RW
437 break;
438 case LYD_DIFF_MOVEDAFTER1:
439 case LYD_DIFF_MOVEDAFTER2:
440 default:
441 continue;
442 }
1c2facd1
RW
443 }
444
445 lyd_free_diff(diff);
446}
447
448int nb_candidate_edit(struct nb_config *candidate,
449 const struct nb_node *nb_node,
450 enum nb_operation operation, const char *xpath,
451 const struct yang_data *previous,
452 const struct yang_data *data)
453{
454 struct lyd_node *dnode;
455 char xpath_edit[XPATH_MAXLEN];
456
457 if (!nb_operation_is_valid(operation, nb_node->snode)) {
458 flog_warn(EC_LIB_NB_CANDIDATE_EDIT_ERROR,
459 "%s: %s operation not valid for %s", __func__,
460 nb_operation_name(operation), xpath);
461 return NB_ERR;
462 }
463
464 /* Use special notation for leaf-lists (RFC 6020, section 9.13.5). */
465 if (nb_node->snode->nodetype == LYS_LEAFLIST)
466 snprintf(xpath_edit, sizeof(xpath_edit), "%s[.='%s']", xpath,
467 data->value);
468 else
469 strlcpy(xpath_edit, xpath, sizeof(xpath_edit));
470
471 switch (operation) {
472 case NB_OP_CREATE:
473 case NB_OP_MODIFY:
474 ly_errno = 0;
475 dnode = lyd_new_path(candidate->dnode, ly_native_ctx,
476 xpath_edit, (void *)data->value, 0,
477 LYD_PATH_OPT_UPDATE);
478 if (!dnode && ly_errno) {
479 flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed",
480 __func__);
481 return NB_ERR;
482 }
483
484 /*
485 * If a new node was created, call lyd_validate() only to create
486 * default child nodes.
487 */
488 if (dnode) {
489 lyd_schema_sort(dnode, 0);
490 lyd_validate(&dnode, LYD_OPT_CONFIG, ly_native_ctx);
491 }
492 break;
95ce849b 493 case NB_OP_DESTROY:
1c2facd1
RW
494 dnode = yang_dnode_get(candidate->dnode, xpath_edit);
495 if (!dnode)
496 /*
497 * Return a special error code so the caller can choose
498 * whether to ignore it or not.
499 */
500 return NB_ERR_NOT_FOUND;
501 lyd_free(dnode);
502 break;
503 case NB_OP_MOVE:
504 /* TODO: update configuration. */
505 break;
506 default:
507 flog_warn(EC_LIB_DEVELOPMENT,
508 "%s: unknown operation (%u) [xpath %s]", __func__,
509 operation, xpath_edit);
510 return NB_ERR;
511 }
512
513 return NB_OK;
514}
515
516bool nb_candidate_needs_update(const struct nb_config *candidate)
517{
518 if (candidate->version < running_config->version)
519 return true;
520
521 return false;
522}
523
524int nb_candidate_update(struct nb_config *candidate)
525{
526 struct nb_config *updated_config;
527
528 updated_config = nb_config_dup(running_config);
529 if (nb_config_merge(updated_config, candidate, true) != NB_OK)
530 return NB_ERR;
531
532 nb_config_replace(candidate, updated_config, false);
533
534 return NB_OK;
535}
536
537/*
538 * The northbound configuration callbacks use the 'priv' pointer present in the
539 * libyang lyd_node structure to store pointers to FRR internal variables
540 * associated to YANG lists and presence containers. Before commiting a
541 * candidate configuration, we must restore the 'priv' pointers stored in the
542 * running configuration since they might be lost while editing the candidate.
543 */
544static void nb_candidate_restore_priv_pointers(struct nb_config *candidate)
545{
546 struct lyd_node *root, *next, *dnode_iter;
547
548 LY_TREE_FOR (running_config->dnode, root) {
549 LY_TREE_DFS_BEGIN (root, next, dnode_iter) {
550 struct lyd_node *dnode_candidate;
551 char xpath[XPATH_MAXLEN];
552
553 if (!dnode_iter->priv)
554 goto next;
555
556 yang_dnode_get_path(dnode_iter, xpath, sizeof(xpath));
557 dnode_candidate =
558 yang_dnode_get(candidate->dnode, xpath);
559 if (dnode_candidate)
560 yang_dnode_set_entry(dnode_candidate,
561 dnode_iter->priv);
562
563 next:
564 LY_TREE_DFS_END(root, next, dnode_iter);
565 }
566 }
567}
568
569/*
570 * Perform YANG syntactic and semantic validation.
571 *
572 * WARNING: lyd_validate() can change the configuration as part of the
573 * validation process.
574 */
575static int nb_candidate_validate_yang(struct nb_config *candidate)
576{
577 if (lyd_validate(&candidate->dnode, LYD_OPT_STRICT | LYD_OPT_CONFIG,
578 ly_native_ctx)
579 != 0)
580 return NB_ERR_VALIDATION;
581
582 return NB_OK;
583}
584
585/* Perform code-level validation using the northbound callbacks. */
586static int nb_candidate_validate_changes(struct nb_config *candidate,
587 struct nb_config_cbs *changes)
588{
589 struct nb_config_cb *cb;
590
591 nb_candidate_restore_priv_pointers(candidate);
592 RB_FOREACH (cb, nb_config_cbs, changes) {
593 struct nb_config_change *change = (struct nb_config_change *)cb;
594 int ret;
595
9eb2c0a1 596 ret = nb_callback_configuration(NB_EV_VALIDATE, change);
1c2facd1
RW
597 if (ret != NB_OK)
598 return NB_ERR_VALIDATION;
599 }
600
601 return NB_OK;
602}
603
604int nb_candidate_validate(struct nb_config *candidate)
605{
606 struct nb_config_cbs changes;
607 int ret;
608
609 if (nb_candidate_validate_yang(candidate) != NB_OK)
610 return NB_ERR_VALIDATION;
611
612 RB_INIT(nb_config_cbs, &changes);
613 nb_config_diff(running_config, candidate, &changes);
614 ret = nb_candidate_validate_changes(candidate, &changes);
615 nb_config_diff_del_changes(&changes);
616
617 return ret;
618}
619
620int nb_candidate_commit_prepare(struct nb_config *candidate,
621 enum nb_client client, const char *comment,
622 struct nb_transaction **transaction)
623{
624 struct nb_config_cbs changes;
625
626 if (nb_candidate_validate_yang(candidate) != NB_OK) {
627 flog_warn(EC_LIB_NB_CANDIDATE_INVALID,
628 "%s: failed to validate candidate configuration",
629 __func__);
630 return NB_ERR_VALIDATION;
631 }
632
633 RB_INIT(nb_config_cbs, &changes);
634 nb_config_diff(running_config, candidate, &changes);
635 if (RB_EMPTY(nb_config_cbs, &changes))
636 return NB_ERR_NO_CHANGES;
637
638 if (nb_candidate_validate_changes(candidate, &changes) != NB_OK) {
639 flog_warn(EC_LIB_NB_CANDIDATE_INVALID,
640 "%s: failed to validate candidate configuration",
641 __func__);
642 nb_config_diff_del_changes(&changes);
643 return NB_ERR_VALIDATION;
644 }
645
646 *transaction = nb_transaction_new(candidate, &changes, client, comment);
647 if (*transaction == NULL) {
648 flog_warn(EC_LIB_NB_TRANSACTION_CREATION_FAILED,
649 "%s: failed to create transaction", __func__);
650 nb_config_diff_del_changes(&changes);
651 return NB_ERR_LOCKED;
652 }
653
654 return nb_transaction_process(NB_EV_PREPARE, *transaction);
655}
656
657void nb_candidate_commit_abort(struct nb_transaction *transaction)
658{
659 (void)nb_transaction_process(NB_EV_ABORT, transaction);
660 nb_transaction_free(transaction);
661}
662
663void nb_candidate_commit_apply(struct nb_transaction *transaction,
664 bool save_transaction, uint32_t *transaction_id)
665{
666 (void)nb_transaction_process(NB_EV_APPLY, transaction);
667 nb_transaction_apply_finish(transaction);
668
669 /* Replace running by candidate. */
670 transaction->config->version++;
671 nb_config_replace(running_config, transaction->config, true);
672
673 /* Record transaction. */
674 if (save_transaction
675 && nb_db_transaction_save(transaction, transaction_id) != NB_OK)
676 flog_warn(EC_LIB_NB_TRANSACTION_RECORD_FAILED,
677 "%s: failed to record transaction", __func__);
678
679 nb_transaction_free(transaction);
680}
681
682int nb_candidate_commit(struct nb_config *candidate, enum nb_client client,
683 bool save_transaction, const char *comment,
684 uint32_t *transaction_id)
685{
686 struct nb_transaction *transaction = NULL;
687 int ret;
688
689 ret = nb_candidate_commit_prepare(candidate, client, comment,
690 &transaction);
691 /*
692 * Apply the changes if the preparation phase succeeded. Otherwise abort
693 * the transaction.
694 */
695 if (ret == NB_OK)
696 nb_candidate_commit_apply(transaction, save_transaction,
697 transaction_id);
698 else if (transaction != NULL)
699 nb_candidate_commit_abort(transaction);
700
701 return ret;
702}
703
704static void nb_log_callback(const enum nb_event event,
705 enum nb_operation operation, const char *xpath,
706 const char *value)
707{
708 zlog_debug(
709 "northbound callback: event [%s] op [%s] xpath [%s] value [%s]",
710 nb_event_name(event), nb_operation_name(operation), xpath,
711 value);
712}
713
714/*
715 * Call the northbound configuration callback associated to a given
716 * configuration change.
717 */
9eb2c0a1 718static int nb_callback_configuration(const enum nb_event event,
1c2facd1
RW
719 struct nb_config_change *change)
720{
721 enum nb_operation operation = change->cb.operation;
722 const char *xpath = change->cb.xpath;
723 const struct nb_node *nb_node = change->cb.nb_node;
724 const struct lyd_node *dnode = change->cb.dnode;
725 union nb_resource *resource;
726 int ret = NB_ERR;
727
9eb2c0a1 728 if (DEBUG_MODE_CHECK(&nb_dbg_cbs_config, DEBUG_MODE_ALL)) {
1c2facd1
RW
729 const char *value = "(none)";
730
731 if (dnode && !yang_snode_is_typeless_data(dnode->schema))
732 value = yang_dnode_get_string(dnode, NULL);
733
734 nb_log_callback(event, operation, xpath, value);
735 }
736
737 if (event == NB_EV_VALIDATE)
738 resource = NULL;
739 else
740 resource = &change->resource;
741
742 switch (operation) {
743 case NB_OP_CREATE:
744 ret = (*nb_node->cbs.create)(event, dnode, resource);
745 break;
746 case NB_OP_MODIFY:
747 ret = (*nb_node->cbs.modify)(event, dnode, resource);
748 break;
95ce849b 749 case NB_OP_DESTROY:
d01b92fd 750 ret = (*nb_node->cbs.destroy)(event, dnode);
1c2facd1
RW
751 break;
752 case NB_OP_MOVE:
753 ret = (*nb_node->cbs.move)(event, dnode);
754 break;
755 default:
756 break;
757 }
758
625b70e3 759 if (ret != NB_OK) {
ec348d43
RW
760 enum lib_log_refs ref = 0;
761
625b70e3
EDP
762 switch (event) {
763 case NB_EV_VALIDATE:
764 ref = EC_LIB_NB_CB_CONFIG_VALIDATE;
765 break;
766 case NB_EV_PREPARE:
767 ref = EC_LIB_NB_CB_CONFIG_PREPARE;
768 break;
769 case NB_EV_ABORT:
770 ref = EC_LIB_NB_CB_CONFIG_ABORT;
771 break;
772 case NB_EV_APPLY:
773 ref = EC_LIB_NB_CB_CONFIG_APPLY;
774 break;
775 }
776 if (event == NB_EV_VALIDATE || event == NB_EV_PREPARE)
777 flog_warn(
778 ref,
779 "%s: error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]",
780 __func__, nb_err_name(ret),
781 nb_event_name(event),
782 nb_operation_name(operation), xpath);
783 else
784 flog_err(
785 ref,
786 "%s: error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]",
787 __func__, nb_err_name(ret),
788 nb_event_name(event),
789 nb_operation_name(operation), xpath);
790 }
1c2facd1
RW
791
792 return ret;
793}
794
9eb2c0a1
RW
795struct yang_data *nb_callback_get_elem(const struct nb_node *nb_node,
796 const char *xpath,
797 const void *list_entry)
798{
799 DEBUGD(&nb_dbg_cbs_state,
800 "northbound callback (get_elem): xpath [%s] list_entry [%p]",
801 xpath, list_entry);
802
803 return nb_node->cbs.get_elem(xpath, list_entry);
804}
805
806const void *nb_callback_get_next(const struct nb_node *nb_node,
807 const void *parent_list_entry,
808 const void *list_entry)
809{
810 DEBUGD(&nb_dbg_cbs_state,
811 "northbound callback (get_next): node [%s] parent_list_entry [%p] list_entry [%p]",
812 nb_node->xpath, parent_list_entry, list_entry);
813
814 return nb_node->cbs.get_next(parent_list_entry, list_entry);
815}
816
817int nb_callback_get_keys(const struct nb_node *nb_node, const void *list_entry,
818 struct yang_list_keys *keys)
819{
820 DEBUGD(&nb_dbg_cbs_state,
821 "northbound callback (get_keys): node [%s] list_entry [%p]",
822 nb_node->xpath, list_entry);
823
824 return nb_node->cbs.get_keys(list_entry, keys);
825}
826
827const void *nb_callback_lookup_entry(const struct nb_node *nb_node,
828 const void *parent_list_entry,
829 const struct yang_list_keys *keys)
830{
831 DEBUGD(&nb_dbg_cbs_state,
832 "northbound callback (lookup_entry): node [%s] parent_list_entry [%p]",
833 nb_node->xpath, parent_list_entry);
834
835 return nb_node->cbs.lookup_entry(parent_list_entry, keys);
836}
837
838int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath,
839 const struct list *input, struct list *output)
840{
841 DEBUGD(&nb_dbg_cbs_rpc, "northbound RPC: %s", xpath);
842
843 return nb_node->cbs.rpc(xpath, input, output);
844}
845
1c2facd1
RW
846static struct nb_transaction *nb_transaction_new(struct nb_config *config,
847 struct nb_config_cbs *changes,
848 enum nb_client client,
849 const char *comment)
850{
851 struct nb_transaction *transaction;
852
853 if (transaction_in_progress) {
854 flog_warn(
855 EC_LIB_NB_TRANSACTION_CREATION_FAILED,
856 "%s: error - there's already another transaction in progress",
857 __func__);
858 return NULL;
859 }
860 transaction_in_progress = true;
861
862 transaction = XCALLOC(MTYPE_TMP, sizeof(*transaction));
863 transaction->client = client;
864 if (comment)
865 strlcpy(transaction->comment, comment,
866 sizeof(transaction->comment));
867 transaction->config = config;
868 transaction->changes = *changes;
869
870 return transaction;
871}
872
873static void nb_transaction_free(struct nb_transaction *transaction)
874{
875 nb_config_diff_del_changes(&transaction->changes);
876 XFREE(MTYPE_TMP, transaction);
877 transaction_in_progress = false;
878}
879
880/* Process all configuration changes associated to a transaction. */
881static int nb_transaction_process(enum nb_event event,
882 struct nb_transaction *transaction)
883{
884 struct nb_config_cb *cb;
885
886 RB_FOREACH (cb, nb_config_cbs, &transaction->changes) {
887 struct nb_config_change *change = (struct nb_config_change *)cb;
888 int ret;
889
890 /*
891 * Only try to release resources that were allocated
892 * successfully.
893 */
894 if (event == NB_EV_ABORT && change->prepare_ok == false)
895 break;
896
897 /* Call the appropriate callback. */
9eb2c0a1 898 ret = nb_callback_configuration(event, change);
1c2facd1
RW
899 switch (event) {
900 case NB_EV_PREPARE:
901 if (ret != NB_OK)
902 return ret;
903 change->prepare_ok = true;
904 break;
905 case NB_EV_ABORT:
906 case NB_EV_APPLY:
907 /*
908 * At this point it's not possible to reject the
909 * transaction anymore, so any failure here can lead to
910 * inconsistencies and should be treated as a bug.
911 * Operations prone to errors, like validations and
912 * resource allocations, should be performed during the
913 * 'prepare' phase.
914 */
915 break;
916 default:
917 break;
918 }
919 }
920
921 return NB_OK;
922}
923
924static struct nb_config_cb *
925nb_apply_finish_cb_new(struct nb_config_cbs *cbs, const char *xpath,
926 const struct nb_node *nb_node,
927 const struct lyd_node *dnode)
928{
929 struct nb_config_cb *cb;
930
931 cb = XCALLOC(MTYPE_TMP, sizeof(*cb));
932 strlcpy(cb->xpath, xpath, sizeof(cb->xpath));
933 cb->nb_node = nb_node;
934 cb->dnode = dnode;
935 RB_INSERT(nb_config_cbs, cbs, cb);
936
937 return cb;
938}
939
940static struct nb_config_cb *
941nb_apply_finish_cb_find(struct nb_config_cbs *cbs, const char *xpath,
942 const struct nb_node *nb_node)
943{
944 struct nb_config_cb s;
945
946 strlcpy(s.xpath, xpath, sizeof(s.xpath));
947 s.nb_node = nb_node;
948 return RB_FIND(nb_config_cbs, cbs, &s);
949}
950
951/* Call the 'apply_finish' callbacks. */
952static void nb_transaction_apply_finish(struct nb_transaction *transaction)
953{
954 struct nb_config_cbs cbs;
955 struct nb_config_cb *cb;
956
957 /* Initialize tree of 'apply_finish' callbacks. */
958 RB_INIT(nb_config_cbs, &cbs);
959
960 /* Identify the 'apply_finish' callbacks that need to be called. */
961 RB_FOREACH (cb, nb_config_cbs, &transaction->changes) {
962 struct nb_config_change *change = (struct nb_config_change *)cb;
963 const struct lyd_node *dnode = change->cb.dnode;
964
965 /*
966 * Iterate up to the root of the data tree. When a node is being
967 * deleted, skip its 'apply_finish' callback if one is defined
968 * (the 'apply_finish' callbacks from the node ancestors should
969 * be called though).
970 */
95ce849b 971 if (change->cb.operation == NB_OP_DESTROY) {
1c2facd1
RW
972 char xpath[XPATH_MAXLEN];
973
974 dnode = dnode->parent;
975 if (!dnode)
976 break;
977
978 /*
979 * The dnode from 'delete' callbacks point to elements
980 * from the running configuration. Use yang_dnode_get()
981 * to get the corresponding dnode from the candidate
982 * configuration that is being committed.
983 */
984 yang_dnode_get_path(dnode, xpath, sizeof(xpath));
985 dnode = yang_dnode_get(transaction->config->dnode,
986 xpath);
987 }
988 while (dnode) {
989 char xpath[XPATH_MAXLEN];
990 struct nb_node *nb_node;
991
992 nb_node = dnode->schema->priv;
993 if (!nb_node->cbs.apply_finish)
994 goto next;
995
996 /*
997 * Don't call the callback more than once for the same
998 * data node.
999 */
1000 yang_dnode_get_path(dnode, xpath, sizeof(xpath));
1001 if (nb_apply_finish_cb_find(&cbs, xpath, nb_node))
1002 goto next;
1003
1004 nb_apply_finish_cb_new(&cbs, xpath, nb_node, dnode);
1005
1006 next:
1007 dnode = dnode->parent;
1008 }
1009 }
1010
1011 /* Call the 'apply_finish' callbacks, sorted by their priorities. */
1012 RB_FOREACH (cb, nb_config_cbs, &cbs) {
9eb2c0a1 1013 if (DEBUG_MODE_CHECK(&nb_dbg_cbs_config, DEBUG_MODE_ALL))
1c2facd1
RW
1014 nb_log_callback(NB_EV_APPLY, NB_OP_APPLY_FINISH,
1015 cb->xpath, NULL);
1016
1017 (*cb->nb_node->cbs.apply_finish)(cb->dnode);
1018 }
1019
1020 /* Release memory. */
1021 while (!RB_EMPTY(nb_config_cbs, &cbs)) {
1022 cb = RB_ROOT(nb_config_cbs, &cbs);
1023 RB_REMOVE(nb_config_cbs, &cbs, cb);
1024 XFREE(MTYPE_TMP, cb);
1025 }
1026}
1027
1a4bc045
RW
1028static int nb_oper_data_iter_children(const struct lys_node *snode,
1029 const char *xpath, const void *list_entry,
1030 const struct yang_list_keys *list_keys,
1031 struct yang_translator *translator,
1032 bool first, uint32_t flags,
1033 nb_oper_data_cb cb, void *arg)
1034{
1035 struct lys_node *child;
1036
1037 LY_TREE_FOR (snode->child, child) {
1038 int ret;
1039
1040 ret = nb_oper_data_iter_node(child, xpath, list_entry,
1041 list_keys, translator, false,
1042 flags, cb, arg);
1043 if (ret != NB_OK)
1044 return ret;
1045 }
1046
1047 return NB_OK;
1048}
1049
1050static int nb_oper_data_iter_leaf(const struct nb_node *nb_node,
1051 const char *xpath, const void *list_entry,
1052 const struct yang_list_keys *list_keys,
1053 struct yang_translator *translator,
1054 uint32_t flags, nb_oper_data_cb cb, void *arg)
1055{
1056 struct yang_data *data;
1057
1058 if (CHECK_FLAG(nb_node->snode->flags, LYS_CONFIG_W))
1059 return NB_OK;
1060
1061 /* Ignore list keys. */
1062 if (lys_is_key((struct lys_node_leaf *)nb_node->snode, NULL))
1063 return NB_OK;
1064
9eb2c0a1 1065 data = nb_callback_get_elem(nb_node, xpath, list_entry);
1a4bc045
RW
1066 if (data == NULL)
1067 /* Leaf of type "empty" is not present. */
1068 return NB_OK;
1069
1070 return (*cb)(nb_node->snode, translator, data, arg);
1071}
1072
1073static int nb_oper_data_iter_container(const struct nb_node *nb_node,
1074 const char *xpath,
1075 const void *list_entry,
1076 const struct yang_list_keys *list_keys,
1077 struct yang_translator *translator,
1078 uint32_t flags, nb_oper_data_cb cb,
1079 void *arg)
1080{
1081 if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
1082 return NB_OK;
1083
1084 /* Presence containers. */
1085 if (nb_node->cbs.get_elem) {
1086 struct yang_data *data;
1087 int ret;
1088
9eb2c0a1 1089 data = nb_callback_get_elem(nb_node, xpath, list_entry);
1a4bc045
RW
1090 if (data == NULL)
1091 /* Presence container is not present. */
1092 return NB_OK;
1093
1094 ret = (*cb)(nb_node->snode, translator, data, arg);
1095 if (ret != NB_OK)
1096 return ret;
1097 }
1098
1099 /* Iterate over the child nodes. */
1100 return nb_oper_data_iter_children(nb_node->snode, xpath, list_entry,
1101 list_keys, translator, false, flags,
1102 cb, arg);
1103}
1104
1105static int
1106nb_oper_data_iter_leaflist(const struct nb_node *nb_node, const char *xpath,
1107 const void *parent_list_entry,
1108 const struct yang_list_keys *parent_list_keys,
1109 struct yang_translator *translator, uint32_t flags,
1110 nb_oper_data_cb cb, void *arg)
1111{
1112 const void *list_entry = NULL;
1113
1114 if (CHECK_FLAG(nb_node->snode->flags, LYS_CONFIG_W))
1115 return NB_OK;
1116
1117 do {
1118 struct yang_data *data;
1119 int ret;
1120
9eb2c0a1
RW
1121 list_entry = nb_callback_get_next(nb_node, parent_list_entry,
1122 list_entry);
1a4bc045
RW
1123 if (!list_entry)
1124 /* End of the list. */
1125 break;
1126
9eb2c0a1 1127 data = nb_callback_get_elem(nb_node, xpath, list_entry);
1a4bc045
RW
1128 if (data == NULL)
1129 continue;
1130
1131 ret = (*cb)(nb_node->snode, translator, data, arg);
1132 if (ret != NB_OK)
1133 return ret;
1134 } while (list_entry);
1135
1136 return NB_OK;
1137}
1138
1139static int nb_oper_data_iter_list(const struct nb_node *nb_node,
1140 const char *xpath_list,
1141 const void *parent_list_entry,
1142 const struct yang_list_keys *parent_list_keys,
1143 struct yang_translator *translator,
1144 uint32_t flags, nb_oper_data_cb cb, void *arg)
1145{
1146 struct lys_node_list *slist = (struct lys_node_list *)nb_node->snode;
1147 const void *list_entry = NULL;
99fb518f 1148 uint32_t position = 1;
1a4bc045
RW
1149
1150 if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
1151 return NB_OK;
1152
1153 /* Iterate over all list entries. */
1154 do {
1155 struct yang_list_keys list_keys;
f999f11e 1156 char xpath[XPATH_MAXLEN * 2];
1a4bc045
RW
1157 int ret;
1158
1159 /* Obtain list entry. */
9eb2c0a1
RW
1160 list_entry = nb_callback_get_next(nb_node, parent_list_entry,
1161 list_entry);
1a4bc045
RW
1162 if (!list_entry)
1163 /* End of the list. */
1164 break;
1165
99fb518f
RW
1166 if (!CHECK_FLAG(nb_node->flags, F_NB_NODE_KEYLESS_LIST)) {
1167 /* Obtain the list entry keys. */
9eb2c0a1
RW
1168 if (nb_callback_get_keys(nb_node, list_entry,
1169 &list_keys)
99fb518f
RW
1170 != NB_OK) {
1171 flog_warn(EC_LIB_NB_CB_STATE,
1172 "%s: failed to get list keys",
1173 __func__);
1174 return NB_ERR;
1175 }
1176
1177 /* Build XPath of the list entry. */
1178 strlcpy(xpath, xpath_list, sizeof(xpath));
1179 for (unsigned int i = 0; i < list_keys.num; i++) {
1180 snprintf(xpath + strlen(xpath),
1181 sizeof(xpath) - strlen(xpath),
1182 "[%s='%s']", slist->keys[i]->name,
1183 list_keys.key[i]);
1184 }
1185 } else {
1186 /*
1187 * Keyless list - build XPath using a positional index.
1188 */
1189 snprintf(xpath, sizeof(xpath), "%s[%u]", xpath_list,
1190 position);
1191 position++;
1a4bc045
RW
1192 }
1193
1194 /* Iterate over the child nodes. */
1195 ret = nb_oper_data_iter_children(
1196 nb_node->snode, xpath, list_entry, &list_keys,
1197 translator, false, flags, cb, arg);
1198 if (ret != NB_OK)
1199 return ret;
1200 } while (list_entry);
1201
1202 return NB_OK;
1203}
1204
1205static int nb_oper_data_iter_node(const struct lys_node *snode,
1206 const char *xpath_parent,
1207 const void *list_entry,
1208 const struct yang_list_keys *list_keys,
1209 struct yang_translator *translator,
1210 bool first, uint32_t flags,
1211 nb_oper_data_cb cb, void *arg)
1212{
1213 struct nb_node *nb_node;
1214 char xpath[XPATH_MAXLEN];
1215 int ret = NB_OK;
1216
1217 if (!first && CHECK_FLAG(flags, NB_OPER_DATA_ITER_NORECURSE)
1218 && CHECK_FLAG(snode->nodetype, LYS_CONTAINER | LYS_LIST))
1219 return NB_OK;
1220
1221 /* Update XPath. */
1222 strlcpy(xpath, xpath_parent, sizeof(xpath));
1223 if (!first && snode->nodetype != LYS_USES)
1224 snprintf(xpath + strlen(xpath), sizeof(xpath) - strlen(xpath),
1225 "/%s", snode->name);
1226
1227 nb_node = snode->priv;
1228 switch (snode->nodetype) {
1229 case LYS_CONTAINER:
1230 ret = nb_oper_data_iter_container(nb_node, xpath, list_entry,
1231 list_keys, translator, flags,
1232 cb, arg);
1233 break;
1234 case LYS_LEAF:
1235 ret = nb_oper_data_iter_leaf(nb_node, xpath, list_entry,
1236 list_keys, translator, flags, cb,
1237 arg);
1238 break;
1239 case LYS_LEAFLIST:
1240 ret = nb_oper_data_iter_leaflist(nb_node, xpath, list_entry,
1241 list_keys, translator, flags,
1242 cb, arg);
1243 break;
1244 case LYS_LIST:
1245 ret = nb_oper_data_iter_list(nb_node, xpath, list_entry,
1246 list_keys, translator, flags, cb,
1247 arg);
1248 break;
1249 case LYS_USES:
1250 ret = nb_oper_data_iter_children(snode, xpath, list_entry,
1251 list_keys, translator, false,
1252 flags, cb, arg);
1253 break;
1254 default:
1255 break;
1256 }
1257
1258 return ret;
1259}
1260
1261int nb_oper_data_iterate(const char *xpath, struct yang_translator *translator,
1262 uint32_t flags, nb_oper_data_cb cb, void *arg)
1263{
1264 struct nb_node *nb_node;
1265 const void *list_entry = NULL;
1266 struct yang_list_keys list_keys;
1267 struct list *list_dnodes;
1268 struct lyd_node *dnode, *dn;
1269 struct listnode *ln;
1270 int ret;
1271
1272 nb_node = nb_node_find(xpath);
1273 if (!nb_node) {
1274 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
1275 "%s: unknown data path: %s", __func__, xpath);
1276 return NB_ERR;
1277 }
1278
1279 /* For now this function works only with containers and lists. */
1280 if (!CHECK_FLAG(nb_node->snode->nodetype, LYS_CONTAINER | LYS_LIST)) {
1281 flog_warn(
1282 EC_LIB_NB_OPERATIONAL_DATA,
1283 "%s: can't iterate over YANG leaf or leaf-list [xpath %s]",
1284 __func__, xpath);
1285 return NB_ERR;
1286 }
1287
1288 /*
1289 * Create a data tree from the XPath so that we can parse the keys of
1290 * all YANG lists (if any).
1291 */
1292 ly_errno = 0;
1293 dnode = lyd_new_path(NULL, ly_native_ctx, xpath, NULL, 0,
1294 LYD_PATH_OPT_UPDATE);
1295 if (!dnode && ly_errno) {
1296 flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed",
1297 __func__);
1298 return NB_ERR;
1299 }
1300 /*
1301 * We can remove the following two lines once we depend on
1302 * libyang-v0.16-r2, which has the LYD_PATH_OPT_NOPARENTRET flag for
1303 * lyd_new_path().
1304 */
1305 dnode = yang_dnode_get(dnode, xpath);
1306 assert(dnode);
1307
1308 /*
1309 * Create a linked list to sort the data nodes starting from the root.
1310 */
1311 list_dnodes = list_new();
1312 for (dn = dnode; dn; dn = dn->parent) {
1313 if (dn->schema->nodetype != LYS_LIST || !dn->child)
1314 continue;
1315 listnode_add_head(list_dnodes, dn);
1316 }
1317 /*
1318 * Use the northbound callbacks to find list entry pointer corresponding
1319 * to the given XPath.
1320 */
1321 for (ALL_LIST_ELEMENTS_RO(list_dnodes, ln, dn)) {
1322 struct lyd_node *child;
1323 struct nb_node *nn;
1324 unsigned int n = 0;
1325
1326 /* Obtain the list entry keys. */
1327 memset(&list_keys, 0, sizeof(list_keys));
1328 LY_TREE_FOR (dn->child, child) {
1329 if (!lys_is_key((struct lys_node_leaf *)child->schema,
1330 NULL))
1331 continue;
1332 strlcpy(list_keys.key[n],
1333 yang_dnode_get_string(child, NULL),
1334 sizeof(list_keys.key[n]));
1335 n++;
1336 }
1337 list_keys.num = n;
9f6de299
RW
1338 if (list_keys.num
1339 != ((struct lys_node_list *)dn->schema)->keys_size) {
1340 list_delete(&list_dnodes);
1341 yang_dnode_free(dnode);
1342 return NB_ERR_NOT_FOUND;
1343 }
1a4bc045
RW
1344
1345 /* Find the list entry pointer. */
1346 nn = dn->schema->priv;
9eb2c0a1
RW
1347 list_entry =
1348 nb_callback_lookup_entry(nn, list_entry, &list_keys);
1a4bc045
RW
1349 if (list_entry == NULL) {
1350 list_delete(&list_dnodes);
1351 yang_dnode_free(dnode);
1352 return NB_ERR_NOT_FOUND;
1353 }
1354 }
1355
1356 /* If a list entry was given, iterate over that list entry only. */
1357 if (dnode->schema->nodetype == LYS_LIST && dnode->child)
1358 ret = nb_oper_data_iter_children(
1359 nb_node->snode, xpath, list_entry, &list_keys,
1360 translator, true, flags, cb, arg);
1361 else
1362 ret = nb_oper_data_iter_node(nb_node->snode, xpath, list_entry,
1363 &list_keys, translator, true,
1364 flags, cb, arg);
1365
1366 list_delete(&list_dnodes);
1367 yang_dnode_free(dnode);
1368
1369 return ret;
1370}
1371
1c2facd1
RW
1372bool nb_operation_is_valid(enum nb_operation operation,
1373 const struct lys_node *snode)
1374{
544ca69a 1375 struct nb_node *nb_node = snode->priv;
1c2facd1
RW
1376 struct lys_node_container *scontainer;
1377 struct lys_node_leaf *sleaf;
1378
1379 switch (operation) {
1380 case NB_OP_CREATE:
db452508 1381 if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
1c2facd1
RW
1382 return false;
1383
1384 switch (snode->nodetype) {
1385 case LYS_LEAF:
1386 sleaf = (struct lys_node_leaf *)snode;
1387 if (sleaf->type.base != LY_TYPE_EMPTY)
1388 return false;
1389 break;
1390 case LYS_CONTAINER:
1391 scontainer = (struct lys_node_container *)snode;
1392 if (!scontainer->presence)
1393 return false;
1394 break;
1395 case LYS_LIST:
1396 case LYS_LEAFLIST:
1397 break;
1398 default:
1399 return false;
1400 }
1401 return true;
1402 case NB_OP_MODIFY:
db452508 1403 if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
1c2facd1
RW
1404 return false;
1405
1406 switch (snode->nodetype) {
1407 case LYS_LEAF:
1408 sleaf = (struct lys_node_leaf *)snode;
1409 if (sleaf->type.base == LY_TYPE_EMPTY)
1410 return false;
1411
1412 /* List keys can't be modified. */
1413 if (lys_is_key(sleaf, NULL))
1414 return false;
1415 break;
1416 default:
1417 return false;
1418 }
1419 return true;
95ce849b 1420 case NB_OP_DESTROY:
db452508 1421 if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
1c2facd1
RW
1422 return false;
1423
1424 switch (snode->nodetype) {
1425 case LYS_LEAF:
1426 sleaf = (struct lys_node_leaf *)snode;
1427
1428 /* List keys can't be deleted. */
1429 if (lys_is_key(sleaf, NULL))
1430 return false;
1431
1432 /*
1433 * Only optional leafs can be deleted, or leafs whose
1434 * parent is a case statement.
1435 */
1436 if (snode->parent->nodetype == LYS_CASE)
1437 return true;
1438 if (sleaf->when)
1439 return true;
db452508
RW
1440 if (CHECK_FLAG(sleaf->flags, LYS_MAND_TRUE)
1441 || sleaf->dflt)
1c2facd1
RW
1442 return false;
1443 break;
1444 case LYS_CONTAINER:
1445 scontainer = (struct lys_node_container *)snode;
1446 if (!scontainer->presence)
1447 return false;
1448 break;
1449 case LYS_LIST:
1450 case LYS_LEAFLIST:
1451 break;
1452 default:
1453 return false;
1454 }
1455 return true;
1456 case NB_OP_MOVE:
db452508 1457 if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
1c2facd1
RW
1458 return false;
1459
1460 switch (snode->nodetype) {
1461 case LYS_LIST:
1462 case LYS_LEAFLIST:
db452508 1463 if (!CHECK_FLAG(snode->flags, LYS_USERORDERED))
1c2facd1
RW
1464 return false;
1465 break;
1466 default:
1467 return false;
1468 }
1469 return true;
1470 case NB_OP_APPLY_FINISH:
db452508 1471 if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
1c2facd1
RW
1472 return false;
1473 return true;
1474 case NB_OP_GET_ELEM:
db452508 1475 if (!CHECK_FLAG(snode->flags, LYS_CONFIG_R))
1c2facd1
RW
1476 return false;
1477
1478 switch (snode->nodetype) {
1479 case LYS_LEAF:
1a4bc045 1480 case LYS_LEAFLIST:
1c2facd1
RW
1481 break;
1482 case LYS_CONTAINER:
1483 scontainer = (struct lys_node_container *)snode;
1484 if (!scontainer->presence)
1485 return false;
1486 break;
1487 default:
1488 return false;
1489 }
1490 return true;
1491 case NB_OP_GET_NEXT:
1a4bc045
RW
1492 switch (snode->nodetype) {
1493 case LYS_LIST:
1494 if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
1495 return false;
1496 break;
1497 case LYS_LEAFLIST:
1498 if (CHECK_FLAG(snode->flags, LYS_CONFIG_W))
1499 return false;
1500 break;
1501 default:
1502 return false;
1503 }
1504 return true;
1c2facd1
RW
1505 case NB_OP_GET_KEYS:
1506 case NB_OP_LOOKUP_ENTRY:
1c2facd1
RW
1507 switch (snode->nodetype) {
1508 case LYS_LIST:
544ca69a
RW
1509 if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
1510 return false;
99fb518f
RW
1511 if (CHECK_FLAG(nb_node->flags, F_NB_NODE_KEYLESS_LIST))
1512 return false;
1c2facd1
RW
1513 break;
1514 default:
1515 return false;
1516 }
1517 return true;
1518 case NB_OP_RPC:
db452508 1519 if (CHECK_FLAG(snode->flags, LYS_CONFIG_W | LYS_CONFIG_R))
1c2facd1
RW
1520 return false;
1521
1522 switch (snode->nodetype) {
1523 case LYS_RPC:
1524 case LYS_ACTION:
1525 break;
1526 default:
1527 return false;
1528 }
1529 return true;
1530 default:
1531 return false;
1532 }
1533}
1534
1535DEFINE_HOOK(nb_notification_send, (const char *xpath, struct list *arguments),
1536 (xpath, arguments));
1537
1538int nb_notification_send(const char *xpath, struct list *arguments)
1539{
1540 int ret;
1541
9eb2c0a1
RW
1542 DEBUGD(&nb_dbg_notif, "northbound notification: %s", xpath);
1543
1c2facd1
RW
1544 ret = hook_call(nb_notification_send, xpath, arguments);
1545 if (arguments)
1546 list_delete(&arguments);
1547
1548 return ret;
1549}
1550
1551const char *nb_event_name(enum nb_event event)
1552{
1553 switch (event) {
1554 case NB_EV_VALIDATE:
1555 return "validate";
1556 case NB_EV_PREPARE:
1557 return "prepare";
1558 case NB_EV_ABORT:
1559 return "abort";
1560 case NB_EV_APPLY:
1561 return "apply";
1562 default:
1563 return "unknown";
1564 }
1565}
1566
1567const char *nb_operation_name(enum nb_operation operation)
1568{
1569 switch (operation) {
1570 case NB_OP_CREATE:
1571 return "create";
1572 case NB_OP_MODIFY:
1573 return "modify";
95ce849b
MS
1574 case NB_OP_DESTROY:
1575 return "destroy";
1c2facd1
RW
1576 case NB_OP_MOVE:
1577 return "move";
1578 case NB_OP_APPLY_FINISH:
1579 return "apply_finish";
1580 case NB_OP_GET_ELEM:
1581 return "get_elem";
1582 case NB_OP_GET_NEXT:
1583 return "get_next";
1584 case NB_OP_GET_KEYS:
1585 return "get_keys";
1586 case NB_OP_LOOKUP_ENTRY:
1587 return "lookup_entry";
1588 case NB_OP_RPC:
1589 return "rpc";
1590 default:
1591 return "unknown";
1592 }
1593}
1594
1595const char *nb_err_name(enum nb_error error)
1596{
1597 switch (error) {
1598 case NB_OK:
1599 return "ok";
1600 case NB_ERR:
1601 return "generic error";
1602 case NB_ERR_NO_CHANGES:
1603 return "no changes";
1604 case NB_ERR_NOT_FOUND:
1605 return "element not found";
1606 case NB_ERR_LOCKED:
1607 return "resource is locked";
1608 case NB_ERR_VALIDATION:
1609 return "validation error";
1610 case NB_ERR_RESOURCE:
1611 return "failed to allocate resource";
1612 case NB_ERR_INCONSISTENCY:
1613 return "internal inconsistency";
1614 default:
1615 return "unknown";
1616 }
1617}
1618
1619const char *nb_client_name(enum nb_client client)
1620{
1621 switch (client) {
1622 case NB_CLIENT_CLI:
1623 return "CLI";
5bce33b3
RW
1624 case NB_CLIENT_CONFD:
1625 return "ConfD";
a7ca2199
RW
1626 case NB_CLIENT_SYSREPO:
1627 return "Sysrepo";
1c2facd1
RW
1628 default:
1629 return "unknown";
1630 }
1631}
1632
1633static void nb_load_callbacks(const struct frr_yang_module_info *module)
1634{
1635 for (size_t i = 0; module->nodes[i].xpath; i++) {
1636 struct nb_node *nb_node;
1637 uint32_t priority;
1638
1639 nb_node = nb_node_find(module->nodes[i].xpath);
1640 if (!nb_node) {
1641 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
1642 "%s: unknown data path: %s", __func__,
1643 module->nodes[i].xpath);
1644 continue;
1645 }
1646
1647 nb_node->cbs = module->nodes[i].cbs;
1648 priority = module->nodes[i].priority;
1649 if (priority != 0)
1650 nb_node->priority = priority;
1651 }
1652}
1653
fbdc1c0a
RW
1654void nb_init(struct thread_master *tm,
1655 const struct frr_yang_module_info *modules[], size_t nmodules)
1c2facd1
RW
1656{
1657 unsigned int errors = 0;
1658
1659 /* Load YANG modules. */
1660 for (size_t i = 0; i < nmodules; i++)
1661 yang_module_load(modules[i]->name);
1662
1663 /* Create a nb_node for all YANG schema nodes. */
544ca69a 1664 nb_nodes_create();
1c2facd1
RW
1665
1666 /* Load northbound callbacks. */
1667 for (size_t i = 0; i < nmodules; i++)
1668 nb_load_callbacks(modules[i]);
1669
1670 /* Validate northbound callbacks. */
e0ccfad2 1671 yang_snodes_iterate_all(nb_node_validate, 0, &errors);
1c2facd1
RW
1672 if (errors > 0) {
1673 flog_err(
1674 EC_LIB_NB_CBS_VALIDATION,
1675 "%s: failed to validate northbound callbacks: %u error(s)",
1676 __func__, errors);
1677 exit(1);
1678 }
1679
1c2facd1
RW
1680 /* Create an empty running configuration. */
1681 running_config = nb_config_new(NULL);
1682
1683 /* Initialize the northbound CLI. */
fbdc1c0a 1684 nb_cli_init(tm);
1c2facd1
RW
1685}
1686
1687void nb_terminate(void)
1688{
1689 /* Terminate the northbound CLI. */
1690 nb_cli_terminate();
1691
1692 /* Delete all nb_node's from all YANG modules. */
544ca69a 1693 nb_nodes_delete();
1c2facd1
RW
1694
1695 /* Delete the running configuration. */
1696 nb_config_free(running_config);
1697}