Coverage Report

Created: 2025-11-09 06:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/lib/mgmt_be_client.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
 * MGMTD Backend Client Library api interfaces
4
 * Copyright (C) 2021  Vmware, Inc.
5
 *           Pushpasis Sarkar <spushpasis@vmware.com>
6
 */
7
8
#include <zebra.h>
9
#include "debug.h"
10
#include "compiler.h"
11
#include "libfrr.h"
12
#include "mgmtd/mgmt.h"
13
#include "mgmt_be_client.h"
14
#include "mgmt_msg.h"
15
#include "mgmt_pb.h"
16
#include "network.h"
17
#include "northbound.h"
18
#include "stream.h"
19
#include "sockopt.h"
20
21
#include "lib/mgmt_be_client_clippy.c"
22
23
8
DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_CLIENT, "backend client");
24
8
DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_CLIENT_NAME, "backend client name");
25
8
DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_BATCH, "backend transaction batch data");
26
8
DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_TXN, "backend transaction data");
27
8
28
8
enum mgmt_be_txn_event {
29
8
  MGMTD_BE_TXN_PROC_SETCFG = 1,
30
8
  MGMTD_BE_TXN_PROC_GETCFG,
31
8
  MGMTD_BE_TXN_PROC_GETDATA
32
8
};
33
8
34
8
struct mgmt_be_set_cfg_req {
35
8
  struct nb_cfg_change cfg_changes[MGMTD_MAX_CFG_CHANGES_IN_BATCH];
36
8
  uint16_t num_cfg_changes;
37
8
};
38
8
39
8
struct mgmt_be_get_data_req {
40
8
  char *xpaths[MGMTD_MAX_NUM_DATA_REQ_IN_BATCH];
41
8
  uint16_t num_xpaths;
42
8
};
43
8
44
8
struct mgmt_be_txn_req {
45
8
  enum mgmt_be_txn_event event;
46
8
  union {
47
8
    struct mgmt_be_set_cfg_req set_cfg;
48
8
    struct mgmt_be_get_data_req get_data;
49
8
  } req;
50
8
};
51
8
52
8
PREDECL_LIST(mgmt_be_batches);
53
8
struct mgmt_be_batch_ctx {
54
8
  /* Batch-Id as assigned by MGMTD */
55
8
  uint64_t batch_id;
56
8
57
8
  struct mgmt_be_txn_req txn_req;
58
8
59
8
  uint32_t flags;
60
8
61
8
  struct mgmt_be_batches_item list_linkage;
62
8
};
63
8
#define MGMTD_BE_BATCH_FLAGS_CFG_PREPARED (1U << 0)
64
8
#define MGMTD_BE_TXN_FLAGS_CFG_APPLIED (1U << 1)
65
8
DECLARE_LIST(mgmt_be_batches, struct mgmt_be_batch_ctx, list_linkage);
66
67
PREDECL_LIST(mgmt_be_txns);
68
struct mgmt_be_txn_ctx {
69
  /* Txn-Id as assigned by MGMTD */
70
  uint64_t txn_id;
71
  uint32_t flags;
72
73
  struct mgmt_be_client_txn_ctx client_data;
74
  struct mgmt_be_client *client;
75
76
  /* List of batches belonging to this transaction */
77
  struct mgmt_be_batches_head cfg_batches;
78
  struct mgmt_be_batches_head apply_cfgs;
79
80
  struct mgmt_be_txns_item list_linkage;
81
82
  struct nb_transaction *nb_txn;
83
  uint32_t nb_txn_id;
84
};
85
#define MGMTD_BE_TXN_FLAGS_CFGPREP_FAILED (1U << 1)
86
87
0
DECLARE_LIST(mgmt_be_txns, struct mgmt_be_txn_ctx, list_linkage);
88
89
#define FOREACH_BE_TXN_BATCH_IN_LIST(txn, batch)                               \
90
0
  frr_each_safe (mgmt_be_batches, &(txn)->cfg_batches, (batch))
91
92
#define FOREACH_BE_APPLY_BATCH_IN_LIST(txn, batch)                             \
93
0
  frr_each_safe (mgmt_be_batches, &(txn)->apply_cfgs, (batch))
94
95
struct mgmt_be_client {
96
  struct msg_client client;
97
98
  char *name;
99
100
  struct nb_config *candidate_config;
101
  struct nb_config *running_config;
102
103
  unsigned long num_edit_nb_cfg;
104
  unsigned long avg_edit_nb_cfg_tm;
105
  unsigned long num_prep_nb_cfg;
106
  unsigned long avg_prep_nb_cfg_tm;
107
  unsigned long num_apply_nb_cfg;
108
  unsigned long avg_apply_nb_cfg_tm;
109
110
  struct mgmt_be_txns_head txn_head;
111
112
  struct mgmt_be_client_cbs cbs;
113
  uintptr_t user_data;
114
};
115
116
#define FOREACH_BE_TXN_IN_LIST(client_ctx, txn)                                \
117
0
  frr_each_safe (mgmt_be_txns, &(client_ctx)->txn_head, (txn))
118
119
struct debug mgmt_dbg_be_client = {0, "Management backend client operations"};
120
121
const char *mgmt_be_client_names[MGMTD_BE_CLIENT_ID_MAX + 1] = {
122
#ifdef HAVE_STATICD
123
  [MGMTD_BE_CLIENT_ID_STATICD] = "staticd",
124
#endif
125
  [MGMTD_BE_CLIENT_ID_MAX] = "Unknown/Invalid",
126
};
127
128
static int mgmt_be_client_send_msg(struct mgmt_be_client *client_ctx,
129
           Mgmtd__BeMessage *be_msg)
