mgmtd: Add MGMT Backend Interface Framework

This commit introduces the MGMT Backend Interface which can be used
by back-end management client daemons like BGPd, Staticd, Zebra to
connect with new FRR Management daemon (MGMTd) and utilize the new
FRR Management Framework to let any Frontend clients to retrieve any
operational data or manipulate any configuration data owned by the
individual Backend daemon component.

This commit includes the following functionalities in the changeset:
1. Add new Backend server for Backend daemons connect to.
2. Add a C-based Backend client library which can be used by daemons
   to communicate with MGMTd via the Backend interface.
3. Maintain a backend adapter for each connection from an appropriate
   Backend client to facilitate client requests and track one or more
   transactions initiated from Frontend client sessions that involves
   the backend client component.
4. Add the following commands to inspect various Backend client
   related information
	a. show mgmt backend-adapter all
	b. show mgmt backend-yang-xpath-registry
        c. show mgmt yang-xpath-subscription

Co-authored-by: Pushpasis Sarkar <pushpasis@gmail.com>
Co-authored-by: Abhinay Ramesh <rabhinay@vmware.com>
Co-authored-by: Ujwal P <ujwalp@vmware.com>
Signed-off-by: Yash Ranjan <ranjany@vmware.com>
This commit is contained in:
Christian Hopps 2023-02-28 10:00:19 -05:00
parent ef43a6329b
commit 7d65b7b7f4
22 changed files with 3941 additions and 124 deletions

View File

@ -52,6 +52,11 @@ ForEachMacros:
- FOR_ALL_INTERFACES
- FOR_ALL_INTERFACES_ADDRESSES
- JSON_FOREACH
- FOREACH_BE_TXN_BATCH_IN_LIST
- FOREACH_BE_APPLY_BATCH_IN_LIST
- FOREACH_BE_TXN_IN_LIST
- FOREACH_SESSION_IN_LIST
- FOREACH_MGMTD_BE_CLIENT_ID
# libyang
- LY_FOR_KEYS
- LY_LIST_FOR

View File

@ -106,18 +106,6 @@ message BeCfgDataCreateReply {
optional string error_if_any = 4;
}
message BeCfgDataValidateReq {
required uint64 txn_id = 1;
repeated uint64 batch_ids = 2;
}
message BeCfgDataValidateReply {
required uint64 txn_id = 1;
repeated uint64 batch_ids = 2;
required bool success = 3;
optional string error_if_any = 4;
}
message BeCfgDataApplyReq {
required uint64 txn_id = 1;
}
@ -181,17 +169,15 @@ message BeMessage {
BeTxnReply txn_reply = 5;
BeCfgDataCreateReq cfg_data_req = 6;
BeCfgDataCreateReply cfg_data_reply = 7;
BeCfgDataValidateReq cfg_validate_req = 8;
BeCfgDataValidateReply cfg_validate_reply = 9;
BeCfgDataApplyReq cfg_apply_req = 10;
BeCfgDataApplyReply cfg_apply_reply = 11;
BeOperDataGetReq get_req = 12;
BeOperDataGetReply get_reply = 13;
BeOperDataNotify notify_data = 14;
BeConfigCmdReq cfg_cmd_req = 15;
BeConfigCmdReply cfg_cmd_reply = 16;
BeShowCmdReq show_cmd_req = 17;
BeShowCmdReply show_cmd_reply = 18;
BeCfgDataApplyReq cfg_apply_req = 8;
BeCfgDataApplyReply cfg_apply_reply = 9;
BeOperDataGetReq get_req = 10;
BeOperDataGetReply get_reply = 11;
BeOperDataNotify notify_data = 12;
BeConfigCmdReq cfg_cmd_req = 13;
BeConfigCmdReply cfg_cmd_reply = 14;
BeShowCmdReq show_cmd_req = 15;
BeShowCmdReply show_cmd_reply = 16;
}
}

1490
lib/mgmt_be_client.c Normal file

File diff suppressed because it is too large Load Diff

273
lib/mgmt_be_client.h Normal file
View File

