/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 | } |