130
0
{
131
0
  return msg_conn_send_msg(
132
0
    &client_ctx->client.conn, MGMT_MSG_VERSION_PROTOBUF, be_msg,
133
0
    mgmtd__be_message__get_packed_size(be_msg),
134
0
    (size_t(*)(void *, void *))mgmtd__be_message__pack, false);
135
0
}
136
137
static struct mgmt_be_batch_ctx *
138
mgmt_be_find_batch_by_id(struct mgmt_be_txn_ctx *txn,
139
          uint64_t batch_id)
140
0
{
141
0
  struct mgmt_be_batch_ctx *batch = NULL;
142
143
0
  FOREACH_BE_TXN_BATCH_IN_LIST (txn, batch) {
144
0
    if (batch->batch_id == batch_id)
145
0
      return batch;
146
0
  }
147
148
0
  return NULL;
149
0
}
150
151
static struct mgmt_be_batch_ctx *
152
mgmt_be_batch_create(struct mgmt_be_txn_ctx *txn, uint64_t batch_id)
153
0
{
154
0
  struct mgmt_be_batch_ctx *batch = NULL;
155
156
0
  batch = mgmt_be_find_batch_by_id(txn, batch_id);
157
0
  if (!batch) {
158
0
    batch = XCALLOC(MTYPE_MGMTD_BE_BATCH,
159
0
        sizeof(struct mgmt_be_batch_ctx));
160
0
    assert(batch);
161
162
0
    batch->batch_id = batch_id;
163
0
    mgmt_be_batches_add_tail(&txn->cfg_batches, batch);
164
165
0
    MGMTD_BE_CLIENT_DBG("Added new batch-id: %" PRIu64
166
0
            " to transaction",
167
0
            batch_id);
168
0
  }
169
170
0
  return batch;
171
0
}
172
173
static void mgmt_be_batch_delete(struct mgmt_be_txn_ctx *txn,
174
            struct mgmt_be_batch_ctx **batch)
175
0
{
176
0
  uint16_t indx;
177
178
0
  if (!batch)
179
0
    return;
180
181
0
  mgmt_be_batches_del(&txn->cfg_batches, *batch);
182
0
  if ((*batch)->txn_req.event == MGMTD_BE_TXN_PROC_SETCFG) {
183
0
    for (indx = 0; indx < MGMTD_MAX_CFG_CHANGES_IN_BATCH; indx++) {
184
0
      if ((*batch)->txn_req.req.set_cfg.cfg_changes[indx]
185
0
            .value) {
186
0
        free((char *)(*batch)
187
0
               ->txn_req.req.set_cfg
188
0
               .cfg_changes[indx]
189
0
               .value);
190
0
      }
191
0
    }
192
0
  }
193
194
0
  XFREE(MTYPE_MGMTD_BE_BATCH, *batch);
195
0
  *batch = NULL;
196
0
}
197
198
static void mgmt_be_cleanup_all_batches(struct mgmt_be_txn_ctx *txn)
199
0
{
200
0
  struct mgmt_be_batch_ctx *batch = NULL;
201
202
0
  FOREACH_BE_TXN_BATCH_IN_LIST (txn, batch) {
203
0
    mgmt_be_batch_delete(txn, &batch);
204
0
  }
205
206
0
  FOREACH_BE_APPLY_BATCH_IN_LIST (txn, batch) {
207
0
    mgmt_be_batch_delete(txn, &batch);
208
0
  }
209
0
}
210
211
static struct mgmt_be_txn_ctx *
212
mgmt_be_find_txn_by_id(struct mgmt_be_client *client_ctx, uint64_t txn_id,
213
           bool warn)
214
0
{
215
0
  struct mgmt_be_txn_ctx *txn = NULL;
216
217
0
  FOREACH_BE_TXN_IN_LIST (client_ctx, txn)
218
0
    if (txn->txn_id == txn_id)
219
0
      return txn;
220
0
  if (warn)
221
0
    MGMTD_BE_CLIENT_ERR("Unknown txn-id: %" PRIu64, txn_id);
222
223
0
  return NULL;
224
0
}
225
226
static struct mgmt_be_txn_ctx *
227
mgmt_be_txn_create(struct mgmt_be_client *client_ctx, uint64_t txn_id)
228
0
{
229
0
  struct mgmt_be_txn_ctx *txn = NULL;
230
231
0
  txn = mgmt_be_find_txn_by_id(client_ctx, txn_id, false);
232
0
  if (txn) {
233
0
    MGMTD_BE_CLIENT_ERR("Can't create existing txn-id: %" PRIu64,
234
0
            txn_id);
235
0
    return NULL;
236
0
  }
237
238
0
  txn = XCALLOC(MTYPE_MGMTD_BE_TXN, sizeof(struct mgmt_be_txn_ctx));
239
0
  txn->txn_id = txn_id;
240
0
  txn->client = client_ctx;
241
0
  mgmt_be_batches_init(&txn->cfg_batches);
242
0
  mgmt_be_batches_init(&txn->apply_cfgs);
243
0
  mgmt_be_txns_add_tail(&client_ctx->txn_head, txn);
244
245
0
  MGMTD_BE_CLIENT_DBG("Created new txn-id: %" PRIu64, txn_id);
246
247
0
  return txn;
248
0
}
249
250
static void mgmt_be_txn_delete(struct mgmt_be_client *client_ctx,
251
             struct mgmt_be_txn_ctx **txn)
