Coverage Report

Created: 2025-07-14 06:48

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