diff --git a/src/common.c b/src/common.c index ceb73979..fe286dd9 100644 --- a/src/common.c +++ b/src/common.c @@ -517,6 +517,88 @@ np_send_notif_session_end(const struct nc_session *session, sr_session_ctx_t *sr return rc; } +int +np_send_notif_confirmed_commit(const struct nc_session *session, sr_session_ctx_t *sr_session, enum np_cc_event event, + uint32_t cc_timeout, uint32_t sr_timeout) +{ + int rc = 0, r; + const struct ly_ctx *ly_ctx; + const struct lys_module *mod; + struct lyd_node *notif = NULL; + char num32[11]; /* max bytes for 32-bit unsigned number + \0 */ + char *value; + + /* get context */ + if (session) { + ly_ctx = nc_session_get_ctx(session); + } else { + assert(event == NP_CC_TIMEOUT); + ly_ctx = sr_session_acquire_context(sr_session); + } + + if ((mod = ly_ctx_get_module_implemented(ly_ctx, "ietf-netconf-notifications"))) { + /* create 'netconf-confirmed-commit' notification */ + if (lyd_new_path(NULL, ly_ctx, "/ietf-netconf-notifications:netconf-confirmed-commit", NULL, 0, ¬if)) { + rc = -1; + goto cleanup; + } + + /* create 'common-session-parms' grouping */ + if ((event != NP_CC_TIMEOUT) && (rc = np_prepare_notif_common_session_parms(session, notif))) { + goto cleanup; + } + + /* create 'confirm-event' node */ + switch (event) { + case NP_CC_START: + value = "start"; + break; + case NP_CC_CANCEL: + value = "cancel"; + break; + case NP_CC_TIMEOUT: + value = "timeout"; + break; + case NP_CC_EXTEND: + value = "extend"; + break; + case NP_CC_COMPLETE: + value = "complete"; + break; + default: + rc = -1; + goto cleanup; + } + if (lyd_new_term(notif, notif->schema->module, "confirm-event", value, 0, NULL)) { + rc = -1; + goto cleanup; + } + + /* create 'timeout' node */ + if (cc_timeout) { + assert((event == NP_CC_START) || (event == NP_CC_EXTEND)); + sprintf(num32, "%" PRIu32, cc_timeout); + if (lyd_new_term(notif, notif->schema->module, "timeout", num32, 0, NULL)) { + rc = -1; + goto cleanup; + } + } + + /* send notification */ + if ((r = sr_notif_send_tree(sr_session, notif, sr_timeout, 0))) { + WRN("Failed to send a notification (%s).", sr_strerror(r)); + rc = -1; + goto cleanup; + } + + VRB("Generated new event (netconf-confirmed-commit)."); + } + +cleanup: + lyd_free_tree(notif); + return rc; +} + const struct ly_ctx * np2srv_acquire_ctx_cb(void *cb_data) { diff --git a/src/common.h b/src/common.h index 62240946..c17aa066 100644 --- a/src/common.h +++ b/src/common.h @@ -246,6 +246,29 @@ int np_send_notif_session_start(const struct nc_session *new_session, sr_session */ int np_send_notif_session_end(const struct nc_session *session, sr_session_ctx_t *sr_session, uint32_t sr_timeout); +enum np_cc_event { + NP_CC_START = 0, /**< The confirmed-commit has started. */ + NP_CC_CANCEL, /**< The confirmed-commit has been canceled, e.g., due to the session being terminated, + or an explicit operation. */ + NP_CC_TIMEOUT, /**< The confirmed-commit has been canceled due to the confirm-timeout interval expiring. */ + NP_CC_EXTEND, /**< The confirmed-commit timeout has been extended, e.g., by a new operation. */ + NP_CC_COMPLETE, /**< The confirmed-commit has been completed. */ +}; + +/** + * @brief Send notification netconf-confirmed-commit. + * + * @param[in] new_session NC session. For :NP_CC_TIMEOUT can be NULL. + * @param[in] sr_session Sysrepo server session. + * @param[in] event Type of confirm-commit event. + * @param[in] cc_timout For event :NP_CC_START or :NP_CC_EXTEND. Number of seconds when the confirmed-commit 'timeout' + * event might occur. + * @param[in] sr_timeout Notification callback timeout in milliseconds. + * @return 0 on success. + */ +int np_send_notif_confirmed_commit(const struct nc_session *session, sr_session_ctx_t *sr_session, + enum np_cc_event event, uint32_t cc_timeout, uint32_t sr_timeout); + /** * @brief NP2 callback for acquiring context. */ diff --git a/src/main.c b/src/main.c index a4a9bbb1..f48d11ca 100644 --- a/src/main.c +++ b/src/main.c @@ -109,7 +109,7 @@ np2srv_del_session_cb(struct nc_session *session) sr_session_unsubscribe(user_sess->sess); /* revert any pending confirmed commits */ - ncc_del_session(session); + ncc_del_session(session, np2srv.sr_sess); /* free sysrepo session, if no callback is using it */ if (ATOMIC_DEC_RELAXED(user_sess->ref_count) == 1) { diff --git a/src/netconf_confirmed_commit.c b/src/netconf_confirmed_commit.c index a2e007b9..72e9cdb7 100644 --- a/src/netconf_confirmed_commit.c +++ b/src/netconf_confirmed_commit.c @@ -510,6 +510,9 @@ ncc_changes_rollback_cb(union sigval sev) if (!sev.sival_int) { /* UNLOCK */ pthread_mutex_unlock(&commit_ctx.lock); + + /* send notification about timeout for confirmed-commits */ + np_send_notif_confirmed_commit(commit_ctx.nc_sess, sr_sess, NP_CC_TIMEOUT, 0, 0); } sr_release_context(np2srv.sr_conn); if (user_sess) { @@ -529,7 +532,7 @@ ncc_changes_rollback_cb(union sigval sev) } void -ncc_del_session(const struct nc_session *nc_sess) +ncc_del_session(const struct nc_session *nc_sess, sr_session_ctx_t *sr_sess) { /* LOCK */ pthread_mutex_lock(&commit_ctx.lock); @@ -538,6 +541,9 @@ ncc_del_session(const struct nc_session *nc_sess) /* rollback */ VRB("Performing confirmed commit rollback after the issuing session has terminated."); ncc_changes_rollback_cb((union sigval)1); + + /* send notification about canceling confirmed-commits */ + np_send_notif_confirmed_commit(nc_sess, sr_sess, NP_CC_CANCEL, 0, 0); } /* UNLOCK */ @@ -828,6 +834,7 @@ np2srv_confirmed_commit_cb(sr_session_ctx_t *session, const struct lyd_node *inp struct lyd_node *node = NULL; char *endptr = NULL; uint32_t timeout; + uint8_t timeout_changed = 0; /* get the user session */ if ((rc = np_find_user_sess(session, __func__, &nc_sess, &user_sess))) { @@ -884,6 +891,10 @@ np2srv_confirmed_commit_cb(sr_session_ctx_t *session, const struct lyd_node *inp /* there is already a pending confirmed commit, keep its backup, but the timeout will be reset */ timer_delete(commit_ctx.timer); commit_ctx.timer = 0; + + /* send notification about extending timeout for confirmed-commits */ + np_send_notif_confirmed_commit(nc_sess, session, NP_CC_EXTEND, 0, 0); + timeout_changed = 1; } /* (re)set the meta file timeout */ @@ -903,6 +914,11 @@ np2srv_confirmed_commit_cb(sr_session_ctx_t *session, const struct lyd_node *inp goto cleanup; } + if (!timeout_changed) { + /* send notification about starting confirmed-commits */ + np_send_notif_confirmed_commit(nc_sess, session, NP_CC_START, 0, 0); + } + /* sysrepo API */ rc = sr_copy_config(user_sess->sess, NULL, SR_DS_CANDIDATE, np2srv.sr_timeout); if (rc == SR_ERR_LOCKED) { @@ -971,6 +987,9 @@ np2srv_rpc_commit_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const c goto cleanup; } ncc_commit_confirmed(); + + /* send notification about complete confirmed-commits */ + np_send_notif_confirmed_commit(nc_sess, session, NP_CC_COMPLETE, 0, 0); } /* sysrepo API */ @@ -1065,6 +1084,9 @@ np2srv_rpc_cancel_commit_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), VRB("Performing confirmed commit rollback after receiving ."); ncc_changes_rollback_cb((union sigval)1); + /* send notification about canceling confirmed-commits */ + np_send_notif_confirmed_commit(nc_sess, session, NP_CC_CANCEL, 0, 0); + cleanup: /* UNLOCK */ pthread_mutex_unlock(&commit_ctx.lock); diff --git a/src/netconf_confirmed_commit.h b/src/netconf_confirmed_commit.h index 0ab36d02..4652a9f2 100644 --- a/src/netconf_confirmed_commit.h +++ b/src/netconf_confirmed_commit.h @@ -47,8 +47,9 @@ void ncc_try_restore(void); * @brief Revert current confirmed commit, if any, if not persistent and this session started it. * * @param[in] nc_sess Terminated NC session. + * @param[in] sr_sess Sysrepo server session. */ -void ncc_del_session(const struct nc_session *nc_sess); +void ncc_del_session(const struct nc_session *nc_sess, sr_session_ctx_t *sr_sess); int np2srv_rpc_commit_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *op_path, const struct lyd_node *input, sr_event_t event, uint32_t request_id, struct lyd_node *output, void *private_data); diff --git a/tests/test_confirmed_commit.c b/tests/test_confirmed_commit.c index d379eef0..8cf1ad4a 100644 --- a/tests/test_confirmed_commit.c +++ b/tests/test_confirmed_commit.c @@ -33,6 +33,74 @@ #include "np_test.h" #include "np_test_config.h" +#define TCC_NOTIF_MOD "ietf-netconf-notifications" +#define TCC_NOTIF_XPATH "/ietf-netconf-notifications:netconf-confirmed-commit" +#define TCC_ASSERT_NOTIF_EVENT(EXPECTED_EVENT, NOTIF_EVENT) \ + assert_int_equal(EXPECTED_EVENT, NOTIF_EVENT); \ + NOTIF_EVENT = TEST_CC_NOTIF_EMPTY; + +enum tcc_notif_event { + TEST_CC_NOTIF_EMPTY = 0, + TEST_CC_NOTIF_ERROR, + TEST_CC_NOTIF_START, + TEST_CC_NOTIF_CANCEL, + TEST_CC_NOTIF_TIMEOUT, + TEST_CC_NOTIF_EXTEND, + TEST_CC_NOTIF_COMPLETE, +}; + +static void +notif_cc_cb(sr_session_ctx_t *session, uint32_t sub_id, const sr_ev_notif_type_t notif_type, + const struct lyd_node *notif, struct timespec *timestamp, void *private_data) +{ + (void)session; + (void)sub_id; + (void)notif_type; + (void)timestamp; + (void)private_data; + + struct lyd_node *node; + enum tcc_notif_event *event; + const char *value; + + event = (enum tcc_notif_event *)private_data; + + if (notif_type == SR_EV_NOTIF_TERMINATED) { + /* Just ignore this. */ + return; + } else if (notif_type != SR_EV_NOTIF_REALTIME) { + /* Other types of notification are not expected. */ + *event = TEST_CC_NOTIF_ERROR; + return; + } else if (*event != TEST_CC_NOTIF_EMPTY) { + /* The notification was not caught in the test. */ + *event = TEST_CC_NOTIF_ERROR; + return; + } + + if (!lyd_find_path(notif, "confirm-event", 0, &node)) { + value = lyd_get_value(node); + + /* Set the corresponding value for confirm-event. */ + if (!strcmp("start", value)) { + *event = TEST_CC_NOTIF_START; + } else if (!strcmp("cancel", value)) { + *event = TEST_CC_NOTIF_CANCEL; + } else if (!strcmp("timeout", value)) { + *event = TEST_CC_NOTIF_TIMEOUT; + } else if (!strcmp("extend", value)) { + *event = TEST_CC_NOTIF_EXTEND; + } else if (!strcmp("complete", value)) { + *event = TEST_CC_NOTIF_COMPLETE; + } else { + *event = TEST_CC_NOTIF_ERROR; + } + } else { + /* The notification has bad data nodes. */ + *event = TEST_CC_NOTIF_ERROR; + } +} + static int local_setup(void **state) { @@ -100,6 +168,8 @@ setup_common(void **state) SR_EDIT_SESSION(st, st->sr_sess2, data); FREE_TEST_VARS(st); + st->sub = NULL; + return 0; } @@ -116,6 +186,10 @@ teardown_common(void **state) SR_EDIT_SESSION(st, st->sr_sess2, data); FREE_TEST_VARS(st); + if (st->sub) { + sr_unsubscribe(st->sub); + } + return 0; } @@ -124,6 +198,11 @@ test_sameas_commit(void **state) { struct np_test *st = *state; const char *expected; + enum tcc_notif_event notif_event = TEST_CC_NOTIF_EMPTY; + + /* Subscribe confirm-commit notification */ + assert_int_equal(sr_notif_subscribe_tree(st->sr_sess, TCC_NOTIF_MOD, TCC_NOTIF_XPATH, + 0, 0, notif_cc_cb, ¬if_event, 0, &st->sub), SR_ERR_OK); /* Prior to the test running of edit1 should be empty */ ASSERT_EMPTY_CONFIG_FILTER(st, "/edit1:*") @@ -137,6 +216,9 @@ test_sameas_commit(void **state) ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + /* Expect 'start' notification */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_START, notif_event); + /* Running should now be same as candidate, same as basic commit */ GET_CONFIG_FILTER(st, "/edit1:*"); expected = @@ -156,6 +238,9 @@ test_sameas_commit(void **state) /* Check if received an OK reply */ ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + + /* Expect 'complete' notification */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_COMPLETE, notif_event); } static void @@ -163,6 +248,11 @@ test_timeout_runout(void **state) { struct np_test *st = *state; const char *expected; + enum tcc_notif_event notif_event = TEST_CC_NOTIF_EMPTY; + + /* Subscribe confirm-commit notification */ + assert_int_equal(sr_notif_subscribe_tree(st->sr_sess, TCC_NOTIF_MOD, TCC_NOTIF_XPATH, + 0, 0, notif_cc_cb, ¬if_event, 0, &st->sub), SR_ERR_OK); /* Prior to the test running of edit1 should be empty */ ASSERT_EMPTY_CONFIG_FILTER(st, "/edit1:*") @@ -185,6 +275,9 @@ test_timeout_runout(void **state) ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + /* Expect 'start' notification */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_START, notif_event); + /* Running should now be same as candidate */ GET_FILTER(st, "/edit1:first"); expected = @@ -201,6 +294,9 @@ test_timeout_runout(void **state) /* wait for the duration of the timeout */ sleep(2); + /* Expect 'timeout' notification */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_TIMEOUT, notif_event); + /* Running should have reverted back to it's original value */ ASSERT_EMPTY_CONFIG_FILTER(st, "/edit1:*"); @@ -212,6 +308,9 @@ test_timeout_runout(void **state) /* received an OK reply */ ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + + /* No notification should occur */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_EMPTY, notif_event); } static void @@ -219,6 +318,11 @@ test_timeout_confirm(void **state) { struct np_test *st = *state; const char *expected; + enum tcc_notif_event notif_event = TEST_CC_NOTIF_EMPTY; + + /* Subscribe confirm-commit notification */ + assert_int_equal(sr_notif_subscribe_tree(st->sr_sess, TCC_NOTIF_MOD, TCC_NOTIF_XPATH, + 0, 0, notif_cc_cb, ¬if_event, 0, &st->sub), SR_ERR_OK); /* Prior to the test running should be empty */ ASSERT_EMPTY_CONFIG_FILTER(st, "/edit1:*"); @@ -232,6 +336,9 @@ test_timeout_confirm(void **state) ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + /* Expect 'start' notification */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_START, notif_event); + /* Running should now be same as candidate */ GET_CONFIG_FILTER(st, "/edit1:*"); expected = @@ -252,12 +359,18 @@ test_timeout_confirm(void **state) ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + /* Expect 'complete' notification */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_COMPLETE, notif_event); + sleep(2); /* Data should remain unchanged */ GET_CONFIG_FILTER(st, "/edit1:*"); assert_string_equal(st->str, expected); FREE_TEST_VARS(st); + + /* No notification should occur */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_EMPTY, notif_event); } static void @@ -266,6 +379,11 @@ test_timeout_confirm_modify(void **state) struct np_test *st = *state; const char *expected; const char *data; + enum tcc_notif_event notif_event = TEST_CC_NOTIF_EMPTY; + + /* Subscribe confirm-commit notification */ + assert_int_equal(sr_notif_subscribe_tree(st->sr_sess, TCC_NOTIF_MOD, TCC_NOTIF_XPATH, + 0, 0, notif_cc_cb, ¬if_event, 0, &st->sub), SR_ERR_OK); /* Prior to the test running should be empty */ ASSERT_EMPTY_CONFIG_FILTER(st, "/edit1:*"); @@ -279,6 +397,9 @@ test_timeout_confirm_modify(void **state) ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + /* Expect 'start' notification */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_START, notif_event); + /* Running should now be same as candidate */ GET_CONFIG_FILTER(st, "/edit1:*"); expected = @@ -304,6 +425,9 @@ test_timeout_confirm_modify(void **state) ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + /* Expect 'complete' notification */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_COMPLETE, notif_event); + sleep(2); /* Data should change */ @@ -316,6 +440,9 @@ test_timeout_confirm_modify(void **state) "\n"; assert_string_equal(st->str, expected); FREE_TEST_VARS(st); + + /* No notification should occur */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_EMPTY, notif_event); } static void @@ -323,6 +450,11 @@ test_timeout_followup(void **state) { struct np_test *st = *state; const char *data, *expected; + enum tcc_notif_event notif_event = TEST_CC_NOTIF_EMPTY; + + /* Subscribe confirm-commit notification */ + assert_int_equal(sr_notif_subscribe_tree(st->sr_sess, TCC_NOTIF_MOD, TCC_NOTIF_XPATH, + 0, 0, notif_cc_cb, ¬if_event, 0, &st->sub), SR_ERR_OK); /* prior to the test running should be empty */ ASSERT_EMPTY_CONFIG_FILTER(st, "/edit1:*"); @@ -334,6 +466,9 @@ test_timeout_followup(void **state) ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + /* Expect 'start' notification */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_START, notif_event); + /* modify candidate */ data = "Test2"; SR_EDIT_SESSION(st, st->sr_sess2, data); @@ -346,6 +481,9 @@ test_timeout_followup(void **state) ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + /* Expect 'extend' notification */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_EXTEND, notif_event); + /* running should now be same as candidate */ GET_CONFIG_FILTER(st, "/edit1:*"); expected = @@ -360,6 +498,9 @@ test_timeout_followup(void **state) /* wait for the rollback */ sleep(2); + /* Expect 'timeout' notification */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_TIMEOUT, notif_event); + /* data should remain unchanged, empty */ ASSERT_EMPTY_CONFIG_FILTER(st, "/edit1:*"); } @@ -369,6 +510,11 @@ test_cancel(void **state) { struct np_test *st = *state; const char *expected, *data; + enum tcc_notif_event notif_event = TEST_CC_NOTIF_EMPTY; + + /* Subscribe confirm-commit notification */ + assert_int_equal(sr_notif_subscribe_tree(st->sr_sess, TCC_NOTIF_MOD, TCC_NOTIF_XPATH, + 0, 0, notif_cc_cb, ¬if_event, 0, &st->sub), SR_ERR_OK); /* prior to the test running should be empty */ ASSERT_EMPTY_CONFIG_FILTER(st, "/edit1:*"); @@ -382,6 +528,9 @@ test_cancel(void **state) ASSERT_ERROR_REPLY(st); FREE_TEST_VARS(st); + /* No notification should occur */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_EMPTY, notif_event); + /* edit running */ data = "val5"; SR_EDIT_SESSION(st, st->sr_sess, data); @@ -396,6 +545,9 @@ test_cancel(void **state) ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + /* Expect 'start' notification */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_START, notif_event); + /* running should now be same as candidate */ GET_CONFIG_FILTER(st, "/edit1:*"); expected = @@ -416,6 +568,9 @@ test_cancel(void **state) ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + /* Expect 'cancel' notification */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_CANCEL, notif_event); + /* running should now be back how it was */ GET_CONFIG_FILTER(st, "/edit1:*"); expected = @@ -438,6 +593,11 @@ test_rollback_disconnect(void **state) struct np_test *st = *state; struct nc_session *ncs; const char *expected; + enum tcc_notif_event notif_event = TEST_CC_NOTIF_EMPTY; + + /* Subscribe confirm-commit notification */ + assert_int_equal(sr_notif_subscribe_tree(st->sr_sess, TCC_NOTIF_MOD, TCC_NOTIF_XPATH, + 0, 0, notif_cc_cb, ¬if_event, 0, &st->sub), SR_ERR_OK); /* prior to the test running should be empty */ ASSERT_EMPTY_CONFIG_FILTER(st, "/edit1:*"); @@ -457,6 +617,9 @@ test_rollback_disconnect(void **state) assert_string_equal(LYD_NAME(lyd_child(st->envp)), "ok"); FREE_TEST_VARS(st); + /* Expect 'start' notification */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_START, notif_event); + /* running should now be same as candidate */ GET_CONFIG_FILTER(st, "/edit1:*"); expected = @@ -474,6 +637,9 @@ test_rollback_disconnect(void **state) /* reply is sent before the server callback is called so give it a chance to perform the rollback */ usleep(100000); + /* Expect 'cancel' notification */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_CANCEL, notif_event); + /* data should remain unchanged, empty */ ASSERT_EMPTY_CONFIG_FILTER(st, "/edit1:*"); } @@ -483,6 +649,11 @@ test_rollback_locked(void **state) { struct np_test *st = *state; const char *expected; + enum tcc_notif_event notif_event = TEST_CC_NOTIF_EMPTY; + + /* Subscribe confirm-commit notification */ + assert_int_equal(sr_notif_subscribe_tree(st->sr_sess, TCC_NOTIF_MOD, TCC_NOTIF_XPATH, + 0, 0, notif_cc_cb, ¬if_event, 0, &st->sub), SR_ERR_OK); /* prior to the test running should be empty */ ASSERT_EMPTY_CONFIG_FILTER(st, "/edit1:*"); @@ -501,6 +672,9 @@ test_rollback_locked(void **state) ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + /* Expect 'start' notification */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_START, notif_event); + /* running should now be the same as candidate */ GET_CONFIG_FILTER(st, "/edit1:*"); expected = @@ -519,6 +693,9 @@ test_rollback_locked(void **state) ASSERT_ERROR_REPLY_SESS2(st); FREE_TEST_VARS(st); + /* No notification should occur */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_EMPTY, notif_event); + /* cancel-commit on the same session */ st->rpc = nc_rpc_cancel("test-persist", NC_PARAMTYPE_CONST); st->msgtype = nc_send_rpc(st->nc_sess, st->rpc, 1000, &st->msgid); @@ -526,6 +703,9 @@ test_rollback_locked(void **state) ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + /* Expect 'cancel' notification */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_CANCEL, notif_event); + /* data should remain unchanged, empty */ ASSERT_EMPTY_CONFIG_FILTER(st, "/edit1:*"); @@ -535,6 +715,9 @@ test_rollback_locked(void **state) assert_int_equal(st->msgtype, NC_MSG_RPC); ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + + /* No notification should occur */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_EMPTY, notif_event); } static void @@ -542,6 +725,11 @@ test_confirm_persist(void **state) { struct np_test *st = *state; const char *expected, *persist = "test-persist-1"; + enum tcc_notif_event notif_event = TEST_CC_NOTIF_EMPTY; + + /* Subscribe confirm-commit notification */ + assert_int_equal(sr_notif_subscribe_tree(st->sr_sess, TCC_NOTIF_MOD, TCC_NOTIF_XPATH, + 0, 0, notif_cc_cb, ¬if_event, 0, &st->sub), SR_ERR_OK); /* Prior to the test running should be empty */ ASSERT_EMPTY_CONFIG_FILTER(st, "/edit1:*"); @@ -555,6 +743,9 @@ test_confirm_persist(void **state) ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + /* Expect 'start' notification */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_START, notif_event); + /* Running should now be same as candidate */ GET_CONFIG_FILTER(st, "/edit1:*"); expected = @@ -575,6 +766,9 @@ test_confirm_persist(void **state) ASSERT_OK_REPLY_SESS2(st); FREE_TEST_VARS(st); + /* Expect 'complete' notification */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_COMPLETE, notif_event); + /* Data should remain unchanged */ GET_CONFIG_FILTER(st, "/edit1:*"); assert_string_equal(st->str, expected); @@ -587,6 +781,11 @@ test_cancel_persist(void **state) struct np_test *st = *state; const char *expected, *persist = "test-persist-2"; struct nc_session *nc_sess; + enum tcc_notif_event notif_event = TEST_CC_NOTIF_EMPTY; + + /* Subscribe confirm-commit notification */ + assert_int_equal(sr_notif_subscribe_tree(st->sr_sess, TCC_NOTIF_MOD, TCC_NOTIF_XPATH, + 0, 0, notif_cc_cb, ¬if_event, 0, &st->sub), SR_ERR_OK); /* prior to the test running should be empty */ ASSERT_EMPTY_CONFIG_FILTER(st, "/edit1:*"); @@ -604,6 +803,9 @@ test_cancel_persist(void **state) ASSERT_OK_REPLY_PARAM(nc_sess, 3000, st) FREE_TEST_VARS(st); + /* Expect 'start' notification */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_START, notif_event); + /* running should now be same as candidate */ GET_CONFIG_FILTER(st, "/edit1:*"); expected = @@ -618,6 +820,9 @@ test_cancel_persist(void **state) /* disconnect NC session */ nc_session_free(nc_sess, NULL); + /* No notification should occur */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_EMPTY, notif_event); + /* send cancel-commit rpc on a different session */ st->rpc = nc_rpc_cancel(persist, NC_PARAMTYPE_CONST); st->msgtype = nc_send_rpc(st->nc_sess, st->rpc, 1000, &st->msgid); @@ -627,6 +832,9 @@ test_cancel_persist(void **state) ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + /* Expect 'cancel' notification */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_CANCEL, notif_event); + /* running should now be empty */ ASSERT_EMPTY_CONFIG_FILTER(st, "/edit1:*"); } @@ -635,6 +843,11 @@ static void test_wrong_session(void **state) { struct np_test *st = *state; + enum tcc_notif_event notif_event = TEST_CC_NOTIF_EMPTY; + + /* Subscribe confirm-commit notification */ + assert_int_equal(sr_notif_subscribe_tree(st->sr_sess, TCC_NOTIF_MOD, TCC_NOTIF_XPATH, + 0, 0, notif_cc_cb, ¬if_event, 0, &st->sub), SR_ERR_OK); /* send a confirmed-commit rpc with 60s timeout */ st->rpc = nc_rpc_commit(1, 60, NULL, NULL, NC_PARAMTYPE_CONST); @@ -642,6 +855,7 @@ test_wrong_session(void **state) assert_int_equal(st->msgtype, NC_MSG_RPC); ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_START, notif_event); /* send another confirmed-commit rpc on a different NC session, invalid */ st->rpc = nc_rpc_commit(1, 1, NULL, NULL, NC_PARAMTYPE_CONST); @@ -650,6 +864,7 @@ test_wrong_session(void **state) ASSERT_ERROR_REPLY_SESS2(st); assert_string_equal(lyd_get_value(lyd_child(lyd_child(st->envp))->next), "operation-failed"); FREE_TEST_VARS(st); + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_EMPTY, notif_event); /* send confirming commit rpc on a different NC session, invalid */ st->rpc = nc_rpc_commit(0, 0, NULL, NULL, NC_PARAMTYPE_CONST); @@ -658,6 +873,7 @@ test_wrong_session(void **state) ASSERT_ERROR_REPLY_SESS2(st); assert_string_equal(lyd_get_value(lyd_child(lyd_child(st->envp))->next), "operation-failed"); FREE_TEST_VARS(st); + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_EMPTY, notif_event); /* send cancel commit rpc on a different NC session, invalid */ st->rpc = nc_rpc_cancel(NULL, NC_PARAMTYPE_CONST); @@ -666,6 +882,7 @@ test_wrong_session(void **state) ASSERT_ERROR_REPLY_SESS2(st); assert_string_equal(lyd_get_value(lyd_child(lyd_child(st->envp))->next), "operation-failed"); FREE_TEST_VARS(st); + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_EMPTY, notif_event); /* send running lock rpc on a different NC session, invalid */ st->rpc = nc_rpc_lock(NC_DATASTORE_RUNNING); @@ -674,6 +891,7 @@ test_wrong_session(void **state) ASSERT_ERROR_REPLY_SESS2(st); assert_string_equal(lyd_get_value(lyd_child(lyd_child(st->envp))->next), "lock-denied"); FREE_TEST_VARS(st); + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_EMPTY, notif_event); /* send cancel-commit rpc */ st->rpc = nc_rpc_cancel(NULL, NC_PARAMTYPE_CONST); @@ -681,6 +899,7 @@ test_wrong_session(void **state) assert_int_equal(st->msgtype, NC_MSG_RPC); ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_CANCEL, notif_event); } static void @@ -688,6 +907,11 @@ test_wrong_persist_id(void **state) { struct np_test *st = *state; const char *persist = "test-persist-3"; + enum tcc_notif_event notif_event = TEST_CC_NOTIF_EMPTY; + + /* Subscribe confirm-commit notification */ + assert_int_equal(sr_notif_subscribe_tree(st->sr_sess, TCC_NOTIF_MOD, TCC_NOTIF_XPATH, + 0, 0, notif_cc_cb, ¬if_event, 0, &st->sub), SR_ERR_OK); /* Send a confirmed-commit rpc with unknown persist-id */ st->rpc = nc_rpc_commit(0, 0, NULL, persist, NC_PARAMTYPE_CONST); @@ -698,6 +922,7 @@ test_wrong_persist_id(void **state) ASSERT_ERROR_REPLY(st); assert_string_equal(lyd_get_value(lyd_child(lyd_child(st->envp))->next), "invalid-value"); FREE_TEST_VARS(st); + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_EMPTY, notif_event); } static int @@ -727,6 +952,9 @@ setup_test_failed_file(void **state) free(file_name); free(test_name); fclose(file); + + st->sub = NULL; + return 0; } @@ -737,6 +965,11 @@ test_failed_file(void **state) struct dirent *file = NULL; int found = 0; DIR *dir = NULL; + enum tcc_notif_event notif_event = TEST_CC_NOTIF_EMPTY; + + /* Subscribe confirm-commit notification */ + assert_int_equal(sr_notif_subscribe_tree(st->sr_sess, TCC_NOTIF_MOD, TCC_NOTIF_XPATH, + 0, 0, notif_cc_cb, ¬if_event, 0, &st->sub), SR_ERR_OK); /* Prior to the test running should be empty */ ASSERT_EMPTY_CONFIG_FILTER(st, "/edit1:*"); @@ -750,9 +983,15 @@ test_failed_file(void **state) ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + /* Expect 'start' notification */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_START, notif_event); + /* Wait for the duration of the timeout */ sleep(2); + /* Expect 'timeout' notification */ + TCC_ASSERT_NOTIF_EVENT(TEST_CC_NOTIF_TIMEOUT, notif_event); + /* Try and find the .failed file, should be exactly one */ dir = opendir(st->path); assert_non_null(dir);