Skip to content

Commit

Permalink
mgmtd: add support for native 'edit' operation
Browse files Browse the repository at this point in the history
This operation basically implements support for RESTCONF operations. It
receives an xpath and data in JSON/XML format, instead of (xpath, value)
tuples as required by the current protobuf interface.

Signed-off-by: Igor Ryzhov <[email protected]>
  • Loading branch information
idryzhov committed Feb 6, 2024
1 parent 627f3ae commit 1869663
Show file tree
Hide file tree
Showing 17 changed files with 776 additions and 49 deletions.
33 changes: 33 additions & 0 deletions lib/mgmt_fe_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,39 @@ int mgmt_fe_send_get_data_req(struct mgmt_fe_client *client,
return ret;
}

int mgmt_fe_send_edit_req(struct mgmt_fe_client *client, uint64_t session_id,
uint64_t req_id, uint8_t datastore,
LYD_FORMAT request_type, uint8_t flags,
uint8_t operation, const char *xpath,
const char *value)
{
struct mgmt_msg_edit *msg;
size_t xplen = strlen(xpath) + 1;
size_t vlen = value ? strlen(value) + 1 : 0;
int ret;

msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_edit, xplen + vlen,
MTYPE_MSG_NATIVE_EDIT);
msg->refer_id = session_id;
msg->req_id = req_id;
msg->code = MGMT_MSG_CODE_EDIT;
msg->request_type = request_type;
msg->flags = flags;
msg->datastore = datastore;
msg->operation = operation;
msg->xpath_len = xplen;
strlcpy(msg->data, xpath, xplen);
if (value)
strlcpy(msg->data + xplen, value, vlen);

debug_fe_client("Sending EDIT_REQ session-id %" PRIu64
" req-id %" PRIu64 " xpath: %s",
session_id, req_id, xpath);

ret = mgmt_msg_native_send_msg(&client->client.conn, msg, false);
mgmt_msg_native_free_msg(msg);
return ret;
}

static int mgmt_fe_client_handle_msg(struct mgmt_fe_client *client,
Mgmtd__FeMessage *fe_msg)
Expand Down
39 changes: 39 additions & 0 deletions lib/mgmt_fe_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,45 @@ extern int mgmt_fe_send_get_data_req(struct mgmt_fe_client *client,
uint8_t flags, uint8_t defaults,
const char *xpath);

/*
* Send EDIT to MGMTD daemon.
*
* client
* Client object.
*
* session_id
* Client session ID.
*
* req_id
* Client request ID.
*
* datastore
* Datastore for editing.
*
* request_type
* The LYD_FORMAT of the request.
*
* flags
* Flags to control the behavior of the request.
*
* operation
* NB_OP_* operation to perform.
*
* xpath
* the xpath to edit.
*
* value
* the value of data node.
*
* Returns:
* 0 on success, otherwise msg_conn_send_msg() return values.
*/
extern int mgmt_fe_send_edit_req(struct mgmt_fe_client *client,
uint64_t session_id, uint64_t req_id,
uint8_t datastore, LYD_FORMAT request_type,
uint8_t flags, uint8_t operation,
const char *xpath, const char *value);

/*
* Destroy library and cleanup everything.
*/
Expand Down
1 change: 1 addition & 0 deletions lib/mgmt_msg_native.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_GET_TREE, "native get tree msg");
DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_TREE_DATA, "native tree data msg");
DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_GET_DATA, "native get data msg");
DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_NOTIFY, "native get data msg");
DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_EDIT, "native edit msg");

