session UPDATE automatic schema-mount support
diff --git a/src/session_client.c b/src/session_client.c
index fd4c5e3..676df95 100644
--- a/src/session_client.c
+++ b/src/session_client.c
@@ -217,8 +217,32 @@
pthread_setspecific(nc_client_context_key, new);
}
+/**
+ * @brief Ext data callback for a context to provide schema mount data.
+ */
+static LY_ERR
+nc_ly_ext_data_clb(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free)
+{
+ struct nc_session *session = user_data;
+
+ if (strcmp(ext->def->module->name, "ietf-yang-schema-mount") || strcmp(ext->def->name, "mount-point")) {
+ return LY_EINVAL;
+ }
+
+ if (!session->opts.client.ext_data) {
+ ERR(session, "Unable to parse mounted data, no operational schema-mounts data received from the server.");
+ return LY_ENOTFOUND;
+ }
+
+ /* return ext data */
+ *ext_data = session->opts.client.ext_data;
+ *ext_data_free = 0;
+
+ return LY_SUCCESS;
+}
+
int
-nc_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx)
+nc_client_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx)
{
/* assign context (dicionary needed for handshake) */
if (!ctx) {
@@ -236,6 +260,9 @@
/* set callback for getting modules, if provided */
ly_ctx_set_module_imp_clb(ctx, client_opts.schema_clb, client_opts.schema_clb_data);
+
+ /* set ext data callback to avoid errors that no callback is set, the data are stored later, if any */
+ ly_ctx_set_ext_data_clb(ctx, nc_ly_ext_data_clb, session);
} else {
session->flags |= NC_SESSION_SHAREDCTX;
}
@@ -762,48 +789,33 @@
}
/**
- * @brief Build server module info from ietf-yang-library data.
+ * @brief Retrieve yang-library and schema-mounts operational data from the server.
*
* @param[in] session NC session.
* @param[in] has_get_data Whether get-data RPC is available or only get.
- * @param[out] result Server modules.
+ * @param[in] filter Filter to use.
+ * @param[out] oper_data Received data.
* @return 0 on success.
* @return -1 on error.
*/
static int
-build_module_info_yl(struct nc_session *session, int has_get_data, struct module_info **result)
+get_oper_data(struct nc_session *session, int has_get_data, const char *filter, struct lyd_node **oper_data)
{
struct nc_rpc *rpc = NULL;
struct lyd_node *op = NULL, *envp = NULL;
struct lyd_node_any *data;
NC_MSG_TYPE msg;
uint64_t msgid;
- struct ly_set *modules = NULL;
- uint32_t u, v, submodules_count, feature_count;
- struct lyd_node *iter, *child;
- struct lys_module *mod;
int ret = 0;
const char *rpc_name;
- /* get yang-library data from the server */
+ /* get data from the server */
if (has_get_data) {
- rpc_name = "get-data";
- if (nc_session_cpblt(session, "urn:ietf:params:netconf:capability:xpath:1.0")) {
- rpc = nc_rpc_getdata("ietf-datastores:operational", "/ietf-yang-library:*", "false", NULL, 0, 0, 0, 0, 0,
- NC_PARAMTYPE_CONST);
- } else {
- rpc = nc_rpc_getdata("ietf-datastores:operational",
- "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>", "false", NULL, 0, 0, 0, 0,
- 0, NC_PARAMTYPE_CONST);
- }
+ rpc_name = "<get-data>";
+ rpc = nc_rpc_getdata("ietf-datastores:operational", filter, "false", NULL, 0, 0, 0, 0, 0, NC_PARAMTYPE_CONST);
} else {
- rpc_name = "get";
- if (nc_session_cpblt(session, "urn:ietf:params:netconf:capability:xpath:1.0")) {
- rpc = nc_rpc_get("/ietf-yang-library:*", 0, NC_PARAMTYPE_CONST);
- } else {
- rpc = nc_rpc_get("<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>", 0,
- NC_PARAMTYPE_CONST);
- }
+ rpc_name = "<get>";
+ rpc = nc_rpc_get(filter, 0, NC_PARAMTYPE_CONST);
}
if (!rpc) {
goto cleanup;
@@ -813,7 +825,7 @@
usleep(1000);
}
if (msg == NC_MSG_ERROR) {
- WRN(session, "Failed to send request for yang-library data.");
+ WRN(session, "Failed to send %s RPC.", rpc_name);
goto cleanup;
}
@@ -824,27 +836,78 @@
msg = nc_recv_reply(session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, &envp, &op);
} while (msg == NC_MSG_NOTIF || msg == NC_MSG_REPLY_ERR_MSGID);
if (msg == NC_MSG_WOULDBLOCK) {
- WRN(session, "Timeout for receiving reply to a <%s> yang-library data expired.", rpc_name);
+ WRN(session, "Timeout for receiving reply to a %s RPC expired.", rpc_name);
goto cleanup;
} else if (msg == NC_MSG_ERROR) {
- WRN(session, "Failed to receive a reply to <%s> of yang-library data.", rpc_name);
+ WRN(session, "Failed to receive a reply to %s RPC.", rpc_name);
goto cleanup;
} else if (!op || !lyd_child(op) || !lyd_child(op)->schema || strcmp(lyd_child(op)->schema->name, "data")) {
- WRN(session, "Unexpected reply without data to a yang-library <%s> RPC.", rpc_name);
+ WRN(session, "Unexpected reply without data to a %s RPC.", rpc_name);
goto cleanup;
}
data = (struct lyd_node_any *)lyd_child(op);
if (data->value_type != LYD_ANYDATA_DATATREE) {
- WRN(session, "Unexpected data in reply to a yang-library <%s> RPC.", rpc_name);
+ WRN(session, "Unexpected data in reply to a %s RPC.", rpc_name);
goto cleanup;
} else if (!data->value.tree) {
- WRN(session, "No data in reply to a yang-library <%s> RPC.", rpc_name);
+ WRN(session, "No data in reply to a %s RPC.", rpc_name);
goto cleanup;
}
- if (lyd_find_xpath(data->value.tree, "/ietf-yang-library:modules-state/module", &modules)) {
- WRN(session, "No module information in reply to a yang-library <%s> RPC.", rpc_name);
+ *oper_data = data->value.tree;
+ data->value.tree = NULL;
+
+cleanup:
+ nc_rpc_free(rpc);
+ lyd_free_tree(envp);
+ lyd_free_tree(op);
+
+ if (session->status != NC_STATUS_RUNNING) {
+ /* something bad happened, discard the session */
+ ERR(session, "Invalid session, discarding.");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Build server module info from ietf-yang-library data.
+ *
+ * @param[in] session NC session.
+ * @param[in] get_data_sup Whether get-data RPC is available or only get.
+ * @param[in] xpath_sup Whether XPath filter is supported or only subtree filter.
+ * @param[out] result Server modules.
+ * @return 0 on success.
+ * @return -1 on error.
+ */
+static int
+build_module_info_yl(struct nc_session *session, int get_data_sup, int xpath_sup, struct module_info **result)
+{
+ struct ly_set *modules = NULL;
+ uint32_t u, v, submodules_count, feature_count;
+ struct lyd_node *iter, *child, *oper_data = NULL;
+ struct lys_module *mod;
+ int ret = 0;
+
+ /* get yang-library operational data */
+ if (xpath_sup) {
+ if (get_oper_data(session, get_data_sup, "/ietf-yang-library:*", &oper_data)) {
+ goto cleanup;
+ }
+ } else {
+ if (get_oper_data(session, get_data_sup,
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>", &oper_data)) {
+ goto cleanup;
+ }
+ }
+ if (!oper_data) {
+ goto cleanup;
+ }
+
+ if (lyd_find_xpath(oper_data, "/ietf-yang-library:modules-state/module", &modules)) {
+ WRN(NULL, "No yang-library module information found.");
goto cleanup;
}
@@ -920,17 +983,8 @@
}
cleanup:
- nc_rpc_free(rpc);
- lyd_free_tree(envp);
- lyd_free_tree(op);
+ lyd_free_siblings(oper_data);
ly_set_free(modules, NULL);
-
- if (session->status != NC_STATUS_RUNNING) {
- /* something bad happened, discard the session */
- ERR(session, "Invalid session, discarding.");
- ret = -1;
- }
-
return ret;
}
@@ -1115,10 +1169,64 @@
return 0;
}
+/**
+ * @brief Set client session context to support schema-mount if possible.
+ *
+ * @param[in] session NC session with the context to modify.
+ * @param[in] get_data_sup Whether get-data RPC is available or only get.
+ * @param[in] xpath_sup Whether XPath filter is supported or only subtree filter.
+ * @return 0 on success.
+ * @return -1 on error.
+ */
+static int
+nc_ctx_schema_mount(struct nc_session *session, int get_data_sup, int xpath_sup)
+{
+ int rc = 0;
+ struct lyd_node *oper_data = NULL;
+
+ if (session->flags & NC_SESSION_SHAREDCTX) {
+ /* context is already fully set up */
+ goto cleanup;
+ }
+
+ /* get yang-library and schema-mounts operational data */
+ if (xpath_sup) {
+ if ((rc = get_oper_data(session, get_data_sup, "/ietf-yang-library:* | /ietf-yang-schema-mount:*", &oper_data))) {
+ goto cleanup;
+ }
+ } else {
+ if ((rc = get_oper_data(session, get_data_sup,
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>"
+ "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\"/>", &oper_data))) {
+ goto cleanup;
+ }
+ }
+
+ if (!oper_data || lyd_find_path(oper_data, "/ietf-yang-schema-mount:schema-mounts", 0, NULL)) {
+ /* no schema-mounts operational data */
+ goto cleanup;
+ }
+
+ /* validate the data for the parent reference prefixes to be resolved */
+ if (lyd_validate_all(&oper_data, NULL, LYD_VALIDATE_PRESENT, NULL)) {
+ ERR(session, "Invalid operational data received from the server (%s).", ly_errmsg(LYD_CTX(oper_data)));
+ rc = -1;
+ goto cleanup;
+ }
+
+ /* store the data in the session */
+ session->opts.client.ext_data = oper_data;
+ oper_data = NULL;
+
+cleanup:
+ lyd_free_siblings(oper_data);
+ return rc;
+}
+
int
nc_ctx_check_and_fill(struct nc_session *session)
{
- int i, get_schema_support = 0, yanglib_support = 0, get_data_support = 0, ret = -1;
+ int i, get_schema_support = 0, yanglib_support = 0, get_data_support = 0, xpath_support = 0, ret = -1;
ly_module_imp_clb old_clb = NULL;
void *old_data = NULL;
struct lys_module *mod = NULL;
@@ -1141,14 +1249,10 @@
for (i = 0; session->opts.client.cpblts[i]; ++i) {
if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?", 52)) {
get_schema_support = 1 + i;
- if (yanglib_support) {
- break;
- }
} else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:netconf:capability:yang-library:", 48)) {
yanglib_support = 1 + i;
- if (get_schema_support) {
- break;
- }
+ } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:netconf:capability:xpath:1.0", 44)) {
+ xpath_support = 1 + i;
}
}
if (get_schema_support) {
@@ -1219,7 +1323,7 @@
/* prepare structured information about server's modules */
if (yanglib_support) {
- if (build_module_info_yl(session, get_data_support, &sm)) {
+ if (build_module_info_yl(session, get_data_support, xpath_support, &sm)) {
goto cleanup;
} else if (!sm) {
VRB(session, "Trying to use capabilities instead of ietf-yang-library data.");
@@ -1244,6 +1348,11 @@
goto cleanup;
}
+ /* set support for schema-mount, if possible */
+ if (nc_ctx_schema_mount(session, get_data_support, xpath_support)) {
+ goto cleanup;
+ }
+
/* success */
ret = 0;
@@ -1289,7 +1398,7 @@
session->ti.fd.in = fdin;
session->ti.fd.out = fdout;
- if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
+ if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
goto fail;
}
ctx = session->ctx;
@@ -1360,7 +1469,7 @@
session->ti.unixsock.sock = sock;
sock = -1; /* do not close sock in fail label anymore */
- if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
+ if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
goto fail;
}
ctx = session->ctx;