252
0
{
253
0
  char err_msg[] = "MGMT Transaction Delete";
254
255
0
  if (!txn)
256
0
    return;
257
258
  /*
259
   * Remove the transaction from the list of transactions
260
   * so that future lookups with the same transaction id
261
   * does not return this one.
262
   */
263
0
  mgmt_be_txns_del(&client_ctx->txn_head, *txn);
264
265
  /*
266
   * Time to delete the transaction which should also
267
   * take care of cleaning up all batches created via
268
   * CFGDATA_CREATE_REQs. But first notify the client
269
   * about the transaction delete.
270
   */
271
0
  if (client_ctx->cbs.txn_notify)
272
0
    (void)(*client_ctx->cbs.txn_notify)(client_ctx,
273
0
                client_ctx->user_data,
274
0
                &(*txn)->client_data, true);
275
276
0
  mgmt_be_cleanup_all_batches(*txn);
277
0
  if ((*txn)->nb_txn)
278
0
    nb_candidate_commit_abort((*txn)->nb_txn, err_msg,
279
0
          sizeof(err_msg));
280
0
  XFREE(MTYPE_MGMTD_BE_TXN, *txn);
281
282
0
  *txn = NULL;
283
0
}
284
285
static void mgmt_be_cleanup_all_txns(struct mgmt_be_client *client_ctx)
286
0
{
287
0
  struct mgmt_be_txn_ctx *txn = NULL;
288
289
0
  FOREACH_BE_TXN_IN_LIST (client_ctx, txn) {
290
0
    mgmt_be_txn_delete(client_ctx, &txn);
291
0
  }
292
0
}
293
294
static int mgmt_be_send_txn_reply(struct mgmt_be_client *client_ctx,
295
          uint64_t txn_id, bool create)
296
0
{
297
0
  Mgmtd__BeMessage be_msg;
298
0
  Mgmtd__BeTxnReply txn_reply;
299
300
0
  mgmtd__be_txn_reply__init(&txn_reply);
301
0
  txn_reply.create = create;
302
0
  txn_reply.txn_id = txn_id;
303
0
  txn_reply.success = true;
304
305
0
  mgmtd__be_message__init(&be_msg);
306
0
  be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_TXN_REPLY;
307
0
  be_msg.txn_reply = &txn_reply;
308
309
0
  MGMTD_BE_CLIENT_DBG("Sending TXN_REPLY txn-id %" PRIu64, txn_id);
310
311
0
  return mgmt_be_client_send_msg(client_ctx, &be_msg);
312
0
}
313
314
static int mgmt_be_process_txn_req(struct mgmt_be_client *client_ctx,
315
           uint64_t txn_id, bool create)
316
0
{
317
0
  struct mgmt_be_txn_ctx *txn;
318
319
0
  if (create) {
320
0
    MGMTD_BE_CLIENT_DBG("Creating new txn-id %" PRIu64, txn_id);
321
322
0
    txn = mgmt_be_txn_create(client_ctx, txn_id);
323
0
    if (!txn)
324
0
      goto failed;
325
326
0
    if (client_ctx->cbs.txn_notify)
327
0
      (*client_ctx->cbs.txn_notify)(client_ctx,
328
0
                  client_ctx->user_data,
329
0
                  &txn->client_data, false);
330
0
  } else {
331
0
    MGMTD_BE_CLIENT_DBG("Deleting txn-id: %" PRIu64, txn_id);
332
0
    txn = mgmt_be_find_txn_by_id(client_ctx, txn_id, false);
333
0
    if (txn)
334
0
      mgmt_be_txn_delete(client_ctx, &txn);
335
0
  }
336
337
0
  return mgmt_be_send_txn_reply(client_ctx, txn_id, create);
338
339
0
failed:
340
0
  msg_conn_disconnect(&client_ctx->client.conn, true);
341
0
  return -1;
342
0
}
343
344
static int mgmt_be_send_cfgdata_create_reply(struct mgmt_be_client *client_ctx,
345
               uint64_t txn_id, uint64_t batch_id,
346
               bool success,
347
               const char *error_if_any)