int vmgmt_msg_native_send_error(struct msg_conn *conn, uint64_t sess_or_txn_id,
uint64_t req_id, bool short_circuit_ok,
Expand Down
47 changes: 47 additions & 0 deletions lib/mgmt_msg_native.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ extern "C" {
#include "memory.h"
#include "mgmt_msg.h"
#include "mgmt_defines.h"
#include "northbound.h"

#include <stdalign.h>

Expand Down Expand Up @@ -144,6 +145,7 @@ DECLARE_MTYPE(MSG_NATIVE_GET_TREE);
DECLARE_MTYPE(MSG_NATIVE_TREE_DATA);
DECLARE_MTYPE(MSG_NATIVE_GET_DATA);
DECLARE_MTYPE(MSG_NATIVE_NOTIFY);
DECLARE_MTYPE(MSG_NATIVE_EDIT);

/*
* Native message codes
Expand All @@ -153,6 +155,7 @@ DECLARE_MTYPE(MSG_NATIVE_NOTIFY);
#define MGMT_MSG_CODE_TREE_DATA 2
#define MGMT_MSG_CODE_GET_DATA 3
#define MGMT_MSG_CODE_NOTIFY 4
#define MGMT_MSG_CODE_EDIT 5

/*
* Datastores
Expand Down Expand Up @@ -317,6 +320,50 @@ _Static_assert(sizeof(struct mgmt_msg_notify_data) ==
offsetof(struct mgmt_msg_notify_data, result),
"Size mismatch");

#define EDIT_FLAG_IMPLICIT_LOCK 0x01
#define EDIT_FLAG_IMPLICIT_COMMIT 0x02

#define EDIT_OP_CREATE 0
#define EDIT_OP_DELETE 4
#define EDIT_OP_MERGE 2
#define EDIT_OP_REPLACE 5
#define EDIT_OP_REMOVE 3

_Static_assert(EDIT_OP_CREATE == NB_OP_CREATE_EXCL, "Operation mismatch");
_Static_assert(EDIT_OP_DELETE == NB_OP_DELETE, "Operation mismatch");
_Static_assert(EDIT_OP_MERGE == NB_OP_MODIFY, "Operation mismatch");
_Static_assert(EDIT_OP_REPLACE == NB_OP_REPLACE, "Operation mismatch");
_Static_assert(EDIT_OP_REMOVE == NB_OP_DESTROY, "Operation mismatch");

/**
* struct mgmt_msg_edit - frontend edit request.
*
* @request_type: ``LYD_FORMAT`` for the @data.
* @flags: combination of ``EDIT_FLAG_*`` flags.
* @datastore: the datastore to edit.
* @operation: one of ``EDIT_OP_*`` operations.
* @xpath_len: the length of the xpath.
* @data: the xpath and value to edit, both NULL terminated.
* for CREATE operation, xpath should point to the parent node.
*/
struct mgmt_msg_edit {
struct mgmt_msg_header;
uint8_t request_type;
uint8_t flags;
uint8_t datastore;
uint8_t operation;

uint32_t xpath_len;

alignas(8) char data[];
};
_Static_assert(sizeof(struct mgmt_msg_edit) ==
offsetof(struct mgmt_msg_edit, data),
"Size mismatch");

#define mgmt_msg_edit_get_xpath(msg) ((msg)->data)
#define mgmt_msg_edit_get_value(msg) ((msg)->data + (msg)->xpath_len)

/*
* Validate that the message ends in a NUL terminating byte
*/
Expand Down
211 changes: 211 additions & 0 deletions lib/northbound.c
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,217 @@ int nb_candidate_edit(struct nb_config *candidate, const struct nb_node *nb_node
return NB_OK;
}

static int nb_candidate_edit_tree_add(struct nb_config *candidate,
enum nb_operation operation,
LYD_FORMAT format, const char *xpath,
const char *value,
struct lyd_node **errors)
{
char parent_xpath[XPATH_MAXLEN];
struct lyd_node *tree = NULL;
struct lyd_node *parent = NULL;
struct lyd_node *dnode = NULL;
struct lyd_node *existing = NULL;
struct lyd_node *ex_parent = NULL;
struct ly_in *in;
bool root;
int ret;
LY_ERR err = LY_SUCCESS;

ly_in_new_memory(value, &in);

strlcpy(parent_xpath, xpath, sizeof(parent_xpath));
root = parent_xpath[0] == 0 ||
(parent_xpath[0] == '/' && parent_xpath[1] == 0);

/*
* NB_OP_CREATE_EXCL already expects parent xpath.
* For others - pop one level if it's not root.
*/
if (operation != NB_OP_CREATE_EXCL && !root) {
ret = yang_xpath_pop_node(parent_xpath);
if (ret) {
yang_create_error(errors, "application", "invalid-value",
NULL, NULL, "Invalid xpath");
goto done;
}
root = parent_xpath[0] == 0 ||
(parent_xpath[0] == '/' && parent_xpath[1] == 0);
}

/*
* Create parent if it's not root. We're creating a new tree here to be
* merged later with candidate.
*/
if (!root) {
err = lyd_new_path2(NULL, ly_native_ctx, parent_xpath, NULL, 0,
0, 0, &tree, &parent);
if (err) {
yang_create_error(errors, "application",
"operation-failed", NULL, NULL,
"Cannot create new data tree");
goto done;
}
assert(parent);
}

/* parse data */
err = yang_lyd_parse_data(ly_native_ctx, parent, in, format,
LYD_PARSE_ONLY | LYD_PARSE_STRICT |
LYD_PARSE_NO_STATE,
0, &dnode);
if (err) {
yang_create_error(errors, "application", "invalid-value", NULL,
NULL, "Invalid data");
goto done;
}

/* set the tree if we created a top-level node */
if (root)
tree = dnode;

/* check if the node already exists in candidate */
if ((operation == NB_OP_CREATE_EXCL || operation == NB_OP_REPLACE) &&
dnode != NULL) {
char path[XPATH_MAXLEN];

lyd_path(dnode, LYD_PATH_STD, path, sizeof(path));

if (root)
existing = candidate->dnode;
else
existing = yang_dnode_get(candidate->dnode, path);

if (existing) {
if (operation == NB_OP_CREATE_EXCL) {
yang_create_error(errors, "application",
"data-exists", NULL, NULL,
"Data already exists");
ret = NB_ERR;
goto done;
}

if (root) {
candidate->dnode = NULL;
} else {
/* if it's the first top-level node, update candidate */
if (candidate->dnode == existing)
candidate->dnode =
candidate->dnode->next;

ex_parent = lyd_parent(existing);
lyd_unlink_tree(existing);
}
}
}

err = lyd_merge_siblings(&candidate->dnode, tree,
LYD_MERGE_DESTRUCT | LYD_MERGE_WITH_FLAGS);
if (err) {
/* if replace failed, restore the original node */
if (existing) {
if (root) {
candidate->dnode = existing;
} else {
if (ex_parent)
lyd_insert_child(ex_parent, existing);
else
lyd_insert_sibling(candidate->dnode,
existing,
&candidate->dnode);
}
}
yang_create_error(errors, "application", "operation-failed",
NULL, NULL, "Cannot update candidate");
goto done;
} else {
/*
* Free existing node after replace.
* We're using `lyd_free_siblings` here to free the whole
* tree if we replaced the root node. It won't affect other
* siblings if it wasn't root, because the existing node
* was unlinked from the tree.
*/
if (existing)
lyd_free_siblings(existing);

tree = NULL; /* LYD_MERGE_DESTRUCT deleted the tree */
}

ret = NB_OK;

done:
if (err)
ret = NB_ERR;

if (tree)
lyd_free_all(tree);
ly_in_free(in, 0);

return ret;
}

static int nb_candidate_edit_tree_del(struct nb_config *candidate,
enum nb_operation operation,
const char *xpath,
struct lyd_node **errors)
{
struct lyd_node *dnode;

/* deleting root - remove the whole config */
if (xpath[0] == 0 || (xpath[0] == '/' && xpath[1] == 0)) {
lyd_free_all(candidate->dnode);
candidate->dnode = NULL;
return NB_OK;
}

dnode = yang_dnode_get(candidate->dnode, xpath);
if (!dnode || (dnode->flags & LYD_DEFAULT)) {
if (operation == NB_OP_DELETE) {
yang_create_error(errors, "application", "data-missing",
NULL, NULL, "Data missing");
return NB_ERR;
} else
return NB_OK;
}

/* if it's the first top-level node, update candidate */
if (candidate->dnode == dnode)
candidate->dnode = candidate->dnode->next;

lyd_free_tree(dnode);

return NB_OK;
}

int nb_candidate_edit_tree(struct nb_config *candidate,
enum nb_operation operation, LYD_FORMAT format,
const char *xpath, const char *value,
struct lyd_node **errors)
{
int ret = NB_ERR;

switch (operation) {
case NB_OP_CREATE_EXCL:
case NB_OP_CREATE:
case NB_OP_MODIFY:
case NB_OP_REPLACE:
ret = nb_candidate_edit_tree_add(candidate, operation, format,
xpath, value, errors);
break;
case NB_OP_DESTROY:
case NB_OP_DELETE:
ret = nb_candidate_edit_tree_del(candidate, operation, xpath,
errors);
break;
case NB_OP_MOVE:
/* not supported yet */
break;
}

return ret;
}

const char *nb_operation_name(enum nb_operation operation)
{
switch (operation) {
Expand Down
Loading

0 comments on commit 1869663

Please sign in to comment.