diff options
| -rw-r--r-- | lib/mgmt_fe_client.c | 121 | ||||
| -rw-r--r-- | lib/mgmt_fe_client.h | 50 | ||||
| -rw-r--r-- | lib/vty.c | 334 | ||||
| -rw-r--r-- | lib/vty.h | 5 | ||||
| -rw-r--r-- | mgmtd/mgmt_history.c | 4 | ||||
| -rw-r--r-- | mgmtd/mgmt_vty.c | 30 |
6 files changed, 527 insertions, 17 deletions
diff --git a/lib/mgmt_fe_client.c b/lib/mgmt_fe_client.c index 4c6f86b194..f6ae485c5c 100644 --- a/lib/mgmt_fe_client.c +++ b/lib/mgmt_fe_client.c @@ -12,6 +12,7 @@ #include "libfrr.h" #include "mgmt_fe_client.h" #include "mgmt_msg.h" +#include "mgmt_msg_native.h" #include "mgmt_pb.h" #include "network.h" #include "stream.h" @@ -34,6 +35,7 @@ DECLARE_LIST(mgmt_sessions, struct mgmt_fe_client_session, list_linkage); DEFINE_MTYPE_STATIC(LIB, MGMTD_FE_CLIENT, "frontend client"); DEFINE_MTYPE_STATIC(LIB, MGMTD_FE_CLIENT_NAME, "frontend client name"); +DEFINE_MTYPE_STATIC(LIB, MGMTD_FE_GET_DATA_MSG, "FE get data msg"); DEFINE_MTYPE_STATIC(LIB, MGMTD_FE_SESSION, "frontend session"); struct mgmt_fe_client { @@ -109,6 +111,13 @@ mgmt_fe_find_session_by_session_id(struct mgmt_fe_client *client, return NULL; } +static int fe_client_send_native_msg(struct mgmt_fe_client *client, void *msg, + size_t len, bool short_circuit_ok) +{ + return msg_conn_send_msg(&client->client.conn, MGMT_MSG_VERSION_NATIVE, + msg, len, NULL, short_circuit_ok); +} + static int mgmt_fe_client_send_msg(struct mgmt_fe_client *client, Mgmtd__FeMessage *fe_msg, bool short_circuit_ok) @@ -304,6 +313,35 @@ int mgmt_fe_send_regnotify_req(struct mgmt_fe_client *client, return mgmt_fe_client_send_msg(client, &fe_msg, false); } +/* + * Send get-tree request. + */ +int mgmt_fe_send_get_tree_req(struct mgmt_fe_client *client, + uint64_t session_id, uint64_t req_id, + LYD_FORMAT result_type, const char *xpath) +{ + struct mgmt_msg_get_tree *msg; + size_t xplen = strlen(xpath); + size_t mlen = sizeof(*msg) + xplen + 1; + int ret; + + msg = XCALLOC(MTYPE_MGMTD_FE_GET_DATA_MSG, mlen); + msg->session_id = session_id; + msg->req_id = req_id; + msg->code = MGMT_MSG_CODE_GET_TREE; + msg->result_type = result_type; + strlcpy(msg->xpath, xpath, xplen + 1); + + MGMTD_FE_CLIENT_DBG("Sending GET_TREE_REQ session-id %" PRIu64 + " req-id %" PRIu64 " xpath: %s", + session_id, req_id, xpath); + + ret = fe_client_send_native_msg(client, msg, mlen, false); + XFREE(MTYPE_MGMTD_FE_GET_DATA_MSG, msg); + return ret; +} + + static int mgmt_fe_client_handle_msg(struct mgmt_fe_client *client, Mgmtd__FeMessage *fe_msg) { @@ -469,6 +507,73 @@ static int mgmt_fe_client_handle_msg(struct mgmt_fe_client *client, return 0; } +/* + * Handle a native encoded message + */ +static void fe_client_handle_native_msg(struct mgmt_fe_client *client, + struct mgmt_msg_header *msg, + size_t msg_len) +{ + struct mgmt_fe_client_session *session; + struct mgmt_msg_tree_data *tree_msg; + struct mgmt_msg_error *err_msg; + + MGMTD_FE_CLIENT_DBG("Got GET_TREE reply for session-id %" PRIu64, + msg->session_id); + + session = mgmt_fe_find_session_by_session_id(client, msg->session_id); + + if (!session || !session->client) { + MGMTD_FE_CLIENT_ERR("No session for received native msg session-id %" PRIu64, + msg->session_id); + return; + } + + switch (msg->code) { + case MGMT_MSG_CODE_ERROR: + if (!session->client->cbs.error_notify) + return; + + err_msg = (typeof(err_msg))msg; + if (!MGMT_MSG_VALIDATE_NUL_TERM(err_msg, msg_len)) { + MGMTD_FE_CLIENT_ERR("Corrupt error msg recv"); + return; + } + session->client->cbs.error_notify(client, client->user_data, + session->client_id, + msg->session_id, + session->user_ctx, + msg->req_id, err_msg->error, + err_msg->errstr); + break; + case MGMT_MSG_CODE_TREE_DATA: + if (!session->client->cbs.get_tree_notify) + return; + + tree_msg = (typeof(tree_msg))msg; + if (msg_len < sizeof(*tree_msg)) { + MGMTD_FE_CLIENT_ERR("Corrupt tree-data msg recv"); + return; + } + session->client->cbs.get_tree_notify(client, client->user_data, + session->client_id, + msg->session_id, + session->user_ctx, + msg->req_id, + MGMTD_DS_OPERATIONAL, + tree_msg->result_type, + tree_msg->result, + msg_len - sizeof(*tree_msg), + tree_msg->partial_error); + break; + default: + MGMTD_FE_CLIENT_ERR("unknown native message session-id %" PRIu64 + " req-id %" PRIu64 " code %u", + msg->session_id, msg->req_id, msg->code); + break; + } +} + static void mgmt_fe_client_process_msg(uint8_t version, uint8_t *data, size_t len, struct msg_conn *conn) { @@ -479,6 +584,17 @@ static void mgmt_fe_client_process_msg(uint8_t version, uint8_t *data, msg_client = container_of(conn, struct msg_client, conn); client = container_of(msg_client, struct mgmt_fe_client, client); + if (version == MGMT_MSG_VERSION_NATIVE) { + struct mgmt_msg_header *msg = (typeof(msg))data; + + if (len >= sizeof(*msg)) + fe_client_handle_native_msg(client, msg, len); + else + MGMTD_FE_CLIENT_ERR("native message to FE client %s too short %zu", + client->name, len); + return; + } + fe_msg = mgmtd__fe_message__unpack(NULL, len, data); if (!fe_msg) { MGMTD_FE_CLIENT_DBG("Failed to decode %zu bytes from server.", @@ -647,6 +763,11 @@ bool mgmt_fe_client_current_msg_short_circuit(struct mgmt_fe_client *client) return client->client.conn.is_short_circuit; } +const char *mgmt_fe_client_name(struct mgmt_fe_client *client) +{ + return client->name; +} + /* * Create a new Session for a Frontend Client connection. */ diff --git a/lib/mgmt_fe_client.h b/lib/mgmt_fe_client.h index d770748f23..32d2920ff9 100644 --- a/lib/mgmt_fe_client.h +++ b/lib/mgmt_fe_client.h @@ -115,6 +115,20 @@ struct mgmt_fe_client_cbs { uintptr_t user_data, uint64_t req_id, Mgmtd__DatastoreId ds_id, Mgmtd__YangData **yang_data, size_t num_data); + + /* Called when get-tree result is returned */ + int (*get_tree_notify)(struct mgmt_fe_client *client, + uintptr_t user_data, uint64_t client_id, + uint64_t session_id, uint64_t session_ctx, + uint64_t req_id, Mgmtd__DatastoreId ds_id, + LYD_FORMAT result_type, void *result, size_t len, + int partial_error); + + /* Called when new native error is returned */ + int (*error_notify)(struct mgmt_fe_client *client, uintptr_t user_data, + uint64_t client_id, uint64_t session_id, + uintptr_t session_ctx, uint64_t req_id, int error, + const char *errstr); }; extern struct debug mgmt_dbg_fe_client; @@ -364,6 +378,31 @@ extern int mgmt_fe_send_regnotify_req(struct mgmt_fe_client *client, int num_reqs); /* + * Send GET-TREE to MGMTD daemon. + * + * client + * Client object. + * + * session_id + * Client session ID. + * + * req_id + * Client request ID. + * + * result_type + * The LYD_FORMAT of the result. + * + * xpath + * the xpath to get. + * + * Returns: + * 0 on success, otherwise msg_conn_send_msg() return values. + */ +extern int mgmt_fe_send_get_tree_req(struct mgmt_fe_client *client, + uint64_t session_id, uint64_t req_id, + LYD_FORMAT result_type, const char *xpath); + +/* * Destroy library and cleanup everything. */ extern void mgmt_fe_client_destroy(struct mgmt_fe_client *client); @@ -379,6 +418,17 @@ extern uint mgmt_fe_client_session_count(struct mgmt_fe_client *client); extern bool mgmt_fe_client_current_msg_short_circuit(struct mgmt_fe_client *client); +/** + * Get the name of the client + * + * Args: + * The client object. + * + * Return: + * The name of the client. + */ +extern const char *mgmt_fe_client_name(struct mgmt_fe_client *client); + #ifdef __cplusplus } #endif @@ -157,10 +157,9 @@ static int vty_mgmt_unlock_running_inline(struct vty *vty) return vty->mgmt_locked_running_ds ? -1 : 0; } -void vty_mgmt_resume_response(struct vty *vty, bool success) +void vty_mgmt_resume_response(struct vty *vty, int ret) { uint8_t header[4] = {0, 0, 0, 0}; - int ret = CMD_SUCCESS; if (!vty->mgmt_req_pending_cmd) { zlog_err( @@ -168,14 +167,10 @@ void vty_mgmt_resume_response(struct vty *vty, bool success) return; } - if (!success) - ret = CMD_WARNING_CONFIG_FAILED; - - MGMTD_FE_CLIENT_DBG( - "resuming CLI cmd after %s on vty session-id: %" PRIu64 - " with '%s'", - vty->mgmt_req_pending_cmd, vty->mgmt_session_id, - success ? "succeeded" : "failed"); + MGMTD_FE_CLIENT_DBG("resuming CLI cmd after %s on vty session-id: %" PRIu64 + " with '%s'", + vty->mgmt_req_pending_cmd, vty->mgmt_session_id, + ret == CMD_SUCCESS ? "success" : "failed"); vty->mgmt_req_pending_cmd = NULL; @@ -3560,7 +3555,8 @@ static void vty_mgmt_ds_lock_notified(struct mgmt_fe_client *client, if (!is_short_circuit && vty->mgmt_req_pending_cmd) { assert(!strcmp(vty->mgmt_req_pending_cmd, "MESSAGE_LOCKDS_REQ")); - vty_mgmt_resume_response(vty, success); + vty_mgmt_resume_response(vty, + success ? CMD_SUCCESS : CMD_WARNING); } } @@ -3592,7 +3588,8 @@ static void vty_mgmt_set_config_result_notified( vty_mgmt_unlock_running_inline(vty); } - vty_mgmt_resume_response(vty, success); + vty_mgmt_resume_response(vty, success ? CMD_SUCCESS + : CMD_WARNING_CONFIG_FAILED); } static void vty_mgmt_commit_config_result_notified( @@ -3620,7 +3617,8 @@ static void vty_mgmt_commit_config_result_notified( vty_out(vty, "MGMTD: %s\n", errmsg_if_any); } - vty_mgmt_resume_response(vty, success); + vty_mgmt_resume_response(vty, success ? CMD_SUCCESS + : CMD_WARNING_CONFIG_FAILED); } static int vty_mgmt_get_data_result_notified( @@ -3640,7 +3638,7 @@ static int vty_mgmt_get_data_result_notified( client_id, errmsg_if_any ? errmsg_if_any : "Unknown"); vty_out(vty, "ERROR: GET_DATA request failed, Error: %s\n", errmsg_if_any ? errmsg_if_any : "Unknown"); - vty_mgmt_resume_response(vty, success); + vty_mgmt_resume_response(vty, CMD_WARNING); return -1; } @@ -3659,9 +3657,290 @@ static int vty_mgmt_get_data_result_notified( } if (next_key < 0) { vty_out(vty, "]\n"); - vty_mgmt_resume_response(vty, success); + vty_mgmt_resume_response(vty, + success ? CMD_SUCCESS : CMD_WARNING); + } + + return 0; +} + +static ssize_t vty_mgmt_libyang_print(void *user_data, const void *buf, + size_t count) +{ + struct vty *vty = user_data; + + vty_out(vty, "%.*s", (int)count, (const char *)buf); + return count; +} + +static void vty_out_yang_error(struct vty *vty, LYD_FORMAT format, + struct ly_err_item *ei) +{ + bool have_apptag = ei->apptag && ei->apptag[0] != 0; + bool have_path = ei->path && ei->path[0] != 0; + bool have_msg = ei->msg && ei->msg[0] != 0; + const char *severity = NULL; + const char *evalid = NULL; + const char *ecode = NULL; + LY_ERR err = ei->no; + + if (ei->level == LY_LLERR) + severity = "error"; + else if (ei->level == LY_LLWRN) + severity = "warning"; + + switch (ei->no) { + case LY_SUCCESS: + ecode = "ok"; + break; + case LY_EMEM: + ecode = "out of memory"; + break; + case LY_ESYS: + ecode = "system error"; + break; + case LY_EINVAL: + ecode = "invalid value given"; + break; + case LY_EEXIST: + ecode = "item exists"; + break; + case LY_ENOTFOUND: + ecode = "item not found"; + break; + case LY_EINT: + ecode = "operation interrupted"; + break; + case LY_EVALID: + ecode = "validation failed"; + break; + case LY_EDENIED: + ecode = "access denied"; + break; + case LY_EINCOMPLETE: + ecode = "incomplete"; + break; + case LY_ERECOMPILE: + ecode = "compile error"; + break; + case LY_ENOT: + ecode = "not"; + break; + default: + case LY_EPLUGIN: + case LY_EOTHER: + ecode = "other"; + break; + } + + if (err == LY_EVALID) { + switch (ei->vecode) { + case LYVE_SUCCESS: + evalid = NULL; + break; + case LYVE_SYNTAX: + evalid = "syntax"; + break; + case LYVE_SYNTAX_YANG: + evalid = "yang-syntax"; + break; + case LYVE_SYNTAX_YIN: + evalid = "yin-syntax"; + break; + case LYVE_REFERENCE: + evalid = "reference"; + break; + case LYVE_XPATH: + evalid = "xpath"; + break; + case LYVE_SEMANTICS: + evalid = "semantics"; + break; + case LYVE_SYNTAX_XML: + evalid = "xml-syntax"; + break; + case LYVE_SYNTAX_JSON: + evalid = "json-syntax"; + break; + case LYVE_DATA: + evalid = "data"; + break; + default: + case LYVE_OTHER: + evalid = "other"; + break; + } + } + + switch (format) { + case LYD_XML: + vty_out(vty, + "<rpc-error xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">"); + vty_out(vty, "<error-type>application</error-type>"); + if (severity) + vty_out(vty, "<error-severity>%s</error-severity>", + severity); + if (ecode) + vty_out(vty, "<error-code>%s</error-code>", ecode); + if (evalid) + vty_out(vty, "<error-validation>%s</error-validation>\n", + evalid); + if (have_path) + vty_out(vty, "<error-path>%s</error-path>\n", ei->path); + if (have_apptag) + vty_out(vty, "<error-app-tag>%s</error-app-tag>\n", + ei->apptag); + if (have_msg) + vty_out(vty, "<error-message>%s</error-message>\n", + ei->msg); + + vty_out(vty, "</rpc-error>"); + break; + case LYD_JSON: + vty_out(vty, "{ \"error-type\": \"application\""); + if (severity) + vty_out(vty, ", \"error-severity\": \"%s\"", severity); + if (ecode) + vty_out(vty, ", \"error-code\": \"%s\"", ecode); + if (evalid) + vty_out(vty, ", \"error-validation\": \"%s\"", evalid); + if (have_path) + vty_out(vty, ", \"error-path\": \"%s\"", ei->path); + if (have_apptag) + vty_out(vty, ", \"error-app-tag\": \"%s\"", ei->apptag); + if (have_msg) + vty_out(vty, ", \"error-message\": \"%s\"", ei->msg); + + vty_out(vty, "}"); + break; + case LYD_UNKNOWN: + case LYD_LYB: + default: + vty_out(vty, "%% error"); + if (severity) + vty_out(vty, " severity: %s", severity); + if (evalid) + vty_out(vty, " invalid: %s", evalid); + if (have_path) + vty_out(vty, " path: %s", ei->path); + if (have_apptag) + vty_out(vty, " app-tag: %s", ei->apptag); + if (have_msg) + vty_out(vty, " msg: %s", ei->msg); + break; + } +} + +static uint vty_out_yang_errors(struct vty *vty, LYD_FORMAT format) +{ + struct ly_err_item *ei = ly_err_first(ly_native_ctx); + uint count; + + if (!ei) + return 0; + + if (format == LYD_JSON) + vty_out(vty, "\"ietf-restconf:errors\": [ "); + + for (count = 0; ei; count++, ei = ei->next) { + if (count) + vty_out(vty, ", "); + vty_out_yang_error(vty, format, ei); } + if (format == LYD_JSON) + vty_out(vty, " ]"); + + ly_err_clean(ly_native_ctx, NULL); + + return count; +} + + +static int vty_mgmt_get_tree_result_notified( + struct mgmt_fe_client *client, uintptr_t user_data, uint64_t client_id, + uint64_t session_id, uint64_t session_ctx, uint64_t req_id, + Mgmtd__DatastoreId ds_id, LYD_FORMAT result_type, void *result, + size_t len, int partial_error) +{ + struct vty *vty; + struct lyd_node *dnode; + int ret = CMD_SUCCESS; + LY_ERR err; + + vty = (struct vty *)session_ctx; + + MGMTD_FE_CLIENT_DBG("GET_TREE request %ssucceeded, client 0x%" PRIx64 + " req-id %" PRIu64, + partial_error ? "partially " : "", client_id, + req_id); + + assert(result_type == LYD_LYB || + result_type == vty->mgmt_req_pending_data); + + if (vty->mgmt_req_pending_data == LYD_XML && partial_error) + vty_out(vty, + "<!-- some errors occurred gathering results -->\n"); + + if (result_type == LYD_LYB) { + /* + * parse binary into tree and print in the specified format + */ + result_type = vty->mgmt_req_pending_data; + + err = lyd_parse_data_mem(ly_native_ctx, result, LYD_LYB, 0, 0, + &dnode); + if (!err) + err = lyd_print_clb(vty_mgmt_libyang_print, vty, dnode, + result_type, LYD_PRINT_WITHSIBLINGS); + lyd_free_all(dnode); + + if (vty_out_yang_errors(vty, result_type) || err) + ret = CMD_WARNING; + } else { + /* + * Print the in-format result + */ + assert(result_type == LYD_XML || result_type == LYD_JSON); + vty_out(vty, "%.*s\n", (int)len - 1, (const char *)result); + } + + vty_mgmt_resume_response(vty, ret); + + return 0; +} + +static int vty_mgmt_error_notified(struct mgmt_fe_client *client, + uintptr_t user_data, uint64_t client_id, + uint64_t session_id, uintptr_t session_ctx, + uint64_t req_id, int error, + const char *errstr) +{ + struct vty *vty = (struct vty *)session_ctx; + const char *cname = mgmt_fe_client_name(client); + + if (!vty->mgmt_req_pending_cmd) { + MGMTD_FE_CLIENT_DBG("Erorr with no pending command: %d returned for client %s 0x%" PRIx64 + " session-id %" PRIu64 " req-id %" PRIu64 + "error-str %s", + error, cname, client_id, session_id, req_id, + errstr); + vty_out(vty, + "%% Error %d from MGMTD for %s with no pending command: %s\n", + error, cname, errstr); + return CMD_WARNING; + } + + MGMTD_FE_CLIENT_DBG("Erorr %d returned for client %s 0x%" PRIx64 + " session-id %" PRIu64 " req-id %" PRIu64 + "error-str %s", + error, cname, client_id, session_id, req_id, errstr); + + vty_out(vty, "%% %s (for %s, client %s)\n", errstr, + vty->mgmt_req_pending_cmd, cname); + + vty_mgmt_resume_response(vty, error ? CMD_WARNING : CMD_SUCCESS); + return 0; } @@ -3672,6 +3951,9 @@ static struct mgmt_fe_client_cbs mgmt_cbs = { .set_config_notify = vty_mgmt_set_config_result_notified, .commit_config_notify = vty_mgmt_commit_config_result_notified, .get_data_notify = vty_mgmt_get_data_result_notified, + .get_tree_notify = vty_mgmt_get_tree_result_notified, + .error_notify = vty_mgmt_error_notified, + }; void vty_init_mgmt_fe(void) @@ -3893,6 +4175,28 @@ int vty_mgmt_send_get_req(struct vty *vty, bool is_config, return 0; } +int vty_mgmt_send_get_tree_req(struct vty *vty, LYD_FORMAT result_type, + const char *xpath) +{ + LYD_FORMAT intern_format = result_type; + + vty->mgmt_req_id++; + + if (mgmt_fe_send_get_tree_req(mgmt_fe_client, vty->mgmt_session_id, + vty->mgmt_req_id, intern_format, xpath)) { + zlog_err("Failed to send GET-TREE to MGMTD session-id: %" PRIu64 + " req-id %" PRIu64 ".", + vty->mgmt_session_id, vty->mgmt_req_id); + vty_out(vty, "Failed to send GET-TREE to MGMTD!\n"); + return -1; + } + + vty->mgmt_req_pending_cmd = "MESSAGE_GET_TREE_REQ"; + vty->mgmt_req_pending_data = result_type; + + return 0; +} + /* Install vty's own commands like `who' command. */ void vty_init(struct event_loop *master_thread, bool do_command_logging) { @@ -229,6 +229,7 @@ struct vty { * CLI command and we are waiting on the reply so we can respond to the * vty user. */ const char *mgmt_req_pending_cmd; + uintptr_t mgmt_req_pending_data; bool mgmt_locked_candidate_ds; bool mgmt_locked_running_ds; /* Need to track when we file-lock in vtysh to re-lock on end/conf t @@ -419,9 +420,11 @@ extern int vty_mgmt_send_commit_config(struct vty *vty, bool validate_only, extern int vty_mgmt_send_get_req(struct vty *vty, bool is_config, Mgmtd__DatastoreId datastore, const char **xpath_list, int num_req); +extern int vty_mgmt_send_get_tree_req(struct vty *vty, LYD_FORMAT result_type, + const char *xpath); extern int vty_mgmt_send_lockds_req(struct vty *vty, Mgmtd__DatastoreId ds_id, bool lock, bool scok); -extern void vty_mgmt_resume_response(struct vty *vty, bool success); +extern void vty_mgmt_resume_response(struct vty *vty, int ret); static inline bool vty_needs_implicit_commit(struct vty *vty) { diff --git a/mgmtd/mgmt_history.c b/mgmtd/mgmt_history.c index d4069325ca..ddc5a1844e 100644 --- a/mgmtd/mgmt_history.c +++ b/mgmtd/mgmt_history.c @@ -261,7 +261,9 @@ failed_unlock: void mgmt_history_rollback_complete(bool success) { - vty_mgmt_resume_response(rollback_vty, success); + vty_mgmt_resume_response(rollback_vty, + success ? CMD_SUCCESS + : CMD_WARNING_CONFIG_FAILED); rollback_vty = NULL; } diff --git a/mgmtd/mgmt_vty.c b/mgmtd/mgmt_vty.c index 3116ccbaf7..68203b8a70 100644 --- a/mgmtd/mgmt_vty.c +++ b/mgmtd/mgmt_vty.c @@ -218,6 +218,35 @@ DEFPY(show_mgmt_get_data, show_mgmt_get_data_cmd, return CMD_SUCCESS; } +DEFPY(show_mgmt_get_data_tree, show_mgmt_get_data_tree_cmd, + "show mgmt get-data-tree WORD$path [json|xml]$fmt", + SHOW_STR MGMTD_STR + "Get a data tree from the operational datastore\n" + "XPath expression specifying the YANG data root\n" + "JSON output format\n" + "XML output format\n") +{ + LYD_FORMAT format = (fmt && fmt[0] == 'x') ? LYD_XML : LYD_JSON; + int plen = strlen(path); + char *xpath = NULL; + + /* get rid of extraneous trailing slash-* or single '/' unless root */ + if (plen > 2 && ((path[plen - 2] == '/' && path[plen - 1] == '*') || + (path[plen - 2] != '/' && path[plen - 1] == '/'))) { + plen = path[plen - 1] == '/' ? plen - 1 : plen - 2; + xpath = XSTRDUP(MTYPE_TMP, path); + xpath[plen] = 0; + path = xpath; + } + + vty_mgmt_send_get_tree_req(vty, format, path); + + if (xpath) + XFREE(MTYPE_TMP, xpath); + + return CMD_SUCCESS; +} + DEFPY(show_mgmt_dump_data, show_mgmt_dump_data_cmd, "show mgmt datastore-contents [candidate|operational|running]$dsname [xpath WORD$path] [file WORD$filepath] <json|xml>$fmt", @@ -512,6 +541,7 @@ void mgmt_vty_init(void) install_element(VIEW_NODE, &show_mgmt_ds_cmd); install_element(VIEW_NODE, &show_mgmt_get_config_cmd); install_element(VIEW_NODE, &show_mgmt_get_data_cmd); + install_element(VIEW_NODE, &show_mgmt_get_data_tree_cmd); install_element(VIEW_NODE, &show_mgmt_dump_data_cmd); install_element(VIEW_NODE, &show_mgmt_map_xpath_cmd); install_element(VIEW_NODE, &show_mgmt_cmt_hist_cmd); |
