From 3f5976c50654d58424dde2f2cb62962ca9d2d456 Mon Sep 17 00:00:00 2001 From: aPiecek Date: Wed, 27 Mar 2024 15:57:45 +0100 Subject: [PATCH] netopeer FEATURE netconf-confirmed commit --- src/common.c | 86 ++++++++++++ src/common.h | 23 ++++ src/main.c | 2 +- src/netconf_confirmed_commit.c | 24 +++- src/netconf_confirmed_commit.h | 3 +- tests/test_confirmed_commit.c | 243 +++++++++++++++++++++++++++++++++ 6 files changed, 378 insertions(+), 3 deletions(-) diff --git a/src/common.c b/src/common.c index e6d7d64f..6cb161d5 100644 --- a/src/common.c +++ b/src/common.c @@ -525,6 +525,92 @@ 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); + } + + /* get module */ + mod = ly_ctx_get_module_implemented(ly_ctx, "ietf-netconf-notifications"); + if (!mod) { + goto cleanup; + } + + /* create 'netconf-confirmed-commit' notification */ + if (lyd_new_inner(NULL, mod, "netconf-confirmed-commit", 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..93dedc5c 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..cd984a23 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, timeout, 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, timeout, 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..311ba84b 100644 --- a/tests/test_confirmed_commit.c +++ b/tests/test_confirmed_commit.c @@ -33,6 +33,81 @@ #include "np_test.h" #include "np_test_config.h" +#define TCC_NOTIF_XMLNS "\"urn:ietf:params:xml:ns:yang:ietf-netconf-notifications\"" + +#define TCC_RECV_NOTIF_PARAM(nc_sess, timeout_ms, state) \ + do { \ + state->msgtype = nc_recv_notif(nc_sess, timeout_ms, &state->envp, &state->op); \ + } while (state->msgtype == NC_MSG_REPLY); \ + assert_int_equal(NC_MSG_NOTIF, state->msgtype); \ + while (state->op->parent) state->op = lyd_parent(state->op); \ + +#define TCC_RECV_NOTIF(state) \ + TCC_RECV_NOTIF_PARAM(state->nc_sess, 3000, state) + +#define TCC_ASSERT_NOTIF_EVENT(state, event, ssid) \ + { \ + assert_int_equal(lyd_print_mem(&state->str, state->op, LYD_XML, 0), LY_SUCCESS); \ + char *exp_cce = notif_cc_event(event, ssid); \ + assert_non_null(exp_cce); \ + assert_string_equal(exp_cce, state->str); \ + free(exp_cce); \ + free(state->str); \ + state->str = NULL; \ + } + +static char * +notif_cc_event(const char *event, uint32_t ssid) +{ + char *msg = NULL; + + ssid = (ssid == 0) ? 1 : ssid; + + /* Check data without 'timeout' leaf */ + if (!strcmp("timeout", event)) { + asprintf(&msg, + "\n" + " timeout\n" + "\n"); + } else { + asprintf(&msg, + "\n" + " %s\n" + " %" PRIu32 "\n" + " %s\n" + "\n", + np_get_user(), ssid, event); + } + + return msg; +} + +static int +notif_check_cc_timeout(struct np_test *st, uint32_t expected_timeout) +{ + struct lyd_node *timeout_node; + const uint32_t timeout_tolerance = 2; + uint32_t timeout; + + timeout_node = lyd_child(st->op)->prev; + if (!timeout_node || strcmp(timeout_node->schema->name, "timeout")) { + /* timeout node is missing */ + return 2; + } + timeout = ((struct lyd_node_term *)timeout_node)->value.uint32; + + if ((expected_timeout <= timeout_tolerance) || + ((timeout >= (expected_timeout - timeout_tolerance)) && + (timeout <= expected_timeout))) { + /* success, timeout node is checked so it can be removed */ + lyd_free_tree(timeout_node); + return 0; + } else { + /* timeout is out of range */ + return 1; + } +} + static int local_setup(void **state) { @@ -69,6 +144,15 @@ local_setup(void **state) rc = setup_nacm(state); assert_int_equal(rc, 0); + /* Enable replay support for ietf-netconf-notifications */ + assert_int_equal(SR_ERR_OK, sr_set_module_replay_support(st->conn, "ietf-netconf-notifications", 1)); + + /* Subscribe confirm-commit notification */ + SEND_RPC_ESTABSUB(st, "/ietf-netconf-notifications:netconf-confirmed-commit", + "ietf-netconf-notifications", NULL, NULL); + ASSERT_OK_SUB_NTF(st); + FREE_TEST_VARS(st); + return 0; } @@ -137,6 +221,12 @@ test_sameas_commit(void **state) ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + /* Expect 'start' notification */ + TCC_RECV_NOTIF(st); + assert_int_equal(notif_check_cc_timeout(st, 600), 0); + TCC_ASSERT_NOTIF_EVENT(st, "start", 0); + FREE_TEST_VARS(st); + /* Running should now be same as candidate, same as basic commit */ GET_CONFIG_FILTER(st, "/edit1:*"); expected = @@ -156,6 +246,11 @@ test_sameas_commit(void **state) /* Check if received an OK reply */ ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + + /* Expect 'complete' notification */ + TCC_RECV_NOTIF(st); + TCC_ASSERT_NOTIF_EVENT(st, "complete", 0); + FREE_TEST_VARS(st); } static void @@ -185,6 +280,12 @@ test_timeout_runout(void **state) ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + /* Expect 'start' notification with 1s timeout */ + TCC_RECV_NOTIF(st); + assert_int_equal(notif_check_cc_timeout(st, 1), 0); + TCC_ASSERT_NOTIF_EVENT(st, "start", 0); + FREE_TEST_VARS(st); + /* Running should now be same as candidate */ GET_FILTER(st, "/edit1:first"); expected = @@ -201,6 +302,11 @@ test_timeout_runout(void **state) /* wait for the duration of the timeout */ sleep(2); + /* Expect 'timeout' notification */ + TCC_RECV_NOTIF(st); + TCC_ASSERT_NOTIF_EVENT(st, "timeout", 0); + FREE_TEST_VARS(st); + /* Running should have reverted back to it's original value */ ASSERT_EMPTY_CONFIG_FILTER(st, "/edit1:*"); @@ -212,6 +318,9 @@ test_timeout_runout(void **state) /* received an OK reply */ ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + + /* No notification should occur */ + ASSERT_NO_NOTIF(st); } static void @@ -232,6 +341,12 @@ test_timeout_confirm(void **state) ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + /* Expect 'start' notification with 1s timeout*/ + TCC_RECV_NOTIF(st); + assert_int_equal(notif_check_cc_timeout(st, 1), 0); + TCC_ASSERT_NOTIF_EVENT(st, "start", 0); + FREE_TEST_VARS(st); + /* Running should now be same as candidate */ GET_CONFIG_FILTER(st, "/edit1:*"); expected = @@ -252,12 +367,20 @@ test_timeout_confirm(void **state) ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + /* Expect 'complete' notification */ + TCC_RECV_NOTIF(st); + TCC_ASSERT_NOTIF_EVENT(st, "complete", 0); + FREE_TEST_VARS(st); + 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 */ + ASSERT_NO_NOTIF(st); } static void @@ -279,6 +402,12 @@ test_timeout_confirm_modify(void **state) ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + /* Send a confirmed-commit rpc with 1s timeout */ + TCC_RECV_NOTIF(st); + assert_int_equal(notif_check_cc_timeout(st, 1), 0); + TCC_ASSERT_NOTIF_EVENT(st, "start", 0); + FREE_TEST_VARS(st); + /* Running should now be same as candidate */ GET_CONFIG_FILTER(st, "/edit1:*"); expected = @@ -306,6 +435,11 @@ test_timeout_confirm_modify(void **state) sleep(2); + /* Expect 'complete' notification */ + TCC_RECV_NOTIF(st); + TCC_ASSERT_NOTIF_EVENT(st, "complete", 0); + FREE_TEST_VARS(st); + /* Data should change */ GET_CONFIG_FILTER(st, "/edit1:*"); expected = @@ -334,6 +468,12 @@ test_timeout_followup(void **state) ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + /* Expect 'start' notification with 60s timeout */ + TCC_RECV_NOTIF(st); + assert_int_equal(notif_check_cc_timeout(st, 60), 0); + TCC_ASSERT_NOTIF_EVENT(st, "start", 0); + FREE_TEST_VARS(st); + /* modify candidate */ data = "Test2"; SR_EDIT_SESSION(st, st->sr_sess2, data); @@ -346,6 +486,12 @@ test_timeout_followup(void **state) ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + /* Expect 'extend' notification with 1s timeout */ + TCC_RECV_NOTIF(st); + assert_int_equal(notif_check_cc_timeout(st, 1), 0); + TCC_ASSERT_NOTIF_EVENT(st, "extend", 0); + FREE_TEST_VARS(st); + /* running should now be same as candidate */ GET_CONFIG_FILTER(st, "/edit1:*"); expected = @@ -360,6 +506,11 @@ test_timeout_followup(void **state) /* wait for the rollback */ sleep(2); + /* Expect 'timeout' notification */ + TCC_RECV_NOTIF(st); + TCC_ASSERT_NOTIF_EVENT(st, "timeout", 0); + FREE_TEST_VARS(st); + /* data should remain unchanged, empty */ ASSERT_EMPTY_CONFIG_FILTER(st, "/edit1:*"); } @@ -382,6 +533,9 @@ test_cancel(void **state) ASSERT_ERROR_REPLY(st); FREE_TEST_VARS(st); + /* No notification should occur */ + ASSERT_NO_NOTIF(st); + /* edit running */ data = "val5"; SR_EDIT_SESSION(st, st->sr_sess, data); @@ -396,6 +550,12 @@ test_cancel(void **state) ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + /* Expect 'start' notification with 10m timeout */ + TCC_RECV_NOTIF(st); + assert_int_equal(notif_check_cc_timeout(st, 600), 0); + TCC_ASSERT_NOTIF_EVENT(st, "start", 0); + FREE_TEST_VARS(st); + /* running should now be same as candidate */ GET_CONFIG_FILTER(st, "/edit1:*"); expected = @@ -416,6 +576,11 @@ test_cancel(void **state) ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + /* Expect 'cancel' notification */ + TCC_RECV_NOTIF(st); + TCC_ASSERT_NOTIF_EVENT(st, "cancel", 0); + FREE_TEST_VARS(st); + /* running should now be back how it was */ GET_CONFIG_FILTER(st, "/edit1:*"); expected = @@ -438,6 +603,7 @@ test_rollback_disconnect(void **state) struct np_test *st = *state; struct nc_session *ncs; const char *expected; + uint32_t sid; /* prior to the test running should be empty */ ASSERT_EMPTY_CONFIG_FILTER(st, "/edit1:*"); @@ -457,6 +623,12 @@ test_rollback_disconnect(void **state) assert_string_equal(LYD_NAME(lyd_child(st->envp)), "ok"); FREE_TEST_VARS(st); + /* Expect 'start' notification with 60s timeout */ + TCC_RECV_NOTIF(st); + assert_int_equal(notif_check_cc_timeout(st, 60), 0); + TCC_ASSERT_NOTIF_EVENT(st, "start", nc_session_get_id(ncs)); + FREE_TEST_VARS(st); + /* running should now be same as candidate */ GET_CONFIG_FILTER(st, "/edit1:*"); expected = @@ -469,11 +641,17 @@ test_rollback_disconnect(void **state) FREE_TEST_VARS(st); /* disconnect session, commit is rolled back */ + sid = nc_session_get_id(ncs); nc_session_free(ncs, NULL); /* reply is sent before the server callback is called so give it a chance to perform the rollback */ usleep(100000); + /* Expect 'cancel' notification */ + TCC_RECV_NOTIF(st); + TCC_ASSERT_NOTIF_EVENT(st, "cancel", sid); + FREE_TEST_VARS(st); + /* data should remain unchanged, empty */ ASSERT_EMPTY_CONFIG_FILTER(st, "/edit1:*"); } @@ -501,6 +679,12 @@ test_rollback_locked(void **state) ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + /* Expect 'start' notification with 60s timeout */ + TCC_RECV_NOTIF(st); + assert_int_equal(notif_check_cc_timeout(st, 60), 0); + TCC_ASSERT_NOTIF_EVENT(st, "start", 0); + FREE_TEST_VARS(st); + /* running should now be the same as candidate */ GET_CONFIG_FILTER(st, "/edit1:*"); expected = @@ -519,6 +703,9 @@ test_rollback_locked(void **state) ASSERT_ERROR_REPLY_SESS2(st); FREE_TEST_VARS(st); + /* No notification should occur */ + ASSERT_NO_NOTIF(st); + /* 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 +713,11 @@ test_rollback_locked(void **state) ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + /* Expect 'cancel' notification */ + TCC_RECV_NOTIF(st); + TCC_ASSERT_NOTIF_EVENT(st, "cancel", 0); + FREE_TEST_VARS(st); + /* data should remain unchanged, empty */ ASSERT_EMPTY_CONFIG_FILTER(st, "/edit1:*"); @@ -535,6 +727,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 */ + ASSERT_NO_NOTIF(st); } static void @@ -555,6 +750,12 @@ test_confirm_persist(void **state) ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + /* Expect 'start' notification */ + TCC_RECV_NOTIF(st); + assert_int_equal(notif_check_cc_timeout(st, 600), 0); + TCC_ASSERT_NOTIF_EVENT(st, "start", 0); + FREE_TEST_VARS(st); + /* Running should now be same as candidate */ GET_CONFIG_FILTER(st, "/edit1:*"); expected = @@ -575,6 +776,11 @@ test_confirm_persist(void **state) ASSERT_OK_REPLY_SESS2(st); FREE_TEST_VARS(st); + /* Expect 'complete' notification */ + TCC_RECV_NOTIF(st); + TCC_ASSERT_NOTIF_EVENT(st, "complete", 2); + FREE_TEST_VARS(st); + /* Data should remain unchanged */ GET_CONFIG_FILTER(st, "/edit1:*"); assert_string_equal(st->str, expected); @@ -604,6 +810,12 @@ test_cancel_persist(void **state) ASSERT_OK_REPLY_PARAM(nc_sess, 3000, st) FREE_TEST_VARS(st); + /* Expect 'start' notification */ + TCC_RECV_NOTIF(st); + assert_int_equal(notif_check_cc_timeout(st, 600), 0); + TCC_ASSERT_NOTIF_EVENT(st, "start", nc_session_get_id(nc_sess)); + FREE_TEST_VARS(st); + /* running should now be same as candidate */ GET_CONFIG_FILTER(st, "/edit1:*"); expected = @@ -618,6 +830,9 @@ test_cancel_persist(void **state) /* disconnect NC session */ nc_session_free(nc_sess, NULL); + /* No notification should occur */ + ASSERT_NO_NOTIF(st); + /* 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 +842,11 @@ test_cancel_persist(void **state) ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + /* Expect 'cancel' notification */ + TCC_RECV_NOTIF(st); + TCC_ASSERT_NOTIF_EVENT(st, "cancel", 0); + FREE_TEST_VARS(st); + /* running should now be empty */ ASSERT_EMPTY_CONFIG_FILTER(st, "/edit1:*"); } @@ -642,6 +862,10 @@ test_wrong_session(void **state) assert_int_equal(st->msgtype, NC_MSG_RPC); ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + TCC_RECV_NOTIF(st); + assert_int_equal(notif_check_cc_timeout(st, 60), 0); + TCC_ASSERT_NOTIF_EVENT(st, "start", 0); + FREE_TEST_VARS(st); /* 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 +874,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); + ASSERT_NO_NOTIF(st); /* send confirming commit rpc on a different NC session, invalid */ st->rpc = nc_rpc_commit(0, 0, NULL, NULL, NC_PARAMTYPE_CONST); @@ -658,6 +883,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); + ASSERT_NO_NOTIF(st); /* send cancel commit rpc on a different NC session, invalid */ st->rpc = nc_rpc_cancel(NULL, NC_PARAMTYPE_CONST); @@ -666,6 +892,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); + ASSERT_NO_NOTIF(st); /* send running lock rpc on a different NC session, invalid */ st->rpc = nc_rpc_lock(NC_DATASTORE_RUNNING); @@ -674,6 +901,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); + ASSERT_NO_NOTIF(st); /* send cancel-commit rpc */ st->rpc = nc_rpc_cancel(NULL, NC_PARAMTYPE_CONST); @@ -681,6 +909,9 @@ test_wrong_session(void **state) assert_int_equal(st->msgtype, NC_MSG_RPC); ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + TCC_RECV_NOTIF(st); + TCC_ASSERT_NOTIF_EVENT(st, "cancel", 0); + FREE_TEST_VARS(st); } static void @@ -698,6 +929,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); + ASSERT_NO_NOTIF(st); } static int @@ -750,9 +982,20 @@ test_failed_file(void **state) ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); + /* Expect 'start' notification with 1s timeout */ + TCC_RECV_NOTIF(st); + assert_int_equal(notif_check_cc_timeout(st, 1), 0); + TCC_ASSERT_NOTIF_EVENT(st, "start", 0); + FREE_TEST_VARS(st); + /* Wait for the duration of the timeout */ sleep(2); + /* Expect 'timeout' notification */ + TCC_RECV_NOTIF(st); + TCC_ASSERT_NOTIF_EVENT(st, "timeout", 0); + FREE_TEST_VARS(st); + /* Try and find the .failed file, should be exactly one */ dir = opendir(st->path); assert_non_null(dir);