diff --git a/lib/mgmt_be_client.c b/lib/mgmt_be_client.c index f03006ad0e..05949fd62d 100644 --- a/lib/mgmt_be_client.c +++ b/lib/mgmt_be_client.c @@ -312,11 +312,11 @@ static int be_client_send_error(struct mgmt_be_client *client, uint64_t txn_id, return ret; } -static int mgmt_be_send_notification(void *__be_client, const char *xpath, - const struct lyd_node *tree) +static int __send_notification(struct mgmt_be_client *client, const char *xpath, + const struct lyd_node *tree, uint8_t op) { - struct mgmt_be_client *client = __be_client; struct mgmt_msg_notify_data *msg = NULL; + // LYD_FORMAT format = LYD_LYB; LYD_FORMAT format = LYD_JSON; uint8_t **darrp; LY_ERR err; @@ -324,37 +324,91 @@ static int mgmt_be_send_notification(void *__be_client, const char *xpath, assert(tree); - debug_be_client("%s: sending YANG notification: %s", __func__, - tree->schema->name); + debug_be_client("%s: sending %sYANG %snotification: %s", __func__, + op == NOTIFY_OP_DS_DELETE ? "delete " + : op == NOTIFY_OP_DS_REPLACE ? "replace " + : op == NOTIFY_OP_DS_PATCH ? "patch " + : "", + op == NOTIFY_OP_NOTIFICATION ? "" : "DS ", xpath ?: tree->schema->name); /* * Allocate a message and append the data to it using `format` */ - msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_notify_data, 0, - MTYPE_MSG_NATIVE_NOTIFY); + msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_notify_data, 0, MTYPE_MSG_NATIVE_NOTIFY); msg->code = MGMT_MSG_CODE_NOTIFY; msg->result_type = format; + msg->op = op; mgmt_msg_native_xpath_encode(msg, xpath); - darrp = mgmt_msg_native_get_darrp(msg); - err = yang_print_tree_append(darrp, tree, format, - (LYD_PRINT_SHRINK | LYD_PRINT_WD_EXPLICIT | - LYD_PRINT_WITHSIBLINGS)); - if (err) { - flog_err(EC_LIB_LIBYANG, - "%s: error creating notification data: %s", __func__, - ly_strerrcode(err)); - ret = 1; - goto done; + if (tree) { + darrp = mgmt_msg_native_get_darrp(msg); + err = yang_print_tree_append(darrp, tree, format, + (LYD_PRINT_SHRINK | LYD_PRINT_WD_EXPLICIT | + LYD_PRINT_WITHSIBLINGS)); + if (err) { + flog_err(EC_LIB_LIBYANG, "%s: error creating notification data: %s", + __func__, ly_strerrcode(err)); + ret = 1; + goto done; + } } - (void)be_client_send_native_msg(client, msg, - mgmt_msg_native_get_msg_len(msg), false); + ret = be_client_send_native_msg(client, msg, mgmt_msg_native_get_msg_len(msg), false); done: mgmt_msg_native_free_msg(msg); return ret; } +/** + * mgmt_be_send_ds_delete_notification() - Send DS notification to mgmtd + */ +int mgmt_be_send_ds_delete_notification(const char *path) +{ + if (!__be_client) { + debug_be_client("%s: No mgmtd connection for DS delete notification: %s", __func__, + path); + return 1; + } + return __send_notification(__be_client, path, NULL, NOTIFY_OP_DS_DELETE); +} + +/** + * mgmt_be_send_ds_patch_notification() - Send a YANG patch DS notification to mgmtd + */ +int mgmt_be_send_ds_patch_notification(const char *path, const struct lyd_node *patch) +{ + if (!__be_client) { + debug_be_client("%s: No mgmtd connection for DS delete notification: %s", __func__, + path); + return 1; + } + return __send_notification(__be_client, path, patch, NOTIFY_OP_DS_PATCH); +} + +/** + * mgmt_be_send_ds_replace_notification() - Send a replace DS notification to mgmtd + */ +int mgmt_be_send_ds_replace_notification(const char *path, const struct lyd_node *tree) +{ + if (!__be_client) { + debug_be_client("%s: No mgmtd connection for DS delete notification: %s", __func__, + path); + return 1; + } + return __send_notification(__be_client, path, tree, NOTIFY_OP_DS_REPLACE); +} + +/** + * mgmt_be_send_notification() - Send notification to mgmtd + * + * This function is attached to the northbound notification hook. + */ +static int mgmt_be_send_notification(void *__client, const char *path, const struct lyd_node *tree) +{ + __send_notification(__client, path, tree, NOTIFY_OP_NOTIFICATION); + return 0; +} + static int mgmt_be_send_txn_reply(struct mgmt_be_client *client_ctx, uint64_t txn_id, bool create) { diff --git a/lib/mgmt_be_client.h b/lib/mgmt_be_client.h index 6ed8c2a39f..a3e3896d52 100644 --- a/lib/mgmt_be_client.h +++ b/lib/mgmt_be_client.h @@ -112,6 +112,22 @@ extern struct mgmt_be_client * mgmt_be_client_create(const char *name, struct mgmt_be_client_cbs *cbs, uintptr_t user_data, struct event_loop *event_loop); + +/** + * mgmt_be_send_ds_delete_notification() - Send a datastore delete notification. + */ +extern int mgmt_be_send_ds_delete_notification(const char *path); + +/** + * mgmt_be_send_ds_patch_notification() - Send a datastore YANG patch notification. + */ +extern int mgmt_be_send_ds_patch_notification(const char *path, const struct lyd_node *tree); + +/** + * mgmt_be_send_ds_replace_notification() - Send a datastore replace notification. + */ +extern int mgmt_be_send_ds_replace_notification(const char *path, const struct lyd_node *tree); + /* * Initialize library vty (adds debug support). * diff --git a/lib/mgmt_msg_native.h b/lib/mgmt_msg_native.h index 587a002801..4076977a22 100644 --- a/lib/mgmt_msg_native.h +++ b/lib/mgmt_msg_native.h @@ -323,22 +323,29 @@ _Static_assert(sizeof(struct mgmt_msg_get_data) == offsetof(struct mgmt_msg_get_data, xpath), "Size mismatch"); + +#define NOTIFY_OP_NOTIFICATION 0 +#define NOTIFY_OP_DS_REPLACE 1 +#define NOTIFY_OP_DS_DELETE 2 +#define NOTIFY_OP_DS_PATCH 3 + /** * struct mgmt_msg_notify_data - Message carrying notification data. * * @result_type: ``LYD_FORMAT`` for format of the @result value. * @data: The xpath string of the notification followed by the tree data in * @result_type format. + * @op: notify operation type. */ struct mgmt_msg_notify_data { struct mgmt_msg_header; uint8_t result_type; - uint8_t resv2[7]; + uint8_t op; + uint8_t resv2[6]; alignas(8) char data[]; }; -_Static_assert(sizeof(struct mgmt_msg_notify_data) == - offsetof(struct mgmt_msg_notify_data, data), +_Static_assert(sizeof(struct mgmt_msg_notify_data) == offsetof(struct mgmt_msg_notify_data, data), "Size mismatch"); #define EDIT_FLAG_IMPLICIT_LOCK 0x01 diff --git a/tests/topotests/lib/fe_client.py b/tests/topotests/lib/fe_client.py index 784f7d17eb..078df8cb33 100755 --- a/tests/topotests/lib/fe_client.py +++ b/tests/topotests/lib/fe_client.py @@ -78,8 +78,13 @@ GET_DATA_FLAG_STATE = 0x1 GET_DATA_FLAG_CONFIG = 0x2 GET_DATA_FLAG_EXACT = 0x4 -MSG_NOTIFY_FMT = "=B7x" +MSG_NOTIFY_FMT = "=BB6x" NOTIFY_FIELD_RESULT_TYPE = 0 +NOTIFY_FIELD_OP = 1 +NOTIFY_OP_NOTIFICATION = 0 +NOTIFY_OP_REPLACE = 1 +NOTIFY_OP_DELETE = 2 +NOTIFY_OP_PATCH = 3 MSG_NOTIFY_SELECT_FMT = "=B7x" @@ -363,10 +368,12 @@ class Session: raise Exception(f"Received NON-NOTIFY Message: {mfixed}: {mdata}") vsplit = mhdr[HDR_FIELD_VSPLIT] + result_type = mfixed[0] + op = mfixed[1] assert mdata[vsplit - 1] == 0 assert mdata[-1] == 0 - # xpath = mdata[: vsplit - 1].decode("utf-8") - return mdata[vsplit:-1].decode("utf-8") + xpath = mdata[: vsplit - 1].decode("utf-8") + return result_type, op, xpath, mdata[vsplit:-1].decode("utf-8") else: raise TimeoutError("Timeout waiting for notifications") @@ -389,6 +396,9 @@ def __parse_args(): parser.add_argument( "-c", "--config-only", action="store_true", help="return config only" ) + parser.add_argument( + "--datastore", action="store_true", help="listen for datastore notifications" + ) parser.add_argument( "-q", "--query", nargs="+", metavar="XPATH", help="xpath[s] to query" ) @@ -434,9 +444,31 @@ def __main(): if args.listen is not None: i = args.notify_count + if args.listen: + sess.add_notify_select(True, args.listen) while i > 0 or args.notify_count == 0: - notif = sess.recv_notify(args.listen) - print(notif) + result_type, op, xpath, notif = sess.recv_notify() + if op == NOTIFY_OP_NOTIFICATION: + if args.datastore: + logging.warning("ignoring non-datastore notification: %s", notif) + else: + print(notif) + elif not args.datastore: + logging.warning( + "ignoring datastore notification op: %s xpath: %s data: %s", + op, + xpath, + notif, + ) + elif op == NOTIFY_OP_PATCH: + print(f"#OP=PATCH: {xpath}") + print(notif) + elif op == NOTIFY_OP_REPLACE: + print(f"#OP=REPLACE: {xpath}") + print(notif) + elif op == NOTIFY_OP_DELETE: + print(f"#OP=DELETE: {xpath}") + assert len(notif) == 0 i -= 1