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