1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * MGMTD Backend Client Library api interfaces
4 * Copyright (C) 2021 Vmware, Inc.
5 * Pushpasis Sarkar <spushpasis@vmware.com>
12 #include "mgmtd/mgmt.h"
13 #include "mgmt_be_client.h"
17 #include "northbound.h"
21 #include "lib/mgmt_be_client_clippy.c"
23 #define MGMTD_BE_CLIENT_DBG(fmt, ...) \
24 DEBUGD(&mgmt_dbg_be_client, "BE-CLIENT: %s:" fmt, __func__, \
26 #define MGMTD_BE_CLIENT_ERR(fmt, ...) \
27 zlog_err("BE-CLIENT: %s: ERROR: " fmt, __func__, ##__VA_ARGS__)
28 #define MGMTD_DBG_BE_CLIENT_CHECK() \
29 DEBUG_MODE_CHECK(&mgmt_dbg_be_client, DEBUG_MODE_ALL)
31 DEFINE_MTYPE_STATIC(LIB
, MGMTD_BE_CLIENT
, "backend client");
32 DEFINE_MTYPE_STATIC(LIB
, MGMTD_BE_CLIENT_NAME
, "backend client name");
33 DEFINE_MTYPE_STATIC(LIB
, MGMTD_BE_BATCH
, "backend transaction batch data");
34 DEFINE_MTYPE_STATIC(LIB
, MGMTD_BE_TXN
, "backend transaction data");
36 enum mgmt_be_txn_event
{
37 MGMTD_BE_TXN_PROC_SETCFG
= 1,
38 MGMTD_BE_TXN_PROC_GETCFG
,
39 MGMTD_BE_TXN_PROC_GETDATA
42 struct mgmt_be_set_cfg_req
{
43 struct nb_cfg_change cfg_changes
[MGMTD_MAX_CFG_CHANGES_IN_BATCH
];
44 uint16_t num_cfg_changes
;
47 struct mgmt_be_get_data_req
{
48 char *xpaths
[MGMTD_MAX_NUM_DATA_REQ_IN_BATCH
];
52 struct mgmt_be_txn_req
{
53 enum mgmt_be_txn_event event
;
55 struct mgmt_be_set_cfg_req set_cfg
;
56 struct mgmt_be_get_data_req get_data
;
60 PREDECL_LIST(mgmt_be_batches
);
61 struct mgmt_be_batch_ctx
{
62 /* Batch-Id as assigned by MGMTD */
65 struct mgmt_be_txn_req txn_req
;
69 struct mgmt_be_batches_item list_linkage
;
71 #define MGMTD_BE_BATCH_FLAGS_CFG_PREPARED (1U << 0)
72 #define MGMTD_BE_TXN_FLAGS_CFG_APPLIED (1U << 1)
73 DECLARE_LIST(mgmt_be_batches
, struct mgmt_be_batch_ctx
, list_linkage
);
75 PREDECL_LIST(mgmt_be_txns
);
76 struct mgmt_be_txn_ctx
{
77 /* Txn-Id as assigned by MGMTD */
81 struct mgmt_be_client_txn_ctx client_data
;
82 struct mgmt_be_client
*client
;
84 /* List of batches belonging to this transaction */
85 struct mgmt_be_batches_head cfg_batches
;
86 struct mgmt_be_batches_head apply_cfgs
;
88 struct mgmt_be_txns_item list_linkage
;
90 struct nb_transaction
*nb_txn
;
93 #define MGMTD_BE_TXN_FLAGS_CFGPREP_FAILED (1U << 1)
95 DECLARE_LIST(mgmt_be_txns
, struct mgmt_be_txn_ctx
, list_linkage
);
97 #define FOREACH_BE_TXN_BATCH_IN_LIST(txn, batch) \
98 frr_each_safe (mgmt_be_batches, &(txn)->cfg_batches, (batch))
100 #define FOREACH_BE_APPLY_BATCH_IN_LIST(txn, batch) \
101 frr_each_safe (mgmt_be_batches, &(txn)->apply_cfgs, (batch))
103 struct mgmt_be_client
{
104 struct msg_client client
;
108 struct nb_config
*candidate_config
;
109 struct nb_config
*running_config
;
111 unsigned long num_edit_nb_cfg
;
112 unsigned long avg_edit_nb_cfg_tm
;
113 unsigned long num_prep_nb_cfg
;
114 unsigned long avg_prep_nb_cfg_tm
;
115 unsigned long num_apply_nb_cfg
;
116 unsigned long avg_apply_nb_cfg_tm
;
118 struct mgmt_be_txns_head txn_head
;
120 struct mgmt_be_client_cbs cbs
;
124 #define FOREACH_BE_TXN_IN_LIST(client_ctx, txn) \
125 frr_each_safe (mgmt_be_txns, &(client_ctx)->txn_head, (txn))
127 struct debug mgmt_dbg_be_client
= {0, "Management backend client operations"};
129 const char *mgmt_be_client_names
[MGMTD_BE_CLIENT_ID_MAX
+ 1] = {
131 [MGMTD_BE_CLIENT_ID_STATICD
] = "staticd",
133 [MGMTD_BE_CLIENT_ID_MAX
] = "Unknown/Invalid",
136 static int mgmt_be_client_send_msg(struct mgmt_be_client
*client_ctx
,
137 Mgmtd__BeMessage
*be_msg
)
139 return msg_conn_send_msg(
140 &client_ctx
->client
.conn
, MGMT_MSG_VERSION_PROTOBUF
, be_msg
,
141 mgmtd__be_message__get_packed_size(be_msg
),
142 (size_t(*)(void *, void *))mgmtd__be_message__pack
, false);
145 static struct mgmt_be_batch_ctx
*
146 mgmt_be_find_batch_by_id(struct mgmt_be_txn_ctx
*txn
,
149 struct mgmt_be_batch_ctx
*batch
= NULL
;
151 FOREACH_BE_TXN_BATCH_IN_LIST (txn
, batch
) {
152 if (batch
->batch_id
== batch_id
)
159 static struct mgmt_be_batch_ctx
*
160 mgmt_be_batch_create(struct mgmt_be_txn_ctx
*txn
, uint64_t batch_id
)
162 struct mgmt_be_batch_ctx
*batch
= NULL
;
164 batch
= mgmt_be_find_batch_by_id(txn
, batch_id
);
166 batch
= XCALLOC(MTYPE_MGMTD_BE_BATCH
,
167 sizeof(struct mgmt_be_batch_ctx
));
170 batch
->batch_id
= batch_id
;
171 mgmt_be_batches_add_tail(&txn
->cfg_batches
, batch
);
173 MGMTD_BE_CLIENT_DBG("Added new batch-id: %" PRIu64
181 static void mgmt_be_batch_delete(struct mgmt_be_txn_ctx
*txn
,
182 struct mgmt_be_batch_ctx
**batch
)
189 mgmt_be_batches_del(&txn
->cfg_batches
, *batch
);
190 if ((*batch
)->txn_req
.event
== MGMTD_BE_TXN_PROC_SETCFG
) {
191 for (indx
= 0; indx
< MGMTD_MAX_CFG_CHANGES_IN_BATCH
; indx
++) {
192 if ((*batch
)->txn_req
.req
.set_cfg
.cfg_changes
[indx
]
194 free((char *)(*batch
)
195 ->txn_req
.req
.set_cfg
202 XFREE(MTYPE_MGMTD_BE_BATCH
, *batch
);
206 static void mgmt_be_cleanup_all_batches(struct mgmt_be_txn_ctx
*txn
)
208 struct mgmt_be_batch_ctx
*batch
= NULL
;
210 FOREACH_BE_TXN_BATCH_IN_LIST (txn
, batch
) {
211 mgmt_be_batch_delete(txn
, &batch
);
214 FOREACH_BE_APPLY_BATCH_IN_LIST (txn
, batch
) {
215 mgmt_be_batch_delete(txn
, &batch
);
219 static struct mgmt_be_txn_ctx
*
220 mgmt_be_find_txn_by_id(struct mgmt_be_client
*client_ctx
, uint64_t txn_id
)
222 struct mgmt_be_txn_ctx
*txn
= NULL
;
224 FOREACH_BE_TXN_IN_LIST (client_ctx
, txn
) {
225 if (txn
->txn_id
== txn_id
)
232 static struct mgmt_be_txn_ctx
*
233 mgmt_be_txn_create(struct mgmt_be_client
*client_ctx
, uint64_t txn_id
)
235 struct mgmt_be_txn_ctx
*txn
= NULL
;
237 txn
= mgmt_be_find_txn_by_id(client_ctx
, txn_id
);
239 txn
= XCALLOC(MTYPE_MGMTD_BE_TXN
,
240 sizeof(struct mgmt_be_txn_ctx
));
243 txn
->txn_id
= txn_id
;
244 txn
->client
= client_ctx
;
245 mgmt_be_batches_init(&txn
->cfg_batches
);
246 mgmt_be_batches_init(&txn
->apply_cfgs
);
247 mgmt_be_txns_add_tail(&client_ctx
->txn_head
, txn
);
249 MGMTD_BE_CLIENT_DBG("Added new txn-id: %" PRIu64
, txn_id
);
255 static void mgmt_be_txn_delete(struct mgmt_be_client
*client_ctx
,
256 struct mgmt_be_txn_ctx
**txn
)
258 char err_msg
[] = "MGMT Transaction Delete";
264 * Remove the transaction from the list of transactions
265 * so that future lookups with the same transaction id
266 * does not return this one.
268 mgmt_be_txns_del(&client_ctx
->txn_head
, *txn
);
271 * Time to delete the transaction which should also
272 * take care of cleaning up all batches created via
273 * CFGDATA_CREATE_REQs. But first notify the client
274 * about the transaction delete.
276 if (client_ctx
->cbs
.txn_notify
)
277 (void)(*client_ctx
->cbs
.txn_notify
)(client_ctx
,
278 client_ctx
->user_data
,
279 &(*txn
)->client_data
, true);
281 mgmt_be_cleanup_all_batches(*txn
);
283 nb_candidate_commit_abort((*txn
)->nb_txn
, err_msg
,
285 XFREE(MTYPE_MGMTD_BE_TXN
, *txn
);
290 static void mgmt_be_cleanup_all_txns(struct mgmt_be_client
*client_ctx
)
292 struct mgmt_be_txn_ctx
*txn
= NULL
;
294 FOREACH_BE_TXN_IN_LIST (client_ctx
, txn
) {
295 mgmt_be_txn_delete(client_ctx
, &txn
);
299 static int mgmt_be_send_txn_reply(struct mgmt_be_client
*client_ctx
,
300 uint64_t txn_id
, bool create
, bool success
)
302 Mgmtd__BeMessage be_msg
;
303 Mgmtd__BeTxnReply txn_reply
;
305 mgmtd__be_txn_reply__init(&txn_reply
);
306 txn_reply
.create
= create
;
307 txn_reply
.txn_id
= txn_id
;
308 txn_reply
.success
= success
;
310 mgmtd__be_message__init(&be_msg
);
311 be_msg
.message_case
= MGMTD__BE_MESSAGE__MESSAGE_TXN_REPLY
;
312 be_msg
.txn_reply
= &txn_reply
;
314 MGMTD_BE_CLIENT_DBG("Sending TXN_REPLY txn-id %" PRIu64
, txn_id
);
316 return mgmt_be_client_send_msg(client_ctx
, &be_msg
);
319 static int mgmt_be_process_txn_req(struct mgmt_be_client
*client_ctx
,
320 uint64_t txn_id
, bool create
)
322 struct mgmt_be_txn_ctx
*txn
;
324 txn
= mgmt_be_find_txn_by_id(client_ctx
, txn_id
);
328 * Transaction with same txn-id already exists.
329 * Should not happen under any circumstances.
332 "txn-id: %" PRIu64
" already exists", txn_id
);
333 mgmt_be_send_txn_reply(client_ctx
, txn_id
, create
,
337 MGMTD_BE_CLIENT_DBG("Created new txn-id %" PRIu64
, txn_id
);
338 txn
= mgmt_be_txn_create(client_ctx
, txn_id
);
340 if (client_ctx
->cbs
.txn_notify
)
341 (void)(*client_ctx
->cbs
.txn_notify
)(
342 client_ctx
, client_ctx
->user_data
,
343 &txn
->client_data
, false);
347 * Transaction with same txn-id does not exists.
348 * Return sucess anyways.
350 MGMTD_BE_CLIENT_DBG("txn-id: %" PRIu64
351 " for delete does NOT exists",
354 MGMTD_BE_CLIENT_DBG("Delete txn-id: %" PRIu64
, txn_id
);
355 mgmt_be_txn_delete(client_ctx
, &txn
);
359 mgmt_be_send_txn_reply(client_ctx
, txn_id
, create
, true);
364 static int mgmt_be_send_cfgdata_create_reply(struct mgmt_be_client
*client_ctx
,
365 uint64_t txn_id
, uint64_t batch_id
,
367 const char *error_if_any
)
369 Mgmtd__BeMessage be_msg
;
370 Mgmtd__BeCfgDataCreateReply cfgdata_reply
;
372 mgmtd__be_cfg_data_create_reply__init(&cfgdata_reply
);
373 cfgdata_reply
.txn_id
= (uint64_t)txn_id
;
374 cfgdata_reply
.batch_id
= (uint64_t)batch_id
;
375 cfgdata_reply
.success
= success
;
377 cfgdata_reply
.error_if_any
= (char *)error_if_any
;
379 mgmtd__be_message__init(&be_msg
);
380 be_msg
.message_case
= MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REPLY
;
381 be_msg
.cfg_data_reply
= &cfgdata_reply
;
383 MGMTD_BE_CLIENT_DBG("Sending CFGDATA_CREATE_REPLY txn-id: %" PRIu64
384 " batch-id: %" PRIu64
,
387 return mgmt_be_client_send_msg(client_ctx
, &be_msg
);
390 static void mgmt_be_txn_cfg_abort(struct mgmt_be_txn_ctx
*txn
)
392 char errmsg
[BUFSIZ
] = {0};
394 assert(txn
&& txn
->client
);
397 "Aborting configs after prep for txn-id: %" PRIu64
,
399 nb_candidate_commit_abort(txn
->nb_txn
, errmsg
, sizeof(errmsg
));
404 * revert candidate back to running
406 * This is one txn ctx but the candidate_config is per client ctx, how
410 "Reset candidate configurations after abort of txn-id: %" PRIu64
,
412 nb_config_replace(txn
->client
->candidate_config
,
413 txn
->client
->running_config
, true);
416 static int mgmt_be_txn_cfg_prepare(struct mgmt_be_txn_ctx
*txn
)
418 struct mgmt_be_client
*client_ctx
;
419 struct mgmt_be_txn_req
*txn_req
= NULL
;
420 struct nb_context nb_ctx
= {0};
421 struct timeval edit_nb_cfg_start
;
422 struct timeval edit_nb_cfg_end
;
423 unsigned long edit_nb_cfg_tm
;
424 struct timeval prep_nb_cfg_start
;
425 struct timeval prep_nb_cfg_end
;
426 unsigned long prep_nb_cfg_tm
;
427 struct mgmt_be_batch_ctx
*batch
;
429 char err_buf
[BUFSIZ
];
430 size_t num_processed
;
433 assert(txn
&& txn
->client
);
434 client_ctx
= txn
->client
;
437 FOREACH_BE_TXN_BATCH_IN_LIST (txn
, batch
) {
438 txn_req
= &batch
->txn_req
;
440 nb_ctx
.client
= NB_CLIENT_CLI
;
441 nb_ctx
.user
= (void *)client_ctx
->user_data
;
445 * This happens when the current backend client is only
446 * interested in consuming the config items but is not
447 * interested in validating it.
451 gettimeofday(&edit_nb_cfg_start
, NULL
);
452 nb_candidate_edit_config_changes(
453 client_ctx
->candidate_config
,
454 txn_req
->req
.set_cfg
.cfg_changes
,
455 (size_t)txn_req
->req
.set_cfg
.num_cfg_changes
,
456 NULL
, NULL
, 0, err_buf
, sizeof(err_buf
),
459 err_buf
[sizeof(err_buf
) - 1] = 0;
461 "Failed to update configs for txn-id: %" PRIu64
462 " batch-id: %" PRIu64
463 " to candidate, err: '%s'",
464 txn
->txn_id
, batch
->batch_id
, err_buf
);
467 gettimeofday(&edit_nb_cfg_end
, NULL
);
468 edit_nb_cfg_tm
= timeval_elapsed(edit_nb_cfg_end
,
470 client_ctx
->avg_edit_nb_cfg_tm
=
471 ((client_ctx
->avg_edit_nb_cfg_tm
*
472 client_ctx
->num_edit_nb_cfg
) +
474 (client_ctx
->num_edit_nb_cfg
+ 1);
475 client_ctx
->num_edit_nb_cfg
++;
485 * Now prepare all the batches we have applied in one go.
487 nb_ctx
.client
= NB_CLIENT_CLI
;
488 nb_ctx
.user
= (void *)client_ctx
->user_data
;
490 gettimeofday(&prep_nb_cfg_start
, NULL
);
491 err
= nb_candidate_commit_prepare(nb_ctx
, client_ctx
->candidate_config
,
492 "MGMTD Backend Txn", &txn
->nb_txn
,
493 #ifdef MGMTD_LOCAL_VALIDATIONS_ENABLED
498 err_buf
, sizeof(err_buf
) - 1);
500 err_buf
[sizeof(err_buf
) - 1] = 0;
501 if (err
== NB_ERR_VALIDATION
)
503 "Failed to validate configs txn-id: %" PRIu64
504 " %zu batches, err: '%s'",
505 txn
->txn_id
, num_processed
, err_buf
);
508 "Failed to prepare configs for txn-id: %" PRIu64
509 " %zu batches, err: '%s'",
510 txn
->txn_id
, num_processed
, err_buf
);
512 SET_FLAG(txn
->flags
, MGMTD_BE_TXN_FLAGS_CFGPREP_FAILED
);
514 MGMTD_BE_CLIENT_DBG("Prepared configs for txn-id: %" PRIu64
516 txn
->txn_id
, num_processed
);
518 gettimeofday(&prep_nb_cfg_end
, NULL
);
519 prep_nb_cfg_tm
= timeval_elapsed(prep_nb_cfg_end
, prep_nb_cfg_start
);
520 client_ctx
->avg_prep_nb_cfg_tm
= ((client_ctx
->avg_prep_nb_cfg_tm
*
521 client_ctx
->num_prep_nb_cfg
) +
523 (client_ctx
->num_prep_nb_cfg
+ 1);
524 client_ctx
->num_prep_nb_cfg
++;
526 FOREACH_BE_TXN_BATCH_IN_LIST (txn
, batch
) {
527 mgmt_be_send_cfgdata_create_reply(
528 client_ctx
, txn
->txn_id
, batch
->batch_id
,
529 error
? false : true, error
? err_buf
: NULL
);
531 SET_FLAG(batch
->flags
,
532 MGMTD_BE_BATCH_FLAGS_CFG_PREPARED
);
533 mgmt_be_batches_del(&txn
->cfg_batches
, batch
);
534 mgmt_be_batches_add_tail(&txn
->apply_cfgs
, batch
);
539 "Avg-nb-edit-duration %lu uSec, nb-prep-duration %lu (avg: %lu) uSec, batch size %u",
540 client_ctx
->avg_edit_nb_cfg_tm
, prep_nb_cfg_tm
,
541 client_ctx
->avg_prep_nb_cfg_tm
, (uint32_t)num_processed
);
544 mgmt_be_txn_cfg_abort(txn
);
550 * Process all CFG_DATA_REQs received so far and prepare them all in one go.
552 static int mgmt_be_update_setcfg_in_batch(struct mgmt_be_client
*client_ctx
,
553 struct mgmt_be_txn_ctx
*txn
,
555 Mgmtd__YangCfgDataReq
*cfg_req
[],
558 struct mgmt_be_batch_ctx
*batch
= NULL
;
559 struct mgmt_be_txn_req
*txn_req
= NULL
;
561 struct nb_cfg_change
*cfg_chg
;
563 batch
= mgmt_be_batch_create(txn
, batch_id
);
565 MGMTD_BE_CLIENT_ERR("Batch create failed!");
569 txn_req
= &batch
->txn_req
;
570 txn_req
->event
= MGMTD_BE_TXN_PROC_SETCFG
;
571 MGMTD_BE_CLIENT_DBG("Created SETCFG request for batch-id: %" PRIu64
572 " txn-id: %" PRIu64
" cfg-items:%d",
573 batch_id
, txn
->txn_id
, num_req
);
575 txn_req
->req
.set_cfg
.num_cfg_changes
= num_req
;
576 for (index
= 0; index
< num_req
; index
++) {
577 cfg_chg
= &txn_req
->req
.set_cfg
.cfg_changes
[index
];
579 if (cfg_req
[index
]->req_type
580 == MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA
)
581 cfg_chg
->operation
= NB_OP_DESTROY
;
583 cfg_chg
->operation
= NB_OP_CREATE
;
585 strlcpy(cfg_chg
->xpath
, cfg_req
[index
]->data
->xpath
,
586 sizeof(cfg_chg
->xpath
));
587 cfg_chg
->value
= (cfg_req
[index
]->data
->value
591 ? strdup(cfg_req
[index
]
596 && !strncmp(cfg_chg
->value
, MGMTD_BE_CONTAINER_NODE_VAL
,
597 strlen(MGMTD_BE_CONTAINER_NODE_VAL
))) {
598 free((char *)cfg_chg
->value
);
599 cfg_chg
->value
= NULL
;
606 static int mgmt_be_process_cfgdata_req(struct mgmt_be_client
*client_ctx
,
607 uint64_t txn_id
, uint64_t batch_id
,
608 Mgmtd__YangCfgDataReq
*cfg_req
[],
609 int num_req
, bool end_of_data
)
611 struct mgmt_be_txn_ctx
*txn
;
613 txn
= mgmt_be_find_txn_by_id(client_ctx
, txn_id
);
615 MGMTD_BE_CLIENT_ERR("Invalid txn-id: %" PRIu64
616 " from MGMTD server",
618 mgmt_be_send_cfgdata_create_reply(
619 client_ctx
, txn_id
, batch_id
, false,
620 "Transaction context not created yet");
622 mgmt_be_update_setcfg_in_batch(client_ctx
, txn
, batch_id
,
626 if (txn
&& end_of_data
) {
627 MGMTD_BE_CLIENT_DBG("Triggering CFG_PREPARE_REQ processing");
628 mgmt_be_txn_cfg_prepare(txn
);
634 static int mgmt_be_send_apply_reply(struct mgmt_be_client
*client_ctx
,
635 uint64_t txn_id
, uint64_t batch_ids
[],
636 size_t num_batch_ids
, bool success
,
637 const char *error_if_any
)
639 Mgmtd__BeMessage be_msg
;
640 Mgmtd__BeCfgDataApplyReply apply_reply
;
642 mgmtd__be_cfg_data_apply_reply__init(&apply_reply
);
643 apply_reply
.success
= success
;
644 apply_reply
.txn_id
= txn_id
;
645 apply_reply
.batch_ids
= (uint64_t *)batch_ids
;
646 apply_reply
.n_batch_ids
= num_batch_ids
;
649 apply_reply
.error_if_any
= (char *)error_if_any
;
651 mgmtd__be_message__init(&be_msg
);
652 be_msg
.message_case
= MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REPLY
;
653 be_msg
.cfg_apply_reply
= &apply_reply
;
656 "Sending CFG_APPLY_REPLY txn-id %" PRIu64
657 " %zu batch ids %" PRIu64
" - %" PRIu64
,
658 txn_id
, num_batch_ids
,
659 success
&& num_batch_ids
? batch_ids
[0] : 0,
660 success
&& num_batch_ids
? batch_ids
[num_batch_ids
- 1] : 0);
662 return mgmt_be_client_send_msg(client_ctx
, &be_msg
);
665 static int mgmt_be_txn_proc_cfgapply(struct mgmt_be_txn_ctx
*txn
)
667 struct mgmt_be_client
*client_ctx
;
668 struct timeval apply_nb_cfg_start
;
669 struct timeval apply_nb_cfg_end
;
670 unsigned long apply_nb_cfg_tm
;
671 struct mgmt_be_batch_ctx
*batch
;
672 char err_buf
[BUFSIZ
];
673 size_t num_processed
;
674 static uint64_t batch_ids
[MGMTD_BE_MAX_BATCH_IDS_IN_REQ
];
676 assert(txn
&& txn
->client
);
677 client_ctx
= txn
->client
;
683 * Now apply all the batches we have applied in one go.
685 gettimeofday(&apply_nb_cfg_start
, NULL
);
686 (void)nb_candidate_commit_apply(txn
->nb_txn
, true, &txn
->nb_txn_id
,
687 err_buf
, sizeof(err_buf
) - 1);
688 gettimeofday(&apply_nb_cfg_end
, NULL
);
690 apply_nb_cfg_tm
= timeval_elapsed(apply_nb_cfg_end
, apply_nb_cfg_start
);
691 client_ctx
->avg_apply_nb_cfg_tm
= ((client_ctx
->avg_apply_nb_cfg_tm
*
692 client_ctx
->num_apply_nb_cfg
) +
694 (client_ctx
->num_apply_nb_cfg
+ 1);
695 client_ctx
->num_apply_nb_cfg
++;
699 * Send back CFG_APPLY_REPLY for all batches applied.
701 FOREACH_BE_APPLY_BATCH_IN_LIST (txn
, batch
) {
703 * No need to delete the batch yet. Will be deleted during
704 * transaction cleanup on receiving TXN_DELETE_REQ.
706 SET_FLAG(batch
->flags
, MGMTD_BE_TXN_FLAGS_CFG_APPLIED
);
707 mgmt_be_batches_del(&txn
->apply_cfgs
, batch
);
708 mgmt_be_batches_add_tail(&txn
->cfg_batches
, batch
);
710 batch_ids
[num_processed
] = batch
->batch_id
;
712 if (num_processed
== MGMTD_BE_MAX_BATCH_IDS_IN_REQ
) {
713 mgmt_be_send_apply_reply(client_ctx
, txn
->txn_id
,
714 batch_ids
, num_processed
,
720 mgmt_be_send_apply_reply(client_ctx
, txn
->txn_id
, batch_ids
,
721 num_processed
, true, NULL
);
723 MGMTD_BE_CLIENT_DBG("Nb-apply-duration %lu (avg: %lu) uSec",
724 apply_nb_cfg_tm
, client_ctx
->avg_apply_nb_cfg_tm
);
729 static int mgmt_be_process_cfg_apply(struct mgmt_be_client
*client_ctx
,
732 struct mgmt_be_txn_ctx
*txn
;
734 txn
= mgmt_be_find_txn_by_id(client_ctx
, txn_id
);
736 mgmt_be_send_apply_reply(client_ctx
, txn_id
, NULL
, 0, false,
737 "Transaction not created yet!");
741 MGMTD_BE_CLIENT_DBG("Trigger CFG_APPLY_REQ processing");
742 mgmt_be_txn_proc_cfgapply(txn
);
747 static int mgmt_be_client_handle_msg(struct mgmt_be_client
*client_ctx
,
748 Mgmtd__BeMessage
*be_msg
)
751 * protobuf-c adds a max size enum with an internal, and changing by
752 * version, name; cast to an int to avoid unhandled enum warnings
754 switch ((int)be_msg
->message_case
) {
755 case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REPLY
:
756 MGMTD_BE_CLIENT_DBG("Got SUBSCR_REPLY success %u",
757 be_msg
->subscr_reply
->success
);
759 case MGMTD__BE_MESSAGE__MESSAGE_TXN_REQ
:
760 MGMTD_BE_CLIENT_DBG("Got TXN_REQ %s txn-id: %" PRIu64
,
761 be_msg
->txn_req
->create
? "Create"
763 be_msg
->txn_req
->txn_id
);
764 mgmt_be_process_txn_req(client_ctx
,
765 be_msg
->txn_req
->txn_id
,
766 be_msg
->txn_req
->create
);
768 case MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REQ
:
769 MGMTD_BE_CLIENT_DBG("Got CFG_DATA_REQ txn-id: %" PRIu64
770 " batch-id: %" PRIu64
" end-of-data %u",
771 be_msg
->cfg_data_req
->txn_id
,
772 be_msg
->cfg_data_req
->batch_id
,
773 be_msg
->cfg_data_req
->end_of_data
);
774 mgmt_be_process_cfgdata_req(
775 client_ctx
, be_msg
->cfg_data_req
->txn_id
,
776 be_msg
->cfg_data_req
->batch_id
,
777 be_msg
->cfg_data_req
->data_req
,
778 be_msg
->cfg_data_req
->n_data_req
,
779 be_msg
->cfg_data_req
->end_of_data
);
781 case MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REQ
:
782 MGMTD_BE_CLIENT_DBG("Got CFG_APPLY_REQ txn-id: %" PRIu64
,
783 be_msg
->cfg_data_req
->txn_id
);
784 mgmt_be_process_cfg_apply(
785 client_ctx
, (uint64_t)be_msg
->cfg_apply_req
->txn_id
);
787 case MGMTD__BE_MESSAGE__MESSAGE_GET_REQ
:
788 case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ
:
789 case MGMTD__BE_MESSAGE__MESSAGE_CFG_CMD_REQ
:
790 case MGMTD__BE_MESSAGE__MESSAGE_SHOW_CMD_REQ
:
791 MGMTD_BE_CLIENT_ERR("Got unhandled message type %u",
792 be_msg
->message_case
);
794 * TODO: Add handling code in future.
798 * NOTE: The following messages are always sent from Backend
799 * clients to MGMTd only and/or need not be handled here.
801 case MGMTD__BE_MESSAGE__MESSAGE_GET_REPLY
:
802 case MGMTD__BE_MESSAGE__MESSAGE_TXN_REPLY
:
803 case MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REPLY
:
804 case MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REPLY
:
805 case MGMTD__BE_MESSAGE__MESSAGE_CFG_CMD_REPLY
:
806 case MGMTD__BE_MESSAGE__MESSAGE_SHOW_CMD_REPLY
:
807 case MGMTD__BE_MESSAGE__MESSAGE_NOTIFY_DATA
:
808 case MGMTD__BE_MESSAGE__MESSAGE__NOT_SET
:
811 * A 'default' case is being added contrary to the
812 * FRR code guidelines to take care of build
813 * failures on certain build systems (courtesy of
814 * the proto-c package).
822 static void mgmt_be_client_process_msg(uint8_t version
, uint8_t *data
,
823 size_t len
, struct msg_conn
*conn
)
825 struct mgmt_be_client
*client_ctx
;
826 struct msg_client
*client
;
827 Mgmtd__BeMessage
*be_msg
;
829 client
= container_of(conn
, struct msg_client
, conn
);
830 client_ctx
= container_of(client
, struct mgmt_be_client
, client
);
832 be_msg
= mgmtd__be_message__unpack(NULL
, len
, data
);
834 MGMTD_BE_CLIENT_DBG("Failed to decode %zu bytes from server",
839 "Decoded %zu bytes of message(msg: %u/%u) from server", len
,
840 be_msg
->message_case
, be_msg
->message_case
);
841 (void)mgmt_be_client_handle_msg(client_ctx
, be_msg
);
842 mgmtd__be_message__free_unpacked(be_msg
, NULL
);
845 int mgmt_be_send_subscr_req(struct mgmt_be_client
*client_ctx
,
846 bool subscr_xpaths
, int num_xpaths
,
849 Mgmtd__BeMessage be_msg
;
850 Mgmtd__BeSubscribeReq subscr_req
;
852 mgmtd__be_subscribe_req__init(&subscr_req
);
853 subscr_req
.client_name
= client_ctx
->name
;
854 subscr_req
.n_xpath_reg
= num_xpaths
;
856 subscr_req
.xpath_reg
= reg_xpaths
;
858 subscr_req
.xpath_reg
= NULL
;
859 subscr_req
.subscribe_xpaths
= subscr_xpaths
;
861 mgmtd__be_message__init(&be_msg
);
862 be_msg
.message_case
= MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ
;
863 be_msg
.subscr_req
= &subscr_req
;
866 "Sending SUBSCR_REQ name: %s subscr_xpaths: %u num_xpaths: %zu",
867 subscr_req
.client_name
, subscr_req
.subscribe_xpaths
,
868 subscr_req
.n_xpath_reg
);
870 return mgmt_be_client_send_msg(client_ctx
, &be_msg
);
873 static int _notify_conenct_disconnect(struct msg_client
*msg_client
,
876 struct mgmt_be_client
*client
=
877 container_of(msg_client
, struct mgmt_be_client
, client
);
881 assert(msg_client
->conn
.fd
!= -1);
882 ret
= mgmt_be_send_subscr_req(client
, false, 0, NULL
);
887 /* Notify BE client through registered callback (if any) */
888 if (client
->cbs
.client_connect_notify
)
889 (void)(*client
->cbs
.client_connect_notify
)(
890 client
, client
->user_data
, connected
);
894 static int mgmt_be_client_notify_conenct(struct msg_client
*client
)
896 return _notify_conenct_disconnect(client
, true);
899 static int mgmt_be_client_notify_disconenct(struct msg_conn
*conn
)
901 struct msg_client
*client
= container_of(conn
, struct msg_client
, conn
);
903 return _notify_conenct_disconnect(client
, false);
910 DEFPY(debug_mgmt_client_be
, debug_mgmt_client_be_cmd
,
911 "[no] debug mgmt client backend",
912 NO_STR DEBUG_STR MGMTD_STR
916 uint32_t mode
= DEBUG_NODE2MODE(vty
->node
);
918 DEBUG_MODE_SET(&mgmt_dbg_be_client
, mode
, !no
);
923 static void mgmt_debug_client_be_set_all(uint32_t flags
, bool set
)
925 DEBUG_FLAGS_SET(&mgmt_dbg_be_client
, flags
, set
);
928 static int mgmt_debug_be_client_config_write(struct vty
*vty
)
930 if (DEBUG_MODE_CHECK(&mgmt_dbg_be_client
, DEBUG_MODE_CONF
))
931 vty_out(vty
, "debug mgmt client frontend\n");
936 void mgmt_debug_be_client_show_debug(struct vty
*vty
)
938 if (MGMTD_DBG_BE_CLIENT_CHECK())
939 vty_out(vty
, "debug mgmt client backend\n");
942 static struct debug_callbacks mgmt_dbg_be_client_cbs
= {
943 .debug_set_all
= mgmt_debug_client_be_set_all
};
945 static struct cmd_node mgmt_dbg_node
= {
946 .name
= "mgmt backend client",
949 .config_write
= mgmt_debug_be_client_config_write
,
952 struct mgmt_be_client
*mgmt_be_client_create(const char *client_name
,
953 struct mgmt_be_client_cbs
*cbs
,
955 struct event_loop
*event_loop
)
957 struct mgmt_be_client
*client
=
958 XCALLOC(MTYPE_MGMTD_BE_CLIENT
, sizeof(*client
));
960 /* Only call after frr_init() */
961 assert(running_config
);
963 client
->name
= XSTRDUP(MTYPE_MGMTD_BE_CLIENT_NAME
, client_name
);
964 client
->running_config
= running_config
;
965 client
->candidate_config
= nb_config_new(NULL
);
968 mgmt_be_txns_init(&client
->txn_head
);
969 msg_client_init(&client
->client
, event_loop
, MGMTD_BE_SERVER_PATH
,
970 mgmt_be_client_notify_conenct
,
971 mgmt_be_client_notify_disconenct
,
972 mgmt_be_client_process_msg
, MGMTD_BE_MAX_NUM_MSG_PROC
,
973 MGMTD_BE_MAX_NUM_MSG_WRITE
, MGMTD_BE_MSG_MAX_LEN
, false,
974 "BE-client", MGMTD_DBG_BE_CLIENT_CHECK());
976 MGMTD_BE_CLIENT_DBG("Initialized client '%s'", client_name
);
982 void mgmt_be_client_lib_vty_init(void)
984 debug_init(&mgmt_dbg_be_client_cbs
);
985 install_node(&mgmt_dbg_node
);
986 install_element(ENABLE_NODE
, &debug_mgmt_client_be_cmd
);
987 install_element(CONFIG_NODE
, &debug_mgmt_client_be_cmd
);
990 void mgmt_be_client_destroy(struct mgmt_be_client
*client
)
992 MGMTD_BE_CLIENT_DBG("Destroying MGMTD Backend Client '%s'",
995 msg_client_cleanup(&client
->client
);
996 mgmt_be_cleanup_all_txns(client
);
997 mgmt_be_txns_fini(&client
->txn_head
);
998 nb_config_free(client
->candidate_config
);
1000 XFREE(MTYPE_MGMTD_BE_CLIENT_NAME
, client
->name
);
1001 XFREE(MTYPE_MGMTD_BE_CLIENT
, client
);