348
0
{
349
0
  Mgmtd__BeMessage be_msg;
350
0
  Mgmtd__BeCfgDataCreateReply cfgdata_reply;
351
352
0
  mgmtd__be_cfg_data_create_reply__init(&cfgdata_reply);
353
0
  cfgdata_reply.txn_id = (uint64_t)txn_id;
354
0
  cfgdata_reply.batch_id = (uint64_t)batch_id;
355
0
  cfgdata_reply.success = success;
356
0
  if (error_if_any)
357
0
    cfgdata_reply.error_if_any = (char *)error_if_any;
358
359
0
  mgmtd__be_message__init(&be_msg);
360
0
  be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REPLY;
361
0
  be_msg.cfg_data_reply = &cfgdata_reply;
362
363
0
  MGMTD_BE_CLIENT_DBG("Sending CFGDATA_CREATE_REPLY txn-id: %" PRIu64
364
0
          " batch-id: %" PRIu64,
365
0
          txn_id, batch_id);
366
367
0
  return mgmt_be_client_send_msg(client_ctx, &be_msg);
368
0
}
369
370
static void mgmt_be_txn_cfg_abort(struct mgmt_be_txn_ctx *txn)
371
0
{
372
0
  char errmsg[BUFSIZ] = {0};
373
374
0
  assert(txn && txn->client);
375
0
  if (txn->nb_txn) {
376
0
    MGMTD_BE_CLIENT_ERR(
377
0
      "Aborting configs after prep for txn-id: %" PRIu64,
378
0
      txn->txn_id);
379
0
    nb_candidate_commit_abort(txn->nb_txn, errmsg, sizeof(errmsg));
380
0
    txn->nb_txn = 0;
381
0
  }
382
383
  /*
384
   * revert candidate back to running
385
   *
386
   * This is one txn ctx but the candidate_config is per client ctx, how
387
   * does that work?
388
   */
389
0
  MGMTD_BE_CLIENT_DBG(
390
0
    "Reset candidate configurations after abort of txn-id: %" PRIu64,
391
0
    txn->txn_id);
392
0
  nb_config_replace(txn->client->candidate_config,
393
0
        txn->client->running_config, true);
394
0
}
395
396
static int mgmt_be_txn_cfg_prepare(struct mgmt_be_txn_ctx *txn)
397
0
{
398
0
  struct mgmt_be_client *client_ctx;
399
0
  struct mgmt_be_txn_req *txn_req = NULL;
400
0
  struct nb_context nb_ctx = {0};
401
0
  struct timeval edit_nb_cfg_start;
402
0
  struct timeval edit_nb_cfg_end;
403
0
  unsigned long edit_nb_cfg_tm;
404
0
  struct timeval prep_nb_cfg_start;
405
0
  struct timeval prep_nb_cfg_end;
406
0
  unsigned long prep_nb_cfg_tm;
407
0
  struct mgmt_be_batch_ctx *batch;
408
0
  bool error;
409
0
  char err_buf[BUFSIZ];
410
0
  size_t num_processed;
411
0
  int err;
412
413
0
  assert(txn && txn->client);
414
0
  client_ctx = txn->client;
415
416
0
  num_processed = 0;
417
0
  FOREACH_BE_TXN_BATCH_IN_LIST (txn, batch) {
418
0
    txn_req = &batch->txn_req;
419
0
    error = false;
420
0
    nb_ctx.client = NB_CLIENT_CLI;
421
0
    nb_ctx.user = (void *)client_ctx->user_data;
422
423
0
    if (!txn->nb_txn) {
424
      /*
425
       * This happens when the current backend client is only
426
       * interested in consuming the config items but is not
427
       * interested in validating it.
428
       */
429
0
      error = false;
430
431
0
      gettimeofday(&edit_nb_cfg_start, NULL);
432
0
      nb_candidate_edit_config_changes(
433
0
        client_ctx->candidate_config,
434
0
        txn_req->req.set_cfg.cfg_changes,
435
0
        (size_t)txn_req->req.set_cfg.num_cfg_changes,
436
0
        NULL, NULL, 0, err_buf, sizeof(err_buf),
437
0
        &error);
438
0
      if (error) {
439
0
        err_buf[sizeof(err_buf) - 1] = 0;
440
0
        MGMTD_BE_CLIENT_ERR(
441
0
          "Failed to update configs for txn-id: %" PRIu64
442
0
          " batch-id: %" PRIu64
443
0
          " to candidate, err: '%s'",
444
0
          txn->txn_id, batch->batch_id, err_buf);
445
0
        return -1;
446
0
      }
447
0
      gettimeofday(&edit_nb_cfg_end, NULL);
448
0
      edit_nb_cfg_tm = timeval_elapsed(edit_nb_cfg_end,
449
0
               edit_nb_cfg_start);
450
0
      client_ctx->avg_edit_nb_cfg_tm =
451
0
        ((client_ctx->avg_edit_nb_cfg_tm *
452
0
          client_ctx->num_edit_nb_cfg) +
453
0
         edit_nb_cfg_tm) /
454
0
        (client_ctx->num_edit_nb_cfg + 1);
455
0
      client_ctx->num_edit_nb_cfg++;
456
0
    }
457
458
0
    num_processed++;
459
0
  }
460
461
0
  if (!num_processed)
462
0
    return 0;
463
464
  /*
465
   * Now prepare all the batches we have applied in one go.
466
   */
467
0
  nb_ctx.client = NB_CLIENT_CLI;
468
0
  nb_ctx.user = (void *)client_ctx->user_data;
469
470
0
  gettimeofday(&prep_nb_cfg_start, NULL);
471
0
  err = nb_candidate_commit_prepare(nb_ctx, client_ctx->candidate_config,
472
0
            "MGMTD Backend Txn", &txn->nb_txn,
473
#ifdef MGMTD_LOCAL_VALIDATIONS_ENABLED
474
            true, true,
475
#else
476
0
            false, true,
477
0
#endif
478
0
            err_buf, sizeof(err_buf) - 1);
479
0
  if (err != NB_OK) {
480
0
    err_buf[sizeof(err_buf) - 1] = 0;
481
0
    if (err == NB_ERR_VALIDATION)
482
0
      MGMTD_BE_CLIENT_ERR(
483
0
        "Failed to validate configs txn-id: %" PRIu64
484
0
        " %zu batches, err: '%s'",
485
0
        txn->txn_id, num_processed, err_buf);
486
0
    else
487
0
      MGMTD_BE_CLIENT_ERR(
488
0
        "Failed to prepare configs for txn-id: %" PRIu64
489
0
        " %zu batches, err: '%s'",
490
0
        txn->txn_id, num_processed, err_buf);
491
0
    error = true;
492
0
    SET_FLAG(txn->flags, MGMTD_BE_TXN_FLAGS_CFGPREP_FAILED);
493
0
  } else
494
0
    MGMTD_BE_CLIENT_DBG("Prepared configs for txn-id: %" PRIu64
495
0
            " %zu batches",
496
0
            txn->txn_id, num_processed);
497
498
0
  gettimeofday(&prep_nb_cfg_end, NULL);
499
0
  prep_nb_cfg_tm = timeval_elapsed(prep_nb_cfg_end, prep_nb_cfg_start);
500
0
  client_ctx->avg_prep_nb_cfg_tm = ((client_ctx->avg_prep_nb_cfg_tm *
501
0
             client_ctx->num_prep_nb_cfg) +
502
0
            prep_nb_cfg_tm) /
503
0
           (client_ctx->num_prep_nb_cfg + 1);
504
0
  client_ctx->num_prep_nb_cfg++;
505
506
0
  FOREACH_BE_TXN_BATCH_IN_LIST (txn, batch) {
507
0
    mgmt_be_send_cfgdata_create_reply(
508
0
      client_ctx, txn->txn_id, batch->batch_id,
509
0
      error ? false : true, error ? err_buf : NULL);
510
0
    if (!error) {
511
0
      SET_FLAG(batch->flags,
512
0
         MGMTD_BE_BATCH_FLAGS_CFG_PREPARED);
513
0
      mgmt_be_batches_del(&txn->cfg_batches, batch);
514
0
      mgmt_be_batches_add_tail(&txn->apply_cfgs, batch);
515
0
    }
516
0
  }
517
518
0
  MGMTD_BE_CLIENT_DBG(
519
0
    "Avg-nb-edit-duration %lu uSec, nb-prep-duration %lu (avg: %lu) uSec, batch size %u",
520
0
    client_ctx->avg_edit_nb_cfg_tm, prep_nb_cfg_tm,
521
0
    client_ctx->avg_prep_nb_cfg_tm, (uint32_t)num_processed);
522
523
0
  if (error)
524
0
    mgmt_be_txn_cfg_abort(txn);
525
526
0
  return 0;
527
0
}
528
529
/*
530
 * Process all CFG_DATA_REQs received so far and prepare them all in one go.
531
 */
