]> git.proxmox.com Git - mirror_frr.git/blob - lib/northbound.c
2fabae22ba186276dd6a2104895ea798309f0883
[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
54 static void nb_node_new_cb(const struct lys_node *snode, void *arg1, void *arg2)
55 {
56 struct nb_node *nb_node;
57 struct lys_node *sparent, *sparent_list;
58
59 nb_node = XCALLOC(MTYPE_NB_NODE, sizeof(*nb_node));
60 yang_snode_get_path(snode, YANG_PATH_DATA, nb_node->xpath,
61 sizeof(nb_node->xpath));
62 nb_node->priority = NB_DFLT_PRIORITY;
63 sparent = yang_snode_real_parent(snode);
64 if (sparent)
65 nb_node->parent = sparent->priv;
66 sparent_list = yang_snode_parent_list(snode);
67 if (sparent_list)
68 nb_node->parent_list = sparent_list->priv;
69
70 /*
71 * Link the northbound node and the libyang schema node with one
72 * another.
73 */
74 nb_node->snode = snode;
75 lys_set_private(snode, nb_node);
76 }
77
78 static void nb_node_del_cb(const struct lys_node *snode, void *arg1, void *arg2)
79 {
80 struct nb_node *nb_node;
81
82 nb_node = snode->priv;
83 lys_set_private(snode, NULL);
84 XFREE(MTYPE_NB_NODE, nb_node);
85 }
86
87 struct nb_node *nb_node_find(const char *xpath)
88 {
89 const struct lys_node *snode;
90
91 /*
92 * Use libyang to find the schema node associated to the xpath and get
93 * the northbound node from there (snode private pointer).
94 */
95 snode = ly_ctx_get_node(ly_native_ctx, NULL, xpath, 0);
96 if (!snode)
97 return NULL;
98
99 return snode->priv;
100 }
101
102 static int nb_node_validate_cb(const struct nb_node *nb_node,
103 enum nb_operation operation,
104 int callback_implemented, bool optional)
105 {
106 bool valid;
107
108 valid = nb_operation_is_valid(operation, nb_node->snode);
109
110 if (!valid && callback_implemented)
111 flog_warn(EC_LIB_NB_CB_UNNEEDED,
112 "unneeded '%s' callback for '%s'",
113 nb_operation_name(operation), nb_node->xpath);
114
115 if (!optional && valid && !callback_implemented) {
116 flog_err(EC_LIB_NB_CB_MISSING, "missing '%s' callback for '%s'",
117 nb_operation_name(operation), nb_node->xpath);
118 return 1;
119 }
120
121 return 0;
122 }
123
124 /*
125 * Check if the required callbacks were implemented for the given northbound
126 * node.
127 */
128 static unsigned int nb_node_validate_cbs(const struct nb_node *nb_node)
129
130 {
131 unsigned int error = 0;
132
133 error += nb_node_validate_cb(nb_node, NB_OP_CREATE,
134 !!nb_node->cbs.create, false);
135 error += nb_node_validate_cb(nb_node, NB_OP_MODIFY,
136 !!nb_node->cbs.modify, false);
137 error += nb_node_validate_cb(nb_node, NB_OP_DELETE,
138 !!nb_node->cbs.delete, false);
139 error += nb_node_validate_cb(nb_node, NB_OP_MOVE, !!nb_node->cbs.move,
140 false);
141 error += nb_node_validate_cb(nb_node, NB_OP_APPLY_FINISH,
142 !!nb_node->cbs.apply_finish, true);
143 error += nb_node_validate_cb(nb_node, NB_OP_GET_ELEM,
144 !!nb_node->cbs.get_elem, false);
145 error += nb_node_validate_cb(nb_node, NB_OP_GET_NEXT,
146 !!nb_node->cbs.get_next, false);
147 error += nb_node_validate_cb(nb_node, NB_OP_GET_KEYS,
148 !!nb_node->cbs.get_keys, false);
149 error += nb_node_validate_cb(nb_node, NB_OP_LOOKUP_ENTRY,
150 !!nb_node->cbs.lookup_entry, false);
151 error += nb_node_validate_cb(nb_node, NB_OP_RPC, !!nb_node->cbs.rpc,
152 false);
153
154 return error;
155 }
156
157 static unsigned int nb_node_validate_priority(const struct nb_node *nb_node)
158 {
159 /* Top-level nodes can have any priority. */
160 if (!nb_node->parent)
161 return 0;
162
163 if (nb_node->priority < nb_node->parent->priority) {
164 flog_err(EC_LIB_NB_CB_INVALID_PRIO,
165 "node has higher priority than its parent [xpath %s]",
166 nb_node->xpath);
167 return 1;
168 }
169
170 return 0;
171 }
172
173 static void nb_node_validate(const struct lys_node *snode, void *arg1,
174 void *arg2)
175 {
176 struct nb_node *nb_node = snode->priv;
177 unsigned int *errors = arg1;
178
179 /* Validate callbacks and priority. */
180 *errors += nb_node_validate_cbs(nb_node);
181 *errors += nb_node_validate_priority(nb_node);
182 }
183
184 struct nb_config *nb_config_new(struct lyd_node *dnode)
185 {
186 struct nb_config *config;
187
188 config = XCALLOC(MTYPE_NB_CONFIG, sizeof(*config));
189 if (dnode)
190 config->dnode = dnode;
191 else
192 config->dnode = yang_dnode_new(ly_native_ctx, true);
193 config->version = 0;
194
195 return config;
196 }
197
198 void nb_config_free(struct nb_config *config)
199 {
200 if (config->dnode)
201 yang_dnode_free(config->dnode);
202 XFREE(MTYPE_NB_CONFIG, config);
203 }
204
205 struct nb_config *nb_config_dup(const struct nb_config *config)
206 {
207 struct nb_config *dup;
208
209 dup = XCALLOC(MTYPE_NB_CONFIG, sizeof(*dup));
210 dup->dnode = yang_dnode_dup(config->dnode);
211 dup->version = config->version;
212
213 return dup;
214 }
215
216 int nb_config_merge(struct nb_config *config_dst, struct nb_config *config_src,
217 bool preserve_source)
218 {
219 int ret;
220
221 ret = lyd_merge(config_dst->dnode, config_src->dnode, LYD_OPT_EXPLICIT);
222 if (ret != 0)
223 flog_warn(EC_LIB_LIBYANG, "%s: lyd_merge() failed", __func__);
224
225 if (!preserve_source)
226 nb_config_free(config_src);
227
228 return (ret == 0) ? NB_OK : NB_ERR;
229 }
230
231 void nb_config_replace(struct nb_config *config_dst,
232 struct nb_config *config_src, bool preserve_source)
233 {
234 /* Update version. */
235 if (config_src->version != 0)
236 config_dst->version = config_src->version;
237
238 /* Update dnode. */
239 yang_dnode_free(config_dst->dnode);
240 if (preserve_source) {
241 config_dst->dnode = yang_dnode_dup(config_src->dnode);
242 } else {
243 config_dst->dnode = config_src->dnode;
244 config_src->dnode = NULL;
245 nb_config_free(config_src);
246 }
247 }
248
249 /* Generate the nb_config_cbs tree. */
250 static inline int nb_config_cb_compare(const struct nb_config_cb *a,
251 const struct nb_config_cb *b)
252 {
253 /* Sort by priority first. */
254 if (a->nb_node->priority < b->nb_node->priority)
255 return -1;
256 if (a->nb_node->priority > b->nb_node->priority)
257 return 1;
258
259 /*
260 * Use XPath as a tie-breaker. This will naturally sort parent nodes
261 * before their children.
262 */
263 return strcmp(a->xpath, b->xpath);
264 }
265 RB_GENERATE(nb_config_cbs, nb_config_cb, entry, nb_config_cb_compare);
266
267 static void nb_config_diff_add_change(struct nb_config_cbs *changes,
268 enum nb_operation operation,
269 const struct lyd_node *dnode)
270 {
271 struct nb_config_change *change;
272
273 change = XCALLOC(MTYPE_TMP, sizeof(*change));
274 change->cb.operation = operation;
275 change->cb.nb_node = dnode->schema->priv;
276 yang_dnode_get_path(dnode, change->cb.xpath, sizeof(change->cb.xpath));
277 change->cb.dnode = dnode;
278
279 RB_INSERT(nb_config_cbs, changes, &change->cb);
280 }
281
282 static void nb_config_diff_del_changes(struct nb_config_cbs *changes)
283 {
284 while (!RB_EMPTY(nb_config_cbs, changes)) {
285 struct nb_config_change *change;
286
287 change = (struct nb_config_change *)RB_ROOT(nb_config_cbs,
288 changes);
289 RB_REMOVE(nb_config_cbs, changes, &change->cb);
290 XFREE(MTYPE_TMP, change);
291 }
292 }
293
294 /*
295 * Helper function used when calculating the delta between two different
296 * configurations. Given a new subtree, calculate all new YANG data nodes,
297 * excluding default leafs and leaf-lists. This is a recursive function.
298 */
299 static void nb_config_diff_new_subtree(const struct lyd_node *dnode,
300 struct nb_config_cbs *changes)
301 {
302 struct lyd_node *child;
303
304 LY_TREE_FOR (dnode->child, child) {
305 enum nb_operation operation;
306
307 switch (child->schema->nodetype) {
308 case LYS_LEAF:
309 case LYS_LEAFLIST:
310 if (lyd_wd_default((struct lyd_node_leaf_list *)child))
311 break;
312
313 if (nb_operation_is_valid(NB_OP_CREATE, child->schema))
314 operation = NB_OP_CREATE;
315 else if (nb_operation_is_valid(NB_OP_MODIFY,
316 child->schema))
317 operation = NB_OP_MODIFY;
318 else
319 continue;
320
321 nb_config_diff_add_change(changes, operation, child);
322 break;
323 case LYS_CONTAINER:
324 case LYS_LIST:
325 if (nb_operation_is_valid(NB_OP_CREATE, child->schema))
326 nb_config_diff_add_change(changes, NB_OP_CREATE,
327 child);
328 nb_config_diff_new_subtree(child, changes);
329 break;
330 default:
331 break;
332 }
333 }
334 }
335
336 /* Calculate the delta between two different configurations. */
337 static void nb_config_diff(const struct nb_config *config1,
338 const struct nb_config *config2,
339 struct nb_config_cbs *changes)
340 {
341 struct lyd_difflist *diff;
342
343 diff = lyd_diff(config1->dnode, config2->dnode,
344 LYD_DIFFOPT_WITHDEFAULTS);
345 assert(diff);
346
347 for (int i = 0; diff->type[i] != LYD_DIFF_END; i++) {
348 LYD_DIFFTYPE type;
349 struct lyd_node *dnode;
350 enum nb_operation operation;
351
352 type = diff->type[i];
353
354 switch (type) {
355 case LYD_DIFF_CREATED:
356 dnode = diff->second[i];
357
358 if (nb_operation_is_valid(NB_OP_CREATE, dnode->schema))
359 operation = NB_OP_CREATE;
360 else if (nb_operation_is_valid(NB_OP_MODIFY,
361 dnode->schema))
362 operation = NB_OP_MODIFY;
363 else
364 continue;
365 break;
366 case LYD_DIFF_DELETED:
367 dnode = diff->first[i];
368 operation = NB_OP_DELETE;
369 break;
370 case LYD_DIFF_CHANGED:
371 dnode = diff->second[i];
372 operation = NB_OP_MODIFY;
373 break;
374 case LYD_DIFF_MOVEDAFTER1:
375 case LYD_DIFF_MOVEDAFTER2:
376 default:
377 continue;
378 }
379
380 nb_config_diff_add_change(changes, operation, dnode);
381
382 if (type == LYD_DIFF_CREATED
383 && CHECK_FLAG(dnode->schema->nodetype,
384 LYS_CONTAINER | LYS_LIST))
385 nb_config_diff_new_subtree(dnode, changes);
386 }
387
388 lyd_free_diff(diff);
389 }
390
391 int nb_candidate_edit(struct nb_config *candidate,
392 const struct nb_node *nb_node,
393 enum nb_operation operation, const char *xpath,
394 const struct yang_data *previous,
395 const struct yang_data *data)
396 {
397 struct lyd_node *dnode;
398 char xpath_edit[XPATH_MAXLEN];
399
400 if (!nb_operation_is_valid(operation, nb_node->snode)) {
401 flog_warn(EC_LIB_NB_CANDIDATE_EDIT_ERROR,
402 "%s: %s operation not valid for %s", __func__,
403 nb_operation_name(operation), xpath);
404 return NB_ERR;
405 }
406
407 /* Use special notation for leaf-lists (RFC 6020, section 9.13.5). */
408 if (nb_node->snode->nodetype == LYS_LEAFLIST)
409 snprintf(xpath_edit, sizeof(xpath_edit), "%s[.='%s']", xpath,
410 data->value);
411 else
412 strlcpy(xpath_edit, xpath, sizeof(xpath_edit));
413
414 switch (operation) {
415 case NB_OP_CREATE:
416 case NB_OP_MODIFY:
417 ly_errno = 0;
418 dnode = lyd_new_path(candidate->dnode, ly_native_ctx,
419 xpath_edit, (void *)data->value, 0,
420 LYD_PATH_OPT_UPDATE);
421 if (!dnode && ly_errno) {
422 flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed",
423 __func__);
424 return NB_ERR;
425 }
426
427 /*
428 * If a new node was created, call lyd_validate() only to create
429 * default child nodes.
430 */
431 if (dnode) {
432 lyd_schema_sort(dnode, 0);
433 lyd_validate(&dnode, LYD_OPT_CONFIG, ly_native_ctx);
434 }
435 break;
436 case NB_OP_DELETE:
437 dnode = yang_dnode_get(candidate->dnode, xpath_edit);
438 if (!dnode)
439 /*
440 * Return a special error code so the caller can choose
441 * whether to ignore it or not.
442 */
443 return NB_ERR_NOT_FOUND;
444 lyd_free(dnode);
445 break;
446 case NB_OP_MOVE:
447 /* TODO: update configuration. */
448 break;
449 default:
450 flog_warn(EC_LIB_DEVELOPMENT,
451 "%s: unknown operation (%u) [xpath %s]", __func__,
452 operation, xpath_edit);
453 return NB_ERR;
454 }
455
456 return NB_OK;
457 }
458
459 bool nb_candidate_needs_update(const struct nb_config *candidate)
460 {
461 if (candidate->version < running_config->version)
462 return true;
463
464 return false;
465 }
466
467 int nb_candidate_update(struct nb_config *candidate)
468 {
469 struct nb_config *updated_config;
470
471 updated_config = nb_config_dup(running_config);
472 if (nb_config_merge(updated_config, candidate, true) != NB_OK)
473 return NB_ERR;
474
475 nb_config_replace(candidate, updated_config, false);
476
477 return NB_OK;
478 }
479
480 /*
481 * The northbound configuration callbacks use the 'priv' pointer present in the
482 * libyang lyd_node structure to store pointers to FRR internal variables
483 * associated to YANG lists and presence containers. Before commiting a
484 * candidate configuration, we must restore the 'priv' pointers stored in the
485 * running configuration since they might be lost while editing the candidate.
486 */
487 static void nb_candidate_restore_priv_pointers(struct nb_config *candidate)
488 {
489 struct lyd_node *root, *next, *dnode_iter;
490
491 LY_TREE_FOR (running_config->dnode, root) {
492 LY_TREE_DFS_BEGIN (root, next, dnode_iter) {
493 struct lyd_node *dnode_candidate;
494 char xpath[XPATH_MAXLEN];
495
496 if (!dnode_iter->priv)
497 goto next;
498
499 yang_dnode_get_path(dnode_iter, xpath, sizeof(xpath));
500 dnode_candidate =
501 yang_dnode_get(candidate->dnode, xpath);
502 if (dnode_candidate)
503 yang_dnode_set_entry(dnode_candidate,
504 dnode_iter->priv);
505
506 next:
507 LY_TREE_DFS_END(root, next, dnode_iter);
508 }
509 }
510 }
511
512 /*
513 * Perform YANG syntactic and semantic validation.
514 *
515 * WARNING: lyd_validate() can change the configuration as part of the
516 * validation process.
517 */
518 static int nb_candidate_validate_yang(struct nb_config *candidate)
519 {
520 if (lyd_validate(&candidate->dnode, LYD_OPT_STRICT | LYD_OPT_CONFIG,
521 ly_native_ctx)
522 != 0)
523 return NB_ERR_VALIDATION;
524
525 return NB_OK;
526 }
527
528 /* Perform code-level validation using the northbound callbacks. */
529 static int nb_candidate_validate_changes(struct nb_config *candidate,
530 struct nb_config_cbs *changes)
531 {
532 struct nb_config_cb *cb;
533
534 nb_candidate_restore_priv_pointers(candidate);
535 RB_FOREACH (cb, nb_config_cbs, changes) {
536 struct nb_config_change *change = (struct nb_config_change *)cb;
537 int ret;
538
539 ret = nb_configuration_callback(NB_EV_VALIDATE, change);
540 if (ret != NB_OK)
541 return NB_ERR_VALIDATION;
542 }
543
544 return NB_OK;
545 }
546
547 int nb_candidate_validate(struct nb_config *candidate)
548 {
549 struct nb_config_cbs changes;
550 int ret;
551
552 if (nb_candidate_validate_yang(candidate) != NB_OK)
553 return NB_ERR_VALIDATION;
554
555 RB_INIT(nb_config_cbs, &changes);
556 nb_config_diff(running_config, candidate, &changes);
557 ret = nb_candidate_validate_changes(candidate, &changes);
558 nb_config_diff_del_changes(&changes);
559
560 return ret;
561 }
562
563 int nb_candidate_commit_prepare(struct nb_config *candidate,
564 enum nb_client client, const char *comment,
565 struct nb_transaction **transaction)
566 {
567 struct nb_config_cbs changes;
568
569 if (nb_candidate_validate_yang(candidate) != NB_OK) {
570 flog_warn(EC_LIB_NB_CANDIDATE_INVALID,
571 "%s: failed to validate candidate configuration",
572 __func__);
573 return NB_ERR_VALIDATION;
574 }
575
576 RB_INIT(nb_config_cbs, &changes);
577 nb_config_diff(running_config, candidate, &changes);
578 if (RB_EMPTY(nb_config_cbs, &changes))
579 return NB_ERR_NO_CHANGES;
580
581 if (nb_candidate_validate_changes(candidate, &changes) != NB_OK) {
582 flog_warn(EC_LIB_NB_CANDIDATE_INVALID,
583 "%s: failed to validate candidate configuration",
584 __func__);
585 nb_config_diff_del_changes(&changes);
586 return NB_ERR_VALIDATION;
587 }
588
589 *transaction = nb_transaction_new(candidate, &changes, client, comment);
590 if (*transaction == NULL) {
591 flog_warn(EC_LIB_NB_TRANSACTION_CREATION_FAILED,
592 "%s: failed to create transaction", __func__);
593 nb_config_diff_del_changes(&changes);
594 return NB_ERR_LOCKED;
595 }
596
597 return nb_transaction_process(NB_EV_PREPARE, *transaction);
598 }
599
600 void nb_candidate_commit_abort(struct nb_transaction *transaction)
601 {
602 (void)nb_transaction_process(NB_EV_ABORT, transaction);
603 nb_transaction_free(transaction);
604 }
605
606 void nb_candidate_commit_apply(struct nb_transaction *transaction,
607 bool save_transaction, uint32_t *transaction_id)
608 {
609 (void)nb_transaction_process(NB_EV_APPLY, transaction);
610 nb_transaction_apply_finish(transaction);
611
612 /* Replace running by candidate. */
613 transaction->config->version++;
614 nb_config_replace(running_config, transaction->config, true);
615
616 /* Record transaction. */
617 if (save_transaction
618 && nb_db_transaction_save(transaction, transaction_id) != NB_OK)
619 flog_warn(EC_LIB_NB_TRANSACTION_RECORD_FAILED,
620 "%s: failed to record transaction", __func__);
621
622 nb_transaction_free(transaction);
623 }
624
625 int nb_candidate_commit(struct nb_config *candidate, enum nb_client client,
626 bool save_transaction, const char *comment,
627 uint32_t *transaction_id)
628 {
629 struct nb_transaction *transaction = NULL;
630 int ret;
631
632 ret = nb_candidate_commit_prepare(candidate, client, comment,
633 &transaction);
634 /*
635 * Apply the changes if the preparation phase succeeded. Otherwise abort
636 * the transaction.
637 */
638 if (ret == NB_OK)
639 nb_candidate_commit_apply(transaction, save_transaction,
640 transaction_id);
641 else if (transaction != NULL)
642 nb_candidate_commit_abort(transaction);
643
644 return ret;
645 }
646
647 static void nb_log_callback(const enum nb_event event,
648 enum nb_operation operation, const char *xpath,
649 const char *value)
650 {
651 zlog_debug(
652 "northbound callback: event [%s] op [%s] xpath [%s] value [%s]",
653 nb_event_name(event), nb_operation_name(operation), xpath,
654 value);
655 }
656
657 /*
658 * Call the northbound configuration callback associated to a given
659 * configuration change.
660 */
661 static int nb_configuration_callback(const enum nb_event event,
662 struct nb_config_change *change)
663 {
664 enum nb_operation operation = change->cb.operation;
665 const char *xpath = change->cb.xpath;
666 const struct nb_node *nb_node = change->cb.nb_node;
667 const struct lyd_node *dnode = change->cb.dnode;
668 union nb_resource *resource;
669 int ret = NB_ERR;
670
671 if (debug_northbound) {
672 const char *value = "(none)";
673
674 if (dnode && !yang_snode_is_typeless_data(dnode->schema))
675 value = yang_dnode_get_string(dnode, NULL);
676
677 nb_log_callback(event, operation, xpath, value);
678 }
679
680 if (event == NB_EV_VALIDATE)
681 resource = NULL;
682 else
683 resource = &change->resource;
684
685 switch (operation) {
686 case NB_OP_CREATE:
687 ret = (*nb_node->cbs.create)(event, dnode, resource);
688 break;
689 case NB_OP_MODIFY:
690 ret = (*nb_node->cbs.modify)(event, dnode, resource);
691 break;
692 case NB_OP_DELETE:
693 ret = (*nb_node->cbs.delete)(event, dnode);
694 break;
695 case NB_OP_MOVE:
696 ret = (*nb_node->cbs.move)(event, dnode);
697 break;
698 default:
699 break;
700 }
701
702 if (ret != NB_OK)
703 flog_warn(
704 EC_LIB_NB_CB_CONFIG,
705 "%s: error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]",
706 __func__, nb_err_name(ret), nb_event_name(event),
707 nb_operation_name(operation), xpath);
708
709 return ret;
710 }
711
712 static struct nb_transaction *nb_transaction_new(struct nb_config *config,
713 struct nb_config_cbs *changes,
714 enum nb_client client,
715 const char *comment)
716 {
717 struct nb_transaction *transaction;
718
719 if (transaction_in_progress) {
720 flog_warn(
721 EC_LIB_NB_TRANSACTION_CREATION_FAILED,
722 "%s: error - there's already another transaction in progress",
723 __func__);
724 return NULL;
725 }
726 transaction_in_progress = true;
727
728 transaction = XCALLOC(MTYPE_TMP, sizeof(*transaction));
729 transaction->client = client;
730 if (comment)
731 strlcpy(transaction->comment, comment,
732 sizeof(transaction->comment));
733 transaction->config = config;
734 transaction->changes = *changes;
735
736 return transaction;
737 }
738
739 static void nb_transaction_free(struct nb_transaction *transaction)
740 {
741 nb_config_diff_del_changes(&transaction->changes);
742 XFREE(MTYPE_TMP, transaction);
743 transaction_in_progress = false;
744 }
745
746 /* Process all configuration changes associated to a transaction. */
747 static int nb_transaction_process(enum nb_event event,
748 struct nb_transaction *transaction)
749 {
750 struct nb_config_cb *cb;
751
752 RB_FOREACH (cb, nb_config_cbs, &transaction->changes) {
753 struct nb_config_change *change = (struct nb_config_change *)cb;
754 int ret;
755
756 /*
757 * Only try to release resources that were allocated
758 * successfully.
759 */
760 if (event == NB_EV_ABORT && change->prepare_ok == false)
761 break;
762
763 /* Call the appropriate callback. */
764 ret = nb_configuration_callback(event, change);
765 switch (event) {
766 case NB_EV_PREPARE:
767 if (ret != NB_OK)
768 return ret;
769 change->prepare_ok = true;
770 break;
771 case NB_EV_ABORT:
772 case NB_EV_APPLY:
773 /*
774 * At this point it's not possible to reject the
775 * transaction anymore, so any failure here can lead to
776 * inconsistencies and should be treated as a bug.
777 * Operations prone to errors, like validations and
778 * resource allocations, should be performed during the
779 * 'prepare' phase.
780 */
781 break;
782 default:
783 break;
784 }
785 }
786
787 return NB_OK;
788 }
789
790 static struct nb_config_cb *
791 nb_apply_finish_cb_new(struct nb_config_cbs *cbs, const char *xpath,
792 const struct nb_node *nb_node,
793 const struct lyd_node *dnode)
794 {
795 struct nb_config_cb *cb;
796
797 cb = XCALLOC(MTYPE_TMP, sizeof(*cb));
798 strlcpy(cb->xpath, xpath, sizeof(cb->xpath));
799 cb->nb_node = nb_node;
800 cb->dnode = dnode;
801 RB_INSERT(nb_config_cbs, cbs, cb);
802
803 return cb;
804 }
805
806 static struct nb_config_cb *
807 nb_apply_finish_cb_find(struct nb_config_cbs *cbs, const char *xpath,
808 const struct nb_node *nb_node)
809 {
810 struct nb_config_cb s;
811
812 strlcpy(s.xpath, xpath, sizeof(s.xpath));
813 s.nb_node = nb_node;
814 return RB_FIND(nb_config_cbs, cbs, &s);
815 }
816
817 /* Call the 'apply_finish' callbacks. */
818 static void nb_transaction_apply_finish(struct nb_transaction *transaction)
819 {
820 struct nb_config_cbs cbs;
821 struct nb_config_cb *cb;
822
823 /* Initialize tree of 'apply_finish' callbacks. */
824 RB_INIT(nb_config_cbs, &cbs);
825
826 /* Identify the 'apply_finish' callbacks that need to be called. */
827 RB_FOREACH (cb, nb_config_cbs, &transaction->changes) {
828 struct nb_config_change *change = (struct nb_config_change *)cb;
829 const struct lyd_node *dnode = change->cb.dnode;
830
831 /*
832 * Iterate up to the root of the data tree. When a node is being
833 * deleted, skip its 'apply_finish' callback if one is defined
834 * (the 'apply_finish' callbacks from the node ancestors should
835 * be called though).
836 */
837 if (change->cb.operation == NB_OP_DELETE) {
838 char xpath[XPATH_MAXLEN];
839
840 dnode = dnode->parent;
841 if (!dnode)
842 break;
843
844 /*
845 * The dnode from 'delete' callbacks point to elements
846 * from the running configuration. Use yang_dnode_get()
847 * to get the corresponding dnode from the candidate
848 * configuration that is being committed.
849 */
850 yang_dnode_get_path(dnode, xpath, sizeof(xpath));
851 dnode = yang_dnode_get(transaction->config->dnode,
852 xpath);
853 }
854 while (dnode) {
855 char xpath[XPATH_MAXLEN];
856 struct nb_node *nb_node;
857
858 nb_node = dnode->schema->priv;
859 if (!nb_node->cbs.apply_finish)
860 goto next;
861
862 /*
863 * Don't call the callback more than once for the same
864 * data node.
865 */
866 yang_dnode_get_path(dnode, xpath, sizeof(xpath));
867 if (nb_apply_finish_cb_find(&cbs, xpath, nb_node))
868 goto next;
869
870 nb_apply_finish_cb_new(&cbs, xpath, nb_node, dnode);
871
872 next:
873 dnode = dnode->parent;
874 }
875 }
876
877 /* Call the 'apply_finish' callbacks, sorted by their priorities. */
878 RB_FOREACH (cb, nb_config_cbs, &cbs) {
879 if (debug_northbound)
880 nb_log_callback(NB_EV_APPLY, NB_OP_APPLY_FINISH,
881 cb->xpath, NULL);
882
883 (*cb->nb_node->cbs.apply_finish)(cb->dnode);
884 }
885
886 /* Release memory. */
887 while (!RB_EMPTY(nb_config_cbs, &cbs)) {
888 cb = RB_ROOT(nb_config_cbs, &cbs);
889 RB_REMOVE(nb_config_cbs, &cbs, cb);
890 XFREE(MTYPE_TMP, cb);
891 }
892 }
893
894 bool nb_operation_is_valid(enum nb_operation operation,
895 const struct lys_node *snode)
896 {
897 struct lys_node_container *scontainer;
898 struct lys_node_leaf *sleaf;
899
900 switch (operation) {
901 case NB_OP_CREATE:
902 if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
903 return false;
904
905 switch (snode->nodetype) {
906 case LYS_LEAF:
907 sleaf = (struct lys_node_leaf *)snode;
908 if (sleaf->type.base != LY_TYPE_EMPTY)
909 return false;
910 break;
911 case LYS_CONTAINER:
912 scontainer = (struct lys_node_container *)snode;
913 if (!scontainer->presence)
914 return false;
915 break;
916 case LYS_LIST:
917 case LYS_LEAFLIST:
918 break;
919 default:
920 return false;
921 }
922 return true;
923 case NB_OP_MODIFY:
924 if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
925 return false;
926
927 switch (snode->nodetype) {
928 case LYS_LEAF:
929 sleaf = (struct lys_node_leaf *)snode;
930 if (sleaf->type.base == LY_TYPE_EMPTY)
931 return false;
932
933 /* List keys can't be modified. */
934 if (lys_is_key(sleaf, NULL))
935 return false;
936 break;
937 default:
938 return false;
939 }
940 return true;
941 case NB_OP_DELETE:
942 if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
943 return false;
944
945 switch (snode->nodetype) {
946 case LYS_LEAF:
947 sleaf = (struct lys_node_leaf *)snode;
948
949 /* List keys can't be deleted. */
950 if (lys_is_key(sleaf, NULL))
951 return false;
952
953 /*
954 * Only optional leafs can be deleted, or leafs whose
955 * parent is a case statement.
956 */
957 if (snode->parent->nodetype == LYS_CASE)
958 return true;
959 if (sleaf->when)
960 return true;
961 if (CHECK_FLAG(sleaf->flags, LYS_MAND_TRUE)
962 || sleaf->dflt)
963 return false;
964 break;
965 case LYS_CONTAINER:
966 scontainer = (struct lys_node_container *)snode;
967 if (!scontainer->presence)
968 return false;
969 break;
970 case LYS_LIST:
971 case LYS_LEAFLIST:
972 break;
973 default:
974 return false;
975 }
976 return true;
977 case NB_OP_MOVE:
978 if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
979 return false;
980
981 switch (snode->nodetype) {
982 case LYS_LIST:
983 case LYS_LEAFLIST:
984 if (!CHECK_FLAG(snode->flags, LYS_USERORDERED))
985 return false;
986 break;
987 default:
988 return false;
989 }
990 return true;
991 case NB_OP_APPLY_FINISH:
992 if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
993 return false;
994 return true;
995 case NB_OP_GET_ELEM:
996 if (!CHECK_FLAG(snode->flags, LYS_CONFIG_R))
997 return false;
998
999 switch (snode->nodetype) {
1000 case LYS_LEAF:
1001 break;
1002 case LYS_CONTAINER:
1003 scontainer = (struct lys_node_container *)snode;
1004 if (!scontainer->presence)
1005 return false;
1006 break;
1007 default:
1008 return false;
1009 }
1010 return true;
1011 case NB_OP_GET_NEXT:
1012 case NB_OP_GET_KEYS:
1013 case NB_OP_LOOKUP_ENTRY:
1014 if (!CHECK_FLAG(snode->flags, LYS_CONFIG_R))
1015 return false;
1016
1017 switch (snode->nodetype) {
1018 case LYS_LIST:
1019 break;
1020 default:
1021 return false;
1022 }
1023 return true;
1024 case NB_OP_RPC:
1025 if (CHECK_FLAG(snode->flags, LYS_CONFIG_W | LYS_CONFIG_R))
1026 return false;
1027
1028 switch (snode->nodetype) {
1029 case LYS_RPC:
1030 case LYS_ACTION:
1031 break;
1032 default:
1033 return false;
1034 }
1035 return true;
1036 default:
1037 return false;
1038 }
1039 }
1040
1041 DEFINE_HOOK(nb_notification_send, (const char *xpath, struct list *arguments),
1042 (xpath, arguments));
1043
1044 int nb_notification_send(const char *xpath, struct list *arguments)
1045 {
1046 int ret;
1047
1048 ret = hook_call(nb_notification_send, xpath, arguments);
1049 if (arguments)
1050 list_delete(&arguments);
1051
1052 return ret;
1053 }
1054
1055 const char *nb_event_name(enum nb_event event)
1056 {
1057 switch (event) {
1058 case NB_EV_VALIDATE:
1059 return "validate";
1060 case NB_EV_PREPARE:
1061 return "prepare";
1062 case NB_EV_ABORT:
1063 return "abort";
1064 case NB_EV_APPLY:
1065 return "apply";
1066 default:
1067 return "unknown";
1068 }
1069 }
1070
1071 const char *nb_operation_name(enum nb_operation operation)
1072 {
1073 switch (operation) {
1074 case NB_OP_CREATE:
1075 return "create";
1076 case NB_OP_MODIFY:
1077 return "modify";
1078 case NB_OP_DELETE:
1079 return "delete";
1080 case NB_OP_MOVE:
1081 return "move";
1082 case NB_OP_APPLY_FINISH:
1083 return "apply_finish";
1084 case NB_OP_GET_ELEM:
1085 return "get_elem";
1086 case NB_OP_GET_NEXT:
1087 return "get_next";
1088 case NB_OP_GET_KEYS:
1089 return "get_keys";
1090 case NB_OP_LOOKUP_ENTRY:
1091 return "lookup_entry";
1092 case NB_OP_RPC:
1093 return "rpc";
1094 default:
1095 return "unknown";
1096 }
1097 }
1098
1099 const char *nb_err_name(enum nb_error error)
1100 {
1101 switch (error) {
1102 case NB_OK:
1103 return "ok";
1104 case NB_ERR:
1105 return "generic error";
1106 case NB_ERR_NO_CHANGES:
1107 return "no changes";
1108 case NB_ERR_NOT_FOUND:
1109 return "element not found";
1110 case NB_ERR_LOCKED:
1111 return "resource is locked";
1112 case NB_ERR_VALIDATION:
1113 return "validation error";
1114 case NB_ERR_RESOURCE:
1115 return "failed to allocate resource";
1116 case NB_ERR_INCONSISTENCY:
1117 return "internal inconsistency";
1118 default:
1119 return "unknown";
1120 }
1121 }
1122
1123 const char *nb_client_name(enum nb_client client)
1124 {
1125 switch (client) {
1126 case NB_CLIENT_CLI:
1127 return "CLI";
1128 case NB_CLIENT_CONFD:
1129 return "ConfD";
1130 case NB_CLIENT_SYSREPO:
1131 return "Sysrepo";
1132 default:
1133 return "unknown";
1134 }
1135 }
1136
1137 static void nb_load_callbacks(const struct frr_yang_module_info *module)
1138 {
1139 for (size_t i = 0; module->nodes[i].xpath; i++) {
1140 struct nb_node *nb_node;
1141 uint32_t priority;
1142
1143 nb_node = nb_node_find(module->nodes[i].xpath);
1144 if (!nb_node) {
1145 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
1146 "%s: unknown data path: %s", __func__,
1147 module->nodes[i].xpath);
1148 continue;
1149 }
1150
1151 nb_node->cbs = module->nodes[i].cbs;
1152 priority = module->nodes[i].priority;
1153 if (priority != 0)
1154 nb_node->priority = priority;
1155 }
1156 }
1157
1158 void nb_init(const struct frr_yang_module_info *modules[], size_t nmodules)
1159 {
1160 unsigned int errors = 0;
1161
1162 /* Load YANG modules. */
1163 for (size_t i = 0; i < nmodules; i++)
1164 yang_module_load(modules[i]->name);
1165
1166 /* Create a nb_node for all YANG schema nodes. */
1167 yang_all_snodes_iterate(nb_node_new_cb, 0, NULL, NULL);
1168
1169 /* Load northbound callbacks. */
1170 for (size_t i = 0; i < nmodules; i++)
1171 nb_load_callbacks(modules[i]);
1172
1173 /* Validate northbound callbacks. */
1174 yang_all_snodes_iterate(nb_node_validate, 0, &errors, NULL);
1175 if (errors > 0) {
1176 flog_err(
1177 EC_LIB_NB_CBS_VALIDATION,
1178 "%s: failed to validate northbound callbacks: %u error(s)",
1179 __func__, errors);
1180 exit(1);
1181 }
1182
1183 /* Initialize the northbound database (used for the rollback log). */
1184 if (nb_db_init() != NB_OK)
1185 flog_warn(EC_LIB_NB_DATABASE,
1186 "%s: failed to initialize northbound database",
1187 __func__);
1188
1189 /* Create an empty running configuration. */
1190 running_config = nb_config_new(NULL);
1191
1192 /* Initialize the northbound CLI. */
1193 nb_cli_init();
1194 }
1195
1196 void nb_terminate(void)
1197 {
1198 /* Terminate the northbound CLI. */
1199 nb_cli_terminate();
1200
1201 /* Delete all nb_node's from all YANG modules. */
1202 yang_all_snodes_iterate(nb_node_del_cb, 0, NULL, NULL);
1203
1204 /* Delete the running configuration. */
1205 nb_config_free(running_config);
1206 }