@ -0,0 +1,273 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* MGMTD Backend Client Library api interfaces
* Copyright (C) 2021 Vmware, Inc.
* Pushpasis Sarkar <spushpasis@vmware.com>
*/
#ifndef _FRR_MGMTD_BE_CLIENT_H_
#define _FRR_MGMTD_BE_CLIENT_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "mgmtd/mgmt_defines.h"
/***************************************************************
* Client IDs
***************************************************************/
/*
* Add enum value for each supported component, wrap with
* #ifdef HAVE_COMPONENT
*/
enum mgmt_be_client_id {
MGMTD_BE_CLIENT_ID_MIN = 0,
MGMTD_BE_CLIENT_ID_INIT = -1,
#if 0 /* example */
#ifdef HAVE_STATICD
MGMTD_BE_CLIENT_ID_STATICD,
#endif
#endif
MGMTD_BE_CLIENT_ID_MAX
};
#define FOREACH_MGMTD_BE_CLIENT_ID(id) \
for ((id) = MGMTD_BE_CLIENT_ID_MIN; \
(id) < MGMTD_BE_CLIENT_ID_MAX; (id)++)
/***************************************************************
* Constants
***************************************************************/
#define MGMTD_BE_CLIENT_ERROR_STRING_MAX_LEN 32
#define MGMTD_BE_DEFAULT_CONN_RETRY_INTVL_SEC 5
#define MGMTD_BE_MSG_PROC_DELAY_USEC 10
#define MGMTD_BE_MAX_NUM_MSG_PROC 500
#define MGMTD_BE_MSG_WRITE_DELAY_MSEC 1
#define MGMTD_BE_MAX_NUM_MSG_WRITE 1000
#define GMGD_BE_MAX_NUM_REQ_ITEMS 64
#define MGMTD_BE_MSG_MAX_LEN 16384
#define MGMTD_SOCKET_BE_SEND_BUF_SIZE 65535
#define MGMTD_SOCKET_BE_RECV_BUF_SIZE MGMTD_SOCKET_BE_SEND_BUF_SIZE
#define MGMTD_MAX_CFG_CHANGES_IN_BATCH \
((10 * MGMTD_BE_MSG_MAX_LEN) / \
(MGMTD_MAX_XPATH_LEN + MGMTD_MAX_YANG_VALUE_LEN))
/*
* MGMTD_BE_MSG_MAX_LEN must be used 80%
* since there is overhead of google protobuf
* that gets added to sent message
*/
#define MGMTD_BE_CFGDATA_PACKING_EFFICIENCY 0.8
#define MGMTD_BE_CFGDATA_MAX_MSG_LEN \
(MGMTD_BE_MSG_MAX_LEN * MGMTD_BE_CFGDATA_PACKING_EFFICIENCY)
#define MGMTD_BE_MAX_BATCH_IDS_IN_REQ \
(MGMTD_BE_MSG_MAX_LEN - 128) / sizeof(uint64_t)
#define MGMTD_BE_CONTAINER_NODE_VAL "<<container>>"
/***************************************************************
* Data-structures
***************************************************************/
#define MGMTD_BE_MAX_CLIENTS_PER_XPATH_REG 32
struct mgmt_be_msg_hdr {
uint16_t marker;
uint16_t len; /* Includes header */
};
#define MGMTD_BE_MSG_HDR_LEN sizeof(struct mgmt_be_msg_hdr)
#define MGMTD_BE_MSG_MARKER 0xfeed
struct mgmt_be_msg {
struct mgmt_be_msg_hdr hdr;
uint8_t payload[];
};
struct mgmt_be_client_txn_ctx {
uintptr_t *user_ctx;
};
/*
* All the client-specific information this library needs to
* initialize itself, setup connection with MGMTD BackEnd interface
* and carry on all required procedures appropriately.
*
* BackEnd clients need to initialise a instance of this structure
* with appropriate data and pass it while calling the API
* to initialize the library (See mgmt_be_client_lib_init for
* more details).
*/
struct mgmt_be_client_params {
char name[MGMTD_CLIENT_NAME_MAX_LEN];
uintptr_t user_data;
unsigned long conn_retry_intvl_sec;
void (*client_connect_notify)(uintptr_t lib_hndl,
uintptr_t usr_data,
bool connected);
void (*client_subscribe_notify)(
uintptr_t lib_hndl, uintptr_t usr_data,
struct nb_yang_xpath **xpath,
enum mgmt_result subscribe_result[], int num_paths);
void (*txn_notify)(
uintptr_t lib_hndl, uintptr_t usr_data,
struct mgmt_be_client_txn_ctx *txn_ctx, bool destroyed);
enum mgmt_result (*data_validate)(
uintptr_t lib_hndl, uintptr_t usr_data,
struct mgmt_be_client_txn_ctx *txn_ctx,
struct nb_yang_xpath *xpath, struct nb_yang_value *data,
bool delete, char *error_if_any);
enum mgmt_result (*data_apply)(
uintptr_t lib_hndl, uintptr_t usr_data,
struct mgmt_be_client_txn_ctx *txn_ctx,
struct nb_yang_xpath *xpath, struct nb_yang_value *data,
bool delete);
enum mgmt_result (*get_data_elem)(
uintptr_t lib_hndl, uintptr_t usr_data,
struct mgmt_be_client_txn_ctx *txn_ctx,
struct nb_yang_xpath *xpath, struct nb_yang_xpath_elem *elem);
enum mgmt_result (*get_data)(
uintptr_t lib_hndl, uintptr_t usr_data,
struct mgmt_be_client_txn_ctx *txn_ctx,
struct nb_yang_xpath *xpath, bool keys_only,
struct nb_yang_xpath_elem **elems, int *num_elems,
int *next_key);
enum mgmt_result (*get_next_data)(
uintptr_t lib_hndl, uintptr_t usr_data,
struct mgmt_be_client_txn_ctx *txn_ctx,
struct nb_yang_xpath *xpath, bool keys_only,
struct nb_yang_xpath_elem **elems, int *num_elems);
};
/***************************************************************
* Global data exported
***************************************************************/
extern const char *mgmt_be_client_names[MGMTD_BE_CLIENT_ID_MAX + 1];
static inline const char *mgmt_be_client_id2name(enum mgmt_be_client_id id)
{
if (id > MGMTD_BE_CLIENT_ID_MAX)
id = MGMTD_BE_CLIENT_ID_MAX;
return mgmt_be_client_names[id];
}
static inline enum mgmt_be_client_id
mgmt_be_client_name2id(const char *name)
{
enum mgmt_be_client_id id;
FOREACH_MGMTD_BE_CLIENT_ID (id) {
if (!strncmp(mgmt_be_client_names[id], name,
MGMTD_CLIENT_NAME_MAX_LEN))
return id;
}
return MGMTD_BE_CLIENT_ID_MAX;
}
/***************************************************************
* API prototypes
***************************************************************/
/*
* Initialize library and try connecting with MGMTD.
*
* params
* Backend client parameters.
*
* master_thread
* Thread master.
*
* Returns:
* Backend client lib handler (nothing but address of mgmt_be_client_ctx)
*/
extern uintptr_t
mgmt_be_client_lib_init(struct mgmt_be_client_params *params,
struct thread_master *master_thread);
/*
* Subscribe with MGMTD for one or more YANG subtree(s).
*
* lib_hndl
* Client library handler.
*
* reg_yang_xpaths
* Yang xpath(s) that needs to be subscribed to.
*
* num_xpaths
* Number of xpaths
*
* Returns:
* MGMTD_SUCCESS on success, MGMTD_* otherwise.
*/
extern enum mgmt_result mgmt_be_subscribe_yang_data(uintptr_t lib_hndl,
char **reg_yang_xpaths,
int num_xpaths);
/*
* Send one or more YANG notifications to MGMTD daemon.
*
* lib_hndl
* Client library handler.
*
* data_elems
* Yang data elements from data tree.
*
* num_elems
* Number of data elements.
*
* Returns:
* MGMTD_SUCCESS on success, MGMTD_* otherwise.
*/
extern enum mgmt_result
mgmt_be_send_yang_notify(uintptr_t lib_hndl, Mgmtd__YangData **data_elems,
int num_elems);
/*
* Un-subscribe with MGMTD for one or more YANG subtree(s).
*
* lib_hndl
* Client library handler.
*
* reg_yang_xpaths
* Yang xpath(s) that needs to be un-subscribed from.
*
* num_reg_xpaths
* Number of subscribed xpaths
*
* Returns:
* MGMTD_SUCCESS on success, MGMTD_* otherwise.
*/
enum mgmt_result mgmt_be_unsubscribe_yang_data(uintptr_t lib_hndl,
char **reg_yang_xpaths,
int num_reg_xpaths);
/*
* Destroy library and cleanup everything.
*/
extern void mgmt_be_client_lib_destroy(uintptr_t lib_hndl);
#ifdef __cplusplus
}
#endif
#endif /* _FRR_MGMTD_BE_CLIENT_H_ */

View File