532
static int mgmt_be_update_setcfg_in_batch(struct mgmt_be_client *client_ctx,
533
            struct mgmt_be_txn_ctx *txn,
534
            uint64_t batch_id,
535
            Mgmtd__YangCfgDataReq *cfg_req[],
536
            int num_req)
537
0
{
538
0
  struct mgmt_be_batch_ctx *batch = NULL;
539
0
  struct mgmt_be_txn_req *txn_req = NULL;
540
0
  int index;
541
0
  struct nb_cfg_change *cfg_chg;
542
543
0
  batch = mgmt_be_batch_create(txn, batch_id);
544
0
  if (!batch) {
545
0
    MGMTD_BE_CLIENT_ERR("Batch create failed!");
546
0
    return -1;
547
0
  }
548
549
0
  txn_req = &batch->txn_req;
550
0
  txn_req->event = MGMTD_BE_TXN_PROC_SETCFG;
551
0
  MGMTD_BE_CLIENT_DBG("Created SETCFG request for batch-id: %" PRIu64
552
0
          " txn-id: %" PRIu64 " cfg-items:%d",
553
0
          batch_id, txn->txn_id, num_req);
554
555
0
  txn_req->req.set_cfg.num_cfg_changes = num_req;
556
0
  for (index = 0; index < num_req; index++) {
557
0
    cfg_chg = &txn_req->req.set_cfg.cfg_changes[index];
558
559
0
    if (cfg_req[index]->req_type
560
0
        == MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA)
561
0
      cfg_chg->operation = NB_OP_DESTROY;
562
0
    else
563
0
      cfg_chg->operation = NB_OP_CREATE;
564
565
0
    strlcpy(cfg_chg->xpath, cfg_req[index]->data->xpath,
566
0
      sizeof(cfg_chg->xpath));
567
0
    cfg_chg->value = (cfg_req[index]->data->value
568
0
              && cfg_req[index]
569
0
                   ->data->value
570
0
                   ->encoded_str_val
571
0
            ? strdup(cfg_req[index]
572
0
                 ->data->value
573
0
                 ->encoded_str_val)
574
0
            : NULL);
575
0
    if (cfg_chg->value
576
0
        && !strncmp(cfg_chg->value, MGMTD_BE_CONTAINER_NODE_VAL,
577
0
        strlen(MGMTD_BE_CONTAINER_NODE_VAL))) {
578
0
      free((char *)cfg_chg->value);
579
0
      cfg_chg->value = NULL;
580
0
    }
581
0
  }
582
583
0
  return 0;
584
0
}
585
586
static int mgmt_be_process_cfgdata_req(struct mgmt_be_client *client_ctx,
587
               uint64_t txn_id, uint64_t batch_id,
588
               Mgmtd__YangCfgDataReq *cfg_req[],
589
               int num_req, bool end_of_data)
590
0
{
591
0
  struct mgmt_be_txn_ctx *txn;
592
593
0
  txn = mgmt_be_find_txn_by_id(client_ctx, txn_id, true);
594
0
  if (!txn)
595
0
    goto failed;
596
597
0
  mgmt_be_update_setcfg_in_batch(client_ctx, txn, batch_id, cfg_req,
598
0
               num_req);
599
600
0
  if (txn && end_of_data) {
601
0
    MGMTD_BE_CLIENT_DBG("End of data; CFG_PREPARE_REQ processing");
602
0
    if (mgmt_be_txn_cfg_prepare(txn))
603
0
      goto failed;
604
0
  }
605
606
0
  return 0;
607
0
failed:
608
0
  msg_conn_disconnect(&client_ctx->client.conn, true);
609
0
  return -1;
610
0
}
611
612
static int mgmt_be_send_apply_reply(struct mgmt_be_client *client_ctx,
613
            uint64_t txn_id, uint64_t batch_ids[],
614
            size_t num_batch_ids, bool success,
615
            const char *error_if_any)
