Line data Source code
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 "frr_pthread.h"
30 : #include "northbound.h"
31 : #include "northbound_cli.h"
32 : #include "northbound_db.h"
33 : #include "frrstr.h"
34 :
35 18 : DEFINE_MTYPE_STATIC(LIB, NB_NODE, "Northbound Node");
36 18 : DEFINE_MTYPE_STATIC(LIB, NB_CONFIG, "Northbound Configuration");
37 18 : DEFINE_MTYPE_STATIC(LIB, NB_CONFIG_ENTRY, "Northbound Configuration Entry");
38 :
39 : /* Running configuration - shouldn't be modified directly. */
40 : struct nb_config *running_config;
41 :
42 : /* Hash table of user pointers associated with configuration entries. */
43 : static struct hash *running_config_entries;
44 :
45 : /* Management lock for the running configuration. */
46 : static struct {
47 : /* Mutex protecting this structure. */
48 : pthread_mutex_t mtx;
49 :
50 : /* Actual lock. */
51 : bool locked;
52 :
53 : /* Northbound client who owns this lock. */
54 : enum nb_client owner_client;
55 :
56 : /* Northbound user who owns this lock. */
57 : const void *owner_user;
58 : } running_config_mgmt_lock;
59 :
60 : /* Knob to record config transaction */
61 : static bool nb_db_enabled;
62 : /*
63 : * Global lock used to prevent multiple configuration transactions from
64 : * happening concurrently.
65 : */
66 : static bool transaction_in_progress;
67 :
68 : static int nb_callback_pre_validate(struct nb_context *context,
69 : const struct nb_node *nb_node,
70 : const struct lyd_node *dnode, char *errmsg,
71 : size_t errmsg_len);
72 : static int nb_callback_configuration(struct nb_context *context,
73 : const enum nb_event event,
74 : struct nb_config_change *change,
75 : char *errmsg, size_t errmsg_len);
76 : static struct nb_transaction *
77 : nb_transaction_new(struct nb_context *context, struct nb_config *config,
78 : struct nb_config_cbs *changes, const char *comment,
79 : char *errmsg, size_t errmsg_len);
80 : static void nb_transaction_free(struct nb_transaction *transaction);
81 : static int nb_transaction_process(enum nb_event event,
82 : struct nb_transaction *transaction,
83 : char *errmsg, size_t errmsg_len);
84 : static void nb_transaction_apply_finish(struct nb_transaction *transaction,
85 : char *errmsg, size_t errmsg_len);
86 : static int nb_oper_data_iter_node(const struct lysc_node *snode,
87 : const char *xpath, const void *list_entry,
88 : const struct yang_list_keys *list_keys,
89 : struct yang_translator *translator,
90 : bool first, uint32_t flags,
91 : nb_oper_data_cb cb, void *arg);
92 :
93 6330 : static int nb_node_check_config_only(const struct lysc_node *snode, void *arg)
94 : {
95 6330 : bool *config_only = arg;
96 :
97 6330 : if (CHECK_FLAG(snode->flags, LYS_CONFIG_R)) {
98 75 : *config_only = false;
99 75 : return YANG_ITER_STOP;
100 : }
101 :
102 : return YANG_ITER_CONTINUE;
103 : }
104 :
105 2703 : static int nb_node_new_cb(const struct lysc_node *snode, void *arg)
106 : {
107 2703 : struct nb_node *nb_node;
108 2703 : struct lysc_node *sparent, *sparent_list;
109 :
110 2703 : nb_node = XCALLOC(MTYPE_NB_NODE, sizeof(*nb_node));
111 2703 : yang_snode_get_path(snode, YANG_PATH_DATA, nb_node->xpath,
112 : sizeof(nb_node->xpath));
113 2703 : nb_node->priority = NB_DFLT_PRIORITY;
114 2703 : sparent = yang_snode_real_parent(snode);
115 2703 : if (sparent)
116 1986 : nb_node->parent = sparent->priv;
117 2703 : sparent_list = yang_snode_parent_list(snode);
118 2703 : if (sparent_list)
119 1986 : nb_node->parent_list = sparent_list->priv;
120 :
121 : /* Set flags. */
122 2703 : if (CHECK_FLAG(snode->nodetype, LYS_CONTAINER | LYS_LIST)) {
123 309 : bool config_only = true;
124 :
125 309 : (void)yang_snodes_iterate_subtree(snode, NULL,
126 : nb_node_check_config_only, 0,
127 : &config_only);
128 309 : if (config_only)
129 234 : SET_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY);
130 : }
131 2703 : if (CHECK_FLAG(snode->nodetype, LYS_LIST)) {
132 153 : if (yang_snode_num_keys(snode) == 0)
133 54 : SET_FLAG(nb_node->flags, F_NB_NODE_KEYLESS_LIST);
134 : }
135 :
136 : /*
137 : * Link the northbound node and the libyang schema node with one
138 : * another.
139 : */
140 2703 : nb_node->snode = snode;
141 2703 : assert(snode->priv == NULL);
142 2703 : ((struct lysc_node *)snode)->priv = nb_node;
143 :
144 2703 : return YANG_ITER_CONTINUE;
145 : }
146 :
147 5406 : static int nb_node_del_cb(const struct lysc_node *snode, void *arg)
148 : {
149 5406 : struct nb_node *nb_node;
150 :
151 5406 : nb_node = snode->priv;
152 5406 : if (nb_node) {
153 5406 : ((struct lysc_node *)snode)->priv = NULL;
154 5406 : XFREE(MTYPE_NB_NODE, nb_node);
155 : }
156 :
157 5406 : return YANG_ITER_CONTINUE;
158 : }
159 :
160 0 : void nb_nodes_create(void)
161 : {
162 0 : yang_snodes_iterate(NULL, nb_node_new_cb, 0, NULL);
163 0 : }
164 :
165 12 : void nb_nodes_delete(void)
166 : {
167 12 : yang_snodes_iterate(NULL, nb_node_del_cb, 0, NULL);
168 12 : }
169 :
170 897 : struct nb_node *nb_node_find(const char *path)
171 : {
172 897 : const struct lysc_node *snode;
173 :
174 : /*
175 : * Use libyang to find the schema node associated to the path and get
176 : * the northbound node from there (snode private pointer).
177 : */
178 897 : snode = lys_find_path(ly_native_ctx, NULL, path, 0);
179 897 : if (!snode)
180 : return NULL;
181 :
182 897 : return snode->priv;
183 : }
184 :
185 0 : void nb_node_set_dependency_cbs(const char *dependency_xpath,
186 : const char *dependant_xpath,
187 : struct nb_dependency_callbacks *cbs)
188 : {
189 0 : struct nb_node *dependency = nb_node_find(dependency_xpath);
190 0 : struct nb_node *dependant = nb_node_find(dependant_xpath);
191 :
192 0 : if (!dependency || !dependant)
193 : return;
194 :
195 0 : dependency->dep_cbs.get_dependant_xpath = cbs->get_dependant_xpath;
196 0 : dependant->dep_cbs.get_dependency_xpath = cbs->get_dependency_xpath;
197 : }
198 :
199 0 : bool nb_node_has_dependency(struct nb_node *node)
200 : {
201 0 : return node->dep_cbs.get_dependency_xpath != NULL;
202 : }
203 :
204 29733 : static int nb_node_validate_cb(const struct nb_node *nb_node,
205 : enum nb_operation operation,
206 : int callback_implemented, bool optional)
207 : {
208 29733 : bool valid;
209 :
210 29733 : valid = nb_operation_is_valid(operation, nb_node->snode);
211 :
212 : /*
213 : * Add an exception for operational data callbacks. A rw list usually
214 : * doesn't need any associated operational data callbacks. But if this
215 : * rw list is augmented by another module which adds state nodes under
216 : * it, then this list will need to have the 'get_next()', 'get_keys()'
217 : * and 'lookup_entry()' callbacks. As such, never log a warning when
218 : * these callbacks are implemented when they are not needed, since this
219 : * depends on context (e.g. some daemons might augment "frr-interface"
220 : * while others don't).
221 : */
222 29733 : if (!valid && callback_implemented && operation != NB_OP_GET_NEXT
223 0 : && operation != NB_OP_GET_KEYS && operation != NB_OP_LOOKUP_ENTRY)
224 0 : flog_warn(EC_LIB_NB_CB_UNNEEDED,
225 : "unneeded '%s' callback for '%s'",
226 : nb_operation_name(operation), nb_node->xpath);
227 :
228 29733 : if (!optional && valid && !callback_implemented) {
229 0 : flog_err(EC_LIB_NB_CB_MISSING, "missing '%s' callback for '%s'",
230 : nb_operation_name(operation), nb_node->xpath);
231 0 : return 1;
232 : }
233 :
234 : return 0;
235 : }
236 :
237 : /*
238 : * Check if the required callbacks were implemented for the given northbound
239 : * node.
240 : */
241 2703 : static unsigned int nb_node_validate_cbs(const struct nb_node *nb_node)
242 :
243 : {
244 2703 : unsigned int error = 0;
245 :
246 5406 : error += nb_node_validate_cb(nb_node, NB_OP_CREATE,
247 2703 : !!nb_node->cbs.create, false);
248 5406 : error += nb_node_validate_cb(nb_node, NB_OP_MODIFY,
249 2703 : !!nb_node->cbs.modify, false);
250 5406 : error += nb_node_validate_cb(nb_node, NB_OP_DESTROY,
251 2703 : !!nb_node->cbs.destroy, false);
252 2703 : error += nb_node_validate_cb(nb_node, NB_OP_MOVE, !!nb_node->cbs.move,
253 : false);
254 5406 : error += nb_node_validate_cb(nb_node, NB_OP_PRE_VALIDATE,
255 2703 : !!nb_node->cbs.pre_validate, true);
256 5406 : error += nb_node_validate_cb(nb_node, NB_OP_APPLY_FINISH,
257 2703 : !!nb_node->cbs.apply_finish, true);
258 5406 : error += nb_node_validate_cb(nb_node, NB_OP_GET_ELEM,
259 2703 : !!nb_node->cbs.get_elem, false);
260 5406 : error += nb_node_validate_cb(nb_node, NB_OP_GET_NEXT,
261 2703 : !!nb_node->cbs.get_next, false);
262 5406 : error += nb_node_validate_cb(nb_node, NB_OP_GET_KEYS,
263 2703 : !!nb_node->cbs.get_keys, false);
264 5406 : error += nb_node_validate_cb(nb_node, NB_OP_LOOKUP_ENTRY,
265 2703 : !!nb_node->cbs.lookup_entry, false);
266 2703 : error += nb_node_validate_cb(nb_node, NB_OP_RPC, !!nb_node->cbs.rpc,
267 : false);
268 :
269 2703 : return error;
270 : }
271 :
272 2703 : static unsigned int nb_node_validate_priority(const struct nb_node *nb_node)
273 : {
274 : /* Top-level nodes can have any priority. */
275 2703 : if (!nb_node->parent)
276 : return 0;
277 :
278 1971 : if (nb_node->priority < nb_node->parent->priority) {
279 0 : flog_err(EC_LIB_NB_CB_INVALID_PRIO,
280 : "node has higher priority than its parent [xpath %s]",
281 : nb_node->xpath);
282 0 : return 1;
283 : }
284 :
285 : return 0;
286 : }
287 :
288 2703 : static int nb_node_validate(const struct lysc_node *snode, void *arg)
289 : {
290 2703 : struct nb_node *nb_node = snode->priv;
291 2703 : unsigned int *errors = arg;
292 :
293 : /* Validate callbacks and priority. */
294 2703 : if (nb_node) {
295 2703 : *errors += nb_node_validate_cbs(nb_node);
296 2703 : *errors += nb_node_validate_priority(nb_node);
297 : }
298 :
299 2703 : return YANG_ITER_CONTINUE;
300 : }
301 :
302 12 : struct nb_config *nb_config_new(struct lyd_node *dnode)
303 : {
304 12 : struct nb_config *config;
305 :
306 12 : config = XCALLOC(MTYPE_NB_CONFIG, sizeof(*config));
307 12 : if (dnode)
308 0 : config->dnode = dnode;
309 : else
310 12 : config->dnode = yang_dnode_new(ly_native_ctx, true);
311 12 : config->version = 0;
312 :
313 12 : return config;
314 : }
315 :
316 24 : void nb_config_free(struct nb_config *config)
317 : {
318 24 : if (config->dnode)
319 24 : yang_dnode_free(config->dnode);
320 24 : XFREE(MTYPE_NB_CONFIG, config);
321 24 : }
322 :
323 0 : struct nb_config *nb_config_dup(const struct nb_config *config)
324 : {
325 0 : struct nb_config *dup;
326 :
327 0 : dup = XCALLOC(MTYPE_NB_CONFIG, sizeof(*dup));
328 0 : dup->dnode = yang_dnode_dup(config->dnode);
329 0 : dup->version = config->version;
330 :
331 0 : return dup;
332 : }
333 :
334 0 : int nb_config_merge(struct nb_config *config_dst, struct nb_config *config_src,
335 : bool preserve_source)
336 : {
337 0 : int ret;
338 :
339 0 : ret = lyd_merge_siblings(&config_dst->dnode, config_src->dnode, 0);
340 0 : if (ret != 0)
341 0 : flog_warn(EC_LIB_LIBYANG, "%s: lyd_merge() failed", __func__);
342 :
343 0 : if (!preserve_source)
344 0 : nb_config_free(config_src);
345 :
346 0 : return (ret == 0) ? NB_OK : NB_ERR;
347 : }
348 :
349 3 : void nb_config_replace(struct nb_config *config_dst,
350 : struct nb_config *config_src, bool preserve_source)
351 : {
352 : /* Update version. */
353 3 : if (config_src->version != 0)
354 3 : config_dst->version = config_src->version;
355 :
356 : /* Update dnode. */
357 3 : if (config_dst->dnode)
358 3 : yang_dnode_free(config_dst->dnode);
359 3 : if (preserve_source) {
360 3 : config_dst->dnode = yang_dnode_dup(config_src->dnode);
361 : } else {
362 0 : config_dst->dnode = config_src->dnode;
363 0 : config_src->dnode = NULL;
364 0 : nb_config_free(config_src);
365 : }
366 3 : }
367 :
368 : /* Generate the nb_config_cbs tree. */
369 0 : static inline int nb_config_cb_compare(const struct nb_config_cb *a,
370 : const struct nb_config_cb *b)
371 : {
372 : /* Sort by priority first. */
373 0 : if (a->nb_node->priority < b->nb_node->priority)
374 : return -1;
375 0 : if (a->nb_node->priority > b->nb_node->priority)
376 : return 1;
377 :
378 : /*
379 : * Preserve the order of the configuration changes as told by libyang.
380 : */
381 0 : if (a->seq < b->seq)
382 : return -1;
383 0 : if (a->seq > b->seq)
384 : return 1;
385 :
386 : /*
387 : * All 'apply_finish' callbacks have their sequence number set to zero.
388 : * In this case, compare them using their dnode pointers (the order
389 : * doesn't matter for callbacks that have the same priority).
390 : */
391 0 : if (a->dnode < b->dnode)
392 : return -1;
393 0 : if (a->dnode > b->dnode)
394 0 : return 1;
395 :
396 : return 0;
397 : }
398 0 : RB_GENERATE(nb_config_cbs, nb_config_cb, entry, nb_config_cb_compare);
399 :
400 3 : static void nb_config_diff_add_change(struct nb_config_cbs *changes,
401 : enum nb_operation operation,
402 : uint32_t *seq,
403 : const struct lyd_node *dnode)
404 : {
405 3 : struct nb_config_change *change;
406 :
407 : /* Ignore unimplemented nodes. */
408 3 : if (!dnode->schema->priv)
409 : return;
410 :
411 3 : change = XCALLOC(MTYPE_TMP, sizeof(*change));
412 3 : change->cb.operation = operation;
413 3 : change->cb.seq = *seq;
414 3 : *seq = *seq + 1;
415 3 : change->cb.nb_node = dnode->schema->priv;
416 3 : change->cb.dnode = dnode;
417 :
418 3 : RB_INSERT(nb_config_cbs, changes, &change->cb);
419 : }
420 :
421 3 : static void nb_config_diff_del_changes(struct nb_config_cbs *changes)
422 : {
423 3 : while (!RB_EMPTY(nb_config_cbs, changes)) {
424 3 : struct nb_config_change *change;
425 :
426 3 : change = (struct nb_config_change *)RB_ROOT(nb_config_cbs,
427 : changes);
428 3 : RB_REMOVE(nb_config_cbs, changes, &change->cb);
429 6 : XFREE(MTYPE_TMP, change);
430 : }
431 3 : }
432 :
433 : /*
434 : * Helper function used when calculating the delta between two different
435 : * configurations. Given a new subtree, calculate all new YANG data nodes,
436 : * excluding default leafs and leaf-lists. This is a recursive function.
437 : */
438 9 : static void nb_config_diff_created(const struct lyd_node *dnode, uint32_t *seq,
439 : struct nb_config_cbs *changes)
440 : {
441 9 : enum nb_operation operation;
442 9 : struct lyd_node *child;
443 :
444 : /* Ignore unimplemented nodes. */
445 9 : if (!dnode->schema->priv)
446 : return;
447 :
448 9 : switch (dnode->schema->nodetype) {
449 3 : case LYS_LEAF:
450 : case LYS_LEAFLIST:
451 3 : if (lyd_is_default(dnode))
452 : break;
453 :
454 3 : if (nb_operation_is_valid(NB_OP_CREATE, dnode->schema))
455 : operation = NB_OP_CREATE;
456 3 : else if (nb_operation_is_valid(NB_OP_MODIFY, dnode->schema))
457 : operation = NB_OP_MODIFY;
458 : else
459 : return;
460 :
461 0 : nb_config_diff_add_change(changes, operation, seq, dnode);
462 0 : break;
463 6 : case LYS_CONTAINER:
464 : case LYS_LIST:
465 6 : if (nb_operation_is_valid(NB_OP_CREATE, dnode->schema))
466 3 : nb_config_diff_add_change(changes, NB_OP_CREATE, seq,
467 : dnode);
468 :
469 : /* Process child nodes recursively. */
470 12 : LY_LIST_FOR (lyd_child(dnode), child) {
471 6 : nb_config_diff_created(child, seq, changes);
472 : }
473 : break;
474 : default:
475 : break;
476 : }
477 : }
478 :
479 0 : static void nb_config_diff_deleted(const struct lyd_node *dnode, uint32_t *seq,
480 : struct nb_config_cbs *changes)
481 : {
482 : /* Ignore unimplemented nodes. */
483 0 : if (!dnode->schema->priv)
484 : return;
485 :
486 0 : if (nb_operation_is_valid(NB_OP_DESTROY, dnode->schema))
487 0 : nb_config_diff_add_change(changes, NB_OP_DESTROY, seq, dnode);
488 0 : else if (CHECK_FLAG(dnode->schema->nodetype, LYS_CONTAINER)) {
489 0 : struct lyd_node *child;
490 :
491 : /*
492 : * Non-presence containers need special handling since they
493 : * don't have "destroy" callbacks. In this case, what we need to
494 : * do is to call the "destroy" callbacks of their child nodes
495 : * when applicable (i.e. optional nodes).
496 : */
497 0 : LY_LIST_FOR (lyd_child(dnode), child) {
498 0 : nb_config_diff_deleted(child, seq, changes);
499 : }
500 : }
501 : }
502 :
503 6 : static int nb_lyd_diff_get_op(const struct lyd_node *dnode)
504 : {
505 6 : const struct lyd_meta *meta;
506 6 : LY_LIST_FOR (dnode->meta, meta) {
507 6 : if (strcmp(meta->name, "operation")
508 6 : || strcmp(meta->annotation->module->name, "yang"))
509 0 : continue;
510 6 : return lyd_get_meta_value(meta)[0];
511 : }
512 : return 'n';
513 : }
514 :
515 : #if 0 /* Used below in nb_config_diff inside normally disabled code */
516 : static inline void nb_config_diff_dnode_log_path(const char *context,
517 : const char *path,
518 : const struct lyd_node *dnode)
519 : {
520 : if (dnode->schema->nodetype & LYD_NODE_TERM)
521 : zlog_debug("nb_config_diff: %s: %s: %s", context, path,
522 : lyd_get_value(dnode));
523 : else
524 : zlog_debug("nb_config_diff: %s: %s", context, path);
525 : }
526 :
527 : static inline void nb_config_diff_dnode_log(const char *context,
528 : const struct lyd_node *dnode)
529 : {
530 : if (!dnode) {
531 : zlog_debug("nb_config_diff: %s: NULL", context);
532 : return;
533 : }
534 :
535 : char *path = lyd_path(dnode, LYD_PATH_STD, NULL, 0);
536 : nb_config_diff_dnode_log_path(context, path, dnode);
537 : free(path);
538 : }
539 : #endif
540 :
541 : /* Calculate the delta between two different configurations. */
542 3 : static void nb_config_diff(const struct nb_config *config1,
543 : const struct nb_config *config2,
544 : struct nb_config_cbs *changes)
545 : {
546 3 : struct lyd_node *diff = NULL;
547 3 : const struct lyd_node *root, *dnode;
548 3 : struct lyd_node *target;
549 3 : int op;
550 3 : LY_ERR err;
551 3 : char *path;
552 :
553 : #if 0 /* Useful (noisy) when debugging diff code, and for improving later */
554 : if (DEBUG_MODE_CHECK(&nb_dbg_cbs_config, DEBUG_MODE_ALL)) {
555 : LY_LIST_FOR(config1->dnode, root) {
556 : LYD_TREE_DFS_BEGIN(root, dnode) {
557 : nb_config_diff_dnode_log("from", dnode);
558 : LYD_TREE_DFS_END(root, dnode);
559 : }
560 : }
561 : LY_LIST_FOR(config2->dnode, root) {
562 : LYD_TREE_DFS_BEGIN(root, dnode) {
563 : nb_config_diff_dnode_log("to", dnode);
564 : LYD_TREE_DFS_END(root, dnode);
565 : }
566 : }
567 : }
568 : #endif
569 :
570 3 : err = lyd_diff_siblings(config1->dnode, config2->dnode,
571 : LYD_DIFF_DEFAULTS, &diff);
572 3 : assert(!err);
573 :
574 3 : if (diff && DEBUG_MODE_CHECK(&nb_dbg_cbs_config, DEBUG_MODE_ALL)) {
575 0 : char *s;
576 :
577 0 : if (!lyd_print_mem(&s, diff, LYD_JSON,
578 : LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_ALL)) {
579 0 : zlog_debug("%s: %s", __func__, s);
580 0 : free(s);
581 : }
582 : }
583 :
584 3 : uint32_t seq = 0;
585 :
586 6 : LY_LIST_FOR (diff, root) {
587 9 : LYD_TREE_DFS_BEGIN (root, dnode) {
588 6 : op = nb_lyd_diff_get_op(dnode);
589 :
590 6 : path = lyd_path(dnode, LYD_PATH_STD, NULL, 0);
591 :
592 : #if 0 /* Useful (noisy) when debugging diff code, and for improving later */
593 : if (DEBUG_MODE_CHECK(&nb_dbg_cbs_config, DEBUG_MODE_ALL)) {
594 : char context[80];
595 : snprintf(context, sizeof(context),
596 : "iterating diff: oper: %c seq: %u", op, seq);
597 : nb_config_diff_dnode_log_path(context, path, dnode);
598 : }
599 : #endif
600 6 : switch (op) {
601 3 : case 'c': /* create */
602 : /*
603 : * This is rather inefficient, but when we use
604 : * dnode from the diff instead of the
605 : * candidate config node we get failures when
606 : * looking up default values, etc, based on
607 : * the diff tree.
608 : */
609 3 : target = yang_dnode_get(config2->dnode, path);
610 3 : assert(target);
611 3 : nb_config_diff_created(target, &seq, changes);
612 :
613 : /* Skip rest of sub-tree, move to next sibling
614 : */
615 3 : LYD_TREE_DFS_continue = 1;
616 3 : break;
617 0 : case 'd': /* delete */
618 0 : target = yang_dnode_get(config1->dnode, path);
619 0 : assert(target);
620 0 : nb_config_diff_deleted(target, &seq, changes);
621 :
622 : /* Skip rest of sub-tree, move to next sibling
623 : */
624 0 : LYD_TREE_DFS_continue = 1;
625 0 : break;
626 0 : case 'r': /* replace */
627 : /* either moving an entry or changing a value */
628 0 : target = yang_dnode_get(config2->dnode, path);
629 0 : assert(target);
630 0 : nb_config_diff_add_change(changes, NB_OP_MODIFY,
631 : &seq, target);
632 0 : break;
633 : case 'n': /* none */
634 : default:
635 : break;
636 : }
637 6 : free(path);
638 15 : LYD_TREE_DFS_END(root, dnode);
639 : }
640 : }
641 :
642 3 : lyd_free_all(diff);
643 3 : }
644 :
645 3 : int nb_candidate_edit(struct nb_config *candidate,
646 : const struct nb_node *nb_node,
647 : enum nb_operation operation, const char *xpath,
648 : const struct yang_data *previous,
649 : const struct yang_data *data)
650 : {
651 3 : struct lyd_node *dnode, *dep_dnode;
652 3 : char xpath_edit[XPATH_MAXLEN];
653 3 : char dep_xpath[XPATH_MAXLEN];
654 3 : LY_ERR err;
655 :
656 : /* Use special notation for leaf-lists (RFC 6020, section 9.13.5). */
657 3 : if (nb_node->snode->nodetype == LYS_LEAFLIST)
658 0 : snprintf(xpath_edit, sizeof(xpath_edit), "%s[.='%s']", xpath,
659 0 : data->value);
660 : else
661 3 : strlcpy(xpath_edit, xpath, sizeof(xpath_edit));
662 :
663 3 : switch (operation) {
664 3 : case NB_OP_CREATE:
665 : case NB_OP_MODIFY:
666 6 : err = lyd_new_path(candidate->dnode, ly_native_ctx, xpath_edit,
667 3 : (void *)data->value, LYD_NEW_PATH_UPDATE,
668 : &dnode);
669 3 : if (err) {
670 0 : flog_warn(EC_LIB_LIBYANG,
671 : "%s: lyd_new_path(%s) failed: %d", __func__,
672 : xpath_edit, err);
673 0 : return NB_ERR;
674 3 : } else if (dnode) {
675 : /* Create default nodes */
676 3 : LY_ERR err = lyd_new_implicit_tree(
677 : dnode, LYD_IMPLICIT_NO_STATE, NULL);
678 3 : if (err) {
679 0 : flog_warn(EC_LIB_LIBYANG,
680 : "%s: lyd_new_implicit_all failed: %d",
681 : __func__, err);
682 : }
683 : /*
684 : * create dependency
685 : *
686 : * dnode returned by the lyd_new_path may be from a
687 : * different schema, so we need to update the nb_node
688 : */
689 3 : nb_node = dnode->schema->priv;
690 3 : if (nb_node->dep_cbs.get_dependency_xpath) {
691 0 : nb_node->dep_cbs.get_dependency_xpath(
692 : dnode, dep_xpath);
693 :
694 0 : err = lyd_new_path(candidate->dnode,
695 : ly_native_ctx, dep_xpath,
696 : NULL, LYD_NEW_PATH_UPDATE,
697 : &dep_dnode);
698 : /* Create default nodes */
699 0 : if (!err && dep_dnode)
700 0 : err = lyd_new_implicit_tree(
701 : dep_dnode,
702 : LYD_IMPLICIT_NO_STATE, NULL);
703 0 : if (err) {
704 0 : flog_warn(
705 : EC_LIB_LIBYANG,
706 : "%s: dependency: lyd_new_path(%s) failed: %d",
707 : __func__, dep_xpath, err);
708 0 : return NB_ERR;
709 : }
710 : }
711 : }
712 : break;
713 0 : case NB_OP_DESTROY:
714 0 : dnode = yang_dnode_get(candidate->dnode, xpath_edit);
715 0 : if (!dnode)
716 : /*
717 : * Return a special error code so the caller can choose
718 : * whether to ignore it or not.
719 : */
720 : return NB_ERR_NOT_FOUND;
721 : /* destroy dependant */
722 0 : if (nb_node->dep_cbs.get_dependant_xpath) {
723 0 : nb_node->dep_cbs.get_dependant_xpath(dnode, dep_xpath);
724 :
725 0 : dep_dnode = yang_dnode_get(candidate->dnode, dep_xpath);
726 0 : if (dep_dnode)
727 0 : lyd_free_tree(dep_dnode);
728 : }
729 0 : lyd_free_tree(dnode);
730 0 : break;
731 : case NB_OP_MOVE:
732 : /* TODO: update configuration. */
733 : break;
734 0 : case NB_OP_PRE_VALIDATE:
735 : case NB_OP_APPLY_FINISH:
736 : case NB_OP_GET_ELEM:
737 : case NB_OP_GET_NEXT:
738 : case NB_OP_GET_KEYS:
739 : case NB_OP_LOOKUP_ENTRY:
740 : case NB_OP_RPC:
741 0 : flog_warn(EC_LIB_DEVELOPMENT,
742 : "%s: unknown operation (%u) [xpath %s]", __func__,
743 : operation, xpath_edit);
744 0 : return NB_ERR;
745 : }
746 :
747 : return NB_OK;
748 : }
749 :
750 0 : bool nb_candidate_needs_update(const struct nb_config *candidate)
751 : {
752 0 : if (candidate->version < running_config->version)
753 0 : return true;
754 :
755 : return false;
756 : }
757 :
758 0 : int nb_candidate_update(struct nb_config *candidate)
759 : {
760 0 : struct nb_config *updated_config;
761 :
762 0 : updated_config = nb_config_dup(running_config);
763 0 : if (nb_config_merge(updated_config, candidate, true) != NB_OK)
764 : return NB_ERR;
765 :
766 0 : nb_config_replace(candidate, updated_config, false);
767 :
768 0 : return NB_OK;
769 : }
770 :
771 : /*
772 : * Perform YANG syntactic and semantic validation.
773 : *
774 : * WARNING: lyd_validate() can change the configuration as part of the
775 : * validation process.
776 : */
777 3 : static int nb_candidate_validate_yang(struct nb_config *candidate, char *errmsg,
778 : size_t errmsg_len)
779 : {
780 3 : if (lyd_validate_all(&candidate->dnode, ly_native_ctx,
781 : LYD_VALIDATE_NO_STATE, NULL)
782 : != 0) {
783 0 : yang_print_errors(ly_native_ctx, errmsg, errmsg_len);
784 0 : return NB_ERR_VALIDATION;
785 : }
786 :
787 : return NB_OK;
788 : }
789 :
790 : /* Perform code-level validation using the northbound callbacks. */
791 3 : static int nb_candidate_validate_code(struct nb_context *context,
792 : struct nb_config *candidate,
793 : struct nb_config_cbs *changes,
794 : char *errmsg, size_t errmsg_len)
795 : {
796 3 : struct nb_config_cb *cb;
797 3 : struct lyd_node *root, *child;
798 3 : int ret;
799 :
800 : /* First validate the candidate as a whole. */
801 21 : LY_LIST_FOR (candidate->dnode, root) {
802 60 : LYD_TREE_DFS_BEGIN (root, child) {
803 51 : struct nb_node *nb_node;
804 :
805 51 : nb_node = child->schema->priv;
806 51 : if (!nb_node || !nb_node->cbs.pre_validate)
807 51 : goto next;
808 :
809 0 : ret = nb_callback_pre_validate(context, nb_node, child,
810 : errmsg, errmsg_len);
811 0 : if (ret != NB_OK)
812 : return NB_ERR_VALIDATION;
813 :
814 0 : next:
815 108 : LYD_TREE_DFS_END(root, child);
816 : }
817 : }
818 :
819 : /* Now validate the configuration changes. */
820 9 : RB_FOREACH (cb, nb_config_cbs, changes) {
821 3 : struct nb_config_change *change = (struct nb_config_change *)cb;
822 :
823 3 : ret = nb_callback_configuration(context, NB_EV_VALIDATE, change,
824 : errmsg, errmsg_len);
825 3 : if (ret != NB_OK)
826 : return NB_ERR_VALIDATION;
827 : }
828 :
829 : return NB_OK;
830 : }
831 :
832 0 : int nb_candidate_validate(struct nb_context *context,
833 : struct nb_config *candidate, char *errmsg,
834 : size_t errmsg_len)
835 : {
836 0 : struct nb_config_cbs changes;
837 0 : int ret;
838 :
839 0 : if (nb_candidate_validate_yang(candidate, errmsg, errmsg_len) != NB_OK)
840 : return NB_ERR_VALIDATION;
841 :
842 0 : RB_INIT(nb_config_cbs, &changes);
843 0 : nb_config_diff(running_config, candidate, &changes);
844 0 : ret = nb_candidate_validate_code(context, candidate, &changes, errmsg,
845 : errmsg_len);
846 0 : nb_config_diff_del_changes(&changes);
847 :
848 0 : return ret;
849 : }
850 :
851 3 : int nb_candidate_commit_prepare(struct nb_context *context,
852 : struct nb_config *candidate,
853 : const char *comment,
854 : struct nb_transaction **transaction,
855 : char *errmsg, size_t errmsg_len)
856 : {
857 3 : struct nb_config_cbs changes;
858 :
859 3 : if (nb_candidate_validate_yang(candidate, errmsg, errmsg_len)
860 : != NB_OK) {
861 0 : flog_warn(EC_LIB_NB_CANDIDATE_INVALID,
862 : "%s: failed to validate candidate configuration",
863 : __func__);
864 0 : return NB_ERR_VALIDATION;
865 : }
866 :
867 3 : RB_INIT(nb_config_cbs, &changes);
868 3 : nb_config_diff(running_config, candidate, &changes);
869 3 : if (RB_EMPTY(nb_config_cbs, &changes)) {
870 0 : snprintf(
871 : errmsg, errmsg_len,
872 : "No changes to apply were found during preparation phase");
873 0 : return NB_ERR_NO_CHANGES;
874 : }
875 :
876 3 : if (nb_candidate_validate_code(context, candidate, &changes, errmsg,
877 : errmsg_len)
878 : != NB_OK) {
879 0 : flog_warn(EC_LIB_NB_CANDIDATE_INVALID,
880 : "%s: failed to validate candidate configuration",
881 : __func__);
882 0 : nb_config_diff_del_changes(&changes);
883 0 : return NB_ERR_VALIDATION;
884 : }
885 :
886 3 : *transaction = nb_transaction_new(context, candidate, &changes, comment,
887 : errmsg, errmsg_len);
888 3 : if (*transaction == NULL) {
889 0 : flog_warn(EC_LIB_NB_TRANSACTION_CREATION_FAILED,
890 : "%s: failed to create transaction: %s", __func__,
891 : errmsg);
892 0 : nb_config_diff_del_changes(&changes);
893 0 : return NB_ERR_LOCKED;
894 : }
895 :
896 3 : return nb_transaction_process(NB_EV_PREPARE, *transaction, errmsg,
897 : errmsg_len);
898 : }
899 :
900 0 : void nb_candidate_commit_abort(struct nb_transaction *transaction, char *errmsg,
901 : size_t errmsg_len)
902 : {
903 0 : (void)nb_transaction_process(NB_EV_ABORT, transaction, errmsg,
904 : errmsg_len);
905 0 : nb_transaction_free(transaction);
906 0 : }
907 :
908 3 : void nb_candidate_commit_apply(struct nb_transaction *transaction,
909 : bool save_transaction, uint32_t *transaction_id,
910 : char *errmsg, size_t errmsg_len)
911 : {
912 3 : (void)nb_transaction_process(NB_EV_APPLY, transaction, errmsg,
913 : errmsg_len);
914 3 : nb_transaction_apply_finish(transaction, errmsg, errmsg_len);
915 :
916 : /* Replace running by candidate. */
917 3 : transaction->config->version++;
918 3 : nb_config_replace(running_config, transaction->config, true);
919 :
920 : /* Record transaction. */
921 3 : if (save_transaction && nb_db_enabled
922 3 : && nb_db_transaction_save(transaction, transaction_id) != NB_OK)
923 0 : flog_warn(EC_LIB_NB_TRANSACTION_RECORD_FAILED,
924 : "%s: failed to record transaction", __func__);
925 :
926 3 : nb_transaction_free(transaction);
927 3 : }
928 :
929 3 : int nb_candidate_commit(struct nb_context *context, struct nb_config *candidate,
930 : bool save_transaction, const char *comment,
931 : uint32_t *transaction_id, char *errmsg,
932 : size_t errmsg_len)
933 : {
934 3 : struct nb_transaction *transaction = NULL;
935 3 : int ret;
936 :
937 3 : ret = nb_candidate_commit_prepare(context, candidate, comment,
938 : &transaction, errmsg, errmsg_len);
939 : /*
940 : * Apply the changes if the preparation phase succeeded. Otherwise abort
941 : * the transaction.
942 : */
943 3 : if (ret == NB_OK)
944 3 : nb_candidate_commit_apply(transaction, save_transaction,
945 : transaction_id, errmsg, errmsg_len);
946 0 : else if (transaction != NULL)
947 0 : nb_candidate_commit_abort(transaction, errmsg, errmsg_len);
948 :
949 3 : return ret;
950 : }
951 :
952 0 : int nb_running_lock(enum nb_client client, const void *user)
953 : {
954 0 : int ret = -1;
955 :
956 0 : frr_with_mutex (&running_config_mgmt_lock.mtx) {
957 0 : if (!running_config_mgmt_lock.locked) {
958 0 : running_config_mgmt_lock.locked = true;
959 0 : running_config_mgmt_lock.owner_client = client;
960 0 : running_config_mgmt_lock.owner_user = user;
961 0 : ret = 0;
962 : }
963 : }
964 :
965 0 : return ret;
966 : }
967 :
968 12 : int nb_running_unlock(enum nb_client client, const void *user)
969 : {
970 12 : int ret = -1;
971 :
972 24 : frr_with_mutex (&running_config_mgmt_lock.mtx) {
973 12 : if (running_config_mgmt_lock.locked
974 0 : && running_config_mgmt_lock.owner_client == client
975 0 : && running_config_mgmt_lock.owner_user == user) {
976 0 : running_config_mgmt_lock.locked = false;
977 0 : running_config_mgmt_lock.owner_client = NB_CLIENT_NONE;
978 0 : running_config_mgmt_lock.owner_user = NULL;
979 0 : ret = 0;
980 : }
981 : }
982 :
983 12 : return ret;
984 : }
985 :
986 3 : int nb_running_lock_check(enum nb_client client, const void *user)
987 : {
988 3 : int ret = -1;
989 :
990 3 : frr_with_mutex (&running_config_mgmt_lock.mtx) {
991 3 : if (!running_config_mgmt_lock.locked
992 0 : || (running_config_mgmt_lock.owner_client == client
993 0 : && running_config_mgmt_lock.owner_user == user))
994 3 : ret = 0;
995 : }
996 :
997 3 : return ret;
998 : }
999 :
1000 9 : static void nb_log_config_callback(const enum nb_event event,
1001 : enum nb_operation operation,
1002 : const struct lyd_node *dnode)
1003 : {
1004 9 : const char *value;
1005 9 : char xpath[XPATH_MAXLEN];
1006 :
1007 9 : if (!DEBUG_MODE_CHECK(&nb_dbg_cbs_config, DEBUG_MODE_ALL))
1008 9 : return;
1009 :
1010 0 : yang_dnode_get_path(dnode, xpath, sizeof(xpath));
1011 0 : if (yang_snode_is_typeless_data(dnode->schema))
1012 : value = "(none)";
1013 : else
1014 0 : value = yang_dnode_get_string(dnode, NULL);
1015 :
1016 0 : zlog_debug(
1017 : "northbound callback: event [%s] op [%s] xpath [%s] value [%s]",
1018 : nb_event_name(event), nb_operation_name(operation), xpath,
1019 : value);
1020 : }
1021 :
1022 9 : static int nb_callback_create(struct nb_context *context,
1023 : const struct nb_node *nb_node,
1024 : enum nb_event event, const struct lyd_node *dnode,
1025 : union nb_resource *resource, char *errmsg,
1026 : size_t errmsg_len)
1027 : {
1028 9 : struct nb_cb_create_args args = {};
1029 9 : bool unexpected_error = false;
1030 9 : int ret;
1031 :
1032 9 : nb_log_config_callback(event, NB_OP_CREATE, dnode);
1033 :
1034 9 : args.context = context;
1035 9 : args.event = event;
1036 9 : args.dnode = dnode;
1037 9 : args.resource = resource;
1038 9 : args.errmsg = errmsg;
1039 9 : args.errmsg_len = errmsg_len;
1040 9 : ret = nb_node->cbs.create(&args);
1041 :
1042 : /* Detect and log unexpected errors. */
1043 9 : switch (ret) {
1044 : case NB_OK:
1045 : case NB_ERR:
1046 : break;
1047 0 : case NB_ERR_VALIDATION:
1048 0 : if (event != NB_EV_VALIDATE)
1049 : unexpected_error = true;
1050 : break;
1051 0 : case NB_ERR_RESOURCE:
1052 0 : if (event != NB_EV_PREPARE)
1053 : unexpected_error = true;
1054 : break;
1055 0 : case NB_ERR_INCONSISTENCY:
1056 0 : if (event == NB_EV_VALIDATE)
1057 : unexpected_error = true;
1058 : break;
1059 : default:
1060 : unexpected_error = true;
1061 : break;
1062 : }
1063 : if (unexpected_error)
1064 0 : DEBUGD(&nb_dbg_cbs_config,
1065 : "northbound callback: unexpected return value: %s",
1066 : nb_err_name(ret));
1067 :
1068 9 : return ret;
1069 : }
1070 :
1071 0 : static int nb_callback_modify(struct nb_context *context,
1072 : const struct nb_node *nb_node,
1073 : enum nb_event event, const struct lyd_node *dnode,
1074 : union nb_resource *resource, char *errmsg,
1075 : size_t errmsg_len)
1076 : {
1077 0 : struct nb_cb_modify_args args = {};
1078 0 : bool unexpected_error = false;
1079 0 : int ret;
1080 :
1081 0 : nb_log_config_callback(event, NB_OP_MODIFY, dnode);
1082 :
1083 0 : args.context = context;
1084 0 : args.event = event;
1085 0 : args.dnode = dnode;
1086 0 : args.resource = resource;
1087 0 : args.errmsg = errmsg;
1088 0 : args.errmsg_len = errmsg_len;
1089 0 : ret = nb_node->cbs.modify(&args);
1090 :
1091 : /* Detect and log unexpected errors. */
1092 0 : switch (ret) {
1093 : case NB_OK:
1094 : case NB_ERR:
1095 : break;
1096 0 : case NB_ERR_VALIDATION:
1097 0 : if (event != NB_EV_VALIDATE)
1098 : unexpected_error = true;
1099 : break;
1100 0 : case NB_ERR_RESOURCE:
1101 0 : if (event != NB_EV_PREPARE)
1102 : unexpected_error = true;
1103 : break;
1104 0 : case NB_ERR_INCONSISTENCY:
1105 0 : if (event == NB_EV_VALIDATE)
1106 : unexpected_error = true;
1107 : break;
1108 : default:
1109 : unexpected_error = true;
1110 : break;
1111 : }
1112 : if (unexpected_error)
1113 0 : DEBUGD(&nb_dbg_cbs_config,
1114 : "northbound callback: unexpected return value: %s",
1115 : nb_err_name(ret));
1116 :
1117 0 : return ret;
1118 : }
1119 :
1120 0 : static int nb_callback_destroy(struct nb_context *context,
1121 : const struct nb_node *nb_node,
1122 : enum nb_event event,
1123 : const struct lyd_node *dnode, char *errmsg,
1124 : size_t errmsg_len)
1125 : {
1126 0 : struct nb_cb_destroy_args args = {};
1127 0 : bool unexpected_error = false;
1128 0 : int ret;
1129 :
1130 0 : nb_log_config_callback(event, NB_OP_DESTROY, dnode);
1131 :
1132 0 : args.context = context;
1133 0 : args.event = event;
1134 0 : args.dnode = dnode;
1135 0 : args.errmsg = errmsg;
1136 0 : args.errmsg_len = errmsg_len;
1137 0 : ret = nb_node->cbs.destroy(&args);
1138 :
1139 : /* Detect and log unexpected errors. */
1140 0 : switch (ret) {
1141 : case NB_OK:
1142 : case NB_ERR:
1143 : break;
1144 0 : case NB_ERR_VALIDATION:
1145 0 : if (event != NB_EV_VALIDATE)
1146 : unexpected_error = true;
1147 : break;
1148 0 : case NB_ERR_INCONSISTENCY:
1149 0 : if (event == NB_EV_VALIDATE)
1150 : unexpected_error = true;
1151 : break;
1152 : default:
1153 : unexpected_error = true;
1154 : break;
1155 : }
1156 : if (unexpected_error)
1157 0 : DEBUGD(&nb_dbg_cbs_config,
1158 : "northbound callback: unexpected return value: %s",
1159 : nb_err_name(ret));
1160 :
1161 0 : return ret;
1162 : }
1163 :
1164 0 : static int nb_callback_move(struct nb_context *context,
1165 : const struct nb_node *nb_node, enum nb_event event,
1166 : const struct lyd_node *dnode, char *errmsg,
1167 : size_t errmsg_len)
1168 : {
1169 0 : struct nb_cb_move_args args = {};
1170 0 : bool unexpected_error = false;
1171 0 : int ret;
1172 :
1173 0 : nb_log_config_callback(event, NB_OP_MOVE, dnode);
1174 :
1175 0 : args.context = context;
1176 0 : args.event = event;
1177 0 : args.dnode = dnode;
1178 0 : args.errmsg = errmsg;
1179 0 : args.errmsg_len = errmsg_len;
1180 0 : ret = nb_node->cbs.move(&args);
1181 :
1182 : /* Detect and log unexpected errors. */
1183 0 : switch (ret) {
1184 : case NB_OK:
1185 : case NB_ERR:
1186 : break;
1187 0 : case NB_ERR_VALIDATION:
1188 0 : if (event != NB_EV_VALIDATE)
1189 : unexpected_error = true;
1190 : break;
1191 0 : case NB_ERR_INCONSISTENCY:
1192 0 : if (event == NB_EV_VALIDATE)
1193 : unexpected_error = true;
1194 : break;
1195 : default:
1196 : unexpected_error = true;
1197 : break;
1198 : }
1199 : if (unexpected_error)
1200 0 : DEBUGD(&nb_dbg_cbs_config,
1201 : "northbound callback: unexpected return value: %s",
1202 : nb_err_name(ret));
1203 :
1204 0 : return ret;
1205 : }
1206 :
1207 0 : static int nb_callback_pre_validate(struct nb_context *context,
1208 : const struct nb_node *nb_node,
1209 : const struct lyd_node *dnode, char *errmsg,
1210 : size_t errmsg_len)
1211 : {
1212 0 : struct nb_cb_pre_validate_args args = {};
1213 0 : bool unexpected_error = false;
1214 0 : int ret;
1215 :
1216 0 : nb_log_config_callback(NB_EV_VALIDATE, NB_OP_PRE_VALIDATE, dnode);
1217 :
1218 0 : args.dnode = dnode;
1219 0 : args.errmsg = errmsg;
1220 0 : args.errmsg_len = errmsg_len;
1221 0 : ret = nb_node->cbs.pre_validate(&args);
1222 :
1223 : /* Detect and log unexpected errors. */
1224 0 : switch (ret) {
1225 : case NB_OK:
1226 : case NB_ERR_VALIDATION:
1227 : break;
1228 : default:
1229 0 : unexpected_error = true;
1230 0 : break;
1231 : }
1232 0 : if (unexpected_error)
1233 0 : DEBUGD(&nb_dbg_cbs_config,
1234 : "northbound callback: unexpected return value: %s",
1235 : nb_err_name(ret));
1236 :
1237 0 : return ret;
1238 : }
1239 :
1240 0 : static void nb_callback_apply_finish(struct nb_context *context,
1241 : const struct nb_node *nb_node,
1242 : const struct lyd_node *dnode, char *errmsg,
1243 : size_t errmsg_len)
1244 : {
1245 0 : struct nb_cb_apply_finish_args args = {};
1246 :
1247 0 : nb_log_config_callback(NB_EV_APPLY, NB_OP_APPLY_FINISH, dnode);
1248 :
1249 0 : args.context = context;
1250 0 : args.dnode = dnode;
1251 0 : args.errmsg = errmsg;
1252 0 : args.errmsg_len = errmsg_len;
1253 0 : nb_node->cbs.apply_finish(&args);
1254 0 : }
1255 :
1256 0 : struct yang_data *nb_callback_get_elem(const struct nb_node *nb_node,
1257 : const char *xpath,
1258 : const void *list_entry)
1259 : {
1260 0 : struct nb_cb_get_elem_args args = {};
1261 :
1262 0 : DEBUGD(&nb_dbg_cbs_state,
1263 : "northbound callback (get_elem): xpath [%s] list_entry [%p]",
1264 : xpath, list_entry);
1265 :
1266 0 : args.xpath = xpath;
1267 0 : args.list_entry = list_entry;
1268 0 : return nb_node->cbs.get_elem(&args);
1269 : }
1270 :
1271 0 : const void *nb_callback_get_next(const struct nb_node *nb_node,
1272 : const void *parent_list_entry,
1273 : const void *list_entry)
1274 : {
1275 0 : struct nb_cb_get_next_args args = {};
1276 :
1277 0 : DEBUGD(&nb_dbg_cbs_state,
1278 : "northbound callback (get_next): node [%s] parent_list_entry [%p] list_entry [%p]",
1279 : nb_node->xpath, parent_list_entry, list_entry);
1280 :
1281 0 : args.parent_list_entry = parent_list_entry;
1282 0 : args.list_entry = list_entry;
1283 0 : return nb_node->cbs.get_next(&args);
1284 : }
1285 :
1286 0 : int nb_callback_get_keys(const struct nb_node *nb_node, const void *list_entry,
1287 : struct yang_list_keys *keys)
1288 : {
1289 0 : struct nb_cb_get_keys_args args = {};
1290 :
1291 0 : DEBUGD(&nb_dbg_cbs_state,
1292 : "northbound callback (get_keys): node [%s] list_entry [%p]",
1293 : nb_node->xpath, list_entry);
1294 :
1295 0 : args.list_entry = list_entry;
1296 0 : args.keys = keys;
1297 0 : return nb_node->cbs.get_keys(&args);
1298 : }
1299 :
1300 0 : const void *nb_callback_lookup_entry(const struct nb_node *nb_node,
1301 : const void *parent_list_entry,
1302 : const struct yang_list_keys *keys)
1303 : {
1304 0 : struct nb_cb_lookup_entry_args args = {};
1305 :
1306 0 : DEBUGD(&nb_dbg_cbs_state,
1307 : "northbound callback (lookup_entry): node [%s] parent_list_entry [%p]",
1308 : nb_node->xpath, parent_list_entry);
1309 :
1310 0 : args.parent_list_entry = parent_list_entry;
1311 0 : args.keys = keys;
1312 0 : return nb_node->cbs.lookup_entry(&args);
1313 : }
1314 :
1315 0 : int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath,
1316 : const struct list *input, struct list *output, char *errmsg,
1317 : size_t errmsg_len)
1318 : {
1319 0 : struct nb_cb_rpc_args args = {};
1320 :
1321 0 : DEBUGD(&nb_dbg_cbs_rpc, "northbound RPC: %s", xpath);
1322 :
1323 0 : args.xpath = xpath;
1324 0 : args.input = input;
1325 0 : args.output = output;
1326 0 : args.errmsg = errmsg;
1327 0 : args.errmsg_len = errmsg_len;
1328 0 : return nb_node->cbs.rpc(&args);
1329 : }
1330 :
1331 : /*
1332 : * Call the northbound configuration callback associated to a given
1333 : * configuration change.
1334 : */
1335 9 : static int nb_callback_configuration(struct nb_context *context,
1336 : const enum nb_event event,
1337 : struct nb_config_change *change,
1338 : char *errmsg, size_t errmsg_len)
1339 : {
1340 9 : enum nb_operation operation = change->cb.operation;
1341 9 : char xpath[XPATH_MAXLEN];
1342 9 : const struct nb_node *nb_node = change->cb.nb_node;
1343 9 : const struct lyd_node *dnode = change->cb.dnode;
1344 9 : union nb_resource *resource;
1345 9 : int ret = NB_ERR;
1346 :
1347 9 : if (event == NB_EV_VALIDATE)
1348 : resource = NULL;
1349 : else
1350 6 : resource = &change->resource;
1351 :
1352 9 : switch (operation) {
1353 9 : case NB_OP_CREATE:
1354 9 : ret = nb_callback_create(context, nb_node, event, dnode,
1355 : resource, errmsg, errmsg_len);
1356 9 : break;
1357 0 : case NB_OP_MODIFY:
1358 0 : ret = nb_callback_modify(context, nb_node, event, dnode,
1359 : resource, errmsg, errmsg_len);
1360 0 : break;
1361 0 : case NB_OP_DESTROY:
1362 0 : ret = nb_callback_destroy(context, nb_node, event, dnode,
1363 : errmsg, errmsg_len);
1364 0 : break;
1365 0 : case NB_OP_MOVE:
1366 0 : ret = nb_callback_move(context, nb_node, event, dnode, errmsg,
1367 : errmsg_len);
1368 0 : break;
1369 0 : case NB_OP_PRE_VALIDATE:
1370 : case NB_OP_APPLY_FINISH:
1371 : case NB_OP_GET_ELEM:
1372 : case NB_OP_GET_NEXT:
1373 : case NB_OP_GET_KEYS:
1374 : case NB_OP_LOOKUP_ENTRY:
1375 : case NB_OP_RPC:
1376 0 : yang_dnode_get_path(dnode, xpath, sizeof(xpath));
1377 0 : flog_err(EC_LIB_DEVELOPMENT,
1378 : "%s: unknown operation (%u) [xpath %s]", __func__,
1379 : operation, xpath);
1380 0 : exit(1);
1381 : }
1382 :
1383 9 : if (ret != NB_OK) {
1384 0 : yang_dnode_get_path(dnode, xpath, sizeof(xpath));
1385 :
1386 0 : switch (event) {
1387 0 : case NB_EV_VALIDATE:
1388 0 : flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE,
1389 : "error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]%s%s",
1390 : nb_err_name(ret), nb_event_name(event),
1391 : nb_operation_name(operation), xpath,
1392 : errmsg[0] ? " message: " : "", errmsg);
1393 0 : break;
1394 0 : case NB_EV_PREPARE:
1395 0 : flog_warn(EC_LIB_NB_CB_CONFIG_PREPARE,
1396 : "error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]%s%s",
1397 : nb_err_name(ret), nb_event_name(event),
1398 : nb_operation_name(operation), xpath,
1399 : errmsg[0] ? " message: " : "", errmsg);
1400 0 : break;
1401 0 : case NB_EV_ABORT:
1402 0 : flog_warn(EC_LIB_NB_CB_CONFIG_ABORT,
1403 : "error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]%s%s",
1404 : nb_err_name(ret), nb_event_name(event),
1405 : nb_operation_name(operation), xpath,
1406 : errmsg[0] ? " message: " : "", errmsg);
1407 0 : break;
1408 0 : case NB_EV_APPLY:
1409 0 : flog_err(EC_LIB_NB_CB_CONFIG_APPLY,
1410 : "error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]%s%s",
1411 : nb_err_name(ret), nb_event_name(event),
1412 : nb_operation_name(operation), xpath,
1413 : errmsg[0] ? " message: " : "", errmsg);
1414 0 : break;
1415 0 : default:
1416 0 : flog_err(EC_LIB_DEVELOPMENT,
1417 : "%s: unknown event (%u) [xpath %s]", __func__,
1418 : event, xpath);
1419 0 : exit(1);
1420 : }
1421 : }
1422 :
1423 9 : return ret;
1424 : }
1425 :
1426 : static struct nb_transaction *
1427 3 : nb_transaction_new(struct nb_context *context, struct nb_config *config,
1428 : struct nb_config_cbs *changes, const char *comment,
1429 : char *errmsg, size_t errmsg_len)
1430 : {
1431 3 : struct nb_transaction *transaction;
1432 :
1433 3 : if (nb_running_lock_check(context->client, context->user)) {
1434 0 : strlcpy(errmsg,
1435 : "running configuration is locked by another client",
1436 : errmsg_len);
1437 0 : return NULL;
1438 : }
1439 :
1440 3 : if (transaction_in_progress) {
1441 0 : strlcpy(errmsg,
1442 : "there's already another transaction in progress",
1443 : errmsg_len);
1444 0 : return NULL;
1445 : }
1446 3 : transaction_in_progress = true;
1447 :
1448 3 : transaction = XCALLOC(MTYPE_TMP, sizeof(*transaction));
1449 3 : transaction->context = context;
1450 3 : if (comment)
1451 0 : strlcpy(transaction->comment, comment,
1452 : sizeof(transaction->comment));
1453 3 : transaction->config = config;
1454 3 : transaction->changes = *changes;
1455 :
1456 3 : return transaction;
1457 : }
1458 :
1459 3 : static void nb_transaction_free(struct nb_transaction *transaction)
1460 : {
1461 3 : nb_config_diff_del_changes(&transaction->changes);
1462 3 : XFREE(MTYPE_TMP, transaction);
1463 3 : transaction_in_progress = false;
1464 3 : }
1465 :
1466 : /* Process all configuration changes associated to a transaction. */
1467 6 : static int nb_transaction_process(enum nb_event event,
1468 : struct nb_transaction *transaction,
1469 : char *errmsg, size_t errmsg_len)
1470 : {
1471 6 : struct nb_config_cb *cb;
1472 :
1473 18 : RB_FOREACH (cb, nb_config_cbs, &transaction->changes) {
1474 6 : struct nb_config_change *change = (struct nb_config_change *)cb;
1475 6 : int ret;
1476 :
1477 : /*
1478 : * Only try to release resources that were allocated
1479 : * successfully.
1480 : */
1481 6 : if (event == NB_EV_ABORT && !change->prepare_ok)
1482 : break;
1483 :
1484 : /* Call the appropriate callback. */
1485 6 : ret = nb_callback_configuration(transaction->context, event,
1486 : change, errmsg, errmsg_len);
1487 6 : switch (event) {
1488 3 : case NB_EV_PREPARE:
1489 3 : if (ret != NB_OK)
1490 0 : return ret;
1491 3 : change->prepare_ok = true;
1492 3 : break;
1493 : case NB_EV_ABORT:
1494 : case NB_EV_APPLY:
1495 : /*
1496 : * At this point it's not possible to reject the
1497 : * transaction anymore, so any failure here can lead to
1498 : * inconsistencies and should be treated as a bug.
1499 : * Operations prone to errors, like validations and
1500 : * resource allocations, should be performed during the
1501 : * 'prepare' phase.
1502 : */
1503 : break;
1504 : case NB_EV_VALIDATE:
1505 : break;
1506 : }
1507 : }
1508 :
1509 : return NB_OK;
1510 : }
1511 :
1512 : static struct nb_config_cb *
1513 0 : nb_apply_finish_cb_new(struct nb_config_cbs *cbs, const struct nb_node *nb_node,
1514 : const struct lyd_node *dnode)
1515 : {
1516 0 : struct nb_config_cb *cb;
1517 :
1518 0 : cb = XCALLOC(MTYPE_TMP, sizeof(*cb));
1519 0 : cb->nb_node = nb_node;
1520 0 : cb->dnode = dnode;
1521 0 : RB_INSERT(nb_config_cbs, cbs, cb);
1522 :
1523 0 : return cb;
1524 : }
1525 :
1526 : static struct nb_config_cb *
1527 0 : nb_apply_finish_cb_find(struct nb_config_cbs *cbs,
1528 : const struct nb_node *nb_node,
1529 : const struct lyd_node *dnode)
1530 : {
1531 0 : struct nb_config_cb s;
1532 :
1533 0 : s.seq = 0;
1534 0 : s.nb_node = nb_node;
1535 0 : s.dnode = dnode;
1536 0 : return RB_FIND(nb_config_cbs, cbs, &s);
1537 : }
1538 :
1539 : /* Call the 'apply_finish' callbacks. */
1540 3 : static void nb_transaction_apply_finish(struct nb_transaction *transaction,
1541 : char *errmsg, size_t errmsg_len)
1542 : {
1543 3 : struct nb_config_cbs cbs;
1544 3 : struct nb_config_cb *cb;
1545 :
1546 : /* Initialize tree of 'apply_finish' callbacks. */
1547 3 : RB_INIT(nb_config_cbs, &cbs);
1548 :
1549 : /* Identify the 'apply_finish' callbacks that need to be called. */
1550 9 : RB_FOREACH (cb, nb_config_cbs, &transaction->changes) {
1551 3 : struct nb_config_change *change = (struct nb_config_change *)cb;
1552 3 : const struct lyd_node *dnode = change->cb.dnode;
1553 :
1554 : /*
1555 : * Iterate up to the root of the data tree. When a node is being
1556 : * deleted, skip its 'apply_finish' callback if one is defined
1557 : * (the 'apply_finish' callbacks from the node ancestors should
1558 : * be called though).
1559 : */
1560 3 : if (change->cb.operation == NB_OP_DESTROY) {
1561 0 : char xpath[XPATH_MAXLEN];
1562 :
1563 0 : dnode = lyd_parent(dnode);
1564 0 : if (!dnode)
1565 : break;
1566 :
1567 : /*
1568 : * The dnode from 'delete' callbacks point to elements
1569 : * from the running configuration. Use yang_dnode_get()
1570 : * to get the corresponding dnode from the candidate
1571 : * configuration that is being committed.
1572 : */
1573 0 : yang_dnode_get_path(dnode, xpath, sizeof(xpath));
1574 0 : dnode = yang_dnode_get(transaction->config->dnode,
1575 : xpath);
1576 : }
1577 9 : while (dnode) {
1578 6 : struct nb_node *nb_node;
1579 :
1580 6 : nb_node = dnode->schema->priv;
1581 6 : if (!nb_node || !nb_node->cbs.apply_finish)
1582 6 : goto next;
1583 :
1584 : /*
1585 : * Don't call the callback more than once for the same
1586 : * data node.
1587 : */
1588 0 : if (nb_apply_finish_cb_find(&cbs, nb_node, dnode))
1589 0 : goto next;
1590 :
1591 0 : nb_apply_finish_cb_new(&cbs, nb_node, dnode);
1592 :
1593 : next:
1594 15 : dnode = lyd_parent(dnode);
1595 : }
1596 : }
1597 :
1598 : /* Call the 'apply_finish' callbacks, sorted by their priorities. */
1599 6 : RB_FOREACH (cb, nb_config_cbs, &cbs)
1600 0 : nb_callback_apply_finish(transaction->context, cb->nb_node,
1601 : cb->dnode, errmsg, errmsg_len);
1602 :
1603 : /* Release memory. */
1604 3 : while (!RB_EMPTY(nb_config_cbs, &cbs)) {
1605 0 : cb = RB_ROOT(nb_config_cbs, &cbs);
1606 0 : RB_REMOVE(nb_config_cbs, &cbs, cb);
1607 3 : XFREE(MTYPE_TMP, cb);
1608 : }
1609 3 : }
1610 :
1611 0 : static int nb_oper_data_iter_children(const struct lysc_node *snode,
1612 : const char *xpath, const void *list_entry,
1613 : const struct yang_list_keys *list_keys,
1614 : struct yang_translator *translator,
1615 : bool first, uint32_t flags,
1616 : nb_oper_data_cb cb, void *arg)
1617 : {
1618 0 : const struct lysc_node *child;
1619 :
1620 0 : LY_LIST_FOR (lysc_node_child(snode), child) {
1621 0 : int ret;
1622 :
1623 0 : ret = nb_oper_data_iter_node(child, xpath, list_entry,
1624 : list_keys, translator, false,
1625 : flags, cb, arg);
1626 0 : if (ret != NB_OK)
1627 0 : return ret;
1628 : }
1629 :
1630 : return NB_OK;
1631 : }
1632 :
1633 0 : static int nb_oper_data_iter_leaf(const struct nb_node *nb_node,
1634 : const char *xpath, const void *list_entry,
1635 : const struct yang_list_keys *list_keys,
1636 : struct yang_translator *translator,
1637 : uint32_t flags, nb_oper_data_cb cb, void *arg)
1638 : {
1639 0 : struct yang_data *data;
1640 :
1641 0 : if (CHECK_FLAG(nb_node->snode->flags, LYS_CONFIG_W))
1642 : return NB_OK;
1643 :
1644 : /* Ignore list keys. */
1645 0 : if (lysc_is_key(nb_node->snode))
1646 : return NB_OK;
1647 :
1648 0 : data = nb_callback_get_elem(nb_node, xpath, list_entry);
1649 0 : if (data == NULL)
1650 : /* Leaf of type "empty" is not present. */
1651 : return NB_OK;
1652 :
1653 0 : return (*cb)(nb_node->snode, translator, data, arg);
1654 : }
1655 :
1656 0 : static int nb_oper_data_iter_container(const struct nb_node *nb_node,
1657 : const char *xpath,
1658 : const void *list_entry,
1659 : const struct yang_list_keys *list_keys,
1660 : struct yang_translator *translator,
1661 : uint32_t flags, nb_oper_data_cb cb,
1662 : void *arg)
1663 : {
1664 0 : const struct lysc_node *snode = nb_node->snode;
1665 :
1666 0 : if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
1667 : return NB_OK;
1668 :
1669 : /* Read-only presence containers. */
1670 0 : if (nb_node->cbs.get_elem) {
1671 0 : struct yang_data *data;
1672 0 : int ret;
1673 :
1674 0 : data = nb_callback_get_elem(nb_node, xpath, list_entry);
1675 0 : if (data == NULL)
1676 : /* Presence container is not present. */
1677 : return NB_OK;
1678 :
1679 0 : ret = (*cb)(snode, translator, data, arg);
1680 0 : if (ret != NB_OK)
1681 : return ret;
1682 : }
1683 :
1684 : /* Read-write presence containers. */
1685 0 : if (CHECK_FLAG(snode->flags, LYS_CONFIG_W)) {
1686 0 : struct lysc_node_container *scontainer;
1687 :
1688 0 : scontainer = (struct lysc_node_container *)snode;
1689 0 : if (CHECK_FLAG(scontainer->flags, LYS_PRESENCE)
1690 0 : && !yang_dnode_get(running_config->dnode, xpath))
1691 : return NB_OK;
1692 : }
1693 :
1694 : /* Iterate over the child nodes. */
1695 0 : return nb_oper_data_iter_children(snode, xpath, list_entry, list_keys,
1696 : translator, false, flags, cb, arg);
1697 : }
1698 :
1699 : static int
1700 0 : nb_oper_data_iter_leaflist(const struct nb_node *nb_node, const char *xpath,
1701 : const void *parent_list_entry,
1702 : const struct yang_list_keys *parent_list_keys,
1703 : struct yang_translator *translator, uint32_t flags,
1704 : nb_oper_data_cb cb, void *arg)
1705 : {
1706 0 : const void *list_entry = NULL;
1707 :
1708 0 : if (CHECK_FLAG(nb_node->snode->flags, LYS_CONFIG_W))
1709 : return NB_OK;
1710 :
1711 0 : do {
1712 0 : struct yang_data *data;
1713 0 : int ret;
1714 :
1715 0 : list_entry = nb_callback_get_next(nb_node, parent_list_entry,
1716 : list_entry);
1717 0 : if (!list_entry)
1718 : /* End of the list. */
1719 : break;
1720 :
1721 0 : data = nb_callback_get_elem(nb_node, xpath, list_entry);
1722 0 : if (data == NULL)
1723 0 : continue;
1724 :
1725 0 : ret = (*cb)(nb_node->snode, translator, data, arg);
1726 0 : if (ret != NB_OK)
1727 0 : return ret;
1728 : } while (list_entry);
1729 :
1730 : return NB_OK;
1731 : }
1732 :
1733 0 : static int nb_oper_data_iter_list(const struct nb_node *nb_node,
1734 : const char *xpath_list,
1735 : const void *parent_list_entry,
1736 : const struct yang_list_keys *parent_list_keys,
1737 : struct yang_translator *translator,
1738 : uint32_t flags, nb_oper_data_cb cb, void *arg)
1739 : {
1740 0 : const struct lysc_node *snode = nb_node->snode;
1741 0 : const void *list_entry = NULL;
1742 0 : uint32_t position = 1;
1743 :
1744 0 : if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
1745 : return NB_OK;
1746 :
1747 : /* Iterate over all list entries. */
1748 0 : do {
1749 0 : const struct lysc_node_leaf *skey;
1750 0 : struct yang_list_keys list_keys;
1751 0 : char xpath[XPATH_MAXLEN * 2];
1752 0 : int ret;
1753 :
1754 : /* Obtain list entry. */
1755 0 : list_entry = nb_callback_get_next(nb_node, parent_list_entry,
1756 : list_entry);
1757 0 : if (!list_entry)
1758 : /* End of the list. */
1759 : break;
1760 :
1761 0 : if (!CHECK_FLAG(nb_node->flags, F_NB_NODE_KEYLESS_LIST)) {
1762 : /* Obtain the list entry keys. */
1763 0 : if (nb_callback_get_keys(nb_node, list_entry,
1764 : &list_keys)
1765 : != NB_OK) {
1766 0 : flog_warn(EC_LIB_NB_CB_STATE,
1767 : "%s: failed to get list keys",
1768 : __func__);
1769 0 : return NB_ERR;
1770 : }
1771 :
1772 : /* Build XPath of the list entry. */
1773 0 : strlcpy(xpath, xpath_list, sizeof(xpath));
1774 0 : unsigned int i = 0;
1775 0 : LY_FOR_KEYS (snode, skey) {
1776 0 : assert(i < list_keys.num);
1777 0 : snprintf(xpath + strlen(xpath),
1778 0 : sizeof(xpath) - strlen(xpath),
1779 0 : "[%s='%s']", skey->name,
1780 0 : list_keys.key[i]);
1781 0 : i++;
1782 : }
1783 0 : assert(i == list_keys.num);
1784 : } else {
1785 : /*
1786 : * Keyless list - build XPath using a positional index.
1787 : */
1788 0 : snprintf(xpath, sizeof(xpath), "%s[%u]", xpath_list,
1789 : position);
1790 0 : position++;
1791 : }
1792 :
1793 : /* Iterate over the child nodes. */
1794 0 : ret = nb_oper_data_iter_children(
1795 0 : nb_node->snode, xpath, list_entry, &list_keys,
1796 : translator, false, flags, cb, arg);
1797 0 : if (ret != NB_OK)
1798 0 : return ret;
1799 0 : } while (list_entry);
1800 :
1801 0 : return NB_OK;
1802 : }
1803 :
1804 0 : static int nb_oper_data_iter_node(const struct lysc_node *snode,
1805 : const char *xpath_parent,
1806 : const void *list_entry,
1807 : const struct yang_list_keys *list_keys,
1808 : struct yang_translator *translator,
1809 : bool first, uint32_t flags,
1810 : nb_oper_data_cb cb, void *arg)
1811 : {
1812 0 : struct nb_node *nb_node;
1813 0 : char xpath[XPATH_MAXLEN];
1814 0 : int ret = NB_OK;
1815 :
1816 0 : if (!first && CHECK_FLAG(flags, NB_OPER_DATA_ITER_NORECURSE)
1817 0 : && CHECK_FLAG(snode->nodetype, LYS_CONTAINER | LYS_LIST))
1818 : return NB_OK;
1819 :
1820 : /* Update XPath. */
1821 0 : strlcpy(xpath, xpath_parent, sizeof(xpath));
1822 0 : if (!first && snode->nodetype != LYS_USES) {
1823 0 : struct lysc_node *parent;
1824 :
1825 : /* Get the real parent. */
1826 0 : parent = snode->parent;
1827 :
1828 : /*
1829 : * When necessary, include the namespace of the augmenting
1830 : * module.
1831 : */
1832 0 : if (parent && parent->module != snode->module)
1833 0 : snprintf(xpath + strlen(xpath),
1834 0 : sizeof(xpath) - strlen(xpath), "/%s:%s",
1835 0 : snode->module->name, snode->name);
1836 : else
1837 0 : snprintf(xpath + strlen(xpath),
1838 0 : sizeof(xpath) - strlen(xpath), "/%s",
1839 0 : snode->name);
1840 : }
1841 :
1842 0 : nb_node = snode->priv;
1843 0 : switch (snode->nodetype) {
1844 0 : case LYS_CONTAINER:
1845 0 : ret = nb_oper_data_iter_container(nb_node, xpath, list_entry,
1846 : list_keys, translator, flags,
1847 : cb, arg);
1848 0 : break;
1849 0 : case LYS_LEAF:
1850 0 : ret = nb_oper_data_iter_leaf(nb_node, xpath, list_entry,
1851 : list_keys, translator, flags, cb,
1852 : arg);
1853 0 : break;
1854 0 : case LYS_LEAFLIST:
1855 0 : ret = nb_oper_data_iter_leaflist(nb_node, xpath, list_entry,
1856 : list_keys, translator, flags,
1857 : cb, arg);
1858 0 : break;
1859 0 : case LYS_LIST:
1860 0 : ret = nb_oper_data_iter_list(nb_node, xpath, list_entry,
1861 : list_keys, translator, flags, cb,
1862 : arg);
1863 0 : break;
1864 0 : case LYS_USES:
1865 0 : ret = nb_oper_data_iter_children(snode, xpath, list_entry,
1866 : list_keys, translator, false,
1867 : flags, cb, arg);
1868 0 : break;
1869 : default:
1870 : break;
1871 : }
1872 :
1873 : return ret;
1874 : }
1875 :
1876 0 : int nb_oper_data_iterate(const char *xpath, struct yang_translator *translator,
1877 : uint32_t flags, nb_oper_data_cb cb, void *arg)
1878 : {
1879 0 : struct nb_node *nb_node;
1880 0 : const void *list_entry = NULL;
1881 0 : struct yang_list_keys list_keys;
1882 0 : struct list *list_dnodes;
1883 0 : struct lyd_node *dnode, *dn;
1884 0 : struct listnode *ln;
1885 0 : int ret;
1886 :
1887 0 : nb_node = nb_node_find(xpath);
1888 0 : if (!nb_node) {
1889 0 : flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
1890 : "%s: unknown data path: %s", __func__, xpath);
1891 0 : return NB_ERR;
1892 : }
1893 :
1894 : /* For now this function works only with containers and lists. */
1895 0 : if (!CHECK_FLAG(nb_node->snode->nodetype, LYS_CONTAINER | LYS_LIST)) {
1896 0 : flog_warn(
1897 : EC_LIB_NB_OPERATIONAL_DATA,
1898 : "%s: can't iterate over YANG leaf or leaf-list [xpath %s]",
1899 : __func__, xpath);
1900 0 : return NB_ERR;
1901 : }
1902 :
1903 : /*
1904 : * Create a data tree from the XPath so that we can parse the keys of
1905 : * all YANG lists (if any).
1906 : */
1907 :
1908 0 : LY_ERR err = lyd_new_path(NULL, ly_native_ctx, xpath, NULL,
1909 : LYD_NEW_PATH_UPDATE, &dnode);
1910 0 : if (err || !dnode) {
1911 0 : const char *errmsg =
1912 0 : err ? ly_errmsg(ly_native_ctx) : "node not found";
1913 0 : flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed %s",
1914 : __func__, errmsg);
1915 0 : return NB_ERR;
1916 : }
1917 :
1918 : /*
1919 : * Create a linked list to sort the data nodes starting from the root.
1920 : */
1921 0 : list_dnodes = list_new();
1922 0 : for (dn = dnode; dn; dn = lyd_parent(dn)) {
1923 0 : if (dn->schema->nodetype != LYS_LIST || !lyd_child(dn))
1924 0 : continue;
1925 0 : listnode_add_head(list_dnodes, dn);
1926 : }
1927 : /*
1928 : * Use the northbound callbacks to find list entry pointer corresponding
1929 : * to the given XPath.
1930 : */
1931 0 : for (ALL_LIST_ELEMENTS_RO(list_dnodes, ln, dn)) {
1932 0 : struct lyd_node *child;
1933 0 : struct nb_node *nn;
1934 0 : unsigned int n = 0;
1935 :
1936 : /* Obtain the list entry keys. */
1937 0 : memset(&list_keys, 0, sizeof(list_keys));
1938 0 : LY_LIST_FOR (lyd_child(dn), child) {
1939 0 : if (!lysc_is_key(child->schema))
1940 : break;
1941 0 : strlcpy(list_keys.key[n],
1942 : yang_dnode_get_string(child, NULL),
1943 : sizeof(list_keys.key[n]));
1944 0 : n++;
1945 : }
1946 0 : list_keys.num = n;
1947 0 : if (list_keys.num != yang_snode_num_keys(dn->schema)) {
1948 0 : list_delete(&list_dnodes);
1949 0 : yang_dnode_free(dnode);
1950 0 : return NB_ERR_NOT_FOUND;
1951 : }
1952 :
1953 : /* Find the list entry pointer. */
1954 0 : nn = dn->schema->priv;
1955 0 : if (!nn->cbs.lookup_entry) {
1956 0 : flog_warn(
1957 : EC_LIB_NB_OPERATIONAL_DATA,
1958 : "%s: data path doesn't support iteration over operational data: %s",
1959 : __func__, xpath);
1960 0 : list_delete(&list_dnodes);
1961 0 : yang_dnode_free(dnode);
1962 0 : return NB_ERR;
1963 : }
1964 :
1965 0 : list_entry =
1966 0 : nb_callback_lookup_entry(nn, list_entry, &list_keys);
1967 0 : if (list_entry == NULL) {
1968 0 : list_delete(&list_dnodes);
1969 0 : yang_dnode_free(dnode);
1970 0 : return NB_ERR_NOT_FOUND;
1971 : }
1972 : }
1973 :
1974 : /* If a list entry was given, iterate over that list entry only. */
1975 0 : if (dnode->schema->nodetype == LYS_LIST && lyd_child(dnode))
1976 0 : ret = nb_oper_data_iter_children(
1977 : nb_node->snode, xpath, list_entry, &list_keys,
1978 : translator, true, flags, cb, arg);
1979 : else
1980 0 : ret = nb_oper_data_iter_node(nb_node->snode, xpath, list_entry,
1981 : &list_keys, translator, true,
1982 : flags, cb, arg);
1983 :
1984 0 : list_delete(&list_dnodes);
1985 0 : yang_dnode_free(dnode);
1986 :
1987 0 : return ret;
1988 : }
1989 :
1990 29745 : bool nb_operation_is_valid(enum nb_operation operation,
1991 : const struct lysc_node *snode)
1992 : {
1993 29745 : struct nb_node *nb_node = snode->priv;
1994 29745 : struct lysc_node_container *scontainer;
1995 29745 : struct lysc_node_leaf *sleaf;
1996 :
1997 29745 : switch (operation) {
1998 2712 : case NB_OP_CREATE:
1999 2712 : if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
2000 : return false;
2001 :
2002 1254 : switch (snode->nodetype) {
2003 666 : case LYS_LEAF:
2004 666 : sleaf = (struct lysc_node_leaf *)snode;
2005 666 : if (sleaf->type->basetype != LY_TYPE_EMPTY)
2006 : return false;
2007 : break;
2008 81 : case LYS_CONTAINER:
2009 81 : scontainer = (struct lysc_node_container *)snode;
2010 81 : if (!CHECK_FLAG(scontainer->flags, LYS_PRESENCE))
2011 : return false;
2012 : break;
2013 : case LYS_LIST:
2014 : case LYS_LEAFLIST:
2015 : break;
2016 : default:
2017 : return false;
2018 : }
2019 : return true;
2020 2706 : case NB_OP_MODIFY:
2021 2706 : if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
2022 : return false;
2023 :
2024 1248 : switch (snode->nodetype) {
2025 666 : case LYS_LEAF:
2026 666 : sleaf = (struct lysc_node_leaf *)snode;
2027 666 : if (sleaf->type->basetype == LY_TYPE_EMPTY)
2028 : return false;
2029 :
2030 : /* List keys can't be modified. */
2031 633 : if (lysc_is_key(sleaf))
2032 : return false;
2033 : break;
2034 : default:
2035 : return false;
2036 : }
2037 : return true;
2038 2703 : case NB_OP_DESTROY:
2039 2703 : if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
2040 : return false;
2041 :
2042 1245 : switch (snode->nodetype) {
2043 : case LYS_LEAF:
2044 663 : sleaf = (struct lysc_node_leaf *)snode;
2045 :
2046 : /* List keys can't be deleted. */
2047 663 : if (lysc_is_key(sleaf))
2048 : return false;
2049 :
2050 : /*
2051 : * Only optional leafs can be deleted, or leafs whose
2052 : * parent is a case statement.
2053 : */
2054 576 : if (snode->parent->nodetype == LYS_CASE)
2055 : return true;
2056 219 : if (sleaf->when)
2057 : return true;
2058 204 : if (CHECK_FLAG(sleaf->flags, LYS_MAND_TRUE)
2059 162 : || sleaf->dflt)
2060 : return false;
2061 : break;
2062 78 : case LYS_CONTAINER:
2063 78 : scontainer = (struct lysc_node_container *)snode;
2064 78 : if (!CHECK_FLAG(scontainer->flags, LYS_PRESENCE))
2065 : return false;
2066 : break;
2067 : case LYS_LIST:
2068 : case LYS_LEAFLIST:
2069 : break;
2070 : default:
2071 : return false;
2072 : }
2073 : return true;
2074 2703 : case NB_OP_MOVE:
2075 2703 : if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
2076 : return false;
2077 :
2078 1245 : switch (snode->nodetype) {
2079 66 : case LYS_LIST:
2080 : case LYS_LEAFLIST:
2081 66 : if (!CHECK_FLAG(snode->flags, LYS_ORDBY_USER))
2082 : return false;
2083 : break;
2084 : default:
2085 : return false;
2086 : }
2087 : return true;
2088 5406 : case NB_OP_PRE_VALIDATE:
2089 : case NB_OP_APPLY_FINISH:
2090 5406 : if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
2091 : return false;
2092 : return true;
2093 2703 : case NB_OP_GET_ELEM:
2094 2703 : if (!CHECK_FLAG(snode->flags, LYS_CONFIG_R))
2095 : return false;
2096 :
2097 228 : switch (snode->nodetype) {
2098 : case LYS_LEAF:
2099 : case LYS_LEAFLIST:
2100 : break;
2101 27 : case LYS_CONTAINER:
2102 27 : scontainer = (struct lysc_node_container *)snode;
2103 27 : if (!CHECK_FLAG(scontainer->flags, LYS_PRESENCE))
2104 : return false;
2105 : break;
2106 : default:
2107 : return false;
2108 : }
2109 : return true;
2110 2703 : case NB_OP_GET_NEXT:
2111 2703 : switch (snode->nodetype) {
2112 153 : case LYS_LIST:
2113 153 : if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
2114 : return false;
2115 : break;
2116 0 : case LYS_LEAFLIST:
2117 0 : if (CHECK_FLAG(snode->flags, LYS_CONFIG_W))
2118 : return false;
2119 : break;
2120 : default:
2121 : return false;
2122 : }
2123 : return true;
2124 5406 : case NB_OP_GET_KEYS:
2125 : case NB_OP_LOOKUP_ENTRY:
2126 5406 : switch (snode->nodetype) {
2127 306 : case LYS_LIST:
2128 306 : if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
2129 : return false;
2130 54 : if (CHECK_FLAG(nb_node->flags, F_NB_NODE_KEYLESS_LIST))
2131 : return false;
2132 : break;
2133 : default:
2134 : return false;
2135 : }
2136 : return true;
2137 2703 : case NB_OP_RPC:
2138 2703 : if (CHECK_FLAG(snode->flags, LYS_CONFIG_W | LYS_CONFIG_R))
2139 : return false;
2140 :
2141 1230 : switch (snode->nodetype) {
2142 : case LYS_RPC:
2143 : case LYS_ACTION:
2144 : break;
2145 : default:
2146 : return false;
2147 : }
2148 : return true;
2149 : default:
2150 : return false;
2151 : }
2152 : }
2153 :
2154 0 : DEFINE_HOOK(nb_notification_send, (const char *xpath, struct list *arguments),
2155 : (xpath, arguments));
2156 :
2157 0 : int nb_notification_send(const char *xpath, struct list *arguments)
2158 : {
2159 0 : int ret;
2160 :
2161 0 : DEBUGD(&nb_dbg_notif, "northbound notification: %s", xpath);
2162 :
2163 0 : ret = hook_call(nb_notification_send, xpath, arguments);
2164 0 : if (arguments)
2165 0 : list_delete(&arguments);
2166 :
2167 0 : return ret;
2168 : }
2169 :
2170 : /* Running configuration user pointers management. */
2171 : struct nb_config_entry {
2172 : char xpath[XPATH_MAXLEN];
2173 : void *entry;
2174 : };
2175 :
2176 0 : static bool running_config_entry_cmp(const void *value1, const void *value2)
2177 : {
2178 0 : const struct nb_config_entry *c1 = value1;
2179 0 : const struct nb_config_entry *c2 = value2;
2180 :
2181 0 : return strmatch(c1->xpath, c2->xpath);
2182 : }
2183 :
2184 3 : static unsigned int running_config_entry_key_make(const void *value)
2185 : {
2186 3 : return string_hash_make(value);
2187 : }
2188 :
2189 3 : static void *running_config_entry_alloc(void *p)
2190 : {
2191 3 : struct nb_config_entry *new, *key = p;
2192 :
2193 3 : new = XCALLOC(MTYPE_NB_CONFIG_ENTRY, sizeof(*new));
2194 3 : strlcpy(new->xpath, key->xpath, sizeof(new->xpath));
2195 :
2196 3 : return new;
2197 : }
2198 :
2199 3 : static void running_config_entry_free(void *arg)
2200 : {
2201 3 : XFREE(MTYPE_NB_CONFIG_ENTRY, arg);
2202 0 : }
2203 :
2204 3 : void nb_running_set_entry(const struct lyd_node *dnode, void *entry)
2205 : {
2206 3 : struct nb_config_entry *config, s;
2207 :
2208 3 : yang_dnode_get_path(dnode, s.xpath, sizeof(s.xpath));
2209 3 : config = hash_get(running_config_entries, &s,
2210 : running_config_entry_alloc);
2211 3 : config->entry = entry;
2212 3 : }
2213 :
2214 0 : void nb_running_move_tree(const char *xpath_from, const char *xpath_to)
2215 : {
2216 0 : struct nb_config_entry *entry;
2217 0 : struct list *entries = hash_to_list(running_config_entries);
2218 0 : struct listnode *ln;
2219 :
2220 0 : for (ALL_LIST_ELEMENTS_RO(entries, ln, entry)) {
2221 0 : if (!frrstr_startswith(entry->xpath, xpath_from))
2222 0 : continue;
2223 :
2224 0 : hash_release(running_config_entries, entry);
2225 :
2226 0 : char *newpath =
2227 0 : frrstr_replace(entry->xpath, xpath_from, xpath_to);
2228 0 : strlcpy(entry->xpath, newpath, sizeof(entry->xpath));
2229 0 : XFREE(MTYPE_TMP, newpath);
2230 :
2231 0 : (void)hash_get(running_config_entries, entry,
2232 : hash_alloc_intern);
2233 : }
2234 :
2235 0 : list_delete(&entries);
2236 0 : }
2237 :
2238 0 : static void *nb_running_unset_entry_helper(const struct lyd_node *dnode)
2239 : {
2240 0 : struct nb_config_entry *config, s;
2241 0 : struct lyd_node *child;
2242 0 : void *entry = NULL;
2243 :
2244 0 : yang_dnode_get_path(dnode, s.xpath, sizeof(s.xpath));
2245 0 : config = hash_release(running_config_entries, &s);
2246 0 : if (config) {
2247 0 : entry = config->entry;
2248 0 : running_config_entry_free(config);
2249 : }
2250 :
2251 : /* Unset user pointers from the child nodes. */
2252 0 : if (CHECK_FLAG(dnode->schema->nodetype, LYS_LIST | LYS_CONTAINER)) {
2253 0 : LY_LIST_FOR (lyd_child(dnode), child) {
2254 0 : (void)nb_running_unset_entry_helper(child);
2255 : }
2256 : }
2257 :
2258 0 : return entry;
2259 : }
2260 :
2261 0 : void *nb_running_unset_entry(const struct lyd_node *dnode)
2262 : {
2263 0 : void *entry;
2264 :
2265 0 : entry = nb_running_unset_entry_helper(dnode);
2266 0 : assert(entry);
2267 :
2268 0 : return entry;
2269 : }
2270 :
2271 0 : static void *nb_running_get_entry_worker(const struct lyd_node *dnode,
2272 : const char *xpath,
2273 : bool abort_if_not_found,
2274 : bool rec_search)
2275 : {
2276 0 : const struct lyd_node *orig_dnode = dnode;
2277 0 : char xpath_buf[XPATH_MAXLEN];
2278 0 : bool rec_flag = true;
2279 :
2280 0 : assert(dnode || xpath);
2281 :
2282 0 : if (!dnode)
2283 0 : dnode = yang_dnode_get(running_config->dnode, xpath);
2284 :
2285 0 : while (rec_flag && dnode) {
2286 0 : struct nb_config_entry *config, s;
2287 :
2288 0 : yang_dnode_get_path(dnode, s.xpath, sizeof(s.xpath));
2289 0 : config = hash_lookup(running_config_entries, &s);
2290 0 : if (config)
2291 0 : return config->entry;
2292 :
2293 0 : rec_flag = rec_search;
2294 :
2295 0 : dnode = lyd_parent(dnode);
2296 : }
2297 :
2298 0 : if (!abort_if_not_found)
2299 : return NULL;
2300 :
2301 0 : yang_dnode_get_path(orig_dnode, xpath_buf, sizeof(xpath_buf));
2302 0 : flog_err(EC_LIB_YANG_DNODE_NOT_FOUND,
2303 : "%s: failed to find entry [xpath %s]", __func__, xpath_buf);
2304 0 : zlog_backtrace(LOG_ERR);
2305 0 : abort();
2306 : }
2307 :
2308 0 : void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath,
2309 : bool abort_if_not_found)
2310 : {
2311 0 : return nb_running_get_entry_worker(dnode, xpath, abort_if_not_found,
2312 : true);
2313 : }
2314 :
2315 0 : void *nb_running_get_entry_non_rec(const struct lyd_node *dnode,
2316 : const char *xpath, bool abort_if_not_found)
2317 : {
2318 0 : return nb_running_get_entry_worker(dnode, xpath, abort_if_not_found,
2319 : false);
2320 : }
2321 :
2322 : /* Logging functions. */
2323 0 : const char *nb_event_name(enum nb_event event)
2324 : {
2325 0 : switch (event) {
2326 : case NB_EV_VALIDATE:
2327 : return "validate";
2328 0 : case NB_EV_PREPARE:
2329 0 : return "prepare";
2330 0 : case NB_EV_ABORT:
2331 0 : return "abort";
2332 0 : case NB_EV_APPLY:
2333 0 : return "apply";
2334 : }
2335 :
2336 0 : assert(!"Reached end of function we should never hit");
2337 : }
2338 :
2339 0 : const char *nb_operation_name(enum nb_operation operation)
2340 : {
2341 0 : switch (operation) {
2342 : case NB_OP_CREATE:
2343 : return "create";
2344 0 : case NB_OP_MODIFY:
2345 0 : return "modify";
2346 0 : case NB_OP_DESTROY:
2347 0 : return "destroy";
2348 0 : case NB_OP_MOVE:
2349 0 : return "move";
2350 0 : case NB_OP_PRE_VALIDATE:
2351 0 : return "pre_validate";
2352 0 : case NB_OP_APPLY_FINISH:
2353 0 : return "apply_finish";
2354 0 : case NB_OP_GET_ELEM:
2355 0 : return "get_elem";
2356 0 : case NB_OP_GET_NEXT:
2357 0 : return "get_next";
2358 0 : case NB_OP_GET_KEYS:
2359 0 : return "get_keys";
2360 0 : case NB_OP_LOOKUP_ENTRY:
2361 0 : return "lookup_entry";
2362 0 : case NB_OP_RPC:
2363 0 : return "rpc";
2364 : }
2365 :
2366 0 : assert(!"Reached end of function we should never hit");
2367 : }
2368 :
2369 0 : const char *nb_err_name(enum nb_error error)
2370 : {
2371 0 : switch (error) {
2372 : case NB_OK:
2373 : return "ok";
2374 0 : case NB_ERR:
2375 0 : return "generic error";
2376 0 : case NB_ERR_NO_CHANGES:
2377 0 : return "no changes";
2378 0 : case NB_ERR_NOT_FOUND:
2379 0 : return "element not found";
2380 0 : case NB_ERR_LOCKED:
2381 0 : return "resource is locked";
2382 0 : case NB_ERR_VALIDATION:
2383 0 : return "validation";
2384 0 : case NB_ERR_RESOURCE:
2385 0 : return "failed to allocate resource";
2386 0 : case NB_ERR_INCONSISTENCY:
2387 0 : return "internal inconsistency";
2388 : }
2389 :
2390 0 : assert(!"Reached end of function we should never hit");
2391 : }
2392 :
2393 0 : const char *nb_client_name(enum nb_client client)
2394 : {
2395 0 : switch (client) {
2396 : case NB_CLIENT_CLI:
2397 : return "CLI";
2398 0 : case NB_CLIENT_CONFD:
2399 0 : return "ConfD";
2400 0 : case NB_CLIENT_SYSREPO:
2401 0 : return "Sysrepo";
2402 0 : case NB_CLIENT_GRPC:
2403 0 : return "gRPC";
2404 0 : case NB_CLIENT_PCEP:
2405 0 : return "Pcep";
2406 0 : case NB_CLIENT_NONE:
2407 0 : return "None";
2408 : }
2409 :
2410 0 : assert(!"Reached end of function we should never hit");
2411 : }
2412 :
2413 36 : static void nb_load_callbacks(const struct frr_yang_module_info *module)
2414 : {
2415 930 : for (size_t i = 0; module->nodes[i].xpath; i++) {
2416 894 : struct nb_node *nb_node;
2417 894 : uint32_t priority;
2418 :
2419 894 : if (i > YANG_MODULE_MAX_NODES) {
2420 0 : zlog_err(
2421 : "%s: %s.yang has more than %u nodes. Please increase YANG_MODULE_MAX_NODES to fix this problem.",
2422 : __func__, module->name, YANG_MODULE_MAX_NODES);
2423 0 : exit(1);
2424 : }
2425 :
2426 894 : nb_node = nb_node_find(module->nodes[i].xpath);
2427 894 : if (!nb_node) {
2428 0 : flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
2429 : "%s: unknown data path: %s", __func__,
2430 : module->nodes[i].xpath);
2431 0 : continue;
2432 : }
2433 :
2434 894 : nb_node->cbs = module->nodes[i].cbs;
2435 894 : priority = module->nodes[i].priority;
2436 894 : if (priority != 0)
2437 6 : nb_node->priority = priority;
2438 : }
2439 36 : }
2440 :
2441 6 : void nb_validate_callbacks(void)
2442 : {
2443 6 : unsigned int errors = 0;
2444 :
2445 6 : yang_snodes_iterate(NULL, nb_node_validate, 0, &errors);
2446 6 : if (errors > 0) {
2447 0 : flog_err(
2448 : EC_LIB_NB_CBS_VALIDATION,
2449 : "%s: failed to validate northbound callbacks: %u error(s)",
2450 : __func__, errors);
2451 0 : exit(1);
2452 : }
2453 6 : }
2454 :
2455 :
2456 6 : void nb_init(struct thread_master *tm,
2457 : const struct frr_yang_module_info *const modules[],
2458 : size_t nmodules, bool db_enabled)
2459 6 : {
2460 6 : struct yang_module *loaded[nmodules], **loadedp = loaded;
2461 6 : bool explicit_compile;
2462 :
2463 : /*
2464 : * Currently using this explicit compile feature in libyang2 leads to
2465 : * incorrect behavior in FRR. The functionality suppresses the compiling
2466 : * of modules until they have all been loaded into the context. This
2467 : * avoids multiple recompiles of the same modules as they are
2468 : * imported/augmented etc.
2469 : */
2470 6 : explicit_compile = false;
2471 :
2472 6 : nb_db_enabled = db_enabled;
2473 :
2474 6 : yang_init(true, explicit_compile);
2475 :
2476 : /* Load YANG modules and their corresponding northbound callbacks. */
2477 42 : for (size_t i = 0; i < nmodules; i++) {
2478 36 : DEBUGD(&nb_dbg_events, "northbound: loading %s.yang",
2479 : modules[i]->name);
2480 36 : *loadedp++ = yang_module_load(modules[i]->name);
2481 : }
2482 :
2483 : if (explicit_compile)
2484 : yang_init_loading_complete();
2485 :
2486 : /* Initialize the compiled nodes with northbound data */
2487 42 : for (size_t i = 0; i < nmodules; i++) {
2488 36 : yang_snodes_iterate(loaded[i]->info, nb_node_new_cb, 0, NULL);
2489 36 : nb_load_callbacks(modules[i]);
2490 : }
2491 :
2492 : /* Validate northbound callbacks. */
2493 6 : nb_validate_callbacks();
2494 :
2495 : /* Create an empty running configuration. */
2496 6 : running_config = nb_config_new(NULL);
2497 6 : running_config_entries = hash_create(running_config_entry_key_make,
2498 : running_config_entry_cmp,
2499 : "Running Configuration Entries");
2500 6 : pthread_mutex_init(&running_config_mgmt_lock.mtx, NULL);
2501 :
2502 : /* Initialize the northbound CLI. */
2503 6 : nb_cli_init(tm);
2504 6 : }
2505 :
2506 12 : void nb_terminate(void)
2507 : {
2508 : /* Terminate the northbound CLI. */
2509 12 : nb_cli_terminate();
2510 :
2511 : /* Delete all nb_node's from all YANG modules. */
2512 12 : nb_nodes_delete();
2513 :
2514 : /* Delete the running configuration. */
2515 12 : hash_clean(running_config_entries, running_config_entry_free);
2516 12 : hash_free(running_config_entries);
2517 12 : nb_config_free(running_config);
2518 12 : pthread_mutex_destroy(&running_config_mgmt_lock.mtx);
2519 12 : }
|