@ -93,7 +93,9 @@ static int nb_node_new_cb(const struct lysc_node *snode, void *arg)
{
struct nb_node *nb_node;
struct lysc_node *sparent, *sparent_list;
struct frr_yang_module_info *module;
module = (struct frr_yang_module_info *)arg;
nb_node = XCALLOC(MTYPE_NB_NODE, sizeof(*nb_node));
yang_snode_get_path(snode, YANG_PATH_DATA, nb_node->xpath,
sizeof(nb_node->xpath));
@ -128,6 +130,9 @@ static int nb_node_new_cb(const struct lysc_node *snode, void *arg)
assert(snode->priv == NULL);
((struct lysc_node *)snode)->priv = nb_node;
if (module && module->ignore_cbs)
SET_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS);
return YANG_ITER_CONTINUE;
}
@ -230,6 +235,9 @@ static unsigned int nb_node_validate_cbs(const struct nb_node *nb_node)
{
unsigned int error = 0;
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
return error;
error += nb_node_validate_cb(nb_node, NB_OP_CREATE,
!!nb_node->cbs.create, false);
error += nb_node_validate_cb(nb_node, NB_OP_MODIFY,
@ -297,6 +305,8 @@ struct nb_config *nb_config_new(struct lyd_node *dnode)
config->dnode = yang_dnode_new(ly_native_ctx, true);
config->version = 0;
RB_INIT(nb_config_cbs, &config->cfg_chgs);
return config;
}
@ -304,6 +314,7 @@ void nb_config_free(struct nb_config *config)
{
if (config->dnode)
yang_dnode_free(config->dnode);
nb_config_diff_del_changes(&config->cfg_chgs);
XFREE(MTYPE_NB_CONFIG, config);
}
@ -315,6 +326,8 @@ struct nb_config *nb_config_dup(const struct nb_config *config)
dup->dnode = yang_dnode_dup(config->dnode);
dup->version = config->version;
RB_INIT(nb_config_cbs, &dup->cfg_chgs);
return dup;
}
@ -405,7 +418,7 @@ static void nb_config_diff_add_change(struct nb_config_cbs *changes,
RB_INSERT(nb_config_cbs, changes, &change->cb);
}
static void nb_config_diff_del_changes(struct nb_config_cbs *changes)
void nb_config_diff_del_changes(struct nb_config_cbs *changes)
{
while (!RB_EMPTY(nb_config_cbs, changes)) {
struct nb_config_change *change;
@ -422,8 +435,8 @@ static void nb_config_diff_del_changes(struct nb_config_cbs *changes)
* configurations. Given a new subtree, calculate all new YANG data nodes,
* excluding default leafs and leaf-lists. This is a recursive function.
*/
static void nb_config_diff_created(const struct lyd_node *dnode, uint32_t *seq,
struct nb_config_cbs *changes)
void nb_config_diff_created(const struct lyd_node *dnode, uint32_t *seq,
struct nb_config_cbs *changes)
{
enum nb_operation operation;
struct lyd_node *child;
@ -525,10 +538,16 @@ static inline void nb_config_diff_dnode_log(const char *context,
}
#endif
/* Calculate the delta between two different configurations. */
static void nb_config_diff(const struct nb_config *config1,
const struct nb_config *config2,
struct nb_config_cbs *changes)
/*
* Calculate the delta between two different configurations.
*
* NOTE: 'config1' is the reference DB, while 'config2' is
* the DB being compared against 'config1'. Typically 'config1'
* should be the Running DB and 'config2' is the Candidate DB.
*/
void nb_config_diff(const struct nb_config *config1,
const struct nb_config *config2,
struct nb_config_cbs *changes)
{
struct lyd_node *diff = NULL;
const struct lyd_node *root, *dnode;
@ -734,6 +753,169 @@ int nb_candidate_edit(struct nb_config *candidate,
return NB_OK;
}
static void nb_update_candidate_changes(struct nb_config *candidate,
struct nb_cfg_change *change,
uint32_t *seq)
{
enum nb_operation oper = change->operation;
char *xpath = change->xpath;
struct lyd_node *root = NULL;
struct lyd_node *dnode;
struct nb_config_cbs *cfg_chgs = &candidate->cfg_chgs;
int op;
switch (oper) {
case NB_OP_CREATE:
case NB_OP_MODIFY:
root = yang_dnode_get(candidate->dnode, xpath);
break;
case NB_OP_DESTROY:
root = yang_dnode_get(running_config->dnode, xpath);
/* code */
break;
case NB_OP_MOVE:
case NB_OP_PRE_VALIDATE:
case NB_OP_APPLY_FINISH:
case NB_OP_GET_ELEM:
case NB_OP_GET_NEXT:
case NB_OP_GET_KEYS:
case NB_OP_LOOKUP_ENTRY:
case NB_OP_RPC:
break;
default:
assert(!"non-enum value, invalid");
}
if (!root)
return;
LYD_TREE_DFS_BEGIN (root, dnode) {
op = nb_lyd_diff_get_op(dnode);
switch (op) {
case 'c':
nb_config_diff_created(dnode, seq, cfg_chgs);
LYD_TREE_DFS_continue = 1;
break;
case 'd':
nb_config_diff_deleted(dnode, seq, cfg_chgs);
LYD_TREE_DFS_continue = 1;
break;
case 'r':
nb_config_diff_add_change(cfg_chgs, NB_OP_MODIFY, seq,
dnode);
break;
default:
break;
}
LYD_TREE_DFS_END(root, dnode);
}
}
static bool nb_is_operation_allowed(struct nb_node *nb_node,
struct nb_cfg_change *change)
{
enum nb_operation oper = change->operation;
if (lysc_is_key(nb_node->snode)) {
if (oper == NB_OP_MODIFY || oper == NB_OP_DESTROY)
return false;
}
return true;
}
void nb_candidate_edit_config_changes(
struct nb_config *candidate_config, struct nb_cfg_change cfg_changes[],
size_t num_cfg_changes, const char *xpath_base, const char *curr_xpath,
int xpath_index, char *err_buf, int err_bufsize, bool *error)
{
uint32_t seq = 0;
if (error)
*error = false;
if (xpath_base == NULL)
xpath_base = "";
/* Edit candidate configuration. */
for (size_t i = 0; i < num_cfg_changes; i++) {
struct nb_cfg_change *change = &cfg_changes[i];
struct nb_node *nb_node;
char xpath[XPATH_MAXLEN];
struct yang_data *data;
int ret;
/* Handle relative XPaths. */
memset(xpath, 0, sizeof(xpath));
if (xpath_index > 0
&& (xpath_base[0] == '.' || change->xpath[0] == '.'))
strlcpy(xpath, curr_xpath, sizeof(xpath));
if (xpath_base[0]) {
if (xpath_base[0] == '.')
strlcat(xpath, xpath_base + 1, sizeof(xpath));
else
strlcat(xpath, xpath_base, sizeof(xpath));
}
if (change->xpath[0] == '.')
strlcat(xpath, change->xpath + 1, sizeof(xpath));
else
strlcpy(xpath, change->xpath, sizeof(xpath));
/* Find the northbound node associated to the data path. */
nb_node = nb_node_find(xpath);
if (!nb_node) {
flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
"%s: unknown data path: %s", __func__, xpath);
if (error)
*error = true;
continue;
}
/* Find if the node to be edited is not a key node */
if (!nb_is_operation_allowed(nb_node, change)) {
zlog_err(" Xpath %s points to key node", xpath);
if (error)
*error = true;
break;
}
/* If the value is not set, get the default if it exists. */
if (change->value == NULL)
change->value = yang_snode_get_default(nb_node->snode);
data = yang_data_new(xpath, change->value);
/*
* Ignore "not found" errors when editing the candidate
* configuration.
*/
ret = nb_candidate_edit(candidate_config, nb_node,
change->operation, xpath, NULL, data);
yang_data_free(data);
if (ret != NB_OK && ret != NB_ERR_NOT_FOUND) {
flog_warn(
EC_LIB_NB_CANDIDATE_EDIT_ERROR,
"%s: failed to edit candidate configuration: operation [%s] xpath [%s]",
__func__, nb_operation_name(change->operation),
xpath);
if (error)
*error = true;
continue;
}
nb_update_candidate_changes(candidate_config, change, &seq);
}
if (error && *error) {
char buf[BUFSIZ];
/*
* Failure to edit the candidate configuration should never
* happen in practice, unless there's a bug in the code. When
* that happens, log the error but otherwise ignore it.
*/
snprintf(err_buf, err_bufsize,
"%% Failed to edit configuration.\n\n%s",
yang_print_errors(ly_native_ctx, buf, sizeof(buf)));
}
}
bool nb_candidate_needs_update(const struct nb_config *candidate)
{
if (candidate->version < running_config->version)
@ -761,8 +943,8 @@ int nb_candidate_update(struct nb_config *candidate)
* WARNING: lyd_validate() can change the configuration as part of the
* validation process.
*/
static int nb_candidate_validate_yang(struct nb_config *candidate, char *errmsg,
size_t errmsg_len)
int nb_candidate_validate_yang(struct nb_config *candidate, char *errmsg,
size_t errmsg_len)
{
if (lyd_validate_all(&candidate->dnode, ly_native_ctx,
LYD_VALIDATE_NO_STATE, NULL)
@ -775,10 +957,10 @@ static int nb_candidate_validate_yang(struct nb_config *candidate, char *errmsg,
}
/* Perform code-level validation using the northbound callbacks. */
static int nb_candidate_validate_code(struct nb_context *context,
struct nb_config *candidate,
struct nb_config_cbs *changes,
char *errmsg, size_t errmsg_len)
int nb_candidate_validate_code(struct nb_context *context,
struct nb_config *candidate,
struct nb_config_cbs *changes, char *errmsg,
size_t errmsg_len)
{
struct nb_config_cb *cb;
struct lyd_node *root, *child;
@ -816,6 +998,21 @@ static int nb_candidate_validate_code(struct nb_context *context,
return NB_OK;
}
int nb_candidate_diff_and_validate_yang(struct nb_context *context,
struct nb_config *candidate,
struct nb_config_cbs *changes,
char *errmsg, size_t errmsg_len)
{
if (nb_candidate_validate_yang(candidate, errmsg, sizeof(errmsg_len))
!= NB_OK)
return NB_ERR_VALIDATION;
RB_INIT(nb_config_cbs, changes);
nb_config_diff(running_config, candidate, changes);
return NB_OK;
}
int nb_candidate_validate(struct nb_context *context,
struct nb_config *candidate, char *errmsg,
size_t errmsg_len)
@ -823,11 +1020,11 @@ int nb_candidate_validate(struct nb_context *context,
struct nb_config_cbs changes;
int ret;
if (nb_candidate_validate_yang(candidate, errmsg, errmsg_len) != NB_OK)
return NB_ERR_VALIDATION;
ret = nb_candidate_diff_and_validate_yang(context, candidate, &changes,
errmsg, errmsg_len);
if (ret != NB_OK)
return ret;
RB_INIT(nb_config_cbs, &changes);
nb_config_diff(running_config, candidate, &changes);
ret = nb_candidate_validate_code(context, candidate, &changes, errmsg,
errmsg_len);
nb_config_diff_del_changes(&changes);
@ -839,12 +1036,14 @@ int nb_candidate_commit_prepare(struct nb_context context,
struct nb_config *candidate,
const char *comment,
struct nb_transaction **transaction,
bool skip_validate, bool ignore_zero_change,
char *errmsg, size_t errmsg_len)
{
struct nb_config_cbs changes;
if (nb_candidate_validate_yang(candidate, errmsg, errmsg_len)
!= NB_OK) {
if (!skip_validate
&& nb_candidate_validate_yang(candidate, errmsg, errmsg_len)
!= NB_OK) {
flog_warn(EC_LIB_NB_CANDIDATE_INVALID,
"%s: failed to validate candidate configuration",
__func__);
@ -853,15 +1052,17 @@ int nb_candidate_commit_prepare(struct nb_context context,
RB_INIT(nb_config_cbs, &changes);
nb_config_diff(running_config, candidate, &changes);
if (RB_EMPTY(nb_config_cbs, &changes)) {
if (!ignore_zero_change && RB_EMPTY(nb_config_cbs, &changes)) {
snprintf(
errmsg, errmsg_len,
"No changes to apply were found during preparation phase");
return NB_ERR_NO_CHANGES;
}
if (nb_candidate_validate_code(&context, candidate, &changes, errmsg,
errmsg_len) != NB_OK) {
if (!skip_validate
&& nb_candidate_validate_code(&context, candidate, &changes, errmsg,
errmsg_len)
!= NB_OK) {
flog_warn(EC_LIB_NB_CANDIDATE_INVALID,
"%s: failed to validate candidate configuration",
__func__);
@ -869,8 +1070,12 @@ int nb_candidate_commit_prepare(struct nb_context context,
return NB_ERR_VALIDATION;
}
*transaction = nb_transaction_new(context, candidate, &changes, comment,
errmsg, errmsg_len);
/*
* Re-use an existing transaction if provided. Else allocate a new one.
*/
if (!*transaction)
*transaction = nb_transaction_new(context, candidate, &changes,
comment, errmsg, errmsg_len);
if (*transaction == NULL) {
flog_warn(EC_LIB_NB_TRANSACTION_CREATION_FAILED,
"%s: failed to create transaction: %s", __func__,
@ -921,7 +1126,8 @@ int nb_candidate_commit(struct nb_context context, struct nb_config *candidate,
int ret;
ret = nb_candidate_commit_prepare(context, candidate, comment,
&transaction, errmsg, errmsg_len);
&transaction, false, false, errmsg,
errmsg_len);
/*
* Apply the changes if the preparation phase succeeded. Otherwise abort
* the transaction.
@ -1015,6 +1221,8 @@ static int nb_callback_create(struct nb_context *context,
bool unexpected_error = false;
int ret;
assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS));
nb_log_config_callback(event, NB_OP_CREATE, dnode);
args.context = context;
@ -1064,6 +1272,8 @@ static int nb_callback_modify(struct nb_context *context,
bool unexpected_error = false;
int ret;
assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS));
nb_log_config_callback(event, NB_OP_MODIFY, dnode);
args.context = context;
@ -1113,6 +1323,8 @@ static int nb_callback_destroy(struct nb_context *context,
bool unexpected_error = false;
int ret;
assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS));
nb_log_config_callback(event, NB_OP_DESTROY, dnode);
args.context = context;
@ -1156,6 +1368,8 @@ static int nb_callback_move(struct nb_context *context,
bool unexpected_error = false;
int ret;
assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS));
nb_log_config_callback(event, NB_OP_MOVE, dnode);
args.context = context;
@ -1199,6 +1413,9 @@ static int nb_callback_pre_validate(struct nb_context *context,
bool unexpected_error = false;
int ret;
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
return 0;
nb_log_config_callback(NB_EV_VALIDATE, NB_OP_PRE_VALIDATE, dnode);
args.dnode = dnode;
@ -1230,6 +1447,9 @@ static void nb_callback_apply_finish(struct nb_context *context,
{
struct nb_cb_apply_finish_args args = {};
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
return;
nb_log_config_callback(NB_EV_APPLY, NB_OP_APPLY_FINISH, dnode);
args.context = context;
@ -1245,6 +1465,9 @@ struct yang_data *nb_callback_get_elem(const struct nb_node *nb_node,
{
struct nb_cb_get_elem_args args = {};
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
return NULL;
DEBUGD(&nb_dbg_cbs_state,
"northbound callback (get_elem): xpath [%s] list_entry [%p]",
xpath, list_entry);
@ -1260,6 +1483,9 @@ const void *nb_callback_get_next(const struct nb_node *nb_node,
{
struct nb_cb_get_next_args args = {};
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
return NULL;
DEBUGD(&nb_dbg_cbs_state,
"northbound callback (get_next): node [%s] parent_list_entry [%p] list_entry [%p]",
nb_node->xpath, parent_list_entry, list_entry);
@ -1274,6 +1500,9 @@ int nb_callback_get_keys(const struct nb_node *nb_node, const void *list_entry,
{
struct nb_cb_get_keys_args args = {};
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
return 0;
DEBUGD(&nb_dbg_cbs_state,
"northbound callback (get_keys): node [%s] list_entry [%p]",
nb_node->xpath, list_entry);
@ -1289,6 +1518,9 @@ const void *nb_callback_lookup_entry(const struct nb_node *nb_node,
{
struct nb_cb_lookup_entry_args args = {};
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
return NULL;
DEBUGD(&nb_dbg_cbs_state,
"northbound callback (lookup_entry): node [%s] parent_list_entry [%p]",
nb_node->xpath, parent_list_entry);
@ -1304,6 +1536,9 @@ int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath,
{
struct nb_cb_rpc_args args = {};
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
return 0;
DEBUGD(&nb_dbg_cbs_rpc, "northbound RPC: %s", xpath);
args.xpath = xpath;
@ -1330,6 +1565,9 @@ static int nb_callback_configuration(struct nb_context *context,
union nb_resource *resource;
int ret = NB_ERR;
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
return NB_OK;
if (event == NB_EV_VALIDATE)
resource = NULL;
else
@ -1733,7 +1971,7 @@ static int nb_oper_data_iter_list(const struct nb_node *nb_node,
/* Iterate over all list entries. */
do {
const struct lysc_node_leaf *skey;
struct yang_list_keys list_keys;
struct yang_list_keys list_keys = {};
char xpath[XPATH_MAXLEN * 2];
int ret;
@ -2391,6 +2629,8 @@ const char *nb_client_name(enum nb_client client)
return "Pcep";
case NB_CLIENT_MGMTD_SERVER:
return "MGMTD Server";
case NB_CLIENT_MGMTD_BE:
return "MGMT Backend";
case NB_CLIENT_NONE:
return "None";
}
@ -2400,6 +2640,10 @@ const char *nb_client_name(enum nb_client client)
static void nb_load_callbacks(const struct frr_yang_module_info *module)
{
if (module->ignore_cbs)
return;
for (size_t i = 0; module->nodes[i].xpath; i++) {
struct nb_node *nb_node;
uint32_t priority;
@ -2473,7 +2717,8 @@ void nb_init(struct thread_master *tm,
/* Initialize the compiled nodes with northbound data */
for (size_t i = 0; i < nmodules; i++) {
yang_snodes_iterate(loaded[i]->info, nb_node_new_cb, 0, NULL);
yang_snodes_iterate(loaded[i]->info, nb_node_new_cb, 0,
(void *)modules[i]);
nb_load_callbacks(modules[i]);
}

View File

@ -22,6 +22,39 @@ extern "C" {
struct vty;
struct debug;
struct nb_yang_xpath_tag {
uint32_t ns;
uint32_t id;
};
struct nb_yang_value {
struct lyd_value value;
LY_DATA_TYPE value_type;
uint8_t value_flags;
};
struct nb_yang_xpath_elem {
struct nb_yang_xpath_tag tag;
struct nb_yang_value val;
};
#define NB_MAX_NUM_KEYS UINT8_MAX
#define NB_MAX_NUM_XPATH_TAGS UINT8_MAX
struct nb_yang_xpath {
uint8_t length;
struct {
uint8_t num_keys;
struct nb_yang_xpath_elem keys[NB_MAX_NUM_KEYS];
} tags[NB_MAX_NUM_XPATH_TAGS];
};
#define NB_YANG_XPATH_KEY(__xpath, __indx1, __indx2) \
((__xpath->num_tags > __indx1) \
&& (__xpath->tags[__indx1].num_keys > __indx2) \
? &__xpath->tags[__indx1].keys[__indx2] \
: NULL)
/* Northbound events. */
enum nb_event {
/*
@ -564,6 +597,8 @@ struct nb_node {
#define F_NB_NODE_CONFIG_ONLY 0x01
/* The YANG list doesn't contain key leafs. */
#define F_NB_NODE_KEYLESS_LIST 0x02
/* Ignore callbacks for this node */
#define F_NB_NODE_IGNORE_CBS 0x04
/*
* HACK: old gcc versions (< 5.x) have a bug that prevents C99 flexible arrays
@ -576,6 +611,12 @@ struct frr_yang_module_info {
/* YANG module name. */
const char *name;
/*
* Ignore callbacks for this module. Set this to true to
* load module without any callbacks.
*/
bool ignore_cbs;
/* Northbound callbacks. */
const struct {
/* Data path of this YANG node. */
@ -620,6 +661,7 @@ enum nb_client {
NB_CLIENT_GRPC,
NB_CLIENT_PCEP,
NB_CLIENT_MGMTD_SERVER,
NB_CLIENT_MGMTD_BE,
};
/* Northbound context. */
@ -631,12 +673,6 @@ struct nb_context {
const void *user;
};
/* Northbound configuration. */
struct nb_config {
struct lyd_node *dnode;
uint32_t version;
};
/* Northbound configuration callback. */
struct nb_config_cb {
RB_ENTRY(nb_config_cb) entry;
@ -663,6 +699,13 @@ struct nb_transaction {
struct nb_config_cbs changes;
};
/* Northbound configuration. */
struct nb_config {
struct lyd_node *dnode;
uint32_t version;
struct nb_config_cbs cfg_chgs;
};
/* Callback function used by nb_oper_data_iterate(). */
typedef int (*nb_oper_data_cb)(const struct lysc_node *snode,
struct yang_translator *translator,
@ -832,6 +875,9 @@ extern int nb_candidate_edit(struct nb_config *candidate,
const struct yang_data *previous,
const struct yang_data *data);
extern void nb_config_diff_created(const struct lyd_node *dnode, uint32_t *seq,
struct nb_config_cbs *changes);
/*
* Check if a candidate configuration is outdated and needs to be updated.
*
@ -843,6 +889,30 @@ extern int nb_candidate_edit(struct nb_config *candidate,
*/
extern bool nb_candidate_needs_update(const struct nb_config *candidate);
extern void nb_candidate_edit_config_changes(
struct nb_config *candidate_config, struct nb_cfg_change cfg_changes[],
size_t num_cfg_changes, const char *xpath_base, const char *curr_xpath,
int xpath_index, char *err_buf, int err_bufsize, bool *error);
extern void nb_config_diff_del_changes(struct nb_config_cbs *changes);
extern int nb_candidate_diff_and_validate_yang(struct nb_context *context,
struct nb_config *candidate,
struct nb_config_cbs *changes,
char *errmsg, size_t errmsg_len);
extern void nb_config_diff(const struct nb_config *reference,
const struct nb_config *incremental,
struct nb_config_cbs *changes);
extern int nb_candidate_validate_yang(struct nb_config *candidate, char *errmsg,
size_t errmsg_len);
extern int nb_candidate_validate_code(struct nb_context *context,
struct nb_config *candidate,
struct nb_config_cbs *changes,
char *errmsg, size_t errmsg_len);
/*
* Update a candidate configuration by rebasing the changes on top of the latest
* running configuration. Resolve conflicts automatically by giving preference
@ -922,7 +992,9 @@ extern int nb_candidate_commit_prepare(struct nb_context context,
struct nb_config *candidate,
const char *comment,
struct nb_transaction **transaction,
char *errmsg, size_t errmsg_len);
bool skip_validate,
bool ignore_zero_change, char *errmsg,
size_t errmsg_len);
/*
* Abort a previously created configuration transaction, releasing all resources

View File

@ -141,79 +141,21 @@ static int nb_cli_apply_changes_internal(struct vty *vty,
bool clear_pending)
{
bool error = false;
if (xpath_base == NULL)
xpath_base = "";
char buf[BUFSIZ];
VTY_CHECK_XPATH;
/* Edit candidate configuration. */
for (size_t i = 0; i < vty->num_cfg_changes; i++) {
struct nb_cfg_change *change = &vty->cfg_changes[i];
struct nb_node *nb_node;
char xpath[XPATH_MAXLEN];
struct yang_data *data;
int ret;
/* Handle relative XPaths. */
memset(xpath, 0, sizeof(xpath));
if (vty->xpath_index > 0
&& (xpath_base[0] == '.' || change->xpath[0] == '.'))
strlcpy(xpath, VTY_CURR_XPATH, sizeof(xpath));
if (xpath_base[0]) {
if (xpath_base[0] == '.')
strlcat(xpath, xpath_base + 1, sizeof(xpath));
else
strlcat(xpath, xpath_base, sizeof(xpath));
}
if (change->xpath[0] == '.')
strlcat(xpath, change->xpath + 1, sizeof(xpath));
else
strlcpy(xpath, change->xpath, sizeof(xpath));
/* Find the northbound node associated to the data path. */
nb_node = nb_node_find(xpath);
if (!nb_node) {
flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
"%s: unknown data path: %s", __func__, xpath);
error = true;
continue;
}
/* If the value is not set, get the default if it exists. */
if (change->value == NULL)
change->value = yang_snode_get_default(nb_node->snode);
data = yang_data_new(xpath, change->value);
/*
* Ignore "not found" errors when editing the candidate
* configuration.
*/
ret = nb_candidate_edit(vty->candidate_config, nb_node,
change->operation, xpath, NULL, data);
yang_data_free(data);
if (ret != NB_OK && ret != NB_ERR_NOT_FOUND) {
flog_warn(
EC_LIB_NB_CANDIDATE_EDIT_ERROR,
"%s: failed to edit candidate configuration: operation [%s] xpath [%s]",
__func__, nb_operation_name(change->operation),
xpath);
error = true;
continue;
}
}
nb_candidate_edit_config_changes(
vty->candidate_config, vty->cfg_changes, vty->num_cfg_changes,
xpath_base, VTY_CURR_XPATH, vty->xpath_index, buf, sizeof(buf),
&error);
if (error) {
char buf[BUFSIZ];
/*
* Failure to edit the candidate configuration should never
* happen in practice, unless there's a bug in the code. When
* that happens, log the error but otherwise ignore it.
*/
vty_out(vty, "%% Failed to edit configuration.\n\n");
vty_out(vty, "%s",
yang_print_errors(ly_native_ctx, buf, sizeof(buf)));
vty_out(vty, "%s", buf);
}
/*

View File

@ -312,7 +312,8 @@ static void frr_confd_cdb_read_cb_prepare(int fd, int *subp, int reslen)
transaction = NULL;
context.client = NB_CLIENT_CONFD;
ret = nb_candidate_commit_prepare(context, candidate, NULL,
&transaction, errmsg, sizeof(errmsg));
&transaction, false, false, errmsg,
sizeof(errmsg));
if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) {
enum confd_errcode errcode;

View File

@ -825,7 +825,8 @@ HandleUnaryCommit(UnaryRpcState<frr::CommitRequest, frr::CommitResponse> *tag)
grpc_debug("`-> Performing PREPARE");
ret = nb_candidate_commit_prepare(
context, candidate->config, comment.c_str(),
&candidate->transaction, errmsg, sizeof(errmsg));
&candidate->transaction, false, false, errmsg,
sizeof(errmsg));
break;
case frr::CommitRequest::ABORT:
grpc_debug("`-> Performing ABORT");

View File

@ -269,7 +269,8 @@ static int frr_sr_config_change_cb_prepare(sr_session_ctx_t *session,
* required to apply them.
*/
ret = nb_candidate_commit_prepare(context, candidate, NULL,
&transaction, errmsg, sizeof(errmsg));
&transaction, false, false, errmsg,
sizeof(errmsg));
if (ret != NB_OK && ret != NB_ERR_NO_CHANGES)
flog_warn(
EC_LIB_LIBSYSREPO,

View File

@ -64,6 +64,7 @@ lib_libfrr_la_SOURCES = \
lib/log_vty.c \
lib/md5.c \
lib/memory.c \
lib/mgmt_be_client.c \
lib/mgmt_fe_client.c \
lib/mlag.c \
lib/module.c \
@ -241,6 +242,7 @@ pkginclude_HEADERS += \
lib/md5.h \
lib/memory.h \
lib/mgmt.pb-c.h \
lib/mgmt_be_client.h \
lib/mgmt_fe_client.h \
lib/mgmt_pb.h \
lib/module.h \

View File

@ -8,6 +8,8 @@
#include <zebra.h>
#include "mgmtd/mgmt.h"
#include "mgmtd/mgmt_be_server.h"
#include "mgmtd/mgmt_be_adapter.h"
#include "mgmtd/mgmt_fe_server.h"
#include "mgmtd/mgmt_fe_adapter.h"
#include "mgmtd/mgmt_ds.h"
@ -47,9 +49,15 @@ void mgmt_init(void)
/* Initialize datastores */
mgmt_ds_init(mm);
/* Initialize the MGMTD Backend Adapter Module */
mgmt_be_adapter_init(mm->master);
/* Initialize the MGMTD Frontend Adapter Module */
mgmt_fe_adapter_init(mm->master, mm);
/* Start the MGMTD Backend Server for clients to connect */
mgmt_be_server_init(mm->master);
/* Start the MGMTD Frontend Server for clients to connect */
mgmt_fe_server_init(mm->master);
@ -61,5 +69,7 @@ void mgmt_terminate(void)
{
mgmt_fe_server_destroy();
mgmt_fe_adapter_destroy();
mgmt_be_server_destroy();
mgmt_be_adapter_destroy();
mgmt_ds_destroy();
}

1288
mgmtd/mgmt_be_adapter.c Normal file

File diff suppressed because it is too large Load Diff

231
mgmtd/mgmt_be_adapter.h Normal file
View File

@ -0,0 +1,231 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* MGMTD Backend Client Connection Adapter
*
* Copyright (C) 2021 Vmware, Inc.
* Pushpasis Sarkar <spushpasis@vmware.com>
*/
#ifndef _FRR_MGMTD_BE_ADAPTER_H_
#define _FRR_MGMTD_BE_ADAPTER_H_
#include "mgmtd/mgmt_defines.h"
#include "mgmt_be_client.h"
#include "mgmtd/mgmt_ds.h"
#define MGMTD_BE_CONN_INIT_DELAY_MSEC 50
#define MGMTD_FIND_ADAPTER_BY_INDEX(adapter_index) \
mgmt_adaptr_ref[adapter_index]
enum mgmt_be_req_type {
MGMTD_BE_REQ_NONE = 0,
MGMTD_BE_REQ_CFG_VALIDATE,
MGMTD_BE_REQ_CFG_APPLY,
MGMTD_BE_REQ_DATA_GET_ELEM,
MGMTD_BE_REQ_DATA_GET_NEXT
};
struct mgmt_be_cfgreq {
Mgmtd__YangCfgDataReq **cfgdata_reqs;
size_t num_reqs;
};
struct mgmt_be_datareq {
Mgmtd__YangGetDataReq **getdata_reqs;
size_t num_reqs;
};
PREDECL_LIST(mgmt_be_adapters);
PREDECL_LIST(mgmt_txn_badapters);
struct mgmt_be_client_adapter {
enum mgmt_be_client_id id;
int conn_fd;
union sockunion conn_su;
struct thread *conn_init_ev;
struct thread *conn_read_ev;
struct thread *conn_write_ev;
struct thread *conn_writes_on;
struct thread *proc_msg_ev;
uint32_t flags;
char name[MGMTD_CLIENT_NAME_MAX_LEN];
uint8_t num_xpath_reg;
char xpath_reg[MGMTD_MAX_NUM_XPATH_REG][MGMTD_MAX_XPATH_LEN];
/* IO streams for read and write */
/* pthread_mutex_t ibuf_mtx; */
struct stream_fifo *ibuf_fifo;
/* pthread_mutex_t obuf_mtx; */
struct stream_fifo *obuf_fifo;
/* Private I/O buffers */
struct stream *ibuf_work;
struct stream *obuf_work;
uint8_t msg_buf[MGMTD_BE_MSG_MAX_LEN];
/* Buffer of data waiting to be written to client. */
/* struct buffer *wb; */
int refcount;
uint32_t num_msg_tx;
uint32_t num_msg_rx;
/*
* List of config items that should be sent to the
* backend during re/connect. This is temporarily
* created and then freed-up as soon as the initial
* config items has been applied onto the backend.
*/
struct nb_config_cbs cfg_chgs;
struct mgmt_be_adapters_item list_linkage;
struct mgmt_txn_badapters_item txn_list_linkage;
};
#define MGMTD_BE_ADAPTER_FLAGS_WRITES_OFF (1U << 0)
#define MGMTD_BE_ADAPTER_FLAGS_CFG_SYNCED (1U << 1)
DECLARE_LIST(mgmt_be_adapters, struct mgmt_be_client_adapter, list_linkage);
DECLARE_LIST(mgmt_txn_badapters, struct mgmt_be_client_adapter,
txn_list_linkage);
union mgmt_be_xpath_subscr_info {
uint8_t subscribed;
struct {
uint8_t validate_config : 1;
uint8_t notify_config : 1;
uint8_t own_oper_data : 1;
};
};
struct mgmt_be_client_subscr_info {
union mgmt_be_xpath_subscr_info xpath_subscr[MGMTD_BE_CLIENT_ID_MAX];
};
/* Initialise backend adapter module. */
extern int mgmt_be_adapter_init(struct thread_master *tm);
/* Destroy the backend adapter module. */
extern void mgmt_be_adapter_destroy(void);
/* Acquire lock for backend adapter. */
extern void mgmt_be_adapter_lock(struct mgmt_be_client_adapter *adapter);
/* Remove lock from backend adapter. */
extern void mgmt_be_adapter_unlock(struct mgmt_be_client_adapter **adapter);
/* Create backend adapter. */
extern struct mgmt_be_client_adapter *
mgmt_be_create_adapter(int conn_fd, union sockunion *su);
/* Fetch backend adapter given an adapter name. */
extern struct mgmt_be_client_adapter *
mgmt_be_get_adapter_by_name(const char *name);
/* Fetch backend adapter given an client ID. */
extern struct mgmt_be_client_adapter *
mgmt_be_get_adapter_by_id(enum mgmt_be_client_id id);
/* Fetch backend adapter config. */
extern int
mgmt_be_get_adapter_config(struct mgmt_be_client_adapter *adapter,
struct mgmt_ds_ctx *ds_ctx,
struct nb_config_cbs **cfg_chgs);
/* Create a transaction. */
extern int mgmt_be_create_txn(struct mgmt_be_client_adapter *adapter,
uint64_t txn_id);
/* Destroy a transaction. */
extern int mgmt_be_destroy_txn(struct mgmt_be_client_adapter *adapter,
uint64_t txn_id);
/*
* Send config data create request to backend client.
*
* adaptr
* Backend adapter information.
*
* txn_id
* Unique transaction identifier.
*
* batch_id
* Request batch ID.
*
* cfg_req
* Config data request.
*
* end_of_data
* TRUE if the data from last batch, FALSE otherwise.
*
* Returns:
* 0 on success, -1 on failure.
*/
extern int mgmt_be_send_cfg_data_create_req(
struct mgmt_be_client_adapter *adapter, uint64_t txn_id,
uint64_t batch_id, struct mgmt_be_cfgreq *cfg_req, bool end_of_data);
/*
* Send config validate request to backend client.
*
* adaptr
* Backend adapter information.
*
* txn_id
* Unique transaction identifier.
*
* batch_ids
* List of request batch IDs.
*
* num_batch_ids
* Number of batch ids.
*
* Returns:
* 0 on success, -1 on failure.
*/
extern int
mgmt_be_send_cfg_validate_req(struct mgmt_be_client_adapter *adapter,
uint64_t txn_id, uint64_t batch_ids[],
size_t num_batch_ids);
/*
* Send config apply request to backend client.
*
* adaptr
* Backend adapter information.
*
* txn_id
* Unique transaction identifier.
*
* Returns:
* 0 on success, -1 on failure.
*/
extern int
mgmt_be_send_cfg_apply_req(struct mgmt_be_client_adapter *adapter,
uint64_t txn_id);
/*
* Dump backend adapter status to vty.
*/
extern void mgmt_be_adapter_status_write(struct vty *vty);
/*
* Dump xpath registry for each backend client to vty.
*/
extern void mgmt_be_xpath_register_write(struct vty *vty);
/*
* Maps a YANG dtata Xpath to one or more
* backend clients that should be contacted for various purposes.
*/
extern int mgmt_be_get_subscr_info_for_xpath(
const char *xpath, struct mgmt_be_client_subscr_info *subscr_info);
/*
* Dump backend client information for a given xpath to vty.
*/
extern void mgmt_be_xpath_subscr_info_write(struct vty *vty,
const char *xpath);
#endif /* _FRR_MGMTD_BE_ADAPTER_H_ */

160
mgmtd/mgmt_be_server.c Normal file
View File

@ -0,0 +1,160 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* MGMTD Backend Server
*
* Copyright (C) 2021 Vmware, Inc.
* Pushpasis Sarkar <spushpasis@vmware.com>
*/
#include <zebra.h>
#include "network.h"
#include "libfrr.h"
#include "mgmtd/mgmt.h"
#include "mgmtd/mgmt_be_server.h"
#include "mgmtd/mgmt_be_adapter.h"
#ifdef REDIRECT_DEBUG_TO_STDERR
#define MGMTD_BE_SRVR_DBG(fmt, ...) \
fprintf(stderr, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
#define MGMTD_BE_SRVR_ERR(fmt, ...) \
fprintf(stderr, "%s: ERROR, " fmt "\n", __func__, ##__VA_ARGS__)
#else /* REDIRECT_DEBUG_TO_STDERR */
#define MGMTD_BE_SRVR_DBG(fmt, ...) \
do { \
if (mgmt_debug_be) \
zlog_debug("%s: " fmt, __func__, ##__VA_ARGS__); \
} while (0)
#define MGMTD_BE_SRVR_ERR(fmt, ...) \
zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__)
#endif /* REDIRECT_DEBUG_TO_STDERR */
static int mgmt_be_listen_fd;
static struct thread_master *mgmt_be_listen_tm;
static struct thread *mgmt_be_listen_ev;
static void mgmt_be_server_register_event(enum mgmt_be_event event);
static void mgmt_be_conn_accept(struct thread *thread)
{
int client_conn_fd;
union sockunion su;
if (mgmt_be_listen_fd < 0)
return;
/* We continue hearing server listen socket. */
mgmt_be_server_register_event(MGMTD_BE_SERVER);
memset(&su, 0, sizeof(union sockunion));
/* We can handle IPv4 or IPv6 socket. */
client_conn_fd = sockunion_accept(mgmt_be_listen_fd, &su);
if (client_conn_fd < 0) {
MGMTD_BE_SRVR_ERR(
"Failed to accept MGMTD Backend client connection : %s",
safe_strerror(errno));
return;
}
set_nonblocking(client_conn_fd);
set_cloexec(client_conn_fd);
MGMTD_BE_SRVR_DBG("Got a new MGMTD Backend connection");
mgmt_be_create_adapter(client_conn_fd, &su);
}
static void mgmt_be_server_register_event(enum mgmt_be_event event)
{
if (event == MGMTD_BE_SERVER) {
thread_add_read(mgmt_be_listen_tm, mgmt_be_conn_accept,
NULL, mgmt_be_listen_fd,
&mgmt_be_listen_ev);
assert(mgmt_be_listen_ev);
} else {
assert(!"mgmt_be_server_post_event() called incorrectly");
}
}
static void mgmt_be_server_start(const char *hostname)
{
int ret;
int sock;
struct sockaddr_un addr;
mode_t old_mask;
/* Set umask */
old_mask = umask(0077);
sock = socket(AF_UNIX, SOCK_STREAM, PF_UNSPEC);
if (sock < 0) {
MGMTD_BE_SRVR_ERR("Failed to create server socket: %s",
safe_strerror(errno));
goto mgmt_be_server_start_failed;
}
addr.sun_family = AF_UNIX,
strlcpy(addr.sun_path, MGMTD_BE_SERVER_PATH, sizeof(addr.sun_path));
unlink(addr.sun_path);
ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0) {
MGMTD_BE_SRVR_ERR(
"Failed to bind server socket to '%s'. Err: %s",
addr.sun_path, safe_strerror(errno));
goto mgmt_be_server_start_failed;
}
ret = listen(sock, MGMTD_BE_MAX_CONN);
if (ret < 0) {
MGMTD_BE_SRVR_ERR("Failed to listen on server socket: %s",
safe_strerror(errno));
goto mgmt_be_server_start_failed;
}
/* Restore umask */
umask(old_mask);
mgmt_be_listen_fd = sock;
mgmt_be_server_register_event(MGMTD_BE_SERVER);
MGMTD_BE_SRVR_DBG("Started MGMTD Backend Server!");
return;
mgmt_be_server_start_failed:
if (sock)
close(sock);
mgmt_be_listen_fd = -1;
exit(-1);
}
int mgmt_be_server_init(struct thread_master *master)
{
if (mgmt_be_listen_tm) {
MGMTD_BE_SRVR_DBG("MGMTD Backend Server already running!");
return 0;
}
mgmt_be_listen_tm = master;
mgmt_be_server_start("localhost");
return 0;
}
void mgmt_be_server_destroy(void)
{
if (mgmt_be_listen_tm) {
MGMTD_BE_SRVR_DBG("Closing MGMTD Backend Server!");
if (mgmt_be_listen_ev) {
THREAD_OFF(mgmt_be_listen_ev);
mgmt_be_listen_ev = NULL;
}
if (mgmt_be_listen_fd >= 0) {
close(mgmt_be_listen_fd);
mgmt_be_listen_fd = -1;
}
mgmt_be_listen_tm = NULL;
}
}

20
mgmtd/mgmt_be_server.h Normal file
View File

@ -0,0 +1,20 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* MGMTD Backend Server
*
* Copyright (C) 2021 Vmware, Inc.
* Pushpasis Sarkar
*/
#ifndef _FRR_MGMTD_BE_SERVER_H_
#define _FRR_MGMTD_BE_SERVER_H_
#define MGMTD_BE_MAX_CONN 32
/* Initialise backend server */
extern int mgmt_be_server_init(struct thread_master *master);
/* Destroy backend server */
extern void mgmt_be_server_destroy(void);
#endif /* _FRR_MGMTD_BE_SERVER_H_ */

View File

@ -17,6 +17,10 @@
#define MGMTD_MAX_YANG_VALUE_LEN YANG_VALUE_MAXLEN
#define MGMTD_MAX_NUM_XPATH_REG 128
#define MGMTD_MAX_NUM_DATA_REQ_IN_BATCH 32
enum mgmt_result {
MGMTD_SUCCESS = 0,
MGMTD_INVALID_PARAM,
@ -35,6 +39,19 @@ enum mgmt_fe_event {
MGMTD_FE_PROC_MSG
};
enum mgmt_be_event {
MGMTD_BE_SERVER = 1,
MGMTD_BE_CONN_INIT,
MGMTD_BE_CONN_READ,
MGMTD_BE_CONN_WRITE,
MGMTD_BE_CONN_WRITES_ON,
MGMTD_BE_PROC_MSG,
MGMTD_BE_SCHED_CFG_PREPARE,
MGMTD_BE_RESCHED_CFG_PREPARE,
MGMTD_BE_SCHED_CFG_APPLY,
MGMTD_BE_RESCHED_CFG_APPLY,
};
#define MGMTD_TXN_ID_NONE 0
#endif /* _FRR_MGMTD_DEFINES_H */

View File

@ -16,6 +16,7 @@
#include "mgmtd/mgmt_ds.h"
#include "routing_nb.h"
/* mgmt options, we use GNU getopt library. */
static const struct option longopts[] = {
{"skip_runas", no_argument, NULL, 'S'},
@ -195,6 +196,17 @@ static void mgmt_vrf_terminate(void)
static const struct frr_yang_module_info *const mgmt_yang_modules[] = {
&frr_filter_info, &frr_interface_info, &frr_route_map_info,
&frr_routing_info, &frr_vrf_info,
/*
* YANG module info supported by backend clients get added here.
* NOTE: Always set .ignore_cbs true for to avoid validating
* backend northbound callbacks during loading.
*/
#if 0
#ifdef HAVE_STATICD
&(struct frr_yang_module_info){.name = "frr-staticd",
.ignore_cbs = true},
#endif
#endif
};
FRR_DAEMON_INFO(mgmtd, MGMTD, .vty_port = MGMTD_VTY_PORT,

View File

@ -19,5 +19,6 @@
DEFINE_MGROUP(MGMTD, "mgmt");
DEFINE_MTYPE(MGMTD, MGMTD, "MGMTD instance");
DEFINE_MTYPE(MGMTD, MGMTD_BE_ADPATER, "MGMTD backend adapter");
DEFINE_MTYPE(MGMTD, MGMTD_FE_ADPATER, "MGMTD Frontend adapter");
DEFINE_MTYPE(MGMTD, MGMTD_FE_SESSION, "MGMTD Frontend Client Session");

View File

@ -13,6 +13,7 @@
DECLARE_MGROUP(MGMTD);
DECLARE_MTYPE(MGMTD);
DECLARE_MTYPE(MGMTD_BE_ADPATER);
DECLARE_MTYPE(MGMTD_FE_ADPATER);
DECLARE_MTYPE(MGMTD_FE_SESSION);
#endif /* _FRR_MGMTD_MEMORY_H */

View File

@ -11,12 +11,39 @@
#include "command.h"
#include "json.h"
#include "mgmtd/mgmt.h"
#include "mgmtd/mgmt_be_server.h"
#include "mgmtd/mgmt_be_adapter.h"
#include "mgmtd/mgmt_fe_server.h"
#include "mgmtd/mgmt_fe_adapter.h"
#include "mgmtd/mgmt_ds.h"
#include "mgmtd/mgmt_vty_clippy.c"
DEFPY(show_mgmt_be_adapter,
show_mgmt_be_adapter_cmd,
"show mgmt backend-adapter all",
SHOW_STR
MGMTD_STR
MGMTD_BE_ADAPTER_STR
"Display all Backend Adapters\n")
{
mgmt_be_adapter_status_write(vty);
return CMD_SUCCESS;
}
DEFPY(show_mgmt_be_xpath_reg,
show_mgmt_be_xpath_reg_cmd,
"show mgmt backend-yang-xpath-registry",
SHOW_STR
MGMTD_STR
"Backend Adapter YANG Xpath Registry\n")
{
mgmt_be_xpath_register_write(vty);
return CMD_SUCCESS;
}
DEFPY(show_mgmt_fe_adapter, show_mgmt_fe_adapter_cmd,
"show mgmt frontend-adapter all [detail$detail]",
SHOW_STR
@ -199,6 +226,18 @@ DEFPY(show_mgmt_dump_data,
return CMD_SUCCESS;
}
DEFPY(show_mgmt_map_xpath,
show_mgmt_map_xpath_cmd,
"show mgmt yang-xpath-subscription WORD$path",
SHOW_STR
MGMTD_STR
"Get YANG Backend Subscription\n"
"XPath expression specifying the YANG data path\n")
{
mgmt_be_xpath_subscr_info_write(vty, path);
return CMD_SUCCESS;
}
DEFPY(mgmt_load_config,
mgmt_load_config_cmd,
"mgmt load-config WORD$filepath <merge|replace>$type",
@ -331,13 +370,29 @@ DEFPY(debug_mgmt,
void mgmt_vty_init(void)
{
/*
* Initialize command handling from VTYSH connection.
* Call command initialization routines defined by
* backend components that are moved to new MGMTD infra
* here one by one.
*/
#if 0
#if HAVE_STATICD
extern void static_vty_init(void);
static_vty_init();
#endif
#endif
install_node(&debug_node);
install_element(VIEW_NODE, &show_mgmt_be_adapter_cmd);
install_element(VIEW_NODE, &show_mgmt_be_xpath_reg_cmd);
install_element(VIEW_NODE, &show_mgmt_fe_adapter_cmd);
install_element(VIEW_NODE, &show_mgmt_ds_cmd);
install_element(VIEW_NODE, &show_mgmt_get_config_cmd);
install_element(VIEW_NODE, &show_mgmt_get_data_cmd);
install_element(VIEW_NODE, &show_mgmt_dump_data_cmd);
install_element(VIEW_NODE, &show_mgmt_map_xpath_cmd);
install_element(CONFIG_NODE, &mgmt_commit_cmd);
install_element(CONFIG_NODE, &mgmt_set_config_data_cmd);

View File

@ -18,6 +18,8 @@ noinst_LIBRARIES += mgmtd/libmgmtd.a
mgmtd_libmgmtd_a_SOURCES = \
mgmtd/mgmt.c \
mgmtd/mgmt_ds.c \
mgmtd/mgmt_be_server.c \
mgmtd/mgmt_be_adapter.c \
mgmtd/mgmt_fe_server.c \
mgmtd/mgmt_fe_adapter.c \
mgmtd/mgmt_memory.c \
@ -31,6 +33,8 @@ mgmtdheader_HEADERS = \
noinst_HEADERS += \
mgmtd/mgmt.h \
mgmtd/mgmt_be_server.h \
mgmtd/mgmt_be_adapter.h \
mgmtd/mgmt_ds.h \
mgmtd/mgmt_fe_server.h \
mgmtd/mgmt_fe_adapter.h \