616
0
{
617
0
  Mgmtd__BeMessage be_msg;
618
0
  Mgmtd__BeCfgDataApplyReply apply_reply;
619
620
0
  mgmtd__be_cfg_data_apply_reply__init(&apply_reply);
621
0
  apply_reply.success = success;
622
0
  apply_reply.txn_id = txn_id;
623
0
  apply_reply.batch_ids = (uint64_t *)batch_ids;
624
0
  apply_reply.n_batch_ids = num_batch_ids;
625
626
0
  if (error_if_any)
627
0
    apply_reply.error_if_any = (char *)error_if_any;
628
629
0
  mgmtd__be_message__init(&be_msg);
630
0
  be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REPLY;
631
0
  be_msg.cfg_apply_reply = &apply_reply;
632
633
0
  MGMTD_BE_CLIENT_DBG(
634
0
    "Sending CFG_APPLY_REPLY txn-id %" PRIu64
635
0
    " %zu batch ids %" PRIu64 " - %" PRIu64,
636
0
    txn_id, num_batch_ids,
637
0
    success && num_batch_ids ? batch_ids[0] : 0,
638
0
    success && num_batch_ids ? batch_ids[num_batch_ids - 1] : 0);
639
640
0
  return mgmt_be_client_send_msg(client_ctx, &be_msg);
641
0
}
642
643
static int mgmt_be_txn_proc_cfgapply(struct mgmt_be_txn_ctx *txn)
644
0
{
645
0
  struct mgmt_be_client *client_ctx;
646
0
  struct timeval apply_nb_cfg_start;
647
0
  struct timeval apply_nb_cfg_end;
648
0
  unsigned long apply_nb_cfg_tm;
649
0
  struct mgmt_be_batch_ctx *batch;
650
0
  char err_buf[BUFSIZ];
651
0
  size_t num_processed;
652
0
  static uint64_t batch_ids[MGMTD_BE_MAX_BATCH_IDS_IN_REQ];
653
654
0
  assert(txn && txn->client);
655
0
  client_ctx = txn->client;
656
657
0
  assert(txn->nb_txn);
658
0
  num_processed = 0;
659
660
  /*
661
   * Now apply all the batches we have applied in one go.
662
   */
663
0
  gettimeofday(&apply_nb_cfg_start, NULL);
664
0
  (void)nb_candidate_commit_apply(txn->nb_txn, true, &txn->nb_txn_id,
665
0
          err_buf, sizeof(err_buf) - 1);
666
0
  gettimeofday(&apply_nb_cfg_end, NULL);
667
668
0
  apply_nb_cfg_tm = timeval_elapsed(apply_nb_cfg_end, apply_nb_cfg_start);
669
0
  client_ctx->avg_apply_nb_cfg_tm = ((client_ctx->avg_apply_nb_cfg_tm *
670
0
              client_ctx->num_apply_nb_cfg) +
671
0
             apply_nb_cfg_tm) /
672
0
            (client_ctx->num_apply_nb_cfg + 1);
673
0
  client_ctx->num_apply_nb_cfg++;
674
0
  txn->nb_txn = NULL;
675
676
  /*
677
   * Send back CFG_APPLY_REPLY for all batches applied.
678
   */
679
0
  FOREACH_BE_APPLY_BATCH_IN_LIST (txn, batch) {
680
    /*
681
     * No need to delete the batch yet. Will be deleted during
682
     * transaction cleanup on receiving TXN_DELETE_REQ.
683
     */
684
0
    SET_FLAG(batch->flags, MGMTD_BE_TXN_FLAGS_CFG_APPLIED);
685
0
    mgmt_be_batches_del(&txn->apply_cfgs, batch);
686
0
    mgmt_be_batches_add_tail(&txn->cfg_batches, batch);
687
688
0
    batch_ids[num_processed] = batch->batch_id;
689
0
    num_processed++;
690
0
    if (num_processed == MGMTD_BE_MAX_BATCH_IDS_IN_REQ) {
691
0
      mgmt_be_send_apply_reply(client_ctx, txn->txn_id,
692
0
                batch_ids, num_processed,
693
0
                true, NULL);
694
0
      num_processed = 0;
695
0
    }
696
0
  }
697
698
0
  mgmt_be_send_apply_reply(client_ctx, txn->txn_id, batch_ids,
699
0
            num_processed, true, NULL);
700
701
0
  MGMTD_BE_CLIENT_DBG("Nb-apply-duration %lu (avg: %lu) uSec",
702
0
          apply_nb_cfg_tm, client_ctx->avg_apply_nb_cfg_tm);
703
704
0
  return 0;
705
0
}
706
707
static int mgmt_be_process_cfg_apply(struct mgmt_be_client *client_ctx,
708
             uint64_t txn_id)
709
0
{
710
0
  struct mgmt_be_txn_ctx *txn;
711
712
0
  txn = mgmt_be_find_txn_by_id(client_ctx, txn_id, true);
713
0
  if (!txn)
714
0
    goto failed;
715
716
0
  MGMTD_BE_CLIENT_DBG("Trigger CFG_APPLY_REQ processing");
717
0
  if (mgmt_be_txn_proc_cfgapply(txn))
718
0
    goto failed;
719
720
0
  return 0;
721
0
failed:
722
0
  msg_conn_disconnect(&client_ctx->client.conn, true);
723
0
  return -1;
724
0
}
725
726
727
static int mgmt_be_client_handle_msg(struct mgmt_be_client *client_ctx,
728
             Mgmtd__BeMessage *be_msg)
