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