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