729
0
{
730
  /*
731
   * On error we may have closed the connection so don't do anything with
732
   * the client_ctx on return.
733
   *
734
   * protobuf-c adds a max size enum with an internal, and changing by
735
   * version, name; cast to an int to avoid unhandled enum warnings
736
   */
737
0
  switch ((int)be_msg->message_case) {
738
0
  case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REPLY:
739
0
    MGMTD_BE_CLIENT_DBG("Got SUBSCR_REPLY success %u",
740
0
            be_msg->subscr_reply->success);
741
0
    break;
742
0
  case MGMTD__BE_MESSAGE__MESSAGE_TXN_REQ:
743
0
    MGMTD_BE_CLIENT_DBG("Got TXN_REQ %s txn-id: %" PRIu64,
744
0
            be_msg->txn_req->create ? "Create"
745
0
                  : "Delete",
746
0
            be_msg->txn_req->txn_id);
747
0
    mgmt_be_process_txn_req(client_ctx,
748
0
              be_msg->txn_req->txn_id,
749
0
              be_msg->txn_req->create);
750
0
    break;
751
0
  case MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REQ:
752
0
    MGMTD_BE_CLIENT_DBG("Got CFG_DATA_REQ txn-id: %" PRIu64
753
0
            " batch-id: %" PRIu64 " end-of-data %u",
754
0
            be_msg->cfg_data_req->txn_id,
755
0
            be_msg->cfg_data_req->batch_id,
756
0
            be_msg->cfg_data_req->end_of_data);
757
0
    mgmt_be_process_cfgdata_req(
758
0
      client_ctx, be_msg->cfg_data_req->txn_id,
759
0
      be_msg->cfg_data_req->batch_id,
760
0
      be_msg->cfg_data_req->data_req,
761
0
      be_msg->cfg_data_req->n_data_req,
762
0
      be_msg->cfg_data_req->end_of_data);
763
0
    break;
764
0
  case MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REQ:
765
0
    MGMTD_BE_CLIENT_DBG("Got CFG_APPLY_REQ txn-id: %" PRIu64,
766
0
            be_msg->cfg_data_req->txn_id);
767
0
    mgmt_be_process_cfg_apply(
768
0
      client_ctx, (uint64_t)be_msg->cfg_apply_req->txn_id);
769
0
    break;
770
0
  case MGMTD__BE_MESSAGE__MESSAGE_GET_REQ:
771
0
    MGMTD_BE_CLIENT_ERR("Got unhandled message type %u",
772
0
            be_msg->message_case);
773
    /*
774
     * TODO: Add handling code in future.
775
     */
776
0
    break;
777
  /*
778
   * NOTE: The following messages are always sent from Backend
779
   * clients to MGMTd only and/or need not be handled here.
780
   */
781
0
  case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ:
782
0
  case MGMTD__BE_MESSAGE__MESSAGE_GET_REPLY:
783
0
  case MGMTD__BE_MESSAGE__MESSAGE_TXN_REPLY:
784
0
  case MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REPLY:
785
0
  case MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REPLY:
786
0
  case MGMTD__BE_MESSAGE__MESSAGE__NOT_SET:
787
0
  default:
788
    /*
789
     * A 'default' case is being added contrary to the
790
     * FRR code guidelines to take care of build
791
     * failures on certain build systems (courtesy of
792
     * the proto-c package).
793
     */
794
0
    break;
795
0
  }
796
797
0
  return 0;
798
0
}
799
800
static void mgmt_be_client_process_msg(uint8_t version, uint8_t *data,
801
               size_t len, struct msg_conn *conn)
802
0
{
803
0
  struct mgmt_be_client *client_ctx;
804
0
  struct msg_client *client;
805
0
  Mgmtd__BeMessage *be_msg;
806
807
0
  client = container_of(conn, struct msg_client, conn);
808
0
  client_ctx = container_of(client, struct mgmt_be_client, client);
809
810
0
  be_msg = mgmtd__be_message__unpack(NULL, len, data);
811
0
  if (!be_msg) {
812
0
    MGMTD_BE_CLIENT_DBG("Failed to decode %zu bytes from server",
813
0
            len);
814
0
    return;
815
0
  }
816
0
  MGMTD_BE_CLIENT_DBG(
817
0
    "Decoded %zu bytes of message(msg: %u/%u) from server", len,
818
0
    be_msg->message_case, be_msg->message_case);
819
0
  (void)mgmt_be_client_handle_msg(client_ctx, be_msg);
820
0
  mgmtd__be_message__free_unpacked(be_msg, NULL);
821
0
}
822
823
int mgmt_be_send_subscr_req(struct mgmt_be_client *client_ctx,
824
          bool subscr_xpaths, int num_xpaths,
825
          char **reg_xpaths)
826
0
{
827
0
  Mgmtd__BeMessage be_msg;
828
0
  Mgmtd__BeSubscribeReq subscr_req;
829
830
0
  mgmtd__be_subscribe_req__init(&subscr_req);
831
0
  subscr_req.client_name = client_ctx->name;
832
0
  subscr_req.n_xpath_reg = num_xpaths;
833
0
  if (num_xpaths)
834
0
    subscr_req.xpath_reg = reg_xpaths;
835
0
  else
836
0
    subscr_req.xpath_reg = NULL;
837
0
  subscr_req.subscribe_xpaths = subscr_xpaths;
838
839
0
  mgmtd__be_message__init(&be_msg);
840
0
  be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ;
841
0
  be_msg.subscr_req = &subscr_req;
842
843
0
  MGMTD_FE_CLIENT_DBG(
844
0
    "Sending SUBSCR_REQ name: %s subscr_xpaths: %u num_xpaths: %zu",
845
0
    subscr_req.client_name, subscr_req.subscribe_xpaths,
846
0
    subscr_req.n_xpath_reg);
847
848
0
  return mgmt_be_client_send_msg(client_ctx, &be_msg);
849
0
}
850
851
static int _notify_conenct_disconnect(struct msg_client *msg_client,
852
              bool connected)
853
0
{
854
0
  struct mgmt_be_client *client =
855
0
    container_of(msg_client, struct mgmt_be_client, client);
856
0
  int ret;
857
858
0
  if (connected) {
859
0
    assert(msg_client->conn.fd != -1);
860
0
    ret = mgmt_be_send_subscr_req(client, false, 0, NULL);
861
0
    if (ret)
862
0
      return ret;
863
0
  }
864
865
  /* Notify BE client through registered callback (if any) */
866
0
  if (client->cbs.client_connect_notify)
867
0
    (void)(*client->cbs.client_connect_notify)(
868
0
      client, client->user_data, connected);
869
870
  /* Cleanup any in-progress TXN on disconnect */
871
0
  if (!connected)
872
0
    mgmt_be_cleanup_all_txns(client);
873
874
0
  return 0;
875
0
}
876
877
static int mgmt_be_client_notify_conenct(struct msg_client *client)
878
0
{
879
0
  return _notify_conenct_disconnect(client, true);
880
0
}
881
882
static int mgmt_be_client_notify_disconenct(struct msg_conn *conn)
883
{
884
  struct msg_client *client = container_of(conn, struct msg_client, conn);
885
886
  return _notify_conenct_disconnect(client, false);
887
}
888
889
/*
890
 * Debug Flags
891
 */
892
893
DEFPY(debug_mgmt_client_be, debug_mgmt_client_be_cmd,
894
      "[no] debug mgmt client backend",
895
      NO_STR DEBUG_STR MGMTD_STR
896
      "client\n"
897
      "backend\n")
898
0
{
899
0
  uint32_t mode = DEBUG_NODE2MODE(vty->node);
900
901
0
  DEBUG_MODE_SET(&mgmt_dbg_be_client, mode, !no);
902
903
0
  return CMD_SUCCESS;
904
0
}
905
906
static void mgmt_debug_client_be_set_all(uint32_t flags, bool set)
907
0
{
908
0
  DEBUG_FLAGS_SET(&mgmt_dbg_be_client, flags, set);
909
0
}
910
911
static int mgmt_debug_be_client_config_write(struct vty *vty)
912
0
{
913
0
  if (DEBUG_MODE_CHECK(&mgmt_dbg_be_client, DEBUG_MODE_CONF))
914
0
    vty_out(vty, "debug mgmt client frontend\n");
915
916
0
  return 1;
917
0
}
918
919
void mgmt_debug_be_client_show_debug(struct vty *vty)
920
0
{
921
0
  if (MGMTD_DBG_BE_CLIENT_CHECK())
922
0
    vty_out(vty, "debug mgmt client backend\n");
923
0
}
924
925
static struct debug_callbacks mgmt_dbg_be_client_cbs = {
926
  .debug_set_all = mgmt_debug_client_be_set_all};
927
928
static struct cmd_node mgmt_dbg_node = {
929
  .name = "mgmt backend client",
930
  .node = DEBUG_NODE,
931
  .prompt = "",
932
  .config_write = mgmt_debug_be_client_config_write,
933
};
934
935
struct mgmt_be_client *mgmt_be_client_create(const char *client_name,
936
               struct mgmt_be_client_cbs *cbs,
937
               uintptr_t user_data,
938
               struct event_loop *event_loop)
939
0
{
940
0
  struct mgmt_be_client *client =
941
0
    XCALLOC(MTYPE_MGMTD_BE_CLIENT, sizeof(*client));
942
943
  /* Only call after frr_init() */
944
0
  assert(running_config);
945
946
0
  client->name = XSTRDUP(MTYPE_MGMTD_BE_CLIENT_NAME, client_name);
947
0
  client->running_config = running_config;
948
0
  client->candidate_config = nb_config_new(NULL);
949
0
  if (cbs)
950
0
    client->cbs = *cbs;
951
0
  mgmt_be_txns_init(&client->txn_head);
952
0
  msg_client_init(&client->client, event_loop, MGMTD_BE_SERVER_PATH,
953
0
      mgmt_be_client_notify_conenct,
954
0
      mgmt_be_client_notify_disconenct,
955
0
      mgmt_be_client_process_msg, MGMTD_BE_MAX_NUM_MSG_PROC,
956
0
      MGMTD_BE_MAX_NUM_MSG_WRITE, MGMTD_BE_MSG_MAX_LEN, false,
957
0
      "BE-client", MGMTD_DBG_BE_CLIENT_CHECK());
958
959
0
  MGMTD_BE_CLIENT_DBG("Initialized client '%s'", client_name);
960
961
0
  return client;
962
0
}
963
964
965
void mgmt_be_client_lib_vty_init(void)
966
0
{
967
0
  debug_init(&mgmt_dbg_be_client_cbs);
968
0
  install_node(&mgmt_dbg_node);
969
0
  install_element(ENABLE_NODE, &debug_mgmt_client_be_cmd);
970
0
  install_element(CONFIG_NODE, &debug_mgmt_client_be_cmd);
971
0
}
972
973
void mgmt_be_client_destroy(struct mgmt_be_client *client)
974
0
{
975
0
  MGMTD_BE_CLIENT_DBG("Destroying MGMTD Backend Client '%s'",
976
0
          client->name);
977
978
0
  msg_client_cleanup(&client->client);
979
0
  mgmt_be_cleanup_all_txns(client);
980
0
  mgmt_be_txns_fini(&client->txn_head);
981
0
  nb_config_free(client->candidate_config);
982
983
0
  XFREE(MTYPE_MGMTD_BE_CLIENT_NAME, client->name);
984
  XFREE(MTYPE_MGMTD_BE_CLIENT, client